diff --git a/MAINTAINERS b/MAINTAINERS index 301b9ba6af79f..379387e20a96d 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -5183,8 +5183,8 @@ S: Supported W: https://wiki.samba.org/index.php/LinuxCIFS T: git git://git.samba.org/sfrench/cifs-2.6.git F: Documentation/admin-guide/cifs/ -F: fs/cifs/ -F: fs/smbfs_common/ +F: fs/smb/client/ +F: fs/smb/common/ F: include/uapi/linux/cifs COMPACTPCI HOTPLUG CORE @@ -11168,8 +11168,8 @@ L: linux-cifs@vger.kernel.org S: Maintained T: git git://git.samba.org/ksmbd.git F: Documentation/filesystems/cifs/ksmbd.rst -F: fs/ksmbd/ -F: fs/smbfs_common/ +F: fs/smb/common/ +F: fs/smb/server/ KERNEL UNIT TESTING FRAMEWORK (KUnit) M: Brendan Higgins diff --git a/Makefile b/Makefile index 46c06af912d9d..cffb83d7a0fb6 100644 --- a/Makefile +++ b/Makefile @@ -1,7 +1,7 @@ # SPDX-License-Identifier: GPL-2.0 VERSION = 6 PATCHLEVEL = 1 -SUBLEVEL = 35 +SUBLEVEL = 36 EXTRAVERSION = NAME = Curry Ramen diff --git a/arch/arm/boot/dts/am57xx-cl-som-am57x.dts b/arch/arm/boot/dts/am57xx-cl-som-am57x.dts index 2fc9a5d5e0c0d..625b9b311b49d 100644 --- a/arch/arm/boot/dts/am57xx-cl-som-am57x.dts +++ b/arch/arm/boot/dts/am57xx-cl-som-am57x.dts @@ -527,7 +527,7 @@ interrupt-parent = <&gpio1>; interrupts = <31 0>; - pendown-gpio = <&gpio1 31 0>; + pendown-gpio = <&gpio1 31 GPIO_ACTIVE_LOW>; ti,x-min = /bits/ 16 <0x0>; diff --git a/arch/arm/boot/dts/at91sam9261ek.dts b/arch/arm/boot/dts/at91sam9261ek.dts index 88869ca874d1a..045cb253f23a6 100644 --- a/arch/arm/boot/dts/at91sam9261ek.dts +++ b/arch/arm/boot/dts/at91sam9261ek.dts @@ -156,7 +156,7 @@ compatible = "ti,ads7843"; interrupts-extended = <&pioC 2 IRQ_TYPE_EDGE_BOTH>; spi-max-frequency = <3000000>; - pendown-gpio = <&pioC 2 GPIO_ACTIVE_HIGH>; + pendown-gpio = <&pioC 2 GPIO_ACTIVE_LOW>; ti,x-min = /bits/ 16 <150>; ti,x-max = /bits/ 16 <3830>; diff --git a/arch/arm/boot/dts/imx7d-pico-hobbit.dts b/arch/arm/boot/dts/imx7d-pico-hobbit.dts index d917dc4f2f227..6ad39dca70096 100644 --- a/arch/arm/boot/dts/imx7d-pico-hobbit.dts +++ b/arch/arm/boot/dts/imx7d-pico-hobbit.dts @@ -64,7 +64,7 @@ interrupt-parent = <&gpio2>; interrupts = <7 0>; spi-max-frequency = <1000000>; - pendown-gpio = <&gpio2 7 0>; + pendown-gpio = <&gpio2 7 GPIO_ACTIVE_LOW>; vcc-supply = <®_3p3v>; ti,x-min = /bits/ 16 <0>; ti,x-max = /bits/ 16 <4095>; diff --git a/arch/arm/boot/dts/imx7d-sdb.dts b/arch/arm/boot/dts/imx7d-sdb.dts index f483bc0afe5ea..234e5fc647b22 100644 --- a/arch/arm/boot/dts/imx7d-sdb.dts +++ b/arch/arm/boot/dts/imx7d-sdb.dts @@ -205,7 +205,7 @@ pinctrl-0 = <&pinctrl_tsc2046_pendown>; interrupt-parent = <&gpio2>; interrupts = <29 0>; - pendown-gpio = <&gpio2 29 GPIO_ACTIVE_HIGH>; + pendown-gpio = <&gpio2 29 GPIO_ACTIVE_LOW>; touchscreen-max-pressure = <255>; wakeup-source; }; diff --git a/arch/arm/boot/dts/omap3-cm-t3x.dtsi b/arch/arm/boot/dts/omap3-cm-t3x.dtsi index e61b8a2bfb7de..51baedf1603bd 100644 --- a/arch/arm/boot/dts/omap3-cm-t3x.dtsi +++ b/arch/arm/boot/dts/omap3-cm-t3x.dtsi @@ -227,7 +227,7 @@ interrupt-parent = <&gpio2>; interrupts = <25 0>; /* gpio_57 */ - pendown-gpio = <&gpio2 25 GPIO_ACTIVE_HIGH>; + pendown-gpio = <&gpio2 25 GPIO_ACTIVE_LOW>; ti,x-min = /bits/ 16 <0x0>; ti,x-max = /bits/ 16 <0x0fff>; diff --git a/arch/arm/boot/dts/omap3-devkit8000-lcd-common.dtsi b/arch/arm/boot/dts/omap3-devkit8000-lcd-common.dtsi index 3decc2d78a6ca..a7f99ae0c1fe9 100644 --- a/arch/arm/boot/dts/omap3-devkit8000-lcd-common.dtsi +++ b/arch/arm/boot/dts/omap3-devkit8000-lcd-common.dtsi @@ -54,7 +54,7 @@ interrupt-parent = <&gpio1>; interrupts = <27 0>; /* gpio_27 */ - pendown-gpio = <&gpio1 27 GPIO_ACTIVE_HIGH>; + pendown-gpio = <&gpio1 27 GPIO_ACTIVE_LOW>; ti,x-min = /bits/ 16 <0x0>; ti,x-max = /bits/ 16 <0x0fff>; diff --git a/arch/arm/boot/dts/omap3-lilly-a83x.dtsi b/arch/arm/boot/dts/omap3-lilly-a83x.dtsi index c595afe4181d7..d310b5c7bac36 100644 --- a/arch/arm/boot/dts/omap3-lilly-a83x.dtsi +++ b/arch/arm/boot/dts/omap3-lilly-a83x.dtsi @@ -311,7 +311,7 @@ interrupt-parent = <&gpio1>; interrupts = <8 0>; /* boot6 / gpio_8 */ spi-max-frequency = <1000000>; - pendown-gpio = <&gpio1 8 GPIO_ACTIVE_HIGH>; + pendown-gpio = <&gpio1 8 GPIO_ACTIVE_LOW>; vcc-supply = <®_vcc3>; pinctrl-names = "default"; pinctrl-0 = <&tsc2048_pins>; diff --git a/arch/arm/boot/dts/omap3-overo-common-lcd35.dtsi b/arch/arm/boot/dts/omap3-overo-common-lcd35.dtsi index 1d6e88f99eb31..c3570acc35fad 100644 --- a/arch/arm/boot/dts/omap3-overo-common-lcd35.dtsi +++ b/arch/arm/boot/dts/omap3-overo-common-lcd35.dtsi @@ -149,7 +149,7 @@ interrupt-parent = <&gpio4>; interrupts = <18 0>; /* gpio_114 */ - pendown-gpio = <&gpio4 18 GPIO_ACTIVE_HIGH>; + pendown-gpio = <&gpio4 18 GPIO_ACTIVE_LOW>; ti,x-min = /bits/ 16 <0x0>; ti,x-max = /bits/ 16 <0x0fff>; diff --git a/arch/arm/boot/dts/omap3-overo-common-lcd43.dtsi b/arch/arm/boot/dts/omap3-overo-common-lcd43.dtsi index 7e30f9d45790e..d95a0e130058c 100644 --- a/arch/arm/boot/dts/omap3-overo-common-lcd43.dtsi +++ b/arch/arm/boot/dts/omap3-overo-common-lcd43.dtsi @@ -160,7 +160,7 @@ interrupt-parent = <&gpio4>; interrupts = <18 0>; /* gpio_114 */ - pendown-gpio = <&gpio4 18 GPIO_ACTIVE_HIGH>; + pendown-gpio = <&gpio4 18 GPIO_ACTIVE_LOW>; ti,x-min = /bits/ 16 <0x0>; ti,x-max = /bits/ 16 <0x0fff>; diff --git a/arch/arm/boot/dts/omap3-pandora-common.dtsi b/arch/arm/boot/dts/omap3-pandora-common.dtsi index 559853764487f..4c3b6bab179cc 100644 --- a/arch/arm/boot/dts/omap3-pandora-common.dtsi +++ b/arch/arm/boot/dts/omap3-pandora-common.dtsi @@ -651,7 +651,7 @@ pinctrl-0 = <&penirq_pins>; interrupt-parent = <&gpio3>; interrupts = <30 IRQ_TYPE_NONE>; /* GPIO_94 */ - pendown-gpio = <&gpio3 30 GPIO_ACTIVE_HIGH>; + pendown-gpio = <&gpio3 30 GPIO_ACTIVE_LOW>; vcc-supply = <&vaux4>; ti,x-min = /bits/ 16 <0>; diff --git a/arch/arm/boot/dts/omap5-cm-t54.dts b/arch/arm/boot/dts/omap5-cm-t54.dts index ca759b7b8a580..e62ea8b6d53fd 100644 --- a/arch/arm/boot/dts/omap5-cm-t54.dts +++ b/arch/arm/boot/dts/omap5-cm-t54.dts @@ -354,7 +354,7 @@ interrupt-parent = <&gpio1>; interrupts = <15 0>; /* gpio1_wk15 */ - pendown-gpio = <&gpio1 15 GPIO_ACTIVE_HIGH>; + pendown-gpio = <&gpio1 15 GPIO_ACTIVE_LOW>; ti,x-min = /bits/ 16 <0x0>; diff --git a/arch/arm64/boot/dts/qcom/sc7280-idp.dtsi b/arch/arm64/boot/dts/qcom/sc7280-idp.dtsi index ca50f0ba9b815..1c370dcfe60b9 100644 --- a/arch/arm64/boot/dts/qcom/sc7280-idp.dtsi +++ b/arch/arm64/boot/dts/qcom/sc7280-idp.dtsi @@ -488,7 +488,6 @@ wcd_rx: codec@0,4 { compatible = "sdw20217010d00"; reg = <0 4>; - #sound-dai-cells = <1>; qcom,rx-port-mapping = <1 2 3 4 5>; }; }; @@ -499,7 +498,6 @@ wcd_tx: codec@0,3 { compatible = "sdw20217010d00"; reg = <0 3>; - #sound-dai-cells = <1>; qcom,tx-port-mapping = <1 2 3 4>; }; }; diff --git a/arch/arm64/boot/dts/qcom/sc7280-qcard.dtsi b/arch/arm64/boot/dts/qcom/sc7280-qcard.dtsi index f7665b3799233..c358abc052eb8 100644 --- a/arch/arm64/boot/dts/qcom/sc7280-qcard.dtsi +++ b/arch/arm64/boot/dts/qcom/sc7280-qcard.dtsi @@ -418,7 +418,6 @@ wcd_rx: codec@0,4 { compatible = "sdw20217010d00"; reg = <0 4>; - #sound-dai-cells = <1>; qcom,rx-port-mapping = <1 2 3 4 5>; }; }; @@ -427,7 +426,6 @@ wcd_tx: codec@0,3 { compatible = "sdw20217010d00"; reg = <0 3>; - #sound-dai-cells = <1>; qcom,tx-port-mapping = <1 2 3 4>; }; }; diff --git a/arch/arm64/boot/dts/rockchip/rk3566-soquartz-cm4.dts b/arch/arm64/boot/dts/rockchip/rk3566-soquartz-cm4.dts index e00568a6be5cc..6ba562b922e6c 100644 --- a/arch/arm64/boot/dts/rockchip/rk3566-soquartz-cm4.dts +++ b/arch/arm64/boot/dts/rockchip/rk3566-soquartz-cm4.dts @@ -28,6 +28,16 @@ regulator-max-microvolt = <5000000>; vin-supply = <&vcc12v_dcin>; }; + + vcc_sd_pwr: vcc-sd-pwr-regulator { + compatible = "regulator-fixed"; + regulator-name = "vcc_sd_pwr"; + regulator-always-on; + regulator-boot-on; + regulator-min-microvolt = <3300000>; + regulator-max-microvolt = <3300000>; + vin-supply = <&vcc3v3_sys>; + }; }; &gmac1 { @@ -119,13 +129,7 @@ }; &sdmmc0 { - vmmc-supply = <&sdmmc_pwr>; - status = "okay"; -}; - -&sdmmc_pwr { - regulator-min-microvolt = <3300000>; - regulator-max-microvolt = <3300000>; + vmmc-supply = <&vcc_sd_pwr>; status = "okay"; }; diff --git a/arch/arm64/boot/dts/rockchip/rk3566-soquartz.dtsi b/arch/arm64/boot/dts/rockchip/rk3566-soquartz.dtsi index 4d494b53a71ab..ba56ca2e66c8d 100644 --- a/arch/arm64/boot/dts/rockchip/rk3566-soquartz.dtsi +++ b/arch/arm64/boot/dts/rockchip/rk3566-soquartz.dtsi @@ -92,16 +92,6 @@ regulator-max-microvolt = <3300000>; vin-supply = <&vcc5v0_sys>; }; - - sdmmc_pwr: sdmmc-pwr-regulator { - compatible = "regulator-fixed"; - enable-active-high; - gpio = <&gpio0 RK_PA5 GPIO_ACTIVE_HIGH>; - pinctrl-names = "default"; - pinctrl-0 = <&sdmmc_pwr_h>; - regulator-name = "sdmmc_pwr"; - status = "disabled"; - }; }; &cpu0 { @@ -143,6 +133,24 @@ status = "disabled"; }; +&gpio0 { + nextrst-hog { + gpio-hog; + /* + * GPIO_ACTIVE_LOW + output-low here means that the pin is set + * to high, because output-low decides the value pre-inversion. + */ + gpios = ; + line-name = "nEXTRST"; + output-low; + }; +}; + +&gpu { + mali-supply = <&vdd_gpu>; + status = "okay"; +}; + &i2c0 { status = "okay"; @@ -480,12 +488,6 @@ rockchip,pins = <2 RK_PC2 RK_FUNC_GPIO &pcfg_pull_none>; }; }; - - sdmmc-pwr { - sdmmc_pwr_h: sdmmc-pwr-h { - rockchip,pins = <0 RK_PA5 RK_FUNC_GPIO &pcfg_pull_none>; - }; - }; }; &pmu_io_domains { diff --git a/arch/arm64/boot/dts/rockchip/rk3568.dtsi b/arch/arm64/boot/dts/rockchip/rk3568.dtsi index ba67b58f05b79..f1be76a54ceb0 100644 --- a/arch/arm64/boot/dts/rockchip/rk3568.dtsi +++ b/arch/arm64/boot/dts/rockchip/rk3568.dtsi @@ -94,9 +94,10 @@ power-domains = <&power RK3568_PD_PIPE>; reg = <0x3 0xc0400000 0x0 0x00400000>, <0x0 0xfe270000 0x0 0x00010000>, - <0x3 0x7f000000 0x0 0x01000000>; - ranges = <0x01000000 0x0 0x3ef00000 0x3 0x7ef00000 0x0 0x00100000>, - <0x02000000 0x0 0x00000000 0x3 0x40000000 0x0 0x3ef00000>; + <0x0 0xf2000000 0x0 0x00100000>; + ranges = <0x01000000 0x0 0xf2100000 0x0 0xf2100000 0x0 0x00100000>, + <0x02000000 0x0 0xf2200000 0x0 0xf2200000 0x0 0x01e00000>, + <0x03000000 0x0 0x40000000 0x3 0x40000000 0x0 0x40000000>; reg-names = "dbi", "apb", "config"; resets = <&cru SRST_PCIE30X1_POWERUP>; reset-names = "pipe"; @@ -146,9 +147,10 @@ power-domains = <&power RK3568_PD_PIPE>; reg = <0x3 0xc0800000 0x0 0x00400000>, <0x0 0xfe280000 0x0 0x00010000>, - <0x3 0xbf000000 0x0 0x01000000>; - ranges = <0x01000000 0x0 0x3ef00000 0x3 0xbef00000 0x0 0x00100000>, - <0x02000000 0x0 0x00000000 0x3 0x80000000 0x0 0x3ef00000>; + <0x0 0xf0000000 0x0 0x00100000>; + ranges = <0x01000000 0x0 0xf0100000 0x0 0xf0100000 0x0 0x00100000>, + <0x02000000 0x0 0xf0200000 0x0 0xf0200000 0x0 0x01e00000>, + <0x03000000 0x0 0x40000000 0x3 0x80000000 0x0 0x40000000>; reg-names = "dbi", "apb", "config"; resets = <&cru SRST_PCIE30X2_POWERUP>; reset-names = "pipe"; diff --git a/arch/arm64/boot/dts/rockchip/rk356x.dtsi b/arch/arm64/boot/dts/rockchip/rk356x.dtsi index 1d423daae971b..234b5bbda1204 100644 --- a/arch/arm64/boot/dts/rockchip/rk356x.dtsi +++ b/arch/arm64/boot/dts/rockchip/rk356x.dtsi @@ -951,7 +951,7 @@ compatible = "rockchip,rk3568-pcie"; reg = <0x3 0xc0000000 0x0 0x00400000>, <0x0 0xfe260000 0x0 0x00010000>, - <0x3 0x3f000000 0x0 0x01000000>; + <0x0 0xf4000000 0x0 0x00100000>; reg-names = "dbi", "apb", "config"; interrupts = , , @@ -981,8 +981,9 @@ phys = <&combphy2 PHY_TYPE_PCIE>; phy-names = "pcie-phy"; power-domains = <&power RK3568_PD_PIPE>; - ranges = <0x01000000 0x0 0x3ef00000 0x3 0x3ef00000 0x0 0x00100000 - 0x02000000 0x0 0x00000000 0x3 0x00000000 0x0 0x3ef00000>; + ranges = <0x01000000 0x0 0xf4100000 0x0 0xf4100000 0x0 0x00100000>, + <0x02000000 0x0 0xf4200000 0x0 0xf4200000 0x0 0x01e00000>, + <0x03000000 0x0 0x40000000 0x3 0x00000000 0x0 0x40000000>; resets = <&cru SRST_PCIE20_POWERUP>; reset-names = "pipe"; #address-cells = <3>; diff --git a/arch/arm64/include/asm/sysreg.h b/arch/arm64/include/asm/sysreg.h index 7d301700d1a93..3a448ab0924b3 100644 --- a/arch/arm64/include/asm/sysreg.h +++ b/arch/arm64/include/asm/sysreg.h @@ -111,8 +111,14 @@ #define SB_BARRIER_INSN __SYS_BARRIER_INSN(0, 7, 31) #define SYS_DC_ISW sys_insn(1, 0, 7, 6, 2) +#define SYS_DC_IGSW sys_insn(1, 0, 7, 6, 4) +#define SYS_DC_IGDSW sys_insn(1, 0, 7, 6, 6) #define SYS_DC_CSW sys_insn(1, 0, 7, 10, 2) +#define SYS_DC_CGSW sys_insn(1, 0, 7, 10, 4) +#define SYS_DC_CGDSW sys_insn(1, 0, 7, 10, 6) #define SYS_DC_CISW sys_insn(1, 0, 7, 14, 2) +#define SYS_DC_CIGSW sys_insn(1, 0, 7, 14, 4) +#define SYS_DC_CIGDSW sys_insn(1, 0, 7, 14, 6) /* * Automatically generated definitions for system registers, the diff --git a/arch/arm64/kvm/hyp/include/hyp/switch.h b/arch/arm64/kvm/hyp/include/hyp/switch.h index 2208d79b18dea..081aca8f432ef 100644 --- a/arch/arm64/kvm/hyp/include/hyp/switch.h +++ b/arch/arm64/kvm/hyp/include/hyp/switch.h @@ -81,7 +81,12 @@ static inline void __activate_traps_common(struct kvm_vcpu *vcpu) * EL1 instead of being trapped to EL2. */ if (kvm_arm_support_pmu_v3()) { + struct kvm_cpu_context *hctxt; + write_sysreg(0, pmselr_el0); + + hctxt = &this_cpu_ptr(&kvm_host_data)->host_ctxt; + ctxt_sys_reg(hctxt, PMUSERENR_EL0) = read_sysreg(pmuserenr_el0); write_sysreg(ARMV8_PMU_USERENR_MASK, pmuserenr_el0); } @@ -105,8 +110,12 @@ static inline void __deactivate_traps_common(struct kvm_vcpu *vcpu) write_sysreg(vcpu->arch.mdcr_el2_host, mdcr_el2); write_sysreg(0, hstr_el2); - if (kvm_arm_support_pmu_v3()) - write_sysreg(0, pmuserenr_el0); + if (kvm_arm_support_pmu_v3()) { + struct kvm_cpu_context *hctxt; + + hctxt = &this_cpu_ptr(&kvm_host_data)->host_ctxt; + write_sysreg(ctxt_sys_reg(hctxt, PMUSERENR_EL0), pmuserenr_el0); + } if (cpus_have_final_cap(ARM64_SME)) { sysreg_clear_set_s(SYS_HFGRTR_EL2, 0, diff --git a/arch/arm64/kvm/vgic/vgic-init.c b/arch/arm64/kvm/vgic/vgic-init.c index 37e74ca4dad85..f2f3bf4a04b0b 100644 --- a/arch/arm64/kvm/vgic/vgic-init.c +++ b/arch/arm64/kvm/vgic/vgic-init.c @@ -446,6 +446,7 @@ int vgic_lazy_init(struct kvm *kvm) int kvm_vgic_map_resources(struct kvm *kvm) { struct vgic_dist *dist = &kvm->arch.vgic; + enum vgic_type type; gpa_t dist_base; int ret = 0; @@ -460,10 +461,13 @@ int kvm_vgic_map_resources(struct kvm *kvm) if (!irqchip_in_kernel(kvm)) goto out; - if (dist->vgic_model == KVM_DEV_TYPE_ARM_VGIC_V2) + if (dist->vgic_model == KVM_DEV_TYPE_ARM_VGIC_V2) { ret = vgic_v2_map_resources(kvm); - else + type = VGIC_V2; + } else { ret = vgic_v3_map_resources(kvm); + type = VGIC_V3; + } if (ret) { __kvm_vgic_destroy(kvm); @@ -473,8 +477,7 @@ int kvm_vgic_map_resources(struct kvm *kvm) dist_base = dist->vgic_dist_base; mutex_unlock(&kvm->arch.config_lock); - ret = vgic_register_dist_iodev(kvm, dist_base, - kvm_vgic_global_state.type); + ret = vgic_register_dist_iodev(kvm, dist_base, type); if (ret) { kvm_err("Unable to register VGIC dist MMIO regions\n"); kvm_vgic_destroy(kvm); diff --git a/arch/s390/purgatory/Makefile b/arch/s390/purgatory/Makefile index d237bc6841cb8..4cbf306b8181f 100644 --- a/arch/s390/purgatory/Makefile +++ b/arch/s390/purgatory/Makefile @@ -26,6 +26,7 @@ KBUILD_CFLAGS += -Wno-pointer-sign -Wno-sign-compare KBUILD_CFLAGS += -fno-zero-initialized-in-bss -fno-builtin -ffreestanding KBUILD_CFLAGS += -c -MD -Os -m64 -msoft-float -fno-common KBUILD_CFLAGS += -fno-stack-protector +KBUILD_CFLAGS += -DDISABLE_BRANCH_PROFILING KBUILD_CFLAGS += $(CLANG_FLAGS) KBUILD_CFLAGS += $(call cc-option,-fno-PIE) KBUILD_AFLAGS := $(filter-out -DCC_USING_EXPOLINE,$(KBUILD_AFLAGS)) diff --git a/arch/x86/kernel/apic/x2apic_phys.c b/arch/x86/kernel/apic/x2apic_phys.c index 6bde05a86b4ed..896bc41cb2ba7 100644 --- a/arch/x86/kernel/apic/x2apic_phys.c +++ b/arch/x86/kernel/apic/x2apic_phys.c @@ -97,7 +97,10 @@ static void init_x2apic_ldr(void) static int x2apic_phys_probe(void) { - if (x2apic_mode && (x2apic_phys || x2apic_fadt_phys())) + if (!x2apic_mode) + return 0; + + if (x2apic_phys || x2apic_fadt_phys()) return 1; return apic == &apic_x2apic_phys; diff --git a/arch/x86/mm/kaslr.c b/arch/x86/mm/kaslr.c index 557f0fe25dff4..37db264866b64 100644 --- a/arch/x86/mm/kaslr.c +++ b/arch/x86/mm/kaslr.c @@ -172,10 +172,10 @@ void __meminit init_trampoline_kaslr(void) set_p4d(p4d_tramp, __p4d(_KERNPG_TABLE | __pa(pud_page_tramp))); - set_pgd(&trampoline_pgd_entry, - __pgd(_KERNPG_TABLE | __pa(p4d_page_tramp))); + trampoline_pgd_entry = + __pgd(_KERNPG_TABLE | __pa(p4d_page_tramp)); } else { - set_pgd(&trampoline_pgd_entry, - __pgd(_KERNPG_TABLE | __pa(pud_page_tramp))); + trampoline_pgd_entry = + __pgd(_KERNPG_TABLE | __pa(pud_page_tramp)); } } diff --git a/arch/x86/net/bpf_jit_comp.c b/arch/x86/net/bpf_jit_comp.c index 99620428ad785..db6053a22e866 100644 --- a/arch/x86/net/bpf_jit_comp.c +++ b/arch/x86/net/bpf_jit_comp.c @@ -2478,7 +2478,7 @@ out_image: } if (bpf_jit_enable > 1) - bpf_jit_dump(prog->len, proglen, pass + 1, image); + bpf_jit_dump(prog->len, proglen, pass + 1, rw_image); if (image) { if (!prog->is_func || extra_pass) { diff --git a/drivers/acpi/acpica/achware.h b/drivers/acpi/acpica/achware.h index 6f2787506b50c..c1b6e7bf4fcb5 100644 --- a/drivers/acpi/acpica/achware.h +++ b/drivers/acpi/acpica/achware.h @@ -101,8 +101,6 @@ acpi_status acpi_hw_get_gpe_status(struct acpi_gpe_event_info *gpe_event_info, acpi_event_status *event_status); -acpi_status acpi_hw_disable_all_gpes(void); - acpi_status acpi_hw_enable_all_runtime_gpes(void); acpi_status acpi_hw_enable_all_wakeup_gpes(void); diff --git a/drivers/acpi/sleep.c b/drivers/acpi/sleep.c index 4ca6672512722..539c12fbd2f14 100644 --- a/drivers/acpi/sleep.c +++ b/drivers/acpi/sleep.c @@ -636,11 +636,19 @@ static int acpi_suspend_enter(suspend_state_t pm_state) } /* - * Disable and clear GPE status before interrupt is enabled. Some GPEs - * (like wakeup GPE) haven't handler, this can avoid such GPE misfire. - * acpi_leave_sleep_state will reenable specific GPEs later + * Disable all GPE and clear their status bits before interrupts are + * enabled. Some GPEs (like wakeup GPEs) have no handlers and this can + * prevent them from producing spurious interrups. + * + * acpi_leave_sleep_state() will reenable specific GPEs later. + * + * Because this code runs on one CPU with disabled interrupts (all of + * the other CPUs are offline at this time), it need not acquire any + * sleeping locks which may trigger an implicit preemption point even + * if there is no contention, so avoid doing that by using a low-level + * library routine here. */ - acpi_disable_all_gpes(); + acpi_hw_disable_all_gpes(); /* Allow EC transactions to happen. */ acpi_ec_unblock_transactions(); diff --git a/drivers/ata/libata-core.c b/drivers/ata/libata-core.c index a5ea144722fa3..0ba0c3d1613f1 100644 --- a/drivers/ata/libata-core.c +++ b/drivers/ata/libata-core.c @@ -5313,7 +5313,7 @@ struct ata_port *ata_port_alloc(struct ata_host *host) mutex_init(&ap->scsi_scan_mutex); INIT_DELAYED_WORK(&ap->hotplug_task, ata_scsi_hotplug); - INIT_WORK(&ap->scsi_rescan_task, ata_scsi_dev_rescan); + INIT_DELAYED_WORK(&ap->scsi_rescan_task, ata_scsi_dev_rescan); INIT_LIST_HEAD(&ap->eh_done_q); init_waitqueue_head(&ap->eh_wait_q); init_completion(&ap->park_req_pending); @@ -5919,6 +5919,7 @@ static void ata_port_detach(struct ata_port *ap) WARN_ON(!(ap->pflags & ATA_PFLAG_UNLOADED)); cancel_delayed_work_sync(&ap->hotplug_task); + cancel_delayed_work_sync(&ap->scsi_rescan_task); skip_eh: /* clean up zpodd on port removal */ diff --git a/drivers/ata/libata-eh.c b/drivers/ata/libata-eh.c index 08e11bc312c28..a3ae5fc2a42fc 100644 --- a/drivers/ata/libata-eh.c +++ b/drivers/ata/libata-eh.c @@ -2973,7 +2973,7 @@ static int ata_eh_revalidate_and_attach(struct ata_link *link, ehc->i.flags |= ATA_EHI_SETMODE; /* schedule the scsi_rescan_device() here */ - schedule_work(&(ap->scsi_rescan_task)); + schedule_delayed_work(&ap->scsi_rescan_task, 0); } else if (dev->class == ATA_DEV_UNKNOWN && ehc->tries[dev->devno] && ata_class_enabled(ehc->classes[dev->devno])) { diff --git a/drivers/ata/libata-scsi.c b/drivers/ata/libata-scsi.c index 39e1ff9b686b9..9c0052d28078a 100644 --- a/drivers/ata/libata-scsi.c +++ b/drivers/ata/libata-scsi.c @@ -4601,10 +4601,11 @@ int ata_scsi_user_scan(struct Scsi_Host *shost, unsigned int channel, void ata_scsi_dev_rescan(struct work_struct *work) { struct ata_port *ap = - container_of(work, struct ata_port, scsi_rescan_task); + container_of(work, struct ata_port, scsi_rescan_task.work); struct ata_link *link; struct ata_device *dev; unsigned long flags; + bool delay_rescan = false; mutex_lock(&ap->scsi_scan_mutex); spin_lock_irqsave(ap->lock, flags); @@ -4618,6 +4619,21 @@ void ata_scsi_dev_rescan(struct work_struct *work) if (scsi_device_get(sdev)) continue; + /* + * If the rescan work was scheduled because of a resume + * event, the port is already fully resumed, but the + * SCSI device may not yet be fully resumed. In such + * case, executing scsi_rescan_device() may cause a + * deadlock with the PM code on device_lock(). Prevent + * this by giving up and retrying rescan after a short + * delay. + */ + delay_rescan = sdev->sdev_gendev.power.is_suspended; + if (delay_rescan) { + scsi_device_put(sdev); + break; + } + spin_unlock_irqrestore(ap->lock, flags); scsi_rescan_device(&(sdev->sdev_gendev)); scsi_device_put(sdev); @@ -4627,4 +4643,8 @@ void ata_scsi_dev_rescan(struct work_struct *work) spin_unlock_irqrestore(ap->lock, flags); mutex_unlock(&ap->scsi_scan_mutex); + + if (delay_rescan) + schedule_delayed_work(&ap->scsi_rescan_task, + msecs_to_jiffies(5)); } diff --git a/drivers/base/regmap/regmap-spi-avmm.c b/drivers/base/regmap/regmap-spi-avmm.c index 4c2b94b3e30be..6af692844c196 100644 --- a/drivers/base/regmap/regmap-spi-avmm.c +++ b/drivers/base/regmap/regmap-spi-avmm.c @@ -660,7 +660,7 @@ static const struct regmap_bus regmap_spi_avmm_bus = { .reg_format_endian_default = REGMAP_ENDIAN_NATIVE, .val_format_endian_default = REGMAP_ENDIAN_NATIVE, .max_raw_read = SPI_AVMM_VAL_SIZE * MAX_READ_CNT, - .max_raw_write = SPI_AVMM_VAL_SIZE * MAX_WRITE_CNT, + .max_raw_write = SPI_AVMM_REG_SIZE + SPI_AVMM_VAL_SIZE * MAX_WRITE_CNT, .free_context = spi_avmm_bridge_ctx_free, }; diff --git a/drivers/block/null_blk/main.c b/drivers/block/null_blk/main.c index c45d09a9a9421..e8cb914223cdf 100644 --- a/drivers/block/null_blk/main.c +++ b/drivers/block/null_blk/main.c @@ -2194,6 +2194,7 @@ static void null_destroy_dev(struct nullb *nullb) struct nullb_device *dev = nullb->dev; null_del_dev(nullb); + null_free_device_storage(dev, false); null_free_dev(dev); } diff --git a/drivers/char/tpm/tpm_crb.c b/drivers/char/tpm/tpm_crb.c index 16fc481d60950..916f4ff246c14 100644 --- a/drivers/char/tpm/tpm_crb.c +++ b/drivers/char/tpm/tpm_crb.c @@ -98,6 +98,8 @@ struct crb_priv { u8 __iomem *rsp; u32 cmd_size; u32 smc_func_id; + u32 __iomem *pluton_start_addr; + u32 __iomem *pluton_reply_addr; }; struct tpm2_crb_smc { @@ -108,6 +110,11 @@ struct tpm2_crb_smc { u32 smc_func_id; }; +struct tpm2_crb_pluton { + u64 start_addr; + u64 reply_addr; +}; + static bool crb_wait_for_reg_32(u32 __iomem *reg, u32 mask, u32 value, unsigned long timeout) { @@ -127,6 +134,25 @@ static bool crb_wait_for_reg_32(u32 __iomem *reg, u32 mask, u32 value, return ((ioread32(reg) & mask) == value); } +static int crb_try_pluton_doorbell(struct crb_priv *priv, bool wait_for_complete) +{ + if (priv->sm != ACPI_TPM2_COMMAND_BUFFER_WITH_PLUTON) + return 0; + + if (!crb_wait_for_reg_32(priv->pluton_reply_addr, ~0, 1, TPM2_TIMEOUT_C)) + return -ETIME; + + iowrite32(1, priv->pluton_start_addr); + if (wait_for_complete == false) + return 0; + + if (!crb_wait_for_reg_32(priv->pluton_start_addr, + 0xffffffff, 0, 200)) + return -ETIME; + + return 0; +} + /** * __crb_go_idle - request tpm crb device to go the idle state * @@ -145,6 +171,8 @@ static bool crb_wait_for_reg_32(u32 __iomem *reg, u32 mask, u32 value, */ static int __crb_go_idle(struct device *dev, struct crb_priv *priv) { + int rc; + if ((priv->sm == ACPI_TPM2_START_METHOD) || (priv->sm == ACPI_TPM2_COMMAND_BUFFER_WITH_START_METHOD) || (priv->sm == ACPI_TPM2_COMMAND_BUFFER_WITH_ARM_SMC)) @@ -152,6 +180,10 @@ static int __crb_go_idle(struct device *dev, struct crb_priv *priv) iowrite32(CRB_CTRL_REQ_GO_IDLE, &priv->regs_t->ctrl_req); + rc = crb_try_pluton_doorbell(priv, true); + if (rc) + return rc; + if (!crb_wait_for_reg_32(&priv->regs_t->ctrl_req, CRB_CTRL_REQ_GO_IDLE/* mask */, 0, /* value */ @@ -188,12 +220,19 @@ static int crb_go_idle(struct tpm_chip *chip) */ static int __crb_cmd_ready(struct device *dev, struct crb_priv *priv) { + int rc; + if ((priv->sm == ACPI_TPM2_START_METHOD) || (priv->sm == ACPI_TPM2_COMMAND_BUFFER_WITH_START_METHOD) || (priv->sm == ACPI_TPM2_COMMAND_BUFFER_WITH_ARM_SMC)) return 0; iowrite32(CRB_CTRL_REQ_CMD_READY, &priv->regs_t->ctrl_req); + + rc = crb_try_pluton_doorbell(priv, true); + if (rc) + return rc; + if (!crb_wait_for_reg_32(&priv->regs_t->ctrl_req, CRB_CTRL_REQ_CMD_READY /* mask */, 0, /* value */ @@ -371,6 +410,10 @@ static int crb_send(struct tpm_chip *chip, u8 *buf, size_t len) return -E2BIG; } + /* Seems to be necessary for every command */ + if (priv->sm == ACPI_TPM2_COMMAND_BUFFER_WITH_PLUTON) + __crb_cmd_ready(&chip->dev, priv); + memcpy_toio(priv->cmd, buf, len); /* Make sure that cmd is populated before issuing start. */ @@ -394,7 +437,10 @@ static int crb_send(struct tpm_chip *chip, u8 *buf, size_t len) rc = tpm_crb_smc_start(&chip->dev, priv->smc_func_id); } - return rc; + if (rc) + return rc; + + return crb_try_pluton_doorbell(priv, false); } static void crb_cancel(struct tpm_chip *chip) @@ -524,15 +570,18 @@ static int crb_map_io(struct acpi_device *device, struct crb_priv *priv, return ret; acpi_dev_free_resource_list(&acpi_resource_list); - if (resource_type(iores_array) != IORESOURCE_MEM) { - dev_err(dev, FW_BUG "TPM2 ACPI table does not define a memory resource\n"); - return -EINVAL; - } else if (resource_type(iores_array + TPM_CRB_MAX_RESOURCES) == - IORESOURCE_MEM) { - dev_warn(dev, "TPM2 ACPI table defines too many memory resources\n"); - memset(iores_array + TPM_CRB_MAX_RESOURCES, - 0, sizeof(*iores_array)); - iores_array[TPM_CRB_MAX_RESOURCES].flags = 0; + /* Pluton doesn't appear to define ACPI memory regions */ + if (priv->sm != ACPI_TPM2_COMMAND_BUFFER_WITH_PLUTON) { + if (resource_type(iores_array) != IORESOURCE_MEM) { + dev_err(dev, FW_BUG "TPM2 ACPI table does not define a memory resource\n"); + return -EINVAL; + } else if (resource_type(iores_array + TPM_CRB_MAX_RESOURCES) == + IORESOURCE_MEM) { + dev_warn(dev, "TPM2 ACPI table defines too many memory resources\n"); + memset(iores_array + TPM_CRB_MAX_RESOURCES, + 0, sizeof(*iores_array)); + iores_array[TPM_CRB_MAX_RESOURCES].flags = 0; + } } iores = NULL; @@ -656,6 +705,22 @@ out_relinquish_locality: return ret; } +static int crb_map_pluton(struct device *dev, struct crb_priv *priv, + struct acpi_table_tpm2 *buf, struct tpm2_crb_pluton *crb_pluton) +{ + priv->pluton_start_addr = crb_map_res(dev, NULL, NULL, + crb_pluton->start_addr, 4); + if (IS_ERR(priv->pluton_start_addr)) + return PTR_ERR(priv->pluton_start_addr); + + priv->pluton_reply_addr = crb_map_res(dev, NULL, NULL, + crb_pluton->reply_addr, 4); + if (IS_ERR(priv->pluton_reply_addr)) + return PTR_ERR(priv->pluton_reply_addr); + + return 0; +} + static int crb_acpi_add(struct acpi_device *device) { struct acpi_table_tpm2 *buf; @@ -663,6 +728,7 @@ static int crb_acpi_add(struct acpi_device *device) struct tpm_chip *chip; struct device *dev = &device->dev; struct tpm2_crb_smc *crb_smc; + struct tpm2_crb_pluton *crb_pluton; acpi_status status; u32 sm; int rc; @@ -700,6 +766,20 @@ static int crb_acpi_add(struct acpi_device *device) priv->smc_func_id = crb_smc->smc_func_id; } + if (sm == ACPI_TPM2_COMMAND_BUFFER_WITH_PLUTON) { + if (buf->header.length < (sizeof(*buf) + sizeof(*crb_pluton))) { + dev_err(dev, + FW_BUG "TPM2 ACPI table has wrong size %u for start method type %d\n", + buf->header.length, + ACPI_TPM2_COMMAND_BUFFER_WITH_PLUTON); + return -EINVAL; + } + crb_pluton = ACPI_ADD_PTR(struct tpm2_crb_pluton, buf, sizeof(*buf)); + rc = crb_map_pluton(dev, priv, buf, crb_pluton); + if (rc) + return rc; + } + priv->sm = sm; priv->hid = acpi_device_hid(device); diff --git a/drivers/char/tpm/tpm_tis_core.c b/drivers/char/tpm/tpm_tis_core.c index e05d2b227de37..55f6ff1e05aa5 100644 --- a/drivers/char/tpm/tpm_tis_core.c +++ b/drivers/char/tpm/tpm_tis_core.c @@ -772,7 +772,9 @@ static irqreturn_t tis_int_handler(int dummy, void *dev_id) wake_up_interruptible(&priv->int_queue); /* Clear interrupts handled with TPM_EOI */ + tpm_tis_request_locality(chip, 0); rc = tpm_tis_write32(priv, TPM_INT_STATUS(priv->locality), interrupt); + tpm_tis_relinquish_locality(chip, 0); if (rc < 0) return IRQ_NONE; diff --git a/drivers/gpio/gpio-sifive.c b/drivers/gpio/gpio-sifive.c index bc5660f61c570..0f1e1226ebbe8 100644 --- a/drivers/gpio/gpio-sifive.c +++ b/drivers/gpio/gpio-sifive.c @@ -221,8 +221,12 @@ static int sifive_gpio_probe(struct platform_device *pdev) return -ENODEV; } - for (i = 0; i < ngpio; i++) - chip->irq_number[i] = platform_get_irq(pdev, i); + for (i = 0; i < ngpio; i++) { + ret = platform_get_irq(pdev, i); + if (ret < 0) + return ret; + chip->irq_number[i] = ret; + } ret = bgpio_init(&chip->gc, dev, 4, chip->base + SIFIVE_GPIO_INPUT_VAL, diff --git a/drivers/gpio/gpiolib.c b/drivers/gpio/gpiolib.c index 5974cfc61b417..6d3e3454a6ed6 100644 --- a/drivers/gpio/gpiolib.c +++ b/drivers/gpio/gpiolib.c @@ -1650,7 +1650,7 @@ static void gpiochip_irqchip_remove(struct gpio_chip *gc) } /* Remove all IRQ mappings and delete the domain */ - if (gc->irq.domain) { + if (!gc->irq.domain_is_allocated_externally && gc->irq.domain) { unsigned int irq; for (offset = 0; offset < gc->ngpio; offset++) { @@ -1696,6 +1696,15 @@ int gpiochip_irqchip_add_domain(struct gpio_chip *gc, gc->to_irq = gpiochip_to_irq; gc->irq.domain = domain; + gc->irq.domain_is_allocated_externally = true; + + /* + * Using barrier() here to prevent compiler from reordering + * gc->irq.initialized before adding irqdomain. + */ + barrier(); + + gc->irq.initialized = true; return 0; } diff --git a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c index 53687de6c0530..91c308cf27eb2 100644 --- a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c +++ b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c @@ -348,6 +348,35 @@ static inline bool is_dc_timing_adjust_needed(struct dm_crtc_state *old_state, return false; } +/** + * update_planes_and_stream_adapter() - Send planes to be updated in DC + * + * DC has a generic way to update planes and stream via + * dc_update_planes_and_stream function; however, DM might need some + * adjustments and preparation before calling it. This function is a wrapper + * for the dc_update_planes_and_stream that does any required configuration + * before passing control to DC. + */ +static inline bool update_planes_and_stream_adapter(struct dc *dc, + int update_type, + int planes_count, + struct dc_stream_state *stream, + struct dc_stream_update *stream_update, + struct dc_surface_update *array_of_surface_update) +{ + /* + * Previous frame finished and HW is ready for optimization. + */ + if (update_type == UPDATE_TYPE_FAST) + dc_post_update_surfaces_to_stream(dc); + + return dc_update_planes_and_stream(dc, + array_of_surface_update, + planes_count, + stream, + stream_update); +} + /** * dm_pflip_high_irq() - Handle pageflip interrupt * @interrupt_params: ignored @@ -2632,10 +2661,13 @@ static void dm_gpureset_commit_state(struct dc_state *dc_state, bundle->surface_updates[m].surface->force_full_update = true; } - dc_commit_updates_for_stream( - dm->dc, bundle->surface_updates, - dc_state->stream_status->plane_count, - dc_state->streams[k], &bundle->stream_update, dc_state); + + update_planes_and_stream_adapter(dm->dc, + UPDATE_TYPE_FULL, + dc_state->stream_status->plane_count, + dc_state->streams[k], + &bundle->stream_update, + bundle->surface_updates); } cleanup: @@ -7870,6 +7902,12 @@ static void amdgpu_dm_commit_planes(struct drm_atomic_state *state, if (acrtc_state->abm_level != dm_old_crtc_state->abm_level) bundle->stream_update.abm_level = &acrtc_state->abm_level; + mutex_lock(&dm->dc_lock); + if ((acrtc_state->update_type > UPDATE_TYPE_FAST) && + acrtc_state->stream->link->psr_settings.psr_allow_active) + amdgpu_dm_psr_disable(acrtc_state->stream); + mutex_unlock(&dm->dc_lock); + /* * If FreeSync state on the stream has changed then we need to * re-adjust the min/max bounds now that DC doesn't handle this @@ -7883,16 +7921,12 @@ static void amdgpu_dm_commit_planes(struct drm_atomic_state *state, spin_unlock_irqrestore(&pcrtc->dev->event_lock, flags); } mutex_lock(&dm->dc_lock); - if ((acrtc_state->update_type > UPDATE_TYPE_FAST) && - acrtc_state->stream->link->psr_settings.psr_allow_active) - amdgpu_dm_psr_disable(acrtc_state->stream); - - dc_commit_updates_for_stream(dm->dc, - bundle->surface_updates, - planes_count, - acrtc_state->stream, - &bundle->stream_update, - dc_state); + update_planes_and_stream_adapter(dm->dc, + acrtc_state->update_type, + planes_count, + acrtc_state->stream, + &bundle->stream_update, + bundle->surface_updates); /** * Enable or disable the interrupts on the backend. @@ -8334,12 +8368,11 @@ static void amdgpu_dm_atomic_commit_tail(struct drm_atomic_state *state) mutex_lock(&dm->dc_lock); - dc_commit_updates_for_stream(dm->dc, - dummy_updates, - status->plane_count, - dm_new_crtc_state->stream, - &stream_update, - dc_state); + dc_update_planes_and_stream(dm->dc, + dummy_updates, + status->plane_count, + dm_new_crtc_state->stream, + &stream_update); mutex_unlock(&dm->dc_lock); } diff --git a/drivers/gpu/drm/exynos/exynos_drm_g2d.c b/drivers/gpu/drm/exynos/exynos_drm_g2d.c index 471fd6c8135f2..27613abeed961 100644 --- a/drivers/gpu/drm/exynos/exynos_drm_g2d.c +++ b/drivers/gpu/drm/exynos/exynos_drm_g2d.c @@ -1335,7 +1335,7 @@ int exynos_g2d_exec_ioctl(struct drm_device *drm_dev, void *data, /* Let the runqueue know that there is work to do. */ queue_work(g2d->g2d_workq, &g2d->runqueue_work); - if (runqueue_node->async) + if (req->async) goto out; wait_for_completion(&runqueue_node->complete); diff --git a/drivers/gpu/drm/exynos/exynos_drm_vidi.c b/drivers/gpu/drm/exynos/exynos_drm_vidi.c index 4d56c8c799c5a..f5e1adfcaa514 100644 --- a/drivers/gpu/drm/exynos/exynos_drm_vidi.c +++ b/drivers/gpu/drm/exynos/exynos_drm_vidi.c @@ -469,8 +469,6 @@ static int vidi_remove(struct platform_device *pdev) if (ctx->raw_edid != (struct edid *)fake_edid_info) { kfree(ctx->raw_edid); ctx->raw_edid = NULL; - - return -EINVAL; } component_del(&pdev->dev, &vidi_component_ops); diff --git a/drivers/gpu/drm/radeon/radeon_gem.c b/drivers/gpu/drm/radeon/radeon_gem.c index 261fcbae88d78..75d79c3110389 100644 --- a/drivers/gpu/drm/radeon/radeon_gem.c +++ b/drivers/gpu/drm/radeon/radeon_gem.c @@ -459,7 +459,6 @@ int radeon_gem_set_domain_ioctl(struct drm_device *dev, void *data, struct radeon_device *rdev = dev->dev_private; struct drm_radeon_gem_set_domain *args = data; struct drm_gem_object *gobj; - struct radeon_bo *robj; int r; /* for now if someone requests domain CPU - @@ -472,13 +471,12 @@ int radeon_gem_set_domain_ioctl(struct drm_device *dev, void *data, up_read(&rdev->exclusive_lock); return -ENOENT; } - robj = gem_to_radeon_bo(gobj); r = radeon_gem_set_domain(gobj, args->read_domains, args->write_domain); drm_gem_object_put(gobj); up_read(&rdev->exclusive_lock); - r = radeon_gem_handle_lockup(robj->rdev, r); + r = radeon_gem_handle_lockup(rdev, r); return r; } diff --git a/drivers/hid/wacom_sys.c b/drivers/hid/wacom_sys.c index fb538a6c4add8..aff4a21a46b6a 100644 --- a/drivers/hid/wacom_sys.c +++ b/drivers/hid/wacom_sys.c @@ -2417,8 +2417,13 @@ static int wacom_parse_and_register(struct wacom *wacom, bool wireless) goto fail_quirks; } - if (features->device_type & WACOM_DEVICETYPE_WL_MONITOR) + if (features->device_type & WACOM_DEVICETYPE_WL_MONITOR) { error = hid_hw_open(hdev); + if (error) { + hid_err(hdev, "hw open failed\n"); + goto fail_quirks; + } + } wacom_set_shared_values(wacom_wac); devres_close_group(&hdev->dev, wacom); diff --git a/drivers/hv/channel_mgmt.c b/drivers/hv/channel_mgmt.c index cc23b90cae02f..d95e567a190d2 100644 --- a/drivers/hv/channel_mgmt.c +++ b/drivers/hv/channel_mgmt.c @@ -829,11 +829,22 @@ static void vmbus_wait_for_unload(void) if (completion_done(&vmbus_connection.unload_event)) goto completed; - for_each_online_cpu(cpu) { + for_each_present_cpu(cpu) { struct hv_per_cpu_context *hv_cpu = per_cpu_ptr(hv_context.cpu_context, cpu); + /* + * In a CoCo VM the synic_message_page is not allocated + * in hv_synic_alloc(). Instead it is set/cleared in + * hv_synic_enable_regs() and hv_synic_disable_regs() + * such that it is set only when the CPU is online. If + * not all present CPUs are online, the message page + * might be NULL, so skip such CPUs. + */ page_addr = hv_cpu->synic_message_page; + if (!page_addr) + continue; + msg = (struct hv_message *)page_addr + VMBUS_MESSAGE_SINT; @@ -867,11 +878,14 @@ completed: * maybe-pending messages on all CPUs to be able to receive new * messages after we reconnect. */ - for_each_online_cpu(cpu) { + for_each_present_cpu(cpu) { struct hv_per_cpu_context *hv_cpu = per_cpu_ptr(hv_context.cpu_context, cpu); page_addr = hv_cpu->synic_message_page; + if (!page_addr) + continue; + msg = (struct hv_message *)page_addr + VMBUS_MESSAGE_SINT; msg->header.message_type = HVMSG_NONE; } diff --git a/drivers/hv/vmbus_drv.c b/drivers/hv/vmbus_drv.c index e592c481f7aee..b03cb7ae7fd38 100644 --- a/drivers/hv/vmbus_drv.c +++ b/drivers/hv/vmbus_drv.c @@ -1504,7 +1504,7 @@ static int vmbus_bus_init(void) ret = cpuhp_setup_state(CPUHP_AP_ONLINE_DYN, "hyperv/vmbus:online", hv_synic_init, hv_synic_cleanup); if (ret < 0) - goto err_cpuhp; + goto err_alloc; hyperv_cpuhp_online = ret; ret = vmbus_connect(); @@ -1555,9 +1555,8 @@ static int vmbus_bus_init(void) err_connect: cpuhp_remove_state(hyperv_cpuhp_online); -err_cpuhp: - hv_synic_free(); err_alloc: + hv_synic_free(); if (vmbus_irq == -1) { hv_remove_vmbus_handler(); } else { diff --git a/drivers/i2c/busses/i2c-imx-lpi2c.c b/drivers/i2c/busses/i2c-imx-lpi2c.c index a49b14d52a986..ff12018bc2060 100644 --- a/drivers/i2c/busses/i2c-imx-lpi2c.c +++ b/drivers/i2c/busses/i2c-imx-lpi2c.c @@ -201,8 +201,8 @@ static void lpi2c_imx_stop(struct lpi2c_imx_struct *lpi2c_imx) /* CLKLO = I2C_CLK_RATIO * CLKHI, SETHOLD = CLKHI, DATAVD = CLKHI/2 */ static int lpi2c_imx_config(struct lpi2c_imx_struct *lpi2c_imx) { - u8 prescale, filt, sethold, clkhi, clklo, datavd; - unsigned int clk_rate, clk_cycle; + u8 prescale, filt, sethold, datavd; + unsigned int clk_rate, clk_cycle, clkhi, clklo; enum lpi2c_imx_pincfg pincfg; unsigned int temp; diff --git a/drivers/i2c/busses/i2c-mchp-pci1xxxx.c b/drivers/i2c/busses/i2c-mchp-pci1xxxx.c index b21ffd6df9276..5ef136c3ecb12 100644 --- a/drivers/i2c/busses/i2c-mchp-pci1xxxx.c +++ b/drivers/i2c/busses/i2c-mchp-pci1xxxx.c @@ -1118,8 +1118,10 @@ static int pci1xxxx_i2c_resume(struct device *dev) static DEFINE_SIMPLE_DEV_PM_OPS(pci1xxxx_i2c_pm_ops, pci1xxxx_i2c_suspend, pci1xxxx_i2c_resume); -static void pci1xxxx_i2c_shutdown(struct pci1xxxx_i2c *i2c) +static void pci1xxxx_i2c_shutdown(void *data) { + struct pci1xxxx_i2c *i2c = data; + pci1xxxx_i2c_config_padctrl(i2c, false); pci1xxxx_i2c_configure_core_reg(i2c, false); } @@ -1156,7 +1158,7 @@ static int pci1xxxx_i2c_probe_pci(struct pci_dev *pdev, init_completion(&i2c->i2c_xfer_done); pci1xxxx_i2c_init(i2c); - ret = devm_add_action(dev, (void (*)(void *))pci1xxxx_i2c_shutdown, i2c); + ret = devm_add_action(dev, pci1xxxx_i2c_shutdown, i2c); if (ret) return ret; diff --git a/drivers/input/misc/soc_button_array.c b/drivers/input/misc/soc_button_array.c index 09489380afda7..e79f5497948b8 100644 --- a/drivers/input/misc/soc_button_array.c +++ b/drivers/input/misc/soc_button_array.c @@ -108,6 +108,27 @@ static const struct dmi_system_id dmi_use_low_level_irq[] = { {} /* Terminating entry */ }; +/* + * Some devices have a wrong entry which points to a GPIO which is + * required in another driver, so this driver must not claim it. + */ +static const struct dmi_system_id dmi_invalid_acpi_index[] = { + { + /* + * Lenovo Yoga Book X90F / X90L, the PNP0C40 home button entry + * points to a GPIO which is not a home button and which is + * required by the lenovo-yogabook driver. + */ + .matches = { + DMI_EXACT_MATCH(DMI_SYS_VENDOR, "Intel Corporation"), + DMI_EXACT_MATCH(DMI_PRODUCT_NAME, "CHERRYVIEW D1 PLATFORM"), + DMI_EXACT_MATCH(DMI_PRODUCT_VERSION, "YETI-11"), + }, + .driver_data = (void *)1l, + }, + {} /* Terminating entry */ +}; + /* * Get the Nth GPIO number from the ACPI object. */ @@ -137,6 +158,8 @@ soc_button_device_create(struct platform_device *pdev, struct platform_device *pd; struct gpio_keys_button *gpio_keys; struct gpio_keys_platform_data *gpio_keys_pdata; + const struct dmi_system_id *dmi_id; + int invalid_acpi_index = -1; int error, gpio, irq; int n_buttons = 0; @@ -154,10 +177,17 @@ soc_button_device_create(struct platform_device *pdev, gpio_keys = (void *)(gpio_keys_pdata + 1); n_buttons = 0; + dmi_id = dmi_first_match(dmi_invalid_acpi_index); + if (dmi_id) + invalid_acpi_index = (long)dmi_id->driver_data; + for (info = button_info; info->name; info++) { if (info->autorepeat != autorepeat) continue; + if (info->acpi_index == invalid_acpi_index) + continue; + error = soc_button_lookup_gpio(&pdev->dev, info->acpi_index, &gpio, &irq); if (error || irq < 0) { /* diff --git a/drivers/media/cec/core/cec-adap.c b/drivers/media/cec/core/cec-adap.c index 4f5ab3cae8a71..b1512f9c5895c 100644 --- a/drivers/media/cec/core/cec-adap.c +++ b/drivers/media/cec/core/cec-adap.c @@ -1090,7 +1090,8 @@ void cec_received_msg_ts(struct cec_adapter *adap, mutex_lock(&adap->lock); dprintk(2, "%s: %*ph\n", __func__, msg->len, msg->msg); - adap->last_initiator = 0xff; + if (!adap->transmit_in_progress) + adap->last_initiator = 0xff; /* Check if this message was for us (directed or broadcast). */ if (!cec_msg_is_broadcast(msg)) @@ -1582,7 +1583,7 @@ static void cec_claim_log_addrs(struct cec_adapter *adap, bool block) * * This function is called with adap->lock held. */ -static int cec_adap_enable(struct cec_adapter *adap) +int cec_adap_enable(struct cec_adapter *adap) { bool enable; int ret = 0; @@ -1592,6 +1593,9 @@ static int cec_adap_enable(struct cec_adapter *adap) if (adap->needs_hpd) enable = enable && adap->phys_addr != CEC_PHYS_ADDR_INVALID; + if (adap->devnode.unregistered) + enable = false; + if (enable == adap->is_enabled) return 0; diff --git a/drivers/media/cec/core/cec-core.c b/drivers/media/cec/core/cec-core.c index af358e901b5f3..7e153c5cad04f 100644 --- a/drivers/media/cec/core/cec-core.c +++ b/drivers/media/cec/core/cec-core.c @@ -191,6 +191,8 @@ static void cec_devnode_unregister(struct cec_adapter *adap) mutex_lock(&adap->lock); __cec_s_phys_addr(adap, CEC_PHYS_ADDR_INVALID, false); __cec_s_log_addrs(adap, NULL, false); + // Disable the adapter (since adap->devnode.unregistered is true) + cec_adap_enable(adap); mutex_unlock(&adap->lock); cdev_device_del(&devnode->cdev, &devnode->dev); diff --git a/drivers/media/cec/core/cec-priv.h b/drivers/media/cec/core/cec-priv.h index b78df931aa74b..ed1f8c67626bf 100644 --- a/drivers/media/cec/core/cec-priv.h +++ b/drivers/media/cec/core/cec-priv.h @@ -47,6 +47,7 @@ int cec_monitor_pin_cnt_inc(struct cec_adapter *adap); void cec_monitor_pin_cnt_dec(struct cec_adapter *adap); int cec_adap_status(struct seq_file *file, void *priv); int cec_thread_func(void *_adap); +int cec_adap_enable(struct cec_adapter *adap); void __cec_s_phys_addr(struct cec_adapter *adap, u16 phys_addr, bool block); int __cec_s_log_addrs(struct cec_adapter *adap, struct cec_log_addrs *log_addrs, bool block); diff --git a/drivers/mmc/host/bcm2835.c b/drivers/mmc/host/bcm2835.c index 641ab4f42125b..c15226be4d2a2 100644 --- a/drivers/mmc/host/bcm2835.c +++ b/drivers/mmc/host/bcm2835.c @@ -1411,8 +1411,8 @@ static int bcm2835_probe(struct platform_device *pdev) host->max_clk = clk_get_rate(clk); host->irq = platform_get_irq(pdev, 0); - if (host->irq <= 0) { - ret = -EINVAL; + if (host->irq < 0) { + ret = host->irq; goto err; } diff --git a/drivers/mmc/host/litex_mmc.c b/drivers/mmc/host/litex_mmc.c index 39c6707fdfdbc..9af6b0902efe1 100644 --- a/drivers/mmc/host/litex_mmc.c +++ b/drivers/mmc/host/litex_mmc.c @@ -649,6 +649,7 @@ static struct platform_driver litex_mmc_driver = { .driver = { .name = "litex-mmc", .of_match_table = litex_match, + .probe_type = PROBE_PREFER_ASYNCHRONOUS, }, }; module_platform_driver(litex_mmc_driver); diff --git a/drivers/mmc/host/meson-gx-mmc.c b/drivers/mmc/host/meson-gx-mmc.c index 5c94ad4661ce3..0f39f86bd0c26 100644 --- a/drivers/mmc/host/meson-gx-mmc.c +++ b/drivers/mmc/host/meson-gx-mmc.c @@ -1007,11 +1007,8 @@ static irqreturn_t meson_mmc_irq(int irq, void *dev_id) if (data && !cmd->error) data->bytes_xfered = data->blksz * data->blocks; - if (meson_mmc_bounce_buf_read(data) || - meson_mmc_get_next_command(cmd)) - ret = IRQ_WAKE_THREAD; - else - ret = IRQ_HANDLED; + + return IRQ_WAKE_THREAD; } out: @@ -1023,9 +1020,6 @@ out: writel(start, host->regs + SD_EMMC_START); } - if (ret == IRQ_HANDLED) - meson_mmc_request_done(host->mmc, cmd->mrq); - return ret; } @@ -1233,8 +1227,8 @@ static int meson_mmc_probe(struct platform_device *pdev) } host->irq = platform_get_irq(pdev, 0); - if (host->irq <= 0) { - ret = -EINVAL; + if (host->irq < 0) { + ret = host->irq; goto free_host; } diff --git a/drivers/mmc/host/mmci.c b/drivers/mmc/host/mmci.c index b9e5dfe74e5c7..1c326e4307f4a 100644 --- a/drivers/mmc/host/mmci.c +++ b/drivers/mmc/host/mmci.c @@ -1735,7 +1735,8 @@ static void mmci_set_max_busy_timeout(struct mmc_host *mmc) return; if (host->variant->busy_timeout && mmc->actual_clock) - max_busy_timeout = ~0UL / (mmc->actual_clock / MSEC_PER_SEC); + max_busy_timeout = U32_MAX / DIV_ROUND_UP(mmc->actual_clock, + MSEC_PER_SEC); mmc->max_busy_timeout = max_busy_timeout; } diff --git a/drivers/mmc/host/mtk-sd.c b/drivers/mmc/host/mtk-sd.c index 26bc59b5a7ccf..425efb3fba048 100644 --- a/drivers/mmc/host/mtk-sd.c +++ b/drivers/mmc/host/mtk-sd.c @@ -2658,7 +2658,7 @@ static int msdc_drv_probe(struct platform_device *pdev) host->irq = platform_get_irq(pdev, 0); if (host->irq < 0) { - ret = -EINVAL; + ret = host->irq; goto host_free; } diff --git a/drivers/mmc/host/mvsdio.c b/drivers/mmc/host/mvsdio.c index 629efbe639c4f..b4f6a0a2fcb51 100644 --- a/drivers/mmc/host/mvsdio.c +++ b/drivers/mmc/host/mvsdio.c @@ -704,7 +704,7 @@ static int mvsd_probe(struct platform_device *pdev) } irq = platform_get_irq(pdev, 0); if (irq < 0) - return -ENXIO; + return irq; mmc = mmc_alloc_host(sizeof(struct mvsd_host), &pdev->dev); if (!mmc) { diff --git a/drivers/mmc/host/omap.c b/drivers/mmc/host/omap.c index 57d39283924da..cc2213ea324f1 100644 --- a/drivers/mmc/host/omap.c +++ b/drivers/mmc/host/omap.c @@ -1343,7 +1343,7 @@ static int mmc_omap_probe(struct platform_device *pdev) irq = platform_get_irq(pdev, 0); if (irq < 0) - return -ENXIO; + return irq; res = platform_get_resource(pdev, IORESOURCE_MEM, 0); host->virt_base = devm_ioremap_resource(&pdev->dev, res); diff --git a/drivers/mmc/host/omap_hsmmc.c b/drivers/mmc/host/omap_hsmmc.c index 4bd7447552055..2db3a16e63c48 100644 --- a/drivers/mmc/host/omap_hsmmc.c +++ b/drivers/mmc/host/omap_hsmmc.c @@ -1791,9 +1791,11 @@ static int omap_hsmmc_probe(struct platform_device *pdev) } res = platform_get_resource(pdev, IORESOURCE_MEM, 0); - irq = platform_get_irq(pdev, 0); - if (res == NULL || irq < 0) + if (!res) return -ENXIO; + irq = platform_get_irq(pdev, 0); + if (irq < 0) + return irq; base = devm_ioremap_resource(&pdev->dev, res); if (IS_ERR(base)) diff --git a/drivers/mmc/host/owl-mmc.c b/drivers/mmc/host/owl-mmc.c index 3dc143b039397..679b8b0b310e5 100644 --- a/drivers/mmc/host/owl-mmc.c +++ b/drivers/mmc/host/owl-mmc.c @@ -638,7 +638,7 @@ static int owl_mmc_probe(struct platform_device *pdev) owl_host->irq = platform_get_irq(pdev, 0); if (owl_host->irq < 0) { - ret = -EINVAL; + ret = owl_host->irq; goto err_release_channel; } diff --git a/drivers/mmc/host/sdhci-acpi.c b/drivers/mmc/host/sdhci-acpi.c index 4cca4c90769bc..b917060a258a4 100644 --- a/drivers/mmc/host/sdhci-acpi.c +++ b/drivers/mmc/host/sdhci-acpi.c @@ -829,7 +829,7 @@ static int sdhci_acpi_probe(struct platform_device *pdev) host->ops = &sdhci_acpi_ops_dflt; host->irq = platform_get_irq(pdev, 0); if (host->irq < 0) { - err = -EINVAL; + err = host->irq; goto err_free; } diff --git a/drivers/mmc/host/sdhci-msm.c b/drivers/mmc/host/sdhci-msm.c index 3a091a387ecbc..a5ab2af3e5201 100644 --- a/drivers/mmc/host/sdhci-msm.c +++ b/drivers/mmc/host/sdhci-msm.c @@ -2486,6 +2486,9 @@ static inline void sdhci_msm_get_of_property(struct platform_device *pdev, msm_host->ddr_config = DDR_CONFIG_POR_VAL; of_property_read_u32(node, "qcom,dll-config", &msm_host->dll_config); + + if (of_device_is_compatible(node, "qcom,msm8916-sdhci")) + host->quirks2 |= SDHCI_QUIRK2_BROKEN_64_BIT_DMA; } static int sdhci_msm_gcc_reset(struct device *dev, struct sdhci_host *host) diff --git a/drivers/mmc/host/sdhci-spear.c b/drivers/mmc/host/sdhci-spear.c index d463e2fd5b1a8..c79035727b20b 100644 --- a/drivers/mmc/host/sdhci-spear.c +++ b/drivers/mmc/host/sdhci-spear.c @@ -65,8 +65,8 @@ static int sdhci_probe(struct platform_device *pdev) host->hw_name = "sdhci"; host->ops = &sdhci_pltfm_ops; host->irq = platform_get_irq(pdev, 0); - if (host->irq <= 0) { - ret = -EINVAL; + if (host->irq < 0) { + ret = host->irq; goto err_host; } host->quirks = SDHCI_QUIRK_BROKEN_ADMA; diff --git a/drivers/mmc/host/sh_mmcif.c b/drivers/mmc/host/sh_mmcif.c index 0fd4c9d644dd5..5cf53348372a4 100644 --- a/drivers/mmc/host/sh_mmcif.c +++ b/drivers/mmc/host/sh_mmcif.c @@ -1400,7 +1400,7 @@ static int sh_mmcif_probe(struct platform_device *pdev) irq[0] = platform_get_irq(pdev, 0); irq[1] = platform_get_irq_optional(pdev, 1); if (irq[0] < 0) - return -ENXIO; + return irq[0]; reg = devm_platform_ioremap_resource(pdev, 0); if (IS_ERR(reg)) diff --git a/drivers/mmc/host/sunxi-mmc.c b/drivers/mmc/host/sunxi-mmc.c index 3db9f32d6a7b9..69dcb8805e05f 100644 --- a/drivers/mmc/host/sunxi-mmc.c +++ b/drivers/mmc/host/sunxi-mmc.c @@ -1350,8 +1350,8 @@ static int sunxi_mmc_resource_request(struct sunxi_mmc_host *host, return ret; host->irq = platform_get_irq(pdev, 0); - if (host->irq <= 0) { - ret = -EINVAL; + if (host->irq < 0) { + ret = host->irq; goto error_disable_mmc; } diff --git a/drivers/mmc/host/usdhi6rol0.c b/drivers/mmc/host/usdhi6rol0.c index 99515be6e5e57..2032e4e1ee68b 100644 --- a/drivers/mmc/host/usdhi6rol0.c +++ b/drivers/mmc/host/usdhi6rol0.c @@ -1757,8 +1757,10 @@ static int usdhi6_probe(struct platform_device *pdev) irq_cd = platform_get_irq_byname(pdev, "card detect"); irq_sd = platform_get_irq_byname(pdev, "data"); irq_sdio = platform_get_irq_byname(pdev, "SDIO"); - if (irq_sd < 0 || irq_sdio < 0) - return -ENODEV; + if (irq_sd < 0) + return irq_sd; + if (irq_sdio < 0) + return irq_sdio; mmc = mmc_alloc_host(sizeof(struct usdhi6_host), dev); if (!mmc) diff --git a/drivers/net/dsa/mt7530.c b/drivers/net/dsa/mt7530.c index 855220c5ce339..51d2ef0dc835c 100644 --- a/drivers/net/dsa/mt7530.c +++ b/drivers/net/dsa/mt7530.c @@ -998,6 +998,18 @@ unlock_exit: mutex_unlock(&priv->reg_mutex); } +static void +mt753x_trap_frames(struct mt7530_priv *priv) +{ + /* Trap BPDUs to the CPU port(s) */ + mt7530_rmw(priv, MT753X_BPC, MT753X_BPDU_PORT_FW_MASK, + MT753X_BPDU_CPU_ONLY); + + /* Trap LLDP frames with :0E MAC DA to the CPU port(s) */ + mt7530_rmw(priv, MT753X_RGAC2, MT753X_R0E_PORT_FW_MASK, + MT753X_R0E_PORT_FW(MT753X_BPDU_CPU_ONLY)); +} + static int mt753x_cpu_port_enable(struct dsa_switch *ds, int port) { @@ -1020,7 +1032,7 @@ mt753x_cpu_port_enable(struct dsa_switch *ds, int port) UNU_FFP(BIT(port))); /* Set CPU port number */ - if (priv->id == ID_MT7621) + if (priv->id == ID_MT7530 || priv->id == ID_MT7621) mt7530_rmw(priv, MT7530_MFC, CPU_MASK, CPU_EN | CPU_PORT(port)); /* CPU port gets connected to all user ports of @@ -2219,6 +2231,8 @@ mt7530_setup(struct dsa_switch *ds) priv->p6_interface = PHY_INTERFACE_MODE_NA; + mt753x_trap_frames(priv); + /* Enable and reset MIB counters */ mt7530_mib_reset(ds); @@ -2325,8 +2339,8 @@ mt7531_setup_common(struct dsa_switch *ds) BIT(cpu_dp->index)); break; } - mt7530_rmw(priv, MT753X_BPC, MT753X_BPDU_PORT_FW_MASK, - MT753X_BPDU_CPU_ONLY); + + mt753x_trap_frames(priv); /* Enable and reset MIB counters */ mt7530_mib_reset(ds); diff --git a/drivers/net/dsa/mt7530.h b/drivers/net/dsa/mt7530.h index e8d9664353504..9a45663d8b4ef 100644 --- a/drivers/net/dsa/mt7530.h +++ b/drivers/net/dsa/mt7530.h @@ -65,6 +65,11 @@ enum mt753x_id { #define MT753X_BPC 0x24 #define MT753X_BPDU_PORT_FW_MASK GENMASK(2, 0) +/* Register for :03 and :0E MAC DA frame control */ +#define MT753X_RGAC2 0x2c +#define MT753X_R0E_PORT_FW_MASK GENMASK(18, 16) +#define MT753X_R0E_PORT_FW(x) FIELD_PREP(MT753X_R0E_PORT_FW_MASK, x) + enum mt753x_bpdu_port_fw { MT753X_BPDU_FOLLOW_MFC, MT753X_BPDU_CPU_EXCLUDE = 4, diff --git a/drivers/net/ethernet/emulex/benet/be_main.c b/drivers/net/ethernet/emulex/benet/be_main.c index a92a747615466..5d39df8452653 100644 --- a/drivers/net/ethernet/emulex/benet/be_main.c +++ b/drivers/net/ethernet/emulex/benet/be_main.c @@ -1136,8 +1136,8 @@ static struct sk_buff *be_lancer_xmit_workarounds(struct be_adapter *adapter, eth_hdr_len = ntohs(skb->protocol) == ETH_P_8021Q ? VLAN_ETH_HLEN : ETH_HLEN; if (skb->len <= 60 && - (lancer_chip(adapter) || skb_vlan_tag_present(skb)) && - is_ipv4_pkt(skb)) { + (lancer_chip(adapter) || BE3_chip(adapter) || + skb_vlan_tag_present(skb)) && is_ipv4_pkt(skb)) { ip = (struct iphdr *)ip_hdr(skb); pskb_trim(skb, eth_hdr_len + ntohs(ip->tot_len)); } diff --git a/drivers/net/ethernet/mellanox/mlx5/core/steering/dr_action.c b/drivers/net/ethernet/mellanox/mlx5/core/steering/dr_action.c index b1dfad274a39e..a3e7602b044e5 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/steering/dr_action.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/steering/dr_action.c @@ -1200,9 +1200,13 @@ dr_action_create_reformat_action(struct mlx5dr_domain *dmn, } case DR_ACTION_TYP_TNL_L3_TO_L2: { - u8 hw_actions[ACTION_CACHE_LINE_SIZE] = {}; + u8 *hw_actions; int ret; + hw_actions = kzalloc(ACTION_CACHE_LINE_SIZE, GFP_KERNEL); + if (!hw_actions) + return -ENOMEM; + ret = mlx5dr_ste_set_action_decap_l3_list(dmn->ste_ctx, data, data_sz, hw_actions, @@ -1210,6 +1214,7 @@ dr_action_create_reformat_action(struct mlx5dr_domain *dmn, &action->rewrite->num_of_actions); if (ret) { mlx5dr_dbg(dmn, "Failed creating decap l3 action list\n"); + kfree(hw_actions); return ret; } @@ -1217,6 +1222,7 @@ dr_action_create_reformat_action(struct mlx5dr_domain *dmn, DR_CHUNK_SIZE_8); if (!action->rewrite->chunk) { mlx5dr_dbg(dmn, "Failed allocating modify header chunk\n"); + kfree(hw_actions); return -ENOMEM; } @@ -1230,6 +1236,7 @@ dr_action_create_reformat_action(struct mlx5dr_domain *dmn, if (ret) { mlx5dr_dbg(dmn, "Writing decap l3 actions to ICM failed\n"); mlx5dr_icm_free_chunk(action->rewrite->chunk); + kfree(hw_actions); return ret; } return 0; diff --git a/drivers/net/ethernet/qualcomm/qca_spi.c b/drivers/net/ethernet/qualcomm/qca_spi.c index c865a4be05eec..4a1b94e5a8ea9 100644 --- a/drivers/net/ethernet/qualcomm/qca_spi.c +++ b/drivers/net/ethernet/qualcomm/qca_spi.c @@ -582,8 +582,7 @@ qcaspi_spi_thread(void *data) while (!kthread_should_stop()) { set_current_state(TASK_INTERRUPTIBLE); if ((qca->intr_req == qca->intr_svc) && - (qca->txr.skb[qca->txr.head] == NULL) && - (qca->sync == QCASPI_SYNC_READY)) + !qca->txr.skb[qca->txr.head]) schedule(); set_current_state(TASK_RUNNING); diff --git a/drivers/net/ethernet/sfc/ef10.c b/drivers/net/ethernet/sfc/ef10.c index d30459dbfe8f8..b63e47af63655 100644 --- a/drivers/net/ethernet/sfc/ef10.c +++ b/drivers/net/ethernet/sfc/ef10.c @@ -2950,7 +2950,7 @@ static u32 efx_ef10_extract_event_ts(efx_qword_t *event) return tstamp; } -static void +static int efx_ef10_handle_tx_event(struct efx_channel *channel, efx_qword_t *event) { struct efx_nic *efx = channel->efx; @@ -2958,13 +2958,14 @@ efx_ef10_handle_tx_event(struct efx_channel *channel, efx_qword_t *event) unsigned int tx_ev_desc_ptr; unsigned int tx_ev_q_label; unsigned int tx_ev_type; + int work_done; u64 ts_part; if (unlikely(READ_ONCE(efx->reset_pending))) - return; + return 0; if (unlikely(EFX_QWORD_FIELD(*event, ESF_DZ_TX_DROP_EVENT))) - return; + return 0; /* Get the transmit queue */ tx_ev_q_label = EFX_QWORD_FIELD(*event, ESF_DZ_TX_QLABEL); @@ -2973,8 +2974,7 @@ efx_ef10_handle_tx_event(struct efx_channel *channel, efx_qword_t *event) if (!tx_queue->timestamping) { /* Transmit completion */ tx_ev_desc_ptr = EFX_QWORD_FIELD(*event, ESF_DZ_TX_DESCR_INDX); - efx_xmit_done(tx_queue, tx_ev_desc_ptr & tx_queue->ptr_mask); - return; + return efx_xmit_done(tx_queue, tx_ev_desc_ptr & tx_queue->ptr_mask); } /* Transmit timestamps are only available for 8XXX series. They result @@ -3000,6 +3000,7 @@ efx_ef10_handle_tx_event(struct efx_channel *channel, efx_qword_t *event) * fields in the event. */ tx_ev_type = EFX_QWORD_FIELD(*event, ESF_EZ_TX_SOFT1); + work_done = 0; switch (tx_ev_type) { case TX_TIMESTAMP_EVENT_TX_EV_COMPLETION: @@ -3016,6 +3017,7 @@ efx_ef10_handle_tx_event(struct efx_channel *channel, efx_qword_t *event) tx_queue->completed_timestamp_major = ts_part; efx_xmit_done_single(tx_queue); + work_done = 1; break; default: @@ -3026,6 +3028,8 @@ efx_ef10_handle_tx_event(struct efx_channel *channel, efx_qword_t *event) EFX_QWORD_VAL(*event)); break; } + + return work_done; } static void @@ -3081,13 +3085,16 @@ static void efx_ef10_handle_driver_generated_event(struct efx_channel *channel, } } +#define EFX_NAPI_MAX_TX 512 + static int efx_ef10_ev_process(struct efx_channel *channel, int quota) { struct efx_nic *efx = channel->efx; efx_qword_t event, *p_event; unsigned int read_ptr; - int ev_code; + int spent_tx = 0; int spent = 0; + int ev_code; if (quota <= 0) return spent; @@ -3126,7 +3133,11 @@ static int efx_ef10_ev_process(struct efx_channel *channel, int quota) } break; case ESE_DZ_EV_CODE_TX_EV: - efx_ef10_handle_tx_event(channel, &event); + spent_tx += efx_ef10_handle_tx_event(channel, &event); + if (spent_tx >= EFX_NAPI_MAX_TX) { + spent = quota; + goto out; + } break; case ESE_DZ_EV_CODE_DRIVER_EV: efx_ef10_handle_driver_event(channel, &event); diff --git a/drivers/net/ethernet/sfc/ef100_nic.c b/drivers/net/ethernet/sfc/ef100_nic.c index ad686c671ab89..fa1f7039a8e28 100644 --- a/drivers/net/ethernet/sfc/ef100_nic.c +++ b/drivers/net/ethernet/sfc/ef100_nic.c @@ -242,6 +242,8 @@ static void ef100_ev_read_ack(struct efx_channel *channel) efx_reg(channel->efx, ER_GZ_EVQ_INT_PRIME)); } +#define EFX_NAPI_MAX_TX 512 + static int ef100_ev_process(struct efx_channel *channel, int quota) { struct efx_nic *efx = channel->efx; @@ -249,6 +251,7 @@ static int ef100_ev_process(struct efx_channel *channel, int quota) bool evq_phase, old_evq_phase; unsigned int read_ptr; efx_qword_t *p_event; + int spent_tx = 0; int spent = 0; bool ev_phase; int ev_type; @@ -284,7 +287,9 @@ static int ef100_ev_process(struct efx_channel *channel, int quota) efx_mcdi_process_event(channel, p_event); break; case ESE_GZ_EF100_EV_TX_COMPLETION: - ef100_ev_tx(channel, p_event); + spent_tx += ef100_ev_tx(channel, p_event); + if (spent_tx >= EFX_NAPI_MAX_TX) + spent = quota; break; case ESE_GZ_EF100_EV_DRIVER: netif_info(efx, drv, efx->net_dev, diff --git a/drivers/net/ethernet/sfc/ef100_tx.c b/drivers/net/ethernet/sfc/ef100_tx.c index 29ffaf35559d6..849e5555bd128 100644 --- a/drivers/net/ethernet/sfc/ef100_tx.c +++ b/drivers/net/ethernet/sfc/ef100_tx.c @@ -346,7 +346,7 @@ void ef100_tx_write(struct efx_tx_queue *tx_queue) ef100_tx_push_buffers(tx_queue); } -void ef100_ev_tx(struct efx_channel *channel, const efx_qword_t *p_event) +int ef100_ev_tx(struct efx_channel *channel, const efx_qword_t *p_event) { unsigned int tx_done = EFX_QWORD_FIELD(*p_event, ESF_GZ_EV_TXCMPL_NUM_DESC); @@ -357,7 +357,7 @@ void ef100_ev_tx(struct efx_channel *channel, const efx_qword_t *p_event) unsigned int tx_index = (tx_queue->read_count + tx_done - 1) & tx_queue->ptr_mask; - efx_xmit_done(tx_queue, tx_index); + return efx_xmit_done(tx_queue, tx_index); } /* Add a socket buffer to a TX queue diff --git a/drivers/net/ethernet/sfc/ef100_tx.h b/drivers/net/ethernet/sfc/ef100_tx.h index e9e11540fcdea..d9a0819c5a72c 100644 --- a/drivers/net/ethernet/sfc/ef100_tx.h +++ b/drivers/net/ethernet/sfc/ef100_tx.h @@ -20,7 +20,7 @@ void ef100_tx_init(struct efx_tx_queue *tx_queue); void ef100_tx_write(struct efx_tx_queue *tx_queue); unsigned int ef100_tx_max_skb_descs(struct efx_nic *efx); -void ef100_ev_tx(struct efx_channel *channel, const efx_qword_t *p_event); +int ef100_ev_tx(struct efx_channel *channel, const efx_qword_t *p_event); netdev_tx_t ef100_enqueue_skb(struct efx_tx_queue *tx_queue, struct sk_buff *skb); int __ef100_enqueue_skb(struct efx_tx_queue *tx_queue, struct sk_buff *skb, diff --git a/drivers/net/ethernet/sfc/tx_common.c b/drivers/net/ethernet/sfc/tx_common.c index 67e789b96c437..755aa92bf8236 100644 --- a/drivers/net/ethernet/sfc/tx_common.c +++ b/drivers/net/ethernet/sfc/tx_common.c @@ -249,7 +249,7 @@ void efx_xmit_done_check_empty(struct efx_tx_queue *tx_queue) } } -void efx_xmit_done(struct efx_tx_queue *tx_queue, unsigned int index) +int efx_xmit_done(struct efx_tx_queue *tx_queue, unsigned int index) { unsigned int fill_level, pkts_compl = 0, bytes_compl = 0; unsigned int efv_pkts_compl = 0; @@ -279,6 +279,8 @@ void efx_xmit_done(struct efx_tx_queue *tx_queue, unsigned int index) } efx_xmit_done_check_empty(tx_queue); + + return pkts_compl + efv_pkts_compl; } /* Remove buffers put into a tx_queue for the current packet. diff --git a/drivers/net/ethernet/sfc/tx_common.h b/drivers/net/ethernet/sfc/tx_common.h index d87aecbc7bf1a..1e9f42938aac9 100644 --- a/drivers/net/ethernet/sfc/tx_common.h +++ b/drivers/net/ethernet/sfc/tx_common.h @@ -28,7 +28,7 @@ static inline bool efx_tx_buffer_in_use(struct efx_tx_buffer *buffer) } void efx_xmit_done_check_empty(struct efx_tx_queue *tx_queue); -void efx_xmit_done(struct efx_tx_queue *tx_queue, unsigned int index); +int efx_xmit_done(struct efx_tx_queue *tx_queue, unsigned int index); void efx_enqueue_unwind(struct efx_tx_queue *tx_queue, unsigned int insert_count); diff --git a/drivers/net/ieee802154/mac802154_hwsim.c b/drivers/net/ieee802154/mac802154_hwsim.c index 2f0544dd7c2ad..9b3da61840a8f 100644 --- a/drivers/net/ieee802154/mac802154_hwsim.c +++ b/drivers/net/ieee802154/mac802154_hwsim.c @@ -522,7 +522,7 @@ static int hwsim_del_edge_nl(struct sk_buff *msg, struct genl_info *info) static int hwsim_set_edge_lqi(struct sk_buff *msg, struct genl_info *info) { struct nlattr *edge_attrs[MAC802154_HWSIM_EDGE_ATTR_MAX + 1]; - struct hwsim_edge_info *einfo; + struct hwsim_edge_info *einfo, *einfo_old; struct hwsim_phy *phy_v0; struct hwsim_edge *e; u32 v0, v1; @@ -560,8 +560,10 @@ static int hwsim_set_edge_lqi(struct sk_buff *msg, struct genl_info *info) list_for_each_entry_rcu(e, &phy_v0->edges, list) { if (e->endpoint->idx == v1) { einfo->lqi = lqi; - rcu_assign_pointer(e->info, einfo); + einfo_old = rcu_replace_pointer(e->info, einfo, + lockdep_is_held(&hwsim_phys_lock)); rcu_read_unlock(); + kfree_rcu(einfo_old, rcu); mutex_unlock(&hwsim_phys_lock); return 0; } diff --git a/drivers/net/phy/dp83867.c b/drivers/net/phy/dp83867.c index 14990f8462ae3..f7436191fa807 100644 --- a/drivers/net/phy/dp83867.c +++ b/drivers/net/phy/dp83867.c @@ -905,7 +905,7 @@ static int dp83867_phy_reset(struct phy_device *phydev) { int err; - err = phy_write(phydev, DP83867_CTRL, DP83867_SW_RESTART); + err = phy_write(phydev, DP83867_CTRL, DP83867_SW_RESET); if (err < 0) return err; diff --git a/drivers/net/wireless/intel/iwlwifi/pcie/drv.c b/drivers/net/wireless/intel/iwlwifi/pcie/drv.c index 03e8234d03520..f6872b2a0d9d0 100644 --- a/drivers/net/wireless/intel/iwlwifi/pcie/drv.c +++ b/drivers/net/wireless/intel/iwlwifi/pcie/drv.c @@ -547,6 +547,8 @@ static const struct iwl_dev_info iwl_dev_info_table[] = { IWL_DEV_INFO(0x54F0, 0x1692, iwlax411_2ax_cfg_so_gf4_a0, iwl_ax411_killer_1690i_name), IWL_DEV_INFO(0x7A70, 0x1691, iwlax411_2ax_cfg_so_gf4_a0, iwl_ax411_killer_1690s_name), IWL_DEV_INFO(0x7A70, 0x1692, iwlax411_2ax_cfg_so_gf4_a0, iwl_ax411_killer_1690i_name), + IWL_DEV_INFO(0x7AF0, 0x1691, iwlax411_2ax_cfg_so_gf4_a0, iwl_ax411_killer_1690s_name), + IWL_DEV_INFO(0x7AF0, 0x1692, iwlax411_2ax_cfg_so_gf4_a0, iwl_ax411_killer_1690i_name), IWL_DEV_INFO(0x271C, 0x0214, iwl9260_2ac_cfg, iwl9260_1_name), IWL_DEV_INFO(0x7E40, 0x1691, iwl_cfg_ma_a0_gf4_a0, iwl_ax411_killer_1690s_name), diff --git a/drivers/nfc/nfcsim.c b/drivers/nfc/nfcsim.c index 85bf8d586c707..0f6befe8be1e2 100644 --- a/drivers/nfc/nfcsim.c +++ b/drivers/nfc/nfcsim.c @@ -336,10 +336,6 @@ static struct dentry *nfcsim_debugfs_root; static void nfcsim_debugfs_init(void) { nfcsim_debugfs_root = debugfs_create_dir("nfcsim", NULL); - - if (!nfcsim_debugfs_root) - pr_err("Could not create debugfs entry\n"); - } static void nfcsim_debugfs_remove(void) diff --git a/drivers/nvme/host/core.c b/drivers/nvme/host/core.c index 2e22c78991ccf..a7d9b5b42b388 100644 --- a/drivers/nvme/host/core.c +++ b/drivers/nvme/host/core.c @@ -395,7 +395,16 @@ void nvme_complete_rq(struct request *req) trace_nvme_complete_rq(req); nvme_cleanup_cmd(req); - if (ctrl->kas) + /* + * Completions of long-running commands should not be able to + * defer sending of periodic keep alives, since the controller + * may have completed processing such commands a long time ago + * (arbitrarily close to command submission time). + * req->deadline - req->timeout is the command submission time + * in jiffies. + */ + if (ctrl->kas && + req->deadline - req->timeout >= ctrl->ka_last_check_time) ctrl->comp_seen = true; switch (nvme_decide_disposition(req)) { @@ -1198,9 +1207,25 @@ EXPORT_SYMBOL_NS_GPL(nvme_execute_passthru_rq, NVME_TARGET_PASSTHRU); * The host should send Keep Alive commands at half of the Keep Alive Timeout * accounting for transport roundtrip times [..]. */ +static unsigned long nvme_keep_alive_work_period(struct nvme_ctrl *ctrl) +{ + unsigned long delay = ctrl->kato * HZ / 2; + + /* + * When using Traffic Based Keep Alive, we need to run + * nvme_keep_alive_work at twice the normal frequency, as one + * command completion can postpone sending a keep alive command + * by up to twice the delay between runs. + */ + if (ctrl->ctratt & NVME_CTRL_ATTR_TBKAS) + delay /= 2; + return delay; +} + static void nvme_queue_keep_alive_work(struct nvme_ctrl *ctrl) { - queue_delayed_work(nvme_wq, &ctrl->ka_work, ctrl->kato * HZ / 2); + queue_delayed_work(nvme_wq, &ctrl->ka_work, + nvme_keep_alive_work_period(ctrl)); } static enum rq_end_io_ret nvme_keep_alive_end_io(struct request *rq, @@ -1209,6 +1234,20 @@ static enum rq_end_io_ret nvme_keep_alive_end_io(struct request *rq, struct nvme_ctrl *ctrl = rq->end_io_data; unsigned long flags; bool startka = false; + unsigned long rtt = jiffies - (rq->deadline - rq->timeout); + unsigned long delay = nvme_keep_alive_work_period(ctrl); + + /* + * Subtract off the keepalive RTT so nvme_keep_alive_work runs + * at the desired frequency. + */ + if (rtt <= delay) { + delay -= rtt; + } else { + dev_warn(ctrl->device, "long keepalive RTT (%u ms)\n", + jiffies_to_msecs(rtt)); + delay = 0; + } blk_mq_free_request(rq); @@ -1219,6 +1258,7 @@ static enum rq_end_io_ret nvme_keep_alive_end_io(struct request *rq, return RQ_END_IO_NONE; } + ctrl->ka_last_check_time = jiffies; ctrl->comp_seen = false; spin_lock_irqsave(&ctrl->lock, flags); if (ctrl->state == NVME_CTRL_LIVE || @@ -1226,7 +1266,7 @@ static enum rq_end_io_ret nvme_keep_alive_end_io(struct request *rq, startka = true; spin_unlock_irqrestore(&ctrl->lock, flags); if (startka) - nvme_queue_keep_alive_work(ctrl); + queue_delayed_work(nvme_wq, &ctrl->ka_work, delay); return RQ_END_IO_NONE; } @@ -1237,6 +1277,8 @@ static void nvme_keep_alive_work(struct work_struct *work) bool comp_seen = ctrl->comp_seen; struct request *rq; + ctrl->ka_last_check_time = jiffies; + if ((ctrl->ctratt & NVME_CTRL_ATTR_TBKAS) && comp_seen) { dev_dbg(ctrl->device, "reschedule traffic based keep-alive timer\n"); diff --git a/drivers/nvme/host/nvme.h b/drivers/nvme/host/nvme.h index 3f82de6060ef7..2aa514c3dfa17 100644 --- a/drivers/nvme/host/nvme.h +++ b/drivers/nvme/host/nvme.h @@ -323,6 +323,7 @@ struct nvme_ctrl { struct delayed_work ka_work; struct delayed_work failfast_work; struct nvme_command ka_cmd; + unsigned long ka_last_check_time; struct work_struct fw_act_work; unsigned long events; diff --git a/drivers/pci/controller/pci-hyperv.c b/drivers/pci/controller/pci-hyperv.c index f1ec8931dfbc5..3351863352d36 100644 --- a/drivers/pci/controller/pci-hyperv.c +++ b/drivers/pci/controller/pci-hyperv.c @@ -489,7 +489,10 @@ struct hv_pcibus_device { struct fwnode_handle *fwnode; /* Protocol version negotiated with the host */ enum pci_protocol_version_t protocol_version; + + struct mutex state_lock; enum hv_pcibus_state state; + struct hv_device *hdev; resource_size_t low_mmio_space; resource_size_t high_mmio_space; @@ -553,19 +556,10 @@ struct hv_dr_state { struct hv_pcidev_description func[]; }; -enum hv_pcichild_state { - hv_pcichild_init = 0, - hv_pcichild_requirements, - hv_pcichild_resourced, - hv_pcichild_ejecting, - hv_pcichild_maximum -}; - struct hv_pci_dev { /* List protected by pci_rescan_remove_lock */ struct list_head list_entry; refcount_t refs; - enum hv_pcichild_state state; struct pci_slot *pci_slot; struct hv_pcidev_description desc; bool reported_missing; @@ -656,6 +650,11 @@ static void hv_arch_irq_unmask(struct irq_data *data) pbus = pdev->bus; hbus = container_of(pbus->sysdata, struct hv_pcibus_device, sysdata); int_desc = data->chip_data; + if (!int_desc) { + dev_warn(&hbus->hdev->device, "%s() can not unmask irq %u\n", + __func__, data->irq); + return; + } spin_lock_irqsave(&hbus->retarget_msi_interrupt_lock, flags); @@ -1924,12 +1923,6 @@ static void hv_compose_msi_msg(struct irq_data *data, struct msi_msg *msg) hv_pci_onchannelcallback(hbus); spin_unlock_irqrestore(&channel->sched_lock, flags); - if (hpdev->state == hv_pcichild_ejecting) { - dev_err_once(&hbus->hdev->device, - "the device is being ejected\n"); - goto enable_tasklet; - } - udelay(100); } @@ -2535,6 +2528,8 @@ static void pci_devices_present_work(struct work_struct *work) if (!dr) return; + mutex_lock(&hbus->state_lock); + /* First, mark all existing children as reported missing. */ spin_lock_irqsave(&hbus->device_list_lock, flags); list_for_each_entry(hpdev, &hbus->children, list_entry) { @@ -2616,6 +2611,8 @@ static void pci_devices_present_work(struct work_struct *work) break; } + mutex_unlock(&hbus->state_lock); + kfree(dr); } @@ -2764,7 +2761,7 @@ static void hv_eject_device_work(struct work_struct *work) hpdev = container_of(work, struct hv_pci_dev, wrk); hbus = hpdev->hbus; - WARN_ON(hpdev->state != hv_pcichild_ejecting); + mutex_lock(&hbus->state_lock); /* * Ejection can come before or after the PCI bus has been set up, so @@ -2802,6 +2799,8 @@ static void hv_eject_device_work(struct work_struct *work) put_pcichild(hpdev); put_pcichild(hpdev); /* hpdev has been freed. Do not use it any more. */ + + mutex_unlock(&hbus->state_lock); } /** @@ -2822,7 +2821,6 @@ static void hv_pci_eject_device(struct hv_pci_dev *hpdev) return; } - hpdev->state = hv_pcichild_ejecting; get_pcichild(hpdev); INIT_WORK(&hpdev->wrk, hv_eject_device_work); queue_work(hbus->wq, &hpdev->wrk); @@ -3251,8 +3249,10 @@ static int hv_pci_enter_d0(struct hv_device *hdev) struct pci_bus_d0_entry *d0_entry; struct hv_pci_compl comp_pkt; struct pci_packet *pkt; + bool retry = true; int ret; +enter_d0_retry: /* * Tell the host that the bus is ready to use, and moved into the * powered-on state. This includes telling the host which region @@ -3279,6 +3279,38 @@ static int hv_pci_enter_d0(struct hv_device *hdev) if (ret) goto exit; + /* + * In certain case (Kdump) the pci device of interest was + * not cleanly shut down and resource is still held on host + * side, the host could return invalid device status. + * We need to explicitly request host to release the resource + * and try to enter D0 again. + */ + if (comp_pkt.completion_status < 0 && retry) { + retry = false; + + dev_err(&hdev->device, "Retrying D0 Entry\n"); + + /* + * Hv_pci_bus_exit() calls hv_send_resource_released() + * to free up resources of its child devices. + * In the kdump kernel we need to set the + * wslot_res_allocated to 255 so it scans all child + * devices to release resources allocated in the + * normal kernel before panic happened. + */ + hbus->wslot_res_allocated = 255; + + ret = hv_pci_bus_exit(hdev, true); + + if (ret == 0) { + kfree(pkt); + goto enter_d0_retry; + } + dev_err(&hdev->device, + "Retrying D0 failed with ret %d\n", ret); + } + if (comp_pkt.completion_status < 0) { dev_err(&hdev->device, "PCI Pass-through VSP failed D0 Entry with status %x\n", @@ -3321,6 +3353,24 @@ static int hv_pci_query_relations(struct hv_device *hdev) if (!ret) ret = wait_for_response(hdev, &comp); + /* + * In the case of fast device addition/removal, it's possible that + * vmbus_sendpacket() or wait_for_response() returns -ENODEV but we + * already got a PCI_BUS_RELATIONS* message from the host and the + * channel callback already scheduled a work to hbus->wq, which can be + * running pci_devices_present_work() -> survey_child_resources() -> + * complete(&hbus->survey_event), even after hv_pci_query_relations() + * exits and the stack variable 'comp' is no longer valid; as a result, + * a hang or a page fault may happen when the complete() calls + * raw_spin_lock_irqsave(). Flush hbus->wq before we exit from + * hv_pci_query_relations() to avoid the issues. Note: if 'ret' is + * -ENODEV, there can't be any more work item scheduled to hbus->wq + * after the flush_workqueue(): see vmbus_onoffer_rescind() -> + * vmbus_reset_channel_cb(), vmbus_rescind_cleanup() -> + * channel->rescind = true. + */ + flush_workqueue(hbus->wq); + return ret; } @@ -3506,7 +3556,6 @@ static int hv_pci_probe(struct hv_device *hdev, struct hv_pcibus_device *hbus; u16 dom_req, dom; char *name; - bool enter_d0_retry = true; int ret; /* @@ -3542,6 +3591,7 @@ static int hv_pci_probe(struct hv_device *hdev, return -ENOMEM; hbus->bridge = bridge; + mutex_init(&hbus->state_lock); hbus->state = hv_pcibus_init; hbus->wslot_res_allocated = -1; @@ -3646,49 +3696,15 @@ static int hv_pci_probe(struct hv_device *hdev, if (ret) goto free_fwnode; -retry: ret = hv_pci_query_relations(hdev); if (ret) goto free_irq_domain; - ret = hv_pci_enter_d0(hdev); - /* - * In certain case (Kdump) the pci device of interest was - * not cleanly shut down and resource is still held on host - * side, the host could return invalid device status. - * We need to explicitly request host to release the resource - * and try to enter D0 again. - * Since the hv_pci_bus_exit() call releases structures - * of all its child devices, we need to start the retry from - * hv_pci_query_relations() call, requesting host to send - * the synchronous child device relations message before this - * information is needed in hv_send_resources_allocated() - * call later. - */ - if (ret == -EPROTO && enter_d0_retry) { - enter_d0_retry = false; - - dev_err(&hdev->device, "Retrying D0 Entry\n"); - - /* - * Hv_pci_bus_exit() calls hv_send_resources_released() - * to free up resources of its child devices. - * In the kdump kernel we need to set the - * wslot_res_allocated to 255 so it scans all child - * devices to release resources allocated in the - * normal kernel before panic happened. - */ - hbus->wslot_res_allocated = 255; - ret = hv_pci_bus_exit(hdev, true); - - if (ret == 0) - goto retry; + mutex_lock(&hbus->state_lock); - dev_err(&hdev->device, - "Retrying D0 failed with ret %d\n", ret); - } + ret = hv_pci_enter_d0(hdev); if (ret) - goto free_irq_domain; + goto release_state_lock; ret = hv_pci_allocate_bridge_windows(hbus); if (ret) @@ -3706,12 +3722,15 @@ retry: if (ret) goto free_windows; + mutex_unlock(&hbus->state_lock); return 0; free_windows: hv_pci_free_bridge_windows(hbus); exit_d0: (void) hv_pci_bus_exit(hdev, true); +release_state_lock: + mutex_unlock(&hbus->state_lock); free_irq_domain: irq_domain_remove(hbus->irq_domain); free_fwnode: @@ -3965,20 +3984,26 @@ static int hv_pci_resume(struct hv_device *hdev) if (ret) goto out; + mutex_lock(&hbus->state_lock); + ret = hv_pci_enter_d0(hdev); if (ret) - goto out; + goto release_state_lock; ret = hv_send_resources_allocated(hdev); if (ret) - goto out; + goto release_state_lock; prepopulate_bars(hbus); hv_pci_restore_msi_state(hbus); hbus->state = hv_pcibus_installed; + mutex_unlock(&hbus->state_lock); return 0; + +release_state_lock: + mutex_unlock(&hbus->state_lock); out: vmbus_close(hdev->channel); return ret; diff --git a/drivers/platform/x86/amd/pmf/core.c b/drivers/platform/x86/amd/pmf/core.c index dc9803e1a4b9b..73d2357e32f8e 100644 --- a/drivers/platform/x86/amd/pmf/core.c +++ b/drivers/platform/x86/amd/pmf/core.c @@ -297,6 +297,8 @@ static void amd_pmf_init_features(struct amd_pmf_dev *dev) /* Enable Static Slider */ if (is_apmf_func_supported(dev, APMF_FUNC_STATIC_SLIDER_GRANULAR)) { amd_pmf_init_sps(dev); + dev->pwr_src_notifier.notifier_call = amd_pmf_pwr_src_notify_call; + power_supply_reg_notifier(&dev->pwr_src_notifier); dev_dbg(dev->dev, "SPS enabled and Platform Profiles registered\n"); } @@ -315,8 +317,10 @@ static void amd_pmf_init_features(struct amd_pmf_dev *dev) static void amd_pmf_deinit_features(struct amd_pmf_dev *dev) { - if (is_apmf_func_supported(dev, APMF_FUNC_STATIC_SLIDER_GRANULAR)) + if (is_apmf_func_supported(dev, APMF_FUNC_STATIC_SLIDER_GRANULAR)) { + power_supply_unreg_notifier(&dev->pwr_src_notifier); amd_pmf_deinit_sps(dev); + } if (is_apmf_func_supported(dev, APMF_FUNC_AUTO_MODE)) { amd_pmf_deinit_auto_mode(dev); @@ -399,9 +403,6 @@ static int amd_pmf_probe(struct platform_device *pdev) apmf_install_handler(dev); amd_pmf_dbgfs_register(dev); - dev->pwr_src_notifier.notifier_call = amd_pmf_pwr_src_notify_call; - power_supply_reg_notifier(&dev->pwr_src_notifier); - dev_info(dev->dev, "registered PMF device successfully\n"); return 0; @@ -411,7 +412,6 @@ static int amd_pmf_remove(struct platform_device *pdev) { struct amd_pmf_dev *dev = platform_get_drvdata(pdev); - power_supply_unreg_notifier(&dev->pwr_src_notifier); amd_pmf_deinit_features(dev); apmf_acpi_deinit(dev); amd_pmf_dbgfs_unregister(dev); diff --git a/drivers/s390/cio/device.c b/drivers/s390/cio/device.c index ba4c69226c337..02813b63f90fd 100644 --- a/drivers/s390/cio/device.c +++ b/drivers/s390/cio/device.c @@ -1367,6 +1367,7 @@ void ccw_device_set_notoper(struct ccw_device *cdev) enum io_sch_action { IO_SCH_UNREG, IO_SCH_ORPH_UNREG, + IO_SCH_UNREG_CDEV, IO_SCH_ATTACH, IO_SCH_UNREG_ATTACH, IO_SCH_ORPH_ATTACH, @@ -1399,7 +1400,7 @@ static enum io_sch_action sch_get_action(struct subchannel *sch) } if ((sch->schib.pmcw.pam & sch->opm) == 0) { if (ccw_device_notify(cdev, CIO_NO_PATH) != NOTIFY_OK) - return IO_SCH_UNREG; + return IO_SCH_UNREG_CDEV; return IO_SCH_DISC; } if (device_is_disconnected(cdev)) @@ -1461,6 +1462,7 @@ static int io_subchannel_sch_event(struct subchannel *sch, int process) case IO_SCH_ORPH_ATTACH: ccw_device_set_disconnected(cdev); break; + case IO_SCH_UNREG_CDEV: case IO_SCH_UNREG_ATTACH: case IO_SCH_UNREG: if (!cdev) @@ -1494,6 +1496,7 @@ static int io_subchannel_sch_event(struct subchannel *sch, int process) if (rc) goto out; break; + case IO_SCH_UNREG_CDEV: case IO_SCH_UNREG_ATTACH: spin_lock_irqsave(sch->lock, flags); sch_set_cdev(sch, NULL); diff --git a/drivers/soundwire/dmi-quirks.c b/drivers/soundwire/dmi-quirks.c index 58ea013fa918a..2a1096dab63d3 100644 --- a/drivers/soundwire/dmi-quirks.c +++ b/drivers/soundwire/dmi-quirks.c @@ -99,6 +99,13 @@ static const struct dmi_system_id adr_remap_quirk_table[] = { }, .driver_data = (void *)intel_tgl_bios, }, + { + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "HP"), + DMI_MATCH(DMI_BOARD_NAME, "8709"), + }, + .driver_data = (void *)intel_tgl_bios, + }, { /* quirk used for NUC15 'Bishop County' LAPBC510 and LAPBC710 skews */ .matches = { diff --git a/drivers/soundwire/qcom.c b/drivers/soundwire/qcom.c index 21c50972047f5..b2eb3090f4b46 100644 --- a/drivers/soundwire/qcom.c +++ b/drivers/soundwire/qcom.c @@ -1090,8 +1090,10 @@ static int qcom_swrm_startup(struct snd_pcm_substream *substream, } sruntime = sdw_alloc_stream(dai->name); - if (!sruntime) - return -ENOMEM; + if (!sruntime) { + ret = -ENOMEM; + goto err_alloc; + } ctrl->sruntime[dai->id] = sruntime; @@ -1101,12 +1103,19 @@ static int qcom_swrm_startup(struct snd_pcm_substream *substream, if (ret < 0 && ret != -ENOTSUPP) { dev_err(dai->dev, "Failed to set sdw stream on %s\n", codec_dai->name); - sdw_release_stream(sruntime); - return ret; + goto err_set_stream; } } return 0; + +err_set_stream: + sdw_release_stream(sruntime); +err_alloc: + pm_runtime_mark_last_busy(ctrl->dev); + pm_runtime_put_autosuspend(ctrl->dev); + + return ret; } static void qcom_swrm_shutdown(struct snd_pcm_substream *substream, diff --git a/drivers/spi/spi-fsl-lpspi.c b/drivers/spi/spi-fsl-lpspi.c index e8c1c8a4c6c82..9e324d72596af 100644 --- a/drivers/spi/spi-fsl-lpspi.c +++ b/drivers/spi/spi-fsl-lpspi.c @@ -905,9 +905,14 @@ static int fsl_lpspi_probe(struct platform_device *pdev) ret = fsl_lpspi_dma_init(&pdev->dev, fsl_lpspi, controller); if (ret == -EPROBE_DEFER) goto out_pm_get; - if (ret < 0) dev_err(&pdev->dev, "dma setup error %d, use pio\n", ret); + else + /* + * disable LPSPI module IRQ when enable DMA mode successfully, + * to prevent the unexpected LPSPI module IRQ events. + */ + disable_irq(irq); ret = devm_spi_register_controller(&pdev->dev, controller); if (ret < 0) { diff --git a/drivers/spi/spi-geni-qcom.c b/drivers/spi/spi-geni-qcom.c index 4e83cc5b445d8..dd1581893fe72 100644 --- a/drivers/spi/spi-geni-qcom.c +++ b/drivers/spi/spi-geni-qcom.c @@ -595,6 +595,8 @@ static int spi_geni_init(struct spi_geni_master *mas) geni_se_select_mode(se, GENI_GPI_DMA); dev_dbg(mas->dev, "Using GPI DMA mode for SPI\n"); break; + } else if (ret == -EPROBE_DEFER) { + goto out_pm; } /* * in case of failure to get dma channel, we can still do the diff --git a/drivers/target/iscsi/iscsi_target_nego.c b/drivers/target/iscsi/iscsi_target_nego.c index ff49c8f3fe241..62b2d0dcfda86 100644 --- a/drivers/target/iscsi/iscsi_target_nego.c +++ b/drivers/target/iscsi/iscsi_target_nego.c @@ -1128,6 +1128,7 @@ int iscsi_target_locate_portal( iscsi_target_set_sock_callbacks(conn); login->np = np; + conn->tpg = NULL; login_req = (struct iscsi_login_req *) login->req; payload_length = ntoh24(login_req->dlength); @@ -1195,7 +1196,6 @@ int iscsi_target_locate_portal( */ sessiontype = strncmp(s_buf, DISCOVERY, 9); if (!sessiontype) { - conn->tpg = iscsit_global->discovery_tpg; if (!login->leading_connection) goto get_target; @@ -1212,9 +1212,11 @@ int iscsi_target_locate_portal( * Serialize access across the discovery struct iscsi_portal_group to * process login attempt. */ + conn->tpg = iscsit_global->discovery_tpg; if (iscsit_access_np(np, conn->tpg) < 0) { iscsit_tx_login_rsp(conn, ISCSI_STATUS_CLS_TARGET_ERR, ISCSI_LOGIN_STATUS_SVC_UNAVAILABLE); + conn->tpg = NULL; ret = -1; goto out; } diff --git a/drivers/tty/serial/fsl_lpuart.c b/drivers/tty/serial/fsl_lpuart.c index dc7ac1ddbca5e..1093c74b52840 100644 --- a/drivers/tty/serial/fsl_lpuart.c +++ b/drivers/tty/serial/fsl_lpuart.c @@ -257,6 +257,7 @@ struct lpuart_port { unsigned int txfifo_size; unsigned int rxfifo_size; + u8 rx_watermark; bool lpuart_dma_tx_use; bool lpuart_dma_rx_use; struct dma_chan *dma_tx_chan; @@ -283,38 +284,45 @@ struct lpuart_soc_data { enum lpuart_type devtype; char iotype; u8 reg_off; + u8 rx_watermark; }; static const struct lpuart_soc_data vf_data = { .devtype = VF610_LPUART, .iotype = UPIO_MEM, + .rx_watermark = 1, }; static const struct lpuart_soc_data ls1021a_data = { .devtype = LS1021A_LPUART, .iotype = UPIO_MEM32BE, + .rx_watermark = 1, }; static const struct lpuart_soc_data ls1028a_data = { .devtype = LS1028A_LPUART, .iotype = UPIO_MEM32, + .rx_watermark = 0, }; static struct lpuart_soc_data imx7ulp_data = { .devtype = IMX7ULP_LPUART, .iotype = UPIO_MEM32, .reg_off = IMX_REG_OFF, + .rx_watermark = 1, }; static struct lpuart_soc_data imx8qxp_data = { .devtype = IMX8QXP_LPUART, .iotype = UPIO_MEM32, .reg_off = IMX_REG_OFF, + .rx_watermark = 1, }; static struct lpuart_soc_data imxrt1050_data = { .devtype = IMXRT1050_LPUART, .iotype = UPIO_MEM32, .reg_off = IMX_REG_OFF, + .rx_watermark = 1, }; static const struct of_device_id lpuart_dt_ids[] = { @@ -1533,7 +1541,7 @@ static void lpuart_setup_watermark(struct lpuart_port *sport) } writeb(0, sport->port.membase + UARTTWFIFO); - writeb(1, sport->port.membase + UARTRWFIFO); + writeb(sport->rx_watermark, sport->port.membase + UARTRWFIFO); /* Restore cr2 */ writeb(cr2_saved, sport->port.membase + UARTCR2); @@ -1568,7 +1576,8 @@ static void lpuart32_setup_watermark(struct lpuart_port *sport) lpuart32_write(&sport->port, val, UARTFIFO); /* set the watermark */ - val = (0x1 << UARTWATER_RXWATER_OFF) | (0x0 << UARTWATER_TXWATER_OFF); + val = (sport->rx_watermark << UARTWATER_RXWATER_OFF) | + (0x0 << UARTWATER_TXWATER_OFF); lpuart32_write(&sport->port, val, UARTWATER); /* Restore cr2 */ @@ -2714,6 +2723,7 @@ static int lpuart_probe(struct platform_device *pdev) sport->port.dev = &pdev->dev; sport->port.type = PORT_LPUART; sport->devtype = sdata->devtype; + sport->rx_watermark = sdata->rx_watermark; ret = platform_get_irq(pdev, 0); if (ret < 0) return ret; diff --git a/drivers/usb/gadget/udc/amd5536udc_pci.c b/drivers/usb/gadget/udc/amd5536udc_pci.c index c80f9bd51b750..a36913ae31f9e 100644 --- a/drivers/usb/gadget/udc/amd5536udc_pci.c +++ b/drivers/usb/gadget/udc/amd5536udc_pci.c @@ -170,6 +170,9 @@ static int udc_pci_probe( retval = -ENODEV; goto err_probe; } + + udc = dev; + return 0; err_probe: diff --git a/drivers/vhost/net.c b/drivers/vhost/net.c index 4c538b30fd76d..4418192ab8aaa 100644 --- a/drivers/vhost/net.c +++ b/drivers/vhost/net.c @@ -934,13 +934,18 @@ static void handle_tx_zerocopy(struct vhost_net *net, struct socket *sock) err = sock->ops->sendmsg(sock, &msg, len); if (unlikely(err < 0)) { + bool retry = err == -EAGAIN || err == -ENOMEM || err == -ENOBUFS; + if (zcopy_used) { if (vq->heads[ubuf->desc].len == VHOST_DMA_IN_PROGRESS) vhost_net_ubuf_put(ubufs); - nvq->upend_idx = ((unsigned)nvq->upend_idx - 1) - % UIO_MAXIOV; + if (retry) + nvq->upend_idx = ((unsigned)nvq->upend_idx - 1) + % UIO_MAXIOV; + else + vq->heads[ubuf->desc].len = VHOST_DMA_DONE_LEN; } - if (err == -EAGAIN || err == -ENOMEM || err == -ENOBUFS) { + if (retry) { vhost_discard_vq_desc(vq, 1); vhost_net_enable_vq(net, vq); break; diff --git a/drivers/vhost/vdpa.c b/drivers/vhost/vdpa.c index d591f77961aa8..31a156669a531 100644 --- a/drivers/vhost/vdpa.c +++ b/drivers/vhost/vdpa.c @@ -377,7 +377,10 @@ static long vhost_vdpa_set_features(struct vhost_vdpa *v, u64 __user *featurep) { struct vdpa_device *vdpa = v->vdpa; const struct vdpa_config_ops *ops = vdpa->config; + struct vhost_dev *d = &v->vdev; + u64 actual_features; u64 features; + int i; /* * It's not allowed to change the features after they have @@ -392,6 +395,16 @@ static long vhost_vdpa_set_features(struct vhost_vdpa *v, u64 __user *featurep) if (vdpa_set_features(vdpa, features)) return -EINVAL; + /* let the vqs know what has been configured */ + actual_features = ops->get_driver_features(vdpa); + for (i = 0; i < d->nvqs; ++i) { + struct vhost_virtqueue *vq = d->vqs[i]; + + mutex_lock(&vq->mutex); + vq->acked_features = actual_features; + mutex_unlock(&vq->mutex); + } + return 0; } diff --git a/fs/Kconfig b/fs/Kconfig index 2685a4d0d3531..703a1cea0fc09 100644 --- a/fs/Kconfig +++ b/fs/Kconfig @@ -371,14 +371,7 @@ config NFS_V4_2_SSC_HELPER source "net/sunrpc/Kconfig" source "fs/ceph/Kconfig" -source "fs/cifs/Kconfig" -source "fs/ksmbd/Kconfig" - -config SMBFS_COMMON - tristate - default y if CIFS=y || SMB_SERVER=y - default m if CIFS=m || SMB_SERVER=m - +source "fs/smb/Kconfig" source "fs/coda/Kconfig" source "fs/afs/Kconfig" source "fs/9p/Kconfig" diff --git a/fs/Makefile b/fs/Makefile index 80ab0154419ec..9b7bcf6e54cd4 100644 --- a/fs/Makefile +++ b/fs/Makefile @@ -94,9 +94,7 @@ obj-$(CONFIG_LOCKD) += lockd/ obj-$(CONFIG_NLS) += nls/ obj-y += unicode/ obj-$(CONFIG_SYSV_FS) += sysv/ -obj-$(CONFIG_SMBFS_COMMON) += smbfs_common/ -obj-$(CONFIG_CIFS) += cifs/ -obj-$(CONFIG_SMB_SERVER) += ksmbd/ +obj-$(CONFIG_SMBFS) += smb/ obj-$(CONFIG_HPFS_FS) += hpfs/ obj-$(CONFIG_NTFS_FS) += ntfs/ obj-$(CONFIG_NTFS3_FS) += ntfs3/ diff --git a/fs/btrfs/tree-log.c b/fs/btrfs/tree-log.c index e71464c0e4667..00be69ce7b90f 100644 --- a/fs/btrfs/tree-log.c +++ b/fs/btrfs/tree-log.c @@ -6205,7 +6205,7 @@ static int log_delayed_deletions_incremental(struct btrfs_trans_handle *trans, { struct btrfs_root *log = inode->root->log_root; const struct btrfs_delayed_item *curr; - u64 last_range_start; + u64 last_range_start = 0; u64 last_range_end = 0; struct btrfs_key key; diff --git a/fs/cifs/Kconfig b/fs/cifs/Kconfig deleted file mode 100644 index 3b7e3b9e4fd2e..0000000000000 --- a/fs/cifs/Kconfig +++ /dev/null @@ -1,203 +0,0 @@ -# SPDX-License-Identifier: GPL-2.0-only -config CIFS - tristate "SMB3 and CIFS support (advanced network filesystem)" - depends on INET - select NLS - select CRYPTO - select CRYPTO_MD5 - select CRYPTO_SHA256 - select CRYPTO_SHA512 - select CRYPTO_CMAC - select CRYPTO_HMAC - select CRYPTO_AEAD2 - select CRYPTO_CCM - select CRYPTO_GCM - select CRYPTO_ECB - select CRYPTO_AES - select KEYS - select DNS_RESOLVER - select ASN1 - select OID_REGISTRY - help - This is the client VFS module for the SMB3 family of NAS protocols, - (including support for the most recent, most secure dialect SMB3.1.1) - as well as for earlier dialects such as SMB2.1, SMB2 and the older - Common Internet File System (CIFS) protocol. CIFS was the successor - to the original dialect, the Server Message Block (SMB) protocol, the - native file sharing mechanism for most early PC operating systems. - - The SMB3 protocol is supported by most modern operating systems - and NAS appliances (e.g. Samba, Windows 10, Windows Server 2016, - MacOS) and even in the cloud (e.g. Microsoft Azure). - The older CIFS protocol was included in Windows NT4, 2000 and XP (and - later) as well by Samba (which provides excellent CIFS and SMB3 - server support for Linux and many other operating systems). Use of - dialects older than SMB2.1 is often discouraged on public networks. - This module also provides limited support for OS/2 and Windows ME - and similar very old servers. - - This module provides an advanced network file system client - for mounting to SMB3 (and CIFS) compliant servers. It includes - support for DFS (hierarchical name space), secure per-user - session establishment via Kerberos or NTLM or NTLMv2, RDMA - (smbdirect), advanced security features, per-share encryption, - directory leases, safe distributed caching (oplock), optional packet - signing, Unicode and other internationalization improvements. - - In general, the default dialects, SMB3 and later, enable better - performance, security and features, than would be possible with CIFS. - Note that when mounting to Samba, due to the CIFS POSIX extensions, - CIFS mounts can provide slightly better POSIX compatibility - than SMB3 mounts. SMB2/SMB3 mount options are also - slightly simpler (compared to CIFS) due to protocol improvements. - - If you need to mount to Samba, Azure, Macs or Windows from this machine, say Y. - -config CIFS_STATS2 - bool "Extended statistics" - depends on CIFS - default y - help - Enabling this option will allow more detailed statistics on SMB - request timing to be displayed in /proc/fs/cifs/DebugData and also - allow optional logging of slow responses to dmesg (depending on the - value of /proc/fs/cifs/cifsFYI). See Documentation/admin-guide/cifs/usage.rst - for more details. These additional statistics may have a minor effect - on performance and memory utilization. - - If unsure, say Y. - -config CIFS_ALLOW_INSECURE_LEGACY - bool "Support legacy servers which use less secure dialects" - depends on CIFS - default y - help - Modern dialects, SMB2.1 and later (including SMB3 and 3.1.1), have - additional security features, including protection against - man-in-the-middle attacks and stronger crypto hashes, so the use - of legacy dialects (SMB1/CIFS and SMB2.0) is discouraged. - - Disabling this option prevents users from using vers=1.0 or vers=2.0 - on mounts with cifs.ko - - If unsure, say Y. - -config CIFS_UPCALL - bool "Kerberos/SPNEGO advanced session setup" - depends on CIFS - help - Enables an upcall mechanism for CIFS which accesses userspace helper - utilities to provide SPNEGO packaged (RFC 4178) Kerberos tickets - which are needed to mount to certain secure servers (for which more - secure Kerberos authentication is required). If unsure, say Y. - -config CIFS_XATTR - bool "CIFS extended attributes" - depends on CIFS - help - Extended attributes are name:value pairs associated with inodes by - the kernel or by users (see the attr(5) manual page for details). - CIFS maps the name of extended attributes beginning with the user - namespace prefix to SMB/CIFS EAs. EAs are stored on Windows - servers without the user namespace prefix, but their names are - seen by Linux cifs clients prefaced by the user namespace prefix. - The system namespace (used by some filesystems to store ACLs) is - not supported at this time. - - If unsure, say Y. - -config CIFS_POSIX - bool "CIFS POSIX Extensions" - depends on CIFS && CIFS_ALLOW_INSECURE_LEGACY && CIFS_XATTR - help - Enabling this option will cause the cifs client to attempt to - negotiate a newer dialect with servers, such as Samba 3.0.5 - or later, that optionally can handle more POSIX like (rather - than Windows like) file behavior. It also enables - support for POSIX ACLs (getfacl and setfacl) to servers - (such as Samba 3.10 and later) which can negotiate - CIFS POSIX ACL support. If unsure, say N. - -config CIFS_DEBUG - bool "Enable CIFS debugging routines" - default y - depends on CIFS - help - Enabling this option adds helpful debugging messages to - the cifs code which increases the size of the cifs module. - If unsure, say Y. - -config CIFS_DEBUG2 - bool "Enable additional CIFS debugging routines" - depends on CIFS_DEBUG - help - Enabling this option adds a few more debugging routines - to the cifs code which slightly increases the size of - the cifs module and can cause additional logging of debug - messages in some error paths, slowing performance. This - option can be turned off unless you are debugging - cifs problems. If unsure, say N. - -config CIFS_DEBUG_DUMP_KEYS - bool "Dump encryption keys for offline decryption (Unsafe)" - depends on CIFS_DEBUG - help - Enabling this will dump the encryption and decryption keys - used to communicate on an encrypted share connection on the - console. This allows Wireshark to decrypt and dissect - encrypted network captures. Enable this carefully. - If unsure, say N. - -config CIFS_DFS_UPCALL - bool "DFS feature support" - depends on CIFS - help - Distributed File System (DFS) support is used to access shares - transparently in an enterprise name space, even if the share - moves to a different server. This feature also enables - an upcall mechanism for CIFS which contacts userspace helper - utilities to provide server name resolution (host names to - IP addresses) which is needed in order to reconnect to - servers if their addresses change or for implicit mounts of - DFS junction points. If unsure, say Y. - -config CIFS_SWN_UPCALL - bool "SWN feature support" - depends on CIFS - help - The Service Witness Protocol (SWN) is used to get notifications - from a highly available server of resource state changes. This - feature enables an upcall mechanism for CIFS which contacts a - userspace daemon to establish the DCE/RPC connection to retrieve - the cluster available interfaces and resource change notifications. - If unsure, say Y. - -config CIFS_NFSD_EXPORT - bool "Allow nfsd to export CIFS file system" - depends on CIFS && BROKEN - help - Allows NFS server to export a CIFS mounted share (nfsd over cifs) - -config CIFS_SMB_DIRECT - bool "SMB Direct support" - depends on CIFS=m && INFINIBAND && INFINIBAND_ADDR_TRANS || CIFS=y && INFINIBAND=y && INFINIBAND_ADDR_TRANS=y - help - Enables SMB Direct support for SMB 3.0, 3.02 and 3.1.1. - SMB Direct allows transferring SMB packets over RDMA. If unsure, - say Y. - -config CIFS_FSCACHE - bool "Provide CIFS client caching support" - depends on CIFS=m && FSCACHE || CIFS=y && FSCACHE=y - help - Makes CIFS FS-Cache capable. Say Y here if you want your CIFS data - to be cached locally on disk through the general filesystem cache - manager. If unsure, say N. - -config CIFS_ROOT - bool "SMB root file system (Experimental)" - depends on CIFS=y && IP_PNP - help - Enables root file system support over SMB protocol. - - Most people say N here. diff --git a/fs/cifs/Makefile b/fs/cifs/Makefile deleted file mode 100644 index 7c9785973f496..0000000000000 --- a/fs/cifs/Makefile +++ /dev/null @@ -1,34 +0,0 @@ -# SPDX-License-Identifier: GPL-2.0 -# -# Makefile for Linux CIFS/SMB2/SMB3 VFS client -# -ccflags-y += -I$(src) # needed for trace events -obj-$(CONFIG_CIFS) += cifs.o - -cifs-y := trace.o cifsfs.o cifs_debug.o connect.o dir.o file.o \ - inode.o link.o misc.o netmisc.o smbencrypt.o transport.o \ - cached_dir.o cifs_unicode.o nterr.o cifsencrypt.o \ - readdir.o ioctl.o sess.o export.o unc.o winucase.o \ - smb2ops.o smb2maperror.o smb2transport.o \ - smb2misc.o smb2pdu.o smb2inode.o smb2file.o cifsacl.o fs_context.o \ - dns_resolve.o cifs_spnego_negtokeninit.asn1.o asn1.o - -$(obj)/asn1.o: $(obj)/cifs_spnego_negtokeninit.asn1.h - -$(obj)/cifs_spnego_negtokeninit.asn1.o: $(obj)/cifs_spnego_negtokeninit.asn1.c $(obj)/cifs_spnego_negtokeninit.asn1.h - -cifs-$(CONFIG_CIFS_XATTR) += xattr.o - -cifs-$(CONFIG_CIFS_UPCALL) += cifs_spnego.o - -cifs-$(CONFIG_CIFS_DFS_UPCALL) += cifs_dfs_ref.o dfs_cache.o - -cifs-$(CONFIG_CIFS_SWN_UPCALL) += netlink.o cifs_swn.o - -cifs-$(CONFIG_CIFS_FSCACHE) += fscache.o - -cifs-$(CONFIG_CIFS_SMB_DIRECT) += smbdirect.o - -cifs-$(CONFIG_CIFS_ROOT) += cifsroot.o - -cifs-$(CONFIG_CIFS_ALLOW_INSECURE_LEGACY) += smb1ops.o cifssmb.o diff --git a/fs/cifs/asn1.c b/fs/cifs/asn1.c deleted file mode 100644 index b5724ef9f182f..0000000000000 --- a/fs/cifs/asn1.c +++ /dev/null @@ -1,63 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-or-later - -#include -#include -#include -#include "cifsglob.h" -#include "cifs_debug.h" -#include "cifsproto.h" -#include "cifs_spnego_negtokeninit.asn1.h" - -int -decode_negTokenInit(unsigned char *security_blob, int length, - struct TCP_Server_Info *server) -{ - if (asn1_ber_decoder(&cifs_spnego_negtokeninit_decoder, server, - security_blob, length) == 0) - return 1; - else - return 0; -} - -int cifs_gssapi_this_mech(void *context, size_t hdrlen, - unsigned char tag, const void *value, size_t vlen) -{ - enum OID oid; - - oid = look_up_OID(value, vlen); - if (oid != OID_spnego) { - char buf[50]; - - sprint_oid(value, vlen, buf, sizeof(buf)); - cifs_dbg(FYI, "Error decoding negTokenInit header: unexpected OID %s\n", - buf); - return -EBADMSG; - } - return 0; -} - -int cifs_neg_token_init_mech_type(void *context, size_t hdrlen, - unsigned char tag, - const void *value, size_t vlen) -{ - struct TCP_Server_Info *server = context; - enum OID oid; - - oid = look_up_OID(value, vlen); - if (oid == OID_mskrb5) - server->sec_mskerberos = true; - else if (oid == OID_krb5u2u) - server->sec_kerberosu2u = true; - else if (oid == OID_krb5) - server->sec_kerberos = true; - else if (oid == OID_ntlmssp) - server->sec_ntlmssp = true; - else { - char buf[50]; - - sprint_oid(value, vlen, buf, sizeof(buf)); - cifs_dbg(FYI, "Decoding negTokenInit: unsupported OID %s\n", - buf); - } - return 0; -} diff --git a/fs/cifs/cached_dir.c b/fs/cifs/cached_dir.c deleted file mode 100644 index bfc964b36c72e..0000000000000 --- a/fs/cifs/cached_dir.c +++ /dev/null @@ -1,606 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0 -/* - * Functions to handle the cached directory entries - * - * Copyright (c) 2022, Ronnie Sahlberg - */ - -#include -#include "cifsglob.h" -#include "cifsproto.h" -#include "cifs_debug.h" -#include "smb2proto.h" -#include "cached_dir.h" - -static struct cached_fid *init_cached_dir(const char *path); -static void free_cached_dir(struct cached_fid *cfid); -static void smb2_close_cached_fid(struct kref *ref); - -static struct cached_fid *find_or_create_cached_dir(struct cached_fids *cfids, - const char *path, - bool lookup_only) -{ - struct cached_fid *cfid; - - spin_lock(&cfids->cfid_list_lock); - list_for_each_entry(cfid, &cfids->entries, entry) { - if (!strcmp(cfid->path, path)) { - /* - * If it doesn't have a lease it is either not yet - * fully cached or it may be in the process of - * being deleted due to a lease break. - */ - if (!cfid->has_lease) { - spin_unlock(&cfids->cfid_list_lock); - return NULL; - } - kref_get(&cfid->refcount); - spin_unlock(&cfids->cfid_list_lock); - return cfid; - } - } - if (lookup_only) { - spin_unlock(&cfids->cfid_list_lock); - return NULL; - } - if (cfids->num_entries >= MAX_CACHED_FIDS) { - spin_unlock(&cfids->cfid_list_lock); - return NULL; - } - cfid = init_cached_dir(path); - if (cfid == NULL) { - spin_unlock(&cfids->cfid_list_lock); - return NULL; - } - cfid->cfids = cfids; - cfids->num_entries++; - list_add(&cfid->entry, &cfids->entries); - cfid->on_list = true; - kref_get(&cfid->refcount); - spin_unlock(&cfids->cfid_list_lock); - return cfid; -} - -static struct dentry * -path_to_dentry(struct cifs_sb_info *cifs_sb, const char *path) -{ - struct dentry *dentry; - const char *s, *p; - char sep; - - sep = CIFS_DIR_SEP(cifs_sb); - dentry = dget(cifs_sb->root); - s = path; - - do { - struct inode *dir = d_inode(dentry); - struct dentry *child; - - if (!S_ISDIR(dir->i_mode)) { - dput(dentry); - dentry = ERR_PTR(-ENOTDIR); - break; - } - - /* skip separators */ - while (*s == sep) - s++; - if (!*s) - break; - p = s++; - /* next separator */ - while (*s && *s != sep) - s++; - - child = lookup_positive_unlocked(p, dentry, s - p); - dput(dentry); - dentry = child; - } while (!IS_ERR(dentry)); - return dentry; -} - -static const char *path_no_prefix(struct cifs_sb_info *cifs_sb, - const char *path) -{ - size_t len = 0; - - if (!*path) - return path; - - if ((cifs_sb->mnt_cifs_flags & CIFS_MOUNT_USE_PREFIX_PATH) && - cifs_sb->prepath) { - len = strlen(cifs_sb->prepath) + 1; - if (unlikely(len > strlen(path))) - return ERR_PTR(-EINVAL); - } - return path + len; -} - -/* - * Open the and cache a directory handle. - * If error then *cfid is not initialized. - */ -int open_cached_dir(unsigned int xid, struct cifs_tcon *tcon, - const char *path, - struct cifs_sb_info *cifs_sb, - bool lookup_only, struct cached_fid **ret_cfid) -{ - struct cifs_ses *ses; - struct TCP_Server_Info *server; - struct cifs_open_parms oparms; - struct smb2_create_rsp *o_rsp = NULL; - struct smb2_query_info_rsp *qi_rsp = NULL; - int resp_buftype[2]; - struct smb_rqst rqst[2]; - struct kvec rsp_iov[2]; - struct kvec open_iov[SMB2_CREATE_IOV_SIZE]; - struct kvec qi_iov[1]; - int rc, flags = 0; - __le16 *utf16_path = NULL; - u8 oplock = SMB2_OPLOCK_LEVEL_II; - struct cifs_fid *pfid; - struct dentry *dentry = NULL; - struct cached_fid *cfid; - struct cached_fids *cfids; - const char *npath; - - if (tcon == NULL || tcon->cfids == NULL || tcon->nohandlecache || - is_smb1_server(tcon->ses->server)) - return -EOPNOTSUPP; - - ses = tcon->ses; - server = ses->server; - cfids = tcon->cfids; - - if (!server->ops->new_lease_key) - return -EIO; - - if (cifs_sb->root == NULL) - return -ENOENT; - - utf16_path = cifs_convert_path_to_utf16(path, cifs_sb); - if (!utf16_path) - return -ENOMEM; - - cfid = find_or_create_cached_dir(cfids, path, lookup_only); - if (cfid == NULL) { - kfree(utf16_path); - return -ENOENT; - } - /* - * At this point we either have a lease already and we can just - * return it. If not we are guaranteed to be the only thread accessing - * this cfid. - */ - if (cfid->has_lease) { - *ret_cfid = cfid; - kfree(utf16_path); - return 0; - } - - /* - * Skip any prefix paths in @path as lookup_positive_unlocked() ends up - * calling ->lookup() which already adds those through - * build_path_from_dentry(). Also, do it earlier as we might reconnect - * below when trying to send compounded request and then potentially - * having a different prefix path (e.g. after DFS failover). - */ - npath = path_no_prefix(cifs_sb, path); - if (IS_ERR(npath)) { - rc = PTR_ERR(npath); - kfree(utf16_path); - return rc; - } - - /* - * We do not hold the lock for the open because in case - * SMB2_open needs to reconnect. - * This is safe because no other thread will be able to get a ref - * to the cfid until we have finished opening the file and (possibly) - * acquired a lease. - */ - if (smb3_encryption_required(tcon)) - flags |= CIFS_TRANSFORM_REQ; - - pfid = &cfid->fid; - server->ops->new_lease_key(pfid); - - memset(rqst, 0, sizeof(rqst)); - resp_buftype[0] = resp_buftype[1] = CIFS_NO_BUFFER; - memset(rsp_iov, 0, sizeof(rsp_iov)); - - /* Open */ - memset(&open_iov, 0, sizeof(open_iov)); - rqst[0].rq_iov = open_iov; - rqst[0].rq_nvec = SMB2_CREATE_IOV_SIZE; - - oparms = (struct cifs_open_parms) { - .tcon = tcon, - .path = path, - .create_options = cifs_create_options(cifs_sb, CREATE_NOT_FILE), - .desired_access = FILE_READ_ATTRIBUTES, - .disposition = FILE_OPEN, - .fid = pfid, - }; - - rc = SMB2_open_init(tcon, server, - &rqst[0], &oplock, &oparms, utf16_path); - if (rc) - goto oshr_free; - smb2_set_next_command(tcon, &rqst[0]); - - memset(&qi_iov, 0, sizeof(qi_iov)); - rqst[1].rq_iov = qi_iov; - rqst[1].rq_nvec = 1; - - rc = SMB2_query_info_init(tcon, server, - &rqst[1], COMPOUND_FID, - COMPOUND_FID, FILE_ALL_INFORMATION, - SMB2_O_INFO_FILE, 0, - sizeof(struct smb2_file_all_info) + - PATH_MAX * 2, 0, NULL); - if (rc) - goto oshr_free; - - smb2_set_related(&rqst[1]); - - rc = compound_send_recv(xid, ses, server, - flags, 2, rqst, - resp_buftype, rsp_iov); - if (rc) { - if (rc == -EREMCHG) { - tcon->need_reconnect = true; - pr_warn_once("server share %s deleted\n", - tcon->tree_name); - } - goto oshr_free; - } - cfid->tcon = tcon; - cfid->is_open = true; - - o_rsp = (struct smb2_create_rsp *)rsp_iov[0].iov_base; - oparms.fid->persistent_fid = o_rsp->PersistentFileId; - oparms.fid->volatile_fid = o_rsp->VolatileFileId; -#ifdef CONFIG_CIFS_DEBUG2 - oparms.fid->mid = le64_to_cpu(o_rsp->hdr.MessageId); -#endif /* CIFS_DEBUG2 */ - - if (o_rsp->OplockLevel != SMB2_OPLOCK_LEVEL_LEASE) - goto oshr_free; - - smb2_parse_contexts(server, o_rsp, - &oparms.fid->epoch, - oparms.fid->lease_key, &oplock, - NULL, NULL); - if (!(oplock & SMB2_LEASE_READ_CACHING_HE)) - goto oshr_free; - qi_rsp = (struct smb2_query_info_rsp *)rsp_iov[1].iov_base; - if (le32_to_cpu(qi_rsp->OutputBufferLength) < sizeof(struct smb2_file_all_info)) - goto oshr_free; - if (!smb2_validate_and_copy_iov( - le16_to_cpu(qi_rsp->OutputBufferOffset), - sizeof(struct smb2_file_all_info), - &rsp_iov[1], sizeof(struct smb2_file_all_info), - (char *)&cfid->file_all_info)) - cfid->file_all_info_is_valid = true; - - if (!npath[0]) - dentry = dget(cifs_sb->root); - else { - dentry = path_to_dentry(cifs_sb, npath); - if (IS_ERR(dentry)) { - rc = -ENOENT; - goto oshr_free; - } - } - cfid->dentry = dentry; - cfid->time = jiffies; - cfid->has_lease = true; - -oshr_free: - kfree(utf16_path); - SMB2_open_free(&rqst[0]); - SMB2_query_info_free(&rqst[1]); - free_rsp_buf(resp_buftype[0], rsp_iov[0].iov_base); - free_rsp_buf(resp_buftype[1], rsp_iov[1].iov_base); - spin_lock(&cfids->cfid_list_lock); - if (rc && !cfid->has_lease) { - if (cfid->on_list) { - list_del(&cfid->entry); - cfid->on_list = false; - cfids->num_entries--; - } - rc = -ENOENT; - } - spin_unlock(&cfids->cfid_list_lock); - if (!rc && !cfid->has_lease) { - /* - * We are guaranteed to have two references at this point. - * One for the caller and one for a potential lease. - * Release the Lease-ref so that the directory will be closed - * when the caller closes the cached handle. - */ - kref_put(&cfid->refcount, smb2_close_cached_fid); - } - if (rc) { - if (cfid->is_open) - SMB2_close(0, cfid->tcon, cfid->fid.persistent_fid, - cfid->fid.volatile_fid); - free_cached_dir(cfid); - cfid = NULL; - } - - if (rc == 0) { - *ret_cfid = cfid; - atomic_inc(&tcon->num_remote_opens); - } - - return rc; -} - -int open_cached_dir_by_dentry(struct cifs_tcon *tcon, - struct dentry *dentry, - struct cached_fid **ret_cfid) -{ - struct cached_fid *cfid; - struct cached_fids *cfids = tcon->cfids; - - if (cfids == NULL) - return -ENOENT; - - spin_lock(&cfids->cfid_list_lock); - list_for_each_entry(cfid, &cfids->entries, entry) { - if (dentry && cfid->dentry == dentry) { - cifs_dbg(FYI, "found a cached root file handle by dentry\n"); - kref_get(&cfid->refcount); - *ret_cfid = cfid; - spin_unlock(&cfids->cfid_list_lock); - return 0; - } - } - spin_unlock(&cfids->cfid_list_lock); - return -ENOENT; -} - -static void -smb2_close_cached_fid(struct kref *ref) -{ - struct cached_fid *cfid = container_of(ref, struct cached_fid, - refcount); - - spin_lock(&cfid->cfids->cfid_list_lock); - if (cfid->on_list) { - list_del(&cfid->entry); - cfid->on_list = false; - cfid->cfids->num_entries--; - } - spin_unlock(&cfid->cfids->cfid_list_lock); - - dput(cfid->dentry); - cfid->dentry = NULL; - - if (cfid->is_open) { - SMB2_close(0, cfid->tcon, cfid->fid.persistent_fid, - cfid->fid.volatile_fid); - atomic_dec(&cfid->tcon->num_remote_opens); - } - - free_cached_dir(cfid); -} - -void drop_cached_dir_by_name(const unsigned int xid, struct cifs_tcon *tcon, - const char *name, struct cifs_sb_info *cifs_sb) -{ - struct cached_fid *cfid = NULL; - int rc; - - rc = open_cached_dir(xid, tcon, name, cifs_sb, true, &cfid); - if (rc) { - cifs_dbg(FYI, "no cached dir found for rmdir(%s)\n", name); - return; - } - spin_lock(&cfid->cfids->cfid_list_lock); - if (cfid->has_lease) { - cfid->has_lease = false; - kref_put(&cfid->refcount, smb2_close_cached_fid); - } - spin_unlock(&cfid->cfids->cfid_list_lock); - close_cached_dir(cfid); -} - - -void close_cached_dir(struct cached_fid *cfid) -{ - kref_put(&cfid->refcount, smb2_close_cached_fid); -} - -/* - * Called from cifs_kill_sb when we unmount a share - */ -void close_all_cached_dirs(struct cifs_sb_info *cifs_sb) -{ - struct rb_root *root = &cifs_sb->tlink_tree; - struct rb_node *node; - struct cached_fid *cfid; - struct cifs_tcon *tcon; - struct tcon_link *tlink; - struct cached_fids *cfids; - - for (node = rb_first(root); node; node = rb_next(node)) { - tlink = rb_entry(node, struct tcon_link, tl_rbnode); - tcon = tlink_tcon(tlink); - if (IS_ERR(tcon)) - continue; - cfids = tcon->cfids; - if (cfids == NULL) - continue; - list_for_each_entry(cfid, &cfids->entries, entry) { - dput(cfid->dentry); - cfid->dentry = NULL; - } - } -} - -/* - * Invalidate all cached dirs when a TCON has been reset - * due to a session loss. - */ -void invalidate_all_cached_dirs(struct cifs_tcon *tcon) -{ - struct cached_fids *cfids = tcon->cfids; - struct cached_fid *cfid, *q; - LIST_HEAD(entry); - - spin_lock(&cfids->cfid_list_lock); - list_for_each_entry_safe(cfid, q, &cfids->entries, entry) { - list_move(&cfid->entry, &entry); - cfids->num_entries--; - cfid->is_open = false; - cfid->on_list = false; - /* To prevent race with smb2_cached_lease_break() */ - kref_get(&cfid->refcount); - } - spin_unlock(&cfids->cfid_list_lock); - - list_for_each_entry_safe(cfid, q, &entry, entry) { - list_del(&cfid->entry); - cancel_work_sync(&cfid->lease_break); - if (cfid->has_lease) { - /* - * We lease was never cancelled from the server so we - * need to drop the reference. - */ - spin_lock(&cfids->cfid_list_lock); - cfid->has_lease = false; - spin_unlock(&cfids->cfid_list_lock); - kref_put(&cfid->refcount, smb2_close_cached_fid); - } - /* Drop the extra reference opened above*/ - kref_put(&cfid->refcount, smb2_close_cached_fid); - } -} - -static void -smb2_cached_lease_break(struct work_struct *work) -{ - struct cached_fid *cfid = container_of(work, - struct cached_fid, lease_break); - - spin_lock(&cfid->cfids->cfid_list_lock); - cfid->has_lease = false; - spin_unlock(&cfid->cfids->cfid_list_lock); - kref_put(&cfid->refcount, smb2_close_cached_fid); -} - -int cached_dir_lease_break(struct cifs_tcon *tcon, __u8 lease_key[16]) -{ - struct cached_fids *cfids = tcon->cfids; - struct cached_fid *cfid; - - if (cfids == NULL) - return false; - - spin_lock(&cfids->cfid_list_lock); - list_for_each_entry(cfid, &cfids->entries, entry) { - if (cfid->has_lease && - !memcmp(lease_key, - cfid->fid.lease_key, - SMB2_LEASE_KEY_SIZE)) { - cfid->time = 0; - /* - * We found a lease remove it from the list - * so no threads can access it. - */ - list_del(&cfid->entry); - cfid->on_list = false; - cfids->num_entries--; - - queue_work(cifsiod_wq, - &cfid->lease_break); - spin_unlock(&cfids->cfid_list_lock); - return true; - } - } - spin_unlock(&cfids->cfid_list_lock); - return false; -} - -static struct cached_fid *init_cached_dir(const char *path) -{ - struct cached_fid *cfid; - - cfid = kzalloc(sizeof(*cfid), GFP_ATOMIC); - if (!cfid) - return NULL; - cfid->path = kstrdup(path, GFP_ATOMIC); - if (!cfid->path) { - kfree(cfid); - return NULL; - } - - INIT_WORK(&cfid->lease_break, smb2_cached_lease_break); - INIT_LIST_HEAD(&cfid->entry); - INIT_LIST_HEAD(&cfid->dirents.entries); - mutex_init(&cfid->dirents.de_mutex); - spin_lock_init(&cfid->fid_lock); - kref_init(&cfid->refcount); - return cfid; -} - -static void free_cached_dir(struct cached_fid *cfid) -{ - struct cached_dirent *dirent, *q; - - dput(cfid->dentry); - cfid->dentry = NULL; - - /* - * Delete all cached dirent names - */ - list_for_each_entry_safe(dirent, q, &cfid->dirents.entries, entry) { - list_del(&dirent->entry); - kfree(dirent->name); - kfree(dirent); - } - - kfree(cfid->path); - cfid->path = NULL; - kfree(cfid); -} - -struct cached_fids *init_cached_dirs(void) -{ - struct cached_fids *cfids; - - cfids = kzalloc(sizeof(*cfids), GFP_KERNEL); - if (!cfids) - return NULL; - spin_lock_init(&cfids->cfid_list_lock); - INIT_LIST_HEAD(&cfids->entries); - return cfids; -} - -/* - * Called from tconInfoFree when we are tearing down the tcon. - * There are no active users or open files/directories at this point. - */ -void free_cached_dirs(struct cached_fids *cfids) -{ - struct cached_fid *cfid, *q; - LIST_HEAD(entry); - - spin_lock(&cfids->cfid_list_lock); - list_for_each_entry_safe(cfid, q, &cfids->entries, entry) { - cfid->on_list = false; - cfid->is_open = false; - list_move(&cfid->entry, &entry); - } - spin_unlock(&cfids->cfid_list_lock); - - list_for_each_entry_safe(cfid, q, &entry, entry) { - list_del(&cfid->entry); - free_cached_dir(cfid); - } - - kfree(cfids); -} diff --git a/fs/cifs/cached_dir.h b/fs/cifs/cached_dir.h deleted file mode 100644 index 2f4e764c9ca9a..0000000000000 --- a/fs/cifs/cached_dir.h +++ /dev/null @@ -1,80 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0 */ -/* - * Functions to handle the cached directory entries - * - * Copyright (c) 2022, Ronnie Sahlberg - */ - -#ifndef _CACHED_DIR_H -#define _CACHED_DIR_H - - -struct cached_dirent { - struct list_head entry; - char *name; - int namelen; - loff_t pos; - - struct cifs_fattr fattr; -}; - -struct cached_dirents { - bool is_valid:1; - bool is_failed:1; - struct dir_context *ctx; /* - * Only used to make sure we only take entries - * from a single context. Never dereferenced. - */ - struct mutex de_mutex; - int pos; /* Expected ctx->pos */ - struct list_head entries; -}; - -struct cached_fid { - struct list_head entry; - struct cached_fids *cfids; - const char *path; - bool has_lease:1; - bool is_open:1; - bool on_list:1; - bool file_all_info_is_valid:1; - unsigned long time; /* jiffies of when lease was taken */ - struct kref refcount; - struct cifs_fid fid; - spinlock_t fid_lock; - struct cifs_tcon *tcon; - struct dentry *dentry; - struct work_struct lease_break; - struct smb2_file_all_info file_all_info; - struct cached_dirents dirents; -}; - -#define MAX_CACHED_FIDS 16 -struct cached_fids { - /* Must be held when: - * - accessing the cfids->entries list - */ - spinlock_t cfid_list_lock; - int num_entries; - struct list_head entries; -}; - -extern struct cached_fids *init_cached_dirs(void); -extern void free_cached_dirs(struct cached_fids *cfids); -extern int open_cached_dir(unsigned int xid, struct cifs_tcon *tcon, - const char *path, - struct cifs_sb_info *cifs_sb, - bool lookup_only, struct cached_fid **cfid); -extern int open_cached_dir_by_dentry(struct cifs_tcon *tcon, - struct dentry *dentry, - struct cached_fid **cfid); -extern void close_cached_dir(struct cached_fid *cfid); -extern void drop_cached_dir_by_name(const unsigned int xid, - struct cifs_tcon *tcon, - const char *name, - struct cifs_sb_info *cifs_sb); -extern void close_all_cached_dirs(struct cifs_sb_info *cifs_sb); -extern void invalidate_all_cached_dirs(struct cifs_tcon *tcon); -extern int cached_dir_lease_break(struct cifs_tcon *tcon, __u8 lease_key[16]); - -#endif /* _CACHED_DIR_H */ diff --git a/fs/cifs/cifs_debug.c b/fs/cifs/cifs_debug.c deleted file mode 100644 index e41154ad96afc..0000000000000 --- a/fs/cifs/cifs_debug.c +++ /dev/null @@ -1,1067 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-or-later -/* - * - * Copyright (C) International Business Machines Corp., 2000,2005 - * - * Modified by Steve French (sfrench@us.ibm.com) - */ -#include -#include -#include -#include -#include -#include -#include "cifspdu.h" -#include "cifsglob.h" -#include "cifsproto.h" -#include "cifs_debug.h" -#include "cifsfs.h" -#include "fs_context.h" -#ifdef CONFIG_CIFS_DFS_UPCALL -#include "dfs_cache.h" -#endif -#ifdef CONFIG_CIFS_SMB_DIRECT -#include "smbdirect.h" -#endif -#include "cifs_swn.h" - -void -cifs_dump_mem(char *label, void *data, int length) -{ - pr_debug("%s: dump of %d bytes of data at 0x%p\n", label, length, data); - print_hex_dump(KERN_DEBUG, "", DUMP_PREFIX_OFFSET, 16, 4, - data, length, true); -} - -void cifs_dump_detail(void *buf, struct TCP_Server_Info *server) -{ -#ifdef CONFIG_CIFS_DEBUG2 - struct smb_hdr *smb = buf; - - cifs_dbg(VFS, "Cmd: %d Err: 0x%x Flags: 0x%x Flgs2: 0x%x Mid: %d Pid: %d\n", - smb->Command, smb->Status.CifsError, - smb->Flags, smb->Flags2, smb->Mid, smb->Pid); - cifs_dbg(VFS, "smb buf %p len %u\n", smb, - server->ops->calc_smb_size(smb)); -#endif /* CONFIG_CIFS_DEBUG2 */ -} - -void cifs_dump_mids(struct TCP_Server_Info *server) -{ -#ifdef CONFIG_CIFS_DEBUG2 - struct mid_q_entry *mid_entry; - - if (server == NULL) - return; - - cifs_dbg(VFS, "Dump pending requests:\n"); - spin_lock(&server->mid_lock); - list_for_each_entry(mid_entry, &server->pending_mid_q, qhead) { - cifs_dbg(VFS, "State: %d Cmd: %d Pid: %d Cbdata: %p Mid %llu\n", - mid_entry->mid_state, - le16_to_cpu(mid_entry->command), - mid_entry->pid, - mid_entry->callback_data, - mid_entry->mid); -#ifdef CONFIG_CIFS_STATS2 - cifs_dbg(VFS, "IsLarge: %d buf: %p time rcv: %ld now: %ld\n", - mid_entry->large_buf, - mid_entry->resp_buf, - mid_entry->when_received, - jiffies); -#endif /* STATS2 */ - cifs_dbg(VFS, "IsMult: %d IsEnd: %d\n", - mid_entry->multiRsp, mid_entry->multiEnd); - if (mid_entry->resp_buf) { - cifs_dump_detail(mid_entry->resp_buf, server); - cifs_dump_mem("existing buf: ", - mid_entry->resp_buf, 62); - } - } - spin_unlock(&server->mid_lock); -#endif /* CONFIG_CIFS_DEBUG2 */ -} - -#ifdef CONFIG_PROC_FS -static void cifs_debug_tcon(struct seq_file *m, struct cifs_tcon *tcon) -{ - __u32 dev_type = le32_to_cpu(tcon->fsDevInfo.DeviceType); - - seq_printf(m, "%s Mounts: %d ", tcon->tree_name, tcon->tc_count); - if (tcon->nativeFileSystem) - seq_printf(m, "Type: %s ", tcon->nativeFileSystem); - seq_printf(m, "DevInfo: 0x%x Attributes: 0x%x\n\tPathComponentMax: %d Status: %d", - le32_to_cpu(tcon->fsDevInfo.DeviceCharacteristics), - le32_to_cpu(tcon->fsAttrInfo.Attributes), - le32_to_cpu(tcon->fsAttrInfo.MaxPathNameComponentLength), - tcon->status); - if (dev_type == FILE_DEVICE_DISK) - seq_puts(m, " type: DISK "); - else if (dev_type == FILE_DEVICE_CD_ROM) - seq_puts(m, " type: CDROM "); - else - seq_printf(m, " type: %d ", dev_type); - - seq_printf(m, "Serial Number: 0x%x", tcon->vol_serial_number); - - if ((tcon->seal) || - (tcon->ses->session_flags & SMB2_SESSION_FLAG_ENCRYPT_DATA) || - (tcon->share_flags & SHI1005_FLAGS_ENCRYPT_DATA)) - seq_printf(m, " Encrypted"); - if (tcon->nocase) - seq_printf(m, " nocase"); - if (tcon->unix_ext) - seq_printf(m, " POSIX Extensions"); - if (tcon->ses->server->ops->dump_share_caps) - tcon->ses->server->ops->dump_share_caps(m, tcon); - if (tcon->use_witness) - seq_puts(m, " Witness"); - if (tcon->broken_sparse_sup) - seq_puts(m, " nosparse"); - if (tcon->need_reconnect) - seq_puts(m, "\tDISCONNECTED "); - seq_putc(m, '\n'); -} - -static void -cifs_dump_channel(struct seq_file *m, int i, struct cifs_chan *chan) -{ - struct TCP_Server_Info *server = chan->server; - - seq_printf(m, "\n\n\t\tChannel: %d ConnectionId: 0x%llx" - "\n\t\tNumber of credits: %d Dialect 0x%x" - "\n\t\tTCP status: %d Instance: %d" - "\n\t\tLocal Users To Server: %d SecMode: 0x%x Req On Wire: %d" - "\n\t\tIn Send: %d In MaxReq Wait: %d", - i+1, server->conn_id, - server->credits, - server->dialect, - server->tcpStatus, - server->reconnect_instance, - server->srv_count, - server->sec_mode, - in_flight(server), - atomic_read(&server->in_send), - atomic_read(&server->num_waiters)); -} - -static void -cifs_dump_iface(struct seq_file *m, struct cifs_server_iface *iface) -{ - struct sockaddr_in *ipv4 = (struct sockaddr_in *)&iface->sockaddr; - struct sockaddr_in6 *ipv6 = (struct sockaddr_in6 *)&iface->sockaddr; - - seq_printf(m, "\tSpeed: %zu bps\n", iface->speed); - seq_puts(m, "\t\tCapabilities: "); - if (iface->rdma_capable) - seq_puts(m, "rdma "); - if (iface->rss_capable) - seq_puts(m, "rss "); - seq_putc(m, '\n'); - if (iface->sockaddr.ss_family == AF_INET) - seq_printf(m, "\t\tIPv4: %pI4\n", &ipv4->sin_addr); - else if (iface->sockaddr.ss_family == AF_INET6) - seq_printf(m, "\t\tIPv6: %pI6\n", &ipv6->sin6_addr); - if (!iface->is_active) - seq_puts(m, "\t\t[for-cleanup]\n"); -} - -static int cifs_debug_files_proc_show(struct seq_file *m, void *v) -{ - struct TCP_Server_Info *server; - struct cifs_ses *ses; - struct cifs_tcon *tcon; - struct cifsFileInfo *cfile; - - seq_puts(m, "# Version:1\n"); - seq_puts(m, "# Format:\n"); - seq_puts(m, "# "); -#ifdef CONFIG_CIFS_DEBUG2 - seq_printf(m, " \n"); -#else - seq_printf(m, " \n"); -#endif /* CIFS_DEBUG2 */ - spin_lock(&cifs_tcp_ses_lock); - list_for_each_entry(server, &cifs_tcp_ses_list, tcp_ses_list) { - list_for_each_entry(ses, &server->smb_ses_list, smb_ses_list) { - list_for_each_entry(tcon, &ses->tcon_list, tcon_list) { - spin_lock(&tcon->open_file_lock); - list_for_each_entry(cfile, &tcon->openFileList, tlist) { - seq_printf(m, - "0x%x 0x%llx 0x%llx 0x%x %d %d %d %pd", - tcon->tid, - ses->Suid, - cfile->fid.persistent_fid, - cfile->f_flags, - cfile->count, - cfile->pid, - from_kuid(&init_user_ns, cfile->uid), - cfile->dentry); -#ifdef CONFIG_CIFS_DEBUG2 - seq_printf(m, " %llu\n", cfile->fid.mid); -#else - seq_printf(m, "\n"); -#endif /* CIFS_DEBUG2 */ - } - spin_unlock(&tcon->open_file_lock); - } - } - } - spin_unlock(&cifs_tcp_ses_lock); - seq_putc(m, '\n'); - return 0; -} - -static int cifs_debug_data_proc_show(struct seq_file *m, void *v) -{ - struct mid_q_entry *mid_entry; - struct TCP_Server_Info *server; - struct TCP_Server_Info *chan_server; - struct cifs_ses *ses; - struct cifs_tcon *tcon; - struct cifs_server_iface *iface; - int c, i, j; - - seq_puts(m, - "Display Internal CIFS Data Structures for Debugging\n" - "---------------------------------------------------\n"); - seq_printf(m, "CIFS Version %s\n", CIFS_VERSION); - seq_printf(m, "Features:"); -#ifdef CONFIG_CIFS_DFS_UPCALL - seq_printf(m, " DFS"); -#endif -#ifdef CONFIG_CIFS_FSCACHE - seq_printf(m, ",FSCACHE"); -#endif -#ifdef CONFIG_CIFS_SMB_DIRECT - seq_printf(m, ",SMB_DIRECT"); -#endif -#ifdef CONFIG_CIFS_STATS2 - seq_printf(m, ",STATS2"); -#else - seq_printf(m, ",STATS"); -#endif -#ifdef CONFIG_CIFS_DEBUG2 - seq_printf(m, ",DEBUG2"); -#elif defined(CONFIG_CIFS_DEBUG) - seq_printf(m, ",DEBUG"); -#endif -#ifdef CONFIG_CIFS_ALLOW_INSECURE_LEGACY - seq_printf(m, ",ALLOW_INSECURE_LEGACY"); -#endif -#ifdef CONFIG_CIFS_POSIX - seq_printf(m, ",CIFS_POSIX"); -#endif -#ifdef CONFIG_CIFS_UPCALL - seq_printf(m, ",UPCALL(SPNEGO)"); -#endif -#ifdef CONFIG_CIFS_XATTR - seq_printf(m, ",XATTR"); -#endif - seq_printf(m, ",ACL"); -#ifdef CONFIG_CIFS_SWN_UPCALL - seq_puts(m, ",WITNESS"); -#endif - seq_putc(m, '\n'); - seq_printf(m, "CIFSMaxBufSize: %d\n", CIFSMaxBufSize); - seq_printf(m, "Active VFS Requests: %d\n", GlobalTotalActiveXid); - - seq_printf(m, "\nServers: "); - - c = 0; - spin_lock(&cifs_tcp_ses_lock); - list_for_each_entry(server, &cifs_tcp_ses_list, tcp_ses_list) { - /* channel info will be printed as a part of sessions below */ - if (CIFS_SERVER_IS_CHAN(server)) - continue; - - c++; - seq_printf(m, "\n%d) ConnectionId: 0x%llx ", - c, server->conn_id); - - spin_lock(&server->srv_lock); - if (server->hostname) - seq_printf(m, "Hostname: %s ", server->hostname); - spin_unlock(&server->srv_lock); -#ifdef CONFIG_CIFS_SMB_DIRECT - if (!server->rdma) - goto skip_rdma; - - if (!server->smbd_conn) { - seq_printf(m, "\nSMBDirect transport not available"); - goto skip_rdma; - } - - seq_printf(m, "\nSMBDirect (in hex) protocol version: %x " - "transport status: %x", - server->smbd_conn->protocol, - server->smbd_conn->transport_status); - seq_printf(m, "\nConn receive_credit_max: %x " - "send_credit_target: %x max_send_size: %x", - server->smbd_conn->receive_credit_max, - server->smbd_conn->send_credit_target, - server->smbd_conn->max_send_size); - seq_printf(m, "\nConn max_fragmented_recv_size: %x " - "max_fragmented_send_size: %x max_receive_size:%x", - server->smbd_conn->max_fragmented_recv_size, - server->smbd_conn->max_fragmented_send_size, - server->smbd_conn->max_receive_size); - seq_printf(m, "\nConn keep_alive_interval: %x " - "max_readwrite_size: %x rdma_readwrite_threshold: %x", - server->smbd_conn->keep_alive_interval, - server->smbd_conn->max_readwrite_size, - server->smbd_conn->rdma_readwrite_threshold); - seq_printf(m, "\nDebug count_get_receive_buffer: %x " - "count_put_receive_buffer: %x count_send_empty: %x", - server->smbd_conn->count_get_receive_buffer, - server->smbd_conn->count_put_receive_buffer, - server->smbd_conn->count_send_empty); - seq_printf(m, "\nRead Queue count_reassembly_queue: %x " - "count_enqueue_reassembly_queue: %x " - "count_dequeue_reassembly_queue: %x " - "fragment_reassembly_remaining: %x " - "reassembly_data_length: %x " - "reassembly_queue_length: %x", - server->smbd_conn->count_reassembly_queue, - server->smbd_conn->count_enqueue_reassembly_queue, - server->smbd_conn->count_dequeue_reassembly_queue, - server->smbd_conn->fragment_reassembly_remaining, - server->smbd_conn->reassembly_data_length, - server->smbd_conn->reassembly_queue_length); - seq_printf(m, "\nCurrent Credits send_credits: %x " - "receive_credits: %x receive_credit_target: %x", - atomic_read(&server->smbd_conn->send_credits), - atomic_read(&server->smbd_conn->receive_credits), - server->smbd_conn->receive_credit_target); - seq_printf(m, "\nPending send_pending: %x ", - atomic_read(&server->smbd_conn->send_pending)); - seq_printf(m, "\nReceive buffers count_receive_queue: %x " - "count_empty_packet_queue: %x", - server->smbd_conn->count_receive_queue, - server->smbd_conn->count_empty_packet_queue); - seq_printf(m, "\nMR responder_resources: %x " - "max_frmr_depth: %x mr_type: %x", - server->smbd_conn->responder_resources, - server->smbd_conn->max_frmr_depth, - server->smbd_conn->mr_type); - seq_printf(m, "\nMR mr_ready_count: %x mr_used_count: %x", - atomic_read(&server->smbd_conn->mr_ready_count), - atomic_read(&server->smbd_conn->mr_used_count)); -skip_rdma: -#endif - seq_printf(m, "\nNumber of credits: %d Dialect 0x%x", - server->credits, server->dialect); - if (server->compress_algorithm == SMB3_COMPRESS_LZNT1) - seq_printf(m, " COMPRESS_LZNT1"); - else if (server->compress_algorithm == SMB3_COMPRESS_LZ77) - seq_printf(m, " COMPRESS_LZ77"); - else if (server->compress_algorithm == SMB3_COMPRESS_LZ77_HUFF) - seq_printf(m, " COMPRESS_LZ77_HUFF"); - if (server->sign) - seq_printf(m, " signed"); - if (server->posix_ext_supported) - seq_printf(m, " posix"); - if (server->nosharesock) - seq_printf(m, " nosharesock"); - - if (server->rdma) - seq_printf(m, "\nRDMA "); - seq_printf(m, "\nTCP status: %d Instance: %d" - "\nLocal Users To Server: %d SecMode: 0x%x Req On Wire: %d", - server->tcpStatus, - server->reconnect_instance, - server->srv_count, - server->sec_mode, in_flight(server)); - - seq_printf(m, "\nIn Send: %d In MaxReq Wait: %d", - atomic_read(&server->in_send), - atomic_read(&server->num_waiters)); - - seq_printf(m, "\n\n\tSessions: "); - i = 0; - list_for_each_entry(ses, &server->smb_ses_list, smb_ses_list) { - i++; - if ((ses->serverDomain == NULL) || - (ses->serverOS == NULL) || - (ses->serverNOS == NULL)) { - seq_printf(m, "\n\t%d) Address: %s Uses: %d Capability: 0x%x\tSession Status: %d ", - i, ses->ip_addr, ses->ses_count, - ses->capabilities, ses->ses_status); - if (ses->session_flags & SMB2_SESSION_FLAG_IS_GUEST) - seq_printf(m, "Guest "); - else if (ses->session_flags & SMB2_SESSION_FLAG_IS_NULL) - seq_printf(m, "Anonymous "); - } else { - seq_printf(m, - "\n\t%d) Name: %s Domain: %s Uses: %d OS: %s " - "\n\tNOS: %s\tCapability: 0x%x" - "\n\tSMB session status: %d ", - i, ses->ip_addr, ses->serverDomain, - ses->ses_count, ses->serverOS, ses->serverNOS, - ses->capabilities, ses->ses_status); - } - - seq_printf(m, "\n\tSecurity type: %s ", - get_security_type_str(server->ops->select_sectype(server, ses->sectype))); - - /* dump session id helpful for use with network trace */ - seq_printf(m, " SessionId: 0x%llx", ses->Suid); - if (ses->session_flags & SMB2_SESSION_FLAG_ENCRYPT_DATA) - seq_puts(m, " encrypted"); - if (ses->sign) - seq_puts(m, " signed"); - - seq_printf(m, "\n\tUser: %d Cred User: %d", - from_kuid(&init_user_ns, ses->linux_uid), - from_kuid(&init_user_ns, ses->cred_uid)); - - spin_lock(&ses->chan_lock); - if (CIFS_CHAN_NEEDS_RECONNECT(ses, 0)) - seq_puts(m, "\tPrimary channel: DISCONNECTED "); - if (CIFS_CHAN_IN_RECONNECT(ses, 0)) - seq_puts(m, "\t[RECONNECTING] "); - - if (ses->chan_count > 1) { - seq_printf(m, "\n\n\tExtra Channels: %zu ", - ses->chan_count-1); - for (j = 1; j < ses->chan_count; j++) { - cifs_dump_channel(m, j, &ses->chans[j]); - if (CIFS_CHAN_NEEDS_RECONNECT(ses, j)) - seq_puts(m, "\tDISCONNECTED "); - if (CIFS_CHAN_IN_RECONNECT(ses, j)) - seq_puts(m, "\t[RECONNECTING] "); - } - } - spin_unlock(&ses->chan_lock); - - seq_puts(m, "\n\n\tShares: "); - j = 0; - - seq_printf(m, "\n\t%d) IPC: ", j); - if (ses->tcon_ipc) - cifs_debug_tcon(m, ses->tcon_ipc); - else - seq_puts(m, "none\n"); - - list_for_each_entry(tcon, &ses->tcon_list, tcon_list) { - ++j; - seq_printf(m, "\n\t%d) ", j); - cifs_debug_tcon(m, tcon); - } - - spin_lock(&ses->iface_lock); - if (ses->iface_count) - seq_printf(m, "\n\n\tServer interfaces: %zu", - ses->iface_count); - j = 0; - list_for_each_entry(iface, &ses->iface_list, - iface_head) { - seq_printf(m, "\n\t%d)", ++j); - cifs_dump_iface(m, iface); - if (is_ses_using_iface(ses, iface)) - seq_puts(m, "\t\t[CONNECTED]\n"); - } - spin_unlock(&ses->iface_lock); - - seq_puts(m, "\n\n\tMIDs: "); - spin_lock(&ses->chan_lock); - for (j = 0; j < ses->chan_count; j++) { - chan_server = ses->chans[j].server; - if (!chan_server) - continue; - - if (list_empty(&chan_server->pending_mid_q)) - continue; - - seq_printf(m, "\n\tServer ConnectionId: 0x%llx", - chan_server->conn_id); - spin_lock(&chan_server->mid_lock); - list_for_each_entry(mid_entry, &chan_server->pending_mid_q, qhead) { - seq_printf(m, "\n\t\tState: %d com: %d pid: %d cbdata: %p mid %llu", - mid_entry->mid_state, - le16_to_cpu(mid_entry->command), - mid_entry->pid, - mid_entry->callback_data, - mid_entry->mid); - } - spin_unlock(&chan_server->mid_lock); - } - spin_unlock(&ses->chan_lock); - seq_puts(m, "\n--\n"); - } - if (i == 0) - seq_printf(m, "\n\t\t[NONE]"); - } - if (c == 0) - seq_printf(m, "\n\t[NONE]"); - - spin_unlock(&cifs_tcp_ses_lock); - seq_putc(m, '\n'); - cifs_swn_dump(m); - - /* BB add code to dump additional info such as TCP session info now */ - return 0; -} - -static ssize_t cifs_stats_proc_write(struct file *file, - const char __user *buffer, size_t count, loff_t *ppos) -{ - bool bv; - int rc; - struct TCP_Server_Info *server; - struct cifs_ses *ses; - struct cifs_tcon *tcon; - - rc = kstrtobool_from_user(buffer, count, &bv); - if (rc == 0) { -#ifdef CONFIG_CIFS_STATS2 - int i; - - atomic_set(&total_buf_alloc_count, 0); - atomic_set(&total_small_buf_alloc_count, 0); -#endif /* CONFIG_CIFS_STATS2 */ - atomic_set(&tcpSesReconnectCount, 0); - atomic_set(&tconInfoReconnectCount, 0); - - spin_lock(&GlobalMid_Lock); - GlobalMaxActiveXid = 0; - GlobalCurrentXid = 0; - spin_unlock(&GlobalMid_Lock); - spin_lock(&cifs_tcp_ses_lock); - list_for_each_entry(server, &cifs_tcp_ses_list, tcp_ses_list) { - server->max_in_flight = 0; -#ifdef CONFIG_CIFS_STATS2 - for (i = 0; i < NUMBER_OF_SMB2_COMMANDS; i++) { - atomic_set(&server->num_cmds[i], 0); - atomic_set(&server->smb2slowcmd[i], 0); - server->time_per_cmd[i] = 0; - server->slowest_cmd[i] = 0; - server->fastest_cmd[0] = 0; - } -#endif /* CONFIG_CIFS_STATS2 */ - list_for_each_entry(ses, &server->smb_ses_list, smb_ses_list) { - list_for_each_entry(tcon, &ses->tcon_list, tcon_list) { - atomic_set(&tcon->num_smbs_sent, 0); - spin_lock(&tcon->stat_lock); - tcon->bytes_read = 0; - tcon->bytes_written = 0; - spin_unlock(&tcon->stat_lock); - if (server->ops->clear_stats) - server->ops->clear_stats(tcon); - } - } - } - spin_unlock(&cifs_tcp_ses_lock); - } else { - return rc; - } - - return count; -} - -static int cifs_stats_proc_show(struct seq_file *m, void *v) -{ - int i; -#ifdef CONFIG_CIFS_STATS2 - int j; -#endif /* STATS2 */ - struct TCP_Server_Info *server; - struct cifs_ses *ses; - struct cifs_tcon *tcon; - - seq_printf(m, "Resources in use\nCIFS Session: %d\n", - sesInfoAllocCount.counter); - seq_printf(m, "Share (unique mount targets): %d\n", - tconInfoAllocCount.counter); - seq_printf(m, "SMB Request/Response Buffer: %d Pool size: %d\n", - buf_alloc_count.counter, - cifs_min_rcv + tcpSesAllocCount.counter); - seq_printf(m, "SMB Small Req/Resp Buffer: %d Pool size: %d\n", - small_buf_alloc_count.counter, cifs_min_small); -#ifdef CONFIG_CIFS_STATS2 - seq_printf(m, "Total Large %d Small %d Allocations\n", - atomic_read(&total_buf_alloc_count), - atomic_read(&total_small_buf_alloc_count)); -#endif /* CONFIG_CIFS_STATS2 */ - - seq_printf(m, "Operations (MIDs): %d\n", atomic_read(&mid_count)); - seq_printf(m, - "\n%d session %d share reconnects\n", - tcpSesReconnectCount.counter, tconInfoReconnectCount.counter); - - seq_printf(m, - "Total vfs operations: %d maximum at one time: %d\n", - GlobalCurrentXid, GlobalMaxActiveXid); - - i = 0; - spin_lock(&cifs_tcp_ses_lock); - list_for_each_entry(server, &cifs_tcp_ses_list, tcp_ses_list) { - seq_printf(m, "\nMax requests in flight: %d", server->max_in_flight); -#ifdef CONFIG_CIFS_STATS2 - seq_puts(m, "\nTotal time spent processing by command. Time "); - seq_printf(m, "units are jiffies (%d per second)\n", HZ); - seq_puts(m, " SMB3 CMD\tNumber\tTotal Time\tFastest\tSlowest\n"); - seq_puts(m, " --------\t------\t----------\t-------\t-------\n"); - for (j = 0; j < NUMBER_OF_SMB2_COMMANDS; j++) - seq_printf(m, " %d\t\t%d\t%llu\t\t%u\t%u\n", j, - atomic_read(&server->num_cmds[j]), - server->time_per_cmd[j], - server->fastest_cmd[j], - server->slowest_cmd[j]); - for (j = 0; j < NUMBER_OF_SMB2_COMMANDS; j++) - if (atomic_read(&server->smb2slowcmd[j])) { - spin_lock(&server->srv_lock); - seq_printf(m, " %d slow responses from %s for command %d\n", - atomic_read(&server->smb2slowcmd[j]), - server->hostname, j); - spin_unlock(&server->srv_lock); - } -#endif /* STATS2 */ - list_for_each_entry(ses, &server->smb_ses_list, smb_ses_list) { - list_for_each_entry(tcon, &ses->tcon_list, tcon_list) { - i++; - seq_printf(m, "\n%d) %s", i, tcon->tree_name); - if (tcon->need_reconnect) - seq_puts(m, "\tDISCONNECTED "); - seq_printf(m, "\nSMBs: %d", - atomic_read(&tcon->num_smbs_sent)); - if (server->ops->print_stats) - server->ops->print_stats(m, tcon); - } - } - } - spin_unlock(&cifs_tcp_ses_lock); - - seq_putc(m, '\n'); - return 0; -} - -static int cifs_stats_proc_open(struct inode *inode, struct file *file) -{ - return single_open(file, cifs_stats_proc_show, NULL); -} - -static const struct proc_ops cifs_stats_proc_ops = { - .proc_open = cifs_stats_proc_open, - .proc_read = seq_read, - .proc_lseek = seq_lseek, - .proc_release = single_release, - .proc_write = cifs_stats_proc_write, -}; - -#ifdef CONFIG_CIFS_SMB_DIRECT -#define PROC_FILE_DEFINE(name) \ -static ssize_t name##_write(struct file *file, const char __user *buffer, \ - size_t count, loff_t *ppos) \ -{ \ - int rc; \ - rc = kstrtoint_from_user(buffer, count, 10, & name); \ - if (rc) \ - return rc; \ - return count; \ -} \ -static int name##_proc_show(struct seq_file *m, void *v) \ -{ \ - seq_printf(m, "%d\n", name ); \ - return 0; \ -} \ -static int name##_open(struct inode *inode, struct file *file) \ -{ \ - return single_open(file, name##_proc_show, NULL); \ -} \ -\ -static const struct proc_ops cifs_##name##_proc_fops = { \ - .proc_open = name##_open, \ - .proc_read = seq_read, \ - .proc_lseek = seq_lseek, \ - .proc_release = single_release, \ - .proc_write = name##_write, \ -} - -PROC_FILE_DEFINE(rdma_readwrite_threshold); -PROC_FILE_DEFINE(smbd_max_frmr_depth); -PROC_FILE_DEFINE(smbd_keep_alive_interval); -PROC_FILE_DEFINE(smbd_max_receive_size); -PROC_FILE_DEFINE(smbd_max_fragmented_recv_size); -PROC_FILE_DEFINE(smbd_max_send_size); -PROC_FILE_DEFINE(smbd_send_credit_target); -PROC_FILE_DEFINE(smbd_receive_credit_max); -#endif - -static struct proc_dir_entry *proc_fs_cifs; -static const struct proc_ops cifsFYI_proc_ops; -static const struct proc_ops cifs_lookup_cache_proc_ops; -static const struct proc_ops traceSMB_proc_ops; -static const struct proc_ops cifs_security_flags_proc_ops; -static const struct proc_ops cifs_linux_ext_proc_ops; -static const struct proc_ops cifs_mount_params_proc_ops; - -void -cifs_proc_init(void) -{ - proc_fs_cifs = proc_mkdir("fs/cifs", NULL); - if (proc_fs_cifs == NULL) - return; - - proc_create_single("DebugData", 0, proc_fs_cifs, - cifs_debug_data_proc_show); - - proc_create_single("open_files", 0400, proc_fs_cifs, - cifs_debug_files_proc_show); - - proc_create("Stats", 0644, proc_fs_cifs, &cifs_stats_proc_ops); - proc_create("cifsFYI", 0644, proc_fs_cifs, &cifsFYI_proc_ops); - proc_create("traceSMB", 0644, proc_fs_cifs, &traceSMB_proc_ops); - proc_create("LinuxExtensionsEnabled", 0644, proc_fs_cifs, - &cifs_linux_ext_proc_ops); - proc_create("SecurityFlags", 0644, proc_fs_cifs, - &cifs_security_flags_proc_ops); - proc_create("LookupCacheEnabled", 0644, proc_fs_cifs, - &cifs_lookup_cache_proc_ops); - - proc_create("mount_params", 0444, proc_fs_cifs, &cifs_mount_params_proc_ops); - -#ifdef CONFIG_CIFS_DFS_UPCALL - proc_create("dfscache", 0644, proc_fs_cifs, &dfscache_proc_ops); -#endif - -#ifdef CONFIG_CIFS_SMB_DIRECT - proc_create("rdma_readwrite_threshold", 0644, proc_fs_cifs, - &cifs_rdma_readwrite_threshold_proc_fops); - proc_create("smbd_max_frmr_depth", 0644, proc_fs_cifs, - &cifs_smbd_max_frmr_depth_proc_fops); - proc_create("smbd_keep_alive_interval", 0644, proc_fs_cifs, - &cifs_smbd_keep_alive_interval_proc_fops); - proc_create("smbd_max_receive_size", 0644, proc_fs_cifs, - &cifs_smbd_max_receive_size_proc_fops); - proc_create("smbd_max_fragmented_recv_size", 0644, proc_fs_cifs, - &cifs_smbd_max_fragmented_recv_size_proc_fops); - proc_create("smbd_max_send_size", 0644, proc_fs_cifs, - &cifs_smbd_max_send_size_proc_fops); - proc_create("smbd_send_credit_target", 0644, proc_fs_cifs, - &cifs_smbd_send_credit_target_proc_fops); - proc_create("smbd_receive_credit_max", 0644, proc_fs_cifs, - &cifs_smbd_receive_credit_max_proc_fops); -#endif -} - -void -cifs_proc_clean(void) -{ - if (proc_fs_cifs == NULL) - return; - - remove_proc_entry("DebugData", proc_fs_cifs); - remove_proc_entry("open_files", proc_fs_cifs); - remove_proc_entry("cifsFYI", proc_fs_cifs); - remove_proc_entry("traceSMB", proc_fs_cifs); - remove_proc_entry("Stats", proc_fs_cifs); - remove_proc_entry("SecurityFlags", proc_fs_cifs); - remove_proc_entry("LinuxExtensionsEnabled", proc_fs_cifs); - remove_proc_entry("LookupCacheEnabled", proc_fs_cifs); - remove_proc_entry("mount_params", proc_fs_cifs); - -#ifdef CONFIG_CIFS_DFS_UPCALL - remove_proc_entry("dfscache", proc_fs_cifs); -#endif -#ifdef CONFIG_CIFS_SMB_DIRECT - remove_proc_entry("rdma_readwrite_threshold", proc_fs_cifs); - remove_proc_entry("smbd_max_frmr_depth", proc_fs_cifs); - remove_proc_entry("smbd_keep_alive_interval", proc_fs_cifs); - remove_proc_entry("smbd_max_receive_size", proc_fs_cifs); - remove_proc_entry("smbd_max_fragmented_recv_size", proc_fs_cifs); - remove_proc_entry("smbd_max_send_size", proc_fs_cifs); - remove_proc_entry("smbd_send_credit_target", proc_fs_cifs); - remove_proc_entry("smbd_receive_credit_max", proc_fs_cifs); -#endif - remove_proc_entry("fs/cifs", NULL); -} - -static int cifsFYI_proc_show(struct seq_file *m, void *v) -{ - seq_printf(m, "%d\n", cifsFYI); - return 0; -} - -static int cifsFYI_proc_open(struct inode *inode, struct file *file) -{ - return single_open(file, cifsFYI_proc_show, NULL); -} - -static ssize_t cifsFYI_proc_write(struct file *file, const char __user *buffer, - size_t count, loff_t *ppos) -{ - char c[2] = { '\0' }; - bool bv; - int rc; - - rc = get_user(c[0], buffer); - if (rc) - return rc; - if (strtobool(c, &bv) == 0) - cifsFYI = bv; - else if ((c[0] > '1') && (c[0] <= '9')) - cifsFYI = (int) (c[0] - '0'); /* see cifs_debug.h for meanings */ - else - return -EINVAL; - - return count; -} - -static const struct proc_ops cifsFYI_proc_ops = { - .proc_open = cifsFYI_proc_open, - .proc_read = seq_read, - .proc_lseek = seq_lseek, - .proc_release = single_release, - .proc_write = cifsFYI_proc_write, -}; - -static int cifs_linux_ext_proc_show(struct seq_file *m, void *v) -{ - seq_printf(m, "%d\n", linuxExtEnabled); - return 0; -} - -static int cifs_linux_ext_proc_open(struct inode *inode, struct file *file) -{ - return single_open(file, cifs_linux_ext_proc_show, NULL); -} - -static ssize_t cifs_linux_ext_proc_write(struct file *file, - const char __user *buffer, size_t count, loff_t *ppos) -{ - int rc; - - rc = kstrtobool_from_user(buffer, count, &linuxExtEnabled); - if (rc) - return rc; - - return count; -} - -static const struct proc_ops cifs_linux_ext_proc_ops = { - .proc_open = cifs_linux_ext_proc_open, - .proc_read = seq_read, - .proc_lseek = seq_lseek, - .proc_release = single_release, - .proc_write = cifs_linux_ext_proc_write, -}; - -static int cifs_lookup_cache_proc_show(struct seq_file *m, void *v) -{ - seq_printf(m, "%d\n", lookupCacheEnabled); - return 0; -} - -static int cifs_lookup_cache_proc_open(struct inode *inode, struct file *file) -{ - return single_open(file, cifs_lookup_cache_proc_show, NULL); -} - -static ssize_t cifs_lookup_cache_proc_write(struct file *file, - const char __user *buffer, size_t count, loff_t *ppos) -{ - int rc; - - rc = kstrtobool_from_user(buffer, count, &lookupCacheEnabled); - if (rc) - return rc; - - return count; -} - -static const struct proc_ops cifs_lookup_cache_proc_ops = { - .proc_open = cifs_lookup_cache_proc_open, - .proc_read = seq_read, - .proc_lseek = seq_lseek, - .proc_release = single_release, - .proc_write = cifs_lookup_cache_proc_write, -}; - -static int traceSMB_proc_show(struct seq_file *m, void *v) -{ - seq_printf(m, "%d\n", traceSMB); - return 0; -} - -static int traceSMB_proc_open(struct inode *inode, struct file *file) -{ - return single_open(file, traceSMB_proc_show, NULL); -} - -static ssize_t traceSMB_proc_write(struct file *file, const char __user *buffer, - size_t count, loff_t *ppos) -{ - int rc; - - rc = kstrtobool_from_user(buffer, count, &traceSMB); - if (rc) - return rc; - - return count; -} - -static const struct proc_ops traceSMB_proc_ops = { - .proc_open = traceSMB_proc_open, - .proc_read = seq_read, - .proc_lseek = seq_lseek, - .proc_release = single_release, - .proc_write = traceSMB_proc_write, -}; - -static int cifs_security_flags_proc_show(struct seq_file *m, void *v) -{ - seq_printf(m, "0x%x\n", global_secflags); - return 0; -} - -static int cifs_security_flags_proc_open(struct inode *inode, struct file *file) -{ - return single_open(file, cifs_security_flags_proc_show, NULL); -} - -/* - * Ensure that if someone sets a MUST flag, that we disable all other MAY - * flags except for the ones corresponding to the given MUST flag. If there are - * multiple MUST flags, then try to prefer more secure ones. - */ -static void -cifs_security_flags_handle_must_flags(unsigned int *flags) -{ - unsigned int signflags = *flags & CIFSSEC_MUST_SIGN; - - if ((*flags & CIFSSEC_MUST_KRB5) == CIFSSEC_MUST_KRB5) - *flags = CIFSSEC_MUST_KRB5; - else if ((*flags & CIFSSEC_MUST_NTLMSSP) == CIFSSEC_MUST_NTLMSSP) - *flags = CIFSSEC_MUST_NTLMSSP; - else if ((*flags & CIFSSEC_MUST_NTLMV2) == CIFSSEC_MUST_NTLMV2) - *flags = CIFSSEC_MUST_NTLMV2; - - *flags |= signflags; -} - -static ssize_t cifs_security_flags_proc_write(struct file *file, - const char __user *buffer, size_t count, loff_t *ppos) -{ - int rc; - unsigned int flags; - char flags_string[12]; - bool bv; - - if ((count < 1) || (count > 11)) - return -EINVAL; - - memset(flags_string, 0, 12); - - if (copy_from_user(flags_string, buffer, count)) - return -EFAULT; - - if (count < 3) { - /* single char or single char followed by null */ - if (strtobool(flags_string, &bv) == 0) { - global_secflags = bv ? CIFSSEC_MAX : CIFSSEC_DEF; - return count; - } else if (!isdigit(flags_string[0])) { - cifs_dbg(VFS, "Invalid SecurityFlags: %s\n", - flags_string); - return -EINVAL; - } - } - - /* else we have a number */ - rc = kstrtouint(flags_string, 0, &flags); - if (rc) { - cifs_dbg(VFS, "Invalid SecurityFlags: %s\n", - flags_string); - return rc; - } - - cifs_dbg(FYI, "sec flags 0x%x\n", flags); - - if (flags == 0) { - cifs_dbg(VFS, "Invalid SecurityFlags: %s\n", flags_string); - return -EINVAL; - } - - if (flags & ~CIFSSEC_MASK) { - cifs_dbg(VFS, "Unsupported security flags: 0x%x\n", - flags & ~CIFSSEC_MASK); - return -EINVAL; - } - - cifs_security_flags_handle_must_flags(&flags); - - /* flags look ok - update the global security flags for cifs module */ - global_secflags = flags; - if (global_secflags & CIFSSEC_MUST_SIGN) { - /* requiring signing implies signing is allowed */ - global_secflags |= CIFSSEC_MAY_SIGN; - cifs_dbg(FYI, "packet signing now required\n"); - } else if ((global_secflags & CIFSSEC_MAY_SIGN) == 0) { - cifs_dbg(FYI, "packet signing disabled\n"); - } - /* BB should we turn on MAY flags for other MUST options? */ - return count; -} - -static const struct proc_ops cifs_security_flags_proc_ops = { - .proc_open = cifs_security_flags_proc_open, - .proc_read = seq_read, - .proc_lseek = seq_lseek, - .proc_release = single_release, - .proc_write = cifs_security_flags_proc_write, -}; - -/* To make it easier to debug, can help to show mount params */ -static int cifs_mount_params_proc_show(struct seq_file *m, void *v) -{ - const struct fs_parameter_spec *p; - const char *type; - - for (p = smb3_fs_parameters; p->name; p++) { - /* cannot use switch with pointers... */ - if (!p->type) { - if (p->flags == fs_param_neg_with_no) - type = "noflag"; - else - type = "flag"; - } else if (p->type == fs_param_is_bool) - type = "bool"; - else if (p->type == fs_param_is_u32) - type = "u32"; - else if (p->type == fs_param_is_u64) - type = "u64"; - else if (p->type == fs_param_is_string) - type = "string"; - else - type = "unknown"; - - seq_printf(m, "%s:%s\n", p->name, type); - } - - return 0; -} - -static int cifs_mount_params_proc_open(struct inode *inode, struct file *file) -{ - return single_open(file, cifs_mount_params_proc_show, NULL); -} - -static const struct proc_ops cifs_mount_params_proc_ops = { - .proc_open = cifs_mount_params_proc_open, - .proc_read = seq_read, - .proc_lseek = seq_lseek, - .proc_release = single_release, - /* No need for write for now */ - /* .proc_write = cifs_mount_params_proc_write, */ -}; - -#else -inline void cifs_proc_init(void) -{ -} - -inline void cifs_proc_clean(void) -{ -} -#endif /* PROC_FS */ diff --git a/fs/cifs/cifs_debug.h b/fs/cifs/cifs_debug.h deleted file mode 100644 index ce5cfd236fdb8..0000000000000 --- a/fs/cifs/cifs_debug.h +++ /dev/null @@ -1,160 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0-or-later */ -/* - * - * Copyright (c) International Business Machines Corp., 2000,2002 - * Modified by Steve French (sfrench@us.ibm.com) - */ - -#ifndef _H_CIFS_DEBUG -#define _H_CIFS_DEBUG - -#ifdef pr_fmt -#undef pr_fmt -#endif - -#define pr_fmt(fmt) "CIFS: " fmt - -void cifs_dump_mem(char *label, void *data, int length); -void cifs_dump_detail(void *buf, struct TCP_Server_Info *ptcp_info); -void cifs_dump_mids(struct TCP_Server_Info *); -extern bool traceSMB; /* flag which enables the function below */ -void dump_smb(void *, int); -#define CIFS_INFO 0x01 -#define CIFS_RC 0x02 -#define CIFS_TIMER 0x04 - -#define VFS 1 -#define FYI 2 -extern int cifsFYI; -#ifdef CONFIG_CIFS_DEBUG2 -#define NOISY 4 -#else -#define NOISY 0 -#endif -#define ONCE 8 - -/* - * debug ON - * -------- - */ -#ifdef CONFIG_CIFS_DEBUG - - -/* - * When adding tracepoints and debug messages we have various choices. - * Some considerations: - * - * Use cifs_dbg(VFS, ...) for things we always want logged, and the user to see - * cifs_info(...) slightly less important, admin can filter via loglevel > 6 - * cifs_dbg(FYI, ...) minor debugging messages, off by default - * trace_smb3_* ftrace functions are preferred for complex debug messages - * intended for developers or experienced admins, off by default - */ - -/* Information level messages, minor events */ -#define cifs_info_func(ratefunc, fmt, ...) \ - pr_info_ ## ratefunc(fmt, ##__VA_ARGS__) - -#define cifs_info(fmt, ...) \ - cifs_info_func(ratelimited, fmt, ##__VA_ARGS__) - -/* information message: e.g., configuration, major event */ -#define cifs_dbg_func(ratefunc, type, fmt, ...) \ -do { \ - if ((type) & FYI && cifsFYI & CIFS_INFO) { \ - pr_debug_ ## ratefunc("%s: " fmt, \ - __FILE__, ##__VA_ARGS__); \ - } else if ((type) & VFS) { \ - pr_err_ ## ratefunc("VFS: " fmt, ##__VA_ARGS__); \ - } else if ((type) & NOISY && (NOISY != 0)) { \ - pr_debug_ ## ratefunc(fmt, ##__VA_ARGS__); \ - } \ -} while (0) - -#define cifs_dbg(type, fmt, ...) \ -do { \ - if ((type) & ONCE) \ - cifs_dbg_func(once, type, fmt, ##__VA_ARGS__); \ - else \ - cifs_dbg_func(ratelimited, type, fmt, ##__VA_ARGS__); \ -} while (0) - -#define cifs_server_dbg_func(ratefunc, type, fmt, ...) \ -do { \ - spin_lock(&server->srv_lock); \ - if ((type) & FYI && cifsFYI & CIFS_INFO) { \ - pr_debug_ ## ratefunc("%s: \\\\%s " fmt, \ - __FILE__, server->hostname, \ - ##__VA_ARGS__); \ - } else if ((type) & VFS) { \ - pr_err_ ## ratefunc("VFS: \\\\%s " fmt, \ - server->hostname, ##__VA_ARGS__); \ - } else if ((type) & NOISY && (NOISY != 0)) { \ - pr_debug_ ## ratefunc("\\\\%s " fmt, \ - server->hostname, ##__VA_ARGS__); \ - } \ - spin_unlock(&server->srv_lock); \ -} while (0) - -#define cifs_server_dbg(type, fmt, ...) \ -do { \ - if ((type) & ONCE) \ - cifs_server_dbg_func(once, type, fmt, ##__VA_ARGS__); \ - else \ - cifs_server_dbg_func(ratelimited, type, fmt, \ - ##__VA_ARGS__); \ -} while (0) - -#define cifs_tcon_dbg_func(ratefunc, type, fmt, ...) \ -do { \ - const char *tn = ""; \ - if (tcon && tcon->tree_name) \ - tn = tcon->tree_name; \ - if ((type) & FYI && cifsFYI & CIFS_INFO) { \ - pr_debug_ ## ratefunc("%s: %s " fmt, \ - __FILE__, tn, ##__VA_ARGS__); \ - } else if ((type) & VFS) { \ - pr_err_ ## ratefunc("VFS: %s " fmt, tn, ##__VA_ARGS__); \ - } else if ((type) & NOISY && (NOISY != 0)) { \ - pr_debug_ ## ratefunc("%s " fmt, tn, ##__VA_ARGS__); \ - } \ -} while (0) - -#define cifs_tcon_dbg(type, fmt, ...) \ -do { \ - if ((type) & ONCE) \ - cifs_tcon_dbg_func(once, type, fmt, ##__VA_ARGS__); \ - else \ - cifs_tcon_dbg_func(ratelimited, type, fmt, \ - ##__VA_ARGS__); \ -} while (0) - -/* - * debug OFF - * --------- - */ -#else /* _CIFS_DEBUG */ -#define cifs_dbg(type, fmt, ...) \ -do { \ - if (0) \ - pr_debug(fmt, ##__VA_ARGS__); \ -} while (0) - -#define cifs_server_dbg(type, fmt, ...) \ -do { \ - if (0) \ - pr_debug("\\\\%s " fmt, \ - server->hostname, ##__VA_ARGS__); \ -} while (0) - -#define cifs_tcon_dbg(type, fmt, ...) \ -do { \ - if (0) \ - pr_debug("%s " fmt, tcon->tree_name, ##__VA_ARGS__); \ -} while (0) - -#define cifs_info(fmt, ...) \ - pr_info(fmt, ##__VA_ARGS__) -#endif - -#endif /* _H_CIFS_DEBUG */ diff --git a/fs/cifs/cifs_dfs_ref.c b/fs/cifs/cifs_dfs_ref.c deleted file mode 100644 index b0864da9ef434..0000000000000 --- a/fs/cifs/cifs_dfs_ref.c +++ /dev/null @@ -1,374 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-or-later -/* - * Contains the CIFS DFS referral mounting routines used for handling - * traversal via DFS junction point - * - * Copyright (c) 2007 Igor Mammedov - * Copyright (C) International Business Machines Corp., 2008 - * Author(s): Igor Mammedov (niallain@gmail.com) - * Steve French (sfrench@us.ibm.com) - */ - -#include -#include -#include -#include -#include -#include -#include -#include "cifsglob.h" -#include "cifsproto.h" -#include "cifsfs.h" -#include "dns_resolve.h" -#include "cifs_debug.h" -#include "cifs_unicode.h" -#include "dfs_cache.h" -#include "fs_context.h" - -static LIST_HEAD(cifs_dfs_automount_list); - -static void cifs_dfs_expire_automounts(struct work_struct *work); -static DECLARE_DELAYED_WORK(cifs_dfs_automount_task, - cifs_dfs_expire_automounts); -static int cifs_dfs_mountpoint_expiry_timeout = 500 * HZ; - -static void cifs_dfs_expire_automounts(struct work_struct *work) -{ - struct list_head *list = &cifs_dfs_automount_list; - - mark_mounts_for_expiry(list); - if (!list_empty(list)) - schedule_delayed_work(&cifs_dfs_automount_task, - cifs_dfs_mountpoint_expiry_timeout); -} - -void cifs_dfs_release_automount_timer(void) -{ - BUG_ON(!list_empty(&cifs_dfs_automount_list)); - cancel_delayed_work_sync(&cifs_dfs_automount_task); -} - -/** - * cifs_build_devname - build a devicename from a UNC and optional prepath - * @nodename: pointer to UNC string - * @prepath: pointer to prefixpath (or NULL if there isn't one) - * - * Build a new cifs devicename after chasing a DFS referral. Allocate a buffer - * big enough to hold the final thing. Copy the UNC from the nodename, and - * concatenate the prepath onto the end of it if there is one. - * - * Returns pointer to the built string, or a ERR_PTR. Caller is responsible - * for freeing the returned string. - */ -static char * -cifs_build_devname(char *nodename, const char *prepath) -{ - size_t pplen; - size_t unclen; - char *dev; - char *pos; - - /* skip over any preceding delimiters */ - nodename += strspn(nodename, "\\"); - if (!*nodename) - return ERR_PTR(-EINVAL); - - /* get length of UNC and set pos to last char */ - unclen = strlen(nodename); - pos = nodename + unclen - 1; - - /* trim off any trailing delimiters */ - while (*pos == '\\') { - --pos; - --unclen; - } - - /* allocate a buffer: - * +2 for preceding "//" - * +1 for delimiter between UNC and prepath - * +1 for trailing NULL - */ - pplen = prepath ? strlen(prepath) : 0; - dev = kmalloc(2 + unclen + 1 + pplen + 1, GFP_KERNEL); - if (!dev) - return ERR_PTR(-ENOMEM); - - pos = dev; - /* add the initial "//" */ - *pos = '/'; - ++pos; - *pos = '/'; - ++pos; - - /* copy in the UNC portion from referral */ - memcpy(pos, nodename, unclen); - pos += unclen; - - /* copy the prefixpath remainder (if there is one) */ - if (pplen) { - *pos = '/'; - ++pos; - memcpy(pos, prepath, pplen); - pos += pplen; - } - - /* NULL terminator */ - *pos = '\0'; - - convert_delimiter(dev, '/'); - return dev; -} - - -/** - * cifs_compose_mount_options - creates mount options for referral - * @sb_mountdata: parent/root DFS mount options (template) - * @fullpath: full path in UNC format - * @ref: optional server's referral - * @devname: return the built cifs device name if passed pointer not NULL - * creates mount options for submount based on template options sb_mountdata - * and replacing unc,ip,prefixpath options with ones we've got form ref_unc. - * - * Returns: pointer to new mount options or ERR_PTR. - * Caller is responsible for freeing returned value if it is not error. - */ -char *cifs_compose_mount_options(const char *sb_mountdata, - const char *fullpath, - const struct dfs_info3_param *ref, - char **devname) -{ - int rc; - char *name; - char *mountdata = NULL; - const char *prepath = NULL; - int md_len; - char *tkn_e; - char *srvIP = NULL; - char sep = ','; - int off, noff; - - if (sb_mountdata == NULL) - return ERR_PTR(-EINVAL); - - if (ref) { - if (WARN_ON_ONCE(!ref->node_name || ref->path_consumed < 0)) - return ERR_PTR(-EINVAL); - - if (strlen(fullpath) - ref->path_consumed) { - prepath = fullpath + ref->path_consumed; - /* skip initial delimiter */ - if (*prepath == '/' || *prepath == '\\') - prepath++; - } - - name = cifs_build_devname(ref->node_name, prepath); - if (IS_ERR(name)) { - rc = PTR_ERR(name); - name = NULL; - goto compose_mount_options_err; - } - } else { - name = cifs_build_devname((char *)fullpath, NULL); - if (IS_ERR(name)) { - rc = PTR_ERR(name); - name = NULL; - goto compose_mount_options_err; - } - } - - rc = dns_resolve_server_name_to_ip(name, &srvIP, NULL); - if (rc < 0) { - cifs_dbg(FYI, "%s: Failed to resolve server part of %s to IP: %d\n", - __func__, name, rc); - goto compose_mount_options_err; - } - - /* - * In most cases, we'll be building a shorter string than the original, - * but we do have to assume that the address in the ip= option may be - * much longer than the original. Add the max length of an address - * string to the length of the original string to allow for worst case. - */ - md_len = strlen(sb_mountdata) + INET6_ADDRSTRLEN; - mountdata = kzalloc(md_len + sizeof("ip=") + 1, GFP_KERNEL); - if (mountdata == NULL) { - rc = -ENOMEM; - goto compose_mount_options_err; - } - - /* copy all options except of unc,ip,prefixpath */ - off = 0; - if (strncmp(sb_mountdata, "sep=", 4) == 0) { - sep = sb_mountdata[4]; - strncpy(mountdata, sb_mountdata, 5); - off += 5; - } - - do { - tkn_e = strchr(sb_mountdata + off, sep); - if (tkn_e == NULL) - noff = strlen(sb_mountdata + off); - else - noff = tkn_e - (sb_mountdata + off) + 1; - - if (strncasecmp(sb_mountdata + off, "cruid=", 6) == 0) { - off += noff; - continue; - } - if (strncasecmp(sb_mountdata + off, "unc=", 4) == 0) { - off += noff; - continue; - } - if (strncasecmp(sb_mountdata + off, "ip=", 3) == 0) { - off += noff; - continue; - } - if (strncasecmp(sb_mountdata + off, "prefixpath=", 11) == 0) { - off += noff; - continue; - } - strncat(mountdata, sb_mountdata + off, noff); - off += noff; - } while (tkn_e); - strcat(mountdata, sb_mountdata + off); - mountdata[md_len] = '\0'; - - /* copy new IP and ref share name */ - if (mountdata[strlen(mountdata) - 1] != sep) - strncat(mountdata, &sep, 1); - strcat(mountdata, "ip="); - strcat(mountdata, srvIP); - - if (devname) - *devname = name; - else - kfree(name); - - /*cifs_dbg(FYI, "%s: parent mountdata: %s\n", __func__, sb_mountdata);*/ - /*cifs_dbg(FYI, "%s: submount mountdata: %s\n", __func__, mountdata );*/ - -compose_mount_options_out: - kfree(srvIP); - return mountdata; - -compose_mount_options_err: - kfree(mountdata); - mountdata = ERR_PTR(rc); - kfree(name); - goto compose_mount_options_out; -} - -/** - * cifs_dfs_do_mount - mounts specified path using DFS full path - * - * Always pass down @fullpath to smb3_do_mount() so we can use the root server - * to perform failover in case we failed to connect to the first target in the - * referral. - * - * @mntpt: directory entry for the path we are trying to automount - * @cifs_sb: parent/root superblock - * @fullpath: full path in UNC format - */ -static struct vfsmount *cifs_dfs_do_mount(struct dentry *mntpt, - struct cifs_sb_info *cifs_sb, - const char *fullpath) -{ - struct vfsmount *mnt; - char *mountdata; - char *devname; - - devname = kstrdup(fullpath, GFP_KERNEL); - if (!devname) - return ERR_PTR(-ENOMEM); - - convert_delimiter(devname, '/'); - - /* TODO: change to call fs_context_for_mount(), fill in context directly, call fc_mount */ - - /* See afs_mntpt_do_automount in fs/afs/mntpt.c for an example */ - - /* strip first '\' from fullpath */ - mountdata = cifs_compose_mount_options(cifs_sb->ctx->mount_options, - fullpath + 1, NULL, NULL); - if (IS_ERR(mountdata)) { - kfree(devname); - return (struct vfsmount *)mountdata; - } - - mnt = vfs_submount(mntpt, &cifs_fs_type, devname, mountdata); - kfree(mountdata); - kfree(devname); - return mnt; -} - -/* - * Create a vfsmount that we can automount - */ -static struct vfsmount *cifs_dfs_do_automount(struct dentry *mntpt) -{ - struct cifs_sb_info *cifs_sb; - void *page; - char *full_path; - struct vfsmount *mnt; - - cifs_dbg(FYI, "in %s\n", __func__); - BUG_ON(IS_ROOT(mntpt)); - - /* - * The MSDFS spec states that paths in DFS referral requests and - * responses must be prefixed by a single '\' character instead of - * the double backslashes usually used in the UNC. This function - * gives us the latter, so we must adjust the result. - */ - cifs_sb = CIFS_SB(mntpt->d_sb); - if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_NO_DFS) { - mnt = ERR_PTR(-EREMOTE); - goto cdda_exit; - } - - page = alloc_dentry_path(); - /* always use tree name prefix */ - full_path = build_path_from_dentry_optional_prefix(mntpt, page, true); - if (IS_ERR(full_path)) { - mnt = ERR_CAST(full_path); - goto free_full_path; - } - - convert_delimiter(full_path, '\\'); - cifs_dbg(FYI, "%s: full_path: %s\n", __func__, full_path); - - mnt = cifs_dfs_do_mount(mntpt, cifs_sb, full_path); - cifs_dbg(FYI, "%s: cifs_dfs_do_mount:%s , mnt:%p\n", __func__, full_path + 1, mnt); - -free_full_path: - free_dentry_path(page); -cdda_exit: - cifs_dbg(FYI, "leaving %s\n" , __func__); - return mnt; -} - -/* - * Attempt to automount the referral - */ -struct vfsmount *cifs_dfs_d_automount(struct path *path) -{ - struct vfsmount *newmnt; - - cifs_dbg(FYI, "in %s\n", __func__); - - newmnt = cifs_dfs_do_automount(path->dentry); - if (IS_ERR(newmnt)) { - cifs_dbg(FYI, "leaving %s [automount failed]\n" , __func__); - return newmnt; - } - - mntget(newmnt); /* prevent immediate expiration */ - mnt_set_expiry(newmnt, &cifs_dfs_automount_list); - schedule_delayed_work(&cifs_dfs_automount_task, - cifs_dfs_mountpoint_expiry_timeout); - cifs_dbg(FYI, "leaving %s [ok]\n" , __func__); - return newmnt; -} - -const struct inode_operations cifs_dfs_referral_inode_operations = { -}; diff --git a/fs/cifs/cifs_fs_sb.h b/fs/cifs/cifs_fs_sb.h deleted file mode 100644 index 013a4bd65280c..0000000000000 --- a/fs/cifs/cifs_fs_sb.h +++ /dev/null @@ -1,76 +0,0 @@ -/* SPDX-License-Identifier: LGPL-2.1 */ -/* - * - * Copyright (c) International Business Machines Corp., 2002,2004 - * Author(s): Steve French (sfrench@us.ibm.com) - * - */ -#include - -#ifndef _CIFS_FS_SB_H -#define _CIFS_FS_SB_H - -#include - -#define CIFS_MOUNT_NO_PERM 1 /* do not do client vfs_perm check */ -#define CIFS_MOUNT_SET_UID 2 /* set current's euid in create etc. */ -#define CIFS_MOUNT_SERVER_INUM 4 /* inode numbers from uniqueid from server */ -#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 */ -#define CIFS_MOUNT_CIFS_ACL 0x200 /* send ACL requests to non-POSIX srv */ -#define CIFS_MOUNT_OVERR_UID 0x400 /* override uid returned from server */ -#define CIFS_MOUNT_OVERR_GID 0x800 /* override gid returned from server */ -#define CIFS_MOUNT_DYNPERM 0x1000 /* allow in-memory only mode setting */ -#define CIFS_MOUNT_NOPOSIXBRL 0x2000 /* mandatory not posix byte range lock */ -#define CIFS_MOUNT_NOSSYNC 0x4000 /* don't do slow SMBflush on every sync*/ -#define CIFS_MOUNT_FSCACHE 0x8000 /* local caching enabled */ -#define CIFS_MOUNT_MF_SYMLINKS 0x10000 /* Minshall+French Symlinks enabled */ -#define CIFS_MOUNT_MULTIUSER 0x20000 /* multiuser mount */ -#define CIFS_MOUNT_STRICT_IO 0x40000 /* strict cache mode */ -#define CIFS_MOUNT_RWPIDFORWARD 0x80000 /* use pid forwarding for rw */ -#define CIFS_MOUNT_POSIXACL 0x100000 /* mirror of SB_POSIXACL in mnt_cifs_flags */ -#define CIFS_MOUNT_CIFS_BACKUPUID 0x200000 /* backup intent bit for a user */ -#define CIFS_MOUNT_CIFS_BACKUPGID 0x400000 /* backup intent bit for a group */ -#define CIFS_MOUNT_MAP_SFM_CHR 0x800000 /* SFM/MAC mapping for illegal chars */ -#define CIFS_MOUNT_USE_PREFIX_PATH 0x1000000 /* make subpath with unaccessible - * root mountable - */ -#define CIFS_MOUNT_UID_FROM_ACL 0x2000000 /* try to get UID via special SID */ -#define CIFS_MOUNT_NO_HANDLE_CACHE 0x4000000 /* disable caching dir handles */ -#define CIFS_MOUNT_NO_DFS 0x8000000 /* disable DFS resolving */ -#define CIFS_MOUNT_MODE_FROM_SID 0x10000000 /* retrieve mode from special ACE */ -#define CIFS_MOUNT_RO_CACHE 0x20000000 /* assumes share will not change */ -#define CIFS_MOUNT_RW_CACHE 0x40000000 /* assumes only client accessing */ -#define CIFS_MOUNT_SHUTDOWN 0x80000000 - -struct cifs_sb_info { - struct rb_root tlink_tree; - spinlock_t tlink_tree_lock; - struct tcon_link *master_tlink; - struct nls_table *local_nls; - struct smb3_fs_context *ctx; - atomic_t active; - unsigned int mnt_cifs_flags; - struct delayed_work prune_tlinks; - struct rcu_head rcu; - - /* only used when CIFS_MOUNT_USE_PREFIX_PATH is set */ - char *prepath; - - /* randomly generated 128-bit number for indexing dfs mount groups in referral cache */ - uuid_t dfs_mount_id; - /* - * Indicate whether serverino option was turned off later - * (cifs_autodisable_serverino) in order to match new mounts. - */ - bool mnt_cifs_serverino_autodisabled; - /* - * Available once the mount has completed. - */ - struct dentry *root; -}; -#endif /* _CIFS_FS_SB_H */ diff --git a/fs/cifs/cifs_ioctl.h b/fs/cifs/cifs_ioctl.h deleted file mode 100644 index d86d78d5bfdc1..0000000000000 --- a/fs/cifs/cifs_ioctl.h +++ /dev/null @@ -1,126 +0,0 @@ -/* SPDX-License-Identifier: LGPL-2.1 */ -/* - * - * Structure definitions for io control for cifs/smb3 - * - * Copyright (c) 2015 Steve French - * - */ - -struct smb_mnt_fs_info { - __u32 version; /* 0001 */ - __u16 protocol_id; - __u16 tcon_flags; - __u32 vol_serial_number; - __u32 vol_create_time; - __u32 share_caps; - __u32 share_flags; - __u32 sector_flags; - __u32 optimal_sector_size; - __u32 max_bytes_chunk; - __u32 fs_attributes; - __u32 max_path_component; - __u32 device_type; - __u32 device_characteristics; - __u32 maximal_access; - __u64 cifs_posix_caps; -} __packed; - -struct smb_snapshot_array { - __u32 number_of_snapshots; - __u32 number_of_snapshots_returned; - __u32 snapshot_array_size; - /* snapshots[]; */ -} __packed; - -/* query_info flags */ -#define PASSTHRU_QUERY_INFO 0x00000000 -#define PASSTHRU_FSCTL 0x00000001 -#define PASSTHRU_SET_INFO 0x00000002 -struct smb_query_info { - __u32 info_type; - __u32 file_info_class; - __u32 additional_information; - __u32 flags; - __u32 input_buffer_length; - __u32 output_buffer_length; - /* char buffer[]; */ -} __packed; - -/* - * Dumping the commonly used 16 byte (e.g. CCM and GCM128) keys still supported - * for backlevel compatibility, but is not sufficient for dumping the less - * frequently used GCM256 (32 byte) keys (see the newer "CIFS_DUMP_FULL_KEY" - * ioctl for dumping decryption info for GCM256 mounts) - */ -struct smb3_key_debug_info { - __u64 Suid; - __u16 cipher_type; - __u8 auth_key[16]; /* SMB2_NTLMV2_SESSKEY_SIZE */ - __u8 smb3encryptionkey[SMB3_SIGN_KEY_SIZE]; - __u8 smb3decryptionkey[SMB3_SIGN_KEY_SIZE]; -} __packed; - -/* - * Dump variable-sized keys - */ -struct smb3_full_key_debug_info { - /* INPUT: size of userspace buffer */ - __u32 in_size; - - /* - * INPUT: 0 for current user, otherwise session to dump - * OUTPUT: session id that was dumped - */ - __u64 session_id; - __u16 cipher_type; - __u8 session_key_length; - __u8 server_in_key_length; - __u8 server_out_key_length; - __u8 data[]; - /* - * return this struct with the keys appended at the end: - * __u8 session_key[session_key_length]; - * __u8 server_in_key[server_in_key_length]; - * __u8 server_out_key[server_out_key_length]; - */ -} __packed; - -struct smb3_notify { - __u32 completion_filter; - bool watch_tree; -} __packed; - -struct smb3_notify_info { - __u32 completion_filter; - bool watch_tree; - __u32 data_len; /* size of notify data below */ - __u8 notify_data[]; -} __packed; - -#define CIFS_IOCTL_MAGIC 0xCF -#define CIFS_IOC_COPYCHUNK_FILE _IOW(CIFS_IOCTL_MAGIC, 3, int) -#define CIFS_IOC_SET_INTEGRITY _IO(CIFS_IOCTL_MAGIC, 4) -#define CIFS_IOC_GET_MNT_INFO _IOR(CIFS_IOCTL_MAGIC, 5, struct smb_mnt_fs_info) -#define CIFS_ENUMERATE_SNAPSHOTS _IOR(CIFS_IOCTL_MAGIC, 6, struct smb_snapshot_array) -#define CIFS_QUERY_INFO _IOWR(CIFS_IOCTL_MAGIC, 7, struct smb_query_info) -#define CIFS_DUMP_KEY _IOWR(CIFS_IOCTL_MAGIC, 8, struct smb3_key_debug_info) -#define CIFS_IOC_NOTIFY _IOW(CIFS_IOCTL_MAGIC, 9, struct smb3_notify) -#define CIFS_DUMP_FULL_KEY _IOWR(CIFS_IOCTL_MAGIC, 10, struct smb3_full_key_debug_info) -#define CIFS_IOC_NOTIFY_INFO _IOWR(CIFS_IOCTL_MAGIC, 11, struct smb3_notify_info) -#define CIFS_IOC_SHUTDOWN _IOR ('X', 125, __u32) - -/* - * Flags for going down operation - */ -#define CIFS_GOING_FLAGS_DEFAULT 0x0 /* going down */ -#define CIFS_GOING_FLAGS_LOGFLUSH 0x1 /* flush log but not data */ -#define CIFS_GOING_FLAGS_NOLOGFLUSH 0x2 /* don't flush log nor data */ - -static inline bool cifs_forced_shutdown(struct cifs_sb_info *sbi) -{ - if (CIFS_MOUNT_SHUTDOWN & sbi->mnt_cifs_flags) - return true; - else - return false; -} diff --git a/fs/cifs/cifs_spnego.c b/fs/cifs/cifs_spnego.c deleted file mode 100644 index 342717bf1dc28..0000000000000 --- a/fs/cifs/cifs_spnego.c +++ /dev/null @@ -1,236 +0,0 @@ -// SPDX-License-Identifier: LGPL-2.1 -/* - * SPNEGO upcall management for CIFS - * - * Copyright (c) 2007 Red Hat, Inc. - * Author(s): Jeff Layton (jlayton@redhat.com) - * - */ - -#include -#include -#include -#include -#include -#include -#include -#include "cifsglob.h" -#include "cifs_spnego.h" -#include "cifs_debug.h" -#include "cifsproto.h" -static const struct cred *spnego_cred; - -/* create a new cifs key */ -static int -cifs_spnego_key_instantiate(struct key *key, struct key_preparsed_payload *prep) -{ - char *payload; - int ret; - - ret = -ENOMEM; - payload = kmemdup(prep->data, prep->datalen, GFP_KERNEL); - if (!payload) - goto error; - - /* attach the data */ - key->payload.data[0] = payload; - ret = 0; - -error: - return ret; -} - -static void -cifs_spnego_key_destroy(struct key *key) -{ - kfree(key->payload.data[0]); -} - - -/* - * keytype for CIFS spnego keys - */ -struct key_type cifs_spnego_key_type = { - .name = "cifs.spnego", - .instantiate = cifs_spnego_key_instantiate, - .destroy = cifs_spnego_key_destroy, - .describe = user_describe, -}; - -/* length of longest version string e.g. strlen("ver=0xFF") */ -#define MAX_VER_STR_LEN 8 - -/* length of longest security mechanism name, eg in future could have - * strlen(";sec=ntlmsspi") */ -#define MAX_MECH_STR_LEN 13 - -/* strlen of "host=" */ -#define HOST_KEY_LEN 5 - -/* strlen of ";ip4=" or ";ip6=" */ -#define IP_KEY_LEN 5 - -/* strlen of ";uid=0x" */ -#define UID_KEY_LEN 7 - -/* strlen of ";creduid=0x" */ -#define CREDUID_KEY_LEN 11 - -/* strlen of ";user=" */ -#define USER_KEY_LEN 6 - -/* strlen of ";pid=0x" */ -#define PID_KEY_LEN 7 - -/* get a key struct with a SPNEGO security blob, suitable for session setup */ -struct key * -cifs_get_spnego_key(struct cifs_ses *sesInfo, - struct TCP_Server_Info *server) -{ - struct sockaddr_in *sa = (struct sockaddr_in *) &server->dstaddr; - struct sockaddr_in6 *sa6 = (struct sockaddr_in6 *) &server->dstaddr; - char *description, *dp; - size_t desc_len; - struct key *spnego_key; - const char *hostname = server->hostname; - const struct cred *saved_cred; - - /* length of fields (with semicolons): ver=0xyz ip4=ipaddress - host=hostname sec=mechanism uid=0xFF user=username */ - desc_len = MAX_VER_STR_LEN + - HOST_KEY_LEN + strlen(hostname) + - IP_KEY_LEN + INET6_ADDRSTRLEN + - MAX_MECH_STR_LEN + - UID_KEY_LEN + (sizeof(uid_t) * 2) + - CREDUID_KEY_LEN + (sizeof(uid_t) * 2) + - PID_KEY_LEN + (sizeof(pid_t) * 2) + 1; - - if (sesInfo->user_name) - desc_len += USER_KEY_LEN + strlen(sesInfo->user_name); - - spnego_key = ERR_PTR(-ENOMEM); - description = kzalloc(desc_len, GFP_KERNEL); - if (description == NULL) - goto out; - - dp = description; - /* start with version and hostname portion of UNC string */ - spnego_key = ERR_PTR(-EINVAL); - sprintf(dp, "ver=0x%x;host=%s;", CIFS_SPNEGO_UPCALL_VERSION, - hostname); - dp = description + strlen(description); - - /* add the server address */ - if (server->dstaddr.ss_family == AF_INET) - sprintf(dp, "ip4=%pI4", &sa->sin_addr); - else if (server->dstaddr.ss_family == AF_INET6) - sprintf(dp, "ip6=%pI6", &sa6->sin6_addr); - else - goto out; - - dp = description + strlen(description); - - /* for now, only sec=krb5 and sec=mskrb5 are valid */ - if (server->sec_kerberos) - sprintf(dp, ";sec=krb5"); - else if (server->sec_mskerberos) - sprintf(dp, ";sec=mskrb5"); - else { - cifs_dbg(VFS, "unknown or missing server auth type, use krb5\n"); - sprintf(dp, ";sec=krb5"); - } - - dp = description + strlen(description); - sprintf(dp, ";uid=0x%x", - from_kuid_munged(&init_user_ns, sesInfo->linux_uid)); - - dp = description + strlen(description); - sprintf(dp, ";creduid=0x%x", - from_kuid_munged(&init_user_ns, sesInfo->cred_uid)); - - if (sesInfo->user_name) { - dp = description + strlen(description); - sprintf(dp, ";user=%s", sesInfo->user_name); - } - - dp = description + strlen(description); - sprintf(dp, ";pid=0x%x", current->pid); - - cifs_dbg(FYI, "key description = %s\n", description); - saved_cred = override_creds(spnego_cred); - spnego_key = request_key(&cifs_spnego_key_type, description, ""); - revert_creds(saved_cred); - -#ifdef CONFIG_CIFS_DEBUG2 - if (cifsFYI && !IS_ERR(spnego_key)) { - struct cifs_spnego_msg *msg = spnego_key->payload.data[0]; - cifs_dump_mem("SPNEGO reply blob:", msg->data, min(1024U, - msg->secblob_len + msg->sesskey_len)); - } -#endif /* CONFIG_CIFS_DEBUG2 */ - -out: - kfree(description); - return spnego_key; -} - -int -init_cifs_spnego(void) -{ - struct cred *cred; - struct key *keyring; - int ret; - - cifs_dbg(FYI, "Registering the %s key type\n", - cifs_spnego_key_type.name); - - /* - * Create an override credential set with special thread keyring for - * spnego upcalls. - */ - - cred = prepare_kernel_cred(NULL); - if (!cred) - return -ENOMEM; - - keyring = keyring_alloc(".cifs_spnego", - GLOBAL_ROOT_UID, GLOBAL_ROOT_GID, cred, - (KEY_POS_ALL & ~KEY_POS_SETATTR) | - KEY_USR_VIEW | KEY_USR_READ, - KEY_ALLOC_NOT_IN_QUOTA, NULL, NULL); - if (IS_ERR(keyring)) { - ret = PTR_ERR(keyring); - goto failed_put_cred; - } - - ret = register_key_type(&cifs_spnego_key_type); - if (ret < 0) - goto failed_put_key; - - /* - * instruct request_key() to use this special keyring as a cache for - * the results it looks up - */ - set_bit(KEY_FLAG_ROOT_CAN_CLEAR, &keyring->flags); - cred->thread_keyring = keyring; - cred->jit_keyring = KEY_REQKEY_DEFL_THREAD_KEYRING; - spnego_cred = cred; - - cifs_dbg(FYI, "cifs spnego keyring: %d\n", key_serial(keyring)); - return 0; - -failed_put_key: - key_put(keyring); -failed_put_cred: - put_cred(cred); - return ret; -} - -void -exit_cifs_spnego(void) -{ - key_revoke(spnego_cred->thread_keyring); - unregister_key_type(&cifs_spnego_key_type); - put_cred(spnego_cred); - cifs_dbg(FYI, "Unregistered %s key type\n", cifs_spnego_key_type.name); -} diff --git a/fs/cifs/cifs_spnego.h b/fs/cifs/cifs_spnego.h deleted file mode 100644 index 7f102ffeb6750..0000000000000 --- a/fs/cifs/cifs_spnego.h +++ /dev/null @@ -1,36 +0,0 @@ -/* SPDX-License-Identifier: LGPL-2.1 */ -/* - * SPNEGO upcall management for CIFS - * - * Copyright (c) 2007 Red Hat, Inc. - * Author(s): Jeff Layton (jlayton@redhat.com) - * Steve French (sfrench@us.ibm.com) - * - */ - -#ifndef _CIFS_SPNEGO_H -#define _CIFS_SPNEGO_H - -#define CIFS_SPNEGO_UPCALL_VERSION 2 - -/* - * The version field should always be set to CIFS_SPNEGO_UPCALL_VERSION. - * The flags field is for future use. The request-key callout should set - * sesskey_len and secblob_len, and then concatenate the SessKey+SecBlob - * and stuff it in the data field. - */ -struct cifs_spnego_msg { - uint32_t version; - uint32_t flags; - uint32_t sesskey_len; - uint32_t secblob_len; - uint8_t data[1]; -}; - -#ifdef __KERNEL__ -extern struct key_type cifs_spnego_key_type; -extern struct key *cifs_get_spnego_key(struct cifs_ses *sesInfo, - struct TCP_Server_Info *server); -#endif /* KERNEL */ - -#endif /* _CIFS_SPNEGO_H */ diff --git a/fs/cifs/cifs_spnego_negtokeninit.asn1 b/fs/cifs/cifs_spnego_negtokeninit.asn1 deleted file mode 100644 index 181c083887d51..0000000000000 --- a/fs/cifs/cifs_spnego_negtokeninit.asn1 +++ /dev/null @@ -1,40 +0,0 @@ -GSSAPI ::= - [APPLICATION 0] IMPLICIT SEQUENCE { - thisMech - OBJECT IDENTIFIER ({cifs_gssapi_this_mech}), - negotiationToken - NegotiationToken - } - -MechType ::= OBJECT IDENTIFIER ({cifs_neg_token_init_mech_type}) - -MechTypeList ::= SEQUENCE OF MechType - -NegHints ::= SEQUENCE { - hintName - [0] GeneralString OPTIONAL, - hintAddress - [1] OCTET STRING OPTIONAL - } - -NegTokenInit2 ::= - SEQUENCE { - mechTypes - [0] MechTypeList OPTIONAL, - reqFlags - [1] BIT STRING OPTIONAL, - mechToken - [2] OCTET STRING OPTIONAL, - negHints - [3] NegHints OPTIONAL, - mechListMIC - [3] OCTET STRING OPTIONAL - } - -NegotiationToken ::= - CHOICE { - negTokenInit - [0] NegTokenInit2, - negTokenTarg - [1] ANY - } diff --git a/fs/cifs/cifs_swn.c b/fs/cifs/cifs_swn.c deleted file mode 100644 index 7233c6a7e6d70..0000000000000 --- a/fs/cifs/cifs_swn.c +++ /dev/null @@ -1,674 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0 -/* - * Witness Service client for CIFS - * - * Copyright (c) 2020 Samuel Cabrero - */ - -#include -#include -#include - -#include "cifs_swn.h" -#include "cifsglob.h" -#include "cifsproto.h" -#include "fscache.h" -#include "cifs_debug.h" -#include "netlink.h" - -static DEFINE_IDR(cifs_swnreg_idr); -static DEFINE_MUTEX(cifs_swnreg_idr_mutex); - -struct cifs_swn_reg { - int id; - struct kref ref_count; - - const char *net_name; - const char *share_name; - bool net_name_notify; - bool share_name_notify; - bool ip_notify; - - struct cifs_tcon *tcon; -}; - -static int cifs_swn_auth_info_krb(struct cifs_tcon *tcon, struct sk_buff *skb) -{ - int ret; - - ret = nla_put_flag(skb, CIFS_GENL_ATTR_SWN_KRB_AUTH); - if (ret < 0) - return ret; - - return 0; -} - -static int cifs_swn_auth_info_ntlm(struct cifs_tcon *tcon, struct sk_buff *skb) -{ - int ret; - - if (tcon->ses->user_name != NULL) { - ret = nla_put_string(skb, CIFS_GENL_ATTR_SWN_USER_NAME, tcon->ses->user_name); - if (ret < 0) - return ret; - } - - if (tcon->ses->password != NULL) { - ret = nla_put_string(skb, CIFS_GENL_ATTR_SWN_PASSWORD, tcon->ses->password); - if (ret < 0) - return ret; - } - - if (tcon->ses->domainName != NULL) { - ret = nla_put_string(skb, CIFS_GENL_ATTR_SWN_DOMAIN_NAME, tcon->ses->domainName); - if (ret < 0) - return ret; - } - - return 0; -} - -/* - * Sends a register message to the userspace daemon based on the registration. - * The authentication information to connect to the witness service is bundled - * into the message. - */ -static int cifs_swn_send_register_message(struct cifs_swn_reg *swnreg) -{ - struct sk_buff *skb; - struct genlmsghdr *hdr; - enum securityEnum authtype; - struct sockaddr_storage *addr; - int ret; - - skb = genlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL); - if (skb == NULL) { - ret = -ENOMEM; - goto fail; - } - - hdr = genlmsg_put(skb, 0, 0, &cifs_genl_family, 0, CIFS_GENL_CMD_SWN_REGISTER); - if (hdr == NULL) { - ret = -ENOMEM; - goto nlmsg_fail; - } - - ret = nla_put_u32(skb, CIFS_GENL_ATTR_SWN_REGISTRATION_ID, swnreg->id); - if (ret < 0) - goto nlmsg_fail; - - ret = nla_put_string(skb, CIFS_GENL_ATTR_SWN_NET_NAME, swnreg->net_name); - if (ret < 0) - goto nlmsg_fail; - - ret = nla_put_string(skb, CIFS_GENL_ATTR_SWN_SHARE_NAME, swnreg->share_name); - if (ret < 0) - goto nlmsg_fail; - - /* - * If there is an address stored use it instead of the server address, because we are - * in the process of reconnecting to it after a share has been moved or we have been - * told to switch to it (client move message). In these cases we unregister from the - * server address and register to the new address when we receive the notification. - */ - if (swnreg->tcon->ses->server->use_swn_dstaddr) - addr = &swnreg->tcon->ses->server->swn_dstaddr; - else - addr = &swnreg->tcon->ses->server->dstaddr; - - ret = nla_put(skb, CIFS_GENL_ATTR_SWN_IP, sizeof(struct sockaddr_storage), addr); - if (ret < 0) - goto nlmsg_fail; - - if (swnreg->net_name_notify) { - ret = nla_put_flag(skb, CIFS_GENL_ATTR_SWN_NET_NAME_NOTIFY); - if (ret < 0) - goto nlmsg_fail; - } - - if (swnreg->share_name_notify) { - ret = nla_put_flag(skb, CIFS_GENL_ATTR_SWN_SHARE_NAME_NOTIFY); - if (ret < 0) - goto nlmsg_fail; - } - - if (swnreg->ip_notify) { - ret = nla_put_flag(skb, CIFS_GENL_ATTR_SWN_IP_NOTIFY); - if (ret < 0) - goto nlmsg_fail; - } - - authtype = cifs_select_sectype(swnreg->tcon->ses->server, swnreg->tcon->ses->sectype); - switch (authtype) { - case Kerberos: - ret = cifs_swn_auth_info_krb(swnreg->tcon, skb); - if (ret < 0) { - cifs_dbg(VFS, "%s: Failed to get kerberos auth info: %d\n", __func__, ret); - goto nlmsg_fail; - } - break; - case NTLMv2: - case RawNTLMSSP: - ret = cifs_swn_auth_info_ntlm(swnreg->tcon, skb); - if (ret < 0) { - cifs_dbg(VFS, "%s: Failed to get NTLM auth info: %d\n", __func__, ret); - goto nlmsg_fail; - } - break; - default: - cifs_dbg(VFS, "%s: secType %d not supported!\n", __func__, authtype); - ret = -EINVAL; - goto nlmsg_fail; - } - - genlmsg_end(skb, hdr); - genlmsg_multicast(&cifs_genl_family, skb, 0, CIFS_GENL_MCGRP_SWN, GFP_ATOMIC); - - cifs_dbg(FYI, "%s: Message to register for network name %s with id %d sent\n", __func__, - swnreg->net_name, swnreg->id); - - return 0; - -nlmsg_fail: - genlmsg_cancel(skb, hdr); - nlmsg_free(skb); -fail: - return ret; -} - -/* - * Sends an uregister message to the userspace daemon based on the registration - */ -static int cifs_swn_send_unregister_message(struct cifs_swn_reg *swnreg) -{ - struct sk_buff *skb; - struct genlmsghdr *hdr; - int ret; - - skb = genlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL); - if (skb == NULL) - return -ENOMEM; - - hdr = genlmsg_put(skb, 0, 0, &cifs_genl_family, 0, CIFS_GENL_CMD_SWN_UNREGISTER); - if (hdr == NULL) { - ret = -ENOMEM; - goto nlmsg_fail; - } - - ret = nla_put_u32(skb, CIFS_GENL_ATTR_SWN_REGISTRATION_ID, swnreg->id); - if (ret < 0) - goto nlmsg_fail; - - ret = nla_put_string(skb, CIFS_GENL_ATTR_SWN_NET_NAME, swnreg->net_name); - if (ret < 0) - goto nlmsg_fail; - - ret = nla_put_string(skb, CIFS_GENL_ATTR_SWN_SHARE_NAME, swnreg->share_name); - if (ret < 0) - goto nlmsg_fail; - - ret = nla_put(skb, CIFS_GENL_ATTR_SWN_IP, sizeof(struct sockaddr_storage), - &swnreg->tcon->ses->server->dstaddr); - if (ret < 0) - goto nlmsg_fail; - - if (swnreg->net_name_notify) { - ret = nla_put_flag(skb, CIFS_GENL_ATTR_SWN_NET_NAME_NOTIFY); - if (ret < 0) - goto nlmsg_fail; - } - - if (swnreg->share_name_notify) { - ret = nla_put_flag(skb, CIFS_GENL_ATTR_SWN_SHARE_NAME_NOTIFY); - if (ret < 0) - goto nlmsg_fail; - } - - if (swnreg->ip_notify) { - ret = nla_put_flag(skb, CIFS_GENL_ATTR_SWN_IP_NOTIFY); - if (ret < 0) - goto nlmsg_fail; - } - - genlmsg_end(skb, hdr); - genlmsg_multicast(&cifs_genl_family, skb, 0, CIFS_GENL_MCGRP_SWN, GFP_ATOMIC); - - cifs_dbg(FYI, "%s: Message to unregister for network name %s with id %d sent\n", __func__, - swnreg->net_name, swnreg->id); - - return 0; - -nlmsg_fail: - genlmsg_cancel(skb, hdr); - nlmsg_free(skb); - return ret; -} - -/* - * Try to find a matching registration for the tcon's server name and share name. - * Calls to this function must be protected by cifs_swnreg_idr_mutex. - * TODO Try to avoid memory allocations - */ -static struct cifs_swn_reg *cifs_find_swn_reg(struct cifs_tcon *tcon) -{ - struct cifs_swn_reg *swnreg; - int id; - const char *share_name; - const char *net_name; - - net_name = extract_hostname(tcon->tree_name); - if (IS_ERR(net_name)) { - int ret; - - ret = PTR_ERR(net_name); - cifs_dbg(VFS, "%s: failed to extract host name from target '%s': %d\n", - __func__, tcon->tree_name, ret); - return ERR_PTR(-EINVAL); - } - - share_name = extract_sharename(tcon->tree_name); - if (IS_ERR(share_name)) { - int ret; - - ret = PTR_ERR(share_name); - cifs_dbg(VFS, "%s: failed to extract share name from target '%s': %d\n", - __func__, tcon->tree_name, ret); - kfree(net_name); - return ERR_PTR(-EINVAL); - } - - idr_for_each_entry(&cifs_swnreg_idr, swnreg, id) { - if (strcasecmp(swnreg->net_name, net_name) != 0 - || strcasecmp(swnreg->share_name, share_name) != 0) { - continue; - } - - cifs_dbg(FYI, "Existing swn registration for %s:%s found\n", swnreg->net_name, - swnreg->share_name); - - kfree(net_name); - kfree(share_name); - - return swnreg; - } - - kfree(net_name); - kfree(share_name); - - return ERR_PTR(-EEXIST); -} - -/* - * Get a registration for the tcon's server and share name, allocating a new one if it does not - * exists - */ -static struct cifs_swn_reg *cifs_get_swn_reg(struct cifs_tcon *tcon) -{ - struct cifs_swn_reg *reg = NULL; - int ret; - - mutex_lock(&cifs_swnreg_idr_mutex); - - /* Check if we are already registered for this network and share names */ - reg = cifs_find_swn_reg(tcon); - if (!IS_ERR(reg)) { - kref_get(®->ref_count); - mutex_unlock(&cifs_swnreg_idr_mutex); - return reg; - } else if (PTR_ERR(reg) != -EEXIST) { - mutex_unlock(&cifs_swnreg_idr_mutex); - return reg; - } - - reg = kmalloc(sizeof(struct cifs_swn_reg), GFP_ATOMIC); - if (reg == NULL) { - mutex_unlock(&cifs_swnreg_idr_mutex); - return ERR_PTR(-ENOMEM); - } - - kref_init(®->ref_count); - - reg->id = idr_alloc(&cifs_swnreg_idr, reg, 1, 0, GFP_ATOMIC); - if (reg->id < 0) { - cifs_dbg(FYI, "%s: failed to allocate registration id\n", __func__); - ret = reg->id; - goto fail; - } - - reg->net_name = extract_hostname(tcon->tree_name); - if (IS_ERR(reg->net_name)) { - ret = PTR_ERR(reg->net_name); - cifs_dbg(VFS, "%s: failed to extract host name from target: %d\n", __func__, ret); - goto fail_idr; - } - - reg->share_name = extract_sharename(tcon->tree_name); - if (IS_ERR(reg->share_name)) { - ret = PTR_ERR(reg->share_name); - cifs_dbg(VFS, "%s: failed to extract share name from target: %d\n", __func__, ret); - goto fail_net_name; - } - - reg->net_name_notify = true; - reg->share_name_notify = true; - reg->ip_notify = (tcon->capabilities & SMB2_SHARE_CAP_SCALEOUT); - - reg->tcon = tcon; - - mutex_unlock(&cifs_swnreg_idr_mutex); - - return reg; - -fail_net_name: - kfree(reg->net_name); -fail_idr: - idr_remove(&cifs_swnreg_idr, reg->id); -fail: - kfree(reg); - mutex_unlock(&cifs_swnreg_idr_mutex); - return ERR_PTR(ret); -} - -static void cifs_swn_reg_release(struct kref *ref) -{ - struct cifs_swn_reg *swnreg = container_of(ref, struct cifs_swn_reg, ref_count); - int ret; - - ret = cifs_swn_send_unregister_message(swnreg); - if (ret < 0) - cifs_dbg(VFS, "%s: Failed to send unregister message: %d\n", __func__, ret); - - idr_remove(&cifs_swnreg_idr, swnreg->id); - kfree(swnreg->net_name); - kfree(swnreg->share_name); - kfree(swnreg); -} - -static void cifs_put_swn_reg(struct cifs_swn_reg *swnreg) -{ - mutex_lock(&cifs_swnreg_idr_mutex); - kref_put(&swnreg->ref_count, cifs_swn_reg_release); - mutex_unlock(&cifs_swnreg_idr_mutex); -} - -static int cifs_swn_resource_state_changed(struct cifs_swn_reg *swnreg, const char *name, int state) -{ - switch (state) { - case CIFS_SWN_RESOURCE_STATE_UNAVAILABLE: - cifs_dbg(FYI, "%s: resource name '%s' become unavailable\n", __func__, name); - cifs_signal_cifsd_for_reconnect(swnreg->tcon->ses->server, true); - break; - case CIFS_SWN_RESOURCE_STATE_AVAILABLE: - cifs_dbg(FYI, "%s: resource name '%s' become available\n", __func__, name); - cifs_signal_cifsd_for_reconnect(swnreg->tcon->ses->server, true); - break; - case CIFS_SWN_RESOURCE_STATE_UNKNOWN: - cifs_dbg(FYI, "%s: resource name '%s' changed to unknown state\n", __func__, name); - break; - } - return 0; -} - -static bool cifs_sockaddr_equal(struct sockaddr_storage *addr1, struct sockaddr_storage *addr2) -{ - if (addr1->ss_family != addr2->ss_family) - return false; - - if (addr1->ss_family == AF_INET) { - return (memcmp(&((const struct sockaddr_in *)addr1)->sin_addr, - &((const struct sockaddr_in *)addr2)->sin_addr, - sizeof(struct in_addr)) == 0); - } - - if (addr1->ss_family == AF_INET6) { - return (memcmp(&((const struct sockaddr_in6 *)addr1)->sin6_addr, - &((const struct sockaddr_in6 *)addr2)->sin6_addr, - sizeof(struct in6_addr)) == 0); - } - - return false; -} - -static int cifs_swn_store_swn_addr(const struct sockaddr_storage *new, - const struct sockaddr_storage *old, - struct sockaddr_storage *dst) -{ - __be16 port = cpu_to_be16(CIFS_PORT); - - if (old->ss_family == AF_INET) { - struct sockaddr_in *ipv4 = (struct sockaddr_in *)old; - - port = ipv4->sin_port; - } else if (old->ss_family == AF_INET6) { - struct sockaddr_in6 *ipv6 = (struct sockaddr_in6 *)old; - - port = ipv6->sin6_port; - } - - if (new->ss_family == AF_INET) { - struct sockaddr_in *ipv4 = (struct sockaddr_in *)new; - - ipv4->sin_port = port; - } else if (new->ss_family == AF_INET6) { - struct sockaddr_in6 *ipv6 = (struct sockaddr_in6 *)new; - - ipv6->sin6_port = port; - } - - *dst = *new; - - return 0; -} - -static int cifs_swn_reconnect(struct cifs_tcon *tcon, struct sockaddr_storage *addr) -{ - int ret = 0; - - /* Store the reconnect address */ - cifs_server_lock(tcon->ses->server); - if (cifs_sockaddr_equal(&tcon->ses->server->dstaddr, addr)) - goto unlock; - - ret = cifs_swn_store_swn_addr(addr, &tcon->ses->server->dstaddr, - &tcon->ses->server->swn_dstaddr); - if (ret < 0) { - cifs_dbg(VFS, "%s: failed to store address: %d\n", __func__, ret); - goto unlock; - } - tcon->ses->server->use_swn_dstaddr = true; - - /* - * Unregister to stop receiving notifications for the old IP address. - */ - ret = cifs_swn_unregister(tcon); - if (ret < 0) { - cifs_dbg(VFS, "%s: Failed to unregister for witness notifications: %d\n", - __func__, ret); - goto unlock; - } - - /* - * And register to receive notifications for the new IP address now that we have - * stored the new address. - */ - ret = cifs_swn_register(tcon); - if (ret < 0) { - cifs_dbg(VFS, "%s: Failed to register for witness notifications: %d\n", - __func__, ret); - goto unlock; - } - - cifs_signal_cifsd_for_reconnect(tcon->ses->server, false); - -unlock: - cifs_server_unlock(tcon->ses->server); - - return ret; -} - -static int cifs_swn_client_move(struct cifs_swn_reg *swnreg, struct sockaddr_storage *addr) -{ - struct sockaddr_in *ipv4 = (struct sockaddr_in *)addr; - struct sockaddr_in6 *ipv6 = (struct sockaddr_in6 *)addr; - - if (addr->ss_family == AF_INET) - cifs_dbg(FYI, "%s: move to %pI4\n", __func__, &ipv4->sin_addr); - else if (addr->ss_family == AF_INET6) - cifs_dbg(FYI, "%s: move to %pI6\n", __func__, &ipv6->sin6_addr); - - return cifs_swn_reconnect(swnreg->tcon, addr); -} - -int cifs_swn_notify(struct sk_buff *skb, struct genl_info *info) -{ - struct cifs_swn_reg *swnreg; - char name[256]; - int type; - - if (info->attrs[CIFS_GENL_ATTR_SWN_REGISTRATION_ID]) { - int swnreg_id; - - swnreg_id = nla_get_u32(info->attrs[CIFS_GENL_ATTR_SWN_REGISTRATION_ID]); - mutex_lock(&cifs_swnreg_idr_mutex); - swnreg = idr_find(&cifs_swnreg_idr, swnreg_id); - mutex_unlock(&cifs_swnreg_idr_mutex); - if (swnreg == NULL) { - cifs_dbg(FYI, "%s: registration id %d not found\n", __func__, swnreg_id); - return -EINVAL; - } - } else { - cifs_dbg(FYI, "%s: missing registration id attribute\n", __func__); - return -EINVAL; - } - - if (info->attrs[CIFS_GENL_ATTR_SWN_NOTIFICATION_TYPE]) { - type = nla_get_u32(info->attrs[CIFS_GENL_ATTR_SWN_NOTIFICATION_TYPE]); - } else { - cifs_dbg(FYI, "%s: missing notification type attribute\n", __func__); - return -EINVAL; - } - - switch (type) { - case CIFS_SWN_NOTIFICATION_RESOURCE_CHANGE: { - int state; - - if (info->attrs[CIFS_GENL_ATTR_SWN_RESOURCE_NAME]) { - nla_strscpy(name, info->attrs[CIFS_GENL_ATTR_SWN_RESOURCE_NAME], - sizeof(name)); - } else { - cifs_dbg(FYI, "%s: missing resource name attribute\n", __func__); - return -EINVAL; - } - if (info->attrs[CIFS_GENL_ATTR_SWN_RESOURCE_STATE]) { - state = nla_get_u32(info->attrs[CIFS_GENL_ATTR_SWN_RESOURCE_STATE]); - } else { - cifs_dbg(FYI, "%s: missing resource state attribute\n", __func__); - return -EINVAL; - } - return cifs_swn_resource_state_changed(swnreg, name, state); - } - case CIFS_SWN_NOTIFICATION_CLIENT_MOVE: { - struct sockaddr_storage addr; - - if (info->attrs[CIFS_GENL_ATTR_SWN_IP]) { - nla_memcpy(&addr, info->attrs[CIFS_GENL_ATTR_SWN_IP], sizeof(addr)); - } else { - cifs_dbg(FYI, "%s: missing IP address attribute\n", __func__); - return -EINVAL; - } - return cifs_swn_client_move(swnreg, &addr); - } - default: - cifs_dbg(FYI, "%s: unknown notification type %d\n", __func__, type); - break; - } - - return 0; -} - -int cifs_swn_register(struct cifs_tcon *tcon) -{ - struct cifs_swn_reg *swnreg; - int ret; - - swnreg = cifs_get_swn_reg(tcon); - if (IS_ERR(swnreg)) - return PTR_ERR(swnreg); - - ret = cifs_swn_send_register_message(swnreg); - if (ret < 0) { - cifs_dbg(VFS, "%s: Failed to send swn register message: %d\n", __func__, ret); - /* Do not put the swnreg or return error, the echo task will retry */ - } - - return 0; -} - -int cifs_swn_unregister(struct cifs_tcon *tcon) -{ - struct cifs_swn_reg *swnreg; - - mutex_lock(&cifs_swnreg_idr_mutex); - - swnreg = cifs_find_swn_reg(tcon); - if (IS_ERR(swnreg)) { - mutex_unlock(&cifs_swnreg_idr_mutex); - return PTR_ERR(swnreg); - } - - mutex_unlock(&cifs_swnreg_idr_mutex); - - cifs_put_swn_reg(swnreg); - - return 0; -} - -void cifs_swn_dump(struct seq_file *m) -{ - struct cifs_swn_reg *swnreg; - struct sockaddr_in *sa; - struct sockaddr_in6 *sa6; - int id; - - seq_puts(m, "Witness registrations:"); - - mutex_lock(&cifs_swnreg_idr_mutex); - idr_for_each_entry(&cifs_swnreg_idr, swnreg, id) { - seq_printf(m, "\nId: %u Refs: %u Network name: '%s'%s Share name: '%s'%s Ip address: ", - id, kref_read(&swnreg->ref_count), - swnreg->net_name, swnreg->net_name_notify ? "(y)" : "(n)", - swnreg->share_name, swnreg->share_name_notify ? "(y)" : "(n)"); - switch (swnreg->tcon->ses->server->dstaddr.ss_family) { - case AF_INET: - sa = (struct sockaddr_in *) &swnreg->tcon->ses->server->dstaddr; - seq_printf(m, "%pI4", &sa->sin_addr.s_addr); - break; - case AF_INET6: - sa6 = (struct sockaddr_in6 *) &swnreg->tcon->ses->server->dstaddr; - seq_printf(m, "%pI6", &sa6->sin6_addr.s6_addr); - if (sa6->sin6_scope_id) - seq_printf(m, "%%%u", sa6->sin6_scope_id); - break; - default: - seq_puts(m, "(unknown)"); - } - seq_printf(m, "%s", swnreg->ip_notify ? "(y)" : "(n)"); - } - mutex_unlock(&cifs_swnreg_idr_mutex); - seq_puts(m, "\n"); -} - -void cifs_swn_check(void) -{ - struct cifs_swn_reg *swnreg; - int id; - int ret; - - mutex_lock(&cifs_swnreg_idr_mutex); - idr_for_each_entry(&cifs_swnreg_idr, swnreg, id) { - ret = cifs_swn_send_register_message(swnreg); - if (ret < 0) - cifs_dbg(FYI, "%s: Failed to send register message: %d\n", __func__, ret); - } - mutex_unlock(&cifs_swnreg_idr_mutex); -} diff --git a/fs/cifs/cifs_swn.h b/fs/cifs/cifs_swn.h deleted file mode 100644 index 8a9d2a5c9077e..0000000000000 --- a/fs/cifs/cifs_swn.h +++ /dev/null @@ -1,52 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0 */ -/* - * Witness Service client for CIFS - * - * Copyright (c) 2020 Samuel Cabrero - */ - -#ifndef _CIFS_SWN_H -#define _CIFS_SWN_H -#include "cifsglob.h" - -struct cifs_tcon; -struct sk_buff; -struct genl_info; - -#ifdef CONFIG_CIFS_SWN_UPCALL -extern int cifs_swn_register(struct cifs_tcon *tcon); - -extern int cifs_swn_unregister(struct cifs_tcon *tcon); - -extern int cifs_swn_notify(struct sk_buff *skb, struct genl_info *info); - -extern void cifs_swn_dump(struct seq_file *m); - -extern void cifs_swn_check(void); - -static inline bool cifs_swn_set_server_dstaddr(struct TCP_Server_Info *server) -{ - if (server->use_swn_dstaddr) { - server->dstaddr = server->swn_dstaddr; - return true; - } - return false; -} - -static inline void cifs_swn_reset_server_dstaddr(struct TCP_Server_Info *server) -{ - server->use_swn_dstaddr = false; -} - -#else - -static inline int cifs_swn_register(struct cifs_tcon *tcon) { return 0; } -static inline int cifs_swn_unregister(struct cifs_tcon *tcon) { return 0; } -static inline int cifs_swn_notify(struct sk_buff *s, struct genl_info *i) { return 0; } -static inline void cifs_swn_dump(struct seq_file *m) {} -static inline void cifs_swn_check(void) {} -static inline bool cifs_swn_set_server_dstaddr(struct TCP_Server_Info *server) { return false; } -static inline void cifs_swn_reset_server_dstaddr(struct TCP_Server_Info *server) {} - -#endif /* CONFIG_CIFS_SWN_UPCALL */ -#endif /* _CIFS_SWN_H */ diff --git a/fs/cifs/cifs_unicode.c b/fs/cifs/cifs_unicode.c deleted file mode 100644 index e7582dd791794..0000000000000 --- a/fs/cifs/cifs_unicode.c +++ /dev/null @@ -1,632 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-or-later -/* - * - * Copyright (c) International Business Machines Corp., 2000,2009 - * Modified by Steve French (sfrench@us.ibm.com) - */ -#include -#include -#include "cifs_fs_sb.h" -#include "cifs_unicode.h" -#include "cifs_uniupr.h" -#include "cifspdu.h" -#include "cifsglob.h" -#include "cifs_debug.h" - -int cifs_remap(struct cifs_sb_info *cifs_sb) -{ - int map_type; - - if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_MAP_SFM_CHR) - map_type = SFM_MAP_UNI_RSVD; - else if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_MAP_SPECIAL_CHR) - map_type = SFU_MAP_UNI_RSVD; - else - map_type = NO_MAP_UNI_RSVD; - - return map_type; -} - -/* Convert character using the SFU - "Services for Unix" remapping range */ -static bool -convert_sfu_char(const __u16 src_char, char *target) -{ - /* - * BB: Cannot handle remapping UNI_SLASH until all the calls to - * build_path_from_dentry are modified, as they use slash as - * separator. - */ - switch (src_char) { - case UNI_COLON: - *target = ':'; - break; - case UNI_ASTERISK: - *target = '*'; - break; - case UNI_QUESTION: - *target = '?'; - break; - case UNI_PIPE: - *target = '|'; - break; - case UNI_GRTRTHAN: - *target = '>'; - break; - case UNI_LESSTHAN: - *target = '<'; - break; - default: - return false; - } - return true; -} - -/* Convert character using the SFM - "Services for Mac" remapping range */ -static bool -convert_sfm_char(const __u16 src_char, char *target) -{ - if (src_char >= 0xF001 && src_char <= 0xF01F) { - *target = src_char - 0xF000; - return true; - } - switch (src_char) { - case SFM_COLON: - *target = ':'; - break; - case SFM_DOUBLEQUOTE: - *target = '"'; - break; - case SFM_ASTERISK: - *target = '*'; - break; - case SFM_QUESTION: - *target = '?'; - break; - case SFM_PIPE: - *target = '|'; - break; - case SFM_GRTRTHAN: - *target = '>'; - break; - case SFM_LESSTHAN: - *target = '<'; - break; - case SFM_SPACE: - *target = ' '; - break; - case SFM_PERIOD: - *target = '.'; - break; - default: - return false; - } - return true; -} - - -/* - * cifs_mapchar - convert a host-endian char to proper char in codepage - * @target - where converted character should be copied - * @src_char - 2 byte host-endian source character - * @cp - codepage to which character should be converted - * @map_type - How should the 7 NTFS/SMB reserved characters be mapped to UCS2? - * - * This function handles the conversion of a single character. It is the - * responsibility of the caller to ensure that the target buffer is large - * enough to hold the result of the conversion (at least NLS_MAX_CHARSET_SIZE). - */ -static int -cifs_mapchar(char *target, const __u16 *from, const struct nls_table *cp, - int maptype) -{ - int len = 1; - __u16 src_char; - - src_char = *from; - - if ((maptype == SFM_MAP_UNI_RSVD) && convert_sfm_char(src_char, target)) - return len; - else if ((maptype == SFU_MAP_UNI_RSVD) && - convert_sfu_char(src_char, target)) - return len; - - /* if character not one of seven in special remap set */ - len = cp->uni2char(src_char, target, NLS_MAX_CHARSET_SIZE); - if (len <= 0) - goto surrogate_pair; - - return len; - -surrogate_pair: - /* convert SURROGATE_PAIR and IVS */ - if (strcmp(cp->charset, "utf8")) - goto unknown; - len = utf16s_to_utf8s(from, 3, UTF16_LITTLE_ENDIAN, target, 6); - if (len <= 0) - goto unknown; - return len; - -unknown: - *target = '?'; - len = 1; - return len; -} - -/* - * cifs_from_utf16 - convert utf16le string to local charset - * @to - destination buffer - * @from - source buffer - * @tolen - destination buffer size (in bytes) - * @fromlen - source buffer size (in bytes) - * @codepage - codepage to which characters should be converted - * @mapchar - should characters be remapped according to the mapchars option? - * - * Convert a little-endian utf16le string (as sent by the server) to a string - * in the provided codepage. The tolen and fromlen parameters are to ensure - * that the code doesn't walk off of the end of the buffer (which is always - * a danger if the alignment of the source buffer is off). The destination - * string is always properly null terminated and fits in the destination - * buffer. Returns the length of the destination string in bytes (including - * null terminator). - * - * Note that some windows versions actually send multiword UTF-16 characters - * instead of straight UTF16-2. The linux nls routines however aren't able to - * deal with those characters properly. In the event that we get some of - * those characters, they won't be translated properly. - */ -int -cifs_from_utf16(char *to, const __le16 *from, int tolen, int fromlen, - const struct nls_table *codepage, int map_type) -{ - int i, charlen, safelen; - int outlen = 0; - int nullsize = nls_nullsize(codepage); - int fromwords = fromlen / 2; - char tmp[NLS_MAX_CHARSET_SIZE]; - __u16 ftmp[3]; /* ftmp[3] = 3array x 2bytes = 6bytes UTF-16 */ - - /* - * because the chars can be of varying widths, we need to take care - * not to overflow the destination buffer when we get close to the - * end of it. Until we get to this offset, we don't need to check - * for overflow however. - */ - safelen = tolen - (NLS_MAX_CHARSET_SIZE + nullsize); - - for (i = 0; i < fromwords; i++) { - ftmp[0] = get_unaligned_le16(&from[i]); - if (ftmp[0] == 0) - break; - if (i + 1 < fromwords) - ftmp[1] = get_unaligned_le16(&from[i + 1]); - else - ftmp[1] = 0; - if (i + 2 < fromwords) - ftmp[2] = get_unaligned_le16(&from[i + 2]); - else - ftmp[2] = 0; - - /* - * check to see if converting this character might make the - * conversion bleed into the null terminator - */ - if (outlen >= safelen) { - charlen = cifs_mapchar(tmp, ftmp, codepage, map_type); - if ((outlen + charlen) > (tolen - nullsize)) - break; - } - - /* put converted char into 'to' buffer */ - charlen = cifs_mapchar(&to[outlen], ftmp, codepage, map_type); - outlen += charlen; - - /* charlen (=bytes of UTF-8 for 1 character) - * 4bytes UTF-8(surrogate pair) is charlen=4 - * (4bytes UTF-16 code) - * 7-8bytes UTF-8(IVS) is charlen=3+4 or 4+4 - * (2 UTF-8 pairs divided to 2 UTF-16 pairs) */ - if (charlen == 4) - i++; - else if (charlen >= 5) - /* 5-6bytes UTF-8 */ - i += 2; - } - - /* properly null-terminate string */ - for (i = 0; i < nullsize; i++) - to[outlen++] = 0; - - return outlen; -} - -/* - * NAME: cifs_strtoUTF16() - * - * FUNCTION: Convert character string to unicode string - * - */ -int -cifs_strtoUTF16(__le16 *to, const char *from, int len, - const struct nls_table *codepage) -{ - int charlen; - int i; - wchar_t wchar_to; /* needed to quiet sparse */ - - /* special case for utf8 to handle no plane0 chars */ - if (!strcmp(codepage->charset, "utf8")) { - /* - * convert utf8 -> utf16, we assume we have enough space - * as caller should have assumed conversion does not overflow - * in destination len is length in wchar_t units (16bits) - */ - i = utf8s_to_utf16s(from, len, UTF16_LITTLE_ENDIAN, - (wchar_t *) to, len); - - /* if success terminate and exit */ - if (i >= 0) - goto success; - /* - * if fails fall back to UCS encoding as this - * function should not return negative values - * currently can fail only if source contains - * invalid encoded characters - */ - } - - for (i = 0; len && *from; i++, from += charlen, len -= charlen) { - charlen = codepage->char2uni(from, len, &wchar_to); - if (charlen < 1) { - cifs_dbg(VFS, "strtoUTF16: char2uni of 0x%x returned %d\n", - *from, charlen); - /* A question mark */ - wchar_to = 0x003f; - charlen = 1; - } - put_unaligned_le16(wchar_to, &to[i]); - } - -success: - put_unaligned_le16(0, &to[i]); - return i; -} - -/* - * cifs_utf16_bytes - how long will a string be after conversion? - * @utf16 - pointer to input string - * @maxbytes - don't go past this many bytes of input string - * @codepage - destination codepage - * - * Walk a utf16le string and return the number of bytes that the string will - * be after being converted to the given charset, not including any null - * termination required. Don't walk past maxbytes in the source buffer. - */ -int -cifs_utf16_bytes(const __le16 *from, int maxbytes, - const struct nls_table *codepage) -{ - int i; - int charlen, outlen = 0; - int maxwords = maxbytes / 2; - char tmp[NLS_MAX_CHARSET_SIZE]; - __u16 ftmp[3]; - - for (i = 0; i < maxwords; i++) { - ftmp[0] = get_unaligned_le16(&from[i]); - if (ftmp[0] == 0) - break; - if (i + 1 < maxwords) - ftmp[1] = get_unaligned_le16(&from[i + 1]); - else - ftmp[1] = 0; - if (i + 2 < maxwords) - ftmp[2] = get_unaligned_le16(&from[i + 2]); - else - ftmp[2] = 0; - - charlen = cifs_mapchar(tmp, ftmp, codepage, NO_MAP_UNI_RSVD); - outlen += charlen; - } - - return outlen; -} - -/* - * cifs_strndup_from_utf16 - copy a string from wire format to the local - * codepage - * @src - source string - * @maxlen - don't walk past this many bytes in the source string - * @is_unicode - is this a unicode string? - * @codepage - destination codepage - * - * Take a string given by the server, convert it to the local codepage and - * put it in a new buffer. Returns a pointer to the new string or NULL on - * error. - */ -char * -cifs_strndup_from_utf16(const char *src, const int maxlen, - const bool is_unicode, const struct nls_table *codepage) -{ - int len; - char *dst; - - if (is_unicode) { - len = cifs_utf16_bytes((__le16 *) src, maxlen, codepage); - len += nls_nullsize(codepage); - dst = kmalloc(len, GFP_KERNEL); - if (!dst) - return NULL; - cifs_from_utf16(dst, (__le16 *) src, len, maxlen, codepage, - NO_MAP_UNI_RSVD); - } else { - dst = kstrndup(src, maxlen, GFP_KERNEL); - } - - return dst; -} - -static __le16 convert_to_sfu_char(char src_char) -{ - __le16 dest_char; - - switch (src_char) { - case ':': - dest_char = cpu_to_le16(UNI_COLON); - break; - case '*': - dest_char = cpu_to_le16(UNI_ASTERISK); - break; - case '?': - dest_char = cpu_to_le16(UNI_QUESTION); - break; - case '<': - dest_char = cpu_to_le16(UNI_LESSTHAN); - break; - case '>': - dest_char = cpu_to_le16(UNI_GRTRTHAN); - break; - case '|': - dest_char = cpu_to_le16(UNI_PIPE); - break; - default: - dest_char = 0; - } - - return dest_char; -} - -static __le16 convert_to_sfm_char(char src_char, bool end_of_string) -{ - __le16 dest_char; - - if (src_char >= 0x01 && src_char <= 0x1F) { - dest_char = cpu_to_le16(src_char + 0xF000); - return dest_char; - } - switch (src_char) { - case ':': - dest_char = cpu_to_le16(SFM_COLON); - break; - case '"': - dest_char = cpu_to_le16(SFM_DOUBLEQUOTE); - break; - case '*': - dest_char = cpu_to_le16(SFM_ASTERISK); - break; - case '?': - dest_char = cpu_to_le16(SFM_QUESTION); - break; - case '<': - dest_char = cpu_to_le16(SFM_LESSTHAN); - break; - case '>': - dest_char = cpu_to_le16(SFM_GRTRTHAN); - break; - case '|': - dest_char = cpu_to_le16(SFM_PIPE); - break; - case '.': - if (end_of_string) - dest_char = cpu_to_le16(SFM_PERIOD); - else - dest_char = 0; - break; - case ' ': - if (end_of_string) - dest_char = cpu_to_le16(SFM_SPACE); - else - dest_char = 0; - break; - default: - dest_char = 0; - } - - return dest_char; -} - -/* - * Convert 16 bit Unicode pathname to wire format from string in current code - * page. Conversion may involve remapping up the six characters that are - * only legal in POSIX-like OS (if they are present in the string). Path - * names are little endian 16 bit Unicode on the wire - */ -int -cifsConvertToUTF16(__le16 *target, const char *source, int srclen, - const struct nls_table *cp, int map_chars) -{ - int i, charlen; - int j = 0; - char src_char; - __le16 dst_char; - wchar_t tmp; - wchar_t *wchar_to; /* UTF-16 */ - int ret; - unicode_t u; - - if (map_chars == NO_MAP_UNI_RSVD) - return cifs_strtoUTF16(target, source, PATH_MAX, cp); - - wchar_to = kzalloc(6, GFP_KERNEL); - - for (i = 0; i < srclen; j++) { - src_char = source[i]; - charlen = 1; - - /* check if end of string */ - if (src_char == 0) - goto ctoUTF16_out; - - /* see if we must remap this char */ - if (map_chars == SFU_MAP_UNI_RSVD) - dst_char = convert_to_sfu_char(src_char); - else if (map_chars == SFM_MAP_UNI_RSVD) { - bool end_of_string; - - /** - * Remap spaces and periods found at the end of every - * component of the path. The special cases of '.' and - * '..' do not need to be dealt with explicitly because - * they are addressed in namei.c:link_path_walk(). - **/ - if ((i == srclen - 1) || (source[i+1] == '\\')) - end_of_string = true; - else - end_of_string = false; - - dst_char = convert_to_sfm_char(src_char, end_of_string); - } else - dst_char = 0; - /* - * FIXME: We can not handle remapping backslash (UNI_SLASH) - * until all the calls to build_path_from_dentry are modified, - * as they use backslash as separator. - */ - if (dst_char == 0) { - charlen = cp->char2uni(source + i, srclen - i, &tmp); - dst_char = cpu_to_le16(tmp); - - /* - * if no match, use question mark, which at least in - * some cases serves as wild card - */ - if (charlen > 0) - goto ctoUTF16; - - /* convert SURROGATE_PAIR */ - if (strcmp(cp->charset, "utf8") || !wchar_to) - goto unknown; - if (*(source + i) & 0x80) { - charlen = utf8_to_utf32(source + i, 6, &u); - if (charlen < 0) - goto unknown; - } else - goto unknown; - ret = utf8s_to_utf16s(source + i, charlen, - UTF16_LITTLE_ENDIAN, - wchar_to, 6); - if (ret < 0) - goto unknown; - - i += charlen; - dst_char = cpu_to_le16(*wchar_to); - if (charlen <= 3) - /* 1-3bytes UTF-8 to 2bytes UTF-16 */ - put_unaligned(dst_char, &target[j]); - else if (charlen == 4) { - /* 4bytes UTF-8(surrogate pair) to 4bytes UTF-16 - * 7-8bytes UTF-8(IVS) divided to 2 UTF-16 - * (charlen=3+4 or 4+4) */ - put_unaligned(dst_char, &target[j]); - dst_char = cpu_to_le16(*(wchar_to + 1)); - j++; - put_unaligned(dst_char, &target[j]); - } else if (charlen >= 5) { - /* 5-6bytes UTF-8 to 6bytes UTF-16 */ - put_unaligned(dst_char, &target[j]); - dst_char = cpu_to_le16(*(wchar_to + 1)); - j++; - put_unaligned(dst_char, &target[j]); - dst_char = cpu_to_le16(*(wchar_to + 2)); - j++; - put_unaligned(dst_char, &target[j]); - } - continue; - -unknown: - dst_char = cpu_to_le16(0x003f); - charlen = 1; - } - -ctoUTF16: - /* - * character may take more than one byte in the source string, - * but will take exactly two bytes in the target string - */ - i += charlen; - put_unaligned(dst_char, &target[j]); - } - -ctoUTF16_out: - put_unaligned(0, &target[j]); /* Null terminate target unicode string */ - kfree(wchar_to); - return j; -} - -/* - * cifs_local_to_utf16_bytes - how long will a string be after conversion? - * @from - pointer to input string - * @maxbytes - don't go past this many bytes of input string - * @codepage - source codepage - * - * Walk a string and return the number of bytes that the string will - * be after being converted to the given charset, not including any null - * termination required. Don't walk past maxbytes in the source buffer. - */ - -static int -cifs_local_to_utf16_bytes(const char *from, int len, - const struct nls_table *codepage) -{ - int charlen; - int i; - wchar_t wchar_to; - - for (i = 0; len && *from; i++, from += charlen, len -= charlen) { - charlen = codepage->char2uni(from, len, &wchar_to); - /* Failed conversion defaults to a question mark */ - if (charlen < 1) - charlen = 1; - } - return 2 * i; /* UTF16 characters are two bytes */ -} - -/* - * cifs_strndup_to_utf16 - copy a string to wire format from the local codepage - * @src - source string - * @maxlen - don't walk past this many bytes in the source string - * @utf16_len - the length of the allocated string in bytes (including null) - * @cp - source codepage - * @remap - map special chars - * - * Take a string convert it from the local codepage to UTF16 and - * put it in a new buffer. Returns a pointer to the new string or NULL on - * error. - */ -__le16 * -cifs_strndup_to_utf16(const char *src, const int maxlen, int *utf16_len, - const struct nls_table *cp, int remap) -{ - int len; - __le16 *dst; - - len = cifs_local_to_utf16_bytes(src, maxlen, cp); - len += 2; /* NULL */ - dst = kmalloc(len, GFP_KERNEL); - if (!dst) { - *utf16_len = 0; - return NULL; - } - cifsConvertToUTF16(dst, src, strlen(src), cp, remap); - *utf16_len = len; - return dst; -} diff --git a/fs/cifs/cifs_unicode.h b/fs/cifs/cifs_unicode.h deleted file mode 100644 index 80b3d845419fa..0000000000000 --- a/fs/cifs/cifs_unicode.h +++ /dev/null @@ -1,404 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0-or-later */ -/* - * cifs_unicode: Unicode kernel case support - * - * Function: - * Convert a unicode character to upper or lower case using - * compressed tables. - * - * Copyright (c) International Business Machines Corp., 2000,2009 - * - * Notes: - * These APIs are based on the C library functions. The semantics - * should match the C functions but with expanded size operands. - * - * The upper/lower functions are based on a table created by mkupr. - * This is a compressed table of upper and lower case conversion. - */ -#ifndef _CIFS_UNICODE_H -#define _CIFS_UNICODE_H - -#include -#include -#include - -#define UNIUPR_NOLOWER /* Example to not expand lower case tables */ - -/* - * Windows maps these to the user defined 16 bit Unicode range since they are - * reserved symbols (along with \ and /), otherwise illegal to store - * in filenames in NTFS - */ -#define UNI_ASTERISK (__u16) ('*' + 0xF000) -#define UNI_QUESTION (__u16) ('?' + 0xF000) -#define UNI_COLON (__u16) (':' + 0xF000) -#define UNI_GRTRTHAN (__u16) ('>' + 0xF000) -#define UNI_LESSTHAN (__u16) ('<' + 0xF000) -#define UNI_PIPE (__u16) ('|' + 0xF000) -#define UNI_SLASH (__u16) ('\\' + 0xF000) - -/* - * Macs use an older "SFM" mapping of the symbols above. Fortunately it does - * not conflict (although almost does) with the mapping above. - */ - -#define SFM_DOUBLEQUOTE ((__u16) 0xF020) -#define SFM_ASTERISK ((__u16) 0xF021) -#define SFM_QUESTION ((__u16) 0xF025) -#define SFM_COLON ((__u16) 0xF022) -#define SFM_GRTRTHAN ((__u16) 0xF024) -#define SFM_LESSTHAN ((__u16) 0xF023) -#define SFM_PIPE ((__u16) 0xF027) -#define SFM_SLASH ((__u16) 0xF026) -#define SFM_SPACE ((__u16) 0xF028) -#define SFM_PERIOD ((__u16) 0xF029) - -/* - * Mapping mechanism to use when one of the seven reserved characters is - * encountered. We can only map using one of the mechanisms at a time - * since otherwise readdir could return directory entries which we would - * not be able to open - * - * NO_MAP_UNI_RSVD = do not perform any remapping of the character - * SFM_MAP_UNI_RSVD = map reserved characters using SFM scheme (MAC compatible) - * SFU_MAP_UNI_RSVD = map reserved characters ala SFU ("mapchars" option) - * - */ -#define NO_MAP_UNI_RSVD 0 -#define SFM_MAP_UNI_RSVD 1 -#define SFU_MAP_UNI_RSVD 2 - -/* Just define what we want from uniupr.h. We don't want to define the tables - * in each source file. - */ -#ifndef UNICASERANGE_DEFINED -struct UniCaseRange { - wchar_t start; - wchar_t end; - signed char *table; -}; -#endif /* UNICASERANGE_DEFINED */ - -#ifndef UNIUPR_NOUPPER -extern signed char CifsUniUpperTable[512]; -extern const struct UniCaseRange CifsUniUpperRange[]; -#endif /* UNIUPR_NOUPPER */ - -#ifndef UNIUPR_NOLOWER -extern signed char CifsUniLowerTable[512]; -extern const struct UniCaseRange CifsUniLowerRange[]; -#endif /* UNIUPR_NOLOWER */ - -#ifdef __KERNEL__ -int cifs_from_utf16(char *to, const __le16 *from, int tolen, int fromlen, - const struct nls_table *cp, int map_type); -int cifs_utf16_bytes(const __le16 *from, int maxbytes, - const struct nls_table *codepage); -int cifs_strtoUTF16(__le16 *, const char *, int, const struct nls_table *); -char *cifs_strndup_from_utf16(const char *src, const int maxlen, - const bool is_unicode, - const struct nls_table *codepage); -extern int cifsConvertToUTF16(__le16 *target, const char *source, int maxlen, - const struct nls_table *cp, int mapChars); -extern int cifs_remap(struct cifs_sb_info *cifs_sb); -extern __le16 *cifs_strndup_to_utf16(const char *src, const int maxlen, - int *utf16_len, const struct nls_table *cp, - int remap); -#endif - -wchar_t cifs_toupper(wchar_t in); - -/* - * UniStrcat: Concatenate the second string to the first - * - * Returns: - * Address of the first string - */ -static inline __le16 * -UniStrcat(__le16 *ucs1, const __le16 *ucs2) -{ - __le16 *anchor = ucs1; /* save a pointer to start of ucs1 */ - - while (*ucs1++) ; /* To end of first string */ - ucs1--; /* Return to the null */ - while ((*ucs1++ = *ucs2++)) ; /* copy string 2 over */ - return anchor; -} - -/* - * UniStrchr: Find a character in a string - * - * Returns: - * Address of first occurrence of character in string - * or NULL if the character is not in the string - */ -static inline wchar_t * -UniStrchr(const wchar_t *ucs, wchar_t uc) -{ - while ((*ucs != uc) && *ucs) - ucs++; - - if (*ucs == uc) - return (wchar_t *) ucs; - return NULL; -} - -/* - * UniStrcmp: Compare two strings - * - * Returns: - * < 0: First string is less than second - * = 0: Strings are equal - * > 0: First string is greater than second - */ -static inline int -UniStrcmp(const wchar_t *ucs1, const wchar_t *ucs2) -{ - while ((*ucs1 == *ucs2) && *ucs1) { - ucs1++; - ucs2++; - } - return (int) *ucs1 - (int) *ucs2; -} - -/* - * UniStrcpy: Copy a string - */ -static inline wchar_t * -UniStrcpy(wchar_t *ucs1, const wchar_t *ucs2) -{ - wchar_t *anchor = ucs1; /* save the start of result string */ - - while ((*ucs1++ = *ucs2++)) ; - return anchor; -} - -/* - * UniStrlen: Return the length of a string (in 16 bit Unicode chars not bytes) - */ -static inline size_t -UniStrlen(const wchar_t *ucs1) -{ - int i = 0; - - while (*ucs1++) - i++; - return i; -} - -/* - * UniStrnlen: Return the length (in 16 bit Unicode chars not bytes) of a - * string (length limited) - */ -static inline size_t -UniStrnlen(const wchar_t *ucs1, int maxlen) -{ - int i = 0; - - while (*ucs1++) { - i++; - if (i >= maxlen) - break; - } - return i; -} - -/* - * UniStrncat: Concatenate length limited string - */ -static inline wchar_t * -UniStrncat(wchar_t *ucs1, const wchar_t *ucs2, size_t n) -{ - wchar_t *anchor = ucs1; /* save pointer to string 1 */ - - while (*ucs1++) ; - ucs1--; /* point to null terminator of s1 */ - while (n-- && (*ucs1 = *ucs2)) { /* copy s2 after s1 */ - ucs1++; - ucs2++; - } - *ucs1 = 0; /* Null terminate the result */ - return (anchor); -} - -/* - * UniStrncmp: Compare length limited string - */ -static inline int -UniStrncmp(const wchar_t *ucs1, const wchar_t *ucs2, size_t n) -{ - if (!n) - return 0; /* Null strings are equal */ - while ((*ucs1 == *ucs2) && *ucs1 && --n) { - ucs1++; - ucs2++; - } - return (int) *ucs1 - (int) *ucs2; -} - -/* - * UniStrncmp_le: Compare length limited string - native to little-endian - */ -static inline int -UniStrncmp_le(const wchar_t *ucs1, const wchar_t *ucs2, size_t n) -{ - if (!n) - return 0; /* Null strings are equal */ - while ((*ucs1 == __le16_to_cpu(*ucs2)) && *ucs1 && --n) { - ucs1++; - ucs2++; - } - return (int) *ucs1 - (int) __le16_to_cpu(*ucs2); -} - -/* - * UniStrncpy: Copy length limited string with pad - */ -static inline wchar_t * -UniStrncpy(wchar_t *ucs1, const wchar_t *ucs2, size_t n) -{ - wchar_t *anchor = ucs1; - - while (n-- && *ucs2) /* Copy the strings */ - *ucs1++ = *ucs2++; - - n++; - while (n--) /* Pad with nulls */ - *ucs1++ = 0; - return anchor; -} - -/* - * UniStrncpy_le: Copy length limited string with pad to little-endian - */ -static inline wchar_t * -UniStrncpy_le(wchar_t *ucs1, const wchar_t *ucs2, size_t n) -{ - wchar_t *anchor = ucs1; - - while (n-- && *ucs2) /* Copy the strings */ - *ucs1++ = __le16_to_cpu(*ucs2++); - - n++; - while (n--) /* Pad with nulls */ - *ucs1++ = 0; - return anchor; -} - -/* - * UniStrstr: Find a string in a string - * - * Returns: - * Address of first match found - * NULL if no matching string is found - */ -static inline wchar_t * -UniStrstr(const wchar_t *ucs1, const wchar_t *ucs2) -{ - const wchar_t *anchor1 = ucs1; - const wchar_t *anchor2 = ucs2; - - while (*ucs1) { - if (*ucs1 == *ucs2) { - /* Partial match found */ - ucs1++; - ucs2++; - } else { - if (!*ucs2) /* Match found */ - return (wchar_t *) anchor1; - ucs1 = ++anchor1; /* No match */ - ucs2 = anchor2; - } - } - - if (!*ucs2) /* Both end together */ - return (wchar_t *) anchor1; /* Match found */ - return NULL; /* No match */ -} - -#ifndef UNIUPR_NOUPPER -/* - * UniToupper: Convert a unicode character to upper case - */ -static inline wchar_t -UniToupper(register wchar_t uc) -{ - register const struct UniCaseRange *rp; - - if (uc < sizeof(CifsUniUpperTable)) { - /* Latin characters */ - return uc + CifsUniUpperTable[uc]; /* Use base tables */ - } else { - rp = CifsUniUpperRange; /* Use range tables */ - while (rp->start) { - if (uc < rp->start) /* Before start of range */ - return uc; /* Uppercase = input */ - if (uc <= rp->end) /* In range */ - return uc + rp->table[uc - rp->start]; - rp++; /* Try next range */ - } - } - return uc; /* Past last range */ -} - -/* - * UniStrupr: Upper case a unicode string - */ -static inline __le16 * -UniStrupr(register __le16 *upin) -{ - register __le16 *up; - - up = upin; - while (*up) { /* For all characters */ - *up = cpu_to_le16(UniToupper(le16_to_cpu(*up))); - up++; - } - return upin; /* Return input pointer */ -} -#endif /* UNIUPR_NOUPPER */ - -#ifndef UNIUPR_NOLOWER -/* - * UniTolower: Convert a unicode character to lower case - */ -static inline wchar_t -UniTolower(register wchar_t uc) -{ - register const struct UniCaseRange *rp; - - if (uc < sizeof(CifsUniLowerTable)) { - /* Latin characters */ - return uc + CifsUniLowerTable[uc]; /* Use base tables */ - } else { - rp = CifsUniLowerRange; /* Use range tables */ - while (rp->start) { - if (uc < rp->start) /* Before start of range */ - return uc; /* Uppercase = input */ - if (uc <= rp->end) /* In range */ - return uc + rp->table[uc - rp->start]; - rp++; /* Try next range */ - } - } - return uc; /* Past last range */ -} - -/* - * UniStrlwr: Lower case a unicode string - */ -static inline wchar_t * -UniStrlwr(register wchar_t *upin) -{ - register wchar_t *up; - - up = upin; - while (*up) { /* For all characters */ - *up = UniTolower(*up); - up++; - } - return upin; /* Return input pointer */ -} - -#endif - -#endif /* _CIFS_UNICODE_H */ diff --git a/fs/cifs/cifs_uniupr.h b/fs/cifs/cifs_uniupr.h deleted file mode 100644 index 7b272fcdf0d3a..0000000000000 --- a/fs/cifs/cifs_uniupr.h +++ /dev/null @@ -1,239 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0-or-later */ -/* - * Copyright (c) International Business Machines Corp., 2000,2002 - * - * uniupr.h - Unicode compressed case ranges -*/ - -#ifndef UNIUPR_NOUPPER -/* - * Latin upper case - */ -signed char CifsUniUpperTable[512] = { - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 000-00f */ - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 010-01f */ - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 020-02f */ - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 030-03f */ - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 040-04f */ - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 050-05f */ - 0, -32, -32, -32, -32, -32, -32, -32, -32, -32, -32, -32, -32, -32, -32, -32, /* 060-06f */ - -32, -32, -32, -32, -32, -32, -32, -32, -32, -32, -32, 0, 0, 0, 0, 0, /* 070-07f */ - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 080-08f */ - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 090-09f */ - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 0a0-0af */ - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 0b0-0bf */ - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 0c0-0cf */ - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 0d0-0df */ - -32, -32, -32, -32, -32, -32, -32, -32, -32, -32, -32, -32, -32, -32, -32, -32, /* 0e0-0ef */ - -32, -32, -32, -32, -32, -32, -32, 0, -32, -32, -32, -32, -32, -32, -32, 121, /* 0f0-0ff */ - 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, /* 100-10f */ - 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, /* 110-11f */ - 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, /* 120-12f */ - 0, 0, 0, -1, 0, -1, 0, -1, 0, 0, -1, 0, -1, 0, -1, 0, /* 130-13f */ - -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, 0, -1, 0, -1, 0, -1, /* 140-14f */ - 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, /* 150-15f */ - 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, /* 160-16f */ - 0, -1, 0, -1, 0, -1, 0, -1, 0, 0, -1, 0, -1, 0, -1, 0, /* 170-17f */ - 0, 0, 0, -1, 0, -1, 0, 0, -1, 0, 0, 0, -1, 0, 0, 0, /* 180-18f */ - 0, 0, -1, 0, 0, 0, 0, 0, 0, -1, 0, 0, 0, 0, 0, 0, /* 190-19f */ - 0, -1, 0, -1, 0, -1, 0, 0, -1, 0, 0, 0, 0, -1, 0, 0, /* 1a0-1af */ - -1, 0, 0, 0, -1, 0, -1, 0, 0, -1, 0, 0, 0, -1, 0, 0, /* 1b0-1bf */ - 0, 0, 0, 0, 0, -1, -2, 0, -1, -2, 0, -1, -2, 0, -1, 0, /* 1c0-1cf */ - -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, -79, 0, -1, /* 1d0-1df */ - 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, /* 1e0-1ef */ - 0, 0, -1, -2, 0, -1, 0, 0, 0, -1, 0, -1, 0, -1, 0, -1, /* 1f0-1ff */ -}; - -/* Upper case range - Greek */ -static signed char UniCaseRangeU03a0[47] = { - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -38, -37, -37, -37, /* 3a0-3af */ - 0, -32, -32, -32, -32, -32, -32, -32, -32, -32, -32, -32, -32, -32, -32, -32, /* 3b0-3bf */ - -32, -32, -31, -32, -32, -32, -32, -32, -32, -32, -32, -32, -64, - -63, -63, -}; - -/* Upper case range - Cyrillic */ -static signed char UniCaseRangeU0430[48] = { - -32, -32, -32, -32, -32, -32, -32, -32, -32, -32, -32, -32, -32, -32, -32, -32, /* 430-43f */ - -32, -32, -32, -32, -32, -32, -32, -32, -32, -32, -32, -32, -32, -32, -32, -32, /* 440-44f */ - 0, -80, -80, -80, -80, -80, -80, -80, -80, -80, -80, -80, -80, 0, -80, -80, /* 450-45f */ -}; - -/* Upper case range - Extended cyrillic */ -static signed char UniCaseRangeU0490[61] = { - 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, /* 490-49f */ - 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, /* 4a0-4af */ - 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, /* 4b0-4bf */ - 0, 0, -1, 0, -1, 0, 0, 0, -1, 0, 0, 0, -1, -}; - -/* Upper case range - Extended latin and greek */ -static signed char UniCaseRangeU1e00[509] = { - 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, /* 1e00-1e0f */ - 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, /* 1e10-1e1f */ - 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, /* 1e20-1e2f */ - 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, /* 1e30-1e3f */ - 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, /* 1e40-1e4f */ - 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, /* 1e50-1e5f */ - 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, /* 1e60-1e6f */ - 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, /* 1e70-1e7f */ - 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, /* 1e80-1e8f */ - 0, -1, 0, -1, 0, -1, 0, 0, 0, 0, 0, -59, 0, -1, 0, -1, /* 1e90-1e9f */ - 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, /* 1ea0-1eaf */ - 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, /* 1eb0-1ebf */ - 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, /* 1ec0-1ecf */ - 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, /* 1ed0-1edf */ - 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, /* 1ee0-1eef */ - 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, 0, 0, 0, 0, 0, /* 1ef0-1eff */ - 8, 8, 8, 8, 8, 8, 8, 8, 0, 0, 0, 0, 0, 0, 0, 0, /* 1f00-1f0f */ - 8, 8, 8, 8, 8, 8, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 1f10-1f1f */ - 8, 8, 8, 8, 8, 8, 8, 8, 0, 0, 0, 0, 0, 0, 0, 0, /* 1f20-1f2f */ - 8, 8, 8, 8, 8, 8, 8, 8, 0, 0, 0, 0, 0, 0, 0, 0, /* 1f30-1f3f */ - 8, 8, 8, 8, 8, 8, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 1f40-1f4f */ - 0, 8, 0, 8, 0, 8, 0, 8, 0, 0, 0, 0, 0, 0, 0, 0, /* 1f50-1f5f */ - 8, 8, 8, 8, 8, 8, 8, 8, 0, 0, 0, 0, 0, 0, 0, 0, /* 1f60-1f6f */ - 74, 74, 86, 86, 86, 86, 100, 100, 0, 0, 112, 112, 126, 126, 0, 0, /* 1f70-1f7f */ - 8, 8, 8, 8, 8, 8, 8, 8, 0, 0, 0, 0, 0, 0, 0, 0, /* 1f80-1f8f */ - 8, 8, 8, 8, 8, 8, 8, 8, 0, 0, 0, 0, 0, 0, 0, 0, /* 1f90-1f9f */ - 8, 8, 8, 8, 8, 8, 8, 8, 0, 0, 0, 0, 0, 0, 0, 0, /* 1fa0-1faf */ - 8, 8, 0, 9, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 1fb0-1fbf */ - 0, 0, 0, 9, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 1fc0-1fcf */ - 8, 8, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 1fd0-1fdf */ - 8, 8, 0, 0, 0, 7, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 1fe0-1fef */ - 0, 0, 0, 9, 0, 0, 0, 0, 0, 0, 0, 0, 0, -}; - -/* Upper case range - Wide latin */ -static signed char UniCaseRangeUff40[27] = { - 0, -32, -32, -32, -32, -32, -32, -32, -32, -32, -32, -32, -32, -32, -32, -32, /* ff40-ff4f */ - -32, -32, -32, -32, -32, -32, -32, -32, -32, -32, -32, -}; - -/* - * Upper Case Range - */ -const struct UniCaseRange CifsUniUpperRange[] = { - {0x03a0, 0x03ce, UniCaseRangeU03a0}, - {0x0430, 0x045f, UniCaseRangeU0430}, - {0x0490, 0x04cc, UniCaseRangeU0490}, - {0x1e00, 0x1ffc, UniCaseRangeU1e00}, - {0xff40, 0xff5a, UniCaseRangeUff40}, - {0} -}; -#endif - -#ifndef UNIUPR_NOLOWER -/* - * Latin lower case - */ -signed char CifsUniLowerTable[512] = { - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 000-00f */ - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 010-01f */ - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 020-02f */ - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 030-03f */ - 0, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, /* 040-04f */ - 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 0, 0, 0, 0, 0, /* 050-05f */ - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 060-06f */ - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 070-07f */ - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 080-08f */ - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 090-09f */ - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 0a0-0af */ - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 0b0-0bf */ - 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, /* 0c0-0cf */ - 32, 32, 32, 32, 32, 32, 32, 0, 32, 32, 32, 32, 32, 32, 32, 0, /* 0d0-0df */ - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 0e0-0ef */ - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 0f0-0ff */ - 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, /* 100-10f */ - 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, /* 110-11f */ - 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, /* 120-12f */ - 0, 0, 1, 0, 1, 0, 1, 0, 0, 1, 0, 1, 0, 1, 0, 1, /* 130-13f */ - 0, 1, 0, 1, 0, 1, 0, 1, 0, 0, 1, 0, 1, 0, 1, 0, /* 140-14f */ - 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, /* 150-15f */ - 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, /* 160-16f */ - 1, 0, 1, 0, 1, 0, 1, 0, -121, 1, 0, 1, 0, 1, 0, 0, /* 170-17f */ - 0, 0, 1, 0, 1, 0, 0, 1, 0, 0, 0, 1, 0, 0, 79, 0, /* 180-18f */ - 0, 1, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, /* 190-19f */ - 1, 0, 1, 0, 1, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 1, /* 1a0-1af */ - 0, 0, 0, 1, 0, 1, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, /* 1b0-1bf */ - 0, 0, 0, 0, 2, 1, 0, 2, 1, 0, 2, 1, 0, 1, 0, 1, /* 1c0-1cf */ - 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 0, 1, 0, /* 1d0-1df */ - 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, /* 1e0-1ef */ - 0, 2, 1, 0, 1, 0, 0, 0, 1, 0, 1, 0, 1, 0, 1, 0, /* 1f0-1ff */ -}; - -/* Lower case range - Greek */ -static signed char UniCaseRangeL0380[44] = { - 0, 0, 0, 0, 0, 0, 38, 0, 37, 37, 37, 0, 64, 0, 63, 63, /* 380-38f */ - 0, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, /* 390-39f */ - 32, 32, 0, 32, 32, 32, 32, 32, 32, 32, 32, 32, -}; - -/* Lower case range - Cyrillic */ -static signed char UniCaseRangeL0400[48] = { - 0, 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, 0, 80, 80, /* 400-40f */ - 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, /* 410-41f */ - 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, /* 420-42f */ -}; - -/* Lower case range - Extended cyrillic */ -static signed char UniCaseRangeL0490[60] = { - 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, /* 490-49f */ - 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, /* 4a0-4af */ - 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, /* 4b0-4bf */ - 0, 1, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, -}; - -/* Lower case range - Extended latin and greek */ -static signed char UniCaseRangeL1e00[504] = { - 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, /* 1e00-1e0f */ - 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, /* 1e10-1e1f */ - 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, /* 1e20-1e2f */ - 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, /* 1e30-1e3f */ - 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, /* 1e40-1e4f */ - 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, /* 1e50-1e5f */ - 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, /* 1e60-1e6f */ - 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, /* 1e70-1e7f */ - 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, /* 1e80-1e8f */ - 1, 0, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 1, 0, 1, 0, /* 1e90-1e9f */ - 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, /* 1ea0-1eaf */ - 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, /* 1eb0-1ebf */ - 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, /* 1ec0-1ecf */ - 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, /* 1ed0-1edf */ - 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, /* 1ee0-1eef */ - 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, /* 1ef0-1eff */ - 0, 0, 0, 0, 0, 0, 0, 0, -8, -8, -8, -8, -8, -8, -8, -8, /* 1f00-1f0f */ - 0, 0, 0, 0, 0, 0, 0, 0, -8, -8, -8, -8, -8, -8, 0, 0, /* 1f10-1f1f */ - 0, 0, 0, 0, 0, 0, 0, 0, -8, -8, -8, -8, -8, -8, -8, -8, /* 1f20-1f2f */ - 0, 0, 0, 0, 0, 0, 0, 0, -8, -8, -8, -8, -8, -8, -8, -8, /* 1f30-1f3f */ - 0, 0, 0, 0, 0, 0, 0, 0, -8, -8, -8, -8, -8, -8, 0, 0, /* 1f40-1f4f */ - 0, 0, 0, 0, 0, 0, 0, 0, 0, -8, 0, -8, 0, -8, 0, -8, /* 1f50-1f5f */ - 0, 0, 0, 0, 0, 0, 0, 0, -8, -8, -8, -8, -8, -8, -8, -8, /* 1f60-1f6f */ - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 1f70-1f7f */ - 0, 0, 0, 0, 0, 0, 0, 0, -8, -8, -8, -8, -8, -8, -8, -8, /* 1f80-1f8f */ - 0, 0, 0, 0, 0, 0, 0, 0, -8, -8, -8, -8, -8, -8, -8, -8, /* 1f90-1f9f */ - 0, 0, 0, 0, 0, 0, 0, 0, -8, -8, -8, -8, -8, -8, -8, -8, /* 1fa0-1faf */ - 0, 0, 0, 0, 0, 0, 0, 0, -8, -8, -74, -74, -9, 0, 0, 0, /* 1fb0-1fbf */ - 0, 0, 0, 0, 0, 0, 0, 0, -86, -86, -86, -86, -9, 0, 0, 0, /* 1fc0-1fcf */ - 0, 0, 0, 0, 0, 0, 0, 0, -8, -8, -100, -100, 0, 0, 0, 0, /* 1fd0-1fdf */ - 0, 0, 0, 0, 0, 0, 0, 0, -8, -8, -112, -112, -7, 0, 0, 0, /* 1fe0-1fef */ - 0, 0, 0, 0, 0, 0, 0, 0, -}; - -/* Lower case range - Wide latin */ -static signed char UniCaseRangeLff20[27] = { - 0, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, /* ff20-ff2f */ - 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, -}; - -/* - * Lower Case Range - */ -const struct UniCaseRange CifsUniLowerRange[] = { - {0x0380, 0x03ab, UniCaseRangeL0380}, - {0x0400, 0x042f, UniCaseRangeL0400}, - {0x0490, 0x04cb, UniCaseRangeL0490}, - {0x1e00, 0x1ff7, UniCaseRangeL1e00}, - {0xff20, 0xff3a, UniCaseRangeLff20}, - {0} -}; -#endif diff --git a/fs/cifs/cifsacl.c b/fs/cifs/cifsacl.c deleted file mode 100644 index a6c7566a01821..0000000000000 --- a/fs/cifs/cifsacl.c +++ /dev/null @@ -1,1672 +0,0 @@ -// SPDX-License-Identifier: LGPL-2.1 -/* - * - * Copyright (C) International Business Machines Corp., 2007,2008 - * Author(s): Steve French (sfrench@us.ibm.com) - * - * Contains the routines for mapping CIFS/NTFS ACLs - * - */ - -#include -#include -#include -#include -#include -#include -#include "cifspdu.h" -#include "cifsglob.h" -#include "cifsacl.h" -#include "cifsproto.h" -#include "cifs_debug.h" -#include "fs_context.h" - -/* security id for everyone/world system group */ -static const struct cifs_sid sid_everyone = { - 1, 1, {0, 0, 0, 0, 0, 1}, {0} }; -/* security id for Authenticated Users system group */ -static const struct cifs_sid sid_authusers = { - 1, 1, {0, 0, 0, 0, 0, 5}, {cpu_to_le32(11)} }; - -/* S-1-22-1 Unmapped Unix users */ -static const struct cifs_sid sid_unix_users = {1, 1, {0, 0, 0, 0, 0, 22}, - {cpu_to_le32(1), 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0} }; - -/* S-1-22-2 Unmapped Unix groups */ -static const struct cifs_sid sid_unix_groups = { 1, 1, {0, 0, 0, 0, 0, 22}, - {cpu_to_le32(2), 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0} }; - -/* - * See https://technet.microsoft.com/en-us/library/hh509017(v=ws.10).aspx - */ - -/* S-1-5-88 MS NFS and Apple style UID/GID/mode */ - -/* S-1-5-88-1 Unix uid */ -static const struct cifs_sid sid_unix_NFS_users = { 1, 2, {0, 0, 0, 0, 0, 5}, - {cpu_to_le32(88), - cpu_to_le32(1), 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0} }; - -/* S-1-5-88-2 Unix gid */ -static const struct cifs_sid sid_unix_NFS_groups = { 1, 2, {0, 0, 0, 0, 0, 5}, - {cpu_to_le32(88), - cpu_to_le32(2), 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0} }; - -/* S-1-5-88-3 Unix mode */ -static const struct cifs_sid sid_unix_NFS_mode = { 1, 2, {0, 0, 0, 0, 0, 5}, - {cpu_to_le32(88), - cpu_to_le32(3), 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0} }; - -static const struct cred *root_cred; - -static int -cifs_idmap_key_instantiate(struct key *key, struct key_preparsed_payload *prep) -{ - char *payload; - - /* - * If the payload is less than or equal to the size of a pointer, then - * an allocation here is wasteful. Just copy the data directly to the - * payload.value union member instead. - * - * With this however, you must check the datalen before trying to - * dereference payload.data! - */ - if (prep->datalen <= sizeof(key->payload)) { - key->payload.data[0] = NULL; - memcpy(&key->payload, prep->data, prep->datalen); - } else { - payload = kmemdup(prep->data, prep->datalen, GFP_KERNEL); - if (!payload) - return -ENOMEM; - key->payload.data[0] = payload; - } - - key->datalen = prep->datalen; - return 0; -} - -static inline void -cifs_idmap_key_destroy(struct key *key) -{ - if (key->datalen > sizeof(key->payload)) - kfree(key->payload.data[0]); -} - -static struct key_type cifs_idmap_key_type = { - .name = "cifs.idmap", - .instantiate = cifs_idmap_key_instantiate, - .destroy = cifs_idmap_key_destroy, - .describe = user_describe, -}; - -static char * -sid_to_key_str(struct cifs_sid *sidptr, unsigned int type) -{ - int i, len; - unsigned int saval; - char *sidstr, *strptr; - unsigned long long id_auth_val; - - /* 3 bytes for prefix */ - sidstr = kmalloc(3 + SID_STRING_BASE_SIZE + - (SID_STRING_SUBAUTH_SIZE * sidptr->num_subauth), - GFP_KERNEL); - if (!sidstr) - return sidstr; - - strptr = sidstr; - len = sprintf(strptr, "%cs:S-%hhu", type == SIDOWNER ? 'o' : 'g', - sidptr->revision); - strptr += len; - - /* The authority field is a single 48-bit number */ - id_auth_val = (unsigned long long)sidptr->authority[5]; - id_auth_val |= (unsigned long long)sidptr->authority[4] << 8; - id_auth_val |= (unsigned long long)sidptr->authority[3] << 16; - id_auth_val |= (unsigned long long)sidptr->authority[2] << 24; - id_auth_val |= (unsigned long long)sidptr->authority[1] << 32; - id_auth_val |= (unsigned long long)sidptr->authority[0] << 48; - - /* - * MS-DTYP states that if the authority is >= 2^32, then it should be - * expressed as a hex value. - */ - if (id_auth_val <= UINT_MAX) - len = sprintf(strptr, "-%llu", id_auth_val); - else - len = sprintf(strptr, "-0x%llx", id_auth_val); - - strptr += len; - - for (i = 0; i < sidptr->num_subauth; ++i) { - saval = le32_to_cpu(sidptr->sub_auth[i]); - len = sprintf(strptr, "-%u", saval); - strptr += len; - } - - return sidstr; -} - -/* - * if the two SIDs (roughly equivalent to a UUID for a user or group) are - * the same returns zero, if they do not match returns non-zero. - */ -static int -compare_sids(const struct cifs_sid *ctsid, const struct cifs_sid *cwsid) -{ - int i; - int num_subauth, num_sat, num_saw; - - if ((!ctsid) || (!cwsid)) - return 1; - - /* compare the revision */ - if (ctsid->revision != cwsid->revision) { - if (ctsid->revision > cwsid->revision) - return 1; - else - return -1; - } - - /* compare all of the six auth values */ - for (i = 0; i < NUM_AUTHS; ++i) { - if (ctsid->authority[i] != cwsid->authority[i]) { - if (ctsid->authority[i] > cwsid->authority[i]) - return 1; - else - return -1; - } - } - - /* compare all of the subauth values if any */ - num_sat = ctsid->num_subauth; - num_saw = cwsid->num_subauth; - num_subauth = num_sat < num_saw ? num_sat : num_saw; - if (num_subauth) { - for (i = 0; i < num_subauth; ++i) { - if (ctsid->sub_auth[i] != cwsid->sub_auth[i]) { - if (le32_to_cpu(ctsid->sub_auth[i]) > - le32_to_cpu(cwsid->sub_auth[i])) - return 1; - else - return -1; - } - } - } - - return 0; /* sids compare/match */ -} - -static bool -is_well_known_sid(const struct cifs_sid *psid, uint32_t *puid, bool is_group) -{ - int i; - int num_subauth; - const struct cifs_sid *pwell_known_sid; - - if (!psid || (puid == NULL)) - return false; - - num_subauth = psid->num_subauth; - - /* check if Mac (or Windows NFS) vs. Samba format for Unix owner SID */ - if (num_subauth == 2) { - if (is_group) - pwell_known_sid = &sid_unix_groups; - else - pwell_known_sid = &sid_unix_users; - } else if (num_subauth == 3) { - if (is_group) - pwell_known_sid = &sid_unix_NFS_groups; - else - pwell_known_sid = &sid_unix_NFS_users; - } else - return false; - - /* compare the revision */ - if (psid->revision != pwell_known_sid->revision) - return false; - - /* compare all of the six auth values */ - for (i = 0; i < NUM_AUTHS; ++i) { - if (psid->authority[i] != pwell_known_sid->authority[i]) { - cifs_dbg(FYI, "auth %d did not match\n", i); - return false; - } - } - - if (num_subauth == 2) { - if (psid->sub_auth[0] != pwell_known_sid->sub_auth[0]) - return false; - - *puid = le32_to_cpu(psid->sub_auth[1]); - } else /* 3 subauths, ie Windows/Mac style */ { - *puid = le32_to_cpu(psid->sub_auth[0]); - if ((psid->sub_auth[0] != pwell_known_sid->sub_auth[0]) || - (psid->sub_auth[1] != pwell_known_sid->sub_auth[1])) - return false; - - *puid = le32_to_cpu(psid->sub_auth[2]); - } - - cifs_dbg(FYI, "Unix UID %d returned from SID\n", *puid); - return true; /* well known sid found, uid returned */ -} - -static __u16 -cifs_copy_sid(struct cifs_sid *dst, const struct cifs_sid *src) -{ - int i; - __u16 size = 1 + 1 + 6; - - dst->revision = src->revision; - dst->num_subauth = min_t(u8, src->num_subauth, SID_MAX_SUB_AUTHORITIES); - for (i = 0; i < NUM_AUTHS; ++i) - dst->authority[i] = src->authority[i]; - for (i = 0; i < dst->num_subauth; ++i) - dst->sub_auth[i] = src->sub_auth[i]; - size += (dst->num_subauth * 4); - - return size; -} - -static int -id_to_sid(unsigned int cid, uint sidtype, struct cifs_sid *ssid) -{ - int rc; - struct key *sidkey; - struct cifs_sid *ksid; - unsigned int ksid_size; - char desc[3 + 10 + 1]; /* 3 byte prefix + 10 bytes for value + NULL */ - const struct cred *saved_cred; - - rc = snprintf(desc, sizeof(desc), "%ci:%u", - sidtype == SIDOWNER ? 'o' : 'g', cid); - if (rc >= sizeof(desc)) - return -EINVAL; - - rc = 0; - saved_cred = override_creds(root_cred); - sidkey = request_key(&cifs_idmap_key_type, desc, ""); - if (IS_ERR(sidkey)) { - rc = -EINVAL; - cifs_dbg(FYI, "%s: Can't map %cid %u to a SID\n", - __func__, sidtype == SIDOWNER ? 'u' : 'g', cid); - goto out_revert_creds; - } else if (sidkey->datalen < CIFS_SID_BASE_SIZE) { - rc = -EIO; - cifs_dbg(FYI, "%s: Downcall contained malformed key (datalen=%hu)\n", - __func__, sidkey->datalen); - goto invalidate_key; - } - - /* - * A sid is usually too large to be embedded in payload.value, but if - * there are no subauthorities and the host has 8-byte pointers, then - * it could be. - */ - ksid = sidkey->datalen <= sizeof(sidkey->payload) ? - (struct cifs_sid *)&sidkey->payload : - (struct cifs_sid *)sidkey->payload.data[0]; - - ksid_size = CIFS_SID_BASE_SIZE + (ksid->num_subauth * sizeof(__le32)); - if (ksid_size > sidkey->datalen) { - rc = -EIO; - cifs_dbg(FYI, "%s: Downcall contained malformed key (datalen=%hu, ksid_size=%u)\n", - __func__, sidkey->datalen, ksid_size); - goto invalidate_key; - } - - cifs_copy_sid(ssid, ksid); -out_key_put: - key_put(sidkey); -out_revert_creds: - revert_creds(saved_cred); - return rc; - -invalidate_key: - key_invalidate(sidkey); - goto out_key_put; -} - -int -sid_to_id(struct cifs_sb_info *cifs_sb, struct cifs_sid *psid, - struct cifs_fattr *fattr, uint sidtype) -{ - int rc = 0; - struct key *sidkey; - char *sidstr; - const struct cred *saved_cred; - kuid_t fuid = cifs_sb->ctx->linux_uid; - kgid_t fgid = cifs_sb->ctx->linux_gid; - - /* - * If we have too many subauthorities, then something is really wrong. - * Just return an error. - */ - if (unlikely(psid->num_subauth > SID_MAX_SUB_AUTHORITIES)) { - cifs_dbg(FYI, "%s: %u subauthorities is too many!\n", - __func__, psid->num_subauth); - return -EIO; - } - - if ((cifs_sb->mnt_cifs_flags & CIFS_MOUNT_UID_FROM_ACL) || - (cifs_sb_master_tcon(cifs_sb)->posix_extensions)) { - uint32_t unix_id; - bool is_group; - - if (sidtype != SIDOWNER) - is_group = true; - else - is_group = false; - - if (is_well_known_sid(psid, &unix_id, is_group) == false) - goto try_upcall_to_get_id; - - if (is_group) { - kgid_t gid; - gid_t id; - - id = (gid_t)unix_id; - gid = make_kgid(&init_user_ns, id); - if (gid_valid(gid)) { - fgid = gid; - goto got_valid_id; - } - } else { - kuid_t uid; - uid_t id; - - id = (uid_t)unix_id; - uid = make_kuid(&init_user_ns, id); - if (uid_valid(uid)) { - fuid = uid; - goto got_valid_id; - } - } - /* If unable to find uid/gid easily from SID try via upcall */ - } - -try_upcall_to_get_id: - sidstr = sid_to_key_str(psid, sidtype); - if (!sidstr) - return -ENOMEM; - - saved_cred = override_creds(root_cred); - sidkey = request_key(&cifs_idmap_key_type, sidstr, ""); - if (IS_ERR(sidkey)) { - cifs_dbg(FYI, "%s: Can't map SID %s to a %cid\n", - __func__, sidstr, sidtype == SIDOWNER ? 'u' : 'g'); - goto out_revert_creds; - } - - /* - * FIXME: Here we assume that uid_t and gid_t are same size. It's - * probably a safe assumption but might be better to check based on - * sidtype. - */ - BUILD_BUG_ON(sizeof(uid_t) != sizeof(gid_t)); - if (sidkey->datalen != sizeof(uid_t)) { - cifs_dbg(FYI, "%s: Downcall contained malformed key (datalen=%hu)\n", - __func__, sidkey->datalen); - key_invalidate(sidkey); - goto out_key_put; - } - - if (sidtype == SIDOWNER) { - kuid_t uid; - uid_t id; - memcpy(&id, &sidkey->payload.data[0], sizeof(uid_t)); - uid = make_kuid(&init_user_ns, id); - if (uid_valid(uid)) - fuid = uid; - } else { - kgid_t gid; - gid_t id; - memcpy(&id, &sidkey->payload.data[0], sizeof(gid_t)); - gid = make_kgid(&init_user_ns, id); - if (gid_valid(gid)) - fgid = gid; - } - -out_key_put: - key_put(sidkey); -out_revert_creds: - revert_creds(saved_cred); - kfree(sidstr); - - /* - * Note that we return 0 here unconditionally. If the mapping - * fails then we just fall back to using the ctx->linux_uid/linux_gid. - */ -got_valid_id: - rc = 0; - if (sidtype == SIDOWNER) - fattr->cf_uid = fuid; - else - fattr->cf_gid = fgid; - return rc; -} - -int -init_cifs_idmap(void) -{ - struct cred *cred; - struct key *keyring; - int ret; - - cifs_dbg(FYI, "Registering the %s key type\n", - cifs_idmap_key_type.name); - - /* create an override credential set with a special thread keyring in - * which requests are cached - * - * this is used to prevent malicious redirections from being installed - * with add_key(). - */ - cred = prepare_kernel_cred(NULL); - if (!cred) - return -ENOMEM; - - keyring = keyring_alloc(".cifs_idmap", - GLOBAL_ROOT_UID, GLOBAL_ROOT_GID, cred, - (KEY_POS_ALL & ~KEY_POS_SETATTR) | - KEY_USR_VIEW | KEY_USR_READ, - KEY_ALLOC_NOT_IN_QUOTA, NULL, NULL); - if (IS_ERR(keyring)) { - ret = PTR_ERR(keyring); - goto failed_put_cred; - } - - ret = register_key_type(&cifs_idmap_key_type); - if (ret < 0) - goto failed_put_key; - - /* instruct request_key() to use this special keyring as a cache for - * the results it looks up */ - set_bit(KEY_FLAG_ROOT_CAN_CLEAR, &keyring->flags); - cred->thread_keyring = keyring; - cred->jit_keyring = KEY_REQKEY_DEFL_THREAD_KEYRING; - root_cred = cred; - - cifs_dbg(FYI, "cifs idmap keyring: %d\n", key_serial(keyring)); - return 0; - -failed_put_key: - key_put(keyring); -failed_put_cred: - put_cred(cred); - return ret; -} - -void -exit_cifs_idmap(void) -{ - key_revoke(root_cred->thread_keyring); - unregister_key_type(&cifs_idmap_key_type); - put_cred(root_cred); - cifs_dbg(FYI, "Unregistered %s key type\n", cifs_idmap_key_type.name); -} - -/* copy ntsd, owner sid, and group sid from a security descriptor to another */ -static __u32 copy_sec_desc(const struct cifs_ntsd *pntsd, - struct cifs_ntsd *pnntsd, - __u32 sidsoffset, - struct cifs_sid *pownersid, - struct cifs_sid *pgrpsid) -{ - struct cifs_sid *owner_sid_ptr, *group_sid_ptr; - struct cifs_sid *nowner_sid_ptr, *ngroup_sid_ptr; - - /* copy security descriptor control portion */ - pnntsd->revision = pntsd->revision; - pnntsd->type = pntsd->type; - pnntsd->dacloffset = cpu_to_le32(sizeof(struct cifs_ntsd)); - pnntsd->sacloffset = 0; - pnntsd->osidoffset = cpu_to_le32(sidsoffset); - pnntsd->gsidoffset = cpu_to_le32(sidsoffset + sizeof(struct cifs_sid)); - - /* copy owner sid */ - if (pownersid) - owner_sid_ptr = pownersid; - else - owner_sid_ptr = (struct cifs_sid *)((char *)pntsd + - le32_to_cpu(pntsd->osidoffset)); - nowner_sid_ptr = (struct cifs_sid *)((char *)pnntsd + sidsoffset); - cifs_copy_sid(nowner_sid_ptr, owner_sid_ptr); - - /* copy group sid */ - if (pgrpsid) - group_sid_ptr = pgrpsid; - else - group_sid_ptr = (struct cifs_sid *)((char *)pntsd + - le32_to_cpu(pntsd->gsidoffset)); - ngroup_sid_ptr = (struct cifs_sid *)((char *)pnntsd + sidsoffset + - sizeof(struct cifs_sid)); - cifs_copy_sid(ngroup_sid_ptr, group_sid_ptr); - - return sidsoffset + (2 * sizeof(struct cifs_sid)); -} - - -/* - change posix mode to reflect permissions - pmode is the existing mode (we only want to overwrite part of this - bits to set can be: S_IRWXU, S_IRWXG or S_IRWXO ie 00700 or 00070 or 00007 -*/ -static void access_flags_to_mode(__le32 ace_flags, int type, umode_t *pmode, - umode_t *pdenied, umode_t mask) -{ - __u32 flags = le32_to_cpu(ace_flags); - /* - * Do not assume "preferred" or "canonical" order. - * The first DENY or ALLOW ACE which matches perfectly is - * the permission to be used. Once allowed or denied, same - * permission in later ACEs do not matter. - */ - - /* If not already allowed, deny these bits */ - if (type == ACCESS_DENIED) { - if (flags & GENERIC_ALL && - !(*pmode & mask & 0777)) - *pdenied |= mask & 0777; - - if (((flags & GENERIC_WRITE) || - ((flags & FILE_WRITE_RIGHTS) == FILE_WRITE_RIGHTS)) && - !(*pmode & mask & 0222)) - *pdenied |= mask & 0222; - - if (((flags & GENERIC_READ) || - ((flags & FILE_READ_RIGHTS) == FILE_READ_RIGHTS)) && - !(*pmode & mask & 0444)) - *pdenied |= mask & 0444; - - if (((flags & GENERIC_EXECUTE) || - ((flags & FILE_EXEC_RIGHTS) == FILE_EXEC_RIGHTS)) && - !(*pmode & mask & 0111)) - *pdenied |= mask & 0111; - - return; - } else if (type != ACCESS_ALLOWED) { - cifs_dbg(VFS, "unknown access control type %d\n", type); - return; - } - /* else ACCESS_ALLOWED type */ - - if ((flags & GENERIC_ALL) && - !(*pdenied & mask & 0777)) { - *pmode |= mask & 0777; - cifs_dbg(NOISY, "all perms\n"); - return; - } - - if (((flags & GENERIC_WRITE) || - ((flags & FILE_WRITE_RIGHTS) == FILE_WRITE_RIGHTS)) && - !(*pdenied & mask & 0222)) - *pmode |= mask & 0222; - - if (((flags & GENERIC_READ) || - ((flags & FILE_READ_RIGHTS) == FILE_READ_RIGHTS)) && - !(*pdenied & mask & 0444)) - *pmode |= mask & 0444; - - if (((flags & GENERIC_EXECUTE) || - ((flags & FILE_EXEC_RIGHTS) == FILE_EXEC_RIGHTS)) && - !(*pdenied & mask & 0111)) - *pmode |= mask & 0111; - - /* If DELETE_CHILD is set only on an owner ACE, set sticky bit */ - if (flags & FILE_DELETE_CHILD) { - if (mask == ACL_OWNER_MASK) { - if (!(*pdenied & 01000)) - *pmode |= 01000; - } else if (!(*pdenied & 01000)) { - *pmode &= ~01000; - *pdenied |= 01000; - } - } - - cifs_dbg(NOISY, "access flags 0x%x mode now %04o\n", flags, *pmode); - return; -} - -/* - Generate access flags to reflect permissions mode is the existing mode. - This function is called for every ACE in the DACL whose SID matches - with either owner or group or everyone. -*/ - -static void mode_to_access_flags(umode_t mode, umode_t bits_to_use, - __u32 *pace_flags) -{ - /* reset access mask */ - *pace_flags = 0x0; - - /* bits to use are either S_IRWXU or S_IRWXG or S_IRWXO */ - mode &= bits_to_use; - - /* check for R/W/X UGO since we do not know whose flags - is this but we have cleared all the bits sans RWX for - either user or group or other as per bits_to_use */ - if (mode & S_IRUGO) - *pace_flags |= SET_FILE_READ_RIGHTS; - if (mode & S_IWUGO) - *pace_flags |= SET_FILE_WRITE_RIGHTS; - if (mode & S_IXUGO) - *pace_flags |= SET_FILE_EXEC_RIGHTS; - - cifs_dbg(NOISY, "mode: %04o, access flags now 0x%x\n", - mode, *pace_flags); - return; -} - -static __u16 cifs_copy_ace(struct cifs_ace *dst, struct cifs_ace *src, struct cifs_sid *psid) -{ - __u16 size = 1 + 1 + 2 + 4; - - dst->type = src->type; - dst->flags = src->flags; - dst->access_req = src->access_req; - - /* Check if there's a replacement sid specified */ - if (psid) - size += cifs_copy_sid(&dst->sid, psid); - else - size += cifs_copy_sid(&dst->sid, &src->sid); - - dst->size = cpu_to_le16(size); - - return size; -} - -static __u16 fill_ace_for_sid(struct cifs_ace *pntace, - const struct cifs_sid *psid, __u64 nmode, - umode_t bits, __u8 access_type, - bool allow_delete_child) -{ - int i; - __u16 size = 0; - __u32 access_req = 0; - - pntace->type = access_type; - pntace->flags = 0x0; - mode_to_access_flags(nmode, bits, &access_req); - - if (access_type == ACCESS_ALLOWED && allow_delete_child) - access_req |= FILE_DELETE_CHILD; - - if (access_type == ACCESS_ALLOWED && !access_req) - access_req = SET_MINIMUM_RIGHTS; - else if (access_type == ACCESS_DENIED) - access_req &= ~SET_MINIMUM_RIGHTS; - - pntace->access_req = cpu_to_le32(access_req); - - pntace->sid.revision = psid->revision; - pntace->sid.num_subauth = psid->num_subauth; - for (i = 0; i < NUM_AUTHS; i++) - pntace->sid.authority[i] = psid->authority[i]; - for (i = 0; i < psid->num_subauth; i++) - pntace->sid.sub_auth[i] = psid->sub_auth[i]; - - size = 1 + 1 + 2 + 4 + 1 + 1 + 6 + (psid->num_subauth * 4); - pntace->size = cpu_to_le16(size); - - return size; -} - - -#ifdef CONFIG_CIFS_DEBUG2 -static void dump_ace(struct cifs_ace *pace, char *end_of_acl) -{ - int num_subauth; - - /* validate that we do not go past end of acl */ - - if (le16_to_cpu(pace->size) < 16) { - cifs_dbg(VFS, "ACE too small %d\n", le16_to_cpu(pace->size)); - return; - } - - if (end_of_acl < (char *)pace + le16_to_cpu(pace->size)) { - cifs_dbg(VFS, "ACL too small to parse ACE\n"); - return; - } - - num_subauth = pace->sid.num_subauth; - if (num_subauth) { - int i; - cifs_dbg(FYI, "ACE revision %d num_auth %d type %d flags %d size %d\n", - pace->sid.revision, pace->sid.num_subauth, pace->type, - pace->flags, le16_to_cpu(pace->size)); - for (i = 0; i < num_subauth; ++i) { - cifs_dbg(FYI, "ACE sub_auth[%d]: 0x%x\n", - i, le32_to_cpu(pace->sid.sub_auth[i])); - } - - /* BB add length check to make sure that we do not have huge - num auths and therefore go off the end */ - } - - return; -} -#endif - -static void parse_dacl(struct cifs_acl *pdacl, char *end_of_acl, - struct cifs_sid *pownersid, struct cifs_sid *pgrpsid, - struct cifs_fattr *fattr, bool mode_from_special_sid) -{ - int i; - int num_aces = 0; - int acl_size; - char *acl_base; - struct cifs_ace **ppace; - - /* BB need to add parm so we can store the SID BB */ - - if (!pdacl) { - /* no DACL in the security descriptor, set - all the permissions for user/group/other */ - fattr->cf_mode |= 0777; - return; - } - - /* validate that we do not go past end of acl */ - if (end_of_acl < (char *)pdacl + le16_to_cpu(pdacl->size)) { - cifs_dbg(VFS, "ACL too small to parse DACL\n"); - return; - } - - cifs_dbg(NOISY, "DACL revision %d size %d num aces %d\n", - le16_to_cpu(pdacl->revision), le16_to_cpu(pdacl->size), - le32_to_cpu(pdacl->num_aces)); - - /* reset rwx permissions for user/group/other. - Also, if num_aces is 0 i.e. DACL has no ACEs, - user/group/other have no permissions */ - fattr->cf_mode &= ~(0777); - - acl_base = (char *)pdacl; - acl_size = sizeof(struct cifs_acl); - - num_aces = le32_to_cpu(pdacl->num_aces); - if (num_aces > 0) { - umode_t denied_mode = 0; - - if (num_aces > ULONG_MAX / sizeof(struct cifs_ace *)) - return; - ppace = kmalloc_array(num_aces, sizeof(struct cifs_ace *), - GFP_KERNEL); - if (!ppace) - return; - - for (i = 0; i < num_aces; ++i) { - ppace[i] = (struct cifs_ace *) (acl_base + acl_size); -#ifdef CONFIG_CIFS_DEBUG2 - dump_ace(ppace[i], end_of_acl); -#endif - if (mode_from_special_sid && - (compare_sids(&(ppace[i]->sid), - &sid_unix_NFS_mode) == 0)) { - /* - * Full permissions are: - * 07777 = S_ISUID | S_ISGID | S_ISVTX | - * S_IRWXU | S_IRWXG | S_IRWXO - */ - fattr->cf_mode &= ~07777; - fattr->cf_mode |= - le32_to_cpu(ppace[i]->sid.sub_auth[2]); - break; - } else { - if (compare_sids(&(ppace[i]->sid), pownersid) == 0) { - access_flags_to_mode(ppace[i]->access_req, - ppace[i]->type, - &fattr->cf_mode, - &denied_mode, - ACL_OWNER_MASK); - } else if (compare_sids(&(ppace[i]->sid), pgrpsid) == 0) { - access_flags_to_mode(ppace[i]->access_req, - ppace[i]->type, - &fattr->cf_mode, - &denied_mode, - ACL_GROUP_MASK); - } else if ((compare_sids(&(ppace[i]->sid), &sid_everyone) == 0) || - (compare_sids(&(ppace[i]->sid), &sid_authusers) == 0)) { - access_flags_to_mode(ppace[i]->access_req, - ppace[i]->type, - &fattr->cf_mode, - &denied_mode, - ACL_EVERYONE_MASK); - } - } - - -/* memcpy((void *)(&(cifscred->aces[i])), - (void *)ppace[i], - sizeof(struct cifs_ace)); */ - - acl_base = (char *)ppace[i]; - acl_size = le16_to_cpu(ppace[i]->size); - } - - kfree(ppace); - } - - return; -} - -unsigned int setup_authusers_ACE(struct cifs_ace *pntace) -{ - int i; - unsigned int ace_size = 20; - - pntace->type = ACCESS_ALLOWED_ACE_TYPE; - pntace->flags = 0x0; - pntace->access_req = cpu_to_le32(GENERIC_ALL); - pntace->sid.num_subauth = 1; - pntace->sid.revision = 1; - for (i = 0; i < NUM_AUTHS; i++) - pntace->sid.authority[i] = sid_authusers.authority[i]; - - pntace->sid.sub_auth[0] = sid_authusers.sub_auth[0]; - - /* size = 1 + 1 + 2 + 4 + 1 + 1 + 6 + (psid->num_subauth*4) */ - pntace->size = cpu_to_le16(ace_size); - return ace_size; -} - -/* - * Fill in the special SID based on the mode. See - * https://technet.microsoft.com/en-us/library/hh509017(v=ws.10).aspx - */ -unsigned int setup_special_mode_ACE(struct cifs_ace *pntace, __u64 nmode) -{ - int i; - unsigned int ace_size = 28; - - pntace->type = ACCESS_DENIED_ACE_TYPE; - pntace->flags = 0x0; - pntace->access_req = 0; - pntace->sid.num_subauth = 3; - pntace->sid.revision = 1; - for (i = 0; i < NUM_AUTHS; i++) - pntace->sid.authority[i] = sid_unix_NFS_mode.authority[i]; - - pntace->sid.sub_auth[0] = sid_unix_NFS_mode.sub_auth[0]; - pntace->sid.sub_auth[1] = sid_unix_NFS_mode.sub_auth[1]; - pntace->sid.sub_auth[2] = cpu_to_le32(nmode & 07777); - - /* size = 1 + 1 + 2 + 4 + 1 + 1 + 6 + (psid->num_subauth*4) */ - pntace->size = cpu_to_le16(ace_size); - return ace_size; -} - -unsigned int setup_special_user_owner_ACE(struct cifs_ace *pntace) -{ - int i; - unsigned int ace_size = 28; - - pntace->type = ACCESS_ALLOWED_ACE_TYPE; - pntace->flags = 0x0; - pntace->access_req = cpu_to_le32(GENERIC_ALL); - pntace->sid.num_subauth = 3; - pntace->sid.revision = 1; - for (i = 0; i < NUM_AUTHS; i++) - pntace->sid.authority[i] = sid_unix_NFS_users.authority[i]; - - pntace->sid.sub_auth[0] = sid_unix_NFS_users.sub_auth[0]; - pntace->sid.sub_auth[1] = sid_unix_NFS_users.sub_auth[1]; - pntace->sid.sub_auth[2] = cpu_to_le32(current_fsgid().val); - - /* size = 1 + 1 + 2 + 4 + 1 + 1 + 6 + (psid->num_subauth*4) */ - pntace->size = cpu_to_le16(ace_size); - return ace_size; -} - -static void populate_new_aces(char *nacl_base, - struct cifs_sid *pownersid, - struct cifs_sid *pgrpsid, - __u64 *pnmode, u32 *pnum_aces, u16 *pnsize, - bool modefromsid) -{ - __u64 nmode; - u32 num_aces = 0; - u16 nsize = 0; - __u64 user_mode; - __u64 group_mode; - __u64 other_mode; - __u64 deny_user_mode = 0; - __u64 deny_group_mode = 0; - bool sticky_set = false; - struct cifs_ace *pnntace = NULL; - - nmode = *pnmode; - num_aces = *pnum_aces; - nsize = *pnsize; - - if (modefromsid) { - pnntace = (struct cifs_ace *) (nacl_base + nsize); - nsize += setup_special_mode_ACE(pnntace, nmode); - num_aces++; - pnntace = (struct cifs_ace *) (nacl_base + nsize); - nsize += setup_authusers_ACE(pnntace); - num_aces++; - goto set_size; - } - - /* - * We'll try to keep the mode as requested by the user. - * But in cases where we cannot meaningfully convert that - * into ACL, return back the updated mode, so that it is - * updated in the inode. - */ - - if (!memcmp(pownersid, pgrpsid, sizeof(struct cifs_sid))) { - /* - * Case when owner and group SIDs are the same. - * Set the more restrictive of the two modes. - */ - user_mode = nmode & (nmode << 3) & 0700; - group_mode = nmode & (nmode >> 3) & 0070; - } else { - user_mode = nmode & 0700; - group_mode = nmode & 0070; - } - - other_mode = nmode & 0007; - - /* We need DENY ACE when the perm is more restrictive than the next sets. */ - deny_user_mode = ~(user_mode) & ((group_mode << 3) | (other_mode << 6)) & 0700; - deny_group_mode = ~(group_mode) & (other_mode << 3) & 0070; - - *pnmode = user_mode | group_mode | other_mode | (nmode & ~0777); - - /* This tells if we should allow delete child for group and everyone. */ - if (nmode & 01000) - sticky_set = true; - - if (deny_user_mode) { - pnntace = (struct cifs_ace *) (nacl_base + nsize); - nsize += fill_ace_for_sid(pnntace, pownersid, deny_user_mode, - 0700, ACCESS_DENIED, false); - num_aces++; - } - - /* Group DENY ACE does not conflict with owner ALLOW ACE. Keep in preferred order*/ - if (deny_group_mode && !(deny_group_mode & (user_mode >> 3))) { - pnntace = (struct cifs_ace *) (nacl_base + nsize); - nsize += fill_ace_for_sid(pnntace, pgrpsid, deny_group_mode, - 0070, ACCESS_DENIED, false); - num_aces++; - } - - pnntace = (struct cifs_ace *) (nacl_base + nsize); - nsize += fill_ace_for_sid(pnntace, pownersid, user_mode, - 0700, ACCESS_ALLOWED, true); - num_aces++; - - /* Group DENY ACE conflicts with owner ALLOW ACE. So keep it after. */ - if (deny_group_mode && (deny_group_mode & (user_mode >> 3))) { - pnntace = (struct cifs_ace *) (nacl_base + nsize); - nsize += fill_ace_for_sid(pnntace, pgrpsid, deny_group_mode, - 0070, ACCESS_DENIED, false); - num_aces++; - } - - pnntace = (struct cifs_ace *) (nacl_base + nsize); - nsize += fill_ace_for_sid(pnntace, pgrpsid, group_mode, - 0070, ACCESS_ALLOWED, !sticky_set); - num_aces++; - - pnntace = (struct cifs_ace *) (nacl_base + nsize); - nsize += fill_ace_for_sid(pnntace, &sid_everyone, other_mode, - 0007, ACCESS_ALLOWED, !sticky_set); - num_aces++; - -set_size: - *pnum_aces = num_aces; - *pnsize = nsize; -} - -static __u16 replace_sids_and_copy_aces(struct cifs_acl *pdacl, struct cifs_acl *pndacl, - struct cifs_sid *pownersid, struct cifs_sid *pgrpsid, - struct cifs_sid *pnownersid, struct cifs_sid *pngrpsid) -{ - int i; - u16 size = 0; - struct cifs_ace *pntace = NULL; - char *acl_base = NULL; - u32 src_num_aces = 0; - u16 nsize = 0; - struct cifs_ace *pnntace = NULL; - char *nacl_base = NULL; - u16 ace_size = 0; - - acl_base = (char *)pdacl; - size = sizeof(struct cifs_acl); - src_num_aces = le32_to_cpu(pdacl->num_aces); - - nacl_base = (char *)pndacl; - nsize = sizeof(struct cifs_acl); - - /* Go through all the ACEs */ - for (i = 0; i < src_num_aces; ++i) { - pntace = (struct cifs_ace *) (acl_base + size); - pnntace = (struct cifs_ace *) (nacl_base + nsize); - - if (pnownersid && compare_sids(&pntace->sid, pownersid) == 0) - ace_size = cifs_copy_ace(pnntace, pntace, pnownersid); - else if (pngrpsid && compare_sids(&pntace->sid, pgrpsid) == 0) - ace_size = cifs_copy_ace(pnntace, pntace, pngrpsid); - else - ace_size = cifs_copy_ace(pnntace, pntace, NULL); - - size += le16_to_cpu(pntace->size); - nsize += ace_size; - } - - return nsize; -} - -static int set_chmod_dacl(struct cifs_acl *pdacl, struct cifs_acl *pndacl, - struct cifs_sid *pownersid, struct cifs_sid *pgrpsid, - __u64 *pnmode, bool mode_from_sid) -{ - int i; - u16 size = 0; - struct cifs_ace *pntace = NULL; - char *acl_base = NULL; - u32 src_num_aces = 0; - u16 nsize = 0; - struct cifs_ace *pnntace = NULL; - char *nacl_base = NULL; - u32 num_aces = 0; - bool new_aces_set = false; - - /* Assuming that pndacl and pnmode are never NULL */ - nacl_base = (char *)pndacl; - nsize = sizeof(struct cifs_acl); - - /* If pdacl is NULL, we don't have a src. Simply populate new ACL. */ - if (!pdacl) { - populate_new_aces(nacl_base, - pownersid, pgrpsid, - pnmode, &num_aces, &nsize, - mode_from_sid); - goto finalize_dacl; - } - - acl_base = (char *)pdacl; - size = sizeof(struct cifs_acl); - src_num_aces = le32_to_cpu(pdacl->num_aces); - - /* Retain old ACEs which we can retain */ - for (i = 0; i < src_num_aces; ++i) { - pntace = (struct cifs_ace *) (acl_base + size); - - if (!new_aces_set && (pntace->flags & INHERITED_ACE)) { - /* Place the new ACEs in between existing explicit and inherited */ - populate_new_aces(nacl_base, - pownersid, pgrpsid, - pnmode, &num_aces, &nsize, - mode_from_sid); - - new_aces_set = true; - } - - /* If it's any one of the ACE we're replacing, skip! */ - if (((compare_sids(&pntace->sid, &sid_unix_NFS_mode) == 0) || - (compare_sids(&pntace->sid, pownersid) == 0) || - (compare_sids(&pntace->sid, pgrpsid) == 0) || - (compare_sids(&pntace->sid, &sid_everyone) == 0) || - (compare_sids(&pntace->sid, &sid_authusers) == 0))) { - goto next_ace; - } - - /* update the pointer to the next ACE to populate*/ - pnntace = (struct cifs_ace *) (nacl_base + nsize); - - nsize += cifs_copy_ace(pnntace, pntace, NULL); - num_aces++; - -next_ace: - size += le16_to_cpu(pntace->size); - } - - /* If inherited ACEs are not present, place the new ones at the tail */ - if (!new_aces_set) { - populate_new_aces(nacl_base, - pownersid, pgrpsid, - pnmode, &num_aces, &nsize, - mode_from_sid); - - new_aces_set = true; - } - -finalize_dacl: - pndacl->num_aces = cpu_to_le32(num_aces); - pndacl->size = cpu_to_le16(nsize); - - return 0; -} - -static int parse_sid(struct cifs_sid *psid, char *end_of_acl) -{ - /* BB need to add parm so we can store the SID BB */ - - /* validate that we do not go past end of ACL - sid must be at least 8 - bytes long (assuming no sub-auths - e.g. the null SID */ - if (end_of_acl < (char *)psid + 8) { - cifs_dbg(VFS, "ACL too small to parse SID %p\n", psid); - return -EINVAL; - } - -#ifdef CONFIG_CIFS_DEBUG2 - if (psid->num_subauth) { - int i; - cifs_dbg(FYI, "SID revision %d num_auth %d\n", - psid->revision, psid->num_subauth); - - for (i = 0; i < psid->num_subauth; i++) { - cifs_dbg(FYI, "SID sub_auth[%d]: 0x%x\n", - i, le32_to_cpu(psid->sub_auth[i])); - } - - /* BB add length check to make sure that we do not have huge - num auths and therefore go off the end */ - cifs_dbg(FYI, "RID 0x%x\n", - le32_to_cpu(psid->sub_auth[psid->num_subauth-1])); - } -#endif - - return 0; -} - - -/* Convert CIFS ACL to POSIX form */ -static int parse_sec_desc(struct cifs_sb_info *cifs_sb, - struct cifs_ntsd *pntsd, int acl_len, struct cifs_fattr *fattr, - bool get_mode_from_special_sid) -{ - int rc = 0; - struct cifs_sid *owner_sid_ptr, *group_sid_ptr; - struct cifs_acl *dacl_ptr; /* no need for SACL ptr */ - char *end_of_acl = ((char *)pntsd) + acl_len; - __u32 dacloffset; - - if (pntsd == NULL) - return -EIO; - - owner_sid_ptr = (struct cifs_sid *)((char *)pntsd + - le32_to_cpu(pntsd->osidoffset)); - group_sid_ptr = (struct cifs_sid *)((char *)pntsd + - le32_to_cpu(pntsd->gsidoffset)); - dacloffset = le32_to_cpu(pntsd->dacloffset); - dacl_ptr = (struct cifs_acl *)((char *)pntsd + dacloffset); - cifs_dbg(NOISY, "revision %d type 0x%x ooffset 0x%x goffset 0x%x sacloffset 0x%x dacloffset 0x%x\n", - pntsd->revision, pntsd->type, le32_to_cpu(pntsd->osidoffset), - le32_to_cpu(pntsd->gsidoffset), - le32_to_cpu(pntsd->sacloffset), dacloffset); -/* cifs_dump_mem("owner_sid: ", owner_sid_ptr, 64); */ - rc = parse_sid(owner_sid_ptr, end_of_acl); - if (rc) { - cifs_dbg(FYI, "%s: Error %d parsing Owner SID\n", __func__, rc); - return rc; - } - rc = sid_to_id(cifs_sb, owner_sid_ptr, fattr, SIDOWNER); - if (rc) { - cifs_dbg(FYI, "%s: Error %d mapping Owner SID to uid\n", - __func__, rc); - return rc; - } - - rc = parse_sid(group_sid_ptr, end_of_acl); - if (rc) { - cifs_dbg(FYI, "%s: Error %d mapping Owner SID to gid\n", - __func__, rc); - return rc; - } - rc = sid_to_id(cifs_sb, group_sid_ptr, fattr, SIDGROUP); - if (rc) { - cifs_dbg(FYI, "%s: Error %d mapping Group SID to gid\n", - __func__, rc); - return rc; - } - - if (dacloffset) - parse_dacl(dacl_ptr, end_of_acl, owner_sid_ptr, - group_sid_ptr, fattr, get_mode_from_special_sid); - else - cifs_dbg(FYI, "no ACL\n"); /* BB grant all or default perms? */ - - return rc; -} - -/* Convert permission bits from mode to equivalent CIFS ACL */ -static int build_sec_desc(struct cifs_ntsd *pntsd, struct cifs_ntsd *pnntsd, - __u32 secdesclen, __u32 *pnsecdesclen, __u64 *pnmode, kuid_t uid, kgid_t gid, - bool mode_from_sid, bool id_from_sid, int *aclflag) -{ - int rc = 0; - __u32 dacloffset; - __u32 ndacloffset; - __u32 sidsoffset; - struct cifs_sid *owner_sid_ptr, *group_sid_ptr; - struct cifs_sid *nowner_sid_ptr = NULL, *ngroup_sid_ptr = NULL; - struct cifs_acl *dacl_ptr = NULL; /* no need for SACL ptr */ - struct cifs_acl *ndacl_ptr = NULL; /* no need for SACL ptr */ - char *end_of_acl = ((char *)pntsd) + secdesclen; - u16 size = 0; - - dacloffset = le32_to_cpu(pntsd->dacloffset); - if (dacloffset) { - dacl_ptr = (struct cifs_acl *)((char *)pntsd + dacloffset); - if (end_of_acl < (char *)dacl_ptr + le16_to_cpu(dacl_ptr->size)) { - cifs_dbg(VFS, "Server returned illegal ACL size\n"); - return -EINVAL; - } - } - - owner_sid_ptr = (struct cifs_sid *)((char *)pntsd + - le32_to_cpu(pntsd->osidoffset)); - group_sid_ptr = (struct cifs_sid *)((char *)pntsd + - le32_to_cpu(pntsd->gsidoffset)); - - if (pnmode && *pnmode != NO_CHANGE_64) { /* chmod */ - ndacloffset = sizeof(struct cifs_ntsd); - ndacl_ptr = (struct cifs_acl *)((char *)pnntsd + ndacloffset); - ndacl_ptr->revision = - dacloffset ? dacl_ptr->revision : cpu_to_le16(ACL_REVISION); - - ndacl_ptr->size = cpu_to_le16(0); - ndacl_ptr->num_aces = cpu_to_le32(0); - - rc = set_chmod_dacl(dacl_ptr, ndacl_ptr, owner_sid_ptr, group_sid_ptr, - pnmode, mode_from_sid); - - sidsoffset = ndacloffset + le16_to_cpu(ndacl_ptr->size); - /* copy the non-dacl portion of secdesc */ - *pnsecdesclen = copy_sec_desc(pntsd, pnntsd, sidsoffset, - NULL, NULL); - - *aclflag |= CIFS_ACL_DACL; - } else { - ndacloffset = sizeof(struct cifs_ntsd); - ndacl_ptr = (struct cifs_acl *)((char *)pnntsd + ndacloffset); - ndacl_ptr->revision = - dacloffset ? dacl_ptr->revision : cpu_to_le16(ACL_REVISION); - ndacl_ptr->num_aces = dacl_ptr ? dacl_ptr->num_aces : 0; - - if (uid_valid(uid)) { /* chown */ - uid_t id; - nowner_sid_ptr = kzalloc(sizeof(struct cifs_sid), - GFP_KERNEL); - if (!nowner_sid_ptr) { - rc = -ENOMEM; - goto chown_chgrp_exit; - } - id = from_kuid(&init_user_ns, uid); - if (id_from_sid) { - struct owner_sid *osid = (struct owner_sid *)nowner_sid_ptr; - /* Populate the user ownership fields S-1-5-88-1 */ - osid->Revision = 1; - osid->NumAuth = 3; - osid->Authority[5] = 5; - osid->SubAuthorities[0] = cpu_to_le32(88); - osid->SubAuthorities[1] = cpu_to_le32(1); - osid->SubAuthorities[2] = cpu_to_le32(id); - - } else { /* lookup sid with upcall */ - rc = id_to_sid(id, SIDOWNER, nowner_sid_ptr); - if (rc) { - cifs_dbg(FYI, "%s: Mapping error %d for owner id %d\n", - __func__, rc, id); - goto chown_chgrp_exit; - } - } - *aclflag |= CIFS_ACL_OWNER; - } - if (gid_valid(gid)) { /* chgrp */ - gid_t id; - ngroup_sid_ptr = kzalloc(sizeof(struct cifs_sid), - GFP_KERNEL); - if (!ngroup_sid_ptr) { - rc = -ENOMEM; - goto chown_chgrp_exit; - } - id = from_kgid(&init_user_ns, gid); - if (id_from_sid) { - struct owner_sid *gsid = (struct owner_sid *)ngroup_sid_ptr; - /* Populate the group ownership fields S-1-5-88-2 */ - gsid->Revision = 1; - gsid->NumAuth = 3; - gsid->Authority[5] = 5; - gsid->SubAuthorities[0] = cpu_to_le32(88); - gsid->SubAuthorities[1] = cpu_to_le32(2); - gsid->SubAuthorities[2] = cpu_to_le32(id); - - } else { /* lookup sid with upcall */ - rc = id_to_sid(id, SIDGROUP, ngroup_sid_ptr); - if (rc) { - cifs_dbg(FYI, "%s: Mapping error %d for group id %d\n", - __func__, rc, id); - goto chown_chgrp_exit; - } - } - *aclflag |= CIFS_ACL_GROUP; - } - - if (dacloffset) { - /* Replace ACEs for old owner with new one */ - size = replace_sids_and_copy_aces(dacl_ptr, ndacl_ptr, - owner_sid_ptr, group_sid_ptr, - nowner_sid_ptr, ngroup_sid_ptr); - ndacl_ptr->size = cpu_to_le16(size); - } - - sidsoffset = ndacloffset + le16_to_cpu(ndacl_ptr->size); - /* copy the non-dacl portion of secdesc */ - *pnsecdesclen = copy_sec_desc(pntsd, pnntsd, sidsoffset, - nowner_sid_ptr, ngroup_sid_ptr); - -chown_chgrp_exit: - /* errors could jump here. So make sure we return soon after this */ - kfree(nowner_sid_ptr); - kfree(ngroup_sid_ptr); - } - - return rc; -} - -#ifdef CONFIG_CIFS_ALLOW_INSECURE_LEGACY -struct cifs_ntsd *get_cifs_acl_by_fid(struct cifs_sb_info *cifs_sb, - const struct cifs_fid *cifsfid, u32 *pacllen, - u32 __maybe_unused unused) -{ - struct cifs_ntsd *pntsd = NULL; - unsigned int xid; - int rc; - struct tcon_link *tlink = cifs_sb_tlink(cifs_sb); - - if (IS_ERR(tlink)) - return ERR_CAST(tlink); - - xid = get_xid(); - rc = CIFSSMBGetCIFSACL(xid, tlink_tcon(tlink), cifsfid->netfid, &pntsd, - pacllen); - free_xid(xid); - - cifs_put_tlink(tlink); - - cifs_dbg(FYI, "%s: rc = %d ACL len %d\n", __func__, rc, *pacllen); - if (rc) - return ERR_PTR(rc); - return pntsd; -} - -static struct cifs_ntsd *get_cifs_acl_by_path(struct cifs_sb_info *cifs_sb, - const char *path, u32 *pacllen) -{ - struct cifs_ntsd *pntsd = NULL; - int oplock = 0; - unsigned int xid; - int rc; - struct cifs_tcon *tcon; - struct tcon_link *tlink = cifs_sb_tlink(cifs_sb); - struct cifs_fid fid; - struct cifs_open_parms oparms; - - if (IS_ERR(tlink)) - return ERR_CAST(tlink); - - tcon = tlink_tcon(tlink); - xid = get_xid(); - - oparms = (struct cifs_open_parms) { - .tcon = tcon, - .cifs_sb = cifs_sb, - .desired_access = READ_CONTROL, - .create_options = cifs_create_options(cifs_sb, 0), - .disposition = FILE_OPEN, - .path = path, - .fid = &fid, - }; - - rc = CIFS_open(xid, &oparms, &oplock, NULL); - if (!rc) { - rc = CIFSSMBGetCIFSACL(xid, tcon, fid.netfid, &pntsd, pacllen); - CIFSSMBClose(xid, tcon, fid.netfid); - } - - cifs_put_tlink(tlink); - free_xid(xid); - - cifs_dbg(FYI, "%s: rc = %d ACL len %d\n", __func__, rc, *pacllen); - if (rc) - return ERR_PTR(rc); - return pntsd; -} - -/* Retrieve an ACL from the server */ -struct cifs_ntsd *get_cifs_acl(struct cifs_sb_info *cifs_sb, - struct inode *inode, const char *path, - u32 *pacllen, u32 info) -{ - struct cifs_ntsd *pntsd = NULL; - struct cifsFileInfo *open_file = NULL; - - if (inode) - open_file = find_readable_file(CIFS_I(inode), true); - if (!open_file) - return get_cifs_acl_by_path(cifs_sb, path, pacllen); - - pntsd = get_cifs_acl_by_fid(cifs_sb, &open_file->fid, pacllen, info); - cifsFileInfo_put(open_file); - return pntsd; -} - - /* Set an ACL on the server */ -int set_cifs_acl(struct cifs_ntsd *pnntsd, __u32 acllen, - struct inode *inode, const char *path, int aclflag) -{ - int oplock = 0; - unsigned int xid; - int rc, access_flags; - struct cifs_tcon *tcon; - struct cifs_sb_info *cifs_sb = CIFS_SB(inode->i_sb); - struct tcon_link *tlink = cifs_sb_tlink(cifs_sb); - struct cifs_fid fid; - struct cifs_open_parms oparms; - - if (IS_ERR(tlink)) - return PTR_ERR(tlink); - - tcon = tlink_tcon(tlink); - xid = get_xid(); - - if (aclflag == CIFS_ACL_OWNER || aclflag == CIFS_ACL_GROUP) - access_flags = WRITE_OWNER; - else - access_flags = WRITE_DAC; - - oparms = (struct cifs_open_parms) { - .tcon = tcon, - .cifs_sb = cifs_sb, - .desired_access = access_flags, - .create_options = cifs_create_options(cifs_sb, 0), - .disposition = FILE_OPEN, - .path = path, - .fid = &fid, - }; - - rc = CIFS_open(xid, &oparms, &oplock, NULL); - if (rc) { - cifs_dbg(VFS, "Unable to open file to set ACL\n"); - goto out; - } - - rc = CIFSSMBSetCIFSACL(xid, tcon, fid.netfid, pnntsd, acllen, aclflag); - cifs_dbg(NOISY, "SetCIFSACL rc = %d\n", rc); - - CIFSSMBClose(xid, tcon, fid.netfid); -out: - free_xid(xid); - cifs_put_tlink(tlink); - return rc; -} -#endif /* CONFIG_CIFS_ALLOW_INSECURE_LEGACY */ - -/* Translate the CIFS ACL (similar to NTFS ACL) for a file into mode bits */ -int -cifs_acl_to_fattr(struct cifs_sb_info *cifs_sb, struct cifs_fattr *fattr, - struct inode *inode, bool mode_from_special_sid, - const char *path, const struct cifs_fid *pfid) -{ - struct cifs_ntsd *pntsd = NULL; - u32 acllen = 0; - int rc = 0; - struct tcon_link *tlink = cifs_sb_tlink(cifs_sb); - struct smb_version_operations *ops; - const u32 info = 0; - - cifs_dbg(NOISY, "converting ACL to mode for %s\n", path); - - if (IS_ERR(tlink)) - return PTR_ERR(tlink); - - ops = tlink_tcon(tlink)->ses->server->ops; - - if (pfid && (ops->get_acl_by_fid)) - pntsd = ops->get_acl_by_fid(cifs_sb, pfid, &acllen, info); - else if (ops->get_acl) - pntsd = ops->get_acl(cifs_sb, inode, path, &acllen, info); - else { - cifs_put_tlink(tlink); - return -EOPNOTSUPP; - } - /* if we can retrieve the ACL, now parse Access Control Entries, ACEs */ - if (IS_ERR(pntsd)) { - rc = PTR_ERR(pntsd); - cifs_dbg(VFS, "%s: error %d getting sec desc\n", __func__, rc); - } else if (mode_from_special_sid) { - rc = parse_sec_desc(cifs_sb, pntsd, acllen, fattr, true); - kfree(pntsd); - } else { - /* get approximated mode from ACL */ - rc = parse_sec_desc(cifs_sb, pntsd, acllen, fattr, false); - kfree(pntsd); - if (rc) - cifs_dbg(VFS, "parse sec desc failed rc = %d\n", rc); - } - - cifs_put_tlink(tlink); - - return rc; -} - -/* Convert mode bits to an ACL so we can update the ACL on the server */ -int -id_mode_to_cifs_acl(struct inode *inode, const char *path, __u64 *pnmode, - kuid_t uid, kgid_t gid) -{ - int rc = 0; - int aclflag = CIFS_ACL_DACL; /* default flag to set */ - __u32 secdesclen = 0; - __u32 nsecdesclen = 0; - __u32 dacloffset = 0; - struct cifs_acl *dacl_ptr = NULL; - struct cifs_ntsd *pntsd = NULL; /* acl obtained from server */ - struct cifs_ntsd *pnntsd = NULL; /* modified acl to be sent to server */ - struct cifs_sb_info *cifs_sb = CIFS_SB(inode->i_sb); - struct tcon_link *tlink = cifs_sb_tlink(cifs_sb); - struct smb_version_operations *ops; - bool mode_from_sid, id_from_sid; - const u32 info = 0; - - if (IS_ERR(tlink)) - return PTR_ERR(tlink); - - ops = tlink_tcon(tlink)->ses->server->ops; - - cifs_dbg(NOISY, "set ACL from mode for %s\n", path); - - /* Get the security descriptor */ - - if (ops->get_acl == NULL) { - cifs_put_tlink(tlink); - return -EOPNOTSUPP; - } - - pntsd = ops->get_acl(cifs_sb, inode, path, &secdesclen, info); - if (IS_ERR(pntsd)) { - rc = PTR_ERR(pntsd); - cifs_dbg(VFS, "%s: error %d getting sec desc\n", __func__, rc); - cifs_put_tlink(tlink); - return rc; - } - - if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_MODE_FROM_SID) - mode_from_sid = true; - else - mode_from_sid = false; - - if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_UID_FROM_ACL) - id_from_sid = true; - else - id_from_sid = false; - - /* Potentially, five new ACEs can be added to the ACL for U,G,O mapping */ - nsecdesclen = secdesclen; - if (pnmode && *pnmode != NO_CHANGE_64) { /* chmod */ - if (mode_from_sid) - nsecdesclen += 2 * sizeof(struct cifs_ace); - else /* cifsacl */ - nsecdesclen += 5 * sizeof(struct cifs_ace); - } else { /* chown */ - /* When ownership changes, changes new owner sid length could be different */ - nsecdesclen = sizeof(struct cifs_ntsd) + (sizeof(struct cifs_sid) * 2); - dacloffset = le32_to_cpu(pntsd->dacloffset); - if (dacloffset) { - dacl_ptr = (struct cifs_acl *)((char *)pntsd + dacloffset); - if (mode_from_sid) - nsecdesclen += - le32_to_cpu(dacl_ptr->num_aces) * sizeof(struct cifs_ace); - else /* cifsacl */ - nsecdesclen += le16_to_cpu(dacl_ptr->size); - } - } - - /* - * Add three ACEs for owner, group, everyone getting rid of other ACEs - * as chmod disables ACEs and set the security descriptor. Allocate - * memory for the smb header, set security descriptor request security - * descriptor parameters, and security descriptor itself - */ - nsecdesclen = max_t(u32, nsecdesclen, DEFAULT_SEC_DESC_LEN); - pnntsd = kmalloc(nsecdesclen, GFP_KERNEL); - if (!pnntsd) { - kfree(pntsd); - cifs_put_tlink(tlink); - return -ENOMEM; - } - - rc = build_sec_desc(pntsd, pnntsd, secdesclen, &nsecdesclen, pnmode, uid, gid, - mode_from_sid, id_from_sid, &aclflag); - - cifs_dbg(NOISY, "build_sec_desc rc: %d\n", rc); - - if (ops->set_acl == NULL) - rc = -EOPNOTSUPP; - - if (!rc) { - /* Set the security descriptor */ - rc = ops->set_acl(pnntsd, nsecdesclen, inode, path, aclflag); - cifs_dbg(NOISY, "set_cifs_acl rc: %d\n", rc); - } - cifs_put_tlink(tlink); - - kfree(pnntsd); - kfree(pntsd); - return rc; -} diff --git a/fs/cifs/cifsacl.h b/fs/cifs/cifsacl.h deleted file mode 100644 index ccbfc754bd3c7..0000000000000 --- a/fs/cifs/cifsacl.h +++ /dev/null @@ -1,199 +0,0 @@ -/* SPDX-License-Identifier: LGPL-2.1 */ -/* - * - * Copyright (c) International Business Machines Corp., 2007 - * Author(s): Steve French (sfrench@us.ibm.com) - * - */ - -#ifndef _CIFSACL_H -#define _CIFSACL_H - -#define NUM_AUTHS (6) /* number of authority fields */ -#define SID_MAX_SUB_AUTHORITIES (15) /* max number of sub authority fields */ - -#define READ_BIT 0x4 -#define WRITE_BIT 0x2 -#define EXEC_BIT 0x1 - -#define ACL_OWNER_MASK 0700 -#define ACL_GROUP_MASK 0070 -#define ACL_EVERYONE_MASK 0007 - -#define UBITSHIFT 6 -#define GBITSHIFT 3 - -#define ACCESS_ALLOWED 0 -#define ACCESS_DENIED 1 - -#define SIDOWNER 1 -#define SIDGROUP 2 - -/* - * Security Descriptor length containing DACL with 3 ACEs (one each for - * owner, group and world). - */ -#define DEFAULT_SEC_DESC_LEN (sizeof(struct cifs_ntsd) + \ - sizeof(struct cifs_acl) + \ - (sizeof(struct cifs_ace) * 4)) - -/* - * Maximum size of a string representation of a SID: - * - * The fields are unsigned values in decimal. So: - * - * u8: max 3 bytes in decimal - * u32: max 10 bytes in decimal - * - * "S-" + 3 bytes for version field + 15 for authority field + NULL terminator - * - * For authority field, max is when all 6 values are non-zero and it must be - * represented in hex. So "-0x" + 12 hex digits. - * - * Add 11 bytes for each subauthority field (10 bytes each + 1 for '-') - */ -#define SID_STRING_BASE_SIZE (2 + 3 + 15 + 1) -#define SID_STRING_SUBAUTH_SIZE (11) /* size of a single subauth string */ - -struct cifs_ntsd { - __le16 revision; /* revision level */ - __le16 type; - __le32 osidoffset; - __le32 gsidoffset; - __le32 sacloffset; - __le32 dacloffset; -} __attribute__((packed)); - -struct cifs_sid { - __u8 revision; /* revision level */ - __u8 num_subauth; - __u8 authority[NUM_AUTHS]; - __le32 sub_auth[SID_MAX_SUB_AUTHORITIES]; /* sub_auth[num_subauth] */ -} __attribute__((packed)); - -/* size of a struct cifs_sid, sans sub_auth array */ -#define CIFS_SID_BASE_SIZE (1 + 1 + NUM_AUTHS) - -struct cifs_acl { - __le16 revision; /* revision level */ - __le16 size; - __le32 num_aces; -} __attribute__((packed)); - -/* ACE types - see MS-DTYP 2.4.4.1 */ -#define ACCESS_ALLOWED_ACE_TYPE 0x00 -#define ACCESS_DENIED_ACE_TYPE 0x01 -#define SYSTEM_AUDIT_ACE_TYPE 0x02 -#define SYSTEM_ALARM_ACE_TYPE 0x03 -#define ACCESS_ALLOWED_COMPOUND_ACE_TYPE 0x04 -#define ACCESS_ALLOWED_OBJECT_ACE_TYPE 0x05 -#define ACCESS_DENIED_OBJECT_ACE_TYPE 0x06 -#define SYSTEM_AUDIT_OBJECT_ACE_TYPE 0x07 -#define SYSTEM_ALARM_OBJECT_ACE_TYPE 0x08 -#define ACCESS_ALLOWED_CALLBACK_ACE_TYPE 0x09 -#define ACCESS_DENIED_CALLBACK_ACE_TYPE 0x0A -#define ACCESS_ALLOWED_CALLBACK_OBJECT_ACE_TYPE 0x0B -#define ACCESS_DENIED_CALLBACK_OBJECT_ACE_TYPE 0x0C -#define SYSTEM_AUDIT_CALLBACK_ACE_TYPE 0x0D -#define SYSTEM_ALARM_CALLBACK_ACE_TYPE 0x0E /* Reserved */ -#define SYSTEM_AUDIT_CALLBACK_OBJECT_ACE_TYPE 0x0F -#define SYSTEM_ALARM_CALLBACK_OBJECT_ACE_TYPE 0x10 /* reserved */ -#define SYSTEM_MANDATORY_LABEL_ACE_TYPE 0x11 -#define SYSTEM_RESOURCE_ATTRIBUTE_ACE_TYPE 0x12 -#define SYSTEM_SCOPED_POLICY_ID_ACE_TYPE 0x13 - -/* ACE flags */ -#define OBJECT_INHERIT_ACE 0x01 -#define CONTAINER_INHERIT_ACE 0x02 -#define NO_PROPAGATE_INHERIT_ACE 0x04 -#define INHERIT_ONLY_ACE 0x08 -#define INHERITED_ACE 0x10 -#define SUCCESSFUL_ACCESS_ACE_FLAG 0x40 -#define FAILED_ACCESS_ACE_FLAG 0x80 - -struct cifs_ace { - __u8 type; /* see above and MS-DTYP 2.4.4.1 */ - __u8 flags; - __le16 size; - __le32 access_req; - struct cifs_sid sid; /* ie UUID of user or group who gets these perms */ -} __attribute__((packed)); - -/* - * The current SMB3 form of security descriptor is similar to what was used for - * cifs (see above) but some fields are split, and fields in the struct below - * matches names of fields to the spec, MS-DTYP (see sections 2.4.5 and - * 2.4.6). Note that "CamelCase" fields are used in this struct in order to - * match the MS-DTYP and MS-SMB2 specs which define the wire format. - */ -struct smb3_sd { - __u8 Revision; /* revision level, MUST be one */ - __u8 Sbz1; /* only meaningful if 'RM' flag set below */ - __le16 Control; - __le32 OffsetOwner; - __le32 OffsetGroup; - __le32 OffsetSacl; - __le32 OffsetDacl; -} __packed; - -/* Meaning of 'Control' field flags */ -#define ACL_CONTROL_SR 0x8000 /* Self relative */ -#define ACL_CONTROL_RM 0x4000 /* Resource manager control bits */ -#define ACL_CONTROL_PS 0x2000 /* SACL protected from inherits */ -#define ACL_CONTROL_PD 0x1000 /* DACL protected from inherits */ -#define ACL_CONTROL_SI 0x0800 /* SACL Auto-Inherited */ -#define ACL_CONTROL_DI 0x0400 /* DACL Auto-Inherited */ -#define ACL_CONTROL_SC 0x0200 /* SACL computed through inheritance */ -#define ACL_CONTROL_DC 0x0100 /* DACL computed through inheritence */ -#define ACL_CONTROL_SS 0x0080 /* Create server ACL */ -#define ACL_CONTROL_DT 0x0040 /* DACL provided by trusted source */ -#define ACL_CONTROL_SD 0x0020 /* SACL defaulted */ -#define ACL_CONTROL_SP 0x0010 /* SACL is present on object */ -#define ACL_CONTROL_DD 0x0008 /* DACL defaulted */ -#define ACL_CONTROL_DP 0x0004 /* DACL is present on object */ -#define ACL_CONTROL_GD 0x0002 /* Group was defaulted */ -#define ACL_CONTROL_OD 0x0001 /* User was defaulted */ - -/* Meaning of AclRevision flags */ -#define ACL_REVISION 0x02 /* See section 2.4.4.1 of MS-DTYP */ -#define ACL_REVISION_DS 0x04 /* Additional AceTypes allowed */ - -struct smb3_acl { - u8 AclRevision; /* revision level */ - u8 Sbz1; /* MBZ */ - __le16 AclSize; - __le16 AceCount; - __le16 Sbz2; /* MBZ */ -} __packed; - -/* - * Used to store the special 'NFS SIDs' used to persist the POSIX uid and gid - * See http://technet.microsoft.com/en-us/library/hh509017(v=ws.10).aspx - */ -struct owner_sid { - u8 Revision; - u8 NumAuth; - u8 Authority[6]; - __le32 SubAuthorities[3]; -} __packed; - -struct owner_group_sids { - struct owner_sid owner; - struct owner_sid group; -} __packed; - -/* - * Minimum security identifier can be one for system defined Users - * and Groups such as NULL SID and World or Built-in accounts such - * as Administrator and Guest and consists of - * Revision + Num (Sub)Auths + Authority + Domain (one Subauthority) - */ -#define MIN_SID_LEN (1 + 1 + 6 + 4) /* in bytes */ - -/* - * Minimum security descriptor can be one without any SACL and DACL and can - * consist of revision, type, and two sids of minimum size for owner and group - */ -#define MIN_SEC_DESC_LEN (sizeof(struct cifs_ntsd) + (2 * MIN_SID_LEN)) - -#endif /* _CIFSACL_H */ diff --git a/fs/cifs/cifsencrypt.c b/fs/cifs/cifsencrypt.c deleted file mode 100644 index cbc18b4a9cb20..0000000000000 --- a/fs/cifs/cifsencrypt.c +++ /dev/null @@ -1,733 +0,0 @@ -// SPDX-License-Identifier: LGPL-2.1 -/* - * - * Encryption and hashing operations relating to NTLM, NTLMv2. See MS-NLMP - * for more detailed information - * - * Copyright (C) International Business Machines Corp., 2005,2013 - * Author(s): Steve French (sfrench@us.ibm.com) - * - */ - -#include -#include -#include "cifspdu.h" -#include "cifsglob.h" -#include "cifs_debug.h" -#include "cifs_unicode.h" -#include "cifsproto.h" -#include "ntlmssp.h" -#include -#include -#include -#include -#include "../smbfs_common/arc4.h" -#include - -int __cifs_calc_signature(struct smb_rqst *rqst, - struct TCP_Server_Info *server, char *signature, - struct shash_desc *shash) -{ - int i; - int rc; - struct kvec *iov = rqst->rq_iov; - int n_vec = rqst->rq_nvec; - - /* iov[0] is actual data and not the rfc1002 length for SMB2+ */ - if (!is_smb1(server)) { - if (iov[0].iov_len <= 4) - return -EIO; - i = 0; - } else { - if (n_vec < 2 || iov[0].iov_len != 4) - return -EIO; - i = 1; /* skip rfc1002 length */ - } - - for (; i < n_vec; i++) { - if (iov[i].iov_len == 0) - continue; - if (iov[i].iov_base == NULL) { - cifs_dbg(VFS, "null iovec entry\n"); - return -EIO; - } - - rc = crypto_shash_update(shash, - iov[i].iov_base, iov[i].iov_len); - if (rc) { - cifs_dbg(VFS, "%s: Could not update with payload\n", - __func__); - return rc; - } - } - - /* now hash over the rq_pages array */ - for (i = 0; i < rqst->rq_npages; i++) { - void *kaddr; - unsigned int len, offset; - - rqst_page_get_length(rqst, i, &len, &offset); - - kaddr = (char *) kmap(rqst->rq_pages[i]) + offset; - - rc = crypto_shash_update(shash, kaddr, len); - if (rc) { - cifs_dbg(VFS, "%s: Could not update with payload\n", - __func__); - kunmap(rqst->rq_pages[i]); - return rc; - } - - kunmap(rqst->rq_pages[i]); - } - - rc = crypto_shash_final(shash, signature); - if (rc) - cifs_dbg(VFS, "%s: Could not generate hash\n", __func__); - - return rc; -} - -/* - * Calculate and return the CIFS signature based on the mac key and SMB PDU. - * The 16 byte signature must be allocated by the caller. Note we only use the - * 1st eight bytes and that the smb header signature field on input contains - * the sequence number before this function is called. Also, this function - * should be called with the server->srv_mutex held. - */ -static int cifs_calc_signature(struct smb_rqst *rqst, - struct TCP_Server_Info *server, char *signature) -{ - int rc; - - if (!rqst->rq_iov || !signature || !server) - return -EINVAL; - - rc = cifs_alloc_hash("md5", &server->secmech.md5); - if (rc) - return -1; - - rc = crypto_shash_init(server->secmech.md5); - if (rc) { - cifs_dbg(VFS, "%s: Could not init md5\n", __func__); - return rc; - } - - rc = crypto_shash_update(server->secmech.md5, - server->session_key.response, server->session_key.len); - if (rc) { - cifs_dbg(VFS, "%s: Could not update with response\n", __func__); - return rc; - } - - return __cifs_calc_signature(rqst, server, signature, server->secmech.md5); -} - -/* must be called with server->srv_mutex held */ -int cifs_sign_rqst(struct smb_rqst *rqst, struct TCP_Server_Info *server, - __u32 *pexpected_response_sequence_number) -{ - int rc = 0; - char smb_signature[20]; - struct smb_hdr *cifs_pdu = (struct smb_hdr *)rqst->rq_iov[0].iov_base; - - if (rqst->rq_iov[0].iov_len != 4 || - rqst->rq_iov[0].iov_base + 4 != rqst->rq_iov[1].iov_base) - return -EIO; - - if ((cifs_pdu == NULL) || (server == NULL)) - return -EINVAL; - - spin_lock(&server->srv_lock); - if (!(cifs_pdu->Flags2 & SMBFLG2_SECURITY_SIGNATURE) || - server->tcpStatus == CifsNeedNegotiate) { - spin_unlock(&server->srv_lock); - return rc; - } - spin_unlock(&server->srv_lock); - - if (!server->session_estab) { - memcpy(cifs_pdu->Signature.SecuritySignature, "BSRSPYL", 8); - return rc; - } - - cifs_pdu->Signature.Sequence.SequenceNumber = - cpu_to_le32(server->sequence_number); - cifs_pdu->Signature.Sequence.Reserved = 0; - - *pexpected_response_sequence_number = ++server->sequence_number; - ++server->sequence_number; - - rc = cifs_calc_signature(rqst, server, smb_signature); - if (rc) - memset(cifs_pdu->Signature.SecuritySignature, 0, 8); - else - memcpy(cifs_pdu->Signature.SecuritySignature, smb_signature, 8); - - return rc; -} - -int cifs_sign_smbv(struct kvec *iov, int n_vec, struct TCP_Server_Info *server, - __u32 *pexpected_response_sequence) -{ - struct smb_rqst rqst = { .rq_iov = iov, - .rq_nvec = n_vec }; - - return cifs_sign_rqst(&rqst, server, pexpected_response_sequence); -} - -/* must be called with server->srv_mutex held */ -int cifs_sign_smb(struct smb_hdr *cifs_pdu, struct TCP_Server_Info *server, - __u32 *pexpected_response_sequence_number) -{ - struct kvec iov[2]; - - iov[0].iov_base = cifs_pdu; - iov[0].iov_len = 4; - iov[1].iov_base = (char *)cifs_pdu + 4; - iov[1].iov_len = be32_to_cpu(cifs_pdu->smb_buf_length); - - return cifs_sign_smbv(iov, 2, server, - pexpected_response_sequence_number); -} - -int cifs_verify_signature(struct smb_rqst *rqst, - struct TCP_Server_Info *server, - __u32 expected_sequence_number) -{ - unsigned int rc; - char server_response_sig[8]; - char what_we_think_sig_should_be[20]; - struct smb_hdr *cifs_pdu = (struct smb_hdr *)rqst->rq_iov[0].iov_base; - - if (rqst->rq_iov[0].iov_len != 4 || - rqst->rq_iov[0].iov_base + 4 != rqst->rq_iov[1].iov_base) - return -EIO; - - if (cifs_pdu == NULL || server == NULL) - return -EINVAL; - - if (!server->session_estab) - return 0; - - if (cifs_pdu->Command == SMB_COM_LOCKING_ANDX) { - struct smb_com_lock_req *pSMB = - (struct smb_com_lock_req *)cifs_pdu; - if (pSMB->LockType & LOCKING_ANDX_OPLOCK_RELEASE) - return 0; - } - - /* BB what if signatures are supposed to be on for session but - server does not send one? BB */ - - /* Do not need to verify session setups with signature "BSRSPYL " */ - if (memcmp(cifs_pdu->Signature.SecuritySignature, "BSRSPYL ", 8) == 0) - cifs_dbg(FYI, "dummy signature received for smb command 0x%x\n", - cifs_pdu->Command); - - /* save off the origiginal signature so we can modify the smb and check - its signature against what the server sent */ - memcpy(server_response_sig, cifs_pdu->Signature.SecuritySignature, 8); - - cifs_pdu->Signature.Sequence.SequenceNumber = - cpu_to_le32(expected_sequence_number); - cifs_pdu->Signature.Sequence.Reserved = 0; - - cifs_server_lock(server); - rc = cifs_calc_signature(rqst, server, what_we_think_sig_should_be); - cifs_server_unlock(server); - - if (rc) - return rc; - -/* cifs_dump_mem("what we think it should be: ", - what_we_think_sig_should_be, 16); */ - - if (memcmp(server_response_sig, what_we_think_sig_should_be, 8)) - return -EACCES; - else - return 0; - -} - -/* Build a proper attribute value/target info pairs blob. - * Fill in netbios and dns domain name and workstation name - * and client time (total five av pairs and + one end of fields indicator. - * Allocate domain name which gets freed when session struct is deallocated. - */ -static int -build_avpair_blob(struct cifs_ses *ses, const struct nls_table *nls_cp) -{ - unsigned int dlen; - unsigned int size = 2 * sizeof(struct ntlmssp2_name); - char *defdmname = "WORKGROUP"; - unsigned char *blobptr; - struct ntlmssp2_name *attrptr; - - if (!ses->domainName) { - ses->domainName = kstrdup(defdmname, GFP_KERNEL); - if (!ses->domainName) - return -ENOMEM; - } - - dlen = strlen(ses->domainName); - - /* - * The length of this blob is two times the size of a - * structure (av pair) which holds name/size - * ( for NTLMSSP_AV_NB_DOMAIN_NAME followed by NTLMSSP_AV_EOL ) + - * unicode length of a netbios domain name - */ - kfree_sensitive(ses->auth_key.response); - ses->auth_key.len = size + 2 * dlen; - ses->auth_key.response = kzalloc(ses->auth_key.len, GFP_KERNEL); - if (!ses->auth_key.response) { - ses->auth_key.len = 0; - return -ENOMEM; - } - - blobptr = ses->auth_key.response; - attrptr = (struct ntlmssp2_name *) blobptr; - - /* - * As defined in MS-NTLM 3.3.2, just this av pair field - * is sufficient as part of the temp - */ - attrptr->type = cpu_to_le16(NTLMSSP_AV_NB_DOMAIN_NAME); - attrptr->length = cpu_to_le16(2 * dlen); - blobptr = (unsigned char *)attrptr + sizeof(struct ntlmssp2_name); - cifs_strtoUTF16((__le16 *)blobptr, ses->domainName, dlen, nls_cp); - - return 0; -} - -/* Server has provided av pairs/target info in the type 2 challenge - * packet and we have plucked it and stored within smb session. - * We parse that blob here to find netbios domain name to be used - * as part of ntlmv2 authentication (in Target String), if not already - * specified on the command line. - * If this function returns without any error but without fetching - * domain name, authentication may fail against some server but - * may not fail against other (those who are not very particular - * about target string i.e. for some, just user name might suffice. - */ -static int -find_domain_name(struct cifs_ses *ses, const struct nls_table *nls_cp) -{ - unsigned int attrsize; - unsigned int type; - unsigned int onesize = sizeof(struct ntlmssp2_name); - unsigned char *blobptr; - unsigned char *blobend; - struct ntlmssp2_name *attrptr; - - if (!ses->auth_key.len || !ses->auth_key.response) - return 0; - - blobptr = ses->auth_key.response; - blobend = blobptr + ses->auth_key.len; - - while (blobptr + onesize < blobend) { - attrptr = (struct ntlmssp2_name *) blobptr; - type = le16_to_cpu(attrptr->type); - if (type == NTLMSSP_AV_EOL) - break; - blobptr += 2; /* advance attr type */ - attrsize = le16_to_cpu(attrptr->length); - blobptr += 2; /* advance attr size */ - if (blobptr + attrsize > blobend) - break; - if (type == NTLMSSP_AV_NB_DOMAIN_NAME) { - if (!attrsize || attrsize >= CIFS_MAX_DOMAINNAME_LEN) - break; - if (!ses->domainName) { - ses->domainName = - kmalloc(attrsize + 1, GFP_KERNEL); - if (!ses->domainName) - return -ENOMEM; - cifs_from_utf16(ses->domainName, - (__le16 *)blobptr, attrsize, attrsize, - nls_cp, NO_MAP_UNI_RSVD); - break; - } - } - blobptr += attrsize; /* advance attr value */ - } - - return 0; -} - -/* Server has provided av pairs/target info in the type 2 challenge - * packet and we have plucked it and stored within smb session. - * We parse that blob here to find the server given timestamp - * as part of ntlmv2 authentication (or local current time as - * default in case of failure) - */ -static __le64 -find_timestamp(struct cifs_ses *ses) -{ - unsigned int attrsize; - unsigned int type; - unsigned int onesize = sizeof(struct ntlmssp2_name); - unsigned char *blobptr; - unsigned char *blobend; - struct ntlmssp2_name *attrptr; - struct timespec64 ts; - - if (!ses->auth_key.len || !ses->auth_key.response) - return 0; - - blobptr = ses->auth_key.response; - blobend = blobptr + ses->auth_key.len; - - while (blobptr + onesize < blobend) { - attrptr = (struct ntlmssp2_name *) blobptr; - type = le16_to_cpu(attrptr->type); - if (type == NTLMSSP_AV_EOL) - break; - blobptr += 2; /* advance attr type */ - attrsize = le16_to_cpu(attrptr->length); - blobptr += 2; /* advance attr size */ - if (blobptr + attrsize > blobend) - break; - if (type == NTLMSSP_AV_TIMESTAMP) { - if (attrsize == sizeof(u64)) - return *((__le64 *)blobptr); - } - blobptr += attrsize; /* advance attr value */ - } - - ktime_get_real_ts64(&ts); - return cpu_to_le64(cifs_UnixTimeToNT(ts)); -} - -static int calc_ntlmv2_hash(struct cifs_ses *ses, char *ntlmv2_hash, - const struct nls_table *nls_cp) -{ - int rc = 0; - int len; - char nt_hash[CIFS_NTHASH_SIZE]; - __le16 *user; - wchar_t *domain; - wchar_t *server; - - if (!ses->server->secmech.hmacmd5) { - cifs_dbg(VFS, "%s: can't generate ntlmv2 hash\n", __func__); - return -1; - } - - /* calculate md4 hash of password */ - E_md4hash(ses->password, nt_hash, nls_cp); - - rc = crypto_shash_setkey(ses->server->secmech.hmacmd5->tfm, nt_hash, - CIFS_NTHASH_SIZE); - if (rc) { - cifs_dbg(VFS, "%s: Could not set NT Hash as a key\n", __func__); - return rc; - } - - rc = crypto_shash_init(ses->server->secmech.hmacmd5); - if (rc) { - cifs_dbg(VFS, "%s: Could not init hmacmd5\n", __func__); - return rc; - } - - /* convert ses->user_name to unicode */ - len = ses->user_name ? strlen(ses->user_name) : 0; - user = kmalloc(2 + (len * 2), GFP_KERNEL); - if (user == NULL) { - rc = -ENOMEM; - return rc; - } - - if (len) { - len = cifs_strtoUTF16(user, ses->user_name, len, nls_cp); - UniStrupr(user); - } else { - memset(user, '\0', 2); - } - - rc = crypto_shash_update(ses->server->secmech.hmacmd5, - (char *)user, 2 * len); - kfree(user); - if (rc) { - cifs_dbg(VFS, "%s: Could not update with user\n", __func__); - return rc; - } - - /* convert ses->domainName to unicode and uppercase */ - if (ses->domainName) { - len = strlen(ses->domainName); - - domain = kmalloc(2 + (len * 2), GFP_KERNEL); - if (domain == NULL) { - rc = -ENOMEM; - return rc; - } - len = cifs_strtoUTF16((__le16 *)domain, ses->domainName, len, - nls_cp); - rc = - crypto_shash_update(ses->server->secmech.hmacmd5, - (char *)domain, 2 * len); - kfree(domain); - if (rc) { - cifs_dbg(VFS, "%s: Could not update with domain\n", - __func__); - return rc; - } - } else { - /* We use ses->ip_addr if no domain name available */ - len = strlen(ses->ip_addr); - - server = kmalloc(2 + (len * 2), GFP_KERNEL); - if (server == NULL) { - rc = -ENOMEM; - return rc; - } - len = cifs_strtoUTF16((__le16 *)server, ses->ip_addr, len, - nls_cp); - rc = - crypto_shash_update(ses->server->secmech.hmacmd5, - (char *)server, 2 * len); - kfree(server); - if (rc) { - cifs_dbg(VFS, "%s: Could not update with server\n", - __func__); - return rc; - } - } - - rc = crypto_shash_final(ses->server->secmech.hmacmd5, - ntlmv2_hash); - if (rc) - cifs_dbg(VFS, "%s: Could not generate md5 hash\n", __func__); - - return rc; -} - -static int -CalcNTLMv2_response(const struct cifs_ses *ses, char *ntlmv2_hash) -{ - int rc; - struct ntlmv2_resp *ntlmv2 = (struct ntlmv2_resp *) - (ses->auth_key.response + CIFS_SESS_KEY_SIZE); - unsigned int hash_len; - - /* The MD5 hash starts at challenge_key.key */ - hash_len = ses->auth_key.len - (CIFS_SESS_KEY_SIZE + - offsetof(struct ntlmv2_resp, challenge.key[0])); - - if (!ses->server->secmech.hmacmd5) { - cifs_dbg(VFS, "%s: can't generate ntlmv2 hash\n", __func__); - return -1; - } - - rc = crypto_shash_setkey(ses->server->secmech.hmacmd5->tfm, - ntlmv2_hash, CIFS_HMAC_MD5_HASH_SIZE); - if (rc) { - cifs_dbg(VFS, "%s: Could not set NTLMV2 Hash as a key\n", - __func__); - return rc; - } - - rc = crypto_shash_init(ses->server->secmech.hmacmd5); - if (rc) { - cifs_dbg(VFS, "%s: Could not init hmacmd5\n", __func__); - return rc; - } - - if (ses->server->negflavor == CIFS_NEGFLAVOR_EXTENDED) - memcpy(ntlmv2->challenge.key, - ses->ntlmssp->cryptkey, CIFS_SERVER_CHALLENGE_SIZE); - else - memcpy(ntlmv2->challenge.key, - ses->server->cryptkey, CIFS_SERVER_CHALLENGE_SIZE); - rc = crypto_shash_update(ses->server->secmech.hmacmd5, - ntlmv2->challenge.key, hash_len); - if (rc) { - cifs_dbg(VFS, "%s: Could not update with response\n", __func__); - return rc; - } - - /* Note that the MD5 digest over writes anon.challenge_key.key */ - rc = crypto_shash_final(ses->server->secmech.hmacmd5, - ntlmv2->ntlmv2_hash); - if (rc) - cifs_dbg(VFS, "%s: Could not generate md5 hash\n", __func__); - - return rc; -} - -int -setup_ntlmv2_rsp(struct cifs_ses *ses, const struct nls_table *nls_cp) -{ - int rc; - int baselen; - unsigned int tilen; - struct ntlmv2_resp *ntlmv2; - char ntlmv2_hash[16]; - unsigned char *tiblob = NULL; /* target info blob */ - __le64 rsp_timestamp; - - if (nls_cp == NULL) { - cifs_dbg(VFS, "%s called with nls_cp==NULL\n", __func__); - return -EINVAL; - } - - if (ses->server->negflavor == CIFS_NEGFLAVOR_EXTENDED) { - if (!ses->domainName) { - if (ses->domainAuto) { - rc = find_domain_name(ses, nls_cp); - if (rc) { - cifs_dbg(VFS, "error %d finding domain name\n", - rc); - goto setup_ntlmv2_rsp_ret; - } - } else { - ses->domainName = kstrdup("", GFP_KERNEL); - } - } - } else { - rc = build_avpair_blob(ses, nls_cp); - if (rc) { - cifs_dbg(VFS, "error %d building av pair blob\n", rc); - goto setup_ntlmv2_rsp_ret; - } - } - - /* Must be within 5 minutes of the server (or in range +/-2h - * in case of Mac OS X), so simply carry over server timestamp - * (as Windows 7 does) - */ - rsp_timestamp = find_timestamp(ses); - - baselen = CIFS_SESS_KEY_SIZE + sizeof(struct ntlmv2_resp); - tilen = ses->auth_key.len; - tiblob = ses->auth_key.response; - - ses->auth_key.response = kmalloc(baselen + tilen, GFP_KERNEL); - if (!ses->auth_key.response) { - rc = -ENOMEM; - ses->auth_key.len = 0; - goto setup_ntlmv2_rsp_ret; - } - ses->auth_key.len += baselen; - - ntlmv2 = (struct ntlmv2_resp *) - (ses->auth_key.response + CIFS_SESS_KEY_SIZE); - ntlmv2->blob_signature = cpu_to_le32(0x00000101); - ntlmv2->reserved = 0; - ntlmv2->time = rsp_timestamp; - - get_random_bytes(&ntlmv2->client_chal, sizeof(ntlmv2->client_chal)); - ntlmv2->reserved2 = 0; - - memcpy(ses->auth_key.response + baselen, tiblob, tilen); - - cifs_server_lock(ses->server); - - rc = cifs_alloc_hash("hmac(md5)", &ses->server->secmech.hmacmd5); - if (rc) { - goto unlock; - } - - /* calculate ntlmv2_hash */ - rc = calc_ntlmv2_hash(ses, ntlmv2_hash, nls_cp); - if (rc) { - cifs_dbg(VFS, "Could not get v2 hash rc %d\n", rc); - goto unlock; - } - - /* calculate first part of the client response (CR1) */ - rc = CalcNTLMv2_response(ses, ntlmv2_hash); - if (rc) { - cifs_dbg(VFS, "Could not calculate CR1 rc: %d\n", rc); - goto unlock; - } - - /* now calculate the session key for NTLMv2 */ - rc = crypto_shash_setkey(ses->server->secmech.hmacmd5->tfm, - ntlmv2_hash, CIFS_HMAC_MD5_HASH_SIZE); - if (rc) { - cifs_dbg(VFS, "%s: Could not set NTLMV2 Hash as a key\n", - __func__); - goto unlock; - } - - rc = crypto_shash_init(ses->server->secmech.hmacmd5); - if (rc) { - cifs_dbg(VFS, "%s: Could not init hmacmd5\n", __func__); - goto unlock; - } - - rc = crypto_shash_update(ses->server->secmech.hmacmd5, - ntlmv2->ntlmv2_hash, - CIFS_HMAC_MD5_HASH_SIZE); - if (rc) { - cifs_dbg(VFS, "%s: Could not update with response\n", __func__); - goto unlock; - } - - rc = crypto_shash_final(ses->server->secmech.hmacmd5, - ses->auth_key.response); - if (rc) - cifs_dbg(VFS, "%s: Could not generate md5 hash\n", __func__); - -unlock: - cifs_server_unlock(ses->server); -setup_ntlmv2_rsp_ret: - kfree_sensitive(tiblob); - - return rc; -} - -int -calc_seckey(struct cifs_ses *ses) -{ - unsigned char sec_key[CIFS_SESS_KEY_SIZE]; /* a nonce */ - struct arc4_ctx *ctx_arc4; - - if (fips_enabled) - return -ENODEV; - - get_random_bytes(sec_key, CIFS_SESS_KEY_SIZE); - - ctx_arc4 = kmalloc(sizeof(*ctx_arc4), GFP_KERNEL); - if (!ctx_arc4) { - cifs_dbg(VFS, "Could not allocate arc4 context\n"); - return -ENOMEM; - } - - cifs_arc4_setkey(ctx_arc4, ses->auth_key.response, CIFS_SESS_KEY_SIZE); - cifs_arc4_crypt(ctx_arc4, ses->ntlmssp->ciphertext, sec_key, - CIFS_CPHTXT_SIZE); - - /* make secondary_key/nonce as session key */ - memcpy(ses->auth_key.response, sec_key, CIFS_SESS_KEY_SIZE); - /* and make len as that of session key only */ - ses->auth_key.len = CIFS_SESS_KEY_SIZE; - - memzero_explicit(sec_key, CIFS_SESS_KEY_SIZE); - kfree_sensitive(ctx_arc4); - return 0; -} - -void -cifs_crypto_secmech_release(struct TCP_Server_Info *server) -{ - cifs_free_hash(&server->secmech.aes_cmac); - cifs_free_hash(&server->secmech.hmacsha256); - cifs_free_hash(&server->secmech.md5); - cifs_free_hash(&server->secmech.sha512); - cifs_free_hash(&server->secmech.hmacmd5); - - if (server->secmech.enc) { - crypto_free_aead(server->secmech.enc); - server->secmech.enc = NULL; - } - - if (server->secmech.dec) { - crypto_free_aead(server->secmech.dec); - server->secmech.dec = NULL; - } -} diff --git a/fs/cifs/cifsfs.c b/fs/cifs/cifsfs.c deleted file mode 100644 index 078df1e2dd18a..0000000000000 --- a/fs/cifs/cifsfs.c +++ /dev/null @@ -1,1857 +0,0 @@ -// SPDX-License-Identifier: LGPL-2.1 -/* - * - * Copyright (C) International Business Machines Corp., 2002,2008 - * Author(s): Steve French (sfrench@us.ibm.com) - * - * Common Internet FileSystem (CIFS) client - * - */ - -/* Note that BB means BUGBUG (ie something to fix eventually) */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include "cifsfs.h" -#include "cifspdu.h" -#define DECLARE_GLOBALS_HERE -#include "cifsglob.h" -#include "cifsproto.h" -#include "cifs_debug.h" -#include "cifs_fs_sb.h" -#include -#include -#include "cifs_spnego.h" -#include "fscache.h" -#ifdef CONFIG_CIFS_DFS_UPCALL -#include "dfs_cache.h" -#endif -#ifdef CONFIG_CIFS_SWN_UPCALL -#include "netlink.h" -#endif -#include "fs_context.h" -#include "cached_dir.h" - -/* - * DOS dates from 1980/1/1 through 2107/12/31 - * Protocol specifications indicate the range should be to 119, which - * limits maximum year to 2099. But this range has not been checked. - */ -#define SMB_DATE_MAX (127<<9 | 12<<5 | 31) -#define SMB_DATE_MIN (0<<9 | 1<<5 | 1) -#define SMB_TIME_MAX (23<<11 | 59<<5 | 29) - -int cifsFYI = 0; -bool traceSMB; -bool enable_oplocks = true; -bool linuxExtEnabled = true; -bool lookupCacheEnabled = true; -bool disable_legacy_dialects; /* false by default */ -bool enable_gcm_256 = true; -bool require_gcm_256; /* false by default */ -bool enable_negotiate_signing; /* false by default */ -unsigned int global_secflags = CIFSSEC_DEF; -/* unsigned int ntlmv2_support = 0; */ -unsigned int sign_CIFS_PDUs = 1; - -/* - * Global transaction id (XID) information - */ -unsigned int GlobalCurrentXid; /* protected by GlobalMid_Sem */ -unsigned int GlobalTotalActiveXid; /* prot by GlobalMid_Sem */ -unsigned int GlobalMaxActiveXid; /* prot by GlobalMid_Sem */ -spinlock_t GlobalMid_Lock; /* protects above & list operations on midQ entries */ - -/* - * Global counters, updated atomically - */ -atomic_t sesInfoAllocCount; -atomic_t tconInfoAllocCount; -atomic_t tcpSesNextId; -atomic_t tcpSesAllocCount; -atomic_t tcpSesReconnectCount; -atomic_t tconInfoReconnectCount; - -atomic_t mid_count; -atomic_t buf_alloc_count; -atomic_t small_buf_alloc_count; -#ifdef CONFIG_CIFS_STATS2 -atomic_t total_buf_alloc_count; -atomic_t total_small_buf_alloc_count; -#endif/* STATS2 */ -struct list_head cifs_tcp_ses_list; -spinlock_t cifs_tcp_ses_lock; -static const struct super_operations cifs_super_ops; -unsigned int CIFSMaxBufSize = CIFS_MAX_MSGSIZE; -module_param(CIFSMaxBufSize, uint, 0444); -MODULE_PARM_DESC(CIFSMaxBufSize, "Network buffer size (not including header) " - "for CIFS requests. " - "Default: 16384 Range: 8192 to 130048"); -unsigned int cifs_min_rcv = CIFS_MIN_RCV_POOL; -module_param(cifs_min_rcv, uint, 0444); -MODULE_PARM_DESC(cifs_min_rcv, "Network buffers in pool. Default: 4 Range: " - "1 to 64"); -unsigned int cifs_min_small = 30; -module_param(cifs_min_small, uint, 0444); -MODULE_PARM_DESC(cifs_min_small, "Small network buffers in pool. Default: 30 " - "Range: 2 to 256"); -unsigned int cifs_max_pending = CIFS_MAX_REQ; -module_param(cifs_max_pending, uint, 0444); -MODULE_PARM_DESC(cifs_max_pending, "Simultaneous requests to server for " - "CIFS/SMB1 dialect (N/A for SMB3) " - "Default: 32767 Range: 2 to 32767."); -#ifdef CONFIG_CIFS_STATS2 -unsigned int slow_rsp_threshold = 1; -module_param(slow_rsp_threshold, uint, 0644); -MODULE_PARM_DESC(slow_rsp_threshold, "Amount of time (in seconds) to wait " - "before logging that a response is delayed. " - "Default: 1 (if set to 0 disables msg)."); -#endif /* STATS2 */ - -module_param(enable_oplocks, bool, 0644); -MODULE_PARM_DESC(enable_oplocks, "Enable or disable oplocks. Default: y/Y/1"); - -module_param(enable_gcm_256, bool, 0644); -MODULE_PARM_DESC(enable_gcm_256, "Enable requesting strongest (256 bit) GCM encryption. Default: n/N/0"); - -module_param(require_gcm_256, bool, 0644); -MODULE_PARM_DESC(require_gcm_256, "Require strongest (256 bit) GCM encryption. Default: n/N/0"); - -module_param(enable_negotiate_signing, bool, 0644); -MODULE_PARM_DESC(enable_negotiate_signing, "Enable negotiating packet signing algorithm with server. Default: n/N/0"); - -module_param(disable_legacy_dialects, bool, 0644); -MODULE_PARM_DESC(disable_legacy_dialects, "To improve security it may be " - "helpful to restrict the ability to " - "override the default dialects (SMB2.1, " - "SMB3 and SMB3.02) on mount with old " - "dialects (CIFS/SMB1 and SMB2) since " - "vers=1.0 (CIFS/SMB1) and vers=2.0 are weaker" - " and less secure. Default: n/N/0"); - -extern mempool_t *cifs_sm_req_poolp; -extern mempool_t *cifs_req_poolp; -extern mempool_t *cifs_mid_poolp; - -struct workqueue_struct *cifsiod_wq; -struct workqueue_struct *decrypt_wq; -struct workqueue_struct *fileinfo_put_wq; -struct workqueue_struct *cifsoplockd_wq; -struct workqueue_struct *deferredclose_wq; -__u32 cifs_lock_secret; - -/* - * Bumps refcount for cifs super block. - * Note that it should be only called if a referece to VFS super block is - * already held, e.g. in open-type syscalls context. Otherwise it can race with - * atomic_dec_and_test in deactivate_locked_super. - */ -void -cifs_sb_active(struct super_block *sb) -{ - struct cifs_sb_info *server = CIFS_SB(sb); - - if (atomic_inc_return(&server->active) == 1) - atomic_inc(&sb->s_active); -} - -void -cifs_sb_deactive(struct super_block *sb) -{ - struct cifs_sb_info *server = CIFS_SB(sb); - - if (atomic_dec_and_test(&server->active)) - deactivate_super(sb); -} - -static int -cifs_read_super(struct super_block *sb) -{ - struct inode *inode; - struct cifs_sb_info *cifs_sb; - struct cifs_tcon *tcon; - struct timespec64 ts; - int rc = 0; - - cifs_sb = CIFS_SB(sb); - tcon = cifs_sb_master_tcon(cifs_sb); - - if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_POSIXACL) - sb->s_flags |= SB_POSIXACL; - - if (tcon->snapshot_time) - sb->s_flags |= SB_RDONLY; - - if (tcon->ses->capabilities & tcon->ses->server->vals->cap_large_files) - sb->s_maxbytes = MAX_LFS_FILESIZE; - else - sb->s_maxbytes = MAX_NON_LFS; - - /* - * Some very old servers like DOS and OS/2 used 2 second granularity - * (while all current servers use 100ns granularity - see MS-DTYP) - * but 1 second is the maximum allowed granularity for the VFS - * so for old servers set time granularity to 1 second while for - * everything else (current servers) set it to 100ns. - */ - if ((tcon->ses->server->vals->protocol_id == SMB10_PROT_ID) && - ((tcon->ses->capabilities & - tcon->ses->server->vals->cap_nt_find) == 0) && - !tcon->unix_ext) { - sb->s_time_gran = 1000000000; /* 1 second is max allowed gran */ - ts = cnvrtDosUnixTm(cpu_to_le16(SMB_DATE_MIN), 0, 0); - sb->s_time_min = ts.tv_sec; - ts = cnvrtDosUnixTm(cpu_to_le16(SMB_DATE_MAX), - cpu_to_le16(SMB_TIME_MAX), 0); - sb->s_time_max = ts.tv_sec; - } else { - /* - * Almost every server, including all SMB2+, uses DCE TIME - * ie 100 nanosecond units, since 1601. See MS-DTYP and MS-FSCC - */ - sb->s_time_gran = 100; - ts = cifs_NTtimeToUnix(0); - sb->s_time_min = ts.tv_sec; - ts = cifs_NTtimeToUnix(cpu_to_le64(S64_MAX)); - sb->s_time_max = ts.tv_sec; - } - - sb->s_magic = CIFS_SUPER_MAGIC; - sb->s_op = &cifs_super_ops; - sb->s_xattr = cifs_xattr_handlers; - rc = super_setup_bdi(sb); - if (rc) - goto out_no_root; - /* tune readahead according to rsize if readahead size not set on mount */ - if (cifs_sb->ctx->rsize == 0) - cifs_sb->ctx->rsize = - tcon->ses->server->ops->negotiate_rsize(tcon, cifs_sb->ctx); - if (cifs_sb->ctx->rasize) - sb->s_bdi->ra_pages = cifs_sb->ctx->rasize / PAGE_SIZE; - else - sb->s_bdi->ra_pages = cifs_sb->ctx->rsize / PAGE_SIZE; - - sb->s_blocksize = CIFS_MAX_MSGSIZE; - sb->s_blocksize_bits = 14; /* default 2**14 = CIFS_MAX_MSGSIZE */ - inode = cifs_root_iget(sb); - - if (IS_ERR(inode)) { - rc = PTR_ERR(inode); - goto out_no_root; - } - - if (tcon->nocase) - sb->s_d_op = &cifs_ci_dentry_ops; - else - sb->s_d_op = &cifs_dentry_ops; - - sb->s_root = d_make_root(inode); - if (!sb->s_root) { - rc = -ENOMEM; - goto out_no_root; - } - -#ifdef CONFIG_CIFS_NFSD_EXPORT - if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_SERVER_INUM) { - cifs_dbg(FYI, "export ops supported\n"); - sb->s_export_op = &cifs_export_ops; - } -#endif /* CONFIG_CIFS_NFSD_EXPORT */ - - return 0; - -out_no_root: - cifs_dbg(VFS, "%s: get root inode failed\n", __func__); - return rc; -} - -static void cifs_kill_sb(struct super_block *sb) -{ - struct cifs_sb_info *cifs_sb = CIFS_SB(sb); - - /* - * We ned to release all dentries for the cached directories - * before we kill the sb. - */ - if (cifs_sb->root) { - close_all_cached_dirs(cifs_sb); - - /* finally release root dentry */ - dput(cifs_sb->root); - cifs_sb->root = NULL; - } - - kill_anon_super(sb); - cifs_umount(cifs_sb); -} - -static int -cifs_statfs(struct dentry *dentry, struct kstatfs *buf) -{ - struct super_block *sb = dentry->d_sb; - struct cifs_sb_info *cifs_sb = CIFS_SB(sb); - struct cifs_tcon *tcon = cifs_sb_master_tcon(cifs_sb); - struct TCP_Server_Info *server = tcon->ses->server; - unsigned int xid; - int rc = 0; - - xid = get_xid(); - - if (le32_to_cpu(tcon->fsAttrInfo.MaxPathNameComponentLength) > 0) - buf->f_namelen = - le32_to_cpu(tcon->fsAttrInfo.MaxPathNameComponentLength); - else - buf->f_namelen = PATH_MAX; - - buf->f_fsid.val[0] = tcon->vol_serial_number; - /* are using part of create time for more randomness, see man statfs */ - buf->f_fsid.val[1] = (int)le64_to_cpu(tcon->vol_create_time); - - buf->f_files = 0; /* undefined */ - buf->f_ffree = 0; /* unlimited */ - - if (server->ops->queryfs) - rc = server->ops->queryfs(xid, tcon, cifs_sb, buf); - - free_xid(xid); - return rc; -} - -static long cifs_fallocate(struct file *file, int mode, loff_t off, loff_t len) -{ - struct cifs_sb_info *cifs_sb = CIFS_FILE_SB(file); - struct cifs_tcon *tcon = cifs_sb_master_tcon(cifs_sb); - struct TCP_Server_Info *server = tcon->ses->server; - - if (server->ops->fallocate) - return server->ops->fallocate(file, tcon, mode, off, len); - - return -EOPNOTSUPP; -} - -static int cifs_permission(struct user_namespace *mnt_userns, - struct inode *inode, int mask) -{ - struct cifs_sb_info *cifs_sb; - - cifs_sb = CIFS_SB(inode->i_sb); - - if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_NO_PERM) { - if ((mask & MAY_EXEC) && !execute_ok(inode)) - return -EACCES; - else - return 0; - } else /* file mode might have been restricted at mount time - on the client (above and beyond ACL on servers) for - servers which do not support setting and viewing mode bits, - so allowing client to check permissions is useful */ - return generic_permission(&init_user_ns, inode, mask); -} - -static struct kmem_cache *cifs_inode_cachep; -static struct kmem_cache *cifs_req_cachep; -static struct kmem_cache *cifs_mid_cachep; -static struct kmem_cache *cifs_sm_req_cachep; -mempool_t *cifs_sm_req_poolp; -mempool_t *cifs_req_poolp; -mempool_t *cifs_mid_poolp; - -static struct inode * -cifs_alloc_inode(struct super_block *sb) -{ - struct cifsInodeInfo *cifs_inode; - cifs_inode = alloc_inode_sb(sb, cifs_inode_cachep, GFP_KERNEL); - if (!cifs_inode) - return NULL; - cifs_inode->cifsAttrs = 0x20; /* default */ - cifs_inode->time = 0; - /* - * Until the file is open and we have gotten oplock info back from the - * server, can not assume caching of file data or metadata. - */ - cifs_set_oplock_level(cifs_inode, 0); - cifs_inode->flags = 0; - spin_lock_init(&cifs_inode->writers_lock); - cifs_inode->writers = 0; - cifs_inode->netfs.inode.i_blkbits = 14; /* 2**14 = CIFS_MAX_MSGSIZE */ - cifs_inode->server_eof = 0; - cifs_inode->uniqueid = 0; - cifs_inode->createtime = 0; - cifs_inode->epoch = 0; - spin_lock_init(&cifs_inode->open_file_lock); - generate_random_uuid(cifs_inode->lease_key); - cifs_inode->symlink_target = NULL; - - /* - * Can not set i_flags here - they get immediately overwritten to zero - * by the VFS. - */ - /* cifs_inode->netfs.inode.i_flags = S_NOATIME | S_NOCMTIME; */ - INIT_LIST_HEAD(&cifs_inode->openFileList); - INIT_LIST_HEAD(&cifs_inode->llist); - INIT_LIST_HEAD(&cifs_inode->deferred_closes); - spin_lock_init(&cifs_inode->deferred_lock); - return &cifs_inode->netfs.inode; -} - -static void -cifs_free_inode(struct inode *inode) -{ - struct cifsInodeInfo *cinode = CIFS_I(inode); - - if (S_ISLNK(inode->i_mode)) - kfree(cinode->symlink_target); - kmem_cache_free(cifs_inode_cachep, cinode); -} - -static void -cifs_evict_inode(struct inode *inode) -{ - truncate_inode_pages_final(&inode->i_data); - if (inode->i_state & I_PINNING_FSCACHE_WB) - cifs_fscache_unuse_inode_cookie(inode, true); - cifs_fscache_release_inode_cookie(inode); - clear_inode(inode); -} - -static void -cifs_show_address(struct seq_file *s, struct TCP_Server_Info *server) -{ - struct sockaddr_in *sa = (struct sockaddr_in *) &server->dstaddr; - struct sockaddr_in6 *sa6 = (struct sockaddr_in6 *) &server->dstaddr; - - seq_puts(s, ",addr="); - - switch (server->dstaddr.ss_family) { - case AF_INET: - seq_printf(s, "%pI4", &sa->sin_addr.s_addr); - break; - case AF_INET6: - seq_printf(s, "%pI6", &sa6->sin6_addr.s6_addr); - if (sa6->sin6_scope_id) - seq_printf(s, "%%%u", sa6->sin6_scope_id); - break; - default: - seq_puts(s, "(unknown)"); - } - if (server->rdma) - seq_puts(s, ",rdma"); -} - -static void -cifs_show_security(struct seq_file *s, struct cifs_ses *ses) -{ - if (ses->sectype == Unspecified) { - if (ses->user_name == NULL) - seq_puts(s, ",sec=none"); - return; - } - - seq_puts(s, ",sec="); - - switch (ses->sectype) { - case NTLMv2: - seq_puts(s, "ntlmv2"); - break; - case Kerberos: - seq_puts(s, "krb5"); - break; - case RawNTLMSSP: - seq_puts(s, "ntlmssp"); - break; - default: - /* shouldn't ever happen */ - seq_puts(s, "unknown"); - break; - } - - if (ses->sign) - seq_puts(s, "i"); - - if (ses->sectype == Kerberos) - seq_printf(s, ",cruid=%u", - from_kuid_munged(&init_user_ns, ses->cred_uid)); -} - -static void -cifs_show_cache_flavor(struct seq_file *s, struct cifs_sb_info *cifs_sb) -{ - seq_puts(s, ",cache="); - - if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_STRICT_IO) - seq_puts(s, "strict"); - else if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_DIRECT_IO) - seq_puts(s, "none"); - else if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_RW_CACHE) - seq_puts(s, "singleclient"); /* assume only one client access */ - else if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_RO_CACHE) - seq_puts(s, "ro"); /* read only caching assumed */ - else - seq_puts(s, "loose"); -} - -/* - * cifs_show_devname() is used so we show the mount device name with correct - * format (e.g. forward slashes vs. back slashes) in /proc/mounts - */ -static int cifs_show_devname(struct seq_file *m, struct dentry *root) -{ - struct cifs_sb_info *cifs_sb = CIFS_SB(root->d_sb); - char *devname = kstrdup(cifs_sb->ctx->source, GFP_KERNEL); - - if (devname == NULL) - seq_puts(m, "none"); - else { - convert_delimiter(devname, '/'); - /* escape all spaces in share names */ - seq_escape(m, devname, " \t"); - kfree(devname); - } - return 0; -} - -/* - * cifs_show_options() is for displaying mount options in /proc/mounts. - * Not all settable options are displayed but most of the important - * ones are. - */ -static int -cifs_show_options(struct seq_file *s, struct dentry *root) -{ - struct cifs_sb_info *cifs_sb = CIFS_SB(root->d_sb); - struct cifs_tcon *tcon = cifs_sb_master_tcon(cifs_sb); - struct sockaddr *srcaddr; - srcaddr = (struct sockaddr *)&tcon->ses->server->srcaddr; - - seq_show_option(s, "vers", tcon->ses->server->vals->version_string); - cifs_show_security(s, tcon->ses); - cifs_show_cache_flavor(s, cifs_sb); - - if (tcon->no_lease) - seq_puts(s, ",nolease"); - if (cifs_sb->ctx->multiuser) - seq_puts(s, ",multiuser"); - else if (tcon->ses->user_name) - seq_show_option(s, "username", tcon->ses->user_name); - - if (tcon->ses->domainName && tcon->ses->domainName[0] != 0) - seq_show_option(s, "domain", tcon->ses->domainName); - - if (srcaddr->sa_family != AF_UNSPEC) { - struct sockaddr_in *saddr4; - struct sockaddr_in6 *saddr6; - saddr4 = (struct sockaddr_in *)srcaddr; - saddr6 = (struct sockaddr_in6 *)srcaddr; - if (srcaddr->sa_family == AF_INET6) - seq_printf(s, ",srcaddr=%pI6c", - &saddr6->sin6_addr); - else if (srcaddr->sa_family == AF_INET) - seq_printf(s, ",srcaddr=%pI4", - &saddr4->sin_addr.s_addr); - else - seq_printf(s, ",srcaddr=BAD-AF:%i", - (int)(srcaddr->sa_family)); - } - - seq_printf(s, ",uid=%u", - from_kuid_munged(&init_user_ns, cifs_sb->ctx->linux_uid)); - if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_OVERR_UID) - seq_puts(s, ",forceuid"); - else - seq_puts(s, ",noforceuid"); - - seq_printf(s, ",gid=%u", - from_kgid_munged(&init_user_ns, cifs_sb->ctx->linux_gid)); - if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_OVERR_GID) - seq_puts(s, ",forcegid"); - else - seq_puts(s, ",noforcegid"); - - cifs_show_address(s, tcon->ses->server); - - if (!tcon->unix_ext) - seq_printf(s, ",file_mode=0%ho,dir_mode=0%ho", - cifs_sb->ctx->file_mode, - cifs_sb->ctx->dir_mode); - if (cifs_sb->ctx->iocharset) - seq_printf(s, ",iocharset=%s", cifs_sb->ctx->iocharset); - if (tcon->seal) - seq_puts(s, ",seal"); - else if (tcon->ses->server->ignore_signature) - seq_puts(s, ",signloosely"); - if (tcon->nocase) - seq_puts(s, ",nocase"); - if (tcon->nodelete) - seq_puts(s, ",nodelete"); - if (cifs_sb->ctx->no_sparse) - seq_puts(s, ",nosparse"); - if (tcon->local_lease) - seq_puts(s, ",locallease"); - if (tcon->retry) - seq_puts(s, ",hard"); - else - seq_puts(s, ",soft"); - if (tcon->use_persistent) - seq_puts(s, ",persistenthandles"); - else if (tcon->use_resilient) - seq_puts(s, ",resilienthandles"); - if (tcon->posix_extensions) - seq_puts(s, ",posix"); - else if (tcon->unix_ext) - seq_puts(s, ",unix"); - else - seq_puts(s, ",nounix"); - if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_NO_DFS) - seq_puts(s, ",nodfs"); - if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_POSIX_PATHS) - seq_puts(s, ",posixpaths"); - if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_SET_UID) - seq_puts(s, ",setuids"); - if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_UID_FROM_ACL) - seq_puts(s, ",idsfromsid"); - if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_SERVER_INUM) - seq_puts(s, ",serverino"); - if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_RWPIDFORWARD) - seq_puts(s, ",rwpidforward"); - if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_NOPOSIXBRL) - seq_puts(s, ",forcemand"); - if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_NO_XATTR) - seq_puts(s, ",nouser_xattr"); - if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_MAP_SPECIAL_CHR) - seq_puts(s, ",mapchars"); - if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_MAP_SFM_CHR) - seq_puts(s, ",mapposix"); - if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_UNX_EMUL) - seq_puts(s, ",sfu"); - if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_NO_BRL) - seq_puts(s, ",nobrl"); - if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_NO_HANDLE_CACHE) - seq_puts(s, ",nohandlecache"); - if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_MODE_FROM_SID) - seq_puts(s, ",modefromsid"); - if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_CIFS_ACL) - seq_puts(s, ",cifsacl"); - if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_DYNPERM) - seq_puts(s, ",dynperm"); - if (root->d_sb->s_flags & SB_POSIXACL) - seq_puts(s, ",acl"); - if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_MF_SYMLINKS) - seq_puts(s, ",mfsymlinks"); - if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_FSCACHE) - seq_puts(s, ",fsc"); - if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_NOSSYNC) - seq_puts(s, ",nostrictsync"); - if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_NO_PERM) - seq_puts(s, ",noperm"); - if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_CIFS_BACKUPUID) - seq_printf(s, ",backupuid=%u", - from_kuid_munged(&init_user_ns, - cifs_sb->ctx->backupuid)); - if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_CIFS_BACKUPGID) - seq_printf(s, ",backupgid=%u", - from_kgid_munged(&init_user_ns, - cifs_sb->ctx->backupgid)); - - seq_printf(s, ",rsize=%u", cifs_sb->ctx->rsize); - seq_printf(s, ",wsize=%u", cifs_sb->ctx->wsize); - seq_printf(s, ",bsize=%u", cifs_sb->ctx->bsize); - if (cifs_sb->ctx->rasize) - seq_printf(s, ",rasize=%u", cifs_sb->ctx->rasize); - if (tcon->ses->server->min_offload) - seq_printf(s, ",esize=%u", tcon->ses->server->min_offload); - seq_printf(s, ",echo_interval=%lu", - tcon->ses->server->echo_interval / HZ); - - /* Only display the following if overridden on mount */ - if (tcon->ses->server->max_credits != SMB2_MAX_CREDITS_AVAILABLE) - seq_printf(s, ",max_credits=%u", tcon->ses->server->max_credits); - if (tcon->ses->server->tcp_nodelay) - seq_puts(s, ",tcpnodelay"); - if (tcon->ses->server->noautotune) - seq_puts(s, ",noautotune"); - if (tcon->ses->server->noblocksnd) - seq_puts(s, ",noblocksend"); - - if (tcon->snapshot_time) - seq_printf(s, ",snapshot=%llu", tcon->snapshot_time); - if (tcon->handle_timeout) - seq_printf(s, ",handletimeout=%u", tcon->handle_timeout); - - /* - * Display file and directory attribute timeout in seconds. - * If file and directory attribute timeout the same then actimeo - * was likely specified on mount - */ - if (cifs_sb->ctx->acdirmax == cifs_sb->ctx->acregmax) - seq_printf(s, ",actimeo=%lu", cifs_sb->ctx->acregmax / HZ); - else { - seq_printf(s, ",acdirmax=%lu", cifs_sb->ctx->acdirmax / HZ); - seq_printf(s, ",acregmax=%lu", cifs_sb->ctx->acregmax / HZ); - } - seq_printf(s, ",closetimeo=%lu", cifs_sb->ctx->closetimeo / HZ); - - if (tcon->ses->chan_max > 1) - seq_printf(s, ",multichannel,max_channels=%zu", - tcon->ses->chan_max); - - if (tcon->use_witness) - seq_puts(s, ",witness"); - - return 0; -} - -static void cifs_umount_begin(struct super_block *sb) -{ - struct cifs_sb_info *cifs_sb = CIFS_SB(sb); - struct cifs_tcon *tcon; - - if (cifs_sb == NULL) - return; - - tcon = cifs_sb_master_tcon(cifs_sb); - - spin_lock(&cifs_tcp_ses_lock); - spin_lock(&tcon->tc_lock); - if ((tcon->tc_count > 1) || (tcon->status == TID_EXITING)) { - /* we have other mounts to same share or we have - already tried to umount this and woken up - all waiting network requests, nothing to do */ - spin_unlock(&tcon->tc_lock); - spin_unlock(&cifs_tcp_ses_lock); - return; - } - /* - * can not set tcon->status to TID_EXITING yet since we don't know if umount -f will - * fail later (e.g. due to open files). TID_EXITING will be set just before tdis req sent - */ - spin_unlock(&tcon->tc_lock); - spin_unlock(&cifs_tcp_ses_lock); - - cifs_close_all_deferred_files(tcon); - /* cancel_brl_requests(tcon); */ /* BB mark all brl mids as exiting */ - /* cancel_notify_requests(tcon); */ - if (tcon->ses && tcon->ses->server) { - cifs_dbg(FYI, "wake up tasks now - umount begin not complete\n"); - wake_up_all(&tcon->ses->server->request_q); - wake_up_all(&tcon->ses->server->response_q); - msleep(1); /* yield */ - /* we have to kick the requests once more */ - wake_up_all(&tcon->ses->server->response_q); - msleep(1); - } - - return; -} - -static int cifs_freeze(struct super_block *sb) -{ - struct cifs_sb_info *cifs_sb = CIFS_SB(sb); - struct cifs_tcon *tcon; - - if (cifs_sb == NULL) - return 0; - - tcon = cifs_sb_master_tcon(cifs_sb); - - cifs_close_all_deferred_files(tcon); - return 0; -} - -#ifdef CONFIG_CIFS_STATS2 -static int cifs_show_stats(struct seq_file *s, struct dentry *root) -{ - /* BB FIXME */ - return 0; -} -#endif - -static int cifs_write_inode(struct inode *inode, struct writeback_control *wbc) -{ - fscache_unpin_writeback(wbc, cifs_inode_cookie(inode)); - return 0; -} - -static int cifs_drop_inode(struct inode *inode) -{ - struct cifs_sb_info *cifs_sb = CIFS_SB(inode->i_sb); - - /* no serverino => unconditional eviction */ - return !(cifs_sb->mnt_cifs_flags & CIFS_MOUNT_SERVER_INUM) || - generic_drop_inode(inode); -} - -static const struct super_operations cifs_super_ops = { - .statfs = cifs_statfs, - .alloc_inode = cifs_alloc_inode, - .write_inode = cifs_write_inode, - .free_inode = cifs_free_inode, - .drop_inode = cifs_drop_inode, - .evict_inode = cifs_evict_inode, -/* .show_path = cifs_show_path, */ /* Would we ever need show path? */ - .show_devname = cifs_show_devname, -/* .delete_inode = cifs_delete_inode, */ /* Do not need above - function 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, - .freeze_fs = cifs_freeze, -#ifdef CONFIG_CIFS_STATS2 - .show_stats = cifs_show_stats, -#endif -}; - -/* - * Get root dentry from superblock according to prefix path mount option. - * Return dentry with refcount + 1 on success and NULL otherwise. - */ -static struct dentry * -cifs_get_root(struct smb3_fs_context *ctx, struct super_block *sb) -{ - struct dentry *dentry; - struct cifs_sb_info *cifs_sb = CIFS_SB(sb); - char *full_path = NULL; - char *s, *p; - char sep; - - if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_USE_PREFIX_PATH) - return dget(sb->s_root); - - full_path = cifs_build_path_to_root(ctx, cifs_sb, - cifs_sb_master_tcon(cifs_sb), 0); - if (full_path == NULL) - return ERR_PTR(-ENOMEM); - - cifs_dbg(FYI, "Get root dentry for %s\n", full_path); - - sep = CIFS_DIR_SEP(cifs_sb); - dentry = dget(sb->s_root); - s = full_path; - - do { - struct inode *dir = d_inode(dentry); - struct dentry *child; - - if (!S_ISDIR(dir->i_mode)) { - dput(dentry); - dentry = ERR_PTR(-ENOTDIR); - break; - } - - /* skip separators */ - while (*s == sep) - s++; - if (!*s) - break; - p = s++; - /* next separator */ - while (*s && *s != sep) - s++; - - child = lookup_positive_unlocked(p, dentry, s - p); - dput(dentry); - dentry = child; - } while (!IS_ERR(dentry)); - kfree(full_path); - return dentry; -} - -static int cifs_set_super(struct super_block *sb, void *data) -{ - struct cifs_mnt_data *mnt_data = data; - sb->s_fs_info = mnt_data->cifs_sb; - return set_anon_super(sb, NULL); -} - -struct dentry * -cifs_smb3_do_mount(struct file_system_type *fs_type, - int flags, struct smb3_fs_context *old_ctx) -{ - int rc; - struct super_block *sb = NULL; - struct cifs_sb_info *cifs_sb = NULL; - struct cifs_mnt_data mnt_data; - struct dentry *root; - - /* - * Prints in Kernel / CIFS log the attempted mount operation - * If CIFS_DEBUG && cifs_FYI - */ - if (cifsFYI) - cifs_dbg(FYI, "Devname: %s flags: %d\n", old_ctx->UNC, flags); - else - cifs_info("Attempting to mount %s\n", old_ctx->UNC); - - cifs_sb = kzalloc(sizeof(struct cifs_sb_info), GFP_KERNEL); - if (cifs_sb == NULL) { - root = ERR_PTR(-ENOMEM); - goto out; - } - - cifs_sb->ctx = kzalloc(sizeof(struct smb3_fs_context), GFP_KERNEL); - if (!cifs_sb->ctx) { - root = ERR_PTR(-ENOMEM); - goto out; - } - rc = smb3_fs_context_dup(cifs_sb->ctx, old_ctx); - if (rc) { - root = ERR_PTR(rc); - goto out; - } - - rc = cifs_setup_volume_info(cifs_sb->ctx, NULL, NULL); - if (rc) { - root = ERR_PTR(rc); - goto out; - } - - rc = cifs_setup_cifs_sb(cifs_sb); - if (rc) { - root = ERR_PTR(rc); - goto out; - } - - rc = cifs_mount(cifs_sb, cifs_sb->ctx); - if (rc) { - if (!(flags & SB_SILENT)) - cifs_dbg(VFS, "cifs_mount failed w/return code = %d\n", - rc); - root = ERR_PTR(rc); - goto out; - } - - mnt_data.ctx = cifs_sb->ctx; - mnt_data.cifs_sb = cifs_sb; - mnt_data.flags = flags; - - /* BB should we make this contingent on mount parm? */ - flags |= SB_NODIRATIME | SB_NOATIME; - - sb = sget(fs_type, cifs_match_super, cifs_set_super, flags, &mnt_data); - if (IS_ERR(sb)) { - root = ERR_CAST(sb); - cifs_umount(cifs_sb); - cifs_sb = NULL; - goto out; - } - - if (sb->s_root) { - cifs_dbg(FYI, "Use existing superblock\n"); - cifs_umount(cifs_sb); - cifs_sb = NULL; - } else { - rc = cifs_read_super(sb); - if (rc) { - root = ERR_PTR(rc); - goto out_super; - } - - sb->s_flags |= SB_ACTIVE; - } - - root = cifs_get_root(cifs_sb ? cifs_sb->ctx : old_ctx, sb); - if (IS_ERR(root)) - goto out_super; - - if (cifs_sb) - cifs_sb->root = dget(root); - - cifs_dbg(FYI, "dentry root is: %p\n", root); - return root; - -out_super: - deactivate_locked_super(sb); - return root; -out: - if (cifs_sb) { - if (!sb || IS_ERR(sb)) { /* otherwise kill_sb will handle */ - kfree(cifs_sb->prepath); - smb3_cleanup_fs_context(cifs_sb->ctx); - kfree(cifs_sb); - } - } - return root; -} - - -static ssize_t -cifs_loose_read_iter(struct kiocb *iocb, struct iov_iter *iter) -{ - ssize_t rc; - struct inode *inode = file_inode(iocb->ki_filp); - - if (iocb->ki_flags & IOCB_DIRECT) - return cifs_user_readv(iocb, iter); - - rc = cifs_revalidate_mapping(inode); - if (rc) - return rc; - - return generic_file_read_iter(iocb, iter); -} - -static ssize_t cifs_file_write_iter(struct kiocb *iocb, struct iov_iter *from) -{ - struct inode *inode = file_inode(iocb->ki_filp); - struct cifsInodeInfo *cinode = CIFS_I(inode); - ssize_t written; - int rc; - - if (iocb->ki_filp->f_flags & O_DIRECT) { - written = cifs_user_writev(iocb, from); - if (written > 0 && CIFS_CACHE_READ(cinode)) { - cifs_zap_mapping(inode); - cifs_dbg(FYI, - "Set no oplock for inode=%p after a write operation\n", - inode); - cinode->oplock = 0; - } - return written; - } - - written = cifs_get_writer(cinode); - if (written) - return written; - - written = generic_file_write_iter(iocb, from); - - if (CIFS_CACHE_WRITE(CIFS_I(inode))) - goto out; - - rc = filemap_fdatawrite(inode->i_mapping); - if (rc) - cifs_dbg(FYI, "cifs_file_write_iter: %d rc on %p inode\n", - rc, inode); - -out: - cifs_put_writer(cinode); - return written; -} - -static loff_t cifs_llseek(struct file *file, loff_t offset, int whence) -{ - struct cifsFileInfo *cfile = file->private_data; - struct cifs_tcon *tcon; - - /* - * whence == SEEK_END || SEEK_DATA || SEEK_HOLE => we must revalidate - * the cached file length - */ - if (whence != SEEK_SET && whence != SEEK_CUR) { - int rc; - struct inode *inode = file_inode(file); - - /* - * We need to be sure that all dirty pages are written and the - * server has the newest file length. - */ - if (!CIFS_CACHE_READ(CIFS_I(inode)) && inode->i_mapping && - inode->i_mapping->nrpages != 0) { - rc = filemap_fdatawait(inode->i_mapping); - if (rc) { - mapping_set_error(inode->i_mapping, rc); - return rc; - } - } - /* - * Some applications poll for the file length in this strange - * way so we must seek to end on non-oplocked files by - * setting the revalidate time to zero. - */ - CIFS_I(inode)->time = 0; - - rc = cifs_revalidate_file_attr(file); - if (rc < 0) - return (loff_t)rc; - } - if (cfile && cfile->tlink) { - tcon = tlink_tcon(cfile->tlink); - if (tcon->ses->server->ops->llseek) - return tcon->ses->server->ops->llseek(file, tcon, - offset, whence); - } - return generic_file_llseek(file, offset, whence); -} - -static int -cifs_setlease(struct file *file, long arg, struct file_lock **lease, void **priv) -{ - /* - * Note that this is called by vfs setlease with i_lock held to - * protect *lease from going away. - */ - struct inode *inode = file_inode(file); - struct cifsFileInfo *cfile = file->private_data; - - if (!(S_ISREG(inode->i_mode))) - return -EINVAL; - - /* Check if file is oplocked if this is request for new lease */ - if (arg == F_UNLCK || - ((arg == F_RDLCK) && CIFS_CACHE_READ(CIFS_I(inode))) || - ((arg == F_WRLCK) && CIFS_CACHE_WRITE(CIFS_I(inode)))) - return generic_setlease(file, arg, lease, priv); - else if (tlink_tcon(cfile->tlink)->local_lease && - !CIFS_CACHE_READ(CIFS_I(inode))) - /* - * If the server claims to support oplock on this file, then we - * still need to check oplock even if the local_lease mount - * option is set, but there are servers which do not support - * oplock for which this mount option may be useful if the user - * knows that the file won't be changed on the server by anyone - * else. - */ - return generic_setlease(file, arg, lease, priv); - else - return -EAGAIN; -} - -struct file_system_type cifs_fs_type = { - .owner = THIS_MODULE, - .name = "cifs", - .init_fs_context = smb3_init_fs_context, - .parameters = smb3_fs_parameters, - .kill_sb = cifs_kill_sb, - .fs_flags = FS_RENAME_DOES_D_MOVE, -}; -MODULE_ALIAS_FS("cifs"); - -struct file_system_type smb3_fs_type = { - .owner = THIS_MODULE, - .name = "smb3", - .init_fs_context = smb3_init_fs_context, - .parameters = smb3_fs_parameters, - .kill_sb = cifs_kill_sb, - .fs_flags = FS_RENAME_DOES_D_MOVE, -}; -MODULE_ALIAS_FS("smb3"); -MODULE_ALIAS("smb3"); - -const struct inode_operations cifs_dir_inode_ops = { - .create = cifs_create, - .atomic_open = cifs_atomic_open, - .lookup = cifs_lookup, - .getattr = cifs_getattr, - .unlink = cifs_unlink, - .link = cifs_hardlink, - .mkdir = cifs_mkdir, - .rmdir = cifs_rmdir, - .rename = cifs_rename2, - .permission = cifs_permission, - .setattr = cifs_setattr, - .symlink = cifs_symlink, - .mknod = cifs_mknod, - .listxattr = cifs_listxattr, -}; - -const struct inode_operations cifs_file_inode_ops = { - .setattr = cifs_setattr, - .getattr = cifs_getattr, - .permission = cifs_permission, - .listxattr = cifs_listxattr, - .fiemap = cifs_fiemap, -}; - -const char *cifs_get_link(struct dentry *dentry, struct inode *inode, - struct delayed_call *done) -{ - char *target_path; - - target_path = kmalloc(PATH_MAX, GFP_KERNEL); - if (!target_path) - return ERR_PTR(-ENOMEM); - - spin_lock(&inode->i_lock); - if (likely(CIFS_I(inode)->symlink_target)) { - strscpy(target_path, CIFS_I(inode)->symlink_target, PATH_MAX); - } else { - kfree(target_path); - target_path = ERR_PTR(-EOPNOTSUPP); - } - spin_unlock(&inode->i_lock); - - if (!IS_ERR(target_path)) - set_delayed_call(done, kfree_link, target_path); - - return target_path; -} - -const struct inode_operations cifs_symlink_inode_ops = { - .get_link = cifs_get_link, - .permission = cifs_permission, - .listxattr = cifs_listxattr, -}; - -static loff_t cifs_remap_file_range(struct file *src_file, loff_t off, - struct file *dst_file, loff_t destoff, loff_t len, - unsigned int remap_flags) -{ - struct inode *src_inode = file_inode(src_file); - struct inode *target_inode = file_inode(dst_file); - struct cifsFileInfo *smb_file_src = src_file->private_data; - struct cifsFileInfo *smb_file_target; - struct cifs_tcon *target_tcon; - unsigned int xid; - int rc; - - if (remap_flags & ~(REMAP_FILE_DEDUP | REMAP_FILE_ADVISORY)) - return -EINVAL; - - cifs_dbg(FYI, "clone range\n"); - - xid = get_xid(); - - if (!src_file->private_data || !dst_file->private_data) { - rc = -EBADF; - cifs_dbg(VFS, "missing cifsFileInfo on copy range src file\n"); - goto out; - } - - smb_file_target = dst_file->private_data; - target_tcon = tlink_tcon(smb_file_target->tlink); - - /* - * Note: cifs case is easier than btrfs since server responsible for - * checks for proper open modes and file type and if it wants - * server could even support copy of range where source = target - */ - lock_two_nondirectories(target_inode, src_inode); - - if (len == 0) - len = src_inode->i_size - off; - - cifs_dbg(FYI, "about to flush pages\n"); - /* should we flush first and last page first */ - truncate_inode_pages_range(&target_inode->i_data, destoff, - PAGE_ALIGN(destoff + len)-1); - - if (target_tcon->ses->server->ops->duplicate_extents) - rc = target_tcon->ses->server->ops->duplicate_extents(xid, - smb_file_src, smb_file_target, off, len, destoff); - else - rc = -EOPNOTSUPP; - - /* force revalidate of size and timestamps of target file now - that target is updated on the server */ - CIFS_I(target_inode)->time = 0; - /* although unlocking in the reverse order from locking is not - strictly necessary here it is a little cleaner to be consistent */ - unlock_two_nondirectories(src_inode, target_inode); -out: - free_xid(xid); - return rc < 0 ? rc : len; -} - -ssize_t cifs_file_copychunk_range(unsigned int xid, - struct file *src_file, loff_t off, - struct file *dst_file, loff_t destoff, - size_t len, unsigned int flags) -{ - struct inode *src_inode = file_inode(src_file); - struct inode *target_inode = file_inode(dst_file); - struct cifsFileInfo *smb_file_src; - struct cifsFileInfo *smb_file_target; - struct cifs_tcon *src_tcon; - struct cifs_tcon *target_tcon; - ssize_t rc; - - cifs_dbg(FYI, "copychunk range\n"); - - if (!src_file->private_data || !dst_file->private_data) { - rc = -EBADF; - cifs_dbg(VFS, "missing cifsFileInfo on copy range src file\n"); - goto out; - } - - rc = -EXDEV; - smb_file_target = dst_file->private_data; - smb_file_src = src_file->private_data; - src_tcon = tlink_tcon(smb_file_src->tlink); - target_tcon = tlink_tcon(smb_file_target->tlink); - - if (src_tcon->ses != target_tcon->ses) { - cifs_dbg(VFS, "source and target of copy not on same server\n"); - goto out; - } - - rc = -EOPNOTSUPP; - if (!target_tcon->ses->server->ops->copychunk_range) - goto out; - - /* - * Note: cifs case is easier than btrfs since server responsible for - * checks for proper open modes and file type and if it wants - * server could even support copy of range where source = target - */ - lock_two_nondirectories(target_inode, src_inode); - - cifs_dbg(FYI, "about to flush pages\n"); - - rc = filemap_write_and_wait_range(src_inode->i_mapping, off, - off + len - 1); - if (rc) - goto unlock; - - /* should we flush first and last page first */ - truncate_inode_pages(&target_inode->i_data, 0); - - rc = file_modified(dst_file); - if (!rc) - rc = target_tcon->ses->server->ops->copychunk_range(xid, - smb_file_src, smb_file_target, off, len, destoff); - - file_accessed(src_file); - - /* force revalidate of size and timestamps of target file now - * that target is updated on the server - */ - CIFS_I(target_inode)->time = 0; - -unlock: - /* although unlocking in the reverse order from locking is not - * strictly necessary here it is a little cleaner to be consistent - */ - unlock_two_nondirectories(src_inode, target_inode); - -out: - return rc; -} - -/* - * Directory operations under CIFS/SMB2/SMB3 are synchronous, so fsync() - * is a dummy operation. - */ -static int cifs_dir_fsync(struct file *file, loff_t start, loff_t end, int datasync) -{ - cifs_dbg(FYI, "Sync directory - name: %pD datasync: 0x%x\n", - file, datasync); - - return 0; -} - -static ssize_t cifs_copy_file_range(struct file *src_file, loff_t off, - struct file *dst_file, loff_t destoff, - size_t len, unsigned int flags) -{ - unsigned int xid = get_xid(); - ssize_t rc; - struct cifsFileInfo *cfile = dst_file->private_data; - - if (cfile->swapfile) { - rc = -EOPNOTSUPP; - free_xid(xid); - return rc; - } - - rc = cifs_file_copychunk_range(xid, src_file, off, dst_file, destoff, - len, flags); - free_xid(xid); - - if (rc == -EOPNOTSUPP || rc == -EXDEV) - rc = generic_copy_file_range(src_file, off, dst_file, - destoff, len, flags); - return rc; -} - -const struct file_operations cifs_file_ops = { - .read_iter = cifs_loose_read_iter, - .write_iter = cifs_file_write_iter, - .open = cifs_open, - .release = cifs_close, - .lock = cifs_lock, - .flock = cifs_flock, - .fsync = cifs_fsync, - .flush = cifs_flush, - .mmap = cifs_file_mmap, - .splice_read = generic_file_splice_read, - .splice_write = iter_file_splice_write, - .llseek = cifs_llseek, - .unlocked_ioctl = cifs_ioctl, - .copy_file_range = cifs_copy_file_range, - .remap_file_range = cifs_remap_file_range, - .setlease = cifs_setlease, - .fallocate = cifs_fallocate, -}; - -const struct file_operations cifs_file_strict_ops = { - .read_iter = cifs_strict_readv, - .write_iter = cifs_strict_writev, - .open = cifs_open, - .release = cifs_close, - .lock = cifs_lock, - .flock = cifs_flock, - .fsync = cifs_strict_fsync, - .flush = cifs_flush, - .mmap = cifs_file_strict_mmap, - .splice_read = generic_file_splice_read, - .splice_write = iter_file_splice_write, - .llseek = cifs_llseek, - .unlocked_ioctl = cifs_ioctl, - .copy_file_range = cifs_copy_file_range, - .remap_file_range = cifs_remap_file_range, - .setlease = cifs_setlease, - .fallocate = cifs_fallocate, -}; - -const struct file_operations cifs_file_direct_ops = { - .read_iter = cifs_direct_readv, - .write_iter = cifs_direct_writev, - .open = cifs_open, - .release = cifs_close, - .lock = cifs_lock, - .flock = cifs_flock, - .fsync = cifs_fsync, - .flush = cifs_flush, - .mmap = cifs_file_mmap, - .splice_read = generic_file_splice_read, - .splice_write = iter_file_splice_write, - .unlocked_ioctl = cifs_ioctl, - .copy_file_range = cifs_copy_file_range, - .remap_file_range = cifs_remap_file_range, - .llseek = cifs_llseek, - .setlease = cifs_setlease, - .fallocate = cifs_fallocate, -}; - -const struct file_operations cifs_file_nobrl_ops = { - .read_iter = cifs_loose_read_iter, - .write_iter = cifs_file_write_iter, - .open = cifs_open, - .release = cifs_close, - .fsync = cifs_fsync, - .flush = cifs_flush, - .mmap = cifs_file_mmap, - .splice_read = generic_file_splice_read, - .splice_write = iter_file_splice_write, - .llseek = cifs_llseek, - .unlocked_ioctl = cifs_ioctl, - .copy_file_range = cifs_copy_file_range, - .remap_file_range = cifs_remap_file_range, - .setlease = cifs_setlease, - .fallocate = cifs_fallocate, -}; - -const struct file_operations cifs_file_strict_nobrl_ops = { - .read_iter = cifs_strict_readv, - .write_iter = cifs_strict_writev, - .open = cifs_open, - .release = cifs_close, - .fsync = cifs_strict_fsync, - .flush = cifs_flush, - .mmap = cifs_file_strict_mmap, - .splice_read = generic_file_splice_read, - .splice_write = iter_file_splice_write, - .llseek = cifs_llseek, - .unlocked_ioctl = cifs_ioctl, - .copy_file_range = cifs_copy_file_range, - .remap_file_range = cifs_remap_file_range, - .setlease = cifs_setlease, - .fallocate = cifs_fallocate, -}; - -const struct file_operations cifs_file_direct_nobrl_ops = { - .read_iter = cifs_direct_readv, - .write_iter = cifs_direct_writev, - .open = cifs_open, - .release = cifs_close, - .fsync = cifs_fsync, - .flush = cifs_flush, - .mmap = cifs_file_mmap, - .splice_read = generic_file_splice_read, - .splice_write = iter_file_splice_write, - .unlocked_ioctl = cifs_ioctl, - .copy_file_range = cifs_copy_file_range, - .remap_file_range = cifs_remap_file_range, - .llseek = cifs_llseek, - .setlease = cifs_setlease, - .fallocate = cifs_fallocate, -}; - -const struct file_operations cifs_dir_ops = { - .iterate_shared = cifs_readdir, - .release = cifs_closedir, - .read = generic_read_dir, - .unlocked_ioctl = cifs_ioctl, - .copy_file_range = cifs_copy_file_range, - .remap_file_range = cifs_remap_file_range, - .llseek = generic_file_llseek, - .fsync = cifs_dir_fsync, -}; - -static void -cifs_init_once(void *inode) -{ - struct cifsInodeInfo *cifsi = inode; - - inode_init_once(&cifsi->netfs.inode); - init_rwsem(&cifsi->lock_sem); -} - -static int __init -cifs_init_inodecache(void) -{ - cifs_inode_cachep = kmem_cache_create("cifs_inode_cache", - sizeof(struct cifsInodeInfo), - 0, (SLAB_RECLAIM_ACCOUNT| - SLAB_MEM_SPREAD|SLAB_ACCOUNT), - cifs_init_once); - if (cifs_inode_cachep == NULL) - return -ENOMEM; - - return 0; -} - -static void -cifs_destroy_inodecache(void) -{ - /* - * Make sure all delayed rcu free inodes are flushed before we - * destroy cache. - */ - rcu_barrier(); - kmem_cache_destroy(cifs_inode_cachep); -} - -static int -cifs_init_request_bufs(void) -{ - /* - * SMB2 maximum header size is bigger than CIFS one - no problems to - * allocate some more bytes for CIFS. - */ - size_t max_hdr_size = MAX_SMB2_HDR_SIZE; - - if (CIFSMaxBufSize < 8192) { - /* Buffer size can not be smaller than 2 * PATH_MAX since maximum - Unicode path name has to fit in any SMB/CIFS path based frames */ - CIFSMaxBufSize = 8192; - } else if (CIFSMaxBufSize > 1024*127) { - CIFSMaxBufSize = 1024 * 127; - } else { - CIFSMaxBufSize &= 0x1FE00; /* Round size to even 512 byte mult*/ - } -/* - cifs_dbg(VFS, "CIFSMaxBufSize %d 0x%x\n", - CIFSMaxBufSize, CIFSMaxBufSize); -*/ - cifs_req_cachep = kmem_cache_create_usercopy("cifs_request", - CIFSMaxBufSize + max_hdr_size, 0, - SLAB_HWCACHE_ALIGN, 0, - CIFSMaxBufSize + max_hdr_size, - NULL); - if (cifs_req_cachep == NULL) - return -ENOMEM; - - if (cifs_min_rcv < 1) - cifs_min_rcv = 1; - else if (cifs_min_rcv > 64) { - cifs_min_rcv = 64; - cifs_dbg(VFS, "cifs_min_rcv set to maximum (64)\n"); - } - - cifs_req_poolp = mempool_create_slab_pool(cifs_min_rcv, - cifs_req_cachep); - - if (cifs_req_poolp == NULL) { - kmem_cache_destroy(cifs_req_cachep); - return -ENOMEM; - } - /* MAX_CIFS_SMALL_BUFFER_SIZE bytes is enough for most SMB responses and - almost all handle based requests (but not write response, nor is it - sufficient for path based requests). A smaller size would have - been more efficient (compacting multiple slab items on one 4k page) - for the case in which debug was on, but this larger size allows - more SMBs to use small buffer alloc and is still much more - efficient to alloc 1 per page off the slab compared to 17K (5page) - alloc of large cifs buffers even when page debugging is on */ - cifs_sm_req_cachep = kmem_cache_create_usercopy("cifs_small_rq", - MAX_CIFS_SMALL_BUFFER_SIZE, 0, SLAB_HWCACHE_ALIGN, - 0, MAX_CIFS_SMALL_BUFFER_SIZE, NULL); - if (cifs_sm_req_cachep == NULL) { - mempool_destroy(cifs_req_poolp); - kmem_cache_destroy(cifs_req_cachep); - return -ENOMEM; - } - - if (cifs_min_small < 2) - cifs_min_small = 2; - else if (cifs_min_small > 256) { - cifs_min_small = 256; - cifs_dbg(FYI, "cifs_min_small set to maximum (256)\n"); - } - - cifs_sm_req_poolp = mempool_create_slab_pool(cifs_min_small, - cifs_sm_req_cachep); - - if (cifs_sm_req_poolp == NULL) { - mempool_destroy(cifs_req_poolp); - kmem_cache_destroy(cifs_req_cachep); - kmem_cache_destroy(cifs_sm_req_cachep); - return -ENOMEM; - } - - return 0; -} - -static void -cifs_destroy_request_bufs(void) -{ - mempool_destroy(cifs_req_poolp); - kmem_cache_destroy(cifs_req_cachep); - mempool_destroy(cifs_sm_req_poolp); - kmem_cache_destroy(cifs_sm_req_cachep); -} - -static int init_mids(void) -{ - cifs_mid_cachep = kmem_cache_create("cifs_mpx_ids", - sizeof(struct mid_q_entry), 0, - SLAB_HWCACHE_ALIGN, NULL); - if (cifs_mid_cachep == NULL) - return -ENOMEM; - - /* 3 is a reasonable minimum number of simultaneous operations */ - cifs_mid_poolp = mempool_create_slab_pool(3, cifs_mid_cachep); - if (cifs_mid_poolp == NULL) { - kmem_cache_destroy(cifs_mid_cachep); - return -ENOMEM; - } - - return 0; -} - -static void destroy_mids(void) -{ - mempool_destroy(cifs_mid_poolp); - kmem_cache_destroy(cifs_mid_cachep); -} - -static int __init -init_cifs(void) -{ - int rc = 0; - cifs_proc_init(); - INIT_LIST_HEAD(&cifs_tcp_ses_list); -/* - * Initialize Global counters - */ - atomic_set(&sesInfoAllocCount, 0); - atomic_set(&tconInfoAllocCount, 0); - atomic_set(&tcpSesNextId, 0); - atomic_set(&tcpSesAllocCount, 0); - atomic_set(&tcpSesReconnectCount, 0); - atomic_set(&tconInfoReconnectCount, 0); - - atomic_set(&buf_alloc_count, 0); - atomic_set(&small_buf_alloc_count, 0); -#ifdef CONFIG_CIFS_STATS2 - atomic_set(&total_buf_alloc_count, 0); - atomic_set(&total_small_buf_alloc_count, 0); - if (slow_rsp_threshold < 1) - cifs_dbg(FYI, "slow_response_threshold msgs disabled\n"); - else if (slow_rsp_threshold > 32767) - cifs_dbg(VFS, - "slow response threshold set higher than recommended (0 to 32767)\n"); -#endif /* CONFIG_CIFS_STATS2 */ - - atomic_set(&mid_count, 0); - GlobalCurrentXid = 0; - GlobalTotalActiveXid = 0; - GlobalMaxActiveXid = 0; - spin_lock_init(&cifs_tcp_ses_lock); - spin_lock_init(&GlobalMid_Lock); - - cifs_lock_secret = get_random_u32(); - - if (cifs_max_pending < 2) { - cifs_max_pending = 2; - cifs_dbg(FYI, "cifs_max_pending set to min of 2\n"); - } else if (cifs_max_pending > CIFS_MAX_REQ) { - cifs_max_pending = CIFS_MAX_REQ; - cifs_dbg(FYI, "cifs_max_pending set to max of %u\n", - CIFS_MAX_REQ); - } - - cifsiod_wq = alloc_workqueue("cifsiod", WQ_FREEZABLE|WQ_MEM_RECLAIM, 0); - if (!cifsiod_wq) { - rc = -ENOMEM; - goto out_clean_proc; - } - - /* - * Consider in future setting limit!=0 maybe to min(num_of_cores - 1, 3) - * so that we don't launch too many worker threads but - * Documentation/core-api/workqueue.rst recommends setting it to 0 - */ - - /* WQ_UNBOUND allows decrypt tasks to run on any CPU */ - decrypt_wq = alloc_workqueue("smb3decryptd", - WQ_UNBOUND|WQ_FREEZABLE|WQ_MEM_RECLAIM, 0); - if (!decrypt_wq) { - rc = -ENOMEM; - goto out_destroy_cifsiod_wq; - } - - fileinfo_put_wq = alloc_workqueue("cifsfileinfoput", - WQ_UNBOUND|WQ_FREEZABLE|WQ_MEM_RECLAIM, 0); - if (!fileinfo_put_wq) { - rc = -ENOMEM; - goto out_destroy_decrypt_wq; - } - - cifsoplockd_wq = alloc_workqueue("cifsoplockd", - WQ_FREEZABLE|WQ_MEM_RECLAIM, 0); - if (!cifsoplockd_wq) { - rc = -ENOMEM; - goto out_destroy_fileinfo_put_wq; - } - - deferredclose_wq = alloc_workqueue("deferredclose", - WQ_FREEZABLE|WQ_MEM_RECLAIM, 0); - if (!deferredclose_wq) { - rc = -ENOMEM; - goto out_destroy_cifsoplockd_wq; - } - - rc = cifs_init_inodecache(); - if (rc) - goto out_destroy_deferredclose_wq; - - rc = init_mids(); - if (rc) - goto out_destroy_inodecache; - - rc = cifs_init_request_bufs(); - if (rc) - goto out_destroy_mids; - -#ifdef CONFIG_CIFS_DFS_UPCALL - rc = dfs_cache_init(); - if (rc) - goto out_destroy_request_bufs; -#endif /* CONFIG_CIFS_DFS_UPCALL */ -#ifdef CONFIG_CIFS_UPCALL - rc = init_cifs_spnego(); - if (rc) - goto out_destroy_dfs_cache; -#endif /* CONFIG_CIFS_UPCALL */ -#ifdef CONFIG_CIFS_SWN_UPCALL - rc = cifs_genl_init(); - if (rc) - goto out_register_key_type; -#endif /* CONFIG_CIFS_SWN_UPCALL */ - - rc = init_cifs_idmap(); - if (rc) - goto out_cifs_swn_init; - - rc = register_filesystem(&cifs_fs_type); - if (rc) - goto out_init_cifs_idmap; - - rc = register_filesystem(&smb3_fs_type); - if (rc) { - unregister_filesystem(&cifs_fs_type); - goto out_init_cifs_idmap; - } - - return 0; - -out_init_cifs_idmap: - exit_cifs_idmap(); -out_cifs_swn_init: -#ifdef CONFIG_CIFS_SWN_UPCALL - cifs_genl_exit(); -out_register_key_type: -#endif -#ifdef CONFIG_CIFS_UPCALL - exit_cifs_spnego(); -out_destroy_dfs_cache: -#endif -#ifdef CONFIG_CIFS_DFS_UPCALL - dfs_cache_destroy(); -out_destroy_request_bufs: -#endif - cifs_destroy_request_bufs(); -out_destroy_mids: - destroy_mids(); -out_destroy_inodecache: - cifs_destroy_inodecache(); -out_destroy_deferredclose_wq: - destroy_workqueue(deferredclose_wq); -out_destroy_cifsoplockd_wq: - destroy_workqueue(cifsoplockd_wq); -out_destroy_fileinfo_put_wq: - destroy_workqueue(fileinfo_put_wq); -out_destroy_decrypt_wq: - destroy_workqueue(decrypt_wq); -out_destroy_cifsiod_wq: - destroy_workqueue(cifsiod_wq); -out_clean_proc: - cifs_proc_clean(); - return rc; -} - -static void __exit -exit_cifs(void) -{ - cifs_dbg(NOISY, "exit_smb3\n"); - unregister_filesystem(&cifs_fs_type); - unregister_filesystem(&smb3_fs_type); - cifs_dfs_release_automount_timer(); - exit_cifs_idmap(); -#ifdef CONFIG_CIFS_SWN_UPCALL - cifs_genl_exit(); -#endif -#ifdef CONFIG_CIFS_UPCALL - exit_cifs_spnego(); -#endif -#ifdef CONFIG_CIFS_DFS_UPCALL - dfs_cache_destroy(); -#endif - cifs_destroy_request_bufs(); - destroy_mids(); - cifs_destroy_inodecache(); - destroy_workqueue(deferredclose_wq); - destroy_workqueue(cifsoplockd_wq); - destroy_workqueue(decrypt_wq); - destroy_workqueue(fileinfo_put_wq); - destroy_workqueue(cifsiod_wq); - cifs_proc_clean(); -} - -MODULE_AUTHOR("Steve French"); -MODULE_LICENSE("GPL"); /* combination of LGPL + GPL source behaves as GPL */ -MODULE_DESCRIPTION - ("VFS to access SMB3 servers e.g. Samba, Macs, Azure and Windows (and " - "also older servers complying with the SNIA CIFS Specification)"); -MODULE_VERSION(CIFS_VERSION); -MODULE_SOFTDEP("ecb"); -MODULE_SOFTDEP("hmac"); -MODULE_SOFTDEP("md5"); -MODULE_SOFTDEP("nls"); -MODULE_SOFTDEP("aes"); -MODULE_SOFTDEP("cmac"); -MODULE_SOFTDEP("sha256"); -MODULE_SOFTDEP("sha512"); -MODULE_SOFTDEP("aead2"); -MODULE_SOFTDEP("ccm"); -MODULE_SOFTDEP("gcm"); -module_init(init_cifs) -module_exit(exit_cifs) diff --git a/fs/cifs/cifsfs.h b/fs/cifs/cifsfs.h deleted file mode 100644 index b6c38896fb2db..0000000000000 --- a/fs/cifs/cifsfs.h +++ /dev/null @@ -1,161 +0,0 @@ -/* SPDX-License-Identifier: LGPL-2.1 */ -/* - * - * Copyright (c) International Business Machines Corp., 2002, 2007 - * Author(s): Steve French (sfrench@us.ibm.com) - * - */ - -#ifndef _CIFSFS_H -#define _CIFSFS_H - -#include - -#define ROOT_I 2 - -/* - * ino_t is 32-bits on 32-bit arch. We have to squash the 64-bit value down - * so that it will fit. We use hash_64 to convert the value to 31 bits, and - * then add 1, to ensure that we don't end up with a 0 as the value. - */ -static inline ino_t -cifs_uniqueid_to_ino_t(u64 fileid) -{ - if ((sizeof(ino_t)) < (sizeof(u64))) - return (ino_t)hash_64(fileid, (sizeof(ino_t) * 8) - 1) + 1; - - return (ino_t)fileid; - -} - -static inline void cifs_set_time(struct dentry *dentry, unsigned long time) -{ - dentry->d_fsdata = (void *) time; -} - -static inline unsigned long cifs_get_time(struct dentry *dentry) -{ - return (unsigned long) dentry->d_fsdata; -} - -extern struct file_system_type cifs_fs_type, smb3_fs_type; -extern const struct address_space_operations cifs_addr_ops; -extern const struct address_space_operations cifs_addr_ops_smallbuf; - -/* Functions related to super block operations */ -extern void cifs_sb_active(struct super_block *sb); -extern void cifs_sb_deactive(struct super_block *sb); - -/* Functions related to inodes */ -extern const struct inode_operations cifs_dir_inode_ops; -extern struct inode *cifs_root_iget(struct super_block *); -extern int cifs_create(struct user_namespace *, struct inode *, - struct dentry *, umode_t, bool excl); -extern int cifs_atomic_open(struct inode *, struct dentry *, - struct file *, unsigned, umode_t); -extern struct dentry *cifs_lookup(struct inode *, struct dentry *, - unsigned int); -extern int cifs_unlink(struct inode *dir, struct dentry *dentry); -extern int cifs_hardlink(struct dentry *, struct inode *, struct dentry *); -extern int cifs_mknod(struct user_namespace *, struct inode *, struct dentry *, - umode_t, dev_t); -extern int cifs_mkdir(struct user_namespace *, struct inode *, struct dentry *, - umode_t); -extern int cifs_rmdir(struct inode *, struct dentry *); -extern int cifs_rename2(struct user_namespace *, struct inode *, - struct dentry *, struct inode *, struct dentry *, - unsigned int); -extern int cifs_revalidate_file_attr(struct file *filp); -extern int cifs_revalidate_dentry_attr(struct dentry *); -extern int cifs_revalidate_file(struct file *filp); -extern int cifs_revalidate_dentry(struct dentry *); -extern int cifs_invalidate_mapping(struct inode *inode); -extern int cifs_revalidate_mapping(struct inode *inode); -extern int cifs_zap_mapping(struct inode *inode); -extern int cifs_getattr(struct user_namespace *, const struct path *, - struct kstat *, u32, unsigned int); -extern int cifs_setattr(struct user_namespace *, struct dentry *, - struct iattr *); -extern int cifs_fiemap(struct inode *, struct fiemap_extent_info *, u64 start, - u64 len); - -extern const struct inode_operations cifs_file_inode_ops; -extern const struct inode_operations cifs_symlink_inode_ops; -extern const struct inode_operations cifs_dfs_referral_inode_operations; - - -/* Functions related to files and directories */ -extern const struct file_operations cifs_file_ops; -extern const struct file_operations cifs_file_direct_ops; /* if directio mnt */ -extern const struct file_operations cifs_file_strict_ops; /* if strictio mnt */ -extern const struct file_operations cifs_file_nobrl_ops; /* no brlocks */ -extern const struct file_operations cifs_file_direct_nobrl_ops; -extern const struct file_operations cifs_file_strict_nobrl_ops; -extern int cifs_open(struct inode *inode, struct file *file); -extern int cifs_close(struct inode *inode, struct file *file); -extern int cifs_closedir(struct inode *inode, struct file *file); -extern ssize_t cifs_user_readv(struct kiocb *iocb, struct iov_iter *to); -extern ssize_t cifs_direct_readv(struct kiocb *iocb, struct iov_iter *to); -extern ssize_t cifs_strict_readv(struct kiocb *iocb, struct iov_iter *to); -extern ssize_t cifs_user_writev(struct kiocb *iocb, struct iov_iter *from); -extern ssize_t cifs_direct_writev(struct kiocb *iocb, struct iov_iter *from); -extern ssize_t cifs_strict_writev(struct kiocb *iocb, struct iov_iter *from); -extern int cifs_flock(struct file *pfile, int cmd, struct file_lock *plock); -extern int cifs_lock(struct file *, int, struct file_lock *); -extern int cifs_fsync(struct file *, loff_t, loff_t, int); -extern int cifs_strict_fsync(struct file *, loff_t, loff_t, int); -extern int cifs_flush(struct file *, fl_owner_t id); -extern int cifs_file_mmap(struct file * , struct vm_area_struct *); -extern int cifs_file_strict_mmap(struct file * , struct vm_area_struct *); -extern const struct file_operations cifs_dir_ops; -extern int cifs_dir_open(struct inode *inode, struct file *file); -extern int cifs_readdir(struct file *file, struct dir_context *ctx); - -/* Functions related to dir entries */ -extern const struct dentry_operations cifs_dentry_ops; -extern const struct dentry_operations cifs_ci_dentry_ops; - -#ifdef CONFIG_CIFS_DFS_UPCALL -extern struct vfsmount *cifs_dfs_d_automount(struct path *path); -#else -static inline struct vfsmount *cifs_dfs_d_automount(struct path *path) -{ - return ERR_PTR(-EREMOTE); -} -#endif - -/* Functions related to symlinks */ -extern const char *cifs_get_link(struct dentry *, struct inode *, - struct delayed_call *); -extern int cifs_symlink(struct user_namespace *mnt_userns, struct inode *inode, - struct dentry *direntry, const char *symname); - -#ifdef CONFIG_CIFS_XATTR -extern const struct xattr_handler *cifs_xattr_handlers[]; -extern ssize_t cifs_listxattr(struct dentry *, char *, size_t); -#else -# define cifs_xattr_handlers NULL -# define cifs_listxattr NULL -#endif - -extern ssize_t cifs_file_copychunk_range(unsigned int xid, - struct file *src_file, loff_t off, - struct file *dst_file, loff_t destoff, - size_t len, unsigned int flags); - -extern long cifs_ioctl(struct file *filep, unsigned int cmd, unsigned long arg); -extern void cifs_setsize(struct inode *inode, loff_t offset); -extern int cifs_truncate_page(struct address_space *mapping, loff_t from); - -struct smb3_fs_context; -extern struct dentry *cifs_smb3_do_mount(struct file_system_type *fs_type, - int flags, struct smb3_fs_context *ctx); - -#ifdef CONFIG_CIFS_NFSD_EXPORT -extern const struct export_operations cifs_export_ops; -#endif /* CONFIG_CIFS_NFSD_EXPORT */ - -/* when changing internal version - update following two lines at same time */ -#define SMB3_PRODUCT_BUILD 40 -#define CIFS_VERSION "2.40" -#endif /* _CIFSFS_H */ diff --git a/fs/cifs/cifsglob.h b/fs/cifs/cifsglob.h deleted file mode 100644 index 6ec1a34738e27..0000000000000 --- a/fs/cifs/cifsglob.h +++ /dev/null @@ -1,2208 +0,0 @@ -/* SPDX-License-Identifier: LGPL-2.1 */ -/* - * - * Copyright (C) International Business Machines Corp., 2002,2008 - * Author(s): Steve French (sfrench@us.ibm.com) - * Jeremy Allison (jra@samba.org) - * - */ -#ifndef _CIFS_GLOB_H -#define _CIFS_GLOB_H - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include "cifs_fs_sb.h" -#include "cifsacl.h" -#include -#include -#include -#include "../smbfs_common/smb2pdu.h" -#include "smb2pdu.h" - -#define SMB_PATH_MAX 260 -#define CIFS_PORT 445 -#define RFC1001_PORT 139 - -/* - * The sizes of various internal tables and strings - */ -#define MAX_UID_INFO 16 -#define MAX_SES_INFO 2 -#define MAX_TCON_INFO 4 - -#define MAX_TREE_SIZE (2 + CIFS_NI_MAXHOST + 1 + CIFS_MAX_SHARE_LEN + 1) - -#define CIFS_MIN_RCV_POOL 4 - -#define MAX_REOPEN_ATT 5 /* these many maximum attempts to reopen a file */ -/* - * default attribute cache timeout (jiffies) - */ -#define CIFS_DEF_ACTIMEO (1 * HZ) - -/* - * max attribute cache timeout (jiffies) - 2^30 - */ -#define CIFS_MAX_ACTIMEO (1 << 30) - -/* - * Max persistent and resilient handle timeout (milliseconds). - * Windows durable max was 960000 (16 minutes) - */ -#define SMB3_MAX_HANDLE_TIMEOUT 960000 - -/* - * MAX_REQ is the maximum number of requests that WE will send - * on one socket concurrently. - */ -#define CIFS_MAX_REQ 32767 - -#define RFC1001_NAME_LEN 15 -#define RFC1001_NAME_LEN_WITH_NULL (RFC1001_NAME_LEN + 1) - -/* maximum length of ip addr as a string (including ipv6 and sctp) */ -#define SERVER_NAME_LENGTH 80 -#define SERVER_NAME_LEN_WITH_NULL (SERVER_NAME_LENGTH + 1) - -/* echo interval in seconds */ -#define SMB_ECHO_INTERVAL_MIN 1 -#define SMB_ECHO_INTERVAL_MAX 600 -#define SMB_ECHO_INTERVAL_DEFAULT 60 - -/* dns resolution intervals in seconds */ -#define SMB_DNS_RESOLVE_INTERVAL_MIN 120 -#define SMB_DNS_RESOLVE_INTERVAL_DEFAULT 600 - -/* smb multichannel query server interfaces interval in seconds */ -#define SMB_INTERFACE_POLL_INTERVAL 600 - -/* maximum number of PDUs in one compound */ -#define MAX_COMPOUND 5 - -/* - * Default number of credits to keep available for SMB3. - * This value is chosen somewhat arbitrarily. The Windows client - * defaults to 128 credits, the Windows server allows clients up to - * 512 credits (or 8K for later versions), and the NetApp server - * does not limit clients at all. Choose a high enough default value - * such that the client shouldn't limit performance, but allow mount - * to override (until you approach 64K, where we limit credits to 65000 - * to reduce possibility of seeing more server credit overflow bugs. - */ -#define SMB2_MAX_CREDITS_AVAILABLE 32000 - -#include "cifspdu.h" - -#ifndef XATTR_DOS_ATTRIB -#define XATTR_DOS_ATTRIB "user.DOSATTRIB" -#endif - -#define CIFS_MAX_WORKSTATION_LEN (__NEW_UTS_LEN + 1) /* reasonable max for client */ - -/* - * CIFS vfs client Status information (based on what we know.) - */ - -/* associated with each connection */ -enum statusEnum { - CifsNew = 0, - CifsGood, - CifsExiting, - CifsNeedReconnect, - CifsNeedNegotiate, - CifsInNegotiate, -}; - -/* associated with each smb session */ -enum ses_status_enum { - SES_NEW = 0, - SES_GOOD, - SES_EXITING, - SES_NEED_RECON, - SES_IN_SETUP -}; - -/* associated with each tree connection to the server */ -enum tid_status_enum { - TID_NEW = 0, - TID_GOOD, - TID_EXITING, - TID_NEED_RECON, - TID_NEED_TCON, - TID_IN_TCON, - TID_NEED_FILES_INVALIDATE, /* currently unused */ - TID_IN_FILES_INVALIDATE -}; - -enum securityEnum { - Unspecified = 0, /* not specified */ - NTLMv2, /* Legacy NTLM auth with NTLMv2 hash */ - RawNTLMSSP, /* NTLMSSP without SPNEGO, NTLMv2 hash */ - Kerberos, /* Kerberos via SPNEGO */ -}; - -struct session_key { - unsigned int len; - char *response; -}; - -/* crypto hashing related structure/fields, not specific to a sec mech */ -struct cifs_secmech { - struct shash_desc *hmacmd5; /* hmacmd5 hash function, for NTLMv2/CR1 hashes */ - struct shash_desc *md5; /* md5 hash function, for CIFS/SMB1 signatures */ - struct shash_desc *hmacsha256; /* hmac-sha256 hash function, for SMB2 signatures */ - struct shash_desc *sha512; /* sha512 hash function, for SMB3.1.1 preauth hash */ - struct shash_desc *aes_cmac; /* block-cipher based MAC function, for SMB3 signatures */ - - struct crypto_aead *enc; /* smb3 encryption AEAD TFM (AES-CCM and AES-GCM) */ - struct crypto_aead *dec; /* smb3 decryption AEAD TFM (AES-CCM and AES-GCM) */ -}; - -/* per smb session structure/fields */ -struct ntlmssp_auth { - bool sesskey_per_smbsess; /* whether session key is per smb session */ - __u32 client_flags; /* sent by client in type 1 ntlmsssp exchange */ - __u32 server_flags; /* sent by server in type 2 ntlmssp exchange */ - unsigned char ciphertext[CIFS_CPHTXT_SIZE]; /* sent to server */ - char cryptkey[CIFS_CRYPTO_KEY_SIZE]; /* used by ntlmssp */ -}; - -struct cifs_cred { - int uid; - int gid; - int mode; - int cecount; - struct cifs_sid osid; - struct cifs_sid gsid; - struct cifs_ntace *ntaces; - struct cifs_ace *aces; -}; - -struct cifs_open_info_data { - char *symlink_target; - union { - struct smb2_file_all_info fi; - struct smb311_posix_qinfo posix_fi; - }; -}; - -static inline void cifs_free_open_info(struct cifs_open_info_data *data) -{ - kfree(data->symlink_target); -} - -/* - ***************************************************************** - * Except the CIFS PDUs themselves all the - * globally interesting structs should go here - ***************************************************************** - */ - -/* - * A smb_rqst represents a complete request to be issued to a server. It's - * formed by a kvec array, followed by an array of pages. Page data is assumed - * to start at the beginning of the first page. - */ -struct smb_rqst { - struct kvec *rq_iov; /* array of kvecs */ - unsigned int rq_nvec; /* number of kvecs in array */ - struct page **rq_pages; /* pointer to array of page ptrs */ - unsigned int rq_offset; /* the offset to the 1st page */ - unsigned int rq_npages; /* number pages in array */ - unsigned int rq_pagesz; /* page size to use */ - unsigned int rq_tailsz; /* length of last page */ -}; - -struct mid_q_entry; -struct TCP_Server_Info; -struct cifsFileInfo; -struct cifs_ses; -struct cifs_tcon; -struct dfs_info3_param; -struct cifs_fattr; -struct smb3_fs_context; -struct cifs_fid; -struct cifs_readdata; -struct cifs_writedata; -struct cifs_io_parms; -struct cifs_search_info; -struct cifsInodeInfo; -struct cifs_open_parms; -struct cifs_credits; - -struct smb_version_operations { - int (*send_cancel)(struct TCP_Server_Info *, struct smb_rqst *, - struct mid_q_entry *); - bool (*compare_fids)(struct cifsFileInfo *, struct cifsFileInfo *); - /* setup request: allocate mid, sign message */ - struct mid_q_entry *(*setup_request)(struct cifs_ses *, - struct TCP_Server_Info *, - struct smb_rqst *); - /* setup async request: allocate mid, sign message */ - struct mid_q_entry *(*setup_async_request)(struct TCP_Server_Info *, - struct smb_rqst *); - /* check response: verify signature, map error */ - int (*check_receive)(struct mid_q_entry *, struct TCP_Server_Info *, - bool); - void (*add_credits)(struct TCP_Server_Info *server, - const struct cifs_credits *credits, - const int optype); - void (*set_credits)(struct TCP_Server_Info *, const int); - int * (*get_credits_field)(struct TCP_Server_Info *, const int); - unsigned int (*get_credits)(struct mid_q_entry *); - __u64 (*get_next_mid)(struct TCP_Server_Info *); - void (*revert_current_mid)(struct TCP_Server_Info *server, - const unsigned int val); - /* data offset from read response message */ - unsigned int (*read_data_offset)(char *); - /* - * Data length from read response message - * When in_remaining is true, the returned data length is in - * message field DataRemaining for out-of-band data read (e.g through - * Memory Registration RDMA write in SMBD). - * Otherwise, the returned data length is in message field DataLength. - */ - unsigned int (*read_data_length)(char *, bool in_remaining); - /* map smb to linux error */ - int (*map_error)(char *, bool); - /* find mid corresponding to the response message */ - struct mid_q_entry * (*find_mid)(struct TCP_Server_Info *, char *); - void (*dump_detail)(void *buf, struct TCP_Server_Info *ptcp_info); - void (*clear_stats)(struct cifs_tcon *); - void (*print_stats)(struct seq_file *m, struct cifs_tcon *); - void (*dump_share_caps)(struct seq_file *, struct cifs_tcon *); - /* verify the message */ - int (*check_message)(char *, unsigned int, struct TCP_Server_Info *); - bool (*is_oplock_break)(char *, struct TCP_Server_Info *); - int (*handle_cancelled_mid)(struct mid_q_entry *, struct TCP_Server_Info *); - void (*downgrade_oplock)(struct TCP_Server_Info *server, - struct cifsInodeInfo *cinode, __u32 oplock, - unsigned int epoch, bool *purge_cache); - /* process transaction2 response */ - bool (*check_trans2)(struct mid_q_entry *, struct TCP_Server_Info *, - char *, int); - /* check if we need to negotiate */ - bool (*need_neg)(struct TCP_Server_Info *); - /* negotiate to the server */ - int (*negotiate)(const unsigned int xid, - struct cifs_ses *ses, - struct TCP_Server_Info *server); - /* set negotiated write size */ - unsigned int (*negotiate_wsize)(struct cifs_tcon *tcon, struct smb3_fs_context *ctx); - /* set negotiated read size */ - unsigned int (*negotiate_rsize)(struct cifs_tcon *tcon, struct smb3_fs_context *ctx); - /* setup smb sessionn */ - int (*sess_setup)(const unsigned int, struct cifs_ses *, - struct TCP_Server_Info *server, - const struct nls_table *); - /* close smb session */ - int (*logoff)(const unsigned int, struct cifs_ses *); - /* connect to a server share */ - int (*tree_connect)(const unsigned int, struct cifs_ses *, const char *, - struct cifs_tcon *, const struct nls_table *); - /* close tree connecion */ - int (*tree_disconnect)(const unsigned int, struct cifs_tcon *); - /* get DFS referrals */ - int (*get_dfs_refer)(const unsigned int, struct cifs_ses *, - const char *, struct dfs_info3_param **, - unsigned int *, const struct nls_table *, int); - /* informational QFS call */ - void (*qfs_tcon)(const unsigned int, struct cifs_tcon *, - struct cifs_sb_info *); - /* check if a path is accessible or not */ - int (*is_path_accessible)(const unsigned int, struct cifs_tcon *, - struct cifs_sb_info *, const char *); - /* query path data from the server */ - int (*query_path_info)(const unsigned int xid, struct cifs_tcon *tcon, - struct cifs_sb_info *cifs_sb, const char *full_path, - struct cifs_open_info_data *data, bool *adjust_tz, bool *reparse); - /* query file data from the server */ - int (*query_file_info)(const unsigned int xid, struct cifs_tcon *tcon, - struct cifsFileInfo *cfile, struct cifs_open_info_data *data); - /* query reparse tag from srv to determine which type of special file */ - int (*query_reparse_tag)(const unsigned int xid, struct cifs_tcon *tcon, - struct cifs_sb_info *cifs_sb, const char *path, - __u32 *reparse_tag); - /* get server index number */ - int (*get_srv_inum)(const unsigned int xid, struct cifs_tcon *tcon, - struct cifs_sb_info *cifs_sb, const char *full_path, u64 *uniqueid, - struct cifs_open_info_data *data); - /* set size by path */ - int (*set_path_size)(const unsigned int, struct cifs_tcon *, - const char *, __u64, struct cifs_sb_info *, bool); - /* set size by file handle */ - int (*set_file_size)(const unsigned int, struct cifs_tcon *, - struct cifsFileInfo *, __u64, bool); - /* set attributes */ - int (*set_file_info)(struct inode *, const char *, FILE_BASIC_INFO *, - const unsigned int); - int (*set_compression)(const unsigned int, struct cifs_tcon *, - struct cifsFileInfo *); - /* check if we can send an echo or nor */ - bool (*can_echo)(struct TCP_Server_Info *); - /* send echo request */ - int (*echo)(struct TCP_Server_Info *); - /* create directory */ - int (*posix_mkdir)(const unsigned int xid, struct inode *inode, - umode_t mode, struct cifs_tcon *tcon, - const char *full_path, - struct cifs_sb_info *cifs_sb); - int (*mkdir)(const unsigned int xid, struct inode *inode, umode_t mode, - struct cifs_tcon *tcon, const char *name, - struct cifs_sb_info *sb); - /* set info on created directory */ - void (*mkdir_setinfo)(struct inode *, const char *, - struct cifs_sb_info *, struct cifs_tcon *, - const unsigned int); - /* remove directory */ - int (*rmdir)(const unsigned int, struct cifs_tcon *, const char *, - struct cifs_sb_info *); - /* unlink file */ - int (*unlink)(const unsigned int, struct cifs_tcon *, const char *, - struct cifs_sb_info *); - /* open, rename and delete file */ - int (*rename_pending_delete)(const char *, struct dentry *, - const unsigned int); - /* send rename request */ - int (*rename)(const unsigned int, struct cifs_tcon *, const char *, - const char *, struct cifs_sb_info *); - /* send create hardlink request */ - int (*create_hardlink)(const unsigned int, struct cifs_tcon *, - const char *, const char *, - struct cifs_sb_info *); - /* query symlink target */ - int (*query_symlink)(const unsigned int, struct cifs_tcon *, - struct cifs_sb_info *, const char *, - char **, bool); - /* open a file for non-posix mounts */ - int (*open)(const unsigned int xid, struct cifs_open_parms *oparms, __u32 *oplock, - void *buf); - /* set fid protocol-specific info */ - void (*set_fid)(struct cifsFileInfo *, struct cifs_fid *, __u32); - /* close a file */ - void (*close)(const unsigned int, struct cifs_tcon *, - struct cifs_fid *); - /* close a file, returning file attributes and timestamps */ - void (*close_getattr)(const unsigned int xid, struct cifs_tcon *tcon, - struct cifsFileInfo *pfile_info); - /* send a flush request to the server */ - int (*flush)(const unsigned int, struct cifs_tcon *, struct cifs_fid *); - /* async read from the server */ - int (*async_readv)(struct cifs_readdata *); - /* async write to the server */ - int (*async_writev)(struct cifs_writedata *, - void (*release)(struct kref *)); - /* sync read from the server */ - int (*sync_read)(const unsigned int, struct cifs_fid *, - struct cifs_io_parms *, unsigned int *, char **, - int *); - /* sync write to the server */ - int (*sync_write)(const unsigned int, struct cifs_fid *, - struct cifs_io_parms *, unsigned int *, struct kvec *, - unsigned long); - /* open dir, start readdir */ - int (*query_dir_first)(const unsigned int, struct cifs_tcon *, - const char *, struct cifs_sb_info *, - struct cifs_fid *, __u16, - struct cifs_search_info *); - /* continue readdir */ - int (*query_dir_next)(const unsigned int, struct cifs_tcon *, - struct cifs_fid *, - __u16, struct cifs_search_info *srch_inf); - /* close dir */ - int (*close_dir)(const unsigned int, struct cifs_tcon *, - struct cifs_fid *); - /* calculate a size of SMB message */ - unsigned int (*calc_smb_size)(void *buf); - /* check for STATUS_PENDING and process the response if yes */ - bool (*is_status_pending)(char *buf, struct TCP_Server_Info *server); - /* check for STATUS_NETWORK_SESSION_EXPIRED */ - bool (*is_session_expired)(char *); - /* send oplock break response */ - int (*oplock_response)(struct cifs_tcon *tcon, __u64 persistent_fid, __u64 volatile_fid, - __u16 net_fid, struct cifsInodeInfo *cifs_inode); - /* query remote filesystem */ - int (*queryfs)(const unsigned int, struct cifs_tcon *, - struct cifs_sb_info *, struct kstatfs *); - /* send mandatory brlock to the server */ - int (*mand_lock)(const unsigned int, struct cifsFileInfo *, __u64, - __u64, __u32, int, int, bool); - /* unlock range of mandatory locks */ - int (*mand_unlock_range)(struct cifsFileInfo *, struct file_lock *, - const unsigned int); - /* push brlocks from the cache to the server */ - int (*push_mand_locks)(struct cifsFileInfo *); - /* get lease key of the inode */ - void (*get_lease_key)(struct inode *, struct cifs_fid *); - /* set lease key of the inode */ - void (*set_lease_key)(struct inode *, struct cifs_fid *); - /* generate new lease key */ - void (*new_lease_key)(struct cifs_fid *); - int (*generate_signingkey)(struct cifs_ses *ses, - struct TCP_Server_Info *server); - int (*calc_signature)(struct smb_rqst *, struct TCP_Server_Info *, - bool allocate_crypto); - int (*set_integrity)(const unsigned int, struct cifs_tcon *tcon, - struct cifsFileInfo *src_file); - int (*enum_snapshots)(const unsigned int xid, struct cifs_tcon *tcon, - struct cifsFileInfo *src_file, void __user *); - int (*notify)(const unsigned int xid, struct file *pfile, - void __user *pbuf, bool return_changes); - int (*query_mf_symlink)(unsigned int, struct cifs_tcon *, - struct cifs_sb_info *, const unsigned char *, - char *, unsigned int *); - int (*create_mf_symlink)(unsigned int, struct cifs_tcon *, - struct cifs_sb_info *, const unsigned char *, - char *, unsigned int *); - /* if we can do cache read operations */ - bool (*is_read_op)(__u32); - /* set oplock level for the inode */ - void (*set_oplock_level)(struct cifsInodeInfo *, __u32, unsigned int, - bool *); - /* create lease context buffer for CREATE request */ - char * (*create_lease_buf)(u8 *lease_key, u8 oplock); - /* parse lease context buffer and return oplock/epoch info */ - __u8 (*parse_lease_buf)(void *buf, unsigned int *epoch, char *lkey); - ssize_t (*copychunk_range)(const unsigned int, - struct cifsFileInfo *src_file, - struct cifsFileInfo *target_file, - u64 src_off, u64 len, u64 dest_off); - int (*duplicate_extents)(const unsigned int, struct cifsFileInfo *src, - struct cifsFileInfo *target_file, u64 src_off, u64 len, - u64 dest_off); - int (*validate_negotiate)(const unsigned int, struct cifs_tcon *); - ssize_t (*query_all_EAs)(const unsigned int, struct cifs_tcon *, - const unsigned char *, const unsigned char *, char *, - size_t, struct cifs_sb_info *); - int (*set_EA)(const unsigned int, struct cifs_tcon *, const char *, - const char *, const void *, const __u16, - const struct nls_table *, struct cifs_sb_info *); - struct cifs_ntsd * (*get_acl)(struct cifs_sb_info *, struct inode *, - const char *, u32 *, u32); - struct cifs_ntsd * (*get_acl_by_fid)(struct cifs_sb_info *, - const struct cifs_fid *, u32 *, u32); - int (*set_acl)(struct cifs_ntsd *, __u32, struct inode *, const char *, - int); - /* writepages retry size */ - unsigned int (*wp_retry_size)(struct inode *); - /* get mtu credits */ - int (*wait_mtu_credits)(struct TCP_Server_Info *, unsigned int, - unsigned int *, struct cifs_credits *); - /* adjust previously taken mtu credits to request size */ - int (*adjust_credits)(struct TCP_Server_Info *server, - struct cifs_credits *credits, - const unsigned int payload_size); - /* check if we need to issue closedir */ - bool (*dir_needs_close)(struct cifsFileInfo *); - long (*fallocate)(struct file *, struct cifs_tcon *, int, loff_t, - loff_t); - /* init transform request - used for encryption for now */ - int (*init_transform_rq)(struct TCP_Server_Info *, int num_rqst, - struct smb_rqst *, struct smb_rqst *); - int (*is_transform_hdr)(void *buf); - int (*receive_transform)(struct TCP_Server_Info *, - struct mid_q_entry **, char **, int *); - enum securityEnum (*select_sectype)(struct TCP_Server_Info *, - enum securityEnum); - int (*next_header)(char *); - /* ioctl passthrough for query_info */ - int (*ioctl_query_info)(const unsigned int xid, - struct cifs_tcon *tcon, - struct cifs_sb_info *cifs_sb, - __le16 *path, int is_dir, - unsigned long p); - /* make unix special files (block, char, fifo, socket) */ - int (*make_node)(unsigned int xid, - struct inode *inode, - struct dentry *dentry, - struct cifs_tcon *tcon, - const char *full_path, - umode_t mode, - dev_t device_number); - /* version specific fiemap implementation */ - int (*fiemap)(struct cifs_tcon *tcon, struct cifsFileInfo *, - struct fiemap_extent_info *, u64, u64); - /* version specific llseek implementation */ - loff_t (*llseek)(struct file *, struct cifs_tcon *, loff_t, int); - /* Check for STATUS_IO_TIMEOUT */ - bool (*is_status_io_timeout)(char *buf); - /* Check for STATUS_NETWORK_NAME_DELETED */ - void (*is_network_name_deleted)(char *buf, struct TCP_Server_Info *srv); -}; - -struct smb_version_values { - char *version_string; - __u16 protocol_id; - __u32 req_capabilities; - __u32 large_lock_type; - __u32 exclusive_lock_type; - __u32 shared_lock_type; - __u32 unlock_lock_type; - size_t header_preamble_size; - size_t header_size; - size_t max_header_size; - size_t read_rsp_size; - __le16 lock_cmd; - unsigned int cap_unix; - unsigned int cap_nt_find; - unsigned int cap_large_files; - __u16 signing_enabled; - __u16 signing_required; - size_t create_lease_size; -}; - -#define HEADER_SIZE(server) (server->vals->header_size) -#define MAX_HEADER_SIZE(server) (server->vals->max_header_size) -#define HEADER_PREAMBLE_SIZE(server) (server->vals->header_preamble_size) -#define MID_HEADER_SIZE(server) (HEADER_SIZE(server) - 1 - HEADER_PREAMBLE_SIZE(server)) - -/** - * CIFS superblock mount flags (mnt_cifs_flags) to consider when - * trying to reuse existing superblock for a new mount - */ -#define CIFS_MOUNT_MASK (CIFS_MOUNT_NO_PERM | CIFS_MOUNT_SET_UID | \ - CIFS_MOUNT_SERVER_INUM | CIFS_MOUNT_DIRECT_IO | \ - CIFS_MOUNT_NO_XATTR | CIFS_MOUNT_MAP_SPECIAL_CHR | \ - CIFS_MOUNT_MAP_SFM_CHR | \ - CIFS_MOUNT_UNX_EMUL | CIFS_MOUNT_NO_BRL | \ - CIFS_MOUNT_CIFS_ACL | CIFS_MOUNT_OVERR_UID | \ - CIFS_MOUNT_OVERR_GID | CIFS_MOUNT_DYNPERM | \ - CIFS_MOUNT_NOPOSIXBRL | CIFS_MOUNT_NOSSYNC | \ - CIFS_MOUNT_FSCACHE | CIFS_MOUNT_MF_SYMLINKS | \ - CIFS_MOUNT_MULTIUSER | CIFS_MOUNT_STRICT_IO | \ - CIFS_MOUNT_CIFS_BACKUPUID | CIFS_MOUNT_CIFS_BACKUPGID | \ - CIFS_MOUNT_UID_FROM_ACL | CIFS_MOUNT_NO_HANDLE_CACHE | \ - CIFS_MOUNT_NO_DFS | CIFS_MOUNT_MODE_FROM_SID | \ - CIFS_MOUNT_RO_CACHE | CIFS_MOUNT_RW_CACHE) - -/** - * Generic VFS superblock mount flags (s_flags) to consider when - * trying to reuse existing superblock for a new mount - */ -#define CIFS_MS_MASK (SB_RDONLY | SB_MANDLOCK | SB_NOEXEC | SB_NOSUID | \ - SB_NODEV | SB_SYNCHRONOUS) - -struct cifs_mnt_data { - struct cifs_sb_info *cifs_sb; - struct smb3_fs_context *ctx; - int flags; -}; - -static inline unsigned int -get_rfc1002_length(void *buf) -{ - return be32_to_cpu(*((__be32 *)buf)) & 0xffffff; -} - -static inline void -inc_rfc1001_len(void *buf, int count) -{ - be32_add_cpu((__be32 *)buf, count); -} - -struct TCP_Server_Info { - struct list_head tcp_ses_list; - struct list_head smb_ses_list; - spinlock_t srv_lock; /* protect anything here that is not protected */ - __u64 conn_id; /* connection identifier (useful for debugging) */ - int srv_count; /* reference counter */ - /* 15 character server name + 0x20 16th byte indicating type = srv */ - char server_RFC1001_name[RFC1001_NAME_LEN_WITH_NULL]; - struct smb_version_operations *ops; - struct smb_version_values *vals; - /* updates to tcpStatus protected by cifs_tcp_ses_lock */ - enum statusEnum tcpStatus; /* what we think the status is */ - char *hostname; /* hostname portion of UNC string */ - struct socket *ssocket; - struct sockaddr_storage dstaddr; - struct sockaddr_storage srcaddr; /* locally bind to this IP */ -#ifdef CONFIG_NET_NS - struct net *net; -#endif - wait_queue_head_t response_q; - wait_queue_head_t request_q; /* if more than maxmpx to srvr must block*/ - spinlock_t mid_lock; /* protect mid queue and it's entries */ - struct list_head pending_mid_q; - bool noblocksnd; /* use blocking sendmsg */ - bool noautotune; /* do not autotune send buf sizes */ - bool nosharesock; - bool tcp_nodelay; - unsigned int credits; /* send no more requests at once */ - unsigned int max_credits; /* can override large 32000 default at mnt */ - unsigned int in_flight; /* number of requests on the wire to server */ - unsigned int max_in_flight; /* max number of requests that were on wire */ - spinlock_t req_lock; /* protect the two values above */ - struct mutex _srv_mutex; - unsigned int nofs_flag; - struct task_struct *tsk; - char server_GUID[16]; - __u16 sec_mode; - bool sign; /* is signing enabled on this connection? */ - bool ignore_signature:1; /* skip validation of signatures in SMB2/3 rsp */ - bool session_estab; /* mark when very first sess is established */ - int echo_credits; /* echo reserved slots */ - int oplock_credits; /* oplock break reserved slots */ - bool echoes:1; /* enable echoes */ - __u8 client_guid[SMB2_CLIENT_GUID_SIZE]; /* Client GUID */ - u16 dialect; /* dialect index that server chose */ - bool oplocks:1; /* enable oplocks */ - unsigned int maxReq; /* Clients should submit no more */ - /* than maxReq distinct unanswered SMBs to the server when using */ - /* multiplexed reads or writes (for SMB1/CIFS only, not SMB2/SMB3) */ - unsigned int maxBuf; /* maxBuf specifies the maximum */ - /* message size the server can send or receive for non-raw SMBs */ - /* maxBuf is returned by SMB NegotiateProtocol so maxBuf is only 0 */ - /* when socket is setup (and during reconnect) before NegProt sent */ - unsigned int max_rw; /* maxRw specifies the maximum */ - /* message size the server can send or receive for */ - /* SMB_COM_WRITE_RAW or SMB_COM_READ_RAW. */ - unsigned int capabilities; /* selective disabling of caps by smb sess */ - int timeAdj; /* Adjust for difference in server time zone in sec */ - __u64 CurrentMid; /* multiplex id - rotating counter, protected by GlobalMid_Lock */ - char cryptkey[CIFS_CRYPTO_KEY_SIZE]; /* used by ntlm, ntlmv2 etc */ - /* 16th byte of RFC1001 workstation name is always null */ - char workstation_RFC1001_name[RFC1001_NAME_LEN_WITH_NULL]; - __u32 sequence_number; /* for signing, protected by srv_mutex */ - __u32 reconnect_instance; /* incremented on each reconnect */ - struct session_key session_key; - unsigned long lstrp; /* when we got last response from this server */ - struct cifs_secmech secmech; /* crypto sec mech functs, descriptors */ -#define CIFS_NEGFLAVOR_UNENCAP 1 /* wct == 17, but no ext_sec */ -#define CIFS_NEGFLAVOR_EXTENDED 2 /* wct == 17, ext_sec bit set */ - char negflavor; /* NEGOTIATE response flavor */ - /* extended security flavors that server supports */ - bool sec_ntlmssp; /* supports NTLMSSP */ - bool sec_kerberosu2u; /* supports U2U Kerberos */ - bool sec_kerberos; /* supports plain Kerberos */ - bool sec_mskerberos; /* supports legacy MS Kerberos */ - bool large_buf; /* is current buffer large? */ - /* use SMBD connection instead of socket */ - bool rdma; - /* point to the SMBD connection if RDMA is used instead of socket */ - struct smbd_connection *smbd_conn; - struct delayed_work echo; /* echo ping workqueue job */ - struct delayed_work resolve; /* dns resolution workqueue job */ - char *smallbuf; /* pointer to current "small" buffer */ - char *bigbuf; /* pointer to current "big" buffer */ - /* Total size of this PDU. Only valid from cifs_demultiplex_thread */ - unsigned int pdu_size; - unsigned int total_read; /* total amount of data read in this pass */ - atomic_t in_send; /* requests trying to send */ - atomic_t num_waiters; /* blocked waiting to get in sendrecv */ -#ifdef CONFIG_CIFS_STATS2 - atomic_t num_cmds[NUMBER_OF_SMB2_COMMANDS]; /* total requests by cmd */ - atomic_t smb2slowcmd[NUMBER_OF_SMB2_COMMANDS]; /* count resps > 1 sec */ - __u64 time_per_cmd[NUMBER_OF_SMB2_COMMANDS]; /* total time per cmd */ - __u32 slowest_cmd[NUMBER_OF_SMB2_COMMANDS]; - __u32 fastest_cmd[NUMBER_OF_SMB2_COMMANDS]; -#endif /* STATS2 */ - unsigned int max_read; - unsigned int max_write; - unsigned int min_offload; - __le16 compress_algorithm; - __u16 signing_algorithm; - __le16 cipher_type; - /* save initital negprot hash */ - __u8 preauth_sha_hash[SMB2_PREAUTH_HASH_SIZE]; - bool signing_negotiated; /* true if valid signing context rcvd from server */ - bool posix_ext_supported; - struct delayed_work reconnect; /* reconnect workqueue job */ - struct mutex reconnect_mutex; /* prevent simultaneous reconnects */ - unsigned long echo_interval; - - /* - * Number of targets available for reconnect. The more targets - * the more tasks have to wait to let the demultiplex thread - * reconnect. - */ - int nr_targets; - bool noblockcnt; /* use non-blocking connect() */ - - /* - * If this is a session channel, - * primary_server holds the ref-counted - * pointer to primary channel connection for the session. - */ -#define CIFS_SERVER_IS_CHAN(server) (!!(server)->primary_server) - struct TCP_Server_Info *primary_server; - -#ifdef CONFIG_CIFS_SWN_UPCALL - bool use_swn_dstaddr; - struct sockaddr_storage swn_dstaddr; -#endif -#ifdef CONFIG_CIFS_DFS_UPCALL - bool is_dfs_conn; /* if a dfs connection */ - struct mutex refpath_lock; /* protects leaf_fullpath */ - /* - * Canonical DFS full paths that were used to chase referrals in mount and reconnect. - * - * origin_fullpath: first or original referral path - * leaf_fullpath: last referral path (might be changed due to nested links in reconnect) - * - * current_fullpath: pointer to either origin_fullpath or leaf_fullpath - * NOTE: cannot be accessed outside cifs_reconnect() and smb2_reconnect() - * - * format: \\HOST\SHARE\[OPTIONAL PATH] - */ - char *origin_fullpath, *leaf_fullpath, *current_fullpath; -#endif -}; - -static inline bool is_smb1(struct TCP_Server_Info *server) -{ - return HEADER_PREAMBLE_SIZE(server) != 0; -} - -static inline void cifs_server_lock(struct TCP_Server_Info *server) -{ - unsigned int nofs_flag = memalloc_nofs_save(); - - mutex_lock(&server->_srv_mutex); - server->nofs_flag = nofs_flag; -} - -static inline void cifs_server_unlock(struct TCP_Server_Info *server) -{ - unsigned int nofs_flag = server->nofs_flag; - - mutex_unlock(&server->_srv_mutex); - memalloc_nofs_restore(nofs_flag); -} - -struct cifs_credits { - unsigned int value; - unsigned int instance; -}; - -static inline unsigned int -in_flight(struct TCP_Server_Info *server) -{ - unsigned int num; - spin_lock(&server->req_lock); - num = server->in_flight; - spin_unlock(&server->req_lock); - return num; -} - -static inline bool -has_credits(struct TCP_Server_Info *server, int *credits, int num_credits) -{ - int num; - spin_lock(&server->req_lock); - num = *credits; - spin_unlock(&server->req_lock); - return num >= num_credits; -} - -static inline void -add_credits(struct TCP_Server_Info *server, const struct cifs_credits *credits, - const int optype) -{ - server->ops->add_credits(server, credits, optype); -} - -static inline void -add_credits_and_wake_if(struct TCP_Server_Info *server, - const struct cifs_credits *credits, const int optype) -{ - if (credits->value) { - server->ops->add_credits(server, credits, optype); - wake_up(&server->request_q); - } -} - -static inline void -set_credits(struct TCP_Server_Info *server, const int val) -{ - server->ops->set_credits(server, val); -} - -static inline int -adjust_credits(struct TCP_Server_Info *server, struct cifs_credits *credits, - const unsigned int payload_size) -{ - return server->ops->adjust_credits ? - server->ops->adjust_credits(server, credits, payload_size) : 0; -} - -static inline __le64 -get_next_mid64(struct TCP_Server_Info *server) -{ - return cpu_to_le64(server->ops->get_next_mid(server)); -} - -static inline __le16 -get_next_mid(struct TCP_Server_Info *server) -{ - __u16 mid = server->ops->get_next_mid(server); - /* - * The value in the SMB header should be little endian for easy - * on-the-wire decoding. - */ - return cpu_to_le16(mid); -} - -static inline void -revert_current_mid(struct TCP_Server_Info *server, const unsigned int val) -{ - if (server->ops->revert_current_mid) - server->ops->revert_current_mid(server, val); -} - -static inline void -revert_current_mid_from_hdr(struct TCP_Server_Info *server, - const struct smb2_hdr *shdr) -{ - unsigned int num = le16_to_cpu(shdr->CreditCharge); - - return revert_current_mid(server, num > 0 ? num : 1); -} - -static inline __u16 -get_mid(const struct smb_hdr *smb) -{ - return le16_to_cpu(smb->Mid); -} - -static inline bool -compare_mid(__u16 mid, const struct smb_hdr *smb) -{ - return mid == le16_to_cpu(smb->Mid); -} - -/* - * When the server supports very large reads and writes via POSIX extensions, - * we can allow up to 2^24-1, minus the size of a READ/WRITE_AND_X header, not - * including the RFC1001 length. - * - * Note that this might make for "interesting" allocation problems during - * writeback however as we have to allocate an array of pointers for the - * pages. A 16M write means ~32kb page array with PAGE_SIZE == 4096. - * - * For reads, there is a similar problem as we need to allocate an array - * of kvecs to handle the receive, though that should only need to be done - * once. - */ -#define CIFS_MAX_WSIZE ((1<<24) - 1 - sizeof(WRITE_REQ) + 4) -#define CIFS_MAX_RSIZE ((1<<24) - sizeof(READ_RSP) + 4) - -/* - * When the server doesn't allow large posix writes, only allow a rsize/wsize - * of 2^17-1 minus the size of the call header. That allows for a read or - * write up to the maximum size described by RFC1002. - */ -#define CIFS_MAX_RFC1002_WSIZE ((1<<17) - 1 - sizeof(WRITE_REQ) + 4) -#define CIFS_MAX_RFC1002_RSIZE ((1<<17) - 1 - sizeof(READ_RSP) + 4) - -#define CIFS_DEFAULT_IOSIZE (1024 * 1024) - -/* - * Windows only supports a max of 60kb reads and 65535 byte writes. Default to - * those values when posix extensions aren't in force. In actuality here, we - * use 65536 to allow for a write that is a multiple of 4k. Most servers seem - * to be ok with the extra byte even though Windows doesn't send writes that - * are that large. - * - * Citation: - * - * https://blogs.msdn.com/b/openspecification/archive/2009/04/10/smb-maximum-transmit-buffer-size-and-performance-tuning.aspx - */ -#define CIFS_DEFAULT_NON_POSIX_RSIZE (60 * 1024) -#define CIFS_DEFAULT_NON_POSIX_WSIZE (65536) - -/* - * Macros to allow the TCP_Server_Info->net field and related code to drop out - * when CONFIG_NET_NS isn't set. - */ - -#ifdef CONFIG_NET_NS - -static inline struct net *cifs_net_ns(struct TCP_Server_Info *srv) -{ - return srv->net; -} - -static inline void cifs_set_net_ns(struct TCP_Server_Info *srv, struct net *net) -{ - srv->net = net; -} - -#else - -static inline struct net *cifs_net_ns(struct TCP_Server_Info *srv) -{ - return &init_net; -} - -static inline void cifs_set_net_ns(struct TCP_Server_Info *srv, struct net *net) -{ -} - -#endif - -struct cifs_server_iface { - struct list_head iface_head; - struct kref refcount; - size_t speed; - unsigned int rdma_capable : 1; - unsigned int rss_capable : 1; - unsigned int is_active : 1; /* unset if non existent */ - struct sockaddr_storage sockaddr; -}; - -/* release iface when last ref is dropped */ -static inline void -release_iface(struct kref *ref) -{ - struct cifs_server_iface *iface = container_of(ref, - struct cifs_server_iface, - refcount); - list_del_init(&iface->iface_head); - kfree(iface); -} - -/* - * compare two interfaces a and b - * return 0 if everything matches. - * return 1 if a has higher link speed, or rdma capable, or rss capable - * return -1 otherwise. - */ -static inline int -iface_cmp(struct cifs_server_iface *a, struct cifs_server_iface *b) -{ - int cmp_ret = 0; - - WARN_ON(!a || !b); - if (a->speed == b->speed) { - if (a->rdma_capable == b->rdma_capable) { - if (a->rss_capable == b->rss_capable) { - cmp_ret = memcmp(&a->sockaddr, &b->sockaddr, - sizeof(a->sockaddr)); - if (!cmp_ret) - return 0; - else if (cmp_ret > 0) - return 1; - else - return -1; - } else if (a->rss_capable > b->rss_capable) - return 1; - else - return -1; - } else if (a->rdma_capable > b->rdma_capable) - return 1; - else - return -1; - } else if (a->speed > b->speed) - return 1; - else - return -1; -} - -struct cifs_chan { - unsigned int in_reconnect : 1; /* if session setup in progress for this channel */ - struct TCP_Server_Info *server; - struct cifs_server_iface *iface; /* interface in use */ - __u8 signkey[SMB3_SIGN_KEY_SIZE]; -}; - -/* - * Session structure. One of these for each uid session with a particular host - */ -struct cifs_ses { - struct list_head smb_ses_list; - struct list_head rlist; /* reconnect list */ - struct list_head tcon_list; - struct cifs_tcon *tcon_ipc; - spinlock_t ses_lock; /* protect anything here that is not protected */ - struct mutex session_mutex; - struct TCP_Server_Info *server; /* pointer to server info */ - int ses_count; /* reference counter */ - enum ses_status_enum ses_status; /* updates protected by cifs_tcp_ses_lock */ - unsigned overrideSecFlg; /* if non-zero override global sec flags */ - char *serverOS; /* name of operating system underlying server */ - char *serverNOS; /* name of network operating system of server */ - char *serverDomain; /* security realm of server */ - __u64 Suid; /* remote smb uid */ - kuid_t linux_uid; /* overriding owner of files on the mount */ - kuid_t cred_uid; /* owner of credentials */ - unsigned int capabilities; - char ip_addr[INET6_ADDRSTRLEN + 1]; /* Max ipv6 (or v4) addr string len */ - char *user_name; /* must not be null except during init of sess - and after mount option parsing we fill it */ - char *domainName; - char *password; - char workstation_name[CIFS_MAX_WORKSTATION_LEN]; - struct session_key auth_key; - struct ntlmssp_auth *ntlmssp; /* ciphertext, flags, server challenge */ - enum securityEnum sectype; /* what security flavor was specified? */ - bool sign; /* is signing required? */ - bool domainAuto:1; - __u16 session_flags; - __u8 smb3signingkey[SMB3_SIGN_KEY_SIZE]; - __u8 smb3encryptionkey[SMB3_ENC_DEC_KEY_SIZE]; - __u8 smb3decryptionkey[SMB3_ENC_DEC_KEY_SIZE]; - __u8 preauth_sha_hash[SMB2_PREAUTH_HASH_SIZE]; - - /* - * Network interfaces available on the server this session is - * connected to. - * - * Other channels can be opened by connecting and binding this - * session to interfaces from this list. - * - * iface_lock should be taken when accessing any of these fields - */ - spinlock_t iface_lock; - /* ========= begin: protected by iface_lock ======== */ - struct list_head iface_list; - size_t iface_count; - unsigned long iface_last_update; /* jiffies */ - /* ========= end: protected by iface_lock ======== */ - - spinlock_t chan_lock; - /* ========= begin: protected by chan_lock ======== */ -#define CIFS_MAX_CHANNELS 16 -#define CIFS_ALL_CHANNELS_SET(ses) \ - ((1UL << (ses)->chan_count) - 1) -#define CIFS_ALL_CHANS_GOOD(ses) \ - (!(ses)->chans_need_reconnect) -#define CIFS_ALL_CHANS_NEED_RECONNECT(ses) \ - ((ses)->chans_need_reconnect == CIFS_ALL_CHANNELS_SET(ses)) -#define CIFS_SET_ALL_CHANS_NEED_RECONNECT(ses) \ - ((ses)->chans_need_reconnect = CIFS_ALL_CHANNELS_SET(ses)) -#define CIFS_CHAN_NEEDS_RECONNECT(ses, index) \ - test_bit((index), &(ses)->chans_need_reconnect) -#define CIFS_CHAN_IN_RECONNECT(ses, index) \ - ((ses)->chans[(index)].in_reconnect) - - struct cifs_chan chans[CIFS_MAX_CHANNELS]; - size_t chan_count; - size_t chan_max; - atomic_t chan_seq; /* round robin state */ - - /* - * chans_need_reconnect is a bitmap indicating which of the channels - * under this smb session needs to be reconnected. - * If not multichannel session, only one bit will be used. - * - * We will ask for sess and tcon reconnection only if all the - * channels are marked for needing reconnection. This will - * enable the sessions on top to continue to live till any - * of the channels below are active. - */ - unsigned long chans_need_reconnect; - /* ========= end: protected by chan_lock ======== */ -}; - -static inline bool -cap_unix(struct cifs_ses *ses) -{ - return ses->server->vals->cap_unix & ses->capabilities; -} - -/* - * common struct for holding inode info when searching for or updating an - * inode with new info - */ - -#define CIFS_FATTR_DFS_REFERRAL 0x1 -#define CIFS_FATTR_DELETE_PENDING 0x2 -#define CIFS_FATTR_NEED_REVAL 0x4 -#define CIFS_FATTR_INO_COLLISION 0x8 -#define CIFS_FATTR_UNKNOWN_NLINK 0x10 -#define CIFS_FATTR_FAKE_ROOT_INO 0x20 - -struct cifs_fattr { - u32 cf_flags; - u32 cf_cifsattrs; - u64 cf_uniqueid; - u64 cf_eof; - u64 cf_bytes; - u64 cf_createtime; - kuid_t cf_uid; - kgid_t cf_gid; - umode_t cf_mode; - dev_t cf_rdev; - unsigned int cf_nlink; - unsigned int cf_dtype; - struct timespec64 cf_atime; - struct timespec64 cf_mtime; - struct timespec64 cf_ctime; - u32 cf_cifstag; - char *cf_symlink_target; -}; - -/* - * there is one of these for each connection to a resource on a particular - * session - */ -struct cifs_tcon { - struct list_head tcon_list; - int tc_count; - struct list_head rlist; /* reconnect list */ - spinlock_t tc_lock; /* protect anything here that is not protected */ - atomic_t num_local_opens; /* num of all opens including disconnected */ - atomic_t num_remote_opens; /* num of all network opens on server */ - struct list_head openFileList; - spinlock_t open_file_lock; /* protects list above */ - struct cifs_ses *ses; /* pointer to session associated with */ - char tree_name[MAX_TREE_SIZE + 1]; /* UNC name of resource in ASCII */ - char *nativeFileSystem; - char *password; /* for share-level security */ - __u32 tid; /* The 4 byte tree id */ - __u16 Flags; /* optional support bits */ - enum tid_status_enum status; - atomic_t num_smbs_sent; - union { - struct { - atomic_t num_writes; - atomic_t num_reads; - atomic_t num_flushes; - atomic_t num_oplock_brks; - atomic_t num_opens; - atomic_t num_closes; - atomic_t num_deletes; - atomic_t num_mkdirs; - atomic_t num_posixopens; - atomic_t num_posixmkdirs; - 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; - atomic_t num_acl_get; - atomic_t num_acl_set; - } cifs_stats; - struct { - atomic_t smb2_com_sent[NUMBER_OF_SMB2_COMMANDS]; - atomic_t smb2_com_failed[NUMBER_OF_SMB2_COMMANDS]; - } smb2_stats; - } stats; - __u64 bytes_read; - __u64 bytes_written; - spinlock_t stat_lock; /* protects the two fields above */ - FILE_SYSTEM_DEVICE_INFO fsDevInfo; - FILE_SYSTEM_ATTRIBUTE_INFO fsAttrInfo; /* ok if fs name truncated */ - FILE_SYSTEM_UNIX_INFO fsUnixInfo; - bool ipc:1; /* set if connection to IPC$ share (always also pipe) */ - bool pipe:1; /* set if connection to pipe share */ - bool print:1; /* set if connection to printer share */ - bool retry:1; - bool nocase:1; - bool nohandlecache:1; /* if strange server resource prob can turn off */ - bool nodelete:1; - bool seal:1; /* transport encryption for this mounted share */ - bool unix_ext:1; /* if false disable Linux extensions to CIFS protocol - for this mount even if server would support */ - bool posix_extensions; /* if true SMB3.11 posix extensions enabled */ - bool local_lease:1; /* check leases (only) on local system not remote */ - bool broken_posix_open; /* e.g. Samba server versions < 3.3.2, 3.2.9 */ - bool broken_sparse_sup; /* if server or share does not support sparse */ - bool need_reconnect:1; /* connection reset, tid now invalid */ - bool need_reopen_files:1; /* need to reopen tcon file handles */ - bool use_resilient:1; /* use resilient instead of durable handles */ - bool use_persistent:1; /* use persistent instead of durable handles */ - bool no_lease:1; /* Do not request leases on files or directories */ - bool use_witness:1; /* use witness protocol */ - __le32 capabilities; - __u32 share_flags; - __u32 maximal_access; - __u32 vol_serial_number; - __le64 vol_create_time; - __u64 snapshot_time; /* for timewarp tokens - timestamp of snapshot */ - __u32 handle_timeout; /* persistent and durable handle timeout in ms */ - __u32 ss_flags; /* sector size flags */ - __u32 perf_sector_size; /* best sector size for perf */ - __u32 max_chunks; - __u32 max_bytes_chunk; - __u32 max_bytes_copy; -#ifdef CONFIG_CIFS_FSCACHE - u64 resource_id; /* server resource id */ - struct fscache_volume *fscache; /* cookie for share */ -#endif - struct list_head pending_opens; /* list of incomplete opens */ - struct cached_fids *cfids; - /* BB add field for back pointer to sb struct(s)? */ -#ifdef CONFIG_CIFS_DFS_UPCALL - struct list_head ulist; /* cache update list */ -#endif - struct delayed_work query_interfaces; /* query interfaces workqueue job */ -}; - -/* - * This is a refcounted and timestamped container for a tcon pointer. The - * container holds a tcon reference. It is considered safe to free one of - * these when the tl_count goes to 0. The tl_time is the time of the last - * "get" on the container. - */ -struct tcon_link { - struct rb_node tl_rbnode; - kuid_t tl_uid; - unsigned long tl_flags; -#define TCON_LINK_MASTER 0 -#define TCON_LINK_PENDING 1 -#define TCON_LINK_IN_TREE 2 - unsigned long tl_time; - atomic_t tl_count; - struct cifs_tcon *tl_tcon; -}; - -extern struct tcon_link *cifs_sb_tlink(struct cifs_sb_info *cifs_sb); -extern void smb3_free_compound_rqst(int num_rqst, struct smb_rqst *rqst); - -static inline struct cifs_tcon * -tlink_tcon(struct tcon_link *tlink) -{ - return tlink->tl_tcon; -} - -static inline struct tcon_link * -cifs_sb_master_tlink(struct cifs_sb_info *cifs_sb) -{ - return cifs_sb->master_tlink; -} - -extern void cifs_put_tlink(struct tcon_link *tlink); - -static inline struct tcon_link * -cifs_get_tlink(struct tcon_link *tlink) -{ - if (tlink && !IS_ERR(tlink)) - atomic_inc(&tlink->tl_count); - return tlink; -} - -/* This function is always expected to succeed */ -extern struct cifs_tcon *cifs_sb_master_tcon(struct cifs_sb_info *cifs_sb); - -#define CIFS_OPLOCK_NO_CHANGE 0xfe - -struct cifs_pending_open { - struct list_head olist; - struct tcon_link *tlink; - __u8 lease_key[16]; - __u32 oplock; -}; - -struct cifs_deferred_close { - struct list_head dlist; - struct tcon_link *tlink; - __u16 netfid; - __u64 persistent_fid; - __u64 volatile_fid; -}; - -/* - * This info hangs off the cifsFileInfo structure, pointed to by llist. - * This is used to track byte stream locks on the file - */ -struct cifsLockInfo { - struct list_head llist; /* pointer to next cifsLockInfo */ - struct list_head blist; /* pointer to locks blocked on this */ - wait_queue_head_t block_q; - __u64 offset; - __u64 length; - __u32 pid; - __u16 type; - __u16 flags; -}; - -/* - * One of these for each open instance of a file - */ -struct cifs_search_info { - loff_t index_of_last_entry; - __u16 entries_in_buffer; - __u16 info_level; - __u32 resume_key; - char *ntwrk_buf_start; - char *srch_entries_start; - char *last_entry; - const char *presume_name; - unsigned int resume_name_len; - bool endOfSearch:1; - bool emptyDir:1; - bool unicode:1; - bool smallBuf:1; /* so we know which buf_release function to call */ -}; - -#define ACL_NO_MODE ((umode_t)(-1)) -struct cifs_open_parms { - struct cifs_tcon *tcon; - struct cifs_sb_info *cifs_sb; - int disposition; - int desired_access; - int create_options; - const char *path; - struct cifs_fid *fid; - umode_t mode; - bool reconnect:1; -}; - -struct cifs_fid { - __u16 netfid; - __u64 persistent_fid; /* persist file id for smb2 */ - __u64 volatile_fid; /* volatile file id for smb2 */ - __u8 lease_key[SMB2_LEASE_KEY_SIZE]; /* lease key for smb2 */ - __u8 create_guid[16]; - __u32 access; - struct cifs_pending_open *pending_open; - unsigned int epoch; -#ifdef CONFIG_CIFS_DEBUG2 - __u64 mid; -#endif /* CIFS_DEBUG2 */ - bool purge_cache; -}; - -struct cifs_fid_locks { - struct list_head llist; - struct cifsFileInfo *cfile; /* fid that owns locks */ - struct list_head locks; /* locks held by fid above */ -}; - -struct cifsFileInfo { - /* following two lists are protected by tcon->open_file_lock */ - struct list_head tlist; /* pointer to next fid owned by tcon */ - struct list_head flist; /* next fid (file instance) for this inode */ - /* lock list below protected by cifsi->lock_sem */ - struct cifs_fid_locks *llist; /* brlocks held by this fid */ - kuid_t uid; /* allows finding which FileInfo structure */ - __u32 pid; /* process id who opened file */ - struct cifs_fid fid; /* file id from remote */ - struct list_head rlist; /* reconnect list */ - /* BB add lock scope info here if needed */ ; - /* lock scope id (0 if none) */ - struct dentry *dentry; - struct tcon_link *tlink; - unsigned int f_flags; - bool invalidHandle:1; /* file closed via session abend */ - bool swapfile:1; - bool oplock_break_cancelled:1; - unsigned int oplock_epoch; /* epoch from the lease break */ - __u32 oplock_level; /* oplock/lease level from the lease break */ - int count; - spinlock_t file_info_lock; /* protects four flag/count fields above */ - struct mutex fh_mutex; /* prevents reopen race after dead ses*/ - struct cifs_search_info srch_inf; - struct work_struct oplock_break; /* work for oplock breaks */ - struct work_struct put; /* work for the final part of _put */ - struct delayed_work deferred; - bool deferred_close_scheduled; /* Flag to indicate close is scheduled */ - char *symlink_target; -}; - -struct cifs_io_parms { - __u16 netfid; - __u64 persistent_fid; /* persist file id for smb2 */ - __u64 volatile_fid; /* volatile file id for smb2 */ - __u32 pid; - __u64 offset; - unsigned int length; - struct cifs_tcon *tcon; - struct TCP_Server_Info *server; -}; - -struct cifs_aio_ctx { - struct kref refcount; - struct list_head list; - struct mutex aio_mutex; - struct completion done; - struct iov_iter iter; - struct kiocb *iocb; - struct cifsFileInfo *cfile; - struct bio_vec *bv; - loff_t pos; - unsigned int npages; - ssize_t rc; - unsigned int len; - unsigned int total_len; - bool should_dirty; - /* - * Indicates if this aio_ctx is for direct_io, - * If yes, iter is a copy of the user passed iov_iter - */ - bool direct_io; -}; - -/* asynchronous read support */ -struct cifs_readdata { - struct kref refcount; - struct list_head list; - struct completion done; - struct cifsFileInfo *cfile; - struct address_space *mapping; - struct cifs_aio_ctx *ctx; - __u64 offset; - unsigned int bytes; - unsigned int got_bytes; - pid_t pid; - int result; - struct work_struct work; - int (*read_into_pages)(struct TCP_Server_Info *server, - struct cifs_readdata *rdata, - unsigned int len); - int (*copy_into_pages)(struct TCP_Server_Info *server, - struct cifs_readdata *rdata, - struct iov_iter *iter); - struct kvec iov[2]; - struct TCP_Server_Info *server; -#ifdef CONFIG_CIFS_SMB_DIRECT - struct smbd_mr *mr; -#endif - unsigned int pagesz; - unsigned int page_offset; - unsigned int tailsz; - struct cifs_credits credits; - unsigned int nr_pages; - struct page **pages; -}; - -/* asynchronous write support */ -struct cifs_writedata { - struct kref refcount; - struct list_head list; - struct completion done; - enum writeback_sync_modes sync_mode; - struct work_struct work; - struct cifsFileInfo *cfile; - struct cifs_aio_ctx *ctx; - __u64 offset; - pid_t pid; - unsigned int bytes; - int result; - struct TCP_Server_Info *server; -#ifdef CONFIG_CIFS_SMB_DIRECT - struct smbd_mr *mr; -#endif - unsigned int pagesz; - unsigned int page_offset; - unsigned int tailsz; - struct cifs_credits credits; - unsigned int nr_pages; - struct page **pages; -}; - -/* - * Take a reference on the file private data. Must be called with - * cfile->file_info_lock held. - */ -static inline void -cifsFileInfo_get_locked(struct cifsFileInfo *cifs_file) -{ - ++cifs_file->count; -} - -struct cifsFileInfo *cifsFileInfo_get(struct cifsFileInfo *cifs_file); -void _cifsFileInfo_put(struct cifsFileInfo *cifs_file, bool wait_oplock_hdlr, - bool offload); -void cifsFileInfo_put(struct cifsFileInfo *cifs_file); - -#define CIFS_CACHE_READ_FLG 1 -#define CIFS_CACHE_HANDLE_FLG 2 -#define CIFS_CACHE_RH_FLG (CIFS_CACHE_READ_FLG | CIFS_CACHE_HANDLE_FLG) -#define CIFS_CACHE_WRITE_FLG 4 -#define CIFS_CACHE_RW_FLG (CIFS_CACHE_READ_FLG | CIFS_CACHE_WRITE_FLG) -#define CIFS_CACHE_RHW_FLG (CIFS_CACHE_RW_FLG | CIFS_CACHE_HANDLE_FLG) - -#define CIFS_CACHE_READ(cinode) ((cinode->oplock & CIFS_CACHE_READ_FLG) || (CIFS_SB(cinode->netfs.inode.i_sb)->mnt_cifs_flags & CIFS_MOUNT_RO_CACHE)) -#define CIFS_CACHE_HANDLE(cinode) (cinode->oplock & CIFS_CACHE_HANDLE_FLG) -#define CIFS_CACHE_WRITE(cinode) ((cinode->oplock & CIFS_CACHE_WRITE_FLG) || (CIFS_SB(cinode->netfs.inode.i_sb)->mnt_cifs_flags & CIFS_MOUNT_RW_CACHE)) - -/* - * One of these for each file inode - */ - -struct cifsInodeInfo { - struct netfs_inode netfs; /* Netfslib context and vfs inode */ - bool can_cache_brlcks; - struct list_head llist; /* locks helb by this inode */ - /* - * NOTE: Some code paths call down_read(lock_sem) twice, so - * we must always use cifs_down_write() instead of down_write() - * for this semaphore to avoid deadlocks. - */ - struct rw_semaphore lock_sem; /* protect the fields above */ - /* BB add in lists for dirty pages i.e. write caching info for oplock */ - struct list_head openFileList; - spinlock_t open_file_lock; /* protects openFileList */ - __u32 cifsAttrs; /* e.g. DOS archive bit, sparse, compressed, system */ - unsigned int oplock; /* oplock/lease level we have */ - unsigned int epoch; /* used to track lease state changes */ -#define CIFS_INODE_PENDING_OPLOCK_BREAK (0) /* oplock break in progress */ -#define CIFS_INODE_PENDING_WRITERS (1) /* Writes in progress */ -#define CIFS_INODE_FLAG_UNUSED (2) /* Unused flag */ -#define CIFS_INO_DELETE_PENDING (3) /* delete pending on server */ -#define CIFS_INO_INVALID_MAPPING (4) /* pagecache is invalid */ -#define CIFS_INO_LOCK (5) /* lock bit for synchronization */ -#define CIFS_INO_MODIFIED_ATTR (6) /* Indicate change in mtime/ctime */ -#define CIFS_INO_CLOSE_ON_LOCK (7) /* Not to defer the close when lock is set */ - unsigned long flags; - spinlock_t writers_lock; - unsigned int writers; /* Number of writers on this inode */ - unsigned long time; /* jiffies of last update of inode */ - u64 server_eof; /* current file size on server -- protected by i_lock */ - u64 uniqueid; /* server inode number */ - u64 createtime; /* creation time on server */ - __u8 lease_key[SMB2_LEASE_KEY_SIZE]; /* lease key for this inode */ - struct list_head deferred_closes; /* list of deferred closes */ - spinlock_t deferred_lock; /* protection on deferred list */ - bool lease_granted; /* Flag to indicate whether lease or oplock is granted. */ - char *symlink_target; -}; - -static inline struct cifsInodeInfo * -CIFS_I(struct inode *inode) -{ - return container_of(inode, struct cifsInodeInfo, netfs.inode); -} - -static inline struct cifs_sb_info * -CIFS_SB(struct super_block *sb) -{ - return sb->s_fs_info; -} - -static inline struct cifs_sb_info * -CIFS_FILE_SB(struct file *file) -{ - return CIFS_SB(file_inode(file)->i_sb); -} - -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 '\\'; -} - -static inline void -convert_delimiter(char *path, char delim) -{ - char old_delim, *pos; - - if (delim == '/') - old_delim = '\\'; - else - old_delim = '/'; - - pos = path; - while ((pos = strchr(pos, old_delim))) - *pos = delim; -} - -#define cifs_stats_inc atomic_inc - -static inline void cifs_stats_bytes_written(struct cifs_tcon *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 cifs_tcon *tcon, - unsigned int bytes) -{ - spin_lock(&tcon->stat_lock); - tcon->bytes_read += bytes; - spin_unlock(&tcon->stat_lock); -} - - -/* - * This is the prototype for the mid receive function. This function is for - * receiving the rest of the SMB frame, starting with the WordCount (which is - * just after the MID in struct smb_hdr). Note: - * - * - This will be called by cifsd, with no locks held. - * - The mid will still be on the pending_mid_q. - * - mid->resp_buf will point to the current buffer. - * - * Returns zero on a successful receive, or an error. The receive state in - * the TCP_Server_Info will also be updated. - */ -typedef int (mid_receive_t)(struct TCP_Server_Info *server, - struct mid_q_entry *mid); - -/* - * This is the prototype for the mid callback function. This is called once the - * mid has been received off of the socket. When creating one, take special - * care to avoid deadlocks. Things to bear in mind: - * - * - it will be called by cifsd, with no locks held - * - the mid will be removed from any lists - */ -typedef void (mid_callback_t)(struct mid_q_entry *mid); - -/* - * This is the protopyte for mid handle function. This is called once the mid - * has been recognized after decryption of the message. - */ -typedef int (mid_handle_t)(struct TCP_Server_Info *server, - struct mid_q_entry *mid); - -/* one of these for every pending CIFS request to the server */ -struct mid_q_entry { - struct list_head qhead; /* mids waiting on reply from this server */ - struct kref refcount; - struct TCP_Server_Info *server; /* server corresponding to this mid */ - __u64 mid; /* multiplex id */ - __u16 credits; /* number of credits consumed by this mid */ - __u16 credits_received; /* number of credits from the response */ - __u32 pid; /* process id */ - __u32 sequence_number; /* for CIFS signing */ - 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 - mid_receive_t *receive; /* call receive callback */ - mid_callback_t *callback; /* call completion callback */ - mid_handle_t *handle; /* call handle mid callback */ - void *callback_data; /* general purpose pointer for callback */ - struct task_struct *creator; - void *resp_buf; /* pointer to received SMB header */ - unsigned int resp_buf_size; - int mid_state; /* wish this were enum but can not pass to wait_event */ - unsigned int mid_flags; - __le16 command; /* smb command code */ - unsigned int optype; /* operation type */ - bool large_buf:1; /* if valid response, is pointer to large buf */ - bool multiRsp:1; /* multiple trans2 responses for one request */ - bool multiEnd:1; /* both received */ - bool decrypted:1; /* decrypted entry */ -}; - -struct close_cancelled_open { - struct cifs_fid fid; - struct cifs_tcon *tcon; - struct work_struct work; - __u64 mid; - __u16 cmd; -}; - -/* Make code in transport.c a little cleaner by moving - update of optional stats into function below */ -static inline void cifs_in_send_inc(struct TCP_Server_Info *server) -{ - atomic_inc(&server->in_send); -} - -static inline void cifs_in_send_dec(struct TCP_Server_Info *server) -{ - atomic_dec(&server->in_send); -} - -static inline void cifs_num_waiters_inc(struct TCP_Server_Info *server) -{ - atomic_inc(&server->num_waiters); -} - -static inline void cifs_num_waiters_dec(struct TCP_Server_Info *server) -{ - atomic_dec(&server->num_waiters); -} - -#ifdef CONFIG_CIFS_STATS2 -static inline void cifs_save_when_sent(struct mid_q_entry *mid) -{ - mid->when_sent = jiffies; -} -#else -static inline void cifs_save_when_sent(struct mid_q_entry *mid) -{ -} -#endif - -/* 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; -}; - -struct dfs_info3_param { - int flags; /* DFSREF_REFERRAL_SERVER, DFSREF_STORAGE_SERVER*/ - int path_consumed; - int server_type; - int ref_flag; - char *path_name; - char *node_name; - int ttl; -}; - -struct file_list { - struct list_head list; - struct cifsFileInfo *cfile; -}; - -static inline void free_dfs_info_param(struct dfs_info3_param *param) -{ - if (param) { - kfree(param->path_name); - kfree(param->node_name); - } -} - -static inline void free_dfs_info_array(struct dfs_info3_param *param, - int number_of_items) -{ - int i; - if ((number_of_items == 0) || (param == NULL)) - return; - for (i = 0; i < number_of_items; i++) { - kfree(param[i].path_name); - kfree(param[i].node_name); - } - kfree(param); -} - -static inline bool is_interrupt_error(int error) -{ - switch (error) { - case -EINTR: - case -ERESTARTSYS: - case -ERESTARTNOHAND: - case -ERESTARTNOINTR: - return true; - } - return false; -} - -static inline bool is_retryable_error(int error) -{ - if (is_interrupt_error(error) || error == -EAGAIN) - return true; - return false; -} - - -/* cifs_get_writable_file() flags */ -#define FIND_WR_ANY 0 -#define FIND_WR_FSUID_ONLY 1 -#define FIND_WR_WITH_DELETE 2 - -#define MID_FREE 0 -#define MID_REQUEST_ALLOCATED 1 -#define MID_REQUEST_SUBMITTED 2 -#define MID_RESPONSE_RECEIVED 4 -#define MID_RETRY_NEEDED 8 /* session closed while this request out */ -#define MID_RESPONSE_MALFORMED 0x10 -#define MID_SHUTDOWN 0x20 - -/* Flags */ -#define MID_WAIT_CANCELLED 1 /* Cancelled while waiting for response */ -#define MID_DELETED 2 /* Mid has been dequeued/deleted */ - -/* Types of response buffer returned from SendReceive2 */ -#define CIFS_NO_BUFFER 0 /* Response buffer not returned */ -#define CIFS_SMALL_BUFFER 1 -#define CIFS_LARGE_BUFFER 2 -#define CIFS_IOVEC 4 /* array of response buffers */ - -/* Type of Request to SendReceive2 */ -#define CIFS_BLOCKING_OP 1 /* operation can block */ -#define CIFS_NON_BLOCKING 2 /* do not block waiting for credits */ -#define CIFS_TIMEOUT_MASK 0x003 /* only one of above set in req */ -#define CIFS_LOG_ERROR 0x010 /* log NT STATUS if non-zero */ -#define CIFS_LARGE_BUF_OP 0x020 /* large request buffer */ -#define CIFS_NO_RSP_BUF 0x040 /* no response buffer required */ - -/* Type of request operation */ -#define CIFS_ECHO_OP 0x080 /* echo request */ -#define CIFS_OBREAK_OP 0x0100 /* oplock break request */ -#define CIFS_NEG_OP 0x0200 /* negotiate request */ -#define CIFS_CP_CREATE_CLOSE_OP 0x0400 /* compound create+close request */ -/* Lower bitmask values are reserved by others below. */ -#define CIFS_SESS_OP 0x2000 /* session setup request */ -#define CIFS_OP_MASK 0x2780 /* mask request type */ - -#define CIFS_HAS_CREDITS 0x0400 /* already has credits */ -#define CIFS_TRANSFORM_REQ 0x0800 /* transform request before sending */ -#define CIFS_NO_SRV_RSP 0x1000 /* there is no server response */ - -/* Security Flags: indicate type of session setup needed */ -#define CIFSSEC_MAY_SIGN 0x00001 -#define CIFSSEC_MAY_NTLMV2 0x00004 -#define CIFSSEC_MAY_KRB5 0x00008 -#define CIFSSEC_MAY_SEAL 0x00040 /* not supported yet */ -#define CIFSSEC_MAY_NTLMSSP 0x00080 /* raw ntlmssp with ntlmv2 */ - -#define CIFSSEC_MUST_SIGN 0x01001 -/* note that only one of the following can be set so the -result of setting MUST flags more than once will be to -require use of the stronger protocol */ -#define CIFSSEC_MUST_NTLMV2 0x04004 -#define CIFSSEC_MUST_KRB5 0x08008 -#ifdef CONFIG_CIFS_UPCALL -#define CIFSSEC_MASK 0x8F08F /* flags supported if no weak allowed */ -#else -#define CIFSSEC_MASK 0x87087 /* flags supported if no weak allowed */ -#endif /* UPCALL */ -#define CIFSSEC_MUST_SEAL 0x40040 /* not supported yet */ -#define CIFSSEC_MUST_NTLMSSP 0x80080 /* raw ntlmssp with ntlmv2 */ - -#define CIFSSEC_DEF (CIFSSEC_MAY_SIGN | CIFSSEC_MAY_NTLMV2 | CIFSSEC_MAY_NTLMSSP) -#define CIFSSEC_MAX (CIFSSEC_MUST_NTLMV2) -#define CIFSSEC_AUTH_MASK (CIFSSEC_MAY_NTLMV2 | CIFSSEC_MAY_KRB5 | CIFSSEC_MAY_NTLMSSP) -/* - ***************************************************************** - * All constants go here - ***************************************************************** - */ - -#define UID_HASH (16) - -/* - * Note that ONE module should define _DECLARE_GLOBALS_HERE to cause the - * following to be declared. - */ - -/**************************************************************************** - * Here are all the locks (spinlock, mutex, semaphore) in cifs.ko, arranged according - * to the locking order. i.e. if two locks are to be held together, the lock that - * appears higher in this list needs to be taken before the other. - * - * If you hold a lock that is lower in this list, and you need to take a higher lock - * (or if you think that one of the functions that you're calling may need to), first - * drop the lock you hold, pick up the higher lock, then the lower one. This will - * ensure that locks are picked up only in one direction in the below table - * (top to bottom). - * - * Also, if you expect a function to be called with a lock held, explicitly document - * this in the comments on top of your function definition. - * - * And also, try to keep the critical sections (lock hold time) to be as minimal as - * possible. Blocking / calling other functions with a lock held always increase - * the risk of a possible deadlock. - * - * Following this rule will avoid unnecessary deadlocks, which can get really hard to - * debug. Also, any new lock that you introduce, please add to this list in the correct - * order. - * - * Please populate this list whenever you introduce new locks in your changes. Or in - * case I've missed some existing locks. Please ensure that it's added in the list - * based on the locking order expected. - * - * ===================================================================================== - * Lock Protects Initialization fn - * ===================================================================================== - * vol_list_lock - * vol_info->ctx_lock vol_info->ctx - * cifs_sb_info->tlink_tree_lock cifs_sb_info->tlink_tree cifs_setup_cifs_sb - * TCP_Server_Info-> TCP_Server_Info cifs_get_tcp_session - * reconnect_mutex - * TCP_Server_Info->srv_mutex TCP_Server_Info cifs_get_tcp_session - * cifs_ses->session_mutex cifs_ses sesInfoAlloc - * cifs_tcon - * cifs_tcon->open_file_lock cifs_tcon->openFileList tconInfoAlloc - * cifs_tcon->pending_opens - * cifs_tcon->stat_lock cifs_tcon->bytes_read tconInfoAlloc - * cifs_tcon->bytes_written - * cifs_tcp_ses_lock cifs_tcp_ses_list sesInfoAlloc - * GlobalMid_Lock GlobalMaxActiveXid init_cifs - * GlobalCurrentXid - * GlobalTotalActiveXid - * TCP_Server_Info->srv_lock (anything in struct not protected by another lock and can change) - * TCP_Server_Info->mid_lock TCP_Server_Info->pending_mid_q cifs_get_tcp_session - * ->CurrentMid - * (any changes in mid_q_entry fields) - * TCP_Server_Info->req_lock TCP_Server_Info->in_flight cifs_get_tcp_session - * ->credits - * ->echo_credits - * ->oplock_credits - * ->reconnect_instance - * cifs_ses->ses_lock (anything that is not protected by another lock and can change) - * cifs_ses->iface_lock cifs_ses->iface_list sesInfoAlloc - * ->iface_count - * ->iface_last_update - * cifs_ses->chan_lock cifs_ses->chans - * ->chans_need_reconnect - * ->chans_in_reconnect - * cifs_tcon->tc_lock (anything that is not protected by another lock and can change) - * cifsInodeInfo->open_file_lock cifsInodeInfo->openFileList cifs_alloc_inode - * cifsInodeInfo->writers_lock cifsInodeInfo->writers cifsInodeInfo_alloc - * cifsInodeInfo->lock_sem cifsInodeInfo->llist cifs_init_once - * ->can_cache_brlcks - * cifsInodeInfo->deferred_lock cifsInodeInfo->deferred_closes cifsInodeInfo_alloc - * cached_fid->fid_mutex cifs_tcon->crfid tconInfoAlloc - * cifsFileInfo->fh_mutex cifsFileInfo cifs_new_fileinfo - * cifsFileInfo->file_info_lock cifsFileInfo->count cifs_new_fileinfo - * ->invalidHandle initiate_cifs_search - * ->oplock_break_cancelled - * cifs_aio_ctx->aio_mutex cifs_aio_ctx cifs_aio_ctx_alloc - ****************************************************************************/ - -#ifdef DECLARE_GLOBALS_HERE -#define GLOBAL_EXTERN -#else -#define GLOBAL_EXTERN extern -#endif - -/* - * the list of TCP_Server_Info structures, ie each of the sockets - * connecting our client to a distinct server (ip address), is - * chained together by cifs_tcp_ses_list. The list of all our SMB - * sessions (and from that the tree connections) can be found - * by iterating over cifs_tcp_ses_list - */ -extern struct list_head cifs_tcp_ses_list; - -/* - * This lock protects the cifs_tcp_ses_list, the list of smb sessions per - * tcp session, and the list of tcon's per smb session. It also protects - * the reference counters for the server, smb session, and tcon. - * generally the locks should be taken in order tcp_ses_lock before - * tcon->open_file_lock and that before file->file_info_lock since the - * structure order is cifs_socket-->cifs_ses-->cifs_tcon-->cifs_file - */ -extern spinlock_t cifs_tcp_ses_lock; - -/* - * Global transaction id (XID) information - */ -extern unsigned int GlobalCurrentXid; /* protected by GlobalMid_Sem */ -extern unsigned int GlobalTotalActiveXid; /* prot by GlobalMid_Sem */ -extern unsigned int GlobalMaxActiveXid; /* prot by GlobalMid_Sem */ -extern spinlock_t GlobalMid_Lock; /* protects above & list operations on midQ entries */ - -/* - * Global counters, updated atomically - */ -extern atomic_t sesInfoAllocCount; -extern atomic_t tconInfoAllocCount; -extern atomic_t tcpSesNextId; -extern atomic_t tcpSesAllocCount; -extern atomic_t tcpSesReconnectCount; -extern atomic_t tconInfoReconnectCount; - -/* Various Debug counters */ -extern atomic_t buf_alloc_count; /* current number allocated */ -extern atomic_t small_buf_alloc_count; -#ifdef CONFIG_CIFS_STATS2 -extern atomic_t total_buf_alloc_count; /* total allocated over all time */ -extern atomic_t total_small_buf_alloc_count; -extern unsigned int slow_rsp_threshold; /* number of secs before logging */ -#endif - -/* Misc globals */ -extern bool enable_oplocks; /* enable or disable oplocks */ -extern bool lookupCacheEnabled; -extern unsigned int global_secflags; /* if on, session setup sent - with more secure ntlmssp2 challenge/resp */ -extern unsigned int sign_CIFS_PDUs; /* enable smb packet signing */ -extern bool enable_gcm_256; /* allow optional negotiate of strongest signing (aes-gcm-256) */ -extern bool require_gcm_256; /* require use of strongest signing (aes-gcm-256) */ -extern bool enable_negotiate_signing; /* request use of faster (GMAC) signing if available */ -extern bool linuxExtEnabled;/*enable Linux/Unix CIFS extensions*/ -extern unsigned int CIFSMaxBufSize; /* max size not including hdr */ -extern unsigned int cifs_min_rcv; /* min size of big ntwrk buf pool */ -extern unsigned int cifs_min_small; /* min size of small buf pool */ -extern unsigned int cifs_max_pending; /* MAX requests at once to server*/ -extern bool disable_legacy_dialects; /* forbid vers=1.0 and vers=2.0 mounts */ -extern atomic_t mid_count; - -void cifs_oplock_break(struct work_struct *work); -void cifs_queue_oplock_break(struct cifsFileInfo *cfile); -void smb2_deferred_work_close(struct work_struct *work); - -extern const struct slow_work_ops cifs_oplock_break_ops; -extern struct workqueue_struct *cifsiod_wq; -extern struct workqueue_struct *decrypt_wq; -extern struct workqueue_struct *fileinfo_put_wq; -extern struct workqueue_struct *cifsoplockd_wq; -extern struct workqueue_struct *deferredclose_wq; -extern __u32 cifs_lock_secret; - -extern mempool_t *cifs_mid_poolp; - -/* Operations for different SMB versions */ -#define SMB1_VERSION_STRING "1.0" -#define SMB20_VERSION_STRING "2.0" -#ifdef CONFIG_CIFS_ALLOW_INSECURE_LEGACY -extern struct smb_version_operations smb1_operations; -extern struct smb_version_values smb1_values; -extern struct smb_version_operations smb20_operations; -extern struct smb_version_values smb20_values; -#endif /* CIFS_ALLOW_INSECURE_LEGACY */ -#define SMB21_VERSION_STRING "2.1" -extern struct smb_version_operations smb21_operations; -extern struct smb_version_values smb21_values; -#define SMBDEFAULT_VERSION_STRING "default" -extern struct smb_version_values smbdefault_values; -#define SMB3ANY_VERSION_STRING "3" -extern struct smb_version_values smb3any_values; -#define SMB30_VERSION_STRING "3.0" -extern struct smb_version_operations smb30_operations; -extern struct smb_version_values smb30_values; -#define SMB302_VERSION_STRING "3.02" -#define ALT_SMB302_VERSION_STRING "3.0.2" -/*extern struct smb_version_operations smb302_operations;*/ /* not needed yet */ -extern struct smb_version_values smb302_values; -#define SMB311_VERSION_STRING "3.1.1" -#define ALT_SMB311_VERSION_STRING "3.11" -extern struct smb_version_operations smb311_operations; -extern struct smb_version_values smb311_values; - -static inline char *get_security_type_str(enum securityEnum sectype) -{ - switch (sectype) { - case RawNTLMSSP: - return "RawNTLMSSP"; - case Kerberos: - return "Kerberos"; - case NTLMv2: - return "NTLMv2"; - default: - return "Unknown"; - } -} - -static inline bool is_smb1_server(struct TCP_Server_Info *server) -{ - return strcmp(server->vals->version_string, SMB1_VERSION_STRING) == 0; -} - -static inline bool is_tcon_dfs(struct cifs_tcon *tcon) -{ - /* - * For SMB1, see MS-CIFS 2.4.55 SMB_COM_TREE_CONNECT_ANDX (0x75) and MS-CIFS 3.3.4.4 DFS - * Subsystem Notifies That a Share Is a DFS Share. - * - * For SMB2+, see MS-SMB2 2.2.10 SMB2 TREE_CONNECT Response and MS-SMB2 3.3.4.14 Server - * Application Updates a Share. - */ - if (!tcon || !tcon->ses || !tcon->ses->server) - return false; - return is_smb1_server(tcon->ses->server) ? tcon->Flags & SMB_SHARE_IS_IN_DFS : - tcon->share_flags & (SHI1005_FLAGS_DFS | SHI1005_FLAGS_DFS_ROOT); -} - -static inline bool cifs_is_referral_server(struct cifs_tcon *tcon, - const struct dfs_info3_param *ref) -{ - /* - * Check if all targets are capable of handling DFS referrals as per - * MS-DFSC 2.2.4 RESP_GET_DFS_REFERRAL. - */ - return is_tcon_dfs(tcon) || (ref && (ref->flags & DFSREF_REFERRAL_SERVER)); -} - -static inline u64 cifs_flock_len(const struct file_lock *fl) -{ - return (u64)fl->fl_end - fl->fl_start + 1; -} - -static inline size_t ntlmssp_workstation_name_size(const struct cifs_ses *ses) -{ - if (WARN_ON_ONCE(!ses || !ses->server)) - return 0; - /* - * Make workstation name no more than 15 chars when using insecure dialects as some legacy - * servers do require it during NTLMSSP. - */ - if (ses->server->dialect <= SMB20_PROT_ID) - return min_t(size_t, sizeof(ses->workstation_name), RFC1001_NAME_LEN_WITH_NULL); - return sizeof(ses->workstation_name); -} - -static inline void move_cifs_info_to_smb2(struct smb2_file_all_info *dst, const FILE_ALL_INFO *src) -{ - memcpy(dst, src, (size_t)((u8 *)&src->AccessFlags - (u8 *)src)); - dst->AccessFlags = src->AccessFlags; - dst->CurrentByteOffset = src->CurrentByteOffset; - dst->Mode = src->Mode; - dst->AlignmentRequirement = src->AlignmentRequirement; - dst->FileNameLength = src->FileNameLength; -} - -static inline unsigned int cifs_get_num_sgs(const struct smb_rqst *rqst, - int num_rqst, - const u8 *sig) -{ - unsigned int len, skip; - unsigned int nents = 0; - unsigned long addr; - int i, j; - - /* Assumes the first rqst has a transform header as the first iov. - * I.e. - * rqst[0].rq_iov[0] is transform header - * rqst[0].rq_iov[1+] data to be encrypted/decrypted - * rqst[1+].rq_iov[0+] data to be encrypted/decrypted - */ - for (i = 0; i < num_rqst; i++) { - /* - * The first rqst has a transform header where the - * first 20 bytes are not part of the encrypted blob. - */ - for (j = 0; j < rqst[i].rq_nvec; j++) { - struct kvec *iov = &rqst[i].rq_iov[j]; - - skip = (i == 0) && (j == 0) ? 20 : 0; - addr = (unsigned long)iov->iov_base + skip; - if (unlikely(is_vmalloc_addr((void *)addr))) { - len = iov->iov_len - skip; - nents += DIV_ROUND_UP(offset_in_page(addr) + len, - PAGE_SIZE); - } else { - nents++; - } - } - nents += rqst[i].rq_npages; - } - nents += DIV_ROUND_UP(offset_in_page(sig) + SMB2_SIGNATURE_SIZE, PAGE_SIZE); - return nents; -} - -/* We can not use the normal sg_set_buf() as we will sometimes pass a - * stack object as buf. - */ -static inline struct scatterlist *cifs_sg_set_buf(struct scatterlist *sg, - const void *buf, - unsigned int buflen) -{ - unsigned long addr = (unsigned long)buf; - unsigned int off = offset_in_page(addr); - - addr &= PAGE_MASK; - if (unlikely(is_vmalloc_addr((void *)addr))) { - do { - unsigned int len = min_t(unsigned int, buflen, PAGE_SIZE - off); - - sg_set_page(sg++, vmalloc_to_page((void *)addr), len, off); - - off = 0; - addr += PAGE_SIZE; - buflen -= len; - } while (buflen); - } else { - sg_set_page(sg++, virt_to_page(addr), buflen, off); - } - return sg; -} - -#endif /* _CIFS_GLOB_H */ diff --git a/fs/cifs/cifspdu.h b/fs/cifs/cifspdu.h deleted file mode 100644 index d1abaeea974a9..0000000000000 --- a/fs/cifs/cifspdu.h +++ /dev/null @@ -1,2730 +0,0 @@ -/* SPDX-License-Identifier: LGPL-2.1 */ -/* - * - * Copyright (c) International Business Machines Corp., 2002,2009 - * Author(s): Steve French (sfrench@us.ibm.com) - * - */ - -#ifndef _CIFSPDU_H -#define _CIFSPDU_H - -#include -#include -#include "../smbfs_common/smbfsctl.h" - -#define CIFS_PROT 0 -#define POSIX_PROT (CIFS_PROT+1) -#define BAD_PROT 0xFFFF - -/* SMB command codes: - * Note some commands have minimal (wct=0,bcc=0), or uninteresting, responses - * (ie which include no useful data other than the SMB error code itself). - * This can allow us to avoid response buffer allocations and copy in some cases - */ -#define SMB_COM_CREATE_DIRECTORY 0x00 /* trivial response */ -#define SMB_COM_DELETE_DIRECTORY 0x01 /* trivial response */ -#define SMB_COM_CLOSE 0x04 /* triv req/rsp, timestamp ignored */ -#define SMB_COM_FLUSH 0x05 /* triv req/rsp */ -#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_ECHO 0x2B /* echo request */ -#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 -#define SMB_COM_TRANSACTION2_SECONDARY 0x33 -#define SMB_COM_FIND_CLOSE2 0x34 /* trivial response */ -#define SMB_COM_TREE_DISCONNECT 0x71 /* trivial response */ -#define SMB_COM_NEGOTIATE 0x72 -#define SMB_COM_SESSION_SETUP_ANDX 0x73 -#define SMB_COM_LOGOFF_ANDX 0x74 /* trivial response */ -#define SMB_COM_TREE_CONNECT_ANDX 0x75 -#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 */ -#define TRANS2_OPEN 0x00 -#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 -#define TRANS2_SET_FILE_INFORMATION 0x08 -#define TRANS2_GET_DFS_REFERRAL 0x10 -#define TRANS2_REPORT_DFS_INCOSISTENCY 0x11 - -/* SMB Transact (Named Pipe) subcommand codes */ -#define TRANS_SET_NMPIPE_STATE 0x0001 -#define TRANS_RAW_READ_NMPIPE 0x0011 -#define TRANS_QUERY_NMPIPE_STATE 0x0021 -#define TRANS_QUERY_NMPIPE_INFO 0x0022 -#define TRANS_PEEK_NMPIPE 0x0023 -#define TRANS_TRANSACT_NMPIPE 0x0026 -#define TRANS_RAW_WRITE_NMPIPE 0x0031 -#define TRANS_READ_NMPIPE 0x0036 -#define TRANS_WRITE_NMPIPE 0x0037 -#define TRANS_WAIT_NMPIPE 0x0053 -#define TRANS_CALL_NMPIPE 0x0054 - -/* NT Transact subcommand codes */ -#define NT_TRANSACT_CREATE 0x01 -#define NT_TRANSACT_IOCTL 0x02 -#define NT_TRANSACT_SET_SECURITY_DESC 0x03 -#define NT_TRANSACT_NOTIFY_CHANGE 0x04 -#define NT_TRANSACT_RENAME 0x05 -#define NT_TRANSACT_QUERY_SECURITY_DESC 0x06 -#define NT_TRANSACT_GET_USER_QUOTA 0x07 -#define NT_TRANSACT_SET_USER_QUOTA 0x08 - -#define MAX_CIFS_SMALL_BUFFER_SIZE 448 /* big enough for most */ -/* future chained NTCreateXReadX bigger, but for time being NTCreateX biggest */ -/* among the requests (NTCreateX response is bigger with wct of 34) */ -#define MAX_CIFS_HDR_SIZE 0x58 /* 4 len + 32 hdr + (2*24 wct) + 2 bct + 2 pad */ -#define CIFS_SMALL_PATH 120 /* allows for (448-88)/3 */ - -/* internal cifs vfs structures */ -/***************************************************************** - * All constants go here - ***************************************************************** - */ - -/* - * Starting value for maximum SMB size negotiation - */ -#define CIFS_MAX_MSGSIZE (4*4096) - -/* - * Size of encrypted user password in bytes - */ -#define CIFS_ENCPWD_SIZE (16) - -/* - * Size of the crypto key returned on the negotiate SMB in bytes - */ -#define CIFS_CRYPTO_KEY_SIZE (8) - -/* - * Size of the ntlm client response - */ -#define CIFS_AUTH_RESP_SIZE (24) - -/* - * Size of the session key (crypto key encrypted with the password - */ -#define CIFS_SESS_KEY_SIZE (16) - -#define CIFS_SERVER_CHALLENGE_SIZE (8) -#define CIFS_HMAC_MD5_HASH_SIZE (16) -#define CIFS_CPHTXT_SIZE (16) -#define CIFS_NTHASH_SIZE (16) - -/* - * Maximum user name length - */ -#define CIFS_UNLEN (20) - -/* - * Flags on SMB open - */ -#define SMBOPEN_WRITE_THROUGH 0x4000 -#define SMBOPEN_DENY_ALL 0x0010 -#define SMBOPEN_DENY_WRITE 0x0020 -#define SMBOPEN_DENY_READ 0x0030 -#define SMBOPEN_DENY_NONE 0x0040 -#define SMBOPEN_READ 0x0000 -#define SMBOPEN_WRITE 0x0001 -#define SMBOPEN_READWRITE 0x0002 -#define SMBOPEN_EXECUTE 0x0003 - -#define SMBOPEN_OCREATE 0x0010 -#define SMBOPEN_OTRUNC 0x0002 -#define SMBOPEN_OAPPEND 0x0001 - -/* - * SMB flag definitions - */ -#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 request) */ -#define SMBFLG_CANONICAL_PATH_FORMAT 0x10 /* obsolete */ -#define SMBFLG_OLD_OPLOCK 0x20 /* obsolete */ -#define SMBFLG_OLD_OPLOCK_NOTIFY 0x40 /* obsolete */ -#define SMBFLG_RESPONSE 0x80 /* this PDU is a response from server */ - -/* - * 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_EAS cpu_to_le16(2) -#define SMBFLG2_SECURITY_SIGNATURE cpu_to_le16(4) -#define SMBFLG2_COMPRESSED (8) -#define SMBFLG2_SECURITY_SIGNATURE_REQUIRED (0x10) -#define SMBFLG2_IS_LONG_NAME cpu_to_le16(0x40) -#define SMBFLG2_REPARSE_PATH (0x400) -#define SMBFLG2_EXT_SEC cpu_to_le16(0x800) -#define SMBFLG2_DFS cpu_to_le16(0x1000) -#define SMBFLG2_PAGING_IO cpu_to_le16(0x2000) -#define SMBFLG2_ERR_STATUS cpu_to_le16(0x4000) -#define SMBFLG2_UNICODE cpu_to_le16(0x8000) - -/* - * These are the file access permission bits defined in CIFS for the - * NTCreateAndX as well as the level 0x107 - * TRANS2_QUERY_PATH_INFORMATION API. The level 0x107, SMB_QUERY_FILE_ALL_INFO - * responds with the AccessFlags. - * The AccessFlags specifies the access permissions a caller has to the - * 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_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 SYSTEM_SECURITY 0x01000000 /* The system access control list */ - /* can be read and changed */ -#define GENERIC_ALL 0x10000000 -#define GENERIC_EXECUTE 0x20000000 -#define GENERIC_WRITE 0x40000000 -#define GENERIC_READ 0x80000000 - /* In summary - Relevant file */ - /* access flags from CIFS are */ - /* file_read_data, file_write_data */ - /* file_execute, file_read_attributes*/ - /* write_dac, and delete. */ - -#define FILE_READ_RIGHTS (FILE_READ_DATA | FILE_READ_EA | FILE_READ_ATTRIBUTES) -#define FILE_WRITE_RIGHTS (FILE_WRITE_DATA | FILE_APPEND_DATA \ - | FILE_WRITE_EA | FILE_WRITE_ATTRIBUTES) -#define FILE_EXEC_RIGHTS (FILE_EXECUTE) - -#define SET_FILE_READ_RIGHTS (FILE_READ_DATA | FILE_READ_EA | FILE_WRITE_EA \ - | FILE_READ_ATTRIBUTES \ - | FILE_WRITE_ATTRIBUTES \ - | DELETE | READ_CONTROL | WRITE_DAC \ - | WRITE_OWNER | SYNCHRONIZE) -#define SET_FILE_WRITE_RIGHTS (FILE_WRITE_DATA | FILE_APPEND_DATA \ - | FILE_READ_EA | FILE_WRITE_EA \ - | FILE_READ_ATTRIBUTES \ - | FILE_WRITE_ATTRIBUTES \ - | DELETE | READ_CONTROL | WRITE_DAC \ - | WRITE_OWNER | SYNCHRONIZE) -#define SET_FILE_EXEC_RIGHTS (FILE_READ_EA | FILE_WRITE_EA | FILE_EXECUTE \ - | FILE_READ_ATTRIBUTES \ - | FILE_WRITE_ATTRIBUTES \ - | DELETE | READ_CONTROL | WRITE_DAC \ - | WRITE_OWNER | SYNCHRONIZE) - -#define SET_MINIMUM_RIGHTS (FILE_READ_EA | FILE_READ_ATTRIBUTES \ - | READ_CONTROL | SYNCHRONIZE) - - -/* - * Invalid readdir handle - */ -#define CIFS_NO_HANDLE 0xFFFF - -#define NO_CHANGE_64 0xFFFFFFFFFFFFFFFFULL - -/* IPC$ in ASCII */ -#define CIFS_IPC_RESOURCE "\x49\x50\x43\x24" - -/* IPC$ in Unicode */ -#define CIFS_IPC_UNICODE_RESOURCE "\x00\x49\x00\x50\x00\x43\x00\x24\x00\x00" - -/* Unicode Null terminate 2 bytes of 0 */ -#define UNICODE_NULL "\x00\x00" -#define ASCII_NULL 0x00 - -/* - * Server type values (returned on EnumServer API - */ -#define CIFS_SV_TYPE_DC 0x00000008 -#define CIFS_SV_TYPE_BACKDC 0x00000010 - -/* - * Alias type flags (From EnumAlias API call - */ -#define CIFS_ALIAS_TYPE_FILE 0x0001 -#define CIFS_SHARE_TYPE_FILE 0x0000 - -/* - * File Attribute flags - */ -#define ATTR_READONLY 0x0001 -#define ATTR_HIDDEN 0x0002 -#define ATTR_SYSTEM 0x0004 -#define ATTR_VOLUME 0x0008 -#define ATTR_DIRECTORY 0x0010 -#define ATTR_ARCHIVE 0x0020 -#define ATTR_DEVICE 0x0040 -#define ATTR_NORMAL 0x0080 -#define ATTR_TEMPORARY 0x0100 -#define ATTR_SPARSE 0x0200 -#define ATTR_REPARSE 0x0400 -#define ATTR_COMPRESSED 0x0800 -#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 -#define ATTR_BACKUP_SEMANTICS 0x02000000 -#define ATTR_DELETE_ON_CLOSE 0x04000000 -#define ATTR_SEQUENTIAL_SCAN 0x08000000 -#define ATTR_RANDOM_ACCESS 0x10000000 -#define ATTR_NO_BUFFERING 0x20000000 -#define ATTR_WRITE_THROUGH 0x80000000 - -/* ShareAccess flags */ -#define FILE_NO_SHARE 0x00000000 -#define FILE_SHARE_READ 0x00000001 -#define FILE_SHARE_WRITE 0x00000002 -#define FILE_SHARE_DELETE 0x00000004 -#define FILE_SHARE_ALL 0x00000007 - -/* CreateDisposition flags, similar to CreateAction as well */ -#define FILE_SUPERSEDE 0x00000000 -#define FILE_OPEN 0x00000001 -#define FILE_CREATE 0x00000002 -#define FILE_OPEN_IF 0x00000003 -#define FILE_OVERWRITE 0x00000004 -#define FILE_OVERWRITE_IF 0x00000005 - -/* CreateOptions */ -#define CREATE_NOT_FILE 0x00000001 /* if set must not be file */ -#define CREATE_WRITE_THROUGH 0x00000002 -#define CREATE_SEQUENTIAL 0x00000004 -#define CREATE_NO_BUFFER 0x00000008 /* should not buffer on srv */ -#define CREATE_SYNC_ALERT 0x00000010 /* MBZ */ -#define CREATE_ASYNC_ALERT 0x00000020 /* MBZ */ -#define CREATE_NOT_DIR 0x00000040 /* if set must not be directory */ -#define CREATE_TREE_CONNECTION 0x00000080 /* should be zero */ -#define CREATE_COMPLETE_IF_OPLK 0x00000100 /* should be zero */ -#define CREATE_NO_EA_KNOWLEDGE 0x00000200 -#define CREATE_EIGHT_DOT_THREE 0x00000400 /* doc says this is obsolete - "open for recovery" flag should - be zero in any case */ -#define CREATE_OPEN_FOR_RECOVERY 0x00000400 -#define CREATE_RANDOM_ACCESS 0x00000800 -#define CREATE_DELETE_ON_CLOSE 0x00001000 -#define CREATE_OPEN_BY_ID 0x00002000 -#define CREATE_OPEN_BACKUP_INTENT 0x00004000 -#define CREATE_NO_COMPRESSION 0x00008000 -#define CREATE_RESERVE_OPFILTER 0x00100000 /* should be zero */ -#define OPEN_REPARSE_POINT 0x00200000 -#define OPEN_NO_RECALL 0x00400000 -#define OPEN_FREE_SPACE_QUERY 0x00800000 /* should be zero */ -#define CREATE_OPTIONS_MASK 0x007FFFFF -#define CREATE_OPTION_READONLY 0x10000000 -#define CREATE_OPTION_SPECIAL 0x20000000 /* system. NB not sent over wire */ - -/* ImpersonationLevel flags */ -#define SECURITY_ANONYMOUS 0 -#define SECURITY_IDENTIFICATION 1 -#define SECURITY_IMPERSONATION 2 -#define SECURITY_DELEGATION 3 - -/* SecurityFlags */ -#define SECURITY_CONTEXT_TRACKING 0x01 -#define SECURITY_EFFECTIVE_ONLY 0x02 - -/* - * Default PID value, used in all SMBs where the PID is not important - */ -#define CIFS_DFT_PID 0x1234 - -/* - * We use the same routine for Copy and Move SMBs. This flag is used to - * distinguish - */ -#define CIFS_COPY_OP 1 -#define CIFS_RENAME_OP 2 - -#define GETU16(var) (*((__u16 *)var)) /* BB check for endian issues */ -#define GETU32(var) (*((__u32 *)var)) /* BB check for endian issues */ - -struct smb_hdr { - __be32 smb_buf_length; /* BB length is only two (rarely three) bytes, - with one or two byte "type" preceding it that will be - zero - we could mask the type byte off */ - __u8 Protocol[4]; - __u8 Command; - union { - struct { - __u8 ErrorClass; - __u8 Reserved; - __le16 Error; - } __attribute__((packed)) DosError; - __le32 CifsError; - } __attribute__((packed)) Status; - __u8 Flags; - __le16 Flags2; /* note: le */ - __le16 PidHigh; - union { - struct { - __le32 SequenceNumber; /* le */ - __u32 Reserved; /* zero */ - } __attribute__((packed)) Sequence; - __u8 SecuritySignature[8]; /* le */ - } __attribute__((packed)) Signature; - __u8 pad[2]; - __u16 Tid; - __le16 Pid; - __u16 Uid; - __le16 Mid; - __u8 WordCount; -} __attribute__((packed)); - -/* given a pointer to an smb_hdr, retrieve a void pointer to the ByteCount */ -static inline void * -BCC(struct smb_hdr *smb) -{ - return (void *)smb + sizeof(*smb) + 2 * smb->WordCount; -} - -/* given a pointer to an smb_hdr retrieve the pointer to the byte area */ -#define pByteArea(smb_var) (BCC(smb_var) + 2) - -/* get the unconverted ByteCount for a SMB packet and return it */ -static inline __u16 -get_bcc(struct smb_hdr *hdr) -{ - __le16 *bc_ptr = (__le16 *)BCC(hdr); - - return get_unaligned_le16(bc_ptr); -} - -/* set the ByteCount for a SMB packet in little-endian */ -static inline void -put_bcc(__u16 count, struct smb_hdr *hdr) -{ - __le16 *bc_ptr = (__le16 *)BCC(hdr); - - put_unaligned_le16(count, bc_ptr); -} - -/* - * Computer Name Length (since Netbios name was length 16 with last byte 0x20) - * No longer as important, now that TCP names are more commonly used to - * resolve hosts. - */ -#define CNLEN 15 - -/* - * Share Name Length (SNLEN) - * Note: This length was limited by the SMB used to get - * the Share info. NetShareEnum only returned 13 - * chars, including the null termination. - * This was removed because it no longer is limiting. - */ - -/* - * Comment Length - */ -#define MAXCOMMENTLEN 40 - -/* - * The OS/2 maximum path name - */ -#define MAX_PATHCONF 256 - -/* - * SMB frame definitions (following must be packed structs) - * See the SNIA CIFS Specification for details. - * - * The Naming convention is the lower case version of the - * smb command code name for the struct and this is typedef to the - * uppercase version of the same name with the prefix SMB_ removed - * for brevity. Although typedefs are not commonly used for - * structure definitions in the Linux kernel, their use in the - * CIFS standards document, which this code is based on, may - * make this one of the cases where typedefs for structures make - * sense to improve readability for readers of the standards doc. - * Typedefs can always be removed later if they are too distracting - * and they are only used for the CIFSs PDUs themselves, not - * internal cifs vfs structures - * - */ - -typedef struct negotiate_req { - struct smb_hdr hdr; /* wct = 0 */ - __le16 ByteCount; - unsigned char DialectsArray[]; -} __attribute__((packed)) NEGOTIATE_REQ; - -#define MIN_TZ_ADJ (15 * 60) /* minimum grid for timezones in seconds */ - -#define READ_RAW_ENABLE 1 -#define WRITE_RAW_ENABLE 2 -#define RAW_ENABLE (READ_RAW_ENABLE | WRITE_RAW_ENABLE) -#define SMB1_CLIENT_GUID_SIZE (16) -typedef struct negotiate_rsp { - struct smb_hdr hdr; /* wct = 17 */ - __le16 DialectIndex; /* 0xFFFF = no dialect acceptable */ - __u8 SecurityMode; - __le16 MaxMpxCount; - __le16 MaxNumberVcs; - __le32 MaxBufferSize; - __le32 MaxRawSize; - __le32 SessionKey; - __le32 Capabilities; /* see below */ - __le32 SystemTimeLow; - __le32 SystemTimeHigh; - __le16 ServerTimeZone; - __u8 EncryptionKeyLength; - __u16 ByteCount; - union { - /* cap extended security off */ - DECLARE_FLEX_ARRAY(unsigned char, EncryptionKey); - /* followed by Domain name - if extended security is off */ - /* followed by 16 bytes of server GUID */ - /* then security blob if cap_extended_security negotiated */ - struct { - unsigned char GUID[SMB1_CLIENT_GUID_SIZE]; - unsigned char SecurityBlob[]; - } __attribute__((packed)) extended_response; - } __attribute__((packed)) u; -} __attribute__((packed)) NEGOTIATE_RSP; - -/* SecurityMode bits */ -#define SECMODE_USER 0x01 /* off indicates share level security */ -#define SECMODE_PW_ENCRYPT 0x02 -#define SECMODE_SIGN_ENABLED 0x04 /* SMB security signatures enabled */ -#define SECMODE_SIGN_REQUIRED 0x08 /* SMB security signatures required */ - -/* Negotiate response Capabilities */ -#define CAP_RAW_MODE 0x00000001 -#define CAP_MPX_MODE 0x00000002 -#define CAP_UNICODE 0x00000004 -#define CAP_LARGE_FILES 0x00000008 -#define CAP_NT_SMBS 0x00000010 /* implies CAP_NT_FIND */ -#define CAP_RPC_REMOTE_APIS 0x00000020 -#define CAP_STATUS32 0x00000040 -#define CAP_LEVEL_II_OPLOCKS 0x00000080 -#define CAP_LOCK_AND_READ 0x00000100 -#define CAP_NT_FIND 0x00000200 -#define CAP_DFS 0x00001000 -#define CAP_INFOLEVEL_PASSTHRU 0x00002000 -#define CAP_LARGE_READ_X 0x00004000 -#define CAP_LARGE_WRITE_X 0x00008000 -#define CAP_LWIO 0x00010000 /* support fctl_srv_req_resume_key */ -#define CAP_UNIX 0x00800000 -#define CAP_COMPRESSED_DATA 0x02000000 -#define CAP_DYNAMIC_REAUTH 0x20000000 -#define CAP_PERSISTENT_HANDLES 0x40000000 -#define CAP_EXTENDED_SECURITY 0x80000000 - -typedef union smb_com_session_setup_andx { - struct { /* request format */ - struct smb_hdr hdr; /* wct = 12 */ - __u8 AndXCommand; - __u8 AndXReserved; - __le16 AndXOffset; - __le16 MaxBufferSize; - __le16 MaxMpxCount; - __le16 VcNumber; - __u32 SessionKey; - __le16 SecurityBlobLength; - __u32 Reserved; - __le32 Capabilities; /* see below */ - __le16 ByteCount; - unsigned char SecurityBlob[1]; /* followed by */ - /* STRING NativeOS */ - /* STRING NativeLanMan */ - } __attribute__((packed)) req; /* NTLM request format (with - extended security */ - - struct { /* request format */ - struct smb_hdr hdr; /* wct = 13 */ - __u8 AndXCommand; - __u8 AndXReserved; - __le16 AndXOffset; - __le16 MaxBufferSize; - __le16 MaxMpxCount; - __le16 VcNumber; - __u32 SessionKey; - __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 * CaseSensitivePassword; */ - /* STRING AccountName */ - /* STRING PrimaryDomain */ - /* STRING NativeOS */ - /* STRING NativeLanMan */ - } __attribute__((packed)) req_no_secext; /* NTLM request format (without - extended security */ - - struct { /* default (NTLM) response format */ - struct smb_hdr hdr; /* wct = 4 */ - __u8 AndXCommand; - __u8 AndXReserved; - __le16 AndXOffset; - __le16 Action; /* see below */ - __le16 SecurityBlobLength; - __u16 ByteCount; - unsigned char SecurityBlob[1]; /* followed by */ -/* unsigned char * NativeOS; */ -/* unsigned char * NativeLanMan; */ -/* unsigned char * PrimaryDomain; */ - } __attribute__((packed)) resp; /* NTLM response - (with or without extended sec) */ - - struct { /* request format */ - struct smb_hdr hdr; /* wct = 10 */ - __u8 AndXCommand; - __u8 AndXReserved; - __le16 AndXOffset; - __le16 MaxBufferSize; - __le16 MaxMpxCount; - __le16 VcNumber; - __u32 SessionKey; - __le16 PasswordLength; - __u32 Reserved; /* encrypt key len and offset */ - __le16 ByteCount; - unsigned char AccountPassword[1]; /* followed by */ - /* STRING AccountName */ - /* STRING PrimaryDomain */ - /* STRING NativeOS */ - /* STRING NativeLanMan */ - } __attribute__((packed)) old_req; /* pre-NTLM (LANMAN2.1) req format */ - - struct { /* default (NTLM) response format */ - struct smb_hdr hdr; /* wct = 3 */ - __u8 AndXCommand; - __u8 AndXReserved; - __le16 AndXOffset; - __le16 Action; /* see below */ - __u16 ByteCount; - unsigned char NativeOS[1]; /* followed by */ -/* unsigned char * NativeLanMan; */ -/* unsigned char * PrimaryDomain; */ - } __attribute__((packed)) old_resp; /* pre-NTLM (LANMAN2.1) response */ -} __attribute__((packed)) SESSION_SETUP_ANDX; - -/* format of NLTMv2 Response ie "case sensitive password" hash when NTLMv2 */ - -#define NTLMSSP_SERVER_TYPE 1 -#define NTLMSSP_DOMAIN_TYPE 2 -#define NTLMSSP_FQ_DOMAIN_TYPE 3 -#define NTLMSSP_DNS_DOMAIN_TYPE 4 -#define NTLMSSP_DNS_PARENT_TYPE 5 - -struct ntlmssp2_name { - __le16 type; - __le16 length; -/* char name[length]; */ -} __attribute__((packed)); - -struct ntlmv2_resp { - union { - char ntlmv2_hash[CIFS_ENCPWD_SIZE]; - struct { - __u8 reserved[8]; - __u8 key[CIFS_SERVER_CHALLENGE_SIZE]; - } __attribute__((packed)) challenge; - } __attribute__((packed)); - __le32 blob_signature; - __u32 reserved; - __le64 time; - __u64 client_chal; /* random */ - __u32 reserved2; - /* array of name entries could follow ending in minimum 4 byte struct */ -} __attribute__((packed)); - - -#define CIFS_NETWORK_OPSYS "CIFS VFS Client for Linux" - -/* Capabilities bits (for NTLM SessSetup request) */ -#define CAP_UNICODE 0x00000004 -#define CAP_LARGE_FILES 0x00000008 -#define CAP_NT_SMBS 0x00000010 -#define CAP_STATUS32 0x00000040 -#define CAP_LEVEL_II_OPLOCKS 0x00000080 -#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 - -/* Action bits */ -#define GUEST_LOGIN 1 - -typedef struct smb_com_tconx_req { - struct smb_hdr hdr; /* wct = 4 */ - __u8 AndXCommand; - __u8 AndXReserved; - __le16 AndXOffset; - __le16 Flags; /* see below */ - __le16 PasswordLength; - __le16 ByteCount; - unsigned char Password[1]; /* followed by */ -/* STRING Path *//* \\server\share name */ - /* STRING Service */ -} __attribute__((packed)) TCONX_REQ; - -typedef struct smb_com_tconx_rsp { - struct smb_hdr hdr; /* wct = 3 , not extended response */ - __u8 AndXCommand; - __u8 AndXReserved; - __le16 AndXOffset; - __le16 OptionalSupport; /* see below */ - __u16 ByteCount; - unsigned char Service[1]; /* always ASCII, not Unicode */ - /* STRING NativeFileSystem */ -} __attribute__((packed)) TCONX_RSP; - -typedef struct smb_com_tconx_rsp_ext { - struct smb_hdr hdr; /* wct = 7, extended response */ - __u8 AndXCommand; - __u8 AndXReserved; - __le16 AndXOffset; - __le16 OptionalSupport; /* see below */ - __le32 MaximalShareAccessRights; - __le32 GuestMaximalShareAccessRights; - __u16 ByteCount; - unsigned char Service[1]; /* always ASCII, not Unicode */ - /* STRING NativeFileSystem */ -} __attribute__((packed)) TCONX_RSP_EXT; - - -/* tree connect Flags */ -#define DISCONNECT_TID 0x0001 -#define TCON_EXTENDED_SIGNATURES 0x0004 -#define TCON_EXTENDED_SECINFO 0x0008 - -/* OptionalSupport bits */ -#define SMB_SUPPORT_SEARCH_BITS 0x0001 /* "must have" directory search bits - (exclusive searches supported) */ -#define SMB_SHARE_IS_IN_DFS 0x0002 -#define SMB_CSC_MASK 0x000C -/* CSC flags defined as follows */ -#define SMB_CSC_CACHE_MANUAL_REINT 0x0000 -#define SMB_CSC_CACHE_AUTO_REINT 0x0004 -#define SMB_CSC_CACHE_VDO 0x0008 -#define SMB_CSC_NO_CACHING 0x000C -#define SMB_UNIQUE_FILE_NAME 0x0010 -#define SMB_EXTENDED_SIGNATURES 0x0020 - -/* services - * - * A: ie disk - * LPT1: ie printer - * IPC ie named pipe - * COMM - * ????? ie any type - * - */ - -typedef struct smb_com_echo_req { - struct smb_hdr hdr; - __le16 EchoCount; - __le16 ByteCount; - char Data[1]; -} __attribute__((packed)) ECHO_REQ; - -typedef struct smb_com_echo_rsp { - struct smb_hdr hdr; - __le16 SequenceNumber; - __le16 ByteCount; - char Data[1]; -} __attribute__((packed)) ECHO_RSP; - -typedef struct smb_com_logoff_andx_req { - struct smb_hdr hdr; /* wct = 2 */ - __u8 AndXCommand; - __u8 AndXReserved; - __u16 AndXOffset; - __u16 ByteCount; -} __attribute__((packed)) LOGOFF_ANDX_REQ; - -typedef struct smb_com_logoff_andx_rsp { - struct smb_hdr hdr; /* wct = 2 */ - __u8 AndXCommand; - __u8 AndXReserved; - __u16 AndXOffset; - __u16 ByteCount; -} __attribute__((packed)) LOGOFF_ANDX_RSP; - -typedef union smb_com_tree_disconnect { /* as an altetnative can use flag on - tree_connect PDU to effect disconnect */ - /* tdis is probably simplest SMB PDU */ - struct { - struct smb_hdr hdr; /* wct = 0 */ - __u16 ByteCount; /* bcc = 0 */ - } __attribute__((packed)) req; - struct { - struct smb_hdr hdr; /* wct = 0 */ - __u16 ByteCount; /* bcc = 0 */ - } __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 or -1 */ - __u16 ByteCount; /* 0 */ -} __attribute__((packed)) CLOSE_REQ; - -typedef struct smb_com_close_rsp { - struct smb_hdr hdr; /* wct = 0 */ - __u16 ByteCount; /* bct = 0 */ -} __attribute__((packed)) CLOSE_RSP; - -typedef struct smb_com_flush_req { - struct smb_hdr hdr; /* wct = 1 */ - __u16 FileID; - __u16 ByteCount; /* 0 */ -} __attribute__((packed)) FLUSH_REQ; - -typedef struct smb_com_findclose_req { - struct smb_hdr hdr; /* wct = 1 */ - __u16 FileID; - __u16 ByteCount; /* 0 */ -} __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 -#define REQ_EXTENDED_INFO 0x00000010 - -/* File type */ -#define DISK_TYPE 0x0000 -#define BYTE_PIPE_TYPE 0x0001 -#define MESSAGE_PIPE_TYPE 0x0002 -#define PRINTER_TYPE 0x0003 -#define COMM_DEV_TYPE 0x0004 -#define UNKNOWN_TYPE 0xFFFF - -/* Device Type or File Status Flags */ -#define NO_EAS 0x0001 -#define NO_SUBSTREAMS 0x0002 -#define NO_REPARSETAG 0x0004 -/* following flags can apply if pipe */ -#define ICOUNT_MASK 0x00FF -#define PIPE_READ_MODE 0x0100 -#define NAMED_PIPE_TYPE 0x0400 -#define PIPE_END_POINT 0x4000 -#define BLOCKING_NAMED_PIPE 0x8000 - -typedef struct smb_com_open_req { /* also handles create */ - struct smb_hdr hdr; /* wct = 24 */ - __u8 AndXCommand; - __u8 AndXReserved; - __le16 AndXOffset; - __u8 Reserved; /* Must Be Zero */ - __le16 NameLength; - __le32 OpenFlags; - __u32 RootDirectoryFid; - __le32 DesiredAccess; - __le64 AllocationSize; - __le32 FileAttributes; - __le32 ShareAccess; - __le32 CreateDisposition; - __le32 CreateOptions; - __le32 ImpersonationLevel; - __u8 SecurityFlags; - __le16 ByteCount; - char fileName[1]; -} __attribute__((packed)) OPEN_REQ; - -/* open response: oplock levels */ -#define OPLOCK_NONE 0 -#define OPLOCK_EXCLUSIVE 1 -#define OPLOCK_BATCH 2 -#define OPLOCK_READ 3 /* level 2 oplock */ - -/* open response for CreateAction shifted left */ -#define CIFS_CREATE_ACTION 0x20000 /* file created */ - -typedef struct smb_com_open_rsp { - struct smb_hdr hdr; /* wct = 34 BB */ - __u8 AndXCommand; - __u8 AndXReserved; - __le16 AndXOffset; - __u8 OplockLevel; - __u16 Fid; - __le32 CreateAction; - __le64 CreationTime; - __le64 LastAccessTime; - __le64 LastWriteTime; - __le64 ChangeTime; - __le32 FileAttributes; - __le64 AllocationSize; - __le64 EndOfFile; - __le16 FileType; - __le16 DeviceState; - __u8 DirectoryFlag; - __u16 ByteCount; /* bct = 0 */ -} __attribute__((packed)) OPEN_RSP; - -typedef struct smb_com_open_rsp_ext { - struct smb_hdr hdr; /* wct = 42 but meaningless due to MS bug? */ - __u8 AndXCommand; - __u8 AndXReserved; - __le16 AndXOffset; - __u8 OplockLevel; - __u16 Fid; - __le32 CreateAction; - __le64 CreationTime; - __le64 LastAccessTime; - __le64 LastWriteTime; - __le64 ChangeTime; - __le32 FileAttributes; - __le64 AllocationSize; - __le64 EndOfFile; - __le16 FileType; - __le16 DeviceState; - __u8 DirectoryFlag; - __u8 VolumeGUID[16]; - __u64 FileId; /* note no endian conversion - is opaque UniqueID */ - __le32 MaximalAccessRights; - __le32 GuestMaximalAccessRights; - __u16 ByteCount; /* bct = 0 */ -} __attribute__((packed)) OPEN_RSP_EXT; - - -/* 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; - -/* For encoding of POSIX Open Request - see trans2 function 0x209 data struct */ - -/* 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[]; -} __attribute__((packed)) WRITEX_REQ; - -typedef struct smb_com_write_req { - struct smb_hdr hdr; /* wct = 14 */ - __u8 AndXCommand; - __u8 AndXReserved; - __le16 AndXOffset; - __u16 Fid; - __le32 OffsetLow; - __u32 Reserved; - __le16 WriteMode; - __le16 Remaining; - __le16 DataLengthHigh; - __le16 DataLengthLow; - __le16 DataOffset; - __le32 OffsetHigh; - __le16 ByteCount; - __u8 Pad; /* BB check for whether padded to DWORD - boundary and optimum performance here */ - char Data[]; -} __attribute__((packed)) WRITE_REQ; - -typedef struct smb_com_write_rsp { - struct smb_hdr hdr; /* wct = 6 */ - __u8 AndXCommand; - __u8 AndXReserved; - __le16 AndXOffset; - __le16 Count; - __le16 Remaining; - __le16 CountHigh; - __u16 Reserved; - __u16 ByteCount; -} __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 */ - __u8 AndXCommand; - __u8 AndXReserved; - __le16 AndXOffset; - __u16 Fid; - __le32 OffsetLow; - __le16 MaxCount; - __le16 MinCount; /* obsolete */ - __le32 MaxCountHigh; - __le16 Remaining; - __le32 OffsetHigh; - __le16 ByteCount; -} __attribute__((packed)) READ_REQ; - -typedef struct smb_com_read_rsp { - struct smb_hdr hdr; /* wct = 12 */ - __u8 AndXCommand; - __u8 AndXReserved; - __le16 AndXOffset; - __le16 Remaining; - __le16 DataCompactionMode; - __le16 Reserved; - __le16 DataLength; - __le16 DataOffset; - __le16 DataLengthHigh; - __u64 Reserved2; - __u16 ByteCount; - /* read response data immediately follows */ -} __attribute__((packed)) READ_RSP; - -typedef struct locking_andx_range { - __le16 Pid; - __le16 Pad; - __le32 OffsetHigh; - __le32 OffsetLow; - __le32 LengthHigh; - __le32 LengthLow; -} __attribute__((packed)) LOCKING_ANDX_RANGE; - -#define LOCKING_ANDX_SHARED_LOCK 0x01 -#define LOCKING_ANDX_OPLOCK_RELEASE 0x02 -#define LOCKING_ANDX_CHANGE_LOCKTYPE 0x04 -#define LOCKING_ANDX_CANCEL_LOCK 0x08 -#define LOCKING_ANDX_LARGE_FILES 0x10 /* always on for us */ - -typedef struct smb_com_lock_req { - struct smb_hdr hdr; /* wct = 8 */ - __u8 AndXCommand; - __u8 AndXReserved; - __le16 AndXOffset; - __u16 Fid; - __u8 LockType; - __u8 OplockLevel; - __le32 Timeout; - __le16 NumberOfUnlocks; - __le16 NumberOfLocks; - __le16 ByteCount; - LOCKING_ANDX_RANGE Locks[1]; -} __attribute__((packed)) LOCK_REQ; - -/* lock type */ -#define CIFS_RDLCK 0 -#define CIFS_WRLCK 1 -#define CIFS_UNLCK 2 -typedef struct cifs_posix_lock { - __le16 lock_type; /* 0 = Read, 1 = Write, 2 = Unlock */ - __le16 lock_flags; /* 1 = Wait (only valid for setlock) */ - __le32 pid; - __le64 start; - __le64 length; - /* BB what about additional owner info to identify network client */ -} __attribute__((packed)) CIFS_POSIX_LOCK; - -typedef struct smb_com_lock_rsp { - struct smb_hdr hdr; /* wct = 2 */ - __u8 AndXCommand; - __u8 AndXReserved; - __le16 AndXOffset; - __u16 ByteCount; -} __attribute__((packed)) LOCK_RSP; - -typedef struct smb_com_rename_req { - struct smb_hdr hdr; /* wct = 1 */ - __le16 SearchAttributes; /* target file attributes */ - __le16 ByteCount; - __u8 BufferFormat; /* 4 = ASCII or Unicode */ - unsigned char OldFileName[1]; - /* followed by __u8 BufferFormat2 */ - /* followed by NewFileName */ -} __attribute__((packed)) RENAME_REQ; - - /* copy request flags */ -#define COPY_MUST_BE_FILE 0x0001 -#define COPY_MUST_BE_DIR 0x0002 -#define COPY_TARGET_MODE_ASCII 0x0004 /* if not set, binary */ -#define COPY_SOURCE_MODE_ASCII 0x0008 /* if not set, binary */ -#define COPY_VERIFY_WRITES 0x0010 -#define COPY_TREE 0x0020 - -typedef struct smb_com_copy_req { - struct smb_hdr hdr; /* wct = 3 */ - __u16 Tid2; - __le16 OpenFunction; - __le16 Flags; - __le16 ByteCount; - __u8 BufferFormat; /* 4 = ASCII or Unicode */ - unsigned char OldFileName[1]; - /* followed by __u8 BufferFormat2 */ - /* followed by NewFileName string */ -} __attribute__((packed)) COPY_REQ; - -typedef struct smb_com_copy_rsp { - struct smb_hdr hdr; /* wct = 1 */ - __le16 CopyCount; /* number of files copied */ - __u16 ByteCount; /* may be zero */ - __u8 BufferFormat; /* 0x04 - only present if errored file follows */ - unsigned char ErrorFileName[1]; /* only present if error in copy */ -} __attribute__((packed)) COPY_RSP; - -#define CREATE_HARD_LINK 0x103 -#define MOVEFILE_COPY_ALLOWED 0x0002 -#define MOVEFILE_REPLACE_EXISTING 0x0001 - -typedef struct smb_com_nt_rename_req { /* A5 - also used for create hardlink */ - struct smb_hdr hdr; /* wct = 4 */ - __le16 SearchAttributes; /* target file attributes */ - __le16 Flags; /* spec says Information Level */ - __le32 ClusterCount; - __le16 ByteCount; - __u8 BufferFormat; /* 4 = ASCII or Unicode */ - unsigned char OldFileName[1]; - /* followed by __u8 BufferFormat2 */ - /* followed by NewFileName */ -} __attribute__((packed)) NT_RENAME_REQ; - -typedef struct smb_com_rename_rsp { - struct smb_hdr hdr; /* wct = 0 */ - __u16 ByteCount; /* bct = 0 */ -} __attribute__((packed)) RENAME_RSP; - -typedef struct smb_com_delete_file_req { - struct smb_hdr hdr; /* wct = 1 */ - __le16 SearchAttributes; - __le16 ByteCount; - __u8 BufferFormat; /* 4 = ASCII */ - unsigned char fileName[1]; -} __attribute__((packed)) DELETE_FILE_REQ; - -typedef struct smb_com_delete_file_rsp { - struct smb_hdr hdr; /* wct = 0 */ - __u16 ByteCount; /* bct = 0 */ -} __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]; -} __attribute__((packed)) DELETE_DIRECTORY_REQ; - -typedef struct smb_com_delete_directory_rsp { - struct smb_hdr hdr; /* wct = 0 */ - __u16 ByteCount; /* bct = 0 */ -} __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]; -} __attribute__((packed)) CREATE_DIRECTORY_REQ; - -typedef struct smb_com_create_directory_rsp { - struct smb_hdr hdr; /* wct = 0 */ - __u16 ByteCount; /* bct = 0 */ -} __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 */ - __le16 attr; - __le16 time_low; - __le16 time_high; - __le16 reserved[5]; /* must be zero */ - __u16 ByteCount; - __u8 BufferFormat; /* 4 = ASCII */ - unsigned char fileName[1]; -} __attribute__((packed)) SETATTR_REQ; - -typedef struct smb_com_setattr_rsp { - struct smb_hdr hdr; /* wct = 0 */ - __u16 ByteCount; /* bct = 0 */ -} __attribute__((packed)) SETATTR_RSP; - -/* empty wct response to setattr */ - -/*******************************************************/ -/* NT Transact structure definitions follow */ -/* Currently only ioctl, acl (get security descriptor) */ -/* and notify are implemented */ -/*******************************************************/ -typedef struct smb_com_ntransact_req { - struct smb_hdr hdr; /* wct >= 19 */ - __u8 MaxSetupCount; - __u16 Reserved; - __le32 TotalParameterCount; - __le32 TotalDataCount; - __le32 MaxParameterCount; - __le32 MaxDataCount; - __le32 ParameterCount; - __le32 ParameterOffset; - __le32 DataCount; - __le32 DataOffset; - __u8 SetupCount; /* four setup words follow subcommand */ - /* SNIA spec incorrectly included spurious pad here */ - __le16 SubCommand; /* 2 = IOCTL/FSCTL */ - /* SetupCount words follow then */ - __le16 ByteCount; - __u8 Pad[3]; - __u8 Parms[]; -} __attribute__((packed)) NTRANSACT_REQ; - -typedef struct smb_com_ntransact_rsp { - struct smb_hdr hdr; /* wct = 18 */ - __u8 Reserved[3]; - __le32 TotalParameterCount; - __le32 TotalDataCount; - __le32 ParameterCount; - __le32 ParameterOffset; - __le32 ParameterDisplacement; - __le32 DataCount; - __le32 DataOffset; - __le32 DataDisplacement; - __u8 SetupCount; /* 0 */ - __u16 ByteCount; - /* __u8 Pad[3]; */ - /* parms and data follow */ -} __attribute__((packed)) NTRANSACT_RSP; - -/* See MS-SMB 2.2.7.2.1.1 */ -struct srv_copychunk { - __le64 SourceOffset; - __le64 DestinationOffset; - __le32 CopyLength; - __u32 Reserved; -} __packed; - -typedef struct smb_com_transaction_ioctl_req { - struct smb_hdr hdr; /* wct = 23 */ - __u8 MaxSetupCount; - __u16 Reserved; - __le32 TotalParameterCount; - __le32 TotalDataCount; - __le32 MaxParameterCount; - __le32 MaxDataCount; - __le32 ParameterCount; - __le32 ParameterOffset; - __le32 DataCount; - __le32 DataOffset; - __u8 SetupCount; /* four setup words follow subcommand */ - /* SNIA spec incorrectly included spurious pad here */ - __le16 SubCommand; /* 2 = IOCTL/FSCTL */ - __le32 FunctionCode; - __u16 Fid; - __u8 IsFsctl; /* 1 = File System Control 0 = device control (IOCTL) */ - __u8 IsRootFlag; /* 1 = apply command to root of share (must be DFS) */ - __le16 ByteCount; - __u8 Pad[3]; - __u8 Data[1]; -} __attribute__((packed)) TRANSACT_IOCTL_REQ; - -typedef struct smb_com_transaction_compr_ioctl_req { - struct smb_hdr hdr; /* wct = 23 */ - __u8 MaxSetupCount; - __u16 Reserved; - __le32 TotalParameterCount; - __le32 TotalDataCount; - __le32 MaxParameterCount; - __le32 MaxDataCount; - __le32 ParameterCount; - __le32 ParameterOffset; - __le32 DataCount; - __le32 DataOffset; - __u8 SetupCount; /* four setup words follow subcommand */ - /* SNIA spec incorrectly included spurious pad here */ - __le16 SubCommand; /* 2 = IOCTL/FSCTL */ - __le32 FunctionCode; - __u16 Fid; - __u8 IsFsctl; /* 1 = File System Control 0 = device control (IOCTL) */ - __u8 IsRootFlag; /* 1 = apply command to root of share (must be DFS) */ - __le16 ByteCount; - __u8 Pad[3]; - __le16 compression_state; /* See below for valid flags */ -} __attribute__((packed)) TRANSACT_COMPR_IOCTL_REQ; - -/* compression state flags */ -#define COMPRESSION_FORMAT_NONE 0x0000 -#define COMPRESSION_FORMAT_DEFAULT 0x0001 -#define COMPRESSION_FORMAT_LZNT1 0x0002 - -typedef struct smb_com_transaction_ioctl_rsp { - struct smb_hdr hdr; /* wct = 19 */ - __u8 Reserved[3]; - __le32 TotalParameterCount; - __le32 TotalDataCount; - __le32 ParameterCount; - __le32 ParameterOffset; - __le32 ParameterDisplacement; - __le32 DataCount; - __le32 DataOffset; - __le32 DataDisplacement; - __u8 SetupCount; /* 1 */ - __le16 ReturnedDataLen; - __u16 ByteCount; -} __attribute__((packed)) TRANSACT_IOCTL_RSP; - -#define CIFS_ACL_OWNER 1 -#define CIFS_ACL_GROUP 2 -#define CIFS_ACL_DACL 4 -#define CIFS_ACL_SACL 8 - -typedef struct smb_com_transaction_qsec_req { - struct smb_hdr hdr; /* wct = 19 */ - __u8 MaxSetupCount; - __u16 Reserved; - __le32 TotalParameterCount; - __le32 TotalDataCount; - __le32 MaxParameterCount; - __le32 MaxDataCount; - __le32 ParameterCount; - __le32 ParameterOffset; - __le32 DataCount; - __le32 DataOffset; - __u8 SetupCount; /* no setup words follow subcommand */ - /* SNIA spec incorrectly included spurious pad here */ - __le16 SubCommand; /* 6 = QUERY_SECURITY_DESC */ - __le16 ByteCount; /* bcc = 3 + 8 */ - __u8 Pad[3]; - __u16 Fid; - __u16 Reserved2; - __le32 AclFlags; -} __attribute__((packed)) QUERY_SEC_DESC_REQ; - - -typedef struct smb_com_transaction_ssec_req { - struct smb_hdr hdr; /* wct = 19 */ - __u8 MaxSetupCount; - __u16 Reserved; - __le32 TotalParameterCount; - __le32 TotalDataCount; - __le32 MaxParameterCount; - __le32 MaxDataCount; - __le32 ParameterCount; - __le32 ParameterOffset; - __le32 DataCount; - __le32 DataOffset; - __u8 SetupCount; /* no setup words follow subcommand */ - /* SNIA spec incorrectly included spurious pad here */ - __le16 SubCommand; /* 3 = SET_SECURITY_DESC */ - __le16 ByteCount; /* bcc = 3 + 8 */ - __u8 Pad[3]; - __u16 Fid; - __u16 Reserved2; - __le32 AclFlags; -} __attribute__((packed)) SET_SEC_DESC_REQ; - -typedef struct smb_com_transaction_change_notify_req { - struct smb_hdr hdr; /* wct = 23 */ - __u8 MaxSetupCount; - __u16 Reserved; - __le32 TotalParameterCount; - __le32 TotalDataCount; - __le32 MaxParameterCount; - __le32 MaxDataCount; - __le32 ParameterCount; - __le32 ParameterOffset; - __le32 DataCount; - __le32 DataOffset; - __u8 SetupCount; /* four setup words follow subcommand */ - /* SNIA spec incorrectly included spurious pad here */ - __le16 SubCommand;/* 4 = Change Notify */ - __le32 CompletionFilter; /* operation to monitor */ - __u16 Fid; - __u8 WatchTree; /* 1 = Monitor subdirectories */ - __u8 Reserved2; - __le16 ByteCount; -/* __u8 Pad[3];*/ -/* __u8 Data[1];*/ -} __attribute__((packed)) TRANSACT_CHANGE_NOTIFY_REQ; - -/* BB eventually change to use generic ntransact rsp struct - and validation routine */ -typedef struct smb_com_transaction_change_notify_rsp { - struct smb_hdr hdr; /* wct = 18 */ - __u8 Reserved[3]; - __le32 TotalParameterCount; - __le32 TotalDataCount; - __le32 ParameterCount; - __le32 ParameterOffset; - __le32 ParameterDisplacement; - __le32 DataCount; - __le32 DataOffset; - __le32 DataDisplacement; - __u8 SetupCount; /* 0 */ - __u16 ByteCount; - /* __u8 Pad[3]; */ -} __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 -#define FILE_NOTIFY_CHANGE_NAME 0x00000003 -#define FILE_NOTIFY_CHANGE_ATTRIBUTES 0x00000004 -#define FILE_NOTIFY_CHANGE_SIZE 0x00000008 -#define FILE_NOTIFY_CHANGE_LAST_WRITE 0x00000010 -#define FILE_NOTIFY_CHANGE_LAST_ACCESS 0x00000020 -#define FILE_NOTIFY_CHANGE_CREATION 0x00000040 -#define FILE_NOTIFY_CHANGE_EA 0x00000080 -#define FILE_NOTIFY_CHANGE_SECURITY 0x00000100 -#define FILE_NOTIFY_CHANGE_STREAM_NAME 0x00000200 -#define FILE_NOTIFY_CHANGE_STREAM_SIZE 0x00000400 -#define FILE_NOTIFY_CHANGE_STREAM_WRITE 0x00000800 - -#define FILE_ACTION_ADDED 0x00000001 -#define FILE_ACTION_REMOVED 0x00000002 -#define FILE_ACTION_MODIFIED 0x00000003 -#define FILE_ACTION_RENAMED_OLD_NAME 0x00000004 -#define FILE_ACTION_RENAMED_NEW_NAME 0x00000005 -#define FILE_ACTION_ADDED_STREAM 0x00000006 -#define FILE_ACTION_REMOVED_STREAM 0x00000007 -#define FILE_ACTION_MODIFIED_STREAM 0x00000008 - -/* response contains array of the following structures */ -struct file_notify_information { - __le32 NextEntryOffset; - __le32 Action; - __le32 FileNameLength; - __u8 FileName[]; -} __attribute__((packed)); - -/* For IO_REPARSE_TAG_SYMLINK */ -struct reparse_symlink_data { - __le32 ReparseTag; - __le16 ReparseDataLength; - __u16 Reserved; - __le16 SubstituteNameOffset; - __le16 SubstituteNameLength; - __le16 PrintNameOffset; - __le16 PrintNameLength; - __le32 Flags; - char PathBuffer[]; -} __attribute__((packed)); - -/* Flag above */ -#define SYMLINK_FLAG_RELATIVE 0x00000001 - -/* For IO_REPARSE_TAG_NFS */ -#define NFS_SPECFILE_LNK 0x00000000014B4E4C -#define NFS_SPECFILE_CHR 0x0000000000524843 -#define NFS_SPECFILE_BLK 0x00000000004B4C42 -#define NFS_SPECFILE_FIFO 0x000000004F464946 -#define NFS_SPECFILE_SOCK 0x000000004B434F53 -struct reparse_posix_data { - __le32 ReparseTag; - __le16 ReparseDataLength; - __u16 Reserved; - __le64 InodeType; /* LNK, FIFO, CHR etc. */ - char PathBuffer[]; -} __attribute__((packed)); - -struct cifs_quota_data { - __u32 rsrvd1; /* 0 */ - __u32 sid_size; - __u64 rsrvd2; /* 0 */ - __u64 space_used; - __u64 soft_limit; - __u64 hard_limit; - char sid[1]; /* variable size? */ -} __attribute__((packed)); - -/* quota sub commands */ -#define QUOTA_LIST_CONTINUE 0 -#define QUOTA_LIST_START 0x100 -#define QUOTA_FOR_SID 0x101 - -struct trans2_req { - /* struct smb_hdr hdr precedes. Set wct = 14+ */ - __le16 TotalParameterCount; - __le16 TotalDataCount; - __le16 MaxParameterCount; - __le16 MaxDataCount; - __u8 MaxSetupCount; - __u8 Reserved; - __le16 Flags; - __le32 Timeout; - __u16 Reserved2; - __le16 ParameterCount; - __le16 ParameterOffset; - __le16 DataCount; - __le16 DataOffset; - __u8 SetupCount; - __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 */ - __le16 TotalParameterCount; - __le16 TotalDataCount; - __u16 Reserved; - __le16 ParameterCount; - __le16 ParameterOffset; - __le16 ParameterDisplacement; - __le16 DataCount; - __le16 DataOffset; - __le16 DataDisplacement; - __u8 SetupCount; - __u8 Reserved1; - /* SetupWords[SetupCount]; - __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 -#define SMB_SET_FILE_EA 2 -#define SMB_QUERY_FILE_EA_SIZE 2 -#define SMB_INFO_QUERY_EAS_FROM_LIST 3 -#define SMB_INFO_QUERY_ALL_EAS 4 -#define SMB_INFO_IS_NAME_VALID 6 -#define SMB_QUERY_FILE_BASIC_INFO 0x101 -#define SMB_QUERY_FILE_STANDARD_INFO 0x102 -#define SMB_QUERY_FILE_EA_INFO 0x103 -#define SMB_QUERY_FILE_NAME_INFO 0x104 -#define SMB_QUERY_FILE_ALLOCATION_INFO 0x105 -#define SMB_QUERY_FILE_END_OF_FILEINFO 0x106 -#define SMB_QUERY_FILE_ALL_INFO 0x107 -#define SMB_QUERY_ALT_NAME_INFO 0x108 -#define SMB_QUERY_FILE_STREAM_INFO 0x109 -#define SMB_QUERY_FILE_COMPRESSION_INFO 0x10B -#define SMB_QUERY_FILE_UNIX_BASIC 0x200 -#define SMB_QUERY_FILE_UNIX_LINK 0x201 -#define SMB_QUERY_POSIX_ACL 0x204 -#define SMB_QUERY_XATTR 0x205 /* e.g. system EA name space */ -#define SMB_QUERY_ATTR_FLAGS 0x206 /* append,immutable etc. */ -#define SMB_QUERY_POSIX_PERMISSION 0x207 -#define SMB_QUERY_POSIX_LOCK 0x208 -/* #define SMB_POSIX_OPEN 0x209 */ -/* #define SMB_POSIX_UNLINK 0x20a */ -#define SMB_QUERY_FILE__UNIX_INFO2 0x20b -#define SMB_QUERY_FILE_INTERNAL_INFO 0x3ee -#define SMB_QUERY_FILE_ACCESS_INFO 0x3f0 -#define SMB_QUERY_FILE_NAME_INFO2 0x3f1 /* 0x30 bytes */ -#define SMB_QUERY_FILE_POSITION_INFO 0x3f6 -#define SMB_QUERY_FILE_MODE_INFO 0x3f8 -#define SMB_QUERY_FILE_ALGN_INFO 0x3f9 - - -#define SMB_SET_FILE_BASIC_INFO 0x101 -#define SMB_SET_FILE_DISPOSITION_INFO 0x102 -#define SMB_SET_FILE_ALLOCATION_INFO 0x103 -#define SMB_SET_FILE_END_OF_FILE_INFO 0x104 -#define SMB_SET_FILE_UNIX_BASIC 0x200 -#define SMB_SET_FILE_UNIX_LINK 0x201 -#define SMB_SET_FILE_UNIX_HLINK 0x203 -#define SMB_SET_POSIX_ACL 0x204 -#define SMB_SET_XATTR 0x205 -#define SMB_SET_ATTR_FLAGS 0x206 /* append, immutable etc. */ -#define SMB_SET_POSIX_LOCK 0x208 -#define SMB_POSIX_OPEN 0x209 -#define SMB_POSIX_UNLINK 0x20a -#define SMB_SET_FILE_UNIX_INFO2 0x20b -#define SMB_SET_FILE_BASIC_INFO2 0x3ec -#define SMB_SET_FILE_RENAME_INFORMATION 0x3f2 /* BB check if qpathinfo too */ -#define SMB_FILE_ALL_INFO2 0x3fa -#define SMB_SET_FILE_ALLOCATION_INFO2 0x3fb -#define SMB_SET_FILE_END_OF_FILE_INFO2 0x3fc -#define SMB_FILE_MOVE_CLUSTER_INFO 0x407 -#define SMB_FILE_QUOTA_INFO 0x408 -#define SMB_FILE_REPARSEPOINT_INFO 0x409 -#define SMB_FILE_MAXIMUM_INFO 0x40d - -/* Find File infolevels */ -#define SMB_FIND_FILE_INFO_STANDARD 0x001 -#define SMB_FIND_FILE_QUERY_EA_SIZE 0x002 -#define SMB_FIND_FILE_QUERY_EAS_FROM_LIST 0x003 -#define SMB_FIND_FILE_DIRECTORY_INFO 0x101 -#define SMB_FIND_FILE_FULL_DIRECTORY_INFO 0x102 -#define SMB_FIND_FILE_NAMES_INFO 0x103 -#define SMB_FIND_FILE_BOTH_DIRECTORY_INFO 0x104 -#define SMB_FIND_FILE_ID_FULL_DIR_INFO 0x105 -#define SMB_FIND_FILE_ID_BOTH_DIR_INFO 0x106 -#define SMB_FIND_FILE_UNIX 0x202 -/* #define SMB_FIND_FILE_POSIX_INFO 0x064 */ - -typedef struct smb_com_transaction2_qpi_req { - struct smb_hdr hdr; /* wct = 14+ */ - __le16 TotalParameterCount; - __le16 TotalDataCount; - __le16 MaxParameterCount; - __le16 MaxDataCount; - __u8 MaxSetupCount; - __u8 Reserved; - __le16 Flags; - __le32 Timeout; - __u16 Reserved2; - __le16 ParameterCount; - __le16 ParameterOffset; - __le16 DataCount; - __le16 DataOffset; - __u8 SetupCount; - __u8 Reserved3; - __le16 SubCommand; /* one setup word */ - __le16 ByteCount; - __u8 Pad; - __le16 InformationLevel; - __u32 Reserved4; - char FileName[1]; -} __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 is present for infolevels > 100 */ -} __attribute__((packed)) TRANSACTION2_QPI_RSP; - -typedef struct smb_com_transaction2_spi_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; - __le16 ParameterOffset; - __le16 DataCount; - __le16 DataOffset; - __u8 SetupCount; - __u8 Reserved3; - __le16 SubCommand; /* one setup word */ - __le16 ByteCount; - __u8 Pad; - __u16 Pad1; - __le16 InformationLevel; - __u32 Reserved4; - char FileName[1]; -} __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 is present for infolevels > 100 */ -} __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[]; /* Must be unicode */ -} __attribute__((packed)); - -struct smb_com_transaction2_sfi_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; - __le16 ParameterOffset; - __le16 DataCount; - __le16 DataOffset; - __u8 SetupCount; - __u8 Reserved3; - __le16 SubCommand; /* one setup word */ - __le16 ByteCount; - __u8 Pad; - __u16 Pad1; - __u16 Fid; - __le16 InformationLevel; - __u16 Reserved4; - __u8 payload[]; -} __attribute__((packed)); - -struct smb_com_transaction2_sfi_rsp { - struct smb_hdr hdr; /* wct = 10 + SetupCount */ - struct trans2_resp t2; - __u16 ByteCount; - __u16 Reserved2; /* parameter word reserved - - present for infolevels > 100 */ -} __attribute__((packed)); - -struct smb_t2_qfi_req { - struct smb_hdr hdr; - struct trans2_req t2; - __u8 Pad; - __u16 Fid; - __le16 InformationLevel; -} __attribute__((packed)); - -struct smb_t2_qfi_rsp { - struct smb_hdr hdr; /* wct = 10 + SetupCount */ - struct trans2_resp t2; - __u16 ByteCount; - __u16 Reserved2; /* parameter word reserved - - present for infolevels > 100 */ -} __attribute__((packed)); - -/* - * Flags on T2 FINDFIRST and FINDNEXT - */ -#define CIFS_SEARCH_CLOSE_ALWAYS 0x0001 -#define CIFS_SEARCH_CLOSE_AT_END 0x0002 -#define CIFS_SEARCH_RETURN_RESUME 0x0004 -#define CIFS_SEARCH_CONTINUE_FROM_LAST 0x0008 -#define CIFS_SEARCH_BACKUP_SEARCH 0x0010 - -/* - * Size of the resume key on FINDFIRST and FINDNEXT calls - */ -#define CIFS_SMB_RESUME_KEY_SIZE 4 - -typedef struct smb_com_transaction2_ffirst_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; - __le16 ParameterOffset; - __le16 DataCount; - __le16 DataOffset; - __u8 SetupCount; /* one */ - __u8 Reserved3; - __le16 SubCommand; /* TRANS2_FIND_FIRST */ - __le16 ByteCount; - __u8 Pad; - __le16 SearchAttributes; - __le16 SearchCount; - __le16 SearchFlags; - __le16 InformationLevel; - __le32 SearchStorageType; - char FileName[1]; -} __attribute__((packed)) TRANSACTION2_FFIRST_REQ; - -typedef struct smb_com_transaction2_ffirst_rsp { - struct smb_hdr hdr; /* wct = 10 */ - struct trans2_resp t2; - __u16 ByteCount; -} __attribute__((packed)) TRANSACTION2_FFIRST_RSP; - -typedef struct smb_com_transaction2_ffirst_rsp_parms { - __u16 SearchHandle; - __le16 SearchCount; - __le16 EndofSearch; - __le16 EAErrorOffset; - __le16 LastNameOffset; -} __attribute__((packed)) T2_FFIRST_RSP_PARMS; - -typedef struct smb_com_transaction2_fnext_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; - __le16 ParameterOffset; - __le16 DataCount; - __le16 DataOffset; - __u8 SetupCount; /* one */ - __u8 Reserved3; - __le16 SubCommand; /* TRANS2_FIND_NEXT */ - __le16 ByteCount; - __u8 Pad; - __u16 SearchHandle; - __le16 SearchCount; - __le16 InformationLevel; - __u32 ResumeKey; - __le16 SearchFlags; - char ResumeFileName[]; -} __attribute__((packed)) TRANSACTION2_FNEXT_REQ; - -typedef struct smb_com_transaction2_fnext_rsp { - struct smb_hdr hdr; /* wct = 10 */ - struct trans2_resp t2; - __u16 ByteCount; -} __attribute__((packed)) TRANSACTION2_FNEXT_RSP; - -typedef struct smb_com_transaction2_fnext_rsp_parms { - __le16 SearchCount; - __le16 EndofSearch; - __le16 EAErrorOffset; - __le16 LastNameOffset; -} __attribute__((packed)) T2_FNEXT_RSP_PARMS; - -/* QFSInfo Levels */ -#define SMB_INFO_ALLOCATION 1 -#define SMB_INFO_VOLUME 2 -#define SMB_QUERY_FS_VOLUME_INFO 0x102 -#define SMB_QUERY_FS_SIZE_INFO 0x103 -#define SMB_QUERY_FS_DEVICE_INFO 0x104 -#define SMB_QUERY_FS_ATTRIBUTE_INFO 0x105 -#define SMB_QUERY_CIFS_UNIX_INFO 0x200 -#define SMB_QUERY_POSIX_FS_INFO 0x201 -#define SMB_QUERY_POSIX_WHO_AM_I 0x202 -#define SMB_REQUEST_TRANSPORT_ENCRYPTION 0x203 -#define SMB_QUERY_FS_PROXY 0x204 /* WAFS enabled. Returns structure - FILE_SYSTEM__UNIX_INFO to tell - whether new NTIOCTL available - (0xACE) for WAN friendly SMB - operations to be carried */ -#define SMB_QUERY_LABEL_INFO 0x3ea -#define SMB_QUERY_FS_QUOTA_INFO 0x3ee -#define SMB_QUERY_FS_FULL_SIZE_INFO 0x3ef -#define SMB_QUERY_OBJECTID_INFO 0x3f0 - -typedef struct smb_com_transaction2_qfsi_req { - struct smb_hdr hdr; /* wct = 14+ */ - __le16 TotalParameterCount; - __le16 TotalDataCount; - __le16 MaxParameterCount; - __le16 MaxDataCount; - __u8 MaxSetupCount; - __u8 Reserved; - __le16 Flags; - __le32 Timeout; - __u16 Reserved2; - __le16 ParameterCount; - __le16 ParameterOffset; - __le16 DataCount; - __le16 DataOffset; - __u8 SetupCount; - __u8 Reserved3; - __le16 SubCommand; /* one setup word */ - __le16 ByteCount; - __u8 Pad; - __le16 InformationLevel; -} __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 */ -} __attribute__((packed)) TRANSACTION2_QFSI_RSP; - -typedef struct whoami_rsp_data { /* Query level 0x202 */ - __u32 flags; /* 0 = Authenticated user 1 = GUEST */ - __u32 mask; /* which flags bits server understands ie 0x0001 */ - __u64 unix_user_id; - __u64 unix_user_gid; - __u32 number_of_supplementary_gids; /* may be zero */ - __u32 number_of_sids; /* may be zero */ - __u32 length_of_sid_array; /* in bytes - may be zero */ - __u32 pad; /* reserved - MBZ */ - /* __u64 gid_array[0]; */ /* may be empty */ - /* __u8 * psid_list */ /* may be empty */ -} __attribute__((packed)) WHOAMI_RSP_DATA; - -/* SETFSInfo Levels */ -#define SMB_SET_CIFS_UNIX_INFO 0x200 -/* level 0x203 is defined above in list of QFS info levels */ -/* #define SMB_REQUEST_TRANSPORT_ENCRYPTION 0x203 */ - -/* Level 0x200 request structure follows */ -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; - -/* level 0x203 request structure follows */ -typedef struct smb_com_transaction2_setfs_enc_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 Reserved4; /* Parameters start. */ - __le16 InformationLevel;/* Parameters end. */ - /* NTLMSSP Blob, Data start. */ -} __attribute__((packed)) TRANSACTION2_SETFSI_ENC_REQ; - -/* response for setfsinfo levels 0x200 and 0x203 */ -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 */ - __le16 TotalParameterCount; - __le16 TotalDataCount; - __le16 MaxParameterCount; - __le16 MaxDataCount; - __u8 MaxSetupCount; - __u8 Reserved; - __le16 Flags; - __le32 Timeout; - __u16 Reserved2; - __le16 ParameterCount; - __le16 ParameterOffset; - __le16 DataCount; - __le16 DataOffset; - __u8 SetupCount; - __u8 Reserved3; - __le16 SubCommand; /* one setup word */ - __le16 ByteCount; - __u8 Pad[3]; /* Win2K has sent 0x0F01 (max response length - perhaps?) followed by one byte pad - doesn't - seem to matter though */ - __le16 MaxReferralLevel; - char RequestFileName[1]; -} __attribute__((packed)) TRANSACTION2_GET_DFS_REFER_REQ; - -#define DFS_VERSION cpu_to_le16(0x0003) - -/* DFS server target type */ -#define DFS_TYPE_LINK 0x0000 /* also for sysvol targets */ -#define DFS_TYPE_ROOT 0x0001 - -/* Referral Entry Flags */ -#define DFS_NAME_LIST_REF 0x0200 /* set for domain or DC referral responses */ -#define DFS_TARGET_SET_BOUNDARY 0x0400 /* only valid with version 4 dfs req */ - -typedef struct dfs_referral_level_3 { /* version 4 is same, + one flag bit */ - __le16 VersionNumber; /* must be 3 or 4 */ - __le16 Size; - __le16 ServerType; /* 0x0001 = root targets; 0x0000 = link targets */ - __le16 ReferralEntryFlags; - __le32 TimeToLive; - __le16 DfsPathOffset; - __le16 DfsAlternatePathOffset; - __le16 NetworkAddressOffset; /* offset of the link target */ - __u8 ServiceSiteGuid[16]; /* MBZ, ignored */ -} __attribute__((packed)) REFERRAL3; - -struct get_dfs_referral_rsp { - __le16 PathConsumed; - __le16 NumberOfReferrals; - __le32 DFSFlags; - REFERRAL3 referrals[1]; /* array of level 3 dfs_referral structures */ - /* followed by the strings pointed to by the referral structures */ -} __packed; - -typedef struct smb_com_transaction_get_dfs_refer_rsp { - struct smb_hdr hdr; /* wct = 10 */ - struct trans2_resp t2; - __u16 ByteCount; - __u8 Pad; - struct get_dfs_referral_rsp dfs_data; -} __packed TRANSACTION2_GET_DFS_REFER_RSP; - -/* DFS Flags */ -#define DFSREF_REFERRAL_SERVER 0x00000001 /* all targets are DFS roots */ -#define DFSREF_STORAGE_SERVER 0x00000002 /* no further ref requests needed */ -#define DFSREF_TARGET_FAILBACK 0x00000004 /* only for DFS referral version 4 */ - -/* - ************************************************************************ - * All structs for everything above the SMB PDUs themselves - * (such as the T2 level specific data) go here - ************************************************************************ - */ - -/* - * Information on a server - */ - -struct serverInfo { - char name[16]; - unsigned char versionMajor; - unsigned char versionMinor; - unsigned long type; - unsigned int commentOffset; -} __attribute__((packed)); - -/* - * The following structure is the format of the data returned on a NetShareEnum - * with level "90" (x5A) - */ - -struct shareInfo { - char shareName[13]; - 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; -} __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; -} __attribute__((packed)) FILE_SYSTEM_UNIX_INFO; /* Unix extension 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 /* Allow POSIX path chars */ -#define CIFS_UNIX_POSIX_PATH_OPS_CAP 0x00000020 /* Allow new POSIX path based - calls including posix open - and posix unlink */ -#define CIFS_UNIX_LARGE_READ_CAP 0x00000040 /* support reads >128K (up - to 0xFFFF00 */ -#define CIFS_UNIX_LARGE_WRITE_CAP 0x00000080 -#define CIFS_UNIX_TRANSPORT_ENCRYPTION_CAP 0x00000100 /* can do SPNEGO crypt */ -#define CIFS_UNIX_TRANSPORT_ENCRYPTION_MANDATORY_CAP 0x00000200 /* must do */ -#define CIFS_UNIX_PROXY_CAP 0x00000400 /* Proxy cap: 0xACE ioctl and - QFS PROXY call */ -#ifdef CONFIG_CIFS_POSIX -/* presumably don't need the 0x20 POSIX_PATH_OPS_CAP since we never send - LockingX instead of posix locking call on unix sess (and we do not expect - LockingX to use different (ie Windows) semantics than posix locking on - the same session (if WINE needs to do this later, we can add this cap - back in later */ -/* #define CIFS_UNIX_CAP_MASK 0x000000fb */ -#define CIFS_UNIX_CAP_MASK 0x000003db -#else -#define CIFS_UNIX_CAP_MASK 0x00000013 -#endif /* CONFIG_CIFS_POSIX */ - - -#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 */ - __le32 BlockSize; - /* The next three fields are in terms of the block size. - (above). If block size is unknown, 4096 would be a - reasonable block size for a server to report. - Note that returning the blocks/blocksavail removes need - to make a second call (to QFSInfo level 0x103 to get this info. - UserBlockAvail is typically less than or equal to BlocksAvail, - if no distinction is made return the same value in each */ - __le64 TotalBlocks; - __le64 BlocksAvail; /* bfree */ - __le64 UserBlocksAvail; /* bavail */ - /* For undefined Node fields or FSID return -1 */ - __le64 TotalFileNodes; - __le64 FreeFileNodes; - __le64 FileSysIdentifier; /* fsid */ - /* NB Namelen comes from FILE_SYSTEM_ATTRIBUTE_INFO call */ - /* NB flags can come from FILE_SYSTEM_DEVICE_INFO call */ -} __attribute__((packed)) FILE_SYSTEM_POSIX_INFO; - -/* DeviceType Flags */ -#define FILE_DEVICE_CD_ROM 0x00000002 -#define FILE_DEVICE_CD_ROM_FILE_SYSTEM 0x00000003 -#define FILE_DEVICE_DFS 0x00000006 -#define FILE_DEVICE_DISK 0x00000007 -#define FILE_DEVICE_DISK_FILE_SYSTEM 0x00000008 -#define FILE_DEVICE_FILE_SYSTEM 0x00000009 -#define FILE_DEVICE_NAMED_PIPE 0x00000011 -#define FILE_DEVICE_NETWORK 0x00000012 -#define FILE_DEVICE_NETWORK_FILE_SYSTEM 0x00000014 -#define FILE_DEVICE_NULL 0x00000015 -#define FILE_DEVICE_PARALLEL_PORT 0x00000016 -#define FILE_DEVICE_PRINTER 0x00000018 -#define FILE_DEVICE_SERIAL_PORT 0x0000001b -#define FILE_DEVICE_STREAMS 0x0000001e -#define FILE_DEVICE_TAPE 0x0000001f -#define FILE_DEVICE_TAPE_FILE_SYSTEM 0x00000020 -#define FILE_DEVICE_VIRTUAL_DISK 0x00000024 -#define FILE_DEVICE_NETWORK_REDIRECTOR 0x00000028 - -/* Device Characteristics */ -#define FILE_REMOVABLE_MEDIA 0x00000001 -#define FILE_READ_ONLY_DEVICE 0x00000002 -#define FILE_FLOPPY_DISKETTE 0x00000004 -#define FILE_WRITE_ONCE_MEDIA 0x00000008 -#define FILE_REMOTE_DEVICE 0x00000010 -#define FILE_DEVICE_IS_MOUNTED 0x00000020 -#define FILE_VIRTUAL_VOLUME 0x00000040 -#define FILE_DEVICE_SECURE_OPEN 0x00000100 -#define FILE_CHARACTERISTIC_TS_DEVICE 0x00001000 -#define FILE_CHARACTERISTIC_WEBDAV_DEVICE 0x00002000 -#define FILE_PORTABLE_DEVICE 0x00004000 -#define FILE_DEVICE_ALLOW_APPCONTAINER_TRAVERSAL 0x00020000 - -typedef struct { - __le32 DeviceType; - __le32 DeviceCharacteristics; -} __attribute__((packed)) FILE_SYSTEM_DEVICE_INFO; /* device info level 0x104 */ - -/* minimum includes first three fields, and empty FS Name */ -#define MIN_FS_ATTR_INFO_SIZE 12 - - -/* List of FileSystemAttributes - see 2.5.1 of MS-FSCC */ -#define FILE_SUPPORTS_SPARSE_VDL 0x10000000 /* faster nonsparse extend */ -#define FILE_SUPPORTS_BLOCK_REFCOUNTING 0x08000000 /* allow ioctl dup extents */ -#define FILE_SUPPORT_INTEGRITY_STREAMS 0x04000000 -#define FILE_SUPPORTS_USN_JOURNAL 0x02000000 -#define FILE_SUPPORTS_OPEN_BY_FILE_ID 0x01000000 -#define FILE_SUPPORTS_EXTENDED_ATTRIBUTES 0x00800000 -#define FILE_SUPPORTS_HARD_LINKS 0x00400000 -#define FILE_SUPPORTS_TRANSACTIONS 0x00200000 -#define FILE_SEQUENTIAL_WRITE_ONCE 0x00100000 -#define FILE_READ_ONLY_VOLUME 0x00080000 -#define FILE_NAMED_STREAMS 0x00040000 -#define FILE_SUPPORTS_ENCRYPTION 0x00020000 -#define FILE_SUPPORTS_OBJECT_IDS 0x00010000 -#define FILE_VOLUME_IS_COMPRESSED 0x00008000 -#define FILE_SUPPORTS_REMOTE_STORAGE 0x00000100 -#define FILE_SUPPORTS_REPARSE_POINTS 0x00000080 -#define FILE_SUPPORTS_SPARSE_FILES 0x00000040 -#define FILE_VOLUME_QUOTAS 0x00000020 -#define FILE_FILE_COMPRESSION 0x00000010 -#define FILE_PERSISTENT_ACLS 0x00000008 -#define FILE_UNICODE_ON_DISK 0x00000004 -#define FILE_CASE_PRESERVED_NAMES 0x00000002 -#define FILE_CASE_SENSITIVE_SEARCH 0x00000001 -typedef struct { - __le32 Attributes; - __le32 MaxPathNameComponentLength; - __le32 FileSystemNameLen; - char FileSystemName[52]; /* do not have to save this - get subset? */ -} __attribute__((packed)) FILE_SYSTEM_ATTRIBUTE_INFO; - -/******************************************************************************/ -/* QueryFileInfo/QueryPathinfo (also for SetPath/SetFile) data buffer formats */ -/******************************************************************************/ -typedef struct { /* data block encoding of response to level 263 QPathInfo */ - __le64 CreationTime; - __le64 LastAccessTime; - __le64 LastWriteTime; - __le64 ChangeTime; - __le32 Attributes; - __u32 Pad1; - __le64 AllocationSize; - __le64 EndOfFile; /* size ie offset to first free byte in file */ - __le32 NumberOfLinks; /* hard links */ - __u8 DeletePending; - __u8 Directory; - __u16 Pad2; - __le64 IndexNumber; - __le32 EASize; - __le32 AccessFlags; - __u64 IndexNumber1; - __le64 CurrentByteOffset; - __le32 Mode; - __le32 AlignmentRequirement; - __le32 FileNameLength; - char FileName[1]; -} __attribute__((packed)) FILE_ALL_INFO; /* level 0x107 QPathInfo */ - -typedef struct { - __le64 AllocationSize; - __le64 EndOfFile; /* size ie offset to first free byte in file */ - __le32 NumberOfLinks; /* hard links */ - __u8 DeletePending; - __u8 Directory; - __u16 Pad; -} __attribute__((packed)) FILE_STANDARD_INFO; /* level 0x102 QPathInfo */ - - -/* defines for enumerating possible values of the Unix type field below */ -#define UNIX_FILE 0 -#define UNIX_DIR 1 -#define UNIX_SYMLINK 2 -#define UNIX_CHARDEV 3 -#define UNIX_BLOCKDEV 4 -#define UNIX_FIFO 5 -#define UNIX_SOCKET 6 -typedef struct { - __le64 EndOfFile; - __le64 NumOfBytes; - __le64 LastStatusChange; /*SNIA specs DCE time for the 3 time fields */ - __le64 LastAccessTime; - __le64 LastModificationTime; - __le64 Uid; - __le64 Gid; - __le32 Type; - __le64 DevMajor; - __le64 DevMinor; - __le64 UniqueId; - __le64 Permissions; - __le64 Nlinks; -} __attribute__((packed)) FILE_UNIX_BASIC_INFO; /* level 0x200 QPathInfo */ - -typedef struct { - char LinkDest[1]; -} __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 - the primitive DOS time format */ -typedef struct { - __u16 Day:5; - __u16 Month:4; - __u16 Year:7; -} __attribute__((packed)) SMB_DATE; - -typedef struct { - __u16 TwoSeconds:5; - __u16 Minutes:6; - __u16 Hours:5; -} __attribute__((packed)) SMB_TIME; - -typedef struct { - __le16 CreationDate; /* SMB Date see above */ - __le16 CreationTime; /* SMB Time */ - __le16 LastAccessDate; - __le16 LastAccessTime; - __le16 LastWriteDate; - __le16 LastWriteTime; - __le32 DataSize; /* File Size (EOF) */ - __le32 AllocationSize; - __le16 Attributes; /* verify not u32 */ - __le32 EASize; -} __attribute__((packed)) FILE_INFO_STANDARD; /* level 1 SetPath/FileInfo */ - -typedef struct { - __le64 CreationTime; - __le64 LastAccessTime; - __le64 LastWriteTime; - __le64 ChangeTime; - __le32 Attributes; - __u32 Pad; -} __attribute__((packed)) FILE_BASIC_INFO; /* size info, level 0x101 */ - -struct file_allocation_info { - __le64 AllocationSize; /* Note old Samba srvr rounds this up too much */ -} __attribute__((packed)); /* size used on disk, for level 0x103 for set, - 0x105 for query */ - -struct file_end_of_file_info { - __le64 FileSize; /* offset to end of file */ -} __attribute__((packed)); /* size info, level 0x104 for set, 0x106 for query */ - -struct file_alt_name_info { - __u8 alt_name[1]; -} __attribute__((packed)); /* level 0x0108 */ - -struct file_stream_info { - __le32 number_of_streams; /* BB check sizes and verify location */ - /* followed by info on streams themselves - u64 size; - u64 allocation_size - stream info */ -}; /* level 0x109 */ - -struct file_compression_info { - __le64 compressed_size; - __le16 format; - __u8 unit_shift; - __u8 ch_shift; - __u8 cl_shift; - __u8 pad[3]; -} __attribute__((packed)); /* level 0x10b */ - -/* POSIX ACL set/query path info structures */ -#define CIFS_ACL_VERSION 1 -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; - __le16 access_entry_count; /* access ACL - count of entries */ - __le16 default_entry_count; /* default ACL - count of entries */ - struct cifs_posix_ace ace_array[]; - /* followed by - struct cifs_posix_ace default_ace_arraay[] */ -} __attribute__((packed)); /* level 0x204 */ - -/* types of access control entries already defined in posix_acl.h */ -/* #define CIFS_POSIX_ACL_USER_OBJ 0x01 -#define CIFS_POSIX_ACL_USER 0x02 -#define CIFS_POSIX_ACL_GROUP_OBJ 0x04 -#define CIFS_POSIX_ACL_GROUP 0x08 -#define CIFS_POSIX_ACL_MASK 0x10 -#define CIFS_POSIX_ACL_OTHER 0x20 */ - -/* types of perms */ -/* #define CIFS_POSIX_ACL_EXECUTE 0x01 -#define CIFS_POSIX_ACL_WRITE 0x02 -#define CIFS_POSIX_ACL_READ 0x04 */ - -/* end of POSIX ACL definitions */ - -/* POSIX Open Flags */ -#define SMB_O_RDONLY 0x1 -#define SMB_O_WRONLY 0x2 -#define SMB_O_RDWR 0x4 -#define SMB_O_CREAT 0x10 -#define SMB_O_EXCL 0x20 -#define SMB_O_TRUNC 0x40 -#define SMB_O_APPEND 0x80 -#define SMB_O_SYNC 0x100 -#define SMB_O_DIRECTORY 0x200 -#define SMB_O_NOFOLLOW 0x400 -#define SMB_O_DIRECT 0x800 - -typedef struct { - __le32 OpenFlags; /* same as NT CreateX */ - __le32 PosixOpenFlags; - __le64 Permissions; - __le16 Level; /* reply level requested (see QPathInfo levels) */ -} __attribute__((packed)) OPEN_PSX_REQ; /* level 0x209 SetPathInfo data */ - -typedef struct { - __le16 OplockFlags; - __u16 Fid; - __le32 CreateAction; - __le16 ReturnedLevel; - __le16 Pad; - /* struct following varies based on requested level */ -} __attribute__((packed)) OPEN_PSX_RSP; /* level 0x209 SetPathInfo data */ - -#define SMB_POSIX_UNLINK_FILE_TARGET 0 -#define SMB_POSIX_UNLINK_DIRECTORY_TARGET 1 - -struct unlink_psx_rq { /* level 0x20a SetPathInfo */ - __le16 type; -} __attribute__((packed)); - -struct file_internal_info { - __le64 UniqueId; /* inode number */ -} __attribute__((packed)); /* level 0x3ee */ - -struct file_mode_info { - __le32 Mode; -} __attribute__((packed)); /* level 0x3f8 */ - -struct file_attrib_tag { - __le32 Attribute; - __le32 ReparseTag; -} __attribute__((packed)); /* level 0x40b */ - - -/********************************************************/ -/* FindFirst/FindNext transact2 data buffer formats */ -/********************************************************/ - -typedef struct { - __le32 NextEntryOffset; - __u32 ResumeKey; /* as with FileIndex - no need to convert */ - FILE_UNIX_BASIC_INFO basic; - char FileName[1]; -} __attribute__((packed)) FILE_UNIX_INFO; /* level 0x202 */ - -typedef struct { - __le32 NextEntryOffset; - __u32 FileIndex; - __le64 CreationTime; - __le64 LastAccessTime; - __le64 LastWriteTime; - __le64 ChangeTime; - __le64 EndOfFile; - __le64 AllocationSize; - __le32 ExtFileAttributes; - __le32 FileNameLength; - char FileName[1]; -} __attribute__((packed)) FILE_DIRECTORY_INFO; /* level 0x101 FF resp data */ - -typedef struct { - __le32 NextEntryOffset; - __u32 FileIndex; - __le64 CreationTime; - __le64 LastAccessTime; - __le64 LastWriteTime; - __le64 ChangeTime; - __le64 EndOfFile; - __le64 AllocationSize; - __le32 ExtFileAttributes; - __le32 FileNameLength; - __le32 EaSize; /* length of the xattrs */ - char FileName[1]; -} __attribute__((packed)) FILE_FULL_DIRECTORY_INFO; /* level 0x102 rsp data */ - -typedef struct { - __le32 NextEntryOffset; - __u32 FileIndex; - __le64 CreationTime; - __le64 LastAccessTime; - __le64 LastWriteTime; - __le64 ChangeTime; - __le64 EndOfFile; - __le64 AllocationSize; - __le32 ExtFileAttributes; - __le32 FileNameLength; - __le32 EaSize; /* EA size */ - __le32 Reserved; - __le64 UniqueId; /* inode num - le since Samba puts ino in low 32 bit*/ - char FileName[1]; -} __attribute__((packed)) SEARCH_ID_FULL_DIR_INFO; /* level 0x105 FF rsp data */ - -typedef struct { - __le32 NextEntryOffset; - __u32 FileIndex; - __le64 CreationTime; - __le64 LastAccessTime; - __le64 LastWriteTime; - __le64 ChangeTime; - __le64 EndOfFile; - __le64 AllocationSize; - __le32 ExtFileAttributes; - __le32 FileNameLength; - __le32 EaSize; /* length of the xattrs */ - __u8 ShortNameLength; - __u8 Reserved; - __u8 ShortName[24]; - char FileName[1]; -} __attribute__((packed)) FILE_BOTH_DIRECTORY_INFO; /* level 0x104 FFrsp data */ - -typedef struct { - __u32 ResumeKey; - __le16 CreationDate; /* SMB Date */ - __le16 CreationTime; /* SMB Time */ - __le16 LastAccessDate; - __le16 LastAccessTime; - __le16 LastWriteDate; - __le16 LastWriteTime; - __le32 DataSize; /* File Size (EOF) */ - __le32 AllocationSize; - __le16 Attributes; /* verify not u32 */ - __u8 FileNameLength; - char FileName[1]; -} __attribute__((packed)) FIND_FILE_STANDARD_INFO; /* level 0x1 FF resp data */ - - -struct win_dev { - unsigned char type[8]; /* IntxCHR or IntxBLK */ - __le64 major; - __le64 minor; -} __attribute__((packed)); - -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; - __u8 name_len; - __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 -/* - For better POSIX semantics from Linux client, (even better - than the existing CIFS Unix Extensions) we need updated PDUs for: - - 1) PosixCreateX - to set and return the mode, inode#, device info and - 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) 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: - 5) FindClose2 (return nanosecond timestamp ??) - 6) Use nanosecond timestamps throughout all time fields if - corresponding attribute flag is set - 7) sendfile - handle based copy - - what about fixing 64 bit alignment - - There are also various legacy SMB/CIFS requests used as is - - From existing Lanman and NTLM dialects: - -------------------------------------- - NEGOTIATE - SESSION_SETUP_ANDX (BB which?) - TREE_CONNECT_ANDX (BB which wct?) - TREE_DISCONNECT (BB add volume timestamp on response) - LOGOFF_ANDX - DELETE (note delete open file behavior) - DELETE_DIRECTORY - READ_AND_X - WRITE_AND_X - LOCKING_AND_X (note posix lock semantics) - RENAME (note rename across dirs and open file rename posix behaviors) - NT_RENAME (for hardlinks) Is this good enough for all features? - FIND_CLOSE2 - TRANSACTION2 (18 cases) - SMB_SET_FILE_END_OF_FILE_INFO2 SMB_SET_PATH_END_OF_FILE_INFO2 - (BB verify that never need to set allocation size) - SMB_SET_FILE_BASIC_INFO2 (setting times - BB can it be done via - Unix ext?) - - COPY (note support for copy across directories) - FUTURE, OPTIONAL - setting/getting OS/2 EAs - FUTURE (BB can this handle - setting Linux xattrs perfectly) - OPTIONAL - dnotify - FUTURE, OPTIONAL - quota - FUTURE, OPTIONAL - - Note that various requests implemented for NT interop such as - NT_TRANSACT (IOCTL) QueryReparseInfo - are unneeded to servers compliant with the CIFS POSIX extensions - - From CIFS Unix Extensions: - ------------------------- - T2 SET_PATH_INFO (SMB_SET_FILE_UNIX_LINK) for symlinks - T2 SET_PATH_INFO (SMB_SET_FILE_BASIC_INFO2) - T2 QUERY_PATH_INFO (SMB_QUERY_FILE_UNIX_LINK) - T2 QUERY_PATH_INFO (SMB_QUERY_FILE_UNIX_BASIC) BB check for missing - inode fields - Actually a need QUERY_FILE_UNIX_INFO - since has inode num - BB what about a) blksize/blkbits/blocks - b) i_version - c) i_rdev - d) notify mask? - e) generation - f) size_seqcount - T2 FIND_FIRST/FIND_NEXT FIND_FILE_UNIX - TRANS2_GET_DFS_REFERRAL - OPTIONAL but recommended - T2_QFS_INFO QueryDevice/AttributeInfo - OPTIONAL - */ - -/* 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 - based symlinks). For such a file to be recognized - as containing symlink data: - - 1) file size must be 1067, - 2) signature must begin file data, - 3) length field must be set to ASCII representation - of a number which is less than or equal to 1024, - 4) md5 must match that of the path data */ - -struct xsymlink { - /* 1067 bytes */ - char signature[4]; /* XSym */ /* not null terminated */ - char cr0; /* \n */ -/* ASCII representation of length (4 bytes decimal) terminated by \n not null */ - char length[4]; - char cr1; /* \n */ -/* md5 of valid subset of path ie path[0] through path[length-1] */ - __u8 md5[32]; - 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 */ - __u32 xattr_name_len; - __u32 xattr_value_len; - char xattr_name[]; - /* followed by xattr_value[xattr_value_len], no pad */ -} __attribute__((packed)) FILE_XATTR_INFO; /* extended attribute info - level 0x205 */ - -/* flags for lsattr and chflags commands removed arein uapi/linux/fs.h */ - -typedef struct file_chattr_info { - __le64 mask; /* list of all possible attribute bits */ - __le64 mode; /* list of actual attribute bits on this inode */ -} __attribute__((packed)) FILE_CHATTR_INFO; /* ext attributes - (chattr, chflags) level 0x206 */ -#endif /* POSIX */ -#endif /* _CIFSPDU_H */ diff --git a/fs/cifs/cifsproto.h b/fs/cifs/cifsproto.h deleted file mode 100644 index 98513f5af3f96..0000000000000 --- a/fs/cifs/cifsproto.h +++ /dev/null @@ -1,696 +0,0 @@ -/* SPDX-License-Identifier: LGPL-2.1 */ -/* - * - * Copyright (c) International Business Machines Corp., 2002,2008 - * Author(s): Steve French (sfrench@us.ibm.com) - * - */ -#ifndef _CIFSPROTO_H -#define _CIFSPROTO_H -#include -#include "trace.h" -#ifdef CONFIG_CIFS_DFS_UPCALL -#include "dfs_cache.h" -#endif - -struct statfs; -struct smb_rqst; -struct smb3_fs_context; - -/* - ***************************************************************** - * All Prototypes - ***************************************************************** - */ - -extern struct smb_hdr *cifs_buf_get(void); -extern void cifs_buf_release(void *); -extern struct smb_hdr *cifs_small_buf_get(void); -extern void cifs_small_buf_release(void *); -extern void free_rsp_buf(int, void *); -extern int smb_send(struct TCP_Server_Info *, struct smb_hdr *, - unsigned int /* length */); -extern unsigned int _get_xid(void); -extern void _free_xid(unsigned int); -#define get_xid() \ -({ \ - unsigned int __xid = _get_xid(); \ - cifs_dbg(FYI, "VFS: in %s as Xid: %u with uid: %d\n", \ - __func__, __xid, \ - from_kuid(&init_user_ns, current_fsuid())); \ - trace_smb3_enter(__xid, __func__); \ - __xid; \ -}) - -#define free_xid(curr_xid) \ -do { \ - _free_xid(curr_xid); \ - cifs_dbg(FYI, "VFS: leaving %s (xid = %u) rc = %d\n", \ - __func__, curr_xid, (int)rc); \ - if (rc) \ - trace_smb3_exit_err(curr_xid, __func__, (int)rc); \ - else \ - trace_smb3_exit_done(curr_xid, __func__); \ -} while (0) -extern int init_cifs_idmap(void); -extern void exit_cifs_idmap(void); -extern int init_cifs_spnego(void); -extern void exit_cifs_spnego(void); -extern const char *build_path_from_dentry(struct dentry *, void *); -extern char *build_path_from_dentry_optional_prefix(struct dentry *direntry, - void *page, bool prefix); -static inline void *alloc_dentry_path(void) -{ - return __getname(); -} - -static inline void free_dentry_path(void *page) -{ - if (page) - __putname(page); -} - -extern char *cifs_build_path_to_root(struct smb3_fs_context *ctx, - struct cifs_sb_info *cifs_sb, - struct cifs_tcon *tcon, - int add_treename); -extern char *build_wildcard_path_from_dentry(struct dentry *direntry); -extern char *cifs_compose_mount_options(const char *sb_mountdata, - const char *fullpath, const struct dfs_info3_param *ref, - char **devname); -extern void delete_mid(struct mid_q_entry *mid); -extern void release_mid(struct mid_q_entry *mid); -extern void cifs_wake_up_task(struct mid_q_entry *mid); -extern int cifs_handle_standard(struct TCP_Server_Info *server, - struct mid_q_entry *mid); -extern int smb3_parse_devname(const char *devname, struct smb3_fs_context *ctx); -extern int smb3_parse_opt(const char *options, const char *key, char **val); -extern bool cifs_match_ipaddr(struct sockaddr *srcaddr, struct sockaddr *rhs); -extern int cifs_discard_remaining_data(struct TCP_Server_Info *server); -extern int cifs_call_async(struct TCP_Server_Info *server, - struct smb_rqst *rqst, - mid_receive_t *receive, mid_callback_t *callback, - mid_handle_t *handle, void *cbdata, const int flags, - const struct cifs_credits *exist_credits); -extern struct TCP_Server_Info *cifs_pick_channel(struct cifs_ses *ses); -extern int cifs_send_recv(const unsigned int xid, struct cifs_ses *ses, - struct TCP_Server_Info *server, - struct smb_rqst *rqst, int *resp_buf_type, - const int flags, struct kvec *resp_iov); -extern int compound_send_recv(const unsigned int xid, struct cifs_ses *ses, - struct TCP_Server_Info *server, - const int flags, const int num_rqst, - struct smb_rqst *rqst, int *resp_buf_type, - struct kvec *resp_iov); -extern int SendReceive(const unsigned int /* xid */ , struct cifs_ses *, - struct smb_hdr * /* input */ , - struct smb_hdr * /* out */ , - int * /* bytes returned */ , const int); -extern int SendReceiveNoRsp(const unsigned int xid, struct cifs_ses *ses, - char *in_buf, int flags); -extern struct mid_q_entry *cifs_setup_request(struct cifs_ses *, - struct TCP_Server_Info *, - struct smb_rqst *); -extern struct mid_q_entry *cifs_setup_async_request(struct TCP_Server_Info *, - struct smb_rqst *); -extern int cifs_check_receive(struct mid_q_entry *mid, - struct TCP_Server_Info *server, bool log_error); -extern int cifs_wait_mtu_credits(struct TCP_Server_Info *server, - unsigned int size, unsigned int *num, - struct cifs_credits *credits); -extern int SendReceive2(const unsigned int /* xid */ , struct cifs_ses *, - struct kvec *, int /* nvec to send */, - int * /* type of buf returned */, const int flags, - struct kvec * /* resp vec */); -extern int SendReceiveBlockingLock(const unsigned int xid, - struct cifs_tcon *ptcon, - struct smb_hdr *in_buf , - struct smb_hdr *out_buf, - int *bytes_returned); -void -cifs_signal_cifsd_for_reconnect(struct TCP_Server_Info *server, - bool all_channels); -void -cifs_mark_tcp_ses_conns_for_reconnect(struct TCP_Server_Info *server, - bool mark_smb_session); -extern int cifs_reconnect(struct TCP_Server_Info *server, - bool mark_smb_session); -extern int checkSMB(char *buf, unsigned int len, struct TCP_Server_Info *srvr); -extern bool is_valid_oplock_break(char *, struct TCP_Server_Info *); -extern bool backup_cred(struct cifs_sb_info *); -extern bool is_size_safe_to_change(struct cifsInodeInfo *, __u64 eof); -extern void cifs_update_eof(struct cifsInodeInfo *cifsi, loff_t offset, - unsigned int bytes_written); -extern struct cifsFileInfo *find_writable_file(struct cifsInodeInfo *, int); -extern int cifs_get_writable_file(struct cifsInodeInfo *cifs_inode, - int flags, - struct cifsFileInfo **ret_file); -extern int cifs_get_writable_path(struct cifs_tcon *tcon, const char *name, - int flags, - struct cifsFileInfo **ret_file); -extern struct cifsFileInfo *find_readable_file(struct cifsInodeInfo *, bool); -extern int cifs_get_readable_path(struct cifs_tcon *tcon, const char *name, - struct cifsFileInfo **ret_file); -extern unsigned int smbCalcSize(void *buf); -extern int decode_negTokenInit(unsigned char *security_blob, int length, - struct TCP_Server_Info *server); -extern int cifs_convert_address(struct sockaddr *dst, const char *src, int len); -extern void cifs_set_port(struct sockaddr *addr, const unsigned short int port); -extern int map_smb_to_linux_error(char *buf, bool logErr); -extern int map_and_check_smb_error(struct mid_q_entry *mid, bool logErr); -extern void header_assemble(struct smb_hdr *, char /* command */ , - const struct cifs_tcon *, int /* length of - fixed section (word count) in two byte units */); -extern int small_smb_init_no_tc(const int smb_cmd, const int wct, - struct cifs_ses *ses, - void **request_buf); -extern enum securityEnum select_sectype(struct TCP_Server_Info *server, - enum securityEnum requested); -extern int CIFS_SessSetup(const unsigned int xid, struct cifs_ses *ses, - struct TCP_Server_Info *server, - const struct nls_table *nls_cp); -extern struct timespec64 cifs_NTtimeToUnix(__le64 utc_nanoseconds_since_1601); -extern u64 cifs_UnixTimeToNT(struct timespec64); -extern struct timespec64 cnvrtDosUnixTm(__le16 le_date, __le16 le_time, - int offset); -extern void cifs_set_oplock_level(struct cifsInodeInfo *cinode, __u32 oplock); -extern int cifs_get_writer(struct cifsInodeInfo *cinode); -extern void cifs_put_writer(struct cifsInodeInfo *cinode); -extern void cifs_done_oplock_break(struct cifsInodeInfo *cinode); -extern int cifs_unlock_range(struct cifsFileInfo *cfile, - struct file_lock *flock, const unsigned int xid); -extern int cifs_push_mandatory_locks(struct cifsFileInfo *cfile); - -extern void cifs_down_write(struct rw_semaphore *sem); -struct cifsFileInfo *cifs_new_fileinfo(struct cifs_fid *fid, struct file *file, - struct tcon_link *tlink, __u32 oplock, - const char *symlink_target); -extern int cifs_posix_open(const char *full_path, struct inode **inode, - struct super_block *sb, int mode, - unsigned int f_flags, __u32 *oplock, __u16 *netfid, - unsigned int xid); -void cifs_fill_uniqueid(struct super_block *sb, struct cifs_fattr *fattr); -extern void cifs_unix_basic_to_fattr(struct cifs_fattr *fattr, - FILE_UNIX_BASIC_INFO *info, - struct cifs_sb_info *cifs_sb); -extern void cifs_dir_info_to_fattr(struct cifs_fattr *, FILE_DIRECTORY_INFO *, - struct cifs_sb_info *); -extern int cifs_fattr_to_inode(struct inode *inode, struct cifs_fattr *fattr); -extern struct inode *cifs_iget(struct super_block *sb, - struct cifs_fattr *fattr); - -int cifs_get_inode_info(struct inode **inode, const char *full_path, - struct cifs_open_info_data *data, struct super_block *sb, int xid, - const struct cifs_fid *fid); -extern int smb311_posix_get_inode_info(struct inode **pinode, const char *search_path, - struct super_block *sb, unsigned int xid); -extern int cifs_get_inode_info_unix(struct inode **pinode, - const unsigned char *search_path, - struct super_block *sb, unsigned int xid); -extern int cifs_set_file_info(struct inode *inode, struct iattr *attrs, - unsigned int xid, const char *full_path, __u32 dosattr); -extern int cifs_rename_pending_delete(const char *full_path, - struct dentry *dentry, - const unsigned int xid); -extern int sid_to_id(struct cifs_sb_info *cifs_sb, struct cifs_sid *psid, - struct cifs_fattr *fattr, uint sidtype); -extern int cifs_acl_to_fattr(struct cifs_sb_info *cifs_sb, - struct cifs_fattr *fattr, struct inode *inode, - bool get_mode_from_special_sid, - const char *path, const struct cifs_fid *pfid); -extern int id_mode_to_cifs_acl(struct inode *inode, const char *path, __u64 *pnmode, - kuid_t uid, kgid_t gid); -extern struct cifs_ntsd *get_cifs_acl(struct cifs_sb_info *, struct inode *, - const char *, u32 *, u32); -extern struct cifs_ntsd *get_cifs_acl_by_fid(struct cifs_sb_info *, - const struct cifs_fid *, u32 *, u32); -extern int set_cifs_acl(struct cifs_ntsd *, __u32, struct inode *, - const char *, int); -extern unsigned int setup_authusers_ACE(struct cifs_ace *pace); -extern unsigned int setup_special_mode_ACE(struct cifs_ace *pace, __u64 nmode); -extern unsigned int setup_special_user_owner_ACE(struct cifs_ace *pace); - -extern void dequeue_mid(struct mid_q_entry *mid, bool malformed); -extern int cifs_read_from_socket(struct TCP_Server_Info *server, char *buf, - unsigned int to_read); -extern ssize_t cifs_discard_from_socket(struct TCP_Server_Info *server, - size_t to_read); -extern int cifs_read_page_from_socket(struct TCP_Server_Info *server, - struct page *page, - unsigned int page_offset, - unsigned int to_read); -extern int cifs_setup_cifs_sb(struct cifs_sb_info *cifs_sb); -extern int cifs_match_super(struct super_block *, void *); -extern int cifs_mount(struct cifs_sb_info *cifs_sb, struct smb3_fs_context *ctx); -extern void cifs_umount(struct cifs_sb_info *); -extern void cifs_mark_open_files_invalid(struct cifs_tcon *tcon); -extern void cifs_reopen_persistent_handles(struct cifs_tcon *tcon); - -extern bool cifs_find_lock_conflict(struct cifsFileInfo *cfile, __u64 offset, - __u64 length, __u8 type, __u16 flags, - struct cifsLockInfo **conf_lock, - int rw_check); -extern void cifs_add_pending_open(struct cifs_fid *fid, - struct tcon_link *tlink, - struct cifs_pending_open *open); -extern void cifs_add_pending_open_locked(struct cifs_fid *fid, - struct tcon_link *tlink, - struct cifs_pending_open *open); -extern void cifs_del_pending_open(struct cifs_pending_open *open); - -extern bool cifs_is_deferred_close(struct cifsFileInfo *cfile, - struct cifs_deferred_close **dclose); - -extern void cifs_add_deferred_close(struct cifsFileInfo *cfile, - struct cifs_deferred_close *dclose); - -extern void cifs_del_deferred_close(struct cifsFileInfo *cfile); - -extern void cifs_close_deferred_file(struct cifsInodeInfo *cifs_inode); - -extern void cifs_close_all_deferred_files(struct cifs_tcon *cifs_tcon); - -extern void cifs_close_deferred_file_under_dentry(struct cifs_tcon *cifs_tcon, - const char *path); -extern struct TCP_Server_Info * -cifs_get_tcp_session(struct smb3_fs_context *ctx, - struct TCP_Server_Info *primary_server); -extern void cifs_put_tcp_session(struct TCP_Server_Info *server, - int from_reconnect); -extern void cifs_put_tcon(struct cifs_tcon *tcon); - -#if IS_ENABLED(CONFIG_CIFS_DFS_UPCALL) -extern void cifs_dfs_release_automount_timer(void); -#else /* ! IS_ENABLED(CONFIG_CIFS_DFS_UPCALL) */ -#define cifs_dfs_release_automount_timer() do { } while (0) -#endif /* ! IS_ENABLED(CONFIG_CIFS_DFS_UPCALL) */ - -void cifs_proc_init(void); -void cifs_proc_clean(void); - -extern void cifs_move_llist(struct list_head *source, struct list_head *dest); -extern void cifs_free_llist(struct list_head *llist); -extern void cifs_del_lock_waiters(struct cifsLockInfo *lock); - -extern int cifs_tree_connect(const unsigned int xid, struct cifs_tcon *tcon, - const struct nls_table *nlsc); - -extern int cifs_negotiate_protocol(const unsigned int xid, - struct cifs_ses *ses, - struct TCP_Server_Info *server); -extern int cifs_setup_session(const unsigned int xid, struct cifs_ses *ses, - struct TCP_Server_Info *server, - struct nls_table *nls_info); -extern int cifs_enable_signing(struct TCP_Server_Info *server, bool mnt_sign_required); -extern int CIFSSMBNegotiate(const unsigned int xid, - struct cifs_ses *ses, - struct TCP_Server_Info *server); - -extern int CIFSTCon(const unsigned int xid, struct cifs_ses *ses, - const char *tree, struct cifs_tcon *tcon, - const struct nls_table *); - -extern int CIFSFindFirst(const unsigned int xid, struct cifs_tcon *tcon, - const char *searchName, struct cifs_sb_info *cifs_sb, - __u16 *searchHandle, __u16 search_flags, - struct cifs_search_info *psrch_inf, - bool msearch); - -extern int CIFSFindNext(const unsigned int xid, struct cifs_tcon *tcon, - __u16 searchHandle, __u16 search_flags, - struct cifs_search_info *psrch_inf); - -extern int CIFSFindClose(const unsigned int xid, struct cifs_tcon *tcon, - const __u16 search_handle); - -extern int CIFSSMBQFileInfo(const unsigned int xid, struct cifs_tcon *tcon, - u16 netfid, FILE_ALL_INFO *pFindData); -extern int CIFSSMBQPathInfo(const unsigned int xid, struct cifs_tcon *tcon, - const char *search_Name, FILE_ALL_INFO *data, - int legacy /* whether to use old info level */, - const struct nls_table *nls_codepage, int remap); -extern int SMBQueryInformation(const unsigned int xid, struct cifs_tcon *tcon, - const char *search_name, FILE_ALL_INFO *data, - const struct nls_table *nls_codepage, int remap); - -extern int CIFSSMBUnixQFileInfo(const unsigned int xid, struct cifs_tcon *tcon, - u16 netfid, FILE_UNIX_BASIC_INFO *pFindData); -extern int CIFSSMBUnixQPathInfo(const unsigned int xid, - struct cifs_tcon *tcon, - const unsigned char *searchName, - FILE_UNIX_BASIC_INFO *pFindData, - const struct nls_table *nls_codepage, int remap); - -extern int CIFSGetDFSRefer(const unsigned int xid, struct cifs_ses *ses, - const char *search_name, - struct dfs_info3_param **target_nodes, - unsigned int *num_of_nodes, - const struct nls_table *nls_codepage, int remap); - -extern int parse_dfs_referrals(struct get_dfs_referral_rsp *rsp, u32 rsp_size, - unsigned int *num_of_nodes, - struct dfs_info3_param **target_nodes, - const struct nls_table *nls_codepage, int remap, - const char *searchName, bool is_unicode); -extern void reset_cifs_unix_caps(unsigned int xid, struct cifs_tcon *tcon, - struct cifs_sb_info *cifs_sb, - struct smb3_fs_context *ctx); -extern int CIFSSMBQFSInfo(const unsigned int xid, struct cifs_tcon *tcon, - struct kstatfs *FSData); -extern int SMBOldQFSInfo(const unsigned int xid, struct cifs_tcon *tcon, - struct kstatfs *FSData); -extern int CIFSSMBSetFSUnixInfo(const unsigned int xid, struct cifs_tcon *tcon, - __u64 cap); - -extern int CIFSSMBQFSAttributeInfo(const unsigned int xid, - struct cifs_tcon *tcon); -extern int CIFSSMBQFSDeviceInfo(const unsigned int xid, struct cifs_tcon *tcon); -extern int CIFSSMBQFSUnixInfo(const unsigned int xid, struct cifs_tcon *tcon); -extern int CIFSSMBQFSPosixInfo(const unsigned int xid, struct cifs_tcon *tcon, - struct kstatfs *FSData); - -extern int CIFSSMBSetPathInfo(const unsigned int xid, struct cifs_tcon *tcon, - const char *fileName, const FILE_BASIC_INFO *data, - const struct nls_table *nls_codepage, - struct cifs_sb_info *cifs_sb); -extern int CIFSSMBSetFileInfo(const unsigned int xid, struct cifs_tcon *tcon, - const FILE_BASIC_INFO *data, __u16 fid, - __u32 pid_of_opener); -extern int CIFSSMBSetFileDisposition(const unsigned int xid, - struct cifs_tcon *tcon, - bool delete_file, __u16 fid, - __u32 pid_of_opener); -extern int CIFSSMBSetEOF(const unsigned int xid, struct cifs_tcon *tcon, - const char *file_name, __u64 size, - struct cifs_sb_info *cifs_sb, bool set_allocation); -extern int CIFSSMBSetFileSize(const unsigned int xid, struct cifs_tcon *tcon, - struct cifsFileInfo *cfile, __u64 size, - bool set_allocation); - -struct cifs_unix_set_info_args { - __u64 ctime; - __u64 atime; - __u64 mtime; - __u64 mode; - kuid_t uid; - kgid_t gid; - dev_t device; -}; - -extern int CIFSSMBUnixSetFileInfo(const unsigned int xid, - struct cifs_tcon *tcon, - const struct cifs_unix_set_info_args *args, - u16 fid, u32 pid_of_opener); - -extern int CIFSSMBUnixSetPathInfo(const unsigned int xid, - struct cifs_tcon *tcon, const char *file_name, - const struct cifs_unix_set_info_args *args, - const struct nls_table *nls_codepage, - int remap); - -extern int CIFSSMBMkDir(const unsigned int xid, struct inode *inode, - umode_t mode, struct cifs_tcon *tcon, - const char *name, struct cifs_sb_info *cifs_sb); -extern int CIFSSMBRmDir(const unsigned int xid, struct cifs_tcon *tcon, - const char *name, struct cifs_sb_info *cifs_sb); -extern int CIFSPOSIXDelFile(const unsigned int xid, struct cifs_tcon *tcon, - const char *name, __u16 type, - const struct nls_table *nls_codepage, - int remap_special_chars); -extern int CIFSSMBDelFile(const unsigned int xid, struct cifs_tcon *tcon, - const char *name, struct cifs_sb_info *cifs_sb); -extern int CIFSSMBRename(const unsigned int xid, struct cifs_tcon *tcon, - const char *from_name, const char *to_name, - struct cifs_sb_info *cifs_sb); -extern int CIFSSMBRenameOpenFile(const unsigned int xid, struct cifs_tcon *tcon, - int netfid, const char *target_name, - const struct nls_table *nls_codepage, - int remap_special_chars); -extern int CIFSCreateHardLink(const unsigned int xid, struct cifs_tcon *tcon, - const char *from_name, const char *to_name, - struct cifs_sb_info *cifs_sb); -extern int CIFSUnixCreateHardLink(const unsigned int xid, - struct cifs_tcon *tcon, - const char *fromName, const char *toName, - const struct nls_table *nls_codepage, - int remap_special_chars); -extern int CIFSUnixCreateSymLink(const unsigned int xid, - struct cifs_tcon *tcon, - const char *fromName, const char *toName, - const struct nls_table *nls_codepage, int remap); -extern int CIFSSMBUnixQuerySymLink(const unsigned int xid, - struct cifs_tcon *tcon, - const unsigned char *searchName, char **syminfo, - const struct nls_table *nls_codepage, int remap); -extern int CIFSSMBQuerySymLink(const unsigned int xid, struct cifs_tcon *tcon, - __u16 fid, char **symlinkinfo, - const struct nls_table *nls_codepage); -extern int CIFSSMB_set_compression(const unsigned int xid, - struct cifs_tcon *tcon, __u16 fid); -extern int CIFS_open(const unsigned int xid, struct cifs_open_parms *oparms, - int *oplock, FILE_ALL_INFO *buf); -extern int SMBLegacyOpen(const unsigned int xid, struct cifs_tcon *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 CIFSPOSIXCreate(const unsigned int xid, struct cifs_tcon *tcon, - u32 posix_flags, __u64 mode, __u16 *netfid, - FILE_UNIX_BASIC_INFO *pRetData, - __u32 *pOplock, const char *name, - const struct nls_table *nls_codepage, int remap); -extern int CIFSSMBClose(const unsigned int xid, struct cifs_tcon *tcon, - const int smb_file_id); - -extern int CIFSSMBFlush(const unsigned int xid, struct cifs_tcon *tcon, - const int smb_file_id); - -extern int CIFSSMBRead(const unsigned int xid, struct cifs_io_parms *io_parms, - unsigned int *nbytes, char **buf, - int *return_buf_type); -extern int CIFSSMBWrite(const unsigned int xid, struct cifs_io_parms *io_parms, - unsigned int *nbytes, const char *buf); -extern int CIFSSMBWrite2(const unsigned int xid, struct cifs_io_parms *io_parms, - unsigned int *nbytes, struct kvec *iov, const int nvec); -extern int CIFSGetSrvInodeNumber(const unsigned int xid, struct cifs_tcon *tcon, - const char *search_name, __u64 *inode_number, - const struct nls_table *nls_codepage, - int remap); - -extern int cifs_lockv(const unsigned int xid, struct cifs_tcon *tcon, - const __u16 netfid, const __u8 lock_type, - const __u32 num_unlock, const __u32 num_lock, - LOCKING_ANDX_RANGE *buf); -extern int CIFSSMBLock(const unsigned int xid, struct cifs_tcon *tcon, - const __u16 netfid, const __u32 netpid, const __u64 len, - const __u64 offset, const __u32 numUnlock, - const __u32 numLock, const __u8 lockType, - const bool waitFlag, const __u8 oplock_level); -extern int CIFSSMBPosixLock(const unsigned int xid, struct cifs_tcon *tcon, - const __u16 smb_file_id, const __u32 netpid, - const loff_t start_offset, const __u64 len, - struct file_lock *, const __u16 lock_type, - const bool waitFlag); -extern int CIFSSMBTDis(const unsigned int xid, struct cifs_tcon *tcon); -extern int CIFSSMBEcho(struct TCP_Server_Info *server); -extern int CIFSSMBLogoff(const unsigned int xid, struct cifs_ses *ses); - -extern struct cifs_ses *sesInfoAlloc(void); -extern void sesInfoFree(struct cifs_ses *); -extern struct cifs_tcon *tconInfoAlloc(void); -extern void tconInfoFree(struct cifs_tcon *); - -extern int cifs_sign_rqst(struct smb_rqst *rqst, struct TCP_Server_Info *server, - __u32 *pexpected_response_sequence_number); -extern int cifs_sign_smbv(struct kvec *iov, int n_vec, struct TCP_Server_Info *, - __u32 *); -extern int cifs_sign_smb(struct smb_hdr *, struct TCP_Server_Info *, __u32 *); -extern int cifs_verify_signature(struct smb_rqst *rqst, - struct TCP_Server_Info *server, - __u32 expected_sequence_number); -extern int setup_ntlmv2_rsp(struct cifs_ses *, const struct nls_table *); -extern void cifs_crypto_secmech_release(struct TCP_Server_Info *server); -extern int calc_seckey(struct cifs_ses *); -extern int generate_smb30signingkey(struct cifs_ses *ses, - struct TCP_Server_Info *server); -extern int generate_smb311signingkey(struct cifs_ses *ses, - struct TCP_Server_Info *server); - -#ifdef CONFIG_CIFS_ALLOW_INSECURE_LEGACY -extern int CIFSSMBCopy(unsigned int xid, - struct cifs_tcon *source_tcon, - const char *fromName, - const __u16 target_tid, - const char *toName, const int flags, - const struct nls_table *nls_codepage, - int remap_special_chars); -extern ssize_t CIFSSMBQAllEAs(const unsigned int xid, struct cifs_tcon *tcon, - const unsigned char *searchName, - const unsigned char *ea_name, char *EAData, - size_t bufsize, struct cifs_sb_info *cifs_sb); -extern int CIFSSMBSetEA(const unsigned int xid, struct cifs_tcon *tcon, - const char *fileName, const char *ea_name, - const void *ea_value, const __u16 ea_value_len, - const struct nls_table *nls_codepage, - struct cifs_sb_info *cifs_sb); -extern int CIFSSMBGetCIFSACL(const unsigned int xid, struct cifs_tcon *tcon, - __u16 fid, struct cifs_ntsd **acl_inf, __u32 *buflen); -extern int CIFSSMBSetCIFSACL(const unsigned int, struct cifs_tcon *, __u16, - struct cifs_ntsd *, __u32, int); -extern int CIFSSMBGetPosixACL(const unsigned int xid, struct cifs_tcon *tcon, - const unsigned char *searchName, - char *acl_inf, const int buflen, const int acl_type, - const struct nls_table *nls_codepage, int remap_special_chars); -extern int CIFSSMBSetPosixACL(const unsigned int xid, struct cifs_tcon *tcon, - const unsigned char *fileName, - const char *local_acl, const int buflen, const int acl_type, - const struct nls_table *nls_codepage, int remap_special_chars); -extern int CIFSGetExtAttr(const unsigned int xid, struct cifs_tcon *tcon, - const int netfid, __u64 *pExtAttrBits, __u64 *pMask); -#endif /* CIFS_ALLOW_INSECURE_LEGACY */ -extern void cifs_autodisable_serverino(struct cifs_sb_info *cifs_sb); -extern bool couldbe_mf_symlink(const struct cifs_fattr *fattr); -extern int check_mf_symlink(unsigned int xid, struct cifs_tcon *tcon, - struct cifs_sb_info *cifs_sb, - struct cifs_fattr *fattr, - const unsigned char *path); -extern int E_md4hash(const unsigned char *passwd, unsigned char *p16, - const struct nls_table *codepage); - -extern int -cifs_setup_volume_info(struct smb3_fs_context *ctx, const char *mntopts, const char *devname); - -extern struct TCP_Server_Info * -cifs_find_tcp_session(struct smb3_fs_context *ctx); - -extern void cifs_put_smb_ses(struct cifs_ses *ses); - -extern struct cifs_ses * -cifs_get_smb_ses(struct TCP_Server_Info *server, struct smb3_fs_context *ctx); - -void cifs_readdata_release(struct kref *refcount); -int cifs_async_readv(struct cifs_readdata *rdata); -int cifs_readv_receive(struct TCP_Server_Info *server, struct mid_q_entry *mid); - -int cifs_async_writev(struct cifs_writedata *wdata, - void (*release)(struct kref *kref)); -void cifs_writev_complete(struct work_struct *work); -struct cifs_writedata *cifs_writedata_alloc(unsigned int nr_pages, - work_func_t complete); -struct cifs_writedata *cifs_writedata_direct_alloc(struct page **pages, - work_func_t complete); -void cifs_writedata_release(struct kref *refcount); -int cifs_query_mf_symlink(unsigned int xid, struct cifs_tcon *tcon, - struct cifs_sb_info *cifs_sb, - const unsigned char *path, char *pbuf, - unsigned int *pbytes_read); -int cifs_create_mf_symlink(unsigned int xid, struct cifs_tcon *tcon, - struct cifs_sb_info *cifs_sb, - const unsigned char *path, char *pbuf, - unsigned int *pbytes_written); -int __cifs_calc_signature(struct smb_rqst *rqst, - struct TCP_Server_Info *server, char *signature, - struct shash_desc *shash); -enum securityEnum cifs_select_sectype(struct TCP_Server_Info *, - enum securityEnum); -struct cifs_aio_ctx *cifs_aio_ctx_alloc(void); -void cifs_aio_ctx_release(struct kref *refcount); -int setup_aio_ctx_iter(struct cifs_aio_ctx *ctx, struct iov_iter *iter, int rw); - -int cifs_alloc_hash(const char *name, struct shash_desc **sdesc); -void cifs_free_hash(struct shash_desc **sdesc); - -void rqst_page_get_length(const struct smb_rqst *rqst, unsigned int page, - unsigned int *len, unsigned int *offset); -struct cifs_chan * -cifs_ses_find_chan(struct cifs_ses *ses, struct TCP_Server_Info *server); -int cifs_try_adding_channels(struct cifs_sb_info *cifs_sb, struct cifs_ses *ses); -bool is_server_using_iface(struct TCP_Server_Info *server, - struct cifs_server_iface *iface); -bool is_ses_using_iface(struct cifs_ses *ses, struct cifs_server_iface *iface); -void cifs_ses_mark_for_reconnect(struct cifs_ses *ses); - -unsigned int -cifs_ses_get_chan_index(struct cifs_ses *ses, - struct TCP_Server_Info *server); -void -cifs_chan_set_in_reconnect(struct cifs_ses *ses, - struct TCP_Server_Info *server); -void -cifs_chan_clear_in_reconnect(struct cifs_ses *ses, - struct TCP_Server_Info *server); -bool -cifs_chan_in_reconnect(struct cifs_ses *ses, - struct TCP_Server_Info *server); -void -cifs_chan_set_need_reconnect(struct cifs_ses *ses, - struct TCP_Server_Info *server); -void -cifs_chan_clear_need_reconnect(struct cifs_ses *ses, - struct TCP_Server_Info *server); -bool -cifs_chan_needs_reconnect(struct cifs_ses *ses, - struct TCP_Server_Info *server); -bool -cifs_chan_is_iface_active(struct cifs_ses *ses, - struct TCP_Server_Info *server); -int -cifs_chan_update_iface(struct cifs_ses *ses, struct TCP_Server_Info *server); -int -SMB3_request_interfaces(const unsigned int xid, struct cifs_tcon *tcon, bool in_mount); - -void extract_unc_hostname(const char *unc, const char **h, size_t *len); -int copy_path_name(char *dst, const char *src); -int smb2_parse_query_directory(struct cifs_tcon *tcon, struct kvec *rsp_iov, - int resp_buftype, - struct cifs_search_info *srch_inf); - -struct super_block *cifs_get_tcp_super(struct TCP_Server_Info *server); -void cifs_put_tcp_super(struct super_block *sb); -int cifs_update_super_prepath(struct cifs_sb_info *cifs_sb, char *prefix); -char *extract_hostname(const char *unc); -char *extract_sharename(const char *unc); - -#ifdef CONFIG_CIFS_DFS_UPCALL -static inline int get_dfs_path(const unsigned int xid, struct cifs_ses *ses, - const char *old_path, - const struct nls_table *nls_codepage, - struct dfs_info3_param *referral, int remap) -{ - return dfs_cache_find(xid, ses, nls_codepage, remap, old_path, - referral, NULL); -} - -int match_target_ip(struct TCP_Server_Info *server, - const char *share, size_t share_len, - bool *result); -int cifs_inval_name_dfs_link_error(const unsigned int xid, - struct cifs_tcon *tcon, - struct cifs_sb_info *cifs_sb, - const char *full_path, - bool *islink); -#else -static inline int cifs_inval_name_dfs_link_error(const unsigned int xid, - struct cifs_tcon *tcon, - struct cifs_sb_info *cifs_sb, - const char *full_path, - bool *islink) -{ - *islink = false; - return 0; -} -#endif - -static inline int cifs_create_options(struct cifs_sb_info *cifs_sb, int options) -{ - if (cifs_sb && (backup_cred(cifs_sb))) - return options | CREATE_OPEN_BACKUP_INTENT; - else - return options; -} - -struct super_block *cifs_get_tcon_super(struct cifs_tcon *tcon); -void cifs_put_tcon_super(struct super_block *sb); -int cifs_wait_for_server_reconnect(struct TCP_Server_Info *server, bool retry); - -#endif /* _CIFSPROTO_H */ diff --git a/fs/cifs/cifsroot.c b/fs/cifs/cifsroot.c deleted file mode 100644 index 56ec1b233f52e..0000000000000 --- a/fs/cifs/cifsroot.c +++ /dev/null @@ -1,94 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0 -/* - * SMB root file system support - * - * Copyright (c) 2019 Paulo Alcantara - */ -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#define DEFAULT_MNT_OPTS \ - "vers=1.0,cifsacl,mfsymlinks,rsize=1048576,wsize=65536,uid=0,gid=0," \ - "hard,rootfs" - -static char root_dev[2048] __initdata = ""; -static char root_opts[1024] __initdata = DEFAULT_MNT_OPTS; - -static __be32 __init parse_srvaddr(char *start, char *end) -{ - /* TODO: ipv6 support */ - char addr[sizeof("aaa.bbb.ccc.ddd")]; - int i = 0; - - while (start < end && i < sizeof(addr) - 1) { - if (isdigit(*start) || *start == '.') - addr[i++] = *start; - start++; - } - addr[i] = '\0'; - return in_aton(addr); -} - -/* cifsroot=///[,options] */ -static int __init cifs_root_setup(char *line) -{ - char *s; - int len; - __be32 srvaddr = htonl(INADDR_NONE); - - ROOT_DEV = Root_CIFS; - - if (strlen(line) > 3 && line[0] == '/' && line[1] == '/') { - s = strchr(&line[2], '/'); - if (!s || s[1] == '\0') - return 1; - - /* make s point to ',' or '\0' at end of line */ - s = strchrnul(s, ','); - /* len is strlen(unc) + '\0' */ - len = s - line + 1; - if (len > sizeof(root_dev)) { - pr_err("Root-CIFS: UNC path too long\n"); - return 1; - } - strscpy(root_dev, line, len); - srvaddr = parse_srvaddr(&line[2], s); - if (*s) { - int n = snprintf(root_opts, - sizeof(root_opts), "%s,%s", - DEFAULT_MNT_OPTS, s + 1); - if (n >= sizeof(root_opts)) { - pr_err("Root-CIFS: mount options string too long\n"); - root_opts[sizeof(root_opts)-1] = '\0'; - return 1; - } - } - } - - root_server_addr = srvaddr; - - return 1; -} - -__setup("cifsroot=", cifs_root_setup); - -int __init cifs_root_data(char **dev, char **opts) -{ - if (!root_dev[0] || root_server_addr == htonl(INADDR_NONE)) { - pr_err("Root-CIFS: no SMB server address\n"); - return -1; - } - - *dev = root_dev; - *opts = root_opts; - - return 0; -} diff --git a/fs/cifs/cifssmb.c b/fs/cifs/cifssmb.c deleted file mode 100644 index c90d4ec9292ca..0000000000000 --- a/fs/cifs/cifssmb.c +++ /dev/null @@ -1,5873 +0,0 @@ -// SPDX-License-Identifier: LGPL-2.1 -/* - * - * Copyright (C) International Business Machines Corp., 2002,2010 - * Author(s): Steve French (sfrench@us.ibm.com) - * - * Contains the routines for constructing the SMB PDUs themselves - * - */ - - /* SMB/CIFS PDU handling routines here - except for leftovers in connect.c */ - /* These are mostly routines that operate on a pathname, or on a tree id */ - /* (mounted volume), but there are eight handle based routines which must be */ - /* treated slightly differently for reconnection purposes since we never */ - /* want to reuse a stale file handle and only the caller knows the file info */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include "cifspdu.h" -#include "cifsglob.h" -#include "cifsacl.h" -#include "cifsproto.h" -#include "cifs_unicode.h" -#include "cifs_debug.h" -#include "fscache.h" -#include "smbdirect.h" -#ifdef CONFIG_CIFS_DFS_UPCALL -#include "dfs_cache.h" -#endif - -#ifdef CONFIG_CIFS_POSIX -static struct { - int index; - char *name; -} protocols[] = { - {CIFS_PROT, "\2NT LM 0.12"}, - {POSIX_PROT, "\2POSIX 2"}, - {BAD_PROT, "\2"} -}; -#else -static struct { - int index; - char *name; -} protocols[] = { - {CIFS_PROT, "\2NT LM 0.12"}, - {BAD_PROT, "\2"} -}; -#endif - -/* define the number of elements in the cifs dialect array */ -#ifdef CONFIG_CIFS_POSIX -#define CIFS_NUM_PROT 2 -#else /* not posix */ -#define CIFS_NUM_PROT 1 -#endif /* CIFS_POSIX */ - - -/* reconnect the socket, tcon, and smb session if needed */ -static int -cifs_reconnect_tcon(struct cifs_tcon *tcon, int smb_command) -{ - int rc; - struct cifs_ses *ses; - struct TCP_Server_Info *server; - struct nls_table *nls_codepage; - - /* - * SMBs NegProt, SessSetup, uLogoff do not have tcon yet so check for - * tcp and smb session status done differently for those three - in the - * calling routine - */ - if (!tcon) - return 0; - - ses = tcon->ses; - server = ses->server; - - /* - * only tree disconnect, open, and write, (and ulogoff which does not - * have tcon) are allowed as we start umount - */ - spin_lock(&tcon->tc_lock); - if (tcon->status == TID_EXITING) { - if (smb_command != SMB_COM_TREE_DISCONNECT) { - spin_unlock(&tcon->tc_lock); - cifs_dbg(FYI, "can not send cmd %d while umounting\n", - smb_command); - return -ENODEV; - } - } - spin_unlock(&tcon->tc_lock); - - rc = cifs_wait_for_server_reconnect(server, tcon->retry); - if (rc) - return rc; - - spin_lock(&ses->chan_lock); - if (!cifs_chan_needs_reconnect(ses, server) && !tcon->need_reconnect) { - spin_unlock(&ses->chan_lock); - return 0; - } - spin_unlock(&ses->chan_lock); - - nls_codepage = load_nls_default(); - - /* - * Recheck after acquire mutex. If another thread is negotiating - * and the server never sends an answer the socket will be closed - * and tcpStatus set to reconnect. - */ - spin_lock(&server->srv_lock); - if (server->tcpStatus == CifsNeedReconnect) { - spin_unlock(&server->srv_lock); - rc = -EHOSTDOWN; - goto out; - } - spin_unlock(&server->srv_lock); - - /* - * need to prevent multiple threads trying to simultaneously - * reconnect the same SMB session - */ - spin_lock(&ses->chan_lock); - if (!cifs_chan_needs_reconnect(ses, server)) { - spin_unlock(&ses->chan_lock); - - /* this means that we only need to tree connect */ - if (tcon->need_reconnect) - goto skip_sess_setup; - - rc = -EHOSTDOWN; - goto out; - } - spin_unlock(&ses->chan_lock); - - mutex_lock(&ses->session_mutex); - rc = cifs_negotiate_protocol(0, ses, server); - if (!rc) - rc = cifs_setup_session(0, ses, server, nls_codepage); - - /* do we need to reconnect tcon? */ - if (rc || !tcon->need_reconnect) { - mutex_unlock(&ses->session_mutex); - goto out; - } - -skip_sess_setup: - cifs_mark_open_files_invalid(tcon); - rc = cifs_tree_connect(0, tcon, nls_codepage); - mutex_unlock(&ses->session_mutex); - cifs_dbg(FYI, "reconnect tcon rc = %d\n", rc); - - if (rc) { - pr_warn_once("reconnect tcon failed rc = %d\n", rc); - goto out; - } - - atomic_inc(&tconInfoReconnectCount); - - /* tell server Unix caps we support */ - if (cap_unix(ses)) - reset_cifs_unix_caps(0, tcon, NULL, NULL); - - /* - * Removed call to reopen open files here. It is safer (and faster) to - * reopen files one at a time as needed in read and write. - * - * FIXME: what about file locks? don't we need to reclaim them ASAP? - */ - -out: - /* - * Check if handle based operation so we know whether we can continue - * or not without returning to caller to reset file handle - */ - switch (smb_command) { - case SMB_COM_READ_ANDX: - case SMB_COM_WRITE_ANDX: - case SMB_COM_CLOSE: - case SMB_COM_FIND_CLOSE2: - case SMB_COM_LOCKING_ANDX: - rc = -EAGAIN; - } - - unload_nls(nls_codepage); - return rc; -} - -/* Allocate and return pointer to an SMB request buffer, and set basic - SMB information in the SMB header. If the return code is zero, this - function must have filled in request_buf pointer */ -static int -small_smb_init(int smb_command, int wct, struct cifs_tcon *tcon, - void **request_buf) -{ - int rc; - - rc = cifs_reconnect_tcon(tcon, smb_command); - if (rc) - return rc; - - *request_buf = cifs_small_buf_get(); - if (*request_buf == NULL) { - /* BB should we add a retry in here if not a writepage? */ - return -ENOMEM; - } - - header_assemble((struct smb_hdr *) *request_buf, smb_command, - tcon, wct); - - if (tcon != NULL) - cifs_stats_inc(&tcon->num_smbs_sent); - - return 0; -} - -int -small_smb_init_no_tc(const int smb_command, const int wct, - struct cifs_ses *ses, void **request_buf) -{ - int rc; - struct smb_hdr *buffer; - - rc = small_smb_init(smb_command, wct, NULL, request_buf); - if (rc) - return rc; - - buffer = (struct smb_hdr *)*request_buf; - buffer->Mid = get_next_mid(ses->server); - if (ses->capabilities & CAP_UNICODE) - buffer->Flags2 |= SMBFLG2_UNICODE; - if (ses->capabilities & CAP_STATUS32) - buffer->Flags2 |= SMBFLG2_ERR_STATUS; - - /* uid, tid can stay at zero as set in header assemble */ - - /* BB add support for turning on the signing when - this function is used after 1st of session setup requests */ - - return rc; -} - -/* If the return code is zero, this function must fill in request_buf pointer */ -static int -__smb_init(int smb_command, int wct, struct cifs_tcon *tcon, - void **request_buf, void **response_buf) -{ - *request_buf = cifs_buf_get(); - if (*request_buf == NULL) { - /* BB should we add a retry in here if not a writepage? */ - return -ENOMEM; - } - /* Although the original thought was we needed the response buf for */ - /* potential retries of smb operations it turns out we can determine */ - /* from the mid flags when the request buffer can be resent without */ - /* having to use a second distinct buffer for the response */ - if (response_buf) - *response_buf = *request_buf; - - header_assemble((struct smb_hdr *) *request_buf, smb_command, tcon, - wct); - - if (tcon != NULL) - cifs_stats_inc(&tcon->num_smbs_sent); - - return 0; -} - -/* If the return code is zero, this function must fill in request_buf pointer */ -static int -smb_init(int smb_command, int wct, struct cifs_tcon *tcon, - void **request_buf, void **response_buf) -{ - int rc; - - rc = cifs_reconnect_tcon(tcon, smb_command); - if (rc) - return rc; - - return __smb_init(smb_command, wct, tcon, request_buf, response_buf); -} - -static int -smb_init_no_reconnect(int smb_command, int wct, struct cifs_tcon *tcon, - void **request_buf, void **response_buf) -{ - spin_lock(&tcon->ses->chan_lock); - if (cifs_chan_needs_reconnect(tcon->ses, tcon->ses->server) || - tcon->need_reconnect) { - spin_unlock(&tcon->ses->chan_lock); - return -EHOSTDOWN; - } - spin_unlock(&tcon->ses->chan_lock); - - return __smb_init(smb_command, wct, tcon, request_buf, response_buf); -} - -static int validate_t2(struct smb_t2_rsp *pSMB) -{ - unsigned int total_size; - - /* check for plausible wct */ - if (pSMB->hdr.WordCount < 10) - goto vt2_err; - - /* check for parm and data offset going beyond end of smb */ - if (get_unaligned_le16(&pSMB->t2_rsp.ParameterOffset) > 1024 || - get_unaligned_le16(&pSMB->t2_rsp.DataOffset) > 1024) - goto vt2_err; - - total_size = get_unaligned_le16(&pSMB->t2_rsp.ParameterCount); - if (total_size >= 512) - goto vt2_err; - - /* check that bcc is at least as big as parms + data, and that it is - * less than negotiated smb buffer - */ - total_size += get_unaligned_le16(&pSMB->t2_rsp.DataCount); - if (total_size > get_bcc(&pSMB->hdr) || - total_size >= CIFSMaxBufSize + MAX_CIFS_HDR_SIZE) - goto vt2_err; - - return 0; -vt2_err: - cifs_dump_mem("Invalid transact2 SMB: ", (char *)pSMB, - sizeof(struct smb_t2_rsp) + 16); - return -EINVAL; -} - -static int -decode_ext_sec_blob(struct cifs_ses *ses, NEGOTIATE_RSP *pSMBr) -{ - int rc = 0; - u16 count; - char *guid = pSMBr->u.extended_response.GUID; - struct TCP_Server_Info *server = ses->server; - - count = get_bcc(&pSMBr->hdr); - if (count < SMB1_CLIENT_GUID_SIZE) - return -EIO; - - spin_lock(&cifs_tcp_ses_lock); - if (server->srv_count > 1) { - spin_unlock(&cifs_tcp_ses_lock); - if (memcmp(server->server_GUID, guid, SMB1_CLIENT_GUID_SIZE) != 0) { - cifs_dbg(FYI, "server UID changed\n"); - memcpy(server->server_GUID, guid, SMB1_CLIENT_GUID_SIZE); - } - } else { - spin_unlock(&cifs_tcp_ses_lock); - memcpy(server->server_GUID, guid, SMB1_CLIENT_GUID_SIZE); - } - - if (count == SMB1_CLIENT_GUID_SIZE) { - server->sec_ntlmssp = true; - } else { - count -= SMB1_CLIENT_GUID_SIZE; - rc = decode_negTokenInit( - pSMBr->u.extended_response.SecurityBlob, count, server); - if (rc != 1) - return -EINVAL; - } - - return 0; -} - -static bool -should_set_ext_sec_flag(enum securityEnum sectype) -{ - switch (sectype) { - case RawNTLMSSP: - case Kerberos: - return true; - case Unspecified: - if (global_secflags & - (CIFSSEC_MAY_KRB5 | CIFSSEC_MAY_NTLMSSP)) - return true; - fallthrough; - default: - return false; - } -} - -int -CIFSSMBNegotiate(const unsigned int xid, - struct cifs_ses *ses, - struct TCP_Server_Info *server) -{ - NEGOTIATE_REQ *pSMB; - NEGOTIATE_RSP *pSMBr; - int rc = 0; - int bytes_returned; - int i; - u16 count; - - if (!server) { - WARN(1, "%s: server is NULL!\n", __func__); - return -EIO; - } - - rc = smb_init(SMB_COM_NEGOTIATE, 0, NULL /* no tcon yet */ , - (void **) &pSMB, (void **) &pSMBr); - if (rc) - return rc; - - pSMB->hdr.Mid = get_next_mid(server); - pSMB->hdr.Flags2 |= (SMBFLG2_UNICODE | SMBFLG2_ERR_STATUS); - - if (should_set_ext_sec_flag(ses->sectype)) { - cifs_dbg(FYI, "Requesting extended security\n"); - pSMB->hdr.Flags2 |= SMBFLG2_EXT_SEC; - } - - count = 0; - /* - * We know that all the name entries in the protocols array - * are short (< 16 bytes anyway) and are NUL terminated. - */ - for (i = 0; i < CIFS_NUM_PROT; i++) { - size_t len = strlen(protocols[i].name) + 1; - - memcpy(&pSMB->DialectsArray[count], protocols[i].name, len); - count += len; - } - inc_rfc1001_len(pSMB, count); - pSMB->ByteCount = cpu_to_le16(count); - - rc = SendReceive(xid, ses, (struct smb_hdr *) pSMB, - (struct smb_hdr *) pSMBr, &bytes_returned, 0); - if (rc != 0) - goto neg_err_exit; - - server->dialect = le16_to_cpu(pSMBr->DialectIndex); - cifs_dbg(FYI, "Dialect: %d\n", server->dialect); - /* Check wct = 1 error case */ - if ((pSMBr->hdr.WordCount <= 13) || (server->dialect == BAD_PROT)) { - /* core returns wct = 1, but we do not ask for core - otherwise - small wct just comes when dialect index is -1 indicating we - could not negotiate a common dialect */ - rc = -EOPNOTSUPP; - goto neg_err_exit; - } else if (pSMBr->hdr.WordCount != 17) { - /* unknown wct */ - rc = -EOPNOTSUPP; - goto neg_err_exit; - } - /* else wct == 17, NTLM or better */ - - server->sec_mode = pSMBr->SecurityMode; - if ((server->sec_mode & SECMODE_USER) == 0) - cifs_dbg(FYI, "share mode security\n"); - - /* one byte, so no need to convert this or EncryptionKeyLen from - little endian */ - server->maxReq = min_t(unsigned int, le16_to_cpu(pSMBr->MaxMpxCount), - cifs_max_pending); - set_credits(server, server->maxReq); - /* probably no need to store and check maxvcs */ - server->maxBuf = le32_to_cpu(pSMBr->MaxBufferSize); - /* set up max_read for readahead check */ - server->max_read = server->maxBuf; - server->max_rw = le32_to_cpu(pSMBr->MaxRawSize); - cifs_dbg(NOISY, "Max buf = %d\n", ses->server->maxBuf); - server->capabilities = le32_to_cpu(pSMBr->Capabilities); - server->timeAdj = (int)(__s16)le16_to_cpu(pSMBr->ServerTimeZone); - server->timeAdj *= 60; - - if (pSMBr->EncryptionKeyLength == CIFS_CRYPTO_KEY_SIZE) { - server->negflavor = CIFS_NEGFLAVOR_UNENCAP; - memcpy(ses->server->cryptkey, pSMBr->u.EncryptionKey, - CIFS_CRYPTO_KEY_SIZE); - } else if (pSMBr->hdr.Flags2 & SMBFLG2_EXT_SEC || - server->capabilities & CAP_EXTENDED_SECURITY) { - server->negflavor = CIFS_NEGFLAVOR_EXTENDED; - rc = decode_ext_sec_blob(ses, pSMBr); - } else if (server->sec_mode & SECMODE_PW_ENCRYPT) { - rc = -EIO; /* no crypt key only if plain text pwd */ - } else { - server->negflavor = CIFS_NEGFLAVOR_UNENCAP; - server->capabilities &= ~CAP_EXTENDED_SECURITY; - } - - if (!rc) - rc = cifs_enable_signing(server, ses->sign); -neg_err_exit: - cifs_buf_release(pSMB); - - cifs_dbg(FYI, "negprot rc %d\n", rc); - return rc; -} - -int -CIFSSMBTDis(const unsigned int xid, struct cifs_tcon *tcon) -{ - struct smb_hdr *smb_buffer; - int rc = 0; - - cifs_dbg(FYI, "In tree disconnect\n"); - - /* BB: do we need to check this? These should never be NULL. */ - if ((tcon->ses == NULL) || (tcon->ses->server == NULL)) - return -EIO; - - /* - * No need to return error on this operation if tid invalidated and - * closed on server already e.g. due to tcp session crashing. Also, - * the tcon is no longer on the list, so no need to take lock before - * checking this. - */ - spin_lock(&tcon->ses->chan_lock); - if ((tcon->need_reconnect) || CIFS_ALL_CHANS_NEED_RECONNECT(tcon->ses)) { - spin_unlock(&tcon->ses->chan_lock); - return -EIO; - } - spin_unlock(&tcon->ses->chan_lock); - - rc = small_smb_init(SMB_COM_TREE_DISCONNECT, 0, tcon, - (void **)&smb_buffer); - if (rc) - return rc; - - rc = SendReceiveNoRsp(xid, tcon->ses, (char *)smb_buffer, 0); - cifs_small_buf_release(smb_buffer); - if (rc) - cifs_dbg(FYI, "Tree disconnect failed %d\n", rc); - - /* No need to return error on this operation if tid invalidated and - closed on server already e.g. due to tcp session crashing */ - if (rc == -EAGAIN) - rc = 0; - - return rc; -} - -/* - * This is a no-op for now. We're not really interested in the reply, but - * rather in the fact that the server sent one and that server->lstrp - * gets updated. - * - * FIXME: maybe we should consider checking that the reply matches request? - */ -static void -cifs_echo_callback(struct mid_q_entry *mid) -{ - struct TCP_Server_Info *server = mid->callback_data; - struct cifs_credits credits = { .value = 1, .instance = 0 }; - - release_mid(mid); - add_credits(server, &credits, CIFS_ECHO_OP); -} - -int -CIFSSMBEcho(struct TCP_Server_Info *server) -{ - ECHO_REQ *smb; - int rc = 0; - struct kvec iov[2]; - struct smb_rqst rqst = { .rq_iov = iov, - .rq_nvec = 2 }; - - cifs_dbg(FYI, "In echo request\n"); - - rc = small_smb_init(SMB_COM_ECHO, 0, NULL, (void **)&smb); - if (rc) - return rc; - - if (server->capabilities & CAP_UNICODE) - smb->hdr.Flags2 |= SMBFLG2_UNICODE; - - /* set up echo request */ - smb->hdr.Tid = 0xffff; - smb->hdr.WordCount = 1; - put_unaligned_le16(1, &smb->EchoCount); - put_bcc(1, &smb->hdr); - smb->Data[0] = 'a'; - inc_rfc1001_len(smb, 3); - - iov[0].iov_len = 4; - iov[0].iov_base = smb; - iov[1].iov_len = get_rfc1002_length(smb); - iov[1].iov_base = (char *)smb + 4; - - rc = cifs_call_async(server, &rqst, NULL, cifs_echo_callback, NULL, - server, CIFS_NON_BLOCKING | CIFS_ECHO_OP, NULL); - if (rc) - cifs_dbg(FYI, "Echo request failed: %d\n", rc); - - cifs_small_buf_release(smb); - - return rc; -} - -int -CIFSSMBLogoff(const unsigned int xid, struct cifs_ses *ses) -{ - LOGOFF_ANDX_REQ *pSMB; - int rc = 0; - - cifs_dbg(FYI, "In SMBLogoff for session disconnect\n"); - - /* - * BB: do we need to check validity of ses and server? They should - * always be valid since we have an active reference. If not, that - * should probably be a BUG() - */ - if (!ses || !ses->server) - return -EIO; - - mutex_lock(&ses->session_mutex); - spin_lock(&ses->chan_lock); - if (CIFS_ALL_CHANS_NEED_RECONNECT(ses)) { - spin_unlock(&ses->chan_lock); - goto session_already_dead; /* no need to send SMBlogoff if uid - already closed due to reconnect */ - } - spin_unlock(&ses->chan_lock); - - rc = small_smb_init(SMB_COM_LOGOFF_ANDX, 2, NULL, (void **)&pSMB); - if (rc) { - mutex_unlock(&ses->session_mutex); - return rc; - } - - pSMB->hdr.Mid = get_next_mid(ses->server); - - if (ses->server->sign) - pSMB->hdr.Flags2 |= SMBFLG2_SECURITY_SIGNATURE; - - pSMB->hdr.Uid = ses->Suid; - - pSMB->AndXCommand = 0xFF; - rc = SendReceiveNoRsp(xid, ses, (char *) pSMB, 0); - cifs_small_buf_release(pSMB); -session_already_dead: - mutex_unlock(&ses->session_mutex); - - /* if session dead then we do not need to do ulogoff, - since server closed smb session, no sense reporting - error */ - if (rc == -EAGAIN) - rc = 0; - return rc; -} - -int -CIFSPOSIXDelFile(const unsigned int xid, struct cifs_tcon *tcon, - const char *fileName, __u16 type, - const struct nls_table *nls_codepage, int remap) -{ - TRANSACTION2_SPI_REQ *pSMB = NULL; - TRANSACTION2_SPI_RSP *pSMBr = NULL; - struct unlink_psx_rq *pRqD; - int name_len; - int rc = 0; - int bytes_returned = 0; - __u16 params, param_offset, offset, byte_count; - - cifs_dbg(FYI, "In POSIX delete\n"); -PsxDelete: - rc = smb_init(SMB_COM_TRANSACTION2, 15, tcon, (void **) &pSMB, - (void **) &pSMBr); - if (rc) - return rc; - - if (pSMB->hdr.Flags2 & SMBFLG2_UNICODE) { - name_len = - cifsConvertToUTF16((__le16 *) pSMB->FileName, fileName, - PATH_MAX, nls_codepage, remap); - name_len++; /* trailing null */ - name_len *= 2; - } else { - name_len = copy_path_name(pSMB->FileName, fileName); - } - - params = 6 + name_len; - pSMB->MaxParameterCount = cpu_to_le16(2); - pSMB->MaxDataCount = 0; /* BB double check this with jra */ - pSMB->MaxSetupCount = 0; - pSMB->Reserved = 0; - pSMB->Flags = 0; - pSMB->Timeout = 0; - pSMB->Reserved2 = 0; - param_offset = offsetof(struct smb_com_transaction2_spi_req, - InformationLevel) - 4; - offset = param_offset + params; - - /* Setup pointer to Request Data (inode type). - * Note that SMB offsets are from the beginning of SMB which is 4 bytes - * in, after RFC1001 field - */ - pRqD = (struct unlink_psx_rq *)((char *)(pSMB) + offset + 4); - pRqD->type = cpu_to_le16(type); - pSMB->ParameterOffset = cpu_to_le16(param_offset); - pSMB->DataOffset = cpu_to_le16(offset); - pSMB->SetupCount = 1; - pSMB->Reserved3 = 0; - pSMB->SubCommand = cpu_to_le16(TRANS2_SET_PATH_INFORMATION); - byte_count = 3 /* pad */ + params + sizeof(struct unlink_psx_rq); - - pSMB->DataCount = cpu_to_le16(sizeof(struct unlink_psx_rq)); - pSMB->TotalDataCount = cpu_to_le16(sizeof(struct unlink_psx_rq)); - pSMB->ParameterCount = cpu_to_le16(params); - pSMB->TotalParameterCount = pSMB->ParameterCount; - pSMB->InformationLevel = cpu_to_le16(SMB_POSIX_UNLINK); - pSMB->Reserved4 = 0; - inc_rfc1001_len(pSMB, 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) - cifs_dbg(FYI, "Posix delete returned %d\n", rc); - cifs_buf_release(pSMB); - - cifs_stats_inc(&tcon->stats.cifs_stats.num_deletes); - - if (rc == -EAGAIN) - goto PsxDelete; - - return rc; -} - -int -CIFSSMBDelFile(const unsigned int xid, struct cifs_tcon *tcon, const char *name, - struct cifs_sb_info *cifs_sb) -{ - DELETE_FILE_REQ *pSMB = NULL; - DELETE_FILE_RSP *pSMBr = NULL; - int rc = 0; - int bytes_returned; - int name_len; - int remap = cifs_remap(cifs_sb); - -DelFileRetry: - rc = smb_init(SMB_COM_DELETE, 1, tcon, (void **) &pSMB, - (void **) &pSMBr); - if (rc) - return rc; - - if (pSMB->hdr.Flags2 & SMBFLG2_UNICODE) { - name_len = cifsConvertToUTF16((__le16 *) pSMB->fileName, name, - PATH_MAX, cifs_sb->local_nls, - remap); - name_len++; /* trailing null */ - name_len *= 2; - } else { - name_len = copy_path_name(pSMB->fileName, name); - } - pSMB->SearchAttributes = - cpu_to_le16(ATTR_READONLY | ATTR_HIDDEN | ATTR_SYSTEM); - pSMB->BufferFormat = 0x04; - inc_rfc1001_len(pSMB, name_len + 1); - 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->stats.cifs_stats.num_deletes); - if (rc) - cifs_dbg(FYI, "Error in RMFile = %d\n", rc); - - cifs_buf_release(pSMB); - if (rc == -EAGAIN) - goto DelFileRetry; - - return rc; -} - -int -CIFSSMBRmDir(const unsigned int xid, struct cifs_tcon *tcon, const char *name, - struct cifs_sb_info *cifs_sb) -{ - DELETE_DIRECTORY_REQ *pSMB = NULL; - DELETE_DIRECTORY_RSP *pSMBr = NULL; - int rc = 0; - int bytes_returned; - int name_len; - int remap = cifs_remap(cifs_sb); - - cifs_dbg(FYI, "In CIFSSMBRmDir\n"); -RmDirRetry: - rc = smb_init(SMB_COM_DELETE_DIRECTORY, 0, tcon, (void **) &pSMB, - (void **) &pSMBr); - if (rc) - return rc; - - if (pSMB->hdr.Flags2 & SMBFLG2_UNICODE) { - name_len = cifsConvertToUTF16((__le16 *) pSMB->DirName, name, - PATH_MAX, cifs_sb->local_nls, - remap); - name_len++; /* trailing null */ - name_len *= 2; - } else { - name_len = copy_path_name(pSMB->DirName, name); - } - - pSMB->BufferFormat = 0x04; - inc_rfc1001_len(pSMB, name_len + 1); - 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->stats.cifs_stats.num_rmdirs); - if (rc) - cifs_dbg(FYI, "Error in RMDir = %d\n", rc); - - cifs_buf_release(pSMB); - if (rc == -EAGAIN) - goto RmDirRetry; - return rc; -} - -int -CIFSSMBMkDir(const unsigned int xid, struct inode *inode, umode_t mode, - struct cifs_tcon *tcon, const char *name, - struct cifs_sb_info *cifs_sb) -{ - int rc = 0; - CREATE_DIRECTORY_REQ *pSMB = NULL; - CREATE_DIRECTORY_RSP *pSMBr = NULL; - int bytes_returned; - int name_len; - int remap = cifs_remap(cifs_sb); - - cifs_dbg(FYI, "In CIFSSMBMkDir\n"); -MkDirRetry: - rc = smb_init(SMB_COM_CREATE_DIRECTORY, 0, tcon, (void **) &pSMB, - (void **) &pSMBr); - if (rc) - return rc; - - if (pSMB->hdr.Flags2 & SMBFLG2_UNICODE) { - name_len = cifsConvertToUTF16((__le16 *) pSMB->DirName, name, - PATH_MAX, cifs_sb->local_nls, - remap); - name_len++; /* trailing null */ - name_len *= 2; - } else { - name_len = copy_path_name(pSMB->DirName, name); - } - - pSMB->BufferFormat = 0x04; - inc_rfc1001_len(pSMB, name_len + 1); - 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->stats.cifs_stats.num_mkdirs); - if (rc) - cifs_dbg(FYI, "Error in Mkdir = %d\n", rc); - - cifs_buf_release(pSMB); - if (rc == -EAGAIN) - goto MkDirRetry; - return rc; -} - -int -CIFSPOSIXCreate(const unsigned int xid, struct cifs_tcon *tcon, - __u32 posix_flags, __u64 mode, __u16 *netfid, - FILE_UNIX_BASIC_INFO *pRetData, __u32 *pOplock, - const char *name, const struct nls_table *nls_codepage, - int remap) -{ - TRANSACTION2_SPI_REQ *pSMB = NULL; - TRANSACTION2_SPI_RSP *pSMBr = NULL; - int name_len; - int rc = 0; - int bytes_returned = 0; - __u16 params, param_offset, offset, byte_count, count; - OPEN_PSX_REQ *pdata; - OPEN_PSX_RSP *psx_rsp; - - cifs_dbg(FYI, "In POSIX Create\n"); -PsxCreat: - rc = smb_init(SMB_COM_TRANSACTION2, 15, tcon, (void **) &pSMB, - (void **) &pSMBr); - if (rc) - return rc; - - if (pSMB->hdr.Flags2 & SMBFLG2_UNICODE) { - name_len = - cifsConvertToUTF16((__le16 *) pSMB->FileName, name, - PATH_MAX, nls_codepage, remap); - name_len++; /* trailing null */ - name_len *= 2; - } else { - name_len = copy_path_name(pSMB->FileName, name); - } - - params = 6 + name_len; - count = sizeof(OPEN_PSX_REQ); - pSMB->MaxParameterCount = cpu_to_le16(2); - pSMB->MaxDataCount = cpu_to_le16(1000); /* large enough */ - pSMB->MaxSetupCount = 0; - pSMB->Reserved = 0; - pSMB->Flags = 0; - pSMB->Timeout = 0; - pSMB->Reserved2 = 0; - param_offset = offsetof(struct smb_com_transaction2_spi_req, - InformationLevel) - 4; - offset = param_offset + params; - /* SMB offsets are from the beginning of SMB which is 4 bytes in, after RFC1001 field */ - pdata = (OPEN_PSX_REQ *)((char *)(pSMB) + offset + 4); - pdata->Level = cpu_to_le16(SMB_QUERY_FILE_UNIX_BASIC); - pdata->Permissions = cpu_to_le64(mode); - pdata->PosixOpenFlags = cpu_to_le32(posix_flags); - pdata->OpenFlags = cpu_to_le32(*pOplock); - pSMB->ParameterOffset = cpu_to_le16(param_offset); - pSMB->DataOffset = cpu_to_le16(offset); - pSMB->SetupCount = 1; - pSMB->Reserved3 = 0; - pSMB->SubCommand = cpu_to_le16(TRANS2_SET_PATH_INFORMATION); - byte_count = 3 /* pad */ + params + count; - - pSMB->DataCount = cpu_to_le16(count); - pSMB->ParameterCount = cpu_to_le16(params); - pSMB->TotalDataCount = pSMB->DataCount; - pSMB->TotalParameterCount = pSMB->ParameterCount; - pSMB->InformationLevel = cpu_to_le16(SMB_POSIX_OPEN); - pSMB->Reserved4 = 0; - inc_rfc1001_len(pSMB, 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) { - cifs_dbg(FYI, "Posix create returned %d\n", rc); - goto psx_create_err; - } - - cifs_dbg(FYI, "copying inode info\n"); - rc = validate_t2((struct smb_t2_rsp *)pSMBr); - - if (rc || get_bcc(&pSMBr->hdr) < sizeof(OPEN_PSX_RSP)) { - rc = -EIO; /* bad smb */ - goto psx_create_err; - } - - /* copy return information to pRetData */ - psx_rsp = (OPEN_PSX_RSP *)((char *) &pSMBr->hdr.Protocol - + le16_to_cpu(pSMBr->t2.DataOffset)); - - *pOplock = le16_to_cpu(psx_rsp->OplockFlags); - if (netfid) - *netfid = psx_rsp->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? */ - if (cpu_to_le32(FILE_CREATE) == psx_rsp->CreateAction) - *pOplock |= CIFS_CREATE_ACTION; - /* check to make sure response data is there */ - if (psx_rsp->ReturnedLevel != cpu_to_le16(SMB_QUERY_FILE_UNIX_BASIC)) { - pRetData->Type = cpu_to_le32(-1); /* unknown */ - cifs_dbg(NOISY, "unknown type\n"); - } else { - if (get_bcc(&pSMBr->hdr) < sizeof(OPEN_PSX_RSP) - + sizeof(FILE_UNIX_BASIC_INFO)) { - cifs_dbg(VFS, "Open response data too small\n"); - pRetData->Type = cpu_to_le32(-1); - goto psx_create_err; - } - memcpy((char *) pRetData, - (char *)psx_rsp + sizeof(OPEN_PSX_RSP), - sizeof(FILE_UNIX_BASIC_INFO)); - } - -psx_create_err: - cifs_buf_release(pSMB); - - if (posix_flags & SMB_O_DIRECTORY) - cifs_stats_inc(&tcon->stats.cifs_stats.num_posixmkdirs); - else - cifs_stats_inc(&tcon->stats.cifs_stats.num_posixopens); - - if (rc == -EAGAIN) - goto PsxCreat; - - 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: - cifs_dbg(FYI, "unknown disposition %d\n", disposition); - ofun = SMBOPEN_OAPPEND; /* regular open */ - } - return ofun; -} - -static int -access_flags_to_smbopen_mode(const int access_flags) -{ - int masked_flags = access_flags & (GENERIC_READ | GENERIC_WRITE); - - if (masked_flags == GENERIC_READ) - return SMBOPEN_READ; - else if (masked_flags == GENERIC_WRITE) - return SMBOPEN_WRITE; - - /* just go for read/write */ - return SMBOPEN_READWRITE; -} - -int -SMBLegacyOpen(const unsigned int xid, struct cifs_tcon *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; - 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 = - cifsConvertToUTF16((__le16 *) (pSMB->fileName + 1), - fileName, PATH_MAX, nls_codepage, remap); - name_len++; /* trailing null */ - name_len *= 2; - } else { - count = 0; /* no pad */ - name_len = copy_path_name(pSMB->fileName, fileName); - } - 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); - pSMB->Mode = cpu_to_le16(access_flags_to_smbopen_mode(access_flags)); - 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 /* BB FIXME BB */ - pSMB->FileAttributes = cpu_to_le16(0/*ATTR_NORMAL*/); - - if (create_options & CREATE_OPTION_READONLY) - pSMB->FileAttributes |= cpu_to_le16(ATTR_READONLY); - - /* 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; - inc_rfc1001_len(pSMB, count); - - pSMB->ByteCount = cpu_to_le16(count); - rc = SendReceive(xid, tcon->ses, (struct smb_hdr *) pSMB, - (struct smb_hdr *)pSMBr, &bytes_returned, 0); - cifs_stats_inc(&tcon->stats.cifs_stats.num_opens); - if (rc) { - cifs_dbg(FYI, "Error in Open = %d\n", rc); - } else { - /* BB verify if wct == 15 */ - -/* *pOplock = pSMBr->OplockLevel; */ /* BB take from action field*/ - - *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); - pfile_info->DeletePending = 0; - } - } - - cifs_buf_release(pSMB); - if (rc == -EAGAIN) - goto OldOpenRetry; - return rc; -} - -int -CIFS_open(const unsigned int xid, struct cifs_open_parms *oparms, int *oplock, - FILE_ALL_INFO *buf) -{ - int rc; - OPEN_REQ *req = NULL; - OPEN_RSP *rsp = NULL; - int bytes_returned; - int name_len; - __u16 count; - struct cifs_sb_info *cifs_sb = oparms->cifs_sb; - struct cifs_tcon *tcon = oparms->tcon; - int remap = cifs_remap(cifs_sb); - const struct nls_table *nls = cifs_sb->local_nls; - int create_options = oparms->create_options; - int desired_access = oparms->desired_access; - int disposition = oparms->disposition; - const char *path = oparms->path; - -openRetry: - rc = smb_init(SMB_COM_NT_CREATE_ANDX, 24, tcon, (void **)&req, - (void **)&rsp); - if (rc) - return rc; - - /* no commands go after this */ - req->AndXCommand = 0xFF; - - if (req->hdr.Flags2 & SMBFLG2_UNICODE) { - /* account for one byte pad to word boundary */ - count = 1; - name_len = cifsConvertToUTF16((__le16 *)(req->fileName + 1), - path, PATH_MAX, nls, remap); - /* trailing null */ - name_len++; - name_len *= 2; - req->NameLength = cpu_to_le16(name_len); - } else { - /* BB improve check for buffer overruns BB */ - /* no pad */ - count = 0; - name_len = copy_path_name(req->fileName, path); - req->NameLength = cpu_to_le16(name_len); - } - - if (*oplock & REQ_OPLOCK) - req->OpenFlags = cpu_to_le32(REQ_OPLOCK); - else if (*oplock & REQ_BATCHOPLOCK) - req->OpenFlags = cpu_to_le32(REQ_BATCHOPLOCK); - - req->DesiredAccess = cpu_to_le32(desired_access); - req->AllocationSize = 0; - - /* - * 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) - req->FileAttributes = cpu_to_le32(ATTR_SYSTEM); - else - req->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. - */ - if (tcon->ses->capabilities & CAP_UNIX) - req->FileAttributes |= cpu_to_le32(ATTR_POSIX_SEMANTICS); - - if (create_options & CREATE_OPTION_READONLY) - req->FileAttributes |= cpu_to_le32(ATTR_READONLY); - - req->ShareAccess = cpu_to_le32(FILE_SHARE_ALL); - req->CreateDisposition = cpu_to_le32(disposition); - req->CreateOptions = cpu_to_le32(create_options & CREATE_OPTIONS_MASK); - - /* BB Expirement with various impersonation levels and verify */ - req->ImpersonationLevel = cpu_to_le32(SECURITY_IMPERSONATION); - req->SecurityFlags = SECURITY_CONTEXT_TRACKING|SECURITY_EFFECTIVE_ONLY; - - count += name_len; - inc_rfc1001_len(req, count); - - req->ByteCount = cpu_to_le16(count); - rc = SendReceive(xid, tcon->ses, (struct smb_hdr *)req, - (struct smb_hdr *)rsp, &bytes_returned, 0); - cifs_stats_inc(&tcon->stats.cifs_stats.num_opens); - if (rc) { - cifs_dbg(FYI, "Error in Open = %d\n", rc); - cifs_buf_release(req); - if (rc == -EAGAIN) - goto openRetry; - return rc; - } - - /* 1 byte no need to le_to_cpu */ - *oplock = rsp->OplockLevel; - /* cifs fid stays in le */ - oparms->fid->netfid = rsp->Fid; - oparms->fid->access = desired_access; - - /* Let caller know file was created so we can set the mode. */ - /* Do we care about the CreateAction in any other cases? */ - if (cpu_to_le32(FILE_CREATE) == rsp->CreateAction) - *oplock |= CIFS_CREATE_ACTION; - - if (buf) { - /* copy from CreationTime to Attributes */ - memcpy((char *)buf, (char *)&rsp->CreationTime, 36); - /* the file_info buf is endian converted by caller */ - buf->AllocationSize = rsp->AllocationSize; - buf->EndOfFile = rsp->EndOfFile; - buf->NumberOfLinks = cpu_to_le32(1); - buf->DeletePending = 0; - } - - cifs_buf_release(req); - return rc; -} - -static void -cifs_readv_callback(struct mid_q_entry *mid) -{ - struct cifs_readdata *rdata = mid->callback_data; - struct cifs_tcon *tcon = tlink_tcon(rdata->cfile->tlink); - struct TCP_Server_Info *server = tcon->ses->server; - struct smb_rqst rqst = { .rq_iov = rdata->iov, - .rq_nvec = 2, - .rq_pages = rdata->pages, - .rq_offset = rdata->page_offset, - .rq_npages = rdata->nr_pages, - .rq_pagesz = rdata->pagesz, - .rq_tailsz = rdata->tailsz }; - struct cifs_credits credits = { .value = 1, .instance = 0 }; - - cifs_dbg(FYI, "%s: mid=%llu state=%d result=%d bytes=%u\n", - __func__, mid->mid, mid->mid_state, rdata->result, - rdata->bytes); - - switch (mid->mid_state) { - case MID_RESPONSE_RECEIVED: - /* result already set, check signature */ - if (server->sign) { - int rc = 0; - - rc = cifs_verify_signature(&rqst, server, - mid->sequence_number); - if (rc) - cifs_dbg(VFS, "SMB signature verification returned error = %d\n", - rc); - } - /* FIXME: should this be counted toward the initiating task? */ - task_io_account_read(rdata->got_bytes); - cifs_stats_bytes_read(tcon, rdata->got_bytes); - break; - case MID_REQUEST_SUBMITTED: - case MID_RETRY_NEEDED: - rdata->result = -EAGAIN; - if (server->sign && rdata->got_bytes) - /* reset bytes number since we can not check a sign */ - rdata->got_bytes = 0; - /* FIXME: should this be counted toward the initiating task? */ - task_io_account_read(rdata->got_bytes); - cifs_stats_bytes_read(tcon, rdata->got_bytes); - break; - default: - rdata->result = -EIO; - } - - queue_work(cifsiod_wq, &rdata->work); - release_mid(mid); - add_credits(server, &credits, 0); -} - -/* cifs_async_readv - send an async write, and set up mid to handle result */ -int -cifs_async_readv(struct cifs_readdata *rdata) -{ - int rc; - READ_REQ *smb = NULL; - int wct; - struct cifs_tcon *tcon = tlink_tcon(rdata->cfile->tlink); - struct smb_rqst rqst = { .rq_iov = rdata->iov, - .rq_nvec = 2 }; - - cifs_dbg(FYI, "%s: offset=%llu bytes=%u\n", - __func__, rdata->offset, rdata->bytes); - - if (tcon->ses->capabilities & CAP_LARGE_FILES) - wct = 12; - else { - wct = 10; /* old style read */ - if ((rdata->offset >> 32) > 0) { - /* can not handle this big offset for old */ - return -EIO; - } - } - - rc = small_smb_init(SMB_COM_READ_ANDX, wct, tcon, (void **)&smb); - if (rc) - return rc; - - smb->hdr.Pid = cpu_to_le16((__u16)rdata->pid); - smb->hdr.PidHigh = cpu_to_le16((__u16)(rdata->pid >> 16)); - - smb->AndXCommand = 0xFF; /* none */ - smb->Fid = rdata->cfile->fid.netfid; - smb->OffsetLow = cpu_to_le32(rdata->offset & 0xFFFFFFFF); - if (wct == 12) - smb->OffsetHigh = cpu_to_le32(rdata->offset >> 32); - smb->Remaining = 0; - smb->MaxCount = cpu_to_le16(rdata->bytes & 0xFFFF); - smb->MaxCountHigh = cpu_to_le32(rdata->bytes >> 16); - if (wct == 12) - smb->ByteCount = 0; - else { - /* old style read */ - struct smb_com_readx_req *smbr = - (struct smb_com_readx_req *)smb; - smbr->ByteCount = 0; - } - - /* 4 for RFC1001 length + 1 for BCC */ - rdata->iov[0].iov_base = smb; - rdata->iov[0].iov_len = 4; - rdata->iov[1].iov_base = (char *)smb + 4; - rdata->iov[1].iov_len = get_rfc1002_length(smb); - - kref_get(&rdata->refcount); - rc = cifs_call_async(tcon->ses->server, &rqst, cifs_readv_receive, - cifs_readv_callback, NULL, rdata, 0, NULL); - - if (rc == 0) - cifs_stats_inc(&tcon->stats.cifs_stats.num_reads); - else - kref_put(&rdata->refcount, cifs_readdata_release); - - cifs_small_buf_release(smb); - return rc; -} - -int -CIFSSMBRead(const unsigned int xid, struct cifs_io_parms *io_parms, - unsigned int *nbytes, char **buf, int *pbuf_type) -{ - int rc = -EACCES; - READ_REQ *pSMB = NULL; - READ_RSP *pSMBr = NULL; - char *pReadData = NULL; - int wct; - int resp_buf_type = 0; - struct kvec iov[1]; - struct kvec rsp_iov; - __u32 pid = io_parms->pid; - __u16 netfid = io_parms->netfid; - __u64 offset = io_parms->offset; - struct cifs_tcon *tcon = io_parms->tcon; - unsigned int count = io_parms->length; - - cifs_dbg(FYI, "Reading %d bytes on fid %d\n", count, netfid); - if (tcon->ses->capabilities & CAP_LARGE_FILES) - wct = 12; - else { - wct = 10; /* old style read */ - if ((offset >> 32) > 0) { - /* can not handle this big offset for old */ - return -EIO; - } - } - - *nbytes = 0; - rc = small_smb_init(SMB_COM_READ_ANDX, wct, tcon, (void **) &pSMB); - if (rc) - return rc; - - pSMB->hdr.Pid = cpu_to_le16((__u16)pid); - pSMB->hdr.PidHigh = cpu_to_le16((__u16)(pid >> 16)); - - /* tcon and ses pointer are checked in smb_init */ - if (tcon->ses->server == NULL) - return -ECONNABORTED; - - pSMB->AndXCommand = 0xFF; /* none */ - pSMB->Fid = netfid; - pSMB->OffsetLow = cpu_to_le32(offset & 0xFFFFFFFF); - if (wct == 12) - pSMB->OffsetHigh = cpu_to_le32(offset >> 32); - - pSMB->Remaining = 0; - pSMB->MaxCount = cpu_to_le16(count & 0xFFFF); - pSMB->MaxCountHigh = cpu_to_le32(count >> 16); - 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; - } - - iov[0].iov_base = (char *)pSMB; - iov[0].iov_len = be32_to_cpu(pSMB->hdr.smb_buf_length) + 4; - rc = SendReceive2(xid, tcon->ses, iov, 1, &resp_buf_type, - CIFS_LOG_ERROR, &rsp_iov); - cifs_small_buf_release(pSMB); - cifs_stats_inc(&tcon->stats.cifs_stats.num_reads); - pSMBr = (READ_RSP *)rsp_iov.iov_base; - if (rc) { - cifs_dbg(VFS, "Send error in read = %d\n", rc); - } else { - int data_length = le16_to_cpu(pSMBr->DataLengthHigh); - data_length = data_length << 16; - data_length += le16_to_cpu(pSMBr->DataLength); - *nbytes = data_length; - - /*check that DataLength would not go beyond end of SMB */ - if ((data_length > CIFSMaxBufSize) - || (data_length > count)) { - cifs_dbg(FYI, "bad length %d for count %d\n", - data_length, count); - rc = -EIO; - *nbytes = 0; - } else { - pReadData = (char *) (&pSMBr->hdr.Protocol) + - le16_to_cpu(pSMBr->DataOffset); -/* if (rc = copy_to_user(buf, pReadData, data_length)) { - cifs_dbg(VFS, "Faulting on read rc = %d\n",rc); - rc = -EFAULT; - }*/ /* can not use copy_to_user when using page cache*/ - if (*buf) - memcpy(*buf, pReadData, data_length); - } - } - - if (*buf) { - free_rsp_buf(resp_buf_type, rsp_iov.iov_base); - } else if (resp_buf_type != CIFS_NO_BUFFER) { - /* return buffer to caller to free */ - *buf = rsp_iov.iov_base; - if (resp_buf_type == CIFS_SMALL_BUFFER) - *pbuf_type = CIFS_SMALL_BUFFER; - else if (resp_buf_type == CIFS_LARGE_BUFFER) - *pbuf_type = CIFS_LARGE_BUFFER; - } /* else no valid buffer on return - leave as null */ - - /* Note: On -EAGAIN error only caller can retry on handle based calls - since file handle passed in no longer valid */ - return rc; -} - - -int -CIFSSMBWrite(const unsigned int xid, struct cifs_io_parms *io_parms, - unsigned int *nbytes, const char *buf) -{ - int rc = -EACCES; - WRITE_REQ *pSMB = NULL; - WRITE_RSP *pSMBr = NULL; - int bytes_returned, wct; - __u32 bytes_sent; - __u16 byte_count; - __u32 pid = io_parms->pid; - __u16 netfid = io_parms->netfid; - __u64 offset = io_parms->offset; - struct cifs_tcon *tcon = io_parms->tcon; - unsigned int count = io_parms->length; - - *nbytes = 0; - - /* cifs_dbg(FYI, "write at %lld %d bytes\n", offset, count);*/ - if (tcon->ses == NULL) - return -ECONNABORTED; - - if (tcon->ses->capabilities & CAP_LARGE_FILES) - wct = 14; - else { - wct = 12; - if ((offset >> 32) > 0) { - /* can not handle big offset for old srv */ - return -EIO; - } - } - - rc = smb_init(SMB_COM_WRITE_ANDX, wct, tcon, (void **) &pSMB, - (void **) &pSMBr); - if (rc) - return rc; - - pSMB->hdr.Pid = cpu_to_le16((__u16)pid); - pSMB->hdr.PidHigh = cpu_to_le16((__u16)(pid >> 16)); - - /* tcon and ses pointer are checked in smb_init */ - if (tcon->ses->server == NULL) - return -ECONNABORTED; - - pSMB->AndXCommand = 0xFF; /* none */ - pSMB->Fid = netfid; - pSMB->OffsetLow = cpu_to_le32(offset & 0xFFFFFFFF); - if (wct == 14) - pSMB->OffsetHigh = cpu_to_le32(offset >> 32); - - pSMB->Reserved = 0xFFFFFFFF; - pSMB->WriteMode = 0; - pSMB->Remaining = 0; - - /* Can increase buffer size if buffer is big enough in some cases ie we - can send more if LARGE_WRITE_X capability returned by the server and if - our buffer is big enough or if we convert to iovecs on socket writes - and eliminate the copy to the CIFS buffer */ - if (tcon->ses->capabilities & CAP_LARGE_WRITE_X) { - bytes_sent = min_t(const unsigned int, CIFSMaxBufSize, count); - } else { - bytes_sent = (tcon->ses->server->maxBuf - MAX_CIFS_HDR_SIZE) - & ~0xFF; - } - - if (bytes_sent > count) - bytes_sent = count; - pSMB->DataOffset = - cpu_to_le16(offsetof(struct smb_com_write_req, Data) - 4); - if (buf) - memcpy(pSMB->Data, buf, bytes_sent); - 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 */ - - pSMB->DataLengthLow = cpu_to_le16(bytes_sent & 0xFFFF); - pSMB->DataLengthHigh = cpu_to_le16(bytes_sent >> 16); - inc_rfc1001_len(pSMB, 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, 0); - cifs_stats_inc(&tcon->stats.cifs_stats.num_writes); - if (rc) { - cifs_dbg(FYI, "Send error in write = %d\n", rc); - } else { - *nbytes = le16_to_cpu(pSMBr->CountHigh); - *nbytes = (*nbytes) << 16; - *nbytes += le16_to_cpu(pSMBr->Count); - - /* - * Mask off high 16 bits when bytes written as returned by the - * server is greater than bytes requested by the client. Some - * OS/2 servers are known to set incorrect CountHigh values. - */ - if (*nbytes > count) - *nbytes &= 0xFFFF; - } - - cifs_buf_release(pSMB); - - /* Note: On -EAGAIN error only caller can retry on handle based calls - since file handle passed in no longer valid */ - - return rc; -} - -/* - * Check the mid_state and signature on received buffer (if any), and queue the - * workqueue completion task. - */ -static void -cifs_writev_callback(struct mid_q_entry *mid) -{ - struct cifs_writedata *wdata = mid->callback_data; - struct cifs_tcon *tcon = tlink_tcon(wdata->cfile->tlink); - unsigned int written; - WRITE_RSP *smb = (WRITE_RSP *)mid->resp_buf; - struct cifs_credits credits = { .value = 1, .instance = 0 }; - - switch (mid->mid_state) { - case MID_RESPONSE_RECEIVED: - wdata->result = cifs_check_receive(mid, tcon->ses->server, 0); - if (wdata->result != 0) - break; - - written = le16_to_cpu(smb->CountHigh); - written <<= 16; - written += le16_to_cpu(smb->Count); - /* - * Mask off high 16 bits when bytes written as returned - * by the server is greater than bytes requested by the - * client. OS/2 servers are known to set incorrect - * CountHigh values. - */ - if (written > wdata->bytes) - written &= 0xFFFF; - - if (written < wdata->bytes) - wdata->result = -ENOSPC; - else - wdata->bytes = written; - break; - case MID_REQUEST_SUBMITTED: - case MID_RETRY_NEEDED: - wdata->result = -EAGAIN; - break; - default: - wdata->result = -EIO; - break; - } - - queue_work(cifsiod_wq, &wdata->work); - release_mid(mid); - add_credits(tcon->ses->server, &credits, 0); -} - -/* cifs_async_writev - send an async write, and set up mid to handle result */ -int -cifs_async_writev(struct cifs_writedata *wdata, - void (*release)(struct kref *kref)) -{ - int rc = -EACCES; - WRITE_REQ *smb = NULL; - int wct; - struct cifs_tcon *tcon = tlink_tcon(wdata->cfile->tlink); - struct kvec iov[2]; - struct smb_rqst rqst = { }; - - if (tcon->ses->capabilities & CAP_LARGE_FILES) { - wct = 14; - } else { - wct = 12; - if (wdata->offset >> 32 > 0) { - /* can not handle big offset for old srv */ - return -EIO; - } - } - - rc = small_smb_init(SMB_COM_WRITE_ANDX, wct, tcon, (void **)&smb); - if (rc) - goto async_writev_out; - - smb->hdr.Pid = cpu_to_le16((__u16)wdata->pid); - smb->hdr.PidHigh = cpu_to_le16((__u16)(wdata->pid >> 16)); - - smb->AndXCommand = 0xFF; /* none */ - smb->Fid = wdata->cfile->fid.netfid; - smb->OffsetLow = cpu_to_le32(wdata->offset & 0xFFFFFFFF); - if (wct == 14) - smb->OffsetHigh = cpu_to_le32(wdata->offset >> 32); - smb->Reserved = 0xFFFFFFFF; - smb->WriteMode = 0; - smb->Remaining = 0; - - smb->DataOffset = - cpu_to_le16(offsetof(struct smb_com_write_req, Data) - 4); - - /* 4 for RFC1001 length + 1 for BCC */ - iov[0].iov_len = 4; - iov[0].iov_base = smb; - iov[1].iov_len = get_rfc1002_length(smb) + 1; - iov[1].iov_base = (char *)smb + 4; - - rqst.rq_iov = iov; - rqst.rq_nvec = 2; - rqst.rq_pages = wdata->pages; - rqst.rq_offset = wdata->page_offset; - rqst.rq_npages = wdata->nr_pages; - rqst.rq_pagesz = wdata->pagesz; - rqst.rq_tailsz = wdata->tailsz; - - cifs_dbg(FYI, "async write at %llu %u bytes\n", - wdata->offset, wdata->bytes); - - smb->DataLengthLow = cpu_to_le16(wdata->bytes & 0xFFFF); - smb->DataLengthHigh = cpu_to_le16(wdata->bytes >> 16); - - if (wct == 14) { - inc_rfc1001_len(&smb->hdr, wdata->bytes + 1); - put_bcc(wdata->bytes + 1, &smb->hdr); - } else { - /* wct == 12 */ - struct smb_com_writex_req *smbw = - (struct smb_com_writex_req *)smb; - inc_rfc1001_len(&smbw->hdr, wdata->bytes + 5); - put_bcc(wdata->bytes + 5, &smbw->hdr); - iov[1].iov_len += 4; /* pad bigger by four bytes */ - } - - kref_get(&wdata->refcount); - rc = cifs_call_async(tcon->ses->server, &rqst, NULL, - cifs_writev_callback, NULL, wdata, 0, NULL); - - if (rc == 0) - cifs_stats_inc(&tcon->stats.cifs_stats.num_writes); - else - kref_put(&wdata->refcount, release); - -async_writev_out: - cifs_small_buf_release(smb); - return rc; -} - -int -CIFSSMBWrite2(const unsigned int xid, struct cifs_io_parms *io_parms, - unsigned int *nbytes, struct kvec *iov, int n_vec) -{ - int rc; - WRITE_REQ *pSMB = NULL; - int wct; - int smb_hdr_len; - int resp_buf_type = 0; - __u32 pid = io_parms->pid; - __u16 netfid = io_parms->netfid; - __u64 offset = io_parms->offset; - struct cifs_tcon *tcon = io_parms->tcon; - unsigned int count = io_parms->length; - struct kvec rsp_iov; - - *nbytes = 0; - - cifs_dbg(FYI, "write2 at %lld %d bytes\n", (long long)offset, count); - - if (tcon->ses->capabilities & CAP_LARGE_FILES) { - wct = 14; - } else { - wct = 12; - if ((offset >> 32) > 0) { - /* can not handle big offset for old srv */ - return -EIO; - } - } - rc = small_smb_init(SMB_COM_WRITE_ANDX, wct, tcon, (void **) &pSMB); - if (rc) - return rc; - - pSMB->hdr.Pid = cpu_to_le16((__u16)pid); - pSMB->hdr.PidHigh = cpu_to_le16((__u16)(pid >> 16)); - - /* tcon and ses pointer are checked in smb_init */ - if (tcon->ses->server == NULL) - return -ECONNABORTED; - - pSMB->AndXCommand = 0xFF; /* none */ - pSMB->Fid = netfid; - pSMB->OffsetLow = cpu_to_le32(offset & 0xFFFFFFFF); - if (wct == 14) - pSMB->OffsetHigh = cpu_to_le32(offset >> 32); - pSMB->Reserved = 0xFFFFFFFF; - pSMB->WriteMode = 0; - pSMB->Remaining = 0; - - pSMB->DataOffset = - cpu_to_le16(offsetof(struct smb_com_write_req, Data) - 4); - - pSMB->DataLengthLow = cpu_to_le16(count & 0xFFFF); - pSMB->DataLengthHigh = cpu_to_le16(count >> 16); - /* header + 1 byte pad */ - smb_hdr_len = be32_to_cpu(pSMB->hdr.smb_buf_length) + 1; - if (wct == 14) - inc_rfc1001_len(pSMB, count + 1); - else /* wct == 12 */ - inc_rfc1001_len(pSMB, 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; - if (wct == 14) - iov[0].iov_len = smb_hdr_len + 4; - else /* wct == 12 pad bigger by four bytes */ - iov[0].iov_len = smb_hdr_len + 8; - - rc = SendReceive2(xid, tcon->ses, iov, n_vec + 1, &resp_buf_type, 0, - &rsp_iov); - cifs_small_buf_release(pSMB); - cifs_stats_inc(&tcon->stats.cifs_stats.num_writes); - if (rc) { - cifs_dbg(FYI, "Send error Write2 = %d\n", rc); - } else if (resp_buf_type == 0) { - /* presumably this can not happen, but best to be safe */ - rc = -EIO; - } else { - WRITE_RSP *pSMBr = (WRITE_RSP *)rsp_iov.iov_base; - *nbytes = le16_to_cpu(pSMBr->CountHigh); - *nbytes = (*nbytes) << 16; - *nbytes += le16_to_cpu(pSMBr->Count); - - /* - * Mask off high 16 bits when bytes written as returned by the - * server is greater than bytes requested by the client. OS/2 - * servers are known to set incorrect CountHigh values. - */ - if (*nbytes > count) - *nbytes &= 0xFFFF; - } - - free_rsp_buf(resp_buf_type, rsp_iov.iov_base); - - /* Note: On -EAGAIN error only caller can retry on handle based calls - since file handle passed in no longer valid */ - - return rc; -} - -int cifs_lockv(const unsigned int xid, struct cifs_tcon *tcon, - const __u16 netfid, const __u8 lock_type, const __u32 num_unlock, - const __u32 num_lock, LOCKING_ANDX_RANGE *buf) -{ - int rc = 0; - LOCK_REQ *pSMB = NULL; - struct kvec iov[2]; - struct kvec rsp_iov; - int resp_buf_type; - __u16 count; - - cifs_dbg(FYI, "cifs_lockv num lock %d num unlock %d\n", - num_lock, num_unlock); - - rc = small_smb_init(SMB_COM_LOCKING_ANDX, 8, tcon, (void **) &pSMB); - if (rc) - return rc; - - pSMB->Timeout = 0; - pSMB->NumberOfLocks = cpu_to_le16(num_lock); - pSMB->NumberOfUnlocks = cpu_to_le16(num_unlock); - pSMB->LockType = lock_type; - pSMB->AndXCommand = 0xFF; /* none */ - pSMB->Fid = netfid; /* netfid stays le */ - - count = (num_unlock + num_lock) * sizeof(LOCKING_ANDX_RANGE); - inc_rfc1001_len(pSMB, count); - pSMB->ByteCount = cpu_to_le16(count); - - iov[0].iov_base = (char *)pSMB; - iov[0].iov_len = be32_to_cpu(pSMB->hdr.smb_buf_length) + 4 - - (num_unlock + num_lock) * sizeof(LOCKING_ANDX_RANGE); - iov[1].iov_base = (char *)buf; - iov[1].iov_len = (num_unlock + num_lock) * sizeof(LOCKING_ANDX_RANGE); - - cifs_stats_inc(&tcon->stats.cifs_stats.num_locks); - rc = SendReceive2(xid, tcon->ses, iov, 2, &resp_buf_type, - CIFS_NO_RSP_BUF, &rsp_iov); - cifs_small_buf_release(pSMB); - if (rc) - cifs_dbg(FYI, "Send error in cifs_lockv = %d\n", rc); - - return rc; -} - -int -CIFSSMBLock(const unsigned int xid, struct cifs_tcon *tcon, - const __u16 smb_file_id, const __u32 netpid, const __u64 len, - const __u64 offset, const __u32 numUnlock, - const __u32 numLock, const __u8 lockType, - const bool waitFlag, const __u8 oplock_level) -{ - int rc = 0; - LOCK_REQ *pSMB = NULL; -/* LOCK_RSP *pSMBr = NULL; */ /* No response data other than rc to parse */ - int bytes_returned; - int flags = 0; - __u16 count; - - cifs_dbg(FYI, "CIFSSMBLock timeout %d numLock %d\n", - (int)waitFlag, numLock); - rc = small_smb_init(SMB_COM_LOCKING_ANDX, 8, tcon, (void **) &pSMB); - - if (rc) - return rc; - - if (lockType == LOCKING_ANDX_OPLOCK_RELEASE) { - /* no response expected */ - flags = CIFS_NO_SRV_RSP | CIFS_NON_BLOCKING | CIFS_OBREAK_OP; - pSMB->Timeout = 0; - } else if (waitFlag) { - flags = CIFS_BLOCKING_OP; /* blocking operation, no timeout */ - pSMB->Timeout = cpu_to_le32(-1);/* blocking - do not time out */ - } else { - pSMB->Timeout = 0; - } - - pSMB->NumberOfLocks = cpu_to_le16(numLock); - pSMB->NumberOfUnlocks = cpu_to_le16(numUnlock); - pSMB->LockType = lockType; - pSMB->OplockLevel = oplock_level; - pSMB->AndXCommand = 0xFF; /* none */ - pSMB->Fid = smb_file_id; /* netfid stays le */ - - if ((numLock != 0) || (numUnlock != 0)) { - pSMB->Locks[0].Pid = cpu_to_le16(netpid); - /* BB where to store pid high? */ - pSMB->Locks[0].LengthLow = cpu_to_le32((u32)len); - pSMB->Locks[0].LengthHigh = cpu_to_le32((u32)(len>>32)); - pSMB->Locks[0].OffsetLow = cpu_to_le32((u32)offset); - pSMB->Locks[0].OffsetHigh = cpu_to_le32((u32)(offset>>32)); - count = sizeof(LOCKING_ANDX_RANGE); - } else { - /* oplock break */ - count = 0; - } - inc_rfc1001_len(pSMB, count); - pSMB->ByteCount = cpu_to_le16(count); - - if (waitFlag) - rc = SendReceiveBlockingLock(xid, tcon, (struct smb_hdr *) pSMB, - (struct smb_hdr *) pSMB, &bytes_returned); - else - rc = SendReceiveNoRsp(xid, tcon->ses, (char *)pSMB, flags); - cifs_small_buf_release(pSMB); - cifs_stats_inc(&tcon->stats.cifs_stats.num_locks); - if (rc) - cifs_dbg(FYI, "Send error in Lock = %d\n", rc); - - /* Note: On -EAGAIN error only caller can retry on handle based calls - since file handle passed in no longer valid */ - return rc; -} - -int -CIFSSMBPosixLock(const unsigned int xid, struct cifs_tcon *tcon, - const __u16 smb_file_id, const __u32 netpid, - const loff_t start_offset, const __u64 len, - struct file_lock *pLockData, const __u16 lock_type, - const bool waitFlag) -{ - struct smb_com_transaction2_sfi_req *pSMB = NULL; - struct smb_com_transaction2_sfi_rsp *pSMBr = NULL; - struct cifs_posix_lock *parm_data; - int rc = 0; - int timeout = 0; - int bytes_returned = 0; - int resp_buf_type = 0; - __u16 params, param_offset, offset, byte_count, count; - struct kvec iov[1]; - struct kvec rsp_iov; - - cifs_dbg(FYI, "Posix Lock\n"); - - rc = small_smb_init(SMB_COM_TRANSACTION2, 15, tcon, (void **) &pSMB); - - if (rc) - return rc; - - pSMBr = (struct smb_com_transaction2_sfi_rsp *)pSMB; - - params = 6; - pSMB->MaxSetupCount = 0; - pSMB->Reserved = 0; - pSMB->Flags = 0; - pSMB->Reserved2 = 0; - param_offset = offsetof(struct smb_com_transaction2_sfi_req, Fid) - 4; - offset = param_offset + params; - - count = sizeof(struct cifs_posix_lock); - pSMB->MaxParameterCount = cpu_to_le16(2); - pSMB->MaxDataCount = cpu_to_le16(1000); /* BB find max SMB from sess */ - pSMB->SetupCount = 1; - pSMB->Reserved3 = 0; - if (pLockData) - pSMB->SubCommand = cpu_to_le16(TRANS2_QUERY_FILE_INFORMATION); - else - pSMB->SubCommand = cpu_to_le16(TRANS2_SET_FILE_INFORMATION); - byte_count = 3 /* pad */ + params + count; - pSMB->DataCount = cpu_to_le16(count); - pSMB->ParameterCount = cpu_to_le16(params); - pSMB->TotalDataCount = pSMB->DataCount; - pSMB->TotalParameterCount = pSMB->ParameterCount; - pSMB->ParameterOffset = cpu_to_le16(param_offset); - /* SMB offsets are from the beginning of SMB which is 4 bytes in, after RFC1001 field */ - parm_data = (struct cifs_posix_lock *) - (((char *)pSMB) + offset + 4); - - parm_data->lock_type = cpu_to_le16(lock_type); - if (waitFlag) { - timeout = CIFS_BLOCKING_OP; /* blocking operation, no timeout */ - parm_data->lock_flags = cpu_to_le16(1); - pSMB->Timeout = cpu_to_le32(-1); - } else - pSMB->Timeout = 0; - - parm_data->pid = cpu_to_le32(netpid); - parm_data->start = cpu_to_le64(start_offset); - parm_data->length = cpu_to_le64(len); /* normalize negative numbers */ - - pSMB->DataOffset = cpu_to_le16(offset); - pSMB->Fid = smb_file_id; - pSMB->InformationLevel = cpu_to_le16(SMB_SET_POSIX_LOCK); - pSMB->Reserved4 = 0; - inc_rfc1001_len(pSMB, byte_count); - pSMB->ByteCount = cpu_to_le16(byte_count); - if (waitFlag) { - rc = SendReceiveBlockingLock(xid, tcon, (struct smb_hdr *) pSMB, - (struct smb_hdr *) pSMBr, &bytes_returned); - } else { - iov[0].iov_base = (char *)pSMB; - iov[0].iov_len = be32_to_cpu(pSMB->hdr.smb_buf_length) + 4; - rc = SendReceive2(xid, tcon->ses, iov, 1 /* num iovecs */, - &resp_buf_type, timeout, &rsp_iov); - pSMBr = (struct smb_com_transaction2_sfi_rsp *)rsp_iov.iov_base; - } - cifs_small_buf_release(pSMB); - - if (rc) { - cifs_dbg(FYI, "Send error in Posix Lock = %d\n", rc); - } else if (pLockData) { - /* lock structure can be returned on get */ - __u16 data_offset; - __u16 data_count; - rc = validate_t2((struct smb_t2_rsp *)pSMBr); - - if (rc || get_bcc(&pSMBr->hdr) < sizeof(*parm_data)) { - rc = -EIO; /* bad smb */ - goto plk_err_exit; - } - data_offset = le16_to_cpu(pSMBr->t2.DataOffset); - data_count = le16_to_cpu(pSMBr->t2.DataCount); - if (data_count < sizeof(struct cifs_posix_lock)) { - rc = -EIO; - goto plk_err_exit; - } - parm_data = (struct cifs_posix_lock *) - ((char *)&pSMBr->hdr.Protocol + data_offset); - if (parm_data->lock_type == cpu_to_le16(CIFS_UNLCK)) - pLockData->fl_type = F_UNLCK; - else { - if (parm_data->lock_type == - cpu_to_le16(CIFS_RDLCK)) - pLockData->fl_type = F_RDLCK; - else if (parm_data->lock_type == - cpu_to_le16(CIFS_WRLCK)) - pLockData->fl_type = F_WRLCK; - - pLockData->fl_start = le64_to_cpu(parm_data->start); - pLockData->fl_end = pLockData->fl_start + - (le64_to_cpu(parm_data->length) ? - le64_to_cpu(parm_data->length) - 1 : 0); - pLockData->fl_pid = -le32_to_cpu(parm_data->pid); - } - } - -plk_err_exit: - free_rsp_buf(resp_buf_type, rsp_iov.iov_base); - - /* Note: On -EAGAIN error only caller can retry on handle based calls - since file handle passed in no longer valid */ - - return rc; -} - - -int -CIFSSMBClose(const unsigned int xid, struct cifs_tcon *tcon, int smb_file_id) -{ - int rc = 0; - CLOSE_REQ *pSMB = NULL; - cifs_dbg(FYI, "In CIFSSMBClose\n"); - -/* do not retry on dead session on close */ - rc = small_smb_init(SMB_COM_CLOSE, 3, tcon, (void **) &pSMB); - if (rc == -EAGAIN) - return 0; - if (rc) - return rc; - - pSMB->FileID = (__u16) smb_file_id; - pSMB->LastWriteTime = 0xFFFFFFFF; - pSMB->ByteCount = 0; - rc = SendReceiveNoRsp(xid, tcon->ses, (char *) pSMB, 0); - cifs_small_buf_release(pSMB); - cifs_stats_inc(&tcon->stats.cifs_stats.num_closes); - if (rc) { - if (rc != -EINTR) { - /* EINTR is expected when user ctl-c to kill app */ - cifs_dbg(VFS, "Send error in Close = %d\n", rc); - } - } - - /* Since session is dead, file will be closed on server already */ - if (rc == -EAGAIN) - rc = 0; - - return rc; -} - -int -CIFSSMBFlush(const unsigned int xid, struct cifs_tcon *tcon, int smb_file_id) -{ - int rc = 0; - FLUSH_REQ *pSMB = NULL; - cifs_dbg(FYI, "In CIFSSMBFlush\n"); - - rc = small_smb_init(SMB_COM_FLUSH, 1, tcon, (void **) &pSMB); - if (rc) - return rc; - - pSMB->FileID = (__u16) smb_file_id; - pSMB->ByteCount = 0; - rc = SendReceiveNoRsp(xid, tcon->ses, (char *) pSMB, 0); - cifs_small_buf_release(pSMB); - cifs_stats_inc(&tcon->stats.cifs_stats.num_flushes); - if (rc) - cifs_dbg(VFS, "Send error in Flush = %d\n", rc); - - return rc; -} - -int -CIFSSMBRename(const unsigned int xid, struct cifs_tcon *tcon, - const char *from_name, const char *to_name, - struct cifs_sb_info *cifs_sb) -{ - int rc = 0; - RENAME_REQ *pSMB = NULL; - RENAME_RSP *pSMBr = NULL; - int bytes_returned; - int name_len, name_len2; - __u16 count; - int remap = cifs_remap(cifs_sb); - - cifs_dbg(FYI, "In CIFSSMBRename\n"); -renameRetry: - rc = smb_init(SMB_COM_RENAME, 1, tcon, (void **) &pSMB, - (void **) &pSMBr); - if (rc) - return rc; - - pSMB->BufferFormat = 0x04; - pSMB->SearchAttributes = - cpu_to_le16(ATTR_READONLY | ATTR_HIDDEN | ATTR_SYSTEM | - ATTR_DIRECTORY); - - if (pSMB->hdr.Flags2 & SMBFLG2_UNICODE) { - name_len = cifsConvertToUTF16((__le16 *) pSMB->OldFileName, - from_name, PATH_MAX, - cifs_sb->local_nls, remap); - name_len++; /* trailing null */ - name_len *= 2; - pSMB->OldFileName[name_len] = 0x04; /* pad */ - /* protocol requires ASCII signature byte on Unicode string */ - pSMB->OldFileName[name_len + 1] = 0x00; - name_len2 = - cifsConvertToUTF16((__le16 *)&pSMB->OldFileName[name_len+2], - to_name, PATH_MAX, cifs_sb->local_nls, - remap); - name_len2 += 1 /* trailing null */ + 1 /* Signature word */ ; - name_len2 *= 2; /* convert to bytes */ - } else { - name_len = copy_path_name(pSMB->OldFileName, from_name); - name_len2 = copy_path_name(pSMB->OldFileName+name_len+1, to_name); - pSMB->OldFileName[name_len] = 0x04; /* 2nd buffer format */ - name_len2++; /* signature byte */ - } - - count = 1 /* 1st signature byte */ + name_len + name_len2; - inc_rfc1001_len(pSMB, count); - pSMB->ByteCount = cpu_to_le16(count); - - rc = SendReceive(xid, tcon->ses, (struct smb_hdr *) pSMB, - (struct smb_hdr *) pSMBr, &bytes_returned, 0); - cifs_stats_inc(&tcon->stats.cifs_stats.num_renames); - if (rc) - cifs_dbg(FYI, "Send error in rename = %d\n", rc); - - cifs_buf_release(pSMB); - - if (rc == -EAGAIN) - goto renameRetry; - - return rc; -} - -int CIFSSMBRenameOpenFile(const unsigned int xid, struct cifs_tcon *pTcon, - int netfid, const char *target_name, - const struct nls_table *nls_codepage, int remap) -{ - struct smb_com_transaction2_sfi_req *pSMB = NULL; - struct smb_com_transaction2_sfi_rsp *pSMBr = NULL; - struct set_file_rename *rename_info; - char *data_offset; - char dummy_string[30]; - int rc = 0; - int bytes_returned = 0; - int len_of_str; - __u16 params, param_offset, offset, count, byte_count; - - cifs_dbg(FYI, "Rename to File by handle\n"); - rc = smb_init(SMB_COM_TRANSACTION2, 15, pTcon, (void **) &pSMB, - (void **) &pSMBr); - if (rc) - return rc; - - params = 6; - pSMB->MaxSetupCount = 0; - pSMB->Reserved = 0; - pSMB->Flags = 0; - pSMB->Timeout = 0; - pSMB->Reserved2 = 0; - param_offset = offsetof(struct smb_com_transaction2_sfi_req, Fid) - 4; - offset = param_offset + params; - - /* SMB offsets are from the beginning of SMB which is 4 bytes in, after RFC1001 field */ - data_offset = (char *)(pSMB) + offset + 4; - rename_info = (struct set_file_rename *) data_offset; - pSMB->MaxParameterCount = cpu_to_le16(2); - pSMB->MaxDataCount = cpu_to_le16(1000); /* BB find max SMB from sess */ - pSMB->SetupCount = 1; - pSMB->Reserved3 = 0; - pSMB->SubCommand = cpu_to_le16(TRANS2_SET_FILE_INFORMATION); - byte_count = 3 /* pad */ + params; - pSMB->ParameterCount = cpu_to_le16(params); - pSMB->TotalParameterCount = pSMB->ParameterCount; - pSMB->ParameterOffset = cpu_to_le16(param_offset); - pSMB->DataOffset = cpu_to_le16(offset); - /* construct random name ".cifs_tmp" */ - rename_info->overwrite = cpu_to_le32(1); - rename_info->root_fid = 0; - /* unicode only call */ - if (target_name == NULL) { - sprintf(dummy_string, "cifs%x", pSMB->hdr.Mid); - len_of_str = - cifsConvertToUTF16((__le16 *)rename_info->target_name, - dummy_string, 24, nls_codepage, remap); - } else { - len_of_str = - cifsConvertToUTF16((__le16 *)rename_info->target_name, - target_name, PATH_MAX, nls_codepage, - remap); - } - rename_info->target_name_len = cpu_to_le32(2 * len_of_str); - count = sizeof(struct set_file_rename) + (2 * len_of_str); - byte_count += count; - pSMB->DataCount = cpu_to_le16(count); - pSMB->TotalDataCount = pSMB->DataCount; - pSMB->Fid = netfid; - pSMB->InformationLevel = - cpu_to_le16(SMB_SET_FILE_RENAME_INFORMATION); - pSMB->Reserved4 = 0; - inc_rfc1001_len(pSMB, byte_count); - 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->stats.cifs_stats.num_t2renames); - if (rc) - cifs_dbg(FYI, "Send error in Rename (by file handle) = %d\n", - rc); - - cifs_buf_release(pSMB); - - /* Note: On -EAGAIN error only caller can retry on handle based calls - since file handle passed in no longer valid */ - - return rc; -} - -int -CIFSSMBCopy(const unsigned int xid, struct cifs_tcon *tcon, - const char *fromName, const __u16 target_tid, const char *toName, - const int flags, const struct nls_table *nls_codepage, int remap) -{ - int rc = 0; - COPY_REQ *pSMB = NULL; - COPY_RSP *pSMBr = NULL; - int bytes_returned; - int name_len, name_len2; - __u16 count; - - cifs_dbg(FYI, "In CIFSSMBCopy\n"); -copyRetry: - rc = smb_init(SMB_COM_COPY, 1, tcon, (void **) &pSMB, - (void **) &pSMBr); - if (rc) - return rc; - - pSMB->BufferFormat = 0x04; - pSMB->Tid2 = target_tid; - - pSMB->Flags = cpu_to_le16(flags & COPY_TREE); - - if (pSMB->hdr.Flags2 & SMBFLG2_UNICODE) { - name_len = cifsConvertToUTF16((__le16 *) pSMB->OldFileName, - fromName, PATH_MAX, nls_codepage, - remap); - name_len++; /* trailing null */ - name_len *= 2; - pSMB->OldFileName[name_len] = 0x04; /* pad */ - /* protocol requires ASCII signature byte on Unicode string */ - pSMB->OldFileName[name_len + 1] = 0x00; - name_len2 = - cifsConvertToUTF16((__le16 *)&pSMB->OldFileName[name_len+2], - toName, PATH_MAX, nls_codepage, remap); - name_len2 += 1 /* trailing null */ + 1 /* Signature word */ ; - name_len2 *= 2; /* convert to bytes */ - } else { - name_len = copy_path_name(pSMB->OldFileName, fromName); - pSMB->OldFileName[name_len] = 0x04; /* 2nd buffer format */ - name_len2 = copy_path_name(pSMB->OldFileName+name_len+1, toName); - name_len2++; /* signature byte */ - } - - count = 1 /* 1st signature byte */ + name_len + name_len2; - inc_rfc1001_len(pSMB, count); - pSMB->ByteCount = cpu_to_le16(count); - - rc = SendReceive(xid, tcon->ses, (struct smb_hdr *) pSMB, - (struct smb_hdr *) pSMBr, &bytes_returned, 0); - if (rc) { - cifs_dbg(FYI, "Send error in copy = %d with %d files copied\n", - rc, le16_to_cpu(pSMBr->CopyCount)); - } - cifs_buf_release(pSMB); - - if (rc == -EAGAIN) - goto copyRetry; - - return rc; -} - -int -CIFSUnixCreateSymLink(const unsigned int xid, struct cifs_tcon *tcon, - const char *fromName, const char *toName, - const struct nls_table *nls_codepage, int remap) -{ - TRANSACTION2_SPI_REQ *pSMB = NULL; - TRANSACTION2_SPI_RSP *pSMBr = NULL; - char *data_offset; - int name_len; - int name_len_target; - int rc = 0; - int bytes_returned = 0; - __u16 params, param_offset, offset, byte_count; - - cifs_dbg(FYI, "In Symlink Unix style\n"); -createSymLinkRetry: - rc = smb_init(SMB_COM_TRANSACTION2, 15, tcon, (void **) &pSMB, - (void **) &pSMBr); - if (rc) - return rc; - - if (pSMB->hdr.Flags2 & SMBFLG2_UNICODE) { - name_len = - cifsConvertToUTF16((__le16 *) pSMB->FileName, fromName, - /* find define for this maxpathcomponent */ - PATH_MAX, nls_codepage, remap); - name_len++; /* trailing null */ - name_len *= 2; - - } else { - name_len = copy_path_name(pSMB->FileName, fromName); - } - params = 6 + name_len; - pSMB->MaxSetupCount = 0; - pSMB->Reserved = 0; - pSMB->Flags = 0; - pSMB->Timeout = 0; - pSMB->Reserved2 = 0; - param_offset = offsetof(struct smb_com_transaction2_spi_req, - InformationLevel) - 4; - offset = param_offset + params; - - /* SMB offsets are from the beginning of SMB which is 4 bytes in, after RFC1001 field */ - data_offset = (char *)pSMB + offset + 4; - if (pSMB->hdr.Flags2 & SMBFLG2_UNICODE) { - name_len_target = - cifsConvertToUTF16((__le16 *) data_offset, toName, - /* find define for this maxpathcomponent */ - PATH_MAX, nls_codepage, remap); - name_len_target++; /* trailing null */ - name_len_target *= 2; - } else { - name_len_target = copy_path_name(data_offset, toName); - } - - pSMB->MaxParameterCount = cpu_to_le16(2); - /* BB find exact max on data count below from sess */ - pSMB->MaxDataCount = cpu_to_le16(1000); - pSMB->SetupCount = 1; - pSMB->Reserved3 = 0; - pSMB->SubCommand = cpu_to_le16(TRANS2_SET_PATH_INFORMATION); - byte_count = 3 /* pad */ + params + name_len_target; - pSMB->DataCount = cpu_to_le16(name_len_target); - 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); - pSMB->InformationLevel = cpu_to_le16(SMB_SET_FILE_UNIX_LINK); - pSMB->Reserved4 = 0; - inc_rfc1001_len(pSMB, 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); - cifs_stats_inc(&tcon->stats.cifs_stats.num_symlinks); - if (rc) - cifs_dbg(FYI, "Send error in SetPathInfo create symlink = %d\n", - rc); - - cifs_buf_release(pSMB); - - if (rc == -EAGAIN) - goto createSymLinkRetry; - - return rc; -} - -int -CIFSUnixCreateHardLink(const unsigned int xid, struct cifs_tcon *tcon, - const char *fromName, const char *toName, - const struct nls_table *nls_codepage, int remap) -{ - TRANSACTION2_SPI_REQ *pSMB = NULL; - TRANSACTION2_SPI_RSP *pSMBr = NULL; - char *data_offset; - int name_len; - int name_len_target; - int rc = 0; - int bytes_returned = 0; - __u16 params, param_offset, offset, byte_count; - - cifs_dbg(FYI, "In Create Hard link Unix style\n"); -createHardLinkRetry: - rc = smb_init(SMB_COM_TRANSACTION2, 15, tcon, (void **) &pSMB, - (void **) &pSMBr); - if (rc) - return rc; - - if (pSMB->hdr.Flags2 & SMBFLG2_UNICODE) { - name_len = cifsConvertToUTF16((__le16 *) pSMB->FileName, toName, - PATH_MAX, nls_codepage, remap); - name_len++; /* trailing null */ - name_len *= 2; - - } else { - name_len = copy_path_name(pSMB->FileName, toName); - } - params = 6 + name_len; - pSMB->MaxSetupCount = 0; - pSMB->Reserved = 0; - pSMB->Flags = 0; - pSMB->Timeout = 0; - pSMB->Reserved2 = 0; - param_offset = offsetof(struct smb_com_transaction2_spi_req, - InformationLevel) - 4; - offset = param_offset + params; - - /* SMB offsets are from the beginning of SMB which is 4 bytes in, after RFC1001 field */ - data_offset = (char *)pSMB + offset + 4; - if (pSMB->hdr.Flags2 & SMBFLG2_UNICODE) { - name_len_target = - cifsConvertToUTF16((__le16 *) data_offset, fromName, - PATH_MAX, nls_codepage, remap); - name_len_target++; /* trailing null */ - name_len_target *= 2; - } else { - name_len_target = copy_path_name(data_offset, fromName); - } - - pSMB->MaxParameterCount = cpu_to_le16(2); - /* BB find exact max on data count below from sess*/ - pSMB->MaxDataCount = cpu_to_le16(1000); - pSMB->SetupCount = 1; - pSMB->Reserved3 = 0; - pSMB->SubCommand = cpu_to_le16(TRANS2_SET_PATH_INFORMATION); - byte_count = 3 /* pad */ + params + name_len_target; - pSMB->ParameterCount = cpu_to_le16(params); - pSMB->TotalParameterCount = pSMB->ParameterCount; - pSMB->DataCount = cpu_to_le16(name_len_target); - pSMB->TotalDataCount = pSMB->DataCount; - pSMB->ParameterOffset = cpu_to_le16(param_offset); - pSMB->DataOffset = cpu_to_le16(offset); - pSMB->InformationLevel = cpu_to_le16(SMB_SET_FILE_UNIX_HLINK); - pSMB->Reserved4 = 0; - inc_rfc1001_len(pSMB, 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); - cifs_stats_inc(&tcon->stats.cifs_stats.num_hardlinks); - if (rc) - cifs_dbg(FYI, "Send error in SetPathInfo (hard link) = %d\n", - rc); - - cifs_buf_release(pSMB); - if (rc == -EAGAIN) - goto createHardLinkRetry; - - return rc; -} - -int -CIFSCreateHardLink(const unsigned int xid, struct cifs_tcon *tcon, - const char *from_name, const char *to_name, - struct cifs_sb_info *cifs_sb) -{ - int rc = 0; - NT_RENAME_REQ *pSMB = NULL; - RENAME_RSP *pSMBr = NULL; - int bytes_returned; - int name_len, name_len2; - __u16 count; - int remap = cifs_remap(cifs_sb); - - cifs_dbg(FYI, "In CIFSCreateHardLink\n"); -winCreateHardLinkRetry: - - rc = smb_init(SMB_COM_NT_RENAME, 4, tcon, (void **) &pSMB, - (void **) &pSMBr); - if (rc) - return rc; - - pSMB->SearchAttributes = - cpu_to_le16(ATTR_READONLY | ATTR_HIDDEN | ATTR_SYSTEM | - ATTR_DIRECTORY); - pSMB->Flags = cpu_to_le16(CREATE_HARD_LINK); - pSMB->ClusterCount = 0; - - pSMB->BufferFormat = 0x04; - - if (pSMB->hdr.Flags2 & SMBFLG2_UNICODE) { - name_len = - cifsConvertToUTF16((__le16 *) pSMB->OldFileName, from_name, - PATH_MAX, cifs_sb->local_nls, remap); - name_len++; /* trailing null */ - name_len *= 2; - - /* protocol specifies ASCII buffer format (0x04) for unicode */ - pSMB->OldFileName[name_len] = 0x04; - pSMB->OldFileName[name_len + 1] = 0x00; /* pad */ - name_len2 = - cifsConvertToUTF16((__le16 *)&pSMB->OldFileName[name_len+2], - to_name, PATH_MAX, cifs_sb->local_nls, - remap); - name_len2 += 1 /* trailing null */ + 1 /* Signature word */ ; - name_len2 *= 2; /* convert to bytes */ - } else { - name_len = copy_path_name(pSMB->OldFileName, from_name); - pSMB->OldFileName[name_len] = 0x04; /* 2nd buffer format */ - name_len2 = copy_path_name(pSMB->OldFileName+name_len+1, to_name); - name_len2++; /* signature byte */ - } - - count = 1 /* string type byte */ + name_len + name_len2; - inc_rfc1001_len(pSMB, count); - pSMB->ByteCount = cpu_to_le16(count); - - rc = SendReceive(xid, tcon->ses, (struct smb_hdr *) pSMB, - (struct smb_hdr *) pSMBr, &bytes_returned, 0); - cifs_stats_inc(&tcon->stats.cifs_stats.num_hardlinks); - if (rc) - cifs_dbg(FYI, "Send error in hard link (NT rename) = %d\n", rc); - - cifs_buf_release(pSMB); - if (rc == -EAGAIN) - goto winCreateHardLinkRetry; - - return rc; -} - -int -CIFSSMBUnixQuerySymLink(const unsigned int xid, struct cifs_tcon *tcon, - const unsigned char *searchName, char **symlinkinfo, - const struct nls_table *nls_codepage, int remap) -{ -/* SMB_QUERY_FILE_UNIX_LINK */ - TRANSACTION2_QPI_REQ *pSMB = NULL; - TRANSACTION2_QPI_RSP *pSMBr = NULL; - int rc = 0; - int bytes_returned; - int name_len; - __u16 params, byte_count; - char *data_start; - - cifs_dbg(FYI, "In QPathSymLinkInfo (Unix) for path %s\n", searchName); - -querySymLinkRetry: - rc = smb_init(SMB_COM_TRANSACTION2, 15, tcon, (void **) &pSMB, - (void **) &pSMBr); - if (rc) - return rc; - - if (pSMB->hdr.Flags2 & SMBFLG2_UNICODE) { - name_len = - cifsConvertToUTF16((__le16 *) pSMB->FileName, - searchName, PATH_MAX, nls_codepage, - remap); - name_len++; /* trailing null */ - name_len *= 2; - } else { - name_len = copy_path_name(pSMB->FileName, searchName); - } - - params = 2 /* level */ + 4 /* rsrvd */ + name_len /* incl null */ ; - pSMB->TotalDataCount = 0; - pSMB->MaxParameterCount = cpu_to_le16(2); - pSMB->MaxDataCount = cpu_to_le16(CIFSMaxBufSize); - pSMB->MaxSetupCount = 0; - pSMB->Reserved = 0; - pSMB->Flags = 0; - pSMB->Timeout = 0; - pSMB->Reserved2 = 0; - pSMB->ParameterOffset = cpu_to_le16(offsetof( - struct smb_com_transaction2_qpi_req, InformationLevel) - 4); - pSMB->DataCount = 0; - pSMB->DataOffset = 0; - pSMB->SetupCount = 1; - pSMB->Reserved3 = 0; - pSMB->SubCommand = cpu_to_le16(TRANS2_QUERY_PATH_INFORMATION); - byte_count = params + 1 /* pad */ ; - pSMB->TotalParameterCount = cpu_to_le16(params); - pSMB->ParameterCount = pSMB->TotalParameterCount; - pSMB->InformationLevel = cpu_to_le16(SMB_QUERY_FILE_UNIX_LINK); - pSMB->Reserved4 = 0; - inc_rfc1001_len(pSMB, 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) { - cifs_dbg(FYI, "Send error in QuerySymLinkInfo = %d\n", rc); - } else { - /* decode response */ - - rc = validate_t2((struct smb_t2_rsp *)pSMBr); - /* BB also check enough total bytes returned */ - if (rc || get_bcc(&pSMBr->hdr) < 2) - rc = -EIO; - else { - bool is_unicode; - u16 count = le16_to_cpu(pSMBr->t2.DataCount); - - data_start = ((char *) &pSMBr->hdr.Protocol) + - le16_to_cpu(pSMBr->t2.DataOffset); - - if (pSMBr->hdr.Flags2 & SMBFLG2_UNICODE) - is_unicode = true; - else - is_unicode = false; - - /* BB FIXME investigate remapping reserved chars here */ - *symlinkinfo = cifs_strndup_from_utf16(data_start, - count, is_unicode, nls_codepage); - if (!*symlinkinfo) - rc = -ENOMEM; - } - } - cifs_buf_release(pSMB); - if (rc == -EAGAIN) - goto querySymLinkRetry; - return rc; -} - -/* - * Recent Windows versions now create symlinks more frequently - * and they use the "reparse point" mechanism below. We can of course - * do symlinks nicely to Samba and other servers which support the - * CIFS Unix Extensions and we can also do SFU symlinks and "client only" - * "MF" symlinks optionally, but for recent Windows we really need to - * reenable the code below and fix the cifs_symlink callers to handle this. - * In the interim this code has been moved to its own config option so - * it is not compiled in by default until callers fixed up and more tested. - */ -int -CIFSSMBQuerySymLink(const unsigned int xid, struct cifs_tcon *tcon, - __u16 fid, char **symlinkinfo, - const struct nls_table *nls_codepage) -{ - int rc = 0; - int bytes_returned; - struct smb_com_transaction_ioctl_req *pSMB; - struct smb_com_transaction_ioctl_rsp *pSMBr; - bool is_unicode; - unsigned int sub_len; - char *sub_start; - struct reparse_symlink_data *reparse_buf; - struct reparse_posix_data *posix_buf; - __u32 data_offset, data_count; - char *end_of_smb; - - cifs_dbg(FYI, "In Windows reparse style QueryLink for fid %u\n", fid); - rc = smb_init(SMB_COM_NT_TRANSACT, 23, tcon, (void **) &pSMB, - (void **) &pSMBr); - if (rc) - return rc; - - pSMB->TotalParameterCount = 0 ; - pSMB->TotalDataCount = 0; - pSMB->MaxParameterCount = cpu_to_le32(2); - /* BB find exact data count max from sess structure BB */ - pSMB->MaxDataCount = cpu_to_le32(CIFSMaxBufSize & 0xFFFFFF00); - pSMB->MaxSetupCount = 4; - pSMB->Reserved = 0; - pSMB->ParameterOffset = 0; - pSMB->DataCount = 0; - pSMB->DataOffset = 0; - pSMB->SetupCount = 4; - pSMB->SubCommand = cpu_to_le16(NT_TRANSACT_IOCTL); - pSMB->ParameterCount = pSMB->TotalParameterCount; - pSMB->FunctionCode = cpu_to_le32(FSCTL_GET_REPARSE_POINT); - pSMB->IsFsctl = 1; /* FSCTL */ - pSMB->IsRootFlag = 0; - pSMB->Fid = fid; /* file handle always le */ - pSMB->ByteCount = 0; - - rc = SendReceive(xid, tcon->ses, (struct smb_hdr *) pSMB, - (struct smb_hdr *) pSMBr, &bytes_returned, 0); - if (rc) { - cifs_dbg(FYI, "Send error in QueryReparseLinkInfo = %d\n", rc); - goto qreparse_out; - } - - data_offset = le32_to_cpu(pSMBr->DataOffset); - data_count = le32_to_cpu(pSMBr->DataCount); - if (get_bcc(&pSMBr->hdr) < 2 || data_offset > 512) { - /* BB also check enough total bytes returned */ - rc = -EIO; /* bad smb */ - goto qreparse_out; - } - if (!data_count || (data_count > 2048)) { - rc = -EIO; - cifs_dbg(FYI, "Invalid return data count on get reparse info ioctl\n"); - goto qreparse_out; - } - end_of_smb = 2 + get_bcc(&pSMBr->hdr) + (char *)&pSMBr->ByteCount; - reparse_buf = (struct reparse_symlink_data *) - ((char *)&pSMBr->hdr.Protocol + data_offset); - if ((char *)reparse_buf >= end_of_smb) { - rc = -EIO; - goto qreparse_out; - } - if (reparse_buf->ReparseTag == cpu_to_le32(IO_REPARSE_TAG_NFS)) { - cifs_dbg(FYI, "NFS style reparse tag\n"); - posix_buf = (struct reparse_posix_data *)reparse_buf; - - if (posix_buf->InodeType != cpu_to_le64(NFS_SPECFILE_LNK)) { - cifs_dbg(FYI, "unsupported file type 0x%llx\n", - le64_to_cpu(posix_buf->InodeType)); - rc = -EOPNOTSUPP; - goto qreparse_out; - } - is_unicode = true; - sub_len = le16_to_cpu(reparse_buf->ReparseDataLength); - if (posix_buf->PathBuffer + sub_len > end_of_smb) { - cifs_dbg(FYI, "reparse buf beyond SMB\n"); - rc = -EIO; - goto qreparse_out; - } - *symlinkinfo = cifs_strndup_from_utf16(posix_buf->PathBuffer, - sub_len, is_unicode, nls_codepage); - goto qreparse_out; - } else if (reparse_buf->ReparseTag != - cpu_to_le32(IO_REPARSE_TAG_SYMLINK)) { - rc = -EOPNOTSUPP; - goto qreparse_out; - } - - /* Reparse tag is NTFS symlink */ - sub_start = le16_to_cpu(reparse_buf->SubstituteNameOffset) + - reparse_buf->PathBuffer; - sub_len = le16_to_cpu(reparse_buf->SubstituteNameLength); - if (sub_start + sub_len > end_of_smb) { - cifs_dbg(FYI, "reparse buf beyond SMB\n"); - rc = -EIO; - goto qreparse_out; - } - if (pSMBr->hdr.Flags2 & SMBFLG2_UNICODE) - is_unicode = true; - else - is_unicode = false; - - /* BB FIXME investigate remapping reserved chars here */ - *symlinkinfo = cifs_strndup_from_utf16(sub_start, sub_len, is_unicode, - nls_codepage); - if (!*symlinkinfo) - rc = -ENOMEM; -qreparse_out: - cifs_buf_release(pSMB); - - /* - * Note: On -EAGAIN error only caller can retry on handle based calls - * since file handle passed in no longer valid. - */ - return rc; -} - -int -CIFSSMB_set_compression(const unsigned int xid, struct cifs_tcon *tcon, - __u16 fid) -{ - int rc = 0; - int bytes_returned; - struct smb_com_transaction_compr_ioctl_req *pSMB; - struct smb_com_transaction_ioctl_rsp *pSMBr; - - cifs_dbg(FYI, "Set compression for %u\n", fid); - rc = smb_init(SMB_COM_NT_TRANSACT, 23, tcon, (void **) &pSMB, - (void **) &pSMBr); - if (rc) - return rc; - - pSMB->compression_state = cpu_to_le16(COMPRESSION_FORMAT_DEFAULT); - - pSMB->TotalParameterCount = 0; - pSMB->TotalDataCount = cpu_to_le32(2); - pSMB->MaxParameterCount = 0; - pSMB->MaxDataCount = 0; - pSMB->MaxSetupCount = 4; - pSMB->Reserved = 0; - pSMB->ParameterOffset = 0; - pSMB->DataCount = cpu_to_le32(2); - pSMB->DataOffset = - cpu_to_le32(offsetof(struct smb_com_transaction_compr_ioctl_req, - compression_state) - 4); /* 84 */ - pSMB->SetupCount = 4; - pSMB->SubCommand = cpu_to_le16(NT_TRANSACT_IOCTL); - pSMB->ParameterCount = 0; - pSMB->FunctionCode = cpu_to_le32(FSCTL_SET_COMPRESSION); - pSMB->IsFsctl = 1; /* FSCTL */ - pSMB->IsRootFlag = 0; - pSMB->Fid = fid; /* file handle always le */ - /* 3 byte pad, followed by 2 byte compress state */ - pSMB->ByteCount = cpu_to_le16(5); - inc_rfc1001_len(pSMB, 5); - - rc = SendReceive(xid, tcon->ses, (struct smb_hdr *) pSMB, - (struct smb_hdr *) pSMBr, &bytes_returned, 0); - if (rc) - cifs_dbg(FYI, "Send error in SetCompression = %d\n", rc); - - cifs_buf_release(pSMB); - - /* - * Note: On -EAGAIN error only caller can retry on handle based calls - * since file handle passed in no longer valid. - */ - return rc; -} - - -#ifdef CONFIG_CIFS_POSIX - -/*Convert an Access Control Entry from wire format to local POSIX xattr format*/ -static void cifs_convert_ace(struct posix_acl_xattr_entry *ace, - struct cifs_posix_ace *cifs_ace) -{ - /* u8 cifs fields do not need le conversion */ - ace->e_perm = cpu_to_le16(cifs_ace->cifs_e_perm); - ace->e_tag = cpu_to_le16(cifs_ace->cifs_e_tag); - ace->e_id = cpu_to_le32(le64_to_cpu(cifs_ace->cifs_uid)); -/* - cifs_dbg(FYI, "perm %d tag %d id %d\n", - ace->e_perm, ace->e_tag, ace->e_id); -*/ - - return; -} - -/* Convert ACL from CIFS POSIX wire format to local Linux POSIX ACL xattr */ -static int cifs_copy_posix_acl(char *trgt, char *src, const int buflen, - const int acl_type, const int size_of_data_area) -{ - int size = 0; - int i; - __u16 count; - struct cifs_posix_ace *pACE; - struct cifs_posix_acl *cifs_acl = (struct cifs_posix_acl *)src; - struct posix_acl_xattr_header *local_acl = (void *)trgt; - - if (le16_to_cpu(cifs_acl->version) != CIFS_ACL_VERSION) - return -EOPNOTSUPP; - - if (acl_type == ACL_TYPE_ACCESS) { - count = le16_to_cpu(cifs_acl->access_entry_count); - pACE = &cifs_acl->ace_array[0]; - size = sizeof(struct cifs_posix_acl); - size += sizeof(struct cifs_posix_ace) * count; - /* check if we would go beyond end of SMB */ - if (size_of_data_area < size) { - cifs_dbg(FYI, "bad CIFS POSIX ACL size %d vs. %d\n", - size_of_data_area, size); - return -EINVAL; - } - } else if (acl_type == ACL_TYPE_DEFAULT) { - count = le16_to_cpu(cifs_acl->access_entry_count); - size = sizeof(struct cifs_posix_acl); - size += sizeof(struct cifs_posix_ace) * count; -/* skip past access ACEs to get to default ACEs */ - pACE = &cifs_acl->ace_array[count]; - count = le16_to_cpu(cifs_acl->default_entry_count); - size += sizeof(struct cifs_posix_ace) * count; - /* check if we would go beyond end of SMB */ - if (size_of_data_area < size) - return -EINVAL; - } else { - /* illegal type */ - return -EINVAL; - } - - size = posix_acl_xattr_size(count); - if ((buflen == 0) || (local_acl == NULL)) { - /* used to query ACL EA size */ - } else if (size > buflen) { - return -ERANGE; - } else /* buffer big enough */ { - struct posix_acl_xattr_entry *ace = (void *)(local_acl + 1); - - local_acl->a_version = cpu_to_le32(POSIX_ACL_XATTR_VERSION); - for (i = 0; i < count ; i++) { - cifs_convert_ace(&ace[i], pACE); - pACE++; - } - } - return size; -} - -static void convert_ace_to_cifs_ace(struct cifs_posix_ace *cifs_ace, - const struct posix_acl_xattr_entry *local_ace) -{ - cifs_ace->cifs_e_perm = le16_to_cpu(local_ace->e_perm); - cifs_ace->cifs_e_tag = le16_to_cpu(local_ace->e_tag); - /* BB is there a better way to handle the large uid? */ - if (local_ace->e_id == cpu_to_le32(-1)) { - /* Probably no need to le convert -1 on any arch but can not hurt */ - cifs_ace->cifs_uid = cpu_to_le64(-1); - } else - cifs_ace->cifs_uid = cpu_to_le64(le32_to_cpu(local_ace->e_id)); -/* - cifs_dbg(FYI, "perm %d tag %d id %d\n", - ace->e_perm, ace->e_tag, ace->e_id); -*/ -} - -/* Convert ACL from local Linux POSIX xattr to CIFS POSIX ACL wire format */ -static __u16 ACL_to_cifs_posix(char *parm_data, const char *pACL, - const int buflen, const int acl_type) -{ - __u16 rc = 0; - struct cifs_posix_acl *cifs_acl = (struct cifs_posix_acl *)parm_data; - struct posix_acl_xattr_header *local_acl = (void *)pACL; - struct posix_acl_xattr_entry *ace = (void *)(local_acl + 1); - int count; - int i; - - if ((buflen == 0) || (pACL == NULL) || (cifs_acl == NULL)) - return 0; - - count = posix_acl_xattr_count((size_t)buflen); - cifs_dbg(FYI, "setting acl with %d entries from buf of length %d and version of %d\n", - count, buflen, le32_to_cpu(local_acl->a_version)); - if (le32_to_cpu(local_acl->a_version) != 2) { - cifs_dbg(FYI, "unknown POSIX ACL version %d\n", - le32_to_cpu(local_acl->a_version)); - return 0; - } - cifs_acl->version = cpu_to_le16(1); - if (acl_type == ACL_TYPE_ACCESS) { - cifs_acl->access_entry_count = cpu_to_le16(count); - cifs_acl->default_entry_count = cpu_to_le16(0xFFFF); - } else if (acl_type == ACL_TYPE_DEFAULT) { - cifs_acl->default_entry_count = cpu_to_le16(count); - cifs_acl->access_entry_count = cpu_to_le16(0xFFFF); - } else { - cifs_dbg(FYI, "unknown ACL type %d\n", acl_type); - return 0; - } - for (i = 0; i < count; i++) - convert_ace_to_cifs_ace(&cifs_acl->ace_array[i], &ace[i]); - if (rc == 0) { - rc = (__u16)(count * sizeof(struct cifs_posix_ace)); - rc += sizeof(struct cifs_posix_acl); - /* BB add check to make sure ACL does not overflow SMB */ - } - return rc; -} - -int -CIFSSMBGetPosixACL(const unsigned int xid, struct cifs_tcon *tcon, - const unsigned char *searchName, - char *acl_inf, const int buflen, const int acl_type, - const struct nls_table *nls_codepage, int remap) -{ -/* SMB_QUERY_POSIX_ACL */ - TRANSACTION2_QPI_REQ *pSMB = NULL; - TRANSACTION2_QPI_RSP *pSMBr = NULL; - int rc = 0; - int bytes_returned; - int name_len; - __u16 params, byte_count; - - cifs_dbg(FYI, "In GetPosixACL (Unix) for path %s\n", searchName); - -queryAclRetry: - rc = smb_init(SMB_COM_TRANSACTION2, 15, tcon, (void **) &pSMB, - (void **) &pSMBr); - if (rc) - return rc; - - if (pSMB->hdr.Flags2 & SMBFLG2_UNICODE) { - name_len = - cifsConvertToUTF16((__le16 *) pSMB->FileName, - searchName, PATH_MAX, nls_codepage, - remap); - name_len++; /* trailing null */ - name_len *= 2; - pSMB->FileName[name_len] = 0; - pSMB->FileName[name_len+1] = 0; - } else { - name_len = copy_path_name(pSMB->FileName, searchName); - } - - params = 2 /* level */ + 4 /* rsrvd */ + name_len /* incl null */ ; - pSMB->TotalDataCount = 0; - pSMB->MaxParameterCount = cpu_to_le16(2); - /* BB find exact max data count below from sess structure BB */ - pSMB->MaxDataCount = cpu_to_le16(4000); - pSMB->MaxSetupCount = 0; - pSMB->Reserved = 0; - pSMB->Flags = 0; - pSMB->Timeout = 0; - pSMB->Reserved2 = 0; - pSMB->ParameterOffset = cpu_to_le16( - offsetof(struct smb_com_transaction2_qpi_req, - InformationLevel) - 4); - pSMB->DataCount = 0; - pSMB->DataOffset = 0; - pSMB->SetupCount = 1; - pSMB->Reserved3 = 0; - pSMB->SubCommand = cpu_to_le16(TRANS2_QUERY_PATH_INFORMATION); - byte_count = params + 1 /* pad */ ; - pSMB->TotalParameterCount = cpu_to_le16(params); - pSMB->ParameterCount = pSMB->TotalParameterCount; - pSMB->InformationLevel = cpu_to_le16(SMB_QUERY_POSIX_ACL); - pSMB->Reserved4 = 0; - inc_rfc1001_len(pSMB, 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); - cifs_stats_inc(&tcon->stats.cifs_stats.num_acl_get); - if (rc) { - cifs_dbg(FYI, "Send error in Query POSIX ACL = %d\n", rc); - } else { - /* decode response */ - - rc = validate_t2((struct smb_t2_rsp *)pSMBr); - /* BB also check enough total bytes returned */ - if (rc || get_bcc(&pSMBr->hdr) < 2) - rc = -EIO; /* bad smb */ - else { - __u16 data_offset = le16_to_cpu(pSMBr->t2.DataOffset); - __u16 count = le16_to_cpu(pSMBr->t2.DataCount); - rc = cifs_copy_posix_acl(acl_inf, - (char *)&pSMBr->hdr.Protocol+data_offset, - buflen, acl_type, count); - } - } - cifs_buf_release(pSMB); - if (rc == -EAGAIN) - goto queryAclRetry; - return rc; -} - -int -CIFSSMBSetPosixACL(const unsigned int xid, struct cifs_tcon *tcon, - const unsigned char *fileName, - const char *local_acl, const int buflen, - const int acl_type, - const struct nls_table *nls_codepage, int remap) -{ - struct smb_com_transaction2_spi_req *pSMB = NULL; - struct smb_com_transaction2_spi_rsp *pSMBr = NULL; - char *parm_data; - int name_len; - int rc = 0; - int bytes_returned = 0; - __u16 params, byte_count, data_count, param_offset, offset; - - cifs_dbg(FYI, "In SetPosixACL (Unix) for path %s\n", fileName); -setAclRetry: - rc = smb_init(SMB_COM_TRANSACTION2, 15, tcon, (void **) &pSMB, - (void **) &pSMBr); - if (rc) - return rc; - if (pSMB->hdr.Flags2 & SMBFLG2_UNICODE) { - name_len = - cifsConvertToUTF16((__le16 *) pSMB->FileName, fileName, - PATH_MAX, nls_codepage, remap); - name_len++; /* trailing null */ - name_len *= 2; - } else { - name_len = copy_path_name(pSMB->FileName, fileName); - } - params = 6 + name_len; - pSMB->MaxParameterCount = cpu_to_le16(2); - /* BB find max SMB size from sess */ - pSMB->MaxDataCount = cpu_to_le16(1000); - pSMB->MaxSetupCount = 0; - pSMB->Reserved = 0; - pSMB->Flags = 0; - pSMB->Timeout = 0; - pSMB->Reserved2 = 0; - param_offset = offsetof(struct smb_com_transaction2_spi_req, - InformationLevel) - 4; - offset = param_offset + params; - parm_data = ((char *) &pSMB->hdr.Protocol) + offset; - pSMB->ParameterOffset = cpu_to_le16(param_offset); - - /* convert to on the wire format for POSIX ACL */ - data_count = ACL_to_cifs_posix(parm_data, local_acl, buflen, acl_type); - - if (data_count == 0) { - rc = -EOPNOTSUPP; - goto setACLerrorExit; - } - pSMB->DataOffset = cpu_to_le16(offset); - pSMB->SetupCount = 1; - pSMB->Reserved3 = 0; - pSMB->SubCommand = cpu_to_le16(TRANS2_SET_PATH_INFORMATION); - pSMB->InformationLevel = cpu_to_le16(SMB_SET_POSIX_ACL); - byte_count = 3 /* pad */ + params + data_count; - pSMB->DataCount = cpu_to_le16(data_count); - pSMB->TotalDataCount = pSMB->DataCount; - pSMB->ParameterCount = cpu_to_le16(params); - pSMB->TotalParameterCount = pSMB->ParameterCount; - pSMB->Reserved4 = 0; - inc_rfc1001_len(pSMB, 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) - cifs_dbg(FYI, "Set POSIX ACL returned %d\n", rc); - -setACLerrorExit: - cifs_buf_release(pSMB); - if (rc == -EAGAIN) - goto setAclRetry; - return rc; -} - -int -CIFSGetExtAttr(const unsigned int xid, struct cifs_tcon *tcon, - const int netfid, __u64 *pExtAttrBits, __u64 *pMask) -{ - int rc = 0; - struct smb_t2_qfi_req *pSMB = NULL; - struct smb_t2_qfi_rsp *pSMBr = NULL; - int bytes_returned; - __u16 params, byte_count; - - cifs_dbg(FYI, "In GetExtAttr\n"); - if (tcon == NULL) - return -ENODEV; - -GetExtAttrRetry: - rc = smb_init(SMB_COM_TRANSACTION2, 15, tcon, (void **) &pSMB, - (void **) &pSMBr); - if (rc) - return rc; - - params = 2 /* level */ + 2 /* fid */; - pSMB->t2.TotalDataCount = 0; - pSMB->t2.MaxParameterCount = cpu_to_le16(4); - /* BB find exact max data count below from sess structure BB */ - pSMB->t2.MaxDataCount = cpu_to_le16(4000); - pSMB->t2.MaxSetupCount = 0; - pSMB->t2.Reserved = 0; - pSMB->t2.Flags = 0; - pSMB->t2.Timeout = 0; - pSMB->t2.Reserved2 = 0; - pSMB->t2.ParameterOffset = cpu_to_le16(offsetof(struct smb_t2_qfi_req, - Fid) - 4); - pSMB->t2.DataCount = 0; - pSMB->t2.DataOffset = 0; - pSMB->t2.SetupCount = 1; - pSMB->t2.Reserved3 = 0; - pSMB->t2.SubCommand = cpu_to_le16(TRANS2_QUERY_FILE_INFORMATION); - byte_count = params + 1 /* pad */ ; - pSMB->t2.TotalParameterCount = cpu_to_le16(params); - pSMB->t2.ParameterCount = pSMB->t2.TotalParameterCount; - pSMB->InformationLevel = cpu_to_le16(SMB_QUERY_ATTR_FLAGS); - pSMB->Pad = 0; - pSMB->Fid = netfid; - inc_rfc1001_len(pSMB, byte_count); - pSMB->t2.ByteCount = cpu_to_le16(byte_count); - - rc = SendReceive(xid, tcon->ses, (struct smb_hdr *) pSMB, - (struct smb_hdr *) pSMBr, &bytes_returned, 0); - if (rc) { - cifs_dbg(FYI, "error %d in GetExtAttr\n", rc); - } else { - /* decode response */ - rc = validate_t2((struct smb_t2_rsp *)pSMBr); - /* BB also check enough total bytes returned */ - if (rc || get_bcc(&pSMBr->hdr) < 2) - /* If rc should we check for EOPNOSUPP and - disable the srvino flag? or in caller? */ - rc = -EIO; /* bad smb */ - else { - __u16 data_offset = le16_to_cpu(pSMBr->t2.DataOffset); - __u16 count = le16_to_cpu(pSMBr->t2.DataCount); - struct file_chattr_info *pfinfo; - - if (count != 16) { - cifs_dbg(FYI, "Invalid size ret in GetExtAttr\n"); - rc = -EIO; - goto GetExtAttrOut; - } - pfinfo = (struct file_chattr_info *) - (data_offset + (char *) &pSMBr->hdr.Protocol); - *pExtAttrBits = le64_to_cpu(pfinfo->mode); - *pMask = le64_to_cpu(pfinfo->mask); - } - } -GetExtAttrOut: - cifs_buf_release(pSMB); - if (rc == -EAGAIN) - goto GetExtAttrRetry; - return rc; -} - -#endif /* CONFIG_POSIX */ - -/* - * Initialize NT TRANSACT SMB into small smb request buffer. This assumes that - * all NT TRANSACTS that we init here have total parm and data under about 400 - * bytes (to fit in small cifs buffer size), which is the case so far, it - * easily fits. NB: Setup words themselves and ByteCount MaxSetupCount (size of - * returned setup area) and MaxParameterCount (returned parms size) must be set - * by caller - */ -static int -smb_init_nttransact(const __u16 sub_command, const int setup_count, - const int parm_len, struct cifs_tcon *tcon, - void **ret_buf) -{ - int rc; - __u32 temp_offset; - struct smb_com_ntransact_req *pSMB; - - rc = small_smb_init(SMB_COM_NT_TRANSACT, 19 + setup_count, tcon, - (void **)&pSMB); - if (rc) - return rc; - *ret_buf = (void *)pSMB; - pSMB->Reserved = 0; - pSMB->TotalParameterCount = cpu_to_le32(parm_len); - pSMB->TotalDataCount = 0; - pSMB->MaxDataCount = cpu_to_le32(CIFSMaxBufSize & 0xFFFFFF00); - pSMB->ParameterCount = pSMB->TotalParameterCount; - pSMB->DataCount = pSMB->TotalDataCount; - temp_offset = offsetof(struct smb_com_ntransact_req, Parms) + - (setup_count * 2) - 4 /* for rfc1001 length itself */; - pSMB->ParameterOffset = cpu_to_le32(temp_offset); - pSMB->DataOffset = cpu_to_le32(temp_offset + parm_len); - pSMB->SetupCount = setup_count; /* no need to le convert byte fields */ - pSMB->SubCommand = cpu_to_le16(sub_command); - return 0; -} - -static int -validate_ntransact(char *buf, char **ppparm, char **ppdata, - __u32 *pparmlen, __u32 *pdatalen) -{ - char *end_of_smb; - __u32 data_count, data_offset, parm_count, parm_offset; - struct smb_com_ntransact_rsp *pSMBr; - u16 bcc; - - *pdatalen = 0; - *pparmlen = 0; - - if (buf == NULL) - return -EINVAL; - - pSMBr = (struct smb_com_ntransact_rsp *)buf; - - bcc = get_bcc(&pSMBr->hdr); - end_of_smb = 2 /* sizeof byte count */ + bcc + - (char *)&pSMBr->ByteCount; - - data_offset = le32_to_cpu(pSMBr->DataOffset); - data_count = le32_to_cpu(pSMBr->DataCount); - parm_offset = le32_to_cpu(pSMBr->ParameterOffset); - parm_count = le32_to_cpu(pSMBr->ParameterCount); - - *ppparm = (char *)&pSMBr->hdr.Protocol + parm_offset; - *ppdata = (char *)&pSMBr->hdr.Protocol + data_offset; - - /* should we also check that parm and data areas do not overlap? */ - if (*ppparm > end_of_smb) { - cifs_dbg(FYI, "parms start after end of smb\n"); - return -EINVAL; - } else if (parm_count + *ppparm > end_of_smb) { - cifs_dbg(FYI, "parm end after end of smb\n"); - return -EINVAL; - } else if (*ppdata > end_of_smb) { - cifs_dbg(FYI, "data starts after end of smb\n"); - return -EINVAL; - } else if (data_count + *ppdata > end_of_smb) { - cifs_dbg(FYI, "data %p + count %d (%p) past smb end %p start %p\n", - *ppdata, data_count, (data_count + *ppdata), - end_of_smb, pSMBr); - return -EINVAL; - } else if (parm_count + data_count > bcc) { - cifs_dbg(FYI, "parm count and data count larger than SMB\n"); - return -EINVAL; - } - *pdatalen = data_count; - *pparmlen = parm_count; - return 0; -} - -/* Get Security Descriptor (by handle) from remote server for a file or dir */ -int -CIFSSMBGetCIFSACL(const unsigned int xid, struct cifs_tcon *tcon, __u16 fid, - struct cifs_ntsd **acl_inf, __u32 *pbuflen) -{ - int rc = 0; - int buf_type = 0; - QUERY_SEC_DESC_REQ *pSMB; - struct kvec iov[1]; - struct kvec rsp_iov; - - cifs_dbg(FYI, "GetCifsACL\n"); - - *pbuflen = 0; - *acl_inf = NULL; - - rc = smb_init_nttransact(NT_TRANSACT_QUERY_SECURITY_DESC, 0, - 8 /* parm len */, tcon, (void **) &pSMB); - if (rc) - return rc; - - pSMB->MaxParameterCount = cpu_to_le32(4); - /* BB TEST with big acls that might need to be e.g. larger than 16K */ - pSMB->MaxSetupCount = 0; - pSMB->Fid = fid; /* file handle always le */ - pSMB->AclFlags = cpu_to_le32(CIFS_ACL_OWNER | CIFS_ACL_GROUP | - CIFS_ACL_DACL); - pSMB->ByteCount = cpu_to_le16(11); /* 3 bytes pad + 8 bytes parm */ - inc_rfc1001_len(pSMB, 11); - iov[0].iov_base = (char *)pSMB; - iov[0].iov_len = be32_to_cpu(pSMB->hdr.smb_buf_length) + 4; - - rc = SendReceive2(xid, tcon->ses, iov, 1 /* num iovec */, &buf_type, - 0, &rsp_iov); - cifs_small_buf_release(pSMB); - cifs_stats_inc(&tcon->stats.cifs_stats.num_acl_get); - if (rc) { - cifs_dbg(FYI, "Send error in QuerySecDesc = %d\n", rc); - } else { /* decode response */ - __le32 *parm; - __u32 parm_len; - __u32 acl_len; - struct smb_com_ntransact_rsp *pSMBr; - char *pdata; - -/* validate_nttransact */ - rc = validate_ntransact(rsp_iov.iov_base, (char **)&parm, - &pdata, &parm_len, pbuflen); - if (rc) - goto qsec_out; - pSMBr = (struct smb_com_ntransact_rsp *)rsp_iov.iov_base; - - cifs_dbg(FYI, "smb %p parm %p data %p\n", - pSMBr, parm, *acl_inf); - - if (le32_to_cpu(pSMBr->ParameterCount) != 4) { - rc = -EIO; /* bad smb */ - *pbuflen = 0; - goto qsec_out; - } - -/* BB check that data area is minimum length and as big as acl_len */ - - acl_len = le32_to_cpu(*parm); - if (acl_len != *pbuflen) { - cifs_dbg(VFS, "acl length %d does not match %d\n", - acl_len, *pbuflen); - if (*pbuflen > acl_len) - *pbuflen = acl_len; - } - - /* check if buffer is big enough for the acl - header followed by the smallest SID */ - if ((*pbuflen < sizeof(struct cifs_ntsd) + 8) || - (*pbuflen >= 64 * 1024)) { - cifs_dbg(VFS, "bad acl length %d\n", *pbuflen); - rc = -EINVAL; - *pbuflen = 0; - } else { - *acl_inf = kmemdup(pdata, *pbuflen, GFP_KERNEL); - if (*acl_inf == NULL) { - *pbuflen = 0; - rc = -ENOMEM; - } - } - } -qsec_out: - free_rsp_buf(buf_type, rsp_iov.iov_base); - return rc; -} - -int -CIFSSMBSetCIFSACL(const unsigned int xid, struct cifs_tcon *tcon, __u16 fid, - struct cifs_ntsd *pntsd, __u32 acllen, int aclflag) -{ - __u16 byte_count, param_count, data_count, param_offset, data_offset; - int rc = 0; - int bytes_returned = 0; - SET_SEC_DESC_REQ *pSMB = NULL; - void *pSMBr; - -setCifsAclRetry: - rc = smb_init(SMB_COM_NT_TRANSACT, 19, tcon, (void **) &pSMB, &pSMBr); - if (rc) - return rc; - - pSMB->MaxSetupCount = 0; - pSMB->Reserved = 0; - - param_count = 8; - param_offset = offsetof(struct smb_com_transaction_ssec_req, Fid) - 4; - data_count = acllen; - data_offset = param_offset + param_count; - byte_count = 3 /* pad */ + param_count; - - pSMB->DataCount = cpu_to_le32(data_count); - pSMB->TotalDataCount = pSMB->DataCount; - pSMB->MaxParameterCount = cpu_to_le32(4); - pSMB->MaxDataCount = cpu_to_le32(16384); - pSMB->ParameterCount = cpu_to_le32(param_count); - pSMB->ParameterOffset = cpu_to_le32(param_offset); - pSMB->TotalParameterCount = pSMB->ParameterCount; - pSMB->DataOffset = cpu_to_le32(data_offset); - pSMB->SetupCount = 0; - pSMB->SubCommand = cpu_to_le16(NT_TRANSACT_SET_SECURITY_DESC); - pSMB->ByteCount = cpu_to_le16(byte_count+data_count); - - pSMB->Fid = fid; /* file handle always le */ - pSMB->Reserved2 = 0; - pSMB->AclFlags = cpu_to_le32(aclflag); - - if (pntsd && acllen) { - memcpy((char *)pSMBr + offsetof(struct smb_hdr, Protocol) + - data_offset, pntsd, acllen); - inc_rfc1001_len(pSMB, byte_count + data_count); - } else - inc_rfc1001_len(pSMB, byte_count); - - rc = SendReceive(xid, tcon->ses, (struct smb_hdr *) pSMB, - (struct smb_hdr *) pSMBr, &bytes_returned, 0); - - cifs_dbg(FYI, "SetCIFSACL bytes_returned: %d, rc: %d\n", - bytes_returned, rc); - if (rc) - cifs_dbg(FYI, "Set CIFS ACL returned %d\n", rc); - cifs_buf_release(pSMB); - - if (rc == -EAGAIN) - goto setCifsAclRetry; - - return (rc); -} - - -/* Legacy Query Path Information call for lookup to old servers such - as Win9x/WinME */ -int -SMBQueryInformation(const unsigned int xid, struct cifs_tcon *tcon, - const char *search_name, FILE_ALL_INFO *data, - 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; - - cifs_dbg(FYI, "In SMBQPath path %s\n", search_name); -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 = - cifsConvertToUTF16((__le16 *) pSMB->FileName, - search_name, PATH_MAX, nls_codepage, - remap); - name_len++; /* trailing null */ - name_len *= 2; - } else { - name_len = copy_path_name(pSMB->FileName, search_name); - } - pSMB->BufferFormat = 0x04; - name_len++; /* account for buffer type byte */ - inc_rfc1001_len(pSMB, (__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) { - cifs_dbg(FYI, "Send error in QueryInfo = %d\n", rc); - } else if (data) { - struct timespec64 ts; - __u32 time = le32_to_cpu(pSMBr->last_write_time); - - /* decode response */ - /* BB FIXME - add time zone adjustment BB */ - memset(data, 0, sizeof(FILE_ALL_INFO)); - ts.tv_nsec = 0; - ts.tv_sec = time; - /* decode time fields */ - data->ChangeTime = cpu_to_le64(cifs_UnixTimeToNT(ts)); - data->LastWriteTime = data->ChangeTime; - data->LastAccessTime = 0; - data->AllocationSize = - cpu_to_le64(le32_to_cpu(pSMBr->size)); - data->EndOfFile = data->AllocationSize; - data->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 -CIFSSMBQFileInfo(const unsigned int xid, struct cifs_tcon *tcon, - u16 netfid, FILE_ALL_INFO *pFindData) -{ - struct smb_t2_qfi_req *pSMB = NULL; - struct smb_t2_qfi_rsp *pSMBr = NULL; - int rc = 0; - int bytes_returned; - __u16 params, byte_count; - -QFileInfoRetry: - rc = smb_init(SMB_COM_TRANSACTION2, 15, tcon, (void **) &pSMB, - (void **) &pSMBr); - if (rc) - return rc; - - params = 2 /* level */ + 2 /* fid */; - pSMB->t2.TotalDataCount = 0; - pSMB->t2.MaxParameterCount = cpu_to_le16(4); - /* BB find exact max data count below from sess structure BB */ - pSMB->t2.MaxDataCount = cpu_to_le16(CIFSMaxBufSize); - pSMB->t2.MaxSetupCount = 0; - pSMB->t2.Reserved = 0; - pSMB->t2.Flags = 0; - pSMB->t2.Timeout = 0; - pSMB->t2.Reserved2 = 0; - pSMB->t2.ParameterOffset = cpu_to_le16(offsetof(struct smb_t2_qfi_req, - Fid) - 4); - pSMB->t2.DataCount = 0; - pSMB->t2.DataOffset = 0; - pSMB->t2.SetupCount = 1; - pSMB->t2.Reserved3 = 0; - pSMB->t2.SubCommand = cpu_to_le16(TRANS2_QUERY_FILE_INFORMATION); - byte_count = params + 1 /* pad */ ; - pSMB->t2.TotalParameterCount = cpu_to_le16(params); - pSMB->t2.ParameterCount = pSMB->t2.TotalParameterCount; - pSMB->InformationLevel = cpu_to_le16(SMB_QUERY_FILE_ALL_INFO); - pSMB->Pad = 0; - pSMB->Fid = netfid; - inc_rfc1001_len(pSMB, byte_count); - pSMB->t2.ByteCount = cpu_to_le16(byte_count); - - rc = SendReceive(xid, tcon->ses, (struct smb_hdr *) pSMB, - (struct smb_hdr *) pSMBr, &bytes_returned, 0); - if (rc) { - cifs_dbg(FYI, "Send error in QFileInfo = %d\n", rc); - } else { /* decode response */ - rc = validate_t2((struct smb_t2_rsp *)pSMBr); - - if (rc) /* BB add auto retry on EOPNOTSUPP? */ - rc = -EIO; - else if (get_bcc(&pSMBr->hdr) < 40) - rc = -EIO; /* bad smb */ - else if (pFindData) { - __u16 data_offset = le16_to_cpu(pSMBr->t2.DataOffset); - memcpy((char *) pFindData, - (char *) &pSMBr->hdr.Protocol + - data_offset, sizeof(FILE_ALL_INFO)); - } else - rc = -ENOMEM; - } - cifs_buf_release(pSMB); - if (rc == -EAGAIN) - goto QFileInfoRetry; - - return rc; -} - -int -CIFSSMBQPathInfo(const unsigned int xid, struct cifs_tcon *tcon, - const char *search_name, FILE_ALL_INFO *data, - int legacy /* old style infolevel */, - const struct nls_table *nls_codepage, int remap) -{ - /* level 263 SMB_QUERY_FILE_ALL_INFO */ - TRANSACTION2_QPI_REQ *pSMB = NULL; - TRANSACTION2_QPI_RSP *pSMBr = NULL; - int rc = 0; - int bytes_returned; - int name_len; - __u16 params, byte_count; - - /* cifs_dbg(FYI, "In QPathInfo path %s\n", search_name); */ -QPathInfoRetry: - rc = smb_init(SMB_COM_TRANSACTION2, 15, tcon, (void **) &pSMB, - (void **) &pSMBr); - if (rc) - return rc; - - if (pSMB->hdr.Flags2 & SMBFLG2_UNICODE) { - name_len = - cifsConvertToUTF16((__le16 *) pSMB->FileName, search_name, - PATH_MAX, nls_codepage, remap); - name_len++; /* trailing null */ - name_len *= 2; - } else { - name_len = copy_path_name(pSMB->FileName, search_name); - } - - params = 2 /* level */ + 4 /* reserved */ + name_len /* includes NUL */; - pSMB->TotalDataCount = 0; - pSMB->MaxParameterCount = cpu_to_le16(2); - /* BB find exact max SMB PDU from sess structure BB */ - pSMB->MaxDataCount = cpu_to_le16(4000); - pSMB->MaxSetupCount = 0; - pSMB->Reserved = 0; - pSMB->Flags = 0; - pSMB->Timeout = 0; - pSMB->Reserved2 = 0; - pSMB->ParameterOffset = cpu_to_le16(offsetof( - struct smb_com_transaction2_qpi_req, InformationLevel) - 4); - pSMB->DataCount = 0; - pSMB->DataOffset = 0; - pSMB->SetupCount = 1; - pSMB->Reserved3 = 0; - pSMB->SubCommand = cpu_to_le16(TRANS2_QUERY_PATH_INFORMATION); - byte_count = params + 1 /* pad */ ; - pSMB->TotalParameterCount = cpu_to_le16(params); - pSMB->ParameterCount = pSMB->TotalParameterCount; - if (legacy) - pSMB->InformationLevel = cpu_to_le16(SMB_INFO_STANDARD); - else - pSMB->InformationLevel = cpu_to_le16(SMB_QUERY_FILE_ALL_INFO); - pSMB->Reserved4 = 0; - inc_rfc1001_len(pSMB, 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) { - cifs_dbg(FYI, "Send error in QPathInfo = %d\n", rc); - } else { /* decode response */ - rc = validate_t2((struct smb_t2_rsp *)pSMBr); - - if (rc) /* BB add auto retry on EOPNOTSUPP? */ - rc = -EIO; - else if (!legacy && get_bcc(&pSMBr->hdr) < 40) - rc = -EIO; /* bad smb */ - else if (legacy && get_bcc(&pSMBr->hdr) < 24) - rc = -EIO; /* 24 or 26 expected but we do not read - last field */ - else if (data) { - int size; - __u16 data_offset = le16_to_cpu(pSMBr->t2.DataOffset); - - /* - * On legacy responses we do not read the last field, - * EAsize, fortunately since it varies by subdialect and - * also note it differs on Set vs Get, ie two bytes or 4 - * bytes depending but we don't care here. - */ - if (legacy) - size = sizeof(FILE_INFO_STANDARD); - else - size = sizeof(FILE_ALL_INFO); - memcpy((char *) data, (char *) &pSMBr->hdr.Protocol + - data_offset, size); - } else - rc = -ENOMEM; - } - cifs_buf_release(pSMB); - if (rc == -EAGAIN) - goto QPathInfoRetry; - - return rc; -} - -int -CIFSSMBUnixQFileInfo(const unsigned int xid, struct cifs_tcon *tcon, - u16 netfid, FILE_UNIX_BASIC_INFO *pFindData) -{ - struct smb_t2_qfi_req *pSMB = NULL; - struct smb_t2_qfi_rsp *pSMBr = NULL; - int rc = 0; - int bytes_returned; - __u16 params, byte_count; - -UnixQFileInfoRetry: - rc = smb_init(SMB_COM_TRANSACTION2, 15, tcon, (void **) &pSMB, - (void **) &pSMBr); - if (rc) - return rc; - - params = 2 /* level */ + 2 /* fid */; - pSMB->t2.TotalDataCount = 0; - pSMB->t2.MaxParameterCount = cpu_to_le16(4); - /* BB find exact max data count below from sess structure BB */ - pSMB->t2.MaxDataCount = cpu_to_le16(CIFSMaxBufSize); - pSMB->t2.MaxSetupCount = 0; - pSMB->t2.Reserved = 0; - pSMB->t2.Flags = 0; - pSMB->t2.Timeout = 0; - pSMB->t2.Reserved2 = 0; - pSMB->t2.ParameterOffset = cpu_to_le16(offsetof(struct smb_t2_qfi_req, - Fid) - 4); - pSMB->t2.DataCount = 0; - pSMB->t2.DataOffset = 0; - pSMB->t2.SetupCount = 1; - pSMB->t2.Reserved3 = 0; - pSMB->t2.SubCommand = cpu_to_le16(TRANS2_QUERY_FILE_INFORMATION); - byte_count = params + 1 /* pad */ ; - pSMB->t2.TotalParameterCount = cpu_to_le16(params); - pSMB->t2.ParameterCount = pSMB->t2.TotalParameterCount; - pSMB->InformationLevel = cpu_to_le16(SMB_QUERY_FILE_UNIX_BASIC); - pSMB->Pad = 0; - pSMB->Fid = netfid; - inc_rfc1001_len(pSMB, byte_count); - pSMB->t2.ByteCount = cpu_to_le16(byte_count); - - rc = SendReceive(xid, tcon->ses, (struct smb_hdr *) pSMB, - (struct smb_hdr *) pSMBr, &bytes_returned, 0); - if (rc) { - cifs_dbg(FYI, "Send error in UnixQFileInfo = %d\n", rc); - } else { /* decode response */ - rc = validate_t2((struct smb_t2_rsp *)pSMBr); - - if (rc || get_bcc(&pSMBr->hdr) < sizeof(FILE_UNIX_BASIC_INFO)) { - cifs_dbg(VFS, "Malformed FILE_UNIX_BASIC_INFO response. Unix Extensions can be disabled on mount by specifying the nosfu mount option.\n"); - rc = -EIO; /* bad smb */ - } else { - __u16 data_offset = le16_to_cpu(pSMBr->t2.DataOffset); - memcpy((char *) pFindData, - (char *) &pSMBr->hdr.Protocol + - data_offset, - sizeof(FILE_UNIX_BASIC_INFO)); - } - } - - cifs_buf_release(pSMB); - if (rc == -EAGAIN) - goto UnixQFileInfoRetry; - - return rc; -} - -int -CIFSSMBUnixQPathInfo(const unsigned int xid, struct cifs_tcon *tcon, - const unsigned char *searchName, - FILE_UNIX_BASIC_INFO *pFindData, - const struct nls_table *nls_codepage, int remap) -{ -/* SMB_QUERY_FILE_UNIX_BASIC */ - TRANSACTION2_QPI_REQ *pSMB = NULL; - TRANSACTION2_QPI_RSP *pSMBr = NULL; - int rc = 0; - int bytes_returned = 0; - int name_len; - __u16 params, byte_count; - - cifs_dbg(FYI, "In QPathInfo (Unix) the path %s\n", searchName); -UnixQPathInfoRetry: - rc = smb_init(SMB_COM_TRANSACTION2, 15, tcon, (void **) &pSMB, - (void **) &pSMBr); - if (rc) - return rc; - - if (pSMB->hdr.Flags2 & SMBFLG2_UNICODE) { - name_len = - cifsConvertToUTF16((__le16 *) pSMB->FileName, searchName, - PATH_MAX, nls_codepage, remap); - name_len++; /* trailing null */ - name_len *= 2; - } else { - name_len = copy_path_name(pSMB->FileName, searchName); - } - - params = 2 /* level */ + 4 /* reserved */ + name_len /* includes NUL */; - pSMB->TotalDataCount = 0; - pSMB->MaxParameterCount = cpu_to_le16(2); - /* BB find exact max SMB PDU from sess structure BB */ - pSMB->MaxDataCount = cpu_to_le16(4000); - pSMB->MaxSetupCount = 0; - pSMB->Reserved = 0; - pSMB->Flags = 0; - pSMB->Timeout = 0; - pSMB->Reserved2 = 0; - pSMB->ParameterOffset = cpu_to_le16(offsetof( - struct smb_com_transaction2_qpi_req, InformationLevel) - 4); - pSMB->DataCount = 0; - pSMB->DataOffset = 0; - pSMB->SetupCount = 1; - pSMB->Reserved3 = 0; - pSMB->SubCommand = cpu_to_le16(TRANS2_QUERY_PATH_INFORMATION); - byte_count = params + 1 /* pad */ ; - pSMB->TotalParameterCount = cpu_to_le16(params); - pSMB->ParameterCount = pSMB->TotalParameterCount; - pSMB->InformationLevel = cpu_to_le16(SMB_QUERY_FILE_UNIX_BASIC); - pSMB->Reserved4 = 0; - inc_rfc1001_len(pSMB, 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) { - cifs_dbg(FYI, "Send error in UnixQPathInfo = %d\n", rc); - } else { /* decode response */ - rc = validate_t2((struct smb_t2_rsp *)pSMBr); - - if (rc || get_bcc(&pSMBr->hdr) < sizeof(FILE_UNIX_BASIC_INFO)) { - cifs_dbg(VFS, "Malformed FILE_UNIX_BASIC_INFO response. Unix Extensions can be disabled on mount by specifying the nosfu mount option.\n"); - rc = -EIO; /* bad smb */ - } else { - __u16 data_offset = le16_to_cpu(pSMBr->t2.DataOffset); - memcpy((char *) pFindData, - (char *) &pSMBr->hdr.Protocol + - data_offset, - sizeof(FILE_UNIX_BASIC_INFO)); - } - } - cifs_buf_release(pSMB); - if (rc == -EAGAIN) - goto UnixQPathInfoRetry; - - return rc; -} - -/* xid, tcon, searchName and codepage are input parms, rest are returned */ -int -CIFSFindFirst(const unsigned int xid, struct cifs_tcon *tcon, - const char *searchName, struct cifs_sb_info *cifs_sb, - __u16 *pnetfid, __u16 search_flags, - struct cifs_search_info *psrch_inf, bool msearch) -{ -/* level 257 SMB_ */ - TRANSACTION2_FFIRST_REQ *pSMB = NULL; - TRANSACTION2_FFIRST_RSP *pSMBr = NULL; - T2_FFIRST_RSP_PARMS *parms; - int rc = 0; - int bytes_returned = 0; - int name_len, remap; - __u16 params, byte_count; - struct nls_table *nls_codepage; - - cifs_dbg(FYI, "In FindFirst for %s\n", searchName); - -findFirstRetry: - rc = smb_init(SMB_COM_TRANSACTION2, 15, tcon, (void **) &pSMB, - (void **) &pSMBr); - if (rc) - return rc; - - nls_codepage = cifs_sb->local_nls; - remap = cifs_remap(cifs_sb); - - if (pSMB->hdr.Flags2 & SMBFLG2_UNICODE) { - name_len = - cifsConvertToUTF16((__le16 *) pSMB->FileName, searchName, - PATH_MAX, nls_codepage, remap); - /* We can not add the asterik earlier in case - it got remapped to 0xF03A as if it were part of the - directory name instead of a wildcard */ - name_len *= 2; - if (msearch) { - pSMB->FileName[name_len] = CIFS_DIR_SEP(cifs_sb); - pSMB->FileName[name_len+1] = 0; - pSMB->FileName[name_len+2] = '*'; - pSMB->FileName[name_len+3] = 0; - name_len += 4; /* now the trailing null */ - /* null terminate just in case */ - pSMB->FileName[name_len] = 0; - pSMB->FileName[name_len+1] = 0; - name_len += 2; - } - } else { - name_len = copy_path_name(pSMB->FileName, searchName); - if (msearch) { - if (WARN_ON_ONCE(name_len > PATH_MAX-2)) - name_len = PATH_MAX-2; - /* overwrite nul byte */ - pSMB->FileName[name_len-1] = CIFS_DIR_SEP(cifs_sb); - pSMB->FileName[name_len] = '*'; - pSMB->FileName[name_len+1] = 0; - name_len += 2; - } - } - - params = 12 + name_len /* includes null */ ; - pSMB->TotalDataCount = 0; /* no EAs */ - pSMB->MaxParameterCount = cpu_to_le16(10); - pSMB->MaxDataCount = cpu_to_le16(CIFSMaxBufSize & 0xFFFFFF00); - 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_ffirst_req, SearchAttributes) - - 4); - pSMB->DataCount = 0; - pSMB->DataOffset = 0; - pSMB->SetupCount = 1; /* one byte, no need to make endian neutral */ - pSMB->Reserved3 = 0; - pSMB->SubCommand = cpu_to_le16(TRANS2_FIND_FIRST); - pSMB->SearchAttributes = - cpu_to_le16(ATTR_READONLY | ATTR_HIDDEN | ATTR_SYSTEM | - ATTR_DIRECTORY); - pSMB->SearchCount = cpu_to_le16(CIFSMaxBufSize/sizeof(FILE_UNIX_INFO)); - pSMB->SearchFlags = cpu_to_le16(search_flags); - pSMB->InformationLevel = cpu_to_le16(psrch_inf->info_level); - - /* BB what should we set StorageType to? Does it matter? BB */ - pSMB->SearchStorageType = 0; - inc_rfc1001_len(pSMB, 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); - cifs_stats_inc(&tcon->stats.cifs_stats.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 */ - cifs_dbg(FYI, "Error in FindFirst = %d\n", rc); - - cifs_buf_release(pSMB); - - /* BB eventually could optimize out free and realloc of buf */ - /* for this case */ - if (rc == -EAGAIN) - goto findFirstRetry; - } else { /* decode response */ - /* BB remember to free buffer if error BB */ - rc = validate_t2((struct smb_t2_rsp *)pSMBr); - if (rc == 0) { - unsigned int lnoff; - - if (pSMBr->hdr.Flags2 & SMBFLG2_UNICODE) - psrch_inf->unicode = true; - else - psrch_inf->unicode = false; - - psrch_inf->ntwrk_buf_start = (char *)pSMBr; - psrch_inf->smallBuf = false; - psrch_inf->srch_entries_start = - (char *) &pSMBr->hdr.Protocol + - le16_to_cpu(pSMBr->t2.DataOffset); - parms = (T2_FFIRST_RSP_PARMS *)((char *) &pSMBr->hdr.Protocol + - le16_to_cpu(pSMBr->t2.ParameterOffset)); - - if (parms->EndofSearch) - psrch_inf->endOfSearch = true; - else - psrch_inf->endOfSearch = false; - - psrch_inf->entries_in_buffer = - le16_to_cpu(parms->SearchCount); - psrch_inf->index_of_last_entry = 2 /* skip . and .. */ + - psrch_inf->entries_in_buffer; - lnoff = le16_to_cpu(parms->LastNameOffset); - if (CIFSMaxBufSize < lnoff) { - cifs_dbg(VFS, "ignoring corrupt resume name\n"); - psrch_inf->last_entry = NULL; - return rc; - } - - psrch_inf->last_entry = psrch_inf->srch_entries_start + - lnoff; - - if (pnetfid) - *pnetfid = parms->SearchHandle; - } else { - cifs_buf_release(pSMB); - } - } - - return rc; -} - -int CIFSFindNext(const unsigned int xid, struct cifs_tcon *tcon, - __u16 searchHandle, __u16 search_flags, - struct cifs_search_info *psrch_inf) -{ - TRANSACTION2_FNEXT_REQ *pSMB = NULL; - TRANSACTION2_FNEXT_RSP *pSMBr = NULL; - T2_FNEXT_RSP_PARMS *parms; - char *response_data; - int rc = 0; - int bytes_returned; - unsigned int name_len; - __u16 params, byte_count; - - cifs_dbg(FYI, "In FindNext\n"); - - if (psrch_inf->endOfSearch) - return -ENOENT; - - rc = smb_init(SMB_COM_TRANSACTION2, 15, tcon, (void **) &pSMB, - (void **) &pSMBr); - if (rc) - return rc; - - params = 14; /* includes 2 bytes of null string, converted to LE below*/ - byte_count = 0; - pSMB->TotalDataCount = 0; /* no EAs */ - pSMB->MaxParameterCount = cpu_to_le16(8); - pSMB->MaxDataCount = cpu_to_le16(CIFSMaxBufSize & 0xFFFFFF00); - pSMB->MaxSetupCount = 0; - pSMB->Reserved = 0; - pSMB->Flags = 0; - pSMB->Timeout = 0; - pSMB->Reserved2 = 0; - pSMB->ParameterOffset = cpu_to_le16( - offsetof(struct smb_com_transaction2_fnext_req,SearchHandle) - 4); - pSMB->DataCount = 0; - pSMB->DataOffset = 0; - pSMB->SetupCount = 1; - pSMB->Reserved3 = 0; - pSMB->SubCommand = cpu_to_le16(TRANS2_FIND_NEXT); - pSMB->SearchHandle = searchHandle; /* always kept as le */ - pSMB->SearchCount = - cpu_to_le16(CIFSMaxBufSize / sizeof(FILE_UNIX_INFO)); - pSMB->InformationLevel = cpu_to_le16(psrch_inf->info_level); - pSMB->ResumeKey = psrch_inf->resume_key; - pSMB->SearchFlags = cpu_to_le16(search_flags); - - name_len = psrch_inf->resume_name_len; - params += name_len; - if (name_len < PATH_MAX) { - memcpy(pSMB->ResumeFileName, psrch_inf->presume_name, name_len); - byte_count += name_len; - /* 14 byte parm len above enough for 2 byte null terminator */ - pSMB->ResumeFileName[name_len] = 0; - pSMB->ResumeFileName[name_len+1] = 0; - } else { - rc = -EINVAL; - goto FNext2_err_exit; - } - byte_count = params + 1 /* pad */ ; - pSMB->TotalParameterCount = cpu_to_le16(params); - pSMB->ParameterCount = pSMB->TotalParameterCount; - inc_rfc1001_len(pSMB, 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); - cifs_stats_inc(&tcon->stats.cifs_stats.num_fnext); - if (rc) { - if (rc == -EBADF) { - psrch_inf->endOfSearch = true; - cifs_buf_release(pSMB); - rc = 0; /* search probably was closed at end of search*/ - } else - cifs_dbg(FYI, "FindNext returned = %d\n", rc); - } else { /* decode response */ - rc = validate_t2((struct smb_t2_rsp *)pSMBr); - - if (rc == 0) { - unsigned int lnoff; - - /* BB fixme add lock for file (srch_info) struct here */ - if (pSMBr->hdr.Flags2 & SMBFLG2_UNICODE) - psrch_inf->unicode = true; - else - psrch_inf->unicode = false; - response_data = (char *) &pSMBr->hdr.Protocol + - le16_to_cpu(pSMBr->t2.ParameterOffset); - parms = (T2_FNEXT_RSP_PARMS *)response_data; - response_data = (char *)&pSMBr->hdr.Protocol + - le16_to_cpu(pSMBr->t2.DataOffset); - if (psrch_inf->smallBuf) - cifs_small_buf_release( - psrch_inf->ntwrk_buf_start); - else - cifs_buf_release(psrch_inf->ntwrk_buf_start); - psrch_inf->srch_entries_start = response_data; - psrch_inf->ntwrk_buf_start = (char *)pSMB; - psrch_inf->smallBuf = false; - if (parms->EndofSearch) - psrch_inf->endOfSearch = true; - else - psrch_inf->endOfSearch = false; - psrch_inf->entries_in_buffer = - le16_to_cpu(parms->SearchCount); - psrch_inf->index_of_last_entry += - psrch_inf->entries_in_buffer; - lnoff = le16_to_cpu(parms->LastNameOffset); - if (CIFSMaxBufSize < lnoff) { - cifs_dbg(VFS, "ignoring corrupt resume name\n"); - psrch_inf->last_entry = NULL; - return rc; - } else - psrch_inf->last_entry = - psrch_inf->srch_entries_start + lnoff; - -/* cifs_dbg(FYI, "fnxt2 entries in buf %d index_of_last %d\n", - psrch_inf->entries_in_buffer, psrch_inf->index_of_last_entry); */ - - /* BB fixme add unlock here */ - } - - } - - /* BB On error, should we leave previous search buf (and count and - last entry fields) intact or free the previous one? */ - - /* Note: On -EAGAIN error only caller can retry on handle based calls - since file handle passed in no longer valid */ -FNext2_err_exit: - if (rc != 0) - cifs_buf_release(pSMB); - return rc; -} - -int -CIFSFindClose(const unsigned int xid, struct cifs_tcon *tcon, - const __u16 searchHandle) -{ - int rc = 0; - FINDCLOSE_REQ *pSMB = NULL; - - cifs_dbg(FYI, "In CIFSSMBFindClose\n"); - rc = small_smb_init(SMB_COM_FIND_CLOSE2, 1, tcon, (void **)&pSMB); - - /* no sense returning error if session restarted - as file handle has been closed */ - if (rc == -EAGAIN) - return 0; - if (rc) - return rc; - - pSMB->FileID = searchHandle; - pSMB->ByteCount = 0; - rc = SendReceiveNoRsp(xid, tcon->ses, (char *) pSMB, 0); - cifs_small_buf_release(pSMB); - if (rc) - cifs_dbg(VFS, "Send error in FindClose = %d\n", rc); - - cifs_stats_inc(&tcon->stats.cifs_stats.num_fclose); - - /* Since session is dead, search handle closed on server already */ - if (rc == -EAGAIN) - rc = 0; - - return rc; -} - -int -CIFSGetSrvInodeNumber(const unsigned int xid, struct cifs_tcon *tcon, - const char *search_name, __u64 *inode_number, - const struct nls_table *nls_codepage, int remap) -{ - int rc = 0; - TRANSACTION2_QPI_REQ *pSMB = NULL; - TRANSACTION2_QPI_RSP *pSMBr = NULL; - int name_len, bytes_returned; - __u16 params, byte_count; - - cifs_dbg(FYI, "In GetSrvInodeNum for %s\n", search_name); - if (tcon == NULL) - return -ENODEV; - -GetInodeNumberRetry: - rc = smb_init(SMB_COM_TRANSACTION2, 15, tcon, (void **) &pSMB, - (void **) &pSMBr); - if (rc) - return rc; - - if (pSMB->hdr.Flags2 & SMBFLG2_UNICODE) { - name_len = - cifsConvertToUTF16((__le16 *) pSMB->FileName, - search_name, PATH_MAX, nls_codepage, - remap); - name_len++; /* trailing null */ - name_len *= 2; - } else { - name_len = copy_path_name(pSMB->FileName, search_name); - } - - params = 2 /* level */ + 4 /* rsrvd */ + name_len /* incl null */ ; - pSMB->TotalDataCount = 0; - pSMB->MaxParameterCount = cpu_to_le16(2); - /* BB find exact max data count below from sess structure BB */ - pSMB->MaxDataCount = cpu_to_le16(4000); - pSMB->MaxSetupCount = 0; - pSMB->Reserved = 0; - pSMB->Flags = 0; - pSMB->Timeout = 0; - pSMB->Reserved2 = 0; - pSMB->ParameterOffset = cpu_to_le16(offsetof( - struct smb_com_transaction2_qpi_req, InformationLevel) - 4); - pSMB->DataCount = 0; - pSMB->DataOffset = 0; - pSMB->SetupCount = 1; - pSMB->Reserved3 = 0; - pSMB->SubCommand = cpu_to_le16(TRANS2_QUERY_PATH_INFORMATION); - byte_count = params + 1 /* pad */ ; - pSMB->TotalParameterCount = cpu_to_le16(params); - pSMB->ParameterCount = pSMB->TotalParameterCount; - pSMB->InformationLevel = cpu_to_le16(SMB_QUERY_FILE_INTERNAL_INFO); - pSMB->Reserved4 = 0; - inc_rfc1001_len(pSMB, 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) { - cifs_dbg(FYI, "error %d in QueryInternalInfo\n", rc); - } else { - /* decode response */ - rc = validate_t2((struct smb_t2_rsp *)pSMBr); - /* BB also check enough total bytes returned */ - if (rc || get_bcc(&pSMBr->hdr) < 2) - /* If rc should we check for EOPNOSUPP and - disable the srvino flag? or in caller? */ - rc = -EIO; /* bad smb */ - else { - __u16 data_offset = le16_to_cpu(pSMBr->t2.DataOffset); - __u16 count = le16_to_cpu(pSMBr->t2.DataCount); - struct file_internal_info *pfinfo; - /* BB Do we need a cast or hash here ? */ - if (count < 8) { - cifs_dbg(FYI, "Invalid size ret in QryIntrnlInf\n"); - rc = -EIO; - goto GetInodeNumOut; - } - pfinfo = (struct file_internal_info *) - (data_offset + (char *) &pSMBr->hdr.Protocol); - *inode_number = le64_to_cpu(pfinfo->UniqueId); - } - } -GetInodeNumOut: - cifs_buf_release(pSMB); - if (rc == -EAGAIN) - goto GetInodeNumberRetry; - return rc; -} - -int -CIFSGetDFSRefer(const unsigned int xid, struct cifs_ses *ses, - const char *search_name, struct dfs_info3_param **target_nodes, - unsigned int *num_of_nodes, - const struct nls_table *nls_codepage, int remap) -{ -/* TRANS2_GET_DFS_REFERRAL */ - TRANSACTION2_GET_DFS_REFER_REQ *pSMB = NULL; - TRANSACTION2_GET_DFS_REFER_RSP *pSMBr = NULL; - int rc = 0; - int bytes_returned; - int name_len; - __u16 params, byte_count; - *num_of_nodes = 0; - *target_nodes = NULL; - - cifs_dbg(FYI, "In GetDFSRefer the path %s\n", search_name); - if (ses == NULL || ses->tcon_ipc == NULL) - return -ENODEV; - -getDFSRetry: - /* - * Use smb_init_no_reconnect() instead of smb_init() as - * CIFSGetDFSRefer() may be called from cifs_reconnect_tcon() and thus - * causing an infinite recursion. - */ - rc = smb_init_no_reconnect(SMB_COM_TRANSACTION2, 15, ses->tcon_ipc, - (void **)&pSMB, (void **)&pSMBr); - if (rc) - return rc; - - /* server pointer checked in called function, - but should never be null here anyway */ - pSMB->hdr.Mid = get_next_mid(ses->server); - pSMB->hdr.Tid = ses->tcon_ipc->tid; - pSMB->hdr.Uid = ses->Suid; - if (ses->capabilities & CAP_STATUS32) - pSMB->hdr.Flags2 |= SMBFLG2_ERR_STATUS; - if (ses->capabilities & CAP_DFS) - pSMB->hdr.Flags2 |= SMBFLG2_DFS; - - if (ses->capabilities & CAP_UNICODE) { - pSMB->hdr.Flags2 |= SMBFLG2_UNICODE; - name_len = - cifsConvertToUTF16((__le16 *) pSMB->RequestFileName, - search_name, PATH_MAX, nls_codepage, - remap); - name_len++; /* trailing null */ - name_len *= 2; - } else { /* BB improve the check for buffer overruns BB */ - name_len = copy_path_name(pSMB->RequestFileName, search_name); - } - - if (ses->server->sign) - pSMB->hdr.Flags2 |= SMBFLG2_SECURITY_SIGNATURE; - - pSMB->hdr.Uid = ses->Suid; - - params = 2 /* level */ + name_len /*includes null */ ; - pSMB->TotalDataCount = 0; - pSMB->DataCount = 0; - pSMB->DataOffset = 0; - pSMB->MaxParameterCount = 0; - /* BB find exact max SMB PDU from sess structure BB */ - pSMB->MaxDataCount = cpu_to_le16(4000); - pSMB->MaxSetupCount = 0; - pSMB->Reserved = 0; - pSMB->Flags = 0; - pSMB->Timeout = 0; - pSMB->Reserved2 = 0; - pSMB->ParameterOffset = cpu_to_le16(offsetof( - struct smb_com_transaction2_get_dfs_refer_req, MaxReferralLevel) - 4); - pSMB->SetupCount = 1; - pSMB->Reserved3 = 0; - pSMB->SubCommand = cpu_to_le16(TRANS2_GET_DFS_REFERRAL); - byte_count = params + 3 /* pad */ ; - pSMB->ParameterCount = cpu_to_le16(params); - pSMB->TotalParameterCount = pSMB->ParameterCount; - pSMB->MaxReferralLevel = cpu_to_le16(3); - inc_rfc1001_len(pSMB, byte_count); - pSMB->ByteCount = cpu_to_le16(byte_count); - - rc = SendReceive(xid, ses, (struct smb_hdr *) pSMB, - (struct smb_hdr *) pSMBr, &bytes_returned, 0); - if (rc) { - cifs_dbg(FYI, "Send error in GetDFSRefer = %d\n", rc); - goto GetDFSRefExit; - } - rc = validate_t2((struct smb_t2_rsp *)pSMBr); - - /* BB Also check if enough total bytes returned? */ - if (rc || get_bcc(&pSMBr->hdr) < 17) { - rc = -EIO; /* bad smb */ - goto GetDFSRefExit; - } - - cifs_dbg(FYI, "Decoding GetDFSRefer response BCC: %d Offset %d\n", - get_bcc(&pSMBr->hdr), le16_to_cpu(pSMBr->t2.DataOffset)); - - /* parse returned result into more usable form */ - rc = parse_dfs_referrals(&pSMBr->dfs_data, - le16_to_cpu(pSMBr->t2.DataCount), - num_of_nodes, target_nodes, nls_codepage, - remap, search_name, - (pSMBr->hdr.Flags2 & SMBFLG2_UNICODE) != 0); - -GetDFSRefExit: - cifs_buf_release(pSMB); - - if (rc == -EAGAIN) - goto getDFSRetry; - - return rc; -} - -/* Query File System Info such as free space to old servers such as Win 9x */ -int -SMBOldQFSInfo(const unsigned int xid, struct cifs_tcon *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; - - cifs_dbg(FYI, "OldQFSInfo\n"); -oldQFSInfoRetry: - 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); - inc_rfc1001_len(pSMB, 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) { - cifs_dbg(FYI, "Send error in QFSInfo = %d\n", rc); - } else { /* decode response */ - rc = validate_t2((struct smb_t2_rsp *)pSMBr); - - if (rc || get_bcc(&pSMBr->hdr) < 18) - rc = -EIO; /* bad smb */ - else { - __u16 data_offset = le16_to_cpu(pSMBr->t2.DataOffset); - cifs_dbg(FYI, "qfsinf resp BCC: %d Offset %d\n", - get_bcc(&pSMBr->hdr), 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); - /* - * much prefer larger but if server doesn't report - * a valid size than 4K is a reasonable minimum - */ - if (FSData->f_bsize < 512) - FSData->f_bsize = 4096; - - FSData->f_blocks = - le32_to_cpu(response_data->TotalAllocationUnits); - FSData->f_bfree = FSData->f_bavail = - le32_to_cpu(response_data->FreeAllocationUnits); - cifs_dbg(FYI, "Blocks: %lld Free: %lld Block size %ld\n", - (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 unsigned int xid, struct cifs_tcon *tcon, - struct kstatfs *FSData) -{ -/* level 0x103 SMB_QUERY_FILE_SYSTEM_INFO */ - TRANSACTION2_QFSI_REQ *pSMB = NULL; - TRANSACTION2_QFSI_RSP *pSMBr = NULL; - FILE_SYSTEM_INFO *response_data; - int rc = 0; - int bytes_returned = 0; - __u16 params, byte_count; - - cifs_dbg(FYI, "In QFSInfo\n"); -QFSInfoRetry: - 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_QUERY_FS_SIZE_INFO); - inc_rfc1001_len(pSMB, 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) { - cifs_dbg(FYI, "Send error in QFSInfo = %d\n", rc); - } else { /* decode response */ - rc = validate_t2((struct smb_t2_rsp *)pSMBr); - - if (rc || get_bcc(&pSMBr->hdr) < 24) - rc = -EIO; /* bad smb */ - else { - __u16 data_offset = le16_to_cpu(pSMBr->t2.DataOffset); - - response_data = - (FILE_SYSTEM_INFO - *) (((char *) &pSMBr->hdr.Protocol) + - data_offset); - FSData->f_bsize = - le32_to_cpu(response_data->BytesPerSector) * - le32_to_cpu(response_data-> - SectorsPerAllocationUnit); - /* - * much prefer larger but if server doesn't report - * a valid size than 4K is a reasonable minimum - */ - if (FSData->f_bsize < 512) - FSData->f_bsize = 4096; - - FSData->f_blocks = - le64_to_cpu(response_data->TotalAllocationUnits); - FSData->f_bfree = FSData->f_bavail = - le64_to_cpu(response_data->FreeAllocationUnits); - cifs_dbg(FYI, "Blocks: %lld Free: %lld Block size %ld\n", - (unsigned long long)FSData->f_blocks, - (unsigned long long)FSData->f_bfree, - FSData->f_bsize); - } - } - cifs_buf_release(pSMB); - - if (rc == -EAGAIN) - goto QFSInfoRetry; - - return rc; -} - -int -CIFSSMBQFSAttributeInfo(const unsigned int xid, struct cifs_tcon *tcon) -{ -/* level 0x105 SMB_QUERY_FILE_SYSTEM_INFO */ - TRANSACTION2_QFSI_REQ *pSMB = NULL; - TRANSACTION2_QFSI_RSP *pSMBr = NULL; - FILE_SYSTEM_ATTRIBUTE_INFO *response_data; - int rc = 0; - int bytes_returned = 0; - __u16 params, byte_count; - - cifs_dbg(FYI, "In QFSAttributeInfo\n"); -QFSAttributeRetry: - 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); - /* 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; - 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_QUERY_FS_ATTRIBUTE_INFO); - inc_rfc1001_len(pSMB, 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) { - cifs_dbg(VFS, "Send error in QFSAttributeInfo = %d\n", rc); - } else { /* decode response */ - rc = validate_t2((struct smb_t2_rsp *)pSMBr); - - if (rc || get_bcc(&pSMBr->hdr) < 13) { - /* BB also check if enough bytes returned */ - rc = -EIO; /* bad smb */ - } else { - __u16 data_offset = le16_to_cpu(pSMBr->t2.DataOffset); - response_data = - (FILE_SYSTEM_ATTRIBUTE_INFO - *) (((char *) &pSMBr->hdr.Protocol) + - data_offset); - memcpy(&tcon->fsAttrInfo, response_data, - sizeof(FILE_SYSTEM_ATTRIBUTE_INFO)); - } - } - cifs_buf_release(pSMB); - - if (rc == -EAGAIN) - goto QFSAttributeRetry; - - return rc; -} - -int -CIFSSMBQFSDeviceInfo(const unsigned int xid, struct cifs_tcon *tcon) -{ -/* level 0x104 SMB_QUERY_FILE_SYSTEM_INFO */ - TRANSACTION2_QFSI_REQ *pSMB = NULL; - TRANSACTION2_QFSI_RSP *pSMBr = NULL; - FILE_SYSTEM_DEVICE_INFO *response_data; - int rc = 0; - int bytes_returned = 0; - __u16 params, byte_count; - - cifs_dbg(FYI, "In QFSDeviceInfo\n"); -QFSDeviceRetry: - 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); - /* 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; - 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_QUERY_FS_DEVICE_INFO); - inc_rfc1001_len(pSMB, 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) { - cifs_dbg(FYI, "Send error in QFSDeviceInfo = %d\n", rc); - } else { /* decode response */ - rc = validate_t2((struct smb_t2_rsp *)pSMBr); - - if (rc || get_bcc(&pSMBr->hdr) < - sizeof(FILE_SYSTEM_DEVICE_INFO)) - rc = -EIO; /* bad smb */ - else { - __u16 data_offset = le16_to_cpu(pSMBr->t2.DataOffset); - response_data = - (FILE_SYSTEM_DEVICE_INFO *) - (((char *) &pSMBr->hdr.Protocol) + - data_offset); - memcpy(&tcon->fsDevInfo, response_data, - sizeof(FILE_SYSTEM_DEVICE_INFO)); - } - } - cifs_buf_release(pSMB); - - if (rc == -EAGAIN) - goto QFSDeviceRetry; - - return rc; -} - -int -CIFSSMBQFSUnixInfo(const unsigned int xid, struct cifs_tcon *tcon) -{ -/* level 0x200 SMB_QUERY_CIFS_UNIX_INFO */ - TRANSACTION2_QFSI_REQ *pSMB = NULL; - TRANSACTION2_QFSI_RSP *pSMBr = NULL; - FILE_SYSTEM_UNIX_INFO *response_data; - int rc = 0; - int bytes_returned = 0; - __u16 params, byte_count; - - cifs_dbg(FYI, "In QFSUnixInfo\n"); -QFSUnixRetry: - rc = smb_init_no_reconnect(SMB_COM_TRANSACTION2, 15, tcon, - (void **) &pSMB, (void **) &pSMBr); - if (rc) - return rc; - - params = 2; /* level */ - pSMB->TotalDataCount = 0; - pSMB->DataCount = 0; - pSMB->DataOffset = 0; - pSMB->MaxParameterCount = cpu_to_le16(2); - /* BB find exact max SMB PDU from sess structure BB */ - pSMB->MaxDataCount = cpu_to_le16(100); - pSMB->MaxSetupCount = 0; - pSMB->Reserved = 0; - pSMB->Flags = 0; - pSMB->Timeout = 0; - pSMB->Reserved2 = 0; - byte_count = params + 1 /* pad */ ; - pSMB->ParameterCount = cpu_to_le16(params); - pSMB->TotalParameterCount = pSMB->ParameterCount; - pSMB->ParameterOffset = cpu_to_le16(offsetof(struct - smb_com_transaction2_qfsi_req, InformationLevel) - 4); - pSMB->SetupCount = 1; - pSMB->Reserved3 = 0; - pSMB->SubCommand = cpu_to_le16(TRANS2_QUERY_FS_INFORMATION); - pSMB->InformationLevel = cpu_to_le16(SMB_QUERY_CIFS_UNIX_INFO); - inc_rfc1001_len(pSMB, 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) { - cifs_dbg(VFS, "Send error in QFSUnixInfo = %d\n", rc); - } else { /* decode response */ - rc = validate_t2((struct smb_t2_rsp *)pSMBr); - - if (rc || get_bcc(&pSMBr->hdr) < 13) { - rc = -EIO; /* bad smb */ - } else { - __u16 data_offset = le16_to_cpu(pSMBr->t2.DataOffset); - response_data = - (FILE_SYSTEM_UNIX_INFO - *) (((char *) &pSMBr->hdr.Protocol) + - data_offset); - memcpy(&tcon->fsUnixInfo, response_data, - sizeof(FILE_SYSTEM_UNIX_INFO)); - } - } - cifs_buf_release(pSMB); - - if (rc == -EAGAIN) - goto QFSUnixRetry; - - - return rc; -} - -int -CIFSSMBSetFSUnixInfo(const unsigned int xid, struct cifs_tcon *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; - - cifs_dbg(FYI, "In SETFSUnixInfo\n"); -SETFSUnixRetry: - /* BB switch to small buf init to save memory */ - rc = smb_init_no_reconnect(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); - /* BB find exact max SMB PDU from sess structure BB */ - pSMB->MaxDataCount = cpu_to_le16(100); - 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); - - inc_rfc1001_len(pSMB, 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) { - cifs_dbg(VFS, "Send error in SETFSUnixInfo = %d\n", 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 unsigned int xid, struct cifs_tcon *tcon, - struct kstatfs *FSData) -{ -/* level 0x201 SMB_QUERY_CIFS_POSIX_INFO */ - TRANSACTION2_QFSI_REQ *pSMB = NULL; - TRANSACTION2_QFSI_RSP *pSMBr = NULL; - FILE_SYSTEM_POSIX_INFO *response_data; - int rc = 0; - int bytes_returned = 0; - __u16 params, byte_count; - - cifs_dbg(FYI, "In QFSPosixInfo\n"); -QFSPosixRetry: - rc = smb_init(SMB_COM_TRANSACTION2, 15, tcon, (void **) &pSMB, - (void **) &pSMBr); - if (rc) - return rc; - - params = 2; /* level */ - pSMB->TotalDataCount = 0; - pSMB->DataCount = 0; - pSMB->DataOffset = 0; - pSMB->MaxParameterCount = cpu_to_le16(2); - /* BB find exact max SMB PDU from sess structure BB */ - pSMB->MaxDataCount = cpu_to_le16(100); - pSMB->MaxSetupCount = 0; - pSMB->Reserved = 0; - pSMB->Flags = 0; - pSMB->Timeout = 0; - pSMB->Reserved2 = 0; - byte_count = params + 1 /* pad */ ; - pSMB->ParameterCount = cpu_to_le16(params); - pSMB->TotalParameterCount = pSMB->ParameterCount; - pSMB->ParameterOffset = cpu_to_le16(offsetof(struct - smb_com_transaction2_qfsi_req, InformationLevel) - 4); - pSMB->SetupCount = 1; - pSMB->Reserved3 = 0; - pSMB->SubCommand = cpu_to_le16(TRANS2_QUERY_FS_INFORMATION); - pSMB->InformationLevel = cpu_to_le16(SMB_QUERY_POSIX_FS_INFO); - inc_rfc1001_len(pSMB, 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) { - cifs_dbg(FYI, "Send error in QFSUnixInfo = %d\n", rc); - } else { /* decode response */ - rc = validate_t2((struct smb_t2_rsp *)pSMBr); - - if (rc || get_bcc(&pSMBr->hdr) < 13) { - rc = -EIO; /* bad smb */ - } else { - __u16 data_offset = le16_to_cpu(pSMBr->t2.DataOffset); - response_data = - (FILE_SYSTEM_POSIX_INFO - *) (((char *) &pSMBr->hdr.Protocol) + - data_offset); - FSData->f_bsize = - le32_to_cpu(response_data->BlockSize); - /* - * much prefer larger but if server doesn't report - * a valid size than 4K is a reasonable minimum - */ - if (FSData->f_bsize < 512) - FSData->f_bsize = 4096; - - FSData->f_blocks = - le64_to_cpu(response_data->TotalBlocks); - FSData->f_bfree = - le64_to_cpu(response_data->BlocksAvail); - 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 != cpu_to_le64(-1)) - FSData->f_files = - le64_to_cpu(response_data->TotalFileNodes); - if (response_data->FreeFileNodes != cpu_to_le64(-1)) - FSData->f_ffree = - le64_to_cpu(response_data->FreeFileNodes); - } - } - cifs_buf_release(pSMB); - - if (rc == -EAGAIN) - goto QFSPosixRetry; - - return rc; -} - - -/* - * We can not use write of zero bytes trick to set file size due to need for - * large file support. Also note that this SetPathInfo is preferred to - * SetFileInfo based method in next routine which is only needed to work around - * a sharing violation bugin Samba which this routine can run into. - */ -int -CIFSSMBSetEOF(const unsigned int xid, struct cifs_tcon *tcon, - const char *file_name, __u64 size, struct cifs_sb_info *cifs_sb, - bool set_allocation) -{ - struct smb_com_transaction2_spi_req *pSMB = NULL; - struct smb_com_transaction2_spi_rsp *pSMBr = NULL; - struct file_end_of_file_info *parm_data; - int name_len; - int rc = 0; - int bytes_returned = 0; - int remap = cifs_remap(cifs_sb); - - __u16 params, byte_count, data_count, param_offset, offset; - - cifs_dbg(FYI, "In SetEOF\n"); -SetEOFRetry: - rc = smb_init(SMB_COM_TRANSACTION2, 15, tcon, (void **) &pSMB, - (void **) &pSMBr); - if (rc) - return rc; - - if (pSMB->hdr.Flags2 & SMBFLG2_UNICODE) { - name_len = - cifsConvertToUTF16((__le16 *) pSMB->FileName, file_name, - PATH_MAX, cifs_sb->local_nls, remap); - name_len++; /* trailing null */ - name_len *= 2; - } else { - name_len = copy_path_name(pSMB->FileName, file_name); - } - params = 6 + name_len; - data_count = sizeof(struct file_end_of_file_info); - pSMB->MaxParameterCount = cpu_to_le16(2); - pSMB->MaxDataCount = cpu_to_le16(4100); - pSMB->MaxSetupCount = 0; - pSMB->Reserved = 0; - pSMB->Flags = 0; - pSMB->Timeout = 0; - pSMB->Reserved2 = 0; - param_offset = offsetof(struct smb_com_transaction2_spi_req, - InformationLevel) - 4; - offset = param_offset + params; - if (set_allocation) { - if (tcon->ses->capabilities & CAP_INFOLEVEL_PASSTHRU) - pSMB->InformationLevel = - cpu_to_le16(SMB_SET_FILE_ALLOCATION_INFO2); - else - pSMB->InformationLevel = - cpu_to_le16(SMB_SET_FILE_ALLOCATION_INFO); - } else /* Set File Size */ { - if (tcon->ses->capabilities & CAP_INFOLEVEL_PASSTHRU) - pSMB->InformationLevel = - cpu_to_le16(SMB_SET_FILE_END_OF_FILE_INFO2); - else - pSMB->InformationLevel = - cpu_to_le16(SMB_SET_FILE_END_OF_FILE_INFO); - } - - parm_data = - (struct file_end_of_file_info *) (((char *) &pSMB->hdr.Protocol) + - offset); - pSMB->ParameterOffset = cpu_to_le16(param_offset); - pSMB->DataOffset = cpu_to_le16(offset); - pSMB->SetupCount = 1; - pSMB->Reserved3 = 0; - pSMB->SubCommand = cpu_to_le16(TRANS2_SET_PATH_INFORMATION); - byte_count = 3 /* pad */ + params + data_count; - pSMB->DataCount = cpu_to_le16(data_count); - pSMB->TotalDataCount = pSMB->DataCount; - pSMB->ParameterCount = cpu_to_le16(params); - pSMB->TotalParameterCount = pSMB->ParameterCount; - pSMB->Reserved4 = 0; - inc_rfc1001_len(pSMB, byte_count); - parm_data->FileSize = cpu_to_le64(size); - 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) - cifs_dbg(FYI, "SetPathInfo (file size) returned %d\n", rc); - - cifs_buf_release(pSMB); - - if (rc == -EAGAIN) - goto SetEOFRetry; - - return rc; -} - -int -CIFSSMBSetFileSize(const unsigned int xid, struct cifs_tcon *tcon, - struct cifsFileInfo *cfile, __u64 size, bool set_allocation) -{ - struct smb_com_transaction2_sfi_req *pSMB = NULL; - struct file_end_of_file_info *parm_data; - int rc = 0; - __u16 params, param_offset, offset, byte_count, count; - - cifs_dbg(FYI, "SetFileSize (via SetFileInfo) %lld\n", - (long long)size); - rc = small_smb_init(SMB_COM_TRANSACTION2, 15, tcon, (void **) &pSMB); - - if (rc) - return rc; - - pSMB->hdr.Pid = cpu_to_le16((__u16)cfile->pid); - pSMB->hdr.PidHigh = cpu_to_le16((__u16)(cfile->pid >> 16)); - - params = 6; - pSMB->MaxSetupCount = 0; - pSMB->Reserved = 0; - pSMB->Flags = 0; - pSMB->Timeout = 0; - pSMB->Reserved2 = 0; - param_offset = offsetof(struct smb_com_transaction2_sfi_req, Fid) - 4; - offset = param_offset + params; - - count = sizeof(struct file_end_of_file_info); - pSMB->MaxParameterCount = cpu_to_le16(2); - /* BB find exact max SMB PDU from sess structure BB */ - pSMB->MaxDataCount = cpu_to_le16(1000); - pSMB->SetupCount = 1; - pSMB->Reserved3 = 0; - pSMB->SubCommand = cpu_to_le16(TRANS2_SET_FILE_INFORMATION); - byte_count = 3 /* pad */ + params + count; - pSMB->DataCount = cpu_to_le16(count); - pSMB->ParameterCount = cpu_to_le16(params); - pSMB->TotalDataCount = pSMB->DataCount; - pSMB->TotalParameterCount = pSMB->ParameterCount; - pSMB->ParameterOffset = cpu_to_le16(param_offset); - /* SMB offsets are from the beginning of SMB which is 4 bytes in, after RFC1001 field */ - parm_data = - (struct file_end_of_file_info *)(((char *)pSMB) + offset + 4); - pSMB->DataOffset = cpu_to_le16(offset); - parm_data->FileSize = cpu_to_le64(size); - pSMB->Fid = cfile->fid.netfid; - if (set_allocation) { - if (tcon->ses->capabilities & CAP_INFOLEVEL_PASSTHRU) - pSMB->InformationLevel = - cpu_to_le16(SMB_SET_FILE_ALLOCATION_INFO2); - else - pSMB->InformationLevel = - cpu_to_le16(SMB_SET_FILE_ALLOCATION_INFO); - } else /* Set File Size */ { - if (tcon->ses->capabilities & CAP_INFOLEVEL_PASSTHRU) - pSMB->InformationLevel = - cpu_to_le16(SMB_SET_FILE_END_OF_FILE_INFO2); - else - pSMB->InformationLevel = - cpu_to_le16(SMB_SET_FILE_END_OF_FILE_INFO); - } - pSMB->Reserved4 = 0; - inc_rfc1001_len(pSMB, byte_count); - pSMB->ByteCount = cpu_to_le16(byte_count); - rc = SendReceiveNoRsp(xid, tcon->ses, (char *) pSMB, 0); - cifs_small_buf_release(pSMB); - if (rc) { - cifs_dbg(FYI, "Send error in SetFileInfo (SetFileSize) = %d\n", - rc); - } - - /* Note: On -EAGAIN error only caller can retry on handle based calls - since file handle passed in no longer valid */ - - return rc; -} - -/* Some legacy servers such as NT4 require that the file times be set on - an open handle, rather than by pathname - this is awkward due to - potential access conflicts on the open, but it is unavoidable for these - old servers since the only other choice is to go from 100 nanosecond DCE - time and resort to the original setpathinfo level which takes the ancient - DOS time format with 2 second granularity */ -int -CIFSSMBSetFileInfo(const unsigned int xid, struct cifs_tcon *tcon, - const FILE_BASIC_INFO *data, __u16 fid, __u32 pid_of_opener) -{ - struct smb_com_transaction2_sfi_req *pSMB = NULL; - char *data_offset; - int rc = 0; - __u16 params, param_offset, offset, byte_count, count; - - cifs_dbg(FYI, "Set Times (via SetFileInfo)\n"); - rc = small_smb_init(SMB_COM_TRANSACTION2, 15, tcon, (void **) &pSMB); - - if (rc) - return rc; - - pSMB->hdr.Pid = cpu_to_le16((__u16)pid_of_opener); - pSMB->hdr.PidHigh = cpu_to_le16((__u16)(pid_of_opener >> 16)); - - params = 6; - pSMB->MaxSetupCount = 0; - pSMB->Reserved = 0; - pSMB->Flags = 0; - pSMB->Timeout = 0; - pSMB->Reserved2 = 0; - param_offset = offsetof(struct smb_com_transaction2_sfi_req, Fid) - 4; - offset = param_offset + params; - - data_offset = (char *)pSMB + - offsetof(struct smb_hdr, Protocol) + offset; - - count = sizeof(FILE_BASIC_INFO); - pSMB->MaxParameterCount = cpu_to_le16(2); - /* BB find max SMB PDU from sess */ - pSMB->MaxDataCount = cpu_to_le16(1000); - pSMB->SetupCount = 1; - pSMB->Reserved3 = 0; - pSMB->SubCommand = cpu_to_le16(TRANS2_SET_FILE_INFORMATION); - byte_count = 3 /* pad */ + params + count; - pSMB->DataCount = cpu_to_le16(count); - 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); - pSMB->Fid = fid; - if (tcon->ses->capabilities & CAP_INFOLEVEL_PASSTHRU) - pSMB->InformationLevel = cpu_to_le16(SMB_SET_FILE_BASIC_INFO2); - else - pSMB->InformationLevel = cpu_to_le16(SMB_SET_FILE_BASIC_INFO); - pSMB->Reserved4 = 0; - inc_rfc1001_len(pSMB, byte_count); - pSMB->ByteCount = cpu_to_le16(byte_count); - memcpy(data_offset, data, sizeof(FILE_BASIC_INFO)); - rc = SendReceiveNoRsp(xid, tcon->ses, (char *) pSMB, 0); - cifs_small_buf_release(pSMB); - if (rc) - cifs_dbg(FYI, "Send error in Set Time (SetFileInfo) = %d\n", - rc); - - /* Note: On -EAGAIN error only caller can retry on handle based calls - since file handle passed in no longer valid */ - - return rc; -} - -int -CIFSSMBSetFileDisposition(const unsigned int xid, struct cifs_tcon *tcon, - bool delete_file, __u16 fid, __u32 pid_of_opener) -{ - struct smb_com_transaction2_sfi_req *pSMB = NULL; - char *data_offset; - int rc = 0; - __u16 params, param_offset, offset, byte_count, count; - - cifs_dbg(FYI, "Set File Disposition (via SetFileInfo)\n"); - rc = small_smb_init(SMB_COM_TRANSACTION2, 15, tcon, (void **) &pSMB); - - if (rc) - return rc; - - pSMB->hdr.Pid = cpu_to_le16((__u16)pid_of_opener); - pSMB->hdr.PidHigh = cpu_to_le16((__u16)(pid_of_opener >> 16)); - - params = 6; - pSMB->MaxSetupCount = 0; - pSMB->Reserved = 0; - pSMB->Flags = 0; - pSMB->Timeout = 0; - pSMB->Reserved2 = 0; - param_offset = offsetof(struct smb_com_transaction2_sfi_req, Fid) - 4; - offset = param_offset + params; - - /* SMB offsets are from the beginning of SMB which is 4 bytes in, after RFC1001 field */ - data_offset = (char *)(pSMB) + offset + 4; - - count = 1; - pSMB->MaxParameterCount = cpu_to_le16(2); - /* BB find max SMB PDU from sess */ - pSMB->MaxDataCount = cpu_to_le16(1000); - pSMB->SetupCount = 1; - pSMB->Reserved3 = 0; - pSMB->SubCommand = cpu_to_le16(TRANS2_SET_FILE_INFORMATION); - byte_count = 3 /* pad */ + params + count; - pSMB->DataCount = cpu_to_le16(count); - 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); - pSMB->Fid = fid; - pSMB->InformationLevel = cpu_to_le16(SMB_SET_FILE_DISPOSITION_INFO); - pSMB->Reserved4 = 0; - inc_rfc1001_len(pSMB, byte_count); - pSMB->ByteCount = cpu_to_le16(byte_count); - *data_offset = delete_file ? 1 : 0; - rc = SendReceiveNoRsp(xid, tcon->ses, (char *) pSMB, 0); - cifs_small_buf_release(pSMB); - if (rc) - cifs_dbg(FYI, "Send error in SetFileDisposition = %d\n", rc); - - return rc; -} - -static int -CIFSSMBSetPathInfoFB(const unsigned int xid, struct cifs_tcon *tcon, - const char *fileName, const FILE_BASIC_INFO *data, - const struct nls_table *nls_codepage, - struct cifs_sb_info *cifs_sb) -{ - int oplock = 0; - struct cifs_open_parms oparms; - struct cifs_fid fid; - int rc; - - oparms = (struct cifs_open_parms) { - .tcon = tcon, - .cifs_sb = cifs_sb, - .desired_access = GENERIC_WRITE, - .create_options = cifs_create_options(cifs_sb, 0), - .disposition = FILE_OPEN, - .path = fileName, - .fid = &fid, - }; - - rc = CIFS_open(xid, &oparms, &oplock, NULL); - if (rc) - goto out; - - rc = CIFSSMBSetFileInfo(xid, tcon, data, fid.netfid, current->tgid); - CIFSSMBClose(xid, tcon, fid.netfid); -out: - - return rc; -} - -int -CIFSSMBSetPathInfo(const unsigned int xid, struct cifs_tcon *tcon, - const char *fileName, const FILE_BASIC_INFO *data, - const struct nls_table *nls_codepage, - struct cifs_sb_info *cifs_sb) -{ - TRANSACTION2_SPI_REQ *pSMB = NULL; - TRANSACTION2_SPI_RSP *pSMBr = NULL; - int name_len; - int rc = 0; - int bytes_returned = 0; - char *data_offset; - __u16 params, param_offset, offset, byte_count, count; - int remap = cifs_remap(cifs_sb); - - cifs_dbg(FYI, "In SetTimes\n"); - -SetTimesRetry: - rc = smb_init(SMB_COM_TRANSACTION2, 15, tcon, (void **) &pSMB, - (void **) &pSMBr); - if (rc) - return rc; - - if (pSMB->hdr.Flags2 & SMBFLG2_UNICODE) { - name_len = - cifsConvertToUTF16((__le16 *) pSMB->FileName, fileName, - PATH_MAX, nls_codepage, remap); - name_len++; /* trailing null */ - name_len *= 2; - } else { - name_len = copy_path_name(pSMB->FileName, fileName); - } - - params = 6 + name_len; - count = sizeof(FILE_BASIC_INFO); - pSMB->MaxParameterCount = cpu_to_le16(2); - /* BB find max SMB PDU from sess structure BB */ - pSMB->MaxDataCount = cpu_to_le16(1000); - pSMB->MaxSetupCount = 0; - pSMB->Reserved = 0; - pSMB->Flags = 0; - pSMB->Timeout = 0; - pSMB->Reserved2 = 0; - param_offset = offsetof(struct smb_com_transaction2_spi_req, - InformationLevel) - 4; - offset = param_offset + params; - data_offset = (char *) (&pSMB->hdr.Protocol) + offset; - pSMB->ParameterOffset = cpu_to_le16(param_offset); - pSMB->DataOffset = cpu_to_le16(offset); - pSMB->SetupCount = 1; - pSMB->Reserved3 = 0; - pSMB->SubCommand = cpu_to_le16(TRANS2_SET_PATH_INFORMATION); - byte_count = 3 /* pad */ + params + count; - - pSMB->DataCount = cpu_to_le16(count); - pSMB->ParameterCount = cpu_to_le16(params); - pSMB->TotalDataCount = pSMB->DataCount; - pSMB->TotalParameterCount = pSMB->ParameterCount; - if (tcon->ses->capabilities & CAP_INFOLEVEL_PASSTHRU) - pSMB->InformationLevel = cpu_to_le16(SMB_SET_FILE_BASIC_INFO2); - else - pSMB->InformationLevel = cpu_to_le16(SMB_SET_FILE_BASIC_INFO); - pSMB->Reserved4 = 0; - inc_rfc1001_len(pSMB, byte_count); - memcpy(data_offset, data, sizeof(FILE_BASIC_INFO)); - 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) - cifs_dbg(FYI, "SetPathInfo (times) returned %d\n", rc); - - cifs_buf_release(pSMB); - - if (rc == -EAGAIN) - goto SetTimesRetry; - - if (rc == -EOPNOTSUPP) - return CIFSSMBSetPathInfoFB(xid, tcon, fileName, data, - nls_codepage, cifs_sb); - - return rc; -} - -static void -cifs_fill_unix_set_info(FILE_UNIX_BASIC_INFO *data_offset, - const struct cifs_unix_set_info_args *args) -{ - u64 uid = NO_CHANGE_64, gid = NO_CHANGE_64; - u64 mode = args->mode; - - if (uid_valid(args->uid)) - uid = from_kuid(&init_user_ns, args->uid); - if (gid_valid(args->gid)) - gid = from_kgid(&init_user_ns, args->gid); - - /* - * Samba server ignores set of file size to zero due to bugs in some - * older clients, but we should be precise - we use SetFileSize to - * set file size and do not want to truncate file size to zero - * accidentally as happened on one Samba server beta by putting - * zero instead of -1 here - */ - data_offset->EndOfFile = cpu_to_le64(NO_CHANGE_64); - data_offset->NumOfBytes = cpu_to_le64(NO_CHANGE_64); - data_offset->LastStatusChange = cpu_to_le64(args->ctime); - data_offset->LastAccessTime = cpu_to_le64(args->atime); - data_offset->LastModificationTime = cpu_to_le64(args->mtime); - data_offset->Uid = cpu_to_le64(uid); - data_offset->Gid = cpu_to_le64(gid); - /* better to leave device as zero when it is */ - data_offset->DevMajor = cpu_to_le64(MAJOR(args->device)); - data_offset->DevMinor = cpu_to_le64(MINOR(args->device)); - data_offset->Permissions = cpu_to_le64(mode); - - if (S_ISREG(mode)) - data_offset->Type = cpu_to_le32(UNIX_FILE); - else if (S_ISDIR(mode)) - data_offset->Type = cpu_to_le32(UNIX_DIR); - else if (S_ISLNK(mode)) - data_offset->Type = cpu_to_le32(UNIX_SYMLINK); - else if (S_ISCHR(mode)) - data_offset->Type = cpu_to_le32(UNIX_CHARDEV); - else if (S_ISBLK(mode)) - data_offset->Type = cpu_to_le32(UNIX_BLOCKDEV); - else if (S_ISFIFO(mode)) - data_offset->Type = cpu_to_le32(UNIX_FIFO); - else if (S_ISSOCK(mode)) - data_offset->Type = cpu_to_le32(UNIX_SOCKET); -} - -int -CIFSSMBUnixSetFileInfo(const unsigned int xid, struct cifs_tcon *tcon, - const struct cifs_unix_set_info_args *args, - u16 fid, u32 pid_of_opener) -{ - struct smb_com_transaction2_sfi_req *pSMB = NULL; - char *data_offset; - int rc = 0; - u16 params, param_offset, offset, byte_count, count; - - cifs_dbg(FYI, "Set Unix Info (via SetFileInfo)\n"); - rc = small_smb_init(SMB_COM_TRANSACTION2, 15, tcon, (void **) &pSMB); - - if (rc) - return rc; - - pSMB->hdr.Pid = cpu_to_le16((__u16)pid_of_opener); - pSMB->hdr.PidHigh = cpu_to_le16((__u16)(pid_of_opener >> 16)); - - params = 6; - pSMB->MaxSetupCount = 0; - pSMB->Reserved = 0; - pSMB->Flags = 0; - pSMB->Timeout = 0; - pSMB->Reserved2 = 0; - param_offset = offsetof(struct smb_com_transaction2_sfi_req, Fid) - 4; - offset = param_offset + params; - - data_offset = (char *)pSMB + - offsetof(struct smb_hdr, Protocol) + offset; - - count = sizeof(FILE_UNIX_BASIC_INFO); - - pSMB->MaxParameterCount = cpu_to_le16(2); - /* BB find max SMB PDU from sess */ - pSMB->MaxDataCount = cpu_to_le16(1000); - pSMB->SetupCount = 1; - pSMB->Reserved3 = 0; - pSMB->SubCommand = cpu_to_le16(TRANS2_SET_FILE_INFORMATION); - byte_count = 3 /* pad */ + params + count; - pSMB->DataCount = cpu_to_le16(count); - 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); - pSMB->Fid = fid; - pSMB->InformationLevel = cpu_to_le16(SMB_SET_FILE_UNIX_BASIC); - pSMB->Reserved4 = 0; - inc_rfc1001_len(pSMB, byte_count); - pSMB->ByteCount = cpu_to_le16(byte_count); - - cifs_fill_unix_set_info((FILE_UNIX_BASIC_INFO *)data_offset, args); - - rc = SendReceiveNoRsp(xid, tcon->ses, (char *) pSMB, 0); - cifs_small_buf_release(pSMB); - if (rc) - cifs_dbg(FYI, "Send error in Set Time (SetFileInfo) = %d\n", - rc); - - /* Note: On -EAGAIN error only caller can retry on handle based calls - since file handle passed in no longer valid */ - - return rc; -} - -int -CIFSSMBUnixSetPathInfo(const unsigned int xid, struct cifs_tcon *tcon, - const char *file_name, - const struct cifs_unix_set_info_args *args, - const struct nls_table *nls_codepage, int remap) -{ - TRANSACTION2_SPI_REQ *pSMB = NULL; - TRANSACTION2_SPI_RSP *pSMBr = NULL; - int name_len; - int rc = 0; - int bytes_returned = 0; - FILE_UNIX_BASIC_INFO *data_offset; - __u16 params, param_offset, offset, count, byte_count; - - cifs_dbg(FYI, "In SetUID/GID/Mode\n"); -setPermsRetry: - rc = smb_init(SMB_COM_TRANSACTION2, 15, tcon, (void **) &pSMB, - (void **) &pSMBr); - if (rc) - return rc; - - if (pSMB->hdr.Flags2 & SMBFLG2_UNICODE) { - name_len = - cifsConvertToUTF16((__le16 *) pSMB->FileName, file_name, - PATH_MAX, nls_codepage, remap); - name_len++; /* trailing null */ - name_len *= 2; - } else { - name_len = copy_path_name(pSMB->FileName, file_name); - } - - params = 6 + name_len; - count = sizeof(FILE_UNIX_BASIC_INFO); - pSMB->MaxParameterCount = cpu_to_le16(2); - /* BB find max SMB PDU from sess structure BB */ - pSMB->MaxDataCount = cpu_to_le16(1000); - pSMB->MaxSetupCount = 0; - pSMB->Reserved = 0; - pSMB->Flags = 0; - pSMB->Timeout = 0; - pSMB->Reserved2 = 0; - param_offset = offsetof(struct smb_com_transaction2_spi_req, - InformationLevel) - 4; - offset = param_offset + params; - /* SMB offsets are from the beginning of SMB which is 4 bytes in, after RFC1001 field */ - data_offset = (FILE_UNIX_BASIC_INFO *)((char *) pSMB + offset + 4); - memset(data_offset, 0, count); - pSMB->DataOffset = cpu_to_le16(offset); - pSMB->ParameterOffset = cpu_to_le16(param_offset); - pSMB->SetupCount = 1; - pSMB->Reserved3 = 0; - pSMB->SubCommand = cpu_to_le16(TRANS2_SET_PATH_INFORMATION); - byte_count = 3 /* pad */ + params + count; - pSMB->ParameterCount = cpu_to_le16(params); - pSMB->DataCount = cpu_to_le16(count); - pSMB->TotalParameterCount = pSMB->ParameterCount; - pSMB->TotalDataCount = pSMB->DataCount; - pSMB->InformationLevel = cpu_to_le16(SMB_SET_FILE_UNIX_BASIC); - pSMB->Reserved4 = 0; - inc_rfc1001_len(pSMB, byte_count); - - cifs_fill_unix_set_info(data_offset, args); - - 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) - cifs_dbg(FYI, "SetPathInfo (perms) returned %d\n", rc); - - cifs_buf_release(pSMB); - if (rc == -EAGAIN) - goto setPermsRetry; - return rc; -} - -#ifdef CONFIG_CIFS_XATTR -/* - * Do a path-based QUERY_ALL_EAS call and parse the result. This is a common - * function used by listxattr and getxattr type calls. When ea_name is set, - * it looks for that attribute name and stuffs that value into the EAData - * buffer. When ea_name is NULL, it stuffs a list of attribute names into the - * buffer. In both cases, the return value is either the length of the - * resulting data or a negative error code. If EAData is a NULL pointer then - * the data isn't copied to it, but the length is returned. - */ -ssize_t -CIFSSMBQAllEAs(const unsigned int xid, struct cifs_tcon *tcon, - const unsigned char *searchName, const unsigned char *ea_name, - char *EAData, size_t buf_size, - struct cifs_sb_info *cifs_sb) -{ - /* BB assumes one setup word */ - TRANSACTION2_QPI_REQ *pSMB = NULL; - TRANSACTION2_QPI_RSP *pSMBr = NULL; - int remap = cifs_remap(cifs_sb); - struct nls_table *nls_codepage = cifs_sb->local_nls; - int rc = 0; - int bytes_returned; - int list_len; - struct fealist *ea_response_data; - struct fea *temp_fea; - char *temp_ptr; - char *end_of_smb; - __u16 params, byte_count, data_offset; - unsigned int ea_name_len = ea_name ? strlen(ea_name) : 0; - - cifs_dbg(FYI, "In Query All EAs path %s\n", searchName); -QAllEAsRetry: - rc = smb_init(SMB_COM_TRANSACTION2, 15, tcon, (void **) &pSMB, - (void **) &pSMBr); - if (rc) - return rc; - - if (pSMB->hdr.Flags2 & SMBFLG2_UNICODE) { - list_len = - cifsConvertToUTF16((__le16 *) pSMB->FileName, searchName, - PATH_MAX, nls_codepage, remap); - list_len++; /* trailing null */ - list_len *= 2; - } else { - list_len = copy_path_name(pSMB->FileName, searchName); - } - - params = 2 /* level */ + 4 /* reserved */ + list_len /* includes NUL */; - pSMB->TotalDataCount = 0; - pSMB->MaxParameterCount = cpu_to_le16(2); - /* BB find exact max SMB PDU from sess structure BB */ - pSMB->MaxDataCount = cpu_to_le16(CIFSMaxBufSize); - pSMB->MaxSetupCount = 0; - pSMB->Reserved = 0; - pSMB->Flags = 0; - pSMB->Timeout = 0; - pSMB->Reserved2 = 0; - pSMB->ParameterOffset = cpu_to_le16(offsetof( - struct smb_com_transaction2_qpi_req, InformationLevel) - 4); - pSMB->DataCount = 0; - pSMB->DataOffset = 0; - pSMB->SetupCount = 1; - pSMB->Reserved3 = 0; - pSMB->SubCommand = cpu_to_le16(TRANS2_QUERY_PATH_INFORMATION); - byte_count = params + 1 /* pad */ ; - pSMB->TotalParameterCount = cpu_to_le16(params); - pSMB->ParameterCount = pSMB->TotalParameterCount; - pSMB->InformationLevel = cpu_to_le16(SMB_INFO_QUERY_ALL_EAS); - pSMB->Reserved4 = 0; - inc_rfc1001_len(pSMB, 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) { - cifs_dbg(FYI, "Send error in QueryAllEAs = %d\n", rc); - goto QAllEAsOut; - } - - - /* BB also check enough total bytes returned */ - /* BB we need to improve the validity checking - of these trans2 responses */ - - rc = validate_t2((struct smb_t2_rsp *)pSMBr); - if (rc || get_bcc(&pSMBr->hdr) < 4) { - rc = -EIO; /* bad smb */ - goto QAllEAsOut; - } - - /* check that length of list is not more than bcc */ - /* check that each entry does not go beyond length - of list */ - /* check that each element of each entry does not - go beyond end of list */ - /* validate_trans2_offsets() */ - /* BB check if start of smb + data_offset > &bcc+ bcc */ - - data_offset = le16_to_cpu(pSMBr->t2.DataOffset); - ea_response_data = (struct fealist *) - (((char *) &pSMBr->hdr.Protocol) + data_offset); - - list_len = le32_to_cpu(ea_response_data->list_len); - cifs_dbg(FYI, "ea length %d\n", list_len); - if (list_len <= 8) { - cifs_dbg(FYI, "empty EA list returned from server\n"); - /* didn't find the named attribute */ - if (ea_name) - rc = -ENODATA; - goto QAllEAsOut; - } - - /* make sure list_len doesn't go past end of SMB */ - end_of_smb = (char *)pByteArea(&pSMBr->hdr) + get_bcc(&pSMBr->hdr); - if ((char *)ea_response_data + list_len > end_of_smb) { - cifs_dbg(FYI, "EA list appears to go beyond SMB\n"); - rc = -EIO; - goto QAllEAsOut; - } - - /* account for ea list len */ - list_len -= 4; - temp_fea = ea_response_data->list; - temp_ptr = (char *)temp_fea; - while (list_len > 0) { - unsigned int name_len; - __u16 value_len; - - list_len -= 4; - temp_ptr += 4; - /* make sure we can read name_len and value_len */ - if (list_len < 0) { - cifs_dbg(FYI, "EA entry goes beyond length of list\n"); - rc = -EIO; - goto QAllEAsOut; - } - - name_len = temp_fea->name_len; - value_len = le16_to_cpu(temp_fea->value_len); - list_len -= name_len + 1 + value_len; - if (list_len < 0) { - cifs_dbg(FYI, "EA entry goes beyond length of list\n"); - rc = -EIO; - goto QAllEAsOut; - } - - if (ea_name) { - if (ea_name_len == name_len && - memcmp(ea_name, temp_ptr, name_len) == 0) { - temp_ptr += name_len + 1; - rc = value_len; - if (buf_size == 0) - goto QAllEAsOut; - if ((size_t)value_len > buf_size) { - rc = -ERANGE; - goto QAllEAsOut; - } - memcpy(EAData, temp_ptr, value_len); - goto QAllEAsOut; - } - } else { - /* account for prefix user. and trailing null */ - rc += (5 + 1 + name_len); - if (rc < (int) buf_size) { - memcpy(EAData, "user.", 5); - EAData += 5; - memcpy(EAData, temp_ptr, name_len); - EAData += name_len; - /* null terminate name */ - *EAData = 0; - ++EAData; - } else if (buf_size == 0) { - /* skip copy - calc size only */ - } else { - /* stop before overrun buffer */ - rc = -ERANGE; - break; - } - } - temp_ptr += name_len + 1 + value_len; - temp_fea = (struct fea *)temp_ptr; - } - - /* didn't find the named attribute */ - if (ea_name) - rc = -ENODATA; - -QAllEAsOut: - cifs_buf_release(pSMB); - if (rc == -EAGAIN) - goto QAllEAsRetry; - - return (ssize_t)rc; -} - -int -CIFSSMBSetEA(const unsigned int xid, struct cifs_tcon *tcon, - const char *fileName, const char *ea_name, const void *ea_value, - const __u16 ea_value_len, const struct nls_table *nls_codepage, - struct cifs_sb_info *cifs_sb) -{ - struct smb_com_transaction2_spi_req *pSMB = NULL; - struct smb_com_transaction2_spi_rsp *pSMBr = NULL; - struct fealist *parm_data; - int name_len; - int rc = 0; - int bytes_returned = 0; - __u16 params, param_offset, byte_count, offset, count; - int remap = cifs_remap(cifs_sb); - - cifs_dbg(FYI, "In SetEA\n"); -SetEARetry: - rc = smb_init(SMB_COM_TRANSACTION2, 15, tcon, (void **) &pSMB, - (void **) &pSMBr); - if (rc) - return rc; - - if (pSMB->hdr.Flags2 & SMBFLG2_UNICODE) { - name_len = - cifsConvertToUTF16((__le16 *) pSMB->FileName, fileName, - PATH_MAX, nls_codepage, remap); - name_len++; /* trailing null */ - name_len *= 2; - } else { - name_len = copy_path_name(pSMB->FileName, fileName); - } - - params = 6 + name_len; - - /* done calculating parms using name_len of file name, - now use name_len to calculate length of ea name - we are going to create in the inode xattrs */ - if (ea_name == NULL) - name_len = 0; - else - name_len = strnlen(ea_name, 255); - - count = sizeof(*parm_data) + ea_value_len + name_len; - pSMB->MaxParameterCount = cpu_to_le16(2); - /* BB find max SMB PDU from sess */ - pSMB->MaxDataCount = cpu_to_le16(1000); - pSMB->MaxSetupCount = 0; - pSMB->Reserved = 0; - pSMB->Flags = 0; - pSMB->Timeout = 0; - pSMB->Reserved2 = 0; - param_offset = offsetof(struct smb_com_transaction2_spi_req, - InformationLevel) - 4; - offset = param_offset + params; - pSMB->InformationLevel = - cpu_to_le16(SMB_SET_FILE_EA); - - parm_data = (void *)pSMB + offsetof(struct smb_hdr, Protocol) + offset; - pSMB->ParameterOffset = cpu_to_le16(param_offset); - pSMB->DataOffset = cpu_to_le16(offset); - pSMB->SetupCount = 1; - pSMB->Reserved3 = 0; - pSMB->SubCommand = cpu_to_le16(TRANS2_SET_PATH_INFORMATION); - byte_count = 3 /* pad */ + params + count; - pSMB->DataCount = cpu_to_le16(count); - parm_data->list_len = cpu_to_le32(count); - parm_data->list[0].EA_flags = 0; - /* we checked above that name len is less than 255 */ - parm_data->list[0].name_len = (__u8)name_len; - /* EA names are always ASCII */ - if (ea_name) - strncpy(parm_data->list[0].name, ea_name, name_len); - parm_data->list[0].name[name_len] = 0; - parm_data->list[0].value_len = cpu_to_le16(ea_value_len); - /* caller ensures that ea_value_len is less than 64K but - we need to ensure that it fits within the smb */ - - /*BB add length check to see if it would fit in - negotiated SMB buffer size BB */ - /* if (ea_value_len > buffer_size - 512 (enough for header)) */ - if (ea_value_len) - memcpy(parm_data->list[0].name+name_len+1, - ea_value, ea_value_len); - - pSMB->TotalDataCount = pSMB->DataCount; - pSMB->ParameterCount = cpu_to_le16(params); - pSMB->TotalParameterCount = pSMB->ParameterCount; - pSMB->Reserved4 = 0; - inc_rfc1001_len(pSMB, 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) - cifs_dbg(FYI, "SetPathInfo (EA) returned %d\n", rc); - - cifs_buf_release(pSMB); - - if (rc == -EAGAIN) - goto SetEARetry; - - return rc; -} -#endif diff --git a/fs/cifs/connect.c b/fs/cifs/connect.c deleted file mode 100644 index 935fe198a4baf..0000000000000 --- a/fs/cifs/connect.c +++ /dev/null @@ -1,4754 +0,0 @@ -// SPDX-License-Identifier: LGPL-2.1 -/* - * - * Copyright (C) International Business Machines Corp., 2002,2011 - * Author(s): Steve French (sfrench@us.ibm.com) - * - */ -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include "cifspdu.h" -#include "cifsglob.h" -#include "cifsproto.h" -#include "cifs_unicode.h" -#include "cifs_debug.h" -#include "cifs_fs_sb.h" -#include "ntlmssp.h" -#include "nterr.h" -#include "rfc1002pdu.h" -#include "fscache.h" -#include "smb2proto.h" -#include "smbdirect.h" -#include "dns_resolve.h" -#ifdef CONFIG_CIFS_DFS_UPCALL -#include "dfs_cache.h" -#endif -#include "fs_context.h" -#include "cifs_swn.h" - -extern mempool_t *cifs_req_poolp; -extern bool disable_legacy_dialects; - -/* FIXME: should these be tunable? */ -#define TLINK_ERROR_EXPIRE (1 * HZ) -#define TLINK_IDLE_EXPIRE (600 * HZ) - -/* Drop the connection to not overload the server */ -#define NUM_STATUS_IO_TIMEOUT 5 - -struct mount_ctx { - struct cifs_sb_info *cifs_sb; - struct smb3_fs_context *fs_ctx; - unsigned int xid; - struct TCP_Server_Info *server; - struct cifs_ses *ses; - struct cifs_tcon *tcon; -#ifdef CONFIG_CIFS_DFS_UPCALL - struct cifs_ses *root_ses; - uuid_t mount_id; - char *origin_fullpath, *leaf_fullpath; -#endif -}; - -static int ip_connect(struct TCP_Server_Info *server); -static int generic_ip_connect(struct TCP_Server_Info *server); -static void tlink_rb_insert(struct rb_root *root, struct tcon_link *new_tlink); -static void cifs_prune_tlinks(struct work_struct *work); - -/* - * Resolve hostname and set ip addr in tcp ses. Useful for hostnames that may - * get their ip addresses changed at some point. - * - * This should be called with server->srv_mutex held. - */ -static int reconn_set_ipaddr_from_hostname(struct TCP_Server_Info *server) -{ - int rc; - int len; - char *unc, *ipaddr = NULL; - time64_t expiry, now; - unsigned long ttl = SMB_DNS_RESOLVE_INTERVAL_DEFAULT; - - if (!server->hostname) - return -EINVAL; - - /* if server hostname isn't populated, there's nothing to do here */ - if (server->hostname[0] == '\0') - return 0; - - len = strlen(server->hostname) + 3; - - unc = kmalloc(len, GFP_KERNEL); - if (!unc) { - cifs_dbg(FYI, "%s: failed to create UNC path\n", __func__); - return -ENOMEM; - } - scnprintf(unc, len, "\\\\%s", server->hostname); - - rc = dns_resolve_server_name_to_ip(unc, &ipaddr, &expiry); - kfree(unc); - - if (rc < 0) { - cifs_dbg(FYI, "%s: failed to resolve server part of %s to IP: %d\n", - __func__, server->hostname, rc); - goto requeue_resolve; - } - - spin_lock(&server->srv_lock); - rc = cifs_convert_address((struct sockaddr *)&server->dstaddr, ipaddr, - strlen(ipaddr)); - spin_unlock(&server->srv_lock); - kfree(ipaddr); - - /* rc == 1 means success here */ - if (rc) { - now = ktime_get_real_seconds(); - if (expiry && expiry > now) - /* - * To make sure we don't use the cached entry, retry 1s - * after expiry. - */ - ttl = max_t(unsigned long, expiry - now, SMB_DNS_RESOLVE_INTERVAL_MIN) + 1; - } - rc = !rc ? -1 : 0; - -requeue_resolve: - cifs_dbg(FYI, "%s: next dns resolution scheduled for %lu seconds in the future\n", - __func__, ttl); - mod_delayed_work(cifsiod_wq, &server->resolve, (ttl * HZ)); - - return rc; -} - -static void smb2_query_server_interfaces(struct work_struct *work) -{ - int rc; - struct cifs_tcon *tcon = container_of(work, - struct cifs_tcon, - query_interfaces.work); - - /* - * query server network interfaces, in case they change - */ - rc = SMB3_request_interfaces(0, tcon, false); - if (rc) { - cifs_dbg(FYI, "%s: failed to query server interfaces: %d\n", - __func__, rc); - } - - queue_delayed_work(cifsiod_wq, &tcon->query_interfaces, - (SMB_INTERFACE_POLL_INTERVAL * HZ)); -} - -static void cifs_resolve_server(struct work_struct *work) -{ - int rc; - struct TCP_Server_Info *server = container_of(work, - struct TCP_Server_Info, resolve.work); - - cifs_server_lock(server); - - /* - * Resolve the hostname again to make sure that IP address is up-to-date. - */ - rc = reconn_set_ipaddr_from_hostname(server); - if (rc) { - cifs_dbg(FYI, "%s: failed to resolve hostname: %d\n", - __func__, rc); - } - - cifs_server_unlock(server); -} - -/* - * Update the tcpStatus for the server. - * This is used to signal the cifsd thread to call cifs_reconnect - * ONLY cifsd thread should call cifs_reconnect. For any other - * thread, use this function - * - * @server: the tcp ses for which reconnect is needed - * @all_channels: if this needs to be done for all channels - */ -void -cifs_signal_cifsd_for_reconnect(struct TCP_Server_Info *server, - bool all_channels) -{ - struct TCP_Server_Info *pserver; - struct cifs_ses *ses; - int i; - - /* If server is a channel, select the primary channel */ - pserver = CIFS_SERVER_IS_CHAN(server) ? server->primary_server : server; - - spin_lock(&pserver->srv_lock); - if (!all_channels) { - pserver->tcpStatus = CifsNeedReconnect; - spin_unlock(&pserver->srv_lock); - return; - } - spin_unlock(&pserver->srv_lock); - - spin_lock(&cifs_tcp_ses_lock); - list_for_each_entry(ses, &pserver->smb_ses_list, smb_ses_list) { - spin_lock(&ses->chan_lock); - for (i = 0; i < ses->chan_count; i++) { - spin_lock(&ses->chans[i].server->srv_lock); - ses->chans[i].server->tcpStatus = CifsNeedReconnect; - spin_unlock(&ses->chans[i].server->srv_lock); - } - spin_unlock(&ses->chan_lock); - } - spin_unlock(&cifs_tcp_ses_lock); -} - -/* - * Mark all sessions and tcons for reconnect. - * IMPORTANT: make sure that this gets called only from - * cifsd thread. For any other thread, use - * cifs_signal_cifsd_for_reconnect - * - * @server: the tcp ses for which reconnect is needed - * @server needs to be previously set to CifsNeedReconnect. - * @mark_smb_session: whether even sessions need to be marked - */ -void -cifs_mark_tcp_ses_conns_for_reconnect(struct TCP_Server_Info *server, - bool mark_smb_session) -{ - struct TCP_Server_Info *pserver; - struct cifs_ses *ses, *nses; - struct cifs_tcon *tcon; - - /* - * before reconnecting the tcp session, mark the smb session (uid) and the tid bad so they - * are not used until reconnected. - */ - cifs_dbg(FYI, "%s: marking necessary sessions and tcons for reconnect\n", __func__); - - /* If server is a channel, select the primary channel */ - pserver = CIFS_SERVER_IS_CHAN(server) ? server->primary_server : server; - - - spin_lock(&cifs_tcp_ses_lock); - list_for_each_entry_safe(ses, nses, &pserver->smb_ses_list, smb_ses_list) { - /* check if iface is still active */ - if (!cifs_chan_is_iface_active(ses, server)) - cifs_chan_update_iface(ses, server); - - spin_lock(&ses->chan_lock); - if (!mark_smb_session && cifs_chan_needs_reconnect(ses, server)) { - spin_unlock(&ses->chan_lock); - continue; - } - - if (mark_smb_session) - CIFS_SET_ALL_CHANS_NEED_RECONNECT(ses); - else - cifs_chan_set_need_reconnect(ses, server); - - cifs_dbg(FYI, "%s: channel connect bitmap: 0x%lx\n", - __func__, ses->chans_need_reconnect); - - /* If all channels need reconnect, then tcon needs reconnect */ - if (!mark_smb_session && !CIFS_ALL_CHANS_NEED_RECONNECT(ses)) { - spin_unlock(&ses->chan_lock); - continue; - } - spin_unlock(&ses->chan_lock); - - spin_lock(&ses->ses_lock); - ses->ses_status = SES_NEED_RECON; - spin_unlock(&ses->ses_lock); - - list_for_each_entry(tcon, &ses->tcon_list, tcon_list) { - tcon->need_reconnect = true; - spin_lock(&tcon->tc_lock); - tcon->status = TID_NEED_RECON; - spin_unlock(&tcon->tc_lock); - } - if (ses->tcon_ipc) { - ses->tcon_ipc->need_reconnect = true; - spin_lock(&ses->tcon_ipc->tc_lock); - ses->tcon_ipc->status = TID_NEED_RECON; - spin_unlock(&ses->tcon_ipc->tc_lock); - } - } - spin_unlock(&cifs_tcp_ses_lock); -} - -static void -cifs_abort_connection(struct TCP_Server_Info *server) -{ - struct mid_q_entry *mid, *nmid; - struct list_head retry_list; - - server->maxBuf = 0; - server->max_read = 0; - - /* do not want to be sending data on a socket we are freeing */ - cifs_dbg(FYI, "%s: tearing down socket\n", __func__); - cifs_server_lock(server); - if (server->ssocket) { - cifs_dbg(FYI, "State: 0x%x Flags: 0x%lx\n", server->ssocket->state, - server->ssocket->flags); - kernel_sock_shutdown(server->ssocket, SHUT_WR); - cifs_dbg(FYI, "Post shutdown state: 0x%x Flags: 0x%lx\n", server->ssocket->state, - server->ssocket->flags); - sock_release(server->ssocket); - server->ssocket = NULL; - } - server->sequence_number = 0; - server->session_estab = false; - kfree_sensitive(server->session_key.response); - server->session_key.response = NULL; - server->session_key.len = 0; - server->lstrp = jiffies; - - /* mark submitted MIDs for retry and issue callback */ - INIT_LIST_HEAD(&retry_list); - cifs_dbg(FYI, "%s: moving mids to private list\n", __func__); - spin_lock(&server->mid_lock); - list_for_each_entry_safe(mid, nmid, &server->pending_mid_q, qhead) { - kref_get(&mid->refcount); - if (mid->mid_state == MID_REQUEST_SUBMITTED) - mid->mid_state = MID_RETRY_NEEDED; - list_move(&mid->qhead, &retry_list); - mid->mid_flags |= MID_DELETED; - } - spin_unlock(&server->mid_lock); - cifs_server_unlock(server); - - cifs_dbg(FYI, "%s: issuing mid callbacks\n", __func__); - list_for_each_entry_safe(mid, nmid, &retry_list, qhead) { - list_del_init(&mid->qhead); - mid->callback(mid); - release_mid(mid); - } - - if (cifs_rdma_enabled(server)) { - cifs_server_lock(server); - smbd_destroy(server); - cifs_server_unlock(server); - } -} - -static bool cifs_tcp_ses_needs_reconnect(struct TCP_Server_Info *server, int num_targets) -{ - spin_lock(&server->srv_lock); - server->nr_targets = num_targets; - if (server->tcpStatus == CifsExiting) { - /* the demux thread will exit normally next time through the loop */ - spin_unlock(&server->srv_lock); - wake_up(&server->response_q); - return false; - } - - cifs_dbg(FYI, "Mark tcp session as need reconnect\n"); - trace_smb3_reconnect(server->CurrentMid, server->conn_id, - server->hostname); - server->tcpStatus = CifsNeedReconnect; - - spin_unlock(&server->srv_lock); - return true; -} - -/* - * cifs tcp session reconnection - * - * mark tcp session as reconnecting so temporarily locked - * mark all smb sessions as reconnecting for tcp session - * reconnect tcp session - * wake up waiters on reconnection? - (not needed currently) - * - * if mark_smb_session is passed as true, unconditionally mark - * the smb session (and tcon) for reconnect as well. This value - * doesn't really matter for non-multichannel scenario. - * - */ -static int __cifs_reconnect(struct TCP_Server_Info *server, - bool mark_smb_session) -{ - int rc = 0; - - if (!cifs_tcp_ses_needs_reconnect(server, 1)) - return 0; - - cifs_mark_tcp_ses_conns_for_reconnect(server, mark_smb_session); - - cifs_abort_connection(server); - - do { - try_to_freeze(); - cifs_server_lock(server); - - if (!cifs_swn_set_server_dstaddr(server)) { - /* resolve the hostname again to make sure that IP address is up-to-date */ - rc = reconn_set_ipaddr_from_hostname(server); - cifs_dbg(FYI, "%s: reconn_set_ipaddr_from_hostname: rc=%d\n", __func__, rc); - } - - if (cifs_rdma_enabled(server)) - rc = smbd_reconnect(server); - else - rc = generic_ip_connect(server); - if (rc) { - cifs_server_unlock(server); - cifs_dbg(FYI, "%s: reconnect error %d\n", __func__, rc); - msleep(3000); - } else { - atomic_inc(&tcpSesReconnectCount); - set_credits(server, 1); - spin_lock(&server->srv_lock); - if (server->tcpStatus != CifsExiting) - server->tcpStatus = CifsNeedNegotiate; - spin_unlock(&server->srv_lock); - cifs_swn_reset_server_dstaddr(server); - cifs_server_unlock(server); - mod_delayed_work(cifsiod_wq, &server->reconnect, 0); - } - } while (server->tcpStatus == CifsNeedReconnect); - - spin_lock(&server->srv_lock); - if (server->tcpStatus == CifsNeedNegotiate) - mod_delayed_work(cifsiod_wq, &server->echo, 0); - spin_unlock(&server->srv_lock); - - wake_up(&server->response_q); - return rc; -} - -#ifdef CONFIG_CIFS_DFS_UPCALL -static int __reconnect_target_unlocked(struct TCP_Server_Info *server, const char *target) -{ - int rc; - char *hostname; - - if (!cifs_swn_set_server_dstaddr(server)) { - if (server->hostname != target) { - hostname = extract_hostname(target); - if (!IS_ERR(hostname)) { - spin_lock(&server->srv_lock); - kfree(server->hostname); - server->hostname = hostname; - spin_unlock(&server->srv_lock); - } else { - cifs_dbg(FYI, "%s: couldn't extract hostname or address from dfs target: %ld\n", - __func__, PTR_ERR(hostname)); - cifs_dbg(FYI, "%s: default to last target server: %s\n", __func__, - server->hostname); - } - } - /* resolve the hostname again to make sure that IP address is up-to-date. */ - rc = reconn_set_ipaddr_from_hostname(server); - cifs_dbg(FYI, "%s: reconn_set_ipaddr_from_hostname: rc=%d\n", __func__, rc); - } - /* Reconnect the socket */ - if (cifs_rdma_enabled(server)) - rc = smbd_reconnect(server); - else - rc = generic_ip_connect(server); - - return rc; -} - -static int reconnect_target_unlocked(struct TCP_Server_Info *server, struct dfs_cache_tgt_list *tl, - struct dfs_cache_tgt_iterator **target_hint) -{ - int rc; - struct dfs_cache_tgt_iterator *tit; - - *target_hint = NULL; - - /* If dfs target list is empty, then reconnect to last server */ - tit = dfs_cache_get_tgt_iterator(tl); - if (!tit) - return __reconnect_target_unlocked(server, server->hostname); - - /* Otherwise, try every dfs target in @tl */ - for (; tit; tit = dfs_cache_get_next_tgt(tl, tit)) { - rc = __reconnect_target_unlocked(server, dfs_cache_get_tgt_name(tit)); - if (!rc) { - *target_hint = tit; - break; - } - } - return rc; -} - -static int reconnect_dfs_server(struct TCP_Server_Info *server) -{ - int rc = 0; - const char *refpath = server->current_fullpath + 1; - struct dfs_cache_tgt_list tl = DFS_CACHE_TGT_LIST_INIT(tl); - struct dfs_cache_tgt_iterator *target_hint = NULL; - int num_targets = 0; - - /* - * Determine the number of dfs targets the referral path in @cifs_sb resolves to. - * - * smb2_reconnect() needs to know how long it should wait based upon the number of dfs - * targets (server->nr_targets). It's also possible that the cached referral was cleared - * through /proc/fs/cifs/dfscache or the target list is empty due to server settings after - * refreshing the referral, so, in this case, default it to 1. - */ - if (!dfs_cache_noreq_find(refpath, NULL, &tl)) - num_targets = dfs_cache_get_nr_tgts(&tl); - if (!num_targets) - num_targets = 1; - - if (!cifs_tcp_ses_needs_reconnect(server, num_targets)) - return 0; - - /* - * Unconditionally mark all sessions & tcons for reconnect as we might be connecting to a - * different server or share during failover. It could be improved by adding some logic to - * only do that in case it connects to a different server or share, though. - */ - cifs_mark_tcp_ses_conns_for_reconnect(server, true); - - cifs_abort_connection(server); - - do { - try_to_freeze(); - cifs_server_lock(server); - - rc = reconnect_target_unlocked(server, &tl, &target_hint); - if (rc) { - /* Failed to reconnect socket */ - cifs_server_unlock(server); - cifs_dbg(FYI, "%s: reconnect error %d\n", __func__, rc); - msleep(3000); - continue; - } - /* - * Socket was created. Update tcp session status to CifsNeedNegotiate so that a - * process waiting for reconnect will know it needs to re-establish session and tcon - * through the reconnected target server. - */ - atomic_inc(&tcpSesReconnectCount); - set_credits(server, 1); - spin_lock(&server->srv_lock); - if (server->tcpStatus != CifsExiting) - server->tcpStatus = CifsNeedNegotiate; - spin_unlock(&server->srv_lock); - cifs_swn_reset_server_dstaddr(server); - cifs_server_unlock(server); - mod_delayed_work(cifsiod_wq, &server->reconnect, 0); - } while (server->tcpStatus == CifsNeedReconnect); - - if (target_hint) - dfs_cache_noreq_update_tgthint(refpath, target_hint); - - dfs_cache_free_tgts(&tl); - - /* Need to set up echo worker again once connection has been established */ - spin_lock(&server->srv_lock); - if (server->tcpStatus == CifsNeedNegotiate) - mod_delayed_work(cifsiod_wq, &server->echo, 0); - spin_unlock(&server->srv_lock); - - wake_up(&server->response_q); - return rc; -} - -int cifs_reconnect(struct TCP_Server_Info *server, bool mark_smb_session) -{ - /* If tcp session is not an dfs connection, then reconnect to last target server */ - spin_lock(&server->srv_lock); - if (!server->is_dfs_conn) { - spin_unlock(&server->srv_lock); - return __cifs_reconnect(server, mark_smb_session); - } - spin_unlock(&server->srv_lock); - - mutex_lock(&server->refpath_lock); - if (!server->origin_fullpath || !server->leaf_fullpath) { - mutex_unlock(&server->refpath_lock); - return __cifs_reconnect(server, mark_smb_session); - } - mutex_unlock(&server->refpath_lock); - - return reconnect_dfs_server(server); -} -#else -int cifs_reconnect(struct TCP_Server_Info *server, bool mark_smb_session) -{ - return __cifs_reconnect(server, mark_smb_session); -} -#endif - -static void -cifs_echo_request(struct work_struct *work) -{ - int rc; - struct TCP_Server_Info *server = container_of(work, - struct TCP_Server_Info, echo.work); - - /* - * We cannot send an echo if it is disabled. - * Also, no need to ping if we got a response recently. - */ - - if (server->tcpStatus == CifsNeedReconnect || - server->tcpStatus == CifsExiting || - server->tcpStatus == CifsNew || - (server->ops->can_echo && !server->ops->can_echo(server)) || - time_before(jiffies, server->lstrp + server->echo_interval - HZ)) - goto requeue_echo; - - rc = server->ops->echo ? server->ops->echo(server) : -ENOSYS; - cifs_server_dbg(FYI, "send echo request: rc = %d\n", rc); - - /* Check witness registrations */ - cifs_swn_check(); - -requeue_echo: - queue_delayed_work(cifsiod_wq, &server->echo, server->echo_interval); -} - -static bool -allocate_buffers(struct TCP_Server_Info *server) -{ - if (!server->bigbuf) { - server->bigbuf = (char *)cifs_buf_get(); - if (!server->bigbuf) { - cifs_server_dbg(VFS, "No memory for large SMB response\n"); - msleep(3000); - /* retry will check if exiting */ - return false; - } - } else if (server->large_buf) { - /* we are reusing a dirty large buf, clear its start */ - memset(server->bigbuf, 0, HEADER_SIZE(server)); - } - - if (!server->smallbuf) { - server->smallbuf = (char *)cifs_small_buf_get(); - if (!server->smallbuf) { - cifs_server_dbg(VFS, "No memory for SMB response\n"); - msleep(1000); - /* retry will check if exiting */ - return false; - } - /* beginning of smb buffer is cleared in our buf_get */ - } else { - /* if existing small buf clear beginning */ - memset(server->smallbuf, 0, HEADER_SIZE(server)); - } - - return true; -} - -static bool -server_unresponsive(struct TCP_Server_Info *server) -{ - /* - * We need to wait 3 echo intervals to make sure we handle such - * situations right: - * 1s client sends a normal SMB request - * 2s client gets a response - * 30s echo workqueue job pops, and decides we got a response recently - * and don't need to send another - * ... - * 65s kernel_recvmsg times out, and we see that we haven't gotten - * a response in >60s. - */ - spin_lock(&server->srv_lock); - if ((server->tcpStatus == CifsGood || - server->tcpStatus == CifsNeedNegotiate) && - (!server->ops->can_echo || server->ops->can_echo(server)) && - time_after(jiffies, server->lstrp + 3 * server->echo_interval)) { - spin_unlock(&server->srv_lock); - cifs_server_dbg(VFS, "has not responded in %lu seconds. Reconnecting...\n", - (3 * server->echo_interval) / HZ); - cifs_reconnect(server, false); - return true; - } - spin_unlock(&server->srv_lock); - - return false; -} - -static inline bool -zero_credits(struct TCP_Server_Info *server) -{ - int val; - - spin_lock(&server->req_lock); - val = server->credits + server->echo_credits + server->oplock_credits; - if (server->in_flight == 0 && val == 0) { - spin_unlock(&server->req_lock); - return true; - } - spin_unlock(&server->req_lock); - return false; -} - -static int -cifs_readv_from_socket(struct TCP_Server_Info *server, struct msghdr *smb_msg) -{ - int length = 0; - int total_read; - - for (total_read = 0; msg_data_left(smb_msg); total_read += length) { - try_to_freeze(); - - /* reconnect if no credits and no requests in flight */ - if (zero_credits(server)) { - cifs_reconnect(server, false); - return -ECONNABORTED; - } - - if (server_unresponsive(server)) - return -ECONNABORTED; - if (cifs_rdma_enabled(server) && server->smbd_conn) - length = smbd_recv(server->smbd_conn, smb_msg); - else - length = sock_recvmsg(server->ssocket, smb_msg, 0); - - spin_lock(&server->srv_lock); - if (server->tcpStatus == CifsExiting) { - spin_unlock(&server->srv_lock); - return -ESHUTDOWN; - } - - if (server->tcpStatus == CifsNeedReconnect) { - spin_unlock(&server->srv_lock); - cifs_reconnect(server, false); - return -ECONNABORTED; - } - spin_unlock(&server->srv_lock); - - if (length == -ERESTARTSYS || - length == -EAGAIN || - length == -EINTR) { - /* - * Minimum sleep to prevent looping, allowing socket - * to clear and app threads to set tcpStatus - * CifsNeedReconnect if server hung. - */ - usleep_range(1000, 2000); - length = 0; - continue; - } - - if (length <= 0) { - cifs_dbg(FYI, "Received no data or error: %d\n", length); - cifs_reconnect(server, false); - return -ECONNABORTED; - } - } - return total_read; -} - -int -cifs_read_from_socket(struct TCP_Server_Info *server, char *buf, - unsigned int to_read) -{ - struct msghdr smb_msg = {}; - struct kvec iov = {.iov_base = buf, .iov_len = to_read}; - iov_iter_kvec(&smb_msg.msg_iter, ITER_DEST, &iov, 1, to_read); - - return cifs_readv_from_socket(server, &smb_msg); -} - -ssize_t -cifs_discard_from_socket(struct TCP_Server_Info *server, size_t to_read) -{ - struct msghdr smb_msg = {}; - - /* - * iov_iter_discard already sets smb_msg.type and count and iov_offset - * and cifs_readv_from_socket sets msg_control and msg_controllen - * so little to initialize in struct msghdr - */ - iov_iter_discard(&smb_msg.msg_iter, ITER_DEST, to_read); - - return cifs_readv_from_socket(server, &smb_msg); -} - -int -cifs_read_page_from_socket(struct TCP_Server_Info *server, struct page *page, - unsigned int page_offset, unsigned int to_read) -{ - struct msghdr smb_msg = {}; - struct bio_vec bv = { - .bv_page = page, .bv_len = to_read, .bv_offset = page_offset}; - iov_iter_bvec(&smb_msg.msg_iter, ITER_DEST, &bv, 1, to_read); - return cifs_readv_from_socket(server, &smb_msg); -} - -static bool -is_smb_response(struct TCP_Server_Info *server, unsigned char type) -{ - /* - * 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. - */ - switch (type) { - case RFC1002_SESSION_MESSAGE: - /* Regular SMB response */ - return true; - case RFC1002_SESSION_KEEP_ALIVE: - cifs_dbg(FYI, "RFC 1002 session keep alive\n"); - break; - case RFC1002_POSITIVE_SESSION_RESPONSE: - cifs_dbg(FYI, "RFC 1002 positive session response\n"); - break; - case RFC1002_NEGATIVE_SESSION_RESPONSE: - /* - * We get this from Windows 98 instead of an error on - * SMB negprot response. - */ - cifs_dbg(FYI, "RFC 1002 negative session response\n"); - /* give server a second to clean up */ - msleep(1000); - /* - * Always try 445 first on reconnect since we get NACK - * on some if we ever connected to port 139 (the NACK - * is since we do not begin with RFC1001 session - * initialize frame). - */ - cifs_set_port((struct sockaddr *)&server->dstaddr, CIFS_PORT); - cifs_reconnect(server, true); - break; - default: - cifs_server_dbg(VFS, "RFC 1002 unknown response type 0x%x\n", type); - cifs_reconnect(server, true); - } - - return false; -} - -void -dequeue_mid(struct mid_q_entry *mid, bool malformed) -{ -#ifdef CONFIG_CIFS_STATS2 - mid->when_received = jiffies; -#endif - spin_lock(&mid->server->mid_lock); - if (!malformed) - mid->mid_state = MID_RESPONSE_RECEIVED; - else - mid->mid_state = MID_RESPONSE_MALFORMED; - /* - * Trying to handle/dequeue a mid after the send_recv() - * function has finished processing it is a bug. - */ - if (mid->mid_flags & MID_DELETED) { - spin_unlock(&mid->server->mid_lock); - pr_warn_once("trying to dequeue a deleted mid\n"); - } else { - list_del_init(&mid->qhead); - mid->mid_flags |= MID_DELETED; - spin_unlock(&mid->server->mid_lock); - } -} - -static unsigned int -smb2_get_credits_from_hdr(char *buffer, struct TCP_Server_Info *server) -{ - struct smb2_hdr *shdr = (struct smb2_hdr *)buffer; - - /* - * SMB1 does not use credits. - */ - if (is_smb1(server)) - return 0; - - return le16_to_cpu(shdr->CreditRequest); -} - -static void -handle_mid(struct mid_q_entry *mid, struct TCP_Server_Info *server, - char *buf, int malformed) -{ - if (server->ops->check_trans2 && - server->ops->check_trans2(mid, server, buf, malformed)) - return; - mid->credits_received = smb2_get_credits_from_hdr(buf, server); - mid->resp_buf = buf; - mid->large_buf = server->large_buf; - /* Was previous buf put in mpx struct for multi-rsp? */ - if (!mid->multiRsp) { - /* smb buffer will be freed by user thread */ - if (server->large_buf) - server->bigbuf = NULL; - else - server->smallbuf = NULL; - } - dequeue_mid(mid, malformed); -} - -int -cifs_enable_signing(struct TCP_Server_Info *server, bool mnt_sign_required) -{ - bool srv_sign_required = server->sec_mode & server->vals->signing_required; - bool srv_sign_enabled = server->sec_mode & server->vals->signing_enabled; - bool mnt_sign_enabled; - - /* - * Is signing required by mnt options? If not then check - * global_secflags to see if it is there. - */ - if (!mnt_sign_required) - mnt_sign_required = ((global_secflags & CIFSSEC_MUST_SIGN) == - CIFSSEC_MUST_SIGN); - - /* - * If signing is required then it's automatically enabled too, - * otherwise, check to see if the secflags allow it. - */ - mnt_sign_enabled = mnt_sign_required ? mnt_sign_required : - (global_secflags & CIFSSEC_MAY_SIGN); - - /* If server requires signing, does client allow it? */ - if (srv_sign_required) { - if (!mnt_sign_enabled) { - cifs_dbg(VFS, "Server requires signing, but it's disabled in SecurityFlags!\n"); - return -EOPNOTSUPP; - } - server->sign = true; - } - - /* If client requires signing, does server allow it? */ - if (mnt_sign_required) { - if (!srv_sign_enabled) { - cifs_dbg(VFS, "Server does not support signing!\n"); - return -EOPNOTSUPP; - } - server->sign = true; - } - - if (cifs_rdma_enabled(server) && server->sign) - cifs_dbg(VFS, "Signing is enabled, and RDMA read/write will be disabled\n"); - - return 0; -} - - -static void clean_demultiplex_info(struct TCP_Server_Info *server) -{ - int length; - - /* take it off the list, if it's not already */ - spin_lock(&server->srv_lock); - list_del_init(&server->tcp_ses_list); - spin_unlock(&server->srv_lock); - - cancel_delayed_work_sync(&server->echo); - cancel_delayed_work_sync(&server->resolve); - - spin_lock(&server->srv_lock); - server->tcpStatus = CifsExiting; - spin_unlock(&server->srv_lock); - wake_up_all(&server->response_q); - - /* check if we have blocked requests that need to free */ - spin_lock(&server->req_lock); - if (server->credits <= 0) - server->credits = 1; - spin_unlock(&server->req_lock); - /* - * Although there should not be any requests blocked on this queue it - * can not hurt to be paranoid and try to wake up requests that may - * haven been blocked when more than 50 at time were on the wire to the - * same server - they now will see the session is in exit state and get - * out of SendReceive. - */ - wake_up_all(&server->request_q); - /* give those requests time to exit */ - msleep(125); - if (cifs_rdma_enabled(server)) - smbd_destroy(server); - if (server->ssocket) { - sock_release(server->ssocket); - server->ssocket = NULL; - } - - if (!list_empty(&server->pending_mid_q)) { - struct list_head dispose_list; - struct mid_q_entry *mid_entry; - struct list_head *tmp, *tmp2; - - INIT_LIST_HEAD(&dispose_list); - spin_lock(&server->mid_lock); - list_for_each_safe(tmp, tmp2, &server->pending_mid_q) { - mid_entry = list_entry(tmp, struct mid_q_entry, qhead); - cifs_dbg(FYI, "Clearing mid %llu\n", mid_entry->mid); - kref_get(&mid_entry->refcount); - mid_entry->mid_state = MID_SHUTDOWN; - list_move(&mid_entry->qhead, &dispose_list); - mid_entry->mid_flags |= MID_DELETED; - } - spin_unlock(&server->mid_lock); - - /* now walk dispose list and issue callbacks */ - list_for_each_safe(tmp, tmp2, &dispose_list) { - mid_entry = list_entry(tmp, struct mid_q_entry, qhead); - cifs_dbg(FYI, "Callback mid %llu\n", mid_entry->mid); - list_del_init(&mid_entry->qhead); - mid_entry->callback(mid_entry); - release_mid(mid_entry); - } - /* 1/8th of sec is more than enough time for them to exit */ - msleep(125); - } - - 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 to wait at - * least 45 seconds before giving up on a request getting a - * response and going ahead and killing cifsd. - */ - cifs_dbg(FYI, "Wait for exit from demultiplex thread\n"); - msleep(46000); - /* - * If threads still have not exited they are probably never - * coming home not much else we can do but free the memory. - */ - } - -#ifdef CONFIG_CIFS_DFS_UPCALL - kfree(server->origin_fullpath); - kfree(server->leaf_fullpath); -#endif - kfree(server); - - length = atomic_dec_return(&tcpSesAllocCount); - if (length > 0) - mempool_resize(cifs_req_poolp, length + cifs_min_rcv); -} - -static int -standard_receive3(struct TCP_Server_Info *server, struct mid_q_entry *mid) -{ - int length; - char *buf = server->smallbuf; - unsigned int pdu_length = server->pdu_size; - - /* make sure this will fit in a large buffer */ - if (pdu_length > CIFSMaxBufSize + MAX_HEADER_SIZE(server) - - HEADER_PREAMBLE_SIZE(server)) { - cifs_server_dbg(VFS, "SMB response too long (%u bytes)\n", pdu_length); - cifs_reconnect(server, true); - return -ECONNABORTED; - } - - /* switch to large buffer if too big for a small one */ - if (pdu_length > MAX_CIFS_SMALL_BUFFER_SIZE - 4) { - server->large_buf = true; - memcpy(server->bigbuf, buf, server->total_read); - buf = server->bigbuf; - } - - /* now read the rest */ - length = cifs_read_from_socket(server, buf + HEADER_SIZE(server) - 1, - pdu_length - MID_HEADER_SIZE(server)); - - if (length < 0) - return length; - server->total_read += length; - - dump_smb(buf, server->total_read); - - return cifs_handle_standard(server, mid); -} - -int -cifs_handle_standard(struct TCP_Server_Info *server, struct mid_q_entry *mid) -{ - char *buf = server->large_buf ? server->bigbuf : server->smallbuf; - int rc; - - /* - * We know that we received enough to get to the MID as we - * checked the pdu_length earlier. Now check to see - * if the rest of the header is OK. - * - * 48 bytes is enough to display the header and a little bit - * into the payload for debugging purposes. - */ - rc = server->ops->check_message(buf, server->total_read, server); - if (rc) - cifs_dump_mem("Bad SMB: ", buf, - min_t(unsigned int, server->total_read, 48)); - - if (server->ops->is_session_expired && - server->ops->is_session_expired(buf)) { - cifs_reconnect(server, true); - return -1; - } - - if (server->ops->is_status_pending && - server->ops->is_status_pending(buf, server)) - return -1; - - if (!mid) - return rc; - - handle_mid(mid, server, buf, rc); - return 0; -} - -static void -smb2_add_credits_from_hdr(char *buffer, struct TCP_Server_Info *server) -{ - struct smb2_hdr *shdr = (struct smb2_hdr *)buffer; - int scredits, in_flight; - - /* - * SMB1 does not use credits. - */ - if (is_smb1(server)) - return; - - if (shdr->CreditRequest) { - spin_lock(&server->req_lock); - server->credits += le16_to_cpu(shdr->CreditRequest); - scredits = server->credits; - in_flight = server->in_flight; - spin_unlock(&server->req_lock); - wake_up(&server->request_q); - - trace_smb3_hdr_credits(server->CurrentMid, - server->conn_id, server->hostname, scredits, - le16_to_cpu(shdr->CreditRequest), in_flight); - cifs_server_dbg(FYI, "%s: added %u credits total=%d\n", - __func__, le16_to_cpu(shdr->CreditRequest), - scredits); - } -} - - -static int -cifs_demultiplex_thread(void *p) -{ - int i, num_mids, length; - struct TCP_Server_Info *server = p; - unsigned int pdu_length; - unsigned int next_offset; - char *buf = NULL; - struct task_struct *task_to_wake = NULL; - struct mid_q_entry *mids[MAX_COMPOUND]; - char *bufs[MAX_COMPOUND]; - unsigned int noreclaim_flag, num_io_timeout = 0; - - noreclaim_flag = memalloc_noreclaim_save(); - cifs_dbg(FYI, "Demultiplex PID: %d\n", task_pid_nr(current)); - - length = atomic_inc_return(&tcpSesAllocCount); - if (length > 1) - mempool_resize(cifs_req_poolp, length + cifs_min_rcv); - - set_freezable(); - allow_kernel_signal(SIGKILL); - while (server->tcpStatus != CifsExiting) { - if (try_to_freeze()) - continue; - - if (!allocate_buffers(server)) - continue; - - server->large_buf = false; - buf = server->smallbuf; - pdu_length = 4; /* enough to get RFC1001 header */ - - length = cifs_read_from_socket(server, buf, pdu_length); - if (length < 0) - continue; - - if (is_smb1(server)) - server->total_read = length; - else - server->total_read = 0; - - /* - * The right amount was read from socket - 4 bytes, - * so we can now interpret the length field. - */ - pdu_length = get_rfc1002_length(buf); - - cifs_dbg(FYI, "RFC1002 header 0x%x\n", pdu_length); - if (!is_smb_response(server, buf[0])) - continue; -next_pdu: - server->pdu_size = pdu_length; - - /* make sure we have enough to get to the MID */ - if (server->pdu_size < MID_HEADER_SIZE(server)) { - cifs_server_dbg(VFS, "SMB response too short (%u bytes)\n", - server->pdu_size); - cifs_reconnect(server, true); - continue; - } - - /* read down to the MID */ - length = cifs_read_from_socket(server, - buf + HEADER_PREAMBLE_SIZE(server), - MID_HEADER_SIZE(server)); - if (length < 0) - continue; - server->total_read += length; - - if (server->ops->next_header) { - next_offset = server->ops->next_header(buf); - if (next_offset) - server->pdu_size = next_offset; - } - - memset(mids, 0, sizeof(mids)); - memset(bufs, 0, sizeof(bufs)); - num_mids = 0; - - if (server->ops->is_transform_hdr && - server->ops->receive_transform && - server->ops->is_transform_hdr(buf)) { - length = server->ops->receive_transform(server, - mids, - bufs, - &num_mids); - } else { - mids[0] = server->ops->find_mid(server, buf); - bufs[0] = buf; - num_mids = 1; - - if (!mids[0] || !mids[0]->receive) - length = standard_receive3(server, mids[0]); - else - length = mids[0]->receive(server, mids[0]); - } - - if (length < 0) { - for (i = 0; i < num_mids; i++) - if (mids[i]) - release_mid(mids[i]); - continue; - } - - if (server->ops->is_status_io_timeout && - server->ops->is_status_io_timeout(buf)) { - num_io_timeout++; - if (num_io_timeout > NUM_STATUS_IO_TIMEOUT) { - cifs_reconnect(server, false); - num_io_timeout = 0; - continue; - } - } - - server->lstrp = jiffies; - - for (i = 0; i < num_mids; i++) { - if (mids[i] != NULL) { - mids[i]->resp_buf_size = server->pdu_size; - - if (bufs[i] && server->ops->is_network_name_deleted) - server->ops->is_network_name_deleted(bufs[i], - server); - - if (!mids[i]->multiRsp || mids[i]->multiEnd) - mids[i]->callback(mids[i]); - - release_mid(mids[i]); - } else if (server->ops->is_oplock_break && - server->ops->is_oplock_break(bufs[i], - server)) { - smb2_add_credits_from_hdr(bufs[i], server); - cifs_dbg(FYI, "Received oplock break\n"); - } else { - cifs_server_dbg(VFS, "No task to wake, unknown frame received! NumMids %d\n", - atomic_read(&mid_count)); - cifs_dump_mem("Received Data is: ", bufs[i], - HEADER_SIZE(server)); - smb2_add_credits_from_hdr(bufs[i], server); -#ifdef CONFIG_CIFS_DEBUG2 - if (server->ops->dump_detail) - server->ops->dump_detail(bufs[i], - server); - cifs_dump_mids(server); -#endif /* CIFS_DEBUG2 */ - } - } - - if (pdu_length > server->pdu_size) { - if (!allocate_buffers(server)) - continue; - pdu_length -= server->pdu_size; - server->total_read = 0; - server->large_buf = false; - buf = server->smallbuf; - goto next_pdu; - } - } /* end while !EXITING */ - - /* buffer usually freed in free_mid - need to free it here on exit */ - cifs_buf_release(server->bigbuf); - if (server->smallbuf) /* no sense logging a debug message if NULL */ - cifs_small_buf_release(server->smallbuf); - - task_to_wake = xchg(&server->tsk, NULL); - clean_demultiplex_info(server); - - /* if server->tsk was NULL then wait for a signal before exiting */ - if (!task_to_wake) { - set_current_state(TASK_INTERRUPTIBLE); - while (!signal_pending(current)) { - schedule(); - set_current_state(TASK_INTERRUPTIBLE); - } - set_current_state(TASK_RUNNING); - } - - memalloc_noreclaim_restore(noreclaim_flag); - module_put_and_kthread_exit(0); -} - -/* - * Returns true if srcaddr isn't specified and rhs isn't specified, or - * if srcaddr is specified and matches the IP address of the rhs argument - */ -bool -cifs_match_ipaddr(struct sockaddr *srcaddr, struct sockaddr *rhs) -{ - switch (srcaddr->sa_family) { - case AF_UNSPEC: - return (rhs->sa_family == AF_UNSPEC); - case AF_INET: { - struct sockaddr_in *saddr4 = (struct sockaddr_in *)srcaddr; - struct sockaddr_in *vaddr4 = (struct sockaddr_in *)rhs; - return (saddr4->sin_addr.s_addr == vaddr4->sin_addr.s_addr); - } - case AF_INET6: { - struct sockaddr_in6 *saddr6 = (struct sockaddr_in6 *)srcaddr; - struct sockaddr_in6 *vaddr6 = (struct sockaddr_in6 *)rhs; - return ipv6_addr_equal(&saddr6->sin6_addr, &vaddr6->sin6_addr); - } - default: - WARN_ON(1); - return false; /* don't expect to be here */ - } -} - -/* - * If no port is specified in addr structure, we try to match with 445 port - * and if it fails - with 139 ports. It should be called only if address - * families of server and addr are equal. - */ -static bool -match_port(struct TCP_Server_Info *server, struct sockaddr *addr) -{ - __be16 port, *sport; - - /* SMBDirect manages its own ports, don't match it here */ - if (server->rdma) - return true; - - switch (addr->sa_family) { - case AF_INET: - sport = &((struct sockaddr_in *) &server->dstaddr)->sin_port; - port = ((struct sockaddr_in *) addr)->sin_port; - break; - case AF_INET6: - sport = &((struct sockaddr_in6 *) &server->dstaddr)->sin6_port; - port = ((struct sockaddr_in6 *) addr)->sin6_port; - break; - default: - WARN_ON(1); - return false; - } - - if (!port) { - port = htons(CIFS_PORT); - if (port == *sport) - return true; - - port = htons(RFC1001_PORT); - } - - return port == *sport; -} - -static bool -match_address(struct TCP_Server_Info *server, struct sockaddr *addr, - struct sockaddr *srcaddr) -{ - switch (addr->sa_family) { - case AF_INET: { - struct sockaddr_in *addr4 = (struct sockaddr_in *)addr; - struct sockaddr_in *srv_addr4 = - (struct sockaddr_in *)&server->dstaddr; - - if (addr4->sin_addr.s_addr != srv_addr4->sin_addr.s_addr) - return false; - break; - } - case AF_INET6: { - struct sockaddr_in6 *addr6 = (struct sockaddr_in6 *)addr; - struct sockaddr_in6 *srv_addr6 = - (struct sockaddr_in6 *)&server->dstaddr; - - if (!ipv6_addr_equal(&addr6->sin6_addr, - &srv_addr6->sin6_addr)) - return false; - if (addr6->sin6_scope_id != srv_addr6->sin6_scope_id) - return false; - break; - } - default: - WARN_ON(1); - return false; /* don't expect to be here */ - } - - if (!cifs_match_ipaddr(srcaddr, (struct sockaddr *)&server->srcaddr)) - return false; - - return true; -} - -static bool -match_security(struct TCP_Server_Info *server, struct smb3_fs_context *ctx) -{ - /* - * The select_sectype function should either return the ctx->sectype - * that was specified, or "Unspecified" if that sectype was not - * compatible with the given NEGOTIATE request. - */ - if (server->ops->select_sectype(server, ctx->sectype) - == Unspecified) - return false; - - /* - * Now check if signing mode is acceptable. No need to check - * global_secflags at this point since if MUST_SIGN is set then - * the server->sign had better be too. - */ - if (ctx->sign && !server->sign) - return false; - - return true; -} - -/* this function must be called with srv_lock held */ -static int match_server(struct TCP_Server_Info *server, struct smb3_fs_context *ctx) -{ - struct sockaddr *addr = (struct sockaddr *)&ctx->dstaddr; - - lockdep_assert_held(&server->srv_lock); - - if (ctx->nosharesock) - return 0; - - /* this server does not share socket */ - if (server->nosharesock) - return 0; - - /* If multidialect negotiation see if existing sessions match one */ - if (strcmp(ctx->vals->version_string, SMB3ANY_VERSION_STRING) == 0) { - if (server->vals->protocol_id < SMB30_PROT_ID) - return 0; - } else if (strcmp(ctx->vals->version_string, - SMBDEFAULT_VERSION_STRING) == 0) { - if (server->vals->protocol_id < SMB21_PROT_ID) - return 0; - } else if ((server->vals != ctx->vals) || (server->ops != ctx->ops)) - return 0; - - if (!net_eq(cifs_net_ns(server), current->nsproxy->net_ns)) - return 0; - - if (strcasecmp(server->hostname, ctx->server_hostname)) - return 0; - - if (!match_address(server, addr, - (struct sockaddr *)&ctx->srcaddr)) - return 0; - - if (!match_port(server, addr)) - return 0; - - if (!match_security(server, ctx)) - return 0; - - if (server->echo_interval != ctx->echo_interval * HZ) - return 0; - - if (server->rdma != ctx->rdma) - return 0; - - if (server->ignore_signature != ctx->ignore_signature) - return 0; - - if (server->min_offload != ctx->min_offload) - return 0; - - return 1; -} - -struct TCP_Server_Info * -cifs_find_tcp_session(struct smb3_fs_context *ctx) -{ - struct TCP_Server_Info *server; - - spin_lock(&cifs_tcp_ses_lock); - list_for_each_entry(server, &cifs_tcp_ses_list, tcp_ses_list) { - spin_lock(&server->srv_lock); -#ifdef CONFIG_CIFS_DFS_UPCALL - /* - * DFS failover implementation in cifs_reconnect() requires unique tcp sessions for - * DFS connections to do failover properly, so avoid sharing them with regular - * shares or even links that may connect to same server but having completely - * different failover targets. - */ - if (server->is_dfs_conn) { - spin_unlock(&server->srv_lock); - continue; - } -#endif - /* - * Skip ses channels since they're only handled in lower layers - * (e.g. cifs_send_recv). - */ - if (CIFS_SERVER_IS_CHAN(server) || !match_server(server, ctx)) { - spin_unlock(&server->srv_lock); - continue; - } - spin_unlock(&server->srv_lock); - - ++server->srv_count; - spin_unlock(&cifs_tcp_ses_lock); - cifs_dbg(FYI, "Existing tcp session with server found\n"); - return server; - } - spin_unlock(&cifs_tcp_ses_lock); - return NULL; -} - -void -cifs_put_tcp_session(struct TCP_Server_Info *server, int from_reconnect) -{ - struct task_struct *task; - - spin_lock(&cifs_tcp_ses_lock); - if (--server->srv_count > 0) { - spin_unlock(&cifs_tcp_ses_lock); - return; - } - - /* srv_count can never go negative */ - WARN_ON(server->srv_count < 0); - - put_net(cifs_net_ns(server)); - - list_del_init(&server->tcp_ses_list); - spin_unlock(&cifs_tcp_ses_lock); - - /* For secondary channels, we pick up ref-count on the primary server */ - if (CIFS_SERVER_IS_CHAN(server)) - cifs_put_tcp_session(server->primary_server, from_reconnect); - - cancel_delayed_work_sync(&server->echo); - cancel_delayed_work_sync(&server->resolve); - - if (from_reconnect) - /* - * Avoid deadlock here: reconnect work calls - * cifs_put_tcp_session() at its end. Need to be sure - * that reconnect work does nothing with server pointer after - * that step. - */ - cancel_delayed_work(&server->reconnect); - else - cancel_delayed_work_sync(&server->reconnect); - - spin_lock(&server->srv_lock); - server->tcpStatus = CifsExiting; - spin_unlock(&server->srv_lock); - - cifs_crypto_secmech_release(server); - - kfree_sensitive(server->session_key.response); - server->session_key.response = NULL; - server->session_key.len = 0; - kfree(server->hostname); - server->hostname = NULL; - - task = xchg(&server->tsk, NULL); - if (task) - send_sig(SIGKILL, task, 1); -} - -struct TCP_Server_Info * -cifs_get_tcp_session(struct smb3_fs_context *ctx, - struct TCP_Server_Info *primary_server) -{ - struct TCP_Server_Info *tcp_ses = NULL; - int rc; - - cifs_dbg(FYI, "UNC: %s\n", ctx->UNC); - - /* see if we already have a matching tcp_ses */ - tcp_ses = cifs_find_tcp_session(ctx); - if (tcp_ses) - return tcp_ses; - - tcp_ses = kzalloc(sizeof(struct TCP_Server_Info), GFP_KERNEL); - if (!tcp_ses) { - rc = -ENOMEM; - goto out_err; - } - - tcp_ses->hostname = kstrdup(ctx->server_hostname, GFP_KERNEL); - if (!tcp_ses->hostname) { - rc = -ENOMEM; - goto out_err; - } - - if (ctx->nosharesock) - tcp_ses->nosharesock = true; - - tcp_ses->ops = ctx->ops; - tcp_ses->vals = ctx->vals; - cifs_set_net_ns(tcp_ses, get_net(current->nsproxy->net_ns)); - - tcp_ses->conn_id = atomic_inc_return(&tcpSesNextId); - tcp_ses->noblockcnt = ctx->rootfs; - tcp_ses->noblocksnd = ctx->noblocksnd || ctx->rootfs; - tcp_ses->noautotune = ctx->noautotune; - tcp_ses->tcp_nodelay = ctx->sockopt_tcp_nodelay; - tcp_ses->rdma = ctx->rdma; - tcp_ses->in_flight = 0; - tcp_ses->max_in_flight = 0; - tcp_ses->credits = 1; - if (primary_server) { - spin_lock(&cifs_tcp_ses_lock); - ++primary_server->srv_count; - spin_unlock(&cifs_tcp_ses_lock); - tcp_ses->primary_server = primary_server; - } - init_waitqueue_head(&tcp_ses->response_q); - init_waitqueue_head(&tcp_ses->request_q); - INIT_LIST_HEAD(&tcp_ses->pending_mid_q); - mutex_init(&tcp_ses->_srv_mutex); - memcpy(tcp_ses->workstation_RFC1001_name, - ctx->source_rfc1001_name, RFC1001_NAME_LEN_WITH_NULL); - memcpy(tcp_ses->server_RFC1001_name, - ctx->target_rfc1001_name, RFC1001_NAME_LEN_WITH_NULL); - tcp_ses->session_estab = false; - tcp_ses->sequence_number = 0; - tcp_ses->reconnect_instance = 1; - tcp_ses->lstrp = jiffies; - tcp_ses->compress_algorithm = cpu_to_le16(ctx->compression); - spin_lock_init(&tcp_ses->req_lock); - spin_lock_init(&tcp_ses->srv_lock); - spin_lock_init(&tcp_ses->mid_lock); - INIT_LIST_HEAD(&tcp_ses->tcp_ses_list); - INIT_LIST_HEAD(&tcp_ses->smb_ses_list); - INIT_DELAYED_WORK(&tcp_ses->echo, cifs_echo_request); - INIT_DELAYED_WORK(&tcp_ses->resolve, cifs_resolve_server); - INIT_DELAYED_WORK(&tcp_ses->reconnect, smb2_reconnect_server); - mutex_init(&tcp_ses->reconnect_mutex); -#ifdef CONFIG_CIFS_DFS_UPCALL - mutex_init(&tcp_ses->refpath_lock); -#endif - memcpy(&tcp_ses->srcaddr, &ctx->srcaddr, - sizeof(tcp_ses->srcaddr)); - memcpy(&tcp_ses->dstaddr, &ctx->dstaddr, - sizeof(tcp_ses->dstaddr)); - if (ctx->use_client_guid) - memcpy(tcp_ses->client_guid, ctx->client_guid, - SMB2_CLIENT_GUID_SIZE); - else - generate_random_uuid(tcp_ses->client_guid); - /* - * at this point we are the only ones with the pointer - * to the struct since the kernel thread not created yet - * no need to spinlock this init of tcpStatus or srv_count - */ - tcp_ses->tcpStatus = CifsNew; - ++tcp_ses->srv_count; - - if (ctx->echo_interval >= SMB_ECHO_INTERVAL_MIN && - ctx->echo_interval <= SMB_ECHO_INTERVAL_MAX) - tcp_ses->echo_interval = ctx->echo_interval * HZ; - else - tcp_ses->echo_interval = SMB_ECHO_INTERVAL_DEFAULT * HZ; - if (tcp_ses->rdma) { -#ifndef CONFIG_CIFS_SMB_DIRECT - cifs_dbg(VFS, "CONFIG_CIFS_SMB_DIRECT is not enabled\n"); - rc = -ENOENT; - goto out_err_crypto_release; -#endif - tcp_ses->smbd_conn = smbd_get_connection( - tcp_ses, (struct sockaddr *)&ctx->dstaddr); - if (tcp_ses->smbd_conn) { - cifs_dbg(VFS, "RDMA transport established\n"); - rc = 0; - goto smbd_connected; - } else { - rc = -ENOENT; - goto out_err_crypto_release; - } - } - rc = ip_connect(tcp_ses); - if (rc < 0) { - cifs_dbg(VFS, "Error connecting to socket. Aborting operation.\n"); - goto out_err_crypto_release; - } -smbd_connected: - /* - * since we're in a cifs function already, we know that - * this will succeed. No need for try_module_get(). - */ - __module_get(THIS_MODULE); - tcp_ses->tsk = kthread_run(cifs_demultiplex_thread, - tcp_ses, "cifsd"); - if (IS_ERR(tcp_ses->tsk)) { - rc = PTR_ERR(tcp_ses->tsk); - cifs_dbg(VFS, "error %d create cifsd thread\n", rc); - module_put(THIS_MODULE); - goto out_err_crypto_release; - } - tcp_ses->min_offload = ctx->min_offload; - /* - * at this point we are the only ones with the pointer - * to the struct since the kernel thread not created yet - * no need to spinlock this update of tcpStatus - */ - spin_lock(&tcp_ses->srv_lock); - tcp_ses->tcpStatus = CifsNeedNegotiate; - spin_unlock(&tcp_ses->srv_lock); - - if ((ctx->max_credits < 20) || (ctx->max_credits > 60000)) - tcp_ses->max_credits = SMB2_MAX_CREDITS_AVAILABLE; - else - tcp_ses->max_credits = ctx->max_credits; - - tcp_ses->nr_targets = 1; - tcp_ses->ignore_signature = ctx->ignore_signature; - /* thread spawned, put it on the list */ - spin_lock(&cifs_tcp_ses_lock); - list_add(&tcp_ses->tcp_ses_list, &cifs_tcp_ses_list); - spin_unlock(&cifs_tcp_ses_lock); - - /* queue echo request delayed work */ - queue_delayed_work(cifsiod_wq, &tcp_ses->echo, tcp_ses->echo_interval); - - /* queue dns resolution delayed work */ - cifs_dbg(FYI, "%s: next dns resolution scheduled for %d seconds in the future\n", - __func__, SMB_DNS_RESOLVE_INTERVAL_DEFAULT); - - queue_delayed_work(cifsiod_wq, &tcp_ses->resolve, (SMB_DNS_RESOLVE_INTERVAL_DEFAULT * HZ)); - - return tcp_ses; - -out_err_crypto_release: - cifs_crypto_secmech_release(tcp_ses); - - put_net(cifs_net_ns(tcp_ses)); - -out_err: - if (tcp_ses) { - if (CIFS_SERVER_IS_CHAN(tcp_ses)) - cifs_put_tcp_session(tcp_ses->primary_server, false); - kfree(tcp_ses->hostname); - if (tcp_ses->ssocket) - sock_release(tcp_ses->ssocket); - kfree(tcp_ses); - } - return ERR_PTR(rc); -} - -/* this function must be called with ses_lock and chan_lock held */ -static int match_session(struct cifs_ses *ses, struct smb3_fs_context *ctx) -{ - if (ctx->sectype != Unspecified && - ctx->sectype != ses->sectype) - return 0; - - /* - * If an existing session is limited to less channels than - * requested, it should not be reused - */ - if (ses->chan_max < ctx->max_channels) - return 0; - - switch (ses->sectype) { - case Kerberos: - if (!uid_eq(ctx->cred_uid, ses->cred_uid)) - return 0; - break; - default: - /* NULL username means anonymous session */ - if (ses->user_name == NULL) { - if (!ctx->nullauth) - return 0; - break; - } - - /* anything else takes username/password */ - if (strncmp(ses->user_name, - ctx->username ? ctx->username : "", - CIFS_MAX_USERNAME_LEN)) - return 0; - if ((ctx->username && strlen(ctx->username) != 0) && - ses->password != NULL && - strncmp(ses->password, - ctx->password ? ctx->password : "", - CIFS_MAX_PASSWORD_LEN)) - return 0; - } - return 1; -} - -/** - * cifs_setup_ipc - helper to setup the IPC tcon for the session - * @ses: smb session to issue the request on - * @ctx: the superblock configuration context to use for building the - * new tree connection for the IPC (interprocess communication RPC) - * - * A new IPC connection is made and stored in the session - * tcon_ipc. The IPC tcon has the same lifetime as the session. - */ -static int -cifs_setup_ipc(struct cifs_ses *ses, struct smb3_fs_context *ctx) -{ - int rc = 0, xid; - struct cifs_tcon *tcon; - char unc[SERVER_NAME_LENGTH + sizeof("//x/IPC$")] = {0}; - bool seal = false; - struct TCP_Server_Info *server = ses->server; - - /* - * If the mount request that resulted in the creation of the - * session requires encryption, force IPC to be encrypted too. - */ - if (ctx->seal) { - if (server->capabilities & SMB2_GLOBAL_CAP_ENCRYPTION) - seal = true; - else { - cifs_server_dbg(VFS, - "IPC: server doesn't support encryption\n"); - return -EOPNOTSUPP; - } - } - - tcon = tconInfoAlloc(); - if (tcon == NULL) - return -ENOMEM; - - spin_lock(&server->srv_lock); - scnprintf(unc, sizeof(unc), "\\\\%s\\IPC$", server->hostname); - spin_unlock(&server->srv_lock); - - xid = get_xid(); - tcon->ses = ses; - tcon->ipc = true; - tcon->seal = seal; - rc = server->ops->tree_connect(xid, ses, unc, tcon, ctx->local_nls); - free_xid(xid); - - if (rc) { - cifs_server_dbg(VFS, "failed to connect to IPC (rc=%d)\n", rc); - tconInfoFree(tcon); - goto out; - } - - cifs_dbg(FYI, "IPC tcon rc=%d ipc tid=0x%x\n", rc, tcon->tid); - - spin_lock(&tcon->tc_lock); - tcon->status = TID_GOOD; - spin_unlock(&tcon->tc_lock); - ses->tcon_ipc = tcon; -out: - return rc; -} - -/** - * cifs_free_ipc - helper to release the session IPC tcon - * @ses: smb session to unmount the IPC from - * - * Needs to be called everytime a session is destroyed. - * - * On session close, the IPC is closed and the server must release all tcons of the session. - * No need to send a tree disconnect here. - * - * Besides, it will make the server to not close durable and resilient files on session close, as - * specified in MS-SMB2 3.3.5.6 Receiving an SMB2 LOGOFF Request. - */ -static int -cifs_free_ipc(struct cifs_ses *ses) -{ - struct cifs_tcon *tcon = ses->tcon_ipc; - - if (tcon == NULL) - return 0; - - tconInfoFree(tcon); - ses->tcon_ipc = NULL; - return 0; -} - -static struct cifs_ses * -cifs_find_smb_ses(struct TCP_Server_Info *server, struct smb3_fs_context *ctx) -{ - struct cifs_ses *ses; - - spin_lock(&cifs_tcp_ses_lock); - list_for_each_entry(ses, &server->smb_ses_list, smb_ses_list) { - spin_lock(&ses->ses_lock); - if (ses->ses_status == SES_EXITING) { - spin_unlock(&ses->ses_lock); - continue; - } - spin_lock(&ses->chan_lock); - if (!match_session(ses, ctx)) { - spin_unlock(&ses->chan_lock); - spin_unlock(&ses->ses_lock); - continue; - } - spin_unlock(&ses->chan_lock); - spin_unlock(&ses->ses_lock); - - ++ses->ses_count; - spin_unlock(&cifs_tcp_ses_lock); - return ses; - } - spin_unlock(&cifs_tcp_ses_lock); - return NULL; -} - -void cifs_put_smb_ses(struct cifs_ses *ses) -{ - unsigned int rc, xid; - unsigned int chan_count; - struct TCP_Server_Info *server = ses->server; - - spin_lock(&ses->ses_lock); - if (ses->ses_status == SES_EXITING) { - spin_unlock(&ses->ses_lock); - return; - } - spin_unlock(&ses->ses_lock); - - cifs_dbg(FYI, "%s: ses_count=%d\n", __func__, ses->ses_count); - cifs_dbg(FYI, - "%s: ses ipc: %s\n", __func__, ses->tcon_ipc ? ses->tcon_ipc->tree_name : "NONE"); - - spin_lock(&cifs_tcp_ses_lock); - if (--ses->ses_count > 0) { - spin_unlock(&cifs_tcp_ses_lock); - return; - } - spin_unlock(&cifs_tcp_ses_lock); - - /* ses_count can never go negative */ - WARN_ON(ses->ses_count < 0); - - if (ses->ses_status == SES_GOOD) - ses->ses_status = SES_EXITING; - - cifs_free_ipc(ses); - - if (ses->ses_status == SES_EXITING && server->ops->logoff) { - xid = get_xid(); - rc = server->ops->logoff(xid, ses); - if (rc) - cifs_server_dbg(VFS, "%s: Session Logoff failure rc=%d\n", - __func__, rc); - _free_xid(xid); - } - - spin_lock(&cifs_tcp_ses_lock); - list_del_init(&ses->smb_ses_list); - spin_unlock(&cifs_tcp_ses_lock); - - chan_count = ses->chan_count; - - /* close any extra channels */ - if (chan_count > 1) { - int i; - - for (i = 1; i < chan_count; i++) { - if (ses->chans[i].iface) { - kref_put(&ses->chans[i].iface->refcount, release_iface); - ses->chans[i].iface = NULL; - } - cifs_put_tcp_session(ses->chans[i].server, 0); - ses->chans[i].server = NULL; - } - } - - sesInfoFree(ses); - cifs_put_tcp_session(server, 0); -} - -#ifdef CONFIG_KEYS - -/* strlen("cifs:a:") + CIFS_MAX_DOMAINNAME_LEN + 1 */ -#define CIFSCREDS_DESC_SIZE (7 + CIFS_MAX_DOMAINNAME_LEN + 1) - -/* Populate username and pw fields from keyring if possible */ -static int -cifs_set_cifscreds(struct smb3_fs_context *ctx, struct cifs_ses *ses) -{ - int rc = 0; - int is_domain = 0; - const char *delim, *payload; - char *desc; - ssize_t len; - struct key *key; - struct TCP_Server_Info *server = ses->server; - struct sockaddr_in *sa; - struct sockaddr_in6 *sa6; - const struct user_key_payload *upayload; - - desc = kmalloc(CIFSCREDS_DESC_SIZE, GFP_KERNEL); - if (!desc) - return -ENOMEM; - - /* try to find an address key first */ - switch (server->dstaddr.ss_family) { - case AF_INET: - sa = (struct sockaddr_in *)&server->dstaddr; - sprintf(desc, "cifs:a:%pI4", &sa->sin_addr.s_addr); - break; - case AF_INET6: - sa6 = (struct sockaddr_in6 *)&server->dstaddr; - sprintf(desc, "cifs:a:%pI6c", &sa6->sin6_addr.s6_addr); - break; - default: - cifs_dbg(FYI, "Bad ss_family (%hu)\n", - server->dstaddr.ss_family); - rc = -EINVAL; - goto out_err; - } - - cifs_dbg(FYI, "%s: desc=%s\n", __func__, desc); - key = request_key(&key_type_logon, desc, ""); - if (IS_ERR(key)) { - if (!ses->domainName) { - cifs_dbg(FYI, "domainName is NULL\n"); - rc = PTR_ERR(key); - goto out_err; - } - - /* didn't work, try to find a domain key */ - sprintf(desc, "cifs:d:%s", ses->domainName); - cifs_dbg(FYI, "%s: desc=%s\n", __func__, desc); - key = request_key(&key_type_logon, desc, ""); - if (IS_ERR(key)) { - rc = PTR_ERR(key); - goto out_err; - } - is_domain = 1; - } - - down_read(&key->sem); - upayload = user_key_payload_locked(key); - if (IS_ERR_OR_NULL(upayload)) { - rc = upayload ? PTR_ERR(upayload) : -EINVAL; - goto out_key_put; - } - - /* find first : in payload */ - payload = upayload->data; - delim = strnchr(payload, upayload->datalen, ':'); - cifs_dbg(FYI, "payload=%s\n", payload); - if (!delim) { - cifs_dbg(FYI, "Unable to find ':' in payload (datalen=%d)\n", - upayload->datalen); - rc = -EINVAL; - goto out_key_put; - } - - len = delim - payload; - if (len > CIFS_MAX_USERNAME_LEN || len <= 0) { - cifs_dbg(FYI, "Bad value from username search (len=%zd)\n", - len); - rc = -EINVAL; - goto out_key_put; - } - - ctx->username = kstrndup(payload, len, GFP_KERNEL); - if (!ctx->username) { - cifs_dbg(FYI, "Unable to allocate %zd bytes for username\n", - len); - rc = -ENOMEM; - goto out_key_put; - } - cifs_dbg(FYI, "%s: username=%s\n", __func__, ctx->username); - - len = key->datalen - (len + 1); - if (len > CIFS_MAX_PASSWORD_LEN || len <= 0) { - cifs_dbg(FYI, "Bad len for password search (len=%zd)\n", len); - rc = -EINVAL; - kfree(ctx->username); - ctx->username = NULL; - goto out_key_put; - } - - ++delim; - ctx->password = kstrndup(delim, len, GFP_KERNEL); - if (!ctx->password) { - cifs_dbg(FYI, "Unable to allocate %zd bytes for password\n", - len); - rc = -ENOMEM; - kfree(ctx->username); - ctx->username = NULL; - goto out_key_put; - } - - /* - * If we have a domain key then we must set the domainName in the - * for the request. - */ - if (is_domain && ses->domainName) { - ctx->domainname = kstrdup(ses->domainName, GFP_KERNEL); - if (!ctx->domainname) { - cifs_dbg(FYI, "Unable to allocate %zd bytes for domain\n", - len); - rc = -ENOMEM; - kfree(ctx->username); - ctx->username = NULL; - kfree_sensitive(ctx->password); - ctx->password = NULL; - goto out_key_put; - } - } - - strscpy(ctx->workstation_name, ses->workstation_name, sizeof(ctx->workstation_name)); - -out_key_put: - up_read(&key->sem); - key_put(key); -out_err: - kfree(desc); - cifs_dbg(FYI, "%s: returning %d\n", __func__, rc); - return rc; -} -#else /* ! CONFIG_KEYS */ -static inline int -cifs_set_cifscreds(struct smb3_fs_context *ctx __attribute__((unused)), - struct cifs_ses *ses __attribute__((unused))) -{ - return -ENOSYS; -} -#endif /* CONFIG_KEYS */ - -/** - * cifs_get_smb_ses - get a session matching @ctx data from @server - * @server: server to setup the session to - * @ctx: superblock configuration context to use to setup the session - * - * This function assumes it is being called from cifs_mount() where we - * already got a server reference (server refcount +1). See - * cifs_get_tcon() for refcount explanations. - */ -struct cifs_ses * -cifs_get_smb_ses(struct TCP_Server_Info *server, struct smb3_fs_context *ctx) -{ - int rc = 0; - unsigned int xid; - struct cifs_ses *ses; - struct sockaddr_in *addr = (struct sockaddr_in *)&server->dstaddr; - struct sockaddr_in6 *addr6 = (struct sockaddr_in6 *)&server->dstaddr; - - xid = get_xid(); - - ses = cifs_find_smb_ses(server, ctx); - if (ses) { - cifs_dbg(FYI, "Existing smb sess found (status=%d)\n", - ses->ses_status); - - spin_lock(&ses->chan_lock); - if (cifs_chan_needs_reconnect(ses, server)) { - spin_unlock(&ses->chan_lock); - cifs_dbg(FYI, "Session needs reconnect\n"); - - mutex_lock(&ses->session_mutex); - rc = cifs_negotiate_protocol(xid, ses, server); - if (rc) { - mutex_unlock(&ses->session_mutex); - /* problem -- put our ses reference */ - cifs_put_smb_ses(ses); - free_xid(xid); - return ERR_PTR(rc); - } - - rc = cifs_setup_session(xid, ses, server, - ctx->local_nls); - if (rc) { - mutex_unlock(&ses->session_mutex); - /* problem -- put our reference */ - cifs_put_smb_ses(ses); - free_xid(xid); - return ERR_PTR(rc); - } - mutex_unlock(&ses->session_mutex); - - spin_lock(&ses->chan_lock); - } - spin_unlock(&ses->chan_lock); - - /* existing SMB ses has a server reference already */ - cifs_put_tcp_session(server, 0); - free_xid(xid); - return ses; - } - - rc = -ENOMEM; - - cifs_dbg(FYI, "Existing smb sess not found\n"); - ses = sesInfoAlloc(); - if (ses == NULL) - goto get_ses_fail; - - /* new SMB session uses our server ref */ - ses->server = server; - if (server->dstaddr.ss_family == AF_INET6) - sprintf(ses->ip_addr, "%pI6", &addr6->sin6_addr); - else - sprintf(ses->ip_addr, "%pI4", &addr->sin_addr); - - if (ctx->username) { - ses->user_name = kstrdup(ctx->username, GFP_KERNEL); - if (!ses->user_name) - goto get_ses_fail; - } - - /* ctx->password freed at unmount */ - if (ctx->password) { - ses->password = kstrdup(ctx->password, GFP_KERNEL); - if (!ses->password) - goto get_ses_fail; - } - if (ctx->domainname) { - ses->domainName = kstrdup(ctx->domainname, GFP_KERNEL); - if (!ses->domainName) - goto get_ses_fail; - } - - strscpy(ses->workstation_name, ctx->workstation_name, sizeof(ses->workstation_name)); - - if (ctx->domainauto) - ses->domainAuto = ctx->domainauto; - ses->cred_uid = ctx->cred_uid; - ses->linux_uid = ctx->linux_uid; - - ses->sectype = ctx->sectype; - ses->sign = ctx->sign; - - /* add server as first channel */ - spin_lock(&ses->chan_lock); - ses->chans[0].server = server; - ses->chan_count = 1; - ses->chan_max = ctx->multichannel ? ctx->max_channels:1; - ses->chans_need_reconnect = 1; - spin_unlock(&ses->chan_lock); - - mutex_lock(&ses->session_mutex); - rc = cifs_negotiate_protocol(xid, ses, server); - if (!rc) - rc = cifs_setup_session(xid, ses, server, ctx->local_nls); - mutex_unlock(&ses->session_mutex); - - /* each channel uses a different signing key */ - spin_lock(&ses->chan_lock); - memcpy(ses->chans[0].signkey, ses->smb3signingkey, - sizeof(ses->smb3signingkey)); - spin_unlock(&ses->chan_lock); - - if (rc) - goto get_ses_fail; - - /* - * success, put it on the list and add it as first channel - * note: the session becomes active soon after this. So you'll - * need to lock before changing something in the session. - */ - spin_lock(&cifs_tcp_ses_lock); - list_add(&ses->smb_ses_list, &server->smb_ses_list); - spin_unlock(&cifs_tcp_ses_lock); - - cifs_setup_ipc(ses, ctx); - - free_xid(xid); - - return ses; - -get_ses_fail: - sesInfoFree(ses); - free_xid(xid); - return ERR_PTR(rc); -} - -/* this function must be called with tc_lock held */ -static int match_tcon(struct cifs_tcon *tcon, struct smb3_fs_context *ctx) -{ - if (tcon->status == TID_EXITING) - return 0; - if (strncmp(tcon->tree_name, ctx->UNC, MAX_TREE_SIZE)) - return 0; - if (tcon->seal != ctx->seal) - return 0; - if (tcon->snapshot_time != ctx->snapshot_time) - return 0; - if (tcon->handle_timeout != ctx->handle_timeout) - return 0; - if (tcon->no_lease != ctx->no_lease) - return 0; - if (tcon->nodelete != ctx->nodelete) - return 0; - return 1; -} - -static struct cifs_tcon * -cifs_find_tcon(struct cifs_ses *ses, struct smb3_fs_context *ctx) -{ - struct cifs_tcon *tcon; - - spin_lock(&cifs_tcp_ses_lock); - list_for_each_entry(tcon, &ses->tcon_list, tcon_list) { - spin_lock(&tcon->tc_lock); - if (!match_tcon(tcon, ctx)) { - spin_unlock(&tcon->tc_lock); - continue; - } - ++tcon->tc_count; - spin_unlock(&tcon->tc_lock); - spin_unlock(&cifs_tcp_ses_lock); - return tcon; - } - spin_unlock(&cifs_tcp_ses_lock); - return NULL; -} - -void -cifs_put_tcon(struct cifs_tcon *tcon) -{ - unsigned int xid; - struct cifs_ses *ses; - - /* - * IPC tcon share the lifetime of their session and are - * destroyed in the session put function - */ - if (tcon == NULL || tcon->ipc) - return; - - ses = tcon->ses; - cifs_dbg(FYI, "%s: tc_count=%d\n", __func__, tcon->tc_count); - spin_lock(&cifs_tcp_ses_lock); - spin_lock(&tcon->tc_lock); - if (--tcon->tc_count > 0) { - spin_unlock(&tcon->tc_lock); - spin_unlock(&cifs_tcp_ses_lock); - return; - } - - /* tc_count can never go negative */ - WARN_ON(tcon->tc_count < 0); - - list_del_init(&tcon->tcon_list); - tcon->status = TID_EXITING; - spin_unlock(&tcon->tc_lock); - spin_unlock(&cifs_tcp_ses_lock); - - /* cancel polling of interfaces */ - cancel_delayed_work_sync(&tcon->query_interfaces); - - if (tcon->use_witness) { - int rc; - - rc = cifs_swn_unregister(tcon); - if (rc < 0) { - cifs_dbg(VFS, "%s: Failed to unregister for witness notifications: %d\n", - __func__, rc); - } - } - - xid = get_xid(); - if (ses->server->ops->tree_disconnect) - ses->server->ops->tree_disconnect(xid, tcon); - _free_xid(xid); - - cifs_fscache_release_super_cookie(tcon); - tconInfoFree(tcon); - cifs_put_smb_ses(ses); -} - -/** - * cifs_get_tcon - get a tcon matching @ctx data from @ses - * @ses: smb session to issue the request on - * @ctx: the superblock configuration context to use for building the - * - * - tcon refcount is the number of mount points using the tcon. - * - ses refcount is the number of tcon using the session. - * - * 1. This function assumes it is being called from cifs_mount() where - * we already got a session reference (ses refcount +1). - * - * 2. Since we're in the context of adding a mount point, the end - * result should be either: - * - * a) a new tcon already allocated with refcount=1 (1 mount point) and - * its session refcount incremented (1 new tcon). This +1 was - * already done in (1). - * - * b) an existing tcon with refcount+1 (add a mount point to it) and - * identical ses refcount (no new tcon). Because of (1) we need to - * decrement the ses refcount. - */ -static struct cifs_tcon * -cifs_get_tcon(struct cifs_ses *ses, struct smb3_fs_context *ctx) -{ - int rc, xid; - struct cifs_tcon *tcon; - - tcon = cifs_find_tcon(ses, ctx); - if (tcon) { - /* - * tcon has refcount already incremented but we need to - * decrement extra ses reference gotten by caller (case b) - */ - cifs_dbg(FYI, "Found match on UNC path\n"); - cifs_put_smb_ses(ses); - return tcon; - } - - if (!ses->server->ops->tree_connect) { - rc = -ENOSYS; - goto out_fail; - } - - tcon = tconInfoAlloc(); - if (tcon == NULL) { - rc = -ENOMEM; - goto out_fail; - } - - if (ctx->snapshot_time) { - if (ses->server->vals->protocol_id == 0) { - cifs_dbg(VFS, - "Use SMB2 or later for snapshot mount option\n"); - rc = -EOPNOTSUPP; - goto out_fail; - } else - tcon->snapshot_time = ctx->snapshot_time; - } - - if (ctx->handle_timeout) { - if (ses->server->vals->protocol_id == 0) { - cifs_dbg(VFS, - "Use SMB2.1 or later for handle timeout option\n"); - rc = -EOPNOTSUPP; - goto out_fail; - } else - tcon->handle_timeout = ctx->handle_timeout; - } - - tcon->ses = ses; - if (ctx->password) { - tcon->password = kstrdup(ctx->password, GFP_KERNEL); - if (!tcon->password) { - rc = -ENOMEM; - goto out_fail; - } - } - - if (ctx->seal) { - if (ses->server->vals->protocol_id == 0) { - cifs_dbg(VFS, - "SMB3 or later required for encryption\n"); - rc = -EOPNOTSUPP; - goto out_fail; - } else if (tcon->ses->server->capabilities & - SMB2_GLOBAL_CAP_ENCRYPTION) - tcon->seal = true; - else { - cifs_dbg(VFS, "Encryption is not supported on share\n"); - rc = -EOPNOTSUPP; - goto out_fail; - } - } - - if (ctx->linux_ext) { - if (ses->server->posix_ext_supported) { - tcon->posix_extensions = true; - pr_warn_once("SMB3.11 POSIX Extensions are experimental\n"); - } else if ((ses->server->vals->protocol_id == SMB311_PROT_ID) || - (strcmp(ses->server->vals->version_string, - SMB3ANY_VERSION_STRING) == 0) || - (strcmp(ses->server->vals->version_string, - SMBDEFAULT_VERSION_STRING) == 0)) { - cifs_dbg(VFS, "Server does not support mounting with posix SMB3.11 extensions\n"); - rc = -EOPNOTSUPP; - goto out_fail; - } else { - cifs_dbg(VFS, "Check vers= mount option. SMB3.11 " - "disabled but required for POSIX extensions\n"); - rc = -EOPNOTSUPP; - goto out_fail; - } - } - - xid = get_xid(); - rc = ses->server->ops->tree_connect(xid, ses, ctx->UNC, tcon, - ctx->local_nls); - free_xid(xid); - cifs_dbg(FYI, "Tcon rc = %d\n", rc); - if (rc) - goto out_fail; - - tcon->use_persistent = false; - /* check if SMB2 or later, CIFS does not support persistent handles */ - if (ctx->persistent) { - if (ses->server->vals->protocol_id == 0) { - cifs_dbg(VFS, - "SMB3 or later required for persistent handles\n"); - rc = -EOPNOTSUPP; - goto out_fail; - } else if (ses->server->capabilities & - SMB2_GLOBAL_CAP_PERSISTENT_HANDLES) - tcon->use_persistent = true; - else /* persistent handles requested but not supported */ { - cifs_dbg(VFS, - "Persistent handles not supported on share\n"); - rc = -EOPNOTSUPP; - goto out_fail; - } - } else if ((tcon->capabilities & SMB2_SHARE_CAP_CONTINUOUS_AVAILABILITY) - && (ses->server->capabilities & SMB2_GLOBAL_CAP_PERSISTENT_HANDLES) - && (ctx->nopersistent == false)) { - cifs_dbg(FYI, "enabling persistent handles\n"); - tcon->use_persistent = true; - } else if (ctx->resilient) { - if (ses->server->vals->protocol_id == 0) { - cifs_dbg(VFS, - "SMB2.1 or later required for resilient handles\n"); - rc = -EOPNOTSUPP; - goto out_fail; - } - tcon->use_resilient = true; - } - - tcon->use_witness = false; - if (IS_ENABLED(CONFIG_CIFS_SWN_UPCALL) && ctx->witness) { - if (ses->server->vals->protocol_id >= SMB30_PROT_ID) { - if (tcon->capabilities & SMB2_SHARE_CAP_CLUSTER) { - /* - * Set witness in use flag in first place - * to retry registration in the echo task - */ - tcon->use_witness = true; - /* And try to register immediately */ - rc = cifs_swn_register(tcon); - if (rc < 0) { - cifs_dbg(VFS, "Failed to register for witness notifications: %d\n", rc); - goto out_fail; - } - } else { - /* TODO: try to extend for non-cluster uses (eg multichannel) */ - cifs_dbg(VFS, "witness requested on mount but no CLUSTER capability on share\n"); - rc = -EOPNOTSUPP; - goto out_fail; - } - } else { - cifs_dbg(VFS, "SMB3 or later required for witness option\n"); - rc = -EOPNOTSUPP; - goto out_fail; - } - } - - /* If the user really knows what they are doing they can override */ - if (tcon->share_flags & SMB2_SHAREFLAG_NO_CACHING) { - if (ctx->cache_ro) - cifs_dbg(VFS, "cache=ro requested on mount but NO_CACHING flag set on share\n"); - else if (ctx->cache_rw) - cifs_dbg(VFS, "cache=singleclient requested on mount but NO_CACHING flag set on share\n"); - } - - if (ctx->no_lease) { - if (ses->server->vals->protocol_id == 0) { - cifs_dbg(VFS, - "SMB2 or later required for nolease option\n"); - rc = -EOPNOTSUPP; - goto out_fail; - } else - tcon->no_lease = ctx->no_lease; - } - - /* - * We can have only one retry value for a connection to a share so for - * resources mounted more than once to the same server share the last - * value passed in for the retry flag is used. - */ - tcon->retry = ctx->retry; - tcon->nocase = ctx->nocase; - tcon->broken_sparse_sup = ctx->no_sparse; - if (ses->server->capabilities & SMB2_GLOBAL_CAP_DIRECTORY_LEASING) - tcon->nohandlecache = ctx->nohandlecache; - else - tcon->nohandlecache = true; - tcon->nodelete = ctx->nodelete; - tcon->local_lease = ctx->local_lease; - INIT_LIST_HEAD(&tcon->pending_opens); - tcon->status = TID_GOOD; - - INIT_DELAYED_WORK(&tcon->query_interfaces, - smb2_query_server_interfaces); - if (ses->server->dialect >= SMB30_PROT_ID && - (ses->server->capabilities & SMB2_GLOBAL_CAP_MULTI_CHANNEL)) { - /* schedule query interfaces poll */ - queue_delayed_work(cifsiod_wq, &tcon->query_interfaces, - (SMB_INTERFACE_POLL_INTERVAL * HZ)); - } - - spin_lock(&cifs_tcp_ses_lock); - list_add(&tcon->tcon_list, &ses->tcon_list); - spin_unlock(&cifs_tcp_ses_lock); - - return tcon; - -out_fail: - tconInfoFree(tcon); - return ERR_PTR(rc); -} - -void -cifs_put_tlink(struct tcon_link *tlink) -{ - if (!tlink || IS_ERR(tlink)) - return; - - if (!atomic_dec_and_test(&tlink->tl_count) || - test_bit(TCON_LINK_IN_TREE, &tlink->tl_flags)) { - tlink->tl_time = jiffies; - return; - } - - if (!IS_ERR(tlink_tcon(tlink))) - cifs_put_tcon(tlink_tcon(tlink)); - kfree(tlink); - return; -} - -static int -compare_mount_options(struct super_block *sb, struct cifs_mnt_data *mnt_data) -{ - struct cifs_sb_info *old = CIFS_SB(sb); - struct cifs_sb_info *new = mnt_data->cifs_sb; - unsigned int oldflags = old->mnt_cifs_flags & CIFS_MOUNT_MASK; - unsigned int newflags = new->mnt_cifs_flags & CIFS_MOUNT_MASK; - - if ((sb->s_flags & CIFS_MS_MASK) != (mnt_data->flags & CIFS_MS_MASK)) - return 0; - - if (old->mnt_cifs_serverino_autodisabled) - newflags &= ~CIFS_MOUNT_SERVER_INUM; - - if (oldflags != newflags) - return 0; - - /* - * We want to share sb only if we don't specify an r/wsize or - * specified r/wsize is greater than or equal to existing one. - */ - if (new->ctx->wsize && new->ctx->wsize < old->ctx->wsize) - return 0; - - if (new->ctx->rsize && new->ctx->rsize < old->ctx->rsize) - return 0; - - if (!uid_eq(old->ctx->linux_uid, new->ctx->linux_uid) || - !gid_eq(old->ctx->linux_gid, new->ctx->linux_gid)) - return 0; - - if (old->ctx->file_mode != new->ctx->file_mode || - old->ctx->dir_mode != new->ctx->dir_mode) - return 0; - - if (strcmp(old->local_nls->charset, new->local_nls->charset)) - return 0; - - if (old->ctx->acregmax != new->ctx->acregmax) - return 0; - if (old->ctx->acdirmax != new->ctx->acdirmax) - return 0; - if (old->ctx->closetimeo != new->ctx->closetimeo) - return 0; - - return 1; -} - -static int -match_prepath(struct super_block *sb, struct cifs_mnt_data *mnt_data) -{ - struct cifs_sb_info *old = CIFS_SB(sb); - struct cifs_sb_info *new = mnt_data->cifs_sb; - bool old_set = (old->mnt_cifs_flags & CIFS_MOUNT_USE_PREFIX_PATH) && - old->prepath; - bool new_set = (new->mnt_cifs_flags & CIFS_MOUNT_USE_PREFIX_PATH) && - new->prepath; - - if (old_set && new_set && !strcmp(new->prepath, old->prepath)) - return 1; - else if (!old_set && !new_set) - return 1; - - return 0; -} - -int -cifs_match_super(struct super_block *sb, void *data) -{ - struct cifs_mnt_data *mnt_data = data; - struct smb3_fs_context *ctx; - struct cifs_sb_info *cifs_sb; - struct TCP_Server_Info *tcp_srv; - struct cifs_ses *ses; - struct cifs_tcon *tcon; - struct tcon_link *tlink; - int rc = 0; - - spin_lock(&cifs_tcp_ses_lock); - cifs_sb = CIFS_SB(sb); - - /* We do not want to use a superblock that has been shutdown */ - if (CIFS_MOUNT_SHUTDOWN & cifs_sb->mnt_cifs_flags) { - spin_unlock(&cifs_tcp_ses_lock); - return 0; - } - - tlink = cifs_get_tlink(cifs_sb_master_tlink(cifs_sb)); - if (tlink == NULL) { - /* can not match superblock if tlink were ever null */ - spin_unlock(&cifs_tcp_ses_lock); - return 0; - } - tcon = tlink_tcon(tlink); - ses = tcon->ses; - tcp_srv = ses->server; - - ctx = mnt_data->ctx; - - spin_lock(&tcp_srv->srv_lock); - spin_lock(&ses->ses_lock); - spin_lock(&ses->chan_lock); - spin_lock(&tcon->tc_lock); - if (!match_server(tcp_srv, ctx) || - !match_session(ses, ctx) || - !match_tcon(tcon, ctx) || - !match_prepath(sb, mnt_data)) { - rc = 0; - goto out; - } - - rc = compare_mount_options(sb, mnt_data); -out: - spin_unlock(&tcon->tc_lock); - spin_unlock(&ses->chan_lock); - spin_unlock(&ses->ses_lock); - spin_unlock(&tcp_srv->srv_lock); - - spin_unlock(&cifs_tcp_ses_lock); - cifs_put_tlink(tlink); - return rc; -} - -#ifdef CONFIG_DEBUG_LOCK_ALLOC -static struct lock_class_key cifs_key[2]; -static struct lock_class_key cifs_slock_key[2]; - -static inline void -cifs_reclassify_socket4(struct socket *sock) -{ - struct sock *sk = sock->sk; - BUG_ON(!sock_allow_reclassification(sk)); - sock_lock_init_class_and_name(sk, "slock-AF_INET-CIFS", - &cifs_slock_key[0], "sk_lock-AF_INET-CIFS", &cifs_key[0]); -} - -static inline void -cifs_reclassify_socket6(struct socket *sock) -{ - struct sock *sk = sock->sk; - BUG_ON(!sock_allow_reclassification(sk)); - sock_lock_init_class_and_name(sk, "slock-AF_INET6-CIFS", - &cifs_slock_key[1], "sk_lock-AF_INET6-CIFS", &cifs_key[1]); -} -#else -static inline void -cifs_reclassify_socket4(struct socket *sock) -{ -} - -static inline void -cifs_reclassify_socket6(struct socket *sock) -{ -} -#endif - -/* See RFC1001 section 14 on representation of Netbios names */ -static void rfc1002mangle(char *target, char *source, unsigned int length) -{ - unsigned int i, j; - - for (i = 0, j = 0; i < (length); i++) { - /* mask a nibble at a time and encode */ - target[j] = 'A' + (0x0F & (source[i] >> 4)); - target[j+1] = 'A' + (0x0F & source[i]); - j += 2; - } - -} - -static int -bind_socket(struct TCP_Server_Info *server) -{ - int rc = 0; - if (server->srcaddr.ss_family != AF_UNSPEC) { - /* Bind to the specified local IP address */ - struct socket *socket = server->ssocket; - rc = socket->ops->bind(socket, - (struct sockaddr *) &server->srcaddr, - sizeof(server->srcaddr)); - if (rc < 0) { - struct sockaddr_in *saddr4; - struct sockaddr_in6 *saddr6; - saddr4 = (struct sockaddr_in *)&server->srcaddr; - saddr6 = (struct sockaddr_in6 *)&server->srcaddr; - if (saddr6->sin6_family == AF_INET6) - cifs_server_dbg(VFS, "Failed to bind to: %pI6c, error: %d\n", - &saddr6->sin6_addr, rc); - else - cifs_server_dbg(VFS, "Failed to bind to: %pI4, error: %d\n", - &saddr4->sin_addr.s_addr, rc); - } - } - return rc; -} - -static int -ip_rfc1001_connect(struct TCP_Server_Info *server) -{ - int rc = 0; - /* - * some servers require RFC1001 sessinit before sending - * negprot - BB check reconnection in case where second - * sessinit is sent but no second negprot - */ - struct rfc1002_session_packet req = {}; - struct smb_hdr *smb_buf = (struct smb_hdr *)&req; - unsigned int len; - - req.trailer.session_req.called_len = sizeof(req.trailer.session_req.called_name); - - if (server->server_RFC1001_name[0] != 0) - rfc1002mangle(req.trailer.session_req.called_name, - server->server_RFC1001_name, - RFC1001_NAME_LEN_WITH_NULL); - else - rfc1002mangle(req.trailer.session_req.called_name, - DEFAULT_CIFS_CALLED_NAME, - RFC1001_NAME_LEN_WITH_NULL); - - req.trailer.session_req.calling_len = sizeof(req.trailer.session_req.calling_name); - - /* calling name ends in null (byte 16) from old smb convention */ - if (server->workstation_RFC1001_name[0] != 0) - rfc1002mangle(req.trailer.session_req.calling_name, - server->workstation_RFC1001_name, - RFC1001_NAME_LEN_WITH_NULL); - else - rfc1002mangle(req.trailer.session_req.calling_name, - "LINUX_CIFS_CLNT", - RFC1001_NAME_LEN_WITH_NULL); - - /* - * As per rfc1002, @len must be the number of bytes that follows the - * length field of a rfc1002 session request payload. - */ - len = sizeof(req) - offsetof(struct rfc1002_session_packet, trailer.session_req); - - smb_buf->smb_buf_length = cpu_to_be32((RFC1002_SESSION_REQUEST << 24) | len); - rc = smb_send(server, smb_buf, len); - /* - * RFC1001 layer in at least one server requires very short break before - * negprot presumably because not expecting negprot to follow so fast. - * This is a simple solution that works without complicating the code - * and causes no significant slowing down on mount for everyone else - */ - usleep_range(1000, 2000); - - return rc; -} - -static int -generic_ip_connect(struct TCP_Server_Info *server) -{ - int rc = 0; - __be16 sport; - int slen, sfamily; - struct socket *socket = server->ssocket; - struct sockaddr *saddr; - - saddr = (struct sockaddr *) &server->dstaddr; - - if (server->dstaddr.ss_family == AF_INET6) { - struct sockaddr_in6 *ipv6 = (struct sockaddr_in6 *)&server->dstaddr; - - sport = ipv6->sin6_port; - slen = sizeof(struct sockaddr_in6); - sfamily = AF_INET6; - cifs_dbg(FYI, "%s: connecting to [%pI6]:%d\n", __func__, &ipv6->sin6_addr, - ntohs(sport)); - } else { - struct sockaddr_in *ipv4 = (struct sockaddr_in *)&server->dstaddr; - - sport = ipv4->sin_port; - slen = sizeof(struct sockaddr_in); - sfamily = AF_INET; - cifs_dbg(FYI, "%s: connecting to %pI4:%d\n", __func__, &ipv4->sin_addr, - ntohs(sport)); - } - - if (socket == NULL) { - rc = __sock_create(cifs_net_ns(server), sfamily, SOCK_STREAM, - IPPROTO_TCP, &socket, 1); - if (rc < 0) { - cifs_server_dbg(VFS, "Error %d creating socket\n", rc); - server->ssocket = NULL; - return rc; - } - - /* BB other socket options to set KEEPALIVE, NODELAY? */ - cifs_dbg(FYI, "Socket created\n"); - server->ssocket = socket; - socket->sk->sk_allocation = GFP_NOFS; - if (sfamily == AF_INET6) - cifs_reclassify_socket6(socket); - else - cifs_reclassify_socket4(socket); - } - - rc = bind_socket(server); - if (rc < 0) - return rc; - - /* - * Eventually check for other socket options to change from - * the default. sock_setsockopt not used because it expects - * user space buffer - */ - socket->sk->sk_rcvtimeo = 7 * HZ; - socket->sk->sk_sndtimeo = 5 * HZ; - - /* make the bufsizes depend on wsize/rsize and max requests */ - if (server->noautotune) { - if (socket->sk->sk_sndbuf < (200 * 1024)) - socket->sk->sk_sndbuf = 200 * 1024; - if (socket->sk->sk_rcvbuf < (140 * 1024)) - socket->sk->sk_rcvbuf = 140 * 1024; - } - - if (server->tcp_nodelay) - tcp_sock_set_nodelay(socket->sk); - - cifs_dbg(FYI, "sndbuf %d rcvbuf %d rcvtimeo 0x%lx\n", - socket->sk->sk_sndbuf, - socket->sk->sk_rcvbuf, socket->sk->sk_rcvtimeo); - - rc = socket->ops->connect(socket, saddr, slen, - server->noblockcnt ? O_NONBLOCK : 0); - /* - * When mounting SMB root file systems, we do not want to block in - * connect. Otherwise bail out and then let cifs_reconnect() perform - * reconnect failover - if possible. - */ - if (server->noblockcnt && rc == -EINPROGRESS) - rc = 0; - if (rc < 0) { - cifs_dbg(FYI, "Error %d connecting to server\n", rc); - trace_smb3_connect_err(server->hostname, server->conn_id, &server->dstaddr, rc); - sock_release(socket); - server->ssocket = NULL; - return rc; - } - trace_smb3_connect_done(server->hostname, server->conn_id, &server->dstaddr); - if (sport == htons(RFC1001_PORT)) - rc = ip_rfc1001_connect(server); - - return rc; -} - -static int -ip_connect(struct TCP_Server_Info *server) -{ - __be16 *sport; - struct sockaddr_in6 *addr6 = (struct sockaddr_in6 *)&server->dstaddr; - struct sockaddr_in *addr = (struct sockaddr_in *)&server->dstaddr; - - if (server->dstaddr.ss_family == AF_INET6) - sport = &addr6->sin6_port; - else - sport = &addr->sin_port; - - if (*sport == 0) { - int rc; - - /* try with 445 port at first */ - *sport = htons(CIFS_PORT); - - rc = generic_ip_connect(server); - if (rc >= 0) - return rc; - - /* if it failed, try with 139 port */ - *sport = htons(RFC1001_PORT); - } - - return generic_ip_connect(server); -} - -#ifdef CONFIG_CIFS_ALLOW_INSECURE_LEGACY -void reset_cifs_unix_caps(unsigned int xid, struct cifs_tcon *tcon, - struct cifs_sb_info *cifs_sb, struct smb3_fs_context *ctx) -{ - /* - * If we are reconnecting then should we check to see if - * any requested capabilities changed locally e.g. via - * remount but we can not do much about it here - * if they have (even if we could detect it by the following) - * Perhaps we could add a backpointer to array of sb from tcon - * or if we change to make all sb to same share the same - * sb as NFS - then we only have one backpointer to sb. - * What if we wanted to mount the server share twice once with - * and once without posixacls or posix paths? - */ - __u64 saved_cap = le64_to_cpu(tcon->fsUnixInfo.Capability); - - if (ctx && ctx->no_linux_ext) { - tcon->fsUnixInfo.Capability = 0; - tcon->unix_ext = 0; /* Unix Extensions disabled */ - cifs_dbg(FYI, "Linux protocol extensions disabled\n"); - return; - } else if (ctx) - tcon->unix_ext = 1; /* Unix Extensions supported */ - - if (!tcon->unix_ext) { - cifs_dbg(FYI, "Unix extensions disabled so not set on reconnect\n"); - return; - } - - if (!CIFSSMBQFSUnixInfo(xid, tcon)) { - __u64 cap = le64_to_cpu(tcon->fsUnixInfo.Capability); - cifs_dbg(FYI, "unix caps which server supports %lld\n", cap); - /* - * check for reconnect case in which we do not - * want to change the mount behavior if we can avoid it - */ - if (ctx == NULL) { - /* - * turn off POSIX ACL and PATHNAMES if not set - * originally at mount time - */ - if ((saved_cap & CIFS_UNIX_POSIX_ACL_CAP) == 0) - cap &= ~CIFS_UNIX_POSIX_ACL_CAP; - if ((saved_cap & CIFS_UNIX_POSIX_PATHNAMES_CAP) == 0) { - if (cap & CIFS_UNIX_POSIX_PATHNAMES_CAP) - cifs_dbg(VFS, "POSIXPATH support change\n"); - cap &= ~CIFS_UNIX_POSIX_PATHNAMES_CAP; - } else if ((cap & CIFS_UNIX_POSIX_PATHNAMES_CAP) == 0) { - cifs_dbg(VFS, "possible reconnect error\n"); - cifs_dbg(VFS, "server disabled POSIX path support\n"); - } - } - - if (cap & CIFS_UNIX_TRANSPORT_ENCRYPTION_MANDATORY_CAP) - cifs_dbg(VFS, "per-share encryption not supported yet\n"); - - cap &= CIFS_UNIX_CAP_MASK; - if (ctx && ctx->no_psx_acl) - cap &= ~CIFS_UNIX_POSIX_ACL_CAP; - else if (CIFS_UNIX_POSIX_ACL_CAP & cap) { - cifs_dbg(FYI, "negotiated posix acl support\n"); - if (cifs_sb) - cifs_sb->mnt_cifs_flags |= - CIFS_MOUNT_POSIXACL; - } - - if (ctx && ctx->posix_paths == 0) - cap &= ~CIFS_UNIX_POSIX_PATHNAMES_CAP; - else if (cap & CIFS_UNIX_POSIX_PATHNAMES_CAP) { - cifs_dbg(FYI, "negotiate posix pathnames\n"); - if (cifs_sb) - cifs_sb->mnt_cifs_flags |= - CIFS_MOUNT_POSIX_PATHS; - } - - cifs_dbg(FYI, "Negotiate caps 0x%x\n", (int)cap); -#ifdef CONFIG_CIFS_DEBUG2 - if (cap & CIFS_UNIX_FCNTL_CAP) - cifs_dbg(FYI, "FCNTL cap\n"); - if (cap & CIFS_UNIX_EXTATTR_CAP) - cifs_dbg(FYI, "EXTATTR cap\n"); - if (cap & CIFS_UNIX_POSIX_PATHNAMES_CAP) - cifs_dbg(FYI, "POSIX path cap\n"); - if (cap & CIFS_UNIX_XATTR_CAP) - cifs_dbg(FYI, "XATTR cap\n"); - if (cap & CIFS_UNIX_POSIX_ACL_CAP) - cifs_dbg(FYI, "POSIX ACL cap\n"); - if (cap & CIFS_UNIX_LARGE_READ_CAP) - cifs_dbg(FYI, "very large read cap\n"); - if (cap & CIFS_UNIX_LARGE_WRITE_CAP) - cifs_dbg(FYI, "very large write cap\n"); - if (cap & CIFS_UNIX_TRANSPORT_ENCRYPTION_CAP) - cifs_dbg(FYI, "transport encryption cap\n"); - if (cap & CIFS_UNIX_TRANSPORT_ENCRYPTION_MANDATORY_CAP) - cifs_dbg(FYI, "mandatory transport encryption cap\n"); -#endif /* CIFS_DEBUG2 */ - if (CIFSSMBSetFSUnixInfo(xid, tcon, cap)) { - if (ctx == NULL) - cifs_dbg(FYI, "resetting capabilities failed\n"); - else - cifs_dbg(VFS, "Negotiating Unix capabilities with the server failed. Consider mounting with the Unix Extensions disabled if problems are found by specifying the nounix mount option.\n"); - - } - } -} -#endif /* CONFIG_CIFS_ALLOW_INSECURE_LEGACY */ - -int cifs_setup_cifs_sb(struct cifs_sb_info *cifs_sb) -{ - struct smb3_fs_context *ctx = cifs_sb->ctx; - - INIT_DELAYED_WORK(&cifs_sb->prune_tlinks, cifs_prune_tlinks); - - spin_lock_init(&cifs_sb->tlink_tree_lock); - cifs_sb->tlink_tree = RB_ROOT; - - cifs_dbg(FYI, "file mode: %04ho dir mode: %04ho\n", - ctx->file_mode, ctx->dir_mode); - - /* this is needed for ASCII cp to Unicode converts */ - if (ctx->iocharset == NULL) { - /* load_nls_default cannot return null */ - cifs_sb->local_nls = load_nls_default(); - } else { - cifs_sb->local_nls = load_nls(ctx->iocharset); - if (cifs_sb->local_nls == NULL) { - cifs_dbg(VFS, "CIFS mount error: iocharset %s not found\n", - ctx->iocharset); - return -ELIBACC; - } - } - ctx->local_nls = cifs_sb->local_nls; - - smb3_update_mnt_flags(cifs_sb); - - if (ctx->direct_io) - cifs_dbg(FYI, "mounting share using direct i/o\n"); - if (ctx->cache_ro) { - cifs_dbg(VFS, "mounting share with read only caching. Ensure that the share will not be modified while in use.\n"); - cifs_sb->mnt_cifs_flags |= CIFS_MOUNT_RO_CACHE; - } else if (ctx->cache_rw) { - cifs_dbg(VFS, "mounting share in single client RW caching mode. Ensure that no other systems will be accessing the share.\n"); - cifs_sb->mnt_cifs_flags |= (CIFS_MOUNT_RO_CACHE | - CIFS_MOUNT_RW_CACHE); - } - - if ((ctx->cifs_acl) && (ctx->dynperm)) - cifs_dbg(VFS, "mount option dynperm ignored if cifsacl mount option supported\n"); - - if (ctx->prepath) { - cifs_sb->prepath = kstrdup(ctx->prepath, GFP_KERNEL); - if (cifs_sb->prepath == NULL) - return -ENOMEM; - cifs_sb->mnt_cifs_flags |= CIFS_MOUNT_USE_PREFIX_PATH; - } - - return 0; -} - -/* Release all succeed connections */ -static inline void mount_put_conns(struct mount_ctx *mnt_ctx) -{ - int rc = 0; - - if (mnt_ctx->tcon) - cifs_put_tcon(mnt_ctx->tcon); - else if (mnt_ctx->ses) - cifs_put_smb_ses(mnt_ctx->ses); - else if (mnt_ctx->server) - cifs_put_tcp_session(mnt_ctx->server, 0); - mnt_ctx->cifs_sb->mnt_cifs_flags &= ~CIFS_MOUNT_POSIX_PATHS; - free_xid(mnt_ctx->xid); -} - -/* Get connections for tcp, ses and tcon */ -static int mount_get_conns(struct mount_ctx *mnt_ctx) -{ - int rc = 0; - struct TCP_Server_Info *server = NULL; - struct cifs_ses *ses = NULL; - struct cifs_tcon *tcon = NULL; - struct smb3_fs_context *ctx = mnt_ctx->fs_ctx; - struct cifs_sb_info *cifs_sb = mnt_ctx->cifs_sb; - unsigned int xid; - - xid = get_xid(); - - /* get a reference to a tcp session */ - server = cifs_get_tcp_session(ctx, NULL); - if (IS_ERR(server)) { - rc = PTR_ERR(server); - server = NULL; - goto out; - } - - /* get a reference to a SMB session */ - ses = cifs_get_smb_ses(server, ctx); - if (IS_ERR(ses)) { - rc = PTR_ERR(ses); - ses = NULL; - goto out; - } - - if ((ctx->persistent == true) && (!(ses->server->capabilities & - SMB2_GLOBAL_CAP_PERSISTENT_HANDLES))) { - cifs_server_dbg(VFS, "persistent handles not supported by server\n"); - rc = -EOPNOTSUPP; - goto out; - } - - /* search for existing tcon to this server share */ - tcon = cifs_get_tcon(ses, ctx); - if (IS_ERR(tcon)) { - rc = PTR_ERR(tcon); - tcon = NULL; - goto out; - } - - /* if new SMB3.11 POSIX extensions are supported do not remap / and \ */ - if (tcon->posix_extensions) - cifs_sb->mnt_cifs_flags |= CIFS_MOUNT_POSIX_PATHS; - -#ifdef CONFIG_CIFS_ALLOW_INSECURE_LEGACY - /* tell server which Unix caps we support */ - if (cap_unix(tcon->ses)) { - /* - * reset of caps checks mount to see if unix extensions disabled - * for just this mount. - */ - reset_cifs_unix_caps(xid, tcon, cifs_sb, ctx); - spin_lock(&tcon->ses->server->srv_lock); - if ((tcon->ses->server->tcpStatus == CifsNeedReconnect) && - (le64_to_cpu(tcon->fsUnixInfo.Capability) & - CIFS_UNIX_TRANSPORT_ENCRYPTION_MANDATORY_CAP)) { - spin_unlock(&tcon->ses->server->srv_lock); - rc = -EACCES; - goto out; - } - spin_unlock(&tcon->ses->server->srv_lock); - } else -#endif /* CONFIG_CIFS_ALLOW_INSECURE_LEGACY */ - tcon->unix_ext = 0; /* server does not support them */ - - /* do not care if a following call succeed - informational */ - if (!tcon->pipe && server->ops->qfs_tcon) { - server->ops->qfs_tcon(xid, tcon, cifs_sb); - if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_RO_CACHE) { - if (tcon->fsDevInfo.DeviceCharacteristics & - cpu_to_le32(FILE_READ_ONLY_DEVICE)) - cifs_dbg(VFS, "mounted to read only share\n"); - else if ((cifs_sb->mnt_cifs_flags & - CIFS_MOUNT_RW_CACHE) == 0) - cifs_dbg(VFS, "read only mount of RW share\n"); - /* no need to log a RW mount of a typical RW share */ - } - } - - /* - * Clamp the rsize/wsize mount arguments if they are too big for the server - * and set the rsize/wsize to the negotiated values if not passed in by - * the user on mount - */ - if ((cifs_sb->ctx->wsize == 0) || - (cifs_sb->ctx->wsize > server->ops->negotiate_wsize(tcon, ctx))) - cifs_sb->ctx->wsize = server->ops->negotiate_wsize(tcon, ctx); - if ((cifs_sb->ctx->rsize == 0) || - (cifs_sb->ctx->rsize > server->ops->negotiate_rsize(tcon, ctx))) - cifs_sb->ctx->rsize = server->ops->negotiate_rsize(tcon, ctx); - - /* - * The cookie is initialized from volume info returned above. - * Inside cifs_fscache_get_super_cookie it checks - * that we do not get super cookie twice. - */ - if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_FSCACHE) - cifs_fscache_get_super_cookie(tcon); - -out: - mnt_ctx->server = server; - mnt_ctx->ses = ses; - mnt_ctx->tcon = tcon; - mnt_ctx->xid = xid; - - return rc; -} - -static int mount_setup_tlink(struct cifs_sb_info *cifs_sb, struct cifs_ses *ses, - struct cifs_tcon *tcon) -{ - struct tcon_link *tlink; - - /* hang the tcon off of the superblock */ - tlink = kzalloc(sizeof(*tlink), GFP_KERNEL); - if (tlink == NULL) - return -ENOMEM; - - tlink->tl_uid = ses->linux_uid; - tlink->tl_tcon = tcon; - tlink->tl_time = jiffies; - set_bit(TCON_LINK_MASTER, &tlink->tl_flags); - set_bit(TCON_LINK_IN_TREE, &tlink->tl_flags); - - cifs_sb->master_tlink = tlink; - spin_lock(&cifs_sb->tlink_tree_lock); - tlink_rb_insert(&cifs_sb->tlink_tree, tlink); - spin_unlock(&cifs_sb->tlink_tree_lock); - - queue_delayed_work(cifsiod_wq, &cifs_sb->prune_tlinks, - TLINK_IDLE_EXPIRE); - return 0; -} - -#ifdef CONFIG_CIFS_DFS_UPCALL -/* Get unique dfs connections */ -static int mount_get_dfs_conns(struct mount_ctx *mnt_ctx) -{ - int rc; - - mnt_ctx->fs_ctx->nosharesock = true; - rc = mount_get_conns(mnt_ctx); - if (mnt_ctx->server) { - cifs_dbg(FYI, "%s: marking tcp session as a dfs connection\n", __func__); - spin_lock(&mnt_ctx->server->srv_lock); - mnt_ctx->server->is_dfs_conn = true; - spin_unlock(&mnt_ctx->server->srv_lock); - } - return rc; -} - -/* - * cifs_build_path_to_root returns full path to root when we do not have an - * existing connection (tcon) - */ -static char * -build_unc_path_to_root(const struct smb3_fs_context *ctx, - const struct cifs_sb_info *cifs_sb, bool useppath) -{ - char *full_path, *pos; - unsigned int pplen = useppath && ctx->prepath ? - strlen(ctx->prepath) + 1 : 0; - unsigned int unc_len = strnlen(ctx->UNC, MAX_TREE_SIZE + 1); - - if (unc_len > MAX_TREE_SIZE) - return ERR_PTR(-EINVAL); - - full_path = kmalloc(unc_len + pplen + 1, GFP_KERNEL); - if (full_path == NULL) - return ERR_PTR(-ENOMEM); - - memcpy(full_path, ctx->UNC, unc_len); - pos = full_path + unc_len; - - if (pplen) { - *pos = CIFS_DIR_SEP(cifs_sb); - memcpy(pos + 1, ctx->prepath, pplen); - pos += pplen; - } - - *pos = '\0'; /* add trailing null */ - convert_delimiter(full_path, CIFS_DIR_SEP(cifs_sb)); - cifs_dbg(FYI, "%s: full_path=%s\n", __func__, full_path); - return full_path; -} - -/* - * expand_dfs_referral - Update cifs_sb from dfs referral path - * - * cifs_sb->ctx->mount_options will be (re-)allocated to a string containing updated options for the - * submount. Otherwise it will be left untouched. - */ -static int expand_dfs_referral(struct mount_ctx *mnt_ctx, const char *full_path, - struct dfs_info3_param *referral) -{ - int rc; - struct cifs_sb_info *cifs_sb = mnt_ctx->cifs_sb; - struct smb3_fs_context *ctx = mnt_ctx->fs_ctx; - char *fake_devname = NULL, *mdata = NULL; - - mdata = cifs_compose_mount_options(cifs_sb->ctx->mount_options, full_path + 1, referral, - &fake_devname); - if (IS_ERR(mdata)) { - rc = PTR_ERR(mdata); - mdata = NULL; - } else { - /* - * We can not clear out the whole structure since we no longer have an explicit - * function to parse a mount-string. Instead we need to clear out the individual - * fields that are no longer valid. - */ - kfree(ctx->prepath); - ctx->prepath = NULL; - rc = cifs_setup_volume_info(ctx, mdata, fake_devname); - } - kfree(fake_devname); - kfree(cifs_sb->ctx->mount_options); - cifs_sb->ctx->mount_options = mdata; - - return rc; -} -#endif - -/* TODO: all callers to this are broken. We are not parsing mount_options here - * we should pass a clone of the original context? - */ -int -cifs_setup_volume_info(struct smb3_fs_context *ctx, const char *mntopts, const char *devname) -{ - int rc; - - if (devname) { - cifs_dbg(FYI, "%s: devname=%s\n", __func__, devname); - rc = smb3_parse_devname(devname, ctx); - if (rc) { - cifs_dbg(VFS, "%s: failed to parse %s: %d\n", __func__, devname, rc); - return rc; - } - } - - if (mntopts) { - char *ip; - - rc = smb3_parse_opt(mntopts, "ip", &ip); - if (rc) { - cifs_dbg(VFS, "%s: failed to parse ip options: %d\n", __func__, rc); - return rc; - } - - rc = cifs_convert_address((struct sockaddr *)&ctx->dstaddr, ip, strlen(ip)); - kfree(ip); - if (!rc) { - cifs_dbg(VFS, "%s: failed to convert ip address\n", __func__); - return -EINVAL; - } - } - - if (ctx->nullauth) { - cifs_dbg(FYI, "Anonymous login\n"); - kfree(ctx->username); - ctx->username = NULL; - } else if (ctx->username) { - /* BB fixme parse for domain name here */ - cifs_dbg(FYI, "Username: %s\n", ctx->username); - } else { - cifs_dbg(VFS, "No username specified\n"); - /* In userspace mount helper we can get user name from alternate - locations such as env variables and files on disk */ - return -EINVAL; - } - - return 0; -} - -static int -cifs_are_all_path_components_accessible(struct TCP_Server_Info *server, - unsigned int xid, - struct cifs_tcon *tcon, - struct cifs_sb_info *cifs_sb, - char *full_path, - int added_treename) -{ - int rc; - char *s; - char sep, tmp; - int skip = added_treename ? 1 : 0; - - sep = CIFS_DIR_SEP(cifs_sb); - s = full_path; - - rc = server->ops->is_path_accessible(xid, tcon, cifs_sb, ""); - while (rc == 0) { - /* skip separators */ - while (*s == sep) - s++; - if (!*s) - break; - /* next separator */ - while (*s && *s != sep) - s++; - /* - * if the treename is added, we then have to skip the first - * part within the separators - */ - if (skip) { - skip = 0; - continue; - } - /* - * temporarily null-terminate the path at the end of - * the current component - */ - tmp = *s; - *s = 0; - rc = server->ops->is_path_accessible(xid, tcon, cifs_sb, - full_path); - *s = tmp; - } - return rc; -} - -/* - * Check if path is remote (i.e. a DFS share). - * - * Return -EREMOTE if it is, otherwise 0 or -errno. - */ -static int is_path_remote(struct mount_ctx *mnt_ctx) -{ - int rc; - struct cifs_sb_info *cifs_sb = mnt_ctx->cifs_sb; - struct TCP_Server_Info *server = mnt_ctx->server; - unsigned int xid = mnt_ctx->xid; - struct cifs_tcon *tcon = mnt_ctx->tcon; - struct smb3_fs_context *ctx = mnt_ctx->fs_ctx; - char *full_path; - - if (!server->ops->is_path_accessible) - return -EOPNOTSUPP; - - /* - * cifs_build_path_to_root works only when we have a valid tcon - */ - full_path = cifs_build_path_to_root(ctx, cifs_sb, tcon, - tcon->Flags & SMB_SHARE_IS_IN_DFS); - if (full_path == NULL) - return -ENOMEM; - - cifs_dbg(FYI, "%s: full_path: %s\n", __func__, full_path); - - rc = server->ops->is_path_accessible(xid, tcon, cifs_sb, - full_path); - if (rc != 0 && rc != -EREMOTE) - goto out; - - if (rc != -EREMOTE) { - rc = cifs_are_all_path_components_accessible(server, xid, tcon, - cifs_sb, full_path, tcon->Flags & SMB_SHARE_IS_IN_DFS); - if (rc != 0) { - cifs_server_dbg(VFS, "cannot query dirs between root and final path, enabling CIFS_MOUNT_USE_PREFIX_PATH\n"); - cifs_sb->mnt_cifs_flags |= CIFS_MOUNT_USE_PREFIX_PATH; - rc = 0; - } - } - -out: - kfree(full_path); - return rc; -} - -#ifdef CONFIG_CIFS_DFS_UPCALL -static void set_root_ses(struct mount_ctx *mnt_ctx) -{ - if (mnt_ctx->ses) { - spin_lock(&cifs_tcp_ses_lock); - mnt_ctx->ses->ses_count++; - spin_unlock(&cifs_tcp_ses_lock); - dfs_cache_add_refsrv_session(&mnt_ctx->mount_id, mnt_ctx->ses); - } - mnt_ctx->root_ses = mnt_ctx->ses; -} - -static int is_dfs_mount(struct mount_ctx *mnt_ctx, bool *isdfs, struct dfs_cache_tgt_list *root_tl) -{ - int rc; - struct cifs_sb_info *cifs_sb = mnt_ctx->cifs_sb; - struct smb3_fs_context *ctx = mnt_ctx->fs_ctx; - - *isdfs = true; - - rc = mount_get_conns(mnt_ctx); - /* - * If called with 'nodfs' mount option, then skip DFS resolving. Otherwise unconditionally - * try to get an DFS referral (even cached) to determine whether it is an DFS mount. - * - * Skip prefix path to provide support for DFS referrals from w2k8 servers which don't seem - * to respond with PATH_NOT_COVERED to requests that include the prefix. - */ - if ((cifs_sb->mnt_cifs_flags & CIFS_MOUNT_NO_DFS) || - dfs_cache_find(mnt_ctx->xid, mnt_ctx->ses, cifs_sb->local_nls, cifs_remap(cifs_sb), - ctx->UNC + 1, NULL, root_tl)) { - if (rc) - return rc; - /* Check if it is fully accessible and then mount it */ - rc = is_path_remote(mnt_ctx); - if (!rc) - *isdfs = false; - else if (rc != -EREMOTE) - return rc; - } - return 0; -} - -static int connect_dfs_target(struct mount_ctx *mnt_ctx, const char *full_path, - const char *ref_path, struct dfs_cache_tgt_iterator *tit) -{ - int rc; - struct dfs_info3_param ref = {}; - struct cifs_sb_info *cifs_sb = mnt_ctx->cifs_sb; - char *oldmnt = cifs_sb->ctx->mount_options; - - cifs_dbg(FYI, "%s: full_path=%s ref_path=%s target=%s\n", __func__, full_path, ref_path, - dfs_cache_get_tgt_name(tit)); - - rc = dfs_cache_get_tgt_referral(ref_path, tit, &ref); - if (rc) - goto out; - - rc = expand_dfs_referral(mnt_ctx, full_path, &ref); - if (rc) - goto out; - - /* Connect to new target only if we were redirected (e.g. mount options changed) */ - if (oldmnt != cifs_sb->ctx->mount_options) { - mount_put_conns(mnt_ctx); - rc = mount_get_dfs_conns(mnt_ctx); - } - if (!rc) { - if (cifs_is_referral_server(mnt_ctx->tcon, &ref)) - set_root_ses(mnt_ctx); - rc = dfs_cache_update_tgthint(mnt_ctx->xid, mnt_ctx->root_ses, cifs_sb->local_nls, - cifs_remap(cifs_sb), ref_path, tit); - } - -out: - free_dfs_info_param(&ref); - return rc; -} - -static int connect_dfs_root(struct mount_ctx *mnt_ctx, struct dfs_cache_tgt_list *root_tl) -{ - int rc; - char *full_path; - struct cifs_sb_info *cifs_sb = mnt_ctx->cifs_sb; - struct smb3_fs_context *ctx = mnt_ctx->fs_ctx; - struct dfs_cache_tgt_iterator *tit; - - /* Put initial connections as they might be shared with other mounts. We need unique dfs - * connections per mount to properly failover, so mount_get_dfs_conns() must be used from - * now on. - */ - mount_put_conns(mnt_ctx); - mount_get_dfs_conns(mnt_ctx); - set_root_ses(mnt_ctx); - - full_path = build_unc_path_to_root(ctx, cifs_sb, true); - if (IS_ERR(full_path)) - return PTR_ERR(full_path); - - mnt_ctx->origin_fullpath = dfs_cache_canonical_path(ctx->UNC, cifs_sb->local_nls, - cifs_remap(cifs_sb)); - if (IS_ERR(mnt_ctx->origin_fullpath)) { - rc = PTR_ERR(mnt_ctx->origin_fullpath); - mnt_ctx->origin_fullpath = NULL; - goto out; - } - - /* Try all dfs root targets */ - for (rc = -ENOENT, tit = dfs_cache_get_tgt_iterator(root_tl); - tit; tit = dfs_cache_get_next_tgt(root_tl, tit)) { - rc = connect_dfs_target(mnt_ctx, full_path, mnt_ctx->origin_fullpath + 1, tit); - if (!rc) { - mnt_ctx->leaf_fullpath = kstrdup(mnt_ctx->origin_fullpath, GFP_KERNEL); - if (!mnt_ctx->leaf_fullpath) - rc = -ENOMEM; - break; - } - } - -out: - kfree(full_path); - return rc; -} - -static int __follow_dfs_link(struct mount_ctx *mnt_ctx) -{ - int rc; - struct cifs_sb_info *cifs_sb = mnt_ctx->cifs_sb; - struct smb3_fs_context *ctx = mnt_ctx->fs_ctx; - char *full_path; - struct dfs_cache_tgt_list tl = DFS_CACHE_TGT_LIST_INIT(tl); - struct dfs_cache_tgt_iterator *tit; - - full_path = build_unc_path_to_root(ctx, cifs_sb, true); - if (IS_ERR(full_path)) - return PTR_ERR(full_path); - - kfree(mnt_ctx->leaf_fullpath); - mnt_ctx->leaf_fullpath = dfs_cache_canonical_path(full_path, cifs_sb->local_nls, - cifs_remap(cifs_sb)); - if (IS_ERR(mnt_ctx->leaf_fullpath)) { - rc = PTR_ERR(mnt_ctx->leaf_fullpath); - mnt_ctx->leaf_fullpath = NULL; - goto out; - } - - /* Get referral from dfs link */ - rc = dfs_cache_find(mnt_ctx->xid, mnt_ctx->root_ses, cifs_sb->local_nls, - cifs_remap(cifs_sb), mnt_ctx->leaf_fullpath + 1, NULL, &tl); - if (rc) - goto out; - - /* Try all dfs link targets. If an I/O fails from currently connected DFS target with an - * error other than STATUS_PATH_NOT_COVERED (-EREMOTE), then retry it from other targets as - * specified in MS-DFSC "3.1.5.2 I/O Operation to Target Fails with an Error Other Than - * STATUS_PATH_NOT_COVERED." - */ - for (rc = -ENOENT, tit = dfs_cache_get_tgt_iterator(&tl); - tit; tit = dfs_cache_get_next_tgt(&tl, tit)) { - rc = connect_dfs_target(mnt_ctx, full_path, mnt_ctx->leaf_fullpath + 1, tit); - if (!rc) { - rc = is_path_remote(mnt_ctx); - if (!rc || rc == -EREMOTE) - break; - } - } - -out: - kfree(full_path); - dfs_cache_free_tgts(&tl); - return rc; -} - -static int follow_dfs_link(struct mount_ctx *mnt_ctx) -{ - int rc; - struct cifs_sb_info *cifs_sb = mnt_ctx->cifs_sb; - struct smb3_fs_context *ctx = mnt_ctx->fs_ctx; - char *full_path; - int num_links = 0; - - full_path = build_unc_path_to_root(ctx, cifs_sb, true); - if (IS_ERR(full_path)) - return PTR_ERR(full_path); - - kfree(mnt_ctx->origin_fullpath); - mnt_ctx->origin_fullpath = dfs_cache_canonical_path(full_path, cifs_sb->local_nls, - cifs_remap(cifs_sb)); - kfree(full_path); - - if (IS_ERR(mnt_ctx->origin_fullpath)) { - rc = PTR_ERR(mnt_ctx->origin_fullpath); - mnt_ctx->origin_fullpath = NULL; - return rc; - } - - do { - rc = __follow_dfs_link(mnt_ctx); - if (!rc || rc != -EREMOTE) - break; - } while (rc = -ELOOP, ++num_links < MAX_NESTED_LINKS); - - return rc; -} - -/* Set up DFS referral paths for failover */ -static void setup_server_referral_paths(struct mount_ctx *mnt_ctx) -{ - struct TCP_Server_Info *server = mnt_ctx->server; - - mutex_lock(&server->refpath_lock); - server->origin_fullpath = mnt_ctx->origin_fullpath; - server->leaf_fullpath = mnt_ctx->leaf_fullpath; - server->current_fullpath = mnt_ctx->leaf_fullpath; - mutex_unlock(&server->refpath_lock); - mnt_ctx->origin_fullpath = mnt_ctx->leaf_fullpath = NULL; -} - -int cifs_mount(struct cifs_sb_info *cifs_sb, struct smb3_fs_context *ctx) -{ - int rc; - struct mount_ctx mnt_ctx = { .cifs_sb = cifs_sb, .fs_ctx = ctx, }; - struct dfs_cache_tgt_list tl = DFS_CACHE_TGT_LIST_INIT(tl); - bool isdfs; - - rc = is_dfs_mount(&mnt_ctx, &isdfs, &tl); - if (rc) - goto error; - if (!isdfs) - goto out; - - /* proceed as DFS mount */ - uuid_gen(&mnt_ctx.mount_id); - rc = connect_dfs_root(&mnt_ctx, &tl); - dfs_cache_free_tgts(&tl); - - if (rc) - goto error; - - rc = is_path_remote(&mnt_ctx); - if (rc) - rc = follow_dfs_link(&mnt_ctx); - if (rc) - goto error; - - setup_server_referral_paths(&mnt_ctx); - /* - * After reconnecting to a different server, unique ids won't match anymore, so we disable - * serverino. This prevents dentry revalidation to think the dentry are stale (ESTALE). - */ - cifs_autodisable_serverino(cifs_sb); - /* - * Force the use of prefix path to support failover on DFS paths that resolve to targets - * that have different prefix paths. - */ - cifs_sb->mnt_cifs_flags |= CIFS_MOUNT_USE_PREFIX_PATH; - kfree(cifs_sb->prepath); - cifs_sb->prepath = ctx->prepath; - ctx->prepath = NULL; - uuid_copy(&cifs_sb->dfs_mount_id, &mnt_ctx.mount_id); - -out: - cifs_try_adding_channels(cifs_sb, mnt_ctx.ses); - rc = mount_setup_tlink(cifs_sb, mnt_ctx.ses, mnt_ctx.tcon); - if (rc) - goto error; - - free_xid(mnt_ctx.xid); - return rc; - -error: - dfs_cache_put_refsrv_sessions(&mnt_ctx.mount_id); - kfree(mnt_ctx.origin_fullpath); - kfree(mnt_ctx.leaf_fullpath); - mount_put_conns(&mnt_ctx); - return rc; -} -#else -int cifs_mount(struct cifs_sb_info *cifs_sb, struct smb3_fs_context *ctx) -{ - int rc = 0; - struct mount_ctx mnt_ctx = { .cifs_sb = cifs_sb, .fs_ctx = ctx, }; - - rc = mount_get_conns(&mnt_ctx); - if (rc) - goto error; - - if (mnt_ctx.tcon) { - rc = is_path_remote(&mnt_ctx); - if (rc == -EREMOTE) - rc = -EOPNOTSUPP; - if (rc) - goto error; - } - - rc = mount_setup_tlink(cifs_sb, mnt_ctx.ses, mnt_ctx.tcon); - if (rc) - goto error; - - free_xid(mnt_ctx.xid); - return rc; - -error: - mount_put_conns(&mnt_ctx); - return rc; -} -#endif - -/* - * Issue a TREE_CONNECT request. - */ -int -CIFSTCon(const unsigned int xid, struct cifs_ses *ses, - const char *tree, struct cifs_tcon *tcon, - const struct nls_table *nls_codepage) -{ - struct smb_hdr *smb_buffer; - struct smb_hdr *smb_buffer_response; - TCONX_REQ *pSMB; - TCONX_RSP *pSMBr; - unsigned char *bcc_ptr; - int rc = 0; - int length; - __u16 bytes_left, count; - - if (ses == NULL) - return -EIO; - - smb_buffer = cifs_buf_get(); - if (smb_buffer == NULL) - return -ENOMEM; - - smb_buffer_response = smb_buffer; - - header_assemble(smb_buffer, SMB_COM_TREE_CONNECT_ANDX, - NULL /*no tid */ , 4 /*wct */ ); - - smb_buffer->Mid = get_next_mid(ses->server); - smb_buffer->Uid = ses->Suid; - pSMB = (TCONX_REQ *) smb_buffer; - pSMBr = (TCONX_RSP *) smb_buffer_response; - - pSMB->AndXCommand = 0xFF; - pSMB->Flags = cpu_to_le16(TCON_EXTENDED_SECINFO); - bcc_ptr = &pSMB->Password[0]; - - pSMB->PasswordLength = cpu_to_le16(1); /* minimum */ - *bcc_ptr = 0; /* password is null byte */ - bcc_ptr++; /* skip password */ - /* already aligned so no need to do it below */ - - if (ses->server->sign) - smb_buffer->Flags2 |= SMBFLG2_SECURITY_SIGNATURE; - - if (ses->capabilities & CAP_STATUS32) { - smb_buffer->Flags2 |= SMBFLG2_ERR_STATUS; - } - if (ses->capabilities & CAP_DFS) { - smb_buffer->Flags2 |= SMBFLG2_DFS; - } - if (ses->capabilities & CAP_UNICODE) { - smb_buffer->Flags2 |= SMBFLG2_UNICODE; - length = - cifs_strtoUTF16((__le16 *) bcc_ptr, tree, - 6 /* max utf8 char length in bytes */ * - (/* server len*/ + 256 /* share len */), nls_codepage); - bcc_ptr += 2 * length; /* convert num 16 bit words to bytes */ - bcc_ptr += 2; /* skip trailing null */ - } else { /* ASCII */ - strcpy(bcc_ptr, tree); - bcc_ptr += strlen(tree) + 1; - } - strcpy(bcc_ptr, "?????"); - bcc_ptr += strlen("?????"); - bcc_ptr += 1; - count = bcc_ptr - &pSMB->Password[0]; - be32_add_cpu(&pSMB->hdr.smb_buf_length, count); - pSMB->ByteCount = cpu_to_le16(count); - - rc = SendReceive(xid, ses, smb_buffer, smb_buffer_response, &length, - 0); - - /* above now done in SendReceive */ - if (rc == 0) { - bool is_unicode; - - tcon->tid = smb_buffer_response->Tid; - bcc_ptr = pByteArea(smb_buffer_response); - bytes_left = get_bcc(smb_buffer_response); - length = strnlen(bcc_ptr, bytes_left - 2); - if (smb_buffer->Flags2 & SMBFLG2_UNICODE) - is_unicode = true; - else - is_unicode = false; - - - /* skip service field (NB: this field is always ASCII) */ - if (length == 3) { - if ((bcc_ptr[0] == 'I') && (bcc_ptr[1] == 'P') && - (bcc_ptr[2] == 'C')) { - cifs_dbg(FYI, "IPC connection\n"); - tcon->ipc = true; - tcon->pipe = true; - } - } else if (length == 2) { - if ((bcc_ptr[0] == 'A') && (bcc_ptr[1] == ':')) { - /* the most common case */ - cifs_dbg(FYI, "disk share connection\n"); - } - } - bcc_ptr += length + 1; - bytes_left -= (length + 1); - strscpy(tcon->tree_name, tree, sizeof(tcon->tree_name)); - - /* mostly informational -- no need to fail on error here */ - kfree(tcon->nativeFileSystem); - tcon->nativeFileSystem = cifs_strndup_from_utf16(bcc_ptr, - bytes_left, is_unicode, - nls_codepage); - - cifs_dbg(FYI, "nativeFileSystem=%s\n", tcon->nativeFileSystem); - - if ((smb_buffer_response->WordCount == 3) || - (smb_buffer_response->WordCount == 7)) - /* field is in same location */ - tcon->Flags = le16_to_cpu(pSMBr->OptionalSupport); - else - tcon->Flags = 0; - cifs_dbg(FYI, "Tcon flags: 0x%x\n", tcon->Flags); - } - - cifs_buf_release(smb_buffer); - return rc; -} - -static void delayed_free(struct rcu_head *p) -{ - struct cifs_sb_info *cifs_sb = container_of(p, struct cifs_sb_info, rcu); - - unload_nls(cifs_sb->local_nls); - smb3_cleanup_fs_context(cifs_sb->ctx); - kfree(cifs_sb); -} - -void -cifs_umount(struct cifs_sb_info *cifs_sb) -{ - struct rb_root *root = &cifs_sb->tlink_tree; - struct rb_node *node; - struct tcon_link *tlink; - - cancel_delayed_work_sync(&cifs_sb->prune_tlinks); - - spin_lock(&cifs_sb->tlink_tree_lock); - while ((node = rb_first(root))) { - tlink = rb_entry(node, struct tcon_link, tl_rbnode); - cifs_get_tlink(tlink); - clear_bit(TCON_LINK_IN_TREE, &tlink->tl_flags); - rb_erase(node, root); - - spin_unlock(&cifs_sb->tlink_tree_lock); - cifs_put_tlink(tlink); - spin_lock(&cifs_sb->tlink_tree_lock); - } - spin_unlock(&cifs_sb->tlink_tree_lock); - - kfree(cifs_sb->prepath); -#ifdef CONFIG_CIFS_DFS_UPCALL - dfs_cache_put_refsrv_sessions(&cifs_sb->dfs_mount_id); -#endif - call_rcu(&cifs_sb->rcu, delayed_free); -} - -int -cifs_negotiate_protocol(const unsigned int xid, struct cifs_ses *ses, - struct TCP_Server_Info *server) -{ - int rc = 0; - - if (!server->ops->need_neg || !server->ops->negotiate) - return -ENOSYS; - - /* only send once per connect */ - spin_lock(&server->srv_lock); - if (server->tcpStatus != CifsGood && - server->tcpStatus != CifsNew && - server->tcpStatus != CifsNeedNegotiate) { - spin_unlock(&server->srv_lock); - return -EHOSTDOWN; - } - - if (!server->ops->need_neg(server) && - server->tcpStatus == CifsGood) { - spin_unlock(&server->srv_lock); - return 0; - } - - server->tcpStatus = CifsInNegotiate; - spin_unlock(&server->srv_lock); - - rc = server->ops->negotiate(xid, ses, server); - if (rc == 0) { - spin_lock(&server->srv_lock); - if (server->tcpStatus == CifsInNegotiate) - server->tcpStatus = CifsGood; - else - rc = -EHOSTDOWN; - spin_unlock(&server->srv_lock); - } else { - spin_lock(&server->srv_lock); - if (server->tcpStatus == CifsInNegotiate) - server->tcpStatus = CifsNeedNegotiate; - spin_unlock(&server->srv_lock); - } - - return rc; -} - -int -cifs_setup_session(const unsigned int xid, struct cifs_ses *ses, - struct TCP_Server_Info *server, - struct nls_table *nls_info) -{ - int rc = -ENOSYS; - struct TCP_Server_Info *pserver = CIFS_SERVER_IS_CHAN(server) ? server->primary_server : server; - struct sockaddr_in6 *addr6 = (struct sockaddr_in6 *)&pserver->dstaddr; - struct sockaddr_in *addr = (struct sockaddr_in *)&pserver->dstaddr; - bool is_binding = false; - - spin_lock(&ses->ses_lock); - cifs_dbg(FYI, "%s: channel connect bitmap: 0x%lx\n", - __func__, ses->chans_need_reconnect); - - if (ses->ses_status != SES_GOOD && - ses->ses_status != SES_NEW && - ses->ses_status != SES_NEED_RECON) { - spin_unlock(&ses->ses_lock); - return -EHOSTDOWN; - } - - /* only send once per connect */ - spin_lock(&ses->chan_lock); - if (CIFS_ALL_CHANS_GOOD(ses)) { - if (ses->ses_status == SES_NEED_RECON) - ses->ses_status = SES_GOOD; - spin_unlock(&ses->chan_lock); - spin_unlock(&ses->ses_lock); - return 0; - } - - cifs_chan_set_in_reconnect(ses, server); - is_binding = !CIFS_ALL_CHANS_NEED_RECONNECT(ses); - spin_unlock(&ses->chan_lock); - - if (!is_binding) - ses->ses_status = SES_IN_SETUP; - spin_unlock(&ses->ses_lock); - - /* update ses ip_addr only for primary chan */ - if (server == pserver) { - if (server->dstaddr.ss_family == AF_INET6) - scnprintf(ses->ip_addr, sizeof(ses->ip_addr), "%pI6", &addr6->sin6_addr); - else - scnprintf(ses->ip_addr, sizeof(ses->ip_addr), "%pI4", &addr->sin_addr); - } - - if (!is_binding) { - ses->capabilities = server->capabilities; - if (!linuxExtEnabled) - ses->capabilities &= (~server->vals->cap_unix); - - if (ses->auth_key.response) { - cifs_dbg(FYI, "Free previous auth_key.response = %p\n", - ses->auth_key.response); - kfree_sensitive(ses->auth_key.response); - ses->auth_key.response = NULL; - ses->auth_key.len = 0; - } - } - - cifs_dbg(FYI, "Security Mode: 0x%x Capabilities: 0x%x TimeAdjust: %d\n", - server->sec_mode, server->capabilities, server->timeAdj); - - if (server->ops->sess_setup) - rc = server->ops->sess_setup(xid, ses, server, nls_info); - - if (rc) { - cifs_server_dbg(VFS, "Send error in SessSetup = %d\n", rc); - spin_lock(&ses->ses_lock); - if (ses->ses_status == SES_IN_SETUP) - ses->ses_status = SES_NEED_RECON; - spin_lock(&ses->chan_lock); - cifs_chan_clear_in_reconnect(ses, server); - spin_unlock(&ses->chan_lock); - spin_unlock(&ses->ses_lock); - } else { - spin_lock(&ses->ses_lock); - if (ses->ses_status == SES_IN_SETUP) - ses->ses_status = SES_GOOD; - spin_lock(&ses->chan_lock); - cifs_chan_clear_in_reconnect(ses, server); - cifs_chan_clear_need_reconnect(ses, server); - spin_unlock(&ses->chan_lock); - spin_unlock(&ses->ses_lock); - } - - return rc; -} - -static int -cifs_set_vol_auth(struct smb3_fs_context *ctx, struct cifs_ses *ses) -{ - ctx->sectype = ses->sectype; - - /* krb5 is special, since we don't need username or pw */ - if (ctx->sectype == Kerberos) - return 0; - - return cifs_set_cifscreds(ctx, ses); -} - -static struct cifs_tcon * -cifs_construct_tcon(struct cifs_sb_info *cifs_sb, kuid_t fsuid) -{ - int rc; - struct cifs_tcon *master_tcon = cifs_sb_master_tcon(cifs_sb); - struct cifs_ses *ses; - struct cifs_tcon *tcon = NULL; - struct smb3_fs_context *ctx; - - ctx = kzalloc(sizeof(*ctx), GFP_KERNEL); - if (ctx == NULL) - return ERR_PTR(-ENOMEM); - - ctx->local_nls = cifs_sb->local_nls; - ctx->linux_uid = fsuid; - ctx->cred_uid = fsuid; - ctx->UNC = master_tcon->tree_name; - ctx->retry = master_tcon->retry; - ctx->nocase = master_tcon->nocase; - ctx->nohandlecache = master_tcon->nohandlecache; - ctx->local_lease = master_tcon->local_lease; - ctx->no_lease = master_tcon->no_lease; - ctx->resilient = master_tcon->use_resilient; - ctx->persistent = master_tcon->use_persistent; - ctx->handle_timeout = master_tcon->handle_timeout; - ctx->no_linux_ext = !master_tcon->unix_ext; - ctx->linux_ext = master_tcon->posix_extensions; - ctx->sectype = master_tcon->ses->sectype; - ctx->sign = master_tcon->ses->sign; - ctx->seal = master_tcon->seal; - ctx->witness = master_tcon->use_witness; - - rc = cifs_set_vol_auth(ctx, master_tcon->ses); - if (rc) { - tcon = ERR_PTR(rc); - goto out; - } - - /* get a reference for the same TCP session */ - spin_lock(&cifs_tcp_ses_lock); - ++master_tcon->ses->server->srv_count; - spin_unlock(&cifs_tcp_ses_lock); - - ses = cifs_get_smb_ses(master_tcon->ses->server, ctx); - if (IS_ERR(ses)) { - tcon = (struct cifs_tcon *)ses; - cifs_put_tcp_session(master_tcon->ses->server, 0); - goto out; - } - - tcon = cifs_get_tcon(ses, ctx); - if (IS_ERR(tcon)) { - cifs_put_smb_ses(ses); - goto out; - } - -#ifdef CONFIG_CIFS_ALLOW_INSECURE_LEGACY - if (cap_unix(ses)) - reset_cifs_unix_caps(0, tcon, NULL, ctx); -#endif /* CONFIG_CIFS_ALLOW_INSECURE_LEGACY */ - -out: - kfree(ctx->username); - kfree_sensitive(ctx->password); - kfree(ctx); - - return tcon; -} - -struct cifs_tcon * -cifs_sb_master_tcon(struct cifs_sb_info *cifs_sb) -{ - return tlink_tcon(cifs_sb_master_tlink(cifs_sb)); -} - -/* find and return a tlink with given uid */ -static struct tcon_link * -tlink_rb_search(struct rb_root *root, kuid_t uid) -{ - struct rb_node *node = root->rb_node; - struct tcon_link *tlink; - - while (node) { - tlink = rb_entry(node, struct tcon_link, tl_rbnode); - - if (uid_gt(tlink->tl_uid, uid)) - node = node->rb_left; - else if (uid_lt(tlink->tl_uid, uid)) - node = node->rb_right; - else - return tlink; - } - return NULL; -} - -/* insert a tcon_link into the tree */ -static void -tlink_rb_insert(struct rb_root *root, struct tcon_link *new_tlink) -{ - struct rb_node **new = &(root->rb_node), *parent = NULL; - struct tcon_link *tlink; - - while (*new) { - tlink = rb_entry(*new, struct tcon_link, tl_rbnode); - parent = *new; - - if (uid_gt(tlink->tl_uid, new_tlink->tl_uid)) - new = &((*new)->rb_left); - else - new = &((*new)->rb_right); - } - - rb_link_node(&new_tlink->tl_rbnode, parent, new); - rb_insert_color(&new_tlink->tl_rbnode, root); -} - -/* - * Find or construct an appropriate tcon given a cifs_sb and the fsuid of the - * current task. - * - * If the superblock doesn't refer to a multiuser mount, then just return - * the master tcon for the mount. - * - * First, search the rbtree for an existing tcon for this fsuid. If one - * exists, then check to see if it's pending construction. If it is then wait - * for construction to complete. Once it's no longer pending, check to see if - * it failed and either return an error or retry construction, depending on - * the timeout. - * - * If one doesn't exist then insert a new tcon_link struct into the tree and - * try to construct a new one. - */ -struct tcon_link * -cifs_sb_tlink(struct cifs_sb_info *cifs_sb) -{ - int ret; - kuid_t fsuid = current_fsuid(); - struct tcon_link *tlink, *newtlink; - - if (!(cifs_sb->mnt_cifs_flags & CIFS_MOUNT_MULTIUSER)) - return cifs_get_tlink(cifs_sb_master_tlink(cifs_sb)); - - spin_lock(&cifs_sb->tlink_tree_lock); - tlink = tlink_rb_search(&cifs_sb->tlink_tree, fsuid); - if (tlink) - cifs_get_tlink(tlink); - spin_unlock(&cifs_sb->tlink_tree_lock); - - if (tlink == NULL) { - newtlink = kzalloc(sizeof(*tlink), GFP_KERNEL); - if (newtlink == NULL) - return ERR_PTR(-ENOMEM); - newtlink->tl_uid = fsuid; - newtlink->tl_tcon = ERR_PTR(-EACCES); - set_bit(TCON_LINK_PENDING, &newtlink->tl_flags); - set_bit(TCON_LINK_IN_TREE, &newtlink->tl_flags); - cifs_get_tlink(newtlink); - - spin_lock(&cifs_sb->tlink_tree_lock); - /* was one inserted after previous search? */ - tlink = tlink_rb_search(&cifs_sb->tlink_tree, fsuid); - if (tlink) { - cifs_get_tlink(tlink); - spin_unlock(&cifs_sb->tlink_tree_lock); - kfree(newtlink); - goto wait_for_construction; - } - tlink = newtlink; - tlink_rb_insert(&cifs_sb->tlink_tree, tlink); - spin_unlock(&cifs_sb->tlink_tree_lock); - } else { -wait_for_construction: - ret = wait_on_bit(&tlink->tl_flags, TCON_LINK_PENDING, - TASK_INTERRUPTIBLE); - if (ret) { - cifs_put_tlink(tlink); - return ERR_PTR(-ERESTARTSYS); - } - - /* if it's good, return it */ - if (!IS_ERR(tlink->tl_tcon)) - return tlink; - - /* return error if we tried this already recently */ - if (time_before(jiffies, tlink->tl_time + TLINK_ERROR_EXPIRE)) { - cifs_put_tlink(tlink); - return ERR_PTR(-EACCES); - } - - if (test_and_set_bit(TCON_LINK_PENDING, &tlink->tl_flags)) - goto wait_for_construction; - } - - tlink->tl_tcon = cifs_construct_tcon(cifs_sb, fsuid); - clear_bit(TCON_LINK_PENDING, &tlink->tl_flags); - wake_up_bit(&tlink->tl_flags, TCON_LINK_PENDING); - - if (IS_ERR(tlink->tl_tcon)) { - cifs_put_tlink(tlink); - return ERR_PTR(-EACCES); - } - - return tlink; -} - -/* - * periodic workqueue job that scans tcon_tree for a superblock and closes - * out tcons. - */ -static void -cifs_prune_tlinks(struct work_struct *work) -{ - struct cifs_sb_info *cifs_sb = container_of(work, struct cifs_sb_info, - prune_tlinks.work); - struct rb_root *root = &cifs_sb->tlink_tree; - struct rb_node *node; - struct rb_node *tmp; - struct tcon_link *tlink; - - /* - * Because we drop the spinlock in the loop in order to put the tlink - * it's not guarded against removal of links from the tree. The only - * places that remove entries from the tree are this function and - * umounts. Because this function is non-reentrant and is canceled - * before umount can proceed, this is safe. - */ - spin_lock(&cifs_sb->tlink_tree_lock); - node = rb_first(root); - while (node != NULL) { - tmp = node; - node = rb_next(tmp); - tlink = rb_entry(tmp, struct tcon_link, tl_rbnode); - - if (test_bit(TCON_LINK_MASTER, &tlink->tl_flags) || - atomic_read(&tlink->tl_count) != 0 || - time_after(tlink->tl_time + TLINK_IDLE_EXPIRE, jiffies)) - continue; - - cifs_get_tlink(tlink); - clear_bit(TCON_LINK_IN_TREE, &tlink->tl_flags); - rb_erase(tmp, root); - - spin_unlock(&cifs_sb->tlink_tree_lock); - cifs_put_tlink(tlink); - spin_lock(&cifs_sb->tlink_tree_lock); - } - spin_unlock(&cifs_sb->tlink_tree_lock); - - queue_delayed_work(cifsiod_wq, &cifs_sb->prune_tlinks, - TLINK_IDLE_EXPIRE); -} - -#ifdef CONFIG_CIFS_DFS_UPCALL -/* Update dfs referral path of superblock */ -static int update_server_fullpath(struct TCP_Server_Info *server, struct cifs_sb_info *cifs_sb, - const char *target) -{ - int rc = 0; - size_t len = strlen(target); - char *refpath, *npath; - - if (unlikely(len < 2 || *target != '\\')) - return -EINVAL; - - if (target[1] == '\\') { - len += 1; - refpath = kmalloc(len, GFP_KERNEL); - if (!refpath) - return -ENOMEM; - - scnprintf(refpath, len, "%s", target); - } else { - len += sizeof("\\"); - refpath = kmalloc(len, GFP_KERNEL); - if (!refpath) - return -ENOMEM; - - scnprintf(refpath, len, "\\%s", target); - } - - npath = dfs_cache_canonical_path(refpath, cifs_sb->local_nls, cifs_remap(cifs_sb)); - kfree(refpath); - - if (IS_ERR(npath)) { - rc = PTR_ERR(npath); - } else { - mutex_lock(&server->refpath_lock); - kfree(server->leaf_fullpath); - server->leaf_fullpath = npath; - mutex_unlock(&server->refpath_lock); - server->current_fullpath = server->leaf_fullpath; - } - return rc; -} - -static int target_share_matches_server(struct TCP_Server_Info *server, const char *tcp_host, - size_t tcp_host_len, char *share, bool *target_match) -{ - int rc = 0; - const char *dfs_host; - size_t dfs_host_len; - - *target_match = true; - extract_unc_hostname(share, &dfs_host, &dfs_host_len); - - /* Check if hostnames or addresses match */ - if (dfs_host_len != tcp_host_len || strncasecmp(dfs_host, tcp_host, dfs_host_len) != 0) { - cifs_dbg(FYI, "%s: %.*s doesn't match %.*s\n", __func__, (int)dfs_host_len, - dfs_host, (int)tcp_host_len, tcp_host); - rc = match_target_ip(server, dfs_host, dfs_host_len, target_match); - if (rc) - cifs_dbg(VFS, "%s: failed to match target ip: %d\n", __func__, rc); - } - return rc; -} - -static int __tree_connect_dfs_target(const unsigned int xid, struct cifs_tcon *tcon, - struct cifs_sb_info *cifs_sb, char *tree, bool islink, - struct dfs_cache_tgt_list *tl) -{ - int rc; - struct TCP_Server_Info *server = tcon->ses->server; - const struct smb_version_operations *ops = server->ops; - struct cifs_tcon *ipc = tcon->ses->tcon_ipc; - char *share = NULL, *prefix = NULL; - const char *tcp_host; - size_t tcp_host_len; - struct dfs_cache_tgt_iterator *tit; - bool target_match; - - extract_unc_hostname(server->hostname, &tcp_host, &tcp_host_len); - - tit = dfs_cache_get_tgt_iterator(tl); - if (!tit) { - rc = -ENOENT; - goto out; - } - - /* Try to tree connect to all dfs targets */ - for (; tit; tit = dfs_cache_get_next_tgt(tl, tit)) { - const char *target = dfs_cache_get_tgt_name(tit); - struct dfs_cache_tgt_list ntl = DFS_CACHE_TGT_LIST_INIT(ntl); - - kfree(share); - kfree(prefix); - share = prefix = NULL; - - /* Check if share matches with tcp ses */ - rc = dfs_cache_get_tgt_share(server->current_fullpath + 1, tit, &share, &prefix); - if (rc) { - cifs_dbg(VFS, "%s: failed to parse target share: %d\n", __func__, rc); - break; - } - - rc = target_share_matches_server(server, tcp_host, tcp_host_len, share, - &target_match); - if (rc) - break; - if (!target_match) { - rc = -EHOSTUNREACH; - continue; - } - - if (ipc->need_reconnect) { - scnprintf(tree, MAX_TREE_SIZE, "\\\\%s\\IPC$", server->hostname); - rc = ops->tree_connect(xid, ipc->ses, tree, ipc, cifs_sb->local_nls); - if (rc) - break; - } - - scnprintf(tree, MAX_TREE_SIZE, "\\%s", share); - if (!islink) { - rc = ops->tree_connect(xid, tcon->ses, tree, tcon, cifs_sb->local_nls); - break; - } - /* - * If no dfs referrals were returned from link target, then just do a TREE_CONNECT - * to it. Otherwise, cache the dfs referral and then mark current tcp ses for - * reconnect so either the demultiplex thread or the echo worker will reconnect to - * newly resolved target. - */ - if (dfs_cache_find(xid, tcon->ses, cifs_sb->local_nls, cifs_remap(cifs_sb), target, - NULL, &ntl)) { - rc = ops->tree_connect(xid, tcon->ses, tree, tcon, cifs_sb->local_nls); - if (rc) - continue; - rc = dfs_cache_noreq_update_tgthint(server->current_fullpath + 1, tit); - if (!rc) - rc = cifs_update_super_prepath(cifs_sb, prefix); - } else { - /* Target is another dfs share */ - rc = update_server_fullpath(server, cifs_sb, target); - dfs_cache_free_tgts(tl); - - if (!rc) { - rc = -EREMOTE; - list_replace_init(&ntl.tl_list, &tl->tl_list); - } else - dfs_cache_free_tgts(&ntl); - } - break; - } - -out: - kfree(share); - kfree(prefix); - - return rc; -} - -static int tree_connect_dfs_target(const unsigned int xid, struct cifs_tcon *tcon, - struct cifs_sb_info *cifs_sb, char *tree, bool islink, - struct dfs_cache_tgt_list *tl) -{ - int rc; - int num_links = 0; - struct TCP_Server_Info *server = tcon->ses->server; - - do { - rc = __tree_connect_dfs_target(xid, tcon, cifs_sb, tree, islink, tl); - if (!rc || rc != -EREMOTE) - break; - } while (rc = -ELOOP, ++num_links < MAX_NESTED_LINKS); - /* - * If we couldn't tree connect to any targets from last referral path, then retry from - * original referral path. - */ - if (rc && server->current_fullpath != server->origin_fullpath) { - server->current_fullpath = server->origin_fullpath; - cifs_signal_cifsd_for_reconnect(server, true); - } - - dfs_cache_free_tgts(tl); - return rc; -} - -int cifs_tree_connect(const unsigned int xid, struct cifs_tcon *tcon, const struct nls_table *nlsc) -{ - int rc; - struct TCP_Server_Info *server = tcon->ses->server; - const struct smb_version_operations *ops = server->ops; - struct super_block *sb = NULL; - struct cifs_sb_info *cifs_sb; - struct dfs_cache_tgt_list tl = DFS_CACHE_TGT_LIST_INIT(tl); - char *tree; - struct dfs_info3_param ref = {0}; - - /* only send once per connect */ - spin_lock(&tcon->tc_lock); - if (tcon->ses->ses_status != SES_GOOD || - (tcon->status != TID_NEW && - tcon->status != TID_NEED_TCON)) { - spin_unlock(&tcon->tc_lock); - return 0; - } - tcon->status = TID_IN_TCON; - spin_unlock(&tcon->tc_lock); - - tree = kzalloc(MAX_TREE_SIZE, GFP_KERNEL); - if (!tree) { - rc = -ENOMEM; - goto out; - } - - if (tcon->ipc) { - scnprintf(tree, MAX_TREE_SIZE, "\\\\%s\\IPC$", server->hostname); - rc = ops->tree_connect(xid, tcon->ses, tree, tcon, nlsc); - goto out; - } - - sb = cifs_get_tcp_super(server); - if (IS_ERR(sb)) { - rc = PTR_ERR(sb); - cifs_dbg(VFS, "%s: could not find superblock: %d\n", __func__, rc); - goto out; - } - - cifs_sb = CIFS_SB(sb); - - /* If it is not dfs or there was no cached dfs referral, then reconnect to same share */ - if (!server->current_fullpath || - dfs_cache_noreq_find(server->current_fullpath + 1, &ref, &tl)) { - rc = ops->tree_connect(xid, tcon->ses, tcon->tree_name, tcon, cifs_sb->local_nls); - goto out; - } - - rc = tree_connect_dfs_target(xid, tcon, cifs_sb, tree, ref.server_type == DFS_TYPE_LINK, - &tl); - free_dfs_info_param(&ref); - -out: - kfree(tree); - cifs_put_tcp_super(sb); - - if (rc) { - spin_lock(&tcon->tc_lock); - if (tcon->status == TID_IN_TCON) - tcon->status = TID_NEED_TCON; - spin_unlock(&tcon->tc_lock); - } else { - spin_lock(&tcon->tc_lock); - if (tcon->status == TID_IN_TCON) - tcon->status = TID_GOOD; - spin_unlock(&tcon->tc_lock); - tcon->need_reconnect = false; - } - - return rc; -} -#else -int cifs_tree_connect(const unsigned int xid, struct cifs_tcon *tcon, const struct nls_table *nlsc) -{ - int rc; - const struct smb_version_operations *ops = tcon->ses->server->ops; - - /* only send once per connect */ - spin_lock(&tcon->tc_lock); - if (tcon->ses->ses_status != SES_GOOD || - (tcon->status != TID_NEW && - tcon->status != TID_NEED_TCON)) { - spin_unlock(&tcon->tc_lock); - return 0; - } - tcon->status = TID_IN_TCON; - spin_unlock(&tcon->tc_lock); - - rc = ops->tree_connect(xid, tcon->ses, tcon->tree_name, tcon, nlsc); - if (rc) { - spin_lock(&tcon->tc_lock); - if (tcon->status == TID_IN_TCON) - tcon->status = TID_NEED_TCON; - spin_unlock(&tcon->tc_lock); - } else { - spin_lock(&tcon->tc_lock); - if (tcon->status == TID_IN_TCON) - tcon->status = TID_GOOD; - tcon->need_reconnect = false; - spin_unlock(&tcon->tc_lock); - } - - return rc; -} -#endif diff --git a/fs/cifs/dfs_cache.c b/fs/cifs/dfs_cache.c deleted file mode 100644 index 3bc1d3494be3a..0000000000000 --- a/fs/cifs/dfs_cache.c +++ /dev/null @@ -1,1690 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0 -/* - * DFS referral cache routines - * - * Copyright (c) 2018-2019 Paulo Alcantara - */ - -#include -#include -#include -#include -#include -#include -#include -#include "cifsglob.h" -#include "smb2pdu.h" -#include "smb2proto.h" -#include "cifsproto.h" -#include "cifs_debug.h" -#include "cifs_unicode.h" -#include "smb2glob.h" -#include "dns_resolve.h" - -#include "dfs_cache.h" - -#define CACHE_HTABLE_SIZE 32 -#define CACHE_MAX_ENTRIES 64 -#define CACHE_MIN_TTL 120 /* 2 minutes */ - -#define IS_DFS_INTERLINK(v) (((v) & DFSREF_REFERRAL_SERVER) && !((v) & DFSREF_STORAGE_SERVER)) - -struct cache_dfs_tgt { - char *name; - int path_consumed; - struct list_head list; -}; - -struct cache_entry { - struct hlist_node hlist; - const char *path; - int hdr_flags; /* RESP_GET_DFS_REFERRAL.ReferralHeaderFlags */ - int ttl; /* DFS_REREFERRAL_V3.TimeToLive */ - int srvtype; /* DFS_REREFERRAL_V3.ServerType */ - int ref_flags; /* DFS_REREFERRAL_V3.ReferralEntryFlags */ - struct timespec64 etime; - int path_consumed; /* RESP_GET_DFS_REFERRAL.PathConsumed */ - int numtgts; - struct list_head tlist; - struct cache_dfs_tgt *tgthint; -}; - -/* List of referral server sessions per dfs mount */ -struct mount_group { - struct list_head list; - uuid_t id; - struct cifs_ses *sessions[CACHE_MAX_ENTRIES]; - int num_sessions; - spinlock_t lock; - struct list_head refresh_list; - struct kref refcount; -}; - -static struct kmem_cache *cache_slab __read_mostly; -static struct workqueue_struct *dfscache_wq __read_mostly; - -static int cache_ttl; -static DEFINE_SPINLOCK(cache_ttl_lock); - -static struct nls_table *cache_cp; - -/* - * Number of entries in the cache - */ -static atomic_t cache_count; - -static struct hlist_head cache_htable[CACHE_HTABLE_SIZE]; -static DECLARE_RWSEM(htable_rw_lock); - -static LIST_HEAD(mount_group_list); -static DEFINE_MUTEX(mount_group_list_lock); - -static void refresh_cache_worker(struct work_struct *work); - -static DECLARE_DELAYED_WORK(refresh_task, refresh_cache_worker); - -static void get_ipc_unc(const char *ref_path, char *ipc, size_t ipclen) -{ - const char *host; - size_t len; - - extract_unc_hostname(ref_path, &host, &len); - scnprintf(ipc, ipclen, "\\\\%.*s\\IPC$", (int)len, host); -} - -static struct cifs_ses *find_ipc_from_server_path(struct cifs_ses **ses, const char *path) -{ - char unc[SERVER_NAME_LENGTH + sizeof("//x/IPC$")] = {0}; - - get_ipc_unc(path, unc, sizeof(unc)); - for (; *ses; ses++) { - if (!strcasecmp(unc, (*ses)->tcon_ipc->tree_name)) - return *ses; - } - return ERR_PTR(-ENOENT); -} - -static void __mount_group_release(struct mount_group *mg) -{ - int i; - - for (i = 0; i < mg->num_sessions; i++) - cifs_put_smb_ses(mg->sessions[i]); - kfree(mg); -} - -static void mount_group_release(struct kref *kref) -{ - struct mount_group *mg = container_of(kref, struct mount_group, refcount); - - mutex_lock(&mount_group_list_lock); - list_del(&mg->list); - mutex_unlock(&mount_group_list_lock); - __mount_group_release(mg); -} - -static struct mount_group *find_mount_group_locked(const uuid_t *id) -{ - struct mount_group *mg; - - list_for_each_entry(mg, &mount_group_list, list) { - if (uuid_equal(&mg->id, id)) - return mg; - } - return ERR_PTR(-ENOENT); -} - -static struct mount_group *__get_mount_group_locked(const uuid_t *id) -{ - struct mount_group *mg; - - mg = find_mount_group_locked(id); - if (!IS_ERR(mg)) - return mg; - - mg = kmalloc(sizeof(*mg), GFP_KERNEL); - if (!mg) - return ERR_PTR(-ENOMEM); - kref_init(&mg->refcount); - uuid_copy(&mg->id, id); - mg->num_sessions = 0; - spin_lock_init(&mg->lock); - list_add(&mg->list, &mount_group_list); - return mg; -} - -static struct mount_group *get_mount_group(const uuid_t *id) -{ - struct mount_group *mg; - - mutex_lock(&mount_group_list_lock); - mg = __get_mount_group_locked(id); - if (!IS_ERR(mg)) - kref_get(&mg->refcount); - mutex_unlock(&mount_group_list_lock); - - return mg; -} - -static void free_mount_group_list(void) -{ - struct mount_group *mg, *tmp_mg; - - list_for_each_entry_safe(mg, tmp_mg, &mount_group_list, list) { - list_del_init(&mg->list); - __mount_group_release(mg); - } -} - -/** - * dfs_cache_canonical_path - get a canonical DFS path - * - * @path: DFS path - * @cp: codepage - * @remap: mapping type - * - * Return canonical path if success, otherwise error. - */ -char *dfs_cache_canonical_path(const char *path, const struct nls_table *cp, int remap) -{ - char *tmp; - int plen = 0; - char *npath; - - if (!path || strlen(path) < 3 || (*path != '\\' && *path != '/')) - return ERR_PTR(-EINVAL); - - if (unlikely(strcmp(cp->charset, cache_cp->charset))) { - tmp = (char *)cifs_strndup_to_utf16(path, strlen(path), &plen, cp, remap); - if (!tmp) { - cifs_dbg(VFS, "%s: failed to convert path to utf16\n", __func__); - return ERR_PTR(-EINVAL); - } - - npath = cifs_strndup_from_utf16(tmp, plen, true, cache_cp); - kfree(tmp); - - if (!npath) { - cifs_dbg(VFS, "%s: failed to convert path from utf16\n", __func__); - return ERR_PTR(-EINVAL); - } - } else { - npath = kstrdup(path, GFP_KERNEL); - if (!npath) - return ERR_PTR(-ENOMEM); - } - convert_delimiter(npath, '\\'); - return npath; -} - -static inline bool cache_entry_expired(const struct cache_entry *ce) -{ - struct timespec64 ts; - - ktime_get_coarse_real_ts64(&ts); - return timespec64_compare(&ts, &ce->etime) >= 0; -} - -static inline void free_tgts(struct cache_entry *ce) -{ - struct cache_dfs_tgt *t, *n; - - list_for_each_entry_safe(t, n, &ce->tlist, list) { - list_del(&t->list); - kfree(t->name); - kfree(t); - } -} - -static inline void flush_cache_ent(struct cache_entry *ce) -{ - hlist_del_init(&ce->hlist); - kfree(ce->path); - free_tgts(ce); - atomic_dec(&cache_count); - kmem_cache_free(cache_slab, ce); -} - -static void flush_cache_ents(void) -{ - int i; - - for (i = 0; i < CACHE_HTABLE_SIZE; i++) { - struct hlist_head *l = &cache_htable[i]; - struct hlist_node *n; - struct cache_entry *ce; - - hlist_for_each_entry_safe(ce, n, l, hlist) { - if (!hlist_unhashed(&ce->hlist)) - flush_cache_ent(ce); - } - } -} - -/* - * dfs cache /proc file - */ -static int dfscache_proc_show(struct seq_file *m, void *v) -{ - int i; - struct cache_entry *ce; - struct cache_dfs_tgt *t; - - seq_puts(m, "DFS cache\n---------\n"); - - down_read(&htable_rw_lock); - for (i = 0; i < CACHE_HTABLE_SIZE; i++) { - struct hlist_head *l = &cache_htable[i]; - - hlist_for_each_entry(ce, l, hlist) { - if (hlist_unhashed(&ce->hlist)) - continue; - - seq_printf(m, - "cache entry: path=%s,type=%s,ttl=%d,etime=%ld,hdr_flags=0x%x,ref_flags=0x%x,interlink=%s,path_consumed=%d,expired=%s\n", - ce->path, ce->srvtype == DFS_TYPE_ROOT ? "root" : "link", - ce->ttl, ce->etime.tv_nsec, ce->hdr_flags, ce->ref_flags, - IS_DFS_INTERLINK(ce->hdr_flags) ? "yes" : "no", - ce->path_consumed, cache_entry_expired(ce) ? "yes" : "no"); - - list_for_each_entry(t, &ce->tlist, list) { - seq_printf(m, " %s%s\n", - t->name, - ce->tgthint == t ? " (target hint)" : ""); - } - } - } - up_read(&htable_rw_lock); - - return 0; -} - -static ssize_t dfscache_proc_write(struct file *file, const char __user *buffer, - size_t count, loff_t *ppos) -{ - char c; - int rc; - - rc = get_user(c, buffer); - if (rc) - return rc; - - if (c != '0') - return -EINVAL; - - cifs_dbg(FYI, "clearing dfs cache\n"); - - down_write(&htable_rw_lock); - flush_cache_ents(); - up_write(&htable_rw_lock); - - return count; -} - -static int dfscache_proc_open(struct inode *inode, struct file *file) -{ - return single_open(file, dfscache_proc_show, NULL); -} - -const struct proc_ops dfscache_proc_ops = { - .proc_open = dfscache_proc_open, - .proc_read = seq_read, - .proc_lseek = seq_lseek, - .proc_release = single_release, - .proc_write = dfscache_proc_write, -}; - -#ifdef CONFIG_CIFS_DEBUG2 -static inline void dump_tgts(const struct cache_entry *ce) -{ - struct cache_dfs_tgt *t; - - cifs_dbg(FYI, "target list:\n"); - list_for_each_entry(t, &ce->tlist, list) { - cifs_dbg(FYI, " %s%s\n", t->name, - ce->tgthint == t ? " (target hint)" : ""); - } -} - -static inline void dump_ce(const struct cache_entry *ce) -{ - cifs_dbg(FYI, "cache entry: path=%s,type=%s,ttl=%d,etime=%ld,hdr_flags=0x%x,ref_flags=0x%x,interlink=%s,path_consumed=%d,expired=%s\n", - ce->path, - ce->srvtype == DFS_TYPE_ROOT ? "root" : "link", ce->ttl, - ce->etime.tv_nsec, - ce->hdr_flags, ce->ref_flags, - IS_DFS_INTERLINK(ce->hdr_flags) ? "yes" : "no", - ce->path_consumed, - cache_entry_expired(ce) ? "yes" : "no"); - dump_tgts(ce); -} - -static inline void dump_refs(const struct dfs_info3_param *refs, int numrefs) -{ - int i; - - cifs_dbg(FYI, "DFS referrals returned by the server:\n"); - for (i = 0; i < numrefs; i++) { - const struct dfs_info3_param *ref = &refs[i]; - - cifs_dbg(FYI, - "\n" - "flags: 0x%x\n" - "path_consumed: %d\n" - "server_type: 0x%x\n" - "ref_flag: 0x%x\n" - "path_name: %s\n" - "node_name: %s\n" - "ttl: %d (%dm)\n", - ref->flags, ref->path_consumed, ref->server_type, - ref->ref_flag, ref->path_name, ref->node_name, - ref->ttl, ref->ttl / 60); - } -} -#else -#define dump_tgts(e) -#define dump_ce(e) -#define dump_refs(r, n) -#endif - -/** - * dfs_cache_init - Initialize DFS referral cache. - * - * Return zero if initialized successfully, otherwise non-zero. - */ -int dfs_cache_init(void) -{ - int rc; - int i; - - dfscache_wq = alloc_workqueue("cifs-dfscache", WQ_FREEZABLE | WQ_UNBOUND, 1); - if (!dfscache_wq) - return -ENOMEM; - - cache_slab = kmem_cache_create("cifs_dfs_cache", - sizeof(struct cache_entry), 0, - SLAB_HWCACHE_ALIGN, NULL); - if (!cache_slab) { - rc = -ENOMEM; - goto out_destroy_wq; - } - - for (i = 0; i < CACHE_HTABLE_SIZE; i++) - INIT_HLIST_HEAD(&cache_htable[i]); - - atomic_set(&cache_count, 0); - cache_cp = load_nls("utf8"); - if (!cache_cp) - cache_cp = load_nls_default(); - - cifs_dbg(FYI, "%s: initialized DFS referral cache\n", __func__); - return 0; - -out_destroy_wq: - destroy_workqueue(dfscache_wq); - return rc; -} - -static int cache_entry_hash(const void *data, int size, unsigned int *hash) -{ - int i, clen; - const unsigned char *s = data; - wchar_t c; - unsigned int h = 0; - - for (i = 0; i < size; i += clen) { - clen = cache_cp->char2uni(&s[i], size - i, &c); - if (unlikely(clen < 0)) { - cifs_dbg(VFS, "%s: can't convert char\n", __func__); - return clen; - } - c = cifs_toupper(c); - h = jhash(&c, sizeof(c), h); - } - *hash = h % CACHE_HTABLE_SIZE; - return 0; -} - -/* Return target hint of a DFS cache entry */ -static inline char *get_tgt_name(const struct cache_entry *ce) -{ - struct cache_dfs_tgt *t = ce->tgthint; - - return t ? t->name : ERR_PTR(-ENOENT); -} - -/* Return expire time out of a new entry's TTL */ -static inline struct timespec64 get_expire_time(int ttl) -{ - struct timespec64 ts = { - .tv_sec = ttl, - .tv_nsec = 0, - }; - struct timespec64 now; - - ktime_get_coarse_real_ts64(&now); - return timespec64_add(now, ts); -} - -/* Allocate a new DFS target */ -static struct cache_dfs_tgt *alloc_target(const char *name, int path_consumed) -{ - struct cache_dfs_tgt *t; - - t = kmalloc(sizeof(*t), GFP_ATOMIC); - if (!t) - return ERR_PTR(-ENOMEM); - t->name = kstrdup(name, GFP_ATOMIC); - if (!t->name) { - kfree(t); - return ERR_PTR(-ENOMEM); - } - t->path_consumed = path_consumed; - INIT_LIST_HEAD(&t->list); - return t; -} - -/* - * Copy DFS referral information to a cache entry and conditionally update - * target hint. - */ -static int copy_ref_data(const struct dfs_info3_param *refs, int numrefs, - struct cache_entry *ce, const char *tgthint) -{ - int i; - - ce->ttl = max_t(int, refs[0].ttl, CACHE_MIN_TTL); - ce->etime = get_expire_time(ce->ttl); - ce->srvtype = refs[0].server_type; - ce->hdr_flags = refs[0].flags; - ce->ref_flags = refs[0].ref_flag; - ce->path_consumed = refs[0].path_consumed; - - for (i = 0; i < numrefs; i++) { - struct cache_dfs_tgt *t; - - t = alloc_target(refs[i].node_name, refs[i].path_consumed); - if (IS_ERR(t)) { - free_tgts(ce); - return PTR_ERR(t); - } - if (tgthint && !strcasecmp(t->name, tgthint)) { - list_add(&t->list, &ce->tlist); - tgthint = NULL; - } else { - list_add_tail(&t->list, &ce->tlist); - } - ce->numtgts++; - } - - ce->tgthint = list_first_entry_or_null(&ce->tlist, - struct cache_dfs_tgt, list); - - return 0; -} - -/* Allocate a new cache entry */ -static struct cache_entry *alloc_cache_entry(struct dfs_info3_param *refs, int numrefs) -{ - struct cache_entry *ce; - int rc; - - ce = kmem_cache_zalloc(cache_slab, GFP_KERNEL); - if (!ce) - return ERR_PTR(-ENOMEM); - - ce->path = refs[0].path_name; - refs[0].path_name = NULL; - - INIT_HLIST_NODE(&ce->hlist); - INIT_LIST_HEAD(&ce->tlist); - - rc = copy_ref_data(refs, numrefs, ce, NULL); - if (rc) { - kfree(ce->path); - kmem_cache_free(cache_slab, ce); - ce = ERR_PTR(rc); - } - return ce; -} - -static void remove_oldest_entry_locked(void) -{ - int i; - struct cache_entry *ce; - struct cache_entry *to_del = NULL; - - WARN_ON(!rwsem_is_locked(&htable_rw_lock)); - - for (i = 0; i < CACHE_HTABLE_SIZE; i++) { - struct hlist_head *l = &cache_htable[i]; - - hlist_for_each_entry(ce, l, hlist) { - if (hlist_unhashed(&ce->hlist)) - continue; - if (!to_del || timespec64_compare(&ce->etime, - &to_del->etime) < 0) - to_del = ce; - } - } - - if (!to_del) { - cifs_dbg(FYI, "%s: no entry to remove\n", __func__); - return; - } - - cifs_dbg(FYI, "%s: removing entry\n", __func__); - dump_ce(to_del); - flush_cache_ent(to_del); -} - -/* Add a new DFS cache entry */ -static int add_cache_entry_locked(struct dfs_info3_param *refs, int numrefs) -{ - int rc; - struct cache_entry *ce; - unsigned int hash; - - WARN_ON(!rwsem_is_locked(&htable_rw_lock)); - - if (atomic_read(&cache_count) >= CACHE_MAX_ENTRIES) { - cifs_dbg(FYI, "%s: reached max cache size (%d)\n", __func__, CACHE_MAX_ENTRIES); - remove_oldest_entry_locked(); - } - - rc = cache_entry_hash(refs[0].path_name, strlen(refs[0].path_name), &hash); - if (rc) - return rc; - - ce = alloc_cache_entry(refs, numrefs); - if (IS_ERR(ce)) - return PTR_ERR(ce); - - spin_lock(&cache_ttl_lock); - if (!cache_ttl) { - cache_ttl = ce->ttl; - queue_delayed_work(dfscache_wq, &refresh_task, cache_ttl * HZ); - } else { - cache_ttl = min_t(int, cache_ttl, ce->ttl); - mod_delayed_work(dfscache_wq, &refresh_task, cache_ttl * HZ); - } - spin_unlock(&cache_ttl_lock); - - hlist_add_head(&ce->hlist, &cache_htable[hash]); - dump_ce(ce); - - atomic_inc(&cache_count); - - return 0; -} - -/* Check if two DFS paths are equal. @s1 and @s2 are expected to be in @cache_cp's charset */ -static bool dfs_path_equal(const char *s1, int len1, const char *s2, int len2) -{ - int i, l1, l2; - wchar_t c1, c2; - - if (len1 != len2) - return false; - - for (i = 0; i < len1; i += l1) { - l1 = cache_cp->char2uni(&s1[i], len1 - i, &c1); - l2 = cache_cp->char2uni(&s2[i], len2 - i, &c2); - if (unlikely(l1 < 0 && l2 < 0)) { - if (s1[i] != s2[i]) - return false; - l1 = 1; - continue; - } - if (l1 != l2) - return false; - if (cifs_toupper(c1) != cifs_toupper(c2)) - return false; - } - return true; -} - -static struct cache_entry *__lookup_cache_entry(const char *path, unsigned int hash, int len) -{ - struct cache_entry *ce; - - hlist_for_each_entry(ce, &cache_htable[hash], hlist) { - if (dfs_path_equal(ce->path, strlen(ce->path), path, len)) { - dump_ce(ce); - return ce; - } - } - return ERR_PTR(-ENOENT); -} - -/* - * Find a DFS cache entry in hash table and optionally check prefix path against normalized @path. - * - * Use whole path components in the match. Must be called with htable_rw_lock held. - * - * Return ERR_PTR(-ENOENT) if the entry is not found. - */ -static struct cache_entry *lookup_cache_entry(const char *path) -{ - struct cache_entry *ce; - int cnt = 0; - const char *s = path, *e; - char sep = *s; - unsigned int hash; - int rc; - - while ((s = strchr(s, sep)) && ++cnt < 3) - s++; - - if (cnt < 3) { - rc = cache_entry_hash(path, strlen(path), &hash); - if (rc) - return ERR_PTR(rc); - return __lookup_cache_entry(path, hash, strlen(path)); - } - /* - * Handle paths that have more than two path components and are a complete prefix of the DFS - * referral request path (@path). - * - * See MS-DFSC 3.2.5.5 "Receiving a Root Referral Request or Link Referral Request". - */ - e = path + strlen(path) - 1; - while (e > s) { - int len; - - /* skip separators */ - while (e > s && *e == sep) - e--; - if (e == s) - break; - - len = e + 1 - path; - rc = cache_entry_hash(path, len, &hash); - if (rc) - return ERR_PTR(rc); - ce = __lookup_cache_entry(path, hash, len); - if (!IS_ERR(ce)) - return ce; - - /* backward until separator */ - while (e > s && *e != sep) - e--; - } - return ERR_PTR(-ENOENT); -} - -/** - * dfs_cache_destroy - destroy DFS referral cache - */ -void dfs_cache_destroy(void) -{ - cancel_delayed_work_sync(&refresh_task); - unload_nls(cache_cp); - free_mount_group_list(); - flush_cache_ents(); - kmem_cache_destroy(cache_slab); - destroy_workqueue(dfscache_wq); - - cifs_dbg(FYI, "%s: destroyed DFS referral cache\n", __func__); -} - -/* Update a cache entry with the new referral in @refs */ -static int update_cache_entry_locked(struct cache_entry *ce, const struct dfs_info3_param *refs, - int numrefs) -{ - int rc; - char *s, *th = NULL; - - WARN_ON(!rwsem_is_locked(&htable_rw_lock)); - - if (ce->tgthint) { - s = ce->tgthint->name; - th = kstrdup(s, GFP_ATOMIC); - if (!th) - return -ENOMEM; - } - - free_tgts(ce); - ce->numtgts = 0; - - rc = copy_ref_data(refs, numrefs, ce, th); - - kfree(th); - - return rc; -} - -static int get_dfs_referral(const unsigned int xid, struct cifs_ses *ses, const char *path, - struct dfs_info3_param **refs, int *numrefs) -{ - int rc; - int i; - - cifs_dbg(FYI, "%s: get an DFS referral for %s\n", __func__, path); - - *refs = NULL; - *numrefs = 0; - - if (!ses || !ses->server || !ses->server->ops->get_dfs_refer) - return -EOPNOTSUPP; - if (unlikely(!cache_cp)) - return -EINVAL; - - rc = ses->server->ops->get_dfs_refer(xid, ses, path, refs, numrefs, cache_cp, - NO_MAP_UNI_RSVD); - if (!rc) { - struct dfs_info3_param *ref = *refs; - - for (i = 0; i < *numrefs; i++) - convert_delimiter(ref[i].path_name, '\\'); - } - return rc; -} - -/* - * Find, create or update a DFS cache entry. - * - * If the entry wasn't found, it will create a new one. Or if it was found but - * expired, then it will update the entry accordingly. - * - * For interlinks, cifs_mount() and expand_dfs_referral() are supposed to - * handle them properly. - */ -static int cache_refresh_path(const unsigned int xid, struct cifs_ses *ses, const char *path) -{ - struct dfs_info3_param *refs = NULL; - struct cache_entry *ce; - int numrefs = 0; - int rc; - - cifs_dbg(FYI, "%s: search path: %s\n", __func__, path); - - down_read(&htable_rw_lock); - - ce = lookup_cache_entry(path); - if (!IS_ERR(ce) && !cache_entry_expired(ce)) { - up_read(&htable_rw_lock); - return 0; - } - /* - * Unlock shared access as we don't want to hold any locks while getting - * a new referral. The @ses used for performing the I/O could be - * reconnecting and it acquires @htable_rw_lock to look up the dfs cache - * in order to failover -- if necessary. - */ - up_read(&htable_rw_lock); - - /* - * Either the entry was not found, or it is expired. - * Request a new DFS referral in order to create or update a cache entry. - */ - rc = get_dfs_referral(xid, ses, path, &refs, &numrefs); - if (rc) - goto out; - - dump_refs(refs, numrefs); - - down_write(&htable_rw_lock); - /* Re-check as another task might have it added or refreshed already */ - ce = lookup_cache_entry(path); - if (!IS_ERR(ce)) { - if (cache_entry_expired(ce)) - rc = update_cache_entry_locked(ce, refs, numrefs); - } else { - rc = add_cache_entry_locked(refs, numrefs); - } - - up_write(&htable_rw_lock); -out: - free_dfs_info_array(refs, numrefs); - return rc; -} - -/* - * Set up a DFS referral from a given cache entry. - * - * Must be called with htable_rw_lock held. - */ -static int setup_referral(const char *path, struct cache_entry *ce, - struct dfs_info3_param *ref, const char *target) -{ - int rc; - - cifs_dbg(FYI, "%s: set up new ref\n", __func__); - - memset(ref, 0, sizeof(*ref)); - - ref->path_name = kstrdup(path, GFP_ATOMIC); - if (!ref->path_name) - return -ENOMEM; - - ref->node_name = kstrdup(target, GFP_ATOMIC); - if (!ref->node_name) { - rc = -ENOMEM; - goto err_free_path; - } - - ref->path_consumed = ce->path_consumed; - ref->ttl = ce->ttl; - ref->server_type = ce->srvtype; - ref->ref_flag = ce->ref_flags; - ref->flags = ce->hdr_flags; - - return 0; - -err_free_path: - kfree(ref->path_name); - ref->path_name = NULL; - return rc; -} - -/* Return target list of a DFS cache entry */ -static int get_targets(struct cache_entry *ce, struct dfs_cache_tgt_list *tl) -{ - int rc; - struct list_head *head = &tl->tl_list; - struct cache_dfs_tgt *t; - struct dfs_cache_tgt_iterator *it, *nit; - - memset(tl, 0, sizeof(*tl)); - INIT_LIST_HEAD(head); - - list_for_each_entry(t, &ce->tlist, list) { - it = kzalloc(sizeof(*it), GFP_ATOMIC); - if (!it) { - rc = -ENOMEM; - goto err_free_it; - } - - it->it_name = kstrdup(t->name, GFP_ATOMIC); - if (!it->it_name) { - kfree(it); - rc = -ENOMEM; - goto err_free_it; - } - it->it_path_consumed = t->path_consumed; - - if (ce->tgthint == t) - list_add(&it->it_list, head); - else - list_add_tail(&it->it_list, head); - } - - tl->tl_numtgts = ce->numtgts; - - return 0; - -err_free_it: - list_for_each_entry_safe(it, nit, head, it_list) { - list_del(&it->it_list); - kfree(it->it_name); - kfree(it); - } - return rc; -} - -/** - * dfs_cache_find - find a DFS cache entry - * - * If it doesn't find the cache entry, then it will get a DFS referral - * for @path and create a new entry. - * - * In case the cache entry exists but expired, it will get a DFS referral - * for @path and then update the respective cache entry. - * - * These parameters are passed down to the get_dfs_refer() call if it - * needs to be issued: - * @xid: syscall xid - * @ses: smb session to issue the request on - * @cp: codepage - * @remap: path character remapping type - * @path: path to lookup in DFS referral cache. - * - * @ref: when non-NULL, store single DFS referral result in it. - * @tgt_list: when non-NULL, store complete DFS target list in it. - * - * Return zero if the target was found, otherwise non-zero. - */ -int dfs_cache_find(const unsigned int xid, struct cifs_ses *ses, const struct nls_table *cp, - int remap, const char *path, struct dfs_info3_param *ref, - struct dfs_cache_tgt_list *tgt_list) -{ - int rc; - const char *npath; - struct cache_entry *ce; - - npath = dfs_cache_canonical_path(path, cp, remap); - if (IS_ERR(npath)) - return PTR_ERR(npath); - - rc = cache_refresh_path(xid, ses, npath); - if (rc) - goto out_free_path; - - down_read(&htable_rw_lock); - - ce = lookup_cache_entry(npath); - if (IS_ERR(ce)) { - up_read(&htable_rw_lock); - rc = PTR_ERR(ce); - goto out_free_path; - } - - if (ref) - rc = setup_referral(path, ce, ref, get_tgt_name(ce)); - else - rc = 0; - if (!rc && tgt_list) - rc = get_targets(ce, tgt_list); - - up_read(&htable_rw_lock); - -out_free_path: - kfree(npath); - return rc; -} - -/** - * dfs_cache_noreq_find - find a DFS cache entry without sending any requests to - * the currently connected server. - * - * NOTE: This function will neither update a cache entry in case it was - * expired, nor create a new cache entry if @path hasn't been found. It heavily - * relies on an existing cache entry. - * - * @path: canonical DFS path to lookup in the DFS referral cache. - * @ref: when non-NULL, store single DFS referral result in it. - * @tgt_list: when non-NULL, store complete DFS target list in it. - * - * Return 0 if successful. - * Return -ENOENT if the entry was not found. - * Return non-zero for other errors. - */ -int dfs_cache_noreq_find(const char *path, struct dfs_info3_param *ref, - struct dfs_cache_tgt_list *tgt_list) -{ - int rc; - struct cache_entry *ce; - - cifs_dbg(FYI, "%s: path: %s\n", __func__, path); - - down_read(&htable_rw_lock); - - ce = lookup_cache_entry(path); - if (IS_ERR(ce)) { - rc = PTR_ERR(ce); - goto out_unlock; - } - - if (ref) - rc = setup_referral(path, ce, ref, get_tgt_name(ce)); - else - rc = 0; - if (!rc && tgt_list) - rc = get_targets(ce, tgt_list); - -out_unlock: - up_read(&htable_rw_lock); - return rc; -} - -/** - * dfs_cache_update_tgthint - update target hint of a DFS cache entry - * - * If it doesn't find the cache entry, then it will get a DFS referral for @path - * and create a new entry. - * - * In case the cache entry exists but expired, it will get a DFS referral - * for @path and then update the respective cache entry. - * - * @xid: syscall id - * @ses: smb session - * @cp: codepage - * @remap: type of character remapping for paths - * @path: path to lookup in DFS referral cache - * @it: DFS target iterator - * - * Return zero if the target hint was updated successfully, otherwise non-zero. - */ -int dfs_cache_update_tgthint(const unsigned int xid, struct cifs_ses *ses, - const struct nls_table *cp, int remap, const char *path, - const struct dfs_cache_tgt_iterator *it) -{ - struct cache_dfs_tgt *t; - struct cache_entry *ce; - const char *npath; - int rc = 0; - - npath = dfs_cache_canonical_path(path, cp, remap); - if (IS_ERR(npath)) - return PTR_ERR(npath); - - cifs_dbg(FYI, "%s: update target hint - path: %s\n", __func__, npath); - - rc = cache_refresh_path(xid, ses, npath); - if (rc) - goto out_free_path; - - down_write(&htable_rw_lock); - - ce = lookup_cache_entry(npath); - if (IS_ERR(ce)) { - rc = PTR_ERR(ce); - goto out_unlock; - } - - t = ce->tgthint; - - if (likely(!strcasecmp(it->it_name, t->name))) - goto out_unlock; - - list_for_each_entry(t, &ce->tlist, list) { - if (!strcasecmp(t->name, it->it_name)) { - ce->tgthint = t; - cifs_dbg(FYI, "%s: new target hint: %s\n", __func__, - it->it_name); - break; - } - } - -out_unlock: - up_write(&htable_rw_lock); -out_free_path: - kfree(npath); - return rc; -} - -/** - * dfs_cache_noreq_update_tgthint - update target hint of a DFS cache entry - * without sending any requests to the currently connected server. - * - * NOTE: This function will neither update a cache entry in case it was - * expired, nor create a new cache entry if @path hasn't been found. It heavily - * relies on an existing cache entry. - * - * @path: canonical DFS path to lookup in DFS referral cache. - * @it: target iterator which contains the target hint to update the cache - * entry with. - * - * Return zero if the target hint was updated successfully, otherwise non-zero. - */ -int dfs_cache_noreq_update_tgthint(const char *path, const struct dfs_cache_tgt_iterator *it) -{ - int rc; - struct cache_entry *ce; - struct cache_dfs_tgt *t; - - if (!it) - return -EINVAL; - - cifs_dbg(FYI, "%s: path: %s\n", __func__, path); - - down_write(&htable_rw_lock); - - ce = lookup_cache_entry(path); - if (IS_ERR(ce)) { - rc = PTR_ERR(ce); - goto out_unlock; - } - - rc = 0; - t = ce->tgthint; - - if (unlikely(!strcasecmp(it->it_name, t->name))) - goto out_unlock; - - list_for_each_entry(t, &ce->tlist, list) { - if (!strcasecmp(t->name, it->it_name)) { - ce->tgthint = t; - cifs_dbg(FYI, "%s: new target hint: %s\n", __func__, - it->it_name); - break; - } - } - -out_unlock: - up_write(&htable_rw_lock); - return rc; -} - -/** - * dfs_cache_get_tgt_referral - returns a DFS referral (@ref) from a given - * target iterator (@it). - * - * @path: canonical DFS path to lookup in DFS referral cache. - * @it: DFS target iterator. - * @ref: DFS referral pointer to set up the gathered information. - * - * Return zero if the DFS referral was set up correctly, otherwise non-zero. - */ -int dfs_cache_get_tgt_referral(const char *path, const struct dfs_cache_tgt_iterator *it, - struct dfs_info3_param *ref) -{ - int rc; - struct cache_entry *ce; - - if (!it || !ref) - return -EINVAL; - - cifs_dbg(FYI, "%s: path: %s\n", __func__, path); - - down_read(&htable_rw_lock); - - ce = lookup_cache_entry(path); - if (IS_ERR(ce)) { - rc = PTR_ERR(ce); - goto out_unlock; - } - - cifs_dbg(FYI, "%s: target name: %s\n", __func__, it->it_name); - - rc = setup_referral(path, ce, ref, it->it_name); - -out_unlock: - up_read(&htable_rw_lock); - return rc; -} - -/** - * dfs_cache_add_refsrv_session - add SMB session of referral server - * - * @mount_id: mount group uuid to lookup. - * @ses: reference counted SMB session of referral server. - */ -void dfs_cache_add_refsrv_session(const uuid_t *mount_id, struct cifs_ses *ses) -{ - struct mount_group *mg; - - if (WARN_ON_ONCE(!mount_id || uuid_is_null(mount_id) || !ses)) - return; - - mg = get_mount_group(mount_id); - if (WARN_ON_ONCE(IS_ERR(mg))) - return; - - spin_lock(&mg->lock); - if (mg->num_sessions < ARRAY_SIZE(mg->sessions)) - mg->sessions[mg->num_sessions++] = ses; - spin_unlock(&mg->lock); - kref_put(&mg->refcount, mount_group_release); -} - -/** - * dfs_cache_put_refsrv_sessions - put all referral server sessions - * - * Put all SMB sessions from the given mount group id. - * - * @mount_id: mount group uuid to lookup. - */ -void dfs_cache_put_refsrv_sessions(const uuid_t *mount_id) -{ - struct mount_group *mg; - - if (!mount_id || uuid_is_null(mount_id)) - return; - - mutex_lock(&mount_group_list_lock); - mg = find_mount_group_locked(mount_id); - if (IS_ERR(mg)) { - mutex_unlock(&mount_group_list_lock); - return; - } - mutex_unlock(&mount_group_list_lock); - kref_put(&mg->refcount, mount_group_release); -} - -/* Extract share from DFS target and return a pointer to prefix path or NULL */ -static const char *parse_target_share(const char *target, char **share) -{ - const char *s, *seps = "/\\"; - size_t len; - - s = strpbrk(target + 1, seps); - if (!s) - return ERR_PTR(-EINVAL); - - len = strcspn(s + 1, seps); - if (!len) - return ERR_PTR(-EINVAL); - s += len; - - len = s - target + 1; - *share = kstrndup(target, len, GFP_KERNEL); - if (!*share) - return ERR_PTR(-ENOMEM); - - s = target + len; - return s + strspn(s, seps); -} - -/** - * dfs_cache_get_tgt_share - parse a DFS target - * - * @path: DFS full path - * @it: DFS target iterator. - * @share: tree name. - * @prefix: prefix path. - * - * Return zero if target was parsed correctly, otherwise non-zero. - */ -int dfs_cache_get_tgt_share(char *path, const struct dfs_cache_tgt_iterator *it, char **share, - char **prefix) -{ - char sep; - char *target_share; - char *ppath = NULL; - const char *target_ppath, *dfsref_ppath; - size_t target_pplen, dfsref_pplen; - size_t len, c; - - if (!it || !path || !share || !prefix || strlen(path) < it->it_path_consumed) - return -EINVAL; - - sep = it->it_name[0]; - if (sep != '\\' && sep != '/') - return -EINVAL; - - target_ppath = parse_target_share(it->it_name, &target_share); - if (IS_ERR(target_ppath)) - return PTR_ERR(target_ppath); - - /* point to prefix in DFS referral path */ - dfsref_ppath = path + it->it_path_consumed; - dfsref_ppath += strspn(dfsref_ppath, "/\\"); - - target_pplen = strlen(target_ppath); - dfsref_pplen = strlen(dfsref_ppath); - - /* merge prefix paths from DFS referral path and target node */ - if (target_pplen || dfsref_pplen) { - len = target_pplen + dfsref_pplen + 2; - ppath = kzalloc(len, GFP_KERNEL); - if (!ppath) { - kfree(target_share); - return -ENOMEM; - } - c = strscpy(ppath, target_ppath, len); - if (c && dfsref_pplen) - ppath[c] = sep; - strlcat(ppath, dfsref_ppath, len); - } - *share = target_share; - *prefix = ppath; - return 0; -} - -static bool target_share_equal(struct TCP_Server_Info *server, const char *s1, const char *s2) -{ - char unc[sizeof("\\\\") + SERVER_NAME_LENGTH] = {0}; - const char *host; - size_t hostlen; - char *ip = NULL; - struct sockaddr sa; - bool match; - int rc; - - if (strcasecmp(s1, s2)) - return false; - - /* - * Resolve share's hostname and check if server address matches. Otherwise just ignore it - * as we could not have upcall to resolve hostname or failed to convert ip address. - */ - match = true; - extract_unc_hostname(s1, &host, &hostlen); - scnprintf(unc, sizeof(unc), "\\\\%.*s", (int)hostlen, host); - - rc = dns_resolve_server_name_to_ip(unc, &ip, NULL); - if (rc < 0) { - cifs_dbg(FYI, "%s: could not resolve %.*s. assuming server address matches.\n", - __func__, (int)hostlen, host); - return true; - } - - if (!cifs_convert_address(&sa, ip, strlen(ip))) { - cifs_dbg(VFS, "%s: failed to convert address \'%s\'. skip address matching.\n", - __func__, ip); - } else { - cifs_server_lock(server); - match = cifs_match_ipaddr((struct sockaddr *)&server->dstaddr, &sa); - cifs_server_unlock(server); - } - - kfree(ip); - return match; -} - -/* - * Mark dfs tcon for reconnecting when the currently connected tcon does not match any of the new - * target shares in @refs. - */ -static void mark_for_reconnect_if_needed(struct cifs_tcon *tcon, struct dfs_cache_tgt_list *tl, - const struct dfs_info3_param *refs, int numrefs) -{ - struct dfs_cache_tgt_iterator *it; - int i; - - for (it = dfs_cache_get_tgt_iterator(tl); it; it = dfs_cache_get_next_tgt(tl, it)) { - for (i = 0; i < numrefs; i++) { - if (target_share_equal(tcon->ses->server, dfs_cache_get_tgt_name(it), - refs[i].node_name)) - return; - } - } - - cifs_dbg(FYI, "%s: no cached or matched targets. mark dfs share for reconnect.\n", __func__); - cifs_signal_cifsd_for_reconnect(tcon->ses->server, true); -} - -/* Refresh dfs referral of tcon and mark it for reconnect if needed */ -static int __refresh_tcon(const char *path, struct cifs_ses **sessions, struct cifs_tcon *tcon, - bool force_refresh) -{ - struct cifs_ses *ses; - struct cache_entry *ce; - struct dfs_info3_param *refs = NULL; - int numrefs = 0; - bool needs_refresh = false; - struct dfs_cache_tgt_list tl = DFS_CACHE_TGT_LIST_INIT(tl); - int rc = 0; - unsigned int xid; - - ses = find_ipc_from_server_path(sessions, path); - if (IS_ERR(ses)) { - cifs_dbg(FYI, "%s: could not find ipc session\n", __func__); - return PTR_ERR(ses); - } - - down_read(&htable_rw_lock); - ce = lookup_cache_entry(path); - needs_refresh = force_refresh || IS_ERR(ce) || cache_entry_expired(ce); - if (!IS_ERR(ce)) { - rc = get_targets(ce, &tl); - if (rc) - cifs_dbg(FYI, "%s: could not get dfs targets: %d\n", __func__, rc); - } - up_read(&htable_rw_lock); - - if (!needs_refresh) { - rc = 0; - goto out; - } - - xid = get_xid(); - rc = get_dfs_referral(xid, ses, path, &refs, &numrefs); - free_xid(xid); - - /* Create or update a cache entry with the new referral */ - if (!rc) { - dump_refs(refs, numrefs); - - down_write(&htable_rw_lock); - ce = lookup_cache_entry(path); - if (IS_ERR(ce)) - add_cache_entry_locked(refs, numrefs); - else if (force_refresh || cache_entry_expired(ce)) - update_cache_entry_locked(ce, refs, numrefs); - up_write(&htable_rw_lock); - - mark_for_reconnect_if_needed(tcon, &tl, refs, numrefs); - } - -out: - dfs_cache_free_tgts(&tl); - free_dfs_info_array(refs, numrefs); - return rc; -} - -static int refresh_tcon(struct cifs_ses **sessions, struct cifs_tcon *tcon, bool force_refresh) -{ - struct TCP_Server_Info *server = tcon->ses->server; - - mutex_lock(&server->refpath_lock); - if (server->origin_fullpath) { - if (server->leaf_fullpath && strcasecmp(server->leaf_fullpath, - server->origin_fullpath)) - __refresh_tcon(server->leaf_fullpath + 1, sessions, tcon, force_refresh); - __refresh_tcon(server->origin_fullpath + 1, sessions, tcon, force_refresh); - } - mutex_unlock(&server->refpath_lock); - - return 0; -} - -/** - * dfs_cache_remount_fs - remount a DFS share - * - * Reconfigure dfs mount by forcing a new DFS referral and if the currently cached targets do not - * match any of the new targets, mark it for reconnect. - * - * @cifs_sb: cifs superblock. - * - * Return zero if remounted, otherwise non-zero. - */ -int dfs_cache_remount_fs(struct cifs_sb_info *cifs_sb) -{ - struct cifs_tcon *tcon; - struct TCP_Server_Info *server; - struct mount_group *mg; - struct cifs_ses *sessions[CACHE_MAX_ENTRIES + 1] = {NULL}; - int rc; - - if (!cifs_sb || !cifs_sb->master_tlink) - return -EINVAL; - - tcon = cifs_sb_master_tcon(cifs_sb); - server = tcon->ses->server; - - if (!server->origin_fullpath) { - cifs_dbg(FYI, "%s: not a dfs mount\n", __func__); - return 0; - } - - if (uuid_is_null(&cifs_sb->dfs_mount_id)) { - cifs_dbg(FYI, "%s: no dfs mount group id\n", __func__); - return -EINVAL; - } - - mutex_lock(&mount_group_list_lock); - mg = find_mount_group_locked(&cifs_sb->dfs_mount_id); - if (IS_ERR(mg)) { - mutex_unlock(&mount_group_list_lock); - cifs_dbg(FYI, "%s: no ipc session for refreshing referral\n", __func__); - return PTR_ERR(mg); - } - kref_get(&mg->refcount); - mutex_unlock(&mount_group_list_lock); - - spin_lock(&mg->lock); - memcpy(&sessions, mg->sessions, mg->num_sessions * sizeof(mg->sessions[0])); - spin_unlock(&mg->lock); - - /* - * After reconnecting to a different server, unique ids won't match anymore, so we disable - * serverino. This prevents dentry revalidation to think the dentry are stale (ESTALE). - */ - cifs_autodisable_serverino(cifs_sb); - /* - * Force the use of prefix path to support failover on DFS paths that resolve to targets - * that have different prefix paths. - */ - cifs_sb->mnt_cifs_flags |= CIFS_MOUNT_USE_PREFIX_PATH; - rc = refresh_tcon(sessions, tcon, true); - - kref_put(&mg->refcount, mount_group_release); - return rc; -} - -/* - * Refresh all active dfs mounts regardless of whether they are in cache or not. - * (cache can be cleared) - */ -static void refresh_mounts(struct cifs_ses **sessions) -{ - struct TCP_Server_Info *server; - struct cifs_ses *ses; - struct cifs_tcon *tcon, *ntcon; - struct list_head tcons; - - INIT_LIST_HEAD(&tcons); - - spin_lock(&cifs_tcp_ses_lock); - list_for_each_entry(server, &cifs_tcp_ses_list, tcp_ses_list) { - spin_lock(&server->srv_lock); - if (!server->is_dfs_conn) { - spin_unlock(&server->srv_lock); - continue; - } - spin_unlock(&server->srv_lock); - - list_for_each_entry(ses, &server->smb_ses_list, smb_ses_list) { - list_for_each_entry(tcon, &ses->tcon_list, tcon_list) { - spin_lock(&tcon->tc_lock); - if (!tcon->ipc && !tcon->need_reconnect) { - tcon->tc_count++; - list_add_tail(&tcon->ulist, &tcons); - } - spin_unlock(&tcon->tc_lock); - } - } - } - spin_unlock(&cifs_tcp_ses_lock); - - list_for_each_entry_safe(tcon, ntcon, &tcons, ulist) { - struct TCP_Server_Info *server = tcon->ses->server; - - list_del_init(&tcon->ulist); - - mutex_lock(&server->refpath_lock); - if (server->origin_fullpath) { - if (server->leaf_fullpath && strcasecmp(server->leaf_fullpath, - server->origin_fullpath)) - __refresh_tcon(server->leaf_fullpath + 1, sessions, tcon, false); - __refresh_tcon(server->origin_fullpath + 1, sessions, tcon, false); - } - mutex_unlock(&server->refpath_lock); - - cifs_put_tcon(tcon); - } -} - -static void refresh_cache(struct cifs_ses **sessions) -{ - int i; - struct cifs_ses *ses; - unsigned int xid; - char *ref_paths[CACHE_MAX_ENTRIES]; - int count = 0; - struct cache_entry *ce; - - /* - * Refresh all cached entries. Get all new referrals outside critical section to avoid - * starvation while performing SMB2 IOCTL on broken or slow connections. - - * The cache entries may cover more paths than the active mounts - * (e.g. domain-based DFS referrals or multi tier DFS setups). - */ - down_read(&htable_rw_lock); - for (i = 0; i < CACHE_HTABLE_SIZE; i++) { - struct hlist_head *l = &cache_htable[i]; - - hlist_for_each_entry(ce, l, hlist) { - if (count == ARRAY_SIZE(ref_paths)) - goto out_unlock; - if (hlist_unhashed(&ce->hlist) || !cache_entry_expired(ce) || - IS_ERR(find_ipc_from_server_path(sessions, ce->path))) - continue; - ref_paths[count++] = kstrdup(ce->path, GFP_ATOMIC); - } - } - -out_unlock: - up_read(&htable_rw_lock); - - for (i = 0; i < count; i++) { - char *path = ref_paths[i]; - struct dfs_info3_param *refs = NULL; - int numrefs = 0; - int rc = 0; - - if (!path) - continue; - - ses = find_ipc_from_server_path(sessions, path); - if (IS_ERR(ses)) - goto next_referral; - - xid = get_xid(); - rc = get_dfs_referral(xid, ses, path, &refs, &numrefs); - free_xid(xid); - - if (!rc) { - down_write(&htable_rw_lock); - ce = lookup_cache_entry(path); - /* - * We need to re-check it because other tasks might have it deleted or - * updated. - */ - if (!IS_ERR(ce) && cache_entry_expired(ce)) - update_cache_entry_locked(ce, refs, numrefs); - up_write(&htable_rw_lock); - } - -next_referral: - kfree(path); - free_dfs_info_array(refs, numrefs); - } -} - -/* - * Worker that will refresh DFS cache and active mounts based on lowest TTL value from a DFS - * referral. - */ -static void refresh_cache_worker(struct work_struct *work) -{ - struct list_head mglist; - struct mount_group *mg, *tmp_mg; - struct cifs_ses *sessions[CACHE_MAX_ENTRIES + 1] = {NULL}; - int max_sessions = ARRAY_SIZE(sessions) - 1; - int i = 0, count; - - INIT_LIST_HEAD(&mglist); - - /* Get refereces of mount groups */ - mutex_lock(&mount_group_list_lock); - list_for_each_entry(mg, &mount_group_list, list) { - kref_get(&mg->refcount); - list_add(&mg->refresh_list, &mglist); - } - mutex_unlock(&mount_group_list_lock); - - /* Fill in local array with an NULL-terminated list of all referral server sessions */ - list_for_each_entry(mg, &mglist, refresh_list) { - if (i >= max_sessions) - break; - - spin_lock(&mg->lock); - if (i + mg->num_sessions > max_sessions) - count = max_sessions - i; - else - count = mg->num_sessions; - memcpy(&sessions[i], mg->sessions, count * sizeof(mg->sessions[0])); - spin_unlock(&mg->lock); - i += count; - } - - if (sessions[0]) { - /* Refresh all active mounts and cached entries */ - refresh_mounts(sessions); - refresh_cache(sessions); - } - - list_for_each_entry_safe(mg, tmp_mg, &mglist, refresh_list) { - list_del_init(&mg->refresh_list); - kref_put(&mg->refcount, mount_group_release); - } - - spin_lock(&cache_ttl_lock); - queue_delayed_work(dfscache_wq, &refresh_task, cache_ttl * HZ); - spin_unlock(&cache_ttl_lock); -} diff --git a/fs/cifs/dfs_cache.h b/fs/cifs/dfs_cache.h deleted file mode 100644 index 52070d1df1897..0000000000000 --- a/fs/cifs/dfs_cache.h +++ /dev/null @@ -1,97 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0 */ -/* - * DFS referral cache routines - * - * Copyright (c) 2018-2019 Paulo Alcantara - */ - -#ifndef _CIFS_DFS_CACHE_H -#define _CIFS_DFS_CACHE_H - -#include -#include -#include -#include "cifsglob.h" - -#define DFS_CACHE_TGT_LIST_INIT(var) { .tl_numtgts = 0, .tl_list = LIST_HEAD_INIT((var).tl_list), } - -struct dfs_cache_tgt_list { - int tl_numtgts; - struct list_head tl_list; -}; - -struct dfs_cache_tgt_iterator { - char *it_name; - int it_path_consumed; - struct list_head it_list; -}; - -int dfs_cache_init(void); -void dfs_cache_destroy(void); -extern const struct proc_ops dfscache_proc_ops; - -int dfs_cache_find(const unsigned int xid, struct cifs_ses *ses, const struct nls_table *cp, - int remap, const char *path, struct dfs_info3_param *ref, - struct dfs_cache_tgt_list *tgt_list); -int dfs_cache_noreq_find(const char *path, struct dfs_info3_param *ref, - struct dfs_cache_tgt_list *tgt_list); -int dfs_cache_update_tgthint(const unsigned int xid, struct cifs_ses *ses, - const struct nls_table *cp, int remap, const char *path, - const struct dfs_cache_tgt_iterator *it); -int dfs_cache_noreq_update_tgthint(const char *path, const struct dfs_cache_tgt_iterator *it); -int dfs_cache_get_tgt_referral(const char *path, const struct dfs_cache_tgt_iterator *it, - struct dfs_info3_param *ref); -int dfs_cache_get_tgt_share(char *path, const struct dfs_cache_tgt_iterator *it, char **share, - char **prefix); -void dfs_cache_put_refsrv_sessions(const uuid_t *mount_id); -void dfs_cache_add_refsrv_session(const uuid_t *mount_id, struct cifs_ses *ses); -char *dfs_cache_canonical_path(const char *path, const struct nls_table *cp, int remap); -int dfs_cache_remount_fs(struct cifs_sb_info *cifs_sb); - -static inline struct dfs_cache_tgt_iterator * -dfs_cache_get_next_tgt(struct dfs_cache_tgt_list *tl, - struct dfs_cache_tgt_iterator *it) -{ - if (!tl || list_empty(&tl->tl_list) || !it || - list_is_last(&it->it_list, &tl->tl_list)) - return NULL; - return list_next_entry(it, it_list); -} - -static inline struct dfs_cache_tgt_iterator * -dfs_cache_get_tgt_iterator(struct dfs_cache_tgt_list *tl) -{ - if (!tl) - return NULL; - return list_first_entry_or_null(&tl->tl_list, - struct dfs_cache_tgt_iterator, - it_list); -} - -static inline void dfs_cache_free_tgts(struct dfs_cache_tgt_list *tl) -{ - struct dfs_cache_tgt_iterator *it, *nit; - - if (!tl || list_empty(&tl->tl_list)) - return; - list_for_each_entry_safe(it, nit, &tl->tl_list, it_list) { - list_del(&it->it_list); - kfree(it->it_name); - kfree(it); - } - tl->tl_numtgts = 0; -} - -static inline const char * -dfs_cache_get_tgt_name(const struct dfs_cache_tgt_iterator *it) -{ - return it ? it->it_name : NULL; -} - -static inline int -dfs_cache_get_nr_tgts(const struct dfs_cache_tgt_list *tl) -{ - return tl ? tl->tl_numtgts : 0; -} - -#endif /* _CIFS_DFS_CACHE_H */ diff --git a/fs/cifs/dir.c b/fs/cifs/dir.c deleted file mode 100644 index e382b794acbed..0000000000000 --- a/fs/cifs/dir.c +++ /dev/null @@ -1,867 +0,0 @@ -// SPDX-License-Identifier: LGPL-2.1 -/* - * - * vfs operations that deal with dentries - * - * Copyright (C) International Business Machines Corp., 2002,2009 - * Author(s): Steve French (sfrench@us.ibm.com) - * - */ -#include -#include -#include -#include -#include -#include -#include "cifsfs.h" -#include "cifspdu.h" -#include "cifsglob.h" -#include "cifsproto.h" -#include "cifs_debug.h" -#include "cifs_fs_sb.h" -#include "cifs_unicode.h" -#include "fs_context.h" -#include "cifs_ioctl.h" -#include "fscache.h" - -static void -renew_parental_timestamps(struct dentry *direntry) -{ - /* BB check if there is a way to get the kernel to do this or if we - really need this */ - do { - cifs_set_time(direntry, jiffies); - direntry = direntry->d_parent; - } while (!IS_ROOT(direntry)); -} - -char * -cifs_build_path_to_root(struct smb3_fs_context *ctx, struct cifs_sb_info *cifs_sb, - struct cifs_tcon *tcon, int add_treename) -{ - int pplen = ctx->prepath ? strlen(ctx->prepath) + 1 : 0; - int dfsplen; - char *full_path = NULL; - - /* if no prefix path, simply set path to the root of share to "" */ - if (pplen == 0) { - full_path = kzalloc(1, GFP_KERNEL); - return full_path; - } - - if (add_treename) - dfsplen = strnlen(tcon->tree_name, MAX_TREE_SIZE + 1); - else - dfsplen = 0; - - full_path = kmalloc(dfsplen + pplen + 1, GFP_KERNEL); - if (full_path == NULL) - return full_path; - - if (dfsplen) - memcpy(full_path, tcon->tree_name, dfsplen); - full_path[dfsplen] = CIFS_DIR_SEP(cifs_sb); - memcpy(full_path + dfsplen + 1, ctx->prepath, pplen); - convert_delimiter(full_path, CIFS_DIR_SEP(cifs_sb)); - return full_path; -} - -/* Note: caller must free return buffer */ -const char * -build_path_from_dentry(struct dentry *direntry, void *page) -{ - struct cifs_sb_info *cifs_sb = CIFS_SB(direntry->d_sb); - struct cifs_tcon *tcon = cifs_sb_master_tcon(cifs_sb); - bool prefix = tcon->Flags & SMB_SHARE_IS_IN_DFS; - - return build_path_from_dentry_optional_prefix(direntry, page, - prefix); -} - -char * -build_path_from_dentry_optional_prefix(struct dentry *direntry, void *page, - bool prefix) -{ - int dfsplen; - int pplen = 0; - struct cifs_sb_info *cifs_sb = CIFS_SB(direntry->d_sb); - struct cifs_tcon *tcon = cifs_sb_master_tcon(cifs_sb); - char dirsep = CIFS_DIR_SEP(cifs_sb); - char *s; - - if (unlikely(!page)) - return ERR_PTR(-ENOMEM); - - if (prefix) - dfsplen = strnlen(tcon->tree_name, MAX_TREE_SIZE + 1); - else - dfsplen = 0; - - if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_USE_PREFIX_PATH) - pplen = cifs_sb->prepath ? strlen(cifs_sb->prepath) + 1 : 0; - - s = dentry_path_raw(direntry, page, PATH_MAX); - if (IS_ERR(s)) - return s; - if (!s[1]) // for root we want "", not "/" - s++; - if (s < (char *)page + pplen + dfsplen) - return ERR_PTR(-ENAMETOOLONG); - if (pplen) { - cifs_dbg(FYI, "using cifs_sb prepath <%s>\n", cifs_sb->prepath); - s -= pplen; - memcpy(s + 1, cifs_sb->prepath, pplen - 1); - *s = '/'; - } - if (dirsep != '/') { - /* BB test paths to Windows with '/' in the midst of prepath */ - char *p; - - for (p = s; *p; p++) - if (*p == '/') - *p = dirsep; - } - if (dfsplen) { - s -= dfsplen; - memcpy(s, tcon->tree_name, dfsplen); - if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_POSIX_PATHS) { - int i; - for (i = 0; i < dfsplen; i++) { - if (s[i] == '\\') - s[i] = '/'; - } - } - } - return s; -} - -/* - * Don't allow path components longer than the server max. - * Don't allow the separator character in a path component. - * The VFS will not allow "/", but "\" is allowed by posix. - */ -static int -check_name(struct dentry *direntry, struct cifs_tcon *tcon) -{ - struct cifs_sb_info *cifs_sb = CIFS_SB(direntry->d_sb); - int i; - - if (unlikely(tcon->fsAttrInfo.MaxPathNameComponentLength && - direntry->d_name.len > - le32_to_cpu(tcon->fsAttrInfo.MaxPathNameComponentLength))) - return -ENAMETOOLONG; - - if (!(cifs_sb->mnt_cifs_flags & CIFS_MOUNT_POSIX_PATHS)) { - for (i = 0; i < direntry->d_name.len; i++) { - if (direntry->d_name.name[i] == '\\') { - cifs_dbg(FYI, "Invalid file name\n"); - return -EINVAL; - } - } - } - return 0; -} - - -/* Inode operations in similar order to how they appear in Linux file fs.h */ - -static int cifs_do_create(struct inode *inode, struct dentry *direntry, unsigned int xid, - struct tcon_link *tlink, unsigned int oflags, umode_t mode, __u32 *oplock, - struct cifs_fid *fid, struct cifs_open_info_data *buf) -{ - int rc = -ENOENT; - int create_options = CREATE_NOT_DIR; - int desired_access; - struct cifs_sb_info *cifs_sb = CIFS_SB(inode->i_sb); - struct cifs_tcon *tcon = tlink_tcon(tlink); - const char *full_path; - void *page = alloc_dentry_path(); - struct inode *newinode = NULL; - int disposition; - struct TCP_Server_Info *server = tcon->ses->server; - struct cifs_open_parms oparms; - - *oplock = 0; - if (tcon->ses->server->oplocks) - *oplock = REQ_OPLOCK; - - full_path = build_path_from_dentry(direntry, page); - if (IS_ERR(full_path)) { - free_dentry_path(page); - return PTR_ERR(full_path); - } - -#ifdef CONFIG_CIFS_ALLOW_INSECURE_LEGACY - if (tcon->unix_ext && cap_unix(tcon->ses) && !tcon->broken_posix_open && - (CIFS_UNIX_POSIX_PATH_OPS_CAP & - le64_to_cpu(tcon->fsUnixInfo.Capability))) { - rc = cifs_posix_open(full_path, &newinode, inode->i_sb, mode, - oflags, oplock, &fid->netfid, xid); - switch (rc) { - case 0: - if (newinode == NULL) { - /* query inode info */ - goto cifs_create_get_file_info; - } - - if (S_ISDIR(newinode->i_mode)) { - CIFSSMBClose(xid, tcon, fid->netfid); - iput(newinode); - rc = -EISDIR; - goto out; - } - - if (!S_ISREG(newinode->i_mode)) { - /* - * The server may allow us to open things like - * FIFOs, but the client isn't set up to deal - * with that. If it's not a regular file, just - * close it and proceed as if it were a normal - * lookup. - */ - CIFSSMBClose(xid, tcon, fid->netfid); - goto cifs_create_get_file_info; - } - /* success, no need to query */ - goto cifs_create_set_dentry; - - case -ENOENT: - goto cifs_create_get_file_info; - - case -EIO: - case -EINVAL: - /* - * EIO could indicate that (posix open) operation is not - * supported, despite what server claimed in capability - * negotiation. - * - * POSIX open in samba versions 3.3.1 and earlier could - * incorrectly fail with invalid parameter. - */ - tcon->broken_posix_open = true; - break; - - case -EREMOTE: - case -EOPNOTSUPP: - /* - * EREMOTE indicates DFS junction, which is not handled - * in posix open. If either that or op not supported - * returned, follow the normal lookup. - */ - break; - - default: - goto out; - } - /* - * fallthrough to retry, using older open call, this is case - * where server does not support this SMB level, and falsely - * claims capability (also get here for DFS case which should be - * rare for path not covered on files) - */ - } -#endif /* CONFIG_CIFS_ALLOW_INSECURE_LEGACY */ - - desired_access = 0; - if (OPEN_FMODE(oflags) & FMODE_READ) - desired_access |= GENERIC_READ; /* is this too little? */ - if (OPEN_FMODE(oflags) & FMODE_WRITE) - desired_access |= GENERIC_WRITE; - - disposition = FILE_OVERWRITE_IF; - if ((oflags & (O_CREAT | O_EXCL)) == (O_CREAT | O_EXCL)) - disposition = FILE_CREATE; - else if ((oflags & (O_CREAT | O_TRUNC)) == (O_CREAT | O_TRUNC)) - disposition = FILE_OVERWRITE_IF; - else if ((oflags & O_CREAT) == O_CREAT) - disposition = FILE_OPEN_IF; - else - cifs_dbg(FYI, "Create flag not set in create function\n"); - - /* - * BB add processing to set equivalent of mode - e.g. via CreateX with - * ACLs - */ - - if (!server->ops->open) { - rc = -ENOSYS; - goto out; - } - - /* - * if we're not using unix extensions, see if we need to set - * ATTR_READONLY on the create call - */ - if (!tcon->unix_ext && (mode & S_IWUGO) == 0) - create_options |= CREATE_OPTION_READONLY; - - oparms = (struct cifs_open_parms) { - .tcon = tcon, - .cifs_sb = cifs_sb, - .desired_access = desired_access, - .create_options = cifs_create_options(cifs_sb, create_options), - .disposition = disposition, - .path = full_path, - .fid = fid, - .mode = mode, - }; - rc = server->ops->open(xid, &oparms, oplock, buf); - if (rc) { - cifs_dbg(FYI, "cifs_create returned 0x%x\n", rc); - goto out; - } - -#ifdef CONFIG_CIFS_ALLOW_INSECURE_LEGACY - /* - * If Open reported that we actually created a file then we now have to - * set the mode if possible. - */ - if ((tcon->unix_ext) && (*oplock & CIFS_CREATE_ACTION)) { - struct cifs_unix_set_info_args args = { - .mode = mode, - .ctime = NO_CHANGE_64, - .atime = NO_CHANGE_64, - .mtime = NO_CHANGE_64, - .device = 0, - }; - - if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_SET_UID) { - args.uid = current_fsuid(); - if (inode->i_mode & S_ISGID) - args.gid = inode->i_gid; - else - args.gid = current_fsgid(); - } else { - args.uid = INVALID_UID; /* no change */ - args.gid = INVALID_GID; /* no change */ - } - CIFSSMBUnixSetFileInfo(xid, tcon, &args, fid->netfid, - current->tgid); - } else { - /* - * BB implement mode setting via Windows security - * descriptors e.g. - */ - /* CIFSSMBWinSetPerms(xid,tcon,path,mode,-1,-1,nls);*/ - - /* Could set r/o dos attribute if mode & 0222 == 0 */ - } - -cifs_create_get_file_info: - /* server might mask mode so we have to query for it */ - if (tcon->unix_ext) - rc = cifs_get_inode_info_unix(&newinode, full_path, inode->i_sb, - xid); - else { -#else - { -#endif /* CONFIG_CIFS_ALLOW_INSECURE_LEGACY */ - /* TODO: Add support for calling POSIX query info here, but passing in fid */ - rc = cifs_get_inode_info(&newinode, full_path, buf, inode->i_sb, xid, fid); - if (newinode) { - if (server->ops->set_lease_key) - server->ops->set_lease_key(newinode, fid); - if ((*oplock & CIFS_CREATE_ACTION) && S_ISREG(newinode->i_mode)) { - if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_DYNPERM) - newinode->i_mode = mode; - if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_SET_UID) { - newinode->i_uid = current_fsuid(); - if (inode->i_mode & S_ISGID) - newinode->i_gid = inode->i_gid; - else - newinode->i_gid = current_fsgid(); - } - } - } - } - -#ifdef CONFIG_CIFS_ALLOW_INSECURE_LEGACY -cifs_create_set_dentry: -#endif /* CONFIG_CIFS_ALLOW_INSECURE_LEGACY */ - if (rc != 0) { - cifs_dbg(FYI, "Create worked, get_inode_info failed rc = %d\n", - rc); - goto out_err; - } - - if (newinode) - if (S_ISDIR(newinode->i_mode)) { - rc = -EISDIR; - goto out_err; - } - - d_drop(direntry); - d_add(direntry, newinode); - -out: - free_dentry_path(page); - return rc; - -out_err: - if (server->ops->close) - server->ops->close(xid, tcon, fid); - if (newinode) - iput(newinode); - goto out; -} - -int -cifs_atomic_open(struct inode *inode, struct dentry *direntry, - struct file *file, unsigned oflags, umode_t mode) -{ - int rc; - unsigned int xid; - struct tcon_link *tlink; - struct cifs_tcon *tcon; - struct TCP_Server_Info *server; - struct cifs_fid fid = {}; - struct cifs_pending_open open; - __u32 oplock; - struct cifsFileInfo *file_info; - struct cifs_open_info_data buf = {}; - - if (unlikely(cifs_forced_shutdown(CIFS_SB(inode->i_sb)))) - return -EIO; - - /* - * Posix open is only called (at lookup time) for file create now. For - * opens (rather than creates), because we do not know if it is a file - * or directory yet, and current Samba no longer allows us to do posix - * open on dirs, we could end up wasting an open call on what turns out - * to be a dir. For file opens, we wait to call posix open till - * cifs_open. It could be added to atomic_open in the future but the - * performance tradeoff of the extra network request when EISDIR or - * EACCES is returned would have to be weighed against the 50% reduction - * in network traffic in the other paths. - */ - if (!(oflags & O_CREAT)) { - struct dentry *res; - - /* - * Check for hashed negative dentry. We have already revalidated - * the dentry and it is fine. No need to perform another lookup. - */ - if (!d_in_lookup(direntry)) - return -ENOENT; - - res = cifs_lookup(inode, direntry, 0); - if (IS_ERR(res)) - return PTR_ERR(res); - - return finish_no_open(file, res); - } - - xid = get_xid(); - - cifs_dbg(FYI, "parent inode = 0x%p name is: %pd and dentry = 0x%p\n", - inode, direntry, direntry); - - tlink = cifs_sb_tlink(CIFS_SB(inode->i_sb)); - if (IS_ERR(tlink)) { - rc = PTR_ERR(tlink); - goto out_free_xid; - } - - tcon = tlink_tcon(tlink); - - rc = check_name(direntry, tcon); - if (rc) - goto out; - - server = tcon->ses->server; - - if (server->ops->new_lease_key) - server->ops->new_lease_key(&fid); - - cifs_add_pending_open(&fid, tlink, &open); - - rc = cifs_do_create(inode, direntry, xid, tlink, oflags, mode, - &oplock, &fid, &buf); - if (rc) { - cifs_del_pending_open(&open); - goto out; - } - - if ((oflags & (O_CREAT | O_EXCL)) == (O_CREAT | O_EXCL)) - file->f_mode |= FMODE_CREATED; - - rc = finish_open(file, direntry, generic_file_open); - if (rc) { - if (server->ops->close) - server->ops->close(xid, tcon, &fid); - cifs_del_pending_open(&open); - goto out; - } - - if (file->f_flags & O_DIRECT && - CIFS_SB(inode->i_sb)->mnt_cifs_flags & CIFS_MOUNT_STRICT_IO) { - if (CIFS_SB(inode->i_sb)->mnt_cifs_flags & CIFS_MOUNT_NO_BRL) - file->f_op = &cifs_file_direct_nobrl_ops; - else - file->f_op = &cifs_file_direct_ops; - } - - file_info = cifs_new_fileinfo(&fid, file, tlink, oplock, buf.symlink_target); - if (file_info == NULL) { - if (server->ops->close) - server->ops->close(xid, tcon, &fid); - cifs_del_pending_open(&open); - rc = -ENOMEM; - goto out; - } - - fscache_use_cookie(cifs_inode_cookie(file_inode(file)), - file->f_mode & FMODE_WRITE); - -out: - cifs_put_tlink(tlink); -out_free_xid: - free_xid(xid); - cifs_free_open_info(&buf); - return rc; -} - -int cifs_create(struct user_namespace *mnt_userns, struct inode *inode, - struct dentry *direntry, umode_t mode, bool excl) -{ - int rc; - unsigned int xid = get_xid(); - /* - * BB below access is probably too much for mknod to request - * but we have to do query and setpathinfo so requesting - * less could fail (unless we want to request getatr and setatr - * permissions (only). At least for POSIX we do not have to - * request so much. - */ - unsigned oflags = O_EXCL | O_CREAT | O_RDWR; - struct tcon_link *tlink; - struct cifs_tcon *tcon; - struct TCP_Server_Info *server; - struct cifs_fid fid; - __u32 oplock; - struct cifs_open_info_data buf = {}; - - cifs_dbg(FYI, "cifs_create parent inode = 0x%p name is: %pd and dentry = 0x%p\n", - inode, direntry, direntry); - - if (unlikely(cifs_forced_shutdown(CIFS_SB(inode->i_sb)))) { - rc = -EIO; - goto out_free_xid; - } - - tlink = cifs_sb_tlink(CIFS_SB(inode->i_sb)); - rc = PTR_ERR(tlink); - if (IS_ERR(tlink)) - goto out_free_xid; - - tcon = tlink_tcon(tlink); - server = tcon->ses->server; - - if (server->ops->new_lease_key) - server->ops->new_lease_key(&fid); - - rc = cifs_do_create(inode, direntry, xid, tlink, oflags, mode, &oplock, &fid, &buf); - if (!rc && server->ops->close) - server->ops->close(xid, tcon, &fid); - - cifs_free_open_info(&buf); - cifs_put_tlink(tlink); -out_free_xid: - free_xid(xid); - return rc; -} - -int cifs_mknod(struct user_namespace *mnt_userns, struct inode *inode, - struct dentry *direntry, umode_t mode, dev_t device_number) -{ - int rc = -EPERM; - unsigned int xid; - struct cifs_sb_info *cifs_sb; - struct tcon_link *tlink; - struct cifs_tcon *tcon; - const char *full_path; - void *page; - - if (!old_valid_dev(device_number)) - return -EINVAL; - - cifs_sb = CIFS_SB(inode->i_sb); - if (unlikely(cifs_forced_shutdown(cifs_sb))) - return -EIO; - - tlink = cifs_sb_tlink(cifs_sb); - if (IS_ERR(tlink)) - return PTR_ERR(tlink); - - page = alloc_dentry_path(); - tcon = tlink_tcon(tlink); - xid = get_xid(); - - full_path = build_path_from_dentry(direntry, page); - if (IS_ERR(full_path)) { - rc = PTR_ERR(full_path); - goto mknod_out; - } - - rc = tcon->ses->server->ops->make_node(xid, inode, direntry, tcon, - full_path, mode, - device_number); - -mknod_out: - free_dentry_path(page); - free_xid(xid); - cifs_put_tlink(tlink); - return rc; -} - -struct dentry * -cifs_lookup(struct inode *parent_dir_inode, struct dentry *direntry, - unsigned int flags) -{ - unsigned int xid; - int rc = 0; /* to get around spurious gcc warning, set to zero here */ - struct cifs_sb_info *cifs_sb; - struct tcon_link *tlink; - struct cifs_tcon *pTcon; - struct inode *newInode = NULL; - const char *full_path; - void *page; - int retry_count = 0; - - xid = get_xid(); - - cifs_dbg(FYI, "parent inode = 0x%p name is: %pd and dentry = 0x%p\n", - parent_dir_inode, direntry, direntry); - - /* check whether path exists */ - - cifs_sb = CIFS_SB(parent_dir_inode->i_sb); - tlink = cifs_sb_tlink(cifs_sb); - if (IS_ERR(tlink)) { - free_xid(xid); - return ERR_CAST(tlink); - } - pTcon = tlink_tcon(tlink); - - rc = check_name(direntry, pTcon); - if (unlikely(rc)) { - cifs_put_tlink(tlink); - free_xid(xid); - return ERR_PTR(rc); - } - - /* can not grab the rename sem here since it would - deadlock in the cases (beginning of sys_rename itself) - in which we already have the sb rename sem */ - page = alloc_dentry_path(); - full_path = build_path_from_dentry(direntry, page); - if (IS_ERR(full_path)) { - cifs_put_tlink(tlink); - free_xid(xid); - free_dentry_path(page); - return ERR_CAST(full_path); - } - - if (d_really_is_positive(direntry)) { - cifs_dbg(FYI, "non-NULL inode in lookup\n"); - } else { - cifs_dbg(FYI, "NULL inode in lookup\n"); - } - cifs_dbg(FYI, "Full path: %s inode = 0x%p\n", - full_path, d_inode(direntry)); - -again: - if (pTcon->posix_extensions) - rc = smb311_posix_get_inode_info(&newInode, full_path, parent_dir_inode->i_sb, xid); - else if (pTcon->unix_ext) { - rc = cifs_get_inode_info_unix(&newInode, full_path, - parent_dir_inode->i_sb, xid); - } else { - rc = cifs_get_inode_info(&newInode, full_path, NULL, - parent_dir_inode->i_sb, xid, NULL); - } - - if (rc == 0) { - /* since paths are not looked up by component - the parent - directories are presumed to be good here */ - renew_parental_timestamps(direntry); - } else if (rc == -EAGAIN && retry_count++ < 10) { - goto again; - } else if (rc == -ENOENT) { - cifs_set_time(direntry, jiffies); - newInode = NULL; - } else { - if (rc != -EACCES) { - cifs_dbg(FYI, "Unexpected lookup error %d\n", rc); - /* We special case check for Access Denied - since that - is a common return code */ - } - newInode = ERR_PTR(rc); - } - free_dentry_path(page); - cifs_put_tlink(tlink); - free_xid(xid); - return d_splice_alias(newInode, direntry); -} - -static int -cifs_d_revalidate(struct dentry *direntry, unsigned int flags) -{ - struct inode *inode; - int rc; - - if (flags & LOOKUP_RCU) - return -ECHILD; - - if (d_really_is_positive(direntry)) { - inode = d_inode(direntry); - if ((flags & LOOKUP_REVAL) && !CIFS_CACHE_READ(CIFS_I(inode))) - CIFS_I(inode)->time = 0; /* force reval */ - - rc = cifs_revalidate_dentry(direntry); - if (rc) { - cifs_dbg(FYI, "cifs_revalidate_dentry failed with rc=%d", rc); - switch (rc) { - case -ENOENT: - case -ESTALE: - /* - * Those errors mean the dentry is invalid - * (file was deleted or recreated) - */ - return 0; - default: - /* - * Otherwise some unexpected error happened - * report it as-is to VFS layer - */ - return rc; - } - } - else { - /* - * If the inode wasn't known to be a dfs entry when - * the dentry was instantiated, such as when created - * via ->readdir(), it needs to be set now since the - * attributes will have been updated by - * cifs_revalidate_dentry(). - */ - if (IS_AUTOMOUNT(inode) && - !(direntry->d_flags & DCACHE_NEED_AUTOMOUNT)) { - spin_lock(&direntry->d_lock); - direntry->d_flags |= DCACHE_NEED_AUTOMOUNT; - spin_unlock(&direntry->d_lock); - } - - return 1; - } - } - - /* - * This may be nfsd (or something), anyway, we can't see the - * intent of this. So, since this can be for creation, drop it. - */ - if (!flags) - return 0; - - /* - * Drop the negative dentry, in order to make sure to use the - * case sensitive name which is specified by user if this is - * for creation. - */ - if (flags & (LOOKUP_CREATE | LOOKUP_RENAME_TARGET)) - return 0; - - if (time_after(jiffies, cifs_get_time(direntry) + HZ) || !lookupCacheEnabled) - return 0; - - return 1; -} - -/* static int cifs_d_delete(struct dentry *direntry) -{ - int rc = 0; - - cifs_dbg(FYI, "In cifs d_delete, name = %pd\n", direntry); - - return rc; -} */ - -const struct dentry_operations cifs_dentry_ops = { - .d_revalidate = cifs_d_revalidate, - .d_automount = cifs_dfs_d_automount, -/* d_delete: cifs_d_delete, */ /* not needed except for debugging */ -}; - -static int cifs_ci_hash(const struct dentry *dentry, struct qstr *q) -{ - struct nls_table *codepage = CIFS_SB(dentry->d_sb)->local_nls; - unsigned long hash; - wchar_t c; - int i, charlen; - - hash = init_name_hash(dentry); - for (i = 0; i < q->len; i += charlen) { - charlen = codepage->char2uni(&q->name[i], q->len - i, &c); - /* error out if we can't convert the character */ - if (unlikely(charlen < 0)) - return charlen; - hash = partial_name_hash(cifs_toupper(c), hash); - } - q->hash = end_name_hash(hash); - - return 0; -} - -static int cifs_ci_compare(const struct dentry *dentry, - unsigned int len, const char *str, const struct qstr *name) -{ - struct nls_table *codepage = CIFS_SB(dentry->d_sb)->local_nls; - wchar_t c1, c2; - int i, l1, l2; - - /* - * We make the assumption here that uppercase characters in the local - * codepage are always the same length as their lowercase counterparts. - * - * If that's ever not the case, then this will fail to match it. - */ - if (name->len != len) - return 1; - - for (i = 0; i < len; i += l1) { - /* Convert characters in both strings to UTF-16. */ - l1 = codepage->char2uni(&str[i], len - i, &c1); - l2 = codepage->char2uni(&name->name[i], name->len - i, &c2); - - /* - * If we can't convert either character, just declare it to - * be 1 byte long and compare the original byte. - */ - if (unlikely(l1 < 0 && l2 < 0)) { - if (str[i] != name->name[i]) - return 1; - l1 = 1; - continue; - } - - /* - * Here, we again ass|u|me that upper/lowercase versions of - * a character are the same length in the local NLS. - */ - if (l1 != l2) - return 1; - - /* Now compare uppercase versions of these characters */ - if (cifs_toupper(c1) != cifs_toupper(c2)) - return 1; - } - - return 0; -} - -const struct dentry_operations cifs_ci_dentry_ops = { - .d_revalidate = cifs_d_revalidate, - .d_hash = cifs_ci_hash, - .d_compare = cifs_ci_compare, - .d_automount = cifs_dfs_d_automount, -}; diff --git a/fs/cifs/dns_resolve.c b/fs/cifs/dns_resolve.c deleted file mode 100644 index 0458d28d71aa6..0000000000000 --- a/fs/cifs/dns_resolve.c +++ /dev/null @@ -1,89 +0,0 @@ -// SPDX-License-Identifier: LGPL-2.1 -/* - * - * Copyright (c) 2007 Igor Mammedov - * Author(s): Igor Mammedov (niallain@gmail.com) - * Steve French (sfrench@us.ibm.com) - * Wang Lei (wang840925@gmail.com) - * David Howells (dhowells@redhat.com) - * - * Contains the CIFS DFS upcall routines used for hostname to - * IP address translation. - * - */ - -#include -#include -#include "dns_resolve.h" -#include "cifsglob.h" -#include "cifsproto.h" -#include "cifs_debug.h" - -/** - * dns_resolve_server_name_to_ip - Resolve UNC server name to ip address. - * @unc: UNC path specifying the server (with '/' as delimiter) - * @ip_addr: Where to return the IP address. - * @expiry: Where to return the expiry time for the dns record. - * - * The IP address will be returned in string form, and the caller is - * responsible for freeing it. - * - * Returns length of result on success, -ve on error. - */ -int -dns_resolve_server_name_to_ip(const char *unc, char **ip_addr, time64_t *expiry) -{ - struct sockaddr_storage ss; - const char *hostname, *sep; - char *name; - int len, rc; - - if (!ip_addr || !unc) - return -EINVAL; - - len = strlen(unc); - if (len < 3) { - cifs_dbg(FYI, "%s: unc is too short: %s\n", __func__, unc); - return -EINVAL; - } - - /* Discount leading slashes for cifs */ - len -= 2; - hostname = unc + 2; - - /* Search for server name delimiter */ - sep = memchr(hostname, '/', len); - if (sep) - len = sep - hostname; - else - cifs_dbg(FYI, "%s: probably server name is whole unc: %s\n", - __func__, unc); - - /* Try to interpret hostname as an IPv4 or IPv6 address */ - rc = cifs_convert_address((struct sockaddr *)&ss, hostname, len); - if (rc > 0) - goto name_is_IP_address; - - /* Perform the upcall */ - rc = dns_query(current->nsproxy->net_ns, NULL, hostname, len, - NULL, ip_addr, expiry, false); - if (rc < 0) - cifs_dbg(FYI, "%s: unable to resolve: %*.*s\n", - __func__, len, len, hostname); - else - cifs_dbg(FYI, "%s: resolved: %*.*s to %s expiry %llu\n", - __func__, len, len, hostname, *ip_addr, - expiry ? (*expiry) : 0); - return rc; - -name_is_IP_address: - name = kmalloc(len + 1, GFP_KERNEL); - if (!name) - return -ENOMEM; - memcpy(name, hostname, len); - name[len] = 0; - cifs_dbg(FYI, "%s: unc is IP, skipping dns upcall: %s\n", - __func__, name); - *ip_addr = name; - return 0; -} diff --git a/fs/cifs/dns_resolve.h b/fs/cifs/dns_resolve.h deleted file mode 100644 index afc0df381246b..0000000000000 --- a/fs/cifs/dns_resolve.h +++ /dev/null @@ -1,18 +0,0 @@ -/* SPDX-License-Identifier: LGPL-2.1 */ -/* - * DNS Resolver upcall management for CIFS DFS - * Handles host name to IP address resolution - * - * Copyright (c) International Business Machines Corp., 2008 - * Author(s): Steve French (sfrench@us.ibm.com) - * - */ - -#ifndef _DNS_RESOLVE_H -#define _DNS_RESOLVE_H - -#ifdef __KERNEL__ -extern int dns_resolve_server_name_to_ip(const char *unc, char **ip_addr, time64_t *expiry); -#endif /* KERNEL */ - -#endif /* _DNS_RESOLVE_H */ diff --git a/fs/cifs/export.c b/fs/cifs/export.c deleted file mode 100644 index 37c28415df1e0..0000000000000 --- a/fs/cifs/export.c +++ /dev/null @@ -1,54 +0,0 @@ -// SPDX-License-Identifier: LGPL-2.1 -/* - * - * Copyright (C) International Business Machines Corp., 2007 - * Author(s): Steve French (sfrench@us.ibm.com) - * - * Common Internet FileSystem (CIFS) client - * - * Operations related to support for exporting files via NFSD - * - */ - - /* - * See Documentation/filesystems/nfs/exporting.rst - * and examples in fs/exportfs - * - * Since cifs is a network file system, an "fsid" must be included for - * any nfs exports file entries which refer to cifs paths. In addition - * the cifs mount must be mounted with the "serverino" option (ie use stable - * server inode numbers instead of locally generated temporary ones). - * Although cifs inodes do not use generation numbers (have generation number - * of zero) - the inode number alone should be good enough for simple cases - * in which users want to export cifs shares with NFS. The decode and encode - * could be improved by using a new routine which expects 64 bit inode numbers - * instead of the default 32 bit routines in fs/exportfs - * - */ - -#include -#include -#include "cifsglob.h" -#include "cifs_debug.h" -#include "cifsfs.h" - -#ifdef CONFIG_CIFS_NFSD_EXPORT -static struct dentry *cifs_get_parent(struct dentry *dentry) -{ - /* BB need to add code here eventually to enable export via NFSD */ - cifs_dbg(FYI, "get parent for %p\n", dentry); - return ERR_PTR(-EACCES); -} - -const struct export_operations cifs_export_ops = { - .get_parent = cifs_get_parent, -/* Following five export operations are unneeded so far and can default: - .get_dentry = - .get_name = - .find_exported_dentry = - .decode_fh = - .encode_fs = */ -}; - -#endif /* CONFIG_CIFS_NFSD_EXPORT */ - diff --git a/fs/cifs/file.c b/fs/cifs/file.c deleted file mode 100644 index 87dcffece7623..0000000000000 --- a/fs/cifs/file.c +++ /dev/null @@ -1,5290 +0,0 @@ -// SPDX-License-Identifier: LGPL-2.1 -/* - * - * vfs operations that deal with files - * - * Copyright (C) International Business Machines Corp., 2002,2010 - * Author(s): Steve French (sfrench@us.ibm.com) - * Jeremy Allison (jra@samba.org) - * - */ -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include "cifsfs.h" -#include "cifspdu.h" -#include "cifsglob.h" -#include "cifsproto.h" -#include "smb2proto.h" -#include "cifs_unicode.h" -#include "cifs_debug.h" -#include "cifs_fs_sb.h" -#include "fscache.h" -#include "smbdirect.h" -#include "fs_context.h" -#include "cifs_ioctl.h" -#include "cached_dir.h" - -/* - * Mark as invalid, all open files on tree connections since they - * were closed when session to server was lost. - */ -void -cifs_mark_open_files_invalid(struct cifs_tcon *tcon) -{ - struct cifsFileInfo *open_file = NULL; - struct list_head *tmp; - struct list_head *tmp1; - - /* only send once per connect */ - spin_lock(&tcon->ses->ses_lock); - if ((tcon->ses->ses_status != SES_GOOD) || (tcon->status != TID_NEED_RECON)) { - spin_unlock(&tcon->ses->ses_lock); - return; - } - tcon->status = TID_IN_FILES_INVALIDATE; - spin_unlock(&tcon->ses->ses_lock); - - /* list all files open on tree connection and mark them invalid */ - spin_lock(&tcon->open_file_lock); - list_for_each_safe(tmp, tmp1, &tcon->openFileList) { - open_file = list_entry(tmp, struct cifsFileInfo, tlist); - open_file->invalidHandle = true; - open_file->oplock_break_cancelled = true; - } - spin_unlock(&tcon->open_file_lock); - - invalidate_all_cached_dirs(tcon); - spin_lock(&tcon->tc_lock); - if (tcon->status == TID_IN_FILES_INVALIDATE) - tcon->status = TID_NEED_TCON; - spin_unlock(&tcon->tc_lock); - - /* - * BB Add call to invalidate_inodes(sb) for all superblocks mounted - * to this tcon. - */ -} - -static inline int cifs_convert_flags(unsigned int flags) -{ - if ((flags & O_ACCMODE) == O_RDONLY) - return GENERIC_READ; - else if ((flags & O_ACCMODE) == O_WRONLY) - return GENERIC_WRITE; - else if ((flags & O_ACCMODE) == O_RDWR) { - /* GENERIC_ALL is too much permission to request - can cause unnecessary access denied on create */ - /* return GENERIC_ALL; */ - return (GENERIC_READ | GENERIC_WRITE); - } - - return (READ_CONTROL | FILE_WRITE_ATTRIBUTES | FILE_READ_ATTRIBUTES | - FILE_WRITE_EA | FILE_APPEND_DATA | FILE_WRITE_DATA | - FILE_READ_DATA); -} - -#ifdef CONFIG_CIFS_ALLOW_INSECURE_LEGACY -static u32 cifs_posix_convert_flags(unsigned int flags) -{ - u32 posix_flags = 0; - - if ((flags & O_ACCMODE) == O_RDONLY) - posix_flags = SMB_O_RDONLY; - else if ((flags & O_ACCMODE) == O_WRONLY) - posix_flags = SMB_O_WRONLY; - else if ((flags & O_ACCMODE) == O_RDWR) - posix_flags = SMB_O_RDWR; - - if (flags & O_CREAT) { - posix_flags |= SMB_O_CREAT; - if (flags & O_EXCL) - posix_flags |= SMB_O_EXCL; - } else if (flags & O_EXCL) - cifs_dbg(FYI, "Application %s pid %d has incorrectly set O_EXCL flag but not O_CREAT on file open. Ignoring O_EXCL\n", - current->comm, current->tgid); - - if (flags & O_TRUNC) - posix_flags |= SMB_O_TRUNC; - /* be safe and imply O_SYNC for O_DSYNC */ - if (flags & O_DSYNC) - posix_flags |= SMB_O_SYNC; - if (flags & O_DIRECTORY) - posix_flags |= SMB_O_DIRECTORY; - if (flags & O_NOFOLLOW) - posix_flags |= SMB_O_NOFOLLOW; - if (flags & O_DIRECT) - posix_flags |= SMB_O_DIRECT; - - return posix_flags; -} -#endif /* CONFIG_CIFS_ALLOW_INSECURE_LEGACY */ - -static inline int cifs_get_disposition(unsigned int flags) -{ - if ((flags & (O_CREAT | O_EXCL)) == (O_CREAT | O_EXCL)) - return FILE_CREATE; - else if ((flags & (O_CREAT | O_TRUNC)) == (O_CREAT | O_TRUNC)) - return FILE_OVERWRITE_IF; - else if ((flags & O_CREAT) == O_CREAT) - return FILE_OPEN_IF; - else if ((flags & O_TRUNC) == O_TRUNC) - return FILE_OVERWRITE; - else - return FILE_OPEN; -} - -#ifdef CONFIG_CIFS_ALLOW_INSECURE_LEGACY -int cifs_posix_open(const char *full_path, struct inode **pinode, - struct super_block *sb, int mode, unsigned int f_flags, - __u32 *poplock, __u16 *pnetfid, unsigned int xid) -{ - int rc; - FILE_UNIX_BASIC_INFO *presp_data; - __u32 posix_flags = 0; - struct cifs_sb_info *cifs_sb = CIFS_SB(sb); - struct cifs_fattr fattr; - struct tcon_link *tlink; - struct cifs_tcon *tcon; - - cifs_dbg(FYI, "posix open %s\n", full_path); - - presp_data = kzalloc(sizeof(FILE_UNIX_BASIC_INFO), GFP_KERNEL); - if (presp_data == NULL) - return -ENOMEM; - - tlink = cifs_sb_tlink(cifs_sb); - if (IS_ERR(tlink)) { - rc = PTR_ERR(tlink); - goto posix_open_ret; - } - - tcon = tlink_tcon(tlink); - mode &= ~current_umask(); - - posix_flags = cifs_posix_convert_flags(f_flags); - rc = CIFSPOSIXCreate(xid, tcon, posix_flags, mode, pnetfid, presp_data, - poplock, full_path, cifs_sb->local_nls, - cifs_remap(cifs_sb)); - cifs_put_tlink(tlink); - - if (rc) - goto posix_open_ret; - - if (presp_data->Type == cpu_to_le32(-1)) - goto posix_open_ret; /* open ok, caller does qpathinfo */ - - if (!pinode) - goto posix_open_ret; /* caller does not need info */ - - cifs_unix_basic_to_fattr(&fattr, presp_data, cifs_sb); - - /* get new inode and set it up */ - if (*pinode == NULL) { - cifs_fill_uniqueid(sb, &fattr); - *pinode = cifs_iget(sb, &fattr); - if (!*pinode) { - rc = -ENOMEM; - goto posix_open_ret; - } - } else { - cifs_revalidate_mapping(*pinode); - rc = cifs_fattr_to_inode(*pinode, &fattr); - } - -posix_open_ret: - kfree(presp_data); - return rc; -} -#endif /* CONFIG_CIFS_ALLOW_INSECURE_LEGACY */ - -static int cifs_nt_open(const char *full_path, struct inode *inode, struct cifs_sb_info *cifs_sb, - struct cifs_tcon *tcon, unsigned int f_flags, __u32 *oplock, - struct cifs_fid *fid, unsigned int xid, struct cifs_open_info_data *buf) -{ - int rc; - int desired_access; - int disposition; - int create_options = CREATE_NOT_DIR; - struct TCP_Server_Info *server = tcon->ses->server; - struct cifs_open_parms oparms; - - if (!server->ops->open) - return -ENOSYS; - - desired_access = cifs_convert_flags(f_flags); - -/********************************************************************* - * open flag mapping table: - * - * POSIX Flag CIFS Disposition - * ---------- ---------------- - * O_CREAT FILE_OPEN_IF - * O_CREAT | O_EXCL FILE_CREATE - * O_CREAT | O_TRUNC FILE_OVERWRITE_IF - * O_TRUNC FILE_OVERWRITE - * none of the above FILE_OPEN - * - * Note that there is not a direct match between disposition - * FILE_SUPERSEDE (ie create whether or not file exists although - * O_CREAT | O_TRUNC is similar but truncates the existing - * file rather than creating a new file as FILE_SUPERSEDE does - * (which uses the attributes / metadata passed in on open call) - *? - *? O_SYNC is a reasonable match to CIFS writethrough flag - *? and the read write flags match reasonably. O_LARGEFILE - *? is irrelevant because largefile support is always used - *? by this client. Flags O_APPEND, O_DIRECT, O_DIRECTORY, - * O_FASYNC, O_NOFOLLOW, O_NONBLOCK need further investigation - *********************************************************************/ - - disposition = cifs_get_disposition(f_flags); - - /* BB pass O_SYNC flag through on file attributes .. BB */ - - /* O_SYNC also has bit for O_DSYNC so following check picks up either */ - if (f_flags & O_SYNC) - create_options |= CREATE_WRITE_THROUGH; - - if (f_flags & O_DIRECT) - create_options |= CREATE_NO_BUFFER; - - oparms = (struct cifs_open_parms) { - .tcon = tcon, - .cifs_sb = cifs_sb, - .desired_access = desired_access, - .create_options = cifs_create_options(cifs_sb, create_options), - .disposition = disposition, - .path = full_path, - .fid = fid, - }; - - rc = server->ops->open(xid, &oparms, oplock, buf); - if (rc) - return rc; - - /* TODO: Add support for calling posix query info but with passing in fid */ - if (tcon->unix_ext) - rc = cifs_get_inode_info_unix(&inode, full_path, inode->i_sb, - xid); - else - rc = cifs_get_inode_info(&inode, full_path, buf, inode->i_sb, - xid, fid); - - if (rc) { - server->ops->close(xid, tcon, fid); - if (rc == -ESTALE) - rc = -EOPENSTALE; - } - - return rc; -} - -static bool -cifs_has_mand_locks(struct cifsInodeInfo *cinode) -{ - struct cifs_fid_locks *cur; - bool has_locks = false; - - down_read(&cinode->lock_sem); - list_for_each_entry(cur, &cinode->llist, llist) { - if (!list_empty(&cur->locks)) { - has_locks = true; - break; - } - } - up_read(&cinode->lock_sem); - return has_locks; -} - -void -cifs_down_write(struct rw_semaphore *sem) -{ - while (!down_write_trylock(sem)) - msleep(10); -} - -static void cifsFileInfo_put_work(struct work_struct *work); - -struct cifsFileInfo *cifs_new_fileinfo(struct cifs_fid *fid, struct file *file, - struct tcon_link *tlink, __u32 oplock, - const char *symlink_target) -{ - struct dentry *dentry = file_dentry(file); - struct inode *inode = d_inode(dentry); - struct cifsInodeInfo *cinode = CIFS_I(inode); - struct cifsFileInfo *cfile; - struct cifs_fid_locks *fdlocks; - struct cifs_tcon *tcon = tlink_tcon(tlink); - struct TCP_Server_Info *server = tcon->ses->server; - - cfile = kzalloc(sizeof(struct cifsFileInfo), GFP_KERNEL); - if (cfile == NULL) - return cfile; - - fdlocks = kzalloc(sizeof(struct cifs_fid_locks), GFP_KERNEL); - if (!fdlocks) { - kfree(cfile); - return NULL; - } - - if (symlink_target) { - cfile->symlink_target = kstrdup(symlink_target, GFP_KERNEL); - if (!cfile->symlink_target) { - kfree(fdlocks); - kfree(cfile); - return NULL; - } - } - - INIT_LIST_HEAD(&fdlocks->locks); - fdlocks->cfile = cfile; - cfile->llist = fdlocks; - - cfile->count = 1; - cfile->pid = current->tgid; - cfile->uid = current_fsuid(); - cfile->dentry = dget(dentry); - cfile->f_flags = file->f_flags; - cfile->invalidHandle = false; - cfile->deferred_close_scheduled = false; - cfile->tlink = cifs_get_tlink(tlink); - INIT_WORK(&cfile->oplock_break, cifs_oplock_break); - INIT_WORK(&cfile->put, cifsFileInfo_put_work); - INIT_DELAYED_WORK(&cfile->deferred, smb2_deferred_work_close); - mutex_init(&cfile->fh_mutex); - spin_lock_init(&cfile->file_info_lock); - - cifs_sb_active(inode->i_sb); - - /* - * If the server returned a read oplock and we have mandatory brlocks, - * set oplock level to None. - */ - if (server->ops->is_read_op(oplock) && cifs_has_mand_locks(cinode)) { - cifs_dbg(FYI, "Reset oplock val from read to None due to mand locks\n"); - oplock = 0; - } - - cifs_down_write(&cinode->lock_sem); - list_add(&fdlocks->llist, &cinode->llist); - up_write(&cinode->lock_sem); - - spin_lock(&tcon->open_file_lock); - if (fid->pending_open->oplock != CIFS_OPLOCK_NO_CHANGE && oplock) - oplock = fid->pending_open->oplock; - list_del(&fid->pending_open->olist); - - fid->purge_cache = false; - server->ops->set_fid(cfile, fid, oplock); - - list_add(&cfile->tlist, &tcon->openFileList); - atomic_inc(&tcon->num_local_opens); - - /* if readable file instance put first in list*/ - spin_lock(&cinode->open_file_lock); - if (file->f_mode & FMODE_READ) - list_add(&cfile->flist, &cinode->openFileList); - else - list_add_tail(&cfile->flist, &cinode->openFileList); - spin_unlock(&cinode->open_file_lock); - spin_unlock(&tcon->open_file_lock); - - if (fid->purge_cache) - cifs_zap_mapping(inode); - - file->private_data = cfile; - return cfile; -} - -struct cifsFileInfo * -cifsFileInfo_get(struct cifsFileInfo *cifs_file) -{ - spin_lock(&cifs_file->file_info_lock); - cifsFileInfo_get_locked(cifs_file); - spin_unlock(&cifs_file->file_info_lock); - return cifs_file; -} - -static void cifsFileInfo_put_final(struct cifsFileInfo *cifs_file) -{ - struct inode *inode = d_inode(cifs_file->dentry); - struct cifsInodeInfo *cifsi = CIFS_I(inode); - struct cifsLockInfo *li, *tmp; - struct super_block *sb = inode->i_sb; - - /* - * Delete any outstanding lock records. We'll lose them when the file - * is closed anyway. - */ - cifs_down_write(&cifsi->lock_sem); - list_for_each_entry_safe(li, tmp, &cifs_file->llist->locks, llist) { - list_del(&li->llist); - cifs_del_lock_waiters(li); - kfree(li); - } - list_del(&cifs_file->llist->llist); - kfree(cifs_file->llist); - up_write(&cifsi->lock_sem); - - cifs_put_tlink(cifs_file->tlink); - dput(cifs_file->dentry); - cifs_sb_deactive(sb); - kfree(cifs_file->symlink_target); - kfree(cifs_file); -} - -static void cifsFileInfo_put_work(struct work_struct *work) -{ - struct cifsFileInfo *cifs_file = container_of(work, - struct cifsFileInfo, put); - - cifsFileInfo_put_final(cifs_file); -} - -/** - * cifsFileInfo_put - release a reference of file priv data - * - * Always potentially wait for oplock handler. See _cifsFileInfo_put(). - * - * @cifs_file: cifs/smb3 specific info (eg refcounts) for an open file - */ -void cifsFileInfo_put(struct cifsFileInfo *cifs_file) -{ - _cifsFileInfo_put(cifs_file, true, true); -} - -/** - * _cifsFileInfo_put - release a reference of file priv data - * - * This may involve closing the filehandle @cifs_file out on the - * server. Must be called without holding tcon->open_file_lock, - * cinode->open_file_lock and cifs_file->file_info_lock. - * - * If @wait_for_oplock_handler is true and we are releasing the last - * reference, wait for any running oplock break handler of the file - * and cancel any pending one. - * - * @cifs_file: cifs/smb3 specific info (eg refcounts) for an open file - * @wait_oplock_handler: must be false if called from oplock_break_handler - * @offload: not offloaded on close and oplock breaks - * - */ -void _cifsFileInfo_put(struct cifsFileInfo *cifs_file, - bool wait_oplock_handler, bool offload) -{ - struct inode *inode = d_inode(cifs_file->dentry); - struct cifs_tcon *tcon = tlink_tcon(cifs_file->tlink); - struct TCP_Server_Info *server = tcon->ses->server; - struct cifsInodeInfo *cifsi = CIFS_I(inode); - struct super_block *sb = inode->i_sb; - struct cifs_sb_info *cifs_sb = CIFS_SB(sb); - struct cifs_fid fid = {}; - struct cifs_pending_open open; - bool oplock_break_cancelled; - - spin_lock(&tcon->open_file_lock); - spin_lock(&cifsi->open_file_lock); - spin_lock(&cifs_file->file_info_lock); - if (--cifs_file->count > 0) { - spin_unlock(&cifs_file->file_info_lock); - spin_unlock(&cifsi->open_file_lock); - spin_unlock(&tcon->open_file_lock); - return; - } - spin_unlock(&cifs_file->file_info_lock); - - if (server->ops->get_lease_key) - server->ops->get_lease_key(inode, &fid); - - /* store open in pending opens to make sure we don't miss lease break */ - cifs_add_pending_open_locked(&fid, cifs_file->tlink, &open); - - /* remove it from the lists */ - list_del(&cifs_file->flist); - list_del(&cifs_file->tlist); - atomic_dec(&tcon->num_local_opens); - - if (list_empty(&cifsi->openFileList)) { - cifs_dbg(FYI, "closing last open instance for inode %p\n", - d_inode(cifs_file->dentry)); - /* - * In strict cache mode we need invalidate mapping on the last - * close because it may cause a error when we open this file - * again and get at least level II oplock. - */ - if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_STRICT_IO) - set_bit(CIFS_INO_INVALID_MAPPING, &cifsi->flags); - cifs_set_oplock_level(cifsi, 0); - } - - spin_unlock(&cifsi->open_file_lock); - spin_unlock(&tcon->open_file_lock); - - oplock_break_cancelled = wait_oplock_handler ? - cancel_work_sync(&cifs_file->oplock_break) : false; - - if (!tcon->need_reconnect && !cifs_file->invalidHandle) { - struct TCP_Server_Info *server = tcon->ses->server; - unsigned int xid; - - xid = get_xid(); - if (server->ops->close_getattr) - server->ops->close_getattr(xid, tcon, cifs_file); - else if (server->ops->close) - server->ops->close(xid, tcon, &cifs_file->fid); - _free_xid(xid); - } - - if (oplock_break_cancelled) - cifs_done_oplock_break(cifsi); - - cifs_del_pending_open(&open); - - if (offload) - queue_work(fileinfo_put_wq, &cifs_file->put); - else - cifsFileInfo_put_final(cifs_file); -} - -int cifs_open(struct inode *inode, struct file *file) - -{ - int rc = -EACCES; - unsigned int xid; - __u32 oplock; - struct cifs_sb_info *cifs_sb; - struct TCP_Server_Info *server; - struct cifs_tcon *tcon; - struct tcon_link *tlink; - struct cifsFileInfo *cfile = NULL; - void *page; - const char *full_path; - bool posix_open_ok = false; - struct cifs_fid fid = {}; - struct cifs_pending_open open; - struct cifs_open_info_data data = {}; - - xid = get_xid(); - - cifs_sb = CIFS_SB(inode->i_sb); - if (unlikely(cifs_forced_shutdown(cifs_sb))) { - free_xid(xid); - return -EIO; - } - - tlink = cifs_sb_tlink(cifs_sb); - if (IS_ERR(tlink)) { - free_xid(xid); - return PTR_ERR(tlink); - } - tcon = tlink_tcon(tlink); - server = tcon->ses->server; - - page = alloc_dentry_path(); - full_path = build_path_from_dentry(file_dentry(file), page); - if (IS_ERR(full_path)) { - rc = PTR_ERR(full_path); - goto out; - } - - cifs_dbg(FYI, "inode = 0x%p file flags are 0x%x for %s\n", - inode, file->f_flags, full_path); - - if (file->f_flags & O_DIRECT && - cifs_sb->mnt_cifs_flags & CIFS_MOUNT_STRICT_IO) { - if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_NO_BRL) - file->f_op = &cifs_file_direct_nobrl_ops; - else - file->f_op = &cifs_file_direct_ops; - } - - /* Get the cached handle as SMB2 close is deferred */ - rc = cifs_get_readable_path(tcon, full_path, &cfile); - if (rc == 0) { - if (file->f_flags == cfile->f_flags) { - file->private_data = cfile; - spin_lock(&CIFS_I(inode)->deferred_lock); - cifs_del_deferred_close(cfile); - spin_unlock(&CIFS_I(inode)->deferred_lock); - goto use_cache; - } else { - _cifsFileInfo_put(cfile, true, false); - } - } - - if (server->oplocks) - oplock = REQ_OPLOCK; - else - oplock = 0; - -#ifdef CONFIG_CIFS_ALLOW_INSECURE_LEGACY - if (!tcon->broken_posix_open && tcon->unix_ext && - cap_unix(tcon->ses) && (CIFS_UNIX_POSIX_PATH_OPS_CAP & - le64_to_cpu(tcon->fsUnixInfo.Capability))) { - /* can not refresh inode info since size could be stale */ - rc = cifs_posix_open(full_path, &inode, inode->i_sb, - cifs_sb->ctx->file_mode /* ignored */, - file->f_flags, &oplock, &fid.netfid, xid); - if (rc == 0) { - cifs_dbg(FYI, "posix open succeeded\n"); - posix_open_ok = true; - } else if ((rc == -EINVAL) || (rc == -EOPNOTSUPP)) { - if (tcon->ses->serverNOS) - cifs_dbg(VFS, "server %s of type %s returned unexpected error on SMB posix open, disabling posix open support. Check if server update available.\n", - tcon->ses->ip_addr, - tcon->ses->serverNOS); - tcon->broken_posix_open = true; - } else if ((rc != -EIO) && (rc != -EREMOTE) && - (rc != -EOPNOTSUPP)) /* path not found or net err */ - goto out; - /* - * Else fallthrough to retry open the old way on network i/o - * or DFS errors. - */ - } -#endif /* CONFIG_CIFS_ALLOW_INSECURE_LEGACY */ - - if (server->ops->get_lease_key) - server->ops->get_lease_key(inode, &fid); - - cifs_add_pending_open(&fid, tlink, &open); - - if (!posix_open_ok) { - if (server->ops->get_lease_key) - server->ops->get_lease_key(inode, &fid); - - rc = cifs_nt_open(full_path, inode, cifs_sb, tcon, file->f_flags, &oplock, &fid, - xid, &data); - if (rc) { - cifs_del_pending_open(&open); - goto out; - } - } - - cfile = cifs_new_fileinfo(&fid, file, tlink, oplock, data.symlink_target); - if (cfile == NULL) { - if (server->ops->close) - server->ops->close(xid, tcon, &fid); - cifs_del_pending_open(&open); - rc = -ENOMEM; - goto out; - } - -#ifdef CONFIG_CIFS_ALLOW_INSECURE_LEGACY - if ((oplock & CIFS_CREATE_ACTION) && !posix_open_ok && tcon->unix_ext) { - /* - * Time to set mode which we can not set earlier due to - * problems creating new read-only files. - */ - struct cifs_unix_set_info_args args = { - .mode = inode->i_mode, - .uid = INVALID_UID, /* no change */ - .gid = INVALID_GID, /* no change */ - .ctime = NO_CHANGE_64, - .atime = NO_CHANGE_64, - .mtime = NO_CHANGE_64, - .device = 0, - }; - CIFSSMBUnixSetFileInfo(xid, tcon, &args, fid.netfid, - cfile->pid); - } -#endif /* CONFIG_CIFS_ALLOW_INSECURE_LEGACY */ - -use_cache: - fscache_use_cookie(cifs_inode_cookie(file_inode(file)), - file->f_mode & FMODE_WRITE); - if (file->f_flags & O_DIRECT && - (!((file->f_flags & O_ACCMODE) != O_RDONLY) || - file->f_flags & O_APPEND)) - cifs_invalidate_cache(file_inode(file), - FSCACHE_INVAL_DIO_WRITE); - -out: - free_dentry_path(page); - free_xid(xid); - cifs_put_tlink(tlink); - cifs_free_open_info(&data); - return rc; -} - -#ifdef CONFIG_CIFS_ALLOW_INSECURE_LEGACY -static int cifs_push_posix_locks(struct cifsFileInfo *cfile); -#endif /* CONFIG_CIFS_ALLOW_INSECURE_LEGACY */ - -/* - * Try to reacquire byte range locks that were released when session - * to server was lost. - */ -static int -cifs_relock_file(struct cifsFileInfo *cfile) -{ - struct cifsInodeInfo *cinode = CIFS_I(d_inode(cfile->dentry)); - struct cifs_tcon *tcon = tlink_tcon(cfile->tlink); - int rc = 0; -#ifdef CONFIG_CIFS_ALLOW_INSECURE_LEGACY - struct cifs_sb_info *cifs_sb = CIFS_SB(cfile->dentry->d_sb); -#endif /* CONFIG_CIFS_ALLOW_INSECURE_LEGACY */ - - down_read_nested(&cinode->lock_sem, SINGLE_DEPTH_NESTING); - if (cinode->can_cache_brlcks) { - /* can cache locks - no need to relock */ - up_read(&cinode->lock_sem); - return rc; - } - -#ifdef CONFIG_CIFS_ALLOW_INSECURE_LEGACY - if (cap_unix(tcon->ses) && - (CIFS_UNIX_FCNTL_CAP & le64_to_cpu(tcon->fsUnixInfo.Capability)) && - ((cifs_sb->mnt_cifs_flags & CIFS_MOUNT_NOPOSIXBRL) == 0)) - rc = cifs_push_posix_locks(cfile); - else -#endif /* CONFIG_CIFS_ALLOW_INSECURE_LEGACY */ - rc = tcon->ses->server->ops->push_mand_locks(cfile); - - up_read(&cinode->lock_sem); - return rc; -} - -static int -cifs_reopen_file(struct cifsFileInfo *cfile, bool can_flush) -{ - int rc = -EACCES; - unsigned int xid; - __u32 oplock; - struct cifs_sb_info *cifs_sb; - struct cifs_tcon *tcon; - struct TCP_Server_Info *server; - struct cifsInodeInfo *cinode; - struct inode *inode; - void *page; - const char *full_path; - int desired_access; - int disposition = FILE_OPEN; - int create_options = CREATE_NOT_DIR; - struct cifs_open_parms oparms; - - xid = get_xid(); - mutex_lock(&cfile->fh_mutex); - if (!cfile->invalidHandle) { - mutex_unlock(&cfile->fh_mutex); - free_xid(xid); - return 0; - } - - inode = d_inode(cfile->dentry); - cifs_sb = CIFS_SB(inode->i_sb); - tcon = tlink_tcon(cfile->tlink); - server = tcon->ses->server; - - /* - * Can not grab rename sem here because various ops, including those - * that already have the rename sem can end up causing writepage to get - * called and if the server was down that means we end up here, and we - * can never tell if the caller already has the rename_sem. - */ - page = alloc_dentry_path(); - full_path = build_path_from_dentry(cfile->dentry, page); - if (IS_ERR(full_path)) { - mutex_unlock(&cfile->fh_mutex); - free_dentry_path(page); - free_xid(xid); - return PTR_ERR(full_path); - } - - cifs_dbg(FYI, "inode = 0x%p file flags 0x%x for %s\n", - inode, cfile->f_flags, full_path); - - if (tcon->ses->server->oplocks) - oplock = REQ_OPLOCK; - else - oplock = 0; - -#ifdef CONFIG_CIFS_ALLOW_INSECURE_LEGACY - if (tcon->unix_ext && cap_unix(tcon->ses) && - (CIFS_UNIX_POSIX_PATH_OPS_CAP & - le64_to_cpu(tcon->fsUnixInfo.Capability))) { - /* - * O_CREAT, O_EXCL and O_TRUNC already had their effect on the - * original open. Must mask them off for a reopen. - */ - unsigned int oflags = cfile->f_flags & - ~(O_CREAT | O_EXCL | O_TRUNC); - - rc = cifs_posix_open(full_path, NULL, inode->i_sb, - cifs_sb->ctx->file_mode /* ignored */, - oflags, &oplock, &cfile->fid.netfid, xid); - if (rc == 0) { - cifs_dbg(FYI, "posix reopen succeeded\n"); - oparms.reconnect = true; - goto reopen_success; - } - /* - * fallthrough to retry open the old way on errors, especially - * in the reconnect path it is important to retry hard - */ - } -#endif /* CONFIG_CIFS_ALLOW_INSECURE_LEGACY */ - - desired_access = cifs_convert_flags(cfile->f_flags); - - /* O_SYNC also has bit for O_DSYNC so following check picks up either */ - if (cfile->f_flags & O_SYNC) - create_options |= CREATE_WRITE_THROUGH; - - if (cfile->f_flags & O_DIRECT) - create_options |= CREATE_NO_BUFFER; - - if (server->ops->get_lease_key) - server->ops->get_lease_key(inode, &cfile->fid); - - oparms = (struct cifs_open_parms) { - .tcon = tcon, - .cifs_sb = cifs_sb, - .desired_access = desired_access, - .create_options = cifs_create_options(cifs_sb, create_options), - .disposition = disposition, - .path = full_path, - .fid = &cfile->fid, - .reconnect = true, - }; - - /* - * Can not refresh inode by passing in file_info buf to be returned by - * ops->open and then calling get_inode_info with returned buf since - * file might have write behind data that needs to be flushed and server - * version of file size can be stale. If we knew for sure that inode was - * not dirty locally we could do this. - */ - rc = server->ops->open(xid, &oparms, &oplock, NULL); - if (rc == -ENOENT && oparms.reconnect == false) { - /* durable handle timeout is expired - open the file again */ - rc = server->ops->open(xid, &oparms, &oplock, NULL); - /* indicate that we need to relock the file */ - oparms.reconnect = true; - } - - if (rc) { - mutex_unlock(&cfile->fh_mutex); - cifs_dbg(FYI, "cifs_reopen returned 0x%x\n", rc); - cifs_dbg(FYI, "oplock: %d\n", oplock); - goto reopen_error_exit; - } - -#ifdef CONFIG_CIFS_ALLOW_INSECURE_LEGACY -reopen_success: -#endif /* CONFIG_CIFS_ALLOW_INSECURE_LEGACY */ - cfile->invalidHandle = false; - mutex_unlock(&cfile->fh_mutex); - cinode = CIFS_I(inode); - - if (can_flush) { - rc = filemap_write_and_wait(inode->i_mapping); - if (!is_interrupt_error(rc)) - mapping_set_error(inode->i_mapping, rc); - - if (tcon->posix_extensions) - rc = smb311_posix_get_inode_info(&inode, full_path, inode->i_sb, xid); - else if (tcon->unix_ext) - rc = cifs_get_inode_info_unix(&inode, full_path, - inode->i_sb, xid); - else - rc = cifs_get_inode_info(&inode, full_path, NULL, - inode->i_sb, xid, NULL); - } - /* - * Else we are writing out data to server already and could deadlock if - * we tried to flush data, and since we do not know if we have data that - * would invalidate the current end of file on the server we can not go - * to the server to get the new inode info. - */ - - /* - * If the server returned a read oplock and we have mandatory brlocks, - * set oplock level to None. - */ - if (server->ops->is_read_op(oplock) && cifs_has_mand_locks(cinode)) { - cifs_dbg(FYI, "Reset oplock val from read to None due to mand locks\n"); - oplock = 0; - } - - server->ops->set_fid(cfile, &cfile->fid, oplock); - if (oparms.reconnect) - cifs_relock_file(cfile); - -reopen_error_exit: - free_dentry_path(page); - free_xid(xid); - return rc; -} - -void smb2_deferred_work_close(struct work_struct *work) -{ - struct cifsFileInfo *cfile = container_of(work, - struct cifsFileInfo, deferred.work); - - spin_lock(&CIFS_I(d_inode(cfile->dentry))->deferred_lock); - cifs_del_deferred_close(cfile); - cfile->deferred_close_scheduled = false; - spin_unlock(&CIFS_I(d_inode(cfile->dentry))->deferred_lock); - _cifsFileInfo_put(cfile, true, false); -} - -int cifs_close(struct inode *inode, struct file *file) -{ - struct cifsFileInfo *cfile; - struct cifsInodeInfo *cinode = CIFS_I(inode); - struct cifs_sb_info *cifs_sb = CIFS_SB(inode->i_sb); - struct cifs_deferred_close *dclose; - - cifs_fscache_unuse_inode_cookie(inode, file->f_mode & FMODE_WRITE); - - if (file->private_data != NULL) { - cfile = file->private_data; - file->private_data = NULL; - dclose = kmalloc(sizeof(struct cifs_deferred_close), GFP_KERNEL); - if ((cinode->oplock == CIFS_CACHE_RHW_FLG) && - cinode->lease_granted && - !test_bit(CIFS_INO_CLOSE_ON_LOCK, &cinode->flags) && - dclose) { - if (test_and_clear_bit(CIFS_INO_MODIFIED_ATTR, &cinode->flags)) { - inode->i_ctime = inode->i_mtime = current_time(inode); - } - spin_lock(&cinode->deferred_lock); - cifs_add_deferred_close(cfile, dclose); - if (cfile->deferred_close_scheduled && - delayed_work_pending(&cfile->deferred)) { - /* - * If there is no pending work, mod_delayed_work queues new work. - * So, Increase the ref count to avoid use-after-free. - */ - if (!mod_delayed_work(deferredclose_wq, - &cfile->deferred, cifs_sb->ctx->closetimeo)) - cifsFileInfo_get(cfile); - } else { - /* Deferred close for files */ - queue_delayed_work(deferredclose_wq, - &cfile->deferred, cifs_sb->ctx->closetimeo); - cfile->deferred_close_scheduled = true; - spin_unlock(&cinode->deferred_lock); - return 0; - } - spin_unlock(&cinode->deferred_lock); - _cifsFileInfo_put(cfile, true, false); - } else { - _cifsFileInfo_put(cfile, true, false); - kfree(dclose); - } - } - - /* return code from the ->release op is always ignored */ - return 0; -} - -void -cifs_reopen_persistent_handles(struct cifs_tcon *tcon) -{ - struct cifsFileInfo *open_file, *tmp; - struct list_head tmp_list; - - if (!tcon->use_persistent || !tcon->need_reopen_files) - return; - - tcon->need_reopen_files = false; - - cifs_dbg(FYI, "Reopen persistent handles\n"); - INIT_LIST_HEAD(&tmp_list); - - /* list all files open on tree connection, reopen resilient handles */ - spin_lock(&tcon->open_file_lock); - list_for_each_entry(open_file, &tcon->openFileList, tlist) { - if (!open_file->invalidHandle) - continue; - cifsFileInfo_get(open_file); - list_add_tail(&open_file->rlist, &tmp_list); - } - spin_unlock(&tcon->open_file_lock); - - list_for_each_entry_safe(open_file, tmp, &tmp_list, rlist) { - if (cifs_reopen_file(open_file, false /* do not flush */)) - tcon->need_reopen_files = true; - list_del_init(&open_file->rlist); - cifsFileInfo_put(open_file); - } -} - -int cifs_closedir(struct inode *inode, struct file *file) -{ - int rc = 0; - unsigned int xid; - struct cifsFileInfo *cfile = file->private_data; - struct cifs_tcon *tcon; - struct TCP_Server_Info *server; - char *buf; - - cifs_dbg(FYI, "Closedir inode = 0x%p\n", inode); - - if (cfile == NULL) - return rc; - - xid = get_xid(); - tcon = tlink_tcon(cfile->tlink); - server = tcon->ses->server; - - cifs_dbg(FYI, "Freeing private data in close dir\n"); - spin_lock(&cfile->file_info_lock); - if (server->ops->dir_needs_close(cfile)) { - cfile->invalidHandle = true; - spin_unlock(&cfile->file_info_lock); - if (server->ops->close_dir) - rc = server->ops->close_dir(xid, tcon, &cfile->fid); - else - rc = -ENOSYS; - cifs_dbg(FYI, "Closing uncompleted readdir with rc %d\n", rc); - /* not much we can do if it fails anyway, ignore rc */ - rc = 0; - } else - spin_unlock(&cfile->file_info_lock); - - buf = cfile->srch_inf.ntwrk_buf_start; - if (buf) { - cifs_dbg(FYI, "closedir free smb buf in srch struct\n"); - cfile->srch_inf.ntwrk_buf_start = NULL; - if (cfile->srch_inf.smallBuf) - cifs_small_buf_release(buf); - else - cifs_buf_release(buf); - } - - cifs_put_tlink(cfile->tlink); - kfree(file->private_data); - file->private_data = NULL; - /* BB can we lock the filestruct while this is going on? */ - free_xid(xid); - return rc; -} - -static struct cifsLockInfo * -cifs_lock_init(__u64 offset, __u64 length, __u8 type, __u16 flags) -{ - struct cifsLockInfo *lock = - kmalloc(sizeof(struct cifsLockInfo), GFP_KERNEL); - if (!lock) - return lock; - lock->offset = offset; - lock->length = length; - lock->type = type; - lock->pid = current->tgid; - lock->flags = flags; - INIT_LIST_HEAD(&lock->blist); - init_waitqueue_head(&lock->block_q); - return lock; -} - -void -cifs_del_lock_waiters(struct cifsLockInfo *lock) -{ - struct cifsLockInfo *li, *tmp; - list_for_each_entry_safe(li, tmp, &lock->blist, blist) { - list_del_init(&li->blist); - wake_up(&li->block_q); - } -} - -#define CIFS_LOCK_OP 0 -#define CIFS_READ_OP 1 -#define CIFS_WRITE_OP 2 - -/* @rw_check : 0 - no op, 1 - read, 2 - write */ -static bool -cifs_find_fid_lock_conflict(struct cifs_fid_locks *fdlocks, __u64 offset, - __u64 length, __u8 type, __u16 flags, - struct cifsFileInfo *cfile, - struct cifsLockInfo **conf_lock, int rw_check) -{ - struct cifsLockInfo *li; - struct cifsFileInfo *cur_cfile = fdlocks->cfile; - struct TCP_Server_Info *server = tlink_tcon(cfile->tlink)->ses->server; - - list_for_each_entry(li, &fdlocks->locks, llist) { - if (offset + length <= li->offset || - offset >= li->offset + li->length) - continue; - if (rw_check != CIFS_LOCK_OP && current->tgid == li->pid && - server->ops->compare_fids(cfile, cur_cfile)) { - /* shared lock prevents write op through the same fid */ - if (!(li->type & server->vals->shared_lock_type) || - rw_check != CIFS_WRITE_OP) - continue; - } - if ((type & server->vals->shared_lock_type) && - ((server->ops->compare_fids(cfile, cur_cfile) && - current->tgid == li->pid) || type == li->type)) - continue; - if (rw_check == CIFS_LOCK_OP && - (flags & FL_OFDLCK) && (li->flags & FL_OFDLCK) && - server->ops->compare_fids(cfile, cur_cfile)) - continue; - if (conf_lock) - *conf_lock = li; - return true; - } - return false; -} - -bool -cifs_find_lock_conflict(struct cifsFileInfo *cfile, __u64 offset, __u64 length, - __u8 type, __u16 flags, - struct cifsLockInfo **conf_lock, int rw_check) -{ - bool rc = false; - struct cifs_fid_locks *cur; - struct cifsInodeInfo *cinode = CIFS_I(d_inode(cfile->dentry)); - - list_for_each_entry(cur, &cinode->llist, llist) { - rc = cifs_find_fid_lock_conflict(cur, offset, length, type, - flags, cfile, conf_lock, - rw_check); - if (rc) - break; - } - - return rc; -} - -/* - * Check if there is another lock that prevents us to set the lock (mandatory - * style). If such a lock exists, update the flock structure with its - * properties. Otherwise, set the flock type to F_UNLCK if we can cache brlocks - * or leave it the same if we can't. Returns 0 if we don't need to request to - * the server or 1 otherwise. - */ -static int -cifs_lock_test(struct cifsFileInfo *cfile, __u64 offset, __u64 length, - __u8 type, struct file_lock *flock) -{ - int rc = 0; - struct cifsLockInfo *conf_lock; - struct cifsInodeInfo *cinode = CIFS_I(d_inode(cfile->dentry)); - struct TCP_Server_Info *server = tlink_tcon(cfile->tlink)->ses->server; - bool exist; - - down_read(&cinode->lock_sem); - - exist = cifs_find_lock_conflict(cfile, offset, length, type, - flock->fl_flags, &conf_lock, - CIFS_LOCK_OP); - if (exist) { - flock->fl_start = conf_lock->offset; - flock->fl_end = conf_lock->offset + conf_lock->length - 1; - flock->fl_pid = conf_lock->pid; - if (conf_lock->type & server->vals->shared_lock_type) - flock->fl_type = F_RDLCK; - else - flock->fl_type = F_WRLCK; - } else if (!cinode->can_cache_brlcks) - rc = 1; - else - flock->fl_type = F_UNLCK; - - up_read(&cinode->lock_sem); - return rc; -} - -static void -cifs_lock_add(struct cifsFileInfo *cfile, struct cifsLockInfo *lock) -{ - struct cifsInodeInfo *cinode = CIFS_I(d_inode(cfile->dentry)); - cifs_down_write(&cinode->lock_sem); - list_add_tail(&lock->llist, &cfile->llist->locks); - up_write(&cinode->lock_sem); -} - -/* - * Set the byte-range lock (mandatory style). Returns: - * 1) 0, if we set the lock and don't need to request to the server; - * 2) 1, if no locks prevent us but we need to request to the server; - * 3) -EACCES, if there is a lock that prevents us and wait is false. - */ -static int -cifs_lock_add_if(struct cifsFileInfo *cfile, struct cifsLockInfo *lock, - bool wait) -{ - struct cifsLockInfo *conf_lock; - struct cifsInodeInfo *cinode = CIFS_I(d_inode(cfile->dentry)); - bool exist; - int rc = 0; - -try_again: - exist = false; - cifs_down_write(&cinode->lock_sem); - - exist = cifs_find_lock_conflict(cfile, lock->offset, lock->length, - lock->type, lock->flags, &conf_lock, - CIFS_LOCK_OP); - if (!exist && cinode->can_cache_brlcks) { - list_add_tail(&lock->llist, &cfile->llist->locks); - up_write(&cinode->lock_sem); - return rc; - } - - if (!exist) - rc = 1; - else if (!wait) - rc = -EACCES; - else { - list_add_tail(&lock->blist, &conf_lock->blist); - up_write(&cinode->lock_sem); - rc = wait_event_interruptible(lock->block_q, - (lock->blist.prev == &lock->blist) && - (lock->blist.next == &lock->blist)); - if (!rc) - goto try_again; - cifs_down_write(&cinode->lock_sem); - list_del_init(&lock->blist); - } - - up_write(&cinode->lock_sem); - return rc; -} - -#ifdef CONFIG_CIFS_ALLOW_INSECURE_LEGACY -/* - * Check if there is another lock that prevents us to set the lock (posix - * style). If such a lock exists, update the flock structure with its - * properties. Otherwise, set the flock type to F_UNLCK if we can cache brlocks - * or leave it the same if we can't. Returns 0 if we don't need to request to - * the server or 1 otherwise. - */ -static int -cifs_posix_lock_test(struct file *file, struct file_lock *flock) -{ - int rc = 0; - struct cifsInodeInfo *cinode = CIFS_I(file_inode(file)); - unsigned char saved_type = flock->fl_type; - - if ((flock->fl_flags & FL_POSIX) == 0) - return 1; - - down_read(&cinode->lock_sem); - posix_test_lock(file, flock); - - if (flock->fl_type == F_UNLCK && !cinode->can_cache_brlcks) { - flock->fl_type = saved_type; - rc = 1; - } - - up_read(&cinode->lock_sem); - return rc; -} - -/* - * Set the byte-range lock (posix style). Returns: - * 1) <0, if the error occurs while setting the lock; - * 2) 0, if we set the lock and don't need to request to the server; - * 3) FILE_LOCK_DEFERRED, if we will wait for some other file_lock; - * 4) FILE_LOCK_DEFERRED + 1, if we need to request to the server. - */ -static int -cifs_posix_lock_set(struct file *file, struct file_lock *flock) -{ - struct cifsInodeInfo *cinode = CIFS_I(file_inode(file)); - int rc = FILE_LOCK_DEFERRED + 1; - - if ((flock->fl_flags & FL_POSIX) == 0) - return rc; - - cifs_down_write(&cinode->lock_sem); - if (!cinode->can_cache_brlcks) { - up_write(&cinode->lock_sem); - return rc; - } - - rc = posix_lock_file(file, flock, NULL); - up_write(&cinode->lock_sem); - return rc; -} - -int -cifs_push_mandatory_locks(struct cifsFileInfo *cfile) -{ - unsigned int xid; - int rc = 0, stored_rc; - struct cifsLockInfo *li, *tmp; - struct cifs_tcon *tcon; - unsigned int num, max_num, max_buf; - LOCKING_ANDX_RANGE *buf, *cur; - static const int types[] = { - LOCKING_ANDX_LARGE_FILES, - LOCKING_ANDX_SHARED_LOCK | LOCKING_ANDX_LARGE_FILES - }; - int i; - - xid = get_xid(); - tcon = tlink_tcon(cfile->tlink); - - /* - * Accessing maxBuf is racy with cifs_reconnect - need to store value - * and check it before using. - */ - max_buf = tcon->ses->server->maxBuf; - if (max_buf < (sizeof(struct smb_hdr) + sizeof(LOCKING_ANDX_RANGE))) { - free_xid(xid); - return -EINVAL; - } - - BUILD_BUG_ON(sizeof(struct smb_hdr) + sizeof(LOCKING_ANDX_RANGE) > - PAGE_SIZE); - max_buf = min_t(unsigned int, max_buf - sizeof(struct smb_hdr), - PAGE_SIZE); - max_num = (max_buf - sizeof(struct smb_hdr)) / - sizeof(LOCKING_ANDX_RANGE); - buf = kcalloc(max_num, sizeof(LOCKING_ANDX_RANGE), GFP_KERNEL); - if (!buf) { - free_xid(xid); - return -ENOMEM; - } - - for (i = 0; i < 2; i++) { - cur = buf; - num = 0; - list_for_each_entry_safe(li, tmp, &cfile->llist->locks, llist) { - if (li->type != types[i]) - continue; - cur->Pid = cpu_to_le16(li->pid); - cur->LengthLow = cpu_to_le32((u32)li->length); - cur->LengthHigh = cpu_to_le32((u32)(li->length>>32)); - cur->OffsetLow = cpu_to_le32((u32)li->offset); - cur->OffsetHigh = cpu_to_le32((u32)(li->offset>>32)); - if (++num == max_num) { - stored_rc = cifs_lockv(xid, tcon, - cfile->fid.netfid, - (__u8)li->type, 0, num, - buf); - if (stored_rc) - rc = stored_rc; - cur = buf; - num = 0; - } else - cur++; - } - - if (num) { - stored_rc = cifs_lockv(xid, tcon, cfile->fid.netfid, - (__u8)types[i], 0, num, buf); - if (stored_rc) - rc = stored_rc; - } - } - - kfree(buf); - free_xid(xid); - return rc; -} - -static __u32 -hash_lockowner(fl_owner_t owner) -{ - return cifs_lock_secret ^ hash32_ptr((const void *)owner); -} -#endif /* CONFIG_CIFS_ALLOW_INSECURE_LEGACY */ - -struct lock_to_push { - struct list_head llist; - __u64 offset; - __u64 length; - __u32 pid; - __u16 netfid; - __u8 type; -}; - -#ifdef CONFIG_CIFS_ALLOW_INSECURE_LEGACY -static int -cifs_push_posix_locks(struct cifsFileInfo *cfile) -{ - struct inode *inode = d_inode(cfile->dentry); - struct cifs_tcon *tcon = tlink_tcon(cfile->tlink); - struct file_lock *flock; - struct file_lock_context *flctx = inode->i_flctx; - unsigned int count = 0, i; - int rc = 0, xid, type; - struct list_head locks_to_send, *el; - struct lock_to_push *lck, *tmp; - __u64 length; - - xid = get_xid(); - - if (!flctx) - goto out; - - spin_lock(&flctx->flc_lock); - list_for_each(el, &flctx->flc_posix) { - count++; - } - spin_unlock(&flctx->flc_lock); - - INIT_LIST_HEAD(&locks_to_send); - - /* - * Allocating count locks is enough because no FL_POSIX locks can be - * added to the list while we are holding cinode->lock_sem that - * protects locking operations of this inode. - */ - for (i = 0; i < count; i++) { - lck = kmalloc(sizeof(struct lock_to_push), GFP_KERNEL); - if (!lck) { - rc = -ENOMEM; - goto err_out; - } - list_add_tail(&lck->llist, &locks_to_send); - } - - el = locks_to_send.next; - spin_lock(&flctx->flc_lock); - list_for_each_entry(flock, &flctx->flc_posix, fl_list) { - if (el == &locks_to_send) { - /* - * The list ended. We don't have enough allocated - * structures - something is really wrong. - */ - cifs_dbg(VFS, "Can't push all brlocks!\n"); - break; - } - length = cifs_flock_len(flock); - if (flock->fl_type == F_RDLCK || flock->fl_type == F_SHLCK) - type = CIFS_RDLCK; - else - type = CIFS_WRLCK; - lck = list_entry(el, struct lock_to_push, llist); - lck->pid = hash_lockowner(flock->fl_owner); - lck->netfid = cfile->fid.netfid; - lck->length = length; - lck->type = type; - lck->offset = flock->fl_start; - } - spin_unlock(&flctx->flc_lock); - - list_for_each_entry_safe(lck, tmp, &locks_to_send, llist) { - int stored_rc; - - stored_rc = CIFSSMBPosixLock(xid, tcon, lck->netfid, lck->pid, - lck->offset, lck->length, NULL, - lck->type, 0); - if (stored_rc) - rc = stored_rc; - list_del(&lck->llist); - kfree(lck); - } - -out: - free_xid(xid); - return rc; -err_out: - list_for_each_entry_safe(lck, tmp, &locks_to_send, llist) { - list_del(&lck->llist); - kfree(lck); - } - goto out; -} -#endif /* CONFIG_CIFS_ALLOW_INSECURE_LEGACY */ - -static int -cifs_push_locks(struct cifsFileInfo *cfile) -{ - struct cifsInodeInfo *cinode = CIFS_I(d_inode(cfile->dentry)); - struct cifs_tcon *tcon = tlink_tcon(cfile->tlink); - int rc = 0; -#ifdef CONFIG_CIFS_ALLOW_INSECURE_LEGACY - struct cifs_sb_info *cifs_sb = CIFS_SB(cfile->dentry->d_sb); -#endif /* CONFIG_CIFS_ALLOW_INSECURE_LEGACY */ - - /* we are going to update can_cache_brlcks here - need a write access */ - cifs_down_write(&cinode->lock_sem); - if (!cinode->can_cache_brlcks) { - up_write(&cinode->lock_sem); - return rc; - } - -#ifdef CONFIG_CIFS_ALLOW_INSECURE_LEGACY - if (cap_unix(tcon->ses) && - (CIFS_UNIX_FCNTL_CAP & le64_to_cpu(tcon->fsUnixInfo.Capability)) && - ((cifs_sb->mnt_cifs_flags & CIFS_MOUNT_NOPOSIXBRL) == 0)) - rc = cifs_push_posix_locks(cfile); - else -#endif /* CONFIG_CIFS_ALLOW_INSECURE_LEGACY */ - rc = tcon->ses->server->ops->push_mand_locks(cfile); - - cinode->can_cache_brlcks = false; - up_write(&cinode->lock_sem); - return rc; -} - -static void -cifs_read_flock(struct file_lock *flock, __u32 *type, int *lock, int *unlock, - bool *wait_flag, struct TCP_Server_Info *server) -{ - if (flock->fl_flags & FL_POSIX) - cifs_dbg(FYI, "Posix\n"); - if (flock->fl_flags & FL_FLOCK) - cifs_dbg(FYI, "Flock\n"); - if (flock->fl_flags & FL_SLEEP) { - cifs_dbg(FYI, "Blocking lock\n"); - *wait_flag = true; - } - if (flock->fl_flags & FL_ACCESS) - cifs_dbg(FYI, "Process suspended by mandatory locking - not implemented yet\n"); - if (flock->fl_flags & FL_LEASE) - cifs_dbg(FYI, "Lease on file - not implemented yet\n"); - if (flock->fl_flags & - (~(FL_POSIX | FL_FLOCK | FL_SLEEP | - FL_ACCESS | FL_LEASE | FL_CLOSE | FL_OFDLCK))) - cifs_dbg(FYI, "Unknown lock flags 0x%x\n", flock->fl_flags); - - *type = server->vals->large_lock_type; - if (flock->fl_type == F_WRLCK) { - cifs_dbg(FYI, "F_WRLCK\n"); - *type |= server->vals->exclusive_lock_type; - *lock = 1; - } else if (flock->fl_type == F_UNLCK) { - cifs_dbg(FYI, "F_UNLCK\n"); - *type |= server->vals->unlock_lock_type; - *unlock = 1; - /* Check if unlock includes more than one lock range */ - } else if (flock->fl_type == F_RDLCK) { - cifs_dbg(FYI, "F_RDLCK\n"); - *type |= server->vals->shared_lock_type; - *lock = 1; - } else if (flock->fl_type == F_EXLCK) { - cifs_dbg(FYI, "F_EXLCK\n"); - *type |= server->vals->exclusive_lock_type; - *lock = 1; - } else if (flock->fl_type == F_SHLCK) { - cifs_dbg(FYI, "F_SHLCK\n"); - *type |= server->vals->shared_lock_type; - *lock = 1; - } else - cifs_dbg(FYI, "Unknown type of lock\n"); -} - -static int -cifs_getlk(struct file *file, struct file_lock *flock, __u32 type, - bool wait_flag, bool posix_lck, unsigned int xid) -{ - int rc = 0; - __u64 length = cifs_flock_len(flock); - struct cifsFileInfo *cfile = (struct cifsFileInfo *)file->private_data; - struct cifs_tcon *tcon = tlink_tcon(cfile->tlink); - struct TCP_Server_Info *server = tcon->ses->server; -#ifdef CONFIG_CIFS_ALLOW_INSECURE_LEGACY - __u16 netfid = cfile->fid.netfid; - - if (posix_lck) { - int posix_lock_type; - - rc = cifs_posix_lock_test(file, flock); - if (!rc) - return rc; - - if (type & server->vals->shared_lock_type) - posix_lock_type = CIFS_RDLCK; - else - posix_lock_type = CIFS_WRLCK; - rc = CIFSSMBPosixLock(xid, tcon, netfid, - hash_lockowner(flock->fl_owner), - flock->fl_start, length, flock, - posix_lock_type, wait_flag); - return rc; - } -#endif /* CONFIG_CIFS_ALLOW_INSECURE_LEGACY */ - - rc = cifs_lock_test(cfile, flock->fl_start, length, type, flock); - if (!rc) - return rc; - - /* BB we could chain these into one lock request BB */ - rc = server->ops->mand_lock(xid, cfile, flock->fl_start, length, type, - 1, 0, false); - if (rc == 0) { - rc = server->ops->mand_lock(xid, cfile, flock->fl_start, length, - type, 0, 1, false); - flock->fl_type = F_UNLCK; - if (rc != 0) - cifs_dbg(VFS, "Error unlocking previously locked range %d during test of lock\n", - rc); - return 0; - } - - if (type & server->vals->shared_lock_type) { - flock->fl_type = F_WRLCK; - return 0; - } - - type &= ~server->vals->exclusive_lock_type; - - rc = server->ops->mand_lock(xid, cfile, flock->fl_start, length, - type | server->vals->shared_lock_type, - 1, 0, false); - if (rc == 0) { - rc = server->ops->mand_lock(xid, cfile, flock->fl_start, length, - type | server->vals->shared_lock_type, 0, 1, false); - flock->fl_type = F_RDLCK; - if (rc != 0) - cifs_dbg(VFS, "Error unlocking previously locked range %d during test of lock\n", - rc); - } else - flock->fl_type = F_WRLCK; - - return 0; -} - -void -cifs_move_llist(struct list_head *source, struct list_head *dest) -{ - struct list_head *li, *tmp; - list_for_each_safe(li, tmp, source) - list_move(li, dest); -} - -void -cifs_free_llist(struct list_head *llist) -{ - struct cifsLockInfo *li, *tmp; - list_for_each_entry_safe(li, tmp, llist, llist) { - cifs_del_lock_waiters(li); - list_del(&li->llist); - kfree(li); - } -} - -#ifdef CONFIG_CIFS_ALLOW_INSECURE_LEGACY -int -cifs_unlock_range(struct cifsFileInfo *cfile, struct file_lock *flock, - unsigned int xid) -{ - int rc = 0, stored_rc; - static const int types[] = { - LOCKING_ANDX_LARGE_FILES, - LOCKING_ANDX_SHARED_LOCK | LOCKING_ANDX_LARGE_FILES - }; - unsigned int i; - unsigned int max_num, num, max_buf; - LOCKING_ANDX_RANGE *buf, *cur; - struct cifs_tcon *tcon = tlink_tcon(cfile->tlink); - struct cifsInodeInfo *cinode = CIFS_I(d_inode(cfile->dentry)); - struct cifsLockInfo *li, *tmp; - __u64 length = cifs_flock_len(flock); - struct list_head tmp_llist; - - INIT_LIST_HEAD(&tmp_llist); - - /* - * Accessing maxBuf is racy with cifs_reconnect - need to store value - * and check it before using. - */ - max_buf = tcon->ses->server->maxBuf; - if (max_buf < (sizeof(struct smb_hdr) + sizeof(LOCKING_ANDX_RANGE))) - return -EINVAL; - - BUILD_BUG_ON(sizeof(struct smb_hdr) + sizeof(LOCKING_ANDX_RANGE) > - PAGE_SIZE); - max_buf = min_t(unsigned int, max_buf - sizeof(struct smb_hdr), - PAGE_SIZE); - max_num = (max_buf - sizeof(struct smb_hdr)) / - sizeof(LOCKING_ANDX_RANGE); - buf = kcalloc(max_num, sizeof(LOCKING_ANDX_RANGE), GFP_KERNEL); - if (!buf) - return -ENOMEM; - - cifs_down_write(&cinode->lock_sem); - for (i = 0; i < 2; i++) { - cur = buf; - num = 0; - list_for_each_entry_safe(li, tmp, &cfile->llist->locks, llist) { - if (flock->fl_start > li->offset || - (flock->fl_start + length) < - (li->offset + li->length)) - continue; - if (current->tgid != li->pid) - continue; - if (types[i] != li->type) - continue; - if (cinode->can_cache_brlcks) { - /* - * We can cache brlock requests - simply remove - * a lock from the file's list. - */ - list_del(&li->llist); - cifs_del_lock_waiters(li); - kfree(li); - continue; - } - cur->Pid = cpu_to_le16(li->pid); - cur->LengthLow = cpu_to_le32((u32)li->length); - cur->LengthHigh = cpu_to_le32((u32)(li->length>>32)); - cur->OffsetLow = cpu_to_le32((u32)li->offset); - cur->OffsetHigh = cpu_to_le32((u32)(li->offset>>32)); - /* - * We need to save a lock here to let us add it again to - * the file's list if the unlock range request fails on - * the server. - */ - list_move(&li->llist, &tmp_llist); - if (++num == max_num) { - stored_rc = cifs_lockv(xid, tcon, - cfile->fid.netfid, - li->type, num, 0, buf); - if (stored_rc) { - /* - * We failed on the unlock range - * request - add all locks from the tmp - * list to the head of the file's list. - */ - cifs_move_llist(&tmp_llist, - &cfile->llist->locks); - rc = stored_rc; - } else - /* - * The unlock range request succeed - - * free the tmp list. - */ - cifs_free_llist(&tmp_llist); - cur = buf; - num = 0; - } else - cur++; - } - if (num) { - stored_rc = cifs_lockv(xid, tcon, cfile->fid.netfid, - types[i], num, 0, buf); - if (stored_rc) { - cifs_move_llist(&tmp_llist, - &cfile->llist->locks); - rc = stored_rc; - } else - cifs_free_llist(&tmp_llist); - } - } - - up_write(&cinode->lock_sem); - kfree(buf); - return rc; -} -#endif /* CONFIG_CIFS_ALLOW_INSECURE_LEGACY */ - -static int -cifs_setlk(struct file *file, struct file_lock *flock, __u32 type, - bool wait_flag, bool posix_lck, int lock, int unlock, - unsigned int xid) -{ - int rc = 0; - __u64 length = cifs_flock_len(flock); - struct cifsFileInfo *cfile = (struct cifsFileInfo *)file->private_data; - struct cifs_tcon *tcon = tlink_tcon(cfile->tlink); - struct TCP_Server_Info *server = tcon->ses->server; - struct inode *inode = d_inode(cfile->dentry); - -#ifdef CONFIG_CIFS_ALLOW_INSECURE_LEGACY - if (posix_lck) { - int posix_lock_type; - - rc = cifs_posix_lock_set(file, flock); - if (rc <= FILE_LOCK_DEFERRED) - return rc; - - if (type & server->vals->shared_lock_type) - posix_lock_type = CIFS_RDLCK; - else - posix_lock_type = CIFS_WRLCK; - - if (unlock == 1) - posix_lock_type = CIFS_UNLCK; - - rc = CIFSSMBPosixLock(xid, tcon, cfile->fid.netfid, - hash_lockowner(flock->fl_owner), - flock->fl_start, length, - NULL, posix_lock_type, wait_flag); - goto out; - } -#endif /* CONFIG_CIFS_ALLOW_INSECURE_LEGACY */ - if (lock) { - struct cifsLockInfo *lock; - - lock = cifs_lock_init(flock->fl_start, length, type, - flock->fl_flags); - if (!lock) - return -ENOMEM; - - rc = cifs_lock_add_if(cfile, lock, wait_flag); - if (rc < 0) { - kfree(lock); - return rc; - } - if (!rc) - goto out; - - /* - * Windows 7 server can delay breaking lease from read to None - * if we set a byte-range lock on a file - break it explicitly - * before sending the lock to the server to be sure the next - * read won't conflict with non-overlapted locks due to - * pagereading. - */ - if (!CIFS_CACHE_WRITE(CIFS_I(inode)) && - CIFS_CACHE_READ(CIFS_I(inode))) { - cifs_zap_mapping(inode); - cifs_dbg(FYI, "Set no oplock for inode=%p due to mand locks\n", - inode); - CIFS_I(inode)->oplock = 0; - } - - rc = server->ops->mand_lock(xid, cfile, flock->fl_start, length, - type, 1, 0, wait_flag); - if (rc) { - kfree(lock); - return rc; - } - - cifs_lock_add(cfile, lock); - } else if (unlock) - rc = server->ops->mand_unlock_range(cfile, flock, xid); - -out: - if ((flock->fl_flags & FL_POSIX) || (flock->fl_flags & FL_FLOCK)) { - /* - * If this is a request to remove all locks because we - * are closing the file, it doesn't matter if the - * unlocking failed as both cifs.ko and the SMB server - * remove the lock on file close - */ - if (rc) { - cifs_dbg(VFS, "%s failed rc=%d\n", __func__, rc); - if (!(flock->fl_flags & FL_CLOSE)) - return rc; - } - rc = locks_lock_file_wait(file, flock); - } - return rc; -} - -int cifs_flock(struct file *file, int cmd, struct file_lock *fl) -{ - int rc, xid; - int lock = 0, unlock = 0; - bool wait_flag = false; - bool posix_lck = false; - struct cifs_sb_info *cifs_sb; - struct cifs_tcon *tcon; - struct cifsFileInfo *cfile; - __u32 type; - - xid = get_xid(); - - if (!(fl->fl_flags & FL_FLOCK)) { - rc = -ENOLCK; - free_xid(xid); - return rc; - } - - cfile = (struct cifsFileInfo *)file->private_data; - tcon = tlink_tcon(cfile->tlink); - - cifs_read_flock(fl, &type, &lock, &unlock, &wait_flag, - tcon->ses->server); - cifs_sb = CIFS_FILE_SB(file); - - if (cap_unix(tcon->ses) && - (CIFS_UNIX_FCNTL_CAP & le64_to_cpu(tcon->fsUnixInfo.Capability)) && - ((cifs_sb->mnt_cifs_flags & CIFS_MOUNT_NOPOSIXBRL) == 0)) - posix_lck = true; - - if (!lock && !unlock) { - /* - * if no lock or unlock then nothing to do since we do not - * know what it is - */ - rc = -EOPNOTSUPP; - free_xid(xid); - return rc; - } - - rc = cifs_setlk(file, fl, type, wait_flag, posix_lck, lock, unlock, - xid); - free_xid(xid); - return rc; - - -} - -int cifs_lock(struct file *file, int cmd, struct file_lock *flock) -{ - int rc, xid; - int lock = 0, unlock = 0; - bool wait_flag = false; - bool posix_lck = false; - struct cifs_sb_info *cifs_sb; - struct cifs_tcon *tcon; - struct cifsFileInfo *cfile; - __u32 type; - - rc = -EACCES; - xid = get_xid(); - - cifs_dbg(FYI, "%s: %pD2 cmd=0x%x type=0x%x flags=0x%x r=%lld:%lld\n", __func__, file, cmd, - flock->fl_flags, flock->fl_type, (long long)flock->fl_start, - (long long)flock->fl_end); - - cfile = (struct cifsFileInfo *)file->private_data; - tcon = tlink_tcon(cfile->tlink); - - cifs_read_flock(flock, &type, &lock, &unlock, &wait_flag, - tcon->ses->server); - cifs_sb = CIFS_FILE_SB(file); - set_bit(CIFS_INO_CLOSE_ON_LOCK, &CIFS_I(d_inode(cfile->dentry))->flags); - - if (cap_unix(tcon->ses) && - (CIFS_UNIX_FCNTL_CAP & le64_to_cpu(tcon->fsUnixInfo.Capability)) && - ((cifs_sb->mnt_cifs_flags & CIFS_MOUNT_NOPOSIXBRL) == 0)) - posix_lck = true; - /* - * BB add code here to normalize offset and length to account for - * negative length which we can not accept over the wire. - */ - if (IS_GETLK(cmd)) { - rc = cifs_getlk(file, flock, type, wait_flag, posix_lck, xid); - free_xid(xid); - return rc; - } - - if (!lock && !unlock) { - /* - * if no lock or unlock then nothing to do since we do not - * know what it is - */ - free_xid(xid); - return -EOPNOTSUPP; - } - - rc = cifs_setlk(file, flock, type, wait_flag, posix_lck, lock, unlock, - xid); - free_xid(xid); - return rc; -} - -/* - * update the file size (if needed) after a write. Should be called with - * the inode->i_lock held - */ -void -cifs_update_eof(struct cifsInodeInfo *cifsi, loff_t offset, - unsigned int bytes_written) -{ - loff_t end_of_write = offset + bytes_written; - - if (end_of_write > cifsi->server_eof) - cifsi->server_eof = end_of_write; -} - -static ssize_t -cifs_write(struct cifsFileInfo *open_file, __u32 pid, const char *write_data, - size_t write_size, loff_t *offset) -{ - int rc = 0; - unsigned int bytes_written = 0; - unsigned int total_written; - struct cifs_tcon *tcon; - struct TCP_Server_Info *server; - unsigned int xid; - struct dentry *dentry = open_file->dentry; - struct cifsInodeInfo *cifsi = CIFS_I(d_inode(dentry)); - struct cifs_io_parms io_parms = {0}; - - cifs_dbg(FYI, "write %zd bytes to offset %lld of %pd\n", - write_size, *offset, dentry); - - tcon = tlink_tcon(open_file->tlink); - server = tcon->ses->server; - - if (!server->ops->sync_write) - return -ENOSYS; - - xid = get_xid(); - - for (total_written = 0; write_size > total_written; - total_written += bytes_written) { - rc = -EAGAIN; - while (rc == -EAGAIN) { - struct kvec iov[2]; - unsigned int len; - - if (open_file->invalidHandle) { - /* we could deadlock if we called - filemap_fdatawait from here so tell - reopen_file not to flush data to - server now */ - rc = cifs_reopen_file(open_file, false); - if (rc != 0) - break; - } - - len = min(server->ops->wp_retry_size(d_inode(dentry)), - (unsigned int)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; - io_parms.pid = pid; - io_parms.tcon = tcon; - io_parms.offset = *offset; - io_parms.length = len; - rc = server->ops->sync_write(xid, &open_file->fid, - &io_parms, &bytes_written, iov, 1); - } - if (rc || (bytes_written == 0)) { - if (total_written) - break; - else { - free_xid(xid); - return rc; - } - } else { - spin_lock(&d_inode(dentry)->i_lock); - cifs_update_eof(cifsi, *offset, bytes_written); - spin_unlock(&d_inode(dentry)->i_lock); - *offset += bytes_written; - } - } - - cifs_stats_bytes_written(tcon, total_written); - - if (total_written > 0) { - spin_lock(&d_inode(dentry)->i_lock); - if (*offset > d_inode(dentry)->i_size) { - i_size_write(d_inode(dentry), *offset); - d_inode(dentry)->i_blocks = (512 - 1 + *offset) >> 9; - } - spin_unlock(&d_inode(dentry)->i_lock); - } - mark_inode_dirty_sync(d_inode(dentry)); - free_xid(xid); - return total_written; -} - -struct cifsFileInfo *find_readable_file(struct cifsInodeInfo *cifs_inode, - bool fsuid_only) -{ - struct cifsFileInfo *open_file = NULL; - struct cifs_sb_info *cifs_sb = CIFS_SB(cifs_inode->netfs.inode.i_sb); - - /* only filter by fsuid on multiuser mounts */ - if (!(cifs_sb->mnt_cifs_flags & CIFS_MOUNT_MULTIUSER)) - fsuid_only = false; - - spin_lock(&cifs_inode->open_file_lock); - /* we could simply get the first_list_entry since write-only entries - are always at the end of the list but since the first entry might - have a close pending, we go through the whole list */ - list_for_each_entry(open_file, &cifs_inode->openFileList, flist) { - if (fsuid_only && !uid_eq(open_file->uid, current_fsuid())) - continue; - if (OPEN_FMODE(open_file->f_flags) & FMODE_READ) { - if ((!open_file->invalidHandle)) { - /* found a good file */ - /* lock it so it will not be closed on us */ - cifsFileInfo_get(open_file); - spin_unlock(&cifs_inode->open_file_lock); - return open_file; - } /* else might as well continue, and look for - another, or simply have the caller reopen it - again rather than trying to fix this handle */ - } else /* write only file */ - break; /* write only files are last so must be done */ - } - spin_unlock(&cifs_inode->open_file_lock); - return NULL; -} - -/* Return -EBADF if no handle is found and general rc otherwise */ -int -cifs_get_writable_file(struct cifsInodeInfo *cifs_inode, int flags, - struct cifsFileInfo **ret_file) -{ - struct cifsFileInfo *open_file, *inv_file = NULL; - struct cifs_sb_info *cifs_sb; - bool any_available = false; - int rc = -EBADF; - unsigned int refind = 0; - bool fsuid_only = flags & FIND_WR_FSUID_ONLY; - bool with_delete = flags & FIND_WR_WITH_DELETE; - *ret_file = NULL; - - /* - * Having a null inode here (because mapping->host was set to zero by - * the VFS or MM) should not happen but we had reports of on oops (due - * to it being zero) during stress testcases so we need to check for it - */ - - if (cifs_inode == NULL) { - cifs_dbg(VFS, "Null inode passed to cifs_writeable_file\n"); - dump_stack(); - return rc; - } - - cifs_sb = CIFS_SB(cifs_inode->netfs.inode.i_sb); - - /* only filter by fsuid on multiuser mounts */ - if (!(cifs_sb->mnt_cifs_flags & CIFS_MOUNT_MULTIUSER)) - fsuid_only = false; - - spin_lock(&cifs_inode->open_file_lock); -refind_writable: - if (refind > MAX_REOPEN_ATT) { - spin_unlock(&cifs_inode->open_file_lock); - return rc; - } - list_for_each_entry(open_file, &cifs_inode->openFileList, flist) { - if (!any_available && open_file->pid != current->tgid) - continue; - if (fsuid_only && !uid_eq(open_file->uid, current_fsuid())) - continue; - if (with_delete && !(open_file->fid.access & DELETE)) - continue; - if (OPEN_FMODE(open_file->f_flags) & FMODE_WRITE) { - if (!open_file->invalidHandle) { - /* found a good writable file */ - cifsFileInfo_get(open_file); - spin_unlock(&cifs_inode->open_file_lock); - *ret_file = open_file; - return 0; - } else { - if (!inv_file) - inv_file = open_file; - } - } - } - /* couldn't find useable FH with same pid, try any available */ - if (!any_available) { - any_available = true; - goto refind_writable; - } - - if (inv_file) { - any_available = false; - cifsFileInfo_get(inv_file); - } - - spin_unlock(&cifs_inode->open_file_lock); - - if (inv_file) { - rc = cifs_reopen_file(inv_file, false); - if (!rc) { - *ret_file = inv_file; - return 0; - } - - spin_lock(&cifs_inode->open_file_lock); - list_move_tail(&inv_file->flist, &cifs_inode->openFileList); - spin_unlock(&cifs_inode->open_file_lock); - cifsFileInfo_put(inv_file); - ++refind; - inv_file = NULL; - spin_lock(&cifs_inode->open_file_lock); - goto refind_writable; - } - - return rc; -} - -struct cifsFileInfo * -find_writable_file(struct cifsInodeInfo *cifs_inode, int flags) -{ - struct cifsFileInfo *cfile; - int rc; - - rc = cifs_get_writable_file(cifs_inode, flags, &cfile); - if (rc) - cifs_dbg(FYI, "Couldn't find writable handle rc=%d\n", rc); - - return cfile; -} - -int -cifs_get_writable_path(struct cifs_tcon *tcon, const char *name, - int flags, - struct cifsFileInfo **ret_file) -{ - struct cifsFileInfo *cfile; - void *page = alloc_dentry_path(); - - *ret_file = NULL; - - spin_lock(&tcon->open_file_lock); - list_for_each_entry(cfile, &tcon->openFileList, tlist) { - struct cifsInodeInfo *cinode; - const char *full_path = build_path_from_dentry(cfile->dentry, page); - if (IS_ERR(full_path)) { - spin_unlock(&tcon->open_file_lock); - free_dentry_path(page); - return PTR_ERR(full_path); - } - if (strcmp(full_path, name)) - continue; - - cinode = CIFS_I(d_inode(cfile->dentry)); - spin_unlock(&tcon->open_file_lock); - free_dentry_path(page); - return cifs_get_writable_file(cinode, flags, ret_file); - } - - spin_unlock(&tcon->open_file_lock); - free_dentry_path(page); - return -ENOENT; -} - -int -cifs_get_readable_path(struct cifs_tcon *tcon, const char *name, - struct cifsFileInfo **ret_file) -{ - struct cifsFileInfo *cfile; - void *page = alloc_dentry_path(); - - *ret_file = NULL; - - spin_lock(&tcon->open_file_lock); - list_for_each_entry(cfile, &tcon->openFileList, tlist) { - struct cifsInodeInfo *cinode; - const char *full_path = build_path_from_dentry(cfile->dentry, page); - if (IS_ERR(full_path)) { - spin_unlock(&tcon->open_file_lock); - free_dentry_path(page); - return PTR_ERR(full_path); - } - if (strcmp(full_path, name)) - continue; - - cinode = CIFS_I(d_inode(cfile->dentry)); - spin_unlock(&tcon->open_file_lock); - free_dentry_path(page); - *ret_file = find_readable_file(cinode, 0); - return *ret_file ? 0 : -ENOENT; - } - - spin_unlock(&tcon->open_file_lock); - free_dentry_path(page); - return -ENOENT; -} - -void -cifs_writedata_release(struct kref *refcount) -{ - struct cifs_writedata *wdata = container_of(refcount, - struct cifs_writedata, refcount); -#ifdef CONFIG_CIFS_SMB_DIRECT - if (wdata->mr) { - smbd_deregister_mr(wdata->mr); - wdata->mr = NULL; - } -#endif - - if (wdata->cfile) - cifsFileInfo_put(wdata->cfile); - - kvfree(wdata->pages); - kfree(wdata); -} - -/* - * Write failed with a retryable error. Resend the write request. It's also - * possible that the page was redirtied so re-clean the page. - */ -static void -cifs_writev_requeue(struct cifs_writedata *wdata) -{ - int i, rc = 0; - struct inode *inode = d_inode(wdata->cfile->dentry); - struct TCP_Server_Info *server; - unsigned int rest_len; - - server = tlink_tcon(wdata->cfile->tlink)->ses->server; - i = 0; - rest_len = wdata->bytes; - do { - struct cifs_writedata *wdata2; - unsigned int j, nr_pages, wsize, tailsz, cur_len; - - wsize = server->ops->wp_retry_size(inode); - if (wsize < rest_len) { - nr_pages = wsize / PAGE_SIZE; - if (!nr_pages) { - rc = -EOPNOTSUPP; - break; - } - cur_len = nr_pages * PAGE_SIZE; - tailsz = PAGE_SIZE; - } else { - nr_pages = DIV_ROUND_UP(rest_len, PAGE_SIZE); - cur_len = rest_len; - tailsz = rest_len - (nr_pages - 1) * PAGE_SIZE; - } - - wdata2 = cifs_writedata_alloc(nr_pages, cifs_writev_complete); - if (!wdata2) { - rc = -ENOMEM; - break; - } - - for (j = 0; j < nr_pages; j++) { - wdata2->pages[j] = wdata->pages[i + j]; - lock_page(wdata2->pages[j]); - clear_page_dirty_for_io(wdata2->pages[j]); - } - - wdata2->sync_mode = wdata->sync_mode; - wdata2->nr_pages = nr_pages; - wdata2->offset = page_offset(wdata2->pages[0]); - wdata2->pagesz = PAGE_SIZE; - wdata2->tailsz = tailsz; - wdata2->bytes = cur_len; - - rc = cifs_get_writable_file(CIFS_I(inode), FIND_WR_ANY, - &wdata2->cfile); - if (!wdata2->cfile) { - cifs_dbg(VFS, "No writable handle to retry writepages rc=%d\n", - rc); - if (!is_retryable_error(rc)) - rc = -EBADF; - } else { - wdata2->pid = wdata2->cfile->pid; - rc = server->ops->async_writev(wdata2, - cifs_writedata_release); - } - - for (j = 0; j < nr_pages; j++) { - unlock_page(wdata2->pages[j]); - if (rc != 0 && !is_retryable_error(rc)) { - SetPageError(wdata2->pages[j]); - end_page_writeback(wdata2->pages[j]); - put_page(wdata2->pages[j]); - } - } - - kref_put(&wdata2->refcount, cifs_writedata_release); - if (rc) { - if (is_retryable_error(rc)) - continue; - i += nr_pages; - break; - } - - rest_len -= cur_len; - i += nr_pages; - } while (i < wdata->nr_pages); - - /* cleanup remaining pages from the original wdata */ - for (; i < wdata->nr_pages; i++) { - SetPageError(wdata->pages[i]); - end_page_writeback(wdata->pages[i]); - put_page(wdata->pages[i]); - } - - if (rc != 0 && !is_retryable_error(rc)) - mapping_set_error(inode->i_mapping, rc); - kref_put(&wdata->refcount, cifs_writedata_release); -} - -void -cifs_writev_complete(struct work_struct *work) -{ - struct cifs_writedata *wdata = container_of(work, - struct cifs_writedata, work); - struct inode *inode = d_inode(wdata->cfile->dentry); - int i = 0; - - if (wdata->result == 0) { - spin_lock(&inode->i_lock); - cifs_update_eof(CIFS_I(inode), wdata->offset, wdata->bytes); - spin_unlock(&inode->i_lock); - cifs_stats_bytes_written(tlink_tcon(wdata->cfile->tlink), - wdata->bytes); - } else if (wdata->sync_mode == WB_SYNC_ALL && wdata->result == -EAGAIN) - return cifs_writev_requeue(wdata); - - for (i = 0; i < wdata->nr_pages; i++) { - struct page *page = wdata->pages[i]; - - if (wdata->result == -EAGAIN) - __set_page_dirty_nobuffers(page); - else if (wdata->result < 0) - SetPageError(page); - end_page_writeback(page); - cifs_readpage_to_fscache(inode, page); - put_page(page); - } - if (wdata->result != -EAGAIN) - mapping_set_error(inode->i_mapping, wdata->result); - kref_put(&wdata->refcount, cifs_writedata_release); -} - -struct cifs_writedata * -cifs_writedata_alloc(unsigned int nr_pages, work_func_t complete) -{ - struct cifs_writedata *writedata = NULL; - struct page **pages = - kcalloc(nr_pages, sizeof(struct page *), GFP_NOFS); - if (pages) { - writedata = cifs_writedata_direct_alloc(pages, complete); - if (!writedata) - kvfree(pages); - } - - return writedata; -} - -struct cifs_writedata * -cifs_writedata_direct_alloc(struct page **pages, work_func_t complete) -{ - struct cifs_writedata *wdata; - - wdata = kzalloc(sizeof(*wdata), GFP_NOFS); - if (wdata != NULL) { - wdata->pages = pages; - kref_init(&wdata->refcount); - INIT_LIST_HEAD(&wdata->list); - init_completion(&wdata->done); - INIT_WORK(&wdata->work, complete); - } - return wdata; -} - - -static int cifs_partialpagewrite(struct page *page, unsigned from, unsigned to) -{ - struct address_space *mapping = page->mapping; - loff_t offset = (loff_t)page->index << PAGE_SHIFT; - char *write_data; - int rc = -EFAULT; - int bytes_written = 0; - struct inode *inode; - struct cifsFileInfo *open_file; - - if (!mapping || !mapping->host) - return -EFAULT; - - inode = page->mapping->host; - - offset += (loff_t)from; - write_data = kmap(page); - write_data += from; - - if ((to > PAGE_SIZE) || (from > to)) { - kunmap(page); - return -EIO; - } - - /* racing with truncate? */ - if (offset > mapping->host->i_size) { - kunmap(page); - return 0; /* don't care */ - } - - /* check to make sure that we are not extending the file */ - if (mapping->host->i_size - offset < (loff_t)to) - to = (unsigned)(mapping->host->i_size - offset); - - rc = cifs_get_writable_file(CIFS_I(mapping->host), FIND_WR_ANY, - &open_file); - if (!rc) { - bytes_written = cifs_write(open_file, open_file->pid, - write_data, to - from, &offset); - cifsFileInfo_put(open_file); - /* Does mm or vfs already set times? */ - inode->i_atime = inode->i_mtime = current_time(inode); - if ((bytes_written > 0) && (offset)) - rc = 0; - else if (bytes_written < 0) - rc = bytes_written; - else - rc = -EFAULT; - } else { - cifs_dbg(FYI, "No writable handle for write page rc=%d\n", rc); - if (!is_retryable_error(rc)) - rc = -EIO; - } - - kunmap(page); - return rc; -} - -static struct cifs_writedata * -wdata_alloc_and_fillpages(pgoff_t tofind, struct address_space *mapping, - pgoff_t end, pgoff_t *index, - unsigned int *found_pages) -{ - struct cifs_writedata *wdata; - - wdata = cifs_writedata_alloc((unsigned int)tofind, - cifs_writev_complete); - if (!wdata) - return NULL; - - *found_pages = find_get_pages_range_tag(mapping, index, end, - PAGECACHE_TAG_DIRTY, tofind, wdata->pages); - return wdata; -} - -static unsigned int -wdata_prepare_pages(struct cifs_writedata *wdata, unsigned int found_pages, - struct address_space *mapping, - struct writeback_control *wbc, - pgoff_t end, pgoff_t *index, pgoff_t *next, bool *done) -{ - unsigned int nr_pages = 0, i; - struct page *page; - - for (i = 0; i < found_pages; i++) { - page = wdata->pages[i]; - /* - * At this point we hold neither the i_pages lock nor the - * page lock: the page may be truncated or invalidated - * (changing page->mapping to NULL), or even swizzled - * back from swapper_space to tmpfs file mapping - */ - - if (nr_pages == 0) - lock_page(page); - else if (!trylock_page(page)) - break; - - if (unlikely(page->mapping != mapping)) { - unlock_page(page); - break; - } - - if (!wbc->range_cyclic && page->index > end) { - *done = true; - 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) || - !clear_page_dirty_for_io(page)) { - unlock_page(page); - break; - } - - /* - * This actually clears the dirty bit in the radix tree. - * See cifs_writepage() for more commentary. - */ - set_page_writeback(page); - if (page_offset(page) >= i_size_read(mapping->host)) { - *done = true; - unlock_page(page); - end_page_writeback(page); - break; - } - - wdata->pages[i] = page; - *next = page->index + 1; - ++nr_pages; - } - - /* reset index to refind any pages skipped */ - if (nr_pages == 0) - *index = wdata->pages[0]->index + 1; - - /* put any pages we aren't going to use */ - for (i = nr_pages; i < found_pages; i++) { - put_page(wdata->pages[i]); - wdata->pages[i] = NULL; - } - - return nr_pages; -} - -static int -wdata_send_pages(struct cifs_writedata *wdata, unsigned int nr_pages, - struct address_space *mapping, struct writeback_control *wbc) -{ - int rc; - - wdata->sync_mode = wbc->sync_mode; - wdata->nr_pages = nr_pages; - wdata->offset = page_offset(wdata->pages[0]); - wdata->pagesz = PAGE_SIZE; - wdata->tailsz = min(i_size_read(mapping->host) - - page_offset(wdata->pages[nr_pages - 1]), - (loff_t)PAGE_SIZE); - wdata->bytes = ((nr_pages - 1) * PAGE_SIZE) + wdata->tailsz; - wdata->pid = wdata->cfile->pid; - - rc = adjust_credits(wdata->server, &wdata->credits, wdata->bytes); - if (rc) - return rc; - - if (wdata->cfile->invalidHandle) - rc = -EAGAIN; - else - rc = wdata->server->ops->async_writev(wdata, - cifs_writedata_release); - - return rc; -} - -static int cifs_writepages(struct address_space *mapping, - struct writeback_control *wbc) -{ - struct inode *inode = mapping->host; - struct cifs_sb_info *cifs_sb = CIFS_SB(inode->i_sb); - struct TCP_Server_Info *server; - bool done = false, scanned = false, range_whole = false; - pgoff_t end, index; - struct cifs_writedata *wdata; - struct cifsFileInfo *cfile = NULL; - int rc = 0; - int saved_rc = 0; - unsigned int xid; - - /* - * If wsize is smaller than the page cache size, default to writing - * one page at a time via cifs_writepage - */ - if (cifs_sb->ctx->wsize < PAGE_SIZE) - return generic_writepages(mapping, wbc); - - xid = get_xid(); - if (wbc->range_cyclic) { - index = mapping->writeback_index; /* Start from prev offset */ - end = -1; - } else { - index = wbc->range_start >> PAGE_SHIFT; - end = wbc->range_end >> PAGE_SHIFT; - if (wbc->range_start == 0 && wbc->range_end == LLONG_MAX) - range_whole = true; - scanned = true; - } - server = cifs_pick_channel(cifs_sb_master_tcon(cifs_sb)->ses); - -retry: - while (!done && index <= end) { - unsigned int i, nr_pages, found_pages, wsize; - pgoff_t next = 0, tofind, saved_index = index; - struct cifs_credits credits_on_stack; - struct cifs_credits *credits = &credits_on_stack; - int get_file_rc = 0; - - if (cfile) - cifsFileInfo_put(cfile); - - rc = cifs_get_writable_file(CIFS_I(inode), FIND_WR_ANY, &cfile); - - /* in case of an error store it to return later */ - if (rc) - get_file_rc = rc; - - rc = server->ops->wait_mtu_credits(server, cifs_sb->ctx->wsize, - &wsize, credits); - if (rc != 0) { - done = true; - break; - } - - tofind = min((wsize / PAGE_SIZE) - 1, end - index) + 1; - - wdata = wdata_alloc_and_fillpages(tofind, mapping, end, &index, - &found_pages); - if (!wdata) { - rc = -ENOMEM; - done = true; - add_credits_and_wake_if(server, credits, 0); - break; - } - - if (found_pages == 0) { - kref_put(&wdata->refcount, cifs_writedata_release); - add_credits_and_wake_if(server, credits, 0); - break; - } - - nr_pages = wdata_prepare_pages(wdata, found_pages, mapping, wbc, - end, &index, &next, &done); - - /* nothing to write? */ - if (nr_pages == 0) { - kref_put(&wdata->refcount, cifs_writedata_release); - add_credits_and_wake_if(server, credits, 0); - continue; - } - - wdata->credits = credits_on_stack; - wdata->cfile = cfile; - wdata->server = server; - cfile = NULL; - - if (!wdata->cfile) { - cifs_dbg(VFS, "No writable handle in writepages rc=%d\n", - get_file_rc); - if (is_retryable_error(get_file_rc)) - rc = get_file_rc; - else - rc = -EBADF; - } else - rc = wdata_send_pages(wdata, nr_pages, mapping, wbc); - - for (i = 0; i < nr_pages; ++i) - unlock_page(wdata->pages[i]); - - /* send failure -- clean up the mess */ - if (rc != 0) { - add_credits_and_wake_if(server, &wdata->credits, 0); - for (i = 0; i < nr_pages; ++i) { - if (is_retryable_error(rc)) - redirty_page_for_writepage(wbc, - wdata->pages[i]); - else - SetPageError(wdata->pages[i]); - end_page_writeback(wdata->pages[i]); - put_page(wdata->pages[i]); - } - if (!is_retryable_error(rc)) - mapping_set_error(mapping, rc); - } - kref_put(&wdata->refcount, cifs_writedata_release); - - if (wbc->sync_mode == WB_SYNC_ALL && rc == -EAGAIN) { - index = saved_index; - continue; - } - - /* Return immediately if we received a signal during writing */ - if (is_interrupt_error(rc)) { - done = true; - break; - } - - if (rc != 0 && saved_rc == 0) - saved_rc = rc; - - wbc->nr_to_write -= nr_pages; - if (wbc->nr_to_write <= 0) - done = true; - - index = next; - } - - 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 = true; - index = 0; - goto retry; - } - - if (saved_rc != 0) - rc = saved_rc; - - if (wbc->range_cyclic || (range_whole && wbc->nr_to_write > 0)) - mapping->writeback_index = index; - - if (cfile) - cifsFileInfo_put(cfile); - free_xid(xid); - /* Indication to update ctime and mtime as close is deferred */ - set_bit(CIFS_INO_MODIFIED_ATTR, &CIFS_I(inode)->flags); - return rc; -} - -static int -cifs_writepage_locked(struct page *page, struct writeback_control *wbc) -{ - int rc; - unsigned int xid; - - xid = get_xid(); -/* BB add check for wbc flags */ - get_page(page); - if (!PageUptodate(page)) - cifs_dbg(FYI, "ppw - page not up to date\n"); - - /* - * Set the "writeback" flag, and clear "dirty" in the radix tree. - * - * A writepage() implementation always needs to do either this, - * or re-dirty the page with "redirty_page_for_writepage()" in - * the case of a failure. - * - * Just unlocking the page will cause the radix tree tag-bits - * to fail to update with the state of the page correctly. - */ - set_page_writeback(page); -retry_write: - rc = cifs_partialpagewrite(page, 0, PAGE_SIZE); - if (is_retryable_error(rc)) { - if (wbc->sync_mode == WB_SYNC_ALL && rc == -EAGAIN) - goto retry_write; - redirty_page_for_writepage(wbc, page); - } else if (rc != 0) { - SetPageError(page); - mapping_set_error(page->mapping, rc); - } else { - SetPageUptodate(page); - } - end_page_writeback(page); - put_page(page); - free_xid(xid); - return rc; -} - -static int cifs_writepage(struct page *page, struct writeback_control *wbc) -{ - int rc = cifs_writepage_locked(page, wbc); - unlock_page(page); - return rc; -} - -static int cifs_write_end(struct file *file, struct address_space *mapping, - loff_t pos, unsigned len, unsigned copied, - struct page *page, void *fsdata) -{ - int rc; - struct inode *inode = mapping->host; - struct cifsFileInfo *cfile = file->private_data; - struct cifs_sb_info *cifs_sb = CIFS_SB(cfile->dentry->d_sb); - __u32 pid; - - if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_RWPIDFORWARD) - pid = cfile->pid; - else - pid = current->tgid; - - cifs_dbg(FYI, "write_end for page %p from pos %lld with %d bytes\n", - page, pos, copied); - - if (PageChecked(page)) { - if (copied == len) - SetPageUptodate(page); - ClearPageChecked(page); - } else if (!PageUptodate(page) && copied == PAGE_SIZE) - SetPageUptodate(page); - - if (!PageUptodate(page)) { - char *page_data; - unsigned offset = pos & (PAGE_SIZE - 1); - unsigned int xid; - - xid = get_xid(); - /* this is probably better than directly calling - partialpage_write since in this function the file handle is - known which we might as well leverage */ - /* BB check if anything else missing out of ppw - such as updating last write time */ - page_data = kmap(page); - rc = cifs_write(cfile, pid, page_data + offset, copied, &pos); - /* if (rc < 0) should we set writebehind rc? */ - kunmap(page); - - free_xid(xid); - } else { - rc = copied; - pos += copied; - set_page_dirty(page); - } - - if (rc > 0) { - spin_lock(&inode->i_lock); - if (pos > inode->i_size) { - i_size_write(inode, pos); - inode->i_blocks = (512 - 1 + pos) >> 9; - } - spin_unlock(&inode->i_lock); - } - - unlock_page(page); - put_page(page); - /* Indication to update ctime and mtime as close is deferred */ - set_bit(CIFS_INO_MODIFIED_ATTR, &CIFS_I(inode)->flags); - - return rc; -} - -int cifs_strict_fsync(struct file *file, loff_t start, loff_t end, - int datasync) -{ - unsigned int xid; - int rc = 0; - struct cifs_tcon *tcon; - struct TCP_Server_Info *server; - struct cifsFileInfo *smbfile = file->private_data; - struct inode *inode = file_inode(file); - struct cifs_sb_info *cifs_sb = CIFS_SB(inode->i_sb); - - rc = file_write_and_wait_range(file, start, end); - if (rc) { - trace_cifs_fsync_err(inode->i_ino, rc); - return rc; - } - - xid = get_xid(); - - cifs_dbg(FYI, "Sync file - name: %pD datasync: 0x%x\n", - file, datasync); - - if (!CIFS_CACHE_READ(CIFS_I(inode))) { - rc = cifs_zap_mapping(inode); - if (rc) { - cifs_dbg(FYI, "rc: %d during invalidate phase\n", rc); - rc = 0; /* don't care about it in fsync */ - } - } - - tcon = tlink_tcon(smbfile->tlink); - if (!(cifs_sb->mnt_cifs_flags & CIFS_MOUNT_NOSSYNC)) { - server = tcon->ses->server; - if (server->ops->flush == NULL) { - rc = -ENOSYS; - goto strict_fsync_exit; - } - - if ((OPEN_FMODE(smbfile->f_flags) & FMODE_WRITE) == 0) { - smbfile = find_writable_file(CIFS_I(inode), FIND_WR_ANY); - if (smbfile) { - rc = server->ops->flush(xid, tcon, &smbfile->fid); - cifsFileInfo_put(smbfile); - } else - cifs_dbg(FYI, "ignore fsync for file not open for write\n"); - } else - rc = server->ops->flush(xid, tcon, &smbfile->fid); - } - -strict_fsync_exit: - free_xid(xid); - return rc; -} - -int cifs_fsync(struct file *file, loff_t start, loff_t end, int datasync) -{ - unsigned int xid; - int rc = 0; - struct cifs_tcon *tcon; - struct TCP_Server_Info *server; - struct cifsFileInfo *smbfile = file->private_data; - struct inode *inode = file_inode(file); - struct cifs_sb_info *cifs_sb = CIFS_FILE_SB(file); - - rc = file_write_and_wait_range(file, start, end); - if (rc) { - trace_cifs_fsync_err(file_inode(file)->i_ino, rc); - return rc; - } - - xid = get_xid(); - - cifs_dbg(FYI, "Sync file - name: %pD datasync: 0x%x\n", - file, datasync); - - tcon = tlink_tcon(smbfile->tlink); - if (!(cifs_sb->mnt_cifs_flags & CIFS_MOUNT_NOSSYNC)) { - server = tcon->ses->server; - if (server->ops->flush == NULL) { - rc = -ENOSYS; - goto fsync_exit; - } - - if ((OPEN_FMODE(smbfile->f_flags) & FMODE_WRITE) == 0) { - smbfile = find_writable_file(CIFS_I(inode), FIND_WR_ANY); - if (smbfile) { - rc = server->ops->flush(xid, tcon, &smbfile->fid); - cifsFileInfo_put(smbfile); - } else - cifs_dbg(FYI, "ignore fsync for file not open for write\n"); - } else - rc = server->ops->flush(xid, tcon, &smbfile->fid); - } - -fsync_exit: - free_xid(xid); - return rc; -} - -/* - * As file closes, flush all cached write data for this inode checking - * for write behind errors. - */ -int cifs_flush(struct file *file, fl_owner_t id) -{ - struct inode *inode = file_inode(file); - int rc = 0; - - if (file->f_mode & FMODE_WRITE) - rc = filemap_write_and_wait(inode->i_mapping); - - cifs_dbg(FYI, "Flush inode %p file %p rc %d\n", inode, file, rc); - if (rc) { - /* get more nuanced writeback errors */ - rc = filemap_check_wb_err(file->f_mapping, 0); - trace_cifs_flush_err(inode->i_ino, rc); - } - return rc; -} - -static int -cifs_write_allocate_pages(struct page **pages, unsigned long num_pages) -{ - int rc = 0; - unsigned long i; - - for (i = 0; i < num_pages; i++) { - pages[i] = alloc_page(GFP_KERNEL|__GFP_HIGHMEM); - if (!pages[i]) { - /* - * save number of pages we have already allocated and - * return with ENOMEM error - */ - num_pages = i; - rc = -ENOMEM; - break; - } - } - - if (rc) { - for (i = 0; i < num_pages; i++) - put_page(pages[i]); - } - return rc; -} - -static inline -size_t get_numpages(const size_t wsize, const size_t len, size_t *cur_len) -{ - size_t num_pages; - size_t clen; - - clen = min_t(const size_t, len, wsize); - num_pages = DIV_ROUND_UP(clen, PAGE_SIZE); - - if (cur_len) - *cur_len = clen; - - return num_pages; -} - -static void -cifs_uncached_writedata_release(struct kref *refcount) -{ - int i; - struct cifs_writedata *wdata = container_of(refcount, - struct cifs_writedata, refcount); - - kref_put(&wdata->ctx->refcount, cifs_aio_ctx_release); - for (i = 0; i < wdata->nr_pages; i++) - put_page(wdata->pages[i]); - cifs_writedata_release(refcount); -} - -static void collect_uncached_write_data(struct cifs_aio_ctx *ctx); - -static void -cifs_uncached_writev_complete(struct work_struct *work) -{ - struct cifs_writedata *wdata = container_of(work, - struct cifs_writedata, work); - struct inode *inode = d_inode(wdata->cfile->dentry); - struct cifsInodeInfo *cifsi = CIFS_I(inode); - - spin_lock(&inode->i_lock); - cifs_update_eof(cifsi, wdata->offset, wdata->bytes); - if (cifsi->server_eof > inode->i_size) - i_size_write(inode, cifsi->server_eof); - spin_unlock(&inode->i_lock); - - complete(&wdata->done); - collect_uncached_write_data(wdata->ctx); - /* the below call can possibly free the last ref to aio ctx */ - kref_put(&wdata->refcount, cifs_uncached_writedata_release); -} - -static int -wdata_fill_from_iovec(struct cifs_writedata *wdata, struct iov_iter *from, - size_t *len, unsigned long *num_pages) -{ - size_t save_len, copied, bytes, cur_len = *len; - unsigned long i, nr_pages = *num_pages; - - save_len = cur_len; - for (i = 0; i < nr_pages; i++) { - bytes = min_t(const size_t, cur_len, PAGE_SIZE); - copied = copy_page_from_iter(wdata->pages[i], 0, bytes, from); - cur_len -= copied; - /* - * If we didn't copy as much as we expected, then that - * may mean we trod into an unmapped area. Stop copying - * at that point. On the next pass through the big - * loop, we'll likely end up getting a zero-length - * write and bailing out of it. - */ - if (copied < bytes) - break; - } - cur_len = save_len - cur_len; - *len = cur_len; - - /* - * If we have no data to send, then that probably means that - * the copy above failed altogether. That's most likely because - * the address in the iovec was bogus. Return -EFAULT and let - * the caller free anything we allocated and bail out. - */ - if (!cur_len) - return -EFAULT; - - /* - * i + 1 now represents the number of pages we actually used in - * the copy phase above. - */ - *num_pages = i + 1; - return 0; -} - -static int -cifs_resend_wdata(struct cifs_writedata *wdata, struct list_head *wdata_list, - struct cifs_aio_ctx *ctx) -{ - unsigned int wsize; - struct cifs_credits credits; - int rc; - struct TCP_Server_Info *server = wdata->server; - - do { - if (wdata->cfile->invalidHandle) { - rc = cifs_reopen_file(wdata->cfile, false); - if (rc == -EAGAIN) - continue; - else if (rc) - break; - } - - - /* - * Wait for credits to resend this wdata. - * Note: we are attempting to resend the whole wdata not in - * segments - */ - do { - rc = server->ops->wait_mtu_credits(server, wdata->bytes, - &wsize, &credits); - if (rc) - goto fail; - - if (wsize < wdata->bytes) { - add_credits_and_wake_if(server, &credits, 0); - msleep(1000); - } - } while (wsize < wdata->bytes); - wdata->credits = credits; - - rc = adjust_credits(server, &wdata->credits, wdata->bytes); - - if (!rc) { - if (wdata->cfile->invalidHandle) - rc = -EAGAIN; - else { -#ifdef CONFIG_CIFS_SMB_DIRECT - if (wdata->mr) { - wdata->mr->need_invalidate = true; - smbd_deregister_mr(wdata->mr); - wdata->mr = NULL; - } -#endif - rc = server->ops->async_writev(wdata, - cifs_uncached_writedata_release); - } - } - - /* If the write was successfully sent, we are done */ - if (!rc) { - list_add_tail(&wdata->list, wdata_list); - return 0; - } - - /* Roll back credits and retry if needed */ - add_credits_and_wake_if(server, &wdata->credits, 0); - } while (rc == -EAGAIN); - -fail: - kref_put(&wdata->refcount, cifs_uncached_writedata_release); - return rc; -} - -static int -cifs_write_from_iter(loff_t offset, size_t len, struct iov_iter *from, - struct cifsFileInfo *open_file, - struct cifs_sb_info *cifs_sb, struct list_head *wdata_list, - struct cifs_aio_ctx *ctx) -{ - int rc = 0; - size_t cur_len; - unsigned long nr_pages, num_pages, i; - struct cifs_writedata *wdata; - struct iov_iter saved_from = *from; - loff_t saved_offset = offset; - pid_t pid; - struct TCP_Server_Info *server; - struct page **pagevec; - size_t start; - unsigned int xid; - - if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_RWPIDFORWARD) - pid = open_file->pid; - else - pid = current->tgid; - - server = cifs_pick_channel(tlink_tcon(open_file->tlink)->ses); - xid = get_xid(); - - do { - unsigned int wsize; - struct cifs_credits credits_on_stack; - struct cifs_credits *credits = &credits_on_stack; - - if (open_file->invalidHandle) { - rc = cifs_reopen_file(open_file, false); - if (rc == -EAGAIN) - continue; - else if (rc) - break; - } - - rc = server->ops->wait_mtu_credits(server, cifs_sb->ctx->wsize, - &wsize, credits); - if (rc) - break; - - cur_len = min_t(const size_t, len, wsize); - - if (ctx->direct_io) { - ssize_t result; - - result = iov_iter_get_pages_alloc2( - from, &pagevec, cur_len, &start); - if (result < 0) { - cifs_dbg(VFS, - "direct_writev couldn't get user pages (rc=%zd) iter type %d iov_offset %zd count %zd\n", - result, iov_iter_type(from), - from->iov_offset, from->count); - dump_stack(); - - rc = result; - add_credits_and_wake_if(server, credits, 0); - break; - } - cur_len = (size_t)result; - - nr_pages = - (cur_len + start + PAGE_SIZE - 1) / PAGE_SIZE; - - wdata = cifs_writedata_direct_alloc(pagevec, - cifs_uncached_writev_complete); - if (!wdata) { - rc = -ENOMEM; - for (i = 0; i < nr_pages; i++) - put_page(pagevec[i]); - kvfree(pagevec); - add_credits_and_wake_if(server, credits, 0); - break; - } - - - wdata->page_offset = start; - wdata->tailsz = - nr_pages > 1 ? - cur_len - (PAGE_SIZE - start) - - (nr_pages - 2) * PAGE_SIZE : - cur_len; - } else { - nr_pages = get_numpages(wsize, len, &cur_len); - wdata = cifs_writedata_alloc(nr_pages, - cifs_uncached_writev_complete); - if (!wdata) { - rc = -ENOMEM; - add_credits_and_wake_if(server, credits, 0); - break; - } - - rc = cifs_write_allocate_pages(wdata->pages, nr_pages); - if (rc) { - kvfree(wdata->pages); - kfree(wdata); - add_credits_and_wake_if(server, credits, 0); - break; - } - - num_pages = nr_pages; - rc = wdata_fill_from_iovec( - wdata, from, &cur_len, &num_pages); - if (rc) { - for (i = 0; i < nr_pages; i++) - put_page(wdata->pages[i]); - kvfree(wdata->pages); - kfree(wdata); - add_credits_and_wake_if(server, credits, 0); - break; - } - - /* - * Bring nr_pages down to the number of pages we - * actually used, and free any pages that we didn't use. - */ - for ( ; nr_pages > num_pages; nr_pages--) - put_page(wdata->pages[nr_pages - 1]); - - wdata->tailsz = cur_len - ((nr_pages - 1) * PAGE_SIZE); - } - - wdata->sync_mode = WB_SYNC_ALL; - wdata->nr_pages = nr_pages; - wdata->offset = (__u64)offset; - wdata->cfile = cifsFileInfo_get(open_file); - wdata->server = server; - wdata->pid = pid; - wdata->bytes = cur_len; - wdata->pagesz = PAGE_SIZE; - wdata->credits = credits_on_stack; - wdata->ctx = ctx; - kref_get(&ctx->refcount); - - rc = adjust_credits(server, &wdata->credits, wdata->bytes); - - if (!rc) { - if (wdata->cfile->invalidHandle) - rc = -EAGAIN; - else - rc = server->ops->async_writev(wdata, - cifs_uncached_writedata_release); - } - - if (rc) { - add_credits_and_wake_if(server, &wdata->credits, 0); - kref_put(&wdata->refcount, - cifs_uncached_writedata_release); - if (rc == -EAGAIN) { - *from = saved_from; - iov_iter_advance(from, offset - saved_offset); - continue; - } - break; - } - - list_add_tail(&wdata->list, wdata_list); - offset += cur_len; - len -= cur_len; - } while (len > 0); - - free_xid(xid); - return rc; -} - -static void collect_uncached_write_data(struct cifs_aio_ctx *ctx) -{ - struct cifs_writedata *wdata, *tmp; - struct cifs_tcon *tcon; - struct cifs_sb_info *cifs_sb; - struct dentry *dentry = ctx->cfile->dentry; - ssize_t rc; - - tcon = tlink_tcon(ctx->cfile->tlink); - cifs_sb = CIFS_SB(dentry->d_sb); - - mutex_lock(&ctx->aio_mutex); - - if (list_empty(&ctx->list)) { - mutex_unlock(&ctx->aio_mutex); - return; - } - - rc = ctx->rc; - /* - * Wait for and collect replies for any successful sends in order of - * increasing offset. Once an error is hit, then return without waiting - * for any more replies. - */ -restart_loop: - list_for_each_entry_safe(wdata, tmp, &ctx->list, list) { - if (!rc) { - if (!try_wait_for_completion(&wdata->done)) { - mutex_unlock(&ctx->aio_mutex); - return; - } - - if (wdata->result) - rc = wdata->result; - else - ctx->total_len += wdata->bytes; - - /* resend call if it's a retryable error */ - if (rc == -EAGAIN) { - struct list_head tmp_list; - struct iov_iter tmp_from = ctx->iter; - - INIT_LIST_HEAD(&tmp_list); - list_del_init(&wdata->list); - - if (ctx->direct_io) - rc = cifs_resend_wdata( - wdata, &tmp_list, ctx); - else { - iov_iter_advance(&tmp_from, - wdata->offset - ctx->pos); - - rc = cifs_write_from_iter(wdata->offset, - wdata->bytes, &tmp_from, - ctx->cfile, cifs_sb, &tmp_list, - ctx); - - kref_put(&wdata->refcount, - cifs_uncached_writedata_release); - } - - list_splice(&tmp_list, &ctx->list); - goto restart_loop; - } - } - list_del_init(&wdata->list); - kref_put(&wdata->refcount, cifs_uncached_writedata_release); - } - - cifs_stats_bytes_written(tcon, ctx->total_len); - set_bit(CIFS_INO_INVALID_MAPPING, &CIFS_I(dentry->d_inode)->flags); - - ctx->rc = (rc == 0) ? ctx->total_len : rc; - - mutex_unlock(&ctx->aio_mutex); - - if (ctx->iocb && ctx->iocb->ki_complete) - ctx->iocb->ki_complete(ctx->iocb, ctx->rc); - else - complete(&ctx->done); -} - -static ssize_t __cifs_writev( - struct kiocb *iocb, struct iov_iter *from, bool direct) -{ - struct file *file = iocb->ki_filp; - ssize_t total_written = 0; - struct cifsFileInfo *cfile; - struct cifs_tcon *tcon; - struct cifs_sb_info *cifs_sb; - struct cifs_aio_ctx *ctx; - struct iov_iter saved_from = *from; - size_t len = iov_iter_count(from); - int rc; - - /* - * iov_iter_get_pages_alloc doesn't work with ITER_KVEC. - * In this case, fall back to non-direct write function. - * this could be improved by getting pages directly in ITER_KVEC - */ - if (direct && iov_iter_is_kvec(from)) { - cifs_dbg(FYI, "use non-direct cifs_writev for kvec I/O\n"); - direct = false; - } - - rc = generic_write_checks(iocb, from); - if (rc <= 0) - return rc; - - cifs_sb = CIFS_FILE_SB(file); - cfile = file->private_data; - tcon = tlink_tcon(cfile->tlink); - - if (!tcon->ses->server->ops->async_writev) - return -ENOSYS; - - ctx = cifs_aio_ctx_alloc(); - if (!ctx) - return -ENOMEM; - - ctx->cfile = cifsFileInfo_get(cfile); - - if (!is_sync_kiocb(iocb)) - ctx->iocb = iocb; - - ctx->pos = iocb->ki_pos; - - if (direct) { - ctx->direct_io = true; - ctx->iter = *from; - ctx->len = len; - } else { - rc = setup_aio_ctx_iter(ctx, from, ITER_SOURCE); - if (rc) { - kref_put(&ctx->refcount, cifs_aio_ctx_release); - return rc; - } - } - - /* grab a lock here due to read response handlers can access ctx */ - mutex_lock(&ctx->aio_mutex); - - rc = cifs_write_from_iter(iocb->ki_pos, ctx->len, &saved_from, - cfile, cifs_sb, &ctx->list, ctx); - - /* - * If at least one write was successfully sent, then discard any rc - * value from the later writes. If the other write succeeds, then - * we'll end up returning whatever was written. If it fails, then - * we'll get a new rc value from that. - */ - if (!list_empty(&ctx->list)) - rc = 0; - - mutex_unlock(&ctx->aio_mutex); - - if (rc) { - kref_put(&ctx->refcount, cifs_aio_ctx_release); - return rc; - } - - if (!is_sync_kiocb(iocb)) { - kref_put(&ctx->refcount, cifs_aio_ctx_release); - return -EIOCBQUEUED; - } - - rc = wait_for_completion_killable(&ctx->done); - if (rc) { - mutex_lock(&ctx->aio_mutex); - ctx->rc = rc = -EINTR; - total_written = ctx->total_len; - mutex_unlock(&ctx->aio_mutex); - } else { - rc = ctx->rc; - total_written = ctx->total_len; - } - - kref_put(&ctx->refcount, cifs_aio_ctx_release); - - if (unlikely(!total_written)) - return rc; - - iocb->ki_pos += total_written; - return total_written; -} - -ssize_t cifs_direct_writev(struct kiocb *iocb, struct iov_iter *from) -{ - struct file *file = iocb->ki_filp; - - cifs_revalidate_mapping(file->f_inode); - return __cifs_writev(iocb, from, true); -} - -ssize_t cifs_user_writev(struct kiocb *iocb, struct iov_iter *from) -{ - return __cifs_writev(iocb, from, false); -} - -static ssize_t -cifs_writev(struct kiocb *iocb, struct iov_iter *from) -{ - struct file *file = iocb->ki_filp; - struct cifsFileInfo *cfile = (struct cifsFileInfo *)file->private_data; - struct inode *inode = file->f_mapping->host; - struct cifsInodeInfo *cinode = CIFS_I(inode); - struct TCP_Server_Info *server = tlink_tcon(cfile->tlink)->ses->server; - ssize_t rc; - - inode_lock(inode); - /* - * We need to hold the sem to be sure nobody modifies lock list - * with a brlock that prevents writing. - */ - down_read(&cinode->lock_sem); - - rc = generic_write_checks(iocb, from); - if (rc <= 0) - goto out; - - if (!cifs_find_lock_conflict(cfile, iocb->ki_pos, iov_iter_count(from), - server->vals->exclusive_lock_type, 0, - NULL, CIFS_WRITE_OP)) - rc = __generic_file_write_iter(iocb, from); - else - rc = -EACCES; -out: - up_read(&cinode->lock_sem); - inode_unlock(inode); - - if (rc > 0) - rc = generic_write_sync(iocb, rc); - return rc; -} - -ssize_t -cifs_strict_writev(struct kiocb *iocb, struct iov_iter *from) -{ - struct inode *inode = file_inode(iocb->ki_filp); - struct cifsInodeInfo *cinode = CIFS_I(inode); - struct cifs_sb_info *cifs_sb = CIFS_SB(inode->i_sb); - struct cifsFileInfo *cfile = (struct cifsFileInfo *) - iocb->ki_filp->private_data; - struct cifs_tcon *tcon = tlink_tcon(cfile->tlink); - ssize_t written; - - written = cifs_get_writer(cinode); - if (written) - return written; - - if (CIFS_CACHE_WRITE(cinode)) { - if (cap_unix(tcon->ses) && - (CIFS_UNIX_FCNTL_CAP & le64_to_cpu(tcon->fsUnixInfo.Capability)) - && ((cifs_sb->mnt_cifs_flags & CIFS_MOUNT_NOPOSIXBRL) == 0)) { - written = generic_file_write_iter(iocb, from); - goto out; - } - written = cifs_writev(iocb, from); - goto out; - } - /* - * For non-oplocked files in strict cache mode we need to write the data - * to the server exactly from the pos to pos+len-1 rather than flush all - * affected pages because it may cause a error with mandatory locks on - * these pages but not on the region from pos to ppos+len-1. - */ - written = cifs_user_writev(iocb, from); - if (CIFS_CACHE_READ(cinode)) { - /* - * We have read level caching and we have just sent a write - * request to the server thus making data in the cache stale. - * Zap the cache and set oplock/lease level to NONE to avoid - * reading stale data from the cache. All subsequent read - * operations will read new data from the server. - */ - cifs_zap_mapping(inode); - cifs_dbg(FYI, "Set Oplock/Lease to NONE for inode=%p after write\n", - inode); - cinode->oplock = 0; - } -out: - cifs_put_writer(cinode); - return written; -} - -static struct cifs_readdata * -cifs_readdata_direct_alloc(struct page **pages, work_func_t complete) -{ - struct cifs_readdata *rdata; - - rdata = kzalloc(sizeof(*rdata), GFP_KERNEL); - if (rdata != NULL) { - rdata->pages = pages; - kref_init(&rdata->refcount); - INIT_LIST_HEAD(&rdata->list); - init_completion(&rdata->done); - INIT_WORK(&rdata->work, complete); - } - - return rdata; -} - -static struct cifs_readdata * -cifs_readdata_alloc(unsigned int nr_pages, work_func_t complete) -{ - struct page **pages = - kcalloc(nr_pages, sizeof(struct page *), GFP_KERNEL); - struct cifs_readdata *ret = NULL; - - if (pages) { - ret = cifs_readdata_direct_alloc(pages, complete); - if (!ret) - kfree(pages); - } - - return ret; -} - -void -cifs_readdata_release(struct kref *refcount) -{ - struct cifs_readdata *rdata = container_of(refcount, - struct cifs_readdata, refcount); -#ifdef CONFIG_CIFS_SMB_DIRECT - if (rdata->mr) { - smbd_deregister_mr(rdata->mr); - rdata->mr = NULL; - } -#endif - if (rdata->cfile) - cifsFileInfo_put(rdata->cfile); - - kvfree(rdata->pages); - kfree(rdata); -} - -static int -cifs_read_allocate_pages(struct cifs_readdata *rdata, unsigned int nr_pages) -{ - int rc = 0; - struct page *page; - unsigned int i; - - for (i = 0; i < nr_pages; i++) { - page = alloc_page(GFP_KERNEL|__GFP_HIGHMEM); - if (!page) { - rc = -ENOMEM; - break; - } - rdata->pages[i] = page; - } - - if (rc) { - unsigned int nr_page_failed = i; - - for (i = 0; i < nr_page_failed; i++) { - put_page(rdata->pages[i]); - rdata->pages[i] = NULL; - } - } - return rc; -} - -static void -cifs_uncached_readdata_release(struct kref *refcount) -{ - struct cifs_readdata *rdata = container_of(refcount, - struct cifs_readdata, refcount); - unsigned int i; - - kref_put(&rdata->ctx->refcount, cifs_aio_ctx_release); - for (i = 0; i < rdata->nr_pages; i++) { - put_page(rdata->pages[i]); - } - cifs_readdata_release(refcount); -} - -/** - * cifs_readdata_to_iov - copy data from pages in response to an iovec - * @rdata: the readdata response with list of pages holding data - * @iter: destination for our data - * - * This function copies data from a list of pages in a readdata response into - * an array of iovecs. It will first calculate where the data should go - * based on the info in the readdata and then copy the data into that spot. - */ -static int -cifs_readdata_to_iov(struct cifs_readdata *rdata, struct iov_iter *iter) -{ - size_t remaining = rdata->got_bytes; - unsigned int i; - - for (i = 0; i < rdata->nr_pages; i++) { - struct page *page = rdata->pages[i]; - size_t copy = min_t(size_t, remaining, PAGE_SIZE); - size_t written; - - if (unlikely(iov_iter_is_pipe(iter))) { - void *addr = kmap_atomic(page); - - written = copy_to_iter(addr, copy, iter); - kunmap_atomic(addr); - } else - written = copy_page_to_iter(page, 0, copy, iter); - remaining -= written; - if (written < copy && iov_iter_count(iter) > 0) - break; - } - return remaining ? -EFAULT : 0; -} - -static void collect_uncached_read_data(struct cifs_aio_ctx *ctx); - -static void -cifs_uncached_readv_complete(struct work_struct *work) -{ - struct cifs_readdata *rdata = container_of(work, - struct cifs_readdata, work); - - complete(&rdata->done); - collect_uncached_read_data(rdata->ctx); - /* the below call can possibly free the last ref to aio ctx */ - kref_put(&rdata->refcount, cifs_uncached_readdata_release); -} - -static int -uncached_fill_pages(struct TCP_Server_Info *server, - struct cifs_readdata *rdata, struct iov_iter *iter, - unsigned int len) -{ - int result = 0; - unsigned int i; - unsigned int nr_pages = rdata->nr_pages; - unsigned int page_offset = rdata->page_offset; - - rdata->got_bytes = 0; - rdata->tailsz = PAGE_SIZE; - for (i = 0; i < nr_pages; i++) { - struct page *page = rdata->pages[i]; - size_t n; - unsigned int segment_size = rdata->pagesz; - - if (i == 0) - segment_size -= page_offset; - else - page_offset = 0; - - - if (len <= 0) { - /* no need to hold page hostage */ - rdata->pages[i] = NULL; - rdata->nr_pages--; - put_page(page); - continue; - } - - n = len; - if (len >= segment_size) - /* enough data to fill the page */ - n = segment_size; - else - rdata->tailsz = len; - len -= n; - - if (iter) - result = copy_page_from_iter( - page, page_offset, n, iter); -#ifdef CONFIG_CIFS_SMB_DIRECT - else if (rdata->mr) - result = n; -#endif - else - result = cifs_read_page_from_socket( - server, page, page_offset, n); - if (result < 0) - break; - - rdata->got_bytes += result; - } - - return result != -ECONNABORTED && rdata->got_bytes > 0 ? - rdata->got_bytes : result; -} - -static int -cifs_uncached_read_into_pages(struct TCP_Server_Info *server, - struct cifs_readdata *rdata, unsigned int len) -{ - return uncached_fill_pages(server, rdata, NULL, len); -} - -static int -cifs_uncached_copy_into_pages(struct TCP_Server_Info *server, - struct cifs_readdata *rdata, - struct iov_iter *iter) -{ - return uncached_fill_pages(server, rdata, iter, iter->count); -} - -static int cifs_resend_rdata(struct cifs_readdata *rdata, - struct list_head *rdata_list, - struct cifs_aio_ctx *ctx) -{ - unsigned int rsize; - struct cifs_credits credits; - int rc; - struct TCP_Server_Info *server; - - /* XXX: should we pick a new channel here? */ - server = rdata->server; - - do { - if (rdata->cfile->invalidHandle) { - rc = cifs_reopen_file(rdata->cfile, true); - if (rc == -EAGAIN) - continue; - else if (rc) - break; - } - - /* - * Wait for credits to resend this rdata. - * Note: we are attempting to resend the whole rdata not in - * segments - */ - do { - rc = server->ops->wait_mtu_credits(server, rdata->bytes, - &rsize, &credits); - - if (rc) - goto fail; - - if (rsize < rdata->bytes) { - add_credits_and_wake_if(server, &credits, 0); - msleep(1000); - } - } while (rsize < rdata->bytes); - rdata->credits = credits; - - rc = adjust_credits(server, &rdata->credits, rdata->bytes); - if (!rc) { - if (rdata->cfile->invalidHandle) - rc = -EAGAIN; - else { -#ifdef CONFIG_CIFS_SMB_DIRECT - if (rdata->mr) { - rdata->mr->need_invalidate = true; - smbd_deregister_mr(rdata->mr); - rdata->mr = NULL; - } -#endif - rc = server->ops->async_readv(rdata); - } - } - - /* If the read was successfully sent, we are done */ - if (!rc) { - /* Add to aio pending list */ - list_add_tail(&rdata->list, rdata_list); - return 0; - } - - /* Roll back credits and retry if needed */ - add_credits_and_wake_if(server, &rdata->credits, 0); - } while (rc == -EAGAIN); - -fail: - kref_put(&rdata->refcount, cifs_uncached_readdata_release); - return rc; -} - -static int -cifs_send_async_read(loff_t offset, size_t len, struct cifsFileInfo *open_file, - struct cifs_sb_info *cifs_sb, struct list_head *rdata_list, - struct cifs_aio_ctx *ctx) -{ - struct cifs_readdata *rdata; - unsigned int npages, rsize; - struct cifs_credits credits_on_stack; - struct cifs_credits *credits = &credits_on_stack; - size_t cur_len; - int rc; - pid_t pid; - struct TCP_Server_Info *server; - struct page **pagevec; - size_t start; - struct iov_iter direct_iov = ctx->iter; - - server = cifs_pick_channel(tlink_tcon(open_file->tlink)->ses); - - if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_RWPIDFORWARD) - pid = open_file->pid; - else - pid = current->tgid; - - if (ctx->direct_io) - iov_iter_advance(&direct_iov, offset - ctx->pos); - - do { - if (open_file->invalidHandle) { - rc = cifs_reopen_file(open_file, true); - if (rc == -EAGAIN) - continue; - else if (rc) - break; - } - - if (cifs_sb->ctx->rsize == 0) - cifs_sb->ctx->rsize = - server->ops->negotiate_rsize(tlink_tcon(open_file->tlink), - cifs_sb->ctx); - - rc = server->ops->wait_mtu_credits(server, cifs_sb->ctx->rsize, - &rsize, credits); - if (rc) - break; - - cur_len = min_t(const size_t, len, rsize); - - if (ctx->direct_io) { - ssize_t result; - - result = iov_iter_get_pages_alloc2( - &direct_iov, &pagevec, - cur_len, &start); - if (result < 0) { - cifs_dbg(VFS, - "Couldn't get user pages (rc=%zd) iter type %d iov_offset %zd count %zd\n", - result, iov_iter_type(&direct_iov), - direct_iov.iov_offset, - direct_iov.count); - dump_stack(); - - rc = result; - add_credits_and_wake_if(server, credits, 0); - break; - } - cur_len = (size_t)result; - - rdata = cifs_readdata_direct_alloc( - pagevec, cifs_uncached_readv_complete); - if (!rdata) { - add_credits_and_wake_if(server, credits, 0); - rc = -ENOMEM; - break; - } - - npages = (cur_len + start + PAGE_SIZE-1) / PAGE_SIZE; - rdata->page_offset = start; - rdata->tailsz = npages > 1 ? - cur_len-(PAGE_SIZE-start)-(npages-2)*PAGE_SIZE : - cur_len; - - } else { - - npages = DIV_ROUND_UP(cur_len, PAGE_SIZE); - /* allocate a readdata struct */ - rdata = cifs_readdata_alloc(npages, - cifs_uncached_readv_complete); - if (!rdata) { - add_credits_and_wake_if(server, credits, 0); - rc = -ENOMEM; - break; - } - - rc = cifs_read_allocate_pages(rdata, npages); - if (rc) { - kvfree(rdata->pages); - kfree(rdata); - add_credits_and_wake_if(server, credits, 0); - break; - } - - rdata->tailsz = PAGE_SIZE; - } - - rdata->server = server; - rdata->cfile = cifsFileInfo_get(open_file); - rdata->nr_pages = npages; - rdata->offset = offset; - rdata->bytes = cur_len; - rdata->pid = pid; - rdata->pagesz = PAGE_SIZE; - rdata->read_into_pages = cifs_uncached_read_into_pages; - rdata->copy_into_pages = cifs_uncached_copy_into_pages; - rdata->credits = credits_on_stack; - rdata->ctx = ctx; - kref_get(&ctx->refcount); - - rc = adjust_credits(server, &rdata->credits, rdata->bytes); - - if (!rc) { - if (rdata->cfile->invalidHandle) - rc = -EAGAIN; - else - rc = server->ops->async_readv(rdata); - } - - if (rc) { - add_credits_and_wake_if(server, &rdata->credits, 0); - kref_put(&rdata->refcount, - cifs_uncached_readdata_release); - if (rc == -EAGAIN) { - iov_iter_revert(&direct_iov, cur_len); - continue; - } - break; - } - - list_add_tail(&rdata->list, rdata_list); - offset += cur_len; - len -= cur_len; - } while (len > 0); - - return rc; -} - -static void -collect_uncached_read_data(struct cifs_aio_ctx *ctx) -{ - struct cifs_readdata *rdata, *tmp; - struct iov_iter *to = &ctx->iter; - struct cifs_sb_info *cifs_sb; - int rc; - - cifs_sb = CIFS_SB(ctx->cfile->dentry->d_sb); - - mutex_lock(&ctx->aio_mutex); - - if (list_empty(&ctx->list)) { - mutex_unlock(&ctx->aio_mutex); - return; - } - - rc = ctx->rc; - /* the loop below should proceed in the order of increasing offsets */ -again: - list_for_each_entry_safe(rdata, tmp, &ctx->list, list) { - if (!rc) { - if (!try_wait_for_completion(&rdata->done)) { - mutex_unlock(&ctx->aio_mutex); - return; - } - - if (rdata->result == -EAGAIN) { - /* resend call if it's a retryable error */ - struct list_head tmp_list; - unsigned int got_bytes = rdata->got_bytes; - - list_del_init(&rdata->list); - INIT_LIST_HEAD(&tmp_list); - - /* - * Got a part of data and then reconnect has - * happened -- fill the buffer and continue - * reading. - */ - if (got_bytes && got_bytes < rdata->bytes) { - rc = 0; - if (!ctx->direct_io) - rc = cifs_readdata_to_iov(rdata, to); - if (rc) { - kref_put(&rdata->refcount, - cifs_uncached_readdata_release); - continue; - } - } - - if (ctx->direct_io) { - /* - * Re-use rdata as this is a - * direct I/O - */ - rc = cifs_resend_rdata( - rdata, - &tmp_list, ctx); - } else { - rc = cifs_send_async_read( - rdata->offset + got_bytes, - rdata->bytes - got_bytes, - rdata->cfile, cifs_sb, - &tmp_list, ctx); - - kref_put(&rdata->refcount, - cifs_uncached_readdata_release); - } - - list_splice(&tmp_list, &ctx->list); - - goto again; - } else if (rdata->result) - rc = rdata->result; - else if (!ctx->direct_io) - rc = cifs_readdata_to_iov(rdata, to); - - /* if there was a short read -- discard anything left */ - if (rdata->got_bytes && rdata->got_bytes < rdata->bytes) - rc = -ENODATA; - - ctx->total_len += rdata->got_bytes; - } - list_del_init(&rdata->list); - kref_put(&rdata->refcount, cifs_uncached_readdata_release); - } - - if (!ctx->direct_io) - ctx->total_len = ctx->len - iov_iter_count(to); - - /* mask nodata case */ - if (rc == -ENODATA) - rc = 0; - - ctx->rc = (rc == 0) ? (ssize_t)ctx->total_len : rc; - - mutex_unlock(&ctx->aio_mutex); - - if (ctx->iocb && ctx->iocb->ki_complete) - ctx->iocb->ki_complete(ctx->iocb, ctx->rc); - else - complete(&ctx->done); -} - -static ssize_t __cifs_readv( - struct kiocb *iocb, struct iov_iter *to, bool direct) -{ - size_t len; - struct file *file = iocb->ki_filp; - struct cifs_sb_info *cifs_sb; - struct cifsFileInfo *cfile; - struct cifs_tcon *tcon; - ssize_t rc, total_read = 0; - loff_t offset = iocb->ki_pos; - struct cifs_aio_ctx *ctx; - - /* - * iov_iter_get_pages_alloc() doesn't work with ITER_KVEC, - * fall back to data copy read path - * this could be improved by getting pages directly in ITER_KVEC - */ - if (direct && iov_iter_is_kvec(to)) { - cifs_dbg(FYI, "use non-direct cifs_user_readv for kvec I/O\n"); - direct = false; - } - - len = iov_iter_count(to); - if (!len) - return 0; - - cifs_sb = CIFS_FILE_SB(file); - cfile = file->private_data; - tcon = tlink_tcon(cfile->tlink); - - if (!tcon->ses->server->ops->async_readv) - return -ENOSYS; - - if ((file->f_flags & O_ACCMODE) == O_WRONLY) - cifs_dbg(FYI, "attempting read on write only file instance\n"); - - ctx = cifs_aio_ctx_alloc(); - if (!ctx) - return -ENOMEM; - - ctx->cfile = cifsFileInfo_get(cfile); - - if (!is_sync_kiocb(iocb)) - ctx->iocb = iocb; - - if (user_backed_iter(to)) - ctx->should_dirty = true; - - if (direct) { - ctx->pos = offset; - ctx->direct_io = true; - ctx->iter = *to; - ctx->len = len; - } else { - rc = setup_aio_ctx_iter(ctx, to, ITER_DEST); - if (rc) { - kref_put(&ctx->refcount, cifs_aio_ctx_release); - return rc; - } - len = ctx->len; - } - - if (direct) { - rc = filemap_write_and_wait_range(file->f_inode->i_mapping, - offset, offset + len - 1); - if (rc) { - kref_put(&ctx->refcount, cifs_aio_ctx_release); - return -EAGAIN; - } - } - - /* grab a lock here due to read response handlers can access ctx */ - mutex_lock(&ctx->aio_mutex); - - rc = cifs_send_async_read(offset, len, cfile, cifs_sb, &ctx->list, ctx); - - /* if at least one read request send succeeded, then reset rc */ - if (!list_empty(&ctx->list)) - rc = 0; - - mutex_unlock(&ctx->aio_mutex); - - if (rc) { - kref_put(&ctx->refcount, cifs_aio_ctx_release); - return rc; - } - - if (!is_sync_kiocb(iocb)) { - kref_put(&ctx->refcount, cifs_aio_ctx_release); - return -EIOCBQUEUED; - } - - rc = wait_for_completion_killable(&ctx->done); - if (rc) { - mutex_lock(&ctx->aio_mutex); - ctx->rc = rc = -EINTR; - total_read = ctx->total_len; - mutex_unlock(&ctx->aio_mutex); - } else { - rc = ctx->rc; - total_read = ctx->total_len; - } - - kref_put(&ctx->refcount, cifs_aio_ctx_release); - - if (total_read) { - iocb->ki_pos += total_read; - return total_read; - } - return rc; -} - -ssize_t cifs_direct_readv(struct kiocb *iocb, struct iov_iter *to) -{ - return __cifs_readv(iocb, to, true); -} - -ssize_t cifs_user_readv(struct kiocb *iocb, struct iov_iter *to) -{ - return __cifs_readv(iocb, to, false); -} - -ssize_t -cifs_strict_readv(struct kiocb *iocb, struct iov_iter *to) -{ - struct inode *inode = file_inode(iocb->ki_filp); - struct cifsInodeInfo *cinode = CIFS_I(inode); - struct cifs_sb_info *cifs_sb = CIFS_SB(inode->i_sb); - struct cifsFileInfo *cfile = (struct cifsFileInfo *) - iocb->ki_filp->private_data; - struct cifs_tcon *tcon = tlink_tcon(cfile->tlink); - int rc = -EACCES; - - /* - * In strict cache mode we need to read from the server all the time - * if we don't have level II oplock because the server can delay mtime - * change - so we can't make a decision about inode invalidating. - * And we can also fail with pagereading if there are mandatory locks - * on pages affected by this read but not on the region from pos to - * pos+len-1. - */ - if (!CIFS_CACHE_READ(cinode)) - return cifs_user_readv(iocb, to); - - if (cap_unix(tcon->ses) && - (CIFS_UNIX_FCNTL_CAP & le64_to_cpu(tcon->fsUnixInfo.Capability)) && - ((cifs_sb->mnt_cifs_flags & CIFS_MOUNT_NOPOSIXBRL) == 0)) - return generic_file_read_iter(iocb, to); - - /* - * We need to hold the sem to be sure nobody modifies lock list - * with a brlock that prevents reading. - */ - down_read(&cinode->lock_sem); - if (!cifs_find_lock_conflict(cfile, iocb->ki_pos, iov_iter_count(to), - tcon->ses->server->vals->shared_lock_type, - 0, NULL, CIFS_READ_OP)) - rc = generic_file_read_iter(iocb, to); - up_read(&cinode->lock_sem); - return rc; -} - -static ssize_t -cifs_read(struct file *file, char *read_data, size_t read_size, loff_t *offset) -{ - int rc = -EACCES; - unsigned int bytes_read = 0; - unsigned int total_read; - unsigned int current_read_size; - unsigned int rsize; - struct cifs_sb_info *cifs_sb; - struct cifs_tcon *tcon; - struct TCP_Server_Info *server; - unsigned int xid; - char *cur_offset; - struct cifsFileInfo *open_file; - struct cifs_io_parms io_parms = {0}; - int buf_type = CIFS_NO_BUFFER; - __u32 pid; - - xid = get_xid(); - cifs_sb = CIFS_FILE_SB(file); - - /* FIXME: set up handlers for larger reads and/or convert to async */ - rsize = min_t(unsigned int, cifs_sb->ctx->rsize, CIFSMaxBufSize); - - if (file->private_data == NULL) { - rc = -EBADF; - free_xid(xid); - return rc; - } - open_file = file->private_data; - tcon = tlink_tcon(open_file->tlink); - server = cifs_pick_channel(tcon->ses); - - if (!server->ops->sync_read) { - free_xid(xid); - return -ENOSYS; - } - - if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_RWPIDFORWARD) - pid = open_file->pid; - else - pid = current->tgid; - - if ((file->f_flags & O_ACCMODE) == O_WRONLY) - cifs_dbg(FYI, "attempting read on write only file instance\n"); - - for (total_read = 0, cur_offset = read_data; read_size > total_read; - total_read += bytes_read, cur_offset += bytes_read) { - do { - current_read_size = min_t(uint, read_size - total_read, - rsize); - /* - * For windows me and 9x we do not want to request more - * than it negotiated since it will refuse the read - * then. - */ - if (!(tcon->ses->capabilities & - tcon->ses->server->vals->cap_large_files)) { - current_read_size = min_t(uint, - current_read_size, CIFSMaxBufSize); - } - if (open_file->invalidHandle) { - rc = cifs_reopen_file(open_file, true); - if (rc != 0) - break; - } - io_parms.pid = pid; - io_parms.tcon = tcon; - io_parms.offset = *offset; - io_parms.length = current_read_size; - io_parms.server = server; - rc = server->ops->sync_read(xid, &open_file->fid, &io_parms, - &bytes_read, &cur_offset, - &buf_type); - } while (rc == -EAGAIN); - - if (rc || (bytes_read == 0)) { - if (total_read) { - break; - } else { - free_xid(xid); - return rc; - } - } else { - cifs_stats_bytes_read(tcon, total_read); - *offset += bytes_read; - } - } - free_xid(xid); - return total_read; -} - -/* - * If the page is mmap'ed into a process' page tables, then we need to make - * sure that it doesn't change while being written back. - */ -static vm_fault_t -cifs_page_mkwrite(struct vm_fault *vmf) -{ - struct page *page = vmf->page; - - /* Wait for the page to be written to the cache before we allow it to - * be modified. We then assume the entire page will need writing back. - */ -#ifdef CONFIG_CIFS_FSCACHE - if (PageFsCache(page) && - wait_on_page_fscache_killable(page) < 0) - return VM_FAULT_RETRY; -#endif - - wait_on_page_writeback(page); - - if (lock_page_killable(page) < 0) - return VM_FAULT_RETRY; - return VM_FAULT_LOCKED; -} - -static const struct vm_operations_struct cifs_file_vm_ops = { - .fault = filemap_fault, - .map_pages = filemap_map_pages, - .page_mkwrite = cifs_page_mkwrite, -}; - -int cifs_file_strict_mmap(struct file *file, struct vm_area_struct *vma) -{ - int xid, rc = 0; - struct inode *inode = file_inode(file); - - xid = get_xid(); - - if (!CIFS_CACHE_READ(CIFS_I(inode))) - rc = cifs_zap_mapping(inode); - if (!rc) - rc = generic_file_mmap(file, vma); - if (!rc) - vma->vm_ops = &cifs_file_vm_ops; - - free_xid(xid); - return rc; -} - -int cifs_file_mmap(struct file *file, struct vm_area_struct *vma) -{ - int rc, xid; - - xid = get_xid(); - - rc = cifs_revalidate_file(file); - if (rc) - cifs_dbg(FYI, "Validation prior to mmap failed, error=%d\n", - rc); - if (!rc) - rc = generic_file_mmap(file, vma); - if (!rc) - vma->vm_ops = &cifs_file_vm_ops; - - free_xid(xid); - return rc; -} - -static void -cifs_readv_complete(struct work_struct *work) -{ - unsigned int i, got_bytes; - struct cifs_readdata *rdata = container_of(work, - struct cifs_readdata, work); - - got_bytes = rdata->got_bytes; - for (i = 0; i < rdata->nr_pages; i++) { - struct page *page = rdata->pages[i]; - - if (rdata->result == 0 || - (rdata->result == -EAGAIN && got_bytes)) { - flush_dcache_page(page); - SetPageUptodate(page); - } else - SetPageError(page); - - if (rdata->result == 0 || - (rdata->result == -EAGAIN && got_bytes)) - cifs_readpage_to_fscache(rdata->mapping->host, page); - - unlock_page(page); - - got_bytes -= min_t(unsigned int, PAGE_SIZE, got_bytes); - - put_page(page); - rdata->pages[i] = NULL; - } - kref_put(&rdata->refcount, cifs_readdata_release); -} - -static int -readpages_fill_pages(struct TCP_Server_Info *server, - struct cifs_readdata *rdata, struct iov_iter *iter, - unsigned int len) -{ - int result = 0; - unsigned int i; - u64 eof; - pgoff_t eof_index; - unsigned int nr_pages = rdata->nr_pages; - unsigned int page_offset = rdata->page_offset; - - /* determine the eof that the server (probably) has */ - eof = CIFS_I(rdata->mapping->host)->server_eof; - eof_index = eof ? (eof - 1) >> PAGE_SHIFT : 0; - cifs_dbg(FYI, "eof=%llu eof_index=%lu\n", eof, eof_index); - - rdata->got_bytes = 0; - rdata->tailsz = PAGE_SIZE; - for (i = 0; i < nr_pages; i++) { - struct page *page = rdata->pages[i]; - unsigned int to_read = rdata->pagesz; - size_t n; - - if (i == 0) - to_read -= page_offset; - else - page_offset = 0; - - n = to_read; - - if (len >= to_read) { - len -= to_read; - } else if (len > 0) { - /* enough for partial page, fill and zero the rest */ - zero_user(page, len + page_offset, to_read - len); - n = rdata->tailsz = len; - len = 0; - } else if (page->index > eof_index) { - /* - * The VFS will not try to do readahead past the - * i_size, but it's possible that we have outstanding - * writes with gaps in the middle and the i_size hasn't - * caught up yet. Populate those with zeroed out pages - * to prevent the VFS from repeatedly attempting to - * fill them until the writes are flushed. - */ - zero_user(page, 0, PAGE_SIZE); - flush_dcache_page(page); - SetPageUptodate(page); - unlock_page(page); - put_page(page); - rdata->pages[i] = NULL; - rdata->nr_pages--; - continue; - } else { - /* no need to hold page hostage */ - unlock_page(page); - put_page(page); - rdata->pages[i] = NULL; - rdata->nr_pages--; - continue; - } - - if (iter) - result = copy_page_from_iter( - page, page_offset, n, iter); -#ifdef CONFIG_CIFS_SMB_DIRECT - else if (rdata->mr) - result = n; -#endif - else - result = cifs_read_page_from_socket( - server, page, page_offset, n); - if (result < 0) - break; - - rdata->got_bytes += result; - } - - return result != -ECONNABORTED && rdata->got_bytes > 0 ? - rdata->got_bytes : result; -} - -static int -cifs_readpages_read_into_pages(struct TCP_Server_Info *server, - struct cifs_readdata *rdata, unsigned int len) -{ - return readpages_fill_pages(server, rdata, NULL, len); -} - -static int -cifs_readpages_copy_into_pages(struct TCP_Server_Info *server, - struct cifs_readdata *rdata, - struct iov_iter *iter) -{ - return readpages_fill_pages(server, rdata, iter, iter->count); -} - -static void cifs_readahead(struct readahead_control *ractl) -{ - int rc; - struct cifsFileInfo *open_file = ractl->file->private_data; - struct cifs_sb_info *cifs_sb = CIFS_FILE_SB(ractl->file); - struct TCP_Server_Info *server; - pid_t pid; - unsigned int xid, nr_pages, last_batch_size = 0, cache_nr_pages = 0; - pgoff_t next_cached = ULONG_MAX; - bool caching = fscache_cookie_enabled(cifs_inode_cookie(ractl->mapping->host)) && - cifs_inode_cookie(ractl->mapping->host)->cache_priv; - bool check_cache = caching; - - xid = get_xid(); - - if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_RWPIDFORWARD) - pid = open_file->pid; - else - pid = current->tgid; - - rc = 0; - server = cifs_pick_channel(tlink_tcon(open_file->tlink)->ses); - - cifs_dbg(FYI, "%s: file=%p mapping=%p num_pages=%u\n", - __func__, ractl->file, ractl->mapping, readahead_count(ractl)); - - /* - * Chop the readahead request up into rsize-sized read requests. - */ - while ((nr_pages = readahead_count(ractl) - last_batch_size)) { - unsigned int i, got, rsize; - struct page *page; - struct cifs_readdata *rdata; - struct cifs_credits credits_on_stack; - struct cifs_credits *credits = &credits_on_stack; - pgoff_t index = readahead_index(ractl) + last_batch_size; - - /* - * Find out if we have anything cached in the range of - * interest, and if so, where the next chunk of cached data is. - */ - if (caching) { - if (check_cache) { - rc = cifs_fscache_query_occupancy( - ractl->mapping->host, index, nr_pages, - &next_cached, &cache_nr_pages); - if (rc < 0) - caching = false; - check_cache = false; - } - - if (index == next_cached) { - /* - * TODO: Send a whole batch of pages to be read - * by the cache. - */ - struct folio *folio = readahead_folio(ractl); - - last_batch_size = folio_nr_pages(folio); - if (cifs_readpage_from_fscache(ractl->mapping->host, - &folio->page) < 0) { - /* - * TODO: Deal with cache read failure - * here, but for the moment, delegate - * that to readpage. - */ - caching = false; - } - folio_unlock(folio); - next_cached++; - cache_nr_pages--; - if (cache_nr_pages == 0) - check_cache = true; - continue; - } - } - - if (open_file->invalidHandle) { - rc = cifs_reopen_file(open_file, true); - if (rc) { - if (rc == -EAGAIN) - continue; - break; - } - } - - if (cifs_sb->ctx->rsize == 0) - cifs_sb->ctx->rsize = - server->ops->negotiate_rsize(tlink_tcon(open_file->tlink), - cifs_sb->ctx); - - rc = server->ops->wait_mtu_credits(server, cifs_sb->ctx->rsize, - &rsize, credits); - if (rc) - break; - nr_pages = min_t(size_t, rsize / PAGE_SIZE, readahead_count(ractl)); - nr_pages = min_t(size_t, nr_pages, next_cached - index); - - /* - * Give up immediately if rsize is too small to read an entire - * page. The VFS will fall back to readpage. We should never - * reach this point however since we set ra_pages to 0 when the - * rsize is smaller than a cache page. - */ - if (unlikely(!nr_pages)) { - add_credits_and_wake_if(server, credits, 0); - break; - } - - rdata = cifs_readdata_alloc(nr_pages, cifs_readv_complete); - if (!rdata) { - /* best to give up if we're out of mem */ - add_credits_and_wake_if(server, credits, 0); - break; - } - - got = __readahead_batch(ractl, rdata->pages, nr_pages); - if (got != nr_pages) { - pr_warn("__readahead_batch() returned %u/%u\n", - got, nr_pages); - nr_pages = got; - } - - rdata->nr_pages = nr_pages; - rdata->bytes = readahead_batch_length(ractl); - rdata->cfile = cifsFileInfo_get(open_file); - rdata->server = server; - rdata->mapping = ractl->mapping; - rdata->offset = readahead_pos(ractl); - rdata->pid = pid; - rdata->pagesz = PAGE_SIZE; - rdata->tailsz = PAGE_SIZE; - rdata->read_into_pages = cifs_readpages_read_into_pages; - rdata->copy_into_pages = cifs_readpages_copy_into_pages; - rdata->credits = credits_on_stack; - - rc = adjust_credits(server, &rdata->credits, rdata->bytes); - if (!rc) { - if (rdata->cfile->invalidHandle) - rc = -EAGAIN; - else - rc = server->ops->async_readv(rdata); - } - - if (rc) { - add_credits_and_wake_if(server, &rdata->credits, 0); - for (i = 0; i < rdata->nr_pages; i++) { - page = rdata->pages[i]; - unlock_page(page); - put_page(page); - } - /* Fallback to the readpage in error/reconnect cases */ - kref_put(&rdata->refcount, cifs_readdata_release); - break; - } - - kref_put(&rdata->refcount, cifs_readdata_release); - last_batch_size = nr_pages; - } - - free_xid(xid); -} - -/* - * cifs_readpage_worker must be called with the page pinned - */ -static int cifs_readpage_worker(struct file *file, struct page *page, - loff_t *poffset) -{ - char *read_data; - int rc; - - /* Is the page cached? */ - rc = cifs_readpage_from_fscache(file_inode(file), page); - if (rc == 0) - goto read_complete; - - read_data = kmap(page); - /* for reads over a certain size could initiate async read ahead */ - - rc = cifs_read(file, read_data, PAGE_SIZE, poffset); - - if (rc < 0) - goto io_error; - else - cifs_dbg(FYI, "Bytes read %d\n", rc); - - /* we do not want atime to be less than mtime, it broke some apps */ - file_inode(file)->i_atime = current_time(file_inode(file)); - if (timespec64_compare(&(file_inode(file)->i_atime), &(file_inode(file)->i_mtime))) - file_inode(file)->i_atime = file_inode(file)->i_mtime; - else - file_inode(file)->i_atime = current_time(file_inode(file)); - - if (PAGE_SIZE > rc) - memset(read_data + rc, 0, PAGE_SIZE - rc); - - flush_dcache_page(page); - SetPageUptodate(page); - - /* send this page to the cache */ - cifs_readpage_to_fscache(file_inode(file), page); - - rc = 0; - -io_error: - kunmap(page); - unlock_page(page); - -read_complete: - return rc; -} - -static int cifs_read_folio(struct file *file, struct folio *folio) -{ - struct page *page = &folio->page; - loff_t offset = page_file_offset(page); - int rc = -EACCES; - unsigned int xid; - - xid = get_xid(); - - if (file->private_data == NULL) { - rc = -EBADF; - free_xid(xid); - return rc; - } - - cifs_dbg(FYI, "read_folio %p at offset %d 0x%x\n", - page, (int)offset, (int)offset); - - rc = cifs_readpage_worker(file, page, &offset); - - free_xid(xid); - return rc; -} - -static int is_inode_writable(struct cifsInodeInfo *cifs_inode) -{ - struct cifsFileInfo *open_file; - - spin_lock(&cifs_inode->open_file_lock); - list_for_each_entry(open_file, &cifs_inode->openFileList, flist) { - if (OPEN_FMODE(open_file->f_flags) & FMODE_WRITE) { - spin_unlock(&cifs_inode->open_file_lock); - return 1; - } - } - spin_unlock(&cifs_inode->open_file_lock); - return 0; -} - -/* We do not want to update the file size from server for inodes - open for write - to avoid races with writepage extending - the file - in the future we could consider allowing - refreshing the inode only on increases in the file size - but this is tricky to do without racing with writebehind - page caching in the current Linux kernel design */ -bool is_size_safe_to_change(struct cifsInodeInfo *cifsInode, __u64 end_of_file) -{ - if (!cifsInode) - return true; - - if (is_inode_writable(cifsInode)) { - /* This inode is open for write at least once */ - struct cifs_sb_info *cifs_sb; - - cifs_sb = CIFS_SB(cifsInode->netfs.inode.i_sb); - if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_DIRECT_IO) { - /* since no page cache to corrupt on directio - we can change size safely */ - return true; - } - - if (i_size_read(&cifsInode->netfs.inode) < end_of_file) - return true; - - return false; - } else - return true; -} - -static int cifs_write_begin(struct file *file, struct address_space *mapping, - loff_t pos, unsigned len, - struct page **pagep, void **fsdata) -{ - int oncethru = 0; - pgoff_t index = pos >> PAGE_SHIFT; - loff_t offset = pos & (PAGE_SIZE - 1); - loff_t page_start = pos & PAGE_MASK; - loff_t i_size; - struct page *page; - int rc = 0; - - cifs_dbg(FYI, "write_begin from %lld len %d\n", (long long)pos, len); - -start: - page = grab_cache_page_write_begin(mapping, index); - if (!page) { - rc = -ENOMEM; - goto out; - } - - if (PageUptodate(page)) - goto out; - - /* - * If we write a full page it will be up to date, no need to read from - * the server. If the write is short, we'll end up doing a sync write - * instead. - */ - if (len == PAGE_SIZE) - goto out; - - /* - * optimize away the read when we have an oplock, and we're not - * expecting to use any of the data we'd be reading in. That - * is, when the page lies beyond the EOF, or straddles the EOF - * and the write will cover all of the existing data. - */ - if (CIFS_CACHE_READ(CIFS_I(mapping->host))) { - i_size = i_size_read(mapping->host); - if (page_start >= i_size || - (offset == 0 && (pos + len) >= i_size)) { - zero_user_segments(page, 0, offset, - offset + len, - PAGE_SIZE); - /* - * PageChecked means that the parts of the page - * to which we're not writing are considered up - * to date. Once the data is copied to the - * page, it can be set uptodate. - */ - SetPageChecked(page); - goto out; - } - } - - if ((file->f_flags & O_ACCMODE) != O_WRONLY && !oncethru) { - /* - * might as well read a page, it is fast enough. If we get - * an error, we don't need to return it. cifs_write_end will - * do a sync write instead since PG_uptodate isn't set. - */ - cifs_readpage_worker(file, page, &page_start); - put_page(page); - oncethru = 1; - goto start; - } else { - /* we could try using another file handle if there is one - - but how would we lock it to prevent close of that handle - racing with this read? In any case - this will be written out by write_end so is fine */ - } -out: - *pagep = page; - return rc; -} - -static bool cifs_release_folio(struct folio *folio, gfp_t gfp) -{ - if (folio_test_private(folio)) - return 0; - if (folio_test_fscache(folio)) { - if (current_is_kswapd() || !(gfp & __GFP_FS)) - return false; - folio_wait_fscache(folio); - } - fscache_note_page_release(cifs_inode_cookie(folio->mapping->host)); - return true; -} - -static void cifs_invalidate_folio(struct folio *folio, size_t offset, - size_t length) -{ - folio_wait_fscache(folio); -} - -static int cifs_launder_folio(struct folio *folio) -{ - int rc = 0; - loff_t range_start = folio_pos(folio); - loff_t range_end = range_start + folio_size(folio); - struct writeback_control wbc = { - .sync_mode = WB_SYNC_ALL, - .nr_to_write = 0, - .range_start = range_start, - .range_end = range_end, - }; - - cifs_dbg(FYI, "Launder page: %lu\n", folio->index); - - if (folio_clear_dirty_for_io(folio)) - rc = cifs_writepage_locked(&folio->page, &wbc); - - folio_wait_fscache(folio); - return rc; -} - -void cifs_oplock_break(struct work_struct *work) -{ - struct cifsFileInfo *cfile = container_of(work, struct cifsFileInfo, - oplock_break); - struct inode *inode = d_inode(cfile->dentry); - struct cifsInodeInfo *cinode = CIFS_I(inode); - struct cifs_tcon *tcon = tlink_tcon(cfile->tlink); - struct TCP_Server_Info *server = tcon->ses->server; - int rc = 0; - bool purge_cache = false, oplock_break_cancelled; - __u64 persistent_fid, volatile_fid; - __u16 net_fid; - - wait_on_bit(&cinode->flags, CIFS_INODE_PENDING_WRITERS, - TASK_UNINTERRUPTIBLE); - - server->ops->downgrade_oplock(server, cinode, cfile->oplock_level, - cfile->oplock_epoch, &purge_cache); - - if (!CIFS_CACHE_WRITE(cinode) && CIFS_CACHE_READ(cinode) && - cifs_has_mand_locks(cinode)) { - cifs_dbg(FYI, "Reset oplock to None for inode=%p due to mand locks\n", - inode); - cinode->oplock = 0; - } - - if (inode && S_ISREG(inode->i_mode)) { - if (CIFS_CACHE_READ(cinode)) - break_lease(inode, O_RDONLY); - else - break_lease(inode, O_WRONLY); - rc = filemap_fdatawrite(inode->i_mapping); - if (!CIFS_CACHE_READ(cinode) || purge_cache) { - rc = filemap_fdatawait(inode->i_mapping); - mapping_set_error(inode->i_mapping, rc); - cifs_zap_mapping(inode); - } - cifs_dbg(FYI, "Oplock flush inode %p rc %d\n", inode, rc); - if (CIFS_CACHE_WRITE(cinode)) - goto oplock_break_ack; - } - - rc = cifs_push_locks(cfile); - if (rc) - cifs_dbg(VFS, "Push locks rc = %d\n", rc); - -oplock_break_ack: - /* - * When oplock break is received and there are no active - * file handles but cached, then schedule deferred close immediately. - * So, new open will not use cached handle. - */ - - if (!CIFS_CACHE_HANDLE(cinode) && !list_empty(&cinode->deferred_closes)) - cifs_close_deferred_file(cinode); - - persistent_fid = cfile->fid.persistent_fid; - volatile_fid = cfile->fid.volatile_fid; - net_fid = cfile->fid.netfid; - oplock_break_cancelled = cfile->oplock_break_cancelled; - - _cifsFileInfo_put(cfile, false /* do not wait for ourself */, false); - /* - * releasing stale oplock after recent reconnect of smb session using - * a now incorrect file handle is not a data integrity issue but do - * not bother sending an oplock release if session to server still is - * disconnected since oplock already released by the server - */ - if (!oplock_break_cancelled) { - /* check for server null since can race with kill_sb calling tree disconnect */ - if (tcon->ses && tcon->ses->server) { - rc = tcon->ses->server->ops->oplock_response(tcon, persistent_fid, - volatile_fid, net_fid, cinode); - cifs_dbg(FYI, "Oplock release rc = %d\n", rc); - } else - pr_warn_once("lease break not sent for unmounted share\n"); - } - - cifs_done_oplock_break(cinode); -} - -/* - * The presence of cifs_direct_io() in the address space ops vector - * allowes open() O_DIRECT flags which would have failed otherwise. - * - * In the non-cached mode (mount with cache=none), we shunt off direct read and write requests - * so this method should never be called. - * - * Direct IO is not yet supported in the cached mode. - */ -static ssize_t -cifs_direct_io(struct kiocb *iocb, struct iov_iter *iter) -{ - /* - * FIXME - * Eventually need to support direct IO for non forcedirectio mounts - */ - return -EINVAL; -} - -static int cifs_swap_activate(struct swap_info_struct *sis, - struct file *swap_file, sector_t *span) -{ - struct cifsFileInfo *cfile = swap_file->private_data; - struct inode *inode = swap_file->f_mapping->host; - unsigned long blocks; - long long isize; - - cifs_dbg(FYI, "swap activate\n"); - - if (!swap_file->f_mapping->a_ops->swap_rw) - /* Cannot support swap */ - return -EINVAL; - - spin_lock(&inode->i_lock); - blocks = inode->i_blocks; - isize = inode->i_size; - spin_unlock(&inode->i_lock); - if (blocks*512 < isize) { - pr_warn("swap activate: swapfile has holes\n"); - return -EINVAL; - } - *span = sis->pages; - - pr_warn_once("Swap support over SMB3 is experimental\n"); - - /* - * TODO: consider adding ACL (or documenting how) to prevent other - * users (on this or other systems) from reading it - */ - - - /* TODO: add sk_set_memalloc(inet) or similar */ - - if (cfile) - cfile->swapfile = true; - /* - * TODO: Since file already open, we can't open with DENY_ALL here - * but we could add call to grab a byte range lock to prevent others - * from reading or writing the file - */ - - sis->flags |= SWP_FS_OPS; - return add_swap_extent(sis, 0, sis->max, 0); -} - -static void cifs_swap_deactivate(struct file *file) -{ - struct cifsFileInfo *cfile = file->private_data; - - cifs_dbg(FYI, "swap deactivate\n"); - - /* TODO: undo sk_set_memalloc(inet) will eventually be needed */ - - if (cfile) - cfile->swapfile = false; - - /* do we need to unpin (or unlock) the file */ -} - -/* - * Mark a page as having been made dirty and thus needing writeback. We also - * need to pin the cache object to write back to. - */ -#ifdef CONFIG_CIFS_FSCACHE -static bool cifs_dirty_folio(struct address_space *mapping, struct folio *folio) -{ - return fscache_dirty_folio(mapping, folio, - cifs_inode_cookie(mapping->host)); -} -#else -#define cifs_dirty_folio filemap_dirty_folio -#endif - -const struct address_space_operations cifs_addr_ops = { - .read_folio = cifs_read_folio, - .readahead = cifs_readahead, - .writepage = cifs_writepage, - .writepages = cifs_writepages, - .write_begin = cifs_write_begin, - .write_end = cifs_write_end, - .dirty_folio = cifs_dirty_folio, - .release_folio = cifs_release_folio, - .direct_IO = cifs_direct_io, - .invalidate_folio = cifs_invalidate_folio, - .launder_folio = cifs_launder_folio, - /* - * TODO: investigate and if useful we could add an cifs_migratePage - * helper (under an CONFIG_MIGRATION) in the future, and also - * investigate and add an is_dirty_writeback helper if needed - */ - .swap_activate = cifs_swap_activate, - .swap_deactivate = cifs_swap_deactivate, -}; - -/* - * cifs_readahead requires the server to support a buffer large enough to - * contain the header plus one complete page of data. Otherwise, we need - * to leave cifs_readahead out of the address space operations. - */ -const struct address_space_operations cifs_addr_ops_smallbuf = { - .read_folio = cifs_read_folio, - .writepage = cifs_writepage, - .writepages = cifs_writepages, - .write_begin = cifs_write_begin, - .write_end = cifs_write_end, - .dirty_folio = cifs_dirty_folio, - .release_folio = cifs_release_folio, - .invalidate_folio = cifs_invalidate_folio, - .launder_folio = cifs_launder_folio, -}; diff --git a/fs/cifs/fs_context.c b/fs/cifs/fs_context.c deleted file mode 100644 index e2e2ef0fa9a0f..0000000000000 --- a/fs/cifs/fs_context.c +++ /dev/null @@ -1,1773 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-or-later -/* - * Copyright (C) 2020, Microsoft Corporation. - * - * Author(s): Steve French - * David Howells - */ - -/* -#include -#include -#include -#include -#include -#include -#ifdef CONFIG_CIFS_DFS_UPCALL -#include "dfs_cache.h" -#endif -*/ - -#include -#include -#include -#include -#include -#include -#include -#include "cifsfs.h" -#include "cifspdu.h" -#include "cifsglob.h" -#include "cifsproto.h" -#include "cifs_unicode.h" -#include "cifs_debug.h" -#include "cifs_fs_sb.h" -#include "ntlmssp.h" -#include "nterr.h" -#include "rfc1002pdu.h" -#include "fs_context.h" - -static DEFINE_MUTEX(cifs_mount_mutex); - -static const match_table_t cifs_smb_version_tokens = { - { Smb_1, SMB1_VERSION_STRING }, - { Smb_20, SMB20_VERSION_STRING}, - { Smb_21, SMB21_VERSION_STRING }, - { Smb_30, SMB30_VERSION_STRING }, - { Smb_302, SMB302_VERSION_STRING }, - { Smb_302, ALT_SMB302_VERSION_STRING }, - { Smb_311, SMB311_VERSION_STRING }, - { Smb_311, ALT_SMB311_VERSION_STRING }, - { Smb_3any, SMB3ANY_VERSION_STRING }, - { Smb_default, SMBDEFAULT_VERSION_STRING }, - { Smb_version_err, NULL } -}; - -static const match_table_t cifs_secflavor_tokens = { - { Opt_sec_krb5, "krb5" }, - { Opt_sec_krb5i, "krb5i" }, - { Opt_sec_krb5p, "krb5p" }, - { Opt_sec_ntlmsspi, "ntlmsspi" }, - { Opt_sec_ntlmssp, "ntlmssp" }, - { Opt_sec_ntlmv2, "nontlm" }, - { Opt_sec_ntlmv2, "ntlmv2" }, - { Opt_sec_ntlmv2i, "ntlmv2i" }, - { Opt_sec_none, "none" }, - - { Opt_sec_err, NULL } -}; - -const struct fs_parameter_spec smb3_fs_parameters[] = { - /* Mount options that take no arguments */ - fsparam_flag_no("user_xattr", Opt_user_xattr), - fsparam_flag_no("forceuid", Opt_forceuid), - fsparam_flag_no("multichannel", Opt_multichannel), - fsparam_flag_no("forcegid", Opt_forcegid), - fsparam_flag("noblocksend", Opt_noblocksend), - fsparam_flag("noautotune", Opt_noautotune), - fsparam_flag("nolease", Opt_nolease), - fsparam_flag_no("hard", Opt_hard), - fsparam_flag_no("soft", Opt_soft), - fsparam_flag_no("perm", Opt_perm), - fsparam_flag("nodelete", Opt_nodelete), - fsparam_flag_no("mapposix", Opt_mapposix), - fsparam_flag("mapchars", Opt_mapchars), - fsparam_flag("nomapchars", Opt_nomapchars), - fsparam_flag_no("sfu", Opt_sfu), - fsparam_flag("nodfs", Opt_nodfs), - fsparam_flag_no("posixpaths", Opt_posixpaths), - fsparam_flag_no("unix", Opt_unix), - fsparam_flag_no("linux", Opt_unix), - fsparam_flag_no("posix", Opt_unix), - fsparam_flag("nocase", Opt_nocase), - fsparam_flag("ignorecase", Opt_nocase), - fsparam_flag_no("brl", Opt_brl), - fsparam_flag_no("handlecache", Opt_handlecache), - fsparam_flag("forcemandatorylock", Opt_forcemandatorylock), - fsparam_flag("forcemand", Opt_forcemandatorylock), - fsparam_flag("setuidfromacl", Opt_setuidfromacl), - fsparam_flag("idsfromsid", Opt_setuidfromacl), - fsparam_flag_no("setuids", Opt_setuids), - fsparam_flag_no("dynperm", Opt_dynperm), - fsparam_flag_no("intr", Opt_intr), - fsparam_flag_no("strictsync", Opt_strictsync), - fsparam_flag_no("serverino", Opt_serverino), - fsparam_flag("rwpidforward", Opt_rwpidforward), - fsparam_flag("cifsacl", Opt_cifsacl), - fsparam_flag_no("acl", Opt_acl), - fsparam_flag("locallease", Opt_locallease), - fsparam_flag("sign", Opt_sign), - fsparam_flag("ignore_signature", Opt_ignore_signature), - fsparam_flag("signloosely", Opt_ignore_signature), - fsparam_flag("seal", Opt_seal), - fsparam_flag("noac", Opt_noac), - fsparam_flag("fsc", Opt_fsc), - fsparam_flag("mfsymlinks", Opt_mfsymlinks), - fsparam_flag("multiuser", Opt_multiuser), - fsparam_flag("sloppy", Opt_sloppy), - fsparam_flag("nosharesock", Opt_nosharesock), - fsparam_flag_no("persistenthandles", Opt_persistent), - fsparam_flag_no("resilienthandles", Opt_resilient), - fsparam_flag_no("tcpnodelay", Opt_tcp_nodelay), - fsparam_flag("nosparse", Opt_nosparse), - fsparam_flag("domainauto", Opt_domainauto), - fsparam_flag("rdma", Opt_rdma), - fsparam_flag("modesid", Opt_modesid), - fsparam_flag("modefromsid", Opt_modesid), - fsparam_flag("rootfs", Opt_rootfs), - fsparam_flag("compress", Opt_compress), - fsparam_flag("witness", Opt_witness), - - /* Mount options which take numeric value */ - fsparam_u32("backupuid", Opt_backupuid), - fsparam_u32("backupgid", Opt_backupgid), - fsparam_u32("uid", Opt_uid), - fsparam_u32("cruid", Opt_cruid), - fsparam_u32("gid", Opt_gid), - fsparam_u32("file_mode", Opt_file_mode), - fsparam_u32("dirmode", Opt_dirmode), - fsparam_u32("dir_mode", Opt_dirmode), - fsparam_u32("port", Opt_port), - fsparam_u32("min_enc_offload", Opt_min_enc_offload), - fsparam_u32("esize", Opt_min_enc_offload), - fsparam_u32("bsize", Opt_blocksize), - fsparam_u32("rasize", Opt_rasize), - fsparam_u32("rsize", Opt_rsize), - fsparam_u32("wsize", Opt_wsize), - fsparam_u32("actimeo", Opt_actimeo), - fsparam_u32("acdirmax", Opt_acdirmax), - fsparam_u32("acregmax", Opt_acregmax), - fsparam_u32("closetimeo", Opt_closetimeo), - fsparam_u32("echo_interval", Opt_echo_interval), - fsparam_u32("max_credits", Opt_max_credits), - fsparam_u32("handletimeout", Opt_handletimeout), - fsparam_u64("snapshot", Opt_snapshot), - fsparam_u32("max_channels", Opt_max_channels), - - /* Mount options which take string value */ - fsparam_string("source", Opt_source), - fsparam_string("user", Opt_user), - fsparam_string("username", Opt_user), - fsparam_string("pass", Opt_pass), - fsparam_string("password", Opt_pass), - fsparam_string("ip", Opt_ip), - fsparam_string("addr", Opt_ip), - fsparam_string("domain", Opt_domain), - fsparam_string("dom", Opt_domain), - fsparam_string("srcaddr", Opt_srcaddr), - fsparam_string("iocharset", Opt_iocharset), - fsparam_string("netbiosname", Opt_netbiosname), - fsparam_string("servern", Opt_servern), - fsparam_string("ver", Opt_ver), - fsparam_string("vers", Opt_vers), - fsparam_string("sec", Opt_sec), - fsparam_string("cache", Opt_cache), - - /* Arguments that should be ignored */ - fsparam_flag("guest", Opt_ignore), - fsparam_flag("noatime", Opt_ignore), - fsparam_flag("relatime", Opt_ignore), - fsparam_flag("_netdev", Opt_ignore), - fsparam_flag_no("suid", Opt_ignore), - fsparam_flag_no("exec", Opt_ignore), - fsparam_flag_no("dev", Opt_ignore), - fsparam_flag_no("mand", Opt_ignore), - fsparam_flag_no("auto", Opt_ignore), - fsparam_string("cred", Opt_ignore), - fsparam_string("credentials", Opt_ignore), - /* - * UNC and prefixpath is now extracted from Opt_source - * in the new mount API so we can just ignore them going forward. - */ - fsparam_string("unc", Opt_ignore), - fsparam_string("prefixpath", Opt_ignore), - {} -}; - -static int -cifs_parse_security_flavors(struct fs_context *fc, char *value, struct smb3_fs_context *ctx) -{ - - substring_t args[MAX_OPT_ARGS]; - - /* - * With mount options, the last one should win. Reset any existing - * settings back to default. - */ - ctx->sectype = Unspecified; - ctx->sign = false; - - switch (match_token(value, cifs_secflavor_tokens, args)) { - case Opt_sec_krb5p: - cifs_errorf(fc, "sec=krb5p is not supported!\n"); - return 1; - case Opt_sec_krb5i: - ctx->sign = true; - fallthrough; - case Opt_sec_krb5: - ctx->sectype = Kerberos; - break; - case Opt_sec_ntlmsspi: - ctx->sign = true; - fallthrough; - case Opt_sec_ntlmssp: - ctx->sectype = RawNTLMSSP; - break; - case Opt_sec_ntlmv2i: - ctx->sign = true; - fallthrough; - case Opt_sec_ntlmv2: - ctx->sectype = NTLMv2; - break; - case Opt_sec_none: - ctx->nullauth = 1; - break; - default: - cifs_errorf(fc, "bad security option: %s\n", value); - return 1; - } - - return 0; -} - -static const match_table_t cifs_cacheflavor_tokens = { - { Opt_cache_loose, "loose" }, - { Opt_cache_strict, "strict" }, - { Opt_cache_none, "none" }, - { Opt_cache_ro, "ro" }, - { Opt_cache_rw, "singleclient" }, - { Opt_cache_err, NULL } -}; - -static int -cifs_parse_cache_flavor(struct fs_context *fc, char *value, struct smb3_fs_context *ctx) -{ - substring_t args[MAX_OPT_ARGS]; - - switch (match_token(value, cifs_cacheflavor_tokens, args)) { - case Opt_cache_loose: - ctx->direct_io = false; - ctx->strict_io = false; - ctx->cache_ro = false; - ctx->cache_rw = false; - break; - case Opt_cache_strict: - ctx->direct_io = false; - ctx->strict_io = true; - ctx->cache_ro = false; - ctx->cache_rw = false; - break; - case Opt_cache_none: - ctx->direct_io = true; - ctx->strict_io = false; - ctx->cache_ro = false; - ctx->cache_rw = false; - break; - case Opt_cache_ro: - ctx->direct_io = false; - ctx->strict_io = false; - ctx->cache_ro = true; - ctx->cache_rw = false; - break; - case Opt_cache_rw: - ctx->direct_io = false; - ctx->strict_io = false; - ctx->cache_ro = false; - ctx->cache_rw = true; - break; - default: - cifs_errorf(fc, "bad cache= option: %s\n", value); - return 1; - } - return 0; -} - -#define DUP_CTX_STR(field) \ -do { \ - if (ctx->field) { \ - new_ctx->field = kstrdup(ctx->field, GFP_ATOMIC); \ - if (new_ctx->field == NULL) { \ - smb3_cleanup_fs_context_contents(new_ctx); \ - return -ENOMEM; \ - } \ - } \ -} while (0) - -int -smb3_fs_context_dup(struct smb3_fs_context *new_ctx, struct smb3_fs_context *ctx) -{ - memcpy(new_ctx, ctx, sizeof(*ctx)); - new_ctx->prepath = NULL; - new_ctx->mount_options = NULL; - new_ctx->nodename = NULL; - new_ctx->username = NULL; - new_ctx->password = NULL; - new_ctx->server_hostname = NULL; - new_ctx->domainname = NULL; - new_ctx->UNC = NULL; - new_ctx->source = NULL; - new_ctx->iocharset = NULL; - /* - * Make sure to stay in sync with smb3_cleanup_fs_context_contents() - */ - DUP_CTX_STR(prepath); - DUP_CTX_STR(mount_options); - DUP_CTX_STR(username); - DUP_CTX_STR(password); - DUP_CTX_STR(server_hostname); - DUP_CTX_STR(UNC); - DUP_CTX_STR(source); - DUP_CTX_STR(domainname); - DUP_CTX_STR(nodename); - DUP_CTX_STR(iocharset); - - return 0; -} - -static int -cifs_parse_smb_version(struct fs_context *fc, char *value, struct smb3_fs_context *ctx, bool is_smb3) -{ - substring_t args[MAX_OPT_ARGS]; - - switch (match_token(value, cifs_smb_version_tokens, args)) { -#ifdef CONFIG_CIFS_ALLOW_INSECURE_LEGACY - case Smb_1: - if (disable_legacy_dialects) { - cifs_errorf(fc, "mount with legacy dialect disabled\n"); - return 1; - } - if (is_smb3) { - cifs_errorf(fc, "vers=1.0 (cifs) not permitted when mounting with smb3\n"); - return 1; - } - cifs_errorf(fc, "Use of the less secure dialect vers=1.0 is not recommended unless required for access to very old servers\n"); - ctx->ops = &smb1_operations; - ctx->vals = &smb1_values; - break; - case Smb_20: - if (disable_legacy_dialects) { - cifs_errorf(fc, "mount with legacy dialect disabled\n"); - return 1; - } - if (is_smb3) { - cifs_errorf(fc, "vers=2.0 not permitted when mounting with smb3\n"); - return 1; - } - ctx->ops = &smb20_operations; - ctx->vals = &smb20_values; - break; -#else - case Smb_1: - cifs_errorf(fc, "vers=1.0 (cifs) mount not permitted when legacy dialects disabled\n"); - return 1; - case Smb_20: - cifs_errorf(fc, "vers=2.0 mount not permitted when legacy dialects disabled\n"); - return 1; -#endif /* CIFS_ALLOW_INSECURE_LEGACY */ - case Smb_21: - ctx->ops = &smb21_operations; - ctx->vals = &smb21_values; - break; - case Smb_30: - ctx->ops = &smb30_operations; - ctx->vals = &smb30_values; - break; - case Smb_302: - ctx->ops = &smb30_operations; /* currently identical with 3.0 */ - ctx->vals = &smb302_values; - break; - case Smb_311: - ctx->ops = &smb311_operations; - ctx->vals = &smb311_values; - break; - case Smb_3any: - ctx->ops = &smb30_operations; /* currently identical with 3.0 */ - ctx->vals = &smb3any_values; - break; - case Smb_default: - ctx->ops = &smb30_operations; - ctx->vals = &smbdefault_values; - break; - default: - cifs_errorf(fc, "Unknown vers= option specified: %s\n", value); - return 1; - } - return 0; -} - -int smb3_parse_opt(const char *options, const char *key, char **val) -{ - int rc = -ENOENT; - char *opts, *orig, *p; - - orig = opts = kstrdup(options, GFP_KERNEL); - if (!opts) - return -ENOMEM; - - while ((p = strsep(&opts, ","))) { - char *nval; - - if (!*p) - continue; - if (strncasecmp(p, key, strlen(key))) - continue; - nval = strchr(p, '='); - if (nval) { - if (nval == p) - continue; - *nval++ = 0; - *val = kstrdup(nval, GFP_KERNEL); - rc = !*val ? -ENOMEM : 0; - goto out; - } - } -out: - kfree(orig); - return rc; -} - -/* - * Remove duplicate path delimiters. Windows is supposed to do that - * but there are some bugs that prevent rename from working if there are - * multiple delimiters. - * - * Returns a sanitized duplicate of @path. @gfp indicates the GFP_* flags - * for kstrdup. - * The caller is responsible for freeing the original. - */ -#define IS_DELIM(c) ((c) == '/' || (c) == '\\') -char *cifs_sanitize_prepath(char *prepath, gfp_t gfp) -{ - char *cursor1 = prepath, *cursor2 = prepath; - - /* skip all prepended delimiters */ - while (IS_DELIM(*cursor1)) - cursor1++; - - /* copy the first letter */ - *cursor2 = *cursor1; - - /* copy the remainder... */ - while (*(cursor1++)) { - /* ... skipping all duplicated delimiters */ - if (IS_DELIM(*cursor1) && IS_DELIM(*cursor2)) - continue; - *(++cursor2) = *cursor1; - } - - /* if the last character is a delimiter, skip it */ - if (IS_DELIM(*(cursor2 - 1))) - cursor2--; - - *(cursor2) = '\0'; - return kstrdup(prepath, gfp); -} - -/* - * Parse a devname into substrings and populate the ctx->UNC and ctx->prepath - * fields with the result. Returns 0 on success and an error otherwise - * (e.g. ENOMEM or EINVAL) - */ -int -smb3_parse_devname(const char *devname, struct smb3_fs_context *ctx) -{ - char *pos; - const char *delims = "/\\"; - size_t len; - - if (unlikely(!devname || !*devname)) { - cifs_dbg(VFS, "Device name not specified\n"); - return -EINVAL; - } - - /* make sure we have a valid UNC double delimiter prefix */ - len = strspn(devname, delims); - if (len != 2) - return -EINVAL; - - /* find delimiter between host and sharename */ - pos = strpbrk(devname + 2, delims); - if (!pos) - return -EINVAL; - - /* record the server hostname */ - kfree(ctx->server_hostname); - ctx->server_hostname = kstrndup(devname + 2, pos - devname - 2, GFP_KERNEL); - if (!ctx->server_hostname) - return -ENOMEM; - - /* skip past delimiter */ - ++pos; - - /* now go until next delimiter or end of string */ - len = strcspn(pos, delims); - - /* move "pos" up to delimiter or NULL */ - pos += len; - kfree(ctx->UNC); - ctx->UNC = kstrndup(devname, pos - devname, GFP_KERNEL); - if (!ctx->UNC) - return -ENOMEM; - - convert_delimiter(ctx->UNC, '\\'); - - /* skip any delimiter */ - if (*pos == '/' || *pos == '\\') - pos++; - - kfree(ctx->prepath); - ctx->prepath = NULL; - - /* If pos is NULL then no prepath */ - if (!*pos) - return 0; - - ctx->prepath = cifs_sanitize_prepath(pos, GFP_KERNEL); - if (!ctx->prepath) - return -ENOMEM; - - return 0; -} - -static void smb3_fs_context_free(struct fs_context *fc); -static int smb3_fs_context_parse_param(struct fs_context *fc, - struct fs_parameter *param); -static int smb3_fs_context_parse_monolithic(struct fs_context *fc, - void *data); -static int smb3_get_tree(struct fs_context *fc); -static int smb3_reconfigure(struct fs_context *fc); - -static const struct fs_context_operations smb3_fs_context_ops = { - .free = smb3_fs_context_free, - .parse_param = smb3_fs_context_parse_param, - .parse_monolithic = smb3_fs_context_parse_monolithic, - .get_tree = smb3_get_tree, - .reconfigure = smb3_reconfigure, -}; - -/* - * Parse a monolithic block of data from sys_mount(). - * smb3_fs_context_parse_monolithic - Parse key[=val][,key[=val]]* mount data - * @ctx: The superblock configuration to fill in. - * @data: The data to parse - * - * Parse a blob of data that's in key[=val][,key[=val]]* form. This can be - * called from the ->monolithic_mount_data() fs_context operation. - * - * Returns 0 on success or the error returned by the ->parse_option() fs_context - * operation on failure. - */ -static int smb3_fs_context_parse_monolithic(struct fs_context *fc, - void *data) -{ - struct smb3_fs_context *ctx = smb3_fc2context(fc); - char *options = data, *key; - int ret = 0; - - if (!options) - return 0; - - ctx->mount_options = kstrdup(data, GFP_KERNEL); - if (ctx->mount_options == NULL) - return -ENOMEM; - - ret = security_sb_eat_lsm_opts(options, &fc->security); - if (ret) - return ret; - - /* BB Need to add support for sep= here TBD */ - while ((key = strsep(&options, ",")) != NULL) { - size_t len; - char *value; - - if (*key == 0) - break; - - /* Check if following character is the deliminator If yes, - * we have encountered a double deliminator reset the NULL - * character to the deliminator - */ - while (options && options[0] == ',') { - len = strlen(key); - strcpy(key + len, options); - options = strchr(options, ','); - if (options) - *options++ = 0; - } - - - len = 0; - value = strchr(key, '='); - if (value) { - if (value == key) - continue; - *value++ = 0; - len = strlen(value); - } - - ret = vfs_parse_fs_string(fc, key, value, len); - if (ret < 0) - break; - } - - return ret; -} - -/* - * Validate the preparsed information in the config. - */ -static int smb3_fs_context_validate(struct fs_context *fc) -{ - struct smb3_fs_context *ctx = smb3_fc2context(fc); - - if (ctx->rdma && ctx->vals->protocol_id < SMB30_PROT_ID) { - cifs_errorf(fc, "SMB Direct requires Version >=3.0\n"); - return -EOPNOTSUPP; - } - -#ifndef CONFIG_KEYS - /* Muliuser mounts require CONFIG_KEYS support */ - if (ctx->multiuser) { - cifs_errorf(fc, "Multiuser mounts require kernels with CONFIG_KEYS enabled\n"); - return -1; - } -#endif - - if (ctx->got_version == false) - pr_warn_once("No dialect specified on mount. Default has changed to a more secure dialect, SMB2.1 or later (e.g. SMB3.1.1), from CIFS (SMB1). To use the less secure SMB1 dialect to access old servers which do not support SMB3.1.1 (or even SMB3 or SMB2.1) specify vers=1.0 on mount.\n"); - - - if (!ctx->UNC) { - cifs_errorf(fc, "CIFS mount error: No usable UNC path provided in device string!\n"); - return -1; - } - - /* make sure UNC has a share name */ - if (strlen(ctx->UNC) < 3 || !strchr(ctx->UNC + 3, '\\')) { - cifs_errorf(fc, "Malformed UNC. Unable to find share name.\n"); - return -ENOENT; - } - - if (!ctx->got_ip) { - int len; - const char *slash; - - /* No ip= option specified? Try to get it from UNC */ - /* Use the address part of the UNC. */ - slash = strchr(&ctx->UNC[2], '\\'); - len = slash - &ctx->UNC[2]; - if (!cifs_convert_address((struct sockaddr *)&ctx->dstaddr, - &ctx->UNC[2], len)) { - pr_err("Unable to determine destination address\n"); - return -EHOSTUNREACH; - } - } - - /* set the port that we got earlier */ - cifs_set_port((struct sockaddr *)&ctx->dstaddr, ctx->port); - - if (ctx->override_uid && !ctx->uid_specified) { - ctx->override_uid = 0; - pr_notice("ignoring forceuid mount option specified with no uid= option\n"); - } - - if (ctx->override_gid && !ctx->gid_specified) { - ctx->override_gid = 0; - pr_notice("ignoring forcegid mount option specified with no gid= option\n"); - } - - return 0; -} - -static int smb3_get_tree_common(struct fs_context *fc) -{ - struct smb3_fs_context *ctx = smb3_fc2context(fc); - struct dentry *root; - int rc = 0; - - root = cifs_smb3_do_mount(fc->fs_type, 0, ctx); - if (IS_ERR(root)) - return PTR_ERR(root); - - fc->root = root; - - return rc; -} - -/* - * Create an SMB3 superblock from the parameters passed. - */ -static int smb3_get_tree(struct fs_context *fc) -{ - int err = smb3_fs_context_validate(fc); - int ret; - - if (err) - return err; - mutex_lock(&cifs_mount_mutex); - ret = smb3_get_tree_common(fc); - mutex_unlock(&cifs_mount_mutex); - return ret; -} - -static void smb3_fs_context_free(struct fs_context *fc) -{ - struct smb3_fs_context *ctx = smb3_fc2context(fc); - - smb3_cleanup_fs_context(ctx); -} - -/* - * Compare the old and new proposed context during reconfigure - * and check if the changes are compatible. - */ -static int smb3_verify_reconfigure_ctx(struct fs_context *fc, - struct smb3_fs_context *new_ctx, - struct smb3_fs_context *old_ctx) -{ - if (new_ctx->posix_paths != old_ctx->posix_paths) { - cifs_errorf(fc, "can not change posixpaths during remount\n"); - return -EINVAL; - } - if (new_ctx->sectype != old_ctx->sectype) { - cifs_errorf(fc, "can not change sec during remount\n"); - return -EINVAL; - } - if (new_ctx->multiuser != old_ctx->multiuser) { - cifs_errorf(fc, "can not change multiuser during remount\n"); - return -EINVAL; - } - if (new_ctx->UNC && - (!old_ctx->UNC || strcmp(new_ctx->UNC, old_ctx->UNC))) { - cifs_errorf(fc, "can not change UNC during remount\n"); - return -EINVAL; - } - if (new_ctx->username && - (!old_ctx->username || strcmp(new_ctx->username, old_ctx->username))) { - cifs_errorf(fc, "can not change username during remount\n"); - return -EINVAL; - } - if (new_ctx->password && - (!old_ctx->password || strcmp(new_ctx->password, old_ctx->password))) { - cifs_errorf(fc, "can not change password during remount\n"); - return -EINVAL; - } - if (new_ctx->domainname && - (!old_ctx->domainname || strcmp(new_ctx->domainname, old_ctx->domainname))) { - cifs_errorf(fc, "can not change domainname during remount\n"); - return -EINVAL; - } - if (strcmp(new_ctx->workstation_name, old_ctx->workstation_name)) { - cifs_errorf(fc, "can not change workstation_name during remount\n"); - return -EINVAL; - } - if (new_ctx->nodename && - (!old_ctx->nodename || strcmp(new_ctx->nodename, old_ctx->nodename))) { - cifs_errorf(fc, "can not change nodename during remount\n"); - return -EINVAL; - } - if (new_ctx->iocharset && - (!old_ctx->iocharset || strcmp(new_ctx->iocharset, old_ctx->iocharset))) { - cifs_errorf(fc, "can not change iocharset during remount\n"); - return -EINVAL; - } - - return 0; -} - -#define STEAL_STRING(cifs_sb, ctx, field) \ -do { \ - kfree(ctx->field); \ - ctx->field = cifs_sb->ctx->field; \ - cifs_sb->ctx->field = NULL; \ -} while (0) - -#define STEAL_STRING_SENSITIVE(cifs_sb, ctx, field) \ -do { \ - kfree_sensitive(ctx->field); \ - ctx->field = cifs_sb->ctx->field; \ - cifs_sb->ctx->field = NULL; \ -} while (0) - -static int smb3_reconfigure(struct fs_context *fc) -{ - struct smb3_fs_context *ctx = smb3_fc2context(fc); - struct dentry *root = fc->root; - struct cifs_sb_info *cifs_sb = CIFS_SB(root->d_sb); - int rc; - - rc = smb3_verify_reconfigure_ctx(fc, ctx, cifs_sb->ctx); - if (rc) - return rc; - - /* - * We can not change UNC/username/password/domainname/ - * workstation_name/nodename/iocharset - * during reconnect so ignore what we have in the new context and - * just use what we already have in cifs_sb->ctx. - */ - STEAL_STRING(cifs_sb, ctx, UNC); - STEAL_STRING(cifs_sb, ctx, source); - STEAL_STRING(cifs_sb, ctx, username); - STEAL_STRING_SENSITIVE(cifs_sb, ctx, password); - STEAL_STRING(cifs_sb, ctx, domainname); - STEAL_STRING(cifs_sb, ctx, nodename); - STEAL_STRING(cifs_sb, ctx, iocharset); - - /* if rsize or wsize not passed in on remount, use previous values */ - if (ctx->rsize == 0) - ctx->rsize = cifs_sb->ctx->rsize; - if (ctx->wsize == 0) - ctx->wsize = cifs_sb->ctx->wsize; - - - smb3_cleanup_fs_context_contents(cifs_sb->ctx); - rc = smb3_fs_context_dup(cifs_sb->ctx, ctx); - smb3_update_mnt_flags(cifs_sb); -#ifdef CONFIG_CIFS_DFS_UPCALL - if (!rc) - rc = dfs_cache_remount_fs(cifs_sb); -#endif - - return rc; -} - -static int smb3_fs_context_parse_param(struct fs_context *fc, - struct fs_parameter *param) -{ - struct fs_parse_result result; - struct smb3_fs_context *ctx = smb3_fc2context(fc); - int i, opt; - bool is_smb3 = !strcmp(fc->fs_type->name, "smb3"); - bool skip_parsing = false; - kuid_t uid; - kgid_t gid; - - cifs_dbg(FYI, "CIFS: parsing cifs mount option '%s'\n", param->key); - - /* - * fs_parse can not handle string options with an empty value so - * we will need special handling of them. - */ - if (param->type == fs_value_is_string && param->string[0] == 0) { - if (!strcmp("pass", param->key) || !strcmp("password", param->key)) { - skip_parsing = true; - opt = Opt_pass; - } else if (!strcmp("user", param->key) || !strcmp("username", param->key)) { - skip_parsing = true; - opt = Opt_user; - } - } - - if (!skip_parsing) { - opt = fs_parse(fc, smb3_fs_parameters, param, &result); - if (opt < 0) - return ctx->sloppy ? 1 : opt; - } - - switch (opt) { - case Opt_compress: - ctx->compression = UNKNOWN_TYPE; - cifs_dbg(VFS, - "SMB3 compression support is experimental\n"); - break; - case Opt_nodfs: - ctx->nodfs = 1; - break; - case Opt_hard: - if (result.negated) - ctx->retry = 0; - else - ctx->retry = 1; - break; - case Opt_soft: - if (result.negated) - ctx->retry = 1; - else - ctx->retry = 0; - break; - case Opt_mapposix: - if (result.negated) - ctx->remap = false; - else { - ctx->remap = true; - ctx->sfu_remap = false; /* disable SFU mapping */ - } - break; - case Opt_mapchars: - if (result.negated) - ctx->sfu_remap = false; - else { - ctx->sfu_remap = true; - ctx->remap = false; /* disable SFM (mapposix) mapping */ - } - break; - case Opt_user_xattr: - if (result.negated) - ctx->no_xattr = 1; - else - ctx->no_xattr = 0; - break; - case Opt_forceuid: - if (result.negated) - ctx->override_uid = 0; - else - ctx->override_uid = 1; - break; - case Opt_forcegid: - if (result.negated) - ctx->override_gid = 0; - else - ctx->override_gid = 1; - break; - case Opt_perm: - if (result.negated) - ctx->noperm = 1; - else - ctx->noperm = 0; - break; - case Opt_dynperm: - if (result.negated) - ctx->dynperm = 0; - else - ctx->dynperm = 1; - break; - case Opt_sfu: - if (result.negated) - ctx->sfu_emul = 0; - else - ctx->sfu_emul = 1; - break; - case Opt_noblocksend: - ctx->noblocksnd = 1; - break; - case Opt_noautotune: - ctx->noautotune = 1; - break; - case Opt_nolease: - ctx->no_lease = 1; - break; - case Opt_nosparse: - ctx->no_sparse = 1; - break; - case Opt_nodelete: - ctx->nodelete = 1; - break; - case Opt_multichannel: - if (result.negated) { - ctx->multichannel = false; - ctx->max_channels = 1; - } else { - ctx->multichannel = true; - /* if number of channels not specified, default to 2 */ - if (ctx->max_channels < 2) - ctx->max_channels = 2; - } - break; - case Opt_uid: - uid = make_kuid(current_user_ns(), result.uint_32); - if (!uid_valid(uid)) - goto cifs_parse_mount_err; - ctx->linux_uid = uid; - ctx->uid_specified = true; - break; - case Opt_cruid: - uid = make_kuid(current_user_ns(), result.uint_32); - if (!uid_valid(uid)) - goto cifs_parse_mount_err; - ctx->cred_uid = uid; - ctx->cruid_specified = true; - break; - case Opt_backupuid: - uid = make_kuid(current_user_ns(), result.uint_32); - if (!uid_valid(uid)) - goto cifs_parse_mount_err; - ctx->backupuid = uid; - ctx->backupuid_specified = true; - break; - case Opt_backupgid: - gid = make_kgid(current_user_ns(), result.uint_32); - if (!gid_valid(gid)) - goto cifs_parse_mount_err; - ctx->backupgid = gid; - ctx->backupgid_specified = true; - break; - case Opt_gid: - gid = make_kgid(current_user_ns(), result.uint_32); - if (!gid_valid(gid)) - goto cifs_parse_mount_err; - ctx->linux_gid = gid; - ctx->gid_specified = true; - break; - case Opt_port: - ctx->port = result.uint_32; - break; - case Opt_file_mode: - ctx->file_mode = result.uint_32; - break; - case Opt_dirmode: - ctx->dir_mode = result.uint_32; - break; - case Opt_min_enc_offload: - ctx->min_offload = result.uint_32; - break; - case Opt_blocksize: - /* - * inode blocksize realistically should never need to be - * less than 16K or greater than 16M and default is 1MB. - * Note that small inode block sizes (e.g. 64K) can lead - * to very poor performance of common tools like cp and scp - */ - if ((result.uint_32 < CIFS_MAX_MSGSIZE) || - (result.uint_32 > (4 * SMB3_DEFAULT_IOSIZE))) { - cifs_errorf(fc, "%s: Invalid blocksize\n", - __func__); - goto cifs_parse_mount_err; - } - ctx->bsize = result.uint_32; - ctx->got_bsize = true; - break; - case Opt_rasize: - /* - * readahead size realistically should never need to be - * less than 1M (CIFS_DEFAULT_IOSIZE) or greater than 32M - * (perhaps an exception should be considered in the - * for the case of a large number of channels - * when multichannel is negotiated) since that would lead - * to plenty of parallel I/O in flight to the server. - * Note that smaller read ahead sizes would - * hurt performance of common tools like cp and scp - * which often trigger sequential i/o with read ahead - */ - if ((result.uint_32 > (8 * SMB3_DEFAULT_IOSIZE)) || - (result.uint_32 < CIFS_DEFAULT_IOSIZE)) { - cifs_errorf(fc, "%s: Invalid rasize %d vs. %d\n", - __func__, result.uint_32, SMB3_DEFAULT_IOSIZE); - goto cifs_parse_mount_err; - } - ctx->rasize = result.uint_32; - break; - case Opt_rsize: - ctx->rsize = result.uint_32; - ctx->got_rsize = true; - break; - case Opt_wsize: - ctx->wsize = result.uint_32; - ctx->got_wsize = true; - break; - case Opt_acregmax: - ctx->acregmax = HZ * result.uint_32; - if (ctx->acregmax > CIFS_MAX_ACTIMEO) { - cifs_errorf(fc, "acregmax too large\n"); - goto cifs_parse_mount_err; - } - break; - case Opt_acdirmax: - ctx->acdirmax = HZ * result.uint_32; - if (ctx->acdirmax > CIFS_MAX_ACTIMEO) { - cifs_errorf(fc, "acdirmax too large\n"); - goto cifs_parse_mount_err; - } - break; - case Opt_actimeo: - if (HZ * result.uint_32 > CIFS_MAX_ACTIMEO) { - cifs_errorf(fc, "timeout too large\n"); - goto cifs_parse_mount_err; - } - if ((ctx->acdirmax != CIFS_DEF_ACTIMEO) || - (ctx->acregmax != CIFS_DEF_ACTIMEO)) { - cifs_errorf(fc, "actimeo ignored since acregmax or acdirmax specified\n"); - break; - } - ctx->acdirmax = ctx->acregmax = HZ * result.uint_32; - break; - case Opt_closetimeo: - ctx->closetimeo = HZ * result.uint_32; - if (ctx->closetimeo > SMB3_MAX_DCLOSETIMEO) { - cifs_errorf(fc, "closetimeo too large\n"); - goto cifs_parse_mount_err; - } - break; - case Opt_echo_interval: - ctx->echo_interval = result.uint_32; - break; - case Opt_snapshot: - ctx->snapshot_time = result.uint_64; - break; - case Opt_max_credits: - if (result.uint_32 < 20 || result.uint_32 > 60000) { - cifs_errorf(fc, "%s: Invalid max_credits value\n", - __func__); - goto cifs_parse_mount_err; - } - ctx->max_credits = result.uint_32; - break; - case Opt_max_channels: - if (result.uint_32 < 1 || result.uint_32 > CIFS_MAX_CHANNELS) { - cifs_errorf(fc, "%s: Invalid max_channels value, needs to be 1-%d\n", - __func__, CIFS_MAX_CHANNELS); - goto cifs_parse_mount_err; - } - ctx->max_channels = result.uint_32; - /* If more than one channel requested ... they want multichan */ - if (result.uint_32 > 1) - ctx->multichannel = true; - break; - case Opt_handletimeout: - ctx->handle_timeout = result.uint_32; - if (ctx->handle_timeout > SMB3_MAX_HANDLE_TIMEOUT) { - cifs_errorf(fc, "Invalid handle cache timeout, longer than 16 minutes\n"); - goto cifs_parse_mount_err; - } - break; - case Opt_source: - kfree(ctx->UNC); - ctx->UNC = NULL; - switch (smb3_parse_devname(param->string, ctx)) { - case 0: - break; - case -ENOMEM: - cifs_errorf(fc, "Unable to allocate memory for devname\n"); - goto cifs_parse_mount_err; - case -EINVAL: - cifs_errorf(fc, "Malformed UNC in devname\n"); - goto cifs_parse_mount_err; - default: - cifs_errorf(fc, "Unknown error parsing devname\n"); - goto cifs_parse_mount_err; - } - ctx->source = kstrdup(param->string, GFP_KERNEL); - if (ctx->source == NULL) { - cifs_errorf(fc, "OOM when copying UNC string\n"); - goto cifs_parse_mount_err; - } - fc->source = kstrdup(param->string, GFP_KERNEL); - if (fc->source == NULL) { - cifs_errorf(fc, "OOM when copying UNC string\n"); - goto cifs_parse_mount_err; - } - break; - case Opt_user: - kfree(ctx->username); - ctx->username = NULL; - if (strlen(param->string) == 0) { - /* null user, ie. anonymous authentication */ - ctx->nullauth = 1; - break; - } - - if (strnlen(param->string, CIFS_MAX_USERNAME_LEN) > - CIFS_MAX_USERNAME_LEN) { - pr_warn("username too long\n"); - goto cifs_parse_mount_err; - } - ctx->username = kstrdup(param->string, GFP_KERNEL); - if (ctx->username == NULL) { - cifs_errorf(fc, "OOM when copying username string\n"); - goto cifs_parse_mount_err; - } - break; - case Opt_pass: - kfree_sensitive(ctx->password); - ctx->password = NULL; - if (strlen(param->string) == 0) - break; - - ctx->password = kstrdup(param->string, GFP_KERNEL); - if (ctx->password == NULL) { - cifs_errorf(fc, "OOM when copying password string\n"); - goto cifs_parse_mount_err; - } - break; - case Opt_ip: - if (strlen(param->string) == 0) { - ctx->got_ip = false; - break; - } - if (!cifs_convert_address((struct sockaddr *)&ctx->dstaddr, - param->string, - strlen(param->string))) { - pr_err("bad ip= option (%s)\n", param->string); - goto cifs_parse_mount_err; - } - ctx->got_ip = true; - break; - case Opt_domain: - if (strnlen(param->string, CIFS_MAX_DOMAINNAME_LEN) - == CIFS_MAX_DOMAINNAME_LEN) { - pr_warn("domain name too long\n"); - goto cifs_parse_mount_err; - } - - kfree(ctx->domainname); - ctx->domainname = kstrdup(param->string, GFP_KERNEL); - if (ctx->domainname == NULL) { - cifs_errorf(fc, "OOM when copying domainname string\n"); - goto cifs_parse_mount_err; - } - cifs_dbg(FYI, "Domain name set\n"); - break; - case Opt_srcaddr: - if (!cifs_convert_address( - (struct sockaddr *)&ctx->srcaddr, - param->string, strlen(param->string))) { - pr_warn("Could not parse srcaddr: %s\n", - param->string); - goto cifs_parse_mount_err; - } - break; - case Opt_iocharset: - if (strnlen(param->string, 1024) >= 65) { - pr_warn("iocharset name too long\n"); - goto cifs_parse_mount_err; - } - - if (strncasecmp(param->string, "default", 7) != 0) { - kfree(ctx->iocharset); - ctx->iocharset = kstrdup(param->string, GFP_KERNEL); - if (ctx->iocharset == NULL) { - cifs_errorf(fc, "OOM when copying iocharset string\n"); - goto cifs_parse_mount_err; - } - } - /* if iocharset not set then load_nls_default - * is used by caller - */ - cifs_dbg(FYI, "iocharset set to %s\n", ctx->iocharset); - break; - case Opt_netbiosname: - memset(ctx->source_rfc1001_name, 0x20, - RFC1001_NAME_LEN); - /* - * FIXME: are there cases in which a comma can - * be valid in workstation netbios name (and - * need special handling)? - */ - for (i = 0; i < RFC1001_NAME_LEN; i++) { - /* don't ucase netbiosname for user */ - if (param->string[i] == 0) - break; - ctx->source_rfc1001_name[i] = param->string[i]; - } - /* The string has 16th byte zero still from - * set at top of the function - */ - if (i == RFC1001_NAME_LEN && param->string[i] != 0) - pr_warn("netbiosname longer than 15 truncated\n"); - break; - case Opt_servern: - /* last byte, type, is 0x20 for servr type */ - memset(ctx->target_rfc1001_name, 0x20, - RFC1001_NAME_LEN_WITH_NULL); - /* - * 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 the netbios name */ - for (i = 0; i < 15; i++) { - if (param->string[i] == 0) - break; - ctx->target_rfc1001_name[i] = param->string[i]; - } - - /* The string has 16th byte zero still from set at top of function */ - if (i == RFC1001_NAME_LEN && param->string[i] != 0) - pr_warn("server netbiosname longer than 15 truncated\n"); - break; - case Opt_ver: - /* version of mount userspace tools, not dialect */ - /* If interface changes in mount.cifs bump to new ver */ - if (strncasecmp(param->string, "1", 1) == 0) { - if (strlen(param->string) > 1) { - pr_warn("Bad mount helper ver=%s. Did you want SMB1 (CIFS) dialect and mean to type vers=1.0 instead?\n", - param->string); - goto cifs_parse_mount_err; - } - /* This is the default */ - break; - } - /* For all other value, error */ - pr_warn("Invalid mount helper version specified\n"); - goto cifs_parse_mount_err; - case Opt_vers: - /* protocol version (dialect) */ - if (cifs_parse_smb_version(fc, param->string, ctx, is_smb3) != 0) - goto cifs_parse_mount_err; - ctx->got_version = true; - break; - case Opt_sec: - if (cifs_parse_security_flavors(fc, param->string, ctx) != 0) - goto cifs_parse_mount_err; - break; - case Opt_cache: - if (cifs_parse_cache_flavor(fc, param->string, ctx) != 0) - goto cifs_parse_mount_err; - break; - case Opt_witness: -#ifndef CONFIG_CIFS_SWN_UPCALL - cifs_errorf(fc, "Witness support needs CONFIG_CIFS_SWN_UPCALL config option\n"); - goto cifs_parse_mount_err; -#endif - ctx->witness = true; - pr_warn_once("Witness protocol support is experimental\n"); - break; - case Opt_rootfs: -#ifndef CONFIG_CIFS_ROOT - cifs_dbg(VFS, "rootfs support requires CONFIG_CIFS_ROOT config option\n"); - goto cifs_parse_mount_err; -#endif - ctx->rootfs = true; - break; - case Opt_posixpaths: - if (result.negated) - ctx->posix_paths = 0; - else - ctx->posix_paths = 1; - break; - case Opt_unix: - if (result.negated) { - if (ctx->linux_ext == 1) - pr_warn_once("conflicting posix mount options specified\n"); - ctx->linux_ext = 0; - ctx->no_linux_ext = 1; - } else { - if (ctx->no_linux_ext == 1) - pr_warn_once("conflicting posix mount options specified\n"); - ctx->linux_ext = 1; - ctx->no_linux_ext = 0; - } - break; - case Opt_nocase: - ctx->nocase = 1; - break; - case Opt_brl: - if (result.negated) { - /* - * turn off mandatory locking in mode - * if remote locking is turned off since the - * local vfs will do advisory - */ - if (ctx->file_mode == - (S_IALLUGO & ~(S_ISUID | S_IXGRP))) - ctx->file_mode = S_IALLUGO; - ctx->nobrl = 1; - } else - ctx->nobrl = 0; - break; - case Opt_handlecache: - if (result.negated) - ctx->nohandlecache = 1; - else - ctx->nohandlecache = 0; - break; - case Opt_forcemandatorylock: - ctx->mand_lock = 1; - break; - case Opt_setuids: - ctx->setuids = result.negated; - break; - case Opt_intr: - ctx->intr = !result.negated; - break; - case Opt_setuidfromacl: - ctx->setuidfromacl = 1; - break; - case Opt_strictsync: - ctx->nostrictsync = result.negated; - break; - case Opt_serverino: - ctx->server_ino = !result.negated; - break; - case Opt_rwpidforward: - ctx->rwpidforward = 1; - break; - case Opt_modesid: - ctx->mode_ace = 1; - break; - case Opt_cifsacl: - ctx->cifs_acl = !result.negated; - break; - case Opt_acl: - ctx->no_psx_acl = result.negated; - break; - case Opt_locallease: - ctx->local_lease = 1; - break; - case Opt_sign: - ctx->sign = true; - break; - case Opt_ignore_signature: - ctx->sign = true; - ctx->ignore_signature = true; - break; - case Opt_seal: - /* we do not do the following in secFlags because seal - * is a per tree connection (mount) not a per socket - * or per-smb connection option in the protocol - * vol->secFlg |= CIFSSEC_MUST_SEAL; - */ - ctx->seal = 1; - break; - case Opt_noac: - pr_warn("Mount option noac not supported. Instead set /proc/fs/cifs/LookupCacheEnabled to 0\n"); - break; - case Opt_fsc: -#ifndef CONFIG_CIFS_FSCACHE - cifs_errorf(fc, "FS-Cache support needs CONFIG_CIFS_FSCACHE kernel config option set\n"); - goto cifs_parse_mount_err; -#endif - ctx->fsc = true; - break; - case Opt_mfsymlinks: - ctx->mfsymlinks = true; - break; - case Opt_multiuser: - ctx->multiuser = true; - break; - case Opt_sloppy: - ctx->sloppy = true; - break; - case Opt_nosharesock: - ctx->nosharesock = true; - break; - case Opt_persistent: - if (result.negated) { - ctx->nopersistent = true; - if (ctx->persistent) { - cifs_errorf(fc, "persistenthandles mount options conflict\n"); - goto cifs_parse_mount_err; - } - } else { - ctx->persistent = true; - if ((ctx->nopersistent) || (ctx->resilient)) { - cifs_errorf(fc, "persistenthandles mount options conflict\n"); - goto cifs_parse_mount_err; - } - } - break; - case Opt_resilient: - if (result.negated) { - ctx->resilient = false; /* already the default */ - } else { - ctx->resilient = true; - if (ctx->persistent) { - cifs_errorf(fc, "persistenthandles mount options conflict\n"); - goto cifs_parse_mount_err; - } - } - break; - case Opt_tcp_nodelay: - /* tcp nodelay should not usually be needed since we CORK/UNCORK the socket */ - if (result.negated) - ctx->sockopt_tcp_nodelay = false; - else - ctx->sockopt_tcp_nodelay = true; - break; - case Opt_domainauto: - ctx->domainauto = true; - break; - case Opt_rdma: - ctx->rdma = true; - break; - } - /* case Opt_ignore: - is ignored as expected ... */ - - return 0; - - cifs_parse_mount_err: - kfree_sensitive(ctx->password); - return -EINVAL; -} - -int smb3_init_fs_context(struct fs_context *fc) -{ - struct smb3_fs_context *ctx; - char *nodename = utsname()->nodename; - int i; - - ctx = kzalloc(sizeof(struct smb3_fs_context), GFP_KERNEL); - if (unlikely(!ctx)) - return -ENOMEM; - - strscpy(ctx->workstation_name, nodename, sizeof(ctx->workstation_name)); - - /* - * does not have to be perfect mapping since field is - * informational, only used for servers that do not support - * port 445 and it can be overridden at mount time - */ - memset(ctx->source_rfc1001_name, 0x20, RFC1001_NAME_LEN); - for (i = 0; i < strnlen(nodename, RFC1001_NAME_LEN); i++) - ctx->source_rfc1001_name[i] = toupper(nodename[i]); - - ctx->source_rfc1001_name[RFC1001_NAME_LEN] = 0; - /* - * null target name indicates to use *SMBSERVR default called name - * if we end up sending RFC1001 session initialize - */ - ctx->target_rfc1001_name[0] = 0; - ctx->cred_uid = current_uid(); - ctx->linux_uid = current_uid(); - ctx->linux_gid = current_gid(); - /* By default 4MB read ahead size, 1MB block size */ - ctx->bsize = CIFS_DEFAULT_IOSIZE; /* can improve cp performance significantly */ - ctx->rasize = 0; /* 0 = use default (ie negotiated rsize) for read ahead pages */ - - /* - * default to SFM style remapping of seven reserved characters - * unless user overrides it or we negotiate CIFS POSIX where - * it is unnecessary. Can not simultaneously use more than one mapping - * since then readdir could list files that open could not open - */ - ctx->remap = true; - - /* default to only allowing write access to owner of the mount */ - ctx->dir_mode = ctx->file_mode = S_IRUGO | S_IXUGO | S_IWUSR; - - /* ctx->retry default is 0 (i.e. "soft" limited retry not hard retry) */ - /* default is always to request posix paths. */ - ctx->posix_paths = 1; - /* default to using server inode numbers where available */ - ctx->server_ino = 1; - - /* default is to use strict cifs caching semantics */ - ctx->strict_io = true; - - ctx->acregmax = CIFS_DEF_ACTIMEO; - ctx->acdirmax = CIFS_DEF_ACTIMEO; - ctx->closetimeo = SMB3_DEF_DCLOSETIMEO; - - /* Most clients set timeout to 0, allows server to use its default */ - ctx->handle_timeout = 0; /* See MS-SMB2 spec section 2.2.14.2.12 */ - - /* offer SMB2.1 and later (SMB3 etc). Secure and widely accepted */ - ctx->ops = &smb30_operations; - ctx->vals = &smbdefault_values; - - ctx->echo_interval = SMB_ECHO_INTERVAL_DEFAULT; - - /* default to no multichannel (single server connection) */ - ctx->multichannel = false; - ctx->max_channels = 1; - - ctx->backupuid_specified = false; /* no backup intent for a user */ - ctx->backupgid_specified = false; /* no backup intent for a group */ - -/* - * short int override_uid = -1; - * short int override_gid = -1; - * char *nodename = strdup(utsname()->nodename); - * struct sockaddr *dstaddr = (struct sockaddr *)&vol->dstaddr; - */ - - fc->fs_private = ctx; - fc->ops = &smb3_fs_context_ops; - return 0; -} - -void -smb3_cleanup_fs_context_contents(struct smb3_fs_context *ctx) -{ - if (ctx == NULL) - return; - - /* - * Make sure this stays in sync with smb3_fs_context_dup() - */ - kfree(ctx->mount_options); - ctx->mount_options = NULL; - kfree(ctx->username); - ctx->username = NULL; - kfree_sensitive(ctx->password); - ctx->password = NULL; - kfree(ctx->server_hostname); - ctx->server_hostname = NULL; - kfree(ctx->UNC); - ctx->UNC = NULL; - kfree(ctx->source); - ctx->source = NULL; - kfree(ctx->domainname); - ctx->domainname = NULL; - kfree(ctx->nodename); - ctx->nodename = NULL; - kfree(ctx->iocharset); - ctx->iocharset = NULL; - kfree(ctx->prepath); - ctx->prepath = NULL; -} - -void -smb3_cleanup_fs_context(struct smb3_fs_context *ctx) -{ - if (!ctx) - return; - smb3_cleanup_fs_context_contents(ctx); - kfree(ctx); -} - -void smb3_update_mnt_flags(struct cifs_sb_info *cifs_sb) -{ - struct smb3_fs_context *ctx = cifs_sb->ctx; - - if (ctx->nodfs) - cifs_sb->mnt_cifs_flags |= CIFS_MOUNT_NO_DFS; - else - cifs_sb->mnt_cifs_flags &= ~CIFS_MOUNT_NO_DFS; - - if (ctx->noperm) - cifs_sb->mnt_cifs_flags |= CIFS_MOUNT_NO_PERM; - else - cifs_sb->mnt_cifs_flags &= ~CIFS_MOUNT_NO_PERM; - - if (ctx->setuids) - cifs_sb->mnt_cifs_flags |= CIFS_MOUNT_SET_UID; - else - cifs_sb->mnt_cifs_flags &= ~CIFS_MOUNT_SET_UID; - - if (ctx->setuidfromacl) - cifs_sb->mnt_cifs_flags |= CIFS_MOUNT_UID_FROM_ACL; - else - cifs_sb->mnt_cifs_flags &= ~CIFS_MOUNT_UID_FROM_ACL; - - if (ctx->server_ino) - cifs_sb->mnt_cifs_flags |= CIFS_MOUNT_SERVER_INUM; - else - cifs_sb->mnt_cifs_flags &= ~CIFS_MOUNT_SERVER_INUM; - - if (ctx->remap) - cifs_sb->mnt_cifs_flags |= CIFS_MOUNT_MAP_SFM_CHR; - else - cifs_sb->mnt_cifs_flags &= ~CIFS_MOUNT_MAP_SFM_CHR; - - if (ctx->sfu_remap) - cifs_sb->mnt_cifs_flags |= CIFS_MOUNT_MAP_SPECIAL_CHR; - else - cifs_sb->mnt_cifs_flags &= ~CIFS_MOUNT_MAP_SPECIAL_CHR; - - if (ctx->no_xattr) - cifs_sb->mnt_cifs_flags |= CIFS_MOUNT_NO_XATTR; - else - cifs_sb->mnt_cifs_flags &= ~CIFS_MOUNT_NO_XATTR; - - if (ctx->sfu_emul) - cifs_sb->mnt_cifs_flags |= CIFS_MOUNT_UNX_EMUL; - else - cifs_sb->mnt_cifs_flags &= ~CIFS_MOUNT_UNX_EMUL; - - if (ctx->nobrl) - cifs_sb->mnt_cifs_flags |= CIFS_MOUNT_NO_BRL; - else - cifs_sb->mnt_cifs_flags &= ~CIFS_MOUNT_NO_BRL; - - if (ctx->nohandlecache) - cifs_sb->mnt_cifs_flags |= CIFS_MOUNT_NO_HANDLE_CACHE; - else - cifs_sb->mnt_cifs_flags &= ~CIFS_MOUNT_NO_HANDLE_CACHE; - - if (ctx->nostrictsync) - cifs_sb->mnt_cifs_flags |= CIFS_MOUNT_NOSSYNC; - else - cifs_sb->mnt_cifs_flags &= ~CIFS_MOUNT_NOSSYNC; - - if (ctx->mand_lock) - cifs_sb->mnt_cifs_flags |= CIFS_MOUNT_NOPOSIXBRL; - else - cifs_sb->mnt_cifs_flags &= ~CIFS_MOUNT_NOPOSIXBRL; - - if (ctx->rwpidforward) - cifs_sb->mnt_cifs_flags |= CIFS_MOUNT_RWPIDFORWARD; - else - cifs_sb->mnt_cifs_flags &= ~CIFS_MOUNT_RWPIDFORWARD; - - if (ctx->mode_ace) - cifs_sb->mnt_cifs_flags |= CIFS_MOUNT_MODE_FROM_SID; - else - cifs_sb->mnt_cifs_flags &= ~CIFS_MOUNT_MODE_FROM_SID; - - if (ctx->cifs_acl) - cifs_sb->mnt_cifs_flags |= CIFS_MOUNT_CIFS_ACL; - else - cifs_sb->mnt_cifs_flags &= ~CIFS_MOUNT_CIFS_ACL; - - if (ctx->backupuid_specified) - cifs_sb->mnt_cifs_flags |= CIFS_MOUNT_CIFS_BACKUPUID; - else - cifs_sb->mnt_cifs_flags &= ~CIFS_MOUNT_CIFS_BACKUPUID; - - if (ctx->backupgid_specified) - cifs_sb->mnt_cifs_flags |= CIFS_MOUNT_CIFS_BACKUPGID; - else - cifs_sb->mnt_cifs_flags &= ~CIFS_MOUNT_CIFS_BACKUPGID; - - if (ctx->override_uid) - cifs_sb->mnt_cifs_flags |= CIFS_MOUNT_OVERR_UID; - else - cifs_sb->mnt_cifs_flags &= ~CIFS_MOUNT_OVERR_UID; - - if (ctx->override_gid) - cifs_sb->mnt_cifs_flags |= CIFS_MOUNT_OVERR_GID; - else - cifs_sb->mnt_cifs_flags &= ~CIFS_MOUNT_OVERR_GID; - - if (ctx->dynperm) - cifs_sb->mnt_cifs_flags |= CIFS_MOUNT_DYNPERM; - else - cifs_sb->mnt_cifs_flags &= ~CIFS_MOUNT_DYNPERM; - - if (ctx->fsc) - cifs_sb->mnt_cifs_flags |= CIFS_MOUNT_FSCACHE; - else - cifs_sb->mnt_cifs_flags &= ~CIFS_MOUNT_FSCACHE; - - if (ctx->multiuser) - cifs_sb->mnt_cifs_flags |= (CIFS_MOUNT_MULTIUSER | - CIFS_MOUNT_NO_PERM); - else - cifs_sb->mnt_cifs_flags &= ~CIFS_MOUNT_MULTIUSER; - - - if (ctx->strict_io) - cifs_sb->mnt_cifs_flags |= CIFS_MOUNT_STRICT_IO; - else - cifs_sb->mnt_cifs_flags &= ~CIFS_MOUNT_STRICT_IO; - - if (ctx->direct_io) - cifs_sb->mnt_cifs_flags |= CIFS_MOUNT_DIRECT_IO; - else - cifs_sb->mnt_cifs_flags &= ~CIFS_MOUNT_DIRECT_IO; - - if (ctx->mfsymlinks) - cifs_sb->mnt_cifs_flags |= CIFS_MOUNT_MF_SYMLINKS; - else - cifs_sb->mnt_cifs_flags &= ~CIFS_MOUNT_MF_SYMLINKS; - if (ctx->mfsymlinks) { - if (ctx->sfu_emul) { - /* - * Our SFU ("Services for Unix" emulation does not allow - * creating symlinks but does allow reading existing SFU - * symlinks (it does allow both creating and reading SFU - * style mknod and FIFOs though). When "mfsymlinks" and - * "sfu" are both enabled at the same time, it allows - * reading both types of symlinks, but will only create - * them with mfsymlinks format. This allows better - * Apple compatibility (probably better for Samba too) - * while still recognizing old Windows style symlinks. - */ - cifs_dbg(VFS, "mount options mfsymlinks and sfu both enabled\n"); - } - } - cifs_sb->mnt_cifs_flags &= ~CIFS_MOUNT_SHUTDOWN; - - return; -} diff --git a/fs/cifs/fs_context.h b/fs/cifs/fs_context.h deleted file mode 100644 index 26093f54d3e65..0000000000000 --- a/fs/cifs/fs_context.h +++ /dev/null @@ -1,293 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0-or-later */ -/* - * Copyright (C) 2020, Microsoft Corporation. - * - * Author(s): Steve French - * David Howells - */ - -#ifndef _FS_CONTEXT_H -#define _FS_CONTEXT_H - -#include "cifsglob.h" -#include -#include - -/* Log errors in fs_context (new mount api) but also in dmesg (old style) */ -#define cifs_errorf(fc, fmt, ...) \ - do { \ - errorf(fc, fmt, ## __VA_ARGS__); \ - cifs_dbg(VFS, fmt, ## __VA_ARGS__); \ - } while (0) - -enum smb_version { - Smb_1 = 1, - Smb_20, - Smb_21, - Smb_30, - Smb_302, - Smb_311, - Smb_3any, - Smb_default, - Smb_version_err -}; - -enum { - Opt_cache_loose, - Opt_cache_strict, - Opt_cache_none, - Opt_cache_ro, - Opt_cache_rw, - Opt_cache_err -}; - -enum cifs_sec_param { - Opt_sec_krb5, - Opt_sec_krb5i, - Opt_sec_krb5p, - Opt_sec_ntlmsspi, - Opt_sec_ntlmssp, - Opt_sec_ntlmv2, - Opt_sec_ntlmv2i, - Opt_sec_none, - - Opt_sec_err -}; - -enum cifs_param { - /* Mount options that take no arguments */ - Opt_user_xattr, - Opt_forceuid, - Opt_forcegid, - Opt_noblocksend, - Opt_noautotune, - Opt_nolease, - Opt_nosparse, - Opt_hard, - Opt_soft, - Opt_perm, - Opt_nodelete, - Opt_mapposix, - Opt_mapchars, - Opt_nomapchars, - Opt_sfu, - Opt_nodfs, - Opt_posixpaths, - Opt_unix, - Opt_nocase, - Opt_brl, - Opt_handlecache, - Opt_forcemandatorylock, - Opt_setuidfromacl, - Opt_setuids, - Opt_dynperm, - Opt_intr, - Opt_strictsync, - Opt_serverino, - Opt_rwpidforward, - Opt_cifsacl, - Opt_acl, - Opt_locallease, - Opt_sign, - Opt_ignore_signature, - Opt_seal, - Opt_noac, - Opt_fsc, - Opt_mfsymlinks, - Opt_multiuser, - Opt_sloppy, - Opt_nosharesock, - Opt_persistent, - Opt_resilient, - Opt_tcp_nodelay, - Opt_domainauto, - Opt_rdma, - Opt_modesid, - Opt_rootfs, - Opt_multichannel, - Opt_compress, - Opt_witness, - - /* Mount options which take numeric value */ - Opt_backupuid, - Opt_backupgid, - Opt_uid, - Opt_cruid, - Opt_gid, - Opt_port, - Opt_file_mode, - Opt_dirmode, - Opt_min_enc_offload, - Opt_blocksize, - Opt_rasize, - Opt_rsize, - Opt_wsize, - Opt_actimeo, - Opt_acdirmax, - Opt_acregmax, - Opt_closetimeo, - Opt_echo_interval, - Opt_max_credits, - Opt_snapshot, - Opt_max_channels, - Opt_handletimeout, - - /* Mount options which take string value */ - Opt_source, - Opt_user, - Opt_pass, - Opt_ip, - Opt_domain, - Opt_srcaddr, - Opt_iocharset, - Opt_netbiosname, - Opt_servern, - Opt_ver, - Opt_vers, - Opt_sec, - Opt_cache, - - /* Mount options to be ignored */ - Opt_ignore, - - Opt_err -}; - -struct smb3_fs_context { - bool uid_specified; - bool cruid_specified; - bool gid_specified; - bool sloppy; - bool got_ip; - bool got_version; - bool got_rsize; - bool got_wsize; - bool got_bsize; - unsigned short port; - - char *username; - char *password; - char *domainname; - char *source; - char *server_hostname; - char *UNC; - char *nodename; - char workstation_name[CIFS_MAX_WORKSTATION_LEN]; - char *iocharset; /* local code page for mapping to and from Unicode */ - char source_rfc1001_name[RFC1001_NAME_LEN_WITH_NULL]; /* clnt nb name */ - char target_rfc1001_name[RFC1001_NAME_LEN_WITH_NULL]; /* srvr nb name */ - kuid_t cred_uid; - kuid_t linux_uid; - kgid_t linux_gid; - kuid_t backupuid; - kgid_t backupgid; - umode_t file_mode; - umode_t dir_mode; - enum securityEnum sectype; /* sectype requested via mnt opts */ - bool sign; /* was signing requested via mnt opts? */ - bool ignore_signature:1; - bool retry:1; - bool intr:1; - bool setuids:1; - bool setuidfromacl:1; - bool override_uid:1; - bool override_gid:1; - bool dynperm:1; - bool noperm:1; - bool nodelete:1; - bool mode_ace:1; - bool no_psx_acl:1; /* set if posix acl support should be disabled */ - bool cifs_acl:1; - bool backupuid_specified; /* mount option backupuid is specified */ - bool backupgid_specified; /* mount option backupgid is specified */ - bool no_xattr:1; /* set if xattr (EA) support should be disabled*/ - bool server_ino:1; /* use inode numbers from server ie UniqueId */ - bool direct_io:1; - bool strict_io:1; /* strict cache behavior */ - bool cache_ro:1; - bool cache_rw:1; - bool remap:1; /* set to remap seven reserved chars in filenames */ - bool sfu_remap:1; /* remap seven reserved chars ala SFU */ - bool posix_paths:1; /* unset to not ask for posix pathnames. */ - bool no_linux_ext:1; - bool linux_ext:1; - bool sfu_emul:1; - bool nullauth:1; /* attempt to authenticate with null user */ - bool nocase:1; /* request case insensitive filenames */ - bool nobrl:1; /* disable sending byte range locks to srv */ - bool nohandlecache:1; /* disable caching dir handles if srvr probs */ - bool mand_lock:1; /* send mandatory not posix byte range lock reqs */ - bool seal:1; /* request transport encryption on share */ - bool nodfs:1; /* Do not request DFS, even if available */ - bool local_lease:1; /* check leases only on local system, not remote */ - bool noblocksnd:1; - bool noautotune:1; - bool nostrictsync:1; /* do not force expensive SMBflush on every sync */ - bool no_lease:1; /* disable requesting leases */ - bool no_sparse:1; /* do not attempt to set files sparse */ - bool fsc:1; /* enable fscache */ - bool mfsymlinks:1; /* use Minshall+French Symlinks */ - bool multiuser:1; - bool rwpidforward:1; /* pid forward for read/write operations */ - bool nosharesock:1; - bool persistent:1; - bool nopersistent:1; - bool resilient:1; /* noresilient not required since not fored for CA */ - bool domainauto:1; - bool rdma:1; - bool multichannel:1; - bool use_client_guid:1; - /* reuse existing guid for multichannel */ - u8 client_guid[SMB2_CLIENT_GUID_SIZE]; - unsigned int bsize; - unsigned int rasize; - unsigned int rsize; - unsigned int wsize; - unsigned int min_offload; - bool sockopt_tcp_nodelay:1; - /* attribute cache timemout for files and directories in jiffies */ - unsigned long acregmax; - unsigned long acdirmax; - /* timeout for deferred close of files in jiffies */ - unsigned long closetimeo; - struct smb_version_operations *ops; - struct smb_version_values *vals; - char *prepath; - struct sockaddr_storage dstaddr; /* destination address */ - struct sockaddr_storage srcaddr; /* allow binding to a local IP */ - struct nls_table *local_nls; /* This is a copy of the pointer in cifs_sb */ - unsigned int echo_interval; /* echo interval in secs */ - __u64 snapshot_time; /* needed for timewarp tokens */ - __u32 handle_timeout; /* persistent and durable handle timeout in ms */ - unsigned int max_credits; /* smb3 max_credits 10 < credits < 60000 */ - unsigned int max_channels; - __u16 compression; /* compression algorithm 0xFFFF default 0=disabled */ - bool rootfs:1; /* if it's a SMB root file system */ - bool witness:1; /* use witness protocol */ - - char *mount_options; -}; - -extern const struct fs_parameter_spec smb3_fs_parameters[]; - -extern int smb3_init_fs_context(struct fs_context *fc); -extern void smb3_cleanup_fs_context_contents(struct smb3_fs_context *ctx); -extern void smb3_cleanup_fs_context(struct smb3_fs_context *ctx); - -static inline struct smb3_fs_context *smb3_fc2context(const struct fs_context *fc) -{ - return fc->fs_private; -} - -extern int smb3_fs_context_dup(struct smb3_fs_context *new_ctx, struct smb3_fs_context *ctx); -extern void smb3_update_mnt_flags(struct cifs_sb_info *cifs_sb); - -/* - * max deferred close timeout (jiffies) - 2^30 - */ -#define SMB3_MAX_DCLOSETIMEO (1 << 30) -#define SMB3_DEF_DCLOSETIMEO (1 * HZ) /* even 1 sec enough to help eg open/write/close/open/read */ - -extern char *cifs_sanitize_prepath(char *prepath, gfp_t gfp); - -#endif diff --git a/fs/cifs/fscache.c b/fs/cifs/fscache.c deleted file mode 100644 index f6f3a6b75601b..0000000000000 --- a/fs/cifs/fscache.c +++ /dev/null @@ -1,253 +0,0 @@ -// SPDX-License-Identifier: LGPL-2.1 -/* - * CIFS filesystem cache interface - * - * Copyright (c) 2010 Novell, Inc. - * Author(s): Suresh Jayaraman - * - */ -#include "fscache.h" -#include "cifsglob.h" -#include "cifs_debug.h" -#include "cifs_fs_sb.h" -#include "cifsproto.h" - -static void cifs_fscache_fill_volume_coherency( - struct cifs_tcon *tcon, - struct cifs_fscache_volume_coherency_data *cd) -{ - memset(cd, 0, sizeof(*cd)); - cd->resource_id = cpu_to_le64(tcon->resource_id); - cd->vol_create_time = tcon->vol_create_time; - cd->vol_serial_number = cpu_to_le32(tcon->vol_serial_number); -} - -int cifs_fscache_get_super_cookie(struct cifs_tcon *tcon) -{ - struct cifs_fscache_volume_coherency_data cd; - struct TCP_Server_Info *server = tcon->ses->server; - struct fscache_volume *vcookie; - const struct sockaddr *sa = (struct sockaddr *)&server->dstaddr; - size_t slen, i; - char *sharename; - char *key; - int ret = -ENOMEM; - - tcon->fscache = NULL; - switch (sa->sa_family) { - case AF_INET: - case AF_INET6: - break; - default: - cifs_dbg(VFS, "Unknown network family '%d'\n", sa->sa_family); - return -EINVAL; - } - - memset(&key, 0, sizeof(key)); - - sharename = extract_sharename(tcon->tree_name); - if (IS_ERR(sharename)) { - cifs_dbg(FYI, "%s: couldn't extract sharename\n", __func__); - return -EINVAL; - } - - slen = strlen(sharename); - for (i = 0; i < slen; i++) - if (sharename[i] == '/') - sharename[i] = ';'; - - key = kasprintf(GFP_KERNEL, "cifs,%pISpc,%s", sa, sharename); - if (!key) - goto out; - - cifs_fscache_fill_volume_coherency(tcon, &cd); - vcookie = fscache_acquire_volume(key, - NULL, /* preferred_cache */ - &cd, sizeof(cd)); - cifs_dbg(FYI, "%s: (%s/0x%p)\n", __func__, key, vcookie); - if (IS_ERR(vcookie)) { - if (vcookie != ERR_PTR(-EBUSY)) { - ret = PTR_ERR(vcookie); - goto out_2; - } - pr_err("Cache volume key already in use (%s)\n", key); - vcookie = NULL; - } - - tcon->fscache = vcookie; - ret = 0; -out_2: - kfree(key); -out: - kfree(sharename); - return ret; -} - -void cifs_fscache_release_super_cookie(struct cifs_tcon *tcon) -{ - struct cifs_fscache_volume_coherency_data cd; - - cifs_dbg(FYI, "%s: (0x%p)\n", __func__, tcon->fscache); - - cifs_fscache_fill_volume_coherency(tcon, &cd); - fscache_relinquish_volume(tcon->fscache, &cd, false); - tcon->fscache = NULL; -} - -void cifs_fscache_get_inode_cookie(struct inode *inode) -{ - struct cifs_fscache_inode_coherency_data cd; - struct cifsInodeInfo *cifsi = CIFS_I(inode); - struct cifs_sb_info *cifs_sb = CIFS_SB(inode->i_sb); - struct cifs_tcon *tcon = cifs_sb_master_tcon(cifs_sb); - - cifs_fscache_fill_coherency(&cifsi->netfs.inode, &cd); - - cifsi->netfs.cache = - fscache_acquire_cookie(tcon->fscache, 0, - &cifsi->uniqueid, sizeof(cifsi->uniqueid), - &cd, sizeof(cd), - i_size_read(&cifsi->netfs.inode)); -} - -void cifs_fscache_unuse_inode_cookie(struct inode *inode, bool update) -{ - if (update) { - struct cifs_fscache_inode_coherency_data cd; - loff_t i_size = i_size_read(inode); - - cifs_fscache_fill_coherency(inode, &cd); - fscache_unuse_cookie(cifs_inode_cookie(inode), &cd, &i_size); - } else { - fscache_unuse_cookie(cifs_inode_cookie(inode), NULL, NULL); - } -} - -void cifs_fscache_release_inode_cookie(struct inode *inode) -{ - struct cifsInodeInfo *cifsi = CIFS_I(inode); - struct fscache_cookie *cookie = cifs_inode_cookie(inode); - - if (cookie) { - cifs_dbg(FYI, "%s: (0x%p)\n", __func__, cookie); - fscache_relinquish_cookie(cookie, false); - cifsi->netfs.cache = NULL; - } -} - -/* - * Fallback page reading interface. - */ -static int fscache_fallback_read_page(struct inode *inode, struct page *page) -{ - struct netfs_cache_resources cres; - struct fscache_cookie *cookie = cifs_inode_cookie(inode); - struct iov_iter iter; - struct bio_vec bvec[1]; - int ret; - - memset(&cres, 0, sizeof(cres)); - bvec[0].bv_page = page; - bvec[0].bv_offset = 0; - bvec[0].bv_len = PAGE_SIZE; - iov_iter_bvec(&iter, ITER_DEST, bvec, ARRAY_SIZE(bvec), PAGE_SIZE); - - ret = fscache_begin_read_operation(&cres, cookie); - if (ret < 0) - return ret; - - ret = fscache_read(&cres, page_offset(page), &iter, NETFS_READ_HOLE_FAIL, - NULL, NULL); - fscache_end_operation(&cres); - return ret; -} - -/* - * Fallback page writing interface. - */ -static int fscache_fallback_write_page(struct inode *inode, struct page *page, - bool no_space_allocated_yet) -{ - struct netfs_cache_resources cres; - struct fscache_cookie *cookie = cifs_inode_cookie(inode); - struct iov_iter iter; - struct bio_vec bvec[1]; - loff_t start = page_offset(page); - size_t len = PAGE_SIZE; - int ret; - - memset(&cres, 0, sizeof(cres)); - bvec[0].bv_page = page; - bvec[0].bv_offset = 0; - bvec[0].bv_len = PAGE_SIZE; - iov_iter_bvec(&iter, ITER_SOURCE, bvec, ARRAY_SIZE(bvec), PAGE_SIZE); - - ret = fscache_begin_write_operation(&cres, cookie); - if (ret < 0) - return ret; - - ret = cres.ops->prepare_write(&cres, &start, &len, i_size_read(inode), - no_space_allocated_yet); - if (ret == 0) - ret = fscache_write(&cres, page_offset(page), &iter, NULL, NULL); - fscache_end_operation(&cres); - return ret; -} - -/* - * Retrieve a page from FS-Cache - */ -int __cifs_readpage_from_fscache(struct inode *inode, struct page *page) -{ - int ret; - - cifs_dbg(FYI, "%s: (fsc:%p, p:%p, i:0x%p\n", - __func__, cifs_inode_cookie(inode), page, inode); - - ret = fscache_fallback_read_page(inode, page); - if (ret < 0) - return ret; - - /* Read completed synchronously */ - SetPageUptodate(page); - return 0; -} - -void __cifs_readpage_to_fscache(struct inode *inode, struct page *page) -{ - cifs_dbg(FYI, "%s: (fsc: %p, p: %p, i: %p)\n", - __func__, cifs_inode_cookie(inode), page, inode); - - fscache_fallback_write_page(inode, page, true); -} - -/* - * Query the cache occupancy. - */ -int __cifs_fscache_query_occupancy(struct inode *inode, - pgoff_t first, unsigned int nr_pages, - pgoff_t *_data_first, - unsigned int *_data_nr_pages) -{ - struct netfs_cache_resources cres; - struct fscache_cookie *cookie = cifs_inode_cookie(inode); - loff_t start, data_start; - size_t len, data_len; - int ret; - - ret = fscache_begin_read_operation(&cres, cookie); - if (ret < 0) - return ret; - - start = first * PAGE_SIZE; - len = nr_pages * PAGE_SIZE; - ret = cres.ops->query_occupancy(&cres, start, len, PAGE_SIZE, - &data_start, &data_len); - if (ret == 0) { - *_data_first = data_start / PAGE_SIZE; - *_data_nr_pages = len / PAGE_SIZE; - } - - fscache_end_operation(&cres); - return ret; -} diff --git a/fs/cifs/fscache.h b/fs/cifs/fscache.h deleted file mode 100644 index 67b601041f0a3..0000000000000 --- a/fs/cifs/fscache.h +++ /dev/null @@ -1,148 +0,0 @@ -/* SPDX-License-Identifier: LGPL-2.1 */ -/* - * CIFS filesystem cache interface definitions - * - * Copyright (c) 2010 Novell, Inc. - * Authors(s): Suresh Jayaraman (sjayaraman@suse.de> - * - */ -#ifndef _CIFS_FSCACHE_H -#define _CIFS_FSCACHE_H - -#include -#include - -#include "cifsglob.h" - -/* - * Coherency data attached to CIFS volume within the cache - */ -struct cifs_fscache_volume_coherency_data { - __le64 resource_id; /* unique server resource id */ - __le64 vol_create_time; - __le32 vol_serial_number; -} __packed; - -/* - * Coherency data attached to CIFS inode within the cache. - */ -struct cifs_fscache_inode_coherency_data { - __le64 last_write_time_sec; - __le64 last_change_time_sec; - __le32 last_write_time_nsec; - __le32 last_change_time_nsec; -}; - -#ifdef CONFIG_CIFS_FSCACHE - -/* - * fscache.c - */ -extern int cifs_fscache_get_super_cookie(struct cifs_tcon *); -extern void cifs_fscache_release_super_cookie(struct cifs_tcon *); - -extern void cifs_fscache_get_inode_cookie(struct inode *inode); -extern void cifs_fscache_release_inode_cookie(struct inode *); -extern void cifs_fscache_unuse_inode_cookie(struct inode *inode, bool update); - -static inline -void cifs_fscache_fill_coherency(struct inode *inode, - struct cifs_fscache_inode_coherency_data *cd) -{ - struct cifsInodeInfo *cifsi = CIFS_I(inode); - - memset(cd, 0, sizeof(*cd)); - cd->last_write_time_sec = cpu_to_le64(cifsi->netfs.inode.i_mtime.tv_sec); - cd->last_write_time_nsec = cpu_to_le32(cifsi->netfs.inode.i_mtime.tv_nsec); - cd->last_change_time_sec = cpu_to_le64(cifsi->netfs.inode.i_ctime.tv_sec); - cd->last_change_time_nsec = cpu_to_le32(cifsi->netfs.inode.i_ctime.tv_nsec); -} - - -static inline struct fscache_cookie *cifs_inode_cookie(struct inode *inode) -{ - return netfs_i_cookie(&CIFS_I(inode)->netfs); -} - -static inline void cifs_invalidate_cache(struct inode *inode, unsigned int flags) -{ - struct cifs_fscache_inode_coherency_data cd; - - cifs_fscache_fill_coherency(inode, &cd); - fscache_invalidate(cifs_inode_cookie(inode), &cd, - i_size_read(inode), flags); -} - -extern int __cifs_fscache_query_occupancy(struct inode *inode, - pgoff_t first, unsigned int nr_pages, - pgoff_t *_data_first, - unsigned int *_data_nr_pages); - -static inline int cifs_fscache_query_occupancy(struct inode *inode, - pgoff_t first, unsigned int nr_pages, - pgoff_t *_data_first, - unsigned int *_data_nr_pages) -{ - if (!cifs_inode_cookie(inode)) - return -ENOBUFS; - return __cifs_fscache_query_occupancy(inode, first, nr_pages, - _data_first, _data_nr_pages); -} - -extern int __cifs_readpage_from_fscache(struct inode *pinode, struct page *ppage); -extern void __cifs_readpage_to_fscache(struct inode *pinode, struct page *ppage); - - -static inline int cifs_readpage_from_fscache(struct inode *inode, - struct page *page) -{ - if (cifs_inode_cookie(inode)) - return __cifs_readpage_from_fscache(inode, page); - return -ENOBUFS; -} - -static inline void cifs_readpage_to_fscache(struct inode *inode, - struct page *page) -{ - if (cifs_inode_cookie(inode)) - __cifs_readpage_to_fscache(inode, page); -} - -#else /* CONFIG_CIFS_FSCACHE */ -static inline -void cifs_fscache_fill_coherency(struct inode *inode, - struct cifs_fscache_inode_coherency_data *cd) -{ -} - -static inline int cifs_fscache_get_super_cookie(struct cifs_tcon *tcon) { return 0; } -static inline void cifs_fscache_release_super_cookie(struct cifs_tcon *tcon) {} - -static inline void cifs_fscache_get_inode_cookie(struct inode *inode) {} -static inline void cifs_fscache_release_inode_cookie(struct inode *inode) {} -static inline void cifs_fscache_unuse_inode_cookie(struct inode *inode, bool update) {} -static inline struct fscache_cookie *cifs_inode_cookie(struct inode *inode) { return NULL; } -static inline void cifs_invalidate_cache(struct inode *inode, unsigned int flags) {} - -static inline int cifs_fscache_query_occupancy(struct inode *inode, - pgoff_t first, unsigned int nr_pages, - pgoff_t *_data_first, - unsigned int *_data_nr_pages) -{ - *_data_first = ULONG_MAX; - *_data_nr_pages = 0; - return -ENOBUFS; -} - -static inline int -cifs_readpage_from_fscache(struct inode *inode, struct page *page) -{ - return -ENOBUFS; -} - -static inline -void cifs_readpage_to_fscache(struct inode *inode, struct page *page) {} - -#endif /* CONFIG_CIFS_FSCACHE */ - -#endif /* _CIFS_FSCACHE_H */ diff --git a/fs/cifs/inode.c b/fs/cifs/inode.c deleted file mode 100644 index 8901d884f5b98..0000000000000 --- a/fs/cifs/inode.c +++ /dev/null @@ -1,3093 +0,0 @@ -// SPDX-License-Identifier: LGPL-2.1 -/* - * - * Copyright (C) International Business Machines Corp., 2002,2010 - * Author(s): Steve French (sfrench@us.ibm.com) - * - */ -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include "cifsfs.h" -#include "cifspdu.h" -#include "cifsglob.h" -#include "cifsproto.h" -#include "smb2proto.h" -#include "cifs_debug.h" -#include "cifs_fs_sb.h" -#include "cifs_unicode.h" -#include "fscache.h" -#include "fs_context.h" -#include "cifs_ioctl.h" -#include "cached_dir.h" - -static void cifs_set_ops(struct inode *inode) -{ - struct cifs_sb_info *cifs_sb = CIFS_SB(inode->i_sb); - - switch (inode->i_mode & S_IFMT) { - case S_IFREG: - inode->i_op = &cifs_file_inode_ops; - if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_DIRECT_IO) { - if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_NO_BRL) - inode->i_fop = &cifs_file_direct_nobrl_ops; - else - inode->i_fop = &cifs_file_direct_ops; - } else if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_STRICT_IO) { - if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_NO_BRL) - inode->i_fop = &cifs_file_strict_nobrl_ops; - else - inode->i_fop = &cifs_file_strict_ops; - } else if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_NO_BRL) - inode->i_fop = &cifs_file_nobrl_ops; - else { /* not direct, send byte range locks */ - inode->i_fop = &cifs_file_ops; - } - - /* check if server can support readahead */ - if (cifs_sb_master_tcon(cifs_sb)->ses->server->max_read < - PAGE_SIZE + MAX_CIFS_HDR_SIZE) - inode->i_data.a_ops = &cifs_addr_ops_smallbuf; - else - inode->i_data.a_ops = &cifs_addr_ops; - break; - case S_IFDIR: -#ifdef CONFIG_CIFS_DFS_UPCALL - if (IS_AUTOMOUNT(inode)) { - inode->i_op = &cifs_dfs_referral_inode_operations; - } else { -#else /* NO DFS support, treat as a directory */ - { -#endif - inode->i_op = &cifs_dir_inode_ops; - inode->i_fop = &cifs_dir_ops; - } - break; - case S_IFLNK: - inode->i_op = &cifs_symlink_inode_ops; - break; - default: - init_special_inode(inode, inode->i_mode, inode->i_rdev); - break; - } -} - -/* check inode attributes against fattr. If they don't match, tag the - * inode for cache invalidation - */ -static void -cifs_revalidate_cache(struct inode *inode, struct cifs_fattr *fattr) -{ - struct cifs_fscache_inode_coherency_data cd; - struct cifsInodeInfo *cifs_i = CIFS_I(inode); - - cifs_dbg(FYI, "%s: revalidating inode %llu\n", - __func__, cifs_i->uniqueid); - - if (inode->i_state & I_NEW) { - cifs_dbg(FYI, "%s: inode %llu is new\n", - __func__, cifs_i->uniqueid); - return; - } - - /* don't bother with revalidation if we have an oplock */ - if (CIFS_CACHE_READ(cifs_i)) { - cifs_dbg(FYI, "%s: inode %llu is oplocked\n", - __func__, cifs_i->uniqueid); - return; - } - - /* revalidate if mtime or size have changed */ - fattr->cf_mtime = timestamp_truncate(fattr->cf_mtime, inode); - if (timespec64_equal(&inode->i_mtime, &fattr->cf_mtime) && - cifs_i->server_eof == fattr->cf_eof) { - cifs_dbg(FYI, "%s: inode %llu is unchanged\n", - __func__, cifs_i->uniqueid); - return; - } - - cifs_dbg(FYI, "%s: invalidating inode %llu mapping\n", - __func__, cifs_i->uniqueid); - set_bit(CIFS_INO_INVALID_MAPPING, &cifs_i->flags); - /* Invalidate fscache cookie */ - cifs_fscache_fill_coherency(&cifs_i->netfs.inode, &cd); - fscache_invalidate(cifs_inode_cookie(inode), &cd, i_size_read(inode), 0); -} - -/* - * copy nlink to the inode, unless it wasn't provided. Provide - * sane values if we don't have an existing one and none was provided - */ -static void -cifs_nlink_fattr_to_inode(struct inode *inode, struct cifs_fattr *fattr) -{ - /* - * if we're in a situation where we can't trust what we - * got from the server (readdir, some non-unix cases) - * fake reasonable values - */ - if (fattr->cf_flags & CIFS_FATTR_UNKNOWN_NLINK) { - /* only provide fake values on a new inode */ - if (inode->i_state & I_NEW) { - if (fattr->cf_cifsattrs & ATTR_DIRECTORY) - set_nlink(inode, 2); - else - set_nlink(inode, 1); - } - return; - } - - /* we trust the server, so update it */ - set_nlink(inode, fattr->cf_nlink); -} - -/* populate an inode with info from a cifs_fattr struct */ -int -cifs_fattr_to_inode(struct inode *inode, struct cifs_fattr *fattr) -{ - struct cifsInodeInfo *cifs_i = CIFS_I(inode); - struct cifs_sb_info *cifs_sb = CIFS_SB(inode->i_sb); - - if (!(inode->i_state & I_NEW) && - unlikely(inode_wrong_type(inode, fattr->cf_mode))) { - CIFS_I(inode)->time = 0; /* force reval */ - return -ESTALE; - } - - cifs_revalidate_cache(inode, fattr); - - spin_lock(&inode->i_lock); - fattr->cf_mtime = timestamp_truncate(fattr->cf_mtime, inode); - fattr->cf_atime = timestamp_truncate(fattr->cf_atime, inode); - fattr->cf_ctime = timestamp_truncate(fattr->cf_ctime, inode); - /* we do not want atime to be less than mtime, it broke some apps */ - if (timespec64_compare(&fattr->cf_atime, &fattr->cf_mtime) < 0) - inode->i_atime = fattr->cf_mtime; - else - inode->i_atime = fattr->cf_atime; - inode->i_mtime = fattr->cf_mtime; - inode->i_ctime = fattr->cf_ctime; - inode->i_rdev = fattr->cf_rdev; - cifs_nlink_fattr_to_inode(inode, fattr); - inode->i_uid = fattr->cf_uid; - inode->i_gid = fattr->cf_gid; - - /* if dynperm is set, don't clobber existing mode */ - if (inode->i_state & I_NEW || - !(cifs_sb->mnt_cifs_flags & CIFS_MOUNT_DYNPERM)) - inode->i_mode = fattr->cf_mode; - - cifs_i->cifsAttrs = fattr->cf_cifsattrs; - - if (fattr->cf_flags & CIFS_FATTR_NEED_REVAL) - cifs_i->time = 0; - else - cifs_i->time = jiffies; - - if (fattr->cf_flags & CIFS_FATTR_DELETE_PENDING) - set_bit(CIFS_INO_DELETE_PENDING, &cifs_i->flags); - else - clear_bit(CIFS_INO_DELETE_PENDING, &cifs_i->flags); - - cifs_i->server_eof = fattr->cf_eof; - /* - * Can't safely change the file size here if the client is writing to - * it due to potential races. - */ - if (is_size_safe_to_change(cifs_i, fattr->cf_eof)) { - i_size_write(inode, fattr->cf_eof); - - /* - * i_blocks is not related to (i_size / i_blksize), - * but instead 512 byte (2**9) size is required for - * calculating num blocks. - */ - inode->i_blocks = (512 - 1 + fattr->cf_bytes) >> 9; - } - - if (S_ISLNK(fattr->cf_mode)) { - kfree(cifs_i->symlink_target); - cifs_i->symlink_target = fattr->cf_symlink_target; - fattr->cf_symlink_target = NULL; - } - spin_unlock(&inode->i_lock); - - if (fattr->cf_flags & CIFS_FATTR_DFS_REFERRAL) - inode->i_flags |= S_AUTOMOUNT; - if (inode->i_state & I_NEW) - cifs_set_ops(inode); - return 0; -} - -void -cifs_fill_uniqueid(struct super_block *sb, struct cifs_fattr *fattr) -{ - struct cifs_sb_info *cifs_sb = CIFS_SB(sb); - - if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_SERVER_INUM) - return; - - fattr->cf_uniqueid = iunique(sb, ROOT_I); -} - -/* Fill a cifs_fattr struct with info from FILE_UNIX_BASIC_INFO. */ -void -cifs_unix_basic_to_fattr(struct cifs_fattr *fattr, FILE_UNIX_BASIC_INFO *info, - struct cifs_sb_info *cifs_sb) -{ - memset(fattr, 0, sizeof(*fattr)); - fattr->cf_uniqueid = le64_to_cpu(info->UniqueId); - fattr->cf_bytes = le64_to_cpu(info->NumOfBytes); - fattr->cf_eof = le64_to_cpu(info->EndOfFile); - - fattr->cf_atime = cifs_NTtimeToUnix(info->LastAccessTime); - fattr->cf_mtime = cifs_NTtimeToUnix(info->LastModificationTime); - fattr->cf_ctime = cifs_NTtimeToUnix(info->LastStatusChange); - /* old POSIX extensions don't get create time */ - - fattr->cf_mode = le64_to_cpu(info->Permissions); - - /* - * Since we set the inode type below we need to mask off - * to avoid strange results if bits set above. - */ - fattr->cf_mode &= ~S_IFMT; - switch (le32_to_cpu(info->Type)) { - case UNIX_FILE: - fattr->cf_mode |= S_IFREG; - fattr->cf_dtype = DT_REG; - break; - case UNIX_SYMLINK: - fattr->cf_mode |= S_IFLNK; - fattr->cf_dtype = DT_LNK; - break; - case UNIX_DIR: - fattr->cf_mode |= S_IFDIR; - fattr->cf_dtype = DT_DIR; - break; - case UNIX_CHARDEV: - fattr->cf_mode |= S_IFCHR; - fattr->cf_dtype = DT_CHR; - fattr->cf_rdev = MKDEV(le64_to_cpu(info->DevMajor), - le64_to_cpu(info->DevMinor) & MINORMASK); - break; - case UNIX_BLOCKDEV: - fattr->cf_mode |= S_IFBLK; - fattr->cf_dtype = DT_BLK; - fattr->cf_rdev = MKDEV(le64_to_cpu(info->DevMajor), - le64_to_cpu(info->DevMinor) & MINORMASK); - break; - case UNIX_FIFO: - fattr->cf_mode |= S_IFIFO; - fattr->cf_dtype = DT_FIFO; - break; - case UNIX_SOCKET: - fattr->cf_mode |= S_IFSOCK; - fattr->cf_dtype = DT_SOCK; - break; - default: - /* safest to call it a file if we do not know */ - fattr->cf_mode |= S_IFREG; - fattr->cf_dtype = DT_REG; - cifs_dbg(FYI, "unknown type %d\n", le32_to_cpu(info->Type)); - break; - } - - fattr->cf_uid = cifs_sb->ctx->linux_uid; - if (!(cifs_sb->mnt_cifs_flags & CIFS_MOUNT_OVERR_UID)) { - u64 id = le64_to_cpu(info->Uid); - if (id < ((uid_t)-1)) { - kuid_t uid = make_kuid(&init_user_ns, id); - if (uid_valid(uid)) - fattr->cf_uid = uid; - } - } - - fattr->cf_gid = cifs_sb->ctx->linux_gid; - if (!(cifs_sb->mnt_cifs_flags & CIFS_MOUNT_OVERR_GID)) { - u64 id = le64_to_cpu(info->Gid); - if (id < ((gid_t)-1)) { - kgid_t gid = make_kgid(&init_user_ns, id); - if (gid_valid(gid)) - fattr->cf_gid = gid; - } - } - - fattr->cf_nlink = le64_to_cpu(info->Nlinks); -} - -/* - * Fill a cifs_fattr struct with fake inode info. - * - * Needed to setup cifs_fattr data for the directory which is the - * junction to the new submount (ie to setup the fake directory - * which represents a DFS referral). - */ -static void -cifs_create_dfs_fattr(struct cifs_fattr *fattr, struct super_block *sb) -{ - struct cifs_sb_info *cifs_sb = CIFS_SB(sb); - - cifs_dbg(FYI, "creating fake fattr for DFS referral\n"); - - memset(fattr, 0, sizeof(*fattr)); - fattr->cf_mode = S_IFDIR | S_IXUGO | S_IRWXU; - fattr->cf_uid = cifs_sb->ctx->linux_uid; - fattr->cf_gid = cifs_sb->ctx->linux_gid; - ktime_get_coarse_real_ts64(&fattr->cf_mtime); - fattr->cf_atime = fattr->cf_ctime = fattr->cf_mtime; - fattr->cf_nlink = 2; - fattr->cf_flags = CIFS_FATTR_DFS_REFERRAL; -} - -#ifdef CONFIG_CIFS_ALLOW_INSECURE_LEGACY -static int -cifs_get_file_info_unix(struct file *filp) -{ - int rc; - unsigned int xid; - FILE_UNIX_BASIC_INFO find_data; - struct cifs_fattr fattr = {}; - struct inode *inode = file_inode(filp); - struct cifs_sb_info *cifs_sb = CIFS_SB(inode->i_sb); - struct cifsFileInfo *cfile = filp->private_data; - struct cifs_tcon *tcon = tlink_tcon(cfile->tlink); - - xid = get_xid(); - - if (cfile->symlink_target) { - fattr.cf_symlink_target = kstrdup(cfile->symlink_target, GFP_KERNEL); - if (!fattr.cf_symlink_target) { - rc = -ENOMEM; - goto cifs_gfiunix_out; - } - } - - rc = CIFSSMBUnixQFileInfo(xid, tcon, cfile->fid.netfid, &find_data); - if (!rc) { - cifs_unix_basic_to_fattr(&fattr, &find_data, cifs_sb); - } else if (rc == -EREMOTE) { - cifs_create_dfs_fattr(&fattr, inode->i_sb); - rc = 0; - } else - goto cifs_gfiunix_out; - - rc = cifs_fattr_to_inode(inode, &fattr); - -cifs_gfiunix_out: - free_xid(xid); - return rc; -} - -int cifs_get_inode_info_unix(struct inode **pinode, - const unsigned char *full_path, - struct super_block *sb, unsigned int xid) -{ - int rc; - FILE_UNIX_BASIC_INFO find_data; - struct cifs_fattr fattr; - struct cifs_tcon *tcon; - struct TCP_Server_Info *server; - struct tcon_link *tlink; - struct cifs_sb_info *cifs_sb = CIFS_SB(sb); - - cifs_dbg(FYI, "Getting info on %s\n", full_path); - - tlink = cifs_sb_tlink(cifs_sb); - if (IS_ERR(tlink)) - return PTR_ERR(tlink); - tcon = tlink_tcon(tlink); - server = tcon->ses->server; - - /* could have done a find first instead but this returns more info */ - rc = CIFSSMBUnixQPathInfo(xid, tcon, full_path, &find_data, - cifs_sb->local_nls, cifs_remap(cifs_sb)); - cifs_dbg(FYI, "%s: query path info: rc = %d\n", __func__, rc); - cifs_put_tlink(tlink); - - if (!rc) { - cifs_unix_basic_to_fattr(&fattr, &find_data, cifs_sb); - } else if (rc == -EREMOTE) { - cifs_create_dfs_fattr(&fattr, sb); - rc = 0; - } else { - return rc; - } - - /* check for Minshall+French symlinks */ - if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_MF_SYMLINKS) { - int tmprc = check_mf_symlink(xid, tcon, cifs_sb, &fattr, - full_path); - if (tmprc) - cifs_dbg(FYI, "check_mf_symlink: %d\n", tmprc); - } - - if (S_ISLNK(fattr.cf_mode) && !fattr.cf_symlink_target) { - if (!server->ops->query_symlink) - return -EOPNOTSUPP; - rc = server->ops->query_symlink(xid, tcon, cifs_sb, full_path, - &fattr.cf_symlink_target, false); - if (rc) { - cifs_dbg(FYI, "%s: query_symlink: %d\n", __func__, rc); - goto cgiiu_exit; - } - } - - if (*pinode == NULL) { - /* get new inode */ - cifs_fill_uniqueid(sb, &fattr); - *pinode = cifs_iget(sb, &fattr); - if (!*pinode) - rc = -ENOMEM; - } else { - /* we already have inode, update it */ - - /* if uniqueid is different, return error */ - if (unlikely(cifs_sb->mnt_cifs_flags & CIFS_MOUNT_SERVER_INUM && - CIFS_I(*pinode)->uniqueid != fattr.cf_uniqueid)) { - CIFS_I(*pinode)->time = 0; /* force reval */ - rc = -ESTALE; - goto cgiiu_exit; - } - - /* if filetype is different, return error */ - rc = cifs_fattr_to_inode(*pinode, &fattr); - } - -cgiiu_exit: - kfree(fattr.cf_symlink_target); - return rc; -} -#else -int cifs_get_inode_info_unix(struct inode **pinode, - const unsigned char *full_path, - struct super_block *sb, unsigned int xid) -{ - return -EOPNOTSUPP; -} -#endif /* CONFIG_CIFS_ALLOW_INSECURE_LEGACY */ - -static int -cifs_sfu_type(struct cifs_fattr *fattr, const char *path, - struct cifs_sb_info *cifs_sb, unsigned int xid) -{ - int rc; - __u32 oplock; - struct tcon_link *tlink; - struct cifs_tcon *tcon; - struct cifs_fid fid; - struct cifs_open_parms oparms; - struct cifs_io_parms io_parms = {0}; - char buf[24]; - unsigned int bytes_read; - char *pbuf; - int buf_type = CIFS_NO_BUFFER; - - pbuf = buf; - - fattr->cf_mode &= ~S_IFMT; - - if (fattr->cf_eof == 0) { - fattr->cf_mode |= S_IFIFO; - fattr->cf_dtype = DT_FIFO; - return 0; - } else if (fattr->cf_eof < 8) { - fattr->cf_mode |= S_IFREG; - fattr->cf_dtype = DT_REG; - return -EINVAL; /* EOPNOTSUPP? */ - } - - tlink = cifs_sb_tlink(cifs_sb); - if (IS_ERR(tlink)) - return PTR_ERR(tlink); - tcon = tlink_tcon(tlink); - - oparms = (struct cifs_open_parms) { - .tcon = tcon, - .cifs_sb = cifs_sb, - .desired_access = GENERIC_READ, - .create_options = cifs_create_options(cifs_sb, CREATE_NOT_DIR), - .disposition = FILE_OPEN, - .path = path, - .fid = &fid, - }; - - if (tcon->ses->server->oplocks) - oplock = REQ_OPLOCK; - else - oplock = 0; - rc = tcon->ses->server->ops->open(xid, &oparms, &oplock, NULL); - if (rc) { - cifs_dbg(FYI, "check sfu type of %s, open rc = %d\n", path, rc); - cifs_put_tlink(tlink); - return rc; - } - - /* Read header */ - io_parms.netfid = fid.netfid; - io_parms.pid = current->tgid; - io_parms.tcon = tcon; - io_parms.offset = 0; - io_parms.length = 24; - - rc = tcon->ses->server->ops->sync_read(xid, &fid, &io_parms, - &bytes_read, &pbuf, &buf_type); - if ((rc == 0) && (bytes_read >= 8)) { - if (memcmp("IntxBLK", pbuf, 8) == 0) { - cifs_dbg(FYI, "Block device\n"); - fattr->cf_mode |= S_IFBLK; - fattr->cf_dtype = DT_BLK; - if (bytes_read == 24) { - /* we have enough to decode dev num */ - __u64 mjr; /* major */ - __u64 mnr; /* minor */ - mjr = le64_to_cpu(*(__le64 *)(pbuf+8)); - mnr = le64_to_cpu(*(__le64 *)(pbuf+16)); - fattr->cf_rdev = MKDEV(mjr, mnr); - } - } else if (memcmp("IntxCHR", pbuf, 8) == 0) { - cifs_dbg(FYI, "Char device\n"); - fattr->cf_mode |= S_IFCHR; - fattr->cf_dtype = DT_CHR; - if (bytes_read == 24) { - /* we have enough to decode dev num */ - __u64 mjr; /* major */ - __u64 mnr; /* minor */ - mjr = le64_to_cpu(*(__le64 *)(pbuf+8)); - mnr = le64_to_cpu(*(__le64 *)(pbuf+16)); - fattr->cf_rdev = MKDEV(mjr, mnr); - } - } else if (memcmp("IntxLNK", pbuf, 7) == 0) { - cifs_dbg(FYI, "Symlink\n"); - fattr->cf_mode |= S_IFLNK; - fattr->cf_dtype = DT_LNK; - } else { - fattr->cf_mode |= S_IFREG; /* file? */ - fattr->cf_dtype = DT_REG; - rc = -EOPNOTSUPP; - } - } else { - fattr->cf_mode |= S_IFREG; /* then it is a file */ - fattr->cf_dtype = DT_REG; - rc = -EOPNOTSUPP; /* or some unknown SFU type */ - } - - tcon->ses->server->ops->close(xid, tcon, &fid); - cifs_put_tlink(tlink); - return rc; -} - -#define SFBITS_MASK (S_ISVTX | S_ISGID | S_ISUID) /* SETFILEBITS valid bits */ - -/* - * Fetch mode bits as provided by SFU. - * - * FIXME: Doesn't this clobber the type bit we got from cifs_sfu_type ? - */ -static int cifs_sfu_mode(struct cifs_fattr *fattr, const unsigned char *path, - struct cifs_sb_info *cifs_sb, unsigned int xid) -{ -#ifdef CONFIG_CIFS_XATTR - ssize_t rc; - char ea_value[4]; - __u32 mode; - struct tcon_link *tlink; - struct cifs_tcon *tcon; - - tlink = cifs_sb_tlink(cifs_sb); - if (IS_ERR(tlink)) - return PTR_ERR(tlink); - tcon = tlink_tcon(tlink); - - if (tcon->ses->server->ops->query_all_EAs == NULL) { - cifs_put_tlink(tlink); - return -EOPNOTSUPP; - } - - rc = tcon->ses->server->ops->query_all_EAs(xid, tcon, path, - "SETFILEBITS", ea_value, 4 /* size of buf */, - cifs_sb); - cifs_put_tlink(tlink); - if (rc < 0) - return (int)rc; - else if (rc > 3) { - mode = le32_to_cpu(*((__le32 *)ea_value)); - fattr->cf_mode &= ~SFBITS_MASK; - cifs_dbg(FYI, "special bits 0%o org mode 0%o\n", - mode, fattr->cf_mode); - fattr->cf_mode = (mode & SFBITS_MASK) | fattr->cf_mode; - cifs_dbg(FYI, "special mode bits 0%o\n", mode); - } - - return 0; -#else - return -EOPNOTSUPP; -#endif -} - -/* Fill a cifs_fattr struct with info from POSIX info struct */ -static void smb311_posix_info_to_fattr(struct cifs_fattr *fattr, struct cifs_open_info_data *data, - struct super_block *sb, bool adjust_tz, bool symlink) -{ - struct smb311_posix_qinfo *info = &data->posix_fi; - struct cifs_sb_info *cifs_sb = CIFS_SB(sb); - struct cifs_tcon *tcon = cifs_sb_master_tcon(cifs_sb); - - memset(fattr, 0, sizeof(*fattr)); - - /* no fattr->flags to set */ - fattr->cf_cifsattrs = le32_to_cpu(info->DosAttributes); - fattr->cf_uniqueid = le64_to_cpu(info->Inode); - - if (info->LastAccessTime) - fattr->cf_atime = cifs_NTtimeToUnix(info->LastAccessTime); - else - ktime_get_coarse_real_ts64(&fattr->cf_atime); - - fattr->cf_ctime = cifs_NTtimeToUnix(info->ChangeTime); - fattr->cf_mtime = cifs_NTtimeToUnix(info->LastWriteTime); - - if (adjust_tz) { - fattr->cf_ctime.tv_sec += tcon->ses->server->timeAdj; - fattr->cf_mtime.tv_sec += tcon->ses->server->timeAdj; - } - - fattr->cf_eof = le64_to_cpu(info->EndOfFile); - fattr->cf_bytes = le64_to_cpu(info->AllocationSize); - fattr->cf_createtime = le64_to_cpu(info->CreationTime); - - fattr->cf_nlink = le32_to_cpu(info->HardLinks); - fattr->cf_mode = (umode_t) le32_to_cpu(info->Mode); - /* The srv fs device id is overridden on network mount so setting rdev isn't needed here */ - /* fattr->cf_rdev = le32_to_cpu(info->DeviceId); */ - - if (symlink) { - fattr->cf_mode |= S_IFLNK; - fattr->cf_dtype = DT_LNK; - fattr->cf_symlink_target = data->symlink_target; - data->symlink_target = NULL; - } else if (fattr->cf_cifsattrs & ATTR_DIRECTORY) { - fattr->cf_mode |= S_IFDIR; - fattr->cf_dtype = DT_DIR; - } else { /* file */ - fattr->cf_mode |= S_IFREG; - fattr->cf_dtype = DT_REG; - } - /* else if reparse point ... TODO: add support for FIFO and blk dev; special file types */ - - fattr->cf_uid = cifs_sb->ctx->linux_uid; /* TODO: map uid and gid from SID */ - fattr->cf_gid = cifs_sb->ctx->linux_gid; - - cifs_dbg(FYI, "POSIX query info: mode 0x%x uniqueid 0x%llx nlink %d\n", - fattr->cf_mode, fattr->cf_uniqueid, fattr->cf_nlink); -} - -static void cifs_open_info_to_fattr(struct cifs_fattr *fattr, struct cifs_open_info_data *data, - struct super_block *sb, bool adjust_tz, bool symlink, - u32 reparse_tag) -{ - struct smb2_file_all_info *info = &data->fi; - struct cifs_sb_info *cifs_sb = CIFS_SB(sb); - struct cifs_tcon *tcon = cifs_sb_master_tcon(cifs_sb); - - memset(fattr, 0, sizeof(*fattr)); - fattr->cf_cifsattrs = le32_to_cpu(info->Attributes); - if (info->DeletePending) - fattr->cf_flags |= CIFS_FATTR_DELETE_PENDING; - - if (info->LastAccessTime) - fattr->cf_atime = cifs_NTtimeToUnix(info->LastAccessTime); - else - ktime_get_coarse_real_ts64(&fattr->cf_atime); - - fattr->cf_ctime = cifs_NTtimeToUnix(info->ChangeTime); - fattr->cf_mtime = cifs_NTtimeToUnix(info->LastWriteTime); - - if (adjust_tz) { - fattr->cf_ctime.tv_sec += tcon->ses->server->timeAdj; - fattr->cf_mtime.tv_sec += tcon->ses->server->timeAdj; - } - - fattr->cf_eof = le64_to_cpu(info->EndOfFile); - fattr->cf_bytes = le64_to_cpu(info->AllocationSize); - fattr->cf_createtime = le64_to_cpu(info->CreationTime); - - fattr->cf_nlink = le32_to_cpu(info->NumberOfLinks); - if (reparse_tag == IO_REPARSE_TAG_LX_SYMLINK) { - fattr->cf_mode |= S_IFLNK | cifs_sb->ctx->file_mode; - fattr->cf_dtype = DT_LNK; - } else if (reparse_tag == IO_REPARSE_TAG_LX_FIFO) { - fattr->cf_mode |= S_IFIFO | cifs_sb->ctx->file_mode; - fattr->cf_dtype = DT_FIFO; - } else if (reparse_tag == IO_REPARSE_TAG_AF_UNIX) { - fattr->cf_mode |= S_IFSOCK | cifs_sb->ctx->file_mode; - fattr->cf_dtype = DT_SOCK; - } else if (reparse_tag == IO_REPARSE_TAG_LX_CHR) { - fattr->cf_mode |= S_IFCHR | cifs_sb->ctx->file_mode; - fattr->cf_dtype = DT_CHR; - } else if (reparse_tag == IO_REPARSE_TAG_LX_BLK) { - fattr->cf_mode |= S_IFBLK | cifs_sb->ctx->file_mode; - fattr->cf_dtype = DT_BLK; - } else if (symlink || reparse_tag == IO_REPARSE_TAG_SYMLINK || - reparse_tag == IO_REPARSE_TAG_NFS) { - fattr->cf_mode = S_IFLNK; - fattr->cf_dtype = DT_LNK; - } else if (fattr->cf_cifsattrs & ATTR_DIRECTORY) { - fattr->cf_mode = S_IFDIR | cifs_sb->ctx->dir_mode; - fattr->cf_dtype = DT_DIR; - /* - * Server can return wrong NumberOfLinks value for directories - * when Unix extensions are disabled - fake it. - */ - if (!tcon->unix_ext) - fattr->cf_flags |= CIFS_FATTR_UNKNOWN_NLINK; - } else { - fattr->cf_mode = S_IFREG | cifs_sb->ctx->file_mode; - fattr->cf_dtype = DT_REG; - - /* clear write bits if ATTR_READONLY is set */ - if (fattr->cf_cifsattrs & ATTR_READONLY) - fattr->cf_mode &= ~(S_IWUGO); - - /* - * Don't accept zero nlink from non-unix servers unless - * delete is pending. Instead mark it as unknown. - */ - if ((fattr->cf_nlink < 1) && !tcon->unix_ext && - !info->DeletePending) { - cifs_dbg(VFS, "bogus file nlink value %u\n", - fattr->cf_nlink); - fattr->cf_flags |= CIFS_FATTR_UNKNOWN_NLINK; - } - } - - if (S_ISLNK(fattr->cf_mode)) { - fattr->cf_symlink_target = data->symlink_target; - data->symlink_target = NULL; - } - - fattr->cf_uid = cifs_sb->ctx->linux_uid; - fattr->cf_gid = cifs_sb->ctx->linux_gid; -} - -static int -cifs_get_file_info(struct file *filp) -{ - int rc; - unsigned int xid; - struct cifs_open_info_data data = {}; - struct cifs_fattr fattr; - struct inode *inode = file_inode(filp); - struct cifsFileInfo *cfile = filp->private_data; - struct cifs_tcon *tcon = tlink_tcon(cfile->tlink); - struct TCP_Server_Info *server = tcon->ses->server; - bool symlink = false; - u32 tag = 0; - - if (!server->ops->query_file_info) - return -ENOSYS; - - xid = get_xid(); - rc = server->ops->query_file_info(xid, tcon, cfile, &data); - switch (rc) { - case 0: - /* TODO: add support to query reparse tag */ - if (data.symlink_target) { - symlink = true; - tag = IO_REPARSE_TAG_SYMLINK; - } - cifs_open_info_to_fattr(&fattr, &data, inode->i_sb, false, symlink, tag); - break; - case -EREMOTE: - cifs_create_dfs_fattr(&fattr, inode->i_sb); - rc = 0; - break; - case -EOPNOTSUPP: - case -EINVAL: - /* - * FIXME: legacy server -- fall back to path-based call? - * for now, just skip revalidating and mark inode for - * immediate reval. - */ - rc = 0; - CIFS_I(inode)->time = 0; - goto cgfi_exit; - default: - goto cgfi_exit; - } - - /* - * don't bother with SFU junk here -- just mark inode as needing - * revalidation. - */ - fattr.cf_uniqueid = CIFS_I(inode)->uniqueid; - fattr.cf_flags |= CIFS_FATTR_NEED_REVAL; - /* if filetype is different, return error */ - rc = cifs_fattr_to_inode(inode, &fattr); -cgfi_exit: - cifs_free_open_info(&data); - free_xid(xid); - return rc; -} - -/* Simple function to return a 64 bit hash of string. Rarely called */ -static __u64 simple_hashstr(const char *str) -{ - const __u64 hash_mult = 1125899906842597ULL; /* a big enough prime */ - __u64 hash = 0; - - while (*str) - hash = (hash + (__u64) *str++) * hash_mult; - - return hash; -} - -#ifdef CONFIG_CIFS_ALLOW_INSECURE_LEGACY -/** - * cifs_backup_query_path_info - SMB1 fallback code to get ino - * - * Fallback code to get file metadata when we don't have access to - * full_path (EACCES) and have backup creds. - * - * @xid: transaction id used to identify original request in logs - * @tcon: information about the server share we have mounted - * @sb: the superblock stores info such as disk space available - * @full_path: name of the file we are getting the metadata for - * @resp_buf: will be set to cifs resp buf and needs to be freed with - * cifs_buf_release() when done with @data - * @data: will be set to search info result buffer - */ -static int -cifs_backup_query_path_info(int xid, - struct cifs_tcon *tcon, - struct super_block *sb, - const char *full_path, - void **resp_buf, - FILE_ALL_INFO **data) -{ - struct cifs_sb_info *cifs_sb = CIFS_SB(sb); - struct cifs_search_info info = {0}; - u16 flags; - int rc; - - *resp_buf = NULL; - info.endOfSearch = false; - if (tcon->unix_ext) - info.info_level = SMB_FIND_FILE_UNIX; - else if ((tcon->ses->capabilities & - tcon->ses->server->vals->cap_nt_find) == 0) - info.info_level = SMB_FIND_FILE_INFO_STANDARD; - else if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_SERVER_INUM) - info.info_level = SMB_FIND_FILE_ID_FULL_DIR_INFO; - else /* no srvino useful for fallback to some netapp */ - info.info_level = SMB_FIND_FILE_DIRECTORY_INFO; - - flags = CIFS_SEARCH_CLOSE_ALWAYS | - CIFS_SEARCH_CLOSE_AT_END | - CIFS_SEARCH_BACKUP_SEARCH; - - rc = CIFSFindFirst(xid, tcon, full_path, - cifs_sb, NULL, flags, &info, false); - if (rc) - return rc; - - *resp_buf = (void *)info.ntwrk_buf_start; - *data = (FILE_ALL_INFO *)info.srch_entries_start; - return 0; -} -#endif /* CONFIG_CIFS_ALLOW_INSECURE_LEGACY */ - -static void cifs_set_fattr_ino(int xid, struct cifs_tcon *tcon, struct super_block *sb, - struct inode **inode, const char *full_path, - struct cifs_open_info_data *data, struct cifs_fattr *fattr) -{ - struct cifs_sb_info *cifs_sb = CIFS_SB(sb); - struct TCP_Server_Info *server = tcon->ses->server; - int rc; - - if (!(cifs_sb->mnt_cifs_flags & CIFS_MOUNT_SERVER_INUM)) { - if (*inode) - fattr->cf_uniqueid = CIFS_I(*inode)->uniqueid; - else - fattr->cf_uniqueid = iunique(sb, ROOT_I); - return; - } - - /* - * If we have an inode pass a NULL tcon to ensure we don't - * make a round trip to the server. This only works for SMB2+. - */ - rc = server->ops->get_srv_inum(xid, *inode ? NULL : tcon, cifs_sb, full_path, - &fattr->cf_uniqueid, data); - if (rc) { - /* - * If that fails reuse existing ino or generate one - * and disable server ones - */ - if (*inode) - fattr->cf_uniqueid = CIFS_I(*inode)->uniqueid; - else { - fattr->cf_uniqueid = iunique(sb, ROOT_I); - cifs_autodisable_serverino(cifs_sb); - } - return; - } - - /* If no errors, check for zero root inode (invalid) */ - if (fattr->cf_uniqueid == 0 && strlen(full_path) == 0) { - cifs_dbg(FYI, "Invalid (0) inodenum\n"); - if (*inode) { - /* reuse */ - fattr->cf_uniqueid = CIFS_I(*inode)->uniqueid; - } else { - /* make an ino by hashing the UNC */ - fattr->cf_flags |= CIFS_FATTR_FAKE_ROOT_INO; - fattr->cf_uniqueid = simple_hashstr(tcon->tree_name); - } - } -} - -static inline bool is_inode_cache_good(struct inode *ino) -{ - return ino && CIFS_CACHE_READ(CIFS_I(ino)) && CIFS_I(ino)->time != 0; -} - -int cifs_get_inode_info(struct inode **inode, const char *full_path, - struct cifs_open_info_data *data, struct super_block *sb, int xid, - const struct cifs_fid *fid) -{ - struct cifs_tcon *tcon; - struct TCP_Server_Info *server; - struct tcon_link *tlink; - struct cifs_sb_info *cifs_sb = CIFS_SB(sb); - bool adjust_tz = false; - struct cifs_fattr fattr = {0}; - bool is_reparse_point = false; - struct cifs_open_info_data tmp_data = {}; - void *smb1_backup_rsp_buf = NULL; - int rc = 0; - int tmprc = 0; - __u32 reparse_tag = 0; - - tlink = cifs_sb_tlink(cifs_sb); - if (IS_ERR(tlink)) - return PTR_ERR(tlink); - tcon = tlink_tcon(tlink); - server = tcon->ses->server; - - /* - * 1. Fetch file metadata if not provided (data) - */ - - if (!data) { - if (is_inode_cache_good(*inode)) { - cifs_dbg(FYI, "No need to revalidate cached inode sizes\n"); - goto out; - } - rc = server->ops->query_path_info(xid, tcon, cifs_sb, full_path, &tmp_data, - &adjust_tz, &is_reparse_point); - data = &tmp_data; - } - - /* - * 2. Convert it to internal cifs metadata (fattr) - */ - - switch (rc) { - case 0: - /* - * If the file is a reparse point, it is more complicated - * since we have to check if its reparse tag matches a known - * special file type e.g. symlink or fifo or char etc. - */ - if (is_reparse_point && data->symlink_target) { - reparse_tag = IO_REPARSE_TAG_SYMLINK; - } else if ((le32_to_cpu(data->fi.Attributes) & ATTR_REPARSE) && - server->ops->query_reparse_tag) { - tmprc = server->ops->query_reparse_tag(xid, tcon, cifs_sb, full_path, - &reparse_tag); - if (tmprc) - cifs_dbg(FYI, "%s: query_reparse_tag: rc = %d\n", __func__, tmprc); - if (server->ops->query_symlink) { - tmprc = server->ops->query_symlink(xid, tcon, cifs_sb, full_path, - &data->symlink_target, - is_reparse_point); - if (tmprc) - cifs_dbg(FYI, "%s: query_symlink: rc = %d\n", __func__, - tmprc); - } - } - cifs_open_info_to_fattr(&fattr, data, sb, adjust_tz, is_reparse_point, reparse_tag); - break; - case -EREMOTE: - /* DFS link, no metadata available on this server */ - cifs_create_dfs_fattr(&fattr, sb); - rc = 0; - break; - case -EACCES: -#ifdef CONFIG_CIFS_ALLOW_INSECURE_LEGACY - /* - * perm errors, try again with backup flags if possible - * - * For SMB2 and later the backup intent flag - * is already sent if needed on open and there - * is no path based FindFirst operation to use - * to retry with - */ - if (backup_cred(cifs_sb) && is_smb1_server(server)) { - /* for easier reading */ - FILE_ALL_INFO *fi; - FILE_DIRECTORY_INFO *fdi; - SEARCH_ID_FULL_DIR_INFO *si; - - rc = cifs_backup_query_path_info(xid, tcon, sb, - full_path, - &smb1_backup_rsp_buf, - &fi); - if (rc) - goto out; - - move_cifs_info_to_smb2(&data->fi, fi); - fdi = (FILE_DIRECTORY_INFO *)fi; - si = (SEARCH_ID_FULL_DIR_INFO *)fi; - - cifs_dir_info_to_fattr(&fattr, fdi, cifs_sb); - fattr.cf_uniqueid = le64_to_cpu(si->UniqueId); - /* uniqueid set, skip get inum step */ - goto handle_mnt_opt; - } else { - /* nothing we can do, bail out */ - goto out; - } -#else - goto out; -#endif /* CONFIG_CIFS_ALLOW_INSECURE_LEGACY */ - break; - default: - cifs_dbg(FYI, "%s: unhandled err rc %d\n", __func__, rc); - goto out; - } - - /* - * 3. Get or update inode number (fattr.cf_uniqueid) - */ - - cifs_set_fattr_ino(xid, tcon, sb, inode, full_path, data, &fattr); - - /* - * 4. Tweak fattr based on mount options - */ -#ifdef CONFIG_CIFS_ALLOW_INSECURE_LEGACY -handle_mnt_opt: -#endif /* CONFIG_CIFS_ALLOW_INSECURE_LEGACY */ - /* query for SFU type info if supported and needed */ - if (fattr.cf_cifsattrs & ATTR_SYSTEM && - cifs_sb->mnt_cifs_flags & CIFS_MOUNT_UNX_EMUL) { - tmprc = cifs_sfu_type(&fattr, full_path, cifs_sb, xid); - if (tmprc) - cifs_dbg(FYI, "cifs_sfu_type failed: %d\n", tmprc); - } - - /* fill in 0777 bits from ACL */ - if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_MODE_FROM_SID) { - rc = cifs_acl_to_fattr(cifs_sb, &fattr, *inode, true, - full_path, fid); - if (rc == -EREMOTE) - rc = 0; - if (rc) { - cifs_dbg(FYI, "%s: Get mode from SID failed. rc=%d\n", - __func__, rc); - goto out; - } - } else if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_CIFS_ACL) { - rc = cifs_acl_to_fattr(cifs_sb, &fattr, *inode, false, - full_path, fid); - if (rc == -EREMOTE) - rc = 0; - if (rc) { - cifs_dbg(FYI, "%s: Getting ACL failed with error: %d\n", - __func__, rc); - goto out; - } - } - - /* fill in remaining high mode bits e.g. SUID, VTX */ - if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_UNX_EMUL) - cifs_sfu_mode(&fattr, full_path, cifs_sb, xid); - - /* check for Minshall+French symlinks */ - if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_MF_SYMLINKS) { - tmprc = check_mf_symlink(xid, tcon, cifs_sb, &fattr, - full_path); - if (tmprc) - cifs_dbg(FYI, "check_mf_symlink: %d\n", tmprc); - } - - /* - * 5. Update inode with final fattr data - */ - - if (!*inode) { - *inode = cifs_iget(sb, &fattr); - if (!*inode) - rc = -ENOMEM; - } else { - /* we already have inode, update it */ - - /* if uniqueid is different, return error */ - if (unlikely(cifs_sb->mnt_cifs_flags & CIFS_MOUNT_SERVER_INUM && - CIFS_I(*inode)->uniqueid != fattr.cf_uniqueid)) { - CIFS_I(*inode)->time = 0; /* force reval */ - rc = -ESTALE; - goto out; - } - /* if filetype is different, return error */ - rc = cifs_fattr_to_inode(*inode, &fattr); - } -out: - cifs_buf_release(smb1_backup_rsp_buf); - cifs_put_tlink(tlink); - cifs_free_open_info(&tmp_data); - kfree(fattr.cf_symlink_target); - return rc; -} - -int -smb311_posix_get_inode_info(struct inode **inode, - const char *full_path, - struct super_block *sb, unsigned int xid) -{ - struct cifs_tcon *tcon; - struct tcon_link *tlink; - struct cifs_sb_info *cifs_sb = CIFS_SB(sb); - bool adjust_tz = false; - struct cifs_fattr fattr = {0}; - bool symlink = false; - struct cifs_open_info_data data = {}; - int rc = 0; - int tmprc = 0; - - tlink = cifs_sb_tlink(cifs_sb); - if (IS_ERR(tlink)) - return PTR_ERR(tlink); - tcon = tlink_tcon(tlink); - - /* - * 1. Fetch file metadata - */ - - if (is_inode_cache_good(*inode)) { - cifs_dbg(FYI, "No need to revalidate cached inode sizes\n"); - goto out; - } - - rc = smb311_posix_query_path_info(xid, tcon, cifs_sb, full_path, &data, &adjust_tz, - &symlink); - - /* - * 2. Convert it to internal cifs metadata (fattr) - */ - - switch (rc) { - case 0: - smb311_posix_info_to_fattr(&fattr, &data, sb, adjust_tz, symlink); - break; - case -EREMOTE: - /* DFS link, no metadata available on this server */ - cifs_create_dfs_fattr(&fattr, sb); - rc = 0; - break; - case -EACCES: - /* - * For SMB2 and later the backup intent flag - * is already sent if needed on open and there - * is no path based FindFirst operation to use - * to retry with so nothing we can do, bail out - */ - goto out; - default: - cifs_dbg(FYI, "%s: unhandled err rc %d\n", __func__, rc); - goto out; - } - - - /* - * 3. Tweak fattr based on mount options - */ - - /* check for Minshall+French symlinks */ - if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_MF_SYMLINKS) { - tmprc = check_mf_symlink(xid, tcon, cifs_sb, &fattr, - full_path); - if (tmprc) - cifs_dbg(FYI, "check_mf_symlink: %d\n", tmprc); - } - - /* - * 4. Update inode with final fattr data - */ - - if (!*inode) { - *inode = cifs_iget(sb, &fattr); - if (!*inode) - rc = -ENOMEM; - } else { - /* we already have inode, update it */ - - /* if uniqueid is different, return error */ - if (unlikely(cifs_sb->mnt_cifs_flags & CIFS_MOUNT_SERVER_INUM && - CIFS_I(*inode)->uniqueid != fattr.cf_uniqueid)) { - CIFS_I(*inode)->time = 0; /* force reval */ - rc = -ESTALE; - goto out; - } - - /* if filetype is different, return error */ - rc = cifs_fattr_to_inode(*inode, &fattr); - } -out: - cifs_put_tlink(tlink); - cifs_free_open_info(&data); - kfree(fattr.cf_symlink_target); - return rc; -} - - -static const struct inode_operations cifs_ipc_inode_ops = { - .lookup = cifs_lookup, -}; - -static int -cifs_find_inode(struct inode *inode, void *opaque) -{ - struct cifs_fattr *fattr = opaque; - - /* don't match inode with different uniqueid */ - if (CIFS_I(inode)->uniqueid != fattr->cf_uniqueid) - return 0; - - /* use createtime like an i_generation field */ - if (CIFS_I(inode)->createtime != fattr->cf_createtime) - return 0; - - /* don't match inode of different type */ - if (inode_wrong_type(inode, fattr->cf_mode)) - return 0; - - /* if it's not a directory or has no dentries, then flag it */ - if (S_ISDIR(inode->i_mode) && !hlist_empty(&inode->i_dentry)) - fattr->cf_flags |= CIFS_FATTR_INO_COLLISION; - - return 1; -} - -static int -cifs_init_inode(struct inode *inode, void *opaque) -{ - struct cifs_fattr *fattr = opaque; - - CIFS_I(inode)->uniqueid = fattr->cf_uniqueid; - CIFS_I(inode)->createtime = fattr->cf_createtime; - return 0; -} - -/* - * walk dentry list for an inode and report whether it has aliases that - * are hashed. We use this to determine if a directory inode can actually - * be used. - */ -static bool -inode_has_hashed_dentries(struct inode *inode) -{ - struct dentry *dentry; - - spin_lock(&inode->i_lock); - hlist_for_each_entry(dentry, &inode->i_dentry, d_u.d_alias) { - if (!d_unhashed(dentry) || IS_ROOT(dentry)) { - spin_unlock(&inode->i_lock); - return true; - } - } - spin_unlock(&inode->i_lock); - return false; -} - -/* Given fattrs, get a corresponding inode */ -struct inode * -cifs_iget(struct super_block *sb, struct cifs_fattr *fattr) -{ - unsigned long hash; - struct inode *inode; - -retry_iget5_locked: - cifs_dbg(FYI, "looking for uniqueid=%llu\n", fattr->cf_uniqueid); - - /* hash down to 32-bits on 32-bit arch */ - hash = cifs_uniqueid_to_ino_t(fattr->cf_uniqueid); - - inode = iget5_locked(sb, hash, cifs_find_inode, cifs_init_inode, fattr); - if (inode) { - /* was there a potentially problematic inode collision? */ - if (fattr->cf_flags & CIFS_FATTR_INO_COLLISION) { - fattr->cf_flags &= ~CIFS_FATTR_INO_COLLISION; - - if (inode_has_hashed_dentries(inode)) { - cifs_autodisable_serverino(CIFS_SB(sb)); - iput(inode); - fattr->cf_uniqueid = iunique(sb, ROOT_I); - goto retry_iget5_locked; - } - } - - /* can't fail - see cifs_find_inode() */ - cifs_fattr_to_inode(inode, fattr); - if (sb->s_flags & SB_NOATIME) - inode->i_flags |= S_NOATIME | S_NOCMTIME; - if (inode->i_state & I_NEW) { - inode->i_ino = hash; - cifs_fscache_get_inode_cookie(inode); - unlock_new_inode(inode); - } - } - - return inode; -} - -/* gets root inode */ -struct inode *cifs_root_iget(struct super_block *sb) -{ - unsigned int xid; - struct cifs_sb_info *cifs_sb = CIFS_SB(sb); - struct inode *inode = NULL; - long rc; - struct cifs_tcon *tcon = cifs_sb_master_tcon(cifs_sb); - char *path = NULL; - int len; - - if ((cifs_sb->mnt_cifs_flags & CIFS_MOUNT_USE_PREFIX_PATH) - && cifs_sb->prepath) { - len = strlen(cifs_sb->prepath); - path = kzalloc(len + 2 /* leading sep + null */, GFP_KERNEL); - if (path == NULL) - return ERR_PTR(-ENOMEM); - path[0] = '/'; - memcpy(path+1, cifs_sb->prepath, len); - } else { - path = kstrdup("", GFP_KERNEL); - if (path == NULL) - return ERR_PTR(-ENOMEM); - } - - xid = get_xid(); - if (tcon->unix_ext) { - rc = cifs_get_inode_info_unix(&inode, path, sb, xid); - /* some servers mistakenly claim POSIX support */ - if (rc != -EOPNOTSUPP) - goto iget_no_retry; - cifs_dbg(VFS, "server does not support POSIX extensions\n"); - tcon->unix_ext = false; - } - - convert_delimiter(path, CIFS_DIR_SEP(cifs_sb)); - if (tcon->posix_extensions) - rc = smb311_posix_get_inode_info(&inode, path, sb, xid); - else - rc = cifs_get_inode_info(&inode, path, NULL, sb, xid, NULL); - -iget_no_retry: - if (!inode) { - inode = ERR_PTR(rc); - goto out; - } - - if (rc && tcon->pipe) { - cifs_dbg(FYI, "ipc connection - fake read inode\n"); - spin_lock(&inode->i_lock); - inode->i_mode |= S_IFDIR; - set_nlink(inode, 2); - inode->i_op = &cifs_ipc_inode_ops; - inode->i_fop = &simple_dir_operations; - inode->i_uid = cifs_sb->ctx->linux_uid; - inode->i_gid = cifs_sb->ctx->linux_gid; - spin_unlock(&inode->i_lock); - } else if (rc) { - iget_failed(inode); - inode = ERR_PTR(rc); - } - -out: - kfree(path); - free_xid(xid); - return inode; -} - -int -cifs_set_file_info(struct inode *inode, struct iattr *attrs, unsigned int xid, - const char *full_path, __u32 dosattr) -{ - bool set_time = false; - struct cifs_sb_info *cifs_sb = CIFS_SB(inode->i_sb); - struct TCP_Server_Info *server; - FILE_BASIC_INFO info_buf; - - if (attrs == NULL) - return -EINVAL; - - server = cifs_sb_master_tcon(cifs_sb)->ses->server; - if (!server->ops->set_file_info) - return -ENOSYS; - - info_buf.Pad = 0; - - if (attrs->ia_valid & ATTR_ATIME) { - set_time = true; - info_buf.LastAccessTime = - cpu_to_le64(cifs_UnixTimeToNT(attrs->ia_atime)); - } else - info_buf.LastAccessTime = 0; - - if (attrs->ia_valid & ATTR_MTIME) { - set_time = true; - info_buf.LastWriteTime = - cpu_to_le64(cifs_UnixTimeToNT(attrs->ia_mtime)); - } else - info_buf.LastWriteTime = 0; - - /* - * Samba throws this field away, but windows may actually use it. - * Do not set ctime unless other time stamps are changed explicitly - * (i.e. by utimes()) since we would then have a mix of client and - * server times. - */ - if (set_time && (attrs->ia_valid & ATTR_CTIME)) { - cifs_dbg(FYI, "CIFS - CTIME changed\n"); - info_buf.ChangeTime = - cpu_to_le64(cifs_UnixTimeToNT(attrs->ia_ctime)); - } else - info_buf.ChangeTime = 0; - - info_buf.CreationTime = 0; /* don't change */ - info_buf.Attributes = cpu_to_le32(dosattr); - - return server->ops->set_file_info(inode, full_path, &info_buf, xid); -} - -#ifdef CONFIG_CIFS_ALLOW_INSECURE_LEGACY -/* - * Open the given file (if it isn't already), set the DELETE_ON_CLOSE bit - * and rename it to a random name that hopefully won't conflict with - * anything else. - */ -int -cifs_rename_pending_delete(const char *full_path, struct dentry *dentry, - const unsigned int xid) -{ - int oplock = 0; - int rc; - struct cifs_fid fid; - struct cifs_open_parms oparms; - struct inode *inode = d_inode(dentry); - struct cifsInodeInfo *cifsInode = CIFS_I(inode); - struct cifs_sb_info *cifs_sb = CIFS_SB(inode->i_sb); - struct tcon_link *tlink; - struct cifs_tcon *tcon; - __u32 dosattr, origattr; - FILE_BASIC_INFO *info_buf = NULL; - - tlink = cifs_sb_tlink(cifs_sb); - if (IS_ERR(tlink)) - return PTR_ERR(tlink); - tcon = tlink_tcon(tlink); - - /* - * We cannot rename the file if the server doesn't support - * CAP_INFOLEVEL_PASSTHRU - */ - if (!(tcon->ses->capabilities & CAP_INFOLEVEL_PASSTHRU)) { - rc = -EBUSY; - goto out; - } - - oparms = (struct cifs_open_parms) { - .tcon = tcon, - .cifs_sb = cifs_sb, - .desired_access = DELETE | FILE_WRITE_ATTRIBUTES, - .create_options = cifs_create_options(cifs_sb, CREATE_NOT_DIR), - .disposition = FILE_OPEN, - .path = full_path, - .fid = &fid, - }; - - rc = CIFS_open(xid, &oparms, &oplock, NULL); - if (rc != 0) - goto out; - - origattr = cifsInode->cifsAttrs; - if (origattr == 0) - origattr |= ATTR_NORMAL; - - dosattr = origattr & ~ATTR_READONLY; - if (dosattr == 0) - dosattr |= ATTR_NORMAL; - dosattr |= ATTR_HIDDEN; - - /* set ATTR_HIDDEN and clear ATTR_READONLY, but only if needed */ - if (dosattr != origattr) { - info_buf = kzalloc(sizeof(*info_buf), GFP_KERNEL); - if (info_buf == NULL) { - rc = -ENOMEM; - goto out_close; - } - info_buf->Attributes = cpu_to_le32(dosattr); - rc = CIFSSMBSetFileInfo(xid, tcon, info_buf, fid.netfid, - current->tgid); - /* although we would like to mark the file hidden - if that fails we will still try to rename it */ - if (!rc) - cifsInode->cifsAttrs = dosattr; - else - dosattr = origattr; /* since not able to change them */ - } - - /* rename the file */ - rc = CIFSSMBRenameOpenFile(xid, tcon, fid.netfid, NULL, - cifs_sb->local_nls, - cifs_remap(cifs_sb)); - if (rc != 0) { - rc = -EBUSY; - goto undo_setattr; - } - - /* try to set DELETE_ON_CLOSE */ - if (!test_bit(CIFS_INO_DELETE_PENDING, &cifsInode->flags)) { - rc = CIFSSMBSetFileDisposition(xid, tcon, true, fid.netfid, - current->tgid); - /* - * some samba versions return -ENOENT when we try to set the - * file disposition here. Likely a samba bug, but work around - * it for now. This means that some cifsXXX files may hang - * around after they shouldn't. - * - * BB: remove this hack after more servers have the fix - */ - if (rc == -ENOENT) - rc = 0; - else if (rc != 0) { - rc = -EBUSY; - goto undo_rename; - } - set_bit(CIFS_INO_DELETE_PENDING, &cifsInode->flags); - } - -out_close: - CIFSSMBClose(xid, tcon, fid.netfid); -out: - kfree(info_buf); - cifs_put_tlink(tlink); - return rc; - - /* - * reset everything back to the original state. Don't bother - * dealing with errors here since we can't do anything about - * them anyway. - */ -undo_rename: - CIFSSMBRenameOpenFile(xid, tcon, fid.netfid, dentry->d_name.name, - cifs_sb->local_nls, cifs_remap(cifs_sb)); -undo_setattr: - if (dosattr != origattr) { - info_buf->Attributes = cpu_to_le32(origattr); - if (!CIFSSMBSetFileInfo(xid, tcon, info_buf, fid.netfid, - current->tgid)) - cifsInode->cifsAttrs = origattr; - } - - goto out_close; -} -#endif /* CONFIG_CIFS_ALLOW_INSECURE_LEGACY */ - -/* copied from fs/nfs/dir.c with small changes */ -static void -cifs_drop_nlink(struct inode *inode) -{ - spin_lock(&inode->i_lock); - if (inode->i_nlink > 0) - drop_nlink(inode); - spin_unlock(&inode->i_lock); -} - -/* - * If d_inode(dentry) is null (usually meaning the cached dentry - * is a negative dentry) then we would attempt a standard SMB delete, but - * if that fails we can not attempt the fall back mechanisms on EACCES - * but will return the EACCES to the caller. Note that the VFS does not call - * unlink on negative dentries currently. - */ -int cifs_unlink(struct inode *dir, struct dentry *dentry) -{ - int rc = 0; - unsigned int xid; - const char *full_path; - void *page; - struct inode *inode = d_inode(dentry); - struct cifsInodeInfo *cifs_inode; - struct super_block *sb = dir->i_sb; - struct cifs_sb_info *cifs_sb = CIFS_SB(sb); - struct tcon_link *tlink; - struct cifs_tcon *tcon; - struct TCP_Server_Info *server; - struct iattr *attrs = NULL; - __u32 dosattr = 0, origattr = 0; - - cifs_dbg(FYI, "cifs_unlink, dir=0x%p, dentry=0x%p\n", dir, dentry); - - if (unlikely(cifs_forced_shutdown(cifs_sb))) - return -EIO; - - tlink = cifs_sb_tlink(cifs_sb); - if (IS_ERR(tlink)) - return PTR_ERR(tlink); - tcon = tlink_tcon(tlink); - server = tcon->ses->server; - - xid = get_xid(); - page = alloc_dentry_path(); - - if (tcon->nodelete) { - rc = -EACCES; - goto unlink_out; - } - - /* Unlink can be called from rename so we can not take the - * sb->s_vfs_rename_mutex here */ - full_path = build_path_from_dentry(dentry, page); - if (IS_ERR(full_path)) { - rc = PTR_ERR(full_path); - goto unlink_out; - } - - cifs_close_deferred_file_under_dentry(tcon, full_path); -#ifdef CONFIG_CIFS_ALLOW_INSECURE_LEGACY - if (cap_unix(tcon->ses) && (CIFS_UNIX_POSIX_PATH_OPS_CAP & - le64_to_cpu(tcon->fsUnixInfo.Capability))) { - rc = CIFSPOSIXDelFile(xid, tcon, full_path, - SMB_POSIX_UNLINK_FILE_TARGET, cifs_sb->local_nls, - cifs_remap(cifs_sb)); - cifs_dbg(FYI, "posix del rc %d\n", rc); - if ((rc == 0) || (rc == -ENOENT)) - goto psx_del_no_retry; - } -#endif /* CONFIG_CIFS_ALLOW_INSECURE_LEGACY */ - -retry_std_delete: - if (!server->ops->unlink) { - rc = -ENOSYS; - goto psx_del_no_retry; - } - - rc = server->ops->unlink(xid, tcon, full_path, cifs_sb); - -psx_del_no_retry: - if (!rc) { - if (inode) - cifs_drop_nlink(inode); - } else if (rc == -ENOENT) { - d_drop(dentry); - } else if (rc == -EBUSY) { - if (server->ops->rename_pending_delete) { - rc = server->ops->rename_pending_delete(full_path, - dentry, xid); - if (rc == 0) - cifs_drop_nlink(inode); - } - } else if ((rc == -EACCES) && (dosattr == 0) && inode) { - attrs = kzalloc(sizeof(*attrs), GFP_KERNEL); - if (attrs == NULL) { - rc = -ENOMEM; - goto out_reval; - } - - /* try to reset dos attributes */ - cifs_inode = CIFS_I(inode); - origattr = cifs_inode->cifsAttrs; - if (origattr == 0) - origattr |= ATTR_NORMAL; - dosattr = origattr & ~ATTR_READONLY; - if (dosattr == 0) - dosattr |= ATTR_NORMAL; - dosattr |= ATTR_HIDDEN; - - rc = cifs_set_file_info(inode, attrs, xid, full_path, dosattr); - if (rc != 0) - goto out_reval; - - goto retry_std_delete; - } - - /* undo the setattr if we errored out and it's needed */ - if (rc != 0 && dosattr != 0) - cifs_set_file_info(inode, attrs, xid, full_path, origattr); - -out_reval: - if (inode) { - cifs_inode = CIFS_I(inode); - cifs_inode->time = 0; /* will force revalidate to get info - when needed */ - inode->i_ctime = current_time(inode); - } - dir->i_ctime = dir->i_mtime = current_time(dir); - cifs_inode = CIFS_I(dir); - CIFS_I(dir)->time = 0; /* force revalidate of dir as well */ -unlink_out: - free_dentry_path(page); - kfree(attrs); - free_xid(xid); - cifs_put_tlink(tlink); - return rc; -} - -static int -cifs_mkdir_qinfo(struct inode *parent, struct dentry *dentry, umode_t mode, - const char *full_path, struct cifs_sb_info *cifs_sb, - struct cifs_tcon *tcon, const unsigned int xid) -{ - int rc = 0; - struct inode *inode = NULL; - - if (tcon->posix_extensions) - rc = smb311_posix_get_inode_info(&inode, full_path, parent->i_sb, xid); -#ifdef CONFIG_CIFS_ALLOW_INSECURE_LEGACY - else if (tcon->unix_ext) - rc = cifs_get_inode_info_unix(&inode, full_path, parent->i_sb, - xid); -#endif /* CONFIG_CIFS_ALLOW_INSECURE_LEGACY */ - else - rc = cifs_get_inode_info(&inode, full_path, NULL, parent->i_sb, - xid, NULL); - - if (rc) - return rc; - - if (!S_ISDIR(inode->i_mode)) { - /* - * mkdir succeeded, but another client has managed to remove the - * sucker and replace it with non-directory. Return success, - * but don't leave the child in dcache. - */ - iput(inode); - d_drop(dentry); - return 0; - } - /* - * setting nlink not necessary except in cases where we failed to get it - * from the server or was set bogus. Also, since this is a brand new - * inode, no need to grab the i_lock before setting the i_nlink. - */ - if (inode->i_nlink < 2) - set_nlink(inode, 2); - mode &= ~current_umask(); - /* must turn on setgid bit if parent dir has it */ - if (parent->i_mode & S_ISGID) - mode |= S_ISGID; - -#ifdef CONFIG_CIFS_ALLOW_INSECURE_LEGACY - if (tcon->unix_ext) { - struct cifs_unix_set_info_args args = { - .mode = mode, - .ctime = NO_CHANGE_64, - .atime = NO_CHANGE_64, - .mtime = NO_CHANGE_64, - .device = 0, - }; - if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_SET_UID) { - args.uid = current_fsuid(); - if (parent->i_mode & S_ISGID) - args.gid = parent->i_gid; - else - args.gid = current_fsgid(); - } else { - args.uid = INVALID_UID; /* no change */ - args.gid = INVALID_GID; /* no change */ - } - CIFSSMBUnixSetPathInfo(xid, tcon, full_path, &args, - cifs_sb->local_nls, - cifs_remap(cifs_sb)); - } else { -#else - { -#endif /* CONFIG_CIFS_ALLOW_INSECURE_LEGACY */ - struct TCP_Server_Info *server = tcon->ses->server; - if (!(cifs_sb->mnt_cifs_flags & CIFS_MOUNT_CIFS_ACL) && - (mode & S_IWUGO) == 0 && server->ops->mkdir_setinfo) - server->ops->mkdir_setinfo(inode, full_path, cifs_sb, - tcon, xid); - if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_DYNPERM) - inode->i_mode = (mode | S_IFDIR); - - if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_SET_UID) { - inode->i_uid = current_fsuid(); - if (inode->i_mode & S_ISGID) - inode->i_gid = parent->i_gid; - else - inode->i_gid = current_fsgid(); - } - } - d_instantiate(dentry, inode); - return 0; -} - -#ifdef CONFIG_CIFS_ALLOW_INSECURE_LEGACY -static int -cifs_posix_mkdir(struct inode *inode, struct dentry *dentry, umode_t mode, - const char *full_path, struct cifs_sb_info *cifs_sb, - struct cifs_tcon *tcon, const unsigned int xid) -{ - int rc = 0; - u32 oplock = 0; - FILE_UNIX_BASIC_INFO *info = NULL; - struct inode *newinode = NULL; - struct cifs_fattr fattr; - - info = kzalloc(sizeof(FILE_UNIX_BASIC_INFO), GFP_KERNEL); - if (info == NULL) { - rc = -ENOMEM; - goto posix_mkdir_out; - } - - mode &= ~current_umask(); - rc = CIFSPOSIXCreate(xid, tcon, SMB_O_DIRECTORY | SMB_O_CREAT, mode, - NULL /* netfid */, info, &oplock, full_path, - cifs_sb->local_nls, cifs_remap(cifs_sb)); - if (rc == -EOPNOTSUPP) - goto posix_mkdir_out; - else if (rc) { - cifs_dbg(FYI, "posix mkdir returned 0x%x\n", rc); - d_drop(dentry); - goto posix_mkdir_out; - } - - if (info->Type == cpu_to_le32(-1)) - /* no return info, go query for it */ - goto posix_mkdir_get_info; - /* - * BB check (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_SET_UID ) to see if - * need to set uid/gid. - */ - - cifs_unix_basic_to_fattr(&fattr, info, cifs_sb); - cifs_fill_uniqueid(inode->i_sb, &fattr); - newinode = cifs_iget(inode->i_sb, &fattr); - if (!newinode) - goto posix_mkdir_get_info; - - d_instantiate(dentry, newinode); - -#ifdef CONFIG_CIFS_DEBUG2 - cifs_dbg(FYI, "instantiated dentry %p %pd to inode %p\n", - dentry, dentry, newinode); - - if (newinode->i_nlink != 2) - cifs_dbg(FYI, "unexpected number of links %d\n", - newinode->i_nlink); -#endif - -posix_mkdir_out: - kfree(info); - return rc; -posix_mkdir_get_info: - rc = cifs_mkdir_qinfo(inode, dentry, mode, full_path, cifs_sb, tcon, - xid); - goto posix_mkdir_out; -} -#endif /* CONFIG_CIFS_ALLOW_INSECURE_LEGACY */ - -int cifs_mkdir(struct user_namespace *mnt_userns, struct inode *inode, - struct dentry *direntry, umode_t mode) -{ - int rc = 0; - unsigned int xid; - struct cifs_sb_info *cifs_sb; - struct tcon_link *tlink; - struct cifs_tcon *tcon; - struct TCP_Server_Info *server; - const char *full_path; - void *page; - - cifs_dbg(FYI, "In cifs_mkdir, mode = %04ho inode = 0x%p\n", - mode, inode); - - cifs_sb = CIFS_SB(inode->i_sb); - if (unlikely(cifs_forced_shutdown(cifs_sb))) - return -EIO; - tlink = cifs_sb_tlink(cifs_sb); - if (IS_ERR(tlink)) - return PTR_ERR(tlink); - tcon = tlink_tcon(tlink); - - xid = get_xid(); - - page = alloc_dentry_path(); - full_path = build_path_from_dentry(direntry, page); - if (IS_ERR(full_path)) { - rc = PTR_ERR(full_path); - goto mkdir_out; - } - - server = tcon->ses->server; - - if ((server->ops->posix_mkdir) && (tcon->posix_extensions)) { - rc = server->ops->posix_mkdir(xid, inode, mode, tcon, full_path, - cifs_sb); - d_drop(direntry); /* for time being always refresh inode info */ - goto mkdir_out; - } - -#ifdef CONFIG_CIFS_ALLOW_INSECURE_LEGACY - if (cap_unix(tcon->ses) && (CIFS_UNIX_POSIX_PATH_OPS_CAP & - le64_to_cpu(tcon->fsUnixInfo.Capability))) { - rc = cifs_posix_mkdir(inode, direntry, mode, full_path, cifs_sb, - tcon, xid); - if (rc != -EOPNOTSUPP) - goto mkdir_out; - } -#endif /* CONFIG_CIFS_ALLOW_INSECURE_LEGACY */ - - if (!server->ops->mkdir) { - rc = -ENOSYS; - goto mkdir_out; - } - - /* BB add setting the equivalent of mode via CreateX w/ACLs */ - rc = server->ops->mkdir(xid, inode, mode, tcon, full_path, cifs_sb); - if (rc) { - cifs_dbg(FYI, "cifs_mkdir returned 0x%x\n", rc); - d_drop(direntry); - goto mkdir_out; - } - - /* TODO: skip this for smb2/smb3 */ - rc = cifs_mkdir_qinfo(inode, direntry, mode, full_path, cifs_sb, tcon, - xid); -mkdir_out: - /* - * Force revalidate to get parent dir info when needed since cached - * attributes are invalid now. - */ - CIFS_I(inode)->time = 0; - free_dentry_path(page); - free_xid(xid); - cifs_put_tlink(tlink); - return rc; -} - -int cifs_rmdir(struct inode *inode, struct dentry *direntry) -{ - int rc = 0; - unsigned int xid; - struct cifs_sb_info *cifs_sb; - struct tcon_link *tlink; - struct cifs_tcon *tcon; - struct TCP_Server_Info *server; - const char *full_path; - void *page = alloc_dentry_path(); - struct cifsInodeInfo *cifsInode; - - cifs_dbg(FYI, "cifs_rmdir, inode = 0x%p\n", inode); - - xid = get_xid(); - - full_path = build_path_from_dentry(direntry, page); - if (IS_ERR(full_path)) { - rc = PTR_ERR(full_path); - goto rmdir_exit; - } - - cifs_sb = CIFS_SB(inode->i_sb); - if (unlikely(cifs_forced_shutdown(cifs_sb))) { - rc = -EIO; - goto rmdir_exit; - } - - tlink = cifs_sb_tlink(cifs_sb); - if (IS_ERR(tlink)) { - rc = PTR_ERR(tlink); - goto rmdir_exit; - } - tcon = tlink_tcon(tlink); - server = tcon->ses->server; - - if (!server->ops->rmdir) { - rc = -ENOSYS; - cifs_put_tlink(tlink); - goto rmdir_exit; - } - - if (tcon->nodelete) { - rc = -EACCES; - cifs_put_tlink(tlink); - goto rmdir_exit; - } - - rc = server->ops->rmdir(xid, tcon, full_path, cifs_sb); - cifs_put_tlink(tlink); - - if (!rc) { - spin_lock(&d_inode(direntry)->i_lock); - i_size_write(d_inode(direntry), 0); - clear_nlink(d_inode(direntry)); - spin_unlock(&d_inode(direntry)->i_lock); - } - - cifsInode = CIFS_I(d_inode(direntry)); - /* force revalidate to go get info when needed */ - cifsInode->time = 0; - - cifsInode = CIFS_I(inode); - /* - * Force revalidate to get parent dir info when needed since cached - * attributes are invalid now. - */ - cifsInode->time = 0; - - d_inode(direntry)->i_ctime = inode->i_ctime = inode->i_mtime = - current_time(inode); - -rmdir_exit: - free_dentry_path(page); - free_xid(xid); - return rc; -} - -static int -cifs_do_rename(const unsigned int xid, struct dentry *from_dentry, - const char *from_path, struct dentry *to_dentry, - const char *to_path) -{ - struct cifs_sb_info *cifs_sb = CIFS_SB(from_dentry->d_sb); - struct tcon_link *tlink; - struct cifs_tcon *tcon; - struct TCP_Server_Info *server; -#ifdef CONFIG_CIFS_ALLOW_INSECURE_LEGACY - struct cifs_fid fid; - struct cifs_open_parms oparms; - int oplock; -#endif /* CONFIG_CIFS_ALLOW_INSECURE_LEGACY */ - int rc; - - tlink = cifs_sb_tlink(cifs_sb); - if (IS_ERR(tlink)) - return PTR_ERR(tlink); - tcon = tlink_tcon(tlink); - server = tcon->ses->server; - - if (!server->ops->rename) - return -ENOSYS; - - /* try path-based rename first */ - rc = server->ops->rename(xid, tcon, from_path, to_path, cifs_sb); - - /* - * Don't bother with rename by filehandle unless file is busy and - * source. Note that cross directory moves do not work with - * rename by filehandle to various Windows servers. - */ - if (rc == 0 || rc != -EBUSY) - goto do_rename_exit; - - /* Don't fall back to using SMB on SMB 2+ mount */ - if (server->vals->protocol_id != 0) - goto do_rename_exit; - -#ifdef CONFIG_CIFS_ALLOW_INSECURE_LEGACY - /* open-file renames don't work across directories */ - if (to_dentry->d_parent != from_dentry->d_parent) - goto do_rename_exit; - - oparms = (struct cifs_open_parms) { - .tcon = tcon, - .cifs_sb = cifs_sb, - /* open the file to be renamed -- we need DELETE perms */ - .desired_access = DELETE, - .create_options = cifs_create_options(cifs_sb, CREATE_NOT_DIR), - .disposition = FILE_OPEN, - .path = from_path, - .fid = &fid, - }; - - rc = CIFS_open(xid, &oparms, &oplock, NULL); - if (rc == 0) { - rc = CIFSSMBRenameOpenFile(xid, tcon, fid.netfid, - (const char *) to_dentry->d_name.name, - cifs_sb->local_nls, cifs_remap(cifs_sb)); - CIFSSMBClose(xid, tcon, fid.netfid); - } -#endif /* CONFIG_CIFS_ALLOW_INSECURE_LEGACY */ -do_rename_exit: - if (rc == 0) - d_move(from_dentry, to_dentry); - cifs_put_tlink(tlink); - return rc; -} - -int -cifs_rename2(struct user_namespace *mnt_userns, struct inode *source_dir, - struct dentry *source_dentry, struct inode *target_dir, - struct dentry *target_dentry, unsigned int flags) -{ - const char *from_name, *to_name; - void *page1, *page2; - struct cifs_sb_info *cifs_sb; - struct tcon_link *tlink; - struct cifs_tcon *tcon; - unsigned int xid; - int rc, tmprc; - int retry_count = 0; - FILE_UNIX_BASIC_INFO *info_buf_source = NULL; -#ifdef CONFIG_CIFS_ALLOW_INSECURE_LEGACY - FILE_UNIX_BASIC_INFO *info_buf_target; -#endif /* CONFIG_CIFS_ALLOW_INSECURE_LEGACY */ - - if (flags & ~RENAME_NOREPLACE) - return -EINVAL; - - cifs_sb = CIFS_SB(source_dir->i_sb); - if (unlikely(cifs_forced_shutdown(cifs_sb))) - return -EIO; - - tlink = cifs_sb_tlink(cifs_sb); - if (IS_ERR(tlink)) - return PTR_ERR(tlink); - tcon = tlink_tcon(tlink); - - page1 = alloc_dentry_path(); - page2 = alloc_dentry_path(); - xid = get_xid(); - - from_name = build_path_from_dentry(source_dentry, page1); - if (IS_ERR(from_name)) { - rc = PTR_ERR(from_name); - goto cifs_rename_exit; - } - - to_name = build_path_from_dentry(target_dentry, page2); - if (IS_ERR(to_name)) { - rc = PTR_ERR(to_name); - goto cifs_rename_exit; - } - - cifs_close_deferred_file_under_dentry(tcon, from_name); - if (d_inode(target_dentry) != NULL) - cifs_close_deferred_file_under_dentry(tcon, to_name); - - rc = cifs_do_rename(xid, source_dentry, from_name, target_dentry, - to_name); - - if (rc == -EACCES) { - while (retry_count < 3) { - cifs_close_all_deferred_files(tcon); - rc = cifs_do_rename(xid, source_dentry, from_name, target_dentry, - to_name); - if (rc != -EACCES) - break; - retry_count++; - } - } - - /* - * No-replace is the natural behavior for CIFS, so skip unlink hacks. - */ - if (flags & RENAME_NOREPLACE) - goto cifs_rename_exit; - -#ifdef CONFIG_CIFS_ALLOW_INSECURE_LEGACY - if (rc == -EEXIST && tcon->unix_ext) { - /* - * Are src and dst hardlinks of same inode? We can only tell - * with unix extensions enabled. - */ - info_buf_source = - kmalloc_array(2, sizeof(FILE_UNIX_BASIC_INFO), - GFP_KERNEL); - if (info_buf_source == NULL) { - rc = -ENOMEM; - goto cifs_rename_exit; - } - - info_buf_target = info_buf_source + 1; - tmprc = CIFSSMBUnixQPathInfo(xid, tcon, from_name, - info_buf_source, - cifs_sb->local_nls, - cifs_remap(cifs_sb)); - if (tmprc != 0) - goto unlink_target; - - tmprc = CIFSSMBUnixQPathInfo(xid, tcon, to_name, - info_buf_target, - cifs_sb->local_nls, - cifs_remap(cifs_sb)); - - if (tmprc == 0 && (info_buf_source->UniqueId == - info_buf_target->UniqueId)) { - /* same file, POSIX says that this is a noop */ - rc = 0; - goto cifs_rename_exit; - } - } - /* - * else ... BB we could add the same check for Windows by - * checking the UniqueId via FILE_INTERNAL_INFO - */ - -unlink_target: -#endif /* CONFIG_CIFS_ALLOW_INSECURE_LEGACY */ - - /* Try unlinking the target dentry if it's not negative */ - if (d_really_is_positive(target_dentry) && (rc == -EACCES || rc == -EEXIST)) { - if (d_is_dir(target_dentry)) - tmprc = cifs_rmdir(target_dir, target_dentry); - else - tmprc = cifs_unlink(target_dir, target_dentry); - if (tmprc) - goto cifs_rename_exit; - rc = cifs_do_rename(xid, source_dentry, from_name, - target_dentry, to_name); - } - - /* force revalidate to go get info when needed */ - CIFS_I(source_dir)->time = CIFS_I(target_dir)->time = 0; - - source_dir->i_ctime = source_dir->i_mtime = target_dir->i_ctime = - target_dir->i_mtime = current_time(source_dir); - -cifs_rename_exit: - kfree(info_buf_source); - free_dentry_path(page2); - free_dentry_path(page1); - free_xid(xid); - cifs_put_tlink(tlink); - return rc; -} - -static bool -cifs_dentry_needs_reval(struct dentry *dentry) -{ - struct inode *inode = d_inode(dentry); - struct cifsInodeInfo *cifs_i = CIFS_I(inode); - struct cifs_sb_info *cifs_sb = CIFS_SB(inode->i_sb); - struct cifs_tcon *tcon = cifs_sb_master_tcon(cifs_sb); - struct cached_fid *cfid = NULL; - - if (cifs_i->time == 0) - return true; - - if (CIFS_CACHE_READ(cifs_i)) - return false; - - if (!lookupCacheEnabled) - return true; - - if (!open_cached_dir_by_dentry(tcon, dentry->d_parent, &cfid)) { - spin_lock(&cfid->fid_lock); - if (cfid->time && cifs_i->time > cfid->time) { - spin_unlock(&cfid->fid_lock); - close_cached_dir(cfid); - return false; - } - spin_unlock(&cfid->fid_lock); - close_cached_dir(cfid); - } - /* - * depending on inode type, check if attribute caching disabled for - * files or directories - */ - if (S_ISDIR(inode->i_mode)) { - if (!cifs_sb->ctx->acdirmax) - return true; - if (!time_in_range(jiffies, cifs_i->time, - cifs_i->time + cifs_sb->ctx->acdirmax)) - return true; - } else { /* file */ - if (!cifs_sb->ctx->acregmax) - return true; - if (!time_in_range(jiffies, cifs_i->time, - cifs_i->time + cifs_sb->ctx->acregmax)) - return true; - } - - /* hardlinked files w/ noserverino get "special" treatment */ - if (!(cifs_sb->mnt_cifs_flags & CIFS_MOUNT_SERVER_INUM) && - S_ISREG(inode->i_mode) && inode->i_nlink != 1) - return true; - - return false; -} - -/* - * Zap the cache. Called when invalid_mapping flag is set. - */ -int -cifs_invalidate_mapping(struct inode *inode) -{ - int rc = 0; - - if (inode->i_mapping && inode->i_mapping->nrpages != 0) { - rc = invalidate_inode_pages2(inode->i_mapping); - if (rc) - cifs_dbg(VFS, "%s: Could not invalidate inode %p\n", - __func__, inode); - } - - return rc; -} - -/** - * cifs_wait_bit_killable - helper for functions that are sleeping on bit locks - * - * @key: currently unused - * @mode: the task state to sleep in - */ -static int -cifs_wait_bit_killable(struct wait_bit_key *key, int mode) -{ - schedule(); - if (signal_pending_state(mode, current)) - return -ERESTARTSYS; - return 0; -} - -int -cifs_revalidate_mapping(struct inode *inode) -{ - int rc; - unsigned long *flags = &CIFS_I(inode)->flags; - struct cifs_sb_info *cifs_sb = CIFS_SB(inode->i_sb); - - /* swapfiles are not supposed to be shared */ - if (IS_SWAPFILE(inode)) - return 0; - - rc = wait_on_bit_lock_action(flags, CIFS_INO_LOCK, cifs_wait_bit_killable, - TASK_KILLABLE|TASK_FREEZABLE_UNSAFE); - if (rc) - return rc; - - if (test_and_clear_bit(CIFS_INO_INVALID_MAPPING, flags)) { - /* for cache=singleclient, do not invalidate */ - if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_RW_CACHE) - goto skip_invalidate; - - rc = cifs_invalidate_mapping(inode); - if (rc) - set_bit(CIFS_INO_INVALID_MAPPING, flags); - } - -skip_invalidate: - clear_bit_unlock(CIFS_INO_LOCK, flags); - smp_mb__after_atomic(); - wake_up_bit(flags, CIFS_INO_LOCK); - - return rc; -} - -int -cifs_zap_mapping(struct inode *inode) -{ - set_bit(CIFS_INO_INVALID_MAPPING, &CIFS_I(inode)->flags); - return cifs_revalidate_mapping(inode); -} - -int cifs_revalidate_file_attr(struct file *filp) -{ - int rc = 0; - struct dentry *dentry = file_dentry(filp); -#ifdef CONFIG_CIFS_ALLOW_INSECURE_LEGACY - struct cifsFileInfo *cfile = (struct cifsFileInfo *) filp->private_data; -#endif /* CONFIG_CIFS_ALLOW_INSECURE_LEGACY */ - - if (!cifs_dentry_needs_reval(dentry)) - return rc; - -#ifdef CONFIG_CIFS_ALLOW_INSECURE_LEGACY - if (tlink_tcon(cfile->tlink)->unix_ext) - rc = cifs_get_file_info_unix(filp); - else -#endif /* CONFIG_CIFS_ALLOW_INSECURE_LEGACY */ - rc = cifs_get_file_info(filp); - - return rc; -} - -int cifs_revalidate_dentry_attr(struct dentry *dentry) -{ - unsigned int xid; - int rc = 0; - struct inode *inode = d_inode(dentry); - struct super_block *sb = dentry->d_sb; - const char *full_path; - void *page; - int count = 0; - - if (inode == NULL) - return -ENOENT; - - if (!cifs_dentry_needs_reval(dentry)) - return rc; - - xid = get_xid(); - - page = alloc_dentry_path(); - full_path = build_path_from_dentry(dentry, page); - if (IS_ERR(full_path)) { - rc = PTR_ERR(full_path); - goto out; - } - - cifs_dbg(FYI, "Update attributes: %s inode 0x%p count %d dentry: 0x%p d_time %ld jiffies %ld\n", - full_path, inode, inode->i_count.counter, - dentry, cifs_get_time(dentry), jiffies); - -again: - if (cifs_sb_master_tcon(CIFS_SB(sb))->posix_extensions) - rc = smb311_posix_get_inode_info(&inode, full_path, sb, xid); - else if (cifs_sb_master_tcon(CIFS_SB(sb))->unix_ext) - rc = cifs_get_inode_info_unix(&inode, full_path, sb, xid); - else - rc = cifs_get_inode_info(&inode, full_path, NULL, sb, - xid, NULL); - if (rc == -EAGAIN && count++ < 10) - goto again; -out: - free_dentry_path(page); - free_xid(xid); - - return rc; -} - -int cifs_revalidate_file(struct file *filp) -{ - int rc; - struct inode *inode = file_inode(filp); - - rc = cifs_revalidate_file_attr(filp); - if (rc) - return rc; - - return cifs_revalidate_mapping(inode); -} - -/* revalidate a dentry's inode attributes */ -int cifs_revalidate_dentry(struct dentry *dentry) -{ - int rc; - struct inode *inode = d_inode(dentry); - - rc = cifs_revalidate_dentry_attr(dentry); - if (rc) - return rc; - - return cifs_revalidate_mapping(inode); -} - -int cifs_getattr(struct user_namespace *mnt_userns, const struct path *path, - struct kstat *stat, u32 request_mask, unsigned int flags) -{ - struct dentry *dentry = path->dentry; - struct cifs_sb_info *cifs_sb = CIFS_SB(dentry->d_sb); - struct cifs_tcon *tcon = cifs_sb_master_tcon(cifs_sb); - struct inode *inode = d_inode(dentry); - int rc; - - if (unlikely(cifs_forced_shutdown(CIFS_SB(inode->i_sb)))) - return -EIO; - - /* - * We need to be sure that all dirty pages are written and the server - * has actual ctime, mtime and file length. - */ - if ((request_mask & (STATX_CTIME | STATX_MTIME | STATX_SIZE | STATX_BLOCKS)) && - !CIFS_CACHE_READ(CIFS_I(inode)) && - inode->i_mapping && inode->i_mapping->nrpages != 0) { - rc = filemap_fdatawait(inode->i_mapping); - if (rc) { - mapping_set_error(inode->i_mapping, rc); - return rc; - } - } - - if ((flags & AT_STATX_SYNC_TYPE) == AT_STATX_FORCE_SYNC) - CIFS_I(inode)->time = 0; /* force revalidate */ - - /* - * If the caller doesn't require syncing, only sync if - * necessary (e.g. due to earlier truncate or setattr - * invalidating the cached metadata) - */ - if (((flags & AT_STATX_SYNC_TYPE) != AT_STATX_DONT_SYNC) || - (CIFS_I(inode)->time == 0)) { - rc = cifs_revalidate_dentry_attr(dentry); - if (rc) - return rc; - } - - generic_fillattr(&init_user_ns, inode, stat); - stat->blksize = cifs_sb->ctx->bsize; - stat->ino = CIFS_I(inode)->uniqueid; - - /* old CIFS Unix Extensions doesn't return create time */ - if (CIFS_I(inode)->createtime) { - stat->result_mask |= STATX_BTIME; - stat->btime = - cifs_NTtimeToUnix(cpu_to_le64(CIFS_I(inode)->createtime)); - } - - stat->attributes_mask |= (STATX_ATTR_COMPRESSED | STATX_ATTR_ENCRYPTED); - if (CIFS_I(inode)->cifsAttrs & FILE_ATTRIBUTE_COMPRESSED) - stat->attributes |= STATX_ATTR_COMPRESSED; - if (CIFS_I(inode)->cifsAttrs & FILE_ATTRIBUTE_ENCRYPTED) - stat->attributes |= STATX_ATTR_ENCRYPTED; - - /* - * If on a multiuser mount without unix extensions or cifsacl being - * enabled, and the admin hasn't overridden them, set the ownership - * to the fsuid/fsgid of the current process. - */ - if ((cifs_sb->mnt_cifs_flags & CIFS_MOUNT_MULTIUSER) && - !(cifs_sb->mnt_cifs_flags & CIFS_MOUNT_CIFS_ACL) && - !tcon->unix_ext) { - if (!(cifs_sb->mnt_cifs_flags & CIFS_MOUNT_OVERR_UID)) - stat->uid = current_fsuid(); - if (!(cifs_sb->mnt_cifs_flags & CIFS_MOUNT_OVERR_GID)) - stat->gid = current_fsgid(); - } - return 0; -} - -int cifs_fiemap(struct inode *inode, struct fiemap_extent_info *fei, u64 start, - u64 len) -{ - struct cifsInodeInfo *cifs_i = CIFS_I(inode); - struct cifs_sb_info *cifs_sb = CIFS_SB(cifs_i->netfs.inode.i_sb); - struct cifs_tcon *tcon = cifs_sb_master_tcon(cifs_sb); - struct TCP_Server_Info *server = tcon->ses->server; - struct cifsFileInfo *cfile; - int rc; - - if (unlikely(cifs_forced_shutdown(cifs_sb))) - return -EIO; - - /* - * We need to be sure that all dirty pages are written as they - * might fill holes on the server. - */ - if (!CIFS_CACHE_READ(CIFS_I(inode)) && inode->i_mapping && - inode->i_mapping->nrpages != 0) { - rc = filemap_fdatawait(inode->i_mapping); - if (rc) { - mapping_set_error(inode->i_mapping, rc); - return rc; - } - } - - cfile = find_readable_file(cifs_i, false); - if (cfile == NULL) - return -EINVAL; - - if (server->ops->fiemap) { - rc = server->ops->fiemap(tcon, cfile, fei, start, len); - cifsFileInfo_put(cfile); - return rc; - } - - cifsFileInfo_put(cfile); - return -ENOTSUPP; -} - -int cifs_truncate_page(struct address_space *mapping, loff_t from) -{ - pgoff_t index = from >> PAGE_SHIFT; - unsigned offset = from & (PAGE_SIZE - 1); - struct page *page; - int rc = 0; - - page = grab_cache_page(mapping, index); - if (!page) - return -ENOMEM; - - zero_user_segment(page, offset, PAGE_SIZE); - unlock_page(page); - put_page(page); - return rc; -} - -void cifs_setsize(struct inode *inode, loff_t offset) -{ - struct cifsInodeInfo *cifs_i = CIFS_I(inode); - - spin_lock(&inode->i_lock); - i_size_write(inode, offset); - spin_unlock(&inode->i_lock); - - /* Cached inode must be refreshed on truncate */ - cifs_i->time = 0; - truncate_pagecache(inode, offset); -} - -static int -cifs_set_file_size(struct inode *inode, struct iattr *attrs, - unsigned int xid, const char *full_path) -{ - int rc; - struct cifsFileInfo *open_file; - struct cifsInodeInfo *cifsInode = CIFS_I(inode); - struct cifs_sb_info *cifs_sb = CIFS_SB(inode->i_sb); - struct tcon_link *tlink = NULL; - struct cifs_tcon *tcon = NULL; - struct TCP_Server_Info *server; - - /* - * 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. - * This keeps our caching token (oplock) and avoids timeouts - * when the local oplock break takes longer to flush - * writebehind data than the SMB timeout for the SetPathInfo - * request would allow - */ - open_file = find_writable_file(cifsInode, FIND_WR_FSUID_ONLY); - if (open_file) { - tcon = tlink_tcon(open_file->tlink); - server = tcon->ses->server; - if (server->ops->set_file_size) - rc = server->ops->set_file_size(xid, tcon, open_file, - attrs->ia_size, false); - else - rc = -ENOSYS; - cifsFileInfo_put(open_file); - cifs_dbg(FYI, "SetFSize for attrs rc = %d\n", rc); - } else - rc = -EINVAL; - - if (!rc) - goto set_size_out; - - if (tcon == NULL) { - tlink = cifs_sb_tlink(cifs_sb); - if (IS_ERR(tlink)) - return PTR_ERR(tlink); - tcon = tlink_tcon(tlink); - server = tcon->ses->server; - } - - /* - * Set file size by pathname rather than by handle either because no - * valid, writeable file handle for it was found or because there was - * an error setting it by handle. - */ - if (server->ops->set_path_size) - rc = server->ops->set_path_size(xid, tcon, full_path, - attrs->ia_size, cifs_sb, false); - else - rc = -ENOSYS; - cifs_dbg(FYI, "SetEOF by path (setattrs) rc = %d\n", rc); - - if (tlink) - cifs_put_tlink(tlink); - -set_size_out: - if (rc == 0) { - cifsInode->server_eof = attrs->ia_size; - cifs_setsize(inode, attrs->ia_size); - /* - * i_blocks is not related to (i_size / i_blksize), but instead - * 512 byte (2**9) size is required for calculating num blocks. - * Until we can query the server for actual allocation size, - * this is best estimate we have for blocks allocated for a file - * Number of blocks must be rounded up so size 1 is not 0 blocks - */ - inode->i_blocks = (512 - 1 + attrs->ia_size) >> 9; - - /* - * The man page of truncate says if the size changed, - * then the st_ctime and st_mtime fields for the file - * are updated. - */ - attrs->ia_ctime = attrs->ia_mtime = current_time(inode); - attrs->ia_valid |= ATTR_CTIME | ATTR_MTIME; - - cifs_truncate_page(inode->i_mapping, inode->i_size); - } - - return rc; -} - -#ifdef CONFIG_CIFS_ALLOW_INSECURE_LEGACY -static int -cifs_setattr_unix(struct dentry *direntry, struct iattr *attrs) -{ - int rc; - unsigned int xid; - const char *full_path; - void *page = alloc_dentry_path(); - struct inode *inode = d_inode(direntry); - struct cifsInodeInfo *cifsInode = CIFS_I(inode); - struct cifs_sb_info *cifs_sb = CIFS_SB(inode->i_sb); - struct tcon_link *tlink; - struct cifs_tcon *pTcon; - struct cifs_unix_set_info_args *args = NULL; - struct cifsFileInfo *open_file; - - cifs_dbg(FYI, "setattr_unix on file %pd attrs->ia_valid=0x%x\n", - direntry, attrs->ia_valid); - - xid = get_xid(); - - if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_NO_PERM) - attrs->ia_valid |= ATTR_FORCE; - - rc = setattr_prepare(&init_user_ns, direntry, attrs); - if (rc < 0) - goto out; - - full_path = build_path_from_dentry(direntry, page); - if (IS_ERR(full_path)) { - rc = PTR_ERR(full_path); - goto out; - } - - /* - * Attempt to flush data before changing attributes. We need to do - * this for ATTR_SIZE and ATTR_MTIME for sure, and if we change the - * ownership or mode then we may also need to do this. Here, we take - * the safe way out and just do the flush on all setattr requests. If - * the flush returns error, store it to report later and continue. - * - * BB: This should be smarter. Why bother flushing pages that - * will be truncated anyway? Also, should we error out here if - * the flush returns error? - */ - rc = filemap_write_and_wait(inode->i_mapping); - if (is_interrupt_error(rc)) { - rc = -ERESTARTSYS; - goto out; - } - - mapping_set_error(inode->i_mapping, rc); - rc = 0; - - if (attrs->ia_valid & ATTR_SIZE) { - rc = cifs_set_file_size(inode, attrs, xid, full_path); - if (rc != 0) - goto out; - } - - /* skip mode change if it's just for clearing setuid/setgid */ - if (attrs->ia_valid & (ATTR_KILL_SUID|ATTR_KILL_SGID)) - attrs->ia_valid &= ~ATTR_MODE; - - args = kmalloc(sizeof(*args), GFP_KERNEL); - if (args == NULL) { - rc = -ENOMEM; - goto out; - } - - /* set up the struct */ - if (attrs->ia_valid & ATTR_MODE) - args->mode = attrs->ia_mode; - else - args->mode = NO_CHANGE_64; - - if (attrs->ia_valid & ATTR_UID) - args->uid = attrs->ia_uid; - else - args->uid = INVALID_UID; /* no change */ - - if (attrs->ia_valid & ATTR_GID) - args->gid = attrs->ia_gid; - else - args->gid = INVALID_GID; /* no change */ - - if (attrs->ia_valid & ATTR_ATIME) - args->atime = cifs_UnixTimeToNT(attrs->ia_atime); - else - args->atime = NO_CHANGE_64; - - if (attrs->ia_valid & ATTR_MTIME) - args->mtime = cifs_UnixTimeToNT(attrs->ia_mtime); - else - args->mtime = NO_CHANGE_64; - - if (attrs->ia_valid & ATTR_CTIME) - args->ctime = cifs_UnixTimeToNT(attrs->ia_ctime); - else - args->ctime = NO_CHANGE_64; - - args->device = 0; - open_file = find_writable_file(cifsInode, FIND_WR_FSUID_ONLY); - if (open_file) { - u16 nfid = open_file->fid.netfid; - u32 npid = open_file->pid; - pTcon = tlink_tcon(open_file->tlink); - rc = CIFSSMBUnixSetFileInfo(xid, pTcon, args, nfid, npid); - cifsFileInfo_put(open_file); - } else { - tlink = cifs_sb_tlink(cifs_sb); - if (IS_ERR(tlink)) { - rc = PTR_ERR(tlink); - goto out; - } - pTcon = tlink_tcon(tlink); - rc = CIFSSMBUnixSetPathInfo(xid, pTcon, full_path, args, - cifs_sb->local_nls, - cifs_remap(cifs_sb)); - cifs_put_tlink(tlink); - } - - if (rc) - goto out; - - if ((attrs->ia_valid & ATTR_SIZE) && - attrs->ia_size != i_size_read(inode)) { - truncate_setsize(inode, attrs->ia_size); - fscache_resize_cookie(cifs_inode_cookie(inode), attrs->ia_size); - } - - setattr_copy(&init_user_ns, inode, attrs); - mark_inode_dirty(inode); - - /* force revalidate when any of these times are set since some - of the fs types (eg ext3, fat) do not have fine enough - time granularity to match protocol, and we do not have a - a way (yet) to query the server fs's time granularity (and - whether it rounds times down). - */ - if (attrs->ia_valid & (ATTR_MTIME | ATTR_CTIME)) - cifsInode->time = 0; -out: - kfree(args); - free_dentry_path(page); - free_xid(xid); - return rc; -} -#endif /* CONFIG_CIFS_ALLOW_INSECURE_LEGACY */ - -static int -cifs_setattr_nounix(struct dentry *direntry, struct iattr *attrs) -{ - unsigned int xid; - kuid_t uid = INVALID_UID; - kgid_t gid = INVALID_GID; - struct inode *inode = d_inode(direntry); - struct cifs_sb_info *cifs_sb = CIFS_SB(inode->i_sb); - struct cifsInodeInfo *cifsInode = CIFS_I(inode); - struct cifsFileInfo *wfile; - struct cifs_tcon *tcon; - const char *full_path; - void *page = alloc_dentry_path(); - int rc = -EACCES; - __u32 dosattr = 0; - __u64 mode = NO_CHANGE_64; - - xid = get_xid(); - - cifs_dbg(FYI, "setattr on file %pd attrs->ia_valid 0x%x\n", - direntry, attrs->ia_valid); - - if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_NO_PERM) - attrs->ia_valid |= ATTR_FORCE; - - rc = setattr_prepare(&init_user_ns, direntry, attrs); - if (rc < 0) - goto cifs_setattr_exit; - - full_path = build_path_from_dentry(direntry, page); - if (IS_ERR(full_path)) { - rc = PTR_ERR(full_path); - goto cifs_setattr_exit; - } - - /* - * Attempt to flush data before changing attributes. We need to do - * this for ATTR_SIZE and ATTR_MTIME. If the flush of the data - * returns error, store it to report later and continue. - * - * BB: This should be smarter. Why bother flushing pages that - * will be truncated anyway? Also, should we error out here if - * the flush returns error? Do we need to check for ATTR_MTIME_SET flag? - */ - if (attrs->ia_valid & (ATTR_MTIME | ATTR_SIZE | ATTR_CTIME)) { - rc = filemap_write_and_wait(inode->i_mapping); - if (is_interrupt_error(rc)) { - rc = -ERESTARTSYS; - goto cifs_setattr_exit; - } - mapping_set_error(inode->i_mapping, rc); - } - - rc = 0; - - if ((attrs->ia_valid & ATTR_MTIME) && - !(cifs_sb->mnt_cifs_flags & CIFS_MOUNT_NOSSYNC)) { - rc = cifs_get_writable_file(cifsInode, FIND_WR_ANY, &wfile); - if (!rc) { - tcon = tlink_tcon(wfile->tlink); - rc = tcon->ses->server->ops->flush(xid, tcon, &wfile->fid); - cifsFileInfo_put(wfile); - if (rc) - goto cifs_setattr_exit; - } else if (rc != -EBADF) - goto cifs_setattr_exit; - else - rc = 0; - } - - if (attrs->ia_valid & ATTR_SIZE) { - rc = cifs_set_file_size(inode, attrs, xid, full_path); - if (rc != 0) - goto cifs_setattr_exit; - } - - if (attrs->ia_valid & ATTR_UID) - uid = attrs->ia_uid; - - if (attrs->ia_valid & ATTR_GID) - gid = attrs->ia_gid; - - if ((cifs_sb->mnt_cifs_flags & CIFS_MOUNT_CIFS_ACL) || - (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_MODE_FROM_SID)) { - if (uid_valid(uid) || gid_valid(gid)) { - mode = NO_CHANGE_64; - rc = id_mode_to_cifs_acl(inode, full_path, &mode, - uid, gid); - if (rc) { - cifs_dbg(FYI, "%s: Setting id failed with error: %d\n", - __func__, rc); - goto cifs_setattr_exit; - } - } - } else - if (!(cifs_sb->mnt_cifs_flags & CIFS_MOUNT_SET_UID)) - attrs->ia_valid &= ~(ATTR_UID | ATTR_GID); - - /* skip mode change if it's just for clearing setuid/setgid */ - if (attrs->ia_valid & (ATTR_KILL_SUID|ATTR_KILL_SGID)) - attrs->ia_valid &= ~ATTR_MODE; - - if (attrs->ia_valid & ATTR_MODE) { - mode = attrs->ia_mode; - rc = 0; - if ((cifs_sb->mnt_cifs_flags & CIFS_MOUNT_CIFS_ACL) || - (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_MODE_FROM_SID)) { - rc = id_mode_to_cifs_acl(inode, full_path, &mode, - INVALID_UID, INVALID_GID); - if (rc) { - cifs_dbg(FYI, "%s: Setting ACL failed with error: %d\n", - __func__, rc); - goto cifs_setattr_exit; - } - - /* - * In case of CIFS_MOUNT_CIFS_ACL, we cannot support all modes. - * Pick up the actual mode bits that were set. - */ - if (mode != attrs->ia_mode) - attrs->ia_mode = mode; - } else - if (((mode & S_IWUGO) == 0) && - (cifsInode->cifsAttrs & ATTR_READONLY) == 0) { - - dosattr = cifsInode->cifsAttrs | ATTR_READONLY; - - /* fix up mode if we're not using dynperm */ - if ((cifs_sb->mnt_cifs_flags & CIFS_MOUNT_DYNPERM) == 0) - attrs->ia_mode = inode->i_mode & ~S_IWUGO; - } else if ((mode & S_IWUGO) && - (cifsInode->cifsAttrs & ATTR_READONLY)) { - - dosattr = cifsInode->cifsAttrs & ~ATTR_READONLY; - /* Attributes of 0 are ignored */ - if (dosattr == 0) - dosattr |= ATTR_NORMAL; - - /* reset local inode permissions to normal */ - if (!(cifs_sb->mnt_cifs_flags & CIFS_MOUNT_DYNPERM)) { - attrs->ia_mode &= ~(S_IALLUGO); - if (S_ISDIR(inode->i_mode)) - attrs->ia_mode |= - cifs_sb->ctx->dir_mode; - else - attrs->ia_mode |= - cifs_sb->ctx->file_mode; - } - } else if (!(cifs_sb->mnt_cifs_flags & CIFS_MOUNT_DYNPERM)) { - /* ignore mode change - ATTR_READONLY hasn't changed */ - attrs->ia_valid &= ~ATTR_MODE; - } - } - - if (attrs->ia_valid & (ATTR_MTIME|ATTR_ATIME|ATTR_CTIME) || - ((attrs->ia_valid & ATTR_MODE) && dosattr)) { - rc = cifs_set_file_info(inode, attrs, xid, full_path, dosattr); - /* BB: check for rc = -EOPNOTSUPP and switch to legacy mode */ - - /* 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) - goto cifs_setattr_exit; - - if ((attrs->ia_valid & ATTR_SIZE) && - attrs->ia_size != i_size_read(inode)) { - truncate_setsize(inode, attrs->ia_size); - fscache_resize_cookie(cifs_inode_cookie(inode), attrs->ia_size); - } - - setattr_copy(&init_user_ns, inode, attrs); - mark_inode_dirty(inode); - -cifs_setattr_exit: - free_xid(xid); - free_dentry_path(page); - return rc; -} - -int -cifs_setattr(struct user_namespace *mnt_userns, struct dentry *direntry, - struct iattr *attrs) -{ - struct cifs_sb_info *cifs_sb = CIFS_SB(direntry->d_sb); - int rc, retries = 0; -#ifdef CONFIG_CIFS_ALLOW_INSECURE_LEGACY - struct cifs_tcon *pTcon = cifs_sb_master_tcon(cifs_sb); -#endif /* CONFIG_CIFS_ALLOW_INSECURE_LEGACY */ - - if (unlikely(cifs_forced_shutdown(cifs_sb))) - return -EIO; - - do { -#ifdef CONFIG_CIFS_ALLOW_INSECURE_LEGACY - if (pTcon->unix_ext) - rc = cifs_setattr_unix(direntry, attrs); - else -#endif /* CONFIG_CIFS_ALLOW_INSECURE_LEGACY */ - rc = cifs_setattr_nounix(direntry, attrs); - retries++; - } while (is_retryable_error(rc) && retries < 2); - - /* BB: add cifs_setattr_legacy for really old servers */ - return rc; -} diff --git a/fs/cifs/ioctl.c b/fs/cifs/ioctl.c deleted file mode 100644 index 6419ec47c2a85..0000000000000 --- a/fs/cifs/ioctl.c +++ /dev/null @@ -1,526 +0,0 @@ -// SPDX-License-Identifier: LGPL-2.1 -/* - * - * vfs operations that deal with io control - * - * Copyright (C) International Business Machines Corp., 2005,2013 - * Author(s): Steve French (sfrench@us.ibm.com) - * - */ - -#include -#include -#include -#include -#include -#include "cifspdu.h" -#include "cifsglob.h" -#include "cifsproto.h" -#include "cifs_debug.h" -#include "cifsfs.h" -#include "cifs_ioctl.h" -#include "smb2proto.h" -#include "smb2glob.h" -#include - -static long cifs_ioctl_query_info(unsigned int xid, struct file *filep, - unsigned long p) -{ - struct inode *inode = file_inode(filep); - struct cifs_sb_info *cifs_sb = CIFS_SB(inode->i_sb); - struct cifs_tcon *tcon = cifs_sb_master_tcon(cifs_sb); - struct dentry *dentry = filep->f_path.dentry; - const unsigned char *path; - void *page = alloc_dentry_path(); - __le16 *utf16_path = NULL, root_path; - int rc = 0; - - path = build_path_from_dentry(dentry, page); - if (IS_ERR(path)) { - free_dentry_path(page); - return PTR_ERR(path); - } - - cifs_dbg(FYI, "%s %s\n", __func__, path); - - if (!path[0]) { - root_path = 0; - utf16_path = &root_path; - } else { - utf16_path = cifs_convert_path_to_utf16(path + 1, cifs_sb); - if (!utf16_path) { - rc = -ENOMEM; - goto ici_exit; - } - } - - if (tcon->ses->server->ops->ioctl_query_info) - rc = tcon->ses->server->ops->ioctl_query_info( - xid, tcon, cifs_sb, utf16_path, - filep->private_data ? 0 : 1, p); - else - rc = -EOPNOTSUPP; - - ici_exit: - if (utf16_path != &root_path) - kfree(utf16_path); - free_dentry_path(page); - return rc; -} - -static long cifs_ioctl_copychunk(unsigned int xid, struct file *dst_file, - unsigned long srcfd) -{ - int rc; - struct fd src_file; - struct inode *src_inode; - - cifs_dbg(FYI, "ioctl copychunk range\n"); - /* the destination must be opened for writing */ - if (!(dst_file->f_mode & FMODE_WRITE)) { - cifs_dbg(FYI, "file target not open for write\n"); - return -EINVAL; - } - - /* check if target volume is readonly and take reference */ - rc = mnt_want_write_file(dst_file); - if (rc) { - cifs_dbg(FYI, "mnt_want_write failed with rc %d\n", rc); - return rc; - } - - src_file = fdget(srcfd); - if (!src_file.file) { - rc = -EBADF; - goto out_drop_write; - } - - if (src_file.file->f_op->unlocked_ioctl != cifs_ioctl) { - rc = -EBADF; - cifs_dbg(VFS, "src file seems to be from a different filesystem type\n"); - goto out_fput; - } - - src_inode = file_inode(src_file.file); - rc = -EINVAL; - if (S_ISDIR(src_inode->i_mode)) - goto out_fput; - - rc = cifs_file_copychunk_range(xid, src_file.file, 0, dst_file, 0, - src_inode->i_size, 0); - if (rc > 0) - rc = 0; -out_fput: - fdput(src_file); -out_drop_write: - mnt_drop_write_file(dst_file); - return rc; -} - -static long smb_mnt_get_fsinfo(unsigned int xid, struct cifs_tcon *tcon, - void __user *arg) -{ - int rc = 0; - struct smb_mnt_fs_info *fsinf; - - fsinf = kzalloc(sizeof(struct smb_mnt_fs_info), GFP_KERNEL); - if (fsinf == NULL) - return -ENOMEM; - - fsinf->version = 1; - fsinf->protocol_id = tcon->ses->server->vals->protocol_id; - fsinf->device_characteristics = - le32_to_cpu(tcon->fsDevInfo.DeviceCharacteristics); - fsinf->device_type = le32_to_cpu(tcon->fsDevInfo.DeviceType); - fsinf->fs_attributes = le32_to_cpu(tcon->fsAttrInfo.Attributes); - fsinf->max_path_component = - le32_to_cpu(tcon->fsAttrInfo.MaxPathNameComponentLength); - fsinf->vol_serial_number = tcon->vol_serial_number; - fsinf->vol_create_time = le64_to_cpu(tcon->vol_create_time); - fsinf->share_flags = tcon->share_flags; - fsinf->share_caps = le32_to_cpu(tcon->capabilities); - fsinf->sector_flags = tcon->ss_flags; - fsinf->optimal_sector_size = tcon->perf_sector_size; - fsinf->max_bytes_chunk = tcon->max_bytes_chunk; - fsinf->maximal_access = tcon->maximal_access; - fsinf->cifs_posix_caps = le64_to_cpu(tcon->fsUnixInfo.Capability); - - if (copy_to_user(arg, fsinf, sizeof(struct smb_mnt_fs_info))) - rc = -EFAULT; - - kfree(fsinf); - return rc; -} - -static int cifs_shutdown(struct super_block *sb, unsigned long arg) -{ - struct cifs_sb_info *sbi = CIFS_SB(sb); - __u32 flags; - - if (!capable(CAP_SYS_ADMIN)) - return -EPERM; - - if (get_user(flags, (__u32 __user *)arg)) - return -EFAULT; - - if (flags > CIFS_GOING_FLAGS_NOLOGFLUSH) - return -EINVAL; - - if (cifs_forced_shutdown(sbi)) - return 0; - - cifs_dbg(VFS, "shut down requested (%d)", flags); -/* trace_cifs_shutdown(sb, flags);*/ - - /* - * see: - * https://man7.org/linux/man-pages/man2/ioctl_xfs_goingdown.2.html - * for more information and description of original intent of the flags - */ - switch (flags) { - /* - * We could add support later for default flag which requires: - * "Flush all dirty data and metadata to disk" - * would need to call syncfs or equivalent to flush page cache for - * the mount and then issue fsync to server (if nostrictsync not set) - */ - case CIFS_GOING_FLAGS_DEFAULT: - cifs_dbg(FYI, "shutdown with default flag not supported\n"); - return -EINVAL; - /* - * FLAGS_LOGFLUSH is easy since it asks to write out metadata (not - * data) but metadata writes are not cached on the client, so can treat - * it similarly to NOLOGFLUSH - */ - case CIFS_GOING_FLAGS_LOGFLUSH: - case CIFS_GOING_FLAGS_NOLOGFLUSH: - sbi->mnt_cifs_flags |= CIFS_MOUNT_SHUTDOWN; - return 0; - default: - return -EINVAL; - } - return 0; -} - -static int cifs_dump_full_key(struct cifs_tcon *tcon, struct smb3_full_key_debug_info __user *in) -{ - struct smb3_full_key_debug_info out; - struct cifs_ses *ses; - int rc = 0; - bool found = false; - u8 __user *end; - - if (!smb3_encryption_required(tcon)) { - rc = -EOPNOTSUPP; - goto out; - } - - /* copy user input into our output buffer */ - if (copy_from_user(&out, in, sizeof(out))) { - rc = -EINVAL; - goto out; - } - - if (!out.session_id) { - /* if ses id is 0, use current user session */ - ses = tcon->ses; - } else { - /* otherwise if a session id is given, look for it in all our sessions */ - struct cifs_ses *ses_it = NULL; - struct TCP_Server_Info *server_it = NULL; - - spin_lock(&cifs_tcp_ses_lock); - list_for_each_entry(server_it, &cifs_tcp_ses_list, tcp_ses_list) { - list_for_each_entry(ses_it, &server_it->smb_ses_list, smb_ses_list) { - if (ses_it->Suid == out.session_id) { - ses = ses_it; - /* - * since we are using the session outside the crit - * section, we need to make sure it won't be released - * so increment its refcount - */ - ses->ses_count++; - found = true; - goto search_end; - } - } - } -search_end: - spin_unlock(&cifs_tcp_ses_lock); - if (!found) { - rc = -ENOENT; - goto out; - } - } - - switch (ses->server->cipher_type) { - case SMB2_ENCRYPTION_AES128_CCM: - case SMB2_ENCRYPTION_AES128_GCM: - out.session_key_length = CIFS_SESS_KEY_SIZE; - out.server_in_key_length = out.server_out_key_length = SMB3_GCM128_CRYPTKEY_SIZE; - break; - case SMB2_ENCRYPTION_AES256_CCM: - case SMB2_ENCRYPTION_AES256_GCM: - out.session_key_length = CIFS_SESS_KEY_SIZE; - out.server_in_key_length = out.server_out_key_length = SMB3_GCM256_CRYPTKEY_SIZE; - break; - default: - rc = -EOPNOTSUPP; - goto out; - } - - /* check if user buffer is big enough to store all the keys */ - if (out.in_size < sizeof(out) + out.session_key_length + out.server_in_key_length - + out.server_out_key_length) { - rc = -ENOBUFS; - goto out; - } - - out.session_id = ses->Suid; - out.cipher_type = le16_to_cpu(ses->server->cipher_type); - - /* overwrite user input with our output */ - if (copy_to_user(in, &out, sizeof(out))) { - rc = -EINVAL; - goto out; - } - - /* append all the keys at the end of the user buffer */ - end = in->data; - if (copy_to_user(end, ses->auth_key.response, out.session_key_length)) { - rc = -EINVAL; - goto out; - } - end += out.session_key_length; - - if (copy_to_user(end, ses->smb3encryptionkey, out.server_in_key_length)) { - rc = -EINVAL; - goto out; - } - end += out.server_in_key_length; - - if (copy_to_user(end, ses->smb3decryptionkey, out.server_out_key_length)) { - rc = -EINVAL; - goto out; - } - -out: - if (found) - cifs_put_smb_ses(ses); - return rc; -} - -long cifs_ioctl(struct file *filep, unsigned int command, unsigned long arg) -{ - struct inode *inode = file_inode(filep); - struct smb3_key_debug_info pkey_inf; - int rc = -ENOTTY; /* strange error - but the precedent */ - unsigned int xid; - struct cifsFileInfo *pSMBFile = filep->private_data; - struct cifs_tcon *tcon; - struct tcon_link *tlink; - struct cifs_sb_info *cifs_sb; - __u64 ExtAttrBits = 0; - __u64 caps; - - xid = get_xid(); - - cifs_dbg(FYI, "cifs ioctl 0x%x\n", command); - switch (command) { - case FS_IOC_GETFLAGS: - if (pSMBFile == NULL) - break; - tcon = tlink_tcon(pSMBFile->tlink); - caps = le64_to_cpu(tcon->fsUnixInfo.Capability); -#ifdef CONFIG_CIFS_POSIX -#ifdef CONFIG_CIFS_ALLOW_INSECURE_LEGACY - if (CIFS_UNIX_EXTATTR_CAP & caps) { - __u64 ExtAttrMask = 0; - rc = CIFSGetExtAttr(xid, tcon, - pSMBFile->fid.netfid, - &ExtAttrBits, &ExtAttrMask); - if (rc == 0) - rc = put_user(ExtAttrBits & - FS_FL_USER_VISIBLE, - (int __user *)arg); - if (rc != -EOPNOTSUPP) - break; - } -#endif /* CONFIG_CIFS_ALLOW_INSECURE_LEGACY */ -#endif /* CONFIG_CIFS_POSIX */ - rc = 0; - if (CIFS_I(inode)->cifsAttrs & ATTR_COMPRESSED) { - /* add in the compressed bit */ - ExtAttrBits = FS_COMPR_FL; - rc = put_user(ExtAttrBits & FS_FL_USER_VISIBLE, - (int __user *)arg); - } - break; - case FS_IOC_SETFLAGS: - if (pSMBFile == NULL) - break; - tcon = tlink_tcon(pSMBFile->tlink); - /* caps = le64_to_cpu(tcon->fsUnixInfo.Capability); */ - - if (get_user(ExtAttrBits, (int __user *)arg)) { - rc = -EFAULT; - break; - } - - /* - * if (CIFS_UNIX_EXTATTR_CAP & caps) - * rc = CIFSSetExtAttr(xid, tcon, - * pSMBFile->fid.netfid, - * extAttrBits, - * &ExtAttrMask); - * if (rc != -EOPNOTSUPP) - * break; - */ - - /* Currently only flag we can set is compressed flag */ - if ((ExtAttrBits & FS_COMPR_FL) == 0) - break; - - /* Try to set compress flag */ - if (tcon->ses->server->ops->set_compression) { - rc = tcon->ses->server->ops->set_compression( - xid, tcon, pSMBFile); - cifs_dbg(FYI, "set compress flag rc %d\n", rc); - } - break; - case CIFS_IOC_COPYCHUNK_FILE: - rc = cifs_ioctl_copychunk(xid, filep, arg); - break; - case CIFS_QUERY_INFO: - rc = cifs_ioctl_query_info(xid, filep, arg); - break; - case CIFS_IOC_SET_INTEGRITY: - if (pSMBFile == NULL) - break; - tcon = tlink_tcon(pSMBFile->tlink); - if (tcon->ses->server->ops->set_integrity) - rc = tcon->ses->server->ops->set_integrity(xid, - tcon, pSMBFile); - else - rc = -EOPNOTSUPP; - break; - case CIFS_IOC_GET_MNT_INFO: - if (pSMBFile == NULL) - break; - tcon = tlink_tcon(pSMBFile->tlink); - rc = smb_mnt_get_fsinfo(xid, tcon, (void __user *)arg); - break; - case CIFS_ENUMERATE_SNAPSHOTS: - if (pSMBFile == NULL) - break; - if (arg == 0) { - rc = -EINVAL; - goto cifs_ioc_exit; - } - tcon = tlink_tcon(pSMBFile->tlink); - if (tcon->ses->server->ops->enum_snapshots) - rc = tcon->ses->server->ops->enum_snapshots(xid, tcon, - pSMBFile, (void __user *)arg); - else - rc = -EOPNOTSUPP; - break; - case CIFS_DUMP_KEY: - /* - * Dump encryption keys. This is an old ioctl that only - * handles AES-128-{CCM,GCM}. - */ - if (pSMBFile == NULL) - break; - if (!capable(CAP_SYS_ADMIN)) { - rc = -EACCES; - break; - } - - tcon = tlink_tcon(pSMBFile->tlink); - if (!smb3_encryption_required(tcon)) { - rc = -EOPNOTSUPP; - break; - } - pkey_inf.cipher_type = - le16_to_cpu(tcon->ses->server->cipher_type); - pkey_inf.Suid = tcon->ses->Suid; - memcpy(pkey_inf.auth_key, tcon->ses->auth_key.response, - 16 /* SMB2_NTLMV2_SESSKEY_SIZE */); - memcpy(pkey_inf.smb3decryptionkey, - tcon->ses->smb3decryptionkey, SMB3_SIGN_KEY_SIZE); - memcpy(pkey_inf.smb3encryptionkey, - tcon->ses->smb3encryptionkey, SMB3_SIGN_KEY_SIZE); - if (copy_to_user((void __user *)arg, &pkey_inf, - sizeof(struct smb3_key_debug_info))) - rc = -EFAULT; - else - rc = 0; - break; - case CIFS_DUMP_FULL_KEY: - /* - * Dump encryption keys (handles any key sizes) - */ - if (pSMBFile == NULL) - break; - if (!capable(CAP_SYS_ADMIN)) { - rc = -EACCES; - break; - } - tcon = tlink_tcon(pSMBFile->tlink); - rc = cifs_dump_full_key(tcon, (void __user *)arg); - break; - case CIFS_IOC_NOTIFY: - if (!S_ISDIR(inode->i_mode)) { - /* Notify can only be done on directories */ - rc = -EOPNOTSUPP; - break; - } - cifs_sb = CIFS_SB(inode->i_sb); - tlink = cifs_sb_tlink(cifs_sb); - if (IS_ERR(tlink)) { - rc = PTR_ERR(tlink); - break; - } - tcon = tlink_tcon(tlink); - if (tcon && tcon->ses->server->ops->notify) { - rc = tcon->ses->server->ops->notify(xid, - filep, (void __user *)arg, - false /* no ret data */); - cifs_dbg(FYI, "ioctl notify rc %d\n", rc); - } else - rc = -EOPNOTSUPP; - cifs_put_tlink(tlink); - break; - case CIFS_IOC_NOTIFY_INFO: - if (!S_ISDIR(inode->i_mode)) { - /* Notify can only be done on directories */ - rc = -EOPNOTSUPP; - break; - } - cifs_sb = CIFS_SB(inode->i_sb); - tlink = cifs_sb_tlink(cifs_sb); - if (IS_ERR(tlink)) { - rc = PTR_ERR(tlink); - break; - } - tcon = tlink_tcon(tlink); - if (tcon && tcon->ses->server->ops->notify) { - rc = tcon->ses->server->ops->notify(xid, - filep, (void __user *)arg, - true /* return details */); - cifs_dbg(FYI, "ioctl notify info rc %d\n", rc); - } else - rc = -EOPNOTSUPP; - cifs_put_tlink(tlink); - break; - case CIFS_IOC_SHUTDOWN: - rc = cifs_shutdown(inode->i_sb, arg); - break; - default: - cifs_dbg(FYI, "unsupported ioctl\n"); - break; - } -cifs_ioc_exit: - free_xid(xid); - return rc; -} diff --git a/fs/cifs/link.c b/fs/cifs/link.c deleted file mode 100644 index c0f101fc1e5d0..0000000000000 --- a/fs/cifs/link.c +++ /dev/null @@ -1,650 +0,0 @@ -// SPDX-License-Identifier: LGPL-2.1 -/* - * - * Copyright (C) International Business Machines Corp., 2002,2008 - * Author(s): Steve French (sfrench@us.ibm.com) - * - */ -#include -#include -#include -#include -#include "cifsfs.h" -#include "cifspdu.h" -#include "cifsglob.h" -#include "cifsproto.h" -#include "cifs_debug.h" -#include "cifs_fs_sb.h" -#include "cifs_unicode.h" -#include "smb2proto.h" -#include "cifs_ioctl.h" - -/* - * M-F Symlink Functions - Begin - */ - -#define CIFS_MF_SYMLINK_LEN_OFFSET (4+1) -#define CIFS_MF_SYMLINK_MD5_OFFSET (CIFS_MF_SYMLINK_LEN_OFFSET+(4+1)) -#define CIFS_MF_SYMLINK_LINK_OFFSET (CIFS_MF_SYMLINK_MD5_OFFSET+(32+1)) -#define CIFS_MF_SYMLINK_LINK_MAXLEN (1024) -#define CIFS_MF_SYMLINK_FILE_SIZE \ - (CIFS_MF_SYMLINK_LINK_OFFSET + CIFS_MF_SYMLINK_LINK_MAXLEN) - -#define CIFS_MF_SYMLINK_LEN_FORMAT "XSym\n%04u\n" -#define CIFS_MF_SYMLINK_MD5_FORMAT "%16phN\n" -#define CIFS_MF_SYMLINK_MD5_ARGS(md5_hash) md5_hash - -static int -symlink_hash(unsigned int link_len, const char *link_str, u8 *md5_hash) -{ - int rc; - struct shash_desc *md5 = NULL; - - rc = cifs_alloc_hash("md5", &md5); - if (rc) - goto symlink_hash_err; - - rc = crypto_shash_init(md5); - if (rc) { - cifs_dbg(VFS, "%s: Could not init md5 shash\n", __func__); - goto symlink_hash_err; - } - rc = crypto_shash_update(md5, link_str, link_len); - if (rc) { - cifs_dbg(VFS, "%s: Could not update with link_str\n", __func__); - goto symlink_hash_err; - } - rc = crypto_shash_final(md5, md5_hash); - if (rc) - cifs_dbg(VFS, "%s: Could not generate md5 hash\n", __func__); - -symlink_hash_err: - cifs_free_hash(&md5); - return rc; -} - -static int -parse_mf_symlink(const u8 *buf, unsigned int buf_len, unsigned int *_link_len, - char **_link_str) -{ - int rc; - unsigned int link_len; - const char *md5_str1; - const char *link_str; - u8 md5_hash[16]; - char md5_str2[34]; - - if (buf_len != CIFS_MF_SYMLINK_FILE_SIZE) - return -EINVAL; - - md5_str1 = (const char *)&buf[CIFS_MF_SYMLINK_MD5_OFFSET]; - link_str = (const char *)&buf[CIFS_MF_SYMLINK_LINK_OFFSET]; - - rc = sscanf(buf, CIFS_MF_SYMLINK_LEN_FORMAT, &link_len); - if (rc != 1) - return -EINVAL; - - if (link_len > CIFS_MF_SYMLINK_LINK_MAXLEN) - return -EINVAL; - - rc = symlink_hash(link_len, link_str, md5_hash); - if (rc) { - cifs_dbg(FYI, "%s: MD5 hash failure: %d\n", __func__, rc); - return rc; - } - - scnprintf(md5_str2, sizeof(md5_str2), - CIFS_MF_SYMLINK_MD5_FORMAT, - CIFS_MF_SYMLINK_MD5_ARGS(md5_hash)); - - if (strncmp(md5_str1, md5_str2, 17) != 0) - return -EINVAL; - - if (_link_str) { - *_link_str = kstrndup(link_str, link_len, GFP_KERNEL); - if (!*_link_str) - return -ENOMEM; - } - - *_link_len = link_len; - return 0; -} - -static int -format_mf_symlink(u8 *buf, unsigned int buf_len, const char *link_str) -{ - int rc; - unsigned int link_len; - unsigned int ofs; - u8 md5_hash[16]; - - if (buf_len != CIFS_MF_SYMLINK_FILE_SIZE) - return -EINVAL; - - link_len = strlen(link_str); - - if (link_len > CIFS_MF_SYMLINK_LINK_MAXLEN) - return -ENAMETOOLONG; - - rc = symlink_hash(link_len, link_str, md5_hash); - if (rc) { - cifs_dbg(FYI, "%s: MD5 hash failure: %d\n", __func__, rc); - return rc; - } - - scnprintf(buf, buf_len, - CIFS_MF_SYMLINK_LEN_FORMAT CIFS_MF_SYMLINK_MD5_FORMAT, - link_len, - CIFS_MF_SYMLINK_MD5_ARGS(md5_hash)); - - ofs = CIFS_MF_SYMLINK_LINK_OFFSET; - memcpy(buf + ofs, link_str, link_len); - - ofs += link_len; - if (ofs < CIFS_MF_SYMLINK_FILE_SIZE) { - buf[ofs] = '\n'; - ofs++; - } - - while (ofs < CIFS_MF_SYMLINK_FILE_SIZE) { - buf[ofs] = ' '; - ofs++; - } - - return 0; -} - -bool -couldbe_mf_symlink(const struct cifs_fattr *fattr) -{ - if (!S_ISREG(fattr->cf_mode)) - /* it's not a symlink */ - return false; - - if (fattr->cf_eof != CIFS_MF_SYMLINK_FILE_SIZE) - /* it's not a symlink */ - return false; - - return true; -} - -static int -create_mf_symlink(const unsigned int xid, struct cifs_tcon *tcon, - struct cifs_sb_info *cifs_sb, const char *fromName, - const char *toName) -{ - int rc; - u8 *buf; - unsigned int bytes_written = 0; - - buf = kmalloc(CIFS_MF_SYMLINK_FILE_SIZE, GFP_KERNEL); - if (!buf) - return -ENOMEM; - - rc = format_mf_symlink(buf, CIFS_MF_SYMLINK_FILE_SIZE, toName); - if (rc) - goto out; - - if (tcon->ses->server->ops->create_mf_symlink) - rc = tcon->ses->server->ops->create_mf_symlink(xid, tcon, - cifs_sb, fromName, buf, &bytes_written); - else - rc = -EOPNOTSUPP; - - if (rc) - goto out; - - if (bytes_written != CIFS_MF_SYMLINK_FILE_SIZE) - rc = -EIO; -out: - kfree(buf); - return rc; -} - -int -check_mf_symlink(unsigned int xid, struct cifs_tcon *tcon, - struct cifs_sb_info *cifs_sb, struct cifs_fattr *fattr, - const unsigned char *path) -{ - int rc; - u8 *buf = NULL; - unsigned int link_len = 0; - unsigned int bytes_read = 0; - char *symlink = NULL; - - if (!couldbe_mf_symlink(fattr)) - /* it's not a symlink */ - return 0; - - buf = kmalloc(CIFS_MF_SYMLINK_FILE_SIZE, GFP_KERNEL); - if (!buf) - return -ENOMEM; - - if (tcon->ses->server->ops->query_mf_symlink) - rc = tcon->ses->server->ops->query_mf_symlink(xid, tcon, - cifs_sb, path, buf, &bytes_read); - else - rc = -ENOSYS; - - if (rc) - goto out; - - if (bytes_read == 0) /* not a symlink */ - goto out; - - rc = parse_mf_symlink(buf, bytes_read, &link_len, &symlink); - if (rc == -EINVAL) { - /* it's not a symlink */ - rc = 0; - goto out; - } - - if (rc != 0) - goto out; - - /* it is a symlink */ - fattr->cf_eof = link_len; - fattr->cf_mode &= ~S_IFMT; - fattr->cf_mode |= S_IFLNK | S_IRWXU | S_IRWXG | S_IRWXO; - fattr->cf_dtype = DT_LNK; - fattr->cf_symlink_target = symlink; -out: - kfree(buf); - return rc; -} - -#ifdef CONFIG_CIFS_ALLOW_INSECURE_LEGACY -/* - * SMB 1.0 Protocol specific functions - */ - -int -cifs_query_mf_symlink(unsigned int xid, struct cifs_tcon *tcon, - struct cifs_sb_info *cifs_sb, const unsigned char *path, - char *pbuf, unsigned int *pbytes_read) -{ - int rc; - int oplock = 0; - struct cifs_fid fid; - struct cifs_open_parms oparms; - struct cifs_io_parms io_parms = {0}; - int buf_type = CIFS_NO_BUFFER; - FILE_ALL_INFO file_info; - - oparms = (struct cifs_open_parms) { - .tcon = tcon, - .cifs_sb = cifs_sb, - .desired_access = GENERIC_READ, - .create_options = cifs_create_options(cifs_sb, CREATE_NOT_DIR), - .disposition = FILE_OPEN, - .path = path, - .fid = &fid, - }; - - rc = CIFS_open(xid, &oparms, &oplock, &file_info); - if (rc) - return rc; - - if (file_info.EndOfFile != cpu_to_le64(CIFS_MF_SYMLINK_FILE_SIZE)) { - rc = -ENOENT; - /* it's not a symlink */ - goto out; - } - - io_parms.netfid = fid.netfid; - io_parms.pid = current->tgid; - io_parms.tcon = tcon; - io_parms.offset = 0; - io_parms.length = CIFS_MF_SYMLINK_FILE_SIZE; - - rc = CIFSSMBRead(xid, &io_parms, pbytes_read, &pbuf, &buf_type); -out: - CIFSSMBClose(xid, tcon, fid.netfid); - return rc; -} - -int -cifs_create_mf_symlink(unsigned int xid, struct cifs_tcon *tcon, - struct cifs_sb_info *cifs_sb, const unsigned char *path, - char *pbuf, unsigned int *pbytes_written) -{ - int rc; - int oplock = 0; - struct cifs_fid fid; - struct cifs_open_parms oparms; - struct cifs_io_parms io_parms = {0}; - - oparms = (struct cifs_open_parms) { - .tcon = tcon, - .cifs_sb = cifs_sb, - .desired_access = GENERIC_WRITE, - .create_options = cifs_create_options(cifs_sb, CREATE_NOT_DIR), - .disposition = FILE_CREATE, - .path = path, - .fid = &fid, - }; - - rc = CIFS_open(xid, &oparms, &oplock, NULL); - if (rc) - return rc; - - io_parms.netfid = fid.netfid; - io_parms.pid = current->tgid; - io_parms.tcon = tcon; - io_parms.offset = 0; - io_parms.length = CIFS_MF_SYMLINK_FILE_SIZE; - - rc = CIFSSMBWrite(xid, &io_parms, pbytes_written, pbuf); - CIFSSMBClose(xid, tcon, fid.netfid); - return rc; -} -#endif /* CONFIG_CIFS_ALLOW_INSECURE_LEGACY */ - -/* - * SMB 2.1/SMB3 Protocol specific functions - */ -int -smb3_query_mf_symlink(unsigned int xid, struct cifs_tcon *tcon, - struct cifs_sb_info *cifs_sb, const unsigned char *path, - char *pbuf, unsigned int *pbytes_read) -{ - int rc; - struct cifs_fid fid; - struct cifs_open_parms oparms; - struct cifs_io_parms io_parms = {0}; - int buf_type = CIFS_NO_BUFFER; - __le16 *utf16_path; - __u8 oplock = SMB2_OPLOCK_LEVEL_NONE; - struct smb2_file_all_info *pfile_info = NULL; - - oparms = (struct cifs_open_parms) { - .tcon = tcon, - .cifs_sb = cifs_sb, - .path = path, - .desired_access = GENERIC_READ, - .create_options = cifs_create_options(cifs_sb, CREATE_NOT_DIR), - .disposition = FILE_OPEN, - .fid = &fid, - }; - - utf16_path = cifs_convert_path_to_utf16(path, cifs_sb); - if (utf16_path == NULL) - return -ENOMEM; - - pfile_info = kzalloc(sizeof(struct smb2_file_all_info) + PATH_MAX * 2, - GFP_KERNEL); - - if (pfile_info == NULL) { - kfree(utf16_path); - return -ENOMEM; - } - - rc = SMB2_open(xid, &oparms, utf16_path, &oplock, pfile_info, NULL, - NULL, NULL); - if (rc) - goto qmf_out_open_fail; - - if (pfile_info->EndOfFile != cpu_to_le64(CIFS_MF_SYMLINK_FILE_SIZE)) { - /* it's not a symlink */ - rc = -ENOENT; /* Is there a better rc to return? */ - goto qmf_out; - } - - io_parms.netfid = fid.netfid; - io_parms.pid = current->tgid; - io_parms.tcon = tcon; - io_parms.offset = 0; - io_parms.length = CIFS_MF_SYMLINK_FILE_SIZE; - io_parms.persistent_fid = fid.persistent_fid; - io_parms.volatile_fid = fid.volatile_fid; - rc = SMB2_read(xid, &io_parms, pbytes_read, &pbuf, &buf_type); -qmf_out: - SMB2_close(xid, tcon, fid.persistent_fid, fid.volatile_fid); -qmf_out_open_fail: - kfree(utf16_path); - kfree(pfile_info); - return rc; -} - -int -smb3_create_mf_symlink(unsigned int xid, struct cifs_tcon *tcon, - struct cifs_sb_info *cifs_sb, const unsigned char *path, - char *pbuf, unsigned int *pbytes_written) -{ - int rc; - struct cifs_fid fid; - struct cifs_open_parms oparms; - struct cifs_io_parms io_parms = {0}; - __le16 *utf16_path; - __u8 oplock = SMB2_OPLOCK_LEVEL_NONE; - struct kvec iov[2]; - - cifs_dbg(FYI, "%s: path: %s\n", __func__, path); - - utf16_path = cifs_convert_path_to_utf16(path, cifs_sb); - if (!utf16_path) - return -ENOMEM; - - oparms = (struct cifs_open_parms) { - .tcon = tcon, - .cifs_sb = cifs_sb, - .path = path, - .desired_access = GENERIC_WRITE, - .create_options = cifs_create_options(cifs_sb, CREATE_NOT_DIR), - .disposition = FILE_CREATE, - .fid = &fid, - .mode = 0644, - }; - - rc = SMB2_open(xid, &oparms, utf16_path, &oplock, NULL, NULL, - NULL, NULL); - if (rc) { - kfree(utf16_path); - return rc; - } - - io_parms.netfid = fid.netfid; - io_parms.pid = current->tgid; - io_parms.tcon = tcon; - io_parms.offset = 0; - io_parms.length = CIFS_MF_SYMLINK_FILE_SIZE; - io_parms.persistent_fid = fid.persistent_fid; - io_parms.volatile_fid = fid.volatile_fid; - - /* iov[0] is reserved for smb header */ - iov[1].iov_base = pbuf; - iov[1].iov_len = CIFS_MF_SYMLINK_FILE_SIZE; - - rc = SMB2_write(xid, &io_parms, pbytes_written, iov, 1); - - /* Make sure we wrote all of the symlink data */ - if ((rc == 0) && (*pbytes_written != CIFS_MF_SYMLINK_FILE_SIZE)) - rc = -EIO; - - SMB2_close(xid, tcon, fid.persistent_fid, fid.volatile_fid); - - kfree(utf16_path); - return rc; -} - -/* - * M-F Symlink Functions - End - */ - -int -cifs_hardlink(struct dentry *old_file, struct inode *inode, - struct dentry *direntry) -{ - int rc = -EACCES; - unsigned int xid; - const char *from_name, *to_name; - void *page1, *page2; - struct cifs_sb_info *cifs_sb = CIFS_SB(inode->i_sb); - struct tcon_link *tlink; - struct cifs_tcon *tcon; - struct TCP_Server_Info *server; - struct cifsInodeInfo *cifsInode; - - if (unlikely(cifs_forced_shutdown(cifs_sb))) - return -EIO; - - tlink = cifs_sb_tlink(cifs_sb); - if (IS_ERR(tlink)) - return PTR_ERR(tlink); - tcon = tlink_tcon(tlink); - - xid = get_xid(); - page1 = alloc_dentry_path(); - page2 = alloc_dentry_path(); - - from_name = build_path_from_dentry(old_file, page1); - if (IS_ERR(from_name)) { - rc = PTR_ERR(from_name); - goto cifs_hl_exit; - } - to_name = build_path_from_dentry(direntry, page2); - if (IS_ERR(to_name)) { - rc = PTR_ERR(to_name); - goto cifs_hl_exit; - } - -#ifdef CONFIG_CIFS_ALLOW_INSECURE_LEGACY - if (tcon->unix_ext) - rc = CIFSUnixCreateHardLink(xid, tcon, from_name, to_name, - cifs_sb->local_nls, - cifs_remap(cifs_sb)); - else { -#else - { -#endif /* CONFIG_CIFS_ALLOW_INSECURE_LEGACY */ - server = tcon->ses->server; - if (!server->ops->create_hardlink) { - rc = -ENOSYS; - goto cifs_hl_exit; - } - rc = server->ops->create_hardlink(xid, tcon, from_name, to_name, - cifs_sb); - if ((rc == -EIO) || (rc == -EINVAL)) - rc = -EOPNOTSUPP; - } - - d_drop(direntry); /* force new lookup from server of target */ - - /* - * if source file is cached (oplocked) revalidate will not go to server - * until the file is closed or oplock broken so update nlinks locally - */ - if (d_really_is_positive(old_file)) { - cifsInode = CIFS_I(d_inode(old_file)); - if (rc == 0) { - spin_lock(&d_inode(old_file)->i_lock); - inc_nlink(d_inode(old_file)); - spin_unlock(&d_inode(old_file)->i_lock); - - /* - * parent dir timestamps will update from srv within a - * second, would it really be worth it to set the parent - * dir cifs inode time to zero to force revalidate - * (faster) for it too? - */ - } - /* - * if not oplocked will force revalidate to get info on source - * file from srv. Note Samba server prior to 4.2 has bug - - * not updating src file ctime on hardlinks but Windows servers - * handle it properly - */ - cifsInode->time = 0; - - /* - * Will update parent dir timestamps from srv within a second. - * Would it really be worth it to set the parent dir (cifs - * inode) time field to zero to force revalidate on parent - * directory faster ie - * - * CIFS_I(inode)->time = 0; - */ - } - -cifs_hl_exit: - free_dentry_path(page1); - free_dentry_path(page2); - free_xid(xid); - cifs_put_tlink(tlink); - return rc; -} - -int -cifs_symlink(struct user_namespace *mnt_userns, struct inode *inode, - struct dentry *direntry, const char *symname) -{ - int rc = -EOPNOTSUPP; - unsigned int xid; - struct cifs_sb_info *cifs_sb = CIFS_SB(inode->i_sb); - struct tcon_link *tlink; - struct cifs_tcon *pTcon; - const char *full_path; - void *page; - struct inode *newinode = NULL; - - if (unlikely(cifs_forced_shutdown(cifs_sb))) - return -EIO; - - page = alloc_dentry_path(); - if (!page) - return -ENOMEM; - - xid = get_xid(); - - tlink = cifs_sb_tlink(cifs_sb); - if (IS_ERR(tlink)) { - rc = PTR_ERR(tlink); - goto symlink_exit; - } - pTcon = tlink_tcon(tlink); - - full_path = build_path_from_dentry(direntry, page); - if (IS_ERR(full_path)) { - rc = PTR_ERR(full_path); - goto symlink_exit; - } - - cifs_dbg(FYI, "Full path: %s\n", full_path); - cifs_dbg(FYI, "symname is %s\n", symname); - - /* BB what if DFS and this volume is on different share? BB */ - if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_MF_SYMLINKS) - rc = create_mf_symlink(xid, pTcon, cifs_sb, full_path, symname); -#ifdef CONFIG_CIFS_ALLOW_INSECURE_LEGACY - else if (pTcon->unix_ext) - rc = CIFSUnixCreateSymLink(xid, pTcon, full_path, symname, - cifs_sb->local_nls, - cifs_remap(cifs_sb)); -#endif /* CONFIG_CIFS_ALLOW_INSECURE_LEGACY */ - /* else - rc = CIFSCreateReparseSymLink(xid, pTcon, fromName, toName, - cifs_sb_target->local_nls); */ - - if (rc == 0) { - if (pTcon->posix_extensions) - rc = smb311_posix_get_inode_info(&newinode, full_path, inode->i_sb, xid); - else if (pTcon->unix_ext) - rc = cifs_get_inode_info_unix(&newinode, full_path, - inode->i_sb, xid); - else - rc = cifs_get_inode_info(&newinode, full_path, NULL, - inode->i_sb, xid, NULL); - - if (rc != 0) { - cifs_dbg(FYI, "Create symlink ok, getinodeinfo fail rc = %d\n", - rc); - } else { - d_instantiate(direntry, newinode); - } - } -symlink_exit: - free_dentry_path(page); - cifs_put_tlink(tlink); - free_xid(xid); - return rc; -} diff --git a/fs/cifs/misc.c b/fs/cifs/misc.c deleted file mode 100644 index 31e06133acc3d..0000000000000 --- a/fs/cifs/misc.c +++ /dev/null @@ -1,1434 +0,0 @@ -// SPDX-License-Identifier: LGPL-2.1 -/* - * - * Copyright (C) International Business Machines Corp., 2002,2008 - * Author(s): Steve French (sfrench@us.ibm.com) - * - */ - -#include -#include -#include -#include -#include "cifspdu.h" -#include "cifsglob.h" -#include "cifsproto.h" -#include "cifs_debug.h" -#include "smberr.h" -#include "nterr.h" -#include "cifs_unicode.h" -#include "smb2pdu.h" -#include "cifsfs.h" -#ifdef CONFIG_CIFS_DFS_UPCALL -#include "dns_resolve.h" -#include "dfs_cache.h" -#endif -#include "fs_context.h" -#include "cached_dir.h" - -extern mempool_t *cifs_sm_req_poolp; -extern mempool_t *cifs_req_poolp; - -/* 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 - will eventually wrap past zero) of the total vfs operations handled - since the cifs fs was mounted */ - -unsigned int -_get_xid(void) -{ - unsigned int xid; - - spin_lock(&GlobalMid_Lock); - GlobalTotalActiveXid++; - - /* keep high water mark for number of simultaneous ops in filesystem */ - if (GlobalTotalActiveXid > GlobalMaxActiveXid) - GlobalMaxActiveXid = GlobalTotalActiveXid; - if (GlobalTotalActiveXid > 65000) - cifs_dbg(FYI, "warning: more than 65000 requests active\n"); - xid = GlobalCurrentXid++; - spin_unlock(&GlobalMid_Lock); - return xid; -} - -void -_free_xid(unsigned int xid) -{ - spin_lock(&GlobalMid_Lock); - /* if (GlobalTotalActiveXid == 0) - BUG(); */ - GlobalTotalActiveXid--; - spin_unlock(&GlobalMid_Lock); -} - -struct cifs_ses * -sesInfoAlloc(void) -{ - struct cifs_ses *ret_buf; - - ret_buf = kzalloc(sizeof(struct cifs_ses), GFP_KERNEL); - if (ret_buf) { - atomic_inc(&sesInfoAllocCount); - spin_lock_init(&ret_buf->ses_lock); - ret_buf->ses_status = SES_NEW; - ++ret_buf->ses_count; - INIT_LIST_HEAD(&ret_buf->smb_ses_list); - INIT_LIST_HEAD(&ret_buf->tcon_list); - mutex_init(&ret_buf->session_mutex); - spin_lock_init(&ret_buf->iface_lock); - INIT_LIST_HEAD(&ret_buf->iface_list); - spin_lock_init(&ret_buf->chan_lock); - } - return ret_buf; -} - -void -sesInfoFree(struct cifs_ses *buf_to_free) -{ - struct cifs_server_iface *iface = NULL, *niface = NULL; - - if (buf_to_free == NULL) { - cifs_dbg(FYI, "Null buffer passed to sesInfoFree\n"); - return; - } - - atomic_dec(&sesInfoAllocCount); - kfree(buf_to_free->serverOS); - kfree(buf_to_free->serverDomain); - kfree(buf_to_free->serverNOS); - kfree_sensitive(buf_to_free->password); - kfree(buf_to_free->user_name); - kfree(buf_to_free->domainName); - kfree_sensitive(buf_to_free->auth_key.response); - spin_lock(&buf_to_free->iface_lock); - list_for_each_entry_safe(iface, niface, &buf_to_free->iface_list, - iface_head) - kref_put(&iface->refcount, release_iface); - spin_unlock(&buf_to_free->iface_lock); - kfree_sensitive(buf_to_free); -} - -struct cifs_tcon * -tconInfoAlloc(void) -{ - struct cifs_tcon *ret_buf; - - ret_buf = kzalloc(sizeof(*ret_buf), GFP_KERNEL); - if (!ret_buf) - return NULL; - ret_buf->cfids = init_cached_dirs(); - if (!ret_buf->cfids) { - kfree(ret_buf); - return NULL; - } - - atomic_inc(&tconInfoAllocCount); - ret_buf->status = TID_NEW; - ++ret_buf->tc_count; - spin_lock_init(&ret_buf->tc_lock); - INIT_LIST_HEAD(&ret_buf->openFileList); - INIT_LIST_HEAD(&ret_buf->tcon_list); - spin_lock_init(&ret_buf->open_file_lock); - spin_lock_init(&ret_buf->stat_lock); - atomic_set(&ret_buf->num_local_opens, 0); - atomic_set(&ret_buf->num_remote_opens, 0); - - return ret_buf; -} - -void -tconInfoFree(struct cifs_tcon *tcon) -{ - if (tcon == NULL) { - cifs_dbg(FYI, "Null buffer passed to tconInfoFree\n"); - return; - } - free_cached_dirs(tcon->cfids); - atomic_dec(&tconInfoAllocCount); - kfree(tcon->nativeFileSystem); - kfree_sensitive(tcon->password); - kfree(tcon); -} - -struct smb_hdr * -cifs_buf_get(void) -{ - struct smb_hdr *ret_buf = NULL; - /* - * SMB2 header is bigger than CIFS one - no problems to clean some - * more bytes for CIFS. - */ - size_t buf_size = sizeof(struct smb2_hdr); - - /* - * We could use negotiated size instead of max_msgsize - - * but it may be more efficient to always alloc same size - * albeit slightly larger than necessary and maxbuffersize - * defaults to this and can not be bigger. - */ - ret_buf = mempool_alloc(cifs_req_poolp, GFP_NOFS); - - /* clear the first few header bytes */ - /* for most paths, more is cleared in header_assemble */ - memset(ret_buf, 0, buf_size + 3); - atomic_inc(&buf_alloc_count); -#ifdef CONFIG_CIFS_STATS2 - atomic_inc(&total_buf_alloc_count); -#endif /* CONFIG_CIFS_STATS2 */ - - return ret_buf; -} - -void -cifs_buf_release(void *buf_to_free) -{ - if (buf_to_free == NULL) { - /* cifs_dbg(FYI, "Null buffer passed to cifs_buf_release\n");*/ - return; - } - mempool_free(buf_to_free, cifs_req_poolp); - - atomic_dec(&buf_alloc_count); - return; -} - -struct smb_hdr * -cifs_small_buf_get(void) -{ - struct smb_hdr *ret_buf = NULL; - -/* We could use negotiated size instead of max_msgsize - - but it may be more efficient to always alloc same size - albeit slightly larger than necessary and maxbuffersize - defaults to this and can not be bigger */ - ret_buf = mempool_alloc(cifs_sm_req_poolp, GFP_NOFS); - /* No need to clear memory here, cleared in header assemble */ - /* memset(ret_buf, 0, sizeof(struct smb_hdr) + 27);*/ - atomic_inc(&small_buf_alloc_count); -#ifdef CONFIG_CIFS_STATS2 - atomic_inc(&total_small_buf_alloc_count); -#endif /* CONFIG_CIFS_STATS2 */ - - return ret_buf; -} - -void -cifs_small_buf_release(void *buf_to_free) -{ - - if (buf_to_free == NULL) { - cifs_dbg(FYI, "Null buffer passed to cifs_small_buf_release\n"); - return; - } - mempool_free(buf_to_free, cifs_sm_req_poolp); - - atomic_dec(&small_buf_alloc_count); - return; -} - -void -free_rsp_buf(int resp_buftype, void *rsp) -{ - if (resp_buftype == CIFS_SMALL_BUFFER) - cifs_small_buf_release(rsp); - else if (resp_buftype == CIFS_LARGE_BUFFER) - cifs_buf_release(rsp); -} - -/* 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 cifs_tcon *treeCon, int word_count - /* length of fixed section (word count) in two byte units */) -{ - char *temp = (char *) buffer; - - memset(temp, 0, 256); /* bigger than MAX_CIFS_HDR_SIZE */ - - buffer->smb_buf_length = cpu_to_be32( - (2 * word_count) + sizeof(struct smb_hdr) - - 4 /* RFC 1001 length field does not count */ + - 2 /* for bcc field itself */) ; - - buffer->Protocol[0] = 0xFF; - buffer->Protocol[1] = 'S'; - buffer->Protocol[2] = 'M'; - buffer->Protocol[3] = 'B'; - buffer->Command = smb_command; - buffer->Flags = 0x00; /* case sensitive */ - buffer->Flags2 = SMBFLG2_KNOWS_LONG_NAMES; - buffer->Pid = cpu_to_le16((__u16)current->tgid); - buffer->PidHigh = cpu_to_le16((__u16)(current->tgid >> 16)); - if (treeCon) { - buffer->Tid = treeCon->tid; - if (treeCon->ses) { - if (treeCon->ses->capabilities & CAP_UNICODE) - buffer->Flags2 |= SMBFLG2_UNICODE; - if (treeCon->ses->capabilities & CAP_STATUS32) - buffer->Flags2 |= SMBFLG2_ERR_STATUS; - - /* Uid is not converted */ - buffer->Uid = treeCon->ses->Suid; - if (treeCon->ses->server) - buffer->Mid = get_next_mid(treeCon->ses->server); - } - 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->sign) - buffer->Flags2 |= SMBFLG2_SECURITY_SIGNATURE; - } - -/* endian conversion of flags is now done just before sending */ - buffer->WordCount = (char) word_count; - return; -} - -static int -check_smb_hdr(struct smb_hdr *smb) -{ - /* does it have the right SMB "signature" ? */ - if (*(__le32 *) smb->Protocol != cpu_to_le32(0x424d53ff)) { - cifs_dbg(VFS, "Bad protocol string signature header 0x%x\n", - *(unsigned int *)smb->Protocol); - return 1; - } - - /* if it's a response then accept */ - if (smb->Flags & SMBFLG_RESPONSE) - return 0; - - /* only one valid case where server sends us request */ - if (smb->Command == SMB_COM_LOCKING_ANDX) - return 0; - - cifs_dbg(VFS, "Server sent request, not response. mid=%u\n", - get_mid(smb)); - return 1; -} - -int -checkSMB(char *buf, unsigned int total_read, struct TCP_Server_Info *server) -{ - struct smb_hdr *smb = (struct smb_hdr *)buf; - __u32 rfclen = be32_to_cpu(smb->smb_buf_length); - __u32 clc_len; /* calculated length */ - cifs_dbg(FYI, "checkSMB Length: 0x%x, smb_buf_length: 0x%x\n", - total_read, rfclen); - - /* is this frame too small to even get to a BCC? */ - if (total_read < 2 + sizeof(struct smb_hdr)) { - if ((total_read >= sizeof(struct smb_hdr) - 1) - && (smb->Status.CifsError != 0)) { - /* it's an error return */ - smb->WordCount = 0; - /* some error cases do not return wct and bcc */ - return 0; - } else if ((total_read == sizeof(struct smb_hdr) + 1) && - (smb->WordCount == 0)) { - char *tmp = (char *)smb; - /* Need to work around a bug in two servers here */ - /* First, check if the part of bcc they sent was zero */ - if (tmp[sizeof(struct smb_hdr)] == 0) { - /* some servers return only half of bcc - * on simple responses (wct, bcc both zero) - * in particular have seen this on - * ulogoffX and FindClose. This leaves - * one byte of bcc potentially unitialized - */ - /* zero rest of bcc */ - tmp[sizeof(struct smb_hdr)+1] = 0; - return 0; - } - cifs_dbg(VFS, "rcvd invalid byte count (bcc)\n"); - } else { - cifs_dbg(VFS, "Length less than smb header size\n"); - } - return -EIO; - } - - /* otherwise, there is enough to get to the BCC */ - if (check_smb_hdr(smb)) - return -EIO; - clc_len = smbCalcSize(smb); - - if (4 + rfclen != total_read) { - cifs_dbg(VFS, "Length read does not match RFC1001 length %d\n", - rfclen); - return -EIO; - } - - if (4 + rfclen != clc_len) { - __u16 mid = get_mid(smb); - /* check if bcc wrapped around for large read responses */ - if ((rfclen > 64 * 1024) && (rfclen > clc_len)) { - /* check if lengths match mod 64K */ - if (((4 + rfclen) & 0xFFFF) == (clc_len & 0xFFFF)) - return 0; /* bcc wrapped */ - } - cifs_dbg(FYI, "Calculated size %u vs length %u mismatch for mid=%u\n", - clc_len, 4 + rfclen, mid); - - if (4 + rfclen < clc_len) { - cifs_dbg(VFS, "RFC1001 size %u smaller than SMB for mid=%u\n", - rfclen, mid); - return -EIO; - } else if (rfclen > clc_len + 512) { - /* - * Some servers (Windows XP in particular) send more - * data than the lengths in the SMB packet would - * indicate on certain calls (byte range locks and - * trans2 find first calls in particular). While the - * client can handle such a frame by ignoring the - * trailing data, we choose limit the amount of extra - * data to 512 bytes. - */ - cifs_dbg(VFS, "RFC1001 size %u more than 512 bytes larger than SMB for mid=%u\n", - rfclen, mid); - return -EIO; - } - } - return 0; -} - -bool -is_valid_oplock_break(char *buffer, struct TCP_Server_Info *srv) -{ - struct smb_hdr *buf = (struct smb_hdr *)buffer; - struct smb_com_lock_req *pSMB = (struct smb_com_lock_req *)buf; - struct TCP_Server_Info *pserver; - struct cifs_ses *ses; - struct cifs_tcon *tcon; - struct cifsInodeInfo *pCifsInode; - struct cifsFileInfo *netfile; - - cifs_dbg(FYI, "Checking for oplock break or dnotify response\n"); - if ((pSMB->hdr.Command == SMB_COM_NT_TRANSACT) && - (pSMB->hdr.Flags & SMBFLG_RESPONSE)) { - struct smb_com_transaction_change_notify_rsp *pSMBr = - (struct smb_com_transaction_change_notify_rsp *)buf; - struct file_notify_information *pnotify; - __u32 data_offset = 0; - size_t len = srv->total_read - sizeof(pSMBr->hdr.smb_buf_length); - - if (get_bcc(buf) > sizeof(struct file_notify_information)) { - data_offset = le32_to_cpu(pSMBr->DataOffset); - - if (data_offset > - len - sizeof(struct file_notify_information)) { - cifs_dbg(FYI, "Invalid data_offset %u\n", - data_offset); - return true; - } - pnotify = (struct file_notify_information *) - ((char *)&pSMBr->hdr.Protocol + data_offset); - cifs_dbg(FYI, "dnotify on %s Action: 0x%x\n", - pnotify->FileName, pnotify->Action); - /* cifs_dump_mem("Rcvd notify Data: ",buf, - sizeof(struct smb_hdr)+60); */ - return true; - } - if (pSMBr->hdr.Status.CifsError) { - cifs_dbg(FYI, "notify err 0x%x\n", - pSMBr->hdr.Status.CifsError); - return true; - } - return false; - } - if (pSMB->hdr.Command != SMB_COM_LOCKING_ANDX) - return false; - if (pSMB->hdr.Flags & SMBFLG_RESPONSE) { - /* no sense logging error on invalid handle on oplock - break - harmless race between close request and oplock - break response is expected from time to time writing out - large dirty files cached on the client */ - if ((NT_STATUS_INVALID_HANDLE) == - le32_to_cpu(pSMB->hdr.Status.CifsError)) { - cifs_dbg(FYI, "Invalid handle on oplock break\n"); - return true; - } else if (ERRbadfid == - le16_to_cpu(pSMB->hdr.Status.DosError.Error)) { - return true; - } else { - return false; /* on valid oplock brk we get "request" */ - } - } - if (pSMB->hdr.WordCount != 8) - return false; - - cifs_dbg(FYI, "oplock type 0x%x level 0x%x\n", - pSMB->LockType, pSMB->OplockLevel); - if (!(pSMB->LockType & LOCKING_ANDX_OPLOCK_RELEASE)) - return false; - - /* If server is a channel, select the primary channel */ - pserver = CIFS_SERVER_IS_CHAN(srv) ? srv->primary_server : srv; - - /* look up tcon based on tid & uid */ - spin_lock(&cifs_tcp_ses_lock); - list_for_each_entry(ses, &pserver->smb_ses_list, smb_ses_list) { - list_for_each_entry(tcon, &ses->tcon_list, tcon_list) { - if (tcon->tid != buf->Tid) - continue; - - cifs_stats_inc(&tcon->stats.cifs_stats.num_oplock_brks); - spin_lock(&tcon->open_file_lock); - list_for_each_entry(netfile, &tcon->openFileList, tlist) { - if (pSMB->Fid != netfile->fid.netfid) - continue; - - cifs_dbg(FYI, "file id match, oplock break\n"); - pCifsInode = CIFS_I(d_inode(netfile->dentry)); - - set_bit(CIFS_INODE_PENDING_OPLOCK_BREAK, - &pCifsInode->flags); - - netfile->oplock_epoch = 0; - netfile->oplock_level = pSMB->OplockLevel; - netfile->oplock_break_cancelled = false; - cifs_queue_oplock_break(netfile); - - spin_unlock(&tcon->open_file_lock); - spin_unlock(&cifs_tcp_ses_lock); - return true; - } - spin_unlock(&tcon->open_file_lock); - spin_unlock(&cifs_tcp_ses_lock); - cifs_dbg(FYI, "No matching file for oplock break\n"); - return true; - } - } - spin_unlock(&cifs_tcp_ses_lock); - cifs_dbg(FYI, "Can not process oplock break for non-existent connection\n"); - return true; -} - -void -dump_smb(void *buf, int smb_buf_length) -{ - if (traceSMB == 0) - return; - - print_hex_dump(KERN_DEBUG, "", DUMP_PREFIX_NONE, 8, 2, buf, - smb_buf_length, true); -} - -void -cifs_autodisable_serverino(struct cifs_sb_info *cifs_sb) -{ - if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_SERVER_INUM) { - struct cifs_tcon *tcon = NULL; - - if (cifs_sb->master_tlink) - tcon = cifs_sb_master_tcon(cifs_sb); - - cifs_sb->mnt_cifs_flags &= ~CIFS_MOUNT_SERVER_INUM; - cifs_sb->mnt_cifs_serverino_autodisabled = true; - cifs_dbg(VFS, "Autodisabling the use of server inode numbers on %s\n", - tcon ? tcon->tree_name : "new server"); - cifs_dbg(VFS, "The server doesn't seem to support them properly or the files might be on different servers (DFS)\n"); - cifs_dbg(VFS, "Hardlinks will not be recognized on this mount. Consider mounting with the \"noserverino\" option to silence this message.\n"); - - } -} - -void cifs_set_oplock_level(struct cifsInodeInfo *cinode, __u32 oplock) -{ - oplock &= 0xF; - - if (oplock == OPLOCK_EXCLUSIVE) { - cinode->oplock = CIFS_CACHE_WRITE_FLG | CIFS_CACHE_READ_FLG; - cifs_dbg(FYI, "Exclusive Oplock granted on inode %p\n", - &cinode->netfs.inode); - } else if (oplock == OPLOCK_READ) { - cinode->oplock = CIFS_CACHE_READ_FLG; - cifs_dbg(FYI, "Level II Oplock granted on inode %p\n", - &cinode->netfs.inode); - } else - cinode->oplock = 0; -} - -/* - * We wait for oplock breaks to be processed before we attempt to perform - * writes. - */ -int cifs_get_writer(struct cifsInodeInfo *cinode) -{ - int rc; - -start: - rc = wait_on_bit(&cinode->flags, CIFS_INODE_PENDING_OPLOCK_BREAK, - TASK_KILLABLE); - if (rc) - return rc; - - spin_lock(&cinode->writers_lock); - if (!cinode->writers) - set_bit(CIFS_INODE_PENDING_WRITERS, &cinode->flags); - cinode->writers++; - /* Check to see if we have started servicing an oplock break */ - if (test_bit(CIFS_INODE_PENDING_OPLOCK_BREAK, &cinode->flags)) { - cinode->writers--; - if (cinode->writers == 0) { - clear_bit(CIFS_INODE_PENDING_WRITERS, &cinode->flags); - wake_up_bit(&cinode->flags, CIFS_INODE_PENDING_WRITERS); - } - spin_unlock(&cinode->writers_lock); - goto start; - } - spin_unlock(&cinode->writers_lock); - return 0; -} - -void cifs_put_writer(struct cifsInodeInfo *cinode) -{ - spin_lock(&cinode->writers_lock); - cinode->writers--; - if (cinode->writers == 0) { - clear_bit(CIFS_INODE_PENDING_WRITERS, &cinode->flags); - wake_up_bit(&cinode->flags, CIFS_INODE_PENDING_WRITERS); - } - spin_unlock(&cinode->writers_lock); -} - -/** - * cifs_queue_oplock_break - queue the oplock break handler for cfile - * @cfile: The file to break the oplock on - * - * This function is called from the demultiplex thread when it - * receives an oplock break for @cfile. - * - * Assumes the tcon->open_file_lock is held. - * Assumes cfile->file_info_lock is NOT held. - */ -void cifs_queue_oplock_break(struct cifsFileInfo *cfile) -{ - /* - * Bump the handle refcount now while we hold the - * open_file_lock to enforce the validity of it for the oplock - * break handler. The matching put is done at the end of the - * handler. - */ - cifsFileInfo_get(cfile); - - queue_work(cifsoplockd_wq, &cfile->oplock_break); -} - -void cifs_done_oplock_break(struct cifsInodeInfo *cinode) -{ - clear_bit(CIFS_INODE_PENDING_OPLOCK_BREAK, &cinode->flags); - wake_up_bit(&cinode->flags, CIFS_INODE_PENDING_OPLOCK_BREAK); -} - -bool -backup_cred(struct cifs_sb_info *cifs_sb) -{ - if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_CIFS_BACKUPUID) { - if (uid_eq(cifs_sb->ctx->backupuid, current_fsuid())) - return true; - } - if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_CIFS_BACKUPGID) { - if (in_group_p(cifs_sb->ctx->backupgid)) - return true; - } - - return false; -} - -void -cifs_del_pending_open(struct cifs_pending_open *open) -{ - spin_lock(&tlink_tcon(open->tlink)->open_file_lock); - list_del(&open->olist); - spin_unlock(&tlink_tcon(open->tlink)->open_file_lock); -} - -void -cifs_add_pending_open_locked(struct cifs_fid *fid, struct tcon_link *tlink, - struct cifs_pending_open *open) -{ - memcpy(open->lease_key, fid->lease_key, SMB2_LEASE_KEY_SIZE); - open->oplock = CIFS_OPLOCK_NO_CHANGE; - open->tlink = tlink; - fid->pending_open = open; - list_add_tail(&open->olist, &tlink_tcon(tlink)->pending_opens); -} - -void -cifs_add_pending_open(struct cifs_fid *fid, struct tcon_link *tlink, - struct cifs_pending_open *open) -{ - spin_lock(&tlink_tcon(tlink)->open_file_lock); - cifs_add_pending_open_locked(fid, tlink, open); - spin_unlock(&tlink_tcon(open->tlink)->open_file_lock); -} - -/* - * Critical section which runs after acquiring deferred_lock. - * As there is no reference count on cifs_deferred_close, pdclose - * should not be used outside deferred_lock. - */ -bool -cifs_is_deferred_close(struct cifsFileInfo *cfile, struct cifs_deferred_close **pdclose) -{ - struct cifs_deferred_close *dclose; - - list_for_each_entry(dclose, &CIFS_I(d_inode(cfile->dentry))->deferred_closes, dlist) { - if ((dclose->netfid == cfile->fid.netfid) && - (dclose->persistent_fid == cfile->fid.persistent_fid) && - (dclose->volatile_fid == cfile->fid.volatile_fid)) { - *pdclose = dclose; - return true; - } - } - return false; -} - -/* - * Critical section which runs after acquiring deferred_lock. - */ -void -cifs_add_deferred_close(struct cifsFileInfo *cfile, struct cifs_deferred_close *dclose) -{ - bool is_deferred = false; - struct cifs_deferred_close *pdclose; - - is_deferred = cifs_is_deferred_close(cfile, &pdclose); - if (is_deferred) { - kfree(dclose); - return; - } - - dclose->tlink = cfile->tlink; - dclose->netfid = cfile->fid.netfid; - dclose->persistent_fid = cfile->fid.persistent_fid; - dclose->volatile_fid = cfile->fid.volatile_fid; - list_add_tail(&dclose->dlist, &CIFS_I(d_inode(cfile->dentry))->deferred_closes); -} - -/* - * Critical section which runs after acquiring deferred_lock. - */ -void -cifs_del_deferred_close(struct cifsFileInfo *cfile) -{ - bool is_deferred = false; - struct cifs_deferred_close *dclose; - - is_deferred = cifs_is_deferred_close(cfile, &dclose); - if (!is_deferred) - return; - list_del(&dclose->dlist); - kfree(dclose); -} - -void -cifs_close_deferred_file(struct cifsInodeInfo *cifs_inode) -{ - struct cifsFileInfo *cfile = NULL; - struct file_list *tmp_list, *tmp_next_list; - struct list_head file_head; - - if (cifs_inode == NULL) - return; - - INIT_LIST_HEAD(&file_head); - spin_lock(&cifs_inode->open_file_lock); - list_for_each_entry(cfile, &cifs_inode->openFileList, flist) { - if (delayed_work_pending(&cfile->deferred)) { - if (cancel_delayed_work(&cfile->deferred)) { - spin_lock(&cifs_inode->deferred_lock); - cifs_del_deferred_close(cfile); - spin_unlock(&cifs_inode->deferred_lock); - - tmp_list = kmalloc(sizeof(struct file_list), GFP_ATOMIC); - if (tmp_list == NULL) - break; - tmp_list->cfile = cfile; - list_add_tail(&tmp_list->list, &file_head); - } - } - } - spin_unlock(&cifs_inode->open_file_lock); - - list_for_each_entry_safe(tmp_list, tmp_next_list, &file_head, list) { - _cifsFileInfo_put(tmp_list->cfile, false, false); - list_del(&tmp_list->list); - kfree(tmp_list); - } -} - -void -cifs_close_all_deferred_files(struct cifs_tcon *tcon) -{ - struct cifsFileInfo *cfile; - struct file_list *tmp_list, *tmp_next_list; - struct list_head file_head; - - INIT_LIST_HEAD(&file_head); - spin_lock(&tcon->open_file_lock); - list_for_each_entry(cfile, &tcon->openFileList, tlist) { - if (delayed_work_pending(&cfile->deferred)) { - if (cancel_delayed_work(&cfile->deferred)) { - spin_lock(&CIFS_I(d_inode(cfile->dentry))->deferred_lock); - cifs_del_deferred_close(cfile); - spin_unlock(&CIFS_I(d_inode(cfile->dentry))->deferred_lock); - - tmp_list = kmalloc(sizeof(struct file_list), GFP_ATOMIC); - if (tmp_list == NULL) - break; - tmp_list->cfile = cfile; - list_add_tail(&tmp_list->list, &file_head); - } - } - } - spin_unlock(&tcon->open_file_lock); - - list_for_each_entry_safe(tmp_list, tmp_next_list, &file_head, list) { - _cifsFileInfo_put(tmp_list->cfile, true, false); - list_del(&tmp_list->list); - kfree(tmp_list); - } -} -void -cifs_close_deferred_file_under_dentry(struct cifs_tcon *tcon, const char *path) -{ - struct cifsFileInfo *cfile; - struct file_list *tmp_list, *tmp_next_list; - struct list_head file_head; - void *page; - const char *full_path; - - INIT_LIST_HEAD(&file_head); - page = alloc_dentry_path(); - spin_lock(&tcon->open_file_lock); - list_for_each_entry(cfile, &tcon->openFileList, tlist) { - full_path = build_path_from_dentry(cfile->dentry, page); - if (strstr(full_path, path)) { - if (delayed_work_pending(&cfile->deferred)) { - if (cancel_delayed_work(&cfile->deferred)) { - spin_lock(&CIFS_I(d_inode(cfile->dentry))->deferred_lock); - cifs_del_deferred_close(cfile); - spin_unlock(&CIFS_I(d_inode(cfile->dentry))->deferred_lock); - - tmp_list = kmalloc(sizeof(struct file_list), GFP_ATOMIC); - if (tmp_list == NULL) - break; - tmp_list->cfile = cfile; - list_add_tail(&tmp_list->list, &file_head); - } - } - } - } - spin_unlock(&tcon->open_file_lock); - - list_for_each_entry_safe(tmp_list, tmp_next_list, &file_head, list) { - _cifsFileInfo_put(tmp_list->cfile, true, false); - list_del(&tmp_list->list); - kfree(tmp_list); - } - free_dentry_path(page); -} - -/* parses DFS referral V3 structure - * caller is responsible for freeing target_nodes - * returns: - * - on success - 0 - * - on failure - errno - */ -int -parse_dfs_referrals(struct get_dfs_referral_rsp *rsp, u32 rsp_size, - unsigned int *num_of_nodes, - struct dfs_info3_param **target_nodes, - const struct nls_table *nls_codepage, int remap, - const char *searchName, bool is_unicode) -{ - int i, rc = 0; - char *data_end; - struct dfs_referral_level_3 *ref; - - *num_of_nodes = le16_to_cpu(rsp->NumberOfReferrals); - - if (*num_of_nodes < 1) { - cifs_dbg(VFS, "num_referrals: must be at least > 0, but we get num_referrals = %d\n", - *num_of_nodes); - rc = -EINVAL; - goto parse_DFS_referrals_exit; - } - - ref = (struct dfs_referral_level_3 *) &(rsp->referrals); - if (ref->VersionNumber != cpu_to_le16(3)) { - cifs_dbg(VFS, "Referrals of V%d version are not supported, should be V3\n", - le16_to_cpu(ref->VersionNumber)); - rc = -EINVAL; - goto parse_DFS_referrals_exit; - } - - /* get the upper boundary of the resp buffer */ - data_end = (char *)rsp + rsp_size; - - cifs_dbg(FYI, "num_referrals: %d dfs flags: 0x%x ...\n", - *num_of_nodes, le32_to_cpu(rsp->DFSFlags)); - - *target_nodes = kcalloc(*num_of_nodes, sizeof(struct dfs_info3_param), - GFP_KERNEL); - if (*target_nodes == NULL) { - rc = -ENOMEM; - goto parse_DFS_referrals_exit; - } - - /* collect necessary data from referrals */ - for (i = 0; i < *num_of_nodes; i++) { - char *temp; - int max_len; - struct dfs_info3_param *node = (*target_nodes)+i; - - node->flags = le32_to_cpu(rsp->DFSFlags); - if (is_unicode) { - __le16 *tmp = kmalloc(strlen(searchName)*2 + 2, - GFP_KERNEL); - if (tmp == NULL) { - rc = -ENOMEM; - goto parse_DFS_referrals_exit; - } - cifsConvertToUTF16((__le16 *) tmp, searchName, - PATH_MAX, nls_codepage, remap); - node->path_consumed = cifs_utf16_bytes(tmp, - le16_to_cpu(rsp->PathConsumed), - nls_codepage); - kfree(tmp); - } else - node->path_consumed = le16_to_cpu(rsp->PathConsumed); - - node->server_type = le16_to_cpu(ref->ServerType); - node->ref_flag = le16_to_cpu(ref->ReferralEntryFlags); - - /* copy DfsPath */ - temp = (char *)ref + le16_to_cpu(ref->DfsPathOffset); - max_len = data_end - temp; - node->path_name = cifs_strndup_from_utf16(temp, max_len, - is_unicode, nls_codepage); - if (!node->path_name) { - rc = -ENOMEM; - goto parse_DFS_referrals_exit; - } - - /* copy link target UNC */ - temp = (char *)ref + le16_to_cpu(ref->NetworkAddressOffset); - max_len = data_end - temp; - node->node_name = cifs_strndup_from_utf16(temp, max_len, - is_unicode, nls_codepage); - if (!node->node_name) { - rc = -ENOMEM; - goto parse_DFS_referrals_exit; - } - - node->ttl = le32_to_cpu(ref->TimeToLive); - - ref++; - } - -parse_DFS_referrals_exit: - if (rc) { - free_dfs_info_array(*target_nodes, *num_of_nodes); - *target_nodes = NULL; - *num_of_nodes = 0; - } - return rc; -} - -struct cifs_aio_ctx * -cifs_aio_ctx_alloc(void) -{ - struct cifs_aio_ctx *ctx; - - /* - * Must use kzalloc to initialize ctx->bv to NULL and ctx->direct_io - * to false so that we know when we have to unreference pages within - * cifs_aio_ctx_release() - */ - ctx = kzalloc(sizeof(struct cifs_aio_ctx), GFP_KERNEL); - if (!ctx) - return NULL; - - INIT_LIST_HEAD(&ctx->list); - mutex_init(&ctx->aio_mutex); - init_completion(&ctx->done); - kref_init(&ctx->refcount); - return ctx; -} - -void -cifs_aio_ctx_release(struct kref *refcount) -{ - struct cifs_aio_ctx *ctx = container_of(refcount, - struct cifs_aio_ctx, refcount); - - cifsFileInfo_put(ctx->cfile); - - /* - * ctx->bv is only set if setup_aio_ctx_iter() was call successfuly - * which means that iov_iter_get_pages() was a success and thus that - * we have taken reference on pages. - */ - if (ctx->bv) { - unsigned i; - - for (i = 0; i < ctx->npages; i++) { - if (ctx->should_dirty) - set_page_dirty(ctx->bv[i].bv_page); - put_page(ctx->bv[i].bv_page); - } - kvfree(ctx->bv); - } - - kfree(ctx); -} - -#define CIFS_AIO_KMALLOC_LIMIT (1024 * 1024) - -int -setup_aio_ctx_iter(struct cifs_aio_ctx *ctx, struct iov_iter *iter, int rw) -{ - ssize_t rc; - unsigned int cur_npages; - unsigned int npages = 0; - unsigned int i; - size_t len; - size_t count = iov_iter_count(iter); - unsigned int saved_len; - size_t start; - unsigned int max_pages = iov_iter_npages(iter, INT_MAX); - struct page **pages = NULL; - struct bio_vec *bv = NULL; - - if (iov_iter_is_kvec(iter)) { - memcpy(&ctx->iter, iter, sizeof(*iter)); - ctx->len = count; - iov_iter_advance(iter, count); - return 0; - } - - if (array_size(max_pages, sizeof(*bv)) <= CIFS_AIO_KMALLOC_LIMIT) - bv = kmalloc_array(max_pages, sizeof(*bv), GFP_KERNEL); - - if (!bv) { - bv = vmalloc(array_size(max_pages, sizeof(*bv))); - if (!bv) - return -ENOMEM; - } - - if (array_size(max_pages, sizeof(*pages)) <= CIFS_AIO_KMALLOC_LIMIT) - pages = kmalloc_array(max_pages, sizeof(*pages), GFP_KERNEL); - - if (!pages) { - pages = vmalloc(array_size(max_pages, sizeof(*pages))); - if (!pages) { - kvfree(bv); - return -ENOMEM; - } - } - - saved_len = count; - - while (count && npages < max_pages) { - rc = iov_iter_get_pages2(iter, pages, count, max_pages, &start); - if (rc < 0) { - cifs_dbg(VFS, "Couldn't get user pages (rc=%zd)\n", rc); - break; - } - - if (rc > count) { - cifs_dbg(VFS, "get pages rc=%zd more than %zu\n", rc, - count); - break; - } - - count -= rc; - rc += start; - cur_npages = DIV_ROUND_UP(rc, PAGE_SIZE); - - if (npages + cur_npages > max_pages) { - cifs_dbg(VFS, "out of vec array capacity (%u vs %u)\n", - npages + cur_npages, max_pages); - break; - } - - for (i = 0; i < cur_npages; i++) { - len = rc > PAGE_SIZE ? PAGE_SIZE : rc; - bv[npages + i].bv_page = pages[i]; - bv[npages + i].bv_offset = start; - bv[npages + i].bv_len = len - start; - rc -= len; - start = 0; - } - - npages += cur_npages; - } - - kvfree(pages); - ctx->bv = bv; - ctx->len = saved_len - count; - ctx->npages = npages; - iov_iter_bvec(&ctx->iter, rw, ctx->bv, npages, ctx->len); - return 0; -} - -/** - * cifs_alloc_hash - allocate hash and hash context together - * @name: The name of the crypto hash algo - * @sdesc: SHASH descriptor where to put the pointer to the hash TFM - * - * The caller has to make sure @sdesc is initialized to either NULL or - * a valid context. It can be freed via cifs_free_hash(). - */ -int -cifs_alloc_hash(const char *name, struct shash_desc **sdesc) -{ - int rc = 0; - struct crypto_shash *alg = NULL; - - if (*sdesc) - return 0; - - alg = crypto_alloc_shash(name, 0, 0); - if (IS_ERR(alg)) { - cifs_dbg(VFS, "Could not allocate shash TFM '%s'\n", name); - rc = PTR_ERR(alg); - *sdesc = NULL; - return rc; - } - - *sdesc = kmalloc(sizeof(struct shash_desc) + crypto_shash_descsize(alg), GFP_KERNEL); - if (*sdesc == NULL) { - cifs_dbg(VFS, "no memory left to allocate shash TFM '%s'\n", name); - crypto_free_shash(alg); - return -ENOMEM; - } - - (*sdesc)->tfm = alg; - return 0; -} - -/** - * cifs_free_hash - free hash and hash context together - * @sdesc: Where to find the pointer to the hash TFM - * - * Freeing a NULL descriptor is safe. - */ -void -cifs_free_hash(struct shash_desc **sdesc) -{ - if (unlikely(!sdesc) || !*sdesc) - return; - - if ((*sdesc)->tfm) { - crypto_free_shash((*sdesc)->tfm); - (*sdesc)->tfm = NULL; - } - - kfree_sensitive(*sdesc); - *sdesc = NULL; -} - -/** - * rqst_page_get_length - obtain the length and offset for a page in smb_rqst - * @rqst: The request descriptor - * @page: The index of the page to query - * @len: Where to store the length for this page: - * @offset: Where to store the offset for this page - */ -void rqst_page_get_length(const struct smb_rqst *rqst, unsigned int page, - unsigned int *len, unsigned int *offset) -{ - *len = rqst->rq_pagesz; - *offset = (page == 0) ? rqst->rq_offset : 0; - - if (rqst->rq_npages == 1 || page == rqst->rq_npages-1) - *len = rqst->rq_tailsz; - else if (page == 0) - *len = rqst->rq_pagesz - rqst->rq_offset; -} - -void extract_unc_hostname(const char *unc, const char **h, size_t *len) -{ - const char *end; - - /* skip initial slashes */ - while (*unc && (*unc == '\\' || *unc == '/')) - unc++; - - end = unc; - - while (*end && !(*end == '\\' || *end == '/')) - end++; - - *h = unc; - *len = end - unc; -} - -/** - * copy_path_name - copy src path to dst, possibly truncating - * @dst: The destination buffer - * @src: The source name - * - * returns number of bytes written (including trailing nul) - */ -int copy_path_name(char *dst, const char *src) -{ - int name_len; - - /* - * PATH_MAX includes nul, so if strlen(src) >= PATH_MAX it - * will truncate and strlen(dst) will be PATH_MAX-1 - */ - name_len = strscpy(dst, src, PATH_MAX); - if (WARN_ON_ONCE(name_len < 0)) - name_len = PATH_MAX-1; - - /* we count the trailing nul */ - name_len++; - return name_len; -} - -struct super_cb_data { - void *data; - struct super_block *sb; -}; - -static void tcp_super_cb(struct super_block *sb, void *arg) -{ - struct super_cb_data *sd = arg; - struct TCP_Server_Info *server = sd->data; - struct cifs_sb_info *cifs_sb; - struct cifs_tcon *tcon; - - if (sd->sb) - return; - - cifs_sb = CIFS_SB(sb); - tcon = cifs_sb_master_tcon(cifs_sb); - if (tcon->ses->server == server) - sd->sb = sb; -} - -static struct super_block *__cifs_get_super(void (*f)(struct super_block *, void *), - void *data) -{ - struct super_cb_data sd = { - .data = data, - .sb = NULL, - }; - struct file_system_type **fs_type = (struct file_system_type *[]) { - &cifs_fs_type, &smb3_fs_type, NULL, - }; - - for (; *fs_type; fs_type++) { - iterate_supers_type(*fs_type, f, &sd); - if (sd.sb) { - /* - * Grab an active reference in order to prevent automounts (DFS links) - * of expiring and then freeing up our cifs superblock pointer while - * we're doing failover. - */ - cifs_sb_active(sd.sb); - return sd.sb; - } - } - return ERR_PTR(-EINVAL); -} - -static void __cifs_put_super(struct super_block *sb) -{ - if (!IS_ERR_OR_NULL(sb)) - cifs_sb_deactive(sb); -} - -struct super_block *cifs_get_tcp_super(struct TCP_Server_Info *server) -{ - return __cifs_get_super(tcp_super_cb, server); -} - -void cifs_put_tcp_super(struct super_block *sb) -{ - __cifs_put_super(sb); -} - -#ifdef CONFIG_CIFS_DFS_UPCALL -int match_target_ip(struct TCP_Server_Info *server, - const char *share, size_t share_len, - bool *result) -{ - int rc; - char *target, *tip = NULL; - struct sockaddr tipaddr; - - *result = false; - - target = kzalloc(share_len + 3, GFP_KERNEL); - if (!target) { - rc = -ENOMEM; - goto out; - } - - scnprintf(target, share_len + 3, "\\\\%.*s", (int)share_len, share); - - cifs_dbg(FYI, "%s: target name: %s\n", __func__, target + 2); - - rc = dns_resolve_server_name_to_ip(target, &tip, NULL); - if (rc < 0) - goto out; - - cifs_dbg(FYI, "%s: target ip: %s\n", __func__, tip); - - if (!cifs_convert_address(&tipaddr, tip, strlen(tip))) { - cifs_dbg(VFS, "%s: failed to convert target ip address\n", - __func__); - rc = -EINVAL; - goto out; - } - - *result = cifs_match_ipaddr((struct sockaddr *)&server->dstaddr, - &tipaddr); - cifs_dbg(FYI, "%s: ip addresses match: %u\n", __func__, *result); - rc = 0; - -out: - kfree(target); - kfree(tip); - - return rc; -} - -int cifs_update_super_prepath(struct cifs_sb_info *cifs_sb, char *prefix) -{ - kfree(cifs_sb->prepath); - - if (prefix && *prefix) { - cifs_sb->prepath = cifs_sanitize_prepath(prefix, GFP_ATOMIC); - if (!cifs_sb->prepath) - return -ENOMEM; - - convert_delimiter(cifs_sb->prepath, CIFS_DIR_SEP(cifs_sb)); - } else - cifs_sb->prepath = NULL; - - cifs_sb->mnt_cifs_flags |= CIFS_MOUNT_USE_PREFIX_PATH; - return 0; -} - -/* - * Handle weird Windows SMB server behaviour. It responds with - * STATUS_OBJECT_NAME_INVALID code to SMB2 QUERY_INFO request for - * "\\\" DFS reference, where contains - * non-ASCII unicode symbols. - */ -int cifs_inval_name_dfs_link_error(const unsigned int xid, - struct cifs_tcon *tcon, - struct cifs_sb_info *cifs_sb, - const char *full_path, - bool *islink) -{ - struct cifs_ses *ses = tcon->ses; - size_t len; - char *path; - char *ref_path; - - *islink = false; - - /* - * Fast path - skip check when @full_path doesn't have a prefix path to - * look up or tcon is not DFS. - */ - if (strlen(full_path) < 2 || !cifs_sb || - (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_NO_DFS) || - !is_tcon_dfs(tcon) || !ses->server->origin_fullpath) - return 0; - - /* - * Slow path - tcon is DFS and @full_path has prefix path, so attempt - * to get a referral to figure out whether it is an DFS link. - */ - len = strnlen(tcon->tree_name, MAX_TREE_SIZE + 1) + strlen(full_path) + 1; - path = kmalloc(len, GFP_KERNEL); - if (!path) - return -ENOMEM; - - scnprintf(path, len, "%s%s", tcon->tree_name, full_path); - ref_path = dfs_cache_canonical_path(path + 1, cifs_sb->local_nls, - cifs_remap(cifs_sb)); - kfree(path); - - if (IS_ERR(ref_path)) { - if (PTR_ERR(ref_path) != -EINVAL) - return PTR_ERR(ref_path); - } else { - struct dfs_info3_param *refs = NULL; - int num_refs = 0; - - /* - * XXX: we are not using dfs_cache_find() here because we might - * end filling all the DFS cache and thus potentially - * removing cached DFS targets that the client would eventually - * need during failover. - */ - if (ses->server->ops->get_dfs_refer && - !ses->server->ops->get_dfs_refer(xid, ses, ref_path, &refs, - &num_refs, cifs_sb->local_nls, - cifs_remap(cifs_sb))) - *islink = refs[0].server_type == DFS_TYPE_LINK; - free_dfs_info_array(refs, num_refs); - kfree(ref_path); - } - return 0; -} -#endif - -int cifs_wait_for_server_reconnect(struct TCP_Server_Info *server, bool retry) -{ - int timeout = 10; - int rc; - - spin_lock(&server->srv_lock); - if (server->tcpStatus != CifsNeedReconnect) { - spin_unlock(&server->srv_lock); - return 0; - } - timeout *= server->nr_targets; - spin_unlock(&server->srv_lock); - - /* - * Give demultiplex thread up to 10 seconds to each target available for - * reconnect -- should be greater than cifs socket timeout which is 7 - * seconds. - * - * On "soft" mounts we wait once. Hard mounts keep retrying until - * process is killed or server comes back on-line. - */ - do { - rc = wait_event_interruptible_timeout(server->response_q, - (server->tcpStatus != CifsNeedReconnect), - timeout * HZ); - if (rc < 0) { - cifs_dbg(FYI, "%s: aborting reconnect due to received signal\n", - __func__); - return -ERESTARTSYS; - } - - /* are we still trying to reconnect? */ - spin_lock(&server->srv_lock); - if (server->tcpStatus != CifsNeedReconnect) { - spin_unlock(&server->srv_lock); - return 0; - } - spin_unlock(&server->srv_lock); - } while (retry); - - cifs_dbg(FYI, "%s: gave up waiting on reconnect\n", __func__); - return -EHOSTDOWN; -} diff --git a/fs/cifs/netlink.c b/fs/cifs/netlink.c deleted file mode 100644 index 147d9409252cd..0000000000000 --- a/fs/cifs/netlink.c +++ /dev/null @@ -1,90 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0 -/* - * Netlink routines for CIFS - * - * Copyright (c) 2020 Samuel Cabrero - */ - -#include -#include - -#include "netlink.h" -#include "cifsglob.h" -#include "cifs_debug.h" -#include "cifs_swn.h" - -static const struct nla_policy cifs_genl_policy[CIFS_GENL_ATTR_MAX + 1] = { - [CIFS_GENL_ATTR_SWN_REGISTRATION_ID] = { .type = NLA_U32 }, - [CIFS_GENL_ATTR_SWN_NET_NAME] = { .type = NLA_STRING }, - [CIFS_GENL_ATTR_SWN_SHARE_NAME] = { .type = NLA_STRING }, - [CIFS_GENL_ATTR_SWN_IP] = { .len = sizeof(struct sockaddr_storage) }, - [CIFS_GENL_ATTR_SWN_NET_NAME_NOTIFY] = { .type = NLA_FLAG }, - [CIFS_GENL_ATTR_SWN_SHARE_NAME_NOTIFY] = { .type = NLA_FLAG }, - [CIFS_GENL_ATTR_SWN_IP_NOTIFY] = { .type = NLA_FLAG }, - [CIFS_GENL_ATTR_SWN_KRB_AUTH] = { .type = NLA_FLAG }, - [CIFS_GENL_ATTR_SWN_USER_NAME] = { .type = NLA_STRING }, - [CIFS_GENL_ATTR_SWN_PASSWORD] = { .type = NLA_STRING }, - [CIFS_GENL_ATTR_SWN_DOMAIN_NAME] = { .type = NLA_STRING }, - [CIFS_GENL_ATTR_SWN_NOTIFICATION_TYPE] = { .type = NLA_U32 }, - [CIFS_GENL_ATTR_SWN_RESOURCE_STATE] = { .type = NLA_U32 }, - [CIFS_GENL_ATTR_SWN_RESOURCE_NAME] = { .type = NLA_STRING}, -}; - -static const struct genl_ops cifs_genl_ops[] = { - { - .cmd = CIFS_GENL_CMD_SWN_NOTIFY, - .validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP, - .doit = cifs_swn_notify, - }, -}; - -static const struct genl_multicast_group cifs_genl_mcgrps[] = { - [CIFS_GENL_MCGRP_SWN] = { .name = CIFS_GENL_MCGRP_SWN_NAME }, -}; - -struct genl_family cifs_genl_family = { - .name = CIFS_GENL_NAME, - .version = CIFS_GENL_VERSION, - .hdrsize = 0, - .maxattr = CIFS_GENL_ATTR_MAX, - .module = THIS_MODULE, - .policy = cifs_genl_policy, - .ops = cifs_genl_ops, - .n_ops = ARRAY_SIZE(cifs_genl_ops), - .resv_start_op = CIFS_GENL_CMD_SWN_NOTIFY + 1, - .mcgrps = cifs_genl_mcgrps, - .n_mcgrps = ARRAY_SIZE(cifs_genl_mcgrps), -}; - -/** - * cifs_genl_init - Register generic netlink family - * - * Return zero if initialized successfully, otherwise non-zero. - */ -int cifs_genl_init(void) -{ - int ret; - - ret = genl_register_family(&cifs_genl_family); - if (ret < 0) { - cifs_dbg(VFS, "%s: failed to register netlink family\n", - __func__); - return ret; - } - - return 0; -} - -/** - * cifs_genl_exit - Unregister generic netlink family - */ -void cifs_genl_exit(void) -{ - int ret; - - ret = genl_unregister_family(&cifs_genl_family); - if (ret < 0) { - cifs_dbg(VFS, "%s: failed to unregister netlink family\n", - __func__); - } -} diff --git a/fs/cifs/netlink.h b/fs/cifs/netlink.h deleted file mode 100644 index e2fa8ed24c546..0000000000000 --- a/fs/cifs/netlink.h +++ /dev/null @@ -1,16 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0 */ -/* - * Netlink routines for CIFS - * - * Copyright (c) 2020 Samuel Cabrero - */ - -#ifndef _CIFS_NETLINK_H -#define _CIFS_NETLINK_H - -extern struct genl_family cifs_genl_family; - -extern int cifs_genl_init(void); -extern void cifs_genl_exit(void); - -#endif /* _CIFS_NETLINK_H */ diff --git a/fs/cifs/netmisc.c b/fs/cifs/netmisc.c deleted file mode 100644 index 1b52e6ac431cb..0000000000000 --- a/fs/cifs/netmisc.c +++ /dev/null @@ -1,1021 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-or-later -/* - * - * Copyright (c) International Business Machines Corp., 2002,2008 - * Author(s): Steve French (sfrench@us.ibm.com) - * - * Error mapping routines from Samba libsmb/errormap.c - * Copyright (C) Andrew Tridgell 2001 - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include "cifsfs.h" -#include "cifspdu.h" -#include "cifsglob.h" -#include "cifsproto.h" -#include "smberr.h" -#include "cifs_debug.h" -#include "nterr.h" - -struct smb_to_posix_error { - __u16 smb_err; - int posix_code; -}; - -static const struct smb_to_posix_error mapping_table_ERRDOS[] = { - {ERRbadfunc, -EINVAL}, - {ERRbadfile, -ENOENT}, - {ERRbadpath, -ENOTDIR}, - {ERRnofids, -EMFILE}, - {ERRnoaccess, -EACCES}, - {ERRbadfid, -EBADF}, - {ERRbadmcb, -EIO}, - {ERRnomem, -EREMOTEIO}, - {ERRbadmem, -EFAULT}, - {ERRbadenv, -EFAULT}, - {ERRbadformat, -EINVAL}, - {ERRbadaccess, -EACCES}, - {ERRbaddata, -EIO}, - {ERRbaddrive, -ENXIO}, - {ERRremcd, -EACCES}, - {ERRdiffdevice, -EXDEV}, - {ERRnofiles, -ENOENT}, - {ERRwriteprot, -EROFS}, - {ERRbadshare, -EBUSY}, - {ERRlock, -EACCES}, - {ERRunsup, -EINVAL}, - {ERRnosuchshare, -ENXIO}, - {ERRfilexists, -EEXIST}, - {ERRinvparm, -EINVAL}, - {ERRdiskfull, -ENOSPC}, - {ERRinvname, -ENOENT}, - {ERRinvlevel, -EOPNOTSUPP}, - {ERRdirnotempty, -ENOTEMPTY}, - {ERRnotlocked, -ENOLCK}, - {ERRcancelviolation, -ENOLCK}, - {ERRalreadyexists, -EEXIST}, - {ERRmoredata, -EOVERFLOW}, - {ERReasnotsupported, -EOPNOTSUPP}, - {ErrQuota, -EDQUOT}, - {ErrNotALink, -ENOLINK}, - {ERRnetlogonNotStarted, -ENOPROTOOPT}, - {ERRsymlink, -EOPNOTSUPP}, - {ErrTooManyLinks, -EMLINK}, - {0, 0} -}; - -static const struct smb_to_posix_error mapping_table_ERRSRV[] = { - {ERRerror, -EIO}, - {ERRbadpw, -EACCES}, /* was EPERM */ - {ERRbadtype, -EREMOTE}, - {ERRaccess, -EACCES}, - {ERRinvtid, -ENXIO}, - {ERRinvnetname, -ENXIO}, - {ERRinvdevice, -ENXIO}, - {ERRqfull, -ENOSPC}, - {ERRqtoobig, -ENOSPC}, - {ERRqeof, -EIO}, - {ERRinvpfid, -EBADF}, - {ERRsmbcmd, -EBADRQC}, - {ERRsrverror, -EIO}, - {ERRbadBID, -EIO}, - {ERRfilespecs, -EINVAL}, - {ERRbadLink, -EIO}, - {ERRbadpermits, -EINVAL}, - {ERRbadPID, -ESRCH}, - {ERRsetattrmode, -EINVAL}, - {ERRpaused, -EHOSTDOWN}, - {ERRmsgoff, -EHOSTDOWN}, - {ERRnoroom, -ENOSPC}, - {ERRrmuns, -EUSERS}, - {ERRtimeout, -ETIME}, - {ERRnoresource, -EREMOTEIO}, - {ERRtoomanyuids, -EUSERS}, - {ERRbaduid, -EACCES}, - {ERRusempx, -EIO}, - {ERRusestd, -EIO}, - {ERR_NOTIFY_ENUM_DIR, -ENOBUFS}, - {ERRnoSuchUser, -EACCES}, -/* {ERRaccountexpired, -EACCES}, - {ERRbadclient, -EACCES}, - {ERRbadLogonTime, -EACCES}, - {ERRpasswordExpired, -EACCES},*/ - {ERRaccountexpired, -EKEYEXPIRED}, - {ERRbadclient, -EACCES}, - {ERRbadLogonTime, -EACCES}, - {ERRpasswordExpired, -EKEYEXPIRED}, - - {ERRnosupport, -EINVAL}, - {0, 0} -}; - -/* - * Convert a string containing text IPv4 or IPv6 address to binary form. - * - * Returns 0 on failure. - */ -static int -cifs_inet_pton(const int address_family, const char *cp, int len, void *dst) -{ - int ret = 0; - - /* calculate length by finding first slash or NULL */ - if (address_family == AF_INET) - ret = in4_pton(cp, len, dst, '\\', NULL); - else if (address_family == AF_INET6) - ret = in6_pton(cp, len, dst , '\\', NULL); - - cifs_dbg(NOISY, "address conversion returned %d for %*.*s\n", - ret, len, len, cp); - if (ret > 0) - ret = 1; - return ret; -} - -/* - * Try to convert a string to an IPv4 address and then attempt to convert - * it to an IPv6 address if that fails. Set the family field if either - * succeeds. If it's an IPv6 address and it has a '%' sign in it, try to - * treat the part following it as a numeric sin6_scope_id. - * - * Returns 0 on failure. - */ -int -cifs_convert_address(struct sockaddr *dst, const char *src, int len) -{ - int rc, alen, slen; - const char *pct; - char scope_id[13]; - struct sockaddr_in *s4 = (struct sockaddr_in *) dst; - struct sockaddr_in6 *s6 = (struct sockaddr_in6 *) dst; - - /* IPv4 address */ - if (cifs_inet_pton(AF_INET, src, len, &s4->sin_addr.s_addr)) { - s4->sin_family = AF_INET; - return 1; - } - - /* attempt to exclude the scope ID from the address part */ - pct = memchr(src, '%', len); - alen = pct ? pct - src : len; - - rc = cifs_inet_pton(AF_INET6, src, alen, &s6->sin6_addr.s6_addr); - if (!rc) - return rc; - - s6->sin6_family = AF_INET6; - if (pct) { - /* grab the scope ID */ - slen = len - (alen + 1); - if (slen <= 0 || slen > 12) - return 0; - memcpy(scope_id, pct + 1, slen); - scope_id[slen] = '\0'; - - rc = kstrtouint(scope_id, 0, &s6->sin6_scope_id); - rc = (rc == 0) ? 1 : 0; - } - - return rc; -} - -void -cifs_set_port(struct sockaddr *addr, const unsigned short int port) -{ - switch (addr->sa_family) { - case AF_INET: - ((struct sockaddr_in *)addr)->sin_port = htons(port); - break; - case AF_INET6: - ((struct sockaddr_in6 *)addr)->sin6_port = htons(port); - break; - } -} - -/***************************************************************************** -convert a NT status code to a dos class/code - *****************************************************************************/ -/* NT status -> dos error map */ -static const struct { - __u8 dos_class; - __u16 dos_code; - __u32 ntstatus; -} ntstatus_to_dos_map[] = { - { - ERRDOS, ERRgeneral, NT_STATUS_UNSUCCESSFUL}, { - ERRDOS, ERRbadfunc, NT_STATUS_NOT_IMPLEMENTED}, { - ERRDOS, ERRinvlevel, NT_STATUS_INVALID_INFO_CLASS}, { - ERRDOS, 24, NT_STATUS_INFO_LENGTH_MISMATCH}, { - ERRHRD, ERRgeneral, NT_STATUS_ACCESS_VIOLATION}, { - ERRHRD, ERRgeneral, NT_STATUS_IN_PAGE_ERROR}, { - ERRHRD, ERRgeneral, NT_STATUS_PAGEFILE_QUOTA}, { - ERRDOS, ERRbadfid, NT_STATUS_INVALID_HANDLE}, { - ERRHRD, ERRgeneral, NT_STATUS_BAD_INITIAL_STACK}, { - ERRDOS, 193, NT_STATUS_BAD_INITIAL_PC}, { - ERRDOS, 87, NT_STATUS_INVALID_CID}, { - ERRHRD, ERRgeneral, NT_STATUS_TIMER_NOT_CANCELED}, { - ERRDOS, 87, NT_STATUS_INVALID_PARAMETER}, { - ERRDOS, ERRbadfile, NT_STATUS_NO_SUCH_DEVICE}, { - ERRDOS, ERRbadfile, NT_STATUS_NO_SUCH_FILE}, { - ERRDOS, ERRbadfunc, NT_STATUS_INVALID_DEVICE_REQUEST}, { - ERRDOS, 38, NT_STATUS_END_OF_FILE}, { - ERRDOS, 34, NT_STATUS_WRONG_VOLUME}, { - ERRDOS, 21, NT_STATUS_NO_MEDIA_IN_DEVICE}, { - ERRHRD, ERRgeneral, NT_STATUS_UNRECOGNIZED_MEDIA}, { - ERRDOS, 27, NT_STATUS_NONEXISTENT_SECTOR}, -/* { This NT error code was 'sqashed' - from NT_STATUS_MORE_PROCESSING_REQUIRED to NT_STATUS_OK - during the session setup } */ - { - ERRDOS, ERRnomem, NT_STATUS_NO_MEMORY}, { - ERRDOS, 487, NT_STATUS_CONFLICTING_ADDRESSES}, { - ERRDOS, 487, NT_STATUS_NOT_MAPPED_VIEW}, { - ERRDOS, 87, NT_STATUS_UNABLE_TO_FREE_VM}, { - ERRDOS, 87, NT_STATUS_UNABLE_TO_DELETE_SECTION}, { - ERRDOS, 2142, NT_STATUS_INVALID_SYSTEM_SERVICE}, { - ERRHRD, ERRgeneral, NT_STATUS_ILLEGAL_INSTRUCTION}, { - ERRDOS, ERRnoaccess, NT_STATUS_INVALID_LOCK_SEQUENCE}, { - ERRDOS, ERRnoaccess, NT_STATUS_INVALID_VIEW_SIZE}, { - ERRDOS, 193, NT_STATUS_INVALID_FILE_FOR_SECTION}, { - ERRDOS, ERRnoaccess, NT_STATUS_ALREADY_COMMITTED}, -/* { This NT error code was 'sqashed' - from NT_STATUS_ACCESS_DENIED to NT_STATUS_TRUSTED_RELATIONSHIP_FAILURE - during the session setup } */ - { - ERRDOS, ERRnoaccess, NT_STATUS_ACCESS_DENIED}, { - ERRDOS, 111, NT_STATUS_BUFFER_TOO_SMALL}, { - ERRDOS, ERRbadfid, NT_STATUS_OBJECT_TYPE_MISMATCH}, { - ERRHRD, ERRgeneral, NT_STATUS_NONCONTINUABLE_EXCEPTION}, { - ERRHRD, ERRgeneral, NT_STATUS_INVALID_DISPOSITION}, { - ERRHRD, ERRgeneral, NT_STATUS_UNWIND}, { - ERRHRD, ERRgeneral, NT_STATUS_BAD_STACK}, { - ERRHRD, ERRgeneral, NT_STATUS_INVALID_UNWIND_TARGET}, { - ERRDOS, 158, NT_STATUS_NOT_LOCKED}, { - ERRHRD, ERRgeneral, NT_STATUS_PARITY_ERROR}, { - ERRDOS, 487, NT_STATUS_UNABLE_TO_DECOMMIT_VM}, { - ERRDOS, 487, NT_STATUS_NOT_COMMITTED}, { - ERRHRD, ERRgeneral, NT_STATUS_INVALID_PORT_ATTRIBUTES}, { - ERRHRD, ERRgeneral, NT_STATUS_PORT_MESSAGE_TOO_LONG}, { - ERRDOS, 87, NT_STATUS_INVALID_PARAMETER_MIX}, { - ERRHRD, ERRgeneral, NT_STATUS_INVALID_QUOTA_LOWER}, { - ERRHRD, ERRgeneral, NT_STATUS_DISK_CORRUPT_ERROR}, { - /* mapping changed since shell does lookup on * expects FileNotFound */ - ERRDOS, ERRbadfile, NT_STATUS_OBJECT_NAME_INVALID}, { - ERRDOS, ERRbadfile, NT_STATUS_OBJECT_NAME_NOT_FOUND}, { - ERRDOS, ERRalreadyexists, NT_STATUS_OBJECT_NAME_COLLISION}, { - ERRHRD, ERRgeneral, NT_STATUS_HANDLE_NOT_WAITABLE}, { - ERRDOS, ERRbadfid, NT_STATUS_PORT_DISCONNECTED}, { - ERRHRD, ERRgeneral, NT_STATUS_DEVICE_ALREADY_ATTACHED}, { - ERRDOS, 161, NT_STATUS_OBJECT_PATH_INVALID}, { - ERRDOS, ERRbadpath, NT_STATUS_OBJECT_PATH_NOT_FOUND}, { - ERRDOS, 161, NT_STATUS_OBJECT_PATH_SYNTAX_BAD}, { - ERRHRD, ERRgeneral, NT_STATUS_DATA_OVERRUN}, { - ERRHRD, ERRgeneral, NT_STATUS_DATA_LATE_ERROR}, { - ERRDOS, 23, NT_STATUS_DATA_ERROR}, { - ERRDOS, 23, NT_STATUS_CRC_ERROR}, { - ERRDOS, ERRnomem, NT_STATUS_SECTION_TOO_BIG}, { - ERRDOS, ERRnoaccess, NT_STATUS_PORT_CONNECTION_REFUSED}, { - ERRDOS, ERRbadfid, NT_STATUS_INVALID_PORT_HANDLE}, { - ERRDOS, ERRbadshare, NT_STATUS_SHARING_VIOLATION}, { - ERRHRD, ERRgeneral, NT_STATUS_QUOTA_EXCEEDED}, { - ERRDOS, 87, NT_STATUS_INVALID_PAGE_PROTECTION}, { - ERRDOS, 288, NT_STATUS_MUTANT_NOT_OWNED}, { - ERRDOS, 298, NT_STATUS_SEMAPHORE_LIMIT_EXCEEDED}, { - ERRDOS, 87, NT_STATUS_PORT_ALREADY_SET}, { - ERRDOS, 87, NT_STATUS_SECTION_NOT_IMAGE}, { - ERRDOS, 156, NT_STATUS_SUSPEND_COUNT_EXCEEDED}, { - ERRDOS, ERRnoaccess, NT_STATUS_THREAD_IS_TERMINATING}, { - ERRDOS, 87, NT_STATUS_BAD_WORKING_SET_LIMIT}, { - ERRDOS, 87, NT_STATUS_INCOMPATIBLE_FILE_MAP}, { - ERRDOS, 87, NT_STATUS_SECTION_PROTECTION}, { - ERRDOS, ERReasnotsupported, NT_STATUS_EAS_NOT_SUPPORTED}, { - ERRDOS, 255, NT_STATUS_EA_TOO_LARGE}, { - ERRHRD, ERRgeneral, NT_STATUS_NONEXISTENT_EA_ENTRY}, { - ERRHRD, ERRgeneral, NT_STATUS_NO_EAS_ON_FILE}, { - ERRHRD, ERRgeneral, NT_STATUS_EA_CORRUPT_ERROR}, { - ERRDOS, ERRlock, NT_STATUS_FILE_LOCK_CONFLICT}, { - ERRDOS, ERRlock, NT_STATUS_LOCK_NOT_GRANTED}, { - ERRDOS, ERRbadfile, NT_STATUS_DELETE_PENDING}, { - ERRDOS, ERRunsup, NT_STATUS_CTL_FILE_NOT_SUPPORTED}, { - ERRHRD, ERRgeneral, NT_STATUS_UNKNOWN_REVISION}, { - ERRHRD, ERRgeneral, NT_STATUS_REVISION_MISMATCH}, { - ERRHRD, ERRgeneral, NT_STATUS_INVALID_OWNER}, { - ERRHRD, ERRgeneral, NT_STATUS_INVALID_PRIMARY_GROUP}, { - ERRHRD, ERRgeneral, NT_STATUS_NO_IMPERSONATION_TOKEN}, { - ERRHRD, ERRgeneral, NT_STATUS_CANT_DISABLE_MANDATORY}, { - ERRDOS, 2215, NT_STATUS_NO_LOGON_SERVERS}, { - ERRHRD, ERRgeneral, NT_STATUS_NO_SUCH_LOGON_SESSION}, { - ERRHRD, ERRgeneral, NT_STATUS_NO_SUCH_PRIVILEGE}, { - ERRDOS, ERRnoaccess, NT_STATUS_PRIVILEGE_NOT_HELD}, { - ERRHRD, ERRgeneral, NT_STATUS_INVALID_ACCOUNT_NAME}, { - ERRHRD, ERRgeneral, NT_STATUS_USER_EXISTS}, -/* { This NT error code was 'sqashed' - from NT_STATUS_NO_SUCH_USER to NT_STATUS_LOGON_FAILURE - during the session setup } */ - { - ERRDOS, ERRnoaccess, NT_STATUS_NO_SUCH_USER}, { /* could map to 2238 */ - ERRHRD, ERRgeneral, NT_STATUS_GROUP_EXISTS}, { - ERRHRD, ERRgeneral, NT_STATUS_NO_SUCH_GROUP}, { - ERRHRD, ERRgeneral, NT_STATUS_MEMBER_IN_GROUP}, { - ERRHRD, ERRgeneral, NT_STATUS_MEMBER_NOT_IN_GROUP}, { - ERRHRD, ERRgeneral, NT_STATUS_LAST_ADMIN}, -/* { This NT error code was 'sqashed' - from NT_STATUS_WRONG_PASSWORD to NT_STATUS_LOGON_FAILURE - during the session setup } */ - { - ERRSRV, ERRbadpw, NT_STATUS_WRONG_PASSWORD}, { - ERRHRD, ERRgeneral, NT_STATUS_ILL_FORMED_PASSWORD}, { - ERRHRD, ERRgeneral, NT_STATUS_PASSWORD_RESTRICTION}, { - ERRDOS, ERRnoaccess, NT_STATUS_LOGON_FAILURE}, { - ERRHRD, ERRgeneral, NT_STATUS_ACCOUNT_RESTRICTION}, { - ERRSRV, ERRbadLogonTime, NT_STATUS_INVALID_LOGON_HOURS}, { - ERRSRV, ERRbadclient, NT_STATUS_INVALID_WORKSTATION}, { - ERRSRV, ERRpasswordExpired, NT_STATUS_PASSWORD_EXPIRED}, { - ERRSRV, ERRaccountexpired, NT_STATUS_ACCOUNT_DISABLED}, { - ERRHRD, ERRgeneral, NT_STATUS_NONE_MAPPED}, { - ERRHRD, ERRgeneral, NT_STATUS_TOO_MANY_LUIDS_REQUESTED}, { - ERRHRD, ERRgeneral, NT_STATUS_LUIDS_EXHAUSTED}, { - ERRHRD, ERRgeneral, NT_STATUS_INVALID_SUB_AUTHORITY}, { - ERRHRD, ERRgeneral, NT_STATUS_INVALID_ACL}, { - ERRHRD, ERRgeneral, NT_STATUS_INVALID_SID}, { - ERRHRD, ERRgeneral, NT_STATUS_INVALID_SECURITY_DESCR}, { - ERRDOS, 127, NT_STATUS_PROCEDURE_NOT_FOUND}, { - ERRDOS, 193, NT_STATUS_INVALID_IMAGE_FORMAT}, { - ERRHRD, ERRgeneral, NT_STATUS_NO_TOKEN}, { - ERRHRD, ERRgeneral, NT_STATUS_BAD_INHERITANCE_ACL}, { - ERRDOS, 158, NT_STATUS_RANGE_NOT_LOCKED}, { - ERRDOS, 112, NT_STATUS_DISK_FULL}, { - ERRHRD, ERRgeneral, NT_STATUS_SERVER_DISABLED}, { - ERRHRD, ERRgeneral, NT_STATUS_SERVER_NOT_DISABLED}, { - ERRDOS, 68, NT_STATUS_TOO_MANY_GUIDS_REQUESTED}, { - ERRDOS, 259, NT_STATUS_GUIDS_EXHAUSTED}, { - ERRHRD, ERRgeneral, NT_STATUS_INVALID_ID_AUTHORITY}, { - ERRDOS, 259, NT_STATUS_AGENTS_EXHAUSTED}, { - ERRDOS, 154, NT_STATUS_INVALID_VOLUME_LABEL}, { - ERRDOS, 14, NT_STATUS_SECTION_NOT_EXTENDED}, { - ERRDOS, 487, NT_STATUS_NOT_MAPPED_DATA}, { - ERRHRD, ERRgeneral, NT_STATUS_RESOURCE_DATA_NOT_FOUND}, { - ERRHRD, ERRgeneral, NT_STATUS_RESOURCE_TYPE_NOT_FOUND}, { - ERRHRD, ERRgeneral, NT_STATUS_RESOURCE_NAME_NOT_FOUND}, { - ERRHRD, ERRgeneral, NT_STATUS_ARRAY_BOUNDS_EXCEEDED}, { - ERRHRD, ERRgeneral, NT_STATUS_FLOAT_DENORMAL_OPERAND}, { - ERRHRD, ERRgeneral, NT_STATUS_FLOAT_DIVIDE_BY_ZERO}, { - ERRHRD, ERRgeneral, NT_STATUS_FLOAT_INEXACT_RESULT}, { - ERRHRD, ERRgeneral, NT_STATUS_FLOAT_INVALID_OPERATION}, { - ERRHRD, ERRgeneral, NT_STATUS_FLOAT_OVERFLOW}, { - ERRHRD, ERRgeneral, NT_STATUS_FLOAT_STACK_CHECK}, { - ERRHRD, ERRgeneral, NT_STATUS_FLOAT_UNDERFLOW}, { - ERRHRD, ERRgeneral, NT_STATUS_INTEGER_DIVIDE_BY_ZERO}, { - ERRDOS, 534, NT_STATUS_INTEGER_OVERFLOW}, { - ERRHRD, ERRgeneral, NT_STATUS_PRIVILEGED_INSTRUCTION}, { - ERRDOS, ERRnomem, NT_STATUS_TOO_MANY_PAGING_FILES}, { - ERRHRD, ERRgeneral, NT_STATUS_FILE_INVALID}, { - ERRHRD, ERRgeneral, NT_STATUS_ALLOTTED_SPACE_EXCEEDED}, -/* { This NT error code was 'sqashed' - from NT_STATUS_INSUFFICIENT_RESOURCES to - NT_STATUS_INSUFF_SERVER_RESOURCES during the session setup } */ - { - ERRDOS, ERRnoresource, NT_STATUS_INSUFFICIENT_RESOURCES}, { - ERRDOS, ERRbadpath, NT_STATUS_DFS_EXIT_PATH_FOUND}, { - ERRDOS, 23, NT_STATUS_DEVICE_DATA_ERROR}, { - ERRHRD, ERRgeneral, NT_STATUS_DEVICE_NOT_CONNECTED}, { - ERRDOS, 21, NT_STATUS_DEVICE_POWER_FAILURE}, { - ERRDOS, 487, NT_STATUS_FREE_VM_NOT_AT_BASE}, { - ERRDOS, 487, NT_STATUS_MEMORY_NOT_ALLOCATED}, { - ERRHRD, ERRgeneral, NT_STATUS_WORKING_SET_QUOTA}, { - ERRDOS, 19, NT_STATUS_MEDIA_WRITE_PROTECTED}, { - ERRDOS, 21, NT_STATUS_DEVICE_NOT_READY}, { - ERRHRD, ERRgeneral, NT_STATUS_INVALID_GROUP_ATTRIBUTES}, { - ERRHRD, ERRgeneral, NT_STATUS_BAD_IMPERSONATION_LEVEL}, { - ERRHRD, ERRgeneral, NT_STATUS_CANT_OPEN_ANONYMOUS}, { - ERRHRD, ERRgeneral, NT_STATUS_BAD_VALIDATION_CLASS}, { - ERRHRD, ERRgeneral, NT_STATUS_BAD_TOKEN_TYPE}, { - ERRDOS, 87, NT_STATUS_BAD_MASTER_BOOT_RECORD}, { - ERRHRD, ERRgeneral, NT_STATUS_INSTRUCTION_MISALIGNMENT}, { - ERRDOS, ERRpipebusy, NT_STATUS_INSTANCE_NOT_AVAILABLE}, { - ERRDOS, ERRpipebusy, NT_STATUS_PIPE_NOT_AVAILABLE}, { - ERRDOS, ERRbadpipe, NT_STATUS_INVALID_PIPE_STATE}, { - ERRDOS, ERRpipebusy, NT_STATUS_PIPE_BUSY}, { - ERRDOS, ERRbadfunc, NT_STATUS_ILLEGAL_FUNCTION}, { - ERRDOS, ERRnotconnected, NT_STATUS_PIPE_DISCONNECTED}, { - ERRDOS, ERRpipeclosing, NT_STATUS_PIPE_CLOSING}, { - ERRHRD, ERRgeneral, NT_STATUS_PIPE_CONNECTED}, { - ERRHRD, ERRgeneral, NT_STATUS_PIPE_LISTENING}, { - ERRDOS, ERRbadpipe, NT_STATUS_INVALID_READ_MODE}, { - ERRDOS, 121, NT_STATUS_IO_TIMEOUT}, { - ERRDOS, 38, NT_STATUS_FILE_FORCED_CLOSED}, { - ERRHRD, ERRgeneral, NT_STATUS_PROFILING_NOT_STARTED}, { - ERRHRD, ERRgeneral, NT_STATUS_PROFILING_NOT_STOPPED}, { - ERRHRD, ERRgeneral, NT_STATUS_COULD_NOT_INTERPRET}, { - ERRDOS, ERRnoaccess, NT_STATUS_FILE_IS_A_DIRECTORY}, { - ERRDOS, ERRunsup, NT_STATUS_NOT_SUPPORTED}, { - ERRDOS, 51, NT_STATUS_REMOTE_NOT_LISTENING}, { - ERRDOS, 52, NT_STATUS_DUPLICATE_NAME}, { - ERRDOS, 53, NT_STATUS_BAD_NETWORK_PATH}, { - ERRDOS, 54, NT_STATUS_NETWORK_BUSY}, { - ERRDOS, 55, NT_STATUS_DEVICE_DOES_NOT_EXIST}, { - ERRDOS, 56, NT_STATUS_TOO_MANY_COMMANDS}, { - ERRDOS, 57, NT_STATUS_ADAPTER_HARDWARE_ERROR}, { - ERRDOS, 58, NT_STATUS_INVALID_NETWORK_RESPONSE}, { - ERRDOS, 59, NT_STATUS_UNEXPECTED_NETWORK_ERROR}, { - ERRDOS, 60, NT_STATUS_BAD_REMOTE_ADAPTER}, { - ERRDOS, 61, NT_STATUS_PRINT_QUEUE_FULL}, { - ERRDOS, 62, NT_STATUS_NO_SPOOL_SPACE}, { - ERRDOS, 63, NT_STATUS_PRINT_CANCELLED}, { - ERRDOS, 64, NT_STATUS_NETWORK_NAME_DELETED}, { - ERRDOS, 65, NT_STATUS_NETWORK_ACCESS_DENIED}, { - ERRDOS, 66, NT_STATUS_BAD_DEVICE_TYPE}, { - ERRDOS, ERRnosuchshare, NT_STATUS_BAD_NETWORK_NAME}, { - ERRDOS, 68, NT_STATUS_TOO_MANY_NAMES}, { - ERRDOS, 69, NT_STATUS_TOO_MANY_SESSIONS}, { - ERRDOS, 70, NT_STATUS_SHARING_PAUSED}, { - ERRDOS, 71, NT_STATUS_REQUEST_NOT_ACCEPTED}, { - ERRDOS, 72, NT_STATUS_REDIRECTOR_PAUSED}, { - ERRDOS, 88, NT_STATUS_NET_WRITE_FAULT}, { - ERRHRD, ERRgeneral, NT_STATUS_PROFILING_AT_LIMIT}, { - ERRDOS, ERRdiffdevice, NT_STATUS_NOT_SAME_DEVICE}, { - ERRDOS, ERRnoaccess, NT_STATUS_FILE_RENAMED}, { - ERRDOS, 240, NT_STATUS_VIRTUAL_CIRCUIT_CLOSED}, { - ERRHRD, ERRgeneral, NT_STATUS_NO_SECURITY_ON_OBJECT}, { - ERRHRD, ERRgeneral, NT_STATUS_CANT_WAIT}, { - ERRDOS, ERRpipeclosing, NT_STATUS_PIPE_EMPTY}, { - ERRHRD, ERRgeneral, NT_STATUS_CANT_ACCESS_DOMAIN_INFO}, { - ERRHRD, ERRgeneral, NT_STATUS_CANT_TERMINATE_SELF}, { - ERRHRD, ERRgeneral, NT_STATUS_INVALID_SERVER_STATE}, { - ERRHRD, ERRgeneral, NT_STATUS_INVALID_DOMAIN_STATE}, { - ERRHRD, ERRgeneral, NT_STATUS_INVALID_DOMAIN_ROLE}, { - ERRHRD, ERRgeneral, NT_STATUS_NO_SUCH_DOMAIN}, { - ERRHRD, ERRgeneral, NT_STATUS_DOMAIN_EXISTS}, { - ERRHRD, ERRgeneral, NT_STATUS_DOMAIN_LIMIT_EXCEEDED}, { - ERRDOS, 300, NT_STATUS_OPLOCK_NOT_GRANTED}, { - ERRDOS, 301, NT_STATUS_INVALID_OPLOCK_PROTOCOL}, { - ERRHRD, ERRgeneral, NT_STATUS_INTERNAL_DB_CORRUPTION}, { - ERRHRD, ERRgeneral, NT_STATUS_INTERNAL_ERROR}, { - ERRHRD, ERRgeneral, NT_STATUS_GENERIC_NOT_MAPPED}, { - ERRHRD, ERRgeneral, NT_STATUS_BAD_DESCRIPTOR_FORMAT}, { - ERRHRD, ERRgeneral, NT_STATUS_INVALID_USER_BUFFER}, { - ERRHRD, ERRgeneral, NT_STATUS_UNEXPECTED_IO_ERROR}, { - ERRHRD, ERRgeneral, NT_STATUS_UNEXPECTED_MM_CREATE_ERR}, { - ERRHRD, ERRgeneral, NT_STATUS_UNEXPECTED_MM_MAP_ERROR}, { - ERRHRD, ERRgeneral, NT_STATUS_UNEXPECTED_MM_EXTEND_ERR}, { - ERRHRD, ERRgeneral, NT_STATUS_NOT_LOGON_PROCESS}, { - ERRHRD, ERRgeneral, NT_STATUS_LOGON_SESSION_EXISTS}, { - ERRDOS, 87, NT_STATUS_INVALID_PARAMETER_1}, { - ERRDOS, 87, NT_STATUS_INVALID_PARAMETER_2}, { - ERRDOS, 87, NT_STATUS_INVALID_PARAMETER_3}, { - ERRDOS, 87, NT_STATUS_INVALID_PARAMETER_4}, { - ERRDOS, 87, NT_STATUS_INVALID_PARAMETER_5}, { - ERRDOS, 87, NT_STATUS_INVALID_PARAMETER_6}, { - ERRDOS, 87, NT_STATUS_INVALID_PARAMETER_7}, { - ERRDOS, 87, NT_STATUS_INVALID_PARAMETER_8}, { - ERRDOS, 87, NT_STATUS_INVALID_PARAMETER_9}, { - ERRDOS, 87, NT_STATUS_INVALID_PARAMETER_10}, { - ERRDOS, 87, NT_STATUS_INVALID_PARAMETER_11}, { - ERRDOS, 87, NT_STATUS_INVALID_PARAMETER_12}, { - ERRDOS, ERRbadpath, NT_STATUS_REDIRECTOR_NOT_STARTED}, { - ERRHRD, ERRgeneral, NT_STATUS_REDIRECTOR_STARTED}, { - ERRHRD, ERRgeneral, NT_STATUS_STACK_OVERFLOW}, { - ERRHRD, ERRgeneral, NT_STATUS_NO_SUCH_PACKAGE}, { - ERRHRD, ERRgeneral, NT_STATUS_BAD_FUNCTION_TABLE}, { - ERRDOS, 203, 0xc0000100}, { - ERRDOS, 145, NT_STATUS_DIRECTORY_NOT_EMPTY}, { - ERRHRD, ERRgeneral, NT_STATUS_FILE_CORRUPT_ERROR}, { - ERRDOS, 267, NT_STATUS_NOT_A_DIRECTORY}, { - ERRHRD, ERRgeneral, NT_STATUS_BAD_LOGON_SESSION_STATE}, { - ERRHRD, ERRgeneral, NT_STATUS_LOGON_SESSION_COLLISION}, { - ERRDOS, 206, NT_STATUS_NAME_TOO_LONG}, { - ERRDOS, 2401, NT_STATUS_FILES_OPEN}, { - ERRDOS, 2404, NT_STATUS_CONNECTION_IN_USE}, { - ERRHRD, ERRgeneral, NT_STATUS_MESSAGE_NOT_FOUND}, { - ERRDOS, ERRnoaccess, NT_STATUS_PROCESS_IS_TERMINATING}, { - ERRHRD, ERRgeneral, NT_STATUS_INVALID_LOGON_TYPE}, { - ERRHRD, ERRgeneral, NT_STATUS_NO_GUID_TRANSLATION}, { - ERRHRD, ERRgeneral, NT_STATUS_CANNOT_IMPERSONATE}, { - ERRHRD, ERRgeneral, NT_STATUS_IMAGE_ALREADY_LOADED}, { - ERRHRD, ERRgeneral, NT_STATUS_ABIOS_NOT_PRESENT}, { - ERRHRD, ERRgeneral, NT_STATUS_ABIOS_LID_NOT_EXIST}, { - ERRHRD, ERRgeneral, NT_STATUS_ABIOS_LID_ALREADY_OWNED}, { - ERRHRD, ERRgeneral, NT_STATUS_ABIOS_NOT_LID_OWNER}, { - ERRHRD, ERRgeneral, NT_STATUS_ABIOS_INVALID_COMMAND}, { - ERRHRD, ERRgeneral, NT_STATUS_ABIOS_INVALID_LID}, { - ERRHRD, ERRgeneral, NT_STATUS_ABIOS_SELECTOR_NOT_AVAILABLE}, { - ERRHRD, ERRgeneral, NT_STATUS_ABIOS_INVALID_SELECTOR}, { - ERRHRD, ERRgeneral, NT_STATUS_NO_LDT}, { - ERRHRD, ERRgeneral, NT_STATUS_INVALID_LDT_SIZE}, { - ERRHRD, ERRgeneral, NT_STATUS_INVALID_LDT_OFFSET}, { - ERRHRD, ERRgeneral, NT_STATUS_INVALID_LDT_DESCRIPTOR}, { - ERRDOS, 193, NT_STATUS_INVALID_IMAGE_NE_FORMAT}, { - ERRHRD, ERRgeneral, NT_STATUS_RXACT_INVALID_STATE}, { - ERRHRD, ERRgeneral, NT_STATUS_RXACT_COMMIT_FAILURE}, { - ERRHRD, ERRgeneral, NT_STATUS_MAPPED_FILE_SIZE_ZERO}, { - ERRDOS, ERRnofids, NT_STATUS_TOO_MANY_OPENED_FILES}, { - ERRHRD, ERRgeneral, NT_STATUS_CANCELLED}, { - ERRDOS, ERRnoaccess, NT_STATUS_CANNOT_DELETE}, { - ERRHRD, ERRgeneral, NT_STATUS_INVALID_COMPUTER_NAME}, { - ERRDOS, ERRnoaccess, NT_STATUS_FILE_DELETED}, { - ERRHRD, ERRgeneral, NT_STATUS_SPECIAL_ACCOUNT}, { - ERRHRD, ERRgeneral, NT_STATUS_SPECIAL_GROUP}, { - ERRHRD, ERRgeneral, NT_STATUS_SPECIAL_USER}, { - ERRHRD, ERRgeneral, NT_STATUS_MEMBERS_PRIMARY_GROUP}, { - ERRDOS, ERRbadfid, NT_STATUS_FILE_CLOSED}, { - ERRHRD, ERRgeneral, NT_STATUS_TOO_MANY_THREADS}, { - ERRHRD, ERRgeneral, NT_STATUS_THREAD_NOT_IN_PROCESS}, { - ERRHRD, ERRgeneral, NT_STATUS_TOKEN_ALREADY_IN_USE}, { - ERRHRD, ERRgeneral, NT_STATUS_PAGEFILE_QUOTA_EXCEEDED}, { - ERRHRD, ERRgeneral, NT_STATUS_COMMITMENT_LIMIT}, { - ERRDOS, 193, NT_STATUS_INVALID_IMAGE_LE_FORMAT}, { - ERRDOS, 193, NT_STATUS_INVALID_IMAGE_NOT_MZ}, { - ERRDOS, 193, NT_STATUS_INVALID_IMAGE_PROTECT}, { - ERRDOS, 193, NT_STATUS_INVALID_IMAGE_WIN_16}, { - ERRHRD, ERRgeneral, NT_STATUS_LOGON_SERVER_CONFLICT}, { - ERRHRD, ERRgeneral, NT_STATUS_TIME_DIFFERENCE_AT_DC}, { - ERRHRD, ERRgeneral, NT_STATUS_SYNCHRONIZATION_REQUIRED}, { - ERRDOS, 126, NT_STATUS_DLL_NOT_FOUND}, { - ERRHRD, ERRgeneral, NT_STATUS_OPEN_FAILED}, { - ERRHRD, ERRgeneral, NT_STATUS_IO_PRIVILEGE_FAILED}, { - ERRDOS, 182, NT_STATUS_ORDINAL_NOT_FOUND}, { - ERRDOS, 127, NT_STATUS_ENTRYPOINT_NOT_FOUND}, { - ERRHRD, ERRgeneral, NT_STATUS_CONTROL_C_EXIT}, { - ERRDOS, 64, NT_STATUS_LOCAL_DISCONNECT}, { - ERRDOS, 64, NT_STATUS_REMOTE_DISCONNECT}, { - ERRDOS, 51, NT_STATUS_REMOTE_RESOURCES}, { - ERRDOS, 59, NT_STATUS_LINK_FAILED}, { - ERRDOS, 59, NT_STATUS_LINK_TIMEOUT}, { - ERRDOS, 59, NT_STATUS_INVALID_CONNECTION}, { - ERRDOS, 59, NT_STATUS_INVALID_ADDRESS}, { - ERRHRD, ERRgeneral, NT_STATUS_DLL_INIT_FAILED}, { - ERRHRD, ERRgeneral, NT_STATUS_MISSING_SYSTEMFILE}, { - ERRHRD, ERRgeneral, NT_STATUS_UNHANDLED_EXCEPTION}, { - ERRHRD, ERRgeneral, NT_STATUS_APP_INIT_FAILURE}, { - ERRHRD, ERRgeneral, NT_STATUS_PAGEFILE_CREATE_FAILED}, { - ERRHRD, ERRgeneral, NT_STATUS_NO_PAGEFILE}, { - ERRDOS, 124, NT_STATUS_INVALID_LEVEL}, { - ERRDOS, 86, NT_STATUS_WRONG_PASSWORD_CORE}, { - ERRHRD, ERRgeneral, NT_STATUS_ILLEGAL_FLOAT_CONTEXT}, { - ERRDOS, 109, NT_STATUS_PIPE_BROKEN}, { - ERRHRD, ERRgeneral, NT_STATUS_REGISTRY_CORRUPT}, { - ERRHRD, ERRgeneral, NT_STATUS_REGISTRY_IO_FAILED}, { - ERRHRD, ERRgeneral, NT_STATUS_NO_EVENT_PAIR}, { - ERRHRD, ERRgeneral, NT_STATUS_UNRECOGNIZED_VOLUME}, { - ERRHRD, ERRgeneral, NT_STATUS_SERIAL_NO_DEVICE_INITED}, { - ERRHRD, ERRgeneral, NT_STATUS_NO_SUCH_ALIAS}, { - ERRHRD, ERRgeneral, NT_STATUS_MEMBER_NOT_IN_ALIAS}, { - ERRHRD, ERRgeneral, NT_STATUS_MEMBER_IN_ALIAS}, { - ERRHRD, ERRgeneral, NT_STATUS_ALIAS_EXISTS}, { - ERRHRD, ERRgeneral, NT_STATUS_LOGON_NOT_GRANTED}, { - ERRHRD, ERRgeneral, NT_STATUS_TOO_MANY_SECRETS}, { - ERRHRD, ERRgeneral, NT_STATUS_SECRET_TOO_LONG}, { - ERRHRD, ERRgeneral, NT_STATUS_INTERNAL_DB_ERROR}, { - ERRHRD, ERRgeneral, NT_STATUS_FULLSCREEN_MODE}, { - ERRHRD, ERRgeneral, NT_STATUS_TOO_MANY_CONTEXT_IDS}, { - ERRDOS, ERRnoaccess, NT_STATUS_LOGON_TYPE_NOT_GRANTED}, { - ERRHRD, ERRgeneral, NT_STATUS_NOT_REGISTRY_FILE}, { - ERRHRD, ERRgeneral, NT_STATUS_NT_CROSS_ENCRYPTION_REQUIRED}, { - ERRHRD, ERRgeneral, NT_STATUS_DOMAIN_CTRLR_CONFIG_ERROR}, { - ERRHRD, ERRgeneral, NT_STATUS_FT_MISSING_MEMBER}, { - ERRHRD, ERRgeneral, NT_STATUS_ILL_FORMED_SERVICE_ENTRY}, { - ERRHRD, ERRgeneral, NT_STATUS_ILLEGAL_CHARACTER}, { - ERRHRD, ERRgeneral, NT_STATUS_UNMAPPABLE_CHARACTER}, { - ERRHRD, ERRgeneral, NT_STATUS_UNDEFINED_CHARACTER}, { - ERRHRD, ERRgeneral, NT_STATUS_FLOPPY_VOLUME}, { - ERRHRD, ERRgeneral, NT_STATUS_FLOPPY_ID_MARK_NOT_FOUND}, { - ERRHRD, ERRgeneral, NT_STATUS_FLOPPY_WRONG_CYLINDER}, { - ERRHRD, ERRgeneral, NT_STATUS_FLOPPY_UNKNOWN_ERROR}, { - ERRHRD, ERRgeneral, NT_STATUS_FLOPPY_BAD_REGISTERS}, { - ERRHRD, ERRgeneral, NT_STATUS_DISK_RECALIBRATE_FAILED}, { - ERRHRD, ERRgeneral, NT_STATUS_DISK_OPERATION_FAILED}, { - ERRHRD, ERRgeneral, NT_STATUS_DISK_RESET_FAILED}, { - ERRHRD, ERRgeneral, NT_STATUS_SHARED_IRQ_BUSY}, { - ERRHRD, ERRgeneral, NT_STATUS_FT_ORPHANING}, { - ERRHRD, ERRgeneral, 0xc000016e}, { - ERRHRD, ERRgeneral, 0xc000016f}, { - ERRHRD, ERRgeneral, 0xc0000170}, { - ERRHRD, ERRgeneral, 0xc0000171}, { - ERRHRD, ERRgeneral, NT_STATUS_PARTITION_FAILURE}, { - ERRHRD, ERRgeneral, NT_STATUS_INVALID_BLOCK_LENGTH}, { - ERRHRD, ERRgeneral, NT_STATUS_DEVICE_NOT_PARTITIONED}, { - ERRHRD, ERRgeneral, NT_STATUS_UNABLE_TO_LOCK_MEDIA}, { - ERRHRD, ERRgeneral, NT_STATUS_UNABLE_TO_UNLOAD_MEDIA}, { - ERRHRD, ERRgeneral, NT_STATUS_EOM_OVERFLOW}, { - ERRHRD, ERRgeneral, NT_STATUS_NO_MEDIA}, { - ERRHRD, ERRgeneral, 0xc0000179}, { - ERRHRD, ERRgeneral, NT_STATUS_NO_SUCH_MEMBER}, { - ERRHRD, ERRgeneral, NT_STATUS_INVALID_MEMBER}, { - ERRHRD, ERRgeneral, NT_STATUS_KEY_DELETED}, { - ERRHRD, ERRgeneral, NT_STATUS_NO_LOG_SPACE}, { - ERRHRD, ERRgeneral, NT_STATUS_TOO_MANY_SIDS}, { - ERRHRD, ERRgeneral, NT_STATUS_LM_CROSS_ENCRYPTION_REQUIRED}, { - ERRHRD, ERRgeneral, NT_STATUS_KEY_HAS_CHILDREN}, { - ERRHRD, ERRgeneral, NT_STATUS_CHILD_MUST_BE_VOLATILE}, { - ERRDOS, 87, NT_STATUS_DEVICE_CONFIGURATION_ERROR}, { - ERRHRD, ERRgeneral, NT_STATUS_DRIVER_INTERNAL_ERROR}, { - ERRDOS, 22, NT_STATUS_INVALID_DEVICE_STATE}, { - ERRHRD, ERRgeneral, NT_STATUS_IO_DEVICE_ERROR}, { - ERRHRD, ERRgeneral, NT_STATUS_DEVICE_PROTOCOL_ERROR}, { - ERRHRD, ERRgeneral, NT_STATUS_BACKUP_CONTROLLER}, { - ERRHRD, ERRgeneral, NT_STATUS_LOG_FILE_FULL}, { - ERRDOS, 19, NT_STATUS_TOO_LATE}, { - ERRDOS, ERRnoaccess, NT_STATUS_NO_TRUST_LSA_SECRET}, -/* { This NT error code was 'sqashed' - from NT_STATUS_NO_TRUST_SAM_ACCOUNT to - NT_STATUS_TRUSTED_RELATIONSHIP_FAILURE during the session setup } */ - { - ERRDOS, ERRnoaccess, NT_STATUS_NO_TRUST_SAM_ACCOUNT}, { - ERRDOS, ERRnoaccess, NT_STATUS_TRUSTED_DOMAIN_FAILURE}, { - ERRDOS, ERRnoaccess, NT_STATUS_TRUSTED_RELATIONSHIP_FAILURE}, { - ERRHRD, ERRgeneral, NT_STATUS_EVENTLOG_FILE_CORRUPT}, { - ERRHRD, ERRgeneral, NT_STATUS_EVENTLOG_CANT_START}, { - ERRDOS, ERRnoaccess, NT_STATUS_TRUST_FAILURE}, { - ERRHRD, ERRgeneral, NT_STATUS_MUTANT_LIMIT_EXCEEDED}, { - ERRDOS, ERRnetlogonNotStarted, NT_STATUS_NETLOGON_NOT_STARTED}, { - ERRSRV, ERRaccountexpired, NT_STATUS_ACCOUNT_EXPIRED}, { - ERRHRD, ERRgeneral, NT_STATUS_POSSIBLE_DEADLOCK}, { - ERRHRD, ERRgeneral, NT_STATUS_NETWORK_CREDENTIAL_CONFLICT}, { - ERRHRD, ERRgeneral, NT_STATUS_REMOTE_SESSION_LIMIT}, { - ERRHRD, ERRgeneral, NT_STATUS_EVENTLOG_FILE_CHANGED}, { - ERRDOS, ERRnoaccess, NT_STATUS_NOLOGON_INTERDOMAIN_TRUST_ACCOUNT}, { - ERRDOS, ERRnoaccess, NT_STATUS_NOLOGON_WORKSTATION_TRUST_ACCOUNT}, { - ERRDOS, ERRnoaccess, NT_STATUS_NOLOGON_SERVER_TRUST_ACCOUNT}, -/* { This NT error code was 'sqashed' - from NT_STATUS_DOMAIN_TRUST_INCONSISTENT to NT_STATUS_LOGON_FAILURE - during the session setup } */ - { - ERRDOS, ERRnoaccess, NT_STATUS_DOMAIN_TRUST_INCONSISTENT}, { - ERRHRD, ERRgeneral, NT_STATUS_FS_DRIVER_REQUIRED}, { - ERRHRD, ERRgeneral, NT_STATUS_NO_USER_SESSION_KEY}, { - ERRDOS, 59, NT_STATUS_USER_SESSION_DELETED}, { - ERRHRD, ERRgeneral, NT_STATUS_RESOURCE_LANG_NOT_FOUND}, { - ERRDOS, ERRnoresource, NT_STATUS_INSUFF_SERVER_RESOURCES}, { - ERRHRD, ERRgeneral, NT_STATUS_INVALID_BUFFER_SIZE}, { - ERRHRD, ERRgeneral, NT_STATUS_INVALID_ADDRESS_COMPONENT}, { - ERRHRD, ERRgeneral, NT_STATUS_INVALID_ADDRESS_WILDCARD}, { - ERRDOS, 68, NT_STATUS_TOO_MANY_ADDRESSES}, { - ERRDOS, 52, NT_STATUS_ADDRESS_ALREADY_EXISTS}, { - ERRDOS, 64, NT_STATUS_ADDRESS_CLOSED}, { - ERRDOS, 64, NT_STATUS_CONNECTION_DISCONNECTED}, { - ERRDOS, 64, NT_STATUS_CONNECTION_RESET}, { - ERRDOS, 68, NT_STATUS_TOO_MANY_NODES}, { - ERRDOS, 59, NT_STATUS_TRANSACTION_ABORTED}, { - ERRDOS, 59, NT_STATUS_TRANSACTION_TIMED_OUT}, { - ERRDOS, 59, NT_STATUS_TRANSACTION_NO_RELEASE}, { - ERRDOS, 59, NT_STATUS_TRANSACTION_NO_MATCH}, { - ERRDOS, 59, NT_STATUS_TRANSACTION_RESPONDED}, { - ERRDOS, 59, NT_STATUS_TRANSACTION_INVALID_ID}, { - ERRDOS, 59, NT_STATUS_TRANSACTION_INVALID_TYPE}, { - ERRDOS, ERRunsup, NT_STATUS_NOT_SERVER_SESSION}, { - ERRDOS, ERRunsup, NT_STATUS_NOT_CLIENT_SESSION}, { - ERRHRD, ERRgeneral, NT_STATUS_CANNOT_LOAD_REGISTRY_FILE}, { - ERRHRD, ERRgeneral, NT_STATUS_DEBUG_ATTACH_FAILED}, { - ERRHRD, ERRgeneral, NT_STATUS_SYSTEM_PROCESS_TERMINATED}, { - ERRHRD, ERRgeneral, NT_STATUS_DATA_NOT_ACCEPTED}, { - ERRHRD, ERRgeneral, NT_STATUS_NO_BROWSER_SERVERS_FOUND}, { - ERRHRD, ERRgeneral, NT_STATUS_VDM_HARD_ERROR}, { - ERRHRD, ERRgeneral, NT_STATUS_DRIVER_CANCEL_TIMEOUT}, { - ERRHRD, ERRgeneral, NT_STATUS_REPLY_MESSAGE_MISMATCH}, { - ERRHRD, ERRgeneral, NT_STATUS_MAPPED_ALIGNMENT}, { - ERRDOS, 193, NT_STATUS_IMAGE_CHECKSUM_MISMATCH}, { - ERRHRD, ERRgeneral, NT_STATUS_LOST_WRITEBEHIND_DATA}, { - ERRHRD, ERRgeneral, NT_STATUS_CLIENT_SERVER_PARAMETERS_INVALID}, { - ERRSRV, ERRpasswordExpired, NT_STATUS_PASSWORD_MUST_CHANGE}, { - ERRHRD, ERRgeneral, NT_STATUS_NOT_FOUND}, { - ERRHRD, ERRgeneral, NT_STATUS_NOT_TINY_STREAM}, { - ERRHRD, ERRgeneral, NT_STATUS_RECOVERY_FAILURE}, { - ERRHRD, ERRgeneral, NT_STATUS_STACK_OVERFLOW_READ}, { - ERRHRD, ERRgeneral, NT_STATUS_FAIL_CHECK}, { - ERRHRD, ERRgeneral, NT_STATUS_DUPLICATE_OBJECTID}, { - ERRHRD, ERRgeneral, NT_STATUS_OBJECTID_EXISTS}, { - ERRHRD, ERRgeneral, NT_STATUS_CONVERT_TO_LARGE}, { - ERRHRD, ERRgeneral, NT_STATUS_RETRY}, { - ERRHRD, ERRgeneral, NT_STATUS_FOUND_OUT_OF_SCOPE}, { - ERRHRD, ERRgeneral, NT_STATUS_ALLOCATE_BUCKET}, { - ERRHRD, ERRgeneral, NT_STATUS_PROPSET_NOT_FOUND}, { - ERRHRD, ERRgeneral, NT_STATUS_MARSHALL_OVERFLOW}, { - ERRHRD, ERRgeneral, NT_STATUS_INVALID_VARIANT}, { - ERRHRD, ERRgeneral, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND}, { - ERRDOS, ERRnoaccess, NT_STATUS_ACCOUNT_LOCKED_OUT}, { - ERRDOS, ERRbadfid, NT_STATUS_HANDLE_NOT_CLOSABLE}, { - ERRHRD, ERRgeneral, NT_STATUS_CONNECTION_REFUSED}, { - ERRHRD, ERRgeneral, NT_STATUS_GRACEFUL_DISCONNECT}, { - ERRHRD, ERRgeneral, NT_STATUS_ADDRESS_ALREADY_ASSOCIATED}, { - ERRHRD, ERRgeneral, NT_STATUS_ADDRESS_NOT_ASSOCIATED}, { - ERRHRD, ERRgeneral, NT_STATUS_CONNECTION_INVALID}, { - ERRHRD, ERRgeneral, NT_STATUS_CONNECTION_ACTIVE}, { - ERRHRD, ERRgeneral, NT_STATUS_NETWORK_UNREACHABLE}, { - ERRHRD, ERRgeneral, NT_STATUS_HOST_UNREACHABLE}, { - ERRHRD, ERRgeneral, NT_STATUS_PROTOCOL_UNREACHABLE}, { - ERRHRD, ERRgeneral, NT_STATUS_PORT_UNREACHABLE}, { - ERRHRD, ERRgeneral, NT_STATUS_REQUEST_ABORTED}, { - ERRHRD, ERRgeneral, NT_STATUS_CONNECTION_ABORTED}, { - ERRHRD, ERRgeneral, NT_STATUS_BAD_COMPRESSION_BUFFER}, { - ERRHRD, ERRgeneral, NT_STATUS_USER_MAPPED_FILE}, { - ERRHRD, ERRgeneral, NT_STATUS_AUDIT_FAILED}, { - ERRHRD, ERRgeneral, NT_STATUS_TIMER_RESOLUTION_NOT_SET}, { - ERRHRD, ERRgeneral, NT_STATUS_CONNECTION_COUNT_LIMIT}, { - ERRHRD, ERRgeneral, NT_STATUS_LOGIN_TIME_RESTRICTION}, { - ERRHRD, ERRgeneral, NT_STATUS_LOGIN_WKSTA_RESTRICTION}, { - ERRDOS, 193, NT_STATUS_IMAGE_MP_UP_MISMATCH}, { - ERRHRD, ERRgeneral, 0xc000024a}, { - ERRHRD, ERRgeneral, 0xc000024b}, { - ERRHRD, ERRgeneral, 0xc000024c}, { - ERRHRD, ERRgeneral, 0xc000024d}, { - ERRHRD, ERRgeneral, 0xc000024e}, { - ERRHRD, ERRgeneral, 0xc000024f}, { - ERRHRD, ERRgeneral, NT_STATUS_INSUFFICIENT_LOGON_INFO}, { - ERRHRD, ERRgeneral, NT_STATUS_BAD_DLL_ENTRYPOINT}, { - ERRHRD, ERRgeneral, NT_STATUS_BAD_SERVICE_ENTRYPOINT}, { - ERRHRD, ERRgeneral, NT_STATUS_LPC_REPLY_LOST}, { - ERRHRD, ERRgeneral, NT_STATUS_IP_ADDRESS_CONFLICT1}, { - ERRHRD, ERRgeneral, NT_STATUS_IP_ADDRESS_CONFLICT2}, { - ERRHRD, ERRgeneral, NT_STATUS_REGISTRY_QUOTA_LIMIT}, { - ERRSRV, 3, NT_STATUS_PATH_NOT_COVERED}, { - ERRHRD, ERRgeneral, NT_STATUS_NO_CALLBACK_ACTIVE}, { - ERRHRD, ERRgeneral, NT_STATUS_LICENSE_QUOTA_EXCEEDED}, { - ERRHRD, ERRgeneral, NT_STATUS_PWD_TOO_SHORT}, { - ERRHRD, ERRgeneral, NT_STATUS_PWD_TOO_RECENT}, { - ERRHRD, ERRgeneral, NT_STATUS_PWD_HISTORY_CONFLICT}, { - ERRHRD, ERRgeneral, 0xc000025d}, { - ERRHRD, ERRgeneral, NT_STATUS_PLUGPLAY_NO_DEVICE}, { - ERRHRD, ERRgeneral, NT_STATUS_UNSUPPORTED_COMPRESSION}, { - ERRHRD, ERRgeneral, NT_STATUS_INVALID_HW_PROFILE}, { - ERRHRD, ERRgeneral, NT_STATUS_INVALID_PLUGPLAY_DEVICE_PATH}, { - ERRDOS, 182, NT_STATUS_DRIVER_ORDINAL_NOT_FOUND}, { - ERRDOS, 127, NT_STATUS_DRIVER_ENTRYPOINT_NOT_FOUND}, { - ERRDOS, 288, NT_STATUS_RESOURCE_NOT_OWNED}, { - ERRDOS, ErrTooManyLinks, NT_STATUS_TOO_MANY_LINKS}, { - ERRHRD, ERRgeneral, NT_STATUS_QUOTA_LIST_INCONSISTENT}, { - ERRHRD, ERRgeneral, NT_STATUS_FILE_IS_OFFLINE}, { - ERRDOS, 21, 0xc000026e}, { - ERRDOS, 161, 0xc0000281}, { - ERRDOS, ERRnoaccess, 0xc000028a}, { - ERRDOS, ERRnoaccess, 0xc000028b}, { - ERRHRD, ERRgeneral, 0xc000028c}, { - ERRDOS, ERRnoaccess, 0xc000028d}, { - ERRDOS, ERRnoaccess, 0xc000028e}, { - ERRDOS, ERRnoaccess, 0xc000028f}, { - ERRDOS, ERRnoaccess, 0xc0000290}, { - ERRDOS, ERRbadfunc, 0xc000029c}, { - ERRDOS, ERRsymlink, NT_STATUS_STOPPED_ON_SYMLINK}, { - ERRDOS, ERRinvlevel, 0x007c0001}, { - 0, 0, 0 } -}; - -/***************************************************************************** - Print an error message from the status code - *****************************************************************************/ -static void -cifs_print_status(__u32 status_code) -{ - int idx = 0; - - while (nt_errs[idx].nt_errstr != NULL) { - if (((nt_errs[idx].nt_errcode) & 0xFFFFFF) == - (status_code & 0xFFFFFF)) { - pr_notice("Status code returned 0x%08x %s\n", - status_code, nt_errs[idx].nt_errstr); - } - idx++; - } - return; -} - - -static void -ntstatus_to_dos(__u32 ntstatus, __u8 *eclass, __u16 *ecode) -{ - int i; - if (ntstatus == 0) { - *eclass = 0; - *ecode = 0; - return; - } - for (i = 0; ntstatus_to_dos_map[i].ntstatus; i++) { - if (ntstatus == ntstatus_to_dos_map[i].ntstatus) { - *eclass = ntstatus_to_dos_map[i].dos_class; - *ecode = ntstatus_to_dos_map[i].dos_code; - return; - } - } - *eclass = ERRHRD; - *ecode = ERRgeneral; -} - -int -map_smb_to_linux_error(char *buf, bool logErr) -{ - struct smb_hdr *smb = (struct smb_hdr *)buf; - unsigned int i; - int rc = -EIO; /* if transport error smb error may not be set */ - __u8 smberrclass; - __u16 smberrcode; - - /* BB if NT Status codes - map NT BB */ - - /* old style smb error codes */ - if (smb->Status.CifsError == 0) - return 0; - - if (smb->Flags2 & SMBFLG2_ERR_STATUS) { - /* translate the newer STATUS codes to old style SMB errors - * and then to POSIX errors */ - __u32 err = le32_to_cpu(smb->Status.CifsError); - if (logErr && (err != (NT_STATUS_MORE_PROCESSING_REQUIRED))) - cifs_print_status(err); - else if (cifsFYI & CIFS_RC) - cifs_print_status(err); - ntstatus_to_dos(err, &smberrclass, &smberrcode); - } else { - smberrclass = smb->Status.DosError.ErrorClass; - smberrcode = le16_to_cpu(smb->Status.DosError.Error); - } - - /* old style errors */ - - /* DOS class smb error codes - map DOS */ - if (smberrclass == ERRDOS) { - /* 1 byte field no need to byte reverse */ - for (i = 0; - i < - sizeof(mapping_table_ERRDOS) / - sizeof(struct smb_to_posix_error); i++) { - if (mapping_table_ERRDOS[i].smb_err == 0) - break; - else if (mapping_table_ERRDOS[i].smb_err == - smberrcode) { - rc = mapping_table_ERRDOS[i].posix_code; - break; - } - /* else try next error mapping one to see if match */ - } - } else if (smberrclass == ERRSRV) { - /* server class of error codes */ - for (i = 0; - i < - sizeof(mapping_table_ERRSRV) / - sizeof(struct smb_to_posix_error); i++) { - if (mapping_table_ERRSRV[i].smb_err == 0) - break; - else if (mapping_table_ERRSRV[i].smb_err == - smberrcode) { - rc = mapping_table_ERRSRV[i].posix_code; - break; - } - /* else try next error mapping to see if match */ - } - } - /* else ERRHRD class errors or junk - return EIO */ - - cifs_dbg(FYI, "Mapping smb error code 0x%x to POSIX err %d\n", - le32_to_cpu(smb->Status.CifsError), rc); - - /* generic corrective action e.g. reconnect SMB session on - * ERRbaduid could be added */ - - return rc; -} - -int -map_and_check_smb_error(struct mid_q_entry *mid, bool logErr) -{ - int rc; - struct smb_hdr *smb = (struct smb_hdr *)mid->resp_buf; - - rc = map_smb_to_linux_error((char *)smb, logErr); - if (rc == -EACCES && !(smb->Flags2 & SMBFLG2_ERR_STATUS)) { - /* possible ERRBaduid */ - __u8 class = smb->Status.DosError.ErrorClass; - __u16 code = le16_to_cpu(smb->Status.DosError.Error); - - /* switch can be used to handle different errors */ - if (class == ERRSRV && code == ERRbaduid) { - cifs_dbg(FYI, "Server returned 0x%x, reconnecting session...\n", - code); - cifs_signal_cifsd_for_reconnect(mid->server, false); - } - } - - return rc; -} - - -/* - * calculate the size of the SMB message based on the fixed header - * portion, the number of word parameters and the data portion of the message - */ -unsigned int -smbCalcSize(void *buf) -{ - struct smb_hdr *ptr = buf; - return (sizeof(struct smb_hdr) + (2 * ptr->WordCount) + - 2 /* size of the bcc field */ + get_bcc(ptr)); -} - -/* The following are taken from fs/ntfs/util.c */ - -#define NTFS_TIME_OFFSET ((u64)(369*365 + 89) * 24 * 3600 * 10000000) - -/* - * Convert the NT UTC (based 1601-01-01, in hundred nanosecond units) - * into Unix UTC (based 1970-01-01, in seconds). - */ -struct timespec64 -cifs_NTtimeToUnix(__le64 ntutc) -{ - struct timespec64 ts; - /* BB what about the timezone? BB */ - - /* Subtract the NTFS time offset, then convert to 1s intervals. */ - s64 t = le64_to_cpu(ntutc) - NTFS_TIME_OFFSET; - u64 abs_t; - - /* - * Unfortunately can not use normal 64 bit division on 32 bit arch, but - * the alternative, do_div, does not work with negative numbers so have - * to special case them - */ - if (t < 0) { - abs_t = -t; - ts.tv_nsec = (time64_t)(do_div(abs_t, 10000000) * 100); - ts.tv_nsec = -ts.tv_nsec; - ts.tv_sec = -abs_t; - } else { - abs_t = t; - ts.tv_nsec = (time64_t)do_div(abs_t, 10000000) * 100; - ts.tv_sec = abs_t; - } - - return ts; -} - -/* Convert the Unix UTC into NT UTC. */ -u64 -cifs_UnixTimeToNT(struct timespec64 t) -{ - /* Convert to 100ns intervals and then add the NTFS time offset. */ - return (u64) t.tv_sec * 10000000 + t.tv_nsec/100 + NTFS_TIME_OFFSET; -} - -static const int total_days_of_prev_months[] = { - 0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334 -}; - -struct timespec64 cnvrtDosUnixTm(__le16 le_date, __le16 le_time, int offset) -{ - struct timespec64 ts; - time64_t sec, days; - int min, day, month, year; - u16 date = le16_to_cpu(le_date); - u16 time = le16_to_cpu(le_time); - SMB_TIME *st = (SMB_TIME *)&time; - SMB_DATE *sd = (SMB_DATE *)&date; - - cifs_dbg(FYI, "date %d time %d\n", date, time); - - sec = 2 * st->TwoSeconds; - min = st->Minutes; - if ((sec > 59) || (min > 59)) - cifs_dbg(VFS, "Invalid time min %d sec %lld\n", min, sec); - sec += (min * 60); - sec += 60 * 60 * st->Hours; - if (st->Hours > 24) - cifs_dbg(VFS, "Invalid hours %d\n", st->Hours); - day = sd->Day; - month = sd->Month; - if (day < 1 || day > 31 || month < 1 || month > 12) { - cifs_dbg(VFS, "Invalid date, month %d day: %d\n", month, day); - day = clamp(day, 1, 31); - month = clamp(month, 1, 12); - } - month -= 1; - days = day + total_days_of_prev_months[month]; - days += 3652; /* account for difference in days between 1980 and 1970 */ - year = sd->Year; - days += year * 365; - days += (year/4); /* leap year */ - /* generalized leap year calculation is more complex, ie no leap year - for years/100 except for years/400, but since the maximum number for DOS - year is 2**7, the last year is 1980+127, which means we need only - consider 2 special case years, ie the years 2000 and 2100, and only - adjust for the lack of leap year for the year 2100, as 2000 was a - leap year (divisable by 400) */ - if (year >= 120) /* the year 2100 */ - days = days - 1; /* do not count leap year for the year 2100 */ - - /* adjust for leap year where we are still before leap day */ - if (year != 120) - days -= ((year & 0x03) == 0) && (month < 2 ? 1 : 0); - sec += 24 * 60 * 60 * days; - - ts.tv_sec = sec + offset; - - /* cifs_dbg(FYI, "sec after cnvrt dos to unix time %d\n",sec); */ - - ts.tv_nsec = 0; - return ts; -} diff --git a/fs/cifs/nterr.c b/fs/cifs/nterr.c deleted file mode 100644 index 358a766375b4a..0000000000000 --- a/fs/cifs/nterr.c +++ /dev/null @@ -1,674 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-or-later -/* - * Unix SMB/Netbios implementation. - * Version 1.9. - * RPC Pipe client / server routines - * Copyright (C) Luke Kenneth Casson Leighton 1997-2001. - */ - -/* NT error codes - see nterr.h */ -#include -#include -#include "nterr.h" - -const struct nt_err_code_struct nt_errs[] = { - {"NT_STATUS_OK", NT_STATUS_OK}, - {"NT_STATUS_UNSUCCESSFUL", NT_STATUS_UNSUCCESSFUL}, - {"NT_STATUS_NOT_IMPLEMENTED", NT_STATUS_NOT_IMPLEMENTED}, - {"NT_STATUS_INVALID_INFO_CLASS", NT_STATUS_INVALID_INFO_CLASS}, - {"NT_STATUS_INFO_LENGTH_MISMATCH", NT_STATUS_INFO_LENGTH_MISMATCH}, - {"NT_STATUS_ACCESS_VIOLATION", NT_STATUS_ACCESS_VIOLATION}, - {"NT_STATUS_BUFFER_OVERFLOW", NT_STATUS_BUFFER_OVERFLOW}, - {"NT_STATUS_IN_PAGE_ERROR", NT_STATUS_IN_PAGE_ERROR}, - {"NT_STATUS_PAGEFILE_QUOTA", NT_STATUS_PAGEFILE_QUOTA}, - {"NT_STATUS_INVALID_HANDLE", NT_STATUS_INVALID_HANDLE}, - {"NT_STATUS_BAD_INITIAL_STACK", NT_STATUS_BAD_INITIAL_STACK}, - {"NT_STATUS_BAD_INITIAL_PC", NT_STATUS_BAD_INITIAL_PC}, - {"NT_STATUS_INVALID_CID", NT_STATUS_INVALID_CID}, - {"NT_STATUS_TIMER_NOT_CANCELED", NT_STATUS_TIMER_NOT_CANCELED}, - {"NT_STATUS_INVALID_PARAMETER", NT_STATUS_INVALID_PARAMETER}, - {"NT_STATUS_NO_SUCH_DEVICE", NT_STATUS_NO_SUCH_DEVICE}, - {"NT_STATUS_NO_SUCH_FILE", NT_STATUS_NO_SUCH_FILE}, - {"NT_STATUS_INVALID_DEVICE_REQUEST", - NT_STATUS_INVALID_DEVICE_REQUEST}, - {"NT_STATUS_END_OF_FILE", NT_STATUS_END_OF_FILE}, - {"NT_STATUS_WRONG_VOLUME", NT_STATUS_WRONG_VOLUME}, - {"NT_STATUS_NO_MEDIA_IN_DEVICE", NT_STATUS_NO_MEDIA_IN_DEVICE}, - {"NT_STATUS_UNRECOGNIZED_MEDIA", NT_STATUS_UNRECOGNIZED_MEDIA}, - {"NT_STATUS_NONEXISTENT_SECTOR", NT_STATUS_NONEXISTENT_SECTOR}, - {"NT_STATUS_MORE_PROCESSING_REQUIRED", - NT_STATUS_MORE_PROCESSING_REQUIRED}, - {"NT_STATUS_NO_MEMORY", NT_STATUS_NO_MEMORY}, - {"NT_STATUS_CONFLICTING_ADDRESSES", - NT_STATUS_CONFLICTING_ADDRESSES}, - {"NT_STATUS_NOT_MAPPED_VIEW", NT_STATUS_NOT_MAPPED_VIEW}, - {"NT_STATUS_UNABLE_TO_FREE_VM", NT_STATUS_UNABLE_TO_FREE_VM}, - {"NT_STATUS_UNABLE_TO_DELETE_SECTION", - NT_STATUS_UNABLE_TO_DELETE_SECTION}, - {"NT_STATUS_INVALID_SYSTEM_SERVICE", - NT_STATUS_INVALID_SYSTEM_SERVICE}, - {"NT_STATUS_ILLEGAL_INSTRUCTION", NT_STATUS_ILLEGAL_INSTRUCTION}, - {"NT_STATUS_INVALID_LOCK_SEQUENCE", - NT_STATUS_INVALID_LOCK_SEQUENCE}, - {"NT_STATUS_INVALID_VIEW_SIZE", NT_STATUS_INVALID_VIEW_SIZE}, - {"NT_STATUS_INVALID_FILE_FOR_SECTION", - NT_STATUS_INVALID_FILE_FOR_SECTION}, - {"NT_STATUS_ALREADY_COMMITTED", NT_STATUS_ALREADY_COMMITTED}, - {"NT_STATUS_ACCESS_DENIED", NT_STATUS_ACCESS_DENIED}, - {"NT_STATUS_BUFFER_TOO_SMALL", NT_STATUS_BUFFER_TOO_SMALL}, - {"NT_STATUS_OBJECT_TYPE_MISMATCH", NT_STATUS_OBJECT_TYPE_MISMATCH}, - {"NT_STATUS_NONCONTINUABLE_EXCEPTION", - NT_STATUS_NONCONTINUABLE_EXCEPTION}, - {"NT_STATUS_INVALID_DISPOSITION", NT_STATUS_INVALID_DISPOSITION}, - {"NT_STATUS_UNWIND", NT_STATUS_UNWIND}, - {"NT_STATUS_BAD_STACK", NT_STATUS_BAD_STACK}, - {"NT_STATUS_INVALID_UNWIND_TARGET", - NT_STATUS_INVALID_UNWIND_TARGET}, - {"NT_STATUS_NOT_LOCKED", NT_STATUS_NOT_LOCKED}, - {"NT_STATUS_PARITY_ERROR", NT_STATUS_PARITY_ERROR}, - {"NT_STATUS_UNABLE_TO_DECOMMIT_VM", - NT_STATUS_UNABLE_TO_DECOMMIT_VM}, - {"NT_STATUS_NOT_COMMITTED", NT_STATUS_NOT_COMMITTED}, - {"NT_STATUS_INVALID_PORT_ATTRIBUTES", - NT_STATUS_INVALID_PORT_ATTRIBUTES}, - {"NT_STATUS_PORT_MESSAGE_TOO_LONG", - NT_STATUS_PORT_MESSAGE_TOO_LONG}, - {"NT_STATUS_INVALID_PARAMETER_MIX", - NT_STATUS_INVALID_PARAMETER_MIX}, - {"NT_STATUS_INVALID_QUOTA_LOWER", NT_STATUS_INVALID_QUOTA_LOWER}, - {"NT_STATUS_DISK_CORRUPT_ERROR", NT_STATUS_DISK_CORRUPT_ERROR}, - {"NT_STATUS_OBJECT_NAME_INVALID", NT_STATUS_OBJECT_NAME_INVALID}, - {"NT_STATUS_OBJECT_NAME_NOT_FOUND", - NT_STATUS_OBJECT_NAME_NOT_FOUND}, - {"NT_STATUS_OBJECT_NAME_COLLISION", - NT_STATUS_OBJECT_NAME_COLLISION}, - {"NT_STATUS_HANDLE_NOT_WAITABLE", NT_STATUS_HANDLE_NOT_WAITABLE}, - {"NT_STATUS_PORT_DISCONNECTED", NT_STATUS_PORT_DISCONNECTED}, - {"NT_STATUS_DEVICE_ALREADY_ATTACHED", - NT_STATUS_DEVICE_ALREADY_ATTACHED}, - {"NT_STATUS_OBJECT_PATH_INVALID", NT_STATUS_OBJECT_PATH_INVALID}, - {"NT_STATUS_OBJECT_PATH_NOT_FOUND", - NT_STATUS_OBJECT_PATH_NOT_FOUND}, - {"NT_STATUS_OBJECT_PATH_SYNTAX_BAD", - NT_STATUS_OBJECT_PATH_SYNTAX_BAD}, - {"NT_STATUS_DATA_OVERRUN", NT_STATUS_DATA_OVERRUN}, - {"NT_STATUS_DATA_LATE_ERROR", NT_STATUS_DATA_LATE_ERROR}, - {"NT_STATUS_DATA_ERROR", NT_STATUS_DATA_ERROR}, - {"NT_STATUS_CRC_ERROR", NT_STATUS_CRC_ERROR}, - {"NT_STATUS_SECTION_TOO_BIG", NT_STATUS_SECTION_TOO_BIG}, - {"NT_STATUS_PORT_CONNECTION_REFUSED", - NT_STATUS_PORT_CONNECTION_REFUSED}, - {"NT_STATUS_INVALID_PORT_HANDLE", NT_STATUS_INVALID_PORT_HANDLE}, - {"NT_STATUS_SHARING_VIOLATION", NT_STATUS_SHARING_VIOLATION}, - {"NT_STATUS_QUOTA_EXCEEDED", NT_STATUS_QUOTA_EXCEEDED}, - {"NT_STATUS_INVALID_PAGE_PROTECTION", - NT_STATUS_INVALID_PAGE_PROTECTION}, - {"NT_STATUS_MUTANT_NOT_OWNED", NT_STATUS_MUTANT_NOT_OWNED}, - {"NT_STATUS_SEMAPHORE_LIMIT_EXCEEDED", - NT_STATUS_SEMAPHORE_LIMIT_EXCEEDED}, - {"NT_STATUS_PORT_ALREADY_SET", NT_STATUS_PORT_ALREADY_SET}, - {"NT_STATUS_SECTION_NOT_IMAGE", NT_STATUS_SECTION_NOT_IMAGE}, - {"NT_STATUS_SUSPEND_COUNT_EXCEEDED", - NT_STATUS_SUSPEND_COUNT_EXCEEDED}, - {"NT_STATUS_THREAD_IS_TERMINATING", - NT_STATUS_THREAD_IS_TERMINATING}, - {"NT_STATUS_BAD_WORKING_SET_LIMIT", - NT_STATUS_BAD_WORKING_SET_LIMIT}, - {"NT_STATUS_INCOMPATIBLE_FILE_MAP", - NT_STATUS_INCOMPATIBLE_FILE_MAP}, - {"NT_STATUS_SECTION_PROTECTION", NT_STATUS_SECTION_PROTECTION}, - {"NT_STATUS_EAS_NOT_SUPPORTED", NT_STATUS_EAS_NOT_SUPPORTED}, - {"NT_STATUS_EA_TOO_LARGE", NT_STATUS_EA_TOO_LARGE}, - {"NT_STATUS_NONEXISTENT_EA_ENTRY", NT_STATUS_NONEXISTENT_EA_ENTRY}, - {"NT_STATUS_NO_EAS_ON_FILE", NT_STATUS_NO_EAS_ON_FILE}, - {"NT_STATUS_EA_CORRUPT_ERROR", NT_STATUS_EA_CORRUPT_ERROR}, - {"NT_STATUS_FILE_LOCK_CONFLICT", NT_STATUS_FILE_LOCK_CONFLICT}, - {"NT_STATUS_LOCK_NOT_GRANTED", NT_STATUS_LOCK_NOT_GRANTED}, - {"NT_STATUS_DELETE_PENDING", NT_STATUS_DELETE_PENDING}, - {"NT_STATUS_CTL_FILE_NOT_SUPPORTED", - NT_STATUS_CTL_FILE_NOT_SUPPORTED}, - {"NT_STATUS_UNKNOWN_REVISION", NT_STATUS_UNKNOWN_REVISION}, - {"NT_STATUS_REVISION_MISMATCH", NT_STATUS_REVISION_MISMATCH}, - {"NT_STATUS_INVALID_OWNER", NT_STATUS_INVALID_OWNER}, - {"NT_STATUS_INVALID_PRIMARY_GROUP", - NT_STATUS_INVALID_PRIMARY_GROUP}, - {"NT_STATUS_NO_IMPERSONATION_TOKEN", - NT_STATUS_NO_IMPERSONATION_TOKEN}, - {"NT_STATUS_CANT_DISABLE_MANDATORY", - NT_STATUS_CANT_DISABLE_MANDATORY}, - {"NT_STATUS_NO_LOGON_SERVERS", NT_STATUS_NO_LOGON_SERVERS}, - {"NT_STATUS_NO_SUCH_LOGON_SESSION", - NT_STATUS_NO_SUCH_LOGON_SESSION}, - {"NT_STATUS_NO_SUCH_PRIVILEGE", NT_STATUS_NO_SUCH_PRIVILEGE}, - {"NT_STATUS_PRIVILEGE_NOT_HELD", NT_STATUS_PRIVILEGE_NOT_HELD}, - {"NT_STATUS_INVALID_ACCOUNT_NAME", NT_STATUS_INVALID_ACCOUNT_NAME}, - {"NT_STATUS_USER_EXISTS", NT_STATUS_USER_EXISTS}, - {"NT_STATUS_NO_SUCH_USER", NT_STATUS_NO_SUCH_USER}, - {"NT_STATUS_GROUP_EXISTS", NT_STATUS_GROUP_EXISTS}, - {"NT_STATUS_NO_SUCH_GROUP", NT_STATUS_NO_SUCH_GROUP}, - {"NT_STATUS_MEMBER_IN_GROUP", NT_STATUS_MEMBER_IN_GROUP}, - {"NT_STATUS_MEMBER_NOT_IN_GROUP", NT_STATUS_MEMBER_NOT_IN_GROUP}, - {"NT_STATUS_LAST_ADMIN", NT_STATUS_LAST_ADMIN}, - {"NT_STATUS_WRONG_PASSWORD", NT_STATUS_WRONG_PASSWORD}, - {"NT_STATUS_ILL_FORMED_PASSWORD", NT_STATUS_ILL_FORMED_PASSWORD}, - {"NT_STATUS_PASSWORD_RESTRICTION", NT_STATUS_PASSWORD_RESTRICTION}, - {"NT_STATUS_LOGON_FAILURE", NT_STATUS_LOGON_FAILURE}, - {"NT_STATUS_ACCOUNT_RESTRICTION", NT_STATUS_ACCOUNT_RESTRICTION}, - {"NT_STATUS_INVALID_LOGON_HOURS", NT_STATUS_INVALID_LOGON_HOURS}, - {"NT_STATUS_INVALID_WORKSTATION", NT_STATUS_INVALID_WORKSTATION}, - {"NT_STATUS_PASSWORD_EXPIRED", NT_STATUS_PASSWORD_EXPIRED}, - {"NT_STATUS_ACCOUNT_DISABLED", NT_STATUS_ACCOUNT_DISABLED}, - {"NT_STATUS_NONE_MAPPED", NT_STATUS_NONE_MAPPED}, - {"NT_STATUS_TOO_MANY_LUIDS_REQUESTED", - NT_STATUS_TOO_MANY_LUIDS_REQUESTED}, - {"NT_STATUS_LUIDS_EXHAUSTED", NT_STATUS_LUIDS_EXHAUSTED}, - {"NT_STATUS_INVALID_SUB_AUTHORITY", - NT_STATUS_INVALID_SUB_AUTHORITY}, - {"NT_STATUS_INVALID_ACL", NT_STATUS_INVALID_ACL}, - {"NT_STATUS_INVALID_SID", NT_STATUS_INVALID_SID}, - {"NT_STATUS_INVALID_SECURITY_DESCR", - NT_STATUS_INVALID_SECURITY_DESCR}, - {"NT_STATUS_PROCEDURE_NOT_FOUND", NT_STATUS_PROCEDURE_NOT_FOUND}, - {"NT_STATUS_INVALID_IMAGE_FORMAT", NT_STATUS_INVALID_IMAGE_FORMAT}, - {"NT_STATUS_NO_TOKEN", NT_STATUS_NO_TOKEN}, - {"NT_STATUS_BAD_INHERITANCE_ACL", NT_STATUS_BAD_INHERITANCE_ACL}, - {"NT_STATUS_RANGE_NOT_LOCKED", NT_STATUS_RANGE_NOT_LOCKED}, - {"NT_STATUS_DISK_FULL", NT_STATUS_DISK_FULL}, - {"NT_STATUS_SERVER_DISABLED", NT_STATUS_SERVER_DISABLED}, - {"NT_STATUS_SERVER_NOT_DISABLED", NT_STATUS_SERVER_NOT_DISABLED}, - {"NT_STATUS_TOO_MANY_GUIDS_REQUESTED", - NT_STATUS_TOO_MANY_GUIDS_REQUESTED}, - {"NT_STATUS_GUIDS_EXHAUSTED", NT_STATUS_GUIDS_EXHAUSTED}, - {"NT_STATUS_INVALID_ID_AUTHORITY", NT_STATUS_INVALID_ID_AUTHORITY}, - {"NT_STATUS_AGENTS_EXHAUSTED", NT_STATUS_AGENTS_EXHAUSTED}, - {"NT_STATUS_INVALID_VOLUME_LABEL", NT_STATUS_INVALID_VOLUME_LABEL}, - {"NT_STATUS_SECTION_NOT_EXTENDED", NT_STATUS_SECTION_NOT_EXTENDED}, - {"NT_STATUS_NOT_MAPPED_DATA", NT_STATUS_NOT_MAPPED_DATA}, - {"NT_STATUS_RESOURCE_DATA_NOT_FOUND", - NT_STATUS_RESOURCE_DATA_NOT_FOUND}, - {"NT_STATUS_RESOURCE_TYPE_NOT_FOUND", - NT_STATUS_RESOURCE_TYPE_NOT_FOUND}, - {"NT_STATUS_RESOURCE_NAME_NOT_FOUND", - NT_STATUS_RESOURCE_NAME_NOT_FOUND}, - {"NT_STATUS_ARRAY_BOUNDS_EXCEEDED", - NT_STATUS_ARRAY_BOUNDS_EXCEEDED}, - {"NT_STATUS_FLOAT_DENORMAL_OPERAND", - NT_STATUS_FLOAT_DENORMAL_OPERAND}, - {"NT_STATUS_FLOAT_DIVIDE_BY_ZERO", NT_STATUS_FLOAT_DIVIDE_BY_ZERO}, - {"NT_STATUS_FLOAT_INEXACT_RESULT", NT_STATUS_FLOAT_INEXACT_RESULT}, - {"NT_STATUS_FLOAT_INVALID_OPERATION", - NT_STATUS_FLOAT_INVALID_OPERATION}, - {"NT_STATUS_FLOAT_OVERFLOW", NT_STATUS_FLOAT_OVERFLOW}, - {"NT_STATUS_FLOAT_STACK_CHECK", NT_STATUS_FLOAT_STACK_CHECK}, - {"NT_STATUS_FLOAT_UNDERFLOW", NT_STATUS_FLOAT_UNDERFLOW}, - {"NT_STATUS_INTEGER_DIVIDE_BY_ZERO", - NT_STATUS_INTEGER_DIVIDE_BY_ZERO}, - {"NT_STATUS_INTEGER_OVERFLOW", NT_STATUS_INTEGER_OVERFLOW}, - {"NT_STATUS_PRIVILEGED_INSTRUCTION", - NT_STATUS_PRIVILEGED_INSTRUCTION}, - {"NT_STATUS_TOO_MANY_PAGING_FILES", - NT_STATUS_TOO_MANY_PAGING_FILES}, - {"NT_STATUS_FILE_INVALID", NT_STATUS_FILE_INVALID}, - {"NT_STATUS_ALLOTTED_SPACE_EXCEEDED", - NT_STATUS_ALLOTTED_SPACE_EXCEEDED}, - {"NT_STATUS_INSUFFICIENT_RESOURCES", - NT_STATUS_INSUFFICIENT_RESOURCES}, - {"NT_STATUS_DFS_EXIT_PATH_FOUND", NT_STATUS_DFS_EXIT_PATH_FOUND}, - {"NT_STATUS_DEVICE_DATA_ERROR", NT_STATUS_DEVICE_DATA_ERROR}, - {"NT_STATUS_DEVICE_NOT_CONNECTED", NT_STATUS_DEVICE_NOT_CONNECTED}, - {"NT_STATUS_DEVICE_POWER_FAILURE", NT_STATUS_DEVICE_POWER_FAILURE}, - {"NT_STATUS_FREE_VM_NOT_AT_BASE", NT_STATUS_FREE_VM_NOT_AT_BASE}, - {"NT_STATUS_MEMORY_NOT_ALLOCATED", NT_STATUS_MEMORY_NOT_ALLOCATED}, - {"NT_STATUS_WORKING_SET_QUOTA", NT_STATUS_WORKING_SET_QUOTA}, - {"NT_STATUS_MEDIA_WRITE_PROTECTED", - NT_STATUS_MEDIA_WRITE_PROTECTED}, - {"NT_STATUS_DEVICE_NOT_READY", NT_STATUS_DEVICE_NOT_READY}, - {"NT_STATUS_INVALID_GROUP_ATTRIBUTES", - NT_STATUS_INVALID_GROUP_ATTRIBUTES}, - {"NT_STATUS_BAD_IMPERSONATION_LEVEL", - NT_STATUS_BAD_IMPERSONATION_LEVEL}, - {"NT_STATUS_CANT_OPEN_ANONYMOUS", NT_STATUS_CANT_OPEN_ANONYMOUS}, - {"NT_STATUS_BAD_VALIDATION_CLASS", NT_STATUS_BAD_VALIDATION_CLASS}, - {"NT_STATUS_BAD_TOKEN_TYPE", NT_STATUS_BAD_TOKEN_TYPE}, - {"NT_STATUS_BAD_MASTER_BOOT_RECORD", - NT_STATUS_BAD_MASTER_BOOT_RECORD}, - {"NT_STATUS_INSTRUCTION_MISALIGNMENT", - NT_STATUS_INSTRUCTION_MISALIGNMENT}, - {"NT_STATUS_INSTANCE_NOT_AVAILABLE", - NT_STATUS_INSTANCE_NOT_AVAILABLE}, - {"NT_STATUS_PIPE_NOT_AVAILABLE", NT_STATUS_PIPE_NOT_AVAILABLE}, - {"NT_STATUS_INVALID_PIPE_STATE", NT_STATUS_INVALID_PIPE_STATE}, - {"NT_STATUS_PIPE_BUSY", NT_STATUS_PIPE_BUSY}, - {"NT_STATUS_ILLEGAL_FUNCTION", NT_STATUS_ILLEGAL_FUNCTION}, - {"NT_STATUS_PIPE_DISCONNECTED", NT_STATUS_PIPE_DISCONNECTED}, - {"NT_STATUS_PIPE_CLOSING", NT_STATUS_PIPE_CLOSING}, - {"NT_STATUS_PIPE_CONNECTED", NT_STATUS_PIPE_CONNECTED}, - {"NT_STATUS_PIPE_LISTENING", NT_STATUS_PIPE_LISTENING}, - {"NT_STATUS_INVALID_READ_MODE", NT_STATUS_INVALID_READ_MODE}, - {"NT_STATUS_IO_TIMEOUT", NT_STATUS_IO_TIMEOUT}, - {"NT_STATUS_FILE_FORCED_CLOSED", NT_STATUS_FILE_FORCED_CLOSED}, - {"NT_STATUS_PROFILING_NOT_STARTED", - NT_STATUS_PROFILING_NOT_STARTED}, - {"NT_STATUS_PROFILING_NOT_STOPPED", - NT_STATUS_PROFILING_NOT_STOPPED}, - {"NT_STATUS_COULD_NOT_INTERPRET", NT_STATUS_COULD_NOT_INTERPRET}, - {"NT_STATUS_FILE_IS_A_DIRECTORY", NT_STATUS_FILE_IS_A_DIRECTORY}, - {"NT_STATUS_NOT_SUPPORTED", NT_STATUS_NOT_SUPPORTED}, - {"NT_STATUS_REMOTE_NOT_LISTENING", NT_STATUS_REMOTE_NOT_LISTENING}, - {"NT_STATUS_DUPLICATE_NAME", NT_STATUS_DUPLICATE_NAME}, - {"NT_STATUS_BAD_NETWORK_PATH", NT_STATUS_BAD_NETWORK_PATH}, - {"NT_STATUS_NETWORK_BUSY", NT_STATUS_NETWORK_BUSY}, - {"NT_STATUS_DEVICE_DOES_NOT_EXIST", - NT_STATUS_DEVICE_DOES_NOT_EXIST}, - {"NT_STATUS_TOO_MANY_COMMANDS", NT_STATUS_TOO_MANY_COMMANDS}, - {"NT_STATUS_ADAPTER_HARDWARE_ERROR", - NT_STATUS_ADAPTER_HARDWARE_ERROR}, - {"NT_STATUS_INVALID_NETWORK_RESPONSE", - NT_STATUS_INVALID_NETWORK_RESPONSE}, - {"NT_STATUS_UNEXPECTED_NETWORK_ERROR", - NT_STATUS_UNEXPECTED_NETWORK_ERROR}, - {"NT_STATUS_BAD_REMOTE_ADAPTER", NT_STATUS_BAD_REMOTE_ADAPTER}, - {"NT_STATUS_PRINT_QUEUE_FULL", NT_STATUS_PRINT_QUEUE_FULL}, - {"NT_STATUS_NO_SPOOL_SPACE", NT_STATUS_NO_SPOOL_SPACE}, - {"NT_STATUS_PRINT_CANCELLED", NT_STATUS_PRINT_CANCELLED}, - {"NT_STATUS_NETWORK_NAME_DELETED", NT_STATUS_NETWORK_NAME_DELETED}, - {"NT_STATUS_NETWORK_ACCESS_DENIED", - NT_STATUS_NETWORK_ACCESS_DENIED}, - {"NT_STATUS_BAD_DEVICE_TYPE", NT_STATUS_BAD_DEVICE_TYPE}, - {"NT_STATUS_BAD_NETWORK_NAME", NT_STATUS_BAD_NETWORK_NAME}, - {"NT_STATUS_TOO_MANY_NAMES", NT_STATUS_TOO_MANY_NAMES}, - {"NT_STATUS_TOO_MANY_SESSIONS", NT_STATUS_TOO_MANY_SESSIONS}, - {"NT_STATUS_SHARING_PAUSED", NT_STATUS_SHARING_PAUSED}, - {"NT_STATUS_REQUEST_NOT_ACCEPTED", NT_STATUS_REQUEST_NOT_ACCEPTED}, - {"NT_STATUS_REDIRECTOR_PAUSED", NT_STATUS_REDIRECTOR_PAUSED}, - {"NT_STATUS_NET_WRITE_FAULT", NT_STATUS_NET_WRITE_FAULT}, - {"NT_STATUS_PROFILING_AT_LIMIT", NT_STATUS_PROFILING_AT_LIMIT}, - {"NT_STATUS_NOT_SAME_DEVICE", NT_STATUS_NOT_SAME_DEVICE}, - {"NT_STATUS_FILE_RENAMED", NT_STATUS_FILE_RENAMED}, - {"NT_STATUS_VIRTUAL_CIRCUIT_CLOSED", - NT_STATUS_VIRTUAL_CIRCUIT_CLOSED}, - {"NT_STATUS_NO_SECURITY_ON_OBJECT", - NT_STATUS_NO_SECURITY_ON_OBJECT}, - {"NT_STATUS_CANT_WAIT", NT_STATUS_CANT_WAIT}, - {"NT_STATUS_PIPE_EMPTY", NT_STATUS_PIPE_EMPTY}, - {"NT_STATUS_CANT_ACCESS_DOMAIN_INFO", - NT_STATUS_CANT_ACCESS_DOMAIN_INFO}, - {"NT_STATUS_CANT_TERMINATE_SELF", NT_STATUS_CANT_TERMINATE_SELF}, - {"NT_STATUS_INVALID_SERVER_STATE", NT_STATUS_INVALID_SERVER_STATE}, - {"NT_STATUS_INVALID_DOMAIN_STATE", NT_STATUS_INVALID_DOMAIN_STATE}, - {"NT_STATUS_INVALID_DOMAIN_ROLE", NT_STATUS_INVALID_DOMAIN_ROLE}, - {"NT_STATUS_NO_SUCH_DOMAIN", NT_STATUS_NO_SUCH_DOMAIN}, - {"NT_STATUS_DOMAIN_EXISTS", NT_STATUS_DOMAIN_EXISTS}, - {"NT_STATUS_DOMAIN_LIMIT_EXCEEDED", - NT_STATUS_DOMAIN_LIMIT_EXCEEDED}, - {"NT_STATUS_OPLOCK_NOT_GRANTED", NT_STATUS_OPLOCK_NOT_GRANTED}, - {"NT_STATUS_INVALID_OPLOCK_PROTOCOL", - NT_STATUS_INVALID_OPLOCK_PROTOCOL}, - {"NT_STATUS_INTERNAL_DB_CORRUPTION", - NT_STATUS_INTERNAL_DB_CORRUPTION}, - {"NT_STATUS_INTERNAL_ERROR", NT_STATUS_INTERNAL_ERROR}, - {"NT_STATUS_GENERIC_NOT_MAPPED", NT_STATUS_GENERIC_NOT_MAPPED}, - {"NT_STATUS_BAD_DESCRIPTOR_FORMAT", - NT_STATUS_BAD_DESCRIPTOR_FORMAT}, - {"NT_STATUS_INVALID_USER_BUFFER", NT_STATUS_INVALID_USER_BUFFER}, - {"NT_STATUS_UNEXPECTED_IO_ERROR", NT_STATUS_UNEXPECTED_IO_ERROR}, - {"NT_STATUS_UNEXPECTED_MM_CREATE_ERR", - NT_STATUS_UNEXPECTED_MM_CREATE_ERR}, - {"NT_STATUS_UNEXPECTED_MM_MAP_ERROR", - NT_STATUS_UNEXPECTED_MM_MAP_ERROR}, - {"NT_STATUS_UNEXPECTED_MM_EXTEND_ERR", - NT_STATUS_UNEXPECTED_MM_EXTEND_ERR}, - {"NT_STATUS_NOT_LOGON_PROCESS", NT_STATUS_NOT_LOGON_PROCESS}, - {"NT_STATUS_LOGON_SESSION_EXISTS", NT_STATUS_LOGON_SESSION_EXISTS}, - {"NT_STATUS_INVALID_PARAMETER_1", NT_STATUS_INVALID_PARAMETER_1}, - {"NT_STATUS_INVALID_PARAMETER_2", NT_STATUS_INVALID_PARAMETER_2}, - {"NT_STATUS_INVALID_PARAMETER_3", NT_STATUS_INVALID_PARAMETER_3}, - {"NT_STATUS_INVALID_PARAMETER_4", NT_STATUS_INVALID_PARAMETER_4}, - {"NT_STATUS_INVALID_PARAMETER_5", NT_STATUS_INVALID_PARAMETER_5}, - {"NT_STATUS_INVALID_PARAMETER_6", NT_STATUS_INVALID_PARAMETER_6}, - {"NT_STATUS_INVALID_PARAMETER_7", NT_STATUS_INVALID_PARAMETER_7}, - {"NT_STATUS_INVALID_PARAMETER_8", NT_STATUS_INVALID_PARAMETER_8}, - {"NT_STATUS_INVALID_PARAMETER_9", NT_STATUS_INVALID_PARAMETER_9}, - {"NT_STATUS_INVALID_PARAMETER_10", NT_STATUS_INVALID_PARAMETER_10}, - {"NT_STATUS_INVALID_PARAMETER_11", NT_STATUS_INVALID_PARAMETER_11}, - {"NT_STATUS_INVALID_PARAMETER_12", NT_STATUS_INVALID_PARAMETER_12}, - {"NT_STATUS_REDIRECTOR_NOT_STARTED", - NT_STATUS_REDIRECTOR_NOT_STARTED}, - {"NT_STATUS_REDIRECTOR_STARTED", NT_STATUS_REDIRECTOR_STARTED}, - {"NT_STATUS_STACK_OVERFLOW", NT_STATUS_STACK_OVERFLOW}, - {"NT_STATUS_NO_SUCH_PACKAGE", NT_STATUS_NO_SUCH_PACKAGE}, - {"NT_STATUS_BAD_FUNCTION_TABLE", NT_STATUS_BAD_FUNCTION_TABLE}, - {"NT_STATUS_DIRECTORY_NOT_EMPTY", NT_STATUS_DIRECTORY_NOT_EMPTY}, - {"NT_STATUS_FILE_CORRUPT_ERROR", NT_STATUS_FILE_CORRUPT_ERROR}, - {"NT_STATUS_NOT_A_DIRECTORY", NT_STATUS_NOT_A_DIRECTORY}, - {"NT_STATUS_BAD_LOGON_SESSION_STATE", - NT_STATUS_BAD_LOGON_SESSION_STATE}, - {"NT_STATUS_LOGON_SESSION_COLLISION", - NT_STATUS_LOGON_SESSION_COLLISION}, - {"NT_STATUS_NAME_TOO_LONG", NT_STATUS_NAME_TOO_LONG}, - {"NT_STATUS_FILES_OPEN", NT_STATUS_FILES_OPEN}, - {"NT_STATUS_CONNECTION_IN_USE", NT_STATUS_CONNECTION_IN_USE}, - {"NT_STATUS_MESSAGE_NOT_FOUND", NT_STATUS_MESSAGE_NOT_FOUND}, - {"NT_STATUS_PROCESS_IS_TERMINATING", - NT_STATUS_PROCESS_IS_TERMINATING}, - {"NT_STATUS_INVALID_LOGON_TYPE", NT_STATUS_INVALID_LOGON_TYPE}, - {"NT_STATUS_NO_GUID_TRANSLATION", NT_STATUS_NO_GUID_TRANSLATION}, - {"NT_STATUS_CANNOT_IMPERSONATE", NT_STATUS_CANNOT_IMPERSONATE}, - {"NT_STATUS_IMAGE_ALREADY_LOADED", NT_STATUS_IMAGE_ALREADY_LOADED}, - {"NT_STATUS_ABIOS_NOT_PRESENT", NT_STATUS_ABIOS_NOT_PRESENT}, - {"NT_STATUS_ABIOS_LID_NOT_EXIST", NT_STATUS_ABIOS_LID_NOT_EXIST}, - {"NT_STATUS_ABIOS_LID_ALREADY_OWNED", - NT_STATUS_ABIOS_LID_ALREADY_OWNED}, - {"NT_STATUS_ABIOS_NOT_LID_OWNER", NT_STATUS_ABIOS_NOT_LID_OWNER}, - {"NT_STATUS_ABIOS_INVALID_COMMAND", - NT_STATUS_ABIOS_INVALID_COMMAND}, - {"NT_STATUS_ABIOS_INVALID_LID", NT_STATUS_ABIOS_INVALID_LID}, - {"NT_STATUS_ABIOS_SELECTOR_NOT_AVAILABLE", - NT_STATUS_ABIOS_SELECTOR_NOT_AVAILABLE}, - {"NT_STATUS_ABIOS_INVALID_SELECTOR", - NT_STATUS_ABIOS_INVALID_SELECTOR}, - {"NT_STATUS_NO_LDT", NT_STATUS_NO_LDT}, - {"NT_STATUS_INVALID_LDT_SIZE", NT_STATUS_INVALID_LDT_SIZE}, - {"NT_STATUS_INVALID_LDT_OFFSET", NT_STATUS_INVALID_LDT_OFFSET}, - {"NT_STATUS_INVALID_LDT_DESCRIPTOR", - NT_STATUS_INVALID_LDT_DESCRIPTOR}, - {"NT_STATUS_INVALID_IMAGE_NE_FORMAT", - NT_STATUS_INVALID_IMAGE_NE_FORMAT}, - {"NT_STATUS_RXACT_INVALID_STATE", NT_STATUS_RXACT_INVALID_STATE}, - {"NT_STATUS_RXACT_COMMIT_FAILURE", NT_STATUS_RXACT_COMMIT_FAILURE}, - {"NT_STATUS_MAPPED_FILE_SIZE_ZERO", - NT_STATUS_MAPPED_FILE_SIZE_ZERO}, - {"NT_STATUS_TOO_MANY_OPENED_FILES", - NT_STATUS_TOO_MANY_OPENED_FILES}, - {"NT_STATUS_CANCELLED", NT_STATUS_CANCELLED}, - {"NT_STATUS_CANNOT_DELETE", NT_STATUS_CANNOT_DELETE}, - {"NT_STATUS_INVALID_COMPUTER_NAME", - NT_STATUS_INVALID_COMPUTER_NAME}, - {"NT_STATUS_FILE_DELETED", NT_STATUS_FILE_DELETED}, - {"NT_STATUS_SPECIAL_ACCOUNT", NT_STATUS_SPECIAL_ACCOUNT}, - {"NT_STATUS_SPECIAL_GROUP", NT_STATUS_SPECIAL_GROUP}, - {"NT_STATUS_SPECIAL_USER", NT_STATUS_SPECIAL_USER}, - {"NT_STATUS_MEMBERS_PRIMARY_GROUP", - NT_STATUS_MEMBERS_PRIMARY_GROUP}, - {"NT_STATUS_FILE_CLOSED", NT_STATUS_FILE_CLOSED}, - {"NT_STATUS_TOO_MANY_THREADS", NT_STATUS_TOO_MANY_THREADS}, - {"NT_STATUS_THREAD_NOT_IN_PROCESS", - NT_STATUS_THREAD_NOT_IN_PROCESS}, - {"NT_STATUS_TOKEN_ALREADY_IN_USE", NT_STATUS_TOKEN_ALREADY_IN_USE}, - {"NT_STATUS_PAGEFILE_QUOTA_EXCEEDED", - NT_STATUS_PAGEFILE_QUOTA_EXCEEDED}, - {"NT_STATUS_COMMITMENT_LIMIT", NT_STATUS_COMMITMENT_LIMIT}, - {"NT_STATUS_INVALID_IMAGE_LE_FORMAT", - NT_STATUS_INVALID_IMAGE_LE_FORMAT}, - {"NT_STATUS_INVALID_IMAGE_NOT_MZ", NT_STATUS_INVALID_IMAGE_NOT_MZ}, - {"NT_STATUS_INVALID_IMAGE_PROTECT", - NT_STATUS_INVALID_IMAGE_PROTECT}, - {"NT_STATUS_INVALID_IMAGE_WIN_16", NT_STATUS_INVALID_IMAGE_WIN_16}, - {"NT_STATUS_LOGON_SERVER_CONFLICT", - NT_STATUS_LOGON_SERVER_CONFLICT}, - {"NT_STATUS_TIME_DIFFERENCE_AT_DC", - NT_STATUS_TIME_DIFFERENCE_AT_DC}, - {"NT_STATUS_SYNCHRONIZATION_REQUIRED", - NT_STATUS_SYNCHRONIZATION_REQUIRED}, - {"NT_STATUS_DLL_NOT_FOUND", NT_STATUS_DLL_NOT_FOUND}, - {"NT_STATUS_OPEN_FAILED", NT_STATUS_OPEN_FAILED}, - {"NT_STATUS_IO_PRIVILEGE_FAILED", NT_STATUS_IO_PRIVILEGE_FAILED}, - {"NT_STATUS_ORDINAL_NOT_FOUND", NT_STATUS_ORDINAL_NOT_FOUND}, - {"NT_STATUS_ENTRYPOINT_NOT_FOUND", NT_STATUS_ENTRYPOINT_NOT_FOUND}, - {"NT_STATUS_CONTROL_C_EXIT", NT_STATUS_CONTROL_C_EXIT}, - {"NT_STATUS_LOCAL_DISCONNECT", NT_STATUS_LOCAL_DISCONNECT}, - {"NT_STATUS_REMOTE_DISCONNECT", NT_STATUS_REMOTE_DISCONNECT}, - {"NT_STATUS_REMOTE_RESOURCES", NT_STATUS_REMOTE_RESOURCES}, - {"NT_STATUS_LINK_FAILED", NT_STATUS_LINK_FAILED}, - {"NT_STATUS_LINK_TIMEOUT", NT_STATUS_LINK_TIMEOUT}, - {"NT_STATUS_INVALID_CONNECTION", NT_STATUS_INVALID_CONNECTION}, - {"NT_STATUS_INVALID_ADDRESS", NT_STATUS_INVALID_ADDRESS}, - {"NT_STATUS_DLL_INIT_FAILED", NT_STATUS_DLL_INIT_FAILED}, - {"NT_STATUS_MISSING_SYSTEMFILE", NT_STATUS_MISSING_SYSTEMFILE}, - {"NT_STATUS_UNHANDLED_EXCEPTION", NT_STATUS_UNHANDLED_EXCEPTION}, - {"NT_STATUS_APP_INIT_FAILURE", NT_STATUS_APP_INIT_FAILURE}, - {"NT_STATUS_PAGEFILE_CREATE_FAILED", - NT_STATUS_PAGEFILE_CREATE_FAILED}, - {"NT_STATUS_NO_PAGEFILE", NT_STATUS_NO_PAGEFILE}, - {"NT_STATUS_INVALID_LEVEL", NT_STATUS_INVALID_LEVEL}, - {"NT_STATUS_WRONG_PASSWORD_CORE", NT_STATUS_WRONG_PASSWORD_CORE}, - {"NT_STATUS_ILLEGAL_FLOAT_CONTEXT", - NT_STATUS_ILLEGAL_FLOAT_CONTEXT}, - {"NT_STATUS_PIPE_BROKEN", NT_STATUS_PIPE_BROKEN}, - {"NT_STATUS_REGISTRY_CORRUPT", NT_STATUS_REGISTRY_CORRUPT}, - {"NT_STATUS_REGISTRY_IO_FAILED", NT_STATUS_REGISTRY_IO_FAILED}, - {"NT_STATUS_NO_EVENT_PAIR", NT_STATUS_NO_EVENT_PAIR}, - {"NT_STATUS_UNRECOGNIZED_VOLUME", NT_STATUS_UNRECOGNIZED_VOLUME}, - {"NT_STATUS_SERIAL_NO_DEVICE_INITED", - NT_STATUS_SERIAL_NO_DEVICE_INITED}, - {"NT_STATUS_NO_SUCH_ALIAS", NT_STATUS_NO_SUCH_ALIAS}, - {"NT_STATUS_MEMBER_NOT_IN_ALIAS", NT_STATUS_MEMBER_NOT_IN_ALIAS}, - {"NT_STATUS_MEMBER_IN_ALIAS", NT_STATUS_MEMBER_IN_ALIAS}, - {"NT_STATUS_ALIAS_EXISTS", NT_STATUS_ALIAS_EXISTS}, - {"NT_STATUS_LOGON_NOT_GRANTED", NT_STATUS_LOGON_NOT_GRANTED}, - {"NT_STATUS_TOO_MANY_SECRETS", NT_STATUS_TOO_MANY_SECRETS}, - {"NT_STATUS_SECRET_TOO_LONG", NT_STATUS_SECRET_TOO_LONG}, - {"NT_STATUS_INTERNAL_DB_ERROR", NT_STATUS_INTERNAL_DB_ERROR}, - {"NT_STATUS_FULLSCREEN_MODE", NT_STATUS_FULLSCREEN_MODE}, - {"NT_STATUS_TOO_MANY_CONTEXT_IDS", NT_STATUS_TOO_MANY_CONTEXT_IDS}, - {"NT_STATUS_LOGON_TYPE_NOT_GRANTED", - NT_STATUS_LOGON_TYPE_NOT_GRANTED}, - {"NT_STATUS_NOT_REGISTRY_FILE", NT_STATUS_NOT_REGISTRY_FILE}, - {"NT_STATUS_NT_CROSS_ENCRYPTION_REQUIRED", - NT_STATUS_NT_CROSS_ENCRYPTION_REQUIRED}, - {"NT_STATUS_DOMAIN_CTRLR_CONFIG_ERROR", - NT_STATUS_DOMAIN_CTRLR_CONFIG_ERROR}, - {"NT_STATUS_FT_MISSING_MEMBER", NT_STATUS_FT_MISSING_MEMBER}, - {"NT_STATUS_ILL_FORMED_SERVICE_ENTRY", - NT_STATUS_ILL_FORMED_SERVICE_ENTRY}, - {"NT_STATUS_ILLEGAL_CHARACTER", NT_STATUS_ILLEGAL_CHARACTER}, - {"NT_STATUS_UNMAPPABLE_CHARACTER", NT_STATUS_UNMAPPABLE_CHARACTER}, - {"NT_STATUS_UNDEFINED_CHARACTER", NT_STATUS_UNDEFINED_CHARACTER}, - {"NT_STATUS_FLOPPY_VOLUME", NT_STATUS_FLOPPY_VOLUME}, - {"NT_STATUS_FLOPPY_ID_MARK_NOT_FOUND", - NT_STATUS_FLOPPY_ID_MARK_NOT_FOUND}, - {"NT_STATUS_FLOPPY_WRONG_CYLINDER", - NT_STATUS_FLOPPY_WRONG_CYLINDER}, - {"NT_STATUS_FLOPPY_UNKNOWN_ERROR", NT_STATUS_FLOPPY_UNKNOWN_ERROR}, - {"NT_STATUS_FLOPPY_BAD_REGISTERS", NT_STATUS_FLOPPY_BAD_REGISTERS}, - {"NT_STATUS_DISK_RECALIBRATE_FAILED", - NT_STATUS_DISK_RECALIBRATE_FAILED}, - {"NT_STATUS_DISK_OPERATION_FAILED", - NT_STATUS_DISK_OPERATION_FAILED}, - {"NT_STATUS_DISK_RESET_FAILED", NT_STATUS_DISK_RESET_FAILED}, - {"NT_STATUS_SHARED_IRQ_BUSY", NT_STATUS_SHARED_IRQ_BUSY}, - {"NT_STATUS_FT_ORPHANING", NT_STATUS_FT_ORPHANING}, - {"NT_STATUS_PARTITION_FAILURE", NT_STATUS_PARTITION_FAILURE}, - {"NT_STATUS_INVALID_BLOCK_LENGTH", NT_STATUS_INVALID_BLOCK_LENGTH}, - {"NT_STATUS_DEVICE_NOT_PARTITIONED", - NT_STATUS_DEVICE_NOT_PARTITIONED}, - {"NT_STATUS_UNABLE_TO_LOCK_MEDIA", NT_STATUS_UNABLE_TO_LOCK_MEDIA}, - {"NT_STATUS_UNABLE_TO_UNLOAD_MEDIA", - NT_STATUS_UNABLE_TO_UNLOAD_MEDIA}, - {"NT_STATUS_EOM_OVERFLOW", NT_STATUS_EOM_OVERFLOW}, - {"NT_STATUS_NO_MEDIA", NT_STATUS_NO_MEDIA}, - {"NT_STATUS_NO_SUCH_MEMBER", NT_STATUS_NO_SUCH_MEMBER}, - {"NT_STATUS_INVALID_MEMBER", NT_STATUS_INVALID_MEMBER}, - {"NT_STATUS_KEY_DELETED", NT_STATUS_KEY_DELETED}, - {"NT_STATUS_NO_LOG_SPACE", NT_STATUS_NO_LOG_SPACE}, - {"NT_STATUS_TOO_MANY_SIDS", NT_STATUS_TOO_MANY_SIDS}, - {"NT_STATUS_LM_CROSS_ENCRYPTION_REQUIRED", - NT_STATUS_LM_CROSS_ENCRYPTION_REQUIRED}, - {"NT_STATUS_KEY_HAS_CHILDREN", NT_STATUS_KEY_HAS_CHILDREN}, - {"NT_STATUS_CHILD_MUST_BE_VOLATILE", - NT_STATUS_CHILD_MUST_BE_VOLATILE}, - {"NT_STATUS_DEVICE_CONFIGURATION_ERROR", - NT_STATUS_DEVICE_CONFIGURATION_ERROR}, - {"NT_STATUS_DRIVER_INTERNAL_ERROR", - NT_STATUS_DRIVER_INTERNAL_ERROR}, - {"NT_STATUS_INVALID_DEVICE_STATE", NT_STATUS_INVALID_DEVICE_STATE}, - {"NT_STATUS_IO_DEVICE_ERROR", NT_STATUS_IO_DEVICE_ERROR}, - {"NT_STATUS_DEVICE_PROTOCOL_ERROR", - NT_STATUS_DEVICE_PROTOCOL_ERROR}, - {"NT_STATUS_BACKUP_CONTROLLER", NT_STATUS_BACKUP_CONTROLLER}, - {"NT_STATUS_LOG_FILE_FULL", NT_STATUS_LOG_FILE_FULL}, - {"NT_STATUS_TOO_LATE", NT_STATUS_TOO_LATE}, - {"NT_STATUS_NO_TRUST_LSA_SECRET", NT_STATUS_NO_TRUST_LSA_SECRET}, - {"NT_STATUS_NO_TRUST_SAM_ACCOUNT", NT_STATUS_NO_TRUST_SAM_ACCOUNT}, - {"NT_STATUS_TRUSTED_DOMAIN_FAILURE", - NT_STATUS_TRUSTED_DOMAIN_FAILURE}, - {"NT_STATUS_TRUSTED_RELATIONSHIP_FAILURE", - NT_STATUS_TRUSTED_RELATIONSHIP_FAILURE}, - {"NT_STATUS_EVENTLOG_FILE_CORRUPT", - NT_STATUS_EVENTLOG_FILE_CORRUPT}, - {"NT_STATUS_EVENTLOG_CANT_START", NT_STATUS_EVENTLOG_CANT_START}, - {"NT_STATUS_TRUST_FAILURE", NT_STATUS_TRUST_FAILURE}, - {"NT_STATUS_MUTANT_LIMIT_EXCEEDED", - NT_STATUS_MUTANT_LIMIT_EXCEEDED}, - {"NT_STATUS_NETLOGON_NOT_STARTED", NT_STATUS_NETLOGON_NOT_STARTED}, - {"NT_STATUS_ACCOUNT_EXPIRED", NT_STATUS_ACCOUNT_EXPIRED}, - {"NT_STATUS_POSSIBLE_DEADLOCK", NT_STATUS_POSSIBLE_DEADLOCK}, - {"NT_STATUS_NETWORK_CREDENTIAL_CONFLICT", - NT_STATUS_NETWORK_CREDENTIAL_CONFLICT}, - {"NT_STATUS_REMOTE_SESSION_LIMIT", NT_STATUS_REMOTE_SESSION_LIMIT}, - {"NT_STATUS_EVENTLOG_FILE_CHANGED", - NT_STATUS_EVENTLOG_FILE_CHANGED}, - {"NT_STATUS_NOLOGON_INTERDOMAIN_TRUST_ACCOUNT", - NT_STATUS_NOLOGON_INTERDOMAIN_TRUST_ACCOUNT}, - {"NT_STATUS_NOLOGON_WORKSTATION_TRUST_ACCOUNT", - NT_STATUS_NOLOGON_WORKSTATION_TRUST_ACCOUNT}, - {"NT_STATUS_NOLOGON_SERVER_TRUST_ACCOUNT", - NT_STATUS_NOLOGON_SERVER_TRUST_ACCOUNT}, - {"NT_STATUS_DOMAIN_TRUST_INCONSISTENT", - NT_STATUS_DOMAIN_TRUST_INCONSISTENT}, - {"NT_STATUS_FS_DRIVER_REQUIRED", NT_STATUS_FS_DRIVER_REQUIRED}, - {"NT_STATUS_NO_USER_SESSION_KEY", NT_STATUS_NO_USER_SESSION_KEY}, - {"NT_STATUS_USER_SESSION_DELETED", NT_STATUS_USER_SESSION_DELETED}, - {"NT_STATUS_RESOURCE_LANG_NOT_FOUND", - NT_STATUS_RESOURCE_LANG_NOT_FOUND}, - {"NT_STATUS_INSUFF_SERVER_RESOURCES", - NT_STATUS_INSUFF_SERVER_RESOURCES}, - {"NT_STATUS_INVALID_BUFFER_SIZE", NT_STATUS_INVALID_BUFFER_SIZE}, - {"NT_STATUS_INVALID_ADDRESS_COMPONENT", - NT_STATUS_INVALID_ADDRESS_COMPONENT}, - {"NT_STATUS_INVALID_ADDRESS_WILDCARD", - NT_STATUS_INVALID_ADDRESS_WILDCARD}, - {"NT_STATUS_TOO_MANY_ADDRESSES", NT_STATUS_TOO_MANY_ADDRESSES}, - {"NT_STATUS_ADDRESS_ALREADY_EXISTS", - NT_STATUS_ADDRESS_ALREADY_EXISTS}, - {"NT_STATUS_ADDRESS_CLOSED", NT_STATUS_ADDRESS_CLOSED}, - {"NT_STATUS_CONNECTION_DISCONNECTED", - NT_STATUS_CONNECTION_DISCONNECTED}, - {"NT_STATUS_CONNECTION_RESET", NT_STATUS_CONNECTION_RESET}, - {"NT_STATUS_TOO_MANY_NODES", NT_STATUS_TOO_MANY_NODES}, - {"NT_STATUS_TRANSACTION_ABORTED", NT_STATUS_TRANSACTION_ABORTED}, - {"NT_STATUS_TRANSACTION_TIMED_OUT", - NT_STATUS_TRANSACTION_TIMED_OUT}, - {"NT_STATUS_TRANSACTION_NO_RELEASE", - NT_STATUS_TRANSACTION_NO_RELEASE}, - {"NT_STATUS_TRANSACTION_NO_MATCH", NT_STATUS_TRANSACTION_NO_MATCH}, - {"NT_STATUS_TRANSACTION_RESPONDED", - NT_STATUS_TRANSACTION_RESPONDED}, - {"NT_STATUS_TRANSACTION_INVALID_ID", - NT_STATUS_TRANSACTION_INVALID_ID}, - {"NT_STATUS_TRANSACTION_INVALID_TYPE", - NT_STATUS_TRANSACTION_INVALID_TYPE}, - {"NT_STATUS_NOT_SERVER_SESSION", NT_STATUS_NOT_SERVER_SESSION}, - {"NT_STATUS_NOT_CLIENT_SESSION", NT_STATUS_NOT_CLIENT_SESSION}, - {"NT_STATUS_CANNOT_LOAD_REGISTRY_FILE", - NT_STATUS_CANNOT_LOAD_REGISTRY_FILE}, - {"NT_STATUS_DEBUG_ATTACH_FAILED", NT_STATUS_DEBUG_ATTACH_FAILED}, - {"NT_STATUS_SYSTEM_PROCESS_TERMINATED", - NT_STATUS_SYSTEM_PROCESS_TERMINATED}, - {"NT_STATUS_DATA_NOT_ACCEPTED", NT_STATUS_DATA_NOT_ACCEPTED}, - {"NT_STATUS_NO_BROWSER_SERVERS_FOUND", - NT_STATUS_NO_BROWSER_SERVERS_FOUND}, - {"NT_STATUS_VDM_HARD_ERROR", NT_STATUS_VDM_HARD_ERROR}, - {"NT_STATUS_DRIVER_CANCEL_TIMEOUT", - NT_STATUS_DRIVER_CANCEL_TIMEOUT}, - {"NT_STATUS_REPLY_MESSAGE_MISMATCH", - NT_STATUS_REPLY_MESSAGE_MISMATCH}, - {"NT_STATUS_MAPPED_ALIGNMENT", NT_STATUS_MAPPED_ALIGNMENT}, - {"NT_STATUS_IMAGE_CHECKSUM_MISMATCH", - NT_STATUS_IMAGE_CHECKSUM_MISMATCH}, - {"NT_STATUS_LOST_WRITEBEHIND_DATA", - NT_STATUS_LOST_WRITEBEHIND_DATA}, - {"NT_STATUS_CLIENT_SERVER_PARAMETERS_INVALID", - NT_STATUS_CLIENT_SERVER_PARAMETERS_INVALID}, - {"NT_STATUS_PASSWORD_MUST_CHANGE", NT_STATUS_PASSWORD_MUST_CHANGE}, - {"NT_STATUS_NOT_FOUND", NT_STATUS_NOT_FOUND}, - {"NT_STATUS_NOT_TINY_STREAM", NT_STATUS_NOT_TINY_STREAM}, - {"NT_STATUS_RECOVERY_FAILURE", NT_STATUS_RECOVERY_FAILURE}, - {"NT_STATUS_STACK_OVERFLOW_READ", NT_STATUS_STACK_OVERFLOW_READ}, - {"NT_STATUS_FAIL_CHECK", NT_STATUS_FAIL_CHECK}, - {"NT_STATUS_DUPLICATE_OBJECTID", NT_STATUS_DUPLICATE_OBJECTID}, - {"NT_STATUS_OBJECTID_EXISTS", NT_STATUS_OBJECTID_EXISTS}, - {"NT_STATUS_CONVERT_TO_LARGE", NT_STATUS_CONVERT_TO_LARGE}, - {"NT_STATUS_RETRY", NT_STATUS_RETRY}, - {"NT_STATUS_FOUND_OUT_OF_SCOPE", NT_STATUS_FOUND_OUT_OF_SCOPE}, - {"NT_STATUS_ALLOCATE_BUCKET", NT_STATUS_ALLOCATE_BUCKET}, - {"NT_STATUS_PROPSET_NOT_FOUND", NT_STATUS_PROPSET_NOT_FOUND}, - {"NT_STATUS_MARSHALL_OVERFLOW", NT_STATUS_MARSHALL_OVERFLOW}, - {"NT_STATUS_INVALID_VARIANT", NT_STATUS_INVALID_VARIANT}, - {"NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND", - NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND}, - {"NT_STATUS_ACCOUNT_LOCKED_OUT", NT_STATUS_ACCOUNT_LOCKED_OUT}, - {"NT_STATUS_HANDLE_NOT_CLOSABLE", NT_STATUS_HANDLE_NOT_CLOSABLE}, - {"NT_STATUS_CONNECTION_REFUSED", NT_STATUS_CONNECTION_REFUSED}, - {"NT_STATUS_GRACEFUL_DISCONNECT", NT_STATUS_GRACEFUL_DISCONNECT}, - {"NT_STATUS_ADDRESS_ALREADY_ASSOCIATED", - NT_STATUS_ADDRESS_ALREADY_ASSOCIATED}, - {"NT_STATUS_ADDRESS_NOT_ASSOCIATED", - NT_STATUS_ADDRESS_NOT_ASSOCIATED}, - {"NT_STATUS_CONNECTION_INVALID", NT_STATUS_CONNECTION_INVALID}, - {"NT_STATUS_CONNECTION_ACTIVE", NT_STATUS_CONNECTION_ACTIVE}, - {"NT_STATUS_NETWORK_UNREACHABLE", NT_STATUS_NETWORK_UNREACHABLE}, - {"NT_STATUS_HOST_UNREACHABLE", NT_STATUS_HOST_UNREACHABLE}, - {"NT_STATUS_PROTOCOL_UNREACHABLE", NT_STATUS_PROTOCOL_UNREACHABLE}, - {"NT_STATUS_PORT_UNREACHABLE", NT_STATUS_PORT_UNREACHABLE}, - {"NT_STATUS_REQUEST_ABORTED", NT_STATUS_REQUEST_ABORTED}, - {"NT_STATUS_CONNECTION_ABORTED", NT_STATUS_CONNECTION_ABORTED}, - {"NT_STATUS_BAD_COMPRESSION_BUFFER", - NT_STATUS_BAD_COMPRESSION_BUFFER}, - {"NT_STATUS_USER_MAPPED_FILE", NT_STATUS_USER_MAPPED_FILE}, - {"NT_STATUS_AUDIT_FAILED", NT_STATUS_AUDIT_FAILED}, - {"NT_STATUS_TIMER_RESOLUTION_NOT_SET", - NT_STATUS_TIMER_RESOLUTION_NOT_SET}, - {"NT_STATUS_CONNECTION_COUNT_LIMIT", - NT_STATUS_CONNECTION_COUNT_LIMIT}, - {"NT_STATUS_LOGIN_TIME_RESTRICTION", - NT_STATUS_LOGIN_TIME_RESTRICTION}, - {"NT_STATUS_LOGIN_WKSTA_RESTRICTION", - NT_STATUS_LOGIN_WKSTA_RESTRICTION}, - {"NT_STATUS_IMAGE_MP_UP_MISMATCH", NT_STATUS_IMAGE_MP_UP_MISMATCH}, - {"NT_STATUS_INSUFFICIENT_LOGON_INFO", - NT_STATUS_INSUFFICIENT_LOGON_INFO}, - {"NT_STATUS_BAD_DLL_ENTRYPOINT", NT_STATUS_BAD_DLL_ENTRYPOINT}, - {"NT_STATUS_BAD_SERVICE_ENTRYPOINT", - NT_STATUS_BAD_SERVICE_ENTRYPOINT}, - {"NT_STATUS_LPC_REPLY_LOST", NT_STATUS_LPC_REPLY_LOST}, - {"NT_STATUS_IP_ADDRESS_CONFLICT1", NT_STATUS_IP_ADDRESS_CONFLICT1}, - {"NT_STATUS_IP_ADDRESS_CONFLICT2", NT_STATUS_IP_ADDRESS_CONFLICT2}, - {"NT_STATUS_REGISTRY_QUOTA_LIMIT", NT_STATUS_REGISTRY_QUOTA_LIMIT}, - {"NT_STATUS_PATH_NOT_COVERED", NT_STATUS_PATH_NOT_COVERED}, - {"NT_STATUS_NO_CALLBACK_ACTIVE", NT_STATUS_NO_CALLBACK_ACTIVE}, - {"NT_STATUS_LICENSE_QUOTA_EXCEEDED", - NT_STATUS_LICENSE_QUOTA_EXCEEDED}, - {"NT_STATUS_PWD_TOO_SHORT", NT_STATUS_PWD_TOO_SHORT}, - {"NT_STATUS_PWD_TOO_RECENT", NT_STATUS_PWD_TOO_RECENT}, - {"NT_STATUS_PWD_HISTORY_CONFLICT", NT_STATUS_PWD_HISTORY_CONFLICT}, - {"NT_STATUS_PLUGPLAY_NO_DEVICE", NT_STATUS_PLUGPLAY_NO_DEVICE}, - {"NT_STATUS_UNSUPPORTED_COMPRESSION", - NT_STATUS_UNSUPPORTED_COMPRESSION}, - {"NT_STATUS_INVALID_HW_PROFILE", NT_STATUS_INVALID_HW_PROFILE}, - {"NT_STATUS_INVALID_PLUGPLAY_DEVICE_PATH", - NT_STATUS_INVALID_PLUGPLAY_DEVICE_PATH}, - {"NT_STATUS_DRIVER_ORDINAL_NOT_FOUND", - NT_STATUS_DRIVER_ORDINAL_NOT_FOUND}, - {"NT_STATUS_DRIVER_ENTRYPOINT_NOT_FOUND", - NT_STATUS_DRIVER_ENTRYPOINT_NOT_FOUND}, - {"NT_STATUS_RESOURCE_NOT_OWNED", NT_STATUS_RESOURCE_NOT_OWNED}, - {"NT_STATUS_TOO_MANY_LINKS", NT_STATUS_TOO_MANY_LINKS}, - {"NT_STATUS_QUOTA_LIST_INCONSISTENT", - NT_STATUS_QUOTA_LIST_INCONSISTENT}, - {"NT_STATUS_FILE_IS_OFFLINE", NT_STATUS_FILE_IS_OFFLINE}, - {"NT_STATUS_NO_MORE_ENTRIES", NT_STATUS_NO_MORE_ENTRIES}, - {"NT_STATUS_MORE_ENTRIES", NT_STATUS_MORE_ENTRIES}, - {"NT_STATUS_SOME_UNMAPPED", NT_STATUS_SOME_UNMAPPED}, - {NULL, 0} -}; diff --git a/fs/cifs/nterr.h b/fs/cifs/nterr.h deleted file mode 100644 index edd4741cab0a1..0000000000000 --- a/fs/cifs/nterr.h +++ /dev/null @@ -1,551 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0-or-later */ -/* - Unix SMB/Netbios implementation. - Version 1.9. - NT error code constants - Copyright (C) Andrew Tridgell 1992-2000 - Copyright (C) John H Terpstra 1996-2000 - Copyright (C) Luke Kenneth Casson Leighton 1996-2000 - Copyright (C) Paul Ashton 1998-2000 - -*/ - - - -#ifndef _NTERR_H -#define _NTERR_H - -struct nt_err_code_struct { - char *nt_errstr; - __u32 nt_errcode; -}; - -extern const struct nt_err_code_struct nt_errs[]; - -/* Win32 Status codes. */ -#define NT_STATUS_MORE_ENTRIES 0x0105 -#define NT_ERROR_INVALID_PARAMETER 0x0057 -#define NT_ERROR_INSUFFICIENT_BUFFER 0x007a -#define NT_STATUS_1804 0x070c -#define NT_STATUS_NOTIFY_ENUM_DIR 0x010c - -/* - * Win32 Error codes extracted using a loop in smbclient then printing a netmon - * sniff to a file. - */ - -#define NT_STATUS_OK 0x0000 -#define NT_STATUS_SOME_UNMAPPED 0x0107 -#define NT_STATUS_BUFFER_OVERFLOW 0x80000005 -#define NT_STATUS_NO_MORE_ENTRIES 0x8000001a -#define NT_STATUS_MEDIA_CHANGED 0x8000001c -#define NT_STATUS_END_OF_MEDIA 0x8000001e -#define NT_STATUS_MEDIA_CHECK 0x80000020 -#define NT_STATUS_NO_DATA_DETECTED 0x8000001c -#define NT_STATUS_STOPPED_ON_SYMLINK 0x8000002d -#define NT_STATUS_DEVICE_REQUIRES_CLEANING 0x80000288 -#define NT_STATUS_DEVICE_DOOR_OPEN 0x80000288 -#define NT_STATUS_UNSUCCESSFUL 0xC0000000 | 0x0001 -#define NT_STATUS_NOT_IMPLEMENTED 0xC0000000 | 0x0002 -#define NT_STATUS_INVALID_INFO_CLASS 0xC0000000 | 0x0003 -#define NT_STATUS_INFO_LENGTH_MISMATCH 0xC0000000 | 0x0004 -#define NT_STATUS_ACCESS_VIOLATION 0xC0000000 | 0x0005 -#define NT_STATUS_IN_PAGE_ERROR 0xC0000000 | 0x0006 -#define NT_STATUS_PAGEFILE_QUOTA 0xC0000000 | 0x0007 -#define NT_STATUS_INVALID_HANDLE 0xC0000000 | 0x0008 -#define NT_STATUS_BAD_INITIAL_STACK 0xC0000000 | 0x0009 -#define NT_STATUS_BAD_INITIAL_PC 0xC0000000 | 0x000a -#define NT_STATUS_INVALID_CID 0xC0000000 | 0x000b -#define NT_STATUS_TIMER_NOT_CANCELED 0xC0000000 | 0x000c -#define NT_STATUS_INVALID_PARAMETER 0xC0000000 | 0x000d -#define NT_STATUS_NO_SUCH_DEVICE 0xC0000000 | 0x000e -#define NT_STATUS_NO_SUCH_FILE 0xC0000000 | 0x000f -#define NT_STATUS_INVALID_DEVICE_REQUEST 0xC0000000 | 0x0010 -#define NT_STATUS_END_OF_FILE 0xC0000000 | 0x0011 -#define NT_STATUS_WRONG_VOLUME 0xC0000000 | 0x0012 -#define NT_STATUS_NO_MEDIA_IN_DEVICE 0xC0000000 | 0x0013 -#define NT_STATUS_UNRECOGNIZED_MEDIA 0xC0000000 | 0x0014 -#define NT_STATUS_NONEXISTENT_SECTOR 0xC0000000 | 0x0015 -#define NT_STATUS_MORE_PROCESSING_REQUIRED 0xC0000000 | 0x0016 -#define NT_STATUS_NO_MEMORY 0xC0000000 | 0x0017 -#define NT_STATUS_CONFLICTING_ADDRESSES 0xC0000000 | 0x0018 -#define NT_STATUS_NOT_MAPPED_VIEW 0xC0000000 | 0x0019 -#define NT_STATUS_UNABLE_TO_FREE_VM 0x80000000 | 0x001a -#define NT_STATUS_UNABLE_TO_DELETE_SECTION 0xC0000000 | 0x001b -#define NT_STATUS_INVALID_SYSTEM_SERVICE 0xC0000000 | 0x001c -#define NT_STATUS_ILLEGAL_INSTRUCTION 0xC0000000 | 0x001d -#define NT_STATUS_INVALID_LOCK_SEQUENCE 0xC0000000 | 0x001e -#define NT_STATUS_INVALID_VIEW_SIZE 0xC0000000 | 0x001f -#define NT_STATUS_INVALID_FILE_FOR_SECTION 0xC0000000 | 0x0020 -#define NT_STATUS_ALREADY_COMMITTED 0xC0000000 | 0x0021 -#define NT_STATUS_ACCESS_DENIED 0xC0000000 | 0x0022 -#define NT_STATUS_BUFFER_TOO_SMALL 0xC0000000 | 0x0023 -#define NT_STATUS_OBJECT_TYPE_MISMATCH 0xC0000000 | 0x0024 -#define NT_STATUS_NONCONTINUABLE_EXCEPTION 0xC0000000 | 0x0025 -#define NT_STATUS_INVALID_DISPOSITION 0xC0000000 | 0x0026 -#define NT_STATUS_UNWIND 0xC0000000 | 0x0027 -#define NT_STATUS_BAD_STACK 0xC0000000 | 0x0028 -#define NT_STATUS_INVALID_UNWIND_TARGET 0xC0000000 | 0x0029 -#define NT_STATUS_NOT_LOCKED 0xC0000000 | 0x002a -#define NT_STATUS_PARITY_ERROR 0xC0000000 | 0x002b -#define NT_STATUS_UNABLE_TO_DECOMMIT_VM 0xC0000000 | 0x002c -#define NT_STATUS_NOT_COMMITTED 0xC0000000 | 0x002d -#define NT_STATUS_INVALID_PORT_ATTRIBUTES 0xC0000000 | 0x002e -#define NT_STATUS_PORT_MESSAGE_TOO_LONG 0xC0000000 | 0x002f -#define NT_STATUS_INVALID_PARAMETER_MIX 0xC0000000 | 0x0030 -#define NT_STATUS_INVALID_QUOTA_LOWER 0xC0000000 | 0x0031 -#define NT_STATUS_DISK_CORRUPT_ERROR 0xC0000000 | 0x0032 -#define NT_STATUS_OBJECT_NAME_INVALID 0xC0000000 | 0x0033 -#define NT_STATUS_OBJECT_NAME_NOT_FOUND 0xC0000000 | 0x0034 -#define NT_STATUS_OBJECT_NAME_COLLISION 0xC0000000 | 0x0035 -#define NT_STATUS_HANDLE_NOT_WAITABLE 0xC0000000 | 0x0036 -#define NT_STATUS_PORT_DISCONNECTED 0xC0000000 | 0x0037 -#define NT_STATUS_DEVICE_ALREADY_ATTACHED 0xC0000000 | 0x0038 -#define NT_STATUS_OBJECT_PATH_INVALID 0xC0000000 | 0x0039 -#define NT_STATUS_OBJECT_PATH_NOT_FOUND 0xC0000000 | 0x003a -#define NT_STATUS_OBJECT_PATH_SYNTAX_BAD 0xC0000000 | 0x003b -#define NT_STATUS_DATA_OVERRUN 0xC0000000 | 0x003c -#define NT_STATUS_DATA_LATE_ERROR 0xC0000000 | 0x003d -#define NT_STATUS_DATA_ERROR 0xC0000000 | 0x003e -#define NT_STATUS_CRC_ERROR 0xC0000000 | 0x003f -#define NT_STATUS_SECTION_TOO_BIG 0xC0000000 | 0x0040 -#define NT_STATUS_PORT_CONNECTION_REFUSED 0xC0000000 | 0x0041 -#define NT_STATUS_INVALID_PORT_HANDLE 0xC0000000 | 0x0042 -#define NT_STATUS_SHARING_VIOLATION 0xC0000000 | 0x0043 -#define NT_STATUS_QUOTA_EXCEEDED 0xC0000000 | 0x0044 -#define NT_STATUS_INVALID_PAGE_PROTECTION 0xC0000000 | 0x0045 -#define NT_STATUS_MUTANT_NOT_OWNED 0xC0000000 | 0x0046 -#define NT_STATUS_SEMAPHORE_LIMIT_EXCEEDED 0xC0000000 | 0x0047 -#define NT_STATUS_PORT_ALREADY_SET 0xC0000000 | 0x0048 -#define NT_STATUS_SECTION_NOT_IMAGE 0xC0000000 | 0x0049 -#define NT_STATUS_SUSPEND_COUNT_EXCEEDED 0xC0000000 | 0x004a -#define NT_STATUS_THREAD_IS_TERMINATING 0xC0000000 | 0x004b -#define NT_STATUS_BAD_WORKING_SET_LIMIT 0xC0000000 | 0x004c -#define NT_STATUS_INCOMPATIBLE_FILE_MAP 0xC0000000 | 0x004d -#define NT_STATUS_SECTION_PROTECTION 0xC0000000 | 0x004e -#define NT_STATUS_EAS_NOT_SUPPORTED 0xC0000000 | 0x004f -#define NT_STATUS_EA_TOO_LARGE 0xC0000000 | 0x0050 -#define NT_STATUS_NONEXISTENT_EA_ENTRY 0xC0000000 | 0x0051 -#define NT_STATUS_NO_EAS_ON_FILE 0xC0000000 | 0x0052 -#define NT_STATUS_EA_CORRUPT_ERROR 0xC0000000 | 0x0053 -#define NT_STATUS_FILE_LOCK_CONFLICT 0xC0000000 | 0x0054 -#define NT_STATUS_LOCK_NOT_GRANTED 0xC0000000 | 0x0055 -#define NT_STATUS_DELETE_PENDING 0xC0000000 | 0x0056 -#define NT_STATUS_CTL_FILE_NOT_SUPPORTED 0xC0000000 | 0x0057 -#define NT_STATUS_UNKNOWN_REVISION 0xC0000000 | 0x0058 -#define NT_STATUS_REVISION_MISMATCH 0xC0000000 | 0x0059 -#define NT_STATUS_INVALID_OWNER 0xC0000000 | 0x005a -#define NT_STATUS_INVALID_PRIMARY_GROUP 0xC0000000 | 0x005b -#define NT_STATUS_NO_IMPERSONATION_TOKEN 0xC0000000 | 0x005c -#define NT_STATUS_CANT_DISABLE_MANDATORY 0xC0000000 | 0x005d -#define NT_STATUS_NO_LOGON_SERVERS 0xC0000000 | 0x005e -#define NT_STATUS_NO_SUCH_LOGON_SESSION 0xC0000000 | 0x005f -#define NT_STATUS_NO_SUCH_PRIVILEGE 0xC0000000 | 0x0060 -#define NT_STATUS_PRIVILEGE_NOT_HELD 0xC0000000 | 0x0061 -#define NT_STATUS_INVALID_ACCOUNT_NAME 0xC0000000 | 0x0062 -#define NT_STATUS_USER_EXISTS 0xC0000000 | 0x0063 -#define NT_STATUS_NO_SUCH_USER 0xC0000000 | 0x0064 -#define NT_STATUS_GROUP_EXISTS 0xC0000000 | 0x0065 -#define NT_STATUS_NO_SUCH_GROUP 0xC0000000 | 0x0066 -#define NT_STATUS_MEMBER_IN_GROUP 0xC0000000 | 0x0067 -#define NT_STATUS_MEMBER_NOT_IN_GROUP 0xC0000000 | 0x0068 -#define NT_STATUS_LAST_ADMIN 0xC0000000 | 0x0069 -#define NT_STATUS_WRONG_PASSWORD 0xC0000000 | 0x006a -#define NT_STATUS_ILL_FORMED_PASSWORD 0xC0000000 | 0x006b -#define NT_STATUS_PASSWORD_RESTRICTION 0xC0000000 | 0x006c -#define NT_STATUS_LOGON_FAILURE 0xC0000000 | 0x006d -#define NT_STATUS_ACCOUNT_RESTRICTION 0xC0000000 | 0x006e -#define NT_STATUS_INVALID_LOGON_HOURS 0xC0000000 | 0x006f -#define NT_STATUS_INVALID_WORKSTATION 0xC0000000 | 0x0070 -#define NT_STATUS_PASSWORD_EXPIRED 0xC0000000 | 0x0071 -#define NT_STATUS_ACCOUNT_DISABLED 0xC0000000 | 0x0072 -#define NT_STATUS_NONE_MAPPED 0xC0000000 | 0x0073 -#define NT_STATUS_TOO_MANY_LUIDS_REQUESTED 0xC0000000 | 0x0074 -#define NT_STATUS_LUIDS_EXHAUSTED 0xC0000000 | 0x0075 -#define NT_STATUS_INVALID_SUB_AUTHORITY 0xC0000000 | 0x0076 -#define NT_STATUS_INVALID_ACL 0xC0000000 | 0x0077 -#define NT_STATUS_INVALID_SID 0xC0000000 | 0x0078 -#define NT_STATUS_INVALID_SECURITY_DESCR 0xC0000000 | 0x0079 -#define NT_STATUS_PROCEDURE_NOT_FOUND 0xC0000000 | 0x007a -#define NT_STATUS_INVALID_IMAGE_FORMAT 0xC0000000 | 0x007b -#define NT_STATUS_NO_TOKEN 0xC0000000 | 0x007c -#define NT_STATUS_BAD_INHERITANCE_ACL 0xC0000000 | 0x007d -#define NT_STATUS_RANGE_NOT_LOCKED 0xC0000000 | 0x007e -#define NT_STATUS_DISK_FULL 0xC0000000 | 0x007f -#define NT_STATUS_SERVER_DISABLED 0xC0000000 | 0x0080 -#define NT_STATUS_SERVER_NOT_DISABLED 0xC0000000 | 0x0081 -#define NT_STATUS_TOO_MANY_GUIDS_REQUESTED 0xC0000000 | 0x0082 -#define NT_STATUS_GUIDS_EXHAUSTED 0xC0000000 | 0x0083 -#define NT_STATUS_INVALID_ID_AUTHORITY 0xC0000000 | 0x0084 -#define NT_STATUS_AGENTS_EXHAUSTED 0xC0000000 | 0x0085 -#define NT_STATUS_INVALID_VOLUME_LABEL 0xC0000000 | 0x0086 -#define NT_STATUS_SECTION_NOT_EXTENDED 0xC0000000 | 0x0087 -#define NT_STATUS_NOT_MAPPED_DATA 0xC0000000 | 0x0088 -#define NT_STATUS_RESOURCE_DATA_NOT_FOUND 0xC0000000 | 0x0089 -#define NT_STATUS_RESOURCE_TYPE_NOT_FOUND 0xC0000000 | 0x008a -#define NT_STATUS_RESOURCE_NAME_NOT_FOUND 0xC0000000 | 0x008b -#define NT_STATUS_ARRAY_BOUNDS_EXCEEDED 0xC0000000 | 0x008c -#define NT_STATUS_FLOAT_DENORMAL_OPERAND 0xC0000000 | 0x008d -#define NT_STATUS_FLOAT_DIVIDE_BY_ZERO 0xC0000000 | 0x008e -#define NT_STATUS_FLOAT_INEXACT_RESULT 0xC0000000 | 0x008f -#define NT_STATUS_FLOAT_INVALID_OPERATION 0xC0000000 | 0x0090 -#define NT_STATUS_FLOAT_OVERFLOW 0xC0000000 | 0x0091 -#define NT_STATUS_FLOAT_STACK_CHECK 0xC0000000 | 0x0092 -#define NT_STATUS_FLOAT_UNDERFLOW 0xC0000000 | 0x0093 -#define NT_STATUS_INTEGER_DIVIDE_BY_ZERO 0xC0000000 | 0x0094 -#define NT_STATUS_INTEGER_OVERFLOW 0xC0000000 | 0x0095 -#define NT_STATUS_PRIVILEGED_INSTRUCTION 0xC0000000 | 0x0096 -#define NT_STATUS_TOO_MANY_PAGING_FILES 0xC0000000 | 0x0097 -#define NT_STATUS_FILE_INVALID 0xC0000000 | 0x0098 -#define NT_STATUS_ALLOTTED_SPACE_EXCEEDED 0xC0000000 | 0x0099 -#define NT_STATUS_INSUFFICIENT_RESOURCES 0xC0000000 | 0x009a -#define NT_STATUS_DFS_EXIT_PATH_FOUND 0xC0000000 | 0x009b -#define NT_STATUS_DEVICE_DATA_ERROR 0xC0000000 | 0x009c -#define NT_STATUS_DEVICE_NOT_CONNECTED 0xC0000000 | 0x009d -#define NT_STATUS_DEVICE_POWER_FAILURE 0xC0000000 | 0x009e -#define NT_STATUS_FREE_VM_NOT_AT_BASE 0xC0000000 | 0x009f -#define NT_STATUS_MEMORY_NOT_ALLOCATED 0xC0000000 | 0x00a0 -#define NT_STATUS_WORKING_SET_QUOTA 0xC0000000 | 0x00a1 -#define NT_STATUS_MEDIA_WRITE_PROTECTED 0xC0000000 | 0x00a2 -#define NT_STATUS_DEVICE_NOT_READY 0xC0000000 | 0x00a3 -#define NT_STATUS_INVALID_GROUP_ATTRIBUTES 0xC0000000 | 0x00a4 -#define NT_STATUS_BAD_IMPERSONATION_LEVEL 0xC0000000 | 0x00a5 -#define NT_STATUS_CANT_OPEN_ANONYMOUS 0xC0000000 | 0x00a6 -#define NT_STATUS_BAD_VALIDATION_CLASS 0xC0000000 | 0x00a7 -#define NT_STATUS_BAD_TOKEN_TYPE 0xC0000000 | 0x00a8 -#define NT_STATUS_BAD_MASTER_BOOT_RECORD 0xC0000000 | 0x00a9 -#define NT_STATUS_INSTRUCTION_MISALIGNMENT 0xC0000000 | 0x00aa -#define NT_STATUS_INSTANCE_NOT_AVAILABLE 0xC0000000 | 0x00ab -#define NT_STATUS_PIPE_NOT_AVAILABLE 0xC0000000 | 0x00ac -#define NT_STATUS_INVALID_PIPE_STATE 0xC0000000 | 0x00ad -#define NT_STATUS_PIPE_BUSY 0xC0000000 | 0x00ae -#define NT_STATUS_ILLEGAL_FUNCTION 0xC0000000 | 0x00af -#define NT_STATUS_PIPE_DISCONNECTED 0xC0000000 | 0x00b0 -#define NT_STATUS_PIPE_CLOSING 0xC0000000 | 0x00b1 -#define NT_STATUS_PIPE_CONNECTED 0xC0000000 | 0x00b2 -#define NT_STATUS_PIPE_LISTENING 0xC0000000 | 0x00b3 -#define NT_STATUS_INVALID_READ_MODE 0xC0000000 | 0x00b4 -#define NT_STATUS_IO_TIMEOUT 0xC0000000 | 0x00b5 -#define NT_STATUS_FILE_FORCED_CLOSED 0xC0000000 | 0x00b6 -#define NT_STATUS_PROFILING_NOT_STARTED 0xC0000000 | 0x00b7 -#define NT_STATUS_PROFILING_NOT_STOPPED 0xC0000000 | 0x00b8 -#define NT_STATUS_COULD_NOT_INTERPRET 0xC0000000 | 0x00b9 -#define NT_STATUS_FILE_IS_A_DIRECTORY 0xC0000000 | 0x00ba -#define NT_STATUS_NOT_SUPPORTED 0xC0000000 | 0x00bb -#define NT_STATUS_REMOTE_NOT_LISTENING 0xC0000000 | 0x00bc -#define NT_STATUS_DUPLICATE_NAME 0xC0000000 | 0x00bd -#define NT_STATUS_BAD_NETWORK_PATH 0xC0000000 | 0x00be -#define NT_STATUS_NETWORK_BUSY 0xC0000000 | 0x00bf -#define NT_STATUS_DEVICE_DOES_NOT_EXIST 0xC0000000 | 0x00c0 -#define NT_STATUS_TOO_MANY_COMMANDS 0xC0000000 | 0x00c1 -#define NT_STATUS_ADAPTER_HARDWARE_ERROR 0xC0000000 | 0x00c2 -#define NT_STATUS_INVALID_NETWORK_RESPONSE 0xC0000000 | 0x00c3 -#define NT_STATUS_UNEXPECTED_NETWORK_ERROR 0xC0000000 | 0x00c4 -#define NT_STATUS_BAD_REMOTE_ADAPTER 0xC0000000 | 0x00c5 -#define NT_STATUS_PRINT_QUEUE_FULL 0xC0000000 | 0x00c6 -#define NT_STATUS_NO_SPOOL_SPACE 0xC0000000 | 0x00c7 -#define NT_STATUS_PRINT_CANCELLED 0xC0000000 | 0x00c8 -#define NT_STATUS_NETWORK_NAME_DELETED 0xC0000000 | 0x00c9 -#define NT_STATUS_NETWORK_ACCESS_DENIED 0xC0000000 | 0x00ca -#define NT_STATUS_BAD_DEVICE_TYPE 0xC0000000 | 0x00cb -#define NT_STATUS_BAD_NETWORK_NAME 0xC0000000 | 0x00cc -#define NT_STATUS_TOO_MANY_NAMES 0xC0000000 | 0x00cd -#define NT_STATUS_TOO_MANY_SESSIONS 0xC0000000 | 0x00ce -#define NT_STATUS_SHARING_PAUSED 0xC0000000 | 0x00cf -#define NT_STATUS_REQUEST_NOT_ACCEPTED 0xC0000000 | 0x00d0 -#define NT_STATUS_REDIRECTOR_PAUSED 0xC0000000 | 0x00d1 -#define NT_STATUS_NET_WRITE_FAULT 0xC0000000 | 0x00d2 -#define NT_STATUS_PROFILING_AT_LIMIT 0xC0000000 | 0x00d3 -#define NT_STATUS_NOT_SAME_DEVICE 0xC0000000 | 0x00d4 -#define NT_STATUS_FILE_RENAMED 0xC0000000 | 0x00d5 -#define NT_STATUS_VIRTUAL_CIRCUIT_CLOSED 0xC0000000 | 0x00d6 -#define NT_STATUS_NO_SECURITY_ON_OBJECT 0xC0000000 | 0x00d7 -#define NT_STATUS_CANT_WAIT 0xC0000000 | 0x00d8 -#define NT_STATUS_PIPE_EMPTY 0xC0000000 | 0x00d9 -#define NT_STATUS_CANT_ACCESS_DOMAIN_INFO 0xC0000000 | 0x00da -#define NT_STATUS_CANT_TERMINATE_SELF 0xC0000000 | 0x00db -#define NT_STATUS_INVALID_SERVER_STATE 0xC0000000 | 0x00dc -#define NT_STATUS_INVALID_DOMAIN_STATE 0xC0000000 | 0x00dd -#define NT_STATUS_INVALID_DOMAIN_ROLE 0xC0000000 | 0x00de -#define NT_STATUS_NO_SUCH_DOMAIN 0xC0000000 | 0x00df -#define NT_STATUS_DOMAIN_EXISTS 0xC0000000 | 0x00e0 -#define NT_STATUS_DOMAIN_LIMIT_EXCEEDED 0xC0000000 | 0x00e1 -#define NT_STATUS_OPLOCK_NOT_GRANTED 0xC0000000 | 0x00e2 -#define NT_STATUS_INVALID_OPLOCK_PROTOCOL 0xC0000000 | 0x00e3 -#define NT_STATUS_INTERNAL_DB_CORRUPTION 0xC0000000 | 0x00e4 -#define NT_STATUS_INTERNAL_ERROR 0xC0000000 | 0x00e5 -#define NT_STATUS_GENERIC_NOT_MAPPED 0xC0000000 | 0x00e6 -#define NT_STATUS_BAD_DESCRIPTOR_FORMAT 0xC0000000 | 0x00e7 -#define NT_STATUS_INVALID_USER_BUFFER 0xC0000000 | 0x00e8 -#define NT_STATUS_UNEXPECTED_IO_ERROR 0xC0000000 | 0x00e9 -#define NT_STATUS_UNEXPECTED_MM_CREATE_ERR 0xC0000000 | 0x00ea -#define NT_STATUS_UNEXPECTED_MM_MAP_ERROR 0xC0000000 | 0x00eb -#define NT_STATUS_UNEXPECTED_MM_EXTEND_ERR 0xC0000000 | 0x00ec -#define NT_STATUS_NOT_LOGON_PROCESS 0xC0000000 | 0x00ed -#define NT_STATUS_LOGON_SESSION_EXISTS 0xC0000000 | 0x00ee -#define NT_STATUS_INVALID_PARAMETER_1 0xC0000000 | 0x00ef -#define NT_STATUS_INVALID_PARAMETER_2 0xC0000000 | 0x00f0 -#define NT_STATUS_INVALID_PARAMETER_3 0xC0000000 | 0x00f1 -#define NT_STATUS_INVALID_PARAMETER_4 0xC0000000 | 0x00f2 -#define NT_STATUS_INVALID_PARAMETER_5 0xC0000000 | 0x00f3 -#define NT_STATUS_INVALID_PARAMETER_6 0xC0000000 | 0x00f4 -#define NT_STATUS_INVALID_PARAMETER_7 0xC0000000 | 0x00f5 -#define NT_STATUS_INVALID_PARAMETER_8 0xC0000000 | 0x00f6 -#define NT_STATUS_INVALID_PARAMETER_9 0xC0000000 | 0x00f7 -#define NT_STATUS_INVALID_PARAMETER_10 0xC0000000 | 0x00f8 -#define NT_STATUS_INVALID_PARAMETER_11 0xC0000000 | 0x00f9 -#define NT_STATUS_INVALID_PARAMETER_12 0xC0000000 | 0x00fa -#define NT_STATUS_REDIRECTOR_NOT_STARTED 0xC0000000 | 0x00fb -#define NT_STATUS_REDIRECTOR_STARTED 0xC0000000 | 0x00fc -#define NT_STATUS_STACK_OVERFLOW 0xC0000000 | 0x00fd -#define NT_STATUS_NO_SUCH_PACKAGE 0xC0000000 | 0x00fe -#define NT_STATUS_BAD_FUNCTION_TABLE 0xC0000000 | 0x00ff -#define NT_STATUS_DIRECTORY_NOT_EMPTY 0xC0000000 | 0x0101 -#define NT_STATUS_FILE_CORRUPT_ERROR 0xC0000000 | 0x0102 -#define NT_STATUS_NOT_A_DIRECTORY 0xC0000000 | 0x0103 -#define NT_STATUS_BAD_LOGON_SESSION_STATE 0xC0000000 | 0x0104 -#define NT_STATUS_LOGON_SESSION_COLLISION 0xC0000000 | 0x0105 -#define NT_STATUS_NAME_TOO_LONG 0xC0000000 | 0x0106 -#define NT_STATUS_FILES_OPEN 0xC0000000 | 0x0107 -#define NT_STATUS_CONNECTION_IN_USE 0xC0000000 | 0x0108 -#define NT_STATUS_MESSAGE_NOT_FOUND 0xC0000000 | 0x0109 -#define NT_STATUS_PROCESS_IS_TERMINATING 0xC0000000 | 0x010a -#define NT_STATUS_INVALID_LOGON_TYPE 0xC0000000 | 0x010b -#define NT_STATUS_NO_GUID_TRANSLATION 0xC0000000 | 0x010c -#define NT_STATUS_CANNOT_IMPERSONATE 0xC0000000 | 0x010d -#define NT_STATUS_IMAGE_ALREADY_LOADED 0xC0000000 | 0x010e -#define NT_STATUS_ABIOS_NOT_PRESENT 0xC0000000 | 0x010f -#define NT_STATUS_ABIOS_LID_NOT_EXIST 0xC0000000 | 0x0110 -#define NT_STATUS_ABIOS_LID_ALREADY_OWNED 0xC0000000 | 0x0111 -#define NT_STATUS_ABIOS_NOT_LID_OWNER 0xC0000000 | 0x0112 -#define NT_STATUS_ABIOS_INVALID_COMMAND 0xC0000000 | 0x0113 -#define NT_STATUS_ABIOS_INVALID_LID 0xC0000000 | 0x0114 -#define NT_STATUS_ABIOS_SELECTOR_NOT_AVAILABLE 0xC0000000 | 0x0115 -#define NT_STATUS_ABIOS_INVALID_SELECTOR 0xC0000000 | 0x0116 -#define NT_STATUS_NO_LDT 0xC0000000 | 0x0117 -#define NT_STATUS_INVALID_LDT_SIZE 0xC0000000 | 0x0118 -#define NT_STATUS_INVALID_LDT_OFFSET 0xC0000000 | 0x0119 -#define NT_STATUS_INVALID_LDT_DESCRIPTOR 0xC0000000 | 0x011a -#define NT_STATUS_INVALID_IMAGE_NE_FORMAT 0xC0000000 | 0x011b -#define NT_STATUS_RXACT_INVALID_STATE 0xC0000000 | 0x011c -#define NT_STATUS_RXACT_COMMIT_FAILURE 0xC0000000 | 0x011d -#define NT_STATUS_MAPPED_FILE_SIZE_ZERO 0xC0000000 | 0x011e -#define NT_STATUS_TOO_MANY_OPENED_FILES 0xC0000000 | 0x011f -#define NT_STATUS_CANCELLED 0xC0000000 | 0x0120 -#define NT_STATUS_CANNOT_DELETE 0xC0000000 | 0x0121 -#define NT_STATUS_INVALID_COMPUTER_NAME 0xC0000000 | 0x0122 -#define NT_STATUS_FILE_DELETED 0xC0000000 | 0x0123 -#define NT_STATUS_SPECIAL_ACCOUNT 0xC0000000 | 0x0124 -#define NT_STATUS_SPECIAL_GROUP 0xC0000000 | 0x0125 -#define NT_STATUS_SPECIAL_USER 0xC0000000 | 0x0126 -#define NT_STATUS_MEMBERS_PRIMARY_GROUP 0xC0000000 | 0x0127 -#define NT_STATUS_FILE_CLOSED 0xC0000000 | 0x0128 -#define NT_STATUS_TOO_MANY_THREADS 0xC0000000 | 0x0129 -#define NT_STATUS_THREAD_NOT_IN_PROCESS 0xC0000000 | 0x012a -#define NT_STATUS_TOKEN_ALREADY_IN_USE 0xC0000000 | 0x012b -#define NT_STATUS_PAGEFILE_QUOTA_EXCEEDED 0xC0000000 | 0x012c -#define NT_STATUS_COMMITMENT_LIMIT 0xC0000000 | 0x012d -#define NT_STATUS_INVALID_IMAGE_LE_FORMAT 0xC0000000 | 0x012e -#define NT_STATUS_INVALID_IMAGE_NOT_MZ 0xC0000000 | 0x012f -#define NT_STATUS_INVALID_IMAGE_PROTECT 0xC0000000 | 0x0130 -#define NT_STATUS_INVALID_IMAGE_WIN_16 0xC0000000 | 0x0131 -#define NT_STATUS_LOGON_SERVER_CONFLICT 0xC0000000 | 0x0132 -#define NT_STATUS_TIME_DIFFERENCE_AT_DC 0xC0000000 | 0x0133 -#define NT_STATUS_SYNCHRONIZATION_REQUIRED 0xC0000000 | 0x0134 -#define NT_STATUS_DLL_NOT_FOUND 0xC0000000 | 0x0135 -#define NT_STATUS_OPEN_FAILED 0xC0000000 | 0x0136 -#define NT_STATUS_IO_PRIVILEGE_FAILED 0xC0000000 | 0x0137 -#define NT_STATUS_ORDINAL_NOT_FOUND 0xC0000000 | 0x0138 -#define NT_STATUS_ENTRYPOINT_NOT_FOUND 0xC0000000 | 0x0139 -#define NT_STATUS_CONTROL_C_EXIT 0xC0000000 | 0x013a -#define NT_STATUS_LOCAL_DISCONNECT 0xC0000000 | 0x013b -#define NT_STATUS_REMOTE_DISCONNECT 0xC0000000 | 0x013c -#define NT_STATUS_REMOTE_RESOURCES 0xC0000000 | 0x013d -#define NT_STATUS_LINK_FAILED 0xC0000000 | 0x013e -#define NT_STATUS_LINK_TIMEOUT 0xC0000000 | 0x013f -#define NT_STATUS_INVALID_CONNECTION 0xC0000000 | 0x0140 -#define NT_STATUS_INVALID_ADDRESS 0xC0000000 | 0x0141 -#define NT_STATUS_DLL_INIT_FAILED 0xC0000000 | 0x0142 -#define NT_STATUS_MISSING_SYSTEMFILE 0xC0000000 | 0x0143 -#define NT_STATUS_UNHANDLED_EXCEPTION 0xC0000000 | 0x0144 -#define NT_STATUS_APP_INIT_FAILURE 0xC0000000 | 0x0145 -#define NT_STATUS_PAGEFILE_CREATE_FAILED 0xC0000000 | 0x0146 -#define NT_STATUS_NO_PAGEFILE 0xC0000000 | 0x0147 -#define NT_STATUS_INVALID_LEVEL 0xC0000000 | 0x0148 -#define NT_STATUS_WRONG_PASSWORD_CORE 0xC0000000 | 0x0149 -#define NT_STATUS_ILLEGAL_FLOAT_CONTEXT 0xC0000000 | 0x014a -#define NT_STATUS_PIPE_BROKEN 0xC0000000 | 0x014b -#define NT_STATUS_REGISTRY_CORRUPT 0xC0000000 | 0x014c -#define NT_STATUS_REGISTRY_IO_FAILED 0xC0000000 | 0x014d -#define NT_STATUS_NO_EVENT_PAIR 0xC0000000 | 0x014e -#define NT_STATUS_UNRECOGNIZED_VOLUME 0xC0000000 | 0x014f -#define NT_STATUS_SERIAL_NO_DEVICE_INITED 0xC0000000 | 0x0150 -#define NT_STATUS_NO_SUCH_ALIAS 0xC0000000 | 0x0151 -#define NT_STATUS_MEMBER_NOT_IN_ALIAS 0xC0000000 | 0x0152 -#define NT_STATUS_MEMBER_IN_ALIAS 0xC0000000 | 0x0153 -#define NT_STATUS_ALIAS_EXISTS 0xC0000000 | 0x0154 -#define NT_STATUS_LOGON_NOT_GRANTED 0xC0000000 | 0x0155 -#define NT_STATUS_TOO_MANY_SECRETS 0xC0000000 | 0x0156 -#define NT_STATUS_SECRET_TOO_LONG 0xC0000000 | 0x0157 -#define NT_STATUS_INTERNAL_DB_ERROR 0xC0000000 | 0x0158 -#define NT_STATUS_FULLSCREEN_MODE 0xC0000000 | 0x0159 -#define NT_STATUS_TOO_MANY_CONTEXT_IDS 0xC0000000 | 0x015a -#define NT_STATUS_LOGON_TYPE_NOT_GRANTED 0xC0000000 | 0x015b -#define NT_STATUS_NOT_REGISTRY_FILE 0xC0000000 | 0x015c -#define NT_STATUS_NT_CROSS_ENCRYPTION_REQUIRED 0xC0000000 | 0x015d -#define NT_STATUS_DOMAIN_CTRLR_CONFIG_ERROR 0xC0000000 | 0x015e -#define NT_STATUS_FT_MISSING_MEMBER 0xC0000000 | 0x015f -#define NT_STATUS_ILL_FORMED_SERVICE_ENTRY 0xC0000000 | 0x0160 -#define NT_STATUS_ILLEGAL_CHARACTER 0xC0000000 | 0x0161 -#define NT_STATUS_UNMAPPABLE_CHARACTER 0xC0000000 | 0x0162 -#define NT_STATUS_UNDEFINED_CHARACTER 0xC0000000 | 0x0163 -#define NT_STATUS_FLOPPY_VOLUME 0xC0000000 | 0x0164 -#define NT_STATUS_FLOPPY_ID_MARK_NOT_FOUND 0xC0000000 | 0x0165 -#define NT_STATUS_FLOPPY_WRONG_CYLINDER 0xC0000000 | 0x0166 -#define NT_STATUS_FLOPPY_UNKNOWN_ERROR 0xC0000000 | 0x0167 -#define NT_STATUS_FLOPPY_BAD_REGISTERS 0xC0000000 | 0x0168 -#define NT_STATUS_DISK_RECALIBRATE_FAILED 0xC0000000 | 0x0169 -#define NT_STATUS_DISK_OPERATION_FAILED 0xC0000000 | 0x016a -#define NT_STATUS_DISK_RESET_FAILED 0xC0000000 | 0x016b -#define NT_STATUS_SHARED_IRQ_BUSY 0xC0000000 | 0x016c -#define NT_STATUS_FT_ORPHANING 0xC0000000 | 0x016d -#define NT_STATUS_PARTITION_FAILURE 0xC0000000 | 0x0172 -#define NT_STATUS_INVALID_BLOCK_LENGTH 0xC0000000 | 0x0173 -#define NT_STATUS_DEVICE_NOT_PARTITIONED 0xC0000000 | 0x0174 -#define NT_STATUS_UNABLE_TO_LOCK_MEDIA 0xC0000000 | 0x0175 -#define NT_STATUS_UNABLE_TO_UNLOAD_MEDIA 0xC0000000 | 0x0176 -#define NT_STATUS_EOM_OVERFLOW 0xC0000000 | 0x0177 -#define NT_STATUS_NO_MEDIA 0xC0000000 | 0x0178 -#define NT_STATUS_NO_SUCH_MEMBER 0xC0000000 | 0x017a -#define NT_STATUS_INVALID_MEMBER 0xC0000000 | 0x017b -#define NT_STATUS_KEY_DELETED 0xC0000000 | 0x017c -#define NT_STATUS_NO_LOG_SPACE 0xC0000000 | 0x017d -#define NT_STATUS_TOO_MANY_SIDS 0xC0000000 | 0x017e -#define NT_STATUS_LM_CROSS_ENCRYPTION_REQUIRED 0xC0000000 | 0x017f -#define NT_STATUS_KEY_HAS_CHILDREN 0xC0000000 | 0x0180 -#define NT_STATUS_CHILD_MUST_BE_VOLATILE 0xC0000000 | 0x0181 -#define NT_STATUS_DEVICE_CONFIGURATION_ERROR 0xC0000000 | 0x0182 -#define NT_STATUS_DRIVER_INTERNAL_ERROR 0xC0000000 | 0x0183 -#define NT_STATUS_INVALID_DEVICE_STATE 0xC0000000 | 0x0184 -#define NT_STATUS_IO_DEVICE_ERROR 0xC0000000 | 0x0185 -#define NT_STATUS_DEVICE_PROTOCOL_ERROR 0xC0000000 | 0x0186 -#define NT_STATUS_BACKUP_CONTROLLER 0xC0000000 | 0x0187 -#define NT_STATUS_LOG_FILE_FULL 0xC0000000 | 0x0188 -#define NT_STATUS_TOO_LATE 0xC0000000 | 0x0189 -#define NT_STATUS_NO_TRUST_LSA_SECRET 0xC0000000 | 0x018a -#define NT_STATUS_NO_TRUST_SAM_ACCOUNT 0xC0000000 | 0x018b -#define NT_STATUS_TRUSTED_DOMAIN_FAILURE 0xC0000000 | 0x018c -#define NT_STATUS_TRUSTED_RELATIONSHIP_FAILURE 0xC0000000 | 0x018d -#define NT_STATUS_EVENTLOG_FILE_CORRUPT 0xC0000000 | 0x018e -#define NT_STATUS_EVENTLOG_CANT_START 0xC0000000 | 0x018f -#define NT_STATUS_TRUST_FAILURE 0xC0000000 | 0x0190 -#define NT_STATUS_MUTANT_LIMIT_EXCEEDED 0xC0000000 | 0x0191 -#define NT_STATUS_NETLOGON_NOT_STARTED 0xC0000000 | 0x0192 -#define NT_STATUS_ACCOUNT_EXPIRED 0xC0000000 | 0x0193 -#define NT_STATUS_POSSIBLE_DEADLOCK 0xC0000000 | 0x0194 -#define NT_STATUS_NETWORK_CREDENTIAL_CONFLICT 0xC0000000 | 0x0195 -#define NT_STATUS_REMOTE_SESSION_LIMIT 0xC0000000 | 0x0196 -#define NT_STATUS_EVENTLOG_FILE_CHANGED 0xC0000000 | 0x0197 -#define NT_STATUS_NOLOGON_INTERDOMAIN_TRUST_ACCOUNT 0xC0000000 | 0x0198 -#define NT_STATUS_NOLOGON_WORKSTATION_TRUST_ACCOUNT 0xC0000000 | 0x0199 -#define NT_STATUS_NOLOGON_SERVER_TRUST_ACCOUNT 0xC0000000 | 0x019a -#define NT_STATUS_DOMAIN_TRUST_INCONSISTENT 0xC0000000 | 0x019b -#define NT_STATUS_FS_DRIVER_REQUIRED 0xC0000000 | 0x019c -#define NT_STATUS_NO_USER_SESSION_KEY 0xC0000000 | 0x0202 -#define NT_STATUS_USER_SESSION_DELETED 0xC0000000 | 0x0203 -#define NT_STATUS_RESOURCE_LANG_NOT_FOUND 0xC0000000 | 0x0204 -#define NT_STATUS_INSUFF_SERVER_RESOURCES 0xC0000000 | 0x0205 -#define NT_STATUS_INVALID_BUFFER_SIZE 0xC0000000 | 0x0206 -#define NT_STATUS_INVALID_ADDRESS_COMPONENT 0xC0000000 | 0x0207 -#define NT_STATUS_INVALID_ADDRESS_WILDCARD 0xC0000000 | 0x0208 -#define NT_STATUS_TOO_MANY_ADDRESSES 0xC0000000 | 0x0209 -#define NT_STATUS_ADDRESS_ALREADY_EXISTS 0xC0000000 | 0x020a -#define NT_STATUS_ADDRESS_CLOSED 0xC0000000 | 0x020b -#define NT_STATUS_CONNECTION_DISCONNECTED 0xC0000000 | 0x020c -#define NT_STATUS_CONNECTION_RESET 0xC0000000 | 0x020d -#define NT_STATUS_TOO_MANY_NODES 0xC0000000 | 0x020e -#define NT_STATUS_TRANSACTION_ABORTED 0xC0000000 | 0x020f -#define NT_STATUS_TRANSACTION_TIMED_OUT 0xC0000000 | 0x0210 -#define NT_STATUS_TRANSACTION_NO_RELEASE 0xC0000000 | 0x0211 -#define NT_STATUS_TRANSACTION_NO_MATCH 0xC0000000 | 0x0212 -#define NT_STATUS_TRANSACTION_RESPONDED 0xC0000000 | 0x0213 -#define NT_STATUS_TRANSACTION_INVALID_ID 0xC0000000 | 0x0214 -#define NT_STATUS_TRANSACTION_INVALID_TYPE 0xC0000000 | 0x0215 -#define NT_STATUS_NOT_SERVER_SESSION 0xC0000000 | 0x0216 -#define NT_STATUS_NOT_CLIENT_SESSION 0xC0000000 | 0x0217 -#define NT_STATUS_CANNOT_LOAD_REGISTRY_FILE 0xC0000000 | 0x0218 -#define NT_STATUS_DEBUG_ATTACH_FAILED 0xC0000000 | 0x0219 -#define NT_STATUS_SYSTEM_PROCESS_TERMINATED 0xC0000000 | 0x021a -#define NT_STATUS_DATA_NOT_ACCEPTED 0xC0000000 | 0x021b -#define NT_STATUS_NO_BROWSER_SERVERS_FOUND 0xC0000000 | 0x021c -#define NT_STATUS_VDM_HARD_ERROR 0xC0000000 | 0x021d -#define NT_STATUS_DRIVER_CANCEL_TIMEOUT 0xC0000000 | 0x021e -#define NT_STATUS_REPLY_MESSAGE_MISMATCH 0xC0000000 | 0x021f -#define NT_STATUS_MAPPED_ALIGNMENT 0xC0000000 | 0x0220 -#define NT_STATUS_IMAGE_CHECKSUM_MISMATCH 0xC0000000 | 0x0221 -#define NT_STATUS_LOST_WRITEBEHIND_DATA 0xC0000000 | 0x0222 -#define NT_STATUS_CLIENT_SERVER_PARAMETERS_INVALID 0xC0000000 | 0x0223 -#define NT_STATUS_PASSWORD_MUST_CHANGE 0xC0000000 | 0x0224 -#define NT_STATUS_NOT_FOUND 0xC0000000 | 0x0225 -#define NT_STATUS_NOT_TINY_STREAM 0xC0000000 | 0x0226 -#define NT_STATUS_RECOVERY_FAILURE 0xC0000000 | 0x0227 -#define NT_STATUS_STACK_OVERFLOW_READ 0xC0000000 | 0x0228 -#define NT_STATUS_FAIL_CHECK 0xC0000000 | 0x0229 -#define NT_STATUS_DUPLICATE_OBJECTID 0xC0000000 | 0x022a -#define NT_STATUS_OBJECTID_EXISTS 0xC0000000 | 0x022b -#define NT_STATUS_CONVERT_TO_LARGE 0xC0000000 | 0x022c -#define NT_STATUS_RETRY 0xC0000000 | 0x022d -#define NT_STATUS_FOUND_OUT_OF_SCOPE 0xC0000000 | 0x022e -#define NT_STATUS_ALLOCATE_BUCKET 0xC0000000 | 0x022f -#define NT_STATUS_PROPSET_NOT_FOUND 0xC0000000 | 0x0230 -#define NT_STATUS_MARSHALL_OVERFLOW 0xC0000000 | 0x0231 -#define NT_STATUS_INVALID_VARIANT 0xC0000000 | 0x0232 -#define NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND 0xC0000000 | 0x0233 -#define NT_STATUS_ACCOUNT_LOCKED_OUT 0xC0000000 | 0x0234 -#define NT_STATUS_HANDLE_NOT_CLOSABLE 0xC0000000 | 0x0235 -#define NT_STATUS_CONNECTION_REFUSED 0xC0000000 | 0x0236 -#define NT_STATUS_GRACEFUL_DISCONNECT 0xC0000000 | 0x0237 -#define NT_STATUS_ADDRESS_ALREADY_ASSOCIATED 0xC0000000 | 0x0238 -#define NT_STATUS_ADDRESS_NOT_ASSOCIATED 0xC0000000 | 0x0239 -#define NT_STATUS_CONNECTION_INVALID 0xC0000000 | 0x023a -#define NT_STATUS_CONNECTION_ACTIVE 0xC0000000 | 0x023b -#define NT_STATUS_NETWORK_UNREACHABLE 0xC0000000 | 0x023c -#define NT_STATUS_HOST_UNREACHABLE 0xC0000000 | 0x023d -#define NT_STATUS_PROTOCOL_UNREACHABLE 0xC0000000 | 0x023e -#define NT_STATUS_PORT_UNREACHABLE 0xC0000000 | 0x023f -#define NT_STATUS_REQUEST_ABORTED 0xC0000000 | 0x0240 -#define NT_STATUS_CONNECTION_ABORTED 0xC0000000 | 0x0241 -#define NT_STATUS_BAD_COMPRESSION_BUFFER 0xC0000000 | 0x0242 -#define NT_STATUS_USER_MAPPED_FILE 0xC0000000 | 0x0243 -#define NT_STATUS_AUDIT_FAILED 0xC0000000 | 0x0244 -#define NT_STATUS_TIMER_RESOLUTION_NOT_SET 0xC0000000 | 0x0245 -#define NT_STATUS_CONNECTION_COUNT_LIMIT 0xC0000000 | 0x0246 -#define NT_STATUS_LOGIN_TIME_RESTRICTION 0xC0000000 | 0x0247 -#define NT_STATUS_LOGIN_WKSTA_RESTRICTION 0xC0000000 | 0x0248 -#define NT_STATUS_IMAGE_MP_UP_MISMATCH 0xC0000000 | 0x0249 -#define NT_STATUS_INSUFFICIENT_LOGON_INFO 0xC0000000 | 0x0250 -#define NT_STATUS_BAD_DLL_ENTRYPOINT 0xC0000000 | 0x0251 -#define NT_STATUS_BAD_SERVICE_ENTRYPOINT 0xC0000000 | 0x0252 -#define NT_STATUS_LPC_REPLY_LOST 0xC0000000 | 0x0253 -#define NT_STATUS_IP_ADDRESS_CONFLICT1 0xC0000000 | 0x0254 -#define NT_STATUS_IP_ADDRESS_CONFLICT2 0xC0000000 | 0x0255 -#define NT_STATUS_REGISTRY_QUOTA_LIMIT 0xC0000000 | 0x0256 -#define NT_STATUS_PATH_NOT_COVERED 0xC0000000 | 0x0257 -#define NT_STATUS_NO_CALLBACK_ACTIVE 0xC0000000 | 0x0258 -#define NT_STATUS_LICENSE_QUOTA_EXCEEDED 0xC0000000 | 0x0259 -#define NT_STATUS_PWD_TOO_SHORT 0xC0000000 | 0x025a -#define NT_STATUS_PWD_TOO_RECENT 0xC0000000 | 0x025b -#define NT_STATUS_PWD_HISTORY_CONFLICT 0xC0000000 | 0x025c -#define NT_STATUS_PLUGPLAY_NO_DEVICE 0xC0000000 | 0x025e -#define NT_STATUS_UNSUPPORTED_COMPRESSION 0xC0000000 | 0x025f -#define NT_STATUS_INVALID_HW_PROFILE 0xC0000000 | 0x0260 -#define NT_STATUS_INVALID_PLUGPLAY_DEVICE_PATH 0xC0000000 | 0x0261 -#define NT_STATUS_DRIVER_ORDINAL_NOT_FOUND 0xC0000000 | 0x0262 -#define NT_STATUS_DRIVER_ENTRYPOINT_NOT_FOUND 0xC0000000 | 0x0263 -#define NT_STATUS_RESOURCE_NOT_OWNED 0xC0000000 | 0x0264 -#define NT_STATUS_TOO_MANY_LINKS 0xC0000000 | 0x0265 -#define NT_STATUS_QUOTA_LIST_INCONSISTENT 0xC0000000 | 0x0266 -#define NT_STATUS_FILE_IS_OFFLINE 0xC0000000 | 0x0267 -#define NT_STATUS_NO_SUCH_JOB 0xC0000000 | 0xEDE /* scheduler */ - -#endif /* _NTERR_H */ diff --git a/fs/cifs/ntlmssp.h b/fs/cifs/ntlmssp.h deleted file mode 100644 index 55758b9ec877e..0000000000000 --- a/fs/cifs/ntlmssp.h +++ /dev/null @@ -1,157 +0,0 @@ -/* SPDX-License-Identifier: LGPL-2.1 */ -/* - * - * Copyright (c) International Business Machines Corp., 2002,2007 - * Author(s): Steve French (sfrench@us.ibm.com) - * - */ - -#define NTLMSSP_SIGNATURE "NTLMSSP" -/* Message Types */ -#define NtLmNegotiate cpu_to_le32(1) -#define NtLmChallenge cpu_to_le32(2) -#define NtLmAuthenticate cpu_to_le32(3) -#define UnknownMessage cpu_to_le32(8) - -/* Negotiate Flags */ -#define NTLMSSP_NEGOTIATE_UNICODE 0x01 /* Text strings are unicode */ -#define NTLMSSP_NEGOTIATE_OEM 0x02 /* Text strings are in OEM */ -#define NTLMSSP_REQUEST_TARGET 0x04 /* Srv returns its auth realm */ -/* define reserved9 0x08 */ -#define NTLMSSP_NEGOTIATE_SIGN 0x0010 /* Request signing capability */ -#define NTLMSSP_NEGOTIATE_SEAL 0x0020 /* Request confidentiality */ -#define NTLMSSP_NEGOTIATE_DGRAM 0x0040 -#define NTLMSSP_NEGOTIATE_LM_KEY 0x0080 /* Use LM session key */ -/* defined reserved 8 0x0100 */ -#define NTLMSSP_NEGOTIATE_NTLM 0x0200 /* NTLM authentication */ -#define NTLMSSP_NEGOTIATE_NT_ONLY 0x0400 /* Lanman not allowed */ -#define NTLMSSP_ANONYMOUS 0x0800 -#define NTLMSSP_NEGOTIATE_DOMAIN_SUPPLIED 0x1000 /* reserved6 */ -#define NTLMSSP_NEGOTIATE_WORKSTATION_SUPPLIED 0x2000 -#define NTLMSSP_NEGOTIATE_LOCAL_CALL 0x4000 /* client/server same machine */ -#define NTLMSSP_NEGOTIATE_ALWAYS_SIGN 0x8000 /* Sign. All security levels */ -#define NTLMSSP_TARGET_TYPE_DOMAIN 0x10000 -#define NTLMSSP_TARGET_TYPE_SERVER 0x20000 -#define NTLMSSP_TARGET_TYPE_SHARE 0x40000 -#define NTLMSSP_NEGOTIATE_EXTENDED_SEC 0x80000 /* NB:not related to NTLMv2 pwd*/ -/* #define NTLMSSP_REQUEST_INIT_RESP 0x100000 */ -#define NTLMSSP_NEGOTIATE_IDENTIFY 0x100000 -#define NTLMSSP_REQUEST_ACCEPT_RESP 0x200000 /* reserved5 */ -#define NTLMSSP_REQUEST_NON_NT_KEY 0x400000 -#define NTLMSSP_NEGOTIATE_TARGET_INFO 0x800000 -/* #define reserved4 0x1000000 */ -#define NTLMSSP_NEGOTIATE_VERSION 0x2000000 /* we only set for SMB2+ */ -/* #define reserved3 0x4000000 */ -/* #define reserved2 0x8000000 */ -/* #define reserved1 0x10000000 */ -#define NTLMSSP_NEGOTIATE_128 0x20000000 -#define NTLMSSP_NEGOTIATE_KEY_XCH 0x40000000 -#define NTLMSSP_NEGOTIATE_56 0x80000000 - -/* Define AV Pair Field IDs */ -enum av_field_type { - NTLMSSP_AV_EOL = 0, - NTLMSSP_AV_NB_COMPUTER_NAME, - NTLMSSP_AV_NB_DOMAIN_NAME, - NTLMSSP_AV_DNS_COMPUTER_NAME, - NTLMSSP_AV_DNS_DOMAIN_NAME, - NTLMSSP_AV_DNS_TREE_NAME, - NTLMSSP_AV_FLAGS, - NTLMSSP_AV_TIMESTAMP, - NTLMSSP_AV_RESTRICTION, - NTLMSSP_AV_TARGET_NAME, - NTLMSSP_AV_CHANNEL_BINDINGS -}; - -/* Although typedefs are not commonly used for structure definitions */ -/* in the Linux kernel, in this particular case they are useful */ -/* to more closely match the standards document for NTLMSSP from */ -/* OpenGroup and to make the code more closely match the standard in */ -/* appearance */ - -typedef struct _SECURITY_BUFFER { - __le16 Length; - __le16 MaximumLength; - __le32 BufferOffset; /* offset to buffer */ -} __attribute__((packed)) SECURITY_BUFFER; - -typedef struct _NEGOTIATE_MESSAGE { - __u8 Signature[sizeof(NTLMSSP_SIGNATURE)]; - __le32 MessageType; /* NtLmNegotiate = 1 */ - __le32 NegotiateFlags; - SECURITY_BUFFER DomainName; /* RFC 1001 style and ASCII */ - SECURITY_BUFFER WorkstationName; /* RFC 1001 and ASCII */ - /* SECURITY_BUFFER for version info not present since we - do not set the version is present flag */ - char DomainString[0]; - /* followed by WorkstationString */ -} __attribute__((packed)) NEGOTIATE_MESSAGE, *PNEGOTIATE_MESSAGE; - -#define NTLMSSP_REVISION_W2K3 0x0F - -/* See MS-NLMP section 2.2.2.10 */ -struct ntlmssp_version { - __u8 ProductMajorVersion; - __u8 ProductMinorVersion; - __le16 ProductBuild; /* we send the cifs.ko module version here */ - __u8 Reserved[3]; - __u8 NTLMRevisionCurrent; /* currently 0x0F */ -} __packed; - -/* see MS-NLMP section 2.2.1.1 */ -struct negotiate_message { - __u8 Signature[sizeof(NTLMSSP_SIGNATURE)]; - __le32 MessageType; /* NtLmNegotiate = 1 */ - __le32 NegotiateFlags; - SECURITY_BUFFER DomainName; /* RFC 1001 style and ASCII */ - SECURITY_BUFFER WorkstationName; /* RFC 1001 and ASCII */ - struct ntlmssp_version Version; - /* SECURITY_BUFFER */ - char DomainString[]; - /* followed by WorkstationString */ -} __packed; - -typedef struct _CHALLENGE_MESSAGE { - __u8 Signature[sizeof(NTLMSSP_SIGNATURE)]; - __le32 MessageType; /* NtLmChallenge = 2 */ - SECURITY_BUFFER TargetName; - __le32 NegotiateFlags; - __u8 Challenge[CIFS_CRYPTO_KEY_SIZE]; - __u8 Reserved[8]; - SECURITY_BUFFER TargetInfoArray; - /* SECURITY_BUFFER for version info not present since we - do not set the version is present flag */ -} __attribute__((packed)) CHALLENGE_MESSAGE, *PCHALLENGE_MESSAGE; - -typedef struct _AUTHENTICATE_MESSAGE { - __u8 Signature[sizeof(NTLMSSP_SIGNATURE)]; - __le32 MessageType; /* NtLmsAuthenticate = 3 */ - SECURITY_BUFFER LmChallengeResponse; - SECURITY_BUFFER NtChallengeResponse; - SECURITY_BUFFER DomainName; - SECURITY_BUFFER UserName; - SECURITY_BUFFER WorkstationName; - SECURITY_BUFFER SessionKey; - __le32 NegotiateFlags; - /* SECURITY_BUFFER for version info not present since we - do not set the version is present flag */ - char UserString[0]; -} __attribute__((packed)) AUTHENTICATE_MESSAGE, *PAUTHENTICATE_MESSAGE; - -/* - * Size of the session key (crypto key encrypted with the password - */ - -int decode_ntlmssp_challenge(char *bcc_ptr, int blob_len, struct cifs_ses *ses); -int build_ntlmssp_negotiate_blob(unsigned char **pbuffer, u16 *buflen, - struct cifs_ses *ses, - struct TCP_Server_Info *server, - const struct nls_table *nls_cp); -int build_ntlmssp_smb3_negotiate_blob(unsigned char **pbuffer, u16 *buflen, - struct cifs_ses *ses, - struct TCP_Server_Info *server, - const struct nls_table *nls_cp); -int build_ntlmssp_auth_blob(unsigned char **pbuffer, u16 *buflen, - struct cifs_ses *ses, - struct TCP_Server_Info *server, - const struct nls_table *nls_cp); diff --git a/fs/cifs/readdir.c b/fs/cifs/readdir.c deleted file mode 100644 index 2d75ba5aaa8ad..0000000000000 --- a/fs/cifs/readdir.c +++ /dev/null @@ -1,1237 +0,0 @@ -// SPDX-License-Identifier: LGPL-2.1 -/* - * - * Directory search handling - * - * Copyright (C) International Business Machines Corp., 2004, 2008 - * Copyright (C) Red Hat, Inc., 2011 - * Author(s): Steve French (sfrench@us.ibm.com) - * - */ -#include -#include -#include -#include -#include "cifspdu.h" -#include "cifsglob.h" -#include "cifsproto.h" -#include "cifs_unicode.h" -#include "cifs_debug.h" -#include "cifs_fs_sb.h" -#include "cifsfs.h" -#include "smb2proto.h" -#include "fs_context.h" -#include "cached_dir.h" - -/* - * To be safe - for UCS to UTF-8 with strings loaded with the rare long - * characters alloc more to account for such multibyte target UTF-8 - * characters. - */ -#define UNICODE_NAME_MAX ((4 * NAME_MAX) + 2) - -#ifdef CONFIG_CIFS_DEBUG2 -static void dump_cifs_file_struct(struct file *file, char *label) -{ - struct cifsFileInfo *cf; - - if (file) { - cf = file->private_data; - if (cf == NULL) { - cifs_dbg(FYI, "empty cifs private file data\n"); - return; - } - if (cf->invalidHandle) - cifs_dbg(FYI, "Invalid handle\n"); - if (cf->srch_inf.endOfSearch) - cifs_dbg(FYI, "end of search\n"); - if (cf->srch_inf.emptyDir) - cifs_dbg(FYI, "empty dir\n"); - } -} -#else -static inline void dump_cifs_file_struct(struct file *file, char *label) -{ -} -#endif /* DEBUG2 */ - -/* - * Attempt to preload the dcache with the results from the FIND_FIRST/NEXT - * - * Find the dentry that matches "name". If there isn't one, create one. If it's - * a negative dentry or the uniqueid or filetype(mode) changed, - * then drop it and recreate it. - */ -static void -cifs_prime_dcache(struct dentry *parent, struct qstr *name, - struct cifs_fattr *fattr) -{ - struct dentry *dentry, *alias; - struct inode *inode; - struct super_block *sb = parent->d_sb; - struct cifs_sb_info *cifs_sb = CIFS_SB(sb); - DECLARE_WAIT_QUEUE_HEAD_ONSTACK(wq); - - cifs_dbg(FYI, "%s: for %s\n", __func__, name->name); - - dentry = d_hash_and_lookup(parent, name); - if (!dentry) { - /* - * If we know that the inode will need to be revalidated - * immediately, then don't create a new dentry for it. - * We'll end up doing an on the wire call either way and - * this spares us an invalidation. - */ - if (fattr->cf_flags & CIFS_FATTR_NEED_REVAL) - return; -retry: - dentry = d_alloc_parallel(parent, name, &wq); - } - if (IS_ERR(dentry)) - return; - if (!d_in_lookup(dentry)) { - inode = d_inode(dentry); - if (inode) { - if (d_mountpoint(dentry)) { - dput(dentry); - return; - } - /* - * If we're generating inode numbers, then we don't - * want to clobber the existing one with the one that - * the readdir code created. - */ - if (!(cifs_sb->mnt_cifs_flags & CIFS_MOUNT_SERVER_INUM)) - fattr->cf_uniqueid = CIFS_I(inode)->uniqueid; - - /* update inode in place - * if both i_ino and i_mode didn't change */ - if (CIFS_I(inode)->uniqueid == fattr->cf_uniqueid && - cifs_fattr_to_inode(inode, fattr) == 0) { - dput(dentry); - return; - } - } - d_invalidate(dentry); - dput(dentry); - goto retry; - } else { - inode = cifs_iget(sb, fattr); - if (!inode) - inode = ERR_PTR(-ENOMEM); - alias = d_splice_alias(inode, dentry); - d_lookup_done(dentry); - if (alias && !IS_ERR(alias)) - dput(alias); - } - dput(dentry); -} - -static bool reparse_file_needs_reval(const struct cifs_fattr *fattr) -{ - if (!(fattr->cf_cifsattrs & ATTR_REPARSE)) - return false; - /* - * The DFS tags should be only intepreted by server side as per - * MS-FSCC 2.1.2.1, but let's include them anyway. - * - * Besides, if cf_cifstag is unset (0), then we still need it to be - * revalidated to know exactly what reparse point it is. - */ - switch (fattr->cf_cifstag) { - case IO_REPARSE_TAG_DFS: - case IO_REPARSE_TAG_DFSR: - case IO_REPARSE_TAG_SYMLINK: - case IO_REPARSE_TAG_NFS: - case 0: - return true; - } - return false; -} - -static void -cifs_fill_common_info(struct cifs_fattr *fattr, struct cifs_sb_info *cifs_sb) -{ - fattr->cf_uid = cifs_sb->ctx->linux_uid; - fattr->cf_gid = cifs_sb->ctx->linux_gid; - - /* - * The IO_REPARSE_TAG_LX_ tags originally were used by WSL but they - * are preferred by the Linux client in some cases since, unlike - * the NFS reparse tag (or EAs), they don't require an extra query - * to determine which type of special file they represent. - * TODO: go through all documented reparse tags to see if we can - * reasonably map some of them to directories vs. files vs. symlinks - */ - if (fattr->cf_cifsattrs & ATTR_DIRECTORY) { - fattr->cf_mode = S_IFDIR | cifs_sb->ctx->dir_mode; - fattr->cf_dtype = DT_DIR; - } else if (fattr->cf_cifstag == IO_REPARSE_TAG_LX_SYMLINK) { - fattr->cf_mode |= S_IFLNK | cifs_sb->ctx->file_mode; - fattr->cf_dtype = DT_LNK; - } else if (fattr->cf_cifstag == IO_REPARSE_TAG_LX_FIFO) { - fattr->cf_mode |= S_IFIFO | cifs_sb->ctx->file_mode; - fattr->cf_dtype = DT_FIFO; - } else if (fattr->cf_cifstag == IO_REPARSE_TAG_AF_UNIX) { - fattr->cf_mode |= S_IFSOCK | cifs_sb->ctx->file_mode; - fattr->cf_dtype = DT_SOCK; - } else if (fattr->cf_cifstag == IO_REPARSE_TAG_LX_CHR) { - fattr->cf_mode |= S_IFCHR | cifs_sb->ctx->file_mode; - fattr->cf_dtype = DT_CHR; - } else if (fattr->cf_cifstag == IO_REPARSE_TAG_LX_BLK) { - fattr->cf_mode |= S_IFBLK | cifs_sb->ctx->file_mode; - fattr->cf_dtype = DT_BLK; - } else { /* TODO: should we mark some other reparse points (like DFSR) as directories? */ - fattr->cf_mode = S_IFREG | cifs_sb->ctx->file_mode; - fattr->cf_dtype = DT_REG; - } - - /* - * We need to revalidate it further to make a decision about whether it - * is a symbolic link, DFS referral or a reparse point with a direct - * access like junctions, deduplicated files, NFS symlinks. - */ - if (reparse_file_needs_reval(fattr)) - fattr->cf_flags |= CIFS_FATTR_NEED_REVAL; - - /* non-unix readdir doesn't provide nlink */ - fattr->cf_flags |= CIFS_FATTR_UNKNOWN_NLINK; - - if (fattr->cf_cifsattrs & ATTR_READONLY) - fattr->cf_mode &= ~S_IWUGO; - - /* - * We of course don't get ACL info in FIND_FIRST/NEXT results, so - * mark it for revalidation so that "ls -l" will look right. It might - * be super-slow, but if we don't do this then the ownership of files - * may look wrong since the inodes may not have timed out by the time - * "ls" does a stat() call on them. - */ - if ((cifs_sb->mnt_cifs_flags & CIFS_MOUNT_CIFS_ACL) || - (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_MODE_FROM_SID)) - fattr->cf_flags |= CIFS_FATTR_NEED_REVAL; - - if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_UNX_EMUL && - fattr->cf_cifsattrs & ATTR_SYSTEM) { - if (fattr->cf_eof == 0) { - fattr->cf_mode &= ~S_IFMT; - fattr->cf_mode |= S_IFIFO; - fattr->cf_dtype = DT_FIFO; - } else { - /* - * trying to get the type and mode via SFU can be slow, - * so just call those regular files for now, and mark - * for reval - */ - fattr->cf_flags |= CIFS_FATTR_NEED_REVAL; - } - } -} - -/* Fill a cifs_fattr struct with info from SMB_FIND_FILE_POSIX_INFO. */ -static void -cifs_posix_to_fattr(struct cifs_fattr *fattr, struct smb2_posix_info *info, - struct cifs_sb_info *cifs_sb) -{ - struct smb2_posix_info_parsed parsed; - - posix_info_parse(info, NULL, &parsed); - - memset(fattr, 0, sizeof(*fattr)); - fattr->cf_uniqueid = le64_to_cpu(info->Inode); - fattr->cf_bytes = le64_to_cpu(info->AllocationSize); - fattr->cf_eof = le64_to_cpu(info->EndOfFile); - - fattr->cf_atime = cifs_NTtimeToUnix(info->LastAccessTime); - fattr->cf_mtime = cifs_NTtimeToUnix(info->LastWriteTime); - fattr->cf_ctime = cifs_NTtimeToUnix(info->CreationTime); - - fattr->cf_nlink = le32_to_cpu(info->HardLinks); - fattr->cf_cifsattrs = le32_to_cpu(info->DosAttributes); - - /* - * Since we set the inode type below we need to mask off - * to avoid strange results if bits set above. - * XXX: why not make server&client use the type bits? - */ - fattr->cf_mode = le32_to_cpu(info->Mode) & ~S_IFMT; - - cifs_dbg(FYI, "posix fattr: dev %d, reparse %d, mode %o\n", - le32_to_cpu(info->DeviceId), - le32_to_cpu(info->ReparseTag), - le32_to_cpu(info->Mode)); - - if (fattr->cf_cifsattrs & ATTR_DIRECTORY) { - fattr->cf_mode |= S_IFDIR; - fattr->cf_dtype = DT_DIR; - } else { - /* - * mark anything that is not a dir as regular - * file. special files should have the REPARSE - * attribute and will be marked as needing revaluation - */ - fattr->cf_mode |= S_IFREG; - fattr->cf_dtype = DT_REG; - } - - if (reparse_file_needs_reval(fattr)) - fattr->cf_flags |= CIFS_FATTR_NEED_REVAL; - - sid_to_id(cifs_sb, &parsed.owner, fattr, SIDOWNER); - sid_to_id(cifs_sb, &parsed.group, fattr, SIDGROUP); -} - -static void __dir_info_to_fattr(struct cifs_fattr *fattr, const void *info) -{ - const FILE_DIRECTORY_INFO *fi = info; - - memset(fattr, 0, sizeof(*fattr)); - fattr->cf_cifsattrs = le32_to_cpu(fi->ExtFileAttributes); - fattr->cf_eof = le64_to_cpu(fi->EndOfFile); - fattr->cf_bytes = le64_to_cpu(fi->AllocationSize); - fattr->cf_createtime = le64_to_cpu(fi->CreationTime); - fattr->cf_atime = cifs_NTtimeToUnix(fi->LastAccessTime); - fattr->cf_ctime = cifs_NTtimeToUnix(fi->ChangeTime); - fattr->cf_mtime = cifs_NTtimeToUnix(fi->LastWriteTime); -} - -void -cifs_dir_info_to_fattr(struct cifs_fattr *fattr, FILE_DIRECTORY_INFO *info, - struct cifs_sb_info *cifs_sb) -{ - __dir_info_to_fattr(fattr, info); - cifs_fill_common_info(fattr, cifs_sb); -} - -static void cifs_fulldir_info_to_fattr(struct cifs_fattr *fattr, - SEARCH_ID_FULL_DIR_INFO *info, - struct cifs_sb_info *cifs_sb) -{ - __dir_info_to_fattr(fattr, info); - - /* See MS-FSCC 2.4.19 FileIdFullDirectoryInformation */ - if (fattr->cf_cifsattrs & ATTR_REPARSE) - fattr->cf_cifstag = le32_to_cpu(info->EaSize); - cifs_fill_common_info(fattr, cifs_sb); -} - -static void -cifs_std_info_to_fattr(struct cifs_fattr *fattr, FIND_FILE_STANDARD_INFO *info, - struct cifs_sb_info *cifs_sb) -{ - int offset = cifs_sb_master_tcon(cifs_sb)->ses->server->timeAdj; - - memset(fattr, 0, sizeof(*fattr)); - fattr->cf_atime = cnvrtDosUnixTm(info->LastAccessDate, - info->LastAccessTime, offset); - fattr->cf_ctime = cnvrtDosUnixTm(info->LastWriteDate, - info->LastWriteTime, offset); - fattr->cf_mtime = cnvrtDosUnixTm(info->LastWriteDate, - info->LastWriteTime, offset); - - fattr->cf_cifsattrs = le16_to_cpu(info->Attributes); - fattr->cf_bytes = le32_to_cpu(info->AllocationSize); - fattr->cf_eof = le32_to_cpu(info->DataSize); - - cifs_fill_common_info(fattr, cifs_sb); -} - -/* BB eventually need to add the following helper function to - resolve NT_STATUS_STOPPED_ON_SYMLINK return code when - we try to do FindFirst on (NTFS) directory symlinks */ -/* -int get_symlink_reparse_path(char *full_path, struct cifs_sb_info *cifs_sb, - unsigned int xid) -{ - __u16 fid; - int len; - int oplock = 0; - int rc; - struct cifs_tcon *ptcon = cifs_sb_tcon(cifs_sb); - char *tmpbuffer; - - rc = CIFSSMBOpen(xid, ptcon, full_path, FILE_OPEN, GENERIC_READ, - OPEN_REPARSE_POINT, &fid, &oplock, NULL, - cifs_sb->local_nls, - cifs_remap(cifs_sb); - if (!rc) { - tmpbuffer = kmalloc(maxpath); - rc = CIFSSMBQueryReparseLinkInfo(xid, ptcon, full_path, - tmpbuffer, - maxpath -1, - fid, - cifs_sb->local_nls); - if (CIFSSMBClose(xid, ptcon, fid)) { - cifs_dbg(FYI, "Error closing temporary reparsepoint open\n"); - } - } -} - */ - -static int -_initiate_cifs_search(const unsigned int xid, struct file *file, - const char *full_path) -{ - __u16 search_flags; - int rc = 0; - struct cifsFileInfo *cifsFile; - struct cifs_sb_info *cifs_sb = CIFS_FILE_SB(file); - struct tcon_link *tlink = NULL; - struct cifs_tcon *tcon; - struct TCP_Server_Info *server; - - if (file->private_data == NULL) { - tlink = cifs_sb_tlink(cifs_sb); - if (IS_ERR(tlink)) - return PTR_ERR(tlink); - - cifsFile = kzalloc(sizeof(struct cifsFileInfo), GFP_KERNEL); - if (cifsFile == NULL) { - rc = -ENOMEM; - goto error_exit; - } - spin_lock_init(&cifsFile->file_info_lock); - file->private_data = cifsFile; - cifsFile->tlink = cifs_get_tlink(tlink); - tcon = tlink_tcon(tlink); - } else { - cifsFile = file->private_data; - tcon = tlink_tcon(cifsFile->tlink); - } - - server = tcon->ses->server; - - if (!server->ops->query_dir_first) { - rc = -ENOSYS; - goto error_exit; - } - - cifsFile->invalidHandle = true; - cifsFile->srch_inf.endOfSearch = false; - - cifs_dbg(FYI, "Full path: %s start at: %lld\n", full_path, file->f_pos); - -ffirst_retry: - /* test for Unix extensions */ - /* but now check for them on the share/mount not on the SMB session */ - /* if (cap_unix(tcon->ses) { */ - if (tcon->unix_ext) - cifsFile->srch_inf.info_level = SMB_FIND_FILE_UNIX; - else if (tcon->posix_extensions) - cifsFile->srch_inf.info_level = SMB_FIND_FILE_POSIX_INFO; - else if ((tcon->ses->capabilities & - tcon->ses->server->vals->cap_nt_find) == 0) { - cifsFile->srch_inf.info_level = SMB_FIND_FILE_INFO_STANDARD; - } else if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_SERVER_INUM) { - cifsFile->srch_inf.info_level = SMB_FIND_FILE_ID_FULL_DIR_INFO; - } else /* not srvinos - BB fixme add check for backlevel? */ { - cifsFile->srch_inf.info_level = SMB_FIND_FILE_DIRECTORY_INFO; - } - - search_flags = CIFS_SEARCH_CLOSE_AT_END | CIFS_SEARCH_RETURN_RESUME; - if (backup_cred(cifs_sb)) - search_flags |= CIFS_SEARCH_BACKUP_SEARCH; - - rc = server->ops->query_dir_first(xid, tcon, full_path, cifs_sb, - &cifsFile->fid, search_flags, - &cifsFile->srch_inf); - - if (rc == 0) - cifsFile->invalidHandle = false; - /* BB add following call to handle readdir on new NTFS symlink errors - else if STATUS_STOPPED_ON_SYMLINK - call get_symlink_reparse_path and retry with new path */ - else if ((rc == -EOPNOTSUPP) && - (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_SERVER_INUM)) { - cifs_sb->mnt_cifs_flags &= ~CIFS_MOUNT_SERVER_INUM; - goto ffirst_retry; - } -error_exit: - cifs_put_tlink(tlink); - return rc; -} - -static int -initiate_cifs_search(const unsigned int xid, struct file *file, - const char *full_path) -{ - int rc, retry_count = 0; - - do { - rc = _initiate_cifs_search(xid, file, full_path); - /* - * If we don't have enough credits to start reading the - * directory just try again after short wait. - */ - if (rc != -EDEADLK) - break; - - usleep_range(512, 2048); - } while (retry_count++ < 5); - - return rc; -} - -/* return length of unicode string in bytes */ -static int cifs_unicode_bytelen(const char *str) -{ - int len; - const __le16 *ustr = (const __le16 *)str; - - for (len = 0; len <= PATH_MAX; len++) { - if (ustr[len] == 0) - return len << 1; - } - cifs_dbg(FYI, "Unicode string longer than PATH_MAX found\n"); - return len << 1; -} - -static char *nxt_dir_entry(char *old_entry, char *end_of_smb, int level) -{ - char *new_entry; - FILE_DIRECTORY_INFO *pDirInfo = (FILE_DIRECTORY_INFO *)old_entry; - - if (level == SMB_FIND_FILE_INFO_STANDARD) { - FIND_FILE_STANDARD_INFO *pfData; - pfData = (FIND_FILE_STANDARD_INFO *)pDirInfo; - - new_entry = old_entry + sizeof(FIND_FILE_STANDARD_INFO) + - pfData->FileNameLength; - } else { - u32 next_offset = le32_to_cpu(pDirInfo->NextEntryOffset); - - if (old_entry + next_offset < old_entry) { - cifs_dbg(VFS, "Invalid offset %u\n", next_offset); - return NULL; - } - new_entry = old_entry + next_offset; - } - cifs_dbg(FYI, "new entry %p old entry %p\n", new_entry, old_entry); - /* validate that new_entry is not past end of SMB */ - if (new_entry >= end_of_smb) { - cifs_dbg(VFS, "search entry %p began after end of SMB %p old entry %p\n", - new_entry, end_of_smb, old_entry); - return NULL; - } else if (((level == SMB_FIND_FILE_INFO_STANDARD) && - (new_entry + sizeof(FIND_FILE_STANDARD_INFO) > end_of_smb)) - || ((level != SMB_FIND_FILE_INFO_STANDARD) && - (new_entry + sizeof(FILE_DIRECTORY_INFO) > end_of_smb))) { - cifs_dbg(VFS, "search entry %p extends after end of SMB %p\n", - new_entry, end_of_smb); - return NULL; - } else - return new_entry; - -} - -struct cifs_dirent { - const char *name; - size_t namelen; - u32 resume_key; - u64 ino; -}; - -static void cifs_fill_dirent_posix(struct cifs_dirent *de, - const struct smb2_posix_info *info) -{ - struct smb2_posix_info_parsed parsed; - - /* payload should have already been checked at this point */ - if (posix_info_parse(info, NULL, &parsed) < 0) { - cifs_dbg(VFS, "Invalid POSIX info payload\n"); - return; - } - - de->name = parsed.name; - de->namelen = parsed.name_len; - de->resume_key = info->Ignored; - de->ino = le64_to_cpu(info->Inode); -} - -static void cifs_fill_dirent_unix(struct cifs_dirent *de, - const FILE_UNIX_INFO *info, bool is_unicode) -{ - de->name = &info->FileName[0]; - if (is_unicode) - de->namelen = cifs_unicode_bytelen(de->name); - else - de->namelen = strnlen(de->name, PATH_MAX); - de->resume_key = info->ResumeKey; - de->ino = le64_to_cpu(info->basic.UniqueId); -} - -static void cifs_fill_dirent_dir(struct cifs_dirent *de, - const FILE_DIRECTORY_INFO *info) -{ - de->name = &info->FileName[0]; - de->namelen = le32_to_cpu(info->FileNameLength); - de->resume_key = info->FileIndex; -} - -static void cifs_fill_dirent_full(struct cifs_dirent *de, - const FILE_FULL_DIRECTORY_INFO *info) -{ - de->name = &info->FileName[0]; - de->namelen = le32_to_cpu(info->FileNameLength); - de->resume_key = info->FileIndex; -} - -static void cifs_fill_dirent_search(struct cifs_dirent *de, - const SEARCH_ID_FULL_DIR_INFO *info) -{ - de->name = &info->FileName[0]; - de->namelen = le32_to_cpu(info->FileNameLength); - de->resume_key = info->FileIndex; - de->ino = le64_to_cpu(info->UniqueId); -} - -static void cifs_fill_dirent_both(struct cifs_dirent *de, - const FILE_BOTH_DIRECTORY_INFO *info) -{ - de->name = &info->FileName[0]; - de->namelen = le32_to_cpu(info->FileNameLength); - de->resume_key = info->FileIndex; -} - -static void cifs_fill_dirent_std(struct cifs_dirent *de, - const FIND_FILE_STANDARD_INFO *info) -{ - de->name = &info->FileName[0]; - /* one byte length, no endianess conversion */ - de->namelen = info->FileNameLength; - de->resume_key = info->ResumeKey; -} - -static int cifs_fill_dirent(struct cifs_dirent *de, const void *info, - u16 level, bool is_unicode) -{ - memset(de, 0, sizeof(*de)); - - switch (level) { - case SMB_FIND_FILE_POSIX_INFO: - cifs_fill_dirent_posix(de, info); - break; - case SMB_FIND_FILE_UNIX: - cifs_fill_dirent_unix(de, info, is_unicode); - break; - case SMB_FIND_FILE_DIRECTORY_INFO: - cifs_fill_dirent_dir(de, info); - break; - case SMB_FIND_FILE_FULL_DIRECTORY_INFO: - cifs_fill_dirent_full(de, info); - break; - case SMB_FIND_FILE_ID_FULL_DIR_INFO: - cifs_fill_dirent_search(de, info); - break; - case SMB_FIND_FILE_BOTH_DIRECTORY_INFO: - cifs_fill_dirent_both(de, info); - break; - case SMB_FIND_FILE_INFO_STANDARD: - cifs_fill_dirent_std(de, info); - break; - default: - cifs_dbg(FYI, "Unknown findfirst level %d\n", level); - return -EINVAL; - } - - return 0; -} - -#define UNICODE_DOT cpu_to_le16(0x2e) - -/* return 0 if no match and 1 for . (current directory) and 2 for .. (parent) */ -static int cifs_entry_is_dot(struct cifs_dirent *de, bool is_unicode) -{ - int rc = 0; - - if (!de->name) - return 0; - - if (is_unicode) { - __le16 *ufilename = (__le16 *)de->name; - if (de->namelen == 2) { - /* check for . */ - if (ufilename[0] == UNICODE_DOT) - rc = 1; - } else if (de->namelen == 4) { - /* check for .. */ - if (ufilename[0] == UNICODE_DOT && - ufilename[1] == UNICODE_DOT) - rc = 2; - } - } else /* ASCII */ { - if (de->namelen == 1) { - if (de->name[0] == '.') - rc = 1; - } else if (de->namelen == 2) { - if (de->name[0] == '.' && de->name[1] == '.') - rc = 2; - } - } - - 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 = file_inode(file); - struct cifsInodeInfo *cifsInfo = CIFS_I(inode); - - if (cifsInfo->time == 0) - return 1; /* directory was changed, perhaps due to unlink */ - else - return 0; - -} - -static int cifs_save_resume_key(const char *current_entry, - struct cifsFileInfo *file_info) -{ - struct cifs_dirent de; - int rc; - - rc = cifs_fill_dirent(&de, current_entry, file_info->srch_inf.info_level, - file_info->srch_inf.unicode); - if (!rc) { - file_info->srch_inf.presume_name = de.name; - file_info->srch_inf.resume_name_len = de.namelen; - file_info->srch_inf.resume_key = de.resume_key; - } - return rc; -} - -/* - * 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 assume that they are located in the findfirst - * return buffer. We start counting in the buffer with entry 2 and increment for - * every entry (do not increment for . or .. entry). - */ -static int -find_cifs_entry(const unsigned int xid, struct cifs_tcon *tcon, loff_t pos, - struct file *file, const char *full_path, - char **current_entry, int *num_to_ret) -{ - __u16 search_flags; - int rc = 0; - int pos_in_buf = 0; - loff_t first_entry_in_buffer; - loff_t index_to_find = pos; - struct cifsFileInfo *cfile = file->private_data; - struct cifs_sb_info *cifs_sb = CIFS_FILE_SB(file); - struct TCP_Server_Info *server = tcon->ses->server; - /* check if index in the buffer */ - - if (!server->ops->query_dir_first || !server->ops->query_dir_next) - return -ENOSYS; - - if ((cfile == NULL) || (current_entry == NULL) || (num_to_ret == NULL)) - return -ENOENT; - - *current_entry = NULL; - first_entry_in_buffer = cfile->srch_inf.index_of_last_entry - - cfile->srch_inf.entries_in_buffer; - - /* - * If first entry in buf is zero then is first buffer - * in search response data which means it is likely . and .. - * will be in this buffer, although some servers do not return - * . and .. for the root of a drive and for those we need - * to start two entries earlier. - */ - - dump_cifs_file_struct(file, "In fce "); - if (((index_to_find < cfile->srch_inf.index_of_last_entry) && - is_dir_changed(file)) || (index_to_find < first_entry_in_buffer)) { - /* close and restart search */ - cifs_dbg(FYI, "search backing up - close and restart search\n"); - spin_lock(&cfile->file_info_lock); - if (server->ops->dir_needs_close(cfile)) { - cfile->invalidHandle = true; - spin_unlock(&cfile->file_info_lock); - if (server->ops->close_dir) - server->ops->close_dir(xid, tcon, &cfile->fid); - } else - spin_unlock(&cfile->file_info_lock); - if (cfile->srch_inf.ntwrk_buf_start) { - cifs_dbg(FYI, "freeing SMB ff cache buf on search rewind\n"); - if (cfile->srch_inf.smallBuf) - cifs_small_buf_release(cfile->srch_inf. - ntwrk_buf_start); - else - cifs_buf_release(cfile->srch_inf. - ntwrk_buf_start); - cfile->srch_inf.ntwrk_buf_start = NULL; - } - rc = initiate_cifs_search(xid, file, full_path); - if (rc) { - cifs_dbg(FYI, "error %d reinitiating a search on rewind\n", - rc); - return rc; - } - /* FindFirst/Next set last_entry to NULL on malformed reply */ - if (cfile->srch_inf.last_entry) - cifs_save_resume_key(cfile->srch_inf.last_entry, cfile); - } - - search_flags = CIFS_SEARCH_CLOSE_AT_END | CIFS_SEARCH_RETURN_RESUME; - if (backup_cred(cifs_sb)) - search_flags |= CIFS_SEARCH_BACKUP_SEARCH; - - while ((index_to_find >= cfile->srch_inf.index_of_last_entry) && - (rc == 0) && !cfile->srch_inf.endOfSearch) { - cifs_dbg(FYI, "calling findnext2\n"); - rc = server->ops->query_dir_next(xid, tcon, &cfile->fid, - search_flags, - &cfile->srch_inf); - /* FindFirst/Next set last_entry to NULL on malformed reply */ - if (cfile->srch_inf.last_entry) - cifs_save_resume_key(cfile->srch_inf.last_entry, cfile); - if (rc) - return -ENOENT; - } - if (index_to_find < cfile->srch_inf.index_of_last_entry) { - /* we found the buffer that contains the entry */ - /* scan and find it */ - int i; - char *cur_ent; - char *end_of_smb; - - if (cfile->srch_inf.ntwrk_buf_start == NULL) { - cifs_dbg(VFS, "ntwrk_buf_start is NULL during readdir\n"); - return -EIO; - } - - end_of_smb = cfile->srch_inf.ntwrk_buf_start + - server->ops->calc_smb_size( - cfile->srch_inf.ntwrk_buf_start); - - cur_ent = cfile->srch_inf.srch_entries_start; - first_entry_in_buffer = cfile->srch_inf.index_of_last_entry - - cfile->srch_inf.entries_in_buffer; - pos_in_buf = index_to_find - first_entry_in_buffer; - cifs_dbg(FYI, "found entry - pos_in_buf %d\n", pos_in_buf); - - for (i = 0; (i < (pos_in_buf)) && (cur_ent != NULL); i++) { - /* go entry by entry figuring out which is first */ - cur_ent = nxt_dir_entry(cur_ent, end_of_smb, - cfile->srch_inf.info_level); - } - if ((cur_ent == NULL) && (i < pos_in_buf)) { - /* BB fixme - check if we should flag this error */ - cifs_dbg(VFS, "reached end of buf searching for pos in buf %d index to find %lld rc %d\n", - pos_in_buf, index_to_find, rc); - } - rc = 0; - *current_entry = cur_ent; - } else { - cifs_dbg(FYI, "index not in buffer - could not findnext into it\n"); - return 0; - } - - if (pos_in_buf >= cfile->srch_inf.entries_in_buffer) { - cifs_dbg(FYI, "can not return entries pos_in_buf beyond last\n"); - *num_to_ret = 0; - } else - *num_to_ret = cfile->srch_inf.entries_in_buffer - pos_in_buf; - - return rc; -} - -static bool emit_cached_dirents(struct cached_dirents *cde, - struct dir_context *ctx) -{ - struct cached_dirent *dirent; - bool rc; - - list_for_each_entry(dirent, &cde->entries, entry) { - /* - * Skip all early entries prior to the current lseek() - * position. - */ - if (ctx->pos > dirent->pos) - continue; - /* - * We recorded the current ->pos value for the dirent - * when we stored it in the cache. - * However, this sequence of ->pos values may have holes - * in it, for example dot-dirs returned from the server - * are suppressed. - * Handle this bu forcing ctx->pos to be the same as the - * ->pos of the current dirent we emit from the cache. - * This means that when we emit these entries from the cache - * we now emit them with the same ->pos value as in the - * initial scan. - */ - ctx->pos = dirent->pos; - rc = dir_emit(ctx, dirent->name, dirent->namelen, - dirent->fattr.cf_uniqueid, - dirent->fattr.cf_dtype); - if (!rc) - return rc; - ctx->pos++; - } - return true; -} - -static void update_cached_dirents_count(struct cached_dirents *cde, - struct dir_context *ctx) -{ - if (cde->ctx != ctx) - return; - if (cde->is_valid || cde->is_failed) - return; - - cde->pos++; -} - -static void finished_cached_dirents_count(struct cached_dirents *cde, - struct dir_context *ctx) -{ - if (cde->ctx != ctx) - return; - if (cde->is_valid || cde->is_failed) - return; - if (ctx->pos != cde->pos) - return; - - cde->is_valid = 1; -} - -static void add_cached_dirent(struct cached_dirents *cde, - struct dir_context *ctx, - const char *name, int namelen, - struct cifs_fattr *fattr) -{ - struct cached_dirent *de; - - if (cde->ctx != ctx) - return; - if (cde->is_valid || cde->is_failed) - return; - if (ctx->pos != cde->pos) { - cde->is_failed = 1; - return; - } - de = kzalloc(sizeof(*de), GFP_ATOMIC); - if (de == NULL) { - cde->is_failed = 1; - return; - } - de->namelen = namelen; - de->name = kstrndup(name, namelen, GFP_ATOMIC); - if (de->name == NULL) { - kfree(de); - cde->is_failed = 1; - return; - } - de->pos = ctx->pos; - - memcpy(&de->fattr, fattr, sizeof(struct cifs_fattr)); - - list_add_tail(&de->entry, &cde->entries); -} - -static bool cifs_dir_emit(struct dir_context *ctx, - const char *name, int namelen, - struct cifs_fattr *fattr, - struct cached_fid *cfid) -{ - bool rc; - ino_t ino = cifs_uniqueid_to_ino_t(fattr->cf_uniqueid); - - rc = dir_emit(ctx, name, namelen, ino, fattr->cf_dtype); - if (!rc) - return rc; - - if (cfid) { - mutex_lock(&cfid->dirents.de_mutex); - add_cached_dirent(&cfid->dirents, ctx, name, namelen, - fattr); - mutex_unlock(&cfid->dirents.de_mutex); - } - - return rc; -} - -static int cifs_filldir(char *find_entry, struct file *file, - struct dir_context *ctx, - char *scratch_buf, unsigned int max_len, - struct cached_fid *cfid) -{ - struct cifsFileInfo *file_info = file->private_data; - struct super_block *sb = file_inode(file)->i_sb; - struct cifs_sb_info *cifs_sb = CIFS_SB(sb); - struct cifs_dirent de = { NULL, }; - struct cifs_fattr fattr; - struct qstr name; - int rc = 0; - - rc = cifs_fill_dirent(&de, find_entry, file_info->srch_inf.info_level, - file_info->srch_inf.unicode); - if (rc) - return rc; - - if (de.namelen > max_len) { - cifs_dbg(VFS, "bad search response length %zd past smb end\n", - de.namelen); - return -EINVAL; - } - - /* skip . and .. since we added them first */ - if (cifs_entry_is_dot(&de, file_info->srch_inf.unicode)) - return 0; - - if (file_info->srch_inf.unicode) { - struct nls_table *nlt = cifs_sb->local_nls; - int map_type; - - map_type = cifs_remap(cifs_sb); - name.name = scratch_buf; - name.len = - cifs_from_utf16((char *)name.name, (__le16 *)de.name, - UNICODE_NAME_MAX, - min_t(size_t, de.namelen, - (size_t)max_len), nlt, map_type); - name.len -= nls_nullsize(nlt); - } else { - name.name = de.name; - name.len = de.namelen; - } - - switch (file_info->srch_inf.info_level) { - case SMB_FIND_FILE_POSIX_INFO: - cifs_posix_to_fattr(&fattr, - (struct smb2_posix_info *)find_entry, - cifs_sb); - break; - case SMB_FIND_FILE_UNIX: - cifs_unix_basic_to_fattr(&fattr, - &((FILE_UNIX_INFO *)find_entry)->basic, - cifs_sb); - if (S_ISLNK(fattr.cf_mode)) - fattr.cf_flags |= CIFS_FATTR_NEED_REVAL; - break; - case SMB_FIND_FILE_INFO_STANDARD: - cifs_std_info_to_fattr(&fattr, - (FIND_FILE_STANDARD_INFO *)find_entry, - cifs_sb); - break; - case SMB_FIND_FILE_ID_FULL_DIR_INFO: - cifs_fulldir_info_to_fattr(&fattr, - (SEARCH_ID_FULL_DIR_INFO *)find_entry, - cifs_sb); - break; - default: - cifs_dir_info_to_fattr(&fattr, - (FILE_DIRECTORY_INFO *)find_entry, - cifs_sb); - break; - } - - if (de.ino && (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_SERVER_INUM)) { - fattr.cf_uniqueid = de.ino; - } else { - fattr.cf_uniqueid = iunique(sb, ROOT_I); - cifs_autodisable_serverino(cifs_sb); - } - - if ((cifs_sb->mnt_cifs_flags & CIFS_MOUNT_MF_SYMLINKS) && - couldbe_mf_symlink(&fattr)) - /* - * trying to get the type and mode can be slow, - * so just call those regular files for now, and mark - * for reval - */ - fattr.cf_flags |= CIFS_FATTR_NEED_REVAL; - - cifs_prime_dcache(file_dentry(file), &name, &fattr); - - return !cifs_dir_emit(ctx, name.name, name.len, - &fattr, cfid); -} - - -int cifs_readdir(struct file *file, struct dir_context *ctx) -{ - int rc = 0; - unsigned int xid; - int i; - struct tcon_link *tlink = NULL; - struct cifs_tcon *tcon; - struct cifsFileInfo *cifsFile; - char *current_entry; - int num_to_fill = 0; - char *tmp_buf = NULL; - char *end_of_smb; - unsigned int max_len; - const char *full_path; - void *page = alloc_dentry_path(); - struct cached_fid *cfid = NULL; - struct cifs_sb_info *cifs_sb = CIFS_FILE_SB(file); - - xid = get_xid(); - - full_path = build_path_from_dentry(file_dentry(file), page); - if (IS_ERR(full_path)) { - rc = PTR_ERR(full_path); - goto rddir2_exit; - } - - if (file->private_data == NULL) { - tlink = cifs_sb_tlink(cifs_sb); - if (IS_ERR(tlink)) - goto cache_not_found; - tcon = tlink_tcon(tlink); - } else { - cifsFile = file->private_data; - tcon = tlink_tcon(cifsFile->tlink); - } - - rc = open_cached_dir(xid, tcon, full_path, cifs_sb, false, &cfid); - cifs_put_tlink(tlink); - if (rc) - goto cache_not_found; - - mutex_lock(&cfid->dirents.de_mutex); - /* - * If this was reading from the start of the directory - * we need to initialize scanning and storing the - * directory content. - */ - if (ctx->pos == 0 && cfid->dirents.ctx == NULL) { - cfid->dirents.ctx = ctx; - cfid->dirents.pos = 2; - } - /* - * If we already have the entire directory cached then - * we can just serve the cache. - */ - if (cfid->dirents.is_valid) { - if (!dir_emit_dots(file, ctx)) { - mutex_unlock(&cfid->dirents.de_mutex); - goto rddir2_exit; - } - emit_cached_dirents(&cfid->dirents, ctx); - mutex_unlock(&cfid->dirents.de_mutex); - goto rddir2_exit; - } - mutex_unlock(&cfid->dirents.de_mutex); - - /* Drop the cache while calling initiate_cifs_search and - * find_cifs_entry in case there will be reconnects during - * query_directory. - */ - close_cached_dir(cfid); - cfid = NULL; - - cache_not_found: - /* - * Ensure FindFirst doesn't fail before doing filldir() for '.' and - * '..'. Otherwise we won't be able to notify VFS in case of failure. - */ - if (file->private_data == NULL) { - rc = initiate_cifs_search(xid, file, full_path); - cifs_dbg(FYI, "initiate cifs search rc %d\n", rc); - if (rc) - goto rddir2_exit; - } - - if (!dir_emit_dots(file, ctx)) - goto rddir2_exit; - - /* 1) If search is active, - is in current search buffer? - if it before then restart search - if after then keep searching till find it */ - cifsFile = file->private_data; - if (cifsFile->srch_inf.endOfSearch) { - if (cifsFile->srch_inf.emptyDir) { - cifs_dbg(FYI, "End of search, empty dir\n"); - rc = 0; - goto rddir2_exit; - } - } /* else { - cifsFile->invalidHandle = true; - tcon->ses->server->close(xid, tcon, &cifsFile->fid); - } */ - - tcon = tlink_tcon(cifsFile->tlink); - rc = find_cifs_entry(xid, tcon, ctx->pos, file, full_path, - ¤t_entry, &num_to_fill); - open_cached_dir(xid, tcon, full_path, cifs_sb, false, &cfid); - if (rc) { - cifs_dbg(FYI, "fce error %d\n", rc); - goto rddir2_exit; - } else if (current_entry != NULL) { - cifs_dbg(FYI, "entry %lld found\n", ctx->pos); - } else { - if (cfid) { - mutex_lock(&cfid->dirents.de_mutex); - finished_cached_dirents_count(&cfid->dirents, ctx); - mutex_unlock(&cfid->dirents.de_mutex); - } - cifs_dbg(FYI, "Could not find entry\n"); - goto rddir2_exit; - } - cifs_dbg(FYI, "loop through %d times filling dir for net buf %p\n", - num_to_fill, cifsFile->srch_inf.ntwrk_buf_start); - max_len = tcon->ses->server->ops->calc_smb_size( - cifsFile->srch_inf.ntwrk_buf_start); - end_of_smb = cifsFile->srch_inf.ntwrk_buf_start + max_len; - - tmp_buf = kmalloc(UNICODE_NAME_MAX, GFP_KERNEL); - if (tmp_buf == NULL) { - rc = -ENOMEM; - goto rddir2_exit; - } - - for (i = 0; i < num_to_fill; i++) { - if (current_entry == NULL) { - /* evaluate whether this case is an error */ - cifs_dbg(VFS, "past SMB end, num to fill %d i %d\n", - num_to_fill, i); - break; - } - /* - * if buggy server returns . and .. late do we want to - * check for that here? - */ - *tmp_buf = 0; - rc = cifs_filldir(current_entry, file, ctx, - tmp_buf, max_len, cfid); - if (rc) { - if (rc > 0) - rc = 0; - break; - } - - ctx->pos++; - if (cfid) { - mutex_lock(&cfid->dirents.de_mutex); - update_cached_dirents_count(&cfid->dirents, ctx); - mutex_unlock(&cfid->dirents.de_mutex); - } - - if (ctx->pos == - cifsFile->srch_inf.index_of_last_entry) { - cifs_dbg(FYI, "last entry in buf at pos %lld %s\n", - ctx->pos, tmp_buf); - cifs_save_resume_key(current_entry, cifsFile); - break; - } - current_entry = - nxt_dir_entry(current_entry, end_of_smb, - cifsFile->srch_inf.info_level); - } - kfree(tmp_buf); - -rddir2_exit: - if (cfid) - close_cached_dir(cfid); - free_dentry_path(page); - free_xid(xid); - return rc; -} diff --git a/fs/cifs/rfc1002pdu.h b/fs/cifs/rfc1002pdu.h deleted file mode 100644 index ae1d025da294a..0000000000000 --- a/fs/cifs/rfc1002pdu.h +++ /dev/null @@ -1,61 +0,0 @@ -/* SPDX-License-Identifier: LGPL-2.1 */ -/* - * - * Protocol Data Unit definitions for RFC 1001/1002 support - * - * Copyright (c) International Business Machines Corp., 2004 - * Author(s): Steve French (sfrench@us.ibm.com) - * - */ - -/* NB: unlike smb/cifs packets, the RFC1002 structures are big endian */ - - /* RFC 1002 session packet types */ -#define RFC1002_SESSION_MESSAGE 0x00 -#define RFC1002_SESSION_REQUEST 0x81 -#define RFC1002_POSITIVE_SESSION_RESPONSE 0x82 -#define RFC1002_NEGATIVE_SESSION_RESPONSE 0x83 -#define RFC1002_RETARGET_SESSION_RESPONSE 0x84 -#define RFC1002_SESSION_KEEP_ALIVE 0x85 - - /* RFC 1002 flags (only one defined */ -#define RFC1002_LENGTH_EXTEND 0x80 /* high order bit of length (ie +64K) */ - -struct rfc1002_session_packet { - __u8 type; - __u8 flags; - __u16 length; - union { - struct { - __u8 called_len; - __u8 called_name[32]; - __u8 scope1; /* null */ - __u8 calling_len; - __u8 calling_name[32]; - __u8 scope2; /* null */ - } __attribute__((packed)) session_req; - struct { - __u32 retarget_ip_addr; - __u16 port; - } __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 */ - } __attribute__((packed)) trailer; -} __attribute__((packed)); - -/* Negative Session Response error codes */ -#define RFC1002_NOT_LISTENING_CALLED 0x80 /* not listening on called name */ -#define RFC1002_NOT_LISTENING_CALLING 0x81 /* not listening on calling name */ -#define RFC1002_NOT_PRESENT 0x82 /* called name not present */ -#define RFC1002_INSUFFICIENT_RESOURCE 0x83 -#define RFC1002_UNSPECIFIED_ERROR 0x8F - -/* RFC 1002 Datagram service packets are not defined here as they -are not needed for the network filesystem client unless we plan on -implementing broadcast resolution of the server ip address (from -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 " diff --git a/fs/cifs/sess.c b/fs/cifs/sess.c deleted file mode 100644 index 81be17845072a..0000000000000 --- a/fs/cifs/sess.c +++ /dev/null @@ -1,1858 +0,0 @@ -// SPDX-License-Identifier: LGPL-2.1 -/* - * - * SMB/CIFS session setup handling routines - * - * Copyright (c) International Business Machines Corp., 2006, 2009 - * Author(s): Steve French (sfrench@us.ibm.com) - * - */ - -#include "cifspdu.h" -#include "cifsglob.h" -#include "cifsproto.h" -#include "cifs_unicode.h" -#include "cifs_debug.h" -#include "ntlmssp.h" -#include "nterr.h" -#include -#include -#include -#include "cifsfs.h" -#include "cifs_spnego.h" -#include "smb2proto.h" -#include "fs_context.h" - -static int -cifs_ses_add_channel(struct cifs_sb_info *cifs_sb, struct cifs_ses *ses, - struct cifs_server_iface *iface); - -bool -is_server_using_iface(struct TCP_Server_Info *server, - struct cifs_server_iface *iface) -{ - struct sockaddr_in *i4 = (struct sockaddr_in *)&iface->sockaddr; - struct sockaddr_in6 *i6 = (struct sockaddr_in6 *)&iface->sockaddr; - struct sockaddr_in *s4 = (struct sockaddr_in *)&server->dstaddr; - struct sockaddr_in6 *s6 = (struct sockaddr_in6 *)&server->dstaddr; - - if (server->dstaddr.ss_family != iface->sockaddr.ss_family) - return false; - if (server->dstaddr.ss_family == AF_INET) { - if (s4->sin_addr.s_addr != i4->sin_addr.s_addr) - return false; - } else if (server->dstaddr.ss_family == AF_INET6) { - if (memcmp(&s6->sin6_addr, &i6->sin6_addr, - sizeof(i6->sin6_addr)) != 0) - return false; - } else { - /* unknown family.. */ - return false; - } - return true; -} - -bool is_ses_using_iface(struct cifs_ses *ses, struct cifs_server_iface *iface) -{ - int i; - - spin_lock(&ses->chan_lock); - for (i = 0; i < ses->chan_count; i++) { - if (ses->chans[i].iface == iface) { - spin_unlock(&ses->chan_lock); - return true; - } - } - spin_unlock(&ses->chan_lock); - return false; -} - -/* channel helper functions. assumed that chan_lock is held by caller. */ - -unsigned int -cifs_ses_get_chan_index(struct cifs_ses *ses, - struct TCP_Server_Info *server) -{ - unsigned int i; - - for (i = 0; i < ses->chan_count; i++) { - if (ses->chans[i].server == server) - return i; - } - - /* If we didn't find the channel, it is likely a bug */ - if (server) - cifs_dbg(VFS, "unable to get chan index for server: 0x%llx", - server->conn_id); - WARN_ON(1); - return 0; -} - -void -cifs_chan_set_in_reconnect(struct cifs_ses *ses, - struct TCP_Server_Info *server) -{ - unsigned int chan_index = cifs_ses_get_chan_index(ses, server); - - ses->chans[chan_index].in_reconnect = true; -} - -void -cifs_chan_clear_in_reconnect(struct cifs_ses *ses, - struct TCP_Server_Info *server) -{ - unsigned int chan_index = cifs_ses_get_chan_index(ses, server); - - ses->chans[chan_index].in_reconnect = false; -} - -bool -cifs_chan_in_reconnect(struct cifs_ses *ses, - struct TCP_Server_Info *server) -{ - unsigned int chan_index = cifs_ses_get_chan_index(ses, server); - - return CIFS_CHAN_IN_RECONNECT(ses, chan_index); -} - -void -cifs_chan_set_need_reconnect(struct cifs_ses *ses, - struct TCP_Server_Info *server) -{ - unsigned int chan_index = cifs_ses_get_chan_index(ses, server); - - set_bit(chan_index, &ses->chans_need_reconnect); - cifs_dbg(FYI, "Set reconnect bitmask for chan %u; now 0x%lx\n", - chan_index, ses->chans_need_reconnect); -} - -void -cifs_chan_clear_need_reconnect(struct cifs_ses *ses, - struct TCP_Server_Info *server) -{ - unsigned int chan_index = cifs_ses_get_chan_index(ses, server); - - clear_bit(chan_index, &ses->chans_need_reconnect); - cifs_dbg(FYI, "Cleared reconnect bitmask for chan %u; now 0x%lx\n", - chan_index, ses->chans_need_reconnect); -} - -bool -cifs_chan_needs_reconnect(struct cifs_ses *ses, - struct TCP_Server_Info *server) -{ - unsigned int chan_index = cifs_ses_get_chan_index(ses, server); - - return CIFS_CHAN_NEEDS_RECONNECT(ses, chan_index); -} - -bool -cifs_chan_is_iface_active(struct cifs_ses *ses, - struct TCP_Server_Info *server) -{ - unsigned int chan_index = cifs_ses_get_chan_index(ses, server); - - return ses->chans[chan_index].iface && - ses->chans[chan_index].iface->is_active; -} - -/* returns number of channels added */ -int cifs_try_adding_channels(struct cifs_sb_info *cifs_sb, struct cifs_ses *ses) -{ - struct TCP_Server_Info *server = ses->server; - int old_chan_count, new_chan_count; - int left; - int rc = 0; - int tries = 0; - struct cifs_server_iface *iface = NULL, *niface = NULL; - - spin_lock(&ses->chan_lock); - - new_chan_count = old_chan_count = ses->chan_count; - left = ses->chan_max - ses->chan_count; - - if (left <= 0) { - spin_unlock(&ses->chan_lock); - cifs_dbg(FYI, - "ses already at max_channels (%zu), nothing to open\n", - ses->chan_max); - return 0; - } - - if (server->dialect < SMB30_PROT_ID) { - spin_unlock(&ses->chan_lock); - cifs_dbg(VFS, "multichannel is not supported on this protocol version, use 3.0 or above\n"); - return 0; - } - - if (!(server->capabilities & SMB2_GLOBAL_CAP_MULTI_CHANNEL)) { - ses->chan_max = 1; - spin_unlock(&ses->chan_lock); - cifs_server_dbg(VFS, "no multichannel support\n"); - return 0; - } - spin_unlock(&ses->chan_lock); - - /* - * Keep connecting to same, fastest, iface for all channels as - * long as its RSS. Try next fastest one if not RSS or channel - * creation fails. - */ - spin_lock(&ses->iface_lock); - iface = list_first_entry(&ses->iface_list, struct cifs_server_iface, - iface_head); - spin_unlock(&ses->iface_lock); - - while (left > 0) { - - tries++; - if (tries > 3*ses->chan_max) { - cifs_dbg(FYI, "too many channel open attempts (%d channels left to open)\n", - left); - break; - } - - spin_lock(&ses->iface_lock); - if (!ses->iface_count) { - spin_unlock(&ses->iface_lock); - break; - } - - list_for_each_entry_safe_from(iface, niface, &ses->iface_list, - iface_head) { - /* skip ifaces that are unusable */ - if (!iface->is_active || - (is_ses_using_iface(ses, iface) && - !iface->rss_capable)) { - continue; - } - - /* take ref before unlock */ - kref_get(&iface->refcount); - - spin_unlock(&ses->iface_lock); - rc = cifs_ses_add_channel(cifs_sb, ses, iface); - spin_lock(&ses->iface_lock); - - if (rc) { - cifs_dbg(VFS, "failed to open extra channel on iface:%pIS rc=%d\n", - &iface->sockaddr, - rc); - kref_put(&iface->refcount, release_iface); - continue; - } - - cifs_dbg(FYI, "successfully opened new channel on iface:%pIS\n", - &iface->sockaddr); - break; - } - spin_unlock(&ses->iface_lock); - - left--; - new_chan_count++; - } - - return new_chan_count - old_chan_count; -} - -/* - * update the iface for the channel if necessary. - * will return 0 when iface is updated, 1 if removed, 2 otherwise - * Must be called with chan_lock held. - */ -int -cifs_chan_update_iface(struct cifs_ses *ses, struct TCP_Server_Info *server) -{ - unsigned int chan_index; - struct cifs_server_iface *iface = NULL; - struct cifs_server_iface *old_iface = NULL; - int rc = 0; - - spin_lock(&ses->chan_lock); - chan_index = cifs_ses_get_chan_index(ses, server); - if (!chan_index) { - spin_unlock(&ses->chan_lock); - return 0; - } - - if (ses->chans[chan_index].iface) { - old_iface = ses->chans[chan_index].iface; - if (old_iface->is_active) { - spin_unlock(&ses->chan_lock); - return 1; - } - } - spin_unlock(&ses->chan_lock); - - spin_lock(&ses->iface_lock); - /* then look for a new one */ - list_for_each_entry(iface, &ses->iface_list, iface_head) { - if (!iface->is_active || - (is_ses_using_iface(ses, iface) && - !iface->rss_capable)) { - continue; - } - kref_get(&iface->refcount); - break; - } - - if (list_entry_is_head(iface, &ses->iface_list, iface_head)) { - rc = 1; - iface = NULL; - cifs_dbg(FYI, "unable to find a suitable iface\n"); - } - - /* now drop the ref to the current iface */ - if (old_iface && iface) { - cifs_dbg(FYI, "replacing iface: %pIS with %pIS\n", - &old_iface->sockaddr, - &iface->sockaddr); - kref_put(&old_iface->refcount, release_iface); - } else if (old_iface) { - cifs_dbg(FYI, "releasing ref to iface: %pIS\n", - &old_iface->sockaddr); - kref_put(&old_iface->refcount, release_iface); - } else { - WARN_ON(!iface); - cifs_dbg(FYI, "adding new iface: %pIS\n", &iface->sockaddr); - } - spin_unlock(&ses->iface_lock); - - spin_lock(&ses->chan_lock); - chan_index = cifs_ses_get_chan_index(ses, server); - ses->chans[chan_index].iface = iface; - - /* No iface is found. if secondary chan, drop connection */ - if (!iface && CIFS_SERVER_IS_CHAN(server)) - ses->chans[chan_index].server = NULL; - - spin_unlock(&ses->chan_lock); - - if (!iface && CIFS_SERVER_IS_CHAN(server)) - cifs_put_tcp_session(server, false); - - return rc; -} - -/* - * If server is a channel of ses, return the corresponding enclosing - * cifs_chan otherwise return NULL. - */ -struct cifs_chan * -cifs_ses_find_chan(struct cifs_ses *ses, struct TCP_Server_Info *server) -{ - int i; - - spin_lock(&ses->chan_lock); - for (i = 0; i < ses->chan_count; i++) { - if (ses->chans[i].server == server) { - spin_unlock(&ses->chan_lock); - return &ses->chans[i]; - } - } - spin_unlock(&ses->chan_lock); - return NULL; -} - -static int -cifs_ses_add_channel(struct cifs_sb_info *cifs_sb, struct cifs_ses *ses, - struct cifs_server_iface *iface) -{ - struct TCP_Server_Info *chan_server; - struct cifs_chan *chan; - struct smb3_fs_context ctx = {NULL}; - static const char unc_fmt[] = "\\%s\\foo"; - char unc[sizeof(unc_fmt)+SERVER_NAME_LEN_WITH_NULL] = {0}; - struct sockaddr_in *ipv4 = (struct sockaddr_in *)&iface->sockaddr; - struct sockaddr_in6 *ipv6 = (struct sockaddr_in6 *)&iface->sockaddr; - int rc; - unsigned int xid = get_xid(); - - if (iface->sockaddr.ss_family == AF_INET) - cifs_dbg(FYI, "adding channel to ses %p (speed:%zu bps rdma:%s ip:%pI4)\n", - ses, iface->speed, iface->rdma_capable ? "yes" : "no", - &ipv4->sin_addr); - else - cifs_dbg(FYI, "adding channel to ses %p (speed:%zu bps rdma:%s ip:%pI6)\n", - ses, iface->speed, iface->rdma_capable ? "yes" : "no", - &ipv6->sin6_addr); - - /* - * Setup a ctx with mostly the same info as the existing - * session and overwrite it with the requested iface data. - * - * We need to setup at least the fields used for negprot and - * sesssetup. - * - * We only need the ctx here, so we can reuse memory from - * the session and server without caring about memory - * management. - */ - - /* Always make new connection for now (TODO?) */ - ctx.nosharesock = true; - - /* Auth */ - ctx.domainauto = ses->domainAuto; - ctx.domainname = ses->domainName; - - /* no hostname for extra channels */ - ctx.server_hostname = ""; - - ctx.username = ses->user_name; - ctx.password = ses->password; - ctx.sectype = ses->sectype; - ctx.sign = ses->sign; - - /* UNC and paths */ - /* XXX: Use ses->server->hostname? */ - sprintf(unc, unc_fmt, ses->ip_addr); - ctx.UNC = unc; - ctx.prepath = ""; - - /* Reuse same version as master connection */ - ctx.vals = ses->server->vals; - ctx.ops = ses->server->ops; - - ctx.noblocksnd = ses->server->noblocksnd; - ctx.noautotune = ses->server->noautotune; - ctx.sockopt_tcp_nodelay = ses->server->tcp_nodelay; - ctx.echo_interval = ses->server->echo_interval / HZ; - ctx.max_credits = ses->server->max_credits; - - /* - * This will be used for encoding/decoding user/domain/pw - * during sess setup auth. - */ - ctx.local_nls = cifs_sb->local_nls; - - /* Use RDMA if possible */ - ctx.rdma = iface->rdma_capable; - memcpy(&ctx.dstaddr, &iface->sockaddr, sizeof(struct sockaddr_storage)); - - /* reuse master con client guid */ - memcpy(&ctx.client_guid, ses->server->client_guid, - SMB2_CLIENT_GUID_SIZE); - ctx.use_client_guid = true; - - chan_server = cifs_get_tcp_session(&ctx, ses->server); - - spin_lock(&ses->chan_lock); - chan = &ses->chans[ses->chan_count]; - chan->server = chan_server; - if (IS_ERR(chan->server)) { - rc = PTR_ERR(chan->server); - chan->server = NULL; - spin_unlock(&ses->chan_lock); - goto out; - } - chan->iface = iface; - ses->chan_count++; - atomic_set(&ses->chan_seq, 0); - - /* Mark this channel as needing connect/setup */ - cifs_chan_set_need_reconnect(ses, chan->server); - - spin_unlock(&ses->chan_lock); - - mutex_lock(&ses->session_mutex); - /* - * We need to allocate the server crypto now as we will need - * to sign packets before we generate the channel signing key - * (we sign with the session key) - */ - rc = smb311_crypto_shash_allocate(chan->server); - if (rc) { - cifs_dbg(VFS, "%s: crypto alloc failed\n", __func__); - mutex_unlock(&ses->session_mutex); - goto out; - } - - rc = cifs_negotiate_protocol(xid, ses, chan->server); - if (!rc) - rc = cifs_setup_session(xid, ses, chan->server, cifs_sb->local_nls); - - mutex_unlock(&ses->session_mutex); - -out: - if (rc && chan->server) { - /* - * we should avoid race with these delayed works before we - * remove this channel - */ - cancel_delayed_work_sync(&chan->server->echo); - cancel_delayed_work_sync(&chan->server->resolve); - cancel_delayed_work_sync(&chan->server->reconnect); - - spin_lock(&ses->chan_lock); - /* we rely on all bits beyond chan_count to be clear */ - cifs_chan_clear_need_reconnect(ses, chan->server); - ses->chan_count--; - /* - * chan_count should never reach 0 as at least the primary - * channel is always allocated - */ - WARN_ON(ses->chan_count < 1); - spin_unlock(&ses->chan_lock); - - cifs_put_tcp_session(chan->server, 0); - } - - free_xid(xid); - return rc; -} - -#ifdef CONFIG_CIFS_ALLOW_INSECURE_LEGACY -static __u32 cifs_ssetup_hdr(struct cifs_ses *ses, - struct TCP_Server_Info *server, - SESSION_SETUP_ANDX *pSMB) -{ - __u32 capabilities = 0; - - /* init fields common to all four types of SessSetup */ - /* Note that offsets for first seven fields in req struct are same */ - /* in CIFS Specs so does not matter which of 3 forms of struct */ - /* that we use in next few lines */ - /* Note that header is initialized to zero in header_assemble */ - pSMB->req.AndXCommand = 0xFF; - pSMB->req.MaxBufferSize = cpu_to_le16(min_t(u32, - CIFSMaxBufSize + MAX_CIFS_HDR_SIZE - 4, - USHRT_MAX)); - pSMB->req.MaxMpxCount = cpu_to_le16(server->maxReq); - pSMB->req.VcNumber = cpu_to_le16(1); - - /* Now no need to set SMBFLG_CASELESS or obsolete CANONICAL PATH */ - - /* BB verify whether signing required on neg or just on auth frame - (and NTLM case) */ - - capabilities = CAP_LARGE_FILES | CAP_NT_SMBS | CAP_LEVEL_II_OPLOCKS | - CAP_LARGE_WRITE_X | CAP_LARGE_READ_X; - - if (server->sign) - pSMB->req.hdr.Flags2 |= SMBFLG2_SECURITY_SIGNATURE; - - if (ses->capabilities & CAP_UNICODE) { - pSMB->req.hdr.Flags2 |= SMBFLG2_UNICODE; - capabilities |= CAP_UNICODE; - } - if (ses->capabilities & CAP_STATUS32) { - pSMB->req.hdr.Flags2 |= SMBFLG2_ERR_STATUS; - capabilities |= CAP_STATUS32; - } - if (ses->capabilities & CAP_DFS) { - pSMB->req.hdr.Flags2 |= SMBFLG2_DFS; - capabilities |= CAP_DFS; - } - if (ses->capabilities & CAP_UNIX) - capabilities |= CAP_UNIX; - - return capabilities; -} - -static void -unicode_oslm_strings(char **pbcc_area, const struct nls_table *nls_cp) -{ - char *bcc_ptr = *pbcc_area; - int bytes_ret = 0; - - /* Copy OS version */ - bytes_ret = cifs_strtoUTF16((__le16 *)bcc_ptr, "Linux version ", 32, - nls_cp); - bcc_ptr += 2 * bytes_ret; - bytes_ret = cifs_strtoUTF16((__le16 *) bcc_ptr, init_utsname()->release, - 32, nls_cp); - bcc_ptr += 2 * bytes_ret; - bcc_ptr += 2; /* trailing null */ - - bytes_ret = cifs_strtoUTF16((__le16 *) bcc_ptr, CIFS_NETWORK_OPSYS, - 32, nls_cp); - bcc_ptr += 2 * bytes_ret; - bcc_ptr += 2; /* trailing null */ - - *pbcc_area = bcc_ptr; -} - -static void unicode_domain_string(char **pbcc_area, struct cifs_ses *ses, - const struct nls_table *nls_cp) -{ - char *bcc_ptr = *pbcc_area; - int bytes_ret = 0; - - /* copy domain */ - if (ses->domainName == NULL) { - /* Sending null domain better than using a bogus domain name (as - we did briefly in 2.6.18) since server will use its default */ - *bcc_ptr = 0; - *(bcc_ptr+1) = 0; - bytes_ret = 0; - } else - bytes_ret = cifs_strtoUTF16((__le16 *) bcc_ptr, ses->domainName, - CIFS_MAX_DOMAINNAME_LEN, nls_cp); - bcc_ptr += 2 * bytes_ret; - bcc_ptr += 2; /* account for null terminator */ - - *pbcc_area = bcc_ptr; -} - -static void unicode_ssetup_strings(char **pbcc_area, struct cifs_ses *ses, - const struct nls_table *nls_cp) -{ - char *bcc_ptr = *pbcc_area; - int bytes_ret = 0; - - /* BB FIXME add check that strings total less - than 335 or will need to send them as arrays */ - - /* copy user */ - if (ses->user_name == NULL) { - /* null user mount */ - *bcc_ptr = 0; - *(bcc_ptr+1) = 0; - } else { - bytes_ret = cifs_strtoUTF16((__le16 *) bcc_ptr, ses->user_name, - CIFS_MAX_USERNAME_LEN, nls_cp); - } - bcc_ptr += 2 * bytes_ret; - bcc_ptr += 2; /* account for null termination */ - - unicode_domain_string(&bcc_ptr, ses, nls_cp); - unicode_oslm_strings(&bcc_ptr, nls_cp); - - *pbcc_area = bcc_ptr; -} - -static void ascii_ssetup_strings(char **pbcc_area, struct cifs_ses *ses, - const struct nls_table *nls_cp) -{ - char *bcc_ptr = *pbcc_area; - int len; - - /* copy user */ - /* BB what about null user mounts - check that we do this BB */ - /* copy user */ - if (ses->user_name != NULL) { - len = strscpy(bcc_ptr, ses->user_name, CIFS_MAX_USERNAME_LEN); - if (WARN_ON_ONCE(len < 0)) - len = CIFS_MAX_USERNAME_LEN - 1; - bcc_ptr += len; - } - /* else null user mount */ - *bcc_ptr = 0; - bcc_ptr++; /* account for null termination */ - - /* copy domain */ - if (ses->domainName != NULL) { - len = strscpy(bcc_ptr, ses->domainName, CIFS_MAX_DOMAINNAME_LEN); - if (WARN_ON_ONCE(len < 0)) - len = CIFS_MAX_DOMAINNAME_LEN - 1; - bcc_ptr += len; - } /* else we will send a null domain name - so the server will default to its own domain */ - *bcc_ptr = 0; - bcc_ptr++; - - /* BB check for overflow here */ - - strcpy(bcc_ptr, "Linux version "); - bcc_ptr += strlen("Linux version "); - strcpy(bcc_ptr, init_utsname()->release); - bcc_ptr += strlen(init_utsname()->release) + 1; - - strcpy(bcc_ptr, CIFS_NETWORK_OPSYS); - bcc_ptr += strlen(CIFS_NETWORK_OPSYS) + 1; - - *pbcc_area = bcc_ptr; -} - -static void -decode_unicode_ssetup(char **pbcc_area, int bleft, struct cifs_ses *ses, - const struct nls_table *nls_cp) -{ - int len; - char *data = *pbcc_area; - - cifs_dbg(FYI, "bleft %d\n", bleft); - - kfree(ses->serverOS); - ses->serverOS = cifs_strndup_from_utf16(data, bleft, true, nls_cp); - cifs_dbg(FYI, "serverOS=%s\n", ses->serverOS); - len = (UniStrnlen((wchar_t *) data, bleft / 2) * 2) + 2; - data += len; - bleft -= len; - if (bleft <= 0) - return; - - kfree(ses->serverNOS); - ses->serverNOS = cifs_strndup_from_utf16(data, bleft, true, nls_cp); - cifs_dbg(FYI, "serverNOS=%s\n", ses->serverNOS); - len = (UniStrnlen((wchar_t *) data, bleft / 2) * 2) + 2; - data += len; - bleft -= len; - if (bleft <= 0) - return; - - kfree(ses->serverDomain); - ses->serverDomain = cifs_strndup_from_utf16(data, bleft, true, nls_cp); - cifs_dbg(FYI, "serverDomain=%s\n", ses->serverDomain); - - return; -} - -static void decode_ascii_ssetup(char **pbcc_area, __u16 bleft, - struct cifs_ses *ses, - const struct nls_table *nls_cp) -{ - int len; - char *bcc_ptr = *pbcc_area; - - cifs_dbg(FYI, "decode sessetup ascii. bleft %d\n", bleft); - - len = strnlen(bcc_ptr, bleft); - if (len >= bleft) - return; - - kfree(ses->serverOS); - - ses->serverOS = kmalloc(len + 1, GFP_KERNEL); - if (ses->serverOS) { - memcpy(ses->serverOS, bcc_ptr, len); - ses->serverOS[len] = 0; - if (strncmp(ses->serverOS, "OS/2", 4) == 0) - cifs_dbg(FYI, "OS/2 server\n"); - } - - bcc_ptr += len + 1; - bleft -= len + 1; - - len = strnlen(bcc_ptr, bleft); - if (len >= bleft) - return; - - kfree(ses->serverNOS); - - ses->serverNOS = kmalloc(len + 1, GFP_KERNEL); - if (ses->serverNOS) { - memcpy(ses->serverNOS, bcc_ptr, len); - ses->serverNOS[len] = 0; - } - - bcc_ptr += len + 1; - bleft -= len + 1; - - len = strnlen(bcc_ptr, bleft); - if (len > bleft) - return; - - /* No domain field in LANMAN case. Domain is - returned by old servers in the SMB negprot response */ - /* BB For newer servers which do not support Unicode, - but thus do return domain here we could add parsing - for it later, but it is not very important */ - cifs_dbg(FYI, "ascii: bytes left %d\n", bleft); -} -#endif /* CONFIG_CIFS_ALLOW_INSECURE_LEGACY */ - -int decode_ntlmssp_challenge(char *bcc_ptr, int blob_len, - struct cifs_ses *ses) -{ - unsigned int tioffset; /* challenge message target info area */ - unsigned int tilen; /* challenge message target info area length */ - CHALLENGE_MESSAGE *pblob = (CHALLENGE_MESSAGE *)bcc_ptr; - __u32 server_flags; - - if (blob_len < sizeof(CHALLENGE_MESSAGE)) { - cifs_dbg(VFS, "challenge blob len %d too small\n", blob_len); - return -EINVAL; - } - - if (memcmp(pblob->Signature, "NTLMSSP", 8)) { - cifs_dbg(VFS, "blob signature incorrect %s\n", - pblob->Signature); - return -EINVAL; - } - if (pblob->MessageType != NtLmChallenge) { - cifs_dbg(VFS, "Incorrect message type %d\n", - pblob->MessageType); - return -EINVAL; - } - - server_flags = le32_to_cpu(pblob->NegotiateFlags); - cifs_dbg(FYI, "%s: negotiate=0x%08x challenge=0x%08x\n", __func__, - ses->ntlmssp->client_flags, server_flags); - - if ((ses->ntlmssp->client_flags & (NTLMSSP_NEGOTIATE_SEAL | NTLMSSP_NEGOTIATE_SIGN)) && - (!(server_flags & NTLMSSP_NEGOTIATE_56) && !(server_flags & NTLMSSP_NEGOTIATE_128))) { - cifs_dbg(VFS, "%s: requested signing/encryption but server did not return either 56-bit or 128-bit session key size\n", - __func__); - return -EINVAL; - } - if (!(server_flags & NTLMSSP_NEGOTIATE_NTLM) && !(server_flags & NTLMSSP_NEGOTIATE_EXTENDED_SEC)) { - cifs_dbg(VFS, "%s: server does not seem to support either NTLMv1 or NTLMv2\n", __func__); - return -EINVAL; - } - if (ses->server->sign && !(server_flags & NTLMSSP_NEGOTIATE_SIGN)) { - cifs_dbg(VFS, "%s: forced packet signing but server does not seem to support it\n", - __func__); - return -EOPNOTSUPP; - } - if ((ses->ntlmssp->client_flags & NTLMSSP_NEGOTIATE_KEY_XCH) && - !(server_flags & NTLMSSP_NEGOTIATE_KEY_XCH)) - pr_warn_once("%s: authentication has been weakened as server does not support key exchange\n", - __func__); - - ses->ntlmssp->server_flags = server_flags; - - memcpy(ses->ntlmssp->cryptkey, pblob->Challenge, CIFS_CRYPTO_KEY_SIZE); - /* In particular we can examine sign flags */ - /* BB spec says that if AvId field of MsvAvTimestamp is populated then - we must set the MIC field of the AUTHENTICATE_MESSAGE */ - - tioffset = le32_to_cpu(pblob->TargetInfoArray.BufferOffset); - tilen = le16_to_cpu(pblob->TargetInfoArray.Length); - if (tioffset > blob_len || tioffset + tilen > blob_len) { - cifs_dbg(VFS, "tioffset + tilen too high %u + %u\n", - tioffset, tilen); - return -EINVAL; - } - if (tilen) { - kfree_sensitive(ses->auth_key.response); - ses->auth_key.response = kmemdup(bcc_ptr + tioffset, tilen, - GFP_KERNEL); - if (!ses->auth_key.response) { - cifs_dbg(VFS, "Challenge target info alloc failure\n"); - return -ENOMEM; - } - ses->auth_key.len = tilen; - } - - return 0; -} - -static int size_of_ntlmssp_blob(struct cifs_ses *ses, int base_size) -{ - int sz = base_size + ses->auth_key.len - - CIFS_SESS_KEY_SIZE + CIFS_CPHTXT_SIZE + 2; - - if (ses->domainName) - sz += sizeof(__le16) * strnlen(ses->domainName, CIFS_MAX_DOMAINNAME_LEN); - else - sz += sizeof(__le16); - - if (ses->user_name) - sz += sizeof(__le16) * strnlen(ses->user_name, CIFS_MAX_USERNAME_LEN); - else - sz += sizeof(__le16); - - if (ses->workstation_name[0]) - sz += sizeof(__le16) * strnlen(ses->workstation_name, - ntlmssp_workstation_name_size(ses)); - else - sz += sizeof(__le16); - - return sz; -} - -static inline void cifs_security_buffer_from_str(SECURITY_BUFFER *pbuf, - char *str_value, - int str_length, - unsigned char *pstart, - unsigned char **pcur, - const struct nls_table *nls_cp) -{ - unsigned char *tmp = pstart; - int len; - - if (!pbuf) - return; - - if (!pcur) - pcur = &tmp; - - if (!str_value) { - pbuf->BufferOffset = cpu_to_le32(*pcur - pstart); - pbuf->Length = 0; - pbuf->MaximumLength = 0; - *pcur += sizeof(__le16); - } else { - len = cifs_strtoUTF16((__le16 *)*pcur, - str_value, - str_length, - nls_cp); - len *= sizeof(__le16); - pbuf->BufferOffset = cpu_to_le32(*pcur - pstart); - pbuf->Length = cpu_to_le16(len); - pbuf->MaximumLength = cpu_to_le16(len); - *pcur += len; - } -} - -/* BB Move to ntlmssp.c eventually */ - -int build_ntlmssp_negotiate_blob(unsigned char **pbuffer, - u16 *buflen, - struct cifs_ses *ses, - struct TCP_Server_Info *server, - const struct nls_table *nls_cp) -{ - int rc = 0; - NEGOTIATE_MESSAGE *sec_blob; - __u32 flags; - unsigned char *tmp; - int len; - - len = size_of_ntlmssp_blob(ses, sizeof(NEGOTIATE_MESSAGE)); - *pbuffer = kmalloc(len, GFP_KERNEL); - if (!*pbuffer) { - rc = -ENOMEM; - cifs_dbg(VFS, "Error %d during NTLMSSP allocation\n", rc); - *buflen = 0; - goto setup_ntlm_neg_ret; - } - sec_blob = (NEGOTIATE_MESSAGE *)*pbuffer; - - memset(*pbuffer, 0, sizeof(NEGOTIATE_MESSAGE)); - memcpy(sec_blob->Signature, NTLMSSP_SIGNATURE, 8); - sec_blob->MessageType = NtLmNegotiate; - - /* BB is NTLMV2 session security format easier to use here? */ - flags = NTLMSSP_NEGOTIATE_56 | NTLMSSP_REQUEST_TARGET | - NTLMSSP_NEGOTIATE_128 | NTLMSSP_NEGOTIATE_UNICODE | - NTLMSSP_NEGOTIATE_NTLM | NTLMSSP_NEGOTIATE_EXTENDED_SEC | - NTLMSSP_NEGOTIATE_ALWAYS_SIGN | NTLMSSP_NEGOTIATE_SEAL | - NTLMSSP_NEGOTIATE_SIGN; - if (!server->session_estab || ses->ntlmssp->sesskey_per_smbsess) - flags |= NTLMSSP_NEGOTIATE_KEY_XCH; - - tmp = *pbuffer + sizeof(NEGOTIATE_MESSAGE); - ses->ntlmssp->client_flags = flags; - sec_blob->NegotiateFlags = cpu_to_le32(flags); - - /* these fields should be null in negotiate phase MS-NLMP 3.1.5.1.1 */ - cifs_security_buffer_from_str(&sec_blob->DomainName, - NULL, - CIFS_MAX_DOMAINNAME_LEN, - *pbuffer, &tmp, - nls_cp); - - cifs_security_buffer_from_str(&sec_blob->WorkstationName, - NULL, - CIFS_MAX_WORKSTATION_LEN, - *pbuffer, &tmp, - nls_cp); - - *buflen = tmp - *pbuffer; -setup_ntlm_neg_ret: - return rc; -} - -/* - * Build ntlmssp blob with additional fields, such as version, - * supported by modern servers. For safety limit to SMB3 or later - * See notes in MS-NLMP Section 2.2.2.1 e.g. - */ -int build_ntlmssp_smb3_negotiate_blob(unsigned char **pbuffer, - u16 *buflen, - struct cifs_ses *ses, - struct TCP_Server_Info *server, - const struct nls_table *nls_cp) -{ - int rc = 0; - struct negotiate_message *sec_blob; - __u32 flags; - unsigned char *tmp; - int len; - - len = size_of_ntlmssp_blob(ses, sizeof(struct negotiate_message)); - *pbuffer = kmalloc(len, GFP_KERNEL); - if (!*pbuffer) { - rc = -ENOMEM; - cifs_dbg(VFS, "Error %d during NTLMSSP allocation\n", rc); - *buflen = 0; - goto setup_ntlm_smb3_neg_ret; - } - sec_blob = (struct negotiate_message *)*pbuffer; - - memset(*pbuffer, 0, sizeof(struct negotiate_message)); - memcpy(sec_blob->Signature, NTLMSSP_SIGNATURE, 8); - sec_blob->MessageType = NtLmNegotiate; - - /* BB is NTLMV2 session security format easier to use here? */ - flags = NTLMSSP_NEGOTIATE_56 | NTLMSSP_REQUEST_TARGET | - NTLMSSP_NEGOTIATE_128 | NTLMSSP_NEGOTIATE_UNICODE | - NTLMSSP_NEGOTIATE_NTLM | NTLMSSP_NEGOTIATE_EXTENDED_SEC | - NTLMSSP_NEGOTIATE_ALWAYS_SIGN | NTLMSSP_NEGOTIATE_SEAL | - NTLMSSP_NEGOTIATE_SIGN | NTLMSSP_NEGOTIATE_VERSION; - if (!server->session_estab || ses->ntlmssp->sesskey_per_smbsess) - flags |= NTLMSSP_NEGOTIATE_KEY_XCH; - - sec_blob->Version.ProductMajorVersion = LINUX_VERSION_MAJOR; - sec_blob->Version.ProductMinorVersion = LINUX_VERSION_PATCHLEVEL; - sec_blob->Version.ProductBuild = cpu_to_le16(SMB3_PRODUCT_BUILD); - sec_blob->Version.NTLMRevisionCurrent = NTLMSSP_REVISION_W2K3; - - tmp = *pbuffer + sizeof(struct negotiate_message); - ses->ntlmssp->client_flags = flags; - sec_blob->NegotiateFlags = cpu_to_le32(flags); - - /* these fields should be null in negotiate phase MS-NLMP 3.1.5.1.1 */ - cifs_security_buffer_from_str(&sec_blob->DomainName, - NULL, - CIFS_MAX_DOMAINNAME_LEN, - *pbuffer, &tmp, - nls_cp); - - cifs_security_buffer_from_str(&sec_blob->WorkstationName, - NULL, - CIFS_MAX_WORKSTATION_LEN, - *pbuffer, &tmp, - nls_cp); - - *buflen = tmp - *pbuffer; -setup_ntlm_smb3_neg_ret: - return rc; -} - - -int build_ntlmssp_auth_blob(unsigned char **pbuffer, - u16 *buflen, - struct cifs_ses *ses, - struct TCP_Server_Info *server, - const struct nls_table *nls_cp) -{ - int rc; - AUTHENTICATE_MESSAGE *sec_blob; - __u32 flags; - unsigned char *tmp; - int len; - - rc = setup_ntlmv2_rsp(ses, nls_cp); - if (rc) { - cifs_dbg(VFS, "Error %d during NTLMSSP authentication\n", rc); - *buflen = 0; - goto setup_ntlmv2_ret; - } - - len = size_of_ntlmssp_blob(ses, sizeof(AUTHENTICATE_MESSAGE)); - *pbuffer = kmalloc(len, GFP_KERNEL); - if (!*pbuffer) { - rc = -ENOMEM; - cifs_dbg(VFS, "Error %d during NTLMSSP allocation\n", rc); - *buflen = 0; - goto setup_ntlmv2_ret; - } - sec_blob = (AUTHENTICATE_MESSAGE *)*pbuffer; - - memcpy(sec_blob->Signature, NTLMSSP_SIGNATURE, 8); - sec_blob->MessageType = NtLmAuthenticate; - - flags = ses->ntlmssp->server_flags | NTLMSSP_REQUEST_TARGET | - NTLMSSP_NEGOTIATE_TARGET_INFO | NTLMSSP_NEGOTIATE_WORKSTATION_SUPPLIED; - - tmp = *pbuffer + sizeof(AUTHENTICATE_MESSAGE); - sec_blob->NegotiateFlags = cpu_to_le32(flags); - - sec_blob->LmChallengeResponse.BufferOffset = - cpu_to_le32(sizeof(AUTHENTICATE_MESSAGE)); - sec_blob->LmChallengeResponse.Length = 0; - sec_blob->LmChallengeResponse.MaximumLength = 0; - - sec_blob->NtChallengeResponse.BufferOffset = - cpu_to_le32(tmp - *pbuffer); - if (ses->user_name != NULL) { - memcpy(tmp, ses->auth_key.response + CIFS_SESS_KEY_SIZE, - ses->auth_key.len - CIFS_SESS_KEY_SIZE); - tmp += ses->auth_key.len - CIFS_SESS_KEY_SIZE; - - sec_blob->NtChallengeResponse.Length = - cpu_to_le16(ses->auth_key.len - CIFS_SESS_KEY_SIZE); - sec_blob->NtChallengeResponse.MaximumLength = - cpu_to_le16(ses->auth_key.len - CIFS_SESS_KEY_SIZE); - } else { - /* - * don't send an NT Response for anonymous access - */ - sec_blob->NtChallengeResponse.Length = 0; - sec_blob->NtChallengeResponse.MaximumLength = 0; - } - - cifs_security_buffer_from_str(&sec_blob->DomainName, - ses->domainName, - CIFS_MAX_DOMAINNAME_LEN, - *pbuffer, &tmp, - nls_cp); - - cifs_security_buffer_from_str(&sec_blob->UserName, - ses->user_name, - CIFS_MAX_USERNAME_LEN, - *pbuffer, &tmp, - nls_cp); - - cifs_security_buffer_from_str(&sec_blob->WorkstationName, - ses->workstation_name, - ntlmssp_workstation_name_size(ses), - *pbuffer, &tmp, - nls_cp); - - if ((ses->ntlmssp->server_flags & NTLMSSP_NEGOTIATE_KEY_XCH) && - (!ses->server->session_estab || ses->ntlmssp->sesskey_per_smbsess) && - !calc_seckey(ses)) { - memcpy(tmp, ses->ntlmssp->ciphertext, CIFS_CPHTXT_SIZE); - sec_blob->SessionKey.BufferOffset = cpu_to_le32(tmp - *pbuffer); - sec_blob->SessionKey.Length = cpu_to_le16(CIFS_CPHTXT_SIZE); - sec_blob->SessionKey.MaximumLength = - cpu_to_le16(CIFS_CPHTXT_SIZE); - tmp += CIFS_CPHTXT_SIZE; - } else { - sec_blob->SessionKey.BufferOffset = cpu_to_le32(tmp - *pbuffer); - sec_blob->SessionKey.Length = 0; - sec_blob->SessionKey.MaximumLength = 0; - } - - *buflen = tmp - *pbuffer; -setup_ntlmv2_ret: - return rc; -} - -enum securityEnum -cifs_select_sectype(struct TCP_Server_Info *server, enum securityEnum requested) -{ - switch (server->negflavor) { - case CIFS_NEGFLAVOR_EXTENDED: - switch (requested) { - case Kerberos: - case RawNTLMSSP: - return requested; - case Unspecified: - if (server->sec_ntlmssp && - (global_secflags & CIFSSEC_MAY_NTLMSSP)) - return RawNTLMSSP; - if ((server->sec_kerberos || server->sec_mskerberos) && - (global_secflags & CIFSSEC_MAY_KRB5)) - return Kerberos; - fallthrough; - default: - return Unspecified; - } - case CIFS_NEGFLAVOR_UNENCAP: - switch (requested) { - case NTLMv2: - return requested; - case Unspecified: - if (global_secflags & CIFSSEC_MAY_NTLMV2) - return NTLMv2; - break; - default: - break; - } - fallthrough; - default: - return Unspecified; - } -} - -struct sess_data { - unsigned int xid; - struct cifs_ses *ses; - struct TCP_Server_Info *server; - struct nls_table *nls_cp; - void (*func)(struct sess_data *); - int result; - - /* we will send the SMB in three pieces: - * a fixed length beginning part, an optional - * SPNEGO blob (which can be zero length), and a - * last part which will include the strings - * and rest of bcc area. This allows us to avoid - * a large buffer 17K allocation - */ - int buf0_type; - struct kvec iov[3]; -}; - -#ifdef CONFIG_CIFS_ALLOW_INSECURE_LEGACY -static int -sess_alloc_buffer(struct sess_data *sess_data, int wct) -{ - int rc; - struct cifs_ses *ses = sess_data->ses; - struct smb_hdr *smb_buf; - - rc = small_smb_init_no_tc(SMB_COM_SESSION_SETUP_ANDX, wct, ses, - (void **)&smb_buf); - - if (rc) - return rc; - - sess_data->iov[0].iov_base = (char *)smb_buf; - sess_data->iov[0].iov_len = be32_to_cpu(smb_buf->smb_buf_length) + 4; - /* - * This variable will be used to clear the buffer - * allocated above in case of any error in the calling function. - */ - sess_data->buf0_type = CIFS_SMALL_BUFFER; - - /* 2000 big enough to fit max user, domain, NOS name etc. */ - sess_data->iov[2].iov_base = kmalloc(2000, GFP_KERNEL); - if (!sess_data->iov[2].iov_base) { - rc = -ENOMEM; - goto out_free_smb_buf; - } - - return 0; - -out_free_smb_buf: - cifs_small_buf_release(smb_buf); - sess_data->iov[0].iov_base = NULL; - sess_data->iov[0].iov_len = 0; - sess_data->buf0_type = CIFS_NO_BUFFER; - return rc; -} - -static void -sess_free_buffer(struct sess_data *sess_data) -{ - struct kvec *iov = sess_data->iov; - - /* - * Zero the session data before freeing, as it might contain sensitive info (keys, etc). - * Note that iov[1] is already freed by caller. - */ - if (sess_data->buf0_type != CIFS_NO_BUFFER && iov[0].iov_base) - memzero_explicit(iov[0].iov_base, iov[0].iov_len); - - free_rsp_buf(sess_data->buf0_type, iov[0].iov_base); - sess_data->buf0_type = CIFS_NO_BUFFER; - kfree_sensitive(iov[2].iov_base); -} - -static int -sess_establish_session(struct sess_data *sess_data) -{ - struct cifs_ses *ses = sess_data->ses; - struct TCP_Server_Info *server = sess_data->server; - - cifs_server_lock(server); - if (!server->session_estab) { - if (server->sign) { - server->session_key.response = - kmemdup(ses->auth_key.response, - ses->auth_key.len, GFP_KERNEL); - if (!server->session_key.response) { - cifs_server_unlock(server); - return -ENOMEM; - } - server->session_key.len = - ses->auth_key.len; - } - server->sequence_number = 0x2; - server->session_estab = true; - } - cifs_server_unlock(server); - - cifs_dbg(FYI, "CIFS session established successfully\n"); - return 0; -} - -static int -sess_sendreceive(struct sess_data *sess_data) -{ - int rc; - struct smb_hdr *smb_buf = (struct smb_hdr *) sess_data->iov[0].iov_base; - __u16 count; - struct kvec rsp_iov = { NULL, 0 }; - - count = sess_data->iov[1].iov_len + sess_data->iov[2].iov_len; - be32_add_cpu(&smb_buf->smb_buf_length, count); - put_bcc(count, smb_buf); - - rc = SendReceive2(sess_data->xid, sess_data->ses, - sess_data->iov, 3 /* num_iovecs */, - &sess_data->buf0_type, - CIFS_LOG_ERROR, &rsp_iov); - cifs_small_buf_release(sess_data->iov[0].iov_base); - memcpy(&sess_data->iov[0], &rsp_iov, sizeof(struct kvec)); - - return rc; -} - -static void -sess_auth_ntlmv2(struct sess_data *sess_data) -{ - int rc = 0; - struct smb_hdr *smb_buf; - SESSION_SETUP_ANDX *pSMB; - char *bcc_ptr; - struct cifs_ses *ses = sess_data->ses; - struct TCP_Server_Info *server = sess_data->server; - __u32 capabilities; - __u16 bytes_remaining; - - /* old style NTLM sessionsetup */ - /* wct = 13 */ - rc = sess_alloc_buffer(sess_data, 13); - if (rc) - goto out; - - pSMB = (SESSION_SETUP_ANDX *)sess_data->iov[0].iov_base; - bcc_ptr = sess_data->iov[2].iov_base; - capabilities = cifs_ssetup_hdr(ses, server, pSMB); - - pSMB->req_no_secext.Capabilities = cpu_to_le32(capabilities); - - /* LM2 password would be here if we supported it */ - pSMB->req_no_secext.CaseInsensitivePasswordLength = 0; - - if (ses->user_name != NULL) { - /* calculate nlmv2 response and session key */ - rc = setup_ntlmv2_rsp(ses, sess_data->nls_cp); - if (rc) { - cifs_dbg(VFS, "Error %d during NTLMv2 authentication\n", rc); - goto out; - } - - memcpy(bcc_ptr, ses->auth_key.response + CIFS_SESS_KEY_SIZE, - ses->auth_key.len - CIFS_SESS_KEY_SIZE); - bcc_ptr += ses->auth_key.len - CIFS_SESS_KEY_SIZE; - - /* set case sensitive password length after tilen may get - * assigned, tilen is 0 otherwise. - */ - pSMB->req_no_secext.CaseSensitivePasswordLength = - cpu_to_le16(ses->auth_key.len - CIFS_SESS_KEY_SIZE); - } else { - pSMB->req_no_secext.CaseSensitivePasswordLength = 0; - } - - if (ses->capabilities & CAP_UNICODE) { - if (!IS_ALIGNED(sess_data->iov[0].iov_len, 2)) { - *bcc_ptr = 0; - bcc_ptr++; - } - unicode_ssetup_strings(&bcc_ptr, ses, sess_data->nls_cp); - } else { - ascii_ssetup_strings(&bcc_ptr, ses, sess_data->nls_cp); - } - - - sess_data->iov[2].iov_len = (long) bcc_ptr - - (long) sess_data->iov[2].iov_base; - - rc = sess_sendreceive(sess_data); - if (rc) - goto out; - - pSMB = (SESSION_SETUP_ANDX *)sess_data->iov[0].iov_base; - smb_buf = (struct smb_hdr *)sess_data->iov[0].iov_base; - - if (smb_buf->WordCount != 3) { - rc = -EIO; - cifs_dbg(VFS, "bad word count %d\n", smb_buf->WordCount); - goto out; - } - - if (le16_to_cpu(pSMB->resp.Action) & GUEST_LOGIN) - cifs_dbg(FYI, "Guest login\n"); /* BB mark SesInfo struct? */ - - ses->Suid = smb_buf->Uid; /* UID left in wire format (le) */ - cifs_dbg(FYI, "UID = %llu\n", ses->Suid); - - bytes_remaining = get_bcc(smb_buf); - bcc_ptr = pByteArea(smb_buf); - - /* BB check if Unicode and decode strings */ - if (bytes_remaining == 0) { - /* no string area to decode, do nothing */ - } else if (smb_buf->Flags2 & SMBFLG2_UNICODE) { - /* unicode string area must be word-aligned */ - if (!IS_ALIGNED((unsigned long)bcc_ptr - (unsigned long)smb_buf, 2)) { - ++bcc_ptr; - --bytes_remaining; - } - decode_unicode_ssetup(&bcc_ptr, bytes_remaining, ses, - sess_data->nls_cp); - } else { - decode_ascii_ssetup(&bcc_ptr, bytes_remaining, ses, - sess_data->nls_cp); - } - - rc = sess_establish_session(sess_data); -out: - sess_data->result = rc; - sess_data->func = NULL; - sess_free_buffer(sess_data); - kfree_sensitive(ses->auth_key.response); - ses->auth_key.response = NULL; -} - -#ifdef CONFIG_CIFS_UPCALL -static void -sess_auth_kerberos(struct sess_data *sess_data) -{ - int rc = 0; - struct smb_hdr *smb_buf; - SESSION_SETUP_ANDX *pSMB; - char *bcc_ptr; - struct cifs_ses *ses = sess_data->ses; - struct TCP_Server_Info *server = sess_data->server; - __u32 capabilities; - __u16 bytes_remaining; - struct key *spnego_key = NULL; - struct cifs_spnego_msg *msg; - u16 blob_len; - - /* extended security */ - /* wct = 12 */ - rc = sess_alloc_buffer(sess_data, 12); - if (rc) - goto out; - - pSMB = (SESSION_SETUP_ANDX *)sess_data->iov[0].iov_base; - bcc_ptr = sess_data->iov[2].iov_base; - capabilities = cifs_ssetup_hdr(ses, server, pSMB); - - spnego_key = cifs_get_spnego_key(ses, server); - if (IS_ERR(spnego_key)) { - rc = PTR_ERR(spnego_key); - spnego_key = NULL; - goto out; - } - - msg = spnego_key->payload.data[0]; - /* - * check version field to make sure that cifs.upcall is - * sending us a response in an expected form - */ - if (msg->version != CIFS_SPNEGO_UPCALL_VERSION) { - cifs_dbg(VFS, "incorrect version of cifs.upcall (expected %d but got %d)\n", - CIFS_SPNEGO_UPCALL_VERSION, msg->version); - rc = -EKEYREJECTED; - goto out_put_spnego_key; - } - - kfree_sensitive(ses->auth_key.response); - ses->auth_key.response = kmemdup(msg->data, msg->sesskey_len, - GFP_KERNEL); - if (!ses->auth_key.response) { - cifs_dbg(VFS, "Kerberos can't allocate (%u bytes) memory\n", - msg->sesskey_len); - rc = -ENOMEM; - goto out_put_spnego_key; - } - ses->auth_key.len = msg->sesskey_len; - - pSMB->req.hdr.Flags2 |= SMBFLG2_EXT_SEC; - capabilities |= CAP_EXTENDED_SECURITY; - pSMB->req.Capabilities = cpu_to_le32(capabilities); - sess_data->iov[1].iov_base = msg->data + msg->sesskey_len; - sess_data->iov[1].iov_len = msg->secblob_len; - pSMB->req.SecurityBlobLength = cpu_to_le16(sess_data->iov[1].iov_len); - - if (ses->capabilities & CAP_UNICODE) { - /* unicode strings must be word aligned */ - if (!IS_ALIGNED(sess_data->iov[0].iov_len + sess_data->iov[1].iov_len, 2)) { - *bcc_ptr = 0; - bcc_ptr++; - } - unicode_oslm_strings(&bcc_ptr, sess_data->nls_cp); - unicode_domain_string(&bcc_ptr, ses, sess_data->nls_cp); - } else { - /* BB: is this right? */ - ascii_ssetup_strings(&bcc_ptr, ses, sess_data->nls_cp); - } - - sess_data->iov[2].iov_len = (long) bcc_ptr - - (long) sess_data->iov[2].iov_base; - - rc = sess_sendreceive(sess_data); - if (rc) - goto out_put_spnego_key; - - pSMB = (SESSION_SETUP_ANDX *)sess_data->iov[0].iov_base; - smb_buf = (struct smb_hdr *)sess_data->iov[0].iov_base; - - if (smb_buf->WordCount != 4) { - rc = -EIO; - cifs_dbg(VFS, "bad word count %d\n", smb_buf->WordCount); - goto out_put_spnego_key; - } - - if (le16_to_cpu(pSMB->resp.Action) & GUEST_LOGIN) - cifs_dbg(FYI, "Guest login\n"); /* BB mark SesInfo struct? */ - - ses->Suid = smb_buf->Uid; /* UID left in wire format (le) */ - cifs_dbg(FYI, "UID = %llu\n", ses->Suid); - - bytes_remaining = get_bcc(smb_buf); - bcc_ptr = pByteArea(smb_buf); - - blob_len = le16_to_cpu(pSMB->resp.SecurityBlobLength); - if (blob_len > bytes_remaining) { - cifs_dbg(VFS, "bad security blob length %d\n", - blob_len); - rc = -EINVAL; - goto out_put_spnego_key; - } - bcc_ptr += blob_len; - bytes_remaining -= blob_len; - - /* BB check if Unicode and decode strings */ - if (bytes_remaining == 0) { - /* no string area to decode, do nothing */ - } else if (smb_buf->Flags2 & SMBFLG2_UNICODE) { - /* unicode string area must be word-aligned */ - if (!IS_ALIGNED((unsigned long)bcc_ptr - (unsigned long)smb_buf, 2)) { - ++bcc_ptr; - --bytes_remaining; - } - decode_unicode_ssetup(&bcc_ptr, bytes_remaining, ses, - sess_data->nls_cp); - } else { - decode_ascii_ssetup(&bcc_ptr, bytes_remaining, ses, - sess_data->nls_cp); - } - - rc = sess_establish_session(sess_data); -out_put_spnego_key: - key_invalidate(spnego_key); - key_put(spnego_key); -out: - sess_data->result = rc; - sess_data->func = NULL; - sess_free_buffer(sess_data); - kfree_sensitive(ses->auth_key.response); - ses->auth_key.response = NULL; -} - -#endif /* ! CONFIG_CIFS_UPCALL */ - -/* - * The required kvec buffers have to be allocated before calling this - * function. - */ -static int -_sess_auth_rawntlmssp_assemble_req(struct sess_data *sess_data) -{ - SESSION_SETUP_ANDX *pSMB; - struct cifs_ses *ses = sess_data->ses; - struct TCP_Server_Info *server = sess_data->server; - __u32 capabilities; - char *bcc_ptr; - - pSMB = (SESSION_SETUP_ANDX *)sess_data->iov[0].iov_base; - - capabilities = cifs_ssetup_hdr(ses, server, pSMB); - if ((pSMB->req.hdr.Flags2 & SMBFLG2_UNICODE) == 0) { - cifs_dbg(VFS, "NTLMSSP requires Unicode support\n"); - return -ENOSYS; - } - - pSMB->req.hdr.Flags2 |= SMBFLG2_EXT_SEC; - capabilities |= CAP_EXTENDED_SECURITY; - pSMB->req.Capabilities |= cpu_to_le32(capabilities); - - bcc_ptr = sess_data->iov[2].iov_base; - /* unicode strings must be word aligned */ - if (!IS_ALIGNED(sess_data->iov[0].iov_len + sess_data->iov[1].iov_len, 2)) { - *bcc_ptr = 0; - bcc_ptr++; - } - unicode_oslm_strings(&bcc_ptr, sess_data->nls_cp); - - sess_data->iov[2].iov_len = (long) bcc_ptr - - (long) sess_data->iov[2].iov_base; - - return 0; -} - -static void -sess_auth_rawntlmssp_authenticate(struct sess_data *sess_data); - -static void -sess_auth_rawntlmssp_negotiate(struct sess_data *sess_data) -{ - int rc; - struct smb_hdr *smb_buf; - SESSION_SETUP_ANDX *pSMB; - struct cifs_ses *ses = sess_data->ses; - struct TCP_Server_Info *server = sess_data->server; - __u16 bytes_remaining; - char *bcc_ptr; - unsigned char *ntlmsspblob = NULL; - u16 blob_len; - - cifs_dbg(FYI, "rawntlmssp session setup negotiate phase\n"); - - /* - * if memory allocation is successful, caller of this function - * frees it. - */ - ses->ntlmssp = kmalloc(sizeof(struct ntlmssp_auth), GFP_KERNEL); - if (!ses->ntlmssp) { - rc = -ENOMEM; - goto out; - } - ses->ntlmssp->sesskey_per_smbsess = false; - - /* wct = 12 */ - rc = sess_alloc_buffer(sess_data, 12); - if (rc) - goto out; - - pSMB = (SESSION_SETUP_ANDX *)sess_data->iov[0].iov_base; - - /* Build security blob before we assemble the request */ - rc = build_ntlmssp_negotiate_blob(&ntlmsspblob, - &blob_len, ses, server, - sess_data->nls_cp); - if (rc) - goto out_free_ntlmsspblob; - - sess_data->iov[1].iov_len = blob_len; - sess_data->iov[1].iov_base = ntlmsspblob; - pSMB->req.SecurityBlobLength = cpu_to_le16(blob_len); - - rc = _sess_auth_rawntlmssp_assemble_req(sess_data); - if (rc) - goto out_free_ntlmsspblob; - - rc = sess_sendreceive(sess_data); - - pSMB = (SESSION_SETUP_ANDX *)sess_data->iov[0].iov_base; - smb_buf = (struct smb_hdr *)sess_data->iov[0].iov_base; - - /* If true, rc here is expected and not an error */ - if (sess_data->buf0_type != CIFS_NO_BUFFER && - smb_buf->Status.CifsError == - cpu_to_le32(NT_STATUS_MORE_PROCESSING_REQUIRED)) - rc = 0; - - if (rc) - goto out_free_ntlmsspblob; - - cifs_dbg(FYI, "rawntlmssp session setup challenge phase\n"); - - if (smb_buf->WordCount != 4) { - rc = -EIO; - cifs_dbg(VFS, "bad word count %d\n", smb_buf->WordCount); - goto out_free_ntlmsspblob; - } - - ses->Suid = smb_buf->Uid; /* UID left in wire format (le) */ - cifs_dbg(FYI, "UID = %llu\n", ses->Suid); - - bytes_remaining = get_bcc(smb_buf); - bcc_ptr = pByteArea(smb_buf); - - blob_len = le16_to_cpu(pSMB->resp.SecurityBlobLength); - if (blob_len > bytes_remaining) { - cifs_dbg(VFS, "bad security blob length %d\n", - blob_len); - rc = -EINVAL; - goto out_free_ntlmsspblob; - } - - rc = decode_ntlmssp_challenge(bcc_ptr, blob_len, ses); - -out_free_ntlmsspblob: - kfree_sensitive(ntlmsspblob); -out: - sess_free_buffer(sess_data); - - if (!rc) { - sess_data->func = sess_auth_rawntlmssp_authenticate; - return; - } - - /* Else error. Cleanup */ - kfree_sensitive(ses->auth_key.response); - ses->auth_key.response = NULL; - kfree_sensitive(ses->ntlmssp); - ses->ntlmssp = NULL; - - sess_data->func = NULL; - sess_data->result = rc; -} - -static void -sess_auth_rawntlmssp_authenticate(struct sess_data *sess_data) -{ - int rc; - struct smb_hdr *smb_buf; - SESSION_SETUP_ANDX *pSMB; - struct cifs_ses *ses = sess_data->ses; - struct TCP_Server_Info *server = sess_data->server; - __u16 bytes_remaining; - char *bcc_ptr; - unsigned char *ntlmsspblob = NULL; - u16 blob_len; - - cifs_dbg(FYI, "rawntlmssp session setup authenticate phase\n"); - - /* wct = 12 */ - rc = sess_alloc_buffer(sess_data, 12); - if (rc) - goto out; - - /* Build security blob before we assemble the request */ - pSMB = (SESSION_SETUP_ANDX *)sess_data->iov[0].iov_base; - smb_buf = (struct smb_hdr *)pSMB; - rc = build_ntlmssp_auth_blob(&ntlmsspblob, - &blob_len, ses, server, - sess_data->nls_cp); - if (rc) - goto out_free_ntlmsspblob; - sess_data->iov[1].iov_len = blob_len; - sess_data->iov[1].iov_base = ntlmsspblob; - pSMB->req.SecurityBlobLength = cpu_to_le16(blob_len); - /* - * Make sure that we tell the server that we are using - * the uid that it just gave us back on the response - * (challenge) - */ - smb_buf->Uid = ses->Suid; - - rc = _sess_auth_rawntlmssp_assemble_req(sess_data); - if (rc) - goto out_free_ntlmsspblob; - - rc = sess_sendreceive(sess_data); - if (rc) - goto out_free_ntlmsspblob; - - pSMB = (SESSION_SETUP_ANDX *)sess_data->iov[0].iov_base; - smb_buf = (struct smb_hdr *)sess_data->iov[0].iov_base; - if (smb_buf->WordCount != 4) { - rc = -EIO; - cifs_dbg(VFS, "bad word count %d\n", smb_buf->WordCount); - goto out_free_ntlmsspblob; - } - - if (le16_to_cpu(pSMB->resp.Action) & GUEST_LOGIN) - cifs_dbg(FYI, "Guest login\n"); /* BB mark SesInfo struct? */ - - if (ses->Suid != smb_buf->Uid) { - ses->Suid = smb_buf->Uid; - cifs_dbg(FYI, "UID changed! new UID = %llu\n", ses->Suid); - } - - bytes_remaining = get_bcc(smb_buf); - bcc_ptr = pByteArea(smb_buf); - blob_len = le16_to_cpu(pSMB->resp.SecurityBlobLength); - if (blob_len > bytes_remaining) { - cifs_dbg(VFS, "bad security blob length %d\n", - blob_len); - rc = -EINVAL; - goto out_free_ntlmsspblob; - } - bcc_ptr += blob_len; - bytes_remaining -= blob_len; - - - /* BB check if Unicode and decode strings */ - if (bytes_remaining == 0) { - /* no string area to decode, do nothing */ - } else if (smb_buf->Flags2 & SMBFLG2_UNICODE) { - /* unicode string area must be word-aligned */ - if (!IS_ALIGNED((unsigned long)bcc_ptr - (unsigned long)smb_buf, 2)) { - ++bcc_ptr; - --bytes_remaining; - } - decode_unicode_ssetup(&bcc_ptr, bytes_remaining, ses, - sess_data->nls_cp); - } else { - decode_ascii_ssetup(&bcc_ptr, bytes_remaining, ses, - sess_data->nls_cp); - } - -out_free_ntlmsspblob: - kfree_sensitive(ntlmsspblob); -out: - sess_free_buffer(sess_data); - - if (!rc) - rc = sess_establish_session(sess_data); - - /* Cleanup */ - kfree_sensitive(ses->auth_key.response); - ses->auth_key.response = NULL; - kfree_sensitive(ses->ntlmssp); - ses->ntlmssp = NULL; - - sess_data->func = NULL; - sess_data->result = rc; -} - -static int select_sec(struct sess_data *sess_data) -{ - int type; - struct cifs_ses *ses = sess_data->ses; - struct TCP_Server_Info *server = sess_data->server; - - type = cifs_select_sectype(server, ses->sectype); - cifs_dbg(FYI, "sess setup type %d\n", type); - if (type == Unspecified) { - cifs_dbg(VFS, "Unable to select appropriate authentication method!\n"); - return -EINVAL; - } - - switch (type) { - case NTLMv2: - sess_data->func = sess_auth_ntlmv2; - break; - case Kerberos: -#ifdef CONFIG_CIFS_UPCALL - sess_data->func = sess_auth_kerberos; - break; -#else - cifs_dbg(VFS, "Kerberos negotiated but upcall support disabled!\n"); - return -ENOSYS; -#endif /* CONFIG_CIFS_UPCALL */ - case RawNTLMSSP: - sess_data->func = sess_auth_rawntlmssp_negotiate; - break; - default: - cifs_dbg(VFS, "secType %d not supported!\n", type); - return -ENOSYS; - } - - return 0; -} - -int CIFS_SessSetup(const unsigned int xid, struct cifs_ses *ses, - struct TCP_Server_Info *server, - const struct nls_table *nls_cp) -{ - int rc = 0; - struct sess_data *sess_data; - - if (ses == NULL) { - WARN(1, "%s: ses == NULL!", __func__); - return -EINVAL; - } - - sess_data = kzalloc(sizeof(struct sess_data), GFP_KERNEL); - if (!sess_data) - return -ENOMEM; - - sess_data->xid = xid; - sess_data->ses = ses; - sess_data->server = server; - sess_data->buf0_type = CIFS_NO_BUFFER; - sess_data->nls_cp = (struct nls_table *) nls_cp; - - rc = select_sec(sess_data); - if (rc) - goto out; - - while (sess_data->func) - sess_data->func(sess_data); - - /* Store result before we free sess_data */ - rc = sess_data->result; - -out: - kfree_sensitive(sess_data); - return rc; -} -#endif /* CONFIG_CIFS_ALLOW_INSECURE_LEGACY */ diff --git a/fs/cifs/smb1ops.c b/fs/cifs/smb1ops.c deleted file mode 100644 index 7d1b3fc014d94..0000000000000 --- a/fs/cifs/smb1ops.c +++ /dev/null @@ -1,1276 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-only -/* - * SMB1 (CIFS) version specific operations - * - * Copyright (c) 2012, Jeff Layton - */ - -#include -#include -#include -#include "cifsglob.h" -#include "cifsproto.h" -#include "cifs_debug.h" -#include "cifspdu.h" -#include "cifs_unicode.h" -#include "fs_context.h" - -/* - * An NT cancel request header looks just like the original request except: - * - * The Command is SMB_COM_NT_CANCEL - * The WordCount is zeroed out - * The ByteCount is zeroed out - * - * This function mangles an existing request buffer into a - * SMB_COM_NT_CANCEL request and then sends it. - */ -static int -send_nt_cancel(struct TCP_Server_Info *server, struct smb_rqst *rqst, - struct mid_q_entry *mid) -{ - int rc = 0; - struct smb_hdr *in_buf = (struct smb_hdr *)rqst->rq_iov[0].iov_base; - - /* -4 for RFC1001 length and +2 for BCC field */ - in_buf->smb_buf_length = cpu_to_be32(sizeof(struct smb_hdr) - 4 + 2); - in_buf->Command = SMB_COM_NT_CANCEL; - in_buf->WordCount = 0; - put_bcc(0, in_buf); - - cifs_server_lock(server); - rc = cifs_sign_smb(in_buf, server, &mid->sequence_number); - if (rc) { - cifs_server_unlock(server); - return rc; - } - - /* - * The response to this call was already factored into the sequence - * number when the call went out, so we must adjust it back downward - * after signing here. - */ - --server->sequence_number; - rc = smb_send(server, in_buf, be32_to_cpu(in_buf->smb_buf_length)); - if (rc < 0) - server->sequence_number--; - - cifs_server_unlock(server); - - cifs_dbg(FYI, "issued NT_CANCEL for mid %u, rc = %d\n", - get_mid(in_buf), rc); - - return rc; -} - -static bool -cifs_compare_fids(struct cifsFileInfo *ob1, struct cifsFileInfo *ob2) -{ - return ob1->fid.netfid == ob2->fid.netfid; -} - -static unsigned int -cifs_read_data_offset(char *buf) -{ - READ_RSP *rsp = (READ_RSP *)buf; - return le16_to_cpu(rsp->DataOffset); -} - -static unsigned int -cifs_read_data_length(char *buf, bool in_remaining) -{ - READ_RSP *rsp = (READ_RSP *)buf; - /* It's a bug reading remaining data for SMB1 packets */ - WARN_ON(in_remaining); - return (le16_to_cpu(rsp->DataLengthHigh) << 16) + - le16_to_cpu(rsp->DataLength); -} - -static struct mid_q_entry * -cifs_find_mid(struct TCP_Server_Info *server, char *buffer) -{ - struct smb_hdr *buf = (struct smb_hdr *)buffer; - struct mid_q_entry *mid; - - spin_lock(&server->mid_lock); - list_for_each_entry(mid, &server->pending_mid_q, qhead) { - if (compare_mid(mid->mid, buf) && - mid->mid_state == MID_REQUEST_SUBMITTED && - le16_to_cpu(mid->command) == buf->Command) { - kref_get(&mid->refcount); - spin_unlock(&server->mid_lock); - return mid; - } - } - spin_unlock(&server->mid_lock); - return NULL; -} - -static void -cifs_add_credits(struct TCP_Server_Info *server, - const struct cifs_credits *credits, const int optype) -{ - spin_lock(&server->req_lock); - server->credits += credits->value; - server->in_flight--; - spin_unlock(&server->req_lock); - wake_up(&server->request_q); -} - -static void -cifs_set_credits(struct TCP_Server_Info *server, const int val) -{ - spin_lock(&server->req_lock); - server->credits = val; - server->oplocks = val > 1 ? enable_oplocks : false; - spin_unlock(&server->req_lock); -} - -static int * -cifs_get_credits_field(struct TCP_Server_Info *server, const int optype) -{ - return &server->credits; -} - -static unsigned int -cifs_get_credits(struct mid_q_entry *mid) -{ - return 1; -} - -/* - * 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. - */ -static __u64 -cifs_get_next_mid(struct TCP_Server_Info *server) -{ - __u64 mid = 0; - __u16 last_mid, cur_mid; - bool collision, reconnect = false; - - spin_lock(&server->mid_lock); - - /* mid is 16 bit only for CIFS/SMB */ - cur_mid = (__u16)((server->CurrentMid) & 0xffff); - /* we do not want to loop forever */ - last_mid = cur_mid; - cur_mid++; - /* avoid 0xFFFF MID */ - if (cur_mid == 0xffff) - cur_mid++; - - /* - * 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 (cur_mid != last_mid) { - struct mid_q_entry *mid_entry; - unsigned int num_mids; - - collision = false; - if (cur_mid == 0) - cur_mid++; - - num_mids = 0; - list_for_each_entry(mid_entry, &server->pending_mid_q, qhead) { - ++num_mids; - if (mid_entry->mid == cur_mid && - mid_entry->mid_state == MID_REQUEST_SUBMITTED) { - /* This mid is in use, try a different one */ - collision = true; - break; - } - } - - /* - * if we have more than 32k mids in the list, then something - * is very wrong. Possibly a local user is trying to DoS the - * box by issuing long-running calls and SIGKILL'ing them. If - * we get to 2^16 mids then we're in big trouble as this - * function could loop forever. - * - * Go ahead and assign out the mid in this situation, but force - * an eventual reconnect to clean out the pending_mid_q. - */ - if (num_mids > 32768) - reconnect = true; - - if (!collision) { - mid = (__u64)cur_mid; - server->CurrentMid = mid; - break; - } - cur_mid++; - } - spin_unlock(&server->mid_lock); - - if (reconnect) { - cifs_signal_cifsd_for_reconnect(server, false); - } - - return mid; -} - -/* - return codes: - 0 not a transact2, or all data present - >0 transact2 with that much data missing - -EINVAL invalid transact2 - */ -static int -check2ndT2(char *buf) -{ - struct smb_hdr *pSMB = (struct smb_hdr *)buf; - struct smb_t2_rsp *pSMBt; - int remaining; - __u16 total_data_size, data_in_this_rsp; - - if (pSMB->Command != SMB_COM_TRANSACTION2) - return 0; - - /* check for plausible wct, bcc and t2 data and parm sizes */ - /* check for parm and data offset going beyond end of smb */ - if (pSMB->WordCount != 10) { /* coalesce_t2 depends on this */ - cifs_dbg(FYI, "Invalid transact2 word count\n"); - return -EINVAL; - } - - pSMBt = (struct smb_t2_rsp *)pSMB; - - total_data_size = get_unaligned_le16(&pSMBt->t2_rsp.TotalDataCount); - data_in_this_rsp = get_unaligned_le16(&pSMBt->t2_rsp.DataCount); - - if (total_data_size == data_in_this_rsp) - return 0; - else if (total_data_size < data_in_this_rsp) { - cifs_dbg(FYI, "total data %d smaller than data in frame %d\n", - total_data_size, data_in_this_rsp); - return -EINVAL; - } - - remaining = total_data_size - data_in_this_rsp; - - cifs_dbg(FYI, "missing %d bytes from transact2, check next response\n", - remaining); - if (total_data_size > CIFSMaxBufSize) { - cifs_dbg(VFS, "TotalDataSize %d is over maximum buffer %d\n", - total_data_size, CIFSMaxBufSize); - return -EINVAL; - } - return remaining; -} - -static int -coalesce_t2(char *second_buf, struct smb_hdr *target_hdr) -{ - struct smb_t2_rsp *pSMBs = (struct smb_t2_rsp *)second_buf; - struct smb_t2_rsp *pSMBt = (struct smb_t2_rsp *)target_hdr; - char *data_area_of_tgt; - char *data_area_of_src; - int remaining; - unsigned int byte_count, total_in_tgt; - __u16 tgt_total_cnt, src_total_cnt, total_in_src; - - src_total_cnt = get_unaligned_le16(&pSMBs->t2_rsp.TotalDataCount); - tgt_total_cnt = get_unaligned_le16(&pSMBt->t2_rsp.TotalDataCount); - - if (tgt_total_cnt != src_total_cnt) - cifs_dbg(FYI, "total data count of primary and secondary t2 differ source=%hu target=%hu\n", - src_total_cnt, tgt_total_cnt); - - total_in_tgt = get_unaligned_le16(&pSMBt->t2_rsp.DataCount); - - remaining = tgt_total_cnt - total_in_tgt; - - if (remaining < 0) { - cifs_dbg(FYI, "Server sent too much data. tgt_total_cnt=%hu total_in_tgt=%u\n", - tgt_total_cnt, total_in_tgt); - return -EPROTO; - } - - if (remaining == 0) { - /* nothing to do, ignore */ - cifs_dbg(FYI, "no more data remains\n"); - return 0; - } - - total_in_src = get_unaligned_le16(&pSMBs->t2_rsp.DataCount); - if (remaining < total_in_src) - cifs_dbg(FYI, "transact2 2nd response contains too much data\n"); - - /* find end of first SMB data area */ - data_area_of_tgt = (char *)&pSMBt->hdr.Protocol + - get_unaligned_le16(&pSMBt->t2_rsp.DataOffset); - - /* validate target area */ - data_area_of_src = (char *)&pSMBs->hdr.Protocol + - get_unaligned_le16(&pSMBs->t2_rsp.DataOffset); - - data_area_of_tgt += total_in_tgt; - - total_in_tgt += total_in_src; - /* is the result too big for the field? */ - if (total_in_tgt > USHRT_MAX) { - cifs_dbg(FYI, "coalesced DataCount too large (%u)\n", - total_in_tgt); - return -EPROTO; - } - put_unaligned_le16(total_in_tgt, &pSMBt->t2_rsp.DataCount); - - /* fix up the BCC */ - byte_count = get_bcc(target_hdr); - byte_count += total_in_src; - /* is the result too big for the field? */ - if (byte_count > USHRT_MAX) { - cifs_dbg(FYI, "coalesced BCC too large (%u)\n", byte_count); - return -EPROTO; - } - put_bcc(byte_count, target_hdr); - - byte_count = be32_to_cpu(target_hdr->smb_buf_length); - byte_count += total_in_src; - /* don't allow buffer to overflow */ - if (byte_count > CIFSMaxBufSize + MAX_CIFS_HDR_SIZE - 4) { - cifs_dbg(FYI, "coalesced BCC exceeds buffer size (%u)\n", - byte_count); - return -ENOBUFS; - } - target_hdr->smb_buf_length = cpu_to_be32(byte_count); - - /* copy second buffer into end of first buffer */ - memcpy(data_area_of_tgt, data_area_of_src, total_in_src); - - if (remaining != total_in_src) { - /* more responses to go */ - cifs_dbg(FYI, "waiting for more secondary responses\n"); - return 1; - } - - /* we are done */ - cifs_dbg(FYI, "found the last secondary response\n"); - return 0; -} - -static void -cifs_downgrade_oplock(struct TCP_Server_Info *server, - struct cifsInodeInfo *cinode, __u32 oplock, - unsigned int epoch, bool *purge_cache) -{ - cifs_set_oplock_level(cinode, oplock); -} - -static bool -cifs_check_trans2(struct mid_q_entry *mid, struct TCP_Server_Info *server, - char *buf, int malformed) -{ - if (malformed) - return false; - if (check2ndT2(buf) <= 0) - return false; - mid->multiRsp = true; - if (mid->resp_buf) { - /* merge response - fix up 1st*/ - malformed = coalesce_t2(buf, mid->resp_buf); - if (malformed > 0) - return true; - /* All parts received or packet is malformed. */ - mid->multiEnd = true; - dequeue_mid(mid, malformed); - return true; - } - if (!server->large_buf) { - /*FIXME: switch to already allocated largebuf?*/ - cifs_dbg(VFS, "1st trans2 resp needs bigbuf\n"); - } else { - /* Have first buffer */ - mid->resp_buf = buf; - mid->large_buf = true; - server->bigbuf = NULL; - } - return true; -} - -static bool -cifs_need_neg(struct TCP_Server_Info *server) -{ - return server->maxBuf == 0; -} - -static int -cifs_negotiate(const unsigned int xid, - struct cifs_ses *ses, - struct TCP_Server_Info *server) -{ - int rc; - rc = CIFSSMBNegotiate(xid, ses, server); - if (rc == -EAGAIN) { - /* retry only once on 1st time connection */ - set_credits(server, 1); - rc = CIFSSMBNegotiate(xid, ses, server); - if (rc == -EAGAIN) - rc = -EHOSTDOWN; - } - return rc; -} - -static unsigned int -cifs_negotiate_wsize(struct cifs_tcon *tcon, struct smb3_fs_context *ctx) -{ - __u64 unix_cap = le64_to_cpu(tcon->fsUnixInfo.Capability); - struct TCP_Server_Info *server = tcon->ses->server; - unsigned int wsize; - - /* start with specified wsize, or default */ - if (ctx->wsize) - wsize = ctx->wsize; - else if (tcon->unix_ext && (unix_cap & CIFS_UNIX_LARGE_WRITE_CAP)) - wsize = CIFS_DEFAULT_IOSIZE; - else - wsize = CIFS_DEFAULT_NON_POSIX_WSIZE; - - /* can server support 24-bit write sizes? (via UNIX extensions) */ - if (!tcon->unix_ext || !(unix_cap & CIFS_UNIX_LARGE_WRITE_CAP)) - wsize = min_t(unsigned int, wsize, CIFS_MAX_RFC1002_WSIZE); - - /* - * no CAP_LARGE_WRITE_X or is signing enabled without CAP_UNIX set? - * Limit it to max buffer offered by the server, minus the size of the - * WRITEX header, not including the 4 byte RFC1001 length. - */ - if (!(server->capabilities & CAP_LARGE_WRITE_X) || - (!(server->capabilities & CAP_UNIX) && server->sign)) - wsize = min_t(unsigned int, wsize, - server->maxBuf - sizeof(WRITE_REQ) + 4); - - /* hard limit of CIFS_MAX_WSIZE */ - wsize = min_t(unsigned int, wsize, CIFS_MAX_WSIZE); - - return wsize; -} - -static unsigned int -cifs_negotiate_rsize(struct cifs_tcon *tcon, struct smb3_fs_context *ctx) -{ - __u64 unix_cap = le64_to_cpu(tcon->fsUnixInfo.Capability); - struct TCP_Server_Info *server = tcon->ses->server; - unsigned int rsize, defsize; - - /* - * Set default value... - * - * HACK alert! Ancient servers have very small buffers. Even though - * MS-CIFS indicates that servers are only limited by the client's - * bufsize for reads, testing against win98se shows that it throws - * INVALID_PARAMETER errors if you try to request too large a read. - * OS/2 just sends back short reads. - * - * If the server doesn't advertise CAP_LARGE_READ_X, then assume that - * it can't handle a read request larger than its MaxBufferSize either. - */ - if (tcon->unix_ext && (unix_cap & CIFS_UNIX_LARGE_READ_CAP)) - defsize = CIFS_DEFAULT_IOSIZE; - else if (server->capabilities & CAP_LARGE_READ_X) - defsize = CIFS_DEFAULT_NON_POSIX_RSIZE; - else - defsize = server->maxBuf - sizeof(READ_RSP); - - rsize = ctx->rsize ? ctx->rsize : defsize; - - /* - * no CAP_LARGE_READ_X? Then MS-CIFS states that we must limit this to - * the client's MaxBufferSize. - */ - if (!(server->capabilities & CAP_LARGE_READ_X)) - rsize = min_t(unsigned int, CIFSMaxBufSize, rsize); - - /* hard limit of CIFS_MAX_RSIZE */ - rsize = min_t(unsigned int, rsize, CIFS_MAX_RSIZE); - - return rsize; -} - -static void -cifs_qfs_tcon(const unsigned int xid, struct cifs_tcon *tcon, - struct cifs_sb_info *cifs_sb) -{ - CIFSSMBQFSDeviceInfo(xid, tcon); - CIFSSMBQFSAttributeInfo(xid, tcon); -} - -static int -cifs_is_path_accessible(const unsigned int xid, struct cifs_tcon *tcon, - struct cifs_sb_info *cifs_sb, const char *full_path) -{ - int rc; - FILE_ALL_INFO *file_info; - - file_info = kmalloc(sizeof(FILE_ALL_INFO), GFP_KERNEL); - if (file_info == NULL) - return -ENOMEM; - - rc = CIFSSMBQPathInfo(xid, tcon, full_path, file_info, - 0 /* not legacy */, cifs_sb->local_nls, - cifs_remap(cifs_sb)); - - if (rc == -EOPNOTSUPP || rc == -EINVAL) - rc = SMBQueryInformation(xid, tcon, full_path, file_info, - cifs_sb->local_nls, cifs_remap(cifs_sb)); - kfree(file_info); - return rc; -} - -static int cifs_query_path_info(const unsigned int xid, struct cifs_tcon *tcon, - struct cifs_sb_info *cifs_sb, const char *full_path, - struct cifs_open_info_data *data, bool *adjustTZ, bool *symlink) -{ - int rc; - FILE_ALL_INFO fi = {}; - - *symlink = false; - - /* could do find first instead but this returns more info */ - rc = CIFSSMBQPathInfo(xid, tcon, full_path, &fi, 0 /* not legacy */, cifs_sb->local_nls, - cifs_remap(cifs_sb)); - /* - * 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, tcon, full_path, &fi, cifs_sb->local_nls, - cifs_remap(cifs_sb)); - *adjustTZ = true; - } - - if (!rc) { - int tmprc; - int oplock = 0; - struct cifs_fid fid; - struct cifs_open_parms oparms; - - move_cifs_info_to_smb2(&data->fi, &fi); - - if (!(le32_to_cpu(fi.Attributes) & ATTR_REPARSE)) - return 0; - - oparms = (struct cifs_open_parms) { - .tcon = tcon, - .cifs_sb = cifs_sb, - .desired_access = FILE_READ_ATTRIBUTES, - .create_options = cifs_create_options(cifs_sb, 0), - .disposition = FILE_OPEN, - .path = full_path, - .fid = &fid, - }; - - /* Need to check if this is a symbolic link or not */ - tmprc = CIFS_open(xid, &oparms, &oplock, NULL); - if (tmprc == -EOPNOTSUPP) - *symlink = true; - else if (tmprc == 0) - CIFSSMBClose(xid, tcon, fid.netfid); - } - - return rc; -} - -static int cifs_get_srv_inum(const unsigned int xid, struct cifs_tcon *tcon, - struct cifs_sb_info *cifs_sb, const char *full_path, - u64 *uniqueid, struct cifs_open_info_data *unused) -{ - /* - * We can not use the IndexNumber field by default from Windows or - * Samba (in ALL_INFO buf) but we can request it explicitly. The SNIA - * CIFS spec claims that this value is unique within the scope of a - * share, and the windows docs hint that it's actually unique - * per-machine. - * - * There may be higher info levels that work but are there Windows - * server or network appliances for which IndexNumber field is not - * guaranteed unique? - */ - return CIFSGetSrvInodeNumber(xid, tcon, full_path, uniqueid, - cifs_sb->local_nls, - cifs_remap(cifs_sb)); -} - -static int cifs_query_file_info(const unsigned int xid, struct cifs_tcon *tcon, - struct cifsFileInfo *cfile, struct cifs_open_info_data *data) -{ - int rc; - FILE_ALL_INFO fi = {}; - - if (cfile->symlink_target) { - data->symlink_target = kstrdup(cfile->symlink_target, GFP_KERNEL); - if (!data->symlink_target) - return -ENOMEM; - } - - rc = CIFSSMBQFileInfo(xid, tcon, cfile->fid.netfid, &fi); - if (!rc) - move_cifs_info_to_smb2(&data->fi, &fi); - return rc; -} - -static void -cifs_clear_stats(struct cifs_tcon *tcon) -{ - atomic_set(&tcon->stats.cifs_stats.num_writes, 0); - atomic_set(&tcon->stats.cifs_stats.num_reads, 0); - atomic_set(&tcon->stats.cifs_stats.num_flushes, 0); - atomic_set(&tcon->stats.cifs_stats.num_oplock_brks, 0); - atomic_set(&tcon->stats.cifs_stats.num_opens, 0); - atomic_set(&tcon->stats.cifs_stats.num_posixopens, 0); - atomic_set(&tcon->stats.cifs_stats.num_posixmkdirs, 0); - atomic_set(&tcon->stats.cifs_stats.num_closes, 0); - atomic_set(&tcon->stats.cifs_stats.num_deletes, 0); - atomic_set(&tcon->stats.cifs_stats.num_mkdirs, 0); - atomic_set(&tcon->stats.cifs_stats.num_rmdirs, 0); - atomic_set(&tcon->stats.cifs_stats.num_renames, 0); - atomic_set(&tcon->stats.cifs_stats.num_t2renames, 0); - atomic_set(&tcon->stats.cifs_stats.num_ffirst, 0); - atomic_set(&tcon->stats.cifs_stats.num_fnext, 0); - atomic_set(&tcon->stats.cifs_stats.num_fclose, 0); - atomic_set(&tcon->stats.cifs_stats.num_hardlinks, 0); - atomic_set(&tcon->stats.cifs_stats.num_symlinks, 0); - atomic_set(&tcon->stats.cifs_stats.num_locks, 0); - atomic_set(&tcon->stats.cifs_stats.num_acl_get, 0); - atomic_set(&tcon->stats.cifs_stats.num_acl_set, 0); -} - -static void -cifs_print_stats(struct seq_file *m, struct cifs_tcon *tcon) -{ - seq_printf(m, " Oplocks breaks: %d", - atomic_read(&tcon->stats.cifs_stats.num_oplock_brks)); - seq_printf(m, "\nReads: %d Bytes: %llu", - atomic_read(&tcon->stats.cifs_stats.num_reads), - (long long)(tcon->bytes_read)); - seq_printf(m, "\nWrites: %d Bytes: %llu", - atomic_read(&tcon->stats.cifs_stats.num_writes), - (long long)(tcon->bytes_written)); - seq_printf(m, "\nFlushes: %d", - atomic_read(&tcon->stats.cifs_stats.num_flushes)); - seq_printf(m, "\nLocks: %d HardLinks: %d Symlinks: %d", - atomic_read(&tcon->stats.cifs_stats.num_locks), - atomic_read(&tcon->stats.cifs_stats.num_hardlinks), - atomic_read(&tcon->stats.cifs_stats.num_symlinks)); - seq_printf(m, "\nOpens: %d Closes: %d Deletes: %d", - atomic_read(&tcon->stats.cifs_stats.num_opens), - atomic_read(&tcon->stats.cifs_stats.num_closes), - atomic_read(&tcon->stats.cifs_stats.num_deletes)); - seq_printf(m, "\nPosix Opens: %d Posix Mkdirs: %d", - atomic_read(&tcon->stats.cifs_stats.num_posixopens), - atomic_read(&tcon->stats.cifs_stats.num_posixmkdirs)); - seq_printf(m, "\nMkdirs: %d Rmdirs: %d", - atomic_read(&tcon->stats.cifs_stats.num_mkdirs), - atomic_read(&tcon->stats.cifs_stats.num_rmdirs)); - seq_printf(m, "\nRenames: %d T2 Renames %d", - atomic_read(&tcon->stats.cifs_stats.num_renames), - atomic_read(&tcon->stats.cifs_stats.num_t2renames)); - seq_printf(m, "\nFindFirst: %d FNext %d FClose %d", - atomic_read(&tcon->stats.cifs_stats.num_ffirst), - atomic_read(&tcon->stats.cifs_stats.num_fnext), - atomic_read(&tcon->stats.cifs_stats.num_fclose)); -} - -static void -cifs_mkdir_setinfo(struct inode *inode, const char *full_path, - struct cifs_sb_info *cifs_sb, struct cifs_tcon *tcon, - const unsigned int xid) -{ - FILE_BASIC_INFO info; - struct cifsInodeInfo *cifsInode; - u32 dosattrs; - int rc; - - memset(&info, 0, sizeof(info)); - cifsInode = CIFS_I(inode); - dosattrs = cifsInode->cifsAttrs|ATTR_READONLY; - info.Attributes = cpu_to_le32(dosattrs); - rc = CIFSSMBSetPathInfo(xid, tcon, full_path, &info, cifs_sb->local_nls, - cifs_sb); - if (rc == 0) - cifsInode->cifsAttrs = dosattrs; -} - -static int cifs_open_file(const unsigned int xid, struct cifs_open_parms *oparms, __u32 *oplock, - void *buf) -{ - struct cifs_open_info_data *data = buf; - FILE_ALL_INFO fi = {}; - int rc; - - if (!(oparms->tcon->ses->capabilities & CAP_NT_SMBS)) - rc = SMBLegacyOpen(xid, oparms->tcon, oparms->path, - oparms->disposition, - oparms->desired_access, - oparms->create_options, - &oparms->fid->netfid, oplock, &fi, - oparms->cifs_sb->local_nls, - cifs_remap(oparms->cifs_sb)); - else - rc = CIFS_open(xid, oparms, oplock, &fi); - - if (!rc && data) - move_cifs_info_to_smb2(&data->fi, &fi); - - return rc; -} - -static void -cifs_set_fid(struct cifsFileInfo *cfile, struct cifs_fid *fid, __u32 oplock) -{ - struct cifsInodeInfo *cinode = CIFS_I(d_inode(cfile->dentry)); - cfile->fid.netfid = fid->netfid; - cifs_set_oplock_level(cinode, oplock); - cinode->can_cache_brlcks = CIFS_CACHE_WRITE(cinode); -} - -static void -cifs_close_file(const unsigned int xid, struct cifs_tcon *tcon, - struct cifs_fid *fid) -{ - CIFSSMBClose(xid, tcon, fid->netfid); -} - -static int -cifs_flush_file(const unsigned int xid, struct cifs_tcon *tcon, - struct cifs_fid *fid) -{ - return CIFSSMBFlush(xid, tcon, fid->netfid); -} - -static int -cifs_sync_read(const unsigned int xid, struct cifs_fid *pfid, - struct cifs_io_parms *parms, unsigned int *bytes_read, - char **buf, int *buf_type) -{ - parms->netfid = pfid->netfid; - return CIFSSMBRead(xid, parms, bytes_read, buf, buf_type); -} - -static int -cifs_sync_write(const unsigned int xid, struct cifs_fid *pfid, - struct cifs_io_parms *parms, unsigned int *written, - struct kvec *iov, unsigned long nr_segs) -{ - - parms->netfid = pfid->netfid; - return CIFSSMBWrite2(xid, parms, written, iov, nr_segs); -} - -static int -smb_set_file_info(struct inode *inode, const char *full_path, - FILE_BASIC_INFO *buf, const unsigned int xid) -{ - int oplock = 0; - int rc; - __u32 netpid; - struct cifs_fid fid; - struct cifs_open_parms oparms; - struct cifsFileInfo *open_file; - struct cifsInodeInfo *cinode = CIFS_I(inode); - struct cifs_sb_info *cifs_sb = CIFS_SB(inode->i_sb); - struct tcon_link *tlink = NULL; - struct cifs_tcon *tcon; - - /* if the file is already open for write, just use that fileid */ - open_file = find_writable_file(cinode, FIND_WR_FSUID_ONLY); - if (open_file) { - fid.netfid = open_file->fid.netfid; - netpid = open_file->pid; - tcon = tlink_tcon(open_file->tlink); - goto set_via_filehandle; - } - - tlink = cifs_sb_tlink(cifs_sb); - if (IS_ERR(tlink)) { - rc = PTR_ERR(tlink); - tlink = NULL; - goto out; - } - tcon = tlink_tcon(tlink); - - rc = CIFSSMBSetPathInfo(xid, tcon, full_path, buf, cifs_sb->local_nls, - cifs_sb); - if (rc == 0) { - cinode->cifsAttrs = le32_to_cpu(buf->Attributes); - goto out; - } else if (rc != -EOPNOTSUPP && rc != -EINVAL) { - goto out; - } - - oparms = (struct cifs_open_parms) { - .tcon = tcon, - .cifs_sb = cifs_sb, - .desired_access = SYNCHRONIZE | FILE_WRITE_ATTRIBUTES, - .create_options = cifs_create_options(cifs_sb, CREATE_NOT_DIR), - .disposition = FILE_OPEN, - .path = full_path, - .fid = &fid, - }; - - cifs_dbg(FYI, "calling SetFileInfo since SetPathInfo for times not supported by this server\n"); - rc = CIFS_open(xid, &oparms, &oplock, NULL); - if (rc != 0) { - if (rc == -EIO) - rc = -EINVAL; - goto out; - } - - netpid = current->tgid; - -set_via_filehandle: - rc = CIFSSMBSetFileInfo(xid, tcon, buf, fid.netfid, netpid); - if (!rc) - cinode->cifsAttrs = le32_to_cpu(buf->Attributes); - - if (open_file == NULL) - CIFSSMBClose(xid, tcon, fid.netfid); - else - cifsFileInfo_put(open_file); -out: - if (tlink != NULL) - cifs_put_tlink(tlink); - return rc; -} - -static int -cifs_set_compression(const unsigned int xid, struct cifs_tcon *tcon, - struct cifsFileInfo *cfile) -{ - return CIFSSMB_set_compression(xid, tcon, cfile->fid.netfid); -} - -static int -cifs_query_dir_first(const unsigned int xid, struct cifs_tcon *tcon, - const char *path, struct cifs_sb_info *cifs_sb, - struct cifs_fid *fid, __u16 search_flags, - struct cifs_search_info *srch_inf) -{ - int rc; - - rc = CIFSFindFirst(xid, tcon, path, cifs_sb, - &fid->netfid, search_flags, srch_inf, true); - if (rc) - cifs_dbg(FYI, "find first failed=%d\n", rc); - return rc; -} - -static int -cifs_query_dir_next(const unsigned int xid, struct cifs_tcon *tcon, - struct cifs_fid *fid, __u16 search_flags, - struct cifs_search_info *srch_inf) -{ - return CIFSFindNext(xid, tcon, fid->netfid, search_flags, srch_inf); -} - -static int -cifs_close_dir(const unsigned int xid, struct cifs_tcon *tcon, - struct cifs_fid *fid) -{ - return CIFSFindClose(xid, tcon, fid->netfid); -} - -static int -cifs_oplock_response(struct cifs_tcon *tcon, __u64 persistent_fid, - __u64 volatile_fid, __u16 net_fid, struct cifsInodeInfo *cinode) -{ - return CIFSSMBLock(0, tcon, net_fid, current->tgid, 0, 0, 0, 0, - LOCKING_ANDX_OPLOCK_RELEASE, false, CIFS_CACHE_READ(cinode) ? 1 : 0); -} - -static int -cifs_queryfs(const unsigned int xid, struct cifs_tcon *tcon, - struct cifs_sb_info *cifs_sb, struct kstatfs *buf) -{ - int rc = -EOPNOTSUPP; - - buf->f_type = CIFS_SUPER_MAGIC; - - /* - * We could add a second check for a QFS Unix capability bit - */ - if ((tcon->ses->capabilities & CAP_UNIX) && - (CIFS_POSIX_EXTENSIONS & le64_to_cpu(tcon->fsUnixInfo.Capability))) - rc = CIFSSMBQFSPosixInfo(xid, tcon, buf); - - /* - * Only need to call the old QFSInfo if failed on newer one, - * e.g. by OS/2. - **/ - if (rc && (tcon->ses->capabilities & CAP_NT_SMBS)) - rc = CIFSSMBQFSInfo(xid, tcon, buf); - - /* - * Some old Windows servers also do not support level 103, retry with - * older level one if old server failed the previous call or we - * bypassed it because we detected that this was an older LANMAN sess - */ - if (rc) - rc = SMBOldQFSInfo(xid, tcon, buf); - return rc; -} - -static int -cifs_mand_lock(const unsigned int xid, struct cifsFileInfo *cfile, __u64 offset, - __u64 length, __u32 type, int lock, int unlock, bool wait) -{ - return CIFSSMBLock(xid, tlink_tcon(cfile->tlink), cfile->fid.netfid, - current->tgid, length, offset, unlock, lock, - (__u8)type, wait, 0); -} - -static int -cifs_unix_dfs_readlink(const unsigned int xid, struct cifs_tcon *tcon, - const unsigned char *searchName, char **symlinkinfo, - const struct nls_table *nls_codepage) -{ -#ifdef CONFIG_CIFS_DFS_UPCALL - int rc; - struct dfs_info3_param referral = {0}; - - rc = get_dfs_path(xid, tcon->ses, searchName, nls_codepage, &referral, - 0); - - if (!rc) { - *symlinkinfo = kstrdup(referral.node_name, GFP_KERNEL); - free_dfs_info_param(&referral); - if (!*symlinkinfo) - rc = -ENOMEM; - } - return rc; -#else /* No DFS support */ - return -EREMOTE; -#endif -} - -static int -cifs_query_symlink(const unsigned int xid, struct cifs_tcon *tcon, - struct cifs_sb_info *cifs_sb, const char *full_path, - char **target_path, bool is_reparse_point) -{ - int rc; - int oplock = 0; - struct cifs_fid fid; - struct cifs_open_parms oparms; - - cifs_dbg(FYI, "%s: path: %s\n", __func__, full_path); - - if (is_reparse_point) { - cifs_dbg(VFS, "reparse points not handled for SMB1 symlinks\n"); - return -EOPNOTSUPP; - } - - /* Check for unix extensions */ - if (cap_unix(tcon->ses)) { - rc = CIFSSMBUnixQuerySymLink(xid, tcon, full_path, target_path, - cifs_sb->local_nls, - cifs_remap(cifs_sb)); - if (rc == -EREMOTE) - rc = cifs_unix_dfs_readlink(xid, tcon, full_path, - target_path, - cifs_sb->local_nls); - - goto out; - } - - oparms = (struct cifs_open_parms) { - .tcon = tcon, - .cifs_sb = cifs_sb, - .desired_access = FILE_READ_ATTRIBUTES, - .create_options = cifs_create_options(cifs_sb, - OPEN_REPARSE_POINT), - .disposition = FILE_OPEN, - .path = full_path, - .fid = &fid, - }; - - rc = CIFS_open(xid, &oparms, &oplock, NULL); - if (rc) - goto out; - - rc = CIFSSMBQuerySymLink(xid, tcon, fid.netfid, target_path, - cifs_sb->local_nls); - if (rc) - goto out_close; - - convert_delimiter(*target_path, '/'); -out_close: - CIFSSMBClose(xid, tcon, fid.netfid); -out: - if (!rc) - cifs_dbg(FYI, "%s: target path: %s\n", __func__, *target_path); - return rc; -} - -static bool -cifs_is_read_op(__u32 oplock) -{ - return oplock == OPLOCK_READ; -} - -static unsigned int -cifs_wp_retry_size(struct inode *inode) -{ - return CIFS_SB(inode->i_sb)->ctx->wsize; -} - -static bool -cifs_dir_needs_close(struct cifsFileInfo *cfile) -{ - return !cfile->srch_inf.endOfSearch && !cfile->invalidHandle; -} - -static bool -cifs_can_echo(struct TCP_Server_Info *server) -{ - if (server->tcpStatus == CifsGood) - return true; - - return false; -} - -static int -cifs_make_node(unsigned int xid, struct inode *inode, - struct dentry *dentry, struct cifs_tcon *tcon, - const char *full_path, umode_t mode, dev_t dev) -{ - struct cifs_sb_info *cifs_sb = CIFS_SB(inode->i_sb); - struct inode *newinode = NULL; - int rc = -EPERM; - struct cifs_open_info_data buf = {}; - struct cifs_io_parms io_parms; - __u32 oplock = 0; - struct cifs_fid fid; - struct cifs_open_parms oparms; - unsigned int bytes_written; - struct win_dev *pdev; - struct kvec iov[2]; - - if (tcon->unix_ext) { - /* - * SMB1 Unix Extensions: requires server support but - * works with all special files - */ - struct cifs_unix_set_info_args args = { - .mode = mode & ~current_umask(), - .ctime = NO_CHANGE_64, - .atime = NO_CHANGE_64, - .mtime = NO_CHANGE_64, - .device = dev, - }; - if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_SET_UID) { - args.uid = current_fsuid(); - args.gid = current_fsgid(); - } else { - args.uid = INVALID_UID; /* no change */ - args.gid = INVALID_GID; /* no change */ - } - rc = CIFSSMBUnixSetPathInfo(xid, tcon, full_path, &args, - cifs_sb->local_nls, - cifs_remap(cifs_sb)); - if (rc) - return rc; - - rc = cifs_get_inode_info_unix(&newinode, full_path, - inode->i_sb, xid); - - if (rc == 0) - d_instantiate(dentry, newinode); - return rc; - } - - /* - * SMB1 SFU emulation: should work with all servers, but only - * support block and char device (no socket & fifo) - */ - if (!(cifs_sb->mnt_cifs_flags & CIFS_MOUNT_UNX_EMUL)) - return rc; - - if (!S_ISCHR(mode) && !S_ISBLK(mode)) - return rc; - - cifs_dbg(FYI, "sfu compat create special file\n"); - - oparms = (struct cifs_open_parms) { - .tcon = tcon, - .cifs_sb = cifs_sb, - .desired_access = GENERIC_WRITE, - .create_options = cifs_create_options(cifs_sb, CREATE_NOT_DIR | - CREATE_OPTION_SPECIAL), - .disposition = FILE_CREATE, - .path = full_path, - .fid = &fid, - }; - - if (tcon->ses->server->oplocks) - oplock = REQ_OPLOCK; - else - oplock = 0; - rc = tcon->ses->server->ops->open(xid, &oparms, &oplock, &buf); - if (rc) - return rc; - - /* - * BB Do not bother to decode buf since no local inode yet to put - * timestamps in, but we can reuse it safely. - */ - - pdev = (struct win_dev *)&buf.fi; - io_parms.pid = current->tgid; - io_parms.tcon = tcon; - io_parms.offset = 0; - io_parms.length = sizeof(struct win_dev); - iov[1].iov_base = &buf.fi; - iov[1].iov_len = sizeof(struct win_dev); - if (S_ISCHR(mode)) { - memcpy(pdev->type, "IntxCHR", 8); - pdev->major = cpu_to_le64(MAJOR(dev)); - pdev->minor = cpu_to_le64(MINOR(dev)); - rc = tcon->ses->server->ops->sync_write(xid, &fid, &io_parms, - &bytes_written, iov, 1); - } else if (S_ISBLK(mode)) { - memcpy(pdev->type, "IntxBLK", 8); - pdev->major = cpu_to_le64(MAJOR(dev)); - pdev->minor = cpu_to_le64(MINOR(dev)); - rc = tcon->ses->server->ops->sync_write(xid, &fid, &io_parms, - &bytes_written, iov, 1); - } - tcon->ses->server->ops->close(xid, tcon, &fid); - d_drop(dentry); - - /* FIXME: add code here to set EAs */ - - cifs_free_open_info(&buf); - return rc; -} - - - -struct smb_version_operations smb1_operations = { - .send_cancel = send_nt_cancel, - .compare_fids = cifs_compare_fids, - .setup_request = cifs_setup_request, - .setup_async_request = cifs_setup_async_request, - .check_receive = cifs_check_receive, - .add_credits = cifs_add_credits, - .set_credits = cifs_set_credits, - .get_credits_field = cifs_get_credits_field, - .get_credits = cifs_get_credits, - .wait_mtu_credits = cifs_wait_mtu_credits, - .get_next_mid = cifs_get_next_mid, - .read_data_offset = cifs_read_data_offset, - .read_data_length = cifs_read_data_length, - .map_error = map_smb_to_linux_error, - .find_mid = cifs_find_mid, - .check_message = checkSMB, - .dump_detail = cifs_dump_detail, - .clear_stats = cifs_clear_stats, - .print_stats = cifs_print_stats, - .is_oplock_break = is_valid_oplock_break, - .downgrade_oplock = cifs_downgrade_oplock, - .check_trans2 = cifs_check_trans2, - .need_neg = cifs_need_neg, - .negotiate = cifs_negotiate, - .negotiate_wsize = cifs_negotiate_wsize, - .negotiate_rsize = cifs_negotiate_rsize, - .sess_setup = CIFS_SessSetup, - .logoff = CIFSSMBLogoff, - .tree_connect = CIFSTCon, - .tree_disconnect = CIFSSMBTDis, - .get_dfs_refer = CIFSGetDFSRefer, - .qfs_tcon = cifs_qfs_tcon, - .is_path_accessible = cifs_is_path_accessible, - .can_echo = cifs_can_echo, - .query_path_info = cifs_query_path_info, - .query_file_info = cifs_query_file_info, - .get_srv_inum = cifs_get_srv_inum, - .set_path_size = CIFSSMBSetEOF, - .set_file_size = CIFSSMBSetFileSize, - .set_file_info = smb_set_file_info, - .set_compression = cifs_set_compression, - .echo = CIFSSMBEcho, - .mkdir = CIFSSMBMkDir, - .mkdir_setinfo = cifs_mkdir_setinfo, - .rmdir = CIFSSMBRmDir, - .unlink = CIFSSMBDelFile, - .rename_pending_delete = cifs_rename_pending_delete, - .rename = CIFSSMBRename, - .create_hardlink = CIFSCreateHardLink, - .query_symlink = cifs_query_symlink, - .open = cifs_open_file, - .set_fid = cifs_set_fid, - .close = cifs_close_file, - .flush = cifs_flush_file, - .async_readv = cifs_async_readv, - .async_writev = cifs_async_writev, - .sync_read = cifs_sync_read, - .sync_write = cifs_sync_write, - .query_dir_first = cifs_query_dir_first, - .query_dir_next = cifs_query_dir_next, - .close_dir = cifs_close_dir, - .calc_smb_size = smbCalcSize, - .oplock_response = cifs_oplock_response, - .queryfs = cifs_queryfs, - .mand_lock = cifs_mand_lock, - .mand_unlock_range = cifs_unlock_range, - .push_mand_locks = cifs_push_mandatory_locks, - .query_mf_symlink = cifs_query_mf_symlink, - .create_mf_symlink = cifs_create_mf_symlink, - .is_read_op = cifs_is_read_op, - .wp_retry_size = cifs_wp_retry_size, - .dir_needs_close = cifs_dir_needs_close, - .select_sectype = cifs_select_sectype, -#ifdef CONFIG_CIFS_XATTR - .query_all_EAs = CIFSSMBQAllEAs, - .set_EA = CIFSSMBSetEA, -#endif /* CIFS_XATTR */ - .get_acl = get_cifs_acl, - .get_acl_by_fid = get_cifs_acl_by_fid, - .set_acl = set_cifs_acl, - .make_node = cifs_make_node, -}; - -struct smb_version_values smb1_values = { - .version_string = SMB1_VERSION_STRING, - .protocol_id = SMB10_PROT_ID, - .large_lock_type = LOCKING_ANDX_LARGE_FILES, - .exclusive_lock_type = 0, - .shared_lock_type = LOCKING_ANDX_SHARED_LOCK, - .unlock_lock_type = 0, - .header_preamble_size = 4, - .header_size = sizeof(struct smb_hdr), - .max_header_size = MAX_CIFS_HDR_SIZE, - .read_rsp_size = sizeof(READ_RSP), - .lock_cmd = cpu_to_le16(SMB_COM_LOCKING_ANDX), - .cap_unix = CAP_UNIX, - .cap_nt_find = CAP_NT_SMBS | CAP_NT_FIND, - .cap_large_files = CAP_LARGE_FILES, - .signing_enabled = SECMODE_SIGN_ENABLED, - .signing_required = SECMODE_SIGN_REQUIRED, -}; diff --git a/fs/cifs/smb2file.c b/fs/cifs/smb2file.c deleted file mode 100644 index ba6cc50af390f..0000000000000 --- a/fs/cifs/smb2file.c +++ /dev/null @@ -1,371 +0,0 @@ -// SPDX-License-Identifier: LGPL-2.1 -/* - * - * Copyright (C) International Business Machines Corp., 2002, 2011 - * Author(s): Steve French (sfrench@us.ibm.com), - * Pavel Shilovsky ((pshilovsky@samba.org) 2012 - * - */ -#include -#include -#include -#include -#include -#include "cifsfs.h" -#include "cifspdu.h" -#include "cifsglob.h" -#include "cifsproto.h" -#include "cifs_debug.h" -#include "cifs_fs_sb.h" -#include "cifs_unicode.h" -#include "fscache.h" -#include "smb2proto.h" -#include "smb2status.h" - -static struct smb2_symlink_err_rsp *symlink_data(const struct kvec *iov) -{ - struct smb2_err_rsp *err = iov->iov_base; - struct smb2_symlink_err_rsp *sym = ERR_PTR(-EINVAL); - u32 len; - - if (err->ErrorContextCount) { - struct smb2_error_context_rsp *p, *end; - - len = (u32)err->ErrorContextCount * (offsetof(struct smb2_error_context_rsp, - ErrorContextData) + - sizeof(struct smb2_symlink_err_rsp)); - if (le32_to_cpu(err->ByteCount) < len || iov->iov_len < len + sizeof(*err)) - return ERR_PTR(-EINVAL); - - p = (struct smb2_error_context_rsp *)err->ErrorData; - end = (struct smb2_error_context_rsp *)((u8 *)err + iov->iov_len); - do { - if (le32_to_cpu(p->ErrorId) == SMB2_ERROR_ID_DEFAULT) { - sym = (struct smb2_symlink_err_rsp *)&p->ErrorContextData; - break; - } - cifs_dbg(FYI, "%s: skipping unhandled error context: 0x%x\n", - __func__, le32_to_cpu(p->ErrorId)); - - len = ALIGN(le32_to_cpu(p->ErrorDataLength), 8); - p = (struct smb2_error_context_rsp *)((u8 *)&p->ErrorContextData + len); - } while (p < end); - } else if (le32_to_cpu(err->ByteCount) >= sizeof(*sym) && - iov->iov_len >= SMB2_SYMLINK_STRUCT_SIZE) { - sym = (struct smb2_symlink_err_rsp *)err->ErrorData; - } - - if (!IS_ERR(sym) && (le32_to_cpu(sym->SymLinkErrorTag) != SYMLINK_ERROR_TAG || - le32_to_cpu(sym->ReparseTag) != IO_REPARSE_TAG_SYMLINK)) - sym = ERR_PTR(-EINVAL); - - return sym; -} - -int smb2_parse_symlink_response(struct cifs_sb_info *cifs_sb, const struct kvec *iov, char **path) -{ - struct smb2_symlink_err_rsp *sym; - unsigned int sub_offs, sub_len; - unsigned int print_offs, print_len; - char *s; - - if (!cifs_sb || !iov || !iov->iov_base || !iov->iov_len || !path) - return -EINVAL; - - sym = symlink_data(iov); - if (IS_ERR(sym)) - return PTR_ERR(sym); - - sub_len = le16_to_cpu(sym->SubstituteNameLength); - sub_offs = le16_to_cpu(sym->SubstituteNameOffset); - print_len = le16_to_cpu(sym->PrintNameLength); - print_offs = le16_to_cpu(sym->PrintNameOffset); - - if (iov->iov_len < SMB2_SYMLINK_STRUCT_SIZE + sub_offs + sub_len || - iov->iov_len < SMB2_SYMLINK_STRUCT_SIZE + print_offs + print_len) - return -EINVAL; - - s = cifs_strndup_from_utf16((char *)sym->PathBuffer + sub_offs, sub_len, true, - cifs_sb->local_nls); - if (!s) - return -ENOMEM; - convert_delimiter(s, '/'); - cifs_dbg(FYI, "%s: symlink target: %s\n", __func__, s); - - *path = s; - return 0; -} - -int smb2_open_file(const unsigned int xid, struct cifs_open_parms *oparms, __u32 *oplock, void *buf) -{ - int rc; - __le16 *smb2_path; - __u8 smb2_oplock; - struct cifs_open_info_data *data = buf; - struct smb2_file_all_info file_info = {}; - struct smb2_file_all_info *smb2_data = data ? &file_info : NULL; - struct kvec err_iov = {}; - int err_buftype = CIFS_NO_BUFFER; - struct cifs_fid *fid = oparms->fid; - struct network_resiliency_req nr_ioctl_req; - - smb2_path = cifs_convert_path_to_utf16(oparms->path, oparms->cifs_sb); - if (smb2_path == NULL) - return -ENOMEM; - - oparms->desired_access |= FILE_READ_ATTRIBUTES; - smb2_oplock = SMB2_OPLOCK_LEVEL_BATCH; - - rc = SMB2_open(xid, oparms, smb2_path, &smb2_oplock, smb2_data, NULL, &err_iov, - &err_buftype); - if (rc && data) { - struct smb2_hdr *hdr = err_iov.iov_base; - - if (unlikely(!err_iov.iov_base || err_buftype == CIFS_NO_BUFFER)) - goto out; - if (hdr->Status == STATUS_STOPPED_ON_SYMLINK) { - rc = smb2_parse_symlink_response(oparms->cifs_sb, &err_iov, - &data->symlink_target); - if (!rc) { - memset(smb2_data, 0, sizeof(*smb2_data)); - oparms->create_options |= OPEN_REPARSE_POINT; - rc = SMB2_open(xid, oparms, smb2_path, &smb2_oplock, smb2_data, - NULL, NULL, NULL); - oparms->create_options &= ~OPEN_REPARSE_POINT; - } - } - } - - if (rc) - goto out; - - if (oparms->tcon->use_resilient) { - /* default timeout is 0, servers pick default (120 seconds) */ - nr_ioctl_req.Timeout = - cpu_to_le32(oparms->tcon->handle_timeout); - nr_ioctl_req.Reserved = 0; - rc = SMB2_ioctl(xid, oparms->tcon, fid->persistent_fid, - fid->volatile_fid, FSCTL_LMR_REQUEST_RESILIENCY, - (char *)&nr_ioctl_req, sizeof(nr_ioctl_req), - CIFSMaxBufSize, NULL, NULL /* no return info */); - if (rc == -EOPNOTSUPP) { - cifs_dbg(VFS, - "resiliency not supported by server, disabling\n"); - oparms->tcon->use_resilient = false; - } else if (rc) - cifs_dbg(FYI, "error %d setting resiliency\n", rc); - - rc = 0; - } - - if (smb2_data) { - /* if open response does not have IndexNumber field - get it */ - if (smb2_data->IndexNumber == 0) { - rc = SMB2_get_srv_num(xid, oparms->tcon, - fid->persistent_fid, - fid->volatile_fid, - &smb2_data->IndexNumber); - if (rc) { - /* - * let get_inode_info disable server inode - * numbers - */ - smb2_data->IndexNumber = 0; - rc = 0; - } - } - memcpy(&data->fi, smb2_data, sizeof(data->fi)); - } - - *oplock = smb2_oplock; -out: - free_rsp_buf(err_buftype, err_iov.iov_base); - kfree(smb2_path); - return rc; -} - -int -smb2_unlock_range(struct cifsFileInfo *cfile, struct file_lock *flock, - const unsigned int xid) -{ - int rc = 0, stored_rc; - unsigned int max_num, num = 0, max_buf; - struct smb2_lock_element *buf, *cur; - struct cifs_tcon *tcon = tlink_tcon(cfile->tlink); - struct cifsInodeInfo *cinode = CIFS_I(d_inode(cfile->dentry)); - struct cifsLockInfo *li, *tmp; - __u64 length = 1 + flock->fl_end - flock->fl_start; - struct list_head tmp_llist; - - INIT_LIST_HEAD(&tmp_llist); - - /* - * Accessing maxBuf is racy with cifs_reconnect - need to store value - * and check it before using. - */ - max_buf = tcon->ses->server->maxBuf; - if (max_buf < sizeof(struct smb2_lock_element)) - return -EINVAL; - - BUILD_BUG_ON(sizeof(struct smb2_lock_element) > PAGE_SIZE); - max_buf = min_t(unsigned int, max_buf, PAGE_SIZE); - max_num = max_buf / sizeof(struct smb2_lock_element); - buf = kcalloc(max_num, sizeof(struct smb2_lock_element), GFP_KERNEL); - if (!buf) - return -ENOMEM; - - cur = buf; - - cifs_down_write(&cinode->lock_sem); - list_for_each_entry_safe(li, tmp, &cfile->llist->locks, llist) { - if (flock->fl_start > li->offset || - (flock->fl_start + length) < - (li->offset + li->length)) - continue; - if (current->tgid != li->pid) - /* - * flock and OFD lock are associated with an open - * file description, not the process. - */ - if (!(flock->fl_flags & (FL_FLOCK | FL_OFDLCK))) - continue; - if (cinode->can_cache_brlcks) { - /* - * We can cache brlock requests - simply remove a lock - * from the file's list. - */ - list_del(&li->llist); - cifs_del_lock_waiters(li); - kfree(li); - continue; - } - cur->Length = cpu_to_le64(li->length); - cur->Offset = cpu_to_le64(li->offset); - cur->Flags = cpu_to_le32(SMB2_LOCKFLAG_UNLOCK); - /* - * We need to save a lock here to let us add it again to the - * file's list if the unlock range request fails on the server. - */ - list_move(&li->llist, &tmp_llist); - if (++num == max_num) { - stored_rc = smb2_lockv(xid, tcon, - cfile->fid.persistent_fid, - cfile->fid.volatile_fid, - current->tgid, num, buf); - if (stored_rc) { - /* - * We failed on the unlock range request - add - * all locks from the tmp list to the head of - * the file's list. - */ - cifs_move_llist(&tmp_llist, - &cfile->llist->locks); - rc = stored_rc; - } else - /* - * The unlock range request succeed - free the - * tmp list. - */ - cifs_free_llist(&tmp_llist); - cur = buf; - num = 0; - } else - cur++; - } - if (num) { - stored_rc = smb2_lockv(xid, tcon, cfile->fid.persistent_fid, - cfile->fid.volatile_fid, current->tgid, - num, buf); - if (stored_rc) { - cifs_move_llist(&tmp_llist, &cfile->llist->locks); - rc = stored_rc; - } else - cifs_free_llist(&tmp_llist); - } - up_write(&cinode->lock_sem); - - kfree(buf); - return rc; -} - -static int -smb2_push_mand_fdlocks(struct cifs_fid_locks *fdlocks, const unsigned int xid, - struct smb2_lock_element *buf, unsigned int max_num) -{ - int rc = 0, stored_rc; - struct cifsFileInfo *cfile = fdlocks->cfile; - struct cifsLockInfo *li; - unsigned int num = 0; - struct smb2_lock_element *cur = buf; - struct cifs_tcon *tcon = tlink_tcon(cfile->tlink); - - list_for_each_entry(li, &fdlocks->locks, llist) { - cur->Length = cpu_to_le64(li->length); - cur->Offset = cpu_to_le64(li->offset); - cur->Flags = cpu_to_le32(li->type | - SMB2_LOCKFLAG_FAIL_IMMEDIATELY); - if (++num == max_num) { - stored_rc = smb2_lockv(xid, tcon, - cfile->fid.persistent_fid, - cfile->fid.volatile_fid, - current->tgid, num, buf); - if (stored_rc) - rc = stored_rc; - cur = buf; - num = 0; - } else - cur++; - } - if (num) { - stored_rc = smb2_lockv(xid, tcon, - cfile->fid.persistent_fid, - cfile->fid.volatile_fid, - current->tgid, num, buf); - if (stored_rc) - rc = stored_rc; - } - - return rc; -} - -int -smb2_push_mandatory_locks(struct cifsFileInfo *cfile) -{ - int rc = 0, stored_rc; - unsigned int xid; - unsigned int max_num, max_buf; - struct smb2_lock_element *buf; - struct cifsInodeInfo *cinode = CIFS_I(d_inode(cfile->dentry)); - struct cifs_fid_locks *fdlocks; - - xid = get_xid(); - - /* - * Accessing maxBuf is racy with cifs_reconnect - need to store value - * and check it for zero before using. - */ - max_buf = tlink_tcon(cfile->tlink)->ses->server->maxBuf; - if (max_buf < sizeof(struct smb2_lock_element)) { - free_xid(xid); - return -EINVAL; - } - - BUILD_BUG_ON(sizeof(struct smb2_lock_element) > PAGE_SIZE); - max_buf = min_t(unsigned int, max_buf, PAGE_SIZE); - max_num = max_buf / sizeof(struct smb2_lock_element); - buf = kcalloc(max_num, sizeof(struct smb2_lock_element), GFP_KERNEL); - if (!buf) { - free_xid(xid); - return -ENOMEM; - } - - list_for_each_entry(fdlocks, &cinode->llist, llist) { - stored_rc = smb2_push_mand_fdlocks(fdlocks, xid, buf, max_num); - if (stored_rc) - rc = stored_rc; - } - - kfree(buf); - free_xid(xid); - return rc; -} diff --git a/fs/cifs/smb2glob.h b/fs/cifs/smb2glob.h deleted file mode 100644 index 82e916ad167c0..0000000000000 --- a/fs/cifs/smb2glob.h +++ /dev/null @@ -1,44 +0,0 @@ -/* SPDX-License-Identifier: LGPL-2.1 */ -/* - * - * Definitions for various global variables and structures - * - * Copyright (C) International Business Machines Corp., 2002, 2011 - * Etersoft, 2012 - * Author(s): Steve French (sfrench@us.ibm.com) - * Jeremy Allison (jra@samba.org) - * Pavel Shilovsky (pshilovsky@samba.org) 2012 - * - */ -#ifndef _SMB2_GLOB_H -#define _SMB2_GLOB_H - -/* - ***************************************************************** - * Constants go here - ***************************************************************** - */ - -/* - * Identifiers for functions that use the open, operation, close pattern - * in smb2inode.c:smb2_compound_op() - */ -#define SMB2_OP_SET_DELETE 1 -#define SMB2_OP_SET_INFO 2 -#define SMB2_OP_QUERY_INFO 3 -#define SMB2_OP_QUERY_DIR 4 -#define SMB2_OP_MKDIR 5 -#define SMB2_OP_RENAME 6 -#define SMB2_OP_DELETE 7 -#define SMB2_OP_HARDLINK 8 -#define SMB2_OP_SET_EOF 9 -#define SMB2_OP_RMDIR 10 -#define SMB2_OP_POSIX_QUERY_INFO 11 - -/* Used when constructing chained read requests. */ -#define CHAINED_REQUEST 1 -#define START_OF_CHAIN 2 -#define END_OF_CHAIN 4 -#define RELATED_REQUEST 8 - -#endif /* _SMB2_GLOB_H */ diff --git a/fs/cifs/smb2inode.c b/fs/cifs/smb2inode.c deleted file mode 100644 index c97e049e29dd3..0000000000000 --- a/fs/cifs/smb2inode.c +++ /dev/null @@ -1,799 +0,0 @@ -// SPDX-License-Identifier: LGPL-2.1 -/* - * - * Copyright (C) International Business Machines Corp., 2002, 2011 - * Etersoft, 2012 - * Author(s): Pavel Shilovsky (pshilovsky@samba.org), - * Steve French (sfrench@us.ibm.com) - * - */ -#include -#include -#include -#include -#include -#include "cifsfs.h" -#include "cifspdu.h" -#include "cifsglob.h" -#include "cifsproto.h" -#include "cifs_debug.h" -#include "cifs_fs_sb.h" -#include "cifs_unicode.h" -#include "fscache.h" -#include "smb2glob.h" -#include "smb2pdu.h" -#include "smb2proto.h" -#include "cached_dir.h" -#include "smb2status.h" - -static void -free_set_inf_compound(struct smb_rqst *rqst) -{ - if (rqst[1].rq_iov) - SMB2_set_info_free(&rqst[1]); - if (rqst[2].rq_iov) - SMB2_close_free(&rqst[2]); -} - - -struct cop_vars { - struct cifs_open_parms oparms; - struct kvec rsp_iov[3]; - struct smb_rqst rqst[3]; - struct kvec open_iov[SMB2_CREATE_IOV_SIZE]; - struct kvec qi_iov[1]; - struct kvec si_iov[SMB2_SET_INFO_IOV_SIZE]; - struct kvec close_iov[1]; - struct smb2_file_rename_info rename_info; - struct smb2_file_link_info link_info; -}; - -/* - * note: If cfile is passed, the reference to it is dropped here. - * So make sure that you do not reuse cfile after return from this func. - * - * If passing @err_iov and @err_buftype, ensure to make them both large enough (>= 3) to hold all - * error responses. Caller is also responsible for freeing them up. - */ -static int smb2_compound_op(const unsigned int xid, struct cifs_tcon *tcon, - struct cifs_sb_info *cifs_sb, const char *full_path, - __u32 desired_access, __u32 create_disposition, __u32 create_options, - umode_t mode, void *ptr, int command, struct cifsFileInfo *cfile, - struct kvec *err_iov, int *err_buftype) -{ - struct cop_vars *vars = NULL; - struct kvec *rsp_iov; - struct smb_rqst *rqst; - int rc; - __le16 *utf16_path = NULL; - __u8 oplock = SMB2_OPLOCK_LEVEL_NONE; - struct cifs_fid fid; - struct cifs_ses *ses = tcon->ses; - struct TCP_Server_Info *server; - int num_rqst = 0; - int resp_buftype[3]; - struct smb2_query_info_rsp *qi_rsp = NULL; - struct cifs_open_info_data *idata; - int flags = 0; - __u8 delete_pending[8] = {1, 0, 0, 0, 0, 0, 0, 0}; - unsigned int size[2]; - void *data[2]; - int len; - - vars = kzalloc(sizeof(*vars), GFP_ATOMIC); - if (vars == NULL) - return -ENOMEM; - rqst = &vars->rqst[0]; - rsp_iov = &vars->rsp_iov[0]; - - server = cifs_pick_channel(ses); - - if (smb3_encryption_required(tcon)) - flags |= CIFS_TRANSFORM_REQ; - - resp_buftype[0] = resp_buftype[1] = resp_buftype[2] = CIFS_NO_BUFFER; - - /* We already have a handle so we can skip the open */ - if (cfile) - goto after_open; - - /* Open */ - utf16_path = cifs_convert_path_to_utf16(full_path, cifs_sb); - if (!utf16_path) { - rc = -ENOMEM; - goto finished; - } - - vars->oparms = (struct cifs_open_parms) { - .tcon = tcon, - .path = full_path, - .desired_access = desired_access, - .disposition = create_disposition, - .create_options = cifs_create_options(cifs_sb, create_options), - .fid = &fid, - .mode = mode, - .cifs_sb = cifs_sb, - }; - - rqst[num_rqst].rq_iov = &vars->open_iov[0]; - rqst[num_rqst].rq_nvec = SMB2_CREATE_IOV_SIZE; - rc = SMB2_open_init(tcon, server, - &rqst[num_rqst], &oplock, &vars->oparms, - utf16_path); - kfree(utf16_path); - if (rc) - goto finished; - - smb2_set_next_command(tcon, &rqst[num_rqst]); - after_open: - num_rqst++; - rc = 0; - - /* Operation */ - switch (command) { - case SMB2_OP_QUERY_INFO: - rqst[num_rqst].rq_iov = &vars->qi_iov[0]; - rqst[num_rqst].rq_nvec = 1; - - if (cfile) - rc = SMB2_query_info_init(tcon, server, - &rqst[num_rqst], - cfile->fid.persistent_fid, - cfile->fid.volatile_fid, - FILE_ALL_INFORMATION, - SMB2_O_INFO_FILE, 0, - sizeof(struct smb2_file_all_info) + - PATH_MAX * 2, 0, NULL); - else { - rc = SMB2_query_info_init(tcon, server, - &rqst[num_rqst], - COMPOUND_FID, - COMPOUND_FID, - FILE_ALL_INFORMATION, - SMB2_O_INFO_FILE, 0, - sizeof(struct smb2_file_all_info) + - PATH_MAX * 2, 0, NULL); - if (!rc) { - smb2_set_next_command(tcon, &rqst[num_rqst]); - smb2_set_related(&rqst[num_rqst]); - } - } - - if (rc) - goto finished; - num_rqst++; - trace_smb3_query_info_compound_enter(xid, ses->Suid, tcon->tid, - full_path); - break; - case SMB2_OP_POSIX_QUERY_INFO: - rqst[num_rqst].rq_iov = &vars->qi_iov[0]; - rqst[num_rqst].rq_nvec = 1; - - if (cfile) - rc = SMB2_query_info_init(tcon, server, - &rqst[num_rqst], - cfile->fid.persistent_fid, - cfile->fid.volatile_fid, - SMB_FIND_FILE_POSIX_INFO, - SMB2_O_INFO_FILE, 0, - /* TBD: fix following to allow for longer SIDs */ - sizeof(struct smb311_posix_qinfo *) + (PATH_MAX * 2) + - (sizeof(struct cifs_sid) * 2), 0, NULL); - else { - rc = SMB2_query_info_init(tcon, server, - &rqst[num_rqst], - COMPOUND_FID, - COMPOUND_FID, - SMB_FIND_FILE_POSIX_INFO, - SMB2_O_INFO_FILE, 0, - sizeof(struct smb311_posix_qinfo *) + (PATH_MAX * 2) + - (sizeof(struct cifs_sid) * 2), 0, NULL); - if (!rc) { - smb2_set_next_command(tcon, &rqst[num_rqst]); - smb2_set_related(&rqst[num_rqst]); - } - } - - if (rc) - goto finished; - num_rqst++; - trace_smb3_posix_query_info_compound_enter(xid, ses->Suid, tcon->tid, full_path); - break; - case SMB2_OP_DELETE: - trace_smb3_delete_enter(xid, ses->Suid, tcon->tid, full_path); - break; - case SMB2_OP_MKDIR: - /* - * Directories are created through parameters in the - * SMB2_open() call. - */ - trace_smb3_mkdir_enter(xid, ses->Suid, tcon->tid, full_path); - break; - case SMB2_OP_RMDIR: - rqst[num_rqst].rq_iov = &vars->si_iov[0]; - rqst[num_rqst].rq_nvec = 1; - - size[0] = 1; /* sizeof __u8 See MS-FSCC section 2.4.11 */ - data[0] = &delete_pending[0]; - - rc = SMB2_set_info_init(tcon, server, - &rqst[num_rqst], COMPOUND_FID, - COMPOUND_FID, current->tgid, - FILE_DISPOSITION_INFORMATION, - SMB2_O_INFO_FILE, 0, data, size); - if (rc) - goto finished; - smb2_set_next_command(tcon, &rqst[num_rqst]); - smb2_set_related(&rqst[num_rqst++]); - trace_smb3_rmdir_enter(xid, ses->Suid, tcon->tid, full_path); - break; - case SMB2_OP_SET_EOF: - rqst[num_rqst].rq_iov = &vars->si_iov[0]; - rqst[num_rqst].rq_nvec = 1; - - size[0] = 8; /* sizeof __le64 */ - data[0] = ptr; - - if (cfile) { - rc = SMB2_set_info_init(tcon, server, - &rqst[num_rqst], - cfile->fid.persistent_fid, - cfile->fid.volatile_fid, - current->tgid, - FILE_END_OF_FILE_INFORMATION, - SMB2_O_INFO_FILE, 0, - data, size); - } else { - rc = SMB2_set_info_init(tcon, server, - &rqst[num_rqst], - COMPOUND_FID, - COMPOUND_FID, - current->tgid, - FILE_END_OF_FILE_INFORMATION, - SMB2_O_INFO_FILE, 0, - data, size); - if (!rc) { - smb2_set_next_command(tcon, &rqst[num_rqst]); - smb2_set_related(&rqst[num_rqst]); - } - } - if (rc) - goto finished; - num_rqst++; - trace_smb3_set_eof_enter(xid, ses->Suid, tcon->tid, full_path); - break; - case SMB2_OP_SET_INFO: - rqst[num_rqst].rq_iov = &vars->si_iov[0]; - rqst[num_rqst].rq_nvec = 1; - - - size[0] = sizeof(FILE_BASIC_INFO); - data[0] = ptr; - - if (cfile) - rc = SMB2_set_info_init(tcon, server, - &rqst[num_rqst], - cfile->fid.persistent_fid, - cfile->fid.volatile_fid, current->tgid, - FILE_BASIC_INFORMATION, - SMB2_O_INFO_FILE, 0, data, size); - else { - rc = SMB2_set_info_init(tcon, server, - &rqst[num_rqst], - COMPOUND_FID, - COMPOUND_FID, current->tgid, - FILE_BASIC_INFORMATION, - SMB2_O_INFO_FILE, 0, data, size); - if (!rc) { - smb2_set_next_command(tcon, &rqst[num_rqst]); - smb2_set_related(&rqst[num_rqst]); - } - } - - if (rc) - goto finished; - num_rqst++; - trace_smb3_set_info_compound_enter(xid, ses->Suid, tcon->tid, - full_path); - break; - case SMB2_OP_RENAME: - rqst[num_rqst].rq_iov = &vars->si_iov[0]; - rqst[num_rqst].rq_nvec = 2; - - len = (2 * UniStrnlen((wchar_t *)ptr, PATH_MAX)); - - vars->rename_info.ReplaceIfExists = 1; - vars->rename_info.RootDirectory = 0; - vars->rename_info.FileNameLength = cpu_to_le32(len); - - size[0] = sizeof(struct smb2_file_rename_info); - data[0] = &vars->rename_info; - - size[1] = len + 2 /* null */; - data[1] = (__le16 *)ptr; - - if (cfile) - rc = SMB2_set_info_init(tcon, server, - &rqst[num_rqst], - cfile->fid.persistent_fid, - cfile->fid.volatile_fid, - current->tgid, FILE_RENAME_INFORMATION, - SMB2_O_INFO_FILE, 0, data, size); - else { - rc = SMB2_set_info_init(tcon, server, - &rqst[num_rqst], - COMPOUND_FID, COMPOUND_FID, - current->tgid, FILE_RENAME_INFORMATION, - SMB2_O_INFO_FILE, 0, data, size); - if (!rc) { - smb2_set_next_command(tcon, &rqst[num_rqst]); - smb2_set_related(&rqst[num_rqst]); - } - } - if (rc) - goto finished; - num_rqst++; - trace_smb3_rename_enter(xid, ses->Suid, tcon->tid, full_path); - break; - case SMB2_OP_HARDLINK: - rqst[num_rqst].rq_iov = &vars->si_iov[0]; - rqst[num_rqst].rq_nvec = 2; - - len = (2 * UniStrnlen((wchar_t *)ptr, PATH_MAX)); - - vars->link_info.ReplaceIfExists = 0; - vars->link_info.RootDirectory = 0; - vars->link_info.FileNameLength = cpu_to_le32(len); - - size[0] = sizeof(struct smb2_file_link_info); - data[0] = &vars->link_info; - - size[1] = len + 2 /* null */; - data[1] = (__le16 *)ptr; - - rc = SMB2_set_info_init(tcon, server, - &rqst[num_rqst], COMPOUND_FID, - COMPOUND_FID, current->tgid, - FILE_LINK_INFORMATION, - SMB2_O_INFO_FILE, 0, data, size); - if (rc) - goto finished; - smb2_set_next_command(tcon, &rqst[num_rqst]); - smb2_set_related(&rqst[num_rqst++]); - trace_smb3_hardlink_enter(xid, ses->Suid, tcon->tid, full_path); - break; - default: - cifs_dbg(VFS, "Invalid command\n"); - rc = -EINVAL; - } - if (rc) - goto finished; - - /* We already have a handle so we can skip the close */ - if (cfile) - goto after_close; - /* Close */ - flags |= CIFS_CP_CREATE_CLOSE_OP; - rqst[num_rqst].rq_iov = &vars->close_iov[0]; - rqst[num_rqst].rq_nvec = 1; - rc = SMB2_close_init(tcon, server, - &rqst[num_rqst], COMPOUND_FID, - COMPOUND_FID, false); - smb2_set_related(&rqst[num_rqst]); - if (rc) - goto finished; - after_close: - num_rqst++; - - if (cfile) { - rc = compound_send_recv(xid, ses, server, - flags, num_rqst - 2, - &rqst[1], &resp_buftype[1], - &rsp_iov[1]); - } else - rc = compound_send_recv(xid, ses, server, - flags, num_rqst, - rqst, resp_buftype, - rsp_iov); - - finished: - if (cfile) - cifsFileInfo_put(cfile); - - SMB2_open_free(&rqst[0]); - if (rc == -EREMCHG) { - pr_warn_once("server share %s deleted\n", tcon->tree_name); - tcon->need_reconnect = true; - } - - switch (command) { - case SMB2_OP_QUERY_INFO: - idata = ptr; - if (rc == 0 && cfile && cfile->symlink_target) { - idata->symlink_target = kstrdup(cfile->symlink_target, GFP_KERNEL); - if (!idata->symlink_target) - rc = -ENOMEM; - } - if (rc == 0) { - qi_rsp = (struct smb2_query_info_rsp *) - rsp_iov[1].iov_base; - rc = smb2_validate_and_copy_iov( - le16_to_cpu(qi_rsp->OutputBufferOffset), - le32_to_cpu(qi_rsp->OutputBufferLength), - &rsp_iov[1], sizeof(idata->fi), (char *)&idata->fi); - } - if (rqst[1].rq_iov) - SMB2_query_info_free(&rqst[1]); - if (rqst[2].rq_iov) - SMB2_close_free(&rqst[2]); - if (rc) - trace_smb3_query_info_compound_err(xid, ses->Suid, - tcon->tid, rc); - else - trace_smb3_query_info_compound_done(xid, ses->Suid, - tcon->tid); - break; - case SMB2_OP_POSIX_QUERY_INFO: - idata = ptr; - if (rc == 0 && cfile && cfile->symlink_target) { - idata->symlink_target = kstrdup(cfile->symlink_target, GFP_KERNEL); - if (!idata->symlink_target) - rc = -ENOMEM; - } - if (rc == 0) { - qi_rsp = (struct smb2_query_info_rsp *) - rsp_iov[1].iov_base; - rc = smb2_validate_and_copy_iov( - le16_to_cpu(qi_rsp->OutputBufferOffset), - le32_to_cpu(qi_rsp->OutputBufferLength), - &rsp_iov[1], sizeof(idata->posix_fi) /* add SIDs */, - (char *)&idata->posix_fi); - } - if (rqst[1].rq_iov) - SMB2_query_info_free(&rqst[1]); - if (rqst[2].rq_iov) - SMB2_close_free(&rqst[2]); - if (rc) - trace_smb3_posix_query_info_compound_err(xid, ses->Suid, tcon->tid, rc); - else - trace_smb3_posix_query_info_compound_done(xid, ses->Suid, tcon->tid); - break; - case SMB2_OP_DELETE: - if (rc) - trace_smb3_delete_err(xid, ses->Suid, tcon->tid, rc); - else - trace_smb3_delete_done(xid, ses->Suid, tcon->tid); - if (rqst[1].rq_iov) - SMB2_close_free(&rqst[1]); - break; - case SMB2_OP_MKDIR: - if (rc) - trace_smb3_mkdir_err(xid, ses->Suid, tcon->tid, rc); - else - trace_smb3_mkdir_done(xid, ses->Suid, tcon->tid); - if (rqst[1].rq_iov) - SMB2_close_free(&rqst[1]); - break; - case SMB2_OP_HARDLINK: - if (rc) - trace_smb3_hardlink_err(xid, ses->Suid, tcon->tid, rc); - else - trace_smb3_hardlink_done(xid, ses->Suid, tcon->tid); - free_set_inf_compound(rqst); - break; - case SMB2_OP_RENAME: - if (rc) - trace_smb3_rename_err(xid, ses->Suid, tcon->tid, rc); - else - trace_smb3_rename_done(xid, ses->Suid, tcon->tid); - free_set_inf_compound(rqst); - break; - case SMB2_OP_RMDIR: - if (rc) - trace_smb3_rmdir_err(xid, ses->Suid, tcon->tid, rc); - else - trace_smb3_rmdir_done(xid, ses->Suid, tcon->tid); - free_set_inf_compound(rqst); - break; - case SMB2_OP_SET_EOF: - if (rc) - trace_smb3_set_eof_err(xid, ses->Suid, tcon->tid, rc); - else - trace_smb3_set_eof_done(xid, ses->Suid, tcon->tid); - free_set_inf_compound(rqst); - break; - case SMB2_OP_SET_INFO: - if (rc) - trace_smb3_set_info_compound_err(xid, ses->Suid, - tcon->tid, rc); - else - trace_smb3_set_info_compound_done(xid, ses->Suid, - tcon->tid); - free_set_inf_compound(rqst); - break; - } - - if (rc && err_iov && err_buftype) { - memcpy(err_iov, rsp_iov, 3 * sizeof(*err_iov)); - memcpy(err_buftype, resp_buftype, 3 * sizeof(*err_buftype)); - } else { - free_rsp_buf(resp_buftype[0], rsp_iov[0].iov_base); - free_rsp_buf(resp_buftype[1], rsp_iov[1].iov_base); - free_rsp_buf(resp_buftype[2], rsp_iov[2].iov_base); - } - kfree(vars); - return rc; -} - -int smb2_query_path_info(const unsigned int xid, struct cifs_tcon *tcon, - struct cifs_sb_info *cifs_sb, const char *full_path, - struct cifs_open_info_data *data, bool *adjust_tz, bool *reparse) -{ - __u32 create_options = 0; - struct cifsFileInfo *cfile; - struct cached_fid *cfid = NULL; - struct kvec err_iov[3] = {}; - int err_buftype[3] = {}; - bool islink; - int rc, rc2; - - *adjust_tz = false; - *reparse = false; - - if (strcmp(full_path, "")) - rc = -ENOENT; - else - rc = open_cached_dir(xid, tcon, full_path, cifs_sb, false, &cfid); - /* If it is a root and its handle is cached then use it */ - if (!rc) { - if (cfid->file_all_info_is_valid) { - memcpy(&data->fi, &cfid->file_all_info, sizeof(data->fi)); - } else { - rc = SMB2_query_info(xid, tcon, cfid->fid.persistent_fid, - cfid->fid.volatile_fid, &data->fi); - } - close_cached_dir(cfid); - return rc; - } - - cifs_get_readable_path(tcon, full_path, &cfile); - rc = smb2_compound_op(xid, tcon, cifs_sb, full_path, FILE_READ_ATTRIBUTES, FILE_OPEN, - create_options, ACL_NO_MODE, data, SMB2_OP_QUERY_INFO, cfile, - err_iov, err_buftype); - if (rc) { - struct smb2_hdr *hdr = err_iov[0].iov_base; - - if (unlikely(!hdr || err_buftype[0] == CIFS_NO_BUFFER)) - goto out; - if (rc == -EOPNOTSUPP && hdr->Command == SMB2_CREATE && - hdr->Status == STATUS_STOPPED_ON_SYMLINK) { - rc = smb2_parse_symlink_response(cifs_sb, err_iov, - &data->symlink_target); - if (rc) - goto out; - - *reparse = true; - create_options |= OPEN_REPARSE_POINT; - - /* Failed on a symbolic link - query a reparse point info */ - cifs_get_readable_path(tcon, full_path, &cfile); - rc = smb2_compound_op(xid, tcon, cifs_sb, full_path, - FILE_READ_ATTRIBUTES, FILE_OPEN, - create_options, ACL_NO_MODE, data, - SMB2_OP_QUERY_INFO, cfile, NULL, NULL); - goto out; - } else if (rc != -EREMOTE && hdr->Status == STATUS_OBJECT_NAME_INVALID) { - rc2 = cifs_inval_name_dfs_link_error(xid, tcon, cifs_sb, - full_path, &islink); - if (rc2) { - rc = rc2; - goto out; - } - if (islink) - rc = -EREMOTE; - } - if (rc == -EREMOTE && IS_ENABLED(CONFIG_CIFS_DFS_UPCALL) && cifs_sb && - (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_NO_DFS)) - rc = -EOPNOTSUPP; - } - -out: - free_rsp_buf(err_buftype[0], err_iov[0].iov_base); - free_rsp_buf(err_buftype[1], err_iov[1].iov_base); - free_rsp_buf(err_buftype[2], err_iov[2].iov_base); - return rc; -} - - -int smb311_posix_query_path_info(const unsigned int xid, struct cifs_tcon *tcon, - struct cifs_sb_info *cifs_sb, const char *full_path, - struct cifs_open_info_data *data, bool *adjust_tz, bool *reparse) -{ - int rc; - __u32 create_options = 0; - struct cifsFileInfo *cfile; - struct kvec err_iov[3] = {}; - int err_buftype[3] = {}; - - *adjust_tz = false; - *reparse = false; - - /* - * BB TODO: Add support for using the cached root handle. - * Create SMB2_query_posix_info worker function to do non-compounded query - * when we already have an open file handle for this. For now this is fast enough - * (always using the compounded version). - */ - - cifs_get_readable_path(tcon, full_path, &cfile); - rc = smb2_compound_op(xid, tcon, cifs_sb, full_path, FILE_READ_ATTRIBUTES, FILE_OPEN, - create_options, ACL_NO_MODE, data, SMB2_OP_POSIX_QUERY_INFO, cfile, - err_iov, err_buftype); - if (rc == -EOPNOTSUPP) { - /* BB TODO: When support for special files added to Samba re-verify this path */ - if (err_iov[0].iov_base && err_buftype[0] != CIFS_NO_BUFFER && - ((struct smb2_hdr *)err_iov[0].iov_base)->Command == SMB2_CREATE && - ((struct smb2_hdr *)err_iov[0].iov_base)->Status == STATUS_STOPPED_ON_SYMLINK) { - rc = smb2_parse_symlink_response(cifs_sb, err_iov, &data->symlink_target); - if (rc) - goto out; - } - *reparse = true; - create_options |= OPEN_REPARSE_POINT; - - /* Failed on a symbolic link - query a reparse point info */ - cifs_get_readable_path(tcon, full_path, &cfile); - rc = smb2_compound_op(xid, tcon, cifs_sb, full_path, FILE_READ_ATTRIBUTES, - FILE_OPEN, create_options, ACL_NO_MODE, data, - SMB2_OP_POSIX_QUERY_INFO, cfile, NULL, NULL); - } - -out: - free_rsp_buf(err_buftype[0], err_iov[0].iov_base); - free_rsp_buf(err_buftype[1], err_iov[1].iov_base); - free_rsp_buf(err_buftype[2], err_iov[2].iov_base); - return rc; -} - -int -smb2_mkdir(const unsigned int xid, struct inode *parent_inode, umode_t mode, - struct cifs_tcon *tcon, const char *name, - struct cifs_sb_info *cifs_sb) -{ - return smb2_compound_op(xid, tcon, cifs_sb, name, - FILE_WRITE_ATTRIBUTES, FILE_CREATE, - CREATE_NOT_FILE, mode, NULL, SMB2_OP_MKDIR, - NULL, NULL, NULL); -} - -void -smb2_mkdir_setinfo(struct inode *inode, const char *name, - struct cifs_sb_info *cifs_sb, struct cifs_tcon *tcon, - const unsigned int xid) -{ - FILE_BASIC_INFO data; - struct cifsInodeInfo *cifs_i; - struct cifsFileInfo *cfile; - u32 dosattrs; - int tmprc; - - memset(&data, 0, sizeof(data)); - cifs_i = CIFS_I(inode); - dosattrs = cifs_i->cifsAttrs | ATTR_READONLY; - data.Attributes = cpu_to_le32(dosattrs); - cifs_get_writable_path(tcon, name, FIND_WR_ANY, &cfile); - tmprc = smb2_compound_op(xid, tcon, cifs_sb, name, - FILE_WRITE_ATTRIBUTES, FILE_CREATE, - CREATE_NOT_FILE, ACL_NO_MODE, - &data, SMB2_OP_SET_INFO, cfile, NULL, NULL); - if (tmprc == 0) - cifs_i->cifsAttrs = dosattrs; -} - -int -smb2_rmdir(const unsigned int xid, struct cifs_tcon *tcon, const char *name, - struct cifs_sb_info *cifs_sb) -{ - drop_cached_dir_by_name(xid, tcon, name, cifs_sb); - return smb2_compound_op(xid, tcon, cifs_sb, name, DELETE, FILE_OPEN, - CREATE_NOT_FILE, ACL_NO_MODE, - NULL, SMB2_OP_RMDIR, NULL, NULL, NULL); -} - -int -smb2_unlink(const unsigned int xid, struct cifs_tcon *tcon, const char *name, - struct cifs_sb_info *cifs_sb) -{ - return smb2_compound_op(xid, tcon, cifs_sb, name, DELETE, FILE_OPEN, - CREATE_DELETE_ON_CLOSE | OPEN_REPARSE_POINT, - ACL_NO_MODE, NULL, SMB2_OP_DELETE, NULL, NULL, NULL); -} - -static int -smb2_set_path_attr(const unsigned int xid, struct cifs_tcon *tcon, - const char *from_name, const char *to_name, - struct cifs_sb_info *cifs_sb, __u32 access, int command, - struct cifsFileInfo *cfile) -{ - __le16 *smb2_to_name = NULL; - int rc; - - smb2_to_name = cifs_convert_path_to_utf16(to_name, cifs_sb); - if (smb2_to_name == NULL) { - rc = -ENOMEM; - goto smb2_rename_path; - } - rc = smb2_compound_op(xid, tcon, cifs_sb, from_name, access, - FILE_OPEN, 0, ACL_NO_MODE, smb2_to_name, - command, cfile, NULL, NULL); -smb2_rename_path: - kfree(smb2_to_name); - return rc; -} - -int -smb2_rename_path(const unsigned int xid, struct cifs_tcon *tcon, - const char *from_name, const char *to_name, - struct cifs_sb_info *cifs_sb) -{ - struct cifsFileInfo *cfile; - - drop_cached_dir_by_name(xid, tcon, from_name, cifs_sb); - cifs_get_writable_path(tcon, from_name, FIND_WR_WITH_DELETE, &cfile); - - return smb2_set_path_attr(xid, tcon, from_name, to_name, - cifs_sb, DELETE, SMB2_OP_RENAME, cfile); -} - -int -smb2_create_hardlink(const unsigned int xid, struct cifs_tcon *tcon, - const char *from_name, const char *to_name, - struct cifs_sb_info *cifs_sb) -{ - return smb2_set_path_attr(xid, tcon, from_name, to_name, cifs_sb, - FILE_READ_ATTRIBUTES, SMB2_OP_HARDLINK, - NULL); -} - -int -smb2_set_path_size(const unsigned int xid, struct cifs_tcon *tcon, - const char *full_path, __u64 size, - struct cifs_sb_info *cifs_sb, bool set_alloc) -{ - __le64 eof = cpu_to_le64(size); - struct cifsFileInfo *cfile; - - cifs_get_writable_path(tcon, full_path, FIND_WR_ANY, &cfile); - return smb2_compound_op(xid, tcon, cifs_sb, full_path, - FILE_WRITE_DATA, FILE_OPEN, 0, ACL_NO_MODE, - &eof, SMB2_OP_SET_EOF, cfile, NULL, NULL); -} - -int -smb2_set_file_info(struct inode *inode, const char *full_path, - FILE_BASIC_INFO *buf, const unsigned int xid) -{ - struct cifs_sb_info *cifs_sb = CIFS_SB(inode->i_sb); - struct tcon_link *tlink; - struct cifs_tcon *tcon; - struct cifsFileInfo *cfile; - int rc; - - if ((buf->CreationTime == 0) && (buf->LastAccessTime == 0) && - (buf->LastWriteTime == 0) && (buf->ChangeTime == 0) && - (buf->Attributes == 0)) - return 0; /* would be a no op, no sense sending this */ - - tlink = cifs_sb_tlink(cifs_sb); - if (IS_ERR(tlink)) - return PTR_ERR(tlink); - tcon = tlink_tcon(tlink); - - cifs_get_writable_path(tcon, full_path, FIND_WR_ANY, &cfile); - rc = smb2_compound_op(xid, tcon, cifs_sb, full_path, - FILE_WRITE_ATTRIBUTES, FILE_OPEN, - 0, ACL_NO_MODE, buf, SMB2_OP_SET_INFO, cfile, - NULL, NULL); - cifs_put_tlink(tlink); - return rc; -} diff --git a/fs/cifs/smb2maperror.c b/fs/cifs/smb2maperror.c deleted file mode 100644 index 194799ddd3828..0000000000000 --- a/fs/cifs/smb2maperror.c +++ /dev/null @@ -1,2481 +0,0 @@ -// SPDX-License-Identifier: LGPL-2.1 -/* - * - * Functions which do error mapping of SMB2 status codes to POSIX errors - * - * Copyright (C) International Business Machines Corp., 2009 - * Author(s): Steve French (sfrench@us.ibm.com) - * - */ -#include -#include "cifsglob.h" -#include "cifs_debug.h" -#include "smb2pdu.h" -#include "smb2proto.h" -#include "smb2status.h" -#include "smb2glob.h" -#include "trace.h" - -struct status_to_posix_error { - __le32 smb2_status; - int posix_error; - char *status_string; -}; - -static const struct status_to_posix_error smb2_error_map_table[] = { - {STATUS_SUCCESS, 0, "STATUS_SUCCESS"}, - {STATUS_WAIT_0, 0, "STATUS_WAIT_0"}, - {STATUS_WAIT_1, -EIO, "STATUS_WAIT_1"}, - {STATUS_WAIT_2, -EIO, "STATUS_WAIT_2"}, - {STATUS_WAIT_3, -EIO, "STATUS_WAIT_3"}, - {STATUS_WAIT_63, -EIO, "STATUS_WAIT_63"}, - {STATUS_ABANDONED, -EIO, "STATUS_ABANDONED"}, - {STATUS_ABANDONED_WAIT_0, -EIO, "STATUS_ABANDONED_WAIT_0"}, - {STATUS_ABANDONED_WAIT_63, -EIO, "STATUS_ABANDONED_WAIT_63"}, - {STATUS_USER_APC, -EIO, "STATUS_USER_APC"}, - {STATUS_KERNEL_APC, -EIO, "STATUS_KERNEL_APC"}, - {STATUS_ALERTED, -EIO, "STATUS_ALERTED"}, - {STATUS_TIMEOUT, -ETIMEDOUT, "STATUS_TIMEOUT"}, - {STATUS_PENDING, -EIO, "STATUS_PENDING"}, - {STATUS_REPARSE, -EIO, "STATUS_REPARSE"}, - {STATUS_MORE_ENTRIES, -EIO, "STATUS_MORE_ENTRIES"}, - {STATUS_NOT_ALL_ASSIGNED, -EIO, "STATUS_NOT_ALL_ASSIGNED"}, - {STATUS_SOME_NOT_MAPPED, -EIO, "STATUS_SOME_NOT_MAPPED"}, - {STATUS_OPLOCK_BREAK_IN_PROGRESS, -EIO, - "STATUS_OPLOCK_BREAK_IN_PROGRESS"}, - {STATUS_VOLUME_MOUNTED, -EIO, "STATUS_VOLUME_MOUNTED"}, - {STATUS_RXACT_COMMITTED, -EIO, "STATUS_RXACT_COMMITTED"}, - {STATUS_NOTIFY_CLEANUP, -EIO, "STATUS_NOTIFY_CLEANUP"}, - {STATUS_NOTIFY_ENUM_DIR, -EIO, "STATUS_NOTIFY_ENUM_DIR"}, - {STATUS_NO_QUOTAS_FOR_ACCOUNT, -EIO, "STATUS_NO_QUOTAS_FOR_ACCOUNT"}, - {STATUS_PRIMARY_TRANSPORT_CONNECT_FAILED, -EIO, - "STATUS_PRIMARY_TRANSPORT_CONNECT_FAILED"}, - {STATUS_PAGE_FAULT_TRANSITION, -EIO, "STATUS_PAGE_FAULT_TRANSITION"}, - {STATUS_PAGE_FAULT_DEMAND_ZERO, -EIO, "STATUS_PAGE_FAULT_DEMAND_ZERO"}, - {STATUS_PAGE_FAULT_COPY_ON_WRITE, -EIO, - "STATUS_PAGE_FAULT_COPY_ON_WRITE"}, - {STATUS_PAGE_FAULT_GUARD_PAGE, -EIO, "STATUS_PAGE_FAULT_GUARD_PAGE"}, - {STATUS_PAGE_FAULT_PAGING_FILE, -EIO, "STATUS_PAGE_FAULT_PAGING_FILE"}, - {STATUS_CACHE_PAGE_LOCKED, -EIO, "STATUS_CACHE_PAGE_LOCKED"}, - {STATUS_CRASH_DUMP, -EIO, "STATUS_CRASH_DUMP"}, - {STATUS_BUFFER_ALL_ZEROS, -EIO, "STATUS_BUFFER_ALL_ZEROS"}, - {STATUS_REPARSE_OBJECT, -EIO, "STATUS_REPARSE_OBJECT"}, - {STATUS_RESOURCE_REQUIREMENTS_CHANGED, -EIO, - "STATUS_RESOURCE_REQUIREMENTS_CHANGED"}, - {STATUS_TRANSLATION_COMPLETE, -EIO, "STATUS_TRANSLATION_COMPLETE"}, - {STATUS_DS_MEMBERSHIP_EVALUATED_LOCALLY, -EIO, - "STATUS_DS_MEMBERSHIP_EVALUATED_LOCALLY"}, - {STATUS_NOTHING_TO_TERMINATE, -EIO, "STATUS_NOTHING_TO_TERMINATE"}, - {STATUS_PROCESS_NOT_IN_JOB, -EIO, "STATUS_PROCESS_NOT_IN_JOB"}, - {STATUS_PROCESS_IN_JOB, -EIO, "STATUS_PROCESS_IN_JOB"}, - {STATUS_VOLSNAP_HIBERNATE_READY, -EIO, - "STATUS_VOLSNAP_HIBERNATE_READY"}, - {STATUS_FSFILTER_OP_COMPLETED_SUCCESSFULLY, -EIO, - "STATUS_FSFILTER_OP_COMPLETED_SUCCESSFULLY"}, - {STATUS_INTERRUPT_VECTOR_ALREADY_CONNECTED, -EIO, - "STATUS_INTERRUPT_VECTOR_ALREADY_CONNECTED"}, - {STATUS_INTERRUPT_STILL_CONNECTED, -EIO, - "STATUS_INTERRUPT_STILL_CONNECTED"}, - {STATUS_PROCESS_CLONED, -EIO, "STATUS_PROCESS_CLONED"}, - {STATUS_FILE_LOCKED_WITH_ONLY_READERS, -EIO, - "STATUS_FILE_LOCKED_WITH_ONLY_READERS"}, - {STATUS_FILE_LOCKED_WITH_WRITERS, -EIO, - "STATUS_FILE_LOCKED_WITH_WRITERS"}, - {STATUS_RESOURCEMANAGER_READ_ONLY, -EROFS, - "STATUS_RESOURCEMANAGER_READ_ONLY"}, - {STATUS_WAIT_FOR_OPLOCK, -EIO, "STATUS_WAIT_FOR_OPLOCK"}, - {DBG_EXCEPTION_HANDLED, -EIO, "DBG_EXCEPTION_HANDLED"}, - {DBG_CONTINUE, -EIO, "DBG_CONTINUE"}, - {STATUS_FLT_IO_COMPLETE, -EIO, "STATUS_FLT_IO_COMPLETE"}, - {STATUS_OBJECT_NAME_EXISTS, -EIO, "STATUS_OBJECT_NAME_EXISTS"}, - {STATUS_THREAD_WAS_SUSPENDED, -EIO, "STATUS_THREAD_WAS_SUSPENDED"}, - {STATUS_WORKING_SET_LIMIT_RANGE, -EIO, - "STATUS_WORKING_SET_LIMIT_RANGE"}, - {STATUS_IMAGE_NOT_AT_BASE, -EIO, "STATUS_IMAGE_NOT_AT_BASE"}, - {STATUS_RXACT_STATE_CREATED, -EIO, "STATUS_RXACT_STATE_CREATED"}, - {STATUS_SEGMENT_NOTIFICATION, -EIO, "STATUS_SEGMENT_NOTIFICATION"}, - {STATUS_LOCAL_USER_SESSION_KEY, -EIO, "STATUS_LOCAL_USER_SESSION_KEY"}, - {STATUS_BAD_CURRENT_DIRECTORY, -EIO, "STATUS_BAD_CURRENT_DIRECTORY"}, - {STATUS_SERIAL_MORE_WRITES, -EIO, "STATUS_SERIAL_MORE_WRITES"}, - {STATUS_REGISTRY_RECOVERED, -EIO, "STATUS_REGISTRY_RECOVERED"}, - {STATUS_FT_READ_RECOVERY_FROM_BACKUP, -EIO, - "STATUS_FT_READ_RECOVERY_FROM_BACKUP"}, - {STATUS_FT_WRITE_RECOVERY, -EIO, "STATUS_FT_WRITE_RECOVERY"}, - {STATUS_SERIAL_COUNTER_TIMEOUT, -ETIMEDOUT, - "STATUS_SERIAL_COUNTER_TIMEOUT"}, - {STATUS_NULL_LM_PASSWORD, -EIO, "STATUS_NULL_LM_PASSWORD"}, - {STATUS_IMAGE_MACHINE_TYPE_MISMATCH, -EIO, - "STATUS_IMAGE_MACHINE_TYPE_MISMATCH"}, - {STATUS_RECEIVE_PARTIAL, -EIO, "STATUS_RECEIVE_PARTIAL"}, - {STATUS_RECEIVE_EXPEDITED, -EIO, "STATUS_RECEIVE_EXPEDITED"}, - {STATUS_RECEIVE_PARTIAL_EXPEDITED, -EIO, - "STATUS_RECEIVE_PARTIAL_EXPEDITED"}, - {STATUS_EVENT_DONE, -EIO, "STATUS_EVENT_DONE"}, - {STATUS_EVENT_PENDING, -EIO, "STATUS_EVENT_PENDING"}, - {STATUS_CHECKING_FILE_SYSTEM, -EIO, "STATUS_CHECKING_FILE_SYSTEM"}, - {STATUS_FATAL_APP_EXIT, -EIO, "STATUS_FATAL_APP_EXIT"}, - {STATUS_PREDEFINED_HANDLE, -EIO, "STATUS_PREDEFINED_HANDLE"}, - {STATUS_WAS_UNLOCKED, -EIO, "STATUS_WAS_UNLOCKED"}, - {STATUS_SERVICE_NOTIFICATION, -EIO, "STATUS_SERVICE_NOTIFICATION"}, - {STATUS_WAS_LOCKED, -EIO, "STATUS_WAS_LOCKED"}, - {STATUS_LOG_HARD_ERROR, -EIO, "STATUS_LOG_HARD_ERROR"}, - {STATUS_ALREADY_WIN32, -EIO, "STATUS_ALREADY_WIN32"}, - {STATUS_WX86_UNSIMULATE, -EIO, "STATUS_WX86_UNSIMULATE"}, - {STATUS_WX86_CONTINUE, -EIO, "STATUS_WX86_CONTINUE"}, - {STATUS_WX86_SINGLE_STEP, -EIO, "STATUS_WX86_SINGLE_STEP"}, - {STATUS_WX86_BREAKPOINT, -EIO, "STATUS_WX86_BREAKPOINT"}, - {STATUS_WX86_EXCEPTION_CONTINUE, -EIO, - "STATUS_WX86_EXCEPTION_CONTINUE"}, - {STATUS_WX86_EXCEPTION_LASTCHANCE, -EIO, - "STATUS_WX86_EXCEPTION_LASTCHANCE"}, - {STATUS_WX86_EXCEPTION_CHAIN, -EIO, "STATUS_WX86_EXCEPTION_CHAIN"}, - {STATUS_IMAGE_MACHINE_TYPE_MISMATCH_EXE, -EIO, - "STATUS_IMAGE_MACHINE_TYPE_MISMATCH_EXE"}, - {STATUS_NO_YIELD_PERFORMED, -EIO, "STATUS_NO_YIELD_PERFORMED"}, - {STATUS_TIMER_RESUME_IGNORED, -EIO, "STATUS_TIMER_RESUME_IGNORED"}, - {STATUS_ARBITRATION_UNHANDLED, -EIO, "STATUS_ARBITRATION_UNHANDLED"}, - {STATUS_CARDBUS_NOT_SUPPORTED, -ENOSYS, "STATUS_CARDBUS_NOT_SUPPORTED"}, - {STATUS_WX86_CREATEWX86TIB, -EIO, "STATUS_WX86_CREATEWX86TIB"}, - {STATUS_MP_PROCESSOR_MISMATCH, -EIO, "STATUS_MP_PROCESSOR_MISMATCH"}, - {STATUS_HIBERNATED, -EIO, "STATUS_HIBERNATED"}, - {STATUS_RESUME_HIBERNATION, -EIO, "STATUS_RESUME_HIBERNATION"}, - {STATUS_FIRMWARE_UPDATED, -EIO, "STATUS_FIRMWARE_UPDATED"}, - {STATUS_DRIVERS_LEAKING_LOCKED_PAGES, -EIO, - "STATUS_DRIVERS_LEAKING_LOCKED_PAGES"}, - {STATUS_MESSAGE_RETRIEVED, -EIO, "STATUS_MESSAGE_RETRIEVED"}, - {STATUS_SYSTEM_POWERSTATE_TRANSITION, -EIO, - "STATUS_SYSTEM_POWERSTATE_TRANSITION"}, - {STATUS_ALPC_CHECK_COMPLETION_LIST, -EIO, - "STATUS_ALPC_CHECK_COMPLETION_LIST"}, - {STATUS_SYSTEM_POWERSTATE_COMPLEX_TRANSITION, -EIO, - "STATUS_SYSTEM_POWERSTATE_COMPLEX_TRANSITION"}, - {STATUS_ACCESS_AUDIT_BY_POLICY, -EIO, "STATUS_ACCESS_AUDIT_BY_POLICY"}, - {STATUS_ABANDON_HIBERFILE, -EIO, "STATUS_ABANDON_HIBERFILE"}, - {STATUS_BIZRULES_NOT_ENABLED, -EIO, "STATUS_BIZRULES_NOT_ENABLED"}, - {STATUS_WAKE_SYSTEM, -EIO, "STATUS_WAKE_SYSTEM"}, - {STATUS_DS_SHUTTING_DOWN, -EIO, "STATUS_DS_SHUTTING_DOWN"}, - {DBG_REPLY_LATER, -EIO, "DBG_REPLY_LATER"}, - {DBG_UNABLE_TO_PROVIDE_HANDLE, -EIO, "DBG_UNABLE_TO_PROVIDE_HANDLE"}, - {DBG_TERMINATE_THREAD, -EIO, "DBG_TERMINATE_THREAD"}, - {DBG_TERMINATE_PROCESS, -EIO, "DBG_TERMINATE_PROCESS"}, - {DBG_CONTROL_C, -EIO, "DBG_CONTROL_C"}, - {DBG_PRINTEXCEPTION_C, -EIO, "DBG_PRINTEXCEPTION_C"}, - {DBG_RIPEXCEPTION, -EIO, "DBG_RIPEXCEPTION"}, - {DBG_CONTROL_BREAK, -EIO, "DBG_CONTROL_BREAK"}, - {DBG_COMMAND_EXCEPTION, -EIO, "DBG_COMMAND_EXCEPTION"}, - {RPC_NT_UUID_LOCAL_ONLY, -EIO, "RPC_NT_UUID_LOCAL_ONLY"}, - {RPC_NT_SEND_INCOMPLETE, -EIO, "RPC_NT_SEND_INCOMPLETE"}, - {STATUS_CTX_CDM_CONNECT, -EIO, "STATUS_CTX_CDM_CONNECT"}, - {STATUS_CTX_CDM_DISCONNECT, -EIO, "STATUS_CTX_CDM_DISCONNECT"}, - {STATUS_SXS_RELEASE_ACTIVATION_CONTEXT, -EIO, - "STATUS_SXS_RELEASE_ACTIVATION_CONTEXT"}, - {STATUS_RECOVERY_NOT_NEEDED, -EIO, "STATUS_RECOVERY_NOT_NEEDED"}, - {STATUS_RM_ALREADY_STARTED, -EIO, "STATUS_RM_ALREADY_STARTED"}, - {STATUS_LOG_NO_RESTART, -EIO, "STATUS_LOG_NO_RESTART"}, - {STATUS_VIDEO_DRIVER_DEBUG_REPORT_REQUEST, -EIO, - "STATUS_VIDEO_DRIVER_DEBUG_REPORT_REQUEST"}, - {STATUS_GRAPHICS_PARTIAL_DATA_POPULATED, -EIO, - "STATUS_GRAPHICS_PARTIAL_DATA_POPULATED"}, - {STATUS_GRAPHICS_DRIVER_MISMATCH, -EIO, - "STATUS_GRAPHICS_DRIVER_MISMATCH"}, - {STATUS_GRAPHICS_MODE_NOT_PINNED, -EIO, - "STATUS_GRAPHICS_MODE_NOT_PINNED"}, - {STATUS_GRAPHICS_NO_PREFERRED_MODE, -EIO, - "STATUS_GRAPHICS_NO_PREFERRED_MODE"}, - {STATUS_GRAPHICS_DATASET_IS_EMPTY, -EIO, - "STATUS_GRAPHICS_DATASET_IS_EMPTY"}, - {STATUS_GRAPHICS_NO_MORE_ELEMENTS_IN_DATASET, -EIO, - "STATUS_GRAPHICS_NO_MORE_ELEMENTS_IN_DATASET"}, - {STATUS_GRAPHICS_PATH_CONTENT_GEOMETRY_TRANSFORMATION_NOT_PINNED, -EIO, - "STATUS_GRAPHICS_PATH_CONTENT_GEOMETRY_TRANSFORMATION_NOT_PINNED"}, - {STATUS_GRAPHICS_UNKNOWN_CHILD_STATUS, -EIO, - "STATUS_GRAPHICS_UNKNOWN_CHILD_STATUS"}, - {STATUS_GRAPHICS_LEADLINK_START_DEFERRED, -EIO, - "STATUS_GRAPHICS_LEADLINK_START_DEFERRED"}, - {STATUS_GRAPHICS_POLLING_TOO_FREQUENTLY, -EIO, - "STATUS_GRAPHICS_POLLING_TOO_FREQUENTLY"}, - {STATUS_GRAPHICS_START_DEFERRED, -EIO, - "STATUS_GRAPHICS_START_DEFERRED"}, - {STATUS_NDIS_INDICATION_REQUIRED, -EIO, - "STATUS_NDIS_INDICATION_REQUIRED"}, - {STATUS_GUARD_PAGE_VIOLATION, -EIO, "STATUS_GUARD_PAGE_VIOLATION"}, - {STATUS_DATATYPE_MISALIGNMENT, -EIO, "STATUS_DATATYPE_MISALIGNMENT"}, - {STATUS_BREAKPOINT, -EIO, "STATUS_BREAKPOINT"}, - {STATUS_SINGLE_STEP, -EIO, "STATUS_SINGLE_STEP"}, - {STATUS_BUFFER_OVERFLOW, -E2BIG, "STATUS_BUFFER_OVERFLOW"}, - {STATUS_NO_MORE_FILES, -ENODATA, "STATUS_NO_MORE_FILES"}, - {STATUS_WAKE_SYSTEM_DEBUGGER, -EIO, "STATUS_WAKE_SYSTEM_DEBUGGER"}, - {STATUS_HANDLES_CLOSED, -EIO, "STATUS_HANDLES_CLOSED"}, - {STATUS_NO_INHERITANCE, -EIO, "STATUS_NO_INHERITANCE"}, - {STATUS_GUID_SUBSTITUTION_MADE, -EIO, "STATUS_GUID_SUBSTITUTION_MADE"}, - {STATUS_PARTIAL_COPY, -EIO, "STATUS_PARTIAL_COPY"}, - {STATUS_DEVICE_PAPER_EMPTY, -EIO, "STATUS_DEVICE_PAPER_EMPTY"}, - {STATUS_DEVICE_POWERED_OFF, -EIO, "STATUS_DEVICE_POWERED_OFF"}, - {STATUS_DEVICE_OFF_LINE, -EIO, "STATUS_DEVICE_OFF_LINE"}, - {STATUS_DEVICE_BUSY, -EBUSY, "STATUS_DEVICE_BUSY"}, - {STATUS_NO_MORE_EAS, -EIO, "STATUS_NO_MORE_EAS"}, - {STATUS_INVALID_EA_NAME, -EINVAL, "STATUS_INVALID_EA_NAME"}, - {STATUS_EA_LIST_INCONSISTENT, -EIO, "STATUS_EA_LIST_INCONSISTENT"}, - {STATUS_INVALID_EA_FLAG, -EINVAL, "STATUS_INVALID_EA_FLAG"}, - {STATUS_VERIFY_REQUIRED, -EIO, "STATUS_VERIFY_REQUIRED"}, - {STATUS_EXTRANEOUS_INFORMATION, -EIO, "STATUS_EXTRANEOUS_INFORMATION"}, - {STATUS_RXACT_COMMIT_NECESSARY, -EIO, "STATUS_RXACT_COMMIT_NECESSARY"}, - {STATUS_NO_MORE_ENTRIES, -EIO, "STATUS_NO_MORE_ENTRIES"}, - {STATUS_FILEMARK_DETECTED, -EIO, "STATUS_FILEMARK_DETECTED"}, - {STATUS_MEDIA_CHANGED, -EIO, "STATUS_MEDIA_CHANGED"}, - {STATUS_BUS_RESET, -EIO, "STATUS_BUS_RESET"}, - {STATUS_END_OF_MEDIA, -EIO, "STATUS_END_OF_MEDIA"}, - {STATUS_BEGINNING_OF_MEDIA, -EIO, "STATUS_BEGINNING_OF_MEDIA"}, - {STATUS_MEDIA_CHECK, -EIO, "STATUS_MEDIA_CHECK"}, - {STATUS_SETMARK_DETECTED, -EIO, "STATUS_SETMARK_DETECTED"}, - {STATUS_NO_DATA_DETECTED, -EIO, "STATUS_NO_DATA_DETECTED"}, - {STATUS_REDIRECTOR_HAS_OPEN_HANDLES, -EIO, - "STATUS_REDIRECTOR_HAS_OPEN_HANDLES"}, - {STATUS_SERVER_HAS_OPEN_HANDLES, -EIO, - "STATUS_SERVER_HAS_OPEN_HANDLES"}, - {STATUS_ALREADY_DISCONNECTED, -EIO, "STATUS_ALREADY_DISCONNECTED"}, - {STATUS_LONGJUMP, -EIO, "STATUS_LONGJUMP"}, - {STATUS_CLEANER_CARTRIDGE_INSTALLED, -EIO, - "STATUS_CLEANER_CARTRIDGE_INSTALLED"}, - {STATUS_PLUGPLAY_QUERY_VETOED, -EIO, "STATUS_PLUGPLAY_QUERY_VETOED"}, - {STATUS_UNWIND_CONSOLIDATE, -EIO, "STATUS_UNWIND_CONSOLIDATE"}, - {STATUS_REGISTRY_HIVE_RECOVERED, -EIO, - "STATUS_REGISTRY_HIVE_RECOVERED"}, - {STATUS_DLL_MIGHT_BE_INSECURE, -EIO, "STATUS_DLL_MIGHT_BE_INSECURE"}, - {STATUS_DLL_MIGHT_BE_INCOMPATIBLE, -EIO, - "STATUS_DLL_MIGHT_BE_INCOMPATIBLE"}, - {STATUS_STOPPED_ON_SYMLINK, -EOPNOTSUPP, "STATUS_STOPPED_ON_SYMLINK"}, - {STATUS_IO_REPARSE_TAG_NOT_HANDLED, -EOPNOTSUPP, - "STATUS_REPARSE_NOT_HANDLED"}, - {STATUS_DEVICE_REQUIRES_CLEANING, -EIO, - "STATUS_DEVICE_REQUIRES_CLEANING"}, - {STATUS_DEVICE_DOOR_OPEN, -EIO, "STATUS_DEVICE_DOOR_OPEN"}, - {STATUS_DATA_LOST_REPAIR, -EIO, "STATUS_DATA_LOST_REPAIR"}, - {DBG_EXCEPTION_NOT_HANDLED, -EIO, "DBG_EXCEPTION_NOT_HANDLED"}, - {STATUS_CLUSTER_NODE_ALREADY_UP, -EIO, - "STATUS_CLUSTER_NODE_ALREADY_UP"}, - {STATUS_CLUSTER_NODE_ALREADY_DOWN, -EIO, - "STATUS_CLUSTER_NODE_ALREADY_DOWN"}, - {STATUS_CLUSTER_NETWORK_ALREADY_ONLINE, -EIO, - "STATUS_CLUSTER_NETWORK_ALREADY_ONLINE"}, - {STATUS_CLUSTER_NETWORK_ALREADY_OFFLINE, -EIO, - "STATUS_CLUSTER_NETWORK_ALREADY_OFFLINE"}, - {STATUS_CLUSTER_NODE_ALREADY_MEMBER, -EIO, - "STATUS_CLUSTER_NODE_ALREADY_MEMBER"}, - {STATUS_COULD_NOT_RESIZE_LOG, -EIO, "STATUS_COULD_NOT_RESIZE_LOG"}, - {STATUS_NO_TXF_METADATA, -EIO, "STATUS_NO_TXF_METADATA"}, - {STATUS_CANT_RECOVER_WITH_HANDLE_OPEN, -EIO, - "STATUS_CANT_RECOVER_WITH_HANDLE_OPEN"}, - {STATUS_TXF_METADATA_ALREADY_PRESENT, -EIO, - "STATUS_TXF_METADATA_ALREADY_PRESENT"}, - {STATUS_TRANSACTION_SCOPE_CALLBACKS_NOT_SET, -EIO, - "STATUS_TRANSACTION_SCOPE_CALLBACKS_NOT_SET"}, - {STATUS_VIDEO_HUNG_DISPLAY_DRIVER_THREAD_RECOVERED, -EIO, - "STATUS_VIDEO_HUNG_DISPLAY_DRIVER_THREAD_RECOVERED"}, - {STATUS_FLT_BUFFER_TOO_SMALL, -ENOBUFS, "STATUS_FLT_BUFFER_TOO_SMALL"}, - {STATUS_FVE_PARTIAL_METADATA, -EIO, "STATUS_FVE_PARTIAL_METADATA"}, - {STATUS_UNSUCCESSFUL, -EIO, "STATUS_UNSUCCESSFUL"}, - {STATUS_NOT_IMPLEMENTED, -EOPNOTSUPP, "STATUS_NOT_IMPLEMENTED"}, - {STATUS_INVALID_INFO_CLASS, -EIO, "STATUS_INVALID_INFO_CLASS"}, - {STATUS_INFO_LENGTH_MISMATCH, -EIO, "STATUS_INFO_LENGTH_MISMATCH"}, - {STATUS_ACCESS_VIOLATION, -EACCES, "STATUS_ACCESS_VIOLATION"}, - {STATUS_IN_PAGE_ERROR, -EFAULT, "STATUS_IN_PAGE_ERROR"}, - {STATUS_PAGEFILE_QUOTA, -EDQUOT, "STATUS_PAGEFILE_QUOTA"}, - {STATUS_INVALID_HANDLE, -EBADF, "STATUS_INVALID_HANDLE"}, - {STATUS_BAD_INITIAL_STACK, -EIO, "STATUS_BAD_INITIAL_STACK"}, - {STATUS_BAD_INITIAL_PC, -EIO, "STATUS_BAD_INITIAL_PC"}, - {STATUS_INVALID_CID, -EIO, "STATUS_INVALID_CID"}, - {STATUS_TIMER_NOT_CANCELED, -EIO, "STATUS_TIMER_NOT_CANCELED"}, - {STATUS_INVALID_PARAMETER, -EINVAL, "STATUS_INVALID_PARAMETER"}, - {STATUS_NO_SUCH_DEVICE, -ENODEV, "STATUS_NO_SUCH_DEVICE"}, - {STATUS_NO_SUCH_FILE, -ENOENT, "STATUS_NO_SUCH_FILE"}, - {STATUS_INVALID_DEVICE_REQUEST, -EOPNOTSUPP, "STATUS_INVALID_DEVICE_REQUEST"}, - {STATUS_END_OF_FILE, -ENODATA, "STATUS_END_OF_FILE"}, - {STATUS_WRONG_VOLUME, -EIO, "STATUS_WRONG_VOLUME"}, - {STATUS_NO_MEDIA_IN_DEVICE, -EIO, "STATUS_NO_MEDIA_IN_DEVICE"}, - {STATUS_UNRECOGNIZED_MEDIA, -EIO, "STATUS_UNRECOGNIZED_MEDIA"}, - {STATUS_NONEXISTENT_SECTOR, -EIO, "STATUS_NONEXISTENT_SECTOR"}, - {STATUS_MORE_PROCESSING_REQUIRED, -EIO, - "STATUS_MORE_PROCESSING_REQUIRED"}, - {STATUS_NO_MEMORY, -EREMOTEIO, "STATUS_NO_MEMORY"}, - {STATUS_CONFLICTING_ADDRESSES, -EADDRINUSE, - "STATUS_CONFLICTING_ADDRESSES"}, - {STATUS_NOT_MAPPED_VIEW, -EIO, "STATUS_NOT_MAPPED_VIEW"}, - {STATUS_UNABLE_TO_FREE_VM, -EIO, "STATUS_UNABLE_TO_FREE_VM"}, - {STATUS_UNABLE_TO_DELETE_SECTION, -EIO, - "STATUS_UNABLE_TO_DELETE_SECTION"}, - {STATUS_INVALID_SYSTEM_SERVICE, -EIO, "STATUS_INVALID_SYSTEM_SERVICE"}, - {STATUS_ILLEGAL_INSTRUCTION, -EIO, "STATUS_ILLEGAL_INSTRUCTION"}, - {STATUS_INVALID_LOCK_SEQUENCE, -EIO, "STATUS_INVALID_LOCK_SEQUENCE"}, - {STATUS_INVALID_VIEW_SIZE, -EIO, "STATUS_INVALID_VIEW_SIZE"}, - {STATUS_INVALID_FILE_FOR_SECTION, -EIO, - "STATUS_INVALID_FILE_FOR_SECTION"}, - {STATUS_ALREADY_COMMITTED, -EIO, "STATUS_ALREADY_COMMITTED"}, - {STATUS_ACCESS_DENIED, -EACCES, "STATUS_ACCESS_DENIED"}, - {STATUS_BUFFER_TOO_SMALL, -EIO, "STATUS_BUFFER_TOO_SMALL"}, - {STATUS_OBJECT_TYPE_MISMATCH, -EIO, "STATUS_OBJECT_TYPE_MISMATCH"}, - {STATUS_NONCONTINUABLE_EXCEPTION, -EIO, - "STATUS_NONCONTINUABLE_EXCEPTION"}, - {STATUS_INVALID_DISPOSITION, -EIO, "STATUS_INVALID_DISPOSITION"}, - {STATUS_UNWIND, -EIO, "STATUS_UNWIND"}, - {STATUS_BAD_STACK, -EIO, "STATUS_BAD_STACK"}, - {STATUS_INVALID_UNWIND_TARGET, -EIO, "STATUS_INVALID_UNWIND_TARGET"}, - {STATUS_NOT_LOCKED, -EIO, "STATUS_NOT_LOCKED"}, - {STATUS_PARITY_ERROR, -EIO, "STATUS_PARITY_ERROR"}, - {STATUS_UNABLE_TO_DECOMMIT_VM, -EIO, "STATUS_UNABLE_TO_DECOMMIT_VM"}, - {STATUS_NOT_COMMITTED, -EIO, "STATUS_NOT_COMMITTED"}, - {STATUS_INVALID_PORT_ATTRIBUTES, -EIO, - "STATUS_INVALID_PORT_ATTRIBUTES"}, - {STATUS_PORT_MESSAGE_TOO_LONG, -EIO, "STATUS_PORT_MESSAGE_TOO_LONG"}, - {STATUS_INVALID_PARAMETER_MIX, -EINVAL, "STATUS_INVALID_PARAMETER_MIX"}, - {STATUS_INVALID_QUOTA_LOWER, -EIO, "STATUS_INVALID_QUOTA_LOWER"}, - {STATUS_DISK_CORRUPT_ERROR, -EIO, "STATUS_DISK_CORRUPT_ERROR"}, - {STATUS_OBJECT_NAME_INVALID, -ENOENT, "STATUS_OBJECT_NAME_INVALID"}, - {STATUS_OBJECT_NAME_NOT_FOUND, -ENOENT, "STATUS_OBJECT_NAME_NOT_FOUND"}, - {STATUS_OBJECT_NAME_COLLISION, -EEXIST, "STATUS_OBJECT_NAME_COLLISION"}, - {STATUS_PORT_DISCONNECTED, -EIO, "STATUS_PORT_DISCONNECTED"}, - {STATUS_DEVICE_ALREADY_ATTACHED, -EIO, - "STATUS_DEVICE_ALREADY_ATTACHED"}, - {STATUS_OBJECT_PATH_INVALID, -ENOTDIR, "STATUS_OBJECT_PATH_INVALID"}, - {STATUS_OBJECT_PATH_NOT_FOUND, -ENOENT, "STATUS_OBJECT_PATH_NOT_FOUND"}, - {STATUS_OBJECT_PATH_SYNTAX_BAD, -EIO, "STATUS_OBJECT_PATH_SYNTAX_BAD"}, - {STATUS_DATA_OVERRUN, -EIO, "STATUS_DATA_OVERRUN"}, - {STATUS_DATA_LATE_ERROR, -EIO, "STATUS_DATA_LATE_ERROR"}, - {STATUS_DATA_ERROR, -EIO, "STATUS_DATA_ERROR"}, - {STATUS_CRC_ERROR, -EIO, "STATUS_CRC_ERROR"}, - {STATUS_SECTION_TOO_BIG, -EIO, "STATUS_SECTION_TOO_BIG"}, - {STATUS_PORT_CONNECTION_REFUSED, -ECONNREFUSED, - "STATUS_PORT_CONNECTION_REFUSED"}, - {STATUS_INVALID_PORT_HANDLE, -EIO, "STATUS_INVALID_PORT_HANDLE"}, - {STATUS_SHARING_VIOLATION, -EBUSY, "STATUS_SHARING_VIOLATION"}, - {STATUS_QUOTA_EXCEEDED, -EDQUOT, "STATUS_QUOTA_EXCEEDED"}, - {STATUS_INVALID_PAGE_PROTECTION, -EIO, - "STATUS_INVALID_PAGE_PROTECTION"}, - {STATUS_MUTANT_NOT_OWNED, -EIO, "STATUS_MUTANT_NOT_OWNED"}, - {STATUS_SEMAPHORE_LIMIT_EXCEEDED, -EIO, - "STATUS_SEMAPHORE_LIMIT_EXCEEDED"}, - {STATUS_PORT_ALREADY_SET, -EIO, "STATUS_PORT_ALREADY_SET"}, - {STATUS_SECTION_NOT_IMAGE, -EIO, "STATUS_SECTION_NOT_IMAGE"}, - {STATUS_SUSPEND_COUNT_EXCEEDED, -EIO, "STATUS_SUSPEND_COUNT_EXCEEDED"}, - {STATUS_THREAD_IS_TERMINATING, -EIO, "STATUS_THREAD_IS_TERMINATING"}, - {STATUS_BAD_WORKING_SET_LIMIT, -EIO, "STATUS_BAD_WORKING_SET_LIMIT"}, - {STATUS_INCOMPATIBLE_FILE_MAP, -EIO, "STATUS_INCOMPATIBLE_FILE_MAP"}, - {STATUS_SECTION_PROTECTION, -EIO, "STATUS_SECTION_PROTECTION"}, - {STATUS_EAS_NOT_SUPPORTED, -EOPNOTSUPP, "STATUS_EAS_NOT_SUPPORTED"}, - {STATUS_EA_TOO_LARGE, -EIO, "STATUS_EA_TOO_LARGE"}, - {STATUS_NONEXISTENT_EA_ENTRY, -EIO, "STATUS_NONEXISTENT_EA_ENTRY"}, - {STATUS_NO_EAS_ON_FILE, -ENODATA, "STATUS_NO_EAS_ON_FILE"}, - {STATUS_EA_CORRUPT_ERROR, -EIO, "STATUS_EA_CORRUPT_ERROR"}, - {STATUS_FILE_LOCK_CONFLICT, -EACCES, "STATUS_FILE_LOCK_CONFLICT"}, - {STATUS_LOCK_NOT_GRANTED, -EACCES, "STATUS_LOCK_NOT_GRANTED"}, - {STATUS_DELETE_PENDING, -ENOENT, "STATUS_DELETE_PENDING"}, - {STATUS_CTL_FILE_NOT_SUPPORTED, -ENOSYS, - "STATUS_CTL_FILE_NOT_SUPPORTED"}, - {STATUS_UNKNOWN_REVISION, -EIO, "STATUS_UNKNOWN_REVISION"}, - {STATUS_REVISION_MISMATCH, -EIO, "STATUS_REVISION_MISMATCH"}, - {STATUS_INVALID_OWNER, -EIO, "STATUS_INVALID_OWNER"}, - {STATUS_INVALID_PRIMARY_GROUP, -EIO, "STATUS_INVALID_PRIMARY_GROUP"}, - {STATUS_NO_IMPERSONATION_TOKEN, -EIO, "STATUS_NO_IMPERSONATION_TOKEN"}, - {STATUS_CANT_DISABLE_MANDATORY, -EIO, "STATUS_CANT_DISABLE_MANDATORY"}, - {STATUS_NO_LOGON_SERVERS, -EIO, "STATUS_NO_LOGON_SERVERS"}, - {STATUS_NO_SUCH_LOGON_SESSION, -EIO, "STATUS_NO_SUCH_LOGON_SESSION"}, - {STATUS_NO_SUCH_PRIVILEGE, -EIO, "STATUS_NO_SUCH_PRIVILEGE"}, - {STATUS_PRIVILEGE_NOT_HELD, -EIO, "STATUS_PRIVILEGE_NOT_HELD"}, - {STATUS_INVALID_ACCOUNT_NAME, -EIO, "STATUS_INVALID_ACCOUNT_NAME"}, - {STATUS_USER_EXISTS, -EIO, "STATUS_USER_EXISTS"}, - {STATUS_NO_SUCH_USER, -EIO, "STATUS_NO_SUCH_USER"}, - {STATUS_GROUP_EXISTS, -EIO, "STATUS_GROUP_EXISTS"}, - {STATUS_NO_SUCH_GROUP, -EIO, "STATUS_NO_SUCH_GROUP"}, - {STATUS_MEMBER_IN_GROUP, -EIO, "STATUS_MEMBER_IN_GROUP"}, - {STATUS_MEMBER_NOT_IN_GROUP, -EIO, "STATUS_MEMBER_NOT_IN_GROUP"}, - {STATUS_LAST_ADMIN, -EIO, "STATUS_LAST_ADMIN"}, - {STATUS_WRONG_PASSWORD, -EACCES, "STATUS_WRONG_PASSWORD"}, - {STATUS_ILL_FORMED_PASSWORD, -EINVAL, "STATUS_ILL_FORMED_PASSWORD"}, - {STATUS_PASSWORD_RESTRICTION, -EACCES, "STATUS_PASSWORD_RESTRICTION"}, - {STATUS_LOGON_FAILURE, -EACCES, "STATUS_LOGON_FAILURE"}, - {STATUS_ACCOUNT_RESTRICTION, -EACCES, "STATUS_ACCOUNT_RESTRICTION"}, - {STATUS_INVALID_LOGON_HOURS, -EACCES, "STATUS_INVALID_LOGON_HOURS"}, - {STATUS_INVALID_WORKSTATION, -EACCES, "STATUS_INVALID_WORKSTATION"}, - {STATUS_PASSWORD_EXPIRED, -EKEYEXPIRED, "STATUS_PASSWORD_EXPIRED"}, - {STATUS_ACCOUNT_DISABLED, -EKEYREVOKED, "STATUS_ACCOUNT_DISABLED"}, - {STATUS_NONE_MAPPED, -EIO, "STATUS_NONE_MAPPED"}, - {STATUS_TOO_MANY_LUIDS_REQUESTED, -EIO, - "STATUS_TOO_MANY_LUIDS_REQUESTED"}, - {STATUS_LUIDS_EXHAUSTED, -EIO, "STATUS_LUIDS_EXHAUSTED"}, - {STATUS_INVALID_SUB_AUTHORITY, -EIO, "STATUS_INVALID_SUB_AUTHORITY"}, - {STATUS_INVALID_ACL, -EIO, "STATUS_INVALID_ACL"}, - {STATUS_INVALID_SID, -EIO, "STATUS_INVALID_SID"}, - {STATUS_INVALID_SECURITY_DESCR, -EIO, "STATUS_INVALID_SECURITY_DESCR"}, - {STATUS_PROCEDURE_NOT_FOUND, -EIO, "STATUS_PROCEDURE_NOT_FOUND"}, - {STATUS_INVALID_IMAGE_FORMAT, -EIO, "STATUS_INVALID_IMAGE_FORMAT"}, - {STATUS_NO_TOKEN, -EIO, "STATUS_NO_TOKEN"}, - {STATUS_BAD_INHERITANCE_ACL, -EIO, "STATUS_BAD_INHERITANCE_ACL"}, - {STATUS_RANGE_NOT_LOCKED, -EIO, "STATUS_RANGE_NOT_LOCKED"}, - {STATUS_DISK_FULL, -ENOSPC, "STATUS_DISK_FULL"}, - {STATUS_SERVER_DISABLED, -EIO, "STATUS_SERVER_DISABLED"}, - {STATUS_SERVER_NOT_DISABLED, -EIO, "STATUS_SERVER_NOT_DISABLED"}, - {STATUS_TOO_MANY_GUIDS_REQUESTED, -EIO, - "STATUS_TOO_MANY_GUIDS_REQUESTED"}, - {STATUS_GUIDS_EXHAUSTED, -EIO, "STATUS_GUIDS_EXHAUSTED"}, - {STATUS_INVALID_ID_AUTHORITY, -EIO, "STATUS_INVALID_ID_AUTHORITY"}, - {STATUS_AGENTS_EXHAUSTED, -EIO, "STATUS_AGENTS_EXHAUSTED"}, - {STATUS_INVALID_VOLUME_LABEL, -EIO, "STATUS_INVALID_VOLUME_LABEL"}, - {STATUS_SECTION_NOT_EXTENDED, -EIO, "STATUS_SECTION_NOT_EXTENDED"}, - {STATUS_NOT_MAPPED_DATA, -EIO, "STATUS_NOT_MAPPED_DATA"}, - {STATUS_RESOURCE_DATA_NOT_FOUND, -EIO, - "STATUS_RESOURCE_DATA_NOT_FOUND"}, - {STATUS_RESOURCE_TYPE_NOT_FOUND, -EIO, - "STATUS_RESOURCE_TYPE_NOT_FOUND"}, - {STATUS_RESOURCE_NAME_NOT_FOUND, -EIO, - "STATUS_RESOURCE_NAME_NOT_FOUND"}, - {STATUS_ARRAY_BOUNDS_EXCEEDED, -EIO, "STATUS_ARRAY_BOUNDS_EXCEEDED"}, - {STATUS_FLOAT_DENORMAL_OPERAND, -EIO, "STATUS_FLOAT_DENORMAL_OPERAND"}, - {STATUS_FLOAT_DIVIDE_BY_ZERO, -EIO, "STATUS_FLOAT_DIVIDE_BY_ZERO"}, - {STATUS_FLOAT_INEXACT_RESULT, -EIO, "STATUS_FLOAT_INEXACT_RESULT"}, - {STATUS_FLOAT_INVALID_OPERATION, -EIO, - "STATUS_FLOAT_INVALID_OPERATION"}, - {STATUS_FLOAT_OVERFLOW, -EIO, "STATUS_FLOAT_OVERFLOW"}, - {STATUS_FLOAT_STACK_CHECK, -EIO, "STATUS_FLOAT_STACK_CHECK"}, - {STATUS_FLOAT_UNDERFLOW, -EIO, "STATUS_FLOAT_UNDERFLOW"}, - {STATUS_INTEGER_DIVIDE_BY_ZERO, -EIO, "STATUS_INTEGER_DIVIDE_BY_ZERO"}, - {STATUS_INTEGER_OVERFLOW, -EIO, "STATUS_INTEGER_OVERFLOW"}, - {STATUS_PRIVILEGED_INSTRUCTION, -EIO, "STATUS_PRIVILEGED_INSTRUCTION"}, - {STATUS_TOO_MANY_PAGING_FILES, -EIO, "STATUS_TOO_MANY_PAGING_FILES"}, - {STATUS_FILE_INVALID, -EIO, "STATUS_FILE_INVALID"}, - {STATUS_ALLOTTED_SPACE_EXCEEDED, -EIO, - "STATUS_ALLOTTED_SPACE_EXCEEDED"}, - {STATUS_INSUFFICIENT_RESOURCES, -EAGAIN, - "STATUS_INSUFFICIENT_RESOURCES"}, - {STATUS_DFS_EXIT_PATH_FOUND, -EIO, "STATUS_DFS_EXIT_PATH_FOUND"}, - {STATUS_DEVICE_DATA_ERROR, -EIO, "STATUS_DEVICE_DATA_ERROR"}, - {STATUS_DEVICE_NOT_CONNECTED, -EIO, "STATUS_DEVICE_NOT_CONNECTED"}, - {STATUS_DEVICE_POWER_FAILURE, -EIO, "STATUS_DEVICE_POWER_FAILURE"}, - {STATUS_FREE_VM_NOT_AT_BASE, -EIO, "STATUS_FREE_VM_NOT_AT_BASE"}, - {STATUS_MEMORY_NOT_ALLOCATED, -EFAULT, "STATUS_MEMORY_NOT_ALLOCATED"}, - {STATUS_WORKING_SET_QUOTA, -EIO, "STATUS_WORKING_SET_QUOTA"}, - {STATUS_MEDIA_WRITE_PROTECTED, -EROFS, "STATUS_MEDIA_WRITE_PROTECTED"}, - {STATUS_DEVICE_NOT_READY, -EIO, "STATUS_DEVICE_NOT_READY"}, - {STATUS_INVALID_GROUP_ATTRIBUTES, -EIO, - "STATUS_INVALID_GROUP_ATTRIBUTES"}, - {STATUS_BAD_IMPERSONATION_LEVEL, -EIO, - "STATUS_BAD_IMPERSONATION_LEVEL"}, - {STATUS_CANT_OPEN_ANONYMOUS, -EIO, "STATUS_CANT_OPEN_ANONYMOUS"}, - {STATUS_BAD_VALIDATION_CLASS, -EIO, "STATUS_BAD_VALIDATION_CLASS"}, - {STATUS_BAD_TOKEN_TYPE, -EIO, "STATUS_BAD_TOKEN_TYPE"}, - {STATUS_BAD_MASTER_BOOT_RECORD, -EIO, "STATUS_BAD_MASTER_BOOT_RECORD"}, - {STATUS_INSTRUCTION_MISALIGNMENT, -EIO, - "STATUS_INSTRUCTION_MISALIGNMENT"}, - {STATUS_INSTANCE_NOT_AVAILABLE, -EIO, "STATUS_INSTANCE_NOT_AVAILABLE"}, - {STATUS_PIPE_NOT_AVAILABLE, -EIO, "STATUS_PIPE_NOT_AVAILABLE"}, - {STATUS_INVALID_PIPE_STATE, -EIO, "STATUS_INVALID_PIPE_STATE"}, - {STATUS_PIPE_BUSY, -EBUSY, "STATUS_PIPE_BUSY"}, - {STATUS_ILLEGAL_FUNCTION, -EIO, "STATUS_ILLEGAL_FUNCTION"}, - {STATUS_PIPE_DISCONNECTED, -EPIPE, "STATUS_PIPE_DISCONNECTED"}, - {STATUS_PIPE_CLOSING, -EIO, "STATUS_PIPE_CLOSING"}, - {STATUS_PIPE_CONNECTED, -EIO, "STATUS_PIPE_CONNECTED"}, - {STATUS_PIPE_LISTENING, -EIO, "STATUS_PIPE_LISTENING"}, - {STATUS_INVALID_READ_MODE, -EIO, "STATUS_INVALID_READ_MODE"}, - {STATUS_IO_TIMEOUT, -EAGAIN, "STATUS_IO_TIMEOUT"}, - {STATUS_FILE_FORCED_CLOSED, -EIO, "STATUS_FILE_FORCED_CLOSED"}, - {STATUS_PROFILING_NOT_STARTED, -EIO, "STATUS_PROFILING_NOT_STARTED"}, - {STATUS_PROFILING_NOT_STOPPED, -EIO, "STATUS_PROFILING_NOT_STOPPED"}, - {STATUS_COULD_NOT_INTERPRET, -EIO, "STATUS_COULD_NOT_INTERPRET"}, - {STATUS_FILE_IS_A_DIRECTORY, -EISDIR, "STATUS_FILE_IS_A_DIRECTORY"}, - {STATUS_NOT_SUPPORTED, -EOPNOTSUPP, "STATUS_NOT_SUPPORTED"}, - {STATUS_REMOTE_NOT_LISTENING, -EHOSTDOWN, - "STATUS_REMOTE_NOT_LISTENING"}, - {STATUS_DUPLICATE_NAME, -ENOTUNIQ, "STATUS_DUPLICATE_NAME"}, - {STATUS_BAD_NETWORK_PATH, -EINVAL, "STATUS_BAD_NETWORK_PATH"}, - {STATUS_NETWORK_BUSY, -EBUSY, "STATUS_NETWORK_BUSY"}, - {STATUS_DEVICE_DOES_NOT_EXIST, -ENODEV, "STATUS_DEVICE_DOES_NOT_EXIST"}, - {STATUS_TOO_MANY_COMMANDS, -EIO, "STATUS_TOO_MANY_COMMANDS"}, - {STATUS_ADAPTER_HARDWARE_ERROR, -EIO, "STATUS_ADAPTER_HARDWARE_ERROR"}, - {STATUS_INVALID_NETWORK_RESPONSE, -EIO, - "STATUS_INVALID_NETWORK_RESPONSE"}, - {STATUS_UNEXPECTED_NETWORK_ERROR, -EIO, - "STATUS_UNEXPECTED_NETWORK_ERROR"}, - {STATUS_BAD_REMOTE_ADAPTER, -EIO, "STATUS_BAD_REMOTE_ADAPTER"}, - {STATUS_PRINT_QUEUE_FULL, -EIO, "STATUS_PRINT_QUEUE_FULL"}, - {STATUS_NO_SPOOL_SPACE, -EIO, "STATUS_NO_SPOOL_SPACE"}, - {STATUS_PRINT_CANCELLED, -EIO, "STATUS_PRINT_CANCELLED"}, - {STATUS_NETWORK_NAME_DELETED, -EREMCHG, "STATUS_NETWORK_NAME_DELETED"}, - {STATUS_NETWORK_ACCESS_DENIED, -EACCES, "STATUS_NETWORK_ACCESS_DENIED"}, - {STATUS_BAD_DEVICE_TYPE, -EIO, "STATUS_BAD_DEVICE_TYPE"}, - {STATUS_BAD_NETWORK_NAME, -ENOENT, "STATUS_BAD_NETWORK_NAME"}, - {STATUS_TOO_MANY_NAMES, -EIO, "STATUS_TOO_MANY_NAMES"}, - {STATUS_TOO_MANY_SESSIONS, -EIO, "STATUS_TOO_MANY_SESSIONS"}, - {STATUS_SHARING_PAUSED, -EIO, "STATUS_SHARING_PAUSED"}, - {STATUS_REQUEST_NOT_ACCEPTED, -EIO, "STATUS_REQUEST_NOT_ACCEPTED"}, - {STATUS_REDIRECTOR_PAUSED, -EIO, "STATUS_REDIRECTOR_PAUSED"}, - {STATUS_NET_WRITE_FAULT, -EIO, "STATUS_NET_WRITE_FAULT"}, - {STATUS_PROFILING_AT_LIMIT, -EIO, "STATUS_PROFILING_AT_LIMIT"}, - {STATUS_NOT_SAME_DEVICE, -EXDEV, "STATUS_NOT_SAME_DEVICE"}, - {STATUS_FILE_RENAMED, -EIO, "STATUS_FILE_RENAMED"}, - {STATUS_VIRTUAL_CIRCUIT_CLOSED, -EIO, "STATUS_VIRTUAL_CIRCUIT_CLOSED"}, - {STATUS_NO_SECURITY_ON_OBJECT, -EIO, "STATUS_NO_SECURITY_ON_OBJECT"}, - {STATUS_CANT_WAIT, -EIO, "STATUS_CANT_WAIT"}, - {STATUS_PIPE_EMPTY, -EIO, "STATUS_PIPE_EMPTY"}, - {STATUS_CANT_ACCESS_DOMAIN_INFO, -EIO, - "STATUS_CANT_ACCESS_DOMAIN_INFO"}, - {STATUS_CANT_TERMINATE_SELF, -EIO, "STATUS_CANT_TERMINATE_SELF"}, - {STATUS_INVALID_SERVER_STATE, -EIO, "STATUS_INVALID_SERVER_STATE"}, - {STATUS_INVALID_DOMAIN_STATE, -EIO, "STATUS_INVALID_DOMAIN_STATE"}, - {STATUS_INVALID_DOMAIN_ROLE, -EIO, "STATUS_INVALID_DOMAIN_ROLE"}, - {STATUS_NO_SUCH_DOMAIN, -EIO, "STATUS_NO_SUCH_DOMAIN"}, - {STATUS_DOMAIN_EXISTS, -EIO, "STATUS_DOMAIN_EXISTS"}, - {STATUS_DOMAIN_LIMIT_EXCEEDED, -EIO, "STATUS_DOMAIN_LIMIT_EXCEEDED"}, - {STATUS_OPLOCK_NOT_GRANTED, -EIO, "STATUS_OPLOCK_NOT_GRANTED"}, - {STATUS_INVALID_OPLOCK_PROTOCOL, -EIO, - "STATUS_INVALID_OPLOCK_PROTOCOL"}, - {STATUS_INTERNAL_DB_CORRUPTION, -EIO, "STATUS_INTERNAL_DB_CORRUPTION"}, - {STATUS_INTERNAL_ERROR, -EIO, "STATUS_INTERNAL_ERROR"}, - {STATUS_GENERIC_NOT_MAPPED, -EIO, "STATUS_GENERIC_NOT_MAPPED"}, - {STATUS_BAD_DESCRIPTOR_FORMAT, -EIO, "STATUS_BAD_DESCRIPTOR_FORMAT"}, - {STATUS_INVALID_USER_BUFFER, -EIO, "STATUS_INVALID_USER_BUFFER"}, - {STATUS_UNEXPECTED_IO_ERROR, -EIO, "STATUS_UNEXPECTED_IO_ERROR"}, - {STATUS_UNEXPECTED_MM_CREATE_ERR, -EIO, - "STATUS_UNEXPECTED_MM_CREATE_ERR"}, - {STATUS_UNEXPECTED_MM_MAP_ERROR, -EIO, - "STATUS_UNEXPECTED_MM_MAP_ERROR"}, - {STATUS_UNEXPECTED_MM_EXTEND_ERR, -EIO, - "STATUS_UNEXPECTED_MM_EXTEND_ERR"}, - {STATUS_NOT_LOGON_PROCESS, -EIO, "STATUS_NOT_LOGON_PROCESS"}, - {STATUS_LOGON_SESSION_EXISTS, -EIO, "STATUS_LOGON_SESSION_EXISTS"}, - {STATUS_INVALID_PARAMETER_1, -EINVAL, "STATUS_INVALID_PARAMETER_1"}, - {STATUS_INVALID_PARAMETER_2, -EINVAL, "STATUS_INVALID_PARAMETER_2"}, - {STATUS_INVALID_PARAMETER_3, -EINVAL, "STATUS_INVALID_PARAMETER_3"}, - {STATUS_INVALID_PARAMETER_4, -EINVAL, "STATUS_INVALID_PARAMETER_4"}, - {STATUS_INVALID_PARAMETER_5, -EINVAL, "STATUS_INVALID_PARAMETER_5"}, - {STATUS_INVALID_PARAMETER_6, -EINVAL, "STATUS_INVALID_PARAMETER_6"}, - {STATUS_INVALID_PARAMETER_7, -EINVAL, "STATUS_INVALID_PARAMETER_7"}, - {STATUS_INVALID_PARAMETER_8, -EINVAL, "STATUS_INVALID_PARAMETER_8"}, - {STATUS_INVALID_PARAMETER_9, -EINVAL, "STATUS_INVALID_PARAMETER_9"}, - {STATUS_INVALID_PARAMETER_10, -EINVAL, "STATUS_INVALID_PARAMETER_10"}, - {STATUS_INVALID_PARAMETER_11, -EINVAL, "STATUS_INVALID_PARAMETER_11"}, - {STATUS_INVALID_PARAMETER_12, -EINVAL, "STATUS_INVALID_PARAMETER_12"}, - {STATUS_REDIRECTOR_NOT_STARTED, -EIO, "STATUS_REDIRECTOR_NOT_STARTED"}, - {STATUS_REDIRECTOR_STARTED, -EIO, "STATUS_REDIRECTOR_STARTED"}, - {STATUS_STACK_OVERFLOW, -EIO, "STATUS_STACK_OVERFLOW"}, - {STATUS_NO_SUCH_PACKAGE, -EIO, "STATUS_NO_SUCH_PACKAGE"}, - {STATUS_BAD_FUNCTION_TABLE, -EIO, "STATUS_BAD_FUNCTION_TABLE"}, - {STATUS_VARIABLE_NOT_FOUND, -EIO, "STATUS_VARIABLE_NOT_FOUND"}, - {STATUS_DIRECTORY_NOT_EMPTY, -ENOTEMPTY, "STATUS_DIRECTORY_NOT_EMPTY"}, - {STATUS_FILE_CORRUPT_ERROR, -EIO, "STATUS_FILE_CORRUPT_ERROR"}, - {STATUS_NOT_A_DIRECTORY, -ENOTDIR, "STATUS_NOT_A_DIRECTORY"}, - {STATUS_BAD_LOGON_SESSION_STATE, -EIO, - "STATUS_BAD_LOGON_SESSION_STATE"}, - {STATUS_LOGON_SESSION_COLLISION, -EIO, - "STATUS_LOGON_SESSION_COLLISION"}, - {STATUS_NAME_TOO_LONG, -ENAMETOOLONG, "STATUS_NAME_TOO_LONG"}, - {STATUS_FILES_OPEN, -EIO, "STATUS_FILES_OPEN"}, - {STATUS_CONNECTION_IN_USE, -EIO, "STATUS_CONNECTION_IN_USE"}, - {STATUS_MESSAGE_NOT_FOUND, -EIO, "STATUS_MESSAGE_NOT_FOUND"}, - {STATUS_PROCESS_IS_TERMINATING, -EIO, "STATUS_PROCESS_IS_TERMINATING"}, - {STATUS_INVALID_LOGON_TYPE, -EIO, "STATUS_INVALID_LOGON_TYPE"}, - {STATUS_NO_GUID_TRANSLATION, -EIO, "STATUS_NO_GUID_TRANSLATION"}, - {STATUS_CANNOT_IMPERSONATE, -EIO, "STATUS_CANNOT_IMPERSONATE"}, - {STATUS_IMAGE_ALREADY_LOADED, -EIO, "STATUS_IMAGE_ALREADY_LOADED"}, - {STATUS_ABIOS_NOT_PRESENT, -EIO, "STATUS_ABIOS_NOT_PRESENT"}, - {STATUS_ABIOS_LID_NOT_EXIST, -EIO, "STATUS_ABIOS_LID_NOT_EXIST"}, - {STATUS_ABIOS_LID_ALREADY_OWNED, -EIO, - "STATUS_ABIOS_LID_ALREADY_OWNED"}, - {STATUS_ABIOS_NOT_LID_OWNER, -EIO, "STATUS_ABIOS_NOT_LID_OWNER"}, - {STATUS_ABIOS_INVALID_COMMAND, -EIO, "STATUS_ABIOS_INVALID_COMMAND"}, - {STATUS_ABIOS_INVALID_LID, -EIO, "STATUS_ABIOS_INVALID_LID"}, - {STATUS_ABIOS_SELECTOR_NOT_AVAILABLE, -EIO, - "STATUS_ABIOS_SELECTOR_NOT_AVAILABLE"}, - {STATUS_ABIOS_INVALID_SELECTOR, -EIO, "STATUS_ABIOS_INVALID_SELECTOR"}, - {STATUS_NO_LDT, -EIO, "STATUS_NO_LDT"}, - {STATUS_INVALID_LDT_SIZE, -EIO, "STATUS_INVALID_LDT_SIZE"}, - {STATUS_INVALID_LDT_OFFSET, -EIO, "STATUS_INVALID_LDT_OFFSET"}, - {STATUS_INVALID_LDT_DESCRIPTOR, -EIO, "STATUS_INVALID_LDT_DESCRIPTOR"}, - {STATUS_INVALID_IMAGE_NE_FORMAT, -EIO, - "STATUS_INVALID_IMAGE_NE_FORMAT"}, - {STATUS_RXACT_INVALID_STATE, -EIO, "STATUS_RXACT_INVALID_STATE"}, - {STATUS_RXACT_COMMIT_FAILURE, -EIO, "STATUS_RXACT_COMMIT_FAILURE"}, - {STATUS_MAPPED_FILE_SIZE_ZERO, -EIO, "STATUS_MAPPED_FILE_SIZE_ZERO"}, - {STATUS_TOO_MANY_OPENED_FILES, -EMFILE, "STATUS_TOO_MANY_OPENED_FILES"}, - {STATUS_CANCELLED, -EIO, "STATUS_CANCELLED"}, - {STATUS_CANNOT_DELETE, -EACCES, "STATUS_CANNOT_DELETE"}, - {STATUS_INVALID_COMPUTER_NAME, -EIO, "STATUS_INVALID_COMPUTER_NAME"}, - {STATUS_FILE_DELETED, -EIO, "STATUS_FILE_DELETED"}, - {STATUS_SPECIAL_ACCOUNT, -EIO, "STATUS_SPECIAL_ACCOUNT"}, - {STATUS_SPECIAL_GROUP, -EIO, "STATUS_SPECIAL_GROUP"}, - {STATUS_SPECIAL_USER, -EIO, "STATUS_SPECIAL_USER"}, - {STATUS_MEMBERS_PRIMARY_GROUP, -EIO, "STATUS_MEMBERS_PRIMARY_GROUP"}, - {STATUS_FILE_CLOSED, -EBADF, "STATUS_FILE_CLOSED"}, - {STATUS_TOO_MANY_THREADS, -EIO, "STATUS_TOO_MANY_THREADS"}, - {STATUS_THREAD_NOT_IN_PROCESS, -EIO, "STATUS_THREAD_NOT_IN_PROCESS"}, - {STATUS_TOKEN_ALREADY_IN_USE, -EIO, "STATUS_TOKEN_ALREADY_IN_USE"}, - {STATUS_PAGEFILE_QUOTA_EXCEEDED, -EDQUOT, - "STATUS_PAGEFILE_QUOTA_EXCEEDED"}, - {STATUS_COMMITMENT_LIMIT, -EIO, "STATUS_COMMITMENT_LIMIT"}, - {STATUS_INVALID_IMAGE_LE_FORMAT, -EIO, - "STATUS_INVALID_IMAGE_LE_FORMAT"}, - {STATUS_INVALID_IMAGE_NOT_MZ, -EIO, "STATUS_INVALID_IMAGE_NOT_MZ"}, - {STATUS_INVALID_IMAGE_PROTECT, -EIO, "STATUS_INVALID_IMAGE_PROTECT"}, - {STATUS_INVALID_IMAGE_WIN_16, -EIO, "STATUS_INVALID_IMAGE_WIN_16"}, - {STATUS_LOGON_SERVER_CONFLICT, -EIO, "STATUS_LOGON_SERVER_CONFLICT"}, - {STATUS_TIME_DIFFERENCE_AT_DC, -EIO, "STATUS_TIME_DIFFERENCE_AT_DC"}, - {STATUS_SYNCHRONIZATION_REQUIRED, -EIO, - "STATUS_SYNCHRONIZATION_REQUIRED"}, - {STATUS_DLL_NOT_FOUND, -ENOENT, "STATUS_DLL_NOT_FOUND"}, - {STATUS_OPEN_FAILED, -EIO, "STATUS_OPEN_FAILED"}, - {STATUS_IO_PRIVILEGE_FAILED, -EIO, "STATUS_IO_PRIVILEGE_FAILED"}, - {STATUS_ORDINAL_NOT_FOUND, -EIO, "STATUS_ORDINAL_NOT_FOUND"}, - {STATUS_ENTRYPOINT_NOT_FOUND, -EIO, "STATUS_ENTRYPOINT_NOT_FOUND"}, - {STATUS_CONTROL_C_EXIT, -EIO, "STATUS_CONTROL_C_EXIT"}, - {STATUS_LOCAL_DISCONNECT, -EIO, "STATUS_LOCAL_DISCONNECT"}, - {STATUS_REMOTE_DISCONNECT, -ESHUTDOWN, "STATUS_REMOTE_DISCONNECT"}, - {STATUS_REMOTE_RESOURCES, -EIO, "STATUS_REMOTE_RESOURCES"}, - {STATUS_LINK_FAILED, -EXDEV, "STATUS_LINK_FAILED"}, - {STATUS_LINK_TIMEOUT, -ETIMEDOUT, "STATUS_LINK_TIMEOUT"}, - {STATUS_INVALID_CONNECTION, -EIO, "STATUS_INVALID_CONNECTION"}, - {STATUS_INVALID_ADDRESS, -EIO, "STATUS_INVALID_ADDRESS"}, - {STATUS_DLL_INIT_FAILED, -EIO, "STATUS_DLL_INIT_FAILED"}, - {STATUS_MISSING_SYSTEMFILE, -EIO, "STATUS_MISSING_SYSTEMFILE"}, - {STATUS_UNHANDLED_EXCEPTION, -EIO, "STATUS_UNHANDLED_EXCEPTION"}, - {STATUS_APP_INIT_FAILURE, -EIO, "STATUS_APP_INIT_FAILURE"}, - {STATUS_PAGEFILE_CREATE_FAILED, -EIO, "STATUS_PAGEFILE_CREATE_FAILED"}, - {STATUS_NO_PAGEFILE, -EIO, "STATUS_NO_PAGEFILE"}, - {STATUS_INVALID_LEVEL, -EIO, "STATUS_INVALID_LEVEL"}, - {STATUS_WRONG_PASSWORD_CORE, -EIO, "STATUS_WRONG_PASSWORD_CORE"}, - {STATUS_ILLEGAL_FLOAT_CONTEXT, -EIO, "STATUS_ILLEGAL_FLOAT_CONTEXT"}, - {STATUS_PIPE_BROKEN, -EPIPE, "STATUS_PIPE_BROKEN"}, - {STATUS_REGISTRY_CORRUPT, -EIO, "STATUS_REGISTRY_CORRUPT"}, - {STATUS_REGISTRY_IO_FAILED, -EIO, "STATUS_REGISTRY_IO_FAILED"}, - {STATUS_NO_EVENT_PAIR, -EIO, "STATUS_NO_EVENT_PAIR"}, - {STATUS_UNRECOGNIZED_VOLUME, -EIO, "STATUS_UNRECOGNIZED_VOLUME"}, - {STATUS_SERIAL_NO_DEVICE_INITED, -EIO, - "STATUS_SERIAL_NO_DEVICE_INITED"}, - {STATUS_NO_SUCH_ALIAS, -EIO, "STATUS_NO_SUCH_ALIAS"}, - {STATUS_MEMBER_NOT_IN_ALIAS, -EIO, "STATUS_MEMBER_NOT_IN_ALIAS"}, - {STATUS_MEMBER_IN_ALIAS, -EIO, "STATUS_MEMBER_IN_ALIAS"}, - {STATUS_ALIAS_EXISTS, -EIO, "STATUS_ALIAS_EXISTS"}, - {STATUS_LOGON_NOT_GRANTED, -EIO, "STATUS_LOGON_NOT_GRANTED"}, - {STATUS_TOO_MANY_SECRETS, -EIO, "STATUS_TOO_MANY_SECRETS"}, - {STATUS_SECRET_TOO_LONG, -EIO, "STATUS_SECRET_TOO_LONG"}, - {STATUS_INTERNAL_DB_ERROR, -EIO, "STATUS_INTERNAL_DB_ERROR"}, - {STATUS_FULLSCREEN_MODE, -EIO, "STATUS_FULLSCREEN_MODE"}, - {STATUS_TOO_MANY_CONTEXT_IDS, -EIO, "STATUS_TOO_MANY_CONTEXT_IDS"}, - {STATUS_LOGON_TYPE_NOT_GRANTED, -EIO, "STATUS_LOGON_TYPE_NOT_GRANTED"}, - {STATUS_NOT_REGISTRY_FILE, -EIO, "STATUS_NOT_REGISTRY_FILE"}, - {STATUS_NT_CROSS_ENCRYPTION_REQUIRED, -EIO, - "STATUS_NT_CROSS_ENCRYPTION_REQUIRED"}, - {STATUS_DOMAIN_CTRLR_CONFIG_ERROR, -EIO, - "STATUS_DOMAIN_CTRLR_CONFIG_ERROR"}, - {STATUS_FT_MISSING_MEMBER, -EIO, "STATUS_FT_MISSING_MEMBER"}, - {STATUS_ILL_FORMED_SERVICE_ENTRY, -EIO, - "STATUS_ILL_FORMED_SERVICE_ENTRY"}, - {STATUS_ILLEGAL_CHARACTER, -EIO, "STATUS_ILLEGAL_CHARACTER"}, - {STATUS_UNMAPPABLE_CHARACTER, -EIO, "STATUS_UNMAPPABLE_CHARACTER"}, - {STATUS_UNDEFINED_CHARACTER, -EIO, "STATUS_UNDEFINED_CHARACTER"}, - {STATUS_FLOPPY_VOLUME, -EIO, "STATUS_FLOPPY_VOLUME"}, - {STATUS_FLOPPY_ID_MARK_NOT_FOUND, -EIO, - "STATUS_FLOPPY_ID_MARK_NOT_FOUND"}, - {STATUS_FLOPPY_WRONG_CYLINDER, -EIO, "STATUS_FLOPPY_WRONG_CYLINDER"}, - {STATUS_FLOPPY_UNKNOWN_ERROR, -EIO, "STATUS_FLOPPY_UNKNOWN_ERROR"}, - {STATUS_FLOPPY_BAD_REGISTERS, -EIO, "STATUS_FLOPPY_BAD_REGISTERS"}, - {STATUS_DISK_RECALIBRATE_FAILED, -EIO, - "STATUS_DISK_RECALIBRATE_FAILED"}, - {STATUS_DISK_OPERATION_FAILED, -EIO, "STATUS_DISK_OPERATION_FAILED"}, - {STATUS_DISK_RESET_FAILED, -EIO, "STATUS_DISK_RESET_FAILED"}, - {STATUS_SHARED_IRQ_BUSY, -EBUSY, "STATUS_SHARED_IRQ_BUSY"}, - {STATUS_FT_ORPHANING, -EIO, "STATUS_FT_ORPHANING"}, - {STATUS_BIOS_FAILED_TO_CONNECT_INTERRUPT, -EIO, - "STATUS_BIOS_FAILED_TO_CONNECT_INTERRUPT"}, - {STATUS_PARTITION_FAILURE, -EIO, "STATUS_PARTITION_FAILURE"}, - {STATUS_INVALID_BLOCK_LENGTH, -EIO, "STATUS_INVALID_BLOCK_LENGTH"}, - {STATUS_DEVICE_NOT_PARTITIONED, -EIO, "STATUS_DEVICE_NOT_PARTITIONED"}, - {STATUS_UNABLE_TO_LOCK_MEDIA, -EIO, "STATUS_UNABLE_TO_LOCK_MEDIA"}, - {STATUS_UNABLE_TO_UNLOAD_MEDIA, -EIO, "STATUS_UNABLE_TO_UNLOAD_MEDIA"}, - {STATUS_EOM_OVERFLOW, -EIO, "STATUS_EOM_OVERFLOW"}, - {STATUS_NO_MEDIA, -EIO, "STATUS_NO_MEDIA"}, - {STATUS_NO_SUCH_MEMBER, -EIO, "STATUS_NO_SUCH_MEMBER"}, - {STATUS_INVALID_MEMBER, -EIO, "STATUS_INVALID_MEMBER"}, - {STATUS_KEY_DELETED, -EIO, "STATUS_KEY_DELETED"}, - {STATUS_NO_LOG_SPACE, -EIO, "STATUS_NO_LOG_SPACE"}, - {STATUS_TOO_MANY_SIDS, -EIO, "STATUS_TOO_MANY_SIDS"}, - {STATUS_LM_CROSS_ENCRYPTION_REQUIRED, -EIO, - "STATUS_LM_CROSS_ENCRYPTION_REQUIRED"}, - {STATUS_KEY_HAS_CHILDREN, -EIO, "STATUS_KEY_HAS_CHILDREN"}, - {STATUS_CHILD_MUST_BE_VOLATILE, -EIO, "STATUS_CHILD_MUST_BE_VOLATILE"}, - {STATUS_DEVICE_CONFIGURATION_ERROR, -EIO, - "STATUS_DEVICE_CONFIGURATION_ERROR"}, - {STATUS_DRIVER_INTERNAL_ERROR, -EIO, "STATUS_DRIVER_INTERNAL_ERROR"}, - {STATUS_INVALID_DEVICE_STATE, -EIO, "STATUS_INVALID_DEVICE_STATE"}, - {STATUS_IO_DEVICE_ERROR, -EIO, "STATUS_IO_DEVICE_ERROR"}, - {STATUS_DEVICE_PROTOCOL_ERROR, -EIO, "STATUS_DEVICE_PROTOCOL_ERROR"}, - {STATUS_BACKUP_CONTROLLER, -EIO, "STATUS_BACKUP_CONTROLLER"}, - {STATUS_LOG_FILE_FULL, -EIO, "STATUS_LOG_FILE_FULL"}, - {STATUS_TOO_LATE, -EIO, "STATUS_TOO_LATE"}, - {STATUS_NO_TRUST_LSA_SECRET, -EIO, "STATUS_NO_TRUST_LSA_SECRET"}, - {STATUS_NO_TRUST_SAM_ACCOUNT, -EIO, "STATUS_NO_TRUST_SAM_ACCOUNT"}, - {STATUS_TRUSTED_DOMAIN_FAILURE, -EIO, "STATUS_TRUSTED_DOMAIN_FAILURE"}, - {STATUS_TRUSTED_RELATIONSHIP_FAILURE, -EIO, - "STATUS_TRUSTED_RELATIONSHIP_FAILURE"}, - {STATUS_EVENTLOG_FILE_CORRUPT, -EIO, "STATUS_EVENTLOG_FILE_CORRUPT"}, - {STATUS_EVENTLOG_CANT_START, -EIO, "STATUS_EVENTLOG_CANT_START"}, - {STATUS_TRUST_FAILURE, -EIO, "STATUS_TRUST_FAILURE"}, - {STATUS_MUTANT_LIMIT_EXCEEDED, -EIO, "STATUS_MUTANT_LIMIT_EXCEEDED"}, - {STATUS_NETLOGON_NOT_STARTED, -EIO, "STATUS_NETLOGON_NOT_STARTED"}, - {STATUS_ACCOUNT_EXPIRED, -EKEYEXPIRED, "STATUS_ACCOUNT_EXPIRED"}, - {STATUS_POSSIBLE_DEADLOCK, -EIO, "STATUS_POSSIBLE_DEADLOCK"}, - {STATUS_NETWORK_CREDENTIAL_CONFLICT, -EIO, - "STATUS_NETWORK_CREDENTIAL_CONFLICT"}, - {STATUS_REMOTE_SESSION_LIMIT, -EIO, "STATUS_REMOTE_SESSION_LIMIT"}, - {STATUS_EVENTLOG_FILE_CHANGED, -EIO, "STATUS_EVENTLOG_FILE_CHANGED"}, - {STATUS_NOLOGON_INTERDOMAIN_TRUST_ACCOUNT, -EIO, - "STATUS_NOLOGON_INTERDOMAIN_TRUST_ACCOUNT"}, - {STATUS_NOLOGON_WORKSTATION_TRUST_ACCOUNT, -EIO, - "STATUS_NOLOGON_WORKSTATION_TRUST_ACCOUNT"}, - {STATUS_NOLOGON_SERVER_TRUST_ACCOUNT, -EIO, - "STATUS_NOLOGON_SERVER_TRUST_ACCOUNT"}, - {STATUS_DOMAIN_TRUST_INCONSISTENT, -EIO, - "STATUS_DOMAIN_TRUST_INCONSISTENT"}, - {STATUS_FS_DRIVER_REQUIRED, -EOPNOTSUPP, "STATUS_FS_DRIVER_REQUIRED"}, - {STATUS_IMAGE_ALREADY_LOADED_AS_DLL, -EIO, - "STATUS_IMAGE_ALREADY_LOADED_AS_DLL"}, - {STATUS_NETWORK_OPEN_RESTRICTION, -EIO, - "STATUS_NETWORK_OPEN_RESTRICTION"}, - {STATUS_NO_USER_SESSION_KEY, -EIO, "STATUS_NO_USER_SESSION_KEY"}, - {STATUS_USER_SESSION_DELETED, -EIO, "STATUS_USER_SESSION_DELETED"}, - {STATUS_RESOURCE_LANG_NOT_FOUND, -EIO, - "STATUS_RESOURCE_LANG_NOT_FOUND"}, - {STATUS_INSUFF_SERVER_RESOURCES, -EIO, - "STATUS_INSUFF_SERVER_RESOURCES"}, - {STATUS_INVALID_BUFFER_SIZE, -EIO, "STATUS_INVALID_BUFFER_SIZE"}, - {STATUS_INVALID_ADDRESS_COMPONENT, -EIO, - "STATUS_INVALID_ADDRESS_COMPONENT"}, - {STATUS_INVALID_ADDRESS_WILDCARD, -EIO, - "STATUS_INVALID_ADDRESS_WILDCARD"}, - {STATUS_TOO_MANY_ADDRESSES, -EIO, "STATUS_TOO_MANY_ADDRESSES"}, - {STATUS_ADDRESS_ALREADY_EXISTS, -EADDRINUSE, - "STATUS_ADDRESS_ALREADY_EXISTS"}, - {STATUS_ADDRESS_CLOSED, -EIO, "STATUS_ADDRESS_CLOSED"}, - {STATUS_CONNECTION_DISCONNECTED, -ECONNABORTED, - "STATUS_CONNECTION_DISCONNECTED"}, - {STATUS_CONNECTION_RESET, -ENETRESET, "STATUS_CONNECTION_RESET"}, - {STATUS_TOO_MANY_NODES, -EIO, "STATUS_TOO_MANY_NODES"}, - {STATUS_TRANSACTION_ABORTED, -EIO, "STATUS_TRANSACTION_ABORTED"}, - {STATUS_TRANSACTION_TIMED_OUT, -EIO, "STATUS_TRANSACTION_TIMED_OUT"}, - {STATUS_TRANSACTION_NO_RELEASE, -EIO, "STATUS_TRANSACTION_NO_RELEASE"}, - {STATUS_TRANSACTION_NO_MATCH, -EIO, "STATUS_TRANSACTION_NO_MATCH"}, - {STATUS_TRANSACTION_RESPONDED, -EIO, "STATUS_TRANSACTION_RESPONDED"}, - {STATUS_TRANSACTION_INVALID_ID, -EIO, "STATUS_TRANSACTION_INVALID_ID"}, - {STATUS_TRANSACTION_INVALID_TYPE, -EIO, - "STATUS_TRANSACTION_INVALID_TYPE"}, - {STATUS_NOT_SERVER_SESSION, -EIO, "STATUS_NOT_SERVER_SESSION"}, - {STATUS_NOT_CLIENT_SESSION, -EIO, "STATUS_NOT_CLIENT_SESSION"}, - {STATUS_CANNOT_LOAD_REGISTRY_FILE, -EIO, - "STATUS_CANNOT_LOAD_REGISTRY_FILE"}, - {STATUS_DEBUG_ATTACH_FAILED, -EIO, "STATUS_DEBUG_ATTACH_FAILED"}, - {STATUS_SYSTEM_PROCESS_TERMINATED, -EIO, - "STATUS_SYSTEM_PROCESS_TERMINATED"}, - {STATUS_DATA_NOT_ACCEPTED, -EIO, "STATUS_DATA_NOT_ACCEPTED"}, - {STATUS_NO_BROWSER_SERVERS_FOUND, -EIO, - "STATUS_NO_BROWSER_SERVERS_FOUND"}, - {STATUS_VDM_HARD_ERROR, -EIO, "STATUS_VDM_HARD_ERROR"}, - {STATUS_DRIVER_CANCEL_TIMEOUT, -EIO, "STATUS_DRIVER_CANCEL_TIMEOUT"}, - {STATUS_REPLY_MESSAGE_MISMATCH, -EIO, "STATUS_REPLY_MESSAGE_MISMATCH"}, - {STATUS_MAPPED_ALIGNMENT, -EIO, "STATUS_MAPPED_ALIGNMENT"}, - {STATUS_IMAGE_CHECKSUM_MISMATCH, -EIO, - "STATUS_IMAGE_CHECKSUM_MISMATCH"}, - {STATUS_LOST_WRITEBEHIND_DATA, -EIO, "STATUS_LOST_WRITEBEHIND_DATA"}, - {STATUS_CLIENT_SERVER_PARAMETERS_INVALID, -EIO, - "STATUS_CLIENT_SERVER_PARAMETERS_INVALID"}, - {STATUS_PASSWORD_MUST_CHANGE, -EIO, "STATUS_PASSWORD_MUST_CHANGE"}, - {STATUS_NOT_FOUND, -ENOENT, "STATUS_NOT_FOUND"}, - {STATUS_NOT_TINY_STREAM, -EIO, "STATUS_NOT_TINY_STREAM"}, - {STATUS_RECOVERY_FAILURE, -EIO, "STATUS_RECOVERY_FAILURE"}, - {STATUS_STACK_OVERFLOW_READ, -EIO, "STATUS_STACK_OVERFLOW_READ"}, - {STATUS_FAIL_CHECK, -EIO, "STATUS_FAIL_CHECK"}, - {STATUS_DUPLICATE_OBJECTID, -EIO, "STATUS_DUPLICATE_OBJECTID"}, - {STATUS_OBJECTID_EXISTS, -EIO, "STATUS_OBJECTID_EXISTS"}, - {STATUS_CONVERT_TO_LARGE, -EIO, "STATUS_CONVERT_TO_LARGE"}, - {STATUS_RETRY, -EAGAIN, "STATUS_RETRY"}, - {STATUS_FOUND_OUT_OF_SCOPE, -EIO, "STATUS_FOUND_OUT_OF_SCOPE"}, - {STATUS_ALLOCATE_BUCKET, -EIO, "STATUS_ALLOCATE_BUCKET"}, - {STATUS_PROPSET_NOT_FOUND, -EIO, "STATUS_PROPSET_NOT_FOUND"}, - {STATUS_MARSHALL_OVERFLOW, -EIO, "STATUS_MARSHALL_OVERFLOW"}, - {STATUS_INVALID_VARIANT, -EIO, "STATUS_INVALID_VARIANT"}, - {STATUS_DOMAIN_CONTROLLER_NOT_FOUND, -EIO, - "STATUS_DOMAIN_CONTROLLER_NOT_FOUND"}, - {STATUS_ACCOUNT_LOCKED_OUT, -EACCES, "STATUS_ACCOUNT_LOCKED_OUT"}, - {STATUS_HANDLE_NOT_CLOSABLE, -EIO, "STATUS_HANDLE_NOT_CLOSABLE"}, - {STATUS_CONNECTION_REFUSED, -EIO, "STATUS_CONNECTION_REFUSED"}, - {STATUS_GRACEFUL_DISCONNECT, -EIO, "STATUS_GRACEFUL_DISCONNECT"}, - {STATUS_ADDRESS_ALREADY_ASSOCIATED, -EIO, - "STATUS_ADDRESS_ALREADY_ASSOCIATED"}, - {STATUS_ADDRESS_NOT_ASSOCIATED, -EIO, "STATUS_ADDRESS_NOT_ASSOCIATED"}, - {STATUS_CONNECTION_INVALID, -EIO, "STATUS_CONNECTION_INVALID"}, - {STATUS_CONNECTION_ACTIVE, -EIO, "STATUS_CONNECTION_ACTIVE"}, - {STATUS_NETWORK_UNREACHABLE, -ENETUNREACH, - "STATUS_NETWORK_UNREACHABLE"}, - {STATUS_HOST_UNREACHABLE, -EHOSTDOWN, "STATUS_HOST_UNREACHABLE"}, - {STATUS_PROTOCOL_UNREACHABLE, -ENETUNREACH, - "STATUS_PROTOCOL_UNREACHABLE"}, - {STATUS_PORT_UNREACHABLE, -ENETUNREACH, "STATUS_PORT_UNREACHABLE"}, - {STATUS_REQUEST_ABORTED, -EIO, "STATUS_REQUEST_ABORTED"}, - {STATUS_CONNECTION_ABORTED, -ECONNABORTED, "STATUS_CONNECTION_ABORTED"}, - {STATUS_BAD_COMPRESSION_BUFFER, -EIO, "STATUS_BAD_COMPRESSION_BUFFER"}, - {STATUS_USER_MAPPED_FILE, -EIO, "STATUS_USER_MAPPED_FILE"}, - {STATUS_AUDIT_FAILED, -EIO, "STATUS_AUDIT_FAILED"}, - {STATUS_TIMER_RESOLUTION_NOT_SET, -EIO, - "STATUS_TIMER_RESOLUTION_NOT_SET"}, - {STATUS_CONNECTION_COUNT_LIMIT, -EIO, "STATUS_CONNECTION_COUNT_LIMIT"}, - {STATUS_LOGIN_TIME_RESTRICTION, -EACCES, - "STATUS_LOGIN_TIME_RESTRICTION"}, - {STATUS_LOGIN_WKSTA_RESTRICTION, -EACCES, - "STATUS_LOGIN_WKSTA_RESTRICTION"}, - {STATUS_IMAGE_MP_UP_MISMATCH, -EIO, "STATUS_IMAGE_MP_UP_MISMATCH"}, - {STATUS_INSUFFICIENT_LOGON_INFO, -EIO, - "STATUS_INSUFFICIENT_LOGON_INFO"}, - {STATUS_BAD_DLL_ENTRYPOINT, -EIO, "STATUS_BAD_DLL_ENTRYPOINT"}, - {STATUS_BAD_SERVICE_ENTRYPOINT, -EIO, "STATUS_BAD_SERVICE_ENTRYPOINT"}, - {STATUS_LPC_REPLY_LOST, -EIO, "STATUS_LPC_REPLY_LOST"}, - {STATUS_IP_ADDRESS_CONFLICT1, -EIO, "STATUS_IP_ADDRESS_CONFLICT1"}, - {STATUS_IP_ADDRESS_CONFLICT2, -EIO, "STATUS_IP_ADDRESS_CONFLICT2"}, - {STATUS_REGISTRY_QUOTA_LIMIT, -EDQUOT, "STATUS_REGISTRY_QUOTA_LIMIT"}, - {STATUS_PATH_NOT_COVERED, -EREMOTE, "STATUS_PATH_NOT_COVERED"}, - {STATUS_NO_CALLBACK_ACTIVE, -EIO, "STATUS_NO_CALLBACK_ACTIVE"}, - {STATUS_LICENSE_QUOTA_EXCEEDED, -EACCES, - "STATUS_LICENSE_QUOTA_EXCEEDED"}, - {STATUS_PWD_TOO_SHORT, -EIO, "STATUS_PWD_TOO_SHORT"}, - {STATUS_PWD_TOO_RECENT, -EIO, "STATUS_PWD_TOO_RECENT"}, - {STATUS_PWD_HISTORY_CONFLICT, -EIO, "STATUS_PWD_HISTORY_CONFLICT"}, - {STATUS_PLUGPLAY_NO_DEVICE, -EIO, "STATUS_PLUGPLAY_NO_DEVICE"}, - {STATUS_UNSUPPORTED_COMPRESSION, -EIO, - "STATUS_UNSUPPORTED_COMPRESSION"}, - {STATUS_INVALID_HW_PROFILE, -EIO, "STATUS_INVALID_HW_PROFILE"}, - {STATUS_INVALID_PLUGPLAY_DEVICE_PATH, -EIO, - "STATUS_INVALID_PLUGPLAY_DEVICE_PATH"}, - {STATUS_DRIVER_ORDINAL_NOT_FOUND, -EIO, - "STATUS_DRIVER_ORDINAL_NOT_FOUND"}, - {STATUS_DRIVER_ENTRYPOINT_NOT_FOUND, -EIO, - "STATUS_DRIVER_ENTRYPOINT_NOT_FOUND"}, - {STATUS_RESOURCE_NOT_OWNED, -EIO, "STATUS_RESOURCE_NOT_OWNED"}, - {STATUS_TOO_MANY_LINKS, -EMLINK, "STATUS_TOO_MANY_LINKS"}, - {STATUS_QUOTA_LIST_INCONSISTENT, -EIO, - "STATUS_QUOTA_LIST_INCONSISTENT"}, - {STATUS_FILE_IS_OFFLINE, -EIO, "STATUS_FILE_IS_OFFLINE"}, - {STATUS_EVALUATION_EXPIRATION, -EIO, "STATUS_EVALUATION_EXPIRATION"}, - {STATUS_ILLEGAL_DLL_RELOCATION, -EIO, "STATUS_ILLEGAL_DLL_RELOCATION"}, - {STATUS_LICENSE_VIOLATION, -EIO, "STATUS_LICENSE_VIOLATION"}, - {STATUS_DLL_INIT_FAILED_LOGOFF, -EIO, "STATUS_DLL_INIT_FAILED_LOGOFF"}, - {STATUS_DRIVER_UNABLE_TO_LOAD, -EIO, "STATUS_DRIVER_UNABLE_TO_LOAD"}, - {STATUS_DFS_UNAVAILABLE, -EIO, "STATUS_DFS_UNAVAILABLE"}, - {STATUS_VOLUME_DISMOUNTED, -EIO, "STATUS_VOLUME_DISMOUNTED"}, - {STATUS_WX86_INTERNAL_ERROR, -EIO, "STATUS_WX86_INTERNAL_ERROR"}, - {STATUS_WX86_FLOAT_STACK_CHECK, -EIO, "STATUS_WX86_FLOAT_STACK_CHECK"}, - {STATUS_VALIDATE_CONTINUE, -EIO, "STATUS_VALIDATE_CONTINUE"}, - {STATUS_NO_MATCH, -EIO, "STATUS_NO_MATCH"}, - {STATUS_NO_MORE_MATCHES, -EIO, "STATUS_NO_MORE_MATCHES"}, - {STATUS_NOT_A_REPARSE_POINT, -EIO, "STATUS_NOT_A_REPARSE_POINT"}, - {STATUS_IO_REPARSE_TAG_INVALID, -EIO, "STATUS_IO_REPARSE_TAG_INVALID"}, - {STATUS_IO_REPARSE_TAG_MISMATCH, -EIO, - "STATUS_IO_REPARSE_TAG_MISMATCH"}, - {STATUS_IO_REPARSE_DATA_INVALID, -EIO, - "STATUS_IO_REPARSE_DATA_INVALID"}, - {STATUS_IO_REPARSE_TAG_NOT_HANDLED, -EIO, - "STATUS_IO_REPARSE_TAG_NOT_HANDLED"}, - {STATUS_REPARSE_POINT_NOT_RESOLVED, -EIO, - "STATUS_REPARSE_POINT_NOT_RESOLVED"}, - {STATUS_DIRECTORY_IS_A_REPARSE_POINT, -EIO, - "STATUS_DIRECTORY_IS_A_REPARSE_POINT"}, - {STATUS_RANGE_LIST_CONFLICT, -EIO, "STATUS_RANGE_LIST_CONFLICT"}, - {STATUS_SOURCE_ELEMENT_EMPTY, -EIO, "STATUS_SOURCE_ELEMENT_EMPTY"}, - {STATUS_DESTINATION_ELEMENT_FULL, -EIO, - "STATUS_DESTINATION_ELEMENT_FULL"}, - {STATUS_ILLEGAL_ELEMENT_ADDRESS, -EIO, - "STATUS_ILLEGAL_ELEMENT_ADDRESS"}, - {STATUS_MAGAZINE_NOT_PRESENT, -EIO, "STATUS_MAGAZINE_NOT_PRESENT"}, - {STATUS_REINITIALIZATION_NEEDED, -EIO, - "STATUS_REINITIALIZATION_NEEDED"}, - {STATUS_ENCRYPTION_FAILED, -EIO, "STATUS_ENCRYPTION_FAILED"}, - {STATUS_DECRYPTION_FAILED, -EIO, "STATUS_DECRYPTION_FAILED"}, - {STATUS_RANGE_NOT_FOUND, -EIO, "STATUS_RANGE_NOT_FOUND"}, - {STATUS_NO_RECOVERY_POLICY, -EIO, "STATUS_NO_RECOVERY_POLICY"}, - {STATUS_NO_EFS, -EIO, "STATUS_NO_EFS"}, - {STATUS_WRONG_EFS, -EIO, "STATUS_WRONG_EFS"}, - {STATUS_NO_USER_KEYS, -EIO, "STATUS_NO_USER_KEYS"}, - {STATUS_FILE_NOT_ENCRYPTED, -EIO, "STATUS_FILE_NOT_ENCRYPTED"}, - {STATUS_NOT_EXPORT_FORMAT, -EIO, "STATUS_NOT_EXPORT_FORMAT"}, - {STATUS_FILE_ENCRYPTED, -EIO, "STATUS_FILE_ENCRYPTED"}, - {STATUS_WMI_GUID_NOT_FOUND, -EIO, "STATUS_WMI_GUID_NOT_FOUND"}, - {STATUS_WMI_INSTANCE_NOT_FOUND, -EIO, "STATUS_WMI_INSTANCE_NOT_FOUND"}, - {STATUS_WMI_ITEMID_NOT_FOUND, -EIO, "STATUS_WMI_ITEMID_NOT_FOUND"}, - {STATUS_WMI_TRY_AGAIN, -EIO, "STATUS_WMI_TRY_AGAIN"}, - {STATUS_SHARED_POLICY, -EIO, "STATUS_SHARED_POLICY"}, - {STATUS_POLICY_OBJECT_NOT_FOUND, -EIO, - "STATUS_POLICY_OBJECT_NOT_FOUND"}, - {STATUS_POLICY_ONLY_IN_DS, -EIO, "STATUS_POLICY_ONLY_IN_DS"}, - {STATUS_VOLUME_NOT_UPGRADED, -EIO, "STATUS_VOLUME_NOT_UPGRADED"}, - {STATUS_REMOTE_STORAGE_NOT_ACTIVE, -EIO, - "STATUS_REMOTE_STORAGE_NOT_ACTIVE"}, - {STATUS_REMOTE_STORAGE_MEDIA_ERROR, -EIO, - "STATUS_REMOTE_STORAGE_MEDIA_ERROR"}, - {STATUS_NO_TRACKING_SERVICE, -EIO, "STATUS_NO_TRACKING_SERVICE"}, - {STATUS_SERVER_SID_MISMATCH, -EIO, "STATUS_SERVER_SID_MISMATCH"}, - {STATUS_DS_NO_ATTRIBUTE_OR_VALUE, -EIO, - "STATUS_DS_NO_ATTRIBUTE_OR_VALUE"}, - {STATUS_DS_INVALID_ATTRIBUTE_SYNTAX, -EIO, - "STATUS_DS_INVALID_ATTRIBUTE_SYNTAX"}, - {STATUS_DS_ATTRIBUTE_TYPE_UNDEFINED, -EIO, - "STATUS_DS_ATTRIBUTE_TYPE_UNDEFINED"}, - {STATUS_DS_ATTRIBUTE_OR_VALUE_EXISTS, -EIO, - "STATUS_DS_ATTRIBUTE_OR_VALUE_EXISTS"}, - {STATUS_DS_BUSY, -EBUSY, "STATUS_DS_BUSY"}, - {STATUS_DS_UNAVAILABLE, -EIO, "STATUS_DS_UNAVAILABLE"}, - {STATUS_DS_NO_RIDS_ALLOCATED, -EIO, "STATUS_DS_NO_RIDS_ALLOCATED"}, - {STATUS_DS_NO_MORE_RIDS, -EIO, "STATUS_DS_NO_MORE_RIDS"}, - {STATUS_DS_INCORRECT_ROLE_OWNER, -EIO, - "STATUS_DS_INCORRECT_ROLE_OWNER"}, - {STATUS_DS_RIDMGR_INIT_ERROR, -EIO, "STATUS_DS_RIDMGR_INIT_ERROR"}, - {STATUS_DS_OBJ_CLASS_VIOLATION, -EIO, "STATUS_DS_OBJ_CLASS_VIOLATION"}, - {STATUS_DS_CANT_ON_NON_LEAF, -EIO, "STATUS_DS_CANT_ON_NON_LEAF"}, - {STATUS_DS_CANT_ON_RDN, -EIO, "STATUS_DS_CANT_ON_RDN"}, - {STATUS_DS_CANT_MOD_OBJ_CLASS, -EIO, "STATUS_DS_CANT_MOD_OBJ_CLASS"}, - {STATUS_DS_CROSS_DOM_MOVE_FAILED, -EIO, - "STATUS_DS_CROSS_DOM_MOVE_FAILED"}, - {STATUS_DS_GC_NOT_AVAILABLE, -EIO, "STATUS_DS_GC_NOT_AVAILABLE"}, - {STATUS_DIRECTORY_SERVICE_REQUIRED, -EIO, - "STATUS_DIRECTORY_SERVICE_REQUIRED"}, - {STATUS_REPARSE_ATTRIBUTE_CONFLICT, -EIO, - "STATUS_REPARSE_ATTRIBUTE_CONFLICT"}, - {STATUS_CANT_ENABLE_DENY_ONLY, -EIO, "STATUS_CANT_ENABLE_DENY_ONLY"}, - {STATUS_FLOAT_MULTIPLE_FAULTS, -EIO, "STATUS_FLOAT_MULTIPLE_FAULTS"}, - {STATUS_FLOAT_MULTIPLE_TRAPS, -EIO, "STATUS_FLOAT_MULTIPLE_TRAPS"}, - {STATUS_DEVICE_REMOVED, -EIO, "STATUS_DEVICE_REMOVED"}, - {STATUS_JOURNAL_DELETE_IN_PROGRESS, -EIO, - "STATUS_JOURNAL_DELETE_IN_PROGRESS"}, - {STATUS_JOURNAL_NOT_ACTIVE, -EIO, "STATUS_JOURNAL_NOT_ACTIVE"}, - {STATUS_NOINTERFACE, -EIO, "STATUS_NOINTERFACE"}, - {STATUS_DS_ADMIN_LIMIT_EXCEEDED, -EIO, - "STATUS_DS_ADMIN_LIMIT_EXCEEDED"}, - {STATUS_DRIVER_FAILED_SLEEP, -EIO, "STATUS_DRIVER_FAILED_SLEEP"}, - {STATUS_MUTUAL_AUTHENTICATION_FAILED, -EIO, - "STATUS_MUTUAL_AUTHENTICATION_FAILED"}, - {STATUS_CORRUPT_SYSTEM_FILE, -EIO, "STATUS_CORRUPT_SYSTEM_FILE"}, - {STATUS_DATATYPE_MISALIGNMENT_ERROR, -EIO, - "STATUS_DATATYPE_MISALIGNMENT_ERROR"}, - {STATUS_WMI_READ_ONLY, -EROFS, "STATUS_WMI_READ_ONLY"}, - {STATUS_WMI_SET_FAILURE, -EIO, "STATUS_WMI_SET_FAILURE"}, - {STATUS_COMMITMENT_MINIMUM, -EIO, "STATUS_COMMITMENT_MINIMUM"}, - {STATUS_REG_NAT_CONSUMPTION, -EIO, "STATUS_REG_NAT_CONSUMPTION"}, - {STATUS_TRANSPORT_FULL, -EIO, "STATUS_TRANSPORT_FULL"}, - {STATUS_DS_SAM_INIT_FAILURE, -EIO, "STATUS_DS_SAM_INIT_FAILURE"}, - {STATUS_ONLY_IF_CONNECTED, -EIO, "STATUS_ONLY_IF_CONNECTED"}, - {STATUS_DS_SENSITIVE_GROUP_VIOLATION, -EIO, - "STATUS_DS_SENSITIVE_GROUP_VIOLATION"}, - {STATUS_PNP_RESTART_ENUMERATION, -EIO, - "STATUS_PNP_RESTART_ENUMERATION"}, - {STATUS_JOURNAL_ENTRY_DELETED, -EIO, "STATUS_JOURNAL_ENTRY_DELETED"}, - {STATUS_DS_CANT_MOD_PRIMARYGROUPID, -EIO, - "STATUS_DS_CANT_MOD_PRIMARYGROUPID"}, - {STATUS_SYSTEM_IMAGE_BAD_SIGNATURE, -EIO, - "STATUS_SYSTEM_IMAGE_BAD_SIGNATURE"}, - {STATUS_PNP_REBOOT_REQUIRED, -EIO, "STATUS_PNP_REBOOT_REQUIRED"}, - {STATUS_POWER_STATE_INVALID, -EIO, "STATUS_POWER_STATE_INVALID"}, - {STATUS_DS_INVALID_GROUP_TYPE, -EIO, "STATUS_DS_INVALID_GROUP_TYPE"}, - {STATUS_DS_NO_NEST_GLOBALGROUP_IN_MIXEDDOMAIN, -EIO, - "STATUS_DS_NO_NEST_GLOBALGROUP_IN_MIXEDDOMAIN"}, - {STATUS_DS_NO_NEST_LOCALGROUP_IN_MIXEDDOMAIN, -EIO, - "STATUS_DS_NO_NEST_LOCALGROUP_IN_MIXEDDOMAIN"}, - {STATUS_DS_GLOBAL_CANT_HAVE_LOCAL_MEMBER, -EIO, - "STATUS_DS_GLOBAL_CANT_HAVE_LOCAL_MEMBER"}, - {STATUS_DS_GLOBAL_CANT_HAVE_UNIVERSAL_MEMBER, -EIO, - "STATUS_DS_GLOBAL_CANT_HAVE_UNIVERSAL_MEMBER"}, - {STATUS_DS_UNIVERSAL_CANT_HAVE_LOCAL_MEMBER, -EIO, - "STATUS_DS_UNIVERSAL_CANT_HAVE_LOCAL_MEMBER"}, - {STATUS_DS_GLOBAL_CANT_HAVE_CROSSDOMAIN_MEMBER, -EIO, - "STATUS_DS_GLOBAL_CANT_HAVE_CROSSDOMAIN_MEMBER"}, - {STATUS_DS_LOCAL_CANT_HAVE_CROSSDOMAIN_LOCAL_MEMBER, -EIO, - "STATUS_DS_LOCAL_CANT_HAVE_CROSSDOMAIN_LOCAL_MEMBER"}, - {STATUS_DS_HAVE_PRIMARY_MEMBERS, -EIO, - "STATUS_DS_HAVE_PRIMARY_MEMBERS"}, - {STATUS_WMI_NOT_SUPPORTED, -EOPNOTSUPP, "STATUS_WMI_NOT_SUPPORTED"}, - {STATUS_INSUFFICIENT_POWER, -EIO, "STATUS_INSUFFICIENT_POWER"}, - {STATUS_SAM_NEED_BOOTKEY_PASSWORD, -EIO, - "STATUS_SAM_NEED_BOOTKEY_PASSWORD"}, - {STATUS_SAM_NEED_BOOTKEY_FLOPPY, -EIO, - "STATUS_SAM_NEED_BOOTKEY_FLOPPY"}, - {STATUS_DS_CANT_START, -EIO, "STATUS_DS_CANT_START"}, - {STATUS_DS_INIT_FAILURE, -EIO, "STATUS_DS_INIT_FAILURE"}, - {STATUS_SAM_INIT_FAILURE, -EIO, "STATUS_SAM_INIT_FAILURE"}, - {STATUS_DS_GC_REQUIRED, -EIO, "STATUS_DS_GC_REQUIRED"}, - {STATUS_DS_LOCAL_MEMBER_OF_LOCAL_ONLY, -EIO, - "STATUS_DS_LOCAL_MEMBER_OF_LOCAL_ONLY"}, - {STATUS_DS_NO_FPO_IN_UNIVERSAL_GROUPS, -EIO, - "STATUS_DS_NO_FPO_IN_UNIVERSAL_GROUPS"}, - {STATUS_DS_MACHINE_ACCOUNT_QUOTA_EXCEEDED, -EDQUOT, - "STATUS_DS_MACHINE_ACCOUNT_QUOTA_EXCEEDED"}, - {STATUS_MULTIPLE_FAULT_VIOLATION, -EIO, - "STATUS_MULTIPLE_FAULT_VIOLATION"}, - {STATUS_CURRENT_DOMAIN_NOT_ALLOWED, -EIO, - "STATUS_CURRENT_DOMAIN_NOT_ALLOWED"}, - {STATUS_CANNOT_MAKE, -EIO, "STATUS_CANNOT_MAKE"}, - {STATUS_SYSTEM_SHUTDOWN, -EIO, "STATUS_SYSTEM_SHUTDOWN"}, - {STATUS_DS_INIT_FAILURE_CONSOLE, -EIO, - "STATUS_DS_INIT_FAILURE_CONSOLE"}, - {STATUS_DS_SAM_INIT_FAILURE_CONSOLE, -EIO, - "STATUS_DS_SAM_INIT_FAILURE_CONSOLE"}, - {STATUS_UNFINISHED_CONTEXT_DELETED, -EIO, - "STATUS_UNFINISHED_CONTEXT_DELETED"}, - {STATUS_NO_TGT_REPLY, -EIO, "STATUS_NO_TGT_REPLY"}, - /* Note that ENOATTTR and ENODATA are the same errno */ - {STATUS_OBJECTID_NOT_FOUND, -ENODATA, "STATUS_OBJECTID_NOT_FOUND"}, - {STATUS_NO_IP_ADDRESSES, -EIO, "STATUS_NO_IP_ADDRESSES"}, - {STATUS_WRONG_CREDENTIAL_HANDLE, -EIO, - "STATUS_WRONG_CREDENTIAL_HANDLE"}, - {STATUS_CRYPTO_SYSTEM_INVALID, -EIO, "STATUS_CRYPTO_SYSTEM_INVALID"}, - {STATUS_MAX_REFERRALS_EXCEEDED, -EIO, "STATUS_MAX_REFERRALS_EXCEEDED"}, - {STATUS_MUST_BE_KDC, -EIO, "STATUS_MUST_BE_KDC"}, - {STATUS_STRONG_CRYPTO_NOT_SUPPORTED, -EIO, - "STATUS_STRONG_CRYPTO_NOT_SUPPORTED"}, - {STATUS_TOO_MANY_PRINCIPALS, -EIO, "STATUS_TOO_MANY_PRINCIPALS"}, - {STATUS_NO_PA_DATA, -EIO, "STATUS_NO_PA_DATA"}, - {STATUS_PKINIT_NAME_MISMATCH, -EIO, "STATUS_PKINIT_NAME_MISMATCH"}, - {STATUS_SMARTCARD_LOGON_REQUIRED, -EIO, - "STATUS_SMARTCARD_LOGON_REQUIRED"}, - {STATUS_KDC_INVALID_REQUEST, -EIO, "STATUS_KDC_INVALID_REQUEST"}, - {STATUS_KDC_UNABLE_TO_REFER, -EIO, "STATUS_KDC_UNABLE_TO_REFER"}, - {STATUS_KDC_UNKNOWN_ETYPE, -EIO, "STATUS_KDC_UNKNOWN_ETYPE"}, - {STATUS_SHUTDOWN_IN_PROGRESS, -EIO, "STATUS_SHUTDOWN_IN_PROGRESS"}, - {STATUS_SERVER_SHUTDOWN_IN_PROGRESS, -EIO, - "STATUS_SERVER_SHUTDOWN_IN_PROGRESS"}, - {STATUS_NOT_SUPPORTED_ON_SBS, -EOPNOTSUPP, - "STATUS_NOT_SUPPORTED_ON_SBS"}, - {STATUS_WMI_GUID_DISCONNECTED, -EIO, "STATUS_WMI_GUID_DISCONNECTED"}, - {STATUS_WMI_ALREADY_DISABLED, -EIO, "STATUS_WMI_ALREADY_DISABLED"}, - {STATUS_WMI_ALREADY_ENABLED, -EIO, "STATUS_WMI_ALREADY_ENABLED"}, - {STATUS_MFT_TOO_FRAGMENTED, -EIO, "STATUS_MFT_TOO_FRAGMENTED"}, - {STATUS_COPY_PROTECTION_FAILURE, -EIO, - "STATUS_COPY_PROTECTION_FAILURE"}, - {STATUS_CSS_AUTHENTICATION_FAILURE, -EIO, - "STATUS_CSS_AUTHENTICATION_FAILURE"}, - {STATUS_CSS_KEY_NOT_PRESENT, -EIO, "STATUS_CSS_KEY_NOT_PRESENT"}, - {STATUS_CSS_KEY_NOT_ESTABLISHED, -EIO, - "STATUS_CSS_KEY_NOT_ESTABLISHED"}, - {STATUS_CSS_SCRAMBLED_SECTOR, -EIO, "STATUS_CSS_SCRAMBLED_SECTOR"}, - {STATUS_CSS_REGION_MISMATCH, -EIO, "STATUS_CSS_REGION_MISMATCH"}, - {STATUS_CSS_RESETS_EXHAUSTED, -EIO, "STATUS_CSS_RESETS_EXHAUSTED"}, - {STATUS_PKINIT_FAILURE, -EIO, "STATUS_PKINIT_FAILURE"}, - {STATUS_SMARTCARD_SUBSYSTEM_FAILURE, -EIO, - "STATUS_SMARTCARD_SUBSYSTEM_FAILURE"}, - {STATUS_NO_KERB_KEY, -EIO, "STATUS_NO_KERB_KEY"}, - {STATUS_HOST_DOWN, -EIO, "STATUS_HOST_DOWN"}, - {STATUS_UNSUPPORTED_PREAUTH, -EIO, "STATUS_UNSUPPORTED_PREAUTH"}, - {STATUS_EFS_ALG_BLOB_TOO_BIG, -EIO, "STATUS_EFS_ALG_BLOB_TOO_BIG"}, - {STATUS_PORT_NOT_SET, -EIO, "STATUS_PORT_NOT_SET"}, - {STATUS_DEBUGGER_INACTIVE, -EIO, "STATUS_DEBUGGER_INACTIVE"}, - {STATUS_DS_VERSION_CHECK_FAILURE, -EIO, - "STATUS_DS_VERSION_CHECK_FAILURE"}, - {STATUS_AUDITING_DISABLED, -EIO, "STATUS_AUDITING_DISABLED"}, - {STATUS_PRENT4_MACHINE_ACCOUNT, -EIO, "STATUS_PRENT4_MACHINE_ACCOUNT"}, - {STATUS_DS_AG_CANT_HAVE_UNIVERSAL_MEMBER, -EIO, - "STATUS_DS_AG_CANT_HAVE_UNIVERSAL_MEMBER"}, - {STATUS_INVALID_IMAGE_WIN_32, -EIO, "STATUS_INVALID_IMAGE_WIN_32"}, - {STATUS_INVALID_IMAGE_WIN_64, -EIO, "STATUS_INVALID_IMAGE_WIN_64"}, - {STATUS_BAD_BINDINGS, -EIO, "STATUS_BAD_BINDINGS"}, - {STATUS_NETWORK_SESSION_EXPIRED, -EIO, - "STATUS_NETWORK_SESSION_EXPIRED"}, - {STATUS_APPHELP_BLOCK, -EIO, "STATUS_APPHELP_BLOCK"}, - {STATUS_ALL_SIDS_FILTERED, -EIO, "STATUS_ALL_SIDS_FILTERED"}, - {STATUS_NOT_SAFE_MODE_DRIVER, -EIO, "STATUS_NOT_SAFE_MODE_DRIVER"}, - {STATUS_ACCESS_DISABLED_BY_POLICY_DEFAULT, -EACCES, - "STATUS_ACCESS_DISABLED_BY_POLICY_DEFAULT"}, - {STATUS_ACCESS_DISABLED_BY_POLICY_PATH, -EACCES, - "STATUS_ACCESS_DISABLED_BY_POLICY_PATH"}, - {STATUS_ACCESS_DISABLED_BY_POLICY_PUBLISHER, -EACCES, - "STATUS_ACCESS_DISABLED_BY_POLICY_PUBLISHER"}, - {STATUS_ACCESS_DISABLED_BY_POLICY_OTHER, -EACCES, - "STATUS_ACCESS_DISABLED_BY_POLICY_OTHER"}, - {STATUS_FAILED_DRIVER_ENTRY, -EIO, "STATUS_FAILED_DRIVER_ENTRY"}, - {STATUS_DEVICE_ENUMERATION_ERROR, -EIO, - "STATUS_DEVICE_ENUMERATION_ERROR"}, - {STATUS_MOUNT_POINT_NOT_RESOLVED, -EIO, - "STATUS_MOUNT_POINT_NOT_RESOLVED"}, - {STATUS_INVALID_DEVICE_OBJECT_PARAMETER, -EIO, - "STATUS_INVALID_DEVICE_OBJECT_PARAMETER"}, - {STATUS_MCA_OCCURED, -EIO, "STATUS_MCA_OCCURED"}, - {STATUS_DRIVER_BLOCKED_CRITICAL, -EIO, - "STATUS_DRIVER_BLOCKED_CRITICAL"}, - {STATUS_DRIVER_BLOCKED, -EIO, "STATUS_DRIVER_BLOCKED"}, - {STATUS_DRIVER_DATABASE_ERROR, -EIO, "STATUS_DRIVER_DATABASE_ERROR"}, - {STATUS_SYSTEM_HIVE_TOO_LARGE, -EIO, "STATUS_SYSTEM_HIVE_TOO_LARGE"}, - {STATUS_INVALID_IMPORT_OF_NON_DLL, -EIO, - "STATUS_INVALID_IMPORT_OF_NON_DLL"}, - {STATUS_NO_SECRETS, -EIO, "STATUS_NO_SECRETS"}, - {STATUS_ACCESS_DISABLED_NO_SAFER_UI_BY_POLICY, -EACCES, - "STATUS_ACCESS_DISABLED_NO_SAFER_UI_BY_POLICY"}, - {STATUS_FAILED_STACK_SWITCH, -EIO, "STATUS_FAILED_STACK_SWITCH"}, - {STATUS_HEAP_CORRUPTION, -EIO, "STATUS_HEAP_CORRUPTION"}, - {STATUS_SMARTCARD_WRONG_PIN, -EIO, "STATUS_SMARTCARD_WRONG_PIN"}, - {STATUS_SMARTCARD_CARD_BLOCKED, -EIO, "STATUS_SMARTCARD_CARD_BLOCKED"}, - {STATUS_SMARTCARD_CARD_NOT_AUTHENTICATED, -EIO, - "STATUS_SMARTCARD_CARD_NOT_AUTHENTICATED"}, - {STATUS_SMARTCARD_NO_CARD, -EIO, "STATUS_SMARTCARD_NO_CARD"}, - {STATUS_SMARTCARD_NO_KEY_CONTAINER, -EIO, - "STATUS_SMARTCARD_NO_KEY_CONTAINER"}, - {STATUS_SMARTCARD_NO_CERTIFICATE, -EIO, - "STATUS_SMARTCARD_NO_CERTIFICATE"}, - {STATUS_SMARTCARD_NO_KEYSET, -EIO, "STATUS_SMARTCARD_NO_KEYSET"}, - {STATUS_SMARTCARD_IO_ERROR, -EIO, "STATUS_SMARTCARD_IO_ERROR"}, - {STATUS_DOWNGRADE_DETECTED, -EIO, "STATUS_DOWNGRADE_DETECTED"}, - {STATUS_SMARTCARD_CERT_REVOKED, -EIO, "STATUS_SMARTCARD_CERT_REVOKED"}, - {STATUS_ISSUING_CA_UNTRUSTED, -EIO, "STATUS_ISSUING_CA_UNTRUSTED"}, - {STATUS_REVOCATION_OFFLINE_C, -EIO, "STATUS_REVOCATION_OFFLINE_C"}, - {STATUS_PKINIT_CLIENT_FAILURE, -EIO, "STATUS_PKINIT_CLIENT_FAILURE"}, - {STATUS_SMARTCARD_CERT_EXPIRED, -EIO, "STATUS_SMARTCARD_CERT_EXPIRED"}, - {STATUS_DRIVER_FAILED_PRIOR_UNLOAD, -EIO, - "STATUS_DRIVER_FAILED_PRIOR_UNLOAD"}, - {STATUS_SMARTCARD_SILENT_CONTEXT, -EIO, - "STATUS_SMARTCARD_SILENT_CONTEXT"}, - {STATUS_PER_USER_TRUST_QUOTA_EXCEEDED, -EDQUOT, - "STATUS_PER_USER_TRUST_QUOTA_EXCEEDED"}, - {STATUS_ALL_USER_TRUST_QUOTA_EXCEEDED, -EDQUOT, - "STATUS_ALL_USER_TRUST_QUOTA_EXCEEDED"}, - {STATUS_USER_DELETE_TRUST_QUOTA_EXCEEDED, -EDQUOT, - "STATUS_USER_DELETE_TRUST_QUOTA_EXCEEDED"}, - {STATUS_DS_NAME_NOT_UNIQUE, -EIO, "STATUS_DS_NAME_NOT_UNIQUE"}, - {STATUS_DS_DUPLICATE_ID_FOUND, -EIO, "STATUS_DS_DUPLICATE_ID_FOUND"}, - {STATUS_DS_GROUP_CONVERSION_ERROR, -EIO, - "STATUS_DS_GROUP_CONVERSION_ERROR"}, - {STATUS_VOLSNAP_PREPARE_HIBERNATE, -EIO, - "STATUS_VOLSNAP_PREPARE_HIBERNATE"}, - {STATUS_USER2USER_REQUIRED, -EIO, "STATUS_USER2USER_REQUIRED"}, - {STATUS_STACK_BUFFER_OVERRUN, -EIO, "STATUS_STACK_BUFFER_OVERRUN"}, - {STATUS_NO_S4U_PROT_SUPPORT, -EIO, "STATUS_NO_S4U_PROT_SUPPORT"}, - {STATUS_CROSSREALM_DELEGATION_FAILURE, -EIO, - "STATUS_CROSSREALM_DELEGATION_FAILURE"}, - {STATUS_REVOCATION_OFFLINE_KDC, -EIO, "STATUS_REVOCATION_OFFLINE_KDC"}, - {STATUS_ISSUING_CA_UNTRUSTED_KDC, -EIO, - "STATUS_ISSUING_CA_UNTRUSTED_KDC"}, - {STATUS_KDC_CERT_EXPIRED, -EIO, "STATUS_KDC_CERT_EXPIRED"}, - {STATUS_KDC_CERT_REVOKED, -EIO, "STATUS_KDC_CERT_REVOKED"}, - {STATUS_PARAMETER_QUOTA_EXCEEDED, -EDQUOT, - "STATUS_PARAMETER_QUOTA_EXCEEDED"}, - {STATUS_HIBERNATION_FAILURE, -EIO, "STATUS_HIBERNATION_FAILURE"}, - {STATUS_DELAY_LOAD_FAILED, -EIO, "STATUS_DELAY_LOAD_FAILED"}, - {STATUS_AUTHENTICATION_FIREWALL_FAILED, -EIO, - "STATUS_AUTHENTICATION_FIREWALL_FAILED"}, - {STATUS_VDM_DISALLOWED, -EIO, "STATUS_VDM_DISALLOWED"}, - {STATUS_HUNG_DISPLAY_DRIVER_THREAD, -EIO, - "STATUS_HUNG_DISPLAY_DRIVER_THREAD"}, - {STATUS_INSUFFICIENT_RESOURCE_FOR_SPECIFIED_SHARED_SECTION_SIZE, -EIO, - "STATUS_INSUFFICIENT_RESOURCE_FOR_SPECIFIED_SHARED_SECTION_SIZE"}, - {STATUS_INVALID_CRUNTIME_PARAMETER, -EIO, - "STATUS_INVALID_CRUNTIME_PARAMETER"}, - {STATUS_NTLM_BLOCKED, -EIO, "STATUS_NTLM_BLOCKED"}, - {STATUS_ASSERTION_FAILURE, -EIO, "STATUS_ASSERTION_FAILURE"}, - {STATUS_VERIFIER_STOP, -EIO, "STATUS_VERIFIER_STOP"}, - {STATUS_CALLBACK_POP_STACK, -EIO, "STATUS_CALLBACK_POP_STACK"}, - {STATUS_INCOMPATIBLE_DRIVER_BLOCKED, -EIO, - "STATUS_INCOMPATIBLE_DRIVER_BLOCKED"}, - {STATUS_HIVE_UNLOADED, -EIO, "STATUS_HIVE_UNLOADED"}, - {STATUS_COMPRESSION_DISABLED, -EIO, "STATUS_COMPRESSION_DISABLED"}, - {STATUS_FILE_SYSTEM_LIMITATION, -EIO, "STATUS_FILE_SYSTEM_LIMITATION"}, - {STATUS_INVALID_IMAGE_HASH, -EIO, "STATUS_INVALID_IMAGE_HASH"}, - {STATUS_NOT_CAPABLE, -EIO, "STATUS_NOT_CAPABLE"}, - {STATUS_REQUEST_OUT_OF_SEQUENCE, -EIO, - "STATUS_REQUEST_OUT_OF_SEQUENCE"}, - {STATUS_IMPLEMENTATION_LIMIT, -EIO, "STATUS_IMPLEMENTATION_LIMIT"}, - {STATUS_ELEVATION_REQUIRED, -EIO, "STATUS_ELEVATION_REQUIRED"}, - {STATUS_BEYOND_VDL, -EIO, "STATUS_BEYOND_VDL"}, - {STATUS_ENCOUNTERED_WRITE_IN_PROGRESS, -EIO, - "STATUS_ENCOUNTERED_WRITE_IN_PROGRESS"}, - {STATUS_PTE_CHANGED, -EIO, "STATUS_PTE_CHANGED"}, - {STATUS_PURGE_FAILED, -EIO, "STATUS_PURGE_FAILED"}, - {STATUS_CRED_REQUIRES_CONFIRMATION, -EIO, - "STATUS_CRED_REQUIRES_CONFIRMATION"}, - {STATUS_CS_ENCRYPTION_INVALID_SERVER_RESPONSE, -EIO, - "STATUS_CS_ENCRYPTION_INVALID_SERVER_RESPONSE"}, - {STATUS_CS_ENCRYPTION_UNSUPPORTED_SERVER, -EIO, - "STATUS_CS_ENCRYPTION_UNSUPPORTED_SERVER"}, - {STATUS_CS_ENCRYPTION_EXISTING_ENCRYPTED_FILE, -EIO, - "STATUS_CS_ENCRYPTION_EXISTING_ENCRYPTED_FILE"}, - {STATUS_CS_ENCRYPTION_NEW_ENCRYPTED_FILE, -EIO, - "STATUS_CS_ENCRYPTION_NEW_ENCRYPTED_FILE"}, - {STATUS_CS_ENCRYPTION_FILE_NOT_CSE, -EIO, - "STATUS_CS_ENCRYPTION_FILE_NOT_CSE"}, - {STATUS_INVALID_LABEL, -EIO, "STATUS_INVALID_LABEL"}, - {STATUS_DRIVER_PROCESS_TERMINATED, -EIO, - "STATUS_DRIVER_PROCESS_TERMINATED"}, - {STATUS_AMBIGUOUS_SYSTEM_DEVICE, -EIO, - "STATUS_AMBIGUOUS_SYSTEM_DEVICE"}, - {STATUS_SYSTEM_DEVICE_NOT_FOUND, -EIO, - "STATUS_SYSTEM_DEVICE_NOT_FOUND"}, - {STATUS_RESTART_BOOT_APPLICATION, -EIO, - "STATUS_RESTART_BOOT_APPLICATION"}, - {STATUS_INVALID_TASK_NAME, -EIO, "STATUS_INVALID_TASK_NAME"}, - {STATUS_INVALID_TASK_INDEX, -EIO, "STATUS_INVALID_TASK_INDEX"}, - {STATUS_THREAD_ALREADY_IN_TASK, -EIO, "STATUS_THREAD_ALREADY_IN_TASK"}, - {STATUS_CALLBACK_BYPASS, -EIO, "STATUS_CALLBACK_BYPASS"}, - {STATUS_PORT_CLOSED, -EIO, "STATUS_PORT_CLOSED"}, - {STATUS_MESSAGE_LOST, -EIO, "STATUS_MESSAGE_LOST"}, - {STATUS_INVALID_MESSAGE, -EIO, "STATUS_INVALID_MESSAGE"}, - {STATUS_REQUEST_CANCELED, -EIO, "STATUS_REQUEST_CANCELED"}, - {STATUS_RECURSIVE_DISPATCH, -EIO, "STATUS_RECURSIVE_DISPATCH"}, - {STATUS_LPC_RECEIVE_BUFFER_EXPECTED, -EIO, - "STATUS_LPC_RECEIVE_BUFFER_EXPECTED"}, - {STATUS_LPC_INVALID_CONNECTION_USAGE, -EIO, - "STATUS_LPC_INVALID_CONNECTION_USAGE"}, - {STATUS_LPC_REQUESTS_NOT_ALLOWED, -EIO, - "STATUS_LPC_REQUESTS_NOT_ALLOWED"}, - {STATUS_RESOURCE_IN_USE, -EIO, "STATUS_RESOURCE_IN_USE"}, - {STATUS_HARDWARE_MEMORY_ERROR, -EIO, "STATUS_HARDWARE_MEMORY_ERROR"}, - {STATUS_THREADPOOL_HANDLE_EXCEPTION, -EIO, - "STATUS_THREADPOOL_HANDLE_EXCEPTION"}, - {STATUS_THREADPOOL_SET_EVENT_ON_COMPLETION_FAILED, -EIO, - "STATUS_THREADPOOL_SET_EVENT_ON_COMPLETION_FAILED"}, - {STATUS_THREADPOOL_RELEASE_SEMAPHORE_ON_COMPLETION_FAILED, -EIO, - "STATUS_THREADPOOL_RELEASE_SEMAPHORE_ON_COMPLETION_FAILED"}, - {STATUS_THREADPOOL_RELEASE_MUTEX_ON_COMPLETION_FAILED, -EIO, - "STATUS_THREADPOOL_RELEASE_MUTEX_ON_COMPLETION_FAILED"}, - {STATUS_THREADPOOL_FREE_LIBRARY_ON_COMPLETION_FAILED, -EIO, - "STATUS_THREADPOOL_FREE_LIBRARY_ON_COMPLETION_FAILED"}, - {STATUS_THREADPOOL_RELEASED_DURING_OPERATION, -EIO, - "STATUS_THREADPOOL_RELEASED_DURING_OPERATION"}, - {STATUS_CALLBACK_RETURNED_WHILE_IMPERSONATING, -EIO, - "STATUS_CALLBACK_RETURNED_WHILE_IMPERSONATING"}, - {STATUS_APC_RETURNED_WHILE_IMPERSONATING, -EIO, - "STATUS_APC_RETURNED_WHILE_IMPERSONATING"}, - {STATUS_PROCESS_IS_PROTECTED, -EIO, "STATUS_PROCESS_IS_PROTECTED"}, - {STATUS_MCA_EXCEPTION, -EIO, "STATUS_MCA_EXCEPTION"}, - {STATUS_CERTIFICATE_MAPPING_NOT_UNIQUE, -EIO, - "STATUS_CERTIFICATE_MAPPING_NOT_UNIQUE"}, - {STATUS_SYMLINK_CLASS_DISABLED, -EIO, "STATUS_SYMLINK_CLASS_DISABLED"}, - {STATUS_INVALID_IDN_NORMALIZATION, -EIO, - "STATUS_INVALID_IDN_NORMALIZATION"}, - {STATUS_NO_UNICODE_TRANSLATION, -EIO, "STATUS_NO_UNICODE_TRANSLATION"}, - {STATUS_ALREADY_REGISTERED, -EIO, "STATUS_ALREADY_REGISTERED"}, - {STATUS_CONTEXT_MISMATCH, -EIO, "STATUS_CONTEXT_MISMATCH"}, - {STATUS_PORT_ALREADY_HAS_COMPLETION_LIST, -EIO, - "STATUS_PORT_ALREADY_HAS_COMPLETION_LIST"}, - {STATUS_CALLBACK_RETURNED_THREAD_PRIORITY, -EIO, - "STATUS_CALLBACK_RETURNED_THREAD_PRIORITY"}, - {STATUS_INVALID_THREAD, -EIO, "STATUS_INVALID_THREAD"}, - {STATUS_CALLBACK_RETURNED_TRANSACTION, -EIO, - "STATUS_CALLBACK_RETURNED_TRANSACTION"}, - {STATUS_CALLBACK_RETURNED_LDR_LOCK, -EIO, - "STATUS_CALLBACK_RETURNED_LDR_LOCK"}, - {STATUS_CALLBACK_RETURNED_LANG, -EIO, "STATUS_CALLBACK_RETURNED_LANG"}, - {STATUS_CALLBACK_RETURNED_PRI_BACK, -EIO, - "STATUS_CALLBACK_RETURNED_PRI_BACK"}, - {STATUS_CALLBACK_RETURNED_THREAD_AFFINITY, -EIO, - "STATUS_CALLBACK_RETURNED_THREAD_AFFINITY"}, - {STATUS_DISK_REPAIR_DISABLED, -EIO, "STATUS_DISK_REPAIR_DISABLED"}, - {STATUS_DS_DOMAIN_RENAME_IN_PROGRESS, -EIO, - "STATUS_DS_DOMAIN_RENAME_IN_PROGRESS"}, - {STATUS_DISK_QUOTA_EXCEEDED, -EDQUOT, "STATUS_DISK_QUOTA_EXCEEDED"}, - {STATUS_CONTENT_BLOCKED, -EIO, "STATUS_CONTENT_BLOCKED"}, - {STATUS_BAD_CLUSTERS, -EIO, "STATUS_BAD_CLUSTERS"}, - {STATUS_VOLUME_DIRTY, -EIO, "STATUS_VOLUME_DIRTY"}, - {STATUS_FILE_CHECKED_OUT, -EIO, "STATUS_FILE_CHECKED_OUT"}, - {STATUS_CHECKOUT_REQUIRED, -EIO, "STATUS_CHECKOUT_REQUIRED"}, - {STATUS_BAD_FILE_TYPE, -EIO, "STATUS_BAD_FILE_TYPE"}, - {STATUS_FILE_TOO_LARGE, -EIO, "STATUS_FILE_TOO_LARGE"}, - {STATUS_FORMS_AUTH_REQUIRED, -EIO, "STATUS_FORMS_AUTH_REQUIRED"}, - {STATUS_VIRUS_INFECTED, -EIO, "STATUS_VIRUS_INFECTED"}, - {STATUS_VIRUS_DELETED, -EIO, "STATUS_VIRUS_DELETED"}, - {STATUS_BAD_MCFG_TABLE, -EIO, "STATUS_BAD_MCFG_TABLE"}, - {STATUS_WOW_ASSERTION, -EIO, "STATUS_WOW_ASSERTION"}, - {STATUS_INVALID_SIGNATURE, -EIO, "STATUS_INVALID_SIGNATURE"}, - {STATUS_HMAC_NOT_SUPPORTED, -EIO, "STATUS_HMAC_NOT_SUPPORTED"}, - {STATUS_IPSEC_QUEUE_OVERFLOW, -EIO, "STATUS_IPSEC_QUEUE_OVERFLOW"}, - {STATUS_ND_QUEUE_OVERFLOW, -EIO, "STATUS_ND_QUEUE_OVERFLOW"}, - {STATUS_HOPLIMIT_EXCEEDED, -EIO, "STATUS_HOPLIMIT_EXCEEDED"}, - {STATUS_PROTOCOL_NOT_SUPPORTED, -EOPNOTSUPP, - "STATUS_PROTOCOL_NOT_SUPPORTED"}, - {STATUS_LOST_WRITEBEHIND_DATA_NETWORK_DISCONNECTED, -EIO, - "STATUS_LOST_WRITEBEHIND_DATA_NETWORK_DISCONNECTED"}, - {STATUS_LOST_WRITEBEHIND_DATA_NETWORK_SERVER_ERROR, -EIO, - "STATUS_LOST_WRITEBEHIND_DATA_NETWORK_SERVER_ERROR"}, - {STATUS_LOST_WRITEBEHIND_DATA_LOCAL_DISK_ERROR, -EIO, - "STATUS_LOST_WRITEBEHIND_DATA_LOCAL_DISK_ERROR"}, - {STATUS_XML_PARSE_ERROR, -EIO, "STATUS_XML_PARSE_ERROR"}, - {STATUS_XMLDSIG_ERROR, -EIO, "STATUS_XMLDSIG_ERROR"}, - {STATUS_WRONG_COMPARTMENT, -EIO, "STATUS_WRONG_COMPARTMENT"}, - {STATUS_AUTHIP_FAILURE, -EIO, "STATUS_AUTHIP_FAILURE"}, - {DBG_NO_STATE_CHANGE, -EIO, "DBG_NO_STATE_CHANGE"}, - {DBG_APP_NOT_IDLE, -EIO, "DBG_APP_NOT_IDLE"}, - {RPC_NT_INVALID_STRING_BINDING, -EIO, "RPC_NT_INVALID_STRING_BINDING"}, - {RPC_NT_WRONG_KIND_OF_BINDING, -EIO, "RPC_NT_WRONG_KIND_OF_BINDING"}, - {RPC_NT_INVALID_BINDING, -EIO, "RPC_NT_INVALID_BINDING"}, - {RPC_NT_PROTSEQ_NOT_SUPPORTED, -EOPNOTSUPP, - "RPC_NT_PROTSEQ_NOT_SUPPORTED"}, - {RPC_NT_INVALID_RPC_PROTSEQ, -EIO, "RPC_NT_INVALID_RPC_PROTSEQ"}, - {RPC_NT_INVALID_STRING_UUID, -EIO, "RPC_NT_INVALID_STRING_UUID"}, - {RPC_NT_INVALID_ENDPOINT_FORMAT, -EIO, - "RPC_NT_INVALID_ENDPOINT_FORMAT"}, - {RPC_NT_INVALID_NET_ADDR, -EIO, "RPC_NT_INVALID_NET_ADDR"}, - {RPC_NT_NO_ENDPOINT_FOUND, -EIO, "RPC_NT_NO_ENDPOINT_FOUND"}, - {RPC_NT_INVALID_TIMEOUT, -EINVAL, "RPC_NT_INVALID_TIMEOUT"}, - {RPC_NT_OBJECT_NOT_FOUND, -ENOENT, "RPC_NT_OBJECT_NOT_FOUND"}, - {RPC_NT_ALREADY_REGISTERED, -EIO, "RPC_NT_ALREADY_REGISTERED"}, - {RPC_NT_TYPE_ALREADY_REGISTERED, -EIO, - "RPC_NT_TYPE_ALREADY_REGISTERED"}, - {RPC_NT_ALREADY_LISTENING, -EIO, "RPC_NT_ALREADY_LISTENING"}, - {RPC_NT_NO_PROTSEQS_REGISTERED, -EIO, "RPC_NT_NO_PROTSEQS_REGISTERED"}, - {RPC_NT_NOT_LISTENING, -EIO, "RPC_NT_NOT_LISTENING"}, - {RPC_NT_UNKNOWN_MGR_TYPE, -EIO, "RPC_NT_UNKNOWN_MGR_TYPE"}, - {RPC_NT_UNKNOWN_IF, -EIO, "RPC_NT_UNKNOWN_IF"}, - {RPC_NT_NO_BINDINGS, -EIO, "RPC_NT_NO_BINDINGS"}, - {RPC_NT_NO_PROTSEQS, -EIO, "RPC_NT_NO_PROTSEQS"}, - {RPC_NT_CANT_CREATE_ENDPOINT, -EIO, "RPC_NT_CANT_CREATE_ENDPOINT"}, - {RPC_NT_OUT_OF_RESOURCES, -EIO, "RPC_NT_OUT_OF_RESOURCES"}, - {RPC_NT_SERVER_UNAVAILABLE, -EIO, "RPC_NT_SERVER_UNAVAILABLE"}, - {RPC_NT_SERVER_TOO_BUSY, -EBUSY, "RPC_NT_SERVER_TOO_BUSY"}, - {RPC_NT_INVALID_NETWORK_OPTIONS, -EIO, - "RPC_NT_INVALID_NETWORK_OPTIONS"}, - {RPC_NT_NO_CALL_ACTIVE, -EIO, "RPC_NT_NO_CALL_ACTIVE"}, - {RPC_NT_CALL_FAILED, -EIO, "RPC_NT_CALL_FAILED"}, - {RPC_NT_CALL_FAILED_DNE, -EIO, "RPC_NT_CALL_FAILED_DNE"}, - {RPC_NT_PROTOCOL_ERROR, -EIO, "RPC_NT_PROTOCOL_ERROR"}, - {RPC_NT_UNSUPPORTED_TRANS_SYN, -EIO, "RPC_NT_UNSUPPORTED_TRANS_SYN"}, - {RPC_NT_UNSUPPORTED_TYPE, -EIO, "RPC_NT_UNSUPPORTED_TYPE"}, - {RPC_NT_INVALID_TAG, -EIO, "RPC_NT_INVALID_TAG"}, - {RPC_NT_INVALID_BOUND, -EIO, "RPC_NT_INVALID_BOUND"}, - {RPC_NT_NO_ENTRY_NAME, -EIO, "RPC_NT_NO_ENTRY_NAME"}, - {RPC_NT_INVALID_NAME_SYNTAX, -EIO, "RPC_NT_INVALID_NAME_SYNTAX"}, - {RPC_NT_UNSUPPORTED_NAME_SYNTAX, -EIO, - "RPC_NT_UNSUPPORTED_NAME_SYNTAX"}, - {RPC_NT_UUID_NO_ADDRESS, -EIO, "RPC_NT_UUID_NO_ADDRESS"}, - {RPC_NT_DUPLICATE_ENDPOINT, -ENOTUNIQ, "RPC_NT_DUPLICATE_ENDPOINT"}, - {RPC_NT_UNKNOWN_AUTHN_TYPE, -EIO, "RPC_NT_UNKNOWN_AUTHN_TYPE"}, - {RPC_NT_MAX_CALLS_TOO_SMALL, -EIO, "RPC_NT_MAX_CALLS_TOO_SMALL"}, - {RPC_NT_STRING_TOO_LONG, -EIO, "RPC_NT_STRING_TOO_LONG"}, - {RPC_NT_PROTSEQ_NOT_FOUND, -EIO, "RPC_NT_PROTSEQ_NOT_FOUND"}, - {RPC_NT_PROCNUM_OUT_OF_RANGE, -EIO, "RPC_NT_PROCNUM_OUT_OF_RANGE"}, - {RPC_NT_BINDING_HAS_NO_AUTH, -EIO, "RPC_NT_BINDING_HAS_NO_AUTH"}, - {RPC_NT_UNKNOWN_AUTHN_SERVICE, -EIO, "RPC_NT_UNKNOWN_AUTHN_SERVICE"}, - {RPC_NT_UNKNOWN_AUTHN_LEVEL, -EIO, "RPC_NT_UNKNOWN_AUTHN_LEVEL"}, - {RPC_NT_INVALID_AUTH_IDENTITY, -EIO, "RPC_NT_INVALID_AUTH_IDENTITY"}, - {RPC_NT_UNKNOWN_AUTHZ_SERVICE, -EIO, "RPC_NT_UNKNOWN_AUTHZ_SERVICE"}, - {EPT_NT_INVALID_ENTRY, -EIO, "EPT_NT_INVALID_ENTRY"}, - {EPT_NT_CANT_PERFORM_OP, -EIO, "EPT_NT_CANT_PERFORM_OP"}, - {EPT_NT_NOT_REGISTERED, -EIO, "EPT_NT_NOT_REGISTERED"}, - {RPC_NT_NOTHING_TO_EXPORT, -EIO, "RPC_NT_NOTHING_TO_EXPORT"}, - {RPC_NT_INCOMPLETE_NAME, -EIO, "RPC_NT_INCOMPLETE_NAME"}, - {RPC_NT_INVALID_VERS_OPTION, -EIO, "RPC_NT_INVALID_VERS_OPTION"}, - {RPC_NT_NO_MORE_MEMBERS, -EIO, "RPC_NT_NO_MORE_MEMBERS"}, - {RPC_NT_NOT_ALL_OBJS_UNEXPORTED, -EIO, - "RPC_NT_NOT_ALL_OBJS_UNEXPORTED"}, - {RPC_NT_INTERFACE_NOT_FOUND, -EIO, "RPC_NT_INTERFACE_NOT_FOUND"}, - {RPC_NT_ENTRY_ALREADY_EXISTS, -EIO, "RPC_NT_ENTRY_ALREADY_EXISTS"}, - {RPC_NT_ENTRY_NOT_FOUND, -EIO, "RPC_NT_ENTRY_NOT_FOUND"}, - {RPC_NT_NAME_SERVICE_UNAVAILABLE, -EIO, - "RPC_NT_NAME_SERVICE_UNAVAILABLE"}, - {RPC_NT_INVALID_NAF_ID, -EIO, "RPC_NT_INVALID_NAF_ID"}, - {RPC_NT_CANNOT_SUPPORT, -EOPNOTSUPP, "RPC_NT_CANNOT_SUPPORT"}, - {RPC_NT_NO_CONTEXT_AVAILABLE, -EIO, "RPC_NT_NO_CONTEXT_AVAILABLE"}, - {RPC_NT_INTERNAL_ERROR, -EIO, "RPC_NT_INTERNAL_ERROR"}, - {RPC_NT_ZERO_DIVIDE, -EIO, "RPC_NT_ZERO_DIVIDE"}, - {RPC_NT_ADDRESS_ERROR, -EIO, "RPC_NT_ADDRESS_ERROR"}, - {RPC_NT_FP_DIV_ZERO, -EIO, "RPC_NT_FP_DIV_ZERO"}, - {RPC_NT_FP_UNDERFLOW, -EIO, "RPC_NT_FP_UNDERFLOW"}, - {RPC_NT_FP_OVERFLOW, -EIO, "RPC_NT_FP_OVERFLOW"}, - {RPC_NT_CALL_IN_PROGRESS, -EIO, "RPC_NT_CALL_IN_PROGRESS"}, - {RPC_NT_NO_MORE_BINDINGS, -EIO, "RPC_NT_NO_MORE_BINDINGS"}, - {RPC_NT_GROUP_MEMBER_NOT_FOUND, -EIO, "RPC_NT_GROUP_MEMBER_NOT_FOUND"}, - {EPT_NT_CANT_CREATE, -EIO, "EPT_NT_CANT_CREATE"}, - {RPC_NT_INVALID_OBJECT, -EIO, "RPC_NT_INVALID_OBJECT"}, - {RPC_NT_NO_INTERFACES, -EIO, "RPC_NT_NO_INTERFACES"}, - {RPC_NT_CALL_CANCELLED, -EIO, "RPC_NT_CALL_CANCELLED"}, - {RPC_NT_BINDING_INCOMPLETE, -EIO, "RPC_NT_BINDING_INCOMPLETE"}, - {RPC_NT_COMM_FAILURE, -EIO, "RPC_NT_COMM_FAILURE"}, - {RPC_NT_UNSUPPORTED_AUTHN_LEVEL, -EIO, - "RPC_NT_UNSUPPORTED_AUTHN_LEVEL"}, - {RPC_NT_NO_PRINC_NAME, -EIO, "RPC_NT_NO_PRINC_NAME"}, - {RPC_NT_NOT_RPC_ERROR, -EIO, "RPC_NT_NOT_RPC_ERROR"}, - {RPC_NT_SEC_PKG_ERROR, -EIO, "RPC_NT_SEC_PKG_ERROR"}, - {RPC_NT_NOT_CANCELLED, -EIO, "RPC_NT_NOT_CANCELLED"}, - {RPC_NT_INVALID_ASYNC_HANDLE, -EIO, "RPC_NT_INVALID_ASYNC_HANDLE"}, - {RPC_NT_INVALID_ASYNC_CALL, -EIO, "RPC_NT_INVALID_ASYNC_CALL"}, - {RPC_NT_PROXY_ACCESS_DENIED, -EACCES, "RPC_NT_PROXY_ACCESS_DENIED"}, - {RPC_NT_NO_MORE_ENTRIES, -EIO, "RPC_NT_NO_MORE_ENTRIES"}, - {RPC_NT_SS_CHAR_TRANS_OPEN_FAIL, -EIO, - "RPC_NT_SS_CHAR_TRANS_OPEN_FAIL"}, - {RPC_NT_SS_CHAR_TRANS_SHORT_FILE, -EIO, - "RPC_NT_SS_CHAR_TRANS_SHORT_FILE"}, - {RPC_NT_SS_IN_NULL_CONTEXT, -EIO, "RPC_NT_SS_IN_NULL_CONTEXT"}, - {RPC_NT_SS_CONTEXT_MISMATCH, -EIO, "RPC_NT_SS_CONTEXT_MISMATCH"}, - {RPC_NT_SS_CONTEXT_DAMAGED, -EIO, "RPC_NT_SS_CONTEXT_DAMAGED"}, - {RPC_NT_SS_HANDLES_MISMATCH, -EIO, "RPC_NT_SS_HANDLES_MISMATCH"}, - {RPC_NT_SS_CANNOT_GET_CALL_HANDLE, -EIO, - "RPC_NT_SS_CANNOT_GET_CALL_HANDLE"}, - {RPC_NT_NULL_REF_POINTER, -EIO, "RPC_NT_NULL_REF_POINTER"}, - {RPC_NT_ENUM_VALUE_OUT_OF_RANGE, -EIO, - "RPC_NT_ENUM_VALUE_OUT_OF_RANGE"}, - {RPC_NT_BYTE_COUNT_TOO_SMALL, -EIO, "RPC_NT_BYTE_COUNT_TOO_SMALL"}, - {RPC_NT_BAD_STUB_DATA, -EIO, "RPC_NT_BAD_STUB_DATA"}, - {RPC_NT_INVALID_ES_ACTION, -EIO, "RPC_NT_INVALID_ES_ACTION"}, - {RPC_NT_WRONG_ES_VERSION, -EIO, "RPC_NT_WRONG_ES_VERSION"}, - {RPC_NT_WRONG_STUB_VERSION, -EIO, "RPC_NT_WRONG_STUB_VERSION"}, - {RPC_NT_INVALID_PIPE_OBJECT, -EIO, "RPC_NT_INVALID_PIPE_OBJECT"}, - {RPC_NT_INVALID_PIPE_OPERATION, -EIO, "RPC_NT_INVALID_PIPE_OPERATION"}, - {RPC_NT_WRONG_PIPE_VERSION, -EIO, "RPC_NT_WRONG_PIPE_VERSION"}, - {RPC_NT_PIPE_CLOSED, -EIO, "RPC_NT_PIPE_CLOSED"}, - {RPC_NT_PIPE_DISCIPLINE_ERROR, -EIO, "RPC_NT_PIPE_DISCIPLINE_ERROR"}, - {RPC_NT_PIPE_EMPTY, -EIO, "RPC_NT_PIPE_EMPTY"}, - {STATUS_PNP_BAD_MPS_TABLE, -EIO, "STATUS_PNP_BAD_MPS_TABLE"}, - {STATUS_PNP_TRANSLATION_FAILED, -EIO, "STATUS_PNP_TRANSLATION_FAILED"}, - {STATUS_PNP_IRQ_TRANSLATION_FAILED, -EIO, - "STATUS_PNP_IRQ_TRANSLATION_FAILED"}, - {STATUS_PNP_INVALID_ID, -EIO, "STATUS_PNP_INVALID_ID"}, - {STATUS_IO_REISSUE_AS_CACHED, -EIO, "STATUS_IO_REISSUE_AS_CACHED"}, - {STATUS_CTX_WINSTATION_NAME_INVALID, -EIO, - "STATUS_CTX_WINSTATION_NAME_INVALID"}, - {STATUS_CTX_INVALID_PD, -EIO, "STATUS_CTX_INVALID_PD"}, - {STATUS_CTX_PD_NOT_FOUND, -EIO, "STATUS_CTX_PD_NOT_FOUND"}, - {STATUS_CTX_CLOSE_PENDING, -EIO, "STATUS_CTX_CLOSE_PENDING"}, - {STATUS_CTX_NO_OUTBUF, -EIO, "STATUS_CTX_NO_OUTBUF"}, - {STATUS_CTX_MODEM_INF_NOT_FOUND, -EIO, - "STATUS_CTX_MODEM_INF_NOT_FOUND"}, - {STATUS_CTX_INVALID_MODEMNAME, -EIO, "STATUS_CTX_INVALID_MODEMNAME"}, - {STATUS_CTX_RESPONSE_ERROR, -EIO, "STATUS_CTX_RESPONSE_ERROR"}, - {STATUS_CTX_MODEM_RESPONSE_TIMEOUT, -ETIMEDOUT, - "STATUS_CTX_MODEM_RESPONSE_TIMEOUT"}, - {STATUS_CTX_MODEM_RESPONSE_NO_CARRIER, -EIO, - "STATUS_CTX_MODEM_RESPONSE_NO_CARRIER"}, - {STATUS_CTX_MODEM_RESPONSE_NO_DIALTONE, -EIO, - "STATUS_CTX_MODEM_RESPONSE_NO_DIALTONE"}, - {STATUS_CTX_MODEM_RESPONSE_BUSY, -EBUSY, - "STATUS_CTX_MODEM_RESPONSE_BUSY"}, - {STATUS_CTX_MODEM_RESPONSE_VOICE, -EIO, - "STATUS_CTX_MODEM_RESPONSE_VOICE"}, - {STATUS_CTX_TD_ERROR, -EIO, "STATUS_CTX_TD_ERROR"}, - {STATUS_CTX_LICENSE_CLIENT_INVALID, -EIO, - "STATUS_CTX_LICENSE_CLIENT_INVALID"}, - {STATUS_CTX_LICENSE_NOT_AVAILABLE, -EIO, - "STATUS_CTX_LICENSE_NOT_AVAILABLE"}, - {STATUS_CTX_LICENSE_EXPIRED, -EIO, "STATUS_CTX_LICENSE_EXPIRED"}, - {STATUS_CTX_WINSTATION_NOT_FOUND, -EIO, - "STATUS_CTX_WINSTATION_NOT_FOUND"}, - {STATUS_CTX_WINSTATION_NAME_COLLISION, -EIO, - "STATUS_CTX_WINSTATION_NAME_COLLISION"}, - {STATUS_CTX_WINSTATION_BUSY, -EBUSY, "STATUS_CTX_WINSTATION_BUSY"}, - {STATUS_CTX_BAD_VIDEO_MODE, -EIO, "STATUS_CTX_BAD_VIDEO_MODE"}, - {STATUS_CTX_GRAPHICS_INVALID, -EIO, "STATUS_CTX_GRAPHICS_INVALID"}, - {STATUS_CTX_NOT_CONSOLE, -EIO, "STATUS_CTX_NOT_CONSOLE"}, - {STATUS_CTX_CLIENT_QUERY_TIMEOUT, -EIO, - "STATUS_CTX_CLIENT_QUERY_TIMEOUT"}, - {STATUS_CTX_CONSOLE_DISCONNECT, -EIO, "STATUS_CTX_CONSOLE_DISCONNECT"}, - {STATUS_CTX_CONSOLE_CONNECT, -EIO, "STATUS_CTX_CONSOLE_CONNECT"}, - {STATUS_CTX_SHADOW_DENIED, -EIO, "STATUS_CTX_SHADOW_DENIED"}, - {STATUS_CTX_WINSTATION_ACCESS_DENIED, -EACCES, - "STATUS_CTX_WINSTATION_ACCESS_DENIED"}, - {STATUS_CTX_INVALID_WD, -EIO, "STATUS_CTX_INVALID_WD"}, - {STATUS_CTX_WD_NOT_FOUND, -EIO, "STATUS_CTX_WD_NOT_FOUND"}, - {STATUS_CTX_SHADOW_INVALID, -EIO, "STATUS_CTX_SHADOW_INVALID"}, - {STATUS_CTX_SHADOW_DISABLED, -EIO, "STATUS_CTX_SHADOW_DISABLED"}, - {STATUS_RDP_PROTOCOL_ERROR, -EIO, "STATUS_RDP_PROTOCOL_ERROR"}, - {STATUS_CTX_CLIENT_LICENSE_NOT_SET, -EIO, - "STATUS_CTX_CLIENT_LICENSE_NOT_SET"}, - {STATUS_CTX_CLIENT_LICENSE_IN_USE, -EIO, - "STATUS_CTX_CLIENT_LICENSE_IN_USE"}, - {STATUS_CTX_SHADOW_ENDED_BY_MODE_CHANGE, -EIO, - "STATUS_CTX_SHADOW_ENDED_BY_MODE_CHANGE"}, - {STATUS_CTX_SHADOW_NOT_RUNNING, -EIO, "STATUS_CTX_SHADOW_NOT_RUNNING"}, - {STATUS_CTX_LOGON_DISABLED, -EIO, "STATUS_CTX_LOGON_DISABLED"}, - {STATUS_CTX_SECURITY_LAYER_ERROR, -EIO, - "STATUS_CTX_SECURITY_LAYER_ERROR"}, - {STATUS_TS_INCOMPATIBLE_SESSIONS, -EIO, - "STATUS_TS_INCOMPATIBLE_SESSIONS"}, - {STATUS_MUI_FILE_NOT_FOUND, -EIO, "STATUS_MUI_FILE_NOT_FOUND"}, - {STATUS_MUI_INVALID_FILE, -EIO, "STATUS_MUI_INVALID_FILE"}, - {STATUS_MUI_INVALID_RC_CONFIG, -EIO, "STATUS_MUI_INVALID_RC_CONFIG"}, - {STATUS_MUI_INVALID_LOCALE_NAME, -EIO, - "STATUS_MUI_INVALID_LOCALE_NAME"}, - {STATUS_MUI_INVALID_ULTIMATEFALLBACK_NAME, -EIO, - "STATUS_MUI_INVALID_ULTIMATEFALLBACK_NAME"}, - {STATUS_MUI_FILE_NOT_LOADED, -EIO, "STATUS_MUI_FILE_NOT_LOADED"}, - {STATUS_RESOURCE_ENUM_USER_STOP, -EIO, - "STATUS_RESOURCE_ENUM_USER_STOP"}, - {STATUS_CLUSTER_INVALID_NODE, -EIO, "STATUS_CLUSTER_INVALID_NODE"}, - {STATUS_CLUSTER_NODE_EXISTS, -EIO, "STATUS_CLUSTER_NODE_EXISTS"}, - {STATUS_CLUSTER_JOIN_IN_PROGRESS, -EIO, - "STATUS_CLUSTER_JOIN_IN_PROGRESS"}, - {STATUS_CLUSTER_NODE_NOT_FOUND, -EIO, "STATUS_CLUSTER_NODE_NOT_FOUND"}, - {STATUS_CLUSTER_LOCAL_NODE_NOT_FOUND, -EIO, - "STATUS_CLUSTER_LOCAL_NODE_NOT_FOUND"}, - {STATUS_CLUSTER_NETWORK_EXISTS, -EIO, "STATUS_CLUSTER_NETWORK_EXISTS"}, - {STATUS_CLUSTER_NETWORK_NOT_FOUND, -EIO, - "STATUS_CLUSTER_NETWORK_NOT_FOUND"}, - {STATUS_CLUSTER_NETINTERFACE_EXISTS, -EIO, - "STATUS_CLUSTER_NETINTERFACE_EXISTS"}, - {STATUS_CLUSTER_NETINTERFACE_NOT_FOUND, -EIO, - "STATUS_CLUSTER_NETINTERFACE_NOT_FOUND"}, - {STATUS_CLUSTER_INVALID_REQUEST, -EIO, - "STATUS_CLUSTER_INVALID_REQUEST"}, - {STATUS_CLUSTER_INVALID_NETWORK_PROVIDER, -EIO, - "STATUS_CLUSTER_INVALID_NETWORK_PROVIDER"}, - {STATUS_CLUSTER_NODE_DOWN, -EIO, "STATUS_CLUSTER_NODE_DOWN"}, - {STATUS_CLUSTER_NODE_UNREACHABLE, -EIO, - "STATUS_CLUSTER_NODE_UNREACHABLE"}, - {STATUS_CLUSTER_NODE_NOT_MEMBER, -EIO, - "STATUS_CLUSTER_NODE_NOT_MEMBER"}, - {STATUS_CLUSTER_JOIN_NOT_IN_PROGRESS, -EIO, - "STATUS_CLUSTER_JOIN_NOT_IN_PROGRESS"}, - {STATUS_CLUSTER_INVALID_NETWORK, -EIO, - "STATUS_CLUSTER_INVALID_NETWORK"}, - {STATUS_CLUSTER_NO_NET_ADAPTERS, -EIO, - "STATUS_CLUSTER_NO_NET_ADAPTERS"}, - {STATUS_CLUSTER_NODE_UP, -EIO, "STATUS_CLUSTER_NODE_UP"}, - {STATUS_CLUSTER_NODE_PAUSED, -EIO, "STATUS_CLUSTER_NODE_PAUSED"}, - {STATUS_CLUSTER_NODE_NOT_PAUSED, -EIO, - "STATUS_CLUSTER_NODE_NOT_PAUSED"}, - {STATUS_CLUSTER_NO_SECURITY_CONTEXT, -EIO, - "STATUS_CLUSTER_NO_SECURITY_CONTEXT"}, - {STATUS_CLUSTER_NETWORK_NOT_INTERNAL, -EIO, - "STATUS_CLUSTER_NETWORK_NOT_INTERNAL"}, - {STATUS_CLUSTER_POISONED, -EIO, "STATUS_CLUSTER_POISONED"}, - {STATUS_ACPI_INVALID_OPCODE, -EIO, "STATUS_ACPI_INVALID_OPCODE"}, - {STATUS_ACPI_STACK_OVERFLOW, -EIO, "STATUS_ACPI_STACK_OVERFLOW"}, - {STATUS_ACPI_ASSERT_FAILED, -EIO, "STATUS_ACPI_ASSERT_FAILED"}, - {STATUS_ACPI_INVALID_INDEX, -EIO, "STATUS_ACPI_INVALID_INDEX"}, - {STATUS_ACPI_INVALID_ARGUMENT, -EIO, "STATUS_ACPI_INVALID_ARGUMENT"}, - {STATUS_ACPI_FATAL, -EIO, "STATUS_ACPI_FATAL"}, - {STATUS_ACPI_INVALID_SUPERNAME, -EIO, "STATUS_ACPI_INVALID_SUPERNAME"}, - {STATUS_ACPI_INVALID_ARGTYPE, -EIO, "STATUS_ACPI_INVALID_ARGTYPE"}, - {STATUS_ACPI_INVALID_OBJTYPE, -EIO, "STATUS_ACPI_INVALID_OBJTYPE"}, - {STATUS_ACPI_INVALID_TARGETTYPE, -EIO, - "STATUS_ACPI_INVALID_TARGETTYPE"}, - {STATUS_ACPI_INCORRECT_ARGUMENT_COUNT, -EIO, - "STATUS_ACPI_INCORRECT_ARGUMENT_COUNT"}, - {STATUS_ACPI_ADDRESS_NOT_MAPPED, -EIO, - "STATUS_ACPI_ADDRESS_NOT_MAPPED"}, - {STATUS_ACPI_INVALID_EVENTTYPE, -EIO, "STATUS_ACPI_INVALID_EVENTTYPE"}, - {STATUS_ACPI_HANDLER_COLLISION, -EIO, "STATUS_ACPI_HANDLER_COLLISION"}, - {STATUS_ACPI_INVALID_DATA, -EIO, "STATUS_ACPI_INVALID_DATA"}, - {STATUS_ACPI_INVALID_REGION, -EIO, "STATUS_ACPI_INVALID_REGION"}, - {STATUS_ACPI_INVALID_ACCESS_SIZE, -EIO, - "STATUS_ACPI_INVALID_ACCESS_SIZE"}, - {STATUS_ACPI_ACQUIRE_GLOBAL_LOCK, -EIO, - "STATUS_ACPI_ACQUIRE_GLOBAL_LOCK"}, - {STATUS_ACPI_ALREADY_INITIALIZED, -EIO, - "STATUS_ACPI_ALREADY_INITIALIZED"}, - {STATUS_ACPI_NOT_INITIALIZED, -EIO, "STATUS_ACPI_NOT_INITIALIZED"}, - {STATUS_ACPI_INVALID_MUTEX_LEVEL, -EIO, - "STATUS_ACPI_INVALID_MUTEX_LEVEL"}, - {STATUS_ACPI_MUTEX_NOT_OWNED, -EIO, "STATUS_ACPI_MUTEX_NOT_OWNED"}, - {STATUS_ACPI_MUTEX_NOT_OWNER, -EIO, "STATUS_ACPI_MUTEX_NOT_OWNER"}, - {STATUS_ACPI_RS_ACCESS, -EIO, "STATUS_ACPI_RS_ACCESS"}, - {STATUS_ACPI_INVALID_TABLE, -EIO, "STATUS_ACPI_INVALID_TABLE"}, - {STATUS_ACPI_REG_HANDLER_FAILED, -EIO, - "STATUS_ACPI_REG_HANDLER_FAILED"}, - {STATUS_ACPI_POWER_REQUEST_FAILED, -EIO, - "STATUS_ACPI_POWER_REQUEST_FAILED"}, - {STATUS_SXS_SECTION_NOT_FOUND, -EIO, "STATUS_SXS_SECTION_NOT_FOUND"}, - {STATUS_SXS_CANT_GEN_ACTCTX, -EIO, "STATUS_SXS_CANT_GEN_ACTCTX"}, - {STATUS_SXS_INVALID_ACTCTXDATA_FORMAT, -EIO, - "STATUS_SXS_INVALID_ACTCTXDATA_FORMAT"}, - {STATUS_SXS_ASSEMBLY_NOT_FOUND, -EIO, "STATUS_SXS_ASSEMBLY_NOT_FOUND"}, - {STATUS_SXS_MANIFEST_FORMAT_ERROR, -EIO, - "STATUS_SXS_MANIFEST_FORMAT_ERROR"}, - {STATUS_SXS_MANIFEST_PARSE_ERROR, -EIO, - "STATUS_SXS_MANIFEST_PARSE_ERROR"}, - {STATUS_SXS_ACTIVATION_CONTEXT_DISABLED, -EIO, - "STATUS_SXS_ACTIVATION_CONTEXT_DISABLED"}, - {STATUS_SXS_KEY_NOT_FOUND, -EIO, "STATUS_SXS_KEY_NOT_FOUND"}, - {STATUS_SXS_VERSION_CONFLICT, -EIO, "STATUS_SXS_VERSION_CONFLICT"}, - {STATUS_SXS_WRONG_SECTION_TYPE, -EIO, "STATUS_SXS_WRONG_SECTION_TYPE"}, - {STATUS_SXS_THREAD_QUERIES_DISABLED, -EIO, - "STATUS_SXS_THREAD_QUERIES_DISABLED"}, - {STATUS_SXS_ASSEMBLY_MISSING, -EIO, "STATUS_SXS_ASSEMBLY_MISSING"}, - {STATUS_SXS_PROCESS_DEFAULT_ALREADY_SET, -EIO, - "STATUS_SXS_PROCESS_DEFAULT_ALREADY_SET"}, - {STATUS_SXS_EARLY_DEACTIVATION, -EIO, "STATUS_SXS_EARLY_DEACTIVATION"}, - {STATUS_SXS_INVALID_DEACTIVATION, -EIO, - "STATUS_SXS_INVALID_DEACTIVATION"}, - {STATUS_SXS_MULTIPLE_DEACTIVATION, -EIO, - "STATUS_SXS_MULTIPLE_DEACTIVATION"}, - {STATUS_SXS_SYSTEM_DEFAULT_ACTIVATION_CONTEXT_EMPTY, -EIO, - "STATUS_SXS_SYSTEM_DEFAULT_ACTIVATION_CONTEXT_EMPTY"}, - {STATUS_SXS_PROCESS_TERMINATION_REQUESTED, -EIO, - "STATUS_SXS_PROCESS_TERMINATION_REQUESTED"}, - {STATUS_SXS_CORRUPT_ACTIVATION_STACK, -EIO, - "STATUS_SXS_CORRUPT_ACTIVATION_STACK"}, - {STATUS_SXS_CORRUPTION, -EIO, "STATUS_SXS_CORRUPTION"}, - {STATUS_SXS_INVALID_IDENTITY_ATTRIBUTE_VALUE, -EIO, - "STATUS_SXS_INVALID_IDENTITY_ATTRIBUTE_VALUE"}, - {STATUS_SXS_INVALID_IDENTITY_ATTRIBUTE_NAME, -EIO, - "STATUS_SXS_INVALID_IDENTITY_ATTRIBUTE_NAME"}, - {STATUS_SXS_IDENTITY_DUPLICATE_ATTRIBUTE, -EIO, - "STATUS_SXS_IDENTITY_DUPLICATE_ATTRIBUTE"}, - {STATUS_SXS_IDENTITY_PARSE_ERROR, -EIO, - "STATUS_SXS_IDENTITY_PARSE_ERROR"}, - {STATUS_SXS_COMPONENT_STORE_CORRUPT, -EIO, - "STATUS_SXS_COMPONENT_STORE_CORRUPT"}, - {STATUS_SXS_FILE_HASH_MISMATCH, -EIO, "STATUS_SXS_FILE_HASH_MISMATCH"}, - {STATUS_SXS_MANIFEST_IDENTITY_SAME_BUT_CONTENTS_DIFFERENT, -EIO, - "STATUS_SXS_MANIFEST_IDENTITY_SAME_BUT_CONTENTS_DIFFERENT"}, - {STATUS_SXS_IDENTITIES_DIFFERENT, -EIO, - "STATUS_SXS_IDENTITIES_DIFFERENT"}, - {STATUS_SXS_ASSEMBLY_IS_NOT_A_DEPLOYMENT, -EIO, - "STATUS_SXS_ASSEMBLY_IS_NOT_A_DEPLOYMENT"}, - {STATUS_SXS_FILE_NOT_PART_OF_ASSEMBLY, -EIO, - "STATUS_SXS_FILE_NOT_PART_OF_ASSEMBLY"}, - {STATUS_ADVANCED_INSTALLER_FAILED, -EIO, - "STATUS_ADVANCED_INSTALLER_FAILED"}, - {STATUS_XML_ENCODING_MISMATCH, -EIO, "STATUS_XML_ENCODING_MISMATCH"}, - {STATUS_SXS_MANIFEST_TOO_BIG, -EIO, "STATUS_SXS_MANIFEST_TOO_BIG"}, - {STATUS_SXS_SETTING_NOT_REGISTERED, -EIO, - "STATUS_SXS_SETTING_NOT_REGISTERED"}, - {STATUS_SXS_TRANSACTION_CLOSURE_INCOMPLETE, -EIO, - "STATUS_SXS_TRANSACTION_CLOSURE_INCOMPLETE"}, - {STATUS_SMI_PRIMITIVE_INSTALLER_FAILED, -EIO, - "STATUS_SMI_PRIMITIVE_INSTALLER_FAILED"}, - {STATUS_GENERIC_COMMAND_FAILED, -EIO, "STATUS_GENERIC_COMMAND_FAILED"}, - {STATUS_SXS_FILE_HASH_MISSING, -EIO, "STATUS_SXS_FILE_HASH_MISSING"}, - {STATUS_TRANSACTIONAL_CONFLICT, -EIO, "STATUS_TRANSACTIONAL_CONFLICT"}, - {STATUS_INVALID_TRANSACTION, -EIO, "STATUS_INVALID_TRANSACTION"}, - {STATUS_TRANSACTION_NOT_ACTIVE, -EIO, "STATUS_TRANSACTION_NOT_ACTIVE"}, - {STATUS_TM_INITIALIZATION_FAILED, -EIO, - "STATUS_TM_INITIALIZATION_FAILED"}, - {STATUS_RM_NOT_ACTIVE, -EIO, "STATUS_RM_NOT_ACTIVE"}, - {STATUS_RM_METADATA_CORRUPT, -EIO, "STATUS_RM_METADATA_CORRUPT"}, - {STATUS_TRANSACTION_NOT_JOINED, -EIO, "STATUS_TRANSACTION_NOT_JOINED"}, - {STATUS_DIRECTORY_NOT_RM, -EIO, "STATUS_DIRECTORY_NOT_RM"}, - {STATUS_TRANSACTIONS_UNSUPPORTED_REMOTE, -EIO, - "STATUS_TRANSACTIONS_UNSUPPORTED_REMOTE"}, - {STATUS_LOG_RESIZE_INVALID_SIZE, -EIO, - "STATUS_LOG_RESIZE_INVALID_SIZE"}, - {STATUS_REMOTE_FILE_VERSION_MISMATCH, -EIO, - "STATUS_REMOTE_FILE_VERSION_MISMATCH"}, - {STATUS_CRM_PROTOCOL_ALREADY_EXISTS, -EIO, - "STATUS_CRM_PROTOCOL_ALREADY_EXISTS"}, - {STATUS_TRANSACTION_PROPAGATION_FAILED, -EIO, - "STATUS_TRANSACTION_PROPAGATION_FAILED"}, - {STATUS_CRM_PROTOCOL_NOT_FOUND, -EIO, "STATUS_CRM_PROTOCOL_NOT_FOUND"}, - {STATUS_TRANSACTION_SUPERIOR_EXISTS, -EIO, - "STATUS_TRANSACTION_SUPERIOR_EXISTS"}, - {STATUS_TRANSACTION_REQUEST_NOT_VALID, -EIO, - "STATUS_TRANSACTION_REQUEST_NOT_VALID"}, - {STATUS_TRANSACTION_NOT_REQUESTED, -EIO, - "STATUS_TRANSACTION_NOT_REQUESTED"}, - {STATUS_TRANSACTION_ALREADY_ABORTED, -EIO, - "STATUS_TRANSACTION_ALREADY_ABORTED"}, - {STATUS_TRANSACTION_ALREADY_COMMITTED, -EIO, - "STATUS_TRANSACTION_ALREADY_COMMITTED"}, - {STATUS_TRANSACTION_INVALID_MARSHALL_BUFFER, -EIO, - "STATUS_TRANSACTION_INVALID_MARSHALL_BUFFER"}, - {STATUS_CURRENT_TRANSACTION_NOT_VALID, -EIO, - "STATUS_CURRENT_TRANSACTION_NOT_VALID"}, - {STATUS_LOG_GROWTH_FAILED, -EIO, "STATUS_LOG_GROWTH_FAILED"}, - {STATUS_OBJECT_NO_LONGER_EXISTS, -EIO, - "STATUS_OBJECT_NO_LONGER_EXISTS"}, - {STATUS_STREAM_MINIVERSION_NOT_FOUND, -EIO, - "STATUS_STREAM_MINIVERSION_NOT_FOUND"}, - {STATUS_STREAM_MINIVERSION_NOT_VALID, -EIO, - "STATUS_STREAM_MINIVERSION_NOT_VALID"}, - {STATUS_MINIVERSION_INACCESSIBLE_FROM_SPECIFIED_TRANSACTION, -EIO, - "STATUS_MINIVERSION_INACCESSIBLE_FROM_SPECIFIED_TRANSACTION"}, - {STATUS_CANT_OPEN_MINIVERSION_WITH_MODIFY_INTENT, -EIO, - "STATUS_CANT_OPEN_MINIVERSION_WITH_MODIFY_INTENT"}, - {STATUS_CANT_CREATE_MORE_STREAM_MINIVERSIONS, -EIO, - "STATUS_CANT_CREATE_MORE_STREAM_MINIVERSIONS"}, - {STATUS_HANDLE_NO_LONGER_VALID, -EIO, "STATUS_HANDLE_NO_LONGER_VALID"}, - {STATUS_LOG_CORRUPTION_DETECTED, -EIO, - "STATUS_LOG_CORRUPTION_DETECTED"}, - {STATUS_RM_DISCONNECTED, -EIO, "STATUS_RM_DISCONNECTED"}, - {STATUS_ENLISTMENT_NOT_SUPERIOR, -EIO, - "STATUS_ENLISTMENT_NOT_SUPERIOR"}, - {STATUS_FILE_IDENTITY_NOT_PERSISTENT, -EIO, - "STATUS_FILE_IDENTITY_NOT_PERSISTENT"}, - {STATUS_CANT_BREAK_TRANSACTIONAL_DEPENDENCY, -EIO, - "STATUS_CANT_BREAK_TRANSACTIONAL_DEPENDENCY"}, - {STATUS_CANT_CROSS_RM_BOUNDARY, -EIO, "STATUS_CANT_CROSS_RM_BOUNDARY"}, - {STATUS_TXF_DIR_NOT_EMPTY, -EIO, "STATUS_TXF_DIR_NOT_EMPTY"}, - {STATUS_INDOUBT_TRANSACTIONS_EXIST, -EIO, - "STATUS_INDOUBT_TRANSACTIONS_EXIST"}, - {STATUS_TM_VOLATILE, -EIO, "STATUS_TM_VOLATILE"}, - {STATUS_ROLLBACK_TIMER_EXPIRED, -EIO, "STATUS_ROLLBACK_TIMER_EXPIRED"}, - {STATUS_TXF_ATTRIBUTE_CORRUPT, -EIO, "STATUS_TXF_ATTRIBUTE_CORRUPT"}, - {STATUS_EFS_NOT_ALLOWED_IN_TRANSACTION, -EIO, - "STATUS_EFS_NOT_ALLOWED_IN_TRANSACTION"}, - {STATUS_TRANSACTIONAL_OPEN_NOT_ALLOWED, -EIO, - "STATUS_TRANSACTIONAL_OPEN_NOT_ALLOWED"}, - {STATUS_TRANSACTED_MAPPING_UNSUPPORTED_REMOTE, -EIO, - "STATUS_TRANSACTED_MAPPING_UNSUPPORTED_REMOTE"}, - {STATUS_TRANSACTION_REQUIRED_PROMOTION, -EIO, - "STATUS_TRANSACTION_REQUIRED_PROMOTION"}, - {STATUS_CANNOT_EXECUTE_FILE_IN_TRANSACTION, -EIO, - "STATUS_CANNOT_EXECUTE_FILE_IN_TRANSACTION"}, - {STATUS_TRANSACTIONS_NOT_FROZEN, -EIO, - "STATUS_TRANSACTIONS_NOT_FROZEN"}, - {STATUS_TRANSACTION_FREEZE_IN_PROGRESS, -EIO, - "STATUS_TRANSACTION_FREEZE_IN_PROGRESS"}, - {STATUS_NOT_SNAPSHOT_VOLUME, -EIO, "STATUS_NOT_SNAPSHOT_VOLUME"}, - {STATUS_NO_SAVEPOINT_WITH_OPEN_FILES, -EIO, - "STATUS_NO_SAVEPOINT_WITH_OPEN_FILES"}, - {STATUS_SPARSE_NOT_ALLOWED_IN_TRANSACTION, -EIO, - "STATUS_SPARSE_NOT_ALLOWED_IN_TRANSACTION"}, - {STATUS_TM_IDENTITY_MISMATCH, -EIO, "STATUS_TM_IDENTITY_MISMATCH"}, - {STATUS_FLOATED_SECTION, -EIO, "STATUS_FLOATED_SECTION"}, - {STATUS_CANNOT_ACCEPT_TRANSACTED_WORK, -EIO, - "STATUS_CANNOT_ACCEPT_TRANSACTED_WORK"}, - {STATUS_CANNOT_ABORT_TRANSACTIONS, -EIO, - "STATUS_CANNOT_ABORT_TRANSACTIONS"}, - {STATUS_TRANSACTION_NOT_FOUND, -EIO, "STATUS_TRANSACTION_NOT_FOUND"}, - {STATUS_RESOURCEMANAGER_NOT_FOUND, -EIO, - "STATUS_RESOURCEMANAGER_NOT_FOUND"}, - {STATUS_ENLISTMENT_NOT_FOUND, -EIO, "STATUS_ENLISTMENT_NOT_FOUND"}, - {STATUS_TRANSACTIONMANAGER_NOT_FOUND, -EIO, - "STATUS_TRANSACTIONMANAGER_NOT_FOUND"}, - {STATUS_TRANSACTIONMANAGER_NOT_ONLINE, -EIO, - "STATUS_TRANSACTIONMANAGER_NOT_ONLINE"}, - {STATUS_TRANSACTIONMANAGER_RECOVERY_NAME_COLLISION, -EIO, - "STATUS_TRANSACTIONMANAGER_RECOVERY_NAME_COLLISION"}, - {STATUS_TRANSACTION_NOT_ROOT, -EIO, "STATUS_TRANSACTION_NOT_ROOT"}, - {STATUS_TRANSACTION_OBJECT_EXPIRED, -EIO, - "STATUS_TRANSACTION_OBJECT_EXPIRED"}, - {STATUS_COMPRESSION_NOT_ALLOWED_IN_TRANSACTION, -EIO, - "STATUS_COMPRESSION_NOT_ALLOWED_IN_TRANSACTION"}, - {STATUS_TRANSACTION_RESPONSE_NOT_ENLISTED, -EIO, - "STATUS_TRANSACTION_RESPONSE_NOT_ENLISTED"}, - {STATUS_TRANSACTION_RECORD_TOO_LONG, -EIO, - "STATUS_TRANSACTION_RECORD_TOO_LONG"}, - {STATUS_NO_LINK_TRACKING_IN_TRANSACTION, -EIO, - "STATUS_NO_LINK_TRACKING_IN_TRANSACTION"}, - {STATUS_OPERATION_NOT_SUPPORTED_IN_TRANSACTION, -EOPNOTSUPP, - "STATUS_OPERATION_NOT_SUPPORTED_IN_TRANSACTION"}, - {STATUS_TRANSACTION_INTEGRITY_VIOLATED, -EIO, - "STATUS_TRANSACTION_INTEGRITY_VIOLATED"}, - {STATUS_LOG_SECTOR_INVALID, -EIO, "STATUS_LOG_SECTOR_INVALID"}, - {STATUS_LOG_SECTOR_PARITY_INVALID, -EIO, - "STATUS_LOG_SECTOR_PARITY_INVALID"}, - {STATUS_LOG_SECTOR_REMAPPED, -EIO, "STATUS_LOG_SECTOR_REMAPPED"}, - {STATUS_LOG_BLOCK_INCOMPLETE, -EIO, "STATUS_LOG_BLOCK_INCOMPLETE"}, - {STATUS_LOG_INVALID_RANGE, -EIO, "STATUS_LOG_INVALID_RANGE"}, - {STATUS_LOG_BLOCKS_EXHAUSTED, -EIO, "STATUS_LOG_BLOCKS_EXHAUSTED"}, - {STATUS_LOG_READ_CONTEXT_INVALID, -EIO, - "STATUS_LOG_READ_CONTEXT_INVALID"}, - {STATUS_LOG_RESTART_INVALID, -EIO, "STATUS_LOG_RESTART_INVALID"}, - {STATUS_LOG_BLOCK_VERSION, -EIO, "STATUS_LOG_BLOCK_VERSION"}, - {STATUS_LOG_BLOCK_INVALID, -EIO, "STATUS_LOG_BLOCK_INVALID"}, - {STATUS_LOG_READ_MODE_INVALID, -EIO, "STATUS_LOG_READ_MODE_INVALID"}, - {STATUS_LOG_METADATA_CORRUPT, -EIO, "STATUS_LOG_METADATA_CORRUPT"}, - {STATUS_LOG_METADATA_INVALID, -EIO, "STATUS_LOG_METADATA_INVALID"}, - {STATUS_LOG_METADATA_INCONSISTENT, -EIO, - "STATUS_LOG_METADATA_INCONSISTENT"}, - {STATUS_LOG_RESERVATION_INVALID, -EIO, - "STATUS_LOG_RESERVATION_INVALID"}, - {STATUS_LOG_CANT_DELETE, -EIO, "STATUS_LOG_CANT_DELETE"}, - {STATUS_LOG_CONTAINER_LIMIT_EXCEEDED, -EIO, - "STATUS_LOG_CONTAINER_LIMIT_EXCEEDED"}, - {STATUS_LOG_START_OF_LOG, -EIO, "STATUS_LOG_START_OF_LOG"}, - {STATUS_LOG_POLICY_ALREADY_INSTALLED, -EIO, - "STATUS_LOG_POLICY_ALREADY_INSTALLED"}, - {STATUS_LOG_POLICY_NOT_INSTALLED, -EIO, - "STATUS_LOG_POLICY_NOT_INSTALLED"}, - {STATUS_LOG_POLICY_INVALID, -EIO, "STATUS_LOG_POLICY_INVALID"}, - {STATUS_LOG_POLICY_CONFLICT, -EIO, "STATUS_LOG_POLICY_CONFLICT"}, - {STATUS_LOG_PINNED_ARCHIVE_TAIL, -EIO, - "STATUS_LOG_PINNED_ARCHIVE_TAIL"}, - {STATUS_LOG_RECORD_NONEXISTENT, -EIO, "STATUS_LOG_RECORD_NONEXISTENT"}, - {STATUS_LOG_RECORDS_RESERVED_INVALID, -EIO, - "STATUS_LOG_RECORDS_RESERVED_INVALID"}, - {STATUS_LOG_SPACE_RESERVED_INVALID, -EIO, - "STATUS_LOG_SPACE_RESERVED_INVALID"}, - {STATUS_LOG_TAIL_INVALID, -EIO, "STATUS_LOG_TAIL_INVALID"}, - {STATUS_LOG_FULL, -EIO, "STATUS_LOG_FULL"}, - {STATUS_LOG_MULTIPLEXED, -EIO, "STATUS_LOG_MULTIPLEXED"}, - {STATUS_LOG_DEDICATED, -EIO, "STATUS_LOG_DEDICATED"}, - {STATUS_LOG_ARCHIVE_NOT_IN_PROGRESS, -EIO, - "STATUS_LOG_ARCHIVE_NOT_IN_PROGRESS"}, - {STATUS_LOG_ARCHIVE_IN_PROGRESS, -EIO, - "STATUS_LOG_ARCHIVE_IN_PROGRESS"}, - {STATUS_LOG_EPHEMERAL, -EIO, "STATUS_LOG_EPHEMERAL"}, - {STATUS_LOG_NOT_ENOUGH_CONTAINERS, -EIO, - "STATUS_LOG_NOT_ENOUGH_CONTAINERS"}, - {STATUS_LOG_CLIENT_ALREADY_REGISTERED, -EIO, - "STATUS_LOG_CLIENT_ALREADY_REGISTERED"}, - {STATUS_LOG_CLIENT_NOT_REGISTERED, -EIO, - "STATUS_LOG_CLIENT_NOT_REGISTERED"}, - {STATUS_LOG_FULL_HANDLER_IN_PROGRESS, -EIO, - "STATUS_LOG_FULL_HANDLER_IN_PROGRESS"}, - {STATUS_LOG_CONTAINER_READ_FAILED, -EIO, - "STATUS_LOG_CONTAINER_READ_FAILED"}, - {STATUS_LOG_CONTAINER_WRITE_FAILED, -EIO, - "STATUS_LOG_CONTAINER_WRITE_FAILED"}, - {STATUS_LOG_CONTAINER_OPEN_FAILED, -EIO, - "STATUS_LOG_CONTAINER_OPEN_FAILED"}, - {STATUS_LOG_CONTAINER_STATE_INVALID, -EIO, - "STATUS_LOG_CONTAINER_STATE_INVALID"}, - {STATUS_LOG_STATE_INVALID, -EIO, "STATUS_LOG_STATE_INVALID"}, - {STATUS_LOG_PINNED, -EIO, "STATUS_LOG_PINNED"}, - {STATUS_LOG_METADATA_FLUSH_FAILED, -EIO, - "STATUS_LOG_METADATA_FLUSH_FAILED"}, - {STATUS_LOG_INCONSISTENT_SECURITY, -EIO, - "STATUS_LOG_INCONSISTENT_SECURITY"}, - {STATUS_LOG_APPENDED_FLUSH_FAILED, -EIO, - "STATUS_LOG_APPENDED_FLUSH_FAILED"}, - {STATUS_LOG_PINNED_RESERVATION, -EIO, "STATUS_LOG_PINNED_RESERVATION"}, - {STATUS_VIDEO_HUNG_DISPLAY_DRIVER_THREAD, -EIO, - "STATUS_VIDEO_HUNG_DISPLAY_DRIVER_THREAD"}, - {STATUS_FLT_NO_HANDLER_DEFINED, -EIO, "STATUS_FLT_NO_HANDLER_DEFINED"}, - {STATUS_FLT_CONTEXT_ALREADY_DEFINED, -EIO, - "STATUS_FLT_CONTEXT_ALREADY_DEFINED"}, - {STATUS_FLT_INVALID_ASYNCHRONOUS_REQUEST, -EIO, - "STATUS_FLT_INVALID_ASYNCHRONOUS_REQUEST"}, - {STATUS_FLT_DISALLOW_FAST_IO, -EIO, "STATUS_FLT_DISALLOW_FAST_IO"}, - {STATUS_FLT_INVALID_NAME_REQUEST, -EIO, - "STATUS_FLT_INVALID_NAME_REQUEST"}, - {STATUS_FLT_NOT_SAFE_TO_POST_OPERATION, -EIO, - "STATUS_FLT_NOT_SAFE_TO_POST_OPERATION"}, - {STATUS_FLT_NOT_INITIALIZED, -EIO, "STATUS_FLT_NOT_INITIALIZED"}, - {STATUS_FLT_FILTER_NOT_READY, -EIO, "STATUS_FLT_FILTER_NOT_READY"}, - {STATUS_FLT_POST_OPERATION_CLEANUP, -EIO, - "STATUS_FLT_POST_OPERATION_CLEANUP"}, - {STATUS_FLT_INTERNAL_ERROR, -EIO, "STATUS_FLT_INTERNAL_ERROR"}, - {STATUS_FLT_DELETING_OBJECT, -EIO, "STATUS_FLT_DELETING_OBJECT"}, - {STATUS_FLT_MUST_BE_NONPAGED_POOL, -EIO, - "STATUS_FLT_MUST_BE_NONPAGED_POOL"}, - {STATUS_FLT_DUPLICATE_ENTRY, -EIO, "STATUS_FLT_DUPLICATE_ENTRY"}, - {STATUS_FLT_CBDQ_DISABLED, -EIO, "STATUS_FLT_CBDQ_DISABLED"}, - {STATUS_FLT_DO_NOT_ATTACH, -EIO, "STATUS_FLT_DO_NOT_ATTACH"}, - {STATUS_FLT_DO_NOT_DETACH, -EIO, "STATUS_FLT_DO_NOT_DETACH"}, - {STATUS_FLT_INSTANCE_ALTITUDE_COLLISION, -EIO, - "STATUS_FLT_INSTANCE_ALTITUDE_COLLISION"}, - {STATUS_FLT_INSTANCE_NAME_COLLISION, -EIO, - "STATUS_FLT_INSTANCE_NAME_COLLISION"}, - {STATUS_FLT_FILTER_NOT_FOUND, -EIO, "STATUS_FLT_FILTER_NOT_FOUND"}, - {STATUS_FLT_VOLUME_NOT_FOUND, -EIO, "STATUS_FLT_VOLUME_NOT_FOUND"}, - {STATUS_FLT_INSTANCE_NOT_FOUND, -EIO, "STATUS_FLT_INSTANCE_NOT_FOUND"}, - {STATUS_FLT_CONTEXT_ALLOCATION_NOT_FOUND, -EIO, - "STATUS_FLT_CONTEXT_ALLOCATION_NOT_FOUND"}, - {STATUS_FLT_INVALID_CONTEXT_REGISTRATION, -EIO, - "STATUS_FLT_INVALID_CONTEXT_REGISTRATION"}, - {STATUS_FLT_NAME_CACHE_MISS, -EIO, "STATUS_FLT_NAME_CACHE_MISS"}, - {STATUS_FLT_NO_DEVICE_OBJECT, -EIO, "STATUS_FLT_NO_DEVICE_OBJECT"}, - {STATUS_FLT_VOLUME_ALREADY_MOUNTED, -EIO, - "STATUS_FLT_VOLUME_ALREADY_MOUNTED"}, - {STATUS_FLT_ALREADY_ENLISTED, -EIO, "STATUS_FLT_ALREADY_ENLISTED"}, - {STATUS_FLT_CONTEXT_ALREADY_LINKED, -EIO, - "STATUS_FLT_CONTEXT_ALREADY_LINKED"}, - {STATUS_FLT_NO_WAITER_FOR_REPLY, -EIO, - "STATUS_FLT_NO_WAITER_FOR_REPLY"}, - {STATUS_MONITOR_NO_DESCRIPTOR, -EIO, "STATUS_MONITOR_NO_DESCRIPTOR"}, - {STATUS_MONITOR_UNKNOWN_DESCRIPTOR_FORMAT, -EIO, - "STATUS_MONITOR_UNKNOWN_DESCRIPTOR_FORMAT"}, - {STATUS_MONITOR_INVALID_DESCRIPTOR_CHECKSUM, -EIO, - "STATUS_MONITOR_INVALID_DESCRIPTOR_CHECKSUM"}, - {STATUS_MONITOR_INVALID_STANDARD_TIMING_BLOCK, -EIO, - "STATUS_MONITOR_INVALID_STANDARD_TIMING_BLOCK"}, - {STATUS_MONITOR_WMI_DATABLOCK_REGISTRATION_FAILED, -EIO, - "STATUS_MONITOR_WMI_DATABLOCK_REGISTRATION_FAILED"}, - {STATUS_MONITOR_INVALID_SERIAL_NUMBER_MONDSC_BLOCK, -EIO, - "STATUS_MONITOR_INVALID_SERIAL_NUMBER_MONDSC_BLOCK"}, - {STATUS_MONITOR_INVALID_USER_FRIENDLY_MONDSC_BLOCK, -EIO, - "STATUS_MONITOR_INVALID_USER_FRIENDLY_MONDSC_BLOCK"}, - {STATUS_MONITOR_NO_MORE_DESCRIPTOR_DATA, -EIO, - "STATUS_MONITOR_NO_MORE_DESCRIPTOR_DATA"}, - {STATUS_MONITOR_INVALID_DETAILED_TIMING_BLOCK, -EIO, - "STATUS_MONITOR_INVALID_DETAILED_TIMING_BLOCK"}, - {STATUS_GRAPHICS_NOT_EXCLUSIVE_MODE_OWNER, -EIO, - "STATUS_GRAPHICS_NOT_EXCLUSIVE_MODE_OWNER"}, - {STATUS_GRAPHICS_INSUFFICIENT_DMA_BUFFER, -EIO, - "STATUS_GRAPHICS_INSUFFICIENT_DMA_BUFFER"}, - {STATUS_GRAPHICS_INVALID_DISPLAY_ADAPTER, -EIO, - "STATUS_GRAPHICS_INVALID_DISPLAY_ADAPTER"}, - {STATUS_GRAPHICS_ADAPTER_WAS_RESET, -EIO, - "STATUS_GRAPHICS_ADAPTER_WAS_RESET"}, - {STATUS_GRAPHICS_INVALID_DRIVER_MODEL, -EIO, - "STATUS_GRAPHICS_INVALID_DRIVER_MODEL"}, - {STATUS_GRAPHICS_PRESENT_MODE_CHANGED, -EIO, - "STATUS_GRAPHICS_PRESENT_MODE_CHANGED"}, - {STATUS_GRAPHICS_PRESENT_OCCLUDED, -EIO, - "STATUS_GRAPHICS_PRESENT_OCCLUDED"}, - {STATUS_GRAPHICS_PRESENT_DENIED, -EIO, - "STATUS_GRAPHICS_PRESENT_DENIED"}, - {STATUS_GRAPHICS_CANNOTCOLORCONVERT, -EIO, - "STATUS_GRAPHICS_CANNOTCOLORCONVERT"}, - {STATUS_GRAPHICS_NO_VIDEO_MEMORY, -EIO, - "STATUS_GRAPHICS_NO_VIDEO_MEMORY"}, - {STATUS_GRAPHICS_CANT_LOCK_MEMORY, -EIO, - "STATUS_GRAPHICS_CANT_LOCK_MEMORY"}, - {STATUS_GRAPHICS_ALLOCATION_BUSY, -EBUSY, - "STATUS_GRAPHICS_ALLOCATION_BUSY"}, - {STATUS_GRAPHICS_TOO_MANY_REFERENCES, -EIO, - "STATUS_GRAPHICS_TOO_MANY_REFERENCES"}, - {STATUS_GRAPHICS_TRY_AGAIN_LATER, -EIO, - "STATUS_GRAPHICS_TRY_AGAIN_LATER"}, - {STATUS_GRAPHICS_TRY_AGAIN_NOW, -EIO, "STATUS_GRAPHICS_TRY_AGAIN_NOW"}, - {STATUS_GRAPHICS_ALLOCATION_INVALID, -EIO, - "STATUS_GRAPHICS_ALLOCATION_INVALID"}, - {STATUS_GRAPHICS_UNSWIZZLING_APERTURE_UNAVAILABLE, -EIO, - "STATUS_GRAPHICS_UNSWIZZLING_APERTURE_UNAVAILABLE"}, - {STATUS_GRAPHICS_UNSWIZZLING_APERTURE_UNSUPPORTED, -EIO, - "STATUS_GRAPHICS_UNSWIZZLING_APERTURE_UNSUPPORTED"}, - {STATUS_GRAPHICS_CANT_EVICT_PINNED_ALLOCATION, -EIO, - "STATUS_GRAPHICS_CANT_EVICT_PINNED_ALLOCATION"}, - {STATUS_GRAPHICS_INVALID_ALLOCATION_USAGE, -EIO, - "STATUS_GRAPHICS_INVALID_ALLOCATION_USAGE"}, - {STATUS_GRAPHICS_CANT_RENDER_LOCKED_ALLOCATION, -EIO, - "STATUS_GRAPHICS_CANT_RENDER_LOCKED_ALLOCATION"}, - {STATUS_GRAPHICS_ALLOCATION_CLOSED, -EIO, - "STATUS_GRAPHICS_ALLOCATION_CLOSED"}, - {STATUS_GRAPHICS_INVALID_ALLOCATION_INSTANCE, -EIO, - "STATUS_GRAPHICS_INVALID_ALLOCATION_INSTANCE"}, - {STATUS_GRAPHICS_INVALID_ALLOCATION_HANDLE, -EIO, - "STATUS_GRAPHICS_INVALID_ALLOCATION_HANDLE"}, - {STATUS_GRAPHICS_WRONG_ALLOCATION_DEVICE, -EIO, - "STATUS_GRAPHICS_WRONG_ALLOCATION_DEVICE"}, - {STATUS_GRAPHICS_ALLOCATION_CONTENT_LOST, -EIO, - "STATUS_GRAPHICS_ALLOCATION_CONTENT_LOST"}, - {STATUS_GRAPHICS_GPU_EXCEPTION_ON_DEVICE, -EIO, - "STATUS_GRAPHICS_GPU_EXCEPTION_ON_DEVICE"}, - {STATUS_GRAPHICS_INVALID_VIDPN_TOPOLOGY, -EIO, - "STATUS_GRAPHICS_INVALID_VIDPN_TOPOLOGY"}, - {STATUS_GRAPHICS_VIDPN_TOPOLOGY_NOT_SUPPORTED, -EIO, - "STATUS_GRAPHICS_VIDPN_TOPOLOGY_NOT_SUPPORTED"}, - {STATUS_GRAPHICS_VIDPN_TOPOLOGY_CURRENTLY_NOT_SUPPORTED, -EIO, - "STATUS_GRAPHICS_VIDPN_TOPOLOGY_CURRENTLY_NOT_SUPPORTED"}, - {STATUS_GRAPHICS_INVALID_VIDPN, -EIO, "STATUS_GRAPHICS_INVALID_VIDPN"}, - {STATUS_GRAPHICS_INVALID_VIDEO_PRESENT_SOURCE, -EIO, - "STATUS_GRAPHICS_INVALID_VIDEO_PRESENT_SOURCE"}, - {STATUS_GRAPHICS_INVALID_VIDEO_PRESENT_TARGET, -EIO, - "STATUS_GRAPHICS_INVALID_VIDEO_PRESENT_TARGET"}, - {STATUS_GRAPHICS_VIDPN_MODALITY_NOT_SUPPORTED, -EIO, - "STATUS_GRAPHICS_VIDPN_MODALITY_NOT_SUPPORTED"}, - {STATUS_GRAPHICS_INVALID_VIDPN_SOURCEMODESET, -EIO, - "STATUS_GRAPHICS_INVALID_VIDPN_SOURCEMODESET"}, - {STATUS_GRAPHICS_INVALID_VIDPN_TARGETMODESET, -EIO, - "STATUS_GRAPHICS_INVALID_VIDPN_TARGETMODESET"}, - {STATUS_GRAPHICS_INVALID_FREQUENCY, -EIO, - "STATUS_GRAPHICS_INVALID_FREQUENCY"}, - {STATUS_GRAPHICS_INVALID_ACTIVE_REGION, -EIO, - "STATUS_GRAPHICS_INVALID_ACTIVE_REGION"}, - {STATUS_GRAPHICS_INVALID_TOTAL_REGION, -EIO, - "STATUS_GRAPHICS_INVALID_TOTAL_REGION"}, - {STATUS_GRAPHICS_INVALID_VIDEO_PRESENT_SOURCE_MODE, -EIO, - "STATUS_GRAPHICS_INVALID_VIDEO_PRESENT_SOURCE_MODE"}, - {STATUS_GRAPHICS_INVALID_VIDEO_PRESENT_TARGET_MODE, -EIO, - "STATUS_GRAPHICS_INVALID_VIDEO_PRESENT_TARGET_MODE"}, - {STATUS_GRAPHICS_PINNED_MODE_MUST_REMAIN_IN_SET, -EIO, - "STATUS_GRAPHICS_PINNED_MODE_MUST_REMAIN_IN_SET"}, - {STATUS_GRAPHICS_PATH_ALREADY_IN_TOPOLOGY, -EIO, - "STATUS_GRAPHICS_PATH_ALREADY_IN_TOPOLOGY"}, - {STATUS_GRAPHICS_MODE_ALREADY_IN_MODESET, -EIO, - "STATUS_GRAPHICS_MODE_ALREADY_IN_MODESET"}, - {STATUS_GRAPHICS_INVALID_VIDEOPRESENTSOURCESET, -EIO, - "STATUS_GRAPHICS_INVALID_VIDEOPRESENTSOURCESET"}, - {STATUS_GRAPHICS_INVALID_VIDEOPRESENTTARGETSET, -EIO, - "STATUS_GRAPHICS_INVALID_VIDEOPRESENTTARGETSET"}, - {STATUS_GRAPHICS_SOURCE_ALREADY_IN_SET, -EIO, - "STATUS_GRAPHICS_SOURCE_ALREADY_IN_SET"}, - {STATUS_GRAPHICS_TARGET_ALREADY_IN_SET, -EIO, - "STATUS_GRAPHICS_TARGET_ALREADY_IN_SET"}, - {STATUS_GRAPHICS_INVALID_VIDPN_PRESENT_PATH, -EIO, - "STATUS_GRAPHICS_INVALID_VIDPN_PRESENT_PATH"}, - {STATUS_GRAPHICS_NO_RECOMMENDED_VIDPN_TOPOLOGY, -EIO, - "STATUS_GRAPHICS_NO_RECOMMENDED_VIDPN_TOPOLOGY"}, - {STATUS_GRAPHICS_INVALID_MONITOR_FREQUENCYRANGESET, -EIO, - "STATUS_GRAPHICS_INVALID_MONITOR_FREQUENCYRANGESET"}, - {STATUS_GRAPHICS_INVALID_MONITOR_FREQUENCYRANGE, -EIO, - "STATUS_GRAPHICS_INVALID_MONITOR_FREQUENCYRANGE"}, - {STATUS_GRAPHICS_FREQUENCYRANGE_NOT_IN_SET, -EIO, - "STATUS_GRAPHICS_FREQUENCYRANGE_NOT_IN_SET"}, - {STATUS_GRAPHICS_FREQUENCYRANGE_ALREADY_IN_SET, -EIO, - "STATUS_GRAPHICS_FREQUENCYRANGE_ALREADY_IN_SET"}, - {STATUS_GRAPHICS_STALE_MODESET, -EIO, "STATUS_GRAPHICS_STALE_MODESET"}, - {STATUS_GRAPHICS_INVALID_MONITOR_SOURCEMODESET, -EIO, - "STATUS_GRAPHICS_INVALID_MONITOR_SOURCEMODESET"}, - {STATUS_GRAPHICS_INVALID_MONITOR_SOURCE_MODE, -EIO, - "STATUS_GRAPHICS_INVALID_MONITOR_SOURCE_MODE"}, - {STATUS_GRAPHICS_NO_RECOMMENDED_FUNCTIONAL_VIDPN, -EIO, - "STATUS_GRAPHICS_NO_RECOMMENDED_FUNCTIONAL_VIDPN"}, - {STATUS_GRAPHICS_MODE_ID_MUST_BE_UNIQUE, -EIO, - "STATUS_GRAPHICS_MODE_ID_MUST_BE_UNIQUE"}, - {STATUS_GRAPHICS_EMPTY_ADAPTER_MONITOR_MODE_SUPPORT_INTERSECTION, -EIO, - "STATUS_GRAPHICS_EMPTY_ADAPTER_MONITOR_MODE_SUPPORT_INTERSECTION"}, - {STATUS_GRAPHICS_VIDEO_PRESENT_TARGETS_LESS_THAN_SOURCES, -EIO, - "STATUS_GRAPHICS_VIDEO_PRESENT_TARGETS_LESS_THAN_SOURCES"}, - {STATUS_GRAPHICS_PATH_NOT_IN_TOPOLOGY, -EIO, - "STATUS_GRAPHICS_PATH_NOT_IN_TOPOLOGY"}, - {STATUS_GRAPHICS_ADAPTER_MUST_HAVE_AT_LEAST_ONE_SOURCE, -EIO, - "STATUS_GRAPHICS_ADAPTER_MUST_HAVE_AT_LEAST_ONE_SOURCE"}, - {STATUS_GRAPHICS_ADAPTER_MUST_HAVE_AT_LEAST_ONE_TARGET, -EIO, - "STATUS_GRAPHICS_ADAPTER_MUST_HAVE_AT_LEAST_ONE_TARGET"}, - {STATUS_GRAPHICS_INVALID_MONITORDESCRIPTORSET, -EIO, - "STATUS_GRAPHICS_INVALID_MONITORDESCRIPTORSET"}, - {STATUS_GRAPHICS_INVALID_MONITORDESCRIPTOR, -EIO, - "STATUS_GRAPHICS_INVALID_MONITORDESCRIPTOR"}, - {STATUS_GRAPHICS_MONITORDESCRIPTOR_NOT_IN_SET, -EIO, - "STATUS_GRAPHICS_MONITORDESCRIPTOR_NOT_IN_SET"}, - {STATUS_GRAPHICS_MONITORDESCRIPTOR_ALREADY_IN_SET, -EIO, - "STATUS_GRAPHICS_MONITORDESCRIPTOR_ALREADY_IN_SET"}, - {STATUS_GRAPHICS_MONITORDESCRIPTOR_ID_MUST_BE_UNIQUE, -EIO, - "STATUS_GRAPHICS_MONITORDESCRIPTOR_ID_MUST_BE_UNIQUE"}, - {STATUS_GRAPHICS_INVALID_VIDPN_TARGET_SUBSET_TYPE, -EIO, - "STATUS_GRAPHICS_INVALID_VIDPN_TARGET_SUBSET_TYPE"}, - {STATUS_GRAPHICS_RESOURCES_NOT_RELATED, -EIO, - "STATUS_GRAPHICS_RESOURCES_NOT_RELATED"}, - {STATUS_GRAPHICS_SOURCE_ID_MUST_BE_UNIQUE, -EIO, - "STATUS_GRAPHICS_SOURCE_ID_MUST_BE_UNIQUE"}, - {STATUS_GRAPHICS_TARGET_ID_MUST_BE_UNIQUE, -EIO, - "STATUS_GRAPHICS_TARGET_ID_MUST_BE_UNIQUE"}, - {STATUS_GRAPHICS_NO_AVAILABLE_VIDPN_TARGET, -EIO, - "STATUS_GRAPHICS_NO_AVAILABLE_VIDPN_TARGET"}, - {STATUS_GRAPHICS_MONITOR_COULD_NOT_BE_ASSOCIATED_WITH_ADAPTER, -EIO, - "STATUS_GRAPHICS_MONITOR_COULD_NOT_BE_ASSOCIATED_WITH_ADAPTER"}, - {STATUS_GRAPHICS_NO_VIDPNMGR, -EIO, "STATUS_GRAPHICS_NO_VIDPNMGR"}, - {STATUS_GRAPHICS_NO_ACTIVE_VIDPN, -EIO, - "STATUS_GRAPHICS_NO_ACTIVE_VIDPN"}, - {STATUS_GRAPHICS_STALE_VIDPN_TOPOLOGY, -EIO, - "STATUS_GRAPHICS_STALE_VIDPN_TOPOLOGY"}, - {STATUS_GRAPHICS_MONITOR_NOT_CONNECTED, -EIO, - "STATUS_GRAPHICS_MONITOR_NOT_CONNECTED"}, - {STATUS_GRAPHICS_SOURCE_NOT_IN_TOPOLOGY, -EIO, - "STATUS_GRAPHICS_SOURCE_NOT_IN_TOPOLOGY"}, - {STATUS_GRAPHICS_INVALID_PRIMARYSURFACE_SIZE, -EIO, - "STATUS_GRAPHICS_INVALID_PRIMARYSURFACE_SIZE"}, - {STATUS_GRAPHICS_INVALID_VISIBLEREGION_SIZE, -EIO, - "STATUS_GRAPHICS_INVALID_VISIBLEREGION_SIZE"}, - {STATUS_GRAPHICS_INVALID_STRIDE, -EIO, - "STATUS_GRAPHICS_INVALID_STRIDE"}, - {STATUS_GRAPHICS_INVALID_PIXELFORMAT, -EIO, - "STATUS_GRAPHICS_INVALID_PIXELFORMAT"}, - {STATUS_GRAPHICS_INVALID_COLORBASIS, -EIO, - "STATUS_GRAPHICS_INVALID_COLORBASIS"}, - {STATUS_GRAPHICS_INVALID_PIXELVALUEACCESSMODE, -EIO, - "STATUS_GRAPHICS_INVALID_PIXELVALUEACCESSMODE"}, - {STATUS_GRAPHICS_TARGET_NOT_IN_TOPOLOGY, -EIO, - "STATUS_GRAPHICS_TARGET_NOT_IN_TOPOLOGY"}, - {STATUS_GRAPHICS_NO_DISPLAY_MODE_MANAGEMENT_SUPPORT, -EIO, - "STATUS_GRAPHICS_NO_DISPLAY_MODE_MANAGEMENT_SUPPORT"}, - {STATUS_GRAPHICS_VIDPN_SOURCE_IN_USE, -EIO, - "STATUS_GRAPHICS_VIDPN_SOURCE_IN_USE"}, - {STATUS_GRAPHICS_CANT_ACCESS_ACTIVE_VIDPN, -EIO, - "STATUS_GRAPHICS_CANT_ACCESS_ACTIVE_VIDPN"}, - {STATUS_GRAPHICS_INVALID_PATH_IMPORTANCE_ORDINAL, -EIO, - "STATUS_GRAPHICS_INVALID_PATH_IMPORTANCE_ORDINAL"}, - {STATUS_GRAPHICS_INVALID_PATH_CONTENT_GEOMETRY_TRANSFORMATION, -EIO, - "STATUS_GRAPHICS_INVALID_PATH_CONTENT_GEOMETRY_TRANSFORMATION"}, - {STATUS_GRAPHICS_PATH_CONTENT_GEOMETRY_TRANSFORMATION_NOT_SUPPORTED, - -EIO, - "STATUS_GRAPHICS_PATH_CONTENT_GEOMETRY_TRANSFORMATION_NOT_SUPPORTED"}, - {STATUS_GRAPHICS_INVALID_GAMMA_RAMP, -EIO, - "STATUS_GRAPHICS_INVALID_GAMMA_RAMP"}, - {STATUS_GRAPHICS_GAMMA_RAMP_NOT_SUPPORTED, -EIO, - "STATUS_GRAPHICS_GAMMA_RAMP_NOT_SUPPORTED"}, - {STATUS_GRAPHICS_MULTISAMPLING_NOT_SUPPORTED, -EIO, - "STATUS_GRAPHICS_MULTISAMPLING_NOT_SUPPORTED"}, - {STATUS_GRAPHICS_MODE_NOT_IN_MODESET, -EIO, - "STATUS_GRAPHICS_MODE_NOT_IN_MODESET"}, - {STATUS_GRAPHICS_INVALID_VIDPN_TOPOLOGY_RECOMMENDATION_REASON, -EIO, - "STATUS_GRAPHICS_INVALID_VIDPN_TOPOLOGY_RECOMMENDATION_REASON"}, - {STATUS_GRAPHICS_INVALID_PATH_CONTENT_TYPE, -EIO, - "STATUS_GRAPHICS_INVALID_PATH_CONTENT_TYPE"}, - {STATUS_GRAPHICS_INVALID_COPYPROTECTION_TYPE, -EIO, - "STATUS_GRAPHICS_INVALID_COPYPROTECTION_TYPE"}, - {STATUS_GRAPHICS_UNASSIGNED_MODESET_ALREADY_EXISTS, -EIO, - "STATUS_GRAPHICS_UNASSIGNED_MODESET_ALREADY_EXISTS"}, - {STATUS_GRAPHICS_INVALID_SCANLINE_ORDERING, -EIO, - "STATUS_GRAPHICS_INVALID_SCANLINE_ORDERING"}, - {STATUS_GRAPHICS_TOPOLOGY_CHANGES_NOT_ALLOWED, -EIO, - "STATUS_GRAPHICS_TOPOLOGY_CHANGES_NOT_ALLOWED"}, - {STATUS_GRAPHICS_NO_AVAILABLE_IMPORTANCE_ORDINALS, -EIO, - "STATUS_GRAPHICS_NO_AVAILABLE_IMPORTANCE_ORDINALS"}, - {STATUS_GRAPHICS_INCOMPATIBLE_PRIVATE_FORMAT, -EIO, - "STATUS_GRAPHICS_INCOMPATIBLE_PRIVATE_FORMAT"}, - {STATUS_GRAPHICS_INVALID_MODE_PRUNING_ALGORITHM, -EIO, - "STATUS_GRAPHICS_INVALID_MODE_PRUNING_ALGORITHM"}, - {STATUS_GRAPHICS_INVALID_MONITOR_CAPABILITY_ORIGIN, -EIO, - "STATUS_GRAPHICS_INVALID_MONITOR_CAPABILITY_ORIGIN"}, - {STATUS_GRAPHICS_INVALID_MONITOR_FREQUENCYRANGE_CONSTRAINT, -EIO, - "STATUS_GRAPHICS_INVALID_MONITOR_FREQUENCYRANGE_CONSTRAINT"}, - {STATUS_GRAPHICS_MAX_NUM_PATHS_REACHED, -EIO, - "STATUS_GRAPHICS_MAX_NUM_PATHS_REACHED"}, - {STATUS_GRAPHICS_CANCEL_VIDPN_TOPOLOGY_AUGMENTATION, -EIO, - "STATUS_GRAPHICS_CANCEL_VIDPN_TOPOLOGY_AUGMENTATION"}, - {STATUS_GRAPHICS_INVALID_CLIENT_TYPE, -EIO, - "STATUS_GRAPHICS_INVALID_CLIENT_TYPE"}, - {STATUS_GRAPHICS_CLIENTVIDPN_NOT_SET, -EIO, - "STATUS_GRAPHICS_CLIENTVIDPN_NOT_SET"}, - {STATUS_GRAPHICS_SPECIFIED_CHILD_ALREADY_CONNECTED, -EIO, - "STATUS_GRAPHICS_SPECIFIED_CHILD_ALREADY_CONNECTED"}, - {STATUS_GRAPHICS_CHILD_DESCRIPTOR_NOT_SUPPORTED, -EIO, - "STATUS_GRAPHICS_CHILD_DESCRIPTOR_NOT_SUPPORTED"}, - {STATUS_GRAPHICS_NOT_A_LINKED_ADAPTER, -EIO, - "STATUS_GRAPHICS_NOT_A_LINKED_ADAPTER"}, - {STATUS_GRAPHICS_LEADLINK_NOT_ENUMERATED, -EIO, - "STATUS_GRAPHICS_LEADLINK_NOT_ENUMERATED"}, - {STATUS_GRAPHICS_CHAINLINKS_NOT_ENUMERATED, -EIO, - "STATUS_GRAPHICS_CHAINLINKS_NOT_ENUMERATED"}, - {STATUS_GRAPHICS_ADAPTER_CHAIN_NOT_READY, -EIO, - "STATUS_GRAPHICS_ADAPTER_CHAIN_NOT_READY"}, - {STATUS_GRAPHICS_CHAINLINKS_NOT_STARTED, -EIO, - "STATUS_GRAPHICS_CHAINLINKS_NOT_STARTED"}, - {STATUS_GRAPHICS_CHAINLINKS_NOT_POWERED_ON, -EIO, - "STATUS_GRAPHICS_CHAINLINKS_NOT_POWERED_ON"}, - {STATUS_GRAPHICS_INCONSISTENT_DEVICE_LINK_STATE, -EIO, - "STATUS_GRAPHICS_INCONSISTENT_DEVICE_LINK_STATE"}, - {STATUS_GRAPHICS_NOT_POST_DEVICE_DRIVER, -EIO, - "STATUS_GRAPHICS_NOT_POST_DEVICE_DRIVER"}, - {STATUS_GRAPHICS_ADAPTER_ACCESS_NOT_EXCLUDED, -EIO, - "STATUS_GRAPHICS_ADAPTER_ACCESS_NOT_EXCLUDED"}, - {STATUS_GRAPHICS_OPM_PROTECTED_OUTPUT_DOES_NOT_HAVE_COPP_SEMANTICS, - -EIO, - "STATUS_GRAPHICS_OPM_PROTECTED_OUTPUT_DOES_NOT_HAVE_COPP_SEMANTICS"}, - {STATUS_GRAPHICS_OPM_INVALID_INFORMATION_REQUEST, -EIO, - "STATUS_GRAPHICS_OPM_INVALID_INFORMATION_REQUEST"}, - {STATUS_GRAPHICS_OPM_DRIVER_INTERNAL_ERROR, -EIO, - "STATUS_GRAPHICS_OPM_DRIVER_INTERNAL_ERROR"}, - {STATUS_GRAPHICS_OPM_PROTECTED_OUTPUT_DOES_NOT_HAVE_OPM_SEMANTICS, -EIO, - "STATUS_GRAPHICS_OPM_PROTECTED_OUTPUT_DOES_NOT_HAVE_OPM_SEMANTICS"}, - {STATUS_GRAPHICS_OPM_SIGNALING_NOT_SUPPORTED, -EIO, - "STATUS_GRAPHICS_OPM_SIGNALING_NOT_SUPPORTED"}, - {STATUS_GRAPHICS_OPM_INVALID_CONFIGURATION_REQUEST, -EIO, - "STATUS_GRAPHICS_OPM_INVALID_CONFIGURATION_REQUEST"}, - {STATUS_GRAPHICS_OPM_NOT_SUPPORTED, -EIO, - "STATUS_GRAPHICS_OPM_NOT_SUPPORTED"}, - {STATUS_GRAPHICS_COPP_NOT_SUPPORTED, -EIO, - "STATUS_GRAPHICS_COPP_NOT_SUPPORTED"}, - {STATUS_GRAPHICS_UAB_NOT_SUPPORTED, -EIO, - "STATUS_GRAPHICS_UAB_NOT_SUPPORTED"}, - {STATUS_GRAPHICS_OPM_INVALID_ENCRYPTED_PARAMETERS, -EIO, - "STATUS_GRAPHICS_OPM_INVALID_ENCRYPTED_PARAMETERS"}, - {STATUS_GRAPHICS_OPM_PARAMETER_ARRAY_TOO_SMALL, -EIO, - "STATUS_GRAPHICS_OPM_PARAMETER_ARRAY_TOO_SMALL"}, - {STATUS_GRAPHICS_OPM_NO_PROTECTED_OUTPUTS_EXIST, -EIO, - "STATUS_GRAPHICS_OPM_NO_PROTECTED_OUTPUTS_EXIST"}, - {STATUS_GRAPHICS_PVP_NO_DISPLAY_DEVICE_CORRESPONDS_TO_NAME, -EIO, - "STATUS_GRAPHICS_PVP_NO_DISPLAY_DEVICE_CORRESPONDS_TO_NAME"}, - {STATUS_GRAPHICS_PVP_DISPLAY_DEVICE_NOT_ATTACHED_TO_DESKTOP, -EIO, - "STATUS_GRAPHICS_PVP_DISPLAY_DEVICE_NOT_ATTACHED_TO_DESKTOP"}, - {STATUS_GRAPHICS_PVP_MIRRORING_DEVICES_NOT_SUPPORTED, -EIO, - "STATUS_GRAPHICS_PVP_MIRRORING_DEVICES_NOT_SUPPORTED"}, - {STATUS_GRAPHICS_OPM_INVALID_POINTER, -EIO, - "STATUS_GRAPHICS_OPM_INVALID_POINTER"}, - {STATUS_GRAPHICS_OPM_INTERNAL_ERROR, -EIO, - "STATUS_GRAPHICS_OPM_INTERNAL_ERROR"}, - {STATUS_GRAPHICS_OPM_INVALID_HANDLE, -EIO, - "STATUS_GRAPHICS_OPM_INVALID_HANDLE"}, - {STATUS_GRAPHICS_PVP_NO_MONITORS_CORRESPOND_TO_DISPLAY_DEVICE, -EIO, - "STATUS_GRAPHICS_PVP_NO_MONITORS_CORRESPOND_TO_DISPLAY_DEVICE"}, - {STATUS_GRAPHICS_PVP_INVALID_CERTIFICATE_LENGTH, -EIO, - "STATUS_GRAPHICS_PVP_INVALID_CERTIFICATE_LENGTH"}, - {STATUS_GRAPHICS_OPM_SPANNING_MODE_ENABLED, -EIO, - "STATUS_GRAPHICS_OPM_SPANNING_MODE_ENABLED"}, - {STATUS_GRAPHICS_OPM_THEATER_MODE_ENABLED, -EIO, - "STATUS_GRAPHICS_OPM_THEATER_MODE_ENABLED"}, - {STATUS_GRAPHICS_PVP_HFS_FAILED, -EIO, - "STATUS_GRAPHICS_PVP_HFS_FAILED"}, - {STATUS_GRAPHICS_OPM_INVALID_SRM, -EIO, - "STATUS_GRAPHICS_OPM_INVALID_SRM"}, - {STATUS_GRAPHICS_OPM_OUTPUT_DOES_NOT_SUPPORT_HDCP, -EIO, - "STATUS_GRAPHICS_OPM_OUTPUT_DOES_NOT_SUPPORT_HDCP"}, - {STATUS_GRAPHICS_OPM_OUTPUT_DOES_NOT_SUPPORT_ACP, -EIO, - "STATUS_GRAPHICS_OPM_OUTPUT_DOES_NOT_SUPPORT_ACP"}, - {STATUS_GRAPHICS_OPM_OUTPUT_DOES_NOT_SUPPORT_CGMSA, -EIO, - "STATUS_GRAPHICS_OPM_OUTPUT_DOES_NOT_SUPPORT_CGMSA"}, - {STATUS_GRAPHICS_OPM_HDCP_SRM_NEVER_SET, -EIO, - "STATUS_GRAPHICS_OPM_HDCP_SRM_NEVER_SET"}, - {STATUS_GRAPHICS_OPM_RESOLUTION_TOO_HIGH, -EIO, - "STATUS_GRAPHICS_OPM_RESOLUTION_TOO_HIGH"}, - {STATUS_GRAPHICS_OPM_ALL_HDCP_HARDWARE_ALREADY_IN_USE, -EIO, - "STATUS_GRAPHICS_OPM_ALL_HDCP_HARDWARE_ALREADY_IN_USE"}, - {STATUS_GRAPHICS_OPM_PROTECTED_OUTPUT_NO_LONGER_EXISTS, -EIO, - "STATUS_GRAPHICS_OPM_PROTECTED_OUTPUT_NO_LONGER_EXISTS"}, - {STATUS_GRAPHICS_OPM_SESSION_TYPE_CHANGE_IN_PROGRESS, -EIO, - "STATUS_GRAPHICS_OPM_SESSION_TYPE_CHANGE_IN_PROGRESS"}, - {STATUS_GRAPHICS_I2C_NOT_SUPPORTED, -EIO, - "STATUS_GRAPHICS_I2C_NOT_SUPPORTED"}, - {STATUS_GRAPHICS_I2C_DEVICE_DOES_NOT_EXIST, -EIO, - "STATUS_GRAPHICS_I2C_DEVICE_DOES_NOT_EXIST"}, - {STATUS_GRAPHICS_I2C_ERROR_TRANSMITTING_DATA, -EIO, - "STATUS_GRAPHICS_I2C_ERROR_TRANSMITTING_DATA"}, - {STATUS_GRAPHICS_I2C_ERROR_RECEIVING_DATA, -EIO, - "STATUS_GRAPHICS_I2C_ERROR_RECEIVING_DATA"}, - {STATUS_GRAPHICS_DDCCI_VCP_NOT_SUPPORTED, -EIO, - "STATUS_GRAPHICS_DDCCI_VCP_NOT_SUPPORTED"}, - {STATUS_GRAPHICS_DDCCI_INVALID_DATA, -EIO, - "STATUS_GRAPHICS_DDCCI_INVALID_DATA"}, - {STATUS_GRAPHICS_DDCCI_MONITOR_RETURNED_INVALID_TIMING_STATUS_BYTE, - -EIO, - "STATUS_GRAPHICS_DDCCI_MONITOR_RETURNED_INVALID_TIMING_STATUS_BYTE"}, - {STATUS_GRAPHICS_DDCCI_INVALID_CAPABILITIES_STRING, -EIO, - "STATUS_GRAPHICS_DDCCI_INVALID_CAPABILITIES_STRING"}, - {STATUS_GRAPHICS_MCA_INTERNAL_ERROR, -EIO, - "STATUS_GRAPHICS_MCA_INTERNAL_ERROR"}, - {STATUS_GRAPHICS_DDCCI_INVALID_MESSAGE_COMMAND, -EIO, - "STATUS_GRAPHICS_DDCCI_INVALID_MESSAGE_COMMAND"}, - {STATUS_GRAPHICS_DDCCI_INVALID_MESSAGE_LENGTH, -EIO, - "STATUS_GRAPHICS_DDCCI_INVALID_MESSAGE_LENGTH"}, - {STATUS_GRAPHICS_DDCCI_INVALID_MESSAGE_CHECKSUM, -EIO, - "STATUS_GRAPHICS_DDCCI_INVALID_MESSAGE_CHECKSUM"}, - {STATUS_GRAPHICS_INVALID_PHYSICAL_MONITOR_HANDLE, -EIO, - "STATUS_GRAPHICS_INVALID_PHYSICAL_MONITOR_HANDLE"}, - {STATUS_GRAPHICS_MONITOR_NO_LONGER_EXISTS, -EIO, - "STATUS_GRAPHICS_MONITOR_NO_LONGER_EXISTS"}, - {STATUS_GRAPHICS_ONLY_CONSOLE_SESSION_SUPPORTED, -EIO, - "STATUS_GRAPHICS_ONLY_CONSOLE_SESSION_SUPPORTED"}, - {STATUS_GRAPHICS_NO_DISPLAY_DEVICE_CORRESPONDS_TO_NAME, -EIO, - "STATUS_GRAPHICS_NO_DISPLAY_DEVICE_CORRESPONDS_TO_NAME"}, - {STATUS_GRAPHICS_DISPLAY_DEVICE_NOT_ATTACHED_TO_DESKTOP, -EIO, - "STATUS_GRAPHICS_DISPLAY_DEVICE_NOT_ATTACHED_TO_DESKTOP"}, - {STATUS_GRAPHICS_MIRRORING_DEVICES_NOT_SUPPORTED, -EIO, - "STATUS_GRAPHICS_MIRRORING_DEVICES_NOT_SUPPORTED"}, - {STATUS_GRAPHICS_INVALID_POINTER, -EIO, - "STATUS_GRAPHICS_INVALID_POINTER"}, - {STATUS_GRAPHICS_NO_MONITORS_CORRESPOND_TO_DISPLAY_DEVICE, -EIO, - "STATUS_GRAPHICS_NO_MONITORS_CORRESPOND_TO_DISPLAY_DEVICE"}, - {STATUS_GRAPHICS_PARAMETER_ARRAY_TOO_SMALL, -EIO, - "STATUS_GRAPHICS_PARAMETER_ARRAY_TOO_SMALL"}, - {STATUS_GRAPHICS_INTERNAL_ERROR, -EIO, - "STATUS_GRAPHICS_INTERNAL_ERROR"}, - {STATUS_GRAPHICS_SESSION_TYPE_CHANGE_IN_PROGRESS, -EIO, - "STATUS_GRAPHICS_SESSION_TYPE_CHANGE_IN_PROGRESS"}, - {STATUS_FVE_LOCKED_VOLUME, -EIO, "STATUS_FVE_LOCKED_VOLUME"}, - {STATUS_FVE_NOT_ENCRYPTED, -EIO, "STATUS_FVE_NOT_ENCRYPTED"}, - {STATUS_FVE_BAD_INFORMATION, -EIO, "STATUS_FVE_BAD_INFORMATION"}, - {STATUS_FVE_TOO_SMALL, -EIO, "STATUS_FVE_TOO_SMALL"}, - {STATUS_FVE_FAILED_WRONG_FS, -EIO, "STATUS_FVE_FAILED_WRONG_FS"}, - {STATUS_FVE_FAILED_BAD_FS, -EIO, "STATUS_FVE_FAILED_BAD_FS"}, - {STATUS_FVE_FS_NOT_EXTENDED, -EIO, "STATUS_FVE_FS_NOT_EXTENDED"}, - {STATUS_FVE_FS_MOUNTED, -EIO, "STATUS_FVE_FS_MOUNTED"}, - {STATUS_FVE_NO_LICENSE, -EIO, "STATUS_FVE_NO_LICENSE"}, - {STATUS_FVE_ACTION_NOT_ALLOWED, -EIO, "STATUS_FVE_ACTION_NOT_ALLOWED"}, - {STATUS_FVE_BAD_DATA, -EIO, "STATUS_FVE_BAD_DATA"}, - {STATUS_FVE_VOLUME_NOT_BOUND, -EIO, "STATUS_FVE_VOLUME_NOT_BOUND"}, - {STATUS_FVE_NOT_DATA_VOLUME, -EIO, "STATUS_FVE_NOT_DATA_VOLUME"}, - {STATUS_FVE_CONV_READ_ERROR, -EIO, "STATUS_FVE_CONV_READ_ERROR"}, - {STATUS_FVE_CONV_WRITE_ERROR, -EIO, "STATUS_FVE_CONV_WRITE_ERROR"}, - {STATUS_FVE_OVERLAPPED_UPDATE, -EIO, "STATUS_FVE_OVERLAPPED_UPDATE"}, - {STATUS_FVE_FAILED_SECTOR_SIZE, -EIO, "STATUS_FVE_FAILED_SECTOR_SIZE"}, - {STATUS_FVE_FAILED_AUTHENTICATION, -EIO, - "STATUS_FVE_FAILED_AUTHENTICATION"}, - {STATUS_FVE_NOT_OS_VOLUME, -EIO, "STATUS_FVE_NOT_OS_VOLUME"}, - {STATUS_FVE_KEYFILE_NOT_FOUND, -EIO, "STATUS_FVE_KEYFILE_NOT_FOUND"}, - {STATUS_FVE_KEYFILE_INVALID, -EIO, "STATUS_FVE_KEYFILE_INVALID"}, - {STATUS_FVE_KEYFILE_NO_VMK, -EIO, "STATUS_FVE_KEYFILE_NO_VMK"}, - {STATUS_FVE_TPM_DISABLED, -EIO, "STATUS_FVE_TPM_DISABLED"}, - {STATUS_FVE_TPM_SRK_AUTH_NOT_ZERO, -EIO, - "STATUS_FVE_TPM_SRK_AUTH_NOT_ZERO"}, - {STATUS_FVE_TPM_INVALID_PCR, -EIO, "STATUS_FVE_TPM_INVALID_PCR"}, - {STATUS_FVE_TPM_NO_VMK, -EIO, "STATUS_FVE_TPM_NO_VMK"}, - {STATUS_FVE_PIN_INVALID, -EIO, "STATUS_FVE_PIN_INVALID"}, - {STATUS_FVE_AUTH_INVALID_APPLICATION, -EIO, - "STATUS_FVE_AUTH_INVALID_APPLICATION"}, - {STATUS_FVE_AUTH_INVALID_CONFIG, -EIO, - "STATUS_FVE_AUTH_INVALID_CONFIG"}, - {STATUS_FVE_DEBUGGER_ENABLED, -EIO, "STATUS_FVE_DEBUGGER_ENABLED"}, - {STATUS_FVE_DRY_RUN_FAILED, -EIO, "STATUS_FVE_DRY_RUN_FAILED"}, - {STATUS_FVE_BAD_METADATA_POINTER, -EIO, - "STATUS_FVE_BAD_METADATA_POINTER"}, - {STATUS_FVE_OLD_METADATA_COPY, -EIO, "STATUS_FVE_OLD_METADATA_COPY"}, - {STATUS_FVE_REBOOT_REQUIRED, -EIO, "STATUS_FVE_REBOOT_REQUIRED"}, - {STATUS_FVE_RAW_ACCESS, -EIO, "STATUS_FVE_RAW_ACCESS"}, - {STATUS_FVE_RAW_BLOCKED, -EIO, "STATUS_FVE_RAW_BLOCKED"}, - {STATUS_FWP_CALLOUT_NOT_FOUND, -EIO, "STATUS_FWP_CALLOUT_NOT_FOUND"}, - {STATUS_FWP_CONDITION_NOT_FOUND, -EIO, - "STATUS_FWP_CONDITION_NOT_FOUND"}, - {STATUS_FWP_FILTER_NOT_FOUND, -EIO, "STATUS_FWP_FILTER_NOT_FOUND"}, - {STATUS_FWP_LAYER_NOT_FOUND, -EIO, "STATUS_FWP_LAYER_NOT_FOUND"}, - {STATUS_FWP_PROVIDER_NOT_FOUND, -EIO, "STATUS_FWP_PROVIDER_NOT_FOUND"}, - {STATUS_FWP_PROVIDER_CONTEXT_NOT_FOUND, -EIO, - "STATUS_FWP_PROVIDER_CONTEXT_NOT_FOUND"}, - {STATUS_FWP_SUBLAYER_NOT_FOUND, -EIO, "STATUS_FWP_SUBLAYER_NOT_FOUND"}, - {STATUS_FWP_NOT_FOUND, -EIO, "STATUS_FWP_NOT_FOUND"}, - {STATUS_FWP_ALREADY_EXISTS, -EIO, "STATUS_FWP_ALREADY_EXISTS"}, - {STATUS_FWP_IN_USE, -EIO, "STATUS_FWP_IN_USE"}, - {STATUS_FWP_DYNAMIC_SESSION_IN_PROGRESS, -EIO, - "STATUS_FWP_DYNAMIC_SESSION_IN_PROGRESS"}, - {STATUS_FWP_WRONG_SESSION, -EIO, "STATUS_FWP_WRONG_SESSION"}, - {STATUS_FWP_NO_TXN_IN_PROGRESS, -EIO, "STATUS_FWP_NO_TXN_IN_PROGRESS"}, - {STATUS_FWP_TXN_IN_PROGRESS, -EIO, "STATUS_FWP_TXN_IN_PROGRESS"}, - {STATUS_FWP_TXN_ABORTED, -EIO, "STATUS_FWP_TXN_ABORTED"}, - {STATUS_FWP_SESSION_ABORTED, -EIO, "STATUS_FWP_SESSION_ABORTED"}, - {STATUS_FWP_INCOMPATIBLE_TXN, -EIO, "STATUS_FWP_INCOMPATIBLE_TXN"}, - {STATUS_FWP_TIMEOUT, -ETIMEDOUT, "STATUS_FWP_TIMEOUT"}, - {STATUS_FWP_NET_EVENTS_DISABLED, -EIO, - "STATUS_FWP_NET_EVENTS_DISABLED"}, - {STATUS_FWP_INCOMPATIBLE_LAYER, -EIO, "STATUS_FWP_INCOMPATIBLE_LAYER"}, - {STATUS_FWP_KM_CLIENTS_ONLY, -EIO, "STATUS_FWP_KM_CLIENTS_ONLY"}, - {STATUS_FWP_LIFETIME_MISMATCH, -EIO, "STATUS_FWP_LIFETIME_MISMATCH"}, - {STATUS_FWP_BUILTIN_OBJECT, -EIO, "STATUS_FWP_BUILTIN_OBJECT"}, - {STATUS_FWP_TOO_MANY_BOOTTIME_FILTERS, -EIO, - "STATUS_FWP_TOO_MANY_BOOTTIME_FILTERS"}, - {STATUS_FWP_TOO_MANY_CALLOUTS, -EIO, "STATUS_FWP_TOO_MANY_CALLOUTS"}, - {STATUS_FWP_NOTIFICATION_DROPPED, -EIO, - "STATUS_FWP_NOTIFICATION_DROPPED"}, - {STATUS_FWP_TRAFFIC_MISMATCH, -EIO, "STATUS_FWP_TRAFFIC_MISMATCH"}, - {STATUS_FWP_INCOMPATIBLE_SA_STATE, -EIO, - "STATUS_FWP_INCOMPATIBLE_SA_STATE"}, - {STATUS_FWP_NULL_POINTER, -EIO, "STATUS_FWP_NULL_POINTER"}, - {STATUS_FWP_INVALID_ENUMERATOR, -EIO, "STATUS_FWP_INVALID_ENUMERATOR"}, - {STATUS_FWP_INVALID_FLAGS, -EIO, "STATUS_FWP_INVALID_FLAGS"}, - {STATUS_FWP_INVALID_NET_MASK, -EIO, "STATUS_FWP_INVALID_NET_MASK"}, - {STATUS_FWP_INVALID_RANGE, -EIO, "STATUS_FWP_INVALID_RANGE"}, - {STATUS_FWP_INVALID_INTERVAL, -EIO, "STATUS_FWP_INVALID_INTERVAL"}, - {STATUS_FWP_ZERO_LENGTH_ARRAY, -EIO, "STATUS_FWP_ZERO_LENGTH_ARRAY"}, - {STATUS_FWP_NULL_DISPLAY_NAME, -EIO, "STATUS_FWP_NULL_DISPLAY_NAME"}, - {STATUS_FWP_INVALID_ACTION_TYPE, -EIO, - "STATUS_FWP_INVALID_ACTION_TYPE"}, - {STATUS_FWP_INVALID_WEIGHT, -EIO, "STATUS_FWP_INVALID_WEIGHT"}, - {STATUS_FWP_MATCH_TYPE_MISMATCH, -EIO, - "STATUS_FWP_MATCH_TYPE_MISMATCH"}, - {STATUS_FWP_TYPE_MISMATCH, -EIO, "STATUS_FWP_TYPE_MISMATCH"}, - {STATUS_FWP_OUT_OF_BOUNDS, -EIO, "STATUS_FWP_OUT_OF_BOUNDS"}, - {STATUS_FWP_RESERVED, -EIO, "STATUS_FWP_RESERVED"}, - {STATUS_FWP_DUPLICATE_CONDITION, -EIO, - "STATUS_FWP_DUPLICATE_CONDITION"}, - {STATUS_FWP_DUPLICATE_KEYMOD, -EIO, "STATUS_FWP_DUPLICATE_KEYMOD"}, - {STATUS_FWP_ACTION_INCOMPATIBLE_WITH_LAYER, -EIO, - "STATUS_FWP_ACTION_INCOMPATIBLE_WITH_LAYER"}, - {STATUS_FWP_ACTION_INCOMPATIBLE_WITH_SUBLAYER, -EIO, - "STATUS_FWP_ACTION_INCOMPATIBLE_WITH_SUBLAYER"}, - {STATUS_FWP_CONTEXT_INCOMPATIBLE_WITH_LAYER, -EIO, - "STATUS_FWP_CONTEXT_INCOMPATIBLE_WITH_LAYER"}, - {STATUS_FWP_CONTEXT_INCOMPATIBLE_WITH_CALLOUT, -EIO, - "STATUS_FWP_CONTEXT_INCOMPATIBLE_WITH_CALLOUT"}, - {STATUS_FWP_INCOMPATIBLE_AUTH_METHOD, -EIO, - "STATUS_FWP_INCOMPATIBLE_AUTH_METHOD"}, - {STATUS_FWP_INCOMPATIBLE_DH_GROUP, -EIO, - "STATUS_FWP_INCOMPATIBLE_DH_GROUP"}, - {STATUS_FWP_EM_NOT_SUPPORTED, -EOPNOTSUPP, - "STATUS_FWP_EM_NOT_SUPPORTED"}, - {STATUS_FWP_NEVER_MATCH, -EIO, "STATUS_FWP_NEVER_MATCH"}, - {STATUS_FWP_PROVIDER_CONTEXT_MISMATCH, -EIO, - "STATUS_FWP_PROVIDER_CONTEXT_MISMATCH"}, - {STATUS_FWP_INVALID_PARAMETER, -EIO, "STATUS_FWP_INVALID_PARAMETER"}, - {STATUS_FWP_TOO_MANY_SUBLAYERS, -EIO, "STATUS_FWP_TOO_MANY_SUBLAYERS"}, - {STATUS_FWP_CALLOUT_NOTIFICATION_FAILED, -EIO, - "STATUS_FWP_CALLOUT_NOTIFICATION_FAILED"}, - {STATUS_FWP_INCOMPATIBLE_AUTH_CONFIG, -EIO, - "STATUS_FWP_INCOMPATIBLE_AUTH_CONFIG"}, - {STATUS_FWP_INCOMPATIBLE_CIPHER_CONFIG, -EIO, - "STATUS_FWP_INCOMPATIBLE_CIPHER_CONFIG"}, - {STATUS_FWP_TCPIP_NOT_READY, -EIO, "STATUS_FWP_TCPIP_NOT_READY"}, - {STATUS_FWP_INJECT_HANDLE_CLOSING, -EIO, - "STATUS_FWP_INJECT_HANDLE_CLOSING"}, - {STATUS_FWP_INJECT_HANDLE_STALE, -EIO, - "STATUS_FWP_INJECT_HANDLE_STALE"}, - {STATUS_FWP_CANNOT_PEND, -EIO, "STATUS_FWP_CANNOT_PEND"}, - {STATUS_NDIS_CLOSING, -EIO, "STATUS_NDIS_CLOSING"}, - {STATUS_NDIS_BAD_VERSION, -EIO, "STATUS_NDIS_BAD_VERSION"}, - {STATUS_NDIS_BAD_CHARACTERISTICS, -EIO, - "STATUS_NDIS_BAD_CHARACTERISTICS"}, - {STATUS_NDIS_ADAPTER_NOT_FOUND, -EIO, "STATUS_NDIS_ADAPTER_NOT_FOUND"}, - {STATUS_NDIS_OPEN_FAILED, -EIO, "STATUS_NDIS_OPEN_FAILED"}, - {STATUS_NDIS_DEVICE_FAILED, -EIO, "STATUS_NDIS_DEVICE_FAILED"}, - {STATUS_NDIS_MULTICAST_FULL, -EIO, "STATUS_NDIS_MULTICAST_FULL"}, - {STATUS_NDIS_MULTICAST_EXISTS, -EIO, "STATUS_NDIS_MULTICAST_EXISTS"}, - {STATUS_NDIS_MULTICAST_NOT_FOUND, -EIO, - "STATUS_NDIS_MULTICAST_NOT_FOUND"}, - {STATUS_NDIS_REQUEST_ABORTED, -EIO, "STATUS_NDIS_REQUEST_ABORTED"}, - {STATUS_NDIS_RESET_IN_PROGRESS, -EIO, "STATUS_NDIS_RESET_IN_PROGRESS"}, - {STATUS_NDIS_INVALID_PACKET, -EIO, "STATUS_NDIS_INVALID_PACKET"}, - {STATUS_NDIS_INVALID_DEVICE_REQUEST, -EIO, - "STATUS_NDIS_INVALID_DEVICE_REQUEST"}, - {STATUS_NDIS_ADAPTER_NOT_READY, -EIO, "STATUS_NDIS_ADAPTER_NOT_READY"}, - {STATUS_NDIS_INVALID_LENGTH, -EIO, "STATUS_NDIS_INVALID_LENGTH"}, - {STATUS_NDIS_INVALID_DATA, -EIO, "STATUS_NDIS_INVALID_DATA"}, - {STATUS_NDIS_BUFFER_TOO_SHORT, -ENOBUFS, - "STATUS_NDIS_BUFFER_TOO_SHORT"}, - {STATUS_NDIS_INVALID_OID, -EIO, "STATUS_NDIS_INVALID_OID"}, - {STATUS_NDIS_ADAPTER_REMOVED, -EIO, "STATUS_NDIS_ADAPTER_REMOVED"}, - {STATUS_NDIS_UNSUPPORTED_MEDIA, -EIO, "STATUS_NDIS_UNSUPPORTED_MEDIA"}, - {STATUS_NDIS_GROUP_ADDRESS_IN_USE, -EIO, - "STATUS_NDIS_GROUP_ADDRESS_IN_USE"}, - {STATUS_NDIS_FILE_NOT_FOUND, -EIO, "STATUS_NDIS_FILE_NOT_FOUND"}, - {STATUS_NDIS_ERROR_READING_FILE, -EIO, - "STATUS_NDIS_ERROR_READING_FILE"}, - {STATUS_NDIS_ALREADY_MAPPED, -EIO, "STATUS_NDIS_ALREADY_MAPPED"}, - {STATUS_NDIS_RESOURCE_CONFLICT, -EIO, "STATUS_NDIS_RESOURCE_CONFLICT"}, - {STATUS_NDIS_MEDIA_DISCONNECTED, -EIO, - "STATUS_NDIS_MEDIA_DISCONNECTED"}, - {STATUS_NDIS_INVALID_ADDRESS, -EIO, "STATUS_NDIS_INVALID_ADDRESS"}, - {STATUS_NDIS_PAUSED, -EIO, "STATUS_NDIS_PAUSED"}, - {STATUS_NDIS_INTERFACE_NOT_FOUND, -EIO, - "STATUS_NDIS_INTERFACE_NOT_FOUND"}, - {STATUS_NDIS_UNSUPPORTED_REVISION, -EIO, - "STATUS_NDIS_UNSUPPORTED_REVISION"}, - {STATUS_NDIS_INVALID_PORT, -EIO, "STATUS_NDIS_INVALID_PORT"}, - {STATUS_NDIS_INVALID_PORT_STATE, -EIO, - "STATUS_NDIS_INVALID_PORT_STATE"}, - {STATUS_NDIS_LOW_POWER_STATE, -EIO, "STATUS_NDIS_LOW_POWER_STATE"}, - {STATUS_NDIS_NOT_SUPPORTED, -ENOSYS, "STATUS_NDIS_NOT_SUPPORTED"}, - {STATUS_NDIS_DOT11_AUTO_CONFIG_ENABLED, -EIO, - "STATUS_NDIS_DOT11_AUTO_CONFIG_ENABLED"}, - {STATUS_NDIS_DOT11_MEDIA_IN_USE, -EIO, - "STATUS_NDIS_DOT11_MEDIA_IN_USE"}, - {STATUS_NDIS_DOT11_POWER_STATE_INVALID, -EIO, - "STATUS_NDIS_DOT11_POWER_STATE_INVALID"}, - {STATUS_IPSEC_BAD_SPI, -EIO, "STATUS_IPSEC_BAD_SPI"}, - {STATUS_IPSEC_SA_LIFETIME_EXPIRED, -EIO, - "STATUS_IPSEC_SA_LIFETIME_EXPIRED"}, - {STATUS_IPSEC_WRONG_SA, -EIO, "STATUS_IPSEC_WRONG_SA"}, - {STATUS_IPSEC_REPLAY_CHECK_FAILED, -EIO, - "STATUS_IPSEC_REPLAY_CHECK_FAILED"}, - {STATUS_IPSEC_INVALID_PACKET, -EIO, "STATUS_IPSEC_INVALID_PACKET"}, - {STATUS_IPSEC_INTEGRITY_CHECK_FAILED, -EIO, - "STATUS_IPSEC_INTEGRITY_CHECK_FAILED"}, - {STATUS_IPSEC_CLEAR_TEXT_DROP, -EIO, "STATUS_IPSEC_CLEAR_TEXT_DROP"}, - {0, 0, NULL} -}; - -/***************************************************************************** - Print an error message from the status code - *****************************************************************************/ -static void -smb2_print_status(__le32 status) -{ - int idx = 0; - - while (smb2_error_map_table[idx].status_string != NULL) { - if ((smb2_error_map_table[idx].smb2_status) == status) { - pr_notice("Status code returned 0x%08x %s\n", status, - smb2_error_map_table[idx].status_string); - } - idx++; - } - return; -} - -int -map_smb2_to_linux_error(char *buf, bool log_err) -{ - struct smb2_hdr *shdr = (struct smb2_hdr *)buf; - unsigned int i; - int rc = -EIO; - __le32 smb2err = shdr->Status; - - if (smb2err == 0) { - trace_smb3_cmd_done(le32_to_cpu(shdr->Id.SyncId.TreeId), - le64_to_cpu(shdr->SessionId), - le16_to_cpu(shdr->Command), - le64_to_cpu(shdr->MessageId)); - return 0; - } - - /* mask facility */ - if (log_err && (smb2err != STATUS_MORE_PROCESSING_REQUIRED) && - (smb2err != STATUS_END_OF_FILE)) - smb2_print_status(smb2err); - else if (cifsFYI & CIFS_RC) - smb2_print_status(smb2err); - - for (i = 0; i < sizeof(smb2_error_map_table) / - sizeof(struct status_to_posix_error); i++) { - if (smb2_error_map_table[i].smb2_status == smb2err) { - rc = smb2_error_map_table[i].posix_error; - break; - } - } - - /* on error mapping not found - return EIO */ - - cifs_dbg(FYI, "Mapping SMB2 status code 0x%08x to POSIX err %d\n", - __le32_to_cpu(smb2err), rc); - - trace_smb3_cmd_err(le32_to_cpu(shdr->Id.SyncId.TreeId), - le64_to_cpu(shdr->SessionId), - le16_to_cpu(shdr->Command), - le64_to_cpu(shdr->MessageId), - le32_to_cpu(smb2err), rc); - return rc; -} diff --git a/fs/cifs/smb2misc.c b/fs/cifs/smb2misc.c deleted file mode 100644 index 572293c18e16f..0000000000000 --- a/fs/cifs/smb2misc.c +++ /dev/null @@ -1,944 +0,0 @@ -// SPDX-License-Identifier: LGPL-2.1 -/* - * - * Copyright (C) International Business Machines Corp., 2002,2011 - * Etersoft, 2012 - * Author(s): Steve French (sfrench@us.ibm.com) - * Pavel Shilovsky (pshilovsky@samba.org) 2012 - * - */ -#include -#include "cifsglob.h" -#include "cifsproto.h" -#include "smb2proto.h" -#include "cifs_debug.h" -#include "cifs_unicode.h" -#include "smb2status.h" -#include "smb2glob.h" -#include "nterr.h" -#include "cached_dir.h" - -static int -check_smb2_hdr(struct smb2_hdr *shdr, __u64 mid) -{ - __u64 wire_mid = le64_to_cpu(shdr->MessageId); - - /* - * Make sure that this really is an SMB, that it is a response, - * and that the message ids match. - */ - if ((shdr->ProtocolId == SMB2_PROTO_NUMBER) && - (mid == wire_mid)) { - if (shdr->Flags & SMB2_FLAGS_SERVER_TO_REDIR) - return 0; - else { - /* only one valid case where server sends us request */ - if (shdr->Command == SMB2_OPLOCK_BREAK) - return 0; - else - cifs_dbg(VFS, "Received Request not response\n"); - } - } else { /* bad signature or mid */ - if (shdr->ProtocolId != SMB2_PROTO_NUMBER) - cifs_dbg(VFS, "Bad protocol string signature header %x\n", - le32_to_cpu(shdr->ProtocolId)); - if (mid != wire_mid) - cifs_dbg(VFS, "Mids do not match: %llu and %llu\n", - mid, wire_mid); - } - cifs_dbg(VFS, "Bad SMB detected. The Mid=%llu\n", wire_mid); - return 1; -} - -/* - * The following table defines the expected "StructureSize" of SMB2 responses - * in order by SMB2 command. This is similar to "wct" in SMB/CIFS responses. - * - * Note that commands are defined in smb2pdu.h in le16 but the array below is - * indexed by command in host byte order - */ -static const __le16 smb2_rsp_struct_sizes[NUMBER_OF_SMB2_COMMANDS] = { - /* SMB2_NEGOTIATE */ cpu_to_le16(65), - /* SMB2_SESSION_SETUP */ cpu_to_le16(9), - /* SMB2_LOGOFF */ cpu_to_le16(4), - /* SMB2_TREE_CONNECT */ cpu_to_le16(16), - /* SMB2_TREE_DISCONNECT */ cpu_to_le16(4), - /* SMB2_CREATE */ cpu_to_le16(89), - /* SMB2_CLOSE */ cpu_to_le16(60), - /* SMB2_FLUSH */ cpu_to_le16(4), - /* SMB2_READ */ cpu_to_le16(17), - /* SMB2_WRITE */ cpu_to_le16(17), - /* SMB2_LOCK */ cpu_to_le16(4), - /* SMB2_IOCTL */ cpu_to_le16(49), - /* BB CHECK this ... not listed in documentation */ - /* SMB2_CANCEL */ cpu_to_le16(0), - /* SMB2_ECHO */ cpu_to_le16(4), - /* SMB2_QUERY_DIRECTORY */ cpu_to_le16(9), - /* SMB2_CHANGE_NOTIFY */ cpu_to_le16(9), - /* SMB2_QUERY_INFO */ cpu_to_le16(9), - /* SMB2_SET_INFO */ cpu_to_le16(2), - /* BB FIXME can also be 44 for lease break */ - /* SMB2_OPLOCK_BREAK */ cpu_to_le16(24) -}; - -#define SMB311_NEGPROT_BASE_SIZE (sizeof(struct smb2_hdr) + sizeof(struct smb2_negotiate_rsp)) - -static __u32 get_neg_ctxt_len(struct smb2_hdr *hdr, __u32 len, - __u32 non_ctxlen) -{ - __u16 neg_count; - __u32 nc_offset, size_of_pad_before_neg_ctxts; - struct smb2_negotiate_rsp *pneg_rsp = (struct smb2_negotiate_rsp *)hdr; - - /* Negotiate contexts are only valid for latest dialect SMB3.11 */ - neg_count = le16_to_cpu(pneg_rsp->NegotiateContextCount); - if ((neg_count == 0) || - (pneg_rsp->DialectRevision != cpu_to_le16(SMB311_PROT_ID))) - return 0; - - /* - * if SPNEGO blob present (ie the RFC2478 GSS info which indicates - * which security mechanisms the server supports) make sure that - * the negotiate contexts start after it - */ - nc_offset = le32_to_cpu(pneg_rsp->NegotiateContextOffset); - /* - * non_ctxlen is at least shdr->StructureSize + pdu->StructureSize2 - * and the latter is 1 byte bigger than the fix-sized area of the - * NEGOTIATE response - */ - if (nc_offset + 1 < non_ctxlen) { - pr_warn_once("Invalid negotiate context offset %d\n", nc_offset); - return 0; - } else if (nc_offset + 1 == non_ctxlen) { - cifs_dbg(FYI, "no SPNEGO security blob in negprot rsp\n"); - size_of_pad_before_neg_ctxts = 0; - } else if (non_ctxlen == SMB311_NEGPROT_BASE_SIZE) - /* has padding, but no SPNEGO blob */ - size_of_pad_before_neg_ctxts = nc_offset - non_ctxlen + 1; - else - size_of_pad_before_neg_ctxts = nc_offset - non_ctxlen; - - /* Verify that at least minimal negotiate contexts fit within frame */ - if (len < nc_offset + (neg_count * sizeof(struct smb2_neg_context))) { - pr_warn_once("negotiate context goes beyond end\n"); - return 0; - } - - cifs_dbg(FYI, "length of negcontexts %d pad %d\n", - len - nc_offset, size_of_pad_before_neg_ctxts); - - /* length of negcontexts including pad from end of sec blob to them */ - return (len - nc_offset) + size_of_pad_before_neg_ctxts; -} - -int -smb2_check_message(char *buf, unsigned int len, struct TCP_Server_Info *server) -{ - struct TCP_Server_Info *pserver; - struct smb2_hdr *shdr = (struct smb2_hdr *)buf; - struct smb2_pdu *pdu = (struct smb2_pdu *)shdr; - int hdr_size = sizeof(struct smb2_hdr); - int pdu_size = sizeof(struct smb2_pdu); - int command; - __u32 calc_len; /* calculated length */ - __u64 mid; - - /* If server is a channel, select the primary channel */ - pserver = CIFS_SERVER_IS_CHAN(server) ? server->primary_server : server; - - /* - * Add function to do table lookup of StructureSize by command - * ie Validate the wct via smb2_struct_sizes table above - */ - if (shdr->ProtocolId == SMB2_TRANSFORM_PROTO_NUM) { - struct smb2_transform_hdr *thdr = - (struct smb2_transform_hdr *)buf; - struct cifs_ses *ses = NULL; - struct cifs_ses *iter; - - /* decrypt frame now that it is completely read in */ - spin_lock(&cifs_tcp_ses_lock); - list_for_each_entry(iter, &pserver->smb_ses_list, smb_ses_list) { - if (iter->Suid == le64_to_cpu(thdr->SessionId)) { - ses = iter; - break; - } - } - spin_unlock(&cifs_tcp_ses_lock); - if (!ses) { - cifs_dbg(VFS, "no decryption - session id not found\n"); - return 1; - } - } - - mid = le64_to_cpu(shdr->MessageId); - if (len < pdu_size) { - if ((len >= hdr_size) - && (shdr->Status != 0)) { - pdu->StructureSize2 = 0; - /* - * As with SMB/CIFS, on some error cases servers may - * not return wct properly - */ - return 0; - } else { - cifs_dbg(VFS, "Length less than SMB header size\n"); - } - return 1; - } - if (len > CIFSMaxBufSize + MAX_SMB2_HDR_SIZE) { - cifs_dbg(VFS, "SMB length greater than maximum, mid=%llu\n", - mid); - return 1; - } - - if (check_smb2_hdr(shdr, mid)) - return 1; - - if (shdr->StructureSize != SMB2_HEADER_STRUCTURE_SIZE) { - cifs_dbg(VFS, "Invalid structure size %u\n", - le16_to_cpu(shdr->StructureSize)); - return 1; - } - - command = le16_to_cpu(shdr->Command); - if (command >= NUMBER_OF_SMB2_COMMANDS) { - cifs_dbg(VFS, "Invalid SMB2 command %d\n", command); - return 1; - } - - if (smb2_rsp_struct_sizes[command] != pdu->StructureSize2) { - if (command != SMB2_OPLOCK_BREAK_HE && (shdr->Status == 0 || - pdu->StructureSize2 != SMB2_ERROR_STRUCTURE_SIZE2_LE)) { - /* error packets have 9 byte structure size */ - cifs_dbg(VFS, "Invalid response size %u for command %d\n", - le16_to_cpu(pdu->StructureSize2), command); - return 1; - } else if (command == SMB2_OPLOCK_BREAK_HE - && (shdr->Status == 0) - && (le16_to_cpu(pdu->StructureSize2) != 44) - && (le16_to_cpu(pdu->StructureSize2) != 36)) { - /* special case for SMB2.1 lease break message */ - cifs_dbg(VFS, "Invalid response size %d for oplock break\n", - le16_to_cpu(pdu->StructureSize2)); - return 1; - } - } - - calc_len = smb2_calc_size(buf); - - /* For SMB2_IOCTL, OutputOffset and OutputLength are optional, so might - * be 0, and not a real miscalculation */ - if (command == SMB2_IOCTL_HE && calc_len == 0) - return 0; - - if (command == SMB2_NEGOTIATE_HE) - calc_len += get_neg_ctxt_len(shdr, len, calc_len); - - if (len != calc_len) { - /* create failed on symlink */ - if (command == SMB2_CREATE_HE && - shdr->Status == STATUS_STOPPED_ON_SYMLINK) - return 0; - /* Windows 7 server returns 24 bytes more */ - if (calc_len + 24 == len && command == SMB2_OPLOCK_BREAK_HE) - return 0; - /* server can return one byte more due to implied bcc[0] */ - if (calc_len == len + 1) - return 0; - - /* - * Some windows servers (win2016) will pad also the final - * PDU in a compound to 8 bytes. - */ - if (ALIGN(calc_len, 8) == len) - return 0; - - /* - * MacOS server pads after SMB2.1 write response with 3 bytes - * of junk. Other servers match RFC1001 len to actual - * SMB2/SMB3 frame length (header + smb2 response specific data) - * Some windows servers also pad up to 8 bytes when compounding. - */ - if (calc_len < len) - return 0; - - /* Only log a message if len was really miscalculated */ - if (unlikely(cifsFYI)) - cifs_dbg(FYI, "Server response too short: calculated " - "length %u doesn't match read length %u (cmd=%d, mid=%llu)\n", - calc_len, len, command, mid); - else - pr_warn("Server response too short: calculated length " - "%u doesn't match read length %u (cmd=%d, mid=%llu)\n", - calc_len, len, command, mid); - - return 1; - } - return 0; -} - -/* - * The size of the variable area depends on the offset and length fields - * located in different fields for various SMB2 responses. SMB2 responses - * with no variable length info, show an offset of zero for the offset field. - */ -static const bool has_smb2_data_area[NUMBER_OF_SMB2_COMMANDS] = { - /* SMB2_NEGOTIATE */ true, - /* SMB2_SESSION_SETUP */ true, - /* SMB2_LOGOFF */ false, - /* SMB2_TREE_CONNECT */ false, - /* SMB2_TREE_DISCONNECT */ false, - /* SMB2_CREATE */ true, - /* SMB2_CLOSE */ false, - /* SMB2_FLUSH */ false, - /* SMB2_READ */ true, - /* SMB2_WRITE */ false, - /* SMB2_LOCK */ false, - /* SMB2_IOCTL */ true, - /* SMB2_CANCEL */ false, /* BB CHECK this not listed in documentation */ - /* SMB2_ECHO */ false, - /* SMB2_QUERY_DIRECTORY */ true, - /* SMB2_CHANGE_NOTIFY */ true, - /* SMB2_QUERY_INFO */ true, - /* SMB2_SET_INFO */ false, - /* SMB2_OPLOCK_BREAK */ false -}; - -/* - * Returns the pointer to the beginning of the data area. Length of the data - * area and the offset to it (from the beginning of the smb are also returned. - */ -char * -smb2_get_data_area_len(int *off, int *len, struct smb2_hdr *shdr) -{ - *off = 0; - *len = 0; - - /* error responses do not have data area */ - if (shdr->Status && shdr->Status != STATUS_MORE_PROCESSING_REQUIRED && - (((struct smb2_err_rsp *)shdr)->StructureSize) == - SMB2_ERROR_STRUCTURE_SIZE2_LE) - return NULL; - - /* - * Following commands have data areas so we have to get the location - * of the data buffer offset and data buffer length for the particular - * command. - */ - switch (shdr->Command) { - case SMB2_NEGOTIATE: - *off = le16_to_cpu( - ((struct smb2_negotiate_rsp *)shdr)->SecurityBufferOffset); - *len = le16_to_cpu( - ((struct smb2_negotiate_rsp *)shdr)->SecurityBufferLength); - break; - case SMB2_SESSION_SETUP: - *off = le16_to_cpu( - ((struct smb2_sess_setup_rsp *)shdr)->SecurityBufferOffset); - *len = le16_to_cpu( - ((struct smb2_sess_setup_rsp *)shdr)->SecurityBufferLength); - break; - case SMB2_CREATE: - *off = le32_to_cpu( - ((struct smb2_create_rsp *)shdr)->CreateContextsOffset); - *len = le32_to_cpu( - ((struct smb2_create_rsp *)shdr)->CreateContextsLength); - break; - case SMB2_QUERY_INFO: - *off = le16_to_cpu( - ((struct smb2_query_info_rsp *)shdr)->OutputBufferOffset); - *len = le32_to_cpu( - ((struct smb2_query_info_rsp *)shdr)->OutputBufferLength); - break; - case SMB2_READ: - /* TODO: is this a bug ? */ - *off = ((struct smb2_read_rsp *)shdr)->DataOffset; - *len = le32_to_cpu(((struct smb2_read_rsp *)shdr)->DataLength); - break; - case SMB2_QUERY_DIRECTORY: - *off = le16_to_cpu( - ((struct smb2_query_directory_rsp *)shdr)->OutputBufferOffset); - *len = le32_to_cpu( - ((struct smb2_query_directory_rsp *)shdr)->OutputBufferLength); - break; - case SMB2_IOCTL: - *off = le32_to_cpu( - ((struct smb2_ioctl_rsp *)shdr)->OutputOffset); - *len = le32_to_cpu( - ((struct smb2_ioctl_rsp *)shdr)->OutputCount); - break; - case SMB2_CHANGE_NOTIFY: - *off = le16_to_cpu( - ((struct smb2_change_notify_rsp *)shdr)->OutputBufferOffset); - *len = le32_to_cpu( - ((struct smb2_change_notify_rsp *)shdr)->OutputBufferLength); - break; - default: - cifs_dbg(VFS, "no length check for command %d\n", le16_to_cpu(shdr->Command)); - break; - } - - /* - * Invalid length or offset probably means data area is invalid, but - * we have little choice but to ignore the data area in this case. - */ - if (*off > 4096) { - cifs_dbg(VFS, "offset %d too large, data area ignored\n", *off); - *len = 0; - *off = 0; - } else if (*off < 0) { - cifs_dbg(VFS, "negative offset %d to data invalid ignore data area\n", - *off); - *off = 0; - *len = 0; - } else if (*len < 0) { - cifs_dbg(VFS, "negative data length %d invalid, data area ignored\n", - *len); - *len = 0; - } else if (*len > 128 * 1024) { - cifs_dbg(VFS, "data area larger than 128K: %d\n", *len); - *len = 0; - } - - /* return pointer to beginning of data area, ie offset from SMB start */ - if ((*off != 0) && (*len != 0)) - return (char *)shdr + *off; - else - return NULL; -} - -/* - * Calculate the size of the SMB message based on the fixed header - * portion, the number of word parameters and the data portion of the message. - */ -unsigned int -smb2_calc_size(void *buf) -{ - struct smb2_pdu *pdu = buf; - struct smb2_hdr *shdr = &pdu->hdr; - int offset; /* the offset from the beginning of SMB to data area */ - int data_length; /* the length of the variable length data area */ - /* Structure Size has already been checked to make sure it is 64 */ - int len = le16_to_cpu(shdr->StructureSize); - - /* - * StructureSize2, ie length of fixed parameter area has already - * been checked to make sure it is the correct length. - */ - len += le16_to_cpu(pdu->StructureSize2); - - if (has_smb2_data_area[le16_to_cpu(shdr->Command)] == false) - goto calc_size_exit; - - smb2_get_data_area_len(&offset, &data_length, shdr); - cifs_dbg(FYI, "SMB2 data length %d offset %d\n", data_length, offset); - - if (data_length > 0) { - /* - * Check to make sure that data area begins after fixed area, - * Note that last byte of the fixed area is part of data area - * for some commands, typically those with odd StructureSize, - * so we must add one to the calculation. - */ - if (offset + 1 < len) { - cifs_dbg(VFS, "data area offset %d overlaps SMB2 header %d\n", - offset + 1, len); - data_length = 0; - } else { - len = offset + data_length; - } - } -calc_size_exit: - cifs_dbg(FYI, "SMB2 len %d\n", len); - return len; -} - -/* Note: caller must free return buffer */ -__le16 * -cifs_convert_path_to_utf16(const char *from, struct cifs_sb_info *cifs_sb) -{ - int len; - const char *start_of_path; - __le16 *to; - int map_type; - - if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_MAP_SFM_CHR) - map_type = SFM_MAP_UNI_RSVD; - else if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_MAP_SPECIAL_CHR) - map_type = SFU_MAP_UNI_RSVD; - else - map_type = NO_MAP_UNI_RSVD; - - /* Windows doesn't allow paths beginning with \ */ - if (from[0] == '\\') - start_of_path = from + 1; - - /* SMB311 POSIX extensions paths do not include leading slash */ - else if (cifs_sb_master_tlink(cifs_sb) && - cifs_sb_master_tcon(cifs_sb)->posix_extensions && - (from[0] == '/')) { - start_of_path = from + 1; - } else - start_of_path = from; - - to = cifs_strndup_to_utf16(start_of_path, PATH_MAX, &len, - cifs_sb->local_nls, map_type); - return to; -} - -__le32 -smb2_get_lease_state(struct cifsInodeInfo *cinode) -{ - __le32 lease = 0; - - if (CIFS_CACHE_WRITE(cinode)) - lease |= SMB2_LEASE_WRITE_CACHING_LE; - if (CIFS_CACHE_HANDLE(cinode)) - lease |= SMB2_LEASE_HANDLE_CACHING_LE; - if (CIFS_CACHE_READ(cinode)) - lease |= SMB2_LEASE_READ_CACHING_LE; - return lease; -} - -struct smb2_lease_break_work { - struct work_struct lease_break; - struct tcon_link *tlink; - __u8 lease_key[16]; - __le32 lease_state; -}; - -static void -cifs_ses_oplock_break(struct work_struct *work) -{ - struct smb2_lease_break_work *lw = container_of(work, - struct smb2_lease_break_work, lease_break); - int rc = 0; - - rc = SMB2_lease_break(0, tlink_tcon(lw->tlink), lw->lease_key, - lw->lease_state); - - cifs_dbg(FYI, "Lease release rc %d\n", rc); - cifs_put_tlink(lw->tlink); - kfree(lw); -} - -static void -smb2_queue_pending_open_break(struct tcon_link *tlink, __u8 *lease_key, - __le32 new_lease_state) -{ - struct smb2_lease_break_work *lw; - - lw = kmalloc(sizeof(struct smb2_lease_break_work), GFP_KERNEL); - if (!lw) { - cifs_put_tlink(tlink); - return; - } - - INIT_WORK(&lw->lease_break, cifs_ses_oplock_break); - lw->tlink = tlink; - lw->lease_state = new_lease_state; - memcpy(lw->lease_key, lease_key, SMB2_LEASE_KEY_SIZE); - queue_work(cifsiod_wq, &lw->lease_break); -} - -static bool -smb2_tcon_has_lease(struct cifs_tcon *tcon, struct smb2_lease_break *rsp) -{ - __u8 lease_state; - struct cifsFileInfo *cfile; - struct cifsInodeInfo *cinode; - int ack_req = le32_to_cpu(rsp->Flags & - SMB2_NOTIFY_BREAK_LEASE_FLAG_ACK_REQUIRED); - - lease_state = le32_to_cpu(rsp->NewLeaseState); - - list_for_each_entry(cfile, &tcon->openFileList, tlist) { - cinode = CIFS_I(d_inode(cfile->dentry)); - - if (memcmp(cinode->lease_key, rsp->LeaseKey, - SMB2_LEASE_KEY_SIZE)) - continue; - - cifs_dbg(FYI, "found in the open list\n"); - cifs_dbg(FYI, "lease key match, lease break 0x%x\n", - lease_state); - - if (ack_req) - cfile->oplock_break_cancelled = false; - else - cfile->oplock_break_cancelled = true; - - set_bit(CIFS_INODE_PENDING_OPLOCK_BREAK, &cinode->flags); - - cfile->oplock_epoch = le16_to_cpu(rsp->Epoch); - cfile->oplock_level = lease_state; - - cifs_queue_oplock_break(cfile); - return true; - } - - return false; -} - -static struct cifs_pending_open * -smb2_tcon_find_pending_open_lease(struct cifs_tcon *tcon, - struct smb2_lease_break *rsp) -{ - __u8 lease_state = le32_to_cpu(rsp->NewLeaseState); - int ack_req = le32_to_cpu(rsp->Flags & - SMB2_NOTIFY_BREAK_LEASE_FLAG_ACK_REQUIRED); - struct cifs_pending_open *open; - struct cifs_pending_open *found = NULL; - - list_for_each_entry(open, &tcon->pending_opens, olist) { - if (memcmp(open->lease_key, rsp->LeaseKey, - SMB2_LEASE_KEY_SIZE)) - continue; - - if (!found && ack_req) { - found = open; - } - - cifs_dbg(FYI, "found in the pending open list\n"); - cifs_dbg(FYI, "lease key match, lease break 0x%x\n", - lease_state); - - open->oplock = lease_state; - } - - return found; -} - -static bool -smb2_is_valid_lease_break(char *buffer, struct TCP_Server_Info *server) -{ - struct smb2_lease_break *rsp = (struct smb2_lease_break *)buffer; - struct TCP_Server_Info *pserver; - struct cifs_ses *ses; - struct cifs_tcon *tcon; - struct cifs_pending_open *open; - - cifs_dbg(FYI, "Checking for lease break\n"); - - /* If server is a channel, select the primary channel */ - pserver = CIFS_SERVER_IS_CHAN(server) ? server->primary_server : server; - - /* look up tcon based on tid & uid */ - spin_lock(&cifs_tcp_ses_lock); - list_for_each_entry(ses, &pserver->smb_ses_list, smb_ses_list) { - list_for_each_entry(tcon, &ses->tcon_list, tcon_list) { - spin_lock(&tcon->open_file_lock); - cifs_stats_inc( - &tcon->stats.cifs_stats.num_oplock_brks); - if (smb2_tcon_has_lease(tcon, rsp)) { - spin_unlock(&tcon->open_file_lock); - spin_unlock(&cifs_tcp_ses_lock); - return true; - } - open = smb2_tcon_find_pending_open_lease(tcon, - rsp); - if (open) { - __u8 lease_key[SMB2_LEASE_KEY_SIZE]; - struct tcon_link *tlink; - - tlink = cifs_get_tlink(open->tlink); - memcpy(lease_key, open->lease_key, - SMB2_LEASE_KEY_SIZE); - spin_unlock(&tcon->open_file_lock); - spin_unlock(&cifs_tcp_ses_lock); - smb2_queue_pending_open_break(tlink, - lease_key, - rsp->NewLeaseState); - return true; - } - spin_unlock(&tcon->open_file_lock); - - if (cached_dir_lease_break(tcon, rsp->LeaseKey)) { - spin_unlock(&cifs_tcp_ses_lock); - return true; - } - } - } - spin_unlock(&cifs_tcp_ses_lock); - cifs_dbg(FYI, "Can not process lease break - no lease matched\n"); - trace_smb3_lease_not_found(le32_to_cpu(rsp->CurrentLeaseState), - le32_to_cpu(rsp->hdr.Id.SyncId.TreeId), - le64_to_cpu(rsp->hdr.SessionId), - *((u64 *)rsp->LeaseKey), - *((u64 *)&rsp->LeaseKey[8])); - - return false; -} - -bool -smb2_is_valid_oplock_break(char *buffer, struct TCP_Server_Info *server) -{ - struct smb2_oplock_break *rsp = (struct smb2_oplock_break *)buffer; - struct TCP_Server_Info *pserver; - struct cifs_ses *ses; - struct cifs_tcon *tcon; - struct cifsInodeInfo *cinode; - struct cifsFileInfo *cfile; - - cifs_dbg(FYI, "Checking for oplock break\n"); - - if (rsp->hdr.Command != SMB2_OPLOCK_BREAK) - return false; - - if (rsp->StructureSize != - smb2_rsp_struct_sizes[SMB2_OPLOCK_BREAK_HE]) { - if (le16_to_cpu(rsp->StructureSize) == 44) - return smb2_is_valid_lease_break(buffer, server); - else - return false; - } - - cifs_dbg(FYI, "oplock level 0x%x\n", rsp->OplockLevel); - - /* If server is a channel, select the primary channel */ - pserver = CIFS_SERVER_IS_CHAN(server) ? server->primary_server : server; - - /* look up tcon based on tid & uid */ - spin_lock(&cifs_tcp_ses_lock); - list_for_each_entry(ses, &pserver->smb_ses_list, smb_ses_list) { - list_for_each_entry(tcon, &ses->tcon_list, tcon_list) { - - spin_lock(&tcon->open_file_lock); - list_for_each_entry(cfile, &tcon->openFileList, tlist) { - if (rsp->PersistentFid != - cfile->fid.persistent_fid || - rsp->VolatileFid != - cfile->fid.volatile_fid) - continue; - - cifs_dbg(FYI, "file id match, oplock break\n"); - cifs_stats_inc( - &tcon->stats.cifs_stats.num_oplock_brks); - cinode = CIFS_I(d_inode(cfile->dentry)); - spin_lock(&cfile->file_info_lock); - if (!CIFS_CACHE_WRITE(cinode) && - rsp->OplockLevel == SMB2_OPLOCK_LEVEL_NONE) - cfile->oplock_break_cancelled = true; - else - cfile->oplock_break_cancelled = false; - - set_bit(CIFS_INODE_PENDING_OPLOCK_BREAK, - &cinode->flags); - - cfile->oplock_epoch = 0; - cfile->oplock_level = rsp->OplockLevel; - - spin_unlock(&cfile->file_info_lock); - - cifs_queue_oplock_break(cfile); - - spin_unlock(&tcon->open_file_lock); - spin_unlock(&cifs_tcp_ses_lock); - return true; - } - spin_unlock(&tcon->open_file_lock); - } - } - spin_unlock(&cifs_tcp_ses_lock); - cifs_dbg(FYI, "No file id matched, oplock break ignored\n"); - trace_smb3_oplock_not_found(0 /* no xid */, rsp->PersistentFid, - le32_to_cpu(rsp->hdr.Id.SyncId.TreeId), - le64_to_cpu(rsp->hdr.SessionId)); - - return true; -} - -void -smb2_cancelled_close_fid(struct work_struct *work) -{ - struct close_cancelled_open *cancelled = container_of(work, - struct close_cancelled_open, work); - struct cifs_tcon *tcon = cancelled->tcon; - int rc; - - if (cancelled->mid) - cifs_tcon_dbg(VFS, "Close unmatched open for MID:%llu\n", - cancelled->mid); - else - cifs_tcon_dbg(VFS, "Close interrupted close\n"); - - rc = SMB2_close(0, tcon, cancelled->fid.persistent_fid, - cancelled->fid.volatile_fid); - if (rc) - cifs_tcon_dbg(VFS, "Close cancelled mid failed rc:%d\n", rc); - - cifs_put_tcon(tcon); - kfree(cancelled); -} - -/* - * Caller should already has an extra reference to @tcon - * This function is used to queue work to close a handle to prevent leaks - * on the server. - * We handle two cases. If an open was interrupted after we sent the - * SMB2_CREATE to the server but before we processed the reply, and second - * if a close was interrupted before we sent the SMB2_CLOSE to the server. - */ -static int -__smb2_handle_cancelled_cmd(struct cifs_tcon *tcon, __u16 cmd, __u64 mid, - __u64 persistent_fid, __u64 volatile_fid) -{ - struct close_cancelled_open *cancelled; - - cancelled = kzalloc(sizeof(*cancelled), GFP_ATOMIC); - if (!cancelled) - return -ENOMEM; - - cancelled->fid.persistent_fid = persistent_fid; - cancelled->fid.volatile_fid = volatile_fid; - cancelled->tcon = tcon; - cancelled->cmd = cmd; - cancelled->mid = mid; - INIT_WORK(&cancelled->work, smb2_cancelled_close_fid); - WARN_ON(queue_work(cifsiod_wq, &cancelled->work) == false); - - return 0; -} - -int -smb2_handle_cancelled_close(struct cifs_tcon *tcon, __u64 persistent_fid, - __u64 volatile_fid) -{ - int rc; - - cifs_dbg(FYI, "%s: tc_count=%d\n", __func__, tcon->tc_count); - spin_lock(&cifs_tcp_ses_lock); - if (tcon->tc_count <= 0) { - struct TCP_Server_Info *server = NULL; - - WARN_ONCE(tcon->tc_count < 0, "tcon refcount is negative"); - spin_unlock(&cifs_tcp_ses_lock); - - if (tcon->ses) - server = tcon->ses->server; - - cifs_server_dbg(FYI, "tid=0x%x: tcon is closing, skipping async close retry of fid %llu %llu\n", - tcon->tid, persistent_fid, volatile_fid); - - return 0; - } - tcon->tc_count++; - spin_unlock(&cifs_tcp_ses_lock); - - rc = __smb2_handle_cancelled_cmd(tcon, SMB2_CLOSE_HE, 0, - persistent_fid, volatile_fid); - if (rc) - cifs_put_tcon(tcon); - - return rc; -} - -int -smb2_handle_cancelled_mid(struct mid_q_entry *mid, struct TCP_Server_Info *server) -{ - struct smb2_hdr *hdr = mid->resp_buf; - struct smb2_create_rsp *rsp = mid->resp_buf; - struct cifs_tcon *tcon; - int rc; - - if ((mid->optype & CIFS_CP_CREATE_CLOSE_OP) || hdr->Command != SMB2_CREATE || - hdr->Status != STATUS_SUCCESS) - return 0; - - tcon = smb2_find_smb_tcon(server, le64_to_cpu(hdr->SessionId), - le32_to_cpu(hdr->Id.SyncId.TreeId)); - if (!tcon) - return -ENOENT; - - rc = __smb2_handle_cancelled_cmd(tcon, - le16_to_cpu(hdr->Command), - le64_to_cpu(hdr->MessageId), - rsp->PersistentFileId, - rsp->VolatileFileId); - if (rc) - cifs_put_tcon(tcon); - - return rc; -} - -/** - * smb311_update_preauth_hash - update @ses hash with the packet data in @iov - * - * Assumes @iov does not contain the rfc1002 length and iov[0] has the - * SMB2 header. - * - * @ses: server session structure - * @server: pointer to server info - * @iov: array containing the SMB request we will send to the server - * @nvec: number of array entries for the iov - */ -int -smb311_update_preauth_hash(struct cifs_ses *ses, struct TCP_Server_Info *server, - struct kvec *iov, int nvec) -{ - int i, rc; - struct smb2_hdr *hdr; - struct shash_desc *sha512 = NULL; - - hdr = (struct smb2_hdr *)iov[0].iov_base; - /* neg prot are always taken */ - if (hdr->Command == SMB2_NEGOTIATE) - goto ok; - - /* - * If we process a command which wasn't a negprot it means the - * neg prot was already done, so the server dialect was set - * and we can test it. Preauth requires 3.1.1 for now. - */ - if (server->dialect != SMB311_PROT_ID) - return 0; - - if (hdr->Command != SMB2_SESSION_SETUP) - return 0; - - /* skip last sess setup response */ - if ((hdr->Flags & SMB2_FLAGS_SERVER_TO_REDIR) - && (hdr->Status == NT_STATUS_OK - || (hdr->Status != - cpu_to_le32(NT_STATUS_MORE_PROCESSING_REQUIRED)))) - return 0; - -ok: - rc = smb311_crypto_shash_allocate(server); - if (rc) - return rc; - - sha512 = server->secmech.sha512; - rc = crypto_shash_init(sha512); - if (rc) { - cifs_dbg(VFS, "%s: Could not init sha512 shash\n", __func__); - return rc; - } - - rc = crypto_shash_update(sha512, ses->preauth_sha_hash, - SMB2_PREAUTH_HASH_SIZE); - if (rc) { - cifs_dbg(VFS, "%s: Could not update sha512 shash\n", __func__); - return rc; - } - - for (i = 0; i < nvec; i++) { - rc = crypto_shash_update(sha512, iov[i].iov_base, iov[i].iov_len); - if (rc) { - cifs_dbg(VFS, "%s: Could not update sha512 shash\n", - __func__); - return rc; - } - } - - rc = crypto_shash_final(sha512, ses->preauth_sha_hash); - if (rc) { - cifs_dbg(VFS, "%s: Could not finalize sha512 shash\n", - __func__); - return rc; - } - - return 0; -} diff --git a/fs/cifs/smb2ops.c b/fs/cifs/smb2ops.c deleted file mode 100644 index d512440d35b6f..0000000000000 --- a/fs/cifs/smb2ops.c +++ /dev/null @@ -1,5805 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0 -/* - * SMB2 version specific operations - * - * Copyright (c) 2012, Jeff Layton - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include "cifsfs.h" -#include "cifsglob.h" -#include "smb2pdu.h" -#include "smb2proto.h" -#include "cifsproto.h" -#include "cifs_debug.h" -#include "cifs_unicode.h" -#include "smb2status.h" -#include "smb2glob.h" -#include "cifs_ioctl.h" -#include "smbdirect.h" -#include "fscache.h" -#include "fs_context.h" -#include "cached_dir.h" - -/* Change credits for different ops and return the total number of credits */ -static int -change_conf(struct TCP_Server_Info *server) -{ - server->credits += server->echo_credits + server->oplock_credits; - server->oplock_credits = server->echo_credits = 0; - switch (server->credits) { - case 0: - return 0; - case 1: - server->echoes = false; - server->oplocks = false; - break; - case 2: - server->echoes = true; - server->oplocks = false; - server->echo_credits = 1; - break; - default: - server->echoes = true; - if (enable_oplocks) { - server->oplocks = true; - server->oplock_credits = 1; - } else - server->oplocks = false; - - server->echo_credits = 1; - } - server->credits -= server->echo_credits + server->oplock_credits; - return server->credits + server->echo_credits + server->oplock_credits; -} - -static void -smb2_add_credits(struct TCP_Server_Info *server, - const struct cifs_credits *credits, const int optype) -{ - int *val, rc = -1; - int scredits, in_flight; - unsigned int add = credits->value; - unsigned int instance = credits->instance; - bool reconnect_detected = false; - bool reconnect_with_invalid_credits = false; - - spin_lock(&server->req_lock); - val = server->ops->get_credits_field(server, optype); - - /* eg found case where write overlapping reconnect messed up credits */ - if (((optype & CIFS_OP_MASK) == CIFS_NEG_OP) && (*val != 0)) - reconnect_with_invalid_credits = true; - - if ((instance == 0) || (instance == server->reconnect_instance)) - *val += add; - else - reconnect_detected = true; - - if (*val > 65000) { - *val = 65000; /* Don't get near 64K credits, avoid srv bugs */ - pr_warn_once("server overflowed SMB3 credits\n"); - trace_smb3_overflow_credits(server->CurrentMid, - server->conn_id, server->hostname, *val, - add, server->in_flight); - } - server->in_flight--; - if (server->in_flight == 0 && - ((optype & CIFS_OP_MASK) != CIFS_NEG_OP) && - ((optype & CIFS_OP_MASK) != CIFS_SESS_OP)) - rc = change_conf(server); - /* - * Sometimes server returns 0 credits on oplock break ack - we need to - * rebalance credits in this case. - */ - else if (server->in_flight > 0 && server->oplock_credits == 0 && - server->oplocks) { - if (server->credits > 1) { - server->credits--; - server->oplock_credits++; - } - } - scredits = *val; - in_flight = server->in_flight; - spin_unlock(&server->req_lock); - wake_up(&server->request_q); - - if (reconnect_detected) { - trace_smb3_reconnect_detected(server->CurrentMid, - server->conn_id, server->hostname, scredits, add, in_flight); - - cifs_dbg(FYI, "trying to put %d credits from the old server instance %d\n", - add, instance); - } - - if (reconnect_with_invalid_credits) { - trace_smb3_reconnect_with_invalid_credits(server->CurrentMid, - server->conn_id, server->hostname, scredits, add, in_flight); - cifs_dbg(FYI, "Negotiate operation when server credits is non-zero. Optype: %d, server credits: %d, credits added: %d\n", - optype, scredits, add); - } - - spin_lock(&server->srv_lock); - if (server->tcpStatus == CifsNeedReconnect - || server->tcpStatus == CifsExiting) { - spin_unlock(&server->srv_lock); - return; - } - spin_unlock(&server->srv_lock); - - switch (rc) { - case -1: - /* change_conf hasn't been executed */ - break; - case 0: - cifs_server_dbg(VFS, "Possible client or server bug - zero credits\n"); - break; - case 1: - cifs_server_dbg(VFS, "disabling echoes and oplocks\n"); - break; - case 2: - cifs_dbg(FYI, "disabling oplocks\n"); - break; - default: - /* change_conf rebalanced credits for different types */ - break; - } - - trace_smb3_add_credits(server->CurrentMid, - server->conn_id, server->hostname, scredits, add, in_flight); - cifs_dbg(FYI, "%s: added %u credits total=%d\n", __func__, add, scredits); -} - -static void -smb2_set_credits(struct TCP_Server_Info *server, const int val) -{ - int scredits, in_flight; - - spin_lock(&server->req_lock); - server->credits = val; - if (val == 1) - server->reconnect_instance++; - scredits = server->credits; - in_flight = server->in_flight; - spin_unlock(&server->req_lock); - - trace_smb3_set_credits(server->CurrentMid, - server->conn_id, server->hostname, scredits, val, in_flight); - cifs_dbg(FYI, "%s: set %u credits\n", __func__, val); - - /* don't log while holding the lock */ - if (val == 1) - cifs_dbg(FYI, "set credits to 1 due to smb2 reconnect\n"); -} - -static int * -smb2_get_credits_field(struct TCP_Server_Info *server, const int optype) -{ - switch (optype) { - case CIFS_ECHO_OP: - return &server->echo_credits; - case CIFS_OBREAK_OP: - return &server->oplock_credits; - default: - return &server->credits; - } -} - -static unsigned int -smb2_get_credits(struct mid_q_entry *mid) -{ - return mid->credits_received; -} - -static int -smb2_wait_mtu_credits(struct TCP_Server_Info *server, unsigned int size, - unsigned int *num, struct cifs_credits *credits) -{ - int rc = 0; - unsigned int scredits, in_flight; - - spin_lock(&server->req_lock); - while (1) { - if (server->credits <= 0) { - spin_unlock(&server->req_lock); - cifs_num_waiters_inc(server); - rc = wait_event_killable(server->request_q, - has_credits(server, &server->credits, 1)); - cifs_num_waiters_dec(server); - if (rc) - return rc; - spin_lock(&server->req_lock); - } else { - spin_unlock(&server->req_lock); - spin_lock(&server->srv_lock); - if (server->tcpStatus == CifsExiting) { - spin_unlock(&server->srv_lock); - return -ENOENT; - } - spin_unlock(&server->srv_lock); - - spin_lock(&server->req_lock); - scredits = server->credits; - /* can deadlock with reopen */ - if (scredits <= 8) { - *num = SMB2_MAX_BUFFER_SIZE; - credits->value = 0; - credits->instance = 0; - break; - } - - /* leave some credits for reopen and other ops */ - scredits -= 8; - *num = min_t(unsigned int, size, - scredits * SMB2_MAX_BUFFER_SIZE); - - credits->value = - DIV_ROUND_UP(*num, SMB2_MAX_BUFFER_SIZE); - credits->instance = server->reconnect_instance; - server->credits -= credits->value; - server->in_flight++; - if (server->in_flight > server->max_in_flight) - server->max_in_flight = server->in_flight; - break; - } - } - scredits = server->credits; - in_flight = server->in_flight; - spin_unlock(&server->req_lock); - - trace_smb3_wait_credits(server->CurrentMid, - server->conn_id, server->hostname, scredits, -(credits->value), in_flight); - cifs_dbg(FYI, "%s: removed %u credits total=%d\n", - __func__, credits->value, scredits); - - return rc; -} - -static int -smb2_adjust_credits(struct TCP_Server_Info *server, - struct cifs_credits *credits, - const unsigned int payload_size) -{ - int new_val = DIV_ROUND_UP(payload_size, SMB2_MAX_BUFFER_SIZE); - int scredits, in_flight; - - if (!credits->value || credits->value == new_val) - return 0; - - if (credits->value < new_val) { - trace_smb3_too_many_credits(server->CurrentMid, - server->conn_id, server->hostname, 0, credits->value - new_val, 0); - cifs_server_dbg(VFS, "request has less credits (%d) than required (%d)", - credits->value, new_val); - - return -ENOTSUPP; - } - - spin_lock(&server->req_lock); - - if (server->reconnect_instance != credits->instance) { - scredits = server->credits; - in_flight = server->in_flight; - spin_unlock(&server->req_lock); - - trace_smb3_reconnect_detected(server->CurrentMid, - server->conn_id, server->hostname, scredits, - credits->value - new_val, in_flight); - cifs_server_dbg(VFS, "trying to return %d credits to old session\n", - credits->value - new_val); - return -EAGAIN; - } - - server->credits += credits->value - new_val; - scredits = server->credits; - in_flight = server->in_flight; - spin_unlock(&server->req_lock); - wake_up(&server->request_q); - - trace_smb3_adj_credits(server->CurrentMid, - server->conn_id, server->hostname, scredits, - credits->value - new_val, in_flight); - cifs_dbg(FYI, "%s: adjust added %u credits total=%d\n", - __func__, credits->value - new_val, scredits); - - credits->value = new_val; - - return 0; -} - -static __u64 -smb2_get_next_mid(struct TCP_Server_Info *server) -{ - __u64 mid; - /* for SMB2 we need the current value */ - spin_lock(&server->mid_lock); - mid = server->CurrentMid++; - spin_unlock(&server->mid_lock); - return mid; -} - -static void -smb2_revert_current_mid(struct TCP_Server_Info *server, const unsigned int val) -{ - spin_lock(&server->mid_lock); - if (server->CurrentMid >= val) - server->CurrentMid -= val; - spin_unlock(&server->mid_lock); -} - -static struct mid_q_entry * -__smb2_find_mid(struct TCP_Server_Info *server, char *buf, bool dequeue) -{ - struct mid_q_entry *mid; - struct smb2_hdr *shdr = (struct smb2_hdr *)buf; - __u64 wire_mid = le64_to_cpu(shdr->MessageId); - - if (shdr->ProtocolId == SMB2_TRANSFORM_PROTO_NUM) { - cifs_server_dbg(VFS, "Encrypted frame parsing not supported yet\n"); - return NULL; - } - - spin_lock(&server->mid_lock); - list_for_each_entry(mid, &server->pending_mid_q, qhead) { - if ((mid->mid == wire_mid) && - (mid->mid_state == MID_REQUEST_SUBMITTED) && - (mid->command == shdr->Command)) { - kref_get(&mid->refcount); - if (dequeue) { - list_del_init(&mid->qhead); - mid->mid_flags |= MID_DELETED; - } - spin_unlock(&server->mid_lock); - return mid; - } - } - spin_unlock(&server->mid_lock); - return NULL; -} - -static struct mid_q_entry * -smb2_find_mid(struct TCP_Server_Info *server, char *buf) -{ - return __smb2_find_mid(server, buf, false); -} - -static struct mid_q_entry * -smb2_find_dequeue_mid(struct TCP_Server_Info *server, char *buf) -{ - return __smb2_find_mid(server, buf, true); -} - -static void -smb2_dump_detail(void *buf, struct TCP_Server_Info *server) -{ -#ifdef CONFIG_CIFS_DEBUG2 - struct smb2_hdr *shdr = (struct smb2_hdr *)buf; - - cifs_server_dbg(VFS, "Cmd: %d Err: 0x%x Flags: 0x%x Mid: %llu Pid: %d\n", - shdr->Command, shdr->Status, shdr->Flags, shdr->MessageId, - shdr->Id.SyncId.ProcessId); - cifs_server_dbg(VFS, "smb buf %p len %u\n", buf, - server->ops->calc_smb_size(buf)); -#endif -} - -static bool -smb2_need_neg(struct TCP_Server_Info *server) -{ - return server->max_read == 0; -} - -static int -smb2_negotiate(const unsigned int xid, - struct cifs_ses *ses, - struct TCP_Server_Info *server) -{ - int rc; - - spin_lock(&server->mid_lock); - server->CurrentMid = 0; - spin_unlock(&server->mid_lock); - rc = SMB2_negotiate(xid, ses, server); - /* BB we probably don't need to retry with modern servers */ - if (rc == -EAGAIN) - rc = -EHOSTDOWN; - return rc; -} - -static unsigned int -smb2_negotiate_wsize(struct cifs_tcon *tcon, struct smb3_fs_context *ctx) -{ - struct TCP_Server_Info *server = tcon->ses->server; - unsigned int wsize; - - /* start with specified wsize, or default */ - wsize = ctx->wsize ? ctx->wsize : CIFS_DEFAULT_IOSIZE; - wsize = min_t(unsigned int, wsize, server->max_write); - if (!(server->capabilities & SMB2_GLOBAL_CAP_LARGE_MTU)) - wsize = min_t(unsigned int, wsize, SMB2_MAX_BUFFER_SIZE); - - return wsize; -} - -static unsigned int -smb3_negotiate_wsize(struct cifs_tcon *tcon, struct smb3_fs_context *ctx) -{ - struct TCP_Server_Info *server = tcon->ses->server; - unsigned int wsize; - - /* start with specified wsize, or default */ - wsize = ctx->wsize ? ctx->wsize : SMB3_DEFAULT_IOSIZE; - wsize = min_t(unsigned int, wsize, server->max_write); -#ifdef CONFIG_CIFS_SMB_DIRECT - if (server->rdma) { - if (server->sign) - /* - * Account for SMB2 data transfer packet header and - * possible encryption header - */ - wsize = min_t(unsigned int, - wsize, - server->smbd_conn->max_fragmented_send_size - - SMB2_READWRITE_PDU_HEADER_SIZE - - sizeof(struct smb2_transform_hdr)); - else - wsize = min_t(unsigned int, - wsize, server->smbd_conn->max_readwrite_size); - } -#endif - if (!(server->capabilities & SMB2_GLOBAL_CAP_LARGE_MTU)) - wsize = min_t(unsigned int, wsize, SMB2_MAX_BUFFER_SIZE); - - return wsize; -} - -static unsigned int -smb2_negotiate_rsize(struct cifs_tcon *tcon, struct smb3_fs_context *ctx) -{ - struct TCP_Server_Info *server = tcon->ses->server; - unsigned int rsize; - - /* start with specified rsize, or default */ - rsize = ctx->rsize ? ctx->rsize : CIFS_DEFAULT_IOSIZE; - rsize = min_t(unsigned int, rsize, server->max_read); - - if (!(server->capabilities & SMB2_GLOBAL_CAP_LARGE_MTU)) - rsize = min_t(unsigned int, rsize, SMB2_MAX_BUFFER_SIZE); - - return rsize; -} - -static unsigned int -smb3_negotiate_rsize(struct cifs_tcon *tcon, struct smb3_fs_context *ctx) -{ - struct TCP_Server_Info *server = tcon->ses->server; - unsigned int rsize; - - /* start with specified rsize, or default */ - rsize = ctx->rsize ? ctx->rsize : SMB3_DEFAULT_IOSIZE; - rsize = min_t(unsigned int, rsize, server->max_read); -#ifdef CONFIG_CIFS_SMB_DIRECT - if (server->rdma) { - if (server->sign) - /* - * Account for SMB2 data transfer packet header and - * possible encryption header - */ - rsize = min_t(unsigned int, - rsize, - server->smbd_conn->max_fragmented_recv_size - - SMB2_READWRITE_PDU_HEADER_SIZE - - sizeof(struct smb2_transform_hdr)); - else - rsize = min_t(unsigned int, - rsize, server->smbd_conn->max_readwrite_size); - } -#endif - - if (!(server->capabilities & SMB2_GLOBAL_CAP_LARGE_MTU)) - rsize = min_t(unsigned int, rsize, SMB2_MAX_BUFFER_SIZE); - - return rsize; -} - -static int -parse_server_interfaces(struct network_interface_info_ioctl_rsp *buf, - size_t buf_len, struct cifs_ses *ses, bool in_mount) -{ - struct network_interface_info_ioctl_rsp *p; - struct sockaddr_in *addr4; - struct sockaddr_in6 *addr6; - struct iface_info_ipv4 *p4; - struct iface_info_ipv6 *p6; - struct cifs_server_iface *info = NULL, *iface = NULL, *niface = NULL; - struct cifs_server_iface tmp_iface; - ssize_t bytes_left; - size_t next = 0; - int nb_iface = 0; - int rc = 0, ret = 0; - - bytes_left = buf_len; - p = buf; - - spin_lock(&ses->iface_lock); - /* do not query too frequently, this time with lock held */ - if (ses->iface_last_update && - time_before(jiffies, ses->iface_last_update + - (SMB_INTERFACE_POLL_INTERVAL * HZ))) { - spin_unlock(&ses->iface_lock); - return 0; - } - - /* - * Go through iface_list and do kref_put to remove - * any unused ifaces. ifaces in use will be removed - * when the last user calls a kref_put on it - */ - list_for_each_entry_safe(iface, niface, &ses->iface_list, - iface_head) { - iface->is_active = 0; - kref_put(&iface->refcount, release_iface); - ses->iface_count--; - } - spin_unlock(&ses->iface_lock); - - /* - * Samba server e.g. can return an empty interface list in some cases, - * which would only be a problem if we were requesting multichannel - */ - if (bytes_left == 0) { - /* avoid spamming logs every 10 minutes, so log only in mount */ - if ((ses->chan_max > 1) && in_mount) - cifs_dbg(VFS, - "multichannel not available\n" - "Empty network interface list returned by server %s\n", - ses->server->hostname); - rc = -EINVAL; - goto out; - } - - while (bytes_left >= sizeof(*p)) { - memset(&tmp_iface, 0, sizeof(tmp_iface)); - tmp_iface.speed = le64_to_cpu(p->LinkSpeed); - tmp_iface.rdma_capable = le32_to_cpu(p->Capability & RDMA_CAPABLE) ? 1 : 0; - tmp_iface.rss_capable = le32_to_cpu(p->Capability & RSS_CAPABLE) ? 1 : 0; - - switch (p->Family) { - /* - * The kernel and wire socket structures have the same - * layout and use network byte order but make the - * conversion explicit in case either one changes. - */ - case INTERNETWORK: - addr4 = (struct sockaddr_in *)&tmp_iface.sockaddr; - p4 = (struct iface_info_ipv4 *)p->Buffer; - addr4->sin_family = AF_INET; - memcpy(&addr4->sin_addr, &p4->IPv4Address, 4); - - /* [MS-SMB2] 2.2.32.5.1.1 Clients MUST ignore these */ - addr4->sin_port = cpu_to_be16(CIFS_PORT); - - cifs_dbg(FYI, "%s: ipv4 %pI4\n", __func__, - &addr4->sin_addr); - break; - case INTERNETWORKV6: - addr6 = (struct sockaddr_in6 *)&tmp_iface.sockaddr; - p6 = (struct iface_info_ipv6 *)p->Buffer; - addr6->sin6_family = AF_INET6; - memcpy(&addr6->sin6_addr, &p6->IPv6Address, 16); - - /* [MS-SMB2] 2.2.32.5.1.2 Clients MUST ignore these */ - addr6->sin6_flowinfo = 0; - addr6->sin6_scope_id = 0; - addr6->sin6_port = cpu_to_be16(CIFS_PORT); - - cifs_dbg(FYI, "%s: ipv6 %pI6\n", __func__, - &addr6->sin6_addr); - break; - default: - cifs_dbg(VFS, - "%s: skipping unsupported socket family\n", - __func__); - goto next_iface; - } - - /* - * The iface_list is assumed to be sorted by speed. - * Check if the new interface exists in that list. - * NEVER change iface. it could be in use. - * Add a new one instead - */ - spin_lock(&ses->iface_lock); - iface = niface = NULL; - list_for_each_entry_safe(iface, niface, &ses->iface_list, - iface_head) { - ret = iface_cmp(iface, &tmp_iface); - if (!ret) { - /* just get a ref so that it doesn't get picked/freed */ - iface->is_active = 1; - kref_get(&iface->refcount); - ses->iface_count++; - spin_unlock(&ses->iface_lock); - goto next_iface; - } else if (ret < 0) { - /* all remaining ifaces are slower */ - kref_get(&iface->refcount); - break; - } - } - spin_unlock(&ses->iface_lock); - - /* no match. insert the entry in the list */ - info = kmalloc(sizeof(struct cifs_server_iface), - GFP_KERNEL); - if (!info) { - rc = -ENOMEM; - goto out; - } - memcpy(info, &tmp_iface, sizeof(tmp_iface)); - - /* add this new entry to the list */ - kref_init(&info->refcount); - info->is_active = 1; - - cifs_dbg(FYI, "%s: adding iface %zu\n", __func__, ses->iface_count); - cifs_dbg(FYI, "%s: speed %zu bps\n", __func__, info->speed); - cifs_dbg(FYI, "%s: capabilities 0x%08x\n", __func__, - le32_to_cpu(p->Capability)); - - spin_lock(&ses->iface_lock); - if (!list_entry_is_head(iface, &ses->iface_list, iface_head)) { - list_add_tail(&info->iface_head, &iface->iface_head); - kref_put(&iface->refcount, release_iface); - } else - list_add_tail(&info->iface_head, &ses->iface_list); - - ses->iface_count++; - spin_unlock(&ses->iface_lock); - ses->iface_last_update = jiffies; -next_iface: - nb_iface++; - next = le32_to_cpu(p->Next); - if (!next) { - bytes_left -= sizeof(*p); - break; - } - p = (struct network_interface_info_ioctl_rsp *)((u8 *)p+next); - bytes_left -= next; - } - - if (!nb_iface) { - cifs_dbg(VFS, "%s: malformed interface info\n", __func__); - rc = -EINVAL; - goto out; - } - - /* Azure rounds the buffer size up 8, to a 16 byte boundary */ - if ((bytes_left > 8) || p->Next) - cifs_dbg(VFS, "%s: incomplete interface info\n", __func__); - - - if (!ses->iface_count) { - rc = -EINVAL; - goto out; - } - -out: - return rc; -} - -int -SMB3_request_interfaces(const unsigned int xid, struct cifs_tcon *tcon, bool in_mount) -{ - int rc; - unsigned int ret_data_len = 0; - struct network_interface_info_ioctl_rsp *out_buf = NULL; - struct cifs_ses *ses = tcon->ses; - - /* do not query too frequently */ - if (ses->iface_last_update && - time_before(jiffies, ses->iface_last_update + - (SMB_INTERFACE_POLL_INTERVAL * HZ))) - return 0; - - rc = SMB2_ioctl(xid, tcon, NO_FILE_ID, NO_FILE_ID, - FSCTL_QUERY_NETWORK_INTERFACE_INFO, - NULL /* no data input */, 0 /* no data input */, - CIFSMaxBufSize, (char **)&out_buf, &ret_data_len); - if (rc == -EOPNOTSUPP) { - cifs_dbg(FYI, - "server does not support query network interfaces\n"); - ret_data_len = 0; - } else if (rc != 0) { - cifs_tcon_dbg(VFS, "error %d on ioctl to get interface list\n", rc); - goto out; - } - - rc = parse_server_interfaces(out_buf, ret_data_len, ses, in_mount); - if (rc) - goto out; - -out: - kfree(out_buf); - return rc; -} - -static void -smb3_qfs_tcon(const unsigned int xid, struct cifs_tcon *tcon, - struct cifs_sb_info *cifs_sb) -{ - int rc; - __le16 srch_path = 0; /* Null - open root of share */ - u8 oplock = SMB2_OPLOCK_LEVEL_NONE; - struct cifs_open_parms oparms; - struct cifs_fid fid; - struct cached_fid *cfid = NULL; - - oparms = (struct cifs_open_parms) { - .tcon = tcon, - .path = "", - .desired_access = FILE_READ_ATTRIBUTES, - .disposition = FILE_OPEN, - .create_options = cifs_create_options(cifs_sb, 0), - .fid = &fid, - }; - - rc = open_cached_dir(xid, tcon, "", cifs_sb, false, &cfid); - if (rc == 0) - memcpy(&fid, &cfid->fid, sizeof(struct cifs_fid)); - else - rc = SMB2_open(xid, &oparms, &srch_path, &oplock, NULL, NULL, - NULL, NULL); - if (rc) - return; - - SMB3_request_interfaces(xid, tcon, true /* called during mount */); - - SMB2_QFS_attr(xid, tcon, fid.persistent_fid, fid.volatile_fid, - FS_ATTRIBUTE_INFORMATION); - SMB2_QFS_attr(xid, tcon, fid.persistent_fid, fid.volatile_fid, - FS_DEVICE_INFORMATION); - SMB2_QFS_attr(xid, tcon, fid.persistent_fid, fid.volatile_fid, - FS_VOLUME_INFORMATION); - SMB2_QFS_attr(xid, tcon, fid.persistent_fid, fid.volatile_fid, - FS_SECTOR_SIZE_INFORMATION); /* SMB3 specific */ - if (cfid == NULL) - SMB2_close(xid, tcon, fid.persistent_fid, fid.volatile_fid); - else - close_cached_dir(cfid); -} - -static void -smb2_qfs_tcon(const unsigned int xid, struct cifs_tcon *tcon, - struct cifs_sb_info *cifs_sb) -{ - int rc; - __le16 srch_path = 0; /* Null - open root of share */ - u8 oplock = SMB2_OPLOCK_LEVEL_NONE; - struct cifs_open_parms oparms; - struct cifs_fid fid; - - oparms = (struct cifs_open_parms) { - .tcon = tcon, - .path = "", - .desired_access = FILE_READ_ATTRIBUTES, - .disposition = FILE_OPEN, - .create_options = cifs_create_options(cifs_sb, 0), - .fid = &fid, - }; - - rc = SMB2_open(xid, &oparms, &srch_path, &oplock, NULL, NULL, - NULL, NULL); - if (rc) - return; - - SMB2_QFS_attr(xid, tcon, fid.persistent_fid, fid.volatile_fid, - FS_ATTRIBUTE_INFORMATION); - SMB2_QFS_attr(xid, tcon, fid.persistent_fid, fid.volatile_fid, - FS_DEVICE_INFORMATION); - SMB2_close(xid, tcon, fid.persistent_fid, fid.volatile_fid); -} - -static int -smb2_is_path_accessible(const unsigned int xid, struct cifs_tcon *tcon, - struct cifs_sb_info *cifs_sb, const char *full_path) -{ - __le16 *utf16_path; - __u8 oplock = SMB2_OPLOCK_LEVEL_NONE; - int err_buftype = CIFS_NO_BUFFER; - struct cifs_open_parms oparms; - struct kvec err_iov = {}; - struct cifs_fid fid; - struct cached_fid *cfid; - bool islink; - int rc, rc2; - - rc = open_cached_dir(xid, tcon, full_path, cifs_sb, true, &cfid); - if (!rc) { - if (cfid->has_lease) { - close_cached_dir(cfid); - return 0; - } - close_cached_dir(cfid); - } - - utf16_path = cifs_convert_path_to_utf16(full_path, cifs_sb); - if (!utf16_path) - return -ENOMEM; - - oparms = (struct cifs_open_parms) { - .tcon = tcon, - .path = full_path, - .desired_access = FILE_READ_ATTRIBUTES, - .disposition = FILE_OPEN, - .create_options = cifs_create_options(cifs_sb, 0), - .fid = &fid, - }; - - rc = SMB2_open(xid, &oparms, utf16_path, &oplock, NULL, NULL, - &err_iov, &err_buftype); - if (rc) { - struct smb2_hdr *hdr = err_iov.iov_base; - - if (unlikely(!hdr || err_buftype == CIFS_NO_BUFFER)) - goto out; - - if (rc != -EREMOTE && hdr->Status == STATUS_OBJECT_NAME_INVALID) { - rc2 = cifs_inval_name_dfs_link_error(xid, tcon, cifs_sb, - full_path, &islink); - if (rc2) { - rc = rc2; - goto out; - } - if (islink) - rc = -EREMOTE; - } - if (rc == -EREMOTE && IS_ENABLED(CONFIG_CIFS_DFS_UPCALL) && cifs_sb && - (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_NO_DFS)) - rc = -EOPNOTSUPP; - goto out; - } - - rc = SMB2_close(xid, tcon, fid.persistent_fid, fid.volatile_fid); - -out: - free_rsp_buf(err_buftype, err_iov.iov_base); - kfree(utf16_path); - return rc; -} - -static int smb2_get_srv_inum(const unsigned int xid, struct cifs_tcon *tcon, - struct cifs_sb_info *cifs_sb, const char *full_path, - u64 *uniqueid, struct cifs_open_info_data *data) -{ - *uniqueid = le64_to_cpu(data->fi.IndexNumber); - return 0; -} - -static int smb2_query_file_info(const unsigned int xid, struct cifs_tcon *tcon, - struct cifsFileInfo *cfile, struct cifs_open_info_data *data) -{ - struct cifs_fid *fid = &cfile->fid; - - if (cfile->symlink_target) { - data->symlink_target = kstrdup(cfile->symlink_target, GFP_KERNEL); - if (!data->symlink_target) - return -ENOMEM; - } - return SMB2_query_info(xid, tcon, fid->persistent_fid, fid->volatile_fid, &data->fi); -} - -#ifdef CONFIG_CIFS_XATTR -static ssize_t -move_smb2_ea_to_cifs(char *dst, size_t dst_size, - struct smb2_file_full_ea_info *src, size_t src_size, - const unsigned char *ea_name) -{ - int rc = 0; - unsigned int ea_name_len = ea_name ? strlen(ea_name) : 0; - char *name, *value; - size_t buf_size = dst_size; - size_t name_len, value_len, user_name_len; - - while (src_size > 0) { - name_len = (size_t)src->ea_name_length; - value_len = (size_t)le16_to_cpu(src->ea_value_length); - - if (name_len == 0) - break; - - if (src_size < 8 + name_len + 1 + value_len) { - cifs_dbg(FYI, "EA entry goes beyond length of list\n"); - rc = -EIO; - goto out; - } - - name = &src->ea_data[0]; - value = &src->ea_data[src->ea_name_length + 1]; - - if (ea_name) { - if (ea_name_len == name_len && - memcmp(ea_name, name, name_len) == 0) { - rc = value_len; - if (dst_size == 0) - goto out; - if (dst_size < value_len) { - rc = -ERANGE; - goto out; - } - memcpy(dst, value, value_len); - goto out; - } - } else { - /* 'user.' plus a terminating null */ - user_name_len = 5 + 1 + name_len; - - if (buf_size == 0) { - /* skip copy - calc size only */ - rc += user_name_len; - } else if (dst_size >= user_name_len) { - dst_size -= user_name_len; - memcpy(dst, "user.", 5); - dst += 5; - memcpy(dst, src->ea_data, name_len); - dst += name_len; - *dst = 0; - ++dst; - rc += user_name_len; - } else { - /* stop before overrun buffer */ - rc = -ERANGE; - break; - } - } - - if (!src->next_entry_offset) - break; - - if (src_size < le32_to_cpu(src->next_entry_offset)) { - /* stop before overrun buffer */ - rc = -ERANGE; - break; - } - src_size -= le32_to_cpu(src->next_entry_offset); - src = (void *)((char *)src + - le32_to_cpu(src->next_entry_offset)); - } - - /* didn't find the named attribute */ - if (ea_name) - rc = -ENODATA; - -out: - return (ssize_t)rc; -} - -static ssize_t -smb2_query_eas(const unsigned int xid, struct cifs_tcon *tcon, - const unsigned char *path, const unsigned char *ea_name, - char *ea_data, size_t buf_size, - struct cifs_sb_info *cifs_sb) -{ - int rc; - struct kvec rsp_iov = {NULL, 0}; - int buftype = CIFS_NO_BUFFER; - struct smb2_query_info_rsp *rsp; - struct smb2_file_full_ea_info *info = NULL; - - rc = smb2_query_info_compound(xid, tcon, path, - FILE_READ_EA, - FILE_FULL_EA_INFORMATION, - SMB2_O_INFO_FILE, - CIFSMaxBufSize - - MAX_SMB2_CREATE_RESPONSE_SIZE - - MAX_SMB2_CLOSE_RESPONSE_SIZE, - &rsp_iov, &buftype, cifs_sb); - if (rc) { - /* - * If ea_name is NULL (listxattr) and there are no EAs, - * return 0 as it's not an error. Otherwise, the specified - * ea_name was not found. - */ - if (!ea_name && rc == -ENODATA) - rc = 0; - goto qeas_exit; - } - - rsp = (struct smb2_query_info_rsp *)rsp_iov.iov_base; - rc = smb2_validate_iov(le16_to_cpu(rsp->OutputBufferOffset), - le32_to_cpu(rsp->OutputBufferLength), - &rsp_iov, - sizeof(struct smb2_file_full_ea_info)); - if (rc) - goto qeas_exit; - - info = (struct smb2_file_full_ea_info *)( - le16_to_cpu(rsp->OutputBufferOffset) + (char *)rsp); - rc = move_smb2_ea_to_cifs(ea_data, buf_size, info, - le32_to_cpu(rsp->OutputBufferLength), ea_name); - - qeas_exit: - free_rsp_buf(buftype, rsp_iov.iov_base); - return rc; -} - - -static int -smb2_set_ea(const unsigned int xid, struct cifs_tcon *tcon, - const char *path, const char *ea_name, const void *ea_value, - const __u16 ea_value_len, const struct nls_table *nls_codepage, - struct cifs_sb_info *cifs_sb) -{ - struct cifs_ses *ses = tcon->ses; - struct TCP_Server_Info *server = cifs_pick_channel(ses); - __le16 *utf16_path = NULL; - int ea_name_len = strlen(ea_name); - int flags = CIFS_CP_CREATE_CLOSE_OP; - int len; - struct smb_rqst rqst[3]; - int resp_buftype[3]; - struct kvec rsp_iov[3]; - struct kvec open_iov[SMB2_CREATE_IOV_SIZE]; - struct cifs_open_parms oparms; - __u8 oplock = SMB2_OPLOCK_LEVEL_NONE; - struct cifs_fid fid; - struct kvec si_iov[SMB2_SET_INFO_IOV_SIZE]; - unsigned int size[1]; - void *data[1]; - struct smb2_file_full_ea_info *ea = NULL; - struct kvec close_iov[1]; - struct smb2_query_info_rsp *rsp; - int rc, used_len = 0; - - if (smb3_encryption_required(tcon)) - flags |= CIFS_TRANSFORM_REQ; - - if (ea_name_len > 255) - return -EINVAL; - - utf16_path = cifs_convert_path_to_utf16(path, cifs_sb); - if (!utf16_path) - return -ENOMEM; - - memset(rqst, 0, sizeof(rqst)); - resp_buftype[0] = resp_buftype[1] = resp_buftype[2] = CIFS_NO_BUFFER; - memset(rsp_iov, 0, sizeof(rsp_iov)); - - if (ses->server->ops->query_all_EAs) { - if (!ea_value) { - rc = ses->server->ops->query_all_EAs(xid, tcon, path, - ea_name, NULL, 0, - cifs_sb); - if (rc == -ENODATA) - goto sea_exit; - } else { - /* If we are adding a attribute we should first check - * if there will be enough space available to store - * the new EA. If not we should not add it since we - * would not be able to even read the EAs back. - */ - rc = smb2_query_info_compound(xid, tcon, path, - FILE_READ_EA, - FILE_FULL_EA_INFORMATION, - SMB2_O_INFO_FILE, - CIFSMaxBufSize - - MAX_SMB2_CREATE_RESPONSE_SIZE - - MAX_SMB2_CLOSE_RESPONSE_SIZE, - &rsp_iov[1], &resp_buftype[1], cifs_sb); - if (rc == 0) { - rsp = (struct smb2_query_info_rsp *)rsp_iov[1].iov_base; - used_len = le32_to_cpu(rsp->OutputBufferLength); - } - free_rsp_buf(resp_buftype[1], rsp_iov[1].iov_base); - resp_buftype[1] = CIFS_NO_BUFFER; - memset(&rsp_iov[1], 0, sizeof(rsp_iov[1])); - rc = 0; - - /* Use a fudge factor of 256 bytes in case we collide - * with a different set_EAs command. - */ - if(CIFSMaxBufSize - MAX_SMB2_CREATE_RESPONSE_SIZE - - MAX_SMB2_CLOSE_RESPONSE_SIZE - 256 < - used_len + ea_name_len + ea_value_len + 1) { - rc = -ENOSPC; - goto sea_exit; - } - } - } - - /* Open */ - memset(&open_iov, 0, sizeof(open_iov)); - rqst[0].rq_iov = open_iov; - rqst[0].rq_nvec = SMB2_CREATE_IOV_SIZE; - - oparms = (struct cifs_open_parms) { - .tcon = tcon, - .path = path, - .desired_access = FILE_WRITE_EA, - .disposition = FILE_OPEN, - .create_options = cifs_create_options(cifs_sb, 0), - .fid = &fid, - }; - - rc = SMB2_open_init(tcon, server, - &rqst[0], &oplock, &oparms, utf16_path); - if (rc) - goto sea_exit; - smb2_set_next_command(tcon, &rqst[0]); - - - /* Set Info */ - memset(&si_iov, 0, sizeof(si_iov)); - rqst[1].rq_iov = si_iov; - rqst[1].rq_nvec = 1; - - len = sizeof(*ea) + ea_name_len + ea_value_len + 1; - ea = kzalloc(len, GFP_KERNEL); - if (ea == NULL) { - rc = -ENOMEM; - goto sea_exit; - } - - ea->ea_name_length = ea_name_len; - ea->ea_value_length = cpu_to_le16(ea_value_len); - memcpy(ea->ea_data, ea_name, ea_name_len + 1); - memcpy(ea->ea_data + ea_name_len + 1, ea_value, ea_value_len); - - size[0] = len; - data[0] = ea; - - rc = SMB2_set_info_init(tcon, server, - &rqst[1], COMPOUND_FID, - COMPOUND_FID, current->tgid, - FILE_FULL_EA_INFORMATION, - SMB2_O_INFO_FILE, 0, data, size); - if (rc) - goto sea_exit; - smb2_set_next_command(tcon, &rqst[1]); - smb2_set_related(&rqst[1]); - - - /* Close */ - memset(&close_iov, 0, sizeof(close_iov)); - rqst[2].rq_iov = close_iov; - rqst[2].rq_nvec = 1; - rc = SMB2_close_init(tcon, server, - &rqst[2], COMPOUND_FID, COMPOUND_FID, false); - if (rc) - goto sea_exit; - smb2_set_related(&rqst[2]); - - rc = compound_send_recv(xid, ses, server, - flags, 3, rqst, - resp_buftype, rsp_iov); - /* no need to bump num_remote_opens because handle immediately closed */ - - sea_exit: - kfree(ea); - kfree(utf16_path); - SMB2_open_free(&rqst[0]); - SMB2_set_info_free(&rqst[1]); - SMB2_close_free(&rqst[2]); - free_rsp_buf(resp_buftype[0], rsp_iov[0].iov_base); - free_rsp_buf(resp_buftype[1], rsp_iov[1].iov_base); - free_rsp_buf(resp_buftype[2], rsp_iov[2].iov_base); - return rc; -} -#endif - -static bool -smb2_can_echo(struct TCP_Server_Info *server) -{ - return server->echoes; -} - -static void -smb2_clear_stats(struct cifs_tcon *tcon) -{ - int i; - - for (i = 0; i < NUMBER_OF_SMB2_COMMANDS; i++) { - atomic_set(&tcon->stats.smb2_stats.smb2_com_sent[i], 0); - atomic_set(&tcon->stats.smb2_stats.smb2_com_failed[i], 0); - } -} - -static void -smb2_dump_share_caps(struct seq_file *m, struct cifs_tcon *tcon) -{ - seq_puts(m, "\n\tShare Capabilities:"); - if (tcon->capabilities & SMB2_SHARE_CAP_DFS) - seq_puts(m, " DFS,"); - if (tcon->capabilities & SMB2_SHARE_CAP_CONTINUOUS_AVAILABILITY) - seq_puts(m, " CONTINUOUS AVAILABILITY,"); - if (tcon->capabilities & SMB2_SHARE_CAP_SCALEOUT) - seq_puts(m, " SCALEOUT,"); - if (tcon->capabilities & SMB2_SHARE_CAP_CLUSTER) - seq_puts(m, " CLUSTER,"); - if (tcon->capabilities & SMB2_SHARE_CAP_ASYMMETRIC) - seq_puts(m, " ASYMMETRIC,"); - if (tcon->capabilities == 0) - seq_puts(m, " None"); - if (tcon->ss_flags & SSINFO_FLAGS_ALIGNED_DEVICE) - seq_puts(m, " Aligned,"); - if (tcon->ss_flags & SSINFO_FLAGS_PARTITION_ALIGNED_ON_DEVICE) - seq_puts(m, " Partition Aligned,"); - if (tcon->ss_flags & SSINFO_FLAGS_NO_SEEK_PENALTY) - seq_puts(m, " SSD,"); - if (tcon->ss_flags & SSINFO_FLAGS_TRIM_ENABLED) - seq_puts(m, " TRIM-support,"); - - seq_printf(m, "\tShare Flags: 0x%x", tcon->share_flags); - seq_printf(m, "\n\ttid: 0x%x", tcon->tid); - if (tcon->perf_sector_size) - seq_printf(m, "\tOptimal sector size: 0x%x", - tcon->perf_sector_size); - seq_printf(m, "\tMaximal Access: 0x%x", tcon->maximal_access); -} - -static void -smb2_print_stats(struct seq_file *m, struct cifs_tcon *tcon) -{ - atomic_t *sent = tcon->stats.smb2_stats.smb2_com_sent; - atomic_t *failed = tcon->stats.smb2_stats.smb2_com_failed; - - /* - * Can't display SMB2_NEGOTIATE, SESSION_SETUP, LOGOFF, CANCEL and ECHO - * totals (requests sent) since those SMBs are per-session not per tcon - */ - seq_printf(m, "\nBytes read: %llu Bytes written: %llu", - (long long)(tcon->bytes_read), - (long long)(tcon->bytes_written)); - seq_printf(m, "\nOpen files: %d total (local), %d open on server", - atomic_read(&tcon->num_local_opens), - atomic_read(&tcon->num_remote_opens)); - seq_printf(m, "\nTreeConnects: %d total %d failed", - atomic_read(&sent[SMB2_TREE_CONNECT_HE]), - atomic_read(&failed[SMB2_TREE_CONNECT_HE])); - seq_printf(m, "\nTreeDisconnects: %d total %d failed", - atomic_read(&sent[SMB2_TREE_DISCONNECT_HE]), - atomic_read(&failed[SMB2_TREE_DISCONNECT_HE])); - seq_printf(m, "\nCreates: %d total %d failed", - atomic_read(&sent[SMB2_CREATE_HE]), - atomic_read(&failed[SMB2_CREATE_HE])); - seq_printf(m, "\nCloses: %d total %d failed", - atomic_read(&sent[SMB2_CLOSE_HE]), - atomic_read(&failed[SMB2_CLOSE_HE])); - seq_printf(m, "\nFlushes: %d total %d failed", - atomic_read(&sent[SMB2_FLUSH_HE]), - atomic_read(&failed[SMB2_FLUSH_HE])); - seq_printf(m, "\nReads: %d total %d failed", - atomic_read(&sent[SMB2_READ_HE]), - atomic_read(&failed[SMB2_READ_HE])); - seq_printf(m, "\nWrites: %d total %d failed", - atomic_read(&sent[SMB2_WRITE_HE]), - atomic_read(&failed[SMB2_WRITE_HE])); - seq_printf(m, "\nLocks: %d total %d failed", - atomic_read(&sent[SMB2_LOCK_HE]), - atomic_read(&failed[SMB2_LOCK_HE])); - seq_printf(m, "\nIOCTLs: %d total %d failed", - atomic_read(&sent[SMB2_IOCTL_HE]), - atomic_read(&failed[SMB2_IOCTL_HE])); - seq_printf(m, "\nQueryDirectories: %d total %d failed", - atomic_read(&sent[SMB2_QUERY_DIRECTORY_HE]), - atomic_read(&failed[SMB2_QUERY_DIRECTORY_HE])); - seq_printf(m, "\nChangeNotifies: %d total %d failed", - atomic_read(&sent[SMB2_CHANGE_NOTIFY_HE]), - atomic_read(&failed[SMB2_CHANGE_NOTIFY_HE])); - seq_printf(m, "\nQueryInfos: %d total %d failed", - atomic_read(&sent[SMB2_QUERY_INFO_HE]), - atomic_read(&failed[SMB2_QUERY_INFO_HE])); - seq_printf(m, "\nSetInfos: %d total %d failed", - atomic_read(&sent[SMB2_SET_INFO_HE]), - atomic_read(&failed[SMB2_SET_INFO_HE])); - seq_printf(m, "\nOplockBreaks: %d sent %d failed", - atomic_read(&sent[SMB2_OPLOCK_BREAK_HE]), - atomic_read(&failed[SMB2_OPLOCK_BREAK_HE])); -} - -static void -smb2_set_fid(struct cifsFileInfo *cfile, struct cifs_fid *fid, __u32 oplock) -{ - struct cifsInodeInfo *cinode = CIFS_I(d_inode(cfile->dentry)); - struct TCP_Server_Info *server = tlink_tcon(cfile->tlink)->ses->server; - - cfile->fid.persistent_fid = fid->persistent_fid; - cfile->fid.volatile_fid = fid->volatile_fid; - cfile->fid.access = fid->access; -#ifdef CONFIG_CIFS_DEBUG2 - cfile->fid.mid = fid->mid; -#endif /* CIFS_DEBUG2 */ - server->ops->set_oplock_level(cinode, oplock, fid->epoch, - &fid->purge_cache); - cinode->can_cache_brlcks = CIFS_CACHE_WRITE(cinode); - memcpy(cfile->fid.create_guid, fid->create_guid, 16); -} - -static void -smb2_close_file(const unsigned int xid, struct cifs_tcon *tcon, - struct cifs_fid *fid) -{ - SMB2_close(xid, tcon, fid->persistent_fid, fid->volatile_fid); -} - -static void -smb2_close_getattr(const unsigned int xid, struct cifs_tcon *tcon, - struct cifsFileInfo *cfile) -{ - struct smb2_file_network_open_info file_inf; - struct inode *inode; - int rc; - - rc = __SMB2_close(xid, tcon, cfile->fid.persistent_fid, - cfile->fid.volatile_fid, &file_inf); - if (rc) - return; - - inode = d_inode(cfile->dentry); - - spin_lock(&inode->i_lock); - CIFS_I(inode)->time = jiffies; - - /* Creation time should not need to be updated on close */ - if (file_inf.LastWriteTime) - inode->i_mtime = cifs_NTtimeToUnix(file_inf.LastWriteTime); - if (file_inf.ChangeTime) - inode->i_ctime = cifs_NTtimeToUnix(file_inf.ChangeTime); - if (file_inf.LastAccessTime) - inode->i_atime = cifs_NTtimeToUnix(file_inf.LastAccessTime); - - /* - * i_blocks is not related to (i_size / i_blksize), - * but instead 512 byte (2**9) size is required for - * calculating num blocks. - */ - if (le64_to_cpu(file_inf.AllocationSize) > 4096) - inode->i_blocks = - (512 - 1 + le64_to_cpu(file_inf.AllocationSize)) >> 9; - - /* End of file and Attributes should not have to be updated on close */ - spin_unlock(&inode->i_lock); -} - -static int -SMB2_request_res_key(const unsigned int xid, struct cifs_tcon *tcon, - u64 persistent_fid, u64 volatile_fid, - struct copychunk_ioctl *pcchunk) -{ - int rc; - unsigned int ret_data_len; - struct resume_key_req *res_key; - - rc = SMB2_ioctl(xid, tcon, persistent_fid, volatile_fid, - FSCTL_SRV_REQUEST_RESUME_KEY, NULL, 0 /* no input */, - CIFSMaxBufSize, (char **)&res_key, &ret_data_len); - - if (rc == -EOPNOTSUPP) { - pr_warn_once("Server share %s does not support copy range\n", tcon->tree_name); - goto req_res_key_exit; - } else if (rc) { - cifs_tcon_dbg(VFS, "refcpy ioctl error %d getting resume key\n", rc); - goto req_res_key_exit; - } - if (ret_data_len < sizeof(struct resume_key_req)) { - cifs_tcon_dbg(VFS, "Invalid refcopy resume key length\n"); - rc = -EINVAL; - goto req_res_key_exit; - } - memcpy(pcchunk->SourceKey, res_key->ResumeKey, COPY_CHUNK_RES_KEY_SIZE); - -req_res_key_exit: - kfree(res_key); - return rc; -} - -struct iqi_vars { - struct smb_rqst rqst[3]; - struct kvec rsp_iov[3]; - struct kvec open_iov[SMB2_CREATE_IOV_SIZE]; - struct kvec qi_iov[1]; - struct kvec io_iov[SMB2_IOCTL_IOV_SIZE]; - struct kvec si_iov[SMB2_SET_INFO_IOV_SIZE]; - struct kvec close_iov[1]; -}; - -static int -smb2_ioctl_query_info(const unsigned int xid, - struct cifs_tcon *tcon, - struct cifs_sb_info *cifs_sb, - __le16 *path, int is_dir, - unsigned long p) -{ - struct iqi_vars *vars; - struct smb_rqst *rqst; - struct kvec *rsp_iov; - struct cifs_ses *ses = tcon->ses; - struct TCP_Server_Info *server = cifs_pick_channel(ses); - char __user *arg = (char __user *)p; - struct smb_query_info qi; - struct smb_query_info __user *pqi; - int rc = 0; - int flags = CIFS_CP_CREATE_CLOSE_OP; - struct smb2_query_info_rsp *qi_rsp = NULL; - struct smb2_ioctl_rsp *io_rsp = NULL; - void *buffer = NULL; - int resp_buftype[3]; - struct cifs_open_parms oparms; - u8 oplock = SMB2_OPLOCK_LEVEL_NONE; - struct cifs_fid fid; - unsigned int size[2]; - void *data[2]; - int create_options = is_dir ? CREATE_NOT_FILE : CREATE_NOT_DIR; - void (*free_req1_func)(struct smb_rqst *r); - - vars = kzalloc(sizeof(*vars), GFP_ATOMIC); - if (vars == NULL) - return -ENOMEM; - rqst = &vars->rqst[0]; - rsp_iov = &vars->rsp_iov[0]; - - resp_buftype[0] = resp_buftype[1] = resp_buftype[2] = CIFS_NO_BUFFER; - - if (copy_from_user(&qi, arg, sizeof(struct smb_query_info))) { - rc = -EFAULT; - goto free_vars; - } - if (qi.output_buffer_length > 1024) { - rc = -EINVAL; - goto free_vars; - } - - if (!ses || !server) { - rc = -EIO; - goto free_vars; - } - - if (smb3_encryption_required(tcon)) - flags |= CIFS_TRANSFORM_REQ; - - if (qi.output_buffer_length) { - buffer = memdup_user(arg + sizeof(struct smb_query_info), qi.output_buffer_length); - if (IS_ERR(buffer)) { - rc = PTR_ERR(buffer); - goto free_vars; - } - } - - /* Open */ - rqst[0].rq_iov = &vars->open_iov[0]; - rqst[0].rq_nvec = SMB2_CREATE_IOV_SIZE; - - oparms = (struct cifs_open_parms) { - .tcon = tcon, - .disposition = FILE_OPEN, - .create_options = cifs_create_options(cifs_sb, create_options), - .fid = &fid, - }; - - if (qi.flags & PASSTHRU_FSCTL) { - switch (qi.info_type & FSCTL_DEVICE_ACCESS_MASK) { - case FSCTL_DEVICE_ACCESS_FILE_READ_WRITE_ACCESS: - oparms.desired_access = FILE_READ_DATA | FILE_WRITE_DATA | FILE_READ_ATTRIBUTES | SYNCHRONIZE; - break; - case FSCTL_DEVICE_ACCESS_FILE_ANY_ACCESS: - oparms.desired_access = GENERIC_ALL; - break; - case FSCTL_DEVICE_ACCESS_FILE_READ_ACCESS: - oparms.desired_access = GENERIC_READ; - break; - case FSCTL_DEVICE_ACCESS_FILE_WRITE_ACCESS: - oparms.desired_access = GENERIC_WRITE; - break; - } - } else if (qi.flags & PASSTHRU_SET_INFO) { - oparms.desired_access = GENERIC_WRITE; - } else { - oparms.desired_access = FILE_READ_ATTRIBUTES | READ_CONTROL; - } - - rc = SMB2_open_init(tcon, server, - &rqst[0], &oplock, &oparms, path); - if (rc) - goto free_output_buffer; - smb2_set_next_command(tcon, &rqst[0]); - - /* Query */ - if (qi.flags & PASSTHRU_FSCTL) { - /* Can eventually relax perm check since server enforces too */ - if (!capable(CAP_SYS_ADMIN)) { - rc = -EPERM; - goto free_open_req; - } - rqst[1].rq_iov = &vars->io_iov[0]; - rqst[1].rq_nvec = SMB2_IOCTL_IOV_SIZE; - - rc = SMB2_ioctl_init(tcon, server, &rqst[1], COMPOUND_FID, COMPOUND_FID, - qi.info_type, buffer, qi.output_buffer_length, - CIFSMaxBufSize - MAX_SMB2_CREATE_RESPONSE_SIZE - - MAX_SMB2_CLOSE_RESPONSE_SIZE); - free_req1_func = SMB2_ioctl_free; - } else if (qi.flags == PASSTHRU_SET_INFO) { - /* Can eventually relax perm check since server enforces too */ - if (!capable(CAP_SYS_ADMIN)) { - rc = -EPERM; - goto free_open_req; - } - if (qi.output_buffer_length < 8) { - rc = -EINVAL; - goto free_open_req; - } - rqst[1].rq_iov = &vars->si_iov[0]; - rqst[1].rq_nvec = 1; - - /* MS-FSCC 2.4.13 FileEndOfFileInformation */ - size[0] = 8; - data[0] = buffer; - - rc = SMB2_set_info_init(tcon, server, &rqst[1], COMPOUND_FID, COMPOUND_FID, - current->tgid, FILE_END_OF_FILE_INFORMATION, - SMB2_O_INFO_FILE, 0, data, size); - free_req1_func = SMB2_set_info_free; - } else if (qi.flags == PASSTHRU_QUERY_INFO) { - rqst[1].rq_iov = &vars->qi_iov[0]; - rqst[1].rq_nvec = 1; - - rc = SMB2_query_info_init(tcon, server, - &rqst[1], COMPOUND_FID, - COMPOUND_FID, qi.file_info_class, - qi.info_type, qi.additional_information, - qi.input_buffer_length, - qi.output_buffer_length, buffer); - free_req1_func = SMB2_query_info_free; - } else { /* unknown flags */ - cifs_tcon_dbg(VFS, "Invalid passthru query flags: 0x%x\n", - qi.flags); - rc = -EINVAL; - } - - if (rc) - goto free_open_req; - smb2_set_next_command(tcon, &rqst[1]); - smb2_set_related(&rqst[1]); - - /* Close */ - rqst[2].rq_iov = &vars->close_iov[0]; - rqst[2].rq_nvec = 1; - - rc = SMB2_close_init(tcon, server, - &rqst[2], COMPOUND_FID, COMPOUND_FID, false); - if (rc) - goto free_req_1; - smb2_set_related(&rqst[2]); - - rc = compound_send_recv(xid, ses, server, - flags, 3, rqst, - resp_buftype, rsp_iov); - if (rc) - goto out; - - /* No need to bump num_remote_opens since handle immediately closed */ - if (qi.flags & PASSTHRU_FSCTL) { - pqi = (struct smb_query_info __user *)arg; - io_rsp = (struct smb2_ioctl_rsp *)rsp_iov[1].iov_base; - if (le32_to_cpu(io_rsp->OutputCount) < qi.input_buffer_length) - qi.input_buffer_length = le32_to_cpu(io_rsp->OutputCount); - if (qi.input_buffer_length > 0 && - le32_to_cpu(io_rsp->OutputOffset) + qi.input_buffer_length - > rsp_iov[1].iov_len) { - rc = -EFAULT; - goto out; - } - - if (copy_to_user(&pqi->input_buffer_length, - &qi.input_buffer_length, - sizeof(qi.input_buffer_length))) { - rc = -EFAULT; - goto out; - } - - if (copy_to_user((void __user *)pqi + sizeof(struct smb_query_info), - (const void *)io_rsp + le32_to_cpu(io_rsp->OutputOffset), - qi.input_buffer_length)) - rc = -EFAULT; - } else { - pqi = (struct smb_query_info __user *)arg; - qi_rsp = (struct smb2_query_info_rsp *)rsp_iov[1].iov_base; - if (le32_to_cpu(qi_rsp->OutputBufferLength) < qi.input_buffer_length) - qi.input_buffer_length = le32_to_cpu(qi_rsp->OutputBufferLength); - if (copy_to_user(&pqi->input_buffer_length, - &qi.input_buffer_length, - sizeof(qi.input_buffer_length))) { - rc = -EFAULT; - goto out; - } - - if (copy_to_user(pqi + 1, qi_rsp->Buffer, - qi.input_buffer_length)) - rc = -EFAULT; - } - -out: - free_rsp_buf(resp_buftype[0], rsp_iov[0].iov_base); - free_rsp_buf(resp_buftype[1], rsp_iov[1].iov_base); - free_rsp_buf(resp_buftype[2], rsp_iov[2].iov_base); - SMB2_close_free(&rqst[2]); -free_req_1: - free_req1_func(&rqst[1]); -free_open_req: - SMB2_open_free(&rqst[0]); -free_output_buffer: - kfree(buffer); -free_vars: - kfree(vars); - return rc; -} - -static ssize_t -smb2_copychunk_range(const unsigned int xid, - struct cifsFileInfo *srcfile, - struct cifsFileInfo *trgtfile, u64 src_off, - u64 len, u64 dest_off) -{ - int rc; - unsigned int ret_data_len; - struct copychunk_ioctl *pcchunk; - struct copychunk_ioctl_rsp *retbuf = NULL; - struct cifs_tcon *tcon; - int chunks_copied = 0; - bool chunk_sizes_updated = false; - ssize_t bytes_written, total_bytes_written = 0; - - pcchunk = kmalloc(sizeof(struct copychunk_ioctl), GFP_KERNEL); - if (pcchunk == NULL) - return -ENOMEM; - - cifs_dbg(FYI, "%s: about to call request res key\n", __func__); - /* Request a key from the server to identify the source of the copy */ - rc = SMB2_request_res_key(xid, tlink_tcon(srcfile->tlink), - srcfile->fid.persistent_fid, - srcfile->fid.volatile_fid, pcchunk); - - /* Note: request_res_key sets res_key null only if rc !=0 */ - if (rc) - goto cchunk_out; - - /* For now array only one chunk long, will make more flexible later */ - pcchunk->ChunkCount = cpu_to_le32(1); - pcchunk->Reserved = 0; - pcchunk->Reserved2 = 0; - - tcon = tlink_tcon(trgtfile->tlink); - - while (len > 0) { - pcchunk->SourceOffset = cpu_to_le64(src_off); - pcchunk->TargetOffset = cpu_to_le64(dest_off); - pcchunk->Length = - cpu_to_le32(min_t(u64, len, tcon->max_bytes_chunk)); - - /* Request server copy to target from src identified by key */ - kfree(retbuf); - retbuf = NULL; - rc = SMB2_ioctl(xid, tcon, trgtfile->fid.persistent_fid, - trgtfile->fid.volatile_fid, FSCTL_SRV_COPYCHUNK_WRITE, - (char *)pcchunk, sizeof(struct copychunk_ioctl), - CIFSMaxBufSize, (char **)&retbuf, &ret_data_len); - if (rc == 0) { - if (ret_data_len != - sizeof(struct copychunk_ioctl_rsp)) { - cifs_tcon_dbg(VFS, "Invalid cchunk response size\n"); - rc = -EIO; - goto cchunk_out; - } - if (retbuf->TotalBytesWritten == 0) { - cifs_dbg(FYI, "no bytes copied\n"); - rc = -EIO; - goto cchunk_out; - } - /* - * Check if server claimed to write more than we asked - */ - if (le32_to_cpu(retbuf->TotalBytesWritten) > - le32_to_cpu(pcchunk->Length)) { - cifs_tcon_dbg(VFS, "Invalid copy chunk response\n"); - rc = -EIO; - goto cchunk_out; - } - if (le32_to_cpu(retbuf->ChunksWritten) != 1) { - cifs_tcon_dbg(VFS, "Invalid num chunks written\n"); - rc = -EIO; - goto cchunk_out; - } - chunks_copied++; - - bytes_written = le32_to_cpu(retbuf->TotalBytesWritten); - src_off += bytes_written; - dest_off += bytes_written; - len -= bytes_written; - total_bytes_written += bytes_written; - - cifs_dbg(FYI, "Chunks %d PartialChunk %d Total %zu\n", - le32_to_cpu(retbuf->ChunksWritten), - le32_to_cpu(retbuf->ChunkBytesWritten), - bytes_written); - } else if (rc == -EINVAL) { - if (ret_data_len != sizeof(struct copychunk_ioctl_rsp)) - goto cchunk_out; - - cifs_dbg(FYI, "MaxChunks %d BytesChunk %d MaxCopy %d\n", - le32_to_cpu(retbuf->ChunksWritten), - le32_to_cpu(retbuf->ChunkBytesWritten), - le32_to_cpu(retbuf->TotalBytesWritten)); - - /* - * Check if this is the first request using these sizes, - * (ie check if copy succeed once with original sizes - * and check if the server gave us different sizes after - * we already updated max sizes on previous request). - * if not then why is the server returning an error now - */ - if ((chunks_copied != 0) || chunk_sizes_updated) - goto cchunk_out; - - /* Check that server is not asking us to grow size */ - if (le32_to_cpu(retbuf->ChunkBytesWritten) < - tcon->max_bytes_chunk) - tcon->max_bytes_chunk = - le32_to_cpu(retbuf->ChunkBytesWritten); - else - goto cchunk_out; /* server gave us bogus size */ - - /* No need to change MaxChunks since already set to 1 */ - chunk_sizes_updated = true; - } else - goto cchunk_out; - } - -cchunk_out: - kfree(pcchunk); - kfree(retbuf); - if (rc) - return rc; - else - return total_bytes_written; -} - -static int -smb2_flush_file(const unsigned int xid, struct cifs_tcon *tcon, - struct cifs_fid *fid) -{ - return SMB2_flush(xid, tcon, fid->persistent_fid, fid->volatile_fid); -} - -static unsigned int -smb2_read_data_offset(char *buf) -{ - struct smb2_read_rsp *rsp = (struct smb2_read_rsp *)buf; - - return rsp->DataOffset; -} - -static unsigned int -smb2_read_data_length(char *buf, bool in_remaining) -{ - struct smb2_read_rsp *rsp = (struct smb2_read_rsp *)buf; - - if (in_remaining) - return le32_to_cpu(rsp->DataRemaining); - - return le32_to_cpu(rsp->DataLength); -} - - -static int -smb2_sync_read(const unsigned int xid, struct cifs_fid *pfid, - struct cifs_io_parms *parms, unsigned int *bytes_read, - char **buf, int *buf_type) -{ - parms->persistent_fid = pfid->persistent_fid; - parms->volatile_fid = pfid->volatile_fid; - return SMB2_read(xid, parms, bytes_read, buf, buf_type); -} - -static int -smb2_sync_write(const unsigned int xid, struct cifs_fid *pfid, - struct cifs_io_parms *parms, unsigned int *written, - struct kvec *iov, unsigned long nr_segs) -{ - - parms->persistent_fid = pfid->persistent_fid; - parms->volatile_fid = pfid->volatile_fid; - return SMB2_write(xid, parms, written, iov, nr_segs); -} - -/* Set or clear the SPARSE_FILE attribute based on value passed in setsparse */ -static bool smb2_set_sparse(const unsigned int xid, struct cifs_tcon *tcon, - struct cifsFileInfo *cfile, struct inode *inode, __u8 setsparse) -{ - struct cifsInodeInfo *cifsi; - int rc; - - cifsi = CIFS_I(inode); - - /* if file already sparse don't bother setting sparse again */ - if ((cifsi->cifsAttrs & FILE_ATTRIBUTE_SPARSE_FILE) && setsparse) - return true; /* already sparse */ - - if (!(cifsi->cifsAttrs & FILE_ATTRIBUTE_SPARSE_FILE) && !setsparse) - return true; /* already not sparse */ - - /* - * Can't check for sparse support on share the usual way via the - * FS attribute info (FILE_SUPPORTS_SPARSE_FILES) on the share - * since Samba server doesn't set the flag on the share, yet - * supports the set sparse FSCTL and returns sparse correctly - * in the file attributes. If we fail setting sparse though we - * mark that server does not support sparse files for this share - * to avoid repeatedly sending the unsupported fsctl to server - * if the file is repeatedly extended. - */ - if (tcon->broken_sparse_sup) - return false; - - rc = SMB2_ioctl(xid, tcon, cfile->fid.persistent_fid, - cfile->fid.volatile_fid, FSCTL_SET_SPARSE, - &setsparse, 1, CIFSMaxBufSize, NULL, NULL); - if (rc) { - tcon->broken_sparse_sup = true; - cifs_dbg(FYI, "set sparse rc = %d\n", rc); - return false; - } - - if (setsparse) - cifsi->cifsAttrs |= FILE_ATTRIBUTE_SPARSE_FILE; - else - cifsi->cifsAttrs &= (~FILE_ATTRIBUTE_SPARSE_FILE); - - return true; -} - -static int -smb2_set_file_size(const unsigned int xid, struct cifs_tcon *tcon, - struct cifsFileInfo *cfile, __u64 size, bool set_alloc) -{ - __le64 eof = cpu_to_le64(size); - struct inode *inode; - - /* - * If extending file more than one page make sparse. Many Linux fs - * make files sparse by default when extending via ftruncate - */ - inode = d_inode(cfile->dentry); - - if (!set_alloc && (size > inode->i_size + 8192)) { - __u8 set_sparse = 1; - - /* whether set sparse succeeds or not, extend the file */ - smb2_set_sparse(xid, tcon, cfile, inode, set_sparse); - } - - return SMB2_set_eof(xid, tcon, cfile->fid.persistent_fid, - cfile->fid.volatile_fid, cfile->pid, &eof); -} - -static int -smb2_duplicate_extents(const unsigned int xid, - struct cifsFileInfo *srcfile, - struct cifsFileInfo *trgtfile, u64 src_off, - u64 len, u64 dest_off) -{ - int rc; - unsigned int ret_data_len; - struct inode *inode; - struct duplicate_extents_to_file dup_ext_buf; - struct cifs_tcon *tcon = tlink_tcon(trgtfile->tlink); - - /* server fileays advertise duplicate extent support with this flag */ - if ((le32_to_cpu(tcon->fsAttrInfo.Attributes) & - FILE_SUPPORTS_BLOCK_REFCOUNTING) == 0) - return -EOPNOTSUPP; - - dup_ext_buf.VolatileFileHandle = srcfile->fid.volatile_fid; - dup_ext_buf.PersistentFileHandle = srcfile->fid.persistent_fid; - dup_ext_buf.SourceFileOffset = cpu_to_le64(src_off); - dup_ext_buf.TargetFileOffset = cpu_to_le64(dest_off); - dup_ext_buf.ByteCount = cpu_to_le64(len); - cifs_dbg(FYI, "Duplicate extents: src off %lld dst off %lld len %lld\n", - src_off, dest_off, len); - - inode = d_inode(trgtfile->dentry); - if (inode->i_size < dest_off + len) { - rc = smb2_set_file_size(xid, tcon, trgtfile, dest_off + len, false); - if (rc) - goto duplicate_extents_out; - - /* - * Although also could set plausible allocation size (i_blocks) - * here in addition to setting the file size, in reflink - * it is likely that the target file is sparse. Its allocation - * size will be queried on next revalidate, but it is important - * to make sure that file's cached size is updated immediately - */ - cifs_setsize(inode, dest_off + len); - } - rc = SMB2_ioctl(xid, tcon, trgtfile->fid.persistent_fid, - trgtfile->fid.volatile_fid, - FSCTL_DUPLICATE_EXTENTS_TO_FILE, - (char *)&dup_ext_buf, - sizeof(struct duplicate_extents_to_file), - CIFSMaxBufSize, NULL, - &ret_data_len); - - if (ret_data_len > 0) - cifs_dbg(FYI, "Non-zero response length in duplicate extents\n"); - -duplicate_extents_out: - return rc; -} - -static int -smb2_set_compression(const unsigned int xid, struct cifs_tcon *tcon, - struct cifsFileInfo *cfile) -{ - return SMB2_set_compression(xid, tcon, cfile->fid.persistent_fid, - cfile->fid.volatile_fid); -} - -static int -smb3_set_integrity(const unsigned int xid, struct cifs_tcon *tcon, - struct cifsFileInfo *cfile) -{ - struct fsctl_set_integrity_information_req integr_info; - unsigned int ret_data_len; - - integr_info.ChecksumAlgorithm = cpu_to_le16(CHECKSUM_TYPE_UNCHANGED); - integr_info.Flags = 0; - integr_info.Reserved = 0; - - return SMB2_ioctl(xid, tcon, cfile->fid.persistent_fid, - cfile->fid.volatile_fid, - FSCTL_SET_INTEGRITY_INFORMATION, - (char *)&integr_info, - sizeof(struct fsctl_set_integrity_information_req), - CIFSMaxBufSize, NULL, - &ret_data_len); - -} - -/* GMT Token is @GMT-YYYY.MM.DD-HH.MM.SS Unicode which is 48 bytes + null */ -#define GMT_TOKEN_SIZE 50 - -#define MIN_SNAPSHOT_ARRAY_SIZE 16 /* See MS-SMB2 section 3.3.5.15.1 */ - -/* - * Input buffer contains (empty) struct smb_snapshot array with size filled in - * For output see struct SRV_SNAPSHOT_ARRAY in MS-SMB2 section 2.2.32.2 - */ -static int -smb3_enum_snapshots(const unsigned int xid, struct cifs_tcon *tcon, - struct cifsFileInfo *cfile, void __user *ioc_buf) -{ - char *retbuf = NULL; - unsigned int ret_data_len = 0; - int rc; - u32 max_response_size; - struct smb_snapshot_array snapshot_in; - - /* - * On the first query to enumerate the list of snapshots available - * for this volume the buffer begins with 0 (number of snapshots - * which can be returned is zero since at that point we do not know - * how big the buffer needs to be). On the second query, - * it (ret_data_len) is set to number of snapshots so we can - * know to set the maximum response size larger (see below). - */ - if (get_user(ret_data_len, (unsigned int __user *)ioc_buf)) - return -EFAULT; - - /* - * Note that for snapshot queries that servers like Azure expect that - * the first query be minimal size (and just used to get the number/size - * of previous versions) so response size must be specified as EXACTLY - * sizeof(struct snapshot_array) which is 16 when rounded up to multiple - * of eight bytes. - */ - if (ret_data_len == 0) - max_response_size = MIN_SNAPSHOT_ARRAY_SIZE; - else - max_response_size = CIFSMaxBufSize; - - rc = SMB2_ioctl(xid, tcon, cfile->fid.persistent_fid, - cfile->fid.volatile_fid, - FSCTL_SRV_ENUMERATE_SNAPSHOTS, - NULL, 0 /* no input data */, max_response_size, - (char **)&retbuf, - &ret_data_len); - cifs_dbg(FYI, "enum snaphots ioctl returned %d and ret buflen is %d\n", - rc, ret_data_len); - if (rc) - return rc; - - if (ret_data_len && (ioc_buf != NULL) && (retbuf != NULL)) { - /* Fixup buffer */ - if (copy_from_user(&snapshot_in, ioc_buf, - sizeof(struct smb_snapshot_array))) { - rc = -EFAULT; - kfree(retbuf); - return rc; - } - - /* - * Check for min size, ie not large enough to fit even one GMT - * token (snapshot). On the first ioctl some users may pass in - * smaller size (or zero) to simply get the size of the array - * so the user space caller can allocate sufficient memory - * and retry the ioctl again with larger array size sufficient - * to hold all of the snapshot GMT tokens on the second try. - */ - if (snapshot_in.snapshot_array_size < GMT_TOKEN_SIZE) - ret_data_len = sizeof(struct smb_snapshot_array); - - /* - * We return struct SRV_SNAPSHOT_ARRAY, followed by - * the snapshot array (of 50 byte GMT tokens) each - * representing an available previous version of the data - */ - if (ret_data_len > (snapshot_in.snapshot_array_size + - sizeof(struct smb_snapshot_array))) - ret_data_len = snapshot_in.snapshot_array_size + - sizeof(struct smb_snapshot_array); - - if (copy_to_user(ioc_buf, retbuf, ret_data_len)) - rc = -EFAULT; - } - - kfree(retbuf); - return rc; -} - - - -static int -smb3_notify(const unsigned int xid, struct file *pfile, - void __user *ioc_buf, bool return_changes) -{ - struct smb3_notify_info notify; - struct smb3_notify_info __user *pnotify_buf; - struct dentry *dentry = pfile->f_path.dentry; - struct inode *inode = file_inode(pfile); - struct cifs_sb_info *cifs_sb = CIFS_SB(inode->i_sb); - struct cifs_open_parms oparms; - struct cifs_fid fid; - struct cifs_tcon *tcon; - const unsigned char *path; - char *returned_ioctl_info = NULL; - void *page = alloc_dentry_path(); - __le16 *utf16_path = NULL; - u8 oplock = SMB2_OPLOCK_LEVEL_NONE; - int rc = 0; - __u32 ret_len = 0; - - path = build_path_from_dentry(dentry, page); - if (IS_ERR(path)) { - rc = PTR_ERR(path); - goto notify_exit; - } - - utf16_path = cifs_convert_path_to_utf16(path, cifs_sb); - if (utf16_path == NULL) { - rc = -ENOMEM; - goto notify_exit; - } - - if (return_changes) { - if (copy_from_user(¬ify, ioc_buf, sizeof(struct smb3_notify_info))) { - rc = -EFAULT; - goto notify_exit; - } - } else { - if (copy_from_user(¬ify, ioc_buf, sizeof(struct smb3_notify))) { - rc = -EFAULT; - goto notify_exit; - } - notify.data_len = 0; - } - - tcon = cifs_sb_master_tcon(cifs_sb); - oparms = (struct cifs_open_parms) { - .tcon = tcon, - .path = path, - .desired_access = FILE_READ_ATTRIBUTES | FILE_READ_DATA, - .disposition = FILE_OPEN, - .create_options = cifs_create_options(cifs_sb, 0), - .fid = &fid, - }; - - rc = SMB2_open(xid, &oparms, utf16_path, &oplock, NULL, NULL, NULL, - NULL); - if (rc) - goto notify_exit; - - rc = SMB2_change_notify(xid, tcon, fid.persistent_fid, fid.volatile_fid, - notify.watch_tree, notify.completion_filter, - notify.data_len, &returned_ioctl_info, &ret_len); - - SMB2_close(xid, tcon, fid.persistent_fid, fid.volatile_fid); - - cifs_dbg(FYI, "change notify for path %s rc %d\n", path, rc); - if (return_changes && (ret_len > 0) && (notify.data_len > 0)) { - if (ret_len > notify.data_len) - ret_len = notify.data_len; - pnotify_buf = (struct smb3_notify_info __user *)ioc_buf; - if (copy_to_user(pnotify_buf->notify_data, returned_ioctl_info, ret_len)) - rc = -EFAULT; - else if (copy_to_user(&pnotify_buf->data_len, &ret_len, sizeof(ret_len))) - rc = -EFAULT; - } - kfree(returned_ioctl_info); -notify_exit: - free_dentry_path(page); - kfree(utf16_path); - return rc; -} - -static int -smb2_query_dir_first(const unsigned int xid, struct cifs_tcon *tcon, - const char *path, struct cifs_sb_info *cifs_sb, - struct cifs_fid *fid, __u16 search_flags, - struct cifs_search_info *srch_inf) -{ - __le16 *utf16_path; - struct smb_rqst rqst[2]; - struct kvec rsp_iov[2]; - int resp_buftype[2]; - struct kvec open_iov[SMB2_CREATE_IOV_SIZE]; - struct kvec qd_iov[SMB2_QUERY_DIRECTORY_IOV_SIZE]; - int rc, flags = 0; - u8 oplock = SMB2_OPLOCK_LEVEL_NONE; - struct cifs_open_parms oparms; - struct smb2_query_directory_rsp *qd_rsp = NULL; - struct smb2_create_rsp *op_rsp = NULL; - struct TCP_Server_Info *server = cifs_pick_channel(tcon->ses); - int retry_count = 0; - - utf16_path = cifs_convert_path_to_utf16(path, cifs_sb); - if (!utf16_path) - return -ENOMEM; - - if (smb3_encryption_required(tcon)) - flags |= CIFS_TRANSFORM_REQ; - - memset(rqst, 0, sizeof(rqst)); - resp_buftype[0] = resp_buftype[1] = CIFS_NO_BUFFER; - memset(rsp_iov, 0, sizeof(rsp_iov)); - - /* Open */ - memset(&open_iov, 0, sizeof(open_iov)); - rqst[0].rq_iov = open_iov; - rqst[0].rq_nvec = SMB2_CREATE_IOV_SIZE; - - oparms = (struct cifs_open_parms) { - .tcon = tcon, - .path = path, - .desired_access = FILE_READ_ATTRIBUTES | FILE_READ_DATA, - .disposition = FILE_OPEN, - .create_options = cifs_create_options(cifs_sb, 0), - .fid = fid, - }; - - rc = SMB2_open_init(tcon, server, - &rqst[0], &oplock, &oparms, utf16_path); - if (rc) - goto qdf_free; - smb2_set_next_command(tcon, &rqst[0]); - - /* Query directory */ - srch_inf->entries_in_buffer = 0; - srch_inf->index_of_last_entry = 2; - - memset(&qd_iov, 0, sizeof(qd_iov)); - rqst[1].rq_iov = qd_iov; - rqst[1].rq_nvec = SMB2_QUERY_DIRECTORY_IOV_SIZE; - - rc = SMB2_query_directory_init(xid, tcon, server, - &rqst[1], - COMPOUND_FID, COMPOUND_FID, - 0, srch_inf->info_level); - if (rc) - goto qdf_free; - - smb2_set_related(&rqst[1]); - -again: - rc = compound_send_recv(xid, tcon->ses, server, - flags, 2, rqst, - resp_buftype, rsp_iov); - - if (rc == -EAGAIN && retry_count++ < 10) - goto again; - - /* If the open failed there is nothing to do */ - op_rsp = (struct smb2_create_rsp *)rsp_iov[0].iov_base; - if (op_rsp == NULL || op_rsp->hdr.Status != STATUS_SUCCESS) { - cifs_dbg(FYI, "query_dir_first: open failed rc=%d\n", rc); - goto qdf_free; - } - fid->persistent_fid = op_rsp->PersistentFileId; - fid->volatile_fid = op_rsp->VolatileFileId; - - /* Anything else than ENODATA means a genuine error */ - if (rc && rc != -ENODATA) { - SMB2_close(xid, tcon, fid->persistent_fid, fid->volatile_fid); - cifs_dbg(FYI, "query_dir_first: query directory failed rc=%d\n", rc); - trace_smb3_query_dir_err(xid, fid->persistent_fid, - tcon->tid, tcon->ses->Suid, 0, 0, rc); - goto qdf_free; - } - - atomic_inc(&tcon->num_remote_opens); - - qd_rsp = (struct smb2_query_directory_rsp *)rsp_iov[1].iov_base; - if (qd_rsp->hdr.Status == STATUS_NO_MORE_FILES) { - trace_smb3_query_dir_done(xid, fid->persistent_fid, - tcon->tid, tcon->ses->Suid, 0, 0); - srch_inf->endOfSearch = true; - rc = 0; - goto qdf_free; - } - - rc = smb2_parse_query_directory(tcon, &rsp_iov[1], resp_buftype[1], - srch_inf); - if (rc) { - trace_smb3_query_dir_err(xid, fid->persistent_fid, tcon->tid, - tcon->ses->Suid, 0, 0, rc); - goto qdf_free; - } - resp_buftype[1] = CIFS_NO_BUFFER; - - trace_smb3_query_dir_done(xid, fid->persistent_fid, tcon->tid, - tcon->ses->Suid, 0, srch_inf->entries_in_buffer); - - qdf_free: - kfree(utf16_path); - SMB2_open_free(&rqst[0]); - SMB2_query_directory_free(&rqst[1]); - free_rsp_buf(resp_buftype[0], rsp_iov[0].iov_base); - free_rsp_buf(resp_buftype[1], rsp_iov[1].iov_base); - return rc; -} - -static int -smb2_query_dir_next(const unsigned int xid, struct cifs_tcon *tcon, - struct cifs_fid *fid, __u16 search_flags, - struct cifs_search_info *srch_inf) -{ - return SMB2_query_directory(xid, tcon, fid->persistent_fid, - fid->volatile_fid, 0, srch_inf); -} - -static int -smb2_close_dir(const unsigned int xid, struct cifs_tcon *tcon, - struct cifs_fid *fid) -{ - return SMB2_close(xid, tcon, fid->persistent_fid, fid->volatile_fid); -} - -/* - * If we negotiate SMB2 protocol and get STATUS_PENDING - update - * the number of credits and return true. Otherwise - return false. - */ -static bool -smb2_is_status_pending(char *buf, struct TCP_Server_Info *server) -{ - struct smb2_hdr *shdr = (struct smb2_hdr *)buf; - int scredits, in_flight; - - if (shdr->Status != STATUS_PENDING) - return false; - - if (shdr->CreditRequest) { - spin_lock(&server->req_lock); - server->credits += le16_to_cpu(shdr->CreditRequest); - scredits = server->credits; - in_flight = server->in_flight; - spin_unlock(&server->req_lock); - wake_up(&server->request_q); - - trace_smb3_pend_credits(server->CurrentMid, - server->conn_id, server->hostname, scredits, - le16_to_cpu(shdr->CreditRequest), in_flight); - cifs_dbg(FYI, "%s: status pending add %u credits total=%d\n", - __func__, le16_to_cpu(shdr->CreditRequest), scredits); - } - - return true; -} - -static bool -smb2_is_session_expired(char *buf) -{ - struct smb2_hdr *shdr = (struct smb2_hdr *)buf; - - if (shdr->Status != STATUS_NETWORK_SESSION_EXPIRED && - shdr->Status != STATUS_USER_SESSION_DELETED) - return false; - - trace_smb3_ses_expired(le32_to_cpu(shdr->Id.SyncId.TreeId), - le64_to_cpu(shdr->SessionId), - le16_to_cpu(shdr->Command), - le64_to_cpu(shdr->MessageId)); - cifs_dbg(FYI, "Session expired or deleted\n"); - - return true; -} - -static bool -smb2_is_status_io_timeout(char *buf) -{ - struct smb2_hdr *shdr = (struct smb2_hdr *)buf; - - if (shdr->Status == STATUS_IO_TIMEOUT) - return true; - else - return false; -} - -static void -smb2_is_network_name_deleted(char *buf, struct TCP_Server_Info *server) -{ - struct smb2_hdr *shdr = (struct smb2_hdr *)buf; - struct TCP_Server_Info *pserver; - struct cifs_ses *ses; - struct cifs_tcon *tcon; - - if (shdr->Status != STATUS_NETWORK_NAME_DELETED) - return; - - /* If server is a channel, select the primary channel */ - pserver = CIFS_SERVER_IS_CHAN(server) ? server->primary_server : server; - - spin_lock(&cifs_tcp_ses_lock); - list_for_each_entry(ses, &pserver->smb_ses_list, smb_ses_list) { - list_for_each_entry(tcon, &ses->tcon_list, tcon_list) { - if (tcon->tid == le32_to_cpu(shdr->Id.SyncId.TreeId)) { - spin_lock(&tcon->tc_lock); - tcon->need_reconnect = true; - spin_unlock(&tcon->tc_lock); - spin_unlock(&cifs_tcp_ses_lock); - pr_warn_once("Server share %s deleted.\n", - tcon->tree_name); - return; - } - } - } - spin_unlock(&cifs_tcp_ses_lock); -} - -static int -smb2_oplock_response(struct cifs_tcon *tcon, __u64 persistent_fid, - __u64 volatile_fid, __u16 net_fid, struct cifsInodeInfo *cinode) -{ - if (tcon->ses->server->capabilities & SMB2_GLOBAL_CAP_LEASING) - return SMB2_lease_break(0, tcon, cinode->lease_key, - smb2_get_lease_state(cinode)); - - return SMB2_oplock_break(0, tcon, persistent_fid, volatile_fid, - CIFS_CACHE_READ(cinode) ? 1 : 0); -} - -void -smb2_set_related(struct smb_rqst *rqst) -{ - struct smb2_hdr *shdr; - - shdr = (struct smb2_hdr *)(rqst->rq_iov[0].iov_base); - if (shdr == NULL) { - cifs_dbg(FYI, "shdr NULL in smb2_set_related\n"); - return; - } - shdr->Flags |= SMB2_FLAGS_RELATED_OPERATIONS; -} - -char smb2_padding[7] = {0, 0, 0, 0, 0, 0, 0}; - -void -smb2_set_next_command(struct cifs_tcon *tcon, struct smb_rqst *rqst) -{ - struct smb2_hdr *shdr; - struct cifs_ses *ses = tcon->ses; - struct TCP_Server_Info *server = ses->server; - unsigned long len = smb_rqst_len(server, rqst); - int i, num_padding; - - shdr = (struct smb2_hdr *)(rqst->rq_iov[0].iov_base); - if (shdr == NULL) { - cifs_dbg(FYI, "shdr NULL in smb2_set_next_command\n"); - return; - } - - /* SMB headers in a compound are 8 byte aligned. */ - - /* No padding needed */ - if (!(len & 7)) - goto finished; - - num_padding = 8 - (len & 7); - if (!smb3_encryption_required(tcon)) { - /* - * If we do not have encryption then we can just add an extra - * iov for the padding. - */ - rqst->rq_iov[rqst->rq_nvec].iov_base = smb2_padding; - rqst->rq_iov[rqst->rq_nvec].iov_len = num_padding; - rqst->rq_nvec++; - len += num_padding; - } else { - /* - * We can not add a small padding iov for the encryption case - * because the encryption framework can not handle the padding - * iovs. - * We have to flatten this into a single buffer and add - * the padding to it. - */ - for (i = 1; i < rqst->rq_nvec; i++) { - memcpy(rqst->rq_iov[0].iov_base + - rqst->rq_iov[0].iov_len, - rqst->rq_iov[i].iov_base, - rqst->rq_iov[i].iov_len); - rqst->rq_iov[0].iov_len += rqst->rq_iov[i].iov_len; - } - memset(rqst->rq_iov[0].iov_base + rqst->rq_iov[0].iov_len, - 0, num_padding); - rqst->rq_iov[0].iov_len += num_padding; - len += num_padding; - rqst->rq_nvec = 1; - } - - finished: - shdr->NextCommand = cpu_to_le32(len); -} - -/* - * Passes the query info response back to the caller on success. - * Caller need to free this with free_rsp_buf(). - */ -int -smb2_query_info_compound(const unsigned int xid, struct cifs_tcon *tcon, - const char *path, u32 desired_access, - u32 class, u32 type, u32 output_len, - struct kvec *rsp, int *buftype, - struct cifs_sb_info *cifs_sb) -{ - struct cifs_ses *ses = tcon->ses; - struct TCP_Server_Info *server = cifs_pick_channel(ses); - int flags = CIFS_CP_CREATE_CLOSE_OP; - struct smb_rqst rqst[3]; - int resp_buftype[3]; - struct kvec rsp_iov[3]; - struct kvec open_iov[SMB2_CREATE_IOV_SIZE]; - struct kvec qi_iov[1]; - struct kvec close_iov[1]; - u8 oplock = SMB2_OPLOCK_LEVEL_NONE; - struct cifs_open_parms oparms; - struct cifs_fid fid; - int rc; - __le16 *utf16_path; - struct cached_fid *cfid = NULL; - - if (!path) - path = ""; - utf16_path = cifs_convert_path_to_utf16(path, cifs_sb); - if (!utf16_path) - return -ENOMEM; - - if (smb3_encryption_required(tcon)) - flags |= CIFS_TRANSFORM_REQ; - - memset(rqst, 0, sizeof(rqst)); - resp_buftype[0] = resp_buftype[1] = resp_buftype[2] = CIFS_NO_BUFFER; - memset(rsp_iov, 0, sizeof(rsp_iov)); - - /* - * We can only call this for things we know are directories. - */ - if (!strcmp(path, "")) - open_cached_dir(xid, tcon, path, cifs_sb, false, - &cfid); /* cfid null if open dir failed */ - - memset(&open_iov, 0, sizeof(open_iov)); - rqst[0].rq_iov = open_iov; - rqst[0].rq_nvec = SMB2_CREATE_IOV_SIZE; - - oparms = (struct cifs_open_parms) { - .tcon = tcon, - .path = path, - .desired_access = desired_access, - .disposition = FILE_OPEN, - .create_options = cifs_create_options(cifs_sb, 0), - .fid = &fid, - }; - - rc = SMB2_open_init(tcon, server, - &rqst[0], &oplock, &oparms, utf16_path); - if (rc) - goto qic_exit; - smb2_set_next_command(tcon, &rqst[0]); - - memset(&qi_iov, 0, sizeof(qi_iov)); - rqst[1].rq_iov = qi_iov; - rqst[1].rq_nvec = 1; - - if (cfid) { - rc = SMB2_query_info_init(tcon, server, - &rqst[1], - cfid->fid.persistent_fid, - cfid->fid.volatile_fid, - class, type, 0, - output_len, 0, - NULL); - } else { - rc = SMB2_query_info_init(tcon, server, - &rqst[1], - COMPOUND_FID, - COMPOUND_FID, - class, type, 0, - output_len, 0, - NULL); - } - if (rc) - goto qic_exit; - if (!cfid) { - smb2_set_next_command(tcon, &rqst[1]); - smb2_set_related(&rqst[1]); - } - - memset(&close_iov, 0, sizeof(close_iov)); - rqst[2].rq_iov = close_iov; - rqst[2].rq_nvec = 1; - - rc = SMB2_close_init(tcon, server, - &rqst[2], COMPOUND_FID, COMPOUND_FID, false); - if (rc) - goto qic_exit; - smb2_set_related(&rqst[2]); - - if (cfid) { - rc = compound_send_recv(xid, ses, server, - flags, 1, &rqst[1], - &resp_buftype[1], &rsp_iov[1]); - } else { - rc = compound_send_recv(xid, ses, server, - flags, 3, rqst, - resp_buftype, rsp_iov); - } - if (rc) { - free_rsp_buf(resp_buftype[1], rsp_iov[1].iov_base); - if (rc == -EREMCHG) { - tcon->need_reconnect = true; - pr_warn_once("server share %s deleted\n", - tcon->tree_name); - } - goto qic_exit; - } - *rsp = rsp_iov[1]; - *buftype = resp_buftype[1]; - - qic_exit: - kfree(utf16_path); - SMB2_open_free(&rqst[0]); - SMB2_query_info_free(&rqst[1]); - SMB2_close_free(&rqst[2]); - free_rsp_buf(resp_buftype[0], rsp_iov[0].iov_base); - free_rsp_buf(resp_buftype[2], rsp_iov[2].iov_base); - if (cfid) - close_cached_dir(cfid); - return rc; -} - -static int -smb2_queryfs(const unsigned int xid, struct cifs_tcon *tcon, - struct cifs_sb_info *cifs_sb, struct kstatfs *buf) -{ - struct smb2_query_info_rsp *rsp; - struct smb2_fs_full_size_info *info = NULL; - struct kvec rsp_iov = {NULL, 0}; - int buftype = CIFS_NO_BUFFER; - int rc; - - - rc = smb2_query_info_compound(xid, tcon, "", - FILE_READ_ATTRIBUTES, - FS_FULL_SIZE_INFORMATION, - SMB2_O_INFO_FILESYSTEM, - sizeof(struct smb2_fs_full_size_info), - &rsp_iov, &buftype, cifs_sb); - if (rc) - goto qfs_exit; - - rsp = (struct smb2_query_info_rsp *)rsp_iov.iov_base; - buf->f_type = SMB2_SUPER_MAGIC; - info = (struct smb2_fs_full_size_info *)( - le16_to_cpu(rsp->OutputBufferOffset) + (char *)rsp); - rc = smb2_validate_iov(le16_to_cpu(rsp->OutputBufferOffset), - le32_to_cpu(rsp->OutputBufferLength), - &rsp_iov, - sizeof(struct smb2_fs_full_size_info)); - if (!rc) - smb2_copy_fs_info_to_kstatfs(info, buf); - -qfs_exit: - free_rsp_buf(buftype, rsp_iov.iov_base); - return rc; -} - -static int -smb311_queryfs(const unsigned int xid, struct cifs_tcon *tcon, - struct cifs_sb_info *cifs_sb, struct kstatfs *buf) -{ - int rc; - __le16 srch_path = 0; /* Null - open root of share */ - u8 oplock = SMB2_OPLOCK_LEVEL_NONE; - struct cifs_open_parms oparms; - struct cifs_fid fid; - - if (!tcon->posix_extensions) - return smb2_queryfs(xid, tcon, cifs_sb, buf); - - oparms = (struct cifs_open_parms) { - .tcon = tcon, - .path = "", - .desired_access = FILE_READ_ATTRIBUTES, - .disposition = FILE_OPEN, - .create_options = cifs_create_options(cifs_sb, 0), - .fid = &fid, - }; - - rc = SMB2_open(xid, &oparms, &srch_path, &oplock, NULL, NULL, - NULL, NULL); - if (rc) - return rc; - - rc = SMB311_posix_qfs_info(xid, tcon, fid.persistent_fid, - fid.volatile_fid, buf); - buf->f_type = SMB2_SUPER_MAGIC; - SMB2_close(xid, tcon, fid.persistent_fid, fid.volatile_fid); - return rc; -} - -static bool -smb2_compare_fids(struct cifsFileInfo *ob1, struct cifsFileInfo *ob2) -{ - return ob1->fid.persistent_fid == ob2->fid.persistent_fid && - ob1->fid.volatile_fid == ob2->fid.volatile_fid; -} - -static int -smb2_mand_lock(const unsigned int xid, struct cifsFileInfo *cfile, __u64 offset, - __u64 length, __u32 type, int lock, int unlock, bool wait) -{ - if (unlock && !lock) - type = SMB2_LOCKFLAG_UNLOCK; - return SMB2_lock(xid, tlink_tcon(cfile->tlink), - cfile->fid.persistent_fid, cfile->fid.volatile_fid, - current->tgid, length, offset, type, wait); -} - -static void -smb2_get_lease_key(struct inode *inode, struct cifs_fid *fid) -{ - memcpy(fid->lease_key, CIFS_I(inode)->lease_key, SMB2_LEASE_KEY_SIZE); -} - -static void -smb2_set_lease_key(struct inode *inode, struct cifs_fid *fid) -{ - memcpy(CIFS_I(inode)->lease_key, fid->lease_key, SMB2_LEASE_KEY_SIZE); -} - -static void -smb2_new_lease_key(struct cifs_fid *fid) -{ - generate_random_uuid(fid->lease_key); -} - -static int -smb2_get_dfs_refer(const unsigned int xid, struct cifs_ses *ses, - const char *search_name, - struct dfs_info3_param **target_nodes, - unsigned int *num_of_nodes, - const struct nls_table *nls_codepage, int remap) -{ - int rc; - __le16 *utf16_path = NULL; - int utf16_path_len = 0; - struct cifs_tcon *tcon; - struct fsctl_get_dfs_referral_req *dfs_req = NULL; - struct get_dfs_referral_rsp *dfs_rsp = NULL; - u32 dfs_req_size = 0, dfs_rsp_size = 0; - int retry_count = 0; - - cifs_dbg(FYI, "%s: path: %s\n", __func__, search_name); - - /* - * Try to use the IPC tcon, otherwise just use any - */ - tcon = ses->tcon_ipc; - if (tcon == NULL) { - spin_lock(&cifs_tcp_ses_lock); - tcon = list_first_entry_or_null(&ses->tcon_list, - struct cifs_tcon, - tcon_list); - if (tcon) - tcon->tc_count++; - spin_unlock(&cifs_tcp_ses_lock); - } - - if (tcon == NULL) { - cifs_dbg(VFS, "session %p has no tcon available for a dfs referral request\n", - ses); - rc = -ENOTCONN; - goto out; - } - - utf16_path = cifs_strndup_to_utf16(search_name, PATH_MAX, - &utf16_path_len, - nls_codepage, remap); - if (!utf16_path) { - rc = -ENOMEM; - goto out; - } - - dfs_req_size = sizeof(*dfs_req) + utf16_path_len; - dfs_req = kzalloc(dfs_req_size, GFP_KERNEL); - if (!dfs_req) { - rc = -ENOMEM; - goto out; - } - - /* Highest DFS referral version understood */ - dfs_req->MaxReferralLevel = DFS_VERSION; - - /* Path to resolve in an UTF-16 null-terminated string */ - memcpy(dfs_req->RequestFileName, utf16_path, utf16_path_len); - - do { - rc = SMB2_ioctl(xid, tcon, NO_FILE_ID, NO_FILE_ID, - FSCTL_DFS_GET_REFERRALS, - (char *)dfs_req, dfs_req_size, CIFSMaxBufSize, - (char **)&dfs_rsp, &dfs_rsp_size); - if (!is_retryable_error(rc)) - break; - usleep_range(512, 2048); - } while (++retry_count < 5); - - if (rc) { - if (!is_retryable_error(rc) && rc != -ENOENT && rc != -EOPNOTSUPP) - cifs_tcon_dbg(VFS, "%s: ioctl error: rc=%d\n", __func__, rc); - goto out; - } - - rc = parse_dfs_referrals(dfs_rsp, dfs_rsp_size, - num_of_nodes, target_nodes, - nls_codepage, remap, search_name, - true /* is_unicode */); - if (rc) { - cifs_tcon_dbg(VFS, "parse error in %s rc=%d\n", __func__, rc); - goto out; - } - - out: - if (tcon && !tcon->ipc) { - /* ipc tcons are not refcounted */ - spin_lock(&cifs_tcp_ses_lock); - tcon->tc_count--; - /* tc_count can never go negative */ - WARN_ON(tcon->tc_count < 0); - spin_unlock(&cifs_tcp_ses_lock); - } - kfree(utf16_path); - kfree(dfs_req); - kfree(dfs_rsp); - return rc; -} - -static int -parse_reparse_posix(struct reparse_posix_data *symlink_buf, - u32 plen, char **target_path, - struct cifs_sb_info *cifs_sb) -{ - unsigned int len; - - /* See MS-FSCC 2.1.2.6 for the 'NFS' style reparse tags */ - len = le16_to_cpu(symlink_buf->ReparseDataLength); - - if (le64_to_cpu(symlink_buf->InodeType) != NFS_SPECFILE_LNK) { - cifs_dbg(VFS, "%lld not a supported symlink type\n", - le64_to_cpu(symlink_buf->InodeType)); - return -EOPNOTSUPP; - } - - *target_path = cifs_strndup_from_utf16( - symlink_buf->PathBuffer, - len, true, cifs_sb->local_nls); - if (!(*target_path)) - return -ENOMEM; - - convert_delimiter(*target_path, '/'); - cifs_dbg(FYI, "%s: target path: %s\n", __func__, *target_path); - - return 0; -} - -static int -parse_reparse_symlink(struct reparse_symlink_data_buffer *symlink_buf, - u32 plen, char **target_path, - struct cifs_sb_info *cifs_sb) -{ - unsigned int sub_len; - unsigned int sub_offset; - - /* We handle Symbolic Link reparse tag here. See: MS-FSCC 2.1.2.4 */ - - sub_offset = le16_to_cpu(symlink_buf->SubstituteNameOffset); - sub_len = le16_to_cpu(symlink_buf->SubstituteNameLength); - if (sub_offset + 20 > plen || - sub_offset + sub_len + 20 > plen) { - cifs_dbg(VFS, "srv returned malformed symlink buffer\n"); - return -EIO; - } - - *target_path = cifs_strndup_from_utf16( - symlink_buf->PathBuffer + sub_offset, - sub_len, true, cifs_sb->local_nls); - if (!(*target_path)) - return -ENOMEM; - - convert_delimiter(*target_path, '/'); - cifs_dbg(FYI, "%s: target path: %s\n", __func__, *target_path); - - return 0; -} - -static int -parse_reparse_point(struct reparse_data_buffer *buf, - u32 plen, char **target_path, - struct cifs_sb_info *cifs_sb) -{ - if (plen < sizeof(struct reparse_data_buffer)) { - cifs_dbg(VFS, "reparse buffer is too small. Must be at least 8 bytes but was %d\n", - plen); - return -EIO; - } - - if (plen < le16_to_cpu(buf->ReparseDataLength) + - sizeof(struct reparse_data_buffer)) { - cifs_dbg(VFS, "srv returned invalid reparse buf length: %d\n", - plen); - return -EIO; - } - - /* See MS-FSCC 2.1.2 */ - switch (le32_to_cpu(buf->ReparseTag)) { - case IO_REPARSE_TAG_NFS: - return parse_reparse_posix( - (struct reparse_posix_data *)buf, - plen, target_path, cifs_sb); - case IO_REPARSE_TAG_SYMLINK: - return parse_reparse_symlink( - (struct reparse_symlink_data_buffer *)buf, - plen, target_path, cifs_sb); - default: - cifs_dbg(VFS, "srv returned unknown symlink buffer tag:0x%08x\n", - le32_to_cpu(buf->ReparseTag)); - return -EOPNOTSUPP; - } -} - -static int -smb2_query_symlink(const unsigned int xid, struct cifs_tcon *tcon, - struct cifs_sb_info *cifs_sb, const char *full_path, - char **target_path, bool is_reparse_point) -{ - int rc; - __le16 *utf16_path = NULL; - __u8 oplock = SMB2_OPLOCK_LEVEL_NONE; - struct cifs_open_parms oparms; - struct cifs_fid fid; - struct kvec err_iov = {NULL, 0}; - struct TCP_Server_Info *server = cifs_pick_channel(tcon->ses); - int flags = CIFS_CP_CREATE_CLOSE_OP; - struct smb_rqst rqst[3]; - int resp_buftype[3]; - struct kvec rsp_iov[3]; - struct kvec open_iov[SMB2_CREATE_IOV_SIZE]; - struct kvec io_iov[SMB2_IOCTL_IOV_SIZE]; - struct kvec close_iov[1]; - struct smb2_create_rsp *create_rsp; - struct smb2_ioctl_rsp *ioctl_rsp; - struct reparse_data_buffer *reparse_buf; - int create_options = is_reparse_point ? OPEN_REPARSE_POINT : 0; - u32 plen; - - cifs_dbg(FYI, "%s: path: %s\n", __func__, full_path); - - *target_path = NULL; - - if (smb3_encryption_required(tcon)) - flags |= CIFS_TRANSFORM_REQ; - - memset(rqst, 0, sizeof(rqst)); - resp_buftype[0] = resp_buftype[1] = resp_buftype[2] = CIFS_NO_BUFFER; - memset(rsp_iov, 0, sizeof(rsp_iov)); - - utf16_path = cifs_convert_path_to_utf16(full_path, cifs_sb); - if (!utf16_path) - return -ENOMEM; - - /* Open */ - memset(&open_iov, 0, sizeof(open_iov)); - rqst[0].rq_iov = open_iov; - rqst[0].rq_nvec = SMB2_CREATE_IOV_SIZE; - - oparms = (struct cifs_open_parms) { - .tcon = tcon, - .path = full_path, - .desired_access = FILE_READ_ATTRIBUTES, - .disposition = FILE_OPEN, - .create_options = cifs_create_options(cifs_sb, create_options), - .fid = &fid, - }; - - rc = SMB2_open_init(tcon, server, - &rqst[0], &oplock, &oparms, utf16_path); - if (rc) - goto querty_exit; - smb2_set_next_command(tcon, &rqst[0]); - - - /* IOCTL */ - memset(&io_iov, 0, sizeof(io_iov)); - rqst[1].rq_iov = io_iov; - rqst[1].rq_nvec = SMB2_IOCTL_IOV_SIZE; - - rc = SMB2_ioctl_init(tcon, server, - &rqst[1], fid.persistent_fid, - fid.volatile_fid, FSCTL_GET_REPARSE_POINT, NULL, 0, - CIFSMaxBufSize - - MAX_SMB2_CREATE_RESPONSE_SIZE - - MAX_SMB2_CLOSE_RESPONSE_SIZE); - if (rc) - goto querty_exit; - - smb2_set_next_command(tcon, &rqst[1]); - smb2_set_related(&rqst[1]); - - - /* Close */ - memset(&close_iov, 0, sizeof(close_iov)); - rqst[2].rq_iov = close_iov; - rqst[2].rq_nvec = 1; - - rc = SMB2_close_init(tcon, server, - &rqst[2], COMPOUND_FID, COMPOUND_FID, false); - if (rc) - goto querty_exit; - - smb2_set_related(&rqst[2]); - - rc = compound_send_recv(xid, tcon->ses, server, - flags, 3, rqst, - resp_buftype, rsp_iov); - - create_rsp = rsp_iov[0].iov_base; - if (create_rsp && create_rsp->hdr.Status) - err_iov = rsp_iov[0]; - ioctl_rsp = rsp_iov[1].iov_base; - - /* - * Open was successful and we got an ioctl response. - */ - if ((rc == 0) && (is_reparse_point)) { - /* See MS-FSCC 2.3.23 */ - - reparse_buf = (struct reparse_data_buffer *) - ((char *)ioctl_rsp + - le32_to_cpu(ioctl_rsp->OutputOffset)); - plen = le32_to_cpu(ioctl_rsp->OutputCount); - - if (plen + le32_to_cpu(ioctl_rsp->OutputOffset) > - rsp_iov[1].iov_len) { - cifs_tcon_dbg(VFS, "srv returned invalid ioctl len: %d\n", - plen); - rc = -EIO; - goto querty_exit; - } - - rc = parse_reparse_point(reparse_buf, plen, target_path, - cifs_sb); - goto querty_exit; - } - - if (!rc || !err_iov.iov_base) { - rc = -ENOENT; - goto querty_exit; - } - - rc = smb2_parse_symlink_response(cifs_sb, &err_iov, target_path); - - querty_exit: - cifs_dbg(FYI, "query symlink rc %d\n", rc); - kfree(utf16_path); - SMB2_open_free(&rqst[0]); - SMB2_ioctl_free(&rqst[1]); - SMB2_close_free(&rqst[2]); - free_rsp_buf(resp_buftype[0], rsp_iov[0].iov_base); - free_rsp_buf(resp_buftype[1], rsp_iov[1].iov_base); - free_rsp_buf(resp_buftype[2], rsp_iov[2].iov_base); - return rc; -} - -int -smb2_query_reparse_tag(const unsigned int xid, struct cifs_tcon *tcon, - struct cifs_sb_info *cifs_sb, const char *full_path, - __u32 *tag) -{ - int rc; - __le16 *utf16_path = NULL; - __u8 oplock = SMB2_OPLOCK_LEVEL_NONE; - struct cifs_open_parms oparms; - struct cifs_fid fid; - struct TCP_Server_Info *server = cifs_pick_channel(tcon->ses); - int flags = CIFS_CP_CREATE_CLOSE_OP; - struct smb_rqst rqst[3]; - int resp_buftype[3]; - struct kvec rsp_iov[3]; - struct kvec open_iov[SMB2_CREATE_IOV_SIZE]; - struct kvec io_iov[SMB2_IOCTL_IOV_SIZE]; - struct kvec close_iov[1]; - struct smb2_ioctl_rsp *ioctl_rsp; - struct reparse_data_buffer *reparse_buf; - u32 plen; - - cifs_dbg(FYI, "%s: path: %s\n", __func__, full_path); - - if (smb3_encryption_required(tcon)) - flags |= CIFS_TRANSFORM_REQ; - - memset(rqst, 0, sizeof(rqst)); - resp_buftype[0] = resp_buftype[1] = resp_buftype[2] = CIFS_NO_BUFFER; - memset(rsp_iov, 0, sizeof(rsp_iov)); - - utf16_path = cifs_convert_path_to_utf16(full_path, cifs_sb); - if (!utf16_path) - return -ENOMEM; - - /* - * setup smb2open - TODO add optimization to call cifs_get_readable_path - * to see if there is a handle already open that we can use - */ - memset(&open_iov, 0, sizeof(open_iov)); - rqst[0].rq_iov = open_iov; - rqst[0].rq_nvec = SMB2_CREATE_IOV_SIZE; - - oparms = (struct cifs_open_parms) { - .tcon = tcon, - .path = full_path, - .desired_access = FILE_READ_ATTRIBUTES, - .disposition = FILE_OPEN, - .create_options = cifs_create_options(cifs_sb, OPEN_REPARSE_POINT), - .fid = &fid, - }; - - rc = SMB2_open_init(tcon, server, - &rqst[0], &oplock, &oparms, utf16_path); - if (rc) - goto query_rp_exit; - smb2_set_next_command(tcon, &rqst[0]); - - - /* IOCTL */ - memset(&io_iov, 0, sizeof(io_iov)); - rqst[1].rq_iov = io_iov; - rqst[1].rq_nvec = SMB2_IOCTL_IOV_SIZE; - - rc = SMB2_ioctl_init(tcon, server, - &rqst[1], COMPOUND_FID, - COMPOUND_FID, FSCTL_GET_REPARSE_POINT, NULL, 0, - CIFSMaxBufSize - - MAX_SMB2_CREATE_RESPONSE_SIZE - - MAX_SMB2_CLOSE_RESPONSE_SIZE); - if (rc) - goto query_rp_exit; - - smb2_set_next_command(tcon, &rqst[1]); - smb2_set_related(&rqst[1]); - - - /* Close */ - memset(&close_iov, 0, sizeof(close_iov)); - rqst[2].rq_iov = close_iov; - rqst[2].rq_nvec = 1; - - rc = SMB2_close_init(tcon, server, - &rqst[2], COMPOUND_FID, COMPOUND_FID, false); - if (rc) - goto query_rp_exit; - - smb2_set_related(&rqst[2]); - - rc = compound_send_recv(xid, tcon->ses, server, - flags, 3, rqst, - resp_buftype, rsp_iov); - - ioctl_rsp = rsp_iov[1].iov_base; - - /* - * Open was successful and we got an ioctl response. - */ - if (rc == 0) { - /* See MS-FSCC 2.3.23 */ - - reparse_buf = (struct reparse_data_buffer *) - ((char *)ioctl_rsp + - le32_to_cpu(ioctl_rsp->OutputOffset)); - plen = le32_to_cpu(ioctl_rsp->OutputCount); - - if (plen + le32_to_cpu(ioctl_rsp->OutputOffset) > - rsp_iov[1].iov_len) { - cifs_tcon_dbg(FYI, "srv returned invalid ioctl len: %d\n", - plen); - rc = -EIO; - goto query_rp_exit; - } - *tag = le32_to_cpu(reparse_buf->ReparseTag); - } - - query_rp_exit: - kfree(utf16_path); - SMB2_open_free(&rqst[0]); - SMB2_ioctl_free(&rqst[1]); - SMB2_close_free(&rqst[2]); - free_rsp_buf(resp_buftype[0], rsp_iov[0].iov_base); - free_rsp_buf(resp_buftype[1], rsp_iov[1].iov_base); - free_rsp_buf(resp_buftype[2], rsp_iov[2].iov_base); - return rc; -} - -static struct cifs_ntsd * -get_smb2_acl_by_fid(struct cifs_sb_info *cifs_sb, - const struct cifs_fid *cifsfid, u32 *pacllen, u32 info) -{ - struct cifs_ntsd *pntsd = NULL; - unsigned int xid; - int rc = -EOPNOTSUPP; - struct tcon_link *tlink = cifs_sb_tlink(cifs_sb); - - if (IS_ERR(tlink)) - return ERR_CAST(tlink); - - xid = get_xid(); - cifs_dbg(FYI, "trying to get acl\n"); - - rc = SMB2_query_acl(xid, tlink_tcon(tlink), cifsfid->persistent_fid, - cifsfid->volatile_fid, (void **)&pntsd, pacllen, - info); - free_xid(xid); - - cifs_put_tlink(tlink); - - cifs_dbg(FYI, "%s: rc = %d ACL len %d\n", __func__, rc, *pacllen); - if (rc) - return ERR_PTR(rc); - return pntsd; - -} - -static struct cifs_ntsd * -get_smb2_acl_by_path(struct cifs_sb_info *cifs_sb, - const char *path, u32 *pacllen, u32 info) -{ - struct cifs_ntsd *pntsd = NULL; - u8 oplock = SMB2_OPLOCK_LEVEL_NONE; - unsigned int xid; - int rc; - struct cifs_tcon *tcon; - struct tcon_link *tlink = cifs_sb_tlink(cifs_sb); - struct cifs_fid fid; - struct cifs_open_parms oparms; - __le16 *utf16_path; - - cifs_dbg(FYI, "get smb3 acl for path %s\n", path); - if (IS_ERR(tlink)) - return ERR_CAST(tlink); - - tcon = tlink_tcon(tlink); - xid = get_xid(); - - utf16_path = cifs_convert_path_to_utf16(path, cifs_sb); - if (!utf16_path) { - rc = -ENOMEM; - free_xid(xid); - return ERR_PTR(rc); - } - - oparms = (struct cifs_open_parms) { - .tcon = tcon, - .path = path, - .desired_access = READ_CONTROL, - .disposition = FILE_OPEN, - /* - * When querying an ACL, even if the file is a symlink - * we want to open the source not the target, and so - * the protocol requires that the client specify this - * flag when opening a reparse point - */ - .create_options = cifs_create_options(cifs_sb, 0) | - OPEN_REPARSE_POINT, - .fid = &fid, - }; - - if (info & SACL_SECINFO) - oparms.desired_access |= SYSTEM_SECURITY; - - rc = SMB2_open(xid, &oparms, utf16_path, &oplock, NULL, NULL, NULL, - NULL); - kfree(utf16_path); - if (!rc) { - rc = SMB2_query_acl(xid, tlink_tcon(tlink), fid.persistent_fid, - fid.volatile_fid, (void **)&pntsd, pacllen, - info); - SMB2_close(xid, tcon, fid.persistent_fid, fid.volatile_fid); - } - - cifs_put_tlink(tlink); - free_xid(xid); - - cifs_dbg(FYI, "%s: rc = %d ACL len %d\n", __func__, rc, *pacllen); - if (rc) - return ERR_PTR(rc); - return pntsd; -} - -static int -set_smb2_acl(struct cifs_ntsd *pnntsd, __u32 acllen, - struct inode *inode, const char *path, int aclflag) -{ - u8 oplock = SMB2_OPLOCK_LEVEL_NONE; - unsigned int xid; - int rc, access_flags = 0; - struct cifs_tcon *tcon; - struct cifs_sb_info *cifs_sb = CIFS_SB(inode->i_sb); - struct tcon_link *tlink = cifs_sb_tlink(cifs_sb); - struct cifs_fid fid; - struct cifs_open_parms oparms; - __le16 *utf16_path; - - cifs_dbg(FYI, "set smb3 acl for path %s\n", path); - if (IS_ERR(tlink)) - return PTR_ERR(tlink); - - tcon = tlink_tcon(tlink); - xid = get_xid(); - - if (aclflag & CIFS_ACL_OWNER || aclflag & CIFS_ACL_GROUP) - access_flags |= WRITE_OWNER; - if (aclflag & CIFS_ACL_SACL) - access_flags |= SYSTEM_SECURITY; - if (aclflag & CIFS_ACL_DACL) - access_flags |= WRITE_DAC; - - utf16_path = cifs_convert_path_to_utf16(path, cifs_sb); - if (!utf16_path) { - rc = -ENOMEM; - free_xid(xid); - return rc; - } - - oparms = (struct cifs_open_parms) { - .tcon = tcon, - .desired_access = access_flags, - .create_options = cifs_create_options(cifs_sb, 0), - .disposition = FILE_OPEN, - .path = path, - .fid = &fid, - }; - - rc = SMB2_open(xid, &oparms, utf16_path, &oplock, NULL, NULL, - NULL, NULL); - kfree(utf16_path); - if (!rc) { - rc = SMB2_set_acl(xid, tlink_tcon(tlink), fid.persistent_fid, - fid.volatile_fid, pnntsd, acllen, aclflag); - SMB2_close(xid, tcon, fid.persistent_fid, fid.volatile_fid); - } - - cifs_put_tlink(tlink); - free_xid(xid); - return rc; -} - -/* Retrieve an ACL from the server */ -static struct cifs_ntsd * -get_smb2_acl(struct cifs_sb_info *cifs_sb, - struct inode *inode, const char *path, - u32 *pacllen, u32 info) -{ - struct cifs_ntsd *pntsd = NULL; - struct cifsFileInfo *open_file = NULL; - - if (inode && !(info & SACL_SECINFO)) - open_file = find_readable_file(CIFS_I(inode), true); - if (!open_file || (info & SACL_SECINFO)) - return get_smb2_acl_by_path(cifs_sb, path, pacllen, info); - - pntsd = get_smb2_acl_by_fid(cifs_sb, &open_file->fid, pacllen, info); - cifsFileInfo_put(open_file); - return pntsd; -} - -static long smb3_zero_data(struct file *file, struct cifs_tcon *tcon, - loff_t offset, loff_t len, unsigned int xid) -{ - struct cifsFileInfo *cfile = file->private_data; - struct file_zero_data_information fsctl_buf; - - cifs_dbg(FYI, "Offset %lld len %lld\n", offset, len); - - fsctl_buf.FileOffset = cpu_to_le64(offset); - fsctl_buf.BeyondFinalZero = cpu_to_le64(offset + len); - - return SMB2_ioctl(xid, tcon, cfile->fid.persistent_fid, - cfile->fid.volatile_fid, FSCTL_SET_ZERO_DATA, - (char *)&fsctl_buf, - sizeof(struct file_zero_data_information), - 0, NULL, NULL); -} - -static long smb3_zero_range(struct file *file, struct cifs_tcon *tcon, - loff_t offset, loff_t len, bool keep_size) -{ - struct cifs_ses *ses = tcon->ses; - struct inode *inode = file_inode(file); - struct cifsInodeInfo *cifsi = CIFS_I(inode); - struct cifsFileInfo *cfile = file->private_data; - long rc; - unsigned int xid; - __le64 eof; - - xid = get_xid(); - - trace_smb3_zero_enter(xid, cfile->fid.persistent_fid, tcon->tid, - ses->Suid, offset, len); - - inode_lock(inode); - filemap_invalidate_lock(inode->i_mapping); - - /* - * We zero the range through ioctl, so we need remove the page caches - * first, otherwise the data may be inconsistent with the server. - */ - truncate_pagecache_range(inode, offset, offset + len - 1); - - /* if file not oplocked can't be sure whether asking to extend size */ - rc = -EOPNOTSUPP; - if (keep_size == false && !CIFS_CACHE_READ(cifsi)) - goto zero_range_exit; - - rc = smb3_zero_data(file, tcon, offset, len, xid); - if (rc < 0) - goto zero_range_exit; - - /* - * do we also need to change the size of the file? - */ - if (keep_size == false && i_size_read(inode) < offset + len) { - eof = cpu_to_le64(offset + len); - rc = SMB2_set_eof(xid, tcon, cfile->fid.persistent_fid, - cfile->fid.volatile_fid, cfile->pid, &eof); - } - - zero_range_exit: - filemap_invalidate_unlock(inode->i_mapping); - inode_unlock(inode); - free_xid(xid); - if (rc) - trace_smb3_zero_err(xid, cfile->fid.persistent_fid, tcon->tid, - ses->Suid, offset, len, rc); - else - trace_smb3_zero_done(xid, cfile->fid.persistent_fid, tcon->tid, - ses->Suid, offset, len); - return rc; -} - -static long smb3_punch_hole(struct file *file, struct cifs_tcon *tcon, - loff_t offset, loff_t len) -{ - struct inode *inode = file_inode(file); - struct cifsFileInfo *cfile = file->private_data; - struct file_zero_data_information fsctl_buf; - long rc; - unsigned int xid; - __u8 set_sparse = 1; - - xid = get_xid(); - - inode_lock(inode); - /* Need to make file sparse, if not already, before freeing range. */ - /* Consider adding equivalent for compressed since it could also work */ - if (!smb2_set_sparse(xid, tcon, cfile, inode, set_sparse)) { - rc = -EOPNOTSUPP; - goto out; - } - - filemap_invalidate_lock(inode->i_mapping); - /* - * We implement the punch hole through ioctl, so we need remove the page - * caches first, otherwise the data may be inconsistent with the server. - */ - truncate_pagecache_range(inode, offset, offset + len - 1); - - cifs_dbg(FYI, "Offset %lld len %lld\n", offset, len); - - fsctl_buf.FileOffset = cpu_to_le64(offset); - fsctl_buf.BeyondFinalZero = cpu_to_le64(offset + len); - - rc = SMB2_ioctl(xid, tcon, cfile->fid.persistent_fid, - cfile->fid.volatile_fid, FSCTL_SET_ZERO_DATA, - (char *)&fsctl_buf, - sizeof(struct file_zero_data_information), - CIFSMaxBufSize, NULL, NULL); - filemap_invalidate_unlock(inode->i_mapping); -out: - inode_unlock(inode); - free_xid(xid); - return rc; -} - -static int smb3_simple_fallocate_write_range(unsigned int xid, - struct cifs_tcon *tcon, - struct cifsFileInfo *cfile, - loff_t off, loff_t len, - char *buf) -{ - struct cifs_io_parms io_parms = {0}; - int nbytes; - int rc = 0; - struct kvec iov[2]; - - io_parms.netfid = cfile->fid.netfid; - io_parms.pid = current->tgid; - io_parms.tcon = tcon; - io_parms.persistent_fid = cfile->fid.persistent_fid; - io_parms.volatile_fid = cfile->fid.volatile_fid; - - while (len) { - io_parms.offset = off; - io_parms.length = len; - if (io_parms.length > SMB2_MAX_BUFFER_SIZE) - io_parms.length = SMB2_MAX_BUFFER_SIZE; - /* iov[0] is reserved for smb header */ - iov[1].iov_base = buf; - iov[1].iov_len = io_parms.length; - rc = SMB2_write(xid, &io_parms, &nbytes, iov, 1); - if (rc) - break; - if (nbytes > len) - return -EINVAL; - buf += nbytes; - off += nbytes; - len -= nbytes; - } - return rc; -} - -static int smb3_simple_fallocate_range(unsigned int xid, - struct cifs_tcon *tcon, - struct cifsFileInfo *cfile, - loff_t off, loff_t len) -{ - struct file_allocated_range_buffer in_data, *out_data = NULL, *tmp_data; - u32 out_data_len; - char *buf = NULL; - loff_t l; - int rc; - - in_data.file_offset = cpu_to_le64(off); - in_data.length = cpu_to_le64(len); - rc = SMB2_ioctl(xid, tcon, cfile->fid.persistent_fid, - cfile->fid.volatile_fid, - FSCTL_QUERY_ALLOCATED_RANGES, - (char *)&in_data, sizeof(in_data), - 1024 * sizeof(struct file_allocated_range_buffer), - (char **)&out_data, &out_data_len); - if (rc) - goto out; - - buf = kzalloc(1024 * 1024, GFP_KERNEL); - if (buf == NULL) { - rc = -ENOMEM; - goto out; - } - - tmp_data = out_data; - while (len) { - /* - * The rest of the region is unmapped so write it all. - */ - if (out_data_len == 0) { - rc = smb3_simple_fallocate_write_range(xid, tcon, - cfile, off, len, buf); - goto out; - } - - if (out_data_len < sizeof(struct file_allocated_range_buffer)) { - rc = -EINVAL; - goto out; - } - - if (off < le64_to_cpu(tmp_data->file_offset)) { - /* - * We are at a hole. Write until the end of the region - * or until the next allocated data, - * whichever comes next. - */ - l = le64_to_cpu(tmp_data->file_offset) - off; - if (len < l) - l = len; - rc = smb3_simple_fallocate_write_range(xid, tcon, - cfile, off, l, buf); - if (rc) - goto out; - off = off + l; - len = len - l; - if (len == 0) - goto out; - } - /* - * We are at a section of allocated data, just skip forward - * until the end of the data or the end of the region - * we are supposed to fallocate, whichever comes first. - */ - l = le64_to_cpu(tmp_data->length); - if (len < l) - l = len; - off += l; - len -= l; - - tmp_data = &tmp_data[1]; - out_data_len -= sizeof(struct file_allocated_range_buffer); - } - - out: - kfree(out_data); - kfree(buf); - return rc; -} - - -static long smb3_simple_falloc(struct file *file, struct cifs_tcon *tcon, - loff_t off, loff_t len, bool keep_size) -{ - struct inode *inode; - struct cifsInodeInfo *cifsi; - struct cifsFileInfo *cfile = file->private_data; - long rc = -EOPNOTSUPP; - unsigned int xid; - __le64 eof; - - xid = get_xid(); - - inode = d_inode(cfile->dentry); - cifsi = CIFS_I(inode); - - trace_smb3_falloc_enter(xid, cfile->fid.persistent_fid, tcon->tid, - tcon->ses->Suid, off, len); - /* if file not oplocked can't be sure whether asking to extend size */ - if (!CIFS_CACHE_READ(cifsi)) - if (keep_size == false) { - trace_smb3_falloc_err(xid, cfile->fid.persistent_fid, - tcon->tid, tcon->ses->Suid, off, len, rc); - free_xid(xid); - return rc; - } - - /* - * Extending the file - */ - if ((keep_size == false) && i_size_read(inode) < off + len) { - rc = inode_newsize_ok(inode, off + len); - if (rc) - goto out; - - if (cifsi->cifsAttrs & FILE_ATTRIBUTE_SPARSE_FILE) - smb2_set_sparse(xid, tcon, cfile, inode, false); - - eof = cpu_to_le64(off + len); - rc = SMB2_set_eof(xid, tcon, cfile->fid.persistent_fid, - cfile->fid.volatile_fid, cfile->pid, &eof); - if (rc == 0) { - cifsi->server_eof = off + len; - cifs_setsize(inode, off + len); - cifs_truncate_page(inode->i_mapping, inode->i_size); - truncate_setsize(inode, off + len); - } - goto out; - } - - /* - * Files are non-sparse by default so falloc may be a no-op - * Must check if file sparse. If not sparse, and since we are not - * extending then no need to do anything since file already allocated - */ - if ((cifsi->cifsAttrs & FILE_ATTRIBUTE_SPARSE_FILE) == 0) { - rc = 0; - goto out; - } - - if (keep_size == true) { - /* - * We can not preallocate pages beyond the end of the file - * in SMB2 - */ - if (off >= i_size_read(inode)) { - rc = 0; - goto out; - } - /* - * For fallocates that are partially beyond the end of file, - * clamp len so we only fallocate up to the end of file. - */ - if (off + len > i_size_read(inode)) { - len = i_size_read(inode) - off; - } - } - - if ((keep_size == true) || (i_size_read(inode) >= off + len)) { - /* - * At this point, we are trying to fallocate an internal - * regions of a sparse file. Since smb2 does not have a - * fallocate command we have two otions on how to emulate this. - * We can either turn the entire file to become non-sparse - * which we only do if the fallocate is for virtually - * the whole file, or we can overwrite the region with zeroes - * using SMB2_write, which could be prohibitevly expensive - * if len is large. - */ - /* - * We are only trying to fallocate a small region so - * just write it with zero. - */ - if (len <= 1024 * 1024) { - rc = smb3_simple_fallocate_range(xid, tcon, cfile, - off, len); - goto out; - } - - /* - * Check if falloc starts within first few pages of file - * and ends within a few pages of the end of file to - * ensure that most of file is being forced to be - * fallocated now. If so then setting whole file sparse - * ie potentially making a few extra pages at the beginning - * or end of the file non-sparse via set_sparse is harmless. - */ - if ((off > 8192) || (off + len + 8192 < i_size_read(inode))) { - rc = -EOPNOTSUPP; - goto out; - } - } - - smb2_set_sparse(xid, tcon, cfile, inode, false); - rc = 0; - -out: - if (rc) - trace_smb3_falloc_err(xid, cfile->fid.persistent_fid, tcon->tid, - tcon->ses->Suid, off, len, rc); - else - trace_smb3_falloc_done(xid, cfile->fid.persistent_fid, tcon->tid, - tcon->ses->Suid, off, len); - - free_xid(xid); - return rc; -} - -static long smb3_collapse_range(struct file *file, struct cifs_tcon *tcon, - loff_t off, loff_t len) -{ - int rc; - unsigned int xid; - struct inode *inode = file_inode(file); - struct cifsFileInfo *cfile = file->private_data; - struct cifsInodeInfo *cifsi = CIFS_I(inode); - __le64 eof; - loff_t old_eof; - - xid = get_xid(); - - inode_lock(inode); - - old_eof = i_size_read(inode); - if ((off >= old_eof) || - off + len >= old_eof) { - rc = -EINVAL; - goto out; - } - - filemap_invalidate_lock(inode->i_mapping); - rc = filemap_write_and_wait_range(inode->i_mapping, off, old_eof - 1); - if (rc < 0) - goto out_2; - - truncate_pagecache_range(inode, off, old_eof); - - rc = smb2_copychunk_range(xid, cfile, cfile, off + len, - old_eof - off - len, off); - if (rc < 0) - goto out_2; - - eof = cpu_to_le64(old_eof - len); - rc = SMB2_set_eof(xid, tcon, cfile->fid.persistent_fid, - cfile->fid.volatile_fid, cfile->pid, &eof); - if (rc < 0) - goto out_2; - - rc = 0; - - cifsi->server_eof = i_size_read(inode) - len; - truncate_setsize(inode, cifsi->server_eof); - fscache_resize_cookie(cifs_inode_cookie(inode), cifsi->server_eof); -out_2: - filemap_invalidate_unlock(inode->i_mapping); - out: - inode_unlock(inode); - free_xid(xid); - return rc; -} - -static long smb3_insert_range(struct file *file, struct cifs_tcon *tcon, - loff_t off, loff_t len) -{ - int rc; - unsigned int xid; - struct cifsFileInfo *cfile = file->private_data; - struct inode *inode = file_inode(file); - __le64 eof; - __u64 count, old_eof; - - xid = get_xid(); - - inode_lock(inode); - - old_eof = i_size_read(inode); - if (off >= old_eof) { - rc = -EINVAL; - goto out; - } - - count = old_eof - off; - eof = cpu_to_le64(old_eof + len); - - filemap_invalidate_lock(inode->i_mapping); - rc = filemap_write_and_wait_range(inode->i_mapping, off, old_eof + len - 1); - if (rc < 0) - goto out_2; - truncate_pagecache_range(inode, off, old_eof); - - rc = SMB2_set_eof(xid, tcon, cfile->fid.persistent_fid, - cfile->fid.volatile_fid, cfile->pid, &eof); - if (rc < 0) - goto out_2; - - rc = smb2_copychunk_range(xid, cfile, cfile, off, count, off + len); - if (rc < 0) - goto out_2; - - rc = smb3_zero_data(file, tcon, off, len, xid); - if (rc < 0) - goto out_2; - - rc = 0; -out_2: - filemap_invalidate_unlock(inode->i_mapping); - out: - inode_unlock(inode); - free_xid(xid); - return rc; -} - -static loff_t smb3_llseek(struct file *file, struct cifs_tcon *tcon, loff_t offset, int whence) -{ - struct cifsFileInfo *wrcfile, *cfile = file->private_data; - struct cifsInodeInfo *cifsi; - struct inode *inode; - int rc = 0; - struct file_allocated_range_buffer in_data, *out_data = NULL; - u32 out_data_len; - unsigned int xid; - - if (whence != SEEK_HOLE && whence != SEEK_DATA) - return generic_file_llseek(file, offset, whence); - - inode = d_inode(cfile->dentry); - cifsi = CIFS_I(inode); - - if (offset < 0 || offset >= i_size_read(inode)) - return -ENXIO; - - xid = get_xid(); - /* - * We need to be sure that all dirty pages are written as they - * might fill holes on the server. - * Note that we also MUST flush any written pages since at least - * some servers (Windows2016) will not reflect recent writes in - * QUERY_ALLOCATED_RANGES until SMB2_flush is called. - */ - wrcfile = find_writable_file(cifsi, FIND_WR_ANY); - if (wrcfile) { - filemap_write_and_wait(inode->i_mapping); - smb2_flush_file(xid, tcon, &wrcfile->fid); - cifsFileInfo_put(wrcfile); - } - - if (!(cifsi->cifsAttrs & FILE_ATTRIBUTE_SPARSE_FILE)) { - if (whence == SEEK_HOLE) - offset = i_size_read(inode); - goto lseek_exit; - } - - in_data.file_offset = cpu_to_le64(offset); - in_data.length = cpu_to_le64(i_size_read(inode)); - - rc = SMB2_ioctl(xid, tcon, cfile->fid.persistent_fid, - cfile->fid.volatile_fid, - FSCTL_QUERY_ALLOCATED_RANGES, - (char *)&in_data, sizeof(in_data), - sizeof(struct file_allocated_range_buffer), - (char **)&out_data, &out_data_len); - if (rc == -E2BIG) - rc = 0; - if (rc) - goto lseek_exit; - - if (whence == SEEK_HOLE && out_data_len == 0) - goto lseek_exit; - - if (whence == SEEK_DATA && out_data_len == 0) { - rc = -ENXIO; - goto lseek_exit; - } - - if (out_data_len < sizeof(struct file_allocated_range_buffer)) { - rc = -EINVAL; - goto lseek_exit; - } - if (whence == SEEK_DATA) { - offset = le64_to_cpu(out_data->file_offset); - goto lseek_exit; - } - if (offset < le64_to_cpu(out_data->file_offset)) - goto lseek_exit; - - offset = le64_to_cpu(out_data->file_offset) + le64_to_cpu(out_data->length); - - lseek_exit: - free_xid(xid); - kfree(out_data); - if (!rc) - return vfs_setpos(file, offset, inode->i_sb->s_maxbytes); - else - return rc; -} - -static int smb3_fiemap(struct cifs_tcon *tcon, - struct cifsFileInfo *cfile, - struct fiemap_extent_info *fei, u64 start, u64 len) -{ - unsigned int xid; - struct file_allocated_range_buffer in_data, *out_data; - u32 out_data_len; - int i, num, rc, flags, last_blob; - u64 next; - - rc = fiemap_prep(d_inode(cfile->dentry), fei, start, &len, 0); - if (rc) - return rc; - - xid = get_xid(); - again: - in_data.file_offset = cpu_to_le64(start); - in_data.length = cpu_to_le64(len); - - rc = SMB2_ioctl(xid, tcon, cfile->fid.persistent_fid, - cfile->fid.volatile_fid, - FSCTL_QUERY_ALLOCATED_RANGES, - (char *)&in_data, sizeof(in_data), - 1024 * sizeof(struct file_allocated_range_buffer), - (char **)&out_data, &out_data_len); - if (rc == -E2BIG) { - last_blob = 0; - rc = 0; - } else - last_blob = 1; - if (rc) - goto out; - - if (out_data_len && out_data_len < sizeof(struct file_allocated_range_buffer)) { - rc = -EINVAL; - goto out; - } - if (out_data_len % sizeof(struct file_allocated_range_buffer)) { - rc = -EINVAL; - goto out; - } - - num = out_data_len / sizeof(struct file_allocated_range_buffer); - for (i = 0; i < num; i++) { - flags = 0; - if (i == num - 1 && last_blob) - flags |= FIEMAP_EXTENT_LAST; - - rc = fiemap_fill_next_extent(fei, - le64_to_cpu(out_data[i].file_offset), - le64_to_cpu(out_data[i].file_offset), - le64_to_cpu(out_data[i].length), - flags); - if (rc < 0) - goto out; - if (rc == 1) { - rc = 0; - goto out; - } - } - - if (!last_blob) { - next = le64_to_cpu(out_data[num - 1].file_offset) + - le64_to_cpu(out_data[num - 1].length); - len = len - (next - start); - start = next; - goto again; - } - - out: - free_xid(xid); - kfree(out_data); - return rc; -} - -static long smb3_fallocate(struct file *file, struct cifs_tcon *tcon, int mode, - loff_t off, loff_t len) -{ - /* KEEP_SIZE already checked for by do_fallocate */ - if (mode & FALLOC_FL_PUNCH_HOLE) - return smb3_punch_hole(file, tcon, off, len); - else if (mode & FALLOC_FL_ZERO_RANGE) { - if (mode & FALLOC_FL_KEEP_SIZE) - return smb3_zero_range(file, tcon, off, len, true); - return smb3_zero_range(file, tcon, off, len, false); - } else if (mode == FALLOC_FL_KEEP_SIZE) - return smb3_simple_falloc(file, tcon, off, len, true); - else if (mode == FALLOC_FL_COLLAPSE_RANGE) - return smb3_collapse_range(file, tcon, off, len); - else if (mode == FALLOC_FL_INSERT_RANGE) - return smb3_insert_range(file, tcon, off, len); - else if (mode == 0) - return smb3_simple_falloc(file, tcon, off, len, false); - - return -EOPNOTSUPP; -} - -static void -smb2_downgrade_oplock(struct TCP_Server_Info *server, - struct cifsInodeInfo *cinode, __u32 oplock, - unsigned int epoch, bool *purge_cache) -{ - server->ops->set_oplock_level(cinode, oplock, 0, NULL); -} - -static void -smb21_set_oplock_level(struct cifsInodeInfo *cinode, __u32 oplock, - unsigned int epoch, bool *purge_cache); - -static void -smb3_downgrade_oplock(struct TCP_Server_Info *server, - struct cifsInodeInfo *cinode, __u32 oplock, - unsigned int epoch, bool *purge_cache) -{ - unsigned int old_state = cinode->oplock; - unsigned int old_epoch = cinode->epoch; - unsigned int new_state; - - if (epoch > old_epoch) { - smb21_set_oplock_level(cinode, oplock, 0, NULL); - cinode->epoch = epoch; - } - - new_state = cinode->oplock; - *purge_cache = false; - - if ((old_state & CIFS_CACHE_READ_FLG) != 0 && - (new_state & CIFS_CACHE_READ_FLG) == 0) - *purge_cache = true; - else if (old_state == new_state && (epoch - old_epoch > 1)) - *purge_cache = true; -} - -static void -smb2_set_oplock_level(struct cifsInodeInfo *cinode, __u32 oplock, - unsigned int epoch, bool *purge_cache) -{ - oplock &= 0xFF; - cinode->lease_granted = false; - if (oplock == SMB2_OPLOCK_LEVEL_NOCHANGE) - return; - if (oplock == SMB2_OPLOCK_LEVEL_BATCH) { - cinode->oplock = CIFS_CACHE_RHW_FLG; - cifs_dbg(FYI, "Batch Oplock granted on inode %p\n", - &cinode->netfs.inode); - } else if (oplock == SMB2_OPLOCK_LEVEL_EXCLUSIVE) { - cinode->oplock = CIFS_CACHE_RW_FLG; - cifs_dbg(FYI, "Exclusive Oplock granted on inode %p\n", - &cinode->netfs.inode); - } else if (oplock == SMB2_OPLOCK_LEVEL_II) { - cinode->oplock = CIFS_CACHE_READ_FLG; - cifs_dbg(FYI, "Level II Oplock granted on inode %p\n", - &cinode->netfs.inode); - } else - cinode->oplock = 0; -} - -static void -smb21_set_oplock_level(struct cifsInodeInfo *cinode, __u32 oplock, - unsigned int epoch, bool *purge_cache) -{ - char message[5] = {0}; - unsigned int new_oplock = 0; - - oplock &= 0xFF; - cinode->lease_granted = true; - if (oplock == SMB2_OPLOCK_LEVEL_NOCHANGE) - return; - - /* Check if the server granted an oplock rather than a lease */ - if (oplock & SMB2_OPLOCK_LEVEL_EXCLUSIVE) - return smb2_set_oplock_level(cinode, oplock, epoch, - purge_cache); - - if (oplock & SMB2_LEASE_READ_CACHING_HE) { - new_oplock |= CIFS_CACHE_READ_FLG; - strcat(message, "R"); - } - if (oplock & SMB2_LEASE_HANDLE_CACHING_HE) { - new_oplock |= CIFS_CACHE_HANDLE_FLG; - strcat(message, "H"); - } - if (oplock & SMB2_LEASE_WRITE_CACHING_HE) { - new_oplock |= CIFS_CACHE_WRITE_FLG; - strcat(message, "W"); - } - if (!new_oplock) - strncpy(message, "None", sizeof(message)); - - cinode->oplock = new_oplock; - cifs_dbg(FYI, "%s Lease granted on inode %p\n", message, - &cinode->netfs.inode); -} - -static void -smb3_set_oplock_level(struct cifsInodeInfo *cinode, __u32 oplock, - unsigned int epoch, bool *purge_cache) -{ - unsigned int old_oplock = cinode->oplock; - - smb21_set_oplock_level(cinode, oplock, epoch, purge_cache); - - if (purge_cache) { - *purge_cache = false; - if (old_oplock == CIFS_CACHE_READ_FLG) { - if (cinode->oplock == CIFS_CACHE_READ_FLG && - (epoch - cinode->epoch > 0)) - *purge_cache = true; - else if (cinode->oplock == CIFS_CACHE_RH_FLG && - (epoch - cinode->epoch > 1)) - *purge_cache = true; - else if (cinode->oplock == CIFS_CACHE_RHW_FLG && - (epoch - cinode->epoch > 1)) - *purge_cache = true; - else if (cinode->oplock == 0 && - (epoch - cinode->epoch > 0)) - *purge_cache = true; - } else if (old_oplock == CIFS_CACHE_RH_FLG) { - if (cinode->oplock == CIFS_CACHE_RH_FLG && - (epoch - cinode->epoch > 0)) - *purge_cache = true; - else if (cinode->oplock == CIFS_CACHE_RHW_FLG && - (epoch - cinode->epoch > 1)) - *purge_cache = true; - } - cinode->epoch = epoch; - } -} - -#ifdef CONFIG_CIFS_ALLOW_INSECURE_LEGACY -static bool -smb2_is_read_op(__u32 oplock) -{ - return oplock == SMB2_OPLOCK_LEVEL_II; -} -#endif /* CIFS_ALLOW_INSECURE_LEGACY */ - -static bool -smb21_is_read_op(__u32 oplock) -{ - return (oplock & SMB2_LEASE_READ_CACHING_HE) && - !(oplock & SMB2_LEASE_WRITE_CACHING_HE); -} - -static __le32 -map_oplock_to_lease(u8 oplock) -{ - if (oplock == SMB2_OPLOCK_LEVEL_EXCLUSIVE) - return SMB2_LEASE_WRITE_CACHING_LE | SMB2_LEASE_READ_CACHING_LE; - else if (oplock == SMB2_OPLOCK_LEVEL_II) - return SMB2_LEASE_READ_CACHING_LE; - else if (oplock == SMB2_OPLOCK_LEVEL_BATCH) - return SMB2_LEASE_HANDLE_CACHING_LE | SMB2_LEASE_READ_CACHING_LE | - SMB2_LEASE_WRITE_CACHING_LE; - return 0; -} - -static char * -smb2_create_lease_buf(u8 *lease_key, u8 oplock) -{ - struct create_lease *buf; - - buf = kzalloc(sizeof(struct create_lease), GFP_KERNEL); - if (!buf) - return NULL; - - memcpy(&buf->lcontext.LeaseKey, lease_key, SMB2_LEASE_KEY_SIZE); - buf->lcontext.LeaseState = map_oplock_to_lease(oplock); - - buf->ccontext.DataOffset = cpu_to_le16(offsetof - (struct create_lease, lcontext)); - buf->ccontext.DataLength = cpu_to_le32(sizeof(struct lease_context)); - buf->ccontext.NameOffset = cpu_to_le16(offsetof - (struct create_lease, Name)); - buf->ccontext.NameLength = cpu_to_le16(4); - /* SMB2_CREATE_REQUEST_LEASE is "RqLs" */ - buf->Name[0] = 'R'; - buf->Name[1] = 'q'; - buf->Name[2] = 'L'; - buf->Name[3] = 's'; - return (char *)buf; -} - -static char * -smb3_create_lease_buf(u8 *lease_key, u8 oplock) -{ - struct create_lease_v2 *buf; - - buf = kzalloc(sizeof(struct create_lease_v2), GFP_KERNEL); - if (!buf) - return NULL; - - memcpy(&buf->lcontext.LeaseKey, lease_key, SMB2_LEASE_KEY_SIZE); - buf->lcontext.LeaseState = map_oplock_to_lease(oplock); - - buf->ccontext.DataOffset = cpu_to_le16(offsetof - (struct create_lease_v2, lcontext)); - buf->ccontext.DataLength = cpu_to_le32(sizeof(struct lease_context_v2)); - buf->ccontext.NameOffset = cpu_to_le16(offsetof - (struct create_lease_v2, Name)); - buf->ccontext.NameLength = cpu_to_le16(4); - /* SMB2_CREATE_REQUEST_LEASE is "RqLs" */ - buf->Name[0] = 'R'; - buf->Name[1] = 'q'; - buf->Name[2] = 'L'; - buf->Name[3] = 's'; - return (char *)buf; -} - -static __u8 -smb2_parse_lease_buf(void *buf, unsigned int *epoch, char *lease_key) -{ - struct create_lease *lc = (struct create_lease *)buf; - - *epoch = 0; /* not used */ - if (lc->lcontext.LeaseFlags & SMB2_LEASE_FLAG_BREAK_IN_PROGRESS_LE) - return SMB2_OPLOCK_LEVEL_NOCHANGE; - return le32_to_cpu(lc->lcontext.LeaseState); -} - -static __u8 -smb3_parse_lease_buf(void *buf, unsigned int *epoch, char *lease_key) -{ - struct create_lease_v2 *lc = (struct create_lease_v2 *)buf; - - *epoch = le16_to_cpu(lc->lcontext.Epoch); - if (lc->lcontext.LeaseFlags & SMB2_LEASE_FLAG_BREAK_IN_PROGRESS_LE) - return SMB2_OPLOCK_LEVEL_NOCHANGE; - if (lease_key) - memcpy(lease_key, &lc->lcontext.LeaseKey, SMB2_LEASE_KEY_SIZE); - return le32_to_cpu(lc->lcontext.LeaseState); -} - -static unsigned int -smb2_wp_retry_size(struct inode *inode) -{ - return min_t(unsigned int, CIFS_SB(inode->i_sb)->ctx->wsize, - SMB2_MAX_BUFFER_SIZE); -} - -static bool -smb2_dir_needs_close(struct cifsFileInfo *cfile) -{ - return !cfile->invalidHandle; -} - -static void -fill_transform_hdr(struct smb2_transform_hdr *tr_hdr, unsigned int orig_len, - struct smb_rqst *old_rq, __le16 cipher_type) -{ - struct smb2_hdr *shdr = - (struct smb2_hdr *)old_rq->rq_iov[0].iov_base; - - memset(tr_hdr, 0, sizeof(struct smb2_transform_hdr)); - tr_hdr->ProtocolId = SMB2_TRANSFORM_PROTO_NUM; - tr_hdr->OriginalMessageSize = cpu_to_le32(orig_len); - tr_hdr->Flags = cpu_to_le16(0x01); - if ((cipher_type == SMB2_ENCRYPTION_AES128_GCM) || - (cipher_type == SMB2_ENCRYPTION_AES256_GCM)) - get_random_bytes(&tr_hdr->Nonce, SMB3_AES_GCM_NONCE); - else - get_random_bytes(&tr_hdr->Nonce, SMB3_AES_CCM_NONCE); - memcpy(&tr_hdr->SessionId, &shdr->SessionId, 8); -} - -static void *smb2_aead_req_alloc(struct crypto_aead *tfm, const struct smb_rqst *rqst, - int num_rqst, const u8 *sig, u8 **iv, - struct aead_request **req, struct scatterlist **sgl, - unsigned int *num_sgs) -{ - unsigned int req_size = sizeof(**req) + crypto_aead_reqsize(tfm); - unsigned int iv_size = crypto_aead_ivsize(tfm); - unsigned int len; - u8 *p; - - *num_sgs = cifs_get_num_sgs(rqst, num_rqst, sig); - - len = iv_size; - len += crypto_aead_alignmask(tfm) & ~(crypto_tfm_ctx_alignment() - 1); - len = ALIGN(len, crypto_tfm_ctx_alignment()); - len += req_size; - len = ALIGN(len, __alignof__(struct scatterlist)); - len += *num_sgs * sizeof(**sgl); - - p = kmalloc(len, GFP_ATOMIC); - if (!p) - return NULL; - - *iv = (u8 *)PTR_ALIGN(p, crypto_aead_alignmask(tfm) + 1); - *req = (struct aead_request *)PTR_ALIGN(*iv + iv_size, - crypto_tfm_ctx_alignment()); - *sgl = (struct scatterlist *)PTR_ALIGN((u8 *)*req + req_size, - __alignof__(struct scatterlist)); - return p; -} - -static void *smb2_get_aead_req(struct crypto_aead *tfm, const struct smb_rqst *rqst, - int num_rqst, const u8 *sig, u8 **iv, - struct aead_request **req, struct scatterlist **sgl) -{ - unsigned int off, len, skip; - struct scatterlist *sg; - unsigned int num_sgs; - unsigned long addr; - int i, j; - void *p; - - p = smb2_aead_req_alloc(tfm, rqst, num_rqst, sig, iv, req, sgl, &num_sgs); - if (!p) - return NULL; - - sg_init_table(*sgl, num_sgs); - sg = *sgl; - - /* Assumes the first rqst has a transform header as the first iov. - * I.e. - * rqst[0].rq_iov[0] is transform header - * rqst[0].rq_iov[1+] data to be encrypted/decrypted - * rqst[1+].rq_iov[0+] data to be encrypted/decrypted - */ - for (i = 0; i < num_rqst; i++) { - /* - * The first rqst has a transform header where the - * first 20 bytes are not part of the encrypted blob. - */ - for (j = 0; j < rqst[i].rq_nvec; j++) { - struct kvec *iov = &rqst[i].rq_iov[j]; - - skip = (i == 0) && (j == 0) ? 20 : 0; - addr = (unsigned long)iov->iov_base + skip; - len = iov->iov_len - skip; - sg = cifs_sg_set_buf(sg, (void *)addr, len); - } - for (j = 0; j < rqst[i].rq_npages; j++) { - rqst_page_get_length(&rqst[i], j, &len, &off); - sg_set_page(sg++, rqst[i].rq_pages[j], len, off); - } - } - cifs_sg_set_buf(sg, sig, SMB2_SIGNATURE_SIZE); - - return p; -} - -static int -smb2_get_enc_key(struct TCP_Server_Info *server, __u64 ses_id, int enc, u8 *key) -{ - struct TCP_Server_Info *pserver; - struct cifs_ses *ses; - u8 *ses_enc_key; - - /* If server is a channel, select the primary channel */ - pserver = CIFS_SERVER_IS_CHAN(server) ? server->primary_server : server; - - spin_lock(&cifs_tcp_ses_lock); - list_for_each_entry(ses, &pserver->smb_ses_list, smb_ses_list) { - if (ses->Suid == ses_id) { - spin_lock(&ses->ses_lock); - ses_enc_key = enc ? ses->smb3encryptionkey : - ses->smb3decryptionkey; - memcpy(key, ses_enc_key, SMB3_ENC_DEC_KEY_SIZE); - spin_unlock(&ses->ses_lock); - spin_unlock(&cifs_tcp_ses_lock); - return 0; - } - } - spin_unlock(&cifs_tcp_ses_lock); - - return -EAGAIN; -} -/* - * Encrypt or decrypt @rqst message. @rqst[0] has the following format: - * iov[0] - transform header (associate data), - * iov[1-N] - SMB2 header and pages - data to encrypt. - * On success return encrypted data in iov[1-N] and pages, leave iov[0] - * untouched. - */ -static int -crypt_message(struct TCP_Server_Info *server, int num_rqst, - struct smb_rqst *rqst, int enc) -{ - struct smb2_transform_hdr *tr_hdr = - (struct smb2_transform_hdr *)rqst[0].rq_iov[0].iov_base; - unsigned int assoc_data_len = sizeof(struct smb2_transform_hdr) - 20; - int rc = 0; - struct scatterlist *sg; - u8 sign[SMB2_SIGNATURE_SIZE] = {}; - u8 key[SMB3_ENC_DEC_KEY_SIZE]; - struct aead_request *req; - u8 *iv; - DECLARE_CRYPTO_WAIT(wait); - struct crypto_aead *tfm; - unsigned int crypt_len = le32_to_cpu(tr_hdr->OriginalMessageSize); - void *creq; - - rc = smb2_get_enc_key(server, le64_to_cpu(tr_hdr->SessionId), enc, key); - if (rc) { - cifs_server_dbg(VFS, "%s: Could not get %scryption key\n", __func__, - enc ? "en" : "de"); - return rc; - } - - rc = smb3_crypto_aead_allocate(server); - if (rc) { - cifs_server_dbg(VFS, "%s: crypto alloc failed\n", __func__); - return rc; - } - - tfm = enc ? server->secmech.enc : server->secmech.dec; - - if ((server->cipher_type == SMB2_ENCRYPTION_AES256_CCM) || - (server->cipher_type == SMB2_ENCRYPTION_AES256_GCM)) - rc = crypto_aead_setkey(tfm, key, SMB3_GCM256_CRYPTKEY_SIZE); - else - rc = crypto_aead_setkey(tfm, key, SMB3_GCM128_CRYPTKEY_SIZE); - - if (rc) { - cifs_server_dbg(VFS, "%s: Failed to set aead key %d\n", __func__, rc); - return rc; - } - - rc = crypto_aead_setauthsize(tfm, SMB2_SIGNATURE_SIZE); - if (rc) { - cifs_server_dbg(VFS, "%s: Failed to set authsize %d\n", __func__, rc); - return rc; - } - - creq = smb2_get_aead_req(tfm, rqst, num_rqst, sign, &iv, &req, &sg); - if (unlikely(!creq)) - return -ENOMEM; - - if (!enc) { - memcpy(sign, &tr_hdr->Signature, SMB2_SIGNATURE_SIZE); - crypt_len += SMB2_SIGNATURE_SIZE; - } - - if ((server->cipher_type == SMB2_ENCRYPTION_AES128_GCM) || - (server->cipher_type == SMB2_ENCRYPTION_AES256_GCM)) - memcpy(iv, (char *)tr_hdr->Nonce, SMB3_AES_GCM_NONCE); - else { - iv[0] = 3; - memcpy(iv + 1, (char *)tr_hdr->Nonce, SMB3_AES_CCM_NONCE); - } - - aead_request_set_tfm(req, tfm); - aead_request_set_crypt(req, sg, sg, crypt_len, iv); - aead_request_set_ad(req, assoc_data_len); - - aead_request_set_callback(req, CRYPTO_TFM_REQ_MAY_BACKLOG, - crypto_req_done, &wait); - - rc = crypto_wait_req(enc ? crypto_aead_encrypt(req) - : crypto_aead_decrypt(req), &wait); - - if (!rc && enc) - memcpy(&tr_hdr->Signature, sign, SMB2_SIGNATURE_SIZE); - - kfree_sensitive(creq); - return rc; -} - -void -smb3_free_compound_rqst(int num_rqst, struct smb_rqst *rqst) -{ - int i, j; - - for (i = 0; i < num_rqst; i++) { - if (rqst[i].rq_pages) { - for (j = rqst[i].rq_npages - 1; j >= 0; j--) - put_page(rqst[i].rq_pages[j]); - kfree(rqst[i].rq_pages); - } - } -} - -/* - * This function will initialize new_rq and encrypt the content. - * The first entry, new_rq[0], only contains a single iov which contains - * a smb2_transform_hdr and is pre-allocated by the caller. - * This function then populates new_rq[1+] with the content from olq_rq[0+]. - * - * The end result is an array of smb_rqst structures where the first structure - * only contains a single iov for the transform header which we then can pass - * to crypt_message(). - * - * new_rq[0].rq_iov[0] : smb2_transform_hdr pre-allocated by the caller - * new_rq[1+].rq_iov[*] == old_rq[0+].rq_iov[*] : SMB2/3 requests - */ -static int -smb3_init_transform_rq(struct TCP_Server_Info *server, int num_rqst, - struct smb_rqst *new_rq, struct smb_rqst *old_rq) -{ - struct page **pages; - struct smb2_transform_hdr *tr_hdr = new_rq[0].rq_iov[0].iov_base; - unsigned int npages; - unsigned int orig_len = 0; - int i, j; - int rc = -ENOMEM; - - for (i = 1; i < num_rqst; i++) { - npages = old_rq[i - 1].rq_npages; - pages = kmalloc_array(npages, sizeof(struct page *), - GFP_KERNEL); - if (!pages) - goto err_free; - - new_rq[i].rq_pages = pages; - new_rq[i].rq_npages = npages; - new_rq[i].rq_offset = old_rq[i - 1].rq_offset; - new_rq[i].rq_pagesz = old_rq[i - 1].rq_pagesz; - new_rq[i].rq_tailsz = old_rq[i - 1].rq_tailsz; - new_rq[i].rq_iov = old_rq[i - 1].rq_iov; - new_rq[i].rq_nvec = old_rq[i - 1].rq_nvec; - - orig_len += smb_rqst_len(server, &old_rq[i - 1]); - - for (j = 0; j < npages; j++) { - pages[j] = alloc_page(GFP_KERNEL|__GFP_HIGHMEM); - if (!pages[j]) - goto err_free; - } - - /* copy pages form the old */ - for (j = 0; j < npages; j++) { - char *dst, *src; - unsigned int offset, len; - - rqst_page_get_length(&new_rq[i], j, &len, &offset); - - dst = (char *) kmap(new_rq[i].rq_pages[j]) + offset; - src = (char *) kmap(old_rq[i - 1].rq_pages[j]) + offset; - - memcpy(dst, src, len); - kunmap(new_rq[i].rq_pages[j]); - kunmap(old_rq[i - 1].rq_pages[j]); - } - } - - /* fill the 1st iov with a transform header */ - fill_transform_hdr(tr_hdr, orig_len, old_rq, server->cipher_type); - - rc = crypt_message(server, num_rqst, new_rq, 1); - cifs_dbg(FYI, "Encrypt message returned %d\n", rc); - if (rc) - goto err_free; - - return rc; - -err_free: - smb3_free_compound_rqst(num_rqst - 1, &new_rq[1]); - return rc; -} - -static int -smb3_is_transform_hdr(void *buf) -{ - struct smb2_transform_hdr *trhdr = buf; - - return trhdr->ProtocolId == SMB2_TRANSFORM_PROTO_NUM; -} - -static int -decrypt_raw_data(struct TCP_Server_Info *server, char *buf, - unsigned int buf_data_size, struct page **pages, - unsigned int npages, unsigned int page_data_size, - bool is_offloaded) -{ - struct kvec iov[2]; - struct smb_rqst rqst = {NULL}; - int rc; - - iov[0].iov_base = buf; - iov[0].iov_len = sizeof(struct smb2_transform_hdr); - iov[1].iov_base = buf + sizeof(struct smb2_transform_hdr); - iov[1].iov_len = buf_data_size; - - rqst.rq_iov = iov; - rqst.rq_nvec = 2; - rqst.rq_pages = pages; - rqst.rq_npages = npages; - rqst.rq_pagesz = PAGE_SIZE; - rqst.rq_tailsz = (page_data_size % PAGE_SIZE) ? : PAGE_SIZE; - - rc = crypt_message(server, 1, &rqst, 0); - cifs_dbg(FYI, "Decrypt message returned %d\n", rc); - - if (rc) - return rc; - - memmove(buf, iov[1].iov_base, buf_data_size); - - if (!is_offloaded) - server->total_read = buf_data_size + page_data_size; - - return rc; -} - -static int -read_data_into_pages(struct TCP_Server_Info *server, struct page **pages, - unsigned int npages, unsigned int len) -{ - int i; - int length; - - for (i = 0; i < npages; i++) { - struct page *page = pages[i]; - size_t n; - - n = len; - if (len >= PAGE_SIZE) { - /* enough data to fill the page */ - n = PAGE_SIZE; - len -= n; - } else { - zero_user(page, len, PAGE_SIZE - len); - len = 0; - } - length = cifs_read_page_from_socket(server, page, 0, n); - if (length < 0) - return length; - server->total_read += length; - } - - return 0; -} - -static int -init_read_bvec(struct page **pages, unsigned int npages, unsigned int data_size, - unsigned int cur_off, struct bio_vec **page_vec) -{ - struct bio_vec *bvec; - int i; - - bvec = kcalloc(npages, sizeof(struct bio_vec), GFP_KERNEL); - if (!bvec) - return -ENOMEM; - - for (i = 0; i < npages; i++) { - bvec[i].bv_page = pages[i]; - bvec[i].bv_offset = (i == 0) ? cur_off : 0; - bvec[i].bv_len = min_t(unsigned int, PAGE_SIZE, data_size); - data_size -= bvec[i].bv_len; - } - - if (data_size != 0) { - cifs_dbg(VFS, "%s: something went wrong\n", __func__); - kfree(bvec); - return -EIO; - } - - *page_vec = bvec; - return 0; -} - -static int -handle_read_data(struct TCP_Server_Info *server, struct mid_q_entry *mid, - char *buf, unsigned int buf_len, struct page **pages, - unsigned int npages, unsigned int page_data_size, - bool is_offloaded) -{ - unsigned int data_offset; - unsigned int data_len; - unsigned int cur_off; - unsigned int cur_page_idx; - unsigned int pad_len; - struct cifs_readdata *rdata = mid->callback_data; - struct smb2_hdr *shdr = (struct smb2_hdr *)buf; - struct bio_vec *bvec = NULL; - struct iov_iter iter; - struct kvec iov; - int length; - bool use_rdma_mr = false; - - if (shdr->Command != SMB2_READ) { - cifs_server_dbg(VFS, "only big read responses are supported\n"); - return -ENOTSUPP; - } - - if (server->ops->is_session_expired && - server->ops->is_session_expired(buf)) { - if (!is_offloaded) - cifs_reconnect(server, true); - return -1; - } - - if (server->ops->is_status_pending && - server->ops->is_status_pending(buf, server)) - return -1; - - /* set up first two iov to get credits */ - rdata->iov[0].iov_base = buf; - rdata->iov[0].iov_len = 0; - rdata->iov[1].iov_base = buf; - rdata->iov[1].iov_len = - min_t(unsigned int, buf_len, server->vals->read_rsp_size); - cifs_dbg(FYI, "0: iov_base=%p iov_len=%zu\n", - rdata->iov[0].iov_base, rdata->iov[0].iov_len); - cifs_dbg(FYI, "1: iov_base=%p iov_len=%zu\n", - rdata->iov[1].iov_base, rdata->iov[1].iov_len); - - rdata->result = server->ops->map_error(buf, true); - if (rdata->result != 0) { - cifs_dbg(FYI, "%s: server returned error %d\n", - __func__, rdata->result); - /* normal error on read response */ - if (is_offloaded) - mid->mid_state = MID_RESPONSE_RECEIVED; - else - dequeue_mid(mid, false); - return 0; - } - - data_offset = server->ops->read_data_offset(buf); -#ifdef CONFIG_CIFS_SMB_DIRECT - use_rdma_mr = rdata->mr; -#endif - data_len = server->ops->read_data_length(buf, use_rdma_mr); - - if (data_offset < server->vals->read_rsp_size) { - /* - * win2k8 sometimes sends an offset of 0 when the read - * is beyond the EOF. Treat it as if the data starts just after - * the header. - */ - cifs_dbg(FYI, "%s: data offset (%u) inside read response header\n", - __func__, data_offset); - data_offset = server->vals->read_rsp_size; - } else if (data_offset > MAX_CIFS_SMALL_BUFFER_SIZE) { - /* data_offset is beyond the end of smallbuf */ - cifs_dbg(FYI, "%s: data offset (%u) beyond end of smallbuf\n", - __func__, data_offset); - rdata->result = -EIO; - if (is_offloaded) - mid->mid_state = MID_RESPONSE_MALFORMED; - else - dequeue_mid(mid, rdata->result); - return 0; - } - - pad_len = data_offset - server->vals->read_rsp_size; - - if (buf_len <= data_offset) { - /* read response payload is in pages */ - cur_page_idx = pad_len / PAGE_SIZE; - cur_off = pad_len % PAGE_SIZE; - - if (cur_page_idx != 0) { - /* data offset is beyond the 1st page of response */ - cifs_dbg(FYI, "%s: data offset (%u) beyond 1st page of response\n", - __func__, data_offset); - rdata->result = -EIO; - if (is_offloaded) - mid->mid_state = MID_RESPONSE_MALFORMED; - else - dequeue_mid(mid, rdata->result); - return 0; - } - - if (data_len > page_data_size - pad_len) { - /* data_len is corrupt -- discard frame */ - rdata->result = -EIO; - if (is_offloaded) - mid->mid_state = MID_RESPONSE_MALFORMED; - else - dequeue_mid(mid, rdata->result); - return 0; - } - - rdata->result = init_read_bvec(pages, npages, page_data_size, - cur_off, &bvec); - if (rdata->result != 0) { - if (is_offloaded) - mid->mid_state = MID_RESPONSE_MALFORMED; - else - dequeue_mid(mid, rdata->result); - return 0; - } - - iov_iter_bvec(&iter, ITER_SOURCE, bvec, npages, data_len); - } else if (buf_len >= data_offset + data_len) { - /* read response payload is in buf */ - WARN_ONCE(npages > 0, "read data can be either in buf or in pages"); - iov.iov_base = buf + data_offset; - iov.iov_len = data_len; - iov_iter_kvec(&iter, ITER_SOURCE, &iov, 1, data_len); - } else { - /* read response payload cannot be in both buf and pages */ - WARN_ONCE(1, "buf can not contain only a part of read data"); - rdata->result = -EIO; - if (is_offloaded) - mid->mid_state = MID_RESPONSE_MALFORMED; - else - dequeue_mid(mid, rdata->result); - return 0; - } - - length = rdata->copy_into_pages(server, rdata, &iter); - - kfree(bvec); - - if (length < 0) - return length; - - if (is_offloaded) - mid->mid_state = MID_RESPONSE_RECEIVED; - else - dequeue_mid(mid, false); - return length; -} - -struct smb2_decrypt_work { - struct work_struct decrypt; - struct TCP_Server_Info *server; - struct page **ppages; - char *buf; - unsigned int npages; - unsigned int len; -}; - - -static void smb2_decrypt_offload(struct work_struct *work) -{ - struct smb2_decrypt_work *dw = container_of(work, - struct smb2_decrypt_work, decrypt); - int i, rc; - struct mid_q_entry *mid; - - rc = decrypt_raw_data(dw->server, dw->buf, dw->server->vals->read_rsp_size, - dw->ppages, dw->npages, dw->len, true); - if (rc) { - cifs_dbg(VFS, "error decrypting rc=%d\n", rc); - goto free_pages; - } - - dw->server->lstrp = jiffies; - mid = smb2_find_dequeue_mid(dw->server, dw->buf); - if (mid == NULL) - cifs_dbg(FYI, "mid not found\n"); - else { - mid->decrypted = true; - rc = handle_read_data(dw->server, mid, dw->buf, - dw->server->vals->read_rsp_size, - dw->ppages, dw->npages, dw->len, - true); - if (rc >= 0) { -#ifdef CONFIG_CIFS_STATS2 - mid->when_received = jiffies; -#endif - if (dw->server->ops->is_network_name_deleted) - dw->server->ops->is_network_name_deleted(dw->buf, - dw->server); - - mid->callback(mid); - } else { - spin_lock(&dw->server->srv_lock); - if (dw->server->tcpStatus == CifsNeedReconnect) { - spin_lock(&dw->server->mid_lock); - mid->mid_state = MID_RETRY_NEEDED; - spin_unlock(&dw->server->mid_lock); - spin_unlock(&dw->server->srv_lock); - mid->callback(mid); - } else { - spin_lock(&dw->server->mid_lock); - mid->mid_state = MID_REQUEST_SUBMITTED; - mid->mid_flags &= ~(MID_DELETED); - list_add_tail(&mid->qhead, - &dw->server->pending_mid_q); - spin_unlock(&dw->server->mid_lock); - spin_unlock(&dw->server->srv_lock); - } - } - release_mid(mid); - } - -free_pages: - for (i = dw->npages-1; i >= 0; i--) - put_page(dw->ppages[i]); - - kfree(dw->ppages); - cifs_small_buf_release(dw->buf); - kfree(dw); -} - - -static int -receive_encrypted_read(struct TCP_Server_Info *server, struct mid_q_entry **mid, - int *num_mids) -{ - char *buf = server->smallbuf; - struct smb2_transform_hdr *tr_hdr = (struct smb2_transform_hdr *)buf; - unsigned int npages; - struct page **pages; - unsigned int len; - unsigned int buflen = server->pdu_size; - int rc; - int i = 0; - struct smb2_decrypt_work *dw; - - *num_mids = 1; - len = min_t(unsigned int, buflen, server->vals->read_rsp_size + - sizeof(struct smb2_transform_hdr)) - HEADER_SIZE(server) + 1; - - rc = cifs_read_from_socket(server, buf + HEADER_SIZE(server) - 1, len); - if (rc < 0) - return rc; - server->total_read += rc; - - len = le32_to_cpu(tr_hdr->OriginalMessageSize) - - server->vals->read_rsp_size; - npages = DIV_ROUND_UP(len, PAGE_SIZE); - - pages = kmalloc_array(npages, sizeof(struct page *), GFP_KERNEL); - if (!pages) { - rc = -ENOMEM; - goto discard_data; - } - - for (; i < npages; i++) { - pages[i] = alloc_page(GFP_KERNEL|__GFP_HIGHMEM); - if (!pages[i]) { - rc = -ENOMEM; - goto discard_data; - } - } - - /* read read data into pages */ - rc = read_data_into_pages(server, pages, npages, len); - if (rc) - goto free_pages; - - rc = cifs_discard_remaining_data(server); - if (rc) - goto free_pages; - - /* - * For large reads, offload to different thread for better performance, - * use more cores decrypting which can be expensive - */ - - if ((server->min_offload) && (server->in_flight > 1) && - (server->pdu_size >= server->min_offload)) { - dw = kmalloc(sizeof(struct smb2_decrypt_work), GFP_KERNEL); - if (dw == NULL) - goto non_offloaded_decrypt; - - dw->buf = server->smallbuf; - server->smallbuf = (char *)cifs_small_buf_get(); - - INIT_WORK(&dw->decrypt, smb2_decrypt_offload); - - dw->npages = npages; - dw->server = server; - dw->ppages = pages; - dw->len = len; - queue_work(decrypt_wq, &dw->decrypt); - *num_mids = 0; /* worker thread takes care of finding mid */ - return -1; - } - -non_offloaded_decrypt: - rc = decrypt_raw_data(server, buf, server->vals->read_rsp_size, - pages, npages, len, false); - if (rc) - goto free_pages; - - *mid = smb2_find_mid(server, buf); - if (*mid == NULL) - cifs_dbg(FYI, "mid not found\n"); - else { - cifs_dbg(FYI, "mid found\n"); - (*mid)->decrypted = true; - rc = handle_read_data(server, *mid, buf, - server->vals->read_rsp_size, - pages, npages, len, false); - if (rc >= 0) { - if (server->ops->is_network_name_deleted) { - server->ops->is_network_name_deleted(buf, - server); - } - } - } - -free_pages: - for (i = i - 1; i >= 0; i--) - put_page(pages[i]); - kfree(pages); - return rc; -discard_data: - cifs_discard_remaining_data(server); - goto free_pages; -} - -static int -receive_encrypted_standard(struct TCP_Server_Info *server, - struct mid_q_entry **mids, char **bufs, - int *num_mids) -{ - int ret, length; - char *buf = server->smallbuf; - struct smb2_hdr *shdr; - unsigned int pdu_length = server->pdu_size; - unsigned int buf_size; - struct mid_q_entry *mid_entry; - int next_is_large; - char *next_buffer = NULL; - - *num_mids = 0; - - /* switch to large buffer if too big for a small one */ - if (pdu_length > MAX_CIFS_SMALL_BUFFER_SIZE) { - server->large_buf = true; - memcpy(server->bigbuf, buf, server->total_read); - buf = server->bigbuf; - } - - /* now read the rest */ - length = cifs_read_from_socket(server, buf + HEADER_SIZE(server) - 1, - pdu_length - HEADER_SIZE(server) + 1); - if (length < 0) - return length; - server->total_read += length; - - buf_size = pdu_length - sizeof(struct smb2_transform_hdr); - length = decrypt_raw_data(server, buf, buf_size, NULL, 0, 0, false); - if (length) - return length; - - next_is_large = server->large_buf; -one_more: - shdr = (struct smb2_hdr *)buf; - if (shdr->NextCommand) { - if (next_is_large) - next_buffer = (char *)cifs_buf_get(); - else - next_buffer = (char *)cifs_small_buf_get(); - memcpy(next_buffer, - buf + le32_to_cpu(shdr->NextCommand), - pdu_length - le32_to_cpu(shdr->NextCommand)); - } - - mid_entry = smb2_find_mid(server, buf); - if (mid_entry == NULL) - cifs_dbg(FYI, "mid not found\n"); - else { - cifs_dbg(FYI, "mid found\n"); - mid_entry->decrypted = true; - mid_entry->resp_buf_size = server->pdu_size; - } - - if (*num_mids >= MAX_COMPOUND) { - cifs_server_dbg(VFS, "too many PDUs in compound\n"); - return -1; - } - bufs[*num_mids] = buf; - mids[(*num_mids)++] = mid_entry; - - if (mid_entry && mid_entry->handle) - ret = mid_entry->handle(server, mid_entry); - else - ret = cifs_handle_standard(server, mid_entry); - - if (ret == 0 && shdr->NextCommand) { - pdu_length -= le32_to_cpu(shdr->NextCommand); - server->large_buf = next_is_large; - if (next_is_large) - server->bigbuf = buf = next_buffer; - else - server->smallbuf = buf = next_buffer; - goto one_more; - } else if (ret != 0) { - /* - * ret != 0 here means that we didn't get to handle_mid() thus - * server->smallbuf and server->bigbuf are still valid. We need - * to free next_buffer because it is not going to be used - * anywhere. - */ - if (next_is_large) - free_rsp_buf(CIFS_LARGE_BUFFER, next_buffer); - else - free_rsp_buf(CIFS_SMALL_BUFFER, next_buffer); - } - - return ret; -} - -static int -smb3_receive_transform(struct TCP_Server_Info *server, - struct mid_q_entry **mids, char **bufs, int *num_mids) -{ - char *buf = server->smallbuf; - unsigned int pdu_length = server->pdu_size; - struct smb2_transform_hdr *tr_hdr = (struct smb2_transform_hdr *)buf; - unsigned int orig_len = le32_to_cpu(tr_hdr->OriginalMessageSize); - - if (pdu_length < sizeof(struct smb2_transform_hdr) + - sizeof(struct smb2_hdr)) { - cifs_server_dbg(VFS, "Transform message is too small (%u)\n", - pdu_length); - cifs_reconnect(server, true); - return -ECONNABORTED; - } - - if (pdu_length < orig_len + sizeof(struct smb2_transform_hdr)) { - cifs_server_dbg(VFS, "Transform message is broken\n"); - cifs_reconnect(server, true); - return -ECONNABORTED; - } - - /* TODO: add support for compounds containing READ. */ - if (pdu_length > CIFSMaxBufSize + MAX_HEADER_SIZE(server)) { - return receive_encrypted_read(server, &mids[0], num_mids); - } - - return receive_encrypted_standard(server, mids, bufs, num_mids); -} - -int -smb3_handle_read_data(struct TCP_Server_Info *server, struct mid_q_entry *mid) -{ - char *buf = server->large_buf ? server->bigbuf : server->smallbuf; - - return handle_read_data(server, mid, buf, server->pdu_size, - NULL, 0, 0, false); -} - -static int -smb2_next_header(char *buf) -{ - struct smb2_hdr *hdr = (struct smb2_hdr *)buf; - struct smb2_transform_hdr *t_hdr = (struct smb2_transform_hdr *)buf; - - if (hdr->ProtocolId == SMB2_TRANSFORM_PROTO_NUM) - return sizeof(struct smb2_transform_hdr) + - le32_to_cpu(t_hdr->OriginalMessageSize); - - return le32_to_cpu(hdr->NextCommand); -} - -static int -smb2_make_node(unsigned int xid, struct inode *inode, - struct dentry *dentry, struct cifs_tcon *tcon, - const char *full_path, umode_t mode, dev_t dev) -{ - struct cifs_sb_info *cifs_sb = CIFS_SB(inode->i_sb); - int rc = -EPERM; - struct cifs_open_info_data buf = {}; - struct cifs_io_parms io_parms = {0}; - __u32 oplock = 0; - struct cifs_fid fid; - struct cifs_open_parms oparms; - unsigned int bytes_written; - struct win_dev *pdev; - struct kvec iov[2]; - - /* - * Check if mounted with mount parm 'sfu' mount parm. - * SFU emulation should work with all servers, but only - * supports block and char device (no socket & fifo), - * and was used by default in earlier versions of Windows - */ - if (!(cifs_sb->mnt_cifs_flags & CIFS_MOUNT_UNX_EMUL)) - return rc; - - /* - * TODO: Add ability to create instead via reparse point. Windows (e.g. - * their current NFS server) uses this approach to expose special files - * over SMB2/SMB3 and Samba will do this with SMB3.1.1 POSIX Extensions - */ - - if (!S_ISCHR(mode) && !S_ISBLK(mode)) - return rc; - - cifs_dbg(FYI, "sfu compat create special file\n"); - - oparms = (struct cifs_open_parms) { - .tcon = tcon, - .cifs_sb = cifs_sb, - .desired_access = GENERIC_WRITE, - .create_options = cifs_create_options(cifs_sb, CREATE_NOT_DIR | - CREATE_OPTION_SPECIAL), - .disposition = FILE_CREATE, - .path = full_path, - .fid = &fid, - }; - - if (tcon->ses->server->oplocks) - oplock = REQ_OPLOCK; - else - oplock = 0; - rc = tcon->ses->server->ops->open(xid, &oparms, &oplock, &buf); - if (rc) - return rc; - - /* - * BB Do not bother to decode buf since no local inode yet to put - * timestamps in, but we can reuse it safely. - */ - - pdev = (struct win_dev *)&buf.fi; - io_parms.pid = current->tgid; - io_parms.tcon = tcon; - io_parms.offset = 0; - io_parms.length = sizeof(struct win_dev); - iov[1].iov_base = &buf.fi; - iov[1].iov_len = sizeof(struct win_dev); - if (S_ISCHR(mode)) { - memcpy(pdev->type, "IntxCHR", 8); - pdev->major = cpu_to_le64(MAJOR(dev)); - pdev->minor = cpu_to_le64(MINOR(dev)); - rc = tcon->ses->server->ops->sync_write(xid, &fid, &io_parms, - &bytes_written, iov, 1); - } else if (S_ISBLK(mode)) { - memcpy(pdev->type, "IntxBLK", 8); - pdev->major = cpu_to_le64(MAJOR(dev)); - pdev->minor = cpu_to_le64(MINOR(dev)); - rc = tcon->ses->server->ops->sync_write(xid, &fid, &io_parms, - &bytes_written, iov, 1); - } - tcon->ses->server->ops->close(xid, tcon, &fid); - d_drop(dentry); - - /* FIXME: add code here to set EAs */ - - cifs_free_open_info(&buf); - return rc; -} - -#ifdef CONFIG_CIFS_ALLOW_INSECURE_LEGACY -struct smb_version_operations smb20_operations = { - .compare_fids = smb2_compare_fids, - .setup_request = smb2_setup_request, - .setup_async_request = smb2_setup_async_request, - .check_receive = smb2_check_receive, - .add_credits = smb2_add_credits, - .set_credits = smb2_set_credits, - .get_credits_field = smb2_get_credits_field, - .get_credits = smb2_get_credits, - .wait_mtu_credits = cifs_wait_mtu_credits, - .get_next_mid = smb2_get_next_mid, - .revert_current_mid = smb2_revert_current_mid, - .read_data_offset = smb2_read_data_offset, - .read_data_length = smb2_read_data_length, - .map_error = map_smb2_to_linux_error, - .find_mid = smb2_find_mid, - .check_message = smb2_check_message, - .dump_detail = smb2_dump_detail, - .clear_stats = smb2_clear_stats, - .print_stats = smb2_print_stats, - .is_oplock_break = smb2_is_valid_oplock_break, - .handle_cancelled_mid = smb2_handle_cancelled_mid, - .downgrade_oplock = smb2_downgrade_oplock, - .need_neg = smb2_need_neg, - .negotiate = smb2_negotiate, - .negotiate_wsize = smb2_negotiate_wsize, - .negotiate_rsize = smb2_negotiate_rsize, - .sess_setup = SMB2_sess_setup, - .logoff = SMB2_logoff, - .tree_connect = SMB2_tcon, - .tree_disconnect = SMB2_tdis, - .qfs_tcon = smb2_qfs_tcon, - .is_path_accessible = smb2_is_path_accessible, - .can_echo = smb2_can_echo, - .echo = SMB2_echo, - .query_path_info = smb2_query_path_info, - .get_srv_inum = smb2_get_srv_inum, - .query_file_info = smb2_query_file_info, - .set_path_size = smb2_set_path_size, - .set_file_size = smb2_set_file_size, - .set_file_info = smb2_set_file_info, - .set_compression = smb2_set_compression, - .mkdir = smb2_mkdir, - .mkdir_setinfo = smb2_mkdir_setinfo, - .rmdir = smb2_rmdir, - .unlink = smb2_unlink, - .rename = smb2_rename_path, - .create_hardlink = smb2_create_hardlink, - .query_symlink = smb2_query_symlink, - .query_mf_symlink = smb3_query_mf_symlink, - .create_mf_symlink = smb3_create_mf_symlink, - .open = smb2_open_file, - .set_fid = smb2_set_fid, - .close = smb2_close_file, - .flush = smb2_flush_file, - .async_readv = smb2_async_readv, - .async_writev = smb2_async_writev, - .sync_read = smb2_sync_read, - .sync_write = smb2_sync_write, - .query_dir_first = smb2_query_dir_first, - .query_dir_next = smb2_query_dir_next, - .close_dir = smb2_close_dir, - .calc_smb_size = smb2_calc_size, - .is_status_pending = smb2_is_status_pending, - .is_session_expired = smb2_is_session_expired, - .oplock_response = smb2_oplock_response, - .queryfs = smb2_queryfs, - .mand_lock = smb2_mand_lock, - .mand_unlock_range = smb2_unlock_range, - .push_mand_locks = smb2_push_mandatory_locks, - .get_lease_key = smb2_get_lease_key, - .set_lease_key = smb2_set_lease_key, - .new_lease_key = smb2_new_lease_key, - .calc_signature = smb2_calc_signature, - .is_read_op = smb2_is_read_op, - .set_oplock_level = smb2_set_oplock_level, - .create_lease_buf = smb2_create_lease_buf, - .parse_lease_buf = smb2_parse_lease_buf, - .copychunk_range = smb2_copychunk_range, - .wp_retry_size = smb2_wp_retry_size, - .dir_needs_close = smb2_dir_needs_close, - .get_dfs_refer = smb2_get_dfs_refer, - .select_sectype = smb2_select_sectype, -#ifdef CONFIG_CIFS_XATTR - .query_all_EAs = smb2_query_eas, - .set_EA = smb2_set_ea, -#endif /* CIFS_XATTR */ - .get_acl = get_smb2_acl, - .get_acl_by_fid = get_smb2_acl_by_fid, - .set_acl = set_smb2_acl, - .next_header = smb2_next_header, - .ioctl_query_info = smb2_ioctl_query_info, - .make_node = smb2_make_node, - .fiemap = smb3_fiemap, - .llseek = smb3_llseek, - .is_status_io_timeout = smb2_is_status_io_timeout, - .is_network_name_deleted = smb2_is_network_name_deleted, -}; -#endif /* CIFS_ALLOW_INSECURE_LEGACY */ - -struct smb_version_operations smb21_operations = { - .compare_fids = smb2_compare_fids, - .setup_request = smb2_setup_request, - .setup_async_request = smb2_setup_async_request, - .check_receive = smb2_check_receive, - .add_credits = smb2_add_credits, - .set_credits = smb2_set_credits, - .get_credits_field = smb2_get_credits_field, - .get_credits = smb2_get_credits, - .wait_mtu_credits = smb2_wait_mtu_credits, - .adjust_credits = smb2_adjust_credits, - .get_next_mid = smb2_get_next_mid, - .revert_current_mid = smb2_revert_current_mid, - .read_data_offset = smb2_read_data_offset, - .read_data_length = smb2_read_data_length, - .map_error = map_smb2_to_linux_error, - .find_mid = smb2_find_mid, - .check_message = smb2_check_message, - .dump_detail = smb2_dump_detail, - .clear_stats = smb2_clear_stats, - .print_stats = smb2_print_stats, - .is_oplock_break = smb2_is_valid_oplock_break, - .handle_cancelled_mid = smb2_handle_cancelled_mid, - .downgrade_oplock = smb2_downgrade_oplock, - .need_neg = smb2_need_neg, - .negotiate = smb2_negotiate, - .negotiate_wsize = smb2_negotiate_wsize, - .negotiate_rsize = smb2_negotiate_rsize, - .sess_setup = SMB2_sess_setup, - .logoff = SMB2_logoff, - .tree_connect = SMB2_tcon, - .tree_disconnect = SMB2_tdis, - .qfs_tcon = smb2_qfs_tcon, - .is_path_accessible = smb2_is_path_accessible, - .can_echo = smb2_can_echo, - .echo = SMB2_echo, - .query_path_info = smb2_query_path_info, - .get_srv_inum = smb2_get_srv_inum, - .query_file_info = smb2_query_file_info, - .set_path_size = smb2_set_path_size, - .set_file_size = smb2_set_file_size, - .set_file_info = smb2_set_file_info, - .set_compression = smb2_set_compression, - .mkdir = smb2_mkdir, - .mkdir_setinfo = smb2_mkdir_setinfo, - .rmdir = smb2_rmdir, - .unlink = smb2_unlink, - .rename = smb2_rename_path, - .create_hardlink = smb2_create_hardlink, - .query_symlink = smb2_query_symlink, - .query_mf_symlink = smb3_query_mf_symlink, - .create_mf_symlink = smb3_create_mf_symlink, - .open = smb2_open_file, - .set_fid = smb2_set_fid, - .close = smb2_close_file, - .flush = smb2_flush_file, - .async_readv = smb2_async_readv, - .async_writev = smb2_async_writev, - .sync_read = smb2_sync_read, - .sync_write = smb2_sync_write, - .query_dir_first = smb2_query_dir_first, - .query_dir_next = smb2_query_dir_next, - .close_dir = smb2_close_dir, - .calc_smb_size = smb2_calc_size, - .is_status_pending = smb2_is_status_pending, - .is_session_expired = smb2_is_session_expired, - .oplock_response = smb2_oplock_response, - .queryfs = smb2_queryfs, - .mand_lock = smb2_mand_lock, - .mand_unlock_range = smb2_unlock_range, - .push_mand_locks = smb2_push_mandatory_locks, - .get_lease_key = smb2_get_lease_key, - .set_lease_key = smb2_set_lease_key, - .new_lease_key = smb2_new_lease_key, - .calc_signature = smb2_calc_signature, - .is_read_op = smb21_is_read_op, - .set_oplock_level = smb21_set_oplock_level, - .create_lease_buf = smb2_create_lease_buf, - .parse_lease_buf = smb2_parse_lease_buf, - .copychunk_range = smb2_copychunk_range, - .wp_retry_size = smb2_wp_retry_size, - .dir_needs_close = smb2_dir_needs_close, - .enum_snapshots = smb3_enum_snapshots, - .notify = smb3_notify, - .get_dfs_refer = smb2_get_dfs_refer, - .select_sectype = smb2_select_sectype, -#ifdef CONFIG_CIFS_XATTR - .query_all_EAs = smb2_query_eas, - .set_EA = smb2_set_ea, -#endif /* CIFS_XATTR */ - .get_acl = get_smb2_acl, - .get_acl_by_fid = get_smb2_acl_by_fid, - .set_acl = set_smb2_acl, - .next_header = smb2_next_header, - .ioctl_query_info = smb2_ioctl_query_info, - .make_node = smb2_make_node, - .fiemap = smb3_fiemap, - .llseek = smb3_llseek, - .is_status_io_timeout = smb2_is_status_io_timeout, - .is_network_name_deleted = smb2_is_network_name_deleted, -}; - -struct smb_version_operations smb30_operations = { - .compare_fids = smb2_compare_fids, - .setup_request = smb2_setup_request, - .setup_async_request = smb2_setup_async_request, - .check_receive = smb2_check_receive, - .add_credits = smb2_add_credits, - .set_credits = smb2_set_credits, - .get_credits_field = smb2_get_credits_field, - .get_credits = smb2_get_credits, - .wait_mtu_credits = smb2_wait_mtu_credits, - .adjust_credits = smb2_adjust_credits, - .get_next_mid = smb2_get_next_mid, - .revert_current_mid = smb2_revert_current_mid, - .read_data_offset = smb2_read_data_offset, - .read_data_length = smb2_read_data_length, - .map_error = map_smb2_to_linux_error, - .find_mid = smb2_find_mid, - .check_message = smb2_check_message, - .dump_detail = smb2_dump_detail, - .clear_stats = smb2_clear_stats, - .print_stats = smb2_print_stats, - .dump_share_caps = smb2_dump_share_caps, - .is_oplock_break = smb2_is_valid_oplock_break, - .handle_cancelled_mid = smb2_handle_cancelled_mid, - .downgrade_oplock = smb3_downgrade_oplock, - .need_neg = smb2_need_neg, - .negotiate = smb2_negotiate, - .negotiate_wsize = smb3_negotiate_wsize, - .negotiate_rsize = smb3_negotiate_rsize, - .sess_setup = SMB2_sess_setup, - .logoff = SMB2_logoff, - .tree_connect = SMB2_tcon, - .tree_disconnect = SMB2_tdis, - .qfs_tcon = smb3_qfs_tcon, - .is_path_accessible = smb2_is_path_accessible, - .can_echo = smb2_can_echo, - .echo = SMB2_echo, - .query_path_info = smb2_query_path_info, - /* WSL tags introduced long after smb2.1, enable for SMB3, 3.11 only */ - .query_reparse_tag = smb2_query_reparse_tag, - .get_srv_inum = smb2_get_srv_inum, - .query_file_info = smb2_query_file_info, - .set_path_size = smb2_set_path_size, - .set_file_size = smb2_set_file_size, - .set_file_info = smb2_set_file_info, - .set_compression = smb2_set_compression, - .mkdir = smb2_mkdir, - .mkdir_setinfo = smb2_mkdir_setinfo, - .rmdir = smb2_rmdir, - .unlink = smb2_unlink, - .rename = smb2_rename_path, - .create_hardlink = smb2_create_hardlink, - .query_symlink = smb2_query_symlink, - .query_mf_symlink = smb3_query_mf_symlink, - .create_mf_symlink = smb3_create_mf_symlink, - .open = smb2_open_file, - .set_fid = smb2_set_fid, - .close = smb2_close_file, - .close_getattr = smb2_close_getattr, - .flush = smb2_flush_file, - .async_readv = smb2_async_readv, - .async_writev = smb2_async_writev, - .sync_read = smb2_sync_read, - .sync_write = smb2_sync_write, - .query_dir_first = smb2_query_dir_first, - .query_dir_next = smb2_query_dir_next, - .close_dir = smb2_close_dir, - .calc_smb_size = smb2_calc_size, - .is_status_pending = smb2_is_status_pending, - .is_session_expired = smb2_is_session_expired, - .oplock_response = smb2_oplock_response, - .queryfs = smb2_queryfs, - .mand_lock = smb2_mand_lock, - .mand_unlock_range = smb2_unlock_range, - .push_mand_locks = smb2_push_mandatory_locks, - .get_lease_key = smb2_get_lease_key, - .set_lease_key = smb2_set_lease_key, - .new_lease_key = smb2_new_lease_key, - .generate_signingkey = generate_smb30signingkey, - .calc_signature = smb3_calc_signature, - .set_integrity = smb3_set_integrity, - .is_read_op = smb21_is_read_op, - .set_oplock_level = smb3_set_oplock_level, - .create_lease_buf = smb3_create_lease_buf, - .parse_lease_buf = smb3_parse_lease_buf, - .copychunk_range = smb2_copychunk_range, - .duplicate_extents = smb2_duplicate_extents, - .validate_negotiate = smb3_validate_negotiate, - .wp_retry_size = smb2_wp_retry_size, - .dir_needs_close = smb2_dir_needs_close, - .fallocate = smb3_fallocate, - .enum_snapshots = smb3_enum_snapshots, - .notify = smb3_notify, - .init_transform_rq = smb3_init_transform_rq, - .is_transform_hdr = smb3_is_transform_hdr, - .receive_transform = smb3_receive_transform, - .get_dfs_refer = smb2_get_dfs_refer, - .select_sectype = smb2_select_sectype, -#ifdef CONFIG_CIFS_XATTR - .query_all_EAs = smb2_query_eas, - .set_EA = smb2_set_ea, -#endif /* CIFS_XATTR */ - .get_acl = get_smb2_acl, - .get_acl_by_fid = get_smb2_acl_by_fid, - .set_acl = set_smb2_acl, - .next_header = smb2_next_header, - .ioctl_query_info = smb2_ioctl_query_info, - .make_node = smb2_make_node, - .fiemap = smb3_fiemap, - .llseek = smb3_llseek, - .is_status_io_timeout = smb2_is_status_io_timeout, - .is_network_name_deleted = smb2_is_network_name_deleted, -}; - -struct smb_version_operations smb311_operations = { - .compare_fids = smb2_compare_fids, - .setup_request = smb2_setup_request, - .setup_async_request = smb2_setup_async_request, - .check_receive = smb2_check_receive, - .add_credits = smb2_add_credits, - .set_credits = smb2_set_credits, - .get_credits_field = smb2_get_credits_field, - .get_credits = smb2_get_credits, - .wait_mtu_credits = smb2_wait_mtu_credits, - .adjust_credits = smb2_adjust_credits, - .get_next_mid = smb2_get_next_mid, - .revert_current_mid = smb2_revert_current_mid, - .read_data_offset = smb2_read_data_offset, - .read_data_length = smb2_read_data_length, - .map_error = map_smb2_to_linux_error, - .find_mid = smb2_find_mid, - .check_message = smb2_check_message, - .dump_detail = smb2_dump_detail, - .clear_stats = smb2_clear_stats, - .print_stats = smb2_print_stats, - .dump_share_caps = smb2_dump_share_caps, - .is_oplock_break = smb2_is_valid_oplock_break, - .handle_cancelled_mid = smb2_handle_cancelled_mid, - .downgrade_oplock = smb3_downgrade_oplock, - .need_neg = smb2_need_neg, - .negotiate = smb2_negotiate, - .negotiate_wsize = smb3_negotiate_wsize, - .negotiate_rsize = smb3_negotiate_rsize, - .sess_setup = SMB2_sess_setup, - .logoff = SMB2_logoff, - .tree_connect = SMB2_tcon, - .tree_disconnect = SMB2_tdis, - .qfs_tcon = smb3_qfs_tcon, - .is_path_accessible = smb2_is_path_accessible, - .can_echo = smb2_can_echo, - .echo = SMB2_echo, - .query_path_info = smb2_query_path_info, - .query_reparse_tag = smb2_query_reparse_tag, - .get_srv_inum = smb2_get_srv_inum, - .query_file_info = smb2_query_file_info, - .set_path_size = smb2_set_path_size, - .set_file_size = smb2_set_file_size, - .set_file_info = smb2_set_file_info, - .set_compression = smb2_set_compression, - .mkdir = smb2_mkdir, - .mkdir_setinfo = smb2_mkdir_setinfo, - .posix_mkdir = smb311_posix_mkdir, - .rmdir = smb2_rmdir, - .unlink = smb2_unlink, - .rename = smb2_rename_path, - .create_hardlink = smb2_create_hardlink, - .query_symlink = smb2_query_symlink, - .query_mf_symlink = smb3_query_mf_symlink, - .create_mf_symlink = smb3_create_mf_symlink, - .open = smb2_open_file, - .set_fid = smb2_set_fid, - .close = smb2_close_file, - .close_getattr = smb2_close_getattr, - .flush = smb2_flush_file, - .async_readv = smb2_async_readv, - .async_writev = smb2_async_writev, - .sync_read = smb2_sync_read, - .sync_write = smb2_sync_write, - .query_dir_first = smb2_query_dir_first, - .query_dir_next = smb2_query_dir_next, - .close_dir = smb2_close_dir, - .calc_smb_size = smb2_calc_size, - .is_status_pending = smb2_is_status_pending, - .is_session_expired = smb2_is_session_expired, - .oplock_response = smb2_oplock_response, - .queryfs = smb311_queryfs, - .mand_lock = smb2_mand_lock, - .mand_unlock_range = smb2_unlock_range, - .push_mand_locks = smb2_push_mandatory_locks, - .get_lease_key = smb2_get_lease_key, - .set_lease_key = smb2_set_lease_key, - .new_lease_key = smb2_new_lease_key, - .generate_signingkey = generate_smb311signingkey, - .calc_signature = smb3_calc_signature, - .set_integrity = smb3_set_integrity, - .is_read_op = smb21_is_read_op, - .set_oplock_level = smb3_set_oplock_level, - .create_lease_buf = smb3_create_lease_buf, - .parse_lease_buf = smb3_parse_lease_buf, - .copychunk_range = smb2_copychunk_range, - .duplicate_extents = smb2_duplicate_extents, -/* .validate_negotiate = smb3_validate_negotiate, */ /* not used in 3.11 */ - .wp_retry_size = smb2_wp_retry_size, - .dir_needs_close = smb2_dir_needs_close, - .fallocate = smb3_fallocate, - .enum_snapshots = smb3_enum_snapshots, - .notify = smb3_notify, - .init_transform_rq = smb3_init_transform_rq, - .is_transform_hdr = smb3_is_transform_hdr, - .receive_transform = smb3_receive_transform, - .get_dfs_refer = smb2_get_dfs_refer, - .select_sectype = smb2_select_sectype, -#ifdef CONFIG_CIFS_XATTR - .query_all_EAs = smb2_query_eas, - .set_EA = smb2_set_ea, -#endif /* CIFS_XATTR */ - .get_acl = get_smb2_acl, - .get_acl_by_fid = get_smb2_acl_by_fid, - .set_acl = set_smb2_acl, - .next_header = smb2_next_header, - .ioctl_query_info = smb2_ioctl_query_info, - .make_node = smb2_make_node, - .fiemap = smb3_fiemap, - .llseek = smb3_llseek, - .is_status_io_timeout = smb2_is_status_io_timeout, - .is_network_name_deleted = smb2_is_network_name_deleted, -}; - -#ifdef CONFIG_CIFS_ALLOW_INSECURE_LEGACY -struct smb_version_values smb20_values = { - .version_string = SMB20_VERSION_STRING, - .protocol_id = SMB20_PROT_ID, - .req_capabilities = 0, /* MBZ */ - .large_lock_type = 0, - .exclusive_lock_type = SMB2_LOCKFLAG_EXCLUSIVE, - .shared_lock_type = SMB2_LOCKFLAG_SHARED, - .unlock_lock_type = SMB2_LOCKFLAG_UNLOCK, - .header_size = sizeof(struct smb2_hdr), - .header_preamble_size = 0, - .max_header_size = MAX_SMB2_HDR_SIZE, - .read_rsp_size = sizeof(struct smb2_read_rsp) - 1, - .lock_cmd = SMB2_LOCK, - .cap_unix = 0, - .cap_nt_find = SMB2_NT_FIND, - .cap_large_files = SMB2_LARGE_FILES, - .signing_enabled = SMB2_NEGOTIATE_SIGNING_ENABLED | SMB2_NEGOTIATE_SIGNING_REQUIRED, - .signing_required = SMB2_NEGOTIATE_SIGNING_REQUIRED, - .create_lease_size = sizeof(struct create_lease), -}; -#endif /* ALLOW_INSECURE_LEGACY */ - -struct smb_version_values smb21_values = { - .version_string = SMB21_VERSION_STRING, - .protocol_id = SMB21_PROT_ID, - .req_capabilities = 0, /* MBZ on negotiate req until SMB3 dialect */ - .large_lock_type = 0, - .exclusive_lock_type = SMB2_LOCKFLAG_EXCLUSIVE, - .shared_lock_type = SMB2_LOCKFLAG_SHARED, - .unlock_lock_type = SMB2_LOCKFLAG_UNLOCK, - .header_size = sizeof(struct smb2_hdr), - .header_preamble_size = 0, - .max_header_size = MAX_SMB2_HDR_SIZE, - .read_rsp_size = sizeof(struct smb2_read_rsp) - 1, - .lock_cmd = SMB2_LOCK, - .cap_unix = 0, - .cap_nt_find = SMB2_NT_FIND, - .cap_large_files = SMB2_LARGE_FILES, - .signing_enabled = SMB2_NEGOTIATE_SIGNING_ENABLED | SMB2_NEGOTIATE_SIGNING_REQUIRED, - .signing_required = SMB2_NEGOTIATE_SIGNING_REQUIRED, - .create_lease_size = sizeof(struct create_lease), -}; - -struct smb_version_values smb3any_values = { - .version_string = SMB3ANY_VERSION_STRING, - .protocol_id = SMB302_PROT_ID, /* doesn't matter, send protocol array */ - .req_capabilities = SMB2_GLOBAL_CAP_DFS | SMB2_GLOBAL_CAP_LEASING | SMB2_GLOBAL_CAP_LARGE_MTU | SMB2_GLOBAL_CAP_PERSISTENT_HANDLES | SMB2_GLOBAL_CAP_ENCRYPTION | SMB2_GLOBAL_CAP_DIRECTORY_LEASING, - .large_lock_type = 0, - .exclusive_lock_type = SMB2_LOCKFLAG_EXCLUSIVE, - .shared_lock_type = SMB2_LOCKFLAG_SHARED, - .unlock_lock_type = SMB2_LOCKFLAG_UNLOCK, - .header_size = sizeof(struct smb2_hdr), - .header_preamble_size = 0, - .max_header_size = MAX_SMB2_HDR_SIZE, - .read_rsp_size = sizeof(struct smb2_read_rsp) - 1, - .lock_cmd = SMB2_LOCK, - .cap_unix = 0, - .cap_nt_find = SMB2_NT_FIND, - .cap_large_files = SMB2_LARGE_FILES, - .signing_enabled = SMB2_NEGOTIATE_SIGNING_ENABLED | SMB2_NEGOTIATE_SIGNING_REQUIRED, - .signing_required = SMB2_NEGOTIATE_SIGNING_REQUIRED, - .create_lease_size = sizeof(struct create_lease_v2), -}; - -struct smb_version_values smbdefault_values = { - .version_string = SMBDEFAULT_VERSION_STRING, - .protocol_id = SMB302_PROT_ID, /* doesn't matter, send protocol array */ - .req_capabilities = SMB2_GLOBAL_CAP_DFS | SMB2_GLOBAL_CAP_LEASING | SMB2_GLOBAL_CAP_LARGE_MTU | SMB2_GLOBAL_CAP_PERSISTENT_HANDLES | SMB2_GLOBAL_CAP_ENCRYPTION | SMB2_GLOBAL_CAP_DIRECTORY_LEASING, - .large_lock_type = 0, - .exclusive_lock_type = SMB2_LOCKFLAG_EXCLUSIVE, - .shared_lock_type = SMB2_LOCKFLAG_SHARED, - .unlock_lock_type = SMB2_LOCKFLAG_UNLOCK, - .header_size = sizeof(struct smb2_hdr), - .header_preamble_size = 0, - .max_header_size = MAX_SMB2_HDR_SIZE, - .read_rsp_size = sizeof(struct smb2_read_rsp) - 1, - .lock_cmd = SMB2_LOCK, - .cap_unix = 0, - .cap_nt_find = SMB2_NT_FIND, - .cap_large_files = SMB2_LARGE_FILES, - .signing_enabled = SMB2_NEGOTIATE_SIGNING_ENABLED | SMB2_NEGOTIATE_SIGNING_REQUIRED, - .signing_required = SMB2_NEGOTIATE_SIGNING_REQUIRED, - .create_lease_size = sizeof(struct create_lease_v2), -}; - -struct smb_version_values smb30_values = { - .version_string = SMB30_VERSION_STRING, - .protocol_id = SMB30_PROT_ID, - .req_capabilities = SMB2_GLOBAL_CAP_DFS | SMB2_GLOBAL_CAP_LEASING | SMB2_GLOBAL_CAP_LARGE_MTU | SMB2_GLOBAL_CAP_PERSISTENT_HANDLES | SMB2_GLOBAL_CAP_ENCRYPTION | SMB2_GLOBAL_CAP_DIRECTORY_LEASING, - .large_lock_type = 0, - .exclusive_lock_type = SMB2_LOCKFLAG_EXCLUSIVE, - .shared_lock_type = SMB2_LOCKFLAG_SHARED, - .unlock_lock_type = SMB2_LOCKFLAG_UNLOCK, - .header_size = sizeof(struct smb2_hdr), - .header_preamble_size = 0, - .max_header_size = MAX_SMB2_HDR_SIZE, - .read_rsp_size = sizeof(struct smb2_read_rsp) - 1, - .lock_cmd = SMB2_LOCK, - .cap_unix = 0, - .cap_nt_find = SMB2_NT_FIND, - .cap_large_files = SMB2_LARGE_FILES, - .signing_enabled = SMB2_NEGOTIATE_SIGNING_ENABLED | SMB2_NEGOTIATE_SIGNING_REQUIRED, - .signing_required = SMB2_NEGOTIATE_SIGNING_REQUIRED, - .create_lease_size = sizeof(struct create_lease_v2), -}; - -struct smb_version_values smb302_values = { - .version_string = SMB302_VERSION_STRING, - .protocol_id = SMB302_PROT_ID, - .req_capabilities = SMB2_GLOBAL_CAP_DFS | SMB2_GLOBAL_CAP_LEASING | SMB2_GLOBAL_CAP_LARGE_MTU | SMB2_GLOBAL_CAP_PERSISTENT_HANDLES | SMB2_GLOBAL_CAP_ENCRYPTION | SMB2_GLOBAL_CAP_DIRECTORY_LEASING, - .large_lock_type = 0, - .exclusive_lock_type = SMB2_LOCKFLAG_EXCLUSIVE, - .shared_lock_type = SMB2_LOCKFLAG_SHARED, - .unlock_lock_type = SMB2_LOCKFLAG_UNLOCK, - .header_size = sizeof(struct smb2_hdr), - .header_preamble_size = 0, - .max_header_size = MAX_SMB2_HDR_SIZE, - .read_rsp_size = sizeof(struct smb2_read_rsp) - 1, - .lock_cmd = SMB2_LOCK, - .cap_unix = 0, - .cap_nt_find = SMB2_NT_FIND, - .cap_large_files = SMB2_LARGE_FILES, - .signing_enabled = SMB2_NEGOTIATE_SIGNING_ENABLED | SMB2_NEGOTIATE_SIGNING_REQUIRED, - .signing_required = SMB2_NEGOTIATE_SIGNING_REQUIRED, - .create_lease_size = sizeof(struct create_lease_v2), -}; - -struct smb_version_values smb311_values = { - .version_string = SMB311_VERSION_STRING, - .protocol_id = SMB311_PROT_ID, - .req_capabilities = SMB2_GLOBAL_CAP_DFS | SMB2_GLOBAL_CAP_LEASING | SMB2_GLOBAL_CAP_LARGE_MTU | SMB2_GLOBAL_CAP_PERSISTENT_HANDLES | SMB2_GLOBAL_CAP_ENCRYPTION | SMB2_GLOBAL_CAP_DIRECTORY_LEASING, - .large_lock_type = 0, - .exclusive_lock_type = SMB2_LOCKFLAG_EXCLUSIVE, - .shared_lock_type = SMB2_LOCKFLAG_SHARED, - .unlock_lock_type = SMB2_LOCKFLAG_UNLOCK, - .header_size = sizeof(struct smb2_hdr), - .header_preamble_size = 0, - .max_header_size = MAX_SMB2_HDR_SIZE, - .read_rsp_size = sizeof(struct smb2_read_rsp) - 1, - .lock_cmd = SMB2_LOCK, - .cap_unix = 0, - .cap_nt_find = SMB2_NT_FIND, - .cap_large_files = SMB2_LARGE_FILES, - .signing_enabled = SMB2_NEGOTIATE_SIGNING_ENABLED | SMB2_NEGOTIATE_SIGNING_REQUIRED, - .signing_required = SMB2_NEGOTIATE_SIGNING_REQUIRED, - .create_lease_size = sizeof(struct create_lease_v2), -}; diff --git a/fs/cifs/smb2pdu.c b/fs/cifs/smb2pdu.c deleted file mode 100644 index 537e8679900b8..0000000000000 --- a/fs/cifs/smb2pdu.c +++ /dev/null @@ -1,5722 +0,0 @@ -// SPDX-License-Identifier: LGPL-2.1 -/* - * - * Copyright (C) International Business Machines Corp., 2009, 2013 - * Etersoft, 2012 - * Author(s): Steve French (sfrench@us.ibm.com) - * Pavel Shilovsky (pshilovsky@samba.org) 2012 - * - * Contains the routines for constructing the SMB2 PDUs themselves - * - */ - - /* SMB2 PDU handling routines here - except for leftovers (eg session setup) */ - /* Note that there are handle based routines which must be */ - /* treated slightly differently for reconnection purposes since we never */ - /* want to reuse a stale file handle and only the caller knows the file info */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include "cifsglob.h" -#include "cifsacl.h" -#include "cifsproto.h" -#include "smb2proto.h" -#include "cifs_unicode.h" -#include "cifs_debug.h" -#include "ntlmssp.h" -#include "smb2status.h" -#include "smb2glob.h" -#include "cifspdu.h" -#include "cifs_spnego.h" -#include "smbdirect.h" -#include "trace.h" -#ifdef CONFIG_CIFS_DFS_UPCALL -#include "dfs_cache.h" -#endif -#include "cached_dir.h" - -/* - * The following table defines the expected "StructureSize" of SMB2 requests - * in order by SMB2 command. This is similar to "wct" in SMB/CIFS requests. - * - * Note that commands are defined in smb2pdu.h in le16 but the array below is - * indexed by command in host byte order. - */ -static const int smb2_req_struct_sizes[NUMBER_OF_SMB2_COMMANDS] = { - /* SMB2_NEGOTIATE */ 36, - /* SMB2_SESSION_SETUP */ 25, - /* SMB2_LOGOFF */ 4, - /* SMB2_TREE_CONNECT */ 9, - /* SMB2_TREE_DISCONNECT */ 4, - /* SMB2_CREATE */ 57, - /* SMB2_CLOSE */ 24, - /* SMB2_FLUSH */ 24, - /* SMB2_READ */ 49, - /* SMB2_WRITE */ 49, - /* SMB2_LOCK */ 48, - /* SMB2_IOCTL */ 57, - /* SMB2_CANCEL */ 4, - /* SMB2_ECHO */ 4, - /* SMB2_QUERY_DIRECTORY */ 33, - /* SMB2_CHANGE_NOTIFY */ 32, - /* SMB2_QUERY_INFO */ 41, - /* SMB2_SET_INFO */ 33, - /* SMB2_OPLOCK_BREAK */ 24 /* BB this is 36 for LEASE_BREAK variant */ -}; - -int smb3_encryption_required(const struct cifs_tcon *tcon) -{ - if (!tcon || !tcon->ses) - return 0; - if ((tcon->ses->session_flags & SMB2_SESSION_FLAG_ENCRYPT_DATA) || - (tcon->share_flags & SHI1005_FLAGS_ENCRYPT_DATA)) - return 1; - if (tcon->seal && - (tcon->ses->server->capabilities & SMB2_GLOBAL_CAP_ENCRYPTION)) - return 1; - return 0; -} - -static void -smb2_hdr_assemble(struct smb2_hdr *shdr, __le16 smb2_cmd, - const struct cifs_tcon *tcon, - struct TCP_Server_Info *server) -{ - shdr->ProtocolId = SMB2_PROTO_NUMBER; - shdr->StructureSize = cpu_to_le16(64); - shdr->Command = smb2_cmd; - if (server) { - spin_lock(&server->req_lock); - /* Request up to 10 credits but don't go over the limit. */ - if (server->credits >= server->max_credits) - shdr->CreditRequest = cpu_to_le16(0); - else - shdr->CreditRequest = cpu_to_le16( - min_t(int, server->max_credits - - server->credits, 10)); - spin_unlock(&server->req_lock); - } else { - shdr->CreditRequest = cpu_to_le16(2); - } - shdr->Id.SyncId.ProcessId = cpu_to_le32((__u16)current->tgid); - - if (!tcon) - goto out; - - /* GLOBAL_CAP_LARGE_MTU will only be set if dialect > SMB2.02 */ - /* See sections 2.2.4 and 3.2.4.1.5 of MS-SMB2 */ - if (server && (server->capabilities & SMB2_GLOBAL_CAP_LARGE_MTU)) - shdr->CreditCharge = cpu_to_le16(1); - /* else CreditCharge MBZ */ - - shdr->Id.SyncId.TreeId = cpu_to_le32(tcon->tid); - /* Uid is not converted */ - if (tcon->ses) - shdr->SessionId = cpu_to_le64(tcon->ses->Suid); - - /* - * If we would set SMB2_FLAGS_DFS_OPERATIONS on open we also would have - * to pass the path on the Open SMB prefixed by \\server\share. - * Not sure when we would need to do the augmented path (if ever) and - * setting this flag breaks the SMB2 open operation since it is - * illegal to send an empty path name (without \\server\share prefix) - * when the DFS flag is set in the SMB open header. We could - * consider setting the flag on all operations other than open - * but it is safer to net set it for now. - */ -/* if (tcon->share_flags & SHI1005_FLAGS_DFS) - shdr->Flags |= SMB2_FLAGS_DFS_OPERATIONS; */ - - if (server && server->sign && !smb3_encryption_required(tcon)) - shdr->Flags |= SMB2_FLAGS_SIGNED; -out: - return; -} - -static int -smb2_reconnect(__le16 smb2_command, struct cifs_tcon *tcon, - struct TCP_Server_Info *server) -{ - int rc = 0; - struct nls_table *nls_codepage = NULL; - struct cifs_ses *ses; - - /* - * SMB2s NegProt, SessSetup, Logoff do not have tcon yet so - * check for tcp and smb session status done differently - * for those three - in the calling routine. - */ - if (tcon == NULL) - return 0; - - /* - * Need to also skip SMB2_IOCTL because it is used for checking nested dfs links in - * cifs_tree_connect(). - */ - if (smb2_command == SMB2_TREE_CONNECT || smb2_command == SMB2_IOCTL) - return 0; - - spin_lock(&tcon->tc_lock); - if (tcon->status == TID_EXITING) { - /* - * only tree disconnect allowed when disconnecting ... - */ - if (smb2_command != SMB2_TREE_DISCONNECT) { - spin_unlock(&tcon->tc_lock); - cifs_dbg(FYI, "can not send cmd %d while umounting\n", - smb2_command); - return -ENODEV; - } - } - spin_unlock(&tcon->tc_lock); - - ses = tcon->ses; - if (!ses) - return -EIO; - spin_lock(&ses->ses_lock); - if (ses->ses_status == SES_EXITING) { - spin_unlock(&ses->ses_lock); - return -EIO; - } - spin_unlock(&ses->ses_lock); - if (!ses->server || !server) - return -EIO; - - spin_lock(&server->srv_lock); - if (server->tcpStatus == CifsNeedReconnect) { - /* - * Return to caller for TREE_DISCONNECT and LOGOFF and CLOSE - * here since they are implicitly done when session drops. - */ - switch (smb2_command) { - /* - * BB Should we keep oplock break and add flush to exceptions? - */ - case SMB2_TREE_DISCONNECT: - case SMB2_CANCEL: - case SMB2_CLOSE: - case SMB2_OPLOCK_BREAK: - spin_unlock(&server->srv_lock); - return -EAGAIN; - } - } - spin_unlock(&server->srv_lock); - -again: - rc = cifs_wait_for_server_reconnect(server, tcon->retry); - if (rc) - return rc; - - spin_lock(&ses->chan_lock); - if (!cifs_chan_needs_reconnect(ses, server) && !tcon->need_reconnect) { - spin_unlock(&ses->chan_lock); - return 0; - } - spin_unlock(&ses->chan_lock); - cifs_dbg(FYI, "sess reconnect mask: 0x%lx, tcon reconnect: %d", - tcon->ses->chans_need_reconnect, - tcon->need_reconnect); - - mutex_lock(&ses->session_mutex); - /* - * Recheck after acquire mutex. If another thread is negotiating - * and the server never sends an answer the socket will be closed - * and tcpStatus set to reconnect. - */ - spin_lock(&server->srv_lock); - if (server->tcpStatus == CifsNeedReconnect) { - spin_unlock(&server->srv_lock); - mutex_unlock(&ses->session_mutex); - - if (tcon->retry) - goto again; - - rc = -EHOSTDOWN; - goto out; - } - spin_unlock(&server->srv_lock); - - nls_codepage = load_nls_default(); - - /* - * need to prevent multiple threads trying to simultaneously - * reconnect the same SMB session - */ - spin_lock(&ses->ses_lock); - spin_lock(&ses->chan_lock); - if (!cifs_chan_needs_reconnect(ses, server) && - ses->ses_status == SES_GOOD) { - spin_unlock(&ses->chan_lock); - spin_unlock(&ses->ses_lock); - /* this means that we only need to tree connect */ - if (tcon->need_reconnect) - goto skip_sess_setup; - - mutex_unlock(&ses->session_mutex); - goto out; - } - spin_unlock(&ses->chan_lock); - spin_unlock(&ses->ses_lock); - - rc = cifs_negotiate_protocol(0, ses, server); - if (!rc) { - rc = cifs_setup_session(0, ses, server, nls_codepage); - if ((rc == -EACCES) && !tcon->retry) { - mutex_unlock(&ses->session_mutex); - rc = -EHOSTDOWN; - goto failed; - } else if (rc) { - mutex_unlock(&ses->session_mutex); - goto out; - } - } else { - mutex_unlock(&ses->session_mutex); - goto out; - } - -skip_sess_setup: - if (!tcon->need_reconnect) { - mutex_unlock(&ses->session_mutex); - goto out; - } - cifs_mark_open_files_invalid(tcon); - if (tcon->use_persistent) - tcon->need_reopen_files = true; - - rc = cifs_tree_connect(0, tcon, nls_codepage); - mutex_unlock(&ses->session_mutex); - - cifs_dbg(FYI, "reconnect tcon rc = %d\n", rc); - if (rc) { - /* If sess reconnected but tcon didn't, something strange ... */ - cifs_dbg(VFS, "reconnect tcon failed rc = %d\n", rc); - goto out; - } - - if (smb2_command != SMB2_INTERNAL_CMD) - mod_delayed_work(cifsiod_wq, &server->reconnect, 0); - - atomic_inc(&tconInfoReconnectCount); -out: - /* - * Check if handle based operation so we know whether we can continue - * or not without returning to caller to reset file handle. - */ - /* - * BB Is flush done by server on drop of tcp session? Should we special - * case it and skip above? - */ - switch (smb2_command) { - case SMB2_FLUSH: - case SMB2_READ: - case SMB2_WRITE: - case SMB2_LOCK: - case SMB2_IOCTL: - case SMB2_QUERY_DIRECTORY: - case SMB2_CHANGE_NOTIFY: - case SMB2_QUERY_INFO: - case SMB2_SET_INFO: - rc = -EAGAIN; - } -failed: - unload_nls(nls_codepage); - return rc; -} - -static void -fill_small_buf(__le16 smb2_command, struct cifs_tcon *tcon, - struct TCP_Server_Info *server, - void *buf, - unsigned int *total_len) -{ - struct smb2_pdu *spdu = buf; - /* lookup word count ie StructureSize from table */ - __u16 parmsize = smb2_req_struct_sizes[le16_to_cpu(smb2_command)]; - - /* - * smaller than SMALL_BUFFER_SIZE but bigger than fixed area of - * largest operations (Create) - */ - memset(buf, 0, 256); - - smb2_hdr_assemble(&spdu->hdr, smb2_command, tcon, server); - spdu->StructureSize2 = cpu_to_le16(parmsize); - - *total_len = parmsize + sizeof(struct smb2_hdr); -} - -/* - * Allocate and return pointer to an SMB request hdr, and set basic - * SMB information in the SMB header. If the return code is zero, this - * function must have filled in request_buf pointer. - */ -static int __smb2_plain_req_init(__le16 smb2_command, struct cifs_tcon *tcon, - struct TCP_Server_Info *server, - void **request_buf, unsigned int *total_len) -{ - /* BB eventually switch this to SMB2 specific small buf size */ - if (smb2_command == SMB2_SET_INFO) - *request_buf = cifs_buf_get(); - else - *request_buf = cifs_small_buf_get(); - if (*request_buf == NULL) { - /* BB should we add a retry in here if not a writepage? */ - return -ENOMEM; - } - - fill_small_buf(smb2_command, tcon, server, - (struct smb2_hdr *)(*request_buf), - total_len); - - if (tcon != NULL) { - uint16_t com_code = le16_to_cpu(smb2_command); - cifs_stats_inc(&tcon->stats.smb2_stats.smb2_com_sent[com_code]); - cifs_stats_inc(&tcon->num_smbs_sent); - } - - return 0; -} - -static int smb2_plain_req_init(__le16 smb2_command, struct cifs_tcon *tcon, - struct TCP_Server_Info *server, - void **request_buf, unsigned int *total_len) -{ - int rc; - - rc = smb2_reconnect(smb2_command, tcon, server); - if (rc) - return rc; - - return __smb2_plain_req_init(smb2_command, tcon, server, request_buf, - total_len); -} - -static int smb2_ioctl_req_init(u32 opcode, struct cifs_tcon *tcon, - struct TCP_Server_Info *server, - void **request_buf, unsigned int *total_len) -{ - /* Skip reconnect only for FSCTL_VALIDATE_NEGOTIATE_INFO IOCTLs */ - if (opcode == FSCTL_VALIDATE_NEGOTIATE_INFO) { - return __smb2_plain_req_init(SMB2_IOCTL, tcon, server, - request_buf, total_len); - } - return smb2_plain_req_init(SMB2_IOCTL, tcon, server, - request_buf, total_len); -} - -/* For explanation of negotiate contexts see MS-SMB2 section 2.2.3.1 */ - -static void -build_preauth_ctxt(struct smb2_preauth_neg_context *pneg_ctxt) -{ - pneg_ctxt->ContextType = SMB2_PREAUTH_INTEGRITY_CAPABILITIES; - pneg_ctxt->DataLength = cpu_to_le16(38); - pneg_ctxt->HashAlgorithmCount = cpu_to_le16(1); - pneg_ctxt->SaltLength = cpu_to_le16(SMB311_SALT_SIZE); - get_random_bytes(pneg_ctxt->Salt, SMB311_SALT_SIZE); - pneg_ctxt->HashAlgorithms = SMB2_PREAUTH_INTEGRITY_SHA512; -} - -static void -build_compression_ctxt(struct smb2_compression_capabilities_context *pneg_ctxt) -{ - pneg_ctxt->ContextType = SMB2_COMPRESSION_CAPABILITIES; - pneg_ctxt->DataLength = - cpu_to_le16(sizeof(struct smb2_compression_capabilities_context) - - sizeof(struct smb2_neg_context)); - pneg_ctxt->CompressionAlgorithmCount = cpu_to_le16(3); - pneg_ctxt->CompressionAlgorithms[0] = SMB3_COMPRESS_LZ77; - pneg_ctxt->CompressionAlgorithms[1] = SMB3_COMPRESS_LZ77_HUFF; - pneg_ctxt->CompressionAlgorithms[2] = SMB3_COMPRESS_LZNT1; -} - -static unsigned int -build_signing_ctxt(struct smb2_signing_capabilities *pneg_ctxt) -{ - unsigned int ctxt_len = sizeof(struct smb2_signing_capabilities); - unsigned short num_algs = 1; /* number of signing algorithms sent */ - - pneg_ctxt->ContextType = SMB2_SIGNING_CAPABILITIES; - /* - * Context Data length must be rounded to multiple of 8 for some servers - */ - pneg_ctxt->DataLength = cpu_to_le16(ALIGN(sizeof(struct smb2_signing_capabilities) - - sizeof(struct smb2_neg_context) + - (num_algs * sizeof(u16)), 8)); - pneg_ctxt->SigningAlgorithmCount = cpu_to_le16(num_algs); - pneg_ctxt->SigningAlgorithms[0] = cpu_to_le16(SIGNING_ALG_AES_CMAC); - - ctxt_len += sizeof(__le16) * num_algs; - ctxt_len = ALIGN(ctxt_len, 8); - return ctxt_len; - /* TBD add SIGNING_ALG_AES_GMAC and/or SIGNING_ALG_HMAC_SHA256 */ -} - -static void -build_encrypt_ctxt(struct smb2_encryption_neg_context *pneg_ctxt) -{ - pneg_ctxt->ContextType = SMB2_ENCRYPTION_CAPABILITIES; - if (require_gcm_256) { - pneg_ctxt->DataLength = cpu_to_le16(4); /* Cipher Count + 1 cipher */ - pneg_ctxt->CipherCount = cpu_to_le16(1); - pneg_ctxt->Ciphers[0] = SMB2_ENCRYPTION_AES256_GCM; - } else if (enable_gcm_256) { - pneg_ctxt->DataLength = cpu_to_le16(8); /* Cipher Count + 3 ciphers */ - pneg_ctxt->CipherCount = cpu_to_le16(3); - pneg_ctxt->Ciphers[0] = SMB2_ENCRYPTION_AES128_GCM; - pneg_ctxt->Ciphers[1] = SMB2_ENCRYPTION_AES256_GCM; - pneg_ctxt->Ciphers[2] = SMB2_ENCRYPTION_AES128_CCM; - } else { - pneg_ctxt->DataLength = cpu_to_le16(6); /* Cipher Count + 2 ciphers */ - pneg_ctxt->CipherCount = cpu_to_le16(2); - pneg_ctxt->Ciphers[0] = SMB2_ENCRYPTION_AES128_GCM; - pneg_ctxt->Ciphers[1] = SMB2_ENCRYPTION_AES128_CCM; - } -} - -static unsigned int -build_netname_ctxt(struct smb2_netname_neg_context *pneg_ctxt, char *hostname) -{ - struct nls_table *cp = load_nls_default(); - - pneg_ctxt->ContextType = SMB2_NETNAME_NEGOTIATE_CONTEXT_ID; - - /* copy up to max of first 100 bytes of server name to NetName field */ - pneg_ctxt->DataLength = cpu_to_le16(2 * cifs_strtoUTF16(pneg_ctxt->NetName, hostname, 100, cp)); - /* context size is DataLength + minimal smb2_neg_context */ - return ALIGN(le16_to_cpu(pneg_ctxt->DataLength) + sizeof(struct smb2_neg_context), 8); -} - -static void -build_posix_ctxt(struct smb2_posix_neg_context *pneg_ctxt) -{ - pneg_ctxt->ContextType = SMB2_POSIX_EXTENSIONS_AVAILABLE; - pneg_ctxt->DataLength = cpu_to_le16(POSIX_CTXT_DATA_LEN); - /* SMB2_CREATE_TAG_POSIX is "0x93AD25509CB411E7B42383DE968BCD7C" */ - pneg_ctxt->Name[0] = 0x93; - pneg_ctxt->Name[1] = 0xAD; - pneg_ctxt->Name[2] = 0x25; - pneg_ctxt->Name[3] = 0x50; - pneg_ctxt->Name[4] = 0x9C; - pneg_ctxt->Name[5] = 0xB4; - pneg_ctxt->Name[6] = 0x11; - pneg_ctxt->Name[7] = 0xE7; - pneg_ctxt->Name[8] = 0xB4; - pneg_ctxt->Name[9] = 0x23; - pneg_ctxt->Name[10] = 0x83; - pneg_ctxt->Name[11] = 0xDE; - pneg_ctxt->Name[12] = 0x96; - pneg_ctxt->Name[13] = 0x8B; - pneg_ctxt->Name[14] = 0xCD; - pneg_ctxt->Name[15] = 0x7C; -} - -static void -assemble_neg_contexts(struct smb2_negotiate_req *req, - struct TCP_Server_Info *server, unsigned int *total_len) -{ - unsigned int ctxt_len, neg_context_count; - struct TCP_Server_Info *pserver; - char *pneg_ctxt; - char *hostname; - - if (*total_len > 200) { - /* In case length corrupted don't want to overrun smb buffer */ - cifs_server_dbg(VFS, "Bad frame length assembling neg contexts\n"); - return; - } - - /* - * round up total_len of fixed part of SMB3 negotiate request to 8 - * byte boundary before adding negotiate contexts - */ - *total_len = ALIGN(*total_len, 8); - - pneg_ctxt = (*total_len) + (char *)req; - req->NegotiateContextOffset = cpu_to_le32(*total_len); - - build_preauth_ctxt((struct smb2_preauth_neg_context *)pneg_ctxt); - ctxt_len = ALIGN(sizeof(struct smb2_preauth_neg_context), 8); - *total_len += ctxt_len; - pneg_ctxt += ctxt_len; - - build_encrypt_ctxt((struct smb2_encryption_neg_context *)pneg_ctxt); - ctxt_len = ALIGN(sizeof(struct smb2_encryption_neg_context), 8); - *total_len += ctxt_len; - pneg_ctxt += ctxt_len; - - /* - * secondary channels don't have the hostname field populated - * use the hostname field in the primary channel instead - */ - pserver = CIFS_SERVER_IS_CHAN(server) ? server->primary_server : server; - cifs_server_lock(pserver); - hostname = pserver->hostname; - if (hostname && (hostname[0] != 0)) { - ctxt_len = build_netname_ctxt((struct smb2_netname_neg_context *)pneg_ctxt, - hostname); - *total_len += ctxt_len; - pneg_ctxt += ctxt_len; - neg_context_count = 3; - } else - neg_context_count = 2; - cifs_server_unlock(pserver); - - build_posix_ctxt((struct smb2_posix_neg_context *)pneg_ctxt); - *total_len += sizeof(struct smb2_posix_neg_context); - pneg_ctxt += sizeof(struct smb2_posix_neg_context); - neg_context_count++; - - if (server->compress_algorithm) { - build_compression_ctxt((struct smb2_compression_capabilities_context *) - pneg_ctxt); - ctxt_len = ALIGN(sizeof(struct smb2_compression_capabilities_context), 8); - *total_len += ctxt_len; - pneg_ctxt += ctxt_len; - neg_context_count++; - } - - if (enable_negotiate_signing) { - ctxt_len = build_signing_ctxt((struct smb2_signing_capabilities *) - pneg_ctxt); - *total_len += ctxt_len; - pneg_ctxt += ctxt_len; - neg_context_count++; - } - - /* check for and add transport_capabilities and signing capabilities */ - req->NegotiateContextCount = cpu_to_le16(neg_context_count); - -} - -/* If invalid preauth context warn but use what we requested, SHA-512 */ -static void decode_preauth_context(struct smb2_preauth_neg_context *ctxt) -{ - unsigned int len = le16_to_cpu(ctxt->DataLength); - - /* - * Caller checked that DataLength remains within SMB boundary. We still - * need to confirm that one HashAlgorithms member is accounted for. - */ - if (len < MIN_PREAUTH_CTXT_DATA_LEN) { - pr_warn_once("server sent bad preauth context\n"); - return; - } else if (len < MIN_PREAUTH_CTXT_DATA_LEN + le16_to_cpu(ctxt->SaltLength)) { - pr_warn_once("server sent invalid SaltLength\n"); - return; - } - if (le16_to_cpu(ctxt->HashAlgorithmCount) != 1) - pr_warn_once("Invalid SMB3 hash algorithm count\n"); - if (ctxt->HashAlgorithms != SMB2_PREAUTH_INTEGRITY_SHA512) - pr_warn_once("unknown SMB3 hash algorithm\n"); -} - -static void decode_compress_ctx(struct TCP_Server_Info *server, - struct smb2_compression_capabilities_context *ctxt) -{ - unsigned int len = le16_to_cpu(ctxt->DataLength); - - /* - * Caller checked that DataLength remains within SMB boundary. We still - * need to confirm that one CompressionAlgorithms member is accounted - * for. - */ - if (len < 10) { - pr_warn_once("server sent bad compression cntxt\n"); - return; - } - if (le16_to_cpu(ctxt->CompressionAlgorithmCount) != 1) { - pr_warn_once("Invalid SMB3 compress algorithm count\n"); - return; - } - if (le16_to_cpu(ctxt->CompressionAlgorithms[0]) > 3) { - pr_warn_once("unknown compression algorithm\n"); - return; - } - server->compress_algorithm = ctxt->CompressionAlgorithms[0]; -} - -static int decode_encrypt_ctx(struct TCP_Server_Info *server, - struct smb2_encryption_neg_context *ctxt) -{ - unsigned int len = le16_to_cpu(ctxt->DataLength); - - cifs_dbg(FYI, "decode SMB3.11 encryption neg context of len %d\n", len); - /* - * Caller checked that DataLength remains within SMB boundary. We still - * need to confirm that one Cipher flexible array member is accounted - * for. - */ - if (len < MIN_ENCRYPT_CTXT_DATA_LEN) { - pr_warn_once("server sent bad crypto ctxt len\n"); - return -EINVAL; - } - - if (le16_to_cpu(ctxt->CipherCount) != 1) { - pr_warn_once("Invalid SMB3.11 cipher count\n"); - return -EINVAL; - } - cifs_dbg(FYI, "SMB311 cipher type:%d\n", le16_to_cpu(ctxt->Ciphers[0])); - if (require_gcm_256) { - if (ctxt->Ciphers[0] != SMB2_ENCRYPTION_AES256_GCM) { - cifs_dbg(VFS, "Server does not support requested encryption type (AES256 GCM)\n"); - return -EOPNOTSUPP; - } - } else if (ctxt->Ciphers[0] == 0) { - /* - * e.g. if server only supported AES256_CCM (very unlikely) - * or server supported no encryption types or had all disabled. - * Since GLOBAL_CAP_ENCRYPTION will be not set, in the case - * in which mount requested encryption ("seal") checks later - * on during tree connection will return proper rc, but if - * seal not requested by client, since server is allowed to - * return 0 to indicate no supported cipher, we can't fail here - */ - server->cipher_type = 0; - server->capabilities &= ~SMB2_GLOBAL_CAP_ENCRYPTION; - pr_warn_once("Server does not support requested encryption types\n"); - return 0; - } else if ((ctxt->Ciphers[0] != SMB2_ENCRYPTION_AES128_CCM) && - (ctxt->Ciphers[0] != SMB2_ENCRYPTION_AES128_GCM) && - (ctxt->Ciphers[0] != SMB2_ENCRYPTION_AES256_GCM)) { - /* server returned a cipher we didn't ask for */ - pr_warn_once("Invalid SMB3.11 cipher returned\n"); - return -EINVAL; - } - server->cipher_type = ctxt->Ciphers[0]; - server->capabilities |= SMB2_GLOBAL_CAP_ENCRYPTION; - return 0; -} - -static void decode_signing_ctx(struct TCP_Server_Info *server, - struct smb2_signing_capabilities *pctxt) -{ - unsigned int len = le16_to_cpu(pctxt->DataLength); - - /* - * Caller checked that DataLength remains within SMB boundary. We still - * need to confirm that one SigningAlgorithms flexible array member is - * accounted for. - */ - if ((len < 4) || (len > 16)) { - pr_warn_once("server sent bad signing negcontext\n"); - return; - } - if (le16_to_cpu(pctxt->SigningAlgorithmCount) != 1) { - pr_warn_once("Invalid signing algorithm count\n"); - return; - } - if (le16_to_cpu(pctxt->SigningAlgorithms[0]) > 2) { - pr_warn_once("unknown signing algorithm\n"); - return; - } - - server->signing_negotiated = true; - server->signing_algorithm = le16_to_cpu(pctxt->SigningAlgorithms[0]); - cifs_dbg(FYI, "signing algorithm %d chosen\n", - server->signing_algorithm); -} - - -static int smb311_decode_neg_context(struct smb2_negotiate_rsp *rsp, - struct TCP_Server_Info *server, - unsigned int len_of_smb) -{ - struct smb2_neg_context *pctx; - unsigned int offset = le32_to_cpu(rsp->NegotiateContextOffset); - unsigned int ctxt_cnt = le16_to_cpu(rsp->NegotiateContextCount); - unsigned int len_of_ctxts, i; - int rc = 0; - - cifs_dbg(FYI, "decoding %d negotiate contexts\n", ctxt_cnt); - if (len_of_smb <= offset) { - cifs_server_dbg(VFS, "Invalid response: negotiate context offset\n"); - return -EINVAL; - } - - len_of_ctxts = len_of_smb - offset; - - for (i = 0; i < ctxt_cnt; i++) { - int clen; - /* check that offset is not beyond end of SMB */ - if (len_of_ctxts < sizeof(struct smb2_neg_context)) - break; - - pctx = (struct smb2_neg_context *)(offset + (char *)rsp); - clen = sizeof(struct smb2_neg_context) - + le16_to_cpu(pctx->DataLength); - /* - * 2.2.4 SMB2 NEGOTIATE Response - * Subsequent negotiate contexts MUST appear at the first 8-byte - * aligned offset following the previous negotiate context. - */ - if (i + 1 != ctxt_cnt) - clen = ALIGN(clen, 8); - if (clen > len_of_ctxts) - break; - - if (pctx->ContextType == SMB2_PREAUTH_INTEGRITY_CAPABILITIES) - decode_preauth_context( - (struct smb2_preauth_neg_context *)pctx); - else if (pctx->ContextType == SMB2_ENCRYPTION_CAPABILITIES) - rc = decode_encrypt_ctx(server, - (struct smb2_encryption_neg_context *)pctx); - else if (pctx->ContextType == SMB2_COMPRESSION_CAPABILITIES) - decode_compress_ctx(server, - (struct smb2_compression_capabilities_context *)pctx); - else if (pctx->ContextType == SMB2_POSIX_EXTENSIONS_AVAILABLE) - server->posix_ext_supported = true; - else if (pctx->ContextType == SMB2_SIGNING_CAPABILITIES) - decode_signing_ctx(server, - (struct smb2_signing_capabilities *)pctx); - else - cifs_server_dbg(VFS, "unknown negcontext of type %d ignored\n", - le16_to_cpu(pctx->ContextType)); - if (rc) - break; - - offset += clen; - len_of_ctxts -= clen; - } - return rc; -} - -static struct create_posix * -create_posix_buf(umode_t mode) -{ - struct create_posix *buf; - - buf = kzalloc(sizeof(struct create_posix), - GFP_KERNEL); - if (!buf) - return NULL; - - buf->ccontext.DataOffset = - cpu_to_le16(offsetof(struct create_posix, Mode)); - buf->ccontext.DataLength = cpu_to_le32(4); - buf->ccontext.NameOffset = - cpu_to_le16(offsetof(struct create_posix, Name)); - buf->ccontext.NameLength = cpu_to_le16(16); - - /* SMB2_CREATE_TAG_POSIX is "0x93AD25509CB411E7B42383DE968BCD7C" */ - buf->Name[0] = 0x93; - buf->Name[1] = 0xAD; - buf->Name[2] = 0x25; - buf->Name[3] = 0x50; - buf->Name[4] = 0x9C; - buf->Name[5] = 0xB4; - buf->Name[6] = 0x11; - buf->Name[7] = 0xE7; - buf->Name[8] = 0xB4; - buf->Name[9] = 0x23; - buf->Name[10] = 0x83; - buf->Name[11] = 0xDE; - buf->Name[12] = 0x96; - buf->Name[13] = 0x8B; - buf->Name[14] = 0xCD; - buf->Name[15] = 0x7C; - buf->Mode = cpu_to_le32(mode); - cifs_dbg(FYI, "mode on posix create 0%o\n", mode); - return buf; -} - -static int -add_posix_context(struct kvec *iov, unsigned int *num_iovec, umode_t mode) -{ - struct smb2_create_req *req = iov[0].iov_base; - unsigned int num = *num_iovec; - - iov[num].iov_base = create_posix_buf(mode); - if (mode == ACL_NO_MODE) - cifs_dbg(FYI, "Invalid mode\n"); - if (iov[num].iov_base == NULL) - return -ENOMEM; - iov[num].iov_len = sizeof(struct create_posix); - if (!req->CreateContextsOffset) - req->CreateContextsOffset = cpu_to_le32( - sizeof(struct smb2_create_req) + - iov[num - 1].iov_len); - le32_add_cpu(&req->CreateContextsLength, sizeof(struct create_posix)); - *num_iovec = num + 1; - return 0; -} - - -/* - * - * SMB2 Worker functions follow: - * - * The general structure of the worker functions is: - * 1) Call smb2_init (assembles SMB2 header) - * 2) Initialize SMB2 command specific fields in fixed length area of SMB - * 3) Call smb_sendrcv2 (sends request on socket and waits for response) - * 4) Decode SMB2 command specific fields in the fixed length area - * 5) Decode variable length data area (if any for this SMB2 command type) - * 6) Call free smb buffer - * 7) return - * - */ - -int -SMB2_negotiate(const unsigned int xid, - struct cifs_ses *ses, - struct TCP_Server_Info *server) -{ - struct smb_rqst rqst; - struct smb2_negotiate_req *req; - struct smb2_negotiate_rsp *rsp; - struct kvec iov[1]; - struct kvec rsp_iov; - int rc; - int resp_buftype; - int blob_offset, blob_length; - char *security_blob; - int flags = CIFS_NEG_OP; - unsigned int total_len; - - cifs_dbg(FYI, "Negotiate protocol\n"); - - if (!server) { - WARN(1, "%s: server is NULL!\n", __func__); - return -EIO; - } - - rc = smb2_plain_req_init(SMB2_NEGOTIATE, NULL, server, - (void **) &req, &total_len); - if (rc) - return rc; - - req->hdr.SessionId = 0; - - memset(server->preauth_sha_hash, 0, SMB2_PREAUTH_HASH_SIZE); - memset(ses->preauth_sha_hash, 0, SMB2_PREAUTH_HASH_SIZE); - - if (strcmp(server->vals->version_string, - SMB3ANY_VERSION_STRING) == 0) { - req->Dialects[0] = cpu_to_le16(SMB30_PROT_ID); - req->Dialects[1] = cpu_to_le16(SMB302_PROT_ID); - req->Dialects[2] = cpu_to_le16(SMB311_PROT_ID); - req->DialectCount = cpu_to_le16(3); - total_len += 6; - } else if (strcmp(server->vals->version_string, - SMBDEFAULT_VERSION_STRING) == 0) { - req->Dialects[0] = cpu_to_le16(SMB21_PROT_ID); - req->Dialects[1] = cpu_to_le16(SMB30_PROT_ID); - req->Dialects[2] = cpu_to_le16(SMB302_PROT_ID); - req->Dialects[3] = cpu_to_le16(SMB311_PROT_ID); - req->DialectCount = cpu_to_le16(4); - total_len += 8; - } else { - /* otherwise send specific dialect */ - req->Dialects[0] = cpu_to_le16(server->vals->protocol_id); - req->DialectCount = cpu_to_le16(1); - total_len += 2; - } - - /* only one of SMB2 signing flags may be set in SMB2 request */ - if (ses->sign) - req->SecurityMode = cpu_to_le16(SMB2_NEGOTIATE_SIGNING_REQUIRED); - else if (global_secflags & CIFSSEC_MAY_SIGN) - req->SecurityMode = cpu_to_le16(SMB2_NEGOTIATE_SIGNING_ENABLED); - else - req->SecurityMode = 0; - - req->Capabilities = cpu_to_le32(server->vals->req_capabilities); - if (ses->chan_max > 1) - req->Capabilities |= cpu_to_le32(SMB2_GLOBAL_CAP_MULTI_CHANNEL); - - /* ClientGUID must be zero for SMB2.02 dialect */ - if (server->vals->protocol_id == SMB20_PROT_ID) - memset(req->ClientGUID, 0, SMB2_CLIENT_GUID_SIZE); - else { - memcpy(req->ClientGUID, server->client_guid, - SMB2_CLIENT_GUID_SIZE); - if ((server->vals->protocol_id == SMB311_PROT_ID) || - (strcmp(server->vals->version_string, - SMB3ANY_VERSION_STRING) == 0) || - (strcmp(server->vals->version_string, - SMBDEFAULT_VERSION_STRING) == 0)) - assemble_neg_contexts(req, server, &total_len); - } - iov[0].iov_base = (char *)req; - iov[0].iov_len = total_len; - - memset(&rqst, 0, sizeof(struct smb_rqst)); - rqst.rq_iov = iov; - rqst.rq_nvec = 1; - - rc = cifs_send_recv(xid, ses, server, - &rqst, &resp_buftype, flags, &rsp_iov); - cifs_small_buf_release(req); - rsp = (struct smb2_negotiate_rsp *)rsp_iov.iov_base; - /* - * No tcon so can't do - * cifs_stats_inc(&tcon->stats.smb2_stats.smb2_com_fail[SMB2...]); - */ - if (rc == -EOPNOTSUPP) { - cifs_server_dbg(VFS, "Dialect not supported by server. Consider specifying vers=1.0 or vers=2.0 on mount for accessing older servers\n"); - goto neg_exit; - } else if (rc != 0) - goto neg_exit; - - rc = -EIO; - if (strcmp(server->vals->version_string, - SMB3ANY_VERSION_STRING) == 0) { - if (rsp->DialectRevision == cpu_to_le16(SMB20_PROT_ID)) { - cifs_server_dbg(VFS, - "SMB2 dialect returned but not requested\n"); - goto neg_exit; - } else if (rsp->DialectRevision == cpu_to_le16(SMB21_PROT_ID)) { - cifs_server_dbg(VFS, - "SMB2.1 dialect returned but not requested\n"); - goto neg_exit; - } else if (rsp->DialectRevision == cpu_to_le16(SMB311_PROT_ID)) { - /* ops set to 3.0 by default for default so update */ - server->ops = &smb311_operations; - server->vals = &smb311_values; - } - } else if (strcmp(server->vals->version_string, - SMBDEFAULT_VERSION_STRING) == 0) { - if (rsp->DialectRevision == cpu_to_le16(SMB20_PROT_ID)) { - cifs_server_dbg(VFS, - "SMB2 dialect returned but not requested\n"); - goto neg_exit; - } else if (rsp->DialectRevision == cpu_to_le16(SMB21_PROT_ID)) { - /* ops set to 3.0 by default for default so update */ - server->ops = &smb21_operations; - server->vals = &smb21_values; - } else if (rsp->DialectRevision == cpu_to_le16(SMB311_PROT_ID)) { - server->ops = &smb311_operations; - server->vals = &smb311_values; - } - } else if (le16_to_cpu(rsp->DialectRevision) != - server->vals->protocol_id) { - /* if requested single dialect ensure returned dialect matched */ - cifs_server_dbg(VFS, "Invalid 0x%x dialect returned: not requested\n", - le16_to_cpu(rsp->DialectRevision)); - goto neg_exit; - } - - cifs_dbg(FYI, "mode 0x%x\n", rsp->SecurityMode); - - if (rsp->DialectRevision == cpu_to_le16(SMB20_PROT_ID)) - cifs_dbg(FYI, "negotiated smb2.0 dialect\n"); - else if (rsp->DialectRevision == cpu_to_le16(SMB21_PROT_ID)) - cifs_dbg(FYI, "negotiated smb2.1 dialect\n"); - else if (rsp->DialectRevision == cpu_to_le16(SMB30_PROT_ID)) - cifs_dbg(FYI, "negotiated smb3.0 dialect\n"); - else if (rsp->DialectRevision == cpu_to_le16(SMB302_PROT_ID)) - cifs_dbg(FYI, "negotiated smb3.02 dialect\n"); - else if (rsp->DialectRevision == cpu_to_le16(SMB311_PROT_ID)) - cifs_dbg(FYI, "negotiated smb3.1.1 dialect\n"); - else { - cifs_server_dbg(VFS, "Invalid dialect returned by server 0x%x\n", - le16_to_cpu(rsp->DialectRevision)); - goto neg_exit; - } - - rc = 0; - server->dialect = le16_to_cpu(rsp->DialectRevision); - - /* - * Keep a copy of the hash after negprot. This hash will be - * the starting hash value for all sessions made from this - * server. - */ - memcpy(server->preauth_sha_hash, ses->preauth_sha_hash, - SMB2_PREAUTH_HASH_SIZE); - - /* SMB2 only has an extended negflavor */ - server->negflavor = CIFS_NEGFLAVOR_EXTENDED; - /* set it to the maximum buffer size value we can send with 1 credit */ - server->maxBuf = min_t(unsigned int, le32_to_cpu(rsp->MaxTransactSize), - SMB2_MAX_BUFFER_SIZE); - server->max_read = le32_to_cpu(rsp->MaxReadSize); - server->max_write = le32_to_cpu(rsp->MaxWriteSize); - server->sec_mode = le16_to_cpu(rsp->SecurityMode); - if ((server->sec_mode & SMB2_SEC_MODE_FLAGS_ALL) != server->sec_mode) - cifs_dbg(FYI, "Server returned unexpected security mode 0x%x\n", - server->sec_mode); - server->capabilities = le32_to_cpu(rsp->Capabilities); - /* Internal types */ - server->capabilities |= SMB2_NT_FIND | SMB2_LARGE_FILES; - - /* - * SMB3.0 supports only 1 cipher and doesn't have a encryption neg context - * Set the cipher type manually. - */ - if (server->dialect == SMB30_PROT_ID && (server->capabilities & SMB2_GLOBAL_CAP_ENCRYPTION)) - server->cipher_type = SMB2_ENCRYPTION_AES128_CCM; - - security_blob = smb2_get_data_area_len(&blob_offset, &blob_length, - (struct smb2_hdr *)rsp); - /* - * See MS-SMB2 section 2.2.4: if no blob, client picks default which - * for us will be - * ses->sectype = RawNTLMSSP; - * but for time being this is our only auth choice so doesn't matter. - * We just found a server which sets blob length to zero expecting raw. - */ - if (blob_length == 0) { - cifs_dbg(FYI, "missing security blob on negprot\n"); - server->sec_ntlmssp = true; - } - - rc = cifs_enable_signing(server, ses->sign); - if (rc) - goto neg_exit; - if (blob_length) { - rc = decode_negTokenInit(security_blob, blob_length, server); - if (rc == 1) - rc = 0; - else if (rc == 0) - rc = -EIO; - } - - if (rsp->DialectRevision == cpu_to_le16(SMB311_PROT_ID)) { - if (rsp->NegotiateContextCount) - rc = smb311_decode_neg_context(rsp, server, - rsp_iov.iov_len); - else - cifs_server_dbg(VFS, "Missing expected negotiate contexts\n"); - } -neg_exit: - free_rsp_buf(resp_buftype, rsp); - return rc; -} - -int smb3_validate_negotiate(const unsigned int xid, struct cifs_tcon *tcon) -{ - int rc; - struct validate_negotiate_info_req *pneg_inbuf; - struct validate_negotiate_info_rsp *pneg_rsp = NULL; - u32 rsplen; - u32 inbuflen; /* max of 4 dialects */ - struct TCP_Server_Info *server = tcon->ses->server; - - cifs_dbg(FYI, "validate negotiate\n"); - - /* In SMB3.11 preauth integrity supersedes validate negotiate */ - if (server->dialect == SMB311_PROT_ID) - return 0; - - /* - * validation ioctl must be signed, so no point sending this if we - * can not sign it (ie are not known user). Even if signing is not - * required (enabled but not negotiated), in those cases we selectively - * sign just this, the first and only signed request on a connection. - * Having validation of negotiate info helps reduce attack vectors. - */ - if (tcon->ses->session_flags & SMB2_SESSION_FLAG_IS_GUEST) - return 0; /* validation requires signing */ - - if (tcon->ses->user_name == NULL) { - cifs_dbg(FYI, "Can't validate negotiate: null user mount\n"); - return 0; /* validation requires signing */ - } - - if (tcon->ses->session_flags & SMB2_SESSION_FLAG_IS_NULL) - cifs_tcon_dbg(VFS, "Unexpected null user (anonymous) auth flag sent by server\n"); - - pneg_inbuf = kmalloc(sizeof(*pneg_inbuf), GFP_NOFS); - if (!pneg_inbuf) - return -ENOMEM; - - pneg_inbuf->Capabilities = - cpu_to_le32(server->vals->req_capabilities); - if (tcon->ses->chan_max > 1) - pneg_inbuf->Capabilities |= cpu_to_le32(SMB2_GLOBAL_CAP_MULTI_CHANNEL); - - memcpy(pneg_inbuf->Guid, server->client_guid, - SMB2_CLIENT_GUID_SIZE); - - if (tcon->ses->sign) - pneg_inbuf->SecurityMode = - cpu_to_le16(SMB2_NEGOTIATE_SIGNING_REQUIRED); - else if (global_secflags & CIFSSEC_MAY_SIGN) - pneg_inbuf->SecurityMode = - cpu_to_le16(SMB2_NEGOTIATE_SIGNING_ENABLED); - else - pneg_inbuf->SecurityMode = 0; - - - if (strcmp(server->vals->version_string, - SMB3ANY_VERSION_STRING) == 0) { - pneg_inbuf->Dialects[0] = cpu_to_le16(SMB30_PROT_ID); - pneg_inbuf->Dialects[1] = cpu_to_le16(SMB302_PROT_ID); - pneg_inbuf->Dialects[2] = cpu_to_le16(SMB311_PROT_ID); - pneg_inbuf->DialectCount = cpu_to_le16(3); - /* SMB 2.1 not included so subtract one dialect from len */ - inbuflen = sizeof(*pneg_inbuf) - - (sizeof(pneg_inbuf->Dialects[0])); - } else if (strcmp(server->vals->version_string, - SMBDEFAULT_VERSION_STRING) == 0) { - pneg_inbuf->Dialects[0] = cpu_to_le16(SMB21_PROT_ID); - pneg_inbuf->Dialects[1] = cpu_to_le16(SMB30_PROT_ID); - pneg_inbuf->Dialects[2] = cpu_to_le16(SMB302_PROT_ID); - pneg_inbuf->Dialects[3] = cpu_to_le16(SMB311_PROT_ID); - pneg_inbuf->DialectCount = cpu_to_le16(4); - /* structure is big enough for 4 dialects */ - inbuflen = sizeof(*pneg_inbuf); - } else { - /* otherwise specific dialect was requested */ - pneg_inbuf->Dialects[0] = - cpu_to_le16(server->vals->protocol_id); - pneg_inbuf->DialectCount = cpu_to_le16(1); - /* structure is big enough for 4 dialects, sending only 1 */ - inbuflen = sizeof(*pneg_inbuf) - - sizeof(pneg_inbuf->Dialects[0]) * 3; - } - - rc = SMB2_ioctl(xid, tcon, NO_FILE_ID, NO_FILE_ID, - FSCTL_VALIDATE_NEGOTIATE_INFO, - (char *)pneg_inbuf, inbuflen, CIFSMaxBufSize, - (char **)&pneg_rsp, &rsplen); - if (rc == -EOPNOTSUPP) { - /* - * Old Windows versions or Netapp SMB server can return - * not supported error. Client should accept it. - */ - cifs_tcon_dbg(VFS, "Server does not support validate negotiate\n"); - rc = 0; - goto out_free_inbuf; - } else if (rc != 0) { - cifs_tcon_dbg(VFS, "validate protocol negotiate failed: %d\n", - rc); - rc = -EIO; - goto out_free_inbuf; - } - - rc = -EIO; - if (rsplen != sizeof(*pneg_rsp)) { - cifs_tcon_dbg(VFS, "Invalid protocol negotiate response size: %d\n", - rsplen); - - /* relax check since Mac returns max bufsize allowed on ioctl */ - if (rsplen > CIFSMaxBufSize || rsplen < sizeof(*pneg_rsp)) - goto out_free_rsp; - } - - /* check validate negotiate info response matches what we got earlier */ - if (pneg_rsp->Dialect != cpu_to_le16(server->dialect)) - goto vneg_out; - - if (pneg_rsp->SecurityMode != cpu_to_le16(server->sec_mode)) - goto vneg_out; - - /* do not validate server guid because not saved at negprot time yet */ - - if ((le32_to_cpu(pneg_rsp->Capabilities) | SMB2_NT_FIND | - SMB2_LARGE_FILES) != server->capabilities) - goto vneg_out; - - /* validate negotiate successful */ - rc = 0; - cifs_dbg(FYI, "validate negotiate info successful\n"); - goto out_free_rsp; - -vneg_out: - cifs_tcon_dbg(VFS, "protocol revalidation - security settings mismatch\n"); -out_free_rsp: - kfree(pneg_rsp); -out_free_inbuf: - kfree(pneg_inbuf); - return rc; -} - -enum securityEnum -smb2_select_sectype(struct TCP_Server_Info *server, enum securityEnum requested) -{ - switch (requested) { - case Kerberos: - case RawNTLMSSP: - return requested; - case NTLMv2: - return RawNTLMSSP; - case Unspecified: - if (server->sec_ntlmssp && - (global_secflags & CIFSSEC_MAY_NTLMSSP)) - return RawNTLMSSP; - if ((server->sec_kerberos || server->sec_mskerberos) && - (global_secflags & CIFSSEC_MAY_KRB5)) - return Kerberos; - fallthrough; - default: - return Unspecified; - } -} - -struct SMB2_sess_data { - unsigned int xid; - struct cifs_ses *ses; - struct TCP_Server_Info *server; - struct nls_table *nls_cp; - void (*func)(struct SMB2_sess_data *); - int result; - u64 previous_session; - - /* we will send the SMB in three pieces: - * a fixed length beginning part, an optional - * SPNEGO blob (which can be zero length), and a - * last part which will include the strings - * and rest of bcc area. This allows us to avoid - * a large buffer 17K allocation - */ - int buf0_type; - struct kvec iov[2]; -}; - -static int -SMB2_sess_alloc_buffer(struct SMB2_sess_data *sess_data) -{ - int rc; - struct cifs_ses *ses = sess_data->ses; - struct TCP_Server_Info *server = sess_data->server; - struct smb2_sess_setup_req *req; - unsigned int total_len; - bool is_binding = false; - - rc = smb2_plain_req_init(SMB2_SESSION_SETUP, NULL, server, - (void **) &req, - &total_len); - if (rc) - return rc; - - spin_lock(&ses->ses_lock); - is_binding = (ses->ses_status == SES_GOOD); - spin_unlock(&ses->ses_lock); - - if (is_binding) { - req->hdr.SessionId = cpu_to_le64(ses->Suid); - req->hdr.Flags |= SMB2_FLAGS_SIGNED; - req->PreviousSessionId = 0; - req->Flags = SMB2_SESSION_REQ_FLAG_BINDING; - cifs_dbg(FYI, "Binding to sess id: %llx\n", ses->Suid); - } else { - /* First session, not a reauthenticate */ - req->hdr.SessionId = 0; - /* - * if reconnect, we need to send previous sess id - * otherwise it is 0 - */ - req->PreviousSessionId = cpu_to_le64(sess_data->previous_session); - req->Flags = 0; /* MBZ */ - cifs_dbg(FYI, "Fresh session. Previous: %llx\n", - sess_data->previous_session); - } - - /* enough to enable echos and oplocks and one max size write */ - req->hdr.CreditRequest = cpu_to_le16(130); - - /* only one of SMB2 signing flags may be set in SMB2 request */ - if (server->sign) - req->SecurityMode = SMB2_NEGOTIATE_SIGNING_REQUIRED; - else if (global_secflags & CIFSSEC_MAY_SIGN) /* one flag unlike MUST_ */ - req->SecurityMode = SMB2_NEGOTIATE_SIGNING_ENABLED; - else - req->SecurityMode = 0; - -#ifdef CONFIG_CIFS_DFS_UPCALL - req->Capabilities = cpu_to_le32(SMB2_GLOBAL_CAP_DFS); -#else - req->Capabilities = 0; -#endif /* DFS_UPCALL */ - - req->Channel = 0; /* MBZ */ - - sess_data->iov[0].iov_base = (char *)req; - /* 1 for pad */ - sess_data->iov[0].iov_len = total_len - 1; - /* - * This variable will be used to clear the buffer - * allocated above in case of any error in the calling function. - */ - sess_data->buf0_type = CIFS_SMALL_BUFFER; - - return 0; -} - -static void -SMB2_sess_free_buffer(struct SMB2_sess_data *sess_data) -{ - struct kvec *iov = sess_data->iov; - - /* iov[1] is already freed by caller */ - if (sess_data->buf0_type != CIFS_NO_BUFFER && iov[0].iov_base) - memzero_explicit(iov[0].iov_base, iov[0].iov_len); - - free_rsp_buf(sess_data->buf0_type, iov[0].iov_base); - sess_data->buf0_type = CIFS_NO_BUFFER; -} - -static int -SMB2_sess_sendreceive(struct SMB2_sess_data *sess_data) -{ - int rc; - struct smb_rqst rqst; - struct smb2_sess_setup_req *req = sess_data->iov[0].iov_base; - struct kvec rsp_iov = { NULL, 0 }; - - /* Testing shows that buffer offset must be at location of Buffer[0] */ - req->SecurityBufferOffset = - cpu_to_le16(sizeof(struct smb2_sess_setup_req) - 1 /* pad */); - req->SecurityBufferLength = cpu_to_le16(sess_data->iov[1].iov_len); - - memset(&rqst, 0, sizeof(struct smb_rqst)); - rqst.rq_iov = sess_data->iov; - rqst.rq_nvec = 2; - - /* BB add code to build os and lm fields */ - rc = cifs_send_recv(sess_data->xid, sess_data->ses, - sess_data->server, - &rqst, - &sess_data->buf0_type, - CIFS_LOG_ERROR | CIFS_SESS_OP, &rsp_iov); - cifs_small_buf_release(sess_data->iov[0].iov_base); - memcpy(&sess_data->iov[0], &rsp_iov, sizeof(struct kvec)); - - return rc; -} - -static int -SMB2_sess_establish_session(struct SMB2_sess_data *sess_data) -{ - int rc = 0; - struct cifs_ses *ses = sess_data->ses; - struct TCP_Server_Info *server = sess_data->server; - - cifs_server_lock(server); - if (server->ops->generate_signingkey) { - rc = server->ops->generate_signingkey(ses, server); - if (rc) { - cifs_dbg(FYI, - "SMB3 session key generation failed\n"); - cifs_server_unlock(server); - return rc; - } - } - if (!server->session_estab) { - server->sequence_number = 0x2; - server->session_estab = true; - } - cifs_server_unlock(server); - - cifs_dbg(FYI, "SMB2/3 session established successfully\n"); - return rc; -} - -#ifdef CONFIG_CIFS_UPCALL -static void -SMB2_auth_kerberos(struct SMB2_sess_data *sess_data) -{ - int rc; - struct cifs_ses *ses = sess_data->ses; - struct TCP_Server_Info *server = sess_data->server; - struct cifs_spnego_msg *msg; - struct key *spnego_key = NULL; - struct smb2_sess_setup_rsp *rsp = NULL; - bool is_binding = false; - - rc = SMB2_sess_alloc_buffer(sess_data); - if (rc) - goto out; - - spnego_key = cifs_get_spnego_key(ses, server); - if (IS_ERR(spnego_key)) { - rc = PTR_ERR(spnego_key); - if (rc == -ENOKEY) - cifs_dbg(VFS, "Verify user has a krb5 ticket and keyutils is installed\n"); - spnego_key = NULL; - goto out; - } - - msg = spnego_key->payload.data[0]; - /* - * check version field to make sure that cifs.upcall is - * sending us a response in an expected form - */ - if (msg->version != CIFS_SPNEGO_UPCALL_VERSION) { - cifs_dbg(VFS, "bad cifs.upcall version. Expected %d got %d\n", - CIFS_SPNEGO_UPCALL_VERSION, msg->version); - rc = -EKEYREJECTED; - goto out_put_spnego_key; - } - - spin_lock(&ses->ses_lock); - is_binding = (ses->ses_status == SES_GOOD); - spin_unlock(&ses->ses_lock); - - /* keep session key if binding */ - if (!is_binding) { - kfree_sensitive(ses->auth_key.response); - ses->auth_key.response = kmemdup(msg->data, msg->sesskey_len, - GFP_KERNEL); - if (!ses->auth_key.response) { - cifs_dbg(VFS, "Kerberos can't allocate (%u bytes) memory\n", - msg->sesskey_len); - rc = -ENOMEM; - goto out_put_spnego_key; - } - ses->auth_key.len = msg->sesskey_len; - } - - sess_data->iov[1].iov_base = msg->data + msg->sesskey_len; - sess_data->iov[1].iov_len = msg->secblob_len; - - rc = SMB2_sess_sendreceive(sess_data); - if (rc) - goto out_put_spnego_key; - - rsp = (struct smb2_sess_setup_rsp *)sess_data->iov[0].iov_base; - /* keep session id and flags if binding */ - if (!is_binding) { - ses->Suid = le64_to_cpu(rsp->hdr.SessionId); - ses->session_flags = le16_to_cpu(rsp->SessionFlags); - } - - rc = SMB2_sess_establish_session(sess_data); -out_put_spnego_key: - key_invalidate(spnego_key); - key_put(spnego_key); - if (rc) { - kfree_sensitive(ses->auth_key.response); - ses->auth_key.response = NULL; - ses->auth_key.len = 0; - } -out: - sess_data->result = rc; - sess_data->func = NULL; - SMB2_sess_free_buffer(sess_data); -} -#else -static void -SMB2_auth_kerberos(struct SMB2_sess_data *sess_data) -{ - cifs_dbg(VFS, "Kerberos negotiated but upcall support disabled!\n"); - sess_data->result = -EOPNOTSUPP; - sess_data->func = NULL; -} -#endif - -static void -SMB2_sess_auth_rawntlmssp_authenticate(struct SMB2_sess_data *sess_data); - -static void -SMB2_sess_auth_rawntlmssp_negotiate(struct SMB2_sess_data *sess_data) -{ - int rc; - struct cifs_ses *ses = sess_data->ses; - struct TCP_Server_Info *server = sess_data->server; - struct smb2_sess_setup_rsp *rsp = NULL; - unsigned char *ntlmssp_blob = NULL; - bool use_spnego = false; /* else use raw ntlmssp */ - u16 blob_length = 0; - bool is_binding = false; - - /* - * If memory allocation is successful, caller of this function - * frees it. - */ - ses->ntlmssp = kmalloc(sizeof(struct ntlmssp_auth), GFP_KERNEL); - if (!ses->ntlmssp) { - rc = -ENOMEM; - goto out_err; - } - ses->ntlmssp->sesskey_per_smbsess = true; - - rc = SMB2_sess_alloc_buffer(sess_data); - if (rc) - goto out_err; - - rc = build_ntlmssp_smb3_negotiate_blob(&ntlmssp_blob, - &blob_length, ses, server, - sess_data->nls_cp); - if (rc) - goto out; - - if (use_spnego) { - /* BB eventually need to add this */ - cifs_dbg(VFS, "spnego not supported for SMB2 yet\n"); - rc = -EOPNOTSUPP; - goto out; - } - sess_data->iov[1].iov_base = ntlmssp_blob; - sess_data->iov[1].iov_len = blob_length; - - rc = SMB2_sess_sendreceive(sess_data); - rsp = (struct smb2_sess_setup_rsp *)sess_data->iov[0].iov_base; - - /* If true, rc here is expected and not an error */ - if (sess_data->buf0_type != CIFS_NO_BUFFER && - rsp->hdr.Status == STATUS_MORE_PROCESSING_REQUIRED) - rc = 0; - - if (rc) - goto out; - - if (offsetof(struct smb2_sess_setup_rsp, Buffer) != - le16_to_cpu(rsp->SecurityBufferOffset)) { - cifs_dbg(VFS, "Invalid security buffer offset %d\n", - le16_to_cpu(rsp->SecurityBufferOffset)); - rc = -EIO; - goto out; - } - rc = decode_ntlmssp_challenge(rsp->Buffer, - le16_to_cpu(rsp->SecurityBufferLength), ses); - if (rc) - goto out; - - cifs_dbg(FYI, "rawntlmssp session setup challenge phase\n"); - - spin_lock(&ses->ses_lock); - is_binding = (ses->ses_status == SES_GOOD); - spin_unlock(&ses->ses_lock); - - /* keep existing ses id and flags if binding */ - if (!is_binding) { - ses->Suid = le64_to_cpu(rsp->hdr.SessionId); - ses->session_flags = le16_to_cpu(rsp->SessionFlags); - } - -out: - kfree_sensitive(ntlmssp_blob); - SMB2_sess_free_buffer(sess_data); - if (!rc) { - sess_data->result = 0; - sess_data->func = SMB2_sess_auth_rawntlmssp_authenticate; - return; - } -out_err: - kfree_sensitive(ses->ntlmssp); - ses->ntlmssp = NULL; - sess_data->result = rc; - sess_data->func = NULL; -} - -static void -SMB2_sess_auth_rawntlmssp_authenticate(struct SMB2_sess_data *sess_data) -{ - int rc; - struct cifs_ses *ses = sess_data->ses; - struct TCP_Server_Info *server = sess_data->server; - struct smb2_sess_setup_req *req; - struct smb2_sess_setup_rsp *rsp = NULL; - unsigned char *ntlmssp_blob = NULL; - bool use_spnego = false; /* else use raw ntlmssp */ - u16 blob_length = 0; - bool is_binding = false; - - rc = SMB2_sess_alloc_buffer(sess_data); - if (rc) - goto out; - - req = (struct smb2_sess_setup_req *) sess_data->iov[0].iov_base; - req->hdr.SessionId = cpu_to_le64(ses->Suid); - - rc = build_ntlmssp_auth_blob(&ntlmssp_blob, &blob_length, - ses, server, - sess_data->nls_cp); - if (rc) { - cifs_dbg(FYI, "build_ntlmssp_auth_blob failed %d\n", rc); - goto out; - } - - if (use_spnego) { - /* BB eventually need to add this */ - cifs_dbg(VFS, "spnego not supported for SMB2 yet\n"); - rc = -EOPNOTSUPP; - goto out; - } - sess_data->iov[1].iov_base = ntlmssp_blob; - sess_data->iov[1].iov_len = blob_length; - - rc = SMB2_sess_sendreceive(sess_data); - if (rc) - goto out; - - rsp = (struct smb2_sess_setup_rsp *)sess_data->iov[0].iov_base; - - spin_lock(&ses->ses_lock); - is_binding = (ses->ses_status == SES_GOOD); - spin_unlock(&ses->ses_lock); - - /* keep existing ses id and flags if binding */ - if (!is_binding) { - ses->Suid = le64_to_cpu(rsp->hdr.SessionId); - ses->session_flags = le16_to_cpu(rsp->SessionFlags); - } - - rc = SMB2_sess_establish_session(sess_data); -#ifdef CONFIG_CIFS_DEBUG_DUMP_KEYS - if (ses->server->dialect < SMB30_PROT_ID) { - cifs_dbg(VFS, "%s: dumping generated SMB2 session keys\n", __func__); - /* - * The session id is opaque in terms of endianness, so we can't - * print it as a long long. we dump it as we got it on the wire - */ - cifs_dbg(VFS, "Session Id %*ph\n", (int)sizeof(ses->Suid), - &ses->Suid); - cifs_dbg(VFS, "Session Key %*ph\n", - SMB2_NTLMV2_SESSKEY_SIZE, ses->auth_key.response); - cifs_dbg(VFS, "Signing Key %*ph\n", - SMB3_SIGN_KEY_SIZE, ses->auth_key.response); - } -#endif -out: - kfree_sensitive(ntlmssp_blob); - SMB2_sess_free_buffer(sess_data); - kfree_sensitive(ses->ntlmssp); - ses->ntlmssp = NULL; - sess_data->result = rc; - sess_data->func = NULL; -} - -static int -SMB2_select_sec(struct SMB2_sess_data *sess_data) -{ - int type; - struct cifs_ses *ses = sess_data->ses; - struct TCP_Server_Info *server = sess_data->server; - - type = smb2_select_sectype(server, ses->sectype); - cifs_dbg(FYI, "sess setup type %d\n", type); - if (type == Unspecified) { - cifs_dbg(VFS, "Unable to select appropriate authentication method!\n"); - return -EINVAL; - } - - switch (type) { - case Kerberos: - sess_data->func = SMB2_auth_kerberos; - break; - case RawNTLMSSP: - sess_data->func = SMB2_sess_auth_rawntlmssp_negotiate; - break; - default: - cifs_dbg(VFS, "secType %d not supported!\n", type); - return -EOPNOTSUPP; - } - - return 0; -} - -int -SMB2_sess_setup(const unsigned int xid, struct cifs_ses *ses, - struct TCP_Server_Info *server, - const struct nls_table *nls_cp) -{ - int rc = 0; - struct SMB2_sess_data *sess_data; - - cifs_dbg(FYI, "Session Setup\n"); - - if (!server) { - WARN(1, "%s: server is NULL!\n", __func__); - return -EIO; - } - - sess_data = kzalloc(sizeof(struct SMB2_sess_data), GFP_KERNEL); - if (!sess_data) - return -ENOMEM; - - sess_data->xid = xid; - sess_data->ses = ses; - sess_data->server = server; - sess_data->buf0_type = CIFS_NO_BUFFER; - sess_data->nls_cp = (struct nls_table *) nls_cp; - sess_data->previous_session = ses->Suid; - - rc = SMB2_select_sec(sess_data); - if (rc) - goto out; - - /* - * Initialize the session hash with the server one. - */ - memcpy(ses->preauth_sha_hash, server->preauth_sha_hash, - SMB2_PREAUTH_HASH_SIZE); - - while (sess_data->func) - sess_data->func(sess_data); - - if ((ses->session_flags & SMB2_SESSION_FLAG_IS_GUEST) && (ses->sign)) - cifs_server_dbg(VFS, "signing requested but authenticated as guest\n"); - rc = sess_data->result; -out: - kfree_sensitive(sess_data); - return rc; -} - -int -SMB2_logoff(const unsigned int xid, struct cifs_ses *ses) -{ - struct smb_rqst rqst; - struct smb2_logoff_req *req; /* response is also trivial struct */ - int rc = 0; - struct TCP_Server_Info *server; - int flags = 0; - unsigned int total_len; - struct kvec iov[1]; - struct kvec rsp_iov; - int resp_buf_type; - - cifs_dbg(FYI, "disconnect session %p\n", ses); - - if (ses && (ses->server)) - server = ses->server; - else - return -EIO; - - /* no need to send SMB logoff if uid already closed due to reconnect */ - spin_lock(&ses->chan_lock); - if (CIFS_ALL_CHANS_NEED_RECONNECT(ses)) { - spin_unlock(&ses->chan_lock); - goto smb2_session_already_dead; - } - spin_unlock(&ses->chan_lock); - - rc = smb2_plain_req_init(SMB2_LOGOFF, NULL, ses->server, - (void **) &req, &total_len); - if (rc) - return rc; - - /* since no tcon, smb2_init can not do this, so do here */ - req->hdr.SessionId = cpu_to_le64(ses->Suid); - - if (ses->session_flags & SMB2_SESSION_FLAG_ENCRYPT_DATA) - flags |= CIFS_TRANSFORM_REQ; - else if (server->sign) - req->hdr.Flags |= SMB2_FLAGS_SIGNED; - - flags |= CIFS_NO_RSP_BUF; - - iov[0].iov_base = (char *)req; - iov[0].iov_len = total_len; - - memset(&rqst, 0, sizeof(struct smb_rqst)); - rqst.rq_iov = iov; - rqst.rq_nvec = 1; - - rc = cifs_send_recv(xid, ses, ses->server, - &rqst, &resp_buf_type, flags, &rsp_iov); - cifs_small_buf_release(req); - /* - * No tcon so can't do - * cifs_stats_inc(&tcon->stats.smb2_stats.smb2_com_fail[SMB2...]); - */ - -smb2_session_already_dead: - return rc; -} - -static inline void cifs_stats_fail_inc(struct cifs_tcon *tcon, uint16_t code) -{ - cifs_stats_inc(&tcon->stats.smb2_stats.smb2_com_failed[code]); -} - -#define MAX_SHARENAME_LENGTH (255 /* server */ + 80 /* share */ + 1 /* NULL */) - -/* These are similar values to what Windows uses */ -static inline void init_copy_chunk_defaults(struct cifs_tcon *tcon) -{ - tcon->max_chunks = 256; - tcon->max_bytes_chunk = 1048576; - tcon->max_bytes_copy = 16777216; -} - -int -SMB2_tcon(const unsigned int xid, struct cifs_ses *ses, const char *tree, - struct cifs_tcon *tcon, const struct nls_table *cp) -{ - struct smb_rqst rqst; - struct smb2_tree_connect_req *req; - struct smb2_tree_connect_rsp *rsp = NULL; - struct kvec iov[2]; - struct kvec rsp_iov = { NULL, 0 }; - int rc = 0; - int resp_buftype; - int unc_path_len; - __le16 *unc_path = NULL; - int flags = 0; - unsigned int total_len; - struct TCP_Server_Info *server; - - /* always use master channel */ - server = ses->server; - - cifs_dbg(FYI, "TCON\n"); - - if (!server || !tree) - return -EIO; - - unc_path = kmalloc(MAX_SHARENAME_LENGTH * 2, GFP_KERNEL); - if (unc_path == NULL) - return -ENOMEM; - - unc_path_len = cifs_strtoUTF16(unc_path, tree, strlen(tree), cp) + 1; - unc_path_len *= 2; - if (unc_path_len < 2) { - kfree(unc_path); - return -EINVAL; - } - - /* SMB2 TREE_CONNECT request must be called with TreeId == 0 */ - tcon->tid = 0; - atomic_set(&tcon->num_remote_opens, 0); - rc = smb2_plain_req_init(SMB2_TREE_CONNECT, tcon, server, - (void **) &req, &total_len); - if (rc) { - kfree(unc_path); - return rc; - } - - if (smb3_encryption_required(tcon)) - flags |= CIFS_TRANSFORM_REQ; - - iov[0].iov_base = (char *)req; - /* 1 for pad */ - iov[0].iov_len = total_len - 1; - - /* Testing shows that buffer offset must be at location of Buffer[0] */ - req->PathOffset = cpu_to_le16(sizeof(struct smb2_tree_connect_req) - - 1 /* pad */); - req->PathLength = cpu_to_le16(unc_path_len - 2); - iov[1].iov_base = unc_path; - iov[1].iov_len = unc_path_len; - - /* - * 3.11 tcon req must be signed if not encrypted. See MS-SMB2 3.2.4.1.1 - * unless it is guest or anonymous user. See MS-SMB2 3.2.5.3.1 - * (Samba servers don't always set the flag so also check if null user) - */ - if ((server->dialect == SMB311_PROT_ID) && - !smb3_encryption_required(tcon) && - !(ses->session_flags & - (SMB2_SESSION_FLAG_IS_GUEST|SMB2_SESSION_FLAG_IS_NULL)) && - ((ses->user_name != NULL) || (ses->sectype == Kerberos))) - req->hdr.Flags |= SMB2_FLAGS_SIGNED; - - memset(&rqst, 0, sizeof(struct smb_rqst)); - rqst.rq_iov = iov; - rqst.rq_nvec = 2; - - /* Need 64 for max size write so ask for more in case not there yet */ - req->hdr.CreditRequest = cpu_to_le16(64); - - rc = cifs_send_recv(xid, ses, server, - &rqst, &resp_buftype, flags, &rsp_iov); - cifs_small_buf_release(req); - rsp = (struct smb2_tree_connect_rsp *)rsp_iov.iov_base; - trace_smb3_tcon(xid, tcon->tid, ses->Suid, tree, rc); - if ((rc != 0) || (rsp == NULL)) { - cifs_stats_fail_inc(tcon, SMB2_TREE_CONNECT_HE); - tcon->need_reconnect = true; - goto tcon_error_exit; - } - - switch (rsp->ShareType) { - case SMB2_SHARE_TYPE_DISK: - cifs_dbg(FYI, "connection to disk share\n"); - break; - case SMB2_SHARE_TYPE_PIPE: - tcon->pipe = true; - cifs_dbg(FYI, "connection to pipe share\n"); - break; - case SMB2_SHARE_TYPE_PRINT: - tcon->print = true; - cifs_dbg(FYI, "connection to printer\n"); - break; - default: - cifs_server_dbg(VFS, "unknown share type %d\n", rsp->ShareType); - rc = -EOPNOTSUPP; - goto tcon_error_exit; - } - - tcon->share_flags = le32_to_cpu(rsp->ShareFlags); - tcon->capabilities = rsp->Capabilities; /* we keep caps little endian */ - tcon->maximal_access = le32_to_cpu(rsp->MaximalAccess); - tcon->tid = le32_to_cpu(rsp->hdr.Id.SyncId.TreeId); - strscpy(tcon->tree_name, tree, sizeof(tcon->tree_name)); - - if ((rsp->Capabilities & SMB2_SHARE_CAP_DFS) && - ((tcon->share_flags & SHI1005_FLAGS_DFS) == 0)) - cifs_tcon_dbg(VFS, "DFS capability contradicts DFS flag\n"); - - if (tcon->seal && - !(server->capabilities & SMB2_GLOBAL_CAP_ENCRYPTION)) - cifs_tcon_dbg(VFS, "Encryption is requested but not supported\n"); - - init_copy_chunk_defaults(tcon); - if (server->ops->validate_negotiate) - rc = server->ops->validate_negotiate(xid, tcon); -tcon_exit: - - free_rsp_buf(resp_buftype, rsp); - kfree(unc_path); - return rc; - -tcon_error_exit: - if (rsp && rsp->hdr.Status == STATUS_BAD_NETWORK_NAME) - cifs_tcon_dbg(VFS, "BAD_NETWORK_NAME: %s\n", tree); - goto tcon_exit; -} - -int -SMB2_tdis(const unsigned int xid, struct cifs_tcon *tcon) -{ - struct smb_rqst rqst; - struct smb2_tree_disconnect_req *req; /* response is trivial */ - int rc = 0; - struct cifs_ses *ses = tcon->ses; - int flags = 0; - unsigned int total_len; - struct kvec iov[1]; - struct kvec rsp_iov; - int resp_buf_type; - - cifs_dbg(FYI, "Tree Disconnect\n"); - - if (!ses || !(ses->server)) - return -EIO; - - trace_smb3_tdis_enter(xid, tcon->tid, ses->Suid, tcon->tree_name); - spin_lock(&ses->chan_lock); - if ((tcon->need_reconnect) || - (CIFS_ALL_CHANS_NEED_RECONNECT(tcon->ses))) { - spin_unlock(&ses->chan_lock); - return 0; - } - spin_unlock(&ses->chan_lock); - - invalidate_all_cached_dirs(tcon); - - rc = smb2_plain_req_init(SMB2_TREE_DISCONNECT, tcon, ses->server, - (void **) &req, - &total_len); - if (rc) - return rc; - - if (smb3_encryption_required(tcon)) - flags |= CIFS_TRANSFORM_REQ; - - flags |= CIFS_NO_RSP_BUF; - - iov[0].iov_base = (char *)req; - iov[0].iov_len = total_len; - - memset(&rqst, 0, sizeof(struct smb_rqst)); - rqst.rq_iov = iov; - rqst.rq_nvec = 1; - - rc = cifs_send_recv(xid, ses, ses->server, - &rqst, &resp_buf_type, flags, &rsp_iov); - cifs_small_buf_release(req); - if (rc) { - cifs_stats_fail_inc(tcon, SMB2_TREE_DISCONNECT_HE); - trace_smb3_tdis_err(xid, tcon->tid, ses->Suid, rc); - } - trace_smb3_tdis_done(xid, tcon->tid, ses->Suid); - - return rc; -} - - -static struct create_durable * -create_durable_buf(void) -{ - struct create_durable *buf; - - buf = kzalloc(sizeof(struct create_durable), GFP_KERNEL); - if (!buf) - return NULL; - - buf->ccontext.DataOffset = cpu_to_le16(offsetof - (struct create_durable, Data)); - buf->ccontext.DataLength = cpu_to_le32(16); - buf->ccontext.NameOffset = cpu_to_le16(offsetof - (struct create_durable, Name)); - buf->ccontext.NameLength = cpu_to_le16(4); - /* SMB2_CREATE_DURABLE_HANDLE_REQUEST is "DHnQ" */ - buf->Name[0] = 'D'; - buf->Name[1] = 'H'; - buf->Name[2] = 'n'; - buf->Name[3] = 'Q'; - return buf; -} - -static struct create_durable * -create_reconnect_durable_buf(struct cifs_fid *fid) -{ - struct create_durable *buf; - - buf = kzalloc(sizeof(struct create_durable), GFP_KERNEL); - if (!buf) - return NULL; - - buf->ccontext.DataOffset = cpu_to_le16(offsetof - (struct create_durable, Data)); - buf->ccontext.DataLength = cpu_to_le32(16); - buf->ccontext.NameOffset = cpu_to_le16(offsetof - (struct create_durable, Name)); - buf->ccontext.NameLength = cpu_to_le16(4); - buf->Data.Fid.PersistentFileId = fid->persistent_fid; - buf->Data.Fid.VolatileFileId = fid->volatile_fid; - /* SMB2_CREATE_DURABLE_HANDLE_RECONNECT is "DHnC" */ - buf->Name[0] = 'D'; - buf->Name[1] = 'H'; - buf->Name[2] = 'n'; - buf->Name[3] = 'C'; - return buf; -} - -static void -parse_query_id_ctxt(struct create_context *cc, struct smb2_file_all_info *buf) -{ - struct create_on_disk_id *pdisk_id = (struct create_on_disk_id *)cc; - - cifs_dbg(FYI, "parse query id context 0x%llx 0x%llx\n", - pdisk_id->DiskFileId, pdisk_id->VolumeId); - buf->IndexNumber = pdisk_id->DiskFileId; -} - -static void -parse_posix_ctxt(struct create_context *cc, struct smb2_file_all_info *info, - struct create_posix_rsp *posix) -{ - int sid_len; - u8 *beg = (u8 *)cc + le16_to_cpu(cc->DataOffset); - u8 *end = beg + le32_to_cpu(cc->DataLength); - u8 *sid; - - memset(posix, 0, sizeof(*posix)); - - posix->nlink = le32_to_cpu(*(__le32 *)(beg + 0)); - posix->reparse_tag = le32_to_cpu(*(__le32 *)(beg + 4)); - posix->mode = le32_to_cpu(*(__le32 *)(beg + 8)); - - sid = beg + 12; - sid_len = posix_info_sid_size(sid, end); - if (sid_len < 0) { - cifs_dbg(VFS, "bad owner sid in posix create response\n"); - return; - } - memcpy(&posix->owner, sid, sid_len); - - sid = sid + sid_len; - sid_len = posix_info_sid_size(sid, end); - if (sid_len < 0) { - cifs_dbg(VFS, "bad group sid in posix create response\n"); - return; - } - memcpy(&posix->group, sid, sid_len); - - cifs_dbg(FYI, "nlink=%d mode=%o reparse_tag=%x\n", - posix->nlink, posix->mode, posix->reparse_tag); -} - -void -smb2_parse_contexts(struct TCP_Server_Info *server, - struct smb2_create_rsp *rsp, - unsigned int *epoch, char *lease_key, __u8 *oplock, - struct smb2_file_all_info *buf, - struct create_posix_rsp *posix) -{ - char *data_offset; - struct create_context *cc; - unsigned int next; - unsigned int remaining; - char *name; - static const char smb3_create_tag_posix[] = { - 0x93, 0xAD, 0x25, 0x50, 0x9C, - 0xB4, 0x11, 0xE7, 0xB4, 0x23, 0x83, - 0xDE, 0x96, 0x8B, 0xCD, 0x7C - }; - - *oplock = 0; - data_offset = (char *)rsp + le32_to_cpu(rsp->CreateContextsOffset); - remaining = le32_to_cpu(rsp->CreateContextsLength); - cc = (struct create_context *)data_offset; - - /* Initialize inode number to 0 in case no valid data in qfid context */ - if (buf) - buf->IndexNumber = 0; - - while (remaining >= sizeof(struct create_context)) { - name = le16_to_cpu(cc->NameOffset) + (char *)cc; - if (le16_to_cpu(cc->NameLength) == 4 && - strncmp(name, SMB2_CREATE_REQUEST_LEASE, 4) == 0) - *oplock = server->ops->parse_lease_buf(cc, epoch, - lease_key); - else if (buf && (le16_to_cpu(cc->NameLength) == 4) && - strncmp(name, SMB2_CREATE_QUERY_ON_DISK_ID, 4) == 0) - parse_query_id_ctxt(cc, buf); - else if ((le16_to_cpu(cc->NameLength) == 16)) { - if (posix && - memcmp(name, smb3_create_tag_posix, 16) == 0) - parse_posix_ctxt(cc, buf, posix); - } - /* else { - cifs_dbg(FYI, "Context not matched with len %d\n", - le16_to_cpu(cc->NameLength)); - cifs_dump_mem("Cctxt name: ", name, 4); - } */ - - next = le32_to_cpu(cc->Next); - if (!next) - break; - remaining -= next; - cc = (struct create_context *)((char *)cc + next); - } - - if (rsp->OplockLevel != SMB2_OPLOCK_LEVEL_LEASE) - *oplock = rsp->OplockLevel; - - return; -} - -static int -add_lease_context(struct TCP_Server_Info *server, struct kvec *iov, - unsigned int *num_iovec, u8 *lease_key, __u8 *oplock) -{ - struct smb2_create_req *req = iov[0].iov_base; - unsigned int num = *num_iovec; - - iov[num].iov_base = server->ops->create_lease_buf(lease_key, *oplock); - if (iov[num].iov_base == NULL) - return -ENOMEM; - iov[num].iov_len = server->vals->create_lease_size; - req->RequestedOplockLevel = SMB2_OPLOCK_LEVEL_LEASE; - if (!req->CreateContextsOffset) - req->CreateContextsOffset = cpu_to_le32( - sizeof(struct smb2_create_req) + - iov[num - 1].iov_len); - le32_add_cpu(&req->CreateContextsLength, - server->vals->create_lease_size); - *num_iovec = num + 1; - return 0; -} - -static struct create_durable_v2 * -create_durable_v2_buf(struct cifs_open_parms *oparms) -{ - struct cifs_fid *pfid = oparms->fid; - struct create_durable_v2 *buf; - - buf = kzalloc(sizeof(struct create_durable_v2), GFP_KERNEL); - if (!buf) - return NULL; - - buf->ccontext.DataOffset = cpu_to_le16(offsetof - (struct create_durable_v2, dcontext)); - buf->ccontext.DataLength = cpu_to_le32(sizeof(struct durable_context_v2)); - buf->ccontext.NameOffset = cpu_to_le16(offsetof - (struct create_durable_v2, Name)); - buf->ccontext.NameLength = cpu_to_le16(4); - - /* - * NB: Handle timeout defaults to 0, which allows server to choose - * (most servers default to 120 seconds) and most clients default to 0. - * This can be overridden at mount ("handletimeout=") if the user wants - * a different persistent (or resilient) handle timeout for all opens - * opens on a particular SMB3 mount. - */ - buf->dcontext.Timeout = cpu_to_le32(oparms->tcon->handle_timeout); - buf->dcontext.Flags = cpu_to_le32(SMB2_DHANDLE_FLAG_PERSISTENT); - generate_random_uuid(buf->dcontext.CreateGuid); - memcpy(pfid->create_guid, buf->dcontext.CreateGuid, 16); - - /* SMB2_CREATE_DURABLE_HANDLE_REQUEST is "DH2Q" */ - buf->Name[0] = 'D'; - buf->Name[1] = 'H'; - buf->Name[2] = '2'; - buf->Name[3] = 'Q'; - return buf; -} - -static struct create_durable_handle_reconnect_v2 * -create_reconnect_durable_v2_buf(struct cifs_fid *fid) -{ - struct create_durable_handle_reconnect_v2 *buf; - - buf = kzalloc(sizeof(struct create_durable_handle_reconnect_v2), - GFP_KERNEL); - if (!buf) - return NULL; - - buf->ccontext.DataOffset = - cpu_to_le16(offsetof(struct create_durable_handle_reconnect_v2, - dcontext)); - buf->ccontext.DataLength = - cpu_to_le32(sizeof(struct durable_reconnect_context_v2)); - buf->ccontext.NameOffset = - cpu_to_le16(offsetof(struct create_durable_handle_reconnect_v2, - Name)); - buf->ccontext.NameLength = cpu_to_le16(4); - - buf->dcontext.Fid.PersistentFileId = fid->persistent_fid; - buf->dcontext.Fid.VolatileFileId = fid->volatile_fid; - buf->dcontext.Flags = cpu_to_le32(SMB2_DHANDLE_FLAG_PERSISTENT); - memcpy(buf->dcontext.CreateGuid, fid->create_guid, 16); - - /* SMB2_CREATE_DURABLE_HANDLE_RECONNECT_V2 is "DH2C" */ - buf->Name[0] = 'D'; - buf->Name[1] = 'H'; - buf->Name[2] = '2'; - buf->Name[3] = 'C'; - return buf; -} - -static int -add_durable_v2_context(struct kvec *iov, unsigned int *num_iovec, - struct cifs_open_parms *oparms) -{ - struct smb2_create_req *req = iov[0].iov_base; - unsigned int num = *num_iovec; - - iov[num].iov_base = create_durable_v2_buf(oparms); - if (iov[num].iov_base == NULL) - return -ENOMEM; - iov[num].iov_len = sizeof(struct create_durable_v2); - if (!req->CreateContextsOffset) - req->CreateContextsOffset = - cpu_to_le32(sizeof(struct smb2_create_req) + - iov[1].iov_len); - le32_add_cpu(&req->CreateContextsLength, sizeof(struct create_durable_v2)); - *num_iovec = num + 1; - return 0; -} - -static int -add_durable_reconnect_v2_context(struct kvec *iov, unsigned int *num_iovec, - struct cifs_open_parms *oparms) -{ - struct smb2_create_req *req = iov[0].iov_base; - unsigned int num = *num_iovec; - - /* indicate that we don't need to relock the file */ - oparms->reconnect = false; - - iov[num].iov_base = create_reconnect_durable_v2_buf(oparms->fid); - if (iov[num].iov_base == NULL) - return -ENOMEM; - iov[num].iov_len = sizeof(struct create_durable_handle_reconnect_v2); - if (!req->CreateContextsOffset) - req->CreateContextsOffset = - cpu_to_le32(sizeof(struct smb2_create_req) + - iov[1].iov_len); - le32_add_cpu(&req->CreateContextsLength, - sizeof(struct create_durable_handle_reconnect_v2)); - *num_iovec = num + 1; - return 0; -} - -static int -add_durable_context(struct kvec *iov, unsigned int *num_iovec, - struct cifs_open_parms *oparms, bool use_persistent) -{ - struct smb2_create_req *req = iov[0].iov_base; - unsigned int num = *num_iovec; - - if (use_persistent) { - if (oparms->reconnect) - return add_durable_reconnect_v2_context(iov, num_iovec, - oparms); - else - return add_durable_v2_context(iov, num_iovec, oparms); - } - - if (oparms->reconnect) { - iov[num].iov_base = create_reconnect_durable_buf(oparms->fid); - /* indicate that we don't need to relock the file */ - oparms->reconnect = false; - } else - iov[num].iov_base = create_durable_buf(); - if (iov[num].iov_base == NULL) - return -ENOMEM; - iov[num].iov_len = sizeof(struct create_durable); - if (!req->CreateContextsOffset) - req->CreateContextsOffset = - cpu_to_le32(sizeof(struct smb2_create_req) + - iov[1].iov_len); - le32_add_cpu(&req->CreateContextsLength, sizeof(struct create_durable)); - *num_iovec = num + 1; - return 0; -} - -/* See MS-SMB2 2.2.13.2.7 */ -static struct crt_twarp_ctxt * -create_twarp_buf(__u64 timewarp) -{ - struct crt_twarp_ctxt *buf; - - buf = kzalloc(sizeof(struct crt_twarp_ctxt), GFP_KERNEL); - if (!buf) - return NULL; - - buf->ccontext.DataOffset = cpu_to_le16(offsetof - (struct crt_twarp_ctxt, Timestamp)); - buf->ccontext.DataLength = cpu_to_le32(8); - buf->ccontext.NameOffset = cpu_to_le16(offsetof - (struct crt_twarp_ctxt, Name)); - buf->ccontext.NameLength = cpu_to_le16(4); - /* SMB2_CREATE_TIMEWARP_TOKEN is "TWrp" */ - buf->Name[0] = 'T'; - buf->Name[1] = 'W'; - buf->Name[2] = 'r'; - buf->Name[3] = 'p'; - buf->Timestamp = cpu_to_le64(timewarp); - return buf; -} - -/* See MS-SMB2 2.2.13.2.7 */ -static int -add_twarp_context(struct kvec *iov, unsigned int *num_iovec, __u64 timewarp) -{ - struct smb2_create_req *req = iov[0].iov_base; - unsigned int num = *num_iovec; - - iov[num].iov_base = create_twarp_buf(timewarp); - if (iov[num].iov_base == NULL) - return -ENOMEM; - iov[num].iov_len = sizeof(struct crt_twarp_ctxt); - if (!req->CreateContextsOffset) - req->CreateContextsOffset = cpu_to_le32( - sizeof(struct smb2_create_req) + - iov[num - 1].iov_len); - le32_add_cpu(&req->CreateContextsLength, sizeof(struct crt_twarp_ctxt)); - *num_iovec = num + 1; - return 0; -} - -/* See See http://technet.microsoft.com/en-us/library/hh509017(v=ws.10).aspx */ -static void setup_owner_group_sids(char *buf) -{ - struct owner_group_sids *sids = (struct owner_group_sids *)buf; - - /* Populate the user ownership fields S-1-5-88-1 */ - sids->owner.Revision = 1; - sids->owner.NumAuth = 3; - sids->owner.Authority[5] = 5; - sids->owner.SubAuthorities[0] = cpu_to_le32(88); - sids->owner.SubAuthorities[1] = cpu_to_le32(1); - sids->owner.SubAuthorities[2] = cpu_to_le32(current_fsuid().val); - - /* Populate the group ownership fields S-1-5-88-2 */ - sids->group.Revision = 1; - sids->group.NumAuth = 3; - sids->group.Authority[5] = 5; - sids->group.SubAuthorities[0] = cpu_to_le32(88); - sids->group.SubAuthorities[1] = cpu_to_le32(2); - sids->group.SubAuthorities[2] = cpu_to_le32(current_fsgid().val); - - cifs_dbg(FYI, "owner S-1-5-88-1-%d, group S-1-5-88-2-%d\n", current_fsuid().val, current_fsgid().val); -} - -/* See MS-SMB2 2.2.13.2.2 and MS-DTYP 2.4.6 */ -static struct crt_sd_ctxt * -create_sd_buf(umode_t mode, bool set_owner, unsigned int *len) -{ - struct crt_sd_ctxt *buf; - __u8 *ptr, *aclptr; - unsigned int acelen, acl_size, ace_count; - unsigned int owner_offset = 0; - unsigned int group_offset = 0; - struct smb3_acl acl = {}; - - *len = round_up(sizeof(struct crt_sd_ctxt) + (sizeof(struct cifs_ace) * 4), 8); - - if (set_owner) { - /* sizeof(struct owner_group_sids) is already multiple of 8 so no need to round */ - *len += sizeof(struct owner_group_sids); - } - - buf = kzalloc(*len, GFP_KERNEL); - if (buf == NULL) - return buf; - - ptr = (__u8 *)&buf[1]; - if (set_owner) { - /* offset fields are from beginning of security descriptor not of create context */ - owner_offset = ptr - (__u8 *)&buf->sd; - buf->sd.OffsetOwner = cpu_to_le32(owner_offset); - group_offset = owner_offset + offsetof(struct owner_group_sids, group); - buf->sd.OffsetGroup = cpu_to_le32(group_offset); - - setup_owner_group_sids(ptr); - ptr += sizeof(struct owner_group_sids); - } else { - buf->sd.OffsetOwner = 0; - buf->sd.OffsetGroup = 0; - } - - buf->ccontext.DataOffset = cpu_to_le16(offsetof(struct crt_sd_ctxt, sd)); - buf->ccontext.NameOffset = cpu_to_le16(offsetof(struct crt_sd_ctxt, Name)); - buf->ccontext.NameLength = cpu_to_le16(4); - /* SMB2_CREATE_SD_BUFFER_TOKEN is "SecD" */ - buf->Name[0] = 'S'; - buf->Name[1] = 'e'; - buf->Name[2] = 'c'; - buf->Name[3] = 'D'; - buf->sd.Revision = 1; /* Must be one see MS-DTYP 2.4.6 */ - - /* - * ACL is "self relative" ie ACL is stored in contiguous block of memory - * and "DP" ie the DACL is present - */ - buf->sd.Control = cpu_to_le16(ACL_CONTROL_SR | ACL_CONTROL_DP); - - /* offset owner, group and Sbz1 and SACL are all zero */ - buf->sd.OffsetDacl = cpu_to_le32(ptr - (__u8 *)&buf->sd); - /* Ship the ACL for now. we will copy it into buf later. */ - aclptr = ptr; - ptr += sizeof(struct smb3_acl); - - /* create one ACE to hold the mode embedded in reserved special SID */ - acelen = setup_special_mode_ACE((struct cifs_ace *)ptr, (__u64)mode); - ptr += acelen; - acl_size = acelen + sizeof(struct smb3_acl); - ace_count = 1; - - if (set_owner) { - /* we do not need to reallocate buffer to add the two more ACEs. plenty of space */ - acelen = setup_special_user_owner_ACE((struct cifs_ace *)ptr); - ptr += acelen; - acl_size += acelen; - ace_count += 1; - } - - /* and one more ACE to allow access for authenticated users */ - acelen = setup_authusers_ACE((struct cifs_ace *)ptr); - ptr += acelen; - acl_size += acelen; - ace_count += 1; - - acl.AclRevision = ACL_REVISION; /* See 2.4.4.1 of MS-DTYP */ - acl.AclSize = cpu_to_le16(acl_size); - acl.AceCount = cpu_to_le16(ace_count); - /* acl.Sbz1 and Sbz2 MBZ so are not set here, but initialized above */ - memcpy(aclptr, &acl, sizeof(struct smb3_acl)); - - buf->ccontext.DataLength = cpu_to_le32(ptr - (__u8 *)&buf->sd); - *len = round_up((unsigned int)(ptr - (__u8 *)buf), 8); - - return buf; -} - -static int -add_sd_context(struct kvec *iov, unsigned int *num_iovec, umode_t mode, bool set_owner) -{ - struct smb2_create_req *req = iov[0].iov_base; - unsigned int num = *num_iovec; - unsigned int len = 0; - - iov[num].iov_base = create_sd_buf(mode, set_owner, &len); - if (iov[num].iov_base == NULL) - return -ENOMEM; - iov[num].iov_len = len; - if (!req->CreateContextsOffset) - req->CreateContextsOffset = cpu_to_le32( - sizeof(struct smb2_create_req) + - iov[num - 1].iov_len); - le32_add_cpu(&req->CreateContextsLength, len); - *num_iovec = num + 1; - return 0; -} - -static struct crt_query_id_ctxt * -create_query_id_buf(void) -{ - struct crt_query_id_ctxt *buf; - - buf = kzalloc(sizeof(struct crt_query_id_ctxt), GFP_KERNEL); - if (!buf) - return NULL; - - buf->ccontext.DataOffset = cpu_to_le16(0); - buf->ccontext.DataLength = cpu_to_le32(0); - buf->ccontext.NameOffset = cpu_to_le16(offsetof - (struct crt_query_id_ctxt, Name)); - buf->ccontext.NameLength = cpu_to_le16(4); - /* SMB2_CREATE_QUERY_ON_DISK_ID is "QFid" */ - buf->Name[0] = 'Q'; - buf->Name[1] = 'F'; - buf->Name[2] = 'i'; - buf->Name[3] = 'd'; - return buf; -} - -/* See MS-SMB2 2.2.13.2.9 */ -static int -add_query_id_context(struct kvec *iov, unsigned int *num_iovec) -{ - struct smb2_create_req *req = iov[0].iov_base; - unsigned int num = *num_iovec; - - iov[num].iov_base = create_query_id_buf(); - if (iov[num].iov_base == NULL) - return -ENOMEM; - iov[num].iov_len = sizeof(struct crt_query_id_ctxt); - if (!req->CreateContextsOffset) - req->CreateContextsOffset = cpu_to_le32( - sizeof(struct smb2_create_req) + - iov[num - 1].iov_len); - le32_add_cpu(&req->CreateContextsLength, sizeof(struct crt_query_id_ctxt)); - *num_iovec = num + 1; - return 0; -} - -static int -alloc_path_with_tree_prefix(__le16 **out_path, int *out_size, int *out_len, - const char *treename, const __le16 *path) -{ - int treename_len, path_len; - struct nls_table *cp; - const __le16 sep[] = {cpu_to_le16('\\'), cpu_to_le16(0x0000)}; - - /* - * skip leading "\\" - */ - treename_len = strlen(treename); - if (treename_len < 2 || !(treename[0] == '\\' && treename[1] == '\\')) - return -EINVAL; - - treename += 2; - treename_len -= 2; - - path_len = UniStrnlen((wchar_t *)path, PATH_MAX); - - /* make room for one path separator only if @path isn't empty */ - *out_len = treename_len + (path[0] ? 1 : 0) + path_len; - - /* - * final path needs to be 8-byte aligned as specified in - * MS-SMB2 2.2.13 SMB2 CREATE Request. - */ - *out_size = round_up(*out_len * sizeof(__le16), 8); - *out_path = kzalloc(*out_size + sizeof(__le16) /* null */, GFP_KERNEL); - if (!*out_path) - return -ENOMEM; - - cp = load_nls_default(); - cifs_strtoUTF16(*out_path, treename, treename_len, cp); - - /* Do not append the separator if the path is empty */ - if (path[0] != cpu_to_le16(0x0000)) { - UniStrcat(*out_path, sep); - UniStrcat(*out_path, path); - } - - unload_nls(cp); - - return 0; -} - -int smb311_posix_mkdir(const unsigned int xid, struct inode *inode, - umode_t mode, struct cifs_tcon *tcon, - const char *full_path, - struct cifs_sb_info *cifs_sb) -{ - struct smb_rqst rqst; - struct smb2_create_req *req; - struct smb2_create_rsp *rsp = NULL; - struct cifs_ses *ses = tcon->ses; - struct kvec iov[3]; /* make sure at least one for each open context */ - struct kvec rsp_iov = {NULL, 0}; - int resp_buftype; - int uni_path_len; - __le16 *copy_path = NULL; - int copy_size; - int rc = 0; - unsigned int n_iov = 2; - __u32 file_attributes = 0; - char *pc_buf = NULL; - int flags = 0; - unsigned int total_len; - __le16 *utf16_path = NULL; - struct TCP_Server_Info *server = cifs_pick_channel(ses); - - cifs_dbg(FYI, "mkdir\n"); - - /* resource #1: path allocation */ - utf16_path = cifs_convert_path_to_utf16(full_path, cifs_sb); - if (!utf16_path) - return -ENOMEM; - - if (!ses || !server) { - rc = -EIO; - goto err_free_path; - } - - /* resource #2: request */ - rc = smb2_plain_req_init(SMB2_CREATE, tcon, server, - (void **) &req, &total_len); - if (rc) - goto err_free_path; - - - if (smb3_encryption_required(tcon)) - flags |= CIFS_TRANSFORM_REQ; - - req->ImpersonationLevel = IL_IMPERSONATION; - req->DesiredAccess = cpu_to_le32(FILE_WRITE_ATTRIBUTES); - /* File attributes ignored on open (used in create though) */ - req->FileAttributes = cpu_to_le32(file_attributes); - req->ShareAccess = FILE_SHARE_ALL_LE; - req->CreateDisposition = cpu_to_le32(FILE_CREATE); - req->CreateOptions = cpu_to_le32(CREATE_NOT_FILE); - - iov[0].iov_base = (char *)req; - /* -1 since last byte is buf[0] which is sent below (path) */ - iov[0].iov_len = total_len - 1; - - req->NameOffset = cpu_to_le16(sizeof(struct smb2_create_req)); - - /* [MS-SMB2] 2.2.13 NameOffset: - * If SMB2_FLAGS_DFS_OPERATIONS is set in the Flags field of - * the SMB2 header, the file name includes a prefix that will - * be processed during DFS name normalization as specified in - * section 3.3.5.9. Otherwise, the file name is relative to - * the share that is identified by the TreeId in the SMB2 - * header. - */ - if (tcon->share_flags & SHI1005_FLAGS_DFS) { - int name_len; - - req->hdr.Flags |= SMB2_FLAGS_DFS_OPERATIONS; - rc = alloc_path_with_tree_prefix(©_path, ©_size, - &name_len, - tcon->tree_name, utf16_path); - if (rc) - goto err_free_req; - - req->NameLength = cpu_to_le16(name_len * 2); - uni_path_len = copy_size; - /* free before overwriting resource */ - kfree(utf16_path); - utf16_path = copy_path; - } else { - uni_path_len = (2 * UniStrnlen((wchar_t *)utf16_path, PATH_MAX)) + 2; - /* MUST set path len (NameLength) to 0 opening root of share */ - req->NameLength = cpu_to_le16(uni_path_len - 2); - if (uni_path_len % 8 != 0) { - copy_size = roundup(uni_path_len, 8); - copy_path = kzalloc(copy_size, GFP_KERNEL); - if (!copy_path) { - rc = -ENOMEM; - goto err_free_req; - } - memcpy((char *)copy_path, (const char *)utf16_path, - uni_path_len); - uni_path_len = copy_size; - /* free before overwriting resource */ - kfree(utf16_path); - utf16_path = copy_path; - } - } - - iov[1].iov_len = uni_path_len; - iov[1].iov_base = utf16_path; - req->RequestedOplockLevel = SMB2_OPLOCK_LEVEL_NONE; - - if (tcon->posix_extensions) { - /* resource #3: posix buf */ - rc = add_posix_context(iov, &n_iov, mode); - if (rc) - goto err_free_req; - pc_buf = iov[n_iov-1].iov_base; - } - - - memset(&rqst, 0, sizeof(struct smb_rqst)); - rqst.rq_iov = iov; - rqst.rq_nvec = n_iov; - - /* no need to inc num_remote_opens because we close it just below */ - trace_smb3_posix_mkdir_enter(xid, tcon->tid, ses->Suid, full_path, CREATE_NOT_FILE, - FILE_WRITE_ATTRIBUTES); - /* resource #4: response buffer */ - rc = cifs_send_recv(xid, ses, server, - &rqst, &resp_buftype, flags, &rsp_iov); - if (rc) { - cifs_stats_fail_inc(tcon, SMB2_CREATE_HE); - trace_smb3_posix_mkdir_err(xid, tcon->tid, ses->Suid, - CREATE_NOT_FILE, - FILE_WRITE_ATTRIBUTES, rc); - goto err_free_rsp_buf; - } - - /* - * Although unlikely to be possible for rsp to be null and rc not set, - * adding check below is slightly safer long term (and quiets Coverity - * warning) - */ - rsp = (struct smb2_create_rsp *)rsp_iov.iov_base; - if (rsp == NULL) { - rc = -EIO; - kfree(pc_buf); - goto err_free_req; - } - - trace_smb3_posix_mkdir_done(xid, rsp->PersistentFileId, tcon->tid, ses->Suid, - CREATE_NOT_FILE, FILE_WRITE_ATTRIBUTES); - - SMB2_close(xid, tcon, rsp->PersistentFileId, rsp->VolatileFileId); - - /* Eventually save off posix specific response info and timestaps */ - -err_free_rsp_buf: - free_rsp_buf(resp_buftype, rsp); - kfree(pc_buf); -err_free_req: - cifs_small_buf_release(req); -err_free_path: - kfree(utf16_path); - return rc; -} - -int -SMB2_open_init(struct cifs_tcon *tcon, struct TCP_Server_Info *server, - struct smb_rqst *rqst, __u8 *oplock, - struct cifs_open_parms *oparms, __le16 *path) -{ - struct smb2_create_req *req; - unsigned int n_iov = 2; - __u32 file_attributes = 0; - int copy_size; - int uni_path_len; - unsigned int total_len; - struct kvec *iov = rqst->rq_iov; - __le16 *copy_path; - int rc; - - rc = smb2_plain_req_init(SMB2_CREATE, tcon, server, - (void **) &req, &total_len); - if (rc) - return rc; - - iov[0].iov_base = (char *)req; - /* -1 since last byte is buf[0] which is sent below (path) */ - iov[0].iov_len = total_len - 1; - - if (oparms->create_options & CREATE_OPTION_READONLY) - file_attributes |= ATTR_READONLY; - if (oparms->create_options & CREATE_OPTION_SPECIAL) - file_attributes |= ATTR_SYSTEM; - - req->ImpersonationLevel = IL_IMPERSONATION; - req->DesiredAccess = cpu_to_le32(oparms->desired_access); - /* File attributes ignored on open (used in create though) */ - req->FileAttributes = cpu_to_le32(file_attributes); - req->ShareAccess = FILE_SHARE_ALL_LE; - - req->CreateDisposition = cpu_to_le32(oparms->disposition); - req->CreateOptions = cpu_to_le32(oparms->create_options & CREATE_OPTIONS_MASK); - req->NameOffset = cpu_to_le16(sizeof(struct smb2_create_req)); - - /* [MS-SMB2] 2.2.13 NameOffset: - * If SMB2_FLAGS_DFS_OPERATIONS is set in the Flags field of - * the SMB2 header, the file name includes a prefix that will - * be processed during DFS name normalization as specified in - * section 3.3.5.9. Otherwise, the file name is relative to - * the share that is identified by the TreeId in the SMB2 - * header. - */ - if (tcon->share_flags & SHI1005_FLAGS_DFS) { - int name_len; - - req->hdr.Flags |= SMB2_FLAGS_DFS_OPERATIONS; - rc = alloc_path_with_tree_prefix(©_path, ©_size, - &name_len, - tcon->tree_name, path); - if (rc) - return rc; - req->NameLength = cpu_to_le16(name_len * 2); - uni_path_len = copy_size; - path = copy_path; - } else { - uni_path_len = (2 * UniStrnlen((wchar_t *)path, PATH_MAX)) + 2; - /* MUST set path len (NameLength) to 0 opening root of share */ - req->NameLength = cpu_to_le16(uni_path_len - 2); - copy_size = round_up(uni_path_len, 8); - copy_path = kzalloc(copy_size, GFP_KERNEL); - if (!copy_path) - return -ENOMEM; - memcpy((char *)copy_path, (const char *)path, - uni_path_len); - uni_path_len = copy_size; - path = copy_path; - } - - iov[1].iov_len = uni_path_len; - iov[1].iov_base = path; - - if ((!server->oplocks) || (tcon->no_lease)) - *oplock = SMB2_OPLOCK_LEVEL_NONE; - - if (!(server->capabilities & SMB2_GLOBAL_CAP_LEASING) || - *oplock == SMB2_OPLOCK_LEVEL_NONE) - req->RequestedOplockLevel = *oplock; - else if (!(server->capabilities & SMB2_GLOBAL_CAP_DIRECTORY_LEASING) && - (oparms->create_options & CREATE_NOT_FILE)) - req->RequestedOplockLevel = *oplock; /* no srv lease support */ - else { - rc = add_lease_context(server, iov, &n_iov, - oparms->fid->lease_key, oplock); - if (rc) - return rc; - } - - if (*oplock == SMB2_OPLOCK_LEVEL_BATCH) { - /* need to set Next field of lease context if we request it */ - if (server->capabilities & SMB2_GLOBAL_CAP_LEASING) { - struct create_context *ccontext = - (struct create_context *)iov[n_iov-1].iov_base; - ccontext->Next = - cpu_to_le32(server->vals->create_lease_size); - } - - rc = add_durable_context(iov, &n_iov, oparms, - tcon->use_persistent); - if (rc) - return rc; - } - - if (tcon->posix_extensions) { - if (n_iov > 2) { - struct create_context *ccontext = - (struct create_context *)iov[n_iov-1].iov_base; - ccontext->Next = - cpu_to_le32(iov[n_iov-1].iov_len); - } - - rc = add_posix_context(iov, &n_iov, oparms->mode); - if (rc) - return rc; - } - - if (tcon->snapshot_time) { - cifs_dbg(FYI, "adding snapshot context\n"); - if (n_iov > 2) { - struct create_context *ccontext = - (struct create_context *)iov[n_iov-1].iov_base; - ccontext->Next = - cpu_to_le32(iov[n_iov-1].iov_len); - } - - rc = add_twarp_context(iov, &n_iov, tcon->snapshot_time); - if (rc) - return rc; - } - - if ((oparms->disposition != FILE_OPEN) && (oparms->cifs_sb)) { - bool set_mode; - bool set_owner; - - if ((oparms->cifs_sb->mnt_cifs_flags & CIFS_MOUNT_MODE_FROM_SID) && - (oparms->mode != ACL_NO_MODE)) - set_mode = true; - else { - set_mode = false; - oparms->mode = ACL_NO_MODE; - } - - if (oparms->cifs_sb->mnt_cifs_flags & CIFS_MOUNT_UID_FROM_ACL) - set_owner = true; - else - set_owner = false; - - if (set_owner | set_mode) { - if (n_iov > 2) { - struct create_context *ccontext = - (struct create_context *)iov[n_iov-1].iov_base; - ccontext->Next = cpu_to_le32(iov[n_iov-1].iov_len); - } - - cifs_dbg(FYI, "add sd with mode 0x%x\n", oparms->mode); - rc = add_sd_context(iov, &n_iov, oparms->mode, set_owner); - if (rc) - return rc; - } - } - - if (n_iov > 2) { - struct create_context *ccontext = - (struct create_context *)iov[n_iov-1].iov_base; - ccontext->Next = cpu_to_le32(iov[n_iov-1].iov_len); - } - add_query_id_context(iov, &n_iov); - - rqst->rq_nvec = n_iov; - return 0; -} - -/* rq_iov[0] is the request and is released by cifs_small_buf_release(). - * All other vectors are freed by kfree(). - */ -void -SMB2_open_free(struct smb_rqst *rqst) -{ - int i; - - if (rqst && rqst->rq_iov) { - cifs_small_buf_release(rqst->rq_iov[0].iov_base); - for (i = 1; i < rqst->rq_nvec; i++) - if (rqst->rq_iov[i].iov_base != smb2_padding) - kfree(rqst->rq_iov[i].iov_base); - } -} - -int -SMB2_open(const unsigned int xid, struct cifs_open_parms *oparms, __le16 *path, - __u8 *oplock, struct smb2_file_all_info *buf, - struct create_posix_rsp *posix, - struct kvec *err_iov, int *buftype) -{ - struct smb_rqst rqst; - struct smb2_create_rsp *rsp = NULL; - struct cifs_tcon *tcon = oparms->tcon; - struct cifs_ses *ses = tcon->ses; - struct TCP_Server_Info *server = cifs_pick_channel(ses); - struct kvec iov[SMB2_CREATE_IOV_SIZE]; - struct kvec rsp_iov = {NULL, 0}; - int resp_buftype = CIFS_NO_BUFFER; - int rc = 0; - int flags = 0; - - cifs_dbg(FYI, "create/open\n"); - if (!ses || !server) - return -EIO; - - if (smb3_encryption_required(tcon)) - flags |= CIFS_TRANSFORM_REQ; - - memset(&rqst, 0, sizeof(struct smb_rqst)); - memset(&iov, 0, sizeof(iov)); - rqst.rq_iov = iov; - rqst.rq_nvec = SMB2_CREATE_IOV_SIZE; - - rc = SMB2_open_init(tcon, server, - &rqst, oplock, oparms, path); - if (rc) - goto creat_exit; - - trace_smb3_open_enter(xid, tcon->tid, tcon->ses->Suid, oparms->path, - oparms->create_options, oparms->desired_access); - - rc = cifs_send_recv(xid, ses, server, - &rqst, &resp_buftype, flags, - &rsp_iov); - rsp = (struct smb2_create_rsp *)rsp_iov.iov_base; - - if (rc != 0) { - cifs_stats_fail_inc(tcon, SMB2_CREATE_HE); - if (err_iov && rsp) { - *err_iov = rsp_iov; - *buftype = resp_buftype; - resp_buftype = CIFS_NO_BUFFER; - rsp = NULL; - } - trace_smb3_open_err(xid, tcon->tid, ses->Suid, - oparms->create_options, oparms->desired_access, rc); - if (rc == -EREMCHG) { - pr_warn_once("server share %s deleted\n", - tcon->tree_name); - tcon->need_reconnect = true; - } - goto creat_exit; - } else if (rsp == NULL) /* unlikely to happen, but safer to check */ - goto creat_exit; - else - trace_smb3_open_done(xid, rsp->PersistentFileId, tcon->tid, ses->Suid, - oparms->create_options, oparms->desired_access); - - atomic_inc(&tcon->num_remote_opens); - oparms->fid->persistent_fid = rsp->PersistentFileId; - oparms->fid->volatile_fid = rsp->VolatileFileId; - oparms->fid->access = oparms->desired_access; -#ifdef CONFIG_CIFS_DEBUG2 - oparms->fid->mid = le64_to_cpu(rsp->hdr.MessageId); -#endif /* CIFS_DEBUG2 */ - - if (buf) { - buf->CreationTime = rsp->CreationTime; - buf->LastAccessTime = rsp->LastAccessTime; - buf->LastWriteTime = rsp->LastWriteTime; - buf->ChangeTime = rsp->ChangeTime; - buf->AllocationSize = rsp->AllocationSize; - buf->EndOfFile = rsp->EndofFile; - buf->Attributes = rsp->FileAttributes; - buf->NumberOfLinks = cpu_to_le32(1); - buf->DeletePending = 0; - } - - - smb2_parse_contexts(server, rsp, &oparms->fid->epoch, - oparms->fid->lease_key, oplock, buf, posix); -creat_exit: - SMB2_open_free(&rqst); - free_rsp_buf(resp_buftype, rsp); - return rc; -} - -int -SMB2_ioctl_init(struct cifs_tcon *tcon, struct TCP_Server_Info *server, - struct smb_rqst *rqst, - u64 persistent_fid, u64 volatile_fid, u32 opcode, - char *in_data, u32 indatalen, - __u32 max_response_size) -{ - struct smb2_ioctl_req *req; - struct kvec *iov = rqst->rq_iov; - unsigned int total_len; - int rc; - char *in_data_buf; - - rc = smb2_ioctl_req_init(opcode, tcon, server, - (void **) &req, &total_len); - if (rc) - return rc; - - if (indatalen) { - /* - * indatalen is usually small at a couple of bytes max, so - * just allocate through generic pool - */ - in_data_buf = kmemdup(in_data, indatalen, GFP_NOFS); - if (!in_data_buf) { - cifs_small_buf_release(req); - return -ENOMEM; - } - } - - req->CtlCode = cpu_to_le32(opcode); - req->PersistentFileId = persistent_fid; - req->VolatileFileId = volatile_fid; - - iov[0].iov_base = (char *)req; - /* - * If no input data, the size of ioctl struct in - * protocol spec still includes a 1 byte data buffer, - * but if input data passed to ioctl, we do not - * want to double count this, so we do not send - * the dummy one byte of data in iovec[0] if sending - * input data (in iovec[1]). - */ - if (indatalen) { - req->InputCount = cpu_to_le32(indatalen); - /* do not set InputOffset if no input data */ - req->InputOffset = - cpu_to_le32(offsetof(struct smb2_ioctl_req, Buffer)); - rqst->rq_nvec = 2; - iov[0].iov_len = total_len - 1; - iov[1].iov_base = in_data_buf; - iov[1].iov_len = indatalen; - } else { - rqst->rq_nvec = 1; - iov[0].iov_len = total_len; - } - - req->OutputOffset = 0; - req->OutputCount = 0; /* MBZ */ - - /* - * In most cases max_response_size is set to 16K (CIFSMaxBufSize) - * We Could increase default MaxOutputResponse, but that could require - * more credits. Windows typically sets this smaller, but for some - * ioctls it may be useful to allow server to send more. No point - * limiting what the server can send as long as fits in one credit - * We can not handle more than CIFS_MAX_BUF_SIZE yet but may want - * to increase this limit up in the future. - * Note that for snapshot queries that servers like Azure expect that - * the first query be minimal size (and just used to get the number/size - * of previous versions) so response size must be specified as EXACTLY - * sizeof(struct snapshot_array) which is 16 when rounded up to multiple - * of eight bytes. Currently that is the only case where we set max - * response size smaller. - */ - req->MaxOutputResponse = cpu_to_le32(max_response_size); - req->hdr.CreditCharge = - cpu_to_le16(DIV_ROUND_UP(max(indatalen, max_response_size), - SMB2_MAX_BUFFER_SIZE)); - /* always an FSCTL (for now) */ - req->Flags = cpu_to_le32(SMB2_0_IOCTL_IS_FSCTL); - - /* validate negotiate request must be signed - see MS-SMB2 3.2.5.5 */ - if (opcode == FSCTL_VALIDATE_NEGOTIATE_INFO) - req->hdr.Flags |= SMB2_FLAGS_SIGNED; - - return 0; -} - -void -SMB2_ioctl_free(struct smb_rqst *rqst) -{ - int i; - if (rqst && rqst->rq_iov) { - cifs_small_buf_release(rqst->rq_iov[0].iov_base); /* request */ - for (i = 1; i < rqst->rq_nvec; i++) - if (rqst->rq_iov[i].iov_base != smb2_padding) - kfree(rqst->rq_iov[i].iov_base); - } -} - - -/* - * SMB2 IOCTL is used for both IOCTLs and FSCTLs - */ -int -SMB2_ioctl(const unsigned int xid, struct cifs_tcon *tcon, u64 persistent_fid, - u64 volatile_fid, u32 opcode, char *in_data, u32 indatalen, - u32 max_out_data_len, char **out_data, - u32 *plen /* returned data len */) -{ - struct smb_rqst rqst; - struct smb2_ioctl_rsp *rsp = NULL; - struct cifs_ses *ses; - struct TCP_Server_Info *server; - struct kvec iov[SMB2_IOCTL_IOV_SIZE]; - struct kvec rsp_iov = {NULL, 0}; - int resp_buftype = CIFS_NO_BUFFER; - int rc = 0; - int flags = 0; - - cifs_dbg(FYI, "SMB2 IOCTL\n"); - - if (out_data != NULL) - *out_data = NULL; - - /* zero out returned data len, in case of error */ - if (plen) - *plen = 0; - - if (!tcon) - return -EIO; - - ses = tcon->ses; - if (!ses) - return -EIO; - - server = cifs_pick_channel(ses); - if (!server) - return -EIO; - - if (smb3_encryption_required(tcon)) - flags |= CIFS_TRANSFORM_REQ; - - memset(&rqst, 0, sizeof(struct smb_rqst)); - memset(&iov, 0, sizeof(iov)); - rqst.rq_iov = iov; - rqst.rq_nvec = SMB2_IOCTL_IOV_SIZE; - - rc = SMB2_ioctl_init(tcon, server, - &rqst, persistent_fid, volatile_fid, opcode, - in_data, indatalen, max_out_data_len); - if (rc) - goto ioctl_exit; - - rc = cifs_send_recv(xid, ses, server, - &rqst, &resp_buftype, flags, - &rsp_iov); - rsp = (struct smb2_ioctl_rsp *)rsp_iov.iov_base; - - if (rc != 0) - trace_smb3_fsctl_err(xid, persistent_fid, tcon->tid, - ses->Suid, 0, opcode, rc); - - if ((rc != 0) && (rc != -EINVAL) && (rc != -E2BIG)) { - cifs_stats_fail_inc(tcon, SMB2_IOCTL_HE); - goto ioctl_exit; - } else if (rc == -EINVAL) { - if ((opcode != FSCTL_SRV_COPYCHUNK_WRITE) && - (opcode != FSCTL_SRV_COPYCHUNK)) { - cifs_stats_fail_inc(tcon, SMB2_IOCTL_HE); - goto ioctl_exit; - } - } else if (rc == -E2BIG) { - if (opcode != FSCTL_QUERY_ALLOCATED_RANGES) { - cifs_stats_fail_inc(tcon, SMB2_IOCTL_HE); - goto ioctl_exit; - } - } - - /* check if caller wants to look at return data or just return rc */ - if ((plen == NULL) || (out_data == NULL)) - goto ioctl_exit; - - /* - * Although unlikely to be possible for rsp to be null and rc not set, - * adding check below is slightly safer long term (and quiets Coverity - * warning) - */ - if (rsp == NULL) { - rc = -EIO; - goto ioctl_exit; - } - - *plen = le32_to_cpu(rsp->OutputCount); - - /* We check for obvious errors in the output buffer length and offset */ - if (*plen == 0) - goto ioctl_exit; /* server returned no data */ - else if (*plen > rsp_iov.iov_len || *plen > 0xFF00) { - cifs_tcon_dbg(VFS, "srv returned invalid ioctl length: %d\n", *plen); - *plen = 0; - rc = -EIO; - goto ioctl_exit; - } - - if (rsp_iov.iov_len - *plen < le32_to_cpu(rsp->OutputOffset)) { - cifs_tcon_dbg(VFS, "Malformed ioctl resp: len %d offset %d\n", *plen, - le32_to_cpu(rsp->OutputOffset)); - *plen = 0; - rc = -EIO; - goto ioctl_exit; - } - - *out_data = kmemdup((char *)rsp + le32_to_cpu(rsp->OutputOffset), - *plen, GFP_KERNEL); - if (*out_data == NULL) { - rc = -ENOMEM; - goto ioctl_exit; - } - -ioctl_exit: - SMB2_ioctl_free(&rqst); - free_rsp_buf(resp_buftype, rsp); - return rc; -} - -/* - * Individual callers to ioctl worker function follow - */ - -int -SMB2_set_compression(const unsigned int xid, struct cifs_tcon *tcon, - u64 persistent_fid, u64 volatile_fid) -{ - int rc; - struct compress_ioctl fsctl_input; - char *ret_data = NULL; - - fsctl_input.CompressionState = - cpu_to_le16(COMPRESSION_FORMAT_DEFAULT); - - rc = SMB2_ioctl(xid, tcon, persistent_fid, volatile_fid, - FSCTL_SET_COMPRESSION, - (char *)&fsctl_input /* data input */, - 2 /* in data len */, CIFSMaxBufSize /* max out data */, - &ret_data /* out data */, NULL); - - cifs_dbg(FYI, "set compression rc %d\n", rc); - - return rc; -} - -int -SMB2_close_init(struct cifs_tcon *tcon, struct TCP_Server_Info *server, - struct smb_rqst *rqst, - u64 persistent_fid, u64 volatile_fid, bool query_attrs) -{ - struct smb2_close_req *req; - struct kvec *iov = rqst->rq_iov; - unsigned int total_len; - int rc; - - rc = smb2_plain_req_init(SMB2_CLOSE, tcon, server, - (void **) &req, &total_len); - if (rc) - return rc; - - req->PersistentFileId = persistent_fid; - req->VolatileFileId = volatile_fid; - if (query_attrs) - req->Flags = SMB2_CLOSE_FLAG_POSTQUERY_ATTRIB; - else - req->Flags = 0; - iov[0].iov_base = (char *)req; - iov[0].iov_len = total_len; - - return 0; -} - -void -SMB2_close_free(struct smb_rqst *rqst) -{ - if (rqst && rqst->rq_iov) - cifs_small_buf_release(rqst->rq_iov[0].iov_base); /* request */ -} - -int -__SMB2_close(const unsigned int xid, struct cifs_tcon *tcon, - u64 persistent_fid, u64 volatile_fid, - struct smb2_file_network_open_info *pbuf) -{ - struct smb_rqst rqst; - struct smb2_close_rsp *rsp = NULL; - struct cifs_ses *ses = tcon->ses; - struct TCP_Server_Info *server = cifs_pick_channel(ses); - struct kvec iov[1]; - struct kvec rsp_iov; - int resp_buftype = CIFS_NO_BUFFER; - int rc = 0; - int flags = 0; - bool query_attrs = false; - - cifs_dbg(FYI, "Close\n"); - - if (!ses || !server) - return -EIO; - - if (smb3_encryption_required(tcon)) - flags |= CIFS_TRANSFORM_REQ; - - memset(&rqst, 0, sizeof(struct smb_rqst)); - memset(&iov, 0, sizeof(iov)); - rqst.rq_iov = iov; - rqst.rq_nvec = 1; - - /* check if need to ask server to return timestamps in close response */ - if (pbuf) - query_attrs = true; - - trace_smb3_close_enter(xid, persistent_fid, tcon->tid, ses->Suid); - rc = SMB2_close_init(tcon, server, - &rqst, persistent_fid, volatile_fid, - query_attrs); - if (rc) - goto close_exit; - - rc = cifs_send_recv(xid, ses, server, - &rqst, &resp_buftype, flags, &rsp_iov); - rsp = (struct smb2_close_rsp *)rsp_iov.iov_base; - - if (rc != 0) { - cifs_stats_fail_inc(tcon, SMB2_CLOSE_HE); - trace_smb3_close_err(xid, persistent_fid, tcon->tid, ses->Suid, - rc); - goto close_exit; - } else { - trace_smb3_close_done(xid, persistent_fid, tcon->tid, - ses->Suid); - /* - * Note that have to subtract 4 since struct network_open_info - * has a final 4 byte pad that close response does not have - */ - if (pbuf) - memcpy(pbuf, (char *)&rsp->CreationTime, sizeof(*pbuf) - 4); - } - - atomic_dec(&tcon->num_remote_opens); -close_exit: - SMB2_close_free(&rqst); - free_rsp_buf(resp_buftype, rsp); - - /* retry close in a worker thread if this one is interrupted */ - if (is_interrupt_error(rc)) { - int tmp_rc; - - tmp_rc = smb2_handle_cancelled_close(tcon, persistent_fid, - volatile_fid); - if (tmp_rc) - cifs_dbg(VFS, "handle cancelled close fid 0x%llx returned error %d\n", - persistent_fid, tmp_rc); - } - return rc; -} - -int -SMB2_close(const unsigned int xid, struct cifs_tcon *tcon, - u64 persistent_fid, u64 volatile_fid) -{ - return __SMB2_close(xid, tcon, persistent_fid, volatile_fid, NULL); -} - -int -smb2_validate_iov(unsigned int offset, unsigned int buffer_length, - struct kvec *iov, unsigned int min_buf_size) -{ - unsigned int smb_len = iov->iov_len; - char *end_of_smb = smb_len + (char *)iov->iov_base; - char *begin_of_buf = offset + (char *)iov->iov_base; - char *end_of_buf = begin_of_buf + buffer_length; - - - if (buffer_length < min_buf_size) { - cifs_dbg(VFS, "buffer length %d smaller than minimum size %d\n", - buffer_length, min_buf_size); - return -EINVAL; - } - - /* check if beyond RFC1001 maximum length */ - if ((smb_len > 0x7FFFFF) || (buffer_length > 0x7FFFFF)) { - cifs_dbg(VFS, "buffer length %d or smb length %d too large\n", - buffer_length, smb_len); - return -EINVAL; - } - - if ((begin_of_buf > end_of_smb) || (end_of_buf > end_of_smb)) { - cifs_dbg(VFS, "Invalid server response, bad offset to data\n"); - return -EINVAL; - } - - return 0; -} - -/* - * If SMB buffer fields are valid, copy into temporary buffer to hold result. - * Caller must free buffer. - */ -int -smb2_validate_and_copy_iov(unsigned int offset, unsigned int buffer_length, - struct kvec *iov, unsigned int minbufsize, - char *data) -{ - char *begin_of_buf = offset + (char *)iov->iov_base; - int rc; - - if (!data) - return -EINVAL; - - rc = smb2_validate_iov(offset, buffer_length, iov, minbufsize); - if (rc) - return rc; - - memcpy(data, begin_of_buf, minbufsize); - - return 0; -} - -int -SMB2_query_info_init(struct cifs_tcon *tcon, struct TCP_Server_Info *server, - struct smb_rqst *rqst, - u64 persistent_fid, u64 volatile_fid, - u8 info_class, u8 info_type, u32 additional_info, - size_t output_len, size_t input_len, void *input) -{ - struct smb2_query_info_req *req; - struct kvec *iov = rqst->rq_iov; - unsigned int total_len; - int rc; - - rc = smb2_plain_req_init(SMB2_QUERY_INFO, tcon, server, - (void **) &req, &total_len); - if (rc) - return rc; - - req->InfoType = info_type; - req->FileInfoClass = info_class; - req->PersistentFileId = persistent_fid; - req->VolatileFileId = volatile_fid; - req->AdditionalInformation = cpu_to_le32(additional_info); - - req->OutputBufferLength = cpu_to_le32(output_len); - if (input_len) { - req->InputBufferLength = cpu_to_le32(input_len); - /* total_len for smb query request never close to le16 max */ - req->InputBufferOffset = cpu_to_le16(total_len - 1); - memcpy(req->Buffer, input, input_len); - } - - iov[0].iov_base = (char *)req; - /* 1 for Buffer */ - iov[0].iov_len = total_len - 1 + input_len; - return 0; -} - -void -SMB2_query_info_free(struct smb_rqst *rqst) -{ - if (rqst && rqst->rq_iov) - cifs_small_buf_release(rqst->rq_iov[0].iov_base); /* request */ -} - -static int -query_info(const unsigned int xid, struct cifs_tcon *tcon, - u64 persistent_fid, u64 volatile_fid, u8 info_class, u8 info_type, - u32 additional_info, size_t output_len, size_t min_len, void **data, - u32 *dlen) -{ - struct smb_rqst rqst; - struct smb2_query_info_rsp *rsp = NULL; - struct kvec iov[1]; - struct kvec rsp_iov; - int rc = 0; - int resp_buftype = CIFS_NO_BUFFER; - struct cifs_ses *ses = tcon->ses; - struct TCP_Server_Info *server; - int flags = 0; - bool allocated = false; - - cifs_dbg(FYI, "Query Info\n"); - - if (!ses) - return -EIO; - server = cifs_pick_channel(ses); - if (!server) - return -EIO; - - if (smb3_encryption_required(tcon)) - flags |= CIFS_TRANSFORM_REQ; - - memset(&rqst, 0, sizeof(struct smb_rqst)); - memset(&iov, 0, sizeof(iov)); - rqst.rq_iov = iov; - rqst.rq_nvec = 1; - - rc = SMB2_query_info_init(tcon, server, - &rqst, persistent_fid, volatile_fid, - info_class, info_type, additional_info, - output_len, 0, NULL); - if (rc) - goto qinf_exit; - - trace_smb3_query_info_enter(xid, persistent_fid, tcon->tid, - ses->Suid, info_class, (__u32)info_type); - - rc = cifs_send_recv(xid, ses, server, - &rqst, &resp_buftype, flags, &rsp_iov); - rsp = (struct smb2_query_info_rsp *)rsp_iov.iov_base; - - if (rc) { - cifs_stats_fail_inc(tcon, SMB2_QUERY_INFO_HE); - trace_smb3_query_info_err(xid, persistent_fid, tcon->tid, - ses->Suid, info_class, (__u32)info_type, rc); - goto qinf_exit; - } - - trace_smb3_query_info_done(xid, persistent_fid, tcon->tid, - ses->Suid, info_class, (__u32)info_type); - - if (dlen) { - *dlen = le32_to_cpu(rsp->OutputBufferLength); - if (!*data) { - *data = kmalloc(*dlen, GFP_KERNEL); - if (!*data) { - cifs_tcon_dbg(VFS, - "Error %d allocating memory for acl\n", - rc); - *dlen = 0; - rc = -ENOMEM; - goto qinf_exit; - } - allocated = true; - } - } - - rc = smb2_validate_and_copy_iov(le16_to_cpu(rsp->OutputBufferOffset), - le32_to_cpu(rsp->OutputBufferLength), - &rsp_iov, dlen ? *dlen : min_len, *data); - if (rc && allocated) { - kfree(*data); - *data = NULL; - *dlen = 0; - } - -qinf_exit: - SMB2_query_info_free(&rqst); - free_rsp_buf(resp_buftype, rsp); - return rc; -} - -int SMB2_query_info(const unsigned int xid, struct cifs_tcon *tcon, - u64 persistent_fid, u64 volatile_fid, struct smb2_file_all_info *data) -{ - return query_info(xid, tcon, persistent_fid, volatile_fid, - FILE_ALL_INFORMATION, SMB2_O_INFO_FILE, 0, - sizeof(struct smb2_file_all_info) + PATH_MAX * 2, - sizeof(struct smb2_file_all_info), (void **)&data, - NULL); -} - -#if 0 -/* currently unused, as now we are doing compounding instead (see smb311_posix_query_path_info) */ -int -SMB311_posix_query_info(const unsigned int xid, struct cifs_tcon *tcon, - u64 persistent_fid, u64 volatile_fid, struct smb311_posix_qinfo *data, u32 *plen) -{ - size_t output_len = sizeof(struct smb311_posix_qinfo *) + - (sizeof(struct cifs_sid) * 2) + (PATH_MAX * 2); - *plen = 0; - - return query_info(xid, tcon, persistent_fid, volatile_fid, - SMB_FIND_FILE_POSIX_INFO, SMB2_O_INFO_FILE, 0, - output_len, sizeof(struct smb311_posix_qinfo), (void **)&data, plen); - /* Note caller must free "data" (passed in above). It may be allocated in query_info call */ -} -#endif - -int -SMB2_query_acl(const unsigned int xid, struct cifs_tcon *tcon, - u64 persistent_fid, u64 volatile_fid, - void **data, u32 *plen, u32 extra_info) -{ - __u32 additional_info = OWNER_SECINFO | GROUP_SECINFO | DACL_SECINFO | - extra_info; - *plen = 0; - - return query_info(xid, tcon, persistent_fid, volatile_fid, - 0, SMB2_O_INFO_SECURITY, additional_info, - SMB2_MAX_BUFFER_SIZE, MIN_SEC_DESC_LEN, data, plen); -} - -int -SMB2_get_srv_num(const unsigned int xid, struct cifs_tcon *tcon, - u64 persistent_fid, u64 volatile_fid, __le64 *uniqueid) -{ - return query_info(xid, tcon, persistent_fid, volatile_fid, - FILE_INTERNAL_INFORMATION, SMB2_O_INFO_FILE, 0, - sizeof(struct smb2_file_internal_info), - sizeof(struct smb2_file_internal_info), - (void **)&uniqueid, NULL); -} - -/* - * CHANGE_NOTIFY Request is sent to get notifications on changes to a directory - * See MS-SMB2 2.2.35 and 2.2.36 - */ - -static int -SMB2_notify_init(const unsigned int xid, struct smb_rqst *rqst, - struct cifs_tcon *tcon, struct TCP_Server_Info *server, - u64 persistent_fid, u64 volatile_fid, - u32 completion_filter, bool watch_tree) -{ - struct smb2_change_notify_req *req; - struct kvec *iov = rqst->rq_iov; - unsigned int total_len; - int rc; - - rc = smb2_plain_req_init(SMB2_CHANGE_NOTIFY, tcon, server, - (void **) &req, &total_len); - if (rc) - return rc; - - req->PersistentFileId = persistent_fid; - req->VolatileFileId = volatile_fid; - /* See note 354 of MS-SMB2, 64K max */ - req->OutputBufferLength = - cpu_to_le32(SMB2_MAX_BUFFER_SIZE - MAX_SMB2_HDR_SIZE); - req->CompletionFilter = cpu_to_le32(completion_filter); - if (watch_tree) - req->Flags = cpu_to_le16(SMB2_WATCH_TREE); - else - req->Flags = 0; - - iov[0].iov_base = (char *)req; - iov[0].iov_len = total_len; - - return 0; -} - -int -SMB2_change_notify(const unsigned int xid, struct cifs_tcon *tcon, - u64 persistent_fid, u64 volatile_fid, bool watch_tree, - u32 completion_filter, u32 max_out_data_len, char **out_data, - u32 *plen /* returned data len */) -{ - struct cifs_ses *ses = tcon->ses; - struct TCP_Server_Info *server = cifs_pick_channel(ses); - struct smb_rqst rqst; - struct smb2_change_notify_rsp *smb_rsp; - struct kvec iov[1]; - struct kvec rsp_iov = {NULL, 0}; - int resp_buftype = CIFS_NO_BUFFER; - int flags = 0; - int rc = 0; - - cifs_dbg(FYI, "change notify\n"); - if (!ses || !server) - return -EIO; - - if (smb3_encryption_required(tcon)) - flags |= CIFS_TRANSFORM_REQ; - - memset(&rqst, 0, sizeof(struct smb_rqst)); - memset(&iov, 0, sizeof(iov)); - if (plen) - *plen = 0; - - rqst.rq_iov = iov; - rqst.rq_nvec = 1; - - rc = SMB2_notify_init(xid, &rqst, tcon, server, - persistent_fid, volatile_fid, - completion_filter, watch_tree); - if (rc) - goto cnotify_exit; - - trace_smb3_notify_enter(xid, persistent_fid, tcon->tid, ses->Suid, - (u8)watch_tree, completion_filter); - rc = cifs_send_recv(xid, ses, server, - &rqst, &resp_buftype, flags, &rsp_iov); - - if (rc != 0) { - cifs_stats_fail_inc(tcon, SMB2_CHANGE_NOTIFY_HE); - trace_smb3_notify_err(xid, persistent_fid, tcon->tid, ses->Suid, - (u8)watch_tree, completion_filter, rc); - } else { - trace_smb3_notify_done(xid, persistent_fid, tcon->tid, - ses->Suid, (u8)watch_tree, completion_filter); - /* validate that notify information is plausible */ - if ((rsp_iov.iov_base == NULL) || - (rsp_iov.iov_len < sizeof(struct smb2_change_notify_rsp))) - goto cnotify_exit; - - smb_rsp = (struct smb2_change_notify_rsp *)rsp_iov.iov_base; - - smb2_validate_iov(le16_to_cpu(smb_rsp->OutputBufferOffset), - le32_to_cpu(smb_rsp->OutputBufferLength), &rsp_iov, - sizeof(struct file_notify_information)); - - *out_data = kmemdup((char *)smb_rsp + le16_to_cpu(smb_rsp->OutputBufferOffset), - le32_to_cpu(smb_rsp->OutputBufferLength), GFP_KERNEL); - if (*out_data == NULL) { - rc = -ENOMEM; - goto cnotify_exit; - } else - *plen = le32_to_cpu(smb_rsp->OutputBufferLength); - } - - cnotify_exit: - if (rqst.rq_iov) - cifs_small_buf_release(rqst.rq_iov[0].iov_base); /* request */ - free_rsp_buf(resp_buftype, rsp_iov.iov_base); - return rc; -} - - - -/* - * This is a no-op for now. We're not really interested in the reply, but - * rather in the fact that the server sent one and that server->lstrp - * gets updated. - * - * FIXME: maybe we should consider checking that the reply matches request? - */ -static void -smb2_echo_callback(struct mid_q_entry *mid) -{ - struct TCP_Server_Info *server = mid->callback_data; - struct smb2_echo_rsp *rsp = (struct smb2_echo_rsp *)mid->resp_buf; - struct cifs_credits credits = { .value = 0, .instance = 0 }; - - if (mid->mid_state == MID_RESPONSE_RECEIVED - || mid->mid_state == MID_RESPONSE_MALFORMED) { - credits.value = le16_to_cpu(rsp->hdr.CreditRequest); - credits.instance = server->reconnect_instance; - } - - release_mid(mid); - add_credits(server, &credits, CIFS_ECHO_OP); -} - -void smb2_reconnect_server(struct work_struct *work) -{ - struct TCP_Server_Info *server = container_of(work, - struct TCP_Server_Info, reconnect.work); - struct TCP_Server_Info *pserver; - struct cifs_ses *ses, *ses2; - struct cifs_tcon *tcon, *tcon2; - struct list_head tmp_list, tmp_ses_list; - bool tcon_exist = false, ses_exist = false; - bool tcon_selected = false; - int rc; - bool resched = false; - - /* If server is a channel, select the primary channel */ - pserver = CIFS_SERVER_IS_CHAN(server) ? server->primary_server : server; - - /* Prevent simultaneous reconnects that can corrupt tcon->rlist list */ - mutex_lock(&pserver->reconnect_mutex); - - INIT_LIST_HEAD(&tmp_list); - INIT_LIST_HEAD(&tmp_ses_list); - cifs_dbg(FYI, "Reconnecting tcons and channels\n"); - - spin_lock(&cifs_tcp_ses_lock); - list_for_each_entry(ses, &pserver->smb_ses_list, smb_ses_list) { - - tcon_selected = false; - - list_for_each_entry(tcon, &ses->tcon_list, tcon_list) { - if (tcon->need_reconnect || tcon->need_reopen_files) { - tcon->tc_count++; - list_add_tail(&tcon->rlist, &tmp_list); - tcon_selected = tcon_exist = true; - } - } - /* - * IPC has the same lifetime as its session and uses its - * refcount. - */ - if (ses->tcon_ipc && ses->tcon_ipc->need_reconnect) { - list_add_tail(&ses->tcon_ipc->rlist, &tmp_list); - tcon_selected = tcon_exist = true; - ses->ses_count++; - } - /* - * handle the case where channel needs to reconnect - * binding session, but tcon is healthy (some other channel - * is active) - */ - spin_lock(&ses->chan_lock); - if (!tcon_selected && cifs_chan_needs_reconnect(ses, server)) { - list_add_tail(&ses->rlist, &tmp_ses_list); - ses_exist = true; - ses->ses_count++; - } - spin_unlock(&ses->chan_lock); - } - /* - * Get the reference to server struct to be sure that the last call of - * cifs_put_tcon() in the loop below won't release the server pointer. - */ - if (tcon_exist || ses_exist) - server->srv_count++; - - spin_unlock(&cifs_tcp_ses_lock); - - list_for_each_entry_safe(tcon, tcon2, &tmp_list, rlist) { - rc = smb2_reconnect(SMB2_INTERNAL_CMD, tcon, server); - if (!rc) - cifs_reopen_persistent_handles(tcon); - else - resched = true; - list_del_init(&tcon->rlist); - if (tcon->ipc) - cifs_put_smb_ses(tcon->ses); - else - cifs_put_tcon(tcon); - } - - if (!ses_exist) - goto done; - - /* allocate a dummy tcon struct used for reconnect */ - tcon = tconInfoAlloc(); - if (!tcon) { - resched = true; - list_for_each_entry_safe(ses, ses2, &tmp_ses_list, rlist) { - list_del_init(&ses->rlist); - cifs_put_smb_ses(ses); - } - goto done; - } - - tcon->status = TID_GOOD; - tcon->retry = false; - tcon->need_reconnect = false; - - /* now reconnect sessions for necessary channels */ - list_for_each_entry_safe(ses, ses2, &tmp_ses_list, rlist) { - tcon->ses = ses; - rc = smb2_reconnect(SMB2_INTERNAL_CMD, tcon, server); - if (rc) - resched = true; - list_del_init(&ses->rlist); - cifs_put_smb_ses(ses); - } - tconInfoFree(tcon); - -done: - cifs_dbg(FYI, "Reconnecting tcons and channels finished\n"); - if (resched) - queue_delayed_work(cifsiod_wq, &server->reconnect, 2 * HZ); - mutex_unlock(&pserver->reconnect_mutex); - - /* now we can safely release srv struct */ - if (tcon_exist || ses_exist) - cifs_put_tcp_session(server, 1); -} - -int -SMB2_echo(struct TCP_Server_Info *server) -{ - struct smb2_echo_req *req; - int rc = 0; - struct kvec iov[1]; - struct smb_rqst rqst = { .rq_iov = iov, - .rq_nvec = 1 }; - unsigned int total_len; - - cifs_dbg(FYI, "In echo request for conn_id %lld\n", server->conn_id); - - spin_lock(&server->srv_lock); - if (server->ops->need_neg && - server->ops->need_neg(server)) { - spin_unlock(&server->srv_lock); - /* No need to send echo on newly established connections */ - mod_delayed_work(cifsiod_wq, &server->reconnect, 0); - return rc; - } - spin_unlock(&server->srv_lock); - - rc = smb2_plain_req_init(SMB2_ECHO, NULL, server, - (void **)&req, &total_len); - if (rc) - return rc; - - req->hdr.CreditRequest = cpu_to_le16(1); - - iov[0].iov_len = total_len; - iov[0].iov_base = (char *)req; - - rc = cifs_call_async(server, &rqst, NULL, smb2_echo_callback, NULL, - server, CIFS_ECHO_OP, NULL); - if (rc) - cifs_dbg(FYI, "Echo request failed: %d\n", rc); - - cifs_small_buf_release(req); - return rc; -} - -void -SMB2_flush_free(struct smb_rqst *rqst) -{ - if (rqst && rqst->rq_iov) - cifs_small_buf_release(rqst->rq_iov[0].iov_base); /* request */ -} - -int -SMB2_flush_init(const unsigned int xid, struct smb_rqst *rqst, - struct cifs_tcon *tcon, struct TCP_Server_Info *server, - u64 persistent_fid, u64 volatile_fid) -{ - struct smb2_flush_req *req; - struct kvec *iov = rqst->rq_iov; - unsigned int total_len; - int rc; - - rc = smb2_plain_req_init(SMB2_FLUSH, tcon, server, - (void **) &req, &total_len); - if (rc) - return rc; - - req->PersistentFileId = persistent_fid; - req->VolatileFileId = volatile_fid; - - iov[0].iov_base = (char *)req; - iov[0].iov_len = total_len; - - return 0; -} - -int -SMB2_flush(const unsigned int xid, struct cifs_tcon *tcon, u64 persistent_fid, - u64 volatile_fid) -{ - struct cifs_ses *ses = tcon->ses; - struct smb_rqst rqst; - struct kvec iov[1]; - struct kvec rsp_iov = {NULL, 0}; - struct TCP_Server_Info *server = cifs_pick_channel(ses); - int resp_buftype = CIFS_NO_BUFFER; - int flags = 0; - int rc = 0; - - cifs_dbg(FYI, "flush\n"); - if (!ses || !(ses->server)) - return -EIO; - - if (smb3_encryption_required(tcon)) - flags |= CIFS_TRANSFORM_REQ; - - memset(&rqst, 0, sizeof(struct smb_rqst)); - memset(&iov, 0, sizeof(iov)); - rqst.rq_iov = iov; - rqst.rq_nvec = 1; - - rc = SMB2_flush_init(xid, &rqst, tcon, server, - persistent_fid, volatile_fid); - if (rc) - goto flush_exit; - - trace_smb3_flush_enter(xid, persistent_fid, tcon->tid, ses->Suid); - rc = cifs_send_recv(xid, ses, server, - &rqst, &resp_buftype, flags, &rsp_iov); - - if (rc != 0) { - cifs_stats_fail_inc(tcon, SMB2_FLUSH_HE); - trace_smb3_flush_err(xid, persistent_fid, tcon->tid, ses->Suid, - rc); - } else - trace_smb3_flush_done(xid, persistent_fid, tcon->tid, - ses->Suid); - - flush_exit: - SMB2_flush_free(&rqst); - free_rsp_buf(resp_buftype, rsp_iov.iov_base); - return rc; -} - -#ifdef CONFIG_CIFS_SMB_DIRECT -static inline bool smb3_use_rdma_offload(struct cifs_io_parms *io_parms) -{ - struct TCP_Server_Info *server = io_parms->server; - struct cifs_tcon *tcon = io_parms->tcon; - - /* we can only offload if we're connected */ - if (!server || !tcon) - return false; - - /* we can only offload on an rdma connection */ - if (!server->rdma || !server->smbd_conn) - return false; - - /* we don't support signed offload yet */ - if (server->sign) - return false; - - /* we don't support encrypted offload yet */ - if (smb3_encryption_required(tcon)) - return false; - - /* offload also has its overhead, so only do it if desired */ - if (io_parms->length < server->smbd_conn->rdma_readwrite_threshold) - return false; - - return true; -} -#endif /* CONFIG_CIFS_SMB_DIRECT */ - -/* - * To form a chain of read requests, any read requests after the first should - * have the end_of_chain boolean set to true. - */ -static int -smb2_new_read_req(void **buf, unsigned int *total_len, - struct cifs_io_parms *io_parms, struct cifs_readdata *rdata, - unsigned int remaining_bytes, int request_type) -{ - int rc = -EACCES; - struct smb2_read_req *req = NULL; - struct smb2_hdr *shdr; - struct TCP_Server_Info *server = io_parms->server; - - rc = smb2_plain_req_init(SMB2_READ, io_parms->tcon, server, - (void **) &req, total_len); - if (rc) - return rc; - - if (server == NULL) - return -ECONNABORTED; - - shdr = &req->hdr; - shdr->Id.SyncId.ProcessId = cpu_to_le32(io_parms->pid); - - req->PersistentFileId = io_parms->persistent_fid; - req->VolatileFileId = io_parms->volatile_fid; - req->ReadChannelInfoOffset = 0; /* reserved */ - req->ReadChannelInfoLength = 0; /* reserved */ - req->Channel = 0; /* reserved */ - req->MinimumCount = 0; - req->Length = cpu_to_le32(io_parms->length); - req->Offset = cpu_to_le64(io_parms->offset); - - trace_smb3_read_enter(0 /* xid */, - io_parms->persistent_fid, - io_parms->tcon->tid, io_parms->tcon->ses->Suid, - io_parms->offset, io_parms->length); -#ifdef CONFIG_CIFS_SMB_DIRECT - /* - * If we want to do a RDMA write, fill in and append - * smbd_buffer_descriptor_v1 to the end of read request - */ - if (smb3_use_rdma_offload(io_parms)) { - struct smbd_buffer_descriptor_v1 *v1; - bool need_invalidate = server->dialect == SMB30_PROT_ID; - - rdata->mr = smbd_register_mr( - server->smbd_conn, rdata->pages, - rdata->nr_pages, rdata->page_offset, - rdata->tailsz, true, need_invalidate); - if (!rdata->mr) - return -EAGAIN; - - req->Channel = SMB2_CHANNEL_RDMA_V1_INVALIDATE; - if (need_invalidate) - req->Channel = SMB2_CHANNEL_RDMA_V1; - req->ReadChannelInfoOffset = - cpu_to_le16(offsetof(struct smb2_read_req, Buffer)); - req->ReadChannelInfoLength = - cpu_to_le16(sizeof(struct smbd_buffer_descriptor_v1)); - v1 = (struct smbd_buffer_descriptor_v1 *) &req->Buffer[0]; - v1->offset = cpu_to_le64(rdata->mr->mr->iova); - v1->token = cpu_to_le32(rdata->mr->mr->rkey); - v1->length = cpu_to_le32(rdata->mr->mr->length); - - *total_len += sizeof(*v1) - 1; - } -#endif - if (request_type & CHAINED_REQUEST) { - if (!(request_type & END_OF_CHAIN)) { - /* next 8-byte aligned request */ - *total_len = ALIGN(*total_len, 8); - shdr->NextCommand = cpu_to_le32(*total_len); - } else /* END_OF_CHAIN */ - shdr->NextCommand = 0; - if (request_type & RELATED_REQUEST) { - shdr->Flags |= SMB2_FLAGS_RELATED_OPERATIONS; - /* - * Related requests use info from previous read request - * in chain. - */ - shdr->SessionId = cpu_to_le64(0xFFFFFFFFFFFFFFFF); - shdr->Id.SyncId.TreeId = cpu_to_le32(0xFFFFFFFF); - req->PersistentFileId = (u64)-1; - req->VolatileFileId = (u64)-1; - } - } - if (remaining_bytes > io_parms->length) - req->RemainingBytes = cpu_to_le32(remaining_bytes); - else - req->RemainingBytes = 0; - - *buf = req; - return rc; -} - -static void -smb2_readv_callback(struct mid_q_entry *mid) -{ - struct cifs_readdata *rdata = mid->callback_data; - struct cifs_tcon *tcon = tlink_tcon(rdata->cfile->tlink); - struct TCP_Server_Info *server = rdata->server; - struct smb2_hdr *shdr = - (struct smb2_hdr *)rdata->iov[0].iov_base; - struct cifs_credits credits = { .value = 0, .instance = 0 }; - struct smb_rqst rqst = { .rq_iov = &rdata->iov[1], - .rq_nvec = 1, }; - - if (rdata->got_bytes) { - rqst.rq_pages = rdata->pages; - rqst.rq_offset = rdata->page_offset; - rqst.rq_npages = rdata->nr_pages; - rqst.rq_pagesz = rdata->pagesz; - rqst.rq_tailsz = rdata->tailsz; - } - - WARN_ONCE(rdata->server != mid->server, - "rdata server %p != mid server %p", - rdata->server, mid->server); - - cifs_dbg(FYI, "%s: mid=%llu state=%d result=%d bytes=%u\n", - __func__, mid->mid, mid->mid_state, rdata->result, - rdata->bytes); - - switch (mid->mid_state) { - case MID_RESPONSE_RECEIVED: - credits.value = le16_to_cpu(shdr->CreditRequest); - credits.instance = server->reconnect_instance; - /* result already set, check signature */ - if (server->sign && !mid->decrypted) { - int rc; - - rc = smb2_verify_signature(&rqst, server); - if (rc) - cifs_tcon_dbg(VFS, "SMB signature verification returned error = %d\n", - rc); - } - /* FIXME: should this be counted toward the initiating task? */ - task_io_account_read(rdata->got_bytes); - cifs_stats_bytes_read(tcon, rdata->got_bytes); - break; - case MID_REQUEST_SUBMITTED: - case MID_RETRY_NEEDED: - rdata->result = -EAGAIN; - if (server->sign && rdata->got_bytes) - /* reset bytes number since we can not check a sign */ - rdata->got_bytes = 0; - /* FIXME: should this be counted toward the initiating task? */ - task_io_account_read(rdata->got_bytes); - cifs_stats_bytes_read(tcon, rdata->got_bytes); - break; - case MID_RESPONSE_MALFORMED: - credits.value = le16_to_cpu(shdr->CreditRequest); - credits.instance = server->reconnect_instance; - fallthrough; - default: - rdata->result = -EIO; - } -#ifdef CONFIG_CIFS_SMB_DIRECT - /* - * If this rdata has a memmory registered, the MR can be freed - * MR needs to be freed as soon as I/O finishes to prevent deadlock - * because they have limited number and are used for future I/Os - */ - if (rdata->mr) { - smbd_deregister_mr(rdata->mr); - rdata->mr = NULL; - } -#endif - if (rdata->result && rdata->result != -ENODATA) { - cifs_stats_fail_inc(tcon, SMB2_READ_HE); - trace_smb3_read_err(0 /* xid */, - rdata->cfile->fid.persistent_fid, - tcon->tid, tcon->ses->Suid, rdata->offset, - rdata->bytes, rdata->result); - } else - trace_smb3_read_done(0 /* xid */, - rdata->cfile->fid.persistent_fid, - tcon->tid, tcon->ses->Suid, - rdata->offset, rdata->got_bytes); - - queue_work(cifsiod_wq, &rdata->work); - release_mid(mid); - add_credits(server, &credits, 0); -} - -/* smb2_async_readv - send an async read, and set up mid to handle result */ -int -smb2_async_readv(struct cifs_readdata *rdata) -{ - int rc, flags = 0; - char *buf; - struct smb2_hdr *shdr; - struct cifs_io_parms io_parms; - struct smb_rqst rqst = { .rq_iov = rdata->iov, - .rq_nvec = 1 }; - struct TCP_Server_Info *server; - struct cifs_tcon *tcon = tlink_tcon(rdata->cfile->tlink); - unsigned int total_len; - - cifs_dbg(FYI, "%s: offset=%llu bytes=%u\n", - __func__, rdata->offset, rdata->bytes); - - if (!rdata->server) - rdata->server = cifs_pick_channel(tcon->ses); - - io_parms.tcon = tlink_tcon(rdata->cfile->tlink); - io_parms.server = server = rdata->server; - io_parms.offset = rdata->offset; - io_parms.length = rdata->bytes; - io_parms.persistent_fid = rdata->cfile->fid.persistent_fid; - io_parms.volatile_fid = rdata->cfile->fid.volatile_fid; - io_parms.pid = rdata->pid; - - rc = smb2_new_read_req( - (void **) &buf, &total_len, &io_parms, rdata, 0, 0); - if (rc) - return rc; - - if (smb3_encryption_required(io_parms.tcon)) - flags |= CIFS_TRANSFORM_REQ; - - rdata->iov[0].iov_base = buf; - rdata->iov[0].iov_len = total_len; - - shdr = (struct smb2_hdr *)buf; - - if (rdata->credits.value > 0) { - shdr->CreditCharge = cpu_to_le16(DIV_ROUND_UP(rdata->bytes, - SMB2_MAX_BUFFER_SIZE)); - shdr->CreditRequest = cpu_to_le16(le16_to_cpu(shdr->CreditCharge) + 8); - - rc = adjust_credits(server, &rdata->credits, rdata->bytes); - if (rc) - goto async_readv_out; - - flags |= CIFS_HAS_CREDITS; - } - - kref_get(&rdata->refcount); - rc = cifs_call_async(server, &rqst, - cifs_readv_receive, smb2_readv_callback, - smb3_handle_read_data, rdata, flags, - &rdata->credits); - if (rc) { - kref_put(&rdata->refcount, cifs_readdata_release); - cifs_stats_fail_inc(io_parms.tcon, SMB2_READ_HE); - trace_smb3_read_err(0 /* xid */, io_parms.persistent_fid, - io_parms.tcon->tid, - io_parms.tcon->ses->Suid, - io_parms.offset, io_parms.length, rc); - } - -async_readv_out: - cifs_small_buf_release(buf); - return rc; -} - -int -SMB2_read(const unsigned int xid, struct cifs_io_parms *io_parms, - unsigned int *nbytes, char **buf, int *buf_type) -{ - struct smb_rqst rqst; - int resp_buftype, rc; - struct smb2_read_req *req = NULL; - struct smb2_read_rsp *rsp = NULL; - struct kvec iov[1]; - struct kvec rsp_iov; - unsigned int total_len; - int flags = CIFS_LOG_ERROR; - struct cifs_ses *ses = io_parms->tcon->ses; - - if (!io_parms->server) - io_parms->server = cifs_pick_channel(io_parms->tcon->ses); - - *nbytes = 0; - rc = smb2_new_read_req((void **)&req, &total_len, io_parms, NULL, 0, 0); - if (rc) - return rc; - - if (smb3_encryption_required(io_parms->tcon)) - flags |= CIFS_TRANSFORM_REQ; - - iov[0].iov_base = (char *)req; - iov[0].iov_len = total_len; - - memset(&rqst, 0, sizeof(struct smb_rqst)); - rqst.rq_iov = iov; - rqst.rq_nvec = 1; - - rc = cifs_send_recv(xid, ses, io_parms->server, - &rqst, &resp_buftype, flags, &rsp_iov); - rsp = (struct smb2_read_rsp *)rsp_iov.iov_base; - - if (rc) { - if (rc != -ENODATA) { - cifs_stats_fail_inc(io_parms->tcon, SMB2_READ_HE); - cifs_dbg(VFS, "Send error in read = %d\n", rc); - trace_smb3_read_err(xid, - req->PersistentFileId, - io_parms->tcon->tid, ses->Suid, - io_parms->offset, io_parms->length, - rc); - } else - trace_smb3_read_done(xid, req->PersistentFileId, io_parms->tcon->tid, - ses->Suid, io_parms->offset, 0); - free_rsp_buf(resp_buftype, rsp_iov.iov_base); - cifs_small_buf_release(req); - return rc == -ENODATA ? 0 : rc; - } else - trace_smb3_read_done(xid, - req->PersistentFileId, - io_parms->tcon->tid, ses->Suid, - io_parms->offset, io_parms->length); - - cifs_small_buf_release(req); - - *nbytes = le32_to_cpu(rsp->DataLength); - if ((*nbytes > CIFS_MAX_MSGSIZE) || - (*nbytes > io_parms->length)) { - cifs_dbg(FYI, "bad length %d for count %d\n", - *nbytes, io_parms->length); - rc = -EIO; - *nbytes = 0; - } - - if (*buf) { - memcpy(*buf, (char *)rsp + rsp->DataOffset, *nbytes); - free_rsp_buf(resp_buftype, rsp_iov.iov_base); - } else if (resp_buftype != CIFS_NO_BUFFER) { - *buf = rsp_iov.iov_base; - if (resp_buftype == CIFS_SMALL_BUFFER) - *buf_type = CIFS_SMALL_BUFFER; - else if (resp_buftype == CIFS_LARGE_BUFFER) - *buf_type = CIFS_LARGE_BUFFER; - } - return rc; -} - -/* - * Check the mid_state and signature on received buffer (if any), and queue the - * workqueue completion task. - */ -static void -smb2_writev_callback(struct mid_q_entry *mid) -{ - struct cifs_writedata *wdata = mid->callback_data; - struct cifs_tcon *tcon = tlink_tcon(wdata->cfile->tlink); - struct TCP_Server_Info *server = wdata->server; - unsigned int written; - struct smb2_write_rsp *rsp = (struct smb2_write_rsp *)mid->resp_buf; - struct cifs_credits credits = { .value = 0, .instance = 0 }; - - WARN_ONCE(wdata->server != mid->server, - "wdata server %p != mid server %p", - wdata->server, mid->server); - - switch (mid->mid_state) { - case MID_RESPONSE_RECEIVED: - credits.value = le16_to_cpu(rsp->hdr.CreditRequest); - credits.instance = server->reconnect_instance; - wdata->result = smb2_check_receive(mid, server, 0); - if (wdata->result != 0) - break; - - written = le32_to_cpu(rsp->DataLength); - /* - * Mask off high 16 bits when bytes written as returned - * by the server is greater than bytes requested by the - * client. OS/2 servers are known to set incorrect - * CountHigh values. - */ - if (written > wdata->bytes) - written &= 0xFFFF; - - if (written < wdata->bytes) - wdata->result = -ENOSPC; - else - wdata->bytes = written; - break; - case MID_REQUEST_SUBMITTED: - case MID_RETRY_NEEDED: - wdata->result = -EAGAIN; - break; - case MID_RESPONSE_MALFORMED: - credits.value = le16_to_cpu(rsp->hdr.CreditRequest); - credits.instance = server->reconnect_instance; - fallthrough; - default: - wdata->result = -EIO; - break; - } -#ifdef CONFIG_CIFS_SMB_DIRECT - /* - * If this wdata has a memory registered, the MR can be freed - * The number of MRs available is limited, it's important to recover - * used MR as soon as I/O is finished. Hold MR longer in the later - * I/O process can possibly result in I/O deadlock due to lack of MR - * to send request on I/O retry - */ - if (wdata->mr) { - smbd_deregister_mr(wdata->mr); - wdata->mr = NULL; - } -#endif - if (wdata->result) { - cifs_stats_fail_inc(tcon, SMB2_WRITE_HE); - trace_smb3_write_err(0 /* no xid */, - wdata->cfile->fid.persistent_fid, - tcon->tid, tcon->ses->Suid, wdata->offset, - wdata->bytes, wdata->result); - if (wdata->result == -ENOSPC) - pr_warn_once("Out of space writing to %s\n", - tcon->tree_name); - } else - trace_smb3_write_done(0 /* no xid */, - wdata->cfile->fid.persistent_fid, - tcon->tid, tcon->ses->Suid, - wdata->offset, wdata->bytes); - - queue_work(cifsiod_wq, &wdata->work); - release_mid(mid); - add_credits(server, &credits, 0); -} - -/* smb2_async_writev - send an async write, and set up mid to handle result */ -int -smb2_async_writev(struct cifs_writedata *wdata, - void (*release)(struct kref *kref)) -{ - int rc = -EACCES, flags = 0; - struct smb2_write_req *req = NULL; - struct smb2_hdr *shdr; - struct cifs_tcon *tcon = tlink_tcon(wdata->cfile->tlink); - struct TCP_Server_Info *server = wdata->server; - struct kvec iov[1]; - struct smb_rqst rqst = { }; - unsigned int total_len; - struct cifs_io_parms _io_parms; - struct cifs_io_parms *io_parms = NULL; - - if (!wdata->server) - server = wdata->server = cifs_pick_channel(tcon->ses); - - /* - * in future we may get cifs_io_parms passed in from the caller, - * but for now we construct it here... - */ - _io_parms = (struct cifs_io_parms) { - .tcon = tcon, - .server = server, - .offset = wdata->offset, - .length = wdata->bytes, - .persistent_fid = wdata->cfile->fid.persistent_fid, - .volatile_fid = wdata->cfile->fid.volatile_fid, - .pid = wdata->pid, - }; - io_parms = &_io_parms; - - rc = smb2_plain_req_init(SMB2_WRITE, tcon, server, - (void **) &req, &total_len); - if (rc) - return rc; - - if (smb3_encryption_required(tcon)) - flags |= CIFS_TRANSFORM_REQ; - - shdr = (struct smb2_hdr *)req; - shdr->Id.SyncId.ProcessId = cpu_to_le32(io_parms->pid); - - req->PersistentFileId = io_parms->persistent_fid; - req->VolatileFileId = io_parms->volatile_fid; - req->WriteChannelInfoOffset = 0; - req->WriteChannelInfoLength = 0; - req->Channel = 0; - req->Offset = cpu_to_le64(io_parms->offset); - req->DataOffset = cpu_to_le16( - offsetof(struct smb2_write_req, Buffer)); - req->RemainingBytes = 0; - - trace_smb3_write_enter(0 /* xid */, - io_parms->persistent_fid, - io_parms->tcon->tid, - io_parms->tcon->ses->Suid, - io_parms->offset, - io_parms->length); - -#ifdef CONFIG_CIFS_SMB_DIRECT - /* - * If we want to do a server RDMA read, fill in and append - * smbd_buffer_descriptor_v1 to the end of write request - */ - if (smb3_use_rdma_offload(io_parms)) { - struct smbd_buffer_descriptor_v1 *v1; - bool need_invalidate = server->dialect == SMB30_PROT_ID; - - wdata->mr = smbd_register_mr( - server->smbd_conn, wdata->pages, - wdata->nr_pages, wdata->page_offset, - wdata->tailsz, false, need_invalidate); - if (!wdata->mr) { - rc = -EAGAIN; - goto async_writev_out; - } - req->Length = 0; - req->DataOffset = 0; - if (wdata->nr_pages > 1) - req->RemainingBytes = - cpu_to_le32( - (wdata->nr_pages - 1) * wdata->pagesz - - wdata->page_offset + wdata->tailsz - ); - else - req->RemainingBytes = cpu_to_le32(wdata->tailsz); - req->Channel = SMB2_CHANNEL_RDMA_V1_INVALIDATE; - if (need_invalidate) - req->Channel = SMB2_CHANNEL_RDMA_V1; - req->WriteChannelInfoOffset = - cpu_to_le16(offsetof(struct smb2_write_req, Buffer)); - req->WriteChannelInfoLength = - cpu_to_le16(sizeof(struct smbd_buffer_descriptor_v1)); - v1 = (struct smbd_buffer_descriptor_v1 *) &req->Buffer[0]; - v1->offset = cpu_to_le64(wdata->mr->mr->iova); - v1->token = cpu_to_le32(wdata->mr->mr->rkey); - v1->length = cpu_to_le32(wdata->mr->mr->length); - } -#endif - iov[0].iov_len = total_len - 1; - iov[0].iov_base = (char *)req; - - rqst.rq_iov = iov; - rqst.rq_nvec = 1; - rqst.rq_pages = wdata->pages; - rqst.rq_offset = wdata->page_offset; - rqst.rq_npages = wdata->nr_pages; - rqst.rq_pagesz = wdata->pagesz; - rqst.rq_tailsz = wdata->tailsz; -#ifdef CONFIG_CIFS_SMB_DIRECT - if (wdata->mr) { - iov[0].iov_len += sizeof(struct smbd_buffer_descriptor_v1); - rqst.rq_npages = 0; - } -#endif - cifs_dbg(FYI, "async write at %llu %u bytes\n", - io_parms->offset, io_parms->length); - -#ifdef CONFIG_CIFS_SMB_DIRECT - /* For RDMA read, I/O size is in RemainingBytes not in Length */ - if (!wdata->mr) - req->Length = cpu_to_le32(io_parms->length); -#else - req->Length = cpu_to_le32(io_parms->length); -#endif - - if (wdata->credits.value > 0) { - shdr->CreditCharge = cpu_to_le16(DIV_ROUND_UP(wdata->bytes, - SMB2_MAX_BUFFER_SIZE)); - shdr->CreditRequest = cpu_to_le16(le16_to_cpu(shdr->CreditCharge) + 8); - - rc = adjust_credits(server, &wdata->credits, io_parms->length); - if (rc) - goto async_writev_out; - - flags |= CIFS_HAS_CREDITS; - } - - kref_get(&wdata->refcount); - rc = cifs_call_async(server, &rqst, NULL, smb2_writev_callback, NULL, - wdata, flags, &wdata->credits); - - if (rc) { - trace_smb3_write_err(0 /* no xid */, - io_parms->persistent_fid, - io_parms->tcon->tid, - io_parms->tcon->ses->Suid, - io_parms->offset, - io_parms->length, - rc); - kref_put(&wdata->refcount, release); - cifs_stats_fail_inc(tcon, SMB2_WRITE_HE); - } - -async_writev_out: - cifs_small_buf_release(req); - return rc; -} - -/* - * SMB2_write function gets iov pointer to kvec array with n_vec as a length. - * The length field from io_parms must be at least 1 and indicates a number of - * elements with data to write that begins with position 1 in iov array. All - * data length is specified by count. - */ -int -SMB2_write(const unsigned int xid, struct cifs_io_parms *io_parms, - unsigned int *nbytes, struct kvec *iov, int n_vec) -{ - struct smb_rqst rqst; - int rc = 0; - struct smb2_write_req *req = NULL; - struct smb2_write_rsp *rsp = NULL; - int resp_buftype; - struct kvec rsp_iov; - int flags = 0; - unsigned int total_len; - struct TCP_Server_Info *server; - - *nbytes = 0; - - if (n_vec < 1) - return rc; - - if (!io_parms->server) - io_parms->server = cifs_pick_channel(io_parms->tcon->ses); - server = io_parms->server; - if (server == NULL) - return -ECONNABORTED; - - rc = smb2_plain_req_init(SMB2_WRITE, io_parms->tcon, server, - (void **) &req, &total_len); - if (rc) - return rc; - - if (smb3_encryption_required(io_parms->tcon)) - flags |= CIFS_TRANSFORM_REQ; - - req->hdr.Id.SyncId.ProcessId = cpu_to_le32(io_parms->pid); - - req->PersistentFileId = io_parms->persistent_fid; - req->VolatileFileId = io_parms->volatile_fid; - req->WriteChannelInfoOffset = 0; - req->WriteChannelInfoLength = 0; - req->Channel = 0; - req->Length = cpu_to_le32(io_parms->length); - req->Offset = cpu_to_le64(io_parms->offset); - req->DataOffset = cpu_to_le16( - offsetof(struct smb2_write_req, Buffer)); - req->RemainingBytes = 0; - - trace_smb3_write_enter(xid, io_parms->persistent_fid, - io_parms->tcon->tid, io_parms->tcon->ses->Suid, - io_parms->offset, io_parms->length); - - iov[0].iov_base = (char *)req; - /* 1 for Buffer */ - iov[0].iov_len = total_len - 1; - - memset(&rqst, 0, sizeof(struct smb_rqst)); - rqst.rq_iov = iov; - rqst.rq_nvec = n_vec + 1; - - rc = cifs_send_recv(xid, io_parms->tcon->ses, server, - &rqst, - &resp_buftype, flags, &rsp_iov); - rsp = (struct smb2_write_rsp *)rsp_iov.iov_base; - - if (rc) { - trace_smb3_write_err(xid, - req->PersistentFileId, - io_parms->tcon->tid, - io_parms->tcon->ses->Suid, - io_parms->offset, io_parms->length, rc); - cifs_stats_fail_inc(io_parms->tcon, SMB2_WRITE_HE); - cifs_dbg(VFS, "Send error in write = %d\n", rc); - } else { - *nbytes = le32_to_cpu(rsp->DataLength); - trace_smb3_write_done(xid, - req->PersistentFileId, - io_parms->tcon->tid, - io_parms->tcon->ses->Suid, - io_parms->offset, *nbytes); - } - - cifs_small_buf_release(req); - free_rsp_buf(resp_buftype, rsp); - return rc; -} - -int posix_info_sid_size(const void *beg, const void *end) -{ - size_t subauth; - int total; - - if (beg + 1 > end) - return -1; - - subauth = *(u8 *)(beg+1); - if (subauth < 1 || subauth > 15) - return -1; - - total = 1 + 1 + 6 + 4*subauth; - if (beg + total > end) - return -1; - - return total; -} - -int posix_info_parse(const void *beg, const void *end, - struct smb2_posix_info_parsed *out) - -{ - int total_len = 0; - int owner_len, group_len; - int name_len; - const void *owner_sid; - const void *group_sid; - const void *name; - - /* if no end bound given, assume payload to be correct */ - if (!end) { - const struct smb2_posix_info *p = beg; - - end = beg + le32_to_cpu(p->NextEntryOffset); - /* last element will have a 0 offset, pick a sensible bound */ - if (end == beg) - end += 0xFFFF; - } - - /* check base buf */ - if (beg + sizeof(struct smb2_posix_info) > end) - return -1; - total_len = sizeof(struct smb2_posix_info); - - /* check owner sid */ - owner_sid = beg + total_len; - owner_len = posix_info_sid_size(owner_sid, end); - if (owner_len < 0) - return -1; - total_len += owner_len; - - /* check group sid */ - group_sid = beg + total_len; - group_len = posix_info_sid_size(group_sid, end); - if (group_len < 0) - return -1; - total_len += group_len; - - /* check name len */ - if (beg + total_len + 4 > end) - return -1; - name_len = le32_to_cpu(*(__le32 *)(beg + total_len)); - if (name_len < 1 || name_len > 0xFFFF) - return -1; - total_len += 4; - - /* check name */ - name = beg + total_len; - if (name + name_len > end) - return -1; - total_len += name_len; - - if (out) { - out->base = beg; - out->size = total_len; - out->name_len = name_len; - out->name = name; - memcpy(&out->owner, owner_sid, owner_len); - memcpy(&out->group, group_sid, group_len); - } - return total_len; -} - -static int posix_info_extra_size(const void *beg, const void *end) -{ - int len = posix_info_parse(beg, end, NULL); - - if (len < 0) - return -1; - return len - sizeof(struct smb2_posix_info); -} - -static unsigned int -num_entries(int infotype, char *bufstart, char *end_of_buf, char **lastentry, - size_t size) -{ - int len; - unsigned int entrycount = 0; - unsigned int next_offset = 0; - char *entryptr; - FILE_DIRECTORY_INFO *dir_info; - - if (bufstart == NULL) - return 0; - - entryptr = bufstart; - - while (1) { - if (entryptr + next_offset < entryptr || - entryptr + next_offset > end_of_buf || - entryptr + next_offset + size > end_of_buf) { - cifs_dbg(VFS, "malformed search entry would overflow\n"); - break; - } - - entryptr = entryptr + next_offset; - dir_info = (FILE_DIRECTORY_INFO *)entryptr; - - if (infotype == SMB_FIND_FILE_POSIX_INFO) - len = posix_info_extra_size(entryptr, end_of_buf); - else - len = le32_to_cpu(dir_info->FileNameLength); - - if (len < 0 || - entryptr + len < entryptr || - entryptr + len > end_of_buf || - entryptr + len + size > end_of_buf) { - cifs_dbg(VFS, "directory entry name would overflow frame end of buf %p\n", - end_of_buf); - break; - } - - *lastentry = entryptr; - entrycount++; - - next_offset = le32_to_cpu(dir_info->NextEntryOffset); - if (!next_offset) - break; - } - - return entrycount; -} - -/* - * Readdir/FindFirst - */ -int SMB2_query_directory_init(const unsigned int xid, - struct cifs_tcon *tcon, - struct TCP_Server_Info *server, - struct smb_rqst *rqst, - u64 persistent_fid, u64 volatile_fid, - int index, int info_level) -{ - struct smb2_query_directory_req *req; - unsigned char *bufptr; - __le16 asteriks = cpu_to_le16('*'); - unsigned int output_size = CIFSMaxBufSize - - MAX_SMB2_CREATE_RESPONSE_SIZE - - MAX_SMB2_CLOSE_RESPONSE_SIZE; - unsigned int total_len; - struct kvec *iov = rqst->rq_iov; - int len, rc; - - rc = smb2_plain_req_init(SMB2_QUERY_DIRECTORY, tcon, server, - (void **) &req, &total_len); - if (rc) - return rc; - - switch (info_level) { - case SMB_FIND_FILE_DIRECTORY_INFO: - req->FileInformationClass = FILE_DIRECTORY_INFORMATION; - break; - case SMB_FIND_FILE_ID_FULL_DIR_INFO: - req->FileInformationClass = FILEID_FULL_DIRECTORY_INFORMATION; - break; - case SMB_FIND_FILE_POSIX_INFO: - req->FileInformationClass = SMB_FIND_FILE_POSIX_INFO; - break; - default: - cifs_tcon_dbg(VFS, "info level %u isn't supported\n", - info_level); - return -EINVAL; - } - - req->FileIndex = cpu_to_le32(index); - req->PersistentFileId = persistent_fid; - req->VolatileFileId = volatile_fid; - - len = 0x2; - bufptr = req->Buffer; - memcpy(bufptr, &asteriks, len); - - req->FileNameOffset = - cpu_to_le16(sizeof(struct smb2_query_directory_req) - 1); - req->FileNameLength = cpu_to_le16(len); - /* - * BB could be 30 bytes or so longer if we used SMB2 specific - * buffer lengths, but this is safe and close enough. - */ - output_size = min_t(unsigned int, output_size, server->maxBuf); - output_size = min_t(unsigned int, output_size, 2 << 15); - req->OutputBufferLength = cpu_to_le32(output_size); - - iov[0].iov_base = (char *)req; - /* 1 for Buffer */ - iov[0].iov_len = total_len - 1; - - iov[1].iov_base = (char *)(req->Buffer); - iov[1].iov_len = len; - - trace_smb3_query_dir_enter(xid, persistent_fid, tcon->tid, - tcon->ses->Suid, index, output_size); - - return 0; -} - -void SMB2_query_directory_free(struct smb_rqst *rqst) -{ - if (rqst && rqst->rq_iov) { - cifs_small_buf_release(rqst->rq_iov[0].iov_base); /* request */ - } -} - -int -smb2_parse_query_directory(struct cifs_tcon *tcon, - struct kvec *rsp_iov, - int resp_buftype, - struct cifs_search_info *srch_inf) -{ - struct smb2_query_directory_rsp *rsp; - size_t info_buf_size; - char *end_of_smb; - int rc; - - rsp = (struct smb2_query_directory_rsp *)rsp_iov->iov_base; - - switch (srch_inf->info_level) { - case SMB_FIND_FILE_DIRECTORY_INFO: - info_buf_size = sizeof(FILE_DIRECTORY_INFO) - 1; - break; - case SMB_FIND_FILE_ID_FULL_DIR_INFO: - info_buf_size = sizeof(SEARCH_ID_FULL_DIR_INFO) - 1; - break; - case SMB_FIND_FILE_POSIX_INFO: - /* note that posix payload are variable size */ - info_buf_size = sizeof(struct smb2_posix_info); - break; - default: - cifs_tcon_dbg(VFS, "info level %u isn't supported\n", - srch_inf->info_level); - return -EINVAL; - } - - rc = smb2_validate_iov(le16_to_cpu(rsp->OutputBufferOffset), - le32_to_cpu(rsp->OutputBufferLength), rsp_iov, - info_buf_size); - if (rc) { - cifs_tcon_dbg(VFS, "bad info payload"); - return rc; - } - - srch_inf->unicode = true; - - if (srch_inf->ntwrk_buf_start) { - if (srch_inf->smallBuf) - cifs_small_buf_release(srch_inf->ntwrk_buf_start); - else - cifs_buf_release(srch_inf->ntwrk_buf_start); - } - srch_inf->ntwrk_buf_start = (char *)rsp; - srch_inf->srch_entries_start = srch_inf->last_entry = - (char *)rsp + le16_to_cpu(rsp->OutputBufferOffset); - end_of_smb = rsp_iov->iov_len + (char *)rsp; - - srch_inf->entries_in_buffer = num_entries( - srch_inf->info_level, - srch_inf->srch_entries_start, - end_of_smb, - &srch_inf->last_entry, - info_buf_size); - - srch_inf->index_of_last_entry += srch_inf->entries_in_buffer; - cifs_dbg(FYI, "num entries %d last_index %lld srch start %p srch end %p\n", - srch_inf->entries_in_buffer, srch_inf->index_of_last_entry, - srch_inf->srch_entries_start, srch_inf->last_entry); - if (resp_buftype == CIFS_LARGE_BUFFER) - srch_inf->smallBuf = false; - else if (resp_buftype == CIFS_SMALL_BUFFER) - srch_inf->smallBuf = true; - else - cifs_tcon_dbg(VFS, "Invalid search buffer type\n"); - - return 0; -} - -int -SMB2_query_directory(const unsigned int xid, struct cifs_tcon *tcon, - u64 persistent_fid, u64 volatile_fid, int index, - struct cifs_search_info *srch_inf) -{ - struct smb_rqst rqst; - struct kvec iov[SMB2_QUERY_DIRECTORY_IOV_SIZE]; - struct smb2_query_directory_rsp *rsp = NULL; - int resp_buftype = CIFS_NO_BUFFER; - struct kvec rsp_iov; - int rc = 0; - struct cifs_ses *ses = tcon->ses; - struct TCP_Server_Info *server = cifs_pick_channel(ses); - int flags = 0; - - if (!ses || !(ses->server)) - return -EIO; - - if (smb3_encryption_required(tcon)) - flags |= CIFS_TRANSFORM_REQ; - - memset(&rqst, 0, sizeof(struct smb_rqst)); - memset(&iov, 0, sizeof(iov)); - rqst.rq_iov = iov; - rqst.rq_nvec = SMB2_QUERY_DIRECTORY_IOV_SIZE; - - rc = SMB2_query_directory_init(xid, tcon, server, - &rqst, persistent_fid, - volatile_fid, index, - srch_inf->info_level); - if (rc) - goto qdir_exit; - - rc = cifs_send_recv(xid, ses, server, - &rqst, &resp_buftype, flags, &rsp_iov); - rsp = (struct smb2_query_directory_rsp *)rsp_iov.iov_base; - - if (rc) { - if (rc == -ENODATA && - rsp->hdr.Status == STATUS_NO_MORE_FILES) { - trace_smb3_query_dir_done(xid, persistent_fid, - tcon->tid, tcon->ses->Suid, index, 0); - srch_inf->endOfSearch = true; - rc = 0; - } else { - trace_smb3_query_dir_err(xid, persistent_fid, tcon->tid, - tcon->ses->Suid, index, 0, rc); - cifs_stats_fail_inc(tcon, SMB2_QUERY_DIRECTORY_HE); - } - goto qdir_exit; - } - - rc = smb2_parse_query_directory(tcon, &rsp_iov, resp_buftype, - srch_inf); - if (rc) { - trace_smb3_query_dir_err(xid, persistent_fid, tcon->tid, - tcon->ses->Suid, index, 0, rc); - goto qdir_exit; - } - resp_buftype = CIFS_NO_BUFFER; - - trace_smb3_query_dir_done(xid, persistent_fid, tcon->tid, - tcon->ses->Suid, index, srch_inf->entries_in_buffer); - -qdir_exit: - SMB2_query_directory_free(&rqst); - free_rsp_buf(resp_buftype, rsp); - return rc; -} - -int -SMB2_set_info_init(struct cifs_tcon *tcon, struct TCP_Server_Info *server, - struct smb_rqst *rqst, - u64 persistent_fid, u64 volatile_fid, u32 pid, - u8 info_class, u8 info_type, u32 additional_info, - void **data, unsigned int *size) -{ - struct smb2_set_info_req *req; - struct kvec *iov = rqst->rq_iov; - unsigned int i, total_len; - int rc; - - rc = smb2_plain_req_init(SMB2_SET_INFO, tcon, server, - (void **) &req, &total_len); - if (rc) - return rc; - - req->hdr.Id.SyncId.ProcessId = cpu_to_le32(pid); - req->InfoType = info_type; - req->FileInfoClass = info_class; - req->PersistentFileId = persistent_fid; - req->VolatileFileId = volatile_fid; - req->AdditionalInformation = cpu_to_le32(additional_info); - - req->BufferOffset = - cpu_to_le16(sizeof(struct smb2_set_info_req) - 1); - req->BufferLength = cpu_to_le32(*size); - - memcpy(req->Buffer, *data, *size); - total_len += *size; - - iov[0].iov_base = (char *)req; - /* 1 for Buffer */ - iov[0].iov_len = total_len - 1; - - for (i = 1; i < rqst->rq_nvec; i++) { - le32_add_cpu(&req->BufferLength, size[i]); - iov[i].iov_base = (char *)data[i]; - iov[i].iov_len = size[i]; - } - - return 0; -} - -void -SMB2_set_info_free(struct smb_rqst *rqst) -{ - if (rqst && rqst->rq_iov) - cifs_buf_release(rqst->rq_iov[0].iov_base); /* request */ -} - -static int -send_set_info(const unsigned int xid, struct cifs_tcon *tcon, - u64 persistent_fid, u64 volatile_fid, u32 pid, u8 info_class, - u8 info_type, u32 additional_info, unsigned int num, - void **data, unsigned int *size) -{ - struct smb_rqst rqst; - struct smb2_set_info_rsp *rsp = NULL; - struct kvec *iov; - struct kvec rsp_iov; - int rc = 0; - int resp_buftype; - struct cifs_ses *ses = tcon->ses; - struct TCP_Server_Info *server = cifs_pick_channel(ses); - int flags = 0; - - if (!ses || !server) - return -EIO; - - if (!num) - return -EINVAL; - - if (smb3_encryption_required(tcon)) - flags |= CIFS_TRANSFORM_REQ; - - iov = kmalloc_array(num, sizeof(struct kvec), GFP_KERNEL); - if (!iov) - return -ENOMEM; - - memset(&rqst, 0, sizeof(struct smb_rqst)); - rqst.rq_iov = iov; - rqst.rq_nvec = num; - - rc = SMB2_set_info_init(tcon, server, - &rqst, persistent_fid, volatile_fid, pid, - info_class, info_type, additional_info, - data, size); - if (rc) { - kfree(iov); - return rc; - } - - - rc = cifs_send_recv(xid, ses, server, - &rqst, &resp_buftype, flags, - &rsp_iov); - SMB2_set_info_free(&rqst); - rsp = (struct smb2_set_info_rsp *)rsp_iov.iov_base; - - if (rc != 0) { - cifs_stats_fail_inc(tcon, SMB2_SET_INFO_HE); - trace_smb3_set_info_err(xid, persistent_fid, tcon->tid, - ses->Suid, info_class, (__u32)info_type, rc); - } - - free_rsp_buf(resp_buftype, rsp); - kfree(iov); - return rc; -} - -int -SMB2_set_eof(const unsigned int xid, struct cifs_tcon *tcon, u64 persistent_fid, - u64 volatile_fid, u32 pid, __le64 *eof) -{ - struct smb2_file_eof_info info; - void *data; - unsigned int size; - - info.EndOfFile = *eof; - - data = &info; - size = sizeof(struct smb2_file_eof_info); - - trace_smb3_set_eof(xid, persistent_fid, tcon->tid, tcon->ses->Suid, le64_to_cpu(*eof)); - - return send_set_info(xid, tcon, persistent_fid, volatile_fid, - pid, FILE_END_OF_FILE_INFORMATION, SMB2_O_INFO_FILE, - 0, 1, &data, &size); -} - -int -SMB2_set_acl(const unsigned int xid, struct cifs_tcon *tcon, - u64 persistent_fid, u64 volatile_fid, - struct cifs_ntsd *pnntsd, int pacllen, int aclflag) -{ - return send_set_info(xid, tcon, persistent_fid, volatile_fid, - current->tgid, 0, SMB2_O_INFO_SECURITY, aclflag, - 1, (void **)&pnntsd, &pacllen); -} - -int -SMB2_set_ea(const unsigned int xid, struct cifs_tcon *tcon, - u64 persistent_fid, u64 volatile_fid, - struct smb2_file_full_ea_info *buf, int len) -{ - return send_set_info(xid, tcon, persistent_fid, volatile_fid, - current->tgid, FILE_FULL_EA_INFORMATION, SMB2_O_INFO_FILE, - 0, 1, (void **)&buf, &len); -} - -int -SMB2_oplock_break(const unsigned int xid, struct cifs_tcon *tcon, - const u64 persistent_fid, const u64 volatile_fid, - __u8 oplock_level) -{ - struct smb_rqst rqst; - int rc; - struct smb2_oplock_break *req = NULL; - struct cifs_ses *ses = tcon->ses; - struct TCP_Server_Info *server = cifs_pick_channel(ses); - int flags = CIFS_OBREAK_OP; - unsigned int total_len; - struct kvec iov[1]; - struct kvec rsp_iov; - int resp_buf_type; - - cifs_dbg(FYI, "SMB2_oplock_break\n"); - rc = smb2_plain_req_init(SMB2_OPLOCK_BREAK, tcon, server, - (void **) &req, &total_len); - if (rc) - return rc; - - if (smb3_encryption_required(tcon)) - flags |= CIFS_TRANSFORM_REQ; - - req->VolatileFid = volatile_fid; - req->PersistentFid = persistent_fid; - req->OplockLevel = oplock_level; - req->hdr.CreditRequest = cpu_to_le16(1); - - flags |= CIFS_NO_RSP_BUF; - - iov[0].iov_base = (char *)req; - iov[0].iov_len = total_len; - - memset(&rqst, 0, sizeof(struct smb_rqst)); - rqst.rq_iov = iov; - rqst.rq_nvec = 1; - - rc = cifs_send_recv(xid, ses, server, - &rqst, &resp_buf_type, flags, &rsp_iov); - cifs_small_buf_release(req); - - if (rc) { - cifs_stats_fail_inc(tcon, SMB2_OPLOCK_BREAK_HE); - cifs_dbg(FYI, "Send error in Oplock Break = %d\n", rc); - } - - return rc; -} - -void -smb2_copy_fs_info_to_kstatfs(struct smb2_fs_full_size_info *pfs_inf, - struct kstatfs *kst) -{ - kst->f_bsize = le32_to_cpu(pfs_inf->BytesPerSector) * - le32_to_cpu(pfs_inf->SectorsPerAllocationUnit); - kst->f_blocks = le64_to_cpu(pfs_inf->TotalAllocationUnits); - kst->f_bfree = kst->f_bavail = - le64_to_cpu(pfs_inf->CallerAvailableAllocationUnits); - return; -} - -static void -copy_posix_fs_info_to_kstatfs(FILE_SYSTEM_POSIX_INFO *response_data, - struct kstatfs *kst) -{ - kst->f_bsize = le32_to_cpu(response_data->BlockSize); - kst->f_blocks = le64_to_cpu(response_data->TotalBlocks); - kst->f_bfree = le64_to_cpu(response_data->BlocksAvail); - if (response_data->UserBlocksAvail == cpu_to_le64(-1)) - kst->f_bavail = kst->f_bfree; - else - kst->f_bavail = le64_to_cpu(response_data->UserBlocksAvail); - if (response_data->TotalFileNodes != cpu_to_le64(-1)) - kst->f_files = le64_to_cpu(response_data->TotalFileNodes); - if (response_data->FreeFileNodes != cpu_to_le64(-1)) - kst->f_ffree = le64_to_cpu(response_data->FreeFileNodes); - - return; -} - -static int -build_qfs_info_req(struct kvec *iov, struct cifs_tcon *tcon, - struct TCP_Server_Info *server, - int level, int outbuf_len, u64 persistent_fid, - u64 volatile_fid) -{ - int rc; - struct smb2_query_info_req *req; - unsigned int total_len; - - cifs_dbg(FYI, "Query FSInfo level %d\n", level); - - if ((tcon->ses == NULL) || server == NULL) - return -EIO; - - rc = smb2_plain_req_init(SMB2_QUERY_INFO, tcon, server, - (void **) &req, &total_len); - if (rc) - return rc; - - req->InfoType = SMB2_O_INFO_FILESYSTEM; - req->FileInfoClass = level; - req->PersistentFileId = persistent_fid; - req->VolatileFileId = volatile_fid; - /* 1 for pad */ - req->InputBufferOffset = - cpu_to_le16(sizeof(struct smb2_query_info_req) - 1); - req->OutputBufferLength = cpu_to_le32( - outbuf_len + sizeof(struct smb2_query_info_rsp) - 1); - - iov->iov_base = (char *)req; - iov->iov_len = total_len; - return 0; -} - -int -SMB311_posix_qfs_info(const unsigned int xid, struct cifs_tcon *tcon, - u64 persistent_fid, u64 volatile_fid, struct kstatfs *fsdata) -{ - struct smb_rqst rqst; - struct smb2_query_info_rsp *rsp = NULL; - struct kvec iov; - struct kvec rsp_iov; - int rc = 0; - int resp_buftype; - struct cifs_ses *ses = tcon->ses; - struct TCP_Server_Info *server = cifs_pick_channel(ses); - FILE_SYSTEM_POSIX_INFO *info = NULL; - int flags = 0; - - rc = build_qfs_info_req(&iov, tcon, server, - FS_POSIX_INFORMATION, - sizeof(FILE_SYSTEM_POSIX_INFO), - persistent_fid, volatile_fid); - if (rc) - return rc; - - if (smb3_encryption_required(tcon)) - flags |= CIFS_TRANSFORM_REQ; - - memset(&rqst, 0, sizeof(struct smb_rqst)); - rqst.rq_iov = &iov; - rqst.rq_nvec = 1; - - rc = cifs_send_recv(xid, ses, server, - &rqst, &resp_buftype, flags, &rsp_iov); - cifs_small_buf_release(iov.iov_base); - if (rc) { - cifs_stats_fail_inc(tcon, SMB2_QUERY_INFO_HE); - goto posix_qfsinf_exit; - } - rsp = (struct smb2_query_info_rsp *)rsp_iov.iov_base; - - info = (FILE_SYSTEM_POSIX_INFO *)( - le16_to_cpu(rsp->OutputBufferOffset) + (char *)rsp); - rc = smb2_validate_iov(le16_to_cpu(rsp->OutputBufferOffset), - le32_to_cpu(rsp->OutputBufferLength), &rsp_iov, - sizeof(FILE_SYSTEM_POSIX_INFO)); - if (!rc) - copy_posix_fs_info_to_kstatfs(info, fsdata); - -posix_qfsinf_exit: - free_rsp_buf(resp_buftype, rsp_iov.iov_base); - return rc; -} - -int -SMB2_QFS_info(const unsigned int xid, struct cifs_tcon *tcon, - u64 persistent_fid, u64 volatile_fid, struct kstatfs *fsdata) -{ - struct smb_rqst rqst; - struct smb2_query_info_rsp *rsp = NULL; - struct kvec iov; - struct kvec rsp_iov; - int rc = 0; - int resp_buftype; - struct cifs_ses *ses = tcon->ses; - struct TCP_Server_Info *server = cifs_pick_channel(ses); - struct smb2_fs_full_size_info *info = NULL; - int flags = 0; - - rc = build_qfs_info_req(&iov, tcon, server, - FS_FULL_SIZE_INFORMATION, - sizeof(struct smb2_fs_full_size_info), - persistent_fid, volatile_fid); - if (rc) - return rc; - - if (smb3_encryption_required(tcon)) - flags |= CIFS_TRANSFORM_REQ; - - memset(&rqst, 0, sizeof(struct smb_rqst)); - rqst.rq_iov = &iov; - rqst.rq_nvec = 1; - - rc = cifs_send_recv(xid, ses, server, - &rqst, &resp_buftype, flags, &rsp_iov); - cifs_small_buf_release(iov.iov_base); - if (rc) { - cifs_stats_fail_inc(tcon, SMB2_QUERY_INFO_HE); - goto qfsinf_exit; - } - rsp = (struct smb2_query_info_rsp *)rsp_iov.iov_base; - - info = (struct smb2_fs_full_size_info *)( - le16_to_cpu(rsp->OutputBufferOffset) + (char *)rsp); - rc = smb2_validate_iov(le16_to_cpu(rsp->OutputBufferOffset), - le32_to_cpu(rsp->OutputBufferLength), &rsp_iov, - sizeof(struct smb2_fs_full_size_info)); - if (!rc) - smb2_copy_fs_info_to_kstatfs(info, fsdata); - -qfsinf_exit: - free_rsp_buf(resp_buftype, rsp_iov.iov_base); - return rc; -} - -int -SMB2_QFS_attr(const unsigned int xid, struct cifs_tcon *tcon, - u64 persistent_fid, u64 volatile_fid, int level) -{ - struct smb_rqst rqst; - struct smb2_query_info_rsp *rsp = NULL; - struct kvec iov; - struct kvec rsp_iov; - int rc = 0; - int resp_buftype, max_len, min_len; - struct cifs_ses *ses = tcon->ses; - struct TCP_Server_Info *server = cifs_pick_channel(ses); - unsigned int rsp_len, offset; - int flags = 0; - - if (level == FS_DEVICE_INFORMATION) { - max_len = sizeof(FILE_SYSTEM_DEVICE_INFO); - min_len = sizeof(FILE_SYSTEM_DEVICE_INFO); - } else if (level == FS_ATTRIBUTE_INFORMATION) { - max_len = sizeof(FILE_SYSTEM_ATTRIBUTE_INFO); - min_len = MIN_FS_ATTR_INFO_SIZE; - } else if (level == FS_SECTOR_SIZE_INFORMATION) { - max_len = sizeof(struct smb3_fs_ss_info); - min_len = sizeof(struct smb3_fs_ss_info); - } else if (level == FS_VOLUME_INFORMATION) { - max_len = sizeof(struct smb3_fs_vol_info) + MAX_VOL_LABEL_LEN; - min_len = sizeof(struct smb3_fs_vol_info); - } else { - cifs_dbg(FYI, "Invalid qfsinfo level %d\n", level); - return -EINVAL; - } - - rc = build_qfs_info_req(&iov, tcon, server, - level, max_len, - persistent_fid, volatile_fid); - if (rc) - return rc; - - if (smb3_encryption_required(tcon)) - flags |= CIFS_TRANSFORM_REQ; - - memset(&rqst, 0, sizeof(struct smb_rqst)); - rqst.rq_iov = &iov; - rqst.rq_nvec = 1; - - rc = cifs_send_recv(xid, ses, server, - &rqst, &resp_buftype, flags, &rsp_iov); - cifs_small_buf_release(iov.iov_base); - if (rc) { - cifs_stats_fail_inc(tcon, SMB2_QUERY_INFO_HE); - goto qfsattr_exit; - } - rsp = (struct smb2_query_info_rsp *)rsp_iov.iov_base; - - rsp_len = le32_to_cpu(rsp->OutputBufferLength); - offset = le16_to_cpu(rsp->OutputBufferOffset); - rc = smb2_validate_iov(offset, rsp_len, &rsp_iov, min_len); - if (rc) - goto qfsattr_exit; - - if (level == FS_ATTRIBUTE_INFORMATION) - memcpy(&tcon->fsAttrInfo, offset - + (char *)rsp, min_t(unsigned int, - rsp_len, max_len)); - else if (level == FS_DEVICE_INFORMATION) - memcpy(&tcon->fsDevInfo, offset - + (char *)rsp, sizeof(FILE_SYSTEM_DEVICE_INFO)); - else if (level == FS_SECTOR_SIZE_INFORMATION) { - struct smb3_fs_ss_info *ss_info = (struct smb3_fs_ss_info *) - (offset + (char *)rsp); - tcon->ss_flags = le32_to_cpu(ss_info->Flags); - tcon->perf_sector_size = - le32_to_cpu(ss_info->PhysicalBytesPerSectorForPerf); - } else if (level == FS_VOLUME_INFORMATION) { - struct smb3_fs_vol_info *vol_info = (struct smb3_fs_vol_info *) - (offset + (char *)rsp); - tcon->vol_serial_number = vol_info->VolumeSerialNumber; - tcon->vol_create_time = vol_info->VolumeCreationTime; - } - -qfsattr_exit: - free_rsp_buf(resp_buftype, rsp_iov.iov_base); - return rc; -} - -int -smb2_lockv(const unsigned int xid, struct cifs_tcon *tcon, - const __u64 persist_fid, const __u64 volatile_fid, const __u32 pid, - const __u32 num_lock, struct smb2_lock_element *buf) -{ - struct smb_rqst rqst; - int rc = 0; - struct smb2_lock_req *req = NULL; - struct kvec iov[2]; - struct kvec rsp_iov; - int resp_buf_type; - unsigned int count; - int flags = CIFS_NO_RSP_BUF; - unsigned int total_len; - struct TCP_Server_Info *server = cifs_pick_channel(tcon->ses); - - cifs_dbg(FYI, "smb2_lockv num lock %d\n", num_lock); - - rc = smb2_plain_req_init(SMB2_LOCK, tcon, server, - (void **) &req, &total_len); - if (rc) - return rc; - - if (smb3_encryption_required(tcon)) - flags |= CIFS_TRANSFORM_REQ; - - req->hdr.Id.SyncId.ProcessId = cpu_to_le32(pid); - req->LockCount = cpu_to_le16(num_lock); - - req->PersistentFileId = persist_fid; - req->VolatileFileId = volatile_fid; - - count = num_lock * sizeof(struct smb2_lock_element); - - iov[0].iov_base = (char *)req; - iov[0].iov_len = total_len - sizeof(struct smb2_lock_element); - iov[1].iov_base = (char *)buf; - iov[1].iov_len = count; - - cifs_stats_inc(&tcon->stats.cifs_stats.num_locks); - - memset(&rqst, 0, sizeof(struct smb_rqst)); - rqst.rq_iov = iov; - rqst.rq_nvec = 2; - - rc = cifs_send_recv(xid, tcon->ses, server, - &rqst, &resp_buf_type, flags, - &rsp_iov); - cifs_small_buf_release(req); - if (rc) { - cifs_dbg(FYI, "Send error in smb2_lockv = %d\n", rc); - cifs_stats_fail_inc(tcon, SMB2_LOCK_HE); - trace_smb3_lock_err(xid, persist_fid, tcon->tid, - tcon->ses->Suid, rc); - } - - return rc; -} - -int -SMB2_lock(const unsigned int xid, struct cifs_tcon *tcon, - const __u64 persist_fid, const __u64 volatile_fid, const __u32 pid, - const __u64 length, const __u64 offset, const __u32 lock_flags, - const bool wait) -{ - struct smb2_lock_element lock; - - lock.Offset = cpu_to_le64(offset); - lock.Length = cpu_to_le64(length); - lock.Flags = cpu_to_le32(lock_flags); - if (!wait && lock_flags != SMB2_LOCKFLAG_UNLOCK) - lock.Flags |= cpu_to_le32(SMB2_LOCKFLAG_FAIL_IMMEDIATELY); - - return smb2_lockv(xid, tcon, persist_fid, volatile_fid, pid, 1, &lock); -} - -int -SMB2_lease_break(const unsigned int xid, struct cifs_tcon *tcon, - __u8 *lease_key, const __le32 lease_state) -{ - struct smb_rqst rqst; - int rc; - struct smb2_lease_ack *req = NULL; - struct cifs_ses *ses = tcon->ses; - int flags = CIFS_OBREAK_OP; - unsigned int total_len; - struct kvec iov[1]; - struct kvec rsp_iov; - int resp_buf_type; - __u64 *please_key_high; - __u64 *please_key_low; - struct TCP_Server_Info *server = cifs_pick_channel(tcon->ses); - - cifs_dbg(FYI, "SMB2_lease_break\n"); - rc = smb2_plain_req_init(SMB2_OPLOCK_BREAK, tcon, server, - (void **) &req, &total_len); - if (rc) - return rc; - - if (smb3_encryption_required(tcon)) - flags |= CIFS_TRANSFORM_REQ; - - req->hdr.CreditRequest = cpu_to_le16(1); - req->StructureSize = cpu_to_le16(36); - total_len += 12; - - memcpy(req->LeaseKey, lease_key, 16); - req->LeaseState = lease_state; - - flags |= CIFS_NO_RSP_BUF; - - iov[0].iov_base = (char *)req; - iov[0].iov_len = total_len; - - memset(&rqst, 0, sizeof(struct smb_rqst)); - rqst.rq_iov = iov; - rqst.rq_nvec = 1; - - rc = cifs_send_recv(xid, ses, server, - &rqst, &resp_buf_type, flags, &rsp_iov); - cifs_small_buf_release(req); - - please_key_low = (__u64 *)lease_key; - please_key_high = (__u64 *)(lease_key+8); - if (rc) { - cifs_stats_fail_inc(tcon, SMB2_OPLOCK_BREAK_HE); - trace_smb3_lease_err(le32_to_cpu(lease_state), tcon->tid, - ses->Suid, *please_key_low, *please_key_high, rc); - cifs_dbg(FYI, "Send error in Lease Break = %d\n", rc); - } else - trace_smb3_lease_done(le32_to_cpu(lease_state), tcon->tid, - ses->Suid, *please_key_low, *please_key_high); - - return rc; -} diff --git a/fs/cifs/smb2pdu.h b/fs/cifs/smb2pdu.h deleted file mode 100644 index 1237bb86e93a8..0000000000000 --- a/fs/cifs/smb2pdu.h +++ /dev/null @@ -1,434 +0,0 @@ -/* SPDX-License-Identifier: LGPL-2.1 */ -/* - * - * Copyright (c) International Business Machines Corp., 2009, 2013 - * Etersoft, 2012 - * Author(s): Steve French (sfrench@us.ibm.com) - * Pavel Shilovsky (pshilovsky@samba.org) 2012 - * - */ - -#ifndef _SMB2PDU_H -#define _SMB2PDU_H - -#include -#include "cifsacl.h" - -/* 52 transform hdr + 64 hdr + 88 create rsp */ -#define SMB2_TRANSFORM_HEADER_SIZE 52 -#define MAX_SMB2_HDR_SIZE 204 - -/* The total header size for SMB2 read and write */ -#define SMB2_READWRITE_PDU_HEADER_SIZE (48 + sizeof(struct smb2_hdr)) - -/* See MS-SMB2 2.2.43 */ -struct smb2_rdma_transform { - __le16 RdmaDescriptorOffset; - __le16 RdmaDescriptorLength; - __le32 Channel; /* for values see channel description in smb2 read above */ - __le16 TransformCount; - __le16 Reserved1; - __le32 Reserved2; -} __packed; - -/* TransformType */ -#define SMB2_RDMA_TRANSFORM_TYPE_ENCRYPTION 0x0001 -#define SMB2_RDMA_TRANSFORM_TYPE_SIGNING 0x0002 - -struct smb2_rdma_crypto_transform { - __le16 TransformType; - __le16 SignatureLength; - __le16 NonceLength; - __u16 Reserved; - __u8 Signature[]; /* variable length */ - /* u8 Nonce[] */ - /* followed by padding */ -} __packed; - -/* - * Definitions for SMB2 Protocol Data Units (network frames) - * - * See MS-SMB2.PDF specification for protocol details. - * The Naming convention is the lower case version of the SMB2 - * command code name for the struct. Note that structures must be packed. - * - */ - -#define COMPOUND_FID 0xFFFFFFFFFFFFFFFFULL - -#define SMB2_SYMLINK_STRUCT_SIZE \ - (sizeof(struct smb2_err_rsp) - 1 + sizeof(struct smb2_symlink_err_rsp)) - -#define SYMLINK_ERROR_TAG 0x4c4d5953 - -struct smb2_symlink_err_rsp { - __le32 SymLinkLength; - __le32 SymLinkErrorTag; - __le32 ReparseTag; - __le16 ReparseDataLength; - __le16 UnparsedPathLength; - __le16 SubstituteNameOffset; - __le16 SubstituteNameLength; - __le16 PrintNameOffset; - __le16 PrintNameLength; - __le32 Flags; - __u8 PathBuffer[]; -} __packed; - -/* SMB 3.1.1 and later dialects. See MS-SMB2 section 2.2.2.1 */ -struct smb2_error_context_rsp { - __le32 ErrorDataLength; - __le32 ErrorId; - __u8 ErrorContextData; /* ErrorDataLength long array */ -} __packed; - -/* ErrorId values */ -#define SMB2_ERROR_ID_DEFAULT 0x00000000 -#define SMB2_ERROR_ID_SHARE_REDIRECT cpu_to_le32(0x72645253) /* "rdRS" */ - -/* Defines for Type field below (see MS-SMB2 2.2.2.2.2.1) */ -#define MOVE_DST_IPADDR_V4 cpu_to_le32(0x00000001) -#define MOVE_DST_IPADDR_V6 cpu_to_le32(0x00000002) - -struct move_dst_ipaddr { - __le32 Type; - __u32 Reserved; - __u8 address[16]; /* IPv4 followed by 12 bytes rsvd or IPv6 address */ -} __packed; - -struct share_redirect_error_context_rsp { - __le32 StructureSize; - __le32 NotificationType; - __le32 ResourceNameOffset; - __le32 ResourceNameLength; - __le16 Reserved; - __le16 TargetType; - __le32 IPAddrCount; - struct move_dst_ipaddr IpAddrMoveList[]; - /* __u8 ResourceName[] */ /* Name of share as counted Unicode string */ -} __packed; - -/* - * Maximum number of iovs we need for an open/create request. - * [0] : struct smb2_create_req - * [1] : path - * [2] : lease context - * [3] : durable context - * [4] : posix context - * [5] : time warp context - * [6] : query id context - * [7] : compound padding - */ -#define SMB2_CREATE_IOV_SIZE 8 - -/* - * Maximum size of a SMB2_CREATE response is 64 (smb2 header) + - * 88 (fixed part of create response) + 520 (path) + 208 (contexts) + - * 2 bytes of padding. - */ -#define MAX_SMB2_CREATE_RESPONSE_SIZE 880 - -#define SMB2_LEASE_READ_CACHING_HE 0x01 -#define SMB2_LEASE_HANDLE_CACHING_HE 0x02 -#define SMB2_LEASE_WRITE_CACHING_HE 0x04 - -struct create_durable { - struct create_context ccontext; - __u8 Name[8]; - union { - __u8 Reserved[16]; - struct { - __u64 PersistentFileId; - __u64 VolatileFileId; - } Fid; - } Data; -} __packed; - -/* See MS-SMB2 2.2.13.2.11 */ -/* Flags */ -#define SMB2_DHANDLE_FLAG_PERSISTENT 0x00000002 -struct durable_context_v2 { - __le32 Timeout; - __le32 Flags; - __u64 Reserved; - __u8 CreateGuid[16]; -} __packed; - -struct create_durable_v2 { - struct create_context ccontext; - __u8 Name[8]; - struct durable_context_v2 dcontext; -} __packed; - -/* See MS-SMB2 2.2.13.2.12 */ -struct durable_reconnect_context_v2 { - struct { - __u64 PersistentFileId; - __u64 VolatileFileId; - } Fid; - __u8 CreateGuid[16]; - __le32 Flags; /* see above DHANDLE_FLAG_PERSISTENT */ -} __packed; - -/* See MS-SMB2 2.2.14.2.9 */ -struct create_on_disk_id { - struct create_context ccontext; - __u8 Name[8]; - __le64 DiskFileId; - __le64 VolumeId; - __u32 Reserved[4]; -} __packed; - -/* See MS-SMB2 2.2.14.2.12 */ -struct durable_reconnect_context_v2_rsp { - __le32 Timeout; - __le32 Flags; /* see above DHANDLE_FLAG_PERSISTENT */ -} __packed; - -struct create_durable_handle_reconnect_v2 { - struct create_context ccontext; - __u8 Name[8]; - struct durable_reconnect_context_v2 dcontext; - __u8 Pad[4]; -} __packed; - -/* See MS-SMB2 2.2.13.2.5 */ -struct crt_twarp_ctxt { - struct create_context ccontext; - __u8 Name[8]; - __le64 Timestamp; - -} __packed; - -/* See MS-SMB2 2.2.13.2.9 */ -struct crt_query_id_ctxt { - struct create_context ccontext; - __u8 Name[8]; -} __packed; - -struct crt_sd_ctxt { - struct create_context ccontext; - __u8 Name[8]; - struct smb3_sd sd; -} __packed; - - -#define COPY_CHUNK_RES_KEY_SIZE 24 -struct resume_key_req { - char ResumeKey[COPY_CHUNK_RES_KEY_SIZE]; - __le32 ContextLength; /* MBZ */ - char Context[]; /* ignored, Windows sets to 4 bytes of zero */ -} __packed; - -/* this goes in the ioctl buffer when doing a copychunk request */ -struct copychunk_ioctl { - char SourceKey[COPY_CHUNK_RES_KEY_SIZE]; - __le32 ChunkCount; /* we are only sending 1 */ - __le32 Reserved; - /* array will only be one chunk long for us */ - __le64 SourceOffset; - __le64 TargetOffset; - __le32 Length; /* how many bytes to copy */ - __u32 Reserved2; -} __packed; - -struct copychunk_ioctl_rsp { - __le32 ChunksWritten; - __le32 ChunkBytesWritten; - __le32 TotalBytesWritten; -} __packed; - -/* See MS-FSCC 2.3.29 and 2.3.30 */ -struct get_retrieval_pointer_count_req { - __le64 StartingVcn; /* virtual cluster number (signed) */ -} __packed; - -struct get_retrieval_pointer_count_rsp { - __le32 ExtentCount; -} __packed; - -/* - * See MS-FSCC 2.3.33 and 2.3.34 - * request is the same as get_retrieval_point_count_req struct above - */ -struct smb3_extents { - __le64 NextVcn; - __le64 Lcn; /* logical cluster number */ -} __packed; - -struct get_retrieval_pointers_refcount_rsp { - __le32 ExtentCount; - __u32 Reserved; - __le64 StartingVcn; - struct smb3_extents extents[]; -} __packed; - -/* See MS-DFSC 2.2.2 */ -struct fsctl_get_dfs_referral_req { - __le16 MaxReferralLevel; - __u8 RequestFileName[]; -} __packed; - -/* DFS response is struct get_dfs_refer_rsp */ - -/* See MS-SMB2 2.2.31.3 */ -struct network_resiliency_req { - __le32 Timeout; - __le32 Reserved; -} __packed; -/* There is no buffer for the response ie no struct network_resiliency_rsp */ - -#define RSS_CAPABLE cpu_to_le32(0x00000001) -#define RDMA_CAPABLE cpu_to_le32(0x00000002) - -#define INTERNETWORK cpu_to_le16(0x0002) -#define INTERNETWORKV6 cpu_to_le16(0x0017) - -struct network_interface_info_ioctl_rsp { - __le32 Next; /* next interface. zero if this is last one */ - __le32 IfIndex; - __le32 Capability; /* RSS or RDMA Capable */ - __le32 Reserved; - __le64 LinkSpeed; - __le16 Family; - __u8 Buffer[126]; -} __packed; - -struct iface_info_ipv4 { - __be16 Port; - __be32 IPv4Address; - __be64 Reserved; -} __packed; - -struct iface_info_ipv6 { - __be16 Port; - __be32 FlowInfo; - __u8 IPv6Address[16]; - __be32 ScopeId; -} __packed; - -#define NO_FILE_ID 0xFFFFFFFFFFFFFFFFULL /* general ioctls to srv not to file */ - -struct compress_ioctl { - __le16 CompressionState; /* See cifspdu.h for possible flag values */ -} __packed; - -/* - * Maximum number of iovs we need for an ioctl request. - * [0] : struct smb2_ioctl_req - * [1] : in_data - */ -#define SMB2_IOCTL_IOV_SIZE 2 - -/* - * PDU query infolevel structure definitions - * BB consider moving to a different header - */ - -struct smb2_file_full_ea_info { /* encoding of response for level 15 */ - __le32 next_entry_offset; - __u8 flags; - __u8 ea_name_length; - __le16 ea_value_length; - char ea_data[]; /* \0 terminated name plus value */ -} __packed; /* level 15 Set */ - -struct smb2_file_reparse_point_info { - __le64 IndexNumber; - __le32 Tag; -} __packed; - -struct smb2_file_network_open_info { - __le64 CreationTime; - __le64 LastAccessTime; - __le64 LastWriteTime; - __le64 ChangeTime; - __le64 AllocationSize; - __le64 EndOfFile; - __le32 Attributes; - __le32 Reserved; -} __packed; /* level 34 Query also similar returned in close rsp and open rsp */ - -/* See MS-FSCC 2.4.21 */ -struct smb2_file_id_information { - __le64 VolumeSerialNumber; - __u64 PersistentFileId; /* opaque endianness */ - __u64 VolatileFileId; /* opaque endianness */ -} __packed; /* level 59 */ - -/* See MS-FSCC 2.4.18 */ -struct smb2_file_id_extd_directory_info { - __le32 NextEntryOffset; - __u32 FileIndex; - __le64 CreationTime; - __le64 LastAccessTime; - __le64 LastWriteTime; - __le64 ChangeTime; - __le64 EndOfFile; - __le64 AllocationSize; - __le32 FileAttributes; - __le32 FileNameLength; - __le32 EaSize; /* EA size */ - __le32 ReparsePointTag; /* valid if FILE_ATTR_REPARSE_POINT set in FileAttributes */ - __le64 UniqueId; /* inode num - le since Samba puts ino in low 32 bit */ - char FileName[1]; -} __packed; /* level 60 */ - -extern char smb2_padding[7]; - -/* equivalent of the contents of SMB3.1.1 POSIX open context response */ -struct create_posix_rsp { - u32 nlink; - u32 reparse_tag; - u32 mode; - struct cifs_sid owner; /* var-sized on the wire */ - struct cifs_sid group; /* var-sized on the wire */ -} __packed; - -#define SMB2_QUERY_DIRECTORY_IOV_SIZE 2 - -/* - * SMB2-only POSIX info level for query dir - * - * See posix_info_sid_size(), posix_info_extra_size() and - * posix_info_parse() to help with the handling of this struct. - */ -struct smb2_posix_info { - __le32 NextEntryOffset; - __u32 Ignored; - __le64 CreationTime; - __le64 LastAccessTime; - __le64 LastWriteTime; - __le64 ChangeTime; - __le64 EndOfFile; - __le64 AllocationSize; - __le32 DosAttributes; - __le64 Inode; - __le32 DeviceId; - __le32 Zero; - /* beginning of POSIX Create Context Response */ - __le32 HardLinks; - __le32 ReparseTag; - __le32 Mode; - /* - * var sized owner SID - * var sized group SID - * le32 filenamelength - * u8 filename[] - */ -} __packed; - -/* - * Parsed version of the above struct. Allows direct access to the - * variable length fields - */ -struct smb2_posix_info_parsed { - const struct smb2_posix_info *base; - size_t size; - struct cifs_sid owner; - struct cifs_sid group; - int name_len; - const u8 *name; -}; - -#endif /* _SMB2PDU_H */ diff --git a/fs/cifs/smb2proto.h b/fs/cifs/smb2proto.h deleted file mode 100644 index be21b5d26f67e..0000000000000 --- a/fs/cifs/smb2proto.h +++ /dev/null @@ -1,284 +0,0 @@ -/* SPDX-License-Identifier: LGPL-2.1 */ -/* - * - * Copyright (c) International Business Machines Corp., 2002, 2011 - * Etersoft, 2012 - * Author(s): Steve French (sfrench@us.ibm.com) - * Pavel Shilovsky (pshilovsky@samba.org) 2012 - * - */ -#ifndef _SMB2PROTO_H -#define _SMB2PROTO_H -#include -#include - -struct statfs; -struct smb_rqst; - -/* - ***************************************************************** - * All Prototypes - ***************************************************************** - */ -extern int map_smb2_to_linux_error(char *buf, bool log_err); -extern int smb2_check_message(char *buf, unsigned int length, - struct TCP_Server_Info *server); -extern unsigned int smb2_calc_size(void *buf); -extern char *smb2_get_data_area_len(int *off, int *len, - struct smb2_hdr *shdr); -extern __le16 *cifs_convert_path_to_utf16(const char *from, - struct cifs_sb_info *cifs_sb); - -extern int smb2_verify_signature(struct smb_rqst *, struct TCP_Server_Info *); -extern int smb2_check_receive(struct mid_q_entry *mid, - struct TCP_Server_Info *server, bool log_error); -extern struct mid_q_entry *smb2_setup_request(struct cifs_ses *ses, - struct TCP_Server_Info *, - struct smb_rqst *rqst); -extern struct mid_q_entry *smb2_setup_async_request( - struct TCP_Server_Info *server, struct smb_rqst *rqst); -extern struct cifs_ses *smb2_find_smb_ses(struct TCP_Server_Info *server, - __u64 ses_id); -extern struct cifs_tcon *smb2_find_smb_tcon(struct TCP_Server_Info *server, - __u64 ses_id, __u32 tid); -extern int smb2_calc_signature(struct smb_rqst *rqst, - struct TCP_Server_Info *server, - bool allocate_crypto); -extern int smb3_calc_signature(struct smb_rqst *rqst, - struct TCP_Server_Info *server, - bool allocate_crypto); -extern void smb2_echo_request(struct work_struct *work); -extern __le32 smb2_get_lease_state(struct cifsInodeInfo *cinode); -extern bool smb2_is_valid_oplock_break(char *buffer, - struct TCP_Server_Info *srv); -extern int smb3_handle_read_data(struct TCP_Server_Info *server, - struct mid_q_entry *mid); -extern int smb2_query_reparse_tag(const unsigned int xid, struct cifs_tcon *tcon, - struct cifs_sb_info *cifs_sb, const char *path, - __u32 *reparse_tag); -int smb2_query_path_info(const unsigned int xid, struct cifs_tcon *tcon, - struct cifs_sb_info *cifs_sb, const char *full_path, - struct cifs_open_info_data *data, bool *adjust_tz, bool *reparse); -extern int smb2_set_path_size(const unsigned int xid, struct cifs_tcon *tcon, - const char *full_path, __u64 size, - struct cifs_sb_info *cifs_sb, bool set_alloc); -extern int smb2_set_file_info(struct inode *inode, const char *full_path, - FILE_BASIC_INFO *buf, const unsigned int xid); -extern int smb311_posix_mkdir(const unsigned int xid, struct inode *inode, - umode_t mode, struct cifs_tcon *tcon, - const char *full_path, - struct cifs_sb_info *cifs_sb); -extern int smb2_mkdir(const unsigned int xid, struct inode *inode, - umode_t mode, struct cifs_tcon *tcon, - const char *name, struct cifs_sb_info *cifs_sb); -extern void smb2_mkdir_setinfo(struct inode *inode, const char *full_path, - struct cifs_sb_info *cifs_sb, - struct cifs_tcon *tcon, const unsigned int xid); -extern int smb2_rmdir(const unsigned int xid, struct cifs_tcon *tcon, - const char *name, struct cifs_sb_info *cifs_sb); -extern int smb2_unlink(const unsigned int xid, struct cifs_tcon *tcon, - const char *name, struct cifs_sb_info *cifs_sb); -extern int smb2_rename_path(const unsigned int xid, struct cifs_tcon *tcon, - const char *from_name, const char *to_name, - struct cifs_sb_info *cifs_sb); -extern int smb2_create_hardlink(const unsigned int xid, struct cifs_tcon *tcon, - const char *from_name, const char *to_name, - struct cifs_sb_info *cifs_sb); -extern int smb3_create_mf_symlink(unsigned int xid, struct cifs_tcon *tcon, - struct cifs_sb_info *cifs_sb, const unsigned char *path, - char *pbuf, unsigned int *pbytes_written); -extern int smb3_query_mf_symlink(unsigned int xid, struct cifs_tcon *tcon, - struct cifs_sb_info *cifs_sb, - const unsigned char *path, char *pbuf, - unsigned int *pbytes_read); -int smb2_parse_symlink_response(struct cifs_sb_info *cifs_sb, const struct kvec *iov, char **path); -int smb2_open_file(const unsigned int xid, struct cifs_open_parms *oparms, __u32 *oplock, - void *buf); -extern int smb2_unlock_range(struct cifsFileInfo *cfile, - struct file_lock *flock, const unsigned int xid); -extern int smb2_push_mandatory_locks(struct cifsFileInfo *cfile); -extern void smb2_reconnect_server(struct work_struct *work); -extern int smb3_crypto_aead_allocate(struct TCP_Server_Info *server); -extern unsigned long smb_rqst_len(struct TCP_Server_Info *server, - struct smb_rqst *rqst); -extern void smb2_set_next_command(struct cifs_tcon *tcon, - struct smb_rqst *rqst); -extern void smb2_set_related(struct smb_rqst *rqst); - -/* - * SMB2 Worker functions - most of protocol specific implementation details - * are contained within these calls. - */ -extern int SMB2_negotiate(const unsigned int xid, - struct cifs_ses *ses, - struct TCP_Server_Info *server); -extern int SMB2_sess_setup(const unsigned int xid, struct cifs_ses *ses, - struct TCP_Server_Info *server, - const struct nls_table *nls_cp); -extern int SMB2_logoff(const unsigned int xid, struct cifs_ses *ses); -extern int SMB2_tcon(const unsigned int xid, struct cifs_ses *ses, - const char *tree, struct cifs_tcon *tcon, - const struct nls_table *); -extern int SMB2_tdis(const unsigned int xid, struct cifs_tcon *tcon); -extern int SMB2_open(const unsigned int xid, struct cifs_open_parms *oparms, - __le16 *path, __u8 *oplock, - struct smb2_file_all_info *buf, - struct create_posix_rsp *posix, - struct kvec *err_iov, int *resp_buftype); -extern int SMB2_open_init(struct cifs_tcon *tcon, - struct TCP_Server_Info *server, - struct smb_rqst *rqst, - __u8 *oplock, struct cifs_open_parms *oparms, - __le16 *path); -extern void SMB2_open_free(struct smb_rqst *rqst); -extern int SMB2_ioctl(const unsigned int xid, struct cifs_tcon *tcon, - u64 persistent_fid, u64 volatile_fid, u32 opcode, - char *in_data, u32 indatalen, u32 maxoutlen, - char **out_data, u32 *plen /* returned data len */); -extern int SMB2_ioctl_init(struct cifs_tcon *tcon, - struct TCP_Server_Info *server, - struct smb_rqst *rqst, - u64 persistent_fid, u64 volatile_fid, u32 opcode, - char *in_data, u32 indatalen, - __u32 max_response_size); -extern void SMB2_ioctl_free(struct smb_rqst *rqst); -extern int SMB2_change_notify(const unsigned int xid, struct cifs_tcon *tcon, - u64 persistent_fid, u64 volatile_fid, bool watch_tree, - u32 completion_filter, u32 max_out_data_len, - char **out_data, u32 *plen /* returned data len */); - -extern int __SMB2_close(const unsigned int xid, struct cifs_tcon *tcon, - u64 persistent_fid, u64 volatile_fid, - struct smb2_file_network_open_info *pbuf); -extern int SMB2_close(const unsigned int xid, struct cifs_tcon *tcon, - u64 persistent_file_id, u64 volatile_file_id); -extern int SMB2_close_init(struct cifs_tcon *tcon, - struct TCP_Server_Info *server, - struct smb_rqst *rqst, - u64 persistent_fid, u64 volatile_fid, - bool query_attrs); -extern void SMB2_close_free(struct smb_rqst *rqst); -extern int SMB2_flush(const unsigned int xid, struct cifs_tcon *tcon, - u64 persistent_file_id, u64 volatile_file_id); -extern int SMB2_flush_init(const unsigned int xid, struct smb_rqst *rqst, - struct cifs_tcon *tcon, - struct TCP_Server_Info *server, - u64 persistent_file_id, u64 volatile_file_id); -extern void SMB2_flush_free(struct smb_rqst *rqst); -extern int SMB311_posix_query_info(const unsigned int xid, struct cifs_tcon *tcon, - u64 persistent_fid, u64 volatile_fid, struct smb311_posix_qinfo *data, u32 *plen); -extern int SMB2_query_info(const unsigned int xid, struct cifs_tcon *tcon, - u64 persistent_file_id, u64 volatile_file_id, - struct smb2_file_all_info *data); -extern int SMB2_query_info_init(struct cifs_tcon *tcon, - struct TCP_Server_Info *server, - struct smb_rqst *rqst, - u64 persistent_fid, u64 volatile_fid, - u8 info_class, u8 info_type, - u32 additional_info, size_t output_len, - size_t input_len, void *input); -extern void SMB2_query_info_free(struct smb_rqst *rqst); -extern int SMB2_query_acl(const unsigned int xid, struct cifs_tcon *tcon, - u64 persistent_file_id, u64 volatile_file_id, - void **data, unsigned int *plen, u32 info); -extern int SMB2_get_srv_num(const unsigned int xid, struct cifs_tcon *tcon, - u64 persistent_fid, u64 volatile_fid, - __le64 *uniqueid); -extern int smb2_async_readv(struct cifs_readdata *rdata); -extern int SMB2_read(const unsigned int xid, struct cifs_io_parms *io_parms, - unsigned int *nbytes, char **buf, int *buf_type); -extern int smb2_async_writev(struct cifs_writedata *wdata, - void (*release)(struct kref *kref)); -extern int SMB2_write(const unsigned int xid, struct cifs_io_parms *io_parms, - unsigned int *nbytes, struct kvec *iov, int n_vec); -extern int SMB2_echo(struct TCP_Server_Info *server); -extern int SMB2_query_directory(const unsigned int xid, struct cifs_tcon *tcon, - u64 persistent_fid, u64 volatile_fid, int index, - struct cifs_search_info *srch_inf); -extern int SMB2_query_directory_init(unsigned int xid, struct cifs_tcon *tcon, - struct TCP_Server_Info *server, - struct smb_rqst *rqst, - u64 persistent_fid, u64 volatile_fid, - int index, int info_level); -extern void SMB2_query_directory_free(struct smb_rqst *rqst); -extern int SMB2_set_eof(const unsigned int xid, struct cifs_tcon *tcon, - u64 persistent_fid, u64 volatile_fid, u32 pid, - __le64 *eof); -extern int SMB2_set_info_init(struct cifs_tcon *tcon, - struct TCP_Server_Info *server, - struct smb_rqst *rqst, - u64 persistent_fid, u64 volatile_fid, u32 pid, - u8 info_class, u8 info_type, u32 additional_info, - void **data, unsigned int *size); -extern void SMB2_set_info_free(struct smb_rqst *rqst); -extern int SMB2_set_acl(const unsigned int xid, struct cifs_tcon *tcon, - u64 persistent_fid, u64 volatile_fid, - struct cifs_ntsd *pnntsd, int pacllen, int aclflag); -extern int SMB2_set_ea(const unsigned int xid, struct cifs_tcon *tcon, - u64 persistent_fid, u64 volatile_fid, - struct smb2_file_full_ea_info *buf, int len); -extern int SMB2_set_compression(const unsigned int xid, struct cifs_tcon *tcon, - u64 persistent_fid, u64 volatile_fid); -extern int SMB2_oplock_break(const unsigned int xid, struct cifs_tcon *tcon, - const u64 persistent_fid, const u64 volatile_fid, - const __u8 oplock_level); -extern int smb2_handle_cancelled_close(struct cifs_tcon *tcon, - __u64 persistent_fid, - __u64 volatile_fid); -extern int smb2_handle_cancelled_mid(struct mid_q_entry *mid, struct TCP_Server_Info *server); -void smb2_cancelled_close_fid(struct work_struct *work); -extern int SMB2_QFS_info(const unsigned int xid, struct cifs_tcon *tcon, - u64 persistent_file_id, u64 volatile_file_id, - struct kstatfs *FSData); -extern int SMB311_posix_qfs_info(const unsigned int xid, struct cifs_tcon *tcon, - u64 persistent_file_id, u64 volatile_file_id, - struct kstatfs *FSData); -extern int SMB2_QFS_attr(const unsigned int xid, struct cifs_tcon *tcon, - u64 persistent_file_id, u64 volatile_file_id, int lvl); -extern int SMB2_lock(const unsigned int xid, struct cifs_tcon *tcon, - const __u64 persist_fid, const __u64 volatile_fid, - const __u32 pid, const __u64 length, const __u64 offset, - const __u32 lockFlags, const bool wait); -extern int smb2_lockv(const unsigned int xid, struct cifs_tcon *tcon, - const __u64 persist_fid, const __u64 volatile_fid, - const __u32 pid, const __u32 num_lock, - struct smb2_lock_element *buf); -extern int SMB2_lease_break(const unsigned int xid, struct cifs_tcon *tcon, - __u8 *lease_key, const __le32 lease_state); -extern int smb3_validate_negotiate(const unsigned int, struct cifs_tcon *); - -extern enum securityEnum smb2_select_sectype(struct TCP_Server_Info *, - enum securityEnum); -extern void smb2_parse_contexts(struct TCP_Server_Info *server, - struct smb2_create_rsp *rsp, - unsigned int *epoch, char *lease_key, - __u8 *oplock, struct smb2_file_all_info *buf, - struct create_posix_rsp *posix); -extern int smb3_encryption_required(const struct cifs_tcon *tcon); -extern int smb2_validate_iov(unsigned int offset, unsigned int buffer_length, - struct kvec *iov, unsigned int min_buf_size); -extern int smb2_validate_and_copy_iov(unsigned int offset, - unsigned int buffer_length, - struct kvec *iov, - unsigned int minbufsize, char *data); -extern void smb2_copy_fs_info_to_kstatfs( - struct smb2_fs_full_size_info *pfs_inf, - struct kstatfs *kst); -extern int smb311_crypto_shash_allocate(struct TCP_Server_Info *server); -extern int smb311_update_preauth_hash(struct cifs_ses *ses, - struct TCP_Server_Info *server, - struct kvec *iov, int nvec); -extern int smb2_query_info_compound(const unsigned int xid, - struct cifs_tcon *tcon, - const char *path, u32 desired_access, - u32 class, u32 type, u32 output_len, - struct kvec *rsp, int *buftype, - struct cifs_sb_info *cifs_sb); -/* query path info from the server using SMB311 POSIX extensions*/ -int smb311_posix_query_path_info(const unsigned int xid, struct cifs_tcon *tcon, - struct cifs_sb_info *cifs_sb, const char *full_path, - struct cifs_open_info_data *data, bool *adjust_tz, bool *reparse); -int posix_info_parse(const void *beg, const void *end, - struct smb2_posix_info_parsed *out); -int posix_info_sid_size(const void *beg, const void *end); -#endif /* _SMB2PROTO_H */ diff --git a/fs/cifs/smb2status.h b/fs/cifs/smb2status.h deleted file mode 100644 index a9e958166fc53..0000000000000 --- a/fs/cifs/smb2status.h +++ /dev/null @@ -1,1769 +0,0 @@ -/* SPDX-License-Identifier: LGPL-2.1 */ -/* - * - * SMB2 Status code (network error) definitions - * Definitions are from MS-ERREF - * - * Copyright (c) International Business Machines Corp., 2009,2011 - * Author(s): Steve French (sfrench@us.ibm.com) - * - */ - -/* - * 0 1 2 3 4 5 6 7 8 9 0 A B C D E F 0 1 2 3 4 5 6 7 8 9 A B C D E F - * SEV C N <-------Facility--------> <------Error Status Code------> - * - * C is set if "customer defined" error, N bit is reserved and MBZ - */ - -#define STATUS_SEVERITY_SUCCESS __constant_cpu_to_le32(0x0000) -#define STATUS_SEVERITY_INFORMATIONAL cpu_to_le32(0x0001) -#define STATUS_SEVERITY_WARNING cpu_to_le32(0x0002) -#define STATUS_SEVERITY_ERROR cpu_to_le32(0x0003) - -struct ntstatus { - /* Facility is the high 12 bits of the following field */ - __le32 Facility; /* low 2 bits Severity, next is Customer, then rsrvd */ - __le32 Code; -}; - -#define STATUS_SUCCESS cpu_to_le32(0x00000000) -#define STATUS_WAIT_0 cpu_to_le32(0x00000000) -#define STATUS_WAIT_1 cpu_to_le32(0x00000001) -#define STATUS_WAIT_2 cpu_to_le32(0x00000002) -#define STATUS_WAIT_3 cpu_to_le32(0x00000003) -#define STATUS_WAIT_63 cpu_to_le32(0x0000003F) -#define STATUS_ABANDONED cpu_to_le32(0x00000080) -#define STATUS_ABANDONED_WAIT_0 cpu_to_le32(0x00000080) -#define STATUS_ABANDONED_WAIT_63 cpu_to_le32(0x000000BF) -#define STATUS_USER_APC cpu_to_le32(0x000000C0) -#define STATUS_KERNEL_APC cpu_to_le32(0x00000100) -#define STATUS_ALERTED cpu_to_le32(0x00000101) -#define STATUS_TIMEOUT cpu_to_le32(0x00000102) -#define STATUS_PENDING cpu_to_le32(0x00000103) -#define STATUS_REPARSE cpu_to_le32(0x00000104) -#define STATUS_MORE_ENTRIES cpu_to_le32(0x00000105) -#define STATUS_NOT_ALL_ASSIGNED cpu_to_le32(0x00000106) -#define STATUS_SOME_NOT_MAPPED cpu_to_le32(0x00000107) -#define STATUS_OPLOCK_BREAK_IN_PROGRESS cpu_to_le32(0x00000108) -#define STATUS_VOLUME_MOUNTED cpu_to_le32(0x00000109) -#define STATUS_RXACT_COMMITTED cpu_to_le32(0x0000010A) -#define STATUS_NOTIFY_CLEANUP cpu_to_le32(0x0000010B) -#define STATUS_NOTIFY_ENUM_DIR cpu_to_le32(0x0000010C) -#define STATUS_NO_QUOTAS_FOR_ACCOUNT cpu_to_le32(0x0000010D) -#define STATUS_PRIMARY_TRANSPORT_CONNECT_FAILED cpu_to_le32(0x0000010E) -#define STATUS_PAGE_FAULT_TRANSITION cpu_to_le32(0x00000110) -#define STATUS_PAGE_FAULT_DEMAND_ZERO cpu_to_le32(0x00000111) -#define STATUS_PAGE_FAULT_COPY_ON_WRITE cpu_to_le32(0x00000112) -#define STATUS_PAGE_FAULT_GUARD_PAGE cpu_to_le32(0x00000113) -#define STATUS_PAGE_FAULT_PAGING_FILE cpu_to_le32(0x00000114) -#define STATUS_CACHE_PAGE_LOCKED cpu_to_le32(0x00000115) -#define STATUS_CRASH_DUMP cpu_to_le32(0x00000116) -#define STATUS_BUFFER_ALL_ZEROS cpu_to_le32(0x00000117) -#define STATUS_REPARSE_OBJECT cpu_to_le32(0x00000118) -#define STATUS_RESOURCE_REQUIREMENTS_CHANGED cpu_to_le32(0x00000119) -#define STATUS_TRANSLATION_COMPLETE cpu_to_le32(0x00000120) -#define STATUS_DS_MEMBERSHIP_EVALUATED_LOCALLY cpu_to_le32(0x00000121) -#define STATUS_NOTHING_TO_TERMINATE cpu_to_le32(0x00000122) -#define STATUS_PROCESS_NOT_IN_JOB cpu_to_le32(0x00000123) -#define STATUS_PROCESS_IN_JOB cpu_to_le32(0x00000124) -#define STATUS_VOLSNAP_HIBERNATE_READY cpu_to_le32(0x00000125) -#define STATUS_FSFILTER_OP_COMPLETED_SUCCESSFULLY cpu_to_le32(0x00000126) -#define STATUS_INTERRUPT_VECTOR_ALREADY_CONNECTED cpu_to_le32(0x00000127) -#define STATUS_INTERRUPT_STILL_CONNECTED cpu_to_le32(0x00000128) -#define STATUS_PROCESS_CLONED cpu_to_le32(0x00000129) -#define STATUS_FILE_LOCKED_WITH_ONLY_READERS cpu_to_le32(0x0000012A) -#define STATUS_FILE_LOCKED_WITH_WRITERS cpu_to_le32(0x0000012B) -#define STATUS_RESOURCEMANAGER_READ_ONLY cpu_to_le32(0x00000202) -#define STATUS_WAIT_FOR_OPLOCK cpu_to_le32(0x00000367) -#define DBG_EXCEPTION_HANDLED cpu_to_le32(0x00010001) -#define DBG_CONTINUE cpu_to_le32(0x00010002) -#define STATUS_FLT_IO_COMPLETE cpu_to_le32(0x001C0001) -#define STATUS_OBJECT_NAME_EXISTS cpu_to_le32(0x40000000) -#define STATUS_THREAD_WAS_SUSPENDED cpu_to_le32(0x40000001) -#define STATUS_WORKING_SET_LIMIT_RANGE cpu_to_le32(0x40000002) -#define STATUS_IMAGE_NOT_AT_BASE cpu_to_le32(0x40000003) -#define STATUS_RXACT_STATE_CREATED cpu_to_le32(0x40000004) -#define STATUS_SEGMENT_NOTIFICATION cpu_to_le32(0x40000005) -#define STATUS_LOCAL_USER_SESSION_KEY cpu_to_le32(0x40000006) -#define STATUS_BAD_CURRENT_DIRECTORY cpu_to_le32(0x40000007) -#define STATUS_SERIAL_MORE_WRITES cpu_to_le32(0x40000008) -#define STATUS_REGISTRY_RECOVERED cpu_to_le32(0x40000009) -#define STATUS_FT_READ_RECOVERY_FROM_BACKUP cpu_to_le32(0x4000000A) -#define STATUS_FT_WRITE_RECOVERY cpu_to_le32(0x4000000B) -#define STATUS_SERIAL_COUNTER_TIMEOUT cpu_to_le32(0x4000000C) -#define STATUS_NULL_LM_PASSWORD cpu_to_le32(0x4000000D) -#define STATUS_IMAGE_MACHINE_TYPE_MISMATCH cpu_to_le32(0x4000000E) -#define STATUS_RECEIVE_PARTIAL cpu_to_le32(0x4000000F) -#define STATUS_RECEIVE_EXPEDITED cpu_to_le32(0x40000010) -#define STATUS_RECEIVE_PARTIAL_EXPEDITED cpu_to_le32(0x40000011) -#define STATUS_EVENT_DONE cpu_to_le32(0x40000012) -#define STATUS_EVENT_PENDING cpu_to_le32(0x40000013) -#define STATUS_CHECKING_FILE_SYSTEM cpu_to_le32(0x40000014) -#define STATUS_FATAL_APP_EXIT cpu_to_le32(0x40000015) -#define STATUS_PREDEFINED_HANDLE cpu_to_le32(0x40000016) -#define STATUS_WAS_UNLOCKED cpu_to_le32(0x40000017) -#define STATUS_SERVICE_NOTIFICATION cpu_to_le32(0x40000018) -#define STATUS_WAS_LOCKED cpu_to_le32(0x40000019) -#define STATUS_LOG_HARD_ERROR cpu_to_le32(0x4000001A) -#define STATUS_ALREADY_WIN32 cpu_to_le32(0x4000001B) -#define STATUS_WX86_UNSIMULATE cpu_to_le32(0x4000001C) -#define STATUS_WX86_CONTINUE cpu_to_le32(0x4000001D) -#define STATUS_WX86_SINGLE_STEP cpu_to_le32(0x4000001E) -#define STATUS_WX86_BREAKPOINT cpu_to_le32(0x4000001F) -#define STATUS_WX86_EXCEPTION_CONTINUE cpu_to_le32(0x40000020) -#define STATUS_WX86_EXCEPTION_LASTCHANCE cpu_to_le32(0x40000021) -#define STATUS_WX86_EXCEPTION_CHAIN cpu_to_le32(0x40000022) -#define STATUS_IMAGE_MACHINE_TYPE_MISMATCH_EXE cpu_to_le32(0x40000023) -#define STATUS_NO_YIELD_PERFORMED cpu_to_le32(0x40000024) -#define STATUS_TIMER_RESUME_IGNORED cpu_to_le32(0x40000025) -#define STATUS_ARBITRATION_UNHANDLED cpu_to_le32(0x40000026) -#define STATUS_CARDBUS_NOT_SUPPORTED cpu_to_le32(0x40000027) -#define STATUS_WX86_CREATEWX86TIB cpu_to_le32(0x40000028) -#define STATUS_MP_PROCESSOR_MISMATCH cpu_to_le32(0x40000029) -#define STATUS_HIBERNATED cpu_to_le32(0x4000002A) -#define STATUS_RESUME_HIBERNATION cpu_to_le32(0x4000002B) -#define STATUS_FIRMWARE_UPDATED cpu_to_le32(0x4000002C) -#define STATUS_DRIVERS_LEAKING_LOCKED_PAGES cpu_to_le32(0x4000002D) -#define STATUS_MESSAGE_RETRIEVED cpu_to_le32(0x4000002E) -#define STATUS_SYSTEM_POWERSTATE_TRANSITION cpu_to_le32(0x4000002F) -#define STATUS_ALPC_CHECK_COMPLETION_LIST cpu_to_le32(0x40000030) -#define STATUS_SYSTEM_POWERSTATE_COMPLEX_TRANSITION cpu_to_le32(0x40000031) -#define STATUS_ACCESS_AUDIT_BY_POLICY cpu_to_le32(0x40000032) -#define STATUS_ABANDON_HIBERFILE cpu_to_le32(0x40000033) -#define STATUS_BIZRULES_NOT_ENABLED cpu_to_le32(0x40000034) -#define STATUS_WAKE_SYSTEM cpu_to_le32(0x40000294) -#define STATUS_DS_SHUTTING_DOWN cpu_to_le32(0x40000370) -#define DBG_REPLY_LATER cpu_to_le32(0x40010001) -#define DBG_UNABLE_TO_PROVIDE_HANDLE cpu_to_le32(0x40010002) -#define DBG_TERMINATE_THREAD cpu_to_le32(0x40010003) -#define DBG_TERMINATE_PROCESS cpu_to_le32(0x40010004) -#define DBG_CONTROL_C cpu_to_le32(0x40010005) -#define DBG_PRINTEXCEPTION_C cpu_to_le32(0x40010006) -#define DBG_RIPEXCEPTION cpu_to_le32(0x40010007) -#define DBG_CONTROL_BREAK cpu_to_le32(0x40010008) -#define DBG_COMMAND_EXCEPTION cpu_to_le32(0x40010009) -#define RPC_NT_UUID_LOCAL_ONLY cpu_to_le32(0x40020056) -#define RPC_NT_SEND_INCOMPLETE cpu_to_le32(0x400200AF) -#define STATUS_CTX_CDM_CONNECT cpu_to_le32(0x400A0004) -#define STATUS_CTX_CDM_DISCONNECT cpu_to_le32(0x400A0005) -#define STATUS_SXS_RELEASE_ACTIVATION_CONTEXT cpu_to_le32(0x4015000D) -#define STATUS_RECOVERY_NOT_NEEDED cpu_to_le32(0x40190034) -#define STATUS_RM_ALREADY_STARTED cpu_to_le32(0x40190035) -#define STATUS_LOG_NO_RESTART cpu_to_le32(0x401A000C) -#define STATUS_VIDEO_DRIVER_DEBUG_REPORT_REQUEST cpu_to_le32(0x401B00EC) -#define STATUS_GRAPHICS_PARTIAL_DATA_POPULATED cpu_to_le32(0x401E000A) -#define STATUS_GRAPHICS_DRIVER_MISMATCH cpu_to_le32(0x401E0117) -#define STATUS_GRAPHICS_MODE_NOT_PINNED cpu_to_le32(0x401E0307) -#define STATUS_GRAPHICS_NO_PREFERRED_MODE cpu_to_le32(0x401E031E) -#define STATUS_GRAPHICS_DATASET_IS_EMPTY cpu_to_le32(0x401E034B) -#define STATUS_GRAPHICS_NO_MORE_ELEMENTS_IN_DATASET cpu_to_le32(0x401E034C) -#define STATUS_GRAPHICS_PATH_CONTENT_GEOMETRY_TRANSFORMATION_NOT_PINNED cpu_to_le32(0x401E0351) -#define STATUS_GRAPHICS_UNKNOWN_CHILD_STATUS cpu_to_le32(0x401E042F) -#define STATUS_GRAPHICS_LEADLINK_START_DEFERRED cpu_to_le32(0x401E0437) -#define STATUS_GRAPHICS_POLLING_TOO_FREQUENTLY cpu_to_le32(0x401E0439) -#define STATUS_GRAPHICS_START_DEFERRED cpu_to_le32(0x401E043A) -#define STATUS_NDIS_INDICATION_REQUIRED cpu_to_le32(0x40230001) -#define STATUS_GUARD_PAGE_VIOLATION cpu_to_le32(0x80000001) -#define STATUS_DATATYPE_MISALIGNMENT cpu_to_le32(0x80000002) -#define STATUS_BREAKPOINT cpu_to_le32(0x80000003) -#define STATUS_SINGLE_STEP cpu_to_le32(0x80000004) -#define STATUS_BUFFER_OVERFLOW cpu_to_le32(0x80000005) -#define STATUS_NO_MORE_FILES cpu_to_le32(0x80000006) -#define STATUS_WAKE_SYSTEM_DEBUGGER cpu_to_le32(0x80000007) -#define STATUS_HANDLES_CLOSED cpu_to_le32(0x8000000A) -#define STATUS_NO_INHERITANCE cpu_to_le32(0x8000000B) -#define STATUS_GUID_SUBSTITUTION_MADE cpu_to_le32(0x8000000C) -#define STATUS_PARTIAL_COPY cpu_to_le32(0x8000000D) -#define STATUS_DEVICE_PAPER_EMPTY cpu_to_le32(0x8000000E) -#define STATUS_DEVICE_POWERED_OFF cpu_to_le32(0x8000000F) -#define STATUS_DEVICE_OFF_LINE cpu_to_le32(0x80000010) -#define STATUS_DEVICE_BUSY cpu_to_le32(0x80000011) -#define STATUS_NO_MORE_EAS cpu_to_le32(0x80000012) -#define STATUS_INVALID_EA_NAME cpu_to_le32(0x80000013) -#define STATUS_EA_LIST_INCONSISTENT cpu_to_le32(0x80000014) -#define STATUS_INVALID_EA_FLAG cpu_to_le32(0x80000015) -#define STATUS_VERIFY_REQUIRED cpu_to_le32(0x80000016) -#define STATUS_EXTRANEOUS_INFORMATION cpu_to_le32(0x80000017) -#define STATUS_RXACT_COMMIT_NECESSARY cpu_to_le32(0x80000018) -#define STATUS_NO_MORE_ENTRIES cpu_to_le32(0x8000001A) -#define STATUS_FILEMARK_DETECTED cpu_to_le32(0x8000001B) -#define STATUS_MEDIA_CHANGED cpu_to_le32(0x8000001C) -#define STATUS_BUS_RESET cpu_to_le32(0x8000001D) -#define STATUS_END_OF_MEDIA cpu_to_le32(0x8000001E) -#define STATUS_BEGINNING_OF_MEDIA cpu_to_le32(0x8000001F) -#define STATUS_MEDIA_CHECK cpu_to_le32(0x80000020) -#define STATUS_SETMARK_DETECTED cpu_to_le32(0x80000021) -#define STATUS_NO_DATA_DETECTED cpu_to_le32(0x80000022) -#define STATUS_REDIRECTOR_HAS_OPEN_HANDLES cpu_to_le32(0x80000023) -#define STATUS_SERVER_HAS_OPEN_HANDLES cpu_to_le32(0x80000024) -#define STATUS_ALREADY_DISCONNECTED cpu_to_le32(0x80000025) -#define STATUS_LONGJUMP cpu_to_le32(0x80000026) -#define STATUS_CLEANER_CARTRIDGE_INSTALLED cpu_to_le32(0x80000027) -#define STATUS_PLUGPLAY_QUERY_VETOED cpu_to_le32(0x80000028) -#define STATUS_UNWIND_CONSOLIDATE cpu_to_le32(0x80000029) -#define STATUS_REGISTRY_HIVE_RECOVERED cpu_to_le32(0x8000002A) -#define STATUS_DLL_MIGHT_BE_INSECURE cpu_to_le32(0x8000002B) -#define STATUS_DLL_MIGHT_BE_INCOMPATIBLE cpu_to_le32(0x8000002C) -#define STATUS_STOPPED_ON_SYMLINK cpu_to_le32(0x8000002D) -#define STATUS_DEVICE_REQUIRES_CLEANING cpu_to_le32(0x80000288) -#define STATUS_DEVICE_DOOR_OPEN cpu_to_le32(0x80000289) -#define STATUS_DATA_LOST_REPAIR cpu_to_le32(0x80000803) -#define DBG_EXCEPTION_NOT_HANDLED cpu_to_le32(0x80010001) -#define STATUS_CLUSTER_NODE_ALREADY_UP cpu_to_le32(0x80130001) -#define STATUS_CLUSTER_NODE_ALREADY_DOWN cpu_to_le32(0x80130002) -#define STATUS_CLUSTER_NETWORK_ALREADY_ONLINE cpu_to_le32(0x80130003) -#define STATUS_CLUSTER_NETWORK_ALREADY_OFFLINE cpu_to_le32(0x80130004) -#define STATUS_CLUSTER_NODE_ALREADY_MEMBER cpu_to_le32(0x80130005) -#define STATUS_COULD_NOT_RESIZE_LOG cpu_to_le32(0x80190009) -#define STATUS_NO_TXF_METADATA cpu_to_le32(0x80190029) -#define STATUS_CANT_RECOVER_WITH_HANDLE_OPEN cpu_to_le32(0x80190031) -#define STATUS_TXF_METADATA_ALREADY_PRESENT cpu_to_le32(0x80190041) -#define STATUS_TRANSACTION_SCOPE_CALLBACKS_NOT_SET cpu_to_le32(0x80190042) -#define STATUS_VIDEO_HUNG_DISPLAY_DRIVER_THREAD_RECOVERED cpu_to_le32(0x801B00EB) -#define STATUS_FLT_BUFFER_TOO_SMALL cpu_to_le32(0x801C0001) -#define STATUS_FVE_PARTIAL_METADATA cpu_to_le32(0x80210001) -#define STATUS_UNSUCCESSFUL cpu_to_le32(0xC0000001) -#define STATUS_NOT_IMPLEMENTED cpu_to_le32(0xC0000002) -#define STATUS_INVALID_INFO_CLASS cpu_to_le32(0xC0000003) -#define STATUS_INFO_LENGTH_MISMATCH cpu_to_le32(0xC0000004) -#define STATUS_ACCESS_VIOLATION cpu_to_le32(0xC0000005) -#define STATUS_IN_PAGE_ERROR cpu_to_le32(0xC0000006) -#define STATUS_PAGEFILE_QUOTA cpu_to_le32(0xC0000007) -#define STATUS_INVALID_HANDLE cpu_to_le32(0xC0000008) -#define STATUS_BAD_INITIAL_STACK cpu_to_le32(0xC0000009) -#define STATUS_BAD_INITIAL_PC cpu_to_le32(0xC000000A) -#define STATUS_INVALID_CID cpu_to_le32(0xC000000B) -#define STATUS_TIMER_NOT_CANCELED cpu_to_le32(0xC000000C) -#define STATUS_INVALID_PARAMETER cpu_to_le32(0xC000000D) -#define STATUS_NO_SUCH_DEVICE cpu_to_le32(0xC000000E) -#define STATUS_NO_SUCH_FILE cpu_to_le32(0xC000000F) -#define STATUS_INVALID_DEVICE_REQUEST cpu_to_le32(0xC0000010) -#define STATUS_END_OF_FILE cpu_to_le32(0xC0000011) -#define STATUS_WRONG_VOLUME cpu_to_le32(0xC0000012) -#define STATUS_NO_MEDIA_IN_DEVICE cpu_to_le32(0xC0000013) -#define STATUS_UNRECOGNIZED_MEDIA cpu_to_le32(0xC0000014) -#define STATUS_NONEXISTENT_SECTOR cpu_to_le32(0xC0000015) -#define STATUS_MORE_PROCESSING_REQUIRED cpu_to_le32(0xC0000016) -#define STATUS_NO_MEMORY cpu_to_le32(0xC0000017) -#define STATUS_CONFLICTING_ADDRESSES cpu_to_le32(0xC0000018) -#define STATUS_NOT_MAPPED_VIEW cpu_to_le32(0xC0000019) -#define STATUS_UNABLE_TO_FREE_VM cpu_to_le32(0xC000001A) -#define STATUS_UNABLE_TO_DELETE_SECTION cpu_to_le32(0xC000001B) -#define STATUS_INVALID_SYSTEM_SERVICE cpu_to_le32(0xC000001C) -#define STATUS_ILLEGAL_INSTRUCTION cpu_to_le32(0xC000001D) -#define STATUS_INVALID_LOCK_SEQUENCE cpu_to_le32(0xC000001E) -#define STATUS_INVALID_VIEW_SIZE cpu_to_le32(0xC000001F) -#define STATUS_INVALID_FILE_FOR_SECTION cpu_to_le32(0xC0000020) -#define STATUS_ALREADY_COMMITTED cpu_to_le32(0xC0000021) -#define STATUS_ACCESS_DENIED cpu_to_le32(0xC0000022) -#define STATUS_BUFFER_TOO_SMALL cpu_to_le32(0xC0000023) -#define STATUS_OBJECT_TYPE_MISMATCH cpu_to_le32(0xC0000024) -#define STATUS_NONCONTINUABLE_EXCEPTION cpu_to_le32(0xC0000025) -#define STATUS_INVALID_DISPOSITION cpu_to_le32(0xC0000026) -#define STATUS_UNWIND cpu_to_le32(0xC0000027) -#define STATUS_BAD_STACK cpu_to_le32(0xC0000028) -#define STATUS_INVALID_UNWIND_TARGET cpu_to_le32(0xC0000029) -#define STATUS_NOT_LOCKED cpu_to_le32(0xC000002A) -#define STATUS_PARITY_ERROR cpu_to_le32(0xC000002B) -#define STATUS_UNABLE_TO_DECOMMIT_VM cpu_to_le32(0xC000002C) -#define STATUS_NOT_COMMITTED cpu_to_le32(0xC000002D) -#define STATUS_INVALID_PORT_ATTRIBUTES cpu_to_le32(0xC000002E) -#define STATUS_PORT_MESSAGE_TOO_LONG cpu_to_le32(0xC000002F) -#define STATUS_INVALID_PARAMETER_MIX cpu_to_le32(0xC0000030) -#define STATUS_INVALID_QUOTA_LOWER cpu_to_le32(0xC0000031) -#define STATUS_DISK_CORRUPT_ERROR cpu_to_le32(0xC0000032) -#define STATUS_OBJECT_NAME_INVALID cpu_to_le32(0xC0000033) -#define STATUS_OBJECT_NAME_NOT_FOUND cpu_to_le32(0xC0000034) -#define STATUS_OBJECT_NAME_COLLISION cpu_to_le32(0xC0000035) -#define STATUS_PORT_DISCONNECTED cpu_to_le32(0xC0000037) -#define STATUS_DEVICE_ALREADY_ATTACHED cpu_to_le32(0xC0000038) -#define STATUS_OBJECT_PATH_INVALID cpu_to_le32(0xC0000039) -#define STATUS_OBJECT_PATH_NOT_FOUND cpu_to_le32(0xC000003A) -#define STATUS_OBJECT_PATH_SYNTAX_BAD cpu_to_le32(0xC000003B) -#define STATUS_DATA_OVERRUN cpu_to_le32(0xC000003C) -#define STATUS_DATA_LATE_ERROR cpu_to_le32(0xC000003D) -#define STATUS_DATA_ERROR cpu_to_le32(0xC000003E) -#define STATUS_CRC_ERROR cpu_to_le32(0xC000003F) -#define STATUS_SECTION_TOO_BIG cpu_to_le32(0xC0000040) -#define STATUS_PORT_CONNECTION_REFUSED cpu_to_le32(0xC0000041) -#define STATUS_INVALID_PORT_HANDLE cpu_to_le32(0xC0000042) -#define STATUS_SHARING_VIOLATION cpu_to_le32(0xC0000043) -#define STATUS_QUOTA_EXCEEDED cpu_to_le32(0xC0000044) -#define STATUS_INVALID_PAGE_PROTECTION cpu_to_le32(0xC0000045) -#define STATUS_MUTANT_NOT_OWNED cpu_to_le32(0xC0000046) -#define STATUS_SEMAPHORE_LIMIT_EXCEEDED cpu_to_le32(0xC0000047) -#define STATUS_PORT_ALREADY_SET cpu_to_le32(0xC0000048) -#define STATUS_SECTION_NOT_IMAGE cpu_to_le32(0xC0000049) -#define STATUS_SUSPEND_COUNT_EXCEEDED cpu_to_le32(0xC000004A) -#define STATUS_THREAD_IS_TERMINATING cpu_to_le32(0xC000004B) -#define STATUS_BAD_WORKING_SET_LIMIT cpu_to_le32(0xC000004C) -#define STATUS_INCOMPATIBLE_FILE_MAP cpu_to_le32(0xC000004D) -#define STATUS_SECTION_PROTECTION cpu_to_le32(0xC000004E) -#define STATUS_EAS_NOT_SUPPORTED cpu_to_le32(0xC000004F) -#define STATUS_EA_TOO_LARGE cpu_to_le32(0xC0000050) -#define STATUS_NONEXISTENT_EA_ENTRY cpu_to_le32(0xC0000051) -#define STATUS_NO_EAS_ON_FILE cpu_to_le32(0xC0000052) -#define STATUS_EA_CORRUPT_ERROR cpu_to_le32(0xC0000053) -#define STATUS_FILE_LOCK_CONFLICT cpu_to_le32(0xC0000054) -#define STATUS_LOCK_NOT_GRANTED cpu_to_le32(0xC0000055) -#define STATUS_DELETE_PENDING cpu_to_le32(0xC0000056) -#define STATUS_CTL_FILE_NOT_SUPPORTED cpu_to_le32(0xC0000057) -#define STATUS_UNKNOWN_REVISION cpu_to_le32(0xC0000058) -#define STATUS_REVISION_MISMATCH cpu_to_le32(0xC0000059) -#define STATUS_INVALID_OWNER cpu_to_le32(0xC000005A) -#define STATUS_INVALID_PRIMARY_GROUP cpu_to_le32(0xC000005B) -#define STATUS_NO_IMPERSONATION_TOKEN cpu_to_le32(0xC000005C) -#define STATUS_CANT_DISABLE_MANDATORY cpu_to_le32(0xC000005D) -#define STATUS_NO_LOGON_SERVERS cpu_to_le32(0xC000005E) -#define STATUS_NO_SUCH_LOGON_SESSION cpu_to_le32(0xC000005F) -#define STATUS_NO_SUCH_PRIVILEGE cpu_to_le32(0xC0000060) -#define STATUS_PRIVILEGE_NOT_HELD cpu_to_le32(0xC0000061) -#define STATUS_INVALID_ACCOUNT_NAME cpu_to_le32(0xC0000062) -#define STATUS_USER_EXISTS cpu_to_le32(0xC0000063) -#define STATUS_NO_SUCH_USER cpu_to_le32(0xC0000064) -#define STATUS_GROUP_EXISTS cpu_to_le32(0xC0000065) -#define STATUS_NO_SUCH_GROUP cpu_to_le32(0xC0000066) -#define STATUS_MEMBER_IN_GROUP cpu_to_le32(0xC0000067) -#define STATUS_MEMBER_NOT_IN_GROUP cpu_to_le32(0xC0000068) -#define STATUS_LAST_ADMIN cpu_to_le32(0xC0000069) -#define STATUS_WRONG_PASSWORD cpu_to_le32(0xC000006A) -#define STATUS_ILL_FORMED_PASSWORD cpu_to_le32(0xC000006B) -#define STATUS_PASSWORD_RESTRICTION cpu_to_le32(0xC000006C) -#define STATUS_LOGON_FAILURE cpu_to_le32(0xC000006D) -#define STATUS_ACCOUNT_RESTRICTION cpu_to_le32(0xC000006E) -#define STATUS_INVALID_LOGON_HOURS cpu_to_le32(0xC000006F) -#define STATUS_INVALID_WORKSTATION cpu_to_le32(0xC0000070) -#define STATUS_PASSWORD_EXPIRED cpu_to_le32(0xC0000071) -#define STATUS_ACCOUNT_DISABLED cpu_to_le32(0xC0000072) -#define STATUS_NONE_MAPPED cpu_to_le32(0xC0000073) -#define STATUS_TOO_MANY_LUIDS_REQUESTED cpu_to_le32(0xC0000074) -#define STATUS_LUIDS_EXHAUSTED cpu_to_le32(0xC0000075) -#define STATUS_INVALID_SUB_AUTHORITY cpu_to_le32(0xC0000076) -#define STATUS_INVALID_ACL cpu_to_le32(0xC0000077) -#define STATUS_INVALID_SID cpu_to_le32(0xC0000078) -#define STATUS_INVALID_SECURITY_DESCR cpu_to_le32(0xC0000079) -#define STATUS_PROCEDURE_NOT_FOUND cpu_to_le32(0xC000007A) -#define STATUS_INVALID_IMAGE_FORMAT cpu_to_le32(0xC000007B) -#define STATUS_NO_TOKEN cpu_to_le32(0xC000007C) -#define STATUS_BAD_INHERITANCE_ACL cpu_to_le32(0xC000007D) -#define STATUS_RANGE_NOT_LOCKED cpu_to_le32(0xC000007E) -#define STATUS_DISK_FULL cpu_to_le32(0xC000007F) -#define STATUS_SERVER_DISABLED cpu_to_le32(0xC0000080) -#define STATUS_SERVER_NOT_DISABLED cpu_to_le32(0xC0000081) -#define STATUS_TOO_MANY_GUIDS_REQUESTED cpu_to_le32(0xC0000082) -#define STATUS_GUIDS_EXHAUSTED cpu_to_le32(0xC0000083) -#define STATUS_INVALID_ID_AUTHORITY cpu_to_le32(0xC0000084) -#define STATUS_AGENTS_EXHAUSTED cpu_to_le32(0xC0000085) -#define STATUS_INVALID_VOLUME_LABEL cpu_to_le32(0xC0000086) -#define STATUS_SECTION_NOT_EXTENDED cpu_to_le32(0xC0000087) -#define STATUS_NOT_MAPPED_DATA cpu_to_le32(0xC0000088) -#define STATUS_RESOURCE_DATA_NOT_FOUND cpu_to_le32(0xC0000089) -#define STATUS_RESOURCE_TYPE_NOT_FOUND cpu_to_le32(0xC000008A) -#define STATUS_RESOURCE_NAME_NOT_FOUND cpu_to_le32(0xC000008B) -#define STATUS_ARRAY_BOUNDS_EXCEEDED cpu_to_le32(0xC000008C) -#define STATUS_FLOAT_DENORMAL_OPERAND cpu_to_le32(0xC000008D) -#define STATUS_FLOAT_DIVIDE_BY_ZERO cpu_to_le32(0xC000008E) -#define STATUS_FLOAT_INEXACT_RESULT cpu_to_le32(0xC000008F) -#define STATUS_FLOAT_INVALID_OPERATION cpu_to_le32(0xC0000090) -#define STATUS_FLOAT_OVERFLOW cpu_to_le32(0xC0000091) -#define STATUS_FLOAT_STACK_CHECK cpu_to_le32(0xC0000092) -#define STATUS_FLOAT_UNDERFLOW cpu_to_le32(0xC0000093) -#define STATUS_INTEGER_DIVIDE_BY_ZERO cpu_to_le32(0xC0000094) -#define STATUS_INTEGER_OVERFLOW cpu_to_le32(0xC0000095) -#define STATUS_PRIVILEGED_INSTRUCTION cpu_to_le32(0xC0000096) -#define STATUS_TOO_MANY_PAGING_FILES cpu_to_le32(0xC0000097) -#define STATUS_FILE_INVALID cpu_to_le32(0xC0000098) -#define STATUS_ALLOTTED_SPACE_EXCEEDED cpu_to_le32(0xC0000099) -#define STATUS_INSUFFICIENT_RESOURCES cpu_to_le32(0xC000009A) -#define STATUS_DFS_EXIT_PATH_FOUND cpu_to_le32(0xC000009B) -#define STATUS_DEVICE_DATA_ERROR cpu_to_le32(0xC000009C) -#define STATUS_DEVICE_NOT_CONNECTED cpu_to_le32(0xC000009D) -#define STATUS_DEVICE_POWER_FAILURE cpu_to_le32(0xC000009E) -#define STATUS_FREE_VM_NOT_AT_BASE cpu_to_le32(0xC000009F) -#define STATUS_MEMORY_NOT_ALLOCATED cpu_to_le32(0xC00000A0) -#define STATUS_WORKING_SET_QUOTA cpu_to_le32(0xC00000A1) -#define STATUS_MEDIA_WRITE_PROTECTED cpu_to_le32(0xC00000A2) -#define STATUS_DEVICE_NOT_READY cpu_to_le32(0xC00000A3) -#define STATUS_INVALID_GROUP_ATTRIBUTES cpu_to_le32(0xC00000A4) -#define STATUS_BAD_IMPERSONATION_LEVEL cpu_to_le32(0xC00000A5) -#define STATUS_CANT_OPEN_ANONYMOUS cpu_to_le32(0xC00000A6) -#define STATUS_BAD_VALIDATION_CLASS cpu_to_le32(0xC00000A7) -#define STATUS_BAD_TOKEN_TYPE cpu_to_le32(0xC00000A8) -#define STATUS_BAD_MASTER_BOOT_RECORD cpu_to_le32(0xC00000A9) -#define STATUS_INSTRUCTION_MISALIGNMENT cpu_to_le32(0xC00000AA) -#define STATUS_INSTANCE_NOT_AVAILABLE cpu_to_le32(0xC00000AB) -#define STATUS_PIPE_NOT_AVAILABLE cpu_to_le32(0xC00000AC) -#define STATUS_INVALID_PIPE_STATE cpu_to_le32(0xC00000AD) -#define STATUS_PIPE_BUSY cpu_to_le32(0xC00000AE) -#define STATUS_ILLEGAL_FUNCTION cpu_to_le32(0xC00000AF) -#define STATUS_PIPE_DISCONNECTED cpu_to_le32(0xC00000B0) -#define STATUS_PIPE_CLOSING cpu_to_le32(0xC00000B1) -#define STATUS_PIPE_CONNECTED cpu_to_le32(0xC00000B2) -#define STATUS_PIPE_LISTENING cpu_to_le32(0xC00000B3) -#define STATUS_INVALID_READ_MODE cpu_to_le32(0xC00000B4) -#define STATUS_IO_TIMEOUT cpu_to_le32(0xC00000B5) -#define STATUS_FILE_FORCED_CLOSED cpu_to_le32(0xC00000B6) -#define STATUS_PROFILING_NOT_STARTED cpu_to_le32(0xC00000B7) -#define STATUS_PROFILING_NOT_STOPPED cpu_to_le32(0xC00000B8) -#define STATUS_COULD_NOT_INTERPRET cpu_to_le32(0xC00000B9) -#define STATUS_FILE_IS_A_DIRECTORY cpu_to_le32(0xC00000BA) -#define STATUS_NOT_SUPPORTED cpu_to_le32(0xC00000BB) -#define STATUS_REMOTE_NOT_LISTENING cpu_to_le32(0xC00000BC) -#define STATUS_DUPLICATE_NAME cpu_to_le32(0xC00000BD) -#define STATUS_BAD_NETWORK_PATH cpu_to_le32(0xC00000BE) -#define STATUS_NETWORK_BUSY cpu_to_le32(0xC00000BF) -#define STATUS_DEVICE_DOES_NOT_EXIST cpu_to_le32(0xC00000C0) -#define STATUS_TOO_MANY_COMMANDS cpu_to_le32(0xC00000C1) -#define STATUS_ADAPTER_HARDWARE_ERROR cpu_to_le32(0xC00000C2) -#define STATUS_INVALID_NETWORK_RESPONSE cpu_to_le32(0xC00000C3) -#define STATUS_UNEXPECTED_NETWORK_ERROR cpu_to_le32(0xC00000C4) -#define STATUS_BAD_REMOTE_ADAPTER cpu_to_le32(0xC00000C5) -#define STATUS_PRINT_QUEUE_FULL cpu_to_le32(0xC00000C6) -#define STATUS_NO_SPOOL_SPACE cpu_to_le32(0xC00000C7) -#define STATUS_PRINT_CANCELLED cpu_to_le32(0xC00000C8) -#define STATUS_NETWORK_NAME_DELETED cpu_to_le32(0xC00000C9) -#define STATUS_NETWORK_ACCESS_DENIED cpu_to_le32(0xC00000CA) -#define STATUS_BAD_DEVICE_TYPE cpu_to_le32(0xC00000CB) -#define STATUS_BAD_NETWORK_NAME cpu_to_le32(0xC00000CC) -#define STATUS_TOO_MANY_NAMES cpu_to_le32(0xC00000CD) -#define STATUS_TOO_MANY_SESSIONS cpu_to_le32(0xC00000CE) -#define STATUS_SHARING_PAUSED cpu_to_le32(0xC00000CF) -#define STATUS_REQUEST_NOT_ACCEPTED cpu_to_le32(0xC00000D0) -#define STATUS_REDIRECTOR_PAUSED cpu_to_le32(0xC00000D1) -#define STATUS_NET_WRITE_FAULT cpu_to_le32(0xC00000D2) -#define STATUS_PROFILING_AT_LIMIT cpu_to_le32(0xC00000D3) -#define STATUS_NOT_SAME_DEVICE cpu_to_le32(0xC00000D4) -#define STATUS_FILE_RENAMED cpu_to_le32(0xC00000D5) -#define STATUS_VIRTUAL_CIRCUIT_CLOSED cpu_to_le32(0xC00000D6) -#define STATUS_NO_SECURITY_ON_OBJECT cpu_to_le32(0xC00000D7) -#define STATUS_CANT_WAIT cpu_to_le32(0xC00000D8) -#define STATUS_PIPE_EMPTY cpu_to_le32(0xC00000D9) -#define STATUS_CANT_ACCESS_DOMAIN_INFO cpu_to_le32(0xC00000DA) -#define STATUS_CANT_TERMINATE_SELF cpu_to_le32(0xC00000DB) -#define STATUS_INVALID_SERVER_STATE cpu_to_le32(0xC00000DC) -#define STATUS_INVALID_DOMAIN_STATE cpu_to_le32(0xC00000DD) -#define STATUS_INVALID_DOMAIN_ROLE cpu_to_le32(0xC00000DE) -#define STATUS_NO_SUCH_DOMAIN cpu_to_le32(0xC00000DF) -#define STATUS_DOMAIN_EXISTS cpu_to_le32(0xC00000E0) -#define STATUS_DOMAIN_LIMIT_EXCEEDED cpu_to_le32(0xC00000E1) -#define STATUS_OPLOCK_NOT_GRANTED cpu_to_le32(0xC00000E2) -#define STATUS_INVALID_OPLOCK_PROTOCOL cpu_to_le32(0xC00000E3) -#define STATUS_INTERNAL_DB_CORRUPTION cpu_to_le32(0xC00000E4) -#define STATUS_INTERNAL_ERROR cpu_to_le32(0xC00000E5) -#define STATUS_GENERIC_NOT_MAPPED cpu_to_le32(0xC00000E6) -#define STATUS_BAD_DESCRIPTOR_FORMAT cpu_to_le32(0xC00000E7) -#define STATUS_INVALID_USER_BUFFER cpu_to_le32(0xC00000E8) -#define STATUS_UNEXPECTED_IO_ERROR cpu_to_le32(0xC00000E9) -#define STATUS_UNEXPECTED_MM_CREATE_ERR cpu_to_le32(0xC00000EA) -#define STATUS_UNEXPECTED_MM_MAP_ERROR cpu_to_le32(0xC00000EB) -#define STATUS_UNEXPECTED_MM_EXTEND_ERR cpu_to_le32(0xC00000EC) -#define STATUS_NOT_LOGON_PROCESS cpu_to_le32(0xC00000ED) -#define STATUS_LOGON_SESSION_EXISTS cpu_to_le32(0xC00000EE) -#define STATUS_INVALID_PARAMETER_1 cpu_to_le32(0xC00000EF) -#define STATUS_INVALID_PARAMETER_2 cpu_to_le32(0xC00000F0) -#define STATUS_INVALID_PARAMETER_3 cpu_to_le32(0xC00000F1) -#define STATUS_INVALID_PARAMETER_4 cpu_to_le32(0xC00000F2) -#define STATUS_INVALID_PARAMETER_5 cpu_to_le32(0xC00000F3) -#define STATUS_INVALID_PARAMETER_6 cpu_to_le32(0xC00000F4) -#define STATUS_INVALID_PARAMETER_7 cpu_to_le32(0xC00000F5) -#define STATUS_INVALID_PARAMETER_8 cpu_to_le32(0xC00000F6) -#define STATUS_INVALID_PARAMETER_9 cpu_to_le32(0xC00000F7) -#define STATUS_INVALID_PARAMETER_10 cpu_to_le32(0xC00000F8) -#define STATUS_INVALID_PARAMETER_11 cpu_to_le32(0xC00000F9) -#define STATUS_INVALID_PARAMETER_12 cpu_to_le32(0xC00000FA) -#define STATUS_REDIRECTOR_NOT_STARTED cpu_to_le32(0xC00000FB) -#define STATUS_REDIRECTOR_STARTED cpu_to_le32(0xC00000FC) -#define STATUS_STACK_OVERFLOW cpu_to_le32(0xC00000FD) -#define STATUS_NO_SUCH_PACKAGE cpu_to_le32(0xC00000FE) -#define STATUS_BAD_FUNCTION_TABLE cpu_to_le32(0xC00000FF) -#define STATUS_VARIABLE_NOT_FOUND cpu_to_le32(0xC0000100) -#define STATUS_DIRECTORY_NOT_EMPTY cpu_to_le32(0xC0000101) -#define STATUS_FILE_CORRUPT_ERROR cpu_to_le32(0xC0000102) -#define STATUS_NOT_A_DIRECTORY cpu_to_le32(0xC0000103) -#define STATUS_BAD_LOGON_SESSION_STATE cpu_to_le32(0xC0000104) -#define STATUS_LOGON_SESSION_COLLISION cpu_to_le32(0xC0000105) -#define STATUS_NAME_TOO_LONG cpu_to_le32(0xC0000106) -#define STATUS_FILES_OPEN cpu_to_le32(0xC0000107) -#define STATUS_CONNECTION_IN_USE cpu_to_le32(0xC0000108) -#define STATUS_MESSAGE_NOT_FOUND cpu_to_le32(0xC0000109) -#define STATUS_PROCESS_IS_TERMINATING cpu_to_le32(0xC000010A) -#define STATUS_INVALID_LOGON_TYPE cpu_to_le32(0xC000010B) -#define STATUS_NO_GUID_TRANSLATION cpu_to_le32(0xC000010C) -#define STATUS_CANNOT_IMPERSONATE cpu_to_le32(0xC000010D) -#define STATUS_IMAGE_ALREADY_LOADED cpu_to_le32(0xC000010E) -#define STATUS_ABIOS_NOT_PRESENT cpu_to_le32(0xC000010F) -#define STATUS_ABIOS_LID_NOT_EXIST cpu_to_le32(0xC0000110) -#define STATUS_ABIOS_LID_ALREADY_OWNED cpu_to_le32(0xC0000111) -#define STATUS_ABIOS_NOT_LID_OWNER cpu_to_le32(0xC0000112) -#define STATUS_ABIOS_INVALID_COMMAND cpu_to_le32(0xC0000113) -#define STATUS_ABIOS_INVALID_LID cpu_to_le32(0xC0000114) -#define STATUS_ABIOS_SELECTOR_NOT_AVAILABLE cpu_to_le32(0xC0000115) -#define STATUS_ABIOS_INVALID_SELECTOR cpu_to_le32(0xC0000116) -#define STATUS_NO_LDT cpu_to_le32(0xC0000117) -#define STATUS_INVALID_LDT_SIZE cpu_to_le32(0xC0000118) -#define STATUS_INVALID_LDT_OFFSET cpu_to_le32(0xC0000119) -#define STATUS_INVALID_LDT_DESCRIPTOR cpu_to_le32(0xC000011A) -#define STATUS_INVALID_IMAGE_NE_FORMAT cpu_to_le32(0xC000011B) -#define STATUS_RXACT_INVALID_STATE cpu_to_le32(0xC000011C) -#define STATUS_RXACT_COMMIT_FAILURE cpu_to_le32(0xC000011D) -#define STATUS_MAPPED_FILE_SIZE_ZERO cpu_to_le32(0xC000011E) -#define STATUS_TOO_MANY_OPENED_FILES cpu_to_le32(0xC000011F) -#define STATUS_CANCELLED cpu_to_le32(0xC0000120) -#define STATUS_CANNOT_DELETE cpu_to_le32(0xC0000121) -#define STATUS_INVALID_COMPUTER_NAME cpu_to_le32(0xC0000122) -#define STATUS_FILE_DELETED cpu_to_le32(0xC0000123) -#define STATUS_SPECIAL_ACCOUNT cpu_to_le32(0xC0000124) -#define STATUS_SPECIAL_GROUP cpu_to_le32(0xC0000125) -#define STATUS_SPECIAL_USER cpu_to_le32(0xC0000126) -#define STATUS_MEMBERS_PRIMARY_GROUP cpu_to_le32(0xC0000127) -#define STATUS_FILE_CLOSED cpu_to_le32(0xC0000128) -#define STATUS_TOO_MANY_THREADS cpu_to_le32(0xC0000129) -#define STATUS_THREAD_NOT_IN_PROCESS cpu_to_le32(0xC000012A) -#define STATUS_TOKEN_ALREADY_IN_USE cpu_to_le32(0xC000012B) -#define STATUS_PAGEFILE_QUOTA_EXCEEDED cpu_to_le32(0xC000012C) -#define STATUS_COMMITMENT_LIMIT cpu_to_le32(0xC000012D) -#define STATUS_INVALID_IMAGE_LE_FORMAT cpu_to_le32(0xC000012E) -#define STATUS_INVALID_IMAGE_NOT_MZ cpu_to_le32(0xC000012F) -#define STATUS_INVALID_IMAGE_PROTECT cpu_to_le32(0xC0000130) -#define STATUS_INVALID_IMAGE_WIN_16 cpu_to_le32(0xC0000131) -#define STATUS_LOGON_SERVER_CONFLICT cpu_to_le32(0xC0000132) -#define STATUS_TIME_DIFFERENCE_AT_DC cpu_to_le32(0xC0000133) -#define STATUS_SYNCHRONIZATION_REQUIRED cpu_to_le32(0xC0000134) -#define STATUS_DLL_NOT_FOUND cpu_to_le32(0xC0000135) -#define STATUS_OPEN_FAILED cpu_to_le32(0xC0000136) -#define STATUS_IO_PRIVILEGE_FAILED cpu_to_le32(0xC0000137) -#define STATUS_ORDINAL_NOT_FOUND cpu_to_le32(0xC0000138) -#define STATUS_ENTRYPOINT_NOT_FOUND cpu_to_le32(0xC0000139) -#define STATUS_CONTROL_C_EXIT cpu_to_le32(0xC000013A) -#define STATUS_LOCAL_DISCONNECT cpu_to_le32(0xC000013B) -#define STATUS_REMOTE_DISCONNECT cpu_to_le32(0xC000013C) -#define STATUS_REMOTE_RESOURCES cpu_to_le32(0xC000013D) -#define STATUS_LINK_FAILED cpu_to_le32(0xC000013E) -#define STATUS_LINK_TIMEOUT cpu_to_le32(0xC000013F) -#define STATUS_INVALID_CONNECTION cpu_to_le32(0xC0000140) -#define STATUS_INVALID_ADDRESS cpu_to_le32(0xC0000141) -#define STATUS_DLL_INIT_FAILED cpu_to_le32(0xC0000142) -#define STATUS_MISSING_SYSTEMFILE cpu_to_le32(0xC0000143) -#define STATUS_UNHANDLED_EXCEPTION cpu_to_le32(0xC0000144) -#define STATUS_APP_INIT_FAILURE cpu_to_le32(0xC0000145) -#define STATUS_PAGEFILE_CREATE_FAILED cpu_to_le32(0xC0000146) -#define STATUS_NO_PAGEFILE cpu_to_le32(0xC0000147) -#define STATUS_INVALID_LEVEL cpu_to_le32(0xC0000148) -#define STATUS_WRONG_PASSWORD_CORE cpu_to_le32(0xC0000149) -#define STATUS_ILLEGAL_FLOAT_CONTEXT cpu_to_le32(0xC000014A) -#define STATUS_PIPE_BROKEN cpu_to_le32(0xC000014B) -#define STATUS_REGISTRY_CORRUPT cpu_to_le32(0xC000014C) -#define STATUS_REGISTRY_IO_FAILED cpu_to_le32(0xC000014D) -#define STATUS_NO_EVENT_PAIR cpu_to_le32(0xC000014E) -#define STATUS_UNRECOGNIZED_VOLUME cpu_to_le32(0xC000014F) -#define STATUS_SERIAL_NO_DEVICE_INITED cpu_to_le32(0xC0000150) -#define STATUS_NO_SUCH_ALIAS cpu_to_le32(0xC0000151) -#define STATUS_MEMBER_NOT_IN_ALIAS cpu_to_le32(0xC0000152) -#define STATUS_MEMBER_IN_ALIAS cpu_to_le32(0xC0000153) -#define STATUS_ALIAS_EXISTS cpu_to_le32(0xC0000154) -#define STATUS_LOGON_NOT_GRANTED cpu_to_le32(0xC0000155) -#define STATUS_TOO_MANY_SECRETS cpu_to_le32(0xC0000156) -#define STATUS_SECRET_TOO_LONG cpu_to_le32(0xC0000157) -#define STATUS_INTERNAL_DB_ERROR cpu_to_le32(0xC0000158) -#define STATUS_FULLSCREEN_MODE cpu_to_le32(0xC0000159) -#define STATUS_TOO_MANY_CONTEXT_IDS cpu_to_le32(0xC000015A) -#define STATUS_LOGON_TYPE_NOT_GRANTED cpu_to_le32(0xC000015B) -#define STATUS_NOT_REGISTRY_FILE cpu_to_le32(0xC000015C) -#define STATUS_NT_CROSS_ENCRYPTION_REQUIRED cpu_to_le32(0xC000015D) -#define STATUS_DOMAIN_CTRLR_CONFIG_ERROR cpu_to_le32(0xC000015E) -#define STATUS_FT_MISSING_MEMBER cpu_to_le32(0xC000015F) -#define STATUS_ILL_FORMED_SERVICE_ENTRY cpu_to_le32(0xC0000160) -#define STATUS_ILLEGAL_CHARACTER cpu_to_le32(0xC0000161) -#define STATUS_UNMAPPABLE_CHARACTER cpu_to_le32(0xC0000162) -#define STATUS_UNDEFINED_CHARACTER cpu_to_le32(0xC0000163) -#define STATUS_FLOPPY_VOLUME cpu_to_le32(0xC0000164) -#define STATUS_FLOPPY_ID_MARK_NOT_FOUND cpu_to_le32(0xC0000165) -#define STATUS_FLOPPY_WRONG_CYLINDER cpu_to_le32(0xC0000166) -#define STATUS_FLOPPY_UNKNOWN_ERROR cpu_to_le32(0xC0000167) -#define STATUS_FLOPPY_BAD_REGISTERS cpu_to_le32(0xC0000168) -#define STATUS_DISK_RECALIBRATE_FAILED cpu_to_le32(0xC0000169) -#define STATUS_DISK_OPERATION_FAILED cpu_to_le32(0xC000016A) -#define STATUS_DISK_RESET_FAILED cpu_to_le32(0xC000016B) -#define STATUS_SHARED_IRQ_BUSY cpu_to_le32(0xC000016C) -#define STATUS_FT_ORPHANING cpu_to_le32(0xC000016D) -#define STATUS_BIOS_FAILED_TO_CONNECT_INTERRUPT cpu_to_le32(0xC000016E) -#define STATUS_PARTITION_FAILURE cpu_to_le32(0xC0000172) -#define STATUS_INVALID_BLOCK_LENGTH cpu_to_le32(0xC0000173) -#define STATUS_DEVICE_NOT_PARTITIONED cpu_to_le32(0xC0000174) -#define STATUS_UNABLE_TO_LOCK_MEDIA cpu_to_le32(0xC0000175) -#define STATUS_UNABLE_TO_UNLOAD_MEDIA cpu_to_le32(0xC0000176) -#define STATUS_EOM_OVERFLOW cpu_to_le32(0xC0000177) -#define STATUS_NO_MEDIA cpu_to_le32(0xC0000178) -#define STATUS_NO_SUCH_MEMBER cpu_to_le32(0xC000017A) -#define STATUS_INVALID_MEMBER cpu_to_le32(0xC000017B) -#define STATUS_KEY_DELETED cpu_to_le32(0xC000017C) -#define STATUS_NO_LOG_SPACE cpu_to_le32(0xC000017D) -#define STATUS_TOO_MANY_SIDS cpu_to_le32(0xC000017E) -#define STATUS_LM_CROSS_ENCRYPTION_REQUIRED cpu_to_le32(0xC000017F) -#define STATUS_KEY_HAS_CHILDREN cpu_to_le32(0xC0000180) -#define STATUS_CHILD_MUST_BE_VOLATILE cpu_to_le32(0xC0000181) -#define STATUS_DEVICE_CONFIGURATION_ERROR cpu_to_le32(0xC0000182) -#define STATUS_DRIVER_INTERNAL_ERROR cpu_to_le32(0xC0000183) -#define STATUS_INVALID_DEVICE_STATE cpu_to_le32(0xC0000184) -#define STATUS_IO_DEVICE_ERROR cpu_to_le32(0xC0000185) -#define STATUS_DEVICE_PROTOCOL_ERROR cpu_to_le32(0xC0000186) -#define STATUS_BACKUP_CONTROLLER cpu_to_le32(0xC0000187) -#define STATUS_LOG_FILE_FULL cpu_to_le32(0xC0000188) -#define STATUS_TOO_LATE cpu_to_le32(0xC0000189) -#define STATUS_NO_TRUST_LSA_SECRET cpu_to_le32(0xC000018A) -#define STATUS_NO_TRUST_SAM_ACCOUNT cpu_to_le32(0xC000018B) -#define STATUS_TRUSTED_DOMAIN_FAILURE cpu_to_le32(0xC000018C) -#define STATUS_TRUSTED_RELATIONSHIP_FAILURE cpu_to_le32(0xC000018D) -#define STATUS_EVENTLOG_FILE_CORRUPT cpu_to_le32(0xC000018E) -#define STATUS_EVENTLOG_CANT_START cpu_to_le32(0xC000018F) -#define STATUS_TRUST_FAILURE cpu_to_le32(0xC0000190) -#define STATUS_MUTANT_LIMIT_EXCEEDED cpu_to_le32(0xC0000191) -#define STATUS_NETLOGON_NOT_STARTED cpu_to_le32(0xC0000192) -#define STATUS_ACCOUNT_EXPIRED cpu_to_le32(0xC0000193) -#define STATUS_POSSIBLE_DEADLOCK cpu_to_le32(0xC0000194) -#define STATUS_NETWORK_CREDENTIAL_CONFLICT cpu_to_le32(0xC0000195) -#define STATUS_REMOTE_SESSION_LIMIT cpu_to_le32(0xC0000196) -#define STATUS_EVENTLOG_FILE_CHANGED cpu_to_le32(0xC0000197) -#define STATUS_NOLOGON_INTERDOMAIN_TRUST_ACCOUNT cpu_to_le32(0xC0000198) -#define STATUS_NOLOGON_WORKSTATION_TRUST_ACCOUNT cpu_to_le32(0xC0000199) -#define STATUS_NOLOGON_SERVER_TRUST_ACCOUNT cpu_to_le32(0xC000019A) -#define STATUS_DOMAIN_TRUST_INCONSISTENT cpu_to_le32(0xC000019B) -#define STATUS_FS_DRIVER_REQUIRED cpu_to_le32(0xC000019C) -#define STATUS_IMAGE_ALREADY_LOADED_AS_DLL cpu_to_le32(0xC000019D) -#define STATUS_NETWORK_OPEN_RESTRICTION cpu_to_le32(0xC0000201) -#define STATUS_NO_USER_SESSION_KEY cpu_to_le32(0xC0000202) -#define STATUS_USER_SESSION_DELETED cpu_to_le32(0xC0000203) -#define STATUS_RESOURCE_LANG_NOT_FOUND cpu_to_le32(0xC0000204) -#define STATUS_INSUFF_SERVER_RESOURCES cpu_to_le32(0xC0000205) -#define STATUS_INVALID_BUFFER_SIZE cpu_to_le32(0xC0000206) -#define STATUS_INVALID_ADDRESS_COMPONENT cpu_to_le32(0xC0000207) -#define STATUS_INVALID_ADDRESS_WILDCARD cpu_to_le32(0xC0000208) -#define STATUS_TOO_MANY_ADDRESSES cpu_to_le32(0xC0000209) -#define STATUS_ADDRESS_ALREADY_EXISTS cpu_to_le32(0xC000020A) -#define STATUS_ADDRESS_CLOSED cpu_to_le32(0xC000020B) -#define STATUS_CONNECTION_DISCONNECTED cpu_to_le32(0xC000020C) -#define STATUS_CONNECTION_RESET cpu_to_le32(0xC000020D) -#define STATUS_TOO_MANY_NODES cpu_to_le32(0xC000020E) -#define STATUS_TRANSACTION_ABORTED cpu_to_le32(0xC000020F) -#define STATUS_TRANSACTION_TIMED_OUT cpu_to_le32(0xC0000210) -#define STATUS_TRANSACTION_NO_RELEASE cpu_to_le32(0xC0000211) -#define STATUS_TRANSACTION_NO_MATCH cpu_to_le32(0xC0000212) -#define STATUS_TRANSACTION_RESPONDED cpu_to_le32(0xC0000213) -#define STATUS_TRANSACTION_INVALID_ID cpu_to_le32(0xC0000214) -#define STATUS_TRANSACTION_INVALID_TYPE cpu_to_le32(0xC0000215) -#define STATUS_NOT_SERVER_SESSION cpu_to_le32(0xC0000216) -#define STATUS_NOT_CLIENT_SESSION cpu_to_le32(0xC0000217) -#define STATUS_CANNOT_LOAD_REGISTRY_FILE cpu_to_le32(0xC0000218) -#define STATUS_DEBUG_ATTACH_FAILED cpu_to_le32(0xC0000219) -#define STATUS_SYSTEM_PROCESS_TERMINATED cpu_to_le32(0xC000021A) -#define STATUS_DATA_NOT_ACCEPTED cpu_to_le32(0xC000021B) -#define STATUS_NO_BROWSER_SERVERS_FOUND cpu_to_le32(0xC000021C) -#define STATUS_VDM_HARD_ERROR cpu_to_le32(0xC000021D) -#define STATUS_DRIVER_CANCEL_TIMEOUT cpu_to_le32(0xC000021E) -#define STATUS_REPLY_MESSAGE_MISMATCH cpu_to_le32(0xC000021F) -#define STATUS_MAPPED_ALIGNMENT cpu_to_le32(0xC0000220) -#define STATUS_IMAGE_CHECKSUM_MISMATCH cpu_to_le32(0xC0000221) -#define STATUS_LOST_WRITEBEHIND_DATA cpu_to_le32(0xC0000222) -#define STATUS_CLIENT_SERVER_PARAMETERS_INVALID cpu_to_le32(0xC0000223) -#define STATUS_PASSWORD_MUST_CHANGE cpu_to_le32(0xC0000224) -#define STATUS_NOT_FOUND cpu_to_le32(0xC0000225) -#define STATUS_NOT_TINY_STREAM cpu_to_le32(0xC0000226) -#define STATUS_RECOVERY_FAILURE cpu_to_le32(0xC0000227) -#define STATUS_STACK_OVERFLOW_READ cpu_to_le32(0xC0000228) -#define STATUS_FAIL_CHECK cpu_to_le32(0xC0000229) -#define STATUS_DUPLICATE_OBJECTID cpu_to_le32(0xC000022A) -#define STATUS_OBJECTID_EXISTS cpu_to_le32(0xC000022B) -#define STATUS_CONVERT_TO_LARGE cpu_to_le32(0xC000022C) -#define STATUS_RETRY cpu_to_le32(0xC000022D) -#define STATUS_FOUND_OUT_OF_SCOPE cpu_to_le32(0xC000022E) -#define STATUS_ALLOCATE_BUCKET cpu_to_le32(0xC000022F) -#define STATUS_PROPSET_NOT_FOUND cpu_to_le32(0xC0000230) -#define STATUS_MARSHALL_OVERFLOW cpu_to_le32(0xC0000231) -#define STATUS_INVALID_VARIANT cpu_to_le32(0xC0000232) -#define STATUS_DOMAIN_CONTROLLER_NOT_FOUND cpu_to_le32(0xC0000233) -#define STATUS_ACCOUNT_LOCKED_OUT cpu_to_le32(0xC0000234) -#define STATUS_HANDLE_NOT_CLOSABLE cpu_to_le32(0xC0000235) -#define STATUS_CONNECTION_REFUSED cpu_to_le32(0xC0000236) -#define STATUS_GRACEFUL_DISCONNECT cpu_to_le32(0xC0000237) -#define STATUS_ADDRESS_ALREADY_ASSOCIATED cpu_to_le32(0xC0000238) -#define STATUS_ADDRESS_NOT_ASSOCIATED cpu_to_le32(0xC0000239) -#define STATUS_CONNECTION_INVALID cpu_to_le32(0xC000023A) -#define STATUS_CONNECTION_ACTIVE cpu_to_le32(0xC000023B) -#define STATUS_NETWORK_UNREACHABLE cpu_to_le32(0xC000023C) -#define STATUS_HOST_UNREACHABLE cpu_to_le32(0xC000023D) -#define STATUS_PROTOCOL_UNREACHABLE cpu_to_le32(0xC000023E) -#define STATUS_PORT_UNREACHABLE cpu_to_le32(0xC000023F) -#define STATUS_REQUEST_ABORTED cpu_to_le32(0xC0000240) -#define STATUS_CONNECTION_ABORTED cpu_to_le32(0xC0000241) -#define STATUS_BAD_COMPRESSION_BUFFER cpu_to_le32(0xC0000242) -#define STATUS_USER_MAPPED_FILE cpu_to_le32(0xC0000243) -#define STATUS_AUDIT_FAILED cpu_to_le32(0xC0000244) -#define STATUS_TIMER_RESOLUTION_NOT_SET cpu_to_le32(0xC0000245) -#define STATUS_CONNECTION_COUNT_LIMIT cpu_to_le32(0xC0000246) -#define STATUS_LOGIN_TIME_RESTRICTION cpu_to_le32(0xC0000247) -#define STATUS_LOGIN_WKSTA_RESTRICTION cpu_to_le32(0xC0000248) -#define STATUS_IMAGE_MP_UP_MISMATCH cpu_to_le32(0xC0000249) -#define STATUS_INSUFFICIENT_LOGON_INFO cpu_to_le32(0xC0000250) -#define STATUS_BAD_DLL_ENTRYPOINT cpu_to_le32(0xC0000251) -#define STATUS_BAD_SERVICE_ENTRYPOINT cpu_to_le32(0xC0000252) -#define STATUS_LPC_REPLY_LOST cpu_to_le32(0xC0000253) -#define STATUS_IP_ADDRESS_CONFLICT1 cpu_to_le32(0xC0000254) -#define STATUS_IP_ADDRESS_CONFLICT2 cpu_to_le32(0xC0000255) -#define STATUS_REGISTRY_QUOTA_LIMIT cpu_to_le32(0xC0000256) -#define STATUS_PATH_NOT_COVERED cpu_to_le32(0xC0000257) -#define STATUS_NO_CALLBACK_ACTIVE cpu_to_le32(0xC0000258) -#define STATUS_LICENSE_QUOTA_EXCEEDED cpu_to_le32(0xC0000259) -#define STATUS_PWD_TOO_SHORT cpu_to_le32(0xC000025A) -#define STATUS_PWD_TOO_RECENT cpu_to_le32(0xC000025B) -#define STATUS_PWD_HISTORY_CONFLICT cpu_to_le32(0xC000025C) -#define STATUS_PLUGPLAY_NO_DEVICE cpu_to_le32(0xC000025E) -#define STATUS_UNSUPPORTED_COMPRESSION cpu_to_le32(0xC000025F) -#define STATUS_INVALID_HW_PROFILE cpu_to_le32(0xC0000260) -#define STATUS_INVALID_PLUGPLAY_DEVICE_PATH cpu_to_le32(0xC0000261) -#define STATUS_DRIVER_ORDINAL_NOT_FOUND cpu_to_le32(0xC0000262) -#define STATUS_DRIVER_ENTRYPOINT_NOT_FOUND cpu_to_le32(0xC0000263) -#define STATUS_RESOURCE_NOT_OWNED cpu_to_le32(0xC0000264) -#define STATUS_TOO_MANY_LINKS cpu_to_le32(0xC0000265) -#define STATUS_QUOTA_LIST_INCONSISTENT cpu_to_le32(0xC0000266) -#define STATUS_FILE_IS_OFFLINE cpu_to_le32(0xC0000267) -#define STATUS_EVALUATION_EXPIRATION cpu_to_le32(0xC0000268) -#define STATUS_ILLEGAL_DLL_RELOCATION cpu_to_le32(0xC0000269) -#define STATUS_LICENSE_VIOLATION cpu_to_le32(0xC000026A) -#define STATUS_DLL_INIT_FAILED_LOGOFF cpu_to_le32(0xC000026B) -#define STATUS_DRIVER_UNABLE_TO_LOAD cpu_to_le32(0xC000026C) -#define STATUS_DFS_UNAVAILABLE cpu_to_le32(0xC000026D) -#define STATUS_VOLUME_DISMOUNTED cpu_to_le32(0xC000026E) -#define STATUS_WX86_INTERNAL_ERROR cpu_to_le32(0xC000026F) -#define STATUS_WX86_FLOAT_STACK_CHECK cpu_to_le32(0xC0000270) -#define STATUS_VALIDATE_CONTINUE cpu_to_le32(0xC0000271) -#define STATUS_NO_MATCH cpu_to_le32(0xC0000272) -#define STATUS_NO_MORE_MATCHES cpu_to_le32(0xC0000273) -#define STATUS_NOT_A_REPARSE_POINT cpu_to_le32(0xC0000275) -#define STATUS_IO_REPARSE_TAG_INVALID cpu_to_le32(0xC0000276) -#define STATUS_IO_REPARSE_TAG_MISMATCH cpu_to_le32(0xC0000277) -#define STATUS_IO_REPARSE_DATA_INVALID cpu_to_le32(0xC0000278) -#define STATUS_IO_REPARSE_TAG_NOT_HANDLED cpu_to_le32(0xC0000279) -#define STATUS_REPARSE_POINT_NOT_RESOLVED cpu_to_le32(0xC0000280) -#define STATUS_DIRECTORY_IS_A_REPARSE_POINT cpu_to_le32(0xC0000281) -#define STATUS_RANGE_LIST_CONFLICT cpu_to_le32(0xC0000282) -#define STATUS_SOURCE_ELEMENT_EMPTY cpu_to_le32(0xC0000283) -#define STATUS_DESTINATION_ELEMENT_FULL cpu_to_le32(0xC0000284) -#define STATUS_ILLEGAL_ELEMENT_ADDRESS cpu_to_le32(0xC0000285) -#define STATUS_MAGAZINE_NOT_PRESENT cpu_to_le32(0xC0000286) -#define STATUS_REINITIALIZATION_NEEDED cpu_to_le32(0xC0000287) -#define STATUS_ENCRYPTION_FAILED cpu_to_le32(0xC000028A) -#define STATUS_DECRYPTION_FAILED cpu_to_le32(0xC000028B) -#define STATUS_RANGE_NOT_FOUND cpu_to_le32(0xC000028C) -#define STATUS_NO_RECOVERY_POLICY cpu_to_le32(0xC000028D) -#define STATUS_NO_EFS cpu_to_le32(0xC000028E) -#define STATUS_WRONG_EFS cpu_to_le32(0xC000028F) -#define STATUS_NO_USER_KEYS cpu_to_le32(0xC0000290) -#define STATUS_FILE_NOT_ENCRYPTED cpu_to_le32(0xC0000291) -#define STATUS_NOT_EXPORT_FORMAT cpu_to_le32(0xC0000292) -#define STATUS_FILE_ENCRYPTED cpu_to_le32(0xC0000293) -#define STATUS_WMI_GUID_NOT_FOUND cpu_to_le32(0xC0000295) -#define STATUS_WMI_INSTANCE_NOT_FOUND cpu_to_le32(0xC0000296) -#define STATUS_WMI_ITEMID_NOT_FOUND cpu_to_le32(0xC0000297) -#define STATUS_WMI_TRY_AGAIN cpu_to_le32(0xC0000298) -#define STATUS_SHARED_POLICY cpu_to_le32(0xC0000299) -#define STATUS_POLICY_OBJECT_NOT_FOUND cpu_to_le32(0xC000029A) -#define STATUS_POLICY_ONLY_IN_DS cpu_to_le32(0xC000029B) -#define STATUS_VOLUME_NOT_UPGRADED cpu_to_le32(0xC000029C) -#define STATUS_REMOTE_STORAGE_NOT_ACTIVE cpu_to_le32(0xC000029D) -#define STATUS_REMOTE_STORAGE_MEDIA_ERROR cpu_to_le32(0xC000029E) -#define STATUS_NO_TRACKING_SERVICE cpu_to_le32(0xC000029F) -#define STATUS_SERVER_SID_MISMATCH cpu_to_le32(0xC00002A0) -#define STATUS_DS_NO_ATTRIBUTE_OR_VALUE cpu_to_le32(0xC00002A1) -#define STATUS_DS_INVALID_ATTRIBUTE_SYNTAX cpu_to_le32(0xC00002A2) -#define STATUS_DS_ATTRIBUTE_TYPE_UNDEFINED cpu_to_le32(0xC00002A3) -#define STATUS_DS_ATTRIBUTE_OR_VALUE_EXISTS cpu_to_le32(0xC00002A4) -#define STATUS_DS_BUSY cpu_to_le32(0xC00002A5) -#define STATUS_DS_UNAVAILABLE cpu_to_le32(0xC00002A6) -#define STATUS_DS_NO_RIDS_ALLOCATED cpu_to_le32(0xC00002A7) -#define STATUS_DS_NO_MORE_RIDS cpu_to_le32(0xC00002A8) -#define STATUS_DS_INCORRECT_ROLE_OWNER cpu_to_le32(0xC00002A9) -#define STATUS_DS_RIDMGR_INIT_ERROR cpu_to_le32(0xC00002AA) -#define STATUS_DS_OBJ_CLASS_VIOLATION cpu_to_le32(0xC00002AB) -#define STATUS_DS_CANT_ON_NON_LEAF cpu_to_le32(0xC00002AC) -#define STATUS_DS_CANT_ON_RDN cpu_to_le32(0xC00002AD) -#define STATUS_DS_CANT_MOD_OBJ_CLASS cpu_to_le32(0xC00002AE) -#define STATUS_DS_CROSS_DOM_MOVE_FAILED cpu_to_le32(0xC00002AF) -#define STATUS_DS_GC_NOT_AVAILABLE cpu_to_le32(0xC00002B0) -#define STATUS_DIRECTORY_SERVICE_REQUIRED cpu_to_le32(0xC00002B1) -#define STATUS_REPARSE_ATTRIBUTE_CONFLICT cpu_to_le32(0xC00002B2) -#define STATUS_CANT_ENABLE_DENY_ONLY cpu_to_le32(0xC00002B3) -#define STATUS_FLOAT_MULTIPLE_FAULTS cpu_to_le32(0xC00002B4) -#define STATUS_FLOAT_MULTIPLE_TRAPS cpu_to_le32(0xC00002B5) -#define STATUS_DEVICE_REMOVED cpu_to_le32(0xC00002B6) -#define STATUS_JOURNAL_DELETE_IN_PROGRESS cpu_to_le32(0xC00002B7) -#define STATUS_JOURNAL_NOT_ACTIVE cpu_to_le32(0xC00002B8) -#define STATUS_NOINTERFACE cpu_to_le32(0xC00002B9) -#define STATUS_DS_ADMIN_LIMIT_EXCEEDED cpu_to_le32(0xC00002C1) -#define STATUS_DRIVER_FAILED_SLEEP cpu_to_le32(0xC00002C2) -#define STATUS_MUTUAL_AUTHENTICATION_FAILED cpu_to_le32(0xC00002C3) -#define STATUS_CORRUPT_SYSTEM_FILE cpu_to_le32(0xC00002C4) -#define STATUS_DATATYPE_MISALIGNMENT_ERROR cpu_to_le32(0xC00002C5) -#define STATUS_WMI_READ_ONLY cpu_to_le32(0xC00002C6) -#define STATUS_WMI_SET_FAILURE cpu_to_le32(0xC00002C7) -#define STATUS_COMMITMENT_MINIMUM cpu_to_le32(0xC00002C8) -#define STATUS_REG_NAT_CONSUMPTION cpu_to_le32(0xC00002C9) -#define STATUS_TRANSPORT_FULL cpu_to_le32(0xC00002CA) -#define STATUS_DS_SAM_INIT_FAILURE cpu_to_le32(0xC00002CB) -#define STATUS_ONLY_IF_CONNECTED cpu_to_le32(0xC00002CC) -#define STATUS_DS_SENSITIVE_GROUP_VIOLATION cpu_to_le32(0xC00002CD) -#define STATUS_PNP_RESTART_ENUMERATION cpu_to_le32(0xC00002CE) -#define STATUS_JOURNAL_ENTRY_DELETED cpu_to_le32(0xC00002CF) -#define STATUS_DS_CANT_MOD_PRIMARYGROUPID cpu_to_le32(0xC00002D0) -#define STATUS_SYSTEM_IMAGE_BAD_SIGNATURE cpu_to_le32(0xC00002D1) -#define STATUS_PNP_REBOOT_REQUIRED cpu_to_le32(0xC00002D2) -#define STATUS_POWER_STATE_INVALID cpu_to_le32(0xC00002D3) -#define STATUS_DS_INVALID_GROUP_TYPE cpu_to_le32(0xC00002D4) -#define STATUS_DS_NO_NEST_GLOBALGROUP_IN_MIXEDDOMAIN cpu_to_le32(0xC00002D5) -#define STATUS_DS_NO_NEST_LOCALGROUP_IN_MIXEDDOMAIN cpu_to_le32(0xC00002D6) -#define STATUS_DS_GLOBAL_CANT_HAVE_LOCAL_MEMBER cpu_to_le32(0xC00002D7) -#define STATUS_DS_GLOBAL_CANT_HAVE_UNIVERSAL_MEMBER cpu_to_le32(0xC00002D8) -#define STATUS_DS_UNIVERSAL_CANT_HAVE_LOCAL_MEMBER cpu_to_le32(0xC00002D9) -#define STATUS_DS_GLOBAL_CANT_HAVE_CROSSDOMAIN_MEMBER cpu_to_le32(0xC00002DA) -#define STATUS_DS_LOCAL_CANT_HAVE_CROSSDOMAIN_LOCAL_MEMBER cpu_to_le32(0xC00002DB) -#define STATUS_DS_HAVE_PRIMARY_MEMBERS cpu_to_le32(0xC00002DC) -#define STATUS_WMI_NOT_SUPPORTED cpu_to_le32(0xC00002DD) -#define STATUS_INSUFFICIENT_POWER cpu_to_le32(0xC00002DE) -#define STATUS_SAM_NEED_BOOTKEY_PASSWORD cpu_to_le32(0xC00002DF) -#define STATUS_SAM_NEED_BOOTKEY_FLOPPY cpu_to_le32(0xC00002E0) -#define STATUS_DS_CANT_START cpu_to_le32(0xC00002E1) -#define STATUS_DS_INIT_FAILURE cpu_to_le32(0xC00002E2) -#define STATUS_SAM_INIT_FAILURE cpu_to_le32(0xC00002E3) -#define STATUS_DS_GC_REQUIRED cpu_to_le32(0xC00002E4) -#define STATUS_DS_LOCAL_MEMBER_OF_LOCAL_ONLY cpu_to_le32(0xC00002E5) -#define STATUS_DS_NO_FPO_IN_UNIVERSAL_GROUPS cpu_to_le32(0xC00002E6) -#define STATUS_DS_MACHINE_ACCOUNT_QUOTA_EXCEEDED cpu_to_le32(0xC00002E7) -#define STATUS_MULTIPLE_FAULT_VIOLATION cpu_to_le32(0xC00002E8) -#define STATUS_CURRENT_DOMAIN_NOT_ALLOWED cpu_to_le32(0xC00002E9) -#define STATUS_CANNOT_MAKE cpu_to_le32(0xC00002EA) -#define STATUS_SYSTEM_SHUTDOWN cpu_to_le32(0xC00002EB) -#define STATUS_DS_INIT_FAILURE_CONSOLE cpu_to_le32(0xC00002EC) -#define STATUS_DS_SAM_INIT_FAILURE_CONSOLE cpu_to_le32(0xC00002ED) -#define STATUS_UNFINISHED_CONTEXT_DELETED cpu_to_le32(0xC00002EE) -#define STATUS_NO_TGT_REPLY cpu_to_le32(0xC00002EF) -#define STATUS_OBJECTID_NOT_FOUND cpu_to_le32(0xC00002F0) -#define STATUS_NO_IP_ADDRESSES cpu_to_le32(0xC00002F1) -#define STATUS_WRONG_CREDENTIAL_HANDLE cpu_to_le32(0xC00002F2) -#define STATUS_CRYPTO_SYSTEM_INVALID cpu_to_le32(0xC00002F3) -#define STATUS_MAX_REFERRALS_EXCEEDED cpu_to_le32(0xC00002F4) -#define STATUS_MUST_BE_KDC cpu_to_le32(0xC00002F5) -#define STATUS_STRONG_CRYPTO_NOT_SUPPORTED cpu_to_le32(0xC00002F6) -#define STATUS_TOO_MANY_PRINCIPALS cpu_to_le32(0xC00002F7) -#define STATUS_NO_PA_DATA cpu_to_le32(0xC00002F8) -#define STATUS_PKINIT_NAME_MISMATCH cpu_to_le32(0xC00002F9) -#define STATUS_SMARTCARD_LOGON_REQUIRED cpu_to_le32(0xC00002FA) -#define STATUS_KDC_INVALID_REQUEST cpu_to_le32(0xC00002FB) -#define STATUS_KDC_UNABLE_TO_REFER cpu_to_le32(0xC00002FC) -#define STATUS_KDC_UNKNOWN_ETYPE cpu_to_le32(0xC00002FD) -#define STATUS_SHUTDOWN_IN_PROGRESS cpu_to_le32(0xC00002FE) -#define STATUS_SERVER_SHUTDOWN_IN_PROGRESS cpu_to_le32(0xC00002FF) -#define STATUS_NOT_SUPPORTED_ON_SBS cpu_to_le32(0xC0000300) -#define STATUS_WMI_GUID_DISCONNECTED cpu_to_le32(0xC0000301) -#define STATUS_WMI_ALREADY_DISABLED cpu_to_le32(0xC0000302) -#define STATUS_WMI_ALREADY_ENABLED cpu_to_le32(0xC0000303) -#define STATUS_MFT_TOO_FRAGMENTED cpu_to_le32(0xC0000304) -#define STATUS_COPY_PROTECTION_FAILURE cpu_to_le32(0xC0000305) -#define STATUS_CSS_AUTHENTICATION_FAILURE cpu_to_le32(0xC0000306) -#define STATUS_CSS_KEY_NOT_PRESENT cpu_to_le32(0xC0000307) -#define STATUS_CSS_KEY_NOT_ESTABLISHED cpu_to_le32(0xC0000308) -#define STATUS_CSS_SCRAMBLED_SECTOR cpu_to_le32(0xC0000309) -#define STATUS_CSS_REGION_MISMATCH cpu_to_le32(0xC000030A) -#define STATUS_CSS_RESETS_EXHAUSTED cpu_to_le32(0xC000030B) -#define STATUS_PKINIT_FAILURE cpu_to_le32(0xC0000320) -#define STATUS_SMARTCARD_SUBSYSTEM_FAILURE cpu_to_le32(0xC0000321) -#define STATUS_NO_KERB_KEY cpu_to_le32(0xC0000322) -#define STATUS_HOST_DOWN cpu_to_le32(0xC0000350) -#define STATUS_UNSUPPORTED_PREAUTH cpu_to_le32(0xC0000351) -#define STATUS_EFS_ALG_BLOB_TOO_BIG cpu_to_le32(0xC0000352) -#define STATUS_PORT_NOT_SET cpu_to_le32(0xC0000353) -#define STATUS_DEBUGGER_INACTIVE cpu_to_le32(0xC0000354) -#define STATUS_DS_VERSION_CHECK_FAILURE cpu_to_le32(0xC0000355) -#define STATUS_AUDITING_DISABLED cpu_to_le32(0xC0000356) -#define STATUS_PRENT4_MACHINE_ACCOUNT cpu_to_le32(0xC0000357) -#define STATUS_DS_AG_CANT_HAVE_UNIVERSAL_MEMBER cpu_to_le32(0xC0000358) -#define STATUS_INVALID_IMAGE_WIN_32 cpu_to_le32(0xC0000359) -#define STATUS_INVALID_IMAGE_WIN_64 cpu_to_le32(0xC000035A) -#define STATUS_BAD_BINDINGS cpu_to_le32(0xC000035B) -#define STATUS_NETWORK_SESSION_EXPIRED cpu_to_le32(0xC000035C) -#define STATUS_APPHELP_BLOCK cpu_to_le32(0xC000035D) -#define STATUS_ALL_SIDS_FILTERED cpu_to_le32(0xC000035E) -#define STATUS_NOT_SAFE_MODE_DRIVER cpu_to_le32(0xC000035F) -#define STATUS_ACCESS_DISABLED_BY_POLICY_DEFAULT cpu_to_le32(0xC0000361) -#define STATUS_ACCESS_DISABLED_BY_POLICY_PATH cpu_to_le32(0xC0000362) -#define STATUS_ACCESS_DISABLED_BY_POLICY_PUBLISHER cpu_to_le32(0xC0000363) -#define STATUS_ACCESS_DISABLED_BY_POLICY_OTHER cpu_to_le32(0xC0000364) -#define STATUS_FAILED_DRIVER_ENTRY cpu_to_le32(0xC0000365) -#define STATUS_DEVICE_ENUMERATION_ERROR cpu_to_le32(0xC0000366) -#define STATUS_MOUNT_POINT_NOT_RESOLVED cpu_to_le32(0xC0000368) -#define STATUS_INVALID_DEVICE_OBJECT_PARAMETER cpu_to_le32(0xC0000369) -#define STATUS_MCA_OCCURED cpu_to_le32(0xC000036A) -#define STATUS_DRIVER_BLOCKED_CRITICAL cpu_to_le32(0xC000036B) -#define STATUS_DRIVER_BLOCKED cpu_to_le32(0xC000036C) -#define STATUS_DRIVER_DATABASE_ERROR cpu_to_le32(0xC000036D) -#define STATUS_SYSTEM_HIVE_TOO_LARGE cpu_to_le32(0xC000036E) -#define STATUS_INVALID_IMPORT_OF_NON_DLL cpu_to_le32(0xC000036F) -#define STATUS_NO_SECRETS cpu_to_le32(0xC0000371) -#define STATUS_ACCESS_DISABLED_NO_SAFER_UI_BY_POLICY cpu_to_le32(0xC0000372) -#define STATUS_FAILED_STACK_SWITCH cpu_to_le32(0xC0000373) -#define STATUS_HEAP_CORRUPTION cpu_to_le32(0xC0000374) -#define STATUS_SMARTCARD_WRONG_PIN cpu_to_le32(0xC0000380) -#define STATUS_SMARTCARD_CARD_BLOCKED cpu_to_le32(0xC0000381) -#define STATUS_SMARTCARD_CARD_NOT_AUTHENTICATED cpu_to_le32(0xC0000382) -#define STATUS_SMARTCARD_NO_CARD cpu_to_le32(0xC0000383) -#define STATUS_SMARTCARD_NO_KEY_CONTAINER cpu_to_le32(0xC0000384) -#define STATUS_SMARTCARD_NO_CERTIFICATE cpu_to_le32(0xC0000385) -#define STATUS_SMARTCARD_NO_KEYSET cpu_to_le32(0xC0000386) -#define STATUS_SMARTCARD_IO_ERROR cpu_to_le32(0xC0000387) -#define STATUS_DOWNGRADE_DETECTED cpu_to_le32(0xC0000388) -#define STATUS_SMARTCARD_CERT_REVOKED cpu_to_le32(0xC0000389) -#define STATUS_ISSUING_CA_UNTRUSTED cpu_to_le32(0xC000038A) -#define STATUS_REVOCATION_OFFLINE_C cpu_to_le32(0xC000038B) -#define STATUS_PKINIT_CLIENT_FAILURE cpu_to_le32(0xC000038C) -#define STATUS_SMARTCARD_CERT_EXPIRED cpu_to_le32(0xC000038D) -#define STATUS_DRIVER_FAILED_PRIOR_UNLOAD cpu_to_le32(0xC000038E) -#define STATUS_SMARTCARD_SILENT_CONTEXT cpu_to_le32(0xC000038F) -#define STATUS_PER_USER_TRUST_QUOTA_EXCEEDED cpu_to_le32(0xC0000401) -#define STATUS_ALL_USER_TRUST_QUOTA_EXCEEDED cpu_to_le32(0xC0000402) -#define STATUS_USER_DELETE_TRUST_QUOTA_EXCEEDED cpu_to_le32(0xC0000403) -#define STATUS_DS_NAME_NOT_UNIQUE cpu_to_le32(0xC0000404) -#define STATUS_DS_DUPLICATE_ID_FOUND cpu_to_le32(0xC0000405) -#define STATUS_DS_GROUP_CONVERSION_ERROR cpu_to_le32(0xC0000406) -#define STATUS_VOLSNAP_PREPARE_HIBERNATE cpu_to_le32(0xC0000407) -#define STATUS_USER2USER_REQUIRED cpu_to_le32(0xC0000408) -#define STATUS_STACK_BUFFER_OVERRUN cpu_to_le32(0xC0000409) -#define STATUS_NO_S4U_PROT_SUPPORT cpu_to_le32(0xC000040A) -#define STATUS_CROSSREALM_DELEGATION_FAILURE cpu_to_le32(0xC000040B) -#define STATUS_REVOCATION_OFFLINE_KDC cpu_to_le32(0xC000040C) -#define STATUS_ISSUING_CA_UNTRUSTED_KDC cpu_to_le32(0xC000040D) -#define STATUS_KDC_CERT_EXPIRED cpu_to_le32(0xC000040E) -#define STATUS_KDC_CERT_REVOKED cpu_to_le32(0xC000040F) -#define STATUS_PARAMETER_QUOTA_EXCEEDED cpu_to_le32(0xC0000410) -#define STATUS_HIBERNATION_FAILURE cpu_to_le32(0xC0000411) -#define STATUS_DELAY_LOAD_FAILED cpu_to_le32(0xC0000412) -#define STATUS_AUTHENTICATION_FIREWALL_FAILED cpu_to_le32(0xC0000413) -#define STATUS_VDM_DISALLOWED cpu_to_le32(0xC0000414) -#define STATUS_HUNG_DISPLAY_DRIVER_THREAD cpu_to_le32(0xC0000415) -#define STATUS_INSUFFICIENT_RESOURCE_FOR_SPECIFIED_SHARED_SECTION_SIZE cpu_to_le32(0xC0000416) -#define STATUS_INVALID_CRUNTIME_PARAMETER cpu_to_le32(0xC0000417) -#define STATUS_NTLM_BLOCKED cpu_to_le32(0xC0000418) -#define STATUS_ASSERTION_FAILURE cpu_to_le32(0xC0000420) -#define STATUS_VERIFIER_STOP cpu_to_le32(0xC0000421) -#define STATUS_CALLBACK_POP_STACK cpu_to_le32(0xC0000423) -#define STATUS_INCOMPATIBLE_DRIVER_BLOCKED cpu_to_le32(0xC0000424) -#define STATUS_HIVE_UNLOADED cpu_to_le32(0xC0000425) -#define STATUS_COMPRESSION_DISABLED cpu_to_le32(0xC0000426) -#define STATUS_FILE_SYSTEM_LIMITATION cpu_to_le32(0xC0000427) -#define STATUS_INVALID_IMAGE_HASH cpu_to_le32(0xC0000428) -#define STATUS_NOT_CAPABLE cpu_to_le32(0xC0000429) -#define STATUS_REQUEST_OUT_OF_SEQUENCE cpu_to_le32(0xC000042A) -#define STATUS_IMPLEMENTATION_LIMIT cpu_to_le32(0xC000042B) -#define STATUS_ELEVATION_REQUIRED cpu_to_le32(0xC000042C) -#define STATUS_BEYOND_VDL cpu_to_le32(0xC0000432) -#define STATUS_ENCOUNTERED_WRITE_IN_PROGRESS cpu_to_le32(0xC0000433) -#define STATUS_PTE_CHANGED cpu_to_le32(0xC0000434) -#define STATUS_PURGE_FAILED cpu_to_le32(0xC0000435) -#define STATUS_CRED_REQUIRES_CONFIRMATION cpu_to_le32(0xC0000440) -#define STATUS_CS_ENCRYPTION_INVALID_SERVER_RESPONSE cpu_to_le32(0xC0000441) -#define STATUS_CS_ENCRYPTION_UNSUPPORTED_SERVER cpu_to_le32(0xC0000442) -#define STATUS_CS_ENCRYPTION_EXISTING_ENCRYPTED_FILE cpu_to_le32(0xC0000443) -#define STATUS_CS_ENCRYPTION_NEW_ENCRYPTED_FILE cpu_to_le32(0xC0000444) -#define STATUS_CS_ENCRYPTION_FILE_NOT_CSE cpu_to_le32(0xC0000445) -#define STATUS_INVALID_LABEL cpu_to_le32(0xC0000446) -#define STATUS_DRIVER_PROCESS_TERMINATED cpu_to_le32(0xC0000450) -#define STATUS_AMBIGUOUS_SYSTEM_DEVICE cpu_to_le32(0xC0000451) -#define STATUS_SYSTEM_DEVICE_NOT_FOUND cpu_to_le32(0xC0000452) -#define STATUS_RESTART_BOOT_APPLICATION cpu_to_le32(0xC0000453) -#define STATUS_INVALID_TASK_NAME cpu_to_le32(0xC0000500) -#define STATUS_INVALID_TASK_INDEX cpu_to_le32(0xC0000501) -#define STATUS_THREAD_ALREADY_IN_TASK cpu_to_le32(0xC0000502) -#define STATUS_CALLBACK_BYPASS cpu_to_le32(0xC0000503) -#define STATUS_PORT_CLOSED cpu_to_le32(0xC0000700) -#define STATUS_MESSAGE_LOST cpu_to_le32(0xC0000701) -#define STATUS_INVALID_MESSAGE cpu_to_le32(0xC0000702) -#define STATUS_REQUEST_CANCELED cpu_to_le32(0xC0000703) -#define STATUS_RECURSIVE_DISPATCH cpu_to_le32(0xC0000704) -#define STATUS_LPC_RECEIVE_BUFFER_EXPECTED cpu_to_le32(0xC0000705) -#define STATUS_LPC_INVALID_CONNECTION_USAGE cpu_to_le32(0xC0000706) -#define STATUS_LPC_REQUESTS_NOT_ALLOWED cpu_to_le32(0xC0000707) -#define STATUS_RESOURCE_IN_USE cpu_to_le32(0xC0000708) -#define STATUS_HARDWARE_MEMORY_ERROR cpu_to_le32(0xC0000709) -#define STATUS_THREADPOOL_HANDLE_EXCEPTION cpu_to_le32(0xC000070A) -#define STATUS_THREADPOOL_SET_EVENT_ON_COMPLETION_FAILED cpu_to_le32(0xC000070B) -#define STATUS_THREADPOOL_RELEASE_SEMAPHORE_ON_COMPLETION_FAILED cpu_to_le32(0xC000070C) -#define STATUS_THREADPOOL_RELEASE_MUTEX_ON_COMPLETION_FAILED cpu_to_le32(0xC000070D) -#define STATUS_THREADPOOL_FREE_LIBRARY_ON_COMPLETION_FAILED cpu_to_le32(0xC000070E) -#define STATUS_THREADPOOL_RELEASED_DURING_OPERATION cpu_to_le32(0xC000070F) -#define STATUS_CALLBACK_RETURNED_WHILE_IMPERSONATING cpu_to_le32(0xC0000710) -#define STATUS_APC_RETURNED_WHILE_IMPERSONATING cpu_to_le32(0xC0000711) -#define STATUS_PROCESS_IS_PROTECTED cpu_to_le32(0xC0000712) -#define STATUS_MCA_EXCEPTION cpu_to_le32(0xC0000713) -#define STATUS_CERTIFICATE_MAPPING_NOT_UNIQUE cpu_to_le32(0xC0000714) -#define STATUS_SYMLINK_CLASS_DISABLED cpu_to_le32(0xC0000715) -#define STATUS_INVALID_IDN_NORMALIZATION cpu_to_le32(0xC0000716) -#define STATUS_NO_UNICODE_TRANSLATION cpu_to_le32(0xC0000717) -#define STATUS_ALREADY_REGISTERED cpu_to_le32(0xC0000718) -#define STATUS_CONTEXT_MISMATCH cpu_to_le32(0xC0000719) -#define STATUS_PORT_ALREADY_HAS_COMPLETION_LIST cpu_to_le32(0xC000071A) -#define STATUS_CALLBACK_RETURNED_THREAD_PRIORITY cpu_to_le32(0xC000071B) -#define STATUS_INVALID_THREAD cpu_to_le32(0xC000071C) -#define STATUS_CALLBACK_RETURNED_TRANSACTION cpu_to_le32(0xC000071D) -#define STATUS_CALLBACK_RETURNED_LDR_LOCK cpu_to_le32(0xC000071E) -#define STATUS_CALLBACK_RETURNED_LANG cpu_to_le32(0xC000071F) -#define STATUS_CALLBACK_RETURNED_PRI_BACK cpu_to_le32(0xC0000720) -#define STATUS_CALLBACK_RETURNED_THREAD_AFFINITY cpu_to_le32(0xC0000721) -#define STATUS_DISK_REPAIR_DISABLED cpu_to_le32(0xC0000800) -#define STATUS_DS_DOMAIN_RENAME_IN_PROGRESS cpu_to_le32(0xC0000801) -#define STATUS_DISK_QUOTA_EXCEEDED cpu_to_le32(0xC0000802) -#define STATUS_CONTENT_BLOCKED cpu_to_le32(0xC0000804) -#define STATUS_BAD_CLUSTERS cpu_to_le32(0xC0000805) -#define STATUS_VOLUME_DIRTY cpu_to_le32(0xC0000806) -#define STATUS_FILE_CHECKED_OUT cpu_to_le32(0xC0000901) -#define STATUS_CHECKOUT_REQUIRED cpu_to_le32(0xC0000902) -#define STATUS_BAD_FILE_TYPE cpu_to_le32(0xC0000903) -#define STATUS_FILE_TOO_LARGE cpu_to_le32(0xC0000904) -#define STATUS_FORMS_AUTH_REQUIRED cpu_to_le32(0xC0000905) -#define STATUS_VIRUS_INFECTED cpu_to_le32(0xC0000906) -#define STATUS_VIRUS_DELETED cpu_to_le32(0xC0000907) -#define STATUS_BAD_MCFG_TABLE cpu_to_le32(0xC0000908) -#define STATUS_WOW_ASSERTION cpu_to_le32(0xC0009898) -#define STATUS_INVALID_SIGNATURE cpu_to_le32(0xC000A000) -#define STATUS_HMAC_NOT_SUPPORTED cpu_to_le32(0xC000A001) -#define STATUS_IPSEC_QUEUE_OVERFLOW cpu_to_le32(0xC000A010) -#define STATUS_ND_QUEUE_OVERFLOW cpu_to_le32(0xC000A011) -#define STATUS_HOPLIMIT_EXCEEDED cpu_to_le32(0xC000A012) -#define STATUS_PROTOCOL_NOT_SUPPORTED cpu_to_le32(0xC000A013) -#define STATUS_LOST_WRITEBEHIND_DATA_NETWORK_DISCONNECTED cpu_to_le32(0xC000A080) -#define STATUS_LOST_WRITEBEHIND_DATA_NETWORK_SERVER_ERROR cpu_to_le32(0xC000A081) -#define STATUS_LOST_WRITEBEHIND_DATA_LOCAL_DISK_ERROR cpu_to_le32(0xC000A082) -#define STATUS_XML_PARSE_ERROR cpu_to_le32(0xC000A083) -#define STATUS_XMLDSIG_ERROR cpu_to_le32(0xC000A084) -#define STATUS_WRONG_COMPARTMENT cpu_to_le32(0xC000A085) -#define STATUS_AUTHIP_FAILURE cpu_to_le32(0xC000A086) -#define DBG_NO_STATE_CHANGE cpu_to_le32(0xC0010001) -#define DBG_APP_NOT_IDLE cpu_to_le32(0xC0010002) -#define RPC_NT_INVALID_STRING_BINDING cpu_to_le32(0xC0020001) -#define RPC_NT_WRONG_KIND_OF_BINDING cpu_to_le32(0xC0020002) -#define RPC_NT_INVALID_BINDING cpu_to_le32(0xC0020003) -#define RPC_NT_PROTSEQ_NOT_SUPPORTED cpu_to_le32(0xC0020004) -#define RPC_NT_INVALID_RPC_PROTSEQ cpu_to_le32(0xC0020005) -#define RPC_NT_INVALID_STRING_UUID cpu_to_le32(0xC0020006) -#define RPC_NT_INVALID_ENDPOINT_FORMAT cpu_to_le32(0xC0020007) -#define RPC_NT_INVALID_NET_ADDR cpu_to_le32(0xC0020008) -#define RPC_NT_NO_ENDPOINT_FOUND cpu_to_le32(0xC0020009) -#define RPC_NT_INVALID_TIMEOUT cpu_to_le32(0xC002000A) -#define RPC_NT_OBJECT_NOT_FOUND cpu_to_le32(0xC002000B) -#define RPC_NT_ALREADY_REGISTERED cpu_to_le32(0xC002000C) -#define RPC_NT_TYPE_ALREADY_REGISTERED cpu_to_le32(0xC002000D) -#define RPC_NT_ALREADY_LISTENING cpu_to_le32(0xC002000E) -#define RPC_NT_NO_PROTSEQS_REGISTERED cpu_to_le32(0xC002000F) -#define RPC_NT_NOT_LISTENING cpu_to_le32(0xC0020010) -#define RPC_NT_UNKNOWN_MGR_TYPE cpu_to_le32(0xC0020011) -#define RPC_NT_UNKNOWN_IF cpu_to_le32(0xC0020012) -#define RPC_NT_NO_BINDINGS cpu_to_le32(0xC0020013) -#define RPC_NT_NO_PROTSEQS cpu_to_le32(0xC0020014) -#define RPC_NT_CANT_CREATE_ENDPOINT cpu_to_le32(0xC0020015) -#define RPC_NT_OUT_OF_RESOURCES cpu_to_le32(0xC0020016) -#define RPC_NT_SERVER_UNAVAILABLE cpu_to_le32(0xC0020017) -#define RPC_NT_SERVER_TOO_BUSY cpu_to_le32(0xC0020018) -#define RPC_NT_INVALID_NETWORK_OPTIONS cpu_to_le32(0xC0020019) -#define RPC_NT_NO_CALL_ACTIVE cpu_to_le32(0xC002001A) -#define RPC_NT_CALL_FAILED cpu_to_le32(0xC002001B) -#define RPC_NT_CALL_FAILED_DNE cpu_to_le32(0xC002001C) -#define RPC_NT_PROTOCOL_ERROR cpu_to_le32(0xC002001D) -#define RPC_NT_UNSUPPORTED_TRANS_SYN cpu_to_le32(0xC002001F) -#define RPC_NT_UNSUPPORTED_TYPE cpu_to_le32(0xC0020021) -#define RPC_NT_INVALID_TAG cpu_to_le32(0xC0020022) -#define RPC_NT_INVALID_BOUND cpu_to_le32(0xC0020023) -#define RPC_NT_NO_ENTRY_NAME cpu_to_le32(0xC0020024) -#define RPC_NT_INVALID_NAME_SYNTAX cpu_to_le32(0xC0020025) -#define RPC_NT_UNSUPPORTED_NAME_SYNTAX cpu_to_le32(0xC0020026) -#define RPC_NT_UUID_NO_ADDRESS cpu_to_le32(0xC0020028) -#define RPC_NT_DUPLICATE_ENDPOINT cpu_to_le32(0xC0020029) -#define RPC_NT_UNKNOWN_AUTHN_TYPE cpu_to_le32(0xC002002A) -#define RPC_NT_MAX_CALLS_TOO_SMALL cpu_to_le32(0xC002002B) -#define RPC_NT_STRING_TOO_LONG cpu_to_le32(0xC002002C) -#define RPC_NT_PROTSEQ_NOT_FOUND cpu_to_le32(0xC002002D) -#define RPC_NT_PROCNUM_OUT_OF_RANGE cpu_to_le32(0xC002002E) -#define RPC_NT_BINDING_HAS_NO_AUTH cpu_to_le32(0xC002002F) -#define RPC_NT_UNKNOWN_AUTHN_SERVICE cpu_to_le32(0xC0020030) -#define RPC_NT_UNKNOWN_AUTHN_LEVEL cpu_to_le32(0xC0020031) -#define RPC_NT_INVALID_AUTH_IDENTITY cpu_to_le32(0xC0020032) -#define RPC_NT_UNKNOWN_AUTHZ_SERVICE cpu_to_le32(0xC0020033) -#define EPT_NT_INVALID_ENTRY cpu_to_le32(0xC0020034) -#define EPT_NT_CANT_PERFORM_OP cpu_to_le32(0xC0020035) -#define EPT_NT_NOT_REGISTERED cpu_to_le32(0xC0020036) -#define RPC_NT_NOTHING_TO_EXPORT cpu_to_le32(0xC0020037) -#define RPC_NT_INCOMPLETE_NAME cpu_to_le32(0xC0020038) -#define RPC_NT_INVALID_VERS_OPTION cpu_to_le32(0xC0020039) -#define RPC_NT_NO_MORE_MEMBERS cpu_to_le32(0xC002003A) -#define RPC_NT_NOT_ALL_OBJS_UNEXPORTED cpu_to_le32(0xC002003B) -#define RPC_NT_INTERFACE_NOT_FOUND cpu_to_le32(0xC002003C) -#define RPC_NT_ENTRY_ALREADY_EXISTS cpu_to_le32(0xC002003D) -#define RPC_NT_ENTRY_NOT_FOUND cpu_to_le32(0xC002003E) -#define RPC_NT_NAME_SERVICE_UNAVAILABLE cpu_to_le32(0xC002003F) -#define RPC_NT_INVALID_NAF_ID cpu_to_le32(0xC0020040) -#define RPC_NT_CANNOT_SUPPORT cpu_to_le32(0xC0020041) -#define RPC_NT_NO_CONTEXT_AVAILABLE cpu_to_le32(0xC0020042) -#define RPC_NT_INTERNAL_ERROR cpu_to_le32(0xC0020043) -#define RPC_NT_ZERO_DIVIDE cpu_to_le32(0xC0020044) -#define RPC_NT_ADDRESS_ERROR cpu_to_le32(0xC0020045) -#define RPC_NT_FP_DIV_ZERO cpu_to_le32(0xC0020046) -#define RPC_NT_FP_UNDERFLOW cpu_to_le32(0xC0020047) -#define RPC_NT_FP_OVERFLOW cpu_to_le32(0xC0020048) -#define RPC_NT_CALL_IN_PROGRESS cpu_to_le32(0xC0020049) -#define RPC_NT_NO_MORE_BINDINGS cpu_to_le32(0xC002004A) -#define RPC_NT_GROUP_MEMBER_NOT_FOUND cpu_to_le32(0xC002004B) -#define EPT_NT_CANT_CREATE cpu_to_le32(0xC002004C) -#define RPC_NT_INVALID_OBJECT cpu_to_le32(0xC002004D) -#define RPC_NT_NO_INTERFACES cpu_to_le32(0xC002004F) -#define RPC_NT_CALL_CANCELLED cpu_to_le32(0xC0020050) -#define RPC_NT_BINDING_INCOMPLETE cpu_to_le32(0xC0020051) -#define RPC_NT_COMM_FAILURE cpu_to_le32(0xC0020052) -#define RPC_NT_UNSUPPORTED_AUTHN_LEVEL cpu_to_le32(0xC0020053) -#define RPC_NT_NO_PRINC_NAME cpu_to_le32(0xC0020054) -#define RPC_NT_NOT_RPC_ERROR cpu_to_le32(0xC0020055) -#define RPC_NT_SEC_PKG_ERROR cpu_to_le32(0xC0020057) -#define RPC_NT_NOT_CANCELLED cpu_to_le32(0xC0020058) -#define RPC_NT_INVALID_ASYNC_HANDLE cpu_to_le32(0xC0020062) -#define RPC_NT_INVALID_ASYNC_CALL cpu_to_le32(0xC0020063) -#define RPC_NT_PROXY_ACCESS_DENIED cpu_to_le32(0xC0020064) -#define RPC_NT_NO_MORE_ENTRIES cpu_to_le32(0xC0030001) -#define RPC_NT_SS_CHAR_TRANS_OPEN_FAIL cpu_to_le32(0xC0030002) -#define RPC_NT_SS_CHAR_TRANS_SHORT_FILE cpu_to_le32(0xC0030003) -#define RPC_NT_SS_IN_NULL_CONTEXT cpu_to_le32(0xC0030004) -#define RPC_NT_SS_CONTEXT_MISMATCH cpu_to_le32(0xC0030005) -#define RPC_NT_SS_CONTEXT_DAMAGED cpu_to_le32(0xC0030006) -#define RPC_NT_SS_HANDLES_MISMATCH cpu_to_le32(0xC0030007) -#define RPC_NT_SS_CANNOT_GET_CALL_HANDLE cpu_to_le32(0xC0030008) -#define RPC_NT_NULL_REF_POINTER cpu_to_le32(0xC0030009) -#define RPC_NT_ENUM_VALUE_OUT_OF_RANGE cpu_to_le32(0xC003000A) -#define RPC_NT_BYTE_COUNT_TOO_SMALL cpu_to_le32(0xC003000B) -#define RPC_NT_BAD_STUB_DATA cpu_to_le32(0xC003000C) -#define RPC_NT_INVALID_ES_ACTION cpu_to_le32(0xC0030059) -#define RPC_NT_WRONG_ES_VERSION cpu_to_le32(0xC003005A) -#define RPC_NT_WRONG_STUB_VERSION cpu_to_le32(0xC003005B) -#define RPC_NT_INVALID_PIPE_OBJECT cpu_to_le32(0xC003005C) -#define RPC_NT_INVALID_PIPE_OPERATION cpu_to_le32(0xC003005D) -#define RPC_NT_WRONG_PIPE_VERSION cpu_to_le32(0xC003005E) -#define RPC_NT_PIPE_CLOSED cpu_to_le32(0xC003005F) -#define RPC_NT_PIPE_DISCIPLINE_ERROR cpu_to_le32(0xC0030060) -#define RPC_NT_PIPE_EMPTY cpu_to_le32(0xC0030061) -#define STATUS_PNP_BAD_MPS_TABLE cpu_to_le32(0xC0040035) -#define STATUS_PNP_TRANSLATION_FAILED cpu_to_le32(0xC0040036) -#define STATUS_PNP_IRQ_TRANSLATION_FAILED cpu_to_le32(0xC0040037) -#define STATUS_PNP_INVALID_ID cpu_to_le32(0xC0040038) -#define STATUS_IO_REISSUE_AS_CACHED cpu_to_le32(0xC0040039) -#define STATUS_CTX_WINSTATION_NAME_INVALID cpu_to_le32(0xC00A0001) -#define STATUS_CTX_INVALID_PD cpu_to_le32(0xC00A0002) -#define STATUS_CTX_PD_NOT_FOUND cpu_to_le32(0xC00A0003) -#define STATUS_CTX_CLOSE_PENDING cpu_to_le32(0xC00A0006) -#define STATUS_CTX_NO_OUTBUF cpu_to_le32(0xC00A0007) -#define STATUS_CTX_MODEM_INF_NOT_FOUND cpu_to_le32(0xC00A0008) -#define STATUS_CTX_INVALID_MODEMNAME cpu_to_le32(0xC00A0009) -#define STATUS_CTX_RESPONSE_ERROR cpu_to_le32(0xC00A000A) -#define STATUS_CTX_MODEM_RESPONSE_TIMEOUT cpu_to_le32(0xC00A000B) -#define STATUS_CTX_MODEM_RESPONSE_NO_CARRIER cpu_to_le32(0xC00A000C) -#define STATUS_CTX_MODEM_RESPONSE_NO_DIALTONE cpu_to_le32(0xC00A000D) -#define STATUS_CTX_MODEM_RESPONSE_BUSY cpu_to_le32(0xC00A000E) -#define STATUS_CTX_MODEM_RESPONSE_VOICE cpu_to_le32(0xC00A000F) -#define STATUS_CTX_TD_ERROR cpu_to_le32(0xC00A0010) -#define STATUS_CTX_LICENSE_CLIENT_INVALID cpu_to_le32(0xC00A0012) -#define STATUS_CTX_LICENSE_NOT_AVAILABLE cpu_to_le32(0xC00A0013) -#define STATUS_CTX_LICENSE_EXPIRED cpu_to_le32(0xC00A0014) -#define STATUS_CTX_WINSTATION_NOT_FOUND cpu_to_le32(0xC00A0015) -#define STATUS_CTX_WINSTATION_NAME_COLLISION cpu_to_le32(0xC00A0016) -#define STATUS_CTX_WINSTATION_BUSY cpu_to_le32(0xC00A0017) -#define STATUS_CTX_BAD_VIDEO_MODE cpu_to_le32(0xC00A0018) -#define STATUS_CTX_GRAPHICS_INVALID cpu_to_le32(0xC00A0022) -#define STATUS_CTX_NOT_CONSOLE cpu_to_le32(0xC00A0024) -#define STATUS_CTX_CLIENT_QUERY_TIMEOUT cpu_to_le32(0xC00A0026) -#define STATUS_CTX_CONSOLE_DISCONNECT cpu_to_le32(0xC00A0027) -#define STATUS_CTX_CONSOLE_CONNECT cpu_to_le32(0xC00A0028) -#define STATUS_CTX_SHADOW_DENIED cpu_to_le32(0xC00A002A) -#define STATUS_CTX_WINSTATION_ACCESS_DENIED cpu_to_le32(0xC00A002B) -#define STATUS_CTX_INVALID_WD cpu_to_le32(0xC00A002E) -#define STATUS_CTX_WD_NOT_FOUND cpu_to_le32(0xC00A002F) -#define STATUS_CTX_SHADOW_INVALID cpu_to_le32(0xC00A0030) -#define STATUS_CTX_SHADOW_DISABLED cpu_to_le32(0xC00A0031) -#define STATUS_RDP_PROTOCOL_ERROR cpu_to_le32(0xC00A0032) -#define STATUS_CTX_CLIENT_LICENSE_NOT_SET cpu_to_le32(0xC00A0033) -#define STATUS_CTX_CLIENT_LICENSE_IN_USE cpu_to_le32(0xC00A0034) -#define STATUS_CTX_SHADOW_ENDED_BY_MODE_CHANGE cpu_to_le32(0xC00A0035) -#define STATUS_CTX_SHADOW_NOT_RUNNING cpu_to_le32(0xC00A0036) -#define STATUS_CTX_LOGON_DISABLED cpu_to_le32(0xC00A0037) -#define STATUS_CTX_SECURITY_LAYER_ERROR cpu_to_le32(0xC00A0038) -#define STATUS_TS_INCOMPATIBLE_SESSIONS cpu_to_le32(0xC00A0039) -#define STATUS_MUI_FILE_NOT_FOUND cpu_to_le32(0xC00B0001) -#define STATUS_MUI_INVALID_FILE cpu_to_le32(0xC00B0002) -#define STATUS_MUI_INVALID_RC_CONFIG cpu_to_le32(0xC00B0003) -#define STATUS_MUI_INVALID_LOCALE_NAME cpu_to_le32(0xC00B0004) -#define STATUS_MUI_INVALID_ULTIMATEFALLBACK_NAME cpu_to_le32(0xC00B0005) -#define STATUS_MUI_FILE_NOT_LOADED cpu_to_le32(0xC00B0006) -#define STATUS_RESOURCE_ENUM_USER_STOP cpu_to_le32(0xC00B0007) -#define STATUS_CLUSTER_INVALID_NODE cpu_to_le32(0xC0130001) -#define STATUS_CLUSTER_NODE_EXISTS cpu_to_le32(0xC0130002) -#define STATUS_CLUSTER_JOIN_IN_PROGRESS cpu_to_le32(0xC0130003) -#define STATUS_CLUSTER_NODE_NOT_FOUND cpu_to_le32(0xC0130004) -#define STATUS_CLUSTER_LOCAL_NODE_NOT_FOUND cpu_to_le32(0xC0130005) -#define STATUS_CLUSTER_NETWORK_EXISTS cpu_to_le32(0xC0130006) -#define STATUS_CLUSTER_NETWORK_NOT_FOUND cpu_to_le32(0xC0130007) -#define STATUS_CLUSTER_NETINTERFACE_EXISTS cpu_to_le32(0xC0130008) -#define STATUS_CLUSTER_NETINTERFACE_NOT_FOUND cpu_to_le32(0xC0130009) -#define STATUS_CLUSTER_INVALID_REQUEST cpu_to_le32(0xC013000A) -#define STATUS_CLUSTER_INVALID_NETWORK_PROVIDER cpu_to_le32(0xC013000B) -#define STATUS_CLUSTER_NODE_DOWN cpu_to_le32(0xC013000C) -#define STATUS_CLUSTER_NODE_UNREACHABLE cpu_to_le32(0xC013000D) -#define STATUS_CLUSTER_NODE_NOT_MEMBER cpu_to_le32(0xC013000E) -#define STATUS_CLUSTER_JOIN_NOT_IN_PROGRESS cpu_to_le32(0xC013000F) -#define STATUS_CLUSTER_INVALID_NETWORK cpu_to_le32(0xC0130010) -#define STATUS_CLUSTER_NO_NET_ADAPTERS cpu_to_le32(0xC0130011) -#define STATUS_CLUSTER_NODE_UP cpu_to_le32(0xC0130012) -#define STATUS_CLUSTER_NODE_PAUSED cpu_to_le32(0xC0130013) -#define STATUS_CLUSTER_NODE_NOT_PAUSED cpu_to_le32(0xC0130014) -#define STATUS_CLUSTER_NO_SECURITY_CONTEXT cpu_to_le32(0xC0130015) -#define STATUS_CLUSTER_NETWORK_NOT_INTERNAL cpu_to_le32(0xC0130016) -#define STATUS_CLUSTER_POISONED cpu_to_le32(0xC0130017) -#define STATUS_ACPI_INVALID_OPCODE cpu_to_le32(0xC0140001) -#define STATUS_ACPI_STACK_OVERFLOW cpu_to_le32(0xC0140002) -#define STATUS_ACPI_ASSERT_FAILED cpu_to_le32(0xC0140003) -#define STATUS_ACPI_INVALID_INDEX cpu_to_le32(0xC0140004) -#define STATUS_ACPI_INVALID_ARGUMENT cpu_to_le32(0xC0140005) -#define STATUS_ACPI_FATAL cpu_to_le32(0xC0140006) -#define STATUS_ACPI_INVALID_SUPERNAME cpu_to_le32(0xC0140007) -#define STATUS_ACPI_INVALID_ARGTYPE cpu_to_le32(0xC0140008) -#define STATUS_ACPI_INVALID_OBJTYPE cpu_to_le32(0xC0140009) -#define STATUS_ACPI_INVALID_TARGETTYPE cpu_to_le32(0xC014000A) -#define STATUS_ACPI_INCORRECT_ARGUMENT_COUNT cpu_to_le32(0xC014000B) -#define STATUS_ACPI_ADDRESS_NOT_MAPPED cpu_to_le32(0xC014000C) -#define STATUS_ACPI_INVALID_EVENTTYPE cpu_to_le32(0xC014000D) -#define STATUS_ACPI_HANDLER_COLLISION cpu_to_le32(0xC014000E) -#define STATUS_ACPI_INVALID_DATA cpu_to_le32(0xC014000F) -#define STATUS_ACPI_INVALID_REGION cpu_to_le32(0xC0140010) -#define STATUS_ACPI_INVALID_ACCESS_SIZE cpu_to_le32(0xC0140011) -#define STATUS_ACPI_ACQUIRE_GLOBAL_LOCK cpu_to_le32(0xC0140012) -#define STATUS_ACPI_ALREADY_INITIALIZED cpu_to_le32(0xC0140013) -#define STATUS_ACPI_NOT_INITIALIZED cpu_to_le32(0xC0140014) -#define STATUS_ACPI_INVALID_MUTEX_LEVEL cpu_to_le32(0xC0140015) -#define STATUS_ACPI_MUTEX_NOT_OWNED cpu_to_le32(0xC0140016) -#define STATUS_ACPI_MUTEX_NOT_OWNER cpu_to_le32(0xC0140017) -#define STATUS_ACPI_RS_ACCESS cpu_to_le32(0xC0140018) -#define STATUS_ACPI_INVALID_TABLE cpu_to_le32(0xC0140019) -#define STATUS_ACPI_REG_HANDLER_FAILED cpu_to_le32(0xC0140020) -#define STATUS_ACPI_POWER_REQUEST_FAILED cpu_to_le32(0xC0140021) -#define STATUS_SXS_SECTION_NOT_FOUND cpu_to_le32(0xC0150001) -#define STATUS_SXS_CANT_GEN_ACTCTX cpu_to_le32(0xC0150002) -#define STATUS_SXS_INVALID_ACTCTXDATA_FORMAT cpu_to_le32(0xC0150003) -#define STATUS_SXS_ASSEMBLY_NOT_FOUND cpu_to_le32(0xC0150004) -#define STATUS_SXS_MANIFEST_FORMAT_ERROR cpu_to_le32(0xC0150005) -#define STATUS_SXS_MANIFEST_PARSE_ERROR cpu_to_le32(0xC0150006) -#define STATUS_SXS_ACTIVATION_CONTEXT_DISABLED cpu_to_le32(0xC0150007) -#define STATUS_SXS_KEY_NOT_FOUND cpu_to_le32(0xC0150008) -#define STATUS_SXS_VERSION_CONFLICT cpu_to_le32(0xC0150009) -#define STATUS_SXS_WRONG_SECTION_TYPE cpu_to_le32(0xC015000A) -#define STATUS_SXS_THREAD_QUERIES_DISABLED cpu_to_le32(0xC015000B) -#define STATUS_SXS_ASSEMBLY_MISSING cpu_to_le32(0xC015000C) -#define STATUS_SXS_PROCESS_DEFAULT_ALREADY_SET cpu_to_le32(0xC015000E) -#define STATUS_SXS_EARLY_DEACTIVATION cpu_to_le32(0xC015000F) -#define STATUS_SXS_INVALID_DEACTIVATION cpu_to_le32(0xC0150010) -#define STATUS_SXS_MULTIPLE_DEACTIVATION cpu_to_le32(0xC0150011) -#define STATUS_SXS_SYSTEM_DEFAULT_ACTIVATION_CONTEXT_EMPTY cpu_to_le32(0xC0150012) -#define STATUS_SXS_PROCESS_TERMINATION_REQUESTED cpu_to_le32(0xC0150013) -#define STATUS_SXS_CORRUPT_ACTIVATION_STACK cpu_to_le32(0xC0150014) -#define STATUS_SXS_CORRUPTION cpu_to_le32(0xC0150015) -#define STATUS_SXS_INVALID_IDENTITY_ATTRIBUTE_VALUE cpu_to_le32(0xC0150016) -#define STATUS_SXS_INVALID_IDENTITY_ATTRIBUTE_NAME cpu_to_le32(0xC0150017) -#define STATUS_SXS_IDENTITY_DUPLICATE_ATTRIBUTE cpu_to_le32(0xC0150018) -#define STATUS_SXS_IDENTITY_PARSE_ERROR cpu_to_le32(0xC0150019) -#define STATUS_SXS_COMPONENT_STORE_CORRUPT cpu_to_le32(0xC015001A) -#define STATUS_SXS_FILE_HASH_MISMATCH cpu_to_le32(0xC015001B) -#define STATUS_SXS_MANIFEST_IDENTITY_SAME_BUT_CONTENTS_DIFFERENT cpu_to_le32(0xC015001C) -#define STATUS_SXS_IDENTITIES_DIFFERENT cpu_to_le32(0xC015001D) -#define STATUS_SXS_ASSEMBLY_IS_NOT_A_DEPLOYMENT cpu_to_le32(0xC015001E) -#define STATUS_SXS_FILE_NOT_PART_OF_ASSEMBLY cpu_to_le32(0xC015001F) -#define STATUS_ADVANCED_INSTALLER_FAILED cpu_to_le32(0xC0150020) -#define STATUS_XML_ENCODING_MISMATCH cpu_to_le32(0xC0150021) -#define STATUS_SXS_MANIFEST_TOO_BIG cpu_to_le32(0xC0150022) -#define STATUS_SXS_SETTING_NOT_REGISTERED cpu_to_le32(0xC0150023) -#define STATUS_SXS_TRANSACTION_CLOSURE_INCOMPLETE cpu_to_le32(0xC0150024) -#define STATUS_SMI_PRIMITIVE_INSTALLER_FAILED cpu_to_le32(0xC0150025) -#define STATUS_GENERIC_COMMAND_FAILED cpu_to_le32(0xC0150026) -#define STATUS_SXS_FILE_HASH_MISSING cpu_to_le32(0xC0150027) -#define STATUS_TRANSACTIONAL_CONFLICT cpu_to_le32(0xC0190001) -#define STATUS_INVALID_TRANSACTION cpu_to_le32(0xC0190002) -#define STATUS_TRANSACTION_NOT_ACTIVE cpu_to_le32(0xC0190003) -#define STATUS_TM_INITIALIZATION_FAILED cpu_to_le32(0xC0190004) -#define STATUS_RM_NOT_ACTIVE cpu_to_le32(0xC0190005) -#define STATUS_RM_METADATA_CORRUPT cpu_to_le32(0xC0190006) -#define STATUS_TRANSACTION_NOT_JOINED cpu_to_le32(0xC0190007) -#define STATUS_DIRECTORY_NOT_RM cpu_to_le32(0xC0190008) -#define STATUS_TRANSACTIONS_UNSUPPORTED_REMOTE cpu_to_le32(0xC019000A) -#define STATUS_LOG_RESIZE_INVALID_SIZE cpu_to_le32(0xC019000B) -#define STATUS_REMOTE_FILE_VERSION_MISMATCH cpu_to_le32(0xC019000C) -#define STATUS_CRM_PROTOCOL_ALREADY_EXISTS cpu_to_le32(0xC019000F) -#define STATUS_TRANSACTION_PROPAGATION_FAILED cpu_to_le32(0xC0190010) -#define STATUS_CRM_PROTOCOL_NOT_FOUND cpu_to_le32(0xC0190011) -#define STATUS_TRANSACTION_SUPERIOR_EXISTS cpu_to_le32(0xC0190012) -#define STATUS_TRANSACTION_REQUEST_NOT_VALID cpu_to_le32(0xC0190013) -#define STATUS_TRANSACTION_NOT_REQUESTED cpu_to_le32(0xC0190014) -#define STATUS_TRANSACTION_ALREADY_ABORTED cpu_to_le32(0xC0190015) -#define STATUS_TRANSACTION_ALREADY_COMMITTED cpu_to_le32(0xC0190016) -#define STATUS_TRANSACTION_INVALID_MARSHALL_BUFFER cpu_to_le32(0xC0190017) -#define STATUS_CURRENT_TRANSACTION_NOT_VALID cpu_to_le32(0xC0190018) -#define STATUS_LOG_GROWTH_FAILED cpu_to_le32(0xC0190019) -#define STATUS_OBJECT_NO_LONGER_EXISTS cpu_to_le32(0xC0190021) -#define STATUS_STREAM_MINIVERSION_NOT_FOUND cpu_to_le32(0xC0190022) -#define STATUS_STREAM_MINIVERSION_NOT_VALID cpu_to_le32(0xC0190023) -#define STATUS_MINIVERSION_INACCESSIBLE_FROM_SPECIFIED_TRANSACTION cpu_to_le32(0xC0190024) -#define STATUS_CANT_OPEN_MINIVERSION_WITH_MODIFY_INTENT cpu_to_le32(0xC0190025) -#define STATUS_CANT_CREATE_MORE_STREAM_MINIVERSIONS cpu_to_le32(0xC0190026) -#define STATUS_HANDLE_NO_LONGER_VALID cpu_to_le32(0xC0190028) -#define STATUS_LOG_CORRUPTION_DETECTED cpu_to_le32(0xC0190030) -#define STATUS_RM_DISCONNECTED cpu_to_le32(0xC0190032) -#define STATUS_ENLISTMENT_NOT_SUPERIOR cpu_to_le32(0xC0190033) -#define STATUS_FILE_IDENTITY_NOT_PERSISTENT cpu_to_le32(0xC0190036) -#define STATUS_CANT_BREAK_TRANSACTIONAL_DEPENDENCY cpu_to_le32(0xC0190037) -#define STATUS_CANT_CROSS_RM_BOUNDARY cpu_to_le32(0xC0190038) -#define STATUS_TXF_DIR_NOT_EMPTY cpu_to_le32(0xC0190039) -#define STATUS_INDOUBT_TRANSACTIONS_EXIST cpu_to_le32(0xC019003A) -#define STATUS_TM_VOLATILE cpu_to_le32(0xC019003B) -#define STATUS_ROLLBACK_TIMER_EXPIRED cpu_to_le32(0xC019003C) -#define STATUS_TXF_ATTRIBUTE_CORRUPT cpu_to_le32(0xC019003D) -#define STATUS_EFS_NOT_ALLOWED_IN_TRANSACTION cpu_to_le32(0xC019003E) -#define STATUS_TRANSACTIONAL_OPEN_NOT_ALLOWED cpu_to_le32(0xC019003F) -#define STATUS_TRANSACTED_MAPPING_UNSUPPORTED_REMOTE cpu_to_le32(0xC0190040) -#define STATUS_TRANSACTION_REQUIRED_PROMOTION cpu_to_le32(0xC0190043) -#define STATUS_CANNOT_EXECUTE_FILE_IN_TRANSACTION cpu_to_le32(0xC0190044) -#define STATUS_TRANSACTIONS_NOT_FROZEN cpu_to_le32(0xC0190045) -#define STATUS_TRANSACTION_FREEZE_IN_PROGRESS cpu_to_le32(0xC0190046) -#define STATUS_NOT_SNAPSHOT_VOLUME cpu_to_le32(0xC0190047) -#define STATUS_NO_SAVEPOINT_WITH_OPEN_FILES cpu_to_le32(0xC0190048) -#define STATUS_SPARSE_NOT_ALLOWED_IN_TRANSACTION cpu_to_le32(0xC0190049) -#define STATUS_TM_IDENTITY_MISMATCH cpu_to_le32(0xC019004A) -#define STATUS_FLOATED_SECTION cpu_to_le32(0xC019004B) -#define STATUS_CANNOT_ACCEPT_TRANSACTED_WORK cpu_to_le32(0xC019004C) -#define STATUS_CANNOT_ABORT_TRANSACTIONS cpu_to_le32(0xC019004D) -#define STATUS_TRANSACTION_NOT_FOUND cpu_to_le32(0xC019004E) -#define STATUS_RESOURCEMANAGER_NOT_FOUND cpu_to_le32(0xC019004F) -#define STATUS_ENLISTMENT_NOT_FOUND cpu_to_le32(0xC0190050) -#define STATUS_TRANSACTIONMANAGER_NOT_FOUND cpu_to_le32(0xC0190051) -#define STATUS_TRANSACTIONMANAGER_NOT_ONLINE cpu_to_le32(0xC0190052) -#define STATUS_TRANSACTIONMANAGER_RECOVERY_NAME_COLLISION cpu_to_le32(0xC0190053) -#define STATUS_TRANSACTION_NOT_ROOT cpu_to_le32(0xC0190054) -#define STATUS_TRANSACTION_OBJECT_EXPIRED cpu_to_le32(0xC0190055) -#define STATUS_COMPRESSION_NOT_ALLOWED_IN_TRANSACTION cpu_to_le32(0xC0190056) -#define STATUS_TRANSACTION_RESPONSE_NOT_ENLISTED cpu_to_le32(0xC0190057) -#define STATUS_TRANSACTION_RECORD_TOO_LONG cpu_to_le32(0xC0190058) -#define STATUS_NO_LINK_TRACKING_IN_TRANSACTION cpu_to_le32(0xC0190059) -#define STATUS_OPERATION_NOT_SUPPORTED_IN_TRANSACTION cpu_to_le32(0xC019005A) -#define STATUS_TRANSACTION_INTEGRITY_VIOLATED cpu_to_le32(0xC019005B) -#define STATUS_LOG_SECTOR_INVALID cpu_to_le32(0xC01A0001) -#define STATUS_LOG_SECTOR_PARITY_INVALID cpu_to_le32(0xC01A0002) -#define STATUS_LOG_SECTOR_REMAPPED cpu_to_le32(0xC01A0003) -#define STATUS_LOG_BLOCK_INCOMPLETE cpu_to_le32(0xC01A0004) -#define STATUS_LOG_INVALID_RANGE cpu_to_le32(0xC01A0005) -#define STATUS_LOG_BLOCKS_EXHAUSTED cpu_to_le32(0xC01A0006) -#define STATUS_LOG_READ_CONTEXT_INVALID cpu_to_le32(0xC01A0007) -#define STATUS_LOG_RESTART_INVALID cpu_to_le32(0xC01A0008) -#define STATUS_LOG_BLOCK_VERSION cpu_to_le32(0xC01A0009) -#define STATUS_LOG_BLOCK_INVALID cpu_to_le32(0xC01A000A) -#define STATUS_LOG_READ_MODE_INVALID cpu_to_le32(0xC01A000B) -#define STATUS_LOG_METADATA_CORRUPT cpu_to_le32(0xC01A000D) -#define STATUS_LOG_METADATA_INVALID cpu_to_le32(0xC01A000E) -#define STATUS_LOG_METADATA_INCONSISTENT cpu_to_le32(0xC01A000F) -#define STATUS_LOG_RESERVATION_INVALID cpu_to_le32(0xC01A0010) -#define STATUS_LOG_CANT_DELETE cpu_to_le32(0xC01A0011) -#define STATUS_LOG_CONTAINER_LIMIT_EXCEEDED cpu_to_le32(0xC01A0012) -#define STATUS_LOG_START_OF_LOG cpu_to_le32(0xC01A0013) -#define STATUS_LOG_POLICY_ALREADY_INSTALLED cpu_to_le32(0xC01A0014) -#define STATUS_LOG_POLICY_NOT_INSTALLED cpu_to_le32(0xC01A0015) -#define STATUS_LOG_POLICY_INVALID cpu_to_le32(0xC01A0016) -#define STATUS_LOG_POLICY_CONFLICT cpu_to_le32(0xC01A0017) -#define STATUS_LOG_PINNED_ARCHIVE_TAIL cpu_to_le32(0xC01A0018) -#define STATUS_LOG_RECORD_NONEXISTENT cpu_to_le32(0xC01A0019) -#define STATUS_LOG_RECORDS_RESERVED_INVALID cpu_to_le32(0xC01A001A) -#define STATUS_LOG_SPACE_RESERVED_INVALID cpu_to_le32(0xC01A001B) -#define STATUS_LOG_TAIL_INVALID cpu_to_le32(0xC01A001C) -#define STATUS_LOG_FULL cpu_to_le32(0xC01A001D) -#define STATUS_LOG_MULTIPLEXED cpu_to_le32(0xC01A001E) -#define STATUS_LOG_DEDICATED cpu_to_le32(0xC01A001F) -#define STATUS_LOG_ARCHIVE_NOT_IN_PROGRESS cpu_to_le32(0xC01A0020) -#define STATUS_LOG_ARCHIVE_IN_PROGRESS cpu_to_le32(0xC01A0021) -#define STATUS_LOG_EPHEMERAL cpu_to_le32(0xC01A0022) -#define STATUS_LOG_NOT_ENOUGH_CONTAINERS cpu_to_le32(0xC01A0023) -#define STATUS_LOG_CLIENT_ALREADY_REGISTERED cpu_to_le32(0xC01A0024) -#define STATUS_LOG_CLIENT_NOT_REGISTERED cpu_to_le32(0xC01A0025) -#define STATUS_LOG_FULL_HANDLER_IN_PROGRESS cpu_to_le32(0xC01A0026) -#define STATUS_LOG_CONTAINER_READ_FAILED cpu_to_le32(0xC01A0027) -#define STATUS_LOG_CONTAINER_WRITE_FAILED cpu_to_le32(0xC01A0028) -#define STATUS_LOG_CONTAINER_OPEN_FAILED cpu_to_le32(0xC01A0029) -#define STATUS_LOG_CONTAINER_STATE_INVALID cpu_to_le32(0xC01A002A) -#define STATUS_LOG_STATE_INVALID cpu_to_le32(0xC01A002B) -#define STATUS_LOG_PINNED cpu_to_le32(0xC01A002C) -#define STATUS_LOG_METADATA_FLUSH_FAILED cpu_to_le32(0xC01A002D) -#define STATUS_LOG_INCONSISTENT_SECURITY cpu_to_le32(0xC01A002E) -#define STATUS_LOG_APPENDED_FLUSH_FAILED cpu_to_le32(0xC01A002F) -#define STATUS_LOG_PINNED_RESERVATION cpu_to_le32(0xC01A0030) -#define STATUS_VIDEO_HUNG_DISPLAY_DRIVER_THREAD cpu_to_le32(0xC01B00EA) -#define STATUS_FLT_NO_HANDLER_DEFINED cpu_to_le32(0xC01C0001) -#define STATUS_FLT_CONTEXT_ALREADY_DEFINED cpu_to_le32(0xC01C0002) -#define STATUS_FLT_INVALID_ASYNCHRONOUS_REQUEST cpu_to_le32(0xC01C0003) -#define STATUS_FLT_DISALLOW_FAST_IO cpu_to_le32(0xC01C0004) -#define STATUS_FLT_INVALID_NAME_REQUEST cpu_to_le32(0xC01C0005) -#define STATUS_FLT_NOT_SAFE_TO_POST_OPERATION cpu_to_le32(0xC01C0006) -#define STATUS_FLT_NOT_INITIALIZED cpu_to_le32(0xC01C0007) -#define STATUS_FLT_FILTER_NOT_READY cpu_to_le32(0xC01C0008) -#define STATUS_FLT_POST_OPERATION_CLEANUP cpu_to_le32(0xC01C0009) -#define STATUS_FLT_INTERNAL_ERROR cpu_to_le32(0xC01C000A) -#define STATUS_FLT_DELETING_OBJECT cpu_to_le32(0xC01C000B) -#define STATUS_FLT_MUST_BE_NONPAGED_POOL cpu_to_le32(0xC01C000C) -#define STATUS_FLT_DUPLICATE_ENTRY cpu_to_le32(0xC01C000D) -#define STATUS_FLT_CBDQ_DISABLED cpu_to_le32(0xC01C000E) -#define STATUS_FLT_DO_NOT_ATTACH cpu_to_le32(0xC01C000F) -#define STATUS_FLT_DO_NOT_DETACH cpu_to_le32(0xC01C0010) -#define STATUS_FLT_INSTANCE_ALTITUDE_COLLISION cpu_to_le32(0xC01C0011) -#define STATUS_FLT_INSTANCE_NAME_COLLISION cpu_to_le32(0xC01C0012) -#define STATUS_FLT_FILTER_NOT_FOUND cpu_to_le32(0xC01C0013) -#define STATUS_FLT_VOLUME_NOT_FOUND cpu_to_le32(0xC01C0014) -#define STATUS_FLT_INSTANCE_NOT_FOUND cpu_to_le32(0xC01C0015) -#define STATUS_FLT_CONTEXT_ALLOCATION_NOT_FOUND cpu_to_le32(0xC01C0016) -#define STATUS_FLT_INVALID_CONTEXT_REGISTRATION cpu_to_le32(0xC01C0017) -#define STATUS_FLT_NAME_CACHE_MISS cpu_to_le32(0xC01C0018) -#define STATUS_FLT_NO_DEVICE_OBJECT cpu_to_le32(0xC01C0019) -#define STATUS_FLT_VOLUME_ALREADY_MOUNTED cpu_to_le32(0xC01C001A) -#define STATUS_FLT_ALREADY_ENLISTED cpu_to_le32(0xC01C001B) -#define STATUS_FLT_CONTEXT_ALREADY_LINKED cpu_to_le32(0xC01C001C) -#define STATUS_FLT_NO_WAITER_FOR_REPLY cpu_to_le32(0xC01C0020) -#define STATUS_MONITOR_NO_DESCRIPTOR cpu_to_le32(0xC01D0001) -#define STATUS_MONITOR_UNKNOWN_DESCRIPTOR_FORMAT cpu_to_le32(0xC01D0002) -#define STATUS_MONITOR_INVALID_DESCRIPTOR_CHECKSUM cpu_to_le32(0xC01D0003) -#define STATUS_MONITOR_INVALID_STANDARD_TIMING_BLOCK cpu_to_le32(0xC01D0004) -#define STATUS_MONITOR_WMI_DATABLOCK_REGISTRATION_FAILED cpu_to_le32(0xC01D0005) -#define STATUS_MONITOR_INVALID_SERIAL_NUMBER_MONDSC_BLOCK cpu_to_le32(0xC01D0006) -#define STATUS_MONITOR_INVALID_USER_FRIENDLY_MONDSC_BLOCK cpu_to_le32(0xC01D0007) -#define STATUS_MONITOR_NO_MORE_DESCRIPTOR_DATA cpu_to_le32(0xC01D0008) -#define STATUS_MONITOR_INVALID_DETAILED_TIMING_BLOCK cpu_to_le32(0xC01D0009) -#define STATUS_GRAPHICS_NOT_EXCLUSIVE_MODE_OWNER cpu_to_le32(0xC01E0000) -#define STATUS_GRAPHICS_INSUFFICIENT_DMA_BUFFER cpu_to_le32(0xC01E0001) -#define STATUS_GRAPHICS_INVALID_DISPLAY_ADAPTER cpu_to_le32(0xC01E0002) -#define STATUS_GRAPHICS_ADAPTER_WAS_RESET cpu_to_le32(0xC01E0003) -#define STATUS_GRAPHICS_INVALID_DRIVER_MODEL cpu_to_le32(0xC01E0004) -#define STATUS_GRAPHICS_PRESENT_MODE_CHANGED cpu_to_le32(0xC01E0005) -#define STATUS_GRAPHICS_PRESENT_OCCLUDED cpu_to_le32(0xC01E0006) -#define STATUS_GRAPHICS_PRESENT_DENIED cpu_to_le32(0xC01E0007) -#define STATUS_GRAPHICS_CANNOTCOLORCONVERT cpu_to_le32(0xC01E0008) -#define STATUS_GRAPHICS_NO_VIDEO_MEMORY cpu_to_le32(0xC01E0100) -#define STATUS_GRAPHICS_CANT_LOCK_MEMORY cpu_to_le32(0xC01E0101) -#define STATUS_GRAPHICS_ALLOCATION_BUSY cpu_to_le32(0xC01E0102) -#define STATUS_GRAPHICS_TOO_MANY_REFERENCES cpu_to_le32(0xC01E0103) -#define STATUS_GRAPHICS_TRY_AGAIN_LATER cpu_to_le32(0xC01E0104) -#define STATUS_GRAPHICS_TRY_AGAIN_NOW cpu_to_le32(0xC01E0105) -#define STATUS_GRAPHICS_ALLOCATION_INVALID cpu_to_le32(0xC01E0106) -#define STATUS_GRAPHICS_UNSWIZZLING_APERTURE_UNAVAILABLE cpu_to_le32(0xC01E0107) -#define STATUS_GRAPHICS_UNSWIZZLING_APERTURE_UNSUPPORTED cpu_to_le32(0xC01E0108) -#define STATUS_GRAPHICS_CANT_EVICT_PINNED_ALLOCATION cpu_to_le32(0xC01E0109) -#define STATUS_GRAPHICS_INVALID_ALLOCATION_USAGE cpu_to_le32(0xC01E0110) -#define STATUS_GRAPHICS_CANT_RENDER_LOCKED_ALLOCATION cpu_to_le32(0xC01E0111) -#define STATUS_GRAPHICS_ALLOCATION_CLOSED cpu_to_le32(0xC01E0112) -#define STATUS_GRAPHICS_INVALID_ALLOCATION_INSTANCE cpu_to_le32(0xC01E0113) -#define STATUS_GRAPHICS_INVALID_ALLOCATION_HANDLE cpu_to_le32(0xC01E0114) -#define STATUS_GRAPHICS_WRONG_ALLOCATION_DEVICE cpu_to_le32(0xC01E0115) -#define STATUS_GRAPHICS_ALLOCATION_CONTENT_LOST cpu_to_le32(0xC01E0116) -#define STATUS_GRAPHICS_GPU_EXCEPTION_ON_DEVICE cpu_to_le32(0xC01E0200) -#define STATUS_GRAPHICS_INVALID_VIDPN_TOPOLOGY cpu_to_le32(0xC01E0300) -#define STATUS_GRAPHICS_VIDPN_TOPOLOGY_NOT_SUPPORTED cpu_to_le32(0xC01E0301) -#define STATUS_GRAPHICS_VIDPN_TOPOLOGY_CURRENTLY_NOT_SUPPORTED cpu_to_le32(0xC01E0302) -#define STATUS_GRAPHICS_INVALID_VIDPN cpu_to_le32(0xC01E0303) -#define STATUS_GRAPHICS_INVALID_VIDEO_PRESENT_SOURCE cpu_to_le32(0xC01E0304) -#define STATUS_GRAPHICS_INVALID_VIDEO_PRESENT_TARGET cpu_to_le32(0xC01E0305) -#define STATUS_GRAPHICS_VIDPN_MODALITY_NOT_SUPPORTED cpu_to_le32(0xC01E0306) -#define STATUS_GRAPHICS_INVALID_VIDPN_SOURCEMODESET cpu_to_le32(0xC01E0308) -#define STATUS_GRAPHICS_INVALID_VIDPN_TARGETMODESET cpu_to_le32(0xC01E0309) -#define STATUS_GRAPHICS_INVALID_FREQUENCY cpu_to_le32(0xC01E030A) -#define STATUS_GRAPHICS_INVALID_ACTIVE_REGION cpu_to_le32(0xC01E030B) -#define STATUS_GRAPHICS_INVALID_TOTAL_REGION cpu_to_le32(0xC01E030C) -#define STATUS_GRAPHICS_INVALID_VIDEO_PRESENT_SOURCE_MODE cpu_to_le32(0xC01E0310) -#define STATUS_GRAPHICS_INVALID_VIDEO_PRESENT_TARGET_MODE cpu_to_le32(0xC01E0311) -#define STATUS_GRAPHICS_PINNED_MODE_MUST_REMAIN_IN_SET cpu_to_le32(0xC01E0312) -#define STATUS_GRAPHICS_PATH_ALREADY_IN_TOPOLOGY cpu_to_le32(0xC01E0313) -#define STATUS_GRAPHICS_MODE_ALREADY_IN_MODESET cpu_to_le32(0xC01E0314) -#define STATUS_GRAPHICS_INVALID_VIDEOPRESENTSOURCESET cpu_to_le32(0xC01E0315) -#define STATUS_GRAPHICS_INVALID_VIDEOPRESENTTARGETSET cpu_to_le32(0xC01E0316) -#define STATUS_GRAPHICS_SOURCE_ALREADY_IN_SET cpu_to_le32(0xC01E0317) -#define STATUS_GRAPHICS_TARGET_ALREADY_IN_SET cpu_to_le32(0xC01E0318) -#define STATUS_GRAPHICS_INVALID_VIDPN_PRESENT_PATH cpu_to_le32(0xC01E0319) -#define STATUS_GRAPHICS_NO_RECOMMENDED_VIDPN_TOPOLOGY cpu_to_le32(0xC01E031A) -#define STATUS_GRAPHICS_INVALID_MONITOR_FREQUENCYRANGESET cpu_to_le32(0xC01E031B) -#define STATUS_GRAPHICS_INVALID_MONITOR_FREQUENCYRANGE cpu_to_le32(0xC01E031C) -#define STATUS_GRAPHICS_FREQUENCYRANGE_NOT_IN_SET cpu_to_le32(0xC01E031D) -#define STATUS_GRAPHICS_FREQUENCYRANGE_ALREADY_IN_SET cpu_to_le32(0xC01E031F) -#define STATUS_GRAPHICS_STALE_MODESET cpu_to_le32(0xC01E0320) -#define STATUS_GRAPHICS_INVALID_MONITOR_SOURCEMODESET cpu_to_le32(0xC01E0321) -#define STATUS_GRAPHICS_INVALID_MONITOR_SOURCE_MODE cpu_to_le32(0xC01E0322) -#define STATUS_GRAPHICS_NO_RECOMMENDED_FUNCTIONAL_VIDPN cpu_to_le32(0xC01E0323) -#define STATUS_GRAPHICS_MODE_ID_MUST_BE_UNIQUE cpu_to_le32(0xC01E0324) -#define STATUS_GRAPHICS_EMPTY_ADAPTER_MONITOR_MODE_SUPPORT_INTERSECTION cpu_to_le32(0xC01E0325) -#define STATUS_GRAPHICS_VIDEO_PRESENT_TARGETS_LESS_THAN_SOURCES cpu_to_le32(0xC01E0326) -#define STATUS_GRAPHICS_PATH_NOT_IN_TOPOLOGY cpu_to_le32(0xC01E0327) -#define STATUS_GRAPHICS_ADAPTER_MUST_HAVE_AT_LEAST_ONE_SOURCE cpu_to_le32(0xC01E0328) -#define STATUS_GRAPHICS_ADAPTER_MUST_HAVE_AT_LEAST_ONE_TARGET cpu_to_le32(0xC01E0329) -#define STATUS_GRAPHICS_INVALID_MONITORDESCRIPTORSET cpu_to_le32(0xC01E032A) -#define STATUS_GRAPHICS_INVALID_MONITORDESCRIPTOR cpu_to_le32(0xC01E032B) -#define STATUS_GRAPHICS_MONITORDESCRIPTOR_NOT_IN_SET cpu_to_le32(0xC01E032C) -#define STATUS_GRAPHICS_MONITORDESCRIPTOR_ALREADY_IN_SET cpu_to_le32(0xC01E032D) -#define STATUS_GRAPHICS_MONITORDESCRIPTOR_ID_MUST_BE_UNIQUE cpu_to_le32(0xC01E032E) -#define STATUS_GRAPHICS_INVALID_VIDPN_TARGET_SUBSET_TYPE cpu_to_le32(0xC01E032F) -#define STATUS_GRAPHICS_RESOURCES_NOT_RELATED cpu_to_le32(0xC01E0330) -#define STATUS_GRAPHICS_SOURCE_ID_MUST_BE_UNIQUE cpu_to_le32(0xC01E0331) -#define STATUS_GRAPHICS_TARGET_ID_MUST_BE_UNIQUE cpu_to_le32(0xC01E0332) -#define STATUS_GRAPHICS_NO_AVAILABLE_VIDPN_TARGET cpu_to_le32(0xC01E0333) -#define STATUS_GRAPHICS_MONITOR_COULD_NOT_BE_ASSOCIATED_WITH_ADAPTER cpu_to_le32(0xC01E0334) -#define STATUS_GRAPHICS_NO_VIDPNMGR cpu_to_le32(0xC01E0335) -#define STATUS_GRAPHICS_NO_ACTIVE_VIDPN cpu_to_le32(0xC01E0336) -#define STATUS_GRAPHICS_STALE_VIDPN_TOPOLOGY cpu_to_le32(0xC01E0337) -#define STATUS_GRAPHICS_MONITOR_NOT_CONNECTED cpu_to_le32(0xC01E0338) -#define STATUS_GRAPHICS_SOURCE_NOT_IN_TOPOLOGY cpu_to_le32(0xC01E0339) -#define STATUS_GRAPHICS_INVALID_PRIMARYSURFACE_SIZE cpu_to_le32(0xC01E033A) -#define STATUS_GRAPHICS_INVALID_VISIBLEREGION_SIZE cpu_to_le32(0xC01E033B) -#define STATUS_GRAPHICS_INVALID_STRIDE cpu_to_le32(0xC01E033C) -#define STATUS_GRAPHICS_INVALID_PIXELFORMAT cpu_to_le32(0xC01E033D) -#define STATUS_GRAPHICS_INVALID_COLORBASIS cpu_to_le32(0xC01E033E) -#define STATUS_GRAPHICS_INVALID_PIXELVALUEACCESSMODE cpu_to_le32(0xC01E033F) -#define STATUS_GRAPHICS_TARGET_NOT_IN_TOPOLOGY cpu_to_le32(0xC01E0340) -#define STATUS_GRAPHICS_NO_DISPLAY_MODE_MANAGEMENT_SUPPORT cpu_to_le32(0xC01E0341) -#define STATUS_GRAPHICS_VIDPN_SOURCE_IN_USE cpu_to_le32(0xC01E0342) -#define STATUS_GRAPHICS_CANT_ACCESS_ACTIVE_VIDPN cpu_to_le32(0xC01E0343) -#define STATUS_GRAPHICS_INVALID_PATH_IMPORTANCE_ORDINAL cpu_to_le32(0xC01E0344) -#define STATUS_GRAPHICS_INVALID_PATH_CONTENT_GEOMETRY_TRANSFORMATION cpu_to_le32(0xC01E0345) -#define STATUS_GRAPHICS_PATH_CONTENT_GEOMETRY_TRANSFORMATION_NOT_SUPPORTED cpu_to_le32(0xC01E0346) -#define STATUS_GRAPHICS_INVALID_GAMMA_RAMP cpu_to_le32(0xC01E0347) -#define STATUS_GRAPHICS_GAMMA_RAMP_NOT_SUPPORTED cpu_to_le32(0xC01E0348) -#define STATUS_GRAPHICS_MULTISAMPLING_NOT_SUPPORTED cpu_to_le32(0xC01E0349) -#define STATUS_GRAPHICS_MODE_NOT_IN_MODESET cpu_to_le32(0xC01E034A) -#define STATUS_GRAPHICS_INVALID_VIDPN_TOPOLOGY_RECOMMENDATION_REASON cpu_to_le32(0xC01E034D) -#define STATUS_GRAPHICS_INVALID_PATH_CONTENT_TYPE cpu_to_le32(0xC01E034E) -#define STATUS_GRAPHICS_INVALID_COPYPROTECTION_TYPE cpu_to_le32(0xC01E034F) -#define STATUS_GRAPHICS_UNASSIGNED_MODESET_ALREADY_EXISTS cpu_to_le32(0xC01E0350) -#define STATUS_GRAPHICS_INVALID_SCANLINE_ORDERING cpu_to_le32(0xC01E0352) -#define STATUS_GRAPHICS_TOPOLOGY_CHANGES_NOT_ALLOWED cpu_to_le32(0xC01E0353) -#define STATUS_GRAPHICS_NO_AVAILABLE_IMPORTANCE_ORDINALS cpu_to_le32(0xC01E0354) -#define STATUS_GRAPHICS_INCOMPATIBLE_PRIVATE_FORMAT cpu_to_le32(0xC01E0355) -#define STATUS_GRAPHICS_INVALID_MODE_PRUNING_ALGORITHM cpu_to_le32(0xC01E0356) -#define STATUS_GRAPHICS_INVALID_MONITOR_CAPABILITY_ORIGIN cpu_to_le32(0xC01E0357) -#define STATUS_GRAPHICS_INVALID_MONITOR_FREQUENCYRANGE_CONSTRAINT cpu_to_le32(0xC01E0358) -#define STATUS_GRAPHICS_MAX_NUM_PATHS_REACHED cpu_to_le32(0xC01E0359) -#define STATUS_GRAPHICS_CANCEL_VIDPN_TOPOLOGY_AUGMENTATION cpu_to_le32(0xC01E035A) -#define STATUS_GRAPHICS_INVALID_CLIENT_TYPE cpu_to_le32(0xC01E035B) -#define STATUS_GRAPHICS_CLIENTVIDPN_NOT_SET cpu_to_le32(0xC01E035C) -#define STATUS_GRAPHICS_SPECIFIED_CHILD_ALREADY_CONNECTED cpu_to_le32(0xC01E0400) -#define STATUS_GRAPHICS_CHILD_DESCRIPTOR_NOT_SUPPORTED cpu_to_le32(0xC01E0401) -#define STATUS_GRAPHICS_NOT_A_LINKED_ADAPTER cpu_to_le32(0xC01E0430) -#define STATUS_GRAPHICS_LEADLINK_NOT_ENUMERATED cpu_to_le32(0xC01E0431) -#define STATUS_GRAPHICS_CHAINLINKS_NOT_ENUMERATED cpu_to_le32(0xC01E0432) -#define STATUS_GRAPHICS_ADAPTER_CHAIN_NOT_READY cpu_to_le32(0xC01E0433) -#define STATUS_GRAPHICS_CHAINLINKS_NOT_STARTED cpu_to_le32(0xC01E0434) -#define STATUS_GRAPHICS_CHAINLINKS_NOT_POWERED_ON cpu_to_le32(0xC01E0435) -#define STATUS_GRAPHICS_INCONSISTENT_DEVICE_LINK_STATE cpu_to_le32(0xC01E0436) -#define STATUS_GRAPHICS_NOT_POST_DEVICE_DRIVER cpu_to_le32(0xC01E0438) -#define STATUS_GRAPHICS_ADAPTER_ACCESS_NOT_EXCLUDED cpu_to_le32(0xC01E043B) -#define STATUS_GRAPHICS_OPM_PROTECTED_OUTPUT_DOES_NOT_HAVE_COPP_SEMANTICS cpu_to_le32(0xC01E051C) -#define STATUS_GRAPHICS_OPM_INVALID_INFORMATION_REQUEST cpu_to_le32(0xC01E051D) -#define STATUS_GRAPHICS_OPM_DRIVER_INTERNAL_ERROR cpu_to_le32(0xC01E051E) -#define STATUS_GRAPHICS_OPM_PROTECTED_OUTPUT_DOES_NOT_HAVE_OPM_SEMANTICS cpu_to_le32(0xC01E051F) -#define STATUS_GRAPHICS_OPM_SIGNALING_NOT_SUPPORTED cpu_to_le32(0xC01E0520) -#define STATUS_GRAPHICS_OPM_INVALID_CONFIGURATION_REQUEST cpu_to_le32(0xC01E0521) -#define STATUS_GRAPHICS_OPM_NOT_SUPPORTED cpu_to_le32(0xC01E0500) -#define STATUS_GRAPHICS_COPP_NOT_SUPPORTED cpu_to_le32(0xC01E0501) -#define STATUS_GRAPHICS_UAB_NOT_SUPPORTED cpu_to_le32(0xC01E0502) -#define STATUS_GRAPHICS_OPM_INVALID_ENCRYPTED_PARAMETERS cpu_to_le32(0xC01E0503) -#define STATUS_GRAPHICS_OPM_PARAMETER_ARRAY_TOO_SMALL cpu_to_le32(0xC01E0504) -#define STATUS_GRAPHICS_OPM_NO_PROTECTED_OUTPUTS_EXIST cpu_to_le32(0xC01E0505) -#define STATUS_GRAPHICS_PVP_NO_DISPLAY_DEVICE_CORRESPONDS_TO_NAME cpu_to_le32(0xC01E0506) -#define STATUS_GRAPHICS_PVP_DISPLAY_DEVICE_NOT_ATTACHED_TO_DESKTOP cpu_to_le32(0xC01E0507) -#define STATUS_GRAPHICS_PVP_MIRRORING_DEVICES_NOT_SUPPORTED cpu_to_le32(0xC01E0508) -#define STATUS_GRAPHICS_OPM_INVALID_POINTER cpu_to_le32(0xC01E050A) -#define STATUS_GRAPHICS_OPM_INTERNAL_ERROR cpu_to_le32(0xC01E050B) -#define STATUS_GRAPHICS_OPM_INVALID_HANDLE cpu_to_le32(0xC01E050C) -#define STATUS_GRAPHICS_PVP_NO_MONITORS_CORRESPOND_TO_DISPLAY_DEVICE cpu_to_le32(0xC01E050D) -#define STATUS_GRAPHICS_PVP_INVALID_CERTIFICATE_LENGTH cpu_to_le32(0xC01E050E) -#define STATUS_GRAPHICS_OPM_SPANNING_MODE_ENABLED cpu_to_le32(0xC01E050F) -#define STATUS_GRAPHICS_OPM_THEATER_MODE_ENABLED cpu_to_le32(0xC01E0510) -#define STATUS_GRAPHICS_PVP_HFS_FAILED cpu_to_le32(0xC01E0511) -#define STATUS_GRAPHICS_OPM_INVALID_SRM cpu_to_le32(0xC01E0512) -#define STATUS_GRAPHICS_OPM_OUTPUT_DOES_NOT_SUPPORT_HDCP cpu_to_le32(0xC01E0513) -#define STATUS_GRAPHICS_OPM_OUTPUT_DOES_NOT_SUPPORT_ACP cpu_to_le32(0xC01E0514) -#define STATUS_GRAPHICS_OPM_OUTPUT_DOES_NOT_SUPPORT_CGMSA cpu_to_le32(0xC01E0515) -#define STATUS_GRAPHICS_OPM_HDCP_SRM_NEVER_SET cpu_to_le32(0xC01E0516) -#define STATUS_GRAPHICS_OPM_RESOLUTION_TOO_HIGH cpu_to_le32(0xC01E0517) -#define STATUS_GRAPHICS_OPM_ALL_HDCP_HARDWARE_ALREADY_IN_USE cpu_to_le32(0xC01E0518) -#define STATUS_GRAPHICS_OPM_PROTECTED_OUTPUT_NO_LONGER_EXISTS cpu_to_le32(0xC01E051A) -#define STATUS_GRAPHICS_OPM_SESSION_TYPE_CHANGE_IN_PROGRESS cpu_to_le32(0xC01E051B) -#define STATUS_GRAPHICS_I2C_NOT_SUPPORTED cpu_to_le32(0xC01E0580) -#define STATUS_GRAPHICS_I2C_DEVICE_DOES_NOT_EXIST cpu_to_le32(0xC01E0581) -#define STATUS_GRAPHICS_I2C_ERROR_TRANSMITTING_DATA cpu_to_le32(0xC01E0582) -#define STATUS_GRAPHICS_I2C_ERROR_RECEIVING_DATA cpu_to_le32(0xC01E0583) -#define STATUS_GRAPHICS_DDCCI_VCP_NOT_SUPPORTED cpu_to_le32(0xC01E0584) -#define STATUS_GRAPHICS_DDCCI_INVALID_DATA cpu_to_le32(0xC01E0585) -#define STATUS_GRAPHICS_DDCCI_MONITOR_RETURNED_INVALID_TIMING_STATUS_BYTE cpu_to_le32(0xC01E0586) -#define STATUS_GRAPHICS_DDCCI_INVALID_CAPABILITIES_STRING cpu_to_le32(0xC01E0587) -#define STATUS_GRAPHICS_MCA_INTERNAL_ERROR cpu_to_le32(0xC01E0588) -#define STATUS_GRAPHICS_DDCCI_INVALID_MESSAGE_COMMAND cpu_to_le32(0xC01E0589) -#define STATUS_GRAPHICS_DDCCI_INVALID_MESSAGE_LENGTH cpu_to_le32(0xC01E058A) -#define STATUS_GRAPHICS_DDCCI_INVALID_MESSAGE_CHECKSUM cpu_to_le32(0xC01E058B) -#define STATUS_GRAPHICS_INVALID_PHYSICAL_MONITOR_HANDLE cpu_to_le32(0xC01E058C) -#define STATUS_GRAPHICS_MONITOR_NO_LONGER_EXISTS cpu_to_le32(0xC01E058D) -#define STATUS_GRAPHICS_ONLY_CONSOLE_SESSION_SUPPORTED cpu_to_le32(0xC01E05E0) -#define STATUS_GRAPHICS_NO_DISPLAY_DEVICE_CORRESPONDS_TO_NAME cpu_to_le32(0xC01E05E1) -#define STATUS_GRAPHICS_DISPLAY_DEVICE_NOT_ATTACHED_TO_DESKTOP cpu_to_le32(0xC01E05E2) -#define STATUS_GRAPHICS_MIRRORING_DEVICES_NOT_SUPPORTED cpu_to_le32(0xC01E05E3) -#define STATUS_GRAPHICS_INVALID_POINTER cpu_to_le32(0xC01E05E4) -#define STATUS_GRAPHICS_NO_MONITORS_CORRESPOND_TO_DISPLAY_DEVICE cpu_to_le32(0xC01E05E5) -#define STATUS_GRAPHICS_PARAMETER_ARRAY_TOO_SMALL cpu_to_le32(0xC01E05E6) -#define STATUS_GRAPHICS_INTERNAL_ERROR cpu_to_le32(0xC01E05E7) -#define STATUS_GRAPHICS_SESSION_TYPE_CHANGE_IN_PROGRESS cpu_to_le32(0xC01E05E8) -#define STATUS_FVE_LOCKED_VOLUME cpu_to_le32(0xC0210000) -#define STATUS_FVE_NOT_ENCRYPTED cpu_to_le32(0xC0210001) -#define STATUS_FVE_BAD_INFORMATION cpu_to_le32(0xC0210002) -#define STATUS_FVE_TOO_SMALL cpu_to_le32(0xC0210003) -#define STATUS_FVE_FAILED_WRONG_FS cpu_to_le32(0xC0210004) -#define STATUS_FVE_FAILED_BAD_FS cpu_to_le32(0xC0210005) -#define STATUS_FVE_FS_NOT_EXTENDED cpu_to_le32(0xC0210006) -#define STATUS_FVE_FS_MOUNTED cpu_to_le32(0xC0210007) -#define STATUS_FVE_NO_LICENSE cpu_to_le32(0xC0210008) -#define STATUS_FVE_ACTION_NOT_ALLOWED cpu_to_le32(0xC0210009) -#define STATUS_FVE_BAD_DATA cpu_to_le32(0xC021000A) -#define STATUS_FVE_VOLUME_NOT_BOUND cpu_to_le32(0xC021000B) -#define STATUS_FVE_NOT_DATA_VOLUME cpu_to_le32(0xC021000C) -#define STATUS_FVE_CONV_READ_ERROR cpu_to_le32(0xC021000D) -#define STATUS_FVE_CONV_WRITE_ERROR cpu_to_le32(0xC021000E) -#define STATUS_FVE_OVERLAPPED_UPDATE cpu_to_le32(0xC021000F) -#define STATUS_FVE_FAILED_SECTOR_SIZE cpu_to_le32(0xC0210010) -#define STATUS_FVE_FAILED_AUTHENTICATION cpu_to_le32(0xC0210011) -#define STATUS_FVE_NOT_OS_VOLUME cpu_to_le32(0xC0210012) -#define STATUS_FVE_KEYFILE_NOT_FOUND cpu_to_le32(0xC0210013) -#define STATUS_FVE_KEYFILE_INVALID cpu_to_le32(0xC0210014) -#define STATUS_FVE_KEYFILE_NO_VMK cpu_to_le32(0xC0210015) -#define STATUS_FVE_TPM_DISABLED cpu_to_le32(0xC0210016) -#define STATUS_FVE_TPM_SRK_AUTH_NOT_ZERO cpu_to_le32(0xC0210017) -#define STATUS_FVE_TPM_INVALID_PCR cpu_to_le32(0xC0210018) -#define STATUS_FVE_TPM_NO_VMK cpu_to_le32(0xC0210019) -#define STATUS_FVE_PIN_INVALID cpu_to_le32(0xC021001A) -#define STATUS_FVE_AUTH_INVALID_APPLICATION cpu_to_le32(0xC021001B) -#define STATUS_FVE_AUTH_INVALID_CONFIG cpu_to_le32(0xC021001C) -#define STATUS_FVE_DEBUGGER_ENABLED cpu_to_le32(0xC021001D) -#define STATUS_FVE_DRY_RUN_FAILED cpu_to_le32(0xC021001E) -#define STATUS_FVE_BAD_METADATA_POINTER cpu_to_le32(0xC021001F) -#define STATUS_FVE_OLD_METADATA_COPY cpu_to_le32(0xC0210020) -#define STATUS_FVE_REBOOT_REQUIRED cpu_to_le32(0xC0210021) -#define STATUS_FVE_RAW_ACCESS cpu_to_le32(0xC0210022) -#define STATUS_FVE_RAW_BLOCKED cpu_to_le32(0xC0210023) -#define STATUS_FWP_CALLOUT_NOT_FOUND cpu_to_le32(0xC0220001) -#define STATUS_FWP_CONDITION_NOT_FOUND cpu_to_le32(0xC0220002) -#define STATUS_FWP_FILTER_NOT_FOUND cpu_to_le32(0xC0220003) -#define STATUS_FWP_LAYER_NOT_FOUND cpu_to_le32(0xC0220004) -#define STATUS_FWP_PROVIDER_NOT_FOUND cpu_to_le32(0xC0220005) -#define STATUS_FWP_PROVIDER_CONTEXT_NOT_FOUND cpu_to_le32(0xC0220006) -#define STATUS_FWP_SUBLAYER_NOT_FOUND cpu_to_le32(0xC0220007) -#define STATUS_FWP_NOT_FOUND cpu_to_le32(0xC0220008) -#define STATUS_FWP_ALREADY_EXISTS cpu_to_le32(0xC0220009) -#define STATUS_FWP_IN_USE cpu_to_le32(0xC022000A) -#define STATUS_FWP_DYNAMIC_SESSION_IN_PROGRESS cpu_to_le32(0xC022000B) -#define STATUS_FWP_WRONG_SESSION cpu_to_le32(0xC022000C) -#define STATUS_FWP_NO_TXN_IN_PROGRESS cpu_to_le32(0xC022000D) -#define STATUS_FWP_TXN_IN_PROGRESS cpu_to_le32(0xC022000E) -#define STATUS_FWP_TXN_ABORTED cpu_to_le32(0xC022000F) -#define STATUS_FWP_SESSION_ABORTED cpu_to_le32(0xC0220010) -#define STATUS_FWP_INCOMPATIBLE_TXN cpu_to_le32(0xC0220011) -#define STATUS_FWP_TIMEOUT cpu_to_le32(0xC0220012) -#define STATUS_FWP_NET_EVENTS_DISABLED cpu_to_le32(0xC0220013) -#define STATUS_FWP_INCOMPATIBLE_LAYER cpu_to_le32(0xC0220014) -#define STATUS_FWP_KM_CLIENTS_ONLY cpu_to_le32(0xC0220015) -#define STATUS_FWP_LIFETIME_MISMATCH cpu_to_le32(0xC0220016) -#define STATUS_FWP_BUILTIN_OBJECT cpu_to_le32(0xC0220017) -#define STATUS_FWP_TOO_MANY_BOOTTIME_FILTERS cpu_to_le32(0xC0220018) -#define STATUS_FWP_TOO_MANY_CALLOUTS cpu_to_le32(0xC0220018) -#define STATUS_FWP_NOTIFICATION_DROPPED cpu_to_le32(0xC0220019) -#define STATUS_FWP_TRAFFIC_MISMATCH cpu_to_le32(0xC022001A) -#define STATUS_FWP_INCOMPATIBLE_SA_STATE cpu_to_le32(0xC022001B) -#define STATUS_FWP_NULL_POINTER cpu_to_le32(0xC022001C) -#define STATUS_FWP_INVALID_ENUMERATOR cpu_to_le32(0xC022001D) -#define STATUS_FWP_INVALID_FLAGS cpu_to_le32(0xC022001E) -#define STATUS_FWP_INVALID_NET_MASK cpu_to_le32(0xC022001F) -#define STATUS_FWP_INVALID_RANGE cpu_to_le32(0xC0220020) -#define STATUS_FWP_INVALID_INTERVAL cpu_to_le32(0xC0220021) -#define STATUS_FWP_ZERO_LENGTH_ARRAY cpu_to_le32(0xC0220022) -#define STATUS_FWP_NULL_DISPLAY_NAME cpu_to_le32(0xC0220023) -#define STATUS_FWP_INVALID_ACTION_TYPE cpu_to_le32(0xC0220024) -#define STATUS_FWP_INVALID_WEIGHT cpu_to_le32(0xC0220025) -#define STATUS_FWP_MATCH_TYPE_MISMATCH cpu_to_le32(0xC0220026) -#define STATUS_FWP_TYPE_MISMATCH cpu_to_le32(0xC0220027) -#define STATUS_FWP_OUT_OF_BOUNDS cpu_to_le32(0xC0220028) -#define STATUS_FWP_RESERVED cpu_to_le32(0xC0220029) -#define STATUS_FWP_DUPLICATE_CONDITION cpu_to_le32(0xC022002A) -#define STATUS_FWP_DUPLICATE_KEYMOD cpu_to_le32(0xC022002B) -#define STATUS_FWP_ACTION_INCOMPATIBLE_WITH_LAYER cpu_to_le32(0xC022002C) -#define STATUS_FWP_ACTION_INCOMPATIBLE_WITH_SUBLAYER cpu_to_le32(0xC022002D) -#define STATUS_FWP_CONTEXT_INCOMPATIBLE_WITH_LAYER cpu_to_le32(0xC022002E) -#define STATUS_FWP_CONTEXT_INCOMPATIBLE_WITH_CALLOUT cpu_to_le32(0xC022002F) -#define STATUS_FWP_INCOMPATIBLE_AUTH_METHOD cpu_to_le32(0xC0220030) -#define STATUS_FWP_INCOMPATIBLE_DH_GROUP cpu_to_le32(0xC0220031) -#define STATUS_FWP_EM_NOT_SUPPORTED cpu_to_le32(0xC0220032) -#define STATUS_FWP_NEVER_MATCH cpu_to_le32(0xC0220033) -#define STATUS_FWP_PROVIDER_CONTEXT_MISMATCH cpu_to_le32(0xC0220034) -#define STATUS_FWP_INVALID_PARAMETER cpu_to_le32(0xC0220035) -#define STATUS_FWP_TOO_MANY_SUBLAYERS cpu_to_le32(0xC0220036) -#define STATUS_FWP_CALLOUT_NOTIFICATION_FAILED cpu_to_le32(0xC0220037) -#define STATUS_FWP_INCOMPATIBLE_AUTH_CONFIG cpu_to_le32(0xC0220038) -#define STATUS_FWP_INCOMPATIBLE_CIPHER_CONFIG cpu_to_le32(0xC0220039) -#define STATUS_FWP_TCPIP_NOT_READY cpu_to_le32(0xC0220100) -#define STATUS_FWP_INJECT_HANDLE_CLOSING cpu_to_le32(0xC0220101) -#define STATUS_FWP_INJECT_HANDLE_STALE cpu_to_le32(0xC0220102) -#define STATUS_FWP_CANNOT_PEND cpu_to_le32(0xC0220103) -#define STATUS_NDIS_CLOSING cpu_to_le32(0xC0230002) -#define STATUS_NDIS_BAD_VERSION cpu_to_le32(0xC0230004) -#define STATUS_NDIS_BAD_CHARACTERISTICS cpu_to_le32(0xC0230005) -#define STATUS_NDIS_ADAPTER_NOT_FOUND cpu_to_le32(0xC0230006) -#define STATUS_NDIS_OPEN_FAILED cpu_to_le32(0xC0230007) -#define STATUS_NDIS_DEVICE_FAILED cpu_to_le32(0xC0230008) -#define STATUS_NDIS_MULTICAST_FULL cpu_to_le32(0xC0230009) -#define STATUS_NDIS_MULTICAST_EXISTS cpu_to_le32(0xC023000A) -#define STATUS_NDIS_MULTICAST_NOT_FOUND cpu_to_le32(0xC023000B) -#define STATUS_NDIS_REQUEST_ABORTED cpu_to_le32(0xC023000C) -#define STATUS_NDIS_RESET_IN_PROGRESS cpu_to_le32(0xC023000D) -#define STATUS_NDIS_INVALID_PACKET cpu_to_le32(0xC023000F) -#define STATUS_NDIS_INVALID_DEVICE_REQUEST cpu_to_le32(0xC0230010) -#define STATUS_NDIS_ADAPTER_NOT_READY cpu_to_le32(0xC0230011) -#define STATUS_NDIS_INVALID_LENGTH cpu_to_le32(0xC0230014) -#define STATUS_NDIS_INVALID_DATA cpu_to_le32(0xC0230015) -#define STATUS_NDIS_BUFFER_TOO_SHORT cpu_to_le32(0xC0230016) -#define STATUS_NDIS_INVALID_OID cpu_to_le32(0xC0230017) -#define STATUS_NDIS_ADAPTER_REMOVED cpu_to_le32(0xC0230018) -#define STATUS_NDIS_UNSUPPORTED_MEDIA cpu_to_le32(0xC0230019) -#define STATUS_NDIS_GROUP_ADDRESS_IN_USE cpu_to_le32(0xC023001A) -#define STATUS_NDIS_FILE_NOT_FOUND cpu_to_le32(0xC023001B) -#define STATUS_NDIS_ERROR_READING_FILE cpu_to_le32(0xC023001C) -#define STATUS_NDIS_ALREADY_MAPPED cpu_to_le32(0xC023001D) -#define STATUS_NDIS_RESOURCE_CONFLICT cpu_to_le32(0xC023001E) -#define STATUS_NDIS_MEDIA_DISCONNECTED cpu_to_le32(0xC023001F) -#define STATUS_NDIS_INVALID_ADDRESS cpu_to_le32(0xC0230022) -#define STATUS_NDIS_PAUSED cpu_to_le32(0xC023002A) -#define STATUS_NDIS_INTERFACE_NOT_FOUND cpu_to_le32(0xC023002B) -#define STATUS_NDIS_UNSUPPORTED_REVISION cpu_to_le32(0xC023002C) -#define STATUS_NDIS_INVALID_PORT cpu_to_le32(0xC023002D) -#define STATUS_NDIS_INVALID_PORT_STATE cpu_to_le32(0xC023002E) -#define STATUS_NDIS_LOW_POWER_STATE cpu_to_le32(0xC023002F) -#define STATUS_NDIS_NOT_SUPPORTED cpu_to_le32(0xC02300BB) -#define STATUS_NDIS_DOT11_AUTO_CONFIG_ENABLED cpu_to_le32(0xC0232000) -#define STATUS_NDIS_DOT11_MEDIA_IN_USE cpu_to_le32(0xC0232001) -#define STATUS_NDIS_DOT11_POWER_STATE_INVALID cpu_to_le32(0xC0232002) -#define STATUS_IPSEC_BAD_SPI cpu_to_le32(0xC0360001) -#define STATUS_IPSEC_SA_LIFETIME_EXPIRED cpu_to_le32(0xC0360002) -#define STATUS_IPSEC_WRONG_SA cpu_to_le32(0xC0360003) -#define STATUS_IPSEC_REPLAY_CHECK_FAILED cpu_to_le32(0xC0360004) -#define STATUS_IPSEC_INVALID_PACKET cpu_to_le32(0xC0360005) -#define STATUS_IPSEC_INTEGRITY_CHECK_FAILED cpu_to_le32(0xC0360006) -#define STATUS_IPSEC_CLEAR_TEXT_DROP cpu_to_le32(0xC0360007) diff --git a/fs/cifs/smb2transport.c b/fs/cifs/smb2transport.c deleted file mode 100644 index 790acf65a0926..0000000000000 --- a/fs/cifs/smb2transport.c +++ /dev/null @@ -1,934 +0,0 @@ -// SPDX-License-Identifier: LGPL-2.1 -/* - * - * Copyright (C) International Business Machines Corp., 2002, 2011 - * Etersoft, 2012 - * Author(s): Steve French (sfrench@us.ibm.com) - * Jeremy Allison (jra@samba.org) 2006 - * Pavel Shilovsky (pshilovsky@samba.org) 2012 - * - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include "cifsglob.h" -#include "cifsproto.h" -#include "smb2proto.h" -#include "cifs_debug.h" -#include "smb2status.h" -#include "smb2glob.h" - -static int -smb3_crypto_shash_allocate(struct TCP_Server_Info *server) -{ - struct cifs_secmech *p = &server->secmech; - int rc; - - rc = cifs_alloc_hash("hmac(sha256)", &p->hmacsha256); - if (rc) - goto err; - - rc = cifs_alloc_hash("cmac(aes)", &p->aes_cmac); - if (rc) - goto err; - - return 0; -err: - cifs_free_hash(&p->hmacsha256); - return rc; -} - -int -smb311_crypto_shash_allocate(struct TCP_Server_Info *server) -{ - struct cifs_secmech *p = &server->secmech; - int rc = 0; - - rc = cifs_alloc_hash("hmac(sha256)", &p->hmacsha256); - if (rc) - return rc; - - rc = cifs_alloc_hash("cmac(aes)", &p->aes_cmac); - if (rc) - goto err; - - rc = cifs_alloc_hash("sha512", &p->sha512); - if (rc) - goto err; - - return 0; - -err: - cifs_free_hash(&p->aes_cmac); - cifs_free_hash(&p->hmacsha256); - return rc; -} - - -static -int smb2_get_sign_key(__u64 ses_id, struct TCP_Server_Info *server, u8 *key) -{ - struct cifs_chan *chan; - struct TCP_Server_Info *pserver; - struct cifs_ses *ses = NULL; - int i; - int rc = 0; - bool is_binding = false; - - spin_lock(&cifs_tcp_ses_lock); - - /* If server is a channel, select the primary channel */ - pserver = CIFS_SERVER_IS_CHAN(server) ? server->primary_server : server; - - list_for_each_entry(ses, &pserver->smb_ses_list, smb_ses_list) { - if (ses->Suid == ses_id) - goto found; - } - cifs_server_dbg(VFS, "%s: Could not find session 0x%llx\n", - __func__, ses_id); - rc = -ENOENT; - goto out; - -found: - spin_lock(&ses->ses_lock); - spin_lock(&ses->chan_lock); - - is_binding = (cifs_chan_needs_reconnect(ses, server) && - ses->ses_status == SES_GOOD); - if (is_binding) { - /* - * If we are in the process of binding a new channel - * to an existing session, use the master connection - * session key - */ - memcpy(key, ses->smb3signingkey, SMB3_SIGN_KEY_SIZE); - spin_unlock(&ses->chan_lock); - spin_unlock(&ses->ses_lock); - goto out; - } - - /* - * Otherwise, use the channel key. - */ - - for (i = 0; i < ses->chan_count; i++) { - chan = ses->chans + i; - if (chan->server == server) { - memcpy(key, chan->signkey, SMB3_SIGN_KEY_SIZE); - spin_unlock(&ses->chan_lock); - spin_unlock(&ses->ses_lock); - goto out; - } - } - spin_unlock(&ses->chan_lock); - spin_unlock(&ses->ses_lock); - - cifs_dbg(VFS, - "%s: Could not find channel signing key for session 0x%llx\n", - __func__, ses_id); - rc = -ENOENT; - -out: - spin_unlock(&cifs_tcp_ses_lock); - return rc; -} - -static struct cifs_ses * -smb2_find_smb_ses_unlocked(struct TCP_Server_Info *server, __u64 ses_id) -{ - struct TCP_Server_Info *pserver; - struct cifs_ses *ses; - - /* If server is a channel, select the primary channel */ - pserver = CIFS_SERVER_IS_CHAN(server) ? server->primary_server : server; - - list_for_each_entry(ses, &pserver->smb_ses_list, smb_ses_list) { - if (ses->Suid != ses_id) - continue; - ++ses->ses_count; - return ses; - } - - return NULL; -} - -struct cifs_ses * -smb2_find_smb_ses(struct TCP_Server_Info *server, __u64 ses_id) -{ - struct cifs_ses *ses; - - spin_lock(&cifs_tcp_ses_lock); - ses = smb2_find_smb_ses_unlocked(server, ses_id); - spin_unlock(&cifs_tcp_ses_lock); - - return ses; -} - -static struct cifs_tcon * -smb2_find_smb_sess_tcon_unlocked(struct cifs_ses *ses, __u32 tid) -{ - struct cifs_tcon *tcon; - - list_for_each_entry(tcon, &ses->tcon_list, tcon_list) { - if (tcon->tid != tid) - continue; - ++tcon->tc_count; - return tcon; - } - - return NULL; -} - -/* - * Obtain tcon corresponding to the tid in the given - * cifs_ses - */ - -struct cifs_tcon * -smb2_find_smb_tcon(struct TCP_Server_Info *server, __u64 ses_id, __u32 tid) -{ - struct cifs_ses *ses; - struct cifs_tcon *tcon; - - spin_lock(&cifs_tcp_ses_lock); - ses = smb2_find_smb_ses_unlocked(server, ses_id); - if (!ses) { - spin_unlock(&cifs_tcp_ses_lock); - return NULL; - } - tcon = smb2_find_smb_sess_tcon_unlocked(ses, tid); - if (!tcon) { - cifs_put_smb_ses(ses); - spin_unlock(&cifs_tcp_ses_lock); - return NULL; - } - spin_unlock(&cifs_tcp_ses_lock); - /* tcon already has a ref to ses, so we don't need ses anymore */ - cifs_put_smb_ses(ses); - - return tcon; -} - -int -smb2_calc_signature(struct smb_rqst *rqst, struct TCP_Server_Info *server, - bool allocate_crypto) -{ - int rc; - unsigned char smb2_signature[SMB2_HMACSHA256_SIZE]; - unsigned char *sigptr = smb2_signature; - struct kvec *iov = rqst->rq_iov; - struct smb2_hdr *shdr = (struct smb2_hdr *)iov[0].iov_base; - struct cifs_ses *ses; - struct shash_desc *shash = NULL; - struct smb_rqst drqst; - - ses = smb2_find_smb_ses(server, le64_to_cpu(shdr->SessionId)); - if (unlikely(!ses)) { - cifs_server_dbg(VFS, "%s: Could not find session\n", __func__); - return -ENOENT; - } - - memset(smb2_signature, 0x0, SMB2_HMACSHA256_SIZE); - memset(shdr->Signature, 0x0, SMB2_SIGNATURE_SIZE); - - if (allocate_crypto) { - rc = cifs_alloc_hash("hmac(sha256)", &shash); - if (rc) { - cifs_server_dbg(VFS, - "%s: sha256 alloc failed\n", __func__); - goto out; - } - } else { - shash = server->secmech.hmacsha256; - } - - rc = crypto_shash_setkey(shash->tfm, ses->auth_key.response, - SMB2_NTLMV2_SESSKEY_SIZE); - if (rc) { - cifs_server_dbg(VFS, - "%s: Could not update with response\n", - __func__); - goto out; - } - - rc = crypto_shash_init(shash); - if (rc) { - cifs_server_dbg(VFS, "%s: Could not init sha256", __func__); - goto out; - } - - /* - * For SMB2+, __cifs_calc_signature() expects to sign only the actual - * data, that is, iov[0] should not contain a rfc1002 length. - * - * Sign the rfc1002 length prior to passing the data (iov[1-N]) down to - * __cifs_calc_signature(). - */ - drqst = *rqst; - if (drqst.rq_nvec >= 2 && iov[0].iov_len == 4) { - rc = crypto_shash_update(shash, iov[0].iov_base, - iov[0].iov_len); - if (rc) { - cifs_server_dbg(VFS, - "%s: Could not update with payload\n", - __func__); - goto out; - } - drqst.rq_iov++; - drqst.rq_nvec--; - } - - rc = __cifs_calc_signature(&drqst, server, sigptr, shash); - if (!rc) - memcpy(shdr->Signature, sigptr, SMB2_SIGNATURE_SIZE); - -out: - if (allocate_crypto) - cifs_free_hash(&shash); - if (ses) - cifs_put_smb_ses(ses); - return rc; -} - -static int generate_key(struct cifs_ses *ses, struct kvec label, - struct kvec context, __u8 *key, unsigned int key_size) -{ - unsigned char zero = 0x0; - __u8 i[4] = {0, 0, 0, 1}; - __u8 L128[4] = {0, 0, 0, 128}; - __u8 L256[4] = {0, 0, 1, 0}; - int rc = 0; - unsigned char prfhash[SMB2_HMACSHA256_SIZE]; - unsigned char *hashptr = prfhash; - struct TCP_Server_Info *server = ses->server; - - memset(prfhash, 0x0, SMB2_HMACSHA256_SIZE); - memset(key, 0x0, key_size); - - rc = smb3_crypto_shash_allocate(server); - if (rc) { - cifs_server_dbg(VFS, "%s: crypto alloc failed\n", __func__); - goto smb3signkey_ret; - } - - rc = crypto_shash_setkey(server->secmech.hmacsha256->tfm, - ses->auth_key.response, SMB2_NTLMV2_SESSKEY_SIZE); - if (rc) { - cifs_server_dbg(VFS, "%s: Could not set with session key\n", __func__); - goto smb3signkey_ret; - } - - rc = crypto_shash_init(server->secmech.hmacsha256); - if (rc) { - cifs_server_dbg(VFS, "%s: Could not init sign hmac\n", __func__); - goto smb3signkey_ret; - } - - rc = crypto_shash_update(server->secmech.hmacsha256, i, 4); - if (rc) { - cifs_server_dbg(VFS, "%s: Could not update with n\n", __func__); - goto smb3signkey_ret; - } - - rc = crypto_shash_update(server->secmech.hmacsha256, label.iov_base, label.iov_len); - if (rc) { - cifs_server_dbg(VFS, "%s: Could not update with label\n", __func__); - goto smb3signkey_ret; - } - - rc = crypto_shash_update(server->secmech.hmacsha256, &zero, 1); - if (rc) { - cifs_server_dbg(VFS, "%s: Could not update with zero\n", __func__); - goto smb3signkey_ret; - } - - rc = crypto_shash_update(server->secmech.hmacsha256, context.iov_base, context.iov_len); - if (rc) { - cifs_server_dbg(VFS, "%s: Could not update with context\n", __func__); - goto smb3signkey_ret; - } - - if ((server->cipher_type == SMB2_ENCRYPTION_AES256_CCM) || - (server->cipher_type == SMB2_ENCRYPTION_AES256_GCM)) { - rc = crypto_shash_update(server->secmech.hmacsha256, L256, 4); - } else { - rc = crypto_shash_update(server->secmech.hmacsha256, L128, 4); - } - if (rc) { - cifs_server_dbg(VFS, "%s: Could not update with L\n", __func__); - goto smb3signkey_ret; - } - - rc = crypto_shash_final(server->secmech.hmacsha256, hashptr); - if (rc) { - cifs_server_dbg(VFS, "%s: Could not generate sha256 hash\n", __func__); - goto smb3signkey_ret; - } - - memcpy(key, hashptr, key_size); - -smb3signkey_ret: - return rc; -} - -struct derivation { - struct kvec label; - struct kvec context; -}; - -struct derivation_triplet { - struct derivation signing; - struct derivation encryption; - struct derivation decryption; -}; - -static int -generate_smb3signingkey(struct cifs_ses *ses, - struct TCP_Server_Info *server, - const struct derivation_triplet *ptriplet) -{ - int rc; - bool is_binding = false; - int chan_index = 0; - - spin_lock(&ses->ses_lock); - spin_lock(&ses->chan_lock); - is_binding = (cifs_chan_needs_reconnect(ses, server) && - ses->ses_status == SES_GOOD); - - chan_index = cifs_ses_get_chan_index(ses, server); - /* TODO: introduce ref counting for channels when the can be freed */ - spin_unlock(&ses->chan_lock); - spin_unlock(&ses->ses_lock); - - /* - * All channels use the same encryption/decryption keys but - * they have their own signing key. - * - * When we generate the keys, check if it is for a new channel - * (binding) in which case we only need to generate a signing - * key and store it in the channel as to not overwrite the - * master connection signing key stored in the session - */ - - if (is_binding) { - rc = generate_key(ses, ptriplet->signing.label, - ptriplet->signing.context, - ses->chans[chan_index].signkey, - SMB3_SIGN_KEY_SIZE); - if (rc) - return rc; - } else { - rc = generate_key(ses, ptriplet->signing.label, - ptriplet->signing.context, - ses->smb3signingkey, - SMB3_SIGN_KEY_SIZE); - if (rc) - return rc; - - /* safe to access primary channel, since it will never go away */ - spin_lock(&ses->chan_lock); - memcpy(ses->chans[chan_index].signkey, ses->smb3signingkey, - SMB3_SIGN_KEY_SIZE); - spin_unlock(&ses->chan_lock); - - rc = generate_key(ses, ptriplet->encryption.label, - ptriplet->encryption.context, - ses->smb3encryptionkey, - SMB3_ENC_DEC_KEY_SIZE); - rc = generate_key(ses, ptriplet->decryption.label, - ptriplet->decryption.context, - ses->smb3decryptionkey, - SMB3_ENC_DEC_KEY_SIZE); - if (rc) - return rc; - } - - if (rc) - return rc; - -#ifdef CONFIG_CIFS_DEBUG_DUMP_KEYS - cifs_dbg(VFS, "%s: dumping generated AES session keys\n", __func__); - /* - * The session id is opaque in terms of endianness, so we can't - * print it as a long long. we dump it as we got it on the wire - */ - cifs_dbg(VFS, "Session Id %*ph\n", (int)sizeof(ses->Suid), - &ses->Suid); - cifs_dbg(VFS, "Cipher type %d\n", server->cipher_type); - cifs_dbg(VFS, "Session Key %*ph\n", - SMB2_NTLMV2_SESSKEY_SIZE, ses->auth_key.response); - cifs_dbg(VFS, "Signing Key %*ph\n", - SMB3_SIGN_KEY_SIZE, ses->smb3signingkey); - if ((server->cipher_type == SMB2_ENCRYPTION_AES256_CCM) || - (server->cipher_type == SMB2_ENCRYPTION_AES256_GCM)) { - cifs_dbg(VFS, "ServerIn Key %*ph\n", - SMB3_GCM256_CRYPTKEY_SIZE, ses->smb3encryptionkey); - cifs_dbg(VFS, "ServerOut Key %*ph\n", - SMB3_GCM256_CRYPTKEY_SIZE, ses->smb3decryptionkey); - } else { - cifs_dbg(VFS, "ServerIn Key %*ph\n", - SMB3_GCM128_CRYPTKEY_SIZE, ses->smb3encryptionkey); - cifs_dbg(VFS, "ServerOut Key %*ph\n", - SMB3_GCM128_CRYPTKEY_SIZE, ses->smb3decryptionkey); - } -#endif - return rc; -} - -int -generate_smb30signingkey(struct cifs_ses *ses, - struct TCP_Server_Info *server) - -{ - struct derivation_triplet triplet; - struct derivation *d; - - d = &triplet.signing; - d->label.iov_base = "SMB2AESCMAC"; - d->label.iov_len = 12; - d->context.iov_base = "SmbSign"; - d->context.iov_len = 8; - - d = &triplet.encryption; - d->label.iov_base = "SMB2AESCCM"; - d->label.iov_len = 11; - d->context.iov_base = "ServerIn "; - d->context.iov_len = 10; - - d = &triplet.decryption; - d->label.iov_base = "SMB2AESCCM"; - d->label.iov_len = 11; - d->context.iov_base = "ServerOut"; - d->context.iov_len = 10; - - return generate_smb3signingkey(ses, server, &triplet); -} - -int -generate_smb311signingkey(struct cifs_ses *ses, - struct TCP_Server_Info *server) - -{ - struct derivation_triplet triplet; - struct derivation *d; - - d = &triplet.signing; - d->label.iov_base = "SMBSigningKey"; - d->label.iov_len = 14; - d->context.iov_base = ses->preauth_sha_hash; - d->context.iov_len = 64; - - d = &triplet.encryption; - d->label.iov_base = "SMBC2SCipherKey"; - d->label.iov_len = 16; - d->context.iov_base = ses->preauth_sha_hash; - d->context.iov_len = 64; - - d = &triplet.decryption; - d->label.iov_base = "SMBS2CCipherKey"; - d->label.iov_len = 16; - d->context.iov_base = ses->preauth_sha_hash; - d->context.iov_len = 64; - - return generate_smb3signingkey(ses, server, &triplet); -} - -int -smb3_calc_signature(struct smb_rqst *rqst, struct TCP_Server_Info *server, - bool allocate_crypto) -{ - int rc; - unsigned char smb3_signature[SMB2_CMACAES_SIZE]; - unsigned char *sigptr = smb3_signature; - struct kvec *iov = rqst->rq_iov; - struct smb2_hdr *shdr = (struct smb2_hdr *)iov[0].iov_base; - struct shash_desc *shash = NULL; - struct smb_rqst drqst; - u8 key[SMB3_SIGN_KEY_SIZE]; - - rc = smb2_get_sign_key(le64_to_cpu(shdr->SessionId), server, key); - if (unlikely(rc)) { - cifs_server_dbg(VFS, "%s: Could not get signing key\n", __func__); - return rc; - } - - if (allocate_crypto) { - rc = cifs_alloc_hash("cmac(aes)", &shash); - if (rc) - return rc; - } else { - shash = server->secmech.aes_cmac; - } - - memset(smb3_signature, 0x0, SMB2_CMACAES_SIZE); - memset(shdr->Signature, 0x0, SMB2_SIGNATURE_SIZE); - - rc = crypto_shash_setkey(shash->tfm, key, SMB2_CMACAES_SIZE); - if (rc) { - cifs_server_dbg(VFS, "%s: Could not set key for cmac aes\n", __func__); - goto out; - } - - /* - * we already allocate aes_cmac when we init smb3 signing key, - * so unlike smb2 case we do not have to check here if secmech are - * initialized - */ - rc = crypto_shash_init(shash); - if (rc) { - cifs_server_dbg(VFS, "%s: Could not init cmac aes\n", __func__); - goto out; - } - - /* - * For SMB2+, __cifs_calc_signature() expects to sign only the actual - * data, that is, iov[0] should not contain a rfc1002 length. - * - * Sign the rfc1002 length prior to passing the data (iov[1-N]) down to - * __cifs_calc_signature(). - */ - drqst = *rqst; - if (drqst.rq_nvec >= 2 && iov[0].iov_len == 4) { - rc = crypto_shash_update(shash, iov[0].iov_base, - iov[0].iov_len); - if (rc) { - cifs_server_dbg(VFS, "%s: Could not update with payload\n", - __func__); - goto out; - } - drqst.rq_iov++; - drqst.rq_nvec--; - } - - rc = __cifs_calc_signature(&drqst, server, sigptr, shash); - if (!rc) - memcpy(shdr->Signature, sigptr, SMB2_SIGNATURE_SIZE); - -out: - if (allocate_crypto) - cifs_free_hash(&shash); - return rc; -} - -/* must be called with server->srv_mutex held */ -static int -smb2_sign_rqst(struct smb_rqst *rqst, struct TCP_Server_Info *server) -{ - int rc = 0; - struct smb2_hdr *shdr; - struct smb2_sess_setup_req *ssr; - bool is_binding; - bool is_signed; - - shdr = (struct smb2_hdr *)rqst->rq_iov[0].iov_base; - ssr = (struct smb2_sess_setup_req *)shdr; - - is_binding = shdr->Command == SMB2_SESSION_SETUP && - (ssr->Flags & SMB2_SESSION_REQ_FLAG_BINDING); - is_signed = shdr->Flags & SMB2_FLAGS_SIGNED; - - if (!is_signed) - return 0; - spin_lock(&server->srv_lock); - if (server->ops->need_neg && - server->ops->need_neg(server)) { - spin_unlock(&server->srv_lock); - return 0; - } - spin_unlock(&server->srv_lock); - if (!is_binding && !server->session_estab) { - strncpy(shdr->Signature, "BSRSPYL", 8); - return 0; - } - - rc = server->ops->calc_signature(rqst, server, false); - - return rc; -} - -int -smb2_verify_signature(struct smb_rqst *rqst, struct TCP_Server_Info *server) -{ - unsigned int rc; - char server_response_sig[SMB2_SIGNATURE_SIZE]; - struct smb2_hdr *shdr = - (struct smb2_hdr *)rqst->rq_iov[0].iov_base; - - if ((shdr->Command == SMB2_NEGOTIATE) || - (shdr->Command == SMB2_SESSION_SETUP) || - (shdr->Command == SMB2_OPLOCK_BREAK) || - server->ignore_signature || - (!server->session_estab)) - return 0; - - /* - * BB what if signatures are supposed to be on for session but - * server does not send one? BB - */ - - /* Do not need to verify session setups with signature "BSRSPYL " */ - if (memcmp(shdr->Signature, "BSRSPYL ", 8) == 0) - cifs_dbg(FYI, "dummy signature received for smb command 0x%x\n", - shdr->Command); - - /* - * Save off the origiginal signature so we can modify the smb and check - * our calculated signature against what the server sent. - */ - memcpy(server_response_sig, shdr->Signature, SMB2_SIGNATURE_SIZE); - - memset(shdr->Signature, 0, SMB2_SIGNATURE_SIZE); - - rc = server->ops->calc_signature(rqst, server, true); - - if (rc) - return rc; - - if (memcmp(server_response_sig, shdr->Signature, SMB2_SIGNATURE_SIZE)) { - cifs_dbg(VFS, "sign fail cmd 0x%x message id 0x%llx\n", - shdr->Command, shdr->MessageId); - return -EACCES; - } else - return 0; -} - -/* - * Set message id for the request. Should be called after wait_for_free_request - * and when srv_mutex is held. - */ -static inline void -smb2_seq_num_into_buf(struct TCP_Server_Info *server, - struct smb2_hdr *shdr) -{ - unsigned int i, num = le16_to_cpu(shdr->CreditCharge); - - shdr->MessageId = get_next_mid64(server); - /* skip message numbers according to CreditCharge field */ - for (i = 1; i < num; i++) - get_next_mid(server); -} - -static struct mid_q_entry * -smb2_mid_entry_alloc(const struct smb2_hdr *shdr, - struct TCP_Server_Info *server) -{ - struct mid_q_entry *temp; - unsigned int credits = le16_to_cpu(shdr->CreditCharge); - - if (server == NULL) { - cifs_dbg(VFS, "Null TCP session in smb2_mid_entry_alloc\n"); - return NULL; - } - - temp = mempool_alloc(cifs_mid_poolp, GFP_NOFS); - memset(temp, 0, sizeof(struct mid_q_entry)); - kref_init(&temp->refcount); - temp->mid = le64_to_cpu(shdr->MessageId); - temp->credits = credits > 0 ? credits : 1; - temp->pid = current->pid; - temp->command = shdr->Command; /* Always LE */ - temp->when_alloc = jiffies; - temp->server = server; - - /* - * The default is for the mid to be synchronous, so the - * default callback just wakes up the current task. - */ - get_task_struct(current); - temp->creator = current; - temp->callback = cifs_wake_up_task; - temp->callback_data = current; - - atomic_inc(&mid_count); - temp->mid_state = MID_REQUEST_ALLOCATED; - trace_smb3_cmd_enter(le32_to_cpu(shdr->Id.SyncId.TreeId), - le64_to_cpu(shdr->SessionId), - le16_to_cpu(shdr->Command), temp->mid); - return temp; -} - -static int -smb2_get_mid_entry(struct cifs_ses *ses, struct TCP_Server_Info *server, - struct smb2_hdr *shdr, struct mid_q_entry **mid) -{ - spin_lock(&server->srv_lock); - if (server->tcpStatus == CifsExiting) { - spin_unlock(&server->srv_lock); - return -ENOENT; - } - - if (server->tcpStatus == CifsNeedReconnect) { - spin_unlock(&server->srv_lock); - cifs_dbg(FYI, "tcp session dead - return to caller to retry\n"); - return -EAGAIN; - } - - if (server->tcpStatus == CifsNeedNegotiate && - shdr->Command != SMB2_NEGOTIATE) { - spin_unlock(&server->srv_lock); - return -EAGAIN; - } - spin_unlock(&server->srv_lock); - - spin_lock(&ses->ses_lock); - if (ses->ses_status == SES_NEW) { - if ((shdr->Command != SMB2_SESSION_SETUP) && - (shdr->Command != SMB2_NEGOTIATE)) { - spin_unlock(&ses->ses_lock); - return -EAGAIN; - } - /* else ok - we are setting up session */ - } - - if (ses->ses_status == SES_EXITING) { - if (shdr->Command != SMB2_LOGOFF) { - spin_unlock(&ses->ses_lock); - return -EAGAIN; - } - /* else ok - we are shutting down the session */ - } - spin_unlock(&ses->ses_lock); - - *mid = smb2_mid_entry_alloc(shdr, server); - if (*mid == NULL) - return -ENOMEM; - spin_lock(&server->mid_lock); - list_add_tail(&(*mid)->qhead, &server->pending_mid_q); - spin_unlock(&server->mid_lock); - - return 0; -} - -int -smb2_check_receive(struct mid_q_entry *mid, struct TCP_Server_Info *server, - bool log_error) -{ - unsigned int len = mid->resp_buf_size; - struct kvec iov[1]; - struct smb_rqst rqst = { .rq_iov = iov, - .rq_nvec = 1 }; - - iov[0].iov_base = (char *)mid->resp_buf; - iov[0].iov_len = len; - - dump_smb(mid->resp_buf, min_t(u32, 80, len)); - /* convert the length into a more usable form */ - if (len > 24 && server->sign && !mid->decrypted) { - int rc; - - rc = smb2_verify_signature(&rqst, server); - if (rc) - cifs_server_dbg(VFS, "SMB signature verification returned error = %d\n", - rc); - } - - return map_smb2_to_linux_error(mid->resp_buf, log_error); -} - -struct mid_q_entry * -smb2_setup_request(struct cifs_ses *ses, struct TCP_Server_Info *server, - struct smb_rqst *rqst) -{ - int rc; - struct smb2_hdr *shdr = - (struct smb2_hdr *)rqst->rq_iov[0].iov_base; - struct mid_q_entry *mid; - - smb2_seq_num_into_buf(server, shdr); - - rc = smb2_get_mid_entry(ses, server, shdr, &mid); - if (rc) { - revert_current_mid_from_hdr(server, shdr); - return ERR_PTR(rc); - } - - rc = smb2_sign_rqst(rqst, server); - if (rc) { - revert_current_mid_from_hdr(server, shdr); - delete_mid(mid); - return ERR_PTR(rc); - } - - return mid; -} - -struct mid_q_entry * -smb2_setup_async_request(struct TCP_Server_Info *server, struct smb_rqst *rqst) -{ - int rc; - struct smb2_hdr *shdr = - (struct smb2_hdr *)rqst->rq_iov[0].iov_base; - struct mid_q_entry *mid; - - spin_lock(&server->srv_lock); - if (server->tcpStatus == CifsNeedNegotiate && - shdr->Command != SMB2_NEGOTIATE) { - spin_unlock(&server->srv_lock); - return ERR_PTR(-EAGAIN); - } - spin_unlock(&server->srv_lock); - - smb2_seq_num_into_buf(server, shdr); - - mid = smb2_mid_entry_alloc(shdr, server); - if (mid == NULL) { - revert_current_mid_from_hdr(server, shdr); - return ERR_PTR(-ENOMEM); - } - - rc = smb2_sign_rqst(rqst, server); - if (rc) { - revert_current_mid_from_hdr(server, shdr); - release_mid(mid); - return ERR_PTR(rc); - } - - return mid; -} - -int -smb3_crypto_aead_allocate(struct TCP_Server_Info *server) -{ - struct crypto_aead *tfm; - - if (!server->secmech.enc) { - if ((server->cipher_type == SMB2_ENCRYPTION_AES128_GCM) || - (server->cipher_type == SMB2_ENCRYPTION_AES256_GCM)) - tfm = crypto_alloc_aead("gcm(aes)", 0, 0); - else - tfm = crypto_alloc_aead("ccm(aes)", 0, 0); - if (IS_ERR(tfm)) { - cifs_server_dbg(VFS, "%s: Failed alloc encrypt aead\n", - __func__); - return PTR_ERR(tfm); - } - server->secmech.enc = tfm; - } - - if (!server->secmech.dec) { - if ((server->cipher_type == SMB2_ENCRYPTION_AES128_GCM) || - (server->cipher_type == SMB2_ENCRYPTION_AES256_GCM)) - tfm = crypto_alloc_aead("gcm(aes)", 0, 0); - else - tfm = crypto_alloc_aead("ccm(aes)", 0, 0); - if (IS_ERR(tfm)) { - crypto_free_aead(server->secmech.enc); - server->secmech.enc = NULL; - cifs_server_dbg(VFS, "%s: Failed to alloc decrypt aead\n", - __func__); - return PTR_ERR(tfm); - } - server->secmech.dec = tfm; - } - - return 0; -} diff --git a/fs/cifs/smbdirect.c b/fs/cifs/smbdirect.c deleted file mode 100644 index cf923f211c512..0000000000000 --- a/fs/cifs/smbdirect.c +++ /dev/null @@ -1,2494 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-or-later -/* - * Copyright (C) 2017, Microsoft Corporation. - * - * Author(s): Long Li - */ -#include -#include -#include "smbdirect.h" -#include "cifs_debug.h" -#include "cifsproto.h" -#include "smb2proto.h" - -static struct smbd_response *get_empty_queue_buffer( - struct smbd_connection *info); -static struct smbd_response *get_receive_buffer( - struct smbd_connection *info); -static void put_receive_buffer( - struct smbd_connection *info, - struct smbd_response *response); -static int allocate_receive_buffers(struct smbd_connection *info, int num_buf); -static void destroy_receive_buffers(struct smbd_connection *info); - -static void put_empty_packet( - struct smbd_connection *info, struct smbd_response *response); -static void enqueue_reassembly( - struct smbd_connection *info, - struct smbd_response *response, int data_length); -static struct smbd_response *_get_first_reassembly( - struct smbd_connection *info); - -static int smbd_post_recv( - struct smbd_connection *info, - struct smbd_response *response); - -static int smbd_post_send_empty(struct smbd_connection *info); -static int smbd_post_send_data( - struct smbd_connection *info, - struct kvec *iov, int n_vec, int remaining_data_length); -static int smbd_post_send_page(struct smbd_connection *info, - struct page *page, unsigned long offset, - size_t size, int remaining_data_length); - -static void destroy_mr_list(struct smbd_connection *info); -static int allocate_mr_list(struct smbd_connection *info); - -/* SMBD version number */ -#define SMBD_V1 0x0100 - -/* Port numbers for SMBD transport */ -#define SMB_PORT 445 -#define SMBD_PORT 5445 - -/* Address lookup and resolve timeout in ms */ -#define RDMA_RESOLVE_TIMEOUT 5000 - -/* SMBD negotiation timeout in seconds */ -#define SMBD_NEGOTIATE_TIMEOUT 120 - -/* SMBD minimum receive size and fragmented sized defined in [MS-SMBD] */ -#define SMBD_MIN_RECEIVE_SIZE 128 -#define SMBD_MIN_FRAGMENTED_SIZE 131072 - -/* - * Default maximum number of RDMA read/write outstanding on this connection - * This value is possibly decreased during QP creation on hardware limit - */ -#define SMBD_CM_RESPONDER_RESOURCES 32 - -/* Maximum number of retries on data transfer operations */ -#define SMBD_CM_RETRY 6 -/* No need to retry on Receiver Not Ready since SMBD manages credits */ -#define SMBD_CM_RNR_RETRY 0 - -/* - * User configurable initial values per SMBD transport connection - * as defined in [MS-SMBD] 3.1.1.1 - * Those may change after a SMBD negotiation - */ -/* The local peer's maximum number of credits to grant to the peer */ -int smbd_receive_credit_max = 255; - -/* The remote peer's credit request of local peer */ -int smbd_send_credit_target = 255; - -/* The maximum single message size can be sent to remote peer */ -int smbd_max_send_size = 1364; - -/* The maximum fragmented upper-layer payload receive size supported */ -int smbd_max_fragmented_recv_size = 1024 * 1024; - -/* The maximum single-message size which can be received */ -int smbd_max_receive_size = 1364; - -/* The timeout to initiate send of a keepalive message on idle */ -int smbd_keep_alive_interval = 120; - -/* - * User configurable initial values for RDMA transport - * The actual values used may be lower and are limited to hardware capabilities - */ -/* Default maximum number of pages in a single RDMA write/read */ -int smbd_max_frmr_depth = 2048; - -/* If payload is less than this byte, use RDMA send/recv not read/write */ -int rdma_readwrite_threshold = 4096; - -/* Transport logging functions - * Logging are defined as classes. They can be OR'ed to define the actual - * logging level via module parameter smbd_logging_class - * e.g. cifs.smbd_logging_class=0xa0 will log all log_rdma_recv() and - * log_rdma_event() - */ -#define LOG_OUTGOING 0x1 -#define LOG_INCOMING 0x2 -#define LOG_READ 0x4 -#define LOG_WRITE 0x8 -#define LOG_RDMA_SEND 0x10 -#define LOG_RDMA_RECV 0x20 -#define LOG_KEEP_ALIVE 0x40 -#define LOG_RDMA_EVENT 0x80 -#define LOG_RDMA_MR 0x100 -static unsigned int smbd_logging_class; -module_param(smbd_logging_class, uint, 0644); -MODULE_PARM_DESC(smbd_logging_class, - "Logging class for SMBD transport 0x0 to 0x100"); - -#define ERR 0x0 -#define INFO 0x1 -static unsigned int smbd_logging_level = ERR; -module_param(smbd_logging_level, uint, 0644); -MODULE_PARM_DESC(smbd_logging_level, - "Logging level for SMBD transport, 0 (default): error, 1: info"); - -#define log_rdma(level, class, fmt, args...) \ -do { \ - if (level <= smbd_logging_level || class & smbd_logging_class) \ - cifs_dbg(VFS, "%s:%d " fmt, __func__, __LINE__, ##args);\ -} while (0) - -#define log_outgoing(level, fmt, args...) \ - log_rdma(level, LOG_OUTGOING, fmt, ##args) -#define log_incoming(level, fmt, args...) \ - log_rdma(level, LOG_INCOMING, fmt, ##args) -#define log_read(level, fmt, args...) log_rdma(level, LOG_READ, fmt, ##args) -#define log_write(level, fmt, args...) log_rdma(level, LOG_WRITE, fmt, ##args) -#define log_rdma_send(level, fmt, args...) \ - log_rdma(level, LOG_RDMA_SEND, fmt, ##args) -#define log_rdma_recv(level, fmt, args...) \ - log_rdma(level, LOG_RDMA_RECV, fmt, ##args) -#define log_keep_alive(level, fmt, args...) \ - log_rdma(level, LOG_KEEP_ALIVE, fmt, ##args) -#define log_rdma_event(level, fmt, args...) \ - log_rdma(level, LOG_RDMA_EVENT, fmt, ##args) -#define log_rdma_mr(level, fmt, args...) \ - log_rdma(level, LOG_RDMA_MR, fmt, ##args) - -static void smbd_disconnect_rdma_work(struct work_struct *work) -{ - struct smbd_connection *info = - container_of(work, struct smbd_connection, disconnect_work); - - if (info->transport_status == SMBD_CONNECTED) { - info->transport_status = SMBD_DISCONNECTING; - rdma_disconnect(info->id); - } -} - -static void smbd_disconnect_rdma_connection(struct smbd_connection *info) -{ - queue_work(info->workqueue, &info->disconnect_work); -} - -/* Upcall from RDMA CM */ -static int smbd_conn_upcall( - struct rdma_cm_id *id, struct rdma_cm_event *event) -{ - struct smbd_connection *info = id->context; - - log_rdma_event(INFO, "event=%d status=%d\n", - event->event, event->status); - - switch (event->event) { - case RDMA_CM_EVENT_ADDR_RESOLVED: - case RDMA_CM_EVENT_ROUTE_RESOLVED: - info->ri_rc = 0; - complete(&info->ri_done); - break; - - case RDMA_CM_EVENT_ADDR_ERROR: - info->ri_rc = -EHOSTUNREACH; - complete(&info->ri_done); - break; - - case RDMA_CM_EVENT_ROUTE_ERROR: - info->ri_rc = -ENETUNREACH; - complete(&info->ri_done); - break; - - case RDMA_CM_EVENT_ESTABLISHED: - log_rdma_event(INFO, "connected event=%d\n", event->event); - info->transport_status = SMBD_CONNECTED; - wake_up_interruptible(&info->conn_wait); - break; - - case RDMA_CM_EVENT_CONNECT_ERROR: - case RDMA_CM_EVENT_UNREACHABLE: - case RDMA_CM_EVENT_REJECTED: - log_rdma_event(INFO, "connecting failed event=%d\n", event->event); - info->transport_status = SMBD_DISCONNECTED; - wake_up_interruptible(&info->conn_wait); - break; - - case RDMA_CM_EVENT_DEVICE_REMOVAL: - case RDMA_CM_EVENT_DISCONNECTED: - /* This happenes when we fail the negotiation */ - if (info->transport_status == SMBD_NEGOTIATE_FAILED) { - info->transport_status = SMBD_DISCONNECTED; - wake_up(&info->conn_wait); - break; - } - - info->transport_status = SMBD_DISCONNECTED; - wake_up_interruptible(&info->disconn_wait); - wake_up_interruptible(&info->wait_reassembly_queue); - wake_up_interruptible_all(&info->wait_send_queue); - break; - - default: - break; - } - - return 0; -} - -/* Upcall from RDMA QP */ -static void -smbd_qp_async_error_upcall(struct ib_event *event, void *context) -{ - struct smbd_connection *info = context; - - log_rdma_event(ERR, "%s on device %s info %p\n", - ib_event_msg(event->event), event->device->name, info); - - switch (event->event) { - case IB_EVENT_CQ_ERR: - case IB_EVENT_QP_FATAL: - smbd_disconnect_rdma_connection(info); - break; - - default: - break; - } -} - -static inline void *smbd_request_payload(struct smbd_request *request) -{ - return (void *)request->packet; -} - -static inline void *smbd_response_payload(struct smbd_response *response) -{ - return (void *)response->packet; -} - -/* Called when a RDMA send is done */ -static void send_done(struct ib_cq *cq, struct ib_wc *wc) -{ - int i; - struct smbd_request *request = - container_of(wc->wr_cqe, struct smbd_request, cqe); - - log_rdma_send(INFO, "smbd_request 0x%p completed wc->status=%d\n", - request, wc->status); - - if (wc->status != IB_WC_SUCCESS || wc->opcode != IB_WC_SEND) { - log_rdma_send(ERR, "wc->status=%d wc->opcode=%d\n", - wc->status, wc->opcode); - smbd_disconnect_rdma_connection(request->info); - } - - for (i = 0; i < request->num_sge; i++) - ib_dma_unmap_single(request->info->id->device, - request->sge[i].addr, - request->sge[i].length, - DMA_TO_DEVICE); - - if (atomic_dec_and_test(&request->info->send_pending)) - wake_up(&request->info->wait_send_pending); - - wake_up(&request->info->wait_post_send); - - mempool_free(request, request->info->request_mempool); -} - -static void dump_smbd_negotiate_resp(struct smbd_negotiate_resp *resp) -{ - log_rdma_event(INFO, "resp message min_version %u max_version %u negotiated_version %u credits_requested %u credits_granted %u status %u max_readwrite_size %u preferred_send_size %u max_receive_size %u max_fragmented_size %u\n", - resp->min_version, resp->max_version, - resp->negotiated_version, resp->credits_requested, - resp->credits_granted, resp->status, - resp->max_readwrite_size, resp->preferred_send_size, - resp->max_receive_size, resp->max_fragmented_size); -} - -/* - * Process a negotiation response message, according to [MS-SMBD]3.1.5.7 - * response, packet_length: the negotiation response message - * return value: true if negotiation is a success, false if failed - */ -static bool process_negotiation_response( - struct smbd_response *response, int packet_length) -{ - struct smbd_connection *info = response->info; - struct smbd_negotiate_resp *packet = smbd_response_payload(response); - - if (packet_length < sizeof(struct smbd_negotiate_resp)) { - log_rdma_event(ERR, - "error: packet_length=%d\n", packet_length); - return false; - } - - if (le16_to_cpu(packet->negotiated_version) != SMBD_V1) { - log_rdma_event(ERR, "error: negotiated_version=%x\n", - le16_to_cpu(packet->negotiated_version)); - return false; - } - info->protocol = le16_to_cpu(packet->negotiated_version); - - if (packet->credits_requested == 0) { - log_rdma_event(ERR, "error: credits_requested==0\n"); - return false; - } - info->receive_credit_target = le16_to_cpu(packet->credits_requested); - - if (packet->credits_granted == 0) { - log_rdma_event(ERR, "error: credits_granted==0\n"); - return false; - } - atomic_set(&info->send_credits, le16_to_cpu(packet->credits_granted)); - - atomic_set(&info->receive_credits, 0); - - if (le32_to_cpu(packet->preferred_send_size) > info->max_receive_size) { - log_rdma_event(ERR, "error: preferred_send_size=%d\n", - le32_to_cpu(packet->preferred_send_size)); - return false; - } - info->max_receive_size = le32_to_cpu(packet->preferred_send_size); - - if (le32_to_cpu(packet->max_receive_size) < SMBD_MIN_RECEIVE_SIZE) { - log_rdma_event(ERR, "error: max_receive_size=%d\n", - le32_to_cpu(packet->max_receive_size)); - return false; - } - info->max_send_size = min_t(int, info->max_send_size, - le32_to_cpu(packet->max_receive_size)); - - if (le32_to_cpu(packet->max_fragmented_size) < - SMBD_MIN_FRAGMENTED_SIZE) { - log_rdma_event(ERR, "error: max_fragmented_size=%d\n", - le32_to_cpu(packet->max_fragmented_size)); - return false; - } - info->max_fragmented_send_size = - le32_to_cpu(packet->max_fragmented_size); - info->rdma_readwrite_threshold = - rdma_readwrite_threshold > info->max_fragmented_send_size ? - info->max_fragmented_send_size : - rdma_readwrite_threshold; - - - info->max_readwrite_size = min_t(u32, - le32_to_cpu(packet->max_readwrite_size), - info->max_frmr_depth * PAGE_SIZE); - info->max_frmr_depth = info->max_readwrite_size / PAGE_SIZE; - - return true; -} - -static void smbd_post_send_credits(struct work_struct *work) -{ - int ret = 0; - int use_receive_queue = 1; - int rc; - struct smbd_response *response; - struct smbd_connection *info = - container_of(work, struct smbd_connection, - post_send_credits_work); - - if (info->transport_status != SMBD_CONNECTED) { - wake_up(&info->wait_receive_queues); - return; - } - - if (info->receive_credit_target > - atomic_read(&info->receive_credits)) { - while (true) { - if (use_receive_queue) - response = get_receive_buffer(info); - else - response = get_empty_queue_buffer(info); - if (!response) { - /* now switch to emtpy packet queue */ - if (use_receive_queue) { - use_receive_queue = 0; - continue; - } else - break; - } - - response->type = SMBD_TRANSFER_DATA; - response->first_segment = false; - rc = smbd_post_recv(info, response); - if (rc) { - log_rdma_recv(ERR, - "post_recv failed rc=%d\n", rc); - put_receive_buffer(info, response); - break; - } - - ret++; - } - } - - spin_lock(&info->lock_new_credits_offered); - info->new_credits_offered += ret; - spin_unlock(&info->lock_new_credits_offered); - - /* Promptly send an immediate packet as defined in [MS-SMBD] 3.1.1.1 */ - info->send_immediate = true; - if (atomic_read(&info->receive_credits) < - info->receive_credit_target - 1) { - if (info->keep_alive_requested == KEEP_ALIVE_PENDING || - info->send_immediate) { - log_keep_alive(INFO, "send an empty message\n"); - smbd_post_send_empty(info); - } - } -} - -/* Called from softirq, when recv is done */ -static void recv_done(struct ib_cq *cq, struct ib_wc *wc) -{ - struct smbd_data_transfer *data_transfer; - struct smbd_response *response = - container_of(wc->wr_cqe, struct smbd_response, cqe); - struct smbd_connection *info = response->info; - int data_length = 0; - - log_rdma_recv(INFO, "response=0x%p type=%d wc status=%d wc opcode %d byte_len=%d pkey_index=%u\n", - response, response->type, wc->status, wc->opcode, - wc->byte_len, wc->pkey_index); - - if (wc->status != IB_WC_SUCCESS || wc->opcode != IB_WC_RECV) { - log_rdma_recv(INFO, "wc->status=%d opcode=%d\n", - wc->status, wc->opcode); - smbd_disconnect_rdma_connection(info); - goto error; - } - - ib_dma_sync_single_for_cpu( - wc->qp->device, - response->sge.addr, - response->sge.length, - DMA_FROM_DEVICE); - - switch (response->type) { - /* SMBD negotiation response */ - case SMBD_NEGOTIATE_RESP: - dump_smbd_negotiate_resp(smbd_response_payload(response)); - info->full_packet_received = true; - info->negotiate_done = - process_negotiation_response(response, wc->byte_len); - complete(&info->negotiate_completion); - break; - - /* SMBD data transfer packet */ - case SMBD_TRANSFER_DATA: - data_transfer = smbd_response_payload(response); - data_length = le32_to_cpu(data_transfer->data_length); - - /* - * If this is a packet with data playload place the data in - * reassembly queue and wake up the reading thread - */ - if (data_length) { - if (info->full_packet_received) - response->first_segment = true; - - if (le32_to_cpu(data_transfer->remaining_data_length)) - info->full_packet_received = false; - else - info->full_packet_received = true; - - enqueue_reassembly( - info, - response, - data_length); - } else - put_empty_packet(info, response); - - if (data_length) - wake_up_interruptible(&info->wait_reassembly_queue); - - atomic_dec(&info->receive_credits); - info->receive_credit_target = - le16_to_cpu(data_transfer->credits_requested); - if (le16_to_cpu(data_transfer->credits_granted)) { - atomic_add(le16_to_cpu(data_transfer->credits_granted), - &info->send_credits); - /* - * We have new send credits granted from remote peer - * If any sender is waiting for credits, unblock it - */ - wake_up_interruptible(&info->wait_send_queue); - } - - log_incoming(INFO, "data flags %d data_offset %d data_length %d remaining_data_length %d\n", - le16_to_cpu(data_transfer->flags), - le32_to_cpu(data_transfer->data_offset), - le32_to_cpu(data_transfer->data_length), - le32_to_cpu(data_transfer->remaining_data_length)); - - /* Send a KEEP_ALIVE response right away if requested */ - info->keep_alive_requested = KEEP_ALIVE_NONE; - if (le16_to_cpu(data_transfer->flags) & - SMB_DIRECT_RESPONSE_REQUESTED) { - info->keep_alive_requested = KEEP_ALIVE_PENDING; - } - - return; - - default: - log_rdma_recv(ERR, - "unexpected response type=%d\n", response->type); - } - -error: - put_receive_buffer(info, response); -} - -static struct rdma_cm_id *smbd_create_id( - struct smbd_connection *info, - struct sockaddr *dstaddr, int port) -{ - struct rdma_cm_id *id; - int rc; - __be16 *sport; - - id = rdma_create_id(&init_net, smbd_conn_upcall, info, - RDMA_PS_TCP, IB_QPT_RC); - if (IS_ERR(id)) { - rc = PTR_ERR(id); - log_rdma_event(ERR, "rdma_create_id() failed %i\n", rc); - return id; - } - - if (dstaddr->sa_family == AF_INET6) - sport = &((struct sockaddr_in6 *)dstaddr)->sin6_port; - else - sport = &((struct sockaddr_in *)dstaddr)->sin_port; - - *sport = htons(port); - - init_completion(&info->ri_done); - info->ri_rc = -ETIMEDOUT; - - rc = rdma_resolve_addr(id, NULL, (struct sockaddr *)dstaddr, - RDMA_RESOLVE_TIMEOUT); - if (rc) { - log_rdma_event(ERR, "rdma_resolve_addr() failed %i\n", rc); - goto out; - } - rc = wait_for_completion_interruptible_timeout( - &info->ri_done, msecs_to_jiffies(RDMA_RESOLVE_TIMEOUT)); - /* e.g. if interrupted returns -ERESTARTSYS */ - if (rc < 0) { - log_rdma_event(ERR, "rdma_resolve_addr timeout rc: %i\n", rc); - goto out; - } - rc = info->ri_rc; - if (rc) { - log_rdma_event(ERR, "rdma_resolve_addr() completed %i\n", rc); - goto out; - } - - info->ri_rc = -ETIMEDOUT; - rc = rdma_resolve_route(id, RDMA_RESOLVE_TIMEOUT); - if (rc) { - log_rdma_event(ERR, "rdma_resolve_route() failed %i\n", rc); - goto out; - } - rc = wait_for_completion_interruptible_timeout( - &info->ri_done, msecs_to_jiffies(RDMA_RESOLVE_TIMEOUT)); - /* e.g. if interrupted returns -ERESTARTSYS */ - if (rc < 0) { - log_rdma_event(ERR, "rdma_resolve_addr timeout rc: %i\n", rc); - goto out; - } - rc = info->ri_rc; - if (rc) { - log_rdma_event(ERR, "rdma_resolve_route() completed %i\n", rc); - goto out; - } - - return id; - -out: - rdma_destroy_id(id); - return ERR_PTR(rc); -} - -/* - * Test if FRWR (Fast Registration Work Requests) is supported on the device - * This implementation requries FRWR on RDMA read/write - * return value: true if it is supported - */ -static bool frwr_is_supported(struct ib_device_attr *attrs) -{ - if (!(attrs->device_cap_flags & IB_DEVICE_MEM_MGT_EXTENSIONS)) - return false; - if (attrs->max_fast_reg_page_list_len == 0) - return false; - return true; -} - -static int smbd_ia_open( - struct smbd_connection *info, - struct sockaddr *dstaddr, int port) -{ - int rc; - - info->id = smbd_create_id(info, dstaddr, port); - if (IS_ERR(info->id)) { - rc = PTR_ERR(info->id); - goto out1; - } - - if (!frwr_is_supported(&info->id->device->attrs)) { - log_rdma_event(ERR, "Fast Registration Work Requests (FRWR) is not supported\n"); - log_rdma_event(ERR, "Device capability flags = %llx max_fast_reg_page_list_len = %u\n", - info->id->device->attrs.device_cap_flags, - info->id->device->attrs.max_fast_reg_page_list_len); - rc = -EPROTONOSUPPORT; - goto out2; - } - info->max_frmr_depth = min_t(int, - smbd_max_frmr_depth, - info->id->device->attrs.max_fast_reg_page_list_len); - info->mr_type = IB_MR_TYPE_MEM_REG; - if (info->id->device->attrs.kernel_cap_flags & IBK_SG_GAPS_REG) - info->mr_type = IB_MR_TYPE_SG_GAPS; - - info->pd = ib_alloc_pd(info->id->device, 0); - if (IS_ERR(info->pd)) { - rc = PTR_ERR(info->pd); - log_rdma_event(ERR, "ib_alloc_pd() returned %d\n", rc); - goto out2; - } - - return 0; - -out2: - rdma_destroy_id(info->id); - info->id = NULL; - -out1: - return rc; -} - -/* - * Send a negotiation request message to the peer - * The negotiation procedure is in [MS-SMBD] 3.1.5.2 and 3.1.5.3 - * After negotiation, the transport is connected and ready for - * carrying upper layer SMB payload - */ -static int smbd_post_send_negotiate_req(struct smbd_connection *info) -{ - struct ib_send_wr send_wr; - int rc = -ENOMEM; - struct smbd_request *request; - struct smbd_negotiate_req *packet; - - request = mempool_alloc(info->request_mempool, GFP_KERNEL); - if (!request) - return rc; - - request->info = info; - - packet = smbd_request_payload(request); - packet->min_version = cpu_to_le16(SMBD_V1); - packet->max_version = cpu_to_le16(SMBD_V1); - packet->reserved = 0; - packet->credits_requested = cpu_to_le16(info->send_credit_target); - packet->preferred_send_size = cpu_to_le32(info->max_send_size); - packet->max_receive_size = cpu_to_le32(info->max_receive_size); - packet->max_fragmented_size = - cpu_to_le32(info->max_fragmented_recv_size); - - request->num_sge = 1; - request->sge[0].addr = ib_dma_map_single( - info->id->device, (void *)packet, - sizeof(*packet), DMA_TO_DEVICE); - if (ib_dma_mapping_error(info->id->device, request->sge[0].addr)) { - rc = -EIO; - goto dma_mapping_failed; - } - - request->sge[0].length = sizeof(*packet); - request->sge[0].lkey = info->pd->local_dma_lkey; - - ib_dma_sync_single_for_device( - info->id->device, request->sge[0].addr, - request->sge[0].length, DMA_TO_DEVICE); - - request->cqe.done = send_done; - - send_wr.next = NULL; - send_wr.wr_cqe = &request->cqe; - send_wr.sg_list = request->sge; - send_wr.num_sge = request->num_sge; - send_wr.opcode = IB_WR_SEND; - send_wr.send_flags = IB_SEND_SIGNALED; - - log_rdma_send(INFO, "sge addr=0x%llx length=%u lkey=0x%x\n", - request->sge[0].addr, - request->sge[0].length, request->sge[0].lkey); - - atomic_inc(&info->send_pending); - rc = ib_post_send(info->id->qp, &send_wr, NULL); - if (!rc) - return 0; - - /* if we reach here, post send failed */ - log_rdma_send(ERR, "ib_post_send failed rc=%d\n", rc); - atomic_dec(&info->send_pending); - ib_dma_unmap_single(info->id->device, request->sge[0].addr, - request->sge[0].length, DMA_TO_DEVICE); - - smbd_disconnect_rdma_connection(info); - -dma_mapping_failed: - mempool_free(request, info->request_mempool); - return rc; -} - -/* - * Extend the credits to remote peer - * This implements [MS-SMBD] 3.1.5.9 - * The idea is that we should extend credits to remote peer as quickly as - * it's allowed, to maintain data flow. We allocate as much receive - * buffer as possible, and extend the receive credits to remote peer - * return value: the new credtis being granted. - */ -static int manage_credits_prior_sending(struct smbd_connection *info) -{ - int new_credits; - - spin_lock(&info->lock_new_credits_offered); - new_credits = info->new_credits_offered; - info->new_credits_offered = 0; - spin_unlock(&info->lock_new_credits_offered); - - return new_credits; -} - -/* - * Check if we need to send a KEEP_ALIVE message - * The idle connection timer triggers a KEEP_ALIVE message when expires - * SMB_DIRECT_RESPONSE_REQUESTED is set in the message flag to have peer send - * back a response. - * return value: - * 1 if SMB_DIRECT_RESPONSE_REQUESTED needs to be set - * 0: otherwise - */ -static int manage_keep_alive_before_sending(struct smbd_connection *info) -{ - if (info->keep_alive_requested == KEEP_ALIVE_PENDING) { - info->keep_alive_requested = KEEP_ALIVE_SENT; - return 1; - } - return 0; -} - -/* Post the send request */ -static int smbd_post_send(struct smbd_connection *info, - struct smbd_request *request) -{ - struct ib_send_wr send_wr; - int rc, i; - - for (i = 0; i < request->num_sge; i++) { - log_rdma_send(INFO, - "rdma_request sge[%d] addr=0x%llx length=%u\n", - i, request->sge[i].addr, request->sge[i].length); - ib_dma_sync_single_for_device( - info->id->device, - request->sge[i].addr, - request->sge[i].length, - DMA_TO_DEVICE); - } - - request->cqe.done = send_done; - - send_wr.next = NULL; - send_wr.wr_cqe = &request->cqe; - send_wr.sg_list = request->sge; - send_wr.num_sge = request->num_sge; - send_wr.opcode = IB_WR_SEND; - send_wr.send_flags = IB_SEND_SIGNALED; - - rc = ib_post_send(info->id->qp, &send_wr, NULL); - if (rc) { - log_rdma_send(ERR, "ib_post_send failed rc=%d\n", rc); - smbd_disconnect_rdma_connection(info); - rc = -EAGAIN; - } else - /* Reset timer for idle connection after packet is sent */ - mod_delayed_work(info->workqueue, &info->idle_timer_work, - info->keep_alive_interval*HZ); - - return rc; -} - -static int smbd_post_send_sgl(struct smbd_connection *info, - struct scatterlist *sgl, int data_length, int remaining_data_length) -{ - int num_sgs; - int i, rc; - int header_length; - struct smbd_request *request; - struct smbd_data_transfer *packet; - int new_credits; - struct scatterlist *sg; - -wait_credit: - /* Wait for send credits. A SMBD packet needs one credit */ - rc = wait_event_interruptible(info->wait_send_queue, - atomic_read(&info->send_credits) > 0 || - info->transport_status != SMBD_CONNECTED); - if (rc) - goto err_wait_credit; - - if (info->transport_status != SMBD_CONNECTED) { - log_outgoing(ERR, "disconnected not sending on wait_credit\n"); - rc = -EAGAIN; - goto err_wait_credit; - } - if (unlikely(atomic_dec_return(&info->send_credits) < 0)) { - atomic_inc(&info->send_credits); - goto wait_credit; - } - -wait_send_queue: - wait_event(info->wait_post_send, - atomic_read(&info->send_pending) < info->send_credit_target || - info->transport_status != SMBD_CONNECTED); - - if (info->transport_status != SMBD_CONNECTED) { - log_outgoing(ERR, "disconnected not sending on wait_send_queue\n"); - rc = -EAGAIN; - goto err_wait_send_queue; - } - - if (unlikely(atomic_inc_return(&info->send_pending) > - info->send_credit_target)) { - atomic_dec(&info->send_pending); - goto wait_send_queue; - } - - request = mempool_alloc(info->request_mempool, GFP_KERNEL); - if (!request) { - rc = -ENOMEM; - goto err_alloc; - } - - request->info = info; - - /* Fill in the packet header */ - packet = smbd_request_payload(request); - packet->credits_requested = cpu_to_le16(info->send_credit_target); - - new_credits = manage_credits_prior_sending(info); - atomic_add(new_credits, &info->receive_credits); - packet->credits_granted = cpu_to_le16(new_credits); - - info->send_immediate = false; - - packet->flags = 0; - if (manage_keep_alive_before_sending(info)) - packet->flags |= cpu_to_le16(SMB_DIRECT_RESPONSE_REQUESTED); - - packet->reserved = 0; - if (!data_length) - packet->data_offset = 0; - else - packet->data_offset = cpu_to_le32(24); - packet->data_length = cpu_to_le32(data_length); - packet->remaining_data_length = cpu_to_le32(remaining_data_length); - packet->padding = 0; - - log_outgoing(INFO, "credits_requested=%d credits_granted=%d data_offset=%d data_length=%d remaining_data_length=%d\n", - le16_to_cpu(packet->credits_requested), - le16_to_cpu(packet->credits_granted), - le32_to_cpu(packet->data_offset), - le32_to_cpu(packet->data_length), - le32_to_cpu(packet->remaining_data_length)); - - /* Map the packet to DMA */ - header_length = sizeof(struct smbd_data_transfer); - /* If this is a packet without payload, don't send padding */ - if (!data_length) - header_length = offsetof(struct smbd_data_transfer, padding); - - request->num_sge = 1; - request->sge[0].addr = ib_dma_map_single(info->id->device, - (void *)packet, - header_length, - DMA_TO_DEVICE); - if (ib_dma_mapping_error(info->id->device, request->sge[0].addr)) { - rc = -EIO; - request->sge[0].addr = 0; - goto err_dma; - } - - request->sge[0].length = header_length; - request->sge[0].lkey = info->pd->local_dma_lkey; - - /* Fill in the packet data payload */ - num_sgs = sgl ? sg_nents(sgl) : 0; - for_each_sg(sgl, sg, num_sgs, i) { - request->sge[i+1].addr = - ib_dma_map_page(info->id->device, sg_page(sg), - sg->offset, sg->length, DMA_TO_DEVICE); - if (ib_dma_mapping_error( - info->id->device, request->sge[i+1].addr)) { - rc = -EIO; - request->sge[i+1].addr = 0; - goto err_dma; - } - request->sge[i+1].length = sg->length; - request->sge[i+1].lkey = info->pd->local_dma_lkey; - request->num_sge++; - } - - rc = smbd_post_send(info, request); - if (!rc) - return 0; - -err_dma: - for (i = 0; i < request->num_sge; i++) - if (request->sge[i].addr) - ib_dma_unmap_single(info->id->device, - request->sge[i].addr, - request->sge[i].length, - DMA_TO_DEVICE); - mempool_free(request, info->request_mempool); - - /* roll back receive credits and credits to be offered */ - spin_lock(&info->lock_new_credits_offered); - info->new_credits_offered += new_credits; - spin_unlock(&info->lock_new_credits_offered); - atomic_sub(new_credits, &info->receive_credits); - -err_alloc: - if (atomic_dec_and_test(&info->send_pending)) - wake_up(&info->wait_send_pending); - -err_wait_send_queue: - /* roll back send credits and pending */ - atomic_inc(&info->send_credits); - -err_wait_credit: - return rc; -} - -/* - * Send a page - * page: the page to send - * offset: offset in the page to send - * size: length in the page to send - * remaining_data_length: remaining data to send in this payload - */ -static int smbd_post_send_page(struct smbd_connection *info, struct page *page, - unsigned long offset, size_t size, int remaining_data_length) -{ - struct scatterlist sgl; - - sg_init_table(&sgl, 1); - sg_set_page(&sgl, page, size, offset); - - return smbd_post_send_sgl(info, &sgl, size, remaining_data_length); -} - -/* - * Send an empty message - * Empty message is used to extend credits to peer to for keep live - * while there is no upper layer payload to send at the time - */ -static int smbd_post_send_empty(struct smbd_connection *info) -{ - info->count_send_empty++; - return smbd_post_send_sgl(info, NULL, 0, 0); -} - -/* - * Send a data buffer - * iov: the iov array describing the data buffers - * n_vec: number of iov array - * remaining_data_length: remaining data to send following this packet - * in segmented SMBD packet - */ -static int smbd_post_send_data( - struct smbd_connection *info, struct kvec *iov, int n_vec, - int remaining_data_length) -{ - int i; - u32 data_length = 0; - struct scatterlist sgl[SMBDIRECT_MAX_SEND_SGE - 1]; - - if (n_vec > SMBDIRECT_MAX_SEND_SGE - 1) { - cifs_dbg(VFS, "Can't fit data to SGL, n_vec=%d\n", n_vec); - return -EINVAL; - } - - sg_init_table(sgl, n_vec); - for (i = 0; i < n_vec; i++) { - data_length += iov[i].iov_len; - sg_set_buf(&sgl[i], iov[i].iov_base, iov[i].iov_len); - } - - return smbd_post_send_sgl(info, sgl, data_length, remaining_data_length); -} - -/* - * Post a receive request to the transport - * The remote peer can only send data when a receive request is posted - * The interaction is controlled by send/receive credit system - */ -static int smbd_post_recv( - struct smbd_connection *info, struct smbd_response *response) -{ - struct ib_recv_wr recv_wr; - int rc = -EIO; - - response->sge.addr = ib_dma_map_single( - info->id->device, response->packet, - info->max_receive_size, DMA_FROM_DEVICE); - if (ib_dma_mapping_error(info->id->device, response->sge.addr)) - return rc; - - response->sge.length = info->max_receive_size; - response->sge.lkey = info->pd->local_dma_lkey; - - response->cqe.done = recv_done; - - recv_wr.wr_cqe = &response->cqe; - recv_wr.next = NULL; - recv_wr.sg_list = &response->sge; - recv_wr.num_sge = 1; - - rc = ib_post_recv(info->id->qp, &recv_wr, NULL); - if (rc) { - ib_dma_unmap_single(info->id->device, response->sge.addr, - response->sge.length, DMA_FROM_DEVICE); - smbd_disconnect_rdma_connection(info); - log_rdma_recv(ERR, "ib_post_recv failed rc=%d\n", rc); - } - - return rc; -} - -/* Perform SMBD negotiate according to [MS-SMBD] 3.1.5.2 */ -static int smbd_negotiate(struct smbd_connection *info) -{ - int rc; - struct smbd_response *response = get_receive_buffer(info); - - response->type = SMBD_NEGOTIATE_RESP; - rc = smbd_post_recv(info, response); - log_rdma_event(INFO, "smbd_post_recv rc=%d iov.addr=0x%llx iov.length=%u iov.lkey=0x%x\n", - rc, response->sge.addr, - response->sge.length, response->sge.lkey); - if (rc) - return rc; - - init_completion(&info->negotiate_completion); - info->negotiate_done = false; - rc = smbd_post_send_negotiate_req(info); - if (rc) - return rc; - - rc = wait_for_completion_interruptible_timeout( - &info->negotiate_completion, SMBD_NEGOTIATE_TIMEOUT * HZ); - log_rdma_event(INFO, "wait_for_completion_timeout rc=%d\n", rc); - - if (info->negotiate_done) - return 0; - - if (rc == 0) - rc = -ETIMEDOUT; - else if (rc == -ERESTARTSYS) - rc = -EINTR; - else - rc = -ENOTCONN; - - return rc; -} - -static void put_empty_packet( - struct smbd_connection *info, struct smbd_response *response) -{ - spin_lock(&info->empty_packet_queue_lock); - list_add_tail(&response->list, &info->empty_packet_queue); - info->count_empty_packet_queue++; - spin_unlock(&info->empty_packet_queue_lock); - - queue_work(info->workqueue, &info->post_send_credits_work); -} - -/* - * Implement Connection.FragmentReassemblyBuffer defined in [MS-SMBD] 3.1.1.1 - * This is a queue for reassembling upper layer payload and present to upper - * layer. All the inncoming payload go to the reassembly queue, regardless of - * if reassembly is required. The uuper layer code reads from the queue for all - * incoming payloads. - * Put a received packet to the reassembly queue - * response: the packet received - * data_length: the size of payload in this packet - */ -static void enqueue_reassembly( - struct smbd_connection *info, - struct smbd_response *response, - int data_length) -{ - spin_lock(&info->reassembly_queue_lock); - list_add_tail(&response->list, &info->reassembly_queue); - info->reassembly_queue_length++; - /* - * Make sure reassembly_data_length is updated after list and - * reassembly_queue_length are updated. On the dequeue side - * reassembly_data_length is checked without a lock to determine - * if reassembly_queue_length and list is up to date - */ - virt_wmb(); - info->reassembly_data_length += data_length; - spin_unlock(&info->reassembly_queue_lock); - info->count_reassembly_queue++; - info->count_enqueue_reassembly_queue++; -} - -/* - * Get the first entry at the front of reassembly queue - * Caller is responsible for locking - * return value: the first entry if any, NULL if queue is empty - */ -static struct smbd_response *_get_first_reassembly(struct smbd_connection *info) -{ - struct smbd_response *ret = NULL; - - if (!list_empty(&info->reassembly_queue)) { - ret = list_first_entry( - &info->reassembly_queue, - struct smbd_response, list); - } - return ret; -} - -static struct smbd_response *get_empty_queue_buffer( - struct smbd_connection *info) -{ - struct smbd_response *ret = NULL; - unsigned long flags; - - spin_lock_irqsave(&info->empty_packet_queue_lock, flags); - if (!list_empty(&info->empty_packet_queue)) { - ret = list_first_entry( - &info->empty_packet_queue, - struct smbd_response, list); - list_del(&ret->list); - info->count_empty_packet_queue--; - } - spin_unlock_irqrestore(&info->empty_packet_queue_lock, flags); - - return ret; -} - -/* - * Get a receive buffer - * For each remote send, we need to post a receive. The receive buffers are - * pre-allocated in advance. - * return value: the receive buffer, NULL if none is available - */ -static struct smbd_response *get_receive_buffer(struct smbd_connection *info) -{ - struct smbd_response *ret = NULL; - unsigned long flags; - - spin_lock_irqsave(&info->receive_queue_lock, flags); - if (!list_empty(&info->receive_queue)) { - ret = list_first_entry( - &info->receive_queue, - struct smbd_response, list); - list_del(&ret->list); - info->count_receive_queue--; - info->count_get_receive_buffer++; - } - spin_unlock_irqrestore(&info->receive_queue_lock, flags); - - return ret; -} - -/* - * Return a receive buffer - * Upon returning of a receive buffer, we can post new receive and extend - * more receive credits to remote peer. This is done immediately after a - * receive buffer is returned. - */ -static void put_receive_buffer( - struct smbd_connection *info, struct smbd_response *response) -{ - unsigned long flags; - - ib_dma_unmap_single(info->id->device, response->sge.addr, - response->sge.length, DMA_FROM_DEVICE); - - spin_lock_irqsave(&info->receive_queue_lock, flags); - list_add_tail(&response->list, &info->receive_queue); - info->count_receive_queue++; - info->count_put_receive_buffer++; - spin_unlock_irqrestore(&info->receive_queue_lock, flags); - - queue_work(info->workqueue, &info->post_send_credits_work); -} - -/* Preallocate all receive buffer on transport establishment */ -static int allocate_receive_buffers(struct smbd_connection *info, int num_buf) -{ - int i; - struct smbd_response *response; - - INIT_LIST_HEAD(&info->reassembly_queue); - spin_lock_init(&info->reassembly_queue_lock); - info->reassembly_data_length = 0; - info->reassembly_queue_length = 0; - - INIT_LIST_HEAD(&info->receive_queue); - spin_lock_init(&info->receive_queue_lock); - info->count_receive_queue = 0; - - INIT_LIST_HEAD(&info->empty_packet_queue); - spin_lock_init(&info->empty_packet_queue_lock); - info->count_empty_packet_queue = 0; - - init_waitqueue_head(&info->wait_receive_queues); - - for (i = 0; i < num_buf; i++) { - response = mempool_alloc(info->response_mempool, GFP_KERNEL); - if (!response) - goto allocate_failed; - - response->info = info; - list_add_tail(&response->list, &info->receive_queue); - info->count_receive_queue++; - } - - return 0; - -allocate_failed: - while (!list_empty(&info->receive_queue)) { - response = list_first_entry( - &info->receive_queue, - struct smbd_response, list); - list_del(&response->list); - info->count_receive_queue--; - - mempool_free(response, info->response_mempool); - } - return -ENOMEM; -} - -static void destroy_receive_buffers(struct smbd_connection *info) -{ - struct smbd_response *response; - - while ((response = get_receive_buffer(info))) - mempool_free(response, info->response_mempool); - - while ((response = get_empty_queue_buffer(info))) - mempool_free(response, info->response_mempool); -} - -/* Implement idle connection timer [MS-SMBD] 3.1.6.2 */ -static void idle_connection_timer(struct work_struct *work) -{ - struct smbd_connection *info = container_of( - work, struct smbd_connection, - idle_timer_work.work); - - if (info->keep_alive_requested != KEEP_ALIVE_NONE) { - log_keep_alive(ERR, - "error status info->keep_alive_requested=%d\n", - info->keep_alive_requested); - smbd_disconnect_rdma_connection(info); - return; - } - - log_keep_alive(INFO, "about to send an empty idle message\n"); - smbd_post_send_empty(info); - - /* Setup the next idle timeout work */ - queue_delayed_work(info->workqueue, &info->idle_timer_work, - info->keep_alive_interval*HZ); -} - -/* - * Destroy the transport and related RDMA and memory resources - * Need to go through all the pending counters and make sure on one is using - * the transport while it is destroyed - */ -void smbd_destroy(struct TCP_Server_Info *server) -{ - struct smbd_connection *info = server->smbd_conn; - struct smbd_response *response; - unsigned long flags; - - if (!info) { - log_rdma_event(INFO, "rdma session already destroyed\n"); - return; - } - - log_rdma_event(INFO, "destroying rdma session\n"); - if (info->transport_status != SMBD_DISCONNECTED) { - rdma_disconnect(server->smbd_conn->id); - log_rdma_event(INFO, "wait for transport being disconnected\n"); - wait_event_interruptible( - info->disconn_wait, - info->transport_status == SMBD_DISCONNECTED); - } - - log_rdma_event(INFO, "destroying qp\n"); - ib_drain_qp(info->id->qp); - rdma_destroy_qp(info->id); - - log_rdma_event(INFO, "cancelling idle timer\n"); - cancel_delayed_work_sync(&info->idle_timer_work); - - log_rdma_event(INFO, "wait for all send posted to IB to finish\n"); - wait_event(info->wait_send_pending, - atomic_read(&info->send_pending) == 0); - - /* It's not possible for upper layer to get to reassembly */ - log_rdma_event(INFO, "drain the reassembly queue\n"); - do { - spin_lock_irqsave(&info->reassembly_queue_lock, flags); - response = _get_first_reassembly(info); - if (response) { - list_del(&response->list); - spin_unlock_irqrestore( - &info->reassembly_queue_lock, flags); - put_receive_buffer(info, response); - } else - spin_unlock_irqrestore( - &info->reassembly_queue_lock, flags); - } while (response); - info->reassembly_data_length = 0; - - log_rdma_event(INFO, "free receive buffers\n"); - wait_event(info->wait_receive_queues, - info->count_receive_queue + info->count_empty_packet_queue - == info->receive_credit_max); - destroy_receive_buffers(info); - - /* - * For performance reasons, memory registration and deregistration - * are not locked by srv_mutex. It is possible some processes are - * blocked on transport srv_mutex while holding memory registration. - * Release the transport srv_mutex to allow them to hit the failure - * path when sending data, and then release memory registartions. - */ - log_rdma_event(INFO, "freeing mr list\n"); - wake_up_interruptible_all(&info->wait_mr); - while (atomic_read(&info->mr_used_count)) { - cifs_server_unlock(server); - msleep(1000); - cifs_server_lock(server); - } - destroy_mr_list(info); - - ib_free_cq(info->send_cq); - ib_free_cq(info->recv_cq); - ib_dealloc_pd(info->pd); - rdma_destroy_id(info->id); - - /* free mempools */ - mempool_destroy(info->request_mempool); - kmem_cache_destroy(info->request_cache); - - mempool_destroy(info->response_mempool); - kmem_cache_destroy(info->response_cache); - - info->transport_status = SMBD_DESTROYED; - - destroy_workqueue(info->workqueue); - log_rdma_event(INFO, "rdma session destroyed\n"); - kfree(info); - server->smbd_conn = NULL; -} - -/* - * Reconnect this SMBD connection, called from upper layer - * return value: 0 on success, or actual error code - */ -int smbd_reconnect(struct TCP_Server_Info *server) -{ - log_rdma_event(INFO, "reconnecting rdma session\n"); - - if (!server->smbd_conn) { - log_rdma_event(INFO, "rdma session already destroyed\n"); - goto create_conn; - } - - /* - * This is possible if transport is disconnected and we haven't received - * notification from RDMA, but upper layer has detected timeout - */ - if (server->smbd_conn->transport_status == SMBD_CONNECTED) { - log_rdma_event(INFO, "disconnecting transport\n"); - smbd_destroy(server); - } - -create_conn: - log_rdma_event(INFO, "creating rdma session\n"); - server->smbd_conn = smbd_get_connection( - server, (struct sockaddr *) &server->dstaddr); - - if (server->smbd_conn) - cifs_dbg(VFS, "RDMA transport re-established\n"); - - return server->smbd_conn ? 0 : -ENOENT; -} - -static void destroy_caches_and_workqueue(struct smbd_connection *info) -{ - destroy_receive_buffers(info); - destroy_workqueue(info->workqueue); - mempool_destroy(info->response_mempool); - kmem_cache_destroy(info->response_cache); - mempool_destroy(info->request_mempool); - kmem_cache_destroy(info->request_cache); -} - -#define MAX_NAME_LEN 80 -static int allocate_caches_and_workqueue(struct smbd_connection *info) -{ - char name[MAX_NAME_LEN]; - int rc; - - scnprintf(name, MAX_NAME_LEN, "smbd_request_%p", info); - info->request_cache = - kmem_cache_create( - name, - sizeof(struct smbd_request) + - sizeof(struct smbd_data_transfer), - 0, SLAB_HWCACHE_ALIGN, NULL); - if (!info->request_cache) - return -ENOMEM; - - info->request_mempool = - mempool_create(info->send_credit_target, mempool_alloc_slab, - mempool_free_slab, info->request_cache); - if (!info->request_mempool) - goto out1; - - scnprintf(name, MAX_NAME_LEN, "smbd_response_%p", info); - info->response_cache = - kmem_cache_create( - name, - sizeof(struct smbd_response) + - info->max_receive_size, - 0, SLAB_HWCACHE_ALIGN, NULL); - if (!info->response_cache) - goto out2; - - info->response_mempool = - mempool_create(info->receive_credit_max, mempool_alloc_slab, - mempool_free_slab, info->response_cache); - if (!info->response_mempool) - goto out3; - - scnprintf(name, MAX_NAME_LEN, "smbd_%p", info); - info->workqueue = create_workqueue(name); - if (!info->workqueue) - goto out4; - - rc = allocate_receive_buffers(info, info->receive_credit_max); - if (rc) { - log_rdma_event(ERR, "failed to allocate receive buffers\n"); - goto out5; - } - - return 0; - -out5: - destroy_workqueue(info->workqueue); -out4: - mempool_destroy(info->response_mempool); -out3: - kmem_cache_destroy(info->response_cache); -out2: - mempool_destroy(info->request_mempool); -out1: - kmem_cache_destroy(info->request_cache); - return -ENOMEM; -} - -/* Create a SMBD connection, called by upper layer */ -static struct smbd_connection *_smbd_get_connection( - struct TCP_Server_Info *server, struct sockaddr *dstaddr, int port) -{ - int rc; - struct smbd_connection *info; - struct rdma_conn_param conn_param; - struct ib_qp_init_attr qp_attr; - struct sockaddr_in *addr_in = (struct sockaddr_in *) dstaddr; - struct ib_port_immutable port_immutable; - u32 ird_ord_hdr[2]; - - info = kzalloc(sizeof(struct smbd_connection), GFP_KERNEL); - if (!info) - return NULL; - - info->transport_status = SMBD_CONNECTING; - rc = smbd_ia_open(info, dstaddr, port); - if (rc) { - log_rdma_event(INFO, "smbd_ia_open rc=%d\n", rc); - goto create_id_failed; - } - - if (smbd_send_credit_target > info->id->device->attrs.max_cqe || - smbd_send_credit_target > info->id->device->attrs.max_qp_wr) { - log_rdma_event(ERR, "consider lowering send_credit_target = %d. Possible CQE overrun, device reporting max_cqe %d max_qp_wr %d\n", - smbd_send_credit_target, - info->id->device->attrs.max_cqe, - info->id->device->attrs.max_qp_wr); - goto config_failed; - } - - if (smbd_receive_credit_max > info->id->device->attrs.max_cqe || - smbd_receive_credit_max > info->id->device->attrs.max_qp_wr) { - log_rdma_event(ERR, "consider lowering receive_credit_max = %d. Possible CQE overrun, device reporting max_cqe %d max_qp_wr %d\n", - smbd_receive_credit_max, - info->id->device->attrs.max_cqe, - info->id->device->attrs.max_qp_wr); - goto config_failed; - } - - info->receive_credit_max = smbd_receive_credit_max; - info->send_credit_target = smbd_send_credit_target; - info->max_send_size = smbd_max_send_size; - info->max_fragmented_recv_size = smbd_max_fragmented_recv_size; - info->max_receive_size = smbd_max_receive_size; - info->keep_alive_interval = smbd_keep_alive_interval; - - if (info->id->device->attrs.max_send_sge < SMBDIRECT_MAX_SEND_SGE || - info->id->device->attrs.max_recv_sge < SMBDIRECT_MAX_RECV_SGE) { - log_rdma_event(ERR, - "device %.*s max_send_sge/max_recv_sge = %d/%d too small\n", - IB_DEVICE_NAME_MAX, - info->id->device->name, - info->id->device->attrs.max_send_sge, - info->id->device->attrs.max_recv_sge); - goto config_failed; - } - - info->send_cq = NULL; - info->recv_cq = NULL; - info->send_cq = - ib_alloc_cq_any(info->id->device, info, - info->send_credit_target, IB_POLL_SOFTIRQ); - if (IS_ERR(info->send_cq)) { - info->send_cq = NULL; - goto alloc_cq_failed; - } - - info->recv_cq = - ib_alloc_cq_any(info->id->device, info, - info->receive_credit_max, IB_POLL_SOFTIRQ); - if (IS_ERR(info->recv_cq)) { - info->recv_cq = NULL; - goto alloc_cq_failed; - } - - memset(&qp_attr, 0, sizeof(qp_attr)); - qp_attr.event_handler = smbd_qp_async_error_upcall; - qp_attr.qp_context = info; - qp_attr.cap.max_send_wr = info->send_credit_target; - qp_attr.cap.max_recv_wr = info->receive_credit_max; - qp_attr.cap.max_send_sge = SMBDIRECT_MAX_SEND_SGE; - qp_attr.cap.max_recv_sge = SMBDIRECT_MAX_RECV_SGE; - qp_attr.cap.max_inline_data = 0; - qp_attr.sq_sig_type = IB_SIGNAL_REQ_WR; - qp_attr.qp_type = IB_QPT_RC; - qp_attr.send_cq = info->send_cq; - qp_attr.recv_cq = info->recv_cq; - qp_attr.port_num = ~0; - - rc = rdma_create_qp(info->id, info->pd, &qp_attr); - if (rc) { - log_rdma_event(ERR, "rdma_create_qp failed %i\n", rc); - goto create_qp_failed; - } - - memset(&conn_param, 0, sizeof(conn_param)); - conn_param.initiator_depth = 0; - - conn_param.responder_resources = - info->id->device->attrs.max_qp_rd_atom - < SMBD_CM_RESPONDER_RESOURCES ? - info->id->device->attrs.max_qp_rd_atom : - SMBD_CM_RESPONDER_RESOURCES; - info->responder_resources = conn_param.responder_resources; - log_rdma_mr(INFO, "responder_resources=%d\n", - info->responder_resources); - - /* Need to send IRD/ORD in private data for iWARP */ - info->id->device->ops.get_port_immutable( - info->id->device, info->id->port_num, &port_immutable); - if (port_immutable.core_cap_flags & RDMA_CORE_PORT_IWARP) { - ird_ord_hdr[0] = info->responder_resources; - ird_ord_hdr[1] = 1; - conn_param.private_data = ird_ord_hdr; - conn_param.private_data_len = sizeof(ird_ord_hdr); - } else { - conn_param.private_data = NULL; - conn_param.private_data_len = 0; - } - - conn_param.retry_count = SMBD_CM_RETRY; - conn_param.rnr_retry_count = SMBD_CM_RNR_RETRY; - conn_param.flow_control = 0; - - log_rdma_event(INFO, "connecting to IP %pI4 port %d\n", - &addr_in->sin_addr, port); - - init_waitqueue_head(&info->conn_wait); - init_waitqueue_head(&info->disconn_wait); - init_waitqueue_head(&info->wait_reassembly_queue); - rc = rdma_connect(info->id, &conn_param); - if (rc) { - log_rdma_event(ERR, "rdma_connect() failed with %i\n", rc); - goto rdma_connect_failed; - } - - wait_event_interruptible( - info->conn_wait, info->transport_status != SMBD_CONNECTING); - - if (info->transport_status != SMBD_CONNECTED) { - log_rdma_event(ERR, "rdma_connect failed port=%d\n", port); - goto rdma_connect_failed; - } - - log_rdma_event(INFO, "rdma_connect connected\n"); - - rc = allocate_caches_and_workqueue(info); - if (rc) { - log_rdma_event(ERR, "cache allocation failed\n"); - goto allocate_cache_failed; - } - - init_waitqueue_head(&info->wait_send_queue); - INIT_DELAYED_WORK(&info->idle_timer_work, idle_connection_timer); - queue_delayed_work(info->workqueue, &info->idle_timer_work, - info->keep_alive_interval*HZ); - - init_waitqueue_head(&info->wait_send_pending); - atomic_set(&info->send_pending, 0); - - init_waitqueue_head(&info->wait_post_send); - - INIT_WORK(&info->disconnect_work, smbd_disconnect_rdma_work); - INIT_WORK(&info->post_send_credits_work, smbd_post_send_credits); - info->new_credits_offered = 0; - spin_lock_init(&info->lock_new_credits_offered); - - rc = smbd_negotiate(info); - if (rc) { - log_rdma_event(ERR, "smbd_negotiate rc=%d\n", rc); - goto negotiation_failed; - } - - rc = allocate_mr_list(info); - if (rc) { - log_rdma_mr(ERR, "memory registration allocation failed\n"); - goto allocate_mr_failed; - } - - return info; - -allocate_mr_failed: - /* At this point, need to a full transport shutdown */ - server->smbd_conn = info; - smbd_destroy(server); - return NULL; - -negotiation_failed: - cancel_delayed_work_sync(&info->idle_timer_work); - destroy_caches_and_workqueue(info); - info->transport_status = SMBD_NEGOTIATE_FAILED; - init_waitqueue_head(&info->conn_wait); - rdma_disconnect(info->id); - wait_event(info->conn_wait, - info->transport_status == SMBD_DISCONNECTED); - -allocate_cache_failed: -rdma_connect_failed: - rdma_destroy_qp(info->id); - -create_qp_failed: -alloc_cq_failed: - if (info->send_cq) - ib_free_cq(info->send_cq); - if (info->recv_cq) - ib_free_cq(info->recv_cq); - -config_failed: - ib_dealloc_pd(info->pd); - rdma_destroy_id(info->id); - -create_id_failed: - kfree(info); - return NULL; -} - -struct smbd_connection *smbd_get_connection( - struct TCP_Server_Info *server, struct sockaddr *dstaddr) -{ - struct smbd_connection *ret; - int port = SMBD_PORT; - -try_again: - ret = _smbd_get_connection(server, dstaddr, port); - - /* Try SMB_PORT if SMBD_PORT doesn't work */ - if (!ret && port == SMBD_PORT) { - port = SMB_PORT; - goto try_again; - } - return ret; -} - -/* - * Receive data from receive reassembly queue - * All the incoming data packets are placed in reassembly queue - * buf: the buffer to read data into - * size: the length of data to read - * return value: actual data read - * Note: this implementation copies the data from reassebmly queue to receive - * buffers used by upper layer. This is not the optimal code path. A better way - * to do it is to not have upper layer allocate its receive buffers but rather - * borrow the buffer from reassembly queue, and return it after data is - * consumed. But this will require more changes to upper layer code, and also - * need to consider packet boundaries while they still being reassembled. - */ -static int smbd_recv_buf(struct smbd_connection *info, char *buf, - unsigned int size) -{ - struct smbd_response *response; - struct smbd_data_transfer *data_transfer; - int to_copy, to_read, data_read, offset; - u32 data_length, remaining_data_length, data_offset; - int rc; - -again: - /* - * No need to hold the reassembly queue lock all the time as we are - * the only one reading from the front of the queue. The transport - * may add more entries to the back of the queue at the same time - */ - log_read(INFO, "size=%d info->reassembly_data_length=%d\n", size, - info->reassembly_data_length); - if (info->reassembly_data_length >= size) { - int queue_length; - int queue_removed = 0; - - /* - * Need to make sure reassembly_data_length is read before - * reading reassembly_queue_length and calling - * _get_first_reassembly. This call is lock free - * as we never read at the end of the queue which are being - * updated in SOFTIRQ as more data is received - */ - virt_rmb(); - queue_length = info->reassembly_queue_length; - data_read = 0; - to_read = size; - offset = info->first_entry_offset; - while (data_read < size) { - response = _get_first_reassembly(info); - data_transfer = smbd_response_payload(response); - data_length = le32_to_cpu(data_transfer->data_length); - remaining_data_length = - le32_to_cpu( - data_transfer->remaining_data_length); - data_offset = le32_to_cpu(data_transfer->data_offset); - - /* - * The upper layer expects RFC1002 length at the - * beginning of the payload. Return it to indicate - * the total length of the packet. This minimize the - * change to upper layer packet processing logic. This - * will be eventually remove when an intermediate - * transport layer is added - */ - if (response->first_segment && size == 4) { - unsigned int rfc1002_len = - data_length + remaining_data_length; - *((__be32 *)buf) = cpu_to_be32(rfc1002_len); - data_read = 4; - response->first_segment = false; - log_read(INFO, "returning rfc1002 length %d\n", - rfc1002_len); - goto read_rfc1002_done; - } - - to_copy = min_t(int, data_length - offset, to_read); - memcpy( - buf + data_read, - (char *)data_transfer + data_offset + offset, - to_copy); - - /* move on to the next buffer? */ - if (to_copy == data_length - offset) { - queue_length--; - /* - * No need to lock if we are not at the - * end of the queue - */ - if (queue_length) - list_del(&response->list); - else { - spin_lock_irq( - &info->reassembly_queue_lock); - list_del(&response->list); - spin_unlock_irq( - &info->reassembly_queue_lock); - } - queue_removed++; - info->count_reassembly_queue--; - info->count_dequeue_reassembly_queue++; - put_receive_buffer(info, response); - offset = 0; - log_read(INFO, "put_receive_buffer offset=0\n"); - } else - offset += to_copy; - - to_read -= to_copy; - data_read += to_copy; - - log_read(INFO, "_get_first_reassembly memcpy %d bytes data_transfer_length-offset=%d after that to_read=%d data_read=%d offset=%d\n", - to_copy, data_length - offset, - to_read, data_read, offset); - } - - spin_lock_irq(&info->reassembly_queue_lock); - info->reassembly_data_length -= data_read; - info->reassembly_queue_length -= queue_removed; - spin_unlock_irq(&info->reassembly_queue_lock); - - info->first_entry_offset = offset; - log_read(INFO, "returning to thread data_read=%d reassembly_data_length=%d first_entry_offset=%d\n", - data_read, info->reassembly_data_length, - info->first_entry_offset); -read_rfc1002_done: - return data_read; - } - - log_read(INFO, "wait_event on more data\n"); - rc = wait_event_interruptible( - info->wait_reassembly_queue, - info->reassembly_data_length >= size || - info->transport_status != SMBD_CONNECTED); - /* Don't return any data if interrupted */ - if (rc) - return rc; - - if (info->transport_status != SMBD_CONNECTED) { - log_read(ERR, "disconnected\n"); - return -ECONNABORTED; - } - - goto again; -} - -/* - * Receive a page from receive reassembly queue - * page: the page to read data into - * to_read: the length of data to read - * return value: actual data read - */ -static int smbd_recv_page(struct smbd_connection *info, - struct page *page, unsigned int page_offset, - unsigned int to_read) -{ - int ret; - char *to_address; - void *page_address; - - /* make sure we have the page ready for read */ - ret = wait_event_interruptible( - info->wait_reassembly_queue, - info->reassembly_data_length >= to_read || - info->transport_status != SMBD_CONNECTED); - if (ret) - return ret; - - /* now we can read from reassembly queue and not sleep */ - page_address = kmap_atomic(page); - to_address = (char *) page_address + page_offset; - - log_read(INFO, "reading from page=%p address=%p to_read=%d\n", - page, to_address, to_read); - - ret = smbd_recv_buf(info, to_address, to_read); - kunmap_atomic(page_address); - - return ret; -} - -/* - * Receive data from transport - * msg: a msghdr point to the buffer, can be ITER_KVEC or ITER_BVEC - * return: total bytes read, or 0. SMB Direct will not do partial read. - */ -int smbd_recv(struct smbd_connection *info, struct msghdr *msg) -{ - char *buf; - struct page *page; - unsigned int to_read, page_offset; - int rc; - - if (iov_iter_rw(&msg->msg_iter) == WRITE) { - /* It's a bug in upper layer to get there */ - cifs_dbg(VFS, "Invalid msg iter dir %u\n", - iov_iter_rw(&msg->msg_iter)); - rc = -EINVAL; - goto out; - } - - switch (iov_iter_type(&msg->msg_iter)) { - case ITER_KVEC: - buf = msg->msg_iter.kvec->iov_base; - to_read = msg->msg_iter.kvec->iov_len; - rc = smbd_recv_buf(info, buf, to_read); - break; - - case ITER_BVEC: - page = msg->msg_iter.bvec->bv_page; - page_offset = msg->msg_iter.bvec->bv_offset; - to_read = msg->msg_iter.bvec->bv_len; - rc = smbd_recv_page(info, page, page_offset, to_read); - break; - - default: - /* It's a bug in upper layer to get there */ - cifs_dbg(VFS, "Invalid msg type %d\n", - iov_iter_type(&msg->msg_iter)); - rc = -EINVAL; - } - -out: - /* SMBDirect will read it all or nothing */ - if (rc > 0) - msg->msg_iter.count = 0; - return rc; -} - -/* - * Send data to transport - * Each rqst is transported as a SMBDirect payload - * rqst: the data to write - * return value: 0 if successfully write, otherwise error code - */ -int smbd_send(struct TCP_Server_Info *server, - int num_rqst, struct smb_rqst *rqst_array) -{ - struct smbd_connection *info = server->smbd_conn; - struct kvec vecs[SMBDIRECT_MAX_SEND_SGE - 1]; - int nvecs; - int size; - unsigned int buflen, remaining_data_length; - unsigned int offset, remaining_vec_data_length; - int start, i, j; - int max_iov_size = - info->max_send_size - sizeof(struct smbd_data_transfer); - struct kvec *iov; - int rc; - struct smb_rqst *rqst; - int rqst_idx; - - if (info->transport_status != SMBD_CONNECTED) - return -EAGAIN; - - /* - * Add in the page array if there is one. The caller needs to set - * rq_tailsz to PAGE_SIZE when the buffer has multiple pages and - * ends at page boundary - */ - remaining_data_length = 0; - for (i = 0; i < num_rqst; i++) - remaining_data_length += smb_rqst_len(server, &rqst_array[i]); - - if (unlikely(remaining_data_length > info->max_fragmented_send_size)) { - /* assertion: payload never exceeds negotiated maximum */ - log_write(ERR, "payload size %d > max size %d\n", - remaining_data_length, info->max_fragmented_send_size); - return -EINVAL; - } - - log_write(INFO, "num_rqst=%d total length=%u\n", - num_rqst, remaining_data_length); - - rqst_idx = 0; - do { - rqst = &rqst_array[rqst_idx]; - iov = rqst->rq_iov; - - cifs_dbg(FYI, "Sending smb (RDMA): idx=%d smb_len=%lu\n", - rqst_idx, smb_rqst_len(server, rqst)); - remaining_vec_data_length = 0; - for (i = 0; i < rqst->rq_nvec; i++) { - remaining_vec_data_length += iov[i].iov_len; - dump_smb(iov[i].iov_base, iov[i].iov_len); - } - - log_write(INFO, "rqst_idx=%d nvec=%d rqst->rq_npages=%d rq_pagesz=%d rq_tailsz=%d buflen=%lu\n", - rqst_idx, rqst->rq_nvec, - rqst->rq_npages, rqst->rq_pagesz, - rqst->rq_tailsz, smb_rqst_len(server, rqst)); - - start = 0; - offset = 0; - do { - buflen = 0; - i = start; - j = 0; - while (i < rqst->rq_nvec && - j < SMBDIRECT_MAX_SEND_SGE - 1 && - buflen < max_iov_size) { - - vecs[j].iov_base = iov[i].iov_base + offset; - if (buflen + iov[i].iov_len > max_iov_size) { - vecs[j].iov_len = - max_iov_size - iov[i].iov_len; - buflen = max_iov_size; - offset = vecs[j].iov_len; - } else { - vecs[j].iov_len = - iov[i].iov_len - offset; - buflen += vecs[j].iov_len; - offset = 0; - ++i; - } - ++j; - } - - remaining_vec_data_length -= buflen; - remaining_data_length -= buflen; - log_write(INFO, "sending %s iov[%d] from start=%d nvecs=%d remaining_data_length=%d\n", - remaining_vec_data_length > 0 ? - "partial" : "complete", - rqst->rq_nvec, start, j, - remaining_data_length); - - start = i; - rc = smbd_post_send_data(info, vecs, j, remaining_data_length); - if (rc) - goto done; - } while (remaining_vec_data_length > 0); - - /* now sending pages if there are any */ - for (i = 0; i < rqst->rq_npages; i++) { - rqst_page_get_length(rqst, i, &buflen, &offset); - nvecs = (buflen + max_iov_size - 1) / max_iov_size; - log_write(INFO, "sending pages buflen=%d nvecs=%d\n", - buflen, nvecs); - for (j = 0; j < nvecs; j++) { - size = min_t(unsigned int, max_iov_size, remaining_data_length); - remaining_data_length -= size; - log_write(INFO, "sending pages i=%d offset=%d size=%d remaining_data_length=%d\n", - i, j * max_iov_size + offset, size, - remaining_data_length); - rc = smbd_post_send_page( - info, rqst->rq_pages[i], - j*max_iov_size + offset, - size, remaining_data_length); - if (rc) - goto done; - } - } - } while (++rqst_idx < num_rqst); - -done: - /* - * As an optimization, we don't wait for individual I/O to finish - * before sending the next one. - * Send them all and wait for pending send count to get to 0 - * that means all the I/Os have been out and we are good to return - */ - - wait_event(info->wait_send_pending, - atomic_read(&info->send_pending) == 0); - - return rc; -} - -static void register_mr_done(struct ib_cq *cq, struct ib_wc *wc) -{ - struct smbd_mr *mr; - struct ib_cqe *cqe; - - if (wc->status) { - log_rdma_mr(ERR, "status=%d\n", wc->status); - cqe = wc->wr_cqe; - mr = container_of(cqe, struct smbd_mr, cqe); - smbd_disconnect_rdma_connection(mr->conn); - } -} - -/* - * The work queue function that recovers MRs - * We need to call ib_dereg_mr() and ib_alloc_mr() before this MR can be used - * again. Both calls are slow, so finish them in a workqueue. This will not - * block I/O path. - * There is one workqueue that recovers MRs, there is no need to lock as the - * I/O requests calling smbd_register_mr will never update the links in the - * mr_list. - */ -static void smbd_mr_recovery_work(struct work_struct *work) -{ - struct smbd_connection *info = - container_of(work, struct smbd_connection, mr_recovery_work); - struct smbd_mr *smbdirect_mr; - int rc; - - list_for_each_entry(smbdirect_mr, &info->mr_list, list) { - if (smbdirect_mr->state == MR_ERROR) { - - /* recover this MR entry */ - rc = ib_dereg_mr(smbdirect_mr->mr); - if (rc) { - log_rdma_mr(ERR, - "ib_dereg_mr failed rc=%x\n", - rc); - smbd_disconnect_rdma_connection(info); - continue; - } - - smbdirect_mr->mr = ib_alloc_mr( - info->pd, info->mr_type, - info->max_frmr_depth); - if (IS_ERR(smbdirect_mr->mr)) { - log_rdma_mr(ERR, "ib_alloc_mr failed mr_type=%x max_frmr_depth=%x\n", - info->mr_type, - info->max_frmr_depth); - smbd_disconnect_rdma_connection(info); - continue; - } - } else - /* This MR is being used, don't recover it */ - continue; - - smbdirect_mr->state = MR_READY; - - /* smbdirect_mr->state is updated by this function - * and is read and updated by I/O issuing CPUs trying - * to get a MR, the call to atomic_inc_return - * implicates a memory barrier and guarantees this - * value is updated before waking up any calls to - * get_mr() from the I/O issuing CPUs - */ - if (atomic_inc_return(&info->mr_ready_count) == 1) - wake_up_interruptible(&info->wait_mr); - } -} - -static void destroy_mr_list(struct smbd_connection *info) -{ - struct smbd_mr *mr, *tmp; - - cancel_work_sync(&info->mr_recovery_work); - list_for_each_entry_safe(mr, tmp, &info->mr_list, list) { - if (mr->state == MR_INVALIDATED) - ib_dma_unmap_sg(info->id->device, mr->sgl, - mr->sgl_count, mr->dir); - ib_dereg_mr(mr->mr); - kfree(mr->sgl); - kfree(mr); - } -} - -/* - * Allocate MRs used for RDMA read/write - * The number of MRs will not exceed hardware capability in responder_resources - * All MRs are kept in mr_list. The MR can be recovered after it's used - * Recovery is done in smbd_mr_recovery_work. The content of list entry changes - * as MRs are used and recovered for I/O, but the list links will not change - */ -static int allocate_mr_list(struct smbd_connection *info) -{ - int i; - struct smbd_mr *smbdirect_mr, *tmp; - - INIT_LIST_HEAD(&info->mr_list); - init_waitqueue_head(&info->wait_mr); - spin_lock_init(&info->mr_list_lock); - atomic_set(&info->mr_ready_count, 0); - atomic_set(&info->mr_used_count, 0); - init_waitqueue_head(&info->wait_for_mr_cleanup); - INIT_WORK(&info->mr_recovery_work, smbd_mr_recovery_work); - /* Allocate more MRs (2x) than hardware responder_resources */ - for (i = 0; i < info->responder_resources * 2; i++) { - smbdirect_mr = kzalloc(sizeof(*smbdirect_mr), GFP_KERNEL); - if (!smbdirect_mr) - goto out; - smbdirect_mr->mr = ib_alloc_mr(info->pd, info->mr_type, - info->max_frmr_depth); - if (IS_ERR(smbdirect_mr->mr)) { - log_rdma_mr(ERR, "ib_alloc_mr failed mr_type=%x max_frmr_depth=%x\n", - info->mr_type, info->max_frmr_depth); - goto out; - } - smbdirect_mr->sgl = kcalloc( - info->max_frmr_depth, - sizeof(struct scatterlist), - GFP_KERNEL); - if (!smbdirect_mr->sgl) { - log_rdma_mr(ERR, "failed to allocate sgl\n"); - ib_dereg_mr(smbdirect_mr->mr); - goto out; - } - smbdirect_mr->state = MR_READY; - smbdirect_mr->conn = info; - - list_add_tail(&smbdirect_mr->list, &info->mr_list); - atomic_inc(&info->mr_ready_count); - } - return 0; - -out: - kfree(smbdirect_mr); - - list_for_each_entry_safe(smbdirect_mr, tmp, &info->mr_list, list) { - list_del(&smbdirect_mr->list); - ib_dereg_mr(smbdirect_mr->mr); - kfree(smbdirect_mr->sgl); - kfree(smbdirect_mr); - } - return -ENOMEM; -} - -/* - * Get a MR from mr_list. This function waits until there is at least one - * MR available in the list. It may access the list while the - * smbd_mr_recovery_work is recovering the MR list. This doesn't need a lock - * as they never modify the same places. However, there may be several CPUs - * issueing I/O trying to get MR at the same time, mr_list_lock is used to - * protect this situation. - */ -static struct smbd_mr *get_mr(struct smbd_connection *info) -{ - struct smbd_mr *ret; - int rc; -again: - rc = wait_event_interruptible(info->wait_mr, - atomic_read(&info->mr_ready_count) || - info->transport_status != SMBD_CONNECTED); - if (rc) { - log_rdma_mr(ERR, "wait_event_interruptible rc=%x\n", rc); - return NULL; - } - - if (info->transport_status != SMBD_CONNECTED) { - log_rdma_mr(ERR, "info->transport_status=%x\n", - info->transport_status); - return NULL; - } - - spin_lock(&info->mr_list_lock); - list_for_each_entry(ret, &info->mr_list, list) { - if (ret->state == MR_READY) { - ret->state = MR_REGISTERED; - spin_unlock(&info->mr_list_lock); - atomic_dec(&info->mr_ready_count); - atomic_inc(&info->mr_used_count); - return ret; - } - } - - spin_unlock(&info->mr_list_lock); - /* - * It is possible that we could fail to get MR because other processes may - * try to acquire a MR at the same time. If this is the case, retry it. - */ - goto again; -} - -/* - * Register memory for RDMA read/write - * pages[]: the list of pages to register memory with - * num_pages: the number of pages to register - * tailsz: if non-zero, the bytes to register in the last page - * writing: true if this is a RDMA write (SMB read), false for RDMA read - * need_invalidate: true if this MR needs to be locally invalidated after I/O - * return value: the MR registered, NULL if failed. - */ -struct smbd_mr *smbd_register_mr( - struct smbd_connection *info, struct page *pages[], int num_pages, - int offset, int tailsz, bool writing, bool need_invalidate) -{ - struct smbd_mr *smbdirect_mr; - int rc, i; - enum dma_data_direction dir; - struct ib_reg_wr *reg_wr; - - if (num_pages > info->max_frmr_depth) { - log_rdma_mr(ERR, "num_pages=%d max_frmr_depth=%d\n", - num_pages, info->max_frmr_depth); - return NULL; - } - - smbdirect_mr = get_mr(info); - if (!smbdirect_mr) { - log_rdma_mr(ERR, "get_mr returning NULL\n"); - return NULL; - } - smbdirect_mr->need_invalidate = need_invalidate; - smbdirect_mr->sgl_count = num_pages; - sg_init_table(smbdirect_mr->sgl, num_pages); - - log_rdma_mr(INFO, "num_pages=0x%x offset=0x%x tailsz=0x%x\n", - num_pages, offset, tailsz); - - if (num_pages == 1) { - sg_set_page(&smbdirect_mr->sgl[0], pages[0], tailsz, offset); - goto skip_multiple_pages; - } - - /* We have at least two pages to register */ - sg_set_page( - &smbdirect_mr->sgl[0], pages[0], PAGE_SIZE - offset, offset); - i = 1; - while (i < num_pages - 1) { - sg_set_page(&smbdirect_mr->sgl[i], pages[i], PAGE_SIZE, 0); - i++; - } - sg_set_page(&smbdirect_mr->sgl[i], pages[i], - tailsz ? tailsz : PAGE_SIZE, 0); - -skip_multiple_pages: - dir = writing ? DMA_FROM_DEVICE : DMA_TO_DEVICE; - smbdirect_mr->dir = dir; - rc = ib_dma_map_sg(info->id->device, smbdirect_mr->sgl, num_pages, dir); - if (!rc) { - log_rdma_mr(ERR, "ib_dma_map_sg num_pages=%x dir=%x rc=%x\n", - num_pages, dir, rc); - goto dma_map_error; - } - - rc = ib_map_mr_sg(smbdirect_mr->mr, smbdirect_mr->sgl, num_pages, - NULL, PAGE_SIZE); - if (rc != num_pages) { - log_rdma_mr(ERR, - "ib_map_mr_sg failed rc = %d num_pages = %x\n", - rc, num_pages); - goto map_mr_error; - } - - ib_update_fast_reg_key(smbdirect_mr->mr, - ib_inc_rkey(smbdirect_mr->mr->rkey)); - reg_wr = &smbdirect_mr->wr; - reg_wr->wr.opcode = IB_WR_REG_MR; - smbdirect_mr->cqe.done = register_mr_done; - reg_wr->wr.wr_cqe = &smbdirect_mr->cqe; - reg_wr->wr.num_sge = 0; - reg_wr->wr.send_flags = IB_SEND_SIGNALED; - reg_wr->mr = smbdirect_mr->mr; - reg_wr->key = smbdirect_mr->mr->rkey; - reg_wr->access = writing ? - IB_ACCESS_REMOTE_WRITE | IB_ACCESS_LOCAL_WRITE : - IB_ACCESS_REMOTE_READ; - - /* - * There is no need for waiting for complemtion on ib_post_send - * on IB_WR_REG_MR. Hardware enforces a barrier and order of execution - * on the next ib_post_send when we actaully send I/O to remote peer - */ - rc = ib_post_send(info->id->qp, ®_wr->wr, NULL); - if (!rc) - return smbdirect_mr; - - log_rdma_mr(ERR, "ib_post_send failed rc=%x reg_wr->key=%x\n", - rc, reg_wr->key); - - /* If all failed, attempt to recover this MR by setting it MR_ERROR*/ -map_mr_error: - ib_dma_unmap_sg(info->id->device, smbdirect_mr->sgl, - smbdirect_mr->sgl_count, smbdirect_mr->dir); - -dma_map_error: - smbdirect_mr->state = MR_ERROR; - if (atomic_dec_and_test(&info->mr_used_count)) - wake_up(&info->wait_for_mr_cleanup); - - smbd_disconnect_rdma_connection(info); - - return NULL; -} - -static void local_inv_done(struct ib_cq *cq, struct ib_wc *wc) -{ - struct smbd_mr *smbdirect_mr; - struct ib_cqe *cqe; - - cqe = wc->wr_cqe; - smbdirect_mr = container_of(cqe, struct smbd_mr, cqe); - smbdirect_mr->state = MR_INVALIDATED; - if (wc->status != IB_WC_SUCCESS) { - log_rdma_mr(ERR, "invalidate failed status=%x\n", wc->status); - smbdirect_mr->state = MR_ERROR; - } - complete(&smbdirect_mr->invalidate_done); -} - -/* - * Deregister a MR after I/O is done - * This function may wait if remote invalidation is not used - * and we have to locally invalidate the buffer to prevent data is being - * modified by remote peer after upper layer consumes it - */ -int smbd_deregister_mr(struct smbd_mr *smbdirect_mr) -{ - struct ib_send_wr *wr; - struct smbd_connection *info = smbdirect_mr->conn; - int rc = 0; - - if (smbdirect_mr->need_invalidate) { - /* Need to finish local invalidation before returning */ - wr = &smbdirect_mr->inv_wr; - wr->opcode = IB_WR_LOCAL_INV; - smbdirect_mr->cqe.done = local_inv_done; - wr->wr_cqe = &smbdirect_mr->cqe; - wr->num_sge = 0; - wr->ex.invalidate_rkey = smbdirect_mr->mr->rkey; - wr->send_flags = IB_SEND_SIGNALED; - - init_completion(&smbdirect_mr->invalidate_done); - rc = ib_post_send(info->id->qp, wr, NULL); - if (rc) { - log_rdma_mr(ERR, "ib_post_send failed rc=%x\n", rc); - smbd_disconnect_rdma_connection(info); - goto done; - } - wait_for_completion(&smbdirect_mr->invalidate_done); - smbdirect_mr->need_invalidate = false; - } else - /* - * For remote invalidation, just set it to MR_INVALIDATED - * and defer to mr_recovery_work to recover the MR for next use - */ - smbdirect_mr->state = MR_INVALIDATED; - - if (smbdirect_mr->state == MR_INVALIDATED) { - ib_dma_unmap_sg( - info->id->device, smbdirect_mr->sgl, - smbdirect_mr->sgl_count, - smbdirect_mr->dir); - smbdirect_mr->state = MR_READY; - if (atomic_inc_return(&info->mr_ready_count) == 1) - wake_up_interruptible(&info->wait_mr); - } else - /* - * Schedule the work to do MR recovery for future I/Os MR - * recovery is slow and don't want it to block current I/O - */ - queue_work(info->workqueue, &info->mr_recovery_work); - -done: - if (atomic_dec_and_test(&info->mr_used_count)) - wake_up(&info->wait_for_mr_cleanup); - - return rc; -} diff --git a/fs/cifs/smbdirect.h b/fs/cifs/smbdirect.h deleted file mode 100644 index 207ef979cd51c..0000000000000 --- a/fs/cifs/smbdirect.h +++ /dev/null @@ -1,320 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0-or-later */ -/* - * Copyright (C) 2017, Microsoft Corporation. - * - * Author(s): Long Li - */ -#ifndef _SMBDIRECT_H -#define _SMBDIRECT_H - -#ifdef CONFIG_CIFS_SMB_DIRECT -#define cifs_rdma_enabled(server) ((server)->rdma) - -#include "cifsglob.h" -#include -#include -#include - -extern int rdma_readwrite_threshold; -extern int smbd_max_frmr_depth; -extern int smbd_keep_alive_interval; -extern int smbd_max_receive_size; -extern int smbd_max_fragmented_recv_size; -extern int smbd_max_send_size; -extern int smbd_send_credit_target; -extern int smbd_receive_credit_max; - -enum keep_alive_status { - KEEP_ALIVE_NONE, - KEEP_ALIVE_PENDING, - KEEP_ALIVE_SENT, -}; - -enum smbd_connection_status { - SMBD_CREATED, - SMBD_CONNECTING, - SMBD_CONNECTED, - SMBD_NEGOTIATE_FAILED, - SMBD_DISCONNECTING, - SMBD_DISCONNECTED, - SMBD_DESTROYED -}; - -/* - * The context for the SMBDirect transport - * Everything related to the transport is here. It has several logical parts - * 1. RDMA related structures - * 2. SMBDirect connection parameters - * 3. Memory registrations - * 4. Receive and reassembly queues for data receive path - * 5. mempools for allocating packets - */ -struct smbd_connection { - enum smbd_connection_status transport_status; - - /* RDMA related */ - struct rdma_cm_id *id; - struct ib_qp_init_attr qp_attr; - struct ib_pd *pd; - struct ib_cq *send_cq, *recv_cq; - struct ib_device_attr dev_attr; - int ri_rc; - struct completion ri_done; - wait_queue_head_t conn_wait; - wait_queue_head_t disconn_wait; - - struct completion negotiate_completion; - bool negotiate_done; - - struct work_struct disconnect_work; - struct work_struct post_send_credits_work; - - spinlock_t lock_new_credits_offered; - int new_credits_offered; - - /* Connection parameters defined in [MS-SMBD] 3.1.1.1 */ - int receive_credit_max; - int send_credit_target; - int max_send_size; - int max_fragmented_recv_size; - int max_fragmented_send_size; - int max_receive_size; - int keep_alive_interval; - int max_readwrite_size; - enum keep_alive_status keep_alive_requested; - int protocol; - atomic_t send_credits; - atomic_t receive_credits; - int receive_credit_target; - int fragment_reassembly_remaining; - - /* Memory registrations */ - /* Maximum number of RDMA read/write outstanding on this connection */ - int responder_resources; - /* Maximum number of pages in a single RDMA write/read on this connection */ - int max_frmr_depth; - /* - * If payload is less than or equal to the threshold, - * use RDMA send/recv to send upper layer I/O. - * If payload is more than the threshold, - * use RDMA read/write through memory registration for I/O. - */ - int rdma_readwrite_threshold; - enum ib_mr_type mr_type; - struct list_head mr_list; - spinlock_t mr_list_lock; - /* The number of available MRs ready for memory registration */ - atomic_t mr_ready_count; - atomic_t mr_used_count; - wait_queue_head_t wait_mr; - struct work_struct mr_recovery_work; - /* Used by transport to wait until all MRs are returned */ - wait_queue_head_t wait_for_mr_cleanup; - - /* Activity accoutning */ - atomic_t send_pending; - wait_queue_head_t wait_send_pending; - wait_queue_head_t wait_post_send; - - /* Receive queue */ - struct list_head receive_queue; - int count_receive_queue; - spinlock_t receive_queue_lock; - - struct list_head empty_packet_queue; - int count_empty_packet_queue; - spinlock_t empty_packet_queue_lock; - - wait_queue_head_t wait_receive_queues; - - /* Reassembly queue */ - struct list_head reassembly_queue; - spinlock_t reassembly_queue_lock; - wait_queue_head_t wait_reassembly_queue; - - /* total data length of reassembly queue */ - int reassembly_data_length; - int reassembly_queue_length; - /* the offset to first buffer in reassembly queue */ - int first_entry_offset; - - bool send_immediate; - - wait_queue_head_t wait_send_queue; - - /* - * Indicate if we have received a full packet on the connection - * This is used to identify the first SMBD packet of a assembled - * payload (SMB packet) in reassembly queue so we can return a - * RFC1002 length to upper layer to indicate the length of the SMB - * packet received - */ - bool full_packet_received; - - struct workqueue_struct *workqueue; - struct delayed_work idle_timer_work; - - /* Memory pool for preallocating buffers */ - /* request pool for RDMA send */ - struct kmem_cache *request_cache; - mempool_t *request_mempool; - - /* response pool for RDMA receive */ - struct kmem_cache *response_cache; - mempool_t *response_mempool; - - /* for debug purposes */ - unsigned int count_get_receive_buffer; - unsigned int count_put_receive_buffer; - unsigned int count_reassembly_queue; - unsigned int count_enqueue_reassembly_queue; - unsigned int count_dequeue_reassembly_queue; - unsigned int count_send_empty; -}; - -enum smbd_message_type { - SMBD_NEGOTIATE_RESP, - SMBD_TRANSFER_DATA, -}; - -#define SMB_DIRECT_RESPONSE_REQUESTED 0x0001 - -/* SMBD negotiation request packet [MS-SMBD] 2.2.1 */ -struct smbd_negotiate_req { - __le16 min_version; - __le16 max_version; - __le16 reserved; - __le16 credits_requested; - __le32 preferred_send_size; - __le32 max_receive_size; - __le32 max_fragmented_size; -} __packed; - -/* SMBD negotiation response packet [MS-SMBD] 2.2.2 */ -struct smbd_negotiate_resp { - __le16 min_version; - __le16 max_version; - __le16 negotiated_version; - __le16 reserved; - __le16 credits_requested; - __le16 credits_granted; - __le32 status; - __le32 max_readwrite_size; - __le32 preferred_send_size; - __le32 max_receive_size; - __le32 max_fragmented_size; -} __packed; - -/* SMBD data transfer packet with payload [MS-SMBD] 2.2.3 */ -struct smbd_data_transfer { - __le16 credits_requested; - __le16 credits_granted; - __le16 flags; - __le16 reserved; - __le32 remaining_data_length; - __le32 data_offset; - __le32 data_length; - __le32 padding; - __u8 buffer[]; -} __packed; - -/* The packet fields for a registered RDMA buffer */ -struct smbd_buffer_descriptor_v1 { - __le64 offset; - __le32 token; - __le32 length; -} __packed; - -/* Maximum number of SGEs used by smbdirect.c in any send work request */ -#define SMBDIRECT_MAX_SEND_SGE 6 - -/* The context for a SMBD request */ -struct smbd_request { - struct smbd_connection *info; - struct ib_cqe cqe; - - /* the SGE entries for this work request */ - struct ib_sge sge[SMBDIRECT_MAX_SEND_SGE]; - int num_sge; - - /* SMBD packet header follows this structure */ - u8 packet[]; -}; - -/* Maximum number of SGEs used by smbdirect.c in any receive work request */ -#define SMBDIRECT_MAX_RECV_SGE 1 - -/* The context for a SMBD response */ -struct smbd_response { - struct smbd_connection *info; - struct ib_cqe cqe; - struct ib_sge sge; - - enum smbd_message_type type; - - /* Link to receive queue or reassembly queue */ - struct list_head list; - - /* Indicate if this is the 1st packet of a payload */ - bool first_segment; - - /* SMBD packet header and payload follows this structure */ - u8 packet[]; -}; - -/* Create a SMBDirect session */ -struct smbd_connection *smbd_get_connection( - struct TCP_Server_Info *server, struct sockaddr *dstaddr); - -/* Reconnect SMBDirect session */ -int smbd_reconnect(struct TCP_Server_Info *server); -/* Destroy SMBDirect session */ -void smbd_destroy(struct TCP_Server_Info *server); - -/* Interface for carrying upper layer I/O through send/recv */ -int smbd_recv(struct smbd_connection *info, struct msghdr *msg); -int smbd_send(struct TCP_Server_Info *server, - int num_rqst, struct smb_rqst *rqst); - -enum mr_state { - MR_READY, - MR_REGISTERED, - MR_INVALIDATED, - MR_ERROR -}; - -struct smbd_mr { - struct smbd_connection *conn; - struct list_head list; - enum mr_state state; - struct ib_mr *mr; - struct scatterlist *sgl; - int sgl_count; - enum dma_data_direction dir; - union { - struct ib_reg_wr wr; - struct ib_send_wr inv_wr; - }; - struct ib_cqe cqe; - bool need_invalidate; - struct completion invalidate_done; -}; - -/* Interfaces to register and deregister MR for RDMA read/write */ -struct smbd_mr *smbd_register_mr( - struct smbd_connection *info, struct page *pages[], int num_pages, - int offset, int tailsz, bool writing, bool need_invalidate); -int smbd_deregister_mr(struct smbd_mr *mr); - -#else -#define cifs_rdma_enabled(server) 0 -struct smbd_connection {}; -static inline void *smbd_get_connection( - struct TCP_Server_Info *server, struct sockaddr *dstaddr) {return NULL;} -static inline int smbd_reconnect(struct TCP_Server_Info *server) {return -1; } -static inline void smbd_destroy(struct TCP_Server_Info *server) {} -static inline int smbd_recv(struct smbd_connection *info, struct msghdr *msg) {return -1; } -static inline int smbd_send(struct TCP_Server_Info *server, int num_rqst, struct smb_rqst *rqst) {return -1; } -#endif - -#endif diff --git a/fs/cifs/smbencrypt.c b/fs/cifs/smbencrypt.c deleted file mode 100644 index 4a04877538691..0000000000000 --- a/fs/cifs/smbencrypt.c +++ /dev/null @@ -1,91 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-or-later -/* - Unix SMB/Netbios implementation. - Version 1.9. - SMB parameters and setup - Copyright (C) Andrew Tridgell 1992-2000 - Copyright (C) Luke Kenneth Casson Leighton 1996-2000 - Modified by Jeremy Allison 1995. - Copyright (C) Andrew Bartlett 2002-2003 - Modified by Steve French (sfrench@us.ibm.com) 2002-2003 - -*/ - -#include -#include -#include -#include -#include -#include -#include -#include "cifs_fs_sb.h" -#include "cifs_unicode.h" -#include "cifspdu.h" -#include "cifsglob.h" -#include "cifs_debug.h" -#include "cifsproto.h" -#include "../smbfs_common/md4.h" - -#ifndef false -#define false 0 -#endif -#ifndef true -#define true 1 -#endif - -/* following came from the other byteorder.h to avoid include conflicts */ -#define CVAL(buf,pos) (((unsigned char *)(buf))[pos]) -#define SSVALX(buf,pos,val) (CVAL(buf,pos)=(val)&0xFF,CVAL(buf,pos+1)=(val)>>8) -#define SSVAL(buf,pos,val) SSVALX((buf),(pos),((__u16)(val))) - -/* produce a md4 message digest from data of length n bytes */ -static int -mdfour(unsigned char *md4_hash, unsigned char *link_str, int link_len) -{ - int rc; - struct md4_ctx mctx; - - rc = cifs_md4_init(&mctx); - if (rc) { - cifs_dbg(VFS, "%s: Could not init MD4\n", __func__); - goto mdfour_err; - } - rc = cifs_md4_update(&mctx, link_str, link_len); - if (rc) { - cifs_dbg(VFS, "%s: Could not update MD4\n", __func__); - goto mdfour_err; - } - rc = cifs_md4_final(&mctx, md4_hash); - if (rc) - cifs_dbg(VFS, "%s: Could not finalize MD4\n", __func__); - - -mdfour_err: - return rc; -} - -/* - * Creates the MD4 Hash of the users password in NT UNICODE. - */ - -int -E_md4hash(const unsigned char *passwd, unsigned char *p16, - const struct nls_table *codepage) -{ - int rc; - int len; - __le16 wpwd[129]; - - /* Password cannot be longer than 128 characters */ - if (passwd) /* Password must be converted to NT unicode */ - len = cifs_strtoUTF16(wpwd, passwd, 128, codepage); - else { - len = 0; - *wpwd = 0; /* Ensure string is null terminated */ - } - - rc = mdfour(p16, (unsigned char *) wpwd, len * sizeof(__le16)); - memzero_explicit(wpwd, sizeof(wpwd)); - - return rc; -} diff --git a/fs/cifs/smberr.h b/fs/cifs/smberr.h deleted file mode 100644 index aeffdad829e2e..0000000000000 --- a/fs/cifs/smberr.h +++ /dev/null @@ -1,171 +0,0 @@ -/* SPDX-License-Identifier: LGPL-2.1 */ -/* - * - * Copyright (c) International Business Machines Corp., 2002,2004 - * Author(s): Steve French (sfrench@us.ibm.com) - * - * See Error Codes section of the SNIA CIFS Specification - * for more information - * - */ - -#define SUCCESS 0x00 /* The request was successful. */ -#define ERRDOS 0x01 /* Error is from the core DOS operating system set */ -#define ERRSRV 0x02 /* Error is generated by the file server daemon */ -#define ERRHRD 0x03 /* Error is a hardware error. */ -#define ERRCMD 0xFF /* Command was not in the "SMB" format. */ - -/* The following error codes may be generated with the SUCCESS error class.*/ - -/*#define SUCCESS 0 The request was successful. */ - -/* The following error codes may be generated with the ERRDOS error class.*/ - -#define ERRbadfunc 1 /* Invalid function. The server did not - recognize or could not perform a - system call generated by the server, - e.g., set the DIRECTORY attribute on - a data file, invalid seek mode. */ -#define ERRbadfile 2 /* File not found. The last component - of a file's pathname could not be - found. */ -#define ERRbadpath 3 /* Directory invalid. A directory - component in a pathname could not be - found. */ -#define ERRnofids 4 /* Too many open files. The server has - no file handles available. */ -#define ERRnoaccess 5 /* Access denied, the client's context - does not permit the requested - function. This includes the - following conditions: invalid rename - command, write to Fid open for read - only, read on Fid open for write - only, attempt to delete a non-empty - directory */ -#define ERRbadfid 6 /* Invalid file handle. The file handle - specified was not recognized by the - server. */ -#define ERRbadmcb 7 /* Memory control blocks destroyed. */ -#define ERRnomem 8 /* Insufficient server memory to - perform the requested function. */ -#define ERRbadmem 9 /* Invalid memory block address. */ -#define ERRbadenv 10 /* Invalid environment. */ -#define ERRbadformat 11 /* Invalid format. */ -#define ERRbadaccess 12 /* Invalid open mode. */ -#define ERRbaddata 13 /* Invalid data (generated only by - IOCTL calls within the server). */ -#define ERRbaddrive 15 /* Invalid drive specified. */ -#define ERRremcd 16 /* A Delete Directory request attempted - to remove the server's current - directory. */ -#define ERRdiffdevice 17 /* Not same device (e.g., a cross - volume rename was attempted */ -#define ERRnofiles 18 /* A File Search command can find no - more files matching the specified - criteria. */ -#define ERRwriteprot 19 /* media is write protected */ -#define ERRgeneral 31 -#define ERRbadshare 32 /* The sharing mode specified for an - Open conflicts with existing FIDs on - the file. */ -#define ERRlock 33 /* A Lock request conflicted with an - existing lock or specified an - invalid mode, or an Unlock requested - attempted to remove a lock held by - another process. */ -#define ERRunsup 50 -#define ERRnosuchshare 67 -#define ERRfilexists 80 /* The file named in the request - already exists. */ -#define ERRinvparm 87 -#define ERRdiskfull 112 -#define ERRinvname 123 -#define ERRinvlevel 124 -#define ERRdirnotempty 145 -#define ERRnotlocked 158 -#define ERRcancelviolation 173 -#define ERRalreadyexists 183 -#define ERRbadpipe 230 -#define ERRpipebusy 231 -#define ERRpipeclosing 232 -#define ERRnotconnected 233 -#define ERRmoredata 234 -#define ERReasnotsupported 282 -#define ErrQuota 0x200 /* The operation would cause a quota - limit to be exceeded. */ -#define ErrNotALink 0x201 /* A link operation was performed on a - pathname that was not a link. */ - -/* Below errors are used internally (do not come over the wire) for passthrough - from STATUS codes to POSIX only */ -#define ERRsymlink 0xFFFD -#define ErrTooManyLinks 0xFFFE - -/* Following error codes may be generated with the ERRSRV error class.*/ - -#define ERRerror 1 /* Non-specific error code. It is - returned under the following - conditions: resource other than disk - space exhausted (e.g. TIDs), first - SMB command was not negotiate, - multiple negotiates attempted, and - internal server error. */ -#define ERRbadpw 2 /* Bad password - name/password pair in - a TreeConnect or Session Setup are - invalid. */ -#define ERRbadtype 3 /* used for indicating DFS referral - needed */ -#define ERRaccess 4 /* The client does not have the - necessary access rights within the - specified context for requested - function. */ -#define ERRinvtid 5 /* The Tid specified in a command was - invalid. */ -#define ERRinvnetname 6 /* Invalid network name in tree - connect. */ -#define ERRinvdevice 7 /* Invalid device - printer request - made to non-printer connection or - non-printer request made to printer - connection. */ -#define ERRqfull 49 /* Print queue full (files) -- returned - by open print file. */ -#define ERRqtoobig 50 /* Print queue full -- no space. */ -#define ERRqeof 51 /* EOF on print queue dump */ -#define ERRinvpfid 52 /* Invalid print file FID. */ -#define ERRsmbcmd 64 /* The server did not recognize the - command received. */ -#define ERRsrverror 65 /* The server encountered an internal - error, e.g., system file - unavailable. */ -#define ERRbadBID 66 /* (obsolete) */ -#define ERRfilespecs 67 /* The Fid and pathname parameters - contained an invalid combination of - values. */ -#define ERRbadLink 68 /* (obsolete) */ -#define ERRbadpermits 69 /* The access permissions specified for - a file or directory are not a valid - combination. */ -#define ERRbadPID 70 -#define ERRsetattrmode 71 /* attribute (mode) is invalid */ -#define ERRpaused 81 /* Server is paused */ -#define ERRmsgoff 82 /* reserved - messaging off */ -#define ERRnoroom 83 /* reserved - no room for message */ -#define ERRrmuns 87 /* reserved - too many remote names */ -#define ERRtimeout 88 /* operation timed out */ -#define ERRnoresource 89 /* No resources available for request - */ -#define ERRtoomanyuids 90 /* Too many UIDs active on this session - */ -#define ERRbaduid 91 /* The UID is not known as a valid user - */ -#define ERRusempx 250 /* temporarily unable to use raw */ -#define ERRusestd 251 /* temporarily unable to use either raw - or mpx */ -#define ERR_NOTIFY_ENUM_DIR 1024 -#define ERRnoSuchUser 2238 /* user account does not exist */ -#define ERRaccountexpired 2239 -#define ERRbadclient 2240 /* can not logon from this client */ -#define ERRbadLogonTime 2241 /* logon hours do not allow this */ -#define ERRpasswordExpired 2242 -#define ERRnetlogonNotStarted 2455 -#define ERRnosupport 0xFFFF diff --git a/fs/cifs/trace.c b/fs/cifs/trace.c deleted file mode 100644 index 4654837871934..0000000000000 --- a/fs/cifs/trace.c +++ /dev/null @@ -1,8 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0 -/* - * Copyright (C) 2018, Microsoft Corporation. - * - * Author(s): Steve French - */ -#define CREATE_TRACE_POINTS -#include "trace.h" diff --git a/fs/cifs/trace.h b/fs/cifs/trace.h deleted file mode 100644 index d3053bd8ae731..0000000000000 --- a/fs/cifs/trace.h +++ /dev/null @@ -1,1070 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0 */ -/* - * Copyright (C) 2018, Microsoft Corporation. - * - * Author(s): Steve French - */ -#undef TRACE_SYSTEM -#define TRACE_SYSTEM cifs - -#if !defined(_CIFS_TRACE_H) || defined(TRACE_HEADER_MULTI_READ) -#define _CIFS_TRACE_H - -#include -#include -#include - -/* - * Please use this 3-part article as a reference for writing new tracepoints: - * https://lwn.net/Articles/379903/ - */ - -/* For logging errors in read or write */ -DECLARE_EVENT_CLASS(smb3_rw_err_class, - TP_PROTO(unsigned int xid, - __u64 fid, - __u32 tid, - __u64 sesid, - __u64 offset, - __u32 len, - int rc), - TP_ARGS(xid, fid, tid, sesid, offset, len, rc), - TP_STRUCT__entry( - __field(unsigned int, xid) - __field(__u64, fid) - __field(__u32, tid) - __field(__u64, sesid) - __field(__u64, offset) - __field(__u32, len) - __field(int, rc) - ), - TP_fast_assign( - __entry->xid = xid; - __entry->fid = fid; - __entry->tid = tid; - __entry->sesid = sesid; - __entry->offset = offset; - __entry->len = len; - __entry->rc = rc; - ), - TP_printk("\txid=%u sid=0x%llx tid=0x%x fid=0x%llx offset=0x%llx len=0x%x rc=%d", - __entry->xid, __entry->sesid, __entry->tid, __entry->fid, - __entry->offset, __entry->len, __entry->rc) -) - -#define DEFINE_SMB3_RW_ERR_EVENT(name) \ -DEFINE_EVENT(smb3_rw_err_class, smb3_##name, \ - TP_PROTO(unsigned int xid, \ - __u64 fid, \ - __u32 tid, \ - __u64 sesid, \ - __u64 offset, \ - __u32 len, \ - int rc), \ - TP_ARGS(xid, fid, tid, sesid, offset, len, rc)) - -DEFINE_SMB3_RW_ERR_EVENT(write_err); -DEFINE_SMB3_RW_ERR_EVENT(read_err); -DEFINE_SMB3_RW_ERR_EVENT(query_dir_err); -DEFINE_SMB3_RW_ERR_EVENT(zero_err); -DEFINE_SMB3_RW_ERR_EVENT(falloc_err); - - -/* For logging successful read or write */ -DECLARE_EVENT_CLASS(smb3_rw_done_class, - TP_PROTO(unsigned int xid, - __u64 fid, - __u32 tid, - __u64 sesid, - __u64 offset, - __u32 len), - TP_ARGS(xid, fid, tid, sesid, offset, len), - TP_STRUCT__entry( - __field(unsigned int, xid) - __field(__u64, fid) - __field(__u32, tid) - __field(__u64, sesid) - __field(__u64, offset) - __field(__u32, len) - ), - TP_fast_assign( - __entry->xid = xid; - __entry->fid = fid; - __entry->tid = tid; - __entry->sesid = sesid; - __entry->offset = offset; - __entry->len = len; - ), - TP_printk("xid=%u sid=0x%llx tid=0x%x fid=0x%llx offset=0x%llx len=0x%x", - __entry->xid, __entry->sesid, __entry->tid, __entry->fid, - __entry->offset, __entry->len) -) - -#define DEFINE_SMB3_RW_DONE_EVENT(name) \ -DEFINE_EVENT(smb3_rw_done_class, smb3_##name, \ - TP_PROTO(unsigned int xid, \ - __u64 fid, \ - __u32 tid, \ - __u64 sesid, \ - __u64 offset, \ - __u32 len), \ - TP_ARGS(xid, fid, tid, sesid, offset, len)) - -DEFINE_SMB3_RW_DONE_EVENT(write_enter); -DEFINE_SMB3_RW_DONE_EVENT(read_enter); -DEFINE_SMB3_RW_DONE_EVENT(query_dir_enter); -DEFINE_SMB3_RW_DONE_EVENT(zero_enter); -DEFINE_SMB3_RW_DONE_EVENT(falloc_enter); -DEFINE_SMB3_RW_DONE_EVENT(write_done); -DEFINE_SMB3_RW_DONE_EVENT(read_done); -DEFINE_SMB3_RW_DONE_EVENT(query_dir_done); -DEFINE_SMB3_RW_DONE_EVENT(zero_done); -DEFINE_SMB3_RW_DONE_EVENT(falloc_done); - -/* For logging successful set EOF (truncate) */ -DECLARE_EVENT_CLASS(smb3_eof_class, - TP_PROTO(unsigned int xid, - __u64 fid, - __u32 tid, - __u64 sesid, - __u64 offset), - TP_ARGS(xid, fid, tid, sesid, offset), - TP_STRUCT__entry( - __field(unsigned int, xid) - __field(__u64, fid) - __field(__u32, tid) - __field(__u64, sesid) - __field(__u64, offset) - ), - TP_fast_assign( - __entry->xid = xid; - __entry->fid = fid; - __entry->tid = tid; - __entry->sesid = sesid; - __entry->offset = offset; - ), - TP_printk("xid=%u sid=0x%llx tid=0x%x fid=0x%llx offset=0x%llx", - __entry->xid, __entry->sesid, __entry->tid, __entry->fid, - __entry->offset) -) - -#define DEFINE_SMB3_EOF_EVENT(name) \ -DEFINE_EVENT(smb3_eof_class, smb3_##name, \ - TP_PROTO(unsigned int xid, \ - __u64 fid, \ - __u32 tid, \ - __u64 sesid, \ - __u64 offset), \ - TP_ARGS(xid, fid, tid, sesid, offset)) - -DEFINE_SMB3_EOF_EVENT(set_eof); - -/* - * For handle based calls other than read and write, and get/set info - */ -DECLARE_EVENT_CLASS(smb3_fd_class, - TP_PROTO(unsigned int xid, - __u64 fid, - __u32 tid, - __u64 sesid), - TP_ARGS(xid, fid, tid, sesid), - TP_STRUCT__entry( - __field(unsigned int, xid) - __field(__u64, fid) - __field(__u32, tid) - __field(__u64, sesid) - ), - TP_fast_assign( - __entry->xid = xid; - __entry->fid = fid; - __entry->tid = tid; - __entry->sesid = sesid; - ), - TP_printk("\txid=%u sid=0x%llx tid=0x%x fid=0x%llx", - __entry->xid, __entry->sesid, __entry->tid, __entry->fid) -) - -#define DEFINE_SMB3_FD_EVENT(name) \ -DEFINE_EVENT(smb3_fd_class, smb3_##name, \ - TP_PROTO(unsigned int xid, \ - __u64 fid, \ - __u32 tid, \ - __u64 sesid), \ - TP_ARGS(xid, fid, tid, sesid)) - -DEFINE_SMB3_FD_EVENT(flush_enter); -DEFINE_SMB3_FD_EVENT(flush_done); -DEFINE_SMB3_FD_EVENT(close_enter); -DEFINE_SMB3_FD_EVENT(close_done); -DEFINE_SMB3_FD_EVENT(oplock_not_found); - -DECLARE_EVENT_CLASS(smb3_fd_err_class, - TP_PROTO(unsigned int xid, - __u64 fid, - __u32 tid, - __u64 sesid, - int rc), - TP_ARGS(xid, fid, tid, sesid, rc), - TP_STRUCT__entry( - __field(unsigned int, xid) - __field(__u64, fid) - __field(__u32, tid) - __field(__u64, sesid) - __field(int, rc) - ), - TP_fast_assign( - __entry->xid = xid; - __entry->fid = fid; - __entry->tid = tid; - __entry->sesid = sesid; - __entry->rc = rc; - ), - TP_printk("\txid=%u sid=0x%llx tid=0x%x fid=0x%llx rc=%d", - __entry->xid, __entry->sesid, __entry->tid, __entry->fid, - __entry->rc) -) - -#define DEFINE_SMB3_FD_ERR_EVENT(name) \ -DEFINE_EVENT(smb3_fd_err_class, smb3_##name, \ - TP_PROTO(unsigned int xid, \ - __u64 fid, \ - __u32 tid, \ - __u64 sesid, \ - int rc), \ - TP_ARGS(xid, fid, tid, sesid, rc)) - -DEFINE_SMB3_FD_ERR_EVENT(flush_err); -DEFINE_SMB3_FD_ERR_EVENT(lock_err); -DEFINE_SMB3_FD_ERR_EVENT(close_err); - -/* - * For handle based query/set info calls - */ -DECLARE_EVENT_CLASS(smb3_inf_enter_class, - TP_PROTO(unsigned int xid, - __u64 fid, - __u32 tid, - __u64 sesid, - __u8 infclass, - __u32 type), - TP_ARGS(xid, fid, tid, sesid, infclass, type), - TP_STRUCT__entry( - __field(unsigned int, xid) - __field(__u64, fid) - __field(__u32, tid) - __field(__u64, sesid) - __field(__u8, infclass) - __field(__u32, type) - ), - TP_fast_assign( - __entry->xid = xid; - __entry->fid = fid; - __entry->tid = tid; - __entry->sesid = sesid; - __entry->infclass = infclass; - __entry->type = type; - ), - TP_printk("xid=%u sid=0x%llx tid=0x%x fid=0x%llx class=%u type=0x%x", - __entry->xid, __entry->sesid, __entry->tid, __entry->fid, - __entry->infclass, __entry->type) -) - -#define DEFINE_SMB3_INF_ENTER_EVENT(name) \ -DEFINE_EVENT(smb3_inf_enter_class, smb3_##name, \ - TP_PROTO(unsigned int xid, \ - __u64 fid, \ - __u32 tid, \ - __u64 sesid, \ - __u8 infclass, \ - __u32 type), \ - TP_ARGS(xid, fid, tid, sesid, infclass, type)) - -DEFINE_SMB3_INF_ENTER_EVENT(query_info_enter); -DEFINE_SMB3_INF_ENTER_EVENT(query_info_done); -DEFINE_SMB3_INF_ENTER_EVENT(notify_enter); -DEFINE_SMB3_INF_ENTER_EVENT(notify_done); - -DECLARE_EVENT_CLASS(smb3_inf_err_class, - TP_PROTO(unsigned int xid, - __u64 fid, - __u32 tid, - __u64 sesid, - __u8 infclass, - __u32 type, - int rc), - TP_ARGS(xid, fid, tid, sesid, infclass, type, rc), - TP_STRUCT__entry( - __field(unsigned int, xid) - __field(__u64, fid) - __field(__u32, tid) - __field(__u64, sesid) - __field(__u8, infclass) - __field(__u32, type) - __field(int, rc) - ), - TP_fast_assign( - __entry->xid = xid; - __entry->fid = fid; - __entry->tid = tid; - __entry->sesid = sesid; - __entry->infclass = infclass; - __entry->type = type; - __entry->rc = rc; - ), - TP_printk("xid=%u sid=0x%llx tid=0x%x fid=0x%llx class=%u type=0x%x rc=%d", - __entry->xid, __entry->sesid, __entry->tid, __entry->fid, - __entry->infclass, __entry->type, __entry->rc) -) - -#define DEFINE_SMB3_INF_ERR_EVENT(name) \ -DEFINE_EVENT(smb3_inf_err_class, smb3_##name, \ - TP_PROTO(unsigned int xid, \ - __u64 fid, \ - __u32 tid, \ - __u64 sesid, \ - __u8 infclass, \ - __u32 type, \ - int rc), \ - TP_ARGS(xid, fid, tid, sesid, infclass, type, rc)) - -DEFINE_SMB3_INF_ERR_EVENT(query_info_err); -DEFINE_SMB3_INF_ERR_EVENT(set_info_err); -DEFINE_SMB3_INF_ERR_EVENT(notify_err); -DEFINE_SMB3_INF_ERR_EVENT(fsctl_err); - -DECLARE_EVENT_CLASS(smb3_inf_compound_enter_class, - TP_PROTO(unsigned int xid, - __u32 tid, - __u64 sesid, - const char *full_path), - TP_ARGS(xid, tid, sesid, full_path), - TP_STRUCT__entry( - __field(unsigned int, xid) - __field(__u32, tid) - __field(__u64, sesid) - __string(path, full_path) - ), - TP_fast_assign( - __entry->xid = xid; - __entry->tid = tid; - __entry->sesid = sesid; - __assign_str(path, full_path); - ), - TP_printk("xid=%u sid=0x%llx tid=0x%x path=%s", - __entry->xid, __entry->sesid, __entry->tid, - __get_str(path)) -) - -#define DEFINE_SMB3_INF_COMPOUND_ENTER_EVENT(name) \ -DEFINE_EVENT(smb3_inf_compound_enter_class, smb3_##name, \ - TP_PROTO(unsigned int xid, \ - __u32 tid, \ - __u64 sesid, \ - const char *full_path), \ - TP_ARGS(xid, tid, sesid, full_path)) - -DEFINE_SMB3_INF_COMPOUND_ENTER_EVENT(query_info_compound_enter); -DEFINE_SMB3_INF_COMPOUND_ENTER_EVENT(posix_query_info_compound_enter); -DEFINE_SMB3_INF_COMPOUND_ENTER_EVENT(hardlink_enter); -DEFINE_SMB3_INF_COMPOUND_ENTER_EVENT(rename_enter); -DEFINE_SMB3_INF_COMPOUND_ENTER_EVENT(rmdir_enter); -DEFINE_SMB3_INF_COMPOUND_ENTER_EVENT(set_eof_enter); -DEFINE_SMB3_INF_COMPOUND_ENTER_EVENT(set_info_compound_enter); -DEFINE_SMB3_INF_COMPOUND_ENTER_EVENT(delete_enter); -DEFINE_SMB3_INF_COMPOUND_ENTER_EVENT(mkdir_enter); -DEFINE_SMB3_INF_COMPOUND_ENTER_EVENT(tdis_enter); - - -DECLARE_EVENT_CLASS(smb3_inf_compound_done_class, - TP_PROTO(unsigned int xid, - __u32 tid, - __u64 sesid), - TP_ARGS(xid, tid, sesid), - TP_STRUCT__entry( - __field(unsigned int, xid) - __field(__u32, tid) - __field(__u64, sesid) - ), - TP_fast_assign( - __entry->xid = xid; - __entry->tid = tid; - __entry->sesid = sesid; - ), - TP_printk("xid=%u sid=0x%llx tid=0x%x", - __entry->xid, __entry->sesid, __entry->tid) -) - -#define DEFINE_SMB3_INF_COMPOUND_DONE_EVENT(name) \ -DEFINE_EVENT(smb3_inf_compound_done_class, smb3_##name, \ - TP_PROTO(unsigned int xid, \ - __u32 tid, \ - __u64 sesid), \ - TP_ARGS(xid, tid, sesid)) - -DEFINE_SMB3_INF_COMPOUND_DONE_EVENT(query_info_compound_done); -DEFINE_SMB3_INF_COMPOUND_DONE_EVENT(posix_query_info_compound_done); -DEFINE_SMB3_INF_COMPOUND_DONE_EVENT(hardlink_done); -DEFINE_SMB3_INF_COMPOUND_DONE_EVENT(rename_done); -DEFINE_SMB3_INF_COMPOUND_DONE_EVENT(rmdir_done); -DEFINE_SMB3_INF_COMPOUND_DONE_EVENT(set_eof_done); -DEFINE_SMB3_INF_COMPOUND_DONE_EVENT(set_info_compound_done); -DEFINE_SMB3_INF_COMPOUND_DONE_EVENT(delete_done); -DEFINE_SMB3_INF_COMPOUND_DONE_EVENT(mkdir_done); -DEFINE_SMB3_INF_COMPOUND_DONE_EVENT(tdis_done); - - -DECLARE_EVENT_CLASS(smb3_inf_compound_err_class, - TP_PROTO(unsigned int xid, - __u32 tid, - __u64 sesid, - int rc), - TP_ARGS(xid, tid, sesid, rc), - TP_STRUCT__entry( - __field(unsigned int, xid) - __field(__u32, tid) - __field(__u64, sesid) - __field(int, rc) - ), - TP_fast_assign( - __entry->xid = xid; - __entry->tid = tid; - __entry->sesid = sesid; - __entry->rc = rc; - ), - TP_printk("xid=%u sid=0x%llx tid=0x%x rc=%d", - __entry->xid, __entry->sesid, __entry->tid, - __entry->rc) -) - -#define DEFINE_SMB3_INF_COMPOUND_ERR_EVENT(name) \ -DEFINE_EVENT(smb3_inf_compound_err_class, smb3_##name, \ - TP_PROTO(unsigned int xid, \ - __u32 tid, \ - __u64 sesid, \ - int rc), \ - TP_ARGS(xid, tid, sesid, rc)) - -DEFINE_SMB3_INF_COMPOUND_ERR_EVENT(query_info_compound_err); -DEFINE_SMB3_INF_COMPOUND_ERR_EVENT(posix_query_info_compound_err); -DEFINE_SMB3_INF_COMPOUND_ERR_EVENT(hardlink_err); -DEFINE_SMB3_INF_COMPOUND_ERR_EVENT(rename_err); -DEFINE_SMB3_INF_COMPOUND_ERR_EVENT(rmdir_err); -DEFINE_SMB3_INF_COMPOUND_ERR_EVENT(set_eof_err); -DEFINE_SMB3_INF_COMPOUND_ERR_EVENT(set_info_compound_err); -DEFINE_SMB3_INF_COMPOUND_ERR_EVENT(mkdir_err); -DEFINE_SMB3_INF_COMPOUND_ERR_EVENT(delete_err); -DEFINE_SMB3_INF_COMPOUND_ERR_EVENT(tdis_err); - -/* - * For logging SMB3 Status code and Command for responses which return errors - */ -DECLARE_EVENT_CLASS(smb3_cmd_err_class, - TP_PROTO(__u32 tid, - __u64 sesid, - __u16 cmd, - __u64 mid, - __u32 status, - int rc), - TP_ARGS(tid, sesid, cmd, mid, status, rc), - TP_STRUCT__entry( - __field(__u32, tid) - __field(__u64, sesid) - __field(__u16, cmd) - __field(__u64, mid) - __field(__u32, status) - __field(int, rc) - ), - TP_fast_assign( - __entry->tid = tid; - __entry->sesid = sesid; - __entry->cmd = cmd; - __entry->mid = mid; - __entry->status = status; - __entry->rc = rc; - ), - TP_printk("\tsid=0x%llx tid=0x%x cmd=%u mid=%llu status=0x%x rc=%d", - __entry->sesid, __entry->tid, __entry->cmd, __entry->mid, - __entry->status, __entry->rc) -) - -#define DEFINE_SMB3_CMD_ERR_EVENT(name) \ -DEFINE_EVENT(smb3_cmd_err_class, smb3_##name, \ - TP_PROTO(__u32 tid, \ - __u64 sesid, \ - __u16 cmd, \ - __u64 mid, \ - __u32 status, \ - int rc), \ - TP_ARGS(tid, sesid, cmd, mid, status, rc)) - -DEFINE_SMB3_CMD_ERR_EVENT(cmd_err); - -DECLARE_EVENT_CLASS(smb3_cmd_done_class, - TP_PROTO(__u32 tid, - __u64 sesid, - __u16 cmd, - __u64 mid), - TP_ARGS(tid, sesid, cmd, mid), - TP_STRUCT__entry( - __field(__u32, tid) - __field(__u64, sesid) - __field(__u16, cmd) - __field(__u64, mid) - ), - TP_fast_assign( - __entry->tid = tid; - __entry->sesid = sesid; - __entry->cmd = cmd; - __entry->mid = mid; - ), - TP_printk("\tsid=0x%llx tid=0x%x cmd=%u mid=%llu", - __entry->sesid, __entry->tid, - __entry->cmd, __entry->mid) -) - -#define DEFINE_SMB3_CMD_DONE_EVENT(name) \ -DEFINE_EVENT(smb3_cmd_done_class, smb3_##name, \ - TP_PROTO(__u32 tid, \ - __u64 sesid, \ - __u16 cmd, \ - __u64 mid), \ - TP_ARGS(tid, sesid, cmd, mid)) - -DEFINE_SMB3_CMD_DONE_EVENT(cmd_enter); -DEFINE_SMB3_CMD_DONE_EVENT(cmd_done); -DEFINE_SMB3_CMD_DONE_EVENT(ses_expired); - -DECLARE_EVENT_CLASS(smb3_mid_class, - TP_PROTO(__u16 cmd, - __u64 mid, - __u32 pid, - unsigned long when_sent, - unsigned long when_received), - TP_ARGS(cmd, mid, pid, when_sent, when_received), - TP_STRUCT__entry( - __field(__u16, cmd) - __field(__u64, mid) - __field(__u32, pid) - __field(unsigned long, when_sent) - __field(unsigned long, when_received) - ), - TP_fast_assign( - __entry->cmd = cmd; - __entry->mid = mid; - __entry->pid = pid; - __entry->when_sent = when_sent; - __entry->when_received = when_received; - ), - TP_printk("\tcmd=%u mid=%llu pid=%u, when_sent=%lu when_rcv=%lu", - __entry->cmd, __entry->mid, __entry->pid, __entry->when_sent, - __entry->when_received) -) - -#define DEFINE_SMB3_MID_EVENT(name) \ -DEFINE_EVENT(smb3_mid_class, smb3_##name, \ - TP_PROTO(__u16 cmd, \ - __u64 mid, \ - __u32 pid, \ - unsigned long when_sent, \ - unsigned long when_received), \ - TP_ARGS(cmd, mid, pid, when_sent, when_received)) - -DEFINE_SMB3_MID_EVENT(slow_rsp); - -DECLARE_EVENT_CLASS(smb3_exit_err_class, - TP_PROTO(unsigned int xid, - const char *func_name, - int rc), - TP_ARGS(xid, func_name, rc), - TP_STRUCT__entry( - __field(unsigned int, xid) - __string(func_name, func_name) - __field(int, rc) - ), - TP_fast_assign( - __entry->xid = xid; - __assign_str(func_name, func_name); - __entry->rc = rc; - ), - TP_printk("\t%s: xid=%u rc=%d", - __get_str(func_name), __entry->xid, __entry->rc) -) - -#define DEFINE_SMB3_EXIT_ERR_EVENT(name) \ -DEFINE_EVENT(smb3_exit_err_class, smb3_##name, \ - TP_PROTO(unsigned int xid, \ - const char *func_name, \ - int rc), \ - TP_ARGS(xid, func_name, rc)) - -DEFINE_SMB3_EXIT_ERR_EVENT(exit_err); - - -DECLARE_EVENT_CLASS(smb3_sync_err_class, - TP_PROTO(unsigned long ino, - int rc), - TP_ARGS(ino, rc), - TP_STRUCT__entry( - __field(unsigned long, ino) - __field(int, rc) - ), - TP_fast_assign( - __entry->ino = ino; - __entry->rc = rc; - ), - TP_printk("\tino=%lu rc=%d", - __entry->ino, __entry->rc) -) - -#define DEFINE_SMB3_SYNC_ERR_EVENT(name) \ -DEFINE_EVENT(smb3_sync_err_class, cifs_##name, \ - TP_PROTO(unsigned long ino, \ - int rc), \ - TP_ARGS(ino, rc)) - -DEFINE_SMB3_SYNC_ERR_EVENT(fsync_err); -DEFINE_SMB3_SYNC_ERR_EVENT(flush_err); - - -DECLARE_EVENT_CLASS(smb3_enter_exit_class, - TP_PROTO(unsigned int xid, - const char *func_name), - TP_ARGS(xid, func_name), - TP_STRUCT__entry( - __field(unsigned int, xid) - __string(func_name, func_name) - ), - TP_fast_assign( - __entry->xid = xid; - __assign_str(func_name, func_name); - ), - TP_printk("\t%s: xid=%u", - __get_str(func_name), __entry->xid) -) - -#define DEFINE_SMB3_ENTER_EXIT_EVENT(name) \ -DEFINE_EVENT(smb3_enter_exit_class, smb3_##name, \ - TP_PROTO(unsigned int xid, \ - const char *func_name), \ - TP_ARGS(xid, func_name)) - -DEFINE_SMB3_ENTER_EXIT_EVENT(enter); -DEFINE_SMB3_ENTER_EXIT_EVENT(exit_done); - -/* - * For SMB2/SMB3 tree connect - */ - -DECLARE_EVENT_CLASS(smb3_tcon_class, - TP_PROTO(unsigned int xid, - __u32 tid, - __u64 sesid, - const char *unc_name, - int rc), - TP_ARGS(xid, tid, sesid, unc_name, rc), - TP_STRUCT__entry( - __field(unsigned int, xid) - __field(__u32, tid) - __field(__u64, sesid) - __string(name, unc_name) - __field(int, rc) - ), - TP_fast_assign( - __entry->xid = xid; - __entry->tid = tid; - __entry->sesid = sesid; - __assign_str(name, unc_name); - __entry->rc = rc; - ), - TP_printk("xid=%u sid=0x%llx tid=0x%x unc_name=%s rc=%d", - __entry->xid, __entry->sesid, __entry->tid, - __get_str(name), __entry->rc) -) - -#define DEFINE_SMB3_TCON_EVENT(name) \ -DEFINE_EVENT(smb3_tcon_class, smb3_##name, \ - TP_PROTO(unsigned int xid, \ - __u32 tid, \ - __u64 sesid, \ - const char *unc_name, \ - int rc), \ - TP_ARGS(xid, tid, sesid, unc_name, rc)) - -DEFINE_SMB3_TCON_EVENT(tcon); - - -/* - * For smb2/smb3 open (including create and mkdir) calls - */ - -DECLARE_EVENT_CLASS(smb3_open_enter_class, - TP_PROTO(unsigned int xid, - __u32 tid, - __u64 sesid, - const char *full_path, - int create_options, - int desired_access), - TP_ARGS(xid, tid, sesid, full_path, create_options, desired_access), - TP_STRUCT__entry( - __field(unsigned int, xid) - __field(__u32, tid) - __field(__u64, sesid) - __string(path, full_path) - __field(int, create_options) - __field(int, desired_access) - ), - TP_fast_assign( - __entry->xid = xid; - __entry->tid = tid; - __entry->sesid = sesid; - __assign_str(path, full_path); - __entry->create_options = create_options; - __entry->desired_access = desired_access; - ), - TP_printk("xid=%u sid=0x%llx tid=0x%x path=%s cr_opts=0x%x des_access=0x%x", - __entry->xid, __entry->sesid, __entry->tid, __get_str(path), - __entry->create_options, __entry->desired_access) -) - -#define DEFINE_SMB3_OPEN_ENTER_EVENT(name) \ -DEFINE_EVENT(smb3_open_enter_class, smb3_##name, \ - TP_PROTO(unsigned int xid, \ - __u32 tid, \ - __u64 sesid, \ - const char *full_path, \ - int create_options, \ - int desired_access), \ - TP_ARGS(xid, tid, sesid, full_path, create_options, desired_access)) - -DEFINE_SMB3_OPEN_ENTER_EVENT(open_enter); -DEFINE_SMB3_OPEN_ENTER_EVENT(posix_mkdir_enter); - -DECLARE_EVENT_CLASS(smb3_open_err_class, - TP_PROTO(unsigned int xid, - __u32 tid, - __u64 sesid, - int create_options, - int desired_access, - int rc), - TP_ARGS(xid, tid, sesid, create_options, desired_access, rc), - TP_STRUCT__entry( - __field(unsigned int, xid) - __field(__u32, tid) - __field(__u64, sesid) - __field(int, create_options) - __field(int, desired_access) - __field(int, rc) - ), - TP_fast_assign( - __entry->xid = xid; - __entry->tid = tid; - __entry->sesid = sesid; - __entry->create_options = create_options; - __entry->desired_access = desired_access; - __entry->rc = rc; - ), - TP_printk("xid=%u sid=0x%llx tid=0x%x cr_opts=0x%x des_access=0x%x rc=%d", - __entry->xid, __entry->sesid, __entry->tid, - __entry->create_options, __entry->desired_access, __entry->rc) -) - -#define DEFINE_SMB3_OPEN_ERR_EVENT(name) \ -DEFINE_EVENT(smb3_open_err_class, smb3_##name, \ - TP_PROTO(unsigned int xid, \ - __u32 tid, \ - __u64 sesid, \ - int create_options, \ - int desired_access, \ - int rc), \ - TP_ARGS(xid, tid, sesid, create_options, desired_access, rc)) - -DEFINE_SMB3_OPEN_ERR_EVENT(open_err); -DEFINE_SMB3_OPEN_ERR_EVENT(posix_mkdir_err); - -DECLARE_EVENT_CLASS(smb3_open_done_class, - TP_PROTO(unsigned int xid, - __u64 fid, - __u32 tid, - __u64 sesid, - int create_options, - int desired_access), - TP_ARGS(xid, fid, tid, sesid, create_options, desired_access), - TP_STRUCT__entry( - __field(unsigned int, xid) - __field(__u64, fid) - __field(__u32, tid) - __field(__u64, sesid) - __field(int, create_options) - __field(int, desired_access) - ), - TP_fast_assign( - __entry->xid = xid; - __entry->fid = fid; - __entry->tid = tid; - __entry->sesid = sesid; - __entry->create_options = create_options; - __entry->desired_access = desired_access; - ), - TP_printk("xid=%u sid=0x%llx tid=0x%x fid=0x%llx cr_opts=0x%x des_access=0x%x", - __entry->xid, __entry->sesid, __entry->tid, __entry->fid, - __entry->create_options, __entry->desired_access) -) - -#define DEFINE_SMB3_OPEN_DONE_EVENT(name) \ -DEFINE_EVENT(smb3_open_done_class, smb3_##name, \ - TP_PROTO(unsigned int xid, \ - __u64 fid, \ - __u32 tid, \ - __u64 sesid, \ - int create_options, \ - int desired_access), \ - TP_ARGS(xid, fid, tid, sesid, create_options, desired_access)) - -DEFINE_SMB3_OPEN_DONE_EVENT(open_done); -DEFINE_SMB3_OPEN_DONE_EVENT(posix_mkdir_done); - - -DECLARE_EVENT_CLASS(smb3_lease_done_class, - TP_PROTO(__u32 lease_state, - __u32 tid, - __u64 sesid, - __u64 lease_key_low, - __u64 lease_key_high), - TP_ARGS(lease_state, tid, sesid, lease_key_low, lease_key_high), - TP_STRUCT__entry( - __field(__u32, lease_state) - __field(__u32, tid) - __field(__u64, sesid) - __field(__u64, lease_key_low) - __field(__u64, lease_key_high) - ), - TP_fast_assign( - __entry->lease_state = lease_state; - __entry->tid = tid; - __entry->sesid = sesid; - __entry->lease_key_low = lease_key_low; - __entry->lease_key_high = lease_key_high; - ), - TP_printk("sid=0x%llx tid=0x%x lease_key=0x%llx%llx lease_state=0x%x", - __entry->sesid, __entry->tid, __entry->lease_key_high, - __entry->lease_key_low, __entry->lease_state) -) - -#define DEFINE_SMB3_LEASE_DONE_EVENT(name) \ -DEFINE_EVENT(smb3_lease_done_class, smb3_##name, \ - TP_PROTO(__u32 lease_state, \ - __u32 tid, \ - __u64 sesid, \ - __u64 lease_key_low, \ - __u64 lease_key_high), \ - TP_ARGS(lease_state, tid, sesid, lease_key_low, lease_key_high)) - -DEFINE_SMB3_LEASE_DONE_EVENT(lease_done); -DEFINE_SMB3_LEASE_DONE_EVENT(lease_not_found); - -DECLARE_EVENT_CLASS(smb3_lease_err_class, - TP_PROTO(__u32 lease_state, - __u32 tid, - __u64 sesid, - __u64 lease_key_low, - __u64 lease_key_high, - int rc), - TP_ARGS(lease_state, tid, sesid, lease_key_low, lease_key_high, rc), - TP_STRUCT__entry( - __field(__u32, lease_state) - __field(__u32, tid) - __field(__u64, sesid) - __field(__u64, lease_key_low) - __field(__u64, lease_key_high) - __field(int, rc) - ), - TP_fast_assign( - __entry->lease_state = lease_state; - __entry->tid = tid; - __entry->sesid = sesid; - __entry->lease_key_low = lease_key_low; - __entry->lease_key_high = lease_key_high; - __entry->rc = rc; - ), - TP_printk("sid=0x%llx tid=0x%x lease_key=0x%llx%llx lease_state=0x%x rc=%d", - __entry->sesid, __entry->tid, __entry->lease_key_high, - __entry->lease_key_low, __entry->lease_state, __entry->rc) -) - -#define DEFINE_SMB3_LEASE_ERR_EVENT(name) \ -DEFINE_EVENT(smb3_lease_err_class, smb3_##name, \ - TP_PROTO(__u32 lease_state, \ - __u32 tid, \ - __u64 sesid, \ - __u64 lease_key_low, \ - __u64 lease_key_high, \ - int rc), \ - TP_ARGS(lease_state, tid, sesid, lease_key_low, lease_key_high, rc)) - -DEFINE_SMB3_LEASE_ERR_EVENT(lease_err); - -DECLARE_EVENT_CLASS(smb3_connect_class, - TP_PROTO(char *hostname, - __u64 conn_id, - const struct __kernel_sockaddr_storage *dst_addr), - TP_ARGS(hostname, conn_id, dst_addr), - TP_STRUCT__entry( - __string(hostname, hostname) - __field(__u64, conn_id) - __array(__u8, dst_addr, sizeof(struct sockaddr_storage)) - ), - TP_fast_assign( - struct sockaddr_storage *pss = NULL; - - __entry->conn_id = conn_id; - pss = (struct sockaddr_storage *)__entry->dst_addr; - *pss = *dst_addr; - __assign_str(hostname, hostname); - ), - TP_printk("conn_id=0x%llx server=%s addr=%pISpsfc", - __entry->conn_id, - __get_str(hostname), - __entry->dst_addr) -) - -#define DEFINE_SMB3_CONNECT_EVENT(name) \ -DEFINE_EVENT(smb3_connect_class, smb3_##name, \ - TP_PROTO(char *hostname, \ - __u64 conn_id, \ - const struct __kernel_sockaddr_storage *addr), \ - TP_ARGS(hostname, conn_id, addr)) - -DEFINE_SMB3_CONNECT_EVENT(connect_done); - -DECLARE_EVENT_CLASS(smb3_connect_err_class, - TP_PROTO(char *hostname, __u64 conn_id, - const struct __kernel_sockaddr_storage *dst_addr, int rc), - TP_ARGS(hostname, conn_id, dst_addr, rc), - TP_STRUCT__entry( - __string(hostname, hostname) - __field(__u64, conn_id) - __array(__u8, dst_addr, sizeof(struct sockaddr_storage)) - __field(int, rc) - ), - TP_fast_assign( - struct sockaddr_storage *pss = NULL; - - __entry->conn_id = conn_id; - __entry->rc = rc; - pss = (struct sockaddr_storage *)__entry->dst_addr; - *pss = *dst_addr; - __assign_str(hostname, hostname); - ), - TP_printk("rc=%d conn_id=0x%llx server=%s addr=%pISpsfc", - __entry->rc, - __entry->conn_id, - __get_str(hostname), - __entry->dst_addr) -) - -#define DEFINE_SMB3_CONNECT_ERR_EVENT(name) \ -DEFINE_EVENT(smb3_connect_err_class, smb3_##name, \ - TP_PROTO(char *hostname, \ - __u64 conn_id, \ - const struct __kernel_sockaddr_storage *addr, \ - int rc), \ - TP_ARGS(hostname, conn_id, addr, rc)) - -DEFINE_SMB3_CONNECT_ERR_EVENT(connect_err); - -DECLARE_EVENT_CLASS(smb3_reconnect_class, - TP_PROTO(__u64 currmid, - __u64 conn_id, - char *hostname), - TP_ARGS(currmid, conn_id, hostname), - TP_STRUCT__entry( - __field(__u64, currmid) - __field(__u64, conn_id) - __string(hostname, hostname) - ), - TP_fast_assign( - __entry->currmid = currmid; - __entry->conn_id = conn_id; - __assign_str(hostname, hostname); - ), - TP_printk("conn_id=0x%llx server=%s current_mid=%llu", - __entry->conn_id, - __get_str(hostname), - __entry->currmid) -) - -#define DEFINE_SMB3_RECONNECT_EVENT(name) \ -DEFINE_EVENT(smb3_reconnect_class, smb3_##name, \ - TP_PROTO(__u64 currmid, \ - __u64 conn_id, \ - char *hostname), \ - TP_ARGS(currmid, conn_id, hostname)) - -DEFINE_SMB3_RECONNECT_EVENT(reconnect); -DEFINE_SMB3_RECONNECT_EVENT(partial_send_reconnect); - -DECLARE_EVENT_CLASS(smb3_credit_class, - TP_PROTO(__u64 currmid, - __u64 conn_id, - char *hostname, - int credits, - int credits_to_add, - int in_flight), - TP_ARGS(currmid, conn_id, hostname, credits, credits_to_add, in_flight), - TP_STRUCT__entry( - __field(__u64, currmid) - __field(__u64, conn_id) - __string(hostname, hostname) - __field(int, credits) - __field(int, credits_to_add) - __field(int, in_flight) - ), - TP_fast_assign( - __entry->currmid = currmid; - __entry->conn_id = conn_id; - __assign_str(hostname, hostname); - __entry->credits = credits; - __entry->credits_to_add = credits_to_add; - __entry->in_flight = in_flight; - ), - TP_printk("conn_id=0x%llx server=%s current_mid=%llu " - "credits=%d credit_change=%d in_flight=%d", - __entry->conn_id, - __get_str(hostname), - __entry->currmid, - __entry->credits, - __entry->credits_to_add, - __entry->in_flight) -) - -#define DEFINE_SMB3_CREDIT_EVENT(name) \ -DEFINE_EVENT(smb3_credit_class, smb3_##name, \ - TP_PROTO(__u64 currmid, \ - __u64 conn_id, \ - char *hostname, \ - int credits, \ - int credits_to_add, \ - int in_flight), \ - TP_ARGS(currmid, conn_id, hostname, credits, credits_to_add, in_flight)) - -DEFINE_SMB3_CREDIT_EVENT(reconnect_with_invalid_credits); -DEFINE_SMB3_CREDIT_EVENT(reconnect_detected); -DEFINE_SMB3_CREDIT_EVENT(credit_timeout); -DEFINE_SMB3_CREDIT_EVENT(insufficient_credits); -DEFINE_SMB3_CREDIT_EVENT(too_many_credits); -DEFINE_SMB3_CREDIT_EVENT(add_credits); -DEFINE_SMB3_CREDIT_EVENT(adj_credits); -DEFINE_SMB3_CREDIT_EVENT(hdr_credits); -DEFINE_SMB3_CREDIT_EVENT(nblk_credits); -DEFINE_SMB3_CREDIT_EVENT(pend_credits); -DEFINE_SMB3_CREDIT_EVENT(wait_credits); -DEFINE_SMB3_CREDIT_EVENT(waitff_credits); -DEFINE_SMB3_CREDIT_EVENT(overflow_credits); -DEFINE_SMB3_CREDIT_EVENT(set_credits); - -#endif /* _CIFS_TRACE_H */ - -#undef TRACE_INCLUDE_PATH -#define TRACE_INCLUDE_PATH . -#define TRACE_INCLUDE_FILE trace -#include diff --git a/fs/cifs/transport.c b/fs/cifs/transport.c deleted file mode 100644 index c961b90f92b9f..0000000000000 --- a/fs/cifs/transport.c +++ /dev/null @@ -1,1807 +0,0 @@ -// SPDX-License-Identifier: LGPL-2.1 -/* - * - * Copyright (C) International Business Machines Corp., 2002,2008 - * Author(s): Steve French (sfrench@us.ibm.com) - * Jeremy Allison (jra@samba.org) 2006. - * - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include "cifspdu.h" -#include "cifsglob.h" -#include "cifsproto.h" -#include "cifs_debug.h" -#include "smb2proto.h" -#include "smbdirect.h" - -/* Max number of iovectors we can use off the stack when sending requests. */ -#define CIFS_MAX_IOV_SIZE 8 - -void -cifs_wake_up_task(struct mid_q_entry *mid) -{ - wake_up_process(mid->callback_data); -} - -static struct mid_q_entry * -alloc_mid(const struct smb_hdr *smb_buffer, struct TCP_Server_Info *server) -{ - struct mid_q_entry *temp; - - if (server == NULL) { - cifs_dbg(VFS, "%s: null TCP session\n", __func__); - return NULL; - } - - temp = mempool_alloc(cifs_mid_poolp, GFP_NOFS); - memset(temp, 0, sizeof(struct mid_q_entry)); - kref_init(&temp->refcount); - temp->mid = get_mid(smb_buffer); - temp->pid = current->pid; - temp->command = cpu_to_le16(smb_buffer->Command); - cifs_dbg(FYI, "For smb_command %d\n", smb_buffer->Command); - /* do_gettimeofday(&temp->when_sent);*/ /* easier to use jiffies */ - /* when mid allocated can be before when sent */ - temp->when_alloc = jiffies; - temp->server = server; - - /* - * The default is for the mid to be synchronous, so the - * default callback just wakes up the current task. - */ - get_task_struct(current); - temp->creator = current; - temp->callback = cifs_wake_up_task; - temp->callback_data = current; - - atomic_inc(&mid_count); - temp->mid_state = MID_REQUEST_ALLOCATED; - return temp; -} - -static void __release_mid(struct kref *refcount) -{ - struct mid_q_entry *midEntry = - container_of(refcount, struct mid_q_entry, refcount); -#ifdef CONFIG_CIFS_STATS2 - __le16 command = midEntry->server->vals->lock_cmd; - __u16 smb_cmd = le16_to_cpu(midEntry->command); - unsigned long now; - unsigned long roundtrip_time; -#endif - struct TCP_Server_Info *server = midEntry->server; - - if (midEntry->resp_buf && (midEntry->mid_flags & MID_WAIT_CANCELLED) && - midEntry->mid_state == MID_RESPONSE_RECEIVED && - server->ops->handle_cancelled_mid) - server->ops->handle_cancelled_mid(midEntry, server); - - midEntry->mid_state = MID_FREE; - atomic_dec(&mid_count); - if (midEntry->large_buf) - cifs_buf_release(midEntry->resp_buf); - else - cifs_small_buf_release(midEntry->resp_buf); -#ifdef CONFIG_CIFS_STATS2 - now = jiffies; - if (now < midEntry->when_alloc) - cifs_server_dbg(VFS, "Invalid mid allocation time\n"); - roundtrip_time = now - midEntry->when_alloc; - - if (smb_cmd < NUMBER_OF_SMB2_COMMANDS) { - if (atomic_read(&server->num_cmds[smb_cmd]) == 0) { - server->slowest_cmd[smb_cmd] = roundtrip_time; - server->fastest_cmd[smb_cmd] = roundtrip_time; - } else { - if (server->slowest_cmd[smb_cmd] < roundtrip_time) - server->slowest_cmd[smb_cmd] = roundtrip_time; - else if (server->fastest_cmd[smb_cmd] > roundtrip_time) - server->fastest_cmd[smb_cmd] = roundtrip_time; - } - cifs_stats_inc(&server->num_cmds[smb_cmd]); - server->time_per_cmd[smb_cmd] += roundtrip_time; - } - /* - * commands taking longer than one second (default) can be indications - * that something is wrong, unless it is quite a slow link or a very - * busy server. Note that this calc is unlikely or impossible to wrap - * as long as slow_rsp_threshold is not set way above recommended max - * value (32767 ie 9 hours) and is generally harmless even if wrong - * since only affects debug counters - so leaving the calc as simple - * comparison rather than doing multiple conversions and overflow - * checks - */ - if ((slow_rsp_threshold != 0) && - time_after(now, midEntry->when_alloc + (slow_rsp_threshold * HZ)) && - (midEntry->command != command)) { - /* - * smb2slowcmd[NUMBER_OF_SMB2_COMMANDS] counts by command - * NB: le16_to_cpu returns unsigned so can not be negative below - */ - if (smb_cmd < NUMBER_OF_SMB2_COMMANDS) - cifs_stats_inc(&server->smb2slowcmd[smb_cmd]); - - trace_smb3_slow_rsp(smb_cmd, midEntry->mid, midEntry->pid, - midEntry->when_sent, midEntry->when_received); - if (cifsFYI & CIFS_TIMER) { - pr_debug("slow rsp: cmd %d mid %llu", - midEntry->command, midEntry->mid); - cifs_info("A: 0x%lx S: 0x%lx R: 0x%lx\n", - now - midEntry->when_alloc, - now - midEntry->when_sent, - now - midEntry->when_received); - } - } -#endif - put_task_struct(midEntry->creator); - - mempool_free(midEntry, cifs_mid_poolp); -} - -void release_mid(struct mid_q_entry *mid) -{ - struct TCP_Server_Info *server = mid->server; - - spin_lock(&server->mid_lock); - kref_put(&mid->refcount, __release_mid); - spin_unlock(&server->mid_lock); -} - -void -delete_mid(struct mid_q_entry *mid) -{ - spin_lock(&mid->server->mid_lock); - if (!(mid->mid_flags & MID_DELETED)) { - list_del_init(&mid->qhead); - mid->mid_flags |= MID_DELETED; - } - spin_unlock(&mid->server->mid_lock); - - release_mid(mid); -} - -/* - * smb_send_kvec - send an array of kvecs to the server - * @server: Server to send the data to - * @smb_msg: Message to send - * @sent: amount of data sent on socket is stored here - * - * Our basic "send data to server" function. Should be called with srv_mutex - * held. The caller is responsible for handling the results. - */ -static int -smb_send_kvec(struct TCP_Server_Info *server, struct msghdr *smb_msg, - size_t *sent) -{ - int rc = 0; - int retries = 0; - struct socket *ssocket = server->ssocket; - - *sent = 0; - - if (server->noblocksnd) - smb_msg->msg_flags = MSG_DONTWAIT + MSG_NOSIGNAL; - else - smb_msg->msg_flags = MSG_NOSIGNAL; - - while (msg_data_left(smb_msg)) { - /* - * If blocking send, we try 3 times, since each can block - * for 5 seconds. For nonblocking we have to try more - * but wait increasing amounts of time allowing time for - * socket to clear. The overall time we wait in either - * case to send on the socket is about 15 seconds. - * Similarly we wait for 15 seconds for a response from - * the server in SendReceive[2] for the server to send - * a response back for most types of requests (except - * SMB Write past end of file which can be slow, and - * blocking lock operations). NFS waits slightly longer - * than CIFS, but this can make it take longer for - * nonresponsive servers to be detected and 15 seconds - * is more than enough time for modern networks to - * send a packet. In most cases if we fail to send - * after the retries we will kill the socket and - * reconnect which may clear the network problem. - */ - rc = sock_sendmsg(ssocket, smb_msg); - if (rc == -EAGAIN) { - retries++; - if (retries >= 14 || - (!server->noblocksnd && (retries > 2))) { - cifs_server_dbg(VFS, "sends on sock %p stuck for 15 seconds\n", - ssocket); - return -EAGAIN; - } - msleep(1 << retries); - continue; - } - - if (rc < 0) - return rc; - - if (rc == 0) { - /* should never happen, letting socket clear before - retrying is our only obvious option here */ - cifs_server_dbg(VFS, "tcp sent no data\n"); - msleep(500); - continue; - } - - /* send was at least partially successful */ - *sent += rc; - retries = 0; /* in case we get ENOSPC on the next send */ - } - return 0; -} - -unsigned long -smb_rqst_len(struct TCP_Server_Info *server, struct smb_rqst *rqst) -{ - unsigned int i; - struct kvec *iov; - int nvec; - unsigned long buflen = 0; - - if (!is_smb1(server) && rqst->rq_nvec >= 2 && - rqst->rq_iov[0].iov_len == 4) { - iov = &rqst->rq_iov[1]; - nvec = rqst->rq_nvec - 1; - } else { - iov = rqst->rq_iov; - nvec = rqst->rq_nvec; - } - - /* total up iov array first */ - for (i = 0; i < nvec; i++) - buflen += iov[i].iov_len; - - /* - * Add in the page array if there is one. The caller needs to make - * sure rq_offset and rq_tailsz are set correctly. If a buffer of - * multiple pages ends at page boundary, rq_tailsz needs to be set to - * PAGE_SIZE. - */ - if (rqst->rq_npages) { - if (rqst->rq_npages == 1) - buflen += rqst->rq_tailsz; - else { - /* - * If there is more than one page, calculate the - * buffer length based on rq_offset and rq_tailsz - */ - buflen += rqst->rq_pagesz * (rqst->rq_npages - 1) - - rqst->rq_offset; - buflen += rqst->rq_tailsz; - } - } - - return buflen; -} - -static int -__smb_send_rqst(struct TCP_Server_Info *server, int num_rqst, - struct smb_rqst *rqst) -{ - int rc; - struct kvec *iov; - int n_vec; - unsigned int send_length = 0; - unsigned int i, j; - sigset_t mask, oldmask; - size_t total_len = 0, sent, size; - struct socket *ssocket = server->ssocket; - struct msghdr smb_msg = {}; - __be32 rfc1002_marker; - - cifs_in_send_inc(server); - if (cifs_rdma_enabled(server)) { - /* return -EAGAIN when connecting or reconnecting */ - rc = -EAGAIN; - if (server->smbd_conn) - rc = smbd_send(server, num_rqst, rqst); - goto smbd_done; - } - - rc = -EAGAIN; - if (ssocket == NULL) - goto out; - - rc = -ERESTARTSYS; - if (fatal_signal_pending(current)) { - cifs_dbg(FYI, "signal pending before send request\n"); - goto out; - } - - rc = 0; - /* cork the socket */ - tcp_sock_set_cork(ssocket->sk, true); - - for (j = 0; j < num_rqst; j++) - send_length += smb_rqst_len(server, &rqst[j]); - rfc1002_marker = cpu_to_be32(send_length); - - /* - * We should not allow signals to interrupt the network send because - * any partial send will cause session reconnects thus increasing - * latency of system calls and overload a server with unnecessary - * requests. - */ - - sigfillset(&mask); - sigprocmask(SIG_BLOCK, &mask, &oldmask); - - /* Generate a rfc1002 marker for SMB2+ */ - if (!is_smb1(server)) { - struct kvec hiov = { - .iov_base = &rfc1002_marker, - .iov_len = 4 - }; - iov_iter_kvec(&smb_msg.msg_iter, ITER_SOURCE, &hiov, 1, 4); - rc = smb_send_kvec(server, &smb_msg, &sent); - if (rc < 0) - goto unmask; - - total_len += sent; - send_length += 4; - } - - cifs_dbg(FYI, "Sending smb: smb_len=%u\n", send_length); - - for (j = 0; j < num_rqst; j++) { - iov = rqst[j].rq_iov; - n_vec = rqst[j].rq_nvec; - - size = 0; - for (i = 0; i < n_vec; i++) { - dump_smb(iov[i].iov_base, iov[i].iov_len); - size += iov[i].iov_len; - } - - iov_iter_kvec(&smb_msg.msg_iter, ITER_SOURCE, iov, n_vec, size); - - rc = smb_send_kvec(server, &smb_msg, &sent); - if (rc < 0) - goto unmask; - - total_len += sent; - - /* now walk the page array and send each page in it */ - for (i = 0; i < rqst[j].rq_npages; i++) { - struct bio_vec bvec; - - bvec.bv_page = rqst[j].rq_pages[i]; - rqst_page_get_length(&rqst[j], i, &bvec.bv_len, - &bvec.bv_offset); - - iov_iter_bvec(&smb_msg.msg_iter, ITER_SOURCE, - &bvec, 1, bvec.bv_len); - rc = smb_send_kvec(server, &smb_msg, &sent); - if (rc < 0) - break; - - total_len += sent; - } - } - -unmask: - sigprocmask(SIG_SETMASK, &oldmask, NULL); - - /* - * If signal is pending but we have already sent the whole packet to - * the server we need to return success status to allow a corresponding - * mid entry to be kept in the pending requests queue thus allowing - * to handle responses from the server by the client. - * - * If only part of the packet has been sent there is no need to hide - * interrupt because the session will be reconnected anyway, so there - * won't be any response from the server to handle. - */ - - if (signal_pending(current) && (total_len != send_length)) { - cifs_dbg(FYI, "signal is pending after attempt to send\n"); - rc = -ERESTARTSYS; - } - - /* uncork it */ - tcp_sock_set_cork(ssocket->sk, false); - - if ((total_len > 0) && (total_len != send_length)) { - cifs_dbg(FYI, "partial send (wanted=%u sent=%zu): terminating session\n", - send_length, total_len); - /* - * If we have only sent part of an SMB then the next SMB could - * be taken as the remainder of this one. We need to kill the - * socket so the server throws away the partial SMB - */ - cifs_signal_cifsd_for_reconnect(server, false); - trace_smb3_partial_send_reconnect(server->CurrentMid, - server->conn_id, server->hostname); - } -smbd_done: - if (rc < 0 && rc != -EINTR) - cifs_server_dbg(VFS, "Error %d sending data on socket to server\n", - rc); - else if (rc > 0) - rc = 0; -out: - cifs_in_send_dec(server); - return rc; -} - -static int -smb_send_rqst(struct TCP_Server_Info *server, int num_rqst, - struct smb_rqst *rqst, int flags) -{ - struct kvec iov; - struct smb2_transform_hdr *tr_hdr; - struct smb_rqst cur_rqst[MAX_COMPOUND]; - int rc; - - if (!(flags & CIFS_TRANSFORM_REQ)) - return __smb_send_rqst(server, num_rqst, rqst); - - if (num_rqst > MAX_COMPOUND - 1) - return -ENOMEM; - - if (!server->ops->init_transform_rq) { - cifs_server_dbg(VFS, "Encryption requested but transform callback is missing\n"); - return -EIO; - } - - tr_hdr = kzalloc(sizeof(*tr_hdr), GFP_NOFS); - if (!tr_hdr) - return -ENOMEM; - - memset(&cur_rqst[0], 0, sizeof(cur_rqst)); - memset(&iov, 0, sizeof(iov)); - - iov.iov_base = tr_hdr; - iov.iov_len = sizeof(*tr_hdr); - cur_rqst[0].rq_iov = &iov; - cur_rqst[0].rq_nvec = 1; - - rc = server->ops->init_transform_rq(server, num_rqst + 1, - &cur_rqst[0], rqst); - if (rc) - goto out; - - rc = __smb_send_rqst(server, num_rqst + 1, &cur_rqst[0]); - smb3_free_compound_rqst(num_rqst, &cur_rqst[1]); -out: - kfree(tr_hdr); - return rc; -} - -int -smb_send(struct TCP_Server_Info *server, struct smb_hdr *smb_buffer, - unsigned int smb_buf_length) -{ - struct kvec iov[2]; - struct smb_rqst rqst = { .rq_iov = iov, - .rq_nvec = 2 }; - - iov[0].iov_base = smb_buffer; - iov[0].iov_len = 4; - iov[1].iov_base = (char *)smb_buffer + 4; - iov[1].iov_len = smb_buf_length; - - return __smb_send_rqst(server, 1, &rqst); -} - -static int -wait_for_free_credits(struct TCP_Server_Info *server, const int num_credits, - const int timeout, const int flags, - unsigned int *instance) -{ - long rc; - int *credits; - int optype; - long int t; - int scredits, in_flight; - - if (timeout < 0) - t = MAX_JIFFY_OFFSET; - else - t = msecs_to_jiffies(timeout); - - optype = flags & CIFS_OP_MASK; - - *instance = 0; - - credits = server->ops->get_credits_field(server, optype); - /* Since an echo is already inflight, no need to wait to send another */ - if (*credits <= 0 && optype == CIFS_ECHO_OP) - return -EAGAIN; - - spin_lock(&server->req_lock); - if ((flags & CIFS_TIMEOUT_MASK) == CIFS_NON_BLOCKING) { - /* oplock breaks must not be held up */ - server->in_flight++; - if (server->in_flight > server->max_in_flight) - server->max_in_flight = server->in_flight; - *credits -= 1; - *instance = server->reconnect_instance; - scredits = *credits; - in_flight = server->in_flight; - spin_unlock(&server->req_lock); - - trace_smb3_nblk_credits(server->CurrentMid, - server->conn_id, server->hostname, scredits, -1, in_flight); - cifs_dbg(FYI, "%s: remove %u credits total=%d\n", - __func__, 1, scredits); - - return 0; - } - - while (1) { - if (*credits < num_credits) { - scredits = *credits; - spin_unlock(&server->req_lock); - - cifs_num_waiters_inc(server); - rc = wait_event_killable_timeout(server->request_q, - has_credits(server, credits, num_credits), t); - cifs_num_waiters_dec(server); - if (!rc) { - spin_lock(&server->req_lock); - scredits = *credits; - in_flight = server->in_flight; - spin_unlock(&server->req_lock); - - trace_smb3_credit_timeout(server->CurrentMid, - server->conn_id, server->hostname, scredits, - num_credits, in_flight); - cifs_server_dbg(VFS, "wait timed out after %d ms\n", - timeout); - return -EBUSY; - } - if (rc == -ERESTARTSYS) - return -ERESTARTSYS; - spin_lock(&server->req_lock); - } else { - spin_unlock(&server->req_lock); - - spin_lock(&server->srv_lock); - if (server->tcpStatus == CifsExiting) { - spin_unlock(&server->srv_lock); - return -ENOENT; - } - spin_unlock(&server->srv_lock); - - /* - * For normal commands, reserve the last MAX_COMPOUND - * credits to compound requests. - * Otherwise these compounds could be permanently - * starved for credits by single-credit requests. - * - * To prevent spinning CPU, block this thread until - * there are >MAX_COMPOUND credits available. - * But only do this is we already have a lot of - * credits in flight to avoid triggering this check - * for servers that are slow to hand out credits on - * new sessions. - */ - spin_lock(&server->req_lock); - if (!optype && num_credits == 1 && - server->in_flight > 2 * MAX_COMPOUND && - *credits <= MAX_COMPOUND) { - spin_unlock(&server->req_lock); - - cifs_num_waiters_inc(server); - rc = wait_event_killable_timeout( - server->request_q, - has_credits(server, credits, - MAX_COMPOUND + 1), - t); - cifs_num_waiters_dec(server); - if (!rc) { - spin_lock(&server->req_lock); - scredits = *credits; - in_flight = server->in_flight; - spin_unlock(&server->req_lock); - - trace_smb3_credit_timeout( - server->CurrentMid, - server->conn_id, server->hostname, - scredits, num_credits, in_flight); - cifs_server_dbg(VFS, "wait timed out after %d ms\n", - timeout); - return -EBUSY; - } - if (rc == -ERESTARTSYS) - return -ERESTARTSYS; - spin_lock(&server->req_lock); - continue; - } - - /* - * Can not count locking commands against total - * as they are allowed to block on server. - */ - - /* update # of requests on the wire to server */ - if ((flags & CIFS_TIMEOUT_MASK) != CIFS_BLOCKING_OP) { - *credits -= num_credits; - server->in_flight += num_credits; - if (server->in_flight > server->max_in_flight) - server->max_in_flight = server->in_flight; - *instance = server->reconnect_instance; - } - scredits = *credits; - in_flight = server->in_flight; - spin_unlock(&server->req_lock); - - trace_smb3_waitff_credits(server->CurrentMid, - server->conn_id, server->hostname, scredits, - -(num_credits), in_flight); - cifs_dbg(FYI, "%s: remove %u credits total=%d\n", - __func__, num_credits, scredits); - break; - } - } - return 0; -} - -static int -wait_for_free_request(struct TCP_Server_Info *server, const int flags, - unsigned int *instance) -{ - return wait_for_free_credits(server, 1, -1, flags, - instance); -} - -static int -wait_for_compound_request(struct TCP_Server_Info *server, int num, - const int flags, unsigned int *instance) -{ - int *credits; - int scredits, in_flight; - - credits = server->ops->get_credits_field(server, flags & CIFS_OP_MASK); - - spin_lock(&server->req_lock); - scredits = *credits; - in_flight = server->in_flight; - - if (*credits < num) { - /* - * If the server is tight on resources or just gives us less - * credits for other reasons (e.g. requests are coming out of - * order and the server delays granting more credits until it - * processes a missing mid) and we exhausted most available - * credits there may be situations when we try to send - * a compound request but we don't have enough credits. At this - * point the client needs to decide if it should wait for - * additional credits or fail the request. If at least one - * request is in flight there is a high probability that the - * server will return enough credits to satisfy this compound - * request. - * - * Return immediately if no requests in flight since we will be - * stuck on waiting for credits. - */ - if (server->in_flight == 0) { - spin_unlock(&server->req_lock); - trace_smb3_insufficient_credits(server->CurrentMid, - server->conn_id, server->hostname, scredits, - num, in_flight); - cifs_dbg(FYI, "%s: %d requests in flight, needed %d total=%d\n", - __func__, in_flight, num, scredits); - return -EDEADLK; - } - } - spin_unlock(&server->req_lock); - - return wait_for_free_credits(server, num, 60000, flags, - instance); -} - -int -cifs_wait_mtu_credits(struct TCP_Server_Info *server, unsigned int size, - unsigned int *num, struct cifs_credits *credits) -{ - *num = size; - credits->value = 0; - credits->instance = server->reconnect_instance; - return 0; -} - -static int allocate_mid(struct cifs_ses *ses, struct smb_hdr *in_buf, - struct mid_q_entry **ppmidQ) -{ - spin_lock(&ses->ses_lock); - if (ses->ses_status == SES_NEW) { - if ((in_buf->Command != SMB_COM_SESSION_SETUP_ANDX) && - (in_buf->Command != SMB_COM_NEGOTIATE)) { - spin_unlock(&ses->ses_lock); - return -EAGAIN; - } - /* else ok - we are setting up session */ - } - - if (ses->ses_status == SES_EXITING) { - /* check if SMB session is bad because we are setting it up */ - if (in_buf->Command != SMB_COM_LOGOFF_ANDX) { - spin_unlock(&ses->ses_lock); - return -EAGAIN; - } - /* else ok - we are shutting down session */ - } - spin_unlock(&ses->ses_lock); - - *ppmidQ = alloc_mid(in_buf, ses->server); - if (*ppmidQ == NULL) - return -ENOMEM; - spin_lock(&ses->server->mid_lock); - list_add_tail(&(*ppmidQ)->qhead, &ses->server->pending_mid_q); - spin_unlock(&ses->server->mid_lock); - return 0; -} - -static int -wait_for_response(struct TCP_Server_Info *server, struct mid_q_entry *midQ) -{ - int error; - - error = wait_event_state(server->response_q, - midQ->mid_state != MID_REQUEST_SUBMITTED, - (TASK_KILLABLE|TASK_FREEZABLE_UNSAFE)); - if (error < 0) - return -ERESTARTSYS; - - return 0; -} - -struct mid_q_entry * -cifs_setup_async_request(struct TCP_Server_Info *server, struct smb_rqst *rqst) -{ - int rc; - struct smb_hdr *hdr = (struct smb_hdr *)rqst->rq_iov[0].iov_base; - struct mid_q_entry *mid; - - if (rqst->rq_iov[0].iov_len != 4 || - rqst->rq_iov[0].iov_base + 4 != rqst->rq_iov[1].iov_base) - return ERR_PTR(-EIO); - - /* enable signing if server requires it */ - if (server->sign) - hdr->Flags2 |= SMBFLG2_SECURITY_SIGNATURE; - - mid = alloc_mid(hdr, server); - if (mid == NULL) - return ERR_PTR(-ENOMEM); - - rc = cifs_sign_rqst(rqst, server, &mid->sequence_number); - if (rc) { - release_mid(mid); - return ERR_PTR(rc); - } - - return mid; -} - -/* - * Send a SMB request and set the callback function in the mid to handle - * the result. Caller is responsible for dealing with timeouts. - */ -int -cifs_call_async(struct TCP_Server_Info *server, struct smb_rqst *rqst, - mid_receive_t *receive, mid_callback_t *callback, - mid_handle_t *handle, void *cbdata, const int flags, - const struct cifs_credits *exist_credits) -{ - int rc; - struct mid_q_entry *mid; - struct cifs_credits credits = { .value = 0, .instance = 0 }; - unsigned int instance; - int optype; - - optype = flags & CIFS_OP_MASK; - - if ((flags & CIFS_HAS_CREDITS) == 0) { - rc = wait_for_free_request(server, flags, &instance); - if (rc) - return rc; - credits.value = 1; - credits.instance = instance; - } else - instance = exist_credits->instance; - - cifs_server_lock(server); - - /* - * We can't use credits obtained from the previous session to send this - * request. Check if there were reconnects after we obtained credits and - * return -EAGAIN in such cases to let callers handle it. - */ - if (instance != server->reconnect_instance) { - cifs_server_unlock(server); - add_credits_and_wake_if(server, &credits, optype); - return -EAGAIN; - } - - mid = server->ops->setup_async_request(server, rqst); - if (IS_ERR(mid)) { - cifs_server_unlock(server); - add_credits_and_wake_if(server, &credits, optype); - return PTR_ERR(mid); - } - - mid->receive = receive; - mid->callback = callback; - mid->callback_data = cbdata; - mid->handle = handle; - mid->mid_state = MID_REQUEST_SUBMITTED; - - /* put it on the pending_mid_q */ - spin_lock(&server->mid_lock); - list_add_tail(&mid->qhead, &server->pending_mid_q); - spin_unlock(&server->mid_lock); - - /* - * Need to store the time in mid before calling I/O. For call_async, - * I/O response may come back and free the mid entry on another thread. - */ - cifs_save_when_sent(mid); - rc = smb_send_rqst(server, 1, rqst, flags); - - if (rc < 0) { - revert_current_mid(server, mid->credits); - server->sequence_number -= 2; - delete_mid(mid); - } - - cifs_server_unlock(server); - - if (rc == 0) - return 0; - - add_credits_and_wake_if(server, &credits, optype); - return rc; -} - -/* - * - * Send an SMB Request. No response info (other than return code) - * needs to be parsed. - * - * flags indicate the type of request buffer and how long to wait - * and whether to log NT STATUS code (error) before mapping it to POSIX error - * - */ -int -SendReceiveNoRsp(const unsigned int xid, struct cifs_ses *ses, - char *in_buf, int flags) -{ - int rc; - struct kvec iov[1]; - struct kvec rsp_iov; - int resp_buf_type; - - iov[0].iov_base = in_buf; - iov[0].iov_len = get_rfc1002_length(in_buf) + 4; - flags |= CIFS_NO_RSP_BUF; - rc = SendReceive2(xid, ses, iov, 1, &resp_buf_type, flags, &rsp_iov); - cifs_dbg(NOISY, "SendRcvNoRsp flags %d rc %d\n", flags, rc); - - return rc; -} - -static int -cifs_sync_mid_result(struct mid_q_entry *mid, struct TCP_Server_Info *server) -{ - int rc = 0; - - cifs_dbg(FYI, "%s: cmd=%d mid=%llu state=%d\n", - __func__, le16_to_cpu(mid->command), mid->mid, mid->mid_state); - - spin_lock(&server->mid_lock); - switch (mid->mid_state) { - case MID_RESPONSE_RECEIVED: - spin_unlock(&server->mid_lock); - return rc; - case MID_RETRY_NEEDED: - rc = -EAGAIN; - break; - case MID_RESPONSE_MALFORMED: - rc = -EIO; - break; - case MID_SHUTDOWN: - rc = -EHOSTDOWN; - break; - default: - if (!(mid->mid_flags & MID_DELETED)) { - list_del_init(&mid->qhead); - mid->mid_flags |= MID_DELETED; - } - cifs_server_dbg(VFS, "%s: invalid mid state mid=%llu state=%d\n", - __func__, mid->mid, mid->mid_state); - rc = -EIO; - } - spin_unlock(&server->mid_lock); - - release_mid(mid); - return rc; -} - -static inline int -send_cancel(struct TCP_Server_Info *server, struct smb_rqst *rqst, - struct mid_q_entry *mid) -{ - return server->ops->send_cancel ? - server->ops->send_cancel(server, rqst, mid) : 0; -} - -int -cifs_check_receive(struct mid_q_entry *mid, struct TCP_Server_Info *server, - bool log_error) -{ - unsigned int len = get_rfc1002_length(mid->resp_buf) + 4; - - dump_smb(mid->resp_buf, min_t(u32, 92, len)); - - /* convert the length into a more usable form */ - if (server->sign) { - struct kvec iov[2]; - int rc = 0; - struct smb_rqst rqst = { .rq_iov = iov, - .rq_nvec = 2 }; - - iov[0].iov_base = mid->resp_buf; - iov[0].iov_len = 4; - iov[1].iov_base = (char *)mid->resp_buf + 4; - iov[1].iov_len = len - 4; - /* FIXME: add code to kill session */ - rc = cifs_verify_signature(&rqst, server, - mid->sequence_number); - if (rc) - cifs_server_dbg(VFS, "SMB signature verification returned error = %d\n", - rc); - } - - /* BB special case reconnect tid and uid here? */ - return map_and_check_smb_error(mid, log_error); -} - -struct mid_q_entry * -cifs_setup_request(struct cifs_ses *ses, struct TCP_Server_Info *ignored, - struct smb_rqst *rqst) -{ - int rc; - struct smb_hdr *hdr = (struct smb_hdr *)rqst->rq_iov[0].iov_base; - struct mid_q_entry *mid; - - if (rqst->rq_iov[0].iov_len != 4 || - rqst->rq_iov[0].iov_base + 4 != rqst->rq_iov[1].iov_base) - return ERR_PTR(-EIO); - - rc = allocate_mid(ses, hdr, &mid); - if (rc) - return ERR_PTR(rc); - rc = cifs_sign_rqst(rqst, ses->server, &mid->sequence_number); - if (rc) { - delete_mid(mid); - return ERR_PTR(rc); - } - return mid; -} - -static void -cifs_compound_callback(struct mid_q_entry *mid) -{ - struct TCP_Server_Info *server = mid->server; - struct cifs_credits credits; - - credits.value = server->ops->get_credits(mid); - credits.instance = server->reconnect_instance; - - add_credits(server, &credits, mid->optype); -} - -static void -cifs_compound_last_callback(struct mid_q_entry *mid) -{ - cifs_compound_callback(mid); - cifs_wake_up_task(mid); -} - -static void -cifs_cancelled_callback(struct mid_q_entry *mid) -{ - cifs_compound_callback(mid); - release_mid(mid); -} - -/* - * Return a channel (master if none) of @ses that can be used to send - * regular requests. - * - * If we are currently binding a new channel (negprot/sess.setup), - * return the new incomplete channel. - */ -struct TCP_Server_Info *cifs_pick_channel(struct cifs_ses *ses) -{ - uint index = 0; - - if (!ses) - return NULL; - - /* round robin */ - index = (uint)atomic_inc_return(&ses->chan_seq); - - spin_lock(&ses->chan_lock); - index %= ses->chan_count; - spin_unlock(&ses->chan_lock); - - return ses->chans[index].server; -} - -int -compound_send_recv(const unsigned int xid, struct cifs_ses *ses, - struct TCP_Server_Info *server, - const int flags, const int num_rqst, struct smb_rqst *rqst, - int *resp_buf_type, struct kvec *resp_iov) -{ - int i, j, optype, rc = 0; - struct mid_q_entry *midQ[MAX_COMPOUND]; - bool cancelled_mid[MAX_COMPOUND] = {false}; - struct cifs_credits credits[MAX_COMPOUND] = { - { .value = 0, .instance = 0 } - }; - unsigned int instance; - char *buf; - - optype = flags & CIFS_OP_MASK; - - for (i = 0; i < num_rqst; i++) - resp_buf_type[i] = CIFS_NO_BUFFER; /* no response buf yet */ - - if (!ses || !ses->server || !server) { - cifs_dbg(VFS, "Null session\n"); - return -EIO; - } - - spin_lock(&server->srv_lock); - if (server->tcpStatus == CifsExiting) { - spin_unlock(&server->srv_lock); - return -ENOENT; - } - spin_unlock(&server->srv_lock); - - /* - * Wait for all the requests to become available. - * This approach still leaves the possibility to be stuck waiting for - * credits if the server doesn't grant credits to the outstanding - * requests and if the client is completely idle, not generating any - * other requests. - * This can be handled by the eventual session reconnect. - */ - rc = wait_for_compound_request(server, num_rqst, flags, - &instance); - if (rc) - return rc; - - for (i = 0; i < num_rqst; i++) { - credits[i].value = 1; - credits[i].instance = instance; - } - - /* - * Make sure that we sign in the same order that we send on this socket - * and avoid races inside tcp sendmsg code that could cause corruption - * of smb data. - */ - - cifs_server_lock(server); - - /* - * All the parts of the compound chain belong obtained credits from the - * same session. We can not use credits obtained from the previous - * session to send this request. Check if there were reconnects after - * we obtained credits and return -EAGAIN in such cases to let callers - * handle it. - */ - if (instance != server->reconnect_instance) { - cifs_server_unlock(server); - for (j = 0; j < num_rqst; j++) - add_credits(server, &credits[j], optype); - return -EAGAIN; - } - - for (i = 0; i < num_rqst; i++) { - midQ[i] = server->ops->setup_request(ses, server, &rqst[i]); - if (IS_ERR(midQ[i])) { - revert_current_mid(server, i); - for (j = 0; j < i; j++) - delete_mid(midQ[j]); - cifs_server_unlock(server); - - /* Update # of requests on wire to server */ - for (j = 0; j < num_rqst; j++) - add_credits(server, &credits[j], optype); - return PTR_ERR(midQ[i]); - } - - midQ[i]->mid_state = MID_REQUEST_SUBMITTED; - midQ[i]->optype = optype; - /* - * Invoke callback for every part of the compound chain - * to calculate credits properly. Wake up this thread only when - * the last element is received. - */ - if (i < num_rqst - 1) - midQ[i]->callback = cifs_compound_callback; - else - midQ[i]->callback = cifs_compound_last_callback; - } - rc = smb_send_rqst(server, num_rqst, rqst, flags); - - for (i = 0; i < num_rqst; i++) - cifs_save_when_sent(midQ[i]); - - if (rc < 0) { - revert_current_mid(server, num_rqst); - server->sequence_number -= 2; - } - - cifs_server_unlock(server); - - /* - * If sending failed for some reason or it is an oplock break that we - * will not receive a response to - return credits back - */ - if (rc < 0 || (flags & CIFS_NO_SRV_RSP)) { - for (i = 0; i < num_rqst; i++) - add_credits(server, &credits[i], optype); - goto out; - } - - /* - * At this point the request is passed to the network stack - we assume - * that any credits taken from the server structure on the client have - * been spent and we can't return them back. Once we receive responses - * we will collect credits granted by the server in the mid callbacks - * and add those credits to the server structure. - */ - - /* - * Compounding is never used during session establish. - */ - spin_lock(&ses->ses_lock); - if ((ses->ses_status == SES_NEW) || (optype & CIFS_NEG_OP) || (optype & CIFS_SESS_OP)) { - spin_unlock(&ses->ses_lock); - - cifs_server_lock(server); - smb311_update_preauth_hash(ses, server, rqst[0].rq_iov, rqst[0].rq_nvec); - cifs_server_unlock(server); - - spin_lock(&ses->ses_lock); - } - spin_unlock(&ses->ses_lock); - - for (i = 0; i < num_rqst; i++) { - rc = wait_for_response(server, midQ[i]); - if (rc != 0) - break; - } - if (rc != 0) { - for (; i < num_rqst; i++) { - cifs_server_dbg(FYI, "Cancelling wait for mid %llu cmd: %d\n", - midQ[i]->mid, le16_to_cpu(midQ[i]->command)); - send_cancel(server, &rqst[i], midQ[i]); - spin_lock(&server->mid_lock); - midQ[i]->mid_flags |= MID_WAIT_CANCELLED; - if (midQ[i]->mid_state == MID_REQUEST_SUBMITTED) { - midQ[i]->callback = cifs_cancelled_callback; - cancelled_mid[i] = true; - credits[i].value = 0; - } - spin_unlock(&server->mid_lock); - } - } - - for (i = 0; i < num_rqst; i++) { - if (rc < 0) - goto out; - - rc = cifs_sync_mid_result(midQ[i], server); - if (rc != 0) { - /* mark this mid as cancelled to not free it below */ - cancelled_mid[i] = true; - goto out; - } - - if (!midQ[i]->resp_buf || - midQ[i]->mid_state != MID_RESPONSE_RECEIVED) { - rc = -EIO; - cifs_dbg(FYI, "Bad MID state?\n"); - goto out; - } - - buf = (char *)midQ[i]->resp_buf; - resp_iov[i].iov_base = buf; - resp_iov[i].iov_len = midQ[i]->resp_buf_size + - HEADER_PREAMBLE_SIZE(server); - - if (midQ[i]->large_buf) - resp_buf_type[i] = CIFS_LARGE_BUFFER; - else - resp_buf_type[i] = CIFS_SMALL_BUFFER; - - rc = server->ops->check_receive(midQ[i], server, - flags & CIFS_LOG_ERROR); - - /* mark it so buf will not be freed by delete_mid */ - if ((flags & CIFS_NO_RSP_BUF) == 0) - midQ[i]->resp_buf = NULL; - - } - - /* - * Compounding is never used during session establish. - */ - spin_lock(&ses->ses_lock); - if ((ses->ses_status == SES_NEW) || (optype & CIFS_NEG_OP) || (optype & CIFS_SESS_OP)) { - struct kvec iov = { - .iov_base = resp_iov[0].iov_base, - .iov_len = resp_iov[0].iov_len - }; - spin_unlock(&ses->ses_lock); - cifs_server_lock(server); - smb311_update_preauth_hash(ses, server, &iov, 1); - cifs_server_unlock(server); - spin_lock(&ses->ses_lock); - } - spin_unlock(&ses->ses_lock); - -out: - /* - * This will dequeue all mids. After this it is important that the - * demultiplex_thread will not process any of these mids any futher. - * This is prevented above by using a noop callback that will not - * wake this thread except for the very last PDU. - */ - for (i = 0; i < num_rqst; i++) { - if (!cancelled_mid[i]) - delete_mid(midQ[i]); - } - - return rc; -} - -int -cifs_send_recv(const unsigned int xid, struct cifs_ses *ses, - struct TCP_Server_Info *server, - struct smb_rqst *rqst, int *resp_buf_type, const int flags, - struct kvec *resp_iov) -{ - return compound_send_recv(xid, ses, server, flags, 1, - rqst, resp_buf_type, resp_iov); -} - -int -SendReceive2(const unsigned int xid, struct cifs_ses *ses, - struct kvec *iov, int n_vec, int *resp_buf_type /* ret */, - const int flags, struct kvec *resp_iov) -{ - struct smb_rqst rqst; - struct kvec s_iov[CIFS_MAX_IOV_SIZE], *new_iov; - int rc; - - if (n_vec + 1 > CIFS_MAX_IOV_SIZE) { - new_iov = kmalloc_array(n_vec + 1, sizeof(struct kvec), - GFP_KERNEL); - if (!new_iov) { - /* otherwise cifs_send_recv below sets resp_buf_type */ - *resp_buf_type = CIFS_NO_BUFFER; - return -ENOMEM; - } - } else - new_iov = s_iov; - - /* 1st iov is a RFC1001 length followed by the rest of the packet */ - memcpy(new_iov + 1, iov, (sizeof(struct kvec) * n_vec)); - - new_iov[0].iov_base = new_iov[1].iov_base; - new_iov[0].iov_len = 4; - new_iov[1].iov_base += 4; - new_iov[1].iov_len -= 4; - - memset(&rqst, 0, sizeof(struct smb_rqst)); - rqst.rq_iov = new_iov; - rqst.rq_nvec = n_vec + 1; - - rc = cifs_send_recv(xid, ses, ses->server, - &rqst, resp_buf_type, flags, resp_iov); - if (n_vec + 1 > CIFS_MAX_IOV_SIZE) - kfree(new_iov); - return rc; -} - -int -SendReceive(const unsigned int xid, struct cifs_ses *ses, - struct smb_hdr *in_buf, struct smb_hdr *out_buf, - int *pbytes_returned, const int flags) -{ - int rc = 0; - struct mid_q_entry *midQ; - unsigned int len = be32_to_cpu(in_buf->smb_buf_length); - struct kvec iov = { .iov_base = in_buf, .iov_len = len }; - struct smb_rqst rqst = { .rq_iov = &iov, .rq_nvec = 1 }; - struct cifs_credits credits = { .value = 1, .instance = 0 }; - struct TCP_Server_Info *server; - - if (ses == NULL) { - cifs_dbg(VFS, "Null smb session\n"); - return -EIO; - } - server = ses->server; - if (server == NULL) { - cifs_dbg(VFS, "Null tcp session\n"); - return -EIO; - } - - spin_lock(&server->srv_lock); - if (server->tcpStatus == CifsExiting) { - spin_unlock(&server->srv_lock); - return -ENOENT; - } - spin_unlock(&server->srv_lock); - - /* Ensure that we do not send more than 50 overlapping requests - to the same server. We may make this configurable later or - use ses->maxReq */ - - if (len > CIFSMaxBufSize + MAX_CIFS_HDR_SIZE - 4) { - cifs_server_dbg(VFS, "Invalid length, greater than maximum frame, %d\n", - len); - return -EIO; - } - - rc = wait_for_free_request(server, flags, &credits.instance); - if (rc) - return rc; - - /* make sure that we sign in the same order that we send on this socket - and avoid races inside tcp sendmsg code that could cause corruption - of smb data */ - - cifs_server_lock(server); - - rc = allocate_mid(ses, in_buf, &midQ); - if (rc) { - cifs_server_unlock(server); - /* Update # of requests on wire to server */ - add_credits(server, &credits, 0); - return rc; - } - - rc = cifs_sign_smb(in_buf, server, &midQ->sequence_number); - if (rc) { - cifs_server_unlock(server); - goto out; - } - - midQ->mid_state = MID_REQUEST_SUBMITTED; - - rc = smb_send(server, in_buf, len); - cifs_save_when_sent(midQ); - - if (rc < 0) - server->sequence_number -= 2; - - cifs_server_unlock(server); - - if (rc < 0) - goto out; - - rc = wait_for_response(server, midQ); - if (rc != 0) { - send_cancel(server, &rqst, midQ); - spin_lock(&server->mid_lock); - if (midQ->mid_state == MID_REQUEST_SUBMITTED) { - /* no longer considered to be "in-flight" */ - midQ->callback = release_mid; - spin_unlock(&server->mid_lock); - add_credits(server, &credits, 0); - return rc; - } - spin_unlock(&server->mid_lock); - } - - rc = cifs_sync_mid_result(midQ, server); - if (rc != 0) { - add_credits(server, &credits, 0); - return rc; - } - - if (!midQ->resp_buf || !out_buf || - midQ->mid_state != MID_RESPONSE_RECEIVED) { - rc = -EIO; - cifs_server_dbg(VFS, "Bad MID state?\n"); - goto out; - } - - *pbytes_returned = get_rfc1002_length(midQ->resp_buf); - memcpy(out_buf, midQ->resp_buf, *pbytes_returned + 4); - rc = cifs_check_receive(midQ, server, 0); -out: - delete_mid(midQ); - add_credits(server, &credits, 0); - - return rc; -} - -/* We send a LOCKINGX_CANCEL_LOCK to cause the Windows - blocking lock to return. */ - -static int -send_lock_cancel(const unsigned int xid, struct cifs_tcon *tcon, - struct smb_hdr *in_buf, - struct smb_hdr *out_buf) -{ - int bytes_returned; - struct cifs_ses *ses = tcon->ses; - LOCK_REQ *pSMB = (LOCK_REQ *)in_buf; - - /* We just modify the current in_buf to change - the type of lock from LOCKING_ANDX_SHARED_LOCK - or LOCKING_ANDX_EXCLUSIVE_LOCK to - LOCKING_ANDX_CANCEL_LOCK. */ - - pSMB->LockType = LOCKING_ANDX_CANCEL_LOCK|LOCKING_ANDX_LARGE_FILES; - pSMB->Timeout = 0; - pSMB->hdr.Mid = get_next_mid(ses->server); - - return SendReceive(xid, ses, in_buf, out_buf, - &bytes_returned, 0); -} - -int -SendReceiveBlockingLock(const unsigned int xid, struct cifs_tcon *tcon, - struct smb_hdr *in_buf, struct smb_hdr *out_buf, - int *pbytes_returned) -{ - int rc = 0; - int rstart = 0; - struct mid_q_entry *midQ; - struct cifs_ses *ses; - unsigned int len = be32_to_cpu(in_buf->smb_buf_length); - struct kvec iov = { .iov_base = in_buf, .iov_len = len }; - struct smb_rqst rqst = { .rq_iov = &iov, .rq_nvec = 1 }; - unsigned int instance; - struct TCP_Server_Info *server; - - if (tcon == NULL || tcon->ses == NULL) { - cifs_dbg(VFS, "Null smb session\n"); - return -EIO; - } - ses = tcon->ses; - server = ses->server; - - if (server == NULL) { - cifs_dbg(VFS, "Null tcp session\n"); - return -EIO; - } - - spin_lock(&server->srv_lock); - if (server->tcpStatus == CifsExiting) { - spin_unlock(&server->srv_lock); - return -ENOENT; - } - spin_unlock(&server->srv_lock); - - /* Ensure that we do not send more than 50 overlapping requests - to the same server. We may make this configurable later or - use ses->maxReq */ - - if (len > CIFSMaxBufSize + MAX_CIFS_HDR_SIZE - 4) { - cifs_tcon_dbg(VFS, "Invalid length, greater than maximum frame, %d\n", - len); - return -EIO; - } - - rc = wait_for_free_request(server, CIFS_BLOCKING_OP, &instance); - if (rc) - return rc; - - /* make sure that we sign in the same order that we send on this socket - and avoid races inside tcp sendmsg code that could cause corruption - of smb data */ - - cifs_server_lock(server); - - rc = allocate_mid(ses, in_buf, &midQ); - if (rc) { - cifs_server_unlock(server); - return rc; - } - - rc = cifs_sign_smb(in_buf, server, &midQ->sequence_number); - if (rc) { - delete_mid(midQ); - cifs_server_unlock(server); - return rc; - } - - midQ->mid_state = MID_REQUEST_SUBMITTED; - rc = smb_send(server, in_buf, len); - cifs_save_when_sent(midQ); - - if (rc < 0) - server->sequence_number -= 2; - - cifs_server_unlock(server); - - if (rc < 0) { - delete_mid(midQ); - return rc; - } - - /* Wait for a reply - allow signals to interrupt. */ - rc = wait_event_interruptible(server->response_q, - (!(midQ->mid_state == MID_REQUEST_SUBMITTED)) || - ((server->tcpStatus != CifsGood) && - (server->tcpStatus != CifsNew))); - - /* Were we interrupted by a signal ? */ - spin_lock(&server->srv_lock); - if ((rc == -ERESTARTSYS) && - (midQ->mid_state == MID_REQUEST_SUBMITTED) && - ((server->tcpStatus == CifsGood) || - (server->tcpStatus == CifsNew))) { - spin_unlock(&server->srv_lock); - - if (in_buf->Command == SMB_COM_TRANSACTION2) { - /* POSIX lock. We send a NT_CANCEL SMB to cause the - blocking lock to return. */ - rc = send_cancel(server, &rqst, midQ); - if (rc) { - delete_mid(midQ); - return rc; - } - } else { - /* Windows lock. We send a LOCKINGX_CANCEL_LOCK - to cause the blocking lock to return. */ - - rc = send_lock_cancel(xid, tcon, in_buf, out_buf); - - /* If we get -ENOLCK back the lock may have - already been removed. Don't exit in this case. */ - if (rc && rc != -ENOLCK) { - delete_mid(midQ); - return rc; - } - } - - rc = wait_for_response(server, midQ); - if (rc) { - send_cancel(server, &rqst, midQ); - spin_lock(&server->mid_lock); - if (midQ->mid_state == MID_REQUEST_SUBMITTED) { - /* no longer considered to be "in-flight" */ - midQ->callback = release_mid; - spin_unlock(&server->mid_lock); - return rc; - } - spin_unlock(&server->mid_lock); - } - - /* We got the response - restart system call. */ - rstart = 1; - spin_lock(&server->srv_lock); - } - spin_unlock(&server->srv_lock); - - rc = cifs_sync_mid_result(midQ, server); - if (rc != 0) - return rc; - - /* rcvd frame is ok */ - if (out_buf == NULL || midQ->mid_state != MID_RESPONSE_RECEIVED) { - rc = -EIO; - cifs_tcon_dbg(VFS, "Bad MID state?\n"); - goto out; - } - - *pbytes_returned = get_rfc1002_length(midQ->resp_buf); - memcpy(out_buf, midQ->resp_buf, *pbytes_returned + 4); - rc = cifs_check_receive(midQ, server, 0); -out: - delete_mid(midQ); - if (rstart && rc == -EACCES) - return -ERESTARTSYS; - return rc; -} - -/* - * Discard any remaining data in the current SMB. To do this, we borrow the - * current bigbuf. - */ -int -cifs_discard_remaining_data(struct TCP_Server_Info *server) -{ - unsigned int rfclen = server->pdu_size; - int remaining = rfclen + HEADER_PREAMBLE_SIZE(server) - - server->total_read; - - while (remaining > 0) { - int length; - - length = cifs_discard_from_socket(server, - min_t(size_t, remaining, - CIFSMaxBufSize + MAX_HEADER_SIZE(server))); - if (length < 0) - return length; - server->total_read += length; - remaining -= length; - } - - return 0; -} - -static int -__cifs_readv_discard(struct TCP_Server_Info *server, struct mid_q_entry *mid, - bool malformed) -{ - int length; - - length = cifs_discard_remaining_data(server); - dequeue_mid(mid, malformed); - mid->resp_buf = server->smallbuf; - server->smallbuf = NULL; - return length; -} - -static int -cifs_readv_discard(struct TCP_Server_Info *server, struct mid_q_entry *mid) -{ - struct cifs_readdata *rdata = mid->callback_data; - - return __cifs_readv_discard(server, mid, rdata->result); -} - -int -cifs_readv_receive(struct TCP_Server_Info *server, struct mid_q_entry *mid) -{ - int length, len; - unsigned int data_offset, data_len; - struct cifs_readdata *rdata = mid->callback_data; - char *buf = server->smallbuf; - unsigned int buflen = server->pdu_size + HEADER_PREAMBLE_SIZE(server); - bool use_rdma_mr = false; - - cifs_dbg(FYI, "%s: mid=%llu offset=%llu bytes=%u\n", - __func__, mid->mid, rdata->offset, rdata->bytes); - - /* - * read the rest of READ_RSP header (sans Data array), or whatever we - * can if there's not enough data. At this point, we've read down to - * the Mid. - */ - len = min_t(unsigned int, buflen, server->vals->read_rsp_size) - - HEADER_SIZE(server) + 1; - - length = cifs_read_from_socket(server, - buf + HEADER_SIZE(server) - 1, len); - if (length < 0) - return length; - server->total_read += length; - - if (server->ops->is_session_expired && - server->ops->is_session_expired(buf)) { - cifs_reconnect(server, true); - return -1; - } - - if (server->ops->is_status_pending && - server->ops->is_status_pending(buf, server)) { - cifs_discard_remaining_data(server); - return -1; - } - - /* set up first two iov for signature check and to get credits */ - rdata->iov[0].iov_base = buf; - rdata->iov[0].iov_len = HEADER_PREAMBLE_SIZE(server); - rdata->iov[1].iov_base = buf + HEADER_PREAMBLE_SIZE(server); - rdata->iov[1].iov_len = - server->total_read - HEADER_PREAMBLE_SIZE(server); - cifs_dbg(FYI, "0: iov_base=%p iov_len=%zu\n", - rdata->iov[0].iov_base, rdata->iov[0].iov_len); - cifs_dbg(FYI, "1: iov_base=%p iov_len=%zu\n", - rdata->iov[1].iov_base, rdata->iov[1].iov_len); - - /* Was the SMB read successful? */ - rdata->result = server->ops->map_error(buf, false); - if (rdata->result != 0) { - cifs_dbg(FYI, "%s: server returned error %d\n", - __func__, rdata->result); - /* normal error on read response */ - return __cifs_readv_discard(server, mid, false); - } - - /* Is there enough to get to the rest of the READ_RSP header? */ - if (server->total_read < server->vals->read_rsp_size) { - cifs_dbg(FYI, "%s: server returned short header. got=%u expected=%zu\n", - __func__, server->total_read, - server->vals->read_rsp_size); - rdata->result = -EIO; - return cifs_readv_discard(server, mid); - } - - data_offset = server->ops->read_data_offset(buf) + - HEADER_PREAMBLE_SIZE(server); - if (data_offset < server->total_read) { - /* - * win2k8 sometimes sends an offset of 0 when the read - * is beyond the EOF. Treat it as if the data starts just after - * the header. - */ - cifs_dbg(FYI, "%s: data offset (%u) inside read response header\n", - __func__, data_offset); - data_offset = server->total_read; - } else if (data_offset > MAX_CIFS_SMALL_BUFFER_SIZE) { - /* data_offset is beyond the end of smallbuf */ - cifs_dbg(FYI, "%s: data offset (%u) beyond end of smallbuf\n", - __func__, data_offset); - rdata->result = -EIO; - return cifs_readv_discard(server, mid); - } - - cifs_dbg(FYI, "%s: total_read=%u data_offset=%u\n", - __func__, server->total_read, data_offset); - - len = data_offset - server->total_read; - if (len > 0) { - /* read any junk before data into the rest of smallbuf */ - length = cifs_read_from_socket(server, - buf + server->total_read, len); - if (length < 0) - return length; - server->total_read += length; - } - - /* how much data is in the response? */ -#ifdef CONFIG_CIFS_SMB_DIRECT - use_rdma_mr = rdata->mr; -#endif - data_len = server->ops->read_data_length(buf, use_rdma_mr); - if (!use_rdma_mr && (data_offset + data_len > buflen)) { - /* data_len is corrupt -- discard frame */ - rdata->result = -EIO; - return cifs_readv_discard(server, mid); - } - - length = rdata->read_into_pages(server, rdata, data_len); - if (length < 0) - return length; - - server->total_read += length; - - cifs_dbg(FYI, "total_read=%u buflen=%u remaining=%u\n", - server->total_read, buflen, data_len); - - /* discard anything left over */ - if (server->total_read < buflen) - return cifs_readv_discard(server, mid); - - dequeue_mid(mid, false); - mid->resp_buf = server->smallbuf; - server->smallbuf = NULL; - return length; -} diff --git a/fs/cifs/unc.c b/fs/cifs/unc.c deleted file mode 100644 index f6fc5e343ea42..0000000000000 --- a/fs/cifs/unc.c +++ /dev/null @@ -1,69 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-or-later -/* - * Copyright (C) 2020, Microsoft Corporation. - * - * Author(s): Steve French - * Suresh Jayaraman - * Jeff Layton - */ - -#include -#include -#include -#include -#include "cifsglob.h" -#include "cifsproto.h" - -/* extract the host portion of the UNC string */ -char *extract_hostname(const char *unc) -{ - const char *src; - char *dst, *delim; - unsigned int len; - - /* skip double chars at beginning of string */ - /* BB: check validity of these bytes? */ - if (strlen(unc) < 3) - return ERR_PTR(-EINVAL); - for (src = unc; *src && *src == '\\'; src++) - ; - if (!*src) - return ERR_PTR(-EINVAL); - - /* delimiter between hostname and sharename is always '\\' now */ - delim = strchr(src, '\\'); - if (!delim) - return ERR_PTR(-EINVAL); - - len = delim - src; - dst = kmalloc((len + 1), GFP_KERNEL); - if (dst == NULL) - return ERR_PTR(-ENOMEM); - - memcpy(dst, src, len); - dst[len] = '\0'; - - return dst; -} - -char *extract_sharename(const char *unc) -{ - const char *src; - char *delim, *dst; - - /* skip double chars at the beginning */ - src = unc + 2; - - /* share name is always preceded by '\\' now */ - delim = strchr(src, '\\'); - if (!delim) - return ERR_PTR(-EINVAL); - delim++; - - /* caller has to free the memory */ - dst = kstrdup(delim, GFP_KERNEL); - if (!dst) - return ERR_PTR(-ENOMEM); - - return dst; -} diff --git a/fs/cifs/winucase.c b/fs/cifs/winucase.c deleted file mode 100644 index 2f075b5b50df0..0000000000000 --- a/fs/cifs/winucase.c +++ /dev/null @@ -1,649 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-or-later -/* - * - * Copyright (c) Jeffrey Layton , 2013 - * - * The const tables in this file were converted from the following info - * provided by Microsoft: - * - * 3.1.5.3 Mapping UTF-16 Strings to Upper Case: - * - * https://msdn.microsoft.com/en-us/library/hh877830.aspx - * http://www.microsoft.com/en-us/download/details.aspx?displaylang=en&id=10921 - * - * In particular, the table in "Windows 8 Upper Case Mapping Table.txt" was - * post-processed using the winucase_convert.pl script. - */ - -#include - -wchar_t cifs_toupper(wchar_t in); /* quiet sparse */ - -static const wchar_t t2_00[256] = { - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0041, 0x0042, 0x0043, 0x0044, 0x0045, 0x0046, 0x0047, - 0x0048, 0x0049, 0x004a, 0x004b, 0x004c, 0x004d, 0x004e, 0x004f, - 0x0050, 0x0051, 0x0052, 0x0053, 0x0054, 0x0055, 0x0056, 0x0057, - 0x0058, 0x0059, 0x005a, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x00c0, 0x00c1, 0x00c2, 0x00c3, 0x00c4, 0x00c5, 0x00c6, 0x00c7, - 0x00c8, 0x00c9, 0x00ca, 0x00cb, 0x00cc, 0x00cd, 0x00ce, 0x00cf, - 0x00d0, 0x00d1, 0x00d2, 0x00d3, 0x00d4, 0x00d5, 0x00d6, 0x0000, - 0x00d8, 0x00d9, 0x00da, 0x00db, 0x00dc, 0x00dd, 0x00de, 0x0178, -}; - -static const wchar_t t2_01[256] = { - 0x0000, 0x0100, 0x0000, 0x0102, 0x0000, 0x0104, 0x0000, 0x0106, - 0x0000, 0x0108, 0x0000, 0x010a, 0x0000, 0x010c, 0x0000, 0x010e, - 0x0000, 0x0110, 0x0000, 0x0112, 0x0000, 0x0114, 0x0000, 0x0116, - 0x0000, 0x0118, 0x0000, 0x011a, 0x0000, 0x011c, 0x0000, 0x011e, - 0x0000, 0x0120, 0x0000, 0x0122, 0x0000, 0x0124, 0x0000, 0x0126, - 0x0000, 0x0128, 0x0000, 0x012a, 0x0000, 0x012c, 0x0000, 0x012e, - 0x0000, 0x0000, 0x0000, 0x0132, 0x0000, 0x0134, 0x0000, 0x0136, - 0x0000, 0x0000, 0x0139, 0x0000, 0x013b, 0x0000, 0x013d, 0x0000, - 0x013f, 0x0000, 0x0141, 0x0000, 0x0143, 0x0000, 0x0145, 0x0000, - 0x0147, 0x0000, 0x0000, 0x014a, 0x0000, 0x014c, 0x0000, 0x014e, - 0x0000, 0x0150, 0x0000, 0x0152, 0x0000, 0x0154, 0x0000, 0x0156, - 0x0000, 0x0158, 0x0000, 0x015a, 0x0000, 0x015c, 0x0000, 0x015e, - 0x0000, 0x0160, 0x0000, 0x0162, 0x0000, 0x0164, 0x0000, 0x0166, - 0x0000, 0x0168, 0x0000, 0x016a, 0x0000, 0x016c, 0x0000, 0x016e, - 0x0000, 0x0170, 0x0000, 0x0172, 0x0000, 0x0174, 0x0000, 0x0176, - 0x0000, 0x0000, 0x0179, 0x0000, 0x017b, 0x0000, 0x017d, 0x0000, - 0x0243, 0x0000, 0x0000, 0x0182, 0x0000, 0x0184, 0x0000, 0x0000, - 0x0187, 0x0000, 0x0000, 0x0000, 0x018b, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0191, 0x0000, 0x0000, 0x01f6, 0x0000, 0x0000, - 0x0000, 0x0198, 0x023d, 0x0000, 0x0000, 0x0000, 0x0220, 0x0000, - 0x0000, 0x01a0, 0x0000, 0x01a2, 0x0000, 0x01a4, 0x0000, 0x0000, - 0x01a7, 0x0000, 0x0000, 0x0000, 0x0000, 0x01ac, 0x0000, 0x0000, - 0x01af, 0x0000, 0x0000, 0x0000, 0x01b3, 0x0000, 0x01b5, 0x0000, - 0x0000, 0x01b8, 0x0000, 0x0000, 0x0000, 0x01bc, 0x0000, 0x01f7, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x01c4, 0x0000, - 0x0000, 0x01c7, 0x0000, 0x0000, 0x01ca, 0x0000, 0x01cd, 0x0000, - 0x01cf, 0x0000, 0x01d1, 0x0000, 0x01d3, 0x0000, 0x01d5, 0x0000, - 0x01d7, 0x0000, 0x01d9, 0x0000, 0x01db, 0x018e, 0x0000, 0x01de, - 0x0000, 0x01e0, 0x0000, 0x01e2, 0x0000, 0x01e4, 0x0000, 0x01e6, - 0x0000, 0x01e8, 0x0000, 0x01ea, 0x0000, 0x01ec, 0x0000, 0x01ee, - 0x0000, 0x0000, 0x0000, 0x01f1, 0x0000, 0x01f4, 0x0000, 0x0000, - 0x0000, 0x01f8, 0x0000, 0x01fa, 0x0000, 0x01fc, 0x0000, 0x01fe, -}; - -static const wchar_t t2_02[256] = { - 0x0000, 0x0200, 0x0000, 0x0202, 0x0000, 0x0204, 0x0000, 0x0206, - 0x0000, 0x0208, 0x0000, 0x020a, 0x0000, 0x020c, 0x0000, 0x020e, - 0x0000, 0x0210, 0x0000, 0x0212, 0x0000, 0x0214, 0x0000, 0x0216, - 0x0000, 0x0218, 0x0000, 0x021a, 0x0000, 0x021c, 0x0000, 0x021e, - 0x0000, 0x0000, 0x0000, 0x0222, 0x0000, 0x0224, 0x0000, 0x0226, - 0x0000, 0x0228, 0x0000, 0x022a, 0x0000, 0x022c, 0x0000, 0x022e, - 0x0000, 0x0230, 0x0000, 0x0232, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x023b, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0241, 0x0000, 0x0000, 0x0000, 0x0000, 0x0246, - 0x0000, 0x0248, 0x0000, 0x024a, 0x0000, 0x024c, 0x0000, 0x024e, - 0x2c6f, 0x2c6d, 0x0000, 0x0181, 0x0186, 0x0000, 0x0189, 0x018a, - 0x0000, 0x018f, 0x0000, 0x0190, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0193, 0x0000, 0x0000, 0x0194, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0197, 0x0196, 0x0000, 0x2c62, 0x0000, 0x0000, 0x0000, 0x019c, - 0x0000, 0x2c6e, 0x019d, 0x0000, 0x0000, 0x019f, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x2c64, 0x0000, 0x0000, - 0x01a6, 0x0000, 0x0000, 0x01a9, 0x0000, 0x0000, 0x0000, 0x0000, - 0x01ae, 0x0244, 0x01b1, 0x01b2, 0x0245, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x01b7, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, -}; - -static const wchar_t t2_03[256] = { - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0370, 0x0000, 0x0372, 0x0000, 0x0000, 0x0000, 0x0376, - 0x0000, 0x0000, 0x0000, 0x03fd, 0x03fe, 0x03ff, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0386, 0x0388, 0x0389, 0x038a, - 0x0000, 0x0391, 0x0392, 0x0393, 0x0394, 0x0395, 0x0396, 0x0397, - 0x0398, 0x0399, 0x039a, 0x039b, 0x039c, 0x039d, 0x039e, 0x039f, - 0x03a0, 0x03a1, 0x0000, 0x03a3, 0x03a4, 0x03a5, 0x03a6, 0x03a7, - 0x03a8, 0x03a9, 0x03aa, 0x03ab, 0x038c, 0x038e, 0x038f, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x03cf, - 0x0000, 0x03d8, 0x0000, 0x03da, 0x0000, 0x03dc, 0x0000, 0x03de, - 0x0000, 0x03e0, 0x0000, 0x03e2, 0x0000, 0x03e4, 0x0000, 0x03e6, - 0x0000, 0x03e8, 0x0000, 0x03ea, 0x0000, 0x03ec, 0x0000, 0x03ee, - 0x0000, 0x0000, 0x03f9, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x03f7, 0x0000, 0x0000, 0x03fa, 0x0000, 0x0000, 0x0000, 0x0000, -}; - -static const wchar_t t2_04[256] = { - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0410, 0x0411, 0x0412, 0x0413, 0x0414, 0x0415, 0x0416, 0x0417, - 0x0418, 0x0419, 0x041a, 0x041b, 0x041c, 0x041d, 0x041e, 0x041f, - 0x0420, 0x0421, 0x0422, 0x0423, 0x0424, 0x0425, 0x0426, 0x0427, - 0x0428, 0x0429, 0x042a, 0x042b, 0x042c, 0x042d, 0x042e, 0x042f, - 0x0400, 0x0401, 0x0402, 0x0403, 0x0404, 0x0405, 0x0406, 0x0407, - 0x0408, 0x0409, 0x040a, 0x040b, 0x040c, 0x040d, 0x040e, 0x040f, - 0x0000, 0x0460, 0x0000, 0x0462, 0x0000, 0x0464, 0x0000, 0x0466, - 0x0000, 0x0468, 0x0000, 0x046a, 0x0000, 0x046c, 0x0000, 0x046e, - 0x0000, 0x0470, 0x0000, 0x0472, 0x0000, 0x0474, 0x0000, 0x0476, - 0x0000, 0x0478, 0x0000, 0x047a, 0x0000, 0x047c, 0x0000, 0x047e, - 0x0000, 0x0480, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x048a, 0x0000, 0x048c, 0x0000, 0x048e, - 0x0000, 0x0490, 0x0000, 0x0492, 0x0000, 0x0494, 0x0000, 0x0496, - 0x0000, 0x0498, 0x0000, 0x049a, 0x0000, 0x049c, 0x0000, 0x049e, - 0x0000, 0x04a0, 0x0000, 0x04a2, 0x0000, 0x04a4, 0x0000, 0x04a6, - 0x0000, 0x04a8, 0x0000, 0x04aa, 0x0000, 0x04ac, 0x0000, 0x04ae, - 0x0000, 0x04b0, 0x0000, 0x04b2, 0x0000, 0x04b4, 0x0000, 0x04b6, - 0x0000, 0x04b8, 0x0000, 0x04ba, 0x0000, 0x04bc, 0x0000, 0x04be, - 0x0000, 0x0000, 0x04c1, 0x0000, 0x04c3, 0x0000, 0x04c5, 0x0000, - 0x04c7, 0x0000, 0x04c9, 0x0000, 0x04cb, 0x0000, 0x04cd, 0x04c0, - 0x0000, 0x04d0, 0x0000, 0x04d2, 0x0000, 0x04d4, 0x0000, 0x04d6, - 0x0000, 0x04d8, 0x0000, 0x04da, 0x0000, 0x04dc, 0x0000, 0x04de, - 0x0000, 0x04e0, 0x0000, 0x04e2, 0x0000, 0x04e4, 0x0000, 0x04e6, - 0x0000, 0x04e8, 0x0000, 0x04ea, 0x0000, 0x04ec, 0x0000, 0x04ee, - 0x0000, 0x04f0, 0x0000, 0x04f2, 0x0000, 0x04f4, 0x0000, 0x04f6, - 0x0000, 0x04f8, 0x0000, 0x04fa, 0x0000, 0x04fc, 0x0000, 0x04fe, -}; - -static const wchar_t t2_05[256] = { - 0x0000, 0x0500, 0x0000, 0x0502, 0x0000, 0x0504, 0x0000, 0x0506, - 0x0000, 0x0508, 0x0000, 0x050a, 0x0000, 0x050c, 0x0000, 0x050e, - 0x0000, 0x0510, 0x0000, 0x0512, 0x0000, 0x0514, 0x0000, 0x0516, - 0x0000, 0x0518, 0x0000, 0x051a, 0x0000, 0x051c, 0x0000, 0x051e, - 0x0000, 0x0520, 0x0000, 0x0522, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0531, 0x0532, 0x0533, 0x0534, 0x0535, 0x0536, 0x0537, - 0x0538, 0x0539, 0x053a, 0x053b, 0x053c, 0x053d, 0x053e, 0x053f, - 0x0540, 0x0541, 0x0542, 0x0543, 0x0544, 0x0545, 0x0546, 0x0547, - 0x0548, 0x0549, 0x054a, 0x054b, 0x054c, 0x054d, 0x054e, 0x054f, - 0x0550, 0x0551, 0x0552, 0x0553, 0x0554, 0x0555, 0x0556, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, -}; - -static const wchar_t t2_1d[256] = { - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0xa77d, 0x0000, 0x0000, 0x0000, 0x2c63, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, -}; - -static const wchar_t t2_1e[256] = { - 0x0000, 0x1e00, 0x0000, 0x1e02, 0x0000, 0x1e04, 0x0000, 0x1e06, - 0x0000, 0x1e08, 0x0000, 0x1e0a, 0x0000, 0x1e0c, 0x0000, 0x1e0e, - 0x0000, 0x1e10, 0x0000, 0x1e12, 0x0000, 0x1e14, 0x0000, 0x1e16, - 0x0000, 0x1e18, 0x0000, 0x1e1a, 0x0000, 0x1e1c, 0x0000, 0x1e1e, - 0x0000, 0x1e20, 0x0000, 0x1e22, 0x0000, 0x1e24, 0x0000, 0x1e26, - 0x0000, 0x1e28, 0x0000, 0x1e2a, 0x0000, 0x1e2c, 0x0000, 0x1e2e, - 0x0000, 0x1e30, 0x0000, 0x1e32, 0x0000, 0x1e34, 0x0000, 0x1e36, - 0x0000, 0x1e38, 0x0000, 0x1e3a, 0x0000, 0x1e3c, 0x0000, 0x1e3e, - 0x0000, 0x1e40, 0x0000, 0x1e42, 0x0000, 0x1e44, 0x0000, 0x1e46, - 0x0000, 0x1e48, 0x0000, 0x1e4a, 0x0000, 0x1e4c, 0x0000, 0x1e4e, - 0x0000, 0x1e50, 0x0000, 0x1e52, 0x0000, 0x1e54, 0x0000, 0x1e56, - 0x0000, 0x1e58, 0x0000, 0x1e5a, 0x0000, 0x1e5c, 0x0000, 0x1e5e, - 0x0000, 0x1e60, 0x0000, 0x1e62, 0x0000, 0x1e64, 0x0000, 0x1e66, - 0x0000, 0x1e68, 0x0000, 0x1e6a, 0x0000, 0x1e6c, 0x0000, 0x1e6e, - 0x0000, 0x1e70, 0x0000, 0x1e72, 0x0000, 0x1e74, 0x0000, 0x1e76, - 0x0000, 0x1e78, 0x0000, 0x1e7a, 0x0000, 0x1e7c, 0x0000, 0x1e7e, - 0x0000, 0x1e80, 0x0000, 0x1e82, 0x0000, 0x1e84, 0x0000, 0x1e86, - 0x0000, 0x1e88, 0x0000, 0x1e8a, 0x0000, 0x1e8c, 0x0000, 0x1e8e, - 0x0000, 0x1e90, 0x0000, 0x1e92, 0x0000, 0x1e94, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x1ea0, 0x0000, 0x1ea2, 0x0000, 0x1ea4, 0x0000, 0x1ea6, - 0x0000, 0x1ea8, 0x0000, 0x1eaa, 0x0000, 0x1eac, 0x0000, 0x1eae, - 0x0000, 0x1eb0, 0x0000, 0x1eb2, 0x0000, 0x1eb4, 0x0000, 0x1eb6, - 0x0000, 0x1eb8, 0x0000, 0x1eba, 0x0000, 0x1ebc, 0x0000, 0x1ebe, - 0x0000, 0x1ec0, 0x0000, 0x1ec2, 0x0000, 0x1ec4, 0x0000, 0x1ec6, - 0x0000, 0x1ec8, 0x0000, 0x1eca, 0x0000, 0x1ecc, 0x0000, 0x1ece, - 0x0000, 0x1ed0, 0x0000, 0x1ed2, 0x0000, 0x1ed4, 0x0000, 0x1ed6, - 0x0000, 0x1ed8, 0x0000, 0x1eda, 0x0000, 0x1edc, 0x0000, 0x1ede, - 0x0000, 0x1ee0, 0x0000, 0x1ee2, 0x0000, 0x1ee4, 0x0000, 0x1ee6, - 0x0000, 0x1ee8, 0x0000, 0x1eea, 0x0000, 0x1eec, 0x0000, 0x1eee, - 0x0000, 0x1ef0, 0x0000, 0x1ef2, 0x0000, 0x1ef4, 0x0000, 0x1ef6, - 0x0000, 0x1ef8, 0x0000, 0x1efa, 0x0000, 0x1efc, 0x0000, 0x1efe, -}; - -static const wchar_t t2_1f[256] = { - 0x1f08, 0x1f09, 0x1f0a, 0x1f0b, 0x1f0c, 0x1f0d, 0x1f0e, 0x1f0f, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x1f18, 0x1f19, 0x1f1a, 0x1f1b, 0x1f1c, 0x1f1d, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x1f28, 0x1f29, 0x1f2a, 0x1f2b, 0x1f2c, 0x1f2d, 0x1f2e, 0x1f2f, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x1f38, 0x1f39, 0x1f3a, 0x1f3b, 0x1f3c, 0x1f3d, 0x1f3e, 0x1f3f, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x1f48, 0x1f49, 0x1f4a, 0x1f4b, 0x1f4c, 0x1f4d, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x1f59, 0x0000, 0x1f5b, 0x0000, 0x1f5d, 0x0000, 0x1f5f, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x1f68, 0x1f69, 0x1f6a, 0x1f6b, 0x1f6c, 0x1f6d, 0x1f6e, 0x1f6f, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x1fba, 0x1fbb, 0x1fc8, 0x1fc9, 0x1fca, 0x1fcb, 0x1fda, 0x1fdb, - 0x1ff8, 0x1ff9, 0x1fea, 0x1feb, 0x1ffa, 0x1ffb, 0x0000, 0x0000, - 0x1f88, 0x1f89, 0x1f8a, 0x1f8b, 0x1f8c, 0x1f8d, 0x1f8e, 0x1f8f, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x1f98, 0x1f99, 0x1f9a, 0x1f9b, 0x1f9c, 0x1f9d, 0x1f9e, 0x1f9f, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x1fa8, 0x1fa9, 0x1faa, 0x1fab, 0x1fac, 0x1fad, 0x1fae, 0x1faf, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x1fb8, 0x1fb9, 0x0000, 0x1fbc, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x1fcc, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x1fd8, 0x1fd9, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x1fe8, 0x1fe9, 0x0000, 0x0000, 0x0000, 0x1fec, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x1ffc, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, -}; - -static const wchar_t t2_21[256] = { - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x2132, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x2160, 0x2161, 0x2162, 0x2163, 0x2164, 0x2165, 0x2166, 0x2167, - 0x2168, 0x2169, 0x216a, 0x216b, 0x216c, 0x216d, 0x216e, 0x216f, - 0x0000, 0x0000, 0x0000, 0x0000, 0x2183, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, -}; - -static const wchar_t t2_24[256] = { - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x24b6, 0x24b7, 0x24b8, 0x24b9, 0x24ba, 0x24bb, 0x24bc, 0x24bd, - 0x24be, 0x24bf, 0x24c0, 0x24c1, 0x24c2, 0x24c3, 0x24c4, 0x24c5, - 0x24c6, 0x24c7, 0x24c8, 0x24c9, 0x24ca, 0x24cb, 0x24cc, 0x24cd, - 0x24ce, 0x24cf, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, -}; - -static const wchar_t t2_2c[256] = { - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x2c00, 0x2c01, 0x2c02, 0x2c03, 0x2c04, 0x2c05, 0x2c06, 0x2c07, - 0x2c08, 0x2c09, 0x2c0a, 0x2c0b, 0x2c0c, 0x2c0d, 0x2c0e, 0x2c0f, - 0x2c10, 0x2c11, 0x2c12, 0x2c13, 0x2c14, 0x2c15, 0x2c16, 0x2c17, - 0x2c18, 0x2c19, 0x2c1a, 0x2c1b, 0x2c1c, 0x2c1d, 0x2c1e, 0x2c1f, - 0x2c20, 0x2c21, 0x2c22, 0x2c23, 0x2c24, 0x2c25, 0x2c26, 0x2c27, - 0x2c28, 0x2c29, 0x2c2a, 0x2c2b, 0x2c2c, 0x2c2d, 0x2c2e, 0x0000, - 0x0000, 0x2c60, 0x0000, 0x0000, 0x0000, 0x023a, 0x023e, 0x0000, - 0x2c67, 0x0000, 0x2c69, 0x0000, 0x2c6b, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x2c72, 0x0000, 0x0000, 0x2c75, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x2c80, 0x0000, 0x2c82, 0x0000, 0x2c84, 0x0000, 0x2c86, - 0x0000, 0x2c88, 0x0000, 0x2c8a, 0x0000, 0x2c8c, 0x0000, 0x2c8e, - 0x0000, 0x2c90, 0x0000, 0x2c92, 0x0000, 0x2c94, 0x0000, 0x2c96, - 0x0000, 0x2c98, 0x0000, 0x2c9a, 0x0000, 0x2c9c, 0x0000, 0x2c9e, - 0x0000, 0x2ca0, 0x0000, 0x2ca2, 0x0000, 0x2ca4, 0x0000, 0x2ca6, - 0x0000, 0x2ca8, 0x0000, 0x2caa, 0x0000, 0x2cac, 0x0000, 0x2cae, - 0x0000, 0x2cb0, 0x0000, 0x2cb2, 0x0000, 0x2cb4, 0x0000, 0x2cb6, - 0x0000, 0x2cb8, 0x0000, 0x2cba, 0x0000, 0x2cbc, 0x0000, 0x2cbe, - 0x0000, 0x2cc0, 0x0000, 0x2cc2, 0x0000, 0x2cc4, 0x0000, 0x2cc6, - 0x0000, 0x2cc8, 0x0000, 0x2cca, 0x0000, 0x2ccc, 0x0000, 0x2cce, - 0x0000, 0x2cd0, 0x0000, 0x2cd2, 0x0000, 0x2cd4, 0x0000, 0x2cd6, - 0x0000, 0x2cd8, 0x0000, 0x2cda, 0x0000, 0x2cdc, 0x0000, 0x2cde, - 0x0000, 0x2ce0, 0x0000, 0x2ce2, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, -}; - -static const wchar_t t2_2d[256] = { - 0x10a0, 0x10a1, 0x10a2, 0x10a3, 0x10a4, 0x10a5, 0x10a6, 0x10a7, - 0x10a8, 0x10a9, 0x10aa, 0x10ab, 0x10ac, 0x10ad, 0x10ae, 0x10af, - 0x10b0, 0x10b1, 0x10b2, 0x10b3, 0x10b4, 0x10b5, 0x10b6, 0x10b7, - 0x10b8, 0x10b9, 0x10ba, 0x10bb, 0x10bc, 0x10bd, 0x10be, 0x10bf, - 0x10c0, 0x10c1, 0x10c2, 0x10c3, 0x10c4, 0x10c5, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, -}; - -static const wchar_t t2_a6[256] = { - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0xa640, 0x0000, 0xa642, 0x0000, 0xa644, 0x0000, 0xa646, - 0x0000, 0xa648, 0x0000, 0xa64a, 0x0000, 0xa64c, 0x0000, 0xa64e, - 0x0000, 0xa650, 0x0000, 0xa652, 0x0000, 0xa654, 0x0000, 0xa656, - 0x0000, 0xa658, 0x0000, 0xa65a, 0x0000, 0xa65c, 0x0000, 0xa65e, - 0x0000, 0x0000, 0x0000, 0xa662, 0x0000, 0xa664, 0x0000, 0xa666, - 0x0000, 0xa668, 0x0000, 0xa66a, 0x0000, 0xa66c, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0xa680, 0x0000, 0xa682, 0x0000, 0xa684, 0x0000, 0xa686, - 0x0000, 0xa688, 0x0000, 0xa68a, 0x0000, 0xa68c, 0x0000, 0xa68e, - 0x0000, 0xa690, 0x0000, 0xa692, 0x0000, 0xa694, 0x0000, 0xa696, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, -}; - -static const wchar_t t2_a7[256] = { - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0xa722, 0x0000, 0xa724, 0x0000, 0xa726, - 0x0000, 0xa728, 0x0000, 0xa72a, 0x0000, 0xa72c, 0x0000, 0xa72e, - 0x0000, 0x0000, 0x0000, 0xa732, 0x0000, 0xa734, 0x0000, 0xa736, - 0x0000, 0xa738, 0x0000, 0xa73a, 0x0000, 0xa73c, 0x0000, 0xa73e, - 0x0000, 0xa740, 0x0000, 0xa742, 0x0000, 0xa744, 0x0000, 0xa746, - 0x0000, 0xa748, 0x0000, 0xa74a, 0x0000, 0xa74c, 0x0000, 0xa74e, - 0x0000, 0xa750, 0x0000, 0xa752, 0x0000, 0xa754, 0x0000, 0xa756, - 0x0000, 0xa758, 0x0000, 0xa75a, 0x0000, 0xa75c, 0x0000, 0xa75e, - 0x0000, 0xa760, 0x0000, 0xa762, 0x0000, 0xa764, 0x0000, 0xa766, - 0x0000, 0xa768, 0x0000, 0xa76a, 0x0000, 0xa76c, 0x0000, 0xa76e, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0xa779, 0x0000, 0xa77b, 0x0000, 0x0000, 0xa77e, - 0x0000, 0xa780, 0x0000, 0xa782, 0x0000, 0xa784, 0x0000, 0xa786, - 0x0000, 0x0000, 0x0000, 0x0000, 0xa78b, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, -}; - -static const wchar_t t2_ff[256] = { - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0xff21, 0xff22, 0xff23, 0xff24, 0xff25, 0xff26, 0xff27, - 0xff28, 0xff29, 0xff2a, 0xff2b, 0xff2c, 0xff2d, 0xff2e, 0xff2f, - 0xff30, 0xff31, 0xff32, 0xff33, 0xff34, 0xff35, 0xff36, 0xff37, - 0xff38, 0xff39, 0xff3a, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, -}; - -static const wchar_t *const toplevel[256] = { - t2_00, t2_01, t2_02, t2_03, t2_04, t2_05, NULL, NULL, - NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, - NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, - NULL, NULL, NULL, NULL, NULL, t2_1d, t2_1e, t2_1f, - NULL, t2_21, NULL, NULL, t2_24, NULL, NULL, NULL, - NULL, NULL, NULL, NULL, t2_2c, t2_2d, NULL, NULL, - NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, - NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, - NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, - NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, - NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, - NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, - NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, - NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, - NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, - NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, - NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, - NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, - NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, - NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, - NULL, NULL, NULL, NULL, NULL, NULL, t2_a6, t2_a7, - NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, - NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, - NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, - NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, - NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, - NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, - NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, - NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, - NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, - NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, - NULL, NULL, NULL, NULL, NULL, NULL, NULL, t2_ff, -}; - -/** - * cifs_toupper - convert a wchar_t from lower to uppercase - * @in: character to convert from lower to uppercase - * - * This function consults the static tables above to convert a wchar_t from - * lower to uppercase. In the event that there is no mapping, the original - * "in" character is returned. - */ -wchar_t -cifs_toupper(wchar_t in) -{ - unsigned char idx; - const wchar_t *tbl; - wchar_t out; - - /* grab upper byte */ - idx = (in & 0xff00) >> 8; - - /* find pointer to 2nd layer table */ - tbl = toplevel[idx]; - if (!tbl) - return in; - - /* grab lower byte */ - idx = in & 0xff; - - /* look up character in table */ - out = tbl[idx]; - if (out) - return out; - - return in; -} diff --git a/fs/cifs/xattr.c b/fs/cifs/xattr.c deleted file mode 100644 index 998fa51f9b684..0000000000000 --- a/fs/cifs/xattr.c +++ /dev/null @@ -1,555 +0,0 @@ -// SPDX-License-Identifier: LGPL-2.1 -/* - * - * Copyright (c) International Business Machines Corp., 2003, 2007 - * Author(s): Steve French (sfrench@us.ibm.com) - * - */ - -#include -#include -#include -#include -#include "cifsfs.h" -#include "cifspdu.h" -#include "cifsglob.h" -#include "cifsproto.h" -#include "cifs_debug.h" -#include "cifs_fs_sb.h" -#include "cifs_unicode.h" -#include "cifs_ioctl.h" - -#define MAX_EA_VALUE_SIZE CIFSMaxBufSize -#define CIFS_XATTR_CIFS_ACL "system.cifs_acl" /* DACL only */ -#define CIFS_XATTR_CIFS_NTSD "system.cifs_ntsd" /* owner plus DACL */ -#define CIFS_XATTR_CIFS_NTSD_FULL "system.cifs_ntsd_full" /* owner/DACL/SACL */ -#define CIFS_XATTR_ATTRIB "cifs.dosattrib" /* full name: user.cifs.dosattrib */ -#define CIFS_XATTR_CREATETIME "cifs.creationtime" /* user.cifs.creationtime */ -/* - * Although these three are just aliases for the above, need to move away from - * confusing users and using the 20+ year old term 'cifs' when it is no longer - * secure, replaced by SMB2 (then even more highly secure SMB3) many years ago - */ -#define SMB3_XATTR_CIFS_ACL "system.smb3_acl" /* DACL only */ -#define SMB3_XATTR_CIFS_NTSD "system.smb3_ntsd" /* owner plus DACL */ -#define SMB3_XATTR_CIFS_NTSD_FULL "system.smb3_ntsd_full" /* owner/DACL/SACL */ -#define SMB3_XATTR_ATTRIB "smb3.dosattrib" /* full name: user.smb3.dosattrib */ -#define SMB3_XATTR_CREATETIME "smb3.creationtime" /* user.smb3.creationtime */ -/* BB need to add server (Samba e.g) support for security and trusted prefix */ - -enum { XATTR_USER, XATTR_CIFS_ACL, XATTR_ACL_ACCESS, XATTR_ACL_DEFAULT, - XATTR_CIFS_NTSD, XATTR_CIFS_NTSD_FULL }; - -static int cifs_attrib_set(unsigned int xid, struct cifs_tcon *pTcon, - struct inode *inode, const char *full_path, - const void *value, size_t size) -{ - ssize_t rc = -EOPNOTSUPP; - __u32 *pattrib = (__u32 *)value; - __u32 attrib; - FILE_BASIC_INFO info_buf; - - if ((value == NULL) || (size != sizeof(__u32))) - return -ERANGE; - - memset(&info_buf, 0, sizeof(info_buf)); - attrib = *pattrib; - info_buf.Attributes = cpu_to_le32(attrib); - if (pTcon->ses->server->ops->set_file_info) - rc = pTcon->ses->server->ops->set_file_info(inode, full_path, - &info_buf, xid); - if (rc == 0) - CIFS_I(inode)->cifsAttrs = attrib; - - return rc; -} - -static int cifs_creation_time_set(unsigned int xid, struct cifs_tcon *pTcon, - struct inode *inode, const char *full_path, - const void *value, size_t size) -{ - ssize_t rc = -EOPNOTSUPP; - __u64 *pcreation_time = (__u64 *)value; - __u64 creation_time; - FILE_BASIC_INFO info_buf; - - if ((value == NULL) || (size != sizeof(__u64))) - return -ERANGE; - - memset(&info_buf, 0, sizeof(info_buf)); - creation_time = *pcreation_time; - info_buf.CreationTime = cpu_to_le64(creation_time); - if (pTcon->ses->server->ops->set_file_info) - rc = pTcon->ses->server->ops->set_file_info(inode, full_path, - &info_buf, xid); - if (rc == 0) - CIFS_I(inode)->createtime = creation_time; - - return rc; -} - -static int cifs_xattr_set(const struct xattr_handler *handler, - struct user_namespace *mnt_userns, - struct dentry *dentry, struct inode *inode, - const char *name, const void *value, - size_t size, int flags) -{ - int rc = -EOPNOTSUPP; - unsigned int xid; - struct super_block *sb = dentry->d_sb; - struct cifs_sb_info *cifs_sb = CIFS_SB(sb); - struct tcon_link *tlink; - struct cifs_tcon *pTcon; - const char *full_path; - void *page; - - tlink = cifs_sb_tlink(cifs_sb); - if (IS_ERR(tlink)) - return PTR_ERR(tlink); - pTcon = tlink_tcon(tlink); - - xid = get_xid(); - page = alloc_dentry_path(); - - full_path = build_path_from_dentry(dentry, page); - if (IS_ERR(full_path)) { - rc = PTR_ERR(full_path); - goto out; - } - /* return dos attributes as pseudo xattr */ - /* return alt name if available as pseudo attr */ - - /* if proc/fs/cifs/streamstoxattr is set then - search server for EAs or streams to - returns as xattrs */ - if (size > MAX_EA_VALUE_SIZE) { - cifs_dbg(FYI, "size of EA value too large\n"); - rc = -EOPNOTSUPP; - goto out; - } - - switch (handler->flags) { - case XATTR_USER: - cifs_dbg(FYI, "%s:setting user xattr %s\n", __func__, name); - if ((strcmp(name, CIFS_XATTR_ATTRIB) == 0) || - (strcmp(name, SMB3_XATTR_ATTRIB) == 0)) { - rc = cifs_attrib_set(xid, pTcon, inode, full_path, - value, size); - if (rc == 0) /* force revalidate of the inode */ - CIFS_I(inode)->time = 0; - break; - } else if ((strcmp(name, CIFS_XATTR_CREATETIME) == 0) || - (strcmp(name, SMB3_XATTR_CREATETIME) == 0)) { - rc = cifs_creation_time_set(xid, pTcon, inode, - full_path, value, size); - if (rc == 0) /* force revalidate of the inode */ - CIFS_I(inode)->time = 0; - break; - } - - if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_NO_XATTR) - goto out; - - if (pTcon->ses->server->ops->set_EA) - rc = pTcon->ses->server->ops->set_EA(xid, pTcon, - full_path, name, value, (__u16)size, - cifs_sb->local_nls, cifs_sb); - break; - - case XATTR_CIFS_ACL: - case XATTR_CIFS_NTSD: - case XATTR_CIFS_NTSD_FULL: { - struct cifs_ntsd *pacl; - - if (!value) - goto out; - pacl = kmalloc(size, GFP_KERNEL); - if (!pacl) { - rc = -ENOMEM; - } else { - memcpy(pacl, value, size); - if (pTcon->ses->server->ops->set_acl) { - int aclflags = 0; - rc = 0; - - switch (handler->flags) { - case XATTR_CIFS_NTSD_FULL: - aclflags = (CIFS_ACL_OWNER | - CIFS_ACL_GROUP | - CIFS_ACL_DACL | - CIFS_ACL_SACL); - break; - case XATTR_CIFS_NTSD: - aclflags = (CIFS_ACL_OWNER | - CIFS_ACL_GROUP | - CIFS_ACL_DACL); - break; - case XATTR_CIFS_ACL: - default: - aclflags = CIFS_ACL_DACL; - } - - rc = pTcon->ses->server->ops->set_acl(pacl, - size, inode, full_path, aclflags); - } else { - rc = -EOPNOTSUPP; - } - if (rc == 0) /* force revalidate of the inode */ - CIFS_I(inode)->time = 0; - kfree(pacl); - } - break; - } - -#ifdef CONFIG_CIFS_ALLOW_INSECURE_LEGACY - case XATTR_ACL_ACCESS: -#ifdef CONFIG_CIFS_POSIX - if (!value) - goto out; - if (sb->s_flags & SB_POSIXACL) - rc = CIFSSMBSetPosixACL(xid, pTcon, full_path, - value, (const int)size, - ACL_TYPE_ACCESS, cifs_sb->local_nls, - cifs_remap(cifs_sb)); -#endif /* CONFIG_CIFS_POSIX */ - break; - - case XATTR_ACL_DEFAULT: -#ifdef CONFIG_CIFS_POSIX - if (!value) - goto out; - if (sb->s_flags & SB_POSIXACL) - rc = CIFSSMBSetPosixACL(xid, pTcon, full_path, - value, (const int)size, - ACL_TYPE_DEFAULT, cifs_sb->local_nls, - cifs_remap(cifs_sb)); -#endif /* CONFIG_CIFS_POSIX */ - break; -#endif /* CONFIG_CIFS_ALLOW_INSECURE_LEGACY */ - } - -out: - free_dentry_path(page); - free_xid(xid); - cifs_put_tlink(tlink); - return rc; -} - -static int cifs_attrib_get(struct dentry *dentry, - struct inode *inode, void *value, - size_t size) -{ - ssize_t rc; - __u32 *pattribute; - - rc = cifs_revalidate_dentry_attr(dentry); - - if (rc) - return rc; - - if ((value == NULL) || (size == 0)) - return sizeof(__u32); - else if (size < sizeof(__u32)) - return -ERANGE; - - /* return dos attributes as pseudo xattr */ - pattribute = (__u32 *)value; - *pattribute = CIFS_I(inode)->cifsAttrs; - - return sizeof(__u32); -} - -static int cifs_creation_time_get(struct dentry *dentry, struct inode *inode, - void *value, size_t size) -{ - ssize_t rc; - __u64 *pcreatetime; - - rc = cifs_revalidate_dentry_attr(dentry); - if (rc) - return rc; - - if ((value == NULL) || (size == 0)) - return sizeof(__u64); - else if (size < sizeof(__u64)) - return -ERANGE; - - /* return dos attributes as pseudo xattr */ - pcreatetime = (__u64 *)value; - *pcreatetime = CIFS_I(inode)->createtime; - return sizeof(__u64); -} - - -static int cifs_xattr_get(const struct xattr_handler *handler, - struct dentry *dentry, struct inode *inode, - const char *name, void *value, size_t size) -{ - ssize_t rc = -EOPNOTSUPP; - unsigned int xid; - struct super_block *sb = dentry->d_sb; - struct cifs_sb_info *cifs_sb = CIFS_SB(sb); - struct tcon_link *tlink; - struct cifs_tcon *pTcon; - const char *full_path; - void *page; - - tlink = cifs_sb_tlink(cifs_sb); - if (IS_ERR(tlink)) - return PTR_ERR(tlink); - pTcon = tlink_tcon(tlink); - - xid = get_xid(); - page = alloc_dentry_path(); - - full_path = build_path_from_dentry(dentry, page); - if (IS_ERR(full_path)) { - rc = PTR_ERR(full_path); - goto out; - } - - /* return alt name if available as pseudo attr */ - switch (handler->flags) { - case XATTR_USER: - cifs_dbg(FYI, "%s:querying user xattr %s\n", __func__, name); - if ((strcmp(name, CIFS_XATTR_ATTRIB) == 0) || - (strcmp(name, SMB3_XATTR_ATTRIB) == 0)) { - rc = cifs_attrib_get(dentry, inode, value, size); - break; - } else if ((strcmp(name, CIFS_XATTR_CREATETIME) == 0) || - (strcmp(name, SMB3_XATTR_CREATETIME) == 0)) { - rc = cifs_creation_time_get(dentry, inode, value, size); - break; - } - - if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_NO_XATTR) - goto out; - - if (pTcon->ses->server->ops->query_all_EAs) - rc = pTcon->ses->server->ops->query_all_EAs(xid, pTcon, - full_path, name, value, size, cifs_sb); - break; - - case XATTR_CIFS_ACL: - case XATTR_CIFS_NTSD: - case XATTR_CIFS_NTSD_FULL: { - /* - * fetch owner, DACL, and SACL if asked for full descriptor, - * fetch owner and DACL otherwise - */ - u32 acllen, extra_info; - struct cifs_ntsd *pacl; - - if (pTcon->ses->server->ops->get_acl == NULL) - goto out; /* rc already EOPNOTSUPP */ - - if (handler->flags == XATTR_CIFS_NTSD_FULL) { - extra_info = SACL_SECINFO; - } else { - extra_info = 0; - } - pacl = pTcon->ses->server->ops->get_acl(cifs_sb, - inode, full_path, &acllen, extra_info); - if (IS_ERR(pacl)) { - rc = PTR_ERR(pacl); - cifs_dbg(VFS, "%s: error %zd getting sec desc\n", - __func__, rc); - } else { - if (value) { - if (acllen > size) - acllen = -ERANGE; - else - memcpy(value, pacl, acllen); - } - rc = acllen; - kfree(pacl); - } - break; - } -#ifdef CONFIG_CIFS_ALLOW_INSECURE_LEGACY - case XATTR_ACL_ACCESS: -#ifdef CONFIG_CIFS_POSIX - if (sb->s_flags & SB_POSIXACL) - rc = CIFSSMBGetPosixACL(xid, pTcon, full_path, - value, size, ACL_TYPE_ACCESS, - cifs_sb->local_nls, - cifs_remap(cifs_sb)); -#endif /* CONFIG_CIFS_POSIX */ - break; - - case XATTR_ACL_DEFAULT: -#ifdef CONFIG_CIFS_POSIX - if (sb->s_flags & SB_POSIXACL) - rc = CIFSSMBGetPosixACL(xid, pTcon, full_path, - value, size, ACL_TYPE_DEFAULT, - cifs_sb->local_nls, - cifs_remap(cifs_sb)); -#endif /* CONFIG_CIFS_POSIX */ - break; -#endif /* ifdef CONFIG_CIFS_ALLOW_INSECURE_LEGACY */ - } - - /* We could add an additional check for streams ie - if proc/fs/cifs/streamstoxattr is set then - search server for EAs or streams to - returns as xattrs */ - - if (rc == -EINVAL) - rc = -EOPNOTSUPP; - -out: - free_dentry_path(page); - free_xid(xid); - cifs_put_tlink(tlink); - return rc; -} - -ssize_t cifs_listxattr(struct dentry *direntry, char *data, size_t buf_size) -{ - ssize_t rc = -EOPNOTSUPP; - unsigned int xid; - struct cifs_sb_info *cifs_sb = CIFS_SB(direntry->d_sb); - struct tcon_link *tlink; - struct cifs_tcon *pTcon; - const char *full_path; - void *page; - - if (unlikely(cifs_forced_shutdown(cifs_sb))) - return -EIO; - - if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_NO_XATTR) - return -EOPNOTSUPP; - - tlink = cifs_sb_tlink(cifs_sb); - if (IS_ERR(tlink)) - return PTR_ERR(tlink); - pTcon = tlink_tcon(tlink); - - xid = get_xid(); - page = alloc_dentry_path(); - - full_path = build_path_from_dentry(direntry, page); - if (IS_ERR(full_path)) { - rc = PTR_ERR(full_path); - goto list_ea_exit; - } - /* return dos attributes as pseudo xattr */ - /* return alt name if available as pseudo attr */ - - /* if proc/fs/cifs/streamstoxattr is set then - search server for EAs or streams to - returns as xattrs */ - - if (pTcon->ses->server->ops->query_all_EAs) - rc = pTcon->ses->server->ops->query_all_EAs(xid, pTcon, - full_path, NULL, data, buf_size, cifs_sb); -list_ea_exit: - free_dentry_path(page); - free_xid(xid); - cifs_put_tlink(tlink); - return rc; -} - -static const struct xattr_handler cifs_user_xattr_handler = { - .prefix = XATTR_USER_PREFIX, - .flags = XATTR_USER, - .get = cifs_xattr_get, - .set = cifs_xattr_set, -}; - -/* os2.* attributes are treated like user.* attributes */ -static const struct xattr_handler cifs_os2_xattr_handler = { - .prefix = XATTR_OS2_PREFIX, - .flags = XATTR_USER, - .get = cifs_xattr_get, - .set = cifs_xattr_set, -}; - -static const struct xattr_handler cifs_cifs_acl_xattr_handler = { - .name = CIFS_XATTR_CIFS_ACL, - .flags = XATTR_CIFS_ACL, - .get = cifs_xattr_get, - .set = cifs_xattr_set, -}; - -/* - * Although this is just an alias for the above, need to move away from - * confusing users and using the 20 year old term 'cifs' when it is no - * longer secure and was replaced by SMB2/SMB3 a long time ago, and - * SMB3 and later are highly secure. - */ -static const struct xattr_handler smb3_acl_xattr_handler = { - .name = SMB3_XATTR_CIFS_ACL, - .flags = XATTR_CIFS_ACL, - .get = cifs_xattr_get, - .set = cifs_xattr_set, -}; - -static const struct xattr_handler cifs_cifs_ntsd_xattr_handler = { - .name = CIFS_XATTR_CIFS_NTSD, - .flags = XATTR_CIFS_NTSD, - .get = cifs_xattr_get, - .set = cifs_xattr_set, -}; - -/* - * Although this is just an alias for the above, need to move away from - * confusing users and using the 20 year old term 'cifs' when it is no - * longer secure and was replaced by SMB2/SMB3 a long time ago, and - * SMB3 and later are highly secure. - */ -static const struct xattr_handler smb3_ntsd_xattr_handler = { - .name = SMB3_XATTR_CIFS_NTSD, - .flags = XATTR_CIFS_NTSD, - .get = cifs_xattr_get, - .set = cifs_xattr_set, -}; - -static const struct xattr_handler cifs_cifs_ntsd_full_xattr_handler = { - .name = CIFS_XATTR_CIFS_NTSD_FULL, - .flags = XATTR_CIFS_NTSD_FULL, - .get = cifs_xattr_get, - .set = cifs_xattr_set, -}; - -/* - * Although this is just an alias for the above, need to move away from - * confusing users and using the 20 year old term 'cifs' when it is no - * longer secure and was replaced by SMB2/SMB3 a long time ago, and - * SMB3 and later are highly secure. - */ -static const struct xattr_handler smb3_ntsd_full_xattr_handler = { - .name = SMB3_XATTR_CIFS_NTSD_FULL, - .flags = XATTR_CIFS_NTSD_FULL, - .get = cifs_xattr_get, - .set = cifs_xattr_set, -}; - - -static const struct xattr_handler cifs_posix_acl_access_xattr_handler = { - .name = XATTR_NAME_POSIX_ACL_ACCESS, - .flags = XATTR_ACL_ACCESS, - .get = cifs_xattr_get, - .set = cifs_xattr_set, -}; - -static const struct xattr_handler cifs_posix_acl_default_xattr_handler = { - .name = XATTR_NAME_POSIX_ACL_DEFAULT, - .flags = XATTR_ACL_DEFAULT, - .get = cifs_xattr_get, - .set = cifs_xattr_set, -}; - -const struct xattr_handler *cifs_xattr_handlers[] = { - &cifs_user_xattr_handler, - &cifs_os2_xattr_handler, - &cifs_cifs_acl_xattr_handler, - &smb3_acl_xattr_handler, /* alias for above since avoiding "cifs" */ - &cifs_cifs_ntsd_xattr_handler, - &smb3_ntsd_xattr_handler, /* alias for above since avoiding "cifs" */ - &cifs_cifs_ntsd_full_xattr_handler, - &smb3_ntsd_full_xattr_handler, /* alias for above since avoiding "cifs" */ - &cifs_posix_acl_access_xattr_handler, - &cifs_posix_acl_default_xattr_handler, - NULL -}; diff --git a/fs/gfs2/file.c b/fs/gfs2/file.c index 60c6fb91fb589..bc6cd5f4b1077 100644 --- a/fs/gfs2/file.c +++ b/fs/gfs2/file.c @@ -783,9 +783,13 @@ static inline bool should_fault_in_pages(struct iov_iter *i, if (!user_backed_iter(i)) return false; + /* + * Try to fault in multiple pages initially. When that doesn't result + * in any progress, fall back to a single page. + */ size = PAGE_SIZE; offs = offset_in_page(iocb->ki_pos); - if (*prev_count != count || !*window_size) { + if (*prev_count != count) { size_t nr_dirtied; nr_dirtied = max(current->nr_dirtied_pause - @@ -869,6 +873,7 @@ static ssize_t gfs2_file_direct_write(struct kiocb *iocb, struct iov_iter *from, struct gfs2_inode *ip = GFS2_I(inode); size_t prev_count = 0, window_size = 0; size_t written = 0; + bool enough_retries; ssize_t ret; /* @@ -912,11 +917,17 @@ retry: if (ret > 0) written = ret; + enough_retries = prev_count == iov_iter_count(from) && + window_size <= PAGE_SIZE; if (should_fault_in_pages(from, iocb, &prev_count, &window_size)) { gfs2_glock_dq(gh); window_size -= fault_in_iov_iter_readable(from, window_size); - if (window_size) - goto retry; + if (window_size) { + if (!enough_retries) + goto retry; + /* fall back to buffered I/O */ + ret = 0; + } } out_unlock: if (gfs2_holder_queued(gh)) diff --git a/fs/ksmbd/Kconfig b/fs/ksmbd/Kconfig deleted file mode 100644 index e1fe17747ed69..0000000000000 --- a/fs/ksmbd/Kconfig +++ /dev/null @@ -1,68 +0,0 @@ -config SMB_SERVER - tristate "SMB3 server support (EXPERIMENTAL)" - depends on INET - depends on MULTIUSER - depends on FILE_LOCKING - select NLS - select NLS_UTF8 - select CRYPTO - select CRYPTO_MD5 - select CRYPTO_HMAC - select CRYPTO_ECB - select CRYPTO_LIB_DES - select CRYPTO_SHA256 - select CRYPTO_CMAC - select CRYPTO_SHA512 - select CRYPTO_AEAD2 - select CRYPTO_CCM - select CRYPTO_GCM - select ASN1 - select OID_REGISTRY - select CRC32 - default n - help - Choose Y here if you want to allow SMB3 compliant clients - to access files residing on this system using SMB3 protocol. - To compile the SMB3 server support as a module, - choose M here: the module will be called ksmbd. - - You may choose to use a samba server instead, in which - case you can choose N here. - - You also need to install user space programs which can be found - in ksmbd-tools, available from - https://github.com/cifsd-team/ksmbd-tools. - More detail about how to run the ksmbd kernel server is - available via README file - (https://github.com/cifsd-team/ksmbd-tools/blob/master/README). - - ksmbd kernel server includes support for auto-negotiation, - Secure negotiate, Pre-authentication integrity, oplock/lease, - compound requests, multi-credit, packet signing, RDMA(smbdirect), - smb3 encryption, copy-offload, secure per-user session - establishment via NTLM or NTLMv2. - -config SMB_SERVER_SMBDIRECT - bool "Support for SMB Direct protocol" - depends on SMB_SERVER=m && INFINIBAND && INFINIBAND_ADDR_TRANS || SMB_SERVER=y && INFINIBAND=y && INFINIBAND_ADDR_TRANS=y - select SG_POOL - default n - - help - Enables SMB Direct support for SMB 3.0, 3.02 and 3.1.1. - - SMB Direct allows transferring SMB packets over RDMA. If unsure, - say N. - -config SMB_SERVER_CHECK_CAP_NET_ADMIN - bool "Enable check network administration capability" - depends on SMB_SERVER - default y - - help - Prevent unprivileged processes to start the ksmbd kernel server. - -config SMB_SERVER_KERBEROS5 - bool "Support for Kerberos 5" - depends on SMB_SERVER - default n diff --git a/fs/ksmbd/Makefile b/fs/ksmbd/Makefile deleted file mode 100644 index 7d6337a7dee40..0000000000000 --- a/fs/ksmbd/Makefile +++ /dev/null @@ -1,20 +0,0 @@ -# SPDX-License-Identifier: GPL-2.0-or-later -# -# Makefile for Linux SMB3 kernel server -# -obj-$(CONFIG_SMB_SERVER) += ksmbd.o - -ksmbd-y := unicode.o auth.o vfs.o vfs_cache.o server.o ndr.o \ - misc.o oplock.o connection.o ksmbd_work.o crypto_ctx.o \ - mgmt/ksmbd_ida.o mgmt/user_config.o mgmt/share_config.o \ - mgmt/tree_connect.o mgmt/user_session.o smb_common.o \ - transport_tcp.o transport_ipc.o smbacl.o smb2pdu.o \ - smb2ops.o smb2misc.o ksmbd_spnego_negtokeninit.asn1.o \ - ksmbd_spnego_negtokentarg.asn1.o asn1.o - -$(obj)/asn1.o: $(obj)/ksmbd_spnego_negtokeninit.asn1.h $(obj)/ksmbd_spnego_negtokentarg.asn1.h - -$(obj)/ksmbd_spnego_negtokeninit.asn1.o: $(obj)/ksmbd_spnego_negtokeninit.asn1.c $(obj)/ksmbd_spnego_negtokeninit.asn1.h -$(obj)/ksmbd_spnego_negtokentarg.asn1.o: $(obj)/ksmbd_spnego_negtokentarg.asn1.c $(obj)/ksmbd_spnego_negtokentarg.asn1.h - -ksmbd-$(CONFIG_SMB_SERVER_SMBDIRECT) += transport_rdma.o diff --git a/fs/ksmbd/asn1.c b/fs/ksmbd/asn1.c deleted file mode 100644 index c03eba0903682..0000000000000 --- a/fs/ksmbd/asn1.c +++ /dev/null @@ -1,239 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-or-later -/* - * The ASB.1/BER parsing code is derived from ip_nat_snmp_basic.c which was in - * turn derived from the gxsnmp package by Gregory McLean & Jochen Friedrich - * - * Copyright (c) 2000 RP Internet (www.rpi.net.au). - */ - -#include -#include -#include -#include -#include -#include - -#include "glob.h" - -#include "asn1.h" -#include "connection.h" -#include "auth.h" -#include "ksmbd_spnego_negtokeninit.asn1.h" -#include "ksmbd_spnego_negtokentarg.asn1.h" - -#define NTLMSSP_OID_LEN 10 - -static char NTLMSSP_OID_STR[NTLMSSP_OID_LEN] = { 0x2b, 0x06, 0x01, 0x04, 0x01, - 0x82, 0x37, 0x02, 0x02, 0x0a }; - -int -ksmbd_decode_negTokenInit(unsigned char *security_blob, int length, - struct ksmbd_conn *conn) -{ - return asn1_ber_decoder(&ksmbd_spnego_negtokeninit_decoder, conn, - security_blob, length); -} - -int -ksmbd_decode_negTokenTarg(unsigned char *security_blob, int length, - struct ksmbd_conn *conn) -{ - return asn1_ber_decoder(&ksmbd_spnego_negtokentarg_decoder, conn, - security_blob, length); -} - -static int compute_asn_hdr_len_bytes(int len) -{ - if (len > 0xFFFFFF) - return 4; - else if (len > 0xFFFF) - return 3; - else if (len > 0xFF) - return 2; - else if (len > 0x7F) - return 1; - else - return 0; -} - -static void encode_asn_tag(char *buf, unsigned int *ofs, char tag, char seq, - int length) -{ - int i; - int index = *ofs; - char hdr_len = compute_asn_hdr_len_bytes(length); - int len = length + 2 + hdr_len; - - /* insert tag */ - buf[index++] = tag; - - if (!hdr_len) { - buf[index++] = len; - } else { - buf[index++] = 0x80 | hdr_len; - for (i = hdr_len - 1; i >= 0; i--) - buf[index++] = (len >> (i * 8)) & 0xFF; - } - - /* insert seq */ - len = len - (index - *ofs); - buf[index++] = seq; - - if (!hdr_len) { - buf[index++] = len; - } else { - buf[index++] = 0x80 | hdr_len; - for (i = hdr_len - 1; i >= 0; i--) - buf[index++] = (len >> (i * 8)) & 0xFF; - } - - *ofs += (index - *ofs); -} - -int build_spnego_ntlmssp_neg_blob(unsigned char **pbuffer, u16 *buflen, - char *ntlm_blob, int ntlm_blob_len) -{ - char *buf; - unsigned int ofs = 0; - int neg_result_len = 4 + compute_asn_hdr_len_bytes(1) * 2 + 1; - int oid_len = 4 + compute_asn_hdr_len_bytes(NTLMSSP_OID_LEN) * 2 + - NTLMSSP_OID_LEN; - int ntlmssp_len = 4 + compute_asn_hdr_len_bytes(ntlm_blob_len) * 2 + - ntlm_blob_len; - int total_len = 4 + compute_asn_hdr_len_bytes(neg_result_len + - oid_len + ntlmssp_len) * 2 + - neg_result_len + oid_len + ntlmssp_len; - - buf = kmalloc(total_len, GFP_KERNEL); - if (!buf) - return -ENOMEM; - - /* insert main gss header */ - encode_asn_tag(buf, &ofs, 0xa1, 0x30, neg_result_len + oid_len + - ntlmssp_len); - - /* insert neg result */ - encode_asn_tag(buf, &ofs, 0xa0, 0x0a, 1); - buf[ofs++] = 1; - - /* insert oid */ - encode_asn_tag(buf, &ofs, 0xa1, 0x06, NTLMSSP_OID_LEN); - memcpy(buf + ofs, NTLMSSP_OID_STR, NTLMSSP_OID_LEN); - ofs += NTLMSSP_OID_LEN; - - /* insert response token - ntlmssp blob */ - encode_asn_tag(buf, &ofs, 0xa2, 0x04, ntlm_blob_len); - memcpy(buf + ofs, ntlm_blob, ntlm_blob_len); - ofs += ntlm_blob_len; - - *pbuffer = buf; - *buflen = total_len; - return 0; -} - -int build_spnego_ntlmssp_auth_blob(unsigned char **pbuffer, u16 *buflen, - int neg_result) -{ - char *buf; - unsigned int ofs = 0; - int neg_result_len = 4 + compute_asn_hdr_len_bytes(1) * 2 + 1; - int total_len = 4 + compute_asn_hdr_len_bytes(neg_result_len) * 2 + - neg_result_len; - - buf = kmalloc(total_len, GFP_KERNEL); - if (!buf) - return -ENOMEM; - - /* insert main gss header */ - encode_asn_tag(buf, &ofs, 0xa1, 0x30, neg_result_len); - - /* insert neg result */ - encode_asn_tag(buf, &ofs, 0xa0, 0x0a, 1); - if (neg_result) - buf[ofs++] = 2; - else - buf[ofs++] = 0; - - *pbuffer = buf; - *buflen = total_len; - return 0; -} - -int ksmbd_gssapi_this_mech(void *context, size_t hdrlen, unsigned char tag, - const void *value, size_t vlen) -{ - enum OID oid; - - oid = look_up_OID(value, vlen); - if (oid != OID_spnego) { - char buf[50]; - - sprint_oid(value, vlen, buf, sizeof(buf)); - ksmbd_debug(AUTH, "Unexpected OID: %s\n", buf); - return -EBADMSG; - } - - return 0; -} - -int ksmbd_neg_token_init_mech_type(void *context, size_t hdrlen, - unsigned char tag, const void *value, - size_t vlen) -{ - struct ksmbd_conn *conn = context; - enum OID oid; - int mech_type; - - oid = look_up_OID(value, vlen); - if (oid == OID_ntlmssp) { - mech_type = KSMBD_AUTH_NTLMSSP; - } else if (oid == OID_mskrb5) { - mech_type = KSMBD_AUTH_MSKRB5; - } else if (oid == OID_krb5) { - mech_type = KSMBD_AUTH_KRB5; - } else if (oid == OID_krb5u2u) { - mech_type = KSMBD_AUTH_KRB5U2U; - } else { - char buf[50]; - - sprint_oid(value, vlen, buf, sizeof(buf)); - ksmbd_debug(AUTH, "Unexpected OID: %s\n", buf); - return -EBADMSG; - } - - conn->auth_mechs |= mech_type; - if (conn->preferred_auth_mech == 0) - conn->preferred_auth_mech = mech_type; - - return 0; -} - -int ksmbd_neg_token_init_mech_token(void *context, size_t hdrlen, - unsigned char tag, const void *value, - size_t vlen) -{ - struct ksmbd_conn *conn = context; - - conn->mechToken = kmalloc(vlen + 1, GFP_KERNEL); - if (!conn->mechToken) - return -ENOMEM; - - memcpy(conn->mechToken, value, vlen); - conn->mechToken[vlen] = '\0'; - return 0; -} - -int ksmbd_neg_token_targ_resp_token(void *context, size_t hdrlen, - unsigned char tag, const void *value, - size_t vlen) -{ - struct ksmbd_conn *conn = context; - - conn->mechToken = kmalloc(vlen + 1, GFP_KERNEL); - if (!conn->mechToken) - return -ENOMEM; - - memcpy(conn->mechToken, value, vlen); - conn->mechToken[vlen] = '\0'; - return 0; -} diff --git a/fs/ksmbd/asn1.h b/fs/ksmbd/asn1.h deleted file mode 100644 index ce105f4ce305a..0000000000000 --- a/fs/ksmbd/asn1.h +++ /dev/null @@ -1,21 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0-or-later */ -/* - * The ASB.1/BER parsing code is derived from ip_nat_snmp_basic.c which was in - * turn derived from the gxsnmp package by Gregory McLean & Jochen Friedrich - * - * Copyright (c) 2000 RP Internet (www.rpi.net.au). - * Copyright (C) 2018 Samsung Electronics Co., Ltd. - */ - -#ifndef __ASN1_H__ -#define __ASN1_H__ - -int ksmbd_decode_negTokenInit(unsigned char *security_blob, int length, - struct ksmbd_conn *conn); -int ksmbd_decode_negTokenTarg(unsigned char *security_blob, int length, - struct ksmbd_conn *conn); -int build_spnego_ntlmssp_neg_blob(unsigned char **pbuffer, u16 *buflen, - char *ntlm_blob, int ntlm_blob_len); -int build_spnego_ntlmssp_auth_blob(unsigned char **pbuffer, u16 *buflen, - int neg_result); -#endif /* __ASN1_H__ */ diff --git a/fs/ksmbd/auth.c b/fs/ksmbd/auth.c deleted file mode 100644 index df8fb076f6f14..0000000000000 --- a/fs/ksmbd/auth.c +++ /dev/null @@ -1,1206 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-or-later -/* - * Copyright (C) 2016 Namjae Jeon - * Copyright (C) 2018 Samsung Electronics Co., Ltd. - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include "auth.h" -#include "glob.h" - -#include -#include - -#include "server.h" -#include "smb_common.h" -#include "connection.h" -#include "mgmt/user_session.h" -#include "mgmt/user_config.h" -#include "crypto_ctx.h" -#include "transport_ipc.h" -#include "../smbfs_common/arc4.h" - -/* - * Fixed format data defining GSS header and fixed string - * "not_defined_in_RFC4178@please_ignore". - * So sec blob data in neg phase could be generated statically. - */ -static char NEGOTIATE_GSS_HEADER[AUTH_GSS_LENGTH] = { -#ifdef CONFIG_SMB_SERVER_KERBEROS5 - 0x60, 0x5e, 0x06, 0x06, 0x2b, 0x06, 0x01, 0x05, - 0x05, 0x02, 0xa0, 0x54, 0x30, 0x52, 0xa0, 0x24, - 0x30, 0x22, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, - 0xf7, 0x12, 0x01, 0x02, 0x02, 0x06, 0x09, 0x2a, - 0x86, 0x48, 0x82, 0xf7, 0x12, 0x01, 0x02, 0x02, - 0x06, 0x0a, 0x2b, 0x06, 0x01, 0x04, 0x01, 0x82, - 0x37, 0x02, 0x02, 0x0a, 0xa3, 0x2a, 0x30, 0x28, - 0xa0, 0x26, 0x1b, 0x24, 0x6e, 0x6f, 0x74, 0x5f, - 0x64, 0x65, 0x66, 0x69, 0x6e, 0x65, 0x64, 0x5f, - 0x69, 0x6e, 0x5f, 0x52, 0x46, 0x43, 0x34, 0x31, - 0x37, 0x38, 0x40, 0x70, 0x6c, 0x65, 0x61, 0x73, - 0x65, 0x5f, 0x69, 0x67, 0x6e, 0x6f, 0x72, 0x65 -#else - 0x60, 0x48, 0x06, 0x06, 0x2b, 0x06, 0x01, 0x05, - 0x05, 0x02, 0xa0, 0x3e, 0x30, 0x3c, 0xa0, 0x0e, - 0x30, 0x0c, 0x06, 0x0a, 0x2b, 0x06, 0x01, 0x04, - 0x01, 0x82, 0x37, 0x02, 0x02, 0x0a, 0xa3, 0x2a, - 0x30, 0x28, 0xa0, 0x26, 0x1b, 0x24, 0x6e, 0x6f, - 0x74, 0x5f, 0x64, 0x65, 0x66, 0x69, 0x6e, 0x65, - 0x64, 0x5f, 0x69, 0x6e, 0x5f, 0x52, 0x46, 0x43, - 0x34, 0x31, 0x37, 0x38, 0x40, 0x70, 0x6c, 0x65, - 0x61, 0x73, 0x65, 0x5f, 0x69, 0x67, 0x6e, 0x6f, - 0x72, 0x65 -#endif -}; - -void ksmbd_copy_gss_neg_header(void *buf) -{ - memcpy(buf, NEGOTIATE_GSS_HEADER, AUTH_GSS_LENGTH); -} - -/** - * ksmbd_gen_sess_key() - function to generate session key - * @sess: session of connection - * @hash: source hash value to be used for find session key - * @hmac: source hmac value to be used for finding session key - * - */ -static int ksmbd_gen_sess_key(struct ksmbd_session *sess, char *hash, - char *hmac) -{ - struct ksmbd_crypto_ctx *ctx; - int rc; - - ctx = ksmbd_crypto_ctx_find_hmacmd5(); - if (!ctx) { - ksmbd_debug(AUTH, "could not crypto alloc hmacmd5\n"); - return -ENOMEM; - } - - rc = crypto_shash_setkey(CRYPTO_HMACMD5_TFM(ctx), - hash, - CIFS_HMAC_MD5_HASH_SIZE); - if (rc) { - ksmbd_debug(AUTH, "hmacmd5 set key fail error %d\n", rc); - goto out; - } - - rc = crypto_shash_init(CRYPTO_HMACMD5(ctx)); - if (rc) { - ksmbd_debug(AUTH, "could not init hmacmd5 error %d\n", rc); - goto out; - } - - rc = crypto_shash_update(CRYPTO_HMACMD5(ctx), - hmac, - SMB2_NTLMV2_SESSKEY_SIZE); - if (rc) { - ksmbd_debug(AUTH, "Could not update with response error %d\n", rc); - goto out; - } - - rc = crypto_shash_final(CRYPTO_HMACMD5(ctx), sess->sess_key); - if (rc) { - ksmbd_debug(AUTH, "Could not generate hmacmd5 hash error %d\n", rc); - goto out; - } - -out: - ksmbd_release_crypto_ctx(ctx); - return rc; -} - -static int calc_ntlmv2_hash(struct ksmbd_conn *conn, struct ksmbd_session *sess, - char *ntlmv2_hash, char *dname) -{ - int ret, len, conv_len; - wchar_t *domain = NULL; - __le16 *uniname = NULL; - struct ksmbd_crypto_ctx *ctx; - - ctx = ksmbd_crypto_ctx_find_hmacmd5(); - if (!ctx) { - ksmbd_debug(AUTH, "can't generate ntlmv2 hash\n"); - return -ENOMEM; - } - - ret = crypto_shash_setkey(CRYPTO_HMACMD5_TFM(ctx), - user_passkey(sess->user), - CIFS_ENCPWD_SIZE); - if (ret) { - ksmbd_debug(AUTH, "Could not set NT Hash as a key\n"); - goto out; - } - - ret = crypto_shash_init(CRYPTO_HMACMD5(ctx)); - if (ret) { - ksmbd_debug(AUTH, "could not init hmacmd5\n"); - goto out; - } - - /* convert user_name to unicode */ - len = strlen(user_name(sess->user)); - uniname = kzalloc(2 + UNICODE_LEN(len), GFP_KERNEL); - if (!uniname) { - ret = -ENOMEM; - goto out; - } - - conv_len = smb_strtoUTF16(uniname, user_name(sess->user), len, - conn->local_nls); - if (conv_len < 0 || conv_len > len) { - ret = -EINVAL; - goto out; - } - UniStrupr(uniname); - - ret = crypto_shash_update(CRYPTO_HMACMD5(ctx), - (char *)uniname, - UNICODE_LEN(conv_len)); - if (ret) { - ksmbd_debug(AUTH, "Could not update with user\n"); - goto out; - } - - /* Convert domain name or conn name to unicode and uppercase */ - len = strlen(dname); - domain = kzalloc(2 + UNICODE_LEN(len), GFP_KERNEL); - if (!domain) { - ret = -ENOMEM; - goto out; - } - - conv_len = smb_strtoUTF16((__le16 *)domain, dname, len, - conn->local_nls); - if (conv_len < 0 || conv_len > len) { - ret = -EINVAL; - goto out; - } - - ret = crypto_shash_update(CRYPTO_HMACMD5(ctx), - (char *)domain, - UNICODE_LEN(conv_len)); - if (ret) { - ksmbd_debug(AUTH, "Could not update with domain\n"); - goto out; - } - - ret = crypto_shash_final(CRYPTO_HMACMD5(ctx), ntlmv2_hash); - if (ret) - ksmbd_debug(AUTH, "Could not generate md5 hash\n"); -out: - kfree(uniname); - kfree(domain); - ksmbd_release_crypto_ctx(ctx); - return ret; -} - -/** - * ksmbd_auth_ntlmv2() - NTLMv2 authentication handler - * @sess: session of connection - * @ntlmv2: NTLMv2 challenge response - * @blen: NTLMv2 blob length - * @domain_name: domain name - * - * Return: 0 on success, error number on error - */ -int ksmbd_auth_ntlmv2(struct ksmbd_conn *conn, struct ksmbd_session *sess, - struct ntlmv2_resp *ntlmv2, int blen, char *domain_name, - char *cryptkey) -{ - char ntlmv2_hash[CIFS_ENCPWD_SIZE]; - char ntlmv2_rsp[CIFS_HMAC_MD5_HASH_SIZE]; - struct ksmbd_crypto_ctx *ctx = NULL; - char *construct = NULL; - int rc, len; - - rc = calc_ntlmv2_hash(conn, sess, ntlmv2_hash, domain_name); - if (rc) { - ksmbd_debug(AUTH, "could not get v2 hash rc %d\n", rc); - goto out; - } - - ctx = ksmbd_crypto_ctx_find_hmacmd5(); - if (!ctx) { - ksmbd_debug(AUTH, "could not crypto alloc hmacmd5\n"); - return -ENOMEM; - } - - rc = crypto_shash_setkey(CRYPTO_HMACMD5_TFM(ctx), - ntlmv2_hash, - CIFS_HMAC_MD5_HASH_SIZE); - if (rc) { - ksmbd_debug(AUTH, "Could not set NTLMV2 Hash as a key\n"); - goto out; - } - - rc = crypto_shash_init(CRYPTO_HMACMD5(ctx)); - if (rc) { - ksmbd_debug(AUTH, "Could not init hmacmd5\n"); - goto out; - } - - len = CIFS_CRYPTO_KEY_SIZE + blen; - construct = kzalloc(len, GFP_KERNEL); - if (!construct) { - rc = -ENOMEM; - goto out; - } - - memcpy(construct, cryptkey, CIFS_CRYPTO_KEY_SIZE); - memcpy(construct + CIFS_CRYPTO_KEY_SIZE, &ntlmv2->blob_signature, blen); - - rc = crypto_shash_update(CRYPTO_HMACMD5(ctx), construct, len); - if (rc) { - ksmbd_debug(AUTH, "Could not update with response\n"); - goto out; - } - - rc = crypto_shash_final(CRYPTO_HMACMD5(ctx), ntlmv2_rsp); - if (rc) { - ksmbd_debug(AUTH, "Could not generate md5 hash\n"); - goto out; - } - ksmbd_release_crypto_ctx(ctx); - ctx = NULL; - - rc = ksmbd_gen_sess_key(sess, ntlmv2_hash, ntlmv2_rsp); - if (rc) { - ksmbd_debug(AUTH, "Could not generate sess key\n"); - goto out; - } - - if (memcmp(ntlmv2->ntlmv2_hash, ntlmv2_rsp, CIFS_HMAC_MD5_HASH_SIZE) != 0) - rc = -EINVAL; -out: - if (ctx) - ksmbd_release_crypto_ctx(ctx); - kfree(construct); - return rc; -} - -/** - * ksmbd_decode_ntlmssp_auth_blob() - helper function to construct - * authenticate blob - * @authblob: authenticate blob source pointer - * @usr: user details - * @sess: session of connection - * - * Return: 0 on success, error number on error - */ -int ksmbd_decode_ntlmssp_auth_blob(struct authenticate_message *authblob, - int blob_len, struct ksmbd_conn *conn, - struct ksmbd_session *sess) -{ - char *domain_name; - unsigned int nt_off, dn_off; - unsigned short nt_len, dn_len; - int ret; - - if (blob_len < sizeof(struct authenticate_message)) { - ksmbd_debug(AUTH, "negotiate blob len %d too small\n", - blob_len); - return -EINVAL; - } - - if (memcmp(authblob->Signature, "NTLMSSP", 8)) { - ksmbd_debug(AUTH, "blob signature incorrect %s\n", - authblob->Signature); - return -EINVAL; - } - - nt_off = le32_to_cpu(authblob->NtChallengeResponse.BufferOffset); - nt_len = le16_to_cpu(authblob->NtChallengeResponse.Length); - dn_off = le32_to_cpu(authblob->DomainName.BufferOffset); - dn_len = le16_to_cpu(authblob->DomainName.Length); - - if (blob_len < (u64)dn_off + dn_len || blob_len < (u64)nt_off + nt_len || - nt_len < CIFS_ENCPWD_SIZE) - return -EINVAL; - - /* TODO : use domain name that imported from configuration file */ - domain_name = smb_strndup_from_utf16((const char *)authblob + dn_off, - dn_len, true, conn->local_nls); - if (IS_ERR(domain_name)) - return PTR_ERR(domain_name); - - /* process NTLMv2 authentication */ - ksmbd_debug(AUTH, "decode_ntlmssp_authenticate_blob dname%s\n", - domain_name); - ret = ksmbd_auth_ntlmv2(conn, sess, - (struct ntlmv2_resp *)((char *)authblob + nt_off), - nt_len - CIFS_ENCPWD_SIZE, - domain_name, conn->ntlmssp.cryptkey); - kfree(domain_name); - - /* The recovered secondary session key */ - if (conn->ntlmssp.client_flags & NTLMSSP_NEGOTIATE_KEY_XCH) { - struct arc4_ctx *ctx_arc4; - unsigned int sess_key_off, sess_key_len; - - sess_key_off = le32_to_cpu(authblob->SessionKey.BufferOffset); - sess_key_len = le16_to_cpu(authblob->SessionKey.Length); - - if (blob_len < (u64)sess_key_off + sess_key_len) - return -EINVAL; - - ctx_arc4 = kmalloc(sizeof(*ctx_arc4), GFP_KERNEL); - if (!ctx_arc4) - return -ENOMEM; - - cifs_arc4_setkey(ctx_arc4, sess->sess_key, - SMB2_NTLMV2_SESSKEY_SIZE); - cifs_arc4_crypt(ctx_arc4, sess->sess_key, - (char *)authblob + sess_key_off, sess_key_len); - kfree_sensitive(ctx_arc4); - } - - return ret; -} - -/** - * ksmbd_decode_ntlmssp_neg_blob() - helper function to construct - * negotiate blob - * @negblob: negotiate blob source pointer - * @rsp: response header pointer to be updated - * @sess: session of connection - * - */ -int ksmbd_decode_ntlmssp_neg_blob(struct negotiate_message *negblob, - int blob_len, struct ksmbd_conn *conn) -{ - if (blob_len < sizeof(struct negotiate_message)) { - ksmbd_debug(AUTH, "negotiate blob len %d too small\n", - blob_len); - return -EINVAL; - } - - if (memcmp(negblob->Signature, "NTLMSSP", 8)) { - ksmbd_debug(AUTH, "blob signature incorrect %s\n", - negblob->Signature); - return -EINVAL; - } - - conn->ntlmssp.client_flags = le32_to_cpu(negblob->NegotiateFlags); - return 0; -} - -/** - * ksmbd_build_ntlmssp_challenge_blob() - helper function to construct - * challenge blob - * @chgblob: challenge blob source pointer to initialize - * @rsp: response header pointer to be updated - * @sess: session of connection - * - */ -unsigned int -ksmbd_build_ntlmssp_challenge_blob(struct challenge_message *chgblob, - struct ksmbd_conn *conn) -{ - struct target_info *tinfo; - wchar_t *name; - __u8 *target_name; - unsigned int flags, blob_off, blob_len, type, target_info_len = 0; - int len, uni_len, conv_len; - int cflags = conn->ntlmssp.client_flags; - - memcpy(chgblob->Signature, NTLMSSP_SIGNATURE, 8); - chgblob->MessageType = NtLmChallenge; - - flags = NTLMSSP_NEGOTIATE_UNICODE | - NTLMSSP_NEGOTIATE_NTLM | NTLMSSP_TARGET_TYPE_SERVER | - NTLMSSP_NEGOTIATE_TARGET_INFO; - - if (cflags & NTLMSSP_NEGOTIATE_SIGN) { - flags |= NTLMSSP_NEGOTIATE_SIGN; - flags |= cflags & (NTLMSSP_NEGOTIATE_128 | - NTLMSSP_NEGOTIATE_56); - } - - if (cflags & NTLMSSP_NEGOTIATE_SEAL && smb3_encryption_negotiated(conn)) - flags |= NTLMSSP_NEGOTIATE_SEAL; - - if (cflags & NTLMSSP_NEGOTIATE_ALWAYS_SIGN) - flags |= NTLMSSP_NEGOTIATE_ALWAYS_SIGN; - - if (cflags & NTLMSSP_REQUEST_TARGET) - flags |= NTLMSSP_REQUEST_TARGET; - - if (conn->use_spnego && - (cflags & NTLMSSP_NEGOTIATE_EXTENDED_SEC)) - flags |= NTLMSSP_NEGOTIATE_EXTENDED_SEC; - - if (cflags & NTLMSSP_NEGOTIATE_KEY_XCH) - flags |= NTLMSSP_NEGOTIATE_KEY_XCH; - - chgblob->NegotiateFlags = cpu_to_le32(flags); - len = strlen(ksmbd_netbios_name()); - name = kmalloc(2 + UNICODE_LEN(len), GFP_KERNEL); - if (!name) - return -ENOMEM; - - conv_len = smb_strtoUTF16((__le16 *)name, ksmbd_netbios_name(), len, - conn->local_nls); - if (conv_len < 0 || conv_len > len) { - kfree(name); - return -EINVAL; - } - - uni_len = UNICODE_LEN(conv_len); - - blob_off = sizeof(struct challenge_message); - blob_len = blob_off + uni_len; - - chgblob->TargetName.Length = cpu_to_le16(uni_len); - chgblob->TargetName.MaximumLength = cpu_to_le16(uni_len); - chgblob->TargetName.BufferOffset = cpu_to_le32(blob_off); - - /* Initialize random conn challenge */ - get_random_bytes(conn->ntlmssp.cryptkey, sizeof(__u64)); - memcpy(chgblob->Challenge, conn->ntlmssp.cryptkey, - CIFS_CRYPTO_KEY_SIZE); - - /* Add Target Information to security buffer */ - chgblob->TargetInfoArray.BufferOffset = cpu_to_le32(blob_len); - - target_name = (__u8 *)chgblob + blob_off; - memcpy(target_name, name, uni_len); - tinfo = (struct target_info *)(target_name + uni_len); - - chgblob->TargetInfoArray.Length = 0; - /* Add target info list for NetBIOS/DNS settings */ - for (type = NTLMSSP_AV_NB_COMPUTER_NAME; - type <= NTLMSSP_AV_DNS_DOMAIN_NAME; type++) { - tinfo->Type = cpu_to_le16(type); - tinfo->Length = cpu_to_le16(uni_len); - memcpy(tinfo->Content, name, uni_len); - tinfo = (struct target_info *)((char *)tinfo + 4 + uni_len); - target_info_len += 4 + uni_len; - } - - /* Add terminator subblock */ - tinfo->Type = 0; - tinfo->Length = 0; - target_info_len += 4; - - chgblob->TargetInfoArray.Length = cpu_to_le16(target_info_len); - chgblob->TargetInfoArray.MaximumLength = cpu_to_le16(target_info_len); - blob_len += target_info_len; - kfree(name); - ksmbd_debug(AUTH, "NTLMSSP SecurityBufferLength %d\n", blob_len); - return blob_len; -} - -#ifdef CONFIG_SMB_SERVER_KERBEROS5 -int ksmbd_krb5_authenticate(struct ksmbd_session *sess, char *in_blob, - int in_len, char *out_blob, int *out_len) -{ - struct ksmbd_spnego_authen_response *resp; - struct ksmbd_user *user = NULL; - int retval; - - resp = ksmbd_ipc_spnego_authen_request(in_blob, in_len); - if (!resp) { - ksmbd_debug(AUTH, "SPNEGO_AUTHEN_REQUEST failure\n"); - return -EINVAL; - } - - if (!(resp->login_response.status & KSMBD_USER_FLAG_OK)) { - ksmbd_debug(AUTH, "krb5 authentication failure\n"); - retval = -EPERM; - goto out; - } - - if (*out_len <= resp->spnego_blob_len) { - ksmbd_debug(AUTH, "buf len %d, but blob len %d\n", - *out_len, resp->spnego_blob_len); - retval = -EINVAL; - goto out; - } - - if (resp->session_key_len > sizeof(sess->sess_key)) { - ksmbd_debug(AUTH, "session key is too long\n"); - retval = -EINVAL; - goto out; - } - - user = ksmbd_alloc_user(&resp->login_response); - if (!user) { - ksmbd_debug(AUTH, "login failure\n"); - retval = -ENOMEM; - goto out; - } - sess->user = user; - - memcpy(sess->sess_key, resp->payload, resp->session_key_len); - memcpy(out_blob, resp->payload + resp->session_key_len, - resp->spnego_blob_len); - *out_len = resp->spnego_blob_len; - retval = 0; -out: - kvfree(resp); - return retval; -} -#else -int ksmbd_krb5_authenticate(struct ksmbd_session *sess, char *in_blob, - int in_len, char *out_blob, int *out_len) -{ - return -EOPNOTSUPP; -} -#endif - -/** - * ksmbd_sign_smb2_pdu() - function to generate packet signing - * @conn: connection - * @key: signing key - * @iov: buffer iov array - * @n_vec: number of iovecs - * @sig: signature value generated for client request packet - * - */ -int ksmbd_sign_smb2_pdu(struct ksmbd_conn *conn, char *key, struct kvec *iov, - int n_vec, char *sig) -{ - struct ksmbd_crypto_ctx *ctx; - int rc, i; - - ctx = ksmbd_crypto_ctx_find_hmacsha256(); - if (!ctx) { - ksmbd_debug(AUTH, "could not crypto alloc hmacmd5\n"); - return -ENOMEM; - } - - rc = crypto_shash_setkey(CRYPTO_HMACSHA256_TFM(ctx), - key, - SMB2_NTLMV2_SESSKEY_SIZE); - if (rc) - goto out; - - rc = crypto_shash_init(CRYPTO_HMACSHA256(ctx)); - if (rc) { - ksmbd_debug(AUTH, "hmacsha256 init error %d\n", rc); - goto out; - } - - for (i = 0; i < n_vec; i++) { - rc = crypto_shash_update(CRYPTO_HMACSHA256(ctx), - iov[i].iov_base, - iov[i].iov_len); - if (rc) { - ksmbd_debug(AUTH, "hmacsha256 update error %d\n", rc); - goto out; - } - } - - rc = crypto_shash_final(CRYPTO_HMACSHA256(ctx), sig); - if (rc) - ksmbd_debug(AUTH, "hmacsha256 generation error %d\n", rc); -out: - ksmbd_release_crypto_ctx(ctx); - return rc; -} - -/** - * ksmbd_sign_smb3_pdu() - function to generate packet signing - * @conn: connection - * @key: signing key - * @iov: buffer iov array - * @n_vec: number of iovecs - * @sig: signature value generated for client request packet - * - */ -int ksmbd_sign_smb3_pdu(struct ksmbd_conn *conn, char *key, struct kvec *iov, - int n_vec, char *sig) -{ - struct ksmbd_crypto_ctx *ctx; - int rc, i; - - ctx = ksmbd_crypto_ctx_find_cmacaes(); - if (!ctx) { - ksmbd_debug(AUTH, "could not crypto alloc cmac\n"); - return -ENOMEM; - } - - rc = crypto_shash_setkey(CRYPTO_CMACAES_TFM(ctx), - key, - SMB2_CMACAES_SIZE); - if (rc) - goto out; - - rc = crypto_shash_init(CRYPTO_CMACAES(ctx)); - if (rc) { - ksmbd_debug(AUTH, "cmaces init error %d\n", rc); - goto out; - } - - for (i = 0; i < n_vec; i++) { - rc = crypto_shash_update(CRYPTO_CMACAES(ctx), - iov[i].iov_base, - iov[i].iov_len); - if (rc) { - ksmbd_debug(AUTH, "cmaces update error %d\n", rc); - goto out; - } - } - - rc = crypto_shash_final(CRYPTO_CMACAES(ctx), sig); - if (rc) - ksmbd_debug(AUTH, "cmaces generation error %d\n", rc); -out: - ksmbd_release_crypto_ctx(ctx); - return rc; -} - -struct derivation { - struct kvec label; - struct kvec context; - bool binding; -}; - -static int generate_key(struct ksmbd_conn *conn, struct ksmbd_session *sess, - struct kvec label, struct kvec context, __u8 *key, - unsigned int key_size) -{ - unsigned char zero = 0x0; - __u8 i[4] = {0, 0, 0, 1}; - __u8 L128[4] = {0, 0, 0, 128}; - __u8 L256[4] = {0, 0, 1, 0}; - int rc; - unsigned char prfhash[SMB2_HMACSHA256_SIZE]; - unsigned char *hashptr = prfhash; - struct ksmbd_crypto_ctx *ctx; - - memset(prfhash, 0x0, SMB2_HMACSHA256_SIZE); - memset(key, 0x0, key_size); - - ctx = ksmbd_crypto_ctx_find_hmacsha256(); - if (!ctx) { - ksmbd_debug(AUTH, "could not crypto alloc hmacmd5\n"); - return -ENOMEM; - } - - rc = crypto_shash_setkey(CRYPTO_HMACSHA256_TFM(ctx), - sess->sess_key, - SMB2_NTLMV2_SESSKEY_SIZE); - if (rc) - goto smb3signkey_ret; - - rc = crypto_shash_init(CRYPTO_HMACSHA256(ctx)); - if (rc) { - ksmbd_debug(AUTH, "hmacsha256 init error %d\n", rc); - goto smb3signkey_ret; - } - - rc = crypto_shash_update(CRYPTO_HMACSHA256(ctx), i, 4); - if (rc) { - ksmbd_debug(AUTH, "could not update with n\n"); - goto smb3signkey_ret; - } - - rc = crypto_shash_update(CRYPTO_HMACSHA256(ctx), - label.iov_base, - label.iov_len); - if (rc) { - ksmbd_debug(AUTH, "could not update with label\n"); - goto smb3signkey_ret; - } - - rc = crypto_shash_update(CRYPTO_HMACSHA256(ctx), &zero, 1); - if (rc) { - ksmbd_debug(AUTH, "could not update with zero\n"); - goto smb3signkey_ret; - } - - rc = crypto_shash_update(CRYPTO_HMACSHA256(ctx), - context.iov_base, - context.iov_len); - if (rc) { - ksmbd_debug(AUTH, "could not update with context\n"); - goto smb3signkey_ret; - } - - if (key_size == SMB3_ENC_DEC_KEY_SIZE && - (conn->cipher_type == SMB2_ENCRYPTION_AES256_CCM || - conn->cipher_type == SMB2_ENCRYPTION_AES256_GCM)) - rc = crypto_shash_update(CRYPTO_HMACSHA256(ctx), L256, 4); - else - rc = crypto_shash_update(CRYPTO_HMACSHA256(ctx), L128, 4); - if (rc) { - ksmbd_debug(AUTH, "could not update with L\n"); - goto smb3signkey_ret; - } - - rc = crypto_shash_final(CRYPTO_HMACSHA256(ctx), hashptr); - if (rc) { - ksmbd_debug(AUTH, "Could not generate hmacmd5 hash error %d\n", - rc); - goto smb3signkey_ret; - } - - memcpy(key, hashptr, key_size); - -smb3signkey_ret: - ksmbd_release_crypto_ctx(ctx); - return rc; -} - -static int generate_smb3signingkey(struct ksmbd_session *sess, - struct ksmbd_conn *conn, - const struct derivation *signing) -{ - int rc; - struct channel *chann; - char *key; - - chann = lookup_chann_list(sess, conn); - if (!chann) - return 0; - - if (conn->dialect >= SMB30_PROT_ID && signing->binding) - key = chann->smb3signingkey; - else - key = sess->smb3signingkey; - - rc = generate_key(conn, sess, signing->label, signing->context, key, - SMB3_SIGN_KEY_SIZE); - if (rc) - return rc; - - if (!(conn->dialect >= SMB30_PROT_ID && signing->binding)) - memcpy(chann->smb3signingkey, key, SMB3_SIGN_KEY_SIZE); - - ksmbd_debug(AUTH, "dumping generated AES signing keys\n"); - ksmbd_debug(AUTH, "Session Id %llu\n", sess->id); - ksmbd_debug(AUTH, "Session Key %*ph\n", - SMB2_NTLMV2_SESSKEY_SIZE, sess->sess_key); - ksmbd_debug(AUTH, "Signing Key %*ph\n", - SMB3_SIGN_KEY_SIZE, key); - return 0; -} - -int ksmbd_gen_smb30_signingkey(struct ksmbd_session *sess, - struct ksmbd_conn *conn) -{ - struct derivation d; - - d.label.iov_base = "SMB2AESCMAC"; - d.label.iov_len = 12; - d.context.iov_base = "SmbSign"; - d.context.iov_len = 8; - d.binding = conn->binding; - - return generate_smb3signingkey(sess, conn, &d); -} - -int ksmbd_gen_smb311_signingkey(struct ksmbd_session *sess, - struct ksmbd_conn *conn) -{ - struct derivation d; - - d.label.iov_base = "SMBSigningKey"; - d.label.iov_len = 14; - if (conn->binding) { - struct preauth_session *preauth_sess; - - preauth_sess = ksmbd_preauth_session_lookup(conn, sess->id); - if (!preauth_sess) - return -ENOENT; - d.context.iov_base = preauth_sess->Preauth_HashValue; - } else { - d.context.iov_base = sess->Preauth_HashValue; - } - d.context.iov_len = 64; - d.binding = conn->binding; - - return generate_smb3signingkey(sess, conn, &d); -} - -struct derivation_twin { - struct derivation encryption; - struct derivation decryption; -}; - -static int generate_smb3encryptionkey(struct ksmbd_conn *conn, - struct ksmbd_session *sess, - const struct derivation_twin *ptwin) -{ - int rc; - - rc = generate_key(conn, sess, ptwin->encryption.label, - ptwin->encryption.context, sess->smb3encryptionkey, - SMB3_ENC_DEC_KEY_SIZE); - if (rc) - return rc; - - rc = generate_key(conn, sess, ptwin->decryption.label, - ptwin->decryption.context, - sess->smb3decryptionkey, SMB3_ENC_DEC_KEY_SIZE); - if (rc) - return rc; - - ksmbd_debug(AUTH, "dumping generated AES encryption keys\n"); - ksmbd_debug(AUTH, "Cipher type %d\n", conn->cipher_type); - ksmbd_debug(AUTH, "Session Id %llu\n", sess->id); - ksmbd_debug(AUTH, "Session Key %*ph\n", - SMB2_NTLMV2_SESSKEY_SIZE, sess->sess_key); - if (conn->cipher_type == SMB2_ENCRYPTION_AES256_CCM || - conn->cipher_type == SMB2_ENCRYPTION_AES256_GCM) { - ksmbd_debug(AUTH, "ServerIn Key %*ph\n", - SMB3_GCM256_CRYPTKEY_SIZE, sess->smb3encryptionkey); - ksmbd_debug(AUTH, "ServerOut Key %*ph\n", - SMB3_GCM256_CRYPTKEY_SIZE, sess->smb3decryptionkey); - } else { - ksmbd_debug(AUTH, "ServerIn Key %*ph\n", - SMB3_GCM128_CRYPTKEY_SIZE, sess->smb3encryptionkey); - ksmbd_debug(AUTH, "ServerOut Key %*ph\n", - SMB3_GCM128_CRYPTKEY_SIZE, sess->smb3decryptionkey); - } - return 0; -} - -int ksmbd_gen_smb30_encryptionkey(struct ksmbd_conn *conn, - struct ksmbd_session *sess) -{ - struct derivation_twin twin; - struct derivation *d; - - d = &twin.encryption; - d->label.iov_base = "SMB2AESCCM"; - d->label.iov_len = 11; - d->context.iov_base = "ServerOut"; - d->context.iov_len = 10; - - d = &twin.decryption; - d->label.iov_base = "SMB2AESCCM"; - d->label.iov_len = 11; - d->context.iov_base = "ServerIn "; - d->context.iov_len = 10; - - return generate_smb3encryptionkey(conn, sess, &twin); -} - -int ksmbd_gen_smb311_encryptionkey(struct ksmbd_conn *conn, - struct ksmbd_session *sess) -{ - struct derivation_twin twin; - struct derivation *d; - - d = &twin.encryption; - d->label.iov_base = "SMBS2CCipherKey"; - d->label.iov_len = 16; - d->context.iov_base = sess->Preauth_HashValue; - d->context.iov_len = 64; - - d = &twin.decryption; - d->label.iov_base = "SMBC2SCipherKey"; - d->label.iov_len = 16; - d->context.iov_base = sess->Preauth_HashValue; - d->context.iov_len = 64; - - return generate_smb3encryptionkey(conn, sess, &twin); -} - -int ksmbd_gen_preauth_integrity_hash(struct ksmbd_conn *conn, char *buf, - __u8 *pi_hash) -{ - int rc; - struct smb2_hdr *rcv_hdr = smb2_get_msg(buf); - char *all_bytes_msg = (char *)&rcv_hdr->ProtocolId; - int msg_size = get_rfc1002_len(buf); - struct ksmbd_crypto_ctx *ctx = NULL; - - if (conn->preauth_info->Preauth_HashId != - SMB2_PREAUTH_INTEGRITY_SHA512) - return -EINVAL; - - ctx = ksmbd_crypto_ctx_find_sha512(); - if (!ctx) { - ksmbd_debug(AUTH, "could not alloc sha512\n"); - return -ENOMEM; - } - - rc = crypto_shash_init(CRYPTO_SHA512(ctx)); - if (rc) { - ksmbd_debug(AUTH, "could not init shashn"); - goto out; - } - - rc = crypto_shash_update(CRYPTO_SHA512(ctx), pi_hash, 64); - if (rc) { - ksmbd_debug(AUTH, "could not update with n\n"); - goto out; - } - - rc = crypto_shash_update(CRYPTO_SHA512(ctx), all_bytes_msg, msg_size); - if (rc) { - ksmbd_debug(AUTH, "could not update with n\n"); - goto out; - } - - rc = crypto_shash_final(CRYPTO_SHA512(ctx), pi_hash); - if (rc) { - ksmbd_debug(AUTH, "Could not generate hash err : %d\n", rc); - goto out; - } -out: - ksmbd_release_crypto_ctx(ctx); - return rc; -} - -int ksmbd_gen_sd_hash(struct ksmbd_conn *conn, char *sd_buf, int len, - __u8 *pi_hash) -{ - int rc; - struct ksmbd_crypto_ctx *ctx = NULL; - - ctx = ksmbd_crypto_ctx_find_sha256(); - if (!ctx) { - ksmbd_debug(AUTH, "could not alloc sha256\n"); - return -ENOMEM; - } - - rc = crypto_shash_init(CRYPTO_SHA256(ctx)); - if (rc) { - ksmbd_debug(AUTH, "could not init shashn"); - goto out; - } - - rc = crypto_shash_update(CRYPTO_SHA256(ctx), sd_buf, len); - if (rc) { - ksmbd_debug(AUTH, "could not update with n\n"); - goto out; - } - - rc = crypto_shash_final(CRYPTO_SHA256(ctx), pi_hash); - if (rc) { - ksmbd_debug(AUTH, "Could not generate hash err : %d\n", rc); - goto out; - } -out: - ksmbd_release_crypto_ctx(ctx); - return rc; -} - -static int ksmbd_get_encryption_key(struct ksmbd_work *work, __u64 ses_id, - int enc, u8 *key) -{ - struct ksmbd_session *sess; - u8 *ses_enc_key; - - if (enc) - sess = work->sess; - else - sess = ksmbd_session_lookup_all(work->conn, ses_id); - if (!sess) - return -EINVAL; - - ses_enc_key = enc ? sess->smb3encryptionkey : - sess->smb3decryptionkey; - memcpy(key, ses_enc_key, SMB3_ENC_DEC_KEY_SIZE); - - return 0; -} - -static inline void smb2_sg_set_buf(struct scatterlist *sg, const void *buf, - unsigned int buflen) -{ - void *addr; - - if (is_vmalloc_addr(buf)) - addr = vmalloc_to_page(buf); - else - addr = virt_to_page(buf); - sg_set_page(sg, addr, buflen, offset_in_page(buf)); -} - -static struct scatterlist *ksmbd_init_sg(struct kvec *iov, unsigned int nvec, - u8 *sign) -{ - struct scatterlist *sg; - unsigned int assoc_data_len = sizeof(struct smb2_transform_hdr) - 20; - int i, nr_entries[3] = {0}, total_entries = 0, sg_idx = 0; - - if (!nvec) - return NULL; - - for (i = 0; i < nvec - 1; i++) { - unsigned long kaddr = (unsigned long)iov[i + 1].iov_base; - - if (is_vmalloc_addr(iov[i + 1].iov_base)) { - nr_entries[i] = ((kaddr + iov[i + 1].iov_len + - PAGE_SIZE - 1) >> PAGE_SHIFT) - - (kaddr >> PAGE_SHIFT); - } else { - nr_entries[i]++; - } - total_entries += nr_entries[i]; - } - - /* Add two entries for transform header and signature */ - total_entries += 2; - - sg = kmalloc_array(total_entries, sizeof(struct scatterlist), GFP_KERNEL); - if (!sg) - return NULL; - - sg_init_table(sg, total_entries); - smb2_sg_set_buf(&sg[sg_idx++], iov[0].iov_base + 24, assoc_data_len); - for (i = 0; i < nvec - 1; i++) { - void *data = iov[i + 1].iov_base; - int len = iov[i + 1].iov_len; - - if (is_vmalloc_addr(data)) { - int j, offset = offset_in_page(data); - - for (j = 0; j < nr_entries[i]; j++) { - unsigned int bytes = PAGE_SIZE - offset; - - if (!len) - break; - - if (bytes > len) - bytes = len; - - sg_set_page(&sg[sg_idx++], - vmalloc_to_page(data), bytes, - offset_in_page(data)); - - data += bytes; - len -= bytes; - offset = 0; - } - } else { - sg_set_page(&sg[sg_idx++], virt_to_page(data), len, - offset_in_page(data)); - } - } - smb2_sg_set_buf(&sg[sg_idx], sign, SMB2_SIGNATURE_SIZE); - return sg; -} - -int ksmbd_crypt_message(struct ksmbd_work *work, struct kvec *iov, - unsigned int nvec, int enc) -{ - struct ksmbd_conn *conn = work->conn; - struct smb2_transform_hdr *tr_hdr = smb2_get_msg(iov[0].iov_base); - unsigned int assoc_data_len = sizeof(struct smb2_transform_hdr) - 20; - int rc; - struct scatterlist *sg; - u8 sign[SMB2_SIGNATURE_SIZE] = {}; - u8 key[SMB3_ENC_DEC_KEY_SIZE]; - struct aead_request *req; - char *iv; - unsigned int iv_len; - struct crypto_aead *tfm; - unsigned int crypt_len = le32_to_cpu(tr_hdr->OriginalMessageSize); - struct ksmbd_crypto_ctx *ctx; - - rc = ksmbd_get_encryption_key(work, - le64_to_cpu(tr_hdr->SessionId), - enc, - key); - if (rc) { - pr_err("Could not get %scryption key\n", enc ? "en" : "de"); - return rc; - } - - if (conn->cipher_type == SMB2_ENCRYPTION_AES128_GCM || - conn->cipher_type == SMB2_ENCRYPTION_AES256_GCM) - ctx = ksmbd_crypto_ctx_find_gcm(); - else - ctx = ksmbd_crypto_ctx_find_ccm(); - if (!ctx) { - pr_err("crypto alloc failed\n"); - return -ENOMEM; - } - - if (conn->cipher_type == SMB2_ENCRYPTION_AES128_GCM || - conn->cipher_type == SMB2_ENCRYPTION_AES256_GCM) - tfm = CRYPTO_GCM(ctx); - else - tfm = CRYPTO_CCM(ctx); - - if (conn->cipher_type == SMB2_ENCRYPTION_AES256_CCM || - conn->cipher_type == SMB2_ENCRYPTION_AES256_GCM) - rc = crypto_aead_setkey(tfm, key, SMB3_GCM256_CRYPTKEY_SIZE); - else - rc = crypto_aead_setkey(tfm, key, SMB3_GCM128_CRYPTKEY_SIZE); - if (rc) { - pr_err("Failed to set aead key %d\n", rc); - goto free_ctx; - } - - rc = crypto_aead_setauthsize(tfm, SMB2_SIGNATURE_SIZE); - if (rc) { - pr_err("Failed to set authsize %d\n", rc); - goto free_ctx; - } - - req = aead_request_alloc(tfm, GFP_KERNEL); - if (!req) { - rc = -ENOMEM; - goto free_ctx; - } - - if (!enc) { - memcpy(sign, &tr_hdr->Signature, SMB2_SIGNATURE_SIZE); - crypt_len += SMB2_SIGNATURE_SIZE; - } - - sg = ksmbd_init_sg(iov, nvec, sign); - if (!sg) { - pr_err("Failed to init sg\n"); - rc = -ENOMEM; - goto free_req; - } - - iv_len = crypto_aead_ivsize(tfm); - iv = kzalloc(iv_len, GFP_KERNEL); - if (!iv) { - rc = -ENOMEM; - goto free_sg; - } - - if (conn->cipher_type == SMB2_ENCRYPTION_AES128_GCM || - conn->cipher_type == SMB2_ENCRYPTION_AES256_GCM) { - memcpy(iv, (char *)tr_hdr->Nonce, SMB3_AES_GCM_NONCE); - } else { - iv[0] = 3; - memcpy(iv + 1, (char *)tr_hdr->Nonce, SMB3_AES_CCM_NONCE); - } - - aead_request_set_crypt(req, sg, sg, crypt_len, iv); - aead_request_set_ad(req, assoc_data_len); - aead_request_set_callback(req, CRYPTO_TFM_REQ_MAY_SLEEP, NULL, NULL); - - if (enc) - rc = crypto_aead_encrypt(req); - else - rc = crypto_aead_decrypt(req); - if (rc) - goto free_iv; - - if (enc) - memcpy(&tr_hdr->Signature, sign, SMB2_SIGNATURE_SIZE); - -free_iv: - kfree(iv); -free_sg: - kfree(sg); -free_req: - kfree(req); -free_ctx: - ksmbd_release_crypto_ctx(ctx); - return rc; -} diff --git a/fs/ksmbd/auth.h b/fs/ksmbd/auth.h deleted file mode 100644 index 362b6159a6cff..0000000000000 --- a/fs/ksmbd/auth.h +++ /dev/null @@ -1,71 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0-or-later */ -/* - * Copyright (C) 2018 Samsung Electronics Co., Ltd. - */ - -#ifndef __AUTH_H__ -#define __AUTH_H__ - -#include "ntlmssp.h" - -#ifdef CONFIG_SMB_SERVER_KERBEROS5 -#define AUTH_GSS_LENGTH 96 -#define AUTH_GSS_PADDING 0 -#else -#define AUTH_GSS_LENGTH 74 -#define AUTH_GSS_PADDING 6 -#endif - -#define CIFS_HMAC_MD5_HASH_SIZE (16) -#define CIFS_NTHASH_SIZE (16) - -/* - * Size of the ntlm client response - */ -#define CIFS_AUTH_RESP_SIZE 24 -#define CIFS_SMB1_SIGNATURE_SIZE 8 -#define CIFS_SMB1_SESSKEY_SIZE 16 - -#define KSMBD_AUTH_NTLMSSP 0x0001 -#define KSMBD_AUTH_KRB5 0x0002 -#define KSMBD_AUTH_MSKRB5 0x0004 -#define KSMBD_AUTH_KRB5U2U 0x0008 - -struct ksmbd_session; -struct ksmbd_conn; -struct ksmbd_work; -struct kvec; - -int ksmbd_crypt_message(struct ksmbd_work *work, struct kvec *iov, - unsigned int nvec, int enc); -void ksmbd_copy_gss_neg_header(void *buf); -int ksmbd_auth_ntlmv2(struct ksmbd_conn *conn, struct ksmbd_session *sess, - struct ntlmv2_resp *ntlmv2, int blen, char *domain_name, - char *cryptkey); -int ksmbd_decode_ntlmssp_auth_blob(struct authenticate_message *authblob, - int blob_len, struct ksmbd_conn *conn, - struct ksmbd_session *sess); -int ksmbd_decode_ntlmssp_neg_blob(struct negotiate_message *negblob, - int blob_len, struct ksmbd_conn *conn); -unsigned int -ksmbd_build_ntlmssp_challenge_blob(struct challenge_message *chgblob, - struct ksmbd_conn *conn); -int ksmbd_krb5_authenticate(struct ksmbd_session *sess, char *in_blob, - int in_len, char *out_blob, int *out_len); -int ksmbd_sign_smb2_pdu(struct ksmbd_conn *conn, char *key, struct kvec *iov, - int n_vec, char *sig); -int ksmbd_sign_smb3_pdu(struct ksmbd_conn *conn, char *key, struct kvec *iov, - int n_vec, char *sig); -int ksmbd_gen_smb30_signingkey(struct ksmbd_session *sess, - struct ksmbd_conn *conn); -int ksmbd_gen_smb311_signingkey(struct ksmbd_session *sess, - struct ksmbd_conn *conn); -int ksmbd_gen_smb30_encryptionkey(struct ksmbd_conn *conn, - struct ksmbd_session *sess); -int ksmbd_gen_smb311_encryptionkey(struct ksmbd_conn *conn, - struct ksmbd_session *sess); -int ksmbd_gen_preauth_integrity_hash(struct ksmbd_conn *conn, char *buf, - __u8 *pi_hash); -int ksmbd_gen_sd_hash(struct ksmbd_conn *conn, char *sd_buf, int len, - __u8 *pi_hash); -#endif diff --git a/fs/ksmbd/connection.c b/fs/ksmbd/connection.c deleted file mode 100644 index e1d2be19cddfa..0000000000000 --- a/fs/ksmbd/connection.c +++ /dev/null @@ -1,485 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-or-later -/* - * Copyright (C) 2016 Namjae Jeon - * Copyright (C) 2018 Samsung Electronics Co., Ltd. - */ - -#include -#include -#include - -#include "server.h" -#include "smb_common.h" -#include "mgmt/ksmbd_ida.h" -#include "connection.h" -#include "transport_tcp.h" -#include "transport_rdma.h" - -static DEFINE_MUTEX(init_lock); - -static struct ksmbd_conn_ops default_conn_ops; - -LIST_HEAD(conn_list); -DECLARE_RWSEM(conn_list_lock); - -/** - * ksmbd_conn_free() - free resources of the connection instance - * - * @conn: connection instance to be cleand up - * - * During the thread termination, the corresponding conn instance - * resources(sock/memory) are released and finally the conn object is freed. - */ -void ksmbd_conn_free(struct ksmbd_conn *conn) -{ - down_write(&conn_list_lock); - list_del(&conn->conns_list); - up_write(&conn_list_lock); - - xa_destroy(&conn->sessions); - kvfree(conn->request_buf); - kfree(conn->preauth_info); - kfree(conn); -} - -/** - * ksmbd_conn_alloc() - initialize a new connection instance - * - * Return: ksmbd_conn struct on success, otherwise NULL - */ -struct ksmbd_conn *ksmbd_conn_alloc(void) -{ - struct ksmbd_conn *conn; - - conn = kzalloc(sizeof(struct ksmbd_conn), GFP_KERNEL); - if (!conn) - return NULL; - - conn->need_neg = true; - ksmbd_conn_set_new(conn); - conn->local_nls = load_nls("utf8"); - if (!conn->local_nls) - conn->local_nls = load_nls_default(); - if (IS_ENABLED(CONFIG_UNICODE)) - conn->um = utf8_load(UNICODE_AGE(12, 1, 0)); - else - conn->um = ERR_PTR(-EOPNOTSUPP); - if (IS_ERR(conn->um)) - conn->um = NULL; - atomic_set(&conn->req_running, 0); - atomic_set(&conn->r_count, 0); - conn->total_credits = 1; - conn->outstanding_credits = 0; - - init_waitqueue_head(&conn->req_running_q); - init_waitqueue_head(&conn->r_count_q); - INIT_LIST_HEAD(&conn->conns_list); - INIT_LIST_HEAD(&conn->requests); - INIT_LIST_HEAD(&conn->async_requests); - spin_lock_init(&conn->request_lock); - spin_lock_init(&conn->credits_lock); - ida_init(&conn->async_ida); - xa_init(&conn->sessions); - - spin_lock_init(&conn->llist_lock); - INIT_LIST_HEAD(&conn->lock_list); - - down_write(&conn_list_lock); - list_add(&conn->conns_list, &conn_list); - up_write(&conn_list_lock); - return conn; -} - -bool ksmbd_conn_lookup_dialect(struct ksmbd_conn *c) -{ - struct ksmbd_conn *t; - bool ret = false; - - down_read(&conn_list_lock); - list_for_each_entry(t, &conn_list, conns_list) { - if (memcmp(t->ClientGUID, c->ClientGUID, SMB2_CLIENT_GUID_SIZE)) - continue; - - ret = true; - break; - } - up_read(&conn_list_lock); - return ret; -} - -void ksmbd_conn_enqueue_request(struct ksmbd_work *work) -{ - struct ksmbd_conn *conn = work->conn; - struct list_head *requests_queue = NULL; - - if (conn->ops->get_cmd_val(work) != SMB2_CANCEL_HE) { - requests_queue = &conn->requests; - work->syncronous = true; - } - - if (requests_queue) { - atomic_inc(&conn->req_running); - spin_lock(&conn->request_lock); - list_add_tail(&work->request_entry, requests_queue); - spin_unlock(&conn->request_lock); - } -} - -int ksmbd_conn_try_dequeue_request(struct ksmbd_work *work) -{ - struct ksmbd_conn *conn = work->conn; - int ret = 1; - - if (list_empty(&work->request_entry) && - list_empty(&work->async_request_entry)) - return 0; - - if (!work->multiRsp) - atomic_dec(&conn->req_running); - spin_lock(&conn->request_lock); - if (!work->multiRsp) { - list_del_init(&work->request_entry); - if (work->syncronous == false) - list_del_init(&work->async_request_entry); - ret = 0; - } - spin_unlock(&conn->request_lock); - - wake_up_all(&conn->req_running_q); - return ret; -} - -void ksmbd_conn_lock(struct ksmbd_conn *conn) -{ - mutex_lock(&conn->srv_mutex); -} - -void ksmbd_conn_unlock(struct ksmbd_conn *conn) -{ - mutex_unlock(&conn->srv_mutex); -} - -void ksmbd_all_conn_set_status(u64 sess_id, u32 status) -{ - struct ksmbd_conn *conn; - - down_read(&conn_list_lock); - list_for_each_entry(conn, &conn_list, conns_list) { - if (conn->binding || xa_load(&conn->sessions, sess_id)) - WRITE_ONCE(conn->status, status); - } - up_read(&conn_list_lock); -} - -void ksmbd_conn_wait_idle(struct ksmbd_conn *conn, u64 sess_id) -{ - struct ksmbd_conn *bind_conn; - - wait_event(conn->req_running_q, atomic_read(&conn->req_running) < 2); - - down_read(&conn_list_lock); - list_for_each_entry(bind_conn, &conn_list, conns_list) { - if (bind_conn == conn) - continue; - - if ((bind_conn->binding || xa_load(&bind_conn->sessions, sess_id)) && - !ksmbd_conn_releasing(bind_conn) && - atomic_read(&bind_conn->req_running)) { - wait_event(bind_conn->req_running_q, - atomic_read(&bind_conn->req_running) == 0); - } - } - up_read(&conn_list_lock); -} - -int ksmbd_conn_write(struct ksmbd_work *work) -{ - struct ksmbd_conn *conn = work->conn; - size_t len = 0; - int sent; - struct kvec iov[3]; - int iov_idx = 0; - - if (!work->response_buf) { - pr_err("NULL response header\n"); - return -EINVAL; - } - - if (work->tr_buf) { - iov[iov_idx] = (struct kvec) { work->tr_buf, - sizeof(struct smb2_transform_hdr) + 4 }; - len += iov[iov_idx++].iov_len; - } - - if (work->aux_payload_sz) { - iov[iov_idx] = (struct kvec) { work->response_buf, work->resp_hdr_sz }; - len += iov[iov_idx++].iov_len; - iov[iov_idx] = (struct kvec) { work->aux_payload_buf, work->aux_payload_sz }; - len += iov[iov_idx++].iov_len; - } else { - if (work->tr_buf) - iov[iov_idx].iov_len = work->resp_hdr_sz; - else - iov[iov_idx].iov_len = get_rfc1002_len(work->response_buf) + 4; - iov[iov_idx].iov_base = work->response_buf; - len += iov[iov_idx++].iov_len; - } - - ksmbd_conn_lock(conn); - sent = conn->transport->ops->writev(conn->transport, &iov[0], - iov_idx, len, - work->need_invalidate_rkey, - work->remote_key); - ksmbd_conn_unlock(conn); - - if (sent < 0) { - pr_err("Failed to send message: %d\n", sent); - return sent; - } - - return 0; -} - -int ksmbd_conn_rdma_read(struct ksmbd_conn *conn, - void *buf, unsigned int buflen, - struct smb2_buffer_desc_v1 *desc, - unsigned int desc_len) -{ - int ret = -EINVAL; - - if (conn->transport->ops->rdma_read) - ret = conn->transport->ops->rdma_read(conn->transport, - buf, buflen, - desc, desc_len); - return ret; -} - -int ksmbd_conn_rdma_write(struct ksmbd_conn *conn, - void *buf, unsigned int buflen, - struct smb2_buffer_desc_v1 *desc, - unsigned int desc_len) -{ - int ret = -EINVAL; - - if (conn->transport->ops->rdma_write) - ret = conn->transport->ops->rdma_write(conn->transport, - buf, buflen, - desc, desc_len); - return ret; -} - -bool ksmbd_conn_alive(struct ksmbd_conn *conn) -{ - if (!ksmbd_server_running()) - return false; - - if (ksmbd_conn_exiting(conn)) - return false; - - if (kthread_should_stop()) - return false; - - if (atomic_read(&conn->stats.open_files_count) > 0) - return true; - - /* - * Stop current session if the time that get last request from client - * is bigger than deadtime user configured and opening file count is - * zero. - */ - if (server_conf.deadtime > 0 && - time_after(jiffies, conn->last_active + server_conf.deadtime)) { - ksmbd_debug(CONN, "No response from client in %lu minutes\n", - server_conf.deadtime / SMB_ECHO_INTERVAL); - return false; - } - return true; -} - -#define SMB1_MIN_SUPPORTED_HEADER_SIZE (sizeof(struct smb_hdr)) -#define SMB2_MIN_SUPPORTED_HEADER_SIZE (sizeof(struct smb2_hdr) + 4) - -/** - * ksmbd_conn_handler_loop() - session thread to listen on new smb requests - * @p: connection instance - * - * One thread each per connection - * - * Return: 0 on success - */ -int ksmbd_conn_handler_loop(void *p) -{ - struct ksmbd_conn *conn = (struct ksmbd_conn *)p; - struct ksmbd_transport *t = conn->transport; - unsigned int pdu_size, max_allowed_pdu_size; - char hdr_buf[4] = {0,}; - int size; - - mutex_init(&conn->srv_mutex); - __module_get(THIS_MODULE); - - if (t->ops->prepare && t->ops->prepare(t)) - goto out; - - conn->last_active = jiffies; - while (ksmbd_conn_alive(conn)) { - if (try_to_freeze()) - continue; - - kvfree(conn->request_buf); - conn->request_buf = NULL; - - size = t->ops->read(t, hdr_buf, sizeof(hdr_buf), -1); - if (size != sizeof(hdr_buf)) - break; - - pdu_size = get_rfc1002_len(hdr_buf); - ksmbd_debug(CONN, "RFC1002 header %u bytes\n", pdu_size); - - if (ksmbd_conn_good(conn)) - max_allowed_pdu_size = - SMB3_MAX_MSGSIZE + conn->vals->max_write_size; - else - max_allowed_pdu_size = SMB3_MAX_MSGSIZE; - - if (pdu_size > max_allowed_pdu_size) { - pr_err_ratelimited("PDU length(%u) excceed maximum allowed pdu size(%u) on connection(%d)\n", - pdu_size, max_allowed_pdu_size, - READ_ONCE(conn->status)); - break; - } - - /* - * Check maximum pdu size(0x00FFFFFF). - */ - if (pdu_size > MAX_STREAM_PROT_LEN) - break; - - if (pdu_size < SMB1_MIN_SUPPORTED_HEADER_SIZE) - break; - - /* 4 for rfc1002 length field */ - /* 1 for implied bcc[0] */ - size = pdu_size + 4 + 1; - conn->request_buf = kvmalloc(size, GFP_KERNEL); - if (!conn->request_buf) - break; - - memcpy(conn->request_buf, hdr_buf, sizeof(hdr_buf)); - - /* - * We already read 4 bytes to find out PDU size, now - * read in PDU - */ - size = t->ops->read(t, conn->request_buf + 4, pdu_size, 2); - if (size < 0) { - pr_err("sock_read failed: %d\n", size); - break; - } - - if (size != pdu_size) { - pr_err("PDU error. Read: %d, Expected: %d\n", - size, pdu_size); - continue; - } - - if (!ksmbd_smb_request(conn)) - break; - - if (((struct smb2_hdr *)smb2_get_msg(conn->request_buf))->ProtocolId == - SMB2_PROTO_NUMBER) { - if (pdu_size < SMB2_MIN_SUPPORTED_HEADER_SIZE) - break; - } - - if (!default_conn_ops.process_fn) { - pr_err("No connection request callback\n"); - break; - } - - if (default_conn_ops.process_fn(conn)) { - pr_err("Cannot handle request\n"); - break; - } - } - -out: - ksmbd_conn_set_releasing(conn); - /* Wait till all reference dropped to the Server object*/ - wait_event(conn->r_count_q, atomic_read(&conn->r_count) == 0); - - if (IS_ENABLED(CONFIG_UNICODE)) - utf8_unload(conn->um); - unload_nls(conn->local_nls); - if (default_conn_ops.terminate_fn) - default_conn_ops.terminate_fn(conn); - t->ops->disconnect(t); - module_put(THIS_MODULE); - return 0; -} - -void ksmbd_conn_init_server_callbacks(struct ksmbd_conn_ops *ops) -{ - default_conn_ops.process_fn = ops->process_fn; - default_conn_ops.terminate_fn = ops->terminate_fn; -} - -int ksmbd_conn_transport_init(void) -{ - int ret; - - mutex_lock(&init_lock); - ret = ksmbd_tcp_init(); - if (ret) { - pr_err("Failed to init TCP subsystem: %d\n", ret); - goto out; - } - - ret = ksmbd_rdma_init(); - if (ret) { - pr_err("Failed to init RDMA subsystem: %d\n", ret); - goto out; - } -out: - mutex_unlock(&init_lock); - return ret; -} - -static void stop_sessions(void) -{ - struct ksmbd_conn *conn; - struct ksmbd_transport *t; - -again: - down_read(&conn_list_lock); - list_for_each_entry(conn, &conn_list, conns_list) { - struct task_struct *task; - - t = conn->transport; - task = t->handler; - if (task) - ksmbd_debug(CONN, "Stop session handler %s/%d\n", - task->comm, task_pid_nr(task)); - ksmbd_conn_set_exiting(conn); - if (t->ops->shutdown) { - up_read(&conn_list_lock); - t->ops->shutdown(t); - down_read(&conn_list_lock); - } - } - up_read(&conn_list_lock); - - if (!list_empty(&conn_list)) { - schedule_timeout_interruptible(HZ / 10); /* 100ms */ - goto again; - } -} - -void ksmbd_conn_transport_destroy(void) -{ - mutex_lock(&init_lock); - ksmbd_tcp_destroy(); - ksmbd_rdma_destroy(); - stop_sessions(); - mutex_unlock(&init_lock); -} diff --git a/fs/ksmbd/connection.h b/fs/ksmbd/connection.h deleted file mode 100644 index ad8dfaa48ffb3..0000000000000 --- a/fs/ksmbd/connection.h +++ /dev/null @@ -1,231 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0-or-later */ -/* - * Copyright (C) 2018 Samsung Electronics Co., Ltd. - */ - -#ifndef __KSMBD_CONNECTION_H__ -#define __KSMBD_CONNECTION_H__ - -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include "smb_common.h" -#include "ksmbd_work.h" - -#define KSMBD_SOCKET_BACKLOG 16 - -enum { - KSMBD_SESS_NEW = 0, - KSMBD_SESS_GOOD, - KSMBD_SESS_EXITING, - KSMBD_SESS_NEED_RECONNECT, - KSMBD_SESS_NEED_NEGOTIATE, - KSMBD_SESS_RELEASING -}; - -struct ksmbd_stats { - atomic_t open_files_count; - atomic64_t request_served; -}; - -struct ksmbd_transport; - -struct ksmbd_conn { - struct smb_version_values *vals; - struct smb_version_ops *ops; - struct smb_version_cmds *cmds; - unsigned int max_cmds; - struct mutex srv_mutex; - int status; - unsigned int cli_cap; - char *request_buf; - struct ksmbd_transport *transport; - struct nls_table *local_nls; - struct unicode_map *um; - struct list_head conns_list; - /* smb session 1 per user */ - struct xarray sessions; - unsigned long last_active; - /* How many request are running currently */ - atomic_t req_running; - /* References which are made for this Server object*/ - atomic_t r_count; - unsigned int total_credits; - unsigned int outstanding_credits; - spinlock_t credits_lock; - wait_queue_head_t req_running_q; - wait_queue_head_t r_count_q; - /* Lock to protect requests list*/ - spinlock_t request_lock; - struct list_head requests; - struct list_head async_requests; - int connection_type; - struct ksmbd_stats stats; - char ClientGUID[SMB2_CLIENT_GUID_SIZE]; - struct ntlmssp_auth ntlmssp; - - spinlock_t llist_lock; - struct list_head lock_list; - - struct preauth_integrity_info *preauth_info; - - bool need_neg; - unsigned int auth_mechs; - unsigned int preferred_auth_mech; - bool sign; - bool use_spnego:1; - __u16 cli_sec_mode; - __u16 srv_sec_mode; - /* dialect index that server chose */ - __u16 dialect; - - char *mechToken; - - struct ksmbd_conn_ops *conn_ops; - - /* Preauth Session Table */ - struct list_head preauth_sess_table; - - struct sockaddr_storage peer_addr; - - /* Identifier for async message */ - struct ida async_ida; - - __le16 cipher_type; - __le16 compress_algorithm; - bool posix_ext_supported; - bool signing_negotiated; - __le16 signing_algorithm; - bool binding; -}; - -struct ksmbd_conn_ops { - int (*process_fn)(struct ksmbd_conn *conn); - int (*terminate_fn)(struct ksmbd_conn *conn); -}; - -struct ksmbd_transport_ops { - int (*prepare)(struct ksmbd_transport *t); - void (*disconnect)(struct ksmbd_transport *t); - void (*shutdown)(struct ksmbd_transport *t); - int (*read)(struct ksmbd_transport *t, char *buf, - unsigned int size, int max_retries); - int (*writev)(struct ksmbd_transport *t, struct kvec *iovs, int niov, - int size, bool need_invalidate_rkey, - unsigned int remote_key); - int (*rdma_read)(struct ksmbd_transport *t, - void *buf, unsigned int len, - struct smb2_buffer_desc_v1 *desc, - unsigned int desc_len); - int (*rdma_write)(struct ksmbd_transport *t, - void *buf, unsigned int len, - struct smb2_buffer_desc_v1 *desc, - unsigned int desc_len); -}; - -struct ksmbd_transport { - struct ksmbd_conn *conn; - struct ksmbd_transport_ops *ops; - struct task_struct *handler; -}; - -#define KSMBD_TCP_RECV_TIMEOUT (7 * HZ) -#define KSMBD_TCP_SEND_TIMEOUT (5 * HZ) -#define KSMBD_TCP_PEER_SOCKADDR(c) ((struct sockaddr *)&((c)->peer_addr)) - -extern struct list_head conn_list; -extern struct rw_semaphore conn_list_lock; - -bool ksmbd_conn_alive(struct ksmbd_conn *conn); -void ksmbd_conn_wait_idle(struct ksmbd_conn *conn, u64 sess_id); -struct ksmbd_conn *ksmbd_conn_alloc(void); -void ksmbd_conn_free(struct ksmbd_conn *conn); -bool ksmbd_conn_lookup_dialect(struct ksmbd_conn *c); -int ksmbd_conn_write(struct ksmbd_work *work); -int ksmbd_conn_rdma_read(struct ksmbd_conn *conn, - void *buf, unsigned int buflen, - struct smb2_buffer_desc_v1 *desc, - unsigned int desc_len); -int ksmbd_conn_rdma_write(struct ksmbd_conn *conn, - void *buf, unsigned int buflen, - struct smb2_buffer_desc_v1 *desc, - unsigned int desc_len); -void ksmbd_conn_enqueue_request(struct ksmbd_work *work); -int ksmbd_conn_try_dequeue_request(struct ksmbd_work *work); -void ksmbd_conn_init_server_callbacks(struct ksmbd_conn_ops *ops); -int ksmbd_conn_handler_loop(void *p); -int ksmbd_conn_transport_init(void); -void ksmbd_conn_transport_destroy(void); -void ksmbd_conn_lock(struct ksmbd_conn *conn); -void ksmbd_conn_unlock(struct ksmbd_conn *conn); - -/* - * WARNING - * - * This is a hack. We will move status to a proper place once we land - * a multi-sessions support. - */ -static inline bool ksmbd_conn_good(struct ksmbd_conn *conn) -{ - return READ_ONCE(conn->status) == KSMBD_SESS_GOOD; -} - -static inline bool ksmbd_conn_need_negotiate(struct ksmbd_conn *conn) -{ - return READ_ONCE(conn->status) == KSMBD_SESS_NEED_NEGOTIATE; -} - -static inline bool ksmbd_conn_need_reconnect(struct ksmbd_conn *conn) -{ - return READ_ONCE(conn->status) == KSMBD_SESS_NEED_RECONNECT; -} - -static inline bool ksmbd_conn_exiting(struct ksmbd_conn *conn) -{ - return READ_ONCE(conn->status) == KSMBD_SESS_EXITING; -} - -static inline bool ksmbd_conn_releasing(struct ksmbd_conn *conn) -{ - return READ_ONCE(conn->status) == KSMBD_SESS_RELEASING; -} - -static inline void ksmbd_conn_set_new(struct ksmbd_conn *conn) -{ - WRITE_ONCE(conn->status, KSMBD_SESS_NEW); -} - -static inline void ksmbd_conn_set_good(struct ksmbd_conn *conn) -{ - WRITE_ONCE(conn->status, KSMBD_SESS_GOOD); -} - -static inline void ksmbd_conn_set_need_negotiate(struct ksmbd_conn *conn) -{ - WRITE_ONCE(conn->status, KSMBD_SESS_NEED_NEGOTIATE); -} - -static inline void ksmbd_conn_set_need_reconnect(struct ksmbd_conn *conn) -{ - WRITE_ONCE(conn->status, KSMBD_SESS_NEED_RECONNECT); -} - -static inline void ksmbd_conn_set_exiting(struct ksmbd_conn *conn) -{ - WRITE_ONCE(conn->status, KSMBD_SESS_EXITING); -} - -static inline void ksmbd_conn_set_releasing(struct ksmbd_conn *conn) -{ - WRITE_ONCE(conn->status, KSMBD_SESS_RELEASING); -} - -void ksmbd_all_conn_set_status(u64 sess_id, u32 status); -#endif /* __CONNECTION_H__ */ diff --git a/fs/ksmbd/crypto_ctx.c b/fs/ksmbd/crypto_ctx.c deleted file mode 100644 index 81488d04199da..0000000000000 --- a/fs/ksmbd/crypto_ctx.c +++ /dev/null @@ -1,266 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-or-later -/* - * Copyright (C) 2019 Samsung Electronics Co., Ltd. - */ - -#include -#include -#include -#include -#include -#include - -#include "glob.h" -#include "crypto_ctx.h" - -struct crypto_ctx_list { - spinlock_t ctx_lock; - int avail_ctx; - struct list_head idle_ctx; - wait_queue_head_t ctx_wait; -}; - -static struct crypto_ctx_list ctx_list; - -static inline void free_aead(struct crypto_aead *aead) -{ - if (aead) - crypto_free_aead(aead); -} - -static void free_shash(struct shash_desc *shash) -{ - if (shash) { - crypto_free_shash(shash->tfm); - kfree(shash); - } -} - -static struct crypto_aead *alloc_aead(int id) -{ - struct crypto_aead *tfm = NULL; - - switch (id) { - case CRYPTO_AEAD_AES_GCM: - tfm = crypto_alloc_aead("gcm(aes)", 0, 0); - break; - case CRYPTO_AEAD_AES_CCM: - tfm = crypto_alloc_aead("ccm(aes)", 0, 0); - break; - default: - pr_err("Does not support encrypt ahead(id : %d)\n", id); - return NULL; - } - - if (IS_ERR(tfm)) { - pr_err("Failed to alloc encrypt aead : %ld\n", PTR_ERR(tfm)); - return NULL; - } - - return tfm; -} - -static struct shash_desc *alloc_shash_desc(int id) -{ - struct crypto_shash *tfm = NULL; - struct shash_desc *shash; - - switch (id) { - case CRYPTO_SHASH_HMACMD5: - tfm = crypto_alloc_shash("hmac(md5)", 0, 0); - break; - case CRYPTO_SHASH_HMACSHA256: - tfm = crypto_alloc_shash("hmac(sha256)", 0, 0); - break; - case CRYPTO_SHASH_CMACAES: - tfm = crypto_alloc_shash("cmac(aes)", 0, 0); - break; - case CRYPTO_SHASH_SHA256: - tfm = crypto_alloc_shash("sha256", 0, 0); - break; - case CRYPTO_SHASH_SHA512: - tfm = crypto_alloc_shash("sha512", 0, 0); - break; - default: - return NULL; - } - - if (IS_ERR(tfm)) - return NULL; - - shash = kzalloc(sizeof(*shash) + crypto_shash_descsize(tfm), - GFP_KERNEL); - if (!shash) - crypto_free_shash(tfm); - else - shash->tfm = tfm; - return shash; -} - -static void ctx_free(struct ksmbd_crypto_ctx *ctx) -{ - int i; - - for (i = 0; i < CRYPTO_SHASH_MAX; i++) - free_shash(ctx->desc[i]); - for (i = 0; i < CRYPTO_AEAD_MAX; i++) - free_aead(ctx->ccmaes[i]); - kfree(ctx); -} - -static struct ksmbd_crypto_ctx *ksmbd_find_crypto_ctx(void) -{ - struct ksmbd_crypto_ctx *ctx; - - while (1) { - spin_lock(&ctx_list.ctx_lock); - if (!list_empty(&ctx_list.idle_ctx)) { - ctx = list_entry(ctx_list.idle_ctx.next, - struct ksmbd_crypto_ctx, - list); - list_del(&ctx->list); - spin_unlock(&ctx_list.ctx_lock); - return ctx; - } - - if (ctx_list.avail_ctx > num_online_cpus()) { - spin_unlock(&ctx_list.ctx_lock); - wait_event(ctx_list.ctx_wait, - !list_empty(&ctx_list.idle_ctx)); - continue; - } - - ctx_list.avail_ctx++; - spin_unlock(&ctx_list.ctx_lock); - - ctx = kzalloc(sizeof(struct ksmbd_crypto_ctx), GFP_KERNEL); - if (!ctx) { - spin_lock(&ctx_list.ctx_lock); - ctx_list.avail_ctx--; - spin_unlock(&ctx_list.ctx_lock); - wait_event(ctx_list.ctx_wait, - !list_empty(&ctx_list.idle_ctx)); - continue; - } - break; - } - return ctx; -} - -void ksmbd_release_crypto_ctx(struct ksmbd_crypto_ctx *ctx) -{ - if (!ctx) - return; - - spin_lock(&ctx_list.ctx_lock); - if (ctx_list.avail_ctx <= num_online_cpus()) { - list_add(&ctx->list, &ctx_list.idle_ctx); - spin_unlock(&ctx_list.ctx_lock); - wake_up(&ctx_list.ctx_wait); - return; - } - - ctx_list.avail_ctx--; - spin_unlock(&ctx_list.ctx_lock); - ctx_free(ctx); -} - -static struct ksmbd_crypto_ctx *____crypto_shash_ctx_find(int id) -{ - struct ksmbd_crypto_ctx *ctx; - - if (id >= CRYPTO_SHASH_MAX) - return NULL; - - ctx = ksmbd_find_crypto_ctx(); - if (ctx->desc[id]) - return ctx; - - ctx->desc[id] = alloc_shash_desc(id); - if (ctx->desc[id]) - return ctx; - ksmbd_release_crypto_ctx(ctx); - return NULL; -} - -struct ksmbd_crypto_ctx *ksmbd_crypto_ctx_find_hmacmd5(void) -{ - return ____crypto_shash_ctx_find(CRYPTO_SHASH_HMACMD5); -} - -struct ksmbd_crypto_ctx *ksmbd_crypto_ctx_find_hmacsha256(void) -{ - return ____crypto_shash_ctx_find(CRYPTO_SHASH_HMACSHA256); -} - -struct ksmbd_crypto_ctx *ksmbd_crypto_ctx_find_cmacaes(void) -{ - return ____crypto_shash_ctx_find(CRYPTO_SHASH_CMACAES); -} - -struct ksmbd_crypto_ctx *ksmbd_crypto_ctx_find_sha256(void) -{ - return ____crypto_shash_ctx_find(CRYPTO_SHASH_SHA256); -} - -struct ksmbd_crypto_ctx *ksmbd_crypto_ctx_find_sha512(void) -{ - return ____crypto_shash_ctx_find(CRYPTO_SHASH_SHA512); -} - -static struct ksmbd_crypto_ctx *____crypto_aead_ctx_find(int id) -{ - struct ksmbd_crypto_ctx *ctx; - - if (id >= CRYPTO_AEAD_MAX) - return NULL; - - ctx = ksmbd_find_crypto_ctx(); - if (ctx->ccmaes[id]) - return ctx; - - ctx->ccmaes[id] = alloc_aead(id); - if (ctx->ccmaes[id]) - return ctx; - ksmbd_release_crypto_ctx(ctx); - return NULL; -} - -struct ksmbd_crypto_ctx *ksmbd_crypto_ctx_find_gcm(void) -{ - return ____crypto_aead_ctx_find(CRYPTO_AEAD_AES_GCM); -} - -struct ksmbd_crypto_ctx *ksmbd_crypto_ctx_find_ccm(void) -{ - return ____crypto_aead_ctx_find(CRYPTO_AEAD_AES_CCM); -} - -void ksmbd_crypto_destroy(void) -{ - struct ksmbd_crypto_ctx *ctx; - - while (!list_empty(&ctx_list.idle_ctx)) { - ctx = list_entry(ctx_list.idle_ctx.next, - struct ksmbd_crypto_ctx, - list); - list_del(&ctx->list); - ctx_free(ctx); - } -} - -int ksmbd_crypto_create(void) -{ - struct ksmbd_crypto_ctx *ctx; - - spin_lock_init(&ctx_list.ctx_lock); - INIT_LIST_HEAD(&ctx_list.idle_ctx); - init_waitqueue_head(&ctx_list.ctx_wait); - ctx_list.avail_ctx = 1; - - ctx = kzalloc(sizeof(struct ksmbd_crypto_ctx), GFP_KERNEL); - if (!ctx) - return -ENOMEM; - list_add(&ctx->list, &ctx_list.idle_ctx); - return 0; -} diff --git a/fs/ksmbd/crypto_ctx.h b/fs/ksmbd/crypto_ctx.h deleted file mode 100644 index 4a367c62f6536..0000000000000 --- a/fs/ksmbd/crypto_ctx.h +++ /dev/null @@ -1,66 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0-or-later */ -/* - * Copyright (C) 2019 Samsung Electronics Co., Ltd. - */ - -#ifndef __CRYPTO_CTX_H__ -#define __CRYPTO_CTX_H__ - -#include -#include - -enum { - CRYPTO_SHASH_HMACMD5 = 0, - CRYPTO_SHASH_HMACSHA256, - CRYPTO_SHASH_CMACAES, - CRYPTO_SHASH_SHA256, - CRYPTO_SHASH_SHA512, - CRYPTO_SHASH_MAX, -}; - -enum { - CRYPTO_AEAD_AES_GCM = 16, - CRYPTO_AEAD_AES_CCM, - CRYPTO_AEAD_MAX, -}; - -enum { - CRYPTO_BLK_ECBDES = 32, - CRYPTO_BLK_MAX, -}; - -struct ksmbd_crypto_ctx { - struct list_head list; - - struct shash_desc *desc[CRYPTO_SHASH_MAX]; - struct crypto_aead *ccmaes[CRYPTO_AEAD_MAX]; -}; - -#define CRYPTO_HMACMD5(c) ((c)->desc[CRYPTO_SHASH_HMACMD5]) -#define CRYPTO_HMACSHA256(c) ((c)->desc[CRYPTO_SHASH_HMACSHA256]) -#define CRYPTO_CMACAES(c) ((c)->desc[CRYPTO_SHASH_CMACAES]) -#define CRYPTO_SHA256(c) ((c)->desc[CRYPTO_SHASH_SHA256]) -#define CRYPTO_SHA512(c) ((c)->desc[CRYPTO_SHASH_SHA512]) - -#define CRYPTO_HMACMD5_TFM(c) ((c)->desc[CRYPTO_SHASH_HMACMD5]->tfm) -#define CRYPTO_HMACSHA256_TFM(c)\ - ((c)->desc[CRYPTO_SHASH_HMACSHA256]->tfm) -#define CRYPTO_CMACAES_TFM(c) ((c)->desc[CRYPTO_SHASH_CMACAES]->tfm) -#define CRYPTO_SHA256_TFM(c) ((c)->desc[CRYPTO_SHASH_SHA256]->tfm) -#define CRYPTO_SHA512_TFM(c) ((c)->desc[CRYPTO_SHASH_SHA512]->tfm) - -#define CRYPTO_GCM(c) ((c)->ccmaes[CRYPTO_AEAD_AES_GCM]) -#define CRYPTO_CCM(c) ((c)->ccmaes[CRYPTO_AEAD_AES_CCM]) - -void ksmbd_release_crypto_ctx(struct ksmbd_crypto_ctx *ctx); -struct ksmbd_crypto_ctx *ksmbd_crypto_ctx_find_hmacmd5(void); -struct ksmbd_crypto_ctx *ksmbd_crypto_ctx_find_hmacsha256(void); -struct ksmbd_crypto_ctx *ksmbd_crypto_ctx_find_cmacaes(void); -struct ksmbd_crypto_ctx *ksmbd_crypto_ctx_find_sha512(void); -struct ksmbd_crypto_ctx *ksmbd_crypto_ctx_find_sha256(void); -struct ksmbd_crypto_ctx *ksmbd_crypto_ctx_find_gcm(void); -struct ksmbd_crypto_ctx *ksmbd_crypto_ctx_find_ccm(void); -void ksmbd_crypto_destroy(void); -int ksmbd_crypto_create(void); - -#endif /* __CRYPTO_CTX_H__ */ diff --git a/fs/ksmbd/glob.h b/fs/ksmbd/glob.h deleted file mode 100644 index 5b8f3e0ebdb36..0000000000000 --- a/fs/ksmbd/glob.h +++ /dev/null @@ -1,49 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0-or-later */ -/* - * Copyright (C) 2016 Namjae Jeon - * Copyright (C) 2018 Samsung Electronics Co., Ltd. - */ - -#ifndef __KSMBD_GLOB_H -#define __KSMBD_GLOB_H - -#include - -#include "unicode.h" -#include "vfs_cache.h" - -#define KSMBD_VERSION "3.4.2" - -extern int ksmbd_debug_types; - -#define KSMBD_DEBUG_SMB BIT(0) -#define KSMBD_DEBUG_AUTH BIT(1) -#define KSMBD_DEBUG_VFS BIT(2) -#define KSMBD_DEBUG_OPLOCK BIT(3) -#define KSMBD_DEBUG_IPC BIT(4) -#define KSMBD_DEBUG_CONN BIT(5) -#define KSMBD_DEBUG_RDMA BIT(6) -#define KSMBD_DEBUG_ALL (KSMBD_DEBUG_SMB | KSMBD_DEBUG_AUTH | \ - KSMBD_DEBUG_VFS | KSMBD_DEBUG_OPLOCK | \ - KSMBD_DEBUG_IPC | KSMBD_DEBUG_CONN | \ - KSMBD_DEBUG_RDMA) - -#ifdef pr_fmt -#undef pr_fmt -#endif - -#ifdef SUBMOD_NAME -#define pr_fmt(fmt) "ksmbd: " SUBMOD_NAME ": " fmt -#else -#define pr_fmt(fmt) "ksmbd: " fmt -#endif - -#define ksmbd_debug(type, fmt, ...) \ - do { \ - if (ksmbd_debug_types & KSMBD_DEBUG_##type) \ - pr_info(fmt, ##__VA_ARGS__); \ - } while (0) - -#define UNICODE_LEN(x) ((x) * 2) - -#endif /* __KSMBD_GLOB_H */ diff --git a/fs/ksmbd/ksmbd_netlink.h b/fs/ksmbd/ksmbd_netlink.h deleted file mode 100644 index ce866ff159bfe..0000000000000 --- a/fs/ksmbd/ksmbd_netlink.h +++ /dev/null @@ -1,412 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0-or-later */ -/* - * Copyright (C) 2018 Samsung Electronics Co., Ltd. - * - * linux-ksmbd-devel@lists.sourceforge.net - */ - -#ifndef _LINUX_KSMBD_SERVER_H -#define _LINUX_KSMBD_SERVER_H - -#include - -/* - * This is a userspace ABI to communicate data between ksmbd and user IPC - * daemon using netlink. This is added to track and cache user account DB - * and share configuration info from userspace. - * - * - KSMBD_EVENT_HEARTBEAT_REQUEST(ksmbd_heartbeat) - * This event is to check whether user IPC daemon is alive. If user IPC - * daemon is dead, ksmbd keep existing connection till disconnecting and - * new connection will be denied. - * - * - KSMBD_EVENT_STARTING_UP(ksmbd_startup_request) - * This event is to receive the information that initializes the ksmbd - * server from the user IPC daemon and to start the server. The global - * section parameters are given from smb.conf as initialization - * information. - * - * - KSMBD_EVENT_SHUTTING_DOWN(ksmbd_shutdown_request) - * This event is to shutdown ksmbd server. - * - * - KSMBD_EVENT_LOGIN_REQUEST/RESPONSE(ksmbd_login_request/response) - * This event is to get user account info to user IPC daemon. - * - * - KSMBD_EVENT_SHARE_CONFIG_REQUEST/RESPONSE(ksmbd_share_config_request/response) - * This event is to get net share configuration info. - * - * - KSMBD_EVENT_TREE_CONNECT_REQUEST/RESPONSE(ksmbd_tree_connect_request/response) - * This event is to get session and tree connect info. - * - * - KSMBD_EVENT_TREE_DISCONNECT_REQUEST(ksmbd_tree_disconnect_request) - * This event is to send tree disconnect info to user IPC daemon. - * - * - KSMBD_EVENT_LOGOUT_REQUEST(ksmbd_logout_request) - * This event is to send logout request to user IPC daemon. - * - * - KSMBD_EVENT_RPC_REQUEST/RESPONSE(ksmbd_rpc_command) - * This event is to make DCE/RPC request like srvsvc, wkssvc, lsarpc, - * samr to be processed in userspace. - * - * - KSMBD_EVENT_SPNEGO_AUTHEN_REQUEST/RESPONSE(ksmbd_spnego_authen_request/response) - * This event is to make kerberos authentication to be processed in - * userspace. - */ - -#define KSMBD_GENL_NAME "SMBD_GENL" -#define KSMBD_GENL_VERSION 0x01 - -#define KSMBD_REQ_MAX_ACCOUNT_NAME_SZ 48 -#define KSMBD_REQ_MAX_HASH_SZ 18 -#define KSMBD_REQ_MAX_SHARE_NAME 64 - -/* - * IPC heartbeat frame to check whether user IPC daemon is alive. - */ -struct ksmbd_heartbeat { - __u32 handle; -}; - -/* - * Global config flags. - */ -#define KSMBD_GLOBAL_FLAG_INVALID (0) -#define KSMBD_GLOBAL_FLAG_SMB2_LEASES BIT(0) -#define KSMBD_GLOBAL_FLAG_SMB2_ENCRYPTION BIT(1) -#define KSMBD_GLOBAL_FLAG_SMB3_MULTICHANNEL BIT(2) - -/* - * IPC request for ksmbd server startup - */ -struct ksmbd_startup_request { - __u32 flags; /* Flags for global config */ - __s32 signing; /* Signing enabled */ - __s8 min_prot[16]; /* The minimum SMB protocol version */ - __s8 max_prot[16]; /* The maximum SMB protocol version */ - __s8 netbios_name[16]; - __s8 work_group[64]; /* Workgroup */ - __s8 server_string[64]; /* Server string */ - __u16 tcp_port; /* tcp port */ - __u16 ipc_timeout; /* - * specifies the number of seconds - * server will wait for the userspace to - * reply to heartbeat frames. - */ - __u32 deadtime; /* Number of minutes of inactivity */ - __u32 file_max; /* Limits the maximum number of open files */ - __u32 smb2_max_write; /* MAX write size */ - __u32 smb2_max_read; /* MAX read size */ - __u32 smb2_max_trans; /* MAX trans size */ - __u32 share_fake_fscaps; /* - * Support some special application that - * makes QFSINFO calls to check whether - * we set the SPARSE_FILES bit (0x40). - */ - __u32 sub_auth[3]; /* Subauth value for Security ID */ - __u32 smb2_max_credits; /* MAX credits */ - __u32 smbd_max_io_size; /* smbd read write size */ - __u32 max_connections; /* Number of maximum simultaneous connections */ - __u32 reserved[126]; /* Reserved room */ - __u32 ifc_list_sz; /* interfaces list size */ - __s8 ____payload[]; -}; - -#define KSMBD_STARTUP_CONFIG_INTERFACES(s) ((s)->____payload) - -/* - * IPC request to shutdown ksmbd server. - */ -struct ksmbd_shutdown_request { - __s32 reserved[16]; -}; - -/* - * IPC user login request. - */ -struct ksmbd_login_request { - __u32 handle; - __s8 account[KSMBD_REQ_MAX_ACCOUNT_NAME_SZ]; /* user account name */ - __u32 reserved[16]; /* Reserved room */ -}; - -/* - * IPC user login response. - */ -struct ksmbd_login_response { - __u32 handle; - __u32 gid; /* group id */ - __u32 uid; /* user id */ - __s8 account[KSMBD_REQ_MAX_ACCOUNT_NAME_SZ]; /* user account name */ - __u16 status; - __u16 hash_sz; /* hash size */ - __s8 hash[KSMBD_REQ_MAX_HASH_SZ]; /* password hash */ - __u32 reserved[16]; /* Reserved room */ -}; - -/* - * IPC request to fetch net share config. - */ -struct ksmbd_share_config_request { - __u32 handle; - __s8 share_name[KSMBD_REQ_MAX_SHARE_NAME]; /* share name */ - __u32 reserved[16]; /* Reserved room */ -}; - -/* - * IPC response to the net share config request. - */ -struct ksmbd_share_config_response { - __u32 handle; - __u32 flags; - __u16 create_mask; - __u16 directory_mask; - __u16 force_create_mode; - __u16 force_directory_mode; - __u16 force_uid; - __u16 force_gid; - __s8 share_name[KSMBD_REQ_MAX_SHARE_NAME]; - __u32 reserved[112]; /* Reserved room */ - __u32 veto_list_sz; - __s8 ____payload[]; -}; - -#define KSMBD_SHARE_CONFIG_VETO_LIST(s) ((s)->____payload) - -static inline char * -ksmbd_share_config_path(struct ksmbd_share_config_response *sc) -{ - char *p = sc->____payload; - - if (sc->veto_list_sz) - p += sc->veto_list_sz + 1; - - return p; -} - -/* - * IPC request for tree connection. This request include session and tree - * connect info from client. - */ -struct ksmbd_tree_connect_request { - __u32 handle; - __u16 account_flags; - __u16 flags; - __u64 session_id; - __u64 connect_id; - __s8 account[KSMBD_REQ_MAX_ACCOUNT_NAME_SZ]; - __s8 share[KSMBD_REQ_MAX_SHARE_NAME]; - __s8 peer_addr[64]; - __u32 reserved[16]; /* Reserved room */ -}; - -/* - * IPC Response structure for tree connection. - */ -struct ksmbd_tree_connect_response { - __u32 handle; - __u16 status; - __u16 connection_flags; - __u32 reserved[16]; /* Reserved room */ -}; - -/* - * IPC Request struture to disconnect tree connection. - */ -struct ksmbd_tree_disconnect_request { - __u64 session_id; /* session id */ - __u64 connect_id; /* tree connection id */ - __u32 reserved[16]; /* Reserved room */ -}; - -/* - * IPC Response structure to logout user account. - */ -struct ksmbd_logout_request { - __s8 account[KSMBD_REQ_MAX_ACCOUNT_NAME_SZ]; /* user account name */ - __u32 account_flags; - __u32 reserved[16]; /* Reserved room */ -}; - -/* - * RPC command structure to send rpc request like srvsvc or wkssvc to - * IPC user daemon. - */ -struct ksmbd_rpc_command { - __u32 handle; - __u32 flags; - __u32 payload_sz; - __u8 payload[]; -}; - -/* - * IPC Request Kerberos authentication - */ -struct ksmbd_spnego_authen_request { - __u32 handle; - __u16 spnego_blob_len; /* the length of spnego_blob */ - __u8 spnego_blob[]; /* - * the GSS token from SecurityBuffer of - * SMB2 SESSION SETUP request - */ -}; - -/* - * Response data which includes the GSS token and the session key generated by - * user daemon. - */ -struct ksmbd_spnego_authen_response { - __u32 handle; - struct ksmbd_login_response login_response; /* - * the login response with - * a user identified by the - * GSS token from a client - */ - __u16 session_key_len; /* the length of the session key */ - __u16 spnego_blob_len; /* - * the length of the GSS token which will be - * stored in SecurityBuffer of SMB2 SESSION - * SETUP response - */ - __u8 payload[]; /* session key + AP_REP */ -}; - -/* - * This also used as NETLINK attribute type value. - * - * NOTE: - * Response message type value should be equal to - * request message type value + 1. - */ -enum ksmbd_event { - KSMBD_EVENT_UNSPEC = 0, - KSMBD_EVENT_HEARTBEAT_REQUEST, - - KSMBD_EVENT_STARTING_UP, - KSMBD_EVENT_SHUTTING_DOWN, - - KSMBD_EVENT_LOGIN_REQUEST, - KSMBD_EVENT_LOGIN_RESPONSE = 5, - - KSMBD_EVENT_SHARE_CONFIG_REQUEST, - KSMBD_EVENT_SHARE_CONFIG_RESPONSE, - - KSMBD_EVENT_TREE_CONNECT_REQUEST, - KSMBD_EVENT_TREE_CONNECT_RESPONSE, - - KSMBD_EVENT_TREE_DISCONNECT_REQUEST = 10, - - KSMBD_EVENT_LOGOUT_REQUEST, - - KSMBD_EVENT_RPC_REQUEST, - KSMBD_EVENT_RPC_RESPONSE, - - KSMBD_EVENT_SPNEGO_AUTHEN_REQUEST, - KSMBD_EVENT_SPNEGO_AUTHEN_RESPONSE = 15, - - KSMBD_EVENT_MAX -}; - -/* - * Enumeration for IPC tree connect status. - */ -enum KSMBD_TREE_CONN_STATUS { - KSMBD_TREE_CONN_STATUS_OK = 0, - KSMBD_TREE_CONN_STATUS_NOMEM, - KSMBD_TREE_CONN_STATUS_NO_SHARE, - KSMBD_TREE_CONN_STATUS_NO_USER, - KSMBD_TREE_CONN_STATUS_INVALID_USER, - KSMBD_TREE_CONN_STATUS_HOST_DENIED = 5, - KSMBD_TREE_CONN_STATUS_CONN_EXIST, - KSMBD_TREE_CONN_STATUS_TOO_MANY_CONNS, - KSMBD_TREE_CONN_STATUS_TOO_MANY_SESSIONS, - KSMBD_TREE_CONN_STATUS_ERROR, -}; - -/* - * User config flags. - */ -#define KSMBD_USER_FLAG_INVALID (0) -#define KSMBD_USER_FLAG_OK BIT(0) -#define KSMBD_USER_FLAG_BAD_PASSWORD BIT(1) -#define KSMBD_USER_FLAG_BAD_UID BIT(2) -#define KSMBD_USER_FLAG_BAD_USER BIT(3) -#define KSMBD_USER_FLAG_GUEST_ACCOUNT BIT(4) -#define KSMBD_USER_FLAG_DELAY_SESSION BIT(5) - -/* - * Share config flags. - */ -#define KSMBD_SHARE_FLAG_INVALID (0) -#define KSMBD_SHARE_FLAG_AVAILABLE BIT(0) -#define KSMBD_SHARE_FLAG_BROWSEABLE BIT(1) -#define KSMBD_SHARE_FLAG_WRITEABLE BIT(2) -#define KSMBD_SHARE_FLAG_READONLY BIT(3) -#define KSMBD_SHARE_FLAG_GUEST_OK BIT(4) -#define KSMBD_SHARE_FLAG_GUEST_ONLY BIT(5) -#define KSMBD_SHARE_FLAG_STORE_DOS_ATTRS BIT(6) -#define KSMBD_SHARE_FLAG_OPLOCKS BIT(7) -#define KSMBD_SHARE_FLAG_PIPE BIT(8) -#define KSMBD_SHARE_FLAG_HIDE_DOT_FILES BIT(9) -#define KSMBD_SHARE_FLAG_INHERIT_OWNER BIT(10) -#define KSMBD_SHARE_FLAG_STREAMS BIT(11) -#define KSMBD_SHARE_FLAG_FOLLOW_SYMLINKS BIT(12) -#define KSMBD_SHARE_FLAG_ACL_XATTR BIT(13) -#define KSMBD_SHARE_FLAG_UPDATE BIT(14) - -/* - * Tree connect request flags. - */ -#define KSMBD_TREE_CONN_FLAG_REQUEST_SMB1 (0) -#define KSMBD_TREE_CONN_FLAG_REQUEST_IPV6 BIT(0) -#define KSMBD_TREE_CONN_FLAG_REQUEST_SMB2 BIT(1) - -/* - * Tree connect flags. - */ -#define KSMBD_TREE_CONN_FLAG_GUEST_ACCOUNT BIT(0) -#define KSMBD_TREE_CONN_FLAG_READ_ONLY BIT(1) -#define KSMBD_TREE_CONN_FLAG_WRITABLE BIT(2) -#define KSMBD_TREE_CONN_FLAG_ADMIN_ACCOUNT BIT(3) -#define KSMBD_TREE_CONN_FLAG_UPDATE BIT(4) - -/* - * RPC over IPC. - */ -#define KSMBD_RPC_METHOD_RETURN BIT(0) -#define KSMBD_RPC_SRVSVC_METHOD_INVOKE BIT(1) -#define KSMBD_RPC_SRVSVC_METHOD_RETURN (KSMBD_RPC_SRVSVC_METHOD_INVOKE | KSMBD_RPC_METHOD_RETURN) -#define KSMBD_RPC_WKSSVC_METHOD_INVOKE BIT(2) -#define KSMBD_RPC_WKSSVC_METHOD_RETURN (KSMBD_RPC_WKSSVC_METHOD_INVOKE | KSMBD_RPC_METHOD_RETURN) -#define KSMBD_RPC_IOCTL_METHOD (BIT(3) | KSMBD_RPC_METHOD_RETURN) -#define KSMBD_RPC_OPEN_METHOD BIT(4) -#define KSMBD_RPC_WRITE_METHOD BIT(5) -#define KSMBD_RPC_READ_METHOD (BIT(6) | KSMBD_RPC_METHOD_RETURN) -#define KSMBD_RPC_CLOSE_METHOD BIT(7) -#define KSMBD_RPC_RAP_METHOD (BIT(8) | KSMBD_RPC_METHOD_RETURN) -#define KSMBD_RPC_RESTRICTED_CONTEXT BIT(9) -#define KSMBD_RPC_SAMR_METHOD_INVOKE BIT(10) -#define KSMBD_RPC_SAMR_METHOD_RETURN (KSMBD_RPC_SAMR_METHOD_INVOKE | KSMBD_RPC_METHOD_RETURN) -#define KSMBD_RPC_LSARPC_METHOD_INVOKE BIT(11) -#define KSMBD_RPC_LSARPC_METHOD_RETURN (KSMBD_RPC_LSARPC_METHOD_INVOKE | KSMBD_RPC_METHOD_RETURN) - -/* - * RPC status definitions. - */ -#define KSMBD_RPC_OK 0 -#define KSMBD_RPC_EBAD_FUNC 0x00000001 -#define KSMBD_RPC_EACCESS_DENIED 0x00000005 -#define KSMBD_RPC_EBAD_FID 0x00000006 -#define KSMBD_RPC_ENOMEM 0x00000008 -#define KSMBD_RPC_EBAD_DATA 0x0000000D -#define KSMBD_RPC_ENOTIMPLEMENTED 0x00000040 -#define KSMBD_RPC_EINVALID_PARAMETER 0x00000057 -#define KSMBD_RPC_EMORE_DATA 0x000000EA -#define KSMBD_RPC_EINVALID_LEVEL 0x0000007C -#define KSMBD_RPC_SOME_NOT_MAPPED 0x00000107 - -#define KSMBD_CONFIG_OPT_DISABLED 0 -#define KSMBD_CONFIG_OPT_ENABLED 1 -#define KSMBD_CONFIG_OPT_AUTO 2 -#define KSMBD_CONFIG_OPT_MANDATORY 3 - -#endif /* _LINUX_KSMBD_SERVER_H */ diff --git a/fs/ksmbd/ksmbd_spnego_negtokeninit.asn1 b/fs/ksmbd/ksmbd_spnego_negtokeninit.asn1 deleted file mode 100644 index 0065f191b54b7..0000000000000 --- a/fs/ksmbd/ksmbd_spnego_negtokeninit.asn1 +++ /dev/null @@ -1,31 +0,0 @@ -GSSAPI ::= - [APPLICATION 0] IMPLICIT SEQUENCE { - thisMech - OBJECT IDENTIFIER ({ksmbd_gssapi_this_mech}), - negotiationToken - NegotiationToken - } - -MechType ::= OBJECT IDENTIFIER ({ksmbd_neg_token_init_mech_type}) - -MechTypeList ::= SEQUENCE OF MechType - -NegTokenInit ::= - SEQUENCE { - mechTypes - [0] MechTypeList, - reqFlags - [1] BIT STRING OPTIONAL, - mechToken - [2] OCTET STRING OPTIONAL ({ksmbd_neg_token_init_mech_token}), - mechListMIC - [3] OCTET STRING OPTIONAL - } - -NegotiationToken ::= - CHOICE { - negTokenInit - [0] NegTokenInit, - negTokenTarg - [1] ANY - } diff --git a/fs/ksmbd/ksmbd_spnego_negtokentarg.asn1 b/fs/ksmbd/ksmbd_spnego_negtokentarg.asn1 deleted file mode 100644 index 1151933e7b9c5..0000000000000 --- a/fs/ksmbd/ksmbd_spnego_negtokentarg.asn1 +++ /dev/null @@ -1,19 +0,0 @@ -GSSAPI ::= - CHOICE { - negTokenInit - [0] ANY, - negTokenTarg - [1] NegTokenTarg - } - -NegTokenTarg ::= - SEQUENCE { - negResult - [0] ENUMERATED OPTIONAL, - supportedMech - [1] OBJECT IDENTIFIER OPTIONAL, - responseToken - [2] OCTET STRING OPTIONAL ({ksmbd_neg_token_targ_resp_token}), - mechListMIC - [3] OCTET STRING OPTIONAL - } diff --git a/fs/ksmbd/ksmbd_work.c b/fs/ksmbd/ksmbd_work.c deleted file mode 100644 index 14b9caebf7a4f..0000000000000 --- a/fs/ksmbd/ksmbd_work.c +++ /dev/null @@ -1,79 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-or-later -/* - * Copyright (C) 2019 Samsung Electronics Co., Ltd. - */ - -#include -#include -#include -#include - -#include "server.h" -#include "connection.h" -#include "ksmbd_work.h" -#include "mgmt/ksmbd_ida.h" - -static struct kmem_cache *work_cache; -static struct workqueue_struct *ksmbd_wq; - -struct ksmbd_work *ksmbd_alloc_work_struct(void) -{ - struct ksmbd_work *work = kmem_cache_zalloc(work_cache, GFP_KERNEL); - - if (work) { - work->compound_fid = KSMBD_NO_FID; - work->compound_pfid = KSMBD_NO_FID; - INIT_LIST_HEAD(&work->request_entry); - INIT_LIST_HEAD(&work->async_request_entry); - INIT_LIST_HEAD(&work->fp_entry); - INIT_LIST_HEAD(&work->interim_entry); - } - return work; -} - -void ksmbd_free_work_struct(struct ksmbd_work *work) -{ - WARN_ON(work->saved_cred != NULL); - - kvfree(work->response_buf); - kvfree(work->aux_payload_buf); - kfree(work->tr_buf); - kvfree(work->request_buf); - if (work->async_id) - ksmbd_release_id(&work->conn->async_ida, work->async_id); - kmem_cache_free(work_cache, work); -} - -void ksmbd_work_pool_destroy(void) -{ - kmem_cache_destroy(work_cache); -} - -int ksmbd_work_pool_init(void) -{ - work_cache = kmem_cache_create("ksmbd_work_cache", - sizeof(struct ksmbd_work), 0, - SLAB_HWCACHE_ALIGN, NULL); - if (!work_cache) - return -ENOMEM; - return 0; -} - -int ksmbd_workqueue_init(void) -{ - ksmbd_wq = alloc_workqueue("ksmbd-io", 0, 0); - if (!ksmbd_wq) - return -ENOMEM; - return 0; -} - -void ksmbd_workqueue_destroy(void) -{ - destroy_workqueue(ksmbd_wq); - ksmbd_wq = NULL; -} - -bool ksmbd_queue_work(struct ksmbd_work *work) -{ - return queue_work(ksmbd_wq, &work->work); -} diff --git a/fs/ksmbd/ksmbd_work.h b/fs/ksmbd/ksmbd_work.h deleted file mode 100644 index 5ece58e40c979..0000000000000 --- a/fs/ksmbd/ksmbd_work.h +++ /dev/null @@ -1,117 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0-or-later */ -/* - * Copyright (C) 2019 Samsung Electronics Co., Ltd. - */ - -#ifndef __KSMBD_WORK_H__ -#define __KSMBD_WORK_H__ - -#include -#include - -struct ksmbd_conn; -struct ksmbd_session; -struct ksmbd_tree_connect; - -enum { - KSMBD_WORK_ACTIVE = 0, - KSMBD_WORK_CANCELLED, - KSMBD_WORK_CLOSED, -}; - -/* one of these for every pending CIFS request at the connection */ -struct ksmbd_work { - /* Server corresponding to this mid */ - struct ksmbd_conn *conn; - struct ksmbd_session *sess; - struct ksmbd_tree_connect *tcon; - - /* Pointer to received SMB header */ - void *request_buf; - /* Response buffer */ - void *response_buf; - - /* Read data buffer */ - void *aux_payload_buf; - - /* Next cmd hdr in compound req buf*/ - int next_smb2_rcv_hdr_off; - /* Next cmd hdr in compound rsp buf*/ - int next_smb2_rsp_hdr_off; - - /* - * Current Local FID assigned compound response if SMB2 CREATE - * command is present in compound request - */ - u64 compound_fid; - u64 compound_pfid; - u64 compound_sid; - - const struct cred *saved_cred; - - /* Number of granted credits */ - unsigned int credits_granted; - - /* response smb header size */ - unsigned int resp_hdr_sz; - unsigned int response_sz; - /* Read data count */ - unsigned int aux_payload_sz; - - void *tr_buf; - - unsigned char state; - /* Multiple responses for one request e.g. SMB ECHO */ - bool multiRsp:1; - /* No response for cancelled request */ - bool send_no_response:1; - /* Request is encrypted */ - bool encrypted:1; - /* Is this SYNC or ASYNC ksmbd_work */ - bool syncronous:1; - bool need_invalidate_rkey:1; - - unsigned int remote_key; - /* cancel works */ - int async_id; - void **cancel_argv; - void (*cancel_fn)(void **argv); - - struct work_struct work; - /* List head at conn->requests */ - struct list_head request_entry; - /* List head at conn->async_requests */ - struct list_head async_request_entry; - struct list_head fp_entry; - struct list_head interim_entry; -}; - -/** - * ksmbd_resp_buf_next - Get next buffer on compound response. - * @work: smb work containing response buffer - */ -static inline void *ksmbd_resp_buf_next(struct ksmbd_work *work) -{ - return work->response_buf + work->next_smb2_rsp_hdr_off + 4; -} - -/** - * ksmbd_req_buf_next - Get next buffer on compound request. - * @work: smb work containing response buffer - */ -static inline void *ksmbd_req_buf_next(struct ksmbd_work *work) -{ - return work->request_buf + work->next_smb2_rcv_hdr_off + 4; -} - -struct ksmbd_work *ksmbd_alloc_work_struct(void); -void ksmbd_free_work_struct(struct ksmbd_work *work); - -void ksmbd_work_pool_destroy(void); -int ksmbd_work_pool_init(void); - -int ksmbd_workqueue_init(void); -void ksmbd_workqueue_destroy(void); -bool ksmbd_queue_work(struct ksmbd_work *work); - -#endif /* __KSMBD_WORK_H__ */ diff --git a/fs/ksmbd/mgmt/ksmbd_ida.c b/fs/ksmbd/mgmt/ksmbd_ida.c deleted file mode 100644 index 54194d959a5ef..0000000000000 --- a/fs/ksmbd/mgmt/ksmbd_ida.c +++ /dev/null @@ -1,46 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-or-later -/* - * Copyright (C) 2018 Samsung Electronics Co., Ltd. - */ - -#include "ksmbd_ida.h" - -static inline int __acquire_id(struct ida *ida, int from, int to) -{ - return ida_simple_get(ida, from, to, GFP_KERNEL); -} - -int ksmbd_acquire_smb2_tid(struct ida *ida) -{ - int id; - - id = __acquire_id(ida, 1, 0xFFFFFFFF); - - return id; -} - -int ksmbd_acquire_smb2_uid(struct ida *ida) -{ - int id; - - id = __acquire_id(ida, 1, 0); - if (id == 0xFFFE) - id = __acquire_id(ida, 1, 0); - - return id; -} - -int ksmbd_acquire_async_msg_id(struct ida *ida) -{ - return __acquire_id(ida, 1, 0); -} - -int ksmbd_acquire_id(struct ida *ida) -{ - return __acquire_id(ida, 0, 0); -} - -void ksmbd_release_id(struct ida *ida, int id) -{ - ida_simple_remove(ida, id); -} diff --git a/fs/ksmbd/mgmt/ksmbd_ida.h b/fs/ksmbd/mgmt/ksmbd_ida.h deleted file mode 100644 index 2bc07b16cfde9..0000000000000 --- a/fs/ksmbd/mgmt/ksmbd_ida.h +++ /dev/null @@ -1,34 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0-or-later */ -/* - * Copyright (C) 2018 Samsung Electronics Co., Ltd. - */ - -#ifndef __KSMBD_IDA_MANAGEMENT_H__ -#define __KSMBD_IDA_MANAGEMENT_H__ - -#include -#include - -/* - * 2.2.1.6.7 TID Generation - * The value 0xFFFF MUST NOT be used as a valid TID. All other - * possible values for TID, including zero (0x0000), are valid. - * The value 0xFFFF is used to specify all TIDs or no TID, - * depending upon the context in which it is used. - */ -int ksmbd_acquire_smb2_tid(struct ida *ida); - -/* - * 2.2.1.6.8 UID Generation - * The value 0xFFFE was declared reserved in the LAN Manager 1.0 - * documentation, so a value of 0xFFFE SHOULD NOT be used as a - * valid UID.<21> All other possible values for a UID, excluding - * zero (0x0000), are valid. - */ -int ksmbd_acquire_smb2_uid(struct ida *ida); -int ksmbd_acquire_async_msg_id(struct ida *ida); - -int ksmbd_acquire_id(struct ida *ida); - -void ksmbd_release_id(struct ida *ida, int id); -#endif /* __KSMBD_IDA_MANAGEMENT_H__ */ diff --git a/fs/ksmbd/mgmt/share_config.c b/fs/ksmbd/mgmt/share_config.c deleted file mode 100644 index 328a412259dc1..0000000000000 --- a/fs/ksmbd/mgmt/share_config.c +++ /dev/null @@ -1,234 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-or-later -/* - * Copyright (C) 2018 Samsung Electronics Co., Ltd. - */ - -#include -#include -#include -#include -#include -#include -#include -#include - -#include "share_config.h" -#include "user_config.h" -#include "user_session.h" -#include "../transport_ipc.h" -#include "../misc.h" - -#define SHARE_HASH_BITS 3 -static DEFINE_HASHTABLE(shares_table, SHARE_HASH_BITS); -static DECLARE_RWSEM(shares_table_lock); - -struct ksmbd_veto_pattern { - char *pattern; - struct list_head list; -}; - -static unsigned int share_name_hash(const char *name) -{ - return jhash(name, strlen(name), 0); -} - -static void kill_share(struct ksmbd_share_config *share) -{ - while (!list_empty(&share->veto_list)) { - struct ksmbd_veto_pattern *p; - - p = list_entry(share->veto_list.next, - struct ksmbd_veto_pattern, - list); - list_del(&p->list); - kfree(p->pattern); - kfree(p); - } - - if (share->path) - path_put(&share->vfs_path); - kfree(share->name); - kfree(share->path); - kfree(share); -} - -void ksmbd_share_config_del(struct ksmbd_share_config *share) -{ - down_write(&shares_table_lock); - hash_del(&share->hlist); - up_write(&shares_table_lock); -} - -void __ksmbd_share_config_put(struct ksmbd_share_config *share) -{ - ksmbd_share_config_del(share); - kill_share(share); -} - -static struct ksmbd_share_config * -__get_share_config(struct ksmbd_share_config *share) -{ - if (!atomic_inc_not_zero(&share->refcount)) - return NULL; - return share; -} - -static struct ksmbd_share_config *__share_lookup(const char *name) -{ - struct ksmbd_share_config *share; - unsigned int key = share_name_hash(name); - - hash_for_each_possible(shares_table, share, hlist, key) { - if (!strcmp(name, share->name)) - return share; - } - return NULL; -} - -static int parse_veto_list(struct ksmbd_share_config *share, - char *veto_list, - int veto_list_sz) -{ - int sz = 0; - - if (!veto_list_sz) - return 0; - - while (veto_list_sz > 0) { - struct ksmbd_veto_pattern *p; - - sz = strlen(veto_list); - if (!sz) - break; - - p = kzalloc(sizeof(struct ksmbd_veto_pattern), GFP_KERNEL); - if (!p) - return -ENOMEM; - - p->pattern = kstrdup(veto_list, GFP_KERNEL); - if (!p->pattern) { - kfree(p); - return -ENOMEM; - } - - list_add(&p->list, &share->veto_list); - - veto_list += sz + 1; - veto_list_sz -= (sz + 1); - } - - return 0; -} - -static struct ksmbd_share_config *share_config_request(struct unicode_map *um, - const char *name) -{ - struct ksmbd_share_config_response *resp; - struct ksmbd_share_config *share = NULL; - struct ksmbd_share_config *lookup; - int ret; - - resp = ksmbd_ipc_share_config_request(name); - if (!resp) - return NULL; - - if (resp->flags == KSMBD_SHARE_FLAG_INVALID) - goto out; - - if (*resp->share_name) { - char *cf_resp_name; - bool equal; - - cf_resp_name = ksmbd_casefold_sharename(um, resp->share_name); - if (IS_ERR(cf_resp_name)) - goto out; - equal = !strcmp(cf_resp_name, name); - kfree(cf_resp_name); - if (!equal) - goto out; - } - - share = kzalloc(sizeof(struct ksmbd_share_config), GFP_KERNEL); - if (!share) - goto out; - - share->flags = resp->flags; - atomic_set(&share->refcount, 1); - INIT_LIST_HEAD(&share->veto_list); - share->name = kstrdup(name, GFP_KERNEL); - - if (!test_share_config_flag(share, KSMBD_SHARE_FLAG_PIPE)) { - share->path = kstrdup(ksmbd_share_config_path(resp), - GFP_KERNEL); - if (share->path) - share->path_sz = strlen(share->path); - share->create_mask = resp->create_mask; - share->directory_mask = resp->directory_mask; - share->force_create_mode = resp->force_create_mode; - share->force_directory_mode = resp->force_directory_mode; - share->force_uid = resp->force_uid; - share->force_gid = resp->force_gid; - ret = parse_veto_list(share, - KSMBD_SHARE_CONFIG_VETO_LIST(resp), - resp->veto_list_sz); - if (!ret && share->path) { - ret = kern_path(share->path, 0, &share->vfs_path); - if (ret) { - ksmbd_debug(SMB, "failed to access '%s'\n", - share->path); - /* Avoid put_path() */ - kfree(share->path); - share->path = NULL; - } - } - if (ret || !share->name) { - kill_share(share); - share = NULL; - goto out; - } - } - - down_write(&shares_table_lock); - lookup = __share_lookup(name); - if (lookup) - lookup = __get_share_config(lookup); - if (!lookup) { - hash_add(shares_table, &share->hlist, share_name_hash(name)); - } else { - kill_share(share); - share = lookup; - } - up_write(&shares_table_lock); - -out: - kvfree(resp); - return share; -} - -struct ksmbd_share_config *ksmbd_share_config_get(struct unicode_map *um, - const char *name) -{ - struct ksmbd_share_config *share; - - down_read(&shares_table_lock); - share = __share_lookup(name); - if (share) - share = __get_share_config(share); - up_read(&shares_table_lock); - - if (share) - return share; - return share_config_request(um, name); -} - -bool ksmbd_share_veto_filename(struct ksmbd_share_config *share, - const char *filename) -{ - struct ksmbd_veto_pattern *p; - - list_for_each_entry(p, &share->veto_list, list) { - if (match_wildcard(p->pattern, filename)) - return true; - } - return false; -} diff --git a/fs/ksmbd/mgmt/share_config.h b/fs/ksmbd/mgmt/share_config.h deleted file mode 100644 index 3fd3382939421..0000000000000 --- a/fs/ksmbd/mgmt/share_config.h +++ /dev/null @@ -1,82 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0-or-later */ -/* - * Copyright (C) 2018 Samsung Electronics Co., Ltd. - */ - -#ifndef __SHARE_CONFIG_MANAGEMENT_H__ -#define __SHARE_CONFIG_MANAGEMENT_H__ - -#include -#include -#include -#include - -struct ksmbd_share_config { - char *name; - char *path; - - unsigned int path_sz; - unsigned int flags; - struct list_head veto_list; - - struct path vfs_path; - - atomic_t refcount; - struct hlist_node hlist; - unsigned short create_mask; - unsigned short directory_mask; - unsigned short force_create_mode; - unsigned short force_directory_mode; - unsigned short force_uid; - unsigned short force_gid; -}; - -#define KSMBD_SHARE_INVALID_UID ((__u16)-1) -#define KSMBD_SHARE_INVALID_GID ((__u16)-1) - -static inline int share_config_create_mode(struct ksmbd_share_config *share, - umode_t posix_mode) -{ - if (!share->force_create_mode) { - if (!posix_mode) - return share->create_mask; - else - return posix_mode & share->create_mask; - } - return share->force_create_mode & share->create_mask; -} - -static inline int share_config_directory_mode(struct ksmbd_share_config *share, - umode_t posix_mode) -{ - if (!share->force_directory_mode) { - if (!posix_mode) - return share->directory_mask; - else - return posix_mode & share->directory_mask; - } - - return share->force_directory_mode & share->directory_mask; -} - -static inline int test_share_config_flag(struct ksmbd_share_config *share, - int flag) -{ - return share->flags & flag; -} - -void ksmbd_share_config_del(struct ksmbd_share_config *share); -void __ksmbd_share_config_put(struct ksmbd_share_config *share); - -static inline void ksmbd_share_config_put(struct ksmbd_share_config *share) -{ - if (!atomic_dec_and_test(&share->refcount)) - return; - __ksmbd_share_config_put(share); -} - -struct ksmbd_share_config *ksmbd_share_config_get(struct unicode_map *um, - const char *name); -bool ksmbd_share_veto_filename(struct ksmbd_share_config *share, - const char *filename); -#endif /* __SHARE_CONFIG_MANAGEMENT_H__ */ diff --git a/fs/ksmbd/mgmt/tree_connect.c b/fs/ksmbd/mgmt/tree_connect.c deleted file mode 100644 index f07a05f376513..0000000000000 --- a/fs/ksmbd/mgmt/tree_connect.c +++ /dev/null @@ -1,147 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-or-later -/* - * Copyright (C) 2018 Samsung Electronics Co., Ltd. - */ - -#include -#include -#include - -#include "../transport_ipc.h" -#include "../connection.h" - -#include "tree_connect.h" -#include "user_config.h" -#include "share_config.h" -#include "user_session.h" - -struct ksmbd_tree_conn_status -ksmbd_tree_conn_connect(struct ksmbd_conn *conn, struct ksmbd_session *sess, - const char *share_name) -{ - struct ksmbd_tree_conn_status status = {-ENOENT, NULL}; - struct ksmbd_tree_connect_response *resp = NULL; - struct ksmbd_share_config *sc; - struct ksmbd_tree_connect *tree_conn = NULL; - struct sockaddr *peer_addr; - int ret; - - sc = ksmbd_share_config_get(conn->um, share_name); - if (!sc) - return status; - - tree_conn = kzalloc(sizeof(struct ksmbd_tree_connect), GFP_KERNEL); - if (!tree_conn) { - status.ret = -ENOMEM; - goto out_error; - } - - tree_conn->id = ksmbd_acquire_tree_conn_id(sess); - if (tree_conn->id < 0) { - status.ret = -EINVAL; - goto out_error; - } - - peer_addr = KSMBD_TCP_PEER_SOCKADDR(conn); - resp = ksmbd_ipc_tree_connect_request(sess, - sc, - tree_conn, - peer_addr); - if (!resp) { - status.ret = -EINVAL; - goto out_error; - } - - status.ret = resp->status; - if (status.ret != KSMBD_TREE_CONN_STATUS_OK) - goto out_error; - - tree_conn->flags = resp->connection_flags; - if (test_tree_conn_flag(tree_conn, KSMBD_TREE_CONN_FLAG_UPDATE)) { - struct ksmbd_share_config *new_sc; - - ksmbd_share_config_del(sc); - new_sc = ksmbd_share_config_get(conn->um, share_name); - if (!new_sc) { - pr_err("Failed to update stale share config\n"); - status.ret = -ESTALE; - goto out_error; - } - ksmbd_share_config_put(sc); - sc = new_sc; - } - - tree_conn->user = sess->user; - tree_conn->share_conf = sc; - status.tree_conn = tree_conn; - - ret = xa_err(xa_store(&sess->tree_conns, tree_conn->id, tree_conn, - GFP_KERNEL)); - if (ret) { - status.ret = -ENOMEM; - goto out_error; - } - kvfree(resp); - return status; - -out_error: - if (tree_conn) - ksmbd_release_tree_conn_id(sess, tree_conn->id); - ksmbd_share_config_put(sc); - kfree(tree_conn); - kvfree(resp); - return status; -} - -int ksmbd_tree_conn_disconnect(struct ksmbd_session *sess, - struct ksmbd_tree_connect *tree_conn) -{ - int ret; - - ret = ksmbd_ipc_tree_disconnect_request(sess->id, tree_conn->id); - ksmbd_release_tree_conn_id(sess, tree_conn->id); - xa_erase(&sess->tree_conns, tree_conn->id); - ksmbd_share_config_put(tree_conn->share_conf); - kfree(tree_conn); - return ret; -} - -struct ksmbd_tree_connect *ksmbd_tree_conn_lookup(struct ksmbd_session *sess, - unsigned int id) -{ - struct ksmbd_tree_connect *tcon; - - tcon = xa_load(&sess->tree_conns, id); - if (tcon) { - if (test_bit(TREE_CONN_EXPIRE, &tcon->status)) - tcon = NULL; - } - - return tcon; -} - -struct ksmbd_share_config *ksmbd_tree_conn_share(struct ksmbd_session *sess, - unsigned int id) -{ - struct ksmbd_tree_connect *tc; - - tc = ksmbd_tree_conn_lookup(sess, id); - if (tc) - return tc->share_conf; - return NULL; -} - -int ksmbd_tree_conn_session_logoff(struct ksmbd_session *sess) -{ - int ret = 0; - struct ksmbd_tree_connect *tc; - unsigned long id; - - if (!sess) - return -EINVAL; - - xa_for_each(&sess->tree_conns, id, tc) - ret |= ksmbd_tree_conn_disconnect(sess, tc); - xa_destroy(&sess->tree_conns); - return ret; -} diff --git a/fs/ksmbd/mgmt/tree_connect.h b/fs/ksmbd/mgmt/tree_connect.h deleted file mode 100644 index 700df36cf3e30..0000000000000 --- a/fs/ksmbd/mgmt/tree_connect.h +++ /dev/null @@ -1,61 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0-or-later */ -/* - * Copyright (C) 2018 Samsung Electronics Co., Ltd. - */ - -#ifndef __TREE_CONNECT_MANAGEMENT_H__ -#define __TREE_CONNECT_MANAGEMENT_H__ - -#include - -#include "../ksmbd_netlink.h" - -struct ksmbd_share_config; -struct ksmbd_user; -struct ksmbd_conn; - -#define TREE_CONN_EXPIRE 1 - -struct ksmbd_tree_connect { - int id; - - unsigned int flags; - struct ksmbd_share_config *share_conf; - struct ksmbd_user *user; - - struct list_head list; - - int maximal_access; - bool posix_extensions; - unsigned long status; -}; - -struct ksmbd_tree_conn_status { - unsigned int ret; - struct ksmbd_tree_connect *tree_conn; -}; - -static inline int test_tree_conn_flag(struct ksmbd_tree_connect *tree_conn, - int flag) -{ - return tree_conn->flags & flag; -} - -struct ksmbd_session; - -struct ksmbd_tree_conn_status -ksmbd_tree_conn_connect(struct ksmbd_conn *conn, struct ksmbd_session *sess, - const char *share_name); - -int ksmbd_tree_conn_disconnect(struct ksmbd_session *sess, - struct ksmbd_tree_connect *tree_conn); - -struct ksmbd_tree_connect *ksmbd_tree_conn_lookup(struct ksmbd_session *sess, - unsigned int id); - -struct ksmbd_share_config *ksmbd_tree_conn_share(struct ksmbd_session *sess, - unsigned int id); - -int ksmbd_tree_conn_session_logoff(struct ksmbd_session *sess); - -#endif /* __TREE_CONNECT_MANAGEMENT_H__ */ diff --git a/fs/ksmbd/mgmt/user_config.c b/fs/ksmbd/mgmt/user_config.c deleted file mode 100644 index 279d00feff216..0000000000000 --- a/fs/ksmbd/mgmt/user_config.c +++ /dev/null @@ -1,79 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-or-later -/* - * Copyright (C) 2018 Samsung Electronics Co., Ltd. - */ - -#include -#include - -#include "user_config.h" -#include "../transport_ipc.h" - -struct ksmbd_user *ksmbd_login_user(const char *account) -{ - struct ksmbd_login_response *resp; - struct ksmbd_user *user = NULL; - - resp = ksmbd_ipc_login_request(account); - if (!resp) - return NULL; - - if (!(resp->status & KSMBD_USER_FLAG_OK)) - goto out; - - user = ksmbd_alloc_user(resp); -out: - kvfree(resp); - return user; -} - -struct ksmbd_user *ksmbd_alloc_user(struct ksmbd_login_response *resp) -{ - struct ksmbd_user *user = NULL; - - user = kmalloc(sizeof(struct ksmbd_user), GFP_KERNEL); - if (!user) - return NULL; - - user->name = kstrdup(resp->account, GFP_KERNEL); - user->flags = resp->status; - user->gid = resp->gid; - user->uid = resp->uid; - user->passkey_sz = resp->hash_sz; - user->passkey = kmalloc(resp->hash_sz, GFP_KERNEL); - if (user->passkey) - memcpy(user->passkey, resp->hash, resp->hash_sz); - - if (!user->name || !user->passkey) { - kfree(user->name); - kfree(user->passkey); - kfree(user); - user = NULL; - } - return user; -} - -void ksmbd_free_user(struct ksmbd_user *user) -{ - ksmbd_ipc_logout_request(user->name, user->flags); - kfree(user->name); - kfree(user->passkey); - kfree(user); -} - -int ksmbd_anonymous_user(struct ksmbd_user *user) -{ - if (user->name[0] == '\0') - return 1; - return 0; -} - -bool ksmbd_compare_user(struct ksmbd_user *u1, struct ksmbd_user *u2) -{ - if (strcmp(u1->name, u2->name)) - return false; - if (memcmp(u1->passkey, u2->passkey, u1->passkey_sz)) - return false; - - return true; -} diff --git a/fs/ksmbd/mgmt/user_config.h b/fs/ksmbd/mgmt/user_config.h deleted file mode 100644 index 6a44109617f14..0000000000000 --- a/fs/ksmbd/mgmt/user_config.h +++ /dev/null @@ -1,68 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0-or-later */ -/* - * Copyright (C) 2018 Samsung Electronics Co., Ltd. - */ - -#ifndef __USER_CONFIG_MANAGEMENT_H__ -#define __USER_CONFIG_MANAGEMENT_H__ - -#include "../glob.h" - -struct ksmbd_user { - unsigned short flags; - - unsigned int uid; - unsigned int gid; - - char *name; - - size_t passkey_sz; - char *passkey; - unsigned int failed_login_count; -}; - -static inline bool user_guest(struct ksmbd_user *user) -{ - return user->flags & KSMBD_USER_FLAG_GUEST_ACCOUNT; -} - -static inline void set_user_flag(struct ksmbd_user *user, int flag) -{ - user->flags |= flag; -} - -static inline int test_user_flag(struct ksmbd_user *user, int flag) -{ - return user->flags & flag; -} - -static inline void set_user_guest(struct ksmbd_user *user) -{ -} - -static inline char *user_passkey(struct ksmbd_user *user) -{ - return user->passkey; -} - -static inline char *user_name(struct ksmbd_user *user) -{ - return user->name; -} - -static inline unsigned int user_uid(struct ksmbd_user *user) -{ - return user->uid; -} - -static inline unsigned int user_gid(struct ksmbd_user *user) -{ - return user->gid; -} - -struct ksmbd_user *ksmbd_login_user(const char *account); -struct ksmbd_user *ksmbd_alloc_user(struct ksmbd_login_response *resp); -void ksmbd_free_user(struct ksmbd_user *user); -int ksmbd_anonymous_user(struct ksmbd_user *user); -bool ksmbd_compare_user(struct ksmbd_user *u1, struct ksmbd_user *u2); -#endif /* __USER_CONFIG_MANAGEMENT_H__ */ diff --git a/fs/ksmbd/mgmt/user_session.c b/fs/ksmbd/mgmt/user_session.c deleted file mode 100644 index ea4b56d570fbb..0000000000000 --- a/fs/ksmbd/mgmt/user_session.c +++ /dev/null @@ -1,400 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-or-later -/* - * Copyright (C) 2018 Samsung Electronics Co., Ltd. - */ - -#include -#include -#include -#include - -#include "ksmbd_ida.h" -#include "user_session.h" -#include "user_config.h" -#include "tree_connect.h" -#include "../transport_ipc.h" -#include "../connection.h" -#include "../vfs_cache.h" - -static DEFINE_IDA(session_ida); - -#define SESSION_HASH_BITS 3 -static DEFINE_HASHTABLE(sessions_table, SESSION_HASH_BITS); -static DECLARE_RWSEM(sessions_table_lock); - -struct ksmbd_session_rpc { - int id; - unsigned int method; - struct list_head list; -}; - -static void free_channel_list(struct ksmbd_session *sess) -{ - struct channel *chann; - unsigned long index; - - xa_for_each(&sess->ksmbd_chann_list, index, chann) { - xa_erase(&sess->ksmbd_chann_list, index); - kfree(chann); - } - - xa_destroy(&sess->ksmbd_chann_list); -} - -static void __session_rpc_close(struct ksmbd_session *sess, - struct ksmbd_session_rpc *entry) -{ - struct ksmbd_rpc_command *resp; - - resp = ksmbd_rpc_close(sess, entry->id); - if (!resp) - pr_err("Unable to close RPC pipe %d\n", entry->id); - - kvfree(resp); - ksmbd_rpc_id_free(entry->id); - kfree(entry); -} - -static void ksmbd_session_rpc_clear_list(struct ksmbd_session *sess) -{ - struct ksmbd_session_rpc *entry; - - while (!list_empty(&sess->rpc_handle_list)) { - entry = list_entry(sess->rpc_handle_list.next, - struct ksmbd_session_rpc, - list); - - list_del(&entry->list); - __session_rpc_close(sess, entry); - } -} - -static int __rpc_method(char *rpc_name) -{ - if (!strcmp(rpc_name, "\\srvsvc") || !strcmp(rpc_name, "srvsvc")) - return KSMBD_RPC_SRVSVC_METHOD_INVOKE; - - if (!strcmp(rpc_name, "\\wkssvc") || !strcmp(rpc_name, "wkssvc")) - return KSMBD_RPC_WKSSVC_METHOD_INVOKE; - - if (!strcmp(rpc_name, "LANMAN") || !strcmp(rpc_name, "lanman")) - return KSMBD_RPC_RAP_METHOD; - - if (!strcmp(rpc_name, "\\samr") || !strcmp(rpc_name, "samr")) - return KSMBD_RPC_SAMR_METHOD_INVOKE; - - if (!strcmp(rpc_name, "\\lsarpc") || !strcmp(rpc_name, "lsarpc")) - return KSMBD_RPC_LSARPC_METHOD_INVOKE; - - pr_err("Unsupported RPC: %s\n", rpc_name); - return 0; -} - -int ksmbd_session_rpc_open(struct ksmbd_session *sess, char *rpc_name) -{ - struct ksmbd_session_rpc *entry; - struct ksmbd_rpc_command *resp; - int method; - - method = __rpc_method(rpc_name); - if (!method) - return -EINVAL; - - entry = kzalloc(sizeof(struct ksmbd_session_rpc), GFP_KERNEL); - if (!entry) - return -EINVAL; - - list_add(&entry->list, &sess->rpc_handle_list); - entry->method = method; - entry->id = ksmbd_ipc_id_alloc(); - if (entry->id < 0) - goto free_entry; - - resp = ksmbd_rpc_open(sess, entry->id); - if (!resp) - goto free_id; - - kvfree(resp); - return entry->id; -free_id: - ksmbd_rpc_id_free(entry->id); -free_entry: - list_del(&entry->list); - kfree(entry); - return -EINVAL; -} - -void ksmbd_session_rpc_close(struct ksmbd_session *sess, int id) -{ - struct ksmbd_session_rpc *entry; - - list_for_each_entry(entry, &sess->rpc_handle_list, list) { - if (entry->id == id) { - list_del(&entry->list); - __session_rpc_close(sess, entry); - break; - } - } -} - -int ksmbd_session_rpc_method(struct ksmbd_session *sess, int id) -{ - struct ksmbd_session_rpc *entry; - - list_for_each_entry(entry, &sess->rpc_handle_list, list) { - if (entry->id == id) - return entry->method; - } - return 0; -} - -void ksmbd_session_destroy(struct ksmbd_session *sess) -{ - if (!sess) - return; - - if (sess->user) - ksmbd_free_user(sess->user); - - ksmbd_tree_conn_session_logoff(sess); - ksmbd_destroy_file_table(&sess->file_table); - ksmbd_session_rpc_clear_list(sess); - free_channel_list(sess); - kfree(sess->Preauth_HashValue); - ksmbd_release_id(&session_ida, sess->id); - kfree(sess); -} - -static struct ksmbd_session *__session_lookup(unsigned long long id) -{ - struct ksmbd_session *sess; - - hash_for_each_possible(sessions_table, sess, hlist, id) { - if (id == sess->id) { - sess->last_active = jiffies; - return sess; - } - } - return NULL; -} - -static void ksmbd_expire_session(struct ksmbd_conn *conn) -{ - unsigned long id; - struct ksmbd_session *sess; - - down_write(&sessions_table_lock); - xa_for_each(&conn->sessions, id, sess) { - if (sess->state != SMB2_SESSION_VALID || - time_after(jiffies, - sess->last_active + SMB2_SESSION_TIMEOUT)) { - xa_erase(&conn->sessions, sess->id); - hash_del(&sess->hlist); - ksmbd_session_destroy(sess); - continue; - } - } - up_write(&sessions_table_lock); -} - -int ksmbd_session_register(struct ksmbd_conn *conn, - struct ksmbd_session *sess) -{ - sess->dialect = conn->dialect; - memcpy(sess->ClientGUID, conn->ClientGUID, SMB2_CLIENT_GUID_SIZE); - ksmbd_expire_session(conn); - return xa_err(xa_store(&conn->sessions, sess->id, sess, GFP_KERNEL)); -} - -static int ksmbd_chann_del(struct ksmbd_conn *conn, struct ksmbd_session *sess) -{ - struct channel *chann; - - chann = xa_erase(&sess->ksmbd_chann_list, (long)conn); - if (!chann) - return -ENOENT; - - kfree(chann); - return 0; -} - -void ksmbd_sessions_deregister(struct ksmbd_conn *conn) -{ - struct ksmbd_session *sess; - unsigned long id; - - down_write(&sessions_table_lock); - if (conn->binding) { - int bkt; - struct hlist_node *tmp; - - hash_for_each_safe(sessions_table, bkt, tmp, sess, hlist) { - if (!ksmbd_chann_del(conn, sess) && - xa_empty(&sess->ksmbd_chann_list)) { - hash_del(&sess->hlist); - ksmbd_session_destroy(sess); - } - } - } - - xa_for_each(&conn->sessions, id, sess) { - unsigned long chann_id; - struct channel *chann; - - xa_for_each(&sess->ksmbd_chann_list, chann_id, chann) { - if (chann->conn != conn) - ksmbd_conn_set_exiting(chann->conn); - } - - ksmbd_chann_del(conn, sess); - if (xa_empty(&sess->ksmbd_chann_list)) { - xa_erase(&conn->sessions, sess->id); - hash_del(&sess->hlist); - ksmbd_session_destroy(sess); - } - } - up_write(&sessions_table_lock); -} - -struct ksmbd_session *ksmbd_session_lookup(struct ksmbd_conn *conn, - unsigned long long id) -{ - struct ksmbd_session *sess; - - sess = xa_load(&conn->sessions, id); - if (sess) - sess->last_active = jiffies; - return sess; -} - -struct ksmbd_session *ksmbd_session_lookup_slowpath(unsigned long long id) -{ - struct ksmbd_session *sess; - - down_read(&sessions_table_lock); - sess = __session_lookup(id); - if (sess) - sess->last_active = jiffies; - up_read(&sessions_table_lock); - - return sess; -} - -struct ksmbd_session *ksmbd_session_lookup_all(struct ksmbd_conn *conn, - unsigned long long id) -{ - struct ksmbd_session *sess; - - sess = ksmbd_session_lookup(conn, id); - if (!sess && conn->binding) - sess = ksmbd_session_lookup_slowpath(id); - if (sess && sess->state != SMB2_SESSION_VALID) - sess = NULL; - return sess; -} - -struct preauth_session *ksmbd_preauth_session_alloc(struct ksmbd_conn *conn, - u64 sess_id) -{ - struct preauth_session *sess; - - sess = kmalloc(sizeof(struct preauth_session), GFP_KERNEL); - if (!sess) - return NULL; - - sess->id = sess_id; - memcpy(sess->Preauth_HashValue, conn->preauth_info->Preauth_HashValue, - PREAUTH_HASHVALUE_SIZE); - list_add(&sess->preauth_entry, &conn->preauth_sess_table); - - return sess; -} - -static bool ksmbd_preauth_session_id_match(struct preauth_session *sess, - unsigned long long id) -{ - return sess->id == id; -} - -struct preauth_session *ksmbd_preauth_session_lookup(struct ksmbd_conn *conn, - unsigned long long id) -{ - struct preauth_session *sess = NULL; - - list_for_each_entry(sess, &conn->preauth_sess_table, preauth_entry) { - if (ksmbd_preauth_session_id_match(sess, id)) - return sess; - } - return NULL; -} - -static int __init_smb2_session(struct ksmbd_session *sess) -{ - int id = ksmbd_acquire_smb2_uid(&session_ida); - - if (id < 0) - return -EINVAL; - sess->id = id; - return 0; -} - -static struct ksmbd_session *__session_create(int protocol) -{ - struct ksmbd_session *sess; - int ret; - - if (protocol != CIFDS_SESSION_FLAG_SMB2) - return NULL; - - sess = kzalloc(sizeof(struct ksmbd_session), GFP_KERNEL); - if (!sess) - return NULL; - - if (ksmbd_init_file_table(&sess->file_table)) - goto error; - - sess->last_active = jiffies; - sess->state = SMB2_SESSION_IN_PROGRESS; - set_session_flag(sess, protocol); - xa_init(&sess->tree_conns); - xa_init(&sess->ksmbd_chann_list); - INIT_LIST_HEAD(&sess->rpc_handle_list); - sess->sequence_number = 1; - - ret = __init_smb2_session(sess); - if (ret) - goto error; - - ida_init(&sess->tree_conn_ida); - - down_write(&sessions_table_lock); - hash_add(sessions_table, &sess->hlist, sess->id); - up_write(&sessions_table_lock); - - return sess; - -error: - ksmbd_session_destroy(sess); - return NULL; -} - -struct ksmbd_session *ksmbd_smb2_session_create(void) -{ - return __session_create(CIFDS_SESSION_FLAG_SMB2); -} - -int ksmbd_acquire_tree_conn_id(struct ksmbd_session *sess) -{ - int id = -EINVAL; - - if (test_session_flag(sess, CIFDS_SESSION_FLAG_SMB2)) - id = ksmbd_acquire_smb2_tid(&sess->tree_conn_ida); - - return id; -} - -void ksmbd_release_tree_conn_id(struct ksmbd_session *sess, int id) -{ - if (id >= 0) - ksmbd_release_id(&sess->tree_conn_ida, id); -} diff --git a/fs/ksmbd/mgmt/user_session.h b/fs/ksmbd/mgmt/user_session.h deleted file mode 100644 index 51f38e5b61abb..0000000000000 --- a/fs/ksmbd/mgmt/user_session.h +++ /dev/null @@ -1,103 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0-or-later */ -/* - * Copyright (C) 2018 Samsung Electronics Co., Ltd. - */ - -#ifndef __USER_SESSION_MANAGEMENT_H__ -#define __USER_SESSION_MANAGEMENT_H__ - -#include -#include - -#include "../smb_common.h" -#include "../ntlmssp.h" - -#define CIFDS_SESSION_FLAG_SMB2 BIT(1) - -#define PREAUTH_HASHVALUE_SIZE 64 - -struct ksmbd_file_table; - -struct channel { - __u8 smb3signingkey[SMB3_SIGN_KEY_SIZE]; - struct ksmbd_conn *conn; -}; - -struct preauth_session { - __u8 Preauth_HashValue[PREAUTH_HASHVALUE_SIZE]; - u64 id; - struct list_head preauth_entry; -}; - -struct ksmbd_session { - u64 id; - - __u16 dialect; - char ClientGUID[SMB2_CLIENT_GUID_SIZE]; - - struct ksmbd_user *user; - unsigned int sequence_number; - unsigned int flags; - - bool sign; - bool enc; - bool is_anonymous; - - int state; - __u8 *Preauth_HashValue; - - char sess_key[CIFS_KEY_SIZE]; - - struct hlist_node hlist; - struct xarray ksmbd_chann_list; - struct xarray tree_conns; - struct ida tree_conn_ida; - struct list_head rpc_handle_list; - - __u8 smb3encryptionkey[SMB3_ENC_DEC_KEY_SIZE]; - __u8 smb3decryptionkey[SMB3_ENC_DEC_KEY_SIZE]; - __u8 smb3signingkey[SMB3_SIGN_KEY_SIZE]; - - struct ksmbd_file_table file_table; - unsigned long last_active; -}; - -static inline int test_session_flag(struct ksmbd_session *sess, int bit) -{ - return sess->flags & bit; -} - -static inline void set_session_flag(struct ksmbd_session *sess, int bit) -{ - sess->flags |= bit; -} - -static inline void clear_session_flag(struct ksmbd_session *sess, int bit) -{ - sess->flags &= ~bit; -} - -struct ksmbd_session *ksmbd_smb2_session_create(void); - -void ksmbd_session_destroy(struct ksmbd_session *sess); - -struct ksmbd_session *ksmbd_session_lookup_slowpath(unsigned long long id); -struct ksmbd_session *ksmbd_session_lookup(struct ksmbd_conn *conn, - unsigned long long id); -int ksmbd_session_register(struct ksmbd_conn *conn, - struct ksmbd_session *sess); -void ksmbd_sessions_deregister(struct ksmbd_conn *conn); -struct ksmbd_session *ksmbd_session_lookup_all(struct ksmbd_conn *conn, - unsigned long long id); -struct preauth_session *ksmbd_preauth_session_alloc(struct ksmbd_conn *conn, - u64 sess_id); -struct preauth_session *ksmbd_preauth_session_lookup(struct ksmbd_conn *conn, - unsigned long long id); - -int ksmbd_acquire_tree_conn_id(struct ksmbd_session *sess); -void ksmbd_release_tree_conn_id(struct ksmbd_session *sess, int id); - -int ksmbd_session_rpc_open(struct ksmbd_session *sess, char *rpc_name); -void ksmbd_session_rpc_close(struct ksmbd_session *sess, int id); -int ksmbd_session_rpc_method(struct ksmbd_session *sess, int id); -#endif /* __USER_SESSION_MANAGEMENT_H__ */ diff --git a/fs/ksmbd/misc.c b/fs/ksmbd/misc.c deleted file mode 100644 index 9e8afaa686e3a..0000000000000 --- a/fs/ksmbd/misc.c +++ /dev/null @@ -1,381 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-or-later -/* - * Copyright (C) 2016 Namjae Jeon - * Copyright (C) 2018 Samsung Electronics Co., Ltd. - */ - -#include -#include -#include -#include - -#include "misc.h" -#include "smb_common.h" -#include "connection.h" -#include "vfs.h" - -#include "mgmt/share_config.h" - -/** - * match_pattern() - compare a string with a pattern which might include - * wildcard '*' and '?' - * TODO : implement consideration about DOS_DOT, DOS_QM and DOS_STAR - * - * @str: string to compare with a pattern - * @len: string length - * @pattern: pattern string which might include wildcard '*' and '?' - * - * Return: 0 if pattern matched with the string, otherwise non zero value - */ -int match_pattern(const char *str, size_t len, const char *pattern) -{ - const char *s = str; - const char *p = pattern; - bool star = false; - - while (*s && len) { - switch (*p) { - case '?': - s++; - len--; - p++; - break; - case '*': - star = true; - str = s; - if (!*++p) - return true; - pattern = p; - break; - default: - if (tolower(*s) == tolower(*p)) { - s++; - len--; - p++; - } else { - if (!star) - return false; - str++; - s = str; - p = pattern; - } - break; - } - } - - if (*p == '*') - ++p; - return !*p; -} - -/* - * is_char_allowed() - check for valid character - * @ch: input character to be checked - * - * Return: 1 if char is allowed, otherwise 0 - */ -static inline int is_char_allowed(char ch) -{ - /* check for control chars, wildcards etc. */ - if (!(ch & 0x80) && - (ch <= 0x1f || - ch == '?' || ch == '"' || ch == '<' || - ch == '>' || ch == '|' || ch == '*')) - return 0; - - return 1; -} - -int ksmbd_validate_filename(char *filename) -{ - while (*filename) { - char c = *filename; - - filename++; - if (!is_char_allowed(c)) { - ksmbd_debug(VFS, "File name validation failed: 0x%x\n", c); - return -ENOENT; - } - } - - return 0; -} - -static int ksmbd_validate_stream_name(char *stream_name) -{ - while (*stream_name) { - char c = *stream_name; - - stream_name++; - if (c == '/' || c == ':' || c == '\\') { - pr_err("Stream name validation failed: %c\n", c); - return -ENOENT; - } - } - - return 0; -} - -int parse_stream_name(char *filename, char **stream_name, int *s_type) -{ - char *stream_type; - char *s_name; - int rc = 0; - - s_name = filename; - filename = strsep(&s_name, ":"); - ksmbd_debug(SMB, "filename : %s, streams : %s\n", filename, s_name); - if (strchr(s_name, ':')) { - stream_type = s_name; - s_name = strsep(&stream_type, ":"); - - rc = ksmbd_validate_stream_name(s_name); - if (rc < 0) { - rc = -ENOENT; - goto out; - } - - ksmbd_debug(SMB, "stream name : %s, stream type : %s\n", s_name, - stream_type); - if (!strncasecmp("$data", stream_type, 5)) - *s_type = DATA_STREAM; - else if (!strncasecmp("$index_allocation", stream_type, 17)) - *s_type = DIR_STREAM; - else - rc = -ENOENT; - } - - *stream_name = s_name; -out: - return rc; -} - -/** - * convert_to_nt_pathname() - extract and return windows path string - * whose share directory prefix was removed from file path - * @share: ksmbd_share_config pointer - * @path: path to report - * - * Return : windows path string or error - */ - -char *convert_to_nt_pathname(struct ksmbd_share_config *share, - const struct path *path) -{ - char *pathname, *ab_pathname, *nt_pathname; - int share_path_len = share->path_sz; - - pathname = kmalloc(PATH_MAX, GFP_KERNEL); - if (!pathname) - return ERR_PTR(-EACCES); - - ab_pathname = d_path(path, pathname, PATH_MAX); - if (IS_ERR(ab_pathname)) { - nt_pathname = ERR_PTR(-EACCES); - goto free_pathname; - } - - if (strncmp(ab_pathname, share->path, share_path_len)) { - nt_pathname = ERR_PTR(-EACCES); - goto free_pathname; - } - - nt_pathname = kzalloc(strlen(&ab_pathname[share_path_len]) + 2, GFP_KERNEL); - if (!nt_pathname) { - nt_pathname = ERR_PTR(-ENOMEM); - goto free_pathname; - } - if (ab_pathname[share_path_len] == '\0') - strcpy(nt_pathname, "/"); - strcat(nt_pathname, &ab_pathname[share_path_len]); - - ksmbd_conv_path_to_windows(nt_pathname); - -free_pathname: - kfree(pathname); - return nt_pathname; -} - -int get_nlink(struct kstat *st) -{ - int nlink; - - nlink = st->nlink; - if (S_ISDIR(st->mode)) - nlink--; - - return nlink; -} - -void ksmbd_conv_path_to_unix(char *path) -{ - strreplace(path, '\\', '/'); -} - -void ksmbd_strip_last_slash(char *path) -{ - int len = strlen(path); - - while (len && path[len - 1] == '/') { - path[len - 1] = '\0'; - len--; - } -} - -void ksmbd_conv_path_to_windows(char *path) -{ - strreplace(path, '/', '\\'); -} - -char *ksmbd_casefold_sharename(struct unicode_map *um, const char *name) -{ - char *cf_name; - int cf_len; - - cf_name = kzalloc(KSMBD_REQ_MAX_SHARE_NAME, GFP_KERNEL); - if (!cf_name) - return ERR_PTR(-ENOMEM); - - if (IS_ENABLED(CONFIG_UNICODE) && um) { - const struct qstr q_name = {.name = name, .len = strlen(name)}; - - cf_len = utf8_casefold(um, &q_name, cf_name, - KSMBD_REQ_MAX_SHARE_NAME); - if (cf_len < 0) - goto out_ascii; - - return cf_name; - } - -out_ascii: - cf_len = strscpy(cf_name, name, KSMBD_REQ_MAX_SHARE_NAME); - if (cf_len < 0) { - kfree(cf_name); - return ERR_PTR(-E2BIG); - } - - for (; *cf_name; ++cf_name) - *cf_name = isascii(*cf_name) ? tolower(*cf_name) : *cf_name; - return cf_name - cf_len; -} - -/** - * ksmbd_extract_sharename() - get share name from tree connect request - * @treename: buffer containing tree name and share name - * - * Return: share name on success, otherwise error - */ -char *ksmbd_extract_sharename(struct unicode_map *um, const char *treename) -{ - const char *name = treename, *pos = strrchr(name, '\\'); - - if (pos) - name = (pos + 1); - - /* caller has to free the memory */ - return ksmbd_casefold_sharename(um, name); -} - -/** - * convert_to_unix_name() - convert windows name to unix format - * @share: ksmbd_share_config pointer - * @name: file name that is relative to share - * - * Return: converted name on success, otherwise NULL - */ -char *convert_to_unix_name(struct ksmbd_share_config *share, const char *name) -{ - int no_slash = 0, name_len, path_len; - char *new_name; - - if (name[0] == '/') - name++; - - path_len = share->path_sz; - name_len = strlen(name); - new_name = kmalloc(path_len + name_len + 2, GFP_KERNEL); - if (!new_name) - return new_name; - - memcpy(new_name, share->path, path_len); - if (new_name[path_len - 1] != '/') { - new_name[path_len] = '/'; - no_slash = 1; - } - - memcpy(new_name + path_len + no_slash, name, name_len); - path_len += name_len + no_slash; - new_name[path_len] = 0x00; - return new_name; -} - -char *ksmbd_convert_dir_info_name(struct ksmbd_dir_info *d_info, - const struct nls_table *local_nls, - int *conv_len) -{ - char *conv; - int sz = min(4 * d_info->name_len, PATH_MAX); - - if (!sz) - return NULL; - - conv = kmalloc(sz, GFP_KERNEL); - if (!conv) - return NULL; - - /* XXX */ - *conv_len = smbConvertToUTF16((__le16 *)conv, d_info->name, - d_info->name_len, local_nls, 0); - *conv_len *= 2; - - /* We allocate buffer twice bigger than needed. */ - conv[*conv_len] = 0x00; - conv[*conv_len + 1] = 0x00; - return conv; -} - -/* - * Convert the NT UTC (based 1601-01-01, in hundred nanosecond units) - * into Unix UTC (based 1970-01-01, in seconds). - */ -struct timespec64 ksmbd_NTtimeToUnix(__le64 ntutc) -{ - struct timespec64 ts; - - /* Subtract the NTFS time offset, then convert to 1s intervals. */ - s64 t = le64_to_cpu(ntutc) - NTFS_TIME_OFFSET; - u64 abs_t; - - /* - * Unfortunately can not use normal 64 bit division on 32 bit arch, but - * the alternative, do_div, does not work with negative numbers so have - * to special case them - */ - if (t < 0) { - abs_t = -t; - ts.tv_nsec = do_div(abs_t, 10000000) * 100; - ts.tv_nsec = -ts.tv_nsec; - ts.tv_sec = -abs_t; - } else { - abs_t = t; - ts.tv_nsec = do_div(abs_t, 10000000) * 100; - ts.tv_sec = abs_t; - } - - return ts; -} - -/* Convert the Unix UTC into NT UTC. */ -inline u64 ksmbd_UnixTimeToNT(struct timespec64 t) -{ - /* Convert to 100ns intervals and then add the NTFS time offset. */ - return (u64)t.tv_sec * 10000000 + t.tv_nsec / 100 + NTFS_TIME_OFFSET; -} - -inline long long ksmbd_systime(void) -{ - struct timespec64 ts; - - ktime_get_real_ts64(&ts); - return ksmbd_UnixTimeToNT(ts); -} diff --git a/fs/ksmbd/misc.h b/fs/ksmbd/misc.h deleted file mode 100644 index 1facfcd21200f..0000000000000 --- a/fs/ksmbd/misc.h +++ /dev/null @@ -1,37 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0-or-later */ -/* - * Copyright (C) 2018 Samsung Electronics Co., Ltd. - */ - -#ifndef __KSMBD_MISC_H__ -#define __KSMBD_MISC_H__ - -struct ksmbd_share_config; -struct nls_table; -struct kstat; -struct ksmbd_file; - -int match_pattern(const char *str, size_t len, const char *pattern); -int ksmbd_validate_filename(char *filename); -int parse_stream_name(char *filename, char **stream_name, int *s_type); -char *convert_to_nt_pathname(struct ksmbd_share_config *share, - const struct path *path); -int get_nlink(struct kstat *st); -void ksmbd_conv_path_to_unix(char *path); -void ksmbd_strip_last_slash(char *path); -void ksmbd_conv_path_to_windows(char *path); -char *ksmbd_casefold_sharename(struct unicode_map *um, const char *name); -char *ksmbd_extract_sharename(struct unicode_map *um, const char *treename); -char *convert_to_unix_name(struct ksmbd_share_config *share, const char *name); - -#define KSMBD_DIR_INFO_ALIGNMENT 8 -struct ksmbd_dir_info; -char *ksmbd_convert_dir_info_name(struct ksmbd_dir_info *d_info, - const struct nls_table *local_nls, - int *conv_len); - -#define NTFS_TIME_OFFSET ((u64)(369 * 365 + 89) * 24 * 3600 * 10000000) -struct timespec64 ksmbd_NTtimeToUnix(__le64 ntutc); -u64 ksmbd_UnixTimeToNT(struct timespec64 t); -long long ksmbd_systime(void); -#endif /* __KSMBD_MISC_H__ */ diff --git a/fs/ksmbd/ndr.c b/fs/ksmbd/ndr.c deleted file mode 100644 index 4d9e0b54e3dbf..0000000000000 --- a/fs/ksmbd/ndr.c +++ /dev/null @@ -1,514 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-or-later -/* - * Copyright (C) 2021 Samsung Electronics Co., Ltd. - * Author(s): Namjae Jeon - */ - -#include - -#include "glob.h" -#include "ndr.h" - -static inline char *ndr_get_field(struct ndr *n) -{ - return n->data + n->offset; -} - -static int try_to_realloc_ndr_blob(struct ndr *n, size_t sz) -{ - char *data; - - data = krealloc(n->data, n->offset + sz + 1024, GFP_KERNEL); - if (!data) - return -ENOMEM; - - n->data = data; - n->length += 1024; - memset(n->data + n->offset, 0, 1024); - return 0; -} - -static int ndr_write_int16(struct ndr *n, __u16 value) -{ - if (n->length <= n->offset + sizeof(value)) { - int ret; - - ret = try_to_realloc_ndr_blob(n, sizeof(value)); - if (ret) - return ret; - } - - *(__le16 *)ndr_get_field(n) = cpu_to_le16(value); - n->offset += sizeof(value); - return 0; -} - -static int ndr_write_int32(struct ndr *n, __u32 value) -{ - if (n->length <= n->offset + sizeof(value)) { - int ret; - - ret = try_to_realloc_ndr_blob(n, sizeof(value)); - if (ret) - return ret; - } - - *(__le32 *)ndr_get_field(n) = cpu_to_le32(value); - n->offset += sizeof(value); - return 0; -} - -static int ndr_write_int64(struct ndr *n, __u64 value) -{ - if (n->length <= n->offset + sizeof(value)) { - int ret; - - ret = try_to_realloc_ndr_blob(n, sizeof(value)); - if (ret) - return ret; - } - - *(__le64 *)ndr_get_field(n) = cpu_to_le64(value); - n->offset += sizeof(value); - return 0; -} - -static int ndr_write_bytes(struct ndr *n, void *value, size_t sz) -{ - if (n->length <= n->offset + sz) { - int ret; - - ret = try_to_realloc_ndr_blob(n, sz); - if (ret) - return ret; - } - - memcpy(ndr_get_field(n), value, sz); - n->offset += sz; - return 0; -} - -static int ndr_write_string(struct ndr *n, char *value) -{ - size_t sz; - - sz = strlen(value) + 1; - if (n->length <= n->offset + sz) { - int ret; - - ret = try_to_realloc_ndr_blob(n, sz); - if (ret) - return ret; - } - - memcpy(ndr_get_field(n), value, sz); - n->offset += sz; - n->offset = ALIGN(n->offset, 2); - return 0; -} - -static int ndr_read_string(struct ndr *n, void *value, size_t sz) -{ - int len; - - if (n->offset + sz > n->length) - return -EINVAL; - - len = strnlen(ndr_get_field(n), sz); - if (value) - memcpy(value, ndr_get_field(n), len); - len++; - n->offset += len; - n->offset = ALIGN(n->offset, 2); - return 0; -} - -static int ndr_read_bytes(struct ndr *n, void *value, size_t sz) -{ - if (n->offset + sz > n->length) - return -EINVAL; - - if (value) - memcpy(value, ndr_get_field(n), sz); - n->offset += sz; - return 0; -} - -static int ndr_read_int16(struct ndr *n, __u16 *value) -{ - if (n->offset + sizeof(__u16) > n->length) - return -EINVAL; - - if (value) - *value = le16_to_cpu(*(__le16 *)ndr_get_field(n)); - n->offset += sizeof(__u16); - return 0; -} - -static int ndr_read_int32(struct ndr *n, __u32 *value) -{ - if (n->offset + sizeof(__u32) > n->length) - return -EINVAL; - - if (value) - *value = le32_to_cpu(*(__le32 *)ndr_get_field(n)); - n->offset += sizeof(__u32); - return 0; -} - -static int ndr_read_int64(struct ndr *n, __u64 *value) -{ - if (n->offset + sizeof(__u64) > n->length) - return -EINVAL; - - if (value) - *value = le64_to_cpu(*(__le64 *)ndr_get_field(n)); - n->offset += sizeof(__u64); - return 0; -} - -int ndr_encode_dos_attr(struct ndr *n, struct xattr_dos_attrib *da) -{ - char hex_attr[12] = {0}; - int ret; - - n->offset = 0; - n->length = 1024; - n->data = kzalloc(n->length, GFP_KERNEL); - if (!n->data) - return -ENOMEM; - - if (da->version == 3) { - snprintf(hex_attr, 10, "0x%x", da->attr); - ret = ndr_write_string(n, hex_attr); - } else { - ret = ndr_write_string(n, ""); - } - if (ret) - return ret; - - ret = ndr_write_int16(n, da->version); - if (ret) - return ret; - - ret = ndr_write_int32(n, da->version); - if (ret) - return ret; - - ret = ndr_write_int32(n, da->flags); - if (ret) - return ret; - - ret = ndr_write_int32(n, da->attr); - if (ret) - return ret; - - if (da->version == 3) { - ret = ndr_write_int32(n, da->ea_size); - if (ret) - return ret; - ret = ndr_write_int64(n, da->size); - if (ret) - return ret; - ret = ndr_write_int64(n, da->alloc_size); - } else { - ret = ndr_write_int64(n, da->itime); - } - if (ret) - return ret; - - ret = ndr_write_int64(n, da->create_time); - if (ret) - return ret; - - if (da->version == 3) - ret = ndr_write_int64(n, da->change_time); - return ret; -} - -int ndr_decode_dos_attr(struct ndr *n, struct xattr_dos_attrib *da) -{ - char hex_attr[12]; - unsigned int version2; - int ret; - - n->offset = 0; - ret = ndr_read_string(n, hex_attr, sizeof(hex_attr)); - if (ret) - return ret; - - ret = ndr_read_int16(n, &da->version); - if (ret) - return ret; - - if (da->version != 3 && da->version != 4) { - ksmbd_debug(VFS, "v%d version is not supported\n", da->version); - return -EINVAL; - } - - ret = ndr_read_int32(n, &version2); - if (ret) - return ret; - - if (da->version != version2) { - ksmbd_debug(VFS, "ndr version mismatched(version: %d, version2: %d)\n", - da->version, version2); - return -EINVAL; - } - - ret = ndr_read_int32(n, NULL); - if (ret) - return ret; - - ret = ndr_read_int32(n, &da->attr); - if (ret) - return ret; - - if (da->version == 4) { - ret = ndr_read_int64(n, &da->itime); - if (ret) - return ret; - - ret = ndr_read_int64(n, &da->create_time); - } else { - ret = ndr_read_int32(n, NULL); - if (ret) - return ret; - - ret = ndr_read_int64(n, NULL); - if (ret) - return ret; - - ret = ndr_read_int64(n, NULL); - if (ret) - return ret; - - ret = ndr_read_int64(n, &da->create_time); - if (ret) - return ret; - - ret = ndr_read_int64(n, NULL); - } - - return ret; -} - -static int ndr_encode_posix_acl_entry(struct ndr *n, struct xattr_smb_acl *acl) -{ - int i, ret; - - ret = ndr_write_int32(n, acl->count); - if (ret) - return ret; - - n->offset = ALIGN(n->offset, 8); - ret = ndr_write_int32(n, acl->count); - if (ret) - return ret; - - ret = ndr_write_int32(n, 0); - if (ret) - return ret; - - for (i = 0; i < acl->count; i++) { - n->offset = ALIGN(n->offset, 8); - ret = ndr_write_int16(n, acl->entries[i].type); - if (ret) - return ret; - - ret = ndr_write_int16(n, acl->entries[i].type); - if (ret) - return ret; - - if (acl->entries[i].type == SMB_ACL_USER) { - n->offset = ALIGN(n->offset, 8); - ret = ndr_write_int64(n, acl->entries[i].uid); - } else if (acl->entries[i].type == SMB_ACL_GROUP) { - n->offset = ALIGN(n->offset, 8); - ret = ndr_write_int64(n, acl->entries[i].gid); - } - if (ret) - return ret; - - /* push permission */ - ret = ndr_write_int32(n, acl->entries[i].perm); - } - - return ret; -} - -int ndr_encode_posix_acl(struct ndr *n, - struct user_namespace *user_ns, - struct inode *inode, - struct xattr_smb_acl *acl, - struct xattr_smb_acl *def_acl) -{ - unsigned int ref_id = 0x00020000; - int ret; - vfsuid_t vfsuid; - vfsgid_t vfsgid; - - n->offset = 0; - n->length = 1024; - n->data = kzalloc(n->length, GFP_KERNEL); - if (!n->data) - return -ENOMEM; - - if (acl) { - /* ACL ACCESS */ - ret = ndr_write_int32(n, ref_id); - ref_id += 4; - } else { - ret = ndr_write_int32(n, 0); - } - if (ret) - return ret; - - if (def_acl) { - /* DEFAULT ACL ACCESS */ - ret = ndr_write_int32(n, ref_id); - ref_id += 4; - } else { - ret = ndr_write_int32(n, 0); - } - if (ret) - return ret; - - vfsuid = i_uid_into_vfsuid(user_ns, inode); - ret = ndr_write_int64(n, from_kuid(&init_user_ns, vfsuid_into_kuid(vfsuid))); - if (ret) - return ret; - vfsgid = i_gid_into_vfsgid(user_ns, inode); - ret = ndr_write_int64(n, from_kgid(&init_user_ns, vfsgid_into_kgid(vfsgid))); - if (ret) - return ret; - ret = ndr_write_int32(n, inode->i_mode); - if (ret) - return ret; - - if (acl) { - ret = ndr_encode_posix_acl_entry(n, acl); - if (def_acl && !ret) - ret = ndr_encode_posix_acl_entry(n, def_acl); - } - return ret; -} - -int ndr_encode_v4_ntacl(struct ndr *n, struct xattr_ntacl *acl) -{ - unsigned int ref_id = 0x00020004; - int ret; - - n->offset = 0; - n->length = 2048; - n->data = kzalloc(n->length, GFP_KERNEL); - if (!n->data) - return -ENOMEM; - - ret = ndr_write_int16(n, acl->version); - if (ret) - return ret; - - ret = ndr_write_int32(n, acl->version); - if (ret) - return ret; - - ret = ndr_write_int16(n, 2); - if (ret) - return ret; - - ret = ndr_write_int32(n, ref_id); - if (ret) - return ret; - - /* push hash type and hash 64bytes */ - ret = ndr_write_int16(n, acl->hash_type); - if (ret) - return ret; - - ret = ndr_write_bytes(n, acl->hash, XATTR_SD_HASH_SIZE); - if (ret) - return ret; - - ret = ndr_write_bytes(n, acl->desc, acl->desc_len); - if (ret) - return ret; - - ret = ndr_write_int64(n, acl->current_time); - if (ret) - return ret; - - ret = ndr_write_bytes(n, acl->posix_acl_hash, XATTR_SD_HASH_SIZE); - if (ret) - return ret; - - /* push ndr for security descriptor */ - ret = ndr_write_bytes(n, acl->sd_buf, acl->sd_size); - return ret; -} - -int ndr_decode_v4_ntacl(struct ndr *n, struct xattr_ntacl *acl) -{ - unsigned int version2; - int ret; - - n->offset = 0; - ret = ndr_read_int16(n, &acl->version); - if (ret) - return ret; - if (acl->version != 4) { - ksmbd_debug(VFS, "v%d version is not supported\n", acl->version); - return -EINVAL; - } - - ret = ndr_read_int32(n, &version2); - if (ret) - return ret; - if (acl->version != version2) { - ksmbd_debug(VFS, "ndr version mismatched(version: %d, version2: %d)\n", - acl->version, version2); - return -EINVAL; - } - - /* Read Level */ - ret = ndr_read_int16(n, NULL); - if (ret) - return ret; - - /* Read Ref Id */ - ret = ndr_read_int32(n, NULL); - if (ret) - return ret; - - ret = ndr_read_int16(n, &acl->hash_type); - if (ret) - return ret; - - ret = ndr_read_bytes(n, acl->hash, XATTR_SD_HASH_SIZE); - if (ret) - return ret; - - ndr_read_bytes(n, acl->desc, 10); - if (strncmp(acl->desc, "posix_acl", 9)) { - pr_err("Invalid acl description : %s\n", acl->desc); - return -EINVAL; - } - - /* Read Time */ - ret = ndr_read_int64(n, NULL); - if (ret) - return ret; - - /* Read Posix ACL hash */ - ret = ndr_read_bytes(n, acl->posix_acl_hash, XATTR_SD_HASH_SIZE); - if (ret) - return ret; - - acl->sd_size = n->length - n->offset; - acl->sd_buf = kzalloc(acl->sd_size, GFP_KERNEL); - if (!acl->sd_buf) - return -ENOMEM; - - ret = ndr_read_bytes(n, acl->sd_buf, acl->sd_size); - return ret; -} diff --git a/fs/ksmbd/ndr.h b/fs/ksmbd/ndr.h deleted file mode 100644 index 60ca265d1bb01..0000000000000 --- a/fs/ksmbd/ndr.h +++ /dev/null @@ -1,22 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0-or-later */ -/* - * Copyright (C) 2020 Samsung Electronics Co., Ltd. - * Author(s): Namjae Jeon - */ - -struct ndr { - char *data; - int offset; - int length; -}; - -#define NDR_NTSD_OFFSETOF 0xA0 - -int ndr_encode_dos_attr(struct ndr *n, struct xattr_dos_attrib *da); -int ndr_decode_dos_attr(struct ndr *n, struct xattr_dos_attrib *da); -int ndr_encode_posix_acl(struct ndr *n, struct user_namespace *user_ns, - struct inode *inode, struct xattr_smb_acl *acl, - struct xattr_smb_acl *def_acl); -int ndr_encode_v4_ntacl(struct ndr *n, struct xattr_ntacl *acl); -int ndr_encode_v3_ntacl(struct ndr *n, struct xattr_ntacl *acl); -int ndr_decode_v4_ntacl(struct ndr *n, struct xattr_ntacl *acl); diff --git a/fs/ksmbd/nterr.h b/fs/ksmbd/nterr.h deleted file mode 100644 index 2f358f88a0188..0000000000000 --- a/fs/ksmbd/nterr.h +++ /dev/null @@ -1,543 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0-or-later */ -/* - * Unix SMB/Netbios implementation. - * Version 1.9. - * NT error code constants - * Copyright (C) Andrew Tridgell 1992-2000 - * Copyright (C) John H Terpstra 1996-2000 - * Copyright (C) Luke Kenneth Casson Leighton 1996-2000 - * Copyright (C) Paul Ashton 1998-2000 - */ - -#ifndef _NTERR_H -#define _NTERR_H - -/* Win32 Status codes. */ -#define NT_STATUS_MORE_ENTRIES 0x0105 -#define NT_ERROR_INVALID_PARAMETER 0x0057 -#define NT_ERROR_INSUFFICIENT_BUFFER 0x007a -#define NT_STATUS_1804 0x070c -#define NT_STATUS_NOTIFY_ENUM_DIR 0x010c -#define NT_STATUS_INVALID_LOCK_RANGE (0xC0000000 | 0x01a1) -/* - * Win32 Error codes extracted using a loop in smbclient then printing a netmon - * sniff to a file. - */ - -#define NT_STATUS_OK 0x0000 -#define NT_STATUS_SOME_UNMAPPED 0x0107 -#define NT_STATUS_BUFFER_OVERFLOW 0x80000005 -#define NT_STATUS_NO_MORE_ENTRIES 0x8000001a -#define NT_STATUS_MEDIA_CHANGED 0x8000001c -#define NT_STATUS_END_OF_MEDIA 0x8000001e -#define NT_STATUS_MEDIA_CHECK 0x80000020 -#define NT_STATUS_NO_DATA_DETECTED 0x8000001c -#define NT_STATUS_STOPPED_ON_SYMLINK 0x8000002d -#define NT_STATUS_DEVICE_REQUIRES_CLEANING 0x80000288 -#define NT_STATUS_DEVICE_DOOR_OPEN 0x80000288 -#define NT_STATUS_UNSUCCESSFUL (0xC0000000 | 0x0001) -#define NT_STATUS_NOT_IMPLEMENTED (0xC0000000 | 0x0002) -#define NT_STATUS_INVALID_INFO_CLASS (0xC0000000 | 0x0003) -#define NT_STATUS_INFO_LENGTH_MISMATCH (0xC0000000 | 0x0004) -#define NT_STATUS_ACCESS_VIOLATION (0xC0000000 | 0x0005) -#define NT_STATUS_IN_PAGE_ERROR (0xC0000000 | 0x0006) -#define NT_STATUS_PAGEFILE_QUOTA (0xC0000000 | 0x0007) -#define NT_STATUS_INVALID_HANDLE (0xC0000000 | 0x0008) -#define NT_STATUS_BAD_INITIAL_STACK (0xC0000000 | 0x0009) -#define NT_STATUS_BAD_INITIAL_PC (0xC0000000 | 0x000a) -#define NT_STATUS_INVALID_CID (0xC0000000 | 0x000b) -#define NT_STATUS_TIMER_NOT_CANCELED (0xC0000000 | 0x000c) -#define NT_STATUS_INVALID_PARAMETER (0xC0000000 | 0x000d) -#define NT_STATUS_NO_SUCH_DEVICE (0xC0000000 | 0x000e) -#define NT_STATUS_NO_SUCH_FILE (0xC0000000 | 0x000f) -#define NT_STATUS_INVALID_DEVICE_REQUEST (0xC0000000 | 0x0010) -#define NT_STATUS_END_OF_FILE (0xC0000000 | 0x0011) -#define NT_STATUS_WRONG_VOLUME (0xC0000000 | 0x0012) -#define NT_STATUS_NO_MEDIA_IN_DEVICE (0xC0000000 | 0x0013) -#define NT_STATUS_UNRECOGNIZED_MEDIA (0xC0000000 | 0x0014) -#define NT_STATUS_NONEXISTENT_SECTOR (0xC0000000 | 0x0015) -#define NT_STATUS_MORE_PROCESSING_REQUIRED (0xC0000000 | 0x0016) -#define NT_STATUS_NO_MEMORY (0xC0000000 | 0x0017) -#define NT_STATUS_CONFLICTING_ADDRESSES (0xC0000000 | 0x0018) -#define NT_STATUS_NOT_MAPPED_VIEW (0xC0000000 | 0x0019) -#define NT_STATUS_UNABLE_TO_FREE_VM (0x80000000 | 0x001a) -#define NT_STATUS_UNABLE_TO_DELETE_SECTION (0xC0000000 | 0x001b) -#define NT_STATUS_INVALID_SYSTEM_SERVICE (0xC0000000 | 0x001c) -#define NT_STATUS_ILLEGAL_INSTRUCTION (0xC0000000 | 0x001d) -#define NT_STATUS_INVALID_LOCK_SEQUENCE (0xC0000000 | 0x001e) -#define NT_STATUS_INVALID_VIEW_SIZE (0xC0000000 | 0x001f) -#define NT_STATUS_INVALID_FILE_FOR_SECTION (0xC0000000 | 0x0020) -#define NT_STATUS_ALREADY_COMMITTED (0xC0000000 | 0x0021) -#define NT_STATUS_ACCESS_DENIED (0xC0000000 | 0x0022) -#define NT_STATUS_BUFFER_TOO_SMALL (0xC0000000 | 0x0023) -#define NT_STATUS_OBJECT_TYPE_MISMATCH (0xC0000000 | 0x0024) -#define NT_STATUS_NONCONTINUABLE_EXCEPTION (0xC0000000 | 0x0025) -#define NT_STATUS_INVALID_DISPOSITION (0xC0000000 | 0x0026) -#define NT_STATUS_UNWIND (0xC0000000 | 0x0027) -#define NT_STATUS_BAD_STACK (0xC0000000 | 0x0028) -#define NT_STATUS_INVALID_UNWIND_TARGET (0xC0000000 | 0x0029) -#define NT_STATUS_NOT_LOCKED (0xC0000000 | 0x002a) -#define NT_STATUS_PARITY_ERROR (0xC0000000 | 0x002b) -#define NT_STATUS_UNABLE_TO_DECOMMIT_VM (0xC0000000 | 0x002c) -#define NT_STATUS_NOT_COMMITTED (0xC0000000 | 0x002d) -#define NT_STATUS_INVALID_PORT_ATTRIBUTES (0xC0000000 | 0x002e) -#define NT_STATUS_PORT_MESSAGE_TOO_LONG (0xC0000000 | 0x002f) -#define NT_STATUS_INVALID_PARAMETER_MIX (0xC0000000 | 0x0030) -#define NT_STATUS_INVALID_QUOTA_LOWER (0xC0000000 | 0x0031) -#define NT_STATUS_DISK_CORRUPT_ERROR (0xC0000000 | 0x0032) -#define NT_STATUS_OBJECT_NAME_INVALID (0xC0000000 | 0x0033) -#define NT_STATUS_OBJECT_NAME_NOT_FOUND (0xC0000000 | 0x0034) -#define NT_STATUS_OBJECT_NAME_COLLISION (0xC0000000 | 0x0035) -#define NT_STATUS_HANDLE_NOT_WAITABLE (0xC0000000 | 0x0036) -#define NT_STATUS_PORT_DISCONNECTED (0xC0000000 | 0x0037) -#define NT_STATUS_DEVICE_ALREADY_ATTACHED (0xC0000000 | 0x0038) -#define NT_STATUS_OBJECT_PATH_INVALID (0xC0000000 | 0x0039) -#define NT_STATUS_OBJECT_PATH_NOT_FOUND (0xC0000000 | 0x003a) -#define NT_STATUS_OBJECT_PATH_SYNTAX_BAD (0xC0000000 | 0x003b) -#define NT_STATUS_DATA_OVERRUN (0xC0000000 | 0x003c) -#define NT_STATUS_DATA_LATE_ERROR (0xC0000000 | 0x003d) -#define NT_STATUS_DATA_ERROR (0xC0000000 | 0x003e) -#define NT_STATUS_CRC_ERROR (0xC0000000 | 0x003f) -#define NT_STATUS_SECTION_TOO_BIG (0xC0000000 | 0x0040) -#define NT_STATUS_PORT_CONNECTION_REFUSED (0xC0000000 | 0x0041) -#define NT_STATUS_INVALID_PORT_HANDLE (0xC0000000 | 0x0042) -#define NT_STATUS_SHARING_VIOLATION (0xC0000000 | 0x0043) -#define NT_STATUS_QUOTA_EXCEEDED (0xC0000000 | 0x0044) -#define NT_STATUS_INVALID_PAGE_PROTECTION (0xC0000000 | 0x0045) -#define NT_STATUS_MUTANT_NOT_OWNED (0xC0000000 | 0x0046) -#define NT_STATUS_SEMAPHORE_LIMIT_EXCEEDED (0xC0000000 | 0x0047) -#define NT_STATUS_PORT_ALREADY_SET (0xC0000000 | 0x0048) -#define NT_STATUS_SECTION_NOT_IMAGE (0xC0000000 | 0x0049) -#define NT_STATUS_SUSPEND_COUNT_EXCEEDED (0xC0000000 | 0x004a) -#define NT_STATUS_THREAD_IS_TERMINATING (0xC0000000 | 0x004b) -#define NT_STATUS_BAD_WORKING_SET_LIMIT (0xC0000000 | 0x004c) -#define NT_STATUS_INCOMPATIBLE_FILE_MAP (0xC0000000 | 0x004d) -#define NT_STATUS_SECTION_PROTECTION (0xC0000000 | 0x004e) -#define NT_STATUS_EAS_NOT_SUPPORTED (0xC0000000 | 0x004f) -#define NT_STATUS_EA_TOO_LARGE (0xC0000000 | 0x0050) -#define NT_STATUS_NONEXISTENT_EA_ENTRY (0xC0000000 | 0x0051) -#define NT_STATUS_NO_EAS_ON_FILE (0xC0000000 | 0x0052) -#define NT_STATUS_EA_CORRUPT_ERROR (0xC0000000 | 0x0053) -#define NT_STATUS_FILE_LOCK_CONFLICT (0xC0000000 | 0x0054) -#define NT_STATUS_LOCK_NOT_GRANTED (0xC0000000 | 0x0055) -#define NT_STATUS_DELETE_PENDING (0xC0000000 | 0x0056) -#define NT_STATUS_CTL_FILE_NOT_SUPPORTED (0xC0000000 | 0x0057) -#define NT_STATUS_UNKNOWN_REVISION (0xC0000000 | 0x0058) -#define NT_STATUS_REVISION_MISMATCH (0xC0000000 | 0x0059) -#define NT_STATUS_INVALID_OWNER (0xC0000000 | 0x005a) -#define NT_STATUS_INVALID_PRIMARY_GROUP (0xC0000000 | 0x005b) -#define NT_STATUS_NO_IMPERSONATION_TOKEN (0xC0000000 | 0x005c) -#define NT_STATUS_CANT_DISABLE_MANDATORY (0xC0000000 | 0x005d) -#define NT_STATUS_NO_LOGON_SERVERS (0xC0000000 | 0x005e) -#define NT_STATUS_NO_SUCH_LOGON_SESSION (0xC0000000 | 0x005f) -#define NT_STATUS_NO_SUCH_PRIVILEGE (0xC0000000 | 0x0060) -#define NT_STATUS_PRIVILEGE_NOT_HELD (0xC0000000 | 0x0061) -#define NT_STATUS_INVALID_ACCOUNT_NAME (0xC0000000 | 0x0062) -#define NT_STATUS_USER_EXISTS (0xC0000000 | 0x0063) -#define NT_STATUS_NO_SUCH_USER (0xC0000000 | 0x0064) -#define NT_STATUS_GROUP_EXISTS (0xC0000000 | 0x0065) -#define NT_STATUS_NO_SUCH_GROUP (0xC0000000 | 0x0066) -#define NT_STATUS_MEMBER_IN_GROUP (0xC0000000 | 0x0067) -#define NT_STATUS_MEMBER_NOT_IN_GROUP (0xC0000000 | 0x0068) -#define NT_STATUS_LAST_ADMIN (0xC0000000 | 0x0069) -#define NT_STATUS_WRONG_PASSWORD (0xC0000000 | 0x006a) -#define NT_STATUS_ILL_FORMED_PASSWORD (0xC0000000 | 0x006b) -#define NT_STATUS_PASSWORD_RESTRICTION (0xC0000000 | 0x006c) -#define NT_STATUS_LOGON_FAILURE (0xC0000000 | 0x006d) -#define NT_STATUS_ACCOUNT_RESTRICTION (0xC0000000 | 0x006e) -#define NT_STATUS_INVALID_LOGON_HOURS (0xC0000000 | 0x006f) -#define NT_STATUS_INVALID_WORKSTATION (0xC0000000 | 0x0070) -#define NT_STATUS_PASSWORD_EXPIRED (0xC0000000 | 0x0071) -#define NT_STATUS_ACCOUNT_DISABLED (0xC0000000 | 0x0072) -#define NT_STATUS_NONE_MAPPED (0xC0000000 | 0x0073) -#define NT_STATUS_TOO_MANY_LUIDS_REQUESTED (0xC0000000 | 0x0074) -#define NT_STATUS_LUIDS_EXHAUSTED (0xC0000000 | 0x0075) -#define NT_STATUS_INVALID_SUB_AUTHORITY (0xC0000000 | 0x0076) -#define NT_STATUS_INVALID_ACL (0xC0000000 | 0x0077) -#define NT_STATUS_INVALID_SID (0xC0000000 | 0x0078) -#define NT_STATUS_INVALID_SECURITY_DESCR (0xC0000000 | 0x0079) -#define NT_STATUS_PROCEDURE_NOT_FOUND (0xC0000000 | 0x007a) -#define NT_STATUS_INVALID_IMAGE_FORMAT (0xC0000000 | 0x007b) -#define NT_STATUS_NO_TOKEN (0xC0000000 | 0x007c) -#define NT_STATUS_BAD_INHERITANCE_ACL (0xC0000000 | 0x007d) -#define NT_STATUS_RANGE_NOT_LOCKED (0xC0000000 | 0x007e) -#define NT_STATUS_DISK_FULL (0xC0000000 | 0x007f) -#define NT_STATUS_SERVER_DISABLED (0xC0000000 | 0x0080) -#define NT_STATUS_SERVER_NOT_DISABLED (0xC0000000 | 0x0081) -#define NT_STATUS_TOO_MANY_GUIDS_REQUESTED (0xC0000000 | 0x0082) -#define NT_STATUS_GUIDS_EXHAUSTED (0xC0000000 | 0x0083) -#define NT_STATUS_INVALID_ID_AUTHORITY (0xC0000000 | 0x0084) -#define NT_STATUS_AGENTS_EXHAUSTED (0xC0000000 | 0x0085) -#define NT_STATUS_INVALID_VOLUME_LABEL (0xC0000000 | 0x0086) -#define NT_STATUS_SECTION_NOT_EXTENDED (0xC0000000 | 0x0087) -#define NT_STATUS_NOT_MAPPED_DATA (0xC0000000 | 0x0088) -#define NT_STATUS_RESOURCE_DATA_NOT_FOUND (0xC0000000 | 0x0089) -#define NT_STATUS_RESOURCE_TYPE_NOT_FOUND (0xC0000000 | 0x008a) -#define NT_STATUS_RESOURCE_NAME_NOT_FOUND (0xC0000000 | 0x008b) -#define NT_STATUS_ARRAY_BOUNDS_EXCEEDED (0xC0000000 | 0x008c) -#define NT_STATUS_FLOAT_DENORMAL_OPERAND (0xC0000000 | 0x008d) -#define NT_STATUS_FLOAT_DIVIDE_BY_ZERO (0xC0000000 | 0x008e) -#define NT_STATUS_FLOAT_INEXACT_RESULT (0xC0000000 | 0x008f) -#define NT_STATUS_FLOAT_INVALID_OPERATION (0xC0000000 | 0x0090) -#define NT_STATUS_FLOAT_OVERFLOW (0xC0000000 | 0x0091) -#define NT_STATUS_FLOAT_STACK_CHECK (0xC0000000 | 0x0092) -#define NT_STATUS_FLOAT_UNDERFLOW (0xC0000000 | 0x0093) -#define NT_STATUS_INTEGER_DIVIDE_BY_ZERO (0xC0000000 | 0x0094) -#define NT_STATUS_INTEGER_OVERFLOW (0xC0000000 | 0x0095) -#define NT_STATUS_PRIVILEGED_INSTRUCTION (0xC0000000 | 0x0096) -#define NT_STATUS_TOO_MANY_PAGING_FILES (0xC0000000 | 0x0097) -#define NT_STATUS_FILE_INVALID (0xC0000000 | 0x0098) -#define NT_STATUS_ALLOTTED_SPACE_EXCEEDED (0xC0000000 | 0x0099) -#define NT_STATUS_INSUFFICIENT_RESOURCES (0xC0000000 | 0x009a) -#define NT_STATUS_DFS_EXIT_PATH_FOUND (0xC0000000 | 0x009b) -#define NT_STATUS_DEVICE_DATA_ERROR (0xC0000000 | 0x009c) -#define NT_STATUS_DEVICE_NOT_CONNECTED (0xC0000000 | 0x009d) -#define NT_STATUS_DEVICE_POWER_FAILURE (0xC0000000 | 0x009e) -#define NT_STATUS_FREE_VM_NOT_AT_BASE (0xC0000000 | 0x009f) -#define NT_STATUS_MEMORY_NOT_ALLOCATED (0xC0000000 | 0x00a0) -#define NT_STATUS_WORKING_SET_QUOTA (0xC0000000 | 0x00a1) -#define NT_STATUS_MEDIA_WRITE_PROTECTED (0xC0000000 | 0x00a2) -#define NT_STATUS_DEVICE_NOT_READY (0xC0000000 | 0x00a3) -#define NT_STATUS_INVALID_GROUP_ATTRIBUTES (0xC0000000 | 0x00a4) -#define NT_STATUS_BAD_IMPERSONATION_LEVEL (0xC0000000 | 0x00a5) -#define NT_STATUS_CANT_OPEN_ANONYMOUS (0xC0000000 | 0x00a6) -#define NT_STATUS_BAD_VALIDATION_CLASS (0xC0000000 | 0x00a7) -#define NT_STATUS_BAD_TOKEN_TYPE (0xC0000000 | 0x00a8) -#define NT_STATUS_BAD_MASTER_BOOT_RECORD (0xC0000000 | 0x00a9) -#define NT_STATUS_INSTRUCTION_MISALIGNMENT (0xC0000000 | 0x00aa) -#define NT_STATUS_INSTANCE_NOT_AVAILABLE (0xC0000000 | 0x00ab) -#define NT_STATUS_PIPE_NOT_AVAILABLE (0xC0000000 | 0x00ac) -#define NT_STATUS_INVALID_PIPE_STATE (0xC0000000 | 0x00ad) -#define NT_STATUS_PIPE_BUSY (0xC0000000 | 0x00ae) -#define NT_STATUS_ILLEGAL_FUNCTION (0xC0000000 | 0x00af) -#define NT_STATUS_PIPE_DISCONNECTED (0xC0000000 | 0x00b0) -#define NT_STATUS_PIPE_CLOSING (0xC0000000 | 0x00b1) -#define NT_STATUS_PIPE_CONNECTED (0xC0000000 | 0x00b2) -#define NT_STATUS_PIPE_LISTENING (0xC0000000 | 0x00b3) -#define NT_STATUS_INVALID_READ_MODE (0xC0000000 | 0x00b4) -#define NT_STATUS_IO_TIMEOUT (0xC0000000 | 0x00b5) -#define NT_STATUS_FILE_FORCED_CLOSED (0xC0000000 | 0x00b6) -#define NT_STATUS_PROFILING_NOT_STARTED (0xC0000000 | 0x00b7) -#define NT_STATUS_PROFILING_NOT_STOPPED (0xC0000000 | 0x00b8) -#define NT_STATUS_COULD_NOT_INTERPRET (0xC0000000 | 0x00b9) -#define NT_STATUS_FILE_IS_A_DIRECTORY (0xC0000000 | 0x00ba) -#define NT_STATUS_NOT_SUPPORTED (0xC0000000 | 0x00bb) -#define NT_STATUS_REMOTE_NOT_LISTENING (0xC0000000 | 0x00bc) -#define NT_STATUS_DUPLICATE_NAME (0xC0000000 | 0x00bd) -#define NT_STATUS_BAD_NETWORK_PATH (0xC0000000 | 0x00be) -#define NT_STATUS_NETWORK_BUSY (0xC0000000 | 0x00bf) -#define NT_STATUS_DEVICE_DOES_NOT_EXIST (0xC0000000 | 0x00c0) -#define NT_STATUS_TOO_MANY_COMMANDS (0xC0000000 | 0x00c1) -#define NT_STATUS_ADAPTER_HARDWARE_ERROR (0xC0000000 | 0x00c2) -#define NT_STATUS_INVALID_NETWORK_RESPONSE (0xC0000000 | 0x00c3) -#define NT_STATUS_UNEXPECTED_NETWORK_ERROR (0xC0000000 | 0x00c4) -#define NT_STATUS_BAD_REMOTE_ADAPTER (0xC0000000 | 0x00c5) -#define NT_STATUS_PRINT_QUEUE_FULL (0xC0000000 | 0x00c6) -#define NT_STATUS_NO_SPOOL_SPACE (0xC0000000 | 0x00c7) -#define NT_STATUS_PRINT_CANCELLED (0xC0000000 | 0x00c8) -#define NT_STATUS_NETWORK_NAME_DELETED (0xC0000000 | 0x00c9) -#define NT_STATUS_NETWORK_ACCESS_DENIED (0xC0000000 | 0x00ca) -#define NT_STATUS_BAD_DEVICE_TYPE (0xC0000000 | 0x00cb) -#define NT_STATUS_BAD_NETWORK_NAME (0xC0000000 | 0x00cc) -#define NT_STATUS_TOO_MANY_NAMES (0xC0000000 | 0x00cd) -#define NT_STATUS_TOO_MANY_SESSIONS (0xC0000000 | 0x00ce) -#define NT_STATUS_SHARING_PAUSED (0xC0000000 | 0x00cf) -#define NT_STATUS_REQUEST_NOT_ACCEPTED (0xC0000000 | 0x00d0) -#define NT_STATUS_REDIRECTOR_PAUSED (0xC0000000 | 0x00d1) -#define NT_STATUS_NET_WRITE_FAULT (0xC0000000 | 0x00d2) -#define NT_STATUS_PROFILING_AT_LIMIT (0xC0000000 | 0x00d3) -#define NT_STATUS_NOT_SAME_DEVICE (0xC0000000 | 0x00d4) -#define NT_STATUS_FILE_RENAMED (0xC0000000 | 0x00d5) -#define NT_STATUS_VIRTUAL_CIRCUIT_CLOSED (0xC0000000 | 0x00d6) -#define NT_STATUS_NO_SECURITY_ON_OBJECT (0xC0000000 | 0x00d7) -#define NT_STATUS_CANT_WAIT (0xC0000000 | 0x00d8) -#define NT_STATUS_PIPE_EMPTY (0xC0000000 | 0x00d9) -#define NT_STATUS_CANT_ACCESS_DOMAIN_INFO (0xC0000000 | 0x00da) -#define NT_STATUS_CANT_TERMINATE_SELF (0xC0000000 | 0x00db) -#define NT_STATUS_INVALID_SERVER_STATE (0xC0000000 | 0x00dc) -#define NT_STATUS_INVALID_DOMAIN_STATE (0xC0000000 | 0x00dd) -#define NT_STATUS_INVALID_DOMAIN_ROLE (0xC0000000 | 0x00de) -#define NT_STATUS_NO_SUCH_DOMAIN (0xC0000000 | 0x00df) -#define NT_STATUS_DOMAIN_EXISTS (0xC0000000 | 0x00e0) -#define NT_STATUS_DOMAIN_LIMIT_EXCEEDED (0xC0000000 | 0x00e1) -#define NT_STATUS_OPLOCK_NOT_GRANTED (0xC0000000 | 0x00e2) -#define NT_STATUS_INVALID_OPLOCK_PROTOCOL (0xC0000000 | 0x00e3) -#define NT_STATUS_INTERNAL_DB_CORRUPTION (0xC0000000 | 0x00e4) -#define NT_STATUS_INTERNAL_ERROR (0xC0000000 | 0x00e5) -#define NT_STATUS_GENERIC_NOT_MAPPED (0xC0000000 | 0x00e6) -#define NT_STATUS_BAD_DESCRIPTOR_FORMAT (0xC0000000 | 0x00e7) -#define NT_STATUS_INVALID_USER_BUFFER (0xC0000000 | 0x00e8) -#define NT_STATUS_UNEXPECTED_IO_ERROR (0xC0000000 | 0x00e9) -#define NT_STATUS_UNEXPECTED_MM_CREATE_ERR (0xC0000000 | 0x00ea) -#define NT_STATUS_UNEXPECTED_MM_MAP_ERROR (0xC0000000 | 0x00eb) -#define NT_STATUS_UNEXPECTED_MM_EXTEND_ERR (0xC0000000 | 0x00ec) -#define NT_STATUS_NOT_LOGON_PROCESS (0xC0000000 | 0x00ed) -#define NT_STATUS_LOGON_SESSION_EXISTS (0xC0000000 | 0x00ee) -#define NT_STATUS_INVALID_PARAMETER_1 (0xC0000000 | 0x00ef) -#define NT_STATUS_INVALID_PARAMETER_2 (0xC0000000 | 0x00f0) -#define NT_STATUS_INVALID_PARAMETER_3 (0xC0000000 | 0x00f1) -#define NT_STATUS_INVALID_PARAMETER_4 (0xC0000000 | 0x00f2) -#define NT_STATUS_INVALID_PARAMETER_5 (0xC0000000 | 0x00f3) -#define NT_STATUS_INVALID_PARAMETER_6 (0xC0000000 | 0x00f4) -#define NT_STATUS_INVALID_PARAMETER_7 (0xC0000000 | 0x00f5) -#define NT_STATUS_INVALID_PARAMETER_8 (0xC0000000 | 0x00f6) -#define NT_STATUS_INVALID_PARAMETER_9 (0xC0000000 | 0x00f7) -#define NT_STATUS_INVALID_PARAMETER_10 (0xC0000000 | 0x00f8) -#define NT_STATUS_INVALID_PARAMETER_11 (0xC0000000 | 0x00f9) -#define NT_STATUS_INVALID_PARAMETER_12 (0xC0000000 | 0x00fa) -#define NT_STATUS_REDIRECTOR_NOT_STARTED (0xC0000000 | 0x00fb) -#define NT_STATUS_REDIRECTOR_STARTED (0xC0000000 | 0x00fc) -#define NT_STATUS_STACK_OVERFLOW (0xC0000000 | 0x00fd) -#define NT_STATUS_NO_SUCH_PACKAGE (0xC0000000 | 0x00fe) -#define NT_STATUS_BAD_FUNCTION_TABLE (0xC0000000 | 0x00ff) -#define NT_STATUS_DIRECTORY_NOT_EMPTY (0xC0000000 | 0x0101) -#define NT_STATUS_FILE_CORRUPT_ERROR (0xC0000000 | 0x0102) -#define NT_STATUS_NOT_A_DIRECTORY (0xC0000000 | 0x0103) -#define NT_STATUS_BAD_LOGON_SESSION_STATE (0xC0000000 | 0x0104) -#define NT_STATUS_LOGON_SESSION_COLLISION (0xC0000000 | 0x0105) -#define NT_STATUS_NAME_TOO_LONG (0xC0000000 | 0x0106) -#define NT_STATUS_FILES_OPEN (0xC0000000 | 0x0107) -#define NT_STATUS_CONNECTION_IN_USE (0xC0000000 | 0x0108) -#define NT_STATUS_MESSAGE_NOT_FOUND (0xC0000000 | 0x0109) -#define NT_STATUS_PROCESS_IS_TERMINATING (0xC0000000 | 0x010a) -#define NT_STATUS_INVALID_LOGON_TYPE (0xC0000000 | 0x010b) -#define NT_STATUS_NO_GUID_TRANSLATION (0xC0000000 | 0x010c) -#define NT_STATUS_CANNOT_IMPERSONATE (0xC0000000 | 0x010d) -#define NT_STATUS_IMAGE_ALREADY_LOADED (0xC0000000 | 0x010e) -#define NT_STATUS_ABIOS_NOT_PRESENT (0xC0000000 | 0x010f) -#define NT_STATUS_ABIOS_LID_NOT_EXIST (0xC0000000 | 0x0110) -#define NT_STATUS_ABIOS_LID_ALREADY_OWNED (0xC0000000 | 0x0111) -#define NT_STATUS_ABIOS_NOT_LID_OWNER (0xC0000000 | 0x0112) -#define NT_STATUS_ABIOS_INVALID_COMMAND (0xC0000000 | 0x0113) -#define NT_STATUS_ABIOS_INVALID_LID (0xC0000000 | 0x0114) -#define NT_STATUS_ABIOS_SELECTOR_NOT_AVAILABLE (0xC0000000 | 0x0115) -#define NT_STATUS_ABIOS_INVALID_SELECTOR (0xC0000000 | 0x0116) -#define NT_STATUS_NO_LDT (0xC0000000 | 0x0117) -#define NT_STATUS_INVALID_LDT_SIZE (0xC0000000 | 0x0118) -#define NT_STATUS_INVALID_LDT_OFFSET (0xC0000000 | 0x0119) -#define NT_STATUS_INVALID_LDT_DESCRIPTOR (0xC0000000 | 0x011a) -#define NT_STATUS_INVALID_IMAGE_NE_FORMAT (0xC0000000 | 0x011b) -#define NT_STATUS_RXACT_INVALID_STATE (0xC0000000 | 0x011c) -#define NT_STATUS_RXACT_COMMIT_FAILURE (0xC0000000 | 0x011d) -#define NT_STATUS_MAPPED_FILE_SIZE_ZERO (0xC0000000 | 0x011e) -#define NT_STATUS_TOO_MANY_OPENED_FILES (0xC0000000 | 0x011f) -#define NT_STATUS_CANCELLED (0xC0000000 | 0x0120) -#define NT_STATUS_CANNOT_DELETE (0xC0000000 | 0x0121) -#define NT_STATUS_INVALID_COMPUTER_NAME (0xC0000000 | 0x0122) -#define NT_STATUS_FILE_DELETED (0xC0000000 | 0x0123) -#define NT_STATUS_SPECIAL_ACCOUNT (0xC0000000 | 0x0124) -#define NT_STATUS_SPECIAL_GROUP (0xC0000000 | 0x0125) -#define NT_STATUS_SPECIAL_USER (0xC0000000 | 0x0126) -#define NT_STATUS_MEMBERS_PRIMARY_GROUP (0xC0000000 | 0x0127) -#define NT_STATUS_FILE_CLOSED (0xC0000000 | 0x0128) -#define NT_STATUS_TOO_MANY_THREADS (0xC0000000 | 0x0129) -#define NT_STATUS_THREAD_NOT_IN_PROCESS (0xC0000000 | 0x012a) -#define NT_STATUS_TOKEN_ALREADY_IN_USE (0xC0000000 | 0x012b) -#define NT_STATUS_PAGEFILE_QUOTA_EXCEEDED (0xC0000000 | 0x012c) -#define NT_STATUS_COMMITMENT_LIMIT (0xC0000000 | 0x012d) -#define NT_STATUS_INVALID_IMAGE_LE_FORMAT (0xC0000000 | 0x012e) -#define NT_STATUS_INVALID_IMAGE_NOT_MZ (0xC0000000 | 0x012f) -#define NT_STATUS_INVALID_IMAGE_PROTECT (0xC0000000 | 0x0130) -#define NT_STATUS_INVALID_IMAGE_WIN_16 (0xC0000000 | 0x0131) -#define NT_STATUS_LOGON_SERVER_CONFLICT (0xC0000000 | 0x0132) -#define NT_STATUS_TIME_DIFFERENCE_AT_DC (0xC0000000 | 0x0133) -#define NT_STATUS_SYNCHRONIZATION_REQUIRED (0xC0000000 | 0x0134) -#define NT_STATUS_DLL_NOT_FOUND (0xC0000000 | 0x0135) -#define NT_STATUS_OPEN_FAILED (0xC0000000 | 0x0136) -#define NT_STATUS_IO_PRIVILEGE_FAILED (0xC0000000 | 0x0137) -#define NT_STATUS_ORDINAL_NOT_FOUND (0xC0000000 | 0x0138) -#define NT_STATUS_ENTRYPOINT_NOT_FOUND (0xC0000000 | 0x0139) -#define NT_STATUS_CONTROL_C_EXIT (0xC0000000 | 0x013a) -#define NT_STATUS_LOCAL_DISCONNECT (0xC0000000 | 0x013b) -#define NT_STATUS_REMOTE_DISCONNECT (0xC0000000 | 0x013c) -#define NT_STATUS_REMOTE_RESOURCES (0xC0000000 | 0x013d) -#define NT_STATUS_LINK_FAILED (0xC0000000 | 0x013e) -#define NT_STATUS_LINK_TIMEOUT (0xC0000000 | 0x013f) -#define NT_STATUS_INVALID_CONNECTION (0xC0000000 | 0x0140) -#define NT_STATUS_INVALID_ADDRESS (0xC0000000 | 0x0141) -#define NT_STATUS_DLL_INIT_FAILED (0xC0000000 | 0x0142) -#define NT_STATUS_MISSING_SYSTEMFILE (0xC0000000 | 0x0143) -#define NT_STATUS_UNHANDLED_EXCEPTION (0xC0000000 | 0x0144) -#define NT_STATUS_APP_INIT_FAILURE (0xC0000000 | 0x0145) -#define NT_STATUS_PAGEFILE_CREATE_FAILED (0xC0000000 | 0x0146) -#define NT_STATUS_NO_PAGEFILE (0xC0000000 | 0x0147) -#define NT_STATUS_INVALID_LEVEL (0xC0000000 | 0x0148) -#define NT_STATUS_WRONG_PASSWORD_CORE (0xC0000000 | 0x0149) -#define NT_STATUS_ILLEGAL_FLOAT_CONTEXT (0xC0000000 | 0x014a) -#define NT_STATUS_PIPE_BROKEN (0xC0000000 | 0x014b) -#define NT_STATUS_REGISTRY_CORRUPT (0xC0000000 | 0x014c) -#define NT_STATUS_REGISTRY_IO_FAILED (0xC0000000 | 0x014d) -#define NT_STATUS_NO_EVENT_PAIR (0xC0000000 | 0x014e) -#define NT_STATUS_UNRECOGNIZED_VOLUME (0xC0000000 | 0x014f) -#define NT_STATUS_SERIAL_NO_DEVICE_INITED (0xC0000000 | 0x0150) -#define NT_STATUS_NO_SUCH_ALIAS (0xC0000000 | 0x0151) -#define NT_STATUS_MEMBER_NOT_IN_ALIAS (0xC0000000 | 0x0152) -#define NT_STATUS_MEMBER_IN_ALIAS (0xC0000000 | 0x0153) -#define NT_STATUS_ALIAS_EXISTS (0xC0000000 | 0x0154) -#define NT_STATUS_LOGON_NOT_GRANTED (0xC0000000 | 0x0155) -#define NT_STATUS_TOO_MANY_SECRETS (0xC0000000 | 0x0156) -#define NT_STATUS_SECRET_TOO_LONG (0xC0000000 | 0x0157) -#define NT_STATUS_INTERNAL_DB_ERROR (0xC0000000 | 0x0158) -#define NT_STATUS_FULLSCREEN_MODE (0xC0000000 | 0x0159) -#define NT_STATUS_TOO_MANY_CONTEXT_IDS (0xC0000000 | 0x015a) -#define NT_STATUS_LOGON_TYPE_NOT_GRANTED (0xC0000000 | 0x015b) -#define NT_STATUS_NOT_REGISTRY_FILE (0xC0000000 | 0x015c) -#define NT_STATUS_NT_CROSS_ENCRYPTION_REQUIRED (0xC0000000 | 0x015d) -#define NT_STATUS_DOMAIN_CTRLR_CONFIG_ERROR (0xC0000000 | 0x015e) -#define NT_STATUS_FT_MISSING_MEMBER (0xC0000000 | 0x015f) -#define NT_STATUS_ILL_FORMED_SERVICE_ENTRY (0xC0000000 | 0x0160) -#define NT_STATUS_ILLEGAL_CHARACTER (0xC0000000 | 0x0161) -#define NT_STATUS_UNMAPPABLE_CHARACTER (0xC0000000 | 0x0162) -#define NT_STATUS_UNDEFINED_CHARACTER (0xC0000000 | 0x0163) -#define NT_STATUS_FLOPPY_VOLUME (0xC0000000 | 0x0164) -#define NT_STATUS_FLOPPY_ID_MARK_NOT_FOUND (0xC0000000 | 0x0165) -#define NT_STATUS_FLOPPY_WRONG_CYLINDER (0xC0000000 | 0x0166) -#define NT_STATUS_FLOPPY_UNKNOWN_ERROR (0xC0000000 | 0x0167) -#define NT_STATUS_FLOPPY_BAD_REGISTERS (0xC0000000 | 0x0168) -#define NT_STATUS_DISK_RECALIBRATE_FAILED (0xC0000000 | 0x0169) -#define NT_STATUS_DISK_OPERATION_FAILED (0xC0000000 | 0x016a) -#define NT_STATUS_DISK_RESET_FAILED (0xC0000000 | 0x016b) -#define NT_STATUS_SHARED_IRQ_BUSY (0xC0000000 | 0x016c) -#define NT_STATUS_FT_ORPHANING (0xC0000000 | 0x016d) -#define NT_STATUS_PARTITION_FAILURE (0xC0000000 | 0x0172) -#define NT_STATUS_INVALID_BLOCK_LENGTH (0xC0000000 | 0x0173) -#define NT_STATUS_DEVICE_NOT_PARTITIONED (0xC0000000 | 0x0174) -#define NT_STATUS_UNABLE_TO_LOCK_MEDIA (0xC0000000 | 0x0175) -#define NT_STATUS_UNABLE_TO_UNLOAD_MEDIA (0xC0000000 | 0x0176) -#define NT_STATUS_EOM_OVERFLOW (0xC0000000 | 0x0177) -#define NT_STATUS_NO_MEDIA (0xC0000000 | 0x0178) -#define NT_STATUS_NO_SUCH_MEMBER (0xC0000000 | 0x017a) -#define NT_STATUS_INVALID_MEMBER (0xC0000000 | 0x017b) -#define NT_STATUS_KEY_DELETED (0xC0000000 | 0x017c) -#define NT_STATUS_NO_LOG_SPACE (0xC0000000 | 0x017d) -#define NT_STATUS_TOO_MANY_SIDS (0xC0000000 | 0x017e) -#define NT_STATUS_LM_CROSS_ENCRYPTION_REQUIRED (0xC0000000 | 0x017f) -#define NT_STATUS_KEY_HAS_CHILDREN (0xC0000000 | 0x0180) -#define NT_STATUS_CHILD_MUST_BE_VOLATILE (0xC0000000 | 0x0181) -#define NT_STATUS_DEVICE_CONFIGURATION_ERROR (0xC0000000 | 0x0182) -#define NT_STATUS_DRIVER_INTERNAL_ERROR (0xC0000000 | 0x0183) -#define NT_STATUS_INVALID_DEVICE_STATE (0xC0000000 | 0x0184) -#define NT_STATUS_IO_DEVICE_ERROR (0xC0000000 | 0x0185) -#define NT_STATUS_DEVICE_PROTOCOL_ERROR (0xC0000000 | 0x0186) -#define NT_STATUS_BACKUP_CONTROLLER (0xC0000000 | 0x0187) -#define NT_STATUS_LOG_FILE_FULL (0xC0000000 | 0x0188) -#define NT_STATUS_TOO_LATE (0xC0000000 | 0x0189) -#define NT_STATUS_NO_TRUST_LSA_SECRET (0xC0000000 | 0x018a) -#define NT_STATUS_NO_TRUST_SAM_ACCOUNT (0xC0000000 | 0x018b) -#define NT_STATUS_TRUSTED_DOMAIN_FAILURE (0xC0000000 | 0x018c) -#define NT_STATUS_TRUSTED_RELATIONSHIP_FAILURE (0xC0000000 | 0x018d) -#define NT_STATUS_EVENTLOG_FILE_CORRUPT (0xC0000000 | 0x018e) -#define NT_STATUS_EVENTLOG_CANT_START (0xC0000000 | 0x018f) -#define NT_STATUS_TRUST_FAILURE (0xC0000000 | 0x0190) -#define NT_STATUS_MUTANT_LIMIT_EXCEEDED (0xC0000000 | 0x0191) -#define NT_STATUS_NETLOGON_NOT_STARTED (0xC0000000 | 0x0192) -#define NT_STATUS_ACCOUNT_EXPIRED (0xC0000000 | 0x0193) -#define NT_STATUS_POSSIBLE_DEADLOCK (0xC0000000 | 0x0194) -#define NT_STATUS_NETWORK_CREDENTIAL_CONFLICT (0xC0000000 | 0x0195) -#define NT_STATUS_REMOTE_SESSION_LIMIT (0xC0000000 | 0x0196) -#define NT_STATUS_EVENTLOG_FILE_CHANGED (0xC0000000 | 0x0197) -#define NT_STATUS_NOLOGON_INTERDOMAIN_TRUST_ACCOUNT (0xC0000000 | 0x0198) -#define NT_STATUS_NOLOGON_WORKSTATION_TRUST_ACCOUNT (0xC0000000 | 0x0199) -#define NT_STATUS_NOLOGON_SERVER_TRUST_ACCOUNT (0xC0000000 | 0x019a) -#define NT_STATUS_DOMAIN_TRUST_INCONSISTENT (0xC0000000 | 0x019b) -#define NT_STATUS_FS_DRIVER_REQUIRED (0xC0000000 | 0x019c) -#define NT_STATUS_NO_USER_SESSION_KEY (0xC0000000 | 0x0202) -#define NT_STATUS_USER_SESSION_DELETED (0xC0000000 | 0x0203) -#define NT_STATUS_RESOURCE_LANG_NOT_FOUND (0xC0000000 | 0x0204) -#define NT_STATUS_INSUFF_SERVER_RESOURCES (0xC0000000 | 0x0205) -#define NT_STATUS_INVALID_BUFFER_SIZE (0xC0000000 | 0x0206) -#define NT_STATUS_INVALID_ADDRESS_COMPONENT (0xC0000000 | 0x0207) -#define NT_STATUS_INVALID_ADDRESS_WILDCARD (0xC0000000 | 0x0208) -#define NT_STATUS_TOO_MANY_ADDRESSES (0xC0000000 | 0x0209) -#define NT_STATUS_ADDRESS_ALREADY_EXISTS (0xC0000000 | 0x020a) -#define NT_STATUS_ADDRESS_CLOSED (0xC0000000 | 0x020b) -#define NT_STATUS_CONNECTION_DISCONNECTED (0xC0000000 | 0x020c) -#define NT_STATUS_CONNECTION_RESET (0xC0000000 | 0x020d) -#define NT_STATUS_TOO_MANY_NODES (0xC0000000 | 0x020e) -#define NT_STATUS_TRANSACTION_ABORTED (0xC0000000 | 0x020f) -#define NT_STATUS_TRANSACTION_TIMED_OUT (0xC0000000 | 0x0210) -#define NT_STATUS_TRANSACTION_NO_RELEASE (0xC0000000 | 0x0211) -#define NT_STATUS_TRANSACTION_NO_MATCH (0xC0000000 | 0x0212) -#define NT_STATUS_TRANSACTION_RESPONDED (0xC0000000 | 0x0213) -#define NT_STATUS_TRANSACTION_INVALID_ID (0xC0000000 | 0x0214) -#define NT_STATUS_TRANSACTION_INVALID_TYPE (0xC0000000 | 0x0215) -#define NT_STATUS_NOT_SERVER_SESSION (0xC0000000 | 0x0216) -#define NT_STATUS_NOT_CLIENT_SESSION (0xC0000000 | 0x0217) -#define NT_STATUS_CANNOT_LOAD_REGISTRY_FILE (0xC0000000 | 0x0218) -#define NT_STATUS_DEBUG_ATTACH_FAILED (0xC0000000 | 0x0219) -#define NT_STATUS_SYSTEM_PROCESS_TERMINATED (0xC0000000 | 0x021a) -#define NT_STATUS_DATA_NOT_ACCEPTED (0xC0000000 | 0x021b) -#define NT_STATUS_NO_BROWSER_SERVERS_FOUND (0xC0000000 | 0x021c) -#define NT_STATUS_VDM_HARD_ERROR (0xC0000000 | 0x021d) -#define NT_STATUS_DRIVER_CANCEL_TIMEOUT (0xC0000000 | 0x021e) -#define NT_STATUS_REPLY_MESSAGE_MISMATCH (0xC0000000 | 0x021f) -#define NT_STATUS_MAPPED_ALIGNMENT (0xC0000000 | 0x0220) -#define NT_STATUS_IMAGE_CHECKSUM_MISMATCH (0xC0000000 | 0x0221) -#define NT_STATUS_LOST_WRITEBEHIND_DATA (0xC0000000 | 0x0222) -#define NT_STATUS_CLIENT_SERVER_PARAMETERS_INVALID (0xC0000000 | 0x0223) -#define NT_STATUS_PASSWORD_MUST_CHANGE (0xC0000000 | 0x0224) -#define NT_STATUS_NOT_FOUND (0xC0000000 | 0x0225) -#define NT_STATUS_NOT_TINY_STREAM (0xC0000000 | 0x0226) -#define NT_STATUS_RECOVERY_FAILURE (0xC0000000 | 0x0227) -#define NT_STATUS_STACK_OVERFLOW_READ (0xC0000000 | 0x0228) -#define NT_STATUS_FAIL_CHECK (0xC0000000 | 0x0229) -#define NT_STATUS_DUPLICATE_OBJECTID (0xC0000000 | 0x022a) -#define NT_STATUS_OBJECTID_EXISTS (0xC0000000 | 0x022b) -#define NT_STATUS_CONVERT_TO_LARGE (0xC0000000 | 0x022c) -#define NT_STATUS_RETRY (0xC0000000 | 0x022d) -#define NT_STATUS_FOUND_OUT_OF_SCOPE (0xC0000000 | 0x022e) -#define NT_STATUS_ALLOCATE_BUCKET (0xC0000000 | 0x022f) -#define NT_STATUS_PROPSET_NOT_FOUND (0xC0000000 | 0x0230) -#define NT_STATUS_MARSHALL_OVERFLOW (0xC0000000 | 0x0231) -#define NT_STATUS_INVALID_VARIANT (0xC0000000 | 0x0232) -#define NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND (0xC0000000 | 0x0233) -#define NT_STATUS_ACCOUNT_LOCKED_OUT (0xC0000000 | 0x0234) -#define NT_STATUS_HANDLE_NOT_CLOSABLE (0xC0000000 | 0x0235) -#define NT_STATUS_CONNECTION_REFUSED (0xC0000000 | 0x0236) -#define NT_STATUS_GRACEFUL_DISCONNECT (0xC0000000 | 0x0237) -#define NT_STATUS_ADDRESS_ALREADY_ASSOCIATED (0xC0000000 | 0x0238) -#define NT_STATUS_ADDRESS_NOT_ASSOCIATED (0xC0000000 | 0x0239) -#define NT_STATUS_CONNECTION_INVALID (0xC0000000 | 0x023a) -#define NT_STATUS_CONNECTION_ACTIVE (0xC0000000 | 0x023b) -#define NT_STATUS_NETWORK_UNREACHABLE (0xC0000000 | 0x023c) -#define NT_STATUS_HOST_UNREACHABLE (0xC0000000 | 0x023d) -#define NT_STATUS_PROTOCOL_UNREACHABLE (0xC0000000 | 0x023e) -#define NT_STATUS_PORT_UNREACHABLE (0xC0000000 | 0x023f) -#define NT_STATUS_REQUEST_ABORTED (0xC0000000 | 0x0240) -#define NT_STATUS_CONNECTION_ABORTED (0xC0000000 | 0x0241) -#define NT_STATUS_BAD_COMPRESSION_BUFFER (0xC0000000 | 0x0242) -#define NT_STATUS_USER_MAPPED_FILE (0xC0000000 | 0x0243) -#define NT_STATUS_AUDIT_FAILED (0xC0000000 | 0x0244) -#define NT_STATUS_TIMER_RESOLUTION_NOT_SET (0xC0000000 | 0x0245) -#define NT_STATUS_CONNECTION_COUNT_LIMIT (0xC0000000 | 0x0246) -#define NT_STATUS_LOGIN_TIME_RESTRICTION (0xC0000000 | 0x0247) -#define NT_STATUS_LOGIN_WKSTA_RESTRICTION (0xC0000000 | 0x0248) -#define NT_STATUS_IMAGE_MP_UP_MISMATCH (0xC0000000 | 0x0249) -#define NT_STATUS_INSUFFICIENT_LOGON_INFO (0xC0000000 | 0x0250) -#define NT_STATUS_BAD_DLL_ENTRYPOINT (0xC0000000 | 0x0251) -#define NT_STATUS_BAD_SERVICE_ENTRYPOINT (0xC0000000 | 0x0252) -#define NT_STATUS_LPC_REPLY_LOST (0xC0000000 | 0x0253) -#define NT_STATUS_IP_ADDRESS_CONFLICT1 (0xC0000000 | 0x0254) -#define NT_STATUS_IP_ADDRESS_CONFLICT2 (0xC0000000 | 0x0255) -#define NT_STATUS_REGISTRY_QUOTA_LIMIT (0xC0000000 | 0x0256) -#define NT_STATUS_PATH_NOT_COVERED (0xC0000000 | 0x0257) -#define NT_STATUS_NO_CALLBACK_ACTIVE (0xC0000000 | 0x0258) -#define NT_STATUS_LICENSE_QUOTA_EXCEEDED (0xC0000000 | 0x0259) -#define NT_STATUS_PWD_TOO_SHORT (0xC0000000 | 0x025a) -#define NT_STATUS_PWD_TOO_RECENT (0xC0000000 | 0x025b) -#define NT_STATUS_PWD_HISTORY_CONFLICT (0xC0000000 | 0x025c) -#define NT_STATUS_PLUGPLAY_NO_DEVICE (0xC0000000 | 0x025e) -#define NT_STATUS_UNSUPPORTED_COMPRESSION (0xC0000000 | 0x025f) -#define NT_STATUS_INVALID_HW_PROFILE (0xC0000000 | 0x0260) -#define NT_STATUS_INVALID_PLUGPLAY_DEVICE_PATH (0xC0000000 | 0x0261) -#define NT_STATUS_DRIVER_ORDINAL_NOT_FOUND (0xC0000000 | 0x0262) -#define NT_STATUS_DRIVER_ENTRYPOINT_NOT_FOUND (0xC0000000 | 0x0263) -#define NT_STATUS_RESOURCE_NOT_OWNED (0xC0000000 | 0x0264) -#define NT_STATUS_TOO_MANY_LINKS (0xC0000000 | 0x0265) -#define NT_STATUS_QUOTA_LIST_INCONSISTENT (0xC0000000 | 0x0266) -#define NT_STATUS_FILE_IS_OFFLINE (0xC0000000 | 0x0267) -#define NT_STATUS_NETWORK_SESSION_EXPIRED (0xC0000000 | 0x035c) -#define NT_STATUS_NO_SUCH_JOB (0xC0000000 | 0xEDE) /* scheduler */ -#define NT_STATUS_NO_PREAUTH_INTEGRITY_HASH_OVERLAP (0xC0000000 | 0x5D0000) -#define NT_STATUS_PENDING 0x00000103 -#endif /* _NTERR_H */ diff --git a/fs/ksmbd/ntlmssp.h b/fs/ksmbd/ntlmssp.h deleted file mode 100644 index f13153c18b4e9..0000000000000 --- a/fs/ksmbd/ntlmssp.h +++ /dev/null @@ -1,169 +0,0 @@ -/* SPDX-License-Identifier: LGPL-2.1+ */ -/* - * Copyright (c) International Business Machines Corp., 2002,2007 - * Author(s): Steve French (sfrench@us.ibm.com) - */ - -#ifndef __KSMBD_NTLMSSP_H -#define __KSMBD_NTLMSSP_H - -#define NTLMSSP_SIGNATURE "NTLMSSP" - -/* Security blob target info data */ -#define TGT_Name "KSMBD" - -/* - * Size of the crypto key returned on the negotiate SMB in bytes - */ -#define CIFS_CRYPTO_KEY_SIZE (8) -#define CIFS_KEY_SIZE (40) - -/* - * Size of encrypted user password in bytes - */ -#define CIFS_ENCPWD_SIZE (16) -#define CIFS_CPHTXT_SIZE (16) - -/* Message Types */ -#define NtLmNegotiate cpu_to_le32(1) -#define NtLmChallenge cpu_to_le32(2) -#define NtLmAuthenticate cpu_to_le32(3) -#define UnknownMessage cpu_to_le32(8) - -/* Negotiate Flags */ -#define NTLMSSP_NEGOTIATE_UNICODE 0x01 /* Text strings are unicode */ -#define NTLMSSP_NEGOTIATE_OEM 0x02 /* Text strings are in OEM */ -#define NTLMSSP_REQUEST_TARGET 0x04 /* Srv returns its auth realm */ -/* define reserved9 0x08 */ -#define NTLMSSP_NEGOTIATE_SIGN 0x0010 /* Request signing capability */ -#define NTLMSSP_NEGOTIATE_SEAL 0x0020 /* Request confidentiality */ -#define NTLMSSP_NEGOTIATE_DGRAM 0x0040 -#define NTLMSSP_NEGOTIATE_LM_KEY 0x0080 /* Use LM session key */ -/* defined reserved 8 0x0100 */ -#define NTLMSSP_NEGOTIATE_NTLM 0x0200 /* NTLM authentication */ -#define NTLMSSP_NEGOTIATE_NT_ONLY 0x0400 /* Lanman not allowed */ -#define NTLMSSP_ANONYMOUS 0x0800 -#define NTLMSSP_NEGOTIATE_DOMAIN_SUPPLIED 0x1000 /* reserved6 */ -#define NTLMSSP_NEGOTIATE_WORKSTATION_SUPPLIED 0x2000 -#define NTLMSSP_NEGOTIATE_LOCAL_CALL 0x4000 /* client/server same machine */ -#define NTLMSSP_NEGOTIATE_ALWAYS_SIGN 0x8000 /* Sign. All security levels */ -#define NTLMSSP_TARGET_TYPE_DOMAIN 0x10000 -#define NTLMSSP_TARGET_TYPE_SERVER 0x20000 -#define NTLMSSP_TARGET_TYPE_SHARE 0x40000 -#define NTLMSSP_NEGOTIATE_EXTENDED_SEC 0x80000 /* NB:not related to NTLMv2 pwd*/ -/* #define NTLMSSP_REQUEST_INIT_RESP 0x100000 */ -#define NTLMSSP_NEGOTIATE_IDENTIFY 0x100000 -#define NTLMSSP_REQUEST_ACCEPT_RESP 0x200000 /* reserved5 */ -#define NTLMSSP_REQUEST_NON_NT_KEY 0x400000 -#define NTLMSSP_NEGOTIATE_TARGET_INFO 0x800000 -/* #define reserved4 0x1000000 */ -#define NTLMSSP_NEGOTIATE_VERSION 0x2000000 /* we do not set */ -/* #define reserved3 0x4000000 */ -/* #define reserved2 0x8000000 */ -/* #define reserved1 0x10000000 */ -#define NTLMSSP_NEGOTIATE_128 0x20000000 -#define NTLMSSP_NEGOTIATE_KEY_XCH 0x40000000 -#define NTLMSSP_NEGOTIATE_56 0x80000000 - -/* Define AV Pair Field IDs */ -enum av_field_type { - NTLMSSP_AV_EOL = 0, - NTLMSSP_AV_NB_COMPUTER_NAME, - NTLMSSP_AV_NB_DOMAIN_NAME, - NTLMSSP_AV_DNS_COMPUTER_NAME, - NTLMSSP_AV_DNS_DOMAIN_NAME, - NTLMSSP_AV_DNS_TREE_NAME, - NTLMSSP_AV_FLAGS, - NTLMSSP_AV_TIMESTAMP, - NTLMSSP_AV_RESTRICTION, - NTLMSSP_AV_TARGET_NAME, - NTLMSSP_AV_CHANNEL_BINDINGS -}; - -/* Although typedefs are not commonly used for structure definitions */ -/* in the Linux kernel, in this particular case they are useful */ -/* to more closely match the standards document for NTLMSSP from */ -/* OpenGroup and to make the code more closely match the standard in */ -/* appearance */ - -struct security_buffer { - __le16 Length; - __le16 MaximumLength; - __le32 BufferOffset; /* offset to buffer */ -} __packed; - -struct target_info { - __le16 Type; - __le16 Length; - __u8 Content[]; -} __packed; - -struct negotiate_message { - __u8 Signature[sizeof(NTLMSSP_SIGNATURE)]; - __le32 MessageType; /* NtLmNegotiate = 1 */ - __le32 NegotiateFlags; - struct security_buffer DomainName; /* RFC 1001 style and ASCII */ - struct security_buffer WorkstationName; /* RFC 1001 and ASCII */ - /* - * struct security_buffer for version info not present since we - * do not set the version is present flag - */ - char DomainString[]; - /* followed by WorkstationString */ -} __packed; - -struct challenge_message { - __u8 Signature[sizeof(NTLMSSP_SIGNATURE)]; - __le32 MessageType; /* NtLmChallenge = 2 */ - struct security_buffer TargetName; - __le32 NegotiateFlags; - __u8 Challenge[CIFS_CRYPTO_KEY_SIZE]; - __u8 Reserved[8]; - struct security_buffer TargetInfoArray; - /* - * struct security_buffer for version info not present since we - * do not set the version is present flag - */ -} __packed; - -struct authenticate_message { - __u8 Signature[sizeof(NTLMSSP_SIGNATURE)]; - __le32 MessageType; /* NtLmsAuthenticate = 3 */ - struct security_buffer LmChallengeResponse; - struct security_buffer NtChallengeResponse; - struct security_buffer DomainName; - struct security_buffer UserName; - struct security_buffer WorkstationName; - struct security_buffer SessionKey; - __le32 NegotiateFlags; - /* - * struct security_buffer for version info not present since we - * do not set the version is present flag - */ - char UserString[]; -} __packed; - -struct ntlmv2_resp { - char ntlmv2_hash[CIFS_ENCPWD_SIZE]; - __le32 blob_signature; - __u32 reserved; - __le64 time; - __u64 client_chal; /* random */ - __u32 reserved2; - /* array of name entries could follow ending in minimum 4 byte struct */ -} __packed; - -/* per smb session structure/fields */ -struct ntlmssp_auth { - /* whether session key is per smb session */ - bool sesskey_per_smbsess; - /* sent by client in type 1 ntlmsssp exchange */ - __u32 client_flags; - /* sent by server in type 2 ntlmssp exchange */ - __u32 conn_flags; - /* sent to server */ - unsigned char ciphertext[CIFS_CPHTXT_SIZE]; - /* used by ntlmssp */ - char cryptkey[CIFS_CRYPTO_KEY_SIZE]; -}; -#endif /* __KSMBD_NTLMSSP_H */ diff --git a/fs/ksmbd/oplock.c b/fs/ksmbd/oplock.c deleted file mode 100644 index 4b210cdd75569..0000000000000 --- a/fs/ksmbd/oplock.c +++ /dev/null @@ -1,1722 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-or-later -/* - * Copyright (C) 2016 Namjae Jeon - * Copyright (C) 2018 Samsung Electronics Co., Ltd. - */ - -#include - -#include "glob.h" -#include "oplock.h" - -#include "smb_common.h" -#include "smbstatus.h" -#include "connection.h" -#include "mgmt/user_session.h" -#include "mgmt/share_config.h" -#include "mgmt/tree_connect.h" - -static LIST_HEAD(lease_table_list); -static DEFINE_RWLOCK(lease_list_lock); - -/** - * alloc_opinfo() - allocate a new opinfo object for oplock info - * @work: smb work - * @id: fid of open file - * @Tid: tree id of connection - * - * Return: allocated opinfo object on success, otherwise NULL - */ -static struct oplock_info *alloc_opinfo(struct ksmbd_work *work, - u64 id, __u16 Tid) -{ - struct ksmbd_conn *conn = work->conn; - struct ksmbd_session *sess = work->sess; - struct oplock_info *opinfo; - - opinfo = kzalloc(sizeof(struct oplock_info), GFP_KERNEL); - if (!opinfo) - return NULL; - - opinfo->sess = sess; - opinfo->conn = conn; - opinfo->level = SMB2_OPLOCK_LEVEL_NONE; - opinfo->op_state = OPLOCK_STATE_NONE; - opinfo->pending_break = 0; - opinfo->fid = id; - opinfo->Tid = Tid; - INIT_LIST_HEAD(&opinfo->op_entry); - INIT_LIST_HEAD(&opinfo->interim_list); - init_waitqueue_head(&opinfo->oplock_q); - init_waitqueue_head(&opinfo->oplock_brk); - atomic_set(&opinfo->refcount, 1); - atomic_set(&opinfo->breaking_cnt, 0); - - return opinfo; -} - -static void lease_add_list(struct oplock_info *opinfo) -{ - struct lease_table *lb = opinfo->o_lease->l_lb; - - spin_lock(&lb->lb_lock); - list_add_rcu(&opinfo->lease_entry, &lb->lease_list); - spin_unlock(&lb->lb_lock); -} - -static void lease_del_list(struct oplock_info *opinfo) -{ - struct lease_table *lb = opinfo->o_lease->l_lb; - - if (!lb) - return; - - spin_lock(&lb->lb_lock); - if (list_empty(&opinfo->lease_entry)) { - spin_unlock(&lb->lb_lock); - return; - } - - list_del_init(&opinfo->lease_entry); - opinfo->o_lease->l_lb = NULL; - spin_unlock(&lb->lb_lock); -} - -static void lb_add(struct lease_table *lb) -{ - write_lock(&lease_list_lock); - list_add(&lb->l_entry, &lease_table_list); - write_unlock(&lease_list_lock); -} - -static int alloc_lease(struct oplock_info *opinfo, struct lease_ctx_info *lctx) -{ - struct lease *lease; - - lease = kmalloc(sizeof(struct lease), GFP_KERNEL); - if (!lease) - return -ENOMEM; - - memcpy(lease->lease_key, lctx->lease_key, SMB2_LEASE_KEY_SIZE); - lease->state = lctx->req_state; - lease->new_state = 0; - lease->flags = lctx->flags; - lease->duration = lctx->duration; - memcpy(lease->parent_lease_key, lctx->parent_lease_key, SMB2_LEASE_KEY_SIZE); - lease->version = lctx->version; - lease->epoch = 0; - INIT_LIST_HEAD(&opinfo->lease_entry); - opinfo->o_lease = lease; - - return 0; -} - -static void free_lease(struct oplock_info *opinfo) -{ - struct lease *lease; - - lease = opinfo->o_lease; - kfree(lease); -} - -static void free_opinfo(struct oplock_info *opinfo) -{ - if (opinfo->is_lease) - free_lease(opinfo); - kfree(opinfo); -} - -static inline void opinfo_free_rcu(struct rcu_head *rcu_head) -{ - struct oplock_info *opinfo; - - opinfo = container_of(rcu_head, struct oplock_info, rcu_head); - free_opinfo(opinfo); -} - -struct oplock_info *opinfo_get(struct ksmbd_file *fp) -{ - struct oplock_info *opinfo; - - rcu_read_lock(); - opinfo = rcu_dereference(fp->f_opinfo); - if (opinfo && !atomic_inc_not_zero(&opinfo->refcount)) - opinfo = NULL; - rcu_read_unlock(); - - return opinfo; -} - -static struct oplock_info *opinfo_get_list(struct ksmbd_inode *ci) -{ - struct oplock_info *opinfo; - - if (list_empty(&ci->m_op_list)) - return NULL; - - rcu_read_lock(); - opinfo = list_first_or_null_rcu(&ci->m_op_list, struct oplock_info, - op_entry); - if (opinfo) { - if (!atomic_inc_not_zero(&opinfo->refcount)) - opinfo = NULL; - else { - atomic_inc(&opinfo->conn->r_count); - if (ksmbd_conn_releasing(opinfo->conn)) { - atomic_dec(&opinfo->conn->r_count); - atomic_dec(&opinfo->refcount); - opinfo = NULL; - } - } - } - - rcu_read_unlock(); - - return opinfo; -} - -static void opinfo_conn_put(struct oplock_info *opinfo) -{ - struct ksmbd_conn *conn; - - if (!opinfo) - return; - - conn = opinfo->conn; - /* - * Checking waitqueue to dropping pending requests on - * disconnection. waitqueue_active is safe because it - * uses atomic operation for condition. - */ - if (!atomic_dec_return(&conn->r_count) && waitqueue_active(&conn->r_count_q)) - wake_up(&conn->r_count_q); - opinfo_put(opinfo); -} - -void opinfo_put(struct oplock_info *opinfo) -{ - if (!atomic_dec_and_test(&opinfo->refcount)) - return; - - call_rcu(&opinfo->rcu_head, opinfo_free_rcu); -} - -static void opinfo_add(struct oplock_info *opinfo) -{ - struct ksmbd_inode *ci = opinfo->o_fp->f_ci; - - write_lock(&ci->m_lock); - list_add_rcu(&opinfo->op_entry, &ci->m_op_list); - write_unlock(&ci->m_lock); -} - -static void opinfo_del(struct oplock_info *opinfo) -{ - struct ksmbd_inode *ci = opinfo->o_fp->f_ci; - - if (opinfo->is_lease) { - write_lock(&lease_list_lock); - lease_del_list(opinfo); - write_unlock(&lease_list_lock); - } - write_lock(&ci->m_lock); - list_del_rcu(&opinfo->op_entry); - write_unlock(&ci->m_lock); -} - -static unsigned long opinfo_count(struct ksmbd_file *fp) -{ - if (ksmbd_stream_fd(fp)) - return atomic_read(&fp->f_ci->sop_count); - else - return atomic_read(&fp->f_ci->op_count); -} - -static void opinfo_count_inc(struct ksmbd_file *fp) -{ - if (ksmbd_stream_fd(fp)) - return atomic_inc(&fp->f_ci->sop_count); - else - return atomic_inc(&fp->f_ci->op_count); -} - -static void opinfo_count_dec(struct ksmbd_file *fp) -{ - if (ksmbd_stream_fd(fp)) - return atomic_dec(&fp->f_ci->sop_count); - else - return atomic_dec(&fp->f_ci->op_count); -} - -/** - * opinfo_write_to_read() - convert a write oplock to read oplock - * @opinfo: current oplock info - * - * Return: 0 on success, otherwise -EINVAL - */ -int opinfo_write_to_read(struct oplock_info *opinfo) -{ - struct lease *lease = opinfo->o_lease; - - if (!(opinfo->level == SMB2_OPLOCK_LEVEL_BATCH || - opinfo->level == SMB2_OPLOCK_LEVEL_EXCLUSIVE)) { - pr_err("bad oplock(0x%x)\n", opinfo->level); - if (opinfo->is_lease) - pr_err("lease state(0x%x)\n", lease->state); - return -EINVAL; - } - opinfo->level = SMB2_OPLOCK_LEVEL_II; - - if (opinfo->is_lease) - lease->state = lease->new_state; - return 0; -} - -/** - * opinfo_read_handle_to_read() - convert a read/handle oplock to read oplock - * @opinfo: current oplock info - * - * Return: 0 on success, otherwise -EINVAL - */ -int opinfo_read_handle_to_read(struct oplock_info *opinfo) -{ - struct lease *lease = opinfo->o_lease; - - lease->state = lease->new_state; - opinfo->level = SMB2_OPLOCK_LEVEL_II; - return 0; -} - -/** - * opinfo_write_to_none() - convert a write oplock to none - * @opinfo: current oplock info - * - * Return: 0 on success, otherwise -EINVAL - */ -int opinfo_write_to_none(struct oplock_info *opinfo) -{ - struct lease *lease = opinfo->o_lease; - - if (!(opinfo->level == SMB2_OPLOCK_LEVEL_BATCH || - opinfo->level == SMB2_OPLOCK_LEVEL_EXCLUSIVE)) { - pr_err("bad oplock(0x%x)\n", opinfo->level); - if (opinfo->is_lease) - pr_err("lease state(0x%x)\n", lease->state); - return -EINVAL; - } - opinfo->level = SMB2_OPLOCK_LEVEL_NONE; - if (opinfo->is_lease) - lease->state = lease->new_state; - return 0; -} - -/** - * opinfo_read_to_none() - convert a write read to none - * @opinfo: current oplock info - * - * Return: 0 on success, otherwise -EINVAL - */ -int opinfo_read_to_none(struct oplock_info *opinfo) -{ - struct lease *lease = opinfo->o_lease; - - if (opinfo->level != SMB2_OPLOCK_LEVEL_II) { - pr_err("bad oplock(0x%x)\n", opinfo->level); - if (opinfo->is_lease) - pr_err("lease state(0x%x)\n", lease->state); - return -EINVAL; - } - opinfo->level = SMB2_OPLOCK_LEVEL_NONE; - if (opinfo->is_lease) - lease->state = lease->new_state; - return 0; -} - -/** - * lease_read_to_write() - upgrade lease state from read to write - * @opinfo: current lease info - * - * Return: 0 on success, otherwise -EINVAL - */ -int lease_read_to_write(struct oplock_info *opinfo) -{ - struct lease *lease = opinfo->o_lease; - - if (!(lease->state & SMB2_LEASE_READ_CACHING_LE)) { - ksmbd_debug(OPLOCK, "bad lease state(0x%x)\n", lease->state); - return -EINVAL; - } - - lease->new_state = SMB2_LEASE_NONE_LE; - lease->state |= SMB2_LEASE_WRITE_CACHING_LE; - if (lease->state & SMB2_LEASE_HANDLE_CACHING_LE) - opinfo->level = SMB2_OPLOCK_LEVEL_BATCH; - else - opinfo->level = SMB2_OPLOCK_LEVEL_EXCLUSIVE; - return 0; -} - -/** - * lease_none_upgrade() - upgrade lease state from none - * @opinfo: current lease info - * @new_state: new lease state - * - * Return: 0 on success, otherwise -EINVAL - */ -static int lease_none_upgrade(struct oplock_info *opinfo, __le32 new_state) -{ - struct lease *lease = opinfo->o_lease; - - if (!(lease->state == SMB2_LEASE_NONE_LE)) { - ksmbd_debug(OPLOCK, "bad lease state(0x%x)\n", lease->state); - return -EINVAL; - } - - lease->new_state = SMB2_LEASE_NONE_LE; - lease->state = new_state; - if (lease->state & SMB2_LEASE_HANDLE_CACHING_LE) - if (lease->state & SMB2_LEASE_WRITE_CACHING_LE) - opinfo->level = SMB2_OPLOCK_LEVEL_BATCH; - else - opinfo->level = SMB2_OPLOCK_LEVEL_II; - else if (lease->state & SMB2_LEASE_WRITE_CACHING_LE) - opinfo->level = SMB2_OPLOCK_LEVEL_EXCLUSIVE; - else if (lease->state & SMB2_LEASE_READ_CACHING_LE) - opinfo->level = SMB2_OPLOCK_LEVEL_II; - - return 0; -} - -/** - * close_id_del_oplock() - release oplock object at file close time - * @fp: ksmbd file pointer - */ -void close_id_del_oplock(struct ksmbd_file *fp) -{ - struct oplock_info *opinfo; - - if (S_ISDIR(file_inode(fp->filp)->i_mode)) - return; - - opinfo = opinfo_get(fp); - if (!opinfo) - return; - - opinfo_del(opinfo); - - rcu_assign_pointer(fp->f_opinfo, NULL); - if (opinfo->op_state == OPLOCK_ACK_WAIT) { - opinfo->op_state = OPLOCK_CLOSING; - wake_up_interruptible_all(&opinfo->oplock_q); - if (opinfo->is_lease) { - atomic_set(&opinfo->breaking_cnt, 0); - wake_up_interruptible_all(&opinfo->oplock_brk); - } - } - - opinfo_count_dec(fp); - atomic_dec(&opinfo->refcount); - opinfo_put(opinfo); -} - -/** - * grant_write_oplock() - grant exclusive/batch oplock or write lease - * @opinfo_new: new oplock info object - * @req_oplock: request oplock - * @lctx: lease context information - * - * Return: 0 - */ -static void grant_write_oplock(struct oplock_info *opinfo_new, int req_oplock, - struct lease_ctx_info *lctx) -{ - struct lease *lease = opinfo_new->o_lease; - - if (req_oplock == SMB2_OPLOCK_LEVEL_BATCH) - opinfo_new->level = SMB2_OPLOCK_LEVEL_BATCH; - else - opinfo_new->level = SMB2_OPLOCK_LEVEL_EXCLUSIVE; - - if (lctx) { - lease->state = lctx->req_state; - memcpy(lease->lease_key, lctx->lease_key, SMB2_LEASE_KEY_SIZE); - } -} - -/** - * grant_read_oplock() - grant level2 oplock or read lease - * @opinfo_new: new oplock info object - * @lctx: lease context information - * - * Return: 0 - */ -static void grant_read_oplock(struct oplock_info *opinfo_new, - struct lease_ctx_info *lctx) -{ - struct lease *lease = opinfo_new->o_lease; - - opinfo_new->level = SMB2_OPLOCK_LEVEL_II; - - if (lctx) { - lease->state = SMB2_LEASE_READ_CACHING_LE; - if (lctx->req_state & SMB2_LEASE_HANDLE_CACHING_LE) - lease->state |= SMB2_LEASE_HANDLE_CACHING_LE; - memcpy(lease->lease_key, lctx->lease_key, SMB2_LEASE_KEY_SIZE); - } -} - -/** - * grant_none_oplock() - grant none oplock or none lease - * @opinfo_new: new oplock info object - * @lctx: lease context information - * - * Return: 0 - */ -static void grant_none_oplock(struct oplock_info *opinfo_new, - struct lease_ctx_info *lctx) -{ - struct lease *lease = opinfo_new->o_lease; - - opinfo_new->level = SMB2_OPLOCK_LEVEL_NONE; - - if (lctx) { - lease->state = 0; - memcpy(lease->lease_key, lctx->lease_key, SMB2_LEASE_KEY_SIZE); - } -} - -static inline int compare_guid_key(struct oplock_info *opinfo, - const char *guid1, const char *key1) -{ - const char *guid2, *key2; - - guid2 = opinfo->conn->ClientGUID; - key2 = opinfo->o_lease->lease_key; - if (!memcmp(guid1, guid2, SMB2_CLIENT_GUID_SIZE) && - !memcmp(key1, key2, SMB2_LEASE_KEY_SIZE)) - return 1; - - return 0; -} - -/** - * same_client_has_lease() - check whether current lease request is - * from lease owner of file - * @ci: master file pointer - * @client_guid: Client GUID - * @lctx: lease context information - * - * Return: oplock(lease) object on success, otherwise NULL - */ -static struct oplock_info *same_client_has_lease(struct ksmbd_inode *ci, - char *client_guid, - struct lease_ctx_info *lctx) -{ - int ret; - struct lease *lease; - struct oplock_info *opinfo; - struct oplock_info *m_opinfo = NULL; - - if (!lctx) - return NULL; - - /* - * Compare lease key and client_guid to know request from same owner - * of same client - */ - read_lock(&ci->m_lock); - list_for_each_entry(opinfo, &ci->m_op_list, op_entry) { - if (!opinfo->is_lease) - continue; - read_unlock(&ci->m_lock); - lease = opinfo->o_lease; - - ret = compare_guid_key(opinfo, client_guid, lctx->lease_key); - if (ret) { - m_opinfo = opinfo; - /* skip upgrading lease about breaking lease */ - if (atomic_read(&opinfo->breaking_cnt)) { - read_lock(&ci->m_lock); - continue; - } - - /* upgrading lease */ - if ((atomic_read(&ci->op_count) + - atomic_read(&ci->sop_count)) == 1) { - if (lease->state == - (lctx->req_state & lease->state)) { - lease->state |= lctx->req_state; - if (lctx->req_state & - SMB2_LEASE_WRITE_CACHING_LE) - lease_read_to_write(opinfo); - } - } else if ((atomic_read(&ci->op_count) + - atomic_read(&ci->sop_count)) > 1) { - if (lctx->req_state == - (SMB2_LEASE_READ_CACHING_LE | - SMB2_LEASE_HANDLE_CACHING_LE)) - lease->state = lctx->req_state; - } - - if (lctx->req_state && lease->state == - SMB2_LEASE_NONE_LE) - lease_none_upgrade(opinfo, lctx->req_state); - } - read_lock(&ci->m_lock); - } - read_unlock(&ci->m_lock); - - return m_opinfo; -} - -static void wait_for_break_ack(struct oplock_info *opinfo) -{ - int rc = 0; - - rc = wait_event_interruptible_timeout(opinfo->oplock_q, - opinfo->op_state == OPLOCK_STATE_NONE || - opinfo->op_state == OPLOCK_CLOSING, - OPLOCK_WAIT_TIME); - - /* is this a timeout ? */ - if (!rc) { - if (opinfo->is_lease) - opinfo->o_lease->state = SMB2_LEASE_NONE_LE; - opinfo->level = SMB2_OPLOCK_LEVEL_NONE; - opinfo->op_state = OPLOCK_STATE_NONE; - } -} - -static void wake_up_oplock_break(struct oplock_info *opinfo) -{ - clear_bit_unlock(0, &opinfo->pending_break); - /* memory barrier is needed for wake_up_bit() */ - smp_mb__after_atomic(); - wake_up_bit(&opinfo->pending_break, 0); -} - -static int oplock_break_pending(struct oplock_info *opinfo, int req_op_level) -{ - while (test_and_set_bit(0, &opinfo->pending_break)) { - wait_on_bit(&opinfo->pending_break, 0, TASK_UNINTERRUPTIBLE); - - /* Not immediately break to none. */ - opinfo->open_trunc = 0; - - if (opinfo->op_state == OPLOCK_CLOSING) - return -ENOENT; - else if (!opinfo->is_lease && opinfo->level <= req_op_level) - return 1; - } - - if (!opinfo->is_lease && opinfo->level <= req_op_level) { - wake_up_oplock_break(opinfo); - return 1; - } - return 0; -} - -static inline int allocate_oplock_break_buf(struct ksmbd_work *work) -{ - work->response_buf = kzalloc(MAX_CIFS_SMALL_BUFFER_SIZE, GFP_KERNEL); - if (!work->response_buf) - return -ENOMEM; - work->response_sz = MAX_CIFS_SMALL_BUFFER_SIZE; - return 0; -} - -/** - * __smb2_oplock_break_noti() - send smb2 oplock break cmd from conn - * to client - * @wk: smb work object - * - * There are two ways this function can be called. 1- while file open we break - * from exclusive/batch lock to levelII oplock and 2- while file write/truncate - * we break from levelII oplock no oplock. - * work->request_buf contains oplock_info. - */ -static void __smb2_oplock_break_noti(struct work_struct *wk) -{ - struct smb2_oplock_break *rsp = NULL; - struct ksmbd_work *work = container_of(wk, struct ksmbd_work, work); - struct ksmbd_conn *conn = work->conn; - struct oplock_break_info *br_info = work->request_buf; - struct smb2_hdr *rsp_hdr; - struct ksmbd_file *fp; - - fp = ksmbd_lookup_durable_fd(br_info->fid); - if (!fp) - goto out; - - if (allocate_oplock_break_buf(work)) { - pr_err("smb2_allocate_rsp_buf failed! "); - ksmbd_fd_put(work, fp); - goto out; - } - - rsp_hdr = smb2_get_msg(work->response_buf); - memset(rsp_hdr, 0, sizeof(struct smb2_hdr) + 2); - *(__be32 *)work->response_buf = - cpu_to_be32(conn->vals->header_size); - rsp_hdr->ProtocolId = SMB2_PROTO_NUMBER; - rsp_hdr->StructureSize = SMB2_HEADER_STRUCTURE_SIZE; - rsp_hdr->CreditRequest = cpu_to_le16(0); - rsp_hdr->Command = SMB2_OPLOCK_BREAK; - rsp_hdr->Flags = (SMB2_FLAGS_SERVER_TO_REDIR); - rsp_hdr->NextCommand = 0; - rsp_hdr->MessageId = cpu_to_le64(-1); - rsp_hdr->Id.SyncId.ProcessId = 0; - rsp_hdr->Id.SyncId.TreeId = 0; - rsp_hdr->SessionId = 0; - memset(rsp_hdr->Signature, 0, 16); - - rsp = smb2_get_msg(work->response_buf); - - rsp->StructureSize = cpu_to_le16(24); - if (!br_info->open_trunc && - (br_info->level == SMB2_OPLOCK_LEVEL_BATCH || - br_info->level == SMB2_OPLOCK_LEVEL_EXCLUSIVE)) - rsp->OplockLevel = SMB2_OPLOCK_LEVEL_II; - else - rsp->OplockLevel = SMB2_OPLOCK_LEVEL_NONE; - rsp->Reserved = 0; - rsp->Reserved2 = 0; - rsp->PersistentFid = fp->persistent_id; - rsp->VolatileFid = fp->volatile_id; - - inc_rfc1001_len(work->response_buf, 24); - - ksmbd_debug(OPLOCK, - "sending oplock break v_id %llu p_id = %llu lock level = %d\n", - rsp->VolatileFid, rsp->PersistentFid, rsp->OplockLevel); - - ksmbd_fd_put(work, fp); - ksmbd_conn_write(work); - -out: - ksmbd_free_work_struct(work); -} - -/** - * smb2_oplock_break_noti() - send smb2 exclusive/batch to level2 oplock - * break command from server to client - * @opinfo: oplock info object - * - * Return: 0 on success, otherwise error - */ -static int smb2_oplock_break_noti(struct oplock_info *opinfo) -{ - struct ksmbd_conn *conn = opinfo->conn; - struct oplock_break_info *br_info; - int ret = 0; - struct ksmbd_work *work = ksmbd_alloc_work_struct(); - - if (!work) - return -ENOMEM; - - br_info = kmalloc(sizeof(struct oplock_break_info), GFP_KERNEL); - if (!br_info) { - ksmbd_free_work_struct(work); - return -ENOMEM; - } - - br_info->level = opinfo->level; - br_info->fid = opinfo->fid; - br_info->open_trunc = opinfo->open_trunc; - - work->request_buf = (char *)br_info; - work->conn = conn; - work->sess = opinfo->sess; - - if (opinfo->op_state == OPLOCK_ACK_WAIT) { - INIT_WORK(&work->work, __smb2_oplock_break_noti); - ksmbd_queue_work(work); - - wait_for_break_ack(opinfo); - } else { - __smb2_oplock_break_noti(&work->work); - if (opinfo->level == SMB2_OPLOCK_LEVEL_II) - opinfo->level = SMB2_OPLOCK_LEVEL_NONE; - } - return ret; -} - -/** - * __smb2_lease_break_noti() - send lease break command from server - * to client - * @wk: smb work object - */ -static void __smb2_lease_break_noti(struct work_struct *wk) -{ - struct smb2_lease_break *rsp = NULL; - struct ksmbd_work *work = container_of(wk, struct ksmbd_work, work); - struct lease_break_info *br_info = work->request_buf; - struct ksmbd_conn *conn = work->conn; - struct smb2_hdr *rsp_hdr; - - if (allocate_oplock_break_buf(work)) { - ksmbd_debug(OPLOCK, "smb2_allocate_rsp_buf failed! "); - goto out; - } - - rsp_hdr = smb2_get_msg(work->response_buf); - memset(rsp_hdr, 0, sizeof(struct smb2_hdr) + 2); - *(__be32 *)work->response_buf = - cpu_to_be32(conn->vals->header_size); - rsp_hdr->ProtocolId = SMB2_PROTO_NUMBER; - rsp_hdr->StructureSize = SMB2_HEADER_STRUCTURE_SIZE; - rsp_hdr->CreditRequest = cpu_to_le16(0); - rsp_hdr->Command = SMB2_OPLOCK_BREAK; - rsp_hdr->Flags = (SMB2_FLAGS_SERVER_TO_REDIR); - rsp_hdr->NextCommand = 0; - rsp_hdr->MessageId = cpu_to_le64(-1); - rsp_hdr->Id.SyncId.ProcessId = 0; - rsp_hdr->Id.SyncId.TreeId = 0; - rsp_hdr->SessionId = 0; - memset(rsp_hdr->Signature, 0, 16); - - rsp = smb2_get_msg(work->response_buf); - rsp->StructureSize = cpu_to_le16(44); - rsp->Epoch = br_info->epoch; - rsp->Flags = 0; - - if (br_info->curr_state & (SMB2_LEASE_WRITE_CACHING_LE | - SMB2_LEASE_HANDLE_CACHING_LE)) - rsp->Flags = SMB2_NOTIFY_BREAK_LEASE_FLAG_ACK_REQUIRED; - - memcpy(rsp->LeaseKey, br_info->lease_key, SMB2_LEASE_KEY_SIZE); - rsp->CurrentLeaseState = br_info->curr_state; - rsp->NewLeaseState = br_info->new_state; - rsp->BreakReason = 0; - rsp->AccessMaskHint = 0; - rsp->ShareMaskHint = 0; - - inc_rfc1001_len(work->response_buf, 44); - - ksmbd_conn_write(work); - -out: - ksmbd_free_work_struct(work); -} - -/** - * smb2_lease_break_noti() - break lease when a new client request - * write lease - * @opinfo: conains lease state information - * - * Return: 0 on success, otherwise error - */ -static int smb2_lease_break_noti(struct oplock_info *opinfo) -{ - struct ksmbd_conn *conn = opinfo->conn; - struct list_head *tmp, *t; - struct ksmbd_work *work; - struct lease_break_info *br_info; - struct lease *lease = opinfo->o_lease; - - work = ksmbd_alloc_work_struct(); - if (!work) - return -ENOMEM; - - br_info = kmalloc(sizeof(struct lease_break_info), GFP_KERNEL); - if (!br_info) { - ksmbd_free_work_struct(work); - return -ENOMEM; - } - - br_info->curr_state = lease->state; - br_info->new_state = lease->new_state; - if (lease->version == 2) - br_info->epoch = cpu_to_le16(++lease->epoch); - else - br_info->epoch = 0; - memcpy(br_info->lease_key, lease->lease_key, SMB2_LEASE_KEY_SIZE); - - work->request_buf = (char *)br_info; - work->conn = conn; - work->sess = opinfo->sess; - - if (opinfo->op_state == OPLOCK_ACK_WAIT) { - list_for_each_safe(tmp, t, &opinfo->interim_list) { - struct ksmbd_work *in_work; - - in_work = list_entry(tmp, struct ksmbd_work, - interim_entry); - setup_async_work(in_work, NULL, NULL); - smb2_send_interim_resp(in_work, STATUS_PENDING); - list_del(&in_work->interim_entry); - } - INIT_WORK(&work->work, __smb2_lease_break_noti); - ksmbd_queue_work(work); - wait_for_break_ack(opinfo); - } else { - __smb2_lease_break_noti(&work->work); - if (opinfo->o_lease->new_state == SMB2_LEASE_NONE_LE) { - opinfo->level = SMB2_OPLOCK_LEVEL_NONE; - opinfo->o_lease->state = SMB2_LEASE_NONE_LE; - } - } - return 0; -} - -static void wait_lease_breaking(struct oplock_info *opinfo) -{ - if (!opinfo->is_lease) - return; - - wake_up_interruptible_all(&opinfo->oplock_brk); - if (atomic_read(&opinfo->breaking_cnt)) { - int ret = 0; - - ret = wait_event_interruptible_timeout(opinfo->oplock_brk, - atomic_read(&opinfo->breaking_cnt) == 0, - HZ); - if (!ret) - atomic_set(&opinfo->breaking_cnt, 0); - } -} - -static int oplock_break(struct oplock_info *brk_opinfo, int req_op_level) -{ - int err = 0; - - /* Need to break exclusive/batch oplock, write lease or overwrite_if */ - ksmbd_debug(OPLOCK, - "request to send oplock(level : 0x%x) break notification\n", - brk_opinfo->level); - - if (brk_opinfo->is_lease) { - struct lease *lease = brk_opinfo->o_lease; - - atomic_inc(&brk_opinfo->breaking_cnt); - - err = oplock_break_pending(brk_opinfo, req_op_level); - if (err) - return err < 0 ? err : 0; - - if (brk_opinfo->open_trunc) { - /* - * Create overwrite break trigger the lease break to - * none. - */ - lease->new_state = SMB2_LEASE_NONE_LE; - } else { - if (lease->state & SMB2_LEASE_WRITE_CACHING_LE) { - if (lease->state & SMB2_LEASE_HANDLE_CACHING_LE) - lease->new_state = - SMB2_LEASE_READ_CACHING_LE | - SMB2_LEASE_HANDLE_CACHING_LE; - else - lease->new_state = - SMB2_LEASE_READ_CACHING_LE; - } else { - if (lease->state & SMB2_LEASE_HANDLE_CACHING_LE) - lease->new_state = - SMB2_LEASE_READ_CACHING_LE; - else - lease->new_state = SMB2_LEASE_NONE_LE; - } - } - - if (lease->state & (SMB2_LEASE_WRITE_CACHING_LE | - SMB2_LEASE_HANDLE_CACHING_LE)) - brk_opinfo->op_state = OPLOCK_ACK_WAIT; - else - atomic_dec(&brk_opinfo->breaking_cnt); - } else { - err = oplock_break_pending(brk_opinfo, req_op_level); - if (err) - return err < 0 ? err : 0; - - if (brk_opinfo->level == SMB2_OPLOCK_LEVEL_BATCH || - brk_opinfo->level == SMB2_OPLOCK_LEVEL_EXCLUSIVE) - brk_opinfo->op_state = OPLOCK_ACK_WAIT; - } - - if (brk_opinfo->is_lease) - err = smb2_lease_break_noti(brk_opinfo); - else - err = smb2_oplock_break_noti(brk_opinfo); - - ksmbd_debug(OPLOCK, "oplock granted = %d\n", brk_opinfo->level); - if (brk_opinfo->op_state == OPLOCK_CLOSING) - err = -ENOENT; - wake_up_oplock_break(brk_opinfo); - - wait_lease_breaking(brk_opinfo); - - return err; -} - -void destroy_lease_table(struct ksmbd_conn *conn) -{ - struct lease_table *lb, *lbtmp; - struct oplock_info *opinfo; - - write_lock(&lease_list_lock); - if (list_empty(&lease_table_list)) { - write_unlock(&lease_list_lock); - return; - } - - list_for_each_entry_safe(lb, lbtmp, &lease_table_list, l_entry) { - if (conn && memcmp(lb->client_guid, conn->ClientGUID, - SMB2_CLIENT_GUID_SIZE)) - continue; -again: - rcu_read_lock(); - list_for_each_entry_rcu(opinfo, &lb->lease_list, - lease_entry) { - rcu_read_unlock(); - lease_del_list(opinfo); - goto again; - } - rcu_read_unlock(); - list_del(&lb->l_entry); - kfree(lb); - } - write_unlock(&lease_list_lock); -} - -int find_same_lease_key(struct ksmbd_session *sess, struct ksmbd_inode *ci, - struct lease_ctx_info *lctx) -{ - struct oplock_info *opinfo; - int err = 0; - struct lease_table *lb; - - if (!lctx) - return err; - - read_lock(&lease_list_lock); - if (list_empty(&lease_table_list)) { - read_unlock(&lease_list_lock); - return 0; - } - - list_for_each_entry(lb, &lease_table_list, l_entry) { - if (!memcmp(lb->client_guid, sess->ClientGUID, - SMB2_CLIENT_GUID_SIZE)) - goto found; - } - read_unlock(&lease_list_lock); - - return 0; - -found: - rcu_read_lock(); - list_for_each_entry_rcu(opinfo, &lb->lease_list, lease_entry) { - if (!atomic_inc_not_zero(&opinfo->refcount)) - continue; - rcu_read_unlock(); - if (opinfo->o_fp->f_ci == ci) - goto op_next; - err = compare_guid_key(opinfo, sess->ClientGUID, - lctx->lease_key); - if (err) { - err = -EINVAL; - ksmbd_debug(OPLOCK, - "found same lease key is already used in other files\n"); - opinfo_put(opinfo); - goto out; - } -op_next: - opinfo_put(opinfo); - rcu_read_lock(); - } - rcu_read_unlock(); - -out: - read_unlock(&lease_list_lock); - return err; -} - -static void copy_lease(struct oplock_info *op1, struct oplock_info *op2) -{ - struct lease *lease1 = op1->o_lease; - struct lease *lease2 = op2->o_lease; - - op2->level = op1->level; - lease2->state = lease1->state; - memcpy(lease2->lease_key, lease1->lease_key, - SMB2_LEASE_KEY_SIZE); - lease2->duration = lease1->duration; - lease2->flags = lease1->flags; -} - -static int add_lease_global_list(struct oplock_info *opinfo) -{ - struct lease_table *lb; - - read_lock(&lease_list_lock); - list_for_each_entry(lb, &lease_table_list, l_entry) { - if (!memcmp(lb->client_guid, opinfo->conn->ClientGUID, - SMB2_CLIENT_GUID_SIZE)) { - opinfo->o_lease->l_lb = lb; - lease_add_list(opinfo); - read_unlock(&lease_list_lock); - return 0; - } - } - read_unlock(&lease_list_lock); - - lb = kmalloc(sizeof(struct lease_table), GFP_KERNEL); - if (!lb) - return -ENOMEM; - - memcpy(lb->client_guid, opinfo->conn->ClientGUID, - SMB2_CLIENT_GUID_SIZE); - INIT_LIST_HEAD(&lb->lease_list); - spin_lock_init(&lb->lb_lock); - opinfo->o_lease->l_lb = lb; - lease_add_list(opinfo); - lb_add(lb); - return 0; -} - -static void set_oplock_level(struct oplock_info *opinfo, int level, - struct lease_ctx_info *lctx) -{ - switch (level) { - case SMB2_OPLOCK_LEVEL_BATCH: - case SMB2_OPLOCK_LEVEL_EXCLUSIVE: - grant_write_oplock(opinfo, level, lctx); - break; - case SMB2_OPLOCK_LEVEL_II: - grant_read_oplock(opinfo, lctx); - break; - default: - grant_none_oplock(opinfo, lctx); - break; - } -} - -/** - * smb_grant_oplock() - handle oplock/lease request on file open - * @work: smb work - * @req_op_level: oplock level - * @pid: id of open file - * @fp: ksmbd file pointer - * @tid: Tree id of connection - * @lctx: lease context information on file open - * @share_ret: share mode - * - * Return: 0 on success, otherwise error - */ -int smb_grant_oplock(struct ksmbd_work *work, int req_op_level, u64 pid, - struct ksmbd_file *fp, __u16 tid, - struct lease_ctx_info *lctx, int share_ret) -{ - struct ksmbd_session *sess = work->sess; - int err = 0; - struct oplock_info *opinfo = NULL, *prev_opinfo = NULL; - struct ksmbd_inode *ci = fp->f_ci; - bool prev_op_has_lease; - __le32 prev_op_state = 0; - - /* not support directory lease */ - if (S_ISDIR(file_inode(fp->filp)->i_mode)) - return 0; - - opinfo = alloc_opinfo(work, pid, tid); - if (!opinfo) - return -ENOMEM; - - if (lctx) { - err = alloc_lease(opinfo, lctx); - if (err) - goto err_out; - opinfo->is_lease = 1; - } - - /* ci does not have any oplock */ - if (!opinfo_count(fp)) - goto set_lev; - - /* grant none-oplock if second open is trunc */ - if (fp->attrib_only && fp->cdoption != FILE_OVERWRITE_IF_LE && - fp->cdoption != FILE_OVERWRITE_LE && - fp->cdoption != FILE_SUPERSEDE_LE) { - req_op_level = SMB2_OPLOCK_LEVEL_NONE; - goto set_lev; - } - - if (lctx) { - struct oplock_info *m_opinfo; - - /* is lease already granted ? */ - m_opinfo = same_client_has_lease(ci, sess->ClientGUID, - lctx); - if (m_opinfo) { - copy_lease(m_opinfo, opinfo); - if (atomic_read(&m_opinfo->breaking_cnt)) - opinfo->o_lease->flags = - SMB2_LEASE_FLAG_BREAK_IN_PROGRESS_LE; - goto out; - } - } - prev_opinfo = opinfo_get_list(ci); - if (!prev_opinfo || - (prev_opinfo->level == SMB2_OPLOCK_LEVEL_NONE && lctx)) { - opinfo_conn_put(prev_opinfo); - goto set_lev; - } - prev_op_has_lease = prev_opinfo->is_lease; - if (prev_op_has_lease) - prev_op_state = prev_opinfo->o_lease->state; - - if (share_ret < 0 && - prev_opinfo->level == SMB2_OPLOCK_LEVEL_EXCLUSIVE) { - err = share_ret; - opinfo_conn_put(prev_opinfo); - goto err_out; - } - - if (prev_opinfo->level != SMB2_OPLOCK_LEVEL_BATCH && - prev_opinfo->level != SMB2_OPLOCK_LEVEL_EXCLUSIVE) { - opinfo_conn_put(prev_opinfo); - goto op_break_not_needed; - } - - list_add(&work->interim_entry, &prev_opinfo->interim_list); - err = oplock_break(prev_opinfo, SMB2_OPLOCK_LEVEL_II); - opinfo_conn_put(prev_opinfo); - if (err == -ENOENT) - goto set_lev; - /* Check all oplock was freed by close */ - else if (err < 0) - goto err_out; - -op_break_not_needed: - if (share_ret < 0) { - err = share_ret; - goto err_out; - } - - if (req_op_level != SMB2_OPLOCK_LEVEL_NONE) - req_op_level = SMB2_OPLOCK_LEVEL_II; - - /* grant fixed oplock on stacked locking between lease and oplock */ - if (prev_op_has_lease && !lctx) - if (prev_op_state & SMB2_LEASE_HANDLE_CACHING_LE) - req_op_level = SMB2_OPLOCK_LEVEL_NONE; - - if (!prev_op_has_lease && lctx) { - req_op_level = SMB2_OPLOCK_LEVEL_II; - lctx->req_state = SMB2_LEASE_READ_CACHING_LE; - } - -set_lev: - set_oplock_level(opinfo, req_op_level, lctx); - -out: - rcu_assign_pointer(fp->f_opinfo, opinfo); - opinfo->o_fp = fp; - - opinfo_count_inc(fp); - opinfo_add(opinfo); - if (opinfo->is_lease) { - err = add_lease_global_list(opinfo); - if (err) - goto err_out; - } - - return 0; -err_out: - free_opinfo(opinfo); - return err; -} - -/** - * smb_break_all_write_oplock() - break batch/exclusive oplock to level2 - * @work: smb work - * @fp: ksmbd file pointer - * @is_trunc: truncate on open - */ -static void smb_break_all_write_oplock(struct ksmbd_work *work, - struct ksmbd_file *fp, int is_trunc) -{ - struct oplock_info *brk_opinfo; - - brk_opinfo = opinfo_get_list(fp->f_ci); - if (!brk_opinfo) - return; - if (brk_opinfo->level != SMB2_OPLOCK_LEVEL_BATCH && - brk_opinfo->level != SMB2_OPLOCK_LEVEL_EXCLUSIVE) { - opinfo_conn_put(brk_opinfo); - return; - } - - brk_opinfo->open_trunc = is_trunc; - list_add(&work->interim_entry, &brk_opinfo->interim_list); - oplock_break(brk_opinfo, SMB2_OPLOCK_LEVEL_II); - opinfo_conn_put(brk_opinfo); -} - -/** - * smb_break_all_levII_oplock() - send level2 oplock or read lease break command - * from server to client - * @work: smb work - * @fp: ksmbd file pointer - * @is_trunc: truncate on open - */ -void smb_break_all_levII_oplock(struct ksmbd_work *work, struct ksmbd_file *fp, - int is_trunc) -{ - struct oplock_info *op, *brk_op; - struct ksmbd_inode *ci; - struct ksmbd_conn *conn = work->conn; - - if (!test_share_config_flag(work->tcon->share_conf, - KSMBD_SHARE_FLAG_OPLOCKS)) - return; - - ci = fp->f_ci; - op = opinfo_get(fp); - - rcu_read_lock(); - list_for_each_entry_rcu(brk_op, &ci->m_op_list, op_entry) { - if (!atomic_inc_not_zero(&brk_op->refcount)) - continue; - - atomic_inc(&brk_op->conn->r_count); - if (ksmbd_conn_releasing(brk_op->conn)) { - atomic_dec(&brk_op->conn->r_count); - continue; - } - - rcu_read_unlock(); - if (brk_op->is_lease && (brk_op->o_lease->state & - (~(SMB2_LEASE_READ_CACHING_LE | - SMB2_LEASE_HANDLE_CACHING_LE)))) { - ksmbd_debug(OPLOCK, "unexpected lease state(0x%x)\n", - brk_op->o_lease->state); - goto next; - } else if (brk_op->level != - SMB2_OPLOCK_LEVEL_II) { - ksmbd_debug(OPLOCK, "unexpected oplock(0x%x)\n", - brk_op->level); - goto next; - } - - /* Skip oplock being break to none */ - if (brk_op->is_lease && - brk_op->o_lease->new_state == SMB2_LEASE_NONE_LE && - atomic_read(&brk_op->breaking_cnt)) - goto next; - - if (op && op->is_lease && brk_op->is_lease && - !memcmp(conn->ClientGUID, brk_op->conn->ClientGUID, - SMB2_CLIENT_GUID_SIZE) && - !memcmp(op->o_lease->lease_key, brk_op->o_lease->lease_key, - SMB2_LEASE_KEY_SIZE)) - goto next; - brk_op->open_trunc = is_trunc; - oplock_break(brk_op, SMB2_OPLOCK_LEVEL_NONE); -next: - opinfo_conn_put(brk_op); - rcu_read_lock(); - } - rcu_read_unlock(); - - if (op) - opinfo_put(op); -} - -/** - * smb_break_all_oplock() - break both batch/exclusive and level2 oplock - * @work: smb work - * @fp: ksmbd file pointer - */ -void smb_break_all_oplock(struct ksmbd_work *work, struct ksmbd_file *fp) -{ - if (!test_share_config_flag(work->tcon->share_conf, - KSMBD_SHARE_FLAG_OPLOCKS)) - return; - - smb_break_all_write_oplock(work, fp, 1); - smb_break_all_levII_oplock(work, fp, 1); -} - -/** - * smb2_map_lease_to_oplock() - map lease state to corresponding oplock type - * @lease_state: lease type - * - * Return: 0 if no mapping, otherwise corresponding oplock type - */ -__u8 smb2_map_lease_to_oplock(__le32 lease_state) -{ - if (lease_state == (SMB2_LEASE_HANDLE_CACHING_LE | - SMB2_LEASE_READ_CACHING_LE | - SMB2_LEASE_WRITE_CACHING_LE)) { - return SMB2_OPLOCK_LEVEL_BATCH; - } else if (lease_state != SMB2_LEASE_WRITE_CACHING_LE && - lease_state & SMB2_LEASE_WRITE_CACHING_LE) { - if (!(lease_state & SMB2_LEASE_HANDLE_CACHING_LE)) - return SMB2_OPLOCK_LEVEL_EXCLUSIVE; - } else if (lease_state & SMB2_LEASE_READ_CACHING_LE) { - return SMB2_OPLOCK_LEVEL_II; - } - return 0; -} - -/** - * create_lease_buf() - create lease context for open cmd response - * @rbuf: buffer to create lease context response - * @lease: buffer to stored parsed lease state information - */ -void create_lease_buf(u8 *rbuf, struct lease *lease) -{ - if (lease->version == 2) { - struct create_lease_v2 *buf = (struct create_lease_v2 *)rbuf; - - memset(buf, 0, sizeof(struct create_lease_v2)); - memcpy(buf->lcontext.LeaseKey, lease->lease_key, - SMB2_LEASE_KEY_SIZE); - buf->lcontext.LeaseFlags = lease->flags; - buf->lcontext.LeaseState = lease->state; - memcpy(buf->lcontext.ParentLeaseKey, lease->parent_lease_key, - SMB2_LEASE_KEY_SIZE); - buf->ccontext.DataOffset = cpu_to_le16(offsetof - (struct create_lease_v2, lcontext)); - buf->ccontext.DataLength = cpu_to_le32(sizeof(struct lease_context_v2)); - buf->ccontext.NameOffset = cpu_to_le16(offsetof - (struct create_lease_v2, Name)); - buf->ccontext.NameLength = cpu_to_le16(4); - buf->Name[0] = 'R'; - buf->Name[1] = 'q'; - buf->Name[2] = 'L'; - buf->Name[3] = 's'; - } else { - struct create_lease *buf = (struct create_lease *)rbuf; - - memset(buf, 0, sizeof(struct create_lease)); - memcpy(buf->lcontext.LeaseKey, lease->lease_key, SMB2_LEASE_KEY_SIZE); - buf->lcontext.LeaseFlags = lease->flags; - buf->lcontext.LeaseState = lease->state; - buf->ccontext.DataOffset = cpu_to_le16(offsetof - (struct create_lease, lcontext)); - buf->ccontext.DataLength = cpu_to_le32(sizeof(struct lease_context)); - buf->ccontext.NameOffset = cpu_to_le16(offsetof - (struct create_lease, Name)); - buf->ccontext.NameLength = cpu_to_le16(4); - buf->Name[0] = 'R'; - buf->Name[1] = 'q'; - buf->Name[2] = 'L'; - buf->Name[3] = 's'; - } -} - -/** - * parse_lease_state() - parse lease context containted in file open request - * @open_req: buffer containing smb2 file open(create) request - * - * Return: oplock state, -ENOENT if create lease context not found - */ -struct lease_ctx_info *parse_lease_state(void *open_req) -{ - struct create_context *cc; - struct smb2_create_req *req = (struct smb2_create_req *)open_req; - struct lease_ctx_info *lreq; - - cc = smb2_find_context_vals(req, SMB2_CREATE_REQUEST_LEASE, 4); - if (IS_ERR_OR_NULL(cc)) - return NULL; - - lreq = kzalloc(sizeof(struct lease_ctx_info), GFP_KERNEL); - if (!lreq) - return NULL; - - if (sizeof(struct lease_context_v2) == le32_to_cpu(cc->DataLength)) { - struct create_lease_v2 *lc = (struct create_lease_v2 *)cc; - - memcpy(lreq->lease_key, lc->lcontext.LeaseKey, SMB2_LEASE_KEY_SIZE); - lreq->req_state = lc->lcontext.LeaseState; - lreq->flags = lc->lcontext.LeaseFlags; - lreq->duration = lc->lcontext.LeaseDuration; - memcpy(lreq->parent_lease_key, lc->lcontext.ParentLeaseKey, - SMB2_LEASE_KEY_SIZE); - lreq->version = 2; - } else { - struct create_lease *lc = (struct create_lease *)cc; - - memcpy(lreq->lease_key, lc->lcontext.LeaseKey, SMB2_LEASE_KEY_SIZE); - lreq->req_state = lc->lcontext.LeaseState; - lreq->flags = lc->lcontext.LeaseFlags; - lreq->duration = lc->lcontext.LeaseDuration; - lreq->version = 1; - } - return lreq; -} - -/** - * smb2_find_context_vals() - find a particular context info in open request - * @open_req: buffer containing smb2 file open(create) request - * @tag: context name to search for - * @tag_len: the length of tag - * - * Return: pointer to requested context, NULL if @str context not found - * or error pointer if name length is invalid. - */ -struct create_context *smb2_find_context_vals(void *open_req, const char *tag, int tag_len) -{ - struct create_context *cc; - unsigned int next = 0; - char *name; - struct smb2_create_req *req = (struct smb2_create_req *)open_req; - unsigned int remain_len, name_off, name_len, value_off, value_len, - cc_len; - - /* - * CreateContextsOffset and CreateContextsLength are guaranteed to - * be valid because of ksmbd_smb2_check_message(). - */ - cc = (struct create_context *)((char *)req + - le32_to_cpu(req->CreateContextsOffset)); - remain_len = le32_to_cpu(req->CreateContextsLength); - do { - cc = (struct create_context *)((char *)cc + next); - if (remain_len < offsetof(struct create_context, Buffer)) - return ERR_PTR(-EINVAL); - - next = le32_to_cpu(cc->Next); - name_off = le16_to_cpu(cc->NameOffset); - name_len = le16_to_cpu(cc->NameLength); - value_off = le16_to_cpu(cc->DataOffset); - value_len = le32_to_cpu(cc->DataLength); - cc_len = next ? next : remain_len; - - if ((next & 0x7) != 0 || - next > remain_len || - name_off != offsetof(struct create_context, Buffer) || - name_len < 4 || - name_off + name_len > cc_len || - (value_off & 0x7) != 0 || - (value_off && (value_off < name_off + name_len)) || - ((u64)value_off + value_len > cc_len)) - return ERR_PTR(-EINVAL); - - name = (char *)cc + name_off; - if (name_len == tag_len && !memcmp(name, tag, name_len)) - return cc; - - remain_len -= next; - } while (next != 0); - - return NULL; -} - -/** - * create_durable_rsp_buf() - create durable handle context - * @cc: buffer to create durable context response - */ -void create_durable_rsp_buf(char *cc) -{ - struct create_durable_rsp *buf; - - buf = (struct create_durable_rsp *)cc; - memset(buf, 0, sizeof(struct create_durable_rsp)); - buf->ccontext.DataOffset = cpu_to_le16(offsetof - (struct create_durable_rsp, Data)); - buf->ccontext.DataLength = cpu_to_le32(8); - buf->ccontext.NameOffset = cpu_to_le16(offsetof - (struct create_durable_rsp, Name)); - buf->ccontext.NameLength = cpu_to_le16(4); - /* SMB2_CREATE_DURABLE_HANDLE_RESPONSE is "DHnQ" */ - buf->Name[0] = 'D'; - buf->Name[1] = 'H'; - buf->Name[2] = 'n'; - buf->Name[3] = 'Q'; -} - -/** - * create_durable_v2_rsp_buf() - create durable handle v2 context - * @cc: buffer to create durable context response - * @fp: ksmbd file pointer - */ -void create_durable_v2_rsp_buf(char *cc, struct ksmbd_file *fp) -{ - struct create_durable_v2_rsp *buf; - - buf = (struct create_durable_v2_rsp *)cc; - memset(buf, 0, sizeof(struct create_durable_rsp)); - buf->ccontext.DataOffset = cpu_to_le16(offsetof - (struct create_durable_rsp, Data)); - buf->ccontext.DataLength = cpu_to_le32(8); - buf->ccontext.NameOffset = cpu_to_le16(offsetof - (struct create_durable_rsp, Name)); - buf->ccontext.NameLength = cpu_to_le16(4); - /* SMB2_CREATE_DURABLE_HANDLE_RESPONSE_V2 is "DH2Q" */ - buf->Name[0] = 'D'; - buf->Name[1] = 'H'; - buf->Name[2] = '2'; - buf->Name[3] = 'Q'; - - buf->Timeout = cpu_to_le32(fp->durable_timeout); -} - -/** - * create_mxac_rsp_buf() - create query maximal access context - * @cc: buffer to create maximal access context response - * @maximal_access: maximal access - */ -void create_mxac_rsp_buf(char *cc, int maximal_access) -{ - struct create_mxac_rsp *buf; - - buf = (struct create_mxac_rsp *)cc; - memset(buf, 0, sizeof(struct create_mxac_rsp)); - buf->ccontext.DataOffset = cpu_to_le16(offsetof - (struct create_mxac_rsp, QueryStatus)); - buf->ccontext.DataLength = cpu_to_le32(8); - buf->ccontext.NameOffset = cpu_to_le16(offsetof - (struct create_mxac_rsp, Name)); - buf->ccontext.NameLength = cpu_to_le16(4); - /* SMB2_CREATE_QUERY_MAXIMAL_ACCESS_RESPONSE is "MxAc" */ - buf->Name[0] = 'M'; - buf->Name[1] = 'x'; - buf->Name[2] = 'A'; - buf->Name[3] = 'c'; - - buf->QueryStatus = STATUS_SUCCESS; - buf->MaximalAccess = cpu_to_le32(maximal_access); -} - -void create_disk_id_rsp_buf(char *cc, __u64 file_id, __u64 vol_id) -{ - struct create_disk_id_rsp *buf; - - buf = (struct create_disk_id_rsp *)cc; - memset(buf, 0, sizeof(struct create_disk_id_rsp)); - buf->ccontext.DataOffset = cpu_to_le16(offsetof - (struct create_disk_id_rsp, DiskFileId)); - buf->ccontext.DataLength = cpu_to_le32(32); - buf->ccontext.NameOffset = cpu_to_le16(offsetof - (struct create_mxac_rsp, Name)); - buf->ccontext.NameLength = cpu_to_le16(4); - /* SMB2_CREATE_QUERY_ON_DISK_ID_RESPONSE is "QFid" */ - buf->Name[0] = 'Q'; - buf->Name[1] = 'F'; - buf->Name[2] = 'i'; - buf->Name[3] = 'd'; - - buf->DiskFileId = cpu_to_le64(file_id); - buf->VolumeId = cpu_to_le64(vol_id); -} - -/** - * create_posix_rsp_buf() - create posix extension context - * @cc: buffer to create posix on posix response - * @fp: ksmbd file pointer - */ -void create_posix_rsp_buf(char *cc, struct ksmbd_file *fp) -{ - struct create_posix_rsp *buf; - struct inode *inode = file_inode(fp->filp); - struct user_namespace *user_ns = file_mnt_user_ns(fp->filp); - vfsuid_t vfsuid = i_uid_into_vfsuid(user_ns, inode); - vfsgid_t vfsgid = i_gid_into_vfsgid(user_ns, inode); - - buf = (struct create_posix_rsp *)cc; - memset(buf, 0, sizeof(struct create_posix_rsp)); - buf->ccontext.DataOffset = cpu_to_le16(offsetof - (struct create_posix_rsp, nlink)); - /* - * DataLength = nlink(4) + reparse_tag(4) + mode(4) + - * domain sid(28) + unix group sid(16). - */ - buf->ccontext.DataLength = cpu_to_le32(56); - buf->ccontext.NameOffset = cpu_to_le16(offsetof - (struct create_posix_rsp, Name)); - buf->ccontext.NameLength = cpu_to_le16(POSIX_CTXT_DATA_LEN); - /* SMB2_CREATE_TAG_POSIX is "0x93AD25509CB411E7B42383DE968BCD7C" */ - buf->Name[0] = 0x93; - buf->Name[1] = 0xAD; - buf->Name[2] = 0x25; - buf->Name[3] = 0x50; - buf->Name[4] = 0x9C; - buf->Name[5] = 0xB4; - buf->Name[6] = 0x11; - buf->Name[7] = 0xE7; - buf->Name[8] = 0xB4; - buf->Name[9] = 0x23; - buf->Name[10] = 0x83; - buf->Name[11] = 0xDE; - buf->Name[12] = 0x96; - buf->Name[13] = 0x8B; - buf->Name[14] = 0xCD; - buf->Name[15] = 0x7C; - - buf->nlink = cpu_to_le32(inode->i_nlink); - buf->reparse_tag = cpu_to_le32(fp->volatile_id); - buf->mode = cpu_to_le32(inode->i_mode & 0777); - /* - * SidBuffer(44) contain two sids(Domain sid(28), UNIX group sid(16)). - * Domain sid(28) = revision(1) + num_subauth(1) + authority(6) + - * sub_auth(4 * 4(num_subauth)) + RID(4). - * UNIX group id(16) = revision(1) + num_subauth(1) + authority(6) + - * sub_auth(4 * 1(num_subauth)) + RID(4). - */ - id_to_sid(from_kuid_munged(&init_user_ns, vfsuid_into_kuid(vfsuid)), - SIDOWNER, (struct smb_sid *)&buf->SidBuffer[0]); - id_to_sid(from_kgid_munged(&init_user_ns, vfsgid_into_kgid(vfsgid)), - SIDUNIX_GROUP, (struct smb_sid *)&buf->SidBuffer[28]); -} - -/* - * Find lease object(opinfo) for given lease key/fid from lease - * break/file close path. - */ -/** - * lookup_lease_in_table() - find a matching lease info object - * @conn: connection instance - * @lease_key: lease key to be searched for - * - * Return: opinfo if found matching opinfo, otherwise NULL - */ -struct oplock_info *lookup_lease_in_table(struct ksmbd_conn *conn, - char *lease_key) -{ - struct oplock_info *opinfo = NULL, *ret_op = NULL; - struct lease_table *lt; - int ret; - - read_lock(&lease_list_lock); - list_for_each_entry(lt, &lease_table_list, l_entry) { - if (!memcmp(lt->client_guid, conn->ClientGUID, - SMB2_CLIENT_GUID_SIZE)) - goto found; - } - - read_unlock(&lease_list_lock); - return NULL; - -found: - rcu_read_lock(); - list_for_each_entry_rcu(opinfo, <->lease_list, lease_entry) { - if (!atomic_inc_not_zero(&opinfo->refcount)) - continue; - rcu_read_unlock(); - if (!opinfo->op_state || opinfo->op_state == OPLOCK_CLOSING) - goto op_next; - if (!(opinfo->o_lease->state & - (SMB2_LEASE_HANDLE_CACHING_LE | - SMB2_LEASE_WRITE_CACHING_LE))) - goto op_next; - ret = compare_guid_key(opinfo, conn->ClientGUID, - lease_key); - if (ret) { - ksmbd_debug(OPLOCK, "found opinfo\n"); - ret_op = opinfo; - goto out; - } -op_next: - opinfo_put(opinfo); - rcu_read_lock(); - } - rcu_read_unlock(); - -out: - read_unlock(&lease_list_lock); - return ret_op; -} diff --git a/fs/ksmbd/oplock.h b/fs/ksmbd/oplock.h deleted file mode 100644 index 4b0fe6da76940..0000000000000 --- a/fs/ksmbd/oplock.h +++ /dev/null @@ -1,127 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0-or-later */ -/* - * Copyright (C) 2016 Namjae Jeon - * Copyright (C) 2018 Samsung Electronics Co., Ltd. - */ - -#ifndef __KSMBD_OPLOCK_H -#define __KSMBD_OPLOCK_H - -#include "smb_common.h" - -#define OPLOCK_WAIT_TIME (35 * HZ) - -/* SMB2 Oplock levels */ -#define SMB2_OPLOCK_LEVEL_NONE 0x00 -#define SMB2_OPLOCK_LEVEL_II 0x01 -#define SMB2_OPLOCK_LEVEL_EXCLUSIVE 0x08 -#define SMB2_OPLOCK_LEVEL_BATCH 0x09 -#define SMB2_OPLOCK_LEVEL_LEASE 0xFF - -/* Oplock states */ -#define OPLOCK_STATE_NONE 0x00 -#define OPLOCK_ACK_WAIT 0x01 -#define OPLOCK_CLOSING 0x02 - -#define OPLOCK_WRITE_TO_READ 0x01 -#define OPLOCK_READ_HANDLE_TO_READ 0x02 -#define OPLOCK_WRITE_TO_NONE 0x04 -#define OPLOCK_READ_TO_NONE 0x08 - -struct lease_ctx_info { - __u8 lease_key[SMB2_LEASE_KEY_SIZE]; - __le32 req_state; - __le32 flags; - __le64 duration; - __u8 parent_lease_key[SMB2_LEASE_KEY_SIZE]; - int version; -}; - -struct lease_table { - char client_guid[SMB2_CLIENT_GUID_SIZE]; - struct list_head lease_list; - struct list_head l_entry; - spinlock_t lb_lock; -}; - -struct lease { - __u8 lease_key[SMB2_LEASE_KEY_SIZE]; - __le32 state; - __le32 new_state; - __le32 flags; - __le64 duration; - __u8 parent_lease_key[SMB2_LEASE_KEY_SIZE]; - int version; - unsigned short epoch; - struct lease_table *l_lb; -}; - -struct oplock_info { - struct ksmbd_conn *conn; - struct ksmbd_session *sess; - struct ksmbd_work *work; - struct ksmbd_file *o_fp; - int level; - int op_state; - unsigned long pending_break; - u64 fid; - atomic_t breaking_cnt; - atomic_t refcount; - __u16 Tid; - bool is_lease; - bool open_trunc; /* truncate on open */ - struct lease *o_lease; - struct list_head interim_list; - struct list_head op_entry; - struct list_head lease_entry; - wait_queue_head_t oplock_q; /* Other server threads */ - wait_queue_head_t oplock_brk; /* oplock breaking wait */ - struct rcu_head rcu_head; -}; - -struct lease_break_info { - __le32 curr_state; - __le32 new_state; - __le16 epoch; - char lease_key[SMB2_LEASE_KEY_SIZE]; -}; - -struct oplock_break_info { - int level; - int open_trunc; - int fid; -}; - -int smb_grant_oplock(struct ksmbd_work *work, int req_op_level, - u64 pid, struct ksmbd_file *fp, __u16 tid, - struct lease_ctx_info *lctx, int share_ret); -void smb_break_all_levII_oplock(struct ksmbd_work *work, - struct ksmbd_file *fp, int is_trunc); -int opinfo_write_to_read(struct oplock_info *opinfo); -int opinfo_read_handle_to_read(struct oplock_info *opinfo); -int opinfo_write_to_none(struct oplock_info *opinfo); -int opinfo_read_to_none(struct oplock_info *opinfo); -void close_id_del_oplock(struct ksmbd_file *fp); -void smb_break_all_oplock(struct ksmbd_work *work, struct ksmbd_file *fp); -struct oplock_info *opinfo_get(struct ksmbd_file *fp); -void opinfo_put(struct oplock_info *opinfo); - -/* Lease related functions */ -void create_lease_buf(u8 *rbuf, struct lease *lease); -struct lease_ctx_info *parse_lease_state(void *open_req); -__u8 smb2_map_lease_to_oplock(__le32 lease_state); -int lease_read_to_write(struct oplock_info *opinfo); - -/* Durable related functions */ -void create_durable_rsp_buf(char *cc); -void create_durable_v2_rsp_buf(char *cc, struct ksmbd_file *fp); -void create_mxac_rsp_buf(char *cc, int maximal_access); -void create_disk_id_rsp_buf(char *cc, __u64 file_id, __u64 vol_id); -void create_posix_rsp_buf(char *cc, struct ksmbd_file *fp); -struct create_context *smb2_find_context_vals(void *open_req, const char *tag, int tag_len); -struct oplock_info *lookup_lease_in_table(struct ksmbd_conn *conn, - char *lease_key); -int find_same_lease_key(struct ksmbd_session *sess, struct ksmbd_inode *ci, - struct lease_ctx_info *lctx); -void destroy_lease_table(struct ksmbd_conn *conn); -#endif /* __KSMBD_OPLOCK_H */ diff --git a/fs/ksmbd/server.c b/fs/ksmbd/server.c deleted file mode 100644 index 8a0ad399f2456..0000000000000 --- a/fs/ksmbd/server.c +++ /dev/null @@ -1,639 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-or-later -/* - * Copyright (C) 2016 Namjae Jeon - * Copyright (C) 2018 Samsung Electronics Co., Ltd. - */ - -#include "glob.h" -#include "oplock.h" -#include "misc.h" -#include -#include -#include -#include -#include - -#include "server.h" -#include "smb_common.h" -#include "smbstatus.h" -#include "connection.h" -#include "transport_ipc.h" -#include "mgmt/user_session.h" -#include "crypto_ctx.h" -#include "auth.h" - -int ksmbd_debug_types; - -struct ksmbd_server_config server_conf; - -enum SERVER_CTRL_TYPE { - SERVER_CTRL_TYPE_INIT, - SERVER_CTRL_TYPE_RESET, -}; - -struct server_ctrl_struct { - int type; - struct work_struct ctrl_work; -}; - -static DEFINE_MUTEX(ctrl_lock); - -static int ___server_conf_set(int idx, char *val) -{ - if (idx >= ARRAY_SIZE(server_conf.conf)) - return -EINVAL; - - if (!val || val[0] == 0x00) - return -EINVAL; - - kfree(server_conf.conf[idx]); - server_conf.conf[idx] = kstrdup(val, GFP_KERNEL); - if (!server_conf.conf[idx]) - return -ENOMEM; - return 0; -} - -int ksmbd_set_netbios_name(char *v) -{ - return ___server_conf_set(SERVER_CONF_NETBIOS_NAME, v); -} - -int ksmbd_set_server_string(char *v) -{ - return ___server_conf_set(SERVER_CONF_SERVER_STRING, v); -} - -int ksmbd_set_work_group(char *v) -{ - return ___server_conf_set(SERVER_CONF_WORK_GROUP, v); -} - -char *ksmbd_netbios_name(void) -{ - return server_conf.conf[SERVER_CONF_NETBIOS_NAME]; -} - -char *ksmbd_server_string(void) -{ - return server_conf.conf[SERVER_CONF_SERVER_STRING]; -} - -char *ksmbd_work_group(void) -{ - return server_conf.conf[SERVER_CONF_WORK_GROUP]; -} - -/** - * check_conn_state() - check state of server thread connection - * @work: smb work containing server thread information - * - * Return: 0 on valid connection, otherwise 1 to reconnect - */ -static inline int check_conn_state(struct ksmbd_work *work) -{ - struct smb_hdr *rsp_hdr; - - if (ksmbd_conn_exiting(work->conn) || - ksmbd_conn_need_reconnect(work->conn)) { - rsp_hdr = work->response_buf; - rsp_hdr->Status.CifsError = STATUS_CONNECTION_DISCONNECTED; - return 1; - } - return 0; -} - -#define SERVER_HANDLER_CONTINUE 0 -#define SERVER_HANDLER_ABORT 1 - -static int __process_request(struct ksmbd_work *work, struct ksmbd_conn *conn, - u16 *cmd) -{ - struct smb_version_cmds *cmds; - u16 command; - int ret; - - if (check_conn_state(work)) - return SERVER_HANDLER_CONTINUE; - - if (ksmbd_verify_smb_message(work)) - return SERVER_HANDLER_ABORT; - - command = conn->ops->get_cmd_val(work); - *cmd = command; - -andx_again: - if (command >= conn->max_cmds) { - conn->ops->set_rsp_status(work, STATUS_INVALID_PARAMETER); - return SERVER_HANDLER_CONTINUE; - } - - cmds = &conn->cmds[command]; - if (!cmds->proc) { - ksmbd_debug(SMB, "*** not implemented yet cmd = %x\n", command); - conn->ops->set_rsp_status(work, STATUS_NOT_IMPLEMENTED); - return SERVER_HANDLER_CONTINUE; - } - - if (work->sess && conn->ops->is_sign_req(work, command)) { - ret = conn->ops->check_sign_req(work); - if (!ret) { - conn->ops->set_rsp_status(work, STATUS_ACCESS_DENIED); - return SERVER_HANDLER_CONTINUE; - } - } - - ret = cmds->proc(work); - - if (ret < 0) - ksmbd_debug(CONN, "Failed to process %u [%d]\n", command, ret); - /* AndX commands - chained request can return positive values */ - else if (ret > 0) { - command = ret; - *cmd = command; - goto andx_again; - } - - if (work->send_no_response) - return SERVER_HANDLER_ABORT; - return SERVER_HANDLER_CONTINUE; -} - -static void __handle_ksmbd_work(struct ksmbd_work *work, - struct ksmbd_conn *conn) -{ - u16 command = 0; - int rc; - - if (conn->ops->allocate_rsp_buf(work)) - return; - - if (conn->ops->is_transform_hdr && - conn->ops->is_transform_hdr(work->request_buf)) { - rc = conn->ops->decrypt_req(work); - if (rc < 0) { - conn->ops->set_rsp_status(work, STATUS_DATA_ERROR); - goto send; - } - - work->encrypted = true; - } - - rc = conn->ops->init_rsp_hdr(work); - if (rc) { - /* either uid or tid is not correct */ - conn->ops->set_rsp_status(work, STATUS_INVALID_HANDLE); - goto send; - } - - if (conn->ops->check_user_session) { - rc = conn->ops->check_user_session(work); - if (rc < 0) { - command = conn->ops->get_cmd_val(work); - conn->ops->set_rsp_status(work, - STATUS_USER_SESSION_DELETED); - goto send; - } else if (rc > 0) { - rc = conn->ops->get_ksmbd_tcon(work); - if (rc < 0) { - conn->ops->set_rsp_status(work, - STATUS_NETWORK_NAME_DELETED); - goto send; - } - } - } - - do { - rc = __process_request(work, conn, &command); - if (rc == SERVER_HANDLER_ABORT) - break; - - /* - * Call smb2_set_rsp_credits() function to set number of credits - * granted in hdr of smb2 response. - */ - if (conn->ops->set_rsp_credits) { - spin_lock(&conn->credits_lock); - rc = conn->ops->set_rsp_credits(work); - spin_unlock(&conn->credits_lock); - if (rc < 0) { - conn->ops->set_rsp_status(work, - STATUS_INVALID_PARAMETER); - goto send; - } - } - - if (work->sess && - (work->sess->sign || smb3_11_final_sess_setup_resp(work) || - conn->ops->is_sign_req(work, command))) - conn->ops->set_sign_rsp(work); - } while (is_chained_smb2_message(work)); - - if (work->send_no_response) - return; - -send: - smb3_preauth_hash_rsp(work); - if (work->sess && work->sess->enc && work->encrypted && - conn->ops->encrypt_resp) { - rc = conn->ops->encrypt_resp(work); - if (rc < 0) - conn->ops->set_rsp_status(work, STATUS_DATA_ERROR); - } - - ksmbd_conn_write(work); -} - -/** - * handle_ksmbd_work() - process pending smb work requests - * @wk: smb work containing request command buffer - * - * called by kworker threads to processing remaining smb work requests - */ -static void handle_ksmbd_work(struct work_struct *wk) -{ - struct ksmbd_work *work = container_of(wk, struct ksmbd_work, work); - struct ksmbd_conn *conn = work->conn; - - atomic64_inc(&conn->stats.request_served); - - __handle_ksmbd_work(work, conn); - - ksmbd_conn_try_dequeue_request(work); - ksmbd_free_work_struct(work); - /* - * Checking waitqueue to dropping pending requests on - * disconnection. waitqueue_active is safe because it - * uses atomic operation for condition. - */ - if (!atomic_dec_return(&conn->r_count) && waitqueue_active(&conn->r_count_q)) - wake_up(&conn->r_count_q); -} - -/** - * queue_ksmbd_work() - queue a smb request to worker thread queue - * for proccessing smb command and sending response - * @conn: connection instance - * - * read remaining data from socket create and submit work. - */ -static int queue_ksmbd_work(struct ksmbd_conn *conn) -{ - struct ksmbd_work *work; - - work = ksmbd_alloc_work_struct(); - if (!work) { - pr_err("allocation for work failed\n"); - return -ENOMEM; - } - - work->conn = conn; - work->request_buf = conn->request_buf; - conn->request_buf = NULL; - - ksmbd_init_smb_server(work); - - ksmbd_conn_enqueue_request(work); - atomic_inc(&conn->r_count); - /* update activity on connection */ - conn->last_active = jiffies; - INIT_WORK(&work->work, handle_ksmbd_work); - ksmbd_queue_work(work); - return 0; -} - -static int ksmbd_server_process_request(struct ksmbd_conn *conn) -{ - return queue_ksmbd_work(conn); -} - -static int ksmbd_server_terminate_conn(struct ksmbd_conn *conn) -{ - ksmbd_sessions_deregister(conn); - destroy_lease_table(conn); - return 0; -} - -static void ksmbd_server_tcp_callbacks_init(void) -{ - struct ksmbd_conn_ops ops; - - ops.process_fn = ksmbd_server_process_request; - ops.terminate_fn = ksmbd_server_terminate_conn; - - ksmbd_conn_init_server_callbacks(&ops); -} - -static void server_conf_free(void) -{ - int i; - - for (i = 0; i < ARRAY_SIZE(server_conf.conf); i++) { - kfree(server_conf.conf[i]); - server_conf.conf[i] = NULL; - } -} - -static int server_conf_init(void) -{ - WRITE_ONCE(server_conf.state, SERVER_STATE_STARTING_UP); - server_conf.enforced_signing = 0; - server_conf.min_protocol = ksmbd_min_protocol(); - server_conf.max_protocol = ksmbd_max_protocol(); - server_conf.auth_mechs = KSMBD_AUTH_NTLMSSP; -#ifdef CONFIG_SMB_SERVER_KERBEROS5 - server_conf.auth_mechs |= KSMBD_AUTH_KRB5 | - KSMBD_AUTH_MSKRB5; -#endif - return 0; -} - -static void server_ctrl_handle_init(struct server_ctrl_struct *ctrl) -{ - int ret; - - ret = ksmbd_conn_transport_init(); - if (ret) { - server_queue_ctrl_reset_work(); - return; - } - - WRITE_ONCE(server_conf.state, SERVER_STATE_RUNNING); -} - -static void server_ctrl_handle_reset(struct server_ctrl_struct *ctrl) -{ - ksmbd_ipc_soft_reset(); - ksmbd_conn_transport_destroy(); - server_conf_free(); - server_conf_init(); - WRITE_ONCE(server_conf.state, SERVER_STATE_STARTING_UP); -} - -static void server_ctrl_handle_work(struct work_struct *work) -{ - struct server_ctrl_struct *ctrl; - - ctrl = container_of(work, struct server_ctrl_struct, ctrl_work); - - mutex_lock(&ctrl_lock); - switch (ctrl->type) { - case SERVER_CTRL_TYPE_INIT: - server_ctrl_handle_init(ctrl); - break; - case SERVER_CTRL_TYPE_RESET: - server_ctrl_handle_reset(ctrl); - break; - default: - pr_err("Unknown server work type: %d\n", ctrl->type); - } - mutex_unlock(&ctrl_lock); - kfree(ctrl); - module_put(THIS_MODULE); -} - -static int __queue_ctrl_work(int type) -{ - struct server_ctrl_struct *ctrl; - - ctrl = kmalloc(sizeof(struct server_ctrl_struct), GFP_KERNEL); - if (!ctrl) - return -ENOMEM; - - __module_get(THIS_MODULE); - ctrl->type = type; - INIT_WORK(&ctrl->ctrl_work, server_ctrl_handle_work); - queue_work(system_long_wq, &ctrl->ctrl_work); - return 0; -} - -int server_queue_ctrl_init_work(void) -{ - return __queue_ctrl_work(SERVER_CTRL_TYPE_INIT); -} - -int server_queue_ctrl_reset_work(void) -{ - return __queue_ctrl_work(SERVER_CTRL_TYPE_RESET); -} - -static ssize_t stats_show(struct class *class, struct class_attribute *attr, - char *buf) -{ - /* - * Inc this each time you change stats output format, - * so user space will know what to do. - */ - static int stats_version = 2; - static const char * const state[] = { - "startup", - "running", - "reset", - "shutdown" - }; - - ssize_t sz = scnprintf(buf, PAGE_SIZE, "%d %s %d %lu\n", stats_version, - state[server_conf.state], server_conf.tcp_port, - server_conf.ipc_last_active / HZ); - return sz; -} - -static ssize_t kill_server_store(struct class *class, - struct class_attribute *attr, const char *buf, - size_t len) -{ - if (!sysfs_streq(buf, "hard")) - return len; - - pr_info("kill command received\n"); - mutex_lock(&ctrl_lock); - WRITE_ONCE(server_conf.state, SERVER_STATE_RESETTING); - __module_get(THIS_MODULE); - server_ctrl_handle_reset(NULL); - module_put(THIS_MODULE); - mutex_unlock(&ctrl_lock); - return len; -} - -static const char * const debug_type_strings[] = {"smb", "auth", "vfs", - "oplock", "ipc", "conn", - "rdma"}; - -static ssize_t debug_show(struct class *class, struct class_attribute *attr, - char *buf) -{ - ssize_t sz = 0; - int i, pos = 0; - - for (i = 0; i < ARRAY_SIZE(debug_type_strings); i++) { - if ((ksmbd_debug_types >> i) & 1) { - pos = scnprintf(buf + sz, - PAGE_SIZE - sz, - "[%s] ", - debug_type_strings[i]); - } else { - pos = scnprintf(buf + sz, - PAGE_SIZE - sz, - "%s ", - debug_type_strings[i]); - } - sz += pos; - } - sz += scnprintf(buf + sz, PAGE_SIZE - sz, "\n"); - return sz; -} - -static ssize_t debug_store(struct class *class, struct class_attribute *attr, - const char *buf, size_t len) -{ - int i; - - for (i = 0; i < ARRAY_SIZE(debug_type_strings); i++) { - if (sysfs_streq(buf, "all")) { - if (ksmbd_debug_types == KSMBD_DEBUG_ALL) - ksmbd_debug_types = 0; - else - ksmbd_debug_types = KSMBD_DEBUG_ALL; - break; - } - - if (sysfs_streq(buf, debug_type_strings[i])) { - if (ksmbd_debug_types & (1 << i)) - ksmbd_debug_types &= ~(1 << i); - else - ksmbd_debug_types |= (1 << i); - break; - } - } - - return len; -} - -static CLASS_ATTR_RO(stats); -static CLASS_ATTR_WO(kill_server); -static CLASS_ATTR_RW(debug); - -static struct attribute *ksmbd_control_class_attrs[] = { - &class_attr_stats.attr, - &class_attr_kill_server.attr, - &class_attr_debug.attr, - NULL, -}; -ATTRIBUTE_GROUPS(ksmbd_control_class); - -static struct class ksmbd_control_class = { - .name = "ksmbd-control", - .owner = THIS_MODULE, - .class_groups = ksmbd_control_class_groups, -}; - -static int ksmbd_server_shutdown(void) -{ - WRITE_ONCE(server_conf.state, SERVER_STATE_SHUTTING_DOWN); - - class_unregister(&ksmbd_control_class); - ksmbd_workqueue_destroy(); - ksmbd_ipc_release(); - ksmbd_conn_transport_destroy(); - ksmbd_crypto_destroy(); - ksmbd_free_global_file_table(); - destroy_lease_table(NULL); - ksmbd_work_pool_destroy(); - ksmbd_exit_file_cache(); - server_conf_free(); - return 0; -} - -static int __init ksmbd_server_init(void) -{ - int ret; - - ret = class_register(&ksmbd_control_class); - if (ret) { - pr_err("Unable to register ksmbd-control class\n"); - return ret; - } - - ksmbd_server_tcp_callbacks_init(); - - ret = server_conf_init(); - if (ret) - goto err_unregister; - - ret = ksmbd_work_pool_init(); - if (ret) - goto err_unregister; - - ret = ksmbd_init_file_cache(); - if (ret) - goto err_destroy_work_pools; - - ret = ksmbd_ipc_init(); - if (ret) - goto err_exit_file_cache; - - ret = ksmbd_init_global_file_table(); - if (ret) - goto err_ipc_release; - - ret = ksmbd_inode_hash_init(); - if (ret) - goto err_destroy_file_table; - - ret = ksmbd_crypto_create(); - if (ret) - goto err_release_inode_hash; - - ret = ksmbd_workqueue_init(); - if (ret) - goto err_crypto_destroy; - - pr_warn_once("The ksmbd server is experimental\n"); - - return 0; - -err_crypto_destroy: - ksmbd_crypto_destroy(); -err_release_inode_hash: - ksmbd_release_inode_hash(); -err_destroy_file_table: - ksmbd_free_global_file_table(); -err_ipc_release: - ksmbd_ipc_release(); -err_exit_file_cache: - ksmbd_exit_file_cache(); -err_destroy_work_pools: - ksmbd_work_pool_destroy(); -err_unregister: - class_unregister(&ksmbd_control_class); - - return ret; -} - -/** - * ksmbd_server_exit() - shutdown forker thread and free memory at module exit - */ -static void __exit ksmbd_server_exit(void) -{ - ksmbd_server_shutdown(); - rcu_barrier(); - ksmbd_release_inode_hash(); -} - -MODULE_AUTHOR("Namjae Jeon "); -MODULE_VERSION(KSMBD_VERSION); -MODULE_DESCRIPTION("Linux kernel CIFS/SMB SERVER"); -MODULE_LICENSE("GPL"); -MODULE_SOFTDEP("pre: ecb"); -MODULE_SOFTDEP("pre: hmac"); -MODULE_SOFTDEP("pre: md5"); -MODULE_SOFTDEP("pre: nls"); -MODULE_SOFTDEP("pre: aes"); -MODULE_SOFTDEP("pre: cmac"); -MODULE_SOFTDEP("pre: sha256"); -MODULE_SOFTDEP("pre: sha512"); -MODULE_SOFTDEP("pre: aead2"); -MODULE_SOFTDEP("pre: ccm"); -MODULE_SOFTDEP("pre: gcm"); -MODULE_SOFTDEP("pre: crc32"); -module_init(ksmbd_server_init) -module_exit(ksmbd_server_exit) diff --git a/fs/ksmbd/server.h b/fs/ksmbd/server.h deleted file mode 100644 index db72781817603..0000000000000 --- a/fs/ksmbd/server.h +++ /dev/null @@ -1,71 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0-or-later */ -/* - * Copyright (C) 2018 Samsung Electronics Co., Ltd. - */ - -#ifndef __SERVER_H__ -#define __SERVER_H__ - -#include "smbacl.h" - -/* - * Server state type - */ -enum { - SERVER_STATE_STARTING_UP, - SERVER_STATE_RUNNING, - SERVER_STATE_RESETTING, - SERVER_STATE_SHUTTING_DOWN, -}; - -/* - * Server global config string index - */ -enum { - SERVER_CONF_NETBIOS_NAME, - SERVER_CONF_SERVER_STRING, - SERVER_CONF_WORK_GROUP, -}; - -struct ksmbd_server_config { - unsigned int flags; - unsigned int state; - short signing; - short enforced_signing; - short min_protocol; - short max_protocol; - unsigned short tcp_port; - unsigned short ipc_timeout; - unsigned long ipc_last_active; - unsigned long deadtime; - unsigned int share_fake_fscaps; - struct smb_sid domain_sid; - unsigned int auth_mechs; - unsigned int max_connections; - - char *conf[SERVER_CONF_WORK_GROUP + 1]; -}; - -extern struct ksmbd_server_config server_conf; - -int ksmbd_set_netbios_name(char *v); -int ksmbd_set_server_string(char *v); -int ksmbd_set_work_group(char *v); - -char *ksmbd_netbios_name(void); -char *ksmbd_server_string(void); -char *ksmbd_work_group(void); - -static inline int ksmbd_server_running(void) -{ - return READ_ONCE(server_conf.state) == SERVER_STATE_RUNNING; -} - -static inline int ksmbd_server_configurable(void) -{ - return READ_ONCE(server_conf.state) < SERVER_STATE_RESETTING; -} - -int server_queue_ctrl_init_work(void); -int server_queue_ctrl_reset_work(void); -#endif /* __SERVER_H__ */ diff --git a/fs/ksmbd/smb2misc.c b/fs/ksmbd/smb2misc.c deleted file mode 100644 index 0ffe663b75906..0000000000000 --- a/fs/ksmbd/smb2misc.c +++ /dev/null @@ -1,447 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-or-later -/* - * Copyright (C) 2016 Namjae Jeon - * Copyright (C) 2018 Samsung Electronics Co., Ltd. - */ - -#include "glob.h" -#include "nterr.h" -#include "smb_common.h" -#include "smbstatus.h" -#include "mgmt/user_session.h" -#include "connection.h" - -static int check_smb2_hdr(struct smb2_hdr *hdr) -{ - /* - * Make sure that this really is an SMB, that it is a response. - */ - if (hdr->Flags & SMB2_FLAGS_SERVER_TO_REDIR) - return 1; - return 0; -} - -/* - * The following table defines the expected "StructureSize" of SMB2 requests - * in order by SMB2 command. This is similar to "wct" in SMB/CIFS requests. - * - * Note that commands are defined in smb2pdu.h in le16 but the array below is - * indexed by command in host byte order - */ -static const __le16 smb2_req_struct_sizes[NUMBER_OF_SMB2_COMMANDS] = { - /* SMB2_NEGOTIATE */ cpu_to_le16(36), - /* SMB2_SESSION_SETUP */ cpu_to_le16(25), - /* SMB2_LOGOFF */ cpu_to_le16(4), - /* SMB2_TREE_CONNECT */ cpu_to_le16(9), - /* SMB2_TREE_DISCONNECT */ cpu_to_le16(4), - /* SMB2_CREATE */ cpu_to_le16(57), - /* SMB2_CLOSE */ cpu_to_le16(24), - /* SMB2_FLUSH */ cpu_to_le16(24), - /* SMB2_READ */ cpu_to_le16(49), - /* SMB2_WRITE */ cpu_to_le16(49), - /* SMB2_LOCK */ cpu_to_le16(48), - /* SMB2_IOCTL */ cpu_to_le16(57), - /* SMB2_CANCEL */ cpu_to_le16(4), - /* SMB2_ECHO */ cpu_to_le16(4), - /* SMB2_QUERY_DIRECTORY */ cpu_to_le16(33), - /* SMB2_CHANGE_NOTIFY */ cpu_to_le16(32), - /* SMB2_QUERY_INFO */ cpu_to_le16(41), - /* SMB2_SET_INFO */ cpu_to_le16(33), - /* use 44 for lease break */ - /* SMB2_OPLOCK_BREAK */ cpu_to_le16(36) -}; - -/* - * The size of the variable area depends on the offset and length fields - * located in different fields for various SMB2 requests. SMB2 requests - * with no variable length info, show an offset of zero for the offset field. - */ -static const bool has_smb2_data_area[NUMBER_OF_SMB2_COMMANDS] = { - /* SMB2_NEGOTIATE */ true, - /* SMB2_SESSION_SETUP */ true, - /* SMB2_LOGOFF */ false, - /* SMB2_TREE_CONNECT */ true, - /* SMB2_TREE_DISCONNECT */ false, - /* SMB2_CREATE */ true, - /* SMB2_CLOSE */ false, - /* SMB2_FLUSH */ false, - /* SMB2_READ */ true, - /* SMB2_WRITE */ true, - /* SMB2_LOCK */ true, - /* SMB2_IOCTL */ true, - /* SMB2_CANCEL */ false, /* BB CHECK this not listed in documentation */ - /* SMB2_ECHO */ false, - /* SMB2_QUERY_DIRECTORY */ true, - /* SMB2_CHANGE_NOTIFY */ false, - /* SMB2_QUERY_INFO */ true, - /* SMB2_SET_INFO */ true, - /* SMB2_OPLOCK_BREAK */ false -}; - -/* - * Set length of the data area and the offset to arguments. - * if they are invalid, return error. - */ -static int smb2_get_data_area_len(unsigned int *off, unsigned int *len, - struct smb2_hdr *hdr) -{ - int ret = 0; - - *off = 0; - *len = 0; - - /* - * Following commands have data areas so we have to get the location - * of the data buffer offset and data buffer length for the particular - * command. - */ - switch (hdr->Command) { - case SMB2_SESSION_SETUP: - *off = le16_to_cpu(((struct smb2_sess_setup_req *)hdr)->SecurityBufferOffset); - *len = le16_to_cpu(((struct smb2_sess_setup_req *)hdr)->SecurityBufferLength); - break; - case SMB2_TREE_CONNECT: - *off = le16_to_cpu(((struct smb2_tree_connect_req *)hdr)->PathOffset); - *len = le16_to_cpu(((struct smb2_tree_connect_req *)hdr)->PathLength); - break; - case SMB2_CREATE: - { - if (((struct smb2_create_req *)hdr)->CreateContextsLength) { - *off = le32_to_cpu(((struct smb2_create_req *) - hdr)->CreateContextsOffset); - *len = le32_to_cpu(((struct smb2_create_req *) - hdr)->CreateContextsLength); - break; - } - - *off = le16_to_cpu(((struct smb2_create_req *)hdr)->NameOffset); - *len = le16_to_cpu(((struct smb2_create_req *)hdr)->NameLength); - break; - } - case SMB2_QUERY_INFO: - *off = le16_to_cpu(((struct smb2_query_info_req *)hdr)->InputBufferOffset); - *len = le32_to_cpu(((struct smb2_query_info_req *)hdr)->InputBufferLength); - break; - case SMB2_SET_INFO: - *off = le16_to_cpu(((struct smb2_set_info_req *)hdr)->BufferOffset); - *len = le32_to_cpu(((struct smb2_set_info_req *)hdr)->BufferLength); - break; - case SMB2_READ: - *off = le16_to_cpu(((struct smb2_read_req *)hdr)->ReadChannelInfoOffset); - *len = le16_to_cpu(((struct smb2_read_req *)hdr)->ReadChannelInfoLength); - break; - case SMB2_WRITE: - if (((struct smb2_write_req *)hdr)->DataOffset || - ((struct smb2_write_req *)hdr)->Length) { - *off = max_t(unsigned int, - le16_to_cpu(((struct smb2_write_req *)hdr)->DataOffset), - offsetof(struct smb2_write_req, Buffer)); - *len = le32_to_cpu(((struct smb2_write_req *)hdr)->Length); - break; - } - - *off = le16_to_cpu(((struct smb2_write_req *)hdr)->WriteChannelInfoOffset); - *len = le16_to_cpu(((struct smb2_write_req *)hdr)->WriteChannelInfoLength); - break; - case SMB2_QUERY_DIRECTORY: - *off = le16_to_cpu(((struct smb2_query_directory_req *)hdr)->FileNameOffset); - *len = le16_to_cpu(((struct smb2_query_directory_req *)hdr)->FileNameLength); - break; - case SMB2_LOCK: - { - unsigned short lock_count; - - lock_count = le16_to_cpu(((struct smb2_lock_req *)hdr)->LockCount); - if (lock_count > 0) { - *off = offsetof(struct smb2_lock_req, locks); - *len = sizeof(struct smb2_lock_element) * lock_count; - } - break; - } - case SMB2_IOCTL: - *off = le32_to_cpu(((struct smb2_ioctl_req *)hdr)->InputOffset); - *len = le32_to_cpu(((struct smb2_ioctl_req *)hdr)->InputCount); - break; - default: - ksmbd_debug(SMB, "no length check for command\n"); - break; - } - - if (*off > 4096) { - ksmbd_debug(SMB, "offset %d too large\n", *off); - ret = -EINVAL; - } else if ((u64)*off + *len > MAX_STREAM_PROT_LEN) { - ksmbd_debug(SMB, "Request is larger than maximum stream protocol length(%u): %llu\n", - MAX_STREAM_PROT_LEN, (u64)*off + *len); - ret = -EINVAL; - } - - return ret; -} - -/* - * Calculate the size of the SMB message based on the fixed header - * portion, the number of word parameters and the data portion of the message. - */ -static int smb2_calc_size(void *buf, unsigned int *len) -{ - struct smb2_pdu *pdu = (struct smb2_pdu *)buf; - struct smb2_hdr *hdr = &pdu->hdr; - unsigned int offset; /* the offset from the beginning of SMB to data area */ - unsigned int data_length; /* the length of the variable length data area */ - int ret; - - /* Structure Size has already been checked to make sure it is 64 */ - *len = le16_to_cpu(hdr->StructureSize); - - /* - * StructureSize2, ie length of fixed parameter area has already - * been checked to make sure it is the correct length. - */ - *len += le16_to_cpu(pdu->StructureSize2); - /* - * StructureSize2 of smb2_lock pdu is set to 48, indicating - * the size of smb2 lock request with single smb2_lock_element - * regardless of number of locks. Subtract single - * smb2_lock_element for correct buffer size check. - */ - if (hdr->Command == SMB2_LOCK) - *len -= sizeof(struct smb2_lock_element); - - if (has_smb2_data_area[le16_to_cpu(hdr->Command)] == false) - goto calc_size_exit; - - ret = smb2_get_data_area_len(&offset, &data_length, hdr); - if (ret) - return ret; - ksmbd_debug(SMB, "SMB2 data length %u offset %u\n", data_length, - offset); - - if (data_length > 0) { - /* - * Check to make sure that data area begins after fixed area, - * Note that last byte of the fixed area is part of data area - * for some commands, typically those with odd StructureSize, - * so we must add one to the calculation. - */ - if (offset + 1 < *len) { - ksmbd_debug(SMB, - "data area offset %d overlaps SMB2 header %u\n", - offset + 1, *len); - return -EINVAL; - } - - *len = offset + data_length; - } - -calc_size_exit: - ksmbd_debug(SMB, "SMB2 len %u\n", *len); - return 0; -} - -static inline int smb2_query_info_req_len(struct smb2_query_info_req *h) -{ - return le32_to_cpu(h->InputBufferLength) + - le32_to_cpu(h->OutputBufferLength); -} - -static inline int smb2_set_info_req_len(struct smb2_set_info_req *h) -{ - return le32_to_cpu(h->BufferLength); -} - -static inline int smb2_read_req_len(struct smb2_read_req *h) -{ - return le32_to_cpu(h->Length); -} - -static inline int smb2_write_req_len(struct smb2_write_req *h) -{ - return le32_to_cpu(h->Length); -} - -static inline int smb2_query_dir_req_len(struct smb2_query_directory_req *h) -{ - return le32_to_cpu(h->OutputBufferLength); -} - -static inline int smb2_ioctl_req_len(struct smb2_ioctl_req *h) -{ - return le32_to_cpu(h->InputCount) + - le32_to_cpu(h->OutputCount); -} - -static inline int smb2_ioctl_resp_len(struct smb2_ioctl_req *h) -{ - return le32_to_cpu(h->MaxInputResponse) + - le32_to_cpu(h->MaxOutputResponse); -} - -static int smb2_validate_credit_charge(struct ksmbd_conn *conn, - struct smb2_hdr *hdr) -{ - unsigned int req_len = 0, expect_resp_len = 0, calc_credit_num, max_len; - unsigned short credit_charge = le16_to_cpu(hdr->CreditCharge); - void *__hdr = hdr; - int ret = 0; - - switch (hdr->Command) { - case SMB2_QUERY_INFO: - req_len = smb2_query_info_req_len(__hdr); - break; - case SMB2_SET_INFO: - req_len = smb2_set_info_req_len(__hdr); - break; - case SMB2_READ: - req_len = smb2_read_req_len(__hdr); - break; - case SMB2_WRITE: - req_len = smb2_write_req_len(__hdr); - break; - case SMB2_QUERY_DIRECTORY: - req_len = smb2_query_dir_req_len(__hdr); - break; - case SMB2_IOCTL: - req_len = smb2_ioctl_req_len(__hdr); - expect_resp_len = smb2_ioctl_resp_len(__hdr); - break; - case SMB2_CANCEL: - return 0; - default: - req_len = 1; - break; - } - - credit_charge = max_t(unsigned short, credit_charge, 1); - max_len = max_t(unsigned int, req_len, expect_resp_len); - calc_credit_num = DIV_ROUND_UP(max_len, SMB2_MAX_BUFFER_SIZE); - - if (credit_charge < calc_credit_num) { - ksmbd_debug(SMB, "Insufficient credit charge, given: %d, needed: %d\n", - credit_charge, calc_credit_num); - return 1; - } else if (credit_charge > conn->vals->max_credits) { - ksmbd_debug(SMB, "Too large credit charge: %d\n", credit_charge); - return 1; - } - - spin_lock(&conn->credits_lock); - if (credit_charge > conn->total_credits) { - ksmbd_debug(SMB, "Insufficient credits granted, given: %u, granted: %u\n", - credit_charge, conn->total_credits); - ret = 1; - } - - if ((u64)conn->outstanding_credits + credit_charge > conn->total_credits) { - ksmbd_debug(SMB, "Limits exceeding the maximum allowable outstanding requests, given : %u, pending : %u\n", - credit_charge, conn->outstanding_credits); - ret = 1; - } else - conn->outstanding_credits += credit_charge; - - spin_unlock(&conn->credits_lock); - - return ret; -} - -int ksmbd_smb2_check_message(struct ksmbd_work *work) -{ - struct smb2_pdu *pdu = ksmbd_req_buf_next(work); - struct smb2_hdr *hdr = &pdu->hdr; - int command; - __u32 clc_len; /* calculated length */ - __u32 len = get_rfc1002_len(work->request_buf); - - if (le32_to_cpu(hdr->NextCommand) > 0) - len = le32_to_cpu(hdr->NextCommand); - else if (work->next_smb2_rcv_hdr_off) - len -= work->next_smb2_rcv_hdr_off; - - if (check_smb2_hdr(hdr)) - return 1; - - if (hdr->StructureSize != SMB2_HEADER_STRUCTURE_SIZE) { - ksmbd_debug(SMB, "Illegal structure size %u\n", - le16_to_cpu(hdr->StructureSize)); - return 1; - } - - command = le16_to_cpu(hdr->Command); - if (command >= NUMBER_OF_SMB2_COMMANDS) { - ksmbd_debug(SMB, "Illegal SMB2 command %d\n", command); - return 1; - } - - if (smb2_req_struct_sizes[command] != pdu->StructureSize2) { - if (command != SMB2_OPLOCK_BREAK_HE && - (hdr->Status == 0 || pdu->StructureSize2 != SMB2_ERROR_STRUCTURE_SIZE2_LE)) { - /* error packets have 9 byte structure size */ - ksmbd_debug(SMB, - "Illegal request size %u for command %d\n", - le16_to_cpu(pdu->StructureSize2), command); - return 1; - } else if (command == SMB2_OPLOCK_BREAK_HE && - hdr->Status == 0 && - le16_to_cpu(pdu->StructureSize2) != OP_BREAK_STRUCT_SIZE_20 && - le16_to_cpu(pdu->StructureSize2) != OP_BREAK_STRUCT_SIZE_21) { - /* special case for SMB2.1 lease break message */ - ksmbd_debug(SMB, - "Illegal request size %d for oplock break\n", - le16_to_cpu(pdu->StructureSize2)); - return 1; - } - } - - if (smb2_calc_size(hdr, &clc_len)) - return 1; - - if (len != clc_len) { - /* client can return one byte more due to implied bcc[0] */ - if (clc_len == len + 1) - goto validate_credit; - - /* - * Some windows servers (win2016) will pad also the final - * PDU in a compound to 8 bytes. - */ - if (ALIGN(clc_len, 8) == len) - goto validate_credit; - - /* - * SMB2 NEGOTIATE request will be validated when message - * handling proceeds. - */ - if (command == SMB2_NEGOTIATE_HE) - goto validate_credit; - - /* - * Allow a message that padded to 8byte boundary. - * Linux 4.19.217 with smb 3.0.2 are sometimes - * sending messages where the cls_len is exactly - * 8 bytes less than len. - */ - if (clc_len < len && (len - clc_len) <= 8) - goto validate_credit; - - pr_err_ratelimited( - "cli req too short, len %d not %d. cmd:%d mid:%llu\n", - len, clc_len, command, - le64_to_cpu(hdr->MessageId)); - - return 1; - } - -validate_credit: - if ((work->conn->vals->capabilities & SMB2_GLOBAL_CAP_LARGE_MTU) && - smb2_validate_credit_charge(work->conn, hdr)) { - work->conn->ops->set_rsp_status(work, STATUS_INVALID_PARAMETER); - return 1; - } - - return 0; -} - -int smb2_negotiate_request(struct ksmbd_work *work) -{ - return ksmbd_smb_negotiate_common(work, SMB2_NEGOTIATE_HE); -} diff --git a/fs/ksmbd/smb2ops.c b/fs/ksmbd/smb2ops.c deleted file mode 100644 index ab23da2120b94..0000000000000 --- a/fs/ksmbd/smb2ops.c +++ /dev/null @@ -1,314 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-or-later -/* - * Copyright (C) 2016 Namjae Jeon - * Copyright (C) 2018 Samsung Electronics Co., Ltd. - */ - -#include -#include "glob.h" - -#include "auth.h" -#include "connection.h" -#include "smb_common.h" -#include "server.h" - -static struct smb_version_values smb21_server_values = { - .version_string = SMB21_VERSION_STRING, - .protocol_id = SMB21_PROT_ID, - .capabilities = SMB2_GLOBAL_CAP_LARGE_MTU, - .max_read_size = SMB21_DEFAULT_IOSIZE, - .max_write_size = SMB21_DEFAULT_IOSIZE, - .max_trans_size = SMB21_DEFAULT_IOSIZE, - .max_credits = SMB2_MAX_CREDITS, - .large_lock_type = 0, - .exclusive_lock_type = SMB2_LOCKFLAG_EXCLUSIVE, - .shared_lock_type = SMB2_LOCKFLAG_SHARED, - .unlock_lock_type = SMB2_LOCKFLAG_UNLOCK, - .header_size = sizeof(struct smb2_hdr), - .max_header_size = MAX_SMB2_HDR_SIZE, - .read_rsp_size = sizeof(struct smb2_read_rsp) - 1, - .lock_cmd = SMB2_LOCK, - .cap_unix = 0, - .cap_nt_find = SMB2_NT_FIND, - .cap_large_files = SMB2_LARGE_FILES, - .create_lease_size = sizeof(struct create_lease), - .create_durable_size = sizeof(struct create_durable_rsp), - .create_mxac_size = sizeof(struct create_mxac_rsp), - .create_disk_id_size = sizeof(struct create_disk_id_rsp), - .create_posix_size = sizeof(struct create_posix_rsp), -}; - -static struct smb_version_values smb30_server_values = { - .version_string = SMB30_VERSION_STRING, - .protocol_id = SMB30_PROT_ID, - .capabilities = SMB2_GLOBAL_CAP_LARGE_MTU, - .max_read_size = SMB3_DEFAULT_IOSIZE, - .max_write_size = SMB3_DEFAULT_IOSIZE, - .max_trans_size = SMB3_DEFAULT_TRANS_SIZE, - .max_credits = SMB2_MAX_CREDITS, - .large_lock_type = 0, - .exclusive_lock_type = SMB2_LOCKFLAG_EXCLUSIVE, - .shared_lock_type = SMB2_LOCKFLAG_SHARED, - .unlock_lock_type = SMB2_LOCKFLAG_UNLOCK, - .header_size = sizeof(struct smb2_hdr), - .max_header_size = MAX_SMB2_HDR_SIZE, - .read_rsp_size = sizeof(struct smb2_read_rsp) - 1, - .lock_cmd = SMB2_LOCK, - .cap_unix = 0, - .cap_nt_find = SMB2_NT_FIND, - .cap_large_files = SMB2_LARGE_FILES, - .create_lease_size = sizeof(struct create_lease_v2), - .create_durable_size = sizeof(struct create_durable_rsp), - .create_durable_v2_size = sizeof(struct create_durable_v2_rsp), - .create_mxac_size = sizeof(struct create_mxac_rsp), - .create_disk_id_size = sizeof(struct create_disk_id_rsp), - .create_posix_size = sizeof(struct create_posix_rsp), -}; - -static struct smb_version_values smb302_server_values = { - .version_string = SMB302_VERSION_STRING, - .protocol_id = SMB302_PROT_ID, - .capabilities = SMB2_GLOBAL_CAP_LARGE_MTU, - .max_read_size = SMB3_DEFAULT_IOSIZE, - .max_write_size = SMB3_DEFAULT_IOSIZE, - .max_trans_size = SMB3_DEFAULT_TRANS_SIZE, - .max_credits = SMB2_MAX_CREDITS, - .large_lock_type = 0, - .exclusive_lock_type = SMB2_LOCKFLAG_EXCLUSIVE, - .shared_lock_type = SMB2_LOCKFLAG_SHARED, - .unlock_lock_type = SMB2_LOCKFLAG_UNLOCK, - .header_size = sizeof(struct smb2_hdr), - .max_header_size = MAX_SMB2_HDR_SIZE, - .read_rsp_size = sizeof(struct smb2_read_rsp) - 1, - .lock_cmd = SMB2_LOCK, - .cap_unix = 0, - .cap_nt_find = SMB2_NT_FIND, - .cap_large_files = SMB2_LARGE_FILES, - .create_lease_size = sizeof(struct create_lease_v2), - .create_durable_size = sizeof(struct create_durable_rsp), - .create_durable_v2_size = sizeof(struct create_durable_v2_rsp), - .create_mxac_size = sizeof(struct create_mxac_rsp), - .create_disk_id_size = sizeof(struct create_disk_id_rsp), - .create_posix_size = sizeof(struct create_posix_rsp), -}; - -static struct smb_version_values smb311_server_values = { - .version_string = SMB311_VERSION_STRING, - .protocol_id = SMB311_PROT_ID, - .capabilities = SMB2_GLOBAL_CAP_LARGE_MTU, - .max_read_size = SMB3_DEFAULT_IOSIZE, - .max_write_size = SMB3_DEFAULT_IOSIZE, - .max_trans_size = SMB3_DEFAULT_TRANS_SIZE, - .max_credits = SMB2_MAX_CREDITS, - .large_lock_type = 0, - .exclusive_lock_type = SMB2_LOCKFLAG_EXCLUSIVE, - .shared_lock_type = SMB2_LOCKFLAG_SHARED, - .unlock_lock_type = SMB2_LOCKFLAG_UNLOCK, - .header_size = sizeof(struct smb2_hdr), - .max_header_size = MAX_SMB2_HDR_SIZE, - .read_rsp_size = sizeof(struct smb2_read_rsp) - 1, - .lock_cmd = SMB2_LOCK, - .cap_unix = 0, - .cap_nt_find = SMB2_NT_FIND, - .cap_large_files = SMB2_LARGE_FILES, - .create_lease_size = sizeof(struct create_lease_v2), - .create_durable_size = sizeof(struct create_durable_rsp), - .create_durable_v2_size = sizeof(struct create_durable_v2_rsp), - .create_mxac_size = sizeof(struct create_mxac_rsp), - .create_disk_id_size = sizeof(struct create_disk_id_rsp), - .create_posix_size = sizeof(struct create_posix_rsp), -}; - -static struct smb_version_ops smb2_0_server_ops = { - .get_cmd_val = get_smb2_cmd_val, - .init_rsp_hdr = init_smb2_rsp_hdr, - .set_rsp_status = set_smb2_rsp_status, - .allocate_rsp_buf = smb2_allocate_rsp_buf, - .set_rsp_credits = smb2_set_rsp_credits, - .check_user_session = smb2_check_user_session, - .get_ksmbd_tcon = smb2_get_ksmbd_tcon, - .is_sign_req = smb2_is_sign_req, - .check_sign_req = smb2_check_sign_req, - .set_sign_rsp = smb2_set_sign_rsp -}; - -static struct smb_version_ops smb3_0_server_ops = { - .get_cmd_val = get_smb2_cmd_val, - .init_rsp_hdr = init_smb2_rsp_hdr, - .set_rsp_status = set_smb2_rsp_status, - .allocate_rsp_buf = smb2_allocate_rsp_buf, - .set_rsp_credits = smb2_set_rsp_credits, - .check_user_session = smb2_check_user_session, - .get_ksmbd_tcon = smb2_get_ksmbd_tcon, - .is_sign_req = smb2_is_sign_req, - .check_sign_req = smb3_check_sign_req, - .set_sign_rsp = smb3_set_sign_rsp, - .generate_signingkey = ksmbd_gen_smb30_signingkey, - .generate_encryptionkey = ksmbd_gen_smb30_encryptionkey, - .is_transform_hdr = smb3_is_transform_hdr, - .decrypt_req = smb3_decrypt_req, - .encrypt_resp = smb3_encrypt_resp -}; - -static struct smb_version_ops smb3_11_server_ops = { - .get_cmd_val = get_smb2_cmd_val, - .init_rsp_hdr = init_smb2_rsp_hdr, - .set_rsp_status = set_smb2_rsp_status, - .allocate_rsp_buf = smb2_allocate_rsp_buf, - .set_rsp_credits = smb2_set_rsp_credits, - .check_user_session = smb2_check_user_session, - .get_ksmbd_tcon = smb2_get_ksmbd_tcon, - .is_sign_req = smb2_is_sign_req, - .check_sign_req = smb3_check_sign_req, - .set_sign_rsp = smb3_set_sign_rsp, - .generate_signingkey = ksmbd_gen_smb311_signingkey, - .generate_encryptionkey = ksmbd_gen_smb311_encryptionkey, - .is_transform_hdr = smb3_is_transform_hdr, - .decrypt_req = smb3_decrypt_req, - .encrypt_resp = smb3_encrypt_resp -}; - -static struct smb_version_cmds smb2_0_server_cmds[NUMBER_OF_SMB2_COMMANDS] = { - [SMB2_NEGOTIATE_HE] = { .proc = smb2_negotiate_request, }, - [SMB2_SESSION_SETUP_HE] = { .proc = smb2_sess_setup, }, - [SMB2_TREE_CONNECT_HE] = { .proc = smb2_tree_connect,}, - [SMB2_TREE_DISCONNECT_HE] = { .proc = smb2_tree_disconnect,}, - [SMB2_LOGOFF_HE] = { .proc = smb2_session_logoff,}, - [SMB2_CREATE_HE] = { .proc = smb2_open}, - [SMB2_QUERY_INFO_HE] = { .proc = smb2_query_info}, - [SMB2_QUERY_DIRECTORY_HE] = { .proc = smb2_query_dir}, - [SMB2_CLOSE_HE] = { .proc = smb2_close}, - [SMB2_ECHO_HE] = { .proc = smb2_echo}, - [SMB2_SET_INFO_HE] = { .proc = smb2_set_info}, - [SMB2_READ_HE] = { .proc = smb2_read}, - [SMB2_WRITE_HE] = { .proc = smb2_write}, - [SMB2_FLUSH_HE] = { .proc = smb2_flush}, - [SMB2_CANCEL_HE] = { .proc = smb2_cancel}, - [SMB2_LOCK_HE] = { .proc = smb2_lock}, - [SMB2_IOCTL_HE] = { .proc = smb2_ioctl}, - [SMB2_OPLOCK_BREAK_HE] = { .proc = smb2_oplock_break}, - [SMB2_CHANGE_NOTIFY_HE] = { .proc = smb2_notify}, -}; - -/** - * init_smb2_1_server() - initialize a smb server connection with smb2.1 - * command dispatcher - * @conn: connection instance - */ -void init_smb2_1_server(struct ksmbd_conn *conn) -{ - conn->vals = &smb21_server_values; - conn->ops = &smb2_0_server_ops; - conn->cmds = smb2_0_server_cmds; - conn->max_cmds = ARRAY_SIZE(smb2_0_server_cmds); - conn->signing_algorithm = SIGNING_ALG_HMAC_SHA256_LE; - - if (server_conf.flags & KSMBD_GLOBAL_FLAG_SMB2_LEASES) - conn->vals->capabilities |= SMB2_GLOBAL_CAP_LEASING; -} - -/** - * init_smb3_0_server() - initialize a smb server connection with smb3.0 - * command dispatcher - * @conn: connection instance - */ -void init_smb3_0_server(struct ksmbd_conn *conn) -{ - conn->vals = &smb30_server_values; - conn->ops = &smb3_0_server_ops; - conn->cmds = smb2_0_server_cmds; - conn->max_cmds = ARRAY_SIZE(smb2_0_server_cmds); - conn->signing_algorithm = SIGNING_ALG_AES_CMAC_LE; - - if (server_conf.flags & KSMBD_GLOBAL_FLAG_SMB2_LEASES) - conn->vals->capabilities |= SMB2_GLOBAL_CAP_LEASING; - - if (server_conf.flags & KSMBD_GLOBAL_FLAG_SMB2_ENCRYPTION && - conn->cli_cap & SMB2_GLOBAL_CAP_ENCRYPTION) - conn->vals->capabilities |= SMB2_GLOBAL_CAP_ENCRYPTION; - - if (server_conf.flags & KSMBD_GLOBAL_FLAG_SMB3_MULTICHANNEL) - conn->vals->capabilities |= SMB2_GLOBAL_CAP_MULTI_CHANNEL; -} - -/** - * init_smb3_02_server() - initialize a smb server connection with smb3.02 - * command dispatcher - * @conn: connection instance - */ -void init_smb3_02_server(struct ksmbd_conn *conn) -{ - conn->vals = &smb302_server_values; - conn->ops = &smb3_0_server_ops; - conn->cmds = smb2_0_server_cmds; - conn->max_cmds = ARRAY_SIZE(smb2_0_server_cmds); - conn->signing_algorithm = SIGNING_ALG_AES_CMAC_LE; - - if (server_conf.flags & KSMBD_GLOBAL_FLAG_SMB2_LEASES) - conn->vals->capabilities |= SMB2_GLOBAL_CAP_LEASING; - - if (server_conf.flags & KSMBD_GLOBAL_FLAG_SMB2_ENCRYPTION && - conn->cli_cap & SMB2_GLOBAL_CAP_ENCRYPTION) - conn->vals->capabilities |= SMB2_GLOBAL_CAP_ENCRYPTION; - - if (server_conf.flags & KSMBD_GLOBAL_FLAG_SMB3_MULTICHANNEL) - conn->vals->capabilities |= SMB2_GLOBAL_CAP_MULTI_CHANNEL; -} - -/** - * init_smb3_11_server() - initialize a smb server connection with smb3.11 - * command dispatcher - * @conn: connection instance - */ -int init_smb3_11_server(struct ksmbd_conn *conn) -{ - conn->vals = &smb311_server_values; - conn->ops = &smb3_11_server_ops; - conn->cmds = smb2_0_server_cmds; - conn->max_cmds = ARRAY_SIZE(smb2_0_server_cmds); - conn->signing_algorithm = SIGNING_ALG_AES_CMAC_LE; - - if (server_conf.flags & KSMBD_GLOBAL_FLAG_SMB2_LEASES) - conn->vals->capabilities |= SMB2_GLOBAL_CAP_LEASING; - - if (server_conf.flags & KSMBD_GLOBAL_FLAG_SMB3_MULTICHANNEL) - conn->vals->capabilities |= SMB2_GLOBAL_CAP_MULTI_CHANNEL; - - INIT_LIST_HEAD(&conn->preauth_sess_table); - return 0; -} - -void init_smb2_max_read_size(unsigned int sz) -{ - sz = clamp_val(sz, SMB3_MIN_IOSIZE, SMB3_MAX_IOSIZE); - smb21_server_values.max_read_size = sz; - smb30_server_values.max_read_size = sz; - smb302_server_values.max_read_size = sz; - smb311_server_values.max_read_size = sz; -} - -void init_smb2_max_write_size(unsigned int sz) -{ - sz = clamp_val(sz, SMB3_MIN_IOSIZE, SMB3_MAX_IOSIZE); - smb21_server_values.max_write_size = sz; - smb30_server_values.max_write_size = sz; - smb302_server_values.max_write_size = sz; - smb311_server_values.max_write_size = sz; -} - -void init_smb2_max_trans_size(unsigned int sz) -{ - sz = clamp_val(sz, SMB3_MIN_IOSIZE, SMB3_MAX_IOSIZE); - smb21_server_values.max_trans_size = sz; - smb30_server_values.max_trans_size = sz; - smb302_server_values.max_trans_size = sz; - smb311_server_values.max_trans_size = sz; -} - -void init_smb2_max_credits(unsigned int sz) -{ - smb21_server_values.max_credits = sz; - smb30_server_values.max_credits = sz; - smb302_server_values.max_credits = sz; - smb311_server_values.max_credits = sz; -} diff --git a/fs/ksmbd/smb2pdu.c b/fs/ksmbd/smb2pdu.c deleted file mode 100644 index 56f661e5ef628..0000000000000 --- a/fs/ksmbd/smb2pdu.c +++ /dev/null @@ -1,8693 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-or-later -/* - * Copyright (C) 2016 Namjae Jeon - * Copyright (C) 2018 Samsung Electronics Co., Ltd. - */ - -#include -#include -#include -#include -#include -#include -#include -#include - -#include "glob.h" -#include "smbfsctl.h" -#include "oplock.h" -#include "smbacl.h" - -#include "auth.h" -#include "asn1.h" -#include "connection.h" -#include "transport_ipc.h" -#include "transport_rdma.h" -#include "vfs.h" -#include "vfs_cache.h" -#include "misc.h" - -#include "server.h" -#include "smb_common.h" -#include "smbstatus.h" -#include "ksmbd_work.h" -#include "mgmt/user_config.h" -#include "mgmt/share_config.h" -#include "mgmt/tree_connect.h" -#include "mgmt/user_session.h" -#include "mgmt/ksmbd_ida.h" -#include "ndr.h" - -static void __wbuf(struct ksmbd_work *work, void **req, void **rsp) -{ - if (work->next_smb2_rcv_hdr_off) { - *req = ksmbd_req_buf_next(work); - *rsp = ksmbd_resp_buf_next(work); - } else { - *req = smb2_get_msg(work->request_buf); - *rsp = smb2_get_msg(work->response_buf); - } -} - -#define WORK_BUFFERS(w, rq, rs) __wbuf((w), (void **)&(rq), (void **)&(rs)) - -/** - * check_session_id() - check for valid session id in smb header - * @conn: connection instance - * @id: session id from smb header - * - * Return: 1 if valid session id, otherwise 0 - */ -static inline bool check_session_id(struct ksmbd_conn *conn, u64 id) -{ - struct ksmbd_session *sess; - - if (id == 0 || id == -1) - return false; - - sess = ksmbd_session_lookup_all(conn, id); - if (sess) - return true; - pr_err("Invalid user session id: %llu\n", id); - return false; -} - -struct channel *lookup_chann_list(struct ksmbd_session *sess, struct ksmbd_conn *conn) -{ - return xa_load(&sess->ksmbd_chann_list, (long)conn); -} - -/** - * smb2_get_ksmbd_tcon() - get tree connection information using a tree id. - * @work: smb work - * - * Return: 0 if there is a tree connection matched or these are - * skipable commands, otherwise error - */ -int smb2_get_ksmbd_tcon(struct ksmbd_work *work) -{ - struct smb2_hdr *req_hdr = smb2_get_msg(work->request_buf); - unsigned int cmd = le16_to_cpu(req_hdr->Command); - int tree_id; - - work->tcon = NULL; - if (cmd == SMB2_TREE_CONNECT_HE || - cmd == SMB2_CANCEL_HE || - cmd == SMB2_LOGOFF_HE) { - ksmbd_debug(SMB, "skip to check tree connect request\n"); - return 0; - } - - if (xa_empty(&work->sess->tree_conns)) { - ksmbd_debug(SMB, "NO tree connected\n"); - return -ENOENT; - } - - tree_id = le32_to_cpu(req_hdr->Id.SyncId.TreeId); - work->tcon = ksmbd_tree_conn_lookup(work->sess, tree_id); - if (!work->tcon) { - pr_err("Invalid tid %d\n", tree_id); - return -EINVAL; - } - - return 1; -} - -/** - * smb2_set_err_rsp() - set error response code on smb response - * @work: smb work containing response buffer - */ -void smb2_set_err_rsp(struct ksmbd_work *work) -{ - struct smb2_err_rsp *err_rsp; - - if (work->next_smb2_rcv_hdr_off) - err_rsp = ksmbd_resp_buf_next(work); - else - err_rsp = smb2_get_msg(work->response_buf); - - if (err_rsp->hdr.Status != STATUS_STOPPED_ON_SYMLINK) { - err_rsp->StructureSize = SMB2_ERROR_STRUCTURE_SIZE2_LE; - err_rsp->ErrorContextCount = 0; - err_rsp->Reserved = 0; - err_rsp->ByteCount = 0; - err_rsp->ErrorData[0] = 0; - inc_rfc1001_len(work->response_buf, SMB2_ERROR_STRUCTURE_SIZE2); - } -} - -/** - * is_smb2_neg_cmd() - is it smb2 negotiation command - * @work: smb work containing smb header - * - * Return: true if smb2 negotiation command, otherwise false - */ -bool is_smb2_neg_cmd(struct ksmbd_work *work) -{ - struct smb2_hdr *hdr = smb2_get_msg(work->request_buf); - - /* is it SMB2 header ? */ - if (hdr->ProtocolId != SMB2_PROTO_NUMBER) - return false; - - /* make sure it is request not response message */ - if (hdr->Flags & SMB2_FLAGS_SERVER_TO_REDIR) - return false; - - if (hdr->Command != SMB2_NEGOTIATE) - return false; - - return true; -} - -/** - * is_smb2_rsp() - is it smb2 response - * @work: smb work containing smb response buffer - * - * Return: true if smb2 response, otherwise false - */ -bool is_smb2_rsp(struct ksmbd_work *work) -{ - struct smb2_hdr *hdr = smb2_get_msg(work->response_buf); - - /* is it SMB2 header ? */ - if (hdr->ProtocolId != SMB2_PROTO_NUMBER) - return false; - - /* make sure it is response not request message */ - if (!(hdr->Flags & SMB2_FLAGS_SERVER_TO_REDIR)) - return false; - - return true; -} - -/** - * get_smb2_cmd_val() - get smb command code from smb header - * @work: smb work containing smb request buffer - * - * Return: smb2 request command value - */ -u16 get_smb2_cmd_val(struct ksmbd_work *work) -{ - struct smb2_hdr *rcv_hdr; - - if (work->next_smb2_rcv_hdr_off) - rcv_hdr = ksmbd_req_buf_next(work); - else - rcv_hdr = smb2_get_msg(work->request_buf); - return le16_to_cpu(rcv_hdr->Command); -} - -/** - * set_smb2_rsp_status() - set error response code on smb2 header - * @work: smb work containing response buffer - * @err: error response code - */ -void set_smb2_rsp_status(struct ksmbd_work *work, __le32 err) -{ - struct smb2_hdr *rsp_hdr; - - if (work->next_smb2_rcv_hdr_off) - rsp_hdr = ksmbd_resp_buf_next(work); - else - rsp_hdr = smb2_get_msg(work->response_buf); - rsp_hdr->Status = err; - smb2_set_err_rsp(work); -} - -/** - * init_smb2_neg_rsp() - initialize smb2 response for negotiate command - * @work: smb work containing smb request buffer - * - * smb2 negotiate response is sent in reply of smb1 negotiate command for - * dialect auto-negotiation. - */ -int init_smb2_neg_rsp(struct ksmbd_work *work) -{ - struct smb2_hdr *rsp_hdr; - struct smb2_negotiate_rsp *rsp; - struct ksmbd_conn *conn = work->conn; - - *(__be32 *)work->response_buf = - cpu_to_be32(conn->vals->header_size); - - rsp_hdr = smb2_get_msg(work->response_buf); - memset(rsp_hdr, 0, sizeof(struct smb2_hdr) + 2); - rsp_hdr->ProtocolId = SMB2_PROTO_NUMBER; - rsp_hdr->StructureSize = SMB2_HEADER_STRUCTURE_SIZE; - rsp_hdr->CreditRequest = cpu_to_le16(2); - rsp_hdr->Command = SMB2_NEGOTIATE; - rsp_hdr->Flags = (SMB2_FLAGS_SERVER_TO_REDIR); - rsp_hdr->NextCommand = 0; - rsp_hdr->MessageId = 0; - rsp_hdr->Id.SyncId.ProcessId = 0; - rsp_hdr->Id.SyncId.TreeId = 0; - rsp_hdr->SessionId = 0; - memset(rsp_hdr->Signature, 0, 16); - - rsp = smb2_get_msg(work->response_buf); - - WARN_ON(ksmbd_conn_good(conn)); - - rsp->StructureSize = cpu_to_le16(65); - ksmbd_debug(SMB, "conn->dialect 0x%x\n", conn->dialect); - rsp->DialectRevision = cpu_to_le16(conn->dialect); - /* Not setting conn guid rsp->ServerGUID, as it - * not used by client for identifying connection - */ - rsp->Capabilities = cpu_to_le32(conn->vals->capabilities); - /* Default Max Message Size till SMB2.0, 64K*/ - rsp->MaxTransactSize = cpu_to_le32(conn->vals->max_trans_size); - rsp->MaxReadSize = cpu_to_le32(conn->vals->max_read_size); - rsp->MaxWriteSize = cpu_to_le32(conn->vals->max_write_size); - - rsp->SystemTime = cpu_to_le64(ksmbd_systime()); - rsp->ServerStartTime = 0; - - rsp->SecurityBufferOffset = cpu_to_le16(128); - rsp->SecurityBufferLength = cpu_to_le16(AUTH_GSS_LENGTH); - ksmbd_copy_gss_neg_header((char *)(&rsp->hdr) + - le16_to_cpu(rsp->SecurityBufferOffset)); - inc_rfc1001_len(work->response_buf, - sizeof(struct smb2_negotiate_rsp) - - sizeof(struct smb2_hdr) - sizeof(rsp->Buffer) + - AUTH_GSS_LENGTH); - rsp->SecurityMode = SMB2_NEGOTIATE_SIGNING_ENABLED_LE; - if (server_conf.signing == KSMBD_CONFIG_OPT_MANDATORY) - rsp->SecurityMode |= SMB2_NEGOTIATE_SIGNING_REQUIRED_LE; - conn->use_spnego = true; - - ksmbd_conn_set_need_negotiate(conn); - return 0; -} - -/** - * smb2_set_rsp_credits() - set number of credits in response buffer - * @work: smb work containing smb response buffer - */ -int smb2_set_rsp_credits(struct ksmbd_work *work) -{ - struct smb2_hdr *req_hdr = ksmbd_req_buf_next(work); - struct smb2_hdr *hdr = ksmbd_resp_buf_next(work); - struct ksmbd_conn *conn = work->conn; - unsigned short credits_requested, aux_max; - unsigned short credit_charge, credits_granted = 0; - - if (work->send_no_response) - return 0; - - hdr->CreditCharge = req_hdr->CreditCharge; - - if (conn->total_credits > conn->vals->max_credits) { - hdr->CreditRequest = 0; - pr_err("Total credits overflow: %d\n", conn->total_credits); - return -EINVAL; - } - - credit_charge = max_t(unsigned short, - le16_to_cpu(req_hdr->CreditCharge), 1); - if (credit_charge > conn->total_credits) { - ksmbd_debug(SMB, "Insufficient credits granted, given: %u, granted: %u\n", - credit_charge, conn->total_credits); - return -EINVAL; - } - - conn->total_credits -= credit_charge; - conn->outstanding_credits -= credit_charge; - credits_requested = max_t(unsigned short, - le16_to_cpu(req_hdr->CreditRequest), 1); - - /* according to smb2.credits smbtorture, Windows server - * 2016 or later grant up to 8192 credits at once. - * - * TODO: Need to adjuct CreditRequest value according to - * current cpu load - */ - if (hdr->Command == SMB2_NEGOTIATE) - aux_max = 1; - else - aux_max = conn->vals->max_credits - conn->total_credits; - credits_granted = min_t(unsigned short, credits_requested, aux_max); - - conn->total_credits += credits_granted; - work->credits_granted += credits_granted; - - if (!req_hdr->NextCommand) { - /* Update CreditRequest in last request */ - hdr->CreditRequest = cpu_to_le16(work->credits_granted); - } - ksmbd_debug(SMB, - "credits: requested[%d] granted[%d] total_granted[%d]\n", - credits_requested, credits_granted, - conn->total_credits); - return 0; -} - -/** - * init_chained_smb2_rsp() - initialize smb2 chained response - * @work: smb work containing smb response buffer - */ -static void init_chained_smb2_rsp(struct ksmbd_work *work) -{ - struct smb2_hdr *req = ksmbd_req_buf_next(work); - struct smb2_hdr *rsp = ksmbd_resp_buf_next(work); - struct smb2_hdr *rsp_hdr; - struct smb2_hdr *rcv_hdr; - int next_hdr_offset = 0; - int len, new_len; - - /* Len of this response = updated RFC len - offset of previous cmd - * in the compound rsp - */ - - /* Storing the current local FID which may be needed by subsequent - * command in the compound request - */ - if (req->Command == SMB2_CREATE && rsp->Status == STATUS_SUCCESS) { - work->compound_fid = ((struct smb2_create_rsp *)rsp)->VolatileFileId; - work->compound_pfid = ((struct smb2_create_rsp *)rsp)->PersistentFileId; - work->compound_sid = le64_to_cpu(rsp->SessionId); - } - - len = get_rfc1002_len(work->response_buf) - work->next_smb2_rsp_hdr_off; - next_hdr_offset = le32_to_cpu(req->NextCommand); - - new_len = ALIGN(len, 8); - inc_rfc1001_len(work->response_buf, - sizeof(struct smb2_hdr) + new_len - len); - rsp->NextCommand = cpu_to_le32(new_len); - - work->next_smb2_rcv_hdr_off += next_hdr_offset; - work->next_smb2_rsp_hdr_off += new_len; - ksmbd_debug(SMB, - "Compound req new_len = %d rcv off = %d rsp off = %d\n", - new_len, work->next_smb2_rcv_hdr_off, - work->next_smb2_rsp_hdr_off); - - rsp_hdr = ksmbd_resp_buf_next(work); - rcv_hdr = ksmbd_req_buf_next(work); - - if (!(rcv_hdr->Flags & SMB2_FLAGS_RELATED_OPERATIONS)) { - ksmbd_debug(SMB, "related flag should be set\n"); - work->compound_fid = KSMBD_NO_FID; - work->compound_pfid = KSMBD_NO_FID; - } - memset((char *)rsp_hdr, 0, sizeof(struct smb2_hdr) + 2); - rsp_hdr->ProtocolId = SMB2_PROTO_NUMBER; - rsp_hdr->StructureSize = SMB2_HEADER_STRUCTURE_SIZE; - rsp_hdr->Command = rcv_hdr->Command; - - /* - * Message is response. We don't grant oplock yet. - */ - rsp_hdr->Flags = (SMB2_FLAGS_SERVER_TO_REDIR | - SMB2_FLAGS_RELATED_OPERATIONS); - rsp_hdr->NextCommand = 0; - rsp_hdr->MessageId = rcv_hdr->MessageId; - rsp_hdr->Id.SyncId.ProcessId = rcv_hdr->Id.SyncId.ProcessId; - rsp_hdr->Id.SyncId.TreeId = rcv_hdr->Id.SyncId.TreeId; - rsp_hdr->SessionId = rcv_hdr->SessionId; - memcpy(rsp_hdr->Signature, rcv_hdr->Signature, 16); -} - -/** - * is_chained_smb2_message() - check for chained command - * @work: smb work containing smb request buffer - * - * Return: true if chained request, otherwise false - */ -bool is_chained_smb2_message(struct ksmbd_work *work) -{ - struct smb2_hdr *hdr = smb2_get_msg(work->request_buf); - unsigned int len, next_cmd; - - if (hdr->ProtocolId != SMB2_PROTO_NUMBER) - return false; - - hdr = ksmbd_req_buf_next(work); - next_cmd = le32_to_cpu(hdr->NextCommand); - if (next_cmd > 0) { - if ((u64)work->next_smb2_rcv_hdr_off + next_cmd + - __SMB2_HEADER_STRUCTURE_SIZE > - get_rfc1002_len(work->request_buf)) { - pr_err("next command(%u) offset exceeds smb msg size\n", - next_cmd); - return false; - } - - if ((u64)get_rfc1002_len(work->response_buf) + MAX_CIFS_SMALL_BUFFER_SIZE > - work->response_sz) { - pr_err("next response offset exceeds response buffer size\n"); - return false; - } - - ksmbd_debug(SMB, "got SMB2 chained command\n"); - init_chained_smb2_rsp(work); - return true; - } else if (work->next_smb2_rcv_hdr_off) { - /* - * This is last request in chained command, - * align response to 8 byte - */ - len = ALIGN(get_rfc1002_len(work->response_buf), 8); - len = len - get_rfc1002_len(work->response_buf); - if (len) { - ksmbd_debug(SMB, "padding len %u\n", len); - inc_rfc1001_len(work->response_buf, len); - if (work->aux_payload_sz) - work->aux_payload_sz += len; - } - } - return false; -} - -/** - * init_smb2_rsp_hdr() - initialize smb2 response - * @work: smb work containing smb request buffer - * - * Return: 0 - */ -int init_smb2_rsp_hdr(struct ksmbd_work *work) -{ - struct smb2_hdr *rsp_hdr = smb2_get_msg(work->response_buf); - struct smb2_hdr *rcv_hdr = smb2_get_msg(work->request_buf); - struct ksmbd_conn *conn = work->conn; - - memset(rsp_hdr, 0, sizeof(struct smb2_hdr) + 2); - *(__be32 *)work->response_buf = - cpu_to_be32(conn->vals->header_size); - rsp_hdr->ProtocolId = rcv_hdr->ProtocolId; - rsp_hdr->StructureSize = SMB2_HEADER_STRUCTURE_SIZE; - rsp_hdr->Command = rcv_hdr->Command; - - /* - * Message is response. We don't grant oplock yet. - */ - rsp_hdr->Flags = (SMB2_FLAGS_SERVER_TO_REDIR); - rsp_hdr->NextCommand = 0; - rsp_hdr->MessageId = rcv_hdr->MessageId; - rsp_hdr->Id.SyncId.ProcessId = rcv_hdr->Id.SyncId.ProcessId; - rsp_hdr->Id.SyncId.TreeId = rcv_hdr->Id.SyncId.TreeId; - rsp_hdr->SessionId = rcv_hdr->SessionId; - memcpy(rsp_hdr->Signature, rcv_hdr->Signature, 16); - - work->syncronous = true; - if (work->async_id) { - ksmbd_release_id(&conn->async_ida, work->async_id); - work->async_id = 0; - } - - return 0; -} - -/** - * smb2_allocate_rsp_buf() - allocate smb2 response buffer - * @work: smb work containing smb request buffer - * - * Return: 0 on success, otherwise -ENOMEM - */ -int smb2_allocate_rsp_buf(struct ksmbd_work *work) -{ - struct smb2_hdr *hdr = smb2_get_msg(work->request_buf); - size_t small_sz = MAX_CIFS_SMALL_BUFFER_SIZE; - size_t large_sz = small_sz + work->conn->vals->max_trans_size; - size_t sz = small_sz; - int cmd = le16_to_cpu(hdr->Command); - - if (cmd == SMB2_IOCTL_HE || cmd == SMB2_QUERY_DIRECTORY_HE) - sz = large_sz; - - if (cmd == SMB2_QUERY_INFO_HE) { - struct smb2_query_info_req *req; - - req = smb2_get_msg(work->request_buf); - if ((req->InfoType == SMB2_O_INFO_FILE && - (req->FileInfoClass == FILE_FULL_EA_INFORMATION || - req->FileInfoClass == FILE_ALL_INFORMATION)) || - req->InfoType == SMB2_O_INFO_SECURITY) - sz = large_sz; - } - - /* allocate large response buf for chained commands */ - if (le32_to_cpu(hdr->NextCommand) > 0) - sz = large_sz; - - work->response_buf = kvmalloc(sz, GFP_KERNEL | __GFP_ZERO); - if (!work->response_buf) - return -ENOMEM; - - work->response_sz = sz; - return 0; -} - -/** - * smb2_check_user_session() - check for valid session for a user - * @work: smb work containing smb request buffer - * - * Return: 0 on success, otherwise error - */ -int smb2_check_user_session(struct ksmbd_work *work) -{ - struct smb2_hdr *req_hdr = smb2_get_msg(work->request_buf); - struct ksmbd_conn *conn = work->conn; - unsigned int cmd = conn->ops->get_cmd_val(work); - unsigned long long sess_id; - - work->sess = NULL; - /* - * SMB2_ECHO, SMB2_NEGOTIATE, SMB2_SESSION_SETUP command do not - * require a session id, so no need to validate user session's for - * these commands. - */ - if (cmd == SMB2_ECHO_HE || cmd == SMB2_NEGOTIATE_HE || - cmd == SMB2_SESSION_SETUP_HE) - return 0; - - if (!ksmbd_conn_good(conn)) - return -EINVAL; - - sess_id = le64_to_cpu(req_hdr->SessionId); - /* Check for validity of user session */ - work->sess = ksmbd_session_lookup_all(conn, sess_id); - if (work->sess) - return 1; - ksmbd_debug(SMB, "Invalid user session, Uid %llu\n", sess_id); - return -EINVAL; -} - -static void destroy_previous_session(struct ksmbd_conn *conn, - struct ksmbd_user *user, u64 id) -{ - struct ksmbd_session *prev_sess = ksmbd_session_lookup_slowpath(id); - struct ksmbd_user *prev_user; - struct channel *chann; - long index; - - if (!prev_sess) - return; - - prev_user = prev_sess->user; - - if (!prev_user || - strcmp(user->name, prev_user->name) || - user->passkey_sz != prev_user->passkey_sz || - memcmp(user->passkey, prev_user->passkey, user->passkey_sz)) - return; - - prev_sess->state = SMB2_SESSION_EXPIRED; - xa_for_each(&prev_sess->ksmbd_chann_list, index, chann) - ksmbd_conn_set_exiting(chann->conn); -} - -/** - * smb2_get_name() - get filename string from on the wire smb format - * @src: source buffer - * @maxlen: maxlen of source string - * @local_nls: nls_table pointer - * - * Return: matching converted filename on success, otherwise error ptr - */ -static char * -smb2_get_name(const char *src, const int maxlen, struct nls_table *local_nls) -{ - char *name; - - name = smb_strndup_from_utf16(src, maxlen, 1, local_nls); - if (IS_ERR(name)) { - pr_err("failed to get name %ld\n", PTR_ERR(name)); - return name; - } - - ksmbd_conv_path_to_unix(name); - ksmbd_strip_last_slash(name); - return name; -} - -int setup_async_work(struct ksmbd_work *work, void (*fn)(void **), void **arg) -{ - struct smb2_hdr *rsp_hdr; - struct ksmbd_conn *conn = work->conn; - int id; - - rsp_hdr = smb2_get_msg(work->response_buf); - rsp_hdr->Flags |= SMB2_FLAGS_ASYNC_COMMAND; - - id = ksmbd_acquire_async_msg_id(&conn->async_ida); - if (id < 0) { - pr_err("Failed to alloc async message id\n"); - return id; - } - work->syncronous = false; - work->async_id = id; - rsp_hdr->Id.AsyncId = cpu_to_le64(id); - - ksmbd_debug(SMB, - "Send interim Response to inform async request id : %d\n", - work->async_id); - - work->cancel_fn = fn; - work->cancel_argv = arg; - - if (list_empty(&work->async_request_entry)) { - spin_lock(&conn->request_lock); - list_add_tail(&work->async_request_entry, &conn->async_requests); - spin_unlock(&conn->request_lock); - } - - return 0; -} - -void smb2_send_interim_resp(struct ksmbd_work *work, __le32 status) -{ - struct smb2_hdr *rsp_hdr; - - rsp_hdr = smb2_get_msg(work->response_buf); - smb2_set_err_rsp(work); - rsp_hdr->Status = status; - - work->multiRsp = 1; - ksmbd_conn_write(work); - rsp_hdr->Status = 0; - work->multiRsp = 0; -} - -static __le32 smb2_get_reparse_tag_special_file(umode_t mode) -{ - if (S_ISDIR(mode) || S_ISREG(mode)) - return 0; - - if (S_ISLNK(mode)) - return IO_REPARSE_TAG_LX_SYMLINK_LE; - else if (S_ISFIFO(mode)) - return IO_REPARSE_TAG_LX_FIFO_LE; - else if (S_ISSOCK(mode)) - return IO_REPARSE_TAG_AF_UNIX_LE; - else if (S_ISCHR(mode)) - return IO_REPARSE_TAG_LX_CHR_LE; - else if (S_ISBLK(mode)) - return IO_REPARSE_TAG_LX_BLK_LE; - - return 0; -} - -/** - * smb2_get_dos_mode() - get file mode in dos format from unix mode - * @stat: kstat containing file mode - * @attribute: attribute flags - * - * Return: converted dos mode - */ -static int smb2_get_dos_mode(struct kstat *stat, int attribute) -{ - int attr = 0; - - if (S_ISDIR(stat->mode)) { - attr = FILE_ATTRIBUTE_DIRECTORY | - (attribute & (FILE_ATTRIBUTE_HIDDEN | FILE_ATTRIBUTE_SYSTEM)); - } else { - attr = (attribute & 0x00005137) | FILE_ATTRIBUTE_ARCHIVE; - attr &= ~(FILE_ATTRIBUTE_DIRECTORY); - if (S_ISREG(stat->mode) && (server_conf.share_fake_fscaps & - FILE_SUPPORTS_SPARSE_FILES)) - attr |= FILE_ATTRIBUTE_SPARSE_FILE; - - if (smb2_get_reparse_tag_special_file(stat->mode)) - attr |= FILE_ATTRIBUTE_REPARSE_POINT; - } - - return attr; -} - -static void build_preauth_ctxt(struct smb2_preauth_neg_context *pneg_ctxt, - __le16 hash_id) -{ - pneg_ctxt->ContextType = SMB2_PREAUTH_INTEGRITY_CAPABILITIES; - pneg_ctxt->DataLength = cpu_to_le16(38); - pneg_ctxt->HashAlgorithmCount = cpu_to_le16(1); - pneg_ctxt->Reserved = cpu_to_le32(0); - pneg_ctxt->SaltLength = cpu_to_le16(SMB311_SALT_SIZE); - get_random_bytes(pneg_ctxt->Salt, SMB311_SALT_SIZE); - pneg_ctxt->HashAlgorithms = hash_id; -} - -static void build_encrypt_ctxt(struct smb2_encryption_neg_context *pneg_ctxt, - __le16 cipher_type) -{ - pneg_ctxt->ContextType = SMB2_ENCRYPTION_CAPABILITIES; - pneg_ctxt->DataLength = cpu_to_le16(4); - pneg_ctxt->Reserved = cpu_to_le32(0); - pneg_ctxt->CipherCount = cpu_to_le16(1); - pneg_ctxt->Ciphers[0] = cipher_type; -} - -static void build_compression_ctxt(struct smb2_compression_capabilities_context *pneg_ctxt, - __le16 comp_algo) -{ - pneg_ctxt->ContextType = SMB2_COMPRESSION_CAPABILITIES; - pneg_ctxt->DataLength = - cpu_to_le16(sizeof(struct smb2_compression_capabilities_context) - - sizeof(struct smb2_neg_context)); - pneg_ctxt->Reserved = cpu_to_le32(0); - pneg_ctxt->CompressionAlgorithmCount = cpu_to_le16(1); - pneg_ctxt->Flags = cpu_to_le32(0); - pneg_ctxt->CompressionAlgorithms[0] = comp_algo; -} - -static void build_sign_cap_ctxt(struct smb2_signing_capabilities *pneg_ctxt, - __le16 sign_algo) -{ - pneg_ctxt->ContextType = SMB2_SIGNING_CAPABILITIES; - pneg_ctxt->DataLength = - cpu_to_le16((sizeof(struct smb2_signing_capabilities) + 2) - - sizeof(struct smb2_neg_context)); - pneg_ctxt->Reserved = cpu_to_le32(0); - pneg_ctxt->SigningAlgorithmCount = cpu_to_le16(1); - pneg_ctxt->SigningAlgorithms[0] = sign_algo; -} - -static void build_posix_ctxt(struct smb2_posix_neg_context *pneg_ctxt) -{ - pneg_ctxt->ContextType = SMB2_POSIX_EXTENSIONS_AVAILABLE; - pneg_ctxt->DataLength = cpu_to_le16(POSIX_CTXT_DATA_LEN); - /* SMB2_CREATE_TAG_POSIX is "0x93AD25509CB411E7B42383DE968BCD7C" */ - pneg_ctxt->Name[0] = 0x93; - pneg_ctxt->Name[1] = 0xAD; - pneg_ctxt->Name[2] = 0x25; - pneg_ctxt->Name[3] = 0x50; - pneg_ctxt->Name[4] = 0x9C; - pneg_ctxt->Name[5] = 0xB4; - pneg_ctxt->Name[6] = 0x11; - pneg_ctxt->Name[7] = 0xE7; - pneg_ctxt->Name[8] = 0xB4; - pneg_ctxt->Name[9] = 0x23; - pneg_ctxt->Name[10] = 0x83; - pneg_ctxt->Name[11] = 0xDE; - pneg_ctxt->Name[12] = 0x96; - pneg_ctxt->Name[13] = 0x8B; - pneg_ctxt->Name[14] = 0xCD; - pneg_ctxt->Name[15] = 0x7C; -} - -static void assemble_neg_contexts(struct ksmbd_conn *conn, - struct smb2_negotiate_rsp *rsp, - void *smb2_buf_len) -{ - char *pneg_ctxt = (char *)rsp + - le32_to_cpu(rsp->NegotiateContextOffset); - int neg_ctxt_cnt = 1; - int ctxt_size; - - ksmbd_debug(SMB, - "assemble SMB2_PREAUTH_INTEGRITY_CAPABILITIES context\n"); - build_preauth_ctxt((struct smb2_preauth_neg_context *)pneg_ctxt, - conn->preauth_info->Preauth_HashId); - rsp->NegotiateContextCount = cpu_to_le16(neg_ctxt_cnt); - inc_rfc1001_len(smb2_buf_len, AUTH_GSS_PADDING); - ctxt_size = sizeof(struct smb2_preauth_neg_context); - /* Round to 8 byte boundary */ - pneg_ctxt += round_up(sizeof(struct smb2_preauth_neg_context), 8); - - if (conn->cipher_type) { - ctxt_size = round_up(ctxt_size, 8); - ksmbd_debug(SMB, - "assemble SMB2_ENCRYPTION_CAPABILITIES context\n"); - build_encrypt_ctxt((struct smb2_encryption_neg_context *)pneg_ctxt, - conn->cipher_type); - rsp->NegotiateContextCount = cpu_to_le16(++neg_ctxt_cnt); - ctxt_size += sizeof(struct smb2_encryption_neg_context) + 2; - /* Round to 8 byte boundary */ - pneg_ctxt += - round_up(sizeof(struct smb2_encryption_neg_context) + 2, - 8); - } - - if (conn->compress_algorithm) { - ctxt_size = round_up(ctxt_size, 8); - ksmbd_debug(SMB, - "assemble SMB2_COMPRESSION_CAPABILITIES context\n"); - /* Temporarily set to SMB3_COMPRESS_NONE */ - build_compression_ctxt((struct smb2_compression_capabilities_context *)pneg_ctxt, - conn->compress_algorithm); - rsp->NegotiateContextCount = cpu_to_le16(++neg_ctxt_cnt); - ctxt_size += sizeof(struct smb2_compression_capabilities_context) + 2; - /* Round to 8 byte boundary */ - pneg_ctxt += round_up(sizeof(struct smb2_compression_capabilities_context) + 2, - 8); - } - - if (conn->posix_ext_supported) { - ctxt_size = round_up(ctxt_size, 8); - ksmbd_debug(SMB, - "assemble SMB2_POSIX_EXTENSIONS_AVAILABLE context\n"); - build_posix_ctxt((struct smb2_posix_neg_context *)pneg_ctxt); - rsp->NegotiateContextCount = cpu_to_le16(++neg_ctxt_cnt); - ctxt_size += sizeof(struct smb2_posix_neg_context); - /* Round to 8 byte boundary */ - pneg_ctxt += round_up(sizeof(struct smb2_posix_neg_context), 8); - } - - if (conn->signing_negotiated) { - ctxt_size = round_up(ctxt_size, 8); - ksmbd_debug(SMB, - "assemble SMB2_SIGNING_CAPABILITIES context\n"); - build_sign_cap_ctxt((struct smb2_signing_capabilities *)pneg_ctxt, - conn->signing_algorithm); - rsp->NegotiateContextCount = cpu_to_le16(++neg_ctxt_cnt); - ctxt_size += sizeof(struct smb2_signing_capabilities) + 2; - } - - inc_rfc1001_len(smb2_buf_len, ctxt_size); -} - -static __le32 decode_preauth_ctxt(struct ksmbd_conn *conn, - struct smb2_preauth_neg_context *pneg_ctxt, - int ctxt_len) -{ - /* - * sizeof(smb2_preauth_neg_context) assumes SMB311_SALT_SIZE Salt, - * which may not be present. Only check for used HashAlgorithms[1]. - */ - if (ctxt_len < - sizeof(struct smb2_neg_context) + MIN_PREAUTH_CTXT_DATA_LEN) - return STATUS_INVALID_PARAMETER; - - if (pneg_ctxt->HashAlgorithms != SMB2_PREAUTH_INTEGRITY_SHA512) - return STATUS_NO_PREAUTH_INTEGRITY_HASH_OVERLAP; - - conn->preauth_info->Preauth_HashId = SMB2_PREAUTH_INTEGRITY_SHA512; - return STATUS_SUCCESS; -} - -static void decode_encrypt_ctxt(struct ksmbd_conn *conn, - struct smb2_encryption_neg_context *pneg_ctxt, - int ctxt_len) -{ - int cph_cnt; - int i, cphs_size; - - if (sizeof(struct smb2_encryption_neg_context) > ctxt_len) { - pr_err("Invalid SMB2_ENCRYPTION_CAPABILITIES context size\n"); - return; - } - - conn->cipher_type = 0; - - cph_cnt = le16_to_cpu(pneg_ctxt->CipherCount); - cphs_size = cph_cnt * sizeof(__le16); - - if (sizeof(struct smb2_encryption_neg_context) + cphs_size > - ctxt_len) { - pr_err("Invalid cipher count(%d)\n", cph_cnt); - return; - } - - if (!(server_conf.flags & KSMBD_GLOBAL_FLAG_SMB2_ENCRYPTION)) - return; - - for (i = 0; i < cph_cnt; i++) { - if (pneg_ctxt->Ciphers[i] == SMB2_ENCRYPTION_AES128_GCM || - pneg_ctxt->Ciphers[i] == SMB2_ENCRYPTION_AES128_CCM || - pneg_ctxt->Ciphers[i] == SMB2_ENCRYPTION_AES256_CCM || - pneg_ctxt->Ciphers[i] == SMB2_ENCRYPTION_AES256_GCM) { - ksmbd_debug(SMB, "Cipher ID = 0x%x\n", - pneg_ctxt->Ciphers[i]); - conn->cipher_type = pneg_ctxt->Ciphers[i]; - break; - } - } -} - -/** - * smb3_encryption_negotiated() - checks if server and client agreed on enabling encryption - * @conn: smb connection - * - * Return: true if connection should be encrypted, else false - */ -bool smb3_encryption_negotiated(struct ksmbd_conn *conn) -{ - if (!conn->ops->generate_encryptionkey) - return false; - - /* - * SMB 3.0 and 3.0.2 dialects use the SMB2_GLOBAL_CAP_ENCRYPTION flag. - * SMB 3.1.1 uses the cipher_type field. - */ - return (conn->vals->capabilities & SMB2_GLOBAL_CAP_ENCRYPTION) || - conn->cipher_type; -} - -static void decode_compress_ctxt(struct ksmbd_conn *conn, - struct smb2_compression_capabilities_context *pneg_ctxt) -{ - conn->compress_algorithm = SMB3_COMPRESS_NONE; -} - -static void decode_sign_cap_ctxt(struct ksmbd_conn *conn, - struct smb2_signing_capabilities *pneg_ctxt, - int ctxt_len) -{ - int sign_algo_cnt; - int i, sign_alos_size; - - if (sizeof(struct smb2_signing_capabilities) > ctxt_len) { - pr_err("Invalid SMB2_SIGNING_CAPABILITIES context length\n"); - return; - } - - conn->signing_negotiated = false; - sign_algo_cnt = le16_to_cpu(pneg_ctxt->SigningAlgorithmCount); - sign_alos_size = sign_algo_cnt * sizeof(__le16); - - if (sizeof(struct smb2_signing_capabilities) + sign_alos_size > - ctxt_len) { - pr_err("Invalid signing algorithm count(%d)\n", sign_algo_cnt); - return; - } - - for (i = 0; i < sign_algo_cnt; i++) { - if (pneg_ctxt->SigningAlgorithms[i] == SIGNING_ALG_HMAC_SHA256_LE || - pneg_ctxt->SigningAlgorithms[i] == SIGNING_ALG_AES_CMAC_LE) { - ksmbd_debug(SMB, "Signing Algorithm ID = 0x%x\n", - pneg_ctxt->SigningAlgorithms[i]); - conn->signing_negotiated = true; - conn->signing_algorithm = - pneg_ctxt->SigningAlgorithms[i]; - break; - } - } -} - -static __le32 deassemble_neg_contexts(struct ksmbd_conn *conn, - struct smb2_negotiate_req *req, - unsigned int len_of_smb) -{ - /* +4 is to account for the RFC1001 len field */ - struct smb2_neg_context *pctx = (struct smb2_neg_context *)req; - int i = 0, len_of_ctxts; - unsigned int offset = le32_to_cpu(req->NegotiateContextOffset); - unsigned int neg_ctxt_cnt = le16_to_cpu(req->NegotiateContextCount); - __le32 status = STATUS_INVALID_PARAMETER; - - ksmbd_debug(SMB, "decoding %d negotiate contexts\n", neg_ctxt_cnt); - if (len_of_smb <= offset) { - ksmbd_debug(SMB, "Invalid response: negotiate context offset\n"); - return status; - } - - len_of_ctxts = len_of_smb - offset; - - while (i++ < neg_ctxt_cnt) { - int clen, ctxt_len; - - if (len_of_ctxts < (int)sizeof(struct smb2_neg_context)) - break; - - pctx = (struct smb2_neg_context *)((char *)pctx + offset); - clen = le16_to_cpu(pctx->DataLength); - ctxt_len = clen + sizeof(struct smb2_neg_context); - - if (ctxt_len > len_of_ctxts) - break; - - if (pctx->ContextType == SMB2_PREAUTH_INTEGRITY_CAPABILITIES) { - ksmbd_debug(SMB, - "deassemble SMB2_PREAUTH_INTEGRITY_CAPABILITIES context\n"); - if (conn->preauth_info->Preauth_HashId) - break; - - status = decode_preauth_ctxt(conn, - (struct smb2_preauth_neg_context *)pctx, - ctxt_len); - if (status != STATUS_SUCCESS) - break; - } else if (pctx->ContextType == SMB2_ENCRYPTION_CAPABILITIES) { - ksmbd_debug(SMB, - "deassemble SMB2_ENCRYPTION_CAPABILITIES context\n"); - if (conn->cipher_type) - break; - - decode_encrypt_ctxt(conn, - (struct smb2_encryption_neg_context *)pctx, - ctxt_len); - } else if (pctx->ContextType == SMB2_COMPRESSION_CAPABILITIES) { - ksmbd_debug(SMB, - "deassemble SMB2_COMPRESSION_CAPABILITIES context\n"); - if (conn->compress_algorithm) - break; - - decode_compress_ctxt(conn, - (struct smb2_compression_capabilities_context *)pctx); - } else if (pctx->ContextType == SMB2_NETNAME_NEGOTIATE_CONTEXT_ID) { - ksmbd_debug(SMB, - "deassemble SMB2_NETNAME_NEGOTIATE_CONTEXT_ID context\n"); - } else if (pctx->ContextType == SMB2_POSIX_EXTENSIONS_AVAILABLE) { - ksmbd_debug(SMB, - "deassemble SMB2_POSIX_EXTENSIONS_AVAILABLE context\n"); - conn->posix_ext_supported = true; - } else if (pctx->ContextType == SMB2_SIGNING_CAPABILITIES) { - ksmbd_debug(SMB, - "deassemble SMB2_SIGNING_CAPABILITIES context\n"); - - decode_sign_cap_ctxt(conn, - (struct smb2_signing_capabilities *)pctx, - ctxt_len); - } - - /* offsets must be 8 byte aligned */ - offset = (ctxt_len + 7) & ~0x7; - len_of_ctxts -= offset; - } - return status; -} - -/** - * smb2_handle_negotiate() - handler for smb2 negotiate command - * @work: smb work containing smb request buffer - * - * Return: 0 - */ -int smb2_handle_negotiate(struct ksmbd_work *work) -{ - struct ksmbd_conn *conn = work->conn; - struct smb2_negotiate_req *req = smb2_get_msg(work->request_buf); - struct smb2_negotiate_rsp *rsp = smb2_get_msg(work->response_buf); - int rc = 0; - unsigned int smb2_buf_len, smb2_neg_size; - __le32 status; - - ksmbd_debug(SMB, "Received negotiate request\n"); - conn->need_neg = false; - if (ksmbd_conn_good(conn)) { - pr_err("conn->tcp_status is already in CifsGood State\n"); - work->send_no_response = 1; - return rc; - } - - smb2_buf_len = get_rfc1002_len(work->request_buf); - smb2_neg_size = offsetof(struct smb2_negotiate_req, Dialects); - if (smb2_neg_size > smb2_buf_len) { - rsp->hdr.Status = STATUS_INVALID_PARAMETER; - rc = -EINVAL; - goto err_out; - } - - if (req->DialectCount == 0) { - pr_err("malformed packet\n"); - rsp->hdr.Status = STATUS_INVALID_PARAMETER; - rc = -EINVAL; - goto err_out; - } - - if (conn->dialect == SMB311_PROT_ID) { - unsigned int nego_ctxt_off = le32_to_cpu(req->NegotiateContextOffset); - - if (smb2_buf_len < nego_ctxt_off) { - rsp->hdr.Status = STATUS_INVALID_PARAMETER; - rc = -EINVAL; - goto err_out; - } - - if (smb2_neg_size > nego_ctxt_off) { - rsp->hdr.Status = STATUS_INVALID_PARAMETER; - rc = -EINVAL; - goto err_out; - } - - if (smb2_neg_size + le16_to_cpu(req->DialectCount) * sizeof(__le16) > - nego_ctxt_off) { - rsp->hdr.Status = STATUS_INVALID_PARAMETER; - rc = -EINVAL; - goto err_out; - } - } else { - if (smb2_neg_size + le16_to_cpu(req->DialectCount) * sizeof(__le16) > - smb2_buf_len) { - rsp->hdr.Status = STATUS_INVALID_PARAMETER; - rc = -EINVAL; - goto err_out; - } - } - - conn->cli_cap = le32_to_cpu(req->Capabilities); - switch (conn->dialect) { - case SMB311_PROT_ID: - conn->preauth_info = - kzalloc(sizeof(struct preauth_integrity_info), - GFP_KERNEL); - if (!conn->preauth_info) { - rc = -ENOMEM; - rsp->hdr.Status = STATUS_INVALID_PARAMETER; - goto err_out; - } - - status = deassemble_neg_contexts(conn, req, - get_rfc1002_len(work->request_buf)); - if (status != STATUS_SUCCESS) { - pr_err("deassemble_neg_contexts error(0x%x)\n", - status); - rsp->hdr.Status = status; - rc = -EINVAL; - kfree(conn->preauth_info); - conn->preauth_info = NULL; - goto err_out; - } - - rc = init_smb3_11_server(conn); - if (rc < 0) { - rsp->hdr.Status = STATUS_INVALID_PARAMETER; - kfree(conn->preauth_info); - conn->preauth_info = NULL; - goto err_out; - } - - ksmbd_gen_preauth_integrity_hash(conn, - work->request_buf, - conn->preauth_info->Preauth_HashValue); - rsp->NegotiateContextOffset = - cpu_to_le32(OFFSET_OF_NEG_CONTEXT); - assemble_neg_contexts(conn, rsp, work->response_buf); - break; - case SMB302_PROT_ID: - init_smb3_02_server(conn); - break; - case SMB30_PROT_ID: - init_smb3_0_server(conn); - break; - case SMB21_PROT_ID: - init_smb2_1_server(conn); - break; - case SMB2X_PROT_ID: - case BAD_PROT_ID: - default: - ksmbd_debug(SMB, "Server dialect :0x%x not supported\n", - conn->dialect); - rsp->hdr.Status = STATUS_NOT_SUPPORTED; - rc = -EINVAL; - goto err_out; - } - rsp->Capabilities = cpu_to_le32(conn->vals->capabilities); - - /* For stats */ - conn->connection_type = conn->dialect; - - rsp->MaxTransactSize = cpu_to_le32(conn->vals->max_trans_size); - rsp->MaxReadSize = cpu_to_le32(conn->vals->max_read_size); - rsp->MaxWriteSize = cpu_to_le32(conn->vals->max_write_size); - - memcpy(conn->ClientGUID, req->ClientGUID, - SMB2_CLIENT_GUID_SIZE); - conn->cli_sec_mode = le16_to_cpu(req->SecurityMode); - - rsp->StructureSize = cpu_to_le16(65); - rsp->DialectRevision = cpu_to_le16(conn->dialect); - /* Not setting conn guid rsp->ServerGUID, as it - * not used by client for identifying server - */ - memset(rsp->ServerGUID, 0, SMB2_CLIENT_GUID_SIZE); - - rsp->SystemTime = cpu_to_le64(ksmbd_systime()); - rsp->ServerStartTime = 0; - ksmbd_debug(SMB, "negotiate context offset %d, count %d\n", - le32_to_cpu(rsp->NegotiateContextOffset), - le16_to_cpu(rsp->NegotiateContextCount)); - - rsp->SecurityBufferOffset = cpu_to_le16(128); - rsp->SecurityBufferLength = cpu_to_le16(AUTH_GSS_LENGTH); - ksmbd_copy_gss_neg_header((char *)(&rsp->hdr) + - le16_to_cpu(rsp->SecurityBufferOffset)); - inc_rfc1001_len(work->response_buf, sizeof(struct smb2_negotiate_rsp) - - sizeof(struct smb2_hdr) - sizeof(rsp->Buffer) + - AUTH_GSS_LENGTH); - rsp->SecurityMode = SMB2_NEGOTIATE_SIGNING_ENABLED_LE; - conn->use_spnego = true; - - if ((server_conf.signing == KSMBD_CONFIG_OPT_AUTO || - server_conf.signing == KSMBD_CONFIG_OPT_DISABLED) && - req->SecurityMode & SMB2_NEGOTIATE_SIGNING_REQUIRED_LE) - conn->sign = true; - else if (server_conf.signing == KSMBD_CONFIG_OPT_MANDATORY) { - server_conf.enforced_signing = true; - rsp->SecurityMode |= SMB2_NEGOTIATE_SIGNING_REQUIRED_LE; - conn->sign = true; - } - - conn->srv_sec_mode = le16_to_cpu(rsp->SecurityMode); - ksmbd_conn_set_need_negotiate(conn); - -err_out: - if (rc < 0) - smb2_set_err_rsp(work); - - return rc; -} - -static int alloc_preauth_hash(struct ksmbd_session *sess, - struct ksmbd_conn *conn) -{ - if (sess->Preauth_HashValue) - return 0; - - sess->Preauth_HashValue = kmemdup(conn->preauth_info->Preauth_HashValue, - PREAUTH_HASHVALUE_SIZE, GFP_KERNEL); - if (!sess->Preauth_HashValue) - return -ENOMEM; - - return 0; -} - -static int generate_preauth_hash(struct ksmbd_work *work) -{ - struct ksmbd_conn *conn = work->conn; - struct ksmbd_session *sess = work->sess; - u8 *preauth_hash; - - if (conn->dialect != SMB311_PROT_ID) - return 0; - - if (conn->binding) { - struct preauth_session *preauth_sess; - - preauth_sess = ksmbd_preauth_session_lookup(conn, sess->id); - if (!preauth_sess) { - preauth_sess = ksmbd_preauth_session_alloc(conn, sess->id); - if (!preauth_sess) - return -ENOMEM; - } - - preauth_hash = preauth_sess->Preauth_HashValue; - } else { - if (!sess->Preauth_HashValue) - if (alloc_preauth_hash(sess, conn)) - return -ENOMEM; - preauth_hash = sess->Preauth_HashValue; - } - - ksmbd_gen_preauth_integrity_hash(conn, work->request_buf, preauth_hash); - return 0; -} - -static int decode_negotiation_token(struct ksmbd_conn *conn, - struct negotiate_message *negblob, - size_t sz) -{ - if (!conn->use_spnego) - return -EINVAL; - - if (ksmbd_decode_negTokenInit((char *)negblob, sz, conn)) { - if (ksmbd_decode_negTokenTarg((char *)negblob, sz, conn)) { - conn->auth_mechs |= KSMBD_AUTH_NTLMSSP; - conn->preferred_auth_mech = KSMBD_AUTH_NTLMSSP; - conn->use_spnego = false; - } - } - return 0; -} - -static int ntlm_negotiate(struct ksmbd_work *work, - struct negotiate_message *negblob, - size_t negblob_len) -{ - struct smb2_sess_setup_rsp *rsp = smb2_get_msg(work->response_buf); - struct challenge_message *chgblob; - unsigned char *spnego_blob = NULL; - u16 spnego_blob_len; - char *neg_blob; - int sz, rc; - - ksmbd_debug(SMB, "negotiate phase\n"); - rc = ksmbd_decode_ntlmssp_neg_blob(negblob, negblob_len, work->conn); - if (rc) - return rc; - - sz = le16_to_cpu(rsp->SecurityBufferOffset); - chgblob = - (struct challenge_message *)((char *)&rsp->hdr.ProtocolId + sz); - memset(chgblob, 0, sizeof(struct challenge_message)); - - if (!work->conn->use_spnego) { - sz = ksmbd_build_ntlmssp_challenge_blob(chgblob, work->conn); - if (sz < 0) - return -ENOMEM; - - rsp->SecurityBufferLength = cpu_to_le16(sz); - return 0; - } - - sz = sizeof(struct challenge_message); - sz += (strlen(ksmbd_netbios_name()) * 2 + 1 + 4) * 6; - - neg_blob = kzalloc(sz, GFP_KERNEL); - if (!neg_blob) - return -ENOMEM; - - chgblob = (struct challenge_message *)neg_blob; - sz = ksmbd_build_ntlmssp_challenge_blob(chgblob, work->conn); - if (sz < 0) { - rc = -ENOMEM; - goto out; - } - - rc = build_spnego_ntlmssp_neg_blob(&spnego_blob, &spnego_blob_len, - neg_blob, sz); - if (rc) { - rc = -ENOMEM; - goto out; - } - - sz = le16_to_cpu(rsp->SecurityBufferOffset); - memcpy((char *)&rsp->hdr.ProtocolId + sz, spnego_blob, spnego_blob_len); - rsp->SecurityBufferLength = cpu_to_le16(spnego_blob_len); - -out: - kfree(spnego_blob); - kfree(neg_blob); - return rc; -} - -static struct authenticate_message *user_authblob(struct ksmbd_conn *conn, - struct smb2_sess_setup_req *req) -{ - int sz; - - if (conn->use_spnego && conn->mechToken) - return (struct authenticate_message *)conn->mechToken; - - sz = le16_to_cpu(req->SecurityBufferOffset); - return (struct authenticate_message *)((char *)&req->hdr.ProtocolId - + sz); -} - -static struct ksmbd_user *session_user(struct ksmbd_conn *conn, - struct smb2_sess_setup_req *req) -{ - struct authenticate_message *authblob; - struct ksmbd_user *user; - char *name; - unsigned int name_off, name_len, secbuf_len; - - secbuf_len = le16_to_cpu(req->SecurityBufferLength); - if (secbuf_len < sizeof(struct authenticate_message)) { - ksmbd_debug(SMB, "blob len %d too small\n", secbuf_len); - return NULL; - } - authblob = user_authblob(conn, req); - name_off = le32_to_cpu(authblob->UserName.BufferOffset); - name_len = le16_to_cpu(authblob->UserName.Length); - - if (secbuf_len < (u64)name_off + name_len) - return NULL; - - name = smb_strndup_from_utf16((const char *)authblob + name_off, - name_len, - true, - conn->local_nls); - if (IS_ERR(name)) { - pr_err("cannot allocate memory\n"); - return NULL; - } - - ksmbd_debug(SMB, "session setup request for user %s\n", name); - user = ksmbd_login_user(name); - kfree(name); - return user; -} - -static int ntlm_authenticate(struct ksmbd_work *work) -{ - struct smb2_sess_setup_req *req = smb2_get_msg(work->request_buf); - struct smb2_sess_setup_rsp *rsp = smb2_get_msg(work->response_buf); - struct ksmbd_conn *conn = work->conn; - struct ksmbd_session *sess = work->sess; - struct channel *chann = NULL; - struct ksmbd_user *user; - u64 prev_id; - int sz, rc; - - ksmbd_debug(SMB, "authenticate phase\n"); - if (conn->use_spnego) { - unsigned char *spnego_blob; - u16 spnego_blob_len; - - rc = build_spnego_ntlmssp_auth_blob(&spnego_blob, - &spnego_blob_len, - 0); - if (rc) - return -ENOMEM; - - sz = le16_to_cpu(rsp->SecurityBufferOffset); - memcpy((char *)&rsp->hdr.ProtocolId + sz, spnego_blob, spnego_blob_len); - rsp->SecurityBufferLength = cpu_to_le16(spnego_blob_len); - kfree(spnego_blob); - inc_rfc1001_len(work->response_buf, spnego_blob_len - 1); - } - - user = session_user(conn, req); - if (!user) { - ksmbd_debug(SMB, "Unknown user name or an error\n"); - return -EPERM; - } - - /* Check for previous session */ - prev_id = le64_to_cpu(req->PreviousSessionId); - if (prev_id && prev_id != sess->id) - destroy_previous_session(conn, user, prev_id); - - if (sess->state == SMB2_SESSION_VALID) { - /* - * Reuse session if anonymous try to connect - * on reauthetication. - */ - if (conn->binding == false && ksmbd_anonymous_user(user)) { - ksmbd_free_user(user); - return 0; - } - - if (!ksmbd_compare_user(sess->user, user)) { - ksmbd_free_user(user); - return -EPERM; - } - ksmbd_free_user(user); - } else { - sess->user = user; - } - - if (conn->binding == false && user_guest(sess->user)) { - rsp->SessionFlags = SMB2_SESSION_FLAG_IS_GUEST_LE; - } else { - struct authenticate_message *authblob; - - authblob = user_authblob(conn, req); - sz = le16_to_cpu(req->SecurityBufferLength); - rc = ksmbd_decode_ntlmssp_auth_blob(authblob, sz, conn, sess); - if (rc) { - set_user_flag(sess->user, KSMBD_USER_FLAG_BAD_PASSWORD); - ksmbd_debug(SMB, "authentication failed\n"); - return -EPERM; - } - } - - /* - * If session state is SMB2_SESSION_VALID, We can assume - * that it is reauthentication. And the user/password - * has been verified, so return it here. - */ - if (sess->state == SMB2_SESSION_VALID) { - if (conn->binding) - goto binding_session; - return 0; - } - - if ((rsp->SessionFlags != SMB2_SESSION_FLAG_IS_GUEST_LE && - (conn->sign || server_conf.enforced_signing)) || - (req->SecurityMode & SMB2_NEGOTIATE_SIGNING_REQUIRED)) - sess->sign = true; - - if (smb3_encryption_negotiated(conn) && - !(req->Flags & SMB2_SESSION_REQ_FLAG_BINDING)) { - rc = conn->ops->generate_encryptionkey(conn, sess); - if (rc) { - ksmbd_debug(SMB, - "SMB3 encryption key generation failed\n"); - return -EINVAL; - } - sess->enc = true; - rsp->SessionFlags = SMB2_SESSION_FLAG_ENCRYPT_DATA_LE; - /* - * signing is disable if encryption is enable - * on this session - */ - sess->sign = false; - } - -binding_session: - if (conn->dialect >= SMB30_PROT_ID) { - chann = lookup_chann_list(sess, conn); - if (!chann) { - chann = kmalloc(sizeof(struct channel), GFP_KERNEL); - if (!chann) - return -ENOMEM; - - chann->conn = conn; - xa_store(&sess->ksmbd_chann_list, (long)conn, chann, GFP_KERNEL); - } - } - - if (conn->ops->generate_signingkey) { - rc = conn->ops->generate_signingkey(sess, conn); - if (rc) { - ksmbd_debug(SMB, "SMB3 signing key generation failed\n"); - return -EINVAL; - } - } - - if (!ksmbd_conn_lookup_dialect(conn)) { - pr_err("fail to verify the dialect\n"); - return -ENOENT; - } - return 0; -} - -#ifdef CONFIG_SMB_SERVER_KERBEROS5 -static int krb5_authenticate(struct ksmbd_work *work) -{ - struct smb2_sess_setup_req *req = smb2_get_msg(work->request_buf); - struct smb2_sess_setup_rsp *rsp = smb2_get_msg(work->response_buf); - struct ksmbd_conn *conn = work->conn; - struct ksmbd_session *sess = work->sess; - char *in_blob, *out_blob; - struct channel *chann = NULL; - u64 prev_sess_id; - int in_len, out_len; - int retval; - - in_blob = (char *)&req->hdr.ProtocolId + - le16_to_cpu(req->SecurityBufferOffset); - in_len = le16_to_cpu(req->SecurityBufferLength); - out_blob = (char *)&rsp->hdr.ProtocolId + - le16_to_cpu(rsp->SecurityBufferOffset); - out_len = work->response_sz - - (le16_to_cpu(rsp->SecurityBufferOffset) + 4); - - /* Check previous session */ - prev_sess_id = le64_to_cpu(req->PreviousSessionId); - if (prev_sess_id && prev_sess_id != sess->id) - destroy_previous_session(conn, sess->user, prev_sess_id); - - if (sess->state == SMB2_SESSION_VALID) - ksmbd_free_user(sess->user); - - retval = ksmbd_krb5_authenticate(sess, in_blob, in_len, - out_blob, &out_len); - if (retval) { - ksmbd_debug(SMB, "krb5 authentication failed\n"); - return -EINVAL; - } - rsp->SecurityBufferLength = cpu_to_le16(out_len); - inc_rfc1001_len(work->response_buf, out_len - 1); - - if ((conn->sign || server_conf.enforced_signing) || - (req->SecurityMode & SMB2_NEGOTIATE_SIGNING_REQUIRED)) - sess->sign = true; - - if (smb3_encryption_negotiated(conn)) { - retval = conn->ops->generate_encryptionkey(conn, sess); - if (retval) { - ksmbd_debug(SMB, - "SMB3 encryption key generation failed\n"); - return -EINVAL; - } - sess->enc = true; - rsp->SessionFlags = SMB2_SESSION_FLAG_ENCRYPT_DATA_LE; - sess->sign = false; - } - - if (conn->dialect >= SMB30_PROT_ID) { - chann = lookup_chann_list(sess, conn); - if (!chann) { - chann = kmalloc(sizeof(struct channel), GFP_KERNEL); - if (!chann) - return -ENOMEM; - - chann->conn = conn; - xa_store(&sess->ksmbd_chann_list, (long)conn, chann, GFP_KERNEL); - } - } - - if (conn->ops->generate_signingkey) { - retval = conn->ops->generate_signingkey(sess, conn); - if (retval) { - ksmbd_debug(SMB, "SMB3 signing key generation failed\n"); - return -EINVAL; - } - } - - if (!ksmbd_conn_lookup_dialect(conn)) { - pr_err("fail to verify the dialect\n"); - return -ENOENT; - } - return 0; -} -#else -static int krb5_authenticate(struct ksmbd_work *work) -{ - return -EOPNOTSUPP; -} -#endif - -int smb2_sess_setup(struct ksmbd_work *work) -{ - struct ksmbd_conn *conn = work->conn; - struct smb2_sess_setup_req *req = smb2_get_msg(work->request_buf); - struct smb2_sess_setup_rsp *rsp = smb2_get_msg(work->response_buf); - struct ksmbd_session *sess; - struct negotiate_message *negblob; - unsigned int negblob_len, negblob_off; - int rc = 0; - - ksmbd_debug(SMB, "Received request for session setup\n"); - - rsp->StructureSize = cpu_to_le16(9); - rsp->SessionFlags = 0; - rsp->SecurityBufferOffset = cpu_to_le16(72); - rsp->SecurityBufferLength = 0; - inc_rfc1001_len(work->response_buf, 9); - - ksmbd_conn_lock(conn); - if (!req->hdr.SessionId) { - sess = ksmbd_smb2_session_create(); - if (!sess) { - rc = -ENOMEM; - goto out_err; - } - rsp->hdr.SessionId = cpu_to_le64(sess->id); - rc = ksmbd_session_register(conn, sess); - if (rc) - goto out_err; - } else if (conn->dialect >= SMB30_PROT_ID && - (server_conf.flags & KSMBD_GLOBAL_FLAG_SMB3_MULTICHANNEL) && - req->Flags & SMB2_SESSION_REQ_FLAG_BINDING) { - u64 sess_id = le64_to_cpu(req->hdr.SessionId); - - sess = ksmbd_session_lookup_slowpath(sess_id); - if (!sess) { - rc = -ENOENT; - goto out_err; - } - - if (conn->dialect != sess->dialect) { - rc = -EINVAL; - goto out_err; - } - - if (!(req->hdr.Flags & SMB2_FLAGS_SIGNED)) { - rc = -EINVAL; - goto out_err; - } - - if (strncmp(conn->ClientGUID, sess->ClientGUID, - SMB2_CLIENT_GUID_SIZE)) { - rc = -ENOENT; - goto out_err; - } - - if (sess->state == SMB2_SESSION_IN_PROGRESS) { - rc = -EACCES; - goto out_err; - } - - if (sess->state == SMB2_SESSION_EXPIRED) { - rc = -EFAULT; - goto out_err; - } - - if (ksmbd_conn_need_reconnect(conn)) { - rc = -EFAULT; - sess = NULL; - goto out_err; - } - - if (ksmbd_session_lookup(conn, sess_id)) { - rc = -EACCES; - goto out_err; - } - - if (user_guest(sess->user)) { - rc = -EOPNOTSUPP; - goto out_err; - } - - conn->binding = true; - } else if ((conn->dialect < SMB30_PROT_ID || - server_conf.flags & KSMBD_GLOBAL_FLAG_SMB3_MULTICHANNEL) && - (req->Flags & SMB2_SESSION_REQ_FLAG_BINDING)) { - sess = NULL; - rc = -EACCES; - goto out_err; - } else { - sess = ksmbd_session_lookup(conn, - le64_to_cpu(req->hdr.SessionId)); - if (!sess) { - rc = -ENOENT; - goto out_err; - } - - if (sess->state == SMB2_SESSION_EXPIRED) { - rc = -EFAULT; - goto out_err; - } - - if (ksmbd_conn_need_reconnect(conn)) { - rc = -EFAULT; - sess = NULL; - goto out_err; - } - } - work->sess = sess; - - negblob_off = le16_to_cpu(req->SecurityBufferOffset); - negblob_len = le16_to_cpu(req->SecurityBufferLength); - if (negblob_off < offsetof(struct smb2_sess_setup_req, Buffer) || - negblob_len < offsetof(struct negotiate_message, NegotiateFlags)) { - rc = -EINVAL; - goto out_err; - } - - negblob = (struct negotiate_message *)((char *)&req->hdr.ProtocolId + - negblob_off); - - if (decode_negotiation_token(conn, negblob, negblob_len) == 0) { - if (conn->mechToken) - negblob = (struct negotiate_message *)conn->mechToken; - } - - if (server_conf.auth_mechs & conn->auth_mechs) { - rc = generate_preauth_hash(work); - if (rc) - goto out_err; - - if (conn->preferred_auth_mech & - (KSMBD_AUTH_KRB5 | KSMBD_AUTH_MSKRB5)) { - rc = krb5_authenticate(work); - if (rc) { - rc = -EINVAL; - goto out_err; - } - - if (!ksmbd_conn_need_reconnect(conn)) { - ksmbd_conn_set_good(conn); - sess->state = SMB2_SESSION_VALID; - } - kfree(sess->Preauth_HashValue); - sess->Preauth_HashValue = NULL; - } else if (conn->preferred_auth_mech == KSMBD_AUTH_NTLMSSP) { - if (negblob->MessageType == NtLmNegotiate) { - rc = ntlm_negotiate(work, negblob, negblob_len); - if (rc) - goto out_err; - rsp->hdr.Status = - STATUS_MORE_PROCESSING_REQUIRED; - /* - * Note: here total size -1 is done as an - * adjustment for 0 size blob - */ - inc_rfc1001_len(work->response_buf, - le16_to_cpu(rsp->SecurityBufferLength) - 1); - - } else if (negblob->MessageType == NtLmAuthenticate) { - rc = ntlm_authenticate(work); - if (rc) - goto out_err; - - if (!ksmbd_conn_need_reconnect(conn)) { - ksmbd_conn_set_good(conn); - sess->state = SMB2_SESSION_VALID; - } - if (conn->binding) { - struct preauth_session *preauth_sess; - - preauth_sess = - ksmbd_preauth_session_lookup(conn, sess->id); - if (preauth_sess) { - list_del(&preauth_sess->preauth_entry); - kfree(preauth_sess); - } - } - kfree(sess->Preauth_HashValue); - sess->Preauth_HashValue = NULL; - } else { - pr_info_ratelimited("Unknown NTLMSSP message type : 0x%x\n", - le32_to_cpu(negblob->MessageType)); - rc = -EINVAL; - } - } else { - /* TODO: need one more negotiation */ - pr_err("Not support the preferred authentication\n"); - rc = -EINVAL; - } - } else { - pr_err("Not support authentication\n"); - rc = -EINVAL; - } - -out_err: - if (rc == -EINVAL) - rsp->hdr.Status = STATUS_INVALID_PARAMETER; - else if (rc == -ENOENT) - rsp->hdr.Status = STATUS_USER_SESSION_DELETED; - else if (rc == -EACCES) - rsp->hdr.Status = STATUS_REQUEST_NOT_ACCEPTED; - else if (rc == -EFAULT) - rsp->hdr.Status = STATUS_NETWORK_SESSION_EXPIRED; - else if (rc == -ENOMEM) - rsp->hdr.Status = STATUS_INSUFFICIENT_RESOURCES; - else if (rc == -EOPNOTSUPP) - rsp->hdr.Status = STATUS_NOT_SUPPORTED; - else if (rc) - rsp->hdr.Status = STATUS_LOGON_FAILURE; - - if (conn->use_spnego && conn->mechToken) { - kfree(conn->mechToken); - conn->mechToken = NULL; - } - - if (rc < 0) { - /* - * SecurityBufferOffset should be set to zero - * in session setup error response. - */ - rsp->SecurityBufferOffset = 0; - - if (sess) { - bool try_delay = false; - - /* - * To avoid dictionary attacks (repeated session setups rapidly sent) to - * connect to server, ksmbd make a delay of a 5 seconds on session setup - * failure to make it harder to send enough random connection requests - * to break into a server. - */ - if (sess->user && sess->user->flags & KSMBD_USER_FLAG_DELAY_SESSION) - try_delay = true; - - sess->last_active = jiffies; - sess->state = SMB2_SESSION_EXPIRED; - if (try_delay) { - ksmbd_conn_set_need_reconnect(conn); - ssleep(5); - ksmbd_conn_set_need_negotiate(conn); - } - } - } - - ksmbd_conn_unlock(conn); - return rc; -} - -/** - * smb2_tree_connect() - handler for smb2 tree connect command - * @work: smb work containing smb request buffer - * - * Return: 0 on success, otherwise error - */ -int smb2_tree_connect(struct ksmbd_work *work) -{ - struct ksmbd_conn *conn = work->conn; - struct smb2_tree_connect_req *req = smb2_get_msg(work->request_buf); - struct smb2_tree_connect_rsp *rsp = smb2_get_msg(work->response_buf); - struct ksmbd_session *sess = work->sess; - char *treename = NULL, *name = NULL; - struct ksmbd_tree_conn_status status; - struct ksmbd_share_config *share; - int rc = -EINVAL; - - treename = smb_strndup_from_utf16(req->Buffer, - le16_to_cpu(req->PathLength), true, - conn->local_nls); - if (IS_ERR(treename)) { - pr_err("treename is NULL\n"); - status.ret = KSMBD_TREE_CONN_STATUS_ERROR; - goto out_err1; - } - - name = ksmbd_extract_sharename(conn->um, treename); - if (IS_ERR(name)) { - status.ret = KSMBD_TREE_CONN_STATUS_ERROR; - goto out_err1; - } - - ksmbd_debug(SMB, "tree connect request for tree %s treename %s\n", - name, treename); - - status = ksmbd_tree_conn_connect(conn, sess, name); - if (status.ret == KSMBD_TREE_CONN_STATUS_OK) - rsp->hdr.Id.SyncId.TreeId = cpu_to_le32(status.tree_conn->id); - else - goto out_err1; - - share = status.tree_conn->share_conf; - if (test_share_config_flag(share, KSMBD_SHARE_FLAG_PIPE)) { - ksmbd_debug(SMB, "IPC share path request\n"); - rsp->ShareType = SMB2_SHARE_TYPE_PIPE; - rsp->MaximalAccess = FILE_READ_DATA_LE | FILE_READ_EA_LE | - FILE_EXECUTE_LE | FILE_READ_ATTRIBUTES_LE | - FILE_DELETE_LE | FILE_READ_CONTROL_LE | - FILE_WRITE_DAC_LE | FILE_WRITE_OWNER_LE | - FILE_SYNCHRONIZE_LE; - } else { - rsp->ShareType = SMB2_SHARE_TYPE_DISK; - rsp->MaximalAccess = FILE_READ_DATA_LE | FILE_READ_EA_LE | - FILE_EXECUTE_LE | FILE_READ_ATTRIBUTES_LE; - if (test_tree_conn_flag(status.tree_conn, - KSMBD_TREE_CONN_FLAG_WRITABLE)) { - rsp->MaximalAccess |= FILE_WRITE_DATA_LE | - FILE_APPEND_DATA_LE | FILE_WRITE_EA_LE | - FILE_DELETE_LE | FILE_WRITE_ATTRIBUTES_LE | - FILE_DELETE_CHILD_LE | FILE_READ_CONTROL_LE | - FILE_WRITE_DAC_LE | FILE_WRITE_OWNER_LE | - FILE_SYNCHRONIZE_LE; - } - } - - status.tree_conn->maximal_access = le32_to_cpu(rsp->MaximalAccess); - if (conn->posix_ext_supported) - status.tree_conn->posix_extensions = true; - - rsp->StructureSize = cpu_to_le16(16); - inc_rfc1001_len(work->response_buf, 16); -out_err1: - rsp->Capabilities = 0; - rsp->Reserved = 0; - /* default manual caching */ - rsp->ShareFlags = SMB2_SHAREFLAG_MANUAL_CACHING; - - if (!IS_ERR(treename)) - kfree(treename); - if (!IS_ERR(name)) - kfree(name); - - switch (status.ret) { - case KSMBD_TREE_CONN_STATUS_OK: - rsp->hdr.Status = STATUS_SUCCESS; - rc = 0; - break; - case -ESTALE: - case -ENOENT: - case KSMBD_TREE_CONN_STATUS_NO_SHARE: - rsp->hdr.Status = STATUS_BAD_NETWORK_NAME; - break; - case -ENOMEM: - case KSMBD_TREE_CONN_STATUS_NOMEM: - rsp->hdr.Status = STATUS_NO_MEMORY; - break; - case KSMBD_TREE_CONN_STATUS_ERROR: - case KSMBD_TREE_CONN_STATUS_TOO_MANY_CONNS: - case KSMBD_TREE_CONN_STATUS_TOO_MANY_SESSIONS: - rsp->hdr.Status = STATUS_ACCESS_DENIED; - break; - case -EINVAL: - rsp->hdr.Status = STATUS_INVALID_PARAMETER; - break; - default: - rsp->hdr.Status = STATUS_ACCESS_DENIED; - } - - if (status.ret != KSMBD_TREE_CONN_STATUS_OK) - smb2_set_err_rsp(work); - - return rc; -} - -/** - * smb2_create_open_flags() - convert smb open flags to unix open flags - * @file_present: is file already present - * @access: file access flags - * @disposition: file disposition flags - * @may_flags: set with MAY_ flags - * - * Return: file open flags - */ -static int smb2_create_open_flags(bool file_present, __le32 access, - __le32 disposition, - int *may_flags) -{ - int oflags = O_NONBLOCK | O_LARGEFILE; - - if (access & FILE_READ_DESIRED_ACCESS_LE && - access & FILE_WRITE_DESIRE_ACCESS_LE) { - oflags |= O_RDWR; - *may_flags = MAY_OPEN | MAY_READ | MAY_WRITE; - } else if (access & FILE_WRITE_DESIRE_ACCESS_LE) { - oflags |= O_WRONLY; - *may_flags = MAY_OPEN | MAY_WRITE; - } else { - oflags |= O_RDONLY; - *may_flags = MAY_OPEN | MAY_READ; - } - - if (access == FILE_READ_ATTRIBUTES_LE) - oflags |= O_PATH; - - if (file_present) { - switch (disposition & FILE_CREATE_MASK_LE) { - case FILE_OPEN_LE: - case FILE_CREATE_LE: - break; - case FILE_SUPERSEDE_LE: - case FILE_OVERWRITE_LE: - case FILE_OVERWRITE_IF_LE: - oflags |= O_TRUNC; - break; - default: - break; - } - } else { - switch (disposition & FILE_CREATE_MASK_LE) { - case FILE_SUPERSEDE_LE: - case FILE_CREATE_LE: - case FILE_OPEN_IF_LE: - case FILE_OVERWRITE_IF_LE: - oflags |= O_CREAT; - break; - case FILE_OPEN_LE: - case FILE_OVERWRITE_LE: - oflags &= ~O_CREAT; - break; - default: - break; - } - } - - return oflags; -} - -/** - * smb2_tree_disconnect() - handler for smb tree connect request - * @work: smb work containing request buffer - * - * Return: 0 - */ -int smb2_tree_disconnect(struct ksmbd_work *work) -{ - struct smb2_tree_disconnect_rsp *rsp = smb2_get_msg(work->response_buf); - struct ksmbd_session *sess = work->sess; - struct ksmbd_tree_connect *tcon = work->tcon; - - rsp->StructureSize = cpu_to_le16(4); - inc_rfc1001_len(work->response_buf, 4); - - ksmbd_debug(SMB, "request\n"); - - if (!tcon || test_and_set_bit(TREE_CONN_EXPIRE, &tcon->status)) { - struct smb2_tree_disconnect_req *req = - smb2_get_msg(work->request_buf); - - ksmbd_debug(SMB, "Invalid tid %d\n", req->hdr.Id.SyncId.TreeId); - - rsp->hdr.Status = STATUS_NETWORK_NAME_DELETED; - smb2_set_err_rsp(work); - return 0; - } - - ksmbd_close_tree_conn_fds(work); - ksmbd_tree_conn_disconnect(sess, tcon); - work->tcon = NULL; - return 0; -} - -/** - * smb2_session_logoff() - handler for session log off request - * @work: smb work containing request buffer - * - * Return: 0 - */ -int smb2_session_logoff(struct ksmbd_work *work) -{ - struct ksmbd_conn *conn = work->conn; - struct smb2_logoff_rsp *rsp = smb2_get_msg(work->response_buf); - struct ksmbd_session *sess; - struct smb2_logoff_req *req = smb2_get_msg(work->request_buf); - u64 sess_id = le64_to_cpu(req->hdr.SessionId); - - rsp->StructureSize = cpu_to_le16(4); - inc_rfc1001_len(work->response_buf, 4); - - ksmbd_debug(SMB, "request\n"); - - ksmbd_all_conn_set_status(sess_id, KSMBD_SESS_NEED_RECONNECT); - ksmbd_close_session_fds(work); - ksmbd_conn_wait_idle(conn, sess_id); - - /* - * Re-lookup session to validate if session is deleted - * while waiting request complete - */ - sess = ksmbd_session_lookup_all(conn, sess_id); - if (ksmbd_tree_conn_session_logoff(sess)) { - ksmbd_debug(SMB, "Invalid tid %d\n", req->hdr.Id.SyncId.TreeId); - rsp->hdr.Status = STATUS_NETWORK_NAME_DELETED; - smb2_set_err_rsp(work); - return 0; - } - - ksmbd_destroy_file_table(&sess->file_table); - sess->state = SMB2_SESSION_EXPIRED; - - ksmbd_free_user(sess->user); - sess->user = NULL; - ksmbd_all_conn_set_status(sess_id, KSMBD_SESS_NEED_NEGOTIATE); - return 0; -} - -/** - * create_smb2_pipe() - create IPC pipe - * @work: smb work containing request buffer - * - * Return: 0 on success, otherwise error - */ -static noinline int create_smb2_pipe(struct ksmbd_work *work) -{ - struct smb2_create_rsp *rsp = smb2_get_msg(work->response_buf); - struct smb2_create_req *req = smb2_get_msg(work->request_buf); - int id; - int err; - char *name; - - name = smb_strndup_from_utf16(req->Buffer, le16_to_cpu(req->NameLength), - 1, work->conn->local_nls); - if (IS_ERR(name)) { - rsp->hdr.Status = STATUS_NO_MEMORY; - err = PTR_ERR(name); - goto out; - } - - id = ksmbd_session_rpc_open(work->sess, name); - if (id < 0) { - pr_err("Unable to open RPC pipe: %d\n", id); - err = id; - goto out; - } - - rsp->hdr.Status = STATUS_SUCCESS; - rsp->StructureSize = cpu_to_le16(89); - rsp->OplockLevel = SMB2_OPLOCK_LEVEL_NONE; - rsp->Flags = 0; - rsp->CreateAction = cpu_to_le32(FILE_OPENED); - - rsp->CreationTime = cpu_to_le64(0); - rsp->LastAccessTime = cpu_to_le64(0); - rsp->ChangeTime = cpu_to_le64(0); - rsp->AllocationSize = cpu_to_le64(0); - rsp->EndofFile = cpu_to_le64(0); - rsp->FileAttributes = FILE_ATTRIBUTE_NORMAL_LE; - rsp->Reserved2 = 0; - rsp->VolatileFileId = id; - rsp->PersistentFileId = 0; - rsp->CreateContextsOffset = 0; - rsp->CreateContextsLength = 0; - - inc_rfc1001_len(work->response_buf, 88); /* StructureSize - 1*/ - kfree(name); - return 0; - -out: - switch (err) { - case -EINVAL: - rsp->hdr.Status = STATUS_INVALID_PARAMETER; - break; - case -ENOSPC: - case -ENOMEM: - rsp->hdr.Status = STATUS_NO_MEMORY; - break; - } - - if (!IS_ERR(name)) - kfree(name); - - smb2_set_err_rsp(work); - return err; -} - -/** - * smb2_set_ea() - handler for setting extended attributes using set - * info command - * @eabuf: set info command buffer - * @buf_len: set info command buffer length - * @path: dentry path for get ea - * - * Return: 0 on success, otherwise error - */ -static int smb2_set_ea(struct smb2_ea_info *eabuf, unsigned int buf_len, - const struct path *path) -{ - struct user_namespace *user_ns = mnt_user_ns(path->mnt); - char *attr_name = NULL, *value; - int rc = 0; - unsigned int next = 0; - - if (buf_len < sizeof(struct smb2_ea_info) + eabuf->EaNameLength + - le16_to_cpu(eabuf->EaValueLength)) - return -EINVAL; - - attr_name = kmalloc(XATTR_NAME_MAX + 1, GFP_KERNEL); - if (!attr_name) - return -ENOMEM; - - do { - if (!eabuf->EaNameLength) - goto next; - - ksmbd_debug(SMB, - "name : <%s>, name_len : %u, value_len : %u, next : %u\n", - eabuf->name, eabuf->EaNameLength, - le16_to_cpu(eabuf->EaValueLength), - le32_to_cpu(eabuf->NextEntryOffset)); - - if (eabuf->EaNameLength > - (XATTR_NAME_MAX - XATTR_USER_PREFIX_LEN)) { - rc = -EINVAL; - break; - } - - memcpy(attr_name, XATTR_USER_PREFIX, XATTR_USER_PREFIX_LEN); - memcpy(&attr_name[XATTR_USER_PREFIX_LEN], eabuf->name, - eabuf->EaNameLength); - attr_name[XATTR_USER_PREFIX_LEN + eabuf->EaNameLength] = '\0'; - value = (char *)&eabuf->name + eabuf->EaNameLength + 1; - - if (!eabuf->EaValueLength) { - rc = ksmbd_vfs_casexattr_len(user_ns, - path->dentry, - attr_name, - XATTR_USER_PREFIX_LEN + - eabuf->EaNameLength); - - /* delete the EA only when it exits */ - if (rc > 0) { - rc = ksmbd_vfs_remove_xattr(user_ns, - path->dentry, - attr_name); - - if (rc < 0) { - ksmbd_debug(SMB, - "remove xattr failed(%d)\n", - rc); - break; - } - } - - /* if the EA doesn't exist, just do nothing. */ - rc = 0; - } else { - rc = ksmbd_vfs_setxattr(user_ns, - path->dentry, attr_name, value, - le16_to_cpu(eabuf->EaValueLength), 0); - if (rc < 0) { - ksmbd_debug(SMB, - "ksmbd_vfs_setxattr is failed(%d)\n", - rc); - break; - } - } - -next: - next = le32_to_cpu(eabuf->NextEntryOffset); - if (next == 0 || buf_len < next) - break; - buf_len -= next; - eabuf = (struct smb2_ea_info *)((char *)eabuf + next); - if (next < (u32)eabuf->EaNameLength + le16_to_cpu(eabuf->EaValueLength)) - break; - - } while (next != 0); - - kfree(attr_name); - return rc; -} - -static noinline int smb2_set_stream_name_xattr(const struct path *path, - struct ksmbd_file *fp, - char *stream_name, int s_type) -{ - struct user_namespace *user_ns = mnt_user_ns(path->mnt); - size_t xattr_stream_size; - char *xattr_stream_name; - int rc; - - rc = ksmbd_vfs_xattr_stream_name(stream_name, - &xattr_stream_name, - &xattr_stream_size, - s_type); - if (rc) - return rc; - - fp->stream.name = xattr_stream_name; - fp->stream.size = xattr_stream_size; - - /* Check if there is stream prefix in xattr space */ - rc = ksmbd_vfs_casexattr_len(user_ns, - path->dentry, - xattr_stream_name, - xattr_stream_size); - if (rc >= 0) - return 0; - - if (fp->cdoption == FILE_OPEN_LE) { - ksmbd_debug(SMB, "XATTR stream name lookup failed: %d\n", rc); - return -EBADF; - } - - rc = ksmbd_vfs_setxattr(user_ns, path->dentry, - xattr_stream_name, NULL, 0, 0); - if (rc < 0) - pr_err("Failed to store XATTR stream name :%d\n", rc); - return 0; -} - -static int smb2_remove_smb_xattrs(const struct path *path) -{ - struct user_namespace *user_ns = mnt_user_ns(path->mnt); - char *name, *xattr_list = NULL; - ssize_t xattr_list_len; - int err = 0; - - xattr_list_len = ksmbd_vfs_listxattr(path->dentry, &xattr_list); - if (xattr_list_len < 0) { - goto out; - } else if (!xattr_list_len) { - ksmbd_debug(SMB, "empty xattr in the file\n"); - goto out; - } - - for (name = xattr_list; name - xattr_list < xattr_list_len; - name += strlen(name) + 1) { - ksmbd_debug(SMB, "%s, len %zd\n", name, strlen(name)); - - if (!strncmp(name, XATTR_USER_PREFIX, XATTR_USER_PREFIX_LEN) && - !strncmp(&name[XATTR_USER_PREFIX_LEN], STREAM_PREFIX, - STREAM_PREFIX_LEN)) { - err = ksmbd_vfs_remove_xattr(user_ns, path->dentry, - name); - if (err) - ksmbd_debug(SMB, "remove xattr failed : %s\n", - name); - } - } -out: - kvfree(xattr_list); - return err; -} - -static int smb2_create_truncate(const struct path *path) -{ - int rc = vfs_truncate(path, 0); - - if (rc) { - pr_err("vfs_truncate failed, rc %d\n", rc); - return rc; - } - - rc = smb2_remove_smb_xattrs(path); - if (rc == -EOPNOTSUPP) - rc = 0; - if (rc) - ksmbd_debug(SMB, - "ksmbd_truncate_stream_name_xattr failed, rc %d\n", - rc); - return rc; -} - -static void smb2_new_xattrs(struct ksmbd_tree_connect *tcon, const struct path *path, - struct ksmbd_file *fp) -{ - struct xattr_dos_attrib da = {0}; - int rc; - - if (!test_share_config_flag(tcon->share_conf, - KSMBD_SHARE_FLAG_STORE_DOS_ATTRS)) - return; - - da.version = 4; - da.attr = le32_to_cpu(fp->f_ci->m_fattr); - da.itime = da.create_time = fp->create_time; - da.flags = XATTR_DOSINFO_ATTRIB | XATTR_DOSINFO_CREATE_TIME | - XATTR_DOSINFO_ITIME; - - rc = ksmbd_vfs_set_dos_attrib_xattr(mnt_user_ns(path->mnt), - path->dentry, &da); - if (rc) - ksmbd_debug(SMB, "failed to store file attribute into xattr\n"); -} - -static void smb2_update_xattrs(struct ksmbd_tree_connect *tcon, - const struct path *path, struct ksmbd_file *fp) -{ - struct xattr_dos_attrib da; - int rc; - - fp->f_ci->m_fattr &= ~(FILE_ATTRIBUTE_HIDDEN_LE | FILE_ATTRIBUTE_SYSTEM_LE); - - /* get FileAttributes from XATTR_NAME_DOS_ATTRIBUTE */ - if (!test_share_config_flag(tcon->share_conf, - KSMBD_SHARE_FLAG_STORE_DOS_ATTRS)) - return; - - rc = ksmbd_vfs_get_dos_attrib_xattr(mnt_user_ns(path->mnt), - path->dentry, &da); - if (rc > 0) { - fp->f_ci->m_fattr = cpu_to_le32(da.attr); - fp->create_time = da.create_time; - fp->itime = da.itime; - } -} - -static int smb2_creat(struct ksmbd_work *work, struct path *path, char *name, - int open_flags, umode_t posix_mode, bool is_dir) -{ - struct ksmbd_tree_connect *tcon = work->tcon; - struct ksmbd_share_config *share = tcon->share_conf; - umode_t mode; - int rc; - - if (!(open_flags & O_CREAT)) - return -EBADF; - - ksmbd_debug(SMB, "file does not exist, so creating\n"); - if (is_dir == true) { - ksmbd_debug(SMB, "creating directory\n"); - - mode = share_config_directory_mode(share, posix_mode); - rc = ksmbd_vfs_mkdir(work, name, mode); - if (rc) - return rc; - } else { - ksmbd_debug(SMB, "creating regular file\n"); - - mode = share_config_create_mode(share, posix_mode); - rc = ksmbd_vfs_create(work, name, mode); - if (rc) - return rc; - } - - rc = ksmbd_vfs_kern_path(work, name, 0, path, 0); - if (rc) { - pr_err("cannot get linux path (%s), err = %d\n", - name, rc); - return rc; - } - return 0; -} - -static int smb2_create_sd_buffer(struct ksmbd_work *work, - struct smb2_create_req *req, - const struct path *path) -{ - struct create_context *context; - struct create_sd_buf_req *sd_buf; - - if (!req->CreateContextsOffset) - return -ENOENT; - - /* Parse SD BUFFER create contexts */ - context = smb2_find_context_vals(req, SMB2_CREATE_SD_BUFFER, 4); - if (!context) - return -ENOENT; - else if (IS_ERR(context)) - return PTR_ERR(context); - - ksmbd_debug(SMB, - "Set ACLs using SMB2_CREATE_SD_BUFFER context\n"); - sd_buf = (struct create_sd_buf_req *)context; - if (le16_to_cpu(context->DataOffset) + - le32_to_cpu(context->DataLength) < - sizeof(struct create_sd_buf_req)) - return -EINVAL; - return set_info_sec(work->conn, work->tcon, path, &sd_buf->ntsd, - le32_to_cpu(sd_buf->ccontext.DataLength), true); -} - -static void ksmbd_acls_fattr(struct smb_fattr *fattr, - struct user_namespace *mnt_userns, - struct inode *inode) -{ - vfsuid_t vfsuid = i_uid_into_vfsuid(mnt_userns, inode); - vfsgid_t vfsgid = i_gid_into_vfsgid(mnt_userns, inode); - - fattr->cf_uid = vfsuid_into_kuid(vfsuid); - fattr->cf_gid = vfsgid_into_kgid(vfsgid); - fattr->cf_mode = inode->i_mode; - fattr->cf_acls = NULL; - fattr->cf_dacls = NULL; - - if (IS_ENABLED(CONFIG_FS_POSIX_ACL)) { - fattr->cf_acls = get_acl(inode, ACL_TYPE_ACCESS); - if (S_ISDIR(inode->i_mode)) - fattr->cf_dacls = get_acl(inode, ACL_TYPE_DEFAULT); - } -} - -/** - * smb2_open() - handler for smb file open request - * @work: smb work containing request buffer - * - * Return: 0 on success, otherwise error - */ -int smb2_open(struct ksmbd_work *work) -{ - struct ksmbd_conn *conn = work->conn; - struct ksmbd_session *sess = work->sess; - struct ksmbd_tree_connect *tcon = work->tcon; - struct smb2_create_req *req; - struct smb2_create_rsp *rsp; - struct path path; - struct ksmbd_share_config *share = tcon->share_conf; - struct ksmbd_file *fp = NULL; - struct file *filp = NULL; - struct user_namespace *user_ns = NULL; - struct kstat stat; - struct create_context *context; - struct lease_ctx_info *lc = NULL; - struct create_ea_buf_req *ea_buf = NULL; - struct oplock_info *opinfo; - __le32 *next_ptr = NULL; - int req_op_level = 0, open_flags = 0, may_flags = 0, file_info = 0; - int rc = 0; - int contxt_cnt = 0, query_disk_id = 0; - int maximal_access_ctxt = 0, posix_ctxt = 0; - int s_type = 0; - int next_off = 0; - char *name = NULL; - char *stream_name = NULL; - bool file_present = false, created = false, already_permitted = false; - int share_ret, need_truncate = 0; - u64 time; - umode_t posix_mode = 0; - __le32 daccess, maximal_access = 0; - - WORK_BUFFERS(work, req, rsp); - - if (req->hdr.NextCommand && !work->next_smb2_rcv_hdr_off && - (req->hdr.Flags & SMB2_FLAGS_RELATED_OPERATIONS)) { - ksmbd_debug(SMB, "invalid flag in chained command\n"); - rsp->hdr.Status = STATUS_INVALID_PARAMETER; - smb2_set_err_rsp(work); - return -EINVAL; - } - - if (test_share_config_flag(share, KSMBD_SHARE_FLAG_PIPE)) { - ksmbd_debug(SMB, "IPC pipe create request\n"); - return create_smb2_pipe(work); - } - - if (req->NameLength) { - if ((req->CreateOptions & FILE_DIRECTORY_FILE_LE) && - *(char *)req->Buffer == '\\') { - pr_err("not allow directory name included leading slash\n"); - rc = -EINVAL; - goto err_out1; - } - - name = smb2_get_name(req->Buffer, - le16_to_cpu(req->NameLength), - work->conn->local_nls); - if (IS_ERR(name)) { - rc = PTR_ERR(name); - if (rc != -ENOMEM) - rc = -ENOENT; - name = NULL; - goto err_out1; - } - - ksmbd_debug(SMB, "converted name = %s\n", name); - if (strchr(name, ':')) { - if (!test_share_config_flag(work->tcon->share_conf, - KSMBD_SHARE_FLAG_STREAMS)) { - rc = -EBADF; - goto err_out1; - } - rc = parse_stream_name(name, &stream_name, &s_type); - if (rc < 0) - goto err_out1; - } - - rc = ksmbd_validate_filename(name); - if (rc < 0) - goto err_out1; - - if (ksmbd_share_veto_filename(share, name)) { - rc = -ENOENT; - ksmbd_debug(SMB, "Reject open(), vetoed file: %s\n", - name); - goto err_out1; - } - } else { - name = kstrdup("", GFP_KERNEL); - if (!name) { - rc = -ENOMEM; - goto err_out1; - } - } - - req_op_level = req->RequestedOplockLevel; - if (req_op_level == SMB2_OPLOCK_LEVEL_LEASE) - lc = parse_lease_state(req); - - if (le32_to_cpu(req->ImpersonationLevel) > le32_to_cpu(IL_DELEGATE)) { - pr_err("Invalid impersonationlevel : 0x%x\n", - le32_to_cpu(req->ImpersonationLevel)); - rc = -EIO; - rsp->hdr.Status = STATUS_BAD_IMPERSONATION_LEVEL; - goto err_out1; - } - - if (req->CreateOptions && !(req->CreateOptions & CREATE_OPTIONS_MASK_LE)) { - pr_err("Invalid create options : 0x%x\n", - le32_to_cpu(req->CreateOptions)); - rc = -EINVAL; - goto err_out1; - } else { - if (req->CreateOptions & FILE_SEQUENTIAL_ONLY_LE && - req->CreateOptions & FILE_RANDOM_ACCESS_LE) - req->CreateOptions = ~(FILE_SEQUENTIAL_ONLY_LE); - - if (req->CreateOptions & - (FILE_OPEN_BY_FILE_ID_LE | CREATE_TREE_CONNECTION | - FILE_RESERVE_OPFILTER_LE)) { - rc = -EOPNOTSUPP; - goto err_out1; - } - - if (req->CreateOptions & FILE_DIRECTORY_FILE_LE) { - if (req->CreateOptions & FILE_NON_DIRECTORY_FILE_LE) { - rc = -EINVAL; - goto err_out1; - } else if (req->CreateOptions & FILE_NO_COMPRESSION_LE) { - req->CreateOptions = ~(FILE_NO_COMPRESSION_LE); - } - } - } - - if (le32_to_cpu(req->CreateDisposition) > - le32_to_cpu(FILE_OVERWRITE_IF_LE)) { - pr_err("Invalid create disposition : 0x%x\n", - le32_to_cpu(req->CreateDisposition)); - rc = -EINVAL; - goto err_out1; - } - - if (!(req->DesiredAccess & DESIRED_ACCESS_MASK)) { - pr_err("Invalid desired access : 0x%x\n", - le32_to_cpu(req->DesiredAccess)); - rc = -EACCES; - goto err_out1; - } - - if (req->FileAttributes && !(req->FileAttributes & FILE_ATTRIBUTE_MASK_LE)) { - pr_err("Invalid file attribute : 0x%x\n", - le32_to_cpu(req->FileAttributes)); - rc = -EINVAL; - goto err_out1; - } - - if (req->CreateContextsOffset) { - /* Parse non-durable handle create contexts */ - context = smb2_find_context_vals(req, SMB2_CREATE_EA_BUFFER, 4); - if (IS_ERR(context)) { - rc = PTR_ERR(context); - goto err_out1; - } else if (context) { - ea_buf = (struct create_ea_buf_req *)context; - if (le16_to_cpu(context->DataOffset) + - le32_to_cpu(context->DataLength) < - sizeof(struct create_ea_buf_req)) { - rc = -EINVAL; - goto err_out1; - } - if (req->CreateOptions & FILE_NO_EA_KNOWLEDGE_LE) { - rsp->hdr.Status = STATUS_ACCESS_DENIED; - rc = -EACCES; - goto err_out1; - } - } - - context = smb2_find_context_vals(req, - SMB2_CREATE_QUERY_MAXIMAL_ACCESS_REQUEST, 4); - if (IS_ERR(context)) { - rc = PTR_ERR(context); - goto err_out1; - } else if (context) { - ksmbd_debug(SMB, - "get query maximal access context\n"); - maximal_access_ctxt = 1; - } - - context = smb2_find_context_vals(req, - SMB2_CREATE_TIMEWARP_REQUEST, 4); - if (IS_ERR(context)) { - rc = PTR_ERR(context); - goto err_out1; - } else if (context) { - ksmbd_debug(SMB, "get timewarp context\n"); - rc = -EBADF; - goto err_out1; - } - - if (tcon->posix_extensions) { - context = smb2_find_context_vals(req, - SMB2_CREATE_TAG_POSIX, 16); - if (IS_ERR(context)) { - rc = PTR_ERR(context); - goto err_out1; - } else if (context) { - struct create_posix *posix = - (struct create_posix *)context; - if (le16_to_cpu(context->DataOffset) + - le32_to_cpu(context->DataLength) < - sizeof(struct create_posix) - 4) { - rc = -EINVAL; - goto err_out1; - } - ksmbd_debug(SMB, "get posix context\n"); - - posix_mode = le32_to_cpu(posix->Mode); - posix_ctxt = 1; - } - } - } - - if (ksmbd_override_fsids(work)) { - rc = -ENOMEM; - goto err_out1; - } - - rc = ksmbd_vfs_kern_path(work, name, LOOKUP_NO_SYMLINKS, &path, 1); - if (!rc) { - if (req->CreateOptions & FILE_DELETE_ON_CLOSE_LE) { - /* - * If file exists with under flags, return access - * denied error. - */ - if (req->CreateDisposition == FILE_OVERWRITE_IF_LE || - req->CreateDisposition == FILE_OPEN_IF_LE) { - rc = -EACCES; - path_put(&path); - goto err_out; - } - - if (!test_tree_conn_flag(tcon, KSMBD_TREE_CONN_FLAG_WRITABLE)) { - ksmbd_debug(SMB, - "User does not have write permission\n"); - rc = -EACCES; - path_put(&path); - goto err_out; - } - } else if (d_is_symlink(path.dentry)) { - rc = -EACCES; - path_put(&path); - goto err_out; - } - } - - if (rc) { - if (rc != -ENOENT) - goto err_out; - ksmbd_debug(SMB, "can not get linux path for %s, rc = %d\n", - name, rc); - rc = 0; - } else { - file_present = true; - user_ns = mnt_user_ns(path.mnt); - } - if (stream_name) { - if (req->CreateOptions & FILE_DIRECTORY_FILE_LE) { - if (s_type == DATA_STREAM) { - rc = -EIO; - rsp->hdr.Status = STATUS_NOT_A_DIRECTORY; - } - } else { - if (file_present && S_ISDIR(d_inode(path.dentry)->i_mode) && - s_type == DATA_STREAM) { - rc = -EIO; - rsp->hdr.Status = STATUS_FILE_IS_A_DIRECTORY; - } - } - - if (req->CreateOptions & FILE_DIRECTORY_FILE_LE && - req->FileAttributes & FILE_ATTRIBUTE_NORMAL_LE) { - rsp->hdr.Status = STATUS_NOT_A_DIRECTORY; - rc = -EIO; - } - - if (rc < 0) - goto err_out; - } - - if (file_present && req->CreateOptions & FILE_NON_DIRECTORY_FILE_LE && - S_ISDIR(d_inode(path.dentry)->i_mode) && - !(req->CreateOptions & FILE_DELETE_ON_CLOSE_LE)) { - ksmbd_debug(SMB, "open() argument is a directory: %s, %x\n", - name, req->CreateOptions); - rsp->hdr.Status = STATUS_FILE_IS_A_DIRECTORY; - rc = -EIO; - goto err_out; - } - - if (file_present && (req->CreateOptions & FILE_DIRECTORY_FILE_LE) && - !(req->CreateDisposition == FILE_CREATE_LE) && - !S_ISDIR(d_inode(path.dentry)->i_mode)) { - rsp->hdr.Status = STATUS_NOT_A_DIRECTORY; - rc = -EIO; - goto err_out; - } - - if (!stream_name && file_present && - req->CreateDisposition == FILE_CREATE_LE) { - rc = -EEXIST; - goto err_out; - } - - daccess = smb_map_generic_desired_access(req->DesiredAccess); - - if (file_present && !(req->CreateOptions & FILE_DELETE_ON_CLOSE_LE)) { - rc = smb_check_perm_dacl(conn, &path, &daccess, - sess->user->uid); - if (rc) - goto err_out; - } - - if (daccess & FILE_MAXIMAL_ACCESS_LE) { - if (!file_present) { - daccess = cpu_to_le32(GENERIC_ALL_FLAGS); - } else { - rc = ksmbd_vfs_query_maximal_access(user_ns, - path.dentry, - &daccess); - if (rc) - goto err_out; - already_permitted = true; - } - maximal_access = daccess; - } - - open_flags = smb2_create_open_flags(file_present, daccess, - req->CreateDisposition, - &may_flags); - - if (!test_tree_conn_flag(tcon, KSMBD_TREE_CONN_FLAG_WRITABLE)) { - if (open_flags & O_CREAT) { - ksmbd_debug(SMB, - "User does not have write permission\n"); - rc = -EACCES; - goto err_out; - } - } - - /*create file if not present */ - if (!file_present) { - rc = smb2_creat(work, &path, name, open_flags, posix_mode, - req->CreateOptions & FILE_DIRECTORY_FILE_LE); - if (rc) { - if (rc == -ENOENT) { - rc = -EIO; - rsp->hdr.Status = STATUS_OBJECT_PATH_NOT_FOUND; - } - goto err_out; - } - - created = true; - user_ns = mnt_user_ns(path.mnt); - if (ea_buf) { - if (le32_to_cpu(ea_buf->ccontext.DataLength) < - sizeof(struct smb2_ea_info)) { - rc = -EINVAL; - goto err_out; - } - - rc = smb2_set_ea(&ea_buf->ea, - le32_to_cpu(ea_buf->ccontext.DataLength), - &path); - if (rc == -EOPNOTSUPP) - rc = 0; - else if (rc) - goto err_out; - } - } else if (!already_permitted) { - /* FILE_READ_ATTRIBUTE is allowed without inode_permission, - * because execute(search) permission on a parent directory, - * is already granted. - */ - if (daccess & ~(FILE_READ_ATTRIBUTES_LE | FILE_READ_CONTROL_LE)) { - rc = inode_permission(user_ns, - d_inode(path.dentry), - may_flags); - if (rc) - goto err_out; - - if ((daccess & FILE_DELETE_LE) || - (req->CreateOptions & FILE_DELETE_ON_CLOSE_LE)) { - rc = ksmbd_vfs_may_delete(user_ns, - path.dentry); - if (rc) - goto err_out; - } - } - } - - rc = ksmbd_query_inode_status(d_inode(path.dentry->d_parent)); - if (rc == KSMBD_INODE_STATUS_PENDING_DELETE) { - rc = -EBUSY; - goto err_out; - } - - rc = 0; - filp = dentry_open(&path, open_flags, current_cred()); - if (IS_ERR(filp)) { - rc = PTR_ERR(filp); - pr_err("dentry open for dir failed, rc %d\n", rc); - goto err_out; - } - - if (file_present) { - if (!(open_flags & O_TRUNC)) - file_info = FILE_OPENED; - else - file_info = FILE_OVERWRITTEN; - - if ((req->CreateDisposition & FILE_CREATE_MASK_LE) == - FILE_SUPERSEDE_LE) - file_info = FILE_SUPERSEDED; - } else if (open_flags & O_CREAT) { - file_info = FILE_CREATED; - } - - ksmbd_vfs_set_fadvise(filp, req->CreateOptions); - - /* Obtain Volatile-ID */ - fp = ksmbd_open_fd(work, filp); - if (IS_ERR(fp)) { - fput(filp); - rc = PTR_ERR(fp); - fp = NULL; - goto err_out; - } - - /* Get Persistent-ID */ - ksmbd_open_durable_fd(fp); - if (!has_file_id(fp->persistent_id)) { - rc = -ENOMEM; - goto err_out; - } - - fp->cdoption = req->CreateDisposition; - fp->daccess = daccess; - fp->saccess = req->ShareAccess; - fp->coption = req->CreateOptions; - - /* Set default windows and posix acls if creating new file */ - if (created) { - int posix_acl_rc; - struct inode *inode = d_inode(path.dentry); - - posix_acl_rc = ksmbd_vfs_inherit_posix_acl(user_ns, - inode, - d_inode(path.dentry->d_parent)); - if (posix_acl_rc) - ksmbd_debug(SMB, "inherit posix acl failed : %d\n", posix_acl_rc); - - if (test_share_config_flag(work->tcon->share_conf, - KSMBD_SHARE_FLAG_ACL_XATTR)) { - rc = smb_inherit_dacl(conn, &path, sess->user->uid, - sess->user->gid); - } - - if (rc) { - rc = smb2_create_sd_buffer(work, req, &path); - if (rc) { - if (posix_acl_rc) - ksmbd_vfs_set_init_posix_acl(user_ns, - inode); - - if (test_share_config_flag(work->tcon->share_conf, - KSMBD_SHARE_FLAG_ACL_XATTR)) { - struct smb_fattr fattr; - struct smb_ntsd *pntsd; - int pntsd_size, ace_num = 0; - - ksmbd_acls_fattr(&fattr, user_ns, inode); - if (fattr.cf_acls) - ace_num = fattr.cf_acls->a_count; - if (fattr.cf_dacls) - ace_num += fattr.cf_dacls->a_count; - - pntsd = kmalloc(sizeof(struct smb_ntsd) + - sizeof(struct smb_sid) * 3 + - sizeof(struct smb_acl) + - sizeof(struct smb_ace) * ace_num * 2, - GFP_KERNEL); - if (!pntsd) { - posix_acl_release(fattr.cf_acls); - posix_acl_release(fattr.cf_dacls); - goto err_out; - } - - rc = build_sec_desc(user_ns, - pntsd, NULL, 0, - OWNER_SECINFO | - GROUP_SECINFO | - DACL_SECINFO, - &pntsd_size, &fattr); - posix_acl_release(fattr.cf_acls); - posix_acl_release(fattr.cf_dacls); - if (rc) { - kfree(pntsd); - goto err_out; - } - - rc = ksmbd_vfs_set_sd_xattr(conn, - user_ns, - path.dentry, - pntsd, - pntsd_size); - kfree(pntsd); - if (rc) - pr_err("failed to store ntacl in xattr : %d\n", - rc); - } - } - } - rc = 0; - } - - if (stream_name) { - rc = smb2_set_stream_name_xattr(&path, - fp, - stream_name, - s_type); - if (rc) - goto err_out; - file_info = FILE_CREATED; - } - - fp->attrib_only = !(req->DesiredAccess & ~(FILE_READ_ATTRIBUTES_LE | - FILE_WRITE_ATTRIBUTES_LE | FILE_SYNCHRONIZE_LE)); - if (!S_ISDIR(file_inode(filp)->i_mode) && open_flags & O_TRUNC && - !fp->attrib_only && !stream_name) { - smb_break_all_oplock(work, fp); - need_truncate = 1; - } - - /* fp should be searchable through ksmbd_inode.m_fp_list - * after daccess, saccess, attrib_only, and stream are - * initialized. - */ - write_lock(&fp->f_ci->m_lock); - list_add(&fp->node, &fp->f_ci->m_fp_list); - write_unlock(&fp->f_ci->m_lock); - - /* Check delete pending among previous fp before oplock break */ - if (ksmbd_inode_pending_delete(fp)) { - rc = -EBUSY; - goto err_out; - } - - share_ret = ksmbd_smb_check_shared_mode(fp->filp, fp); - if (!test_share_config_flag(work->tcon->share_conf, KSMBD_SHARE_FLAG_OPLOCKS) || - (req_op_level == SMB2_OPLOCK_LEVEL_LEASE && - !(conn->vals->capabilities & SMB2_GLOBAL_CAP_LEASING))) { - if (share_ret < 0 && !S_ISDIR(file_inode(fp->filp)->i_mode)) { - rc = share_ret; - goto err_out; - } - } else { - if (req_op_level == SMB2_OPLOCK_LEVEL_LEASE) { - req_op_level = smb2_map_lease_to_oplock(lc->req_state); - ksmbd_debug(SMB, - "lease req for(%s) req oplock state 0x%x, lease state 0x%x\n", - name, req_op_level, lc->req_state); - rc = find_same_lease_key(sess, fp->f_ci, lc); - if (rc) - goto err_out; - } else if (open_flags == O_RDONLY && - (req_op_level == SMB2_OPLOCK_LEVEL_BATCH || - req_op_level == SMB2_OPLOCK_LEVEL_EXCLUSIVE)) - req_op_level = SMB2_OPLOCK_LEVEL_II; - - rc = smb_grant_oplock(work, req_op_level, - fp->persistent_id, fp, - le32_to_cpu(req->hdr.Id.SyncId.TreeId), - lc, share_ret); - if (rc < 0) - goto err_out; - } - - if (req->CreateOptions & FILE_DELETE_ON_CLOSE_LE) - ksmbd_fd_set_delete_on_close(fp, file_info); - - if (need_truncate) { - rc = smb2_create_truncate(&path); - if (rc) - goto err_out; - } - - if (req->CreateContextsOffset) { - struct create_alloc_size_req *az_req; - - az_req = (struct create_alloc_size_req *)smb2_find_context_vals(req, - SMB2_CREATE_ALLOCATION_SIZE, 4); - if (IS_ERR(az_req)) { - rc = PTR_ERR(az_req); - goto err_out; - } else if (az_req) { - loff_t alloc_size; - int err; - - if (le16_to_cpu(az_req->ccontext.DataOffset) + - le32_to_cpu(az_req->ccontext.DataLength) < - sizeof(struct create_alloc_size_req)) { - rc = -EINVAL; - goto err_out; - } - alloc_size = le64_to_cpu(az_req->AllocationSize); - ksmbd_debug(SMB, - "request smb2 create allocate size : %llu\n", - alloc_size); - smb_break_all_levII_oplock(work, fp, 1); - err = vfs_fallocate(fp->filp, FALLOC_FL_KEEP_SIZE, 0, - alloc_size); - if (err < 0) - ksmbd_debug(SMB, - "vfs_fallocate is failed : %d\n", - err); - } - - context = smb2_find_context_vals(req, SMB2_CREATE_QUERY_ON_DISK_ID, 4); - if (IS_ERR(context)) { - rc = PTR_ERR(context); - goto err_out; - } else if (context) { - ksmbd_debug(SMB, "get query on disk id context\n"); - query_disk_id = 1; - } - } - - rc = ksmbd_vfs_getattr(&path, &stat); - if (rc) - goto err_out; - - if (stat.result_mask & STATX_BTIME) - fp->create_time = ksmbd_UnixTimeToNT(stat.btime); - else - fp->create_time = ksmbd_UnixTimeToNT(stat.ctime); - if (req->FileAttributes || fp->f_ci->m_fattr == 0) - fp->f_ci->m_fattr = - cpu_to_le32(smb2_get_dos_mode(&stat, le32_to_cpu(req->FileAttributes))); - - if (!created) - smb2_update_xattrs(tcon, &path, fp); - else - smb2_new_xattrs(tcon, &path, fp); - - memcpy(fp->client_guid, conn->ClientGUID, SMB2_CLIENT_GUID_SIZE); - - rsp->StructureSize = cpu_to_le16(89); - rcu_read_lock(); - opinfo = rcu_dereference(fp->f_opinfo); - rsp->OplockLevel = opinfo != NULL ? opinfo->level : 0; - rcu_read_unlock(); - rsp->Flags = 0; - rsp->CreateAction = cpu_to_le32(file_info); - rsp->CreationTime = cpu_to_le64(fp->create_time); - time = ksmbd_UnixTimeToNT(stat.atime); - rsp->LastAccessTime = cpu_to_le64(time); - time = ksmbd_UnixTimeToNT(stat.mtime); - rsp->LastWriteTime = cpu_to_le64(time); - time = ksmbd_UnixTimeToNT(stat.ctime); - rsp->ChangeTime = cpu_to_le64(time); - rsp->AllocationSize = S_ISDIR(stat.mode) ? 0 : - cpu_to_le64(stat.blocks << 9); - rsp->EndofFile = S_ISDIR(stat.mode) ? 0 : cpu_to_le64(stat.size); - rsp->FileAttributes = fp->f_ci->m_fattr; - - rsp->Reserved2 = 0; - - rsp->PersistentFileId = fp->persistent_id; - rsp->VolatileFileId = fp->volatile_id; - - rsp->CreateContextsOffset = 0; - rsp->CreateContextsLength = 0; - inc_rfc1001_len(work->response_buf, 88); /* StructureSize - 1*/ - - /* If lease is request send lease context response */ - if (opinfo && opinfo->is_lease) { - struct create_context *lease_ccontext; - - ksmbd_debug(SMB, "lease granted on(%s) lease state 0x%x\n", - name, opinfo->o_lease->state); - rsp->OplockLevel = SMB2_OPLOCK_LEVEL_LEASE; - - lease_ccontext = (struct create_context *)rsp->Buffer; - contxt_cnt++; - create_lease_buf(rsp->Buffer, opinfo->o_lease); - le32_add_cpu(&rsp->CreateContextsLength, - conn->vals->create_lease_size); - inc_rfc1001_len(work->response_buf, - conn->vals->create_lease_size); - next_ptr = &lease_ccontext->Next; - next_off = conn->vals->create_lease_size; - } - - if (maximal_access_ctxt) { - struct create_context *mxac_ccontext; - - if (maximal_access == 0) - ksmbd_vfs_query_maximal_access(user_ns, - path.dentry, - &maximal_access); - mxac_ccontext = (struct create_context *)(rsp->Buffer + - le32_to_cpu(rsp->CreateContextsLength)); - contxt_cnt++; - create_mxac_rsp_buf(rsp->Buffer + - le32_to_cpu(rsp->CreateContextsLength), - le32_to_cpu(maximal_access)); - le32_add_cpu(&rsp->CreateContextsLength, - conn->vals->create_mxac_size); - inc_rfc1001_len(work->response_buf, - conn->vals->create_mxac_size); - if (next_ptr) - *next_ptr = cpu_to_le32(next_off); - next_ptr = &mxac_ccontext->Next; - next_off = conn->vals->create_mxac_size; - } - - if (query_disk_id) { - struct create_context *disk_id_ccontext; - - disk_id_ccontext = (struct create_context *)(rsp->Buffer + - le32_to_cpu(rsp->CreateContextsLength)); - contxt_cnt++; - create_disk_id_rsp_buf(rsp->Buffer + - le32_to_cpu(rsp->CreateContextsLength), - stat.ino, tcon->id); - le32_add_cpu(&rsp->CreateContextsLength, - conn->vals->create_disk_id_size); - inc_rfc1001_len(work->response_buf, - conn->vals->create_disk_id_size); - if (next_ptr) - *next_ptr = cpu_to_le32(next_off); - next_ptr = &disk_id_ccontext->Next; - next_off = conn->vals->create_disk_id_size; - } - - if (posix_ctxt) { - contxt_cnt++; - create_posix_rsp_buf(rsp->Buffer + - le32_to_cpu(rsp->CreateContextsLength), - fp); - le32_add_cpu(&rsp->CreateContextsLength, - conn->vals->create_posix_size); - inc_rfc1001_len(work->response_buf, - conn->vals->create_posix_size); - if (next_ptr) - *next_ptr = cpu_to_le32(next_off); - } - - if (contxt_cnt > 0) { - rsp->CreateContextsOffset = - cpu_to_le32(offsetof(struct smb2_create_rsp, Buffer)); - } - -err_out: - if (file_present || created) - path_put(&path); - ksmbd_revert_fsids(work); -err_out1: - if (rc) { - if (rc == -EINVAL) - rsp->hdr.Status = STATUS_INVALID_PARAMETER; - else if (rc == -EOPNOTSUPP) - rsp->hdr.Status = STATUS_NOT_SUPPORTED; - else if (rc == -EACCES || rc == -ESTALE || rc == -EXDEV) - rsp->hdr.Status = STATUS_ACCESS_DENIED; - else if (rc == -ENOENT) - rsp->hdr.Status = STATUS_OBJECT_NAME_INVALID; - else if (rc == -EPERM) - rsp->hdr.Status = STATUS_SHARING_VIOLATION; - else if (rc == -EBUSY) - rsp->hdr.Status = STATUS_DELETE_PENDING; - else if (rc == -EBADF) - rsp->hdr.Status = STATUS_OBJECT_NAME_NOT_FOUND; - else if (rc == -ENOEXEC) - rsp->hdr.Status = STATUS_DUPLICATE_OBJECTID; - else if (rc == -ENXIO) - rsp->hdr.Status = STATUS_NO_SUCH_DEVICE; - else if (rc == -EEXIST) - rsp->hdr.Status = STATUS_OBJECT_NAME_COLLISION; - else if (rc == -EMFILE) - rsp->hdr.Status = STATUS_INSUFFICIENT_RESOURCES; - if (!rsp->hdr.Status) - rsp->hdr.Status = STATUS_UNEXPECTED_IO_ERROR; - - if (fp) - ksmbd_fd_put(work, fp); - smb2_set_err_rsp(work); - ksmbd_debug(SMB, "Error response: %x\n", rsp->hdr.Status); - } - - kfree(name); - kfree(lc); - - return 0; -} - -static int readdir_info_level_struct_sz(int info_level) -{ - switch (info_level) { - case FILE_FULL_DIRECTORY_INFORMATION: - return sizeof(struct file_full_directory_info); - case FILE_BOTH_DIRECTORY_INFORMATION: - return sizeof(struct file_both_directory_info); - case FILE_DIRECTORY_INFORMATION: - return sizeof(struct file_directory_info); - case FILE_NAMES_INFORMATION: - return sizeof(struct file_names_info); - case FILEID_FULL_DIRECTORY_INFORMATION: - return sizeof(struct file_id_full_dir_info); - case FILEID_BOTH_DIRECTORY_INFORMATION: - return sizeof(struct file_id_both_directory_info); - case SMB_FIND_FILE_POSIX_INFO: - return sizeof(struct smb2_posix_info); - default: - return -EOPNOTSUPP; - } -} - -static int dentry_name(struct ksmbd_dir_info *d_info, int info_level) -{ - switch (info_level) { - case FILE_FULL_DIRECTORY_INFORMATION: - { - struct file_full_directory_info *ffdinfo; - - ffdinfo = (struct file_full_directory_info *)d_info->rptr; - d_info->rptr += le32_to_cpu(ffdinfo->NextEntryOffset); - d_info->name = ffdinfo->FileName; - d_info->name_len = le32_to_cpu(ffdinfo->FileNameLength); - return 0; - } - case FILE_BOTH_DIRECTORY_INFORMATION: - { - struct file_both_directory_info *fbdinfo; - - fbdinfo = (struct file_both_directory_info *)d_info->rptr; - d_info->rptr += le32_to_cpu(fbdinfo->NextEntryOffset); - d_info->name = fbdinfo->FileName; - d_info->name_len = le32_to_cpu(fbdinfo->FileNameLength); - return 0; - } - case FILE_DIRECTORY_INFORMATION: - { - struct file_directory_info *fdinfo; - - fdinfo = (struct file_directory_info *)d_info->rptr; - d_info->rptr += le32_to_cpu(fdinfo->NextEntryOffset); - d_info->name = fdinfo->FileName; - d_info->name_len = le32_to_cpu(fdinfo->FileNameLength); - return 0; - } - case FILE_NAMES_INFORMATION: - { - struct file_names_info *fninfo; - - fninfo = (struct file_names_info *)d_info->rptr; - d_info->rptr += le32_to_cpu(fninfo->NextEntryOffset); - d_info->name = fninfo->FileName; - d_info->name_len = le32_to_cpu(fninfo->FileNameLength); - return 0; - } - case FILEID_FULL_DIRECTORY_INFORMATION: - { - struct file_id_full_dir_info *dinfo; - - dinfo = (struct file_id_full_dir_info *)d_info->rptr; - d_info->rptr += le32_to_cpu(dinfo->NextEntryOffset); - d_info->name = dinfo->FileName; - d_info->name_len = le32_to_cpu(dinfo->FileNameLength); - return 0; - } - case FILEID_BOTH_DIRECTORY_INFORMATION: - { - struct file_id_both_directory_info *fibdinfo; - - fibdinfo = (struct file_id_both_directory_info *)d_info->rptr; - d_info->rptr += le32_to_cpu(fibdinfo->NextEntryOffset); - d_info->name = fibdinfo->FileName; - d_info->name_len = le32_to_cpu(fibdinfo->FileNameLength); - return 0; - } - case SMB_FIND_FILE_POSIX_INFO: - { - struct smb2_posix_info *posix_info; - - posix_info = (struct smb2_posix_info *)d_info->rptr; - d_info->rptr += le32_to_cpu(posix_info->NextEntryOffset); - d_info->name = posix_info->name; - d_info->name_len = le32_to_cpu(posix_info->name_len); - return 0; - } - default: - return -EINVAL; - } -} - -/** - * smb2_populate_readdir_entry() - encode directory entry in smb2 response - * buffer - * @conn: connection instance - * @info_level: smb information level - * @d_info: structure included variables for query dir - * @ksmbd_kstat: ksmbd wrapper of dirent stat information - * - * if directory has many entries, find first can't read it fully. - * find next might be called multiple times to read remaining dir entries - * - * Return: 0 on success, otherwise error - */ -static int smb2_populate_readdir_entry(struct ksmbd_conn *conn, int info_level, - struct ksmbd_dir_info *d_info, - struct ksmbd_kstat *ksmbd_kstat) -{ - int next_entry_offset = 0; - char *conv_name; - int conv_len; - void *kstat; - int struct_sz, rc = 0; - - conv_name = ksmbd_convert_dir_info_name(d_info, - conn->local_nls, - &conv_len); - if (!conv_name) - return -ENOMEM; - - /* Somehow the name has only terminating NULL bytes */ - if (conv_len < 0) { - rc = -EINVAL; - goto free_conv_name; - } - - struct_sz = readdir_info_level_struct_sz(info_level) - 1 + conv_len; - next_entry_offset = ALIGN(struct_sz, KSMBD_DIR_INFO_ALIGNMENT); - d_info->last_entry_off_align = next_entry_offset - struct_sz; - - if (next_entry_offset > d_info->out_buf_len) { - d_info->out_buf_len = 0; - rc = -ENOSPC; - goto free_conv_name; - } - - kstat = d_info->wptr; - if (info_level != FILE_NAMES_INFORMATION) - kstat = ksmbd_vfs_init_kstat(&d_info->wptr, ksmbd_kstat); - - switch (info_level) { - case FILE_FULL_DIRECTORY_INFORMATION: - { - struct file_full_directory_info *ffdinfo; - - ffdinfo = (struct file_full_directory_info *)kstat; - ffdinfo->FileNameLength = cpu_to_le32(conv_len); - ffdinfo->EaSize = - smb2_get_reparse_tag_special_file(ksmbd_kstat->kstat->mode); - if (ffdinfo->EaSize) - ffdinfo->ExtFileAttributes = FILE_ATTRIBUTE_REPARSE_POINT_LE; - if (d_info->hide_dot_file && d_info->name[0] == '.') - ffdinfo->ExtFileAttributes |= FILE_ATTRIBUTE_HIDDEN_LE; - memcpy(ffdinfo->FileName, conv_name, conv_len); - ffdinfo->NextEntryOffset = cpu_to_le32(next_entry_offset); - break; - } - case FILE_BOTH_DIRECTORY_INFORMATION: - { - struct file_both_directory_info *fbdinfo; - - fbdinfo = (struct file_both_directory_info *)kstat; - fbdinfo->FileNameLength = cpu_to_le32(conv_len); - fbdinfo->EaSize = - smb2_get_reparse_tag_special_file(ksmbd_kstat->kstat->mode); - if (fbdinfo->EaSize) - fbdinfo->ExtFileAttributes = FILE_ATTRIBUTE_REPARSE_POINT_LE; - fbdinfo->ShortNameLength = 0; - fbdinfo->Reserved = 0; - if (d_info->hide_dot_file && d_info->name[0] == '.') - fbdinfo->ExtFileAttributes |= FILE_ATTRIBUTE_HIDDEN_LE; - memcpy(fbdinfo->FileName, conv_name, conv_len); - fbdinfo->NextEntryOffset = cpu_to_le32(next_entry_offset); - break; - } - case FILE_DIRECTORY_INFORMATION: - { - struct file_directory_info *fdinfo; - - fdinfo = (struct file_directory_info *)kstat; - fdinfo->FileNameLength = cpu_to_le32(conv_len); - if (d_info->hide_dot_file && d_info->name[0] == '.') - fdinfo->ExtFileAttributes |= FILE_ATTRIBUTE_HIDDEN_LE; - memcpy(fdinfo->FileName, conv_name, conv_len); - fdinfo->NextEntryOffset = cpu_to_le32(next_entry_offset); - break; - } - case FILE_NAMES_INFORMATION: - { - struct file_names_info *fninfo; - - fninfo = (struct file_names_info *)kstat; - fninfo->FileNameLength = cpu_to_le32(conv_len); - memcpy(fninfo->FileName, conv_name, conv_len); - fninfo->NextEntryOffset = cpu_to_le32(next_entry_offset); - break; - } - case FILEID_FULL_DIRECTORY_INFORMATION: - { - struct file_id_full_dir_info *dinfo; - - dinfo = (struct file_id_full_dir_info *)kstat; - dinfo->FileNameLength = cpu_to_le32(conv_len); - dinfo->EaSize = - smb2_get_reparse_tag_special_file(ksmbd_kstat->kstat->mode); - if (dinfo->EaSize) - dinfo->ExtFileAttributes = FILE_ATTRIBUTE_REPARSE_POINT_LE; - dinfo->Reserved = 0; - dinfo->UniqueId = cpu_to_le64(ksmbd_kstat->kstat->ino); - if (d_info->hide_dot_file && d_info->name[0] == '.') - dinfo->ExtFileAttributes |= FILE_ATTRIBUTE_HIDDEN_LE; - memcpy(dinfo->FileName, conv_name, conv_len); - dinfo->NextEntryOffset = cpu_to_le32(next_entry_offset); - break; - } - case FILEID_BOTH_DIRECTORY_INFORMATION: - { - struct file_id_both_directory_info *fibdinfo; - - fibdinfo = (struct file_id_both_directory_info *)kstat; - fibdinfo->FileNameLength = cpu_to_le32(conv_len); - fibdinfo->EaSize = - smb2_get_reparse_tag_special_file(ksmbd_kstat->kstat->mode); - if (fibdinfo->EaSize) - fibdinfo->ExtFileAttributes = FILE_ATTRIBUTE_REPARSE_POINT_LE; - fibdinfo->UniqueId = cpu_to_le64(ksmbd_kstat->kstat->ino); - fibdinfo->ShortNameLength = 0; - fibdinfo->Reserved = 0; - fibdinfo->Reserved2 = cpu_to_le16(0); - if (d_info->hide_dot_file && d_info->name[0] == '.') - fibdinfo->ExtFileAttributes |= FILE_ATTRIBUTE_HIDDEN_LE; - memcpy(fibdinfo->FileName, conv_name, conv_len); - fibdinfo->NextEntryOffset = cpu_to_le32(next_entry_offset); - break; - } - case SMB_FIND_FILE_POSIX_INFO: - { - struct smb2_posix_info *posix_info; - u64 time; - - posix_info = (struct smb2_posix_info *)kstat; - posix_info->Ignored = 0; - posix_info->CreationTime = cpu_to_le64(ksmbd_kstat->create_time); - time = ksmbd_UnixTimeToNT(ksmbd_kstat->kstat->ctime); - posix_info->ChangeTime = cpu_to_le64(time); - time = ksmbd_UnixTimeToNT(ksmbd_kstat->kstat->atime); - posix_info->LastAccessTime = cpu_to_le64(time); - time = ksmbd_UnixTimeToNT(ksmbd_kstat->kstat->mtime); - posix_info->LastWriteTime = cpu_to_le64(time); - posix_info->EndOfFile = cpu_to_le64(ksmbd_kstat->kstat->size); - posix_info->AllocationSize = cpu_to_le64(ksmbd_kstat->kstat->blocks << 9); - posix_info->DeviceId = cpu_to_le32(ksmbd_kstat->kstat->rdev); - posix_info->HardLinks = cpu_to_le32(ksmbd_kstat->kstat->nlink); - posix_info->Mode = cpu_to_le32(ksmbd_kstat->kstat->mode & 0777); - posix_info->Inode = cpu_to_le64(ksmbd_kstat->kstat->ino); - posix_info->DosAttributes = - S_ISDIR(ksmbd_kstat->kstat->mode) ? - FILE_ATTRIBUTE_DIRECTORY_LE : FILE_ATTRIBUTE_ARCHIVE_LE; - if (d_info->hide_dot_file && d_info->name[0] == '.') - posix_info->DosAttributes |= FILE_ATTRIBUTE_HIDDEN_LE; - /* - * SidBuffer(32) contain two sids(Domain sid(16), UNIX group sid(16)). - * UNIX sid(16) = revision(1) + num_subauth(1) + authority(6) + - * sub_auth(4 * 1(num_subauth)) + RID(4). - */ - id_to_sid(from_kuid_munged(&init_user_ns, ksmbd_kstat->kstat->uid), - SIDUNIX_USER, (struct smb_sid *)&posix_info->SidBuffer[0]); - id_to_sid(from_kgid_munged(&init_user_ns, ksmbd_kstat->kstat->gid), - SIDUNIX_GROUP, (struct smb_sid *)&posix_info->SidBuffer[16]); - memcpy(posix_info->name, conv_name, conv_len); - posix_info->name_len = cpu_to_le32(conv_len); - posix_info->NextEntryOffset = cpu_to_le32(next_entry_offset); - break; - } - - } /* switch (info_level) */ - - d_info->last_entry_offset = d_info->data_count; - d_info->data_count += next_entry_offset; - d_info->out_buf_len -= next_entry_offset; - d_info->wptr += next_entry_offset; - - ksmbd_debug(SMB, - "info_level : %d, buf_len :%d, next_offset : %d, data_count : %d\n", - info_level, d_info->out_buf_len, - next_entry_offset, d_info->data_count); - -free_conv_name: - kfree(conv_name); - return rc; -} - -struct smb2_query_dir_private { - struct ksmbd_work *work; - char *search_pattern; - struct ksmbd_file *dir_fp; - - struct ksmbd_dir_info *d_info; - int info_level; -}; - -static void lock_dir(struct ksmbd_file *dir_fp) -{ - struct dentry *dir = dir_fp->filp->f_path.dentry; - - inode_lock_nested(d_inode(dir), I_MUTEX_PARENT); -} - -static void unlock_dir(struct ksmbd_file *dir_fp) -{ - struct dentry *dir = dir_fp->filp->f_path.dentry; - - inode_unlock(d_inode(dir)); -} - -static int process_query_dir_entries(struct smb2_query_dir_private *priv) -{ - struct user_namespace *user_ns = file_mnt_user_ns(priv->dir_fp->filp); - struct kstat kstat; - struct ksmbd_kstat ksmbd_kstat; - int rc; - int i; - - for (i = 0; i < priv->d_info->num_entry; i++) { - struct dentry *dent; - - if (dentry_name(priv->d_info, priv->info_level)) - return -EINVAL; - - lock_dir(priv->dir_fp); - dent = lookup_one(user_ns, priv->d_info->name, - priv->dir_fp->filp->f_path.dentry, - priv->d_info->name_len); - unlock_dir(priv->dir_fp); - - if (IS_ERR(dent)) { - ksmbd_debug(SMB, "Cannot lookup `%s' [%ld]\n", - priv->d_info->name, - PTR_ERR(dent)); - continue; - } - if (unlikely(d_is_negative(dent))) { - dput(dent); - ksmbd_debug(SMB, "Negative dentry `%s'\n", - priv->d_info->name); - continue; - } - - ksmbd_kstat.kstat = &kstat; - if (priv->info_level != FILE_NAMES_INFORMATION) - ksmbd_vfs_fill_dentry_attrs(priv->work, - user_ns, - dent, - &ksmbd_kstat); - - rc = smb2_populate_readdir_entry(priv->work->conn, - priv->info_level, - priv->d_info, - &ksmbd_kstat); - dput(dent); - if (rc) - return rc; - } - return 0; -} - -static int reserve_populate_dentry(struct ksmbd_dir_info *d_info, - int info_level) -{ - int struct_sz; - int conv_len; - int next_entry_offset; - - struct_sz = readdir_info_level_struct_sz(info_level); - if (struct_sz == -EOPNOTSUPP) - return -EOPNOTSUPP; - - conv_len = (d_info->name_len + 1) * 2; - next_entry_offset = ALIGN(struct_sz - 1 + conv_len, - KSMBD_DIR_INFO_ALIGNMENT); - - if (next_entry_offset > d_info->out_buf_len) { - d_info->out_buf_len = 0; - return -ENOSPC; - } - - switch (info_level) { - case FILE_FULL_DIRECTORY_INFORMATION: - { - struct file_full_directory_info *ffdinfo; - - ffdinfo = (struct file_full_directory_info *)d_info->wptr; - memcpy(ffdinfo->FileName, d_info->name, d_info->name_len); - ffdinfo->FileName[d_info->name_len] = 0x00; - ffdinfo->FileNameLength = cpu_to_le32(d_info->name_len); - ffdinfo->NextEntryOffset = cpu_to_le32(next_entry_offset); - break; - } - case FILE_BOTH_DIRECTORY_INFORMATION: - { - struct file_both_directory_info *fbdinfo; - - fbdinfo = (struct file_both_directory_info *)d_info->wptr; - memcpy(fbdinfo->FileName, d_info->name, d_info->name_len); - fbdinfo->FileName[d_info->name_len] = 0x00; - fbdinfo->FileNameLength = cpu_to_le32(d_info->name_len); - fbdinfo->NextEntryOffset = cpu_to_le32(next_entry_offset); - break; - } - case FILE_DIRECTORY_INFORMATION: - { - struct file_directory_info *fdinfo; - - fdinfo = (struct file_directory_info *)d_info->wptr; - memcpy(fdinfo->FileName, d_info->name, d_info->name_len); - fdinfo->FileName[d_info->name_len] = 0x00; - fdinfo->FileNameLength = cpu_to_le32(d_info->name_len); - fdinfo->NextEntryOffset = cpu_to_le32(next_entry_offset); - break; - } - case FILE_NAMES_INFORMATION: - { - struct file_names_info *fninfo; - - fninfo = (struct file_names_info *)d_info->wptr; - memcpy(fninfo->FileName, d_info->name, d_info->name_len); - fninfo->FileName[d_info->name_len] = 0x00; - fninfo->FileNameLength = cpu_to_le32(d_info->name_len); - fninfo->NextEntryOffset = cpu_to_le32(next_entry_offset); - break; - } - case FILEID_FULL_DIRECTORY_INFORMATION: - { - struct file_id_full_dir_info *dinfo; - - dinfo = (struct file_id_full_dir_info *)d_info->wptr; - memcpy(dinfo->FileName, d_info->name, d_info->name_len); - dinfo->FileName[d_info->name_len] = 0x00; - dinfo->FileNameLength = cpu_to_le32(d_info->name_len); - dinfo->NextEntryOffset = cpu_to_le32(next_entry_offset); - break; - } - case FILEID_BOTH_DIRECTORY_INFORMATION: - { - struct file_id_both_directory_info *fibdinfo; - - fibdinfo = (struct file_id_both_directory_info *)d_info->wptr; - memcpy(fibdinfo->FileName, d_info->name, d_info->name_len); - fibdinfo->FileName[d_info->name_len] = 0x00; - fibdinfo->FileNameLength = cpu_to_le32(d_info->name_len); - fibdinfo->NextEntryOffset = cpu_to_le32(next_entry_offset); - break; - } - case SMB_FIND_FILE_POSIX_INFO: - { - struct smb2_posix_info *posix_info; - - posix_info = (struct smb2_posix_info *)d_info->wptr; - memcpy(posix_info->name, d_info->name, d_info->name_len); - posix_info->name[d_info->name_len] = 0x00; - posix_info->name_len = cpu_to_le32(d_info->name_len); - posix_info->NextEntryOffset = - cpu_to_le32(next_entry_offset); - break; - } - } /* switch (info_level) */ - - d_info->num_entry++; - d_info->out_buf_len -= next_entry_offset; - d_info->wptr += next_entry_offset; - return 0; -} - -static bool __query_dir(struct dir_context *ctx, const char *name, int namlen, - loff_t offset, u64 ino, unsigned int d_type) -{ - struct ksmbd_readdir_data *buf; - struct smb2_query_dir_private *priv; - struct ksmbd_dir_info *d_info; - int rc; - - buf = container_of(ctx, struct ksmbd_readdir_data, ctx); - priv = buf->private; - d_info = priv->d_info; - - /* dot and dotdot entries are already reserved */ - if (!strcmp(".", name) || !strcmp("..", name)) - return true; - if (ksmbd_share_veto_filename(priv->work->tcon->share_conf, name)) - return true; - if (!match_pattern(name, namlen, priv->search_pattern)) - return true; - - d_info->name = name; - d_info->name_len = namlen; - rc = reserve_populate_dentry(d_info, priv->info_level); - if (rc) - return false; - if (d_info->flags & SMB2_RETURN_SINGLE_ENTRY) - d_info->out_buf_len = 0; - return true; -} - -static int verify_info_level(int info_level) -{ - switch (info_level) { - case FILE_FULL_DIRECTORY_INFORMATION: - case FILE_BOTH_DIRECTORY_INFORMATION: - case FILE_DIRECTORY_INFORMATION: - case FILE_NAMES_INFORMATION: - case FILEID_FULL_DIRECTORY_INFORMATION: - case FILEID_BOTH_DIRECTORY_INFORMATION: - case SMB_FIND_FILE_POSIX_INFO: - break; - default: - return -EOPNOTSUPP; - } - - return 0; -} - -static int smb2_resp_buf_len(struct ksmbd_work *work, unsigned short hdr2_len) -{ - int free_len; - - free_len = (int)(work->response_sz - - (get_rfc1002_len(work->response_buf) + 4)) - hdr2_len; - return free_len; -} - -static int smb2_calc_max_out_buf_len(struct ksmbd_work *work, - unsigned short hdr2_len, - unsigned int out_buf_len) -{ - int free_len; - - if (out_buf_len > work->conn->vals->max_trans_size) - return -EINVAL; - - free_len = smb2_resp_buf_len(work, hdr2_len); - if (free_len < 0) - return -EINVAL; - - return min_t(int, out_buf_len, free_len); -} - -int smb2_query_dir(struct ksmbd_work *work) -{ - struct ksmbd_conn *conn = work->conn; - struct smb2_query_directory_req *req; - struct smb2_query_directory_rsp *rsp; - struct ksmbd_share_config *share = work->tcon->share_conf; - struct ksmbd_file *dir_fp = NULL; - struct ksmbd_dir_info d_info; - int rc = 0; - char *srch_ptr = NULL; - unsigned char srch_flag; - int buffer_sz; - struct smb2_query_dir_private query_dir_private = {NULL, }; - - WORK_BUFFERS(work, req, rsp); - - if (ksmbd_override_fsids(work)) { - rsp->hdr.Status = STATUS_NO_MEMORY; - smb2_set_err_rsp(work); - return -ENOMEM; - } - - rc = verify_info_level(req->FileInformationClass); - if (rc) { - rc = -EFAULT; - goto err_out2; - } - - dir_fp = ksmbd_lookup_fd_slow(work, req->VolatileFileId, req->PersistentFileId); - if (!dir_fp) { - rc = -EBADF; - goto err_out2; - } - - if (!(dir_fp->daccess & FILE_LIST_DIRECTORY_LE) || - inode_permission(file_mnt_user_ns(dir_fp->filp), - file_inode(dir_fp->filp), - MAY_READ | MAY_EXEC)) { - pr_err("no right to enumerate directory (%pD)\n", dir_fp->filp); - rc = -EACCES; - goto err_out2; - } - - if (!S_ISDIR(file_inode(dir_fp->filp)->i_mode)) { - pr_err("can't do query dir for a file\n"); - rc = -EINVAL; - goto err_out2; - } - - srch_flag = req->Flags; - srch_ptr = smb_strndup_from_utf16(req->Buffer, - le16_to_cpu(req->FileNameLength), 1, - conn->local_nls); - if (IS_ERR(srch_ptr)) { - ksmbd_debug(SMB, "Search Pattern not found\n"); - rc = -EINVAL; - goto err_out2; - } else { - ksmbd_debug(SMB, "Search pattern is %s\n", srch_ptr); - } - - if (srch_flag & SMB2_REOPEN || srch_flag & SMB2_RESTART_SCANS) { - ksmbd_debug(SMB, "Restart directory scan\n"); - generic_file_llseek(dir_fp->filp, 0, SEEK_SET); - } - - memset(&d_info, 0, sizeof(struct ksmbd_dir_info)); - d_info.wptr = (char *)rsp->Buffer; - d_info.rptr = (char *)rsp->Buffer; - d_info.out_buf_len = - smb2_calc_max_out_buf_len(work, 8, - le32_to_cpu(req->OutputBufferLength)); - if (d_info.out_buf_len < 0) { - rc = -EINVAL; - goto err_out; - } - d_info.flags = srch_flag; - - /* - * reserve dot and dotdot entries in head of buffer - * in first response - */ - rc = ksmbd_populate_dot_dotdot_entries(work, req->FileInformationClass, - dir_fp, &d_info, srch_ptr, - smb2_populate_readdir_entry); - if (rc == -ENOSPC) - rc = 0; - else if (rc) - goto err_out; - - if (test_share_config_flag(share, KSMBD_SHARE_FLAG_HIDE_DOT_FILES)) - d_info.hide_dot_file = true; - - buffer_sz = d_info.out_buf_len; - d_info.rptr = d_info.wptr; - query_dir_private.work = work; - query_dir_private.search_pattern = srch_ptr; - query_dir_private.dir_fp = dir_fp; - query_dir_private.d_info = &d_info; - query_dir_private.info_level = req->FileInformationClass; - dir_fp->readdir_data.private = &query_dir_private; - set_ctx_actor(&dir_fp->readdir_data.ctx, __query_dir); - - rc = iterate_dir(dir_fp->filp, &dir_fp->readdir_data.ctx); - /* - * req->OutputBufferLength is too small to contain even one entry. - * In this case, it immediately returns OutputBufferLength 0 to client. - */ - if (!d_info.out_buf_len && !d_info.num_entry) - goto no_buf_len; - if (rc > 0 || rc == -ENOSPC) - rc = 0; - else if (rc) - goto err_out; - - d_info.wptr = d_info.rptr; - d_info.out_buf_len = buffer_sz; - rc = process_query_dir_entries(&query_dir_private); - if (rc) - goto err_out; - - if (!d_info.data_count && d_info.out_buf_len >= 0) { - if (srch_flag & SMB2_RETURN_SINGLE_ENTRY && !is_asterisk(srch_ptr)) { - rsp->hdr.Status = STATUS_NO_SUCH_FILE; - } else { - dir_fp->dot_dotdot[0] = dir_fp->dot_dotdot[1] = 0; - rsp->hdr.Status = STATUS_NO_MORE_FILES; - } - rsp->StructureSize = cpu_to_le16(9); - rsp->OutputBufferOffset = cpu_to_le16(0); - rsp->OutputBufferLength = cpu_to_le32(0); - rsp->Buffer[0] = 0; - inc_rfc1001_len(work->response_buf, 9); - } else { -no_buf_len: - ((struct file_directory_info *) - ((char *)rsp->Buffer + d_info.last_entry_offset)) - ->NextEntryOffset = 0; - if (d_info.data_count >= d_info.last_entry_off_align) - d_info.data_count -= d_info.last_entry_off_align; - - rsp->StructureSize = cpu_to_le16(9); - rsp->OutputBufferOffset = cpu_to_le16(72); - rsp->OutputBufferLength = cpu_to_le32(d_info.data_count); - inc_rfc1001_len(work->response_buf, 8 + d_info.data_count); - } - - kfree(srch_ptr); - ksmbd_fd_put(work, dir_fp); - ksmbd_revert_fsids(work); - return 0; - -err_out: - pr_err("error while processing smb2 query dir rc = %d\n", rc); - kfree(srch_ptr); - -err_out2: - if (rc == -EINVAL) - rsp->hdr.Status = STATUS_INVALID_PARAMETER; - else if (rc == -EACCES) - rsp->hdr.Status = STATUS_ACCESS_DENIED; - else if (rc == -ENOENT) - rsp->hdr.Status = STATUS_NO_SUCH_FILE; - else if (rc == -EBADF) - rsp->hdr.Status = STATUS_FILE_CLOSED; - else if (rc == -ENOMEM) - rsp->hdr.Status = STATUS_NO_MEMORY; - else if (rc == -EFAULT) - rsp->hdr.Status = STATUS_INVALID_INFO_CLASS; - else if (rc == -EIO) - rsp->hdr.Status = STATUS_FILE_CORRUPT_ERROR; - if (!rsp->hdr.Status) - rsp->hdr.Status = STATUS_UNEXPECTED_IO_ERROR; - - smb2_set_err_rsp(work); - ksmbd_fd_put(work, dir_fp); - ksmbd_revert_fsids(work); - return 0; -} - -/** - * buffer_check_err() - helper function to check buffer errors - * @reqOutputBufferLength: max buffer length expected in command response - * @rsp: query info response buffer contains output buffer length - * @rsp_org: base response buffer pointer in case of chained response - * @infoclass_size: query info class response buffer size - * - * Return: 0 on success, otherwise error - */ -static int buffer_check_err(int reqOutputBufferLength, - struct smb2_query_info_rsp *rsp, - void *rsp_org, int infoclass_size) -{ - if (reqOutputBufferLength < le32_to_cpu(rsp->OutputBufferLength)) { - if (reqOutputBufferLength < infoclass_size) { - pr_err("Invalid Buffer Size Requested\n"); - rsp->hdr.Status = STATUS_INFO_LENGTH_MISMATCH; - *(__be32 *)rsp_org = cpu_to_be32(sizeof(struct smb2_hdr)); - return -EINVAL; - } - - ksmbd_debug(SMB, "Buffer Overflow\n"); - rsp->hdr.Status = STATUS_BUFFER_OVERFLOW; - *(__be32 *)rsp_org = cpu_to_be32(sizeof(struct smb2_hdr) + - reqOutputBufferLength); - rsp->OutputBufferLength = cpu_to_le32(reqOutputBufferLength); - } - return 0; -} - -static void get_standard_info_pipe(struct smb2_query_info_rsp *rsp, - void *rsp_org) -{ - struct smb2_file_standard_info *sinfo; - - sinfo = (struct smb2_file_standard_info *)rsp->Buffer; - - sinfo->AllocationSize = cpu_to_le64(4096); - sinfo->EndOfFile = cpu_to_le64(0); - sinfo->NumberOfLinks = cpu_to_le32(1); - sinfo->DeletePending = 1; - sinfo->Directory = 0; - rsp->OutputBufferLength = - cpu_to_le32(sizeof(struct smb2_file_standard_info)); - inc_rfc1001_len(rsp_org, sizeof(struct smb2_file_standard_info)); -} - -static void get_internal_info_pipe(struct smb2_query_info_rsp *rsp, u64 num, - void *rsp_org) -{ - struct smb2_file_internal_info *file_info; - - file_info = (struct smb2_file_internal_info *)rsp->Buffer; - - /* any unique number */ - file_info->IndexNumber = cpu_to_le64(num | (1ULL << 63)); - rsp->OutputBufferLength = - cpu_to_le32(sizeof(struct smb2_file_internal_info)); - inc_rfc1001_len(rsp_org, sizeof(struct smb2_file_internal_info)); -} - -static int smb2_get_info_file_pipe(struct ksmbd_session *sess, - struct smb2_query_info_req *req, - struct smb2_query_info_rsp *rsp, - void *rsp_org) -{ - u64 id; - int rc; - - /* - * Windows can sometime send query file info request on - * pipe without opening it, checking error condition here - */ - id = req->VolatileFileId; - if (!ksmbd_session_rpc_method(sess, id)) - return -ENOENT; - - ksmbd_debug(SMB, "FileInfoClass %u, FileId 0x%llx\n", - req->FileInfoClass, req->VolatileFileId); - - switch (req->FileInfoClass) { - case FILE_STANDARD_INFORMATION: - get_standard_info_pipe(rsp, rsp_org); - rc = buffer_check_err(le32_to_cpu(req->OutputBufferLength), - rsp, rsp_org, - FILE_STANDARD_INFORMATION_SIZE); - break; - case FILE_INTERNAL_INFORMATION: - get_internal_info_pipe(rsp, id, rsp_org); - rc = buffer_check_err(le32_to_cpu(req->OutputBufferLength), - rsp, rsp_org, - FILE_INTERNAL_INFORMATION_SIZE); - break; - default: - ksmbd_debug(SMB, "smb2_info_file_pipe for %u not supported\n", - req->FileInfoClass); - rc = -EOPNOTSUPP; - } - return rc; -} - -/** - * smb2_get_ea() - handler for smb2 get extended attribute command - * @work: smb work containing query info command buffer - * @fp: ksmbd_file pointer - * @req: get extended attribute request - * @rsp: response buffer pointer - * @rsp_org: base response buffer pointer in case of chained response - * - * Return: 0 on success, otherwise error - */ -static int smb2_get_ea(struct ksmbd_work *work, struct ksmbd_file *fp, - struct smb2_query_info_req *req, - struct smb2_query_info_rsp *rsp, void *rsp_org) -{ - struct smb2_ea_info *eainfo, *prev_eainfo; - char *name, *ptr, *xattr_list = NULL, *buf; - int rc, name_len, value_len, xattr_list_len, idx; - ssize_t buf_free_len, alignment_bytes, next_offset, rsp_data_cnt = 0; - struct smb2_ea_info_req *ea_req = NULL; - const struct path *path; - struct user_namespace *user_ns = file_mnt_user_ns(fp->filp); - - if (!(fp->daccess & FILE_READ_EA_LE)) { - pr_err("Not permitted to read ext attr : 0x%x\n", - fp->daccess); - return -EACCES; - } - - path = &fp->filp->f_path; - /* single EA entry is requested with given user.* name */ - if (req->InputBufferLength) { - if (le32_to_cpu(req->InputBufferLength) < - sizeof(struct smb2_ea_info_req)) - return -EINVAL; - - ea_req = (struct smb2_ea_info_req *)req->Buffer; - } else { - /* need to send all EAs, if no specific EA is requested*/ - if (le32_to_cpu(req->Flags) & SL_RETURN_SINGLE_ENTRY) - ksmbd_debug(SMB, - "All EAs are requested but need to send single EA entry in rsp flags 0x%x\n", - le32_to_cpu(req->Flags)); - } - - buf_free_len = - smb2_calc_max_out_buf_len(work, 8, - le32_to_cpu(req->OutputBufferLength)); - if (buf_free_len < 0) - return -EINVAL; - - rc = ksmbd_vfs_listxattr(path->dentry, &xattr_list); - if (rc < 0) { - rsp->hdr.Status = STATUS_INVALID_HANDLE; - goto out; - } else if (!rc) { /* there is no EA in the file */ - ksmbd_debug(SMB, "no ea data in the file\n"); - goto done; - } - xattr_list_len = rc; - - ptr = (char *)rsp->Buffer; - eainfo = (struct smb2_ea_info *)ptr; - prev_eainfo = eainfo; - idx = 0; - - while (idx < xattr_list_len) { - name = xattr_list + idx; - name_len = strlen(name); - - ksmbd_debug(SMB, "%s, len %d\n", name, name_len); - idx += name_len + 1; - - /* - * CIFS does not support EA other than user.* namespace, - * still keep the framework generic, to list other attrs - * in future. - */ - if (strncmp(name, XATTR_USER_PREFIX, XATTR_USER_PREFIX_LEN)) - continue; - - if (!strncmp(&name[XATTR_USER_PREFIX_LEN], STREAM_PREFIX, - STREAM_PREFIX_LEN)) - continue; - - if (req->InputBufferLength && - strncmp(&name[XATTR_USER_PREFIX_LEN], ea_req->name, - ea_req->EaNameLength)) - continue; - - if (!strncmp(&name[XATTR_USER_PREFIX_LEN], - DOS_ATTRIBUTE_PREFIX, DOS_ATTRIBUTE_PREFIX_LEN)) - continue; - - if (!strncmp(name, XATTR_USER_PREFIX, XATTR_USER_PREFIX_LEN)) - name_len -= XATTR_USER_PREFIX_LEN; - - ptr = (char *)(&eainfo->name + name_len + 1); - buf_free_len -= (offsetof(struct smb2_ea_info, name) + - name_len + 1); - /* bailout if xattr can't fit in buf_free_len */ - value_len = ksmbd_vfs_getxattr(user_ns, path->dentry, - name, &buf); - if (value_len <= 0) { - rc = -ENOENT; - rsp->hdr.Status = STATUS_INVALID_HANDLE; - goto out; - } - - buf_free_len -= value_len; - if (buf_free_len < 0) { - kfree(buf); - break; - } - - memcpy(ptr, buf, value_len); - kfree(buf); - - ptr += value_len; - eainfo->Flags = 0; - eainfo->EaNameLength = name_len; - - if (!strncmp(name, XATTR_USER_PREFIX, XATTR_USER_PREFIX_LEN)) - memcpy(eainfo->name, &name[XATTR_USER_PREFIX_LEN], - name_len); - else - memcpy(eainfo->name, name, name_len); - - eainfo->name[name_len] = '\0'; - eainfo->EaValueLength = cpu_to_le16(value_len); - next_offset = offsetof(struct smb2_ea_info, name) + - name_len + 1 + value_len; - - /* align next xattr entry at 4 byte bundary */ - alignment_bytes = ((next_offset + 3) & ~3) - next_offset; - if (alignment_bytes) { - memset(ptr, '\0', alignment_bytes); - ptr += alignment_bytes; - next_offset += alignment_bytes; - buf_free_len -= alignment_bytes; - } - eainfo->NextEntryOffset = cpu_to_le32(next_offset); - prev_eainfo = eainfo; - eainfo = (struct smb2_ea_info *)ptr; - rsp_data_cnt += next_offset; - - if (req->InputBufferLength) { - ksmbd_debug(SMB, "single entry requested\n"); - break; - } - } - - /* no more ea entries */ - prev_eainfo->NextEntryOffset = 0; -done: - rc = 0; - if (rsp_data_cnt == 0) - rsp->hdr.Status = STATUS_NO_EAS_ON_FILE; - rsp->OutputBufferLength = cpu_to_le32(rsp_data_cnt); - inc_rfc1001_len(rsp_org, rsp_data_cnt); -out: - kvfree(xattr_list); - return rc; -} - -static void get_file_access_info(struct smb2_query_info_rsp *rsp, - struct ksmbd_file *fp, void *rsp_org) -{ - struct smb2_file_access_info *file_info; - - file_info = (struct smb2_file_access_info *)rsp->Buffer; - file_info->AccessFlags = fp->daccess; - rsp->OutputBufferLength = - cpu_to_le32(sizeof(struct smb2_file_access_info)); - inc_rfc1001_len(rsp_org, sizeof(struct smb2_file_access_info)); -} - -static int get_file_basic_info(struct smb2_query_info_rsp *rsp, - struct ksmbd_file *fp, void *rsp_org) -{ - struct smb2_file_basic_info *basic_info; - struct kstat stat; - u64 time; - - if (!(fp->daccess & FILE_READ_ATTRIBUTES_LE)) { - pr_err("no right to read the attributes : 0x%x\n", - fp->daccess); - return -EACCES; - } - - basic_info = (struct smb2_file_basic_info *)rsp->Buffer; - generic_fillattr(file_mnt_user_ns(fp->filp), file_inode(fp->filp), - &stat); - basic_info->CreationTime = cpu_to_le64(fp->create_time); - time = ksmbd_UnixTimeToNT(stat.atime); - basic_info->LastAccessTime = cpu_to_le64(time); - time = ksmbd_UnixTimeToNT(stat.mtime); - basic_info->LastWriteTime = cpu_to_le64(time); - time = ksmbd_UnixTimeToNT(stat.ctime); - basic_info->ChangeTime = cpu_to_le64(time); - basic_info->Attributes = fp->f_ci->m_fattr; - basic_info->Pad1 = 0; - rsp->OutputBufferLength = - cpu_to_le32(sizeof(struct smb2_file_basic_info)); - inc_rfc1001_len(rsp_org, sizeof(struct smb2_file_basic_info)); - return 0; -} - -static void get_file_standard_info(struct smb2_query_info_rsp *rsp, - struct ksmbd_file *fp, void *rsp_org) -{ - struct smb2_file_standard_info *sinfo; - unsigned int delete_pending; - struct inode *inode; - struct kstat stat; - - inode = file_inode(fp->filp); - generic_fillattr(file_mnt_user_ns(fp->filp), inode, &stat); - - sinfo = (struct smb2_file_standard_info *)rsp->Buffer; - delete_pending = ksmbd_inode_pending_delete(fp); - - sinfo->AllocationSize = cpu_to_le64(inode->i_blocks << 9); - sinfo->EndOfFile = S_ISDIR(stat.mode) ? 0 : cpu_to_le64(stat.size); - sinfo->NumberOfLinks = cpu_to_le32(get_nlink(&stat) - delete_pending); - sinfo->DeletePending = delete_pending; - sinfo->Directory = S_ISDIR(stat.mode) ? 1 : 0; - rsp->OutputBufferLength = - cpu_to_le32(sizeof(struct smb2_file_standard_info)); - inc_rfc1001_len(rsp_org, - sizeof(struct smb2_file_standard_info)); -} - -static void get_file_alignment_info(struct smb2_query_info_rsp *rsp, - void *rsp_org) -{ - struct smb2_file_alignment_info *file_info; - - file_info = (struct smb2_file_alignment_info *)rsp->Buffer; - file_info->AlignmentRequirement = 0; - rsp->OutputBufferLength = - cpu_to_le32(sizeof(struct smb2_file_alignment_info)); - inc_rfc1001_len(rsp_org, - sizeof(struct smb2_file_alignment_info)); -} - -static int get_file_all_info(struct ksmbd_work *work, - struct smb2_query_info_rsp *rsp, - struct ksmbd_file *fp, - void *rsp_org) -{ - struct ksmbd_conn *conn = work->conn; - struct smb2_file_all_info *file_info; - unsigned int delete_pending; - struct inode *inode; - struct kstat stat; - int conv_len; - char *filename; - u64 time; - - if (!(fp->daccess & FILE_READ_ATTRIBUTES_LE)) { - ksmbd_debug(SMB, "no right to read the attributes : 0x%x\n", - fp->daccess); - return -EACCES; - } - - filename = convert_to_nt_pathname(work->tcon->share_conf, &fp->filp->f_path); - if (IS_ERR(filename)) - return PTR_ERR(filename); - - inode = file_inode(fp->filp); - generic_fillattr(file_mnt_user_ns(fp->filp), inode, &stat); - - ksmbd_debug(SMB, "filename = %s\n", filename); - delete_pending = ksmbd_inode_pending_delete(fp); - file_info = (struct smb2_file_all_info *)rsp->Buffer; - - file_info->CreationTime = cpu_to_le64(fp->create_time); - time = ksmbd_UnixTimeToNT(stat.atime); - file_info->LastAccessTime = cpu_to_le64(time); - time = ksmbd_UnixTimeToNT(stat.mtime); - file_info->LastWriteTime = cpu_to_le64(time); - time = ksmbd_UnixTimeToNT(stat.ctime); - file_info->ChangeTime = cpu_to_le64(time); - file_info->Attributes = fp->f_ci->m_fattr; - file_info->Pad1 = 0; - file_info->AllocationSize = - cpu_to_le64(inode->i_blocks << 9); - file_info->EndOfFile = S_ISDIR(stat.mode) ? 0 : cpu_to_le64(stat.size); - file_info->NumberOfLinks = - cpu_to_le32(get_nlink(&stat) - delete_pending); - file_info->DeletePending = delete_pending; - file_info->Directory = S_ISDIR(stat.mode) ? 1 : 0; - file_info->Pad2 = 0; - file_info->IndexNumber = cpu_to_le64(stat.ino); - file_info->EASize = 0; - file_info->AccessFlags = fp->daccess; - file_info->CurrentByteOffset = cpu_to_le64(fp->filp->f_pos); - file_info->Mode = fp->coption; - file_info->AlignmentRequirement = 0; - conv_len = smbConvertToUTF16((__le16 *)file_info->FileName, filename, - PATH_MAX, conn->local_nls, 0); - conv_len *= 2; - file_info->FileNameLength = cpu_to_le32(conv_len); - rsp->OutputBufferLength = - cpu_to_le32(sizeof(struct smb2_file_all_info) + conv_len - 1); - kfree(filename); - inc_rfc1001_len(rsp_org, le32_to_cpu(rsp->OutputBufferLength)); - return 0; -} - -static void get_file_alternate_info(struct ksmbd_work *work, - struct smb2_query_info_rsp *rsp, - struct ksmbd_file *fp, - void *rsp_org) -{ - struct ksmbd_conn *conn = work->conn; - struct smb2_file_alt_name_info *file_info; - struct dentry *dentry = fp->filp->f_path.dentry; - int conv_len; - - spin_lock(&dentry->d_lock); - file_info = (struct smb2_file_alt_name_info *)rsp->Buffer; - conv_len = ksmbd_extract_shortname(conn, - dentry->d_name.name, - file_info->FileName); - spin_unlock(&dentry->d_lock); - file_info->FileNameLength = cpu_to_le32(conv_len); - rsp->OutputBufferLength = - cpu_to_le32(sizeof(struct smb2_file_alt_name_info) + conv_len); - inc_rfc1001_len(rsp_org, le32_to_cpu(rsp->OutputBufferLength)); -} - -static void get_file_stream_info(struct ksmbd_work *work, - struct smb2_query_info_rsp *rsp, - struct ksmbd_file *fp, - void *rsp_org) -{ - struct ksmbd_conn *conn = work->conn; - struct smb2_file_stream_info *file_info; - char *stream_name, *xattr_list = NULL, *stream_buf; - struct kstat stat; - const struct path *path = &fp->filp->f_path; - ssize_t xattr_list_len; - int nbytes = 0, streamlen, stream_name_len, next, idx = 0; - int buf_free_len; - struct smb2_query_info_req *req = ksmbd_req_buf_next(work); - - generic_fillattr(file_mnt_user_ns(fp->filp), file_inode(fp->filp), - &stat); - file_info = (struct smb2_file_stream_info *)rsp->Buffer; - - buf_free_len = - smb2_calc_max_out_buf_len(work, 8, - le32_to_cpu(req->OutputBufferLength)); - if (buf_free_len < 0) - goto out; - - xattr_list_len = ksmbd_vfs_listxattr(path->dentry, &xattr_list); - if (xattr_list_len < 0) { - goto out; - } else if (!xattr_list_len) { - ksmbd_debug(SMB, "empty xattr in the file\n"); - goto out; - } - - while (idx < xattr_list_len) { - stream_name = xattr_list + idx; - streamlen = strlen(stream_name); - idx += streamlen + 1; - - ksmbd_debug(SMB, "%s, len %d\n", stream_name, streamlen); - - if (strncmp(&stream_name[XATTR_USER_PREFIX_LEN], - STREAM_PREFIX, STREAM_PREFIX_LEN)) - continue; - - stream_name_len = streamlen - (XATTR_USER_PREFIX_LEN + - STREAM_PREFIX_LEN); - streamlen = stream_name_len; - - /* plus : size */ - streamlen += 1; - stream_buf = kmalloc(streamlen + 1, GFP_KERNEL); - if (!stream_buf) - break; - - streamlen = snprintf(stream_buf, streamlen + 1, - ":%s", &stream_name[XATTR_NAME_STREAM_LEN]); - - next = sizeof(struct smb2_file_stream_info) + streamlen * 2; - if (next > buf_free_len) { - kfree(stream_buf); - break; - } - - file_info = (struct smb2_file_stream_info *)&rsp->Buffer[nbytes]; - streamlen = smbConvertToUTF16((__le16 *)file_info->StreamName, - stream_buf, streamlen, - conn->local_nls, 0); - streamlen *= 2; - kfree(stream_buf); - file_info->StreamNameLength = cpu_to_le32(streamlen); - file_info->StreamSize = cpu_to_le64(stream_name_len); - file_info->StreamAllocationSize = cpu_to_le64(stream_name_len); - - nbytes += next; - buf_free_len -= next; - file_info->NextEntryOffset = cpu_to_le32(next); - } - -out: - if (!S_ISDIR(stat.mode) && - buf_free_len >= sizeof(struct smb2_file_stream_info) + 7 * 2) { - file_info = (struct smb2_file_stream_info *) - &rsp->Buffer[nbytes]; - streamlen = smbConvertToUTF16((__le16 *)file_info->StreamName, - "::$DATA", 7, conn->local_nls, 0); - streamlen *= 2; - file_info->StreamNameLength = cpu_to_le32(streamlen); - file_info->StreamSize = cpu_to_le64(stat.size); - file_info->StreamAllocationSize = cpu_to_le64(stat.blocks << 9); - nbytes += sizeof(struct smb2_file_stream_info) + streamlen; - } - - /* last entry offset should be 0 */ - file_info->NextEntryOffset = 0; - kvfree(xattr_list); - - rsp->OutputBufferLength = cpu_to_le32(nbytes); - inc_rfc1001_len(rsp_org, nbytes); -} - -static void get_file_internal_info(struct smb2_query_info_rsp *rsp, - struct ksmbd_file *fp, void *rsp_org) -{ - struct smb2_file_internal_info *file_info; - struct kstat stat; - - generic_fillattr(file_mnt_user_ns(fp->filp), file_inode(fp->filp), - &stat); - file_info = (struct smb2_file_internal_info *)rsp->Buffer; - file_info->IndexNumber = cpu_to_le64(stat.ino); - rsp->OutputBufferLength = - cpu_to_le32(sizeof(struct smb2_file_internal_info)); - inc_rfc1001_len(rsp_org, sizeof(struct smb2_file_internal_info)); -} - -static int get_file_network_open_info(struct smb2_query_info_rsp *rsp, - struct ksmbd_file *fp, void *rsp_org) -{ - struct smb2_file_ntwrk_info *file_info; - struct inode *inode; - struct kstat stat; - u64 time; - - if (!(fp->daccess & FILE_READ_ATTRIBUTES_LE)) { - pr_err("no right to read the attributes : 0x%x\n", - fp->daccess); - return -EACCES; - } - - file_info = (struct smb2_file_ntwrk_info *)rsp->Buffer; - - inode = file_inode(fp->filp); - generic_fillattr(file_mnt_user_ns(fp->filp), inode, &stat); - - file_info->CreationTime = cpu_to_le64(fp->create_time); - time = ksmbd_UnixTimeToNT(stat.atime); - file_info->LastAccessTime = cpu_to_le64(time); - time = ksmbd_UnixTimeToNT(stat.mtime); - file_info->LastWriteTime = cpu_to_le64(time); - time = ksmbd_UnixTimeToNT(stat.ctime); - file_info->ChangeTime = cpu_to_le64(time); - file_info->Attributes = fp->f_ci->m_fattr; - file_info->AllocationSize = - cpu_to_le64(inode->i_blocks << 9); - file_info->EndOfFile = S_ISDIR(stat.mode) ? 0 : cpu_to_le64(stat.size); - file_info->Reserved = cpu_to_le32(0); - rsp->OutputBufferLength = - cpu_to_le32(sizeof(struct smb2_file_ntwrk_info)); - inc_rfc1001_len(rsp_org, sizeof(struct smb2_file_ntwrk_info)); - return 0; -} - -static void get_file_ea_info(struct smb2_query_info_rsp *rsp, void *rsp_org) -{ - struct smb2_file_ea_info *file_info; - - file_info = (struct smb2_file_ea_info *)rsp->Buffer; - file_info->EASize = 0; - rsp->OutputBufferLength = - cpu_to_le32(sizeof(struct smb2_file_ea_info)); - inc_rfc1001_len(rsp_org, sizeof(struct smb2_file_ea_info)); -} - -static void get_file_position_info(struct smb2_query_info_rsp *rsp, - struct ksmbd_file *fp, void *rsp_org) -{ - struct smb2_file_pos_info *file_info; - - file_info = (struct smb2_file_pos_info *)rsp->Buffer; - file_info->CurrentByteOffset = cpu_to_le64(fp->filp->f_pos); - rsp->OutputBufferLength = - cpu_to_le32(sizeof(struct smb2_file_pos_info)); - inc_rfc1001_len(rsp_org, sizeof(struct smb2_file_pos_info)); -} - -static void get_file_mode_info(struct smb2_query_info_rsp *rsp, - struct ksmbd_file *fp, void *rsp_org) -{ - struct smb2_file_mode_info *file_info; - - file_info = (struct smb2_file_mode_info *)rsp->Buffer; - file_info->Mode = fp->coption & FILE_MODE_INFO_MASK; - rsp->OutputBufferLength = - cpu_to_le32(sizeof(struct smb2_file_mode_info)); - inc_rfc1001_len(rsp_org, sizeof(struct smb2_file_mode_info)); -} - -static void get_file_compression_info(struct smb2_query_info_rsp *rsp, - struct ksmbd_file *fp, void *rsp_org) -{ - struct smb2_file_comp_info *file_info; - struct kstat stat; - - generic_fillattr(file_mnt_user_ns(fp->filp), file_inode(fp->filp), - &stat); - - file_info = (struct smb2_file_comp_info *)rsp->Buffer; - file_info->CompressedFileSize = cpu_to_le64(stat.blocks << 9); - file_info->CompressionFormat = COMPRESSION_FORMAT_NONE; - file_info->CompressionUnitShift = 0; - file_info->ChunkShift = 0; - file_info->ClusterShift = 0; - memset(&file_info->Reserved[0], 0, 3); - - rsp->OutputBufferLength = - cpu_to_le32(sizeof(struct smb2_file_comp_info)); - inc_rfc1001_len(rsp_org, sizeof(struct smb2_file_comp_info)); -} - -static int get_file_attribute_tag_info(struct smb2_query_info_rsp *rsp, - struct ksmbd_file *fp, void *rsp_org) -{ - struct smb2_file_attr_tag_info *file_info; - - if (!(fp->daccess & FILE_READ_ATTRIBUTES_LE)) { - pr_err("no right to read the attributes : 0x%x\n", - fp->daccess); - return -EACCES; - } - - file_info = (struct smb2_file_attr_tag_info *)rsp->Buffer; - file_info->FileAttributes = fp->f_ci->m_fattr; - file_info->ReparseTag = 0; - rsp->OutputBufferLength = - cpu_to_le32(sizeof(struct smb2_file_attr_tag_info)); - inc_rfc1001_len(rsp_org, sizeof(struct smb2_file_attr_tag_info)); - return 0; -} - -static int find_file_posix_info(struct smb2_query_info_rsp *rsp, - struct ksmbd_file *fp, void *rsp_org) -{ - struct smb311_posix_qinfo *file_info; - struct inode *inode = file_inode(fp->filp); - struct user_namespace *user_ns = file_mnt_user_ns(fp->filp); - vfsuid_t vfsuid = i_uid_into_vfsuid(user_ns, inode); - vfsgid_t vfsgid = i_gid_into_vfsgid(user_ns, inode); - u64 time; - int out_buf_len = sizeof(struct smb311_posix_qinfo) + 32; - - file_info = (struct smb311_posix_qinfo *)rsp->Buffer; - file_info->CreationTime = cpu_to_le64(fp->create_time); - time = ksmbd_UnixTimeToNT(inode->i_atime); - file_info->LastAccessTime = cpu_to_le64(time); - time = ksmbd_UnixTimeToNT(inode->i_mtime); - file_info->LastWriteTime = cpu_to_le64(time); - time = ksmbd_UnixTimeToNT(inode->i_ctime); - file_info->ChangeTime = cpu_to_le64(time); - file_info->DosAttributes = fp->f_ci->m_fattr; - file_info->Inode = cpu_to_le64(inode->i_ino); - file_info->EndOfFile = cpu_to_le64(inode->i_size); - file_info->AllocationSize = cpu_to_le64(inode->i_blocks << 9); - file_info->HardLinks = cpu_to_le32(inode->i_nlink); - file_info->Mode = cpu_to_le32(inode->i_mode & 0777); - file_info->DeviceId = cpu_to_le32(inode->i_rdev); - - /* - * Sids(32) contain two sids(Domain sid(16), UNIX group sid(16)). - * UNIX sid(16) = revision(1) + num_subauth(1) + authority(6) + - * sub_auth(4 * 1(num_subauth)) + RID(4). - */ - id_to_sid(from_kuid_munged(&init_user_ns, vfsuid_into_kuid(vfsuid)), - SIDUNIX_USER, (struct smb_sid *)&file_info->Sids[0]); - id_to_sid(from_kgid_munged(&init_user_ns, vfsgid_into_kgid(vfsgid)), - SIDUNIX_GROUP, (struct smb_sid *)&file_info->Sids[16]); - - rsp->OutputBufferLength = cpu_to_le32(out_buf_len); - inc_rfc1001_len(rsp_org, out_buf_len); - return out_buf_len; -} - -static int smb2_get_info_file(struct ksmbd_work *work, - struct smb2_query_info_req *req, - struct smb2_query_info_rsp *rsp) -{ - struct ksmbd_file *fp; - int fileinfoclass = 0; - int rc = 0; - int file_infoclass_size; - unsigned int id = KSMBD_NO_FID, pid = KSMBD_NO_FID; - - if (test_share_config_flag(work->tcon->share_conf, - KSMBD_SHARE_FLAG_PIPE)) { - /* smb2 info file called for pipe */ - return smb2_get_info_file_pipe(work->sess, req, rsp, - work->response_buf); - } - - if (work->next_smb2_rcv_hdr_off) { - if (!has_file_id(req->VolatileFileId)) { - ksmbd_debug(SMB, "Compound request set FID = %llu\n", - work->compound_fid); - id = work->compound_fid; - pid = work->compound_pfid; - } - } - - if (!has_file_id(id)) { - id = req->VolatileFileId; - pid = req->PersistentFileId; - } - - fp = ksmbd_lookup_fd_slow(work, id, pid); - if (!fp) - return -ENOENT; - - fileinfoclass = req->FileInfoClass; - - switch (fileinfoclass) { - case FILE_ACCESS_INFORMATION: - get_file_access_info(rsp, fp, work->response_buf); - file_infoclass_size = FILE_ACCESS_INFORMATION_SIZE; - break; - - case FILE_BASIC_INFORMATION: - rc = get_file_basic_info(rsp, fp, work->response_buf); - file_infoclass_size = FILE_BASIC_INFORMATION_SIZE; - break; - - case FILE_STANDARD_INFORMATION: - get_file_standard_info(rsp, fp, work->response_buf); - file_infoclass_size = FILE_STANDARD_INFORMATION_SIZE; - break; - - case FILE_ALIGNMENT_INFORMATION: - get_file_alignment_info(rsp, work->response_buf); - file_infoclass_size = FILE_ALIGNMENT_INFORMATION_SIZE; - break; - - case FILE_ALL_INFORMATION: - rc = get_file_all_info(work, rsp, fp, work->response_buf); - file_infoclass_size = FILE_ALL_INFORMATION_SIZE; - break; - - case FILE_ALTERNATE_NAME_INFORMATION: - get_file_alternate_info(work, rsp, fp, work->response_buf); - file_infoclass_size = FILE_ALTERNATE_NAME_INFORMATION_SIZE; - break; - - case FILE_STREAM_INFORMATION: - get_file_stream_info(work, rsp, fp, work->response_buf); - file_infoclass_size = FILE_STREAM_INFORMATION_SIZE; - break; - - case FILE_INTERNAL_INFORMATION: - get_file_internal_info(rsp, fp, work->response_buf); - file_infoclass_size = FILE_INTERNAL_INFORMATION_SIZE; - break; - - case FILE_NETWORK_OPEN_INFORMATION: - rc = get_file_network_open_info(rsp, fp, work->response_buf); - file_infoclass_size = FILE_NETWORK_OPEN_INFORMATION_SIZE; - break; - - case FILE_EA_INFORMATION: - get_file_ea_info(rsp, work->response_buf); - file_infoclass_size = FILE_EA_INFORMATION_SIZE; - break; - - case FILE_FULL_EA_INFORMATION: - rc = smb2_get_ea(work, fp, req, rsp, work->response_buf); - file_infoclass_size = FILE_FULL_EA_INFORMATION_SIZE; - break; - - case FILE_POSITION_INFORMATION: - get_file_position_info(rsp, fp, work->response_buf); - file_infoclass_size = FILE_POSITION_INFORMATION_SIZE; - break; - - case FILE_MODE_INFORMATION: - get_file_mode_info(rsp, fp, work->response_buf); - file_infoclass_size = FILE_MODE_INFORMATION_SIZE; - break; - - case FILE_COMPRESSION_INFORMATION: - get_file_compression_info(rsp, fp, work->response_buf); - file_infoclass_size = FILE_COMPRESSION_INFORMATION_SIZE; - break; - - case FILE_ATTRIBUTE_TAG_INFORMATION: - rc = get_file_attribute_tag_info(rsp, fp, work->response_buf); - file_infoclass_size = FILE_ATTRIBUTE_TAG_INFORMATION_SIZE; - break; - case SMB_FIND_FILE_POSIX_INFO: - if (!work->tcon->posix_extensions) { - pr_err("client doesn't negotiate with SMB3.1.1 POSIX Extensions\n"); - rc = -EOPNOTSUPP; - } else { - file_infoclass_size = find_file_posix_info(rsp, fp, - work->response_buf); - } - break; - default: - ksmbd_debug(SMB, "fileinfoclass %d not supported yet\n", - fileinfoclass); - rc = -EOPNOTSUPP; - } - if (!rc) - rc = buffer_check_err(le32_to_cpu(req->OutputBufferLength), - rsp, work->response_buf, - file_infoclass_size); - ksmbd_fd_put(work, fp); - return rc; -} - -static int smb2_get_info_filesystem(struct ksmbd_work *work, - struct smb2_query_info_req *req, - struct smb2_query_info_rsp *rsp) -{ - struct ksmbd_session *sess = work->sess; - struct ksmbd_conn *conn = work->conn; - struct ksmbd_share_config *share = work->tcon->share_conf; - int fsinfoclass = 0; - struct kstatfs stfs; - struct path path; - int rc = 0, len; - int fs_infoclass_size = 0; - - if (!share->path) - return -EIO; - - rc = kern_path(share->path, LOOKUP_NO_SYMLINKS, &path); - if (rc) { - pr_err("cannot create vfs path\n"); - return -EIO; - } - - rc = vfs_statfs(&path, &stfs); - if (rc) { - pr_err("cannot do stat of path %s\n", share->path); - path_put(&path); - return -EIO; - } - - fsinfoclass = req->FileInfoClass; - - switch (fsinfoclass) { - case FS_DEVICE_INFORMATION: - { - struct filesystem_device_info *info; - - info = (struct filesystem_device_info *)rsp->Buffer; - - info->DeviceType = cpu_to_le32(stfs.f_type); - info->DeviceCharacteristics = cpu_to_le32(0x00000020); - rsp->OutputBufferLength = cpu_to_le32(8); - inc_rfc1001_len(work->response_buf, 8); - fs_infoclass_size = FS_DEVICE_INFORMATION_SIZE; - break; - } - case FS_ATTRIBUTE_INFORMATION: - { - struct filesystem_attribute_info *info; - size_t sz; - - info = (struct filesystem_attribute_info *)rsp->Buffer; - info->Attributes = cpu_to_le32(FILE_SUPPORTS_OBJECT_IDS | - FILE_PERSISTENT_ACLS | - FILE_UNICODE_ON_DISK | - FILE_CASE_PRESERVED_NAMES | - FILE_CASE_SENSITIVE_SEARCH | - FILE_SUPPORTS_BLOCK_REFCOUNTING); - - info->Attributes |= cpu_to_le32(server_conf.share_fake_fscaps); - - if (test_share_config_flag(work->tcon->share_conf, - KSMBD_SHARE_FLAG_STREAMS)) - info->Attributes |= cpu_to_le32(FILE_NAMED_STREAMS); - - info->MaxPathNameComponentLength = cpu_to_le32(stfs.f_namelen); - len = smbConvertToUTF16((__le16 *)info->FileSystemName, - "NTFS", PATH_MAX, conn->local_nls, 0); - len = len * 2; - info->FileSystemNameLen = cpu_to_le32(len); - sz = sizeof(struct filesystem_attribute_info) - 2 + len; - rsp->OutputBufferLength = cpu_to_le32(sz); - inc_rfc1001_len(work->response_buf, sz); - fs_infoclass_size = FS_ATTRIBUTE_INFORMATION_SIZE; - break; - } - case FS_VOLUME_INFORMATION: - { - struct filesystem_vol_info *info; - size_t sz; - unsigned int serial_crc = 0; - - info = (struct filesystem_vol_info *)(rsp->Buffer); - info->VolumeCreationTime = 0; - serial_crc = crc32_le(serial_crc, share->name, - strlen(share->name)); - serial_crc = crc32_le(serial_crc, share->path, - strlen(share->path)); - serial_crc = crc32_le(serial_crc, ksmbd_netbios_name(), - strlen(ksmbd_netbios_name())); - /* Taking dummy value of serial number*/ - info->SerialNumber = cpu_to_le32(serial_crc); - len = smbConvertToUTF16((__le16 *)info->VolumeLabel, - share->name, PATH_MAX, - conn->local_nls, 0); - len = len * 2; - info->VolumeLabelSize = cpu_to_le32(len); - info->Reserved = 0; - sz = sizeof(struct filesystem_vol_info) - 2 + len; - rsp->OutputBufferLength = cpu_to_le32(sz); - inc_rfc1001_len(work->response_buf, sz); - fs_infoclass_size = FS_VOLUME_INFORMATION_SIZE; - break; - } - case FS_SIZE_INFORMATION: - { - struct filesystem_info *info; - - info = (struct filesystem_info *)(rsp->Buffer); - info->TotalAllocationUnits = cpu_to_le64(stfs.f_blocks); - info->FreeAllocationUnits = cpu_to_le64(stfs.f_bfree); - info->SectorsPerAllocationUnit = cpu_to_le32(1); - info->BytesPerSector = cpu_to_le32(stfs.f_bsize); - rsp->OutputBufferLength = cpu_to_le32(24); - inc_rfc1001_len(work->response_buf, 24); - fs_infoclass_size = FS_SIZE_INFORMATION_SIZE; - break; - } - case FS_FULL_SIZE_INFORMATION: - { - struct smb2_fs_full_size_info *info; - - info = (struct smb2_fs_full_size_info *)(rsp->Buffer); - info->TotalAllocationUnits = cpu_to_le64(stfs.f_blocks); - info->CallerAvailableAllocationUnits = - cpu_to_le64(stfs.f_bavail); - info->ActualAvailableAllocationUnits = - cpu_to_le64(stfs.f_bfree); - info->SectorsPerAllocationUnit = cpu_to_le32(1); - info->BytesPerSector = cpu_to_le32(stfs.f_bsize); - rsp->OutputBufferLength = cpu_to_le32(32); - inc_rfc1001_len(work->response_buf, 32); - fs_infoclass_size = FS_FULL_SIZE_INFORMATION_SIZE; - break; - } - case FS_OBJECT_ID_INFORMATION: - { - struct object_id_info *info; - - info = (struct object_id_info *)(rsp->Buffer); - - if (!user_guest(sess->user)) - memcpy(info->objid, user_passkey(sess->user), 16); - else - memset(info->objid, 0, 16); - - info->extended_info.magic = cpu_to_le32(EXTENDED_INFO_MAGIC); - info->extended_info.version = cpu_to_le32(1); - info->extended_info.release = cpu_to_le32(1); - info->extended_info.rel_date = 0; - memcpy(info->extended_info.version_string, "1.1.0", strlen("1.1.0")); - rsp->OutputBufferLength = cpu_to_le32(64); - inc_rfc1001_len(work->response_buf, 64); - fs_infoclass_size = FS_OBJECT_ID_INFORMATION_SIZE; - break; - } - case FS_SECTOR_SIZE_INFORMATION: - { - struct smb3_fs_ss_info *info; - unsigned int sector_size = - min_t(unsigned int, path.mnt->mnt_sb->s_blocksize, 4096); - - info = (struct smb3_fs_ss_info *)(rsp->Buffer); - - info->LogicalBytesPerSector = cpu_to_le32(sector_size); - info->PhysicalBytesPerSectorForAtomicity = - cpu_to_le32(sector_size); - info->PhysicalBytesPerSectorForPerf = cpu_to_le32(sector_size); - info->FSEffPhysicalBytesPerSectorForAtomicity = - cpu_to_le32(sector_size); - info->Flags = cpu_to_le32(SSINFO_FLAGS_ALIGNED_DEVICE | - SSINFO_FLAGS_PARTITION_ALIGNED_ON_DEVICE); - info->ByteOffsetForSectorAlignment = 0; - info->ByteOffsetForPartitionAlignment = 0; - rsp->OutputBufferLength = cpu_to_le32(28); - inc_rfc1001_len(work->response_buf, 28); - fs_infoclass_size = FS_SECTOR_SIZE_INFORMATION_SIZE; - break; - } - case FS_CONTROL_INFORMATION: - { - /* - * TODO : The current implementation is based on - * test result with win7(NTFS) server. It's need to - * modify this to get valid Quota values - * from Linux kernel - */ - struct smb2_fs_control_info *info; - - info = (struct smb2_fs_control_info *)(rsp->Buffer); - info->FreeSpaceStartFiltering = 0; - info->FreeSpaceThreshold = 0; - info->FreeSpaceStopFiltering = 0; - info->DefaultQuotaThreshold = cpu_to_le64(SMB2_NO_FID); - info->DefaultQuotaLimit = cpu_to_le64(SMB2_NO_FID); - info->Padding = 0; - rsp->OutputBufferLength = cpu_to_le32(48); - inc_rfc1001_len(work->response_buf, 48); - fs_infoclass_size = FS_CONTROL_INFORMATION_SIZE; - break; - } - case FS_POSIX_INFORMATION: - { - struct filesystem_posix_info *info; - - if (!work->tcon->posix_extensions) { - pr_err("client doesn't negotiate with SMB3.1.1 POSIX Extensions\n"); - rc = -EOPNOTSUPP; - } else { - info = (struct filesystem_posix_info *)(rsp->Buffer); - info->OptimalTransferSize = cpu_to_le32(stfs.f_bsize); - info->BlockSize = cpu_to_le32(stfs.f_bsize); - info->TotalBlocks = cpu_to_le64(stfs.f_blocks); - info->BlocksAvail = cpu_to_le64(stfs.f_bfree); - info->UserBlocksAvail = cpu_to_le64(stfs.f_bavail); - info->TotalFileNodes = cpu_to_le64(stfs.f_files); - info->FreeFileNodes = cpu_to_le64(stfs.f_ffree); - rsp->OutputBufferLength = cpu_to_le32(56); - inc_rfc1001_len(work->response_buf, 56); - fs_infoclass_size = FS_POSIX_INFORMATION_SIZE; - } - break; - } - default: - path_put(&path); - return -EOPNOTSUPP; - } - rc = buffer_check_err(le32_to_cpu(req->OutputBufferLength), - rsp, work->response_buf, - fs_infoclass_size); - path_put(&path); - return rc; -} - -static int smb2_get_info_sec(struct ksmbd_work *work, - struct smb2_query_info_req *req, - struct smb2_query_info_rsp *rsp) -{ - struct ksmbd_file *fp; - struct user_namespace *user_ns; - struct smb_ntsd *pntsd = (struct smb_ntsd *)rsp->Buffer, *ppntsd = NULL; - struct smb_fattr fattr = {{0}}; - struct inode *inode; - __u32 secdesclen = 0; - unsigned int id = KSMBD_NO_FID, pid = KSMBD_NO_FID; - int addition_info = le32_to_cpu(req->AdditionalInformation); - int rc = 0, ppntsd_size = 0; - - if (addition_info & ~(OWNER_SECINFO | GROUP_SECINFO | DACL_SECINFO | - PROTECTED_DACL_SECINFO | - UNPROTECTED_DACL_SECINFO)) { - ksmbd_debug(SMB, "Unsupported addition info: 0x%x)\n", - addition_info); - - pntsd->revision = cpu_to_le16(1); - pntsd->type = cpu_to_le16(SELF_RELATIVE | DACL_PROTECTED); - pntsd->osidoffset = 0; - pntsd->gsidoffset = 0; - pntsd->sacloffset = 0; - pntsd->dacloffset = 0; - - secdesclen = sizeof(struct smb_ntsd); - rsp->OutputBufferLength = cpu_to_le32(secdesclen); - inc_rfc1001_len(work->response_buf, secdesclen); - - return 0; - } - - if (work->next_smb2_rcv_hdr_off) { - if (!has_file_id(req->VolatileFileId)) { - ksmbd_debug(SMB, "Compound request set FID = %llu\n", - work->compound_fid); - id = work->compound_fid; - pid = work->compound_pfid; - } - } - - if (!has_file_id(id)) { - id = req->VolatileFileId; - pid = req->PersistentFileId; - } - - fp = ksmbd_lookup_fd_slow(work, id, pid); - if (!fp) - return -ENOENT; - - user_ns = file_mnt_user_ns(fp->filp); - inode = file_inode(fp->filp); - ksmbd_acls_fattr(&fattr, user_ns, inode); - - if (test_share_config_flag(work->tcon->share_conf, - KSMBD_SHARE_FLAG_ACL_XATTR)) - ppntsd_size = ksmbd_vfs_get_sd_xattr(work->conn, user_ns, - fp->filp->f_path.dentry, - &ppntsd); - - /* Check if sd buffer size exceeds response buffer size */ - if (smb2_resp_buf_len(work, 8) > ppntsd_size) - rc = build_sec_desc(user_ns, pntsd, ppntsd, ppntsd_size, - addition_info, &secdesclen, &fattr); - posix_acl_release(fattr.cf_acls); - posix_acl_release(fattr.cf_dacls); - kfree(ppntsd); - ksmbd_fd_put(work, fp); - if (rc) - return rc; - - rsp->OutputBufferLength = cpu_to_le32(secdesclen); - inc_rfc1001_len(work->response_buf, secdesclen); - return 0; -} - -/** - * smb2_query_info() - handler for smb2 query info command - * @work: smb work containing query info request buffer - * - * Return: 0 on success, otherwise error - */ -int smb2_query_info(struct ksmbd_work *work) -{ - struct smb2_query_info_req *req; - struct smb2_query_info_rsp *rsp; - int rc = 0; - - WORK_BUFFERS(work, req, rsp); - - ksmbd_debug(SMB, "GOT query info request\n"); - - switch (req->InfoType) { - case SMB2_O_INFO_FILE: - ksmbd_debug(SMB, "GOT SMB2_O_INFO_FILE\n"); - rc = smb2_get_info_file(work, req, rsp); - break; - case SMB2_O_INFO_FILESYSTEM: - ksmbd_debug(SMB, "GOT SMB2_O_INFO_FILESYSTEM\n"); - rc = smb2_get_info_filesystem(work, req, rsp); - break; - case SMB2_O_INFO_SECURITY: - ksmbd_debug(SMB, "GOT SMB2_O_INFO_SECURITY\n"); - rc = smb2_get_info_sec(work, req, rsp); - break; - default: - ksmbd_debug(SMB, "InfoType %d not supported yet\n", - req->InfoType); - rc = -EOPNOTSUPP; - } - - if (rc < 0) { - if (rc == -EACCES) - rsp->hdr.Status = STATUS_ACCESS_DENIED; - else if (rc == -ENOENT) - rsp->hdr.Status = STATUS_FILE_CLOSED; - else if (rc == -EIO) - rsp->hdr.Status = STATUS_UNEXPECTED_IO_ERROR; - else if (rc == -EOPNOTSUPP || rsp->hdr.Status == 0) - rsp->hdr.Status = STATUS_INVALID_INFO_CLASS; - smb2_set_err_rsp(work); - - ksmbd_debug(SMB, "error while processing smb2 query rc = %d\n", - rc); - return rc; - } - rsp->StructureSize = cpu_to_le16(9); - rsp->OutputBufferOffset = cpu_to_le16(72); - inc_rfc1001_len(work->response_buf, 8); - return 0; -} - -/** - * smb2_close_pipe() - handler for closing IPC pipe - * @work: smb work containing close request buffer - * - * Return: 0 - */ -static noinline int smb2_close_pipe(struct ksmbd_work *work) -{ - u64 id; - struct smb2_close_req *req = smb2_get_msg(work->request_buf); - struct smb2_close_rsp *rsp = smb2_get_msg(work->response_buf); - - id = req->VolatileFileId; - ksmbd_session_rpc_close(work->sess, id); - - rsp->StructureSize = cpu_to_le16(60); - rsp->Flags = 0; - rsp->Reserved = 0; - rsp->CreationTime = 0; - rsp->LastAccessTime = 0; - rsp->LastWriteTime = 0; - rsp->ChangeTime = 0; - rsp->AllocationSize = 0; - rsp->EndOfFile = 0; - rsp->Attributes = 0; - inc_rfc1001_len(work->response_buf, 60); - return 0; -} - -/** - * smb2_close() - handler for smb2 close file command - * @work: smb work containing close request buffer - * - * Return: 0 - */ -int smb2_close(struct ksmbd_work *work) -{ - u64 volatile_id = KSMBD_NO_FID; - u64 sess_id; - struct smb2_close_req *req; - struct smb2_close_rsp *rsp; - struct ksmbd_conn *conn = work->conn; - struct ksmbd_file *fp; - struct inode *inode; - u64 time; - int err = 0; - - WORK_BUFFERS(work, req, rsp); - - if (test_share_config_flag(work->tcon->share_conf, - KSMBD_SHARE_FLAG_PIPE)) { - ksmbd_debug(SMB, "IPC pipe close request\n"); - return smb2_close_pipe(work); - } - - sess_id = le64_to_cpu(req->hdr.SessionId); - if (req->hdr.Flags & SMB2_FLAGS_RELATED_OPERATIONS) - sess_id = work->compound_sid; - - work->compound_sid = 0; - if (check_session_id(conn, sess_id)) { - work->compound_sid = sess_id; - } else { - rsp->hdr.Status = STATUS_USER_SESSION_DELETED; - if (req->hdr.Flags & SMB2_FLAGS_RELATED_OPERATIONS) - rsp->hdr.Status = STATUS_INVALID_PARAMETER; - err = -EBADF; - goto out; - } - - if (work->next_smb2_rcv_hdr_off && - !has_file_id(req->VolatileFileId)) { - if (!has_file_id(work->compound_fid)) { - /* file already closed, return FILE_CLOSED */ - ksmbd_debug(SMB, "file already closed\n"); - rsp->hdr.Status = STATUS_FILE_CLOSED; - err = -EBADF; - goto out; - } else { - ksmbd_debug(SMB, - "Compound request set FID = %llu:%llu\n", - work->compound_fid, - work->compound_pfid); - volatile_id = work->compound_fid; - - /* file closed, stored id is not valid anymore */ - work->compound_fid = KSMBD_NO_FID; - work->compound_pfid = KSMBD_NO_FID; - } - } else { - volatile_id = req->VolatileFileId; - } - ksmbd_debug(SMB, "volatile_id = %llu\n", volatile_id); - - rsp->StructureSize = cpu_to_le16(60); - rsp->Reserved = 0; - - if (req->Flags == SMB2_CLOSE_FLAG_POSTQUERY_ATTRIB) { - fp = ksmbd_lookup_fd_fast(work, volatile_id); - if (!fp) { - err = -ENOENT; - goto out; - } - - inode = file_inode(fp->filp); - rsp->Flags = SMB2_CLOSE_FLAG_POSTQUERY_ATTRIB; - rsp->AllocationSize = S_ISDIR(inode->i_mode) ? 0 : - cpu_to_le64(inode->i_blocks << 9); - rsp->EndOfFile = cpu_to_le64(inode->i_size); - rsp->Attributes = fp->f_ci->m_fattr; - rsp->CreationTime = cpu_to_le64(fp->create_time); - time = ksmbd_UnixTimeToNT(inode->i_atime); - rsp->LastAccessTime = cpu_to_le64(time); - time = ksmbd_UnixTimeToNT(inode->i_mtime); - rsp->LastWriteTime = cpu_to_le64(time); - time = ksmbd_UnixTimeToNT(inode->i_ctime); - rsp->ChangeTime = cpu_to_le64(time); - ksmbd_fd_put(work, fp); - } else { - rsp->Flags = 0; - rsp->AllocationSize = 0; - rsp->EndOfFile = 0; - rsp->Attributes = 0; - rsp->CreationTime = 0; - rsp->LastAccessTime = 0; - rsp->LastWriteTime = 0; - rsp->ChangeTime = 0; - } - - err = ksmbd_close_fd(work, volatile_id); -out: - if (err) { - if (rsp->hdr.Status == 0) - rsp->hdr.Status = STATUS_FILE_CLOSED; - smb2_set_err_rsp(work); - } else { - inc_rfc1001_len(work->response_buf, 60); - } - - return 0; -} - -/** - * smb2_echo() - handler for smb2 echo(ping) command - * @work: smb work containing echo request buffer - * - * Return: 0 - */ -int smb2_echo(struct ksmbd_work *work) -{ - struct smb2_echo_rsp *rsp = smb2_get_msg(work->response_buf); - - rsp->StructureSize = cpu_to_le16(4); - rsp->Reserved = 0; - inc_rfc1001_len(work->response_buf, 4); - return 0; -} - -static int smb2_rename(struct ksmbd_work *work, - struct ksmbd_file *fp, - struct user_namespace *user_ns, - struct smb2_file_rename_info *file_info, - struct nls_table *local_nls) -{ - struct ksmbd_share_config *share = fp->tcon->share_conf; - char *new_name = NULL, *abs_oldname = NULL, *old_name = NULL; - char *pathname = NULL; - struct path path; - bool file_present = true; - int rc; - - ksmbd_debug(SMB, "setting FILE_RENAME_INFO\n"); - pathname = kmalloc(PATH_MAX, GFP_KERNEL); - if (!pathname) - return -ENOMEM; - - abs_oldname = file_path(fp->filp, pathname, PATH_MAX); - if (IS_ERR(abs_oldname)) { - rc = -EINVAL; - goto out; - } - old_name = strrchr(abs_oldname, '/'); - if (old_name && old_name[1] != '\0') { - old_name++; - } else { - ksmbd_debug(SMB, "can't get last component in path %s\n", - abs_oldname); - rc = -ENOENT; - goto out; - } - - new_name = smb2_get_name(file_info->FileName, - le32_to_cpu(file_info->FileNameLength), - local_nls); - if (IS_ERR(new_name)) { - rc = PTR_ERR(new_name); - goto out; - } - - if (strchr(new_name, ':')) { - int s_type; - char *xattr_stream_name, *stream_name = NULL; - size_t xattr_stream_size; - int len; - - rc = parse_stream_name(new_name, &stream_name, &s_type); - if (rc < 0) - goto out; - - len = strlen(new_name); - if (len > 0 && new_name[len - 1] != '/') { - pr_err("not allow base filename in rename\n"); - rc = -ESHARE; - goto out; - } - - rc = ksmbd_vfs_xattr_stream_name(stream_name, - &xattr_stream_name, - &xattr_stream_size, - s_type); - if (rc) - goto out; - - rc = ksmbd_vfs_setxattr(user_ns, - fp->filp->f_path.dentry, - xattr_stream_name, - NULL, 0, 0); - if (rc < 0) { - pr_err("failed to store stream name in xattr: %d\n", - rc); - rc = -EINVAL; - goto out; - } - - goto out; - } - - ksmbd_debug(SMB, "new name %s\n", new_name); - rc = ksmbd_vfs_kern_path(work, new_name, LOOKUP_NO_SYMLINKS, &path, 1); - if (rc) { - if (rc != -ENOENT) - goto out; - file_present = false; - } else { - path_put(&path); - } - - if (ksmbd_share_veto_filename(share, new_name)) { - rc = -ENOENT; - ksmbd_debug(SMB, "Can't rename vetoed file: %s\n", new_name); - goto out; - } - - if (file_info->ReplaceIfExists) { - if (file_present) { - rc = ksmbd_vfs_remove_file(work, new_name); - if (rc) { - if (rc != -ENOTEMPTY) - rc = -EINVAL; - ksmbd_debug(SMB, "cannot delete %s, rc %d\n", - new_name, rc); - goto out; - } - } - } else { - if (file_present && - strncmp(old_name, path.dentry->d_name.name, strlen(old_name))) { - rc = -EEXIST; - ksmbd_debug(SMB, - "cannot rename already existing file\n"); - goto out; - } - } - - rc = ksmbd_vfs_fp_rename(work, fp, new_name); -out: - kfree(pathname); - if (!IS_ERR(new_name)) - kfree(new_name); - return rc; -} - -static int smb2_create_link(struct ksmbd_work *work, - struct ksmbd_share_config *share, - struct smb2_file_link_info *file_info, - unsigned int buf_len, struct file *filp, - struct nls_table *local_nls) -{ - char *link_name = NULL, *target_name = NULL, *pathname = NULL; - struct path path; - bool file_present = true; - int rc; - - if (buf_len < (u64)sizeof(struct smb2_file_link_info) + - le32_to_cpu(file_info->FileNameLength)) - return -EINVAL; - - ksmbd_debug(SMB, "setting FILE_LINK_INFORMATION\n"); - pathname = kmalloc(PATH_MAX, GFP_KERNEL); - if (!pathname) - return -ENOMEM; - - link_name = smb2_get_name(file_info->FileName, - le32_to_cpu(file_info->FileNameLength), - local_nls); - if (IS_ERR(link_name) || S_ISDIR(file_inode(filp)->i_mode)) { - rc = -EINVAL; - goto out; - } - - ksmbd_debug(SMB, "link name is %s\n", link_name); - target_name = file_path(filp, pathname, PATH_MAX); - if (IS_ERR(target_name)) { - rc = -EINVAL; - goto out; - } - - ksmbd_debug(SMB, "target name is %s\n", target_name); - rc = ksmbd_vfs_kern_path(work, link_name, LOOKUP_NO_SYMLINKS, &path, 0); - if (rc) { - if (rc != -ENOENT) - goto out; - file_present = false; - } else { - path_put(&path); - } - - if (file_info->ReplaceIfExists) { - if (file_present) { - rc = ksmbd_vfs_remove_file(work, link_name); - if (rc) { - rc = -EINVAL; - ksmbd_debug(SMB, "cannot delete %s\n", - link_name); - goto out; - } - } - } else { - if (file_present) { - rc = -EEXIST; - ksmbd_debug(SMB, "link already exists\n"); - goto out; - } - } - - rc = ksmbd_vfs_link(work, target_name, link_name); - if (rc) - rc = -EINVAL; -out: - if (!IS_ERR(link_name)) - kfree(link_name); - kfree(pathname); - return rc; -} - -static int set_file_basic_info(struct ksmbd_file *fp, - struct smb2_file_basic_info *file_info, - struct ksmbd_share_config *share) -{ - struct iattr attrs; - struct file *filp; - struct inode *inode; - struct user_namespace *user_ns; - int rc = 0; - - if (!(fp->daccess & FILE_WRITE_ATTRIBUTES_LE)) - return -EACCES; - - attrs.ia_valid = 0; - filp = fp->filp; - inode = file_inode(filp); - user_ns = file_mnt_user_ns(filp); - - if (file_info->CreationTime) - fp->create_time = le64_to_cpu(file_info->CreationTime); - - if (file_info->LastAccessTime) { - attrs.ia_atime = ksmbd_NTtimeToUnix(file_info->LastAccessTime); - attrs.ia_valid |= (ATTR_ATIME | ATTR_ATIME_SET); - } - - attrs.ia_valid |= ATTR_CTIME; - if (file_info->ChangeTime) - attrs.ia_ctime = ksmbd_NTtimeToUnix(file_info->ChangeTime); - else - attrs.ia_ctime = inode->i_ctime; - - if (file_info->LastWriteTime) { - attrs.ia_mtime = ksmbd_NTtimeToUnix(file_info->LastWriteTime); - attrs.ia_valid |= (ATTR_MTIME | ATTR_MTIME_SET); - } - - if (file_info->Attributes) { - if (!S_ISDIR(inode->i_mode) && - file_info->Attributes & FILE_ATTRIBUTE_DIRECTORY_LE) { - pr_err("can't change a file to a directory\n"); - return -EINVAL; - } - - if (!(S_ISDIR(inode->i_mode) && file_info->Attributes == FILE_ATTRIBUTE_NORMAL_LE)) - fp->f_ci->m_fattr = file_info->Attributes | - (fp->f_ci->m_fattr & FILE_ATTRIBUTE_DIRECTORY_LE); - } - - if (test_share_config_flag(share, KSMBD_SHARE_FLAG_STORE_DOS_ATTRS) && - (file_info->CreationTime || file_info->Attributes)) { - struct xattr_dos_attrib da = {0}; - - da.version = 4; - da.itime = fp->itime; - da.create_time = fp->create_time; - da.attr = le32_to_cpu(fp->f_ci->m_fattr); - da.flags = XATTR_DOSINFO_ATTRIB | XATTR_DOSINFO_CREATE_TIME | - XATTR_DOSINFO_ITIME; - - rc = ksmbd_vfs_set_dos_attrib_xattr(user_ns, - filp->f_path.dentry, &da); - if (rc) - ksmbd_debug(SMB, - "failed to restore file attribute in EA\n"); - rc = 0; - } - - if (attrs.ia_valid) { - struct dentry *dentry = filp->f_path.dentry; - struct inode *inode = d_inode(dentry); - - if (IS_IMMUTABLE(inode) || IS_APPEND(inode)) - return -EACCES; - - inode_lock(inode); - inode->i_ctime = attrs.ia_ctime; - attrs.ia_valid &= ~ATTR_CTIME; - rc = notify_change(user_ns, dentry, &attrs, NULL); - inode_unlock(inode); - } - return rc; -} - -static int set_file_allocation_info(struct ksmbd_work *work, - struct ksmbd_file *fp, - struct smb2_file_alloc_info *file_alloc_info) -{ - /* - * TODO : It's working fine only when store dos attributes - * is not yes. need to implement a logic which works - * properly with any smb.conf option - */ - - loff_t alloc_blks; - struct inode *inode; - int rc; - - if (!(fp->daccess & FILE_WRITE_DATA_LE)) - return -EACCES; - - alloc_blks = (le64_to_cpu(file_alloc_info->AllocationSize) + 511) >> 9; - inode = file_inode(fp->filp); - - if (alloc_blks > inode->i_blocks) { - smb_break_all_levII_oplock(work, fp, 1); - rc = vfs_fallocate(fp->filp, FALLOC_FL_KEEP_SIZE, 0, - alloc_blks * 512); - if (rc && rc != -EOPNOTSUPP) { - pr_err("vfs_fallocate is failed : %d\n", rc); - return rc; - } - } else if (alloc_blks < inode->i_blocks) { - loff_t size; - - /* - * Allocation size could be smaller than original one - * which means allocated blocks in file should be - * deallocated. use truncate to cut out it, but inode - * size is also updated with truncate offset. - * inode size is retained by backup inode size. - */ - size = i_size_read(inode); - rc = ksmbd_vfs_truncate(work, fp, alloc_blks * 512); - if (rc) { - pr_err("truncate failed!, err %d\n", rc); - return rc; - } - if (size < alloc_blks * 512) - i_size_write(inode, size); - } - return 0; -} - -static int set_end_of_file_info(struct ksmbd_work *work, struct ksmbd_file *fp, - struct smb2_file_eof_info *file_eof_info) -{ - loff_t newsize; - struct inode *inode; - int rc; - - if (!(fp->daccess & FILE_WRITE_DATA_LE)) - return -EACCES; - - newsize = le64_to_cpu(file_eof_info->EndOfFile); - inode = file_inode(fp->filp); - - /* - * If FILE_END_OF_FILE_INFORMATION of set_info_file is called - * on FAT32 shared device, truncate execution time is too long - * and network error could cause from windows client. because - * truncate of some filesystem like FAT32 fill zero data in - * truncated range. - */ - if (inode->i_sb->s_magic != MSDOS_SUPER_MAGIC) { - ksmbd_debug(SMB, "truncated to newsize %lld\n", newsize); - rc = ksmbd_vfs_truncate(work, fp, newsize); - if (rc) { - ksmbd_debug(SMB, "truncate failed!, err %d\n", rc); - if (rc != -EAGAIN) - rc = -EBADF; - return rc; - } - } - return 0; -} - -static int set_rename_info(struct ksmbd_work *work, struct ksmbd_file *fp, - struct smb2_file_rename_info *rename_info, - unsigned int buf_len) -{ - struct user_namespace *user_ns; - struct ksmbd_file *parent_fp; - struct dentry *parent; - struct dentry *dentry = fp->filp->f_path.dentry; - int ret; - - if (!(fp->daccess & FILE_DELETE_LE)) { - pr_err("no right to delete : 0x%x\n", fp->daccess); - return -EACCES; - } - - if (buf_len < (u64)sizeof(struct smb2_file_rename_info) + - le32_to_cpu(rename_info->FileNameLength)) - return -EINVAL; - - user_ns = file_mnt_user_ns(fp->filp); - if (ksmbd_stream_fd(fp)) - goto next; - - parent = dget_parent(dentry); - ret = ksmbd_vfs_lock_parent(user_ns, parent, dentry); - if (ret) { - dput(parent); - return ret; - } - - parent_fp = ksmbd_lookup_fd_inode(d_inode(parent)); - inode_unlock(d_inode(parent)); - dput(parent); - - if (parent_fp) { - if (parent_fp->daccess & FILE_DELETE_LE) { - pr_err("parent dir is opened with delete access\n"); - ksmbd_fd_put(work, parent_fp); - return -ESHARE; - } - ksmbd_fd_put(work, parent_fp); - } -next: - return smb2_rename(work, fp, user_ns, rename_info, - work->conn->local_nls); -} - -static int set_file_disposition_info(struct ksmbd_file *fp, - struct smb2_file_disposition_info *file_info) -{ - struct inode *inode; - - if (!(fp->daccess & FILE_DELETE_LE)) { - pr_err("no right to delete : 0x%x\n", fp->daccess); - return -EACCES; - } - - inode = file_inode(fp->filp); - if (file_info->DeletePending) { - if (S_ISDIR(inode->i_mode) && - ksmbd_vfs_empty_dir(fp) == -ENOTEMPTY) - return -EBUSY; - ksmbd_set_inode_pending_delete(fp); - } else { - ksmbd_clear_inode_pending_delete(fp); - } - return 0; -} - -static int set_file_position_info(struct ksmbd_file *fp, - struct smb2_file_pos_info *file_info) -{ - loff_t current_byte_offset; - unsigned long sector_size; - struct inode *inode; - - inode = file_inode(fp->filp); - current_byte_offset = le64_to_cpu(file_info->CurrentByteOffset); - sector_size = inode->i_sb->s_blocksize; - - if (current_byte_offset < 0 || - (fp->coption == FILE_NO_INTERMEDIATE_BUFFERING_LE && - current_byte_offset & (sector_size - 1))) { - pr_err("CurrentByteOffset is not valid : %llu\n", - current_byte_offset); - return -EINVAL; - } - - fp->filp->f_pos = current_byte_offset; - return 0; -} - -static int set_file_mode_info(struct ksmbd_file *fp, - struct smb2_file_mode_info *file_info) -{ - __le32 mode; - - mode = file_info->Mode; - - if ((mode & ~FILE_MODE_INFO_MASK)) { - pr_err("Mode is not valid : 0x%x\n", le32_to_cpu(mode)); - return -EINVAL; - } - - /* - * TODO : need to implement consideration for - * FILE_SYNCHRONOUS_IO_ALERT and FILE_SYNCHRONOUS_IO_NONALERT - */ - ksmbd_vfs_set_fadvise(fp->filp, mode); - fp->coption = mode; - return 0; -} - -/** - * smb2_set_info_file() - handler for smb2 set info command - * @work: smb work containing set info command buffer - * @fp: ksmbd_file pointer - * @req: request buffer pointer - * @share: ksmbd_share_config pointer - * - * Return: 0 on success, otherwise error - * TODO: need to implement an error handling for STATUS_INFO_LENGTH_MISMATCH - */ -static int smb2_set_info_file(struct ksmbd_work *work, struct ksmbd_file *fp, - struct smb2_set_info_req *req, - struct ksmbd_share_config *share) -{ - unsigned int buf_len = le32_to_cpu(req->BufferLength); - - switch (req->FileInfoClass) { - case FILE_BASIC_INFORMATION: - { - if (buf_len < sizeof(struct smb2_file_basic_info)) - return -EINVAL; - - return set_file_basic_info(fp, (struct smb2_file_basic_info *)req->Buffer, share); - } - case FILE_ALLOCATION_INFORMATION: - { - if (buf_len < sizeof(struct smb2_file_alloc_info)) - return -EINVAL; - - return set_file_allocation_info(work, fp, - (struct smb2_file_alloc_info *)req->Buffer); - } - case FILE_END_OF_FILE_INFORMATION: - { - if (buf_len < sizeof(struct smb2_file_eof_info)) - return -EINVAL; - - return set_end_of_file_info(work, fp, - (struct smb2_file_eof_info *)req->Buffer); - } - case FILE_RENAME_INFORMATION: - { - if (!test_tree_conn_flag(work->tcon, KSMBD_TREE_CONN_FLAG_WRITABLE)) { - ksmbd_debug(SMB, - "User does not have write permission\n"); - return -EACCES; - } - - if (buf_len < sizeof(struct smb2_file_rename_info)) - return -EINVAL; - - return set_rename_info(work, fp, - (struct smb2_file_rename_info *)req->Buffer, - buf_len); - } - case FILE_LINK_INFORMATION: - { - if (buf_len < sizeof(struct smb2_file_link_info)) - return -EINVAL; - - return smb2_create_link(work, work->tcon->share_conf, - (struct smb2_file_link_info *)req->Buffer, - buf_len, fp->filp, - work->conn->local_nls); - } - case FILE_DISPOSITION_INFORMATION: - { - if (!test_tree_conn_flag(work->tcon, KSMBD_TREE_CONN_FLAG_WRITABLE)) { - ksmbd_debug(SMB, - "User does not have write permission\n"); - return -EACCES; - } - - if (buf_len < sizeof(struct smb2_file_disposition_info)) - return -EINVAL; - - return set_file_disposition_info(fp, - (struct smb2_file_disposition_info *)req->Buffer); - } - case FILE_FULL_EA_INFORMATION: - { - if (!(fp->daccess & FILE_WRITE_EA_LE)) { - pr_err("Not permitted to write ext attr: 0x%x\n", - fp->daccess); - return -EACCES; - } - - if (buf_len < sizeof(struct smb2_ea_info)) - return -EINVAL; - - return smb2_set_ea((struct smb2_ea_info *)req->Buffer, - buf_len, &fp->filp->f_path); - } - case FILE_POSITION_INFORMATION: - { - if (buf_len < sizeof(struct smb2_file_pos_info)) - return -EINVAL; - - return set_file_position_info(fp, (struct smb2_file_pos_info *)req->Buffer); - } - case FILE_MODE_INFORMATION: - { - if (buf_len < sizeof(struct smb2_file_mode_info)) - return -EINVAL; - - return set_file_mode_info(fp, (struct smb2_file_mode_info *)req->Buffer); - } - } - - pr_err("Unimplemented Fileinfoclass :%d\n", req->FileInfoClass); - return -EOPNOTSUPP; -} - -static int smb2_set_info_sec(struct ksmbd_file *fp, int addition_info, - char *buffer, int buf_len) -{ - struct smb_ntsd *pntsd = (struct smb_ntsd *)buffer; - - fp->saccess |= FILE_SHARE_DELETE_LE; - - return set_info_sec(fp->conn, fp->tcon, &fp->filp->f_path, pntsd, - buf_len, false); -} - -/** - * smb2_set_info() - handler for smb2 set info command handler - * @work: smb work containing set info request buffer - * - * Return: 0 on success, otherwise error - */ -int smb2_set_info(struct ksmbd_work *work) -{ - struct smb2_set_info_req *req; - struct smb2_set_info_rsp *rsp; - struct ksmbd_file *fp; - int rc = 0; - unsigned int id = KSMBD_NO_FID, pid = KSMBD_NO_FID; - - ksmbd_debug(SMB, "Received set info request\n"); - - if (work->next_smb2_rcv_hdr_off) { - req = ksmbd_req_buf_next(work); - rsp = ksmbd_resp_buf_next(work); - if (!has_file_id(req->VolatileFileId)) { - ksmbd_debug(SMB, "Compound request set FID = %llu\n", - work->compound_fid); - id = work->compound_fid; - pid = work->compound_pfid; - } - } else { - req = smb2_get_msg(work->request_buf); - rsp = smb2_get_msg(work->response_buf); - } - - if (!has_file_id(id)) { - id = req->VolatileFileId; - pid = req->PersistentFileId; - } - - fp = ksmbd_lookup_fd_slow(work, id, pid); - if (!fp) { - ksmbd_debug(SMB, "Invalid id for close: %u\n", id); - rc = -ENOENT; - goto err_out; - } - - switch (req->InfoType) { - case SMB2_O_INFO_FILE: - ksmbd_debug(SMB, "GOT SMB2_O_INFO_FILE\n"); - rc = smb2_set_info_file(work, fp, req, work->tcon->share_conf); - break; - case SMB2_O_INFO_SECURITY: - ksmbd_debug(SMB, "GOT SMB2_O_INFO_SECURITY\n"); - if (ksmbd_override_fsids(work)) { - rc = -ENOMEM; - goto err_out; - } - rc = smb2_set_info_sec(fp, - le32_to_cpu(req->AdditionalInformation), - req->Buffer, - le32_to_cpu(req->BufferLength)); - ksmbd_revert_fsids(work); - break; - default: - rc = -EOPNOTSUPP; - } - - if (rc < 0) - goto err_out; - - rsp->StructureSize = cpu_to_le16(2); - inc_rfc1001_len(work->response_buf, 2); - ksmbd_fd_put(work, fp); - return 0; - -err_out: - if (rc == -EACCES || rc == -EPERM || rc == -EXDEV) - rsp->hdr.Status = STATUS_ACCESS_DENIED; - else if (rc == -EINVAL) - rsp->hdr.Status = STATUS_INVALID_PARAMETER; - else if (rc == -ESHARE) - rsp->hdr.Status = STATUS_SHARING_VIOLATION; - else if (rc == -ENOENT) - rsp->hdr.Status = STATUS_OBJECT_NAME_INVALID; - else if (rc == -EBUSY || rc == -ENOTEMPTY) - rsp->hdr.Status = STATUS_DIRECTORY_NOT_EMPTY; - else if (rc == -EAGAIN) - rsp->hdr.Status = STATUS_FILE_LOCK_CONFLICT; - else if (rc == -EBADF || rc == -ESTALE) - rsp->hdr.Status = STATUS_INVALID_HANDLE; - else if (rc == -EEXIST) - rsp->hdr.Status = STATUS_OBJECT_NAME_COLLISION; - else if (rsp->hdr.Status == 0 || rc == -EOPNOTSUPP) - rsp->hdr.Status = STATUS_INVALID_INFO_CLASS; - smb2_set_err_rsp(work); - ksmbd_fd_put(work, fp); - ksmbd_debug(SMB, "error while processing smb2 query rc = %d\n", rc); - return rc; -} - -/** - * smb2_read_pipe() - handler for smb2 read from IPC pipe - * @work: smb work containing read IPC pipe command buffer - * - * Return: 0 on success, otherwise error - */ -static noinline int smb2_read_pipe(struct ksmbd_work *work) -{ - int nbytes = 0, err; - u64 id; - struct ksmbd_rpc_command *rpc_resp; - struct smb2_read_req *req = smb2_get_msg(work->request_buf); - struct smb2_read_rsp *rsp = smb2_get_msg(work->response_buf); - - id = req->VolatileFileId; - - inc_rfc1001_len(work->response_buf, 16); - rpc_resp = ksmbd_rpc_read(work->sess, id); - if (rpc_resp) { - if (rpc_resp->flags != KSMBD_RPC_OK) { - err = -EINVAL; - goto out; - } - - work->aux_payload_buf = - kvmalloc(rpc_resp->payload_sz, GFP_KERNEL | __GFP_ZERO); - if (!work->aux_payload_buf) { - err = -ENOMEM; - goto out; - } - - memcpy(work->aux_payload_buf, rpc_resp->payload, - rpc_resp->payload_sz); - - nbytes = rpc_resp->payload_sz; - work->resp_hdr_sz = get_rfc1002_len(work->response_buf) + 4; - work->aux_payload_sz = nbytes; - kvfree(rpc_resp); - } - - rsp->StructureSize = cpu_to_le16(17); - rsp->DataOffset = 80; - rsp->Reserved = 0; - rsp->DataLength = cpu_to_le32(nbytes); - rsp->DataRemaining = 0; - rsp->Flags = 0; - inc_rfc1001_len(work->response_buf, nbytes); - return 0; - -out: - rsp->hdr.Status = STATUS_UNEXPECTED_IO_ERROR; - smb2_set_err_rsp(work); - kvfree(rpc_resp); - return err; -} - -static int smb2_set_remote_key_for_rdma(struct ksmbd_work *work, - struct smb2_buffer_desc_v1 *desc, - __le32 Channel, - __le16 ChannelInfoLength) -{ - unsigned int i, ch_count; - - if (work->conn->dialect == SMB30_PROT_ID && - Channel != SMB2_CHANNEL_RDMA_V1) - return -EINVAL; - - ch_count = le16_to_cpu(ChannelInfoLength) / sizeof(*desc); - if (ksmbd_debug_types & KSMBD_DEBUG_RDMA) { - for (i = 0; i < ch_count; i++) { - pr_info("RDMA r/w request %#x: token %#x, length %#x\n", - i, - le32_to_cpu(desc[i].token), - le32_to_cpu(desc[i].length)); - } - } - if (!ch_count) - return -EINVAL; - - work->need_invalidate_rkey = - (Channel == SMB2_CHANNEL_RDMA_V1_INVALIDATE); - if (Channel == SMB2_CHANNEL_RDMA_V1_INVALIDATE) - work->remote_key = le32_to_cpu(desc->token); - return 0; -} - -static ssize_t smb2_read_rdma_channel(struct ksmbd_work *work, - struct smb2_read_req *req, void *data_buf, - size_t length) -{ - int err; - - err = ksmbd_conn_rdma_write(work->conn, data_buf, length, - (struct smb2_buffer_desc_v1 *) - ((char *)req + le16_to_cpu(req->ReadChannelInfoOffset)), - le16_to_cpu(req->ReadChannelInfoLength)); - if (err) - return err; - - return length; -} - -/** - * smb2_read() - handler for smb2 read from file - * @work: smb work containing read command buffer - * - * Return: 0 on success, otherwise error - */ -int smb2_read(struct ksmbd_work *work) -{ - struct ksmbd_conn *conn = work->conn; - struct smb2_read_req *req; - struct smb2_read_rsp *rsp; - struct ksmbd_file *fp = NULL; - loff_t offset; - size_t length, mincount; - ssize_t nbytes = 0, remain_bytes = 0; - int err = 0; - bool is_rdma_channel = false; - unsigned int max_read_size = conn->vals->max_read_size; - - WORK_BUFFERS(work, req, rsp); - - if (test_share_config_flag(work->tcon->share_conf, - KSMBD_SHARE_FLAG_PIPE)) { - ksmbd_debug(SMB, "IPC pipe read request\n"); - return smb2_read_pipe(work); - } - - if (req->Channel == SMB2_CHANNEL_RDMA_V1_INVALIDATE || - req->Channel == SMB2_CHANNEL_RDMA_V1) { - is_rdma_channel = true; - max_read_size = get_smbd_max_read_write_size(); - } - - if (is_rdma_channel == true) { - unsigned int ch_offset = le16_to_cpu(req->ReadChannelInfoOffset); - - if (ch_offset < offsetof(struct smb2_read_req, Buffer)) { - err = -EINVAL; - goto out; - } - err = smb2_set_remote_key_for_rdma(work, - (struct smb2_buffer_desc_v1 *) - ((char *)req + ch_offset), - req->Channel, - req->ReadChannelInfoLength); - if (err) - goto out; - } - - fp = ksmbd_lookup_fd_slow(work, req->VolatileFileId, req->PersistentFileId); - if (!fp) { - err = -ENOENT; - goto out; - } - - if (!(fp->daccess & (FILE_READ_DATA_LE | FILE_READ_ATTRIBUTES_LE))) { - pr_err("Not permitted to read : 0x%x\n", fp->daccess); - err = -EACCES; - goto out; - } - - offset = le64_to_cpu(req->Offset); - length = le32_to_cpu(req->Length); - mincount = le32_to_cpu(req->MinimumCount); - - if (length > max_read_size) { - ksmbd_debug(SMB, "limiting read size to max size(%u)\n", - max_read_size); - err = -EINVAL; - goto out; - } - - ksmbd_debug(SMB, "filename %pD, offset %lld, len %zu\n", - fp->filp, offset, length); - - work->aux_payload_buf = kvmalloc(length, GFP_KERNEL | __GFP_ZERO); - if (!work->aux_payload_buf) { - err = -ENOMEM; - goto out; - } - - nbytes = ksmbd_vfs_read(work, fp, length, &offset); - if (nbytes < 0) { - err = nbytes; - goto out; - } - - if ((nbytes == 0 && length != 0) || nbytes < mincount) { - kvfree(work->aux_payload_buf); - work->aux_payload_buf = NULL; - rsp->hdr.Status = STATUS_END_OF_FILE; - smb2_set_err_rsp(work); - ksmbd_fd_put(work, fp); - return 0; - } - - ksmbd_debug(SMB, "nbytes %zu, offset %lld mincount %zu\n", - nbytes, offset, mincount); - - if (is_rdma_channel == true) { - /* write data to the client using rdma channel */ - remain_bytes = smb2_read_rdma_channel(work, req, - work->aux_payload_buf, - nbytes); - kvfree(work->aux_payload_buf); - work->aux_payload_buf = NULL; - - nbytes = 0; - if (remain_bytes < 0) { - err = (int)remain_bytes; - goto out; - } - } - - rsp->StructureSize = cpu_to_le16(17); - rsp->DataOffset = 80; - rsp->Reserved = 0; - rsp->DataLength = cpu_to_le32(nbytes); - rsp->DataRemaining = cpu_to_le32(remain_bytes); - rsp->Flags = 0; - inc_rfc1001_len(work->response_buf, 16); - work->resp_hdr_sz = get_rfc1002_len(work->response_buf) + 4; - work->aux_payload_sz = nbytes; - inc_rfc1001_len(work->response_buf, nbytes); - ksmbd_fd_put(work, fp); - return 0; - -out: - if (err) { - if (err == -EISDIR) - rsp->hdr.Status = STATUS_INVALID_DEVICE_REQUEST; - else if (err == -EAGAIN) - rsp->hdr.Status = STATUS_FILE_LOCK_CONFLICT; - else if (err == -ENOENT) - rsp->hdr.Status = STATUS_FILE_CLOSED; - else if (err == -EACCES) - rsp->hdr.Status = STATUS_ACCESS_DENIED; - else if (err == -ESHARE) - rsp->hdr.Status = STATUS_SHARING_VIOLATION; - else if (err == -EINVAL) - rsp->hdr.Status = STATUS_INVALID_PARAMETER; - else - rsp->hdr.Status = STATUS_INVALID_HANDLE; - - smb2_set_err_rsp(work); - } - ksmbd_fd_put(work, fp); - return err; -} - -/** - * smb2_write_pipe() - handler for smb2 write on IPC pipe - * @work: smb work containing write IPC pipe command buffer - * - * Return: 0 on success, otherwise error - */ -static noinline int smb2_write_pipe(struct ksmbd_work *work) -{ - struct smb2_write_req *req = smb2_get_msg(work->request_buf); - struct smb2_write_rsp *rsp = smb2_get_msg(work->response_buf); - struct ksmbd_rpc_command *rpc_resp; - u64 id = 0; - int err = 0, ret = 0; - char *data_buf; - size_t length; - - length = le32_to_cpu(req->Length); - id = req->VolatileFileId; - - if ((u64)le16_to_cpu(req->DataOffset) + length > - get_rfc1002_len(work->request_buf)) { - pr_err("invalid write data offset %u, smb_len %u\n", - le16_to_cpu(req->DataOffset), - get_rfc1002_len(work->request_buf)); - err = -EINVAL; - goto out; - } - - data_buf = (char *)(((char *)&req->hdr.ProtocolId) + - le16_to_cpu(req->DataOffset)); - - rpc_resp = ksmbd_rpc_write(work->sess, id, data_buf, length); - if (rpc_resp) { - if (rpc_resp->flags == KSMBD_RPC_ENOTIMPLEMENTED) { - rsp->hdr.Status = STATUS_NOT_SUPPORTED; - kvfree(rpc_resp); - smb2_set_err_rsp(work); - return -EOPNOTSUPP; - } - if (rpc_resp->flags != KSMBD_RPC_OK) { - rsp->hdr.Status = STATUS_INVALID_HANDLE; - smb2_set_err_rsp(work); - kvfree(rpc_resp); - return ret; - } - kvfree(rpc_resp); - } - - rsp->StructureSize = cpu_to_le16(17); - rsp->DataOffset = 0; - rsp->Reserved = 0; - rsp->DataLength = cpu_to_le32(length); - rsp->DataRemaining = 0; - rsp->Reserved2 = 0; - inc_rfc1001_len(work->response_buf, 16); - return 0; -out: - if (err) { - rsp->hdr.Status = STATUS_INVALID_HANDLE; - smb2_set_err_rsp(work); - } - - return err; -} - -static ssize_t smb2_write_rdma_channel(struct ksmbd_work *work, - struct smb2_write_req *req, - struct ksmbd_file *fp, - loff_t offset, size_t length, bool sync) -{ - char *data_buf; - int ret; - ssize_t nbytes; - - data_buf = kvmalloc(length, GFP_KERNEL | __GFP_ZERO); - if (!data_buf) - return -ENOMEM; - - ret = ksmbd_conn_rdma_read(work->conn, data_buf, length, - (struct smb2_buffer_desc_v1 *) - ((char *)req + le16_to_cpu(req->WriteChannelInfoOffset)), - le16_to_cpu(req->WriteChannelInfoLength)); - if (ret < 0) { - kvfree(data_buf); - return ret; - } - - ret = ksmbd_vfs_write(work, fp, data_buf, length, &offset, sync, &nbytes); - kvfree(data_buf); - if (ret < 0) - return ret; - - return nbytes; -} - -/** - * smb2_write() - handler for smb2 write from file - * @work: smb work containing write command buffer - * - * Return: 0 on success, otherwise error - */ -int smb2_write(struct ksmbd_work *work) -{ - struct smb2_write_req *req; - struct smb2_write_rsp *rsp; - struct ksmbd_file *fp = NULL; - loff_t offset; - size_t length; - ssize_t nbytes; - char *data_buf; - bool writethrough = false, is_rdma_channel = false; - int err = 0; - unsigned int max_write_size = work->conn->vals->max_write_size; - - WORK_BUFFERS(work, req, rsp); - - if (test_share_config_flag(work->tcon->share_conf, KSMBD_SHARE_FLAG_PIPE)) { - ksmbd_debug(SMB, "IPC pipe write request\n"); - return smb2_write_pipe(work); - } - - offset = le64_to_cpu(req->Offset); - length = le32_to_cpu(req->Length); - - if (req->Channel == SMB2_CHANNEL_RDMA_V1 || - req->Channel == SMB2_CHANNEL_RDMA_V1_INVALIDATE) { - is_rdma_channel = true; - max_write_size = get_smbd_max_read_write_size(); - length = le32_to_cpu(req->RemainingBytes); - } - - if (is_rdma_channel == true) { - unsigned int ch_offset = le16_to_cpu(req->WriteChannelInfoOffset); - - if (req->Length != 0 || req->DataOffset != 0 || - ch_offset < offsetof(struct smb2_write_req, Buffer)) { - err = -EINVAL; - goto out; - } - err = smb2_set_remote_key_for_rdma(work, - (struct smb2_buffer_desc_v1 *) - ((char *)req + ch_offset), - req->Channel, - req->WriteChannelInfoLength); - if (err) - goto out; - } - - if (!test_tree_conn_flag(work->tcon, KSMBD_TREE_CONN_FLAG_WRITABLE)) { - ksmbd_debug(SMB, "User does not have write permission\n"); - err = -EACCES; - goto out; - } - - fp = ksmbd_lookup_fd_slow(work, req->VolatileFileId, req->PersistentFileId); - if (!fp) { - err = -ENOENT; - goto out; - } - - if (!(fp->daccess & (FILE_WRITE_DATA_LE | FILE_READ_ATTRIBUTES_LE))) { - pr_err("Not permitted to write : 0x%x\n", fp->daccess); - err = -EACCES; - goto out; - } - - if (length > max_write_size) { - ksmbd_debug(SMB, "limiting write size to max size(%u)\n", - max_write_size); - err = -EINVAL; - goto out; - } - - ksmbd_debug(SMB, "flags %u\n", le32_to_cpu(req->Flags)); - if (le32_to_cpu(req->Flags) & SMB2_WRITEFLAG_WRITE_THROUGH) - writethrough = true; - - if (is_rdma_channel == false) { - if (le16_to_cpu(req->DataOffset) < - offsetof(struct smb2_write_req, Buffer)) { - err = -EINVAL; - goto out; - } - - data_buf = (char *)(((char *)&req->hdr.ProtocolId) + - le16_to_cpu(req->DataOffset)); - - ksmbd_debug(SMB, "filename %pD, offset %lld, len %zu\n", - fp->filp, offset, length); - err = ksmbd_vfs_write(work, fp, data_buf, length, &offset, - writethrough, &nbytes); - if (err < 0) - goto out; - } else { - /* read data from the client using rdma channel, and - * write the data. - */ - nbytes = smb2_write_rdma_channel(work, req, fp, offset, length, - writethrough); - if (nbytes < 0) { - err = (int)nbytes; - goto out; - } - } - - rsp->StructureSize = cpu_to_le16(17); - rsp->DataOffset = 0; - rsp->Reserved = 0; - rsp->DataLength = cpu_to_le32(nbytes); - rsp->DataRemaining = 0; - rsp->Reserved2 = 0; - inc_rfc1001_len(work->response_buf, 16); - ksmbd_fd_put(work, fp); - return 0; - -out: - if (err == -EAGAIN) - rsp->hdr.Status = STATUS_FILE_LOCK_CONFLICT; - else if (err == -ENOSPC || err == -EFBIG) - rsp->hdr.Status = STATUS_DISK_FULL; - else if (err == -ENOENT) - rsp->hdr.Status = STATUS_FILE_CLOSED; - else if (err == -EACCES) - rsp->hdr.Status = STATUS_ACCESS_DENIED; - else if (err == -ESHARE) - rsp->hdr.Status = STATUS_SHARING_VIOLATION; - else if (err == -EINVAL) - rsp->hdr.Status = STATUS_INVALID_PARAMETER; - else - rsp->hdr.Status = STATUS_INVALID_HANDLE; - - smb2_set_err_rsp(work); - ksmbd_fd_put(work, fp); - return err; -} - -/** - * smb2_flush() - handler for smb2 flush file - fsync - * @work: smb work containing flush command buffer - * - * Return: 0 on success, otherwise error - */ -int smb2_flush(struct ksmbd_work *work) -{ - struct smb2_flush_req *req; - struct smb2_flush_rsp *rsp; - int err; - - WORK_BUFFERS(work, req, rsp); - - ksmbd_debug(SMB, "SMB2_FLUSH called for fid %llu\n", req->VolatileFileId); - - err = ksmbd_vfs_fsync(work, req->VolatileFileId, req->PersistentFileId); - if (err) - goto out; - - rsp->StructureSize = cpu_to_le16(4); - rsp->Reserved = 0; - inc_rfc1001_len(work->response_buf, 4); - return 0; - -out: - if (err) { - rsp->hdr.Status = STATUS_INVALID_HANDLE; - smb2_set_err_rsp(work); - } - - return err; -} - -/** - * smb2_cancel() - handler for smb2 cancel command - * @work: smb work containing cancel command buffer - * - * Return: 0 on success, otherwise error - */ -int smb2_cancel(struct ksmbd_work *work) -{ - struct ksmbd_conn *conn = work->conn; - struct smb2_hdr *hdr = smb2_get_msg(work->request_buf); - struct smb2_hdr *chdr; - struct ksmbd_work *iter; - struct list_head *command_list; - - ksmbd_debug(SMB, "smb2 cancel called on mid %llu, async flags 0x%x\n", - hdr->MessageId, hdr->Flags); - - if (hdr->Flags & SMB2_FLAGS_ASYNC_COMMAND) { - command_list = &conn->async_requests; - - spin_lock(&conn->request_lock); - list_for_each_entry(iter, command_list, - async_request_entry) { - chdr = smb2_get_msg(iter->request_buf); - - if (iter->async_id != - le64_to_cpu(hdr->Id.AsyncId)) - continue; - - ksmbd_debug(SMB, - "smb2 with AsyncId %llu cancelled command = 0x%x\n", - le64_to_cpu(hdr->Id.AsyncId), - le16_to_cpu(chdr->Command)); - iter->state = KSMBD_WORK_CANCELLED; - if (iter->cancel_fn) - iter->cancel_fn(iter->cancel_argv); - break; - } - spin_unlock(&conn->request_lock); - } else { - command_list = &conn->requests; - - spin_lock(&conn->request_lock); - list_for_each_entry(iter, command_list, request_entry) { - chdr = smb2_get_msg(iter->request_buf); - - if (chdr->MessageId != hdr->MessageId || - iter == work) - continue; - - ksmbd_debug(SMB, - "smb2 with mid %llu cancelled command = 0x%x\n", - le64_to_cpu(hdr->MessageId), - le16_to_cpu(chdr->Command)); - iter->state = KSMBD_WORK_CANCELLED; - break; - } - spin_unlock(&conn->request_lock); - } - - /* For SMB2_CANCEL command itself send no response*/ - work->send_no_response = 1; - return 0; -} - -struct file_lock *smb_flock_init(struct file *f) -{ - struct file_lock *fl; - - fl = locks_alloc_lock(); - if (!fl) - goto out; - - locks_init_lock(fl); - - fl->fl_owner = f; - fl->fl_pid = current->tgid; - fl->fl_file = f; - fl->fl_flags = FL_POSIX; - fl->fl_ops = NULL; - fl->fl_lmops = NULL; - -out: - return fl; -} - -static int smb2_set_flock_flags(struct file_lock *flock, int flags) -{ - int cmd = -EINVAL; - - /* Checking for wrong flag combination during lock request*/ - switch (flags) { - case SMB2_LOCKFLAG_SHARED: - ksmbd_debug(SMB, "received shared request\n"); - cmd = F_SETLKW; - flock->fl_type = F_RDLCK; - flock->fl_flags |= FL_SLEEP; - break; - case SMB2_LOCKFLAG_EXCLUSIVE: - ksmbd_debug(SMB, "received exclusive request\n"); - cmd = F_SETLKW; - flock->fl_type = F_WRLCK; - flock->fl_flags |= FL_SLEEP; - break; - case SMB2_LOCKFLAG_SHARED | SMB2_LOCKFLAG_FAIL_IMMEDIATELY: - ksmbd_debug(SMB, - "received shared & fail immediately request\n"); - cmd = F_SETLK; - flock->fl_type = F_RDLCK; - break; - case SMB2_LOCKFLAG_EXCLUSIVE | SMB2_LOCKFLAG_FAIL_IMMEDIATELY: - ksmbd_debug(SMB, - "received exclusive & fail immediately request\n"); - cmd = F_SETLK; - flock->fl_type = F_WRLCK; - break; - case SMB2_LOCKFLAG_UNLOCK: - ksmbd_debug(SMB, "received unlock request\n"); - flock->fl_type = F_UNLCK; - cmd = 0; - break; - } - - return cmd; -} - -static struct ksmbd_lock *smb2_lock_init(struct file_lock *flock, - unsigned int cmd, int flags, - struct list_head *lock_list) -{ - struct ksmbd_lock *lock; - - lock = kzalloc(sizeof(struct ksmbd_lock), GFP_KERNEL); - if (!lock) - return NULL; - - lock->cmd = cmd; - lock->fl = flock; - lock->start = flock->fl_start; - lock->end = flock->fl_end; - lock->flags = flags; - if (lock->start == lock->end) - lock->zero_len = 1; - INIT_LIST_HEAD(&lock->clist); - INIT_LIST_HEAD(&lock->flist); - INIT_LIST_HEAD(&lock->llist); - list_add_tail(&lock->llist, lock_list); - - return lock; -} - -static void smb2_remove_blocked_lock(void **argv) -{ - struct file_lock *flock = (struct file_lock *)argv[0]; - - ksmbd_vfs_posix_lock_unblock(flock); - wake_up(&flock->fl_wait); -} - -static inline bool lock_defer_pending(struct file_lock *fl) -{ - /* check pending lock waiters */ - return waitqueue_active(&fl->fl_wait); -} - -/** - * smb2_lock() - handler for smb2 file lock command - * @work: smb work containing lock command buffer - * - * Return: 0 on success, otherwise error - */ -int smb2_lock(struct ksmbd_work *work) -{ - struct smb2_lock_req *req = smb2_get_msg(work->request_buf); - struct smb2_lock_rsp *rsp = smb2_get_msg(work->response_buf); - struct smb2_lock_element *lock_ele; - struct ksmbd_file *fp = NULL; - struct file_lock *flock = NULL; - struct file *filp = NULL; - int lock_count; - int flags = 0; - int cmd = 0; - int err = -EIO, i, rc = 0; - u64 lock_start, lock_length; - struct ksmbd_lock *smb_lock = NULL, *cmp_lock, *tmp, *tmp2; - struct ksmbd_conn *conn; - int nolock = 0; - LIST_HEAD(lock_list); - LIST_HEAD(rollback_list); - int prior_lock = 0; - - ksmbd_debug(SMB, "Received lock request\n"); - fp = ksmbd_lookup_fd_slow(work, req->VolatileFileId, req->PersistentFileId); - if (!fp) { - ksmbd_debug(SMB, "Invalid file id for lock : %llu\n", req->VolatileFileId); - err = -ENOENT; - goto out2; - } - - filp = fp->filp; - lock_count = le16_to_cpu(req->LockCount); - lock_ele = req->locks; - - ksmbd_debug(SMB, "lock count is %d\n", lock_count); - if (!lock_count) { - err = -EINVAL; - goto out2; - } - - for (i = 0; i < lock_count; i++) { - flags = le32_to_cpu(lock_ele[i].Flags); - - flock = smb_flock_init(filp); - if (!flock) - goto out; - - cmd = smb2_set_flock_flags(flock, flags); - - lock_start = le64_to_cpu(lock_ele[i].Offset); - lock_length = le64_to_cpu(lock_ele[i].Length); - if (lock_start > U64_MAX - lock_length) { - pr_err("Invalid lock range requested\n"); - rsp->hdr.Status = STATUS_INVALID_LOCK_RANGE; - goto out; - } - - if (lock_start > OFFSET_MAX) - flock->fl_start = OFFSET_MAX; - else - flock->fl_start = lock_start; - - lock_length = le64_to_cpu(lock_ele[i].Length); - if (lock_length > OFFSET_MAX - flock->fl_start) - lock_length = OFFSET_MAX - flock->fl_start; - - flock->fl_end = flock->fl_start + lock_length; - - if (flock->fl_end < flock->fl_start) { - ksmbd_debug(SMB, - "the end offset(%llx) is smaller than the start offset(%llx)\n", - flock->fl_end, flock->fl_start); - rsp->hdr.Status = STATUS_INVALID_LOCK_RANGE; - goto out; - } - - /* Check conflict locks in one request */ - list_for_each_entry(cmp_lock, &lock_list, llist) { - if (cmp_lock->fl->fl_start <= flock->fl_start && - cmp_lock->fl->fl_end >= flock->fl_end) { - if (cmp_lock->fl->fl_type != F_UNLCK && - flock->fl_type != F_UNLCK) { - pr_err("conflict two locks in one request\n"); - err = -EINVAL; - goto out; - } - } - } - - smb_lock = smb2_lock_init(flock, cmd, flags, &lock_list); - if (!smb_lock) { - err = -EINVAL; - goto out; - } - } - - list_for_each_entry_safe(smb_lock, tmp, &lock_list, llist) { - if (smb_lock->cmd < 0) { - err = -EINVAL; - goto out; - } - - if (!(smb_lock->flags & SMB2_LOCKFLAG_MASK)) { - err = -EINVAL; - goto out; - } - - if ((prior_lock & (SMB2_LOCKFLAG_EXCLUSIVE | SMB2_LOCKFLAG_SHARED) && - smb_lock->flags & SMB2_LOCKFLAG_UNLOCK) || - (prior_lock == SMB2_LOCKFLAG_UNLOCK && - !(smb_lock->flags & SMB2_LOCKFLAG_UNLOCK))) { - err = -EINVAL; - goto out; - } - - prior_lock = smb_lock->flags; - - if (!(smb_lock->flags & SMB2_LOCKFLAG_UNLOCK) && - !(smb_lock->flags & SMB2_LOCKFLAG_FAIL_IMMEDIATELY)) - goto no_check_cl; - - nolock = 1; - /* check locks in connection list */ - down_read(&conn_list_lock); - list_for_each_entry(conn, &conn_list, conns_list) { - spin_lock(&conn->llist_lock); - list_for_each_entry_safe(cmp_lock, tmp2, &conn->lock_list, clist) { - if (file_inode(cmp_lock->fl->fl_file) != - file_inode(smb_lock->fl->fl_file)) - continue; - - if (smb_lock->fl->fl_type == F_UNLCK) { - if (cmp_lock->fl->fl_file == smb_lock->fl->fl_file && - cmp_lock->start == smb_lock->start && - cmp_lock->end == smb_lock->end && - !lock_defer_pending(cmp_lock->fl)) { - nolock = 0; - list_del(&cmp_lock->flist); - list_del(&cmp_lock->clist); - spin_unlock(&conn->llist_lock); - up_read(&conn_list_lock); - - locks_free_lock(cmp_lock->fl); - kfree(cmp_lock); - goto out_check_cl; - } - continue; - } - - if (cmp_lock->fl->fl_file == smb_lock->fl->fl_file) { - if (smb_lock->flags & SMB2_LOCKFLAG_SHARED) - continue; - } else { - if (cmp_lock->flags & SMB2_LOCKFLAG_SHARED) - continue; - } - - /* check zero byte lock range */ - if (cmp_lock->zero_len && !smb_lock->zero_len && - cmp_lock->start > smb_lock->start && - cmp_lock->start < smb_lock->end) { - spin_unlock(&conn->llist_lock); - up_read(&conn_list_lock); - pr_err("previous lock conflict with zero byte lock range\n"); - goto out; - } - - if (smb_lock->zero_len && !cmp_lock->zero_len && - smb_lock->start > cmp_lock->start && - smb_lock->start < cmp_lock->end) { - spin_unlock(&conn->llist_lock); - up_read(&conn_list_lock); - pr_err("current lock conflict with zero byte lock range\n"); - goto out; - } - - if (((cmp_lock->start <= smb_lock->start && - cmp_lock->end > smb_lock->start) || - (cmp_lock->start < smb_lock->end && - cmp_lock->end >= smb_lock->end)) && - !cmp_lock->zero_len && !smb_lock->zero_len) { - spin_unlock(&conn->llist_lock); - up_read(&conn_list_lock); - pr_err("Not allow lock operation on exclusive lock range\n"); - goto out; - } - } - spin_unlock(&conn->llist_lock); - } - up_read(&conn_list_lock); -out_check_cl: - if (smb_lock->fl->fl_type == F_UNLCK && nolock) { - pr_err("Try to unlock nolocked range\n"); - rsp->hdr.Status = STATUS_RANGE_NOT_LOCKED; - goto out; - } - -no_check_cl: - if (smb_lock->zero_len) { - err = 0; - goto skip; - } - - flock = smb_lock->fl; - list_del(&smb_lock->llist); -retry: - rc = vfs_lock_file(filp, smb_lock->cmd, flock, NULL); -skip: - if (flags & SMB2_LOCKFLAG_UNLOCK) { - if (!rc) { - ksmbd_debug(SMB, "File unlocked\n"); - } else if (rc == -ENOENT) { - rsp->hdr.Status = STATUS_NOT_LOCKED; - goto out; - } - locks_free_lock(flock); - kfree(smb_lock); - } else { - if (rc == FILE_LOCK_DEFERRED) { - void **argv; - - ksmbd_debug(SMB, - "would have to wait for getting lock\n"); - spin_lock(&work->conn->llist_lock); - list_add_tail(&smb_lock->clist, - &work->conn->lock_list); - spin_unlock(&work->conn->llist_lock); - list_add(&smb_lock->llist, &rollback_list); - - argv = kmalloc(sizeof(void *), GFP_KERNEL); - if (!argv) { - err = -ENOMEM; - goto out; - } - argv[0] = flock; - - rc = setup_async_work(work, - smb2_remove_blocked_lock, - argv); - if (rc) { - err = -ENOMEM; - goto out; - } - spin_lock(&fp->f_lock); - list_add(&work->fp_entry, &fp->blocked_works); - spin_unlock(&fp->f_lock); - - smb2_send_interim_resp(work, STATUS_PENDING); - - ksmbd_vfs_posix_lock_wait(flock); - - spin_lock(&work->conn->request_lock); - spin_lock(&fp->f_lock); - list_del(&work->fp_entry); - work->cancel_fn = NULL; - kfree(argv); - spin_unlock(&fp->f_lock); - spin_unlock(&work->conn->request_lock); - - if (work->state != KSMBD_WORK_ACTIVE) { - list_del(&smb_lock->llist); - spin_lock(&work->conn->llist_lock); - list_del(&smb_lock->clist); - spin_unlock(&work->conn->llist_lock); - locks_free_lock(flock); - - if (work->state == KSMBD_WORK_CANCELLED) { - rsp->hdr.Status = - STATUS_CANCELLED; - kfree(smb_lock); - smb2_send_interim_resp(work, - STATUS_CANCELLED); - work->send_no_response = 1; - goto out; - } - init_smb2_rsp_hdr(work); - smb2_set_err_rsp(work); - rsp->hdr.Status = - STATUS_RANGE_NOT_LOCKED; - kfree(smb_lock); - goto out2; - } - - list_del(&smb_lock->llist); - spin_lock(&work->conn->llist_lock); - list_del(&smb_lock->clist); - spin_unlock(&work->conn->llist_lock); - - goto retry; - } else if (!rc) { - spin_lock(&work->conn->llist_lock); - list_add_tail(&smb_lock->clist, - &work->conn->lock_list); - list_add_tail(&smb_lock->flist, - &fp->lock_list); - spin_unlock(&work->conn->llist_lock); - list_add(&smb_lock->llist, &rollback_list); - ksmbd_debug(SMB, "successful in taking lock\n"); - } else { - goto out; - } - } - } - - if (atomic_read(&fp->f_ci->op_count) > 1) - smb_break_all_oplock(work, fp); - - rsp->StructureSize = cpu_to_le16(4); - ksmbd_debug(SMB, "successful in taking lock\n"); - rsp->hdr.Status = STATUS_SUCCESS; - rsp->Reserved = 0; - inc_rfc1001_len(work->response_buf, 4); - ksmbd_fd_put(work, fp); - return 0; - -out: - list_for_each_entry_safe(smb_lock, tmp, &lock_list, llist) { - locks_free_lock(smb_lock->fl); - list_del(&smb_lock->llist); - kfree(smb_lock); - } - - list_for_each_entry_safe(smb_lock, tmp, &rollback_list, llist) { - struct file_lock *rlock = NULL; - - rlock = smb_flock_init(filp); - rlock->fl_type = F_UNLCK; - rlock->fl_start = smb_lock->start; - rlock->fl_end = smb_lock->end; - - rc = vfs_lock_file(filp, 0, rlock, NULL); - if (rc) - pr_err("rollback unlock fail : %d\n", rc); - - list_del(&smb_lock->llist); - spin_lock(&work->conn->llist_lock); - if (!list_empty(&smb_lock->flist)) - list_del(&smb_lock->flist); - list_del(&smb_lock->clist); - spin_unlock(&work->conn->llist_lock); - - locks_free_lock(smb_lock->fl); - locks_free_lock(rlock); - kfree(smb_lock); - } -out2: - ksmbd_debug(SMB, "failed in taking lock(flags : %x), err : %d\n", flags, err); - - if (!rsp->hdr.Status) { - if (err == -EINVAL) - rsp->hdr.Status = STATUS_INVALID_PARAMETER; - else if (err == -ENOMEM) - rsp->hdr.Status = STATUS_INSUFFICIENT_RESOURCES; - else if (err == -ENOENT) - rsp->hdr.Status = STATUS_FILE_CLOSED; - else - rsp->hdr.Status = STATUS_LOCK_NOT_GRANTED; - } - - smb2_set_err_rsp(work); - ksmbd_fd_put(work, fp); - return err; -} - -static int fsctl_copychunk(struct ksmbd_work *work, - struct copychunk_ioctl_req *ci_req, - unsigned int cnt_code, - unsigned int input_count, - unsigned long long volatile_id, - unsigned long long persistent_id, - struct smb2_ioctl_rsp *rsp) -{ - struct copychunk_ioctl_rsp *ci_rsp; - struct ksmbd_file *src_fp = NULL, *dst_fp = NULL; - struct srv_copychunk *chunks; - unsigned int i, chunk_count, chunk_count_written = 0; - unsigned int chunk_size_written = 0; - loff_t total_size_written = 0; - int ret = 0; - - ci_rsp = (struct copychunk_ioctl_rsp *)&rsp->Buffer[0]; - - rsp->VolatileFileId = volatile_id; - rsp->PersistentFileId = persistent_id; - ci_rsp->ChunksWritten = - cpu_to_le32(ksmbd_server_side_copy_max_chunk_count()); - ci_rsp->ChunkBytesWritten = - cpu_to_le32(ksmbd_server_side_copy_max_chunk_size()); - ci_rsp->TotalBytesWritten = - cpu_to_le32(ksmbd_server_side_copy_max_total_size()); - - chunks = (struct srv_copychunk *)&ci_req->Chunks[0]; - chunk_count = le32_to_cpu(ci_req->ChunkCount); - if (chunk_count == 0) - goto out; - total_size_written = 0; - - /* verify the SRV_COPYCHUNK_COPY packet */ - if (chunk_count > ksmbd_server_side_copy_max_chunk_count() || - input_count < offsetof(struct copychunk_ioctl_req, Chunks) + - chunk_count * sizeof(struct srv_copychunk)) { - rsp->hdr.Status = STATUS_INVALID_PARAMETER; - return -EINVAL; - } - - for (i = 0; i < chunk_count; i++) { - if (le32_to_cpu(chunks[i].Length) == 0 || - le32_to_cpu(chunks[i].Length) > ksmbd_server_side_copy_max_chunk_size()) - break; - total_size_written += le32_to_cpu(chunks[i].Length); - } - - if (i < chunk_count || - total_size_written > ksmbd_server_side_copy_max_total_size()) { - rsp->hdr.Status = STATUS_INVALID_PARAMETER; - return -EINVAL; - } - - src_fp = ksmbd_lookup_foreign_fd(work, - le64_to_cpu(ci_req->ResumeKey[0])); - dst_fp = ksmbd_lookup_fd_slow(work, volatile_id, persistent_id); - ret = -EINVAL; - if (!src_fp || - src_fp->persistent_id != le64_to_cpu(ci_req->ResumeKey[1])) { - rsp->hdr.Status = STATUS_OBJECT_NAME_NOT_FOUND; - goto out; - } - - if (!dst_fp) { - rsp->hdr.Status = STATUS_FILE_CLOSED; - goto out; - } - - /* - * FILE_READ_DATA should only be included in - * the FSCTL_COPYCHUNK case - */ - if (cnt_code == FSCTL_COPYCHUNK && - !(dst_fp->daccess & (FILE_READ_DATA_LE | FILE_GENERIC_READ_LE))) { - rsp->hdr.Status = STATUS_ACCESS_DENIED; - goto out; - } - - ret = ksmbd_vfs_copy_file_ranges(work, src_fp, dst_fp, - chunks, chunk_count, - &chunk_count_written, - &chunk_size_written, - &total_size_written); - if (ret < 0) { - if (ret == -EACCES) - rsp->hdr.Status = STATUS_ACCESS_DENIED; - if (ret == -EAGAIN) - rsp->hdr.Status = STATUS_FILE_LOCK_CONFLICT; - else if (ret == -EBADF) - rsp->hdr.Status = STATUS_INVALID_HANDLE; - else if (ret == -EFBIG || ret == -ENOSPC) - rsp->hdr.Status = STATUS_DISK_FULL; - else if (ret == -EINVAL) - rsp->hdr.Status = STATUS_INVALID_PARAMETER; - else if (ret == -EISDIR) - rsp->hdr.Status = STATUS_FILE_IS_A_DIRECTORY; - else if (ret == -E2BIG) - rsp->hdr.Status = STATUS_INVALID_VIEW_SIZE; - else - rsp->hdr.Status = STATUS_UNEXPECTED_IO_ERROR; - } - - ci_rsp->ChunksWritten = cpu_to_le32(chunk_count_written); - ci_rsp->ChunkBytesWritten = cpu_to_le32(chunk_size_written); - ci_rsp->TotalBytesWritten = cpu_to_le32(total_size_written); -out: - ksmbd_fd_put(work, src_fp); - ksmbd_fd_put(work, dst_fp); - return ret; -} - -static __be32 idev_ipv4_address(struct in_device *idev) -{ - __be32 addr = 0; - - struct in_ifaddr *ifa; - - rcu_read_lock(); - in_dev_for_each_ifa_rcu(ifa, idev) { - if (ifa->ifa_flags & IFA_F_SECONDARY) - continue; - - addr = ifa->ifa_address; - break; - } - rcu_read_unlock(); - return addr; -} - -static int fsctl_query_iface_info_ioctl(struct ksmbd_conn *conn, - struct smb2_ioctl_rsp *rsp, - unsigned int out_buf_len) -{ - struct network_interface_info_ioctl_rsp *nii_rsp = NULL; - int nbytes = 0; - struct net_device *netdev; - struct sockaddr_storage_rsp *sockaddr_storage; - unsigned int flags; - unsigned long long speed; - - rtnl_lock(); - for_each_netdev(&init_net, netdev) { - bool ipv4_set = false; - - if (netdev->type == ARPHRD_LOOPBACK) - continue; - - flags = dev_get_flags(netdev); - if (!(flags & IFF_RUNNING)) - continue; -ipv6_retry: - if (out_buf_len < - nbytes + sizeof(struct network_interface_info_ioctl_rsp)) { - rtnl_unlock(); - return -ENOSPC; - } - - nii_rsp = (struct network_interface_info_ioctl_rsp *) - &rsp->Buffer[nbytes]; - nii_rsp->IfIndex = cpu_to_le32(netdev->ifindex); - - nii_rsp->Capability = 0; - if (netdev->real_num_tx_queues > 1) - nii_rsp->Capability |= cpu_to_le32(RSS_CAPABLE); - if (ksmbd_rdma_capable_netdev(netdev)) - nii_rsp->Capability |= cpu_to_le32(RDMA_CAPABLE); - - nii_rsp->Next = cpu_to_le32(152); - nii_rsp->Reserved = 0; - - if (netdev->ethtool_ops->get_link_ksettings) { - struct ethtool_link_ksettings cmd; - - netdev->ethtool_ops->get_link_ksettings(netdev, &cmd); - speed = cmd.base.speed; - } else { - ksmbd_debug(SMB, "%s %s\n", netdev->name, - "speed is unknown, defaulting to 1Gb/sec"); - speed = SPEED_1000; - } - - speed *= 1000000; - nii_rsp->LinkSpeed = cpu_to_le64(speed); - - sockaddr_storage = (struct sockaddr_storage_rsp *) - nii_rsp->SockAddr_Storage; - memset(sockaddr_storage, 0, 128); - - if (!ipv4_set) { - struct in_device *idev; - - sockaddr_storage->Family = cpu_to_le16(INTERNETWORK); - sockaddr_storage->addr4.Port = 0; - - idev = __in_dev_get_rtnl(netdev); - if (!idev) - continue; - sockaddr_storage->addr4.IPv4address = - idev_ipv4_address(idev); - nbytes += sizeof(struct network_interface_info_ioctl_rsp); - ipv4_set = true; - goto ipv6_retry; - } else { - struct inet6_dev *idev6; - struct inet6_ifaddr *ifa; - __u8 *ipv6_addr = sockaddr_storage->addr6.IPv6address; - - sockaddr_storage->Family = cpu_to_le16(INTERNETWORKV6); - sockaddr_storage->addr6.Port = 0; - sockaddr_storage->addr6.FlowInfo = 0; - - idev6 = __in6_dev_get(netdev); - if (!idev6) - continue; - - list_for_each_entry(ifa, &idev6->addr_list, if_list) { - if (ifa->flags & (IFA_F_TENTATIVE | - IFA_F_DEPRECATED)) - continue; - memcpy(ipv6_addr, ifa->addr.s6_addr, 16); - break; - } - sockaddr_storage->addr6.ScopeId = 0; - nbytes += sizeof(struct network_interface_info_ioctl_rsp); - } - } - rtnl_unlock(); - - /* zero if this is last one */ - if (nii_rsp) - nii_rsp->Next = 0; - - rsp->PersistentFileId = SMB2_NO_FID; - rsp->VolatileFileId = SMB2_NO_FID; - return nbytes; -} - -static int fsctl_validate_negotiate_info(struct ksmbd_conn *conn, - struct validate_negotiate_info_req *neg_req, - struct validate_negotiate_info_rsp *neg_rsp, - unsigned int in_buf_len) -{ - int ret = 0; - int dialect; - - if (in_buf_len < offsetof(struct validate_negotiate_info_req, Dialects) + - le16_to_cpu(neg_req->DialectCount) * sizeof(__le16)) - return -EINVAL; - - dialect = ksmbd_lookup_dialect_by_id(neg_req->Dialects, - neg_req->DialectCount); - if (dialect == BAD_PROT_ID || dialect != conn->dialect) { - ret = -EINVAL; - goto err_out; - } - - if (strncmp(neg_req->Guid, conn->ClientGUID, SMB2_CLIENT_GUID_SIZE)) { - ret = -EINVAL; - goto err_out; - } - - if (le16_to_cpu(neg_req->SecurityMode) != conn->cli_sec_mode) { - ret = -EINVAL; - goto err_out; - } - - if (le32_to_cpu(neg_req->Capabilities) != conn->cli_cap) { - ret = -EINVAL; - goto err_out; - } - - neg_rsp->Capabilities = cpu_to_le32(conn->vals->capabilities); - memset(neg_rsp->Guid, 0, SMB2_CLIENT_GUID_SIZE); - neg_rsp->SecurityMode = cpu_to_le16(conn->srv_sec_mode); - neg_rsp->Dialect = cpu_to_le16(conn->dialect); -err_out: - return ret; -} - -static int fsctl_query_allocated_ranges(struct ksmbd_work *work, u64 id, - struct file_allocated_range_buffer *qar_req, - struct file_allocated_range_buffer *qar_rsp, - unsigned int in_count, unsigned int *out_count) -{ - struct ksmbd_file *fp; - loff_t start, length; - int ret = 0; - - *out_count = 0; - if (in_count == 0) - return -EINVAL; - - start = le64_to_cpu(qar_req->file_offset); - length = le64_to_cpu(qar_req->length); - - if (start < 0 || length < 0) - return -EINVAL; - - fp = ksmbd_lookup_fd_fast(work, id); - if (!fp) - return -ENOENT; - - ret = ksmbd_vfs_fqar_lseek(fp, start, length, - qar_rsp, in_count, out_count); - if (ret && ret != -E2BIG) - *out_count = 0; - - ksmbd_fd_put(work, fp); - return ret; -} - -static int fsctl_pipe_transceive(struct ksmbd_work *work, u64 id, - unsigned int out_buf_len, - struct smb2_ioctl_req *req, - struct smb2_ioctl_rsp *rsp) -{ - struct ksmbd_rpc_command *rpc_resp; - char *data_buf = (char *)&req->Buffer[0]; - int nbytes = 0; - - rpc_resp = ksmbd_rpc_ioctl(work->sess, id, data_buf, - le32_to_cpu(req->InputCount)); - if (rpc_resp) { - if (rpc_resp->flags == KSMBD_RPC_SOME_NOT_MAPPED) { - /* - * set STATUS_SOME_NOT_MAPPED response - * for unknown domain sid. - */ - rsp->hdr.Status = STATUS_SOME_NOT_MAPPED; - } else if (rpc_resp->flags == KSMBD_RPC_ENOTIMPLEMENTED) { - rsp->hdr.Status = STATUS_NOT_SUPPORTED; - goto out; - } else if (rpc_resp->flags != KSMBD_RPC_OK) { - rsp->hdr.Status = STATUS_INVALID_PARAMETER; - goto out; - } - - nbytes = rpc_resp->payload_sz; - if (rpc_resp->payload_sz > out_buf_len) { - rsp->hdr.Status = STATUS_BUFFER_OVERFLOW; - nbytes = out_buf_len; - } - - if (!rpc_resp->payload_sz) { - rsp->hdr.Status = - STATUS_UNEXPECTED_IO_ERROR; - goto out; - } - - memcpy((char *)rsp->Buffer, rpc_resp->payload, nbytes); - } -out: - kvfree(rpc_resp); - return nbytes; -} - -static inline int fsctl_set_sparse(struct ksmbd_work *work, u64 id, - struct file_sparse *sparse) -{ - struct ksmbd_file *fp; - struct user_namespace *user_ns; - int ret = 0; - __le32 old_fattr; - - fp = ksmbd_lookup_fd_fast(work, id); - if (!fp) - return -ENOENT; - user_ns = file_mnt_user_ns(fp->filp); - - old_fattr = fp->f_ci->m_fattr; - if (sparse->SetSparse) - fp->f_ci->m_fattr |= FILE_ATTRIBUTE_SPARSE_FILE_LE; - else - fp->f_ci->m_fattr &= ~FILE_ATTRIBUTE_SPARSE_FILE_LE; - - if (fp->f_ci->m_fattr != old_fattr && - test_share_config_flag(work->tcon->share_conf, - KSMBD_SHARE_FLAG_STORE_DOS_ATTRS)) { - struct xattr_dos_attrib da; - - ret = ksmbd_vfs_get_dos_attrib_xattr(user_ns, - fp->filp->f_path.dentry, &da); - if (ret <= 0) - goto out; - - da.attr = le32_to_cpu(fp->f_ci->m_fattr); - ret = ksmbd_vfs_set_dos_attrib_xattr(user_ns, - fp->filp->f_path.dentry, &da); - if (ret) - fp->f_ci->m_fattr = old_fattr; - } - -out: - ksmbd_fd_put(work, fp); - return ret; -} - -static int fsctl_request_resume_key(struct ksmbd_work *work, - struct smb2_ioctl_req *req, - struct resume_key_ioctl_rsp *key_rsp) -{ - struct ksmbd_file *fp; - - fp = ksmbd_lookup_fd_slow(work, req->VolatileFileId, req->PersistentFileId); - if (!fp) - return -ENOENT; - - memset(key_rsp, 0, sizeof(*key_rsp)); - key_rsp->ResumeKey[0] = req->VolatileFileId; - key_rsp->ResumeKey[1] = req->PersistentFileId; - ksmbd_fd_put(work, fp); - - return 0; -} - -/** - * smb2_ioctl() - handler for smb2 ioctl command - * @work: smb work containing ioctl command buffer - * - * Return: 0 on success, otherwise error - */ -int smb2_ioctl(struct ksmbd_work *work) -{ - struct smb2_ioctl_req *req; - struct smb2_ioctl_rsp *rsp; - unsigned int cnt_code, nbytes = 0, out_buf_len, in_buf_len; - u64 id = KSMBD_NO_FID; - struct ksmbd_conn *conn = work->conn; - int ret = 0; - - if (work->next_smb2_rcv_hdr_off) { - req = ksmbd_req_buf_next(work); - rsp = ksmbd_resp_buf_next(work); - if (!has_file_id(req->VolatileFileId)) { - ksmbd_debug(SMB, "Compound request set FID = %llu\n", - work->compound_fid); - id = work->compound_fid; - } - } else { - req = smb2_get_msg(work->request_buf); - rsp = smb2_get_msg(work->response_buf); - } - - if (!has_file_id(id)) - id = req->VolatileFileId; - - if (req->Flags != cpu_to_le32(SMB2_0_IOCTL_IS_FSCTL)) { - rsp->hdr.Status = STATUS_NOT_SUPPORTED; - goto out; - } - - cnt_code = le32_to_cpu(req->CtlCode); - ret = smb2_calc_max_out_buf_len(work, 48, - le32_to_cpu(req->MaxOutputResponse)); - if (ret < 0) { - rsp->hdr.Status = STATUS_INVALID_PARAMETER; - goto out; - } - out_buf_len = (unsigned int)ret; - in_buf_len = le32_to_cpu(req->InputCount); - - switch (cnt_code) { - case FSCTL_DFS_GET_REFERRALS: - case FSCTL_DFS_GET_REFERRALS_EX: - /* Not support DFS yet */ - rsp->hdr.Status = STATUS_FS_DRIVER_REQUIRED; - goto out; - case FSCTL_CREATE_OR_GET_OBJECT_ID: - { - struct file_object_buf_type1_ioctl_rsp *obj_buf; - - nbytes = sizeof(struct file_object_buf_type1_ioctl_rsp); - obj_buf = (struct file_object_buf_type1_ioctl_rsp *) - &rsp->Buffer[0]; - - /* - * TODO: This is dummy implementation to pass smbtorture - * Need to check correct response later - */ - memset(obj_buf->ObjectId, 0x0, 16); - memset(obj_buf->BirthVolumeId, 0x0, 16); - memset(obj_buf->BirthObjectId, 0x0, 16); - memset(obj_buf->DomainId, 0x0, 16); - - break; - } - case FSCTL_PIPE_TRANSCEIVE: - out_buf_len = min_t(u32, KSMBD_IPC_MAX_PAYLOAD, out_buf_len); - nbytes = fsctl_pipe_transceive(work, id, out_buf_len, req, rsp); - break; - case FSCTL_VALIDATE_NEGOTIATE_INFO: - if (conn->dialect < SMB30_PROT_ID) { - ret = -EOPNOTSUPP; - goto out; - } - - if (in_buf_len < offsetof(struct validate_negotiate_info_req, - Dialects)) { - ret = -EINVAL; - goto out; - } - - if (out_buf_len < sizeof(struct validate_negotiate_info_rsp)) { - ret = -EINVAL; - goto out; - } - - ret = fsctl_validate_negotiate_info(conn, - (struct validate_negotiate_info_req *)&req->Buffer[0], - (struct validate_negotiate_info_rsp *)&rsp->Buffer[0], - in_buf_len); - if (ret < 0) - goto out; - - nbytes = sizeof(struct validate_negotiate_info_rsp); - rsp->PersistentFileId = SMB2_NO_FID; - rsp->VolatileFileId = SMB2_NO_FID; - break; - case FSCTL_QUERY_NETWORK_INTERFACE_INFO: - ret = fsctl_query_iface_info_ioctl(conn, rsp, out_buf_len); - if (ret < 0) - goto out; - nbytes = ret; - break; - case FSCTL_REQUEST_RESUME_KEY: - if (out_buf_len < sizeof(struct resume_key_ioctl_rsp)) { - ret = -EINVAL; - goto out; - } - - ret = fsctl_request_resume_key(work, req, - (struct resume_key_ioctl_rsp *)&rsp->Buffer[0]); - if (ret < 0) - goto out; - rsp->PersistentFileId = req->PersistentFileId; - rsp->VolatileFileId = req->VolatileFileId; - nbytes = sizeof(struct resume_key_ioctl_rsp); - break; - case FSCTL_COPYCHUNK: - case FSCTL_COPYCHUNK_WRITE: - if (!test_tree_conn_flag(work->tcon, KSMBD_TREE_CONN_FLAG_WRITABLE)) { - ksmbd_debug(SMB, - "User does not have write permission\n"); - ret = -EACCES; - goto out; - } - - if (in_buf_len < sizeof(struct copychunk_ioctl_req)) { - ret = -EINVAL; - goto out; - } - - if (out_buf_len < sizeof(struct copychunk_ioctl_rsp)) { - ret = -EINVAL; - goto out; - } - - nbytes = sizeof(struct copychunk_ioctl_rsp); - rsp->VolatileFileId = req->VolatileFileId; - rsp->PersistentFileId = req->PersistentFileId; - fsctl_copychunk(work, - (struct copychunk_ioctl_req *)&req->Buffer[0], - le32_to_cpu(req->CtlCode), - le32_to_cpu(req->InputCount), - req->VolatileFileId, - req->PersistentFileId, - rsp); - break; - case FSCTL_SET_SPARSE: - if (in_buf_len < sizeof(struct file_sparse)) { - ret = -EINVAL; - goto out; - } - - ret = fsctl_set_sparse(work, id, - (struct file_sparse *)&req->Buffer[0]); - if (ret < 0) - goto out; - break; - case FSCTL_SET_ZERO_DATA: - { - struct file_zero_data_information *zero_data; - struct ksmbd_file *fp; - loff_t off, len, bfz; - - if (!test_tree_conn_flag(work->tcon, KSMBD_TREE_CONN_FLAG_WRITABLE)) { - ksmbd_debug(SMB, - "User does not have write permission\n"); - ret = -EACCES; - goto out; - } - - if (in_buf_len < sizeof(struct file_zero_data_information)) { - ret = -EINVAL; - goto out; - } - - zero_data = - (struct file_zero_data_information *)&req->Buffer[0]; - - off = le64_to_cpu(zero_data->FileOffset); - bfz = le64_to_cpu(zero_data->BeyondFinalZero); - if (off < 0 || bfz < 0 || off > bfz) { - ret = -EINVAL; - goto out; - } - - len = bfz - off; - if (len) { - fp = ksmbd_lookup_fd_fast(work, id); - if (!fp) { - ret = -ENOENT; - goto out; - } - - ret = ksmbd_vfs_zero_data(work, fp, off, len); - ksmbd_fd_put(work, fp); - if (ret < 0) - goto out; - } - break; - } - case FSCTL_QUERY_ALLOCATED_RANGES: - if (in_buf_len < sizeof(struct file_allocated_range_buffer)) { - ret = -EINVAL; - goto out; - } - - ret = fsctl_query_allocated_ranges(work, id, - (struct file_allocated_range_buffer *)&req->Buffer[0], - (struct file_allocated_range_buffer *)&rsp->Buffer[0], - out_buf_len / - sizeof(struct file_allocated_range_buffer), &nbytes); - if (ret == -E2BIG) { - rsp->hdr.Status = STATUS_BUFFER_OVERFLOW; - } else if (ret < 0) { - nbytes = 0; - goto out; - } - - nbytes *= sizeof(struct file_allocated_range_buffer); - break; - case FSCTL_GET_REPARSE_POINT: - { - struct reparse_data_buffer *reparse_ptr; - struct ksmbd_file *fp; - - reparse_ptr = (struct reparse_data_buffer *)&rsp->Buffer[0]; - fp = ksmbd_lookup_fd_fast(work, id); - if (!fp) { - pr_err("not found fp!!\n"); - ret = -ENOENT; - goto out; - } - - reparse_ptr->ReparseTag = - smb2_get_reparse_tag_special_file(file_inode(fp->filp)->i_mode); - reparse_ptr->ReparseDataLength = 0; - ksmbd_fd_put(work, fp); - nbytes = sizeof(struct reparse_data_buffer); - break; - } - case FSCTL_DUPLICATE_EXTENTS_TO_FILE: - { - struct ksmbd_file *fp_in, *fp_out = NULL; - struct duplicate_extents_to_file *dup_ext; - loff_t src_off, dst_off, length, cloned; - - if (in_buf_len < sizeof(struct duplicate_extents_to_file)) { - ret = -EINVAL; - goto out; - } - - dup_ext = (struct duplicate_extents_to_file *)&req->Buffer[0]; - - fp_in = ksmbd_lookup_fd_slow(work, dup_ext->VolatileFileHandle, - dup_ext->PersistentFileHandle); - if (!fp_in) { - pr_err("not found file handle in duplicate extent to file\n"); - ret = -ENOENT; - goto out; - } - - fp_out = ksmbd_lookup_fd_fast(work, id); - if (!fp_out) { - pr_err("not found fp\n"); - ret = -ENOENT; - goto dup_ext_out; - } - - src_off = le64_to_cpu(dup_ext->SourceFileOffset); - dst_off = le64_to_cpu(dup_ext->TargetFileOffset); - length = le64_to_cpu(dup_ext->ByteCount); - /* - * XXX: It is not clear if FSCTL_DUPLICATE_EXTENTS_TO_FILE - * should fall back to vfs_copy_file_range(). This could be - * beneficial when re-exporting nfs/smb mount, but note that - * this can result in partial copy that returns an error status. - * If/when FSCTL_DUPLICATE_EXTENTS_TO_FILE_EX is implemented, - * fall back to vfs_copy_file_range(), should be avoided when - * the flag DUPLICATE_EXTENTS_DATA_EX_SOURCE_ATOMIC is set. - */ - cloned = vfs_clone_file_range(fp_in->filp, src_off, - fp_out->filp, dst_off, length, 0); - if (cloned == -EXDEV || cloned == -EOPNOTSUPP) { - ret = -EOPNOTSUPP; - goto dup_ext_out; - } else if (cloned != length) { - cloned = vfs_copy_file_range(fp_in->filp, src_off, - fp_out->filp, dst_off, - length, 0); - if (cloned != length) { - if (cloned < 0) - ret = cloned; - else - ret = -EINVAL; - } - } - -dup_ext_out: - ksmbd_fd_put(work, fp_in); - ksmbd_fd_put(work, fp_out); - if (ret < 0) - goto out; - break; - } - default: - ksmbd_debug(SMB, "not implemented yet ioctl command 0x%x\n", - cnt_code); - ret = -EOPNOTSUPP; - goto out; - } - - rsp->CtlCode = cpu_to_le32(cnt_code); - rsp->InputCount = cpu_to_le32(0); - rsp->InputOffset = cpu_to_le32(112); - rsp->OutputOffset = cpu_to_le32(112); - rsp->OutputCount = cpu_to_le32(nbytes); - rsp->StructureSize = cpu_to_le16(49); - rsp->Reserved = cpu_to_le16(0); - rsp->Flags = cpu_to_le32(0); - rsp->Reserved2 = cpu_to_le32(0); - inc_rfc1001_len(work->response_buf, 48 + nbytes); - - return 0; - -out: - if (ret == -EACCES) - rsp->hdr.Status = STATUS_ACCESS_DENIED; - else if (ret == -ENOENT) - rsp->hdr.Status = STATUS_OBJECT_NAME_NOT_FOUND; - else if (ret == -EOPNOTSUPP) - rsp->hdr.Status = STATUS_NOT_SUPPORTED; - else if (ret == -ENOSPC) - rsp->hdr.Status = STATUS_BUFFER_TOO_SMALL; - else if (ret < 0 || rsp->hdr.Status == 0) - rsp->hdr.Status = STATUS_INVALID_PARAMETER; - smb2_set_err_rsp(work); - return 0; -} - -/** - * smb20_oplock_break_ack() - handler for smb2.0 oplock break command - * @work: smb work containing oplock break command buffer - * - * Return: 0 - */ -static void smb20_oplock_break_ack(struct ksmbd_work *work) -{ - struct smb2_oplock_break *req = smb2_get_msg(work->request_buf); - struct smb2_oplock_break *rsp = smb2_get_msg(work->response_buf); - struct ksmbd_file *fp; - struct oplock_info *opinfo = NULL; - __le32 err = 0; - int ret = 0; - u64 volatile_id, persistent_id; - char req_oplevel = 0, rsp_oplevel = 0; - unsigned int oplock_change_type; - - volatile_id = req->VolatileFid; - persistent_id = req->PersistentFid; - req_oplevel = req->OplockLevel; - ksmbd_debug(OPLOCK, "v_id %llu, p_id %llu request oplock level %d\n", - volatile_id, persistent_id, req_oplevel); - - fp = ksmbd_lookup_fd_slow(work, volatile_id, persistent_id); - if (!fp) { - rsp->hdr.Status = STATUS_FILE_CLOSED; - smb2_set_err_rsp(work); - return; - } - - opinfo = opinfo_get(fp); - if (!opinfo) { - pr_err("unexpected null oplock_info\n"); - rsp->hdr.Status = STATUS_INVALID_OPLOCK_PROTOCOL; - smb2_set_err_rsp(work); - ksmbd_fd_put(work, fp); - return; - } - - if (opinfo->level == SMB2_OPLOCK_LEVEL_NONE) { - rsp->hdr.Status = STATUS_INVALID_OPLOCK_PROTOCOL; - goto err_out; - } - - if (opinfo->op_state == OPLOCK_STATE_NONE) { - ksmbd_debug(SMB, "unexpected oplock state 0x%x\n", opinfo->op_state); - rsp->hdr.Status = STATUS_UNSUCCESSFUL; - goto err_out; - } - - if ((opinfo->level == SMB2_OPLOCK_LEVEL_EXCLUSIVE || - opinfo->level == SMB2_OPLOCK_LEVEL_BATCH) && - (req_oplevel != SMB2_OPLOCK_LEVEL_II && - req_oplevel != SMB2_OPLOCK_LEVEL_NONE)) { - err = STATUS_INVALID_OPLOCK_PROTOCOL; - oplock_change_type = OPLOCK_WRITE_TO_NONE; - } else if (opinfo->level == SMB2_OPLOCK_LEVEL_II && - req_oplevel != SMB2_OPLOCK_LEVEL_NONE) { - err = STATUS_INVALID_OPLOCK_PROTOCOL; - oplock_change_type = OPLOCK_READ_TO_NONE; - } else if (req_oplevel == SMB2_OPLOCK_LEVEL_II || - req_oplevel == SMB2_OPLOCK_LEVEL_NONE) { - err = STATUS_INVALID_DEVICE_STATE; - if ((opinfo->level == SMB2_OPLOCK_LEVEL_EXCLUSIVE || - opinfo->level == SMB2_OPLOCK_LEVEL_BATCH) && - req_oplevel == SMB2_OPLOCK_LEVEL_II) { - oplock_change_type = OPLOCK_WRITE_TO_READ; - } else if ((opinfo->level == SMB2_OPLOCK_LEVEL_EXCLUSIVE || - opinfo->level == SMB2_OPLOCK_LEVEL_BATCH) && - req_oplevel == SMB2_OPLOCK_LEVEL_NONE) { - oplock_change_type = OPLOCK_WRITE_TO_NONE; - } else if (opinfo->level == SMB2_OPLOCK_LEVEL_II && - req_oplevel == SMB2_OPLOCK_LEVEL_NONE) { - oplock_change_type = OPLOCK_READ_TO_NONE; - } else { - oplock_change_type = 0; - } - } else { - oplock_change_type = 0; - } - - switch (oplock_change_type) { - case OPLOCK_WRITE_TO_READ: - ret = opinfo_write_to_read(opinfo); - rsp_oplevel = SMB2_OPLOCK_LEVEL_II; - break; - case OPLOCK_WRITE_TO_NONE: - ret = opinfo_write_to_none(opinfo); - rsp_oplevel = SMB2_OPLOCK_LEVEL_NONE; - break; - case OPLOCK_READ_TO_NONE: - ret = opinfo_read_to_none(opinfo); - rsp_oplevel = SMB2_OPLOCK_LEVEL_NONE; - break; - default: - pr_err("unknown oplock change 0x%x -> 0x%x\n", - opinfo->level, rsp_oplevel); - } - - if (ret < 0) { - rsp->hdr.Status = err; - goto err_out; - } - - opinfo_put(opinfo); - ksmbd_fd_put(work, fp); - opinfo->op_state = OPLOCK_STATE_NONE; - wake_up_interruptible_all(&opinfo->oplock_q); - - rsp->StructureSize = cpu_to_le16(24); - rsp->OplockLevel = rsp_oplevel; - rsp->Reserved = 0; - rsp->Reserved2 = 0; - rsp->VolatileFid = volatile_id; - rsp->PersistentFid = persistent_id; - inc_rfc1001_len(work->response_buf, 24); - return; - -err_out: - opinfo->op_state = OPLOCK_STATE_NONE; - wake_up_interruptible_all(&opinfo->oplock_q); - - opinfo_put(opinfo); - ksmbd_fd_put(work, fp); - smb2_set_err_rsp(work); -} - -static int check_lease_state(struct lease *lease, __le32 req_state) -{ - if ((lease->new_state == - (SMB2_LEASE_READ_CACHING_LE | SMB2_LEASE_HANDLE_CACHING_LE)) && - !(req_state & SMB2_LEASE_WRITE_CACHING_LE)) { - lease->new_state = req_state; - return 0; - } - - if (lease->new_state == req_state) - return 0; - - return 1; -} - -/** - * smb21_lease_break_ack() - handler for smb2.1 lease break command - * @work: smb work containing lease break command buffer - * - * Return: 0 - */ -static void smb21_lease_break_ack(struct ksmbd_work *work) -{ - struct ksmbd_conn *conn = work->conn; - struct smb2_lease_ack *req = smb2_get_msg(work->request_buf); - struct smb2_lease_ack *rsp = smb2_get_msg(work->response_buf); - struct oplock_info *opinfo; - __le32 err = 0; - int ret = 0; - unsigned int lease_change_type; - __le32 lease_state; - struct lease *lease; - - ksmbd_debug(OPLOCK, "smb21 lease break, lease state(0x%x)\n", - le32_to_cpu(req->LeaseState)); - opinfo = lookup_lease_in_table(conn, req->LeaseKey); - if (!opinfo) { - ksmbd_debug(OPLOCK, "file not opened\n"); - smb2_set_err_rsp(work); - rsp->hdr.Status = STATUS_UNSUCCESSFUL; - return; - } - lease = opinfo->o_lease; - - if (opinfo->op_state == OPLOCK_STATE_NONE) { - pr_err("unexpected lease break state 0x%x\n", - opinfo->op_state); - rsp->hdr.Status = STATUS_UNSUCCESSFUL; - goto err_out; - } - - if (check_lease_state(lease, req->LeaseState)) { - rsp->hdr.Status = STATUS_REQUEST_NOT_ACCEPTED; - ksmbd_debug(OPLOCK, - "req lease state: 0x%x, expected state: 0x%x\n", - req->LeaseState, lease->new_state); - goto err_out; - } - - if (!atomic_read(&opinfo->breaking_cnt)) { - rsp->hdr.Status = STATUS_UNSUCCESSFUL; - goto err_out; - } - - /* check for bad lease state */ - if (req->LeaseState & - (~(SMB2_LEASE_READ_CACHING_LE | SMB2_LEASE_HANDLE_CACHING_LE))) { - err = STATUS_INVALID_OPLOCK_PROTOCOL; - if (lease->state & SMB2_LEASE_WRITE_CACHING_LE) - lease_change_type = OPLOCK_WRITE_TO_NONE; - else - lease_change_type = OPLOCK_READ_TO_NONE; - ksmbd_debug(OPLOCK, "handle bad lease state 0x%x -> 0x%x\n", - le32_to_cpu(lease->state), - le32_to_cpu(req->LeaseState)); - } else if (lease->state == SMB2_LEASE_READ_CACHING_LE && - req->LeaseState != SMB2_LEASE_NONE_LE) { - err = STATUS_INVALID_OPLOCK_PROTOCOL; - lease_change_type = OPLOCK_READ_TO_NONE; - ksmbd_debug(OPLOCK, "handle bad lease state 0x%x -> 0x%x\n", - le32_to_cpu(lease->state), - le32_to_cpu(req->LeaseState)); - } else { - /* valid lease state changes */ - err = STATUS_INVALID_DEVICE_STATE; - if (req->LeaseState == SMB2_LEASE_NONE_LE) { - if (lease->state & SMB2_LEASE_WRITE_CACHING_LE) - lease_change_type = OPLOCK_WRITE_TO_NONE; - else - lease_change_type = OPLOCK_READ_TO_NONE; - } else if (req->LeaseState & SMB2_LEASE_READ_CACHING_LE) { - if (lease->state & SMB2_LEASE_WRITE_CACHING_LE) - lease_change_type = OPLOCK_WRITE_TO_READ; - else - lease_change_type = OPLOCK_READ_HANDLE_TO_READ; - } else { - lease_change_type = 0; - } - } - - switch (lease_change_type) { - case OPLOCK_WRITE_TO_READ: - ret = opinfo_write_to_read(opinfo); - break; - case OPLOCK_READ_HANDLE_TO_READ: - ret = opinfo_read_handle_to_read(opinfo); - break; - case OPLOCK_WRITE_TO_NONE: - ret = opinfo_write_to_none(opinfo); - break; - case OPLOCK_READ_TO_NONE: - ret = opinfo_read_to_none(opinfo); - break; - default: - ksmbd_debug(OPLOCK, "unknown lease change 0x%x -> 0x%x\n", - le32_to_cpu(lease->state), - le32_to_cpu(req->LeaseState)); - } - - lease_state = lease->state; - opinfo->op_state = OPLOCK_STATE_NONE; - wake_up_interruptible_all(&opinfo->oplock_q); - atomic_dec(&opinfo->breaking_cnt); - wake_up_interruptible_all(&opinfo->oplock_brk); - opinfo_put(opinfo); - - if (ret < 0) { - rsp->hdr.Status = err; - goto err_out; - } - - rsp->StructureSize = cpu_to_le16(36); - rsp->Reserved = 0; - rsp->Flags = 0; - memcpy(rsp->LeaseKey, req->LeaseKey, 16); - rsp->LeaseState = lease_state; - rsp->LeaseDuration = 0; - inc_rfc1001_len(work->response_buf, 36); - return; - -err_out: - opinfo->op_state = OPLOCK_STATE_NONE; - wake_up_interruptible_all(&opinfo->oplock_q); - atomic_dec(&opinfo->breaking_cnt); - wake_up_interruptible_all(&opinfo->oplock_brk); - - opinfo_put(opinfo); - smb2_set_err_rsp(work); -} - -/** - * smb2_oplock_break() - dispatcher for smb2.0 and 2.1 oplock/lease break - * @work: smb work containing oplock/lease break command buffer - * - * Return: 0 - */ -int smb2_oplock_break(struct ksmbd_work *work) -{ - struct smb2_oplock_break *req = smb2_get_msg(work->request_buf); - struct smb2_oplock_break *rsp = smb2_get_msg(work->response_buf); - - switch (le16_to_cpu(req->StructureSize)) { - case OP_BREAK_STRUCT_SIZE_20: - smb20_oplock_break_ack(work); - break; - case OP_BREAK_STRUCT_SIZE_21: - smb21_lease_break_ack(work); - break; - default: - ksmbd_debug(OPLOCK, "invalid break cmd %d\n", - le16_to_cpu(req->StructureSize)); - rsp->hdr.Status = STATUS_INVALID_PARAMETER; - smb2_set_err_rsp(work); - } - - return 0; -} - -/** - * smb2_notify() - handler for smb2 notify request - * @work: smb work containing notify command buffer - * - * Return: 0 - */ -int smb2_notify(struct ksmbd_work *work) -{ - struct smb2_change_notify_req *req; - struct smb2_change_notify_rsp *rsp; - - WORK_BUFFERS(work, req, rsp); - - if (work->next_smb2_rcv_hdr_off && req->hdr.NextCommand) { - rsp->hdr.Status = STATUS_INTERNAL_ERROR; - smb2_set_err_rsp(work); - return 0; - } - - smb2_set_err_rsp(work); - rsp->hdr.Status = STATUS_NOT_IMPLEMENTED; - return 0; -} - -/** - * smb2_is_sign_req() - handler for checking packet signing status - * @work: smb work containing notify command buffer - * @command: SMB2 command id - * - * Return: true if packed is signed, false otherwise - */ -bool smb2_is_sign_req(struct ksmbd_work *work, unsigned int command) -{ - struct smb2_hdr *rcv_hdr2 = smb2_get_msg(work->request_buf); - - if ((rcv_hdr2->Flags & SMB2_FLAGS_SIGNED) && - command != SMB2_NEGOTIATE_HE && - command != SMB2_SESSION_SETUP_HE && - command != SMB2_OPLOCK_BREAK_HE) - return true; - - return false; -} - -/** - * smb2_check_sign_req() - handler for req packet sign processing - * @work: smb work containing notify command buffer - * - * Return: 1 on success, 0 otherwise - */ -int smb2_check_sign_req(struct ksmbd_work *work) -{ - struct smb2_hdr *hdr; - char signature_req[SMB2_SIGNATURE_SIZE]; - char signature[SMB2_HMACSHA256_SIZE]; - struct kvec iov[1]; - size_t len; - - hdr = smb2_get_msg(work->request_buf); - if (work->next_smb2_rcv_hdr_off) - hdr = ksmbd_req_buf_next(work); - - if (!hdr->NextCommand && !work->next_smb2_rcv_hdr_off) - len = get_rfc1002_len(work->request_buf); - else if (hdr->NextCommand) - len = le32_to_cpu(hdr->NextCommand); - else - len = get_rfc1002_len(work->request_buf) - - work->next_smb2_rcv_hdr_off; - - memcpy(signature_req, hdr->Signature, SMB2_SIGNATURE_SIZE); - memset(hdr->Signature, 0, SMB2_SIGNATURE_SIZE); - - iov[0].iov_base = (char *)&hdr->ProtocolId; - iov[0].iov_len = len; - - if (ksmbd_sign_smb2_pdu(work->conn, work->sess->sess_key, iov, 1, - signature)) - return 0; - - if (memcmp(signature, signature_req, SMB2_SIGNATURE_SIZE)) { - pr_err("bad smb2 signature\n"); - return 0; - } - - return 1; -} - -/** - * smb2_set_sign_rsp() - handler for rsp packet sign processing - * @work: smb work containing notify command buffer - * - */ -void smb2_set_sign_rsp(struct ksmbd_work *work) -{ - struct smb2_hdr *hdr; - struct smb2_hdr *req_hdr; - char signature[SMB2_HMACSHA256_SIZE]; - struct kvec iov[2]; - size_t len; - int n_vec = 1; - - hdr = smb2_get_msg(work->response_buf); - if (work->next_smb2_rsp_hdr_off) - hdr = ksmbd_resp_buf_next(work); - - req_hdr = ksmbd_req_buf_next(work); - - if (!work->next_smb2_rsp_hdr_off) { - len = get_rfc1002_len(work->response_buf); - if (req_hdr->NextCommand) - len = ALIGN(len, 8); - } else { - len = get_rfc1002_len(work->response_buf) - - work->next_smb2_rsp_hdr_off; - len = ALIGN(len, 8); - } - - if (req_hdr->NextCommand) - hdr->NextCommand = cpu_to_le32(len); - - hdr->Flags |= SMB2_FLAGS_SIGNED; - memset(hdr->Signature, 0, SMB2_SIGNATURE_SIZE); - - iov[0].iov_base = (char *)&hdr->ProtocolId; - iov[0].iov_len = len; - - if (work->aux_payload_sz) { - iov[0].iov_len -= work->aux_payload_sz; - - iov[1].iov_base = work->aux_payload_buf; - iov[1].iov_len = work->aux_payload_sz; - n_vec++; - } - - if (!ksmbd_sign_smb2_pdu(work->conn, work->sess->sess_key, iov, n_vec, - signature)) - memcpy(hdr->Signature, signature, SMB2_SIGNATURE_SIZE); -} - -/** - * smb3_check_sign_req() - handler for req packet sign processing - * @work: smb work containing notify command buffer - * - * Return: 1 on success, 0 otherwise - */ -int smb3_check_sign_req(struct ksmbd_work *work) -{ - struct ksmbd_conn *conn = work->conn; - char *signing_key; - struct smb2_hdr *hdr; - struct channel *chann; - char signature_req[SMB2_SIGNATURE_SIZE]; - char signature[SMB2_CMACAES_SIZE]; - struct kvec iov[1]; - size_t len; - - hdr = smb2_get_msg(work->request_buf); - if (work->next_smb2_rcv_hdr_off) - hdr = ksmbd_req_buf_next(work); - - if (!hdr->NextCommand && !work->next_smb2_rcv_hdr_off) - len = get_rfc1002_len(work->request_buf); - else if (hdr->NextCommand) - len = le32_to_cpu(hdr->NextCommand); - else - len = get_rfc1002_len(work->request_buf) - - work->next_smb2_rcv_hdr_off; - - if (le16_to_cpu(hdr->Command) == SMB2_SESSION_SETUP_HE) { - signing_key = work->sess->smb3signingkey; - } else { - chann = lookup_chann_list(work->sess, conn); - if (!chann) { - return 0; - } - signing_key = chann->smb3signingkey; - } - - if (!signing_key) { - pr_err("SMB3 signing key is not generated\n"); - return 0; - } - - memcpy(signature_req, hdr->Signature, SMB2_SIGNATURE_SIZE); - memset(hdr->Signature, 0, SMB2_SIGNATURE_SIZE); - iov[0].iov_base = (char *)&hdr->ProtocolId; - iov[0].iov_len = len; - - if (ksmbd_sign_smb3_pdu(conn, signing_key, iov, 1, signature)) - return 0; - - if (memcmp(signature, signature_req, SMB2_SIGNATURE_SIZE)) { - pr_err("bad smb2 signature\n"); - return 0; - } - - return 1; -} - -/** - * smb3_set_sign_rsp() - handler for rsp packet sign processing - * @work: smb work containing notify command buffer - * - */ -void smb3_set_sign_rsp(struct ksmbd_work *work) -{ - struct ksmbd_conn *conn = work->conn; - struct smb2_hdr *req_hdr, *hdr; - struct channel *chann; - char signature[SMB2_CMACAES_SIZE]; - struct kvec iov[2]; - int n_vec = 1; - size_t len; - char *signing_key; - - hdr = smb2_get_msg(work->response_buf); - if (work->next_smb2_rsp_hdr_off) - hdr = ksmbd_resp_buf_next(work); - - req_hdr = ksmbd_req_buf_next(work); - - if (!work->next_smb2_rsp_hdr_off) { - len = get_rfc1002_len(work->response_buf); - if (req_hdr->NextCommand) - len = ALIGN(len, 8); - } else { - len = get_rfc1002_len(work->response_buf) - - work->next_smb2_rsp_hdr_off; - len = ALIGN(len, 8); - } - - if (conn->binding == false && - le16_to_cpu(hdr->Command) == SMB2_SESSION_SETUP_HE) { - signing_key = work->sess->smb3signingkey; - } else { - chann = lookup_chann_list(work->sess, work->conn); - if (!chann) { - return; - } - signing_key = chann->smb3signingkey; - } - - if (!signing_key) - return; - - if (req_hdr->NextCommand) - hdr->NextCommand = cpu_to_le32(len); - - hdr->Flags |= SMB2_FLAGS_SIGNED; - memset(hdr->Signature, 0, SMB2_SIGNATURE_SIZE); - iov[0].iov_base = (char *)&hdr->ProtocolId; - iov[0].iov_len = len; - if (work->aux_payload_sz) { - iov[0].iov_len -= work->aux_payload_sz; - iov[1].iov_base = work->aux_payload_buf; - iov[1].iov_len = work->aux_payload_sz; - n_vec++; - } - - if (!ksmbd_sign_smb3_pdu(conn, signing_key, iov, n_vec, signature)) - memcpy(hdr->Signature, signature, SMB2_SIGNATURE_SIZE); -} - -/** - * smb3_preauth_hash_rsp() - handler for computing preauth hash on response - * @work: smb work containing response buffer - * - */ -void smb3_preauth_hash_rsp(struct ksmbd_work *work) -{ - struct ksmbd_conn *conn = work->conn; - struct ksmbd_session *sess = work->sess; - struct smb2_hdr *req, *rsp; - - if (conn->dialect != SMB311_PROT_ID) - return; - - WORK_BUFFERS(work, req, rsp); - - if (le16_to_cpu(req->Command) == SMB2_NEGOTIATE_HE && - conn->preauth_info) - ksmbd_gen_preauth_integrity_hash(conn, work->response_buf, - conn->preauth_info->Preauth_HashValue); - - if (le16_to_cpu(rsp->Command) == SMB2_SESSION_SETUP_HE && sess) { - __u8 *hash_value; - - if (conn->binding) { - struct preauth_session *preauth_sess; - - preauth_sess = ksmbd_preauth_session_lookup(conn, sess->id); - if (!preauth_sess) - return; - hash_value = preauth_sess->Preauth_HashValue; - } else { - hash_value = sess->Preauth_HashValue; - if (!hash_value) - return; - } - ksmbd_gen_preauth_integrity_hash(conn, work->response_buf, - hash_value); - } -} - -static void fill_transform_hdr(void *tr_buf, char *old_buf, __le16 cipher_type) -{ - struct smb2_transform_hdr *tr_hdr = tr_buf + 4; - struct smb2_hdr *hdr = smb2_get_msg(old_buf); - unsigned int orig_len = get_rfc1002_len(old_buf); - - /* tr_buf must be cleared by the caller */ - tr_hdr->ProtocolId = SMB2_TRANSFORM_PROTO_NUM; - tr_hdr->OriginalMessageSize = cpu_to_le32(orig_len); - tr_hdr->Flags = cpu_to_le16(TRANSFORM_FLAG_ENCRYPTED); - if (cipher_type == SMB2_ENCRYPTION_AES128_GCM || - cipher_type == SMB2_ENCRYPTION_AES256_GCM) - get_random_bytes(&tr_hdr->Nonce, SMB3_AES_GCM_NONCE); - else - get_random_bytes(&tr_hdr->Nonce, SMB3_AES_CCM_NONCE); - memcpy(&tr_hdr->SessionId, &hdr->SessionId, 8); - inc_rfc1001_len(tr_buf, sizeof(struct smb2_transform_hdr)); - inc_rfc1001_len(tr_buf, orig_len); -} - -int smb3_encrypt_resp(struct ksmbd_work *work) -{ - char *buf = work->response_buf; - struct kvec iov[3]; - int rc = -ENOMEM; - int buf_size = 0, rq_nvec = 2 + (work->aux_payload_sz ? 1 : 0); - - if (ARRAY_SIZE(iov) < rq_nvec) - return -ENOMEM; - - work->tr_buf = kzalloc(sizeof(struct smb2_transform_hdr) + 4, GFP_KERNEL); - if (!work->tr_buf) - return rc; - - /* fill transform header */ - fill_transform_hdr(work->tr_buf, buf, work->conn->cipher_type); - - iov[0].iov_base = work->tr_buf; - iov[0].iov_len = sizeof(struct smb2_transform_hdr) + 4; - buf_size += iov[0].iov_len - 4; - - iov[1].iov_base = buf + 4; - iov[1].iov_len = get_rfc1002_len(buf); - if (work->aux_payload_sz) { - iov[1].iov_len = work->resp_hdr_sz - 4; - - iov[2].iov_base = work->aux_payload_buf; - iov[2].iov_len = work->aux_payload_sz; - buf_size += iov[2].iov_len; - } - buf_size += iov[1].iov_len; - work->resp_hdr_sz = iov[1].iov_len; - - rc = ksmbd_crypt_message(work, iov, rq_nvec, 1); - if (rc) - return rc; - - memmove(buf, iov[1].iov_base, iov[1].iov_len); - *(__be32 *)work->tr_buf = cpu_to_be32(buf_size); - - return rc; -} - -bool smb3_is_transform_hdr(void *buf) -{ - struct smb2_transform_hdr *trhdr = smb2_get_msg(buf); - - return trhdr->ProtocolId == SMB2_TRANSFORM_PROTO_NUM; -} - -int smb3_decrypt_req(struct ksmbd_work *work) -{ - struct ksmbd_session *sess; - char *buf = work->request_buf; - unsigned int pdu_length = get_rfc1002_len(buf); - struct kvec iov[2]; - int buf_data_size = pdu_length - sizeof(struct smb2_transform_hdr); - struct smb2_transform_hdr *tr_hdr = smb2_get_msg(buf); - int rc = 0; - - if (buf_data_size < sizeof(struct smb2_hdr)) { - pr_err("Transform message is too small (%u)\n", - pdu_length); - return -ECONNABORTED; - } - - if (buf_data_size < le32_to_cpu(tr_hdr->OriginalMessageSize)) { - pr_err("Transform message is broken\n"); - return -ECONNABORTED; - } - - sess = ksmbd_session_lookup_all(work->conn, le64_to_cpu(tr_hdr->SessionId)); - if (!sess) { - pr_err("invalid session id(%llx) in transform header\n", - le64_to_cpu(tr_hdr->SessionId)); - return -ECONNABORTED; - } - - iov[0].iov_base = buf; - iov[0].iov_len = sizeof(struct smb2_transform_hdr) + 4; - iov[1].iov_base = buf + sizeof(struct smb2_transform_hdr) + 4; - iov[1].iov_len = buf_data_size; - rc = ksmbd_crypt_message(work, iov, 2, 0); - if (rc) - return rc; - - memmove(buf + 4, iov[1].iov_base, buf_data_size); - *(__be32 *)buf = cpu_to_be32(buf_data_size); - - return rc; -} - -bool smb3_11_final_sess_setup_resp(struct ksmbd_work *work) -{ - struct ksmbd_conn *conn = work->conn; - struct ksmbd_session *sess = work->sess; - struct smb2_hdr *rsp = smb2_get_msg(work->response_buf); - - if (conn->dialect < SMB30_PROT_ID) - return false; - - if (work->next_smb2_rcv_hdr_off) - rsp = ksmbd_resp_buf_next(work); - - if (le16_to_cpu(rsp->Command) == SMB2_SESSION_SETUP_HE && - sess->user && !user_guest(sess->user) && - rsp->Status == STATUS_SUCCESS) - return true; - return false; -} diff --git a/fs/ksmbd/smb2pdu.h b/fs/ksmbd/smb2pdu.h deleted file mode 100644 index dd10f8031606b..0000000000000 --- a/fs/ksmbd/smb2pdu.h +++ /dev/null @@ -1,536 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0-or-later */ -/* - * Copyright (C) 2016 Namjae Jeon - * Copyright (C) 2018 Samsung Electronics Co., Ltd. - */ - -#ifndef _SMB2PDU_H -#define _SMB2PDU_H - -#include "ntlmssp.h" -#include "smbacl.h" - -/*Create Action Flags*/ -#define FILE_SUPERSEDED 0x00000000 -#define FILE_OPENED 0x00000001 -#define FILE_CREATED 0x00000002 -#define FILE_OVERWRITTEN 0x00000003 - -/* SMB2 Max Credits */ -#define SMB2_MAX_CREDITS 8192 - -/* BB FIXME - analyze following length BB */ -#define MAX_SMB2_HDR_SIZE 0x78 /* 4 len + 64 hdr + (2*24 wct) + 2 bct + 2 pad */ - -#define SMB21_DEFAULT_IOSIZE (1024 * 1024) -#define SMB3_DEFAULT_TRANS_SIZE (1024 * 1024) -#define SMB3_MIN_IOSIZE (64 * 1024) -#define SMB3_MAX_IOSIZE (8 * 1024 * 1024) -#define SMB3_MAX_MSGSIZE (4 * 4096) - -/* - * Definitions for SMB2 Protocol Data Units (network frames) - * - * See MS-SMB2.PDF specification for protocol details. - * The Naming convention is the lower case version of the SMB2 - * command code name for the struct. Note that structures must be packed. - * - */ - -struct preauth_integrity_info { - /* PreAuth integrity Hash ID */ - __le16 Preauth_HashId; - /* PreAuth integrity Hash Value */ - __u8 Preauth_HashValue[SMB2_PREAUTH_HASH_SIZE]; -}; - -/* offset is sizeof smb2_negotiate_rsp but rounded up to 8 bytes. */ -#ifdef CONFIG_SMB_SERVER_KERBEROS5 -/* sizeof(struct smb2_negotiate_rsp) = - * header(64) + response(64) + GSS_LENGTH(96) + GSS_PADDING(0) - */ -#define OFFSET_OF_NEG_CONTEXT 0xe0 -#else -/* sizeof(struct smb2_negotiate_rsp) = - * header(64) + response(64) + GSS_LENGTH(74) + GSS_PADDING(6) - */ -#define OFFSET_OF_NEG_CONTEXT 0xd0 -#endif - -#define SMB2_SESSION_EXPIRED (0) -#define SMB2_SESSION_IN_PROGRESS BIT(0) -#define SMB2_SESSION_VALID BIT(1) - -#define SMB2_SESSION_TIMEOUT (10 * HZ) - -struct create_durable_req_v2 { - struct create_context ccontext; - __u8 Name[8]; - __le32 Timeout; - __le32 Flags; - __u8 Reserved[8]; - __u8 CreateGuid[16]; -} __packed; - -struct create_durable_reconn_req { - struct create_context ccontext; - __u8 Name[8]; - union { - __u8 Reserved[16]; - struct { - __u64 PersistentFileId; - __u64 VolatileFileId; - } Fid; - } Data; -} __packed; - -struct create_durable_reconn_v2_req { - struct create_context ccontext; - __u8 Name[8]; - struct { - __u64 PersistentFileId; - __u64 VolatileFileId; - } Fid; - __u8 CreateGuid[16]; - __le32 Flags; -} __packed; - -struct create_app_inst_id { - struct create_context ccontext; - __u8 Name[8]; - __u8 Reserved[8]; - __u8 AppInstanceId[16]; -} __packed; - -struct create_app_inst_id_vers { - struct create_context ccontext; - __u8 Name[8]; - __u8 Reserved[2]; - __u8 Padding[4]; - __le64 AppInstanceVersionHigh; - __le64 AppInstanceVersionLow; -} __packed; - -struct create_mxac_req { - struct create_context ccontext; - __u8 Name[8]; - __le64 Timestamp; -} __packed; - -struct create_alloc_size_req { - struct create_context ccontext; - __u8 Name[8]; - __le64 AllocationSize; -} __packed; - -struct create_durable_rsp { - struct create_context ccontext; - __u8 Name[8]; - union { - __u8 Reserved[8]; - __u64 data; - } Data; -} __packed; - -struct create_durable_v2_rsp { - struct create_context ccontext; - __u8 Name[8]; - __le32 Timeout; - __le32 Flags; -} __packed; - -struct create_mxac_rsp { - struct create_context ccontext; - __u8 Name[8]; - __le32 QueryStatus; - __le32 MaximalAccess; -} __packed; - -struct create_disk_id_rsp { - struct create_context ccontext; - __u8 Name[8]; - __le64 DiskFileId; - __le64 VolumeId; - __u8 Reserved[16]; -} __packed; - -/* equivalent of the contents of SMB3.1.1 POSIX open context response */ -struct create_posix_rsp { - struct create_context ccontext; - __u8 Name[16]; - __le32 nlink; - __le32 reparse_tag; - __le32 mode; - /* SidBuffer contain two sids(Domain sid(28), UNIX group sid(16)) */ - u8 SidBuffer[44]; -} __packed; - -struct smb2_buffer_desc_v1 { - __le64 offset; - __le32 token; - __le32 length; -} __packed; - -#define SMB2_0_IOCTL_IS_FSCTL 0x00000001 - -struct smb_sockaddr_in { - __be16 Port; - __be32 IPv4address; - __u8 Reserved[8]; -} __packed; - -struct smb_sockaddr_in6 { - __be16 Port; - __be32 FlowInfo; - __u8 IPv6address[16]; - __be32 ScopeId; -} __packed; - -#define INTERNETWORK 0x0002 -#define INTERNETWORKV6 0x0017 - -struct sockaddr_storage_rsp { - __le16 Family; - union { - struct smb_sockaddr_in addr4; - struct smb_sockaddr_in6 addr6; - }; -} __packed; - -#define RSS_CAPABLE 0x00000001 -#define RDMA_CAPABLE 0x00000002 - -struct network_interface_info_ioctl_rsp { - __le32 Next; /* next interface. zero if this is last one */ - __le32 IfIndex; - __le32 Capability; /* RSS or RDMA Capable */ - __le32 Reserved; - __le64 LinkSpeed; - char SockAddr_Storage[128]; -} __packed; - -struct file_object_buf_type1_ioctl_rsp { - __u8 ObjectId[16]; - __u8 BirthVolumeId[16]; - __u8 BirthObjectId[16]; - __u8 DomainId[16]; -} __packed; - -struct resume_key_ioctl_rsp { - __u64 ResumeKey[3]; - __le32 ContextLength; - __u8 Context[4]; /* ignored, Windows sets to 4 bytes of zero */ -} __packed; - -struct copychunk_ioctl_req { - __le64 ResumeKey[3]; - __le32 ChunkCount; - __le32 Reserved; - __u8 Chunks[1]; /* array of srv_copychunk */ -} __packed; - -struct srv_copychunk { - __le64 SourceOffset; - __le64 TargetOffset; - __le32 Length; - __le32 Reserved; -} __packed; - -struct copychunk_ioctl_rsp { - __le32 ChunksWritten; - __le32 ChunkBytesWritten; - __le32 TotalBytesWritten; -} __packed; - -struct file_sparse { - __u8 SetSparse; -} __packed; - -/* FILE Info response size */ -#define FILE_DIRECTORY_INFORMATION_SIZE 1 -#define FILE_FULL_DIRECTORY_INFORMATION_SIZE 2 -#define FILE_BOTH_DIRECTORY_INFORMATION_SIZE 3 -#define FILE_BASIC_INFORMATION_SIZE 40 -#define FILE_STANDARD_INFORMATION_SIZE 24 -#define FILE_INTERNAL_INFORMATION_SIZE 8 -#define FILE_EA_INFORMATION_SIZE 4 -#define FILE_ACCESS_INFORMATION_SIZE 4 -#define FILE_NAME_INFORMATION_SIZE 9 -#define FILE_RENAME_INFORMATION_SIZE 10 -#define FILE_LINK_INFORMATION_SIZE 11 -#define FILE_NAMES_INFORMATION_SIZE 12 -#define FILE_DISPOSITION_INFORMATION_SIZE 13 -#define FILE_POSITION_INFORMATION_SIZE 14 -#define FILE_FULL_EA_INFORMATION_SIZE 15 -#define FILE_MODE_INFORMATION_SIZE 4 -#define FILE_ALIGNMENT_INFORMATION_SIZE 4 -#define FILE_ALL_INFORMATION_SIZE 104 -#define FILE_ALLOCATION_INFORMATION_SIZE 19 -#define FILE_END_OF_FILE_INFORMATION_SIZE 20 -#define FILE_ALTERNATE_NAME_INFORMATION_SIZE 8 -#define FILE_STREAM_INFORMATION_SIZE 32 -#define FILE_PIPE_INFORMATION_SIZE 23 -#define FILE_PIPE_LOCAL_INFORMATION_SIZE 24 -#define FILE_PIPE_REMOTE_INFORMATION_SIZE 25 -#define FILE_MAILSLOT_QUERY_INFORMATION_SIZE 26 -#define FILE_MAILSLOT_SET_INFORMATION_SIZE 27 -#define FILE_COMPRESSION_INFORMATION_SIZE 16 -#define FILE_OBJECT_ID_INFORMATION_SIZE 29 -/* Number 30 not defined in documents */ -#define FILE_MOVE_CLUSTER_INFORMATION_SIZE 31 -#define FILE_QUOTA_INFORMATION_SIZE 32 -#define FILE_REPARSE_POINT_INFORMATION_SIZE 33 -#define FILE_NETWORK_OPEN_INFORMATION_SIZE 56 -#define FILE_ATTRIBUTE_TAG_INFORMATION_SIZE 8 - -/* FS Info response size */ -#define FS_DEVICE_INFORMATION_SIZE 8 -#define FS_ATTRIBUTE_INFORMATION_SIZE 16 -#define FS_VOLUME_INFORMATION_SIZE 24 -#define FS_SIZE_INFORMATION_SIZE 24 -#define FS_FULL_SIZE_INFORMATION_SIZE 32 -#define FS_SECTOR_SIZE_INFORMATION_SIZE 28 -#define FS_OBJECT_ID_INFORMATION_SIZE 64 -#define FS_CONTROL_INFORMATION_SIZE 48 -#define FS_POSIX_INFORMATION_SIZE 56 - -/* FS_ATTRIBUTE_File_System_Name */ -#define FS_TYPE_SUPPORT_SIZE 44 -struct fs_type_info { - char *fs_name; - long magic_number; -} __packed; - -/* - * PDU query infolevel structure definitions - * BB consider moving to a different header - */ - -struct smb2_file_access_info { - __le32 AccessFlags; -} __packed; - -struct smb2_file_alignment_info { - __le32 AlignmentRequirement; -} __packed; - -struct smb2_file_basic_info { /* data block encoding of response to level 18 */ - __le64 CreationTime; /* Beginning of FILE_BASIC_INFO equivalent */ - __le64 LastAccessTime; - __le64 LastWriteTime; - __le64 ChangeTime; - __le32 Attributes; - __u32 Pad1; /* End of FILE_BASIC_INFO_INFO equivalent */ -} __packed; - -struct smb2_file_alt_name_info { - __le32 FileNameLength; - char FileName[]; -} __packed; - -struct smb2_file_stream_info { - __le32 NextEntryOffset; - __le32 StreamNameLength; - __le64 StreamSize; - __le64 StreamAllocationSize; - char StreamName[]; -} __packed; - -struct smb2_file_ntwrk_info { - __le64 CreationTime; - __le64 LastAccessTime; - __le64 LastWriteTime; - __le64 ChangeTime; - __le64 AllocationSize; - __le64 EndOfFile; - __le32 Attributes; - __le32 Reserved; -} __packed; - -struct smb2_file_standard_info { - __le64 AllocationSize; - __le64 EndOfFile; - __le32 NumberOfLinks; /* hard links */ - __u8 DeletePending; - __u8 Directory; - __le16 Reserved; -} __packed; /* level 18 Query */ - -struct smb2_file_ea_info { - __le32 EASize; -} __packed; - -struct smb2_file_alloc_info { - __le64 AllocationSize; -} __packed; - -struct smb2_file_disposition_info { - __u8 DeletePending; -} __packed; - -struct smb2_file_pos_info { - __le64 CurrentByteOffset; -} __packed; - -#define FILE_MODE_INFO_MASK cpu_to_le32(0x0000100e) - -struct smb2_file_mode_info { - __le32 Mode; -} __packed; - -#define COMPRESSION_FORMAT_NONE 0x0000 -#define COMPRESSION_FORMAT_LZNT1 0x0002 - -struct smb2_file_comp_info { - __le64 CompressedFileSize; - __le16 CompressionFormat; - __u8 CompressionUnitShift; - __u8 ChunkShift; - __u8 ClusterShift; - __u8 Reserved[3]; -} __packed; - -struct smb2_file_attr_tag_info { - __le32 FileAttributes; - __le32 ReparseTag; -} __packed; - -#define SL_RESTART_SCAN 0x00000001 -#define SL_RETURN_SINGLE_ENTRY 0x00000002 -#define SL_INDEX_SPECIFIED 0x00000004 - -struct smb2_ea_info_req { - __le32 NextEntryOffset; - __u8 EaNameLength; - char name[1]; -} __packed; /* level 15 Query */ - -struct smb2_ea_info { - __le32 NextEntryOffset; - __u8 Flags; - __u8 EaNameLength; - __le16 EaValueLength; - char name[1]; - /* optionally followed by value */ -} __packed; /* level 15 Query */ - -struct create_ea_buf_req { - struct create_context ccontext; - __u8 Name[8]; - struct smb2_ea_info ea; -} __packed; - -struct create_sd_buf_req { - struct create_context ccontext; - __u8 Name[8]; - struct smb_ntsd ntsd; -} __packed; - -struct smb2_posix_info { - __le32 NextEntryOffset; - __u32 Ignored; - __le64 CreationTime; - __le64 LastAccessTime; - __le64 LastWriteTime; - __le64 ChangeTime; - __le64 EndOfFile; - __le64 AllocationSize; - __le32 DosAttributes; - __le64 Inode; - __le32 DeviceId; - __le32 Zero; - /* beginning of POSIX Create Context Response */ - __le32 HardLinks; - __le32 ReparseTag; - __le32 Mode; - /* SidBuffer contain two sids (UNIX user sid(16), UNIX group sid(16)) */ - u8 SidBuffer[32]; - __le32 name_len; - u8 name[1]; - /* - * var sized owner SID - * var sized group SID - * le32 filenamelength - * u8 filename[] - */ -} __packed; - -/* functions */ -void init_smb2_1_server(struct ksmbd_conn *conn); -void init_smb3_0_server(struct ksmbd_conn *conn); -void init_smb3_02_server(struct ksmbd_conn *conn); -int init_smb3_11_server(struct ksmbd_conn *conn); - -void init_smb2_max_read_size(unsigned int sz); -void init_smb2_max_write_size(unsigned int sz); -void init_smb2_max_trans_size(unsigned int sz); -void init_smb2_max_credits(unsigned int sz); - -bool is_smb2_neg_cmd(struct ksmbd_work *work); -bool is_smb2_rsp(struct ksmbd_work *work); - -u16 get_smb2_cmd_val(struct ksmbd_work *work); -void set_smb2_rsp_status(struct ksmbd_work *work, __le32 err); -int init_smb2_rsp_hdr(struct ksmbd_work *work); -int smb2_allocate_rsp_buf(struct ksmbd_work *work); -bool is_chained_smb2_message(struct ksmbd_work *work); -int init_smb2_neg_rsp(struct ksmbd_work *work); -void smb2_set_err_rsp(struct ksmbd_work *work); -int smb2_check_user_session(struct ksmbd_work *work); -int smb2_get_ksmbd_tcon(struct ksmbd_work *work); -bool smb2_is_sign_req(struct ksmbd_work *work, unsigned int command); -int smb2_check_sign_req(struct ksmbd_work *work); -void smb2_set_sign_rsp(struct ksmbd_work *work); -int smb3_check_sign_req(struct ksmbd_work *work); -void smb3_set_sign_rsp(struct ksmbd_work *work); -int find_matching_smb2_dialect(int start_index, __le16 *cli_dialects, - __le16 dialects_count); -struct file_lock *smb_flock_init(struct file *f); -int setup_async_work(struct ksmbd_work *work, void (*fn)(void **), - void **arg); -void smb2_send_interim_resp(struct ksmbd_work *work, __le32 status); -struct channel *lookup_chann_list(struct ksmbd_session *sess, - struct ksmbd_conn *conn); -void smb3_preauth_hash_rsp(struct ksmbd_work *work); -bool smb3_is_transform_hdr(void *buf); -int smb3_decrypt_req(struct ksmbd_work *work); -int smb3_encrypt_resp(struct ksmbd_work *work); -bool smb3_11_final_sess_setup_resp(struct ksmbd_work *work); -int smb2_set_rsp_credits(struct ksmbd_work *work); -bool smb3_encryption_negotiated(struct ksmbd_conn *conn); - -/* smb2 misc functions */ -int ksmbd_smb2_check_message(struct ksmbd_work *work); - -/* smb2 command handlers */ -int smb2_handle_negotiate(struct ksmbd_work *work); -int smb2_negotiate_request(struct ksmbd_work *work); -int smb2_sess_setup(struct ksmbd_work *work); -int smb2_tree_connect(struct ksmbd_work *work); -int smb2_tree_disconnect(struct ksmbd_work *work); -int smb2_session_logoff(struct ksmbd_work *work); -int smb2_open(struct ksmbd_work *work); -int smb2_query_info(struct ksmbd_work *work); -int smb2_query_dir(struct ksmbd_work *work); -int smb2_close(struct ksmbd_work *work); -int smb2_echo(struct ksmbd_work *work); -int smb2_set_info(struct ksmbd_work *work); -int smb2_read(struct ksmbd_work *work); -int smb2_write(struct ksmbd_work *work); -int smb2_flush(struct ksmbd_work *work); -int smb2_cancel(struct ksmbd_work *work); -int smb2_lock(struct ksmbd_work *work); -int smb2_ioctl(struct ksmbd_work *work); -int smb2_oplock_break(struct ksmbd_work *work); -int smb2_notify(struct ksmbd_work *ksmbd_work); - -/* - * Get the body of the smb2 message excluding the 4 byte rfc1002 headers - * from request/response buffer. - */ -static inline void *smb2_get_msg(void *buf) -{ - return buf + 4; -} - -#endif /* _SMB2PDU_H */ diff --git a/fs/ksmbd/smb_common.c b/fs/ksmbd/smb_common.c deleted file mode 100644 index 05d7f3e910bf4..0000000000000 --- a/fs/ksmbd/smb_common.c +++ /dev/null @@ -1,797 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-or-later -/* - * Copyright (C) 2018 Samsung Electronics Co., Ltd. - * Copyright (C) 2018 Namjae Jeon - */ - -#include - -#include "smb_common.h" -#include "server.h" -#include "misc.h" -#include "smbstatus.h" -#include "connection.h" -#include "ksmbd_work.h" -#include "mgmt/user_session.h" -#include "mgmt/user_config.h" -#include "mgmt/tree_connect.h" -#include "mgmt/share_config.h" - -/*for shortname implementation */ -static const char basechars[43] = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ_-!@#$%"; -#define MANGLE_BASE (sizeof(basechars) / sizeof(char) - 1) -#define MAGIC_CHAR '~' -#define PERIOD '.' -#define mangle(V) ((char)(basechars[(V) % MANGLE_BASE])) - -struct smb_protocol { - int index; - char *name; - char *prot; - __u16 prot_id; -}; - -static struct smb_protocol smb1_protos[] = { - { - SMB21_PROT, - "\2SMB 2.1", - "SMB2_10", - SMB21_PROT_ID - }, - { - SMB2X_PROT, - "\2SMB 2.???", - "SMB2_22", - SMB2X_PROT_ID - }, -}; - -static struct smb_protocol smb2_protos[] = { - { - SMB21_PROT, - "\2SMB 2.1", - "SMB2_10", - SMB21_PROT_ID - }, - { - SMB30_PROT, - "\2SMB 3.0", - "SMB3_00", - SMB30_PROT_ID - }, - { - SMB302_PROT, - "\2SMB 3.02", - "SMB3_02", - SMB302_PROT_ID - }, - { - SMB311_PROT, - "\2SMB 3.1.1", - "SMB3_11", - SMB311_PROT_ID - }, -}; - -unsigned int ksmbd_server_side_copy_max_chunk_count(void) -{ - return 256; -} - -unsigned int ksmbd_server_side_copy_max_chunk_size(void) -{ - return (2U << 30) - 1; -} - -unsigned int ksmbd_server_side_copy_max_total_size(void) -{ - return (2U << 30) - 1; -} - -inline int ksmbd_min_protocol(void) -{ - return SMB21_PROT; -} - -inline int ksmbd_max_protocol(void) -{ - return SMB311_PROT; -} - -int ksmbd_lookup_protocol_idx(char *str) -{ - int offt = ARRAY_SIZE(smb1_protos) - 1; - int len = strlen(str); - - while (offt >= 0) { - if (!strncmp(str, smb1_protos[offt].prot, len)) { - ksmbd_debug(SMB, "selected %s dialect idx = %d\n", - smb1_protos[offt].prot, offt); - return smb1_protos[offt].index; - } - offt--; - } - - offt = ARRAY_SIZE(smb2_protos) - 1; - while (offt >= 0) { - if (!strncmp(str, smb2_protos[offt].prot, len)) { - ksmbd_debug(SMB, "selected %s dialect idx = %d\n", - smb2_protos[offt].prot, offt); - return smb2_protos[offt].index; - } - offt--; - } - return -1; -} - -/** - * ksmbd_verify_smb_message() - check for valid smb2 request header - * @work: smb work - * - * check for valid smb signature and packet direction(request/response) - * - * Return: 0 on success, otherwise -EINVAL - */ -int ksmbd_verify_smb_message(struct ksmbd_work *work) -{ - struct smb2_hdr *smb2_hdr = ksmbd_req_buf_next(work); - struct smb_hdr *hdr; - - if (smb2_hdr->ProtocolId == SMB2_PROTO_NUMBER) - return ksmbd_smb2_check_message(work); - - hdr = work->request_buf; - if (*(__le32 *)hdr->Protocol == SMB1_PROTO_NUMBER && - hdr->Command == SMB_COM_NEGOTIATE) { - work->conn->outstanding_credits++; - return 0; - } - - return -EINVAL; -} - -/** - * ksmbd_smb_request() - check for valid smb request type - * @conn: connection instance - * - * Return: true on success, otherwise false - */ -bool ksmbd_smb_request(struct ksmbd_conn *conn) -{ - __le32 *proto = (__le32 *)smb2_get_msg(conn->request_buf); - - if (*proto == SMB2_COMPRESSION_TRANSFORM_ID) { - pr_err_ratelimited("smb2 compression not support yet"); - return false; - } - - if (*proto != SMB1_PROTO_NUMBER && - *proto != SMB2_PROTO_NUMBER && - *proto != SMB2_TRANSFORM_PROTO_NUM) - return false; - - return true; -} - -static bool supported_protocol(int idx) -{ - if (idx == SMB2X_PROT && - (server_conf.min_protocol >= SMB21_PROT || - server_conf.max_protocol <= SMB311_PROT)) - return true; - - return (server_conf.min_protocol <= idx && - idx <= server_conf.max_protocol); -} - -static char *next_dialect(char *dialect, int *next_off, int bcount) -{ - dialect = dialect + *next_off; - *next_off = strnlen(dialect, bcount); - if (dialect[*next_off] != '\0') - return NULL; - return dialect; -} - -static int ksmbd_lookup_dialect_by_name(char *cli_dialects, __le16 byte_count) -{ - int i, seq_num, bcount, next; - char *dialect; - - for (i = ARRAY_SIZE(smb1_protos) - 1; i >= 0; i--) { - seq_num = 0; - next = 0; - dialect = cli_dialects; - bcount = le16_to_cpu(byte_count); - do { - dialect = next_dialect(dialect, &next, bcount); - if (!dialect) - break; - ksmbd_debug(SMB, "client requested dialect %s\n", - dialect); - if (!strcmp(dialect, smb1_protos[i].name)) { - if (supported_protocol(smb1_protos[i].index)) { - ksmbd_debug(SMB, - "selected %s dialect\n", - smb1_protos[i].name); - if (smb1_protos[i].index == SMB1_PROT) - return seq_num; - return smb1_protos[i].prot_id; - } - } - seq_num++; - bcount -= (++next); - } while (bcount > 0); - } - - return BAD_PROT_ID; -} - -int ksmbd_lookup_dialect_by_id(__le16 *cli_dialects, __le16 dialects_count) -{ - int i; - int count; - - for (i = ARRAY_SIZE(smb2_protos) - 1; i >= 0; i--) { - count = le16_to_cpu(dialects_count); - while (--count >= 0) { - ksmbd_debug(SMB, "client requested dialect 0x%x\n", - le16_to_cpu(cli_dialects[count])); - if (le16_to_cpu(cli_dialects[count]) != - smb2_protos[i].prot_id) - continue; - - if (supported_protocol(smb2_protos[i].index)) { - ksmbd_debug(SMB, "selected %s dialect\n", - smb2_protos[i].name); - return smb2_protos[i].prot_id; - } - } - } - - return BAD_PROT_ID; -} - -static int ksmbd_negotiate_smb_dialect(void *buf) -{ - int smb_buf_length = get_rfc1002_len(buf); - __le32 proto = ((struct smb2_hdr *)smb2_get_msg(buf))->ProtocolId; - - if (proto == SMB2_PROTO_NUMBER) { - struct smb2_negotiate_req *req; - int smb2_neg_size = - offsetof(struct smb2_negotiate_req, Dialects); - - req = (struct smb2_negotiate_req *)smb2_get_msg(buf); - if (smb2_neg_size > smb_buf_length) - goto err_out; - - if (smb2_neg_size + le16_to_cpu(req->DialectCount) * sizeof(__le16) > - smb_buf_length) - goto err_out; - - return ksmbd_lookup_dialect_by_id(req->Dialects, - req->DialectCount); - } - - proto = *(__le32 *)((struct smb_hdr *)buf)->Protocol; - if (proto == SMB1_PROTO_NUMBER) { - struct smb_negotiate_req *req; - - req = (struct smb_negotiate_req *)buf; - if (le16_to_cpu(req->ByteCount) < 2) - goto err_out; - - if (offsetof(struct smb_negotiate_req, DialectsArray) - 4 + - le16_to_cpu(req->ByteCount) > smb_buf_length) { - goto err_out; - } - - return ksmbd_lookup_dialect_by_name(req->DialectsArray, - req->ByteCount); - } - -err_out: - return BAD_PROT_ID; -} - -#define SMB_COM_NEGOTIATE_EX 0x0 - -/** - * get_smb1_cmd_val() - get smb command value from smb header - * @work: smb work containing smb header - * - * Return: smb command value - */ -static u16 get_smb1_cmd_val(struct ksmbd_work *work) -{ - return SMB_COM_NEGOTIATE_EX; -} - -/** - * init_smb1_rsp_hdr() - initialize smb negotiate response header - * @work: smb work containing smb request - * - * Return: 0 on success, otherwise -EINVAL - */ -static int init_smb1_rsp_hdr(struct ksmbd_work *work) -{ - struct smb_hdr *rsp_hdr = (struct smb_hdr *)work->response_buf; - struct smb_hdr *rcv_hdr = (struct smb_hdr *)work->request_buf; - - /* - * Remove 4 byte direct TCP header. - */ - *(__be32 *)work->response_buf = - cpu_to_be32(sizeof(struct smb_hdr) - 4); - - rsp_hdr->Command = SMB_COM_NEGOTIATE; - *(__le32 *)rsp_hdr->Protocol = SMB1_PROTO_NUMBER; - rsp_hdr->Flags = SMBFLG_RESPONSE; - rsp_hdr->Flags2 = SMBFLG2_UNICODE | SMBFLG2_ERR_STATUS | - SMBFLG2_EXT_SEC | SMBFLG2_IS_LONG_NAME; - rsp_hdr->Pid = rcv_hdr->Pid; - rsp_hdr->Mid = rcv_hdr->Mid; - return 0; -} - -/** - * smb1_check_user_session() - check for valid session for a user - * @work: smb work containing smb request buffer - * - * Return: 0 on success, otherwise error - */ -static int smb1_check_user_session(struct ksmbd_work *work) -{ - unsigned int cmd = work->conn->ops->get_cmd_val(work); - - if (cmd == SMB_COM_NEGOTIATE_EX) - return 0; - - return -EINVAL; -} - -/** - * smb1_allocate_rsp_buf() - allocate response buffer for a command - * @work: smb work containing smb request - * - * Return: 0 on success, otherwise -ENOMEM - */ -static int smb1_allocate_rsp_buf(struct ksmbd_work *work) -{ - work->response_buf = kmalloc(MAX_CIFS_SMALL_BUFFER_SIZE, - GFP_KERNEL | __GFP_ZERO); - work->response_sz = MAX_CIFS_SMALL_BUFFER_SIZE; - - if (!work->response_buf) { - pr_err("Failed to allocate %u bytes buffer\n", - MAX_CIFS_SMALL_BUFFER_SIZE); - return -ENOMEM; - } - - return 0; -} - -static struct smb_version_ops smb1_server_ops = { - .get_cmd_val = get_smb1_cmd_val, - .init_rsp_hdr = init_smb1_rsp_hdr, - .allocate_rsp_buf = smb1_allocate_rsp_buf, - .check_user_session = smb1_check_user_session, -}; - -static int smb1_negotiate(struct ksmbd_work *work) -{ - return ksmbd_smb_negotiate_common(work, SMB_COM_NEGOTIATE); -} - -static struct smb_version_cmds smb1_server_cmds[1] = { - [SMB_COM_NEGOTIATE_EX] = { .proc = smb1_negotiate, }, -}; - -static void init_smb1_server(struct ksmbd_conn *conn) -{ - conn->ops = &smb1_server_ops; - conn->cmds = smb1_server_cmds; - conn->max_cmds = ARRAY_SIZE(smb1_server_cmds); -} - -void ksmbd_init_smb_server(struct ksmbd_work *work) -{ - struct ksmbd_conn *conn = work->conn; - __le32 proto; - - if (conn->need_neg == false) - return; - - proto = *(__le32 *)((struct smb_hdr *)work->request_buf)->Protocol; - if (proto == SMB1_PROTO_NUMBER) - init_smb1_server(conn); - else - init_smb3_11_server(conn); -} - -int ksmbd_populate_dot_dotdot_entries(struct ksmbd_work *work, int info_level, - struct ksmbd_file *dir, - struct ksmbd_dir_info *d_info, - char *search_pattern, - int (*fn)(struct ksmbd_conn *, int, - struct ksmbd_dir_info *, - struct ksmbd_kstat *)) -{ - int i, rc = 0; - struct ksmbd_conn *conn = work->conn; - struct user_namespace *user_ns = file_mnt_user_ns(dir->filp); - - for (i = 0; i < 2; i++) { - struct kstat kstat; - struct ksmbd_kstat ksmbd_kstat; - struct dentry *dentry; - - if (!dir->dot_dotdot[i]) { /* fill dot entry info */ - if (i == 0) { - d_info->name = "."; - d_info->name_len = 1; - dentry = dir->filp->f_path.dentry; - } else { - d_info->name = ".."; - d_info->name_len = 2; - dentry = dir->filp->f_path.dentry->d_parent; - } - - if (!match_pattern(d_info->name, d_info->name_len, - search_pattern)) { - dir->dot_dotdot[i] = 1; - continue; - } - - ksmbd_kstat.kstat = &kstat; - ksmbd_vfs_fill_dentry_attrs(work, - user_ns, - dentry, - &ksmbd_kstat); - rc = fn(conn, info_level, d_info, &ksmbd_kstat); - if (rc) - break; - if (d_info->out_buf_len <= 0) - break; - - dir->dot_dotdot[i] = 1; - if (d_info->flags & SMB2_RETURN_SINGLE_ENTRY) { - d_info->out_buf_len = 0; - break; - } - } - } - - return rc; -} - -/** - * ksmbd_extract_shortname() - get shortname from long filename - * @conn: connection instance - * @longname: source long filename - * @shortname: destination short filename - * - * Return: shortname length or 0 when source long name is '.' or '..' - * TODO: Though this function comforms the restriction of 8.3 Filename spec, - * but the result is different with Windows 7's one. need to check. - */ -int ksmbd_extract_shortname(struct ksmbd_conn *conn, const char *longname, - char *shortname) -{ - const char *p; - char base[9], extension[4]; - char out[13] = {0}; - int baselen = 0; - int extlen = 0, len = 0; - unsigned int csum = 0; - const unsigned char *ptr; - bool dot_present = true; - - p = longname; - if ((*p == '.') || (!(strcmp(p, "..")))) { - /*no mangling required */ - return 0; - } - - p = strrchr(longname, '.'); - if (p == longname) { /*name starts with a dot*/ - strscpy(extension, "___", strlen("___")); - } else { - if (p) { - p++; - while (*p && extlen < 3) { - if (*p != '.') - extension[extlen++] = toupper(*p); - p++; - } - extension[extlen] = '\0'; - } else { - dot_present = false; - } - } - - p = longname; - if (*p == '.') { - p++; - longname++; - } - while (*p && (baselen < 5)) { - if (*p != '.') - base[baselen++] = toupper(*p); - p++; - } - - base[baselen] = MAGIC_CHAR; - memcpy(out, base, baselen + 1); - - ptr = longname; - len = strlen(longname); - for (; len > 0; len--, ptr++) - csum += *ptr; - - csum = csum % (MANGLE_BASE * MANGLE_BASE); - out[baselen + 1] = mangle(csum / MANGLE_BASE); - out[baselen + 2] = mangle(csum); - out[baselen + 3] = PERIOD; - - if (dot_present) - memcpy(&out[baselen + 4], extension, 4); - else - out[baselen + 4] = '\0'; - smbConvertToUTF16((__le16 *)shortname, out, PATH_MAX, - conn->local_nls, 0); - len = strlen(out) * 2; - return len; -} - -static int __smb2_negotiate(struct ksmbd_conn *conn) -{ - return (conn->dialect >= SMB20_PROT_ID && - conn->dialect <= SMB311_PROT_ID); -} - -static int smb_handle_negotiate(struct ksmbd_work *work) -{ - struct smb_negotiate_rsp *neg_rsp = work->response_buf; - - ksmbd_debug(SMB, "Unsupported SMB1 protocol\n"); - - /* Add 2 byte bcc and 2 byte DialectIndex. */ - inc_rfc1001_len(work->response_buf, 4); - neg_rsp->hdr.Status.CifsError = STATUS_SUCCESS; - - neg_rsp->hdr.WordCount = 1; - neg_rsp->DialectIndex = cpu_to_le16(work->conn->dialect); - neg_rsp->ByteCount = 0; - return 0; -} - -int ksmbd_smb_negotiate_common(struct ksmbd_work *work, unsigned int command) -{ - struct ksmbd_conn *conn = work->conn; - int ret; - - conn->dialect = - ksmbd_negotiate_smb_dialect(work->request_buf); - ksmbd_debug(SMB, "conn->dialect 0x%x\n", conn->dialect); - - if (command == SMB2_NEGOTIATE_HE) { - ret = smb2_handle_negotiate(work); - return ret; - } - - if (command == SMB_COM_NEGOTIATE) { - if (__smb2_negotiate(conn)) { - init_smb3_11_server(conn); - init_smb2_neg_rsp(work); - ksmbd_debug(SMB, "Upgrade to SMB2 negotiation\n"); - return 0; - } - return smb_handle_negotiate(work); - } - - pr_err("Unknown SMB negotiation command: %u\n", command); - return -EINVAL; -} - -enum SHARED_MODE_ERRORS { - SHARE_DELETE_ERROR, - SHARE_READ_ERROR, - SHARE_WRITE_ERROR, - FILE_READ_ERROR, - FILE_WRITE_ERROR, - FILE_DELETE_ERROR, -}; - -static const char * const shared_mode_errors[] = { - "Current access mode does not permit SHARE_DELETE", - "Current access mode does not permit SHARE_READ", - "Current access mode does not permit SHARE_WRITE", - "Desired access mode does not permit FILE_READ", - "Desired access mode does not permit FILE_WRITE", - "Desired access mode does not permit FILE_DELETE", -}; - -static void smb_shared_mode_error(int error, struct ksmbd_file *prev_fp, - struct ksmbd_file *curr_fp) -{ - ksmbd_debug(SMB, "%s\n", shared_mode_errors[error]); - ksmbd_debug(SMB, "Current mode: 0x%x Desired mode: 0x%x\n", - prev_fp->saccess, curr_fp->daccess); -} - -int ksmbd_smb_check_shared_mode(struct file *filp, struct ksmbd_file *curr_fp) -{ - int rc = 0; - struct ksmbd_file *prev_fp; - - /* - * Lookup fp in master fp list, and check desired access and - * shared mode between previous open and current open. - */ - read_lock(&curr_fp->f_ci->m_lock); - list_for_each_entry(prev_fp, &curr_fp->f_ci->m_fp_list, node) { - if (file_inode(filp) != file_inode(prev_fp->filp)) - continue; - - if (filp == prev_fp->filp) - continue; - - if (ksmbd_stream_fd(prev_fp) && ksmbd_stream_fd(curr_fp)) - if (strcmp(prev_fp->stream.name, curr_fp->stream.name)) - continue; - - if (prev_fp->attrib_only != curr_fp->attrib_only) - continue; - - if (!(prev_fp->saccess & FILE_SHARE_DELETE_LE) && - curr_fp->daccess & FILE_DELETE_LE) { - smb_shared_mode_error(SHARE_DELETE_ERROR, - prev_fp, - curr_fp); - rc = -EPERM; - break; - } - - /* - * Only check FILE_SHARE_DELETE if stream opened and - * normal file opened. - */ - if (ksmbd_stream_fd(prev_fp) && !ksmbd_stream_fd(curr_fp)) - continue; - - if (!(prev_fp->saccess & FILE_SHARE_READ_LE) && - curr_fp->daccess & (FILE_EXECUTE_LE | FILE_READ_DATA_LE)) { - smb_shared_mode_error(SHARE_READ_ERROR, - prev_fp, - curr_fp); - rc = -EPERM; - break; - } - - if (!(prev_fp->saccess & FILE_SHARE_WRITE_LE) && - curr_fp->daccess & (FILE_WRITE_DATA_LE | FILE_APPEND_DATA_LE)) { - smb_shared_mode_error(SHARE_WRITE_ERROR, - prev_fp, - curr_fp); - rc = -EPERM; - break; - } - - if (prev_fp->daccess & (FILE_EXECUTE_LE | FILE_READ_DATA_LE) && - !(curr_fp->saccess & FILE_SHARE_READ_LE)) { - smb_shared_mode_error(FILE_READ_ERROR, - prev_fp, - curr_fp); - rc = -EPERM; - break; - } - - if (prev_fp->daccess & (FILE_WRITE_DATA_LE | FILE_APPEND_DATA_LE) && - !(curr_fp->saccess & FILE_SHARE_WRITE_LE)) { - smb_shared_mode_error(FILE_WRITE_ERROR, - prev_fp, - curr_fp); - rc = -EPERM; - break; - } - - if (prev_fp->daccess & FILE_DELETE_LE && - !(curr_fp->saccess & FILE_SHARE_DELETE_LE)) { - smb_shared_mode_error(FILE_DELETE_ERROR, - prev_fp, - curr_fp); - rc = -EPERM; - break; - } - } - read_unlock(&curr_fp->f_ci->m_lock); - - return rc; -} - -bool is_asterisk(char *p) -{ - return p && p[0] == '*'; -} - -int ksmbd_override_fsids(struct ksmbd_work *work) -{ - struct ksmbd_session *sess = work->sess; - struct ksmbd_share_config *share = work->tcon->share_conf; - struct cred *cred; - struct group_info *gi; - unsigned int uid; - unsigned int gid; - - uid = user_uid(sess->user); - gid = user_gid(sess->user); - if (share->force_uid != KSMBD_SHARE_INVALID_UID) - uid = share->force_uid; - if (share->force_gid != KSMBD_SHARE_INVALID_GID) - gid = share->force_gid; - - cred = prepare_kernel_cred(NULL); - if (!cred) - return -ENOMEM; - - cred->fsuid = make_kuid(&init_user_ns, uid); - cred->fsgid = make_kgid(&init_user_ns, gid); - - gi = groups_alloc(0); - if (!gi) { - abort_creds(cred); - return -ENOMEM; - } - set_groups(cred, gi); - put_group_info(gi); - - if (!uid_eq(cred->fsuid, GLOBAL_ROOT_UID)) - cred->cap_effective = cap_drop_fs_set(cred->cap_effective); - - WARN_ON(work->saved_cred); - work->saved_cred = override_creds(cred); - if (!work->saved_cred) { - abort_creds(cred); - return -EINVAL; - } - return 0; -} - -void ksmbd_revert_fsids(struct ksmbd_work *work) -{ - const struct cred *cred; - - WARN_ON(!work->saved_cred); - - cred = current_cred(); - revert_creds(work->saved_cred); - put_cred(cred); - work->saved_cred = NULL; -} - -__le32 smb_map_generic_desired_access(__le32 daccess) -{ - if (daccess & FILE_GENERIC_READ_LE) { - daccess |= cpu_to_le32(GENERIC_READ_FLAGS); - daccess &= ~FILE_GENERIC_READ_LE; - } - - if (daccess & FILE_GENERIC_WRITE_LE) { - daccess |= cpu_to_le32(GENERIC_WRITE_FLAGS); - daccess &= ~FILE_GENERIC_WRITE_LE; - } - - if (daccess & FILE_GENERIC_EXECUTE_LE) { - daccess |= cpu_to_le32(GENERIC_EXECUTE_FLAGS); - daccess &= ~FILE_GENERIC_EXECUTE_LE; - } - - if (daccess & FILE_GENERIC_ALL_LE) { - daccess |= cpu_to_le32(GENERIC_ALL_FLAGS); - daccess &= ~FILE_GENERIC_ALL_LE; - } - - return daccess; -} diff --git a/fs/ksmbd/smb_common.h b/fs/ksmbd/smb_common.h deleted file mode 100644 index 78c44978a906a..0000000000000 --- a/fs/ksmbd/smb_common.h +++ /dev/null @@ -1,468 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0-or-later */ -/* - * Copyright (C) 2018 Samsung Electronics Co., Ltd. - */ - -#ifndef __SMB_COMMON_H__ -#define __SMB_COMMON_H__ - -#include - -#include "glob.h" -#include "nterr.h" -#include "../smbfs_common/smb2pdu.h" -#include "smb2pdu.h" - -/* ksmbd's Specific ERRNO */ -#define ESHARE 50000 - -#define SMB1_PROT 0 -#define SMB2_PROT 1 -#define SMB21_PROT 2 -/* multi-protocol negotiate request */ -#define SMB2X_PROT 3 -#define SMB30_PROT 4 -#define SMB302_PROT 5 -#define SMB311_PROT 6 -#define BAD_PROT 0xFFFF - -#define SMB1_VERSION_STRING "1.0" -#define SMB20_VERSION_STRING "2.0" -#define SMB21_VERSION_STRING "2.1" -#define SMB30_VERSION_STRING "3.0" -#define SMB302_VERSION_STRING "3.02" -#define SMB311_VERSION_STRING "3.1.1" - -#define SMB_ECHO_INTERVAL (60 * HZ) - -#define CIFS_DEFAULT_IOSIZE (64 * 1024) -#define MAX_CIFS_SMALL_BUFFER_SIZE 448 /* big enough for most */ - -#define MAX_STREAM_PROT_LEN 0x00FFFFFF - -/* Responses when opening a file. */ -#define F_SUPERSEDED 0 -#define F_OPENED 1 -#define F_CREATED 2 -#define F_OVERWRITTEN 3 - -/* - * File Attribute flags - */ -#define ATTR_POSIX_SEMANTICS 0x01000000 -#define ATTR_BACKUP_SEMANTICS 0x02000000 -#define ATTR_DELETE_ON_CLOSE 0x04000000 -#define ATTR_SEQUENTIAL_SCAN 0x08000000 -#define ATTR_RANDOM_ACCESS 0x10000000 -#define ATTR_NO_BUFFERING 0x20000000 -#define ATTR_WRITE_THROUGH 0x80000000 - -/* List of FileSystemAttributes - see 2.5.1 of MS-FSCC */ -#define FILE_SUPPORTS_SPARSE_VDL 0x10000000 /* faster nonsparse extend */ -#define FILE_SUPPORTS_BLOCK_REFCOUNTING 0x08000000 /* allow ioctl dup extents */ -#define FILE_SUPPORT_INTEGRITY_STREAMS 0x04000000 -#define FILE_SUPPORTS_USN_JOURNAL 0x02000000 -#define FILE_SUPPORTS_OPEN_BY_FILE_ID 0x01000000 -#define FILE_SUPPORTS_EXTENDED_ATTRIBUTES 0x00800000 -#define FILE_SUPPORTS_HARD_LINKS 0x00400000 -#define FILE_SUPPORTS_TRANSACTIONS 0x00200000 -#define FILE_SEQUENTIAL_WRITE_ONCE 0x00100000 -#define FILE_READ_ONLY_VOLUME 0x00080000 -#define FILE_NAMED_STREAMS 0x00040000 -#define FILE_SUPPORTS_ENCRYPTION 0x00020000 -#define FILE_SUPPORTS_OBJECT_IDS 0x00010000 -#define FILE_VOLUME_IS_COMPRESSED 0x00008000 -#define FILE_SUPPORTS_REMOTE_STORAGE 0x00000100 -#define FILE_SUPPORTS_REPARSE_POINTS 0x00000080 -#define FILE_SUPPORTS_SPARSE_FILES 0x00000040 -#define FILE_VOLUME_QUOTAS 0x00000020 -#define FILE_FILE_COMPRESSION 0x00000010 -#define FILE_PERSISTENT_ACLS 0x00000008 -#define FILE_UNICODE_ON_DISK 0x00000004 -#define FILE_CASE_PRESERVED_NAMES 0x00000002 -#define FILE_CASE_SENSITIVE_SEARCH 0x00000001 - -#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 GENERIC_ALL 0x10000000 -#define GENERIC_EXECUTE 0x20000000 -#define GENERIC_WRITE 0x40000000 -#define GENERIC_READ 0x80000000 -/* In summary - Relevant file */ -/* access flags from CIFS are */ -/* file_read_data, file_write_data */ -/* file_execute, file_read_attributes*/ -/* write_dac, and delete. */ - -#define SET_FILE_READ_RIGHTS (FILE_READ_DATA | FILE_READ_EA \ - | FILE_READ_ATTRIBUTES \ - | DELETE | READ_CONTROL | WRITE_DAC \ - | WRITE_OWNER | SYNCHRONIZE) -#define SET_FILE_WRITE_RIGHTS (FILE_WRITE_DATA | FILE_APPEND_DATA \ - | FILE_WRITE_EA \ - | FILE_DELETE_CHILD \ - | FILE_WRITE_ATTRIBUTES \ - | DELETE | READ_CONTROL | WRITE_DAC \ - | WRITE_OWNER | SYNCHRONIZE) -#define SET_FILE_EXEC_RIGHTS (FILE_READ_EA | FILE_WRITE_EA | FILE_EXECUTE \ - | FILE_READ_ATTRIBUTES \ - | FILE_WRITE_ATTRIBUTES \ - | DELETE | READ_CONTROL | WRITE_DAC \ - | WRITE_OWNER | SYNCHRONIZE) - -#define SET_MINIMUM_RIGHTS (FILE_READ_EA | FILE_READ_ATTRIBUTES \ - | READ_CONTROL | SYNCHRONIZE) - -/* generic flags for file open */ -#define GENERIC_READ_FLAGS (READ_CONTROL | FILE_READ_DATA | \ - FILE_READ_ATTRIBUTES | \ - FILE_READ_EA | SYNCHRONIZE) - -#define GENERIC_WRITE_FLAGS (READ_CONTROL | FILE_WRITE_DATA | \ - FILE_WRITE_ATTRIBUTES | FILE_WRITE_EA | \ - FILE_APPEND_DATA | SYNCHRONIZE) - -#define GENERIC_EXECUTE_FLAGS (READ_CONTROL | FILE_EXECUTE | \ - FILE_READ_ATTRIBUTES | SYNCHRONIZE) - -#define GENERIC_ALL_FLAGS (DELETE | READ_CONTROL | WRITE_DAC | \ - WRITE_OWNER | SYNCHRONIZE | FILE_READ_DATA | \ - FILE_WRITE_DATA | FILE_APPEND_DATA | \ - FILE_READ_EA | FILE_WRITE_EA | \ - FILE_EXECUTE | FILE_DELETE_CHILD | \ - FILE_READ_ATTRIBUTES | FILE_WRITE_ATTRIBUTES) - -#define SMB1_PROTO_NUMBER cpu_to_le32(0x424d53ff) -#define SMB_COM_NEGOTIATE 0x72 -#define SMB1_CLIENT_GUID_SIZE (16) - -#define SMBFLG_RESPONSE 0x80 /* this PDU is a response from server */ - -#define SMBFLG2_IS_LONG_NAME cpu_to_le16(0x40) -#define SMBFLG2_EXT_SEC cpu_to_le16(0x800) -#define SMBFLG2_ERR_STATUS cpu_to_le16(0x4000) -#define SMBFLG2_UNICODE cpu_to_le16(0x8000) - -struct smb_hdr { - __be32 smb_buf_length; - __u8 Protocol[4]; - __u8 Command; - union { - struct { - __u8 ErrorClass; - __u8 Reserved; - __le16 Error; - } __packed DosError; - __le32 CifsError; - } __packed Status; - __u8 Flags; - __le16 Flags2; /* note: le */ - __le16 PidHigh; - union { - struct { - __le32 SequenceNumber; /* le */ - __u32 Reserved; /* zero */ - } __packed Sequence; - __u8 SecuritySignature[8]; /* le */ - } __packed Signature; - __u8 pad[2]; - __le16 Tid; - __le16 Pid; - __le16 Uid; - __le16 Mid; - __u8 WordCount; -} __packed; - -struct smb_negotiate_req { - struct smb_hdr hdr; /* wct = 0 */ - __le16 ByteCount; - unsigned char DialectsArray[1]; -} __packed; - -struct smb_negotiate_rsp { - struct smb_hdr hdr; /* wct = 17 */ - __le16 DialectIndex; /* 0xFFFF = no dialect acceptable */ - __le16 ByteCount; -} __packed; - -struct filesystem_attribute_info { - __le32 Attributes; - __le32 MaxPathNameComponentLength; - __le32 FileSystemNameLen; - __le16 FileSystemName[1]; /* do not have to save this - get subset? */ -} __packed; - -struct filesystem_device_info { - __le32 DeviceType; - __le32 DeviceCharacteristics; -} __packed; /* device info level 0x104 */ - -struct filesystem_vol_info { - __le64 VolumeCreationTime; - __le32 SerialNumber; - __le32 VolumeLabelSize; - __le16 Reserved; - __le16 VolumeLabel[1]; -} __packed; - -struct filesystem_info { - __le64 TotalAllocationUnits; - __le64 FreeAllocationUnits; - __le32 SectorsPerAllocationUnit; - __le32 BytesPerSector; -} __packed; /* size info, level 0x103 */ - -#define EXTENDED_INFO_MAGIC 0x43667364 /* Cfsd */ -#define STRING_LENGTH 28 - -struct fs_extended_info { - __le32 magic; - __le32 version; - __le32 release; - __u64 rel_date; - char version_string[STRING_LENGTH]; -} __packed; - -struct object_id_info { - char objid[16]; - struct fs_extended_info extended_info; -} __packed; - -struct file_directory_info { - __le32 NextEntryOffset; - __u32 FileIndex; - __le64 CreationTime; - __le64 LastAccessTime; - __le64 LastWriteTime; - __le64 ChangeTime; - __le64 EndOfFile; - __le64 AllocationSize; - __le32 ExtFileAttributes; - __le32 FileNameLength; - char FileName[1]; -} __packed; /* level 0x101 FF resp data */ - -struct file_names_info { - __le32 NextEntryOffset; - __u32 FileIndex; - __le32 FileNameLength; - char FileName[1]; -} __packed; /* level 0xc FF resp data */ - -struct file_full_directory_info { - __le32 NextEntryOffset; - __u32 FileIndex; - __le64 CreationTime; - __le64 LastAccessTime; - __le64 LastWriteTime; - __le64 ChangeTime; - __le64 EndOfFile; - __le64 AllocationSize; - __le32 ExtFileAttributes; - __le32 FileNameLength; - __le32 EaSize; - char FileName[1]; -} __packed; /* level 0x102 FF resp */ - -struct file_both_directory_info { - __le32 NextEntryOffset; - __u32 FileIndex; - __le64 CreationTime; - __le64 LastAccessTime; - __le64 LastWriteTime; - __le64 ChangeTime; - __le64 EndOfFile; - __le64 AllocationSize; - __le32 ExtFileAttributes; - __le32 FileNameLength; - __le32 EaSize; /* length of the xattrs */ - __u8 ShortNameLength; - __u8 Reserved; - __u8 ShortName[24]; - char FileName[1]; -} __packed; /* level 0x104 FFrsp data */ - -struct file_id_both_directory_info { - __le32 NextEntryOffset; - __u32 FileIndex; - __le64 CreationTime; - __le64 LastAccessTime; - __le64 LastWriteTime; - __le64 ChangeTime; - __le64 EndOfFile; - __le64 AllocationSize; - __le32 ExtFileAttributes; - __le32 FileNameLength; - __le32 EaSize; /* length of the xattrs */ - __u8 ShortNameLength; - __u8 Reserved; - __u8 ShortName[24]; - __le16 Reserved2; - __le64 UniqueId; - char FileName[1]; -} __packed; - -struct file_id_full_dir_info { - __le32 NextEntryOffset; - __u32 FileIndex; - __le64 CreationTime; - __le64 LastAccessTime; - __le64 LastWriteTime; - __le64 ChangeTime; - __le64 EndOfFile; - __le64 AllocationSize; - __le32 ExtFileAttributes; - __le32 FileNameLength; - __le32 EaSize; /* EA size */ - __le32 Reserved; - __le64 UniqueId; /* inode num - le since Samba puts ino in low 32 bit*/ - char FileName[1]; -} __packed; /* level 0x105 FF rsp data */ - -struct smb_version_values { - char *version_string; - __u16 protocol_id; - __le16 lock_cmd; - __u32 capabilities; - __u32 max_read_size; - __u32 max_write_size; - __u32 max_trans_size; - __u32 max_credits; - __u32 large_lock_type; - __u32 exclusive_lock_type; - __u32 shared_lock_type; - __u32 unlock_lock_type; - size_t header_size; - size_t max_header_size; - size_t read_rsp_size; - unsigned int cap_unix; - unsigned int cap_nt_find; - unsigned int cap_large_files; - __u16 signing_enabled; - __u16 signing_required; - size_t create_lease_size; - size_t create_durable_size; - size_t create_durable_v2_size; - size_t create_mxac_size; - size_t create_disk_id_size; - size_t create_posix_size; -}; - -struct filesystem_posix_info { - /* For undefined recommended transfer size return -1 in that field */ - __le32 OptimalTransferSize; /* bsize on some os, iosize on other os */ - __le32 BlockSize; - /* The next three fields are in terms of the block size. - * (above). If block size is unknown, 4096 would be a - * reasonable block size for a server to report. - * Note that returning the blocks/blocksavail removes need - * to make a second call (to QFSInfo level 0x103 to get this info. - * UserBlockAvail is typically less than or equal to BlocksAvail, - * if no distinction is made return the same value in each - */ - __le64 TotalBlocks; - __le64 BlocksAvail; /* bfree */ - __le64 UserBlocksAvail; /* bavail */ - /* For undefined Node fields or FSID return -1 */ - __le64 TotalFileNodes; - __le64 FreeFileNodes; - __le64 FileSysIdentifier; /* fsid */ - /* NB Namelen comes from FILE_SYSTEM_ATTRIBUTE_INFO call */ - /* NB flags can come from FILE_SYSTEM_DEVICE_INFO call */ -} __packed; - -struct smb_version_ops { - u16 (*get_cmd_val)(struct ksmbd_work *swork); - int (*init_rsp_hdr)(struct ksmbd_work *swork); - void (*set_rsp_status)(struct ksmbd_work *swork, __le32 err); - int (*allocate_rsp_buf)(struct ksmbd_work *work); - int (*set_rsp_credits)(struct ksmbd_work *work); - int (*check_user_session)(struct ksmbd_work *work); - int (*get_ksmbd_tcon)(struct ksmbd_work *work); - bool (*is_sign_req)(struct ksmbd_work *work, unsigned int command); - int (*check_sign_req)(struct ksmbd_work *work); - void (*set_sign_rsp)(struct ksmbd_work *work); - int (*generate_signingkey)(struct ksmbd_session *sess, struct ksmbd_conn *conn); - int (*generate_encryptionkey)(struct ksmbd_conn *conn, struct ksmbd_session *sess); - bool (*is_transform_hdr)(void *buf); - int (*decrypt_req)(struct ksmbd_work *work); - int (*encrypt_resp)(struct ksmbd_work *work); -}; - -struct smb_version_cmds { - int (*proc)(struct ksmbd_work *swork); -}; - -int ksmbd_min_protocol(void); -int ksmbd_max_protocol(void); - -int ksmbd_lookup_protocol_idx(char *str); - -int ksmbd_verify_smb_message(struct ksmbd_work *work); -bool ksmbd_smb_request(struct ksmbd_conn *conn); - -int ksmbd_lookup_dialect_by_id(__le16 *cli_dialects, __le16 dialects_count); - -void ksmbd_init_smb_server(struct ksmbd_work *work); - -struct ksmbd_kstat; -int ksmbd_populate_dot_dotdot_entries(struct ksmbd_work *work, - int info_level, - struct ksmbd_file *dir, - struct ksmbd_dir_info *d_info, - char *search_pattern, - int (*fn)(struct ksmbd_conn *, - int, - struct ksmbd_dir_info *, - struct ksmbd_kstat *)); - -int ksmbd_extract_shortname(struct ksmbd_conn *conn, - const char *longname, - char *shortname); - -int ksmbd_smb_negotiate_common(struct ksmbd_work *work, unsigned int command); - -int ksmbd_smb_check_shared_mode(struct file *filp, struct ksmbd_file *curr_fp); -int ksmbd_override_fsids(struct ksmbd_work *work); -void ksmbd_revert_fsids(struct ksmbd_work *work); - -unsigned int ksmbd_server_side_copy_max_chunk_count(void); -unsigned int ksmbd_server_side_copy_max_chunk_size(void); -unsigned int ksmbd_server_side_copy_max_total_size(void); -bool is_asterisk(char *p); -__le32 smb_map_generic_desired_access(__le32 daccess); - -static inline unsigned int get_rfc1002_len(void *buf) -{ - return be32_to_cpu(*((__be32 *)buf)) & 0xffffff; -} - -static inline void inc_rfc1001_len(void *buf, int count) -{ - be32_add_cpu((__be32 *)buf, count); -} -#endif /* __SMB_COMMON_H__ */ diff --git a/fs/ksmbd/smbacl.c b/fs/ksmbd/smbacl.c deleted file mode 100644 index b05ff9b146b55..0000000000000 --- a/fs/ksmbd/smbacl.c +++ /dev/null @@ -1,1436 +0,0 @@ -// SPDX-License-Identifier: LGPL-2.1+ -/* - * Copyright (C) International Business Machines Corp., 2007,2008 - * Author(s): Steve French (sfrench@us.ibm.com) - * Copyright (C) 2020 Samsung Electronics Co., Ltd. - * Author(s): Namjae Jeon - */ - -#include -#include -#include -#include - -#include "smbacl.h" -#include "smb_common.h" -#include "server.h" -#include "misc.h" -#include "mgmt/share_config.h" - -static const struct smb_sid domain = {1, 4, {0, 0, 0, 0, 0, 5}, - {cpu_to_le32(21), cpu_to_le32(1), cpu_to_le32(2), cpu_to_le32(3), - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0} }; - -/* security id for everyone/world system group */ -static const struct smb_sid creator_owner = { - 1, 1, {0, 0, 0, 0, 0, 3}, {0} }; -/* security id for everyone/world system group */ -static const struct smb_sid creator_group = { - 1, 1, {0, 0, 0, 0, 0, 3}, {cpu_to_le32(1)} }; - -/* security id for everyone/world system group */ -static const struct smb_sid sid_everyone = { - 1, 1, {0, 0, 0, 0, 0, 1}, {0} }; -/* security id for Authenticated Users system group */ -static const struct smb_sid sid_authusers = { - 1, 1, {0, 0, 0, 0, 0, 5}, {cpu_to_le32(11)} }; - -/* S-1-22-1 Unmapped Unix users */ -static const struct smb_sid sid_unix_users = {1, 1, {0, 0, 0, 0, 0, 22}, - {cpu_to_le32(1), 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0} }; - -/* S-1-22-2 Unmapped Unix groups */ -static const struct smb_sid sid_unix_groups = { 1, 1, {0, 0, 0, 0, 0, 22}, - {cpu_to_le32(2), 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0} }; - -/* - * See http://technet.microsoft.com/en-us/library/hh509017(v=ws.10).aspx - */ - -/* S-1-5-88 MS NFS and Apple style UID/GID/mode */ - -/* S-1-5-88-1 Unix uid */ -static const struct smb_sid sid_unix_NFS_users = { 1, 2, {0, 0, 0, 0, 0, 5}, - {cpu_to_le32(88), - cpu_to_le32(1), 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0} }; - -/* S-1-5-88-2 Unix gid */ -static const struct smb_sid sid_unix_NFS_groups = { 1, 2, {0, 0, 0, 0, 0, 5}, - {cpu_to_le32(88), - cpu_to_le32(2), 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0} }; - -/* S-1-5-88-3 Unix mode */ -static const struct smb_sid sid_unix_NFS_mode = { 1, 2, {0, 0, 0, 0, 0, 5}, - {cpu_to_le32(88), - cpu_to_le32(3), 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0} }; - -/* - * if the two SIDs (roughly equivalent to a UUID for a user or group) are - * the same returns zero, if they do not match returns non-zero. - */ -int compare_sids(const struct smb_sid *ctsid, const struct smb_sid *cwsid) -{ - int i; - int num_subauth, num_sat, num_saw; - - if (!ctsid || !cwsid) - return 1; - - /* compare the revision */ - if (ctsid->revision != cwsid->revision) { - if (ctsid->revision > cwsid->revision) - return 1; - else - return -1; - } - - /* compare all of the six auth values */ - for (i = 0; i < NUM_AUTHS; ++i) { - if (ctsid->authority[i] != cwsid->authority[i]) { - if (ctsid->authority[i] > cwsid->authority[i]) - return 1; - else - return -1; - } - } - - /* compare all of the subauth values if any */ - num_sat = ctsid->num_subauth; - num_saw = cwsid->num_subauth; - num_subauth = num_sat < num_saw ? num_sat : num_saw; - if (num_subauth) { - for (i = 0; i < num_subauth; ++i) { - if (ctsid->sub_auth[i] != cwsid->sub_auth[i]) { - if (le32_to_cpu(ctsid->sub_auth[i]) > - le32_to_cpu(cwsid->sub_auth[i])) - return 1; - else - return -1; - } - } - } - - return 0; /* sids compare/match */ -} - -static void smb_copy_sid(struct smb_sid *dst, const struct smb_sid *src) -{ - int i; - - dst->revision = src->revision; - dst->num_subauth = min_t(u8, src->num_subauth, SID_MAX_SUB_AUTHORITIES); - for (i = 0; i < NUM_AUTHS; ++i) - dst->authority[i] = src->authority[i]; - for (i = 0; i < dst->num_subauth; ++i) - dst->sub_auth[i] = src->sub_auth[i]; -} - -/* - * change posix mode to reflect permissions - * pmode is the existing mode (we only want to overwrite part of this - * bits to set can be: S_IRWXU, S_IRWXG or S_IRWXO ie 00700 or 00070 or 00007 - */ -static umode_t access_flags_to_mode(struct smb_fattr *fattr, __le32 ace_flags, - int type) -{ - __u32 flags = le32_to_cpu(ace_flags); - umode_t mode = 0; - - if (flags & GENERIC_ALL) { - mode = 0777; - ksmbd_debug(SMB, "all perms\n"); - return mode; - } - - if ((flags & GENERIC_READ) || (flags & FILE_READ_RIGHTS)) - mode = 0444; - if ((flags & GENERIC_WRITE) || (flags & FILE_WRITE_RIGHTS)) { - mode |= 0222; - if (S_ISDIR(fattr->cf_mode)) - mode |= 0111; - } - if ((flags & GENERIC_EXECUTE) || (flags & FILE_EXEC_RIGHTS)) - mode |= 0111; - - if (type == ACCESS_DENIED_ACE_TYPE || type == ACCESS_DENIED_OBJECT_ACE_TYPE) - mode = ~mode; - - ksmbd_debug(SMB, "access flags 0x%x mode now %04o\n", flags, mode); - - return mode; -} - -/* - * Generate access flags to reflect permissions mode is the existing mode. - * This function is called for every ACE in the DACL whose SID matches - * with either owner or group or everyone. - */ -static void mode_to_access_flags(umode_t mode, umode_t bits_to_use, - __u32 *pace_flags) -{ - /* reset access mask */ - *pace_flags = 0x0; - - /* bits to use are either S_IRWXU or S_IRWXG or S_IRWXO */ - mode &= bits_to_use; - - /* - * check for R/W/X UGO since we do not know whose flags - * is this but we have cleared all the bits sans RWX for - * either user or group or other as per bits_to_use - */ - if (mode & 0444) - *pace_flags |= SET_FILE_READ_RIGHTS; - if (mode & 0222) - *pace_flags |= FILE_WRITE_RIGHTS; - if (mode & 0111) - *pace_flags |= SET_FILE_EXEC_RIGHTS; - - ksmbd_debug(SMB, "mode: %o, access flags now 0x%x\n", - mode, *pace_flags); -} - -static __u16 fill_ace_for_sid(struct smb_ace *pntace, - const struct smb_sid *psid, int type, int flags, - umode_t mode, umode_t bits) -{ - int i; - __u16 size = 0; - __u32 access_req = 0; - - pntace->type = type; - pntace->flags = flags; - mode_to_access_flags(mode, bits, &access_req); - if (!access_req) - access_req = SET_MINIMUM_RIGHTS; - pntace->access_req = cpu_to_le32(access_req); - - pntace->sid.revision = psid->revision; - pntace->sid.num_subauth = psid->num_subauth; - for (i = 0; i < NUM_AUTHS; i++) - pntace->sid.authority[i] = psid->authority[i]; - for (i = 0; i < psid->num_subauth; i++) - pntace->sid.sub_auth[i] = psid->sub_auth[i]; - - size = 1 + 1 + 2 + 4 + 1 + 1 + 6 + (psid->num_subauth * 4); - pntace->size = cpu_to_le16(size); - - return size; -} - -void id_to_sid(unsigned int cid, uint sidtype, struct smb_sid *ssid) -{ - switch (sidtype) { - case SIDOWNER: - smb_copy_sid(ssid, &server_conf.domain_sid); - break; - case SIDUNIX_USER: - smb_copy_sid(ssid, &sid_unix_users); - break; - case SIDUNIX_GROUP: - smb_copy_sid(ssid, &sid_unix_groups); - break; - case SIDCREATOR_OWNER: - smb_copy_sid(ssid, &creator_owner); - return; - case SIDCREATOR_GROUP: - smb_copy_sid(ssid, &creator_group); - return; - case SIDNFS_USER: - smb_copy_sid(ssid, &sid_unix_NFS_users); - break; - case SIDNFS_GROUP: - smb_copy_sid(ssid, &sid_unix_NFS_groups); - break; - case SIDNFS_MODE: - smb_copy_sid(ssid, &sid_unix_NFS_mode); - break; - default: - return; - } - - /* RID */ - ssid->sub_auth[ssid->num_subauth] = cpu_to_le32(cid); - ssid->num_subauth++; -} - -static int sid_to_id(struct user_namespace *user_ns, - struct smb_sid *psid, uint sidtype, - struct smb_fattr *fattr) -{ - int rc = -EINVAL; - - /* - * If we have too many subauthorities, then something is really wrong. - * Just return an error. - */ - if (unlikely(psid->num_subauth > SID_MAX_SUB_AUTHORITIES)) { - pr_err("%s: %u subauthorities is too many!\n", - __func__, psid->num_subauth); - return -EIO; - } - - if (sidtype == SIDOWNER) { - kuid_t uid; - uid_t id; - - id = le32_to_cpu(psid->sub_auth[psid->num_subauth - 1]); - uid = KUIDT_INIT(id); - uid = from_vfsuid(user_ns, &init_user_ns, VFSUIDT_INIT(uid)); - if (uid_valid(uid)) { - fattr->cf_uid = uid; - rc = 0; - } - } else { - kgid_t gid; - gid_t id; - - id = le32_to_cpu(psid->sub_auth[psid->num_subauth - 1]); - gid = KGIDT_INIT(id); - gid = from_vfsgid(user_ns, &init_user_ns, VFSGIDT_INIT(gid)); - if (gid_valid(gid)) { - fattr->cf_gid = gid; - rc = 0; - } - } - - return rc; -} - -void posix_state_to_acl(struct posix_acl_state *state, - struct posix_acl_entry *pace) -{ - int i; - - pace->e_tag = ACL_USER_OBJ; - pace->e_perm = state->owner.allow; - for (i = 0; i < state->users->n; i++) { - pace++; - pace->e_tag = ACL_USER; - pace->e_uid = state->users->aces[i].uid; - pace->e_perm = state->users->aces[i].perms.allow; - } - - pace++; - pace->e_tag = ACL_GROUP_OBJ; - pace->e_perm = state->group.allow; - - for (i = 0; i < state->groups->n; i++) { - pace++; - pace->e_tag = ACL_GROUP; - pace->e_gid = state->groups->aces[i].gid; - pace->e_perm = state->groups->aces[i].perms.allow; - } - - if (state->users->n || state->groups->n) { - pace++; - pace->e_tag = ACL_MASK; - pace->e_perm = state->mask.allow; - } - - pace++; - pace->e_tag = ACL_OTHER; - pace->e_perm = state->other.allow; -} - -int init_acl_state(struct posix_acl_state *state, int cnt) -{ - int alloc; - - memset(state, 0, sizeof(struct posix_acl_state)); - /* - * In the worst case, each individual acl could be for a distinct - * named user or group, but we don't know which, so we allocate - * enough space for either: - */ - alloc = sizeof(struct posix_ace_state_array) - + cnt * sizeof(struct posix_user_ace_state); - state->users = kzalloc(alloc, GFP_KERNEL); - if (!state->users) - return -ENOMEM; - state->groups = kzalloc(alloc, GFP_KERNEL); - if (!state->groups) { - kfree(state->users); - return -ENOMEM; - } - return 0; -} - -void free_acl_state(struct posix_acl_state *state) -{ - kfree(state->users); - kfree(state->groups); -} - -static void parse_dacl(struct user_namespace *user_ns, - struct smb_acl *pdacl, char *end_of_acl, - struct smb_sid *pownersid, struct smb_sid *pgrpsid, - struct smb_fattr *fattr) -{ - int i, ret; - int num_aces = 0; - unsigned int acl_size; - char *acl_base; - struct smb_ace **ppace; - struct posix_acl_entry *cf_pace, *cf_pdace; - struct posix_acl_state acl_state, default_acl_state; - umode_t mode = 0, acl_mode; - bool owner_found = false, group_found = false, others_found = false; - - if (!pdacl) - return; - - /* validate that we do not go past end of acl */ - if (end_of_acl < (char *)pdacl + sizeof(struct smb_acl) || - end_of_acl < (char *)pdacl + le16_to_cpu(pdacl->size)) { - pr_err("ACL too small to parse DACL\n"); - return; - } - - ksmbd_debug(SMB, "DACL revision %d size %d num aces %d\n", - le16_to_cpu(pdacl->revision), le16_to_cpu(pdacl->size), - le32_to_cpu(pdacl->num_aces)); - - acl_base = (char *)pdacl; - acl_size = sizeof(struct smb_acl); - - num_aces = le32_to_cpu(pdacl->num_aces); - if (num_aces <= 0) - return; - - if (num_aces > ULONG_MAX / sizeof(struct smb_ace *)) - return; - - ppace = kmalloc_array(num_aces, sizeof(struct smb_ace *), GFP_KERNEL); - if (!ppace) - return; - - ret = init_acl_state(&acl_state, num_aces); - if (ret) - return; - ret = init_acl_state(&default_acl_state, num_aces); - if (ret) { - free_acl_state(&acl_state); - return; - } - - /* - * reset rwx permissions for user/group/other. - * Also, if num_aces is 0 i.e. DACL has no ACEs, - * user/group/other have no permissions - */ - for (i = 0; i < num_aces; ++i) { - if (end_of_acl - acl_base < acl_size) - break; - - ppace[i] = (struct smb_ace *)(acl_base + acl_size); - acl_base = (char *)ppace[i]; - acl_size = offsetof(struct smb_ace, sid) + - offsetof(struct smb_sid, sub_auth); - - if (end_of_acl - acl_base < acl_size || - ppace[i]->sid.num_subauth > SID_MAX_SUB_AUTHORITIES || - (end_of_acl - acl_base < - acl_size + sizeof(__le32) * ppace[i]->sid.num_subauth) || - (le16_to_cpu(ppace[i]->size) < - acl_size + sizeof(__le32) * ppace[i]->sid.num_subauth)) - break; - - acl_size = le16_to_cpu(ppace[i]->size); - ppace[i]->access_req = - smb_map_generic_desired_access(ppace[i]->access_req); - - if (!(compare_sids(&ppace[i]->sid, &sid_unix_NFS_mode))) { - fattr->cf_mode = - le32_to_cpu(ppace[i]->sid.sub_auth[2]); - break; - } else if (!compare_sids(&ppace[i]->sid, pownersid)) { - acl_mode = access_flags_to_mode(fattr, - ppace[i]->access_req, - ppace[i]->type); - acl_mode &= 0700; - - if (!owner_found) { - mode &= ~(0700); - mode |= acl_mode; - } - owner_found = true; - } else if (!compare_sids(&ppace[i]->sid, pgrpsid) || - ppace[i]->sid.sub_auth[ppace[i]->sid.num_subauth - 1] == - DOMAIN_USER_RID_LE) { - acl_mode = access_flags_to_mode(fattr, - ppace[i]->access_req, - ppace[i]->type); - acl_mode &= 0070; - if (!group_found) { - mode &= ~(0070); - mode |= acl_mode; - } - group_found = true; - } else if (!compare_sids(&ppace[i]->sid, &sid_everyone)) { - acl_mode = access_flags_to_mode(fattr, - ppace[i]->access_req, - ppace[i]->type); - acl_mode &= 0007; - if (!others_found) { - mode &= ~(0007); - mode |= acl_mode; - } - others_found = true; - } else if (!compare_sids(&ppace[i]->sid, &creator_owner)) { - continue; - } else if (!compare_sids(&ppace[i]->sid, &creator_group)) { - continue; - } else if (!compare_sids(&ppace[i]->sid, &sid_authusers)) { - continue; - } else { - struct smb_fattr temp_fattr; - - acl_mode = access_flags_to_mode(fattr, ppace[i]->access_req, - ppace[i]->type); - temp_fattr.cf_uid = INVALID_UID; - ret = sid_to_id(user_ns, &ppace[i]->sid, SIDOWNER, &temp_fattr); - if (ret || uid_eq(temp_fattr.cf_uid, INVALID_UID)) { - pr_err("%s: Error %d mapping Owner SID to uid\n", - __func__, ret); - continue; - } - - acl_state.owner.allow = ((acl_mode & 0700) >> 6) | 0004; - acl_state.users->aces[acl_state.users->n].uid = - temp_fattr.cf_uid; - acl_state.users->aces[acl_state.users->n++].perms.allow = - ((acl_mode & 0700) >> 6) | 0004; - default_acl_state.owner.allow = ((acl_mode & 0700) >> 6) | 0004; - default_acl_state.users->aces[default_acl_state.users->n].uid = - temp_fattr.cf_uid; - default_acl_state.users->aces[default_acl_state.users->n++].perms.allow = - ((acl_mode & 0700) >> 6) | 0004; - } - } - kfree(ppace); - - if (owner_found) { - /* The owner must be set to at least read-only. */ - acl_state.owner.allow = ((mode & 0700) >> 6) | 0004; - acl_state.users->aces[acl_state.users->n].uid = fattr->cf_uid; - acl_state.users->aces[acl_state.users->n++].perms.allow = - ((mode & 0700) >> 6) | 0004; - default_acl_state.owner.allow = ((mode & 0700) >> 6) | 0004; - default_acl_state.users->aces[default_acl_state.users->n].uid = - fattr->cf_uid; - default_acl_state.users->aces[default_acl_state.users->n++].perms.allow = - ((mode & 0700) >> 6) | 0004; - } - - if (group_found) { - acl_state.group.allow = (mode & 0070) >> 3; - acl_state.groups->aces[acl_state.groups->n].gid = - fattr->cf_gid; - acl_state.groups->aces[acl_state.groups->n++].perms.allow = - (mode & 0070) >> 3; - default_acl_state.group.allow = (mode & 0070) >> 3; - default_acl_state.groups->aces[default_acl_state.groups->n].gid = - fattr->cf_gid; - default_acl_state.groups->aces[default_acl_state.groups->n++].perms.allow = - (mode & 0070) >> 3; - } - - if (others_found) { - fattr->cf_mode &= ~(0007); - fattr->cf_mode |= mode & 0007; - - acl_state.other.allow = mode & 0007; - default_acl_state.other.allow = mode & 0007; - } - - if (acl_state.users->n || acl_state.groups->n) { - acl_state.mask.allow = 0x07; - - if (IS_ENABLED(CONFIG_FS_POSIX_ACL)) { - fattr->cf_acls = - posix_acl_alloc(acl_state.users->n + - acl_state.groups->n + 4, GFP_KERNEL); - if (fattr->cf_acls) { - cf_pace = fattr->cf_acls->a_entries; - posix_state_to_acl(&acl_state, cf_pace); - } - } - } - - if (default_acl_state.users->n || default_acl_state.groups->n) { - default_acl_state.mask.allow = 0x07; - - if (IS_ENABLED(CONFIG_FS_POSIX_ACL)) { - fattr->cf_dacls = - posix_acl_alloc(default_acl_state.users->n + - default_acl_state.groups->n + 4, GFP_KERNEL); - if (fattr->cf_dacls) { - cf_pdace = fattr->cf_dacls->a_entries; - posix_state_to_acl(&default_acl_state, cf_pdace); - } - } - } - free_acl_state(&acl_state); - free_acl_state(&default_acl_state); -} - -static void set_posix_acl_entries_dacl(struct user_namespace *user_ns, - struct smb_ace *pndace, - struct smb_fattr *fattr, u32 *num_aces, - u16 *size, u32 nt_aces_num) -{ - struct posix_acl_entry *pace; - struct smb_sid *sid; - struct smb_ace *ntace; - int i, j; - - if (!fattr->cf_acls) - goto posix_default_acl; - - pace = fattr->cf_acls->a_entries; - for (i = 0; i < fattr->cf_acls->a_count; i++, pace++) { - int flags = 0; - - sid = kmalloc(sizeof(struct smb_sid), GFP_KERNEL); - if (!sid) - break; - - if (pace->e_tag == ACL_USER) { - uid_t uid; - unsigned int sid_type = SIDOWNER; - - uid = posix_acl_uid_translate(user_ns, pace); - if (!uid) - sid_type = SIDUNIX_USER; - id_to_sid(uid, sid_type, sid); - } else if (pace->e_tag == ACL_GROUP) { - gid_t gid; - - gid = posix_acl_gid_translate(user_ns, pace); - id_to_sid(gid, SIDUNIX_GROUP, sid); - } else if (pace->e_tag == ACL_OTHER && !nt_aces_num) { - smb_copy_sid(sid, &sid_everyone); - } else { - kfree(sid); - continue; - } - ntace = pndace; - for (j = 0; j < nt_aces_num; j++) { - if (ntace->sid.sub_auth[ntace->sid.num_subauth - 1] == - sid->sub_auth[sid->num_subauth - 1]) - goto pass_same_sid; - ntace = (struct smb_ace *)((char *)ntace + - le16_to_cpu(ntace->size)); - } - - if (S_ISDIR(fattr->cf_mode) && pace->e_tag == ACL_OTHER) - flags = 0x03; - - ntace = (struct smb_ace *)((char *)pndace + *size); - *size += fill_ace_for_sid(ntace, sid, ACCESS_ALLOWED, flags, - pace->e_perm, 0777); - (*num_aces)++; - if (pace->e_tag == ACL_USER) - ntace->access_req |= - FILE_DELETE_LE | FILE_DELETE_CHILD_LE; - - if (S_ISDIR(fattr->cf_mode) && - (pace->e_tag == ACL_USER || pace->e_tag == ACL_GROUP)) { - ntace = (struct smb_ace *)((char *)pndace + *size); - *size += fill_ace_for_sid(ntace, sid, ACCESS_ALLOWED, - 0x03, pace->e_perm, 0777); - (*num_aces)++; - if (pace->e_tag == ACL_USER) - ntace->access_req |= - FILE_DELETE_LE | FILE_DELETE_CHILD_LE; - } - -pass_same_sid: - kfree(sid); - } - - if (nt_aces_num) - return; - -posix_default_acl: - if (!fattr->cf_dacls) - return; - - pace = fattr->cf_dacls->a_entries; - for (i = 0; i < fattr->cf_dacls->a_count; i++, pace++) { - sid = kmalloc(sizeof(struct smb_sid), GFP_KERNEL); - if (!sid) - break; - - if (pace->e_tag == ACL_USER) { - uid_t uid; - - uid = posix_acl_uid_translate(user_ns, pace); - id_to_sid(uid, SIDCREATOR_OWNER, sid); - } else if (pace->e_tag == ACL_GROUP) { - gid_t gid; - - gid = posix_acl_gid_translate(user_ns, pace); - id_to_sid(gid, SIDCREATOR_GROUP, sid); - } else { - kfree(sid); - continue; - } - - ntace = (struct smb_ace *)((char *)pndace + *size); - *size += fill_ace_for_sid(ntace, sid, ACCESS_ALLOWED, 0x0b, - pace->e_perm, 0777); - (*num_aces)++; - if (pace->e_tag == ACL_USER) - ntace->access_req |= - FILE_DELETE_LE | FILE_DELETE_CHILD_LE; - kfree(sid); - } -} - -static void set_ntacl_dacl(struct user_namespace *user_ns, - struct smb_acl *pndacl, - struct smb_acl *nt_dacl, - unsigned int aces_size, - const struct smb_sid *pownersid, - const struct smb_sid *pgrpsid, - struct smb_fattr *fattr) -{ - struct smb_ace *ntace, *pndace; - int nt_num_aces = le32_to_cpu(nt_dacl->num_aces), num_aces = 0; - unsigned short size = 0; - int i; - - pndace = (struct smb_ace *)((char *)pndacl + sizeof(struct smb_acl)); - if (nt_num_aces) { - ntace = (struct smb_ace *)((char *)nt_dacl + sizeof(struct smb_acl)); - for (i = 0; i < nt_num_aces; i++) { - unsigned short nt_ace_size; - - if (offsetof(struct smb_ace, access_req) > aces_size) - break; - - nt_ace_size = le16_to_cpu(ntace->size); - if (nt_ace_size > aces_size) - break; - - memcpy((char *)pndace + size, ntace, nt_ace_size); - size += nt_ace_size; - aces_size -= nt_ace_size; - ntace = (struct smb_ace *)((char *)ntace + nt_ace_size); - num_aces++; - } - } - - set_posix_acl_entries_dacl(user_ns, pndace, fattr, - &num_aces, &size, nt_num_aces); - pndacl->num_aces = cpu_to_le32(num_aces); - pndacl->size = cpu_to_le16(le16_to_cpu(pndacl->size) + size); -} - -static void set_mode_dacl(struct user_namespace *user_ns, - struct smb_acl *pndacl, struct smb_fattr *fattr) -{ - struct smb_ace *pace, *pndace; - u32 num_aces = 0; - u16 size = 0, ace_size = 0; - uid_t uid; - const struct smb_sid *sid; - - pace = pndace = (struct smb_ace *)((char *)pndacl + sizeof(struct smb_acl)); - - if (fattr->cf_acls) { - set_posix_acl_entries_dacl(user_ns, pndace, fattr, - &num_aces, &size, num_aces); - goto out; - } - - /* owner RID */ - uid = from_kuid(&init_user_ns, fattr->cf_uid); - if (uid) - sid = &server_conf.domain_sid; - else - sid = &sid_unix_users; - ace_size = fill_ace_for_sid(pace, sid, ACCESS_ALLOWED, 0, - fattr->cf_mode, 0700); - pace->sid.sub_auth[pace->sid.num_subauth++] = cpu_to_le32(uid); - pace->size = cpu_to_le16(ace_size + 4); - size += le16_to_cpu(pace->size); - pace = (struct smb_ace *)((char *)pndace + size); - - /* Group RID */ - ace_size = fill_ace_for_sid(pace, &sid_unix_groups, - ACCESS_ALLOWED, 0, fattr->cf_mode, 0070); - pace->sid.sub_auth[pace->sid.num_subauth++] = - cpu_to_le32(from_kgid(&init_user_ns, fattr->cf_gid)); - pace->size = cpu_to_le16(ace_size + 4); - size += le16_to_cpu(pace->size); - pace = (struct smb_ace *)((char *)pndace + size); - num_aces = 3; - - if (S_ISDIR(fattr->cf_mode)) { - pace = (struct smb_ace *)((char *)pndace + size); - - /* creator owner */ - size += fill_ace_for_sid(pace, &creator_owner, ACCESS_ALLOWED, - 0x0b, fattr->cf_mode, 0700); - pace = (struct smb_ace *)((char *)pndace + size); - - /* creator group */ - size += fill_ace_for_sid(pace, &creator_group, ACCESS_ALLOWED, - 0x0b, fattr->cf_mode, 0070); - pace = (struct smb_ace *)((char *)pndace + size); - num_aces = 5; - } - - /* other */ - size += fill_ace_for_sid(pace, &sid_everyone, ACCESS_ALLOWED, 0, - fattr->cf_mode, 0007); - -out: - pndacl->num_aces = cpu_to_le32(num_aces); - pndacl->size = cpu_to_le16(le16_to_cpu(pndacl->size) + size); -} - -static int parse_sid(struct smb_sid *psid, char *end_of_acl) -{ - /* - * validate that we do not go past end of ACL - sid must be at least 8 - * bytes long (assuming no sub-auths - e.g. the null SID - */ - if (end_of_acl < (char *)psid + 8) { - pr_err("ACL too small to parse SID %p\n", psid); - return -EINVAL; - } - - return 0; -} - -/* Convert CIFS ACL to POSIX form */ -int parse_sec_desc(struct user_namespace *user_ns, struct smb_ntsd *pntsd, - int acl_len, struct smb_fattr *fattr) -{ - int rc = 0; - struct smb_sid *owner_sid_ptr, *group_sid_ptr; - struct smb_acl *dacl_ptr; /* no need for SACL ptr */ - char *end_of_acl = ((char *)pntsd) + acl_len; - __u32 dacloffset; - int pntsd_type; - - if (!pntsd) - return -EIO; - - if (acl_len < sizeof(struct smb_ntsd)) - return -EINVAL; - - owner_sid_ptr = (struct smb_sid *)((char *)pntsd + - le32_to_cpu(pntsd->osidoffset)); - group_sid_ptr = (struct smb_sid *)((char *)pntsd + - le32_to_cpu(pntsd->gsidoffset)); - dacloffset = le32_to_cpu(pntsd->dacloffset); - dacl_ptr = (struct smb_acl *)((char *)pntsd + dacloffset); - ksmbd_debug(SMB, - "revision %d type 0x%x ooffset 0x%x goffset 0x%x sacloffset 0x%x dacloffset 0x%x\n", - pntsd->revision, pntsd->type, le32_to_cpu(pntsd->osidoffset), - le32_to_cpu(pntsd->gsidoffset), - le32_to_cpu(pntsd->sacloffset), dacloffset); - - pntsd_type = le16_to_cpu(pntsd->type); - if (!(pntsd_type & DACL_PRESENT)) { - ksmbd_debug(SMB, "DACL_PRESENT in DACL type is not set\n"); - return rc; - } - - pntsd->type = cpu_to_le16(DACL_PRESENT); - - if (pntsd->osidoffset) { - rc = parse_sid(owner_sid_ptr, end_of_acl); - if (rc) { - pr_err("%s: Error %d parsing Owner SID\n", __func__, rc); - return rc; - } - - rc = sid_to_id(user_ns, owner_sid_ptr, SIDOWNER, fattr); - if (rc) { - pr_err("%s: Error %d mapping Owner SID to uid\n", - __func__, rc); - owner_sid_ptr = NULL; - } - } - - if (pntsd->gsidoffset) { - rc = parse_sid(group_sid_ptr, end_of_acl); - if (rc) { - pr_err("%s: Error %d mapping Owner SID to gid\n", - __func__, rc); - return rc; - } - rc = sid_to_id(user_ns, group_sid_ptr, SIDUNIX_GROUP, fattr); - if (rc) { - pr_err("%s: Error %d mapping Group SID to gid\n", - __func__, rc); - group_sid_ptr = NULL; - } - } - - if ((pntsd_type & (DACL_AUTO_INHERITED | DACL_AUTO_INHERIT_REQ)) == - (DACL_AUTO_INHERITED | DACL_AUTO_INHERIT_REQ)) - pntsd->type |= cpu_to_le16(DACL_AUTO_INHERITED); - if (pntsd_type & DACL_PROTECTED) - pntsd->type |= cpu_to_le16(DACL_PROTECTED); - - if (dacloffset) { - parse_dacl(user_ns, dacl_ptr, end_of_acl, - owner_sid_ptr, group_sid_ptr, fattr); - } - - return 0; -} - -/* Convert permission bits from mode to equivalent CIFS ACL */ -int build_sec_desc(struct user_namespace *user_ns, - struct smb_ntsd *pntsd, struct smb_ntsd *ppntsd, - int ppntsd_size, int addition_info, __u32 *secdesclen, - struct smb_fattr *fattr) -{ - int rc = 0; - __u32 offset; - struct smb_sid *owner_sid_ptr, *group_sid_ptr; - struct smb_sid *nowner_sid_ptr, *ngroup_sid_ptr; - struct smb_acl *dacl_ptr = NULL; /* no need for SACL ptr */ - uid_t uid; - gid_t gid; - unsigned int sid_type = SIDOWNER; - - nowner_sid_ptr = kmalloc(sizeof(struct smb_sid), GFP_KERNEL); - if (!nowner_sid_ptr) - return -ENOMEM; - - uid = from_kuid(&init_user_ns, fattr->cf_uid); - if (!uid) - sid_type = SIDUNIX_USER; - id_to_sid(uid, sid_type, nowner_sid_ptr); - - ngroup_sid_ptr = kmalloc(sizeof(struct smb_sid), GFP_KERNEL); - if (!ngroup_sid_ptr) { - kfree(nowner_sid_ptr); - return -ENOMEM; - } - - gid = from_kgid(&init_user_ns, fattr->cf_gid); - id_to_sid(gid, SIDUNIX_GROUP, ngroup_sid_ptr); - - offset = sizeof(struct smb_ntsd); - pntsd->sacloffset = 0; - pntsd->revision = cpu_to_le16(1); - pntsd->type = cpu_to_le16(SELF_RELATIVE); - if (ppntsd) - pntsd->type |= ppntsd->type; - - if (addition_info & OWNER_SECINFO) { - pntsd->osidoffset = cpu_to_le32(offset); - owner_sid_ptr = (struct smb_sid *)((char *)pntsd + offset); - smb_copy_sid(owner_sid_ptr, nowner_sid_ptr); - offset += 1 + 1 + 6 + (nowner_sid_ptr->num_subauth * 4); - } - - if (addition_info & GROUP_SECINFO) { - pntsd->gsidoffset = cpu_to_le32(offset); - group_sid_ptr = (struct smb_sid *)((char *)pntsd + offset); - smb_copy_sid(group_sid_ptr, ngroup_sid_ptr); - offset += 1 + 1 + 6 + (ngroup_sid_ptr->num_subauth * 4); - } - - if (addition_info & DACL_SECINFO) { - pntsd->type |= cpu_to_le16(DACL_PRESENT); - dacl_ptr = (struct smb_acl *)((char *)pntsd + offset); - dacl_ptr->revision = cpu_to_le16(2); - dacl_ptr->size = cpu_to_le16(sizeof(struct smb_acl)); - dacl_ptr->num_aces = 0; - - if (!ppntsd) { - set_mode_dacl(user_ns, dacl_ptr, fattr); - } else { - struct smb_acl *ppdacl_ptr; - unsigned int dacl_offset = le32_to_cpu(ppntsd->dacloffset); - int ppdacl_size, ntacl_size = ppntsd_size - dacl_offset; - - if (!dacl_offset || - (dacl_offset + sizeof(struct smb_acl) > ppntsd_size)) - goto out; - - ppdacl_ptr = (struct smb_acl *)((char *)ppntsd + dacl_offset); - ppdacl_size = le16_to_cpu(ppdacl_ptr->size); - if (ppdacl_size > ntacl_size || - ppdacl_size < sizeof(struct smb_acl)) - goto out; - - set_ntacl_dacl(user_ns, dacl_ptr, ppdacl_ptr, - ntacl_size - sizeof(struct smb_acl), - nowner_sid_ptr, ngroup_sid_ptr, - fattr); - } - pntsd->dacloffset = cpu_to_le32(offset); - offset += le16_to_cpu(dacl_ptr->size); - } - -out: - kfree(nowner_sid_ptr); - kfree(ngroup_sid_ptr); - *secdesclen = offset; - return rc; -} - -static void smb_set_ace(struct smb_ace *ace, const struct smb_sid *sid, u8 type, - u8 flags, __le32 access_req) -{ - ace->type = type; - ace->flags = flags; - ace->access_req = access_req; - smb_copy_sid(&ace->sid, sid); - ace->size = cpu_to_le16(1 + 1 + 2 + 4 + 1 + 1 + 6 + (sid->num_subauth * 4)); -} - -int smb_inherit_dacl(struct ksmbd_conn *conn, - const struct path *path, - unsigned int uid, unsigned int gid) -{ - const struct smb_sid *psid, *creator = NULL; - struct smb_ace *parent_aces, *aces; - struct smb_acl *parent_pdacl; - struct smb_ntsd *parent_pntsd = NULL; - struct smb_sid owner_sid, group_sid; - struct dentry *parent = path->dentry->d_parent; - struct user_namespace *user_ns = mnt_user_ns(path->mnt); - int inherited_flags = 0, flags = 0, i, ace_cnt = 0, nt_size = 0, pdacl_size; - int rc = 0, num_aces, dacloffset, pntsd_type, pntsd_size, acl_len, aces_size; - char *aces_base; - bool is_dir = S_ISDIR(d_inode(path->dentry)->i_mode); - - pntsd_size = ksmbd_vfs_get_sd_xattr(conn, user_ns, - parent, &parent_pntsd); - if (pntsd_size <= 0) - return -ENOENT; - dacloffset = le32_to_cpu(parent_pntsd->dacloffset); - if (!dacloffset || (dacloffset + sizeof(struct smb_acl) > pntsd_size)) { - rc = -EINVAL; - goto free_parent_pntsd; - } - - parent_pdacl = (struct smb_acl *)((char *)parent_pntsd + dacloffset); - acl_len = pntsd_size - dacloffset; - num_aces = le32_to_cpu(parent_pdacl->num_aces); - pntsd_type = le16_to_cpu(parent_pntsd->type); - pdacl_size = le16_to_cpu(parent_pdacl->size); - - if (pdacl_size > acl_len || pdacl_size < sizeof(struct smb_acl)) { - rc = -EINVAL; - goto free_parent_pntsd; - } - - aces_base = kmalloc(sizeof(struct smb_ace) * num_aces * 2, GFP_KERNEL); - if (!aces_base) { - rc = -ENOMEM; - goto free_parent_pntsd; - } - - aces = (struct smb_ace *)aces_base; - parent_aces = (struct smb_ace *)((char *)parent_pdacl + - sizeof(struct smb_acl)); - aces_size = acl_len - sizeof(struct smb_acl); - - if (pntsd_type & DACL_AUTO_INHERITED) - inherited_flags = INHERITED_ACE; - - for (i = 0; i < num_aces; i++) { - int pace_size; - - if (offsetof(struct smb_ace, access_req) > aces_size) - break; - - pace_size = le16_to_cpu(parent_aces->size); - if (pace_size > aces_size) - break; - - aces_size -= pace_size; - - flags = parent_aces->flags; - if (!smb_inherit_flags(flags, is_dir)) - goto pass; - if (is_dir) { - flags &= ~(INHERIT_ONLY_ACE | INHERITED_ACE); - if (!(flags & CONTAINER_INHERIT_ACE)) - flags |= INHERIT_ONLY_ACE; - if (flags & NO_PROPAGATE_INHERIT_ACE) - flags = 0; - } else { - flags = 0; - } - - if (!compare_sids(&creator_owner, &parent_aces->sid)) { - creator = &creator_owner; - id_to_sid(uid, SIDOWNER, &owner_sid); - psid = &owner_sid; - } else if (!compare_sids(&creator_group, &parent_aces->sid)) { - creator = &creator_group; - id_to_sid(gid, SIDUNIX_GROUP, &group_sid); - psid = &group_sid; - } else { - creator = NULL; - psid = &parent_aces->sid; - } - - if (is_dir && creator && flags & CONTAINER_INHERIT_ACE) { - smb_set_ace(aces, psid, parent_aces->type, inherited_flags, - parent_aces->access_req); - nt_size += le16_to_cpu(aces->size); - ace_cnt++; - aces = (struct smb_ace *)((char *)aces + le16_to_cpu(aces->size)); - flags |= INHERIT_ONLY_ACE; - psid = creator; - } else if (is_dir && !(parent_aces->flags & NO_PROPAGATE_INHERIT_ACE)) { - psid = &parent_aces->sid; - } - - smb_set_ace(aces, psid, parent_aces->type, flags | inherited_flags, - parent_aces->access_req); - nt_size += le16_to_cpu(aces->size); - aces = (struct smb_ace *)((char *)aces + le16_to_cpu(aces->size)); - ace_cnt++; -pass: - parent_aces = (struct smb_ace *)((char *)parent_aces + pace_size); - } - - if (nt_size > 0) { - struct smb_ntsd *pntsd; - struct smb_acl *pdacl; - struct smb_sid *powner_sid = NULL, *pgroup_sid = NULL; - int powner_sid_size = 0, pgroup_sid_size = 0, pntsd_size; - - if (parent_pntsd->osidoffset) { - powner_sid = (struct smb_sid *)((char *)parent_pntsd + - le32_to_cpu(parent_pntsd->osidoffset)); - powner_sid_size = 1 + 1 + 6 + (powner_sid->num_subauth * 4); - } - if (parent_pntsd->gsidoffset) { - pgroup_sid = (struct smb_sid *)((char *)parent_pntsd + - le32_to_cpu(parent_pntsd->gsidoffset)); - pgroup_sid_size = 1 + 1 + 6 + (pgroup_sid->num_subauth * 4); - } - - pntsd = kzalloc(sizeof(struct smb_ntsd) + powner_sid_size + - pgroup_sid_size + sizeof(struct smb_acl) + - nt_size, GFP_KERNEL); - if (!pntsd) { - rc = -ENOMEM; - goto free_aces_base; - } - - pntsd->revision = cpu_to_le16(1); - pntsd->type = cpu_to_le16(SELF_RELATIVE | DACL_PRESENT); - if (le16_to_cpu(parent_pntsd->type) & DACL_AUTO_INHERITED) - pntsd->type |= cpu_to_le16(DACL_AUTO_INHERITED); - pntsd_size = sizeof(struct smb_ntsd); - pntsd->osidoffset = parent_pntsd->osidoffset; - pntsd->gsidoffset = parent_pntsd->gsidoffset; - pntsd->dacloffset = parent_pntsd->dacloffset; - - if (pntsd->osidoffset) { - struct smb_sid *owner_sid = (struct smb_sid *)((char *)pntsd + - le32_to_cpu(pntsd->osidoffset)); - memcpy(owner_sid, powner_sid, powner_sid_size); - pntsd_size += powner_sid_size; - } - - if (pntsd->gsidoffset) { - struct smb_sid *group_sid = (struct smb_sid *)((char *)pntsd + - le32_to_cpu(pntsd->gsidoffset)); - memcpy(group_sid, pgroup_sid, pgroup_sid_size); - pntsd_size += pgroup_sid_size; - } - - if (pntsd->dacloffset) { - struct smb_ace *pace; - - pdacl = (struct smb_acl *)((char *)pntsd + le32_to_cpu(pntsd->dacloffset)); - pdacl->revision = cpu_to_le16(2); - pdacl->size = cpu_to_le16(sizeof(struct smb_acl) + nt_size); - pdacl->num_aces = cpu_to_le32(ace_cnt); - pace = (struct smb_ace *)((char *)pdacl + sizeof(struct smb_acl)); - memcpy(pace, aces_base, nt_size); - pntsd_size += sizeof(struct smb_acl) + nt_size; - } - - ksmbd_vfs_set_sd_xattr(conn, user_ns, - path->dentry, pntsd, pntsd_size); - kfree(pntsd); - } - -free_aces_base: - kfree(aces_base); -free_parent_pntsd: - kfree(parent_pntsd); - return rc; -} - -bool smb_inherit_flags(int flags, bool is_dir) -{ - if (!is_dir) - return (flags & OBJECT_INHERIT_ACE) != 0; - - if (flags & OBJECT_INHERIT_ACE && !(flags & NO_PROPAGATE_INHERIT_ACE)) - return true; - - if (flags & CONTAINER_INHERIT_ACE) - return true; - return false; -} - -int smb_check_perm_dacl(struct ksmbd_conn *conn, const struct path *path, - __le32 *pdaccess, int uid) -{ - struct user_namespace *user_ns = mnt_user_ns(path->mnt); - struct smb_ntsd *pntsd = NULL; - struct smb_acl *pdacl; - struct posix_acl *posix_acls; - int rc = 0, pntsd_size, acl_size, aces_size, pdacl_size, dacl_offset; - struct smb_sid sid; - int granted = le32_to_cpu(*pdaccess & ~FILE_MAXIMAL_ACCESS_LE); - struct smb_ace *ace; - int i, found = 0; - unsigned int access_bits = 0; - struct smb_ace *others_ace = NULL; - struct posix_acl_entry *pa_entry; - unsigned int sid_type = SIDOWNER; - unsigned short ace_size; - - ksmbd_debug(SMB, "check permission using windows acl\n"); - pntsd_size = ksmbd_vfs_get_sd_xattr(conn, user_ns, - path->dentry, &pntsd); - if (pntsd_size <= 0 || !pntsd) - goto err_out; - - dacl_offset = le32_to_cpu(pntsd->dacloffset); - if (!dacl_offset || - (dacl_offset + sizeof(struct smb_acl) > pntsd_size)) - goto err_out; - - pdacl = (struct smb_acl *)((char *)pntsd + le32_to_cpu(pntsd->dacloffset)); - acl_size = pntsd_size - dacl_offset; - pdacl_size = le16_to_cpu(pdacl->size); - - if (pdacl_size > acl_size || pdacl_size < sizeof(struct smb_acl)) - goto err_out; - - if (!pdacl->num_aces) { - if (!(pdacl_size - sizeof(struct smb_acl)) && - *pdaccess & ~(FILE_READ_CONTROL_LE | FILE_WRITE_DAC_LE)) { - rc = -EACCES; - goto err_out; - } - goto err_out; - } - - if (*pdaccess & FILE_MAXIMAL_ACCESS_LE) { - granted = READ_CONTROL | WRITE_DAC | FILE_READ_ATTRIBUTES | - DELETE; - - ace = (struct smb_ace *)((char *)pdacl + sizeof(struct smb_acl)); - aces_size = acl_size - sizeof(struct smb_acl); - for (i = 0; i < le32_to_cpu(pdacl->num_aces); i++) { - if (offsetof(struct smb_ace, access_req) > aces_size) - break; - ace_size = le16_to_cpu(ace->size); - if (ace_size > aces_size) - break; - aces_size -= ace_size; - granted |= le32_to_cpu(ace->access_req); - ace = (struct smb_ace *)((char *)ace + le16_to_cpu(ace->size)); - } - - if (!pdacl->num_aces) - granted = GENERIC_ALL_FLAGS; - } - - if (!uid) - sid_type = SIDUNIX_USER; - id_to_sid(uid, sid_type, &sid); - - ace = (struct smb_ace *)((char *)pdacl + sizeof(struct smb_acl)); - aces_size = acl_size - sizeof(struct smb_acl); - for (i = 0; i < le32_to_cpu(pdacl->num_aces); i++) { - if (offsetof(struct smb_ace, access_req) > aces_size) - break; - ace_size = le16_to_cpu(ace->size); - if (ace_size > aces_size) - break; - aces_size -= ace_size; - - if (!compare_sids(&sid, &ace->sid) || - !compare_sids(&sid_unix_NFS_mode, &ace->sid)) { - found = 1; - break; - } - if (!compare_sids(&sid_everyone, &ace->sid)) - others_ace = ace; - - ace = (struct smb_ace *)((char *)ace + le16_to_cpu(ace->size)); - } - - if (*pdaccess & FILE_MAXIMAL_ACCESS_LE && found) { - granted = READ_CONTROL | WRITE_DAC | FILE_READ_ATTRIBUTES | - DELETE; - - granted |= le32_to_cpu(ace->access_req); - - if (!pdacl->num_aces) - granted = GENERIC_ALL_FLAGS; - } - - if (IS_ENABLED(CONFIG_FS_POSIX_ACL)) { - posix_acls = get_acl(d_inode(path->dentry), ACL_TYPE_ACCESS); - if (posix_acls && !found) { - unsigned int id = -1; - - pa_entry = posix_acls->a_entries; - for (i = 0; i < posix_acls->a_count; i++, pa_entry++) { - if (pa_entry->e_tag == ACL_USER) - id = posix_acl_uid_translate(user_ns, pa_entry); - else if (pa_entry->e_tag == ACL_GROUP) - id = posix_acl_gid_translate(user_ns, pa_entry); - else - continue; - - if (id == uid) { - mode_to_access_flags(pa_entry->e_perm, - 0777, - &access_bits); - if (!access_bits) - access_bits = - SET_MINIMUM_RIGHTS; - posix_acl_release(posix_acls); - goto check_access_bits; - } - } - } - if (posix_acls) - posix_acl_release(posix_acls); - } - - if (!found) { - if (others_ace) { - ace = others_ace; - } else { - ksmbd_debug(SMB, "Can't find corresponding sid\n"); - rc = -EACCES; - goto err_out; - } - } - - switch (ace->type) { - case ACCESS_ALLOWED_ACE_TYPE: - access_bits = le32_to_cpu(ace->access_req); - break; - case ACCESS_DENIED_ACE_TYPE: - case ACCESS_DENIED_CALLBACK_ACE_TYPE: - access_bits = le32_to_cpu(~ace->access_req); - break; - } - -check_access_bits: - if (granted & - ~(access_bits | FILE_READ_ATTRIBUTES | READ_CONTROL | WRITE_DAC | DELETE)) { - ksmbd_debug(SMB, "Access denied with winACL, granted : %x, access_req : %x\n", - granted, le32_to_cpu(ace->access_req)); - rc = -EACCES; - goto err_out; - } - - *pdaccess = cpu_to_le32(granted); -err_out: - kfree(pntsd); - return rc; -} - -int set_info_sec(struct ksmbd_conn *conn, struct ksmbd_tree_connect *tcon, - const struct path *path, struct smb_ntsd *pntsd, int ntsd_len, - bool type_check) -{ - int rc; - struct smb_fattr fattr = {{0}}; - struct inode *inode = d_inode(path->dentry); - struct user_namespace *user_ns = mnt_user_ns(path->mnt); - struct iattr newattrs; - - fattr.cf_uid = INVALID_UID; - fattr.cf_gid = INVALID_GID; - fattr.cf_mode = inode->i_mode; - - rc = parse_sec_desc(user_ns, pntsd, ntsd_len, &fattr); - if (rc) - goto out; - - newattrs.ia_valid = ATTR_CTIME; - if (!uid_eq(fattr.cf_uid, INVALID_UID)) { - newattrs.ia_valid |= ATTR_UID; - newattrs.ia_uid = fattr.cf_uid; - } - if (!gid_eq(fattr.cf_gid, INVALID_GID)) { - newattrs.ia_valid |= ATTR_GID; - newattrs.ia_gid = fattr.cf_gid; - } - newattrs.ia_valid |= ATTR_MODE; - newattrs.ia_mode = (inode->i_mode & ~0777) | (fattr.cf_mode & 0777); - - ksmbd_vfs_remove_acl_xattrs(user_ns, path->dentry); - /* Update posix acls */ - if (IS_ENABLED(CONFIG_FS_POSIX_ACL) && fattr.cf_dacls) { - rc = set_posix_acl(user_ns, inode, - ACL_TYPE_ACCESS, fattr.cf_acls); - if (rc < 0) - ksmbd_debug(SMB, - "Set posix acl(ACL_TYPE_ACCESS) failed, rc : %d\n", - rc); - if (S_ISDIR(inode->i_mode) && fattr.cf_dacls) { - rc = set_posix_acl(user_ns, inode, - ACL_TYPE_DEFAULT, fattr.cf_dacls); - if (rc) - ksmbd_debug(SMB, - "Set posix acl(ACL_TYPE_DEFAULT) failed, rc : %d\n", - rc); - } - } - - inode_lock(inode); - rc = notify_change(user_ns, path->dentry, &newattrs, NULL); - inode_unlock(inode); - if (rc) - goto out; - - /* Check it only calling from SD BUFFER context */ - if (type_check && !(le16_to_cpu(pntsd->type) & DACL_PRESENT)) - goto out; - - if (test_share_config_flag(tcon->share_conf, KSMBD_SHARE_FLAG_ACL_XATTR)) { - /* Update WinACL in xattr */ - ksmbd_vfs_remove_sd_xattrs(user_ns, path->dentry); - ksmbd_vfs_set_sd_xattr(conn, user_ns, - path->dentry, pntsd, ntsd_len); - } - -out: - posix_acl_release(fattr.cf_acls); - posix_acl_release(fattr.cf_dacls); - mark_inode_dirty(inode); - return rc; -} - -void ksmbd_init_domain(u32 *sub_auth) -{ - int i; - - memcpy(&server_conf.domain_sid, &domain, sizeof(struct smb_sid)); - for (i = 0; i < 3; ++i) - server_conf.domain_sid.sub_auth[i + 1] = cpu_to_le32(sub_auth[i]); -} diff --git a/fs/ksmbd/smbacl.h b/fs/ksmbd/smbacl.h deleted file mode 100644 index 618f2e0236b31..0000000000000 --- a/fs/ksmbd/smbacl.h +++ /dev/null @@ -1,238 +0,0 @@ -/* SPDX-License-Identifier: LGPL-2.1+ */ -/* - * Copyright (c) International Business Machines Corp., 2007 - * Author(s): Steve French (sfrench@us.ibm.com) - * Modified by Namjae Jeon (linkinjeon@kernel.org) - */ - -#ifndef _SMBACL_H -#define _SMBACL_H - -#include -#include -#include -#include - -#include "mgmt/tree_connect.h" - -#define NUM_AUTHS (6) /* number of authority fields */ -#define SID_MAX_SUB_AUTHORITIES (15) /* max number of sub authority fields */ - -/* - * ACE types - see MS-DTYP 2.4.4.1 - */ -enum { - ACCESS_ALLOWED, - ACCESS_DENIED, -}; - -/* - * Security ID types - */ -enum { - SIDOWNER = 1, - SIDGROUP, - SIDCREATOR_OWNER, - SIDCREATOR_GROUP, - SIDUNIX_USER, - SIDUNIX_GROUP, - SIDNFS_USER, - SIDNFS_GROUP, - SIDNFS_MODE, -}; - -/* Revision for ACLs */ -#define SD_REVISION 1 - -/* Control flags for Security Descriptor */ -#define OWNER_DEFAULTED 0x0001 -#define GROUP_DEFAULTED 0x0002 -#define DACL_PRESENT 0x0004 -#define DACL_DEFAULTED 0x0008 -#define SACL_PRESENT 0x0010 -#define SACL_DEFAULTED 0x0020 -#define DACL_TRUSTED 0x0040 -#define SERVER_SECURITY 0x0080 -#define DACL_AUTO_INHERIT_REQ 0x0100 -#define SACL_AUTO_INHERIT_REQ 0x0200 -#define DACL_AUTO_INHERITED 0x0400 -#define SACL_AUTO_INHERITED 0x0800 -#define DACL_PROTECTED 0x1000 -#define SACL_PROTECTED 0x2000 -#define RM_CONTROL_VALID 0x4000 -#define SELF_RELATIVE 0x8000 - -/* ACE types - see MS-DTYP 2.4.4.1 */ -#define ACCESS_ALLOWED_ACE_TYPE 0x00 -#define ACCESS_DENIED_ACE_TYPE 0x01 -#define SYSTEM_AUDIT_ACE_TYPE 0x02 -#define SYSTEM_ALARM_ACE_TYPE 0x03 -#define ACCESS_ALLOWED_COMPOUND_ACE_TYPE 0x04 -#define ACCESS_ALLOWED_OBJECT_ACE_TYPE 0x05 -#define ACCESS_DENIED_OBJECT_ACE_TYPE 0x06 -#define SYSTEM_AUDIT_OBJECT_ACE_TYPE 0x07 -#define SYSTEM_ALARM_OBJECT_ACE_TYPE 0x08 -#define ACCESS_ALLOWED_CALLBACK_ACE_TYPE 0x09 -#define ACCESS_DENIED_CALLBACK_ACE_TYPE 0x0A -#define ACCESS_ALLOWED_CALLBACK_OBJECT_ACE_TYPE 0x0B -#define ACCESS_DENIED_CALLBACK_OBJECT_ACE_TYPE 0x0C -#define SYSTEM_AUDIT_CALLBACK_ACE_TYPE 0x0D -#define SYSTEM_ALARM_CALLBACK_ACE_TYPE 0x0E /* Reserved */ -#define SYSTEM_AUDIT_CALLBACK_OBJECT_ACE_TYPE 0x0F -#define SYSTEM_ALARM_CALLBACK_OBJECT_ACE_TYPE 0x10 /* reserved */ -#define SYSTEM_MANDATORY_LABEL_ACE_TYPE 0x11 -#define SYSTEM_RESOURCE_ATTRIBUTE_ACE_TYPE 0x12 -#define SYSTEM_SCOPED_POLICY_ID_ACE_TYPE 0x13 - -/* ACE flags */ -#define OBJECT_INHERIT_ACE 0x01 -#define CONTAINER_INHERIT_ACE 0x02 -#define NO_PROPAGATE_INHERIT_ACE 0x04 -#define INHERIT_ONLY_ACE 0x08 -#define INHERITED_ACE 0x10 -#define SUCCESSFUL_ACCESS_ACE_FLAG 0x40 -#define FAILED_ACCESS_ACE_FLAG 0x80 - -/* - * Maximum size of a string representation of a SID: - * - * The fields are unsigned values in decimal. So: - * - * u8: max 3 bytes in decimal - * u32: max 10 bytes in decimal - * - * "S-" + 3 bytes for version field + 15 for authority field + NULL terminator - * - * For authority field, max is when all 6 values are non-zero and it must be - * represented in hex. So "-0x" + 12 hex digits. - * - * Add 11 bytes for each subauthority field (10 bytes each + 1 for '-') - */ -#define SID_STRING_BASE_SIZE (2 + 3 + 15 + 1) -#define SID_STRING_SUBAUTH_SIZE (11) /* size of a single subauth string */ - -#define DOMAIN_USER_RID_LE cpu_to_le32(513) - -struct ksmbd_conn; - -struct smb_ntsd { - __le16 revision; /* revision level */ - __le16 type; - __le32 osidoffset; - __le32 gsidoffset; - __le32 sacloffset; - __le32 dacloffset; -} __packed; - -struct smb_sid { - __u8 revision; /* revision level */ - __u8 num_subauth; - __u8 authority[NUM_AUTHS]; - __le32 sub_auth[SID_MAX_SUB_AUTHORITIES]; /* sub_auth[num_subauth] */ -} __packed; - -/* size of a struct cifs_sid, sans sub_auth array */ -#define CIFS_SID_BASE_SIZE (1 + 1 + NUM_AUTHS) - -struct smb_acl { - __le16 revision; /* revision level */ - __le16 size; - __le32 num_aces; -} __packed; - -struct smb_ace { - __u8 type; - __u8 flags; - __le16 size; - __le32 access_req; - struct smb_sid sid; /* ie UUID of user or group who gets these perms */ -} __packed; - -struct smb_fattr { - kuid_t cf_uid; - kgid_t cf_gid; - umode_t cf_mode; - __le32 daccess; - struct posix_acl *cf_acls; - struct posix_acl *cf_dacls; -}; - -struct posix_ace_state { - u32 allow; - u32 deny; -}; - -struct posix_user_ace_state { - union { - kuid_t uid; - kgid_t gid; - }; - struct posix_ace_state perms; -}; - -struct posix_ace_state_array { - int n; - struct posix_user_ace_state aces[]; -}; - -/* - * while processing the nfsv4 ace, this maintains the partial permissions - * calculated so far: - */ - -struct posix_acl_state { - struct posix_ace_state owner; - struct posix_ace_state group; - struct posix_ace_state other; - struct posix_ace_state everyone; - struct posix_ace_state mask; /* deny unused in this case */ - struct posix_ace_state_array *users; - struct posix_ace_state_array *groups; -}; - -int parse_sec_desc(struct user_namespace *user_ns, struct smb_ntsd *pntsd, - int acl_len, struct smb_fattr *fattr); -int build_sec_desc(struct user_namespace *user_ns, struct smb_ntsd *pntsd, - struct smb_ntsd *ppntsd, int ppntsd_size, int addition_info, - __u32 *secdesclen, struct smb_fattr *fattr); -int init_acl_state(struct posix_acl_state *state, int cnt); -void free_acl_state(struct posix_acl_state *state); -void posix_state_to_acl(struct posix_acl_state *state, - struct posix_acl_entry *pace); -int compare_sids(const struct smb_sid *ctsid, const struct smb_sid *cwsid); -bool smb_inherit_flags(int flags, bool is_dir); -int smb_inherit_dacl(struct ksmbd_conn *conn, const struct path *path, - unsigned int uid, unsigned int gid); -int smb_check_perm_dacl(struct ksmbd_conn *conn, const struct path *path, - __le32 *pdaccess, int uid); -int set_info_sec(struct ksmbd_conn *conn, struct ksmbd_tree_connect *tcon, - const struct path *path, struct smb_ntsd *pntsd, int ntsd_len, - bool type_check); -void id_to_sid(unsigned int cid, uint sidtype, struct smb_sid *ssid); -void ksmbd_init_domain(u32 *sub_auth); - -static inline uid_t posix_acl_uid_translate(struct user_namespace *mnt_userns, - struct posix_acl_entry *pace) -{ - vfsuid_t vfsuid; - - /* If this is an idmapped mount, apply the idmapping. */ - vfsuid = make_vfsuid(mnt_userns, &init_user_ns, pace->e_uid); - - /* Translate the kuid into a userspace id ksmbd would see. */ - return from_kuid(&init_user_ns, vfsuid_into_kuid(vfsuid)); -} - -static inline gid_t posix_acl_gid_translate(struct user_namespace *mnt_userns, - struct posix_acl_entry *pace) -{ - vfsgid_t vfsgid; - - /* If this is an idmapped mount, apply the idmapping. */ - vfsgid = make_vfsgid(mnt_userns, &init_user_ns, pace->e_gid); - - /* Translate the kgid into a userspace id ksmbd would see. */ - return from_kgid(&init_user_ns, vfsgid_into_kgid(vfsgid)); -} - -#endif /* _SMBACL_H */ diff --git a/fs/ksmbd/smbfsctl.h b/fs/ksmbd/smbfsctl.h deleted file mode 100644 index b98418aae20cd..0000000000000 --- a/fs/ksmbd/smbfsctl.h +++ /dev/null @@ -1,91 +0,0 @@ -/* SPDX-License-Identifier: LGPL-2.1+ */ -/* - * fs/cifs/smbfsctl.h: SMB, CIFS, SMB2 FSCTL definitions - * - * Copyright (c) International Business Machines Corp., 2002,2009 - * Author(s): Steve French (sfrench@us.ibm.com) - */ - -/* IOCTL information */ -/* - * List of ioctl/fsctl function codes that are or could be useful in the - * future to remote clients like cifs or SMB2 client. There is probably - * a slightly larger set of fsctls that NTFS local filesystem could handle, - * including the seven below that we do not have struct definitions for. - * Even with protocol definitions for most of these now available, we still - * need to do some experimentation to identify which are practical to do - * remotely. Some of the following, such as the encryption/compression ones - * could be invoked from tools via a specialized hook into the VFS rather - * than via the standard vfs entry points - */ - -#ifndef __KSMBD_SMBFSCTL_H -#define __KSMBD_SMBFSCTL_H - -#define FSCTL_DFS_GET_REFERRALS 0x00060194 -#define FSCTL_DFS_GET_REFERRALS_EX 0x000601B0 -#define FSCTL_REQUEST_OPLOCK_LEVEL_1 0x00090000 -#define FSCTL_REQUEST_OPLOCK_LEVEL_2 0x00090004 -#define FSCTL_REQUEST_BATCH_OPLOCK 0x00090008 -#define FSCTL_LOCK_VOLUME 0x00090018 -#define FSCTL_UNLOCK_VOLUME 0x0009001C -#define FSCTL_IS_PATHNAME_VALID 0x0009002C /* BB add struct */ -#define FSCTL_GET_COMPRESSION 0x0009003C /* BB add struct */ -#define FSCTL_SET_COMPRESSION 0x0009C040 /* BB add struct */ -#define FSCTL_QUERY_FAT_BPB 0x00090058 /* BB add struct */ -/* Verify the next FSCTL number, we had it as 0x00090090 before */ -#define FSCTL_FILESYSTEM_GET_STATS 0x00090060 /* BB add struct */ -#define FSCTL_GET_NTFS_VOLUME_DATA 0x00090064 /* BB add struct */ -#define FSCTL_GET_RETRIEVAL_POINTERS 0x00090073 /* BB add struct */ -#define FSCTL_IS_VOLUME_DIRTY 0x00090078 /* BB add struct */ -#define FSCTL_ALLOW_EXTENDED_DASD_IO 0x00090083 /* BB add struct */ -#define FSCTL_REQUEST_FILTER_OPLOCK 0x0009008C -#define FSCTL_FIND_FILES_BY_SID 0x0009008F /* BB add struct */ -#define FSCTL_SET_OBJECT_ID 0x00090098 /* BB add struct */ -#define FSCTL_GET_OBJECT_ID 0x0009009C /* BB add struct */ -#define FSCTL_DELETE_OBJECT_ID 0x000900A0 /* BB add struct */ -#define FSCTL_SET_REPARSE_POINT 0x000900A4 /* BB add struct */ -#define FSCTL_GET_REPARSE_POINT 0x000900A8 /* BB add struct */ -#define FSCTL_DELETE_REPARSE_POINT 0x000900AC /* BB add struct */ -#define FSCTL_SET_OBJECT_ID_EXTENDED 0x000900BC /* BB add struct */ -#define FSCTL_CREATE_OR_GET_OBJECT_ID 0x000900C0 /* BB add struct */ -#define FSCTL_SET_SPARSE 0x000900C4 /* BB add struct */ -#define FSCTL_SET_ZERO_DATA 0x000980C8 /* BB add struct */ -#define FSCTL_SET_ENCRYPTION 0x000900D7 /* BB add struct */ -#define FSCTL_ENCRYPTION_FSCTL_IO 0x000900DB /* BB add struct */ -#define FSCTL_WRITE_RAW_ENCRYPTED 0x000900DF /* BB add struct */ -#define FSCTL_READ_RAW_ENCRYPTED 0x000900E3 /* BB add struct */ -#define FSCTL_READ_FILE_USN_DATA 0x000900EB /* BB add struct */ -#define FSCTL_WRITE_USN_CLOSE_RECORD 0x000900EF /* BB add struct */ -#define FSCTL_SIS_COPYFILE 0x00090100 /* BB add struct */ -#define FSCTL_RECALL_FILE 0x00090117 /* BB add struct */ -#define FSCTL_QUERY_SPARING_INFO 0x00090138 /* BB add struct */ -#define FSCTL_SET_ZERO_ON_DEALLOC 0x00090194 /* BB add struct */ -#define FSCTL_SET_SHORT_NAME_BEHAVIOR 0x000901B4 /* BB add struct */ -#define FSCTL_QUERY_ALLOCATED_RANGES 0x000940CF /* BB add struct */ -#define FSCTL_SET_DEFECT_MANAGEMENT 0x00098134 /* BB add struct */ -#define FSCTL_DUPLICATE_EXTENTS_TO_FILE 0x00098344 -#define FSCTL_SIS_LINK_FILES 0x0009C104 -#define FSCTL_PIPE_PEEK 0x0011400C /* BB add struct */ -#define FSCTL_PIPE_TRANSCEIVE 0x0011C017 /* BB add struct */ -/* strange that the number for this op is not sequential with previous op */ -#define FSCTL_PIPE_WAIT 0x00110018 /* BB add struct */ -#define FSCTL_REQUEST_RESUME_KEY 0x00140078 -#define FSCTL_LMR_GET_LINK_TRACK_INF 0x001400E8 /* BB add struct */ -#define FSCTL_LMR_SET_LINK_TRACK_INF 0x001400EC /* BB add struct */ -#define FSCTL_VALIDATE_NEGOTIATE_INFO 0x00140204 -#define FSCTL_QUERY_NETWORK_INTERFACE_INFO 0x001401FC -#define FSCTL_COPYCHUNK 0x001440F2 -#define FSCTL_COPYCHUNK_WRITE 0x001480F2 - -#define IO_REPARSE_TAG_MOUNT_POINT 0xA0000003 -#define IO_REPARSE_TAG_HSM 0xC0000004 -#define IO_REPARSE_TAG_SIS 0x80000007 - -/* WSL reparse tags */ -#define IO_REPARSE_TAG_LX_SYMLINK_LE cpu_to_le32(0xA000001D) -#define IO_REPARSE_TAG_AF_UNIX_LE cpu_to_le32(0x80000023) -#define IO_REPARSE_TAG_LX_FIFO_LE cpu_to_le32(0x80000024) -#define IO_REPARSE_TAG_LX_CHR_LE cpu_to_le32(0x80000025) -#define IO_REPARSE_TAG_LX_BLK_LE cpu_to_le32(0x80000026) -#endif /* __KSMBD_SMBFSCTL_H */ diff --git a/fs/ksmbd/smbstatus.h b/fs/ksmbd/smbstatus.h deleted file mode 100644 index 108a8b6ed24a0..0000000000000 --- a/fs/ksmbd/smbstatus.h +++ /dev/null @@ -1,1822 +0,0 @@ -/* SPDX-License-Identifier: LGPL-2.1+ */ -/* - * fs/cifs/smb2status.h - * - * SMB2 Status code (network error) definitions - * Definitions are from MS-ERREF - * - * Copyright (c) International Business Machines Corp., 2009,2011 - * Author(s): Steve French (sfrench@us.ibm.com) - */ - -/* - * 0 1 2 3 4 5 6 7 8 9 0 A B C D E F 0 1 2 3 4 5 6 7 8 9 A B C D E F - * SEV C N <-------Facility--------> <------Error Status Code------> - * - * C is set if "customer defined" error, N bit is reserved and MBZ - */ - -#define STATUS_SEVERITY_SUCCESS cpu_to_le32(0x0000) -#define STATUS_SEVERITY_INFORMATIONAL cpu_to_le32(0x0001) -#define STATUS_SEVERITY_WARNING cpu_to_le32(0x0002) -#define STATUS_SEVERITY_ERROR cpu_to_le32(0x0003) - -struct ntstatus { - /* Facility is the high 12 bits of the following field */ - __le32 Facility; /* low 2 bits Severity, next is Customer, then rsrvd */ - __le32 Code; -}; - -#define STATUS_SUCCESS 0x00000000 -#define STATUS_WAIT_0 cpu_to_le32(0x00000000) -#define STATUS_WAIT_1 cpu_to_le32(0x00000001) -#define STATUS_WAIT_2 cpu_to_le32(0x00000002) -#define STATUS_WAIT_3 cpu_to_le32(0x00000003) -#define STATUS_WAIT_63 cpu_to_le32(0x0000003F) -#define STATUS_ABANDONED cpu_to_le32(0x00000080) -#define STATUS_ABANDONED_WAIT_0 cpu_to_le32(0x00000080) -#define STATUS_ABANDONED_WAIT_63 cpu_to_le32(0x000000BF) -#define STATUS_USER_APC cpu_to_le32(0x000000C0) -#define STATUS_KERNEL_APC cpu_to_le32(0x00000100) -#define STATUS_ALERTED cpu_to_le32(0x00000101) -#define STATUS_TIMEOUT cpu_to_le32(0x00000102) -#define STATUS_PENDING cpu_to_le32(0x00000103) -#define STATUS_REPARSE cpu_to_le32(0x00000104) -#define STATUS_MORE_ENTRIES cpu_to_le32(0x00000105) -#define STATUS_NOT_ALL_ASSIGNED cpu_to_le32(0x00000106) -#define STATUS_SOME_NOT_MAPPED cpu_to_le32(0x00000107) -#define STATUS_OPLOCK_BREAK_IN_PROGRESS cpu_to_le32(0x00000108) -#define STATUS_VOLUME_MOUNTED cpu_to_le32(0x00000109) -#define STATUS_RXACT_COMMITTED cpu_to_le32(0x0000010A) -#define STATUS_NOTIFY_CLEANUP cpu_to_le32(0x0000010B) -#define STATUS_NOTIFY_ENUM_DIR cpu_to_le32(0x0000010C) -#define STATUS_NO_QUOTAS_FOR_ACCOUNT cpu_to_le32(0x0000010D) -#define STATUS_PRIMARY_TRANSPORT_CONNECT_FAILED cpu_to_le32(0x0000010E) -#define STATUS_PAGE_FAULT_TRANSITION cpu_to_le32(0x00000110) -#define STATUS_PAGE_FAULT_DEMAND_ZERO cpu_to_le32(0x00000111) -#define STATUS_PAGE_FAULT_COPY_ON_WRITE cpu_to_le32(0x00000112) -#define STATUS_PAGE_FAULT_GUARD_PAGE cpu_to_le32(0x00000113) -#define STATUS_PAGE_FAULT_PAGING_FILE cpu_to_le32(0x00000114) -#define STATUS_CACHE_PAGE_LOCKED cpu_to_le32(0x00000115) -#define STATUS_CRASH_DUMP cpu_to_le32(0x00000116) -#define STATUS_BUFFER_ALL_ZEROS cpu_to_le32(0x00000117) -#define STATUS_REPARSE_OBJECT cpu_to_le32(0x00000118) -#define STATUS_RESOURCE_REQUIREMENTS_CHANGED cpu_to_le32(0x00000119) -#define STATUS_TRANSLATION_COMPLETE cpu_to_le32(0x00000120) -#define STATUS_DS_MEMBERSHIP_EVALUATED_LOCALLY cpu_to_le32(0x00000121) -#define STATUS_NOTHING_TO_TERMINATE cpu_to_le32(0x00000122) -#define STATUS_PROCESS_NOT_IN_JOB cpu_to_le32(0x00000123) -#define STATUS_PROCESS_IN_JOB cpu_to_le32(0x00000124) -#define STATUS_VOLSNAP_HIBERNATE_READY cpu_to_le32(0x00000125) -#define STATUS_FSFILTER_OP_COMPLETED_SUCCESSFULLY cpu_to_le32(0x00000126) -#define STATUS_INTERRUPT_VECTOR_ALREADY_CONNECTED cpu_to_le32(0x00000127) -#define STATUS_INTERRUPT_STILL_CONNECTED cpu_to_le32(0x00000128) -#define STATUS_PROCESS_CLONED cpu_to_le32(0x00000129) -#define STATUS_FILE_LOCKED_WITH_ONLY_READERS cpu_to_le32(0x0000012A) -#define STATUS_FILE_LOCKED_WITH_WRITERS cpu_to_le32(0x0000012B) -#define STATUS_RESOURCEMANAGER_READ_ONLY cpu_to_le32(0x00000202) -#define STATUS_WAIT_FOR_OPLOCK cpu_to_le32(0x00000367) -#define DBG_EXCEPTION_HANDLED cpu_to_le32(0x00010001) -#define DBG_CONTINUE cpu_to_le32(0x00010002) -#define STATUS_FLT_IO_COMPLETE cpu_to_le32(0x001C0001) -#define STATUS_OBJECT_NAME_EXISTS cpu_to_le32(0x40000000) -#define STATUS_THREAD_WAS_SUSPENDED cpu_to_le32(0x40000001) -#define STATUS_WORKING_SET_LIMIT_RANGE cpu_to_le32(0x40000002) -#define STATUS_IMAGE_NOT_AT_BASE cpu_to_le32(0x40000003) -#define STATUS_RXACT_STATE_CREATED cpu_to_le32(0x40000004) -#define STATUS_SEGMENT_NOTIFICATION cpu_to_le32(0x40000005) -#define STATUS_LOCAL_USER_SESSION_KEY cpu_to_le32(0x40000006) -#define STATUS_BAD_CURRENT_DIRECTORY cpu_to_le32(0x40000007) -#define STATUS_SERIAL_MORE_WRITES cpu_to_le32(0x40000008) -#define STATUS_REGISTRY_RECOVERED cpu_to_le32(0x40000009) -#define STATUS_FT_READ_RECOVERY_FROM_BACKUP cpu_to_le32(0x4000000A) -#define STATUS_FT_WRITE_RECOVERY cpu_to_le32(0x4000000B) -#define STATUS_SERIAL_COUNTER_TIMEOUT cpu_to_le32(0x4000000C) -#define STATUS_NULL_LM_PASSWORD cpu_to_le32(0x4000000D) -#define STATUS_IMAGE_MACHINE_TYPE_MISMATCH cpu_to_le32(0x4000000E) -#define STATUS_RECEIVE_PARTIAL cpu_to_le32(0x4000000F) -#define STATUS_RECEIVE_EXPEDITED cpu_to_le32(0x40000010) -#define STATUS_RECEIVE_PARTIAL_EXPEDITED cpu_to_le32(0x40000011) -#define STATUS_EVENT_DONE cpu_to_le32(0x40000012) -#define STATUS_EVENT_PENDING cpu_to_le32(0x40000013) -#define STATUS_CHECKING_FILE_SYSTEM cpu_to_le32(0x40000014) -#define STATUS_FATAL_APP_EXIT cpu_to_le32(0x40000015) -#define STATUS_PREDEFINED_HANDLE cpu_to_le32(0x40000016) -#define STATUS_WAS_UNLOCKED cpu_to_le32(0x40000017) -#define STATUS_SERVICE_NOTIFICATION cpu_to_le32(0x40000018) -#define STATUS_WAS_LOCKED cpu_to_le32(0x40000019) -#define STATUS_LOG_HARD_ERROR cpu_to_le32(0x4000001A) -#define STATUS_ALREADY_WIN32 cpu_to_le32(0x4000001B) -#define STATUS_WX86_UNSIMULATE cpu_to_le32(0x4000001C) -#define STATUS_WX86_CONTINUE cpu_to_le32(0x4000001D) -#define STATUS_WX86_SINGLE_STEP cpu_to_le32(0x4000001E) -#define STATUS_WX86_BREAKPOINT cpu_to_le32(0x4000001F) -#define STATUS_WX86_EXCEPTION_CONTINUE cpu_to_le32(0x40000020) -#define STATUS_WX86_EXCEPTION_LASTCHANCE cpu_to_le32(0x40000021) -#define STATUS_WX86_EXCEPTION_CHAIN cpu_to_le32(0x40000022) -#define STATUS_IMAGE_MACHINE_TYPE_MISMATCH_EXE cpu_to_le32(0x40000023) -#define STATUS_NO_YIELD_PERFORMED cpu_to_le32(0x40000024) -#define STATUS_TIMER_RESUME_IGNORED cpu_to_le32(0x40000025) -#define STATUS_ARBITRATION_UNHANDLED cpu_to_le32(0x40000026) -#define STATUS_CARDBUS_NOT_SUPPORTED cpu_to_le32(0x40000027) -#define STATUS_WX86_CREATEWX86TIB cpu_to_le32(0x40000028) -#define STATUS_MP_PROCESSOR_MISMATCH cpu_to_le32(0x40000029) -#define STATUS_HIBERNATED cpu_to_le32(0x4000002A) -#define STATUS_RESUME_HIBERNATION cpu_to_le32(0x4000002B) -#define STATUS_FIRMWARE_UPDATED cpu_to_le32(0x4000002C) -#define STATUS_DRIVERS_LEAKING_LOCKED_PAGES cpu_to_le32(0x4000002D) -#define STATUS_MESSAGE_RETRIEVED cpu_to_le32(0x4000002E) -#define STATUS_SYSTEM_POWERSTATE_TRANSITION cpu_to_le32(0x4000002F) -#define STATUS_ALPC_CHECK_COMPLETION_LIST cpu_to_le32(0x40000030) -#define STATUS_SYSTEM_POWERSTATE_COMPLEX_TRANSITION cpu_to_le32(0x40000031) -#define STATUS_ACCESS_AUDIT_BY_POLICY cpu_to_le32(0x40000032) -#define STATUS_ABANDON_HIBERFILE cpu_to_le32(0x40000033) -#define STATUS_BIZRULES_NOT_ENABLED cpu_to_le32(0x40000034) -#define STATUS_WAKE_SYSTEM cpu_to_le32(0x40000294) -#define STATUS_DS_SHUTTING_DOWN cpu_to_le32(0x40000370) -#define DBG_REPLY_LATER cpu_to_le32(0x40010001) -#define DBG_UNABLE_TO_PROVIDE_HANDLE cpu_to_le32(0x40010002) -#define DBG_TERMINATE_THREAD cpu_to_le32(0x40010003) -#define DBG_TERMINATE_PROCESS cpu_to_le32(0x40010004) -#define DBG_CONTROL_C cpu_to_le32(0x40010005) -#define DBG_PRINTEXCEPTION_C cpu_to_le32(0x40010006) -#define DBG_RIPEXCEPTION cpu_to_le32(0x40010007) -#define DBG_CONTROL_BREAK cpu_to_le32(0x40010008) -#define DBG_COMMAND_EXCEPTION cpu_to_le32(0x40010009) -#define RPC_NT_UUID_LOCAL_ONLY cpu_to_le32(0x40020056) -#define RPC_NT_SEND_INCOMPLETE cpu_to_le32(0x400200AF) -#define STATUS_CTX_CDM_CONNECT cpu_to_le32(0x400A0004) -#define STATUS_CTX_CDM_DISCONNECT cpu_to_le32(0x400A0005) -#define STATUS_SXS_RELEASE_ACTIVATION_CONTEXT cpu_to_le32(0x4015000D) -#define STATUS_RECOVERY_NOT_NEEDED cpu_to_le32(0x40190034) -#define STATUS_RM_ALREADY_STARTED cpu_to_le32(0x40190035) -#define STATUS_LOG_NO_RESTART cpu_to_le32(0x401A000C) -#define STATUS_VIDEO_DRIVER_DEBUG_REPORT_REQUEST cpu_to_le32(0x401B00EC) -#define STATUS_GRAPHICS_PARTIAL_DATA_POPULATED cpu_to_le32(0x401E000A) -#define STATUS_GRAPHICS_DRIVER_MISMATCH cpu_to_le32(0x401E0117) -#define STATUS_GRAPHICS_MODE_NOT_PINNED cpu_to_le32(0x401E0307) -#define STATUS_GRAPHICS_NO_PREFERRED_MODE cpu_to_le32(0x401E031E) -#define STATUS_GRAPHICS_DATASET_IS_EMPTY cpu_to_le32(0x401E034B) -#define STATUS_GRAPHICS_NO_MORE_ELEMENTS_IN_DATASET cpu_to_le32(0x401E034C) -#define STATUS_GRAPHICS_PATH_CONTENT_GEOMETRY_TRANSFORMATION_NOT_PINNED \ - cpu_to_le32(0x401E0351) -#define STATUS_GRAPHICS_UNKNOWN_CHILD_STATUS cpu_to_le32(0x401E042F) -#define STATUS_GRAPHICS_LEADLINK_START_DEFERRED cpu_to_le32(0x401E0437) -#define STATUS_GRAPHICS_POLLING_TOO_FREQUENTLY cpu_to_le32(0x401E0439) -#define STATUS_GRAPHICS_START_DEFERRED cpu_to_le32(0x401E043A) -#define STATUS_NDIS_INDICATION_REQUIRED cpu_to_le32(0x40230001) -#define STATUS_GUARD_PAGE_VIOLATION cpu_to_le32(0x80000001) -#define STATUS_DATATYPE_MISALIGNMENT cpu_to_le32(0x80000002) -#define STATUS_BREAKPOINT cpu_to_le32(0x80000003) -#define STATUS_SINGLE_STEP cpu_to_le32(0x80000004) -#define STATUS_BUFFER_OVERFLOW cpu_to_le32(0x80000005) -#define STATUS_NO_MORE_FILES cpu_to_le32(0x80000006) -#define STATUS_WAKE_SYSTEM_DEBUGGER cpu_to_le32(0x80000007) -#define STATUS_HANDLES_CLOSED cpu_to_le32(0x8000000A) -#define STATUS_NO_INHERITANCE cpu_to_le32(0x8000000B) -#define STATUS_GUID_SUBSTITUTION_MADE cpu_to_le32(0x8000000C) -#define STATUS_PARTIAL_COPY cpu_to_le32(0x8000000D) -#define STATUS_DEVICE_PAPER_EMPTY cpu_to_le32(0x8000000E) -#define STATUS_DEVICE_POWERED_OFF cpu_to_le32(0x8000000F) -#define STATUS_DEVICE_OFF_LINE cpu_to_le32(0x80000010) -#define STATUS_DEVICE_BUSY cpu_to_le32(0x80000011) -#define STATUS_NO_MORE_EAS cpu_to_le32(0x80000012) -#define STATUS_INVALID_EA_NAME cpu_to_le32(0x80000013) -#define STATUS_EA_LIST_INCONSISTENT cpu_to_le32(0x80000014) -#define STATUS_INVALID_EA_FLAG cpu_to_le32(0x80000015) -#define STATUS_VERIFY_REQUIRED cpu_to_le32(0x80000016) -#define STATUS_EXTRANEOUS_INFORMATION cpu_to_le32(0x80000017) -#define STATUS_RXACT_COMMIT_NECESSARY cpu_to_le32(0x80000018) -#define STATUS_NO_MORE_ENTRIES cpu_to_le32(0x8000001A) -#define STATUS_FILEMARK_DETECTED cpu_to_le32(0x8000001B) -#define STATUS_MEDIA_CHANGED cpu_to_le32(0x8000001C) -#define STATUS_BUS_RESET cpu_to_le32(0x8000001D) -#define STATUS_END_OF_MEDIA cpu_to_le32(0x8000001E) -#define STATUS_BEGINNING_OF_MEDIA cpu_to_le32(0x8000001F) -#define STATUS_MEDIA_CHECK cpu_to_le32(0x80000020) -#define STATUS_SETMARK_DETECTED cpu_to_le32(0x80000021) -#define STATUS_NO_DATA_DETECTED cpu_to_le32(0x80000022) -#define STATUS_REDIRECTOR_HAS_OPEN_HANDLES cpu_to_le32(0x80000023) -#define STATUS_SERVER_HAS_OPEN_HANDLES cpu_to_le32(0x80000024) -#define STATUS_ALREADY_DISCONNECTED cpu_to_le32(0x80000025) -#define STATUS_LONGJUMP cpu_to_le32(0x80000026) -#define STATUS_CLEANER_CARTRIDGE_INSTALLED cpu_to_le32(0x80000027) -#define STATUS_PLUGPLAY_QUERY_VETOED cpu_to_le32(0x80000028) -#define STATUS_UNWIND_CONSOLIDATE cpu_to_le32(0x80000029) -#define STATUS_REGISTRY_HIVE_RECOVERED cpu_to_le32(0x8000002A) -#define STATUS_DLL_MIGHT_BE_INSECURE cpu_to_le32(0x8000002B) -#define STATUS_DLL_MIGHT_BE_INCOMPATIBLE cpu_to_le32(0x8000002C) -#define STATUS_STOPPED_ON_SYMLINK cpu_to_le32(0x8000002D) -#define STATUS_DEVICE_REQUIRES_CLEANING cpu_to_le32(0x80000288) -#define STATUS_DEVICE_DOOR_OPEN cpu_to_le32(0x80000289) -#define STATUS_DATA_LOST_REPAIR cpu_to_le32(0x80000803) -#define DBG_EXCEPTION_NOT_HANDLED cpu_to_le32(0x80010001) -#define STATUS_CLUSTER_NODE_ALREADY_UP cpu_to_le32(0x80130001) -#define STATUS_CLUSTER_NODE_ALREADY_DOWN cpu_to_le32(0x80130002) -#define STATUS_CLUSTER_NETWORK_ALREADY_ONLINE cpu_to_le32(0x80130003) -#define STATUS_CLUSTER_NETWORK_ALREADY_OFFLINE cpu_to_le32(0x80130004) -#define STATUS_CLUSTER_NODE_ALREADY_MEMBER cpu_to_le32(0x80130005) -#define STATUS_COULD_NOT_RESIZE_LOG cpu_to_le32(0x80190009) -#define STATUS_NO_TXF_METADATA cpu_to_le32(0x80190029) -#define STATUS_CANT_RECOVER_WITH_HANDLE_OPEN cpu_to_le32(0x80190031) -#define STATUS_TXF_METADATA_ALREADY_PRESENT cpu_to_le32(0x80190041) -#define STATUS_TRANSACTION_SCOPE_CALLBACKS_NOT_SET cpu_to_le32(0x80190042) -#define STATUS_VIDEO_HUNG_DISPLAY_DRIVER_THREAD_RECOVERED \ - cpu_to_le32(0x801B00EB) -#define STATUS_FLT_BUFFER_TOO_SMALL cpu_to_le32(0x801C0001) -#define STATUS_FVE_PARTIAL_METADATA cpu_to_le32(0x80210001) -#define STATUS_UNSUCCESSFUL cpu_to_le32(0xC0000001) -#define STATUS_NOT_IMPLEMENTED cpu_to_le32(0xC0000002) -#define STATUS_INVALID_INFO_CLASS cpu_to_le32(0xC0000003) -#define STATUS_INFO_LENGTH_MISMATCH cpu_to_le32(0xC0000004) -#define STATUS_ACCESS_VIOLATION cpu_to_le32(0xC0000005) -#define STATUS_IN_PAGE_ERROR cpu_to_le32(0xC0000006) -#define STATUS_PAGEFILE_QUOTA cpu_to_le32(0xC0000007) -#define STATUS_INVALID_HANDLE cpu_to_le32(0xC0000008) -#define STATUS_BAD_INITIAL_STACK cpu_to_le32(0xC0000009) -#define STATUS_BAD_INITIAL_PC cpu_to_le32(0xC000000A) -#define STATUS_INVALID_CID cpu_to_le32(0xC000000B) -#define STATUS_TIMER_NOT_CANCELED cpu_to_le32(0xC000000C) -#define STATUS_INVALID_PARAMETER cpu_to_le32(0xC000000D) -#define STATUS_NO_SUCH_DEVICE cpu_to_le32(0xC000000E) -#define STATUS_NO_SUCH_FILE cpu_to_le32(0xC000000F) -#define STATUS_INVALID_DEVICE_REQUEST cpu_to_le32(0xC0000010) -#define STATUS_END_OF_FILE cpu_to_le32(0xC0000011) -#define STATUS_WRONG_VOLUME cpu_to_le32(0xC0000012) -#define STATUS_NO_MEDIA_IN_DEVICE cpu_to_le32(0xC0000013) -#define STATUS_UNRECOGNIZED_MEDIA cpu_to_le32(0xC0000014) -#define STATUS_NONEXISTENT_SECTOR cpu_to_le32(0xC0000015) -#define STATUS_MORE_PROCESSING_REQUIRED cpu_to_le32(0xC0000016) -#define STATUS_NO_MEMORY cpu_to_le32(0xC0000017) -#define STATUS_CONFLICTING_ADDRESSES cpu_to_le32(0xC0000018) -#define STATUS_NOT_MAPPED_VIEW cpu_to_le32(0xC0000019) -#define STATUS_UNABLE_TO_FREE_VM cpu_to_le32(0xC000001A) -#define STATUS_UNABLE_TO_DELETE_SECTION cpu_to_le32(0xC000001B) -#define STATUS_INVALID_SYSTEM_SERVICE cpu_to_le32(0xC000001C) -#define STATUS_ILLEGAL_INSTRUCTION cpu_to_le32(0xC000001D) -#define STATUS_INVALID_LOCK_SEQUENCE cpu_to_le32(0xC000001E) -#define STATUS_INVALID_VIEW_SIZE cpu_to_le32(0xC000001F) -#define STATUS_INVALID_FILE_FOR_SECTION cpu_to_le32(0xC0000020) -#define STATUS_ALREADY_COMMITTED cpu_to_le32(0xC0000021) -#define STATUS_ACCESS_DENIED cpu_to_le32(0xC0000022) -#define STATUS_BUFFER_TOO_SMALL cpu_to_le32(0xC0000023) -#define STATUS_OBJECT_TYPE_MISMATCH cpu_to_le32(0xC0000024) -#define STATUS_NONCONTINUABLE_EXCEPTION cpu_to_le32(0xC0000025) -#define STATUS_INVALID_DISPOSITION cpu_to_le32(0xC0000026) -#define STATUS_UNWIND cpu_to_le32(0xC0000027) -#define STATUS_BAD_STACK cpu_to_le32(0xC0000028) -#define STATUS_INVALID_UNWIND_TARGET cpu_to_le32(0xC0000029) -#define STATUS_NOT_LOCKED cpu_to_le32(0xC000002A) -#define STATUS_PARITY_ERROR cpu_to_le32(0xC000002B) -#define STATUS_UNABLE_TO_DECOMMIT_VM cpu_to_le32(0xC000002C) -#define STATUS_NOT_COMMITTED cpu_to_le32(0xC000002D) -#define STATUS_INVALID_PORT_ATTRIBUTES cpu_to_le32(0xC000002E) -#define STATUS_PORT_MESSAGE_TOO_LONG cpu_to_le32(0xC000002F) -#define STATUS_INVALID_PARAMETER_MIX cpu_to_le32(0xC0000030) -#define STATUS_INVALID_QUOTA_LOWER cpu_to_le32(0xC0000031) -#define STATUS_DISK_CORRUPT_ERROR cpu_to_le32(0xC0000032) -#define STATUS_OBJECT_NAME_INVALID cpu_to_le32(0xC0000033) -#define STATUS_OBJECT_NAME_NOT_FOUND cpu_to_le32(0xC0000034) -#define STATUS_OBJECT_NAME_COLLISION cpu_to_le32(0xC0000035) -#define STATUS_PORT_DISCONNECTED cpu_to_le32(0xC0000037) -#define STATUS_DEVICE_ALREADY_ATTACHED cpu_to_le32(0xC0000038) -#define STATUS_OBJECT_PATH_INVALID cpu_to_le32(0xC0000039) -#define STATUS_OBJECT_PATH_NOT_FOUND cpu_to_le32(0xC000003A) -#define STATUS_OBJECT_PATH_SYNTAX_BAD cpu_to_le32(0xC000003B) -#define STATUS_DATA_OVERRUN cpu_to_le32(0xC000003C) -#define STATUS_DATA_LATE_ERROR cpu_to_le32(0xC000003D) -#define STATUS_DATA_ERROR cpu_to_le32(0xC000003E) -#define STATUS_CRC_ERROR cpu_to_le32(0xC000003F) -#define STATUS_SECTION_TOO_BIG cpu_to_le32(0xC0000040) -#define STATUS_PORT_CONNECTION_REFUSED cpu_to_le32(0xC0000041) -#define STATUS_INVALID_PORT_HANDLE cpu_to_le32(0xC0000042) -#define STATUS_SHARING_VIOLATION cpu_to_le32(0xC0000043) -#define STATUS_QUOTA_EXCEEDED cpu_to_le32(0xC0000044) -#define STATUS_INVALID_PAGE_PROTECTION cpu_to_le32(0xC0000045) -#define STATUS_MUTANT_NOT_OWNED cpu_to_le32(0xC0000046) -#define STATUS_SEMAPHORE_LIMIT_EXCEEDED cpu_to_le32(0xC0000047) -#define STATUS_PORT_ALREADY_SET cpu_to_le32(0xC0000048) -#define STATUS_SECTION_NOT_IMAGE cpu_to_le32(0xC0000049) -#define STATUS_SUSPEND_COUNT_EXCEEDED cpu_to_le32(0xC000004A) -#define STATUS_THREAD_IS_TERMINATING cpu_to_le32(0xC000004B) -#define STATUS_BAD_WORKING_SET_LIMIT cpu_to_le32(0xC000004C) -#define STATUS_INCOMPATIBLE_FILE_MAP cpu_to_le32(0xC000004D) -#define STATUS_SECTION_PROTECTION cpu_to_le32(0xC000004E) -#define STATUS_EAS_NOT_SUPPORTED cpu_to_le32(0xC000004F) -#define STATUS_EA_TOO_LARGE cpu_to_le32(0xC0000050) -#define STATUS_NONEXISTENT_EA_ENTRY cpu_to_le32(0xC0000051) -#define STATUS_NO_EAS_ON_FILE cpu_to_le32(0xC0000052) -#define STATUS_EA_CORRUPT_ERROR cpu_to_le32(0xC0000053) -#define STATUS_FILE_LOCK_CONFLICT cpu_to_le32(0xC0000054) -#define STATUS_LOCK_NOT_GRANTED cpu_to_le32(0xC0000055) -#define STATUS_DELETE_PENDING cpu_to_le32(0xC0000056) -#define STATUS_CTL_FILE_NOT_SUPPORTED cpu_to_le32(0xC0000057) -#define STATUS_UNKNOWN_REVISION cpu_to_le32(0xC0000058) -#define STATUS_REVISION_MISMATCH cpu_to_le32(0xC0000059) -#define STATUS_INVALID_OWNER cpu_to_le32(0xC000005A) -#define STATUS_INVALID_PRIMARY_GROUP cpu_to_le32(0xC000005B) -#define STATUS_NO_IMPERSONATION_TOKEN cpu_to_le32(0xC000005C) -#define STATUS_CANT_DISABLE_MANDATORY cpu_to_le32(0xC000005D) -#define STATUS_NO_LOGON_SERVERS cpu_to_le32(0xC000005E) -#define STATUS_NO_SUCH_LOGON_SESSION cpu_to_le32(0xC000005F) -#define STATUS_NO_SUCH_PRIVILEGE cpu_to_le32(0xC0000060) -#define STATUS_PRIVILEGE_NOT_HELD cpu_to_le32(0xC0000061) -#define STATUS_INVALID_ACCOUNT_NAME cpu_to_le32(0xC0000062) -#define STATUS_USER_EXISTS cpu_to_le32(0xC0000063) -#define STATUS_NO_SUCH_USER cpu_to_le32(0xC0000064) -#define STATUS_GROUP_EXISTS cpu_to_le32(0xC0000065) -#define STATUS_NO_SUCH_GROUP cpu_to_le32(0xC0000066) -#define STATUS_MEMBER_IN_GROUP cpu_to_le32(0xC0000067) -#define STATUS_MEMBER_NOT_IN_GROUP cpu_to_le32(0xC0000068) -#define STATUS_LAST_ADMIN cpu_to_le32(0xC0000069) -#define STATUS_WRONG_PASSWORD cpu_to_le32(0xC000006A) -#define STATUS_ILL_FORMED_PASSWORD cpu_to_le32(0xC000006B) -#define STATUS_PASSWORD_RESTRICTION cpu_to_le32(0xC000006C) -#define STATUS_LOGON_FAILURE cpu_to_le32(0xC000006D) -#define STATUS_ACCOUNT_RESTRICTION cpu_to_le32(0xC000006E) -#define STATUS_INVALID_LOGON_HOURS cpu_to_le32(0xC000006F) -#define STATUS_INVALID_WORKSTATION cpu_to_le32(0xC0000070) -#define STATUS_PASSWORD_EXPIRED cpu_to_le32(0xC0000071) -#define STATUS_ACCOUNT_DISABLED cpu_to_le32(0xC0000072) -#define STATUS_NONE_MAPPED cpu_to_le32(0xC0000073) -#define STATUS_TOO_MANY_LUIDS_REQUESTED cpu_to_le32(0xC0000074) -#define STATUS_LUIDS_EXHAUSTED cpu_to_le32(0xC0000075) -#define STATUS_INVALID_SUB_AUTHORITY cpu_to_le32(0xC0000076) -#define STATUS_INVALID_ACL cpu_to_le32(0xC0000077) -#define STATUS_INVALID_SID cpu_to_le32(0xC0000078) -#define STATUS_INVALID_SECURITY_DESCR cpu_to_le32(0xC0000079) -#define STATUS_PROCEDURE_NOT_FOUND cpu_to_le32(0xC000007A) -#define STATUS_INVALID_IMAGE_FORMAT cpu_to_le32(0xC000007B) -#define STATUS_NO_TOKEN cpu_to_le32(0xC000007C) -#define STATUS_BAD_INHERITANCE_ACL cpu_to_le32(0xC000007D) -#define STATUS_RANGE_NOT_LOCKED cpu_to_le32(0xC000007E) -#define STATUS_DISK_FULL cpu_to_le32(0xC000007F) -#define STATUS_SERVER_DISABLED cpu_to_le32(0xC0000080) -#define STATUS_SERVER_NOT_DISABLED cpu_to_le32(0xC0000081) -#define STATUS_TOO_MANY_GUIDS_REQUESTED cpu_to_le32(0xC0000082) -#define STATUS_GUIDS_EXHAUSTED cpu_to_le32(0xC0000083) -#define STATUS_INVALID_ID_AUTHORITY cpu_to_le32(0xC0000084) -#define STATUS_AGENTS_EXHAUSTED cpu_to_le32(0xC0000085) -#define STATUS_INVALID_VOLUME_LABEL cpu_to_le32(0xC0000086) -#define STATUS_SECTION_NOT_EXTENDED cpu_to_le32(0xC0000087) -#define STATUS_NOT_MAPPED_DATA cpu_to_le32(0xC0000088) -#define STATUS_RESOURCE_DATA_NOT_FOUND cpu_to_le32(0xC0000089) -#define STATUS_RESOURCE_TYPE_NOT_FOUND cpu_to_le32(0xC000008A) -#define STATUS_RESOURCE_NAME_NOT_FOUND cpu_to_le32(0xC000008B) -#define STATUS_ARRAY_BOUNDS_EXCEEDED cpu_to_le32(0xC000008C) -#define STATUS_FLOAT_DENORMAL_OPERAND cpu_to_le32(0xC000008D) -#define STATUS_FLOAT_DIVIDE_BY_ZERO cpu_to_le32(0xC000008E) -#define STATUS_FLOAT_INEXACT_RESULT cpu_to_le32(0xC000008F) -#define STATUS_FLOAT_INVALID_OPERATION cpu_to_le32(0xC0000090) -#define STATUS_FLOAT_OVERFLOW cpu_to_le32(0xC0000091) -#define STATUS_FLOAT_STACK_CHECK cpu_to_le32(0xC0000092) -#define STATUS_FLOAT_UNDERFLOW cpu_to_le32(0xC0000093) -#define STATUS_INTEGER_DIVIDE_BY_ZERO cpu_to_le32(0xC0000094) -#define STATUS_INTEGER_OVERFLOW cpu_to_le32(0xC0000095) -#define STATUS_PRIVILEGED_INSTRUCTION cpu_to_le32(0xC0000096) -#define STATUS_TOO_MANY_PAGING_FILES cpu_to_le32(0xC0000097) -#define STATUS_FILE_INVALID cpu_to_le32(0xC0000098) -#define STATUS_ALLOTTED_SPACE_EXCEEDED cpu_to_le32(0xC0000099) -#define STATUS_INSUFFICIENT_RESOURCES cpu_to_le32(0xC000009A) -#define STATUS_DFS_EXIT_PATH_FOUND cpu_to_le32(0xC000009B) -#define STATUS_DEVICE_DATA_ERROR cpu_to_le32(0xC000009C) -#define STATUS_DEVICE_NOT_CONNECTED cpu_to_le32(0xC000009D) -#define STATUS_DEVICE_POWER_FAILURE cpu_to_le32(0xC000009E) -#define STATUS_FREE_VM_NOT_AT_BASE cpu_to_le32(0xC000009F) -#define STATUS_MEMORY_NOT_ALLOCATED cpu_to_le32(0xC00000A0) -#define STATUS_WORKING_SET_QUOTA cpu_to_le32(0xC00000A1) -#define STATUS_MEDIA_WRITE_PROTECTED cpu_to_le32(0xC00000A2) -#define STATUS_DEVICE_NOT_READY cpu_to_le32(0xC00000A3) -#define STATUS_INVALID_GROUP_ATTRIBUTES cpu_to_le32(0xC00000A4) -#define STATUS_BAD_IMPERSONATION_LEVEL cpu_to_le32(0xC00000A5) -#define STATUS_CANT_OPEN_ANONYMOUS cpu_to_le32(0xC00000A6) -#define STATUS_BAD_VALIDATION_CLASS cpu_to_le32(0xC00000A7) -#define STATUS_BAD_TOKEN_TYPE cpu_to_le32(0xC00000A8) -#define STATUS_BAD_MASTER_BOOT_RECORD cpu_to_le32(0xC00000A9) -#define STATUS_INSTRUCTION_MISALIGNMENT cpu_to_le32(0xC00000AA) -#define STATUS_INSTANCE_NOT_AVAILABLE cpu_to_le32(0xC00000AB) -#define STATUS_PIPE_NOT_AVAILABLE cpu_to_le32(0xC00000AC) -#define STATUS_INVALID_PIPE_STATE cpu_to_le32(0xC00000AD) -#define STATUS_PIPE_BUSY cpu_to_le32(0xC00000AE) -#define STATUS_ILLEGAL_FUNCTION cpu_to_le32(0xC00000AF) -#define STATUS_PIPE_DISCONNECTED cpu_to_le32(0xC00000B0) -#define STATUS_PIPE_CLOSING cpu_to_le32(0xC00000B1) -#define STATUS_PIPE_CONNECTED cpu_to_le32(0xC00000B2) -#define STATUS_PIPE_LISTENING cpu_to_le32(0xC00000B3) -#define STATUS_INVALID_READ_MODE cpu_to_le32(0xC00000B4) -#define STATUS_IO_TIMEOUT cpu_to_le32(0xC00000B5) -#define STATUS_FILE_FORCED_CLOSED cpu_to_le32(0xC00000B6) -#define STATUS_PROFILING_NOT_STARTED cpu_to_le32(0xC00000B7) -#define STATUS_PROFILING_NOT_STOPPED cpu_to_le32(0xC00000B8) -#define STATUS_COULD_NOT_INTERPRET cpu_to_le32(0xC00000B9) -#define STATUS_FILE_IS_A_DIRECTORY cpu_to_le32(0xC00000BA) -#define STATUS_NOT_SUPPORTED cpu_to_le32(0xC00000BB) -#define STATUS_REMOTE_NOT_LISTENING cpu_to_le32(0xC00000BC) -#define STATUS_DUPLICATE_NAME cpu_to_le32(0xC00000BD) -#define STATUS_BAD_NETWORK_PATH cpu_to_le32(0xC00000BE) -#define STATUS_NETWORK_BUSY cpu_to_le32(0xC00000BF) -#define STATUS_DEVICE_DOES_NOT_EXIST cpu_to_le32(0xC00000C0) -#define STATUS_TOO_MANY_COMMANDS cpu_to_le32(0xC00000C1) -#define STATUS_ADAPTER_HARDWARE_ERROR cpu_to_le32(0xC00000C2) -#define STATUS_INVALID_NETWORK_RESPONSE cpu_to_le32(0xC00000C3) -#define STATUS_UNEXPECTED_NETWORK_ERROR cpu_to_le32(0xC00000C4) -#define STATUS_BAD_REMOTE_ADAPTER cpu_to_le32(0xC00000C5) -#define STATUS_PRINT_QUEUE_FULL cpu_to_le32(0xC00000C6) -#define STATUS_NO_SPOOL_SPACE cpu_to_le32(0xC00000C7) -#define STATUS_PRINT_CANCELLED cpu_to_le32(0xC00000C8) -#define STATUS_NETWORK_NAME_DELETED cpu_to_le32(0xC00000C9) -#define STATUS_NETWORK_ACCESS_DENIED cpu_to_le32(0xC00000CA) -#define STATUS_BAD_DEVICE_TYPE cpu_to_le32(0xC00000CB) -#define STATUS_BAD_NETWORK_NAME cpu_to_le32(0xC00000CC) -#define STATUS_TOO_MANY_NAMES cpu_to_le32(0xC00000CD) -#define STATUS_TOO_MANY_SESSIONS cpu_to_le32(0xC00000CE) -#define STATUS_SHARING_PAUSED cpu_to_le32(0xC00000CF) -#define STATUS_REQUEST_NOT_ACCEPTED cpu_to_le32(0xC00000D0) -#define STATUS_REDIRECTOR_PAUSED cpu_to_le32(0xC00000D1) -#define STATUS_NET_WRITE_FAULT cpu_to_le32(0xC00000D2) -#define STATUS_PROFILING_AT_LIMIT cpu_to_le32(0xC00000D3) -#define STATUS_NOT_SAME_DEVICE cpu_to_le32(0xC00000D4) -#define STATUS_FILE_RENAMED cpu_to_le32(0xC00000D5) -#define STATUS_VIRTUAL_CIRCUIT_CLOSED cpu_to_le32(0xC00000D6) -#define STATUS_NO_SECURITY_ON_OBJECT cpu_to_le32(0xC00000D7) -#define STATUS_CANT_WAIT cpu_to_le32(0xC00000D8) -#define STATUS_PIPE_EMPTY cpu_to_le32(0xC00000D9) -#define STATUS_CANT_ACCESS_DOMAIN_INFO cpu_to_le32(0xC00000DA) -#define STATUS_CANT_TERMINATE_SELF cpu_to_le32(0xC00000DB) -#define STATUS_INVALID_SERVER_STATE cpu_to_le32(0xC00000DC) -#define STATUS_INVALID_DOMAIN_STATE cpu_to_le32(0xC00000DD) -#define STATUS_INVALID_DOMAIN_ROLE cpu_to_le32(0xC00000DE) -#define STATUS_NO_SUCH_DOMAIN cpu_to_le32(0xC00000DF) -#define STATUS_DOMAIN_EXISTS cpu_to_le32(0xC00000E0) -#define STATUS_DOMAIN_LIMIT_EXCEEDED cpu_to_le32(0xC00000E1) -#define STATUS_OPLOCK_NOT_GRANTED cpu_to_le32(0xC00000E2) -#define STATUS_INVALID_OPLOCK_PROTOCOL cpu_to_le32(0xC00000E3) -#define STATUS_INTERNAL_DB_CORRUPTION cpu_to_le32(0xC00000E4) -#define STATUS_INTERNAL_ERROR cpu_to_le32(0xC00000E5) -#define STATUS_GENERIC_NOT_MAPPED cpu_to_le32(0xC00000E6) -#define STATUS_BAD_DESCRIPTOR_FORMAT cpu_to_le32(0xC00000E7) -#define STATUS_INVALID_USER_BUFFER cpu_to_le32(0xC00000E8) -#define STATUS_UNEXPECTED_IO_ERROR cpu_to_le32(0xC00000E9) -#define STATUS_UNEXPECTED_MM_CREATE_ERR cpu_to_le32(0xC00000EA) -#define STATUS_UNEXPECTED_MM_MAP_ERROR cpu_to_le32(0xC00000EB) -#define STATUS_UNEXPECTED_MM_EXTEND_ERR cpu_to_le32(0xC00000EC) -#define STATUS_NOT_LOGON_PROCESS cpu_to_le32(0xC00000ED) -#define STATUS_LOGON_SESSION_EXISTS cpu_to_le32(0xC00000EE) -#define STATUS_INVALID_PARAMETER_1 cpu_to_le32(0xC00000EF) -#define STATUS_INVALID_PARAMETER_2 cpu_to_le32(0xC00000F0) -#define STATUS_INVALID_PARAMETER_3 cpu_to_le32(0xC00000F1) -#define STATUS_INVALID_PARAMETER_4 cpu_to_le32(0xC00000F2) -#define STATUS_INVALID_PARAMETER_5 cpu_to_le32(0xC00000F3) -#define STATUS_INVALID_PARAMETER_6 cpu_to_le32(0xC00000F4) -#define STATUS_INVALID_PARAMETER_7 cpu_to_le32(0xC00000F5) -#define STATUS_INVALID_PARAMETER_8 cpu_to_le32(0xC00000F6) -#define STATUS_INVALID_PARAMETER_9 cpu_to_le32(0xC00000F7) -#define STATUS_INVALID_PARAMETER_10 cpu_to_le32(0xC00000F8) -#define STATUS_INVALID_PARAMETER_11 cpu_to_le32(0xC00000F9) -#define STATUS_INVALID_PARAMETER_12 cpu_to_le32(0xC00000FA) -#define STATUS_REDIRECTOR_NOT_STARTED cpu_to_le32(0xC00000FB) -#define STATUS_REDIRECTOR_STARTED cpu_to_le32(0xC00000FC) -#define STATUS_STACK_OVERFLOW cpu_to_le32(0xC00000FD) -#define STATUS_NO_SUCH_PACKAGE cpu_to_le32(0xC00000FE) -#define STATUS_BAD_FUNCTION_TABLE cpu_to_le32(0xC00000FF) -#define STATUS_VARIABLE_NOT_FOUND cpu_to_le32(0xC0000100) -#define STATUS_DIRECTORY_NOT_EMPTY cpu_to_le32(0xC0000101) -#define STATUS_FILE_CORRUPT_ERROR cpu_to_le32(0xC0000102) -#define STATUS_NOT_A_DIRECTORY cpu_to_le32(0xC0000103) -#define STATUS_BAD_LOGON_SESSION_STATE cpu_to_le32(0xC0000104) -#define STATUS_LOGON_SESSION_COLLISION cpu_to_le32(0xC0000105) -#define STATUS_NAME_TOO_LONG cpu_to_le32(0xC0000106) -#define STATUS_FILES_OPEN cpu_to_le32(0xC0000107) -#define STATUS_CONNECTION_IN_USE cpu_to_le32(0xC0000108) -#define STATUS_MESSAGE_NOT_FOUND cpu_to_le32(0xC0000109) -#define STATUS_PROCESS_IS_TERMINATING cpu_to_le32(0xC000010A) -#define STATUS_INVALID_LOGON_TYPE cpu_to_le32(0xC000010B) -#define STATUS_NO_GUID_TRANSLATION cpu_to_le32(0xC000010C) -#define STATUS_CANNOT_IMPERSONATE cpu_to_le32(0xC000010D) -#define STATUS_IMAGE_ALREADY_LOADED cpu_to_le32(0xC000010E) -#define STATUS_ABIOS_NOT_PRESENT cpu_to_le32(0xC000010F) -#define STATUS_ABIOS_LID_NOT_EXIST cpu_to_le32(0xC0000110) -#define STATUS_ABIOS_LID_ALREADY_OWNED cpu_to_le32(0xC0000111) -#define STATUS_ABIOS_NOT_LID_OWNER cpu_to_le32(0xC0000112) -#define STATUS_ABIOS_INVALID_COMMAND cpu_to_le32(0xC0000113) -#define STATUS_ABIOS_INVALID_LID cpu_to_le32(0xC0000114) -#define STATUS_ABIOS_SELECTOR_NOT_AVAILABLE cpu_to_le32(0xC0000115) -#define STATUS_ABIOS_INVALID_SELECTOR cpu_to_le32(0xC0000116) -#define STATUS_NO_LDT cpu_to_le32(0xC0000117) -#define STATUS_INVALID_LDT_SIZE cpu_to_le32(0xC0000118) -#define STATUS_INVALID_LDT_OFFSET cpu_to_le32(0xC0000119) -#define STATUS_INVALID_LDT_DESCRIPTOR cpu_to_le32(0xC000011A) -#define STATUS_INVALID_IMAGE_NE_FORMAT cpu_to_le32(0xC000011B) -#define STATUS_RXACT_INVALID_STATE cpu_to_le32(0xC000011C) -#define STATUS_RXACT_COMMIT_FAILURE cpu_to_le32(0xC000011D) -#define STATUS_MAPPED_FILE_SIZE_ZERO cpu_to_le32(0xC000011E) -#define STATUS_TOO_MANY_OPENED_FILES cpu_to_le32(0xC000011F) -#define STATUS_CANCELLED cpu_to_le32(0xC0000120) -#define STATUS_CANNOT_DELETE cpu_to_le32(0xC0000121) -#define STATUS_INVALID_COMPUTER_NAME cpu_to_le32(0xC0000122) -#define STATUS_FILE_DELETED cpu_to_le32(0xC0000123) -#define STATUS_SPECIAL_ACCOUNT cpu_to_le32(0xC0000124) -#define STATUS_SPECIAL_GROUP cpu_to_le32(0xC0000125) -#define STATUS_SPECIAL_USER cpu_to_le32(0xC0000126) -#define STATUS_MEMBERS_PRIMARY_GROUP cpu_to_le32(0xC0000127) -#define STATUS_FILE_CLOSED cpu_to_le32(0xC0000128) -#define STATUS_TOO_MANY_THREADS cpu_to_le32(0xC0000129) -#define STATUS_THREAD_NOT_IN_PROCESS cpu_to_le32(0xC000012A) -#define STATUS_TOKEN_ALREADY_IN_USE cpu_to_le32(0xC000012B) -#define STATUS_PAGEFILE_QUOTA_EXCEEDED cpu_to_le32(0xC000012C) -#define STATUS_COMMITMENT_LIMIT cpu_to_le32(0xC000012D) -#define STATUS_INVALID_IMAGE_LE_FORMAT cpu_to_le32(0xC000012E) -#define STATUS_INVALID_IMAGE_NOT_MZ cpu_to_le32(0xC000012F) -#define STATUS_INVALID_IMAGE_PROTECT cpu_to_le32(0xC0000130) -#define STATUS_INVALID_IMAGE_WIN_16 cpu_to_le32(0xC0000131) -#define STATUS_LOGON_SERVER_CONFLICT cpu_to_le32(0xC0000132) -#define STATUS_TIME_DIFFERENCE_AT_DC cpu_to_le32(0xC0000133) -#define STATUS_SYNCHRONIZATION_REQUIRED cpu_to_le32(0xC0000134) -#define STATUS_DLL_NOT_FOUND cpu_to_le32(0xC0000135) -#define STATUS_OPEN_FAILED cpu_to_le32(0xC0000136) -#define STATUS_IO_PRIVILEGE_FAILED cpu_to_le32(0xC0000137) -#define STATUS_ORDINAL_NOT_FOUND cpu_to_le32(0xC0000138) -#define STATUS_ENTRYPOINT_NOT_FOUND cpu_to_le32(0xC0000139) -#define STATUS_CONTROL_C_EXIT cpu_to_le32(0xC000013A) -#define STATUS_LOCAL_DISCONNECT cpu_to_le32(0xC000013B) -#define STATUS_REMOTE_DISCONNECT cpu_to_le32(0xC000013C) -#define STATUS_REMOTE_RESOURCES cpu_to_le32(0xC000013D) -#define STATUS_LINK_FAILED cpu_to_le32(0xC000013E) -#define STATUS_LINK_TIMEOUT cpu_to_le32(0xC000013F) -#define STATUS_INVALID_CONNECTION cpu_to_le32(0xC0000140) -#define STATUS_INVALID_ADDRESS cpu_to_le32(0xC0000141) -#define STATUS_DLL_INIT_FAILED cpu_to_le32(0xC0000142) -#define STATUS_MISSING_SYSTEMFILE cpu_to_le32(0xC0000143) -#define STATUS_UNHANDLED_EXCEPTION cpu_to_le32(0xC0000144) -#define STATUS_APP_INIT_FAILURE cpu_to_le32(0xC0000145) -#define STATUS_PAGEFILE_CREATE_FAILED cpu_to_le32(0xC0000146) -#define STATUS_NO_PAGEFILE cpu_to_le32(0xC0000147) -#define STATUS_INVALID_LEVEL cpu_to_le32(0xC0000148) -#define STATUS_WRONG_PASSWORD_CORE cpu_to_le32(0xC0000149) -#define STATUS_ILLEGAL_FLOAT_CONTEXT cpu_to_le32(0xC000014A) -#define STATUS_PIPE_BROKEN cpu_to_le32(0xC000014B) -#define STATUS_REGISTRY_CORRUPT cpu_to_le32(0xC000014C) -#define STATUS_REGISTRY_IO_FAILED cpu_to_le32(0xC000014D) -#define STATUS_NO_EVENT_PAIR cpu_to_le32(0xC000014E) -#define STATUS_UNRECOGNIZED_VOLUME cpu_to_le32(0xC000014F) -#define STATUS_SERIAL_NO_DEVICE_INITED cpu_to_le32(0xC0000150) -#define STATUS_NO_SUCH_ALIAS cpu_to_le32(0xC0000151) -#define STATUS_MEMBER_NOT_IN_ALIAS cpu_to_le32(0xC0000152) -#define STATUS_MEMBER_IN_ALIAS cpu_to_le32(0xC0000153) -#define STATUS_ALIAS_EXISTS cpu_to_le32(0xC0000154) -#define STATUS_LOGON_NOT_GRANTED cpu_to_le32(0xC0000155) -#define STATUS_TOO_MANY_SECRETS cpu_to_le32(0xC0000156) -#define STATUS_SECRET_TOO_LONG cpu_to_le32(0xC0000157) -#define STATUS_INTERNAL_DB_ERROR cpu_to_le32(0xC0000158) -#define STATUS_FULLSCREEN_MODE cpu_to_le32(0xC0000159) -#define STATUS_TOO_MANY_CONTEXT_IDS cpu_to_le32(0xC000015A) -#define STATUS_LOGON_TYPE_NOT_GRANTED cpu_to_le32(0xC000015B) -#define STATUS_NOT_REGISTRY_FILE cpu_to_le32(0xC000015C) -#define STATUS_NT_CROSS_ENCRYPTION_REQUIRED cpu_to_le32(0xC000015D) -#define STATUS_DOMAIN_CTRLR_CONFIG_ERROR cpu_to_le32(0xC000015E) -#define STATUS_FT_MISSING_MEMBER cpu_to_le32(0xC000015F) -#define STATUS_ILL_FORMED_SERVICE_ENTRY cpu_to_le32(0xC0000160) -#define STATUS_ILLEGAL_CHARACTER cpu_to_le32(0xC0000161) -#define STATUS_UNMAPPABLE_CHARACTER cpu_to_le32(0xC0000162) -#define STATUS_UNDEFINED_CHARACTER cpu_to_le32(0xC0000163) -#define STATUS_FLOPPY_VOLUME cpu_to_le32(0xC0000164) -#define STATUS_FLOPPY_ID_MARK_NOT_FOUND cpu_to_le32(0xC0000165) -#define STATUS_FLOPPY_WRONG_CYLINDER cpu_to_le32(0xC0000166) -#define STATUS_FLOPPY_UNKNOWN_ERROR cpu_to_le32(0xC0000167) -#define STATUS_FLOPPY_BAD_REGISTERS cpu_to_le32(0xC0000168) -#define STATUS_DISK_RECALIBRATE_FAILED cpu_to_le32(0xC0000169) -#define STATUS_DISK_OPERATION_FAILED cpu_to_le32(0xC000016A) -#define STATUS_DISK_RESET_FAILED cpu_to_le32(0xC000016B) -#define STATUS_SHARED_IRQ_BUSY cpu_to_le32(0xC000016C) -#define STATUS_FT_ORPHANING cpu_to_le32(0xC000016D) -#define STATUS_BIOS_FAILED_TO_CONNECT_INTERRUPT cpu_to_le32(0xC000016E) -#define STATUS_PARTITION_FAILURE cpu_to_le32(0xC0000172) -#define STATUS_INVALID_BLOCK_LENGTH cpu_to_le32(0xC0000173) -#define STATUS_DEVICE_NOT_PARTITIONED cpu_to_le32(0xC0000174) -#define STATUS_UNABLE_TO_LOCK_MEDIA cpu_to_le32(0xC0000175) -#define STATUS_UNABLE_TO_UNLOAD_MEDIA cpu_to_le32(0xC0000176) -#define STATUS_EOM_OVERFLOW cpu_to_le32(0xC0000177) -#define STATUS_NO_MEDIA cpu_to_le32(0xC0000178) -#define STATUS_NO_SUCH_MEMBER cpu_to_le32(0xC000017A) -#define STATUS_INVALID_MEMBER cpu_to_le32(0xC000017B) -#define STATUS_KEY_DELETED cpu_to_le32(0xC000017C) -#define STATUS_NO_LOG_SPACE cpu_to_le32(0xC000017D) -#define STATUS_TOO_MANY_SIDS cpu_to_le32(0xC000017E) -#define STATUS_LM_CROSS_ENCRYPTION_REQUIRED cpu_to_le32(0xC000017F) -#define STATUS_KEY_HAS_CHILDREN cpu_to_le32(0xC0000180) -#define STATUS_CHILD_MUST_BE_VOLATILE cpu_to_le32(0xC0000181) -#define STATUS_DEVICE_CONFIGURATION_ERROR cpu_to_le32(0xC0000182) -#define STATUS_DRIVER_INTERNAL_ERROR cpu_to_le32(0xC0000183) -#define STATUS_INVALID_DEVICE_STATE cpu_to_le32(0xC0000184) -#define STATUS_IO_DEVICE_ERROR cpu_to_le32(0xC0000185) -#define STATUS_DEVICE_PROTOCOL_ERROR cpu_to_le32(0xC0000186) -#define STATUS_BACKUP_CONTROLLER cpu_to_le32(0xC0000187) -#define STATUS_LOG_FILE_FULL cpu_to_le32(0xC0000188) -#define STATUS_TOO_LATE cpu_to_le32(0xC0000189) -#define STATUS_NO_TRUST_LSA_SECRET cpu_to_le32(0xC000018A) -#define STATUS_NO_TRUST_SAM_ACCOUNT cpu_to_le32(0xC000018B) -#define STATUS_TRUSTED_DOMAIN_FAILURE cpu_to_le32(0xC000018C) -#define STATUS_TRUSTED_RELATIONSHIP_FAILURE cpu_to_le32(0xC000018D) -#define STATUS_EVENTLOG_FILE_CORRUPT cpu_to_le32(0xC000018E) -#define STATUS_EVENTLOG_CANT_START cpu_to_le32(0xC000018F) -#define STATUS_TRUST_FAILURE cpu_to_le32(0xC0000190) -#define STATUS_MUTANT_LIMIT_EXCEEDED cpu_to_le32(0xC0000191) -#define STATUS_NETLOGON_NOT_STARTED cpu_to_le32(0xC0000192) -#define STATUS_ACCOUNT_EXPIRED cpu_to_le32(0xC0000193) -#define STATUS_POSSIBLE_DEADLOCK cpu_to_le32(0xC0000194) -#define STATUS_NETWORK_CREDENTIAL_CONFLICT cpu_to_le32(0xC0000195) -#define STATUS_REMOTE_SESSION_LIMIT cpu_to_le32(0xC0000196) -#define STATUS_EVENTLOG_FILE_CHANGED cpu_to_le32(0xC0000197) -#define STATUS_NOLOGON_INTERDOMAIN_TRUST_ACCOUNT cpu_to_le32(0xC0000198) -#define STATUS_NOLOGON_WORKSTATION_TRUST_ACCOUNT cpu_to_le32(0xC0000199) -#define STATUS_NOLOGON_SERVER_TRUST_ACCOUNT cpu_to_le32(0xC000019A) -#define STATUS_DOMAIN_TRUST_INCONSISTENT cpu_to_le32(0xC000019B) -#define STATUS_FS_DRIVER_REQUIRED cpu_to_le32(0xC000019C) -#define STATUS_IMAGE_ALREADY_LOADED_AS_DLL cpu_to_le32(0xC000019D) -#define STATUS_NETWORK_OPEN_RESTRICTION cpu_to_le32(0xC0000201) -#define STATUS_NO_USER_SESSION_KEY cpu_to_le32(0xC0000202) -#define STATUS_USER_SESSION_DELETED cpu_to_le32(0xC0000203) -#define STATUS_RESOURCE_LANG_NOT_FOUND cpu_to_le32(0xC0000204) -#define STATUS_INSUFF_SERVER_RESOURCES cpu_to_le32(0xC0000205) -#define STATUS_INVALID_BUFFER_SIZE cpu_to_le32(0xC0000206) -#define STATUS_INVALID_ADDRESS_COMPONENT cpu_to_le32(0xC0000207) -#define STATUS_INVALID_ADDRESS_WILDCARD cpu_to_le32(0xC0000208) -#define STATUS_TOO_MANY_ADDRESSES cpu_to_le32(0xC0000209) -#define STATUS_ADDRESS_ALREADY_EXISTS cpu_to_le32(0xC000020A) -#define STATUS_ADDRESS_CLOSED cpu_to_le32(0xC000020B) -#define STATUS_CONNECTION_DISCONNECTED cpu_to_le32(0xC000020C) -#define STATUS_CONNECTION_RESET cpu_to_le32(0xC000020D) -#define STATUS_TOO_MANY_NODES cpu_to_le32(0xC000020E) -#define STATUS_TRANSACTION_ABORTED cpu_to_le32(0xC000020F) -#define STATUS_TRANSACTION_TIMED_OUT cpu_to_le32(0xC0000210) -#define STATUS_TRANSACTION_NO_RELEASE cpu_to_le32(0xC0000211) -#define STATUS_TRANSACTION_NO_MATCH cpu_to_le32(0xC0000212) -#define STATUS_TRANSACTION_RESPONDED cpu_to_le32(0xC0000213) -#define STATUS_TRANSACTION_INVALID_ID cpu_to_le32(0xC0000214) -#define STATUS_TRANSACTION_INVALID_TYPE cpu_to_le32(0xC0000215) -#define STATUS_NOT_SERVER_SESSION cpu_to_le32(0xC0000216) -#define STATUS_NOT_CLIENT_SESSION cpu_to_le32(0xC0000217) -#define STATUS_CANNOT_LOAD_REGISTRY_FILE cpu_to_le32(0xC0000218) -#define STATUS_DEBUG_ATTACH_FAILED cpu_to_le32(0xC0000219) -#define STATUS_SYSTEM_PROCESS_TERMINATED cpu_to_le32(0xC000021A) -#define STATUS_DATA_NOT_ACCEPTED cpu_to_le32(0xC000021B) -#define STATUS_NO_BROWSER_SERVERS_FOUND cpu_to_le32(0xC000021C) -#define STATUS_VDM_HARD_ERROR cpu_to_le32(0xC000021D) -#define STATUS_DRIVER_CANCEL_TIMEOUT cpu_to_le32(0xC000021E) -#define STATUS_REPLY_MESSAGE_MISMATCH cpu_to_le32(0xC000021F) -#define STATUS_MAPPED_ALIGNMENT cpu_to_le32(0xC0000220) -#define STATUS_IMAGE_CHECKSUM_MISMATCH cpu_to_le32(0xC0000221) -#define STATUS_LOST_WRITEBEHIND_DATA cpu_to_le32(0xC0000222) -#define STATUS_CLIENT_SERVER_PARAMETERS_INVALID cpu_to_le32(0xC0000223) -#define STATUS_PASSWORD_MUST_CHANGE cpu_to_le32(0xC0000224) -#define STATUS_NOT_FOUND cpu_to_le32(0xC0000225) -#define STATUS_NOT_TINY_STREAM cpu_to_le32(0xC0000226) -#define STATUS_RECOVERY_FAILURE cpu_to_le32(0xC0000227) -#define STATUS_STACK_OVERFLOW_READ cpu_to_le32(0xC0000228) -#define STATUS_FAIL_CHECK cpu_to_le32(0xC0000229) -#define STATUS_DUPLICATE_OBJECTID cpu_to_le32(0xC000022A) -#define STATUS_OBJECTID_EXISTS cpu_to_le32(0xC000022B) -#define STATUS_CONVERT_TO_LARGE cpu_to_le32(0xC000022C) -#define STATUS_RETRY cpu_to_le32(0xC000022D) -#define STATUS_FOUND_OUT_OF_SCOPE cpu_to_le32(0xC000022E) -#define STATUS_ALLOCATE_BUCKET cpu_to_le32(0xC000022F) -#define STATUS_PROPSET_NOT_FOUND cpu_to_le32(0xC0000230) -#define STATUS_MARSHALL_OVERFLOW cpu_to_le32(0xC0000231) -#define STATUS_INVALID_VARIANT cpu_to_le32(0xC0000232) -#define STATUS_DOMAIN_CONTROLLER_NOT_FOUND cpu_to_le32(0xC0000233) -#define STATUS_ACCOUNT_LOCKED_OUT cpu_to_le32(0xC0000234) -#define STATUS_HANDLE_NOT_CLOSABLE cpu_to_le32(0xC0000235) -#define STATUS_CONNECTION_REFUSED cpu_to_le32(0xC0000236) -#define STATUS_GRACEFUL_DISCONNECT cpu_to_le32(0xC0000237) -#define STATUS_ADDRESS_ALREADY_ASSOCIATED cpu_to_le32(0xC0000238) -#define STATUS_ADDRESS_NOT_ASSOCIATED cpu_to_le32(0xC0000239) -#define STATUS_CONNECTION_INVALID cpu_to_le32(0xC000023A) -#define STATUS_CONNECTION_ACTIVE cpu_to_le32(0xC000023B) -#define STATUS_NETWORK_UNREACHABLE cpu_to_le32(0xC000023C) -#define STATUS_HOST_UNREACHABLE cpu_to_le32(0xC000023D) -#define STATUS_PROTOCOL_UNREACHABLE cpu_to_le32(0xC000023E) -#define STATUS_PORT_UNREACHABLE cpu_to_le32(0xC000023F) -#define STATUS_REQUEST_ABORTED cpu_to_le32(0xC0000240) -#define STATUS_CONNECTION_ABORTED cpu_to_le32(0xC0000241) -#define STATUS_BAD_COMPRESSION_BUFFER cpu_to_le32(0xC0000242) -#define STATUS_USER_MAPPED_FILE cpu_to_le32(0xC0000243) -#define STATUS_AUDIT_FAILED cpu_to_le32(0xC0000244) -#define STATUS_TIMER_RESOLUTION_NOT_SET cpu_to_le32(0xC0000245) -#define STATUS_CONNECTION_COUNT_LIMIT cpu_to_le32(0xC0000246) -#define STATUS_LOGIN_TIME_RESTRICTION cpu_to_le32(0xC0000247) -#define STATUS_LOGIN_WKSTA_RESTRICTION cpu_to_le32(0xC0000248) -#define STATUS_IMAGE_MP_UP_MISMATCH cpu_to_le32(0xC0000249) -#define STATUS_INSUFFICIENT_LOGON_INFO cpu_to_le32(0xC0000250) -#define STATUS_BAD_DLL_ENTRYPOINT cpu_to_le32(0xC0000251) -#define STATUS_BAD_SERVICE_ENTRYPOINT cpu_to_le32(0xC0000252) -#define STATUS_LPC_REPLY_LOST cpu_to_le32(0xC0000253) -#define STATUS_IP_ADDRESS_CONFLICT1 cpu_to_le32(0xC0000254) -#define STATUS_IP_ADDRESS_CONFLICT2 cpu_to_le32(0xC0000255) -#define STATUS_REGISTRY_QUOTA_LIMIT cpu_to_le32(0xC0000256) -#define STATUS_PATH_NOT_COVERED cpu_to_le32(0xC0000257) -#define STATUS_NO_CALLBACK_ACTIVE cpu_to_le32(0xC0000258) -#define STATUS_LICENSE_QUOTA_EXCEEDED cpu_to_le32(0xC0000259) -#define STATUS_PWD_TOO_SHORT cpu_to_le32(0xC000025A) -#define STATUS_PWD_TOO_RECENT cpu_to_le32(0xC000025B) -#define STATUS_PWD_HISTORY_CONFLICT cpu_to_le32(0xC000025C) -#define STATUS_PLUGPLAY_NO_DEVICE cpu_to_le32(0xC000025E) -#define STATUS_UNSUPPORTED_COMPRESSION cpu_to_le32(0xC000025F) -#define STATUS_INVALID_HW_PROFILE cpu_to_le32(0xC0000260) -#define STATUS_INVALID_PLUGPLAY_DEVICE_PATH cpu_to_le32(0xC0000261) -#define STATUS_DRIVER_ORDINAL_NOT_FOUND cpu_to_le32(0xC0000262) -#define STATUS_DRIVER_ENTRYPOINT_NOT_FOUND cpu_to_le32(0xC0000263) -#define STATUS_RESOURCE_NOT_OWNED cpu_to_le32(0xC0000264) -#define STATUS_TOO_MANY_LINKS cpu_to_le32(0xC0000265) -#define STATUS_QUOTA_LIST_INCONSISTENT cpu_to_le32(0xC0000266) -#define STATUS_FILE_IS_OFFLINE cpu_to_le32(0xC0000267) -#define STATUS_EVALUATION_EXPIRATION cpu_to_le32(0xC0000268) -#define STATUS_ILLEGAL_DLL_RELOCATION cpu_to_le32(0xC0000269) -#define STATUS_LICENSE_VIOLATION cpu_to_le32(0xC000026A) -#define STATUS_DLL_INIT_FAILED_LOGOFF cpu_to_le32(0xC000026B) -#define STATUS_DRIVER_UNABLE_TO_LOAD cpu_to_le32(0xC000026C) -#define STATUS_DFS_UNAVAILABLE cpu_to_le32(0xC000026D) -#define STATUS_VOLUME_DISMOUNTED cpu_to_le32(0xC000026E) -#define STATUS_WX86_INTERNAL_ERROR cpu_to_le32(0xC000026F) -#define STATUS_WX86_FLOAT_STACK_CHECK cpu_to_le32(0xC0000270) -#define STATUS_VALIDATE_CONTINUE cpu_to_le32(0xC0000271) -#define STATUS_NO_MATCH cpu_to_le32(0xC0000272) -#define STATUS_NO_MORE_MATCHES cpu_to_le32(0xC0000273) -#define STATUS_NOT_A_REPARSE_POINT cpu_to_le32(0xC0000275) -#define STATUS_IO_REPARSE_TAG_INVALID cpu_to_le32(0xC0000276) -#define STATUS_IO_REPARSE_TAG_MISMATCH cpu_to_le32(0xC0000277) -#define STATUS_IO_REPARSE_DATA_INVALID cpu_to_le32(0xC0000278) -#define STATUS_IO_REPARSE_TAG_NOT_HANDLED cpu_to_le32(0xC0000279) -#define STATUS_REPARSE_POINT_NOT_RESOLVED cpu_to_le32(0xC0000280) -#define STATUS_DIRECTORY_IS_A_REPARSE_POINT cpu_to_le32(0xC0000281) -#define STATUS_RANGE_LIST_CONFLICT cpu_to_le32(0xC0000282) -#define STATUS_SOURCE_ELEMENT_EMPTY cpu_to_le32(0xC0000283) -#define STATUS_DESTINATION_ELEMENT_FULL cpu_to_le32(0xC0000284) -#define STATUS_ILLEGAL_ELEMENT_ADDRESS cpu_to_le32(0xC0000285) -#define STATUS_MAGAZINE_NOT_PRESENT cpu_to_le32(0xC0000286) -#define STATUS_REINITIALIZATION_NEEDED cpu_to_le32(0xC0000287) -#define STATUS_ENCRYPTION_FAILED cpu_to_le32(0xC000028A) -#define STATUS_DECRYPTION_FAILED cpu_to_le32(0xC000028B) -#define STATUS_RANGE_NOT_FOUND cpu_to_le32(0xC000028C) -#define STATUS_NO_RECOVERY_POLICY cpu_to_le32(0xC000028D) -#define STATUS_NO_EFS cpu_to_le32(0xC000028E) -#define STATUS_WRONG_EFS cpu_to_le32(0xC000028F) -#define STATUS_NO_USER_KEYS cpu_to_le32(0xC0000290) -#define STATUS_FILE_NOT_ENCRYPTED cpu_to_le32(0xC0000291) -#define STATUS_NOT_EXPORT_FORMAT cpu_to_le32(0xC0000292) -#define STATUS_FILE_ENCRYPTED cpu_to_le32(0xC0000293) -#define STATUS_WMI_GUID_NOT_FOUND cpu_to_le32(0xC0000295) -#define STATUS_WMI_INSTANCE_NOT_FOUND cpu_to_le32(0xC0000296) -#define STATUS_WMI_ITEMID_NOT_FOUND cpu_to_le32(0xC0000297) -#define STATUS_WMI_TRY_AGAIN cpu_to_le32(0xC0000298) -#define STATUS_SHARED_POLICY cpu_to_le32(0xC0000299) -#define STATUS_POLICY_OBJECT_NOT_FOUND cpu_to_le32(0xC000029A) -#define STATUS_POLICY_ONLY_IN_DS cpu_to_le32(0xC000029B) -#define STATUS_VOLUME_NOT_UPGRADED cpu_to_le32(0xC000029C) -#define STATUS_REMOTE_STORAGE_NOT_ACTIVE cpu_to_le32(0xC000029D) -#define STATUS_REMOTE_STORAGE_MEDIA_ERROR cpu_to_le32(0xC000029E) -#define STATUS_NO_TRACKING_SERVICE cpu_to_le32(0xC000029F) -#define STATUS_SERVER_SID_MISMATCH cpu_to_le32(0xC00002A0) -#define STATUS_DS_NO_ATTRIBUTE_OR_VALUE cpu_to_le32(0xC00002A1) -#define STATUS_DS_INVALID_ATTRIBUTE_SYNTAX cpu_to_le32(0xC00002A2) -#define STATUS_DS_ATTRIBUTE_TYPE_UNDEFINED cpu_to_le32(0xC00002A3) -#define STATUS_DS_ATTRIBUTE_OR_VALUE_EXISTS cpu_to_le32(0xC00002A4) -#define STATUS_DS_BUSY cpu_to_le32(0xC00002A5) -#define STATUS_DS_UNAVAILABLE cpu_to_le32(0xC00002A6) -#define STATUS_DS_NO_RIDS_ALLOCATED cpu_to_le32(0xC00002A7) -#define STATUS_DS_NO_MORE_RIDS cpu_to_le32(0xC00002A8) -#define STATUS_DS_INCORRECT_ROLE_OWNER cpu_to_le32(0xC00002A9) -#define STATUS_DS_RIDMGR_INIT_ERROR cpu_to_le32(0xC00002AA) -#define STATUS_DS_OBJ_CLASS_VIOLATION cpu_to_le32(0xC00002AB) -#define STATUS_DS_CANT_ON_NON_LEAF cpu_to_le32(0xC00002AC) -#define STATUS_DS_CANT_ON_RDN cpu_to_le32(0xC00002AD) -#define STATUS_DS_CANT_MOD_OBJ_CLASS cpu_to_le32(0xC00002AE) -#define STATUS_DS_CROSS_DOM_MOVE_FAILED cpu_to_le32(0xC00002AF) -#define STATUS_DS_GC_NOT_AVAILABLE cpu_to_le32(0xC00002B0) -#define STATUS_DIRECTORY_SERVICE_REQUIRED cpu_to_le32(0xC00002B1) -#define STATUS_REPARSE_ATTRIBUTE_CONFLICT cpu_to_le32(0xC00002B2) -#define STATUS_CANT_ENABLE_DENY_ONLY cpu_to_le32(0xC00002B3) -#define STATUS_FLOAT_MULTIPLE_FAULTS cpu_to_le32(0xC00002B4) -#define STATUS_FLOAT_MULTIPLE_TRAPS cpu_to_le32(0xC00002B5) -#define STATUS_DEVICE_REMOVED cpu_to_le32(0xC00002B6) -#define STATUS_JOURNAL_DELETE_IN_PROGRESS cpu_to_le32(0xC00002B7) -#define STATUS_JOURNAL_NOT_ACTIVE cpu_to_le32(0xC00002B8) -#define STATUS_NOINTERFACE cpu_to_le32(0xC00002B9) -#define STATUS_DS_ADMIN_LIMIT_EXCEEDED cpu_to_le32(0xC00002C1) -#define STATUS_DRIVER_FAILED_SLEEP cpu_to_le32(0xC00002C2) -#define STATUS_MUTUAL_AUTHENTICATION_FAILED cpu_to_le32(0xC00002C3) -#define STATUS_CORRUPT_SYSTEM_FILE cpu_to_le32(0xC00002C4) -#define STATUS_DATATYPE_MISALIGNMENT_ERROR cpu_to_le32(0xC00002C5) -#define STATUS_WMI_READ_ONLY cpu_to_le32(0xC00002C6) -#define STATUS_WMI_SET_FAILURE cpu_to_le32(0xC00002C7) -#define STATUS_COMMITMENT_MINIMUM cpu_to_le32(0xC00002C8) -#define STATUS_REG_NAT_CONSUMPTION cpu_to_le32(0xC00002C9) -#define STATUS_TRANSPORT_FULL cpu_to_le32(0xC00002CA) -#define STATUS_DS_SAM_INIT_FAILURE cpu_to_le32(0xC00002CB) -#define STATUS_ONLY_IF_CONNECTED cpu_to_le32(0xC00002CC) -#define STATUS_DS_SENSITIVE_GROUP_VIOLATION cpu_to_le32(0xC00002CD) -#define STATUS_PNP_RESTART_ENUMERATION cpu_to_le32(0xC00002CE) -#define STATUS_JOURNAL_ENTRY_DELETED cpu_to_le32(0xC00002CF) -#define STATUS_DS_CANT_MOD_PRIMARYGROUPID cpu_to_le32(0xC00002D0) -#define STATUS_SYSTEM_IMAGE_BAD_SIGNATURE cpu_to_le32(0xC00002D1) -#define STATUS_PNP_REBOOT_REQUIRED cpu_to_le32(0xC00002D2) -#define STATUS_POWER_STATE_INVALID cpu_to_le32(0xC00002D3) -#define STATUS_DS_INVALID_GROUP_TYPE cpu_to_le32(0xC00002D4) -#define STATUS_DS_NO_NEST_GLOBALGROUP_IN_MIXEDDOMAIN cpu_to_le32(0xC00002D5) -#define STATUS_DS_NO_NEST_LOCALGROUP_IN_MIXEDDOMAIN cpu_to_le32(0xC00002D6) -#define STATUS_DS_GLOBAL_CANT_HAVE_LOCAL_MEMBER cpu_to_le32(0xC00002D7) -#define STATUS_DS_GLOBAL_CANT_HAVE_UNIVERSAL_MEMBER cpu_to_le32(0xC00002D8) -#define STATUS_DS_UNIVERSAL_CANT_HAVE_LOCAL_MEMBER cpu_to_le32(0xC00002D9) -#define STATUS_DS_GLOBAL_CANT_HAVE_CROSSDOMAIN_MEMBER cpu_to_le32(0xC00002DA) -#define STATUS_DS_LOCAL_CANT_HAVE_CROSSDOMAIN_LOCAL_MEMBER \ - cpu_to_le32(0xC00002DB) -#define STATUS_DS_HAVE_PRIMARY_MEMBERS cpu_to_le32(0xC00002DC) -#define STATUS_WMI_NOT_SUPPORTED cpu_to_le32(0xC00002DD) -#define STATUS_INSUFFICIENT_POWER cpu_to_le32(0xC00002DE) -#define STATUS_SAM_NEED_BOOTKEY_PASSWORD cpu_to_le32(0xC00002DF) -#define STATUS_SAM_NEED_BOOTKEY_FLOPPY cpu_to_le32(0xC00002E0) -#define STATUS_DS_CANT_START cpu_to_le32(0xC00002E1) -#define STATUS_DS_INIT_FAILURE cpu_to_le32(0xC00002E2) -#define STATUS_SAM_INIT_FAILURE cpu_to_le32(0xC00002E3) -#define STATUS_DS_GC_REQUIRED cpu_to_le32(0xC00002E4) -#define STATUS_DS_LOCAL_MEMBER_OF_LOCAL_ONLY cpu_to_le32(0xC00002E5) -#define STATUS_DS_NO_FPO_IN_UNIVERSAL_GROUPS cpu_to_le32(0xC00002E6) -#define STATUS_DS_MACHINE_ACCOUNT_QUOTA_EXCEEDED cpu_to_le32(0xC00002E7) -#define STATUS_MULTIPLE_FAULT_VIOLATION cpu_to_le32(0xC00002E8) -#define STATUS_CURRENT_DOMAIN_NOT_ALLOWED cpu_to_le32(0xC00002E9) -#define STATUS_CANNOT_MAKE cpu_to_le32(0xC00002EA) -#define STATUS_SYSTEM_SHUTDOWN cpu_to_le32(0xC00002EB) -#define STATUS_DS_INIT_FAILURE_CONSOLE cpu_to_le32(0xC00002EC) -#define STATUS_DS_SAM_INIT_FAILURE_CONSOLE cpu_to_le32(0xC00002ED) -#define STATUS_UNFINISHED_CONTEXT_DELETED cpu_to_le32(0xC00002EE) -#define STATUS_NO_TGT_REPLY cpu_to_le32(0xC00002EF) -#define STATUS_OBJECTID_NOT_FOUND cpu_to_le32(0xC00002F0) -#define STATUS_NO_IP_ADDRESSES cpu_to_le32(0xC00002F1) -#define STATUS_WRONG_CREDENTIAL_HANDLE cpu_to_le32(0xC00002F2) -#define STATUS_CRYPTO_SYSTEM_INVALID cpu_to_le32(0xC00002F3) -#define STATUS_MAX_REFERRALS_EXCEEDED cpu_to_le32(0xC00002F4) -#define STATUS_MUST_BE_KDC cpu_to_le32(0xC00002F5) -#define STATUS_STRONG_CRYPTO_NOT_SUPPORTED cpu_to_le32(0xC00002F6) -#define STATUS_TOO_MANY_PRINCIPALS cpu_to_le32(0xC00002F7) -#define STATUS_NO_PA_DATA cpu_to_le32(0xC00002F8) -#define STATUS_PKINIT_NAME_MISMATCH cpu_to_le32(0xC00002F9) -#define STATUS_SMARTCARD_LOGON_REQUIRED cpu_to_le32(0xC00002FA) -#define STATUS_KDC_INVALID_REQUEST cpu_to_le32(0xC00002FB) -#define STATUS_KDC_UNABLE_TO_REFER cpu_to_le32(0xC00002FC) -#define STATUS_KDC_UNKNOWN_ETYPE cpu_to_le32(0xC00002FD) -#define STATUS_SHUTDOWN_IN_PROGRESS cpu_to_le32(0xC00002FE) -#define STATUS_SERVER_SHUTDOWN_IN_PROGRESS cpu_to_le32(0xC00002FF) -#define STATUS_NOT_SUPPORTED_ON_SBS cpu_to_le32(0xC0000300) -#define STATUS_WMI_GUID_DISCONNECTED cpu_to_le32(0xC0000301) -#define STATUS_WMI_ALREADY_DISABLED cpu_to_le32(0xC0000302) -#define STATUS_WMI_ALREADY_ENABLED cpu_to_le32(0xC0000303) -#define STATUS_MFT_TOO_FRAGMENTED cpu_to_le32(0xC0000304) -#define STATUS_COPY_PROTECTION_FAILURE cpu_to_le32(0xC0000305) -#define STATUS_CSS_AUTHENTICATION_FAILURE cpu_to_le32(0xC0000306) -#define STATUS_CSS_KEY_NOT_PRESENT cpu_to_le32(0xC0000307) -#define STATUS_CSS_KEY_NOT_ESTABLISHED cpu_to_le32(0xC0000308) -#define STATUS_CSS_SCRAMBLED_SECTOR cpu_to_le32(0xC0000309) -#define STATUS_CSS_REGION_MISMATCH cpu_to_le32(0xC000030A) -#define STATUS_CSS_RESETS_EXHAUSTED cpu_to_le32(0xC000030B) -#define STATUS_PKINIT_FAILURE cpu_to_le32(0xC0000320) -#define STATUS_SMARTCARD_SUBSYSTEM_FAILURE cpu_to_le32(0xC0000321) -#define STATUS_NO_KERB_KEY cpu_to_le32(0xC0000322) -#define STATUS_HOST_DOWN cpu_to_le32(0xC0000350) -#define STATUS_UNSUPPORTED_PREAUTH cpu_to_le32(0xC0000351) -#define STATUS_EFS_ALG_BLOB_TOO_BIG cpu_to_le32(0xC0000352) -#define STATUS_PORT_NOT_SET cpu_to_le32(0xC0000353) -#define STATUS_DEBUGGER_INACTIVE cpu_to_le32(0xC0000354) -#define STATUS_DS_VERSION_CHECK_FAILURE cpu_to_le32(0xC0000355) -#define STATUS_AUDITING_DISABLED cpu_to_le32(0xC0000356) -#define STATUS_PRENT4_MACHINE_ACCOUNT cpu_to_le32(0xC0000357) -#define STATUS_DS_AG_CANT_HAVE_UNIVERSAL_MEMBER cpu_to_le32(0xC0000358) -#define STATUS_INVALID_IMAGE_WIN_32 cpu_to_le32(0xC0000359) -#define STATUS_INVALID_IMAGE_WIN_64 cpu_to_le32(0xC000035A) -#define STATUS_BAD_BINDINGS cpu_to_le32(0xC000035B) -#define STATUS_NETWORK_SESSION_EXPIRED cpu_to_le32(0xC000035C) -#define STATUS_APPHELP_BLOCK cpu_to_le32(0xC000035D) -#define STATUS_ALL_SIDS_FILTERED cpu_to_le32(0xC000035E) -#define STATUS_NOT_SAFE_MODE_DRIVER cpu_to_le32(0xC000035F) -#define STATUS_ACCESS_DISABLED_BY_POLICY_DEFAULT cpu_to_le32(0xC0000361) -#define STATUS_ACCESS_DISABLED_BY_POLICY_PATH cpu_to_le32(0xC0000362) -#define STATUS_ACCESS_DISABLED_BY_POLICY_PUBLISHER cpu_to_le32(0xC0000363) -#define STATUS_ACCESS_DISABLED_BY_POLICY_OTHER cpu_to_le32(0xC0000364) -#define STATUS_FAILED_DRIVER_ENTRY cpu_to_le32(0xC0000365) -#define STATUS_DEVICE_ENUMERATION_ERROR cpu_to_le32(0xC0000366) -#define STATUS_MOUNT_POINT_NOT_RESOLVED cpu_to_le32(0xC0000368) -#define STATUS_INVALID_DEVICE_OBJECT_PARAMETER cpu_to_le32(0xC0000369) -#define STATUS_MCA_OCCURRED cpu_to_le32(0xC000036A) -#define STATUS_DRIVER_BLOCKED_CRITICAL cpu_to_le32(0xC000036B) -#define STATUS_DRIVER_BLOCKED cpu_to_le32(0xC000036C) -#define STATUS_DRIVER_DATABASE_ERROR cpu_to_le32(0xC000036D) -#define STATUS_SYSTEM_HIVE_TOO_LARGE cpu_to_le32(0xC000036E) -#define STATUS_INVALID_IMPORT_OF_NON_DLL cpu_to_le32(0xC000036F) -#define STATUS_NO_SECRETS cpu_to_le32(0xC0000371) -#define STATUS_ACCESS_DISABLED_NO_SAFER_UI_BY_POLICY cpu_to_le32(0xC0000372) -#define STATUS_FAILED_STACK_SWITCH cpu_to_le32(0xC0000373) -#define STATUS_HEAP_CORRUPTION cpu_to_le32(0xC0000374) -#define STATUS_SMARTCARD_WRONG_PIN cpu_to_le32(0xC0000380) -#define STATUS_SMARTCARD_CARD_BLOCKED cpu_to_le32(0xC0000381) -#define STATUS_SMARTCARD_CARD_NOT_AUTHENTICATED cpu_to_le32(0xC0000382) -#define STATUS_SMARTCARD_NO_CARD cpu_to_le32(0xC0000383) -#define STATUS_SMARTCARD_NO_KEY_CONTAINER cpu_to_le32(0xC0000384) -#define STATUS_SMARTCARD_NO_CERTIFICATE cpu_to_le32(0xC0000385) -#define STATUS_SMARTCARD_NO_KEYSET cpu_to_le32(0xC0000386) -#define STATUS_SMARTCARD_IO_ERROR cpu_to_le32(0xC0000387) -#define STATUS_DOWNGRADE_DETECTED cpu_to_le32(0xC0000388) -#define STATUS_SMARTCARD_CERT_REVOKED cpu_to_le32(0xC0000389) -#define STATUS_ISSUING_CA_UNTRUSTED cpu_to_le32(0xC000038A) -#define STATUS_REVOCATION_OFFLINE_C cpu_to_le32(0xC000038B) -#define STATUS_PKINIT_CLIENT_FAILURE cpu_to_le32(0xC000038C) -#define STATUS_SMARTCARD_CERT_EXPIRED cpu_to_le32(0xC000038D) -#define STATUS_DRIVER_FAILED_PRIOR_UNLOAD cpu_to_le32(0xC000038E) -#define STATUS_SMARTCARD_SILENT_CONTEXT cpu_to_le32(0xC000038F) -#define STATUS_PER_USER_TRUST_QUOTA_EXCEEDED cpu_to_le32(0xC0000401) -#define STATUS_ALL_USER_TRUST_QUOTA_EXCEEDED cpu_to_le32(0xC0000402) -#define STATUS_USER_DELETE_TRUST_QUOTA_EXCEEDED cpu_to_le32(0xC0000403) -#define STATUS_DS_NAME_NOT_UNIQUE cpu_to_le32(0xC0000404) -#define STATUS_DS_DUPLICATE_ID_FOUND cpu_to_le32(0xC0000405) -#define STATUS_DS_GROUP_CONVERSION_ERROR cpu_to_le32(0xC0000406) -#define STATUS_VOLSNAP_PREPARE_HIBERNATE cpu_to_le32(0xC0000407) -#define STATUS_USER2USER_REQUIRED cpu_to_le32(0xC0000408) -#define STATUS_STACK_BUFFER_OVERRUN cpu_to_le32(0xC0000409) -#define STATUS_NO_S4U_PROT_SUPPORT cpu_to_le32(0xC000040A) -#define STATUS_CROSSREALM_DELEGATION_FAILURE cpu_to_le32(0xC000040B) -#define STATUS_REVOCATION_OFFLINE_KDC cpu_to_le32(0xC000040C) -#define STATUS_ISSUING_CA_UNTRUSTED_KDC cpu_to_le32(0xC000040D) -#define STATUS_KDC_CERT_EXPIRED cpu_to_le32(0xC000040E) -#define STATUS_KDC_CERT_REVOKED cpu_to_le32(0xC000040F) -#define STATUS_PARAMETER_QUOTA_EXCEEDED cpu_to_le32(0xC0000410) -#define STATUS_HIBERNATION_FAILURE cpu_to_le32(0xC0000411) -#define STATUS_DELAY_LOAD_FAILED cpu_to_le32(0xC0000412) -#define STATUS_AUTHENTICATION_FIREWALL_FAILED cpu_to_le32(0xC0000413) -#define STATUS_VDM_DISALLOWED cpu_to_le32(0xC0000414) -#define STATUS_HUNG_DISPLAY_DRIVER_THREAD cpu_to_le32(0xC0000415) -#define STATUS_INSUFFICIENT_RESOURCE_FOR_SPECIFIED_SHARED_SECTION_SIZE \ - cpu_to_le32(0xC0000416) -#define STATUS_INVALID_CRUNTIME_PARAMETER cpu_to_le32(0xC0000417) -#define STATUS_NTLM_BLOCKED cpu_to_le32(0xC0000418) -#define STATUS_ASSERTION_FAILURE cpu_to_le32(0xC0000420) -#define STATUS_VERIFIER_STOP cpu_to_le32(0xC0000421) -#define STATUS_CALLBACK_POP_STACK cpu_to_le32(0xC0000423) -#define STATUS_INCOMPATIBLE_DRIVER_BLOCKED cpu_to_le32(0xC0000424) -#define STATUS_HIVE_UNLOADED cpu_to_le32(0xC0000425) -#define STATUS_COMPRESSION_DISABLED cpu_to_le32(0xC0000426) -#define STATUS_FILE_SYSTEM_LIMITATION cpu_to_le32(0xC0000427) -#define STATUS_INVALID_IMAGE_HASH cpu_to_le32(0xC0000428) -#define STATUS_NOT_CAPABLE cpu_to_le32(0xC0000429) -#define STATUS_REQUEST_OUT_OF_SEQUENCE cpu_to_le32(0xC000042A) -#define STATUS_IMPLEMENTATION_LIMIT cpu_to_le32(0xC000042B) -#define STATUS_ELEVATION_REQUIRED cpu_to_le32(0xC000042C) -#define STATUS_BEYOND_VDL cpu_to_le32(0xC0000432) -#define STATUS_ENCOUNTERED_WRITE_IN_PROGRESS cpu_to_le32(0xC0000433) -#define STATUS_PTE_CHANGED cpu_to_le32(0xC0000434) -#define STATUS_PURGE_FAILED cpu_to_le32(0xC0000435) -#define STATUS_CRED_REQUIRES_CONFIRMATION cpu_to_le32(0xC0000440) -#define STATUS_CS_ENCRYPTION_INVALID_SERVER_RESPONSE cpu_to_le32(0xC0000441) -#define STATUS_CS_ENCRYPTION_UNSUPPORTED_SERVER cpu_to_le32(0xC0000442) -#define STATUS_CS_ENCRYPTION_EXISTING_ENCRYPTED_FILE cpu_to_le32(0xC0000443) -#define STATUS_CS_ENCRYPTION_NEW_ENCRYPTED_FILE cpu_to_le32(0xC0000444) -#define STATUS_CS_ENCRYPTION_FILE_NOT_CSE cpu_to_le32(0xC0000445) -#define STATUS_INVALID_LABEL cpu_to_le32(0xC0000446) -#define STATUS_DRIVER_PROCESS_TERMINATED cpu_to_le32(0xC0000450) -#define STATUS_AMBIGUOUS_SYSTEM_DEVICE cpu_to_le32(0xC0000451) -#define STATUS_SYSTEM_DEVICE_NOT_FOUND cpu_to_le32(0xC0000452) -#define STATUS_RESTART_BOOT_APPLICATION cpu_to_le32(0xC0000453) -#define STATUS_INVALID_TASK_NAME cpu_to_le32(0xC0000500) -#define STATUS_INVALID_TASK_INDEX cpu_to_le32(0xC0000501) -#define STATUS_THREAD_ALREADY_IN_TASK cpu_to_le32(0xC0000502) -#define STATUS_CALLBACK_BYPASS cpu_to_le32(0xC0000503) -#define STATUS_PORT_CLOSED cpu_to_le32(0xC0000700) -#define STATUS_MESSAGE_LOST cpu_to_le32(0xC0000701) -#define STATUS_INVALID_MESSAGE cpu_to_le32(0xC0000702) -#define STATUS_REQUEST_CANCELED cpu_to_le32(0xC0000703) -#define STATUS_RECURSIVE_DISPATCH cpu_to_le32(0xC0000704) -#define STATUS_LPC_RECEIVE_BUFFER_EXPECTED cpu_to_le32(0xC0000705) -#define STATUS_LPC_INVALID_CONNECTION_USAGE cpu_to_le32(0xC0000706) -#define STATUS_LPC_REQUESTS_NOT_ALLOWED cpu_to_le32(0xC0000707) -#define STATUS_RESOURCE_IN_USE cpu_to_le32(0xC0000708) -#define STATUS_HARDWARE_MEMORY_ERROR cpu_to_le32(0xC0000709) -#define STATUS_THREADPOOL_HANDLE_EXCEPTION cpu_to_le32(0xC000070A) -#define STATUS_THREADPOOL_SET_EVENT_ON_COMPLETION_FAILED cpu_to_le32(0xC000070B) -#define STATUS_THREADPOOL_RELEASE_SEMAPHORE_ON_COMPLETION_FAILED \ - cpu_to_le32(0xC000070C) -#define STATUS_THREADPOOL_RELEASE_MUTEX_ON_COMPLETION_FAILED \ - cpu_to_le32(0xC000070D) -#define STATUS_THREADPOOL_FREE_LIBRARY_ON_COMPLETION_FAILED \ - cpu_to_le32(0xC000070E) -#define STATUS_THREADPOOL_RELEASED_DURING_OPERATION cpu_to_le32(0xC000070F) -#define STATUS_CALLBACK_RETURNED_WHILE_IMPERSONATING cpu_to_le32(0xC0000710) -#define STATUS_APC_RETURNED_WHILE_IMPERSONATING cpu_to_le32(0xC0000711) -#define STATUS_PROCESS_IS_PROTECTED cpu_to_le32(0xC0000712) -#define STATUS_MCA_EXCEPTION cpu_to_le32(0xC0000713) -#define STATUS_CERTIFICATE_MAPPING_NOT_UNIQUE cpu_to_le32(0xC0000714) -#define STATUS_SYMLINK_CLASS_DISABLED cpu_to_le32(0xC0000715) -#define STATUS_INVALID_IDN_NORMALIZATION cpu_to_le32(0xC0000716) -#define STATUS_NO_UNICODE_TRANSLATION cpu_to_le32(0xC0000717) -#define STATUS_ALREADY_REGISTERED cpu_to_le32(0xC0000718) -#define STATUS_CONTEXT_MISMATCH cpu_to_le32(0xC0000719) -#define STATUS_PORT_ALREADY_HAS_COMPLETION_LIST cpu_to_le32(0xC000071A) -#define STATUS_CALLBACK_RETURNED_THREAD_PRIORITY cpu_to_le32(0xC000071B) -#define STATUS_INVALID_THREAD cpu_to_le32(0xC000071C) -#define STATUS_CALLBACK_RETURNED_TRANSACTION cpu_to_le32(0xC000071D) -#define STATUS_CALLBACK_RETURNED_LDR_LOCK cpu_to_le32(0xC000071E) -#define STATUS_CALLBACK_RETURNED_LANG cpu_to_le32(0xC000071F) -#define STATUS_CALLBACK_RETURNED_PRI_BACK cpu_to_le32(0xC0000720) -#define STATUS_CALLBACK_RETURNED_THREAD_AFFINITY cpu_to_le32(0xC0000721) -#define STATUS_DISK_REPAIR_DISABLED cpu_to_le32(0xC0000800) -#define STATUS_DS_DOMAIN_RENAME_IN_PROGRESS cpu_to_le32(0xC0000801) -#define STATUS_DISK_QUOTA_EXCEEDED cpu_to_le32(0xC0000802) -#define STATUS_CONTENT_BLOCKED cpu_to_le32(0xC0000804) -#define STATUS_BAD_CLUSTERS cpu_to_le32(0xC0000805) -#define STATUS_VOLUME_DIRTY cpu_to_le32(0xC0000806) -#define STATUS_FILE_CHECKED_OUT cpu_to_le32(0xC0000901) -#define STATUS_CHECKOUT_REQUIRED cpu_to_le32(0xC0000902) -#define STATUS_BAD_FILE_TYPE cpu_to_le32(0xC0000903) -#define STATUS_FILE_TOO_LARGE cpu_to_le32(0xC0000904) -#define STATUS_FORMS_AUTH_REQUIRED cpu_to_le32(0xC0000905) -#define STATUS_VIRUS_INFECTED cpu_to_le32(0xC0000906) -#define STATUS_VIRUS_DELETED cpu_to_le32(0xC0000907) -#define STATUS_BAD_MCFG_TABLE cpu_to_le32(0xC0000908) -#define STATUS_WOW_ASSERTION cpu_to_le32(0xC0009898) -#define STATUS_INVALID_SIGNATURE cpu_to_le32(0xC000A000) -#define STATUS_HMAC_NOT_SUPPORTED cpu_to_le32(0xC000A001) -#define STATUS_IPSEC_QUEUE_OVERFLOW cpu_to_le32(0xC000A010) -#define STATUS_ND_QUEUE_OVERFLOW cpu_to_le32(0xC000A011) -#define STATUS_HOPLIMIT_EXCEEDED cpu_to_le32(0xC000A012) -#define STATUS_PROTOCOL_NOT_SUPPORTED cpu_to_le32(0xC000A013) -#define STATUS_LOST_WRITEBEHIND_DATA_NETWORK_DISCONNECTED \ - cpu_to_le32(0xC000A080) -#define STATUS_LOST_WRITEBEHIND_DATA_NETWORK_SERVER_ERROR \ - cpu_to_le32(0xC000A081) -#define STATUS_LOST_WRITEBEHIND_DATA_LOCAL_DISK_ERROR cpu_to_le32(0xC000A082) -#define STATUS_XML_PARSE_ERROR cpu_to_le32(0xC000A083) -#define STATUS_XMLDSIG_ERROR cpu_to_le32(0xC000A084) -#define STATUS_WRONG_COMPARTMENT cpu_to_le32(0xC000A085) -#define STATUS_AUTHIP_FAILURE cpu_to_le32(0xC000A086) -#define DBG_NO_STATE_CHANGE cpu_to_le32(0xC0010001) -#define DBG_APP_NOT_IDLE cpu_to_le32(0xC0010002) -#define RPC_NT_INVALID_STRING_BINDING cpu_to_le32(0xC0020001) -#define RPC_NT_WRONG_KIND_OF_BINDING cpu_to_le32(0xC0020002) -#define RPC_NT_INVALID_BINDING cpu_to_le32(0xC0020003) -#define RPC_NT_PROTSEQ_NOT_SUPPORTED cpu_to_le32(0xC0020004) -#define RPC_NT_INVALID_RPC_PROTSEQ cpu_to_le32(0xC0020005) -#define RPC_NT_INVALID_STRING_UUID cpu_to_le32(0xC0020006) -#define RPC_NT_INVALID_ENDPOINT_FORMAT cpu_to_le32(0xC0020007) -#define RPC_NT_INVALID_NET_ADDR cpu_to_le32(0xC0020008) -#define RPC_NT_NO_ENDPOINT_FOUND cpu_to_le32(0xC0020009) -#define RPC_NT_INVALID_TIMEOUT cpu_to_le32(0xC002000A) -#define RPC_NT_OBJECT_NOT_FOUND cpu_to_le32(0xC002000B) -#define RPC_NT_ALREADY_REGISTERED cpu_to_le32(0xC002000C) -#define RPC_NT_TYPE_ALREADY_REGISTERED cpu_to_le32(0xC002000D) -#define RPC_NT_ALREADY_LISTENING cpu_to_le32(0xC002000E) -#define RPC_NT_NO_PROTSEQS_REGISTERED cpu_to_le32(0xC002000F) -#define RPC_NT_NOT_LISTENING cpu_to_le32(0xC0020010) -#define RPC_NT_UNKNOWN_MGR_TYPE cpu_to_le32(0xC0020011) -#define RPC_NT_UNKNOWN_IF cpu_to_le32(0xC0020012) -#define RPC_NT_NO_BINDINGS cpu_to_le32(0xC0020013) -#define RPC_NT_NO_PROTSEQS cpu_to_le32(0xC0020014) -#define RPC_NT_CANT_CREATE_ENDPOINT cpu_to_le32(0xC0020015) -#define RPC_NT_OUT_OF_RESOURCES cpu_to_le32(0xC0020016) -#define RPC_NT_SERVER_UNAVAILABLE cpu_to_le32(0xC0020017) -#define RPC_NT_SERVER_TOO_BUSY cpu_to_le32(0xC0020018) -#define RPC_NT_INVALID_NETWORK_OPTIONS cpu_to_le32(0xC0020019) -#define RPC_NT_NO_CALL_ACTIVE cpu_to_le32(0xC002001A) -#define RPC_NT_CALL_FAILED cpu_to_le32(0xC002001B) -#define RPC_NT_CALL_FAILED_DNE cpu_to_le32(0xC002001C) -#define RPC_NT_PROTOCOL_ERROR cpu_to_le32(0xC002001D) -#define RPC_NT_UNSUPPORTED_TRANS_SYN cpu_to_le32(0xC002001F) -#define RPC_NT_UNSUPPORTED_TYPE cpu_to_le32(0xC0020021) -#define RPC_NT_INVALID_TAG cpu_to_le32(0xC0020022) -#define RPC_NT_INVALID_BOUND cpu_to_le32(0xC0020023) -#define RPC_NT_NO_ENTRY_NAME cpu_to_le32(0xC0020024) -#define RPC_NT_INVALID_NAME_SYNTAX cpu_to_le32(0xC0020025) -#define RPC_NT_UNSUPPORTED_NAME_SYNTAX cpu_to_le32(0xC0020026) -#define RPC_NT_UUID_NO_ADDRESS cpu_to_le32(0xC0020028) -#define RPC_NT_DUPLICATE_ENDPOINT cpu_to_le32(0xC0020029) -#define RPC_NT_UNKNOWN_AUTHN_TYPE cpu_to_le32(0xC002002A) -#define RPC_NT_MAX_CALLS_TOO_SMALL cpu_to_le32(0xC002002B) -#define RPC_NT_STRING_TOO_LONG cpu_to_le32(0xC002002C) -#define RPC_NT_PROTSEQ_NOT_FOUND cpu_to_le32(0xC002002D) -#define RPC_NT_PROCNUM_OUT_OF_RANGE cpu_to_le32(0xC002002E) -#define RPC_NT_BINDING_HAS_NO_AUTH cpu_to_le32(0xC002002F) -#define RPC_NT_UNKNOWN_AUTHN_SERVICE cpu_to_le32(0xC0020030) -#define RPC_NT_UNKNOWN_AUTHN_LEVEL cpu_to_le32(0xC0020031) -#define RPC_NT_INVALID_AUTH_IDENTITY cpu_to_le32(0xC0020032) -#define RPC_NT_UNKNOWN_AUTHZ_SERVICE cpu_to_le32(0xC0020033) -#define EPT_NT_INVALID_ENTRY cpu_to_le32(0xC0020034) -#define EPT_NT_CANT_PERFORM_OP cpu_to_le32(0xC0020035) -#define EPT_NT_NOT_REGISTERED cpu_to_le32(0xC0020036) -#define RPC_NT_NOTHING_TO_EXPORT cpu_to_le32(0xC0020037) -#define RPC_NT_INCOMPLETE_NAME cpu_to_le32(0xC0020038) -#define RPC_NT_INVALID_VERS_OPTION cpu_to_le32(0xC0020039) -#define RPC_NT_NO_MORE_MEMBERS cpu_to_le32(0xC002003A) -#define RPC_NT_NOT_ALL_OBJS_UNEXPORTED cpu_to_le32(0xC002003B) -#define RPC_NT_INTERFACE_NOT_FOUND cpu_to_le32(0xC002003C) -#define RPC_NT_ENTRY_ALREADY_EXISTS cpu_to_le32(0xC002003D) -#define RPC_NT_ENTRY_NOT_FOUND cpu_to_le32(0xC002003E) -#define RPC_NT_NAME_SERVICE_UNAVAILABLE cpu_to_le32(0xC002003F) -#define RPC_NT_INVALID_NAF_ID cpu_to_le32(0xC0020040) -#define RPC_NT_CANNOT_SUPPORT cpu_to_le32(0xC0020041) -#define RPC_NT_NO_CONTEXT_AVAILABLE cpu_to_le32(0xC0020042) -#define RPC_NT_INTERNAL_ERROR cpu_to_le32(0xC0020043) -#define RPC_NT_ZERO_DIVIDE cpu_to_le32(0xC0020044) -#define RPC_NT_ADDRESS_ERROR cpu_to_le32(0xC0020045) -#define RPC_NT_FP_DIV_ZERO cpu_to_le32(0xC0020046) -#define RPC_NT_FP_UNDERFLOW cpu_to_le32(0xC0020047) -#define RPC_NT_FP_OVERFLOW cpu_to_le32(0xC0020048) -#define RPC_NT_CALL_IN_PROGRESS cpu_to_le32(0xC0020049) -#define RPC_NT_NO_MORE_BINDINGS cpu_to_le32(0xC002004A) -#define RPC_NT_GROUP_MEMBER_NOT_FOUND cpu_to_le32(0xC002004B) -#define EPT_NT_CANT_CREATE cpu_to_le32(0xC002004C) -#define RPC_NT_INVALID_OBJECT cpu_to_le32(0xC002004D) -#define RPC_NT_NO_INTERFACES cpu_to_le32(0xC002004F) -#define RPC_NT_CALL_CANCELLED cpu_to_le32(0xC0020050) -#define RPC_NT_BINDING_INCOMPLETE cpu_to_le32(0xC0020051) -#define RPC_NT_COMM_FAILURE cpu_to_le32(0xC0020052) -#define RPC_NT_UNSUPPORTED_AUTHN_LEVEL cpu_to_le32(0xC0020053) -#define RPC_NT_NO_PRINC_NAME cpu_to_le32(0xC0020054) -#define RPC_NT_NOT_RPC_ERROR cpu_to_le32(0xC0020055) -#define RPC_NT_SEC_PKG_ERROR cpu_to_le32(0xC0020057) -#define RPC_NT_NOT_CANCELLED cpu_to_le32(0xC0020058) -#define RPC_NT_INVALID_ASYNC_HANDLE cpu_to_le32(0xC0020062) -#define RPC_NT_INVALID_ASYNC_CALL cpu_to_le32(0xC0020063) -#define RPC_NT_PROXY_ACCESS_DENIED cpu_to_le32(0xC0020064) -#define RPC_NT_NO_MORE_ENTRIES cpu_to_le32(0xC0030001) -#define RPC_NT_SS_CHAR_TRANS_OPEN_FAIL cpu_to_le32(0xC0030002) -#define RPC_NT_SS_CHAR_TRANS_SHORT_FILE cpu_to_le32(0xC0030003) -#define RPC_NT_SS_IN_NULL_CONTEXT cpu_to_le32(0xC0030004) -#define RPC_NT_SS_CONTEXT_MISMATCH cpu_to_le32(0xC0030005) -#define RPC_NT_SS_CONTEXT_DAMAGED cpu_to_le32(0xC0030006) -#define RPC_NT_SS_HANDLES_MISMATCH cpu_to_le32(0xC0030007) -#define RPC_NT_SS_CANNOT_GET_CALL_HANDLE cpu_to_le32(0xC0030008) -#define RPC_NT_NULL_REF_POINTER cpu_to_le32(0xC0030009) -#define RPC_NT_ENUM_VALUE_OUT_OF_RANGE cpu_to_le32(0xC003000A) -#define RPC_NT_BYTE_COUNT_TOO_SMALL cpu_to_le32(0xC003000B) -#define RPC_NT_BAD_STUB_DATA cpu_to_le32(0xC003000C) -#define RPC_NT_INVALID_ES_ACTION cpu_to_le32(0xC0030059) -#define RPC_NT_WRONG_ES_VERSION cpu_to_le32(0xC003005A) -#define RPC_NT_WRONG_STUB_VERSION cpu_to_le32(0xC003005B) -#define RPC_NT_INVALID_PIPE_OBJECT cpu_to_le32(0xC003005C) -#define RPC_NT_INVALID_PIPE_OPERATION cpu_to_le32(0xC003005D) -#define RPC_NT_WRONG_PIPE_VERSION cpu_to_le32(0xC003005E) -#define RPC_NT_PIPE_CLOSED cpu_to_le32(0xC003005F) -#define RPC_NT_PIPE_DISCIPLINE_ERROR cpu_to_le32(0xC0030060) -#define RPC_NT_PIPE_EMPTY cpu_to_le32(0xC0030061) -#define STATUS_PNP_BAD_MPS_TABLE cpu_to_le32(0xC0040035) -#define STATUS_PNP_TRANSLATION_FAILED cpu_to_le32(0xC0040036) -#define STATUS_PNP_IRQ_TRANSLATION_FAILED cpu_to_le32(0xC0040037) -#define STATUS_PNP_INVALID_ID cpu_to_le32(0xC0040038) -#define STATUS_IO_REISSUE_AS_CACHED cpu_to_le32(0xC0040039) -#define STATUS_CTX_WINSTATION_NAME_INVALID cpu_to_le32(0xC00A0001) -#define STATUS_CTX_INVALID_PD cpu_to_le32(0xC00A0002) -#define STATUS_CTX_PD_NOT_FOUND cpu_to_le32(0xC00A0003) -#define STATUS_CTX_CLOSE_PENDING cpu_to_le32(0xC00A0006) -#define STATUS_CTX_NO_OUTBUF cpu_to_le32(0xC00A0007) -#define STATUS_CTX_MODEM_INF_NOT_FOUND cpu_to_le32(0xC00A0008) -#define STATUS_CTX_INVALID_MODEMNAME cpu_to_le32(0xC00A0009) -#define STATUS_CTX_RESPONSE_ERROR cpu_to_le32(0xC00A000A) -#define STATUS_CTX_MODEM_RESPONSE_TIMEOUT cpu_to_le32(0xC00A000B) -#define STATUS_CTX_MODEM_RESPONSE_NO_CARRIER cpu_to_le32(0xC00A000C) -#define STATUS_CTX_MODEM_RESPONSE_NO_DIALTONE cpu_to_le32(0xC00A000D) -#define STATUS_CTX_MODEM_RESPONSE_BUSY cpu_to_le32(0xC00A000E) -#define STATUS_CTX_MODEM_RESPONSE_VOICE cpu_to_le32(0xC00A000F) -#define STATUS_CTX_TD_ERROR cpu_to_le32(0xC00A0010) -#define STATUS_CTX_LICENSE_CLIENT_INVALID cpu_to_le32(0xC00A0012) -#define STATUS_CTX_LICENSE_NOT_AVAILABLE cpu_to_le32(0xC00A0013) -#define STATUS_CTX_LICENSE_EXPIRED cpu_to_le32(0xC00A0014) -#define STATUS_CTX_WINSTATION_NOT_FOUND cpu_to_le32(0xC00A0015) -#define STATUS_CTX_WINSTATION_NAME_COLLISION cpu_to_le32(0xC00A0016) -#define STATUS_CTX_WINSTATION_BUSY cpu_to_le32(0xC00A0017) -#define STATUS_CTX_BAD_VIDEO_MODE cpu_to_le32(0xC00A0018) -#define STATUS_CTX_GRAPHICS_INVALID cpu_to_le32(0xC00A0022) -#define STATUS_CTX_NOT_CONSOLE cpu_to_le32(0xC00A0024) -#define STATUS_CTX_CLIENT_QUERY_TIMEOUT cpu_to_le32(0xC00A0026) -#define STATUS_CTX_CONSOLE_DISCONNECT cpu_to_le32(0xC00A0027) -#define STATUS_CTX_CONSOLE_CONNECT cpu_to_le32(0xC00A0028) -#define STATUS_CTX_SHADOW_DENIED cpu_to_le32(0xC00A002A) -#define STATUS_CTX_WINSTATION_ACCESS_DENIED cpu_to_le32(0xC00A002B) -#define STATUS_CTX_INVALID_WD cpu_to_le32(0xC00A002E) -#define STATUS_CTX_WD_NOT_FOUND cpu_to_le32(0xC00A002F) -#define STATUS_CTX_SHADOW_INVALID cpu_to_le32(0xC00A0030) -#define STATUS_CTX_SHADOW_DISABLED cpu_to_le32(0xC00A0031) -#define STATUS_RDP_PROTOCOL_ERROR cpu_to_le32(0xC00A0032) -#define STATUS_CTX_CLIENT_LICENSE_NOT_SET cpu_to_le32(0xC00A0033) -#define STATUS_CTX_CLIENT_LICENSE_IN_USE cpu_to_le32(0xC00A0034) -#define STATUS_CTX_SHADOW_ENDED_BY_MODE_CHANGE cpu_to_le32(0xC00A0035) -#define STATUS_CTX_SHADOW_NOT_RUNNING cpu_to_le32(0xC00A0036) -#define STATUS_CTX_LOGON_DISABLED cpu_to_le32(0xC00A0037) -#define STATUS_CTX_SECURITY_LAYER_ERROR cpu_to_le32(0xC00A0038) -#define STATUS_TS_INCOMPATIBLE_SESSIONS cpu_to_le32(0xC00A0039) -#define STATUS_MUI_FILE_NOT_FOUND cpu_to_le32(0xC00B0001) -#define STATUS_MUI_INVALID_FILE cpu_to_le32(0xC00B0002) -#define STATUS_MUI_INVALID_RC_CONFIG cpu_to_le32(0xC00B0003) -#define STATUS_MUI_INVALID_LOCALE_NAME cpu_to_le32(0xC00B0004) -#define STATUS_MUI_INVALID_ULTIMATEFALLBACK_NAME cpu_to_le32(0xC00B0005) -#define STATUS_MUI_FILE_NOT_LOADED cpu_to_le32(0xC00B0006) -#define STATUS_RESOURCE_ENUM_USER_STOP cpu_to_le32(0xC00B0007) -#define STATUS_CLUSTER_INVALID_NODE cpu_to_le32(0xC0130001) -#define STATUS_CLUSTER_NODE_EXISTS cpu_to_le32(0xC0130002) -#define STATUS_CLUSTER_JOIN_IN_PROGRESS cpu_to_le32(0xC0130003) -#define STATUS_CLUSTER_NODE_NOT_FOUND cpu_to_le32(0xC0130004) -#define STATUS_CLUSTER_LOCAL_NODE_NOT_FOUND cpu_to_le32(0xC0130005) -#define STATUS_CLUSTER_NETWORK_EXISTS cpu_to_le32(0xC0130006) -#define STATUS_CLUSTER_NETWORK_NOT_FOUND cpu_to_le32(0xC0130007) -#define STATUS_CLUSTER_NETINTERFACE_EXISTS cpu_to_le32(0xC0130008) -#define STATUS_CLUSTER_NETINTERFACE_NOT_FOUND cpu_to_le32(0xC0130009) -#define STATUS_CLUSTER_INVALID_REQUEST cpu_to_le32(0xC013000A) -#define STATUS_CLUSTER_INVALID_NETWORK_PROVIDER cpu_to_le32(0xC013000B) -#define STATUS_CLUSTER_NODE_DOWN cpu_to_le32(0xC013000C) -#define STATUS_CLUSTER_NODE_UNREACHABLE cpu_to_le32(0xC013000D) -#define STATUS_CLUSTER_NODE_NOT_MEMBER cpu_to_le32(0xC013000E) -#define STATUS_CLUSTER_JOIN_NOT_IN_PROGRESS cpu_to_le32(0xC013000F) -#define STATUS_CLUSTER_INVALID_NETWORK cpu_to_le32(0xC0130010) -#define STATUS_CLUSTER_NO_NET_ADAPTERS cpu_to_le32(0xC0130011) -#define STATUS_CLUSTER_NODE_UP cpu_to_le32(0xC0130012) -#define STATUS_CLUSTER_NODE_PAUSED cpu_to_le32(0xC0130013) -#define STATUS_CLUSTER_NODE_NOT_PAUSED cpu_to_le32(0xC0130014) -#define STATUS_CLUSTER_NO_SECURITY_CONTEXT cpu_to_le32(0xC0130015) -#define STATUS_CLUSTER_NETWORK_NOT_INTERNAL cpu_to_le32(0xC0130016) -#define STATUS_CLUSTER_POISONED cpu_to_le32(0xC0130017) -#define STATUS_ACPI_INVALID_OPCODE cpu_to_le32(0xC0140001) -#define STATUS_ACPI_STACK_OVERFLOW cpu_to_le32(0xC0140002) -#define STATUS_ACPI_ASSERT_FAILED cpu_to_le32(0xC0140003) -#define STATUS_ACPI_INVALID_INDEX cpu_to_le32(0xC0140004) -#define STATUS_ACPI_INVALID_ARGUMENT cpu_to_le32(0xC0140005) -#define STATUS_ACPI_FATAL cpu_to_le32(0xC0140006) -#define STATUS_ACPI_INVALID_SUPERNAME cpu_to_le32(0xC0140007) -#define STATUS_ACPI_INVALID_ARGTYPE cpu_to_le32(0xC0140008) -#define STATUS_ACPI_INVALID_OBJTYPE cpu_to_le32(0xC0140009) -#define STATUS_ACPI_INVALID_TARGETTYPE cpu_to_le32(0xC014000A) -#define STATUS_ACPI_INCORRECT_ARGUMENT_COUNT cpu_to_le32(0xC014000B) -#define STATUS_ACPI_ADDRESS_NOT_MAPPED cpu_to_le32(0xC014000C) -#define STATUS_ACPI_INVALID_EVENTTYPE cpu_to_le32(0xC014000D) -#define STATUS_ACPI_HANDLER_COLLISION cpu_to_le32(0xC014000E) -#define STATUS_ACPI_INVALID_DATA cpu_to_le32(0xC014000F) -#define STATUS_ACPI_INVALID_REGION cpu_to_le32(0xC0140010) -#define STATUS_ACPI_INVALID_ACCESS_SIZE cpu_to_le32(0xC0140011) -#define STATUS_ACPI_ACQUIRE_GLOBAL_LOCK cpu_to_le32(0xC0140012) -#define STATUS_ACPI_ALREADY_INITIALIZED cpu_to_le32(0xC0140013) -#define STATUS_ACPI_NOT_INITIALIZED cpu_to_le32(0xC0140014) -#define STATUS_ACPI_INVALID_MUTEX_LEVEL cpu_to_le32(0xC0140015) -#define STATUS_ACPI_MUTEX_NOT_OWNED cpu_to_le32(0xC0140016) -#define STATUS_ACPI_MUTEX_NOT_OWNER cpu_to_le32(0xC0140017) -#define STATUS_ACPI_RS_ACCESS cpu_to_le32(0xC0140018) -#define STATUS_ACPI_INVALID_TABLE cpu_to_le32(0xC0140019) -#define STATUS_ACPI_REG_HANDLER_FAILED cpu_to_le32(0xC0140020) -#define STATUS_ACPI_POWER_REQUEST_FAILED cpu_to_le32(0xC0140021) -#define STATUS_SXS_SECTION_NOT_FOUND cpu_to_le32(0xC0150001) -#define STATUS_SXS_CANT_GEN_ACTCTX cpu_to_le32(0xC0150002) -#define STATUS_SXS_INVALID_ACTCTXDATA_FORMAT cpu_to_le32(0xC0150003) -#define STATUS_SXS_ASSEMBLY_NOT_FOUND cpu_to_le32(0xC0150004) -#define STATUS_SXS_MANIFEST_FORMAT_ERROR cpu_to_le32(0xC0150005) -#define STATUS_SXS_MANIFEST_PARSE_ERROR cpu_to_le32(0xC0150006) -#define STATUS_SXS_ACTIVATION_CONTEXT_DISABLED cpu_to_le32(0xC0150007) -#define STATUS_SXS_KEY_NOT_FOUND cpu_to_le32(0xC0150008) -#define STATUS_SXS_VERSION_CONFLICT cpu_to_le32(0xC0150009) -#define STATUS_SXS_WRONG_SECTION_TYPE cpu_to_le32(0xC015000A) -#define STATUS_SXS_THREAD_QUERIES_DISABLED cpu_to_le32(0xC015000B) -#define STATUS_SXS_ASSEMBLY_MISSING cpu_to_le32(0xC015000C) -#define STATUS_SXS_PROCESS_DEFAULT_ALREADY_SET cpu_to_le32(0xC015000E) -#define STATUS_SXS_EARLY_DEACTIVATION cpu_to_le32(0xC015000F) -#define STATUS_SXS_INVALID_DEACTIVATION cpu_to_le32(0xC0150010) -#define STATUS_SXS_MULTIPLE_DEACTIVATION cpu_to_le32(0xC0150011) -#define STATUS_SXS_SYSTEM_DEFAULT_ACTIVATION_CONTEXT_EMPTY \ - cpu_to_le32(0xC0150012) -#define STATUS_SXS_PROCESS_TERMINATION_REQUESTED cpu_to_le32(0xC0150013) -#define STATUS_SXS_CORRUPT_ACTIVATION_STACK cpu_to_le32(0xC0150014) -#define STATUS_SXS_CORRUPTION cpu_to_le32(0xC0150015) -#define STATUS_SXS_INVALID_IDENTITY_ATTRIBUTE_VALUE cpu_to_le32(0xC0150016) -#define STATUS_SXS_INVALID_IDENTITY_ATTRIBUTE_NAME cpu_to_le32(0xC0150017) -#define STATUS_SXS_IDENTITY_DUPLICATE_ATTRIBUTE cpu_to_le32(0xC0150018) -#define STATUS_SXS_IDENTITY_PARSE_ERROR cpu_to_le32(0xC0150019) -#define STATUS_SXS_COMPONENT_STORE_CORRUPT cpu_to_le32(0xC015001A) -#define STATUS_SXS_FILE_HASH_MISMATCH cpu_to_le32(0xC015001B) -#define STATUS_SXS_MANIFEST_IDENTITY_SAME_BUT_CONTENTS_DIFFERENT \ - cpu_to_le32(0xC015001C) -#define STATUS_SXS_IDENTITIES_DIFFERENT cpu_to_le32(0xC015001D) -#define STATUS_SXS_ASSEMBLY_IS_NOT_A_DEPLOYMENT cpu_to_le32(0xC015001E) -#define STATUS_SXS_FILE_NOT_PART_OF_ASSEMBLY cpu_to_le32(0xC015001F) -#define STATUS_ADVANCED_INSTALLER_FAILED cpu_to_le32(0xC0150020) -#define STATUS_XML_ENCODING_MISMATCH cpu_to_le32(0xC0150021) -#define STATUS_SXS_MANIFEST_TOO_BIG cpu_to_le32(0xC0150022) -#define STATUS_SXS_SETTING_NOT_REGISTERED cpu_to_le32(0xC0150023) -#define STATUS_SXS_TRANSACTION_CLOSURE_INCOMPLETE cpu_to_le32(0xC0150024) -#define STATUS_SMI_PRIMITIVE_INSTALLER_FAILED cpu_to_le32(0xC0150025) -#define STATUS_GENERIC_COMMAND_FAILED cpu_to_le32(0xC0150026) -#define STATUS_SXS_FILE_HASH_MISSING cpu_to_le32(0xC0150027) -#define STATUS_TRANSACTIONAL_CONFLICT cpu_to_le32(0xC0190001) -#define STATUS_INVALID_TRANSACTION cpu_to_le32(0xC0190002) -#define STATUS_TRANSACTION_NOT_ACTIVE cpu_to_le32(0xC0190003) -#define STATUS_TM_INITIALIZATION_FAILED cpu_to_le32(0xC0190004) -#define STATUS_RM_NOT_ACTIVE cpu_to_le32(0xC0190005) -#define STATUS_RM_METADATA_CORRUPT cpu_to_le32(0xC0190006) -#define STATUS_TRANSACTION_NOT_JOINED cpu_to_le32(0xC0190007) -#define STATUS_DIRECTORY_NOT_RM cpu_to_le32(0xC0190008) -#define STATUS_TRANSACTIONS_UNSUPPORTED_REMOTE cpu_to_le32(0xC019000A) -#define STATUS_LOG_RESIZE_INVALID_SIZE cpu_to_le32(0xC019000B) -#define STATUS_REMOTE_FILE_VERSION_MISMATCH cpu_to_le32(0xC019000C) -#define STATUS_CRM_PROTOCOL_ALREADY_EXISTS cpu_to_le32(0xC019000F) -#define STATUS_TRANSACTION_PROPAGATION_FAILED cpu_to_le32(0xC0190010) -#define STATUS_CRM_PROTOCOL_NOT_FOUND cpu_to_le32(0xC0190011) -#define STATUS_TRANSACTION_SUPERIOR_EXISTS cpu_to_le32(0xC0190012) -#define STATUS_TRANSACTION_REQUEST_NOT_VALID cpu_to_le32(0xC0190013) -#define STATUS_TRANSACTION_NOT_REQUESTED cpu_to_le32(0xC0190014) -#define STATUS_TRANSACTION_ALREADY_ABORTED cpu_to_le32(0xC0190015) -#define STATUS_TRANSACTION_ALREADY_COMMITTED cpu_to_le32(0xC0190016) -#define STATUS_TRANSACTION_INVALID_MARSHALL_BUFFER cpu_to_le32(0xC0190017) -#define STATUS_CURRENT_TRANSACTION_NOT_VALID cpu_to_le32(0xC0190018) -#define STATUS_LOG_GROWTH_FAILED cpu_to_le32(0xC0190019) -#define STATUS_OBJECT_NO_LONGER_EXISTS cpu_to_le32(0xC0190021) -#define STATUS_STREAM_MINIVERSION_NOT_FOUND cpu_to_le32(0xC0190022) -#define STATUS_STREAM_MINIVERSION_NOT_VALID cpu_to_le32(0xC0190023) -#define STATUS_MINIVERSION_INACCESSIBLE_FROM_SPECIFIED_TRANSACTION \ - cpu_to_le32(0xC0190024) -#define STATUS_CANT_OPEN_MINIVERSION_WITH_MODIFY_INTENT cpu_to_le32(0xC0190025) -#define STATUS_CANT_CREATE_MORE_STREAM_MINIVERSIONS cpu_to_le32(0xC0190026) -#define STATUS_HANDLE_NO_LONGER_VALID cpu_to_le32(0xC0190028) -#define STATUS_LOG_CORRUPTION_DETECTED cpu_to_le32(0xC0190030) -#define STATUS_RM_DISCONNECTED cpu_to_le32(0xC0190032) -#define STATUS_ENLISTMENT_NOT_SUPERIOR cpu_to_le32(0xC0190033) -#define STATUS_FILE_IDENTITY_NOT_PERSISTENT cpu_to_le32(0xC0190036) -#define STATUS_CANT_BREAK_TRANSACTIONAL_DEPENDENCY cpu_to_le32(0xC0190037) -#define STATUS_CANT_CROSS_RM_BOUNDARY cpu_to_le32(0xC0190038) -#define STATUS_TXF_DIR_NOT_EMPTY cpu_to_le32(0xC0190039) -#define STATUS_INDOUBT_TRANSACTIONS_EXIST cpu_to_le32(0xC019003A) -#define STATUS_TM_VOLATILE cpu_to_le32(0xC019003B) -#define STATUS_ROLLBACK_TIMER_EXPIRED cpu_to_le32(0xC019003C) -#define STATUS_TXF_ATTRIBUTE_CORRUPT cpu_to_le32(0xC019003D) -#define STATUS_EFS_NOT_ALLOWED_IN_TRANSACTION cpu_to_le32(0xC019003E) -#define STATUS_TRANSACTIONAL_OPEN_NOT_ALLOWED cpu_to_le32(0xC019003F) -#define STATUS_TRANSACTED_MAPPING_UNSUPPORTED_REMOTE cpu_to_le32(0xC0190040) -#define STATUS_TRANSACTION_REQUIRED_PROMOTION cpu_to_le32(0xC0190043) -#define STATUS_CANNOT_EXECUTE_FILE_IN_TRANSACTION cpu_to_le32(0xC0190044) -#define STATUS_TRANSACTIONS_NOT_FROZEN cpu_to_le32(0xC0190045) -#define STATUS_TRANSACTION_FREEZE_IN_PROGRESS cpu_to_le32(0xC0190046) -#define STATUS_NOT_SNAPSHOT_VOLUME cpu_to_le32(0xC0190047) -#define STATUS_NO_SAVEPOINT_WITH_OPEN_FILES cpu_to_le32(0xC0190048) -#define STATUS_SPARSE_NOT_ALLOWED_IN_TRANSACTION cpu_to_le32(0xC0190049) -#define STATUS_TM_IDENTITY_MISMATCH cpu_to_le32(0xC019004A) -#define STATUS_FLOATED_SECTION cpu_to_le32(0xC019004B) -#define STATUS_CANNOT_ACCEPT_TRANSACTED_WORK cpu_to_le32(0xC019004C) -#define STATUS_CANNOT_ABORT_TRANSACTIONS cpu_to_le32(0xC019004D) -#define STATUS_TRANSACTION_NOT_FOUND cpu_to_le32(0xC019004E) -#define STATUS_RESOURCEMANAGER_NOT_FOUND cpu_to_le32(0xC019004F) -#define STATUS_ENLISTMENT_NOT_FOUND cpu_to_le32(0xC0190050) -#define STATUS_TRANSACTIONMANAGER_NOT_FOUND cpu_to_le32(0xC0190051) -#define STATUS_TRANSACTIONMANAGER_NOT_ONLINE cpu_to_le32(0xC0190052) -#define STATUS_TRANSACTIONMANAGER_RECOVERY_NAME_COLLISION \ - cpu_to_le32(0xC0190053) -#define STATUS_TRANSACTION_NOT_ROOT cpu_to_le32(0xC0190054) -#define STATUS_TRANSACTION_OBJECT_EXPIRED cpu_to_le32(0xC0190055) -#define STATUS_COMPRESSION_NOT_ALLOWED_IN_TRANSACTION cpu_to_le32(0xC0190056) -#define STATUS_TRANSACTION_RESPONSE_NOT_ENLISTED cpu_to_le32(0xC0190057) -#define STATUS_TRANSACTION_RECORD_TOO_LONG cpu_to_le32(0xC0190058) -#define STATUS_NO_LINK_TRACKING_IN_TRANSACTION cpu_to_le32(0xC0190059) -#define STATUS_OPERATION_NOT_SUPPORTED_IN_TRANSACTION cpu_to_le32(0xC019005A) -#define STATUS_TRANSACTION_INTEGRITY_VIOLATED cpu_to_le32(0xC019005B) -#define STATUS_LOG_SECTOR_INVALID cpu_to_le32(0xC01A0001) -#define STATUS_LOG_SECTOR_PARITY_INVALID cpu_to_le32(0xC01A0002) -#define STATUS_LOG_SECTOR_REMAPPED cpu_to_le32(0xC01A0003) -#define STATUS_LOG_BLOCK_INCOMPLETE cpu_to_le32(0xC01A0004) -#define STATUS_LOG_INVALID_RANGE cpu_to_le32(0xC01A0005) -#define STATUS_LOG_BLOCKS_EXHAUSTED cpu_to_le32(0xC01A0006) -#define STATUS_LOG_READ_CONTEXT_INVALID cpu_to_le32(0xC01A0007) -#define STATUS_LOG_RESTART_INVALID cpu_to_le32(0xC01A0008) -#define STATUS_LOG_BLOCK_VERSION cpu_to_le32(0xC01A0009) -#define STATUS_LOG_BLOCK_INVALID cpu_to_le32(0xC01A000A) -#define STATUS_LOG_READ_MODE_INVALID cpu_to_le32(0xC01A000B) -#define STATUS_LOG_METADATA_CORRUPT cpu_to_le32(0xC01A000D) -#define STATUS_LOG_METADATA_INVALID cpu_to_le32(0xC01A000E) -#define STATUS_LOG_METADATA_INCONSISTENT cpu_to_le32(0xC01A000F) -#define STATUS_LOG_RESERVATION_INVALID cpu_to_le32(0xC01A0010) -#define STATUS_LOG_CANT_DELETE cpu_to_le32(0xC01A0011) -#define STATUS_LOG_CONTAINER_LIMIT_EXCEEDED cpu_to_le32(0xC01A0012) -#define STATUS_LOG_START_OF_LOG cpu_to_le32(0xC01A0013) -#define STATUS_LOG_POLICY_ALREADY_INSTALLED cpu_to_le32(0xC01A0014) -#define STATUS_LOG_POLICY_NOT_INSTALLED cpu_to_le32(0xC01A0015) -#define STATUS_LOG_POLICY_INVALID cpu_to_le32(0xC01A0016) -#define STATUS_LOG_POLICY_CONFLICT cpu_to_le32(0xC01A0017) -#define STATUS_LOG_PINNED_ARCHIVE_TAIL cpu_to_le32(0xC01A0018) -#define STATUS_LOG_RECORD_NONEXISTENT cpu_to_le32(0xC01A0019) -#define STATUS_LOG_RECORDS_RESERVED_INVALID cpu_to_le32(0xC01A001A) -#define STATUS_LOG_SPACE_RESERVED_INVALID cpu_to_le32(0xC01A001B) -#define STATUS_LOG_TAIL_INVALID cpu_to_le32(0xC01A001C) -#define STATUS_LOG_FULL cpu_to_le32(0xC01A001D) -#define STATUS_LOG_MULTIPLEXED cpu_to_le32(0xC01A001E) -#define STATUS_LOG_DEDICATED cpu_to_le32(0xC01A001F) -#define STATUS_LOG_ARCHIVE_NOT_IN_PROGRESS cpu_to_le32(0xC01A0020) -#define STATUS_LOG_ARCHIVE_IN_PROGRESS cpu_to_le32(0xC01A0021) -#define STATUS_LOG_EPHEMERAL cpu_to_le32(0xC01A0022) -#define STATUS_LOG_NOT_ENOUGH_CONTAINERS cpu_to_le32(0xC01A0023) -#define STATUS_LOG_CLIENT_ALREADY_REGISTERED cpu_to_le32(0xC01A0024) -#define STATUS_LOG_CLIENT_NOT_REGISTERED cpu_to_le32(0xC01A0025) -#define STATUS_LOG_FULL_HANDLER_IN_PROGRESS cpu_to_le32(0xC01A0026) -#define STATUS_LOG_CONTAINER_READ_FAILED cpu_to_le32(0xC01A0027) -#define STATUS_LOG_CONTAINER_WRITE_FAILED cpu_to_le32(0xC01A0028) -#define STATUS_LOG_CONTAINER_OPEN_FAILED cpu_to_le32(0xC01A0029) -#define STATUS_LOG_CONTAINER_STATE_INVALID cpu_to_le32(0xC01A002A) -#define STATUS_LOG_STATE_INVALID cpu_to_le32(0xC01A002B) -#define STATUS_LOG_PINNED cpu_to_le32(0xC01A002C) -#define STATUS_LOG_METADATA_FLUSH_FAILED cpu_to_le32(0xC01A002D) -#define STATUS_LOG_INCONSISTENT_SECURITY cpu_to_le32(0xC01A002E) -#define STATUS_LOG_APPENDED_FLUSH_FAILED cpu_to_le32(0xC01A002F) -#define STATUS_LOG_PINNED_RESERVATION cpu_to_le32(0xC01A0030) -#define STATUS_VIDEO_HUNG_DISPLAY_DRIVER_THREAD cpu_to_le32(0xC01B00EA) -#define STATUS_FLT_NO_HANDLER_DEFINED cpu_to_le32(0xC01C0001) -#define STATUS_FLT_CONTEXT_ALREADY_DEFINED cpu_to_le32(0xC01C0002) -#define STATUS_FLT_INVALID_ASYNCHRONOUS_REQUEST cpu_to_le32(0xC01C0003) -#define STATUS_FLT_DISALLOW_FAST_IO cpu_to_le32(0xC01C0004) -#define STATUS_FLT_INVALID_NAME_REQUEST cpu_to_le32(0xC01C0005) -#define STATUS_FLT_NOT_SAFE_TO_POST_OPERATION cpu_to_le32(0xC01C0006) -#define STATUS_FLT_NOT_INITIALIZED cpu_to_le32(0xC01C0007) -#define STATUS_FLT_FILTER_NOT_READY cpu_to_le32(0xC01C0008) -#define STATUS_FLT_POST_OPERATION_CLEANUP cpu_to_le32(0xC01C0009) -#define STATUS_FLT_INTERNAL_ERROR cpu_to_le32(0xC01C000A) -#define STATUS_FLT_DELETING_OBJECT cpu_to_le32(0xC01C000B) -#define STATUS_FLT_MUST_BE_NONPAGED_POOL cpu_to_le32(0xC01C000C) -#define STATUS_FLT_DUPLICATE_ENTRY cpu_to_le32(0xC01C000D) -#define STATUS_FLT_CBDQ_DISABLED cpu_to_le32(0xC01C000E) -#define STATUS_FLT_DO_NOT_ATTACH cpu_to_le32(0xC01C000F) -#define STATUS_FLT_DO_NOT_DETACH cpu_to_le32(0xC01C0010) -#define STATUS_FLT_INSTANCE_ALTITUDE_COLLISION cpu_to_le32(0xC01C0011) -#define STATUS_FLT_INSTANCE_NAME_COLLISION cpu_to_le32(0xC01C0012) -#define STATUS_FLT_FILTER_NOT_FOUND cpu_to_le32(0xC01C0013) -#define STATUS_FLT_VOLUME_NOT_FOUND cpu_to_le32(0xC01C0014) -#define STATUS_FLT_INSTANCE_NOT_FOUND cpu_to_le32(0xC01C0015) -#define STATUS_FLT_CONTEXT_ALLOCATION_NOT_FOUND cpu_to_le32(0xC01C0016) -#define STATUS_FLT_INVALID_CONTEXT_REGISTRATION cpu_to_le32(0xC01C0017) -#define STATUS_FLT_NAME_CACHE_MISS cpu_to_le32(0xC01C0018) -#define STATUS_FLT_NO_DEVICE_OBJECT cpu_to_le32(0xC01C0019) -#define STATUS_FLT_VOLUME_ALREADY_MOUNTED cpu_to_le32(0xC01C001A) -#define STATUS_FLT_ALREADY_ENLISTED cpu_to_le32(0xC01C001B) -#define STATUS_FLT_CONTEXT_ALREADY_LINKED cpu_to_le32(0xC01C001C) -#define STATUS_FLT_NO_WAITER_FOR_REPLY cpu_to_le32(0xC01C0020) -#define STATUS_MONITOR_NO_DESCRIPTOR cpu_to_le32(0xC01D0001) -#define STATUS_MONITOR_UNKNOWN_DESCRIPTOR_FORMAT cpu_to_le32(0xC01D0002) -#define STATUS_MONITOR_INVALID_DESCRIPTOR_CHECKSUM cpu_to_le32(0xC01D0003) -#define STATUS_MONITOR_INVALID_STANDARD_TIMING_BLOCK cpu_to_le32(0xC01D0004) -#define STATUS_MONITOR_WMI_DATABLOCK_REGISTRATION_FAILED cpu_to_le32(0xC01D0005) -#define STATUS_MONITOR_INVALID_SERIAL_NUMBER_MONDSC_BLOCK \ - cpu_to_le32(0xC01D0006) -#define STATUS_MONITOR_INVALID_USER_FRIENDLY_MONDSC_BLOCK \ - cpu_to_le32(0xC01D0007) -#define STATUS_MONITOR_NO_MORE_DESCRIPTOR_DATA cpu_to_le32(0xC01D0008) -#define STATUS_MONITOR_INVALID_DETAILED_TIMING_BLOCK cpu_to_le32(0xC01D0009) -#define STATUS_GRAPHICS_NOT_EXCLUSIVE_MODE_OWNER cpu_to_le32(0xC01E0000) -#define STATUS_GRAPHICS_INSUFFICIENT_DMA_BUFFER cpu_to_le32(0xC01E0001) -#define STATUS_GRAPHICS_INVALID_DISPLAY_ADAPTER cpu_to_le32(0xC01E0002) -#define STATUS_GRAPHICS_ADAPTER_WAS_RESET cpu_to_le32(0xC01E0003) -#define STATUS_GRAPHICS_INVALID_DRIVER_MODEL cpu_to_le32(0xC01E0004) -#define STATUS_GRAPHICS_PRESENT_MODE_CHANGED cpu_to_le32(0xC01E0005) -#define STATUS_GRAPHICS_PRESENT_OCCLUDED cpu_to_le32(0xC01E0006) -#define STATUS_GRAPHICS_PRESENT_DENIED cpu_to_le32(0xC01E0007) -#define STATUS_GRAPHICS_CANNOTCOLORCONVERT cpu_to_le32(0xC01E0008) -#define STATUS_GRAPHICS_NO_VIDEO_MEMORY cpu_to_le32(0xC01E0100) -#define STATUS_GRAPHICS_CANT_LOCK_MEMORY cpu_to_le32(0xC01E0101) -#define STATUS_GRAPHICS_ALLOCATION_BUSY cpu_to_le32(0xC01E0102) -#define STATUS_GRAPHICS_TOO_MANY_REFERENCES cpu_to_le32(0xC01E0103) -#define STATUS_GRAPHICS_TRY_AGAIN_LATER cpu_to_le32(0xC01E0104) -#define STATUS_GRAPHICS_TRY_AGAIN_NOW cpu_to_le32(0xC01E0105) -#define STATUS_GRAPHICS_ALLOCATION_INVALID cpu_to_le32(0xC01E0106) -#define STATUS_GRAPHICS_UNSWIZZLING_APERTURE_UNAVAILABLE cpu_to_le32(0xC01E0107) -#define STATUS_GRAPHICS_UNSWIZZLING_APERTURE_UNSUPPORTED cpu_to_le32(0xC01E0108) -#define STATUS_GRAPHICS_CANT_EVICT_PINNED_ALLOCATION cpu_to_le32(0xC01E0109) -#define STATUS_GRAPHICS_INVALID_ALLOCATION_USAGE cpu_to_le32(0xC01E0110) -#define STATUS_GRAPHICS_CANT_RENDER_LOCKED_ALLOCATION cpu_to_le32(0xC01E0111) -#define STATUS_GRAPHICS_ALLOCATION_CLOSED cpu_to_le32(0xC01E0112) -#define STATUS_GRAPHICS_INVALID_ALLOCATION_INSTANCE cpu_to_le32(0xC01E0113) -#define STATUS_GRAPHICS_INVALID_ALLOCATION_HANDLE cpu_to_le32(0xC01E0114) -#define STATUS_GRAPHICS_WRONG_ALLOCATION_DEVICE cpu_to_le32(0xC01E0115) -#define STATUS_GRAPHICS_ALLOCATION_CONTENT_LOST cpu_to_le32(0xC01E0116) -#define STATUS_GRAPHICS_GPU_EXCEPTION_ON_DEVICE cpu_to_le32(0xC01E0200) -#define STATUS_GRAPHICS_INVALID_VIDPN_TOPOLOGY cpu_to_le32(0xC01E0300) -#define STATUS_GRAPHICS_VIDPN_TOPOLOGY_NOT_SUPPORTED cpu_to_le32(0xC01E0301) -#define STATUS_GRAPHICS_VIDPN_TOPOLOGY_CURRENTLY_NOT_SUPPORTED \ - cpu_to_le32(0xC01E0302) -#define STATUS_GRAPHICS_INVALID_VIDPN cpu_to_le32(0xC01E0303) -#define STATUS_GRAPHICS_INVALID_VIDEO_PRESENT_SOURCE cpu_to_le32(0xC01E0304) -#define STATUS_GRAPHICS_INVALID_VIDEO_PRESENT_TARGET cpu_to_le32(0xC01E0305) -#define STATUS_GRAPHICS_VIDPN_MODALITY_NOT_SUPPORTED cpu_to_le32(0xC01E0306) -#define STATUS_GRAPHICS_INVALID_VIDPN_SOURCEMODESET cpu_to_le32(0xC01E0308) -#define STATUS_GRAPHICS_INVALID_VIDPN_TARGETMODESET cpu_to_le32(0xC01E0309) -#define STATUS_GRAPHICS_INVALID_FREQUENCY cpu_to_le32(0xC01E030A) -#define STATUS_GRAPHICS_INVALID_ACTIVE_REGION cpu_to_le32(0xC01E030B) -#define STATUS_GRAPHICS_INVALID_TOTAL_REGION cpu_to_le32(0xC01E030C) -#define STATUS_GRAPHICS_INVALID_VIDEO_PRESENT_SOURCE_MODE \ - cpu_to_le32(0xC01E0310) -#define STATUS_GRAPHICS_INVALID_VIDEO_PRESENT_TARGET_MODE \ - cpu_to_le32(0xC01E0311) -#define STATUS_GRAPHICS_PINNED_MODE_MUST_REMAIN_IN_SET cpu_to_le32(0xC01E0312) -#define STATUS_GRAPHICS_PATH_ALREADY_IN_TOPOLOGY cpu_to_le32(0xC01E0313) -#define STATUS_GRAPHICS_MODE_ALREADY_IN_MODESET cpu_to_le32(0xC01E0314) -#define STATUS_GRAPHICS_INVALID_VIDEOPRESENTSOURCESET cpu_to_le32(0xC01E0315) -#define STATUS_GRAPHICS_INVALID_VIDEOPRESENTTARGETSET cpu_to_le32(0xC01E0316) -#define STATUS_GRAPHICS_SOURCE_ALREADY_IN_SET cpu_to_le32(0xC01E0317) -#define STATUS_GRAPHICS_TARGET_ALREADY_IN_SET cpu_to_le32(0xC01E0318) -#define STATUS_GRAPHICS_INVALID_VIDPN_PRESENT_PATH cpu_to_le32(0xC01E0319) -#define STATUS_GRAPHICS_NO_RECOMMENDED_VIDPN_TOPOLOGY cpu_to_le32(0xC01E031A) -#define STATUS_GRAPHICS_INVALID_MONITOR_FREQUENCYRANGESET \ - cpu_to_le32(0xC01E031B) -#define STATUS_GRAPHICS_INVALID_MONITOR_FREQUENCYRANGE cpu_to_le32(0xC01E031C) -#define STATUS_GRAPHICS_FREQUENCYRANGE_NOT_IN_SET cpu_to_le32(0xC01E031D) -#define STATUS_GRAPHICS_FREQUENCYRANGE_ALREADY_IN_SET cpu_to_le32(0xC01E031F) -#define STATUS_GRAPHICS_STALE_MODESET cpu_to_le32(0xC01E0320) -#define STATUS_GRAPHICS_INVALID_MONITOR_SOURCEMODESET cpu_to_le32(0xC01E0321) -#define STATUS_GRAPHICS_INVALID_MONITOR_SOURCE_MODE cpu_to_le32(0xC01E0322) -#define STATUS_GRAPHICS_NO_RECOMMENDED_FUNCTIONAL_VIDPN cpu_to_le32(0xC01E0323) -#define STATUS_GRAPHICS_MODE_ID_MUST_BE_UNIQUE cpu_to_le32(0xC01E0324) -#define STATUS_GRAPHICS_EMPTY_ADAPTER_MONITOR_MODE_SUPPORT_INTERSECTION \ - cpu_to_le32(0xC01E0325) -#define STATUS_GRAPHICS_VIDEO_PRESENT_TARGETS_LESS_THAN_SOURCES \ - cpu_to_le32(0xC01E0326) -#define STATUS_GRAPHICS_PATH_NOT_IN_TOPOLOGY cpu_to_le32(0xC01E0327) -#define STATUS_GRAPHICS_ADAPTER_MUST_HAVE_AT_LEAST_ONE_SOURCE \ - cpu_to_le32(0xC01E0328) -#define STATUS_GRAPHICS_ADAPTER_MUST_HAVE_AT_LEAST_ONE_TARGET \ - cpu_to_le32(0xC01E0329) -#define STATUS_GRAPHICS_INVALID_MONITORDESCRIPTORSET cpu_to_le32(0xC01E032A) -#define STATUS_GRAPHICS_INVALID_MONITORDESCRIPTOR cpu_to_le32(0xC01E032B) -#define STATUS_GRAPHICS_MONITORDESCRIPTOR_NOT_IN_SET cpu_to_le32(0xC01E032C) -#define STATUS_GRAPHICS_MONITORDESCRIPTOR_ALREADY_IN_SET cpu_to_le32(0xC01E032D) -#define STATUS_GRAPHICS_MONITORDESCRIPTOR_ID_MUST_BE_UNIQUE \ - cpu_to_le32(0xC01E032E) -#define STATUS_GRAPHICS_INVALID_VIDPN_TARGET_SUBSET_TYPE cpu_to_le32(0xC01E032F) -#define STATUS_GRAPHICS_RESOURCES_NOT_RELATED cpu_to_le32(0xC01E0330) -#define STATUS_GRAPHICS_SOURCE_ID_MUST_BE_UNIQUE cpu_to_le32(0xC01E0331) -#define STATUS_GRAPHICS_TARGET_ID_MUST_BE_UNIQUE cpu_to_le32(0xC01E0332) -#define STATUS_GRAPHICS_NO_AVAILABLE_VIDPN_TARGET cpu_to_le32(0xC01E0333) -#define STATUS_GRAPHICS_MONITOR_COULD_NOT_BE_ASSOCIATED_WITH_ADAPTER \ - cpu_to_le32(0xC01E0334) -#define STATUS_GRAPHICS_NO_VIDPNMGR cpu_to_le32(0xC01E0335) -#define STATUS_GRAPHICS_NO_ACTIVE_VIDPN cpu_to_le32(0xC01E0336) -#define STATUS_GRAPHICS_STALE_VIDPN_TOPOLOGY cpu_to_le32(0xC01E0337) -#define STATUS_GRAPHICS_MONITOR_NOT_CONNECTED cpu_to_le32(0xC01E0338) -#define STATUS_GRAPHICS_SOURCE_NOT_IN_TOPOLOGY cpu_to_le32(0xC01E0339) -#define STATUS_GRAPHICS_INVALID_PRIMARYSURFACE_SIZE cpu_to_le32(0xC01E033A) -#define STATUS_GRAPHICS_INVALID_VISIBLEREGION_SIZE cpu_to_le32(0xC01E033B) -#define STATUS_GRAPHICS_INVALID_STRIDE cpu_to_le32(0xC01E033C) -#define STATUS_GRAPHICS_INVALID_PIXELFORMAT cpu_to_le32(0xC01E033D) -#define STATUS_GRAPHICS_INVALID_COLORBASIS cpu_to_le32(0xC01E033E) -#define STATUS_GRAPHICS_INVALID_PIXELVALUEACCESSMODE cpu_to_le32(0xC01E033F) -#define STATUS_GRAPHICS_TARGET_NOT_IN_TOPOLOGY cpu_to_le32(0xC01E0340) -#define STATUS_GRAPHICS_NO_DISPLAY_MODE_MANAGEMENT_SUPPORT \ - cpu_to_le32(0xC01E0341) -#define STATUS_GRAPHICS_VIDPN_SOURCE_IN_USE cpu_to_le32(0xC01E0342) -#define STATUS_GRAPHICS_CANT_ACCESS_ACTIVE_VIDPN cpu_to_le32(0xC01E0343) -#define STATUS_GRAPHICS_INVALID_PATH_IMPORTANCE_ORDINAL cpu_to_le32(0xC01E0344) -#define STATUS_GRAPHICS_INVALID_PATH_CONTENT_GEOMETRY_TRANSFORMATION \ - cpu_to_le32(0xC01E0345) -#define STATUS_GRAPHICS_PATH_CONTENT_GEOMETRY_TRANSFORMATION_NOT_SUPPORTED \ - cpu_to_le32(0xC01E0346) -#define STATUS_GRAPHICS_INVALID_GAMMA_RAMP cpu_to_le32(0xC01E0347) -#define STATUS_GRAPHICS_GAMMA_RAMP_NOT_SUPPORTED cpu_to_le32(0xC01E0348) -#define STATUS_GRAPHICS_MULTISAMPLING_NOT_SUPPORTED cpu_to_le32(0xC01E0349) -#define STATUS_GRAPHICS_MODE_NOT_IN_MODESET cpu_to_le32(0xC01E034A) -#define STATUS_GRAPHICS_INVALID_VIDPN_TOPOLOGY_RECOMMENDATION_REASON \ - cpu_to_le32(0xC01E034D) -#define STATUS_GRAPHICS_INVALID_PATH_CONTENT_TYPE cpu_to_le32(0xC01E034E) -#define STATUS_GRAPHICS_INVALID_COPYPROTECTION_TYPE cpu_to_le32(0xC01E034F) -#define STATUS_GRAPHICS_UNASSIGNED_MODESET_ALREADY_EXISTS \ - cpu_to_le32(0xC01E0350) -#define STATUS_GRAPHICS_INVALID_SCANLINE_ORDERING cpu_to_le32(0xC01E0352) -#define STATUS_GRAPHICS_TOPOLOGY_CHANGES_NOT_ALLOWED cpu_to_le32(0xC01E0353) -#define STATUS_GRAPHICS_NO_AVAILABLE_IMPORTANCE_ORDINALS cpu_to_le32(0xC01E0354) -#define STATUS_GRAPHICS_INCOMPATIBLE_PRIVATE_FORMAT cpu_to_le32(0xC01E0355) -#define STATUS_GRAPHICS_INVALID_MODE_PRUNING_ALGORITHM cpu_to_le32(0xC01E0356) -#define STATUS_GRAPHICS_INVALID_MONITOR_CAPABILITY_ORIGIN \ - cpu_to_le32(0xC01E0357) -#define STATUS_GRAPHICS_INVALID_MONITOR_FREQUENCYRANGE_CONSTRAINT \ - cpu_to_le32(0xC01E0358) -#define STATUS_GRAPHICS_MAX_NUM_PATHS_REACHED cpu_to_le32(0xC01E0359) -#define STATUS_GRAPHICS_CANCEL_VIDPN_TOPOLOGY_AUGMENTATION \ - cpu_to_le32(0xC01E035A) -#define STATUS_GRAPHICS_INVALID_CLIENT_TYPE cpu_to_le32(0xC01E035B) -#define STATUS_GRAPHICS_CLIENTVIDPN_NOT_SET cpu_to_le32(0xC01E035C) -#define STATUS_GRAPHICS_SPECIFIED_CHILD_ALREADY_CONNECTED \ - cpu_to_le32(0xC01E0400) -#define STATUS_GRAPHICS_CHILD_DESCRIPTOR_NOT_SUPPORTED cpu_to_le32(0xC01E0401) -#define STATUS_GRAPHICS_NOT_A_LINKED_ADAPTER cpu_to_le32(0xC01E0430) -#define STATUS_GRAPHICS_LEADLINK_NOT_ENUMERATED cpu_to_le32(0xC01E0431) -#define STATUS_GRAPHICS_CHAINLINKS_NOT_ENUMERATED cpu_to_le32(0xC01E0432) -#define STATUS_GRAPHICS_ADAPTER_CHAIN_NOT_READY cpu_to_le32(0xC01E0433) -#define STATUS_GRAPHICS_CHAINLINKS_NOT_STARTED cpu_to_le32(0xC01E0434) -#define STATUS_GRAPHICS_CHAINLINKS_NOT_POWERED_ON cpu_to_le32(0xC01E0435) -#define STATUS_GRAPHICS_INCONSISTENT_DEVICE_LINK_STATE cpu_to_le32(0xC01E0436) -#define STATUS_GRAPHICS_NOT_POST_DEVICE_DRIVER cpu_to_le32(0xC01E0438) -#define STATUS_GRAPHICS_ADAPTER_ACCESS_NOT_EXCLUDED cpu_to_le32(0xC01E043B) -#define STATUS_GRAPHICS_OPM_PROTECTED_OUTPUT_DOES_NOT_HAVE_COPP_SEMANTICS \ - cpu_to_le32(0xC01E051C) -#define STATUS_GRAPHICS_OPM_INVALID_INFORMATION_REQUEST cpu_to_le32(0xC01E051D) -#define STATUS_GRAPHICS_OPM_DRIVER_INTERNAL_ERROR cpu_to_le32(0xC01E051E) -#define STATUS_GRAPHICS_OPM_PROTECTED_OUTPUT_DOES_NOT_HAVE_OPM_SEMANTICS \ - cpu_to_le32(0xC01E051F) -#define STATUS_GRAPHICS_OPM_SIGNALING_NOT_SUPPORTED cpu_to_le32(0xC01E0520) -#define STATUS_GRAPHICS_OPM_INVALID_CONFIGURATION_REQUEST \ - cpu_to_le32(0xC01E0521) -#define STATUS_GRAPHICS_OPM_NOT_SUPPORTED cpu_to_le32(0xC01E0500) -#define STATUS_GRAPHICS_COPP_NOT_SUPPORTED cpu_to_le32(0xC01E0501) -#define STATUS_GRAPHICS_UAB_NOT_SUPPORTED cpu_to_le32(0xC01E0502) -#define STATUS_GRAPHICS_OPM_INVALID_ENCRYPTED_PARAMETERS cpu_to_le32(0xC01E0503) -#define STATUS_GRAPHICS_OPM_PARAMETER_ARRAY_TOO_SMALL cpu_to_le32(0xC01E0504) -#define STATUS_GRAPHICS_OPM_NO_PROTECTED_OUTPUTS_EXIST cpu_to_le32(0xC01E0505) -#define STATUS_GRAPHICS_PVP_NO_DISPLAY_DEVICE_CORRESPONDS_TO_NAME \ - cpu_to_le32(0xC01E0506) -#define STATUS_GRAPHICS_PVP_DISPLAY_DEVICE_NOT_ATTACHED_TO_DESKTOP \ - cpu_to_le32(0xC01E0507) -#define STATUS_GRAPHICS_PVP_MIRRORING_DEVICES_NOT_SUPPORTED \ - cpu_to_le32(0xC01E0508) -#define STATUS_GRAPHICS_OPM_INVALID_POINTER cpu_to_le32(0xC01E050A) -#define STATUS_GRAPHICS_OPM_INTERNAL_ERROR cpu_to_le32(0xC01E050B) -#define STATUS_GRAPHICS_OPM_INVALID_HANDLE cpu_to_le32(0xC01E050C) -#define STATUS_GRAPHICS_PVP_NO_MONITORS_CORRESPOND_TO_DISPLAY_DEVICE \ - cpu_to_le32(0xC01E050D) -#define STATUS_GRAPHICS_PVP_INVALID_CERTIFICATE_LENGTH cpu_to_le32(0xC01E050E) -#define STATUS_GRAPHICS_OPM_SPANNING_MODE_ENABLED cpu_to_le32(0xC01E050F) -#define STATUS_GRAPHICS_OPM_THEATER_MODE_ENABLED cpu_to_le32(0xC01E0510) -#define STATUS_GRAPHICS_PVP_HFS_FAILED cpu_to_le32(0xC01E0511) -#define STATUS_GRAPHICS_OPM_INVALID_SRM cpu_to_le32(0xC01E0512) -#define STATUS_GRAPHICS_OPM_OUTPUT_DOES_NOT_SUPPORT_HDCP cpu_to_le32(0xC01E0513) -#define STATUS_GRAPHICS_OPM_OUTPUT_DOES_NOT_SUPPORT_ACP cpu_to_le32(0xC01E0514) -#define STATUS_GRAPHICS_OPM_OUTPUT_DOES_NOT_SUPPORT_CGMSA \ - cpu_to_le32(0xC01E0515) -#define STATUS_GRAPHICS_OPM_HDCP_SRM_NEVER_SET cpu_to_le32(0xC01E0516) -#define STATUS_GRAPHICS_OPM_RESOLUTION_TOO_HIGH cpu_to_le32(0xC01E0517) -#define STATUS_GRAPHICS_OPM_ALL_HDCP_HARDWARE_ALREADY_IN_USE \ - cpu_to_le32(0xC01E0518) -#define STATUS_GRAPHICS_OPM_PROTECTED_OUTPUT_NO_LONGER_EXISTS \ - cpu_to_le32(0xC01E051A) -#define STATUS_GRAPHICS_OPM_SESSION_TYPE_CHANGE_IN_PROGRESS \ - cpu_to_le32(0xC01E051B) -#define STATUS_GRAPHICS_I2C_NOT_SUPPORTED cpu_to_le32(0xC01E0580) -#define STATUS_GRAPHICS_I2C_DEVICE_DOES_NOT_EXIST cpu_to_le32(0xC01E0581) -#define STATUS_GRAPHICS_I2C_ERROR_TRANSMITTING_DATA cpu_to_le32(0xC01E0582) -#define STATUS_GRAPHICS_I2C_ERROR_RECEIVING_DATA cpu_to_le32(0xC01E0583) -#define STATUS_GRAPHICS_DDCCI_VCP_NOT_SUPPORTED cpu_to_le32(0xC01E0584) -#define STATUS_GRAPHICS_DDCCI_INVALID_DATA cpu_to_le32(0xC01E0585) -#define STATUS_GRAPHICS_DDCCI_MONITOR_RETURNED_INVALID_TIMING_STATUS_BYTE \ - cpu_to_le32(0xC01E0586) -#define STATUS_GRAPHICS_DDCCI_INVALID_CAPABILITIES_STRING \ - cpu_to_le32(0xC01E0587) -#define STATUS_GRAPHICS_MCA_INTERNAL_ERROR cpu_to_le32(0xC01E0588) -#define STATUS_GRAPHICS_DDCCI_INVALID_MESSAGE_COMMAND cpu_to_le32(0xC01E0589) -#define STATUS_GRAPHICS_DDCCI_INVALID_MESSAGE_LENGTH cpu_to_le32(0xC01E058A) -#define STATUS_GRAPHICS_DDCCI_INVALID_MESSAGE_CHECKSUM cpu_to_le32(0xC01E058B) -#define STATUS_GRAPHICS_INVALID_PHYSICAL_MONITOR_HANDLE cpu_to_le32(0xC01E058C) -#define STATUS_GRAPHICS_MONITOR_NO_LONGER_EXISTS cpu_to_le32(0xC01E058D) -#define STATUS_GRAPHICS_ONLY_CONSOLE_SESSION_SUPPORTED cpu_to_le32(0xC01E05E0) -#define STATUS_GRAPHICS_NO_DISPLAY_DEVICE_CORRESPONDS_TO_NAME \ - cpu_to_le32(0xC01E05E1) -#define STATUS_GRAPHICS_DISPLAY_DEVICE_NOT_ATTACHED_TO_DESKTOP \ - cpu_to_le32(0xC01E05E2) -#define STATUS_GRAPHICS_MIRRORING_DEVICES_NOT_SUPPORTED cpu_to_le32(0xC01E05E3) -#define STATUS_GRAPHICS_INVALID_POINTER cpu_to_le32(0xC01E05E4) -#define STATUS_GRAPHICS_NO_MONITORS_CORRESPOND_TO_DISPLAY_DEVICE \ - cpu_to_le32(0xC01E05E5) -#define STATUS_GRAPHICS_PARAMETER_ARRAY_TOO_SMALL cpu_to_le32(0xC01E05E6) -#define STATUS_GRAPHICS_INTERNAL_ERROR cpu_to_le32(0xC01E05E7) -#define STATUS_GRAPHICS_SESSION_TYPE_CHANGE_IN_PROGRESS cpu_to_le32(0xC01E05E8) -#define STATUS_FVE_LOCKED_VOLUME cpu_to_le32(0xC0210000) -#define STATUS_FVE_NOT_ENCRYPTED cpu_to_le32(0xC0210001) -#define STATUS_FVE_BAD_INFORMATION cpu_to_le32(0xC0210002) -#define STATUS_FVE_TOO_SMALL cpu_to_le32(0xC0210003) -#define STATUS_FVE_FAILED_WRONG_FS cpu_to_le32(0xC0210004) -#define STATUS_FVE_FAILED_BAD_FS cpu_to_le32(0xC0210005) -#define STATUS_FVE_FS_NOT_EXTENDED cpu_to_le32(0xC0210006) -#define STATUS_FVE_FS_MOUNTED cpu_to_le32(0xC0210007) -#define STATUS_FVE_NO_LICENSE cpu_to_le32(0xC0210008) -#define STATUS_FVE_ACTION_NOT_ALLOWED cpu_to_le32(0xC0210009) -#define STATUS_FVE_BAD_DATA cpu_to_le32(0xC021000A) -#define STATUS_FVE_VOLUME_NOT_BOUND cpu_to_le32(0xC021000B) -#define STATUS_FVE_NOT_DATA_VOLUME cpu_to_le32(0xC021000C) -#define STATUS_FVE_CONV_READ_ERROR cpu_to_le32(0xC021000D) -#define STATUS_FVE_CONV_WRITE_ERROR cpu_to_le32(0xC021000E) -#define STATUS_FVE_OVERLAPPED_UPDATE cpu_to_le32(0xC021000F) -#define STATUS_FVE_FAILED_SECTOR_SIZE cpu_to_le32(0xC0210010) -#define STATUS_FVE_FAILED_AUTHENTICATION cpu_to_le32(0xC0210011) -#define STATUS_FVE_NOT_OS_VOLUME cpu_to_le32(0xC0210012) -#define STATUS_FVE_KEYFILE_NOT_FOUND cpu_to_le32(0xC0210013) -#define STATUS_FVE_KEYFILE_INVALID cpu_to_le32(0xC0210014) -#define STATUS_FVE_KEYFILE_NO_VMK cpu_to_le32(0xC0210015) -#define STATUS_FVE_TPM_DISABLED cpu_to_le32(0xC0210016) -#define STATUS_FVE_TPM_SRK_AUTH_NOT_ZERO cpu_to_le32(0xC0210017) -#define STATUS_FVE_TPM_INVALID_PCR cpu_to_le32(0xC0210018) -#define STATUS_FVE_TPM_NO_VMK cpu_to_le32(0xC0210019) -#define STATUS_FVE_PIN_INVALID cpu_to_le32(0xC021001A) -#define STATUS_FVE_AUTH_INVALID_APPLICATION cpu_to_le32(0xC021001B) -#define STATUS_FVE_AUTH_INVALID_CONFIG cpu_to_le32(0xC021001C) -#define STATUS_FVE_DEBUGGER_ENABLED cpu_to_le32(0xC021001D) -#define STATUS_FVE_DRY_RUN_FAILED cpu_to_le32(0xC021001E) -#define STATUS_FVE_BAD_METADATA_POINTER cpu_to_le32(0xC021001F) -#define STATUS_FVE_OLD_METADATA_COPY cpu_to_le32(0xC0210020) -#define STATUS_FVE_REBOOT_REQUIRED cpu_to_le32(0xC0210021) -#define STATUS_FVE_RAW_ACCESS cpu_to_le32(0xC0210022) -#define STATUS_FVE_RAW_BLOCKED cpu_to_le32(0xC0210023) -#define STATUS_FWP_CALLOUT_NOT_FOUND cpu_to_le32(0xC0220001) -#define STATUS_FWP_CONDITION_NOT_FOUND cpu_to_le32(0xC0220002) -#define STATUS_FWP_FILTER_NOT_FOUND cpu_to_le32(0xC0220003) -#define STATUS_FWP_LAYER_NOT_FOUND cpu_to_le32(0xC0220004) -#define STATUS_FWP_PROVIDER_NOT_FOUND cpu_to_le32(0xC0220005) -#define STATUS_FWP_PROVIDER_CONTEXT_NOT_FOUND cpu_to_le32(0xC0220006) -#define STATUS_FWP_SUBLAYER_NOT_FOUND cpu_to_le32(0xC0220007) -#define STATUS_FWP_NOT_FOUND cpu_to_le32(0xC0220008) -#define STATUS_FWP_ALREADY_EXISTS cpu_to_le32(0xC0220009) -#define STATUS_FWP_IN_USE cpu_to_le32(0xC022000A) -#define STATUS_FWP_DYNAMIC_SESSION_IN_PROGRESS cpu_to_le32(0xC022000B) -#define STATUS_FWP_WRONG_SESSION cpu_to_le32(0xC022000C) -#define STATUS_FWP_NO_TXN_IN_PROGRESS cpu_to_le32(0xC022000D) -#define STATUS_FWP_TXN_IN_PROGRESS cpu_to_le32(0xC022000E) -#define STATUS_FWP_TXN_ABORTED cpu_to_le32(0xC022000F) -#define STATUS_FWP_SESSION_ABORTED cpu_to_le32(0xC0220010) -#define STATUS_FWP_INCOMPATIBLE_TXN cpu_to_le32(0xC0220011) -#define STATUS_FWP_TIMEOUT cpu_to_le32(0xC0220012) -#define STATUS_FWP_NET_EVENTS_DISABLED cpu_to_le32(0xC0220013) -#define STATUS_FWP_INCOMPATIBLE_LAYER cpu_to_le32(0xC0220014) -#define STATUS_FWP_KM_CLIENTS_ONLY cpu_to_le32(0xC0220015) -#define STATUS_FWP_LIFETIME_MISMATCH cpu_to_le32(0xC0220016) -#define STATUS_FWP_BUILTIN_OBJECT cpu_to_le32(0xC0220017) -#define STATUS_FWP_TOO_MANY_BOOTTIME_FILTERS cpu_to_le32(0xC0220018) -#define STATUS_FWP_TOO_MANY_CALLOUTS cpu_to_le32(0xC0220018) -#define STATUS_FWP_NOTIFICATION_DROPPED cpu_to_le32(0xC0220019) -#define STATUS_FWP_TRAFFIC_MISMATCH cpu_to_le32(0xC022001A) -#define STATUS_FWP_INCOMPATIBLE_SA_STATE cpu_to_le32(0xC022001B) -#define STATUS_FWP_NULL_POINTER cpu_to_le32(0xC022001C) -#define STATUS_FWP_INVALID_ENUMERATOR cpu_to_le32(0xC022001D) -#define STATUS_FWP_INVALID_FLAGS cpu_to_le32(0xC022001E) -#define STATUS_FWP_INVALID_NET_MASK cpu_to_le32(0xC022001F) -#define STATUS_FWP_INVALID_RANGE cpu_to_le32(0xC0220020) -#define STATUS_FWP_INVALID_INTERVAL cpu_to_le32(0xC0220021) -#define STATUS_FWP_ZERO_LENGTH_ARRAY cpu_to_le32(0xC0220022) -#define STATUS_FWP_NULL_DISPLAY_NAME cpu_to_le32(0xC0220023) -#define STATUS_FWP_INVALID_ACTION_TYPE cpu_to_le32(0xC0220024) -#define STATUS_FWP_INVALID_WEIGHT cpu_to_le32(0xC0220025) -#define STATUS_FWP_MATCH_TYPE_MISMATCH cpu_to_le32(0xC0220026) -#define STATUS_FWP_TYPE_MISMATCH cpu_to_le32(0xC0220027) -#define STATUS_FWP_OUT_OF_BOUNDS cpu_to_le32(0xC0220028) -#define STATUS_FWP_RESERVED cpu_to_le32(0xC0220029) -#define STATUS_FWP_DUPLICATE_CONDITION cpu_to_le32(0xC022002A) -#define STATUS_FWP_DUPLICATE_KEYMOD cpu_to_le32(0xC022002B) -#define STATUS_FWP_ACTION_INCOMPATIBLE_WITH_LAYER cpu_to_le32(0xC022002C) -#define STATUS_FWP_ACTION_INCOMPATIBLE_WITH_SUBLAYER cpu_to_le32(0xC022002D) -#define STATUS_FWP_CONTEXT_INCOMPATIBLE_WITH_LAYER cpu_to_le32(0xC022002E) -#define STATUS_FWP_CONTEXT_INCOMPATIBLE_WITH_CALLOUT cpu_to_le32(0xC022002F) -#define STATUS_FWP_INCOMPATIBLE_AUTH_METHOD cpu_to_le32(0xC0220030) -#define STATUS_FWP_INCOMPATIBLE_DH_GROUP cpu_to_le32(0xC0220031) -#define STATUS_FWP_EM_NOT_SUPPORTED cpu_to_le32(0xC0220032) -#define STATUS_FWP_NEVER_MATCH cpu_to_le32(0xC0220033) -#define STATUS_FWP_PROVIDER_CONTEXT_MISMATCH cpu_to_le32(0xC0220034) -#define STATUS_FWP_INVALID_PARAMETER cpu_to_le32(0xC0220035) -#define STATUS_FWP_TOO_MANY_SUBLAYERS cpu_to_le32(0xC0220036) -#define STATUS_FWP_CALLOUT_NOTIFICATION_FAILED cpu_to_le32(0xC0220037) -#define STATUS_FWP_INCOMPATIBLE_AUTH_CONFIG cpu_to_le32(0xC0220038) -#define STATUS_FWP_INCOMPATIBLE_CIPHER_CONFIG cpu_to_le32(0xC0220039) -#define STATUS_FWP_TCPIP_NOT_READY cpu_to_le32(0xC0220100) -#define STATUS_FWP_INJECT_HANDLE_CLOSING cpu_to_le32(0xC0220101) -#define STATUS_FWP_INJECT_HANDLE_STALE cpu_to_le32(0xC0220102) -#define STATUS_FWP_CANNOT_PEND cpu_to_le32(0xC0220103) -#define STATUS_NDIS_CLOSING cpu_to_le32(0xC0230002) -#define STATUS_NDIS_BAD_VERSION cpu_to_le32(0xC0230004) -#define STATUS_NDIS_BAD_CHARACTERISTICS cpu_to_le32(0xC0230005) -#define STATUS_NDIS_ADAPTER_NOT_FOUND cpu_to_le32(0xC0230006) -#define STATUS_NDIS_OPEN_FAILED cpu_to_le32(0xC0230007) -#define STATUS_NDIS_DEVICE_FAILED cpu_to_le32(0xC0230008) -#define STATUS_NDIS_MULTICAST_FULL cpu_to_le32(0xC0230009) -#define STATUS_NDIS_MULTICAST_EXISTS cpu_to_le32(0xC023000A) -#define STATUS_NDIS_MULTICAST_NOT_FOUND cpu_to_le32(0xC023000B) -#define STATUS_NDIS_REQUEST_ABORTED cpu_to_le32(0xC023000C) -#define STATUS_NDIS_RESET_IN_PROGRESS cpu_to_le32(0xC023000D) -#define STATUS_NDIS_INVALID_PACKET cpu_to_le32(0xC023000F) -#define STATUS_NDIS_INVALID_DEVICE_REQUEST cpu_to_le32(0xC0230010) -#define STATUS_NDIS_ADAPTER_NOT_READY cpu_to_le32(0xC0230011) -#define STATUS_NDIS_INVALID_LENGTH cpu_to_le32(0xC0230014) -#define STATUS_NDIS_INVALID_DATA cpu_to_le32(0xC0230015) -#define STATUS_NDIS_BUFFER_TOO_SHORT cpu_to_le32(0xC0230016) -#define STATUS_NDIS_INVALID_OID cpu_to_le32(0xC0230017) -#define STATUS_NDIS_ADAPTER_REMOVED cpu_to_le32(0xC0230018) -#define STATUS_NDIS_UNSUPPORTED_MEDIA cpu_to_le32(0xC0230019) -#define STATUS_NDIS_GROUP_ADDRESS_IN_USE cpu_to_le32(0xC023001A) -#define STATUS_NDIS_FILE_NOT_FOUND cpu_to_le32(0xC023001B) -#define STATUS_NDIS_ERROR_READING_FILE cpu_to_le32(0xC023001C) -#define STATUS_NDIS_ALREADY_MAPPED cpu_to_le32(0xC023001D) -#define STATUS_NDIS_RESOURCE_CONFLICT cpu_to_le32(0xC023001E) -#define STATUS_NDIS_MEDIA_DISCONNECTED cpu_to_le32(0xC023001F) -#define STATUS_NDIS_INVALID_ADDRESS cpu_to_le32(0xC0230022) -#define STATUS_NDIS_PAUSED cpu_to_le32(0xC023002A) -#define STATUS_NDIS_INTERFACE_NOT_FOUND cpu_to_le32(0xC023002B) -#define STATUS_NDIS_UNSUPPORTED_REVISION cpu_to_le32(0xC023002C) -#define STATUS_NDIS_INVALID_PORT cpu_to_le32(0xC023002D) -#define STATUS_NDIS_INVALID_PORT_STATE cpu_to_le32(0xC023002E) -#define STATUS_NDIS_LOW_POWER_STATE cpu_to_le32(0xC023002F) -#define STATUS_NDIS_NOT_SUPPORTED cpu_to_le32(0xC02300BB) -#define STATUS_NDIS_DOT11_AUTO_CONFIG_ENABLED cpu_to_le32(0xC0232000) -#define STATUS_NDIS_DOT11_MEDIA_IN_USE cpu_to_le32(0xC0232001) -#define STATUS_NDIS_DOT11_POWER_STATE_INVALID cpu_to_le32(0xC0232002) -#define STATUS_IPSEC_BAD_SPI cpu_to_le32(0xC0360001) -#define STATUS_IPSEC_SA_LIFETIME_EXPIRED cpu_to_le32(0xC0360002) -#define STATUS_IPSEC_WRONG_SA cpu_to_le32(0xC0360003) -#define STATUS_IPSEC_REPLAY_CHECK_FAILED cpu_to_le32(0xC0360004) -#define STATUS_IPSEC_INVALID_PACKET cpu_to_le32(0xC0360005) -#define STATUS_IPSEC_INTEGRITY_CHECK_FAILED cpu_to_le32(0xC0360006) -#define STATUS_IPSEC_CLEAR_TEXT_DROP cpu_to_le32(0xC0360007) - -#define STATUS_NO_PREAUTH_INTEGRITY_HASH_OVERLAP cpu_to_le32(0xC05D0000) -#define STATUS_INVALID_LOCK_RANGE cpu_to_le32(0xC00001a1) diff --git a/fs/ksmbd/transport_ipc.c b/fs/ksmbd/transport_ipc.c deleted file mode 100644 index 40c721f9227e4..0000000000000 --- a/fs/ksmbd/transport_ipc.c +++ /dev/null @@ -1,884 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-or-later -/* - * Copyright (C) 2018 Samsung Electronics Co., Ltd. - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include "vfs_cache.h" -#include "transport_ipc.h" -#include "server.h" -#include "smb_common.h" - -#include "mgmt/user_config.h" -#include "mgmt/share_config.h" -#include "mgmt/user_session.h" -#include "mgmt/tree_connect.h" -#include "mgmt/ksmbd_ida.h" -#include "connection.h" -#include "transport_tcp.h" -#include "transport_rdma.h" - -#define IPC_WAIT_TIMEOUT (2 * HZ) - -#define IPC_MSG_HASH_BITS 3 -static DEFINE_HASHTABLE(ipc_msg_table, IPC_MSG_HASH_BITS); -static DECLARE_RWSEM(ipc_msg_table_lock); -static DEFINE_MUTEX(startup_lock); - -static DEFINE_IDA(ipc_ida); - -static unsigned int ksmbd_tools_pid; - -static bool ksmbd_ipc_validate_version(struct genl_info *m) -{ - if (m->genlhdr->version != KSMBD_GENL_VERSION) { - pr_err("%s. ksmbd: %d, kernel module: %d. %s.\n", - "Daemon and kernel module version mismatch", - m->genlhdr->version, - KSMBD_GENL_VERSION, - "User-space ksmbd should terminate"); - return false; - } - return true; -} - -struct ksmbd_ipc_msg { - unsigned int type; - unsigned int sz; - unsigned char payload[]; -}; - -struct ipc_msg_table_entry { - unsigned int handle; - unsigned int type; - wait_queue_head_t wait; - struct hlist_node ipc_table_hlist; - - void *response; -}; - -static struct delayed_work ipc_timer_work; - -static int handle_startup_event(struct sk_buff *skb, struct genl_info *info); -static int handle_unsupported_event(struct sk_buff *skb, struct genl_info *info); -static int handle_generic_event(struct sk_buff *skb, struct genl_info *info); -static int ksmbd_ipc_heartbeat_request(void); - -static const struct nla_policy ksmbd_nl_policy[KSMBD_EVENT_MAX] = { - [KSMBD_EVENT_UNSPEC] = { - .len = 0, - }, - [KSMBD_EVENT_HEARTBEAT_REQUEST] = { - .len = sizeof(struct ksmbd_heartbeat), - }, - [KSMBD_EVENT_STARTING_UP] = { - .len = sizeof(struct ksmbd_startup_request), - }, - [KSMBD_EVENT_SHUTTING_DOWN] = { - .len = sizeof(struct ksmbd_shutdown_request), - }, - [KSMBD_EVENT_LOGIN_REQUEST] = { - .len = sizeof(struct ksmbd_login_request), - }, - [KSMBD_EVENT_LOGIN_RESPONSE] = { - .len = sizeof(struct ksmbd_login_response), - }, - [KSMBD_EVENT_SHARE_CONFIG_REQUEST] = { - .len = sizeof(struct ksmbd_share_config_request), - }, - [KSMBD_EVENT_SHARE_CONFIG_RESPONSE] = { - .len = sizeof(struct ksmbd_share_config_response), - }, - [KSMBD_EVENT_TREE_CONNECT_REQUEST] = { - .len = sizeof(struct ksmbd_tree_connect_request), - }, - [KSMBD_EVENT_TREE_CONNECT_RESPONSE] = { - .len = sizeof(struct ksmbd_tree_connect_response), - }, - [KSMBD_EVENT_TREE_DISCONNECT_REQUEST] = { - .len = sizeof(struct ksmbd_tree_disconnect_request), - }, - [KSMBD_EVENT_LOGOUT_REQUEST] = { - .len = sizeof(struct ksmbd_logout_request), - }, - [KSMBD_EVENT_RPC_REQUEST] = { - }, - [KSMBD_EVENT_RPC_RESPONSE] = { - }, - [KSMBD_EVENT_SPNEGO_AUTHEN_REQUEST] = { - }, - [KSMBD_EVENT_SPNEGO_AUTHEN_RESPONSE] = { - }, -}; - -static struct genl_ops ksmbd_genl_ops[] = { - { - .cmd = KSMBD_EVENT_UNSPEC, - .doit = handle_unsupported_event, - }, - { - .cmd = KSMBD_EVENT_HEARTBEAT_REQUEST, - .doit = handle_unsupported_event, - }, - { - .cmd = KSMBD_EVENT_STARTING_UP, - .doit = handle_startup_event, - }, - { - .cmd = KSMBD_EVENT_SHUTTING_DOWN, - .doit = handle_unsupported_event, - }, - { - .cmd = KSMBD_EVENT_LOGIN_REQUEST, - .doit = handle_unsupported_event, - }, - { - .cmd = KSMBD_EVENT_LOGIN_RESPONSE, - .doit = handle_generic_event, - }, - { - .cmd = KSMBD_EVENT_SHARE_CONFIG_REQUEST, - .doit = handle_unsupported_event, - }, - { - .cmd = KSMBD_EVENT_SHARE_CONFIG_RESPONSE, - .doit = handle_generic_event, - }, - { - .cmd = KSMBD_EVENT_TREE_CONNECT_REQUEST, - .doit = handle_unsupported_event, - }, - { - .cmd = KSMBD_EVENT_TREE_CONNECT_RESPONSE, - .doit = handle_generic_event, - }, - { - .cmd = KSMBD_EVENT_TREE_DISCONNECT_REQUEST, - .doit = handle_unsupported_event, - }, - { - .cmd = KSMBD_EVENT_LOGOUT_REQUEST, - .doit = handle_unsupported_event, - }, - { - .cmd = KSMBD_EVENT_RPC_REQUEST, - .doit = handle_unsupported_event, - }, - { - .cmd = KSMBD_EVENT_RPC_RESPONSE, - .doit = handle_generic_event, - }, - { - .cmd = KSMBD_EVENT_SPNEGO_AUTHEN_REQUEST, - .doit = handle_unsupported_event, - }, - { - .cmd = KSMBD_EVENT_SPNEGO_AUTHEN_RESPONSE, - .doit = handle_generic_event, - }, -}; - -static struct genl_family ksmbd_genl_family = { - .name = KSMBD_GENL_NAME, - .version = KSMBD_GENL_VERSION, - .hdrsize = 0, - .maxattr = KSMBD_EVENT_MAX, - .netnsok = true, - .module = THIS_MODULE, - .ops = ksmbd_genl_ops, - .n_ops = ARRAY_SIZE(ksmbd_genl_ops), - .resv_start_op = KSMBD_EVENT_SPNEGO_AUTHEN_RESPONSE + 1, -}; - -static void ksmbd_nl_init_fixup(void) -{ - int i; - - for (i = 0; i < ARRAY_SIZE(ksmbd_genl_ops); i++) - ksmbd_genl_ops[i].validate = GENL_DONT_VALIDATE_STRICT | - GENL_DONT_VALIDATE_DUMP; - - ksmbd_genl_family.policy = ksmbd_nl_policy; -} - -static int rpc_context_flags(struct ksmbd_session *sess) -{ - if (user_guest(sess->user)) - return KSMBD_RPC_RESTRICTED_CONTEXT; - return 0; -} - -static void ipc_update_last_active(void) -{ - if (server_conf.ipc_timeout) - server_conf.ipc_last_active = jiffies; -} - -static struct ksmbd_ipc_msg *ipc_msg_alloc(size_t sz) -{ - struct ksmbd_ipc_msg *msg; - size_t msg_sz = sz + sizeof(struct ksmbd_ipc_msg); - - msg = kvmalloc(msg_sz, GFP_KERNEL | __GFP_ZERO); - if (msg) - msg->sz = sz; - return msg; -} - -static void ipc_msg_free(struct ksmbd_ipc_msg *msg) -{ - kvfree(msg); -} - -static void ipc_msg_handle_free(int handle) -{ - if (handle >= 0) - ksmbd_release_id(&ipc_ida, handle); -} - -static int handle_response(int type, void *payload, size_t sz) -{ - unsigned int handle = *(unsigned int *)payload; - struct ipc_msg_table_entry *entry; - int ret = 0; - - ipc_update_last_active(); - down_read(&ipc_msg_table_lock); - hash_for_each_possible(ipc_msg_table, entry, ipc_table_hlist, handle) { - if (handle != entry->handle) - continue; - - entry->response = NULL; - /* - * Response message type value should be equal to - * request message type + 1. - */ - if (entry->type + 1 != type) { - pr_err("Waiting for IPC type %d, got %d. Ignore.\n", - entry->type + 1, type); - } - - entry->response = kvmalloc(sz, GFP_KERNEL | __GFP_ZERO); - if (!entry->response) { - ret = -ENOMEM; - break; - } - - memcpy(entry->response, payload, sz); - wake_up_interruptible(&entry->wait); - ret = 0; - break; - } - up_read(&ipc_msg_table_lock); - - return ret; -} - -static int ipc_server_config_on_startup(struct ksmbd_startup_request *req) -{ - int ret; - - ksmbd_set_fd_limit(req->file_max); - server_conf.flags = req->flags; - server_conf.signing = req->signing; - server_conf.tcp_port = req->tcp_port; - server_conf.ipc_timeout = req->ipc_timeout * HZ; - server_conf.deadtime = req->deadtime * SMB_ECHO_INTERVAL; - server_conf.share_fake_fscaps = req->share_fake_fscaps; - ksmbd_init_domain(req->sub_auth); - - if (req->smb2_max_read) - init_smb2_max_read_size(req->smb2_max_read); - if (req->smb2_max_write) - init_smb2_max_write_size(req->smb2_max_write); - if (req->smb2_max_trans) - init_smb2_max_trans_size(req->smb2_max_trans); - if (req->smb2_max_credits) - init_smb2_max_credits(req->smb2_max_credits); - if (req->smbd_max_io_size) - init_smbd_max_io_size(req->smbd_max_io_size); - - if (req->max_connections) - server_conf.max_connections = req->max_connections; - - ret = ksmbd_set_netbios_name(req->netbios_name); - ret |= ksmbd_set_server_string(req->server_string); - ret |= ksmbd_set_work_group(req->work_group); - ret |= ksmbd_tcp_set_interfaces(KSMBD_STARTUP_CONFIG_INTERFACES(req), - req->ifc_list_sz); - if (ret) { - pr_err("Server configuration error: %s %s %s\n", - req->netbios_name, req->server_string, - req->work_group); - return ret; - } - - if (req->min_prot[0]) { - ret = ksmbd_lookup_protocol_idx(req->min_prot); - if (ret >= 0) - server_conf.min_protocol = ret; - } - if (req->max_prot[0]) { - ret = ksmbd_lookup_protocol_idx(req->max_prot); - if (ret >= 0) - server_conf.max_protocol = ret; - } - - if (server_conf.ipc_timeout) - schedule_delayed_work(&ipc_timer_work, server_conf.ipc_timeout); - return 0; -} - -static int handle_startup_event(struct sk_buff *skb, struct genl_info *info) -{ - int ret = 0; - -#ifdef CONFIG_SMB_SERVER_CHECK_CAP_NET_ADMIN - if (!netlink_capable(skb, CAP_NET_ADMIN)) - return -EPERM; -#endif - - if (!ksmbd_ipc_validate_version(info)) - return -EINVAL; - - if (!info->attrs[KSMBD_EVENT_STARTING_UP]) - return -EINVAL; - - mutex_lock(&startup_lock); - if (!ksmbd_server_configurable()) { - mutex_unlock(&startup_lock); - pr_err("Server reset is in progress, can't start daemon\n"); - return -EINVAL; - } - - if (ksmbd_tools_pid) { - if (ksmbd_ipc_heartbeat_request() == 0) { - ret = -EINVAL; - goto out; - } - - pr_err("Reconnect to a new user space daemon\n"); - } else { - struct ksmbd_startup_request *req; - - req = nla_data(info->attrs[info->genlhdr->cmd]); - ret = ipc_server_config_on_startup(req); - if (ret) - goto out; - server_queue_ctrl_init_work(); - } - - ksmbd_tools_pid = info->snd_portid; - ipc_update_last_active(); - -out: - mutex_unlock(&startup_lock); - return ret; -} - -static int handle_unsupported_event(struct sk_buff *skb, struct genl_info *info) -{ - pr_err("Unknown IPC event: %d, ignore.\n", info->genlhdr->cmd); - return -EINVAL; -} - -static int handle_generic_event(struct sk_buff *skb, struct genl_info *info) -{ - void *payload; - int sz; - int type = info->genlhdr->cmd; - -#ifdef CONFIG_SMB_SERVER_CHECK_CAP_NET_ADMIN - if (!netlink_capable(skb, CAP_NET_ADMIN)) - return -EPERM; -#endif - - if (type >= KSMBD_EVENT_MAX) { - WARN_ON(1); - return -EINVAL; - } - - if (!ksmbd_ipc_validate_version(info)) - return -EINVAL; - - if (!info->attrs[type]) - return -EINVAL; - - payload = nla_data(info->attrs[info->genlhdr->cmd]); - sz = nla_len(info->attrs[info->genlhdr->cmd]); - return handle_response(type, payload, sz); -} - -static int ipc_msg_send(struct ksmbd_ipc_msg *msg) -{ - struct genlmsghdr *nlh; - struct sk_buff *skb; - int ret = -EINVAL; - - if (!ksmbd_tools_pid) - return ret; - - skb = genlmsg_new(msg->sz, GFP_KERNEL); - if (!skb) - return -ENOMEM; - - nlh = genlmsg_put(skb, 0, 0, &ksmbd_genl_family, 0, msg->type); - if (!nlh) - goto out; - - ret = nla_put(skb, msg->type, msg->sz, msg->payload); - if (ret) { - genlmsg_cancel(skb, nlh); - goto out; - } - - genlmsg_end(skb, nlh); - ret = genlmsg_unicast(&init_net, skb, ksmbd_tools_pid); - if (!ret) - ipc_update_last_active(); - return ret; - -out: - nlmsg_free(skb); - return ret; -} - -static void *ipc_msg_send_request(struct ksmbd_ipc_msg *msg, unsigned int handle) -{ - struct ipc_msg_table_entry entry; - int ret; - - if ((int)handle < 0) - return NULL; - - entry.type = msg->type; - entry.response = NULL; - init_waitqueue_head(&entry.wait); - - down_write(&ipc_msg_table_lock); - entry.handle = handle; - hash_add(ipc_msg_table, &entry.ipc_table_hlist, entry.handle); - up_write(&ipc_msg_table_lock); - - ret = ipc_msg_send(msg); - if (ret) - goto out; - - ret = wait_event_interruptible_timeout(entry.wait, - entry.response != NULL, - IPC_WAIT_TIMEOUT); -out: - down_write(&ipc_msg_table_lock); - hash_del(&entry.ipc_table_hlist); - up_write(&ipc_msg_table_lock); - return entry.response; -} - -static int ksmbd_ipc_heartbeat_request(void) -{ - struct ksmbd_ipc_msg *msg; - int ret; - - msg = ipc_msg_alloc(sizeof(struct ksmbd_heartbeat)); - if (!msg) - return -EINVAL; - - msg->type = KSMBD_EVENT_HEARTBEAT_REQUEST; - ret = ipc_msg_send(msg); - ipc_msg_free(msg); - return ret; -} - -struct ksmbd_login_response *ksmbd_ipc_login_request(const char *account) -{ - struct ksmbd_ipc_msg *msg; - struct ksmbd_login_request *req; - struct ksmbd_login_response *resp; - - if (strlen(account) >= KSMBD_REQ_MAX_ACCOUNT_NAME_SZ) - return NULL; - - msg = ipc_msg_alloc(sizeof(struct ksmbd_login_request)); - if (!msg) - return NULL; - - msg->type = KSMBD_EVENT_LOGIN_REQUEST; - req = (struct ksmbd_login_request *)msg->payload; - req->handle = ksmbd_acquire_id(&ipc_ida); - strscpy(req->account, account, KSMBD_REQ_MAX_ACCOUNT_NAME_SZ); - - resp = ipc_msg_send_request(msg, req->handle); - ipc_msg_handle_free(req->handle); - ipc_msg_free(msg); - return resp; -} - -struct ksmbd_spnego_authen_response * -ksmbd_ipc_spnego_authen_request(const char *spnego_blob, int blob_len) -{ - struct ksmbd_ipc_msg *msg; - struct ksmbd_spnego_authen_request *req; - struct ksmbd_spnego_authen_response *resp; - - msg = ipc_msg_alloc(sizeof(struct ksmbd_spnego_authen_request) + - blob_len + 1); - if (!msg) - return NULL; - - msg->type = KSMBD_EVENT_SPNEGO_AUTHEN_REQUEST; - req = (struct ksmbd_spnego_authen_request *)msg->payload; - req->handle = ksmbd_acquire_id(&ipc_ida); - req->spnego_blob_len = blob_len; - memcpy(req->spnego_blob, spnego_blob, blob_len); - - resp = ipc_msg_send_request(msg, req->handle); - ipc_msg_handle_free(req->handle); - ipc_msg_free(msg); - return resp; -} - -struct ksmbd_tree_connect_response * -ksmbd_ipc_tree_connect_request(struct ksmbd_session *sess, - struct ksmbd_share_config *share, - struct ksmbd_tree_connect *tree_conn, - struct sockaddr *peer_addr) -{ - struct ksmbd_ipc_msg *msg; - struct ksmbd_tree_connect_request *req; - struct ksmbd_tree_connect_response *resp; - - if (strlen(user_name(sess->user)) >= KSMBD_REQ_MAX_ACCOUNT_NAME_SZ) - return NULL; - - if (strlen(share->name) >= KSMBD_REQ_MAX_SHARE_NAME) - return NULL; - - msg = ipc_msg_alloc(sizeof(struct ksmbd_tree_connect_request)); - if (!msg) - return NULL; - - msg->type = KSMBD_EVENT_TREE_CONNECT_REQUEST; - req = (struct ksmbd_tree_connect_request *)msg->payload; - - req->handle = ksmbd_acquire_id(&ipc_ida); - req->account_flags = sess->user->flags; - req->session_id = sess->id; - req->connect_id = tree_conn->id; - strscpy(req->account, user_name(sess->user), KSMBD_REQ_MAX_ACCOUNT_NAME_SZ); - strscpy(req->share, share->name, KSMBD_REQ_MAX_SHARE_NAME); - snprintf(req->peer_addr, sizeof(req->peer_addr), "%pIS", peer_addr); - - if (peer_addr->sa_family == AF_INET6) - req->flags |= KSMBD_TREE_CONN_FLAG_REQUEST_IPV6; - if (test_session_flag(sess, CIFDS_SESSION_FLAG_SMB2)) - req->flags |= KSMBD_TREE_CONN_FLAG_REQUEST_SMB2; - - resp = ipc_msg_send_request(msg, req->handle); - ipc_msg_handle_free(req->handle); - ipc_msg_free(msg); - return resp; -} - -int ksmbd_ipc_tree_disconnect_request(unsigned long long session_id, - unsigned long long connect_id) -{ - struct ksmbd_ipc_msg *msg; - struct ksmbd_tree_disconnect_request *req; - int ret; - - msg = ipc_msg_alloc(sizeof(struct ksmbd_tree_disconnect_request)); - if (!msg) - return -ENOMEM; - - msg->type = KSMBD_EVENT_TREE_DISCONNECT_REQUEST; - req = (struct ksmbd_tree_disconnect_request *)msg->payload; - req->session_id = session_id; - req->connect_id = connect_id; - - ret = ipc_msg_send(msg); - ipc_msg_free(msg); - return ret; -} - -int ksmbd_ipc_logout_request(const char *account, int flags) -{ - struct ksmbd_ipc_msg *msg; - struct ksmbd_logout_request *req; - int ret; - - if (strlen(account) >= KSMBD_REQ_MAX_ACCOUNT_NAME_SZ) - return -EINVAL; - - msg = ipc_msg_alloc(sizeof(struct ksmbd_logout_request)); - if (!msg) - return -ENOMEM; - - msg->type = KSMBD_EVENT_LOGOUT_REQUEST; - req = (struct ksmbd_logout_request *)msg->payload; - req->account_flags = flags; - strscpy(req->account, account, KSMBD_REQ_MAX_ACCOUNT_NAME_SZ); - - ret = ipc_msg_send(msg); - ipc_msg_free(msg); - return ret; -} - -struct ksmbd_share_config_response * -ksmbd_ipc_share_config_request(const char *name) -{ - struct ksmbd_ipc_msg *msg; - struct ksmbd_share_config_request *req; - struct ksmbd_share_config_response *resp; - - if (strlen(name) >= KSMBD_REQ_MAX_SHARE_NAME) - return NULL; - - msg = ipc_msg_alloc(sizeof(struct ksmbd_share_config_request)); - if (!msg) - return NULL; - - msg->type = KSMBD_EVENT_SHARE_CONFIG_REQUEST; - req = (struct ksmbd_share_config_request *)msg->payload; - req->handle = ksmbd_acquire_id(&ipc_ida); - strscpy(req->share_name, name, KSMBD_REQ_MAX_SHARE_NAME); - - resp = ipc_msg_send_request(msg, req->handle); - ipc_msg_handle_free(req->handle); - ipc_msg_free(msg); - return resp; -} - -struct ksmbd_rpc_command *ksmbd_rpc_open(struct ksmbd_session *sess, int handle) -{ - struct ksmbd_ipc_msg *msg; - struct ksmbd_rpc_command *req; - struct ksmbd_rpc_command *resp; - - msg = ipc_msg_alloc(sizeof(struct ksmbd_rpc_command)); - if (!msg) - return NULL; - - msg->type = KSMBD_EVENT_RPC_REQUEST; - req = (struct ksmbd_rpc_command *)msg->payload; - req->handle = handle; - req->flags = ksmbd_session_rpc_method(sess, handle); - req->flags |= KSMBD_RPC_OPEN_METHOD; - req->payload_sz = 0; - - resp = ipc_msg_send_request(msg, req->handle); - ipc_msg_free(msg); - return resp; -} - -struct ksmbd_rpc_command *ksmbd_rpc_close(struct ksmbd_session *sess, int handle) -{ - struct ksmbd_ipc_msg *msg; - struct ksmbd_rpc_command *req; - struct ksmbd_rpc_command *resp; - - msg = ipc_msg_alloc(sizeof(struct ksmbd_rpc_command)); - if (!msg) - return NULL; - - msg->type = KSMBD_EVENT_RPC_REQUEST; - req = (struct ksmbd_rpc_command *)msg->payload; - req->handle = handle; - req->flags = ksmbd_session_rpc_method(sess, handle); - req->flags |= KSMBD_RPC_CLOSE_METHOD; - req->payload_sz = 0; - - resp = ipc_msg_send_request(msg, req->handle); - ipc_msg_free(msg); - return resp; -} - -struct ksmbd_rpc_command *ksmbd_rpc_write(struct ksmbd_session *sess, int handle, - void *payload, size_t payload_sz) -{ - struct ksmbd_ipc_msg *msg; - struct ksmbd_rpc_command *req; - struct ksmbd_rpc_command *resp; - - msg = ipc_msg_alloc(sizeof(struct ksmbd_rpc_command) + payload_sz + 1); - if (!msg) - return NULL; - - msg->type = KSMBD_EVENT_RPC_REQUEST; - req = (struct ksmbd_rpc_command *)msg->payload; - req->handle = handle; - req->flags = ksmbd_session_rpc_method(sess, handle); - req->flags |= rpc_context_flags(sess); - req->flags |= KSMBD_RPC_WRITE_METHOD; - req->payload_sz = payload_sz; - memcpy(req->payload, payload, payload_sz); - - resp = ipc_msg_send_request(msg, req->handle); - ipc_msg_free(msg); - return resp; -} - -struct ksmbd_rpc_command *ksmbd_rpc_read(struct ksmbd_session *sess, int handle) -{ - struct ksmbd_ipc_msg *msg; - struct ksmbd_rpc_command *req; - struct ksmbd_rpc_command *resp; - - msg = ipc_msg_alloc(sizeof(struct ksmbd_rpc_command)); - if (!msg) - return NULL; - - msg->type = KSMBD_EVENT_RPC_REQUEST; - req = (struct ksmbd_rpc_command *)msg->payload; - req->handle = handle; - req->flags = ksmbd_session_rpc_method(sess, handle); - req->flags |= rpc_context_flags(sess); - req->flags |= KSMBD_RPC_READ_METHOD; - req->payload_sz = 0; - - resp = ipc_msg_send_request(msg, req->handle); - ipc_msg_free(msg); - return resp; -} - -struct ksmbd_rpc_command *ksmbd_rpc_ioctl(struct ksmbd_session *sess, int handle, - void *payload, size_t payload_sz) -{ - struct ksmbd_ipc_msg *msg; - struct ksmbd_rpc_command *req; - struct ksmbd_rpc_command *resp; - - msg = ipc_msg_alloc(sizeof(struct ksmbd_rpc_command) + payload_sz + 1); - if (!msg) - return NULL; - - msg->type = KSMBD_EVENT_RPC_REQUEST; - req = (struct ksmbd_rpc_command *)msg->payload; - req->handle = handle; - req->flags = ksmbd_session_rpc_method(sess, handle); - req->flags |= rpc_context_flags(sess); - req->flags |= KSMBD_RPC_IOCTL_METHOD; - req->payload_sz = payload_sz; - memcpy(req->payload, payload, payload_sz); - - resp = ipc_msg_send_request(msg, req->handle); - ipc_msg_free(msg); - return resp; -} - -struct ksmbd_rpc_command *ksmbd_rpc_rap(struct ksmbd_session *sess, void *payload, - size_t payload_sz) -{ - struct ksmbd_ipc_msg *msg; - struct ksmbd_rpc_command *req; - struct ksmbd_rpc_command *resp; - - msg = ipc_msg_alloc(sizeof(struct ksmbd_rpc_command) + payload_sz + 1); - if (!msg) - return NULL; - - msg->type = KSMBD_EVENT_RPC_REQUEST; - req = (struct ksmbd_rpc_command *)msg->payload; - req->handle = ksmbd_acquire_id(&ipc_ida); - req->flags = rpc_context_flags(sess); - req->flags |= KSMBD_RPC_RAP_METHOD; - req->payload_sz = payload_sz; - memcpy(req->payload, payload, payload_sz); - - resp = ipc_msg_send_request(msg, req->handle); - ipc_msg_handle_free(req->handle); - ipc_msg_free(msg); - return resp; -} - -static int __ipc_heartbeat(void) -{ - unsigned long delta; - - if (!ksmbd_server_running()) - return 0; - - if (time_after(jiffies, server_conf.ipc_last_active)) { - delta = (jiffies - server_conf.ipc_last_active); - } else { - ipc_update_last_active(); - schedule_delayed_work(&ipc_timer_work, - server_conf.ipc_timeout); - return 0; - } - - if (delta < server_conf.ipc_timeout) { - schedule_delayed_work(&ipc_timer_work, - server_conf.ipc_timeout - delta); - return 0; - } - - if (ksmbd_ipc_heartbeat_request() == 0) { - schedule_delayed_work(&ipc_timer_work, - server_conf.ipc_timeout); - return 0; - } - - mutex_lock(&startup_lock); - WRITE_ONCE(server_conf.state, SERVER_STATE_RESETTING); - server_conf.ipc_last_active = 0; - ksmbd_tools_pid = 0; - pr_err("No IPC daemon response for %lus\n", delta / HZ); - mutex_unlock(&startup_lock); - return -EINVAL; -} - -static void ipc_timer_heartbeat(struct work_struct *w) -{ - if (__ipc_heartbeat()) - server_queue_ctrl_reset_work(); -} - -int ksmbd_ipc_id_alloc(void) -{ - return ksmbd_acquire_id(&ipc_ida); -} - -void ksmbd_rpc_id_free(int handle) -{ - ksmbd_release_id(&ipc_ida, handle); -} - -void ksmbd_ipc_release(void) -{ - cancel_delayed_work_sync(&ipc_timer_work); - genl_unregister_family(&ksmbd_genl_family); -} - -void ksmbd_ipc_soft_reset(void) -{ - mutex_lock(&startup_lock); - ksmbd_tools_pid = 0; - cancel_delayed_work_sync(&ipc_timer_work); - mutex_unlock(&startup_lock); -} - -int ksmbd_ipc_init(void) -{ - int ret = 0; - - ksmbd_nl_init_fixup(); - INIT_DELAYED_WORK(&ipc_timer_work, ipc_timer_heartbeat); - - ret = genl_register_family(&ksmbd_genl_family); - if (ret) { - pr_err("Failed to register KSMBD netlink interface %d\n", ret); - cancel_delayed_work_sync(&ipc_timer_work); - } - - return ret; -} diff --git a/fs/ksmbd/transport_ipc.h b/fs/ksmbd/transport_ipc.h deleted file mode 100644 index 5e5b90a0c1879..0000000000000 --- a/fs/ksmbd/transport_ipc.h +++ /dev/null @@ -1,47 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0-or-later */ -/* - * Copyright (C) 2018 Samsung Electronics Co., Ltd. - */ - -#ifndef __KSMBD_TRANSPORT_IPC_H__ -#define __KSMBD_TRANSPORT_IPC_H__ - -#include - -#define KSMBD_IPC_MAX_PAYLOAD 4096 - -struct ksmbd_login_response * -ksmbd_ipc_login_request(const char *account); - -struct ksmbd_session; -struct ksmbd_share_config; -struct ksmbd_tree_connect; -struct sockaddr; - -struct ksmbd_tree_connect_response * -ksmbd_ipc_tree_connect_request(struct ksmbd_session *sess, - struct ksmbd_share_config *share, - struct ksmbd_tree_connect *tree_conn, - struct sockaddr *peer_addr); -int ksmbd_ipc_tree_disconnect_request(unsigned long long session_id, - unsigned long long connect_id); -int ksmbd_ipc_logout_request(const char *account, int flags); -struct ksmbd_share_config_response * -ksmbd_ipc_share_config_request(const char *name); -struct ksmbd_spnego_authen_response * -ksmbd_ipc_spnego_authen_request(const char *spnego_blob, int blob_len); -int ksmbd_ipc_id_alloc(void); -void ksmbd_rpc_id_free(int handle); -struct ksmbd_rpc_command *ksmbd_rpc_open(struct ksmbd_session *sess, int handle); -struct ksmbd_rpc_command *ksmbd_rpc_close(struct ksmbd_session *sess, int handle); -struct ksmbd_rpc_command *ksmbd_rpc_write(struct ksmbd_session *sess, int handle, - void *payload, size_t payload_sz); -struct ksmbd_rpc_command *ksmbd_rpc_read(struct ksmbd_session *sess, int handle); -struct ksmbd_rpc_command *ksmbd_rpc_ioctl(struct ksmbd_session *sess, int handle, - void *payload, size_t payload_sz); -struct ksmbd_rpc_command *ksmbd_rpc_rap(struct ksmbd_session *sess, void *payload, - size_t payload_sz); -void ksmbd_ipc_release(void); -void ksmbd_ipc_soft_reset(void); -int ksmbd_ipc_init(void); -#endif /* __KSMBD_TRANSPORT_IPC_H__ */ diff --git a/fs/ksmbd/transport_rdma.c b/fs/ksmbd/transport_rdma.c deleted file mode 100644 index c06efc020bd95..0000000000000 --- a/fs/ksmbd/transport_rdma.c +++ /dev/null @@ -1,2273 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-or-later -/* - * Copyright (C) 2017, Microsoft Corporation. - * Copyright (C) 2018, LG Electronics. - * - * Author(s): Long Li , - * Hyunchul Lee - */ - -#define SUBMOD_NAME "smb_direct" - -#include -#include -#include -#include -#include -#include -#include -#include - -#include "glob.h" -#include "connection.h" -#include "smb_common.h" -#include "smbstatus.h" -#include "transport_rdma.h" - -#define SMB_DIRECT_PORT_IWARP 5445 -#define SMB_DIRECT_PORT_INFINIBAND 445 - -#define SMB_DIRECT_VERSION_LE cpu_to_le16(0x0100) - -/* SMB_DIRECT negotiation timeout in seconds */ -#define SMB_DIRECT_NEGOTIATE_TIMEOUT 120 - -#define SMB_DIRECT_MAX_SEND_SGES 6 -#define SMB_DIRECT_MAX_RECV_SGES 1 - -/* - * Default maximum number of RDMA read/write outstanding on this connection - * This value is possibly decreased during QP creation on hardware limit - */ -#define SMB_DIRECT_CM_INITIATOR_DEPTH 8 - -/* Maximum number of retries on data transfer operations */ -#define SMB_DIRECT_CM_RETRY 6 -/* No need to retry on Receiver Not Ready since SMB_DIRECT manages credits */ -#define SMB_DIRECT_CM_RNR_RETRY 0 - -/* - * User configurable initial values per SMB_DIRECT transport connection - * as defined in [MS-SMBD] 3.1.1.1 - * Those may change after a SMB_DIRECT negotiation - */ - -/* Set 445 port to SMB Direct port by default */ -static int smb_direct_port = SMB_DIRECT_PORT_INFINIBAND; - -/* The local peer's maximum number of credits to grant to the peer */ -static int smb_direct_receive_credit_max = 255; - -/* The remote peer's credit request of local peer */ -static int smb_direct_send_credit_target = 255; - -/* The maximum single message size can be sent to remote peer */ -static int smb_direct_max_send_size = 1364; - -/* The maximum fragmented upper-layer payload receive size supported */ -static int smb_direct_max_fragmented_recv_size = 1024 * 1024; - -/* The maximum single-message size which can be received */ -static int smb_direct_max_receive_size = 1364; - -static int smb_direct_max_read_write_size = SMBD_DEFAULT_IOSIZE; - -static LIST_HEAD(smb_direct_device_list); -static DEFINE_RWLOCK(smb_direct_device_lock); - -struct smb_direct_device { - struct ib_device *ib_dev; - struct list_head list; -}; - -static struct smb_direct_listener { - struct rdma_cm_id *cm_id; -} smb_direct_listener; - -static struct workqueue_struct *smb_direct_wq; - -enum smb_direct_status { - SMB_DIRECT_CS_NEW = 0, - SMB_DIRECT_CS_CONNECTED, - SMB_DIRECT_CS_DISCONNECTING, - SMB_DIRECT_CS_DISCONNECTED, -}; - -struct smb_direct_transport { - struct ksmbd_transport transport; - - enum smb_direct_status status; - bool full_packet_received; - wait_queue_head_t wait_status; - - struct rdma_cm_id *cm_id; - struct ib_cq *send_cq; - struct ib_cq *recv_cq; - struct ib_pd *pd; - struct ib_qp *qp; - - int max_send_size; - int max_recv_size; - int max_fragmented_send_size; - int max_fragmented_recv_size; - int max_rdma_rw_size; - - spinlock_t reassembly_queue_lock; - struct list_head reassembly_queue; - int reassembly_data_length; - int reassembly_queue_length; - int first_entry_offset; - wait_queue_head_t wait_reassembly_queue; - - spinlock_t receive_credit_lock; - int recv_credits; - int count_avail_recvmsg; - int recv_credit_max; - int recv_credit_target; - - spinlock_t recvmsg_queue_lock; - struct list_head recvmsg_queue; - - spinlock_t empty_recvmsg_queue_lock; - struct list_head empty_recvmsg_queue; - - int send_credit_target; - atomic_t send_credits; - spinlock_t lock_new_recv_credits; - int new_recv_credits; - int max_rw_credits; - int pages_per_rw_credit; - atomic_t rw_credits; - - wait_queue_head_t wait_send_credits; - wait_queue_head_t wait_rw_credits; - - mempool_t *sendmsg_mempool; - struct kmem_cache *sendmsg_cache; - mempool_t *recvmsg_mempool; - struct kmem_cache *recvmsg_cache; - - wait_queue_head_t wait_send_pending; - atomic_t send_pending; - - struct delayed_work post_recv_credits_work; - struct work_struct send_immediate_work; - struct work_struct disconnect_work; - - bool negotiation_requested; -}; - -#define KSMBD_TRANS(t) ((struct ksmbd_transport *)&((t)->transport)) - -enum { - SMB_DIRECT_MSG_NEGOTIATE_REQ = 0, - SMB_DIRECT_MSG_DATA_TRANSFER -}; - -static struct ksmbd_transport_ops ksmbd_smb_direct_transport_ops; - -struct smb_direct_send_ctx { - struct list_head msg_list; - int wr_cnt; - bool need_invalidate_rkey; - unsigned int remote_key; -}; - -struct smb_direct_sendmsg { - struct smb_direct_transport *transport; - struct ib_send_wr wr; - struct list_head list; - int num_sge; - struct ib_sge sge[SMB_DIRECT_MAX_SEND_SGES]; - struct ib_cqe cqe; - u8 packet[]; -}; - -struct smb_direct_recvmsg { - struct smb_direct_transport *transport; - struct list_head list; - int type; - struct ib_sge sge; - struct ib_cqe cqe; - bool first_segment; - u8 packet[]; -}; - -struct smb_direct_rdma_rw_msg { - struct smb_direct_transport *t; - struct ib_cqe cqe; - int status; - struct completion *completion; - struct list_head list; - struct rdma_rw_ctx rw_ctx; - struct sg_table sgt; - struct scatterlist sg_list[]; -}; - -void init_smbd_max_io_size(unsigned int sz) -{ - sz = clamp_val(sz, SMBD_MIN_IOSIZE, SMBD_MAX_IOSIZE); - smb_direct_max_read_write_size = sz; -} - -unsigned int get_smbd_max_read_write_size(void) -{ - return smb_direct_max_read_write_size; -} - -static inline int get_buf_page_count(void *buf, int size) -{ - return DIV_ROUND_UP((uintptr_t)buf + size, PAGE_SIZE) - - (uintptr_t)buf / PAGE_SIZE; -} - -static void smb_direct_destroy_pools(struct smb_direct_transport *transport); -static void smb_direct_post_recv_credits(struct work_struct *work); -static int smb_direct_post_send_data(struct smb_direct_transport *t, - struct smb_direct_send_ctx *send_ctx, - struct kvec *iov, int niov, - int remaining_data_length); - -static inline struct smb_direct_transport * -smb_trans_direct_transfort(struct ksmbd_transport *t) -{ - return container_of(t, struct smb_direct_transport, transport); -} - -static inline void -*smb_direct_recvmsg_payload(struct smb_direct_recvmsg *recvmsg) -{ - return (void *)recvmsg->packet; -} - -static inline bool is_receive_credit_post_required(int receive_credits, - int avail_recvmsg_count) -{ - return receive_credits <= (smb_direct_receive_credit_max >> 3) && - avail_recvmsg_count >= (receive_credits >> 2); -} - -static struct -smb_direct_recvmsg *get_free_recvmsg(struct smb_direct_transport *t) -{ - struct smb_direct_recvmsg *recvmsg = NULL; - - spin_lock(&t->recvmsg_queue_lock); - if (!list_empty(&t->recvmsg_queue)) { - recvmsg = list_first_entry(&t->recvmsg_queue, - struct smb_direct_recvmsg, - list); - list_del(&recvmsg->list); - } - spin_unlock(&t->recvmsg_queue_lock); - return recvmsg; -} - -static void put_recvmsg(struct smb_direct_transport *t, - struct smb_direct_recvmsg *recvmsg) -{ - ib_dma_unmap_single(t->cm_id->device, recvmsg->sge.addr, - recvmsg->sge.length, DMA_FROM_DEVICE); - - spin_lock(&t->recvmsg_queue_lock); - list_add(&recvmsg->list, &t->recvmsg_queue); - spin_unlock(&t->recvmsg_queue_lock); -} - -static struct -smb_direct_recvmsg *get_empty_recvmsg(struct smb_direct_transport *t) -{ - struct smb_direct_recvmsg *recvmsg = NULL; - - spin_lock(&t->empty_recvmsg_queue_lock); - if (!list_empty(&t->empty_recvmsg_queue)) { - recvmsg = list_first_entry(&t->empty_recvmsg_queue, - struct smb_direct_recvmsg, list); - list_del(&recvmsg->list); - } - spin_unlock(&t->empty_recvmsg_queue_lock); - return recvmsg; -} - -static void put_empty_recvmsg(struct smb_direct_transport *t, - struct smb_direct_recvmsg *recvmsg) -{ - ib_dma_unmap_single(t->cm_id->device, recvmsg->sge.addr, - recvmsg->sge.length, DMA_FROM_DEVICE); - - spin_lock(&t->empty_recvmsg_queue_lock); - list_add_tail(&recvmsg->list, &t->empty_recvmsg_queue); - spin_unlock(&t->empty_recvmsg_queue_lock); -} - -static void enqueue_reassembly(struct smb_direct_transport *t, - struct smb_direct_recvmsg *recvmsg, - int data_length) -{ - spin_lock(&t->reassembly_queue_lock); - list_add_tail(&recvmsg->list, &t->reassembly_queue); - t->reassembly_queue_length++; - /* - * Make sure reassembly_data_length is updated after list and - * reassembly_queue_length are updated. On the dequeue side - * reassembly_data_length is checked without a lock to determine - * if reassembly_queue_length and list is up to date - */ - virt_wmb(); - t->reassembly_data_length += data_length; - spin_unlock(&t->reassembly_queue_lock); -} - -static struct smb_direct_recvmsg *get_first_reassembly(struct smb_direct_transport *t) -{ - if (!list_empty(&t->reassembly_queue)) - return list_first_entry(&t->reassembly_queue, - struct smb_direct_recvmsg, list); - else - return NULL; -} - -static void smb_direct_disconnect_rdma_work(struct work_struct *work) -{ - struct smb_direct_transport *t = - container_of(work, struct smb_direct_transport, - disconnect_work); - - if (t->status == SMB_DIRECT_CS_CONNECTED) { - t->status = SMB_DIRECT_CS_DISCONNECTING; - rdma_disconnect(t->cm_id); - } -} - -static void -smb_direct_disconnect_rdma_connection(struct smb_direct_transport *t) -{ - if (t->status == SMB_DIRECT_CS_CONNECTED) - queue_work(smb_direct_wq, &t->disconnect_work); -} - -static void smb_direct_send_immediate_work(struct work_struct *work) -{ - struct smb_direct_transport *t = container_of(work, - struct smb_direct_transport, send_immediate_work); - - if (t->status != SMB_DIRECT_CS_CONNECTED) - return; - - smb_direct_post_send_data(t, NULL, NULL, 0, 0); -} - -static struct smb_direct_transport *alloc_transport(struct rdma_cm_id *cm_id) -{ - struct smb_direct_transport *t; - struct ksmbd_conn *conn; - - t = kzalloc(sizeof(*t), GFP_KERNEL); - if (!t) - return NULL; - - t->cm_id = cm_id; - cm_id->context = t; - - t->status = SMB_DIRECT_CS_NEW; - init_waitqueue_head(&t->wait_status); - - spin_lock_init(&t->reassembly_queue_lock); - INIT_LIST_HEAD(&t->reassembly_queue); - t->reassembly_data_length = 0; - t->reassembly_queue_length = 0; - init_waitqueue_head(&t->wait_reassembly_queue); - init_waitqueue_head(&t->wait_send_credits); - init_waitqueue_head(&t->wait_rw_credits); - - spin_lock_init(&t->receive_credit_lock); - spin_lock_init(&t->recvmsg_queue_lock); - INIT_LIST_HEAD(&t->recvmsg_queue); - - spin_lock_init(&t->empty_recvmsg_queue_lock); - INIT_LIST_HEAD(&t->empty_recvmsg_queue); - - init_waitqueue_head(&t->wait_send_pending); - atomic_set(&t->send_pending, 0); - - spin_lock_init(&t->lock_new_recv_credits); - - INIT_DELAYED_WORK(&t->post_recv_credits_work, - smb_direct_post_recv_credits); - INIT_WORK(&t->send_immediate_work, smb_direct_send_immediate_work); - INIT_WORK(&t->disconnect_work, smb_direct_disconnect_rdma_work); - - conn = ksmbd_conn_alloc(); - if (!conn) - goto err; - conn->transport = KSMBD_TRANS(t); - KSMBD_TRANS(t)->conn = conn; - KSMBD_TRANS(t)->ops = &ksmbd_smb_direct_transport_ops; - return t; -err: - kfree(t); - return NULL; -} - -static void free_transport(struct smb_direct_transport *t) -{ - struct smb_direct_recvmsg *recvmsg; - - wake_up_interruptible(&t->wait_send_credits); - - ksmbd_debug(RDMA, "wait for all send posted to IB to finish\n"); - wait_event(t->wait_send_pending, - atomic_read(&t->send_pending) == 0); - - cancel_work_sync(&t->disconnect_work); - cancel_delayed_work_sync(&t->post_recv_credits_work); - cancel_work_sync(&t->send_immediate_work); - - if (t->qp) { - ib_drain_qp(t->qp); - ib_mr_pool_destroy(t->qp, &t->qp->rdma_mrs); - ib_destroy_qp(t->qp); - } - - ksmbd_debug(RDMA, "drain the reassembly queue\n"); - do { - spin_lock(&t->reassembly_queue_lock); - recvmsg = get_first_reassembly(t); - if (recvmsg) { - list_del(&recvmsg->list); - spin_unlock(&t->reassembly_queue_lock); - put_recvmsg(t, recvmsg); - } else { - spin_unlock(&t->reassembly_queue_lock); - } - } while (recvmsg); - t->reassembly_data_length = 0; - - if (t->send_cq) - ib_free_cq(t->send_cq); - if (t->recv_cq) - ib_free_cq(t->recv_cq); - if (t->pd) - ib_dealloc_pd(t->pd); - if (t->cm_id) - rdma_destroy_id(t->cm_id); - - smb_direct_destroy_pools(t); - ksmbd_conn_free(KSMBD_TRANS(t)->conn); - kfree(t); -} - -static struct smb_direct_sendmsg -*smb_direct_alloc_sendmsg(struct smb_direct_transport *t) -{ - struct smb_direct_sendmsg *msg; - - msg = mempool_alloc(t->sendmsg_mempool, GFP_KERNEL); - if (!msg) - return ERR_PTR(-ENOMEM); - msg->transport = t; - INIT_LIST_HEAD(&msg->list); - msg->num_sge = 0; - return msg; -} - -static void smb_direct_free_sendmsg(struct smb_direct_transport *t, - struct smb_direct_sendmsg *msg) -{ - int i; - - if (msg->num_sge > 0) { - ib_dma_unmap_single(t->cm_id->device, - msg->sge[0].addr, msg->sge[0].length, - DMA_TO_DEVICE); - for (i = 1; i < msg->num_sge; i++) - ib_dma_unmap_page(t->cm_id->device, - msg->sge[i].addr, msg->sge[i].length, - DMA_TO_DEVICE); - } - mempool_free(msg, t->sendmsg_mempool); -} - -static int smb_direct_check_recvmsg(struct smb_direct_recvmsg *recvmsg) -{ - switch (recvmsg->type) { - case SMB_DIRECT_MSG_DATA_TRANSFER: { - struct smb_direct_data_transfer *req = - (struct smb_direct_data_transfer *)recvmsg->packet; - struct smb2_hdr *hdr = (struct smb2_hdr *)(recvmsg->packet - + le32_to_cpu(req->data_offset)); - ksmbd_debug(RDMA, - "CreditGranted: %u, CreditRequested: %u, DataLength: %u, RemainingDataLength: %u, SMB: %x, Command: %u\n", - le16_to_cpu(req->credits_granted), - le16_to_cpu(req->credits_requested), - req->data_length, req->remaining_data_length, - hdr->ProtocolId, hdr->Command); - break; - } - case SMB_DIRECT_MSG_NEGOTIATE_REQ: { - struct smb_direct_negotiate_req *req = - (struct smb_direct_negotiate_req *)recvmsg->packet; - ksmbd_debug(RDMA, - "MinVersion: %u, MaxVersion: %u, CreditRequested: %u, MaxSendSize: %u, MaxRecvSize: %u, MaxFragmentedSize: %u\n", - le16_to_cpu(req->min_version), - le16_to_cpu(req->max_version), - le16_to_cpu(req->credits_requested), - le32_to_cpu(req->preferred_send_size), - le32_to_cpu(req->max_receive_size), - le32_to_cpu(req->max_fragmented_size)); - if (le16_to_cpu(req->min_version) > 0x0100 || - le16_to_cpu(req->max_version) < 0x0100) - return -EOPNOTSUPP; - if (le16_to_cpu(req->credits_requested) <= 0 || - le32_to_cpu(req->max_receive_size) <= 128 || - le32_to_cpu(req->max_fragmented_size) <= - 128 * 1024) - return -ECONNABORTED; - - break; - } - default: - return -EINVAL; - } - return 0; -} - -static void recv_done(struct ib_cq *cq, struct ib_wc *wc) -{ - struct smb_direct_recvmsg *recvmsg; - struct smb_direct_transport *t; - - recvmsg = container_of(wc->wr_cqe, struct smb_direct_recvmsg, cqe); - t = recvmsg->transport; - - if (wc->status != IB_WC_SUCCESS || wc->opcode != IB_WC_RECV) { - if (wc->status != IB_WC_WR_FLUSH_ERR) { - pr_err("Recv error. status='%s (%d)' opcode=%d\n", - ib_wc_status_msg(wc->status), wc->status, - wc->opcode); - smb_direct_disconnect_rdma_connection(t); - } - put_empty_recvmsg(t, recvmsg); - return; - } - - ksmbd_debug(RDMA, "Recv completed. status='%s (%d)', opcode=%d\n", - ib_wc_status_msg(wc->status), wc->status, - wc->opcode); - - ib_dma_sync_single_for_cpu(wc->qp->device, recvmsg->sge.addr, - recvmsg->sge.length, DMA_FROM_DEVICE); - - switch (recvmsg->type) { - case SMB_DIRECT_MSG_NEGOTIATE_REQ: - if (wc->byte_len < sizeof(struct smb_direct_negotiate_req)) { - put_empty_recvmsg(t, recvmsg); - return; - } - t->negotiation_requested = true; - t->full_packet_received = true; - t->status = SMB_DIRECT_CS_CONNECTED; - enqueue_reassembly(t, recvmsg, 0); - wake_up_interruptible(&t->wait_status); - break; - case SMB_DIRECT_MSG_DATA_TRANSFER: { - struct smb_direct_data_transfer *data_transfer = - (struct smb_direct_data_transfer *)recvmsg->packet; - unsigned int data_length; - int avail_recvmsg_count, receive_credits; - - if (wc->byte_len < - offsetof(struct smb_direct_data_transfer, padding)) { - put_empty_recvmsg(t, recvmsg); - return; - } - - data_length = le32_to_cpu(data_transfer->data_length); - if (data_length) { - if (wc->byte_len < sizeof(struct smb_direct_data_transfer) + - (u64)data_length) { - put_empty_recvmsg(t, recvmsg); - return; - } - - if (t->full_packet_received) - recvmsg->first_segment = true; - - if (le32_to_cpu(data_transfer->remaining_data_length)) - t->full_packet_received = false; - else - t->full_packet_received = true; - - enqueue_reassembly(t, recvmsg, (int)data_length); - wake_up_interruptible(&t->wait_reassembly_queue); - - spin_lock(&t->receive_credit_lock); - receive_credits = --(t->recv_credits); - avail_recvmsg_count = t->count_avail_recvmsg; - spin_unlock(&t->receive_credit_lock); - } else { - put_empty_recvmsg(t, recvmsg); - - spin_lock(&t->receive_credit_lock); - receive_credits = --(t->recv_credits); - avail_recvmsg_count = ++(t->count_avail_recvmsg); - spin_unlock(&t->receive_credit_lock); - } - - t->recv_credit_target = - le16_to_cpu(data_transfer->credits_requested); - atomic_add(le16_to_cpu(data_transfer->credits_granted), - &t->send_credits); - - if (le16_to_cpu(data_transfer->flags) & - SMB_DIRECT_RESPONSE_REQUESTED) - queue_work(smb_direct_wq, &t->send_immediate_work); - - if (atomic_read(&t->send_credits) > 0) - wake_up_interruptible(&t->wait_send_credits); - - if (is_receive_credit_post_required(receive_credits, avail_recvmsg_count)) - mod_delayed_work(smb_direct_wq, - &t->post_recv_credits_work, 0); - break; - } - default: - break; - } -} - -static int smb_direct_post_recv(struct smb_direct_transport *t, - struct smb_direct_recvmsg *recvmsg) -{ - struct ib_recv_wr wr; - int ret; - - recvmsg->sge.addr = ib_dma_map_single(t->cm_id->device, - recvmsg->packet, t->max_recv_size, - DMA_FROM_DEVICE); - ret = ib_dma_mapping_error(t->cm_id->device, recvmsg->sge.addr); - if (ret) - return ret; - recvmsg->sge.length = t->max_recv_size; - recvmsg->sge.lkey = t->pd->local_dma_lkey; - recvmsg->cqe.done = recv_done; - - wr.wr_cqe = &recvmsg->cqe; - wr.next = NULL; - wr.sg_list = &recvmsg->sge; - wr.num_sge = 1; - - ret = ib_post_recv(t->qp, &wr, NULL); - if (ret) { - pr_err("Can't post recv: %d\n", ret); - ib_dma_unmap_single(t->cm_id->device, - recvmsg->sge.addr, recvmsg->sge.length, - DMA_FROM_DEVICE); - smb_direct_disconnect_rdma_connection(t); - return ret; - } - return ret; -} - -static int smb_direct_read(struct ksmbd_transport *t, char *buf, - unsigned int size, int unused) -{ - struct smb_direct_recvmsg *recvmsg; - struct smb_direct_data_transfer *data_transfer; - int to_copy, to_read, data_read, offset; - u32 data_length, remaining_data_length, data_offset; - int rc; - struct smb_direct_transport *st = smb_trans_direct_transfort(t); - -again: - if (st->status != SMB_DIRECT_CS_CONNECTED) { - pr_err("disconnected\n"); - return -ENOTCONN; - } - - /* - * No need to hold the reassembly queue lock all the time as we are - * the only one reading from the front of the queue. The transport - * may add more entries to the back of the queue at the same time - */ - if (st->reassembly_data_length >= size) { - int queue_length; - int queue_removed = 0; - - /* - * Need to make sure reassembly_data_length is read before - * reading reassembly_queue_length and calling - * get_first_reassembly. This call is lock free - * as we never read at the end of the queue which are being - * updated in SOFTIRQ as more data is received - */ - virt_rmb(); - queue_length = st->reassembly_queue_length; - data_read = 0; - to_read = size; - offset = st->first_entry_offset; - while (data_read < size) { - recvmsg = get_first_reassembly(st); - data_transfer = smb_direct_recvmsg_payload(recvmsg); - data_length = le32_to_cpu(data_transfer->data_length); - remaining_data_length = - le32_to_cpu(data_transfer->remaining_data_length); - data_offset = le32_to_cpu(data_transfer->data_offset); - - /* - * The upper layer expects RFC1002 length at the - * beginning of the payload. Return it to indicate - * the total length of the packet. This minimize the - * change to upper layer packet processing logic. This - * will be eventually remove when an intermediate - * transport layer is added - */ - if (recvmsg->first_segment && size == 4) { - unsigned int rfc1002_len = - data_length + remaining_data_length; - *((__be32 *)buf) = cpu_to_be32(rfc1002_len); - data_read = 4; - recvmsg->first_segment = false; - ksmbd_debug(RDMA, - "returning rfc1002 length %d\n", - rfc1002_len); - goto read_rfc1002_done; - } - - to_copy = min_t(int, data_length - offset, to_read); - memcpy(buf + data_read, (char *)data_transfer + data_offset + offset, - to_copy); - - /* move on to the next buffer? */ - if (to_copy == data_length - offset) { - queue_length--; - /* - * No need to lock if we are not at the - * end of the queue - */ - if (queue_length) { - list_del(&recvmsg->list); - } else { - spin_lock_irq(&st->reassembly_queue_lock); - list_del(&recvmsg->list); - spin_unlock_irq(&st->reassembly_queue_lock); - } - queue_removed++; - put_recvmsg(st, recvmsg); - offset = 0; - } else { - offset += to_copy; - } - - to_read -= to_copy; - data_read += to_copy; - } - - spin_lock_irq(&st->reassembly_queue_lock); - st->reassembly_data_length -= data_read; - st->reassembly_queue_length -= queue_removed; - spin_unlock_irq(&st->reassembly_queue_lock); - - spin_lock(&st->receive_credit_lock); - st->count_avail_recvmsg += queue_removed; - if (is_receive_credit_post_required(st->recv_credits, st->count_avail_recvmsg)) { - spin_unlock(&st->receive_credit_lock); - mod_delayed_work(smb_direct_wq, - &st->post_recv_credits_work, 0); - } else { - spin_unlock(&st->receive_credit_lock); - } - - st->first_entry_offset = offset; - ksmbd_debug(RDMA, - "returning to thread data_read=%d reassembly_data_length=%d first_entry_offset=%d\n", - data_read, st->reassembly_data_length, - st->first_entry_offset); -read_rfc1002_done: - return data_read; - } - - ksmbd_debug(RDMA, "wait_event on more data\n"); - rc = wait_event_interruptible(st->wait_reassembly_queue, - st->reassembly_data_length >= size || - st->status != SMB_DIRECT_CS_CONNECTED); - if (rc) - return -EINTR; - - goto again; -} - -static void smb_direct_post_recv_credits(struct work_struct *work) -{ - struct smb_direct_transport *t = container_of(work, - struct smb_direct_transport, post_recv_credits_work.work); - struct smb_direct_recvmsg *recvmsg; - int receive_credits, credits = 0; - int ret; - int use_free = 1; - - spin_lock(&t->receive_credit_lock); - receive_credits = t->recv_credits; - spin_unlock(&t->receive_credit_lock); - - if (receive_credits < t->recv_credit_target) { - while (true) { - if (use_free) - recvmsg = get_free_recvmsg(t); - else - recvmsg = get_empty_recvmsg(t); - if (!recvmsg) { - if (use_free) { - use_free = 0; - continue; - } else { - break; - } - } - - recvmsg->type = SMB_DIRECT_MSG_DATA_TRANSFER; - recvmsg->first_segment = false; - - ret = smb_direct_post_recv(t, recvmsg); - if (ret) { - pr_err("Can't post recv: %d\n", ret); - put_recvmsg(t, recvmsg); - break; - } - credits++; - } - } - - spin_lock(&t->receive_credit_lock); - t->recv_credits += credits; - t->count_avail_recvmsg -= credits; - spin_unlock(&t->receive_credit_lock); - - spin_lock(&t->lock_new_recv_credits); - t->new_recv_credits += credits; - spin_unlock(&t->lock_new_recv_credits); - - if (credits) - queue_work(smb_direct_wq, &t->send_immediate_work); -} - -static void send_done(struct ib_cq *cq, struct ib_wc *wc) -{ - struct smb_direct_sendmsg *sendmsg, *sibling; - struct smb_direct_transport *t; - struct list_head *pos, *prev, *end; - - sendmsg = container_of(wc->wr_cqe, struct smb_direct_sendmsg, cqe); - t = sendmsg->transport; - - ksmbd_debug(RDMA, "Send completed. status='%s (%d)', opcode=%d\n", - ib_wc_status_msg(wc->status), wc->status, - wc->opcode); - - if (wc->status != IB_WC_SUCCESS || wc->opcode != IB_WC_SEND) { - pr_err("Send error. status='%s (%d)', opcode=%d\n", - ib_wc_status_msg(wc->status), wc->status, - wc->opcode); - smb_direct_disconnect_rdma_connection(t); - } - - if (atomic_dec_and_test(&t->send_pending)) - wake_up(&t->wait_send_pending); - - /* iterate and free the list of messages in reverse. the list's head - * is invalid. - */ - for (pos = &sendmsg->list, prev = pos->prev, end = sendmsg->list.next; - prev != end; pos = prev, prev = prev->prev) { - sibling = container_of(pos, struct smb_direct_sendmsg, list); - smb_direct_free_sendmsg(t, sibling); - } - - sibling = container_of(pos, struct smb_direct_sendmsg, list); - smb_direct_free_sendmsg(t, sibling); -} - -static int manage_credits_prior_sending(struct smb_direct_transport *t) -{ - int new_credits; - - spin_lock(&t->lock_new_recv_credits); - new_credits = t->new_recv_credits; - t->new_recv_credits = 0; - spin_unlock(&t->lock_new_recv_credits); - - return new_credits; -} - -static int smb_direct_post_send(struct smb_direct_transport *t, - struct ib_send_wr *wr) -{ - int ret; - - atomic_inc(&t->send_pending); - ret = ib_post_send(t->qp, wr, NULL); - if (ret) { - pr_err("failed to post send: %d\n", ret); - if (atomic_dec_and_test(&t->send_pending)) - wake_up(&t->wait_send_pending); - smb_direct_disconnect_rdma_connection(t); - } - return ret; -} - -static void smb_direct_send_ctx_init(struct smb_direct_transport *t, - struct smb_direct_send_ctx *send_ctx, - bool need_invalidate_rkey, - unsigned int remote_key) -{ - INIT_LIST_HEAD(&send_ctx->msg_list); - send_ctx->wr_cnt = 0; - send_ctx->need_invalidate_rkey = need_invalidate_rkey; - send_ctx->remote_key = remote_key; -} - -static int smb_direct_flush_send_list(struct smb_direct_transport *t, - struct smb_direct_send_ctx *send_ctx, - bool is_last) -{ - struct smb_direct_sendmsg *first, *last; - int ret; - - if (list_empty(&send_ctx->msg_list)) - return 0; - - first = list_first_entry(&send_ctx->msg_list, - struct smb_direct_sendmsg, - list); - last = list_last_entry(&send_ctx->msg_list, - struct smb_direct_sendmsg, - list); - - last->wr.send_flags = IB_SEND_SIGNALED; - last->wr.wr_cqe = &last->cqe; - if (is_last && send_ctx->need_invalidate_rkey) { - last->wr.opcode = IB_WR_SEND_WITH_INV; - last->wr.ex.invalidate_rkey = send_ctx->remote_key; - } - - ret = smb_direct_post_send(t, &first->wr); - if (!ret) { - smb_direct_send_ctx_init(t, send_ctx, - send_ctx->need_invalidate_rkey, - send_ctx->remote_key); - } else { - atomic_add(send_ctx->wr_cnt, &t->send_credits); - wake_up(&t->wait_send_credits); - list_for_each_entry_safe(first, last, &send_ctx->msg_list, - list) { - smb_direct_free_sendmsg(t, first); - } - } - return ret; -} - -static int wait_for_credits(struct smb_direct_transport *t, - wait_queue_head_t *waitq, atomic_t *total_credits, - int needed) -{ - int ret; - - do { - if (atomic_sub_return(needed, total_credits) >= 0) - return 0; - - atomic_add(needed, total_credits); - ret = wait_event_interruptible(*waitq, - atomic_read(total_credits) >= needed || - t->status != SMB_DIRECT_CS_CONNECTED); - - if (t->status != SMB_DIRECT_CS_CONNECTED) - return -ENOTCONN; - else if (ret < 0) - return ret; - } while (true); -} - -static int wait_for_send_credits(struct smb_direct_transport *t, - struct smb_direct_send_ctx *send_ctx) -{ - int ret; - - if (send_ctx && - (send_ctx->wr_cnt >= 16 || atomic_read(&t->send_credits) <= 1)) { - ret = smb_direct_flush_send_list(t, send_ctx, false); - if (ret) - return ret; - } - - return wait_for_credits(t, &t->wait_send_credits, &t->send_credits, 1); -} - -static int wait_for_rw_credits(struct smb_direct_transport *t, int credits) -{ - return wait_for_credits(t, &t->wait_rw_credits, &t->rw_credits, credits); -} - -static int calc_rw_credits(struct smb_direct_transport *t, - char *buf, unsigned int len) -{ - return DIV_ROUND_UP(get_buf_page_count(buf, len), - t->pages_per_rw_credit); -} - -static int smb_direct_create_header(struct smb_direct_transport *t, - int size, int remaining_data_length, - struct smb_direct_sendmsg **sendmsg_out) -{ - struct smb_direct_sendmsg *sendmsg; - struct smb_direct_data_transfer *packet; - int header_length; - int ret; - - sendmsg = smb_direct_alloc_sendmsg(t); - if (IS_ERR(sendmsg)) - return PTR_ERR(sendmsg); - - /* Fill in the packet header */ - packet = (struct smb_direct_data_transfer *)sendmsg->packet; - packet->credits_requested = cpu_to_le16(t->send_credit_target); - packet->credits_granted = cpu_to_le16(manage_credits_prior_sending(t)); - - packet->flags = 0; - packet->reserved = 0; - if (!size) - packet->data_offset = 0; - else - packet->data_offset = cpu_to_le32(24); - packet->data_length = cpu_to_le32(size); - packet->remaining_data_length = cpu_to_le32(remaining_data_length); - packet->padding = 0; - - ksmbd_debug(RDMA, - "credits_requested=%d credits_granted=%d data_offset=%d data_length=%d remaining_data_length=%d\n", - le16_to_cpu(packet->credits_requested), - le16_to_cpu(packet->credits_granted), - le32_to_cpu(packet->data_offset), - le32_to_cpu(packet->data_length), - le32_to_cpu(packet->remaining_data_length)); - - /* Map the packet to DMA */ - header_length = sizeof(struct smb_direct_data_transfer); - /* If this is a packet without payload, don't send padding */ - if (!size) - header_length = - offsetof(struct smb_direct_data_transfer, padding); - - sendmsg->sge[0].addr = ib_dma_map_single(t->cm_id->device, - (void *)packet, - header_length, - DMA_TO_DEVICE); - ret = ib_dma_mapping_error(t->cm_id->device, sendmsg->sge[0].addr); - if (ret) { - smb_direct_free_sendmsg(t, sendmsg); - return ret; - } - - sendmsg->num_sge = 1; - sendmsg->sge[0].length = header_length; - sendmsg->sge[0].lkey = t->pd->local_dma_lkey; - - *sendmsg_out = sendmsg; - return 0; -} - -static int get_sg_list(void *buf, int size, struct scatterlist *sg_list, int nentries) -{ - bool high = is_vmalloc_addr(buf); - struct page *page; - int offset, len; - int i = 0; - - if (size <= 0 || nentries < get_buf_page_count(buf, size)) - return -EINVAL; - - offset = offset_in_page(buf); - buf -= offset; - while (size > 0) { - len = min_t(int, PAGE_SIZE - offset, size); - if (high) - page = vmalloc_to_page(buf); - else - page = kmap_to_page(buf); - - if (!sg_list) - return -EINVAL; - sg_set_page(sg_list, page, len, offset); - sg_list = sg_next(sg_list); - - buf += PAGE_SIZE; - size -= len; - offset = 0; - i++; - } - return i; -} - -static int get_mapped_sg_list(struct ib_device *device, void *buf, int size, - struct scatterlist *sg_list, int nentries, - enum dma_data_direction dir) -{ - int npages; - - npages = get_sg_list(buf, size, sg_list, nentries); - if (npages < 0) - return -EINVAL; - return ib_dma_map_sg(device, sg_list, npages, dir); -} - -static int post_sendmsg(struct smb_direct_transport *t, - struct smb_direct_send_ctx *send_ctx, - struct smb_direct_sendmsg *msg) -{ - int i; - - for (i = 0; i < msg->num_sge; i++) - ib_dma_sync_single_for_device(t->cm_id->device, - msg->sge[i].addr, msg->sge[i].length, - DMA_TO_DEVICE); - - msg->cqe.done = send_done; - msg->wr.opcode = IB_WR_SEND; - msg->wr.sg_list = &msg->sge[0]; - msg->wr.num_sge = msg->num_sge; - msg->wr.next = NULL; - - if (send_ctx) { - msg->wr.wr_cqe = NULL; - msg->wr.send_flags = 0; - if (!list_empty(&send_ctx->msg_list)) { - struct smb_direct_sendmsg *last; - - last = list_last_entry(&send_ctx->msg_list, - struct smb_direct_sendmsg, - list); - last->wr.next = &msg->wr; - } - list_add_tail(&msg->list, &send_ctx->msg_list); - send_ctx->wr_cnt++; - return 0; - } - - msg->wr.wr_cqe = &msg->cqe; - msg->wr.send_flags = IB_SEND_SIGNALED; - return smb_direct_post_send(t, &msg->wr); -} - -static int smb_direct_post_send_data(struct smb_direct_transport *t, - struct smb_direct_send_ctx *send_ctx, - struct kvec *iov, int niov, - int remaining_data_length) -{ - int i, j, ret; - struct smb_direct_sendmsg *msg; - int data_length; - struct scatterlist sg[SMB_DIRECT_MAX_SEND_SGES - 1]; - - ret = wait_for_send_credits(t, send_ctx); - if (ret) - return ret; - - data_length = 0; - for (i = 0; i < niov; i++) - data_length += iov[i].iov_len; - - ret = smb_direct_create_header(t, data_length, remaining_data_length, - &msg); - if (ret) { - atomic_inc(&t->send_credits); - return ret; - } - - for (i = 0; i < niov; i++) { - struct ib_sge *sge; - int sg_cnt; - - sg_init_table(sg, SMB_DIRECT_MAX_SEND_SGES - 1); - sg_cnt = get_mapped_sg_list(t->cm_id->device, - iov[i].iov_base, iov[i].iov_len, - sg, SMB_DIRECT_MAX_SEND_SGES - 1, - DMA_TO_DEVICE); - if (sg_cnt <= 0) { - pr_err("failed to map buffer\n"); - ret = -ENOMEM; - goto err; - } else if (sg_cnt + msg->num_sge > SMB_DIRECT_MAX_SEND_SGES) { - pr_err("buffer not fitted into sges\n"); - ret = -E2BIG; - ib_dma_unmap_sg(t->cm_id->device, sg, sg_cnt, - DMA_TO_DEVICE); - goto err; - } - - for (j = 0; j < sg_cnt; j++) { - sge = &msg->sge[msg->num_sge]; - sge->addr = sg_dma_address(&sg[j]); - sge->length = sg_dma_len(&sg[j]); - sge->lkey = t->pd->local_dma_lkey; - msg->num_sge++; - } - } - - ret = post_sendmsg(t, send_ctx, msg); - if (ret) - goto err; - return 0; -err: - smb_direct_free_sendmsg(t, msg); - atomic_inc(&t->send_credits); - return ret; -} - -static int smb_direct_writev(struct ksmbd_transport *t, - struct kvec *iov, int niovs, int buflen, - bool need_invalidate, unsigned int remote_key) -{ - struct smb_direct_transport *st = smb_trans_direct_transfort(t); - int remaining_data_length; - int start, i, j; - int max_iov_size = st->max_send_size - - sizeof(struct smb_direct_data_transfer); - int ret; - struct kvec vec; - struct smb_direct_send_ctx send_ctx; - - if (st->status != SMB_DIRECT_CS_CONNECTED) - return -ENOTCONN; - - //FIXME: skip RFC1002 header.. - buflen -= 4; - iov[0].iov_base += 4; - iov[0].iov_len -= 4; - - remaining_data_length = buflen; - ksmbd_debug(RDMA, "Sending smb (RDMA): smb_len=%u\n", buflen); - - smb_direct_send_ctx_init(st, &send_ctx, need_invalidate, remote_key); - start = i = 0; - buflen = 0; - while (true) { - buflen += iov[i].iov_len; - if (buflen > max_iov_size) { - if (i > start) { - remaining_data_length -= - (buflen - iov[i].iov_len); - ret = smb_direct_post_send_data(st, &send_ctx, - &iov[start], i - start, - remaining_data_length); - if (ret) - goto done; - } else { - /* iov[start] is too big, break it */ - int nvec = (buflen + max_iov_size - 1) / - max_iov_size; - - for (j = 0; j < nvec; j++) { - vec.iov_base = - (char *)iov[start].iov_base + - j * max_iov_size; - vec.iov_len = - min_t(int, max_iov_size, - buflen - max_iov_size * j); - remaining_data_length -= vec.iov_len; - ret = smb_direct_post_send_data(st, &send_ctx, &vec, 1, - remaining_data_length); - if (ret) - goto done; - } - i++; - if (i == niovs) - break; - } - start = i; - buflen = 0; - } else { - i++; - if (i == niovs) { - /* send out all remaining vecs */ - remaining_data_length -= buflen; - ret = smb_direct_post_send_data(st, &send_ctx, - &iov[start], i - start, - remaining_data_length); - if (ret) - goto done; - break; - } - } - } - -done: - ret = smb_direct_flush_send_list(st, &send_ctx, true); - - /* - * As an optimization, we don't wait for individual I/O to finish - * before sending the next one. - * Send them all and wait for pending send count to get to 0 - * that means all the I/Os have been out and we are good to return - */ - - wait_event(st->wait_send_pending, - atomic_read(&st->send_pending) == 0); - return ret; -} - -static void smb_direct_free_rdma_rw_msg(struct smb_direct_transport *t, - struct smb_direct_rdma_rw_msg *msg, - enum dma_data_direction dir) -{ - rdma_rw_ctx_destroy(&msg->rw_ctx, t->qp, t->qp->port, - msg->sgt.sgl, msg->sgt.nents, dir); - sg_free_table_chained(&msg->sgt, SG_CHUNK_SIZE); - kfree(msg); -} - -static void read_write_done(struct ib_cq *cq, struct ib_wc *wc, - enum dma_data_direction dir) -{ - struct smb_direct_rdma_rw_msg *msg = container_of(wc->wr_cqe, - struct smb_direct_rdma_rw_msg, cqe); - struct smb_direct_transport *t = msg->t; - - if (wc->status != IB_WC_SUCCESS) { - msg->status = -EIO; - pr_err("read/write error. opcode = %d, status = %s(%d)\n", - wc->opcode, ib_wc_status_msg(wc->status), wc->status); - if (wc->status != IB_WC_WR_FLUSH_ERR) - smb_direct_disconnect_rdma_connection(t); - } - - complete(msg->completion); -} - -static void read_done(struct ib_cq *cq, struct ib_wc *wc) -{ - read_write_done(cq, wc, DMA_FROM_DEVICE); -} - -static void write_done(struct ib_cq *cq, struct ib_wc *wc) -{ - read_write_done(cq, wc, DMA_TO_DEVICE); -} - -static int smb_direct_rdma_xmit(struct smb_direct_transport *t, - void *buf, int buf_len, - struct smb2_buffer_desc_v1 *desc, - unsigned int desc_len, - bool is_read) -{ - struct smb_direct_rdma_rw_msg *msg, *next_msg; - int i, ret; - DECLARE_COMPLETION_ONSTACK(completion); - struct ib_send_wr *first_wr; - LIST_HEAD(msg_list); - char *desc_buf; - int credits_needed; - unsigned int desc_buf_len; - size_t total_length = 0; - - if (t->status != SMB_DIRECT_CS_CONNECTED) - return -ENOTCONN; - - /* calculate needed credits */ - credits_needed = 0; - desc_buf = buf; - for (i = 0; i < desc_len / sizeof(*desc); i++) { - desc_buf_len = le32_to_cpu(desc[i].length); - - credits_needed += calc_rw_credits(t, desc_buf, desc_buf_len); - desc_buf += desc_buf_len; - total_length += desc_buf_len; - if (desc_buf_len == 0 || total_length > buf_len || - total_length > t->max_rdma_rw_size) - return -EINVAL; - } - - ksmbd_debug(RDMA, "RDMA %s, len %#x, needed credits %#x\n", - is_read ? "read" : "write", buf_len, credits_needed); - - ret = wait_for_rw_credits(t, credits_needed); - if (ret < 0) - return ret; - - /* build rdma_rw_ctx for each descriptor */ - desc_buf = buf; - for (i = 0; i < desc_len / sizeof(*desc); i++) { - msg = kzalloc(offsetof(struct smb_direct_rdma_rw_msg, sg_list) + - sizeof(struct scatterlist) * SG_CHUNK_SIZE, GFP_KERNEL); - if (!msg) { - ret = -ENOMEM; - goto out; - } - - desc_buf_len = le32_to_cpu(desc[i].length); - - msg->t = t; - msg->cqe.done = is_read ? read_done : write_done; - msg->completion = &completion; - - msg->sgt.sgl = &msg->sg_list[0]; - ret = sg_alloc_table_chained(&msg->sgt, - get_buf_page_count(desc_buf, desc_buf_len), - msg->sg_list, SG_CHUNK_SIZE); - if (ret) { - kfree(msg); - ret = -ENOMEM; - goto out; - } - - ret = get_sg_list(desc_buf, desc_buf_len, - msg->sgt.sgl, msg->sgt.orig_nents); - if (ret < 0) { - sg_free_table_chained(&msg->sgt, SG_CHUNK_SIZE); - kfree(msg); - goto out; - } - - ret = rdma_rw_ctx_init(&msg->rw_ctx, t->qp, t->qp->port, - msg->sgt.sgl, - get_buf_page_count(desc_buf, desc_buf_len), - 0, - le64_to_cpu(desc[i].offset), - le32_to_cpu(desc[i].token), - is_read ? DMA_FROM_DEVICE : DMA_TO_DEVICE); - if (ret < 0) { - pr_err("failed to init rdma_rw_ctx: %d\n", ret); - sg_free_table_chained(&msg->sgt, SG_CHUNK_SIZE); - kfree(msg); - goto out; - } - - list_add_tail(&msg->list, &msg_list); - desc_buf += desc_buf_len; - } - - /* concatenate work requests of rdma_rw_ctxs */ - first_wr = NULL; - list_for_each_entry_reverse(msg, &msg_list, list) { - first_wr = rdma_rw_ctx_wrs(&msg->rw_ctx, t->qp, t->qp->port, - &msg->cqe, first_wr); - } - - ret = ib_post_send(t->qp, first_wr, NULL); - if (ret) { - pr_err("failed to post send wr for RDMA R/W: %d\n", ret); - goto out; - } - - msg = list_last_entry(&msg_list, struct smb_direct_rdma_rw_msg, list); - wait_for_completion(&completion); - ret = msg->status; -out: - list_for_each_entry_safe(msg, next_msg, &msg_list, list) { - list_del(&msg->list); - smb_direct_free_rdma_rw_msg(t, msg, - is_read ? DMA_FROM_DEVICE : DMA_TO_DEVICE); - } - atomic_add(credits_needed, &t->rw_credits); - wake_up(&t->wait_rw_credits); - return ret; -} - -static int smb_direct_rdma_write(struct ksmbd_transport *t, - void *buf, unsigned int buflen, - struct smb2_buffer_desc_v1 *desc, - unsigned int desc_len) -{ - return smb_direct_rdma_xmit(smb_trans_direct_transfort(t), buf, buflen, - desc, desc_len, false); -} - -static int smb_direct_rdma_read(struct ksmbd_transport *t, - void *buf, unsigned int buflen, - struct smb2_buffer_desc_v1 *desc, - unsigned int desc_len) -{ - return smb_direct_rdma_xmit(smb_trans_direct_transfort(t), buf, buflen, - desc, desc_len, true); -} - -static void smb_direct_disconnect(struct ksmbd_transport *t) -{ - struct smb_direct_transport *st = smb_trans_direct_transfort(t); - - ksmbd_debug(RDMA, "Disconnecting cm_id=%p\n", st->cm_id); - - smb_direct_disconnect_rdma_work(&st->disconnect_work); - wait_event_interruptible(st->wait_status, - st->status == SMB_DIRECT_CS_DISCONNECTED); - free_transport(st); -} - -static void smb_direct_shutdown(struct ksmbd_transport *t) -{ - struct smb_direct_transport *st = smb_trans_direct_transfort(t); - - ksmbd_debug(RDMA, "smb-direct shutdown cm_id=%p\n", st->cm_id); - - smb_direct_disconnect_rdma_work(&st->disconnect_work); -} - -static int smb_direct_cm_handler(struct rdma_cm_id *cm_id, - struct rdma_cm_event *event) -{ - struct smb_direct_transport *t = cm_id->context; - - ksmbd_debug(RDMA, "RDMA CM event. cm_id=%p event=%s (%d)\n", - cm_id, rdma_event_msg(event->event), event->event); - - switch (event->event) { - case RDMA_CM_EVENT_ESTABLISHED: { - t->status = SMB_DIRECT_CS_CONNECTED; - wake_up_interruptible(&t->wait_status); - break; - } - case RDMA_CM_EVENT_DEVICE_REMOVAL: - case RDMA_CM_EVENT_DISCONNECTED: { - ib_drain_qp(t->qp); - - t->status = SMB_DIRECT_CS_DISCONNECTED; - wake_up_interruptible(&t->wait_status); - wake_up_interruptible(&t->wait_reassembly_queue); - wake_up(&t->wait_send_credits); - break; - } - case RDMA_CM_EVENT_CONNECT_ERROR: { - t->status = SMB_DIRECT_CS_DISCONNECTED; - wake_up_interruptible(&t->wait_status); - break; - } - default: - pr_err("Unexpected RDMA CM event. cm_id=%p, event=%s (%d)\n", - cm_id, rdma_event_msg(event->event), - event->event); - break; - } - return 0; -} - -static void smb_direct_qpair_handler(struct ib_event *event, void *context) -{ - struct smb_direct_transport *t = context; - - ksmbd_debug(RDMA, "Received QP event. cm_id=%p, event=%s (%d)\n", - t->cm_id, ib_event_msg(event->event), event->event); - - switch (event->event) { - case IB_EVENT_CQ_ERR: - case IB_EVENT_QP_FATAL: - smb_direct_disconnect_rdma_connection(t); - break; - default: - break; - } -} - -static int smb_direct_send_negotiate_response(struct smb_direct_transport *t, - int failed) -{ - struct smb_direct_sendmsg *sendmsg; - struct smb_direct_negotiate_resp *resp; - int ret; - - sendmsg = smb_direct_alloc_sendmsg(t); - if (IS_ERR(sendmsg)) - return -ENOMEM; - - resp = (struct smb_direct_negotiate_resp *)sendmsg->packet; - if (failed) { - memset(resp, 0, sizeof(*resp)); - resp->min_version = cpu_to_le16(0x0100); - resp->max_version = cpu_to_le16(0x0100); - resp->status = STATUS_NOT_SUPPORTED; - } else { - resp->status = STATUS_SUCCESS; - resp->min_version = SMB_DIRECT_VERSION_LE; - resp->max_version = SMB_DIRECT_VERSION_LE; - resp->negotiated_version = SMB_DIRECT_VERSION_LE; - resp->reserved = 0; - resp->credits_requested = - cpu_to_le16(t->send_credit_target); - resp->credits_granted = cpu_to_le16(manage_credits_prior_sending(t)); - resp->max_readwrite_size = cpu_to_le32(t->max_rdma_rw_size); - resp->preferred_send_size = cpu_to_le32(t->max_send_size); - resp->max_receive_size = cpu_to_le32(t->max_recv_size); - resp->max_fragmented_size = - cpu_to_le32(t->max_fragmented_recv_size); - } - - sendmsg->sge[0].addr = ib_dma_map_single(t->cm_id->device, - (void *)resp, sizeof(*resp), - DMA_TO_DEVICE); - ret = ib_dma_mapping_error(t->cm_id->device, sendmsg->sge[0].addr); - if (ret) { - smb_direct_free_sendmsg(t, sendmsg); - return ret; - } - - sendmsg->num_sge = 1; - sendmsg->sge[0].length = sizeof(*resp); - sendmsg->sge[0].lkey = t->pd->local_dma_lkey; - - ret = post_sendmsg(t, NULL, sendmsg); - if (ret) { - smb_direct_free_sendmsg(t, sendmsg); - return ret; - } - - wait_event(t->wait_send_pending, - atomic_read(&t->send_pending) == 0); - return 0; -} - -static int smb_direct_accept_client(struct smb_direct_transport *t) -{ - struct rdma_conn_param conn_param; - struct ib_port_immutable port_immutable; - u32 ird_ord_hdr[2]; - int ret; - - memset(&conn_param, 0, sizeof(conn_param)); - conn_param.initiator_depth = min_t(u8, t->cm_id->device->attrs.max_qp_rd_atom, - SMB_DIRECT_CM_INITIATOR_DEPTH); - conn_param.responder_resources = 0; - - t->cm_id->device->ops.get_port_immutable(t->cm_id->device, - t->cm_id->port_num, - &port_immutable); - if (port_immutable.core_cap_flags & RDMA_CORE_PORT_IWARP) { - ird_ord_hdr[0] = conn_param.responder_resources; - ird_ord_hdr[1] = 1; - conn_param.private_data = ird_ord_hdr; - conn_param.private_data_len = sizeof(ird_ord_hdr); - } else { - conn_param.private_data = NULL; - conn_param.private_data_len = 0; - } - conn_param.retry_count = SMB_DIRECT_CM_RETRY; - conn_param.rnr_retry_count = SMB_DIRECT_CM_RNR_RETRY; - conn_param.flow_control = 0; - - ret = rdma_accept(t->cm_id, &conn_param); - if (ret) { - pr_err("error at rdma_accept: %d\n", ret); - return ret; - } - return 0; -} - -static int smb_direct_prepare_negotiation(struct smb_direct_transport *t) -{ - int ret; - struct smb_direct_recvmsg *recvmsg; - - recvmsg = get_free_recvmsg(t); - if (!recvmsg) - return -ENOMEM; - recvmsg->type = SMB_DIRECT_MSG_NEGOTIATE_REQ; - - ret = smb_direct_post_recv(t, recvmsg); - if (ret) { - pr_err("Can't post recv: %d\n", ret); - goto out_err; - } - - t->negotiation_requested = false; - ret = smb_direct_accept_client(t); - if (ret) { - pr_err("Can't accept client\n"); - goto out_err; - } - - smb_direct_post_recv_credits(&t->post_recv_credits_work.work); - return 0; -out_err: - put_recvmsg(t, recvmsg); - return ret; -} - -static unsigned int smb_direct_get_max_fr_pages(struct smb_direct_transport *t) -{ - return min_t(unsigned int, - t->cm_id->device->attrs.max_fast_reg_page_list_len, - 256); -} - -static int smb_direct_init_params(struct smb_direct_transport *t, - struct ib_qp_cap *cap) -{ - struct ib_device *device = t->cm_id->device; - int max_send_sges, max_rw_wrs, max_send_wrs; - unsigned int max_sge_per_wr, wrs_per_credit; - - /* need 3 more sge. because a SMB_DIRECT header, SMB2 header, - * SMB2 response could be mapped. - */ - t->max_send_size = smb_direct_max_send_size; - max_send_sges = DIV_ROUND_UP(t->max_send_size, PAGE_SIZE) + 3; - if (max_send_sges > SMB_DIRECT_MAX_SEND_SGES) { - pr_err("max_send_size %d is too large\n", t->max_send_size); - return -EINVAL; - } - - /* Calculate the number of work requests for RDMA R/W. - * The maximum number of pages which can be registered - * with one Memory region can be transferred with one - * R/W credit. And at least 4 work requests for each credit - * are needed for MR registration, RDMA R/W, local & remote - * MR invalidation. - */ - t->max_rdma_rw_size = smb_direct_max_read_write_size; - t->pages_per_rw_credit = smb_direct_get_max_fr_pages(t); - t->max_rw_credits = DIV_ROUND_UP(t->max_rdma_rw_size, - (t->pages_per_rw_credit - 1) * - PAGE_SIZE); - - max_sge_per_wr = min_t(unsigned int, device->attrs.max_send_sge, - device->attrs.max_sge_rd); - max_sge_per_wr = max_t(unsigned int, max_sge_per_wr, - max_send_sges); - wrs_per_credit = max_t(unsigned int, 4, - DIV_ROUND_UP(t->pages_per_rw_credit, - max_sge_per_wr) + 1); - max_rw_wrs = t->max_rw_credits * wrs_per_credit; - - max_send_wrs = smb_direct_send_credit_target + max_rw_wrs; - if (max_send_wrs > device->attrs.max_cqe || - max_send_wrs > device->attrs.max_qp_wr) { - pr_err("consider lowering send_credit_target = %d\n", - smb_direct_send_credit_target); - pr_err("Possible CQE overrun, device reporting max_cqe %d max_qp_wr %d\n", - device->attrs.max_cqe, device->attrs.max_qp_wr); - return -EINVAL; - } - - if (smb_direct_receive_credit_max > device->attrs.max_cqe || - smb_direct_receive_credit_max > device->attrs.max_qp_wr) { - pr_err("consider lowering receive_credit_max = %d\n", - smb_direct_receive_credit_max); - pr_err("Possible CQE overrun, device reporting max_cpe %d max_qp_wr %d\n", - device->attrs.max_cqe, device->attrs.max_qp_wr); - return -EINVAL; - } - - if (device->attrs.max_recv_sge < SMB_DIRECT_MAX_RECV_SGES) { - pr_err("warning: device max_recv_sge = %d too small\n", - device->attrs.max_recv_sge); - return -EINVAL; - } - - t->recv_credits = 0; - t->count_avail_recvmsg = 0; - - t->recv_credit_max = smb_direct_receive_credit_max; - t->recv_credit_target = 10; - t->new_recv_credits = 0; - - t->send_credit_target = smb_direct_send_credit_target; - atomic_set(&t->send_credits, 0); - atomic_set(&t->rw_credits, t->max_rw_credits); - - t->max_send_size = smb_direct_max_send_size; - t->max_recv_size = smb_direct_max_receive_size; - t->max_fragmented_recv_size = smb_direct_max_fragmented_recv_size; - - cap->max_send_wr = max_send_wrs; - cap->max_recv_wr = t->recv_credit_max; - cap->max_send_sge = max_sge_per_wr; - cap->max_recv_sge = SMB_DIRECT_MAX_RECV_SGES; - cap->max_inline_data = 0; - cap->max_rdma_ctxs = t->max_rw_credits; - return 0; -} - -static void smb_direct_destroy_pools(struct smb_direct_transport *t) -{ - struct smb_direct_recvmsg *recvmsg; - - while ((recvmsg = get_free_recvmsg(t))) - mempool_free(recvmsg, t->recvmsg_mempool); - while ((recvmsg = get_empty_recvmsg(t))) - mempool_free(recvmsg, t->recvmsg_mempool); - - mempool_destroy(t->recvmsg_mempool); - t->recvmsg_mempool = NULL; - - kmem_cache_destroy(t->recvmsg_cache); - t->recvmsg_cache = NULL; - - mempool_destroy(t->sendmsg_mempool); - t->sendmsg_mempool = NULL; - - kmem_cache_destroy(t->sendmsg_cache); - t->sendmsg_cache = NULL; -} - -static int smb_direct_create_pools(struct smb_direct_transport *t) -{ - char name[80]; - int i; - struct smb_direct_recvmsg *recvmsg; - - snprintf(name, sizeof(name), "smb_direct_rqst_pool_%p", t); - t->sendmsg_cache = kmem_cache_create(name, - sizeof(struct smb_direct_sendmsg) + - sizeof(struct smb_direct_negotiate_resp), - 0, SLAB_HWCACHE_ALIGN, NULL); - if (!t->sendmsg_cache) - return -ENOMEM; - - t->sendmsg_mempool = mempool_create(t->send_credit_target, - mempool_alloc_slab, mempool_free_slab, - t->sendmsg_cache); - if (!t->sendmsg_mempool) - goto err; - - snprintf(name, sizeof(name), "smb_direct_resp_%p", t); - t->recvmsg_cache = kmem_cache_create(name, - sizeof(struct smb_direct_recvmsg) + - t->max_recv_size, - 0, SLAB_HWCACHE_ALIGN, NULL); - if (!t->recvmsg_cache) - goto err; - - t->recvmsg_mempool = - mempool_create(t->recv_credit_max, mempool_alloc_slab, - mempool_free_slab, t->recvmsg_cache); - if (!t->recvmsg_mempool) - goto err; - - INIT_LIST_HEAD(&t->recvmsg_queue); - - for (i = 0; i < t->recv_credit_max; i++) { - recvmsg = mempool_alloc(t->recvmsg_mempool, GFP_KERNEL); - if (!recvmsg) - goto err; - recvmsg->transport = t; - list_add(&recvmsg->list, &t->recvmsg_queue); - } - t->count_avail_recvmsg = t->recv_credit_max; - - return 0; -err: - smb_direct_destroy_pools(t); - return -ENOMEM; -} - -static int smb_direct_create_qpair(struct smb_direct_transport *t, - struct ib_qp_cap *cap) -{ - int ret; - struct ib_qp_init_attr qp_attr; - int pages_per_rw; - - t->pd = ib_alloc_pd(t->cm_id->device, 0); - if (IS_ERR(t->pd)) { - pr_err("Can't create RDMA PD\n"); - ret = PTR_ERR(t->pd); - t->pd = NULL; - return ret; - } - - t->send_cq = ib_alloc_cq(t->cm_id->device, t, - smb_direct_send_credit_target + cap->max_rdma_ctxs, - 0, IB_POLL_WORKQUEUE); - if (IS_ERR(t->send_cq)) { - pr_err("Can't create RDMA send CQ\n"); - ret = PTR_ERR(t->send_cq); - t->send_cq = NULL; - goto err; - } - - t->recv_cq = ib_alloc_cq(t->cm_id->device, t, - t->recv_credit_max, 0, IB_POLL_WORKQUEUE); - if (IS_ERR(t->recv_cq)) { - pr_err("Can't create RDMA recv CQ\n"); - ret = PTR_ERR(t->recv_cq); - t->recv_cq = NULL; - goto err; - } - - memset(&qp_attr, 0, sizeof(qp_attr)); - qp_attr.event_handler = smb_direct_qpair_handler; - qp_attr.qp_context = t; - qp_attr.cap = *cap; - qp_attr.sq_sig_type = IB_SIGNAL_REQ_WR; - qp_attr.qp_type = IB_QPT_RC; - qp_attr.send_cq = t->send_cq; - qp_attr.recv_cq = t->recv_cq; - qp_attr.port_num = ~0; - - ret = rdma_create_qp(t->cm_id, t->pd, &qp_attr); - if (ret) { - pr_err("Can't create RDMA QP: %d\n", ret); - goto err; - } - - t->qp = t->cm_id->qp; - t->cm_id->event_handler = smb_direct_cm_handler; - - pages_per_rw = DIV_ROUND_UP(t->max_rdma_rw_size, PAGE_SIZE) + 1; - if (pages_per_rw > t->cm_id->device->attrs.max_sgl_rd) { - ret = ib_mr_pool_init(t->qp, &t->qp->rdma_mrs, - t->max_rw_credits, IB_MR_TYPE_MEM_REG, - t->pages_per_rw_credit, 0); - if (ret) { - pr_err("failed to init mr pool count %d pages %d\n", - t->max_rw_credits, t->pages_per_rw_credit); - goto err; - } - } - - return 0; -err: - if (t->qp) { - ib_destroy_qp(t->qp); - t->qp = NULL; - } - if (t->recv_cq) { - ib_destroy_cq(t->recv_cq); - t->recv_cq = NULL; - } - if (t->send_cq) { - ib_destroy_cq(t->send_cq); - t->send_cq = NULL; - } - if (t->pd) { - ib_dealloc_pd(t->pd); - t->pd = NULL; - } - return ret; -} - -static int smb_direct_prepare(struct ksmbd_transport *t) -{ - struct smb_direct_transport *st = smb_trans_direct_transfort(t); - struct smb_direct_recvmsg *recvmsg; - struct smb_direct_negotiate_req *req; - int ret; - - ksmbd_debug(RDMA, "Waiting for SMB_DIRECT negotiate request\n"); - ret = wait_event_interruptible_timeout(st->wait_status, - st->negotiation_requested || - st->status == SMB_DIRECT_CS_DISCONNECTED, - SMB_DIRECT_NEGOTIATE_TIMEOUT * HZ); - if (ret <= 0 || st->status == SMB_DIRECT_CS_DISCONNECTED) - return ret < 0 ? ret : -ETIMEDOUT; - - recvmsg = get_first_reassembly(st); - if (!recvmsg) - return -ECONNABORTED; - - ret = smb_direct_check_recvmsg(recvmsg); - if (ret == -ECONNABORTED) - goto out; - - req = (struct smb_direct_negotiate_req *)recvmsg->packet; - st->max_recv_size = min_t(int, st->max_recv_size, - le32_to_cpu(req->preferred_send_size)); - st->max_send_size = min_t(int, st->max_send_size, - le32_to_cpu(req->max_receive_size)); - st->max_fragmented_send_size = - le32_to_cpu(req->max_fragmented_size); - st->max_fragmented_recv_size = - (st->recv_credit_max * st->max_recv_size) / 2; - - ret = smb_direct_send_negotiate_response(st, ret); -out: - spin_lock_irq(&st->reassembly_queue_lock); - st->reassembly_queue_length--; - list_del(&recvmsg->list); - spin_unlock_irq(&st->reassembly_queue_lock); - put_recvmsg(st, recvmsg); - - return ret; -} - -static int smb_direct_connect(struct smb_direct_transport *st) -{ - int ret; - struct ib_qp_cap qp_cap; - - ret = smb_direct_init_params(st, &qp_cap); - if (ret) { - pr_err("Can't configure RDMA parameters\n"); - return ret; - } - - ret = smb_direct_create_pools(st); - if (ret) { - pr_err("Can't init RDMA pool: %d\n", ret); - return ret; - } - - ret = smb_direct_create_qpair(st, &qp_cap); - if (ret) { - pr_err("Can't accept RDMA client: %d\n", ret); - return ret; - } - - ret = smb_direct_prepare_negotiation(st); - if (ret) { - pr_err("Can't negotiate: %d\n", ret); - return ret; - } - return 0; -} - -static bool rdma_frwr_is_supported(struct ib_device_attr *attrs) -{ - if (!(attrs->device_cap_flags & IB_DEVICE_MEM_MGT_EXTENSIONS)) - return false; - if (attrs->max_fast_reg_page_list_len == 0) - return false; - return true; -} - -static int smb_direct_handle_connect_request(struct rdma_cm_id *new_cm_id) -{ - struct smb_direct_transport *t; - int ret; - - if (!rdma_frwr_is_supported(&new_cm_id->device->attrs)) { - ksmbd_debug(RDMA, - "Fast Registration Work Requests is not supported. device capabilities=%llx\n", - new_cm_id->device->attrs.device_cap_flags); - return -EPROTONOSUPPORT; - } - - t = alloc_transport(new_cm_id); - if (!t) - return -ENOMEM; - - ret = smb_direct_connect(t); - if (ret) - goto out_err; - - KSMBD_TRANS(t)->handler = kthread_run(ksmbd_conn_handler_loop, - KSMBD_TRANS(t)->conn, "ksmbd:r%u", - smb_direct_port); - if (IS_ERR(KSMBD_TRANS(t)->handler)) { - ret = PTR_ERR(KSMBD_TRANS(t)->handler); - pr_err("Can't start thread\n"); - goto out_err; - } - - return 0; -out_err: - free_transport(t); - return ret; -} - -static int smb_direct_listen_handler(struct rdma_cm_id *cm_id, - struct rdma_cm_event *event) -{ - switch (event->event) { - case RDMA_CM_EVENT_CONNECT_REQUEST: { - int ret = smb_direct_handle_connect_request(cm_id); - - if (ret) { - pr_err("Can't create transport: %d\n", ret); - return ret; - } - - ksmbd_debug(RDMA, "Received connection request. cm_id=%p\n", - cm_id); - break; - } - default: - pr_err("Unexpected listen event. cm_id=%p, event=%s (%d)\n", - cm_id, rdma_event_msg(event->event), event->event); - break; - } - return 0; -} - -static int smb_direct_listen(int port) -{ - int ret; - struct rdma_cm_id *cm_id; - struct sockaddr_in sin = { - .sin_family = AF_INET, - .sin_addr.s_addr = htonl(INADDR_ANY), - .sin_port = htons(port), - }; - - cm_id = rdma_create_id(&init_net, smb_direct_listen_handler, - &smb_direct_listener, RDMA_PS_TCP, IB_QPT_RC); - if (IS_ERR(cm_id)) { - pr_err("Can't create cm id: %ld\n", PTR_ERR(cm_id)); - return PTR_ERR(cm_id); - } - - ret = rdma_bind_addr(cm_id, (struct sockaddr *)&sin); - if (ret) { - pr_err("Can't bind: %d\n", ret); - goto err; - } - - smb_direct_listener.cm_id = cm_id; - - ret = rdma_listen(cm_id, 10); - if (ret) { - pr_err("Can't listen: %d\n", ret); - goto err; - } - return 0; -err: - smb_direct_listener.cm_id = NULL; - rdma_destroy_id(cm_id); - return ret; -} - -static int smb_direct_ib_client_add(struct ib_device *ib_dev) -{ - struct smb_direct_device *smb_dev; - - /* Set 5445 port if device type is iWARP(No IB) */ - if (ib_dev->node_type != RDMA_NODE_IB_CA) - smb_direct_port = SMB_DIRECT_PORT_IWARP; - - if (!ib_dev->ops.get_netdev || - !rdma_frwr_is_supported(&ib_dev->attrs)) - return 0; - - smb_dev = kzalloc(sizeof(*smb_dev), GFP_KERNEL); - if (!smb_dev) - return -ENOMEM; - smb_dev->ib_dev = ib_dev; - - write_lock(&smb_direct_device_lock); - list_add(&smb_dev->list, &smb_direct_device_list); - write_unlock(&smb_direct_device_lock); - - ksmbd_debug(RDMA, "ib device added: name %s\n", ib_dev->name); - return 0; -} - -static void smb_direct_ib_client_remove(struct ib_device *ib_dev, - void *client_data) -{ - struct smb_direct_device *smb_dev, *tmp; - - write_lock(&smb_direct_device_lock); - list_for_each_entry_safe(smb_dev, tmp, &smb_direct_device_list, list) { - if (smb_dev->ib_dev == ib_dev) { - list_del(&smb_dev->list); - kfree(smb_dev); - break; - } - } - write_unlock(&smb_direct_device_lock); -} - -static struct ib_client smb_direct_ib_client = { - .name = "ksmbd_smb_direct_ib", - .add = smb_direct_ib_client_add, - .remove = smb_direct_ib_client_remove, -}; - -int ksmbd_rdma_init(void) -{ - int ret; - - smb_direct_listener.cm_id = NULL; - - ret = ib_register_client(&smb_direct_ib_client); - if (ret) { - pr_err("failed to ib_register_client\n"); - return ret; - } - - /* When a client is running out of send credits, the credits are - * granted by the server's sending a packet using this queue. - * This avoids the situation that a clients cannot send packets - * for lack of credits - */ - smb_direct_wq = alloc_workqueue("ksmbd-smb_direct-wq", - WQ_HIGHPRI | WQ_MEM_RECLAIM, 0); - if (!smb_direct_wq) - return -ENOMEM; - - ret = smb_direct_listen(smb_direct_port); - if (ret) { - destroy_workqueue(smb_direct_wq); - smb_direct_wq = NULL; - pr_err("Can't listen: %d\n", ret); - return ret; - } - - ksmbd_debug(RDMA, "init RDMA listener. cm_id=%p\n", - smb_direct_listener.cm_id); - return 0; -} - -void ksmbd_rdma_destroy(void) -{ - if (!smb_direct_listener.cm_id) - return; - - ib_unregister_client(&smb_direct_ib_client); - rdma_destroy_id(smb_direct_listener.cm_id); - - smb_direct_listener.cm_id = NULL; - - if (smb_direct_wq) { - destroy_workqueue(smb_direct_wq); - smb_direct_wq = NULL; - } -} - -bool ksmbd_rdma_capable_netdev(struct net_device *netdev) -{ - struct smb_direct_device *smb_dev; - int i; - bool rdma_capable = false; - - read_lock(&smb_direct_device_lock); - list_for_each_entry(smb_dev, &smb_direct_device_list, list) { - for (i = 0; i < smb_dev->ib_dev->phys_port_cnt; i++) { - struct net_device *ndev; - - ndev = smb_dev->ib_dev->ops.get_netdev(smb_dev->ib_dev, - i + 1); - if (!ndev) - continue; - - if (ndev == netdev) { - dev_put(ndev); - rdma_capable = true; - goto out; - } - dev_put(ndev); - } - } -out: - read_unlock(&smb_direct_device_lock); - - if (rdma_capable == false) { - struct ib_device *ibdev; - - ibdev = ib_device_get_by_netdev(netdev, RDMA_DRIVER_UNKNOWN); - if (ibdev) { - if (rdma_frwr_is_supported(&ibdev->attrs)) - rdma_capable = true; - ib_device_put(ibdev); - } - } - - return rdma_capable; -} - -static struct ksmbd_transport_ops ksmbd_smb_direct_transport_ops = { - .prepare = smb_direct_prepare, - .disconnect = smb_direct_disconnect, - .shutdown = smb_direct_shutdown, - .writev = smb_direct_writev, - .read = smb_direct_read, - .rdma_read = smb_direct_rdma_read, - .rdma_write = smb_direct_rdma_write, -}; diff --git a/fs/ksmbd/transport_rdma.h b/fs/ksmbd/transport_rdma.h deleted file mode 100644 index 77aee4e5c9dcd..0000000000000 --- a/fs/ksmbd/transport_rdma.h +++ /dev/null @@ -1,69 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0-or-later */ -/* - * Copyright (C) 2017, Microsoft Corporation. - * Copyright (C) 2018, LG Electronics. - */ - -#ifndef __KSMBD_TRANSPORT_RDMA_H__ -#define __KSMBD_TRANSPORT_RDMA_H__ - -#define SMBD_DEFAULT_IOSIZE (8 * 1024 * 1024) -#define SMBD_MIN_IOSIZE (512 * 1024) -#define SMBD_MAX_IOSIZE (16 * 1024 * 1024) - -/* SMB DIRECT negotiation request packet [MS-SMBD] 2.2.1 */ -struct smb_direct_negotiate_req { - __le16 min_version; - __le16 max_version; - __le16 reserved; - __le16 credits_requested; - __le32 preferred_send_size; - __le32 max_receive_size; - __le32 max_fragmented_size; -} __packed; - -/* SMB DIRECT negotiation response packet [MS-SMBD] 2.2.2 */ -struct smb_direct_negotiate_resp { - __le16 min_version; - __le16 max_version; - __le16 negotiated_version; - __le16 reserved; - __le16 credits_requested; - __le16 credits_granted; - __le32 status; - __le32 max_readwrite_size; - __le32 preferred_send_size; - __le32 max_receive_size; - __le32 max_fragmented_size; -} __packed; - -#define SMB_DIRECT_RESPONSE_REQUESTED 0x0001 - -/* SMB DIRECT data transfer packet with payload [MS-SMBD] 2.2.3 */ -struct smb_direct_data_transfer { - __le16 credits_requested; - __le16 credits_granted; - __le16 flags; - __le16 reserved; - __le32 remaining_data_length; - __le32 data_offset; - __le32 data_length; - __le32 padding; - __u8 buffer[]; -} __packed; - -#ifdef CONFIG_SMB_SERVER_SMBDIRECT -int ksmbd_rdma_init(void); -void ksmbd_rdma_destroy(void); -bool ksmbd_rdma_capable_netdev(struct net_device *netdev); -void init_smbd_max_io_size(unsigned int sz); -unsigned int get_smbd_max_read_write_size(void); -#else -static inline int ksmbd_rdma_init(void) { return 0; } -static inline int ksmbd_rdma_destroy(void) { return 0; } -static inline bool ksmbd_rdma_capable_netdev(struct net_device *netdev) { return false; } -static inline void init_smbd_max_io_size(unsigned int sz) { } -static inline unsigned int get_smbd_max_read_write_size(void) { return 0; } -#endif - -#endif /* __KSMBD_TRANSPORT_RDMA_H__ */ diff --git a/fs/ksmbd/transport_tcp.c b/fs/ksmbd/transport_tcp.c deleted file mode 100644 index eff7a1d793f00..0000000000000 --- a/fs/ksmbd/transport_tcp.c +++ /dev/null @@ -1,649 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-or-later -/* - * Copyright (C) 2016 Namjae Jeon - * Copyright (C) 2018 Samsung Electronics Co., Ltd. - */ - -#include - -#include "smb_common.h" -#include "server.h" -#include "auth.h" -#include "connection.h" -#include "transport_tcp.h" - -#define IFACE_STATE_DOWN BIT(0) -#define IFACE_STATE_CONFIGURED BIT(1) - -static atomic_t active_num_conn; - -struct interface { - struct task_struct *ksmbd_kthread; - struct socket *ksmbd_socket; - struct list_head entry; - char *name; - struct mutex sock_release_lock; - int state; -}; - -static LIST_HEAD(iface_list); - -static int bind_additional_ifaces; - -struct tcp_transport { - struct ksmbd_transport transport; - struct socket *sock; - struct kvec *iov; - unsigned int nr_iov; -}; - -static struct ksmbd_transport_ops ksmbd_tcp_transport_ops; - -static void tcp_stop_kthread(struct task_struct *kthread); -static struct interface *alloc_iface(char *ifname); - -#define KSMBD_TRANS(t) (&(t)->transport) -#define TCP_TRANS(t) ((struct tcp_transport *)container_of(t, \ - struct tcp_transport, transport)) - -static inline void ksmbd_tcp_nodelay(struct socket *sock) -{ - tcp_sock_set_nodelay(sock->sk); -} - -static inline void ksmbd_tcp_reuseaddr(struct socket *sock) -{ - sock_set_reuseaddr(sock->sk); -} - -static inline void ksmbd_tcp_rcv_timeout(struct socket *sock, s64 secs) -{ - lock_sock(sock->sk); - if (secs && secs < MAX_SCHEDULE_TIMEOUT / HZ - 1) - sock->sk->sk_rcvtimeo = secs * HZ; - else - sock->sk->sk_rcvtimeo = MAX_SCHEDULE_TIMEOUT; - release_sock(sock->sk); -} - -static inline void ksmbd_tcp_snd_timeout(struct socket *sock, s64 secs) -{ - sock_set_sndtimeo(sock->sk, secs); -} - -static struct tcp_transport *alloc_transport(struct socket *client_sk) -{ - struct tcp_transport *t; - struct ksmbd_conn *conn; - - t = kzalloc(sizeof(*t), GFP_KERNEL); - if (!t) - return NULL; - t->sock = client_sk; - - conn = ksmbd_conn_alloc(); - if (!conn) { - kfree(t); - return NULL; - } - - conn->transport = KSMBD_TRANS(t); - KSMBD_TRANS(t)->conn = conn; - KSMBD_TRANS(t)->ops = &ksmbd_tcp_transport_ops; - return t; -} - -static void free_transport(struct tcp_transport *t) -{ - kernel_sock_shutdown(t->sock, SHUT_RDWR); - sock_release(t->sock); - t->sock = NULL; - - ksmbd_conn_free(KSMBD_TRANS(t)->conn); - kfree(t->iov); - kfree(t); -} - -/** - * kvec_array_init() - initialize a IO vector segment - * @new: IO vector to be initialized - * @iov: base IO vector - * @nr_segs: number of segments in base iov - * @bytes: total iovec length so far for read - * - * Return: Number of IO segments - */ -static unsigned int kvec_array_init(struct kvec *new, struct kvec *iov, - unsigned int nr_segs, size_t bytes) -{ - size_t base = 0; - - while (bytes || !iov->iov_len) { - int copy = min(bytes, iov->iov_len); - - bytes -= copy; - base += copy; - if (iov->iov_len == base) { - iov++; - nr_segs--; - base = 0; - } - } - - memcpy(new, iov, sizeof(*iov) * nr_segs); - new->iov_base += base; - new->iov_len -= base; - return nr_segs; -} - -/** - * get_conn_iovec() - get connection iovec for reading from socket - * @t: TCP transport instance - * @nr_segs: number of segments in iov - * - * Return: return existing or newly allocate iovec - */ -static struct kvec *get_conn_iovec(struct tcp_transport *t, unsigned int nr_segs) -{ - struct kvec *new_iov; - - if (t->iov && nr_segs <= t->nr_iov) - return t->iov; - - /* not big enough -- allocate a new one and release the old */ - new_iov = kmalloc_array(nr_segs, sizeof(*new_iov), GFP_KERNEL); - if (new_iov) { - kfree(t->iov); - t->iov = new_iov; - t->nr_iov = nr_segs; - } - return new_iov; -} - -static unsigned short ksmbd_tcp_get_port(const struct sockaddr *sa) -{ - switch (sa->sa_family) { - case AF_INET: - return ntohs(((struct sockaddr_in *)sa)->sin_port); - case AF_INET6: - return ntohs(((struct sockaddr_in6 *)sa)->sin6_port); - } - return 0; -} - -/** - * ksmbd_tcp_new_connection() - create a new tcp session on mount - * @client_sk: socket associated with new connection - * - * whenever a new connection is requested, create a conn thread - * (session thread) to handle new incoming smb requests from the connection - * - * Return: 0 on success, otherwise error - */ -static int ksmbd_tcp_new_connection(struct socket *client_sk) -{ - struct sockaddr *csin; - int rc = 0; - struct tcp_transport *t; - - t = alloc_transport(client_sk); - if (!t) { - sock_release(client_sk); - return -ENOMEM; - } - - csin = KSMBD_TCP_PEER_SOCKADDR(KSMBD_TRANS(t)->conn); - if (kernel_getpeername(client_sk, csin) < 0) { - pr_err("client ip resolution failed\n"); - rc = -EINVAL; - goto out_error; - } - - KSMBD_TRANS(t)->handler = kthread_run(ksmbd_conn_handler_loop, - KSMBD_TRANS(t)->conn, - "ksmbd:%u", - ksmbd_tcp_get_port(csin)); - if (IS_ERR(KSMBD_TRANS(t)->handler)) { - pr_err("cannot start conn thread\n"); - rc = PTR_ERR(KSMBD_TRANS(t)->handler); - free_transport(t); - } - return rc; - -out_error: - free_transport(t); - return rc; -} - -/** - * ksmbd_kthread_fn() - listen to new SMB connections and callback server - * @p: arguments to forker thread - * - * Return: 0 on success, error number otherwise - */ -static int ksmbd_kthread_fn(void *p) -{ - struct socket *client_sk = NULL; - struct interface *iface = (struct interface *)p; - int ret; - - while (!kthread_should_stop()) { - mutex_lock(&iface->sock_release_lock); - if (!iface->ksmbd_socket) { - mutex_unlock(&iface->sock_release_lock); - break; - } - ret = kernel_accept(iface->ksmbd_socket, &client_sk, - SOCK_NONBLOCK); - mutex_unlock(&iface->sock_release_lock); - if (ret) { - if (ret == -EAGAIN) - /* check for new connections every 100 msecs */ - schedule_timeout_interruptible(HZ / 10); - continue; - } - - if (server_conf.max_connections && - atomic_inc_return(&active_num_conn) >= server_conf.max_connections) { - pr_info_ratelimited("Limit the maximum number of connections(%u)\n", - atomic_read(&active_num_conn)); - atomic_dec(&active_num_conn); - sock_release(client_sk); - continue; - } - - ksmbd_debug(CONN, "connect success: accepted new connection\n"); - client_sk->sk->sk_rcvtimeo = KSMBD_TCP_RECV_TIMEOUT; - client_sk->sk->sk_sndtimeo = KSMBD_TCP_SEND_TIMEOUT; - - ksmbd_tcp_new_connection(client_sk); - } - - ksmbd_debug(CONN, "releasing socket\n"); - return 0; -} - -/** - * ksmbd_tcp_run_kthread() - start forker thread - * @iface: pointer to struct interface - * - * start forker thread(ksmbd/0) at module init time to listen - * on port 445 for new SMB connection requests. It creates per connection - * server threads(ksmbd/x) - * - * Return: 0 on success or error number - */ -static int ksmbd_tcp_run_kthread(struct interface *iface) -{ - int rc; - struct task_struct *kthread; - - kthread = kthread_run(ksmbd_kthread_fn, (void *)iface, "ksmbd-%s", - iface->name); - if (IS_ERR(kthread)) { - rc = PTR_ERR(kthread); - return rc; - } - iface->ksmbd_kthread = kthread; - - return 0; -} - -/** - * ksmbd_tcp_readv() - read data from socket in given iovec - * @t: TCP transport instance - * @iov_orig: base IO vector - * @nr_segs: number of segments in base iov - * @to_read: number of bytes to read from socket - * @max_retries: maximum retry count - * - * Return: on success return number of bytes read from socket, - * otherwise return error number - */ -static int ksmbd_tcp_readv(struct tcp_transport *t, struct kvec *iov_orig, - unsigned int nr_segs, unsigned int to_read, - int max_retries) -{ - int length = 0; - int total_read; - unsigned int segs; - struct msghdr ksmbd_msg; - struct kvec *iov; - struct ksmbd_conn *conn = KSMBD_TRANS(t)->conn; - - iov = get_conn_iovec(t, nr_segs); - if (!iov) - return -ENOMEM; - - ksmbd_msg.msg_control = NULL; - ksmbd_msg.msg_controllen = 0; - - for (total_read = 0; to_read; total_read += length, to_read -= length) { - try_to_freeze(); - - if (!ksmbd_conn_alive(conn)) { - total_read = -ESHUTDOWN; - break; - } - segs = kvec_array_init(iov, iov_orig, nr_segs, total_read); - - length = kernel_recvmsg(t->sock, &ksmbd_msg, - iov, segs, to_read, 0); - - if (length == -EINTR) { - total_read = -ESHUTDOWN; - break; - } else if (ksmbd_conn_need_reconnect(conn)) { - total_read = -EAGAIN; - break; - } else if (length == -ERESTARTSYS || length == -EAGAIN) { - /* - * If max_retries is negative, Allow unlimited - * retries to keep connection with inactive sessions. - */ - if (max_retries == 0) { - total_read = length; - break; - } else if (max_retries > 0) { - max_retries--; - } - - usleep_range(1000, 2000); - length = 0; - continue; - } else if (length <= 0) { - total_read = length; - break; - } - } - return total_read; -} - -/** - * ksmbd_tcp_read() - read data from socket in given buffer - * @t: TCP transport instance - * @buf: buffer to store read data from socket - * @to_read: number of bytes to read from socket - * - * Return: on success return number of bytes read from socket, - * otherwise return error number - */ -static int ksmbd_tcp_read(struct ksmbd_transport *t, char *buf, - unsigned int to_read, int max_retries) -{ - struct kvec iov; - - iov.iov_base = buf; - iov.iov_len = to_read; - - return ksmbd_tcp_readv(TCP_TRANS(t), &iov, 1, to_read, max_retries); -} - -static int ksmbd_tcp_writev(struct ksmbd_transport *t, struct kvec *iov, - int nvecs, int size, bool need_invalidate, - unsigned int remote_key) - -{ - struct msghdr smb_msg = {.msg_flags = MSG_NOSIGNAL}; - - return kernel_sendmsg(TCP_TRANS(t)->sock, &smb_msg, iov, nvecs, size); -} - -static void ksmbd_tcp_disconnect(struct ksmbd_transport *t) -{ - free_transport(TCP_TRANS(t)); - if (server_conf.max_connections) - atomic_dec(&active_num_conn); -} - -static void tcp_destroy_socket(struct socket *ksmbd_socket) -{ - int ret; - - if (!ksmbd_socket) - return; - - /* set zero to timeout */ - ksmbd_tcp_rcv_timeout(ksmbd_socket, 0); - ksmbd_tcp_snd_timeout(ksmbd_socket, 0); - - ret = kernel_sock_shutdown(ksmbd_socket, SHUT_RDWR); - if (ret) - pr_err("Failed to shutdown socket: %d\n", ret); - sock_release(ksmbd_socket); -} - -/** - * create_socket - create socket for ksmbd/0 - * - * Return: 0 on success, error number otherwise - */ -static int create_socket(struct interface *iface) -{ - int ret; - struct sockaddr_in6 sin6; - struct sockaddr_in sin; - struct socket *ksmbd_socket; - bool ipv4 = false; - - ret = sock_create(PF_INET6, SOCK_STREAM, IPPROTO_TCP, &ksmbd_socket); - if (ret) { - if (ret != -EAFNOSUPPORT) - pr_err("Can't create socket for ipv6, fallback to ipv4: %d\n", ret); - ret = sock_create(PF_INET, SOCK_STREAM, IPPROTO_TCP, - &ksmbd_socket); - if (ret) { - pr_err("Can't create socket for ipv4: %d\n", ret); - goto out_clear; - } - - sin.sin_family = PF_INET; - sin.sin_addr.s_addr = htonl(INADDR_ANY); - sin.sin_port = htons(server_conf.tcp_port); - ipv4 = true; - } else { - sin6.sin6_family = PF_INET6; - sin6.sin6_addr = in6addr_any; - sin6.sin6_port = htons(server_conf.tcp_port); - } - - ksmbd_tcp_nodelay(ksmbd_socket); - ksmbd_tcp_reuseaddr(ksmbd_socket); - - ret = sock_setsockopt(ksmbd_socket, - SOL_SOCKET, - SO_BINDTODEVICE, - KERNEL_SOCKPTR(iface->name), - strlen(iface->name)); - if (ret != -ENODEV && ret < 0) { - pr_err("Failed to set SO_BINDTODEVICE: %d\n", ret); - goto out_error; - } - - if (ipv4) - ret = kernel_bind(ksmbd_socket, (struct sockaddr *)&sin, - sizeof(sin)); - else - ret = kernel_bind(ksmbd_socket, (struct sockaddr *)&sin6, - sizeof(sin6)); - if (ret) { - pr_err("Failed to bind socket: %d\n", ret); - goto out_error; - } - - ksmbd_socket->sk->sk_rcvtimeo = KSMBD_TCP_RECV_TIMEOUT; - ksmbd_socket->sk->sk_sndtimeo = KSMBD_TCP_SEND_TIMEOUT; - - ret = kernel_listen(ksmbd_socket, KSMBD_SOCKET_BACKLOG); - if (ret) { - pr_err("Port listen() error: %d\n", ret); - goto out_error; - } - - iface->ksmbd_socket = ksmbd_socket; - ret = ksmbd_tcp_run_kthread(iface); - if (ret) { - pr_err("Can't start ksmbd main kthread: %d\n", ret); - goto out_error; - } - iface->state = IFACE_STATE_CONFIGURED; - - return 0; - -out_error: - tcp_destroy_socket(ksmbd_socket); -out_clear: - iface->ksmbd_socket = NULL; - return ret; -} - -static int ksmbd_netdev_event(struct notifier_block *nb, unsigned long event, - void *ptr) -{ - struct net_device *netdev = netdev_notifier_info_to_dev(ptr); - struct interface *iface; - int ret, found = 0; - - switch (event) { - case NETDEV_UP: - if (netif_is_bridge_port(netdev)) - return NOTIFY_OK; - - list_for_each_entry(iface, &iface_list, entry) { - if (!strcmp(iface->name, netdev->name)) { - found = 1; - if (iface->state != IFACE_STATE_DOWN) - break; - ret = create_socket(iface); - if (ret) - return NOTIFY_OK; - break; - } - } - if (!found && bind_additional_ifaces) { - iface = alloc_iface(kstrdup(netdev->name, GFP_KERNEL)); - if (!iface) - return NOTIFY_OK; - ret = create_socket(iface); - if (ret) - break; - } - break; - case NETDEV_DOWN: - list_for_each_entry(iface, &iface_list, entry) { - if (!strcmp(iface->name, netdev->name) && - iface->state == IFACE_STATE_CONFIGURED) { - tcp_stop_kthread(iface->ksmbd_kthread); - iface->ksmbd_kthread = NULL; - mutex_lock(&iface->sock_release_lock); - tcp_destroy_socket(iface->ksmbd_socket); - iface->ksmbd_socket = NULL; - mutex_unlock(&iface->sock_release_lock); - - iface->state = IFACE_STATE_DOWN; - break; - } - } - break; - } - - return NOTIFY_DONE; -} - -static struct notifier_block ksmbd_netdev_notifier = { - .notifier_call = ksmbd_netdev_event, -}; - -int ksmbd_tcp_init(void) -{ - register_netdevice_notifier(&ksmbd_netdev_notifier); - - return 0; -} - -static void tcp_stop_kthread(struct task_struct *kthread) -{ - int ret; - - if (!kthread) - return; - - ret = kthread_stop(kthread); - if (ret) - pr_err("failed to stop forker thread\n"); -} - -void ksmbd_tcp_destroy(void) -{ - struct interface *iface, *tmp; - - unregister_netdevice_notifier(&ksmbd_netdev_notifier); - - list_for_each_entry_safe(iface, tmp, &iface_list, entry) { - list_del(&iface->entry); - kfree(iface->name); - kfree(iface); - } -} - -static struct interface *alloc_iface(char *ifname) -{ - struct interface *iface; - - if (!ifname) - return NULL; - - iface = kzalloc(sizeof(struct interface), GFP_KERNEL); - if (!iface) { - kfree(ifname); - return NULL; - } - - iface->name = ifname; - iface->state = IFACE_STATE_DOWN; - list_add(&iface->entry, &iface_list); - mutex_init(&iface->sock_release_lock); - return iface; -} - -int ksmbd_tcp_set_interfaces(char *ifc_list, int ifc_list_sz) -{ - int sz = 0; - - if (!ifc_list_sz) { - struct net_device *netdev; - - rtnl_lock(); - for_each_netdev(&init_net, netdev) { - if (netif_is_bridge_port(netdev)) - continue; - if (!alloc_iface(kstrdup(netdev->name, GFP_KERNEL))) - return -ENOMEM; - } - rtnl_unlock(); - bind_additional_ifaces = 1; - return 0; - } - - while (ifc_list_sz > 0) { - if (!alloc_iface(kstrdup(ifc_list, GFP_KERNEL))) - return -ENOMEM; - - sz = strlen(ifc_list); - if (!sz) - break; - - ifc_list += sz + 1; - ifc_list_sz -= (sz + 1); - } - - bind_additional_ifaces = 0; - - return 0; -} - -static struct ksmbd_transport_ops ksmbd_tcp_transport_ops = { - .read = ksmbd_tcp_read, - .writev = ksmbd_tcp_writev, - .disconnect = ksmbd_tcp_disconnect, -}; diff --git a/fs/ksmbd/transport_tcp.h b/fs/ksmbd/transport_tcp.h deleted file mode 100644 index e338bebe322f1..0000000000000 --- a/fs/ksmbd/transport_tcp.h +++ /dev/null @@ -1,13 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0-or-later */ -/* - * Copyright (C) 2018 Samsung Electronics Co., Ltd. - */ - -#ifndef __KSMBD_TRANSPORT_TCP_H__ -#define __KSMBD_TRANSPORT_TCP_H__ - -int ksmbd_tcp_set_interfaces(char *ifc_list, int ifc_list_sz); -int ksmbd_tcp_init(void); -void ksmbd_tcp_destroy(void); - -#endif /* __KSMBD_TRANSPORT_TCP_H__ */ diff --git a/fs/ksmbd/unicode.c b/fs/ksmbd/unicode.c deleted file mode 100644 index a0db699ddafda..0000000000000 --- a/fs/ksmbd/unicode.c +++ /dev/null @@ -1,384 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-or-later -/* - * Some of the source code in this file came from fs/cifs/cifs_unicode.c - * - * Copyright (c) International Business Machines Corp., 2000,2009 - * Modified by Steve French (sfrench@us.ibm.com) - * Modified by Namjae Jeon (linkinjeon@kernel.org) - */ -#include -#include -#include -#include "glob.h" -#include "unicode.h" -#include "uniupr.h" -#include "smb_common.h" - -/* - * smb_utf16_bytes() - how long will a string be after conversion? - * @from: pointer to input string - * @maxbytes: don't go past this many bytes of input string - * @codepage: destination codepage - * - * Walk a utf16le string and return the number of bytes that the string will - * be after being converted to the given charset, not including any null - * termination required. Don't walk past maxbytes in the source buffer. - * - * Return: string length after conversion - */ -static int smb_utf16_bytes(const __le16 *from, int maxbytes, - const struct nls_table *codepage) -{ - int i; - int charlen, outlen = 0; - int maxwords = maxbytes / 2; - char tmp[NLS_MAX_CHARSET_SIZE]; - __u16 ftmp; - - for (i = 0; i < maxwords; i++) { - ftmp = get_unaligned_le16(&from[i]); - if (ftmp == 0) - break; - - charlen = codepage->uni2char(ftmp, tmp, NLS_MAX_CHARSET_SIZE); - if (charlen > 0) - outlen += charlen; - else - outlen++; - } - - return outlen; -} - -/* - * cifs_mapchar() - convert a host-endian char to proper char in codepage - * @target: where converted character should be copied - * @src_char: 2 byte host-endian source character - * @cp: codepage to which character should be converted - * @mapchar: should character be mapped according to mapchars mount option? - * - * This function handles the conversion of a single character. It is the - * responsibility of the caller to ensure that the target buffer is large - * enough to hold the result of the conversion (at least NLS_MAX_CHARSET_SIZE). - * - * Return: string length after conversion - */ -static int -cifs_mapchar(char *target, const __u16 src_char, const struct nls_table *cp, - bool mapchar) -{ - int len = 1; - - if (!mapchar) - goto cp_convert; - - /* - * BB: Cannot handle remapping UNI_SLASH until all the calls to - * build_path_from_dentry are modified, as they use slash as - * separator. - */ - switch (src_char) { - case UNI_COLON: - *target = ':'; - break; - case UNI_ASTERISK: - *target = '*'; - break; - case UNI_QUESTION: - *target = '?'; - break; - case UNI_PIPE: - *target = '|'; - break; - case UNI_GRTRTHAN: - *target = '>'; - break; - case UNI_LESSTHAN: - *target = '<'; - break; - default: - goto cp_convert; - } - -out: - return len; - -cp_convert: - len = cp->uni2char(src_char, target, NLS_MAX_CHARSET_SIZE); - if (len <= 0) { - *target = '?'; - len = 1; - } - - goto out; -} - -/* - * is_char_allowed() - check for valid character - * @ch: input character to be checked - * - * Return: 1 if char is allowed, otherwise 0 - */ -static inline int is_char_allowed(char *ch) -{ - /* check for control chars, wildcards etc. */ - if (!(*ch & 0x80) && - (*ch <= 0x1f || - *ch == '?' || *ch == '"' || *ch == '<' || - *ch == '>' || *ch == '|')) - return 0; - - return 1; -} - -/* - * smb_from_utf16() - convert utf16le string to local charset - * @to: destination buffer - * @from: source buffer - * @tolen: destination buffer size (in bytes) - * @fromlen: source buffer size (in bytes) - * @codepage: codepage to which characters should be converted - * @mapchar: should characters be remapped according to the mapchars option? - * - * Convert a little-endian utf16le string (as sent by the server) to a string - * in the provided codepage. The tolen and fromlen parameters are to ensure - * that the code doesn't walk off of the end of the buffer (which is always - * a danger if the alignment of the source buffer is off). The destination - * string is always properly null terminated and fits in the destination - * buffer. Returns the length of the destination string in bytes (including - * null terminator). - * - * Note that some windows versions actually send multiword UTF-16 characters - * instead of straight UTF16-2. The linux nls routines however aren't able to - * deal with those characters properly. In the event that we get some of - * those characters, they won't be translated properly. - * - * Return: string length after conversion - */ -static int smb_from_utf16(char *to, const __le16 *from, int tolen, int fromlen, - const struct nls_table *codepage, bool mapchar) -{ - int i, charlen, safelen; - int outlen = 0; - int nullsize = nls_nullsize(codepage); - int fromwords = fromlen / 2; - char tmp[NLS_MAX_CHARSET_SIZE]; - __u16 ftmp; - - /* - * because the chars can be of varying widths, we need to take care - * not to overflow the destination buffer when we get close to the - * end of it. Until we get to this offset, we don't need to check - * for overflow however. - */ - safelen = tolen - (NLS_MAX_CHARSET_SIZE + nullsize); - - for (i = 0; i < fromwords; i++) { - ftmp = get_unaligned_le16(&from[i]); - if (ftmp == 0) - break; - - /* - * check to see if converting this character might make the - * conversion bleed into the null terminator - */ - if (outlen >= safelen) { - charlen = cifs_mapchar(tmp, ftmp, codepage, mapchar); - if ((outlen + charlen) > (tolen - nullsize)) - break; - } - - /* put converted char into 'to' buffer */ - charlen = cifs_mapchar(&to[outlen], ftmp, codepage, mapchar); - outlen += charlen; - } - - /* properly null-terminate string */ - for (i = 0; i < nullsize; i++) - to[outlen++] = 0; - - return outlen; -} - -/* - * smb_strtoUTF16() - Convert character string to unicode string - * @to: destination buffer - * @from: source buffer - * @len: destination buffer size (in bytes) - * @codepage: codepage to which characters should be converted - * - * Return: string length after conversion - */ -int smb_strtoUTF16(__le16 *to, const char *from, int len, - const struct nls_table *codepage) -{ - int charlen; - int i; - wchar_t wchar_to; /* needed to quiet sparse */ - - /* special case for utf8 to handle no plane0 chars */ - if (!strcmp(codepage->charset, "utf8")) { - /* - * convert utf8 -> utf16, we assume we have enough space - * as caller should have assumed conversion does not overflow - * in destination len is length in wchar_t units (16bits) - */ - i = utf8s_to_utf16s(from, len, UTF16_LITTLE_ENDIAN, - (wchar_t *)to, len); - - /* if success terminate and exit */ - if (i >= 0) - goto success; - /* - * if fails fall back to UCS encoding as this - * function should not return negative values - * currently can fail only if source contains - * invalid encoded characters - */ - } - - for (i = 0; len > 0 && *from; i++, from += charlen, len -= charlen) { - charlen = codepage->char2uni(from, len, &wchar_to); - if (charlen < 1) { - /* A question mark */ - wchar_to = 0x003f; - charlen = 1; - } - put_unaligned_le16(wchar_to, &to[i]); - } - -success: - put_unaligned_le16(0, &to[i]); - return i; -} - -/* - * smb_strndup_from_utf16() - copy a string from wire format to the local - * codepage - * @src: source string - * @maxlen: don't walk past this many bytes in the source string - * @is_unicode: is this a unicode string? - * @codepage: destination codepage - * - * Take a string given by the server, convert it to the local codepage and - * put it in a new buffer. Returns a pointer to the new string or NULL on - * error. - * - * Return: destination string buffer or error ptr - */ -char *smb_strndup_from_utf16(const char *src, const int maxlen, - const bool is_unicode, - const struct nls_table *codepage) -{ - int len, ret; - char *dst; - - if (is_unicode) { - len = smb_utf16_bytes((__le16 *)src, maxlen, codepage); - len += nls_nullsize(codepage); - dst = kmalloc(len, GFP_KERNEL); - if (!dst) - return ERR_PTR(-ENOMEM); - ret = smb_from_utf16(dst, (__le16 *)src, len, maxlen, codepage, - false); - if (ret < 0) { - kfree(dst); - return ERR_PTR(-EINVAL); - } - } else { - len = strnlen(src, maxlen); - len++; - dst = kmalloc(len, GFP_KERNEL); - if (!dst) - return ERR_PTR(-ENOMEM); - strscpy(dst, src, len); - } - - return dst; -} - -/* - * Convert 16 bit Unicode pathname to wire format from string in current code - * page. Conversion may involve remapping up the six characters that are - * only legal in POSIX-like OS (if they are present in the string). Path - * names are little endian 16 bit Unicode on the wire - */ -/* - * smbConvertToUTF16() - convert string from local charset to utf16 - * @target: destination buffer - * @source: source buffer - * @srclen: source buffer size (in bytes) - * @cp: codepage to which characters should be converted - * @mapchar: should characters be remapped according to the mapchars option? - * - * Convert 16 bit Unicode pathname to wire format from string in current code - * page. Conversion may involve remapping up the six characters that are - * only legal in POSIX-like OS (if they are present in the string). Path - * names are little endian 16 bit Unicode on the wire - * - * Return: char length after conversion - */ -int smbConvertToUTF16(__le16 *target, const char *source, int srclen, - const struct nls_table *cp, int mapchars) -{ - int i, j, charlen; - char src_char; - __le16 dst_char; - wchar_t tmp; - - if (!mapchars) - return smb_strtoUTF16(target, source, srclen, cp); - - for (i = 0, j = 0; i < srclen; j++) { - src_char = source[i]; - charlen = 1; - switch (src_char) { - case 0: - put_unaligned(0, &target[j]); - return j; - case ':': - dst_char = cpu_to_le16(UNI_COLON); - break; - case '*': - dst_char = cpu_to_le16(UNI_ASTERISK); - break; - case '?': - dst_char = cpu_to_le16(UNI_QUESTION); - break; - case '<': - dst_char = cpu_to_le16(UNI_LESSTHAN); - break; - case '>': - dst_char = cpu_to_le16(UNI_GRTRTHAN); - break; - case '|': - dst_char = cpu_to_le16(UNI_PIPE); - break; - /* - * FIXME: We can not handle remapping backslash (UNI_SLASH) - * until all the calls to build_path_from_dentry are modified, - * as they use backslash as separator. - */ - default: - charlen = cp->char2uni(source + i, srclen - i, &tmp); - dst_char = cpu_to_le16(tmp); - - /* - * if no match, use question mark, which at least in - * some cases serves as wild card - */ - if (charlen < 1) { - dst_char = cpu_to_le16(0x003f); - charlen = 1; - } - } - /* - * character may take more than one byte in the source string, - * but will take exactly two bytes in the target string - */ - i += charlen; - put_unaligned(dst_char, &target[j]); - } - - return j; -} diff --git a/fs/ksmbd/unicode.h b/fs/ksmbd/unicode.h deleted file mode 100644 index 076f6034a7899..0000000000000 --- a/fs/ksmbd/unicode.h +++ /dev/null @@ -1,358 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0-or-later */ -/* - * Some of the source code in this file came from fs/cifs/cifs_unicode.c - * cifs_unicode: Unicode kernel case support - * - * Function: - * Convert a unicode character to upper or lower case using - * compressed tables. - * - * Copyright (c) International Business Machines Corp., 2000,2009 - * - * - * Notes: - * These APIs are based on the C library functions. The semantics - * should match the C functions but with expanded size operands. - * - * The upper/lower functions are based on a table created by mkupr. - * This is a compressed table of upper and lower case conversion. - * - */ -#ifndef _CIFS_UNICODE_H -#define _CIFS_UNICODE_H - -#include -#include -#include -#include - -#define UNIUPR_NOLOWER /* Example to not expand lower case tables */ - -/* - * Windows maps these to the user defined 16 bit Unicode range since they are - * reserved symbols (along with \ and /), otherwise illegal to store - * in filenames in NTFS - */ -#define UNI_ASTERISK ((__u16)('*' + 0xF000)) -#define UNI_QUESTION ((__u16)('?' + 0xF000)) -#define UNI_COLON ((__u16)(':' + 0xF000)) -#define UNI_GRTRTHAN ((__u16)('>' + 0xF000)) -#define UNI_LESSTHAN ((__u16)('<' + 0xF000)) -#define UNI_PIPE ((__u16)('|' + 0xF000)) -#define UNI_SLASH ((__u16)('\\' + 0xF000)) - -/* Just define what we want from uniupr.h. We don't want to define the tables - * in each source file. - */ -#ifndef UNICASERANGE_DEFINED -struct UniCaseRange { - wchar_t start; - wchar_t end; - signed char *table; -}; -#endif /* UNICASERANGE_DEFINED */ - -#ifndef UNIUPR_NOUPPER -extern signed char SmbUniUpperTable[512]; -extern const struct UniCaseRange SmbUniUpperRange[]; -#endif /* UNIUPR_NOUPPER */ - -#ifndef UNIUPR_NOLOWER -extern signed char CifsUniLowerTable[512]; -extern const struct UniCaseRange CifsUniLowerRange[]; -#endif /* UNIUPR_NOLOWER */ - -#ifdef __KERNEL__ -int smb_strtoUTF16(__le16 *to, const char *from, int len, - const struct nls_table *codepage); -char *smb_strndup_from_utf16(const char *src, const int maxlen, - const bool is_unicode, - const struct nls_table *codepage); -int smbConvertToUTF16(__le16 *target, const char *source, int srclen, - const struct nls_table *cp, int mapchars); -char *ksmbd_extract_sharename(struct unicode_map *um, const char *treename); -#endif - -/* - * UniStrcat: Concatenate the second string to the first - * - * Returns: - * Address of the first string - */ -static inline wchar_t *UniStrcat(wchar_t *ucs1, const wchar_t *ucs2) -{ - wchar_t *anchor = ucs1; /* save a pointer to start of ucs1 */ - - while (*ucs1++) - /*NULL*/; /* To end of first string */ - ucs1--; /* Return to the null */ - while ((*ucs1++ = *ucs2++)) - /*NULL*/; /* copy string 2 over */ - return anchor; -} - -/* - * UniStrchr: Find a character in a string - * - * Returns: - * Address of first occurrence of character in string - * or NULL if the character is not in the string - */ -static inline wchar_t *UniStrchr(const wchar_t *ucs, wchar_t uc) -{ - while ((*ucs != uc) && *ucs) - ucs++; - - if (*ucs == uc) - return (wchar_t *)ucs; - return NULL; -} - -/* - * UniStrcmp: Compare two strings - * - * Returns: - * < 0: First string is less than second - * = 0: Strings are equal - * > 0: First string is greater than second - */ -static inline int UniStrcmp(const wchar_t *ucs1, const wchar_t *ucs2) -{ - while ((*ucs1 == *ucs2) && *ucs1) { - ucs1++; - ucs2++; - } - return (int)*ucs1 - (int)*ucs2; -} - -/* - * UniStrcpy: Copy a string - */ -static inline wchar_t *UniStrcpy(wchar_t *ucs1, const wchar_t *ucs2) -{ - wchar_t *anchor = ucs1; /* save the start of result string */ - - while ((*ucs1++ = *ucs2++)) - /*NULL*/; - return anchor; -} - -/* - * UniStrlen: Return the length of a string (in 16 bit Unicode chars not bytes) - */ -static inline size_t UniStrlen(const wchar_t *ucs1) -{ - int i = 0; - - while (*ucs1++) - i++; - return i; -} - -/* - * UniStrnlen: Return the length (in 16 bit Unicode chars not bytes) of a - * string (length limited) - */ -static inline size_t UniStrnlen(const wchar_t *ucs1, int maxlen) -{ - int i = 0; - - while (*ucs1++) { - i++; - if (i >= maxlen) - break; - } - return i; -} - -/* - * UniStrncat: Concatenate length limited string - */ -static inline wchar_t *UniStrncat(wchar_t *ucs1, const wchar_t *ucs2, size_t n) -{ - wchar_t *anchor = ucs1; /* save pointer to string 1 */ - - while (*ucs1++) - /*NULL*/; - ucs1--; /* point to null terminator of s1 */ - while (n-- && (*ucs1 = *ucs2)) { /* copy s2 after s1 */ - ucs1++; - ucs2++; - } - *ucs1 = 0; /* Null terminate the result */ - return anchor; -} - -/* - * UniStrncmp: Compare length limited string - */ -static inline int UniStrncmp(const wchar_t *ucs1, const wchar_t *ucs2, size_t n) -{ - if (!n) - return 0; /* Null strings are equal */ - while ((*ucs1 == *ucs2) && *ucs1 && --n) { - ucs1++; - ucs2++; - } - return (int)*ucs1 - (int)*ucs2; -} - -/* - * UniStrncmp_le: Compare length limited string - native to little-endian - */ -static inline int -UniStrncmp_le(const wchar_t *ucs1, const wchar_t *ucs2, size_t n) -{ - if (!n) - return 0; /* Null strings are equal */ - while ((*ucs1 == __le16_to_cpu(*ucs2)) && *ucs1 && --n) { - ucs1++; - ucs2++; - } - return (int)*ucs1 - (int)__le16_to_cpu(*ucs2); -} - -/* - * UniStrncpy: Copy length limited string with pad - */ -static inline wchar_t *UniStrncpy(wchar_t *ucs1, const wchar_t *ucs2, size_t n) -{ - wchar_t *anchor = ucs1; - - while (n-- && *ucs2) /* Copy the strings */ - *ucs1++ = *ucs2++; - - n++; - while (n--) /* Pad with nulls */ - *ucs1++ = 0; - return anchor; -} - -/* - * UniStrncpy_le: Copy length limited string with pad to little-endian - */ -static inline wchar_t *UniStrncpy_le(wchar_t *ucs1, const wchar_t *ucs2, size_t n) -{ - wchar_t *anchor = ucs1; - - while (n-- && *ucs2) /* Copy the strings */ - *ucs1++ = __le16_to_cpu(*ucs2++); - - n++; - while (n--) /* Pad with nulls */ - *ucs1++ = 0; - return anchor; -} - -/* - * UniStrstr: Find a string in a string - * - * Returns: - * Address of first match found - * NULL if no matching string is found - */ -static inline wchar_t *UniStrstr(const wchar_t *ucs1, const wchar_t *ucs2) -{ - const wchar_t *anchor1 = ucs1; - const wchar_t *anchor2 = ucs2; - - while (*ucs1) { - if (*ucs1 == *ucs2) { - /* Partial match found */ - ucs1++; - ucs2++; - } else { - if (!*ucs2) /* Match found */ - return (wchar_t *)anchor1; - ucs1 = ++anchor1; /* No match */ - ucs2 = anchor2; - } - } - - if (!*ucs2) /* Both end together */ - return (wchar_t *)anchor1; /* Match found */ - return NULL; /* No match */ -} - -#ifndef UNIUPR_NOUPPER -/* - * UniToupper: Convert a unicode character to upper case - */ -static inline wchar_t UniToupper(register wchar_t uc) -{ - register const struct UniCaseRange *rp; - - if (uc < sizeof(SmbUniUpperTable)) { - /* Latin characters */ - return uc + SmbUniUpperTable[uc]; /* Use base tables */ - } - - rp = SmbUniUpperRange; /* Use range tables */ - while (rp->start) { - if (uc < rp->start) /* Before start of range */ - return uc; /* Uppercase = input */ - if (uc <= rp->end) /* In range */ - return uc + rp->table[uc - rp->start]; - rp++; /* Try next range */ - } - return uc; /* Past last range */ -} - -/* - * UniStrupr: Upper case a unicode string - */ -static inline __le16 *UniStrupr(register __le16 *upin) -{ - register __le16 *up; - - up = upin; - while (*up) { /* For all characters */ - *up = cpu_to_le16(UniToupper(le16_to_cpu(*up))); - up++; - } - return upin; /* Return input pointer */ -} -#endif /* UNIUPR_NOUPPER */ - -#ifndef UNIUPR_NOLOWER -/* - * UniTolower: Convert a unicode character to lower case - */ -static inline wchar_t UniTolower(register wchar_t uc) -{ - register const struct UniCaseRange *rp; - - if (uc < sizeof(CifsUniLowerTable)) { - /* Latin characters */ - return uc + CifsUniLowerTable[uc]; /* Use base tables */ - } - - rp = CifsUniLowerRange; /* Use range tables */ - while (rp->start) { - if (uc < rp->start) /* Before start of range */ - return uc; /* Uppercase = input */ - if (uc <= rp->end) /* In range */ - return uc + rp->table[uc - rp->start]; - rp++; /* Try next range */ - } - return uc; /* Past last range */ -} - -/* - * UniStrlwr: Lower case a unicode string - */ -static inline wchar_t *UniStrlwr(register wchar_t *upin) -{ - register wchar_t *up; - - up = upin; - while (*up) { /* For all characters */ - *up = UniTolower(*up); - up++; - } - return upin; /* Return input pointer */ -} - -#endif - -#endif /* _CIFS_UNICODE_H */ diff --git a/fs/ksmbd/uniupr.h b/fs/ksmbd/uniupr.h deleted file mode 100644 index 26583b776897b..0000000000000 --- a/fs/ksmbd/uniupr.h +++ /dev/null @@ -1,268 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0-or-later */ -/* - * Some of the source code in this file came from fs/cifs/uniupr.h - * Copyright (c) International Business Machines Corp., 2000,2002 - * - * uniupr.h - Unicode compressed case ranges - * - */ -#ifndef __KSMBD_UNIUPR_H -#define __KSMBD_UNIUPR_H - -#ifndef UNIUPR_NOUPPER -/* - * Latin upper case - */ -signed char SmbUniUpperTable[512] = { - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 000-00f */ - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 010-01f */ - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 020-02f */ - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 030-03f */ - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 040-04f */ - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 050-05f */ - 0, -32, -32, -32, -32, -32, -32, -32, -32, -32, -32, - -32, -32, -32, -32, -32, /* 060-06f */ - -32, -32, -32, -32, -32, -32, -32, -32, -32, -32, - -32, 0, 0, 0, 0, 0, /* 070-07f */ - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 080-08f */ - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 090-09f */ - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 0a0-0af */ - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 0b0-0bf */ - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 0c0-0cf */ - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 0d0-0df */ - -32, -32, -32, -32, -32, -32, -32, -32, -32, -32, - -32, -32, -32, -32, -32, -32, /* 0e0-0ef */ - -32, -32, -32, -32, -32, -32, -32, 0, -32, -32, - -32, -32, -32, -32, -32, 121, /* 0f0-0ff */ - 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, /* 100-10f */ - 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, /* 110-11f */ - 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, /* 120-12f */ - 0, 0, 0, -1, 0, -1, 0, -1, 0, 0, -1, 0, -1, 0, -1, 0, /* 130-13f */ - -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, 0, -1, 0, -1, 0, -1, /* 140-14f */ - 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, /* 150-15f */ - 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, /* 160-16f */ - 0, -1, 0, -1, 0, -1, 0, -1, 0, 0, -1, 0, -1, 0, -1, 0, /* 170-17f */ - 0, 0, 0, -1, 0, -1, 0, 0, -1, 0, 0, 0, -1, 0, 0, 0, /* 180-18f */ - 0, 0, -1, 0, 0, 0, 0, 0, 0, -1, 0, 0, 0, 0, 0, 0, /* 190-19f */ - 0, -1, 0, -1, 0, -1, 0, 0, -1, 0, 0, 0, 0, -1, 0, 0, /* 1a0-1af */ - -1, 0, 0, 0, -1, 0, -1, 0, 0, -1, 0, 0, 0, -1, 0, 0, /* 1b0-1bf */ - 0, 0, 0, 0, 0, -1, -2, 0, -1, -2, 0, -1, -2, 0, -1, 0, /* 1c0-1cf */ - -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, -79, 0, -1, /* 1d0-1df */ - 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, /* 1e0-1ef */ - 0, 0, -1, -2, 0, -1, 0, 0, 0, -1, 0, -1, 0, -1, 0, -1, /* 1f0-1ff */ -}; - -/* Upper case range - Greek */ -static signed char UniCaseRangeU03a0[47] = { - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -38, -37, -37, -37, /* 3a0-3af */ - 0, -32, -32, -32, -32, -32, -32, -32, -32, -32, -32, -32, - -32, -32, -32, -32, /* 3b0-3bf */ - -32, -32, -31, -32, -32, -32, -32, -32, -32, -32, -32, -32, -64, - -63, -63, -}; - -/* Upper case range - Cyrillic */ -static signed char UniCaseRangeU0430[48] = { - -32, -32, -32, -32, -32, -32, -32, -32, -32, -32, -32, -32, - -32, -32, -32, -32, /* 430-43f */ - -32, -32, -32, -32, -32, -32, -32, -32, -32, -32, -32, -32, - -32, -32, -32, -32, /* 440-44f */ - 0, -80, -80, -80, -80, -80, -80, -80, -80, -80, -80, - -80, -80, 0, -80, -80, /* 450-45f */ -}; - -/* Upper case range - Extended cyrillic */ -static signed char UniCaseRangeU0490[61] = { - 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, /* 490-49f */ - 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, /* 4a0-4af */ - 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, /* 4b0-4bf */ - 0, 0, -1, 0, -1, 0, 0, 0, -1, 0, 0, 0, -1, -}; - -/* Upper case range - Extended latin and greek */ -static signed char UniCaseRangeU1e00[509] = { - 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, /* 1e00-1e0f */ - 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, /* 1e10-1e1f */ - 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, /* 1e20-1e2f */ - 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, /* 1e30-1e3f */ - 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, /* 1e40-1e4f */ - 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, /* 1e50-1e5f */ - 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, /* 1e60-1e6f */ - 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, /* 1e70-1e7f */ - 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, /* 1e80-1e8f */ - 0, -1, 0, -1, 0, -1, 0, 0, 0, 0, 0, -59, 0, -1, 0, -1, /* 1e90-1e9f */ - 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, /* 1ea0-1eaf */ - 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, /* 1eb0-1ebf */ - 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, /* 1ec0-1ecf */ - 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, /* 1ed0-1edf */ - 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, /* 1ee0-1eef */ - 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, 0, 0, 0, 0, 0, /* 1ef0-1eff */ - 8, 8, 8, 8, 8, 8, 8, 8, 0, 0, 0, 0, 0, 0, 0, 0, /* 1f00-1f0f */ - 8, 8, 8, 8, 8, 8, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 1f10-1f1f */ - 8, 8, 8, 8, 8, 8, 8, 8, 0, 0, 0, 0, 0, 0, 0, 0, /* 1f20-1f2f */ - 8, 8, 8, 8, 8, 8, 8, 8, 0, 0, 0, 0, 0, 0, 0, 0, /* 1f30-1f3f */ - 8, 8, 8, 8, 8, 8, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 1f40-1f4f */ - 0, 8, 0, 8, 0, 8, 0, 8, 0, 0, 0, 0, 0, 0, 0, 0, /* 1f50-1f5f */ - 8, 8, 8, 8, 8, 8, 8, 8, 0, 0, 0, 0, 0, 0, 0, 0, /* 1f60-1f6f */ - 74, 74, 86, 86, 86, 86, 100, 100, 0, 0, 112, 112, - 126, 126, 0, 0, /* 1f70-1f7f */ - 8, 8, 8, 8, 8, 8, 8, 8, 0, 0, 0, 0, 0, 0, 0, 0, /* 1f80-1f8f */ - 8, 8, 8, 8, 8, 8, 8, 8, 0, 0, 0, 0, 0, 0, 0, 0, /* 1f90-1f9f */ - 8, 8, 8, 8, 8, 8, 8, 8, 0, 0, 0, 0, 0, 0, 0, 0, /* 1fa0-1faf */ - 8, 8, 0, 9, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 1fb0-1fbf */ - 0, 0, 0, 9, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 1fc0-1fcf */ - 8, 8, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 1fd0-1fdf */ - 8, 8, 0, 0, 0, 7, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 1fe0-1fef */ - 0, 0, 0, 9, 0, 0, 0, 0, 0, 0, 0, 0, 0, -}; - -/* Upper case range - Wide latin */ -static signed char UniCaseRangeUff40[27] = { - 0, -32, -32, -32, -32, -32, -32, -32, -32, -32, -32, - -32, -32, -32, -32, -32, /* ff40-ff4f */ - -32, -32, -32, -32, -32, -32, -32, -32, -32, -32, -32, -}; - -/* - * Upper Case Range - */ -const struct UniCaseRange SmbUniUpperRange[] = { - {0x03a0, 0x03ce, UniCaseRangeU03a0}, - {0x0430, 0x045f, UniCaseRangeU0430}, - {0x0490, 0x04cc, UniCaseRangeU0490}, - {0x1e00, 0x1ffc, UniCaseRangeU1e00}, - {0xff40, 0xff5a, UniCaseRangeUff40}, - {0} -}; -#endif - -#ifndef UNIUPR_NOLOWER -/* - * Latin lower case - */ -signed char CifsUniLowerTable[512] = { - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 000-00f */ - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 010-01f */ - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 020-02f */ - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 030-03f */ - 0, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, - 32, 32, 32, /* 040-04f */ - 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 0, 0, - 0, 0, 0, /* 050-05f */ - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 060-06f */ - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 070-07f */ - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 080-08f */ - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 090-09f */ - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 0a0-0af */ - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 0b0-0bf */ - 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, - 32, 32, 32, 32, /* 0c0-0cf */ - 32, 32, 32, 32, 32, 32, 32, 0, 32, 32, 32, 32, - 32, 32, 32, 0, /* 0d0-0df */ - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 0e0-0ef */ - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 0f0-0ff */ - 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, /* 100-10f */ - 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, /* 110-11f */ - 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, /* 120-12f */ - 0, 0, 1, 0, 1, 0, 1, 0, 0, 1, 0, 1, 0, 1, 0, 1, /* 130-13f */ - 0, 1, 0, 1, 0, 1, 0, 1, 0, 0, 1, 0, 1, 0, 1, 0, /* 140-14f */ - 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, /* 150-15f */ - 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, /* 160-16f */ - 1, 0, 1, 0, 1, 0, 1, 0, -121, 1, 0, 1, 0, 1, 0, - 0, /* 170-17f */ - 0, 0, 1, 0, 1, 0, 0, 1, 0, 0, 0, 1, 0, 0, 79, - 0, /* 180-18f */ - 0, 1, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, /* 190-19f */ - 1, 0, 1, 0, 1, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 1, /* 1a0-1af */ - 0, 0, 0, 1, 0, 1, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, /* 1b0-1bf */ - 0, 0, 0, 0, 2, 1, 0, 2, 1, 0, 2, 1, 0, 1, 0, 1, /* 1c0-1cf */ - 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 0, 1, 0, /* 1d0-1df */ - 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, /* 1e0-1ef */ - 0, 2, 1, 0, 1, 0, 0, 0, 1, 0, 1, 0, 1, 0, 1, 0, /* 1f0-1ff */ -}; - -/* Lower case range - Greek */ -static signed char UniCaseRangeL0380[44] = { - 0, 0, 0, 0, 0, 0, 38, 0, 37, 37, 37, 0, 64, 0, 63, 63, /* 380-38f */ - 0, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, - 32, 32, 32, /* 390-39f */ - 32, 32, 0, 32, 32, 32, 32, 32, 32, 32, 32, 32, -}; - -/* Lower case range - Cyrillic */ -static signed char UniCaseRangeL0400[48] = { - 0, 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, - 0, 80, 80, /* 400-40f */ - 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, - 32, 32, 32, /* 410-41f */ - 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, - 32, 32, 32, /* 420-42f */ -}; - -/* Lower case range - Extended cyrillic */ -static signed char UniCaseRangeL0490[60] = { - 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, /* 490-49f */ - 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, /* 4a0-4af */ - 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, /* 4b0-4bf */ - 0, 1, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, -}; - -/* Lower case range - Extended latin and greek */ -static signed char UniCaseRangeL1e00[504] = { - 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, /* 1e00-1e0f */ - 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, /* 1e10-1e1f */ - 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, /* 1e20-1e2f */ - 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, /* 1e30-1e3f */ - 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, /* 1e40-1e4f */ - 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, /* 1e50-1e5f */ - 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, /* 1e60-1e6f */ - 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, /* 1e70-1e7f */ - 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, /* 1e80-1e8f */ - 1, 0, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 1, 0, 1, 0, /* 1e90-1e9f */ - 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, /* 1ea0-1eaf */ - 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, /* 1eb0-1ebf */ - 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, /* 1ec0-1ecf */ - 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, /* 1ed0-1edf */ - 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, /* 1ee0-1eef */ - 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, /* 1ef0-1eff */ - 0, 0, 0, 0, 0, 0, 0, 0, -8, -8, -8, -8, -8, -8, -8, -8, /* 1f00-1f0f */ - 0, 0, 0, 0, 0, 0, 0, 0, -8, -8, -8, -8, -8, -8, 0, 0, /* 1f10-1f1f */ - 0, 0, 0, 0, 0, 0, 0, 0, -8, -8, -8, -8, -8, -8, -8, -8, /* 1f20-1f2f */ - 0, 0, 0, 0, 0, 0, 0, 0, -8, -8, -8, -8, -8, -8, -8, -8, /* 1f30-1f3f */ - 0, 0, 0, 0, 0, 0, 0, 0, -8, -8, -8, -8, -8, -8, 0, 0, /* 1f40-1f4f */ - 0, 0, 0, 0, 0, 0, 0, 0, 0, -8, 0, -8, 0, -8, 0, -8, /* 1f50-1f5f */ - 0, 0, 0, 0, 0, 0, 0, 0, -8, -8, -8, -8, -8, -8, -8, -8, /* 1f60-1f6f */ - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 1f70-1f7f */ - 0, 0, 0, 0, 0, 0, 0, 0, -8, -8, -8, -8, -8, -8, -8, -8, /* 1f80-1f8f */ - 0, 0, 0, 0, 0, 0, 0, 0, -8, -8, -8, -8, -8, -8, -8, -8, /* 1f90-1f9f */ - 0, 0, 0, 0, 0, 0, 0, 0, -8, -8, -8, -8, -8, -8, -8, -8, /* 1fa0-1faf */ - 0, 0, 0, 0, 0, 0, 0, 0, -8, -8, -74, -74, -9, 0, 0, 0, /* 1fb0-1fbf */ - 0, 0, 0, 0, 0, 0, 0, 0, -86, -86, -86, -86, -9, 0, - 0, 0, /* 1fc0-1fcf */ - 0, 0, 0, 0, 0, 0, 0, 0, -8, -8, -100, -100, 0, 0, 0, 0, /* 1fd0-1fdf */ - 0, 0, 0, 0, 0, 0, 0, 0, -8, -8, -112, -112, -7, 0, - 0, 0, /* 1fe0-1fef */ - 0, 0, 0, 0, 0, 0, 0, 0, -}; - -/* Lower case range - Wide latin */ -static signed char UniCaseRangeLff20[27] = { - 0, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, - 32, /* ff20-ff2f */ - 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, -}; - -/* - * Lower Case Range - */ -const struct UniCaseRange CifsUniLowerRange[] = { - {0x0380, 0x03ab, UniCaseRangeL0380}, - {0x0400, 0x042f, UniCaseRangeL0400}, - {0x0490, 0x04cb, UniCaseRangeL0490}, - {0x1e00, 0x1ff7, UniCaseRangeL1e00}, - {0xff20, 0xff3a, UniCaseRangeLff20}, - {0} -}; -#endif - -#endif /* __KSMBD_UNIUPR_H */ diff --git a/fs/ksmbd/vfs.c b/fs/ksmbd/vfs.c deleted file mode 100644 index 94b8ed4ef8707..0000000000000 --- a/fs/ksmbd/vfs.c +++ /dev/null @@ -1,1911 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-or-later -/* - * Copyright (C) 2016 Namjae Jeon - * Copyright (C) 2018 Samsung Electronics Co., Ltd. - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include "../internal.h" /* for vfs_path_lookup */ - -#include "glob.h" -#include "oplock.h" -#include "connection.h" -#include "vfs.h" -#include "vfs_cache.h" -#include "smbacl.h" -#include "ndr.h" -#include "auth.h" -#include "misc.h" - -#include "smb_common.h" -#include "mgmt/share_config.h" -#include "mgmt/tree_connect.h" -#include "mgmt/user_session.h" -#include "mgmt/user_config.h" - -static char *extract_last_component(char *path) -{ - char *p = strrchr(path, '/'); - - if (p && p[1] != '\0') { - *p = '\0'; - p++; - } else { - p = NULL; - } - return p; -} - -static void ksmbd_vfs_inherit_owner(struct ksmbd_work *work, - struct inode *parent_inode, - struct inode *inode) -{ - if (!test_share_config_flag(work->tcon->share_conf, - KSMBD_SHARE_FLAG_INHERIT_OWNER)) - return; - - i_uid_write(inode, i_uid_read(parent_inode)); -} - -/** - * ksmbd_vfs_lock_parent() - lock parent dentry if it is stable - * - * the parent dentry got by dget_parent or @parent could be - * unstable, we try to lock a parent inode and lookup the - * child dentry again. - * - * the reference count of @parent isn't incremented. - */ -int ksmbd_vfs_lock_parent(struct user_namespace *user_ns, struct dentry *parent, - struct dentry *child) -{ - struct dentry *dentry; - int ret = 0; - - inode_lock_nested(d_inode(parent), I_MUTEX_PARENT); - dentry = lookup_one(user_ns, child->d_name.name, parent, - child->d_name.len); - if (IS_ERR(dentry)) { - ret = PTR_ERR(dentry); - goto out_err; - } - - if (dentry != child) { - ret = -ESTALE; - dput(dentry); - goto out_err; - } - - dput(dentry); - return 0; -out_err: - inode_unlock(d_inode(parent)); - return ret; -} - -int ksmbd_vfs_may_delete(struct user_namespace *user_ns, - struct dentry *dentry) -{ - struct dentry *parent; - int ret; - - parent = dget_parent(dentry); - ret = ksmbd_vfs_lock_parent(user_ns, parent, dentry); - if (ret) { - dput(parent); - return ret; - } - - ret = inode_permission(user_ns, d_inode(parent), - MAY_EXEC | MAY_WRITE); - - inode_unlock(d_inode(parent)); - dput(parent); - return ret; -} - -int ksmbd_vfs_query_maximal_access(struct user_namespace *user_ns, - struct dentry *dentry, __le32 *daccess) -{ - struct dentry *parent; - int ret = 0; - - *daccess = cpu_to_le32(FILE_READ_ATTRIBUTES | READ_CONTROL); - - if (!inode_permission(user_ns, d_inode(dentry), MAY_OPEN | MAY_WRITE)) - *daccess |= cpu_to_le32(WRITE_DAC | WRITE_OWNER | SYNCHRONIZE | - FILE_WRITE_DATA | FILE_APPEND_DATA | - FILE_WRITE_EA | FILE_WRITE_ATTRIBUTES | - FILE_DELETE_CHILD); - - if (!inode_permission(user_ns, d_inode(dentry), MAY_OPEN | MAY_READ)) - *daccess |= FILE_READ_DATA_LE | FILE_READ_EA_LE; - - if (!inode_permission(user_ns, d_inode(dentry), MAY_OPEN | MAY_EXEC)) - *daccess |= FILE_EXECUTE_LE; - - parent = dget_parent(dentry); - ret = ksmbd_vfs_lock_parent(user_ns, parent, dentry); - if (ret) { - dput(parent); - return ret; - } - - if (!inode_permission(user_ns, d_inode(parent), MAY_EXEC | MAY_WRITE)) - *daccess |= FILE_DELETE_LE; - - inode_unlock(d_inode(parent)); - dput(parent); - return ret; -} - -/** - * ksmbd_vfs_create() - vfs helper for smb create file - * @work: work - * @name: file name that is relative to share - * @mode: file create mode - * - * Return: 0 on success, otherwise error - */ -int ksmbd_vfs_create(struct ksmbd_work *work, const char *name, umode_t mode) -{ - struct path path; - struct dentry *dentry; - int err; - - dentry = ksmbd_vfs_kern_path_create(work, name, - LOOKUP_NO_SYMLINKS, &path); - if (IS_ERR(dentry)) { - err = PTR_ERR(dentry); - if (err != -ENOENT) - pr_err("path create failed for %s, err %d\n", - name, err); - return err; - } - - mode |= S_IFREG; - err = vfs_create(mnt_user_ns(path.mnt), d_inode(path.dentry), - dentry, mode, true); - if (!err) { - ksmbd_vfs_inherit_owner(work, d_inode(path.dentry), - d_inode(dentry)); - } else { - pr_err("File(%s): creation failed (err:%d)\n", name, err); - } - done_path_create(&path, dentry); - return err; -} - -/** - * ksmbd_vfs_mkdir() - vfs helper for smb create directory - * @work: work - * @name: directory name that is relative to share - * @mode: directory create mode - * - * Return: 0 on success, otherwise error - */ -int ksmbd_vfs_mkdir(struct ksmbd_work *work, const char *name, umode_t mode) -{ - struct user_namespace *user_ns; - struct path path; - struct dentry *dentry; - int err; - - dentry = ksmbd_vfs_kern_path_create(work, name, - LOOKUP_NO_SYMLINKS | LOOKUP_DIRECTORY, - &path); - if (IS_ERR(dentry)) { - err = PTR_ERR(dentry); - if (err != -EEXIST) - ksmbd_debug(VFS, "path create failed for %s, err %d\n", - name, err); - return err; - } - - user_ns = mnt_user_ns(path.mnt); - mode |= S_IFDIR; - err = vfs_mkdir(user_ns, d_inode(path.dentry), dentry, mode); - if (err) { - goto out; - } else if (d_unhashed(dentry)) { - struct dentry *d; - - d = lookup_one(user_ns, dentry->d_name.name, dentry->d_parent, - dentry->d_name.len); - if (IS_ERR(d)) { - err = PTR_ERR(d); - goto out; - } - if (unlikely(d_is_negative(d))) { - dput(d); - err = -ENOENT; - goto out; - } - - ksmbd_vfs_inherit_owner(work, d_inode(path.dentry), d_inode(d)); - dput(d); - } -out: - done_path_create(&path, dentry); - if (err) - pr_err("mkdir(%s): creation failed (err:%d)\n", name, err); - return err; -} - -static ssize_t ksmbd_vfs_getcasexattr(struct user_namespace *user_ns, - struct dentry *dentry, char *attr_name, - int attr_name_len, char **attr_value) -{ - char *name, *xattr_list = NULL; - ssize_t value_len = -ENOENT, xattr_list_len; - - xattr_list_len = ksmbd_vfs_listxattr(dentry, &xattr_list); - if (xattr_list_len <= 0) - goto out; - - for (name = xattr_list; name - xattr_list < xattr_list_len; - name += strlen(name) + 1) { - ksmbd_debug(VFS, "%s, len %zd\n", name, strlen(name)); - if (strncasecmp(attr_name, name, attr_name_len)) - continue; - - value_len = ksmbd_vfs_getxattr(user_ns, - dentry, - name, - attr_value); - if (value_len < 0) - pr_err("failed to get xattr in file\n"); - break; - } - -out: - kvfree(xattr_list); - return value_len; -} - -static int ksmbd_vfs_stream_read(struct ksmbd_file *fp, char *buf, loff_t *pos, - size_t count) -{ - ssize_t v_len; - char *stream_buf = NULL; - - ksmbd_debug(VFS, "read stream data pos : %llu, count : %zd\n", - *pos, count); - - v_len = ksmbd_vfs_getcasexattr(file_mnt_user_ns(fp->filp), - fp->filp->f_path.dentry, - fp->stream.name, - fp->stream.size, - &stream_buf); - if ((int)v_len <= 0) - return (int)v_len; - - if (v_len <= *pos) { - count = -EINVAL; - goto free_buf; - } - - if (v_len - *pos < count) - count = v_len - *pos; - - memcpy(buf, &stream_buf[*pos], count); - -free_buf: - kvfree(stream_buf); - return count; -} - -/** - * check_lock_range() - vfs helper for smb byte range file locking - * @filp: the file to apply the lock to - * @start: lock start byte offset - * @end: lock end byte offset - * @type: byte range type read/write - * - * Return: 0 on success, otherwise error - */ -static int check_lock_range(struct file *filp, loff_t start, loff_t end, - unsigned char type) -{ - struct file_lock *flock; - struct file_lock_context *ctx = file_inode(filp)->i_flctx; - int error = 0; - - if (!ctx || list_empty_careful(&ctx->flc_posix)) - return 0; - - spin_lock(&ctx->flc_lock); - list_for_each_entry(flock, &ctx->flc_posix, fl_list) { - /* check conflict locks */ - if (flock->fl_end >= start && end >= flock->fl_start) { - if (flock->fl_type == F_RDLCK) { - if (type == WRITE) { - pr_err("not allow write by shared lock\n"); - error = 1; - goto out; - } - } else if (flock->fl_type == F_WRLCK) { - /* check owner in lock */ - if (flock->fl_file != filp) { - error = 1; - pr_err("not allow rw access by exclusive lock from other opens\n"); - goto out; - } - } - } - } -out: - spin_unlock(&ctx->flc_lock); - return error; -} - -/** - * ksmbd_vfs_read() - vfs helper for smb file read - * @work: smb work - * @fid: file id of open file - * @count: read byte count - * @pos: file pos - * - * Return: number of read bytes on success, otherwise error - */ -int ksmbd_vfs_read(struct ksmbd_work *work, struct ksmbd_file *fp, size_t count, - loff_t *pos) -{ - struct file *filp = fp->filp; - ssize_t nbytes = 0; - char *rbuf = work->aux_payload_buf; - struct inode *inode = file_inode(filp); - - if (S_ISDIR(inode->i_mode)) - return -EISDIR; - - if (unlikely(count == 0)) - return 0; - - if (work->conn->connection_type) { - if (!(fp->daccess & (FILE_READ_DATA_LE | FILE_EXECUTE_LE))) { - pr_err("no right to read(%pD)\n", fp->filp); - return -EACCES; - } - } - - if (ksmbd_stream_fd(fp)) - return ksmbd_vfs_stream_read(fp, rbuf, pos, count); - - if (!work->tcon->posix_extensions) { - int ret; - - ret = check_lock_range(filp, *pos, *pos + count - 1, READ); - if (ret) { - pr_err("unable to read due to lock\n"); - return -EAGAIN; - } - } - - nbytes = kernel_read(filp, rbuf, count, pos); - if (nbytes < 0) { - pr_err("smb read failed, err = %zd\n", nbytes); - return nbytes; - } - - filp->f_pos = *pos; - return nbytes; -} - -static int ksmbd_vfs_stream_write(struct ksmbd_file *fp, char *buf, loff_t *pos, - size_t count) -{ - char *stream_buf = NULL, *wbuf; - struct user_namespace *user_ns = file_mnt_user_ns(fp->filp); - size_t size, v_len; - int err = 0; - - ksmbd_debug(VFS, "write stream data pos : %llu, count : %zd\n", - *pos, count); - - size = *pos + count; - if (size > XATTR_SIZE_MAX) { - size = XATTR_SIZE_MAX; - count = (*pos + count) - XATTR_SIZE_MAX; - } - - v_len = ksmbd_vfs_getcasexattr(user_ns, - fp->filp->f_path.dentry, - fp->stream.name, - fp->stream.size, - &stream_buf); - if ((int)v_len < 0) { - pr_err("not found stream in xattr : %zd\n", v_len); - err = (int)v_len; - goto out; - } - - if (v_len < size) { - wbuf = kvmalloc(size, GFP_KERNEL | __GFP_ZERO); - if (!wbuf) { - err = -ENOMEM; - goto out; - } - - if (v_len > 0) - memcpy(wbuf, stream_buf, v_len); - kvfree(stream_buf); - stream_buf = wbuf; - } - - memcpy(&stream_buf[*pos], buf, count); - - err = ksmbd_vfs_setxattr(user_ns, - fp->filp->f_path.dentry, - fp->stream.name, - (void *)stream_buf, - size, - 0); - if (err < 0) - goto out; - - fp->filp->f_pos = *pos; - err = 0; -out: - kvfree(stream_buf); - return err; -} - -/** - * ksmbd_vfs_write() - vfs helper for smb file write - * @work: work - * @fid: file id of open file - * @buf: buf containing data for writing - * @count: read byte count - * @pos: file pos - * @sync: fsync after write - * @written: number of bytes written - * - * Return: 0 on success, otherwise error - */ -int ksmbd_vfs_write(struct ksmbd_work *work, struct ksmbd_file *fp, - char *buf, size_t count, loff_t *pos, bool sync, - ssize_t *written) -{ - struct file *filp; - loff_t offset = *pos; - int err = 0; - - if (work->conn->connection_type) { - if (!(fp->daccess & FILE_WRITE_DATA_LE)) { - pr_err("no right to write(%pD)\n", fp->filp); - err = -EACCES; - goto out; - } - } - - filp = fp->filp; - - if (ksmbd_stream_fd(fp)) { - err = ksmbd_vfs_stream_write(fp, buf, pos, count); - if (!err) - *written = count; - goto out; - } - - if (!work->tcon->posix_extensions) { - err = check_lock_range(filp, *pos, *pos + count - 1, WRITE); - if (err) { - pr_err("unable to write due to lock\n"); - err = -EAGAIN; - goto out; - } - } - - /* Do we need to break any of a levelII oplock? */ - smb_break_all_levII_oplock(work, fp, 1); - - err = kernel_write(filp, buf, count, pos); - if (err < 0) { - ksmbd_debug(VFS, "smb write failed, err = %d\n", err); - goto out; - } - - filp->f_pos = *pos; - *written = err; - err = 0; - if (sync) { - err = vfs_fsync_range(filp, offset, offset + *written, 0); - if (err < 0) - pr_err("fsync failed for filename = %pD, err = %d\n", - fp->filp, err); - } - -out: - return err; -} - -/** - * ksmbd_vfs_getattr() - vfs helper for smb getattr - * @work: work - * @fid: file id of open file - * @attrs: inode attributes - * - * Return: 0 on success, otherwise error - */ -int ksmbd_vfs_getattr(const struct path *path, struct kstat *stat) -{ - int err; - - err = vfs_getattr(path, stat, STATX_BTIME, AT_STATX_SYNC_AS_STAT); - if (err) - pr_err("getattr failed, err %d\n", err); - return err; -} - -/** - * ksmbd_vfs_fsync() - vfs helper for smb fsync - * @work: work - * @fid: file id of open file - * - * Return: 0 on success, otherwise error - */ -int ksmbd_vfs_fsync(struct ksmbd_work *work, u64 fid, u64 p_id) -{ - struct ksmbd_file *fp; - int err; - - fp = ksmbd_lookup_fd_slow(work, fid, p_id); - if (!fp) { - pr_err("failed to get filp for fid %llu\n", fid); - return -ENOENT; - } - err = vfs_fsync(fp->filp, 0); - if (err < 0) - pr_err("smb fsync failed, err = %d\n", err); - ksmbd_fd_put(work, fp); - return err; -} - -/** - * ksmbd_vfs_remove_file() - vfs helper for smb rmdir or unlink - * @name: directory or file name that is relative to share - * - * Return: 0 on success, otherwise error - */ -int ksmbd_vfs_remove_file(struct ksmbd_work *work, char *name) -{ - struct user_namespace *user_ns; - struct path path; - struct dentry *parent; - int err; - - if (ksmbd_override_fsids(work)) - return -ENOMEM; - - err = ksmbd_vfs_kern_path(work, name, LOOKUP_NO_SYMLINKS, &path, false); - if (err) { - ksmbd_debug(VFS, "can't get %s, err %d\n", name, err); - ksmbd_revert_fsids(work); - return err; - } - - user_ns = mnt_user_ns(path.mnt); - parent = dget_parent(path.dentry); - err = ksmbd_vfs_lock_parent(user_ns, parent, path.dentry); - if (err) { - dput(parent); - path_put(&path); - ksmbd_revert_fsids(work); - return err; - } - - if (!d_inode(path.dentry)->i_nlink) { - err = -ENOENT; - goto out_err; - } - - if (S_ISDIR(d_inode(path.dentry)->i_mode)) { - err = vfs_rmdir(user_ns, d_inode(parent), path.dentry); - if (err && err != -ENOTEMPTY) - ksmbd_debug(VFS, "%s: rmdir failed, err %d\n", name, - err); - } else { - err = vfs_unlink(user_ns, d_inode(parent), path.dentry, NULL); - if (err) - ksmbd_debug(VFS, "%s: unlink failed, err %d\n", name, - err); - } - -out_err: - inode_unlock(d_inode(parent)); - dput(parent); - path_put(&path); - ksmbd_revert_fsids(work); - return err; -} - -/** - * ksmbd_vfs_link() - vfs helper for creating smb hardlink - * @oldname: source file name - * @newname: hardlink name that is relative to share - * - * Return: 0 on success, otherwise error - */ -int ksmbd_vfs_link(struct ksmbd_work *work, const char *oldname, - const char *newname) -{ - struct path oldpath, newpath; - struct dentry *dentry; - int err; - - if (ksmbd_override_fsids(work)) - return -ENOMEM; - - err = kern_path(oldname, LOOKUP_NO_SYMLINKS, &oldpath); - if (err) { - pr_err("cannot get linux path for %s, err = %d\n", - oldname, err); - goto out1; - } - - dentry = ksmbd_vfs_kern_path_create(work, newname, - LOOKUP_NO_SYMLINKS | LOOKUP_REVAL, - &newpath); - if (IS_ERR(dentry)) { - err = PTR_ERR(dentry); - pr_err("path create err for %s, err %d\n", newname, err); - goto out2; - } - - err = -EXDEV; - if (oldpath.mnt != newpath.mnt) { - pr_err("vfs_link failed err %d\n", err); - goto out3; - } - - err = vfs_link(oldpath.dentry, mnt_user_ns(newpath.mnt), - d_inode(newpath.dentry), - dentry, NULL); - if (err) - ksmbd_debug(VFS, "vfs_link failed err %d\n", err); - -out3: - done_path_create(&newpath, dentry); -out2: - path_put(&oldpath); -out1: - ksmbd_revert_fsids(work); - return err; -} - -static int ksmbd_validate_entry_in_use(struct dentry *src_dent) -{ - struct dentry *dst_dent; - - spin_lock(&src_dent->d_lock); - list_for_each_entry(dst_dent, &src_dent->d_subdirs, d_child) { - struct ksmbd_file *child_fp; - - if (d_really_is_negative(dst_dent)) - continue; - - child_fp = ksmbd_lookup_fd_inode(d_inode(dst_dent)); - if (child_fp) { - spin_unlock(&src_dent->d_lock); - ksmbd_debug(VFS, "Forbid rename, sub file/dir is in use\n"); - return -EACCES; - } - } - spin_unlock(&src_dent->d_lock); - - return 0; -} - -static int __ksmbd_vfs_rename(struct ksmbd_work *work, - struct user_namespace *src_user_ns, - struct dentry *src_dent_parent, - struct dentry *src_dent, - struct user_namespace *dst_user_ns, - struct dentry *dst_dent_parent, - struct dentry *trap_dent, - char *dst_name) -{ - struct dentry *dst_dent; - int err; - - if (!work->tcon->posix_extensions) { - err = ksmbd_validate_entry_in_use(src_dent); - if (err) - return err; - } - - if (d_really_is_negative(src_dent_parent)) - return -ENOENT; - if (d_really_is_negative(dst_dent_parent)) - return -ENOENT; - if (d_really_is_negative(src_dent)) - return -ENOENT; - if (src_dent == trap_dent) - return -EINVAL; - - if (ksmbd_override_fsids(work)) - return -ENOMEM; - - dst_dent = lookup_one(dst_user_ns, dst_name, dst_dent_parent, - strlen(dst_name)); - err = PTR_ERR(dst_dent); - if (IS_ERR(dst_dent)) { - pr_err("lookup failed %s [%d]\n", dst_name, err); - goto out; - } - - err = -ENOTEMPTY; - if (dst_dent != trap_dent && !d_really_is_positive(dst_dent)) { - struct renamedata rd = { - .old_mnt_userns = src_user_ns, - .old_dir = d_inode(src_dent_parent), - .old_dentry = src_dent, - .new_mnt_userns = dst_user_ns, - .new_dir = d_inode(dst_dent_parent), - .new_dentry = dst_dent, - }; - err = vfs_rename(&rd); - } - if (err) - pr_err("vfs_rename failed err %d\n", err); - if (dst_dent) - dput(dst_dent); -out: - ksmbd_revert_fsids(work); - return err; -} - -int ksmbd_vfs_fp_rename(struct ksmbd_work *work, struct ksmbd_file *fp, - char *newname) -{ - struct user_namespace *user_ns; - struct path dst_path; - struct dentry *src_dent_parent, *dst_dent_parent; - struct dentry *src_dent, *trap_dent, *src_child; - char *dst_name; - int err; - - dst_name = extract_last_component(newname); - if (!dst_name) { - dst_name = newname; - newname = ""; - } - - src_dent_parent = dget_parent(fp->filp->f_path.dentry); - src_dent = fp->filp->f_path.dentry; - - err = ksmbd_vfs_kern_path(work, newname, - LOOKUP_NO_SYMLINKS | LOOKUP_DIRECTORY, - &dst_path, false); - if (err) { - ksmbd_debug(VFS, "Cannot get path for %s [%d]\n", newname, err); - goto out; - } - dst_dent_parent = dst_path.dentry; - - trap_dent = lock_rename(src_dent_parent, dst_dent_parent); - dget(src_dent); - dget(dst_dent_parent); - user_ns = file_mnt_user_ns(fp->filp); - src_child = lookup_one(user_ns, src_dent->d_name.name, src_dent_parent, - src_dent->d_name.len); - if (IS_ERR(src_child)) { - err = PTR_ERR(src_child); - goto out_lock; - } - - if (src_child != src_dent) { - err = -ESTALE; - dput(src_child); - goto out_lock; - } - dput(src_child); - - err = __ksmbd_vfs_rename(work, - user_ns, - src_dent_parent, - src_dent, - mnt_user_ns(dst_path.mnt), - dst_dent_parent, - trap_dent, - dst_name); -out_lock: - dput(src_dent); - dput(dst_dent_parent); - unlock_rename(src_dent_parent, dst_dent_parent); - path_put(&dst_path); -out: - dput(src_dent_parent); - return err; -} - -/** - * ksmbd_vfs_truncate() - vfs helper for smb file truncate - * @work: work - * @fid: file id of old file - * @size: truncate to given size - * - * Return: 0 on success, otherwise error - */ -int ksmbd_vfs_truncate(struct ksmbd_work *work, - struct ksmbd_file *fp, loff_t size) -{ - int err = 0; - struct file *filp; - - filp = fp->filp; - - /* Do we need to break any of a levelII oplock? */ - smb_break_all_levII_oplock(work, fp, 1); - - if (!work->tcon->posix_extensions) { - struct inode *inode = file_inode(filp); - - if (size < inode->i_size) { - err = check_lock_range(filp, size, - inode->i_size - 1, WRITE); - } else { - err = check_lock_range(filp, inode->i_size, - size - 1, WRITE); - } - - if (err) { - pr_err("failed due to lock\n"); - return -EAGAIN; - } - } - - err = vfs_truncate(&filp->f_path, size); - if (err) - pr_err("truncate failed, err %d\n", err); - return err; -} - -/** - * ksmbd_vfs_listxattr() - vfs helper for smb list extended attributes - * @dentry: dentry of file for listing xattrs - * @list: destination buffer - * @size: destination buffer length - * - * Return: xattr list length on success, otherwise error - */ -ssize_t ksmbd_vfs_listxattr(struct dentry *dentry, char **list) -{ - ssize_t size; - char *vlist = NULL; - - size = vfs_listxattr(dentry, NULL, 0); - if (size <= 0) - return size; - - vlist = kvmalloc(size, GFP_KERNEL | __GFP_ZERO); - if (!vlist) - return -ENOMEM; - - *list = vlist; - size = vfs_listxattr(dentry, vlist, size); - if (size < 0) { - ksmbd_debug(VFS, "listxattr failed\n"); - kvfree(vlist); - *list = NULL; - } - - return size; -} - -static ssize_t ksmbd_vfs_xattr_len(struct user_namespace *user_ns, - struct dentry *dentry, char *xattr_name) -{ - return vfs_getxattr(user_ns, dentry, xattr_name, NULL, 0); -} - -/** - * ksmbd_vfs_getxattr() - vfs helper for smb get extended attributes value - * @user_ns: user namespace - * @dentry: dentry of file for getting xattrs - * @xattr_name: name of xattr name to query - * @xattr_buf: destination buffer xattr value - * - * Return: read xattr value length on success, otherwise error - */ -ssize_t ksmbd_vfs_getxattr(struct user_namespace *user_ns, - struct dentry *dentry, - char *xattr_name, char **xattr_buf) -{ - ssize_t xattr_len; - char *buf; - - *xattr_buf = NULL; - xattr_len = ksmbd_vfs_xattr_len(user_ns, dentry, xattr_name); - if (xattr_len < 0) - return xattr_len; - - buf = kmalloc(xattr_len + 1, GFP_KERNEL); - if (!buf) - return -ENOMEM; - - xattr_len = vfs_getxattr(user_ns, dentry, xattr_name, - (void *)buf, xattr_len); - if (xattr_len > 0) - *xattr_buf = buf; - else - kfree(buf); - return xattr_len; -} - -/** - * ksmbd_vfs_setxattr() - vfs helper for smb set extended attributes value - * @user_ns: user namespace - * @dentry: dentry to set XATTR at - * @name: xattr name for setxattr - * @value: xattr value to set - * @size: size of xattr value - * @flags: destination buffer length - * - * Return: 0 on success, otherwise error - */ -int ksmbd_vfs_setxattr(struct user_namespace *user_ns, - struct dentry *dentry, const char *attr_name, - void *attr_value, size_t attr_size, int flags) -{ - int err; - - err = vfs_setxattr(user_ns, - dentry, - attr_name, - attr_value, - attr_size, - flags); - if (err) - ksmbd_debug(VFS, "setxattr failed, err %d\n", err); - return err; -} - -/** - * ksmbd_vfs_set_fadvise() - convert smb IO caching options to linux options - * @filp: file pointer for IO - * @options: smb IO options - */ -void ksmbd_vfs_set_fadvise(struct file *filp, __le32 option) -{ - struct address_space *mapping; - - mapping = filp->f_mapping; - - if (!option || !mapping) - return; - - if (option & FILE_WRITE_THROUGH_LE) { - filp->f_flags |= O_SYNC; - } else if (option & FILE_SEQUENTIAL_ONLY_LE) { - filp->f_ra.ra_pages = inode_to_bdi(mapping->host)->ra_pages * 2; - spin_lock(&filp->f_lock); - filp->f_mode &= ~FMODE_RANDOM; - spin_unlock(&filp->f_lock); - } else if (option & FILE_RANDOM_ACCESS_LE) { - spin_lock(&filp->f_lock); - filp->f_mode |= FMODE_RANDOM; - spin_unlock(&filp->f_lock); - } -} - -int ksmbd_vfs_zero_data(struct ksmbd_work *work, struct ksmbd_file *fp, - loff_t off, loff_t len) -{ - smb_break_all_levII_oplock(work, fp, 1); - if (fp->f_ci->m_fattr & FILE_ATTRIBUTE_SPARSE_FILE_LE) - return vfs_fallocate(fp->filp, - FALLOC_FL_PUNCH_HOLE | FALLOC_FL_KEEP_SIZE, - off, len); - - return vfs_fallocate(fp->filp, - FALLOC_FL_ZERO_RANGE | FALLOC_FL_KEEP_SIZE, - off, len); -} - -int ksmbd_vfs_fqar_lseek(struct ksmbd_file *fp, loff_t start, loff_t length, - struct file_allocated_range_buffer *ranges, - unsigned int in_count, unsigned int *out_count) -{ - struct file *f = fp->filp; - struct inode *inode = file_inode(fp->filp); - loff_t maxbytes = (u64)inode->i_sb->s_maxbytes, end; - loff_t extent_start, extent_end; - int ret = 0; - - if (start > maxbytes) - return -EFBIG; - - if (!in_count) - return 0; - - /* - * Shrink request scope to what the fs can actually handle. - */ - if (length > maxbytes || (maxbytes - length) < start) - length = maxbytes - start; - - if (start + length > inode->i_size) - length = inode->i_size - start; - - *out_count = 0; - end = start + length; - while (start < end && *out_count < in_count) { - extent_start = vfs_llseek(f, start, SEEK_DATA); - if (extent_start < 0) { - if (extent_start != -ENXIO) - ret = (int)extent_start; - break; - } - - if (extent_start >= end) - break; - - extent_end = vfs_llseek(f, extent_start, SEEK_HOLE); - if (extent_end < 0) { - if (extent_end != -ENXIO) - ret = (int)extent_end; - break; - } else if (extent_start >= extent_end) { - break; - } - - ranges[*out_count].file_offset = cpu_to_le64(extent_start); - ranges[(*out_count)++].length = - cpu_to_le64(min(extent_end, end) - extent_start); - - start = extent_end; - } - - return ret; -} - -int ksmbd_vfs_remove_xattr(struct user_namespace *user_ns, - struct dentry *dentry, char *attr_name) -{ - return vfs_removexattr(user_ns, dentry, attr_name); -} - -int ksmbd_vfs_unlink(struct user_namespace *user_ns, - struct dentry *dir, struct dentry *dentry) -{ - int err = 0; - - err = ksmbd_vfs_lock_parent(user_ns, dir, dentry); - if (err) - return err; - dget(dentry); - - if (S_ISDIR(d_inode(dentry)->i_mode)) - err = vfs_rmdir(user_ns, d_inode(dir), dentry); - else - err = vfs_unlink(user_ns, d_inode(dir), dentry, NULL); - - dput(dentry); - inode_unlock(d_inode(dir)); - if (err) - ksmbd_debug(VFS, "failed to delete, err %d\n", err); - - return err; -} - -static bool __dir_empty(struct dir_context *ctx, const char *name, int namlen, - loff_t offset, u64 ino, unsigned int d_type) -{ - struct ksmbd_readdir_data *buf; - - buf = container_of(ctx, struct ksmbd_readdir_data, ctx); - buf->dirent_count++; - - return buf->dirent_count <= 2; -} - -/** - * ksmbd_vfs_empty_dir() - check for empty directory - * @fp: ksmbd file pointer - * - * Return: true if directory empty, otherwise false - */ -int ksmbd_vfs_empty_dir(struct ksmbd_file *fp) -{ - int err; - struct ksmbd_readdir_data readdir_data; - - memset(&readdir_data, 0, sizeof(struct ksmbd_readdir_data)); - - set_ctx_actor(&readdir_data.ctx, __dir_empty); - readdir_data.dirent_count = 0; - - err = iterate_dir(fp->filp, &readdir_data.ctx); - if (readdir_data.dirent_count > 2) - err = -ENOTEMPTY; - else - err = 0; - return err; -} - -static bool __caseless_lookup(struct dir_context *ctx, const char *name, - int namlen, loff_t offset, u64 ino, - unsigned int d_type) -{ - struct ksmbd_readdir_data *buf; - int cmp = -EINVAL; - - buf = container_of(ctx, struct ksmbd_readdir_data, ctx); - - if (buf->used != namlen) - return true; - if (IS_ENABLED(CONFIG_UNICODE) && buf->um) { - const struct qstr q_buf = {.name = buf->private, - .len = buf->used}; - const struct qstr q_name = {.name = name, - .len = namlen}; - - cmp = utf8_strncasecmp(buf->um, &q_buf, &q_name); - } - if (cmp < 0) - cmp = strncasecmp((char *)buf->private, name, namlen); - if (!cmp) { - memcpy((char *)buf->private, name, namlen); - buf->dirent_count = 1; - return false; - } - return true; -} - -/** - * ksmbd_vfs_lookup_in_dir() - lookup a file in a directory - * @dir: path info - * @name: filename to lookup - * @namelen: filename length - * - * Return: 0 on success, otherwise error - */ -static int ksmbd_vfs_lookup_in_dir(const struct path *dir, char *name, - size_t namelen, struct unicode_map *um) -{ - int ret; - struct file *dfilp; - int flags = O_RDONLY | O_LARGEFILE; - struct ksmbd_readdir_data readdir_data = { - .ctx.actor = __caseless_lookup, - .private = name, - .used = namelen, - .dirent_count = 0, - .um = um, - }; - - dfilp = dentry_open(dir, flags, current_cred()); - if (IS_ERR(dfilp)) - return PTR_ERR(dfilp); - - ret = iterate_dir(dfilp, &readdir_data.ctx); - if (readdir_data.dirent_count > 0) - ret = 0; - fput(dfilp); - return ret; -} - -/** - * ksmbd_vfs_kern_path() - lookup a file and get path info - * @name: file path that is relative to share - * @flags: lookup flags - * @path: if lookup succeed, return path info - * @caseless: caseless filename lookup - * - * Return: 0 on success, otherwise error - */ -int ksmbd_vfs_kern_path(struct ksmbd_work *work, char *name, - unsigned int flags, struct path *path, bool caseless) -{ - struct ksmbd_share_config *share_conf = work->tcon->share_conf; - int err; - - flags |= LOOKUP_BENEATH; - err = vfs_path_lookup(share_conf->vfs_path.dentry, - share_conf->vfs_path.mnt, - name, - flags, - path); - if (!err) - return 0; - - if (caseless) { - char *filepath; - struct path parent; - size_t path_len, remain_len; - - filepath = kstrdup(name, GFP_KERNEL); - if (!filepath) - return -ENOMEM; - - path_len = strlen(filepath); - remain_len = path_len; - - parent = share_conf->vfs_path; - path_get(&parent); - - while (d_can_lookup(parent.dentry)) { - char *filename = filepath + path_len - remain_len; - char *next = strchrnul(filename, '/'); - size_t filename_len = next - filename; - bool is_last = !next[0]; - - if (filename_len == 0) - break; - - err = ksmbd_vfs_lookup_in_dir(&parent, filename, - filename_len, - work->conn->um); - path_put(&parent); - if (err) - goto out; - - next[0] = '\0'; - - err = vfs_path_lookup(share_conf->vfs_path.dentry, - share_conf->vfs_path.mnt, - filepath, - flags, - &parent); - if (err) - goto out; - else if (is_last) { - *path = parent; - goto out; - } - - next[0] = '/'; - remain_len -= filename_len + 1; - } - - path_put(&parent); - err = -EINVAL; -out: - kfree(filepath); - } - return err; -} - -struct dentry *ksmbd_vfs_kern_path_create(struct ksmbd_work *work, - const char *name, - unsigned int flags, - struct path *path) -{ - char *abs_name; - struct dentry *dent; - - abs_name = convert_to_unix_name(work->tcon->share_conf, name); - if (!abs_name) - return ERR_PTR(-ENOMEM); - - dent = kern_path_create(AT_FDCWD, abs_name, path, flags); - kfree(abs_name); - return dent; -} - -int ksmbd_vfs_remove_acl_xattrs(struct user_namespace *user_ns, - struct dentry *dentry) -{ - char *name, *xattr_list = NULL; - ssize_t xattr_list_len; - int err = 0; - - xattr_list_len = ksmbd_vfs_listxattr(dentry, &xattr_list); - if (xattr_list_len < 0) { - goto out; - } else if (!xattr_list_len) { - ksmbd_debug(SMB, "empty xattr in the file\n"); - goto out; - } - - for (name = xattr_list; name - xattr_list < xattr_list_len; - name += strlen(name) + 1) { - ksmbd_debug(SMB, "%s, len %zd\n", name, strlen(name)); - - if (!strncmp(name, XATTR_NAME_POSIX_ACL_ACCESS, - sizeof(XATTR_NAME_POSIX_ACL_ACCESS) - 1) || - !strncmp(name, XATTR_NAME_POSIX_ACL_DEFAULT, - sizeof(XATTR_NAME_POSIX_ACL_DEFAULT) - 1)) { - err = ksmbd_vfs_remove_xattr(user_ns, dentry, name); - if (err) - ksmbd_debug(SMB, - "remove acl xattr failed : %s\n", name); - } - } -out: - kvfree(xattr_list); - return err; -} - -int ksmbd_vfs_remove_sd_xattrs(struct user_namespace *user_ns, - struct dentry *dentry) -{ - char *name, *xattr_list = NULL; - ssize_t xattr_list_len; - int err = 0; - - xattr_list_len = ksmbd_vfs_listxattr(dentry, &xattr_list); - if (xattr_list_len < 0) { - goto out; - } else if (!xattr_list_len) { - ksmbd_debug(SMB, "empty xattr in the file\n"); - goto out; - } - - for (name = xattr_list; name - xattr_list < xattr_list_len; - name += strlen(name) + 1) { - ksmbd_debug(SMB, "%s, len %zd\n", name, strlen(name)); - - if (!strncmp(name, XATTR_NAME_SD, XATTR_NAME_SD_LEN)) { - err = ksmbd_vfs_remove_xattr(user_ns, dentry, name); - if (err) - ksmbd_debug(SMB, "remove xattr failed : %s\n", name); - } - } -out: - kvfree(xattr_list); - return err; -} - -static struct xattr_smb_acl *ksmbd_vfs_make_xattr_posix_acl(struct user_namespace *user_ns, - struct inode *inode, - int acl_type) -{ - struct xattr_smb_acl *smb_acl = NULL; - struct posix_acl *posix_acls; - struct posix_acl_entry *pa_entry; - struct xattr_acl_entry *xa_entry; - int i; - - if (!IS_ENABLED(CONFIG_FS_POSIX_ACL)) - return NULL; - - posix_acls = get_acl(inode, acl_type); - if (!posix_acls) - return NULL; - - smb_acl = kzalloc(sizeof(struct xattr_smb_acl) + - sizeof(struct xattr_acl_entry) * posix_acls->a_count, - GFP_KERNEL); - if (!smb_acl) - goto out; - - smb_acl->count = posix_acls->a_count; - pa_entry = posix_acls->a_entries; - xa_entry = smb_acl->entries; - for (i = 0; i < posix_acls->a_count; i++, pa_entry++, xa_entry++) { - switch (pa_entry->e_tag) { - case ACL_USER: - xa_entry->type = SMB_ACL_USER; - xa_entry->uid = posix_acl_uid_translate(user_ns, pa_entry); - break; - case ACL_USER_OBJ: - xa_entry->type = SMB_ACL_USER_OBJ; - break; - case ACL_GROUP: - xa_entry->type = SMB_ACL_GROUP; - xa_entry->gid = posix_acl_gid_translate(user_ns, pa_entry); - break; - case ACL_GROUP_OBJ: - xa_entry->type = SMB_ACL_GROUP_OBJ; - break; - case ACL_OTHER: - xa_entry->type = SMB_ACL_OTHER; - break; - case ACL_MASK: - xa_entry->type = SMB_ACL_MASK; - break; - default: - pr_err("unknown type : 0x%x\n", pa_entry->e_tag); - goto out; - } - - if (pa_entry->e_perm & ACL_READ) - xa_entry->perm |= SMB_ACL_READ; - if (pa_entry->e_perm & ACL_WRITE) - xa_entry->perm |= SMB_ACL_WRITE; - if (pa_entry->e_perm & ACL_EXECUTE) - xa_entry->perm |= SMB_ACL_EXECUTE; - } -out: - posix_acl_release(posix_acls); - return smb_acl; -} - -int ksmbd_vfs_set_sd_xattr(struct ksmbd_conn *conn, - struct user_namespace *user_ns, - struct dentry *dentry, - struct smb_ntsd *pntsd, int len) -{ - int rc; - struct ndr sd_ndr = {0}, acl_ndr = {0}; - struct xattr_ntacl acl = {0}; - struct xattr_smb_acl *smb_acl, *def_smb_acl = NULL; - struct inode *inode = d_inode(dentry); - - acl.version = 4; - acl.hash_type = XATTR_SD_HASH_TYPE_SHA256; - acl.current_time = ksmbd_UnixTimeToNT(current_time(inode)); - - memcpy(acl.desc, "posix_acl", 9); - acl.desc_len = 10; - - pntsd->osidoffset = - cpu_to_le32(le32_to_cpu(pntsd->osidoffset) + NDR_NTSD_OFFSETOF); - pntsd->gsidoffset = - cpu_to_le32(le32_to_cpu(pntsd->gsidoffset) + NDR_NTSD_OFFSETOF); - pntsd->dacloffset = - cpu_to_le32(le32_to_cpu(pntsd->dacloffset) + NDR_NTSD_OFFSETOF); - - acl.sd_buf = (char *)pntsd; - acl.sd_size = len; - - rc = ksmbd_gen_sd_hash(conn, acl.sd_buf, acl.sd_size, acl.hash); - if (rc) { - pr_err("failed to generate hash for ndr acl\n"); - return rc; - } - - smb_acl = ksmbd_vfs_make_xattr_posix_acl(user_ns, inode, - ACL_TYPE_ACCESS); - if (S_ISDIR(inode->i_mode)) - def_smb_acl = ksmbd_vfs_make_xattr_posix_acl(user_ns, inode, - ACL_TYPE_DEFAULT); - - rc = ndr_encode_posix_acl(&acl_ndr, user_ns, inode, - smb_acl, def_smb_acl); - if (rc) { - pr_err("failed to encode ndr to posix acl\n"); - goto out; - } - - rc = ksmbd_gen_sd_hash(conn, acl_ndr.data, acl_ndr.offset, - acl.posix_acl_hash); - if (rc) { - pr_err("failed to generate hash for ndr acl\n"); - goto out; - } - - rc = ndr_encode_v4_ntacl(&sd_ndr, &acl); - if (rc) { - pr_err("failed to encode ndr to posix acl\n"); - goto out; - } - - rc = ksmbd_vfs_setxattr(user_ns, dentry, - XATTR_NAME_SD, sd_ndr.data, - sd_ndr.offset, 0); - if (rc < 0) - pr_err("Failed to store XATTR ntacl :%d\n", rc); - - kfree(sd_ndr.data); -out: - kfree(acl_ndr.data); - kfree(smb_acl); - kfree(def_smb_acl); - return rc; -} - -int ksmbd_vfs_get_sd_xattr(struct ksmbd_conn *conn, - struct user_namespace *user_ns, - struct dentry *dentry, - struct smb_ntsd **pntsd) -{ - int rc; - struct ndr n; - struct inode *inode = d_inode(dentry); - struct ndr acl_ndr = {0}; - struct xattr_ntacl acl; - struct xattr_smb_acl *smb_acl = NULL, *def_smb_acl = NULL; - __u8 cmp_hash[XATTR_SD_HASH_SIZE] = {0}; - - rc = ksmbd_vfs_getxattr(user_ns, dentry, XATTR_NAME_SD, &n.data); - if (rc <= 0) - return rc; - - n.length = rc; - rc = ndr_decode_v4_ntacl(&n, &acl); - if (rc) - goto free_n_data; - - smb_acl = ksmbd_vfs_make_xattr_posix_acl(user_ns, inode, - ACL_TYPE_ACCESS); - if (S_ISDIR(inode->i_mode)) - def_smb_acl = ksmbd_vfs_make_xattr_posix_acl(user_ns, inode, - ACL_TYPE_DEFAULT); - - rc = ndr_encode_posix_acl(&acl_ndr, user_ns, inode, smb_acl, - def_smb_acl); - if (rc) { - pr_err("failed to encode ndr to posix acl\n"); - goto out_free; - } - - rc = ksmbd_gen_sd_hash(conn, acl_ndr.data, acl_ndr.offset, cmp_hash); - if (rc) { - pr_err("failed to generate hash for ndr acl\n"); - goto out_free; - } - - if (memcmp(cmp_hash, acl.posix_acl_hash, XATTR_SD_HASH_SIZE)) { - pr_err("hash value diff\n"); - rc = -EINVAL; - goto out_free; - } - - *pntsd = acl.sd_buf; - if (acl.sd_size < sizeof(struct smb_ntsd)) { - pr_err("sd size is invalid\n"); - goto out_free; - } - - (*pntsd)->osidoffset = cpu_to_le32(le32_to_cpu((*pntsd)->osidoffset) - - NDR_NTSD_OFFSETOF); - (*pntsd)->gsidoffset = cpu_to_le32(le32_to_cpu((*pntsd)->gsidoffset) - - NDR_NTSD_OFFSETOF); - (*pntsd)->dacloffset = cpu_to_le32(le32_to_cpu((*pntsd)->dacloffset) - - NDR_NTSD_OFFSETOF); - - rc = acl.sd_size; -out_free: - kfree(acl_ndr.data); - kfree(smb_acl); - kfree(def_smb_acl); - if (rc < 0) { - kfree(acl.sd_buf); - *pntsd = NULL; - } - -free_n_data: - kfree(n.data); - return rc; -} - -int ksmbd_vfs_set_dos_attrib_xattr(struct user_namespace *user_ns, - struct dentry *dentry, - struct xattr_dos_attrib *da) -{ - struct ndr n; - int err; - - err = ndr_encode_dos_attr(&n, da); - if (err) - return err; - - err = ksmbd_vfs_setxattr(user_ns, dentry, XATTR_NAME_DOS_ATTRIBUTE, - (void *)n.data, n.offset, 0); - if (err) - ksmbd_debug(SMB, "failed to store dos attribute in xattr\n"); - kfree(n.data); - - return err; -} - -int ksmbd_vfs_get_dos_attrib_xattr(struct user_namespace *user_ns, - struct dentry *dentry, - struct xattr_dos_attrib *da) -{ - struct ndr n; - int err; - - err = ksmbd_vfs_getxattr(user_ns, dentry, XATTR_NAME_DOS_ATTRIBUTE, - (char **)&n.data); - if (err > 0) { - n.length = err; - if (ndr_decode_dos_attr(&n, da)) - err = -EINVAL; - kfree(n.data); - } else { - ksmbd_debug(SMB, "failed to load dos attribute in xattr\n"); - } - - return err; -} - -/** - * ksmbd_vfs_init_kstat() - convert unix stat information to smb stat format - * @p: destination buffer - * @ksmbd_kstat: ksmbd kstat wrapper - */ -void *ksmbd_vfs_init_kstat(char **p, struct ksmbd_kstat *ksmbd_kstat) -{ - struct file_directory_info *info = (struct file_directory_info *)(*p); - struct kstat *kstat = ksmbd_kstat->kstat; - u64 time; - - info->FileIndex = 0; - info->CreationTime = cpu_to_le64(ksmbd_kstat->create_time); - time = ksmbd_UnixTimeToNT(kstat->atime); - info->LastAccessTime = cpu_to_le64(time); - time = ksmbd_UnixTimeToNT(kstat->mtime); - info->LastWriteTime = cpu_to_le64(time); - time = ksmbd_UnixTimeToNT(kstat->ctime); - info->ChangeTime = cpu_to_le64(time); - - if (ksmbd_kstat->file_attributes & FILE_ATTRIBUTE_DIRECTORY_LE) { - info->EndOfFile = 0; - info->AllocationSize = 0; - } else { - info->EndOfFile = cpu_to_le64(kstat->size); - info->AllocationSize = cpu_to_le64(kstat->blocks << 9); - } - info->ExtFileAttributes = ksmbd_kstat->file_attributes; - - return info; -} - -int ksmbd_vfs_fill_dentry_attrs(struct ksmbd_work *work, - struct user_namespace *user_ns, - struct dentry *dentry, - struct ksmbd_kstat *ksmbd_kstat) -{ - u64 time; - int rc; - - generic_fillattr(user_ns, d_inode(dentry), ksmbd_kstat->kstat); - - time = ksmbd_UnixTimeToNT(ksmbd_kstat->kstat->ctime); - ksmbd_kstat->create_time = time; - - /* - * set default value for the case that store dos attributes is not yes - * or that acl is disable in server's filesystem and the config is yes. - */ - if (S_ISDIR(ksmbd_kstat->kstat->mode)) - ksmbd_kstat->file_attributes = FILE_ATTRIBUTE_DIRECTORY_LE; - else - ksmbd_kstat->file_attributes = FILE_ATTRIBUTE_ARCHIVE_LE; - - if (test_share_config_flag(work->tcon->share_conf, - KSMBD_SHARE_FLAG_STORE_DOS_ATTRS)) { - struct xattr_dos_attrib da; - - rc = ksmbd_vfs_get_dos_attrib_xattr(user_ns, dentry, &da); - if (rc > 0) { - ksmbd_kstat->file_attributes = cpu_to_le32(da.attr); - ksmbd_kstat->create_time = da.create_time; - } else { - ksmbd_debug(VFS, "fail to load dos attribute.\n"); - } - } - - return 0; -} - -ssize_t ksmbd_vfs_casexattr_len(struct user_namespace *user_ns, - struct dentry *dentry, char *attr_name, - int attr_name_len) -{ - char *name, *xattr_list = NULL; - ssize_t value_len = -ENOENT, xattr_list_len; - - xattr_list_len = ksmbd_vfs_listxattr(dentry, &xattr_list); - if (xattr_list_len <= 0) - goto out; - - for (name = xattr_list; name - xattr_list < xattr_list_len; - name += strlen(name) + 1) { - ksmbd_debug(VFS, "%s, len %zd\n", name, strlen(name)); - if (strncasecmp(attr_name, name, attr_name_len)) - continue; - - value_len = ksmbd_vfs_xattr_len(user_ns, dentry, name); - break; - } - -out: - kvfree(xattr_list); - return value_len; -} - -int ksmbd_vfs_xattr_stream_name(char *stream_name, char **xattr_stream_name, - size_t *xattr_stream_name_size, int s_type) -{ - char *type, *buf; - - if (s_type == DIR_STREAM) - type = ":$INDEX_ALLOCATION"; - else - type = ":$DATA"; - - buf = kasprintf(GFP_KERNEL, "%s%s%s", - XATTR_NAME_STREAM, stream_name, type); - if (!buf) - return -ENOMEM; - - *xattr_stream_name = buf; - *xattr_stream_name_size = strlen(buf) + 1; - - return 0; -} - -int ksmbd_vfs_copy_file_ranges(struct ksmbd_work *work, - struct ksmbd_file *src_fp, - struct ksmbd_file *dst_fp, - struct srv_copychunk *chunks, - unsigned int chunk_count, - unsigned int *chunk_count_written, - unsigned int *chunk_size_written, - loff_t *total_size_written) -{ - unsigned int i; - loff_t src_off, dst_off, src_file_size; - size_t len; - int ret; - - *chunk_count_written = 0; - *chunk_size_written = 0; - *total_size_written = 0; - - if (!(src_fp->daccess & (FILE_READ_DATA_LE | FILE_EXECUTE_LE))) { - pr_err("no right to read(%pD)\n", src_fp->filp); - return -EACCES; - } - if (!(dst_fp->daccess & (FILE_WRITE_DATA_LE | FILE_APPEND_DATA_LE))) { - pr_err("no right to write(%pD)\n", dst_fp->filp); - return -EACCES; - } - - if (ksmbd_stream_fd(src_fp) || ksmbd_stream_fd(dst_fp)) - return -EBADF; - - smb_break_all_levII_oplock(work, dst_fp, 1); - - if (!work->tcon->posix_extensions) { - for (i = 0; i < chunk_count; i++) { - src_off = le64_to_cpu(chunks[i].SourceOffset); - dst_off = le64_to_cpu(chunks[i].TargetOffset); - len = le32_to_cpu(chunks[i].Length); - - if (check_lock_range(src_fp->filp, src_off, - src_off + len - 1, READ)) - return -EAGAIN; - if (check_lock_range(dst_fp->filp, dst_off, - dst_off + len - 1, WRITE)) - return -EAGAIN; - } - } - - src_file_size = i_size_read(file_inode(src_fp->filp)); - - for (i = 0; i < chunk_count; i++) { - src_off = le64_to_cpu(chunks[i].SourceOffset); - dst_off = le64_to_cpu(chunks[i].TargetOffset); - len = le32_to_cpu(chunks[i].Length); - - if (src_off + len > src_file_size) - return -E2BIG; - - ret = vfs_copy_file_range(src_fp->filp, src_off, - dst_fp->filp, dst_off, len, 0); - if (ret == -EOPNOTSUPP || ret == -EXDEV) - ret = vfs_copy_file_range(src_fp->filp, src_off, - dst_fp->filp, dst_off, len, - COPY_FILE_SPLICE); - if (ret < 0) - return ret; - - *chunk_count_written += 1; - *total_size_written += ret; - } - return 0; -} - -void ksmbd_vfs_posix_lock_wait(struct file_lock *flock) -{ - wait_event(flock->fl_wait, !flock->fl_blocker); -} - -int ksmbd_vfs_posix_lock_wait_timeout(struct file_lock *flock, long timeout) -{ - return wait_event_interruptible_timeout(flock->fl_wait, - !flock->fl_blocker, - timeout); -} - -void ksmbd_vfs_posix_lock_unblock(struct file_lock *flock) -{ - locks_delete_block(flock); -} - -int ksmbd_vfs_set_init_posix_acl(struct user_namespace *user_ns, - struct inode *inode) -{ - struct posix_acl_state acl_state; - struct posix_acl *acls; - int rc; - - if (!IS_ENABLED(CONFIG_FS_POSIX_ACL)) - return -EOPNOTSUPP; - - ksmbd_debug(SMB, "Set posix acls\n"); - rc = init_acl_state(&acl_state, 1); - if (rc) - return rc; - - /* Set default owner group */ - acl_state.owner.allow = (inode->i_mode & 0700) >> 6; - acl_state.group.allow = (inode->i_mode & 0070) >> 3; - acl_state.other.allow = inode->i_mode & 0007; - acl_state.users->aces[acl_state.users->n].uid = inode->i_uid; - acl_state.users->aces[acl_state.users->n++].perms.allow = - acl_state.owner.allow; - acl_state.groups->aces[acl_state.groups->n].gid = inode->i_gid; - acl_state.groups->aces[acl_state.groups->n++].perms.allow = - acl_state.group.allow; - acl_state.mask.allow = 0x07; - - acls = posix_acl_alloc(6, GFP_KERNEL); - if (!acls) { - free_acl_state(&acl_state); - return -ENOMEM; - } - posix_state_to_acl(&acl_state, acls->a_entries); - rc = set_posix_acl(user_ns, inode, ACL_TYPE_ACCESS, acls); - if (rc < 0) - ksmbd_debug(SMB, "Set posix acl(ACL_TYPE_ACCESS) failed, rc : %d\n", - rc); - else if (S_ISDIR(inode->i_mode)) { - posix_state_to_acl(&acl_state, acls->a_entries); - rc = set_posix_acl(user_ns, inode, ACL_TYPE_DEFAULT, - acls); - if (rc < 0) - ksmbd_debug(SMB, "Set posix acl(ACL_TYPE_DEFAULT) failed, rc : %d\n", - rc); - } - free_acl_state(&acl_state); - posix_acl_release(acls); - return rc; -} - -int ksmbd_vfs_inherit_posix_acl(struct user_namespace *user_ns, - struct inode *inode, struct inode *parent_inode) -{ - struct posix_acl *acls; - struct posix_acl_entry *pace; - int rc, i; - - if (!IS_ENABLED(CONFIG_FS_POSIX_ACL)) - return -EOPNOTSUPP; - - acls = get_acl(parent_inode, ACL_TYPE_DEFAULT); - if (!acls) - return -ENOENT; - pace = acls->a_entries; - - for (i = 0; i < acls->a_count; i++, pace++) { - if (pace->e_tag == ACL_MASK) { - pace->e_perm = 0x07; - break; - } - } - - rc = set_posix_acl(user_ns, inode, ACL_TYPE_ACCESS, acls); - if (rc < 0) - ksmbd_debug(SMB, "Set posix acl(ACL_TYPE_ACCESS) failed, rc : %d\n", - rc); - if (S_ISDIR(inode->i_mode)) { - rc = set_posix_acl(user_ns, inode, ACL_TYPE_DEFAULT, - acls); - if (rc < 0) - ksmbd_debug(SMB, "Set posix acl(ACL_TYPE_DEFAULT) failed, rc : %d\n", - rc); - } - posix_acl_release(acls); - return rc; -} diff --git a/fs/ksmbd/vfs.h b/fs/ksmbd/vfs.h deleted file mode 100644 index 593059ca85112..0000000000000 --- a/fs/ksmbd/vfs.h +++ /dev/null @@ -1,167 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0-or-later */ -/* - * Copyright (C) 2016 Namjae Jeon - * Copyright (C) 2018 Samsung Electronics Co., Ltd. - */ - -#ifndef __KSMBD_VFS_H__ -#define __KSMBD_VFS_H__ - -#include -#include -#include -#include -#include -#include - -#include "smbacl.h" -#include "xattr.h" - -/* - * Enumeration for stream type. - */ -enum { - DATA_STREAM = 1, /* type $DATA */ - DIR_STREAM /* type $INDEX_ALLOCATION */ -}; - -/* CreateOptions */ -#define CREATE_TREE_CONNECTION cpu_to_le32(0x00000080) -#define FILE_RESERVE_OPFILTER_LE cpu_to_le32(0x00100000) - -#define CREATE_OPTION_READONLY 0x10000000 -/* system. NB not sent over wire */ -#define CREATE_OPTION_SPECIAL 0x20000000 - -struct ksmbd_work; -struct ksmbd_file; -struct ksmbd_conn; - -struct ksmbd_dir_info { - const char *name; - char *wptr; - char *rptr; - int name_len; - int out_buf_len; - int num_entry; - int data_count; - int last_entry_offset; - bool hide_dot_file; - int flags; - int last_entry_off_align; -}; - -struct ksmbd_readdir_data { - struct dir_context ctx; - union { - void *private; - char *dirent; - }; - - unsigned int used; - unsigned int dirent_count; - unsigned int file_attr; - struct unicode_map *um; -}; - -/* ksmbd kstat wrapper to get valid create time when reading dir entry */ -struct ksmbd_kstat { - struct kstat *kstat; - unsigned long long create_time; - __le32 file_attributes; -}; - -int ksmbd_vfs_lock_parent(struct user_namespace *user_ns, struct dentry *parent, - struct dentry *child); -int ksmbd_vfs_may_delete(struct user_namespace *user_ns, struct dentry *dentry); -int ksmbd_vfs_query_maximal_access(struct user_namespace *user_ns, - struct dentry *dentry, __le32 *daccess); -int ksmbd_vfs_create(struct ksmbd_work *work, const char *name, umode_t mode); -int ksmbd_vfs_mkdir(struct ksmbd_work *work, const char *name, umode_t mode); -int ksmbd_vfs_read(struct ksmbd_work *work, struct ksmbd_file *fp, - size_t count, loff_t *pos); -int ksmbd_vfs_write(struct ksmbd_work *work, struct ksmbd_file *fp, - char *buf, size_t count, loff_t *pos, bool sync, - ssize_t *written); -int ksmbd_vfs_fsync(struct ksmbd_work *work, u64 fid, u64 p_id); -int ksmbd_vfs_remove_file(struct ksmbd_work *work, char *name); -int ksmbd_vfs_link(struct ksmbd_work *work, - const char *oldname, const char *newname); -int ksmbd_vfs_getattr(const struct path *path, struct kstat *stat); -int ksmbd_vfs_fp_rename(struct ksmbd_work *work, struct ksmbd_file *fp, - char *newname); -int ksmbd_vfs_truncate(struct ksmbd_work *work, - struct ksmbd_file *fp, loff_t size); -struct srv_copychunk; -int ksmbd_vfs_copy_file_ranges(struct ksmbd_work *work, - struct ksmbd_file *src_fp, - struct ksmbd_file *dst_fp, - struct srv_copychunk *chunks, - unsigned int chunk_count, - unsigned int *chunk_count_written, - unsigned int *chunk_size_written, - loff_t *total_size_written); -ssize_t ksmbd_vfs_listxattr(struct dentry *dentry, char **list); -ssize_t ksmbd_vfs_getxattr(struct user_namespace *user_ns, - struct dentry *dentry, - char *xattr_name, - char **xattr_buf); -ssize_t ksmbd_vfs_casexattr_len(struct user_namespace *user_ns, - struct dentry *dentry, char *attr_name, - int attr_name_len); -int ksmbd_vfs_setxattr(struct user_namespace *user_ns, - struct dentry *dentry, const char *attr_name, - void *attr_value, size_t attr_size, int flags); -int ksmbd_vfs_xattr_stream_name(char *stream_name, char **xattr_stream_name, - size_t *xattr_stream_name_size, int s_type); -int ksmbd_vfs_remove_xattr(struct user_namespace *user_ns, - struct dentry *dentry, char *attr_name); -int ksmbd_vfs_kern_path(struct ksmbd_work *work, - char *name, unsigned int flags, struct path *path, - bool caseless); -struct dentry *ksmbd_vfs_kern_path_create(struct ksmbd_work *work, - const char *name, - unsigned int flags, - struct path *path); -int ksmbd_vfs_empty_dir(struct ksmbd_file *fp); -void ksmbd_vfs_set_fadvise(struct file *filp, __le32 option); -int ksmbd_vfs_zero_data(struct ksmbd_work *work, struct ksmbd_file *fp, - loff_t off, loff_t len); -struct file_allocated_range_buffer; -int ksmbd_vfs_fqar_lseek(struct ksmbd_file *fp, loff_t start, loff_t length, - struct file_allocated_range_buffer *ranges, - unsigned int in_count, unsigned int *out_count); -int ksmbd_vfs_unlink(struct user_namespace *user_ns, - struct dentry *dir, struct dentry *dentry); -void *ksmbd_vfs_init_kstat(char **p, struct ksmbd_kstat *ksmbd_kstat); -int ksmbd_vfs_fill_dentry_attrs(struct ksmbd_work *work, - struct user_namespace *user_ns, - struct dentry *dentry, - struct ksmbd_kstat *ksmbd_kstat); -void ksmbd_vfs_posix_lock_wait(struct file_lock *flock); -int ksmbd_vfs_posix_lock_wait_timeout(struct file_lock *flock, long timeout); -void ksmbd_vfs_posix_lock_unblock(struct file_lock *flock); -int ksmbd_vfs_remove_acl_xattrs(struct user_namespace *user_ns, - struct dentry *dentry); -int ksmbd_vfs_remove_sd_xattrs(struct user_namespace *user_ns, - struct dentry *dentry); -int ksmbd_vfs_set_sd_xattr(struct ksmbd_conn *conn, - struct user_namespace *user_ns, - struct dentry *dentry, - struct smb_ntsd *pntsd, int len); -int ksmbd_vfs_get_sd_xattr(struct ksmbd_conn *conn, - struct user_namespace *user_ns, - struct dentry *dentry, - struct smb_ntsd **pntsd); -int ksmbd_vfs_set_dos_attrib_xattr(struct user_namespace *user_ns, - struct dentry *dentry, - struct xattr_dos_attrib *da); -int ksmbd_vfs_get_dos_attrib_xattr(struct user_namespace *user_ns, - struct dentry *dentry, - struct xattr_dos_attrib *da); -int ksmbd_vfs_set_init_posix_acl(struct user_namespace *user_ns, - struct inode *inode); -int ksmbd_vfs_inherit_posix_acl(struct user_namespace *user_ns, - struct inode *inode, - struct inode *parent_inode); -#endif /* __KSMBD_VFS_H__ */ diff --git a/fs/ksmbd/vfs_cache.c b/fs/ksmbd/vfs_cache.c deleted file mode 100644 index 0ae5dd0829e92..0000000000000 --- a/fs/ksmbd/vfs_cache.c +++ /dev/null @@ -1,708 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-or-later -/* - * Copyright (C) 2016 Namjae Jeon - * Copyright (C) 2019 Samsung Electronics Co., Ltd. - */ - -#include -#include -#include - -#include "glob.h" -#include "vfs_cache.h" -#include "oplock.h" -#include "vfs.h" -#include "connection.h" -#include "mgmt/tree_connect.h" -#include "mgmt/user_session.h" -#include "smb_common.h" - -#define S_DEL_PENDING 1 -#define S_DEL_ON_CLS 2 -#define S_DEL_ON_CLS_STREAM 8 - -static unsigned int inode_hash_mask __read_mostly; -static unsigned int inode_hash_shift __read_mostly; -static struct hlist_head *inode_hashtable __read_mostly; -static DEFINE_RWLOCK(inode_hash_lock); - -static struct ksmbd_file_table global_ft; -static atomic_long_t fd_limit; -static struct kmem_cache *filp_cache; - -void ksmbd_set_fd_limit(unsigned long limit) -{ - limit = min(limit, get_max_files()); - atomic_long_set(&fd_limit, limit); -} - -static bool fd_limit_depleted(void) -{ - long v = atomic_long_dec_return(&fd_limit); - - if (v >= 0) - return false; - atomic_long_inc(&fd_limit); - return true; -} - -static void fd_limit_close(void) -{ - atomic_long_inc(&fd_limit); -} - -/* - * INODE hash - */ - -static unsigned long inode_hash(struct super_block *sb, unsigned long hashval) -{ - unsigned long tmp; - - tmp = (hashval * (unsigned long)sb) ^ (GOLDEN_RATIO_PRIME + hashval) / - L1_CACHE_BYTES; - tmp = tmp ^ ((tmp ^ GOLDEN_RATIO_PRIME) >> inode_hash_shift); - return tmp & inode_hash_mask; -} - -static struct ksmbd_inode *__ksmbd_inode_lookup(struct inode *inode) -{ - struct hlist_head *head = inode_hashtable + - inode_hash(inode->i_sb, inode->i_ino); - struct ksmbd_inode *ci = NULL, *ret_ci = NULL; - - hlist_for_each_entry(ci, head, m_hash) { - if (ci->m_inode == inode) { - if (atomic_inc_not_zero(&ci->m_count)) - ret_ci = ci; - break; - } - } - return ret_ci; -} - -static struct ksmbd_inode *ksmbd_inode_lookup(struct ksmbd_file *fp) -{ - return __ksmbd_inode_lookup(file_inode(fp->filp)); -} - -static struct ksmbd_inode *ksmbd_inode_lookup_by_vfsinode(struct inode *inode) -{ - struct ksmbd_inode *ci; - - read_lock(&inode_hash_lock); - ci = __ksmbd_inode_lookup(inode); - read_unlock(&inode_hash_lock); - return ci; -} - -int ksmbd_query_inode_status(struct inode *inode) -{ - struct ksmbd_inode *ci; - int ret = KSMBD_INODE_STATUS_UNKNOWN; - - read_lock(&inode_hash_lock); - ci = __ksmbd_inode_lookup(inode); - if (ci) { - ret = KSMBD_INODE_STATUS_OK; - if (ci->m_flags & S_DEL_PENDING) - ret = KSMBD_INODE_STATUS_PENDING_DELETE; - atomic_dec(&ci->m_count); - } - read_unlock(&inode_hash_lock); - return ret; -} - -bool ksmbd_inode_pending_delete(struct ksmbd_file *fp) -{ - return (fp->f_ci->m_flags & S_DEL_PENDING); -} - -void ksmbd_set_inode_pending_delete(struct ksmbd_file *fp) -{ - fp->f_ci->m_flags |= S_DEL_PENDING; -} - -void ksmbd_clear_inode_pending_delete(struct ksmbd_file *fp) -{ - fp->f_ci->m_flags &= ~S_DEL_PENDING; -} - -void ksmbd_fd_set_delete_on_close(struct ksmbd_file *fp, - int file_info) -{ - if (ksmbd_stream_fd(fp)) { - fp->f_ci->m_flags |= S_DEL_ON_CLS_STREAM; - return; - } - - fp->f_ci->m_flags |= S_DEL_ON_CLS; -} - -static void ksmbd_inode_hash(struct ksmbd_inode *ci) -{ - struct hlist_head *b = inode_hashtable + - inode_hash(ci->m_inode->i_sb, ci->m_inode->i_ino); - - hlist_add_head(&ci->m_hash, b); -} - -static void ksmbd_inode_unhash(struct ksmbd_inode *ci) -{ - write_lock(&inode_hash_lock); - hlist_del_init(&ci->m_hash); - write_unlock(&inode_hash_lock); -} - -static int ksmbd_inode_init(struct ksmbd_inode *ci, struct ksmbd_file *fp) -{ - ci->m_inode = file_inode(fp->filp); - atomic_set(&ci->m_count, 1); - atomic_set(&ci->op_count, 0); - atomic_set(&ci->sop_count, 0); - ci->m_flags = 0; - ci->m_fattr = 0; - INIT_LIST_HEAD(&ci->m_fp_list); - INIT_LIST_HEAD(&ci->m_op_list); - rwlock_init(&ci->m_lock); - return 0; -} - -static struct ksmbd_inode *ksmbd_inode_get(struct ksmbd_file *fp) -{ - struct ksmbd_inode *ci, *tmpci; - int rc; - - read_lock(&inode_hash_lock); - ci = ksmbd_inode_lookup(fp); - read_unlock(&inode_hash_lock); - if (ci) - return ci; - - ci = kmalloc(sizeof(struct ksmbd_inode), GFP_KERNEL); - if (!ci) - return NULL; - - rc = ksmbd_inode_init(ci, fp); - if (rc) { - pr_err("inode initialized failed\n"); - kfree(ci); - return NULL; - } - - write_lock(&inode_hash_lock); - tmpci = ksmbd_inode_lookup(fp); - if (!tmpci) { - ksmbd_inode_hash(ci); - } else { - kfree(ci); - ci = tmpci; - } - write_unlock(&inode_hash_lock); - return ci; -} - -static void ksmbd_inode_free(struct ksmbd_inode *ci) -{ - ksmbd_inode_unhash(ci); - kfree(ci); -} - -static void ksmbd_inode_put(struct ksmbd_inode *ci) -{ - if (atomic_dec_and_test(&ci->m_count)) - ksmbd_inode_free(ci); -} - -int __init ksmbd_inode_hash_init(void) -{ - unsigned int loop; - unsigned long numentries = 16384; - unsigned long bucketsize = sizeof(struct hlist_head); - unsigned long size; - - inode_hash_shift = ilog2(numentries); - inode_hash_mask = (1 << inode_hash_shift) - 1; - - size = bucketsize << inode_hash_shift; - - /* init master fp hash table */ - inode_hashtable = vmalloc(size); - if (!inode_hashtable) - return -ENOMEM; - - for (loop = 0; loop < (1U << inode_hash_shift); loop++) - INIT_HLIST_HEAD(&inode_hashtable[loop]); - return 0; -} - -void ksmbd_release_inode_hash(void) -{ - vfree(inode_hashtable); -} - -static void __ksmbd_inode_close(struct ksmbd_file *fp) -{ - struct dentry *dir, *dentry; - struct ksmbd_inode *ci = fp->f_ci; - int err; - struct file *filp; - - filp = fp->filp; - if (ksmbd_stream_fd(fp) && (ci->m_flags & S_DEL_ON_CLS_STREAM)) { - ci->m_flags &= ~S_DEL_ON_CLS_STREAM; - err = ksmbd_vfs_remove_xattr(file_mnt_user_ns(filp), - filp->f_path.dentry, - fp->stream.name); - if (err) - pr_err("remove xattr failed : %s\n", - fp->stream.name); - } - - if (atomic_dec_and_test(&ci->m_count)) { - write_lock(&ci->m_lock); - if (ci->m_flags & (S_DEL_ON_CLS | S_DEL_PENDING)) { - dentry = filp->f_path.dentry; - dir = dentry->d_parent; - ci->m_flags &= ~(S_DEL_ON_CLS | S_DEL_PENDING); - write_unlock(&ci->m_lock); - ksmbd_vfs_unlink(file_mnt_user_ns(filp), dir, dentry); - write_lock(&ci->m_lock); - } - write_unlock(&ci->m_lock); - - ksmbd_inode_free(ci); - } -} - -static void __ksmbd_remove_durable_fd(struct ksmbd_file *fp) -{ - if (!has_file_id(fp->persistent_id)) - return; - - write_lock(&global_ft.lock); - idr_remove(global_ft.idr, fp->persistent_id); - write_unlock(&global_ft.lock); -} - -static void __ksmbd_remove_fd(struct ksmbd_file_table *ft, struct ksmbd_file *fp) -{ - if (!has_file_id(fp->volatile_id)) - return; - - write_lock(&fp->f_ci->m_lock); - list_del_init(&fp->node); - write_unlock(&fp->f_ci->m_lock); - - write_lock(&ft->lock); - idr_remove(ft->idr, fp->volatile_id); - write_unlock(&ft->lock); -} - -static void __ksmbd_close_fd(struct ksmbd_file_table *ft, struct ksmbd_file *fp) -{ - struct file *filp; - struct ksmbd_lock *smb_lock, *tmp_lock; - - fd_limit_close(); - __ksmbd_remove_durable_fd(fp); - __ksmbd_remove_fd(ft, fp); - - close_id_del_oplock(fp); - filp = fp->filp; - - __ksmbd_inode_close(fp); - if (!IS_ERR_OR_NULL(filp)) - fput(filp); - - /* because the reference count of fp is 0, it is guaranteed that - * there are not accesses to fp->lock_list. - */ - list_for_each_entry_safe(smb_lock, tmp_lock, &fp->lock_list, flist) { - spin_lock(&fp->conn->llist_lock); - list_del(&smb_lock->clist); - spin_unlock(&fp->conn->llist_lock); - - list_del(&smb_lock->flist); - locks_free_lock(smb_lock->fl); - kfree(smb_lock); - } - - if (ksmbd_stream_fd(fp)) - kfree(fp->stream.name); - kmem_cache_free(filp_cache, fp); -} - -static struct ksmbd_file *ksmbd_fp_get(struct ksmbd_file *fp) -{ - if (!atomic_inc_not_zero(&fp->refcount)) - return NULL; - return fp; -} - -static struct ksmbd_file *__ksmbd_lookup_fd(struct ksmbd_file_table *ft, - u64 id) -{ - struct ksmbd_file *fp; - - if (!has_file_id(id)) - return NULL; - - read_lock(&ft->lock); - fp = idr_find(ft->idr, id); - if (fp) - fp = ksmbd_fp_get(fp); - read_unlock(&ft->lock); - return fp; -} - -static void __put_fd_final(struct ksmbd_work *work, struct ksmbd_file *fp) -{ - __ksmbd_close_fd(&work->sess->file_table, fp); - atomic_dec(&work->conn->stats.open_files_count); -} - -static void set_close_state_blocked_works(struct ksmbd_file *fp) -{ - struct ksmbd_work *cancel_work; - - spin_lock(&fp->f_lock); - list_for_each_entry(cancel_work, &fp->blocked_works, - fp_entry) { - cancel_work->state = KSMBD_WORK_CLOSED; - cancel_work->cancel_fn(cancel_work->cancel_argv); - } - spin_unlock(&fp->f_lock); -} - -int ksmbd_close_fd(struct ksmbd_work *work, u64 id) -{ - struct ksmbd_file *fp; - struct ksmbd_file_table *ft; - - if (!has_file_id(id)) - return 0; - - ft = &work->sess->file_table; - read_lock(&ft->lock); - fp = idr_find(ft->idr, id); - if (fp) { - set_close_state_blocked_works(fp); - - if (!atomic_dec_and_test(&fp->refcount)) - fp = NULL; - } - read_unlock(&ft->lock); - - if (!fp) - return -EINVAL; - - __put_fd_final(work, fp); - return 0; -} - -void ksmbd_fd_put(struct ksmbd_work *work, struct ksmbd_file *fp) -{ - if (!fp) - return; - - if (!atomic_dec_and_test(&fp->refcount)) - return; - __put_fd_final(work, fp); -} - -static bool __sanity_check(struct ksmbd_tree_connect *tcon, struct ksmbd_file *fp) -{ - if (!fp) - return false; - if (fp->tcon != tcon) - return false; - return true; -} - -struct ksmbd_file *ksmbd_lookup_foreign_fd(struct ksmbd_work *work, u64 id) -{ - return __ksmbd_lookup_fd(&work->sess->file_table, id); -} - -struct ksmbd_file *ksmbd_lookup_fd_fast(struct ksmbd_work *work, u64 id) -{ - struct ksmbd_file *fp = __ksmbd_lookup_fd(&work->sess->file_table, id); - - if (__sanity_check(work->tcon, fp)) - return fp; - - ksmbd_fd_put(work, fp); - return NULL; -} - -struct ksmbd_file *ksmbd_lookup_fd_slow(struct ksmbd_work *work, u64 id, - u64 pid) -{ - struct ksmbd_file *fp; - - if (!has_file_id(id)) { - id = work->compound_fid; - pid = work->compound_pfid; - } - - fp = __ksmbd_lookup_fd(&work->sess->file_table, id); - if (!__sanity_check(work->tcon, fp)) { - ksmbd_fd_put(work, fp); - return NULL; - } - if (fp->persistent_id != pid) { - ksmbd_fd_put(work, fp); - return NULL; - } - return fp; -} - -struct ksmbd_file *ksmbd_lookup_durable_fd(unsigned long long id) -{ - return __ksmbd_lookup_fd(&global_ft, id); -} - -struct ksmbd_file *ksmbd_lookup_fd_cguid(char *cguid) -{ - struct ksmbd_file *fp = NULL; - unsigned int id; - - read_lock(&global_ft.lock); - idr_for_each_entry(global_ft.idr, fp, id) { - if (!memcmp(fp->create_guid, - cguid, - SMB2_CREATE_GUID_SIZE)) { - fp = ksmbd_fp_get(fp); - break; - } - } - read_unlock(&global_ft.lock); - - return fp; -} - -struct ksmbd_file *ksmbd_lookup_fd_inode(struct inode *inode) -{ - struct ksmbd_file *lfp; - struct ksmbd_inode *ci; - - ci = ksmbd_inode_lookup_by_vfsinode(inode); - if (!ci) - return NULL; - - read_lock(&ci->m_lock); - list_for_each_entry(lfp, &ci->m_fp_list, node) { - if (inode == file_inode(lfp->filp)) { - atomic_dec(&ci->m_count); - lfp = ksmbd_fp_get(lfp); - read_unlock(&ci->m_lock); - return lfp; - } - } - atomic_dec(&ci->m_count); - read_unlock(&ci->m_lock); - return NULL; -} - -#define OPEN_ID_TYPE_VOLATILE_ID (0) -#define OPEN_ID_TYPE_PERSISTENT_ID (1) - -static void __open_id_set(struct ksmbd_file *fp, u64 id, int type) -{ - if (type == OPEN_ID_TYPE_VOLATILE_ID) - fp->volatile_id = id; - if (type == OPEN_ID_TYPE_PERSISTENT_ID) - fp->persistent_id = id; -} - -static int __open_id(struct ksmbd_file_table *ft, struct ksmbd_file *fp, - int type) -{ - u64 id = 0; - int ret; - - if (type == OPEN_ID_TYPE_VOLATILE_ID && fd_limit_depleted()) { - __open_id_set(fp, KSMBD_NO_FID, type); - return -EMFILE; - } - - idr_preload(GFP_KERNEL); - write_lock(&ft->lock); - ret = idr_alloc_cyclic(ft->idr, fp, 0, INT_MAX - 1, GFP_NOWAIT); - if (ret >= 0) { - id = ret; - ret = 0; - } else { - id = KSMBD_NO_FID; - fd_limit_close(); - } - - __open_id_set(fp, id, type); - write_unlock(&ft->lock); - idr_preload_end(); - return ret; -} - -unsigned int ksmbd_open_durable_fd(struct ksmbd_file *fp) -{ - __open_id(&global_ft, fp, OPEN_ID_TYPE_PERSISTENT_ID); - return fp->persistent_id; -} - -struct ksmbd_file *ksmbd_open_fd(struct ksmbd_work *work, struct file *filp) -{ - struct ksmbd_file *fp; - int ret; - - fp = kmem_cache_zalloc(filp_cache, GFP_KERNEL); - if (!fp) { - pr_err("Failed to allocate memory\n"); - return ERR_PTR(-ENOMEM); - } - - INIT_LIST_HEAD(&fp->blocked_works); - INIT_LIST_HEAD(&fp->node); - INIT_LIST_HEAD(&fp->lock_list); - spin_lock_init(&fp->f_lock); - atomic_set(&fp->refcount, 1); - - fp->filp = filp; - fp->conn = work->conn; - fp->tcon = work->tcon; - fp->volatile_id = KSMBD_NO_FID; - fp->persistent_id = KSMBD_NO_FID; - fp->f_ci = ksmbd_inode_get(fp); - - if (!fp->f_ci) { - ret = -ENOMEM; - goto err_out; - } - - ret = __open_id(&work->sess->file_table, fp, OPEN_ID_TYPE_VOLATILE_ID); - if (ret) { - ksmbd_inode_put(fp->f_ci); - goto err_out; - } - - atomic_inc(&work->conn->stats.open_files_count); - return fp; - -err_out: - kmem_cache_free(filp_cache, fp); - return ERR_PTR(ret); -} - -static int -__close_file_table_ids(struct ksmbd_file_table *ft, - struct ksmbd_tree_connect *tcon, - bool (*skip)(struct ksmbd_tree_connect *tcon, - struct ksmbd_file *fp)) -{ - unsigned int id; - struct ksmbd_file *fp; - int num = 0; - - idr_for_each_entry(ft->idr, fp, id) { - if (skip(tcon, fp)) - continue; - - set_close_state_blocked_works(fp); - - if (!atomic_dec_and_test(&fp->refcount)) - continue; - __ksmbd_close_fd(ft, fp); - num++; - } - return num; -} - -static bool tree_conn_fd_check(struct ksmbd_tree_connect *tcon, - struct ksmbd_file *fp) -{ - return fp->tcon != tcon; -} - -static bool session_fd_check(struct ksmbd_tree_connect *tcon, - struct ksmbd_file *fp) -{ - return false; -} - -void ksmbd_close_tree_conn_fds(struct ksmbd_work *work) -{ - int num = __close_file_table_ids(&work->sess->file_table, - work->tcon, - tree_conn_fd_check); - - atomic_sub(num, &work->conn->stats.open_files_count); -} - -void ksmbd_close_session_fds(struct ksmbd_work *work) -{ - int num = __close_file_table_ids(&work->sess->file_table, - work->tcon, - session_fd_check); - - atomic_sub(num, &work->conn->stats.open_files_count); -} - -int ksmbd_init_global_file_table(void) -{ - return ksmbd_init_file_table(&global_ft); -} - -void ksmbd_free_global_file_table(void) -{ - struct ksmbd_file *fp = NULL; - unsigned int id; - - idr_for_each_entry(global_ft.idr, fp, id) { - __ksmbd_remove_durable_fd(fp); - kmem_cache_free(filp_cache, fp); - } - - ksmbd_destroy_file_table(&global_ft); -} - -int ksmbd_init_file_table(struct ksmbd_file_table *ft) -{ - ft->idr = kzalloc(sizeof(struct idr), GFP_KERNEL); - if (!ft->idr) - return -ENOMEM; - - idr_init(ft->idr); - rwlock_init(&ft->lock); - return 0; -} - -void ksmbd_destroy_file_table(struct ksmbd_file_table *ft) -{ - if (!ft->idr) - return; - - __close_file_table_ids(ft, NULL, session_fd_check); - idr_destroy(ft->idr); - kfree(ft->idr); - ft->idr = NULL; -} - -int ksmbd_init_file_cache(void) -{ - filp_cache = kmem_cache_create("ksmbd_file_cache", - sizeof(struct ksmbd_file), 0, - SLAB_HWCACHE_ALIGN, NULL); - if (!filp_cache) - goto out; - - return 0; - -out: - pr_err("failed to allocate file cache\n"); - return -ENOMEM; -} - -void ksmbd_exit_file_cache(void) -{ - kmem_cache_destroy(filp_cache); -} diff --git a/fs/ksmbd/vfs_cache.h b/fs/ksmbd/vfs_cache.h deleted file mode 100644 index fcb13413fa8d9..0000000000000 --- a/fs/ksmbd/vfs_cache.h +++ /dev/null @@ -1,166 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0-or-later */ -/* - * Copyright (C) 2019 Samsung Electronics Co., Ltd. - */ - -#ifndef __VFS_CACHE_H__ -#define __VFS_CACHE_H__ - -#include -#include -#include -#include -#include -#include - -#include "vfs.h" - -/* Windows style file permissions for extended response */ -#define FILE_GENERIC_ALL 0x1F01FF -#define FILE_GENERIC_READ 0x120089 -#define FILE_GENERIC_WRITE 0x120116 -#define FILE_GENERIC_EXECUTE 0X1200a0 - -#define KSMBD_START_FID 0 -#define KSMBD_NO_FID (INT_MAX) -#define SMB2_NO_FID (0xFFFFFFFFFFFFFFFFULL) - -struct ksmbd_conn; -struct ksmbd_session; - -struct ksmbd_lock { - struct file_lock *fl; - struct list_head clist; - struct list_head flist; - struct list_head llist; - unsigned int flags; - int cmd; - int zero_len; - unsigned long long start; - unsigned long long end; -}; - -struct stream { - char *name; - ssize_t size; -}; - -struct ksmbd_inode { - rwlock_t m_lock; - atomic_t m_count; - atomic_t op_count; - /* opinfo count for streams */ - atomic_t sop_count; - struct inode *m_inode; - unsigned int m_flags; - struct hlist_node m_hash; - struct list_head m_fp_list; - struct list_head m_op_list; - struct oplock_info *m_opinfo; - __le32 m_fattr; -}; - -struct ksmbd_file { - struct file *filp; - u64 persistent_id; - u64 volatile_id; - - spinlock_t f_lock; - - struct ksmbd_inode *f_ci; - struct ksmbd_inode *f_parent_ci; - struct oplock_info __rcu *f_opinfo; - struct ksmbd_conn *conn; - struct ksmbd_tree_connect *tcon; - - atomic_t refcount; - __le32 daccess; - __le32 saccess; - __le32 coption; - __le32 cdoption; - __u64 create_time; - __u64 itime; - - bool is_nt_open; - bool attrib_only; - - char client_guid[16]; - char create_guid[16]; - char app_instance_id[16]; - - struct stream stream; - struct list_head node; - struct list_head blocked_works; - struct list_head lock_list; - - int durable_timeout; - - /* if ls is happening on directory, below is valid*/ - struct ksmbd_readdir_data readdir_data; - int dot_dotdot[2]; -}; - -static inline void set_ctx_actor(struct dir_context *ctx, - filldir_t actor) -{ - ctx->actor = actor; -} - -#define KSMBD_NR_OPEN_DEFAULT BITS_PER_LONG - -struct ksmbd_file_table { - rwlock_t lock; - struct idr *idr; -}; - -static inline bool has_file_id(u64 id) -{ - return id < KSMBD_NO_FID; -} - -static inline bool ksmbd_stream_fd(struct ksmbd_file *fp) -{ - return fp->stream.name != NULL; -} - -int ksmbd_init_file_table(struct ksmbd_file_table *ft); -void ksmbd_destroy_file_table(struct ksmbd_file_table *ft); -int ksmbd_close_fd(struct ksmbd_work *work, u64 id); -struct ksmbd_file *ksmbd_lookup_fd_fast(struct ksmbd_work *work, u64 id); -struct ksmbd_file *ksmbd_lookup_foreign_fd(struct ksmbd_work *work, u64 id); -struct ksmbd_file *ksmbd_lookup_fd_slow(struct ksmbd_work *work, u64 id, - u64 pid); -void ksmbd_fd_put(struct ksmbd_work *work, struct ksmbd_file *fp); -struct ksmbd_file *ksmbd_lookup_durable_fd(unsigned long long id); -struct ksmbd_file *ksmbd_lookup_fd_cguid(char *cguid); -struct ksmbd_file *ksmbd_lookup_fd_inode(struct inode *inode); -unsigned int ksmbd_open_durable_fd(struct ksmbd_file *fp); -struct ksmbd_file *ksmbd_open_fd(struct ksmbd_work *work, struct file *filp); -void ksmbd_close_tree_conn_fds(struct ksmbd_work *work); -void ksmbd_close_session_fds(struct ksmbd_work *work); -int ksmbd_close_inode_fds(struct ksmbd_work *work, struct inode *inode); -int ksmbd_init_global_file_table(void); -void ksmbd_free_global_file_table(void); -void ksmbd_set_fd_limit(unsigned long limit); - -/* - * INODE hash - */ -int __init ksmbd_inode_hash_init(void); -void ksmbd_release_inode_hash(void); - -enum KSMBD_INODE_STATUS { - KSMBD_INODE_STATUS_OK, - KSMBD_INODE_STATUS_UNKNOWN, - KSMBD_INODE_STATUS_PENDING_DELETE, -}; - -int ksmbd_query_inode_status(struct inode *inode); -bool ksmbd_inode_pending_delete(struct ksmbd_file *fp); -void ksmbd_set_inode_pending_delete(struct ksmbd_file *fp); -void ksmbd_clear_inode_pending_delete(struct ksmbd_file *fp); -void ksmbd_fd_set_delete_on_close(struct ksmbd_file *fp, - int file_info); -int ksmbd_init_file_cache(void); -void ksmbd_exit_file_cache(void); -#endif /* __VFS_CACHE_H__ */ diff --git a/fs/ksmbd/xattr.h b/fs/ksmbd/xattr.h deleted file mode 100644 index 16499ca5c82d3..0000000000000 --- a/fs/ksmbd/xattr.h +++ /dev/null @@ -1,122 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0-or-later */ -/* - * Copyright (C) 2021 Samsung Electronics Co., Ltd. - */ - -#ifndef __XATTR_H__ -#define __XATTR_H__ - -/* - * These are on-disk structures to store additional metadata into xattr to - * reproduce windows filesystem semantics. And they are encoded with NDR to - * compatible with samba's xattr meta format. The compatibility with samba - * is important because it can lose the information(file attribute, - * creation time, acls) about the existing files when switching between - * ksmbd and samba. - */ - -/* - * Dos attribute flags used for what variable is valid. - */ -enum { - XATTR_DOSINFO_ATTRIB = 0x00000001, - XATTR_DOSINFO_EA_SIZE = 0x00000002, - XATTR_DOSINFO_SIZE = 0x00000004, - XATTR_DOSINFO_ALLOC_SIZE = 0x00000008, - XATTR_DOSINFO_CREATE_TIME = 0x00000010, - XATTR_DOSINFO_CHANGE_TIME = 0x00000020, - XATTR_DOSINFO_ITIME = 0x00000040 -}; - -/* - * Dos attribute structure which is compatible with samba's one. - * Storing it into the xattr named "DOSATTRIB" separately from inode - * allows ksmbd to faithfully reproduce windows filesystem semantics - * on top of a POSIX filesystem. - */ -struct xattr_dos_attrib { - __u16 version; /* version 3 or version 4 */ - __u32 flags; /* valid flags */ - __u32 attr; /* Dos attribute */ - __u32 ea_size; /* EA size */ - __u64 size; - __u64 alloc_size; - __u64 create_time; /* File creation time */ - __u64 change_time; /* File change time */ - __u64 itime; /* Invented/Initial time */ -}; - -/* - * Enumeration is used for computing posix acl hash. - */ -enum { - SMB_ACL_TAG_INVALID = 0, - SMB_ACL_USER, - SMB_ACL_USER_OBJ, - SMB_ACL_GROUP, - SMB_ACL_GROUP_OBJ, - SMB_ACL_OTHER, - SMB_ACL_MASK -}; - -#define SMB_ACL_READ 4 -#define SMB_ACL_WRITE 2 -#define SMB_ACL_EXECUTE 1 - -struct xattr_acl_entry { - int type; - uid_t uid; - gid_t gid; - mode_t perm; -}; - -/* - * xattr_smb_acl structure is used for computing posix acl hash. - */ -struct xattr_smb_acl { - int count; - int next; - struct xattr_acl_entry entries[]; -}; - -/* 64bytes hash in xattr_ntacl is computed with sha256 */ -#define XATTR_SD_HASH_TYPE_SHA256 0x1 -#define XATTR_SD_HASH_SIZE 64 - -/* - * xattr_ntacl is used for storing ntacl and hashes. - * Hash is used for checking valid posix acl and ntacl in xattr. - */ -struct xattr_ntacl { - __u16 version; /* version 4*/ - void *sd_buf; - __u32 sd_size; - __u16 hash_type; /* hash type */ - __u8 desc[10]; /* posix_acl description */ - __u16 desc_len; - __u64 current_time; - __u8 hash[XATTR_SD_HASH_SIZE]; /* 64bytes hash for ntacl */ - __u8 posix_acl_hash[XATTR_SD_HASH_SIZE]; /* 64bytes hash for posix acl */ -}; - -/* DOS ATTRIBUITE XATTR PREFIX */ -#define DOS_ATTRIBUTE_PREFIX "DOSATTRIB" -#define DOS_ATTRIBUTE_PREFIX_LEN (sizeof(DOS_ATTRIBUTE_PREFIX) - 1) -#define XATTR_NAME_DOS_ATTRIBUTE (XATTR_USER_PREFIX DOS_ATTRIBUTE_PREFIX) -#define XATTR_NAME_DOS_ATTRIBUTE_LEN \ - (sizeof(XATTR_USER_PREFIX DOS_ATTRIBUTE_PREFIX) - 1) - -/* STREAM XATTR PREFIX */ -#define STREAM_PREFIX "DosStream." -#define STREAM_PREFIX_LEN (sizeof(STREAM_PREFIX) - 1) -#define XATTR_NAME_STREAM (XATTR_USER_PREFIX STREAM_PREFIX) -#define XATTR_NAME_STREAM_LEN (sizeof(XATTR_NAME_STREAM) - 1) - -/* SECURITY DESCRIPTOR(NTACL) XATTR PREFIX */ -#define SD_PREFIX "NTACL" -#define SD_PREFIX_LEN (sizeof(SD_PREFIX) - 1) -#define XATTR_NAME_SD (XATTR_SECURITY_PREFIX SD_PREFIX) -#define XATTR_NAME_SD_LEN \ - (sizeof(XATTR_SECURITY_PREFIX SD_PREFIX) - 1) - -#endif /* __XATTR_H__ */ diff --git a/fs/nilfs2/page.c b/fs/nilfs2/page.c index 39b7eea2642a0..7d31833e68d1f 100644 --- a/fs/nilfs2/page.c +++ b/fs/nilfs2/page.c @@ -369,7 +369,15 @@ void nilfs_clear_dirty_pages(struct address_space *mapping, bool silent) struct page *page = pvec.pages[i]; lock_page(page); - nilfs_clear_dirty_page(page, silent); + + /* + * This page may have been removed from the address + * space by truncation or invalidation when the lock + * was acquired. Skip processing in that case. + */ + if (likely(page->mapping == mapping)) + nilfs_clear_dirty_page(page, silent); + unlock_page(page); } pagevec_release(&pvec); diff --git a/fs/nilfs2/segbuf.c b/fs/nilfs2/segbuf.c index 1362ccb64ec7d..6e59dc19a7324 100644 --- a/fs/nilfs2/segbuf.c +++ b/fs/nilfs2/segbuf.c @@ -101,6 +101,12 @@ int nilfs_segbuf_extend_segsum(struct nilfs_segment_buffer *segbuf) if (unlikely(!bh)) return -ENOMEM; + lock_buffer(bh); + if (!buffer_uptodate(bh)) { + memset(bh->b_data, 0, bh->b_size); + set_buffer_uptodate(bh); + } + unlock_buffer(bh); nilfs_segbuf_add_segsum_buffer(segbuf, bh); return 0; } diff --git a/fs/nilfs2/segment.c b/fs/nilfs2/segment.c index 209e46431a5ea..6cf64023be31e 100644 --- a/fs/nilfs2/segment.c +++ b/fs/nilfs2/segment.c @@ -979,10 +979,13 @@ static void nilfs_segctor_fill_in_super_root(struct nilfs_sc_info *sci, unsigned int isz, srsz; bh_sr = NILFS_LAST_SEGBUF(&sci->sc_segbufs)->sb_super_root; + + lock_buffer(bh_sr); raw_sr = (struct nilfs_super_root *)bh_sr->b_data; isz = nilfs->ns_inode_size; srsz = NILFS_SR_BYTES(isz); + raw_sr->sr_sum = 0; /* Ensure initialization within this update */ raw_sr->sr_bytes = cpu_to_le16(srsz); raw_sr->sr_nongc_ctime = cpu_to_le64(nilfs_doing_gc() ? @@ -996,6 +999,8 @@ static void nilfs_segctor_fill_in_super_root(struct nilfs_sc_info *sci, nilfs_write_inode_common(nilfs->ns_sufile, (void *)raw_sr + NILFS_SR_SUFILE_OFFSET(isz), 1); memset((void *)raw_sr + srsz, 0, nilfs->ns_blocksize - srsz); + set_buffer_uptodate(bh_sr); + unlock_buffer(bh_sr); } static void nilfs_redirty_inodes(struct list_head *head) @@ -1778,6 +1783,7 @@ static void nilfs_abort_logs(struct list_head *logs, int err) list_for_each_entry(segbuf, logs, sb_list) { list_for_each_entry(bh, &segbuf->sb_segsum_buffers, b_assoc_buffers) { + clear_buffer_uptodate(bh); if (bh->b_page != bd_page) { if (bd_page) end_page_writeback(bd_page); @@ -1789,6 +1795,7 @@ static void nilfs_abort_logs(struct list_head *logs, int err) b_assoc_buffers) { clear_buffer_async_write(bh); if (bh == segbuf->sb_super_root) { + clear_buffer_uptodate(bh); if (bh->b_page != bd_page) { end_page_writeback(bd_page); bd_page = bh->b_page; diff --git a/fs/nilfs2/super.c b/fs/nilfs2/super.c index 77f1e5778d1c8..9ba4933087af0 100644 --- a/fs/nilfs2/super.c +++ b/fs/nilfs2/super.c @@ -372,10 +372,31 @@ static int nilfs_move_2nd_super(struct super_block *sb, loff_t sb2off) goto out; } nsbp = (void *)nsbh->b_data + offset; - memset(nsbp, 0, nilfs->ns_blocksize); + lock_buffer(nsbh); if (sb2i >= 0) { + /* + * The position of the second superblock only changes by 4KiB, + * which is larger than the maximum superblock data size + * (= 1KiB), so there is no need to use memmove() to allow + * overlap between source and destination. + */ memcpy(nsbp, nilfs->ns_sbp[sb2i], nilfs->ns_sbsize); + + /* + * Zero fill after copy to avoid overwriting in case of move + * within the same block. + */ + memset(nsbh->b_data, 0, offset); + memset((void *)nsbp + nilfs->ns_sbsize, 0, + nsbh->b_size - offset - nilfs->ns_sbsize); + } else { + memset(nsbh->b_data, 0, nsbh->b_size); + } + set_buffer_uptodate(nsbh); + unlock_buffer(nsbh); + + if (sb2i >= 0) { brelse(nilfs->ns_sbh[sb2i]); nilfs->ns_sbh[sb2i] = nsbh; nilfs->ns_sbp[sb2i] = nsbp; diff --git a/fs/smb/Kconfig b/fs/smb/Kconfig new file mode 100644 index 0000000000000..ef425789fa6ad --- /dev/null +++ b/fs/smb/Kconfig @@ -0,0 +1,11 @@ +# SPDX-License-Identifier: GPL-2.0-only +# +# smbfs configuration + +source "fs/smb/client/Kconfig" +source "fs/smb/server/Kconfig" + +config SMBFS + tristate + default y if CIFS=y || SMB_SERVER=y + default m if CIFS=m || SMB_SERVER=m diff --git a/fs/smb/Makefile b/fs/smb/Makefile new file mode 100644 index 0000000000000..9a1bf59a1a651 --- /dev/null +++ b/fs/smb/Makefile @@ -0,0 +1,5 @@ +# SPDX-License-Identifier: GPL-2.0 + +obj-$(CONFIG_SMBFS) += common/ +obj-$(CONFIG_CIFS) += client/ +obj-$(CONFIG_SMB_SERVER) += server/ diff --git a/fs/smb/client/Kconfig b/fs/smb/client/Kconfig new file mode 100644 index 0000000000000..3b7e3b9e4fd2e --- /dev/null +++ b/fs/smb/client/Kconfig @@ -0,0 +1,203 @@ +# SPDX-License-Identifier: GPL-2.0-only +config CIFS + tristate "SMB3 and CIFS support (advanced network filesystem)" + depends on INET + select NLS + select CRYPTO + select CRYPTO_MD5 + select CRYPTO_SHA256 + select CRYPTO_SHA512 + select CRYPTO_CMAC + select CRYPTO_HMAC + select CRYPTO_AEAD2 + select CRYPTO_CCM + select CRYPTO_GCM + select CRYPTO_ECB + select CRYPTO_AES + select KEYS + select DNS_RESOLVER + select ASN1 + select OID_REGISTRY + help + This is the client VFS module for the SMB3 family of NAS protocols, + (including support for the most recent, most secure dialect SMB3.1.1) + as well as for earlier dialects such as SMB2.1, SMB2 and the older + Common Internet File System (CIFS) protocol. CIFS was the successor + to the original dialect, the Server Message Block (SMB) protocol, the + native file sharing mechanism for most early PC operating systems. + + The SMB3 protocol is supported by most modern operating systems + and NAS appliances (e.g. Samba, Windows 10, Windows Server 2016, + MacOS) and even in the cloud (e.g. Microsoft Azure). + The older CIFS protocol was included in Windows NT4, 2000 and XP (and + later) as well by Samba (which provides excellent CIFS and SMB3 + server support for Linux and many other operating systems). Use of + dialects older than SMB2.1 is often discouraged on public networks. + This module also provides limited support for OS/2 and Windows ME + and similar very old servers. + + This module provides an advanced network file system client + for mounting to SMB3 (and CIFS) compliant servers. It includes + support for DFS (hierarchical name space), secure per-user + session establishment via Kerberos or NTLM or NTLMv2, RDMA + (smbdirect), advanced security features, per-share encryption, + directory leases, safe distributed caching (oplock), optional packet + signing, Unicode and other internationalization improvements. + + In general, the default dialects, SMB3 and later, enable better + performance, security and features, than would be possible with CIFS. + Note that when mounting to Samba, due to the CIFS POSIX extensions, + CIFS mounts can provide slightly better POSIX compatibility + than SMB3 mounts. SMB2/SMB3 mount options are also + slightly simpler (compared to CIFS) due to protocol improvements. + + If you need to mount to Samba, Azure, Macs or Windows from this machine, say Y. + +config CIFS_STATS2 + bool "Extended statistics" + depends on CIFS + default y + help + Enabling this option will allow more detailed statistics on SMB + request timing to be displayed in /proc/fs/cifs/DebugData and also + allow optional logging of slow responses to dmesg (depending on the + value of /proc/fs/cifs/cifsFYI). See Documentation/admin-guide/cifs/usage.rst + for more details. These additional statistics may have a minor effect + on performance and memory utilization. + + If unsure, say Y. + +config CIFS_ALLOW_INSECURE_LEGACY + bool "Support legacy servers which use less secure dialects" + depends on CIFS + default y + help + Modern dialects, SMB2.1 and later (including SMB3 and 3.1.1), have + additional security features, including protection against + man-in-the-middle attacks and stronger crypto hashes, so the use + of legacy dialects (SMB1/CIFS and SMB2.0) is discouraged. + + Disabling this option prevents users from using vers=1.0 or vers=2.0 + on mounts with cifs.ko + + If unsure, say Y. + +config CIFS_UPCALL + bool "Kerberos/SPNEGO advanced session setup" + depends on CIFS + help + Enables an upcall mechanism for CIFS which accesses userspace helper + utilities to provide SPNEGO packaged (RFC 4178) Kerberos tickets + which are needed to mount to certain secure servers (for which more + secure Kerberos authentication is required). If unsure, say Y. + +config CIFS_XATTR + bool "CIFS extended attributes" + depends on CIFS + help + Extended attributes are name:value pairs associated with inodes by + the kernel or by users (see the attr(5) manual page for details). + CIFS maps the name of extended attributes beginning with the user + namespace prefix to SMB/CIFS EAs. EAs are stored on Windows + servers without the user namespace prefix, but their names are + seen by Linux cifs clients prefaced by the user namespace prefix. + The system namespace (used by some filesystems to store ACLs) is + not supported at this time. + + If unsure, say Y. + +config CIFS_POSIX + bool "CIFS POSIX Extensions" + depends on CIFS && CIFS_ALLOW_INSECURE_LEGACY && CIFS_XATTR + help + Enabling this option will cause the cifs client to attempt to + negotiate a newer dialect with servers, such as Samba 3.0.5 + or later, that optionally can handle more POSIX like (rather + than Windows like) file behavior. It also enables + support for POSIX ACLs (getfacl and setfacl) to servers + (such as Samba 3.10 and later) which can negotiate + CIFS POSIX ACL support. If unsure, say N. + +config CIFS_DEBUG + bool "Enable CIFS debugging routines" + default y + depends on CIFS + help + Enabling this option adds helpful debugging messages to + the cifs code which increases the size of the cifs module. + If unsure, say Y. + +config CIFS_DEBUG2 + bool "Enable additional CIFS debugging routines" + depends on CIFS_DEBUG + help + Enabling this option adds a few more debugging routines + to the cifs code which slightly increases the size of + the cifs module and can cause additional logging of debug + messages in some error paths, slowing performance. This + option can be turned off unless you are debugging + cifs problems. If unsure, say N. + +config CIFS_DEBUG_DUMP_KEYS + bool "Dump encryption keys for offline decryption (Unsafe)" + depends on CIFS_DEBUG + help + Enabling this will dump the encryption and decryption keys + used to communicate on an encrypted share connection on the + console. This allows Wireshark to decrypt and dissect + encrypted network captures. Enable this carefully. + If unsure, say N. + +config CIFS_DFS_UPCALL + bool "DFS feature support" + depends on CIFS + help + Distributed File System (DFS) support is used to access shares + transparently in an enterprise name space, even if the share + moves to a different server. This feature also enables + an upcall mechanism for CIFS which contacts userspace helper + utilities to provide server name resolution (host names to + IP addresses) which is needed in order to reconnect to + servers if their addresses change or for implicit mounts of + DFS junction points. If unsure, say Y. + +config CIFS_SWN_UPCALL + bool "SWN feature support" + depends on CIFS + help + The Service Witness Protocol (SWN) is used to get notifications + from a highly available server of resource state changes. This + feature enables an upcall mechanism for CIFS which contacts a + userspace daemon to establish the DCE/RPC connection to retrieve + the cluster available interfaces and resource change notifications. + If unsure, say Y. + +config CIFS_NFSD_EXPORT + bool "Allow nfsd to export CIFS file system" + depends on CIFS && BROKEN + help + Allows NFS server to export a CIFS mounted share (nfsd over cifs) + +config CIFS_SMB_DIRECT + bool "SMB Direct support" + depends on CIFS=m && INFINIBAND && INFINIBAND_ADDR_TRANS || CIFS=y && INFINIBAND=y && INFINIBAND_ADDR_TRANS=y + help + Enables SMB Direct support for SMB 3.0, 3.02 and 3.1.1. + SMB Direct allows transferring SMB packets over RDMA. If unsure, + say Y. + +config CIFS_FSCACHE + bool "Provide CIFS client caching support" + depends on CIFS=m && FSCACHE || CIFS=y && FSCACHE=y + help + Makes CIFS FS-Cache capable. Say Y here if you want your CIFS data + to be cached locally on disk through the general filesystem cache + manager. If unsure, say N. + +config CIFS_ROOT + bool "SMB root file system (Experimental)" + depends on CIFS=y && IP_PNP + help + Enables root file system support over SMB protocol. + + Most people say N here. diff --git a/fs/smb/client/Makefile b/fs/smb/client/Makefile new file mode 100644 index 0000000000000..7c9785973f496 --- /dev/null +++ b/fs/smb/client/Makefile @@ -0,0 +1,34 @@ +# SPDX-License-Identifier: GPL-2.0 +# +# Makefile for Linux CIFS/SMB2/SMB3 VFS client +# +ccflags-y += -I$(src) # needed for trace events +obj-$(CONFIG_CIFS) += cifs.o + +cifs-y := trace.o cifsfs.o cifs_debug.o connect.o dir.o file.o \ + inode.o link.o misc.o netmisc.o smbencrypt.o transport.o \ + cached_dir.o cifs_unicode.o nterr.o cifsencrypt.o \ + readdir.o ioctl.o sess.o export.o unc.o winucase.o \ + smb2ops.o smb2maperror.o smb2transport.o \ + smb2misc.o smb2pdu.o smb2inode.o smb2file.o cifsacl.o fs_context.o \ + dns_resolve.o cifs_spnego_negtokeninit.asn1.o asn1.o + +$(obj)/asn1.o: $(obj)/cifs_spnego_negtokeninit.asn1.h + +$(obj)/cifs_spnego_negtokeninit.asn1.o: $(obj)/cifs_spnego_negtokeninit.asn1.c $(obj)/cifs_spnego_negtokeninit.asn1.h + +cifs-$(CONFIG_CIFS_XATTR) += xattr.o + +cifs-$(CONFIG_CIFS_UPCALL) += cifs_spnego.o + +cifs-$(CONFIG_CIFS_DFS_UPCALL) += cifs_dfs_ref.o dfs_cache.o + +cifs-$(CONFIG_CIFS_SWN_UPCALL) += netlink.o cifs_swn.o + +cifs-$(CONFIG_CIFS_FSCACHE) += fscache.o + +cifs-$(CONFIG_CIFS_SMB_DIRECT) += smbdirect.o + +cifs-$(CONFIG_CIFS_ROOT) += cifsroot.o + +cifs-$(CONFIG_CIFS_ALLOW_INSECURE_LEGACY) += smb1ops.o cifssmb.o diff --git a/fs/smb/client/asn1.c b/fs/smb/client/asn1.c new file mode 100644 index 0000000000000..b5724ef9f182f --- /dev/null +++ b/fs/smb/client/asn1.c @@ -0,0 +1,63 @@ +// SPDX-License-Identifier: GPL-2.0-or-later + +#include +#include +#include +#include "cifsglob.h" +#include "cifs_debug.h" +#include "cifsproto.h" +#include "cifs_spnego_negtokeninit.asn1.h" + +int +decode_negTokenInit(unsigned char *security_blob, int length, + struct TCP_Server_Info *server) +{ + if (asn1_ber_decoder(&cifs_spnego_negtokeninit_decoder, server, + security_blob, length) == 0) + return 1; + else + return 0; +} + +int cifs_gssapi_this_mech(void *context, size_t hdrlen, + unsigned char tag, const void *value, size_t vlen) +{ + enum OID oid; + + oid = look_up_OID(value, vlen); + if (oid != OID_spnego) { + char buf[50]; + + sprint_oid(value, vlen, buf, sizeof(buf)); + cifs_dbg(FYI, "Error decoding negTokenInit header: unexpected OID %s\n", + buf); + return -EBADMSG; + } + return 0; +} + +int cifs_neg_token_init_mech_type(void *context, size_t hdrlen, + unsigned char tag, + const void *value, size_t vlen) +{ + struct TCP_Server_Info *server = context; + enum OID oid; + + oid = look_up_OID(value, vlen); + if (oid == OID_mskrb5) + server->sec_mskerberos = true; + else if (oid == OID_krb5u2u) + server->sec_kerberosu2u = true; + else if (oid == OID_krb5) + server->sec_kerberos = true; + else if (oid == OID_ntlmssp) + server->sec_ntlmssp = true; + else { + char buf[50]; + + sprint_oid(value, vlen, buf, sizeof(buf)); + cifs_dbg(FYI, "Decoding negTokenInit: unsupported OID %s\n", + buf); + } + return 0; +} diff --git a/fs/smb/client/cached_dir.c b/fs/smb/client/cached_dir.c new file mode 100644 index 0000000000000..bfc964b36c72e --- /dev/null +++ b/fs/smb/client/cached_dir.c @@ -0,0 +1,606 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Functions to handle the cached directory entries + * + * Copyright (c) 2022, Ronnie Sahlberg + */ + +#include +#include "cifsglob.h" +#include "cifsproto.h" +#include "cifs_debug.h" +#include "smb2proto.h" +#include "cached_dir.h" + +static struct cached_fid *init_cached_dir(const char *path); +static void free_cached_dir(struct cached_fid *cfid); +static void smb2_close_cached_fid(struct kref *ref); + +static struct cached_fid *find_or_create_cached_dir(struct cached_fids *cfids, + const char *path, + bool lookup_only) +{ + struct cached_fid *cfid; + + spin_lock(&cfids->cfid_list_lock); + list_for_each_entry(cfid, &cfids->entries, entry) { + if (!strcmp(cfid->path, path)) { + /* + * If it doesn't have a lease it is either not yet + * fully cached or it may be in the process of + * being deleted due to a lease break. + */ + if (!cfid->has_lease) { + spin_unlock(&cfids->cfid_list_lock); + return NULL; + } + kref_get(&cfid->refcount); + spin_unlock(&cfids->cfid_list_lock); + return cfid; + } + } + if (lookup_only) { + spin_unlock(&cfids->cfid_list_lock); + return NULL; + } + if (cfids->num_entries >= MAX_CACHED_FIDS) { + spin_unlock(&cfids->cfid_list_lock); + return NULL; + } + cfid = init_cached_dir(path); + if (cfid == NULL) { + spin_unlock(&cfids->cfid_list_lock); + return NULL; + } + cfid->cfids = cfids; + cfids->num_entries++; + list_add(&cfid->entry, &cfids->entries); + cfid->on_list = true; + kref_get(&cfid->refcount); + spin_unlock(&cfids->cfid_list_lock); + return cfid; +} + +static struct dentry * +path_to_dentry(struct cifs_sb_info *cifs_sb, const char *path) +{ + struct dentry *dentry; + const char *s, *p; + char sep; + + sep = CIFS_DIR_SEP(cifs_sb); + dentry = dget(cifs_sb->root); + s = path; + + do { + struct inode *dir = d_inode(dentry); + struct dentry *child; + + if (!S_ISDIR(dir->i_mode)) { + dput(dentry); + dentry = ERR_PTR(-ENOTDIR); + break; + } + + /* skip separators */ + while (*s == sep) + s++; + if (!*s) + break; + p = s++; + /* next separator */ + while (*s && *s != sep) + s++; + + child = lookup_positive_unlocked(p, dentry, s - p); + dput(dentry); + dentry = child; + } while (!IS_ERR(dentry)); + return dentry; +} + +static const char *path_no_prefix(struct cifs_sb_info *cifs_sb, + const char *path) +{ + size_t len = 0; + + if (!*path) + return path; + + if ((cifs_sb->mnt_cifs_flags & CIFS_MOUNT_USE_PREFIX_PATH) && + cifs_sb->prepath) { + len = strlen(cifs_sb->prepath) + 1; + if (unlikely(len > strlen(path))) + return ERR_PTR(-EINVAL); + } + return path + len; +} + +/* + * Open the and cache a directory handle. + * If error then *cfid is not initialized. + */ +int open_cached_dir(unsigned int xid, struct cifs_tcon *tcon, + const char *path, + struct cifs_sb_info *cifs_sb, + bool lookup_only, struct cached_fid **ret_cfid) +{ + struct cifs_ses *ses; + struct TCP_Server_Info *server; + struct cifs_open_parms oparms; + struct smb2_create_rsp *o_rsp = NULL; + struct smb2_query_info_rsp *qi_rsp = NULL; + int resp_buftype[2]; + struct smb_rqst rqst[2]; + struct kvec rsp_iov[2]; + struct kvec open_iov[SMB2_CREATE_IOV_SIZE]; + struct kvec qi_iov[1]; + int rc, flags = 0; + __le16 *utf16_path = NULL; + u8 oplock = SMB2_OPLOCK_LEVEL_II; + struct cifs_fid *pfid; + struct dentry *dentry = NULL; + struct cached_fid *cfid; + struct cached_fids *cfids; + const char *npath; + + if (tcon == NULL || tcon->cfids == NULL || tcon->nohandlecache || + is_smb1_server(tcon->ses->server)) + return -EOPNOTSUPP; + + ses = tcon->ses; + server = ses->server; + cfids = tcon->cfids; + + if (!server->ops->new_lease_key) + return -EIO; + + if (cifs_sb->root == NULL) + return -ENOENT; + + utf16_path = cifs_convert_path_to_utf16(path, cifs_sb); + if (!utf16_path) + return -ENOMEM; + + cfid = find_or_create_cached_dir(cfids, path, lookup_only); + if (cfid == NULL) { + kfree(utf16_path); + return -ENOENT; + } + /* + * At this point we either have a lease already and we can just + * return it. If not we are guaranteed to be the only thread accessing + * this cfid. + */ + if (cfid->has_lease) { + *ret_cfid = cfid; + kfree(utf16_path); + return 0; + } + + /* + * Skip any prefix paths in @path as lookup_positive_unlocked() ends up + * calling ->lookup() which already adds those through + * build_path_from_dentry(). Also, do it earlier as we might reconnect + * below when trying to send compounded request and then potentially + * having a different prefix path (e.g. after DFS failover). + */ + npath = path_no_prefix(cifs_sb, path); + if (IS_ERR(npath)) { + rc = PTR_ERR(npath); + kfree(utf16_path); + return rc; + } + + /* + * We do not hold the lock for the open because in case + * SMB2_open needs to reconnect. + * This is safe because no other thread will be able to get a ref + * to the cfid until we have finished opening the file and (possibly) + * acquired a lease. + */ + if (smb3_encryption_required(tcon)) + flags |= CIFS_TRANSFORM_REQ; + + pfid = &cfid->fid; + server->ops->new_lease_key(pfid); + + memset(rqst, 0, sizeof(rqst)); + resp_buftype[0] = resp_buftype[1] = CIFS_NO_BUFFER; + memset(rsp_iov, 0, sizeof(rsp_iov)); + + /* Open */ + memset(&open_iov, 0, sizeof(open_iov)); + rqst[0].rq_iov = open_iov; + rqst[0].rq_nvec = SMB2_CREATE_IOV_SIZE; + + oparms = (struct cifs_open_parms) { + .tcon = tcon, + .path = path, + .create_options = cifs_create_options(cifs_sb, CREATE_NOT_FILE), + .desired_access = FILE_READ_ATTRIBUTES, + .disposition = FILE_OPEN, + .fid = pfid, + }; + + rc = SMB2_open_init(tcon, server, + &rqst[0], &oplock, &oparms, utf16_path); + if (rc) + goto oshr_free; + smb2_set_next_command(tcon, &rqst[0]); + + memset(&qi_iov, 0, sizeof(qi_iov)); + rqst[1].rq_iov = qi_iov; + rqst[1].rq_nvec = 1; + + rc = SMB2_query_info_init(tcon, server, + &rqst[1], COMPOUND_FID, + COMPOUND_FID, FILE_ALL_INFORMATION, + SMB2_O_INFO_FILE, 0, + sizeof(struct smb2_file_all_info) + + PATH_MAX * 2, 0, NULL); + if (rc) + goto oshr_free; + + smb2_set_related(&rqst[1]); + + rc = compound_send_recv(xid, ses, server, + flags, 2, rqst, + resp_buftype, rsp_iov); + if (rc) { + if (rc == -EREMCHG) { + tcon->need_reconnect = true; + pr_warn_once("server share %s deleted\n", + tcon->tree_name); + } + goto oshr_free; + } + cfid->tcon = tcon; + cfid->is_open = true; + + o_rsp = (struct smb2_create_rsp *)rsp_iov[0].iov_base; + oparms.fid->persistent_fid = o_rsp->PersistentFileId; + oparms.fid->volatile_fid = o_rsp->VolatileFileId; +#ifdef CONFIG_CIFS_DEBUG2 + oparms.fid->mid = le64_to_cpu(o_rsp->hdr.MessageId); +#endif /* CIFS_DEBUG2 */ + + if (o_rsp->OplockLevel != SMB2_OPLOCK_LEVEL_LEASE) + goto oshr_free; + + smb2_parse_contexts(server, o_rsp, + &oparms.fid->epoch, + oparms.fid->lease_key, &oplock, + NULL, NULL); + if (!(oplock & SMB2_LEASE_READ_CACHING_HE)) + goto oshr_free; + qi_rsp = (struct smb2_query_info_rsp *)rsp_iov[1].iov_base; + if (le32_to_cpu(qi_rsp->OutputBufferLength) < sizeof(struct smb2_file_all_info)) + goto oshr_free; + if (!smb2_validate_and_copy_iov( + le16_to_cpu(qi_rsp->OutputBufferOffset), + sizeof(struct smb2_file_all_info), + &rsp_iov[1], sizeof(struct smb2_file_all_info), + (char *)&cfid->file_all_info)) + cfid->file_all_info_is_valid = true; + + if (!npath[0]) + dentry = dget(cifs_sb->root); + else { + dentry = path_to_dentry(cifs_sb, npath); + if (IS_ERR(dentry)) { + rc = -ENOENT; + goto oshr_free; + } + } + cfid->dentry = dentry; + cfid->time = jiffies; + cfid->has_lease = true; + +oshr_free: + kfree(utf16_path); + SMB2_open_free(&rqst[0]); + SMB2_query_info_free(&rqst[1]); + free_rsp_buf(resp_buftype[0], rsp_iov[0].iov_base); + free_rsp_buf(resp_buftype[1], rsp_iov[1].iov_base); + spin_lock(&cfids->cfid_list_lock); + if (rc && !cfid->has_lease) { + if (cfid->on_list) { + list_del(&cfid->entry); + cfid->on_list = false; + cfids->num_entries--; + } + rc = -ENOENT; + } + spin_unlock(&cfids->cfid_list_lock); + if (!rc && !cfid->has_lease) { + /* + * We are guaranteed to have two references at this point. + * One for the caller and one for a potential lease. + * Release the Lease-ref so that the directory will be closed + * when the caller closes the cached handle. + */ + kref_put(&cfid->refcount, smb2_close_cached_fid); + } + if (rc) { + if (cfid->is_open) + SMB2_close(0, cfid->tcon, cfid->fid.persistent_fid, + cfid->fid.volatile_fid); + free_cached_dir(cfid); + cfid = NULL; + } + + if (rc == 0) { + *ret_cfid = cfid; + atomic_inc(&tcon->num_remote_opens); + } + + return rc; +} + +int open_cached_dir_by_dentry(struct cifs_tcon *tcon, + struct dentry *dentry, + struct cached_fid **ret_cfid) +{ + struct cached_fid *cfid; + struct cached_fids *cfids = tcon->cfids; + + if (cfids == NULL) + return -ENOENT; + + spin_lock(&cfids->cfid_list_lock); + list_for_each_entry(cfid, &cfids->entries, entry) { + if (dentry && cfid->dentry == dentry) { + cifs_dbg(FYI, "found a cached root file handle by dentry\n"); + kref_get(&cfid->refcount); + *ret_cfid = cfid; + spin_unlock(&cfids->cfid_list_lock); + return 0; + } + } + spin_unlock(&cfids->cfid_list_lock); + return -ENOENT; +} + +static void +smb2_close_cached_fid(struct kref *ref) +{ + struct cached_fid *cfid = container_of(ref, struct cached_fid, + refcount); + + spin_lock(&cfid->cfids->cfid_list_lock); + if (cfid->on_list) { + list_del(&cfid->entry); + cfid->on_list = false; + cfid->cfids->num_entries--; + } + spin_unlock(&cfid->cfids->cfid_list_lock); + + dput(cfid->dentry); + cfid->dentry = NULL; + + if (cfid->is_open) { + SMB2_close(0, cfid->tcon, cfid->fid.persistent_fid, + cfid->fid.volatile_fid); + atomic_dec(&cfid->tcon->num_remote_opens); + } + + free_cached_dir(cfid); +} + +void drop_cached_dir_by_name(const unsigned int xid, struct cifs_tcon *tcon, + const char *name, struct cifs_sb_info *cifs_sb) +{ + struct cached_fid *cfid = NULL; + int rc; + + rc = open_cached_dir(xid, tcon, name, cifs_sb, true, &cfid); + if (rc) { + cifs_dbg(FYI, "no cached dir found for rmdir(%s)\n", name); + return; + } + spin_lock(&cfid->cfids->cfid_list_lock); + if (cfid->has_lease) { + cfid->has_lease = false; + kref_put(&cfid->refcount, smb2_close_cached_fid); + } + spin_unlock(&cfid->cfids->cfid_list_lock); + close_cached_dir(cfid); +} + + +void close_cached_dir(struct cached_fid *cfid) +{ + kref_put(&cfid->refcount, smb2_close_cached_fid); +} + +/* + * Called from cifs_kill_sb when we unmount a share + */ +void close_all_cached_dirs(struct cifs_sb_info *cifs_sb) +{ + struct rb_root *root = &cifs_sb->tlink_tree; + struct rb_node *node; + struct cached_fid *cfid; + struct cifs_tcon *tcon; + struct tcon_link *tlink; + struct cached_fids *cfids; + + for (node = rb_first(root); node; node = rb_next(node)) { + tlink = rb_entry(node, struct tcon_link, tl_rbnode); + tcon = tlink_tcon(tlink); + if (IS_ERR(tcon)) + continue; + cfids = tcon->cfids; + if (cfids == NULL) + continue; + list_for_each_entry(cfid, &cfids->entries, entry) { + dput(cfid->dentry); + cfid->dentry = NULL; + } + } +} + +/* + * Invalidate all cached dirs when a TCON has been reset + * due to a session loss. + */ +void invalidate_all_cached_dirs(struct cifs_tcon *tcon) +{ + struct cached_fids *cfids = tcon->cfids; + struct cached_fid *cfid, *q; + LIST_HEAD(entry); + + spin_lock(&cfids->cfid_list_lock); + list_for_each_entry_safe(cfid, q, &cfids->entries, entry) { + list_move(&cfid->entry, &entry); + cfids->num_entries--; + cfid->is_open = false; + cfid->on_list = false; + /* To prevent race with smb2_cached_lease_break() */ + kref_get(&cfid->refcount); + } + spin_unlock(&cfids->cfid_list_lock); + + list_for_each_entry_safe(cfid, q, &entry, entry) { + list_del(&cfid->entry); + cancel_work_sync(&cfid->lease_break); + if (cfid->has_lease) { + /* + * We lease was never cancelled from the server so we + * need to drop the reference. + */ + spin_lock(&cfids->cfid_list_lock); + cfid->has_lease = false; + spin_unlock(&cfids->cfid_list_lock); + kref_put(&cfid->refcount, smb2_close_cached_fid); + } + /* Drop the extra reference opened above*/ + kref_put(&cfid->refcount, smb2_close_cached_fid); + } +} + +static void +smb2_cached_lease_break(struct work_struct *work) +{ + struct cached_fid *cfid = container_of(work, + struct cached_fid, lease_break); + + spin_lock(&cfid->cfids->cfid_list_lock); + cfid->has_lease = false; + spin_unlock(&cfid->cfids->cfid_list_lock); + kref_put(&cfid->refcount, smb2_close_cached_fid); +} + +int cached_dir_lease_break(struct cifs_tcon *tcon, __u8 lease_key[16]) +{ + struct cached_fids *cfids = tcon->cfids; + struct cached_fid *cfid; + + if (cfids == NULL) + return false; + + spin_lock(&cfids->cfid_list_lock); + list_for_each_entry(cfid, &cfids->entries, entry) { + if (cfid->has_lease && + !memcmp(lease_key, + cfid->fid.lease_key, + SMB2_LEASE_KEY_SIZE)) { + cfid->time = 0; + /* + * We found a lease remove it from the list + * so no threads can access it. + */ + list_del(&cfid->entry); + cfid->on_list = false; + cfids->num_entries--; + + queue_work(cifsiod_wq, + &cfid->lease_break); + spin_unlock(&cfids->cfid_list_lock); + return true; + } + } + spin_unlock(&cfids->cfid_list_lock); + return false; +} + +static struct cached_fid *init_cached_dir(const char *path) +{ + struct cached_fid *cfid; + + cfid = kzalloc(sizeof(*cfid), GFP_ATOMIC); + if (!cfid) + return NULL; + cfid->path = kstrdup(path, GFP_ATOMIC); + if (!cfid->path) { + kfree(cfid); + return NULL; + } + + INIT_WORK(&cfid->lease_break, smb2_cached_lease_break); + INIT_LIST_HEAD(&cfid->entry); + INIT_LIST_HEAD(&cfid->dirents.entries); + mutex_init(&cfid->dirents.de_mutex); + spin_lock_init(&cfid->fid_lock); + kref_init(&cfid->refcount); + return cfid; +} + +static void free_cached_dir(struct cached_fid *cfid) +{ + struct cached_dirent *dirent, *q; + + dput(cfid->dentry); + cfid->dentry = NULL; + + /* + * Delete all cached dirent names + */ + list_for_each_entry_safe(dirent, q, &cfid->dirents.entries, entry) { + list_del(&dirent->entry); + kfree(dirent->name); + kfree(dirent); + } + + kfree(cfid->path); + cfid->path = NULL; + kfree(cfid); +} + +struct cached_fids *init_cached_dirs(void) +{ + struct cached_fids *cfids; + + cfids = kzalloc(sizeof(*cfids), GFP_KERNEL); + if (!cfids) + return NULL; + spin_lock_init(&cfids->cfid_list_lock); + INIT_LIST_HEAD(&cfids->entries); + return cfids; +} + +/* + * Called from tconInfoFree when we are tearing down the tcon. + * There are no active users or open files/directories at this point. + */ +void free_cached_dirs(struct cached_fids *cfids) +{ + struct cached_fid *cfid, *q; + LIST_HEAD(entry); + + spin_lock(&cfids->cfid_list_lock); + list_for_each_entry_safe(cfid, q, &cfids->entries, entry) { + cfid->on_list = false; + cfid->is_open = false; + list_move(&cfid->entry, &entry); + } + spin_unlock(&cfids->cfid_list_lock); + + list_for_each_entry_safe(cfid, q, &entry, entry) { + list_del(&cfid->entry); + free_cached_dir(cfid); + } + + kfree(cfids); +} diff --git a/fs/smb/client/cached_dir.h b/fs/smb/client/cached_dir.h new file mode 100644 index 0000000000000..2f4e764c9ca9a --- /dev/null +++ b/fs/smb/client/cached_dir.h @@ -0,0 +1,80 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * Functions to handle the cached directory entries + * + * Copyright (c) 2022, Ronnie Sahlberg + */ + +#ifndef _CACHED_DIR_H +#define _CACHED_DIR_H + + +struct cached_dirent { + struct list_head entry; + char *name; + int namelen; + loff_t pos; + + struct cifs_fattr fattr; +}; + +struct cached_dirents { + bool is_valid:1; + bool is_failed:1; + struct dir_context *ctx; /* + * Only used to make sure we only take entries + * from a single context. Never dereferenced. + */ + struct mutex de_mutex; + int pos; /* Expected ctx->pos */ + struct list_head entries; +}; + +struct cached_fid { + struct list_head entry; + struct cached_fids *cfids; + const char *path; + bool has_lease:1; + bool is_open:1; + bool on_list:1; + bool file_all_info_is_valid:1; + unsigned long time; /* jiffies of when lease was taken */ + struct kref refcount; + struct cifs_fid fid; + spinlock_t fid_lock; + struct cifs_tcon *tcon; + struct dentry *dentry; + struct work_struct lease_break; + struct smb2_file_all_info file_all_info; + struct cached_dirents dirents; +}; + +#define MAX_CACHED_FIDS 16 +struct cached_fids { + /* Must be held when: + * - accessing the cfids->entries list + */ + spinlock_t cfid_list_lock; + int num_entries; + struct list_head entries; +}; + +extern struct cached_fids *init_cached_dirs(void); +extern void free_cached_dirs(struct cached_fids *cfids); +extern int open_cached_dir(unsigned int xid, struct cifs_tcon *tcon, + const char *path, + struct cifs_sb_info *cifs_sb, + bool lookup_only, struct cached_fid **cfid); +extern int open_cached_dir_by_dentry(struct cifs_tcon *tcon, + struct dentry *dentry, + struct cached_fid **cfid); +extern void close_cached_dir(struct cached_fid *cfid); +extern void drop_cached_dir_by_name(const unsigned int xid, + struct cifs_tcon *tcon, + const char *name, + struct cifs_sb_info *cifs_sb); +extern void close_all_cached_dirs(struct cifs_sb_info *cifs_sb); +extern void invalidate_all_cached_dirs(struct cifs_tcon *tcon); +extern int cached_dir_lease_break(struct cifs_tcon *tcon, __u8 lease_key[16]); + +#endif /* _CACHED_DIR_H */ diff --git a/fs/smb/client/cifs_debug.c b/fs/smb/client/cifs_debug.c new file mode 100644 index 0000000000000..e41154ad96afc --- /dev/null +++ b/fs/smb/client/cifs_debug.c @@ -0,0 +1,1067 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * + * Copyright (C) International Business Machines Corp., 2000,2005 + * + * Modified by Steve French (sfrench@us.ibm.com) + */ +#include +#include +#include +#include +#include +#include +#include "cifspdu.h" +#include "cifsglob.h" +#include "cifsproto.h" +#include "cifs_debug.h" +#include "cifsfs.h" +#include "fs_context.h" +#ifdef CONFIG_CIFS_DFS_UPCALL +#include "dfs_cache.h" +#endif +#ifdef CONFIG_CIFS_SMB_DIRECT +#include "smbdirect.h" +#endif +#include "cifs_swn.h" + +void +cifs_dump_mem(char *label, void *data, int length) +{ + pr_debug("%s: dump of %d bytes of data at 0x%p\n", label, length, data); + print_hex_dump(KERN_DEBUG, "", DUMP_PREFIX_OFFSET, 16, 4, + data, length, true); +} + +void cifs_dump_detail(void *buf, struct TCP_Server_Info *server) +{ +#ifdef CONFIG_CIFS_DEBUG2 + struct smb_hdr *smb = buf; + + cifs_dbg(VFS, "Cmd: %d Err: 0x%x Flags: 0x%x Flgs2: 0x%x Mid: %d Pid: %d\n", + smb->Command, smb->Status.CifsError, + smb->Flags, smb->Flags2, smb->Mid, smb->Pid); + cifs_dbg(VFS, "smb buf %p len %u\n", smb, + server->ops->calc_smb_size(smb)); +#endif /* CONFIG_CIFS_DEBUG2 */ +} + +void cifs_dump_mids(struct TCP_Server_Info *server) +{ +#ifdef CONFIG_CIFS_DEBUG2 + struct mid_q_entry *mid_entry; + + if (server == NULL) + return; + + cifs_dbg(VFS, "Dump pending requests:\n"); + spin_lock(&server->mid_lock); + list_for_each_entry(mid_entry, &server->pending_mid_q, qhead) { + cifs_dbg(VFS, "State: %d Cmd: %d Pid: %d Cbdata: %p Mid %llu\n", + mid_entry->mid_state, + le16_to_cpu(mid_entry->command), + mid_entry->pid, + mid_entry->callback_data, + mid_entry->mid); +#ifdef CONFIG_CIFS_STATS2 + cifs_dbg(VFS, "IsLarge: %d buf: %p time rcv: %ld now: %ld\n", + mid_entry->large_buf, + mid_entry->resp_buf, + mid_entry->when_received, + jiffies); +#endif /* STATS2 */ + cifs_dbg(VFS, "IsMult: %d IsEnd: %d\n", + mid_entry->multiRsp, mid_entry->multiEnd); + if (mid_entry->resp_buf) { + cifs_dump_detail(mid_entry->resp_buf, server); + cifs_dump_mem("existing buf: ", + mid_entry->resp_buf, 62); + } + } + spin_unlock(&server->mid_lock); +#endif /* CONFIG_CIFS_DEBUG2 */ +} + +#ifdef CONFIG_PROC_FS +static void cifs_debug_tcon(struct seq_file *m, struct cifs_tcon *tcon) +{ + __u32 dev_type = le32_to_cpu(tcon->fsDevInfo.DeviceType); + + seq_printf(m, "%s Mounts: %d ", tcon->tree_name, tcon->tc_count); + if (tcon->nativeFileSystem) + seq_printf(m, "Type: %s ", tcon->nativeFileSystem); + seq_printf(m, "DevInfo: 0x%x Attributes: 0x%x\n\tPathComponentMax: %d Status: %d", + le32_to_cpu(tcon->fsDevInfo.DeviceCharacteristics), + le32_to_cpu(tcon->fsAttrInfo.Attributes), + le32_to_cpu(tcon->fsAttrInfo.MaxPathNameComponentLength), + tcon->status); + if (dev_type == FILE_DEVICE_DISK) + seq_puts(m, " type: DISK "); + else if (dev_type == FILE_DEVICE_CD_ROM) + seq_puts(m, " type: CDROM "); + else + seq_printf(m, " type: %d ", dev_type); + + seq_printf(m, "Serial Number: 0x%x", tcon->vol_serial_number); + + if ((tcon->seal) || + (tcon->ses->session_flags & SMB2_SESSION_FLAG_ENCRYPT_DATA) || + (tcon->share_flags & SHI1005_FLAGS_ENCRYPT_DATA)) + seq_printf(m, " Encrypted"); + if (tcon->nocase) + seq_printf(m, " nocase"); + if (tcon->unix_ext) + seq_printf(m, " POSIX Extensions"); + if (tcon->ses->server->ops->dump_share_caps) + tcon->ses->server->ops->dump_share_caps(m, tcon); + if (tcon->use_witness) + seq_puts(m, " Witness"); + if (tcon->broken_sparse_sup) + seq_puts(m, " nosparse"); + if (tcon->need_reconnect) + seq_puts(m, "\tDISCONNECTED "); + seq_putc(m, '\n'); +} + +static void +cifs_dump_channel(struct seq_file *m, int i, struct cifs_chan *chan) +{ + struct TCP_Server_Info *server = chan->server; + + seq_printf(m, "\n\n\t\tChannel: %d ConnectionId: 0x%llx" + "\n\t\tNumber of credits: %d Dialect 0x%x" + "\n\t\tTCP status: %d Instance: %d" + "\n\t\tLocal Users To Server: %d SecMode: 0x%x Req On Wire: %d" + "\n\t\tIn Send: %d In MaxReq Wait: %d", + i+1, server->conn_id, + server->credits, + server->dialect, + server->tcpStatus, + server->reconnect_instance, + server->srv_count, + server->sec_mode, + in_flight(server), + atomic_read(&server->in_send), + atomic_read(&server->num_waiters)); +} + +static void +cifs_dump_iface(struct seq_file *m, struct cifs_server_iface *iface) +{ + struct sockaddr_in *ipv4 = (struct sockaddr_in *)&iface->sockaddr; + struct sockaddr_in6 *ipv6 = (struct sockaddr_in6 *)&iface->sockaddr; + + seq_printf(m, "\tSpeed: %zu bps\n", iface->speed); + seq_puts(m, "\t\tCapabilities: "); + if (iface->rdma_capable) + seq_puts(m, "rdma "); + if (iface->rss_capable) + seq_puts(m, "rss "); + seq_putc(m, '\n'); + if (iface->sockaddr.ss_family == AF_INET) + seq_printf(m, "\t\tIPv4: %pI4\n", &ipv4->sin_addr); + else if (iface->sockaddr.ss_family == AF_INET6) + seq_printf(m, "\t\tIPv6: %pI6\n", &ipv6->sin6_addr); + if (!iface->is_active) + seq_puts(m, "\t\t[for-cleanup]\n"); +} + +static int cifs_debug_files_proc_show(struct seq_file *m, void *v) +{ + struct TCP_Server_Info *server; + struct cifs_ses *ses; + struct cifs_tcon *tcon; + struct cifsFileInfo *cfile; + + seq_puts(m, "# Version:1\n"); + seq_puts(m, "# Format:\n"); + seq_puts(m, "# "); +#ifdef CONFIG_CIFS_DEBUG2 + seq_printf(m, " \n"); +#else + seq_printf(m, " \n"); +#endif /* CIFS_DEBUG2 */ + spin_lock(&cifs_tcp_ses_lock); + list_for_each_entry(server, &cifs_tcp_ses_list, tcp_ses_list) { + list_for_each_entry(ses, &server->smb_ses_list, smb_ses_list) { + list_for_each_entry(tcon, &ses->tcon_list, tcon_list) { + spin_lock(&tcon->open_file_lock); + list_for_each_entry(cfile, &tcon->openFileList, tlist) { + seq_printf(m, + "0x%x 0x%llx 0x%llx 0x%x %d %d %d %pd", + tcon->tid, + ses->Suid, + cfile->fid.persistent_fid, + cfile->f_flags, + cfile->count, + cfile->pid, + from_kuid(&init_user_ns, cfile->uid), + cfile->dentry); +#ifdef CONFIG_CIFS_DEBUG2 + seq_printf(m, " %llu\n", cfile->fid.mid); +#else + seq_printf(m, "\n"); +#endif /* CIFS_DEBUG2 */ + } + spin_unlock(&tcon->open_file_lock); + } + } + } + spin_unlock(&cifs_tcp_ses_lock); + seq_putc(m, '\n'); + return 0; +} + +static int cifs_debug_data_proc_show(struct seq_file *m, void *v) +{ + struct mid_q_entry *mid_entry; + struct TCP_Server_Info *server; + struct TCP_Server_Info *chan_server; + struct cifs_ses *ses; + struct cifs_tcon *tcon; + struct cifs_server_iface *iface; + int c, i, j; + + seq_puts(m, + "Display Internal CIFS Data Structures for Debugging\n" + "---------------------------------------------------\n"); + seq_printf(m, "CIFS Version %s\n", CIFS_VERSION); + seq_printf(m, "Features:"); +#ifdef CONFIG_CIFS_DFS_UPCALL + seq_printf(m, " DFS"); +#endif +#ifdef CONFIG_CIFS_FSCACHE + seq_printf(m, ",FSCACHE"); +#endif +#ifdef CONFIG_CIFS_SMB_DIRECT + seq_printf(m, ",SMB_DIRECT"); +#endif +#ifdef CONFIG_CIFS_STATS2 + seq_printf(m, ",STATS2"); +#else + seq_printf(m, ",STATS"); +#endif +#ifdef CONFIG_CIFS_DEBUG2 + seq_printf(m, ",DEBUG2"); +#elif defined(CONFIG_CIFS_DEBUG) + seq_printf(m, ",DEBUG"); +#endif +#ifdef CONFIG_CIFS_ALLOW_INSECURE_LEGACY + seq_printf(m, ",ALLOW_INSECURE_LEGACY"); +#endif +#ifdef CONFIG_CIFS_POSIX + seq_printf(m, ",CIFS_POSIX"); +#endif +#ifdef CONFIG_CIFS_UPCALL + seq_printf(m, ",UPCALL(SPNEGO)"); +#endif +#ifdef CONFIG_CIFS_XATTR + seq_printf(m, ",XATTR"); +#endif + seq_printf(m, ",ACL"); +#ifdef CONFIG_CIFS_SWN_UPCALL + seq_puts(m, ",WITNESS"); +#endif + seq_putc(m, '\n'); + seq_printf(m, "CIFSMaxBufSize: %d\n", CIFSMaxBufSize); + seq_printf(m, "Active VFS Requests: %d\n", GlobalTotalActiveXid); + + seq_printf(m, "\nServers: "); + + c = 0; + spin_lock(&cifs_tcp_ses_lock); + list_for_each_entry(server, &cifs_tcp_ses_list, tcp_ses_list) { + /* channel info will be printed as a part of sessions below */ + if (CIFS_SERVER_IS_CHAN(server)) + continue; + + c++; + seq_printf(m, "\n%d) ConnectionId: 0x%llx ", + c, server->conn_id); + + spin_lock(&server->srv_lock); + if (server->hostname) + seq_printf(m, "Hostname: %s ", server->hostname); + spin_unlock(&server->srv_lock); +#ifdef CONFIG_CIFS_SMB_DIRECT + if (!server->rdma) + goto skip_rdma; + + if (!server->smbd_conn) { + seq_printf(m, "\nSMBDirect transport not available"); + goto skip_rdma; + } + + seq_printf(m, "\nSMBDirect (in hex) protocol version: %x " + "transport status: %x", + server->smbd_conn->protocol, + server->smbd_conn->transport_status); + seq_printf(m, "\nConn receive_credit_max: %x " + "send_credit_target: %x max_send_size: %x", + server->smbd_conn->receive_credit_max, + server->smbd_conn->send_credit_target, + server->smbd_conn->max_send_size); + seq_printf(m, "\nConn max_fragmented_recv_size: %x " + "max_fragmented_send_size: %x max_receive_size:%x", + server->smbd_conn->max_fragmented_recv_size, + server->smbd_conn->max_fragmented_send_size, + server->smbd_conn->max_receive_size); + seq_printf(m, "\nConn keep_alive_interval: %x " + "max_readwrite_size: %x rdma_readwrite_threshold: %x", + server->smbd_conn->keep_alive_interval, + server->smbd_conn->max_readwrite_size, + server->smbd_conn->rdma_readwrite_threshold); + seq_printf(m, "\nDebug count_get_receive_buffer: %x " + "count_put_receive_buffer: %x count_send_empty: %x", + server->smbd_conn->count_get_receive_buffer, + server->smbd_conn->count_put_receive_buffer, + server->smbd_conn->count_send_empty); + seq_printf(m, "\nRead Queue count_reassembly_queue: %x " + "count_enqueue_reassembly_queue: %x " + "count_dequeue_reassembly_queue: %x " + "fragment_reassembly_remaining: %x " + "reassembly_data_length: %x " + "reassembly_queue_length: %x", + server->smbd_conn->count_reassembly_queue, + server->smbd_conn->count_enqueue_reassembly_queue, + server->smbd_conn->count_dequeue_reassembly_queue, + server->smbd_conn->fragment_reassembly_remaining, + server->smbd_conn->reassembly_data_length, + server->smbd_conn->reassembly_queue_length); + seq_printf(m, "\nCurrent Credits send_credits: %x " + "receive_credits: %x receive_credit_target: %x", + atomic_read(&server->smbd_conn->send_credits), + atomic_read(&server->smbd_conn->receive_credits), + server->smbd_conn->receive_credit_target); + seq_printf(m, "\nPending send_pending: %x ", + atomic_read(&server->smbd_conn->send_pending)); + seq_printf(m, "\nReceive buffers count_receive_queue: %x " + "count_empty_packet_queue: %x", + server->smbd_conn->count_receive_queue, + server->smbd_conn->count_empty_packet_queue); + seq_printf(m, "\nMR responder_resources: %x " + "max_frmr_depth: %x mr_type: %x", + server->smbd_conn->responder_resources, + server->smbd_conn->max_frmr_depth, + server->smbd_conn->mr_type); + seq_printf(m, "\nMR mr_ready_count: %x mr_used_count: %x", + atomic_read(&server->smbd_conn->mr_ready_count), + atomic_read(&server->smbd_conn->mr_used_count)); +skip_rdma: +#endif + seq_printf(m, "\nNumber of credits: %d Dialect 0x%x", + server->credits, server->dialect); + if (server->compress_algorithm == SMB3_COMPRESS_LZNT1) + seq_printf(m, " COMPRESS_LZNT1"); + else if (server->compress_algorithm == SMB3_COMPRESS_LZ77) + seq_printf(m, " COMPRESS_LZ77"); + else if (server->compress_algorithm == SMB3_COMPRESS_LZ77_HUFF) + seq_printf(m, " COMPRESS_LZ77_HUFF"); + if (server->sign) + seq_printf(m, " signed"); + if (server->posix_ext_supported) + seq_printf(m, " posix"); + if (server->nosharesock) + seq_printf(m, " nosharesock"); + + if (server->rdma) + seq_printf(m, "\nRDMA "); + seq_printf(m, "\nTCP status: %d Instance: %d" + "\nLocal Users To Server: %d SecMode: 0x%x Req On Wire: %d", + server->tcpStatus, + server->reconnect_instance, + server->srv_count, + server->sec_mode, in_flight(server)); + + seq_printf(m, "\nIn Send: %d In MaxReq Wait: %d", + atomic_read(&server->in_send), + atomic_read(&server->num_waiters)); + + seq_printf(m, "\n\n\tSessions: "); + i = 0; + list_for_each_entry(ses, &server->smb_ses_list, smb_ses_list) { + i++; + if ((ses->serverDomain == NULL) || + (ses->serverOS == NULL) || + (ses->serverNOS == NULL)) { + seq_printf(m, "\n\t%d) Address: %s Uses: %d Capability: 0x%x\tSession Status: %d ", + i, ses->ip_addr, ses->ses_count, + ses->capabilities, ses->ses_status); + if (ses->session_flags & SMB2_SESSION_FLAG_IS_GUEST) + seq_printf(m, "Guest "); + else if (ses->session_flags & SMB2_SESSION_FLAG_IS_NULL) + seq_printf(m, "Anonymous "); + } else { + seq_printf(m, + "\n\t%d) Name: %s Domain: %s Uses: %d OS: %s " + "\n\tNOS: %s\tCapability: 0x%x" + "\n\tSMB session status: %d ", + i, ses->ip_addr, ses->serverDomain, + ses->ses_count, ses->serverOS, ses->serverNOS, + ses->capabilities, ses->ses_status); + } + + seq_printf(m, "\n\tSecurity type: %s ", + get_security_type_str(server->ops->select_sectype(server, ses->sectype))); + + /* dump session id helpful for use with network trace */ + seq_printf(m, " SessionId: 0x%llx", ses->Suid); + if (ses->session_flags & SMB2_SESSION_FLAG_ENCRYPT_DATA) + seq_puts(m, " encrypted"); + if (ses->sign) + seq_puts(m, " signed"); + + seq_printf(m, "\n\tUser: %d Cred User: %d", + from_kuid(&init_user_ns, ses->linux_uid), + from_kuid(&init_user_ns, ses->cred_uid)); + + spin_lock(&ses->chan_lock); + if (CIFS_CHAN_NEEDS_RECONNECT(ses, 0)) + seq_puts(m, "\tPrimary channel: DISCONNECTED "); + if (CIFS_CHAN_IN_RECONNECT(ses, 0)) + seq_puts(m, "\t[RECONNECTING] "); + + if (ses->chan_count > 1) { + seq_printf(m, "\n\n\tExtra Channels: %zu ", + ses->chan_count-1); + for (j = 1; j < ses->chan_count; j++) { + cifs_dump_channel(m, j, &ses->chans[j]); + if (CIFS_CHAN_NEEDS_RECONNECT(ses, j)) + seq_puts(m, "\tDISCONNECTED "); + if (CIFS_CHAN_IN_RECONNECT(ses, j)) + seq_puts(m, "\t[RECONNECTING] "); + } + } + spin_unlock(&ses->chan_lock); + + seq_puts(m, "\n\n\tShares: "); + j = 0; + + seq_printf(m, "\n\t%d) IPC: ", j); + if (ses->tcon_ipc) + cifs_debug_tcon(m, ses->tcon_ipc); + else + seq_puts(m, "none\n"); + + list_for_each_entry(tcon, &ses->tcon_list, tcon_list) { + ++j; + seq_printf(m, "\n\t%d) ", j); + cifs_debug_tcon(m, tcon); + } + + spin_lock(&ses->iface_lock); + if (ses->iface_count) + seq_printf(m, "\n\n\tServer interfaces: %zu", + ses->iface_count); + j = 0; + list_for_each_entry(iface, &ses->iface_list, + iface_head) { + seq_printf(m, "\n\t%d)", ++j); + cifs_dump_iface(m, iface); + if (is_ses_using_iface(ses, iface)) + seq_puts(m, "\t\t[CONNECTED]\n"); + } + spin_unlock(&ses->iface_lock); + + seq_puts(m, "\n\n\tMIDs: "); + spin_lock(&ses->chan_lock); + for (j = 0; j < ses->chan_count; j++) { + chan_server = ses->chans[j].server; + if (!chan_server) + continue; + + if (list_empty(&chan_server->pending_mid_q)) + continue; + + seq_printf(m, "\n\tServer ConnectionId: 0x%llx", + chan_server->conn_id); + spin_lock(&chan_server->mid_lock); + list_for_each_entry(mid_entry, &chan_server->pending_mid_q, qhead) { + seq_printf(m, "\n\t\tState: %d com: %d pid: %d cbdata: %p mid %llu", + mid_entry->mid_state, + le16_to_cpu(mid_entry->command), + mid_entry->pid, + mid_entry->callback_data, + mid_entry->mid); + } + spin_unlock(&chan_server->mid_lock); + } + spin_unlock(&ses->chan_lock); + seq_puts(m, "\n--\n"); + } + if (i == 0) + seq_printf(m, "\n\t\t[NONE]"); + } + if (c == 0) + seq_printf(m, "\n\t[NONE]"); + + spin_unlock(&cifs_tcp_ses_lock); + seq_putc(m, '\n'); + cifs_swn_dump(m); + + /* BB add code to dump additional info such as TCP session info now */ + return 0; +} + +static ssize_t cifs_stats_proc_write(struct file *file, + const char __user *buffer, size_t count, loff_t *ppos) +{ + bool bv; + int rc; + struct TCP_Server_Info *server; + struct cifs_ses *ses; + struct cifs_tcon *tcon; + + rc = kstrtobool_from_user(buffer, count, &bv); + if (rc == 0) { +#ifdef CONFIG_CIFS_STATS2 + int i; + + atomic_set(&total_buf_alloc_count, 0); + atomic_set(&total_small_buf_alloc_count, 0); +#endif /* CONFIG_CIFS_STATS2 */ + atomic_set(&tcpSesReconnectCount, 0); + atomic_set(&tconInfoReconnectCount, 0); + + spin_lock(&GlobalMid_Lock); + GlobalMaxActiveXid = 0; + GlobalCurrentXid = 0; + spin_unlock(&GlobalMid_Lock); + spin_lock(&cifs_tcp_ses_lock); + list_for_each_entry(server, &cifs_tcp_ses_list, tcp_ses_list) { + server->max_in_flight = 0; +#ifdef CONFIG_CIFS_STATS2 + for (i = 0; i < NUMBER_OF_SMB2_COMMANDS; i++) { + atomic_set(&server->num_cmds[i], 0); + atomic_set(&server->smb2slowcmd[i], 0); + server->time_per_cmd[i] = 0; + server->slowest_cmd[i] = 0; + server->fastest_cmd[0] = 0; + } +#endif /* CONFIG_CIFS_STATS2 */ + list_for_each_entry(ses, &server->smb_ses_list, smb_ses_list) { + list_for_each_entry(tcon, &ses->tcon_list, tcon_list) { + atomic_set(&tcon->num_smbs_sent, 0); + spin_lock(&tcon->stat_lock); + tcon->bytes_read = 0; + tcon->bytes_written = 0; + spin_unlock(&tcon->stat_lock); + if (server->ops->clear_stats) + server->ops->clear_stats(tcon); + } + } + } + spin_unlock(&cifs_tcp_ses_lock); + } else { + return rc; + } + + return count; +} + +static int cifs_stats_proc_show(struct seq_file *m, void *v) +{ + int i; +#ifdef CONFIG_CIFS_STATS2 + int j; +#endif /* STATS2 */ + struct TCP_Server_Info *server; + struct cifs_ses *ses; + struct cifs_tcon *tcon; + + seq_printf(m, "Resources in use\nCIFS Session: %d\n", + sesInfoAllocCount.counter); + seq_printf(m, "Share (unique mount targets): %d\n", + tconInfoAllocCount.counter); + seq_printf(m, "SMB Request/Response Buffer: %d Pool size: %d\n", + buf_alloc_count.counter, + cifs_min_rcv + tcpSesAllocCount.counter); + seq_printf(m, "SMB Small Req/Resp Buffer: %d Pool size: %d\n", + small_buf_alloc_count.counter, cifs_min_small); +#ifdef CONFIG_CIFS_STATS2 + seq_printf(m, "Total Large %d Small %d Allocations\n", + atomic_read(&total_buf_alloc_count), + atomic_read(&total_small_buf_alloc_count)); +#endif /* CONFIG_CIFS_STATS2 */ + + seq_printf(m, "Operations (MIDs): %d\n", atomic_read(&mid_count)); + seq_printf(m, + "\n%d session %d share reconnects\n", + tcpSesReconnectCount.counter, tconInfoReconnectCount.counter); + + seq_printf(m, + "Total vfs operations: %d maximum at one time: %d\n", + GlobalCurrentXid, GlobalMaxActiveXid); + + i = 0; + spin_lock(&cifs_tcp_ses_lock); + list_for_each_entry(server, &cifs_tcp_ses_list, tcp_ses_list) { + seq_printf(m, "\nMax requests in flight: %d", server->max_in_flight); +#ifdef CONFIG_CIFS_STATS2 + seq_puts(m, "\nTotal time spent processing by command. Time "); + seq_printf(m, "units are jiffies (%d per second)\n", HZ); + seq_puts(m, " SMB3 CMD\tNumber\tTotal Time\tFastest\tSlowest\n"); + seq_puts(m, " --------\t------\t----------\t-------\t-------\n"); + for (j = 0; j < NUMBER_OF_SMB2_COMMANDS; j++) + seq_printf(m, " %d\t\t%d\t%llu\t\t%u\t%u\n", j, + atomic_read(&server->num_cmds[j]), + server->time_per_cmd[j], + server->fastest_cmd[j], + server->slowest_cmd[j]); + for (j = 0; j < NUMBER_OF_SMB2_COMMANDS; j++) + if (atomic_read(&server->smb2slowcmd[j])) { + spin_lock(&server->srv_lock); + seq_printf(m, " %d slow responses from %s for command %d\n", + atomic_read(&server->smb2slowcmd[j]), + server->hostname, j); + spin_unlock(&server->srv_lock); + } +#endif /* STATS2 */ + list_for_each_entry(ses, &server->smb_ses_list, smb_ses_list) { + list_for_each_entry(tcon, &ses->tcon_list, tcon_list) { + i++; + seq_printf(m, "\n%d) %s", i, tcon->tree_name); + if (tcon->need_reconnect) + seq_puts(m, "\tDISCONNECTED "); + seq_printf(m, "\nSMBs: %d", + atomic_read(&tcon->num_smbs_sent)); + if (server->ops->print_stats) + server->ops->print_stats(m, tcon); + } + } + } + spin_unlock(&cifs_tcp_ses_lock); + + seq_putc(m, '\n'); + return 0; +} + +static int cifs_stats_proc_open(struct inode *inode, struct file *file) +{ + return single_open(file, cifs_stats_proc_show, NULL); +} + +static const struct proc_ops cifs_stats_proc_ops = { + .proc_open = cifs_stats_proc_open, + .proc_read = seq_read, + .proc_lseek = seq_lseek, + .proc_release = single_release, + .proc_write = cifs_stats_proc_write, +}; + +#ifdef CONFIG_CIFS_SMB_DIRECT +#define PROC_FILE_DEFINE(name) \ +static ssize_t name##_write(struct file *file, const char __user *buffer, \ + size_t count, loff_t *ppos) \ +{ \ + int rc; \ + rc = kstrtoint_from_user(buffer, count, 10, & name); \ + if (rc) \ + return rc; \ + return count; \ +} \ +static int name##_proc_show(struct seq_file *m, void *v) \ +{ \ + seq_printf(m, "%d\n", name ); \ + return 0; \ +} \ +static int name##_open(struct inode *inode, struct file *file) \ +{ \ + return single_open(file, name##_proc_show, NULL); \ +} \ +\ +static const struct proc_ops cifs_##name##_proc_fops = { \ + .proc_open = name##_open, \ + .proc_read = seq_read, \ + .proc_lseek = seq_lseek, \ + .proc_release = single_release, \ + .proc_write = name##_write, \ +} + +PROC_FILE_DEFINE(rdma_readwrite_threshold); +PROC_FILE_DEFINE(smbd_max_frmr_depth); +PROC_FILE_DEFINE(smbd_keep_alive_interval); +PROC_FILE_DEFINE(smbd_max_receive_size); +PROC_FILE_DEFINE(smbd_max_fragmented_recv_size); +PROC_FILE_DEFINE(smbd_max_send_size); +PROC_FILE_DEFINE(smbd_send_credit_target); +PROC_FILE_DEFINE(smbd_receive_credit_max); +#endif + +static struct proc_dir_entry *proc_fs_cifs; +static const struct proc_ops cifsFYI_proc_ops; +static const struct proc_ops cifs_lookup_cache_proc_ops; +static const struct proc_ops traceSMB_proc_ops; +static const struct proc_ops cifs_security_flags_proc_ops; +static const struct proc_ops cifs_linux_ext_proc_ops; +static const struct proc_ops cifs_mount_params_proc_ops; + +void +cifs_proc_init(void) +{ + proc_fs_cifs = proc_mkdir("fs/cifs", NULL); + if (proc_fs_cifs == NULL) + return; + + proc_create_single("DebugData", 0, proc_fs_cifs, + cifs_debug_data_proc_show); + + proc_create_single("open_files", 0400, proc_fs_cifs, + cifs_debug_files_proc_show); + + proc_create("Stats", 0644, proc_fs_cifs, &cifs_stats_proc_ops); + proc_create("cifsFYI", 0644, proc_fs_cifs, &cifsFYI_proc_ops); + proc_create("traceSMB", 0644, proc_fs_cifs, &traceSMB_proc_ops); + proc_create("LinuxExtensionsEnabled", 0644, proc_fs_cifs, + &cifs_linux_ext_proc_ops); + proc_create("SecurityFlags", 0644, proc_fs_cifs, + &cifs_security_flags_proc_ops); + proc_create("LookupCacheEnabled", 0644, proc_fs_cifs, + &cifs_lookup_cache_proc_ops); + + proc_create("mount_params", 0444, proc_fs_cifs, &cifs_mount_params_proc_ops); + +#ifdef CONFIG_CIFS_DFS_UPCALL + proc_create("dfscache", 0644, proc_fs_cifs, &dfscache_proc_ops); +#endif + +#ifdef CONFIG_CIFS_SMB_DIRECT + proc_create("rdma_readwrite_threshold", 0644, proc_fs_cifs, + &cifs_rdma_readwrite_threshold_proc_fops); + proc_create("smbd_max_frmr_depth", 0644, proc_fs_cifs, + &cifs_smbd_max_frmr_depth_proc_fops); + proc_create("smbd_keep_alive_interval", 0644, proc_fs_cifs, + &cifs_smbd_keep_alive_interval_proc_fops); + proc_create("smbd_max_receive_size", 0644, proc_fs_cifs, + &cifs_smbd_max_receive_size_proc_fops); + proc_create("smbd_max_fragmented_recv_size", 0644, proc_fs_cifs, + &cifs_smbd_max_fragmented_recv_size_proc_fops); + proc_create("smbd_max_send_size", 0644, proc_fs_cifs, + &cifs_smbd_max_send_size_proc_fops); + proc_create("smbd_send_credit_target", 0644, proc_fs_cifs, + &cifs_smbd_send_credit_target_proc_fops); + proc_create("smbd_receive_credit_max", 0644, proc_fs_cifs, + &cifs_smbd_receive_credit_max_proc_fops); +#endif +} + +void +cifs_proc_clean(void) +{ + if (proc_fs_cifs == NULL) + return; + + remove_proc_entry("DebugData", proc_fs_cifs); + remove_proc_entry("open_files", proc_fs_cifs); + remove_proc_entry("cifsFYI", proc_fs_cifs); + remove_proc_entry("traceSMB", proc_fs_cifs); + remove_proc_entry("Stats", proc_fs_cifs); + remove_proc_entry("SecurityFlags", proc_fs_cifs); + remove_proc_entry("LinuxExtensionsEnabled", proc_fs_cifs); + remove_proc_entry("LookupCacheEnabled", proc_fs_cifs); + remove_proc_entry("mount_params", proc_fs_cifs); + +#ifdef CONFIG_CIFS_DFS_UPCALL + remove_proc_entry("dfscache", proc_fs_cifs); +#endif +#ifdef CONFIG_CIFS_SMB_DIRECT + remove_proc_entry("rdma_readwrite_threshold", proc_fs_cifs); + remove_proc_entry("smbd_max_frmr_depth", proc_fs_cifs); + remove_proc_entry("smbd_keep_alive_interval", proc_fs_cifs); + remove_proc_entry("smbd_max_receive_size", proc_fs_cifs); + remove_proc_entry("smbd_max_fragmented_recv_size", proc_fs_cifs); + remove_proc_entry("smbd_max_send_size", proc_fs_cifs); + remove_proc_entry("smbd_send_credit_target", proc_fs_cifs); + remove_proc_entry("smbd_receive_credit_max", proc_fs_cifs); +#endif + remove_proc_entry("fs/cifs", NULL); +} + +static int cifsFYI_proc_show(struct seq_file *m, void *v) +{ + seq_printf(m, "%d\n", cifsFYI); + return 0; +} + +static int cifsFYI_proc_open(struct inode *inode, struct file *file) +{ + return single_open(file, cifsFYI_proc_show, NULL); +} + +static ssize_t cifsFYI_proc_write(struct file *file, const char __user *buffer, + size_t count, loff_t *ppos) +{ + char c[2] = { '\0' }; + bool bv; + int rc; + + rc = get_user(c[0], buffer); + if (rc) + return rc; + if (strtobool(c, &bv) == 0) + cifsFYI = bv; + else if ((c[0] > '1') && (c[0] <= '9')) + cifsFYI = (int) (c[0] - '0'); /* see cifs_debug.h for meanings */ + else + return -EINVAL; + + return count; +} + +static const struct proc_ops cifsFYI_proc_ops = { + .proc_open = cifsFYI_proc_open, + .proc_read = seq_read, + .proc_lseek = seq_lseek, + .proc_release = single_release, + .proc_write = cifsFYI_proc_write, +}; + +static int cifs_linux_ext_proc_show(struct seq_file *m, void *v) +{ + seq_printf(m, "%d\n", linuxExtEnabled); + return 0; +} + +static int cifs_linux_ext_proc_open(struct inode *inode, struct file *file) +{ + return single_open(file, cifs_linux_ext_proc_show, NULL); +} + +static ssize_t cifs_linux_ext_proc_write(struct file *file, + const char __user *buffer, size_t count, loff_t *ppos) +{ + int rc; + + rc = kstrtobool_from_user(buffer, count, &linuxExtEnabled); + if (rc) + return rc; + + return count; +} + +static const struct proc_ops cifs_linux_ext_proc_ops = { + .proc_open = cifs_linux_ext_proc_open, + .proc_read = seq_read, + .proc_lseek = seq_lseek, + .proc_release = single_release, + .proc_write = cifs_linux_ext_proc_write, +}; + +static int cifs_lookup_cache_proc_show(struct seq_file *m, void *v) +{ + seq_printf(m, "%d\n", lookupCacheEnabled); + return 0; +} + +static int cifs_lookup_cache_proc_open(struct inode *inode, struct file *file) +{ + return single_open(file, cifs_lookup_cache_proc_show, NULL); +} + +static ssize_t cifs_lookup_cache_proc_write(struct file *file, + const char __user *buffer, size_t count, loff_t *ppos) +{ + int rc; + + rc = kstrtobool_from_user(buffer, count, &lookupCacheEnabled); + if (rc) + return rc; + + return count; +} + +static const struct proc_ops cifs_lookup_cache_proc_ops = { + .proc_open = cifs_lookup_cache_proc_open, + .proc_read = seq_read, + .proc_lseek = seq_lseek, + .proc_release = single_release, + .proc_write = cifs_lookup_cache_proc_write, +}; + +static int traceSMB_proc_show(struct seq_file *m, void *v) +{ + seq_printf(m, "%d\n", traceSMB); + return 0; +} + +static int traceSMB_proc_open(struct inode *inode, struct file *file) +{ + return single_open(file, traceSMB_proc_show, NULL); +} + +static ssize_t traceSMB_proc_write(struct file *file, const char __user *buffer, + size_t count, loff_t *ppos) +{ + int rc; + + rc = kstrtobool_from_user(buffer, count, &traceSMB); + if (rc) + return rc; + + return count; +} + +static const struct proc_ops traceSMB_proc_ops = { + .proc_open = traceSMB_proc_open, + .proc_read = seq_read, + .proc_lseek = seq_lseek, + .proc_release = single_release, + .proc_write = traceSMB_proc_write, +}; + +static int cifs_security_flags_proc_show(struct seq_file *m, void *v) +{ + seq_printf(m, "0x%x\n", global_secflags); + return 0; +} + +static int cifs_security_flags_proc_open(struct inode *inode, struct file *file) +{ + return single_open(file, cifs_security_flags_proc_show, NULL); +} + +/* + * Ensure that if someone sets a MUST flag, that we disable all other MAY + * flags except for the ones corresponding to the given MUST flag. If there are + * multiple MUST flags, then try to prefer more secure ones. + */ +static void +cifs_security_flags_handle_must_flags(unsigned int *flags) +{ + unsigned int signflags = *flags & CIFSSEC_MUST_SIGN; + + if ((*flags & CIFSSEC_MUST_KRB5) == CIFSSEC_MUST_KRB5) + *flags = CIFSSEC_MUST_KRB5; + else if ((*flags & CIFSSEC_MUST_NTLMSSP) == CIFSSEC_MUST_NTLMSSP) + *flags = CIFSSEC_MUST_NTLMSSP; + else if ((*flags & CIFSSEC_MUST_NTLMV2) == CIFSSEC_MUST_NTLMV2) + *flags = CIFSSEC_MUST_NTLMV2; + + *flags |= signflags; +} + +static ssize_t cifs_security_flags_proc_write(struct file *file, + const char __user *buffer, size_t count, loff_t *ppos) +{ + int rc; + unsigned int flags; + char flags_string[12]; + bool bv; + + if ((count < 1) || (count > 11)) + return -EINVAL; + + memset(flags_string, 0, 12); + + if (copy_from_user(flags_string, buffer, count)) + return -EFAULT; + + if (count < 3) { + /* single char or single char followed by null */ + if (strtobool(flags_string, &bv) == 0) { + global_secflags = bv ? CIFSSEC_MAX : CIFSSEC_DEF; + return count; + } else if (!isdigit(flags_string[0])) { + cifs_dbg(VFS, "Invalid SecurityFlags: %s\n", + flags_string); + return -EINVAL; + } + } + + /* else we have a number */ + rc = kstrtouint(flags_string, 0, &flags); + if (rc) { + cifs_dbg(VFS, "Invalid SecurityFlags: %s\n", + flags_string); + return rc; + } + + cifs_dbg(FYI, "sec flags 0x%x\n", flags); + + if (flags == 0) { + cifs_dbg(VFS, "Invalid SecurityFlags: %s\n", flags_string); + return -EINVAL; + } + + if (flags & ~CIFSSEC_MASK) { + cifs_dbg(VFS, "Unsupported security flags: 0x%x\n", + flags & ~CIFSSEC_MASK); + return -EINVAL; + } + + cifs_security_flags_handle_must_flags(&flags); + + /* flags look ok - update the global security flags for cifs module */ + global_secflags = flags; + if (global_secflags & CIFSSEC_MUST_SIGN) { + /* requiring signing implies signing is allowed */ + global_secflags |= CIFSSEC_MAY_SIGN; + cifs_dbg(FYI, "packet signing now required\n"); + } else if ((global_secflags & CIFSSEC_MAY_SIGN) == 0) { + cifs_dbg(FYI, "packet signing disabled\n"); + } + /* BB should we turn on MAY flags for other MUST options? */ + return count; +} + +static const struct proc_ops cifs_security_flags_proc_ops = { + .proc_open = cifs_security_flags_proc_open, + .proc_read = seq_read, + .proc_lseek = seq_lseek, + .proc_release = single_release, + .proc_write = cifs_security_flags_proc_write, +}; + +/* To make it easier to debug, can help to show mount params */ +static int cifs_mount_params_proc_show(struct seq_file *m, void *v) +{ + const struct fs_parameter_spec *p; + const char *type; + + for (p = smb3_fs_parameters; p->name; p++) { + /* cannot use switch with pointers... */ + if (!p->type) { + if (p->flags == fs_param_neg_with_no) + type = "noflag"; + else + type = "flag"; + } else if (p->type == fs_param_is_bool) + type = "bool"; + else if (p->type == fs_param_is_u32) + type = "u32"; + else if (p->type == fs_param_is_u64) + type = "u64"; + else if (p->type == fs_param_is_string) + type = "string"; + else + type = "unknown"; + + seq_printf(m, "%s:%s\n", p->name, type); + } + + return 0; +} + +static int cifs_mount_params_proc_open(struct inode *inode, struct file *file) +{ + return single_open(file, cifs_mount_params_proc_show, NULL); +} + +static const struct proc_ops cifs_mount_params_proc_ops = { + .proc_open = cifs_mount_params_proc_open, + .proc_read = seq_read, + .proc_lseek = seq_lseek, + .proc_release = single_release, + /* No need for write for now */ + /* .proc_write = cifs_mount_params_proc_write, */ +}; + +#else +inline void cifs_proc_init(void) +{ +} + +inline void cifs_proc_clean(void) +{ +} +#endif /* PROC_FS */ diff --git a/fs/smb/client/cifs_debug.h b/fs/smb/client/cifs_debug.h new file mode 100644 index 0000000000000..ce5cfd236fdb8 --- /dev/null +++ b/fs/smb/client/cifs_debug.h @@ -0,0 +1,160 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + * + * Copyright (c) International Business Machines Corp., 2000,2002 + * Modified by Steve French (sfrench@us.ibm.com) + */ + +#ifndef _H_CIFS_DEBUG +#define _H_CIFS_DEBUG + +#ifdef pr_fmt +#undef pr_fmt +#endif + +#define pr_fmt(fmt) "CIFS: " fmt + +void cifs_dump_mem(char *label, void *data, int length); +void cifs_dump_detail(void *buf, struct TCP_Server_Info *ptcp_info); +void cifs_dump_mids(struct TCP_Server_Info *); +extern bool traceSMB; /* flag which enables the function below */ +void dump_smb(void *, int); +#define CIFS_INFO 0x01 +#define CIFS_RC 0x02 +#define CIFS_TIMER 0x04 + +#define VFS 1 +#define FYI 2 +extern int cifsFYI; +#ifdef CONFIG_CIFS_DEBUG2 +#define NOISY 4 +#else +#define NOISY 0 +#endif +#define ONCE 8 + +/* + * debug ON + * -------- + */ +#ifdef CONFIG_CIFS_DEBUG + + +/* + * When adding tracepoints and debug messages we have various choices. + * Some considerations: + * + * Use cifs_dbg(VFS, ...) for things we always want logged, and the user to see + * cifs_info(...) slightly less important, admin can filter via loglevel > 6 + * cifs_dbg(FYI, ...) minor debugging messages, off by default + * trace_smb3_* ftrace functions are preferred for complex debug messages + * intended for developers or experienced admins, off by default + */ + +/* Information level messages, minor events */ +#define cifs_info_func(ratefunc, fmt, ...) \ + pr_info_ ## ratefunc(fmt, ##__VA_ARGS__) + +#define cifs_info(fmt, ...) \ + cifs_info_func(ratelimited, fmt, ##__VA_ARGS__) + +/* information message: e.g., configuration, major event */ +#define cifs_dbg_func(ratefunc, type, fmt, ...) \ +do { \ + if ((type) & FYI && cifsFYI & CIFS_INFO) { \ + pr_debug_ ## ratefunc("%s: " fmt, \ + __FILE__, ##__VA_ARGS__); \ + } else if ((type) & VFS) { \ + pr_err_ ## ratefunc("VFS: " fmt, ##__VA_ARGS__); \ + } else if ((type) & NOISY && (NOISY != 0)) { \ + pr_debug_ ## ratefunc(fmt, ##__VA_ARGS__); \ + } \ +} while (0) + +#define cifs_dbg(type, fmt, ...) \ +do { \ + if ((type) & ONCE) \ + cifs_dbg_func(once, type, fmt, ##__VA_ARGS__); \ + else \ + cifs_dbg_func(ratelimited, type, fmt, ##__VA_ARGS__); \ +} while (0) + +#define cifs_server_dbg_func(ratefunc, type, fmt, ...) \ +do { \ + spin_lock(&server->srv_lock); \ + if ((type) & FYI && cifsFYI & CIFS_INFO) { \ + pr_debug_ ## ratefunc("%s: \\\\%s " fmt, \ + __FILE__, server->hostname, \ + ##__VA_ARGS__); \ + } else if ((type) & VFS) { \ + pr_err_ ## ratefunc("VFS: \\\\%s " fmt, \ + server->hostname, ##__VA_ARGS__); \ + } else if ((type) & NOISY && (NOISY != 0)) { \ + pr_debug_ ## ratefunc("\\\\%s " fmt, \ + server->hostname, ##__VA_ARGS__); \ + } \ + spin_unlock(&server->srv_lock); \ +} while (0) + +#define cifs_server_dbg(type, fmt, ...) \ +do { \ + if ((type) & ONCE) \ + cifs_server_dbg_func(once, type, fmt, ##__VA_ARGS__); \ + else \ + cifs_server_dbg_func(ratelimited, type, fmt, \ + ##__VA_ARGS__); \ +} while (0) + +#define cifs_tcon_dbg_func(ratefunc, type, fmt, ...) \ +do { \ + const char *tn = ""; \ + if (tcon && tcon->tree_name) \ + tn = tcon->tree_name; \ + if ((type) & FYI && cifsFYI & CIFS_INFO) { \ + pr_debug_ ## ratefunc("%s: %s " fmt, \ + __FILE__, tn, ##__VA_ARGS__); \ + } else if ((type) & VFS) { \ + pr_err_ ## ratefunc("VFS: %s " fmt, tn, ##__VA_ARGS__); \ + } else if ((type) & NOISY && (NOISY != 0)) { \ + pr_debug_ ## ratefunc("%s " fmt, tn, ##__VA_ARGS__); \ + } \ +} while (0) + +#define cifs_tcon_dbg(type, fmt, ...) \ +do { \ + if ((type) & ONCE) \ + cifs_tcon_dbg_func(once, type, fmt, ##__VA_ARGS__); \ + else \ + cifs_tcon_dbg_func(ratelimited, type, fmt, \ + ##__VA_ARGS__); \ +} while (0) + +/* + * debug OFF + * --------- + */ +#else /* _CIFS_DEBUG */ +#define cifs_dbg(type, fmt, ...) \ +do { \ + if (0) \ + pr_debug(fmt, ##__VA_ARGS__); \ +} while (0) + +#define cifs_server_dbg(type, fmt, ...) \ +do { \ + if (0) \ + pr_debug("\\\\%s " fmt, \ + server->hostname, ##__VA_ARGS__); \ +} while (0) + +#define cifs_tcon_dbg(type, fmt, ...) \ +do { \ + if (0) \ + pr_debug("%s " fmt, tcon->tree_name, ##__VA_ARGS__); \ +} while (0) + +#define cifs_info(fmt, ...) \ + pr_info(fmt, ##__VA_ARGS__) +#endif + +#endif /* _H_CIFS_DEBUG */ diff --git a/fs/smb/client/cifs_dfs_ref.c b/fs/smb/client/cifs_dfs_ref.c new file mode 100644 index 0000000000000..b0864da9ef434 --- /dev/null +++ b/fs/smb/client/cifs_dfs_ref.c @@ -0,0 +1,374 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Contains the CIFS DFS referral mounting routines used for handling + * traversal via DFS junction point + * + * Copyright (c) 2007 Igor Mammedov + * Copyright (C) International Business Machines Corp., 2008 + * Author(s): Igor Mammedov (niallain@gmail.com) + * Steve French (sfrench@us.ibm.com) + */ + +#include +#include +#include +#include +#include +#include +#include +#include "cifsglob.h" +#include "cifsproto.h" +#include "cifsfs.h" +#include "dns_resolve.h" +#include "cifs_debug.h" +#include "cifs_unicode.h" +#include "dfs_cache.h" +#include "fs_context.h" + +static LIST_HEAD(cifs_dfs_automount_list); + +static void cifs_dfs_expire_automounts(struct work_struct *work); +static DECLARE_DELAYED_WORK(cifs_dfs_automount_task, + cifs_dfs_expire_automounts); +static int cifs_dfs_mountpoint_expiry_timeout = 500 * HZ; + +static void cifs_dfs_expire_automounts(struct work_struct *work) +{ + struct list_head *list = &cifs_dfs_automount_list; + + mark_mounts_for_expiry(list); + if (!list_empty(list)) + schedule_delayed_work(&cifs_dfs_automount_task, + cifs_dfs_mountpoint_expiry_timeout); +} + +void cifs_dfs_release_automount_timer(void) +{ + BUG_ON(!list_empty(&cifs_dfs_automount_list)); + cancel_delayed_work_sync(&cifs_dfs_automount_task); +} + +/** + * cifs_build_devname - build a devicename from a UNC and optional prepath + * @nodename: pointer to UNC string + * @prepath: pointer to prefixpath (or NULL if there isn't one) + * + * Build a new cifs devicename after chasing a DFS referral. Allocate a buffer + * big enough to hold the final thing. Copy the UNC from the nodename, and + * concatenate the prepath onto the end of it if there is one. + * + * Returns pointer to the built string, or a ERR_PTR. Caller is responsible + * for freeing the returned string. + */ +static char * +cifs_build_devname(char *nodename, const char *prepath) +{ + size_t pplen; + size_t unclen; + char *dev; + char *pos; + + /* skip over any preceding delimiters */ + nodename += strspn(nodename, "\\"); + if (!*nodename) + return ERR_PTR(-EINVAL); + + /* get length of UNC and set pos to last char */ + unclen = strlen(nodename); + pos = nodename + unclen - 1; + + /* trim off any trailing delimiters */ + while (*pos == '\\') { + --pos; + --unclen; + } + + /* allocate a buffer: + * +2 for preceding "//" + * +1 for delimiter between UNC and prepath + * +1 for trailing NULL + */ + pplen = prepath ? strlen(prepath) : 0; + dev = kmalloc(2 + unclen + 1 + pplen + 1, GFP_KERNEL); + if (!dev) + return ERR_PTR(-ENOMEM); + + pos = dev; + /* add the initial "//" */ + *pos = '/'; + ++pos; + *pos = '/'; + ++pos; + + /* copy in the UNC portion from referral */ + memcpy(pos, nodename, unclen); + pos += unclen; + + /* copy the prefixpath remainder (if there is one) */ + if (pplen) { + *pos = '/'; + ++pos; + memcpy(pos, prepath, pplen); + pos += pplen; + } + + /* NULL terminator */ + *pos = '\0'; + + convert_delimiter(dev, '/'); + return dev; +} + + +/** + * cifs_compose_mount_options - creates mount options for referral + * @sb_mountdata: parent/root DFS mount options (template) + * @fullpath: full path in UNC format + * @ref: optional server's referral + * @devname: return the built cifs device name if passed pointer not NULL + * creates mount options for submount based on template options sb_mountdata + * and replacing unc,ip,prefixpath options with ones we've got form ref_unc. + * + * Returns: pointer to new mount options or ERR_PTR. + * Caller is responsible for freeing returned value if it is not error. + */ +char *cifs_compose_mount_options(const char *sb_mountdata, + const char *fullpath, + const struct dfs_info3_param *ref, + char **devname) +{ + int rc; + char *name; + char *mountdata = NULL; + const char *prepath = NULL; + int md_len; + char *tkn_e; + char *srvIP = NULL; + char sep = ','; + int off, noff; + + if (sb_mountdata == NULL) + return ERR_PTR(-EINVAL); + + if (ref) { + if (WARN_ON_ONCE(!ref->node_name || ref->path_consumed < 0)) + return ERR_PTR(-EINVAL); + + if (strlen(fullpath) - ref->path_consumed) { + prepath = fullpath + ref->path_consumed; + /* skip initial delimiter */ + if (*prepath == '/' || *prepath == '\\') + prepath++; + } + + name = cifs_build_devname(ref->node_name, prepath); + if (IS_ERR(name)) { + rc = PTR_ERR(name); + name = NULL; + goto compose_mount_options_err; + } + } else { + name = cifs_build_devname((char *)fullpath, NULL); + if (IS_ERR(name)) { + rc = PTR_ERR(name); + name = NULL; + goto compose_mount_options_err; + } + } + + rc = dns_resolve_server_name_to_ip(name, &srvIP, NULL); + if (rc < 0) { + cifs_dbg(FYI, "%s: Failed to resolve server part of %s to IP: %d\n", + __func__, name, rc); + goto compose_mount_options_err; + } + + /* + * In most cases, we'll be building a shorter string than the original, + * but we do have to assume that the address in the ip= option may be + * much longer than the original. Add the max length of an address + * string to the length of the original string to allow for worst case. + */ + md_len = strlen(sb_mountdata) + INET6_ADDRSTRLEN; + mountdata = kzalloc(md_len + sizeof("ip=") + 1, GFP_KERNEL); + if (mountdata == NULL) { + rc = -ENOMEM; + goto compose_mount_options_err; + } + + /* copy all options except of unc,ip,prefixpath */ + off = 0; + if (strncmp(sb_mountdata, "sep=", 4) == 0) { + sep = sb_mountdata[4]; + strncpy(mountdata, sb_mountdata, 5); + off += 5; + } + + do { + tkn_e = strchr(sb_mountdata + off, sep); + if (tkn_e == NULL) + noff = strlen(sb_mountdata + off); + else + noff = tkn_e - (sb_mountdata + off) + 1; + + if (strncasecmp(sb_mountdata + off, "cruid=", 6) == 0) { + off += noff; + continue; + } + if (strncasecmp(sb_mountdata + off, "unc=", 4) == 0) { + off += noff; + continue; + } + if (strncasecmp(sb_mountdata + off, "ip=", 3) == 0) { + off += noff; + continue; + } + if (strncasecmp(sb_mountdata + off, "prefixpath=", 11) == 0) { + off += noff; + continue; + } + strncat(mountdata, sb_mountdata + off, noff); + off += noff; + } while (tkn_e); + strcat(mountdata, sb_mountdata + off); + mountdata[md_len] = '\0'; + + /* copy new IP and ref share name */ + if (mountdata[strlen(mountdata) - 1] != sep) + strncat(mountdata, &sep, 1); + strcat(mountdata, "ip="); + strcat(mountdata, srvIP); + + if (devname) + *devname = name; + else + kfree(name); + + /*cifs_dbg(FYI, "%s: parent mountdata: %s\n", __func__, sb_mountdata);*/ + /*cifs_dbg(FYI, "%s: submount mountdata: %s\n", __func__, mountdata );*/ + +compose_mount_options_out: + kfree(srvIP); + return mountdata; + +compose_mount_options_err: + kfree(mountdata); + mountdata = ERR_PTR(rc); + kfree(name); + goto compose_mount_options_out; +} + +/** + * cifs_dfs_do_mount - mounts specified path using DFS full path + * + * Always pass down @fullpath to smb3_do_mount() so we can use the root server + * to perform failover in case we failed to connect to the first target in the + * referral. + * + * @mntpt: directory entry for the path we are trying to automount + * @cifs_sb: parent/root superblock + * @fullpath: full path in UNC format + */ +static struct vfsmount *cifs_dfs_do_mount(struct dentry *mntpt, + struct cifs_sb_info *cifs_sb, + const char *fullpath) +{ + struct vfsmount *mnt; + char *mountdata; + char *devname; + + devname = kstrdup(fullpath, GFP_KERNEL); + if (!devname) + return ERR_PTR(-ENOMEM); + + convert_delimiter(devname, '/'); + + /* TODO: change to call fs_context_for_mount(), fill in context directly, call fc_mount */ + + /* See afs_mntpt_do_automount in fs/afs/mntpt.c for an example */ + + /* strip first '\' from fullpath */ + mountdata = cifs_compose_mount_options(cifs_sb->ctx->mount_options, + fullpath + 1, NULL, NULL); + if (IS_ERR(mountdata)) { + kfree(devname); + return (struct vfsmount *)mountdata; + } + + mnt = vfs_submount(mntpt, &cifs_fs_type, devname, mountdata); + kfree(mountdata); + kfree(devname); + return mnt; +} + +/* + * Create a vfsmount that we can automount + */ +static struct vfsmount *cifs_dfs_do_automount(struct dentry *mntpt) +{ + struct cifs_sb_info *cifs_sb; + void *page; + char *full_path; + struct vfsmount *mnt; + + cifs_dbg(FYI, "in %s\n", __func__); + BUG_ON(IS_ROOT(mntpt)); + + /* + * The MSDFS spec states that paths in DFS referral requests and + * responses must be prefixed by a single '\' character instead of + * the double backslashes usually used in the UNC. This function + * gives us the latter, so we must adjust the result. + */ + cifs_sb = CIFS_SB(mntpt->d_sb); + if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_NO_DFS) { + mnt = ERR_PTR(-EREMOTE); + goto cdda_exit; + } + + page = alloc_dentry_path(); + /* always use tree name prefix */ + full_path = build_path_from_dentry_optional_prefix(mntpt, page, true); + if (IS_ERR(full_path)) { + mnt = ERR_CAST(full_path); + goto free_full_path; + } + + convert_delimiter(full_path, '\\'); + cifs_dbg(FYI, "%s: full_path: %s\n", __func__, full_path); + + mnt = cifs_dfs_do_mount(mntpt, cifs_sb, full_path); + cifs_dbg(FYI, "%s: cifs_dfs_do_mount:%s , mnt:%p\n", __func__, full_path + 1, mnt); + +free_full_path: + free_dentry_path(page); +cdda_exit: + cifs_dbg(FYI, "leaving %s\n" , __func__); + return mnt; +} + +/* + * Attempt to automount the referral + */ +struct vfsmount *cifs_dfs_d_automount(struct path *path) +{ + struct vfsmount *newmnt; + + cifs_dbg(FYI, "in %s\n", __func__); + + newmnt = cifs_dfs_do_automount(path->dentry); + if (IS_ERR(newmnt)) { + cifs_dbg(FYI, "leaving %s [automount failed]\n" , __func__); + return newmnt; + } + + mntget(newmnt); /* prevent immediate expiration */ + mnt_set_expiry(newmnt, &cifs_dfs_automount_list); + schedule_delayed_work(&cifs_dfs_automount_task, + cifs_dfs_mountpoint_expiry_timeout); + cifs_dbg(FYI, "leaving %s [ok]\n" , __func__); + return newmnt; +} + +const struct inode_operations cifs_dfs_referral_inode_operations = { +}; diff --git a/fs/smb/client/cifs_fs_sb.h b/fs/smb/client/cifs_fs_sb.h new file mode 100644 index 0000000000000..013a4bd65280c --- /dev/null +++ b/fs/smb/client/cifs_fs_sb.h @@ -0,0 +1,76 @@ +/* SPDX-License-Identifier: LGPL-2.1 */ +/* + * + * Copyright (c) International Business Machines Corp., 2002,2004 + * Author(s): Steve French (sfrench@us.ibm.com) + * + */ +#include + +#ifndef _CIFS_FS_SB_H +#define _CIFS_FS_SB_H + +#include + +#define CIFS_MOUNT_NO_PERM 1 /* do not do client vfs_perm check */ +#define CIFS_MOUNT_SET_UID 2 /* set current's euid in create etc. */ +#define CIFS_MOUNT_SERVER_INUM 4 /* inode numbers from uniqueid from server */ +#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 */ +#define CIFS_MOUNT_CIFS_ACL 0x200 /* send ACL requests to non-POSIX srv */ +#define CIFS_MOUNT_OVERR_UID 0x400 /* override uid returned from server */ +#define CIFS_MOUNT_OVERR_GID 0x800 /* override gid returned from server */ +#define CIFS_MOUNT_DYNPERM 0x1000 /* allow in-memory only mode setting */ +#define CIFS_MOUNT_NOPOSIXBRL 0x2000 /* mandatory not posix byte range lock */ +#define CIFS_MOUNT_NOSSYNC 0x4000 /* don't do slow SMBflush on every sync*/ +#define CIFS_MOUNT_FSCACHE 0x8000 /* local caching enabled */ +#define CIFS_MOUNT_MF_SYMLINKS 0x10000 /* Minshall+French Symlinks enabled */ +#define CIFS_MOUNT_MULTIUSER 0x20000 /* multiuser mount */ +#define CIFS_MOUNT_STRICT_IO 0x40000 /* strict cache mode */ +#define CIFS_MOUNT_RWPIDFORWARD 0x80000 /* use pid forwarding for rw */ +#define CIFS_MOUNT_POSIXACL 0x100000 /* mirror of SB_POSIXACL in mnt_cifs_flags */ +#define CIFS_MOUNT_CIFS_BACKUPUID 0x200000 /* backup intent bit for a user */ +#define CIFS_MOUNT_CIFS_BACKUPGID 0x400000 /* backup intent bit for a group */ +#define CIFS_MOUNT_MAP_SFM_CHR 0x800000 /* SFM/MAC mapping for illegal chars */ +#define CIFS_MOUNT_USE_PREFIX_PATH 0x1000000 /* make subpath with unaccessible + * root mountable + */ +#define CIFS_MOUNT_UID_FROM_ACL 0x2000000 /* try to get UID via special SID */ +#define CIFS_MOUNT_NO_HANDLE_CACHE 0x4000000 /* disable caching dir handles */ +#define CIFS_MOUNT_NO_DFS 0x8000000 /* disable DFS resolving */ +#define CIFS_MOUNT_MODE_FROM_SID 0x10000000 /* retrieve mode from special ACE */ +#define CIFS_MOUNT_RO_CACHE 0x20000000 /* assumes share will not change */ +#define CIFS_MOUNT_RW_CACHE 0x40000000 /* assumes only client accessing */ +#define CIFS_MOUNT_SHUTDOWN 0x80000000 + +struct cifs_sb_info { + struct rb_root tlink_tree; + spinlock_t tlink_tree_lock; + struct tcon_link *master_tlink; + struct nls_table *local_nls; + struct smb3_fs_context *ctx; + atomic_t active; + unsigned int mnt_cifs_flags; + struct delayed_work prune_tlinks; + struct rcu_head rcu; + + /* only used when CIFS_MOUNT_USE_PREFIX_PATH is set */ + char *prepath; + + /* randomly generated 128-bit number for indexing dfs mount groups in referral cache */ + uuid_t dfs_mount_id; + /* + * Indicate whether serverino option was turned off later + * (cifs_autodisable_serverino) in order to match new mounts. + */ + bool mnt_cifs_serverino_autodisabled; + /* + * Available once the mount has completed. + */ + struct dentry *root; +}; +#endif /* _CIFS_FS_SB_H */ diff --git a/fs/smb/client/cifs_ioctl.h b/fs/smb/client/cifs_ioctl.h new file mode 100644 index 0000000000000..d86d78d5bfdc1 --- /dev/null +++ b/fs/smb/client/cifs_ioctl.h @@ -0,0 +1,126 @@ +/* SPDX-License-Identifier: LGPL-2.1 */ +/* + * + * Structure definitions for io control for cifs/smb3 + * + * Copyright (c) 2015 Steve French + * + */ + +struct smb_mnt_fs_info { + __u32 version; /* 0001 */ + __u16 protocol_id; + __u16 tcon_flags; + __u32 vol_serial_number; + __u32 vol_create_time; + __u32 share_caps; + __u32 share_flags; + __u32 sector_flags; + __u32 optimal_sector_size; + __u32 max_bytes_chunk; + __u32 fs_attributes; + __u32 max_path_component; + __u32 device_type; + __u32 device_characteristics; + __u32 maximal_access; + __u64 cifs_posix_caps; +} __packed; + +struct smb_snapshot_array { + __u32 number_of_snapshots; + __u32 number_of_snapshots_returned; + __u32 snapshot_array_size; + /* snapshots[]; */ +} __packed; + +/* query_info flags */ +#define PASSTHRU_QUERY_INFO 0x00000000 +#define PASSTHRU_FSCTL 0x00000001 +#define PASSTHRU_SET_INFO 0x00000002 +struct smb_query_info { + __u32 info_type; + __u32 file_info_class; + __u32 additional_information; + __u32 flags; + __u32 input_buffer_length; + __u32 output_buffer_length; + /* char buffer[]; */ +} __packed; + +/* + * Dumping the commonly used 16 byte (e.g. CCM and GCM128) keys still supported + * for backlevel compatibility, but is not sufficient for dumping the less + * frequently used GCM256 (32 byte) keys (see the newer "CIFS_DUMP_FULL_KEY" + * ioctl for dumping decryption info for GCM256 mounts) + */ +struct smb3_key_debug_info { + __u64 Suid; + __u16 cipher_type; + __u8 auth_key[16]; /* SMB2_NTLMV2_SESSKEY_SIZE */ + __u8 smb3encryptionkey[SMB3_SIGN_KEY_SIZE]; + __u8 smb3decryptionkey[SMB3_SIGN_KEY_SIZE]; +} __packed; + +/* + * Dump variable-sized keys + */ +struct smb3_full_key_debug_info { + /* INPUT: size of userspace buffer */ + __u32 in_size; + + /* + * INPUT: 0 for current user, otherwise session to dump + * OUTPUT: session id that was dumped + */ + __u64 session_id; + __u16 cipher_type; + __u8 session_key_length; + __u8 server_in_key_length; + __u8 server_out_key_length; + __u8 data[]; + /* + * return this struct with the keys appended at the end: + * __u8 session_key[session_key_length]; + * __u8 server_in_key[server_in_key_length]; + * __u8 server_out_key[server_out_key_length]; + */ +} __packed; + +struct smb3_notify { + __u32 completion_filter; + bool watch_tree; +} __packed; + +struct smb3_notify_info { + __u32 completion_filter; + bool watch_tree; + __u32 data_len; /* size of notify data below */ + __u8 notify_data[]; +} __packed; + +#define CIFS_IOCTL_MAGIC 0xCF +#define CIFS_IOC_COPYCHUNK_FILE _IOW(CIFS_IOCTL_MAGIC, 3, int) +#define CIFS_IOC_SET_INTEGRITY _IO(CIFS_IOCTL_MAGIC, 4) +#define CIFS_IOC_GET_MNT_INFO _IOR(CIFS_IOCTL_MAGIC, 5, struct smb_mnt_fs_info) +#define CIFS_ENUMERATE_SNAPSHOTS _IOR(CIFS_IOCTL_MAGIC, 6, struct smb_snapshot_array) +#define CIFS_QUERY_INFO _IOWR(CIFS_IOCTL_MAGIC, 7, struct smb_query_info) +#define CIFS_DUMP_KEY _IOWR(CIFS_IOCTL_MAGIC, 8, struct smb3_key_debug_info) +#define CIFS_IOC_NOTIFY _IOW(CIFS_IOCTL_MAGIC, 9, struct smb3_notify) +#define CIFS_DUMP_FULL_KEY _IOWR(CIFS_IOCTL_MAGIC, 10, struct smb3_full_key_debug_info) +#define CIFS_IOC_NOTIFY_INFO _IOWR(CIFS_IOCTL_MAGIC, 11, struct smb3_notify_info) +#define CIFS_IOC_SHUTDOWN _IOR ('X', 125, __u32) + +/* + * Flags for going down operation + */ +#define CIFS_GOING_FLAGS_DEFAULT 0x0 /* going down */ +#define CIFS_GOING_FLAGS_LOGFLUSH 0x1 /* flush log but not data */ +#define CIFS_GOING_FLAGS_NOLOGFLUSH 0x2 /* don't flush log nor data */ + +static inline bool cifs_forced_shutdown(struct cifs_sb_info *sbi) +{ + if (CIFS_MOUNT_SHUTDOWN & sbi->mnt_cifs_flags) + return true; + else + return false; +} diff --git a/fs/smb/client/cifs_spnego.c b/fs/smb/client/cifs_spnego.c new file mode 100644 index 0000000000000..342717bf1dc28 --- /dev/null +++ b/fs/smb/client/cifs_spnego.c @@ -0,0 +1,236 @@ +// SPDX-License-Identifier: LGPL-2.1 +/* + * SPNEGO upcall management for CIFS + * + * Copyright (c) 2007 Red Hat, Inc. + * Author(s): Jeff Layton (jlayton@redhat.com) + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include "cifsglob.h" +#include "cifs_spnego.h" +#include "cifs_debug.h" +#include "cifsproto.h" +static const struct cred *spnego_cred; + +/* create a new cifs key */ +static int +cifs_spnego_key_instantiate(struct key *key, struct key_preparsed_payload *prep) +{ + char *payload; + int ret; + + ret = -ENOMEM; + payload = kmemdup(prep->data, prep->datalen, GFP_KERNEL); + if (!payload) + goto error; + + /* attach the data */ + key->payload.data[0] = payload; + ret = 0; + +error: + return ret; +} + +static void +cifs_spnego_key_destroy(struct key *key) +{ + kfree(key->payload.data[0]); +} + + +/* + * keytype for CIFS spnego keys + */ +struct key_type cifs_spnego_key_type = { + .name = "cifs.spnego", + .instantiate = cifs_spnego_key_instantiate, + .destroy = cifs_spnego_key_destroy, + .describe = user_describe, +}; + +/* length of longest version string e.g. strlen("ver=0xFF") */ +#define MAX_VER_STR_LEN 8 + +/* length of longest security mechanism name, eg in future could have + * strlen(";sec=ntlmsspi") */ +#define MAX_MECH_STR_LEN 13 + +/* strlen of "host=" */ +#define HOST_KEY_LEN 5 + +/* strlen of ";ip4=" or ";ip6=" */ +#define IP_KEY_LEN 5 + +/* strlen of ";uid=0x" */ +#define UID_KEY_LEN 7 + +/* strlen of ";creduid=0x" */ +#define CREDUID_KEY_LEN 11 + +/* strlen of ";user=" */ +#define USER_KEY_LEN 6 + +/* strlen of ";pid=0x" */ +#define PID_KEY_LEN 7 + +/* get a key struct with a SPNEGO security blob, suitable for session setup */ +struct key * +cifs_get_spnego_key(struct cifs_ses *sesInfo, + struct TCP_Server_Info *server) +{ + struct sockaddr_in *sa = (struct sockaddr_in *) &server->dstaddr; + struct sockaddr_in6 *sa6 = (struct sockaddr_in6 *) &server->dstaddr; + char *description, *dp; + size_t desc_len; + struct key *spnego_key; + const char *hostname = server->hostname; + const struct cred *saved_cred; + + /* length of fields (with semicolons): ver=0xyz ip4=ipaddress + host=hostname sec=mechanism uid=0xFF user=username */ + desc_len = MAX_VER_STR_LEN + + HOST_KEY_LEN + strlen(hostname) + + IP_KEY_LEN + INET6_ADDRSTRLEN + + MAX_MECH_STR_LEN + + UID_KEY_LEN + (sizeof(uid_t) * 2) + + CREDUID_KEY_LEN + (sizeof(uid_t) * 2) + + PID_KEY_LEN + (sizeof(pid_t) * 2) + 1; + + if (sesInfo->user_name) + desc_len += USER_KEY_LEN + strlen(sesInfo->user_name); + + spnego_key = ERR_PTR(-ENOMEM); + description = kzalloc(desc_len, GFP_KERNEL); + if (description == NULL) + goto out; + + dp = description; + /* start with version and hostname portion of UNC string */ + spnego_key = ERR_PTR(-EINVAL); + sprintf(dp, "ver=0x%x;host=%s;", CIFS_SPNEGO_UPCALL_VERSION, + hostname); + dp = description + strlen(description); + + /* add the server address */ + if (server->dstaddr.ss_family == AF_INET) + sprintf(dp, "ip4=%pI4", &sa->sin_addr); + else if (server->dstaddr.ss_family == AF_INET6) + sprintf(dp, "ip6=%pI6", &sa6->sin6_addr); + else + goto out; + + dp = description + strlen(description); + + /* for now, only sec=krb5 and sec=mskrb5 are valid */ + if (server->sec_kerberos) + sprintf(dp, ";sec=krb5"); + else if (server->sec_mskerberos) + sprintf(dp, ";sec=mskrb5"); + else { + cifs_dbg(VFS, "unknown or missing server auth type, use krb5\n"); + sprintf(dp, ";sec=krb5"); + } + + dp = description + strlen(description); + sprintf(dp, ";uid=0x%x", + from_kuid_munged(&init_user_ns, sesInfo->linux_uid)); + + dp = description + strlen(description); + sprintf(dp, ";creduid=0x%x", + from_kuid_munged(&init_user_ns, sesInfo->cred_uid)); + + if (sesInfo->user_name) { + dp = description + strlen(description); + sprintf(dp, ";user=%s", sesInfo->user_name); + } + + dp = description + strlen(description); + sprintf(dp, ";pid=0x%x", current->pid); + + cifs_dbg(FYI, "key description = %s\n", description); + saved_cred = override_creds(spnego_cred); + spnego_key = request_key(&cifs_spnego_key_type, description, ""); + revert_creds(saved_cred); + +#ifdef CONFIG_CIFS_DEBUG2 + if (cifsFYI && !IS_ERR(spnego_key)) { + struct cifs_spnego_msg *msg = spnego_key->payload.data[0]; + cifs_dump_mem("SPNEGO reply blob:", msg->data, min(1024U, + msg->secblob_len + msg->sesskey_len)); + } +#endif /* CONFIG_CIFS_DEBUG2 */ + +out: + kfree(description); + return spnego_key; +} + +int +init_cifs_spnego(void) +{ + struct cred *cred; + struct key *keyring; + int ret; + + cifs_dbg(FYI, "Registering the %s key type\n", + cifs_spnego_key_type.name); + + /* + * Create an override credential set with special thread keyring for + * spnego upcalls. + */ + + cred = prepare_kernel_cred(NULL); + if (!cred) + return -ENOMEM; + + keyring = keyring_alloc(".cifs_spnego", + GLOBAL_ROOT_UID, GLOBAL_ROOT_GID, cred, + (KEY_POS_ALL & ~KEY_POS_SETATTR) | + KEY_USR_VIEW | KEY_USR_READ, + KEY_ALLOC_NOT_IN_QUOTA, NULL, NULL); + if (IS_ERR(keyring)) { + ret = PTR_ERR(keyring); + goto failed_put_cred; + } + + ret = register_key_type(&cifs_spnego_key_type); + if (ret < 0) + goto failed_put_key; + + /* + * instruct request_key() to use this special keyring as a cache for + * the results it looks up + */ + set_bit(KEY_FLAG_ROOT_CAN_CLEAR, &keyring->flags); + cred->thread_keyring = keyring; + cred->jit_keyring = KEY_REQKEY_DEFL_THREAD_KEYRING; + spnego_cred = cred; + + cifs_dbg(FYI, "cifs spnego keyring: %d\n", key_serial(keyring)); + return 0; + +failed_put_key: + key_put(keyring); +failed_put_cred: + put_cred(cred); + return ret; +} + +void +exit_cifs_spnego(void) +{ + key_revoke(spnego_cred->thread_keyring); + unregister_key_type(&cifs_spnego_key_type); + put_cred(spnego_cred); + cifs_dbg(FYI, "Unregistered %s key type\n", cifs_spnego_key_type.name); +} diff --git a/fs/smb/client/cifs_spnego.h b/fs/smb/client/cifs_spnego.h new file mode 100644 index 0000000000000..7f102ffeb6750 --- /dev/null +++ b/fs/smb/client/cifs_spnego.h @@ -0,0 +1,36 @@ +/* SPDX-License-Identifier: LGPL-2.1 */ +/* + * SPNEGO upcall management for CIFS + * + * Copyright (c) 2007 Red Hat, Inc. + * Author(s): Jeff Layton (jlayton@redhat.com) + * Steve French (sfrench@us.ibm.com) + * + */ + +#ifndef _CIFS_SPNEGO_H +#define _CIFS_SPNEGO_H + +#define CIFS_SPNEGO_UPCALL_VERSION 2 + +/* + * The version field should always be set to CIFS_SPNEGO_UPCALL_VERSION. + * The flags field is for future use. The request-key callout should set + * sesskey_len and secblob_len, and then concatenate the SessKey+SecBlob + * and stuff it in the data field. + */ +struct cifs_spnego_msg { + uint32_t version; + uint32_t flags; + uint32_t sesskey_len; + uint32_t secblob_len; + uint8_t data[1]; +}; + +#ifdef __KERNEL__ +extern struct key_type cifs_spnego_key_type; +extern struct key *cifs_get_spnego_key(struct cifs_ses *sesInfo, + struct TCP_Server_Info *server); +#endif /* KERNEL */ + +#endif /* _CIFS_SPNEGO_H */ diff --git a/fs/smb/client/cifs_spnego_negtokeninit.asn1 b/fs/smb/client/cifs_spnego_negtokeninit.asn1 new file mode 100644 index 0000000000000..181c083887d51 --- /dev/null +++ b/fs/smb/client/cifs_spnego_negtokeninit.asn1 @@ -0,0 +1,40 @@ +GSSAPI ::= + [APPLICATION 0] IMPLICIT SEQUENCE { + thisMech + OBJECT IDENTIFIER ({cifs_gssapi_this_mech}), + negotiationToken + NegotiationToken + } + +MechType ::= OBJECT IDENTIFIER ({cifs_neg_token_init_mech_type}) + +MechTypeList ::= SEQUENCE OF MechType + +NegHints ::= SEQUENCE { + hintName + [0] GeneralString OPTIONAL, + hintAddress + [1] OCTET STRING OPTIONAL + } + +NegTokenInit2 ::= + SEQUENCE { + mechTypes + [0] MechTypeList OPTIONAL, + reqFlags + [1] BIT STRING OPTIONAL, + mechToken + [2] OCTET STRING OPTIONAL, + negHints + [3] NegHints OPTIONAL, + mechListMIC + [3] OCTET STRING OPTIONAL + } + +NegotiationToken ::= + CHOICE { + negTokenInit + [0] NegTokenInit2, + negTokenTarg + [1] ANY + } diff --git a/fs/smb/client/cifs_swn.c b/fs/smb/client/cifs_swn.c new file mode 100644 index 0000000000000..7233c6a7e6d70 --- /dev/null +++ b/fs/smb/client/cifs_swn.c @@ -0,0 +1,674 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Witness Service client for CIFS + * + * Copyright (c) 2020 Samuel Cabrero + */ + +#include +#include +#include + +#include "cifs_swn.h" +#include "cifsglob.h" +#include "cifsproto.h" +#include "fscache.h" +#include "cifs_debug.h" +#include "netlink.h" + +static DEFINE_IDR(cifs_swnreg_idr); +static DEFINE_MUTEX(cifs_swnreg_idr_mutex); + +struct cifs_swn_reg { + int id; + struct kref ref_count; + + const char *net_name; + const char *share_name; + bool net_name_notify; + bool share_name_notify; + bool ip_notify; + + struct cifs_tcon *tcon; +}; + +static int cifs_swn_auth_info_krb(struct cifs_tcon *tcon, struct sk_buff *skb) +{ + int ret; + + ret = nla_put_flag(skb, CIFS_GENL_ATTR_SWN_KRB_AUTH); + if (ret < 0) + return ret; + + return 0; +} + +static int cifs_swn_auth_info_ntlm(struct cifs_tcon *tcon, struct sk_buff *skb) +{ + int ret; + + if (tcon->ses->user_name != NULL) { + ret = nla_put_string(skb, CIFS_GENL_ATTR_SWN_USER_NAME, tcon->ses->user_name); + if (ret < 0) + return ret; + } + + if (tcon->ses->password != NULL) { + ret = nla_put_string(skb, CIFS_GENL_ATTR_SWN_PASSWORD, tcon->ses->password); + if (ret < 0) + return ret; + } + + if (tcon->ses->domainName != NULL) { + ret = nla_put_string(skb, CIFS_GENL_ATTR_SWN_DOMAIN_NAME, tcon->ses->domainName); + if (ret < 0) + return ret; + } + + return 0; +} + +/* + * Sends a register message to the userspace daemon based on the registration. + * The authentication information to connect to the witness service is bundled + * into the message. + */ +static int cifs_swn_send_register_message(struct cifs_swn_reg *swnreg) +{ + struct sk_buff *skb; + struct genlmsghdr *hdr; + enum securityEnum authtype; + struct sockaddr_storage *addr; + int ret; + + skb = genlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL); + if (skb == NULL) { + ret = -ENOMEM; + goto fail; + } + + hdr = genlmsg_put(skb, 0, 0, &cifs_genl_family, 0, CIFS_GENL_CMD_SWN_REGISTER); + if (hdr == NULL) { + ret = -ENOMEM; + goto nlmsg_fail; + } + + ret = nla_put_u32(skb, CIFS_GENL_ATTR_SWN_REGISTRATION_ID, swnreg->id); + if (ret < 0) + goto nlmsg_fail; + + ret = nla_put_string(skb, CIFS_GENL_ATTR_SWN_NET_NAME, swnreg->net_name); + if (ret < 0) + goto nlmsg_fail; + + ret = nla_put_string(skb, CIFS_GENL_ATTR_SWN_SHARE_NAME, swnreg->share_name); + if (ret < 0) + goto nlmsg_fail; + + /* + * If there is an address stored use it instead of the server address, because we are + * in the process of reconnecting to it after a share has been moved or we have been + * told to switch to it (client move message). In these cases we unregister from the + * server address and register to the new address when we receive the notification. + */ + if (swnreg->tcon->ses->server->use_swn_dstaddr) + addr = &swnreg->tcon->ses->server->swn_dstaddr; + else + addr = &swnreg->tcon->ses->server->dstaddr; + + ret = nla_put(skb, CIFS_GENL_ATTR_SWN_IP, sizeof(struct sockaddr_storage), addr); + if (ret < 0) + goto nlmsg_fail; + + if (swnreg->net_name_notify) { + ret = nla_put_flag(skb, CIFS_GENL_ATTR_SWN_NET_NAME_NOTIFY); + if (ret < 0) + goto nlmsg_fail; + } + + if (swnreg->share_name_notify) { + ret = nla_put_flag(skb, CIFS_GENL_ATTR_SWN_SHARE_NAME_NOTIFY); + if (ret < 0) + goto nlmsg_fail; + } + + if (swnreg->ip_notify) { + ret = nla_put_flag(skb, CIFS_GENL_ATTR_SWN_IP_NOTIFY); + if (ret < 0) + goto nlmsg_fail; + } + + authtype = cifs_select_sectype(swnreg->tcon->ses->server, swnreg->tcon->ses->sectype); + switch (authtype) { + case Kerberos: + ret = cifs_swn_auth_info_krb(swnreg->tcon, skb); + if (ret < 0) { + cifs_dbg(VFS, "%s: Failed to get kerberos auth info: %d\n", __func__, ret); + goto nlmsg_fail; + } + break; + case NTLMv2: + case RawNTLMSSP: + ret = cifs_swn_auth_info_ntlm(swnreg->tcon, skb); + if (ret < 0) { + cifs_dbg(VFS, "%s: Failed to get NTLM auth info: %d\n", __func__, ret); + goto nlmsg_fail; + } + break; + default: + cifs_dbg(VFS, "%s: secType %d not supported!\n", __func__, authtype); + ret = -EINVAL; + goto nlmsg_fail; + } + + genlmsg_end(skb, hdr); + genlmsg_multicast(&cifs_genl_family, skb, 0, CIFS_GENL_MCGRP_SWN, GFP_ATOMIC); + + cifs_dbg(FYI, "%s: Message to register for network name %s with id %d sent\n", __func__, + swnreg->net_name, swnreg->id); + + return 0; + +nlmsg_fail: + genlmsg_cancel(skb, hdr); + nlmsg_free(skb); +fail: + return ret; +} + +/* + * Sends an uregister message to the userspace daemon based on the registration + */ +static int cifs_swn_send_unregister_message(struct cifs_swn_reg *swnreg) +{ + struct sk_buff *skb; + struct genlmsghdr *hdr; + int ret; + + skb = genlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL); + if (skb == NULL) + return -ENOMEM; + + hdr = genlmsg_put(skb, 0, 0, &cifs_genl_family, 0, CIFS_GENL_CMD_SWN_UNREGISTER); + if (hdr == NULL) { + ret = -ENOMEM; + goto nlmsg_fail; + } + + ret = nla_put_u32(skb, CIFS_GENL_ATTR_SWN_REGISTRATION_ID, swnreg->id); + if (ret < 0) + goto nlmsg_fail; + + ret = nla_put_string(skb, CIFS_GENL_ATTR_SWN_NET_NAME, swnreg->net_name); + if (ret < 0) + goto nlmsg_fail; + + ret = nla_put_string(skb, CIFS_GENL_ATTR_SWN_SHARE_NAME, swnreg->share_name); + if (ret < 0) + goto nlmsg_fail; + + ret = nla_put(skb, CIFS_GENL_ATTR_SWN_IP, sizeof(struct sockaddr_storage), + &swnreg->tcon->ses->server->dstaddr); + if (ret < 0) + goto nlmsg_fail; + + if (swnreg->net_name_notify) { + ret = nla_put_flag(skb, CIFS_GENL_ATTR_SWN_NET_NAME_NOTIFY); + if (ret < 0) + goto nlmsg_fail; + } + + if (swnreg->share_name_notify) { + ret = nla_put_flag(skb, CIFS_GENL_ATTR_SWN_SHARE_NAME_NOTIFY); + if (ret < 0) + goto nlmsg_fail; + } + + if (swnreg->ip_notify) { + ret = nla_put_flag(skb, CIFS_GENL_ATTR_SWN_IP_NOTIFY); + if (ret < 0) + goto nlmsg_fail; + } + + genlmsg_end(skb, hdr); + genlmsg_multicast(&cifs_genl_family, skb, 0, CIFS_GENL_MCGRP_SWN, GFP_ATOMIC); + + cifs_dbg(FYI, "%s: Message to unregister for network name %s with id %d sent\n", __func__, + swnreg->net_name, swnreg->id); + + return 0; + +nlmsg_fail: + genlmsg_cancel(skb, hdr); + nlmsg_free(skb); + return ret; +} + +/* + * Try to find a matching registration for the tcon's server name and share name. + * Calls to this function must be protected by cifs_swnreg_idr_mutex. + * TODO Try to avoid memory allocations + */ +static struct cifs_swn_reg *cifs_find_swn_reg(struct cifs_tcon *tcon) +{ + struct cifs_swn_reg *swnreg; + int id; + const char *share_name; + const char *net_name; + + net_name = extract_hostname(tcon->tree_name); + if (IS_ERR(net_name)) { + int ret; + + ret = PTR_ERR(net_name); + cifs_dbg(VFS, "%s: failed to extract host name from target '%s': %d\n", + __func__, tcon->tree_name, ret); + return ERR_PTR(-EINVAL); + } + + share_name = extract_sharename(tcon->tree_name); + if (IS_ERR(share_name)) { + int ret; + + ret = PTR_ERR(share_name); + cifs_dbg(VFS, "%s: failed to extract share name from target '%s': %d\n", + __func__, tcon->tree_name, ret); + kfree(net_name); + return ERR_PTR(-EINVAL); + } + + idr_for_each_entry(&cifs_swnreg_idr, swnreg, id) { + if (strcasecmp(swnreg->net_name, net_name) != 0 + || strcasecmp(swnreg->share_name, share_name) != 0) { + continue; + } + + cifs_dbg(FYI, "Existing swn registration for %s:%s found\n", swnreg->net_name, + swnreg->share_name); + + kfree(net_name); + kfree(share_name); + + return swnreg; + } + + kfree(net_name); + kfree(share_name); + + return ERR_PTR(-EEXIST); +} + +/* + * Get a registration for the tcon's server and share name, allocating a new one if it does not + * exists + */ +static struct cifs_swn_reg *cifs_get_swn_reg(struct cifs_tcon *tcon) +{ + struct cifs_swn_reg *reg = NULL; + int ret; + + mutex_lock(&cifs_swnreg_idr_mutex); + + /* Check if we are already registered for this network and share names */ + reg = cifs_find_swn_reg(tcon); + if (!IS_ERR(reg)) { + kref_get(®->ref_count); + mutex_unlock(&cifs_swnreg_idr_mutex); + return reg; + } else if (PTR_ERR(reg) != -EEXIST) { + mutex_unlock(&cifs_swnreg_idr_mutex); + return reg; + } + + reg = kmalloc(sizeof(struct cifs_swn_reg), GFP_ATOMIC); + if (reg == NULL) { + mutex_unlock(&cifs_swnreg_idr_mutex); + return ERR_PTR(-ENOMEM); + } + + kref_init(®->ref_count); + + reg->id = idr_alloc(&cifs_swnreg_idr, reg, 1, 0, GFP_ATOMIC); + if (reg->id < 0) { + cifs_dbg(FYI, "%s: failed to allocate registration id\n", __func__); + ret = reg->id; + goto fail; + } + + reg->net_name = extract_hostname(tcon->tree_name); + if (IS_ERR(reg->net_name)) { + ret = PTR_ERR(reg->net_name); + cifs_dbg(VFS, "%s: failed to extract host name from target: %d\n", __func__, ret); + goto fail_idr; + } + + reg->share_name = extract_sharename(tcon->tree_name); + if (IS_ERR(reg->share_name)) { + ret = PTR_ERR(reg->share_name); + cifs_dbg(VFS, "%s: failed to extract share name from target: %d\n", __func__, ret); + goto fail_net_name; + } + + reg->net_name_notify = true; + reg->share_name_notify = true; + reg->ip_notify = (tcon->capabilities & SMB2_SHARE_CAP_SCALEOUT); + + reg->tcon = tcon; + + mutex_unlock(&cifs_swnreg_idr_mutex); + + return reg; + +fail_net_name: + kfree(reg->net_name); +fail_idr: + idr_remove(&cifs_swnreg_idr, reg->id); +fail: + kfree(reg); + mutex_unlock(&cifs_swnreg_idr_mutex); + return ERR_PTR(ret); +} + +static void cifs_swn_reg_release(struct kref *ref) +{ + struct cifs_swn_reg *swnreg = container_of(ref, struct cifs_swn_reg, ref_count); + int ret; + + ret = cifs_swn_send_unregister_message(swnreg); + if (ret < 0) + cifs_dbg(VFS, "%s: Failed to send unregister message: %d\n", __func__, ret); + + idr_remove(&cifs_swnreg_idr, swnreg->id); + kfree(swnreg->net_name); + kfree(swnreg->share_name); + kfree(swnreg); +} + +static void cifs_put_swn_reg(struct cifs_swn_reg *swnreg) +{ + mutex_lock(&cifs_swnreg_idr_mutex); + kref_put(&swnreg->ref_count, cifs_swn_reg_release); + mutex_unlock(&cifs_swnreg_idr_mutex); +} + +static int cifs_swn_resource_state_changed(struct cifs_swn_reg *swnreg, const char *name, int state) +{ + switch (state) { + case CIFS_SWN_RESOURCE_STATE_UNAVAILABLE: + cifs_dbg(FYI, "%s: resource name '%s' become unavailable\n", __func__, name); + cifs_signal_cifsd_for_reconnect(swnreg->tcon->ses->server, true); + break; + case CIFS_SWN_RESOURCE_STATE_AVAILABLE: + cifs_dbg(FYI, "%s: resource name '%s' become available\n", __func__, name); + cifs_signal_cifsd_for_reconnect(swnreg->tcon->ses->server, true); + break; + case CIFS_SWN_RESOURCE_STATE_UNKNOWN: + cifs_dbg(FYI, "%s: resource name '%s' changed to unknown state\n", __func__, name); + break; + } + return 0; +} + +static bool cifs_sockaddr_equal(struct sockaddr_storage *addr1, struct sockaddr_storage *addr2) +{ + if (addr1->ss_family != addr2->ss_family) + return false; + + if (addr1->ss_family == AF_INET) { + return (memcmp(&((const struct sockaddr_in *)addr1)->sin_addr, + &((const struct sockaddr_in *)addr2)->sin_addr, + sizeof(struct in_addr)) == 0); + } + + if (addr1->ss_family == AF_INET6) { + return (memcmp(&((const struct sockaddr_in6 *)addr1)->sin6_addr, + &((const struct sockaddr_in6 *)addr2)->sin6_addr, + sizeof(struct in6_addr)) == 0); + } + + return false; +} + +static int cifs_swn_store_swn_addr(const struct sockaddr_storage *new, + const struct sockaddr_storage *old, + struct sockaddr_storage *dst) +{ + __be16 port = cpu_to_be16(CIFS_PORT); + + if (old->ss_family == AF_INET) { + struct sockaddr_in *ipv4 = (struct sockaddr_in *)old; + + port = ipv4->sin_port; + } else if (old->ss_family == AF_INET6) { + struct sockaddr_in6 *ipv6 = (struct sockaddr_in6 *)old; + + port = ipv6->sin6_port; + } + + if (new->ss_family == AF_INET) { + struct sockaddr_in *ipv4 = (struct sockaddr_in *)new; + + ipv4->sin_port = port; + } else if (new->ss_family == AF_INET6) { + struct sockaddr_in6 *ipv6 = (struct sockaddr_in6 *)new; + + ipv6->sin6_port = port; + } + + *dst = *new; + + return 0; +} + +static int cifs_swn_reconnect(struct cifs_tcon *tcon, struct sockaddr_storage *addr) +{ + int ret = 0; + + /* Store the reconnect address */ + cifs_server_lock(tcon->ses->server); + if (cifs_sockaddr_equal(&tcon->ses->server->dstaddr, addr)) + goto unlock; + + ret = cifs_swn_store_swn_addr(addr, &tcon->ses->server->dstaddr, + &tcon->ses->server->swn_dstaddr); + if (ret < 0) { + cifs_dbg(VFS, "%s: failed to store address: %d\n", __func__, ret); + goto unlock; + } + tcon->ses->server->use_swn_dstaddr = true; + + /* + * Unregister to stop receiving notifications for the old IP address. + */ + ret = cifs_swn_unregister(tcon); + if (ret < 0) { + cifs_dbg(VFS, "%s: Failed to unregister for witness notifications: %d\n", + __func__, ret); + goto unlock; + } + + /* + * And register to receive notifications for the new IP address now that we have + * stored the new address. + */ + ret = cifs_swn_register(tcon); + if (ret < 0) { + cifs_dbg(VFS, "%s: Failed to register for witness notifications: %d\n", + __func__, ret); + goto unlock; + } + + cifs_signal_cifsd_for_reconnect(tcon->ses->server, false); + +unlock: + cifs_server_unlock(tcon->ses->server); + + return ret; +} + +static int cifs_swn_client_move(struct cifs_swn_reg *swnreg, struct sockaddr_storage *addr) +{ + struct sockaddr_in *ipv4 = (struct sockaddr_in *)addr; + struct sockaddr_in6 *ipv6 = (struct sockaddr_in6 *)addr; + + if (addr->ss_family == AF_INET) + cifs_dbg(FYI, "%s: move to %pI4\n", __func__, &ipv4->sin_addr); + else if (addr->ss_family == AF_INET6) + cifs_dbg(FYI, "%s: move to %pI6\n", __func__, &ipv6->sin6_addr); + + return cifs_swn_reconnect(swnreg->tcon, addr); +} + +int cifs_swn_notify(struct sk_buff *skb, struct genl_info *info) +{ + struct cifs_swn_reg *swnreg; + char name[256]; + int type; + + if (info->attrs[CIFS_GENL_ATTR_SWN_REGISTRATION_ID]) { + int swnreg_id; + + swnreg_id = nla_get_u32(info->attrs[CIFS_GENL_ATTR_SWN_REGISTRATION_ID]); + mutex_lock(&cifs_swnreg_idr_mutex); + swnreg = idr_find(&cifs_swnreg_idr, swnreg_id); + mutex_unlock(&cifs_swnreg_idr_mutex); + if (swnreg == NULL) { + cifs_dbg(FYI, "%s: registration id %d not found\n", __func__, swnreg_id); + return -EINVAL; + } + } else { + cifs_dbg(FYI, "%s: missing registration id attribute\n", __func__); + return -EINVAL; + } + + if (info->attrs[CIFS_GENL_ATTR_SWN_NOTIFICATION_TYPE]) { + type = nla_get_u32(info->attrs[CIFS_GENL_ATTR_SWN_NOTIFICATION_TYPE]); + } else { + cifs_dbg(FYI, "%s: missing notification type attribute\n", __func__); + return -EINVAL; + } + + switch (type) { + case CIFS_SWN_NOTIFICATION_RESOURCE_CHANGE: { + int state; + + if (info->attrs[CIFS_GENL_ATTR_SWN_RESOURCE_NAME]) { + nla_strscpy(name, info->attrs[CIFS_GENL_ATTR_SWN_RESOURCE_NAME], + sizeof(name)); + } else { + cifs_dbg(FYI, "%s: missing resource name attribute\n", __func__); + return -EINVAL; + } + if (info->attrs[CIFS_GENL_ATTR_SWN_RESOURCE_STATE]) { + state = nla_get_u32(info->attrs[CIFS_GENL_ATTR_SWN_RESOURCE_STATE]); + } else { + cifs_dbg(FYI, "%s: missing resource state attribute\n", __func__); + return -EINVAL; + } + return cifs_swn_resource_state_changed(swnreg, name, state); + } + case CIFS_SWN_NOTIFICATION_CLIENT_MOVE: { + struct sockaddr_storage addr; + + if (info->attrs[CIFS_GENL_ATTR_SWN_IP]) { + nla_memcpy(&addr, info->attrs[CIFS_GENL_ATTR_SWN_IP], sizeof(addr)); + } else { + cifs_dbg(FYI, "%s: missing IP address attribute\n", __func__); + return -EINVAL; + } + return cifs_swn_client_move(swnreg, &addr); + } + default: + cifs_dbg(FYI, "%s: unknown notification type %d\n", __func__, type); + break; + } + + return 0; +} + +int cifs_swn_register(struct cifs_tcon *tcon) +{ + struct cifs_swn_reg *swnreg; + int ret; + + swnreg = cifs_get_swn_reg(tcon); + if (IS_ERR(swnreg)) + return PTR_ERR(swnreg); + + ret = cifs_swn_send_register_message(swnreg); + if (ret < 0) { + cifs_dbg(VFS, "%s: Failed to send swn register message: %d\n", __func__, ret); + /* Do not put the swnreg or return error, the echo task will retry */ + } + + return 0; +} + +int cifs_swn_unregister(struct cifs_tcon *tcon) +{ + struct cifs_swn_reg *swnreg; + + mutex_lock(&cifs_swnreg_idr_mutex); + + swnreg = cifs_find_swn_reg(tcon); + if (IS_ERR(swnreg)) { + mutex_unlock(&cifs_swnreg_idr_mutex); + return PTR_ERR(swnreg); + } + + mutex_unlock(&cifs_swnreg_idr_mutex); + + cifs_put_swn_reg(swnreg); + + return 0; +} + +void cifs_swn_dump(struct seq_file *m) +{ + struct cifs_swn_reg *swnreg; + struct sockaddr_in *sa; + struct sockaddr_in6 *sa6; + int id; + + seq_puts(m, "Witness registrations:"); + + mutex_lock(&cifs_swnreg_idr_mutex); + idr_for_each_entry(&cifs_swnreg_idr, swnreg, id) { + seq_printf(m, "\nId: %u Refs: %u Network name: '%s'%s Share name: '%s'%s Ip address: ", + id, kref_read(&swnreg->ref_count), + swnreg->net_name, swnreg->net_name_notify ? "(y)" : "(n)", + swnreg->share_name, swnreg->share_name_notify ? "(y)" : "(n)"); + switch (swnreg->tcon->ses->server->dstaddr.ss_family) { + case AF_INET: + sa = (struct sockaddr_in *) &swnreg->tcon->ses->server->dstaddr; + seq_printf(m, "%pI4", &sa->sin_addr.s_addr); + break; + case AF_INET6: + sa6 = (struct sockaddr_in6 *) &swnreg->tcon->ses->server->dstaddr; + seq_printf(m, "%pI6", &sa6->sin6_addr.s6_addr); + if (sa6->sin6_scope_id) + seq_printf(m, "%%%u", sa6->sin6_scope_id); + break; + default: + seq_puts(m, "(unknown)"); + } + seq_printf(m, "%s", swnreg->ip_notify ? "(y)" : "(n)"); + } + mutex_unlock(&cifs_swnreg_idr_mutex); + seq_puts(m, "\n"); +} + +void cifs_swn_check(void) +{ + struct cifs_swn_reg *swnreg; + int id; + int ret; + + mutex_lock(&cifs_swnreg_idr_mutex); + idr_for_each_entry(&cifs_swnreg_idr, swnreg, id) { + ret = cifs_swn_send_register_message(swnreg); + if (ret < 0) + cifs_dbg(FYI, "%s: Failed to send register message: %d\n", __func__, ret); + } + mutex_unlock(&cifs_swnreg_idr_mutex); +} diff --git a/fs/smb/client/cifs_swn.h b/fs/smb/client/cifs_swn.h new file mode 100644 index 0000000000000..8a9d2a5c9077e --- /dev/null +++ b/fs/smb/client/cifs_swn.h @@ -0,0 +1,52 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * Witness Service client for CIFS + * + * Copyright (c) 2020 Samuel Cabrero + */ + +#ifndef _CIFS_SWN_H +#define _CIFS_SWN_H +#include "cifsglob.h" + +struct cifs_tcon; +struct sk_buff; +struct genl_info; + +#ifdef CONFIG_CIFS_SWN_UPCALL +extern int cifs_swn_register(struct cifs_tcon *tcon); + +extern int cifs_swn_unregister(struct cifs_tcon *tcon); + +extern int cifs_swn_notify(struct sk_buff *skb, struct genl_info *info); + +extern void cifs_swn_dump(struct seq_file *m); + +extern void cifs_swn_check(void); + +static inline bool cifs_swn_set_server_dstaddr(struct TCP_Server_Info *server) +{ + if (server->use_swn_dstaddr) { + server->dstaddr = server->swn_dstaddr; + return true; + } + return false; +} + +static inline void cifs_swn_reset_server_dstaddr(struct TCP_Server_Info *server) +{ + server->use_swn_dstaddr = false; +} + +#else + +static inline int cifs_swn_register(struct cifs_tcon *tcon) { return 0; } +static inline int cifs_swn_unregister(struct cifs_tcon *tcon) { return 0; } +static inline int cifs_swn_notify(struct sk_buff *s, struct genl_info *i) { return 0; } +static inline void cifs_swn_dump(struct seq_file *m) {} +static inline void cifs_swn_check(void) {} +static inline bool cifs_swn_set_server_dstaddr(struct TCP_Server_Info *server) { return false; } +static inline void cifs_swn_reset_server_dstaddr(struct TCP_Server_Info *server) {} + +#endif /* CONFIG_CIFS_SWN_UPCALL */ +#endif /* _CIFS_SWN_H */ diff --git a/fs/smb/client/cifs_unicode.c b/fs/smb/client/cifs_unicode.c new file mode 100644 index 0000000000000..e7582dd791794 --- /dev/null +++ b/fs/smb/client/cifs_unicode.c @@ -0,0 +1,632 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * + * Copyright (c) International Business Machines Corp., 2000,2009 + * Modified by Steve French (sfrench@us.ibm.com) + */ +#include +#include +#include "cifs_fs_sb.h" +#include "cifs_unicode.h" +#include "cifs_uniupr.h" +#include "cifspdu.h" +#include "cifsglob.h" +#include "cifs_debug.h" + +int cifs_remap(struct cifs_sb_info *cifs_sb) +{ + int map_type; + + if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_MAP_SFM_CHR) + map_type = SFM_MAP_UNI_RSVD; + else if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_MAP_SPECIAL_CHR) + map_type = SFU_MAP_UNI_RSVD; + else + map_type = NO_MAP_UNI_RSVD; + + return map_type; +} + +/* Convert character using the SFU - "Services for Unix" remapping range */ +static bool +convert_sfu_char(const __u16 src_char, char *target) +{ + /* + * BB: Cannot handle remapping UNI_SLASH until all the calls to + * build_path_from_dentry are modified, as they use slash as + * separator. + */ + switch (src_char) { + case UNI_COLON: + *target = ':'; + break; + case UNI_ASTERISK: + *target = '*'; + break; + case UNI_QUESTION: + *target = '?'; + break; + case UNI_PIPE: + *target = '|'; + break; + case UNI_GRTRTHAN: + *target = '>'; + break; + case UNI_LESSTHAN: + *target = '<'; + break; + default: + return false; + } + return true; +} + +/* Convert character using the SFM - "Services for Mac" remapping range */ +static bool +convert_sfm_char(const __u16 src_char, char *target) +{ + if (src_char >= 0xF001 && src_char <= 0xF01F) { + *target = src_char - 0xF000; + return true; + } + switch (src_char) { + case SFM_COLON: + *target = ':'; + break; + case SFM_DOUBLEQUOTE: + *target = '"'; + break; + case SFM_ASTERISK: + *target = '*'; + break; + case SFM_QUESTION: + *target = '?'; + break; + case SFM_PIPE: + *target = '|'; + break; + case SFM_GRTRTHAN: + *target = '>'; + break; + case SFM_LESSTHAN: + *target = '<'; + break; + case SFM_SPACE: + *target = ' '; + break; + case SFM_PERIOD: + *target = '.'; + break; + default: + return false; + } + return true; +} + + +/* + * cifs_mapchar - convert a host-endian char to proper char in codepage + * @target - where converted character should be copied + * @src_char - 2 byte host-endian source character + * @cp - codepage to which character should be converted + * @map_type - How should the 7 NTFS/SMB reserved characters be mapped to UCS2? + * + * This function handles the conversion of a single character. It is the + * responsibility of the caller to ensure that the target buffer is large + * enough to hold the result of the conversion (at least NLS_MAX_CHARSET_SIZE). + */ +static int +cifs_mapchar(char *target, const __u16 *from, const struct nls_table *cp, + int maptype) +{ + int len = 1; + __u16 src_char; + + src_char = *from; + + if ((maptype == SFM_MAP_UNI_RSVD) && convert_sfm_char(src_char, target)) + return len; + else if ((maptype == SFU_MAP_UNI_RSVD) && + convert_sfu_char(src_char, target)) + return len; + + /* if character not one of seven in special remap set */ + len = cp->uni2char(src_char, target, NLS_MAX_CHARSET_SIZE); + if (len <= 0) + goto surrogate_pair; + + return len; + +surrogate_pair: + /* convert SURROGATE_PAIR and IVS */ + if (strcmp(cp->charset, "utf8")) + goto unknown; + len = utf16s_to_utf8s(from, 3, UTF16_LITTLE_ENDIAN, target, 6); + if (len <= 0) + goto unknown; + return len; + +unknown: + *target = '?'; + len = 1; + return len; +} + +/* + * cifs_from_utf16 - convert utf16le string to local charset + * @to - destination buffer + * @from - source buffer + * @tolen - destination buffer size (in bytes) + * @fromlen - source buffer size (in bytes) + * @codepage - codepage to which characters should be converted + * @mapchar - should characters be remapped according to the mapchars option? + * + * Convert a little-endian utf16le string (as sent by the server) to a string + * in the provided codepage. The tolen and fromlen parameters are to ensure + * that the code doesn't walk off of the end of the buffer (which is always + * a danger if the alignment of the source buffer is off). The destination + * string is always properly null terminated and fits in the destination + * buffer. Returns the length of the destination string in bytes (including + * null terminator). + * + * Note that some windows versions actually send multiword UTF-16 characters + * instead of straight UTF16-2. The linux nls routines however aren't able to + * deal with those characters properly. In the event that we get some of + * those characters, they won't be translated properly. + */ +int +cifs_from_utf16(char *to, const __le16 *from, int tolen, int fromlen, + const struct nls_table *codepage, int map_type) +{ + int i, charlen, safelen; + int outlen = 0; + int nullsize = nls_nullsize(codepage); + int fromwords = fromlen / 2; + char tmp[NLS_MAX_CHARSET_SIZE]; + __u16 ftmp[3]; /* ftmp[3] = 3array x 2bytes = 6bytes UTF-16 */ + + /* + * because the chars can be of varying widths, we need to take care + * not to overflow the destination buffer when we get close to the + * end of it. Until we get to this offset, we don't need to check + * for overflow however. + */ + safelen = tolen - (NLS_MAX_CHARSET_SIZE + nullsize); + + for (i = 0; i < fromwords; i++) { + ftmp[0] = get_unaligned_le16(&from[i]); + if (ftmp[0] == 0) + break; + if (i + 1 < fromwords) + ftmp[1] = get_unaligned_le16(&from[i + 1]); + else + ftmp[1] = 0; + if (i + 2 < fromwords) + ftmp[2] = get_unaligned_le16(&from[i + 2]); + else + ftmp[2] = 0; + + /* + * check to see if converting this character might make the + * conversion bleed into the null terminator + */ + if (outlen >= safelen) { + charlen = cifs_mapchar(tmp, ftmp, codepage, map_type); + if ((outlen + charlen) > (tolen - nullsize)) + break; + } + + /* put converted char into 'to' buffer */ + charlen = cifs_mapchar(&to[outlen], ftmp, codepage, map_type); + outlen += charlen; + + /* charlen (=bytes of UTF-8 for 1 character) + * 4bytes UTF-8(surrogate pair) is charlen=4 + * (4bytes UTF-16 code) + * 7-8bytes UTF-8(IVS) is charlen=3+4 or 4+4 + * (2 UTF-8 pairs divided to 2 UTF-16 pairs) */ + if (charlen == 4) + i++; + else if (charlen >= 5) + /* 5-6bytes UTF-8 */ + i += 2; + } + + /* properly null-terminate string */ + for (i = 0; i < nullsize; i++) + to[outlen++] = 0; + + return outlen; +} + +/* + * NAME: cifs_strtoUTF16() + * + * FUNCTION: Convert character string to unicode string + * + */ +int +cifs_strtoUTF16(__le16 *to, const char *from, int len, + const struct nls_table *codepage) +{ + int charlen; + int i; + wchar_t wchar_to; /* needed to quiet sparse */ + + /* special case for utf8 to handle no plane0 chars */ + if (!strcmp(codepage->charset, "utf8")) { + /* + * convert utf8 -> utf16, we assume we have enough space + * as caller should have assumed conversion does not overflow + * in destination len is length in wchar_t units (16bits) + */ + i = utf8s_to_utf16s(from, len, UTF16_LITTLE_ENDIAN, + (wchar_t *) to, len); + + /* if success terminate and exit */ + if (i >= 0) + goto success; + /* + * if fails fall back to UCS encoding as this + * function should not return negative values + * currently can fail only if source contains + * invalid encoded characters + */ + } + + for (i = 0; len && *from; i++, from += charlen, len -= charlen) { + charlen = codepage->char2uni(from, len, &wchar_to); + if (charlen < 1) { + cifs_dbg(VFS, "strtoUTF16: char2uni of 0x%x returned %d\n", + *from, charlen); + /* A question mark */ + wchar_to = 0x003f; + charlen = 1; + } + put_unaligned_le16(wchar_to, &to[i]); + } + +success: + put_unaligned_le16(0, &to[i]); + return i; +} + +/* + * cifs_utf16_bytes - how long will a string be after conversion? + * @utf16 - pointer to input string + * @maxbytes - don't go past this many bytes of input string + * @codepage - destination codepage + * + * Walk a utf16le string and return the number of bytes that the string will + * be after being converted to the given charset, not including any null + * termination required. Don't walk past maxbytes in the source buffer. + */ +int +cifs_utf16_bytes(const __le16 *from, int maxbytes, + const struct nls_table *codepage) +{ + int i; + int charlen, outlen = 0; + int maxwords = maxbytes / 2; + char tmp[NLS_MAX_CHARSET_SIZE]; + __u16 ftmp[3]; + + for (i = 0; i < maxwords; i++) { + ftmp[0] = get_unaligned_le16(&from[i]); + if (ftmp[0] == 0) + break; + if (i + 1 < maxwords) + ftmp[1] = get_unaligned_le16(&from[i + 1]); + else + ftmp[1] = 0; + if (i + 2 < maxwords) + ftmp[2] = get_unaligned_le16(&from[i + 2]); + else + ftmp[2] = 0; + + charlen = cifs_mapchar(tmp, ftmp, codepage, NO_MAP_UNI_RSVD); + outlen += charlen; + } + + return outlen; +} + +/* + * cifs_strndup_from_utf16 - copy a string from wire format to the local + * codepage + * @src - source string + * @maxlen - don't walk past this many bytes in the source string + * @is_unicode - is this a unicode string? + * @codepage - destination codepage + * + * Take a string given by the server, convert it to the local codepage and + * put it in a new buffer. Returns a pointer to the new string or NULL on + * error. + */ +char * +cifs_strndup_from_utf16(const char *src, const int maxlen, + const bool is_unicode, const struct nls_table *codepage) +{ + int len; + char *dst; + + if (is_unicode) { + len = cifs_utf16_bytes((__le16 *) src, maxlen, codepage); + len += nls_nullsize(codepage); + dst = kmalloc(len, GFP_KERNEL); + if (!dst) + return NULL; + cifs_from_utf16(dst, (__le16 *) src, len, maxlen, codepage, + NO_MAP_UNI_RSVD); + } else { + dst = kstrndup(src, maxlen, GFP_KERNEL); + } + + return dst; +} + +static __le16 convert_to_sfu_char(char src_char) +{ + __le16 dest_char; + + switch (src_char) { + case ':': + dest_char = cpu_to_le16(UNI_COLON); + break; + case '*': + dest_char = cpu_to_le16(UNI_ASTERISK); + break; + case '?': + dest_char = cpu_to_le16(UNI_QUESTION); + break; + case '<': + dest_char = cpu_to_le16(UNI_LESSTHAN); + break; + case '>': + dest_char = cpu_to_le16(UNI_GRTRTHAN); + break; + case '|': + dest_char = cpu_to_le16(UNI_PIPE); + break; + default: + dest_char = 0; + } + + return dest_char; +} + +static __le16 convert_to_sfm_char(char src_char, bool end_of_string) +{ + __le16 dest_char; + + if (src_char >= 0x01 && src_char <= 0x1F) { + dest_char = cpu_to_le16(src_char + 0xF000); + return dest_char; + } + switch (src_char) { + case ':': + dest_char = cpu_to_le16(SFM_COLON); + break; + case '"': + dest_char = cpu_to_le16(SFM_DOUBLEQUOTE); + break; + case '*': + dest_char = cpu_to_le16(SFM_ASTERISK); + break; + case '?': + dest_char = cpu_to_le16(SFM_QUESTION); + break; + case '<': + dest_char = cpu_to_le16(SFM_LESSTHAN); + break; + case '>': + dest_char = cpu_to_le16(SFM_GRTRTHAN); + break; + case '|': + dest_char = cpu_to_le16(SFM_PIPE); + break; + case '.': + if (end_of_string) + dest_char = cpu_to_le16(SFM_PERIOD); + else + dest_char = 0; + break; + case ' ': + if (end_of_string) + dest_char = cpu_to_le16(SFM_SPACE); + else + dest_char = 0; + break; + default: + dest_char = 0; + } + + return dest_char; +} + +/* + * Convert 16 bit Unicode pathname to wire format from string in current code + * page. Conversion may involve remapping up the six characters that are + * only legal in POSIX-like OS (if they are present in the string). Path + * names are little endian 16 bit Unicode on the wire + */ +int +cifsConvertToUTF16(__le16 *target, const char *source, int srclen, + const struct nls_table *cp, int map_chars) +{ + int i, charlen; + int j = 0; + char src_char; + __le16 dst_char; + wchar_t tmp; + wchar_t *wchar_to; /* UTF-16 */ + int ret; + unicode_t u; + + if (map_chars == NO_MAP_UNI_RSVD) + return cifs_strtoUTF16(target, source, PATH_MAX, cp); + + wchar_to = kzalloc(6, GFP_KERNEL); + + for (i = 0; i < srclen; j++) { + src_char = source[i]; + charlen = 1; + + /* check if end of string */ + if (src_char == 0) + goto ctoUTF16_out; + + /* see if we must remap this char */ + if (map_chars == SFU_MAP_UNI_RSVD) + dst_char = convert_to_sfu_char(src_char); + else if (map_chars == SFM_MAP_UNI_RSVD) { + bool end_of_string; + + /** + * Remap spaces and periods found at the end of every + * component of the path. The special cases of '.' and + * '..' do not need to be dealt with explicitly because + * they are addressed in namei.c:link_path_walk(). + **/ + if ((i == srclen - 1) || (source[i+1] == '\\')) + end_of_string = true; + else + end_of_string = false; + + dst_char = convert_to_sfm_char(src_char, end_of_string); + } else + dst_char = 0; + /* + * FIXME: We can not handle remapping backslash (UNI_SLASH) + * until all the calls to build_path_from_dentry are modified, + * as they use backslash as separator. + */ + if (dst_char == 0) { + charlen = cp->char2uni(source + i, srclen - i, &tmp); + dst_char = cpu_to_le16(tmp); + + /* + * if no match, use question mark, which at least in + * some cases serves as wild card + */ + if (charlen > 0) + goto ctoUTF16; + + /* convert SURROGATE_PAIR */ + if (strcmp(cp->charset, "utf8") || !wchar_to) + goto unknown; + if (*(source + i) & 0x80) { + charlen = utf8_to_utf32(source + i, 6, &u); + if (charlen < 0) + goto unknown; + } else + goto unknown; + ret = utf8s_to_utf16s(source + i, charlen, + UTF16_LITTLE_ENDIAN, + wchar_to, 6); + if (ret < 0) + goto unknown; + + i += charlen; + dst_char = cpu_to_le16(*wchar_to); + if (charlen <= 3) + /* 1-3bytes UTF-8 to 2bytes UTF-16 */ + put_unaligned(dst_char, &target[j]); + else if (charlen == 4) { + /* 4bytes UTF-8(surrogate pair) to 4bytes UTF-16 + * 7-8bytes UTF-8(IVS) divided to 2 UTF-16 + * (charlen=3+4 or 4+4) */ + put_unaligned(dst_char, &target[j]); + dst_char = cpu_to_le16(*(wchar_to + 1)); + j++; + put_unaligned(dst_char, &target[j]); + } else if (charlen >= 5) { + /* 5-6bytes UTF-8 to 6bytes UTF-16 */ + put_unaligned(dst_char, &target[j]); + dst_char = cpu_to_le16(*(wchar_to + 1)); + j++; + put_unaligned(dst_char, &target[j]); + dst_char = cpu_to_le16(*(wchar_to + 2)); + j++; + put_unaligned(dst_char, &target[j]); + } + continue; + +unknown: + dst_char = cpu_to_le16(0x003f); + charlen = 1; + } + +ctoUTF16: + /* + * character may take more than one byte in the source string, + * but will take exactly two bytes in the target string + */ + i += charlen; + put_unaligned(dst_char, &target[j]); + } + +ctoUTF16_out: + put_unaligned(0, &target[j]); /* Null terminate target unicode string */ + kfree(wchar_to); + return j; +} + +/* + * cifs_local_to_utf16_bytes - how long will a string be after conversion? + * @from - pointer to input string + * @maxbytes - don't go past this many bytes of input string + * @codepage - source codepage + * + * Walk a string and return the number of bytes that the string will + * be after being converted to the given charset, not including any null + * termination required. Don't walk past maxbytes in the source buffer. + */ + +static int +cifs_local_to_utf16_bytes(const char *from, int len, + const struct nls_table *codepage) +{ + int charlen; + int i; + wchar_t wchar_to; + + for (i = 0; len && *from; i++, from += charlen, len -= charlen) { + charlen = codepage->char2uni(from, len, &wchar_to); + /* Failed conversion defaults to a question mark */ + if (charlen < 1) + charlen = 1; + } + return 2 * i; /* UTF16 characters are two bytes */ +} + +/* + * cifs_strndup_to_utf16 - copy a string to wire format from the local codepage + * @src - source string + * @maxlen - don't walk past this many bytes in the source string + * @utf16_len - the length of the allocated string in bytes (including null) + * @cp - source codepage + * @remap - map special chars + * + * Take a string convert it from the local codepage to UTF16 and + * put it in a new buffer. Returns a pointer to the new string or NULL on + * error. + */ +__le16 * +cifs_strndup_to_utf16(const char *src, const int maxlen, int *utf16_len, + const struct nls_table *cp, int remap) +{ + int len; + __le16 *dst; + + len = cifs_local_to_utf16_bytes(src, maxlen, cp); + len += 2; /* NULL */ + dst = kmalloc(len, GFP_KERNEL); + if (!dst) { + *utf16_len = 0; + return NULL; + } + cifsConvertToUTF16(dst, src, strlen(src), cp, remap); + *utf16_len = len; + return dst; +} diff --git a/fs/smb/client/cifs_unicode.h b/fs/smb/client/cifs_unicode.h new file mode 100644 index 0000000000000..80b3d845419fa --- /dev/null +++ b/fs/smb/client/cifs_unicode.h @@ -0,0 +1,404 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + * cifs_unicode: Unicode kernel case support + * + * Function: + * Convert a unicode character to upper or lower case using + * compressed tables. + * + * Copyright (c) International Business Machines Corp., 2000,2009 + * + * Notes: + * These APIs are based on the C library functions. The semantics + * should match the C functions but with expanded size operands. + * + * The upper/lower functions are based on a table created by mkupr. + * This is a compressed table of upper and lower case conversion. + */ +#ifndef _CIFS_UNICODE_H +#define _CIFS_UNICODE_H + +#include +#include +#include + +#define UNIUPR_NOLOWER /* Example to not expand lower case tables */ + +/* + * Windows maps these to the user defined 16 bit Unicode range since they are + * reserved symbols (along with \ and /), otherwise illegal to store + * in filenames in NTFS + */ +#define UNI_ASTERISK (__u16) ('*' + 0xF000) +#define UNI_QUESTION (__u16) ('?' + 0xF000) +#define UNI_COLON (__u16) (':' + 0xF000) +#define UNI_GRTRTHAN (__u16) ('>' + 0xF000) +#define UNI_LESSTHAN (__u16) ('<' + 0xF000) +#define UNI_PIPE (__u16) ('|' + 0xF000) +#define UNI_SLASH (__u16) ('\\' + 0xF000) + +/* + * Macs use an older "SFM" mapping of the symbols above. Fortunately it does + * not conflict (although almost does) with the mapping above. + */ + +#define SFM_DOUBLEQUOTE ((__u16) 0xF020) +#define SFM_ASTERISK ((__u16) 0xF021) +#define SFM_QUESTION ((__u16) 0xF025) +#define SFM_COLON ((__u16) 0xF022) +#define SFM_GRTRTHAN ((__u16) 0xF024) +#define SFM_LESSTHAN ((__u16) 0xF023) +#define SFM_PIPE ((__u16) 0xF027) +#define SFM_SLASH ((__u16) 0xF026) +#define SFM_SPACE ((__u16) 0xF028) +#define SFM_PERIOD ((__u16) 0xF029) + +/* + * Mapping mechanism to use when one of the seven reserved characters is + * encountered. We can only map using one of the mechanisms at a time + * since otherwise readdir could return directory entries which we would + * not be able to open + * + * NO_MAP_UNI_RSVD = do not perform any remapping of the character + * SFM_MAP_UNI_RSVD = map reserved characters using SFM scheme (MAC compatible) + * SFU_MAP_UNI_RSVD = map reserved characters ala SFU ("mapchars" option) + * + */ +#define NO_MAP_UNI_RSVD 0 +#define SFM_MAP_UNI_RSVD 1 +#define SFU_MAP_UNI_RSVD 2 + +/* Just define what we want from uniupr.h. We don't want to define the tables + * in each source file. + */ +#ifndef UNICASERANGE_DEFINED +struct UniCaseRange { + wchar_t start; + wchar_t end; + signed char *table; +}; +#endif /* UNICASERANGE_DEFINED */ + +#ifndef UNIUPR_NOUPPER +extern signed char CifsUniUpperTable[512]; +extern const struct UniCaseRange CifsUniUpperRange[]; +#endif /* UNIUPR_NOUPPER */ + +#ifndef UNIUPR_NOLOWER +extern signed char CifsUniLowerTable[512]; +extern const struct UniCaseRange CifsUniLowerRange[]; +#endif /* UNIUPR_NOLOWER */ + +#ifdef __KERNEL__ +int cifs_from_utf16(char *to, const __le16 *from, int tolen, int fromlen, + const struct nls_table *cp, int map_type); +int cifs_utf16_bytes(const __le16 *from, int maxbytes, + const struct nls_table *codepage); +int cifs_strtoUTF16(__le16 *, const char *, int, const struct nls_table *); +char *cifs_strndup_from_utf16(const char *src, const int maxlen, + const bool is_unicode, + const struct nls_table *codepage); +extern int cifsConvertToUTF16(__le16 *target, const char *source, int maxlen, + const struct nls_table *cp, int mapChars); +extern int cifs_remap(struct cifs_sb_info *cifs_sb); +extern __le16 *cifs_strndup_to_utf16(const char *src, const int maxlen, + int *utf16_len, const struct nls_table *cp, + int remap); +#endif + +wchar_t cifs_toupper(wchar_t in); + +/* + * UniStrcat: Concatenate the second string to the first + * + * Returns: + * Address of the first string + */ +static inline __le16 * +UniStrcat(__le16 *ucs1, const __le16 *ucs2) +{ + __le16 *anchor = ucs1; /* save a pointer to start of ucs1 */ + + while (*ucs1++) ; /* To end of first string */ + ucs1--; /* Return to the null */ + while ((*ucs1++ = *ucs2++)) ; /* copy string 2 over */ + return anchor; +} + +/* + * UniStrchr: Find a character in a string + * + * Returns: + * Address of first occurrence of character in string + * or NULL if the character is not in the string + */ +static inline wchar_t * +UniStrchr(const wchar_t *ucs, wchar_t uc) +{ + while ((*ucs != uc) && *ucs) + ucs++; + + if (*ucs == uc) + return (wchar_t *) ucs; + return NULL; +} + +/* + * UniStrcmp: Compare two strings + * + * Returns: + * < 0: First string is less than second + * = 0: Strings are equal + * > 0: First string is greater than second + */ +static inline int +UniStrcmp(const wchar_t *ucs1, const wchar_t *ucs2) +{ + while ((*ucs1 == *ucs2) && *ucs1) { + ucs1++; + ucs2++; + } + return (int) *ucs1 - (int) *ucs2; +} + +/* + * UniStrcpy: Copy a string + */ +static inline wchar_t * +UniStrcpy(wchar_t *ucs1, const wchar_t *ucs2) +{ + wchar_t *anchor = ucs1; /* save the start of result string */ + + while ((*ucs1++ = *ucs2++)) ; + return anchor; +} + +/* + * UniStrlen: Return the length of a string (in 16 bit Unicode chars not bytes) + */ +static inline size_t +UniStrlen(const wchar_t *ucs1) +{ + int i = 0; + + while (*ucs1++) + i++; + return i; +} + +/* + * UniStrnlen: Return the length (in 16 bit Unicode chars not bytes) of a + * string (length limited) + */ +static inline size_t +UniStrnlen(const wchar_t *ucs1, int maxlen) +{ + int i = 0; + + while (*ucs1++) { + i++; + if (i >= maxlen) + break; + } + return i; +} + +/* + * UniStrncat: Concatenate length limited string + */ +static inline wchar_t * +UniStrncat(wchar_t *ucs1, const wchar_t *ucs2, size_t n) +{ + wchar_t *anchor = ucs1; /* save pointer to string 1 */ + + while (*ucs1++) ; + ucs1--; /* point to null terminator of s1 */ + while (n-- && (*ucs1 = *ucs2)) { /* copy s2 after s1 */ + ucs1++; + ucs2++; + } + *ucs1 = 0; /* Null terminate the result */ + return (anchor); +} + +/* + * UniStrncmp: Compare length limited string + */ +static inline int +UniStrncmp(const wchar_t *ucs1, const wchar_t *ucs2, size_t n) +{ + if (!n) + return 0; /* Null strings are equal */ + while ((*ucs1 == *ucs2) && *ucs1 && --n) { + ucs1++; + ucs2++; + } + return (int) *ucs1 - (int) *ucs2; +} + +/* + * UniStrncmp_le: Compare length limited string - native to little-endian + */ +static inline int +UniStrncmp_le(const wchar_t *ucs1, const wchar_t *ucs2, size_t n) +{ + if (!n) + return 0; /* Null strings are equal */ + while ((*ucs1 == __le16_to_cpu(*ucs2)) && *ucs1 && --n) { + ucs1++; + ucs2++; + } + return (int) *ucs1 - (int) __le16_to_cpu(*ucs2); +} + +/* + * UniStrncpy: Copy length limited string with pad + */ +static inline wchar_t * +UniStrncpy(wchar_t *ucs1, const wchar_t *ucs2, size_t n) +{ + wchar_t *anchor = ucs1; + + while (n-- && *ucs2) /* Copy the strings */ + *ucs1++ = *ucs2++; + + n++; + while (n--) /* Pad with nulls */ + *ucs1++ = 0; + return anchor; +} + +/* + * UniStrncpy_le: Copy length limited string with pad to little-endian + */ +static inline wchar_t * +UniStrncpy_le(wchar_t *ucs1, const wchar_t *ucs2, size_t n) +{ + wchar_t *anchor = ucs1; + + while (n-- && *ucs2) /* Copy the strings */ + *ucs1++ = __le16_to_cpu(*ucs2++); + + n++; + while (n--) /* Pad with nulls */ + *ucs1++ = 0; + return anchor; +} + +/* + * UniStrstr: Find a string in a string + * + * Returns: + * Address of first match found + * NULL if no matching string is found + */ +static inline wchar_t * +UniStrstr(const wchar_t *ucs1, const wchar_t *ucs2) +{ + const wchar_t *anchor1 = ucs1; + const wchar_t *anchor2 = ucs2; + + while (*ucs1) { + if (*ucs1 == *ucs2) { + /* Partial match found */ + ucs1++; + ucs2++; + } else { + if (!*ucs2) /* Match found */ + return (wchar_t *) anchor1; + ucs1 = ++anchor1; /* No match */ + ucs2 = anchor2; + } + } + + if (!*ucs2) /* Both end together */ + return (wchar_t *) anchor1; /* Match found */ + return NULL; /* No match */ +} + +#ifndef UNIUPR_NOUPPER +/* + * UniToupper: Convert a unicode character to upper case + */ +static inline wchar_t +UniToupper(register wchar_t uc) +{ + register const struct UniCaseRange *rp; + + if (uc < sizeof(CifsUniUpperTable)) { + /* Latin characters */ + return uc + CifsUniUpperTable[uc]; /* Use base tables */ + } else { + rp = CifsUniUpperRange; /* Use range tables */ + while (rp->start) { + if (uc < rp->start) /* Before start of range */ + return uc; /* Uppercase = input */ + if (uc <= rp->end) /* In range */ + return uc + rp->table[uc - rp->start]; + rp++; /* Try next range */ + } + } + return uc; /* Past last range */ +} + +/* + * UniStrupr: Upper case a unicode string + */ +static inline __le16 * +UniStrupr(register __le16 *upin) +{ + register __le16 *up; + + up = upin; + while (*up) { /* For all characters */ + *up = cpu_to_le16(UniToupper(le16_to_cpu(*up))); + up++; + } + return upin; /* Return input pointer */ +} +#endif /* UNIUPR_NOUPPER */ + +#ifndef UNIUPR_NOLOWER +/* + * UniTolower: Convert a unicode character to lower case + */ +static inline wchar_t +UniTolower(register wchar_t uc) +{ + register const struct UniCaseRange *rp; + + if (uc < sizeof(CifsUniLowerTable)) { + /* Latin characters */ + return uc + CifsUniLowerTable[uc]; /* Use base tables */ + } else { + rp = CifsUniLowerRange; /* Use range tables */ + while (rp->start) { + if (uc < rp->start) /* Before start of range */ + return uc; /* Uppercase = input */ + if (uc <= rp->end) /* In range */ + return uc + rp->table[uc - rp->start]; + rp++; /* Try next range */ + } + } + return uc; /* Past last range */ +} + +/* + * UniStrlwr: Lower case a unicode string + */ +static inline wchar_t * +UniStrlwr(register wchar_t *upin) +{ + register wchar_t *up; + + up = upin; + while (*up) { /* For all characters */ + *up = UniTolower(*up); + up++; + } + return upin; /* Return input pointer */ +} + +#endif + +#endif /* _CIFS_UNICODE_H */ diff --git a/fs/smb/client/cifs_uniupr.h b/fs/smb/client/cifs_uniupr.h new file mode 100644 index 0000000000000..7b272fcdf0d3a --- /dev/null +++ b/fs/smb/client/cifs_uniupr.h @@ -0,0 +1,239 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + * Copyright (c) International Business Machines Corp., 2000,2002 + * + * uniupr.h - Unicode compressed case ranges +*/ + +#ifndef UNIUPR_NOUPPER +/* + * Latin upper case + */ +signed char CifsUniUpperTable[512] = { + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 000-00f */ + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 010-01f */ + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 020-02f */ + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 030-03f */ + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 040-04f */ + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 050-05f */ + 0, -32, -32, -32, -32, -32, -32, -32, -32, -32, -32, -32, -32, -32, -32, -32, /* 060-06f */ + -32, -32, -32, -32, -32, -32, -32, -32, -32, -32, -32, 0, 0, 0, 0, 0, /* 070-07f */ + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 080-08f */ + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 090-09f */ + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 0a0-0af */ + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 0b0-0bf */ + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 0c0-0cf */ + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 0d0-0df */ + -32, -32, -32, -32, -32, -32, -32, -32, -32, -32, -32, -32, -32, -32, -32, -32, /* 0e0-0ef */ + -32, -32, -32, -32, -32, -32, -32, 0, -32, -32, -32, -32, -32, -32, -32, 121, /* 0f0-0ff */ + 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, /* 100-10f */ + 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, /* 110-11f */ + 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, /* 120-12f */ + 0, 0, 0, -1, 0, -1, 0, -1, 0, 0, -1, 0, -1, 0, -1, 0, /* 130-13f */ + -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, 0, -1, 0, -1, 0, -1, /* 140-14f */ + 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, /* 150-15f */ + 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, /* 160-16f */ + 0, -1, 0, -1, 0, -1, 0, -1, 0, 0, -1, 0, -1, 0, -1, 0, /* 170-17f */ + 0, 0, 0, -1, 0, -1, 0, 0, -1, 0, 0, 0, -1, 0, 0, 0, /* 180-18f */ + 0, 0, -1, 0, 0, 0, 0, 0, 0, -1, 0, 0, 0, 0, 0, 0, /* 190-19f */ + 0, -1, 0, -1, 0, -1, 0, 0, -1, 0, 0, 0, 0, -1, 0, 0, /* 1a0-1af */ + -1, 0, 0, 0, -1, 0, -1, 0, 0, -1, 0, 0, 0, -1, 0, 0, /* 1b0-1bf */ + 0, 0, 0, 0, 0, -1, -2, 0, -1, -2, 0, -1, -2, 0, -1, 0, /* 1c0-1cf */ + -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, -79, 0, -1, /* 1d0-1df */ + 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, /* 1e0-1ef */ + 0, 0, -1, -2, 0, -1, 0, 0, 0, -1, 0, -1, 0, -1, 0, -1, /* 1f0-1ff */ +}; + +/* Upper case range - Greek */ +static signed char UniCaseRangeU03a0[47] = { + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -38, -37, -37, -37, /* 3a0-3af */ + 0, -32, -32, -32, -32, -32, -32, -32, -32, -32, -32, -32, -32, -32, -32, -32, /* 3b0-3bf */ + -32, -32, -31, -32, -32, -32, -32, -32, -32, -32, -32, -32, -64, + -63, -63, +}; + +/* Upper case range - Cyrillic */ +static signed char UniCaseRangeU0430[48] = { + -32, -32, -32, -32, -32, -32, -32, -32, -32, -32, -32, -32, -32, -32, -32, -32, /* 430-43f */ + -32, -32, -32, -32, -32, -32, -32, -32, -32, -32, -32, -32, -32, -32, -32, -32, /* 440-44f */ + 0, -80, -80, -80, -80, -80, -80, -80, -80, -80, -80, -80, -80, 0, -80, -80, /* 450-45f */ +}; + +/* Upper case range - Extended cyrillic */ +static signed char UniCaseRangeU0490[61] = { + 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, /* 490-49f */ + 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, /* 4a0-4af */ + 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, /* 4b0-4bf */ + 0, 0, -1, 0, -1, 0, 0, 0, -1, 0, 0, 0, -1, +}; + +/* Upper case range - Extended latin and greek */ +static signed char UniCaseRangeU1e00[509] = { + 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, /* 1e00-1e0f */ + 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, /* 1e10-1e1f */ + 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, /* 1e20-1e2f */ + 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, /* 1e30-1e3f */ + 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, /* 1e40-1e4f */ + 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, /* 1e50-1e5f */ + 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, /* 1e60-1e6f */ + 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, /* 1e70-1e7f */ + 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, /* 1e80-1e8f */ + 0, -1, 0, -1, 0, -1, 0, 0, 0, 0, 0, -59, 0, -1, 0, -1, /* 1e90-1e9f */ + 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, /* 1ea0-1eaf */ + 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, /* 1eb0-1ebf */ + 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, /* 1ec0-1ecf */ + 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, /* 1ed0-1edf */ + 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, /* 1ee0-1eef */ + 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, 0, 0, 0, 0, 0, /* 1ef0-1eff */ + 8, 8, 8, 8, 8, 8, 8, 8, 0, 0, 0, 0, 0, 0, 0, 0, /* 1f00-1f0f */ + 8, 8, 8, 8, 8, 8, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 1f10-1f1f */ + 8, 8, 8, 8, 8, 8, 8, 8, 0, 0, 0, 0, 0, 0, 0, 0, /* 1f20-1f2f */ + 8, 8, 8, 8, 8, 8, 8, 8, 0, 0, 0, 0, 0, 0, 0, 0, /* 1f30-1f3f */ + 8, 8, 8, 8, 8, 8, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 1f40-1f4f */ + 0, 8, 0, 8, 0, 8, 0, 8, 0, 0, 0, 0, 0, 0, 0, 0, /* 1f50-1f5f */ + 8, 8, 8, 8, 8, 8, 8, 8, 0, 0, 0, 0, 0, 0, 0, 0, /* 1f60-1f6f */ + 74, 74, 86, 86, 86, 86, 100, 100, 0, 0, 112, 112, 126, 126, 0, 0, /* 1f70-1f7f */ + 8, 8, 8, 8, 8, 8, 8, 8, 0, 0, 0, 0, 0, 0, 0, 0, /* 1f80-1f8f */ + 8, 8, 8, 8, 8, 8, 8, 8, 0, 0, 0, 0, 0, 0, 0, 0, /* 1f90-1f9f */ + 8, 8, 8, 8, 8, 8, 8, 8, 0, 0, 0, 0, 0, 0, 0, 0, /* 1fa0-1faf */ + 8, 8, 0, 9, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 1fb0-1fbf */ + 0, 0, 0, 9, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 1fc0-1fcf */ + 8, 8, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 1fd0-1fdf */ + 8, 8, 0, 0, 0, 7, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 1fe0-1fef */ + 0, 0, 0, 9, 0, 0, 0, 0, 0, 0, 0, 0, 0, +}; + +/* Upper case range - Wide latin */ +static signed char UniCaseRangeUff40[27] = { + 0, -32, -32, -32, -32, -32, -32, -32, -32, -32, -32, -32, -32, -32, -32, -32, /* ff40-ff4f */ + -32, -32, -32, -32, -32, -32, -32, -32, -32, -32, -32, +}; + +/* + * Upper Case Range + */ +const struct UniCaseRange CifsUniUpperRange[] = { + {0x03a0, 0x03ce, UniCaseRangeU03a0}, + {0x0430, 0x045f, UniCaseRangeU0430}, + {0x0490, 0x04cc, UniCaseRangeU0490}, + {0x1e00, 0x1ffc, UniCaseRangeU1e00}, + {0xff40, 0xff5a, UniCaseRangeUff40}, + {0} +}; +#endif + +#ifndef UNIUPR_NOLOWER +/* + * Latin lower case + */ +signed char CifsUniLowerTable[512] = { + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 000-00f */ + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 010-01f */ + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 020-02f */ + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 030-03f */ + 0, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, /* 040-04f */ + 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 0, 0, 0, 0, 0, /* 050-05f */ + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 060-06f */ + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 070-07f */ + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 080-08f */ + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 090-09f */ + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 0a0-0af */ + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 0b0-0bf */ + 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, /* 0c0-0cf */ + 32, 32, 32, 32, 32, 32, 32, 0, 32, 32, 32, 32, 32, 32, 32, 0, /* 0d0-0df */ + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 0e0-0ef */ + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 0f0-0ff */ + 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, /* 100-10f */ + 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, /* 110-11f */ + 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, /* 120-12f */ + 0, 0, 1, 0, 1, 0, 1, 0, 0, 1, 0, 1, 0, 1, 0, 1, /* 130-13f */ + 0, 1, 0, 1, 0, 1, 0, 1, 0, 0, 1, 0, 1, 0, 1, 0, /* 140-14f */ + 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, /* 150-15f */ + 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, /* 160-16f */ + 1, 0, 1, 0, 1, 0, 1, 0, -121, 1, 0, 1, 0, 1, 0, 0, /* 170-17f */ + 0, 0, 1, 0, 1, 0, 0, 1, 0, 0, 0, 1, 0, 0, 79, 0, /* 180-18f */ + 0, 1, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, /* 190-19f */ + 1, 0, 1, 0, 1, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 1, /* 1a0-1af */ + 0, 0, 0, 1, 0, 1, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, /* 1b0-1bf */ + 0, 0, 0, 0, 2, 1, 0, 2, 1, 0, 2, 1, 0, 1, 0, 1, /* 1c0-1cf */ + 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 0, 1, 0, /* 1d0-1df */ + 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, /* 1e0-1ef */ + 0, 2, 1, 0, 1, 0, 0, 0, 1, 0, 1, 0, 1, 0, 1, 0, /* 1f0-1ff */ +}; + +/* Lower case range - Greek */ +static signed char UniCaseRangeL0380[44] = { + 0, 0, 0, 0, 0, 0, 38, 0, 37, 37, 37, 0, 64, 0, 63, 63, /* 380-38f */ + 0, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, /* 390-39f */ + 32, 32, 0, 32, 32, 32, 32, 32, 32, 32, 32, 32, +}; + +/* Lower case range - Cyrillic */ +static signed char UniCaseRangeL0400[48] = { + 0, 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, 0, 80, 80, /* 400-40f */ + 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, /* 410-41f */ + 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, /* 420-42f */ +}; + +/* Lower case range - Extended cyrillic */ +static signed char UniCaseRangeL0490[60] = { + 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, /* 490-49f */ + 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, /* 4a0-4af */ + 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, /* 4b0-4bf */ + 0, 1, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, +}; + +/* Lower case range - Extended latin and greek */ +static signed char UniCaseRangeL1e00[504] = { + 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, /* 1e00-1e0f */ + 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, /* 1e10-1e1f */ + 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, /* 1e20-1e2f */ + 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, /* 1e30-1e3f */ + 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, /* 1e40-1e4f */ + 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, /* 1e50-1e5f */ + 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, /* 1e60-1e6f */ + 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, /* 1e70-1e7f */ + 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, /* 1e80-1e8f */ + 1, 0, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 1, 0, 1, 0, /* 1e90-1e9f */ + 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, /* 1ea0-1eaf */ + 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, /* 1eb0-1ebf */ + 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, /* 1ec0-1ecf */ + 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, /* 1ed0-1edf */ + 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, /* 1ee0-1eef */ + 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, /* 1ef0-1eff */ + 0, 0, 0, 0, 0, 0, 0, 0, -8, -8, -8, -8, -8, -8, -8, -8, /* 1f00-1f0f */ + 0, 0, 0, 0, 0, 0, 0, 0, -8, -8, -8, -8, -8, -8, 0, 0, /* 1f10-1f1f */ + 0, 0, 0, 0, 0, 0, 0, 0, -8, -8, -8, -8, -8, -8, -8, -8, /* 1f20-1f2f */ + 0, 0, 0, 0, 0, 0, 0, 0, -8, -8, -8, -8, -8, -8, -8, -8, /* 1f30-1f3f */ + 0, 0, 0, 0, 0, 0, 0, 0, -8, -8, -8, -8, -8, -8, 0, 0, /* 1f40-1f4f */ + 0, 0, 0, 0, 0, 0, 0, 0, 0, -8, 0, -8, 0, -8, 0, -8, /* 1f50-1f5f */ + 0, 0, 0, 0, 0, 0, 0, 0, -8, -8, -8, -8, -8, -8, -8, -8, /* 1f60-1f6f */ + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 1f70-1f7f */ + 0, 0, 0, 0, 0, 0, 0, 0, -8, -8, -8, -8, -8, -8, -8, -8, /* 1f80-1f8f */ + 0, 0, 0, 0, 0, 0, 0, 0, -8, -8, -8, -8, -8, -8, -8, -8, /* 1f90-1f9f */ + 0, 0, 0, 0, 0, 0, 0, 0, -8, -8, -8, -8, -8, -8, -8, -8, /* 1fa0-1faf */ + 0, 0, 0, 0, 0, 0, 0, 0, -8, -8, -74, -74, -9, 0, 0, 0, /* 1fb0-1fbf */ + 0, 0, 0, 0, 0, 0, 0, 0, -86, -86, -86, -86, -9, 0, 0, 0, /* 1fc0-1fcf */ + 0, 0, 0, 0, 0, 0, 0, 0, -8, -8, -100, -100, 0, 0, 0, 0, /* 1fd0-1fdf */ + 0, 0, 0, 0, 0, 0, 0, 0, -8, -8, -112, -112, -7, 0, 0, 0, /* 1fe0-1fef */ + 0, 0, 0, 0, 0, 0, 0, 0, +}; + +/* Lower case range - Wide latin */ +static signed char UniCaseRangeLff20[27] = { + 0, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, /* ff20-ff2f */ + 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, +}; + +/* + * Lower Case Range + */ +const struct UniCaseRange CifsUniLowerRange[] = { + {0x0380, 0x03ab, UniCaseRangeL0380}, + {0x0400, 0x042f, UniCaseRangeL0400}, + {0x0490, 0x04cb, UniCaseRangeL0490}, + {0x1e00, 0x1ff7, UniCaseRangeL1e00}, + {0xff20, 0xff3a, UniCaseRangeLff20}, + {0} +}; +#endif diff --git a/fs/smb/client/cifsacl.c b/fs/smb/client/cifsacl.c new file mode 100644 index 0000000000000..a6c7566a01821 --- /dev/null +++ b/fs/smb/client/cifsacl.c @@ -0,0 +1,1672 @@ +// SPDX-License-Identifier: LGPL-2.1 +/* + * + * Copyright (C) International Business Machines Corp., 2007,2008 + * Author(s): Steve French (sfrench@us.ibm.com) + * + * Contains the routines for mapping CIFS/NTFS ACLs + * + */ + +#include +#include +#include +#include +#include +#include +#include "cifspdu.h" +#include "cifsglob.h" +#include "cifsacl.h" +#include "cifsproto.h" +#include "cifs_debug.h" +#include "fs_context.h" + +/* security id for everyone/world system group */ +static const struct cifs_sid sid_everyone = { + 1, 1, {0, 0, 0, 0, 0, 1}, {0} }; +/* security id for Authenticated Users system group */ +static const struct cifs_sid sid_authusers = { + 1, 1, {0, 0, 0, 0, 0, 5}, {cpu_to_le32(11)} }; + +/* S-1-22-1 Unmapped Unix users */ +static const struct cifs_sid sid_unix_users = {1, 1, {0, 0, 0, 0, 0, 22}, + {cpu_to_le32(1), 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0} }; + +/* S-1-22-2 Unmapped Unix groups */ +static const struct cifs_sid sid_unix_groups = { 1, 1, {0, 0, 0, 0, 0, 22}, + {cpu_to_le32(2), 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0} }; + +/* + * See https://technet.microsoft.com/en-us/library/hh509017(v=ws.10).aspx + */ + +/* S-1-5-88 MS NFS and Apple style UID/GID/mode */ + +/* S-1-5-88-1 Unix uid */ +static const struct cifs_sid sid_unix_NFS_users = { 1, 2, {0, 0, 0, 0, 0, 5}, + {cpu_to_le32(88), + cpu_to_le32(1), 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0} }; + +/* S-1-5-88-2 Unix gid */ +static const struct cifs_sid sid_unix_NFS_groups = { 1, 2, {0, 0, 0, 0, 0, 5}, + {cpu_to_le32(88), + cpu_to_le32(2), 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0} }; + +/* S-1-5-88-3 Unix mode */ +static const struct cifs_sid sid_unix_NFS_mode = { 1, 2, {0, 0, 0, 0, 0, 5}, + {cpu_to_le32(88), + cpu_to_le32(3), 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0} }; + +static const struct cred *root_cred; + +static int +cifs_idmap_key_instantiate(struct key *key, struct key_preparsed_payload *prep) +{ + char *payload; + + /* + * If the payload is less than or equal to the size of a pointer, then + * an allocation here is wasteful. Just copy the data directly to the + * payload.value union member instead. + * + * With this however, you must check the datalen before trying to + * dereference payload.data! + */ + if (prep->datalen <= sizeof(key->payload)) { + key->payload.data[0] = NULL; + memcpy(&key->payload, prep->data, prep->datalen); + } else { + payload = kmemdup(prep->data, prep->datalen, GFP_KERNEL); + if (!payload) + return -ENOMEM; + key->payload.data[0] = payload; + } + + key->datalen = prep->datalen; + return 0; +} + +static inline void +cifs_idmap_key_destroy(struct key *key) +{ + if (key->datalen > sizeof(key->payload)) + kfree(key->payload.data[0]); +} + +static struct key_type cifs_idmap_key_type = { + .name = "cifs.idmap", + .instantiate = cifs_idmap_key_instantiate, + .destroy = cifs_idmap_key_destroy, + .describe = user_describe, +}; + +static char * +sid_to_key_str(struct cifs_sid *sidptr, unsigned int type) +{ + int i, len; + unsigned int saval; + char *sidstr, *strptr; + unsigned long long id_auth_val; + + /* 3 bytes for prefix */ + sidstr = kmalloc(3 + SID_STRING_BASE_SIZE + + (SID_STRING_SUBAUTH_SIZE * sidptr->num_subauth), + GFP_KERNEL); + if (!sidstr) + return sidstr; + + strptr = sidstr; + len = sprintf(strptr, "%cs:S-%hhu", type == SIDOWNER ? 'o' : 'g', + sidptr->revision); + strptr += len; + + /* The authority field is a single 48-bit number */ + id_auth_val = (unsigned long long)sidptr->authority[5]; + id_auth_val |= (unsigned long long)sidptr->authority[4] << 8; + id_auth_val |= (unsigned long long)sidptr->authority[3] << 16; + id_auth_val |= (unsigned long long)sidptr->authority[2] << 24; + id_auth_val |= (unsigned long long)sidptr->authority[1] << 32; + id_auth_val |= (unsigned long long)sidptr->authority[0] << 48; + + /* + * MS-DTYP states that if the authority is >= 2^32, then it should be + * expressed as a hex value. + */ + if (id_auth_val <= UINT_MAX) + len = sprintf(strptr, "-%llu", id_auth_val); + else + len = sprintf(strptr, "-0x%llx", id_auth_val); + + strptr += len; + + for (i = 0; i < sidptr->num_subauth; ++i) { + saval = le32_to_cpu(sidptr->sub_auth[i]); + len = sprintf(strptr, "-%u", saval); + strptr += len; + } + + return sidstr; +} + +/* + * if the two SIDs (roughly equivalent to a UUID for a user or group) are + * the same returns zero, if they do not match returns non-zero. + */ +static int +compare_sids(const struct cifs_sid *ctsid, const struct cifs_sid *cwsid) +{ + int i; + int num_subauth, num_sat, num_saw; + + if ((!ctsid) || (!cwsid)) + return 1; + + /* compare the revision */ + if (ctsid->revision != cwsid->revision) { + if (ctsid->revision > cwsid->revision) + return 1; + else + return -1; + } + + /* compare all of the six auth values */ + for (i = 0; i < NUM_AUTHS; ++i) { + if (ctsid->authority[i] != cwsid->authority[i]) { + if (ctsid->authority[i] > cwsid->authority[i]) + return 1; + else + return -1; + } + } + + /* compare all of the subauth values if any */ + num_sat = ctsid->num_subauth; + num_saw = cwsid->num_subauth; + num_subauth = num_sat < num_saw ? num_sat : num_saw; + if (num_subauth) { + for (i = 0; i < num_subauth; ++i) { + if (ctsid->sub_auth[i] != cwsid->sub_auth[i]) { + if (le32_to_cpu(ctsid->sub_auth[i]) > + le32_to_cpu(cwsid->sub_auth[i])) + return 1; + else + return -1; + } + } + } + + return 0; /* sids compare/match */ +} + +static bool +is_well_known_sid(const struct cifs_sid *psid, uint32_t *puid, bool is_group) +{ + int i; + int num_subauth; + const struct cifs_sid *pwell_known_sid; + + if (!psid || (puid == NULL)) + return false; + + num_subauth = psid->num_subauth; + + /* check if Mac (or Windows NFS) vs. Samba format for Unix owner SID */ + if (num_subauth == 2) { + if (is_group) + pwell_known_sid = &sid_unix_groups; + else + pwell_known_sid = &sid_unix_users; + } else if (num_subauth == 3) { + if (is_group) + pwell_known_sid = &sid_unix_NFS_groups; + else + pwell_known_sid = &sid_unix_NFS_users; + } else + return false; + + /* compare the revision */ + if (psid->revision != pwell_known_sid->revision) + return false; + + /* compare all of the six auth values */ + for (i = 0; i < NUM_AUTHS; ++i) { + if (psid->authority[i] != pwell_known_sid->authority[i]) { + cifs_dbg(FYI, "auth %d did not match\n", i); + return false; + } + } + + if (num_subauth == 2) { + if (psid->sub_auth[0] != pwell_known_sid->sub_auth[0]) + return false; + + *puid = le32_to_cpu(psid->sub_auth[1]); + } else /* 3 subauths, ie Windows/Mac style */ { + *puid = le32_to_cpu(psid->sub_auth[0]); + if ((psid->sub_auth[0] != pwell_known_sid->sub_auth[0]) || + (psid->sub_auth[1] != pwell_known_sid->sub_auth[1])) + return false; + + *puid = le32_to_cpu(psid->sub_auth[2]); + } + + cifs_dbg(FYI, "Unix UID %d returned from SID\n", *puid); + return true; /* well known sid found, uid returned */ +} + +static __u16 +cifs_copy_sid(struct cifs_sid *dst, const struct cifs_sid *src) +{ + int i; + __u16 size = 1 + 1 + 6; + + dst->revision = src->revision; + dst->num_subauth = min_t(u8, src->num_subauth, SID_MAX_SUB_AUTHORITIES); + for (i = 0; i < NUM_AUTHS; ++i) + dst->authority[i] = src->authority[i]; + for (i = 0; i < dst->num_subauth; ++i) + dst->sub_auth[i] = src->sub_auth[i]; + size += (dst->num_subauth * 4); + + return size; +} + +static int +id_to_sid(unsigned int cid, uint sidtype, struct cifs_sid *ssid) +{ + int rc; + struct key *sidkey; + struct cifs_sid *ksid; + unsigned int ksid_size; + char desc[3 + 10 + 1]; /* 3 byte prefix + 10 bytes for value + NULL */ + const struct cred *saved_cred; + + rc = snprintf(desc, sizeof(desc), "%ci:%u", + sidtype == SIDOWNER ? 'o' : 'g', cid); + if (rc >= sizeof(desc)) + return -EINVAL; + + rc = 0; + saved_cred = override_creds(root_cred); + sidkey = request_key(&cifs_idmap_key_type, desc, ""); + if (IS_ERR(sidkey)) { + rc = -EINVAL; + cifs_dbg(FYI, "%s: Can't map %cid %u to a SID\n", + __func__, sidtype == SIDOWNER ? 'u' : 'g', cid); + goto out_revert_creds; + } else if (sidkey->datalen < CIFS_SID_BASE_SIZE) { + rc = -EIO; + cifs_dbg(FYI, "%s: Downcall contained malformed key (datalen=%hu)\n", + __func__, sidkey->datalen); + goto invalidate_key; + } + + /* + * A sid is usually too large to be embedded in payload.value, but if + * there are no subauthorities and the host has 8-byte pointers, then + * it could be. + */ + ksid = sidkey->datalen <= sizeof(sidkey->payload) ? + (struct cifs_sid *)&sidkey->payload : + (struct cifs_sid *)sidkey->payload.data[0]; + + ksid_size = CIFS_SID_BASE_SIZE + (ksid->num_subauth * sizeof(__le32)); + if (ksid_size > sidkey->datalen) { + rc = -EIO; + cifs_dbg(FYI, "%s: Downcall contained malformed key (datalen=%hu, ksid_size=%u)\n", + __func__, sidkey->datalen, ksid_size); + goto invalidate_key; + } + + cifs_copy_sid(ssid, ksid); +out_key_put: + key_put(sidkey); +out_revert_creds: + revert_creds(saved_cred); + return rc; + +invalidate_key: + key_invalidate(sidkey); + goto out_key_put; +} + +int +sid_to_id(struct cifs_sb_info *cifs_sb, struct cifs_sid *psid, + struct cifs_fattr *fattr, uint sidtype) +{ + int rc = 0; + struct key *sidkey; + char *sidstr; + const struct cred *saved_cred; + kuid_t fuid = cifs_sb->ctx->linux_uid; + kgid_t fgid = cifs_sb->ctx->linux_gid; + + /* + * If we have too many subauthorities, then something is really wrong. + * Just return an error. + */ + if (unlikely(psid->num_subauth > SID_MAX_SUB_AUTHORITIES)) { + cifs_dbg(FYI, "%s: %u subauthorities is too many!\n", + __func__, psid->num_subauth); + return -EIO; + } + + if ((cifs_sb->mnt_cifs_flags & CIFS_MOUNT_UID_FROM_ACL) || + (cifs_sb_master_tcon(cifs_sb)->posix_extensions)) { + uint32_t unix_id; + bool is_group; + + if (sidtype != SIDOWNER) + is_group = true; + else + is_group = false; + + if (is_well_known_sid(psid, &unix_id, is_group) == false) + goto try_upcall_to_get_id; + + if (is_group) { + kgid_t gid; + gid_t id; + + id = (gid_t)unix_id; + gid = make_kgid(&init_user_ns, id); + if (gid_valid(gid)) { + fgid = gid; + goto got_valid_id; + } + } else { + kuid_t uid; + uid_t id; + + id = (uid_t)unix_id; + uid = make_kuid(&init_user_ns, id); + if (uid_valid(uid)) { + fuid = uid; + goto got_valid_id; + } + } + /* If unable to find uid/gid easily from SID try via upcall */ + } + +try_upcall_to_get_id: + sidstr = sid_to_key_str(psid, sidtype); + if (!sidstr) + return -ENOMEM; + + saved_cred = override_creds(root_cred); + sidkey = request_key(&cifs_idmap_key_type, sidstr, ""); + if (IS_ERR(sidkey)) { + cifs_dbg(FYI, "%s: Can't map SID %s to a %cid\n", + __func__, sidstr, sidtype == SIDOWNER ? 'u' : 'g'); + goto out_revert_creds; + } + + /* + * FIXME: Here we assume that uid_t and gid_t are same size. It's + * probably a safe assumption but might be better to check based on + * sidtype. + */ + BUILD_BUG_ON(sizeof(uid_t) != sizeof(gid_t)); + if (sidkey->datalen != sizeof(uid_t)) { + cifs_dbg(FYI, "%s: Downcall contained malformed key (datalen=%hu)\n", + __func__, sidkey->datalen); + key_invalidate(sidkey); + goto out_key_put; + } + + if (sidtype == SIDOWNER) { + kuid_t uid; + uid_t id; + memcpy(&id, &sidkey->payload.data[0], sizeof(uid_t)); + uid = make_kuid(&init_user_ns, id); + if (uid_valid(uid)) + fuid = uid; + } else { + kgid_t gid; + gid_t id; + memcpy(&id, &sidkey->payload.data[0], sizeof(gid_t)); + gid = make_kgid(&init_user_ns, id); + if (gid_valid(gid)) + fgid = gid; + } + +out_key_put: + key_put(sidkey); +out_revert_creds: + revert_creds(saved_cred); + kfree(sidstr); + + /* + * Note that we return 0 here unconditionally. If the mapping + * fails then we just fall back to using the ctx->linux_uid/linux_gid. + */ +got_valid_id: + rc = 0; + if (sidtype == SIDOWNER) + fattr->cf_uid = fuid; + else + fattr->cf_gid = fgid; + return rc; +} + +int +init_cifs_idmap(void) +{ + struct cred *cred; + struct key *keyring; + int ret; + + cifs_dbg(FYI, "Registering the %s key type\n", + cifs_idmap_key_type.name); + + /* create an override credential set with a special thread keyring in + * which requests are cached + * + * this is used to prevent malicious redirections from being installed + * with add_key(). + */ + cred = prepare_kernel_cred(NULL); + if (!cred) + return -ENOMEM; + + keyring = keyring_alloc(".cifs_idmap", + GLOBAL_ROOT_UID, GLOBAL_ROOT_GID, cred, + (KEY_POS_ALL & ~KEY_POS_SETATTR) | + KEY_USR_VIEW | KEY_USR_READ, + KEY_ALLOC_NOT_IN_QUOTA, NULL, NULL); + if (IS_ERR(keyring)) { + ret = PTR_ERR(keyring); + goto failed_put_cred; + } + + ret = register_key_type(&cifs_idmap_key_type); + if (ret < 0) + goto failed_put_key; + + /* instruct request_key() to use this special keyring as a cache for + * the results it looks up */ + set_bit(KEY_FLAG_ROOT_CAN_CLEAR, &keyring->flags); + cred->thread_keyring = keyring; + cred->jit_keyring = KEY_REQKEY_DEFL_THREAD_KEYRING; + root_cred = cred; + + cifs_dbg(FYI, "cifs idmap keyring: %d\n", key_serial(keyring)); + return 0; + +failed_put_key: + key_put(keyring); +failed_put_cred: + put_cred(cred); + return ret; +} + +void +exit_cifs_idmap(void) +{ + key_revoke(root_cred->thread_keyring); + unregister_key_type(&cifs_idmap_key_type); + put_cred(root_cred); + cifs_dbg(FYI, "Unregistered %s key type\n", cifs_idmap_key_type.name); +} + +/* copy ntsd, owner sid, and group sid from a security descriptor to another */ +static __u32 copy_sec_desc(const struct cifs_ntsd *pntsd, + struct cifs_ntsd *pnntsd, + __u32 sidsoffset, + struct cifs_sid *pownersid, + struct cifs_sid *pgrpsid) +{ + struct cifs_sid *owner_sid_ptr, *group_sid_ptr; + struct cifs_sid *nowner_sid_ptr, *ngroup_sid_ptr; + + /* copy security descriptor control portion */ + pnntsd->revision = pntsd->revision; + pnntsd->type = pntsd->type; + pnntsd->dacloffset = cpu_to_le32(sizeof(struct cifs_ntsd)); + pnntsd->sacloffset = 0; + pnntsd->osidoffset = cpu_to_le32(sidsoffset); + pnntsd->gsidoffset = cpu_to_le32(sidsoffset + sizeof(struct cifs_sid)); + + /* copy owner sid */ + if (pownersid) + owner_sid_ptr = pownersid; + else + owner_sid_ptr = (struct cifs_sid *)((char *)pntsd + + le32_to_cpu(pntsd->osidoffset)); + nowner_sid_ptr = (struct cifs_sid *)((char *)pnntsd + sidsoffset); + cifs_copy_sid(nowner_sid_ptr, owner_sid_ptr); + + /* copy group sid */ + if (pgrpsid) + group_sid_ptr = pgrpsid; + else + group_sid_ptr = (struct cifs_sid *)((char *)pntsd + + le32_to_cpu(pntsd->gsidoffset)); + ngroup_sid_ptr = (struct cifs_sid *)((char *)pnntsd + sidsoffset + + sizeof(struct cifs_sid)); + cifs_copy_sid(ngroup_sid_ptr, group_sid_ptr); + + return sidsoffset + (2 * sizeof(struct cifs_sid)); +} + + +/* + change posix mode to reflect permissions + pmode is the existing mode (we only want to overwrite part of this + bits to set can be: S_IRWXU, S_IRWXG or S_IRWXO ie 00700 or 00070 or 00007 +*/ +static void access_flags_to_mode(__le32 ace_flags, int type, umode_t *pmode, + umode_t *pdenied, umode_t mask) +{ + __u32 flags = le32_to_cpu(ace_flags); + /* + * Do not assume "preferred" or "canonical" order. + * The first DENY or ALLOW ACE which matches perfectly is + * the permission to be used. Once allowed or denied, same + * permission in later ACEs do not matter. + */ + + /* If not already allowed, deny these bits */ + if (type == ACCESS_DENIED) { + if (flags & GENERIC_ALL && + !(*pmode & mask & 0777)) + *pdenied |= mask & 0777; + + if (((flags & GENERIC_WRITE) || + ((flags & FILE_WRITE_RIGHTS) == FILE_WRITE_RIGHTS)) && + !(*pmode & mask & 0222)) + *pdenied |= mask & 0222; + + if (((flags & GENERIC_READ) || + ((flags & FILE_READ_RIGHTS) == FILE_READ_RIGHTS)) && + !(*pmode & mask & 0444)) + *pdenied |= mask & 0444; + + if (((flags & GENERIC_EXECUTE) || + ((flags & FILE_EXEC_RIGHTS) == FILE_EXEC_RIGHTS)) && + !(*pmode & mask & 0111)) + *pdenied |= mask & 0111; + + return; + } else if (type != ACCESS_ALLOWED) { + cifs_dbg(VFS, "unknown access control type %d\n", type); + return; + } + /* else ACCESS_ALLOWED type */ + + if ((flags & GENERIC_ALL) && + !(*pdenied & mask & 0777)) { + *pmode |= mask & 0777; + cifs_dbg(NOISY, "all perms\n"); + return; + } + + if (((flags & GENERIC_WRITE) || + ((flags & FILE_WRITE_RIGHTS) == FILE_WRITE_RIGHTS)) && + !(*pdenied & mask & 0222)) + *pmode |= mask & 0222; + + if (((flags & GENERIC_READ) || + ((flags & FILE_READ_RIGHTS) == FILE_READ_RIGHTS)) && + !(*pdenied & mask & 0444)) + *pmode |= mask & 0444; + + if (((flags & GENERIC_EXECUTE) || + ((flags & FILE_EXEC_RIGHTS) == FILE_EXEC_RIGHTS)) && + !(*pdenied & mask & 0111)) + *pmode |= mask & 0111; + + /* If DELETE_CHILD is set only on an owner ACE, set sticky bit */ + if (flags & FILE_DELETE_CHILD) { + if (mask == ACL_OWNER_MASK) { + if (!(*pdenied & 01000)) + *pmode |= 01000; + } else if (!(*pdenied & 01000)) { + *pmode &= ~01000; + *pdenied |= 01000; + } + } + + cifs_dbg(NOISY, "access flags 0x%x mode now %04o\n", flags, *pmode); + return; +} + +/* + Generate access flags to reflect permissions mode is the existing mode. + This function is called for every ACE in the DACL whose SID matches + with either owner or group or everyone. +*/ + +static void mode_to_access_flags(umode_t mode, umode_t bits_to_use, + __u32 *pace_flags) +{ + /* reset access mask */ + *pace_flags = 0x0; + + /* bits to use are either S_IRWXU or S_IRWXG or S_IRWXO */ + mode &= bits_to_use; + + /* check for R/W/X UGO since we do not know whose flags + is this but we have cleared all the bits sans RWX for + either user or group or other as per bits_to_use */ + if (mode & S_IRUGO) + *pace_flags |= SET_FILE_READ_RIGHTS; + if (mode & S_IWUGO) + *pace_flags |= SET_FILE_WRITE_RIGHTS; + if (mode & S_IXUGO) + *pace_flags |= SET_FILE_EXEC_RIGHTS; + + cifs_dbg(NOISY, "mode: %04o, access flags now 0x%x\n", + mode, *pace_flags); + return; +} + +static __u16 cifs_copy_ace(struct cifs_ace *dst, struct cifs_ace *src, struct cifs_sid *psid) +{ + __u16 size = 1 + 1 + 2 + 4; + + dst->type = src->type; + dst->flags = src->flags; + dst->access_req = src->access_req; + + /* Check if there's a replacement sid specified */ + if (psid) + size += cifs_copy_sid(&dst->sid, psid); + else + size += cifs_copy_sid(&dst->sid, &src->sid); + + dst->size = cpu_to_le16(size); + + return size; +} + +static __u16 fill_ace_for_sid(struct cifs_ace *pntace, + const struct cifs_sid *psid, __u64 nmode, + umode_t bits, __u8 access_type, + bool allow_delete_child) +{ + int i; + __u16 size = 0; + __u32 access_req = 0; + + pntace->type = access_type; + pntace->flags = 0x0; + mode_to_access_flags(nmode, bits, &access_req); + + if (access_type == ACCESS_ALLOWED && allow_delete_child) + access_req |= FILE_DELETE_CHILD; + + if (access_type == ACCESS_ALLOWED && !access_req) + access_req = SET_MINIMUM_RIGHTS; + else if (access_type == ACCESS_DENIED) + access_req &= ~SET_MINIMUM_RIGHTS; + + pntace->access_req = cpu_to_le32(access_req); + + pntace->sid.revision = psid->revision; + pntace->sid.num_subauth = psid->num_subauth; + for (i = 0; i < NUM_AUTHS; i++) + pntace->sid.authority[i] = psid->authority[i]; + for (i = 0; i < psid->num_subauth; i++) + pntace->sid.sub_auth[i] = psid->sub_auth[i]; + + size = 1 + 1 + 2 + 4 + 1 + 1 + 6 + (psid->num_subauth * 4); + pntace->size = cpu_to_le16(size); + + return size; +} + + +#ifdef CONFIG_CIFS_DEBUG2 +static void dump_ace(struct cifs_ace *pace, char *end_of_acl) +{ + int num_subauth; + + /* validate that we do not go past end of acl */ + + if (le16_to_cpu(pace->size) < 16) { + cifs_dbg(VFS, "ACE too small %d\n", le16_to_cpu(pace->size)); + return; + } + + if (end_of_acl < (char *)pace + le16_to_cpu(pace->size)) { + cifs_dbg(VFS, "ACL too small to parse ACE\n"); + return; + } + + num_subauth = pace->sid.num_subauth; + if (num_subauth) { + int i; + cifs_dbg(FYI, "ACE revision %d num_auth %d type %d flags %d size %d\n", + pace->sid.revision, pace->sid.num_subauth, pace->type, + pace->flags, le16_to_cpu(pace->size)); + for (i = 0; i < num_subauth; ++i) { + cifs_dbg(FYI, "ACE sub_auth[%d]: 0x%x\n", + i, le32_to_cpu(pace->sid.sub_auth[i])); + } + + /* BB add length check to make sure that we do not have huge + num auths and therefore go off the end */ + } + + return; +} +#endif + +static void parse_dacl(struct cifs_acl *pdacl, char *end_of_acl, + struct cifs_sid *pownersid, struct cifs_sid *pgrpsid, + struct cifs_fattr *fattr, bool mode_from_special_sid) +{ + int i; + int num_aces = 0; + int acl_size; + char *acl_base; + struct cifs_ace **ppace; + + /* BB need to add parm so we can store the SID BB */ + + if (!pdacl) { + /* no DACL in the security descriptor, set + all the permissions for user/group/other */ + fattr->cf_mode |= 0777; + return; + } + + /* validate that we do not go past end of acl */ + if (end_of_acl < (char *)pdacl + le16_to_cpu(pdacl->size)) { + cifs_dbg(VFS, "ACL too small to parse DACL\n"); + return; + } + + cifs_dbg(NOISY, "DACL revision %d size %d num aces %d\n", + le16_to_cpu(pdacl->revision), le16_to_cpu(pdacl->size), + le32_to_cpu(pdacl->num_aces)); + + /* reset rwx permissions for user/group/other. + Also, if num_aces is 0 i.e. DACL has no ACEs, + user/group/other have no permissions */ + fattr->cf_mode &= ~(0777); + + acl_base = (char *)pdacl; + acl_size = sizeof(struct cifs_acl); + + num_aces = le32_to_cpu(pdacl->num_aces); + if (num_aces > 0) { + umode_t denied_mode = 0; + + if (num_aces > ULONG_MAX / sizeof(struct cifs_ace *)) + return; + ppace = kmalloc_array(num_aces, sizeof(struct cifs_ace *), + GFP_KERNEL); + if (!ppace) + return; + + for (i = 0; i < num_aces; ++i) { + ppace[i] = (struct cifs_ace *) (acl_base + acl_size); +#ifdef CONFIG_CIFS_DEBUG2 + dump_ace(ppace[i], end_of_acl); +#endif + if (mode_from_special_sid && + (compare_sids(&(ppace[i]->sid), + &sid_unix_NFS_mode) == 0)) { + /* + * Full permissions are: + * 07777 = S_ISUID | S_ISGID | S_ISVTX | + * S_IRWXU | S_IRWXG | S_IRWXO + */ + fattr->cf_mode &= ~07777; + fattr->cf_mode |= + le32_to_cpu(ppace[i]->sid.sub_auth[2]); + break; + } else { + if (compare_sids(&(ppace[i]->sid), pownersid) == 0) { + access_flags_to_mode(ppace[i]->access_req, + ppace[i]->type, + &fattr->cf_mode, + &denied_mode, + ACL_OWNER_MASK); + } else if (compare_sids(&(ppace[i]->sid), pgrpsid) == 0) { + access_flags_to_mode(ppace[i]->access_req, + ppace[i]->type, + &fattr->cf_mode, + &denied_mode, + ACL_GROUP_MASK); + } else if ((compare_sids(&(ppace[i]->sid), &sid_everyone) == 0) || + (compare_sids(&(ppace[i]->sid), &sid_authusers) == 0)) { + access_flags_to_mode(ppace[i]->access_req, + ppace[i]->type, + &fattr->cf_mode, + &denied_mode, + ACL_EVERYONE_MASK); + } + } + + +/* memcpy((void *)(&(cifscred->aces[i])), + (void *)ppace[i], + sizeof(struct cifs_ace)); */ + + acl_base = (char *)ppace[i]; + acl_size = le16_to_cpu(ppace[i]->size); + } + + kfree(ppace); + } + + return; +} + +unsigned int setup_authusers_ACE(struct cifs_ace *pntace) +{ + int i; + unsigned int ace_size = 20; + + pntace->type = ACCESS_ALLOWED_ACE_TYPE; + pntace->flags = 0x0; + pntace->access_req = cpu_to_le32(GENERIC_ALL); + pntace->sid.num_subauth = 1; + pntace->sid.revision = 1; + for (i = 0; i < NUM_AUTHS; i++) + pntace->sid.authority[i] = sid_authusers.authority[i]; + + pntace->sid.sub_auth[0] = sid_authusers.sub_auth[0]; + + /* size = 1 + 1 + 2 + 4 + 1 + 1 + 6 + (psid->num_subauth*4) */ + pntace->size = cpu_to_le16(ace_size); + return ace_size; +} + +/* + * Fill in the special SID based on the mode. See + * https://technet.microsoft.com/en-us/library/hh509017(v=ws.10).aspx + */ +unsigned int setup_special_mode_ACE(struct cifs_ace *pntace, __u64 nmode) +{ + int i; + unsigned int ace_size = 28; + + pntace->type = ACCESS_DENIED_ACE_TYPE; + pntace->flags = 0x0; + pntace->access_req = 0; + pntace->sid.num_subauth = 3; + pntace->sid.revision = 1; + for (i = 0; i < NUM_AUTHS; i++) + pntace->sid.authority[i] = sid_unix_NFS_mode.authority[i]; + + pntace->sid.sub_auth[0] = sid_unix_NFS_mode.sub_auth[0]; + pntace->sid.sub_auth[1] = sid_unix_NFS_mode.sub_auth[1]; + pntace->sid.sub_auth[2] = cpu_to_le32(nmode & 07777); + + /* size = 1 + 1 + 2 + 4 + 1 + 1 + 6 + (psid->num_subauth*4) */ + pntace->size = cpu_to_le16(ace_size); + return ace_size; +} + +unsigned int setup_special_user_owner_ACE(struct cifs_ace *pntace) +{ + int i; + unsigned int ace_size = 28; + + pntace->type = ACCESS_ALLOWED_ACE_TYPE; + pntace->flags = 0x0; + pntace->access_req = cpu_to_le32(GENERIC_ALL); + pntace->sid.num_subauth = 3; + pntace->sid.revision = 1; + for (i = 0; i < NUM_AUTHS; i++) + pntace->sid.authority[i] = sid_unix_NFS_users.authority[i]; + + pntace->sid.sub_auth[0] = sid_unix_NFS_users.sub_auth[0]; + pntace->sid.sub_auth[1] = sid_unix_NFS_users.sub_auth[1]; + pntace->sid.sub_auth[2] = cpu_to_le32(current_fsgid().val); + + /* size = 1 + 1 + 2 + 4 + 1 + 1 + 6 + (psid->num_subauth*4) */ + pntace->size = cpu_to_le16(ace_size); + return ace_size; +} + +static void populate_new_aces(char *nacl_base, + struct cifs_sid *pownersid, + struct cifs_sid *pgrpsid, + __u64 *pnmode, u32 *pnum_aces, u16 *pnsize, + bool modefromsid) +{ + __u64 nmode; + u32 num_aces = 0; + u16 nsize = 0; + __u64 user_mode; + __u64 group_mode; + __u64 other_mode; + __u64 deny_user_mode = 0; + __u64 deny_group_mode = 0; + bool sticky_set = false; + struct cifs_ace *pnntace = NULL; + + nmode = *pnmode; + num_aces = *pnum_aces; + nsize = *pnsize; + + if (modefromsid) { + pnntace = (struct cifs_ace *) (nacl_base + nsize); + nsize += setup_special_mode_ACE(pnntace, nmode); + num_aces++; + pnntace = (struct cifs_ace *) (nacl_base + nsize); + nsize += setup_authusers_ACE(pnntace); + num_aces++; + goto set_size; + } + + /* + * We'll try to keep the mode as requested by the user. + * But in cases where we cannot meaningfully convert that + * into ACL, return back the updated mode, so that it is + * updated in the inode. + */ + + if (!memcmp(pownersid, pgrpsid, sizeof(struct cifs_sid))) { + /* + * Case when owner and group SIDs are the same. + * Set the more restrictive of the two modes. + */ + user_mode = nmode & (nmode << 3) & 0700; + group_mode = nmode & (nmode >> 3) & 0070; + } else { + user_mode = nmode & 0700; + group_mode = nmode & 0070; + } + + other_mode = nmode & 0007; + + /* We need DENY ACE when the perm is more restrictive than the next sets. */ + deny_user_mode = ~(user_mode) & ((group_mode << 3) | (other_mode << 6)) & 0700; + deny_group_mode = ~(group_mode) & (other_mode << 3) & 0070; + + *pnmode = user_mode | group_mode | other_mode | (nmode & ~0777); + + /* This tells if we should allow delete child for group and everyone. */ + if (nmode & 01000) + sticky_set = true; + + if (deny_user_mode) { + pnntace = (struct cifs_ace *) (nacl_base + nsize); + nsize += fill_ace_for_sid(pnntace, pownersid, deny_user_mode, + 0700, ACCESS_DENIED, false); + num_aces++; + } + + /* Group DENY ACE does not conflict with owner ALLOW ACE. Keep in preferred order*/ + if (deny_group_mode && !(deny_group_mode & (user_mode >> 3))) { + pnntace = (struct cifs_ace *) (nacl_base + nsize); + nsize += fill_ace_for_sid(pnntace, pgrpsid, deny_group_mode, + 0070, ACCESS_DENIED, false); + num_aces++; + } + + pnntace = (struct cifs_ace *) (nacl_base + nsize); + nsize += fill_ace_for_sid(pnntace, pownersid, user_mode, + 0700, ACCESS_ALLOWED, true); + num_aces++; + + /* Group DENY ACE conflicts with owner ALLOW ACE. So keep it after. */ + if (deny_group_mode && (deny_group_mode & (user_mode >> 3))) { + pnntace = (struct cifs_ace *) (nacl_base + nsize); + nsize += fill_ace_for_sid(pnntace, pgrpsid, deny_group_mode, + 0070, ACCESS_DENIED, false); + num_aces++; + } + + pnntace = (struct cifs_ace *) (nacl_base + nsize); + nsize += fill_ace_for_sid(pnntace, pgrpsid, group_mode, + 0070, ACCESS_ALLOWED, !sticky_set); + num_aces++; + + pnntace = (struct cifs_ace *) (nacl_base + nsize); + nsize += fill_ace_for_sid(pnntace, &sid_everyone, other_mode, + 0007, ACCESS_ALLOWED, !sticky_set); + num_aces++; + +set_size: + *pnum_aces = num_aces; + *pnsize = nsize; +} + +static __u16 replace_sids_and_copy_aces(struct cifs_acl *pdacl, struct cifs_acl *pndacl, + struct cifs_sid *pownersid, struct cifs_sid *pgrpsid, + struct cifs_sid *pnownersid, struct cifs_sid *pngrpsid) +{ + int i; + u16 size = 0; + struct cifs_ace *pntace = NULL; + char *acl_base = NULL; + u32 src_num_aces = 0; + u16 nsize = 0; + struct cifs_ace *pnntace = NULL; + char *nacl_base = NULL; + u16 ace_size = 0; + + acl_base = (char *)pdacl; + size = sizeof(struct cifs_acl); + src_num_aces = le32_to_cpu(pdacl->num_aces); + + nacl_base = (char *)pndacl; + nsize = sizeof(struct cifs_acl); + + /* Go through all the ACEs */ + for (i = 0; i < src_num_aces; ++i) { + pntace = (struct cifs_ace *) (acl_base + size); + pnntace = (struct cifs_ace *) (nacl_base + nsize); + + if (pnownersid && compare_sids(&pntace->sid, pownersid) == 0) + ace_size = cifs_copy_ace(pnntace, pntace, pnownersid); + else if (pngrpsid && compare_sids(&pntace->sid, pgrpsid) == 0) + ace_size = cifs_copy_ace(pnntace, pntace, pngrpsid); + else + ace_size = cifs_copy_ace(pnntace, pntace, NULL); + + size += le16_to_cpu(pntace->size); + nsize += ace_size; + } + + return nsize; +} + +static int set_chmod_dacl(struct cifs_acl *pdacl, struct cifs_acl *pndacl, + struct cifs_sid *pownersid, struct cifs_sid *pgrpsid, + __u64 *pnmode, bool mode_from_sid) +{ + int i; + u16 size = 0; + struct cifs_ace *pntace = NULL; + char *acl_base = NULL; + u32 src_num_aces = 0; + u16 nsize = 0; + struct cifs_ace *pnntace = NULL; + char *nacl_base = NULL; + u32 num_aces = 0; + bool new_aces_set = false; + + /* Assuming that pndacl and pnmode are never NULL */ + nacl_base = (char *)pndacl; + nsize = sizeof(struct cifs_acl); + + /* If pdacl is NULL, we don't have a src. Simply populate new ACL. */ + if (!pdacl) { + populate_new_aces(nacl_base, + pownersid, pgrpsid, + pnmode, &num_aces, &nsize, + mode_from_sid); + goto finalize_dacl; + } + + acl_base = (char *)pdacl; + size = sizeof(struct cifs_acl); + src_num_aces = le32_to_cpu(pdacl->num_aces); + + /* Retain old ACEs which we can retain */ + for (i = 0; i < src_num_aces; ++i) { + pntace = (struct cifs_ace *) (acl_base + size); + + if (!new_aces_set && (pntace->flags & INHERITED_ACE)) { + /* Place the new ACEs in between existing explicit and inherited */ + populate_new_aces(nacl_base, + pownersid, pgrpsid, + pnmode, &num_aces, &nsize, + mode_from_sid); + + new_aces_set = true; + } + + /* If it's any one of the ACE we're replacing, skip! */ + if (((compare_sids(&pntace->sid, &sid_unix_NFS_mode) == 0) || + (compare_sids(&pntace->sid, pownersid) == 0) || + (compare_sids(&pntace->sid, pgrpsid) == 0) || + (compare_sids(&pntace->sid, &sid_everyone) == 0) || + (compare_sids(&pntace->sid, &sid_authusers) == 0))) { + goto next_ace; + } + + /* update the pointer to the next ACE to populate*/ + pnntace = (struct cifs_ace *) (nacl_base + nsize); + + nsize += cifs_copy_ace(pnntace, pntace, NULL); + num_aces++; + +next_ace: + size += le16_to_cpu(pntace->size); + } + + /* If inherited ACEs are not present, place the new ones at the tail */ + if (!new_aces_set) { + populate_new_aces(nacl_base, + pownersid, pgrpsid, + pnmode, &num_aces, &nsize, + mode_from_sid); + + new_aces_set = true; + } + +finalize_dacl: + pndacl->num_aces = cpu_to_le32(num_aces); + pndacl->size = cpu_to_le16(nsize); + + return 0; +} + +static int parse_sid(struct cifs_sid *psid, char *end_of_acl) +{ + /* BB need to add parm so we can store the SID BB */ + + /* validate that we do not go past end of ACL - sid must be at least 8 + bytes long (assuming no sub-auths - e.g. the null SID */ + if (end_of_acl < (char *)psid + 8) { + cifs_dbg(VFS, "ACL too small to parse SID %p\n", psid); + return -EINVAL; + } + +#ifdef CONFIG_CIFS_DEBUG2 + if (psid->num_subauth) { + int i; + cifs_dbg(FYI, "SID revision %d num_auth %d\n", + psid->revision, psid->num_subauth); + + for (i = 0; i < psid->num_subauth; i++) { + cifs_dbg(FYI, "SID sub_auth[%d]: 0x%x\n", + i, le32_to_cpu(psid->sub_auth[i])); + } + + /* BB add length check to make sure that we do not have huge + num auths and therefore go off the end */ + cifs_dbg(FYI, "RID 0x%x\n", + le32_to_cpu(psid->sub_auth[psid->num_subauth-1])); + } +#endif + + return 0; +} + + +/* Convert CIFS ACL to POSIX form */ +static int parse_sec_desc(struct cifs_sb_info *cifs_sb, + struct cifs_ntsd *pntsd, int acl_len, struct cifs_fattr *fattr, + bool get_mode_from_special_sid) +{ + int rc = 0; + struct cifs_sid *owner_sid_ptr, *group_sid_ptr; + struct cifs_acl *dacl_ptr; /* no need for SACL ptr */ + char *end_of_acl = ((char *)pntsd) + acl_len; + __u32 dacloffset; + + if (pntsd == NULL) + return -EIO; + + owner_sid_ptr = (struct cifs_sid *)((char *)pntsd + + le32_to_cpu(pntsd->osidoffset)); + group_sid_ptr = (struct cifs_sid *)((char *)pntsd + + le32_to_cpu(pntsd->gsidoffset)); + dacloffset = le32_to_cpu(pntsd->dacloffset); + dacl_ptr = (struct cifs_acl *)((char *)pntsd + dacloffset); + cifs_dbg(NOISY, "revision %d type 0x%x ooffset 0x%x goffset 0x%x sacloffset 0x%x dacloffset 0x%x\n", + pntsd->revision, pntsd->type, le32_to_cpu(pntsd->osidoffset), + le32_to_cpu(pntsd->gsidoffset), + le32_to_cpu(pntsd->sacloffset), dacloffset); +/* cifs_dump_mem("owner_sid: ", owner_sid_ptr, 64); */ + rc = parse_sid(owner_sid_ptr, end_of_acl); + if (rc) { + cifs_dbg(FYI, "%s: Error %d parsing Owner SID\n", __func__, rc); + return rc; + } + rc = sid_to_id(cifs_sb, owner_sid_ptr, fattr, SIDOWNER); + if (rc) { + cifs_dbg(FYI, "%s: Error %d mapping Owner SID to uid\n", + __func__, rc); + return rc; + } + + rc = parse_sid(group_sid_ptr, end_of_acl); + if (rc) { + cifs_dbg(FYI, "%s: Error %d mapping Owner SID to gid\n", + __func__, rc); + return rc; + } + rc = sid_to_id(cifs_sb, group_sid_ptr, fattr, SIDGROUP); + if (rc) { + cifs_dbg(FYI, "%s: Error %d mapping Group SID to gid\n", + __func__, rc); + return rc; + } + + if (dacloffset) + parse_dacl(dacl_ptr, end_of_acl, owner_sid_ptr, + group_sid_ptr, fattr, get_mode_from_special_sid); + else + cifs_dbg(FYI, "no ACL\n"); /* BB grant all or default perms? */ + + return rc; +} + +/* Convert permission bits from mode to equivalent CIFS ACL */ +static int build_sec_desc(struct cifs_ntsd *pntsd, struct cifs_ntsd *pnntsd, + __u32 secdesclen, __u32 *pnsecdesclen, __u64 *pnmode, kuid_t uid, kgid_t gid, + bool mode_from_sid, bool id_from_sid, int *aclflag) +{ + int rc = 0; + __u32 dacloffset; + __u32 ndacloffset; + __u32 sidsoffset; + struct cifs_sid *owner_sid_ptr, *group_sid_ptr; + struct cifs_sid *nowner_sid_ptr = NULL, *ngroup_sid_ptr = NULL; + struct cifs_acl *dacl_ptr = NULL; /* no need for SACL ptr */ + struct cifs_acl *ndacl_ptr = NULL; /* no need for SACL ptr */ + char *end_of_acl = ((char *)pntsd) + secdesclen; + u16 size = 0; + + dacloffset = le32_to_cpu(pntsd->dacloffset); + if (dacloffset) { + dacl_ptr = (struct cifs_acl *)((char *)pntsd + dacloffset); + if (end_of_acl < (char *)dacl_ptr + le16_to_cpu(dacl_ptr->size)) { + cifs_dbg(VFS, "Server returned illegal ACL size\n"); + return -EINVAL; + } + } + + owner_sid_ptr = (struct cifs_sid *)((char *)pntsd + + le32_to_cpu(pntsd->osidoffset)); + group_sid_ptr = (struct cifs_sid *)((char *)pntsd + + le32_to_cpu(pntsd->gsidoffset)); + + if (pnmode && *pnmode != NO_CHANGE_64) { /* chmod */ + ndacloffset = sizeof(struct cifs_ntsd); + ndacl_ptr = (struct cifs_acl *)((char *)pnntsd + ndacloffset); + ndacl_ptr->revision = + dacloffset ? dacl_ptr->revision : cpu_to_le16(ACL_REVISION); + + ndacl_ptr->size = cpu_to_le16(0); + ndacl_ptr->num_aces = cpu_to_le32(0); + + rc = set_chmod_dacl(dacl_ptr, ndacl_ptr, owner_sid_ptr, group_sid_ptr, + pnmode, mode_from_sid); + + sidsoffset = ndacloffset + le16_to_cpu(ndacl_ptr->size); + /* copy the non-dacl portion of secdesc */ + *pnsecdesclen = copy_sec_desc(pntsd, pnntsd, sidsoffset, + NULL, NULL); + + *aclflag |= CIFS_ACL_DACL; + } else { + ndacloffset = sizeof(struct cifs_ntsd); + ndacl_ptr = (struct cifs_acl *)((char *)pnntsd + ndacloffset); + ndacl_ptr->revision = + dacloffset ? dacl_ptr->revision : cpu_to_le16(ACL_REVISION); + ndacl_ptr->num_aces = dacl_ptr ? dacl_ptr->num_aces : 0; + + if (uid_valid(uid)) { /* chown */ + uid_t id; + nowner_sid_ptr = kzalloc(sizeof(struct cifs_sid), + GFP_KERNEL); + if (!nowner_sid_ptr) { + rc = -ENOMEM; + goto chown_chgrp_exit; + } + id = from_kuid(&init_user_ns, uid); + if (id_from_sid) { + struct owner_sid *osid = (struct owner_sid *)nowner_sid_ptr; + /* Populate the user ownership fields S-1-5-88-1 */ + osid->Revision = 1; + osid->NumAuth = 3; + osid->Authority[5] = 5; + osid->SubAuthorities[0] = cpu_to_le32(88); + osid->SubAuthorities[1] = cpu_to_le32(1); + osid->SubAuthorities[2] = cpu_to_le32(id); + + } else { /* lookup sid with upcall */ + rc = id_to_sid(id, SIDOWNER, nowner_sid_ptr); + if (rc) { + cifs_dbg(FYI, "%s: Mapping error %d for owner id %d\n", + __func__, rc, id); + goto chown_chgrp_exit; + } + } + *aclflag |= CIFS_ACL_OWNER; + } + if (gid_valid(gid)) { /* chgrp */ + gid_t id; + ngroup_sid_ptr = kzalloc(sizeof(struct cifs_sid), + GFP_KERNEL); + if (!ngroup_sid_ptr) { + rc = -ENOMEM; + goto chown_chgrp_exit; + } + id = from_kgid(&init_user_ns, gid); + if (id_from_sid) { + struct owner_sid *gsid = (struct owner_sid *)ngroup_sid_ptr; + /* Populate the group ownership fields S-1-5-88-2 */ + gsid->Revision = 1; + gsid->NumAuth = 3; + gsid->Authority[5] = 5; + gsid->SubAuthorities[0] = cpu_to_le32(88); + gsid->SubAuthorities[1] = cpu_to_le32(2); + gsid->SubAuthorities[2] = cpu_to_le32(id); + + } else { /* lookup sid with upcall */ + rc = id_to_sid(id, SIDGROUP, ngroup_sid_ptr); + if (rc) { + cifs_dbg(FYI, "%s: Mapping error %d for group id %d\n", + __func__, rc, id); + goto chown_chgrp_exit; + } + } + *aclflag |= CIFS_ACL_GROUP; + } + + if (dacloffset) { + /* Replace ACEs for old owner with new one */ + size = replace_sids_and_copy_aces(dacl_ptr, ndacl_ptr, + owner_sid_ptr, group_sid_ptr, + nowner_sid_ptr, ngroup_sid_ptr); + ndacl_ptr->size = cpu_to_le16(size); + } + + sidsoffset = ndacloffset + le16_to_cpu(ndacl_ptr->size); + /* copy the non-dacl portion of secdesc */ + *pnsecdesclen = copy_sec_desc(pntsd, pnntsd, sidsoffset, + nowner_sid_ptr, ngroup_sid_ptr); + +chown_chgrp_exit: + /* errors could jump here. So make sure we return soon after this */ + kfree(nowner_sid_ptr); + kfree(ngroup_sid_ptr); + } + + return rc; +} + +#ifdef CONFIG_CIFS_ALLOW_INSECURE_LEGACY +struct cifs_ntsd *get_cifs_acl_by_fid(struct cifs_sb_info *cifs_sb, + const struct cifs_fid *cifsfid, u32 *pacllen, + u32 __maybe_unused unused) +{ + struct cifs_ntsd *pntsd = NULL; + unsigned int xid; + int rc; + struct tcon_link *tlink = cifs_sb_tlink(cifs_sb); + + if (IS_ERR(tlink)) + return ERR_CAST(tlink); + + xid = get_xid(); + rc = CIFSSMBGetCIFSACL(xid, tlink_tcon(tlink), cifsfid->netfid, &pntsd, + pacllen); + free_xid(xid); + + cifs_put_tlink(tlink); + + cifs_dbg(FYI, "%s: rc = %d ACL len %d\n", __func__, rc, *pacllen); + if (rc) + return ERR_PTR(rc); + return pntsd; +} + +static struct cifs_ntsd *get_cifs_acl_by_path(struct cifs_sb_info *cifs_sb, + const char *path, u32 *pacllen) +{ + struct cifs_ntsd *pntsd = NULL; + int oplock = 0; + unsigned int xid; + int rc; + struct cifs_tcon *tcon; + struct tcon_link *tlink = cifs_sb_tlink(cifs_sb); + struct cifs_fid fid; + struct cifs_open_parms oparms; + + if (IS_ERR(tlink)) + return ERR_CAST(tlink); + + tcon = tlink_tcon(tlink); + xid = get_xid(); + + oparms = (struct cifs_open_parms) { + .tcon = tcon, + .cifs_sb = cifs_sb, + .desired_access = READ_CONTROL, + .create_options = cifs_create_options(cifs_sb, 0), + .disposition = FILE_OPEN, + .path = path, + .fid = &fid, + }; + + rc = CIFS_open(xid, &oparms, &oplock, NULL); + if (!rc) { + rc = CIFSSMBGetCIFSACL(xid, tcon, fid.netfid, &pntsd, pacllen); + CIFSSMBClose(xid, tcon, fid.netfid); + } + + cifs_put_tlink(tlink); + free_xid(xid); + + cifs_dbg(FYI, "%s: rc = %d ACL len %d\n", __func__, rc, *pacllen); + if (rc) + return ERR_PTR(rc); + return pntsd; +} + +/* Retrieve an ACL from the server */ +struct cifs_ntsd *get_cifs_acl(struct cifs_sb_info *cifs_sb, + struct inode *inode, const char *path, + u32 *pacllen, u32 info) +{ + struct cifs_ntsd *pntsd = NULL; + struct cifsFileInfo *open_file = NULL; + + if (inode) + open_file = find_readable_file(CIFS_I(inode), true); + if (!open_file) + return get_cifs_acl_by_path(cifs_sb, path, pacllen); + + pntsd = get_cifs_acl_by_fid(cifs_sb, &open_file->fid, pacllen, info); + cifsFileInfo_put(open_file); + return pntsd; +} + + /* Set an ACL on the server */ +int set_cifs_acl(struct cifs_ntsd *pnntsd, __u32 acllen, + struct inode *inode, const char *path, int aclflag) +{ + int oplock = 0; + unsigned int xid; + int rc, access_flags; + struct cifs_tcon *tcon; + struct cifs_sb_info *cifs_sb = CIFS_SB(inode->i_sb); + struct tcon_link *tlink = cifs_sb_tlink(cifs_sb); + struct cifs_fid fid; + struct cifs_open_parms oparms; + + if (IS_ERR(tlink)) + return PTR_ERR(tlink); + + tcon = tlink_tcon(tlink); + xid = get_xid(); + + if (aclflag == CIFS_ACL_OWNER || aclflag == CIFS_ACL_GROUP) + access_flags = WRITE_OWNER; + else + access_flags = WRITE_DAC; + + oparms = (struct cifs_open_parms) { + .tcon = tcon, + .cifs_sb = cifs_sb, + .desired_access = access_flags, + .create_options = cifs_create_options(cifs_sb, 0), + .disposition = FILE_OPEN, + .path = path, + .fid = &fid, + }; + + rc = CIFS_open(xid, &oparms, &oplock, NULL); + if (rc) { + cifs_dbg(VFS, "Unable to open file to set ACL\n"); + goto out; + } + + rc = CIFSSMBSetCIFSACL(xid, tcon, fid.netfid, pnntsd, acllen, aclflag); + cifs_dbg(NOISY, "SetCIFSACL rc = %d\n", rc); + + CIFSSMBClose(xid, tcon, fid.netfid); +out: + free_xid(xid); + cifs_put_tlink(tlink); + return rc; +} +#endif /* CONFIG_CIFS_ALLOW_INSECURE_LEGACY */ + +/* Translate the CIFS ACL (similar to NTFS ACL) for a file into mode bits */ +int +cifs_acl_to_fattr(struct cifs_sb_info *cifs_sb, struct cifs_fattr *fattr, + struct inode *inode, bool mode_from_special_sid, + const char *path, const struct cifs_fid *pfid) +{ + struct cifs_ntsd *pntsd = NULL; + u32 acllen = 0; + int rc = 0; + struct tcon_link *tlink = cifs_sb_tlink(cifs_sb); + struct smb_version_operations *ops; + const u32 info = 0; + + cifs_dbg(NOISY, "converting ACL to mode for %s\n", path); + + if (IS_ERR(tlink)) + return PTR_ERR(tlink); + + ops = tlink_tcon(tlink)->ses->server->ops; + + if (pfid && (ops->get_acl_by_fid)) + pntsd = ops->get_acl_by_fid(cifs_sb, pfid, &acllen, info); + else if (ops->get_acl) + pntsd = ops->get_acl(cifs_sb, inode, path, &acllen, info); + else { + cifs_put_tlink(tlink); + return -EOPNOTSUPP; + } + /* if we can retrieve the ACL, now parse Access Control Entries, ACEs */ + if (IS_ERR(pntsd)) { + rc = PTR_ERR(pntsd); + cifs_dbg(VFS, "%s: error %d getting sec desc\n", __func__, rc); + } else if (mode_from_special_sid) { + rc = parse_sec_desc(cifs_sb, pntsd, acllen, fattr, true); + kfree(pntsd); + } else { + /* get approximated mode from ACL */ + rc = parse_sec_desc(cifs_sb, pntsd, acllen, fattr, false); + kfree(pntsd); + if (rc) + cifs_dbg(VFS, "parse sec desc failed rc = %d\n", rc); + } + + cifs_put_tlink(tlink); + + return rc; +} + +/* Convert mode bits to an ACL so we can update the ACL on the server */ +int +id_mode_to_cifs_acl(struct inode *inode, const char *path, __u64 *pnmode, + kuid_t uid, kgid_t gid) +{ + int rc = 0; + int aclflag = CIFS_ACL_DACL; /* default flag to set */ + __u32 secdesclen = 0; + __u32 nsecdesclen = 0; + __u32 dacloffset = 0; + struct cifs_acl *dacl_ptr = NULL; + struct cifs_ntsd *pntsd = NULL; /* acl obtained from server */ + struct cifs_ntsd *pnntsd = NULL; /* modified acl to be sent to server */ + struct cifs_sb_info *cifs_sb = CIFS_SB(inode->i_sb); + struct tcon_link *tlink = cifs_sb_tlink(cifs_sb); + struct smb_version_operations *ops; + bool mode_from_sid, id_from_sid; + const u32 info = 0; + + if (IS_ERR(tlink)) + return PTR_ERR(tlink); + + ops = tlink_tcon(tlink)->ses->server->ops; + + cifs_dbg(NOISY, "set ACL from mode for %s\n", path); + + /* Get the security descriptor */ + + if (ops->get_acl == NULL) { + cifs_put_tlink(tlink); + return -EOPNOTSUPP; + } + + pntsd = ops->get_acl(cifs_sb, inode, path, &secdesclen, info); + if (IS_ERR(pntsd)) { + rc = PTR_ERR(pntsd); + cifs_dbg(VFS, "%s: error %d getting sec desc\n", __func__, rc); + cifs_put_tlink(tlink); + return rc; + } + + if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_MODE_FROM_SID) + mode_from_sid = true; + else + mode_from_sid = false; + + if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_UID_FROM_ACL) + id_from_sid = true; + else + id_from_sid = false; + + /* Potentially, five new ACEs can be added to the ACL for U,G,O mapping */ + nsecdesclen = secdesclen; + if (pnmode && *pnmode != NO_CHANGE_64) { /* chmod */ + if (mode_from_sid) + nsecdesclen += 2 * sizeof(struct cifs_ace); + else /* cifsacl */ + nsecdesclen += 5 * sizeof(struct cifs_ace); + } else { /* chown */ + /* When ownership changes, changes new owner sid length could be different */ + nsecdesclen = sizeof(struct cifs_ntsd) + (sizeof(struct cifs_sid) * 2); + dacloffset = le32_to_cpu(pntsd->dacloffset); + if (dacloffset) { + dacl_ptr = (struct cifs_acl *)((char *)pntsd + dacloffset); + if (mode_from_sid) + nsecdesclen += + le32_to_cpu(dacl_ptr->num_aces) * sizeof(struct cifs_ace); + else /* cifsacl */ + nsecdesclen += le16_to_cpu(dacl_ptr->size); + } + } + + /* + * Add three ACEs for owner, group, everyone getting rid of other ACEs + * as chmod disables ACEs and set the security descriptor. Allocate + * memory for the smb header, set security descriptor request security + * descriptor parameters, and security descriptor itself + */ + nsecdesclen = max_t(u32, nsecdesclen, DEFAULT_SEC_DESC_LEN); + pnntsd = kmalloc(nsecdesclen, GFP_KERNEL); + if (!pnntsd) { + kfree(pntsd); + cifs_put_tlink(tlink); + return -ENOMEM; + } + + rc = build_sec_desc(pntsd, pnntsd, secdesclen, &nsecdesclen, pnmode, uid, gid, + mode_from_sid, id_from_sid, &aclflag); + + cifs_dbg(NOISY, "build_sec_desc rc: %d\n", rc); + + if (ops->set_acl == NULL) + rc = -EOPNOTSUPP; + + if (!rc) { + /* Set the security descriptor */ + rc = ops->set_acl(pnntsd, nsecdesclen, inode, path, aclflag); + cifs_dbg(NOISY, "set_cifs_acl rc: %d\n", rc); + } + cifs_put_tlink(tlink); + + kfree(pnntsd); + kfree(pntsd); + return rc; +} diff --git a/fs/smb/client/cifsacl.h b/fs/smb/client/cifsacl.h new file mode 100644 index 0000000000000..ccbfc754bd3c7 --- /dev/null +++ b/fs/smb/client/cifsacl.h @@ -0,0 +1,199 @@ +/* SPDX-License-Identifier: LGPL-2.1 */ +/* + * + * Copyright (c) International Business Machines Corp., 2007 + * Author(s): Steve French (sfrench@us.ibm.com) + * + */ + +#ifndef _CIFSACL_H +#define _CIFSACL_H + +#define NUM_AUTHS (6) /* number of authority fields */ +#define SID_MAX_SUB_AUTHORITIES (15) /* max number of sub authority fields */ + +#define READ_BIT 0x4 +#define WRITE_BIT 0x2 +#define EXEC_BIT 0x1 + +#define ACL_OWNER_MASK 0700 +#define ACL_GROUP_MASK 0070 +#define ACL_EVERYONE_MASK 0007 + +#define UBITSHIFT 6 +#define GBITSHIFT 3 + +#define ACCESS_ALLOWED 0 +#define ACCESS_DENIED 1 + +#define SIDOWNER 1 +#define SIDGROUP 2 + +/* + * Security Descriptor length containing DACL with 3 ACEs (one each for + * owner, group and world). + */ +#define DEFAULT_SEC_DESC_LEN (sizeof(struct cifs_ntsd) + \ + sizeof(struct cifs_acl) + \ + (sizeof(struct cifs_ace) * 4)) + +/* + * Maximum size of a string representation of a SID: + * + * The fields are unsigned values in decimal. So: + * + * u8: max 3 bytes in decimal + * u32: max 10 bytes in decimal + * + * "S-" + 3 bytes for version field + 15 for authority field + NULL terminator + * + * For authority field, max is when all 6 values are non-zero and it must be + * represented in hex. So "-0x" + 12 hex digits. + * + * Add 11 bytes for each subauthority field (10 bytes each + 1 for '-') + */ +#define SID_STRING_BASE_SIZE (2 + 3 + 15 + 1) +#define SID_STRING_SUBAUTH_SIZE (11) /* size of a single subauth string */ + +struct cifs_ntsd { + __le16 revision; /* revision level */ + __le16 type; + __le32 osidoffset; + __le32 gsidoffset; + __le32 sacloffset; + __le32 dacloffset; +} __attribute__((packed)); + +struct cifs_sid { + __u8 revision; /* revision level */ + __u8 num_subauth; + __u8 authority[NUM_AUTHS]; + __le32 sub_auth[SID_MAX_SUB_AUTHORITIES]; /* sub_auth[num_subauth] */ +} __attribute__((packed)); + +/* size of a struct cifs_sid, sans sub_auth array */ +#define CIFS_SID_BASE_SIZE (1 + 1 + NUM_AUTHS) + +struct cifs_acl { + __le16 revision; /* revision level */ + __le16 size; + __le32 num_aces; +} __attribute__((packed)); + +/* ACE types - see MS-DTYP 2.4.4.1 */ +#define ACCESS_ALLOWED_ACE_TYPE 0x00 +#define ACCESS_DENIED_ACE_TYPE 0x01 +#define SYSTEM_AUDIT_ACE_TYPE 0x02 +#define SYSTEM_ALARM_ACE_TYPE 0x03 +#define ACCESS_ALLOWED_COMPOUND_ACE_TYPE 0x04 +#define ACCESS_ALLOWED_OBJECT_ACE_TYPE 0x05 +#define ACCESS_DENIED_OBJECT_ACE_TYPE 0x06 +#define SYSTEM_AUDIT_OBJECT_ACE_TYPE 0x07 +#define SYSTEM_ALARM_OBJECT_ACE_TYPE 0x08 +#define ACCESS_ALLOWED_CALLBACK_ACE_TYPE 0x09 +#define ACCESS_DENIED_CALLBACK_ACE_TYPE 0x0A +#define ACCESS_ALLOWED_CALLBACK_OBJECT_ACE_TYPE 0x0B +#define ACCESS_DENIED_CALLBACK_OBJECT_ACE_TYPE 0x0C +#define SYSTEM_AUDIT_CALLBACK_ACE_TYPE 0x0D +#define SYSTEM_ALARM_CALLBACK_ACE_TYPE 0x0E /* Reserved */ +#define SYSTEM_AUDIT_CALLBACK_OBJECT_ACE_TYPE 0x0F +#define SYSTEM_ALARM_CALLBACK_OBJECT_ACE_TYPE 0x10 /* reserved */ +#define SYSTEM_MANDATORY_LABEL_ACE_TYPE 0x11 +#define SYSTEM_RESOURCE_ATTRIBUTE_ACE_TYPE 0x12 +#define SYSTEM_SCOPED_POLICY_ID_ACE_TYPE 0x13 + +/* ACE flags */ +#define OBJECT_INHERIT_ACE 0x01 +#define CONTAINER_INHERIT_ACE 0x02 +#define NO_PROPAGATE_INHERIT_ACE 0x04 +#define INHERIT_ONLY_ACE 0x08 +#define INHERITED_ACE 0x10 +#define SUCCESSFUL_ACCESS_ACE_FLAG 0x40 +#define FAILED_ACCESS_ACE_FLAG 0x80 + +struct cifs_ace { + __u8 type; /* see above and MS-DTYP 2.4.4.1 */ + __u8 flags; + __le16 size; + __le32 access_req; + struct cifs_sid sid; /* ie UUID of user or group who gets these perms */ +} __attribute__((packed)); + +/* + * The current SMB3 form of security descriptor is similar to what was used for + * cifs (see above) but some fields are split, and fields in the struct below + * matches names of fields to the spec, MS-DTYP (see sections 2.4.5 and + * 2.4.6). Note that "CamelCase" fields are used in this struct in order to + * match the MS-DTYP and MS-SMB2 specs which define the wire format. + */ +struct smb3_sd { + __u8 Revision; /* revision level, MUST be one */ + __u8 Sbz1; /* only meaningful if 'RM' flag set below */ + __le16 Control; + __le32 OffsetOwner; + __le32 OffsetGroup; + __le32 OffsetSacl; + __le32 OffsetDacl; +} __packed; + +/* Meaning of 'Control' field flags */ +#define ACL_CONTROL_SR 0x8000 /* Self relative */ +#define ACL_CONTROL_RM 0x4000 /* Resource manager control bits */ +#define ACL_CONTROL_PS 0x2000 /* SACL protected from inherits */ +#define ACL_CONTROL_PD 0x1000 /* DACL protected from inherits */ +#define ACL_CONTROL_SI 0x0800 /* SACL Auto-Inherited */ +#define ACL_CONTROL_DI 0x0400 /* DACL Auto-Inherited */ +#define ACL_CONTROL_SC 0x0200 /* SACL computed through inheritance */ +#define ACL_CONTROL_DC 0x0100 /* DACL computed through inheritence */ +#define ACL_CONTROL_SS 0x0080 /* Create server ACL */ +#define ACL_CONTROL_DT 0x0040 /* DACL provided by trusted source */ +#define ACL_CONTROL_SD 0x0020 /* SACL defaulted */ +#define ACL_CONTROL_SP 0x0010 /* SACL is present on object */ +#define ACL_CONTROL_DD 0x0008 /* DACL defaulted */ +#define ACL_CONTROL_DP 0x0004 /* DACL is present on object */ +#define ACL_CONTROL_GD 0x0002 /* Group was defaulted */ +#define ACL_CONTROL_OD 0x0001 /* User was defaulted */ + +/* Meaning of AclRevision flags */ +#define ACL_REVISION 0x02 /* See section 2.4.4.1 of MS-DTYP */ +#define ACL_REVISION_DS 0x04 /* Additional AceTypes allowed */ + +struct smb3_acl { + u8 AclRevision; /* revision level */ + u8 Sbz1; /* MBZ */ + __le16 AclSize; + __le16 AceCount; + __le16 Sbz2; /* MBZ */ +} __packed; + +/* + * Used to store the special 'NFS SIDs' used to persist the POSIX uid and gid + * See http://technet.microsoft.com/en-us/library/hh509017(v=ws.10).aspx + */ +struct owner_sid { + u8 Revision; + u8 NumAuth; + u8 Authority[6]; + __le32 SubAuthorities[3]; +} __packed; + +struct owner_group_sids { + struct owner_sid owner; + struct owner_sid group; +} __packed; + +/* + * Minimum security identifier can be one for system defined Users + * and Groups such as NULL SID and World or Built-in accounts such + * as Administrator and Guest and consists of + * Revision + Num (Sub)Auths + Authority + Domain (one Subauthority) + */ +#define MIN_SID_LEN (1 + 1 + 6 + 4) /* in bytes */ + +/* + * Minimum security descriptor can be one without any SACL and DACL and can + * consist of revision, type, and two sids of minimum size for owner and group + */ +#define MIN_SEC_DESC_LEN (sizeof(struct cifs_ntsd) + (2 * MIN_SID_LEN)) + +#endif /* _CIFSACL_H */ diff --git a/fs/smb/client/cifsencrypt.c b/fs/smb/client/cifsencrypt.c new file mode 100644 index 0000000000000..d0ac2648c0d61 --- /dev/null +++ b/fs/smb/client/cifsencrypt.c @@ -0,0 +1,733 @@ +// SPDX-License-Identifier: LGPL-2.1 +/* + * + * Encryption and hashing operations relating to NTLM, NTLMv2. See MS-NLMP + * for more detailed information + * + * Copyright (C) International Business Machines Corp., 2005,2013 + * Author(s): Steve French (sfrench@us.ibm.com) + * + */ + +#include +#include +#include "cifspdu.h" +#include "cifsglob.h" +#include "cifs_debug.h" +#include "cifs_unicode.h" +#include "cifsproto.h" +#include "ntlmssp.h" +#include +#include +#include +#include +#include "../common/arc4.h" +#include + +int __cifs_calc_signature(struct smb_rqst *rqst, + struct TCP_Server_Info *server, char *signature, + struct shash_desc *shash) +{ + int i; + int rc; + struct kvec *iov = rqst->rq_iov; + int n_vec = rqst->rq_nvec; + + /* iov[0] is actual data and not the rfc1002 length for SMB2+ */ + if (!is_smb1(server)) { + if (iov[0].iov_len <= 4) + return -EIO; + i = 0; + } else { + if (n_vec < 2 || iov[0].iov_len != 4) + return -EIO; + i = 1; /* skip rfc1002 length */ + } + + for (; i < n_vec; i++) { + if (iov[i].iov_len == 0) + continue; + if (iov[i].iov_base == NULL) { + cifs_dbg(VFS, "null iovec entry\n"); + return -EIO; + } + + rc = crypto_shash_update(shash, + iov[i].iov_base, iov[i].iov_len); + if (rc) { + cifs_dbg(VFS, "%s: Could not update with payload\n", + __func__); + return rc; + } + } + + /* now hash over the rq_pages array */ + for (i = 0; i < rqst->rq_npages; i++) { + void *kaddr; + unsigned int len, offset; + + rqst_page_get_length(rqst, i, &len, &offset); + + kaddr = (char *) kmap(rqst->rq_pages[i]) + offset; + + rc = crypto_shash_update(shash, kaddr, len); + if (rc) { + cifs_dbg(VFS, "%s: Could not update with payload\n", + __func__); + kunmap(rqst->rq_pages[i]); + return rc; + } + + kunmap(rqst->rq_pages[i]); + } + + rc = crypto_shash_final(shash, signature); + if (rc) + cifs_dbg(VFS, "%s: Could not generate hash\n", __func__); + + return rc; +} + +/* + * Calculate and return the CIFS signature based on the mac key and SMB PDU. + * The 16 byte signature must be allocated by the caller. Note we only use the + * 1st eight bytes and that the smb header signature field on input contains + * the sequence number before this function is called. Also, this function + * should be called with the server->srv_mutex held. + */ +static int cifs_calc_signature(struct smb_rqst *rqst, + struct TCP_Server_Info *server, char *signature) +{ + int rc; + + if (!rqst->rq_iov || !signature || !server) + return -EINVAL; + + rc = cifs_alloc_hash("md5", &server->secmech.md5); + if (rc) + return -1; + + rc = crypto_shash_init(server->secmech.md5); + if (rc) { + cifs_dbg(VFS, "%s: Could not init md5\n", __func__); + return rc; + } + + rc = crypto_shash_update(server->secmech.md5, + server->session_key.response, server->session_key.len); + if (rc) { + cifs_dbg(VFS, "%s: Could not update with response\n", __func__); + return rc; + } + + return __cifs_calc_signature(rqst, server, signature, server->secmech.md5); +} + +/* must be called with server->srv_mutex held */ +int cifs_sign_rqst(struct smb_rqst *rqst, struct TCP_Server_Info *server, + __u32 *pexpected_response_sequence_number) +{ + int rc = 0; + char smb_signature[20]; + struct smb_hdr *cifs_pdu = (struct smb_hdr *)rqst->rq_iov[0].iov_base; + + if (rqst->rq_iov[0].iov_len != 4 || + rqst->rq_iov[0].iov_base + 4 != rqst->rq_iov[1].iov_base) + return -EIO; + + if ((cifs_pdu == NULL) || (server == NULL)) + return -EINVAL; + + spin_lock(&server->srv_lock); + if (!(cifs_pdu->Flags2 & SMBFLG2_SECURITY_SIGNATURE) || + server->tcpStatus == CifsNeedNegotiate) { + spin_unlock(&server->srv_lock); + return rc; + } + spin_unlock(&server->srv_lock); + + if (!server->session_estab) { + memcpy(cifs_pdu->Signature.SecuritySignature, "BSRSPYL", 8); + return rc; + } + + cifs_pdu->Signature.Sequence.SequenceNumber = + cpu_to_le32(server->sequence_number); + cifs_pdu->Signature.Sequence.Reserved = 0; + + *pexpected_response_sequence_number = ++server->sequence_number; + ++server->sequence_number; + + rc = cifs_calc_signature(rqst, server, smb_signature); + if (rc) + memset(cifs_pdu->Signature.SecuritySignature, 0, 8); + else + memcpy(cifs_pdu->Signature.SecuritySignature, smb_signature, 8); + + return rc; +} + +int cifs_sign_smbv(struct kvec *iov, int n_vec, struct TCP_Server_Info *server, + __u32 *pexpected_response_sequence) +{ + struct smb_rqst rqst = { .rq_iov = iov, + .rq_nvec = n_vec }; + + return cifs_sign_rqst(&rqst, server, pexpected_response_sequence); +} + +/* must be called with server->srv_mutex held */ +int cifs_sign_smb(struct smb_hdr *cifs_pdu, struct TCP_Server_Info *server, + __u32 *pexpected_response_sequence_number) +{ + struct kvec iov[2]; + + iov[0].iov_base = cifs_pdu; + iov[0].iov_len = 4; + iov[1].iov_base = (char *)cifs_pdu + 4; + iov[1].iov_len = be32_to_cpu(cifs_pdu->smb_buf_length); + + return cifs_sign_smbv(iov, 2, server, + pexpected_response_sequence_number); +} + +int cifs_verify_signature(struct smb_rqst *rqst, + struct TCP_Server_Info *server, + __u32 expected_sequence_number) +{ + unsigned int rc; + char server_response_sig[8]; + char what_we_think_sig_should_be[20]; + struct smb_hdr *cifs_pdu = (struct smb_hdr *)rqst->rq_iov[0].iov_base; + + if (rqst->rq_iov[0].iov_len != 4 || + rqst->rq_iov[0].iov_base + 4 != rqst->rq_iov[1].iov_base) + return -EIO; + + if (cifs_pdu == NULL || server == NULL) + return -EINVAL; + + if (!server->session_estab) + return 0; + + if (cifs_pdu->Command == SMB_COM_LOCKING_ANDX) { + struct smb_com_lock_req *pSMB = + (struct smb_com_lock_req *)cifs_pdu; + if (pSMB->LockType & LOCKING_ANDX_OPLOCK_RELEASE) + return 0; + } + + /* BB what if signatures are supposed to be on for session but + server does not send one? BB */ + + /* Do not need to verify session setups with signature "BSRSPYL " */ + if (memcmp(cifs_pdu->Signature.SecuritySignature, "BSRSPYL ", 8) == 0) + cifs_dbg(FYI, "dummy signature received for smb command 0x%x\n", + cifs_pdu->Command); + + /* save off the origiginal signature so we can modify the smb and check + its signature against what the server sent */ + memcpy(server_response_sig, cifs_pdu->Signature.SecuritySignature, 8); + + cifs_pdu->Signature.Sequence.SequenceNumber = + cpu_to_le32(expected_sequence_number); + cifs_pdu->Signature.Sequence.Reserved = 0; + + cifs_server_lock(server); + rc = cifs_calc_signature(rqst, server, what_we_think_sig_should_be); + cifs_server_unlock(server); + + if (rc) + return rc; + +/* cifs_dump_mem("what we think it should be: ", + what_we_think_sig_should_be, 16); */ + + if (memcmp(server_response_sig, what_we_think_sig_should_be, 8)) + return -EACCES; + else + return 0; + +} + +/* Build a proper attribute value/target info pairs blob. + * Fill in netbios and dns domain name and workstation name + * and client time (total five av pairs and + one end of fields indicator. + * Allocate domain name which gets freed when session struct is deallocated. + */ +static int +build_avpair_blob(struct cifs_ses *ses, const struct nls_table *nls_cp) +{ + unsigned int dlen; + unsigned int size = 2 * sizeof(struct ntlmssp2_name); + char *defdmname = "WORKGROUP"; + unsigned char *blobptr; + struct ntlmssp2_name *attrptr; + + if (!ses->domainName) { + ses->domainName = kstrdup(defdmname, GFP_KERNEL); + if (!ses->domainName) + return -ENOMEM; + } + + dlen = strlen(ses->domainName); + + /* + * The length of this blob is two times the size of a + * structure (av pair) which holds name/size + * ( for NTLMSSP_AV_NB_DOMAIN_NAME followed by NTLMSSP_AV_EOL ) + + * unicode length of a netbios domain name + */ + kfree_sensitive(ses->auth_key.response); + ses->auth_key.len = size + 2 * dlen; + ses->auth_key.response = kzalloc(ses->auth_key.len, GFP_KERNEL); + if (!ses->auth_key.response) { + ses->auth_key.len = 0; + return -ENOMEM; + } + + blobptr = ses->auth_key.response; + attrptr = (struct ntlmssp2_name *) blobptr; + + /* + * As defined in MS-NTLM 3.3.2, just this av pair field + * is sufficient as part of the temp + */ + attrptr->type = cpu_to_le16(NTLMSSP_AV_NB_DOMAIN_NAME); + attrptr->length = cpu_to_le16(2 * dlen); + blobptr = (unsigned char *)attrptr + sizeof(struct ntlmssp2_name); + cifs_strtoUTF16((__le16 *)blobptr, ses->domainName, dlen, nls_cp); + + return 0; +} + +/* Server has provided av pairs/target info in the type 2 challenge + * packet and we have plucked it and stored within smb session. + * We parse that blob here to find netbios domain name to be used + * as part of ntlmv2 authentication (in Target String), if not already + * specified on the command line. + * If this function returns without any error but without fetching + * domain name, authentication may fail against some server but + * may not fail against other (those who are not very particular + * about target string i.e. for some, just user name might suffice. + */ +static int +find_domain_name(struct cifs_ses *ses, const struct nls_table *nls_cp) +{ + unsigned int attrsize; + unsigned int type; + unsigned int onesize = sizeof(struct ntlmssp2_name); + unsigned char *blobptr; + unsigned char *blobend; + struct ntlmssp2_name *attrptr; + + if (!ses->auth_key.len || !ses->auth_key.response) + return 0; + + blobptr = ses->auth_key.response; + blobend = blobptr + ses->auth_key.len; + + while (blobptr + onesize < blobend) { + attrptr = (struct ntlmssp2_name *) blobptr; + type = le16_to_cpu(attrptr->type); + if (type == NTLMSSP_AV_EOL) + break; + blobptr += 2; /* advance attr type */ + attrsize = le16_to_cpu(attrptr->length); + blobptr += 2; /* advance attr size */ + if (blobptr + attrsize > blobend) + break; + if (type == NTLMSSP_AV_NB_DOMAIN_NAME) { + if (!attrsize || attrsize >= CIFS_MAX_DOMAINNAME_LEN) + break; + if (!ses->domainName) { + ses->domainName = + kmalloc(attrsize + 1, GFP_KERNEL); + if (!ses->domainName) + return -ENOMEM; + cifs_from_utf16(ses->domainName, + (__le16 *)blobptr, attrsize, attrsize, + nls_cp, NO_MAP_UNI_RSVD); + break; + } + } + blobptr += attrsize; /* advance attr value */ + } + + return 0; +} + +/* Server has provided av pairs/target info in the type 2 challenge + * packet and we have plucked it and stored within smb session. + * We parse that blob here to find the server given timestamp + * as part of ntlmv2 authentication (or local current time as + * default in case of failure) + */ +static __le64 +find_timestamp(struct cifs_ses *ses) +{ + unsigned int attrsize; + unsigned int type; + unsigned int onesize = sizeof(struct ntlmssp2_name); + unsigned char *blobptr; + unsigned char *blobend; + struct ntlmssp2_name *attrptr; + struct timespec64 ts; + + if (!ses->auth_key.len || !ses->auth_key.response) + return 0; + + blobptr = ses->auth_key.response; + blobend = blobptr + ses->auth_key.len; + + while (blobptr + onesize < blobend) { + attrptr = (struct ntlmssp2_name *) blobptr; + type = le16_to_cpu(attrptr->type); + if (type == NTLMSSP_AV_EOL) + break; + blobptr += 2; /* advance attr type */ + attrsize = le16_to_cpu(attrptr->length); + blobptr += 2; /* advance attr size */ + if (blobptr + attrsize > blobend) + break; + if (type == NTLMSSP_AV_TIMESTAMP) { + if (attrsize == sizeof(u64)) + return *((__le64 *)blobptr); + } + blobptr += attrsize; /* advance attr value */ + } + + ktime_get_real_ts64(&ts); + return cpu_to_le64(cifs_UnixTimeToNT(ts)); +} + +static int calc_ntlmv2_hash(struct cifs_ses *ses, char *ntlmv2_hash, + const struct nls_table *nls_cp) +{ + int rc = 0; + int len; + char nt_hash[CIFS_NTHASH_SIZE]; + __le16 *user; + wchar_t *domain; + wchar_t *server; + + if (!ses->server->secmech.hmacmd5) { + cifs_dbg(VFS, "%s: can't generate ntlmv2 hash\n", __func__); + return -1; + } + + /* calculate md4 hash of password */ + E_md4hash(ses->password, nt_hash, nls_cp); + + rc = crypto_shash_setkey(ses->server->secmech.hmacmd5->tfm, nt_hash, + CIFS_NTHASH_SIZE); + if (rc) { + cifs_dbg(VFS, "%s: Could not set NT Hash as a key\n", __func__); + return rc; + } + + rc = crypto_shash_init(ses->server->secmech.hmacmd5); + if (rc) { + cifs_dbg(VFS, "%s: Could not init hmacmd5\n", __func__); + return rc; + } + + /* convert ses->user_name to unicode */ + len = ses->user_name ? strlen(ses->user_name) : 0; + user = kmalloc(2 + (len * 2), GFP_KERNEL); + if (user == NULL) { + rc = -ENOMEM; + return rc; + } + + if (len) { + len = cifs_strtoUTF16(user, ses->user_name, len, nls_cp); + UniStrupr(user); + } else { + memset(user, '\0', 2); + } + + rc = crypto_shash_update(ses->server->secmech.hmacmd5, + (char *)user, 2 * len); + kfree(user); + if (rc) { + cifs_dbg(VFS, "%s: Could not update with user\n", __func__); + return rc; + } + + /* convert ses->domainName to unicode and uppercase */ + if (ses->domainName) { + len = strlen(ses->domainName); + + domain = kmalloc(2 + (len * 2), GFP_KERNEL); + if (domain == NULL) { + rc = -ENOMEM; + return rc; + } + len = cifs_strtoUTF16((__le16 *)domain, ses->domainName, len, + nls_cp); + rc = + crypto_shash_update(ses->server->secmech.hmacmd5, + (char *)domain, 2 * len); + kfree(domain); + if (rc) { + cifs_dbg(VFS, "%s: Could not update with domain\n", + __func__); + return rc; + } + } else { + /* We use ses->ip_addr if no domain name available */ + len = strlen(ses->ip_addr); + + server = kmalloc(2 + (len * 2), GFP_KERNEL); + if (server == NULL) { + rc = -ENOMEM; + return rc; + } + len = cifs_strtoUTF16((__le16 *)server, ses->ip_addr, len, + nls_cp); + rc = + crypto_shash_update(ses->server->secmech.hmacmd5, + (char *)server, 2 * len); + kfree(server); + if (rc) { + cifs_dbg(VFS, "%s: Could not update with server\n", + __func__); + return rc; + } + } + + rc = crypto_shash_final(ses->server->secmech.hmacmd5, + ntlmv2_hash); + if (rc) + cifs_dbg(VFS, "%s: Could not generate md5 hash\n", __func__); + + return rc; +} + +static int +CalcNTLMv2_response(const struct cifs_ses *ses, char *ntlmv2_hash) +{ + int rc; + struct ntlmv2_resp *ntlmv2 = (struct ntlmv2_resp *) + (ses->auth_key.response + CIFS_SESS_KEY_SIZE); + unsigned int hash_len; + + /* The MD5 hash starts at challenge_key.key */ + hash_len = ses->auth_key.len - (CIFS_SESS_KEY_SIZE + + offsetof(struct ntlmv2_resp, challenge.key[0])); + + if (!ses->server->secmech.hmacmd5) { + cifs_dbg(VFS, "%s: can't generate ntlmv2 hash\n", __func__); + return -1; + } + + rc = crypto_shash_setkey(ses->server->secmech.hmacmd5->tfm, + ntlmv2_hash, CIFS_HMAC_MD5_HASH_SIZE); + if (rc) { + cifs_dbg(VFS, "%s: Could not set NTLMV2 Hash as a key\n", + __func__); + return rc; + } + + rc = crypto_shash_init(ses->server->secmech.hmacmd5); + if (rc) { + cifs_dbg(VFS, "%s: Could not init hmacmd5\n", __func__); + return rc; + } + + if (ses->server->negflavor == CIFS_NEGFLAVOR_EXTENDED) + memcpy(ntlmv2->challenge.key, + ses->ntlmssp->cryptkey, CIFS_SERVER_CHALLENGE_SIZE); + else + memcpy(ntlmv2->challenge.key, + ses->server->cryptkey, CIFS_SERVER_CHALLENGE_SIZE); + rc = crypto_shash_update(ses->server->secmech.hmacmd5, + ntlmv2->challenge.key, hash_len); + if (rc) { + cifs_dbg(VFS, "%s: Could not update with response\n", __func__); + return rc; + } + + /* Note that the MD5 digest over writes anon.challenge_key.key */ + rc = crypto_shash_final(ses->server->secmech.hmacmd5, + ntlmv2->ntlmv2_hash); + if (rc) + cifs_dbg(VFS, "%s: Could not generate md5 hash\n", __func__); + + return rc; +} + +int +setup_ntlmv2_rsp(struct cifs_ses *ses, const struct nls_table *nls_cp) +{ + int rc; + int baselen; + unsigned int tilen; + struct ntlmv2_resp *ntlmv2; + char ntlmv2_hash[16]; + unsigned char *tiblob = NULL; /* target info blob */ + __le64 rsp_timestamp; + + if (nls_cp == NULL) { + cifs_dbg(VFS, "%s called with nls_cp==NULL\n", __func__); + return -EINVAL; + } + + if (ses->server->negflavor == CIFS_NEGFLAVOR_EXTENDED) { + if (!ses->domainName) { + if (ses->domainAuto) { + rc = find_domain_name(ses, nls_cp); + if (rc) { + cifs_dbg(VFS, "error %d finding domain name\n", + rc); + goto setup_ntlmv2_rsp_ret; + } + } else { + ses->domainName = kstrdup("", GFP_KERNEL); + } + } + } else { + rc = build_avpair_blob(ses, nls_cp); + if (rc) { + cifs_dbg(VFS, "error %d building av pair blob\n", rc); + goto setup_ntlmv2_rsp_ret; + } + } + + /* Must be within 5 minutes of the server (or in range +/-2h + * in case of Mac OS X), so simply carry over server timestamp + * (as Windows 7 does) + */ + rsp_timestamp = find_timestamp(ses); + + baselen = CIFS_SESS_KEY_SIZE + sizeof(struct ntlmv2_resp); + tilen = ses->auth_key.len; + tiblob = ses->auth_key.response; + + ses->auth_key.response = kmalloc(baselen + tilen, GFP_KERNEL); + if (!ses->auth_key.response) { + rc = -ENOMEM; + ses->auth_key.len = 0; + goto setup_ntlmv2_rsp_ret; + } + ses->auth_key.len += baselen; + + ntlmv2 = (struct ntlmv2_resp *) + (ses->auth_key.response + CIFS_SESS_KEY_SIZE); + ntlmv2->blob_signature = cpu_to_le32(0x00000101); + ntlmv2->reserved = 0; + ntlmv2->time = rsp_timestamp; + + get_random_bytes(&ntlmv2->client_chal, sizeof(ntlmv2->client_chal)); + ntlmv2->reserved2 = 0; + + memcpy(ses->auth_key.response + baselen, tiblob, tilen); + + cifs_server_lock(ses->server); + + rc = cifs_alloc_hash("hmac(md5)", &ses->server->secmech.hmacmd5); + if (rc) { + goto unlock; + } + + /* calculate ntlmv2_hash */ + rc = calc_ntlmv2_hash(ses, ntlmv2_hash, nls_cp); + if (rc) { + cifs_dbg(VFS, "Could not get v2 hash rc %d\n", rc); + goto unlock; + } + + /* calculate first part of the client response (CR1) */ + rc = CalcNTLMv2_response(ses, ntlmv2_hash); + if (rc) { + cifs_dbg(VFS, "Could not calculate CR1 rc: %d\n", rc); + goto unlock; + } + + /* now calculate the session key for NTLMv2 */ + rc = crypto_shash_setkey(ses->server->secmech.hmacmd5->tfm, + ntlmv2_hash, CIFS_HMAC_MD5_HASH_SIZE); + if (rc) { + cifs_dbg(VFS, "%s: Could not set NTLMV2 Hash as a key\n", + __func__); + goto unlock; + } + + rc = crypto_shash_init(ses->server->secmech.hmacmd5); + if (rc) { + cifs_dbg(VFS, "%s: Could not init hmacmd5\n", __func__); + goto unlock; + } + + rc = crypto_shash_update(ses->server->secmech.hmacmd5, + ntlmv2->ntlmv2_hash, + CIFS_HMAC_MD5_HASH_SIZE); + if (rc) { + cifs_dbg(VFS, "%s: Could not update with response\n", __func__); + goto unlock; + } + + rc = crypto_shash_final(ses->server->secmech.hmacmd5, + ses->auth_key.response); + if (rc) + cifs_dbg(VFS, "%s: Could not generate md5 hash\n", __func__); + +unlock: + cifs_server_unlock(ses->server); +setup_ntlmv2_rsp_ret: + kfree_sensitive(tiblob); + + return rc; +} + +int +calc_seckey(struct cifs_ses *ses) +{ + unsigned char sec_key[CIFS_SESS_KEY_SIZE]; /* a nonce */ + struct arc4_ctx *ctx_arc4; + + if (fips_enabled) + return -ENODEV; + + get_random_bytes(sec_key, CIFS_SESS_KEY_SIZE); + + ctx_arc4 = kmalloc(sizeof(*ctx_arc4), GFP_KERNEL); + if (!ctx_arc4) { + cifs_dbg(VFS, "Could not allocate arc4 context\n"); + return -ENOMEM; + } + + cifs_arc4_setkey(ctx_arc4, ses->auth_key.response, CIFS_SESS_KEY_SIZE); + cifs_arc4_crypt(ctx_arc4, ses->ntlmssp->ciphertext, sec_key, + CIFS_CPHTXT_SIZE); + + /* make secondary_key/nonce as session key */ + memcpy(ses->auth_key.response, sec_key, CIFS_SESS_KEY_SIZE); + /* and make len as that of session key only */ + ses->auth_key.len = CIFS_SESS_KEY_SIZE; + + memzero_explicit(sec_key, CIFS_SESS_KEY_SIZE); + kfree_sensitive(ctx_arc4); + return 0; +} + +void +cifs_crypto_secmech_release(struct TCP_Server_Info *server) +{ + cifs_free_hash(&server->secmech.aes_cmac); + cifs_free_hash(&server->secmech.hmacsha256); + cifs_free_hash(&server->secmech.md5); + cifs_free_hash(&server->secmech.sha512); + cifs_free_hash(&server->secmech.hmacmd5); + + if (server->secmech.enc) { + crypto_free_aead(server->secmech.enc); + server->secmech.enc = NULL; + } + + if (server->secmech.dec) { + crypto_free_aead(server->secmech.dec); + server->secmech.dec = NULL; + } +} diff --git a/fs/smb/client/cifsfs.c b/fs/smb/client/cifsfs.c new file mode 100644 index 0000000000000..078df1e2dd18a --- /dev/null +++ b/fs/smb/client/cifsfs.c @@ -0,0 +1,1857 @@ +// SPDX-License-Identifier: LGPL-2.1 +/* + * + * Copyright (C) International Business Machines Corp., 2002,2008 + * Author(s): Steve French (sfrench@us.ibm.com) + * + * Common Internet FileSystem (CIFS) client + * + */ + +/* Note that BB means BUGBUG (ie something to fix eventually) */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "cifsfs.h" +#include "cifspdu.h" +#define DECLARE_GLOBALS_HERE +#include "cifsglob.h" +#include "cifsproto.h" +#include "cifs_debug.h" +#include "cifs_fs_sb.h" +#include +#include +#include "cifs_spnego.h" +#include "fscache.h" +#ifdef CONFIG_CIFS_DFS_UPCALL +#include "dfs_cache.h" +#endif +#ifdef CONFIG_CIFS_SWN_UPCALL +#include "netlink.h" +#endif +#include "fs_context.h" +#include "cached_dir.h" + +/* + * DOS dates from 1980/1/1 through 2107/12/31 + * Protocol specifications indicate the range should be to 119, which + * limits maximum year to 2099. But this range has not been checked. + */ +#define SMB_DATE_MAX (127<<9 | 12<<5 | 31) +#define SMB_DATE_MIN (0<<9 | 1<<5 | 1) +#define SMB_TIME_MAX (23<<11 | 59<<5 | 29) + +int cifsFYI = 0; +bool traceSMB; +bool enable_oplocks = true; +bool linuxExtEnabled = true; +bool lookupCacheEnabled = true; +bool disable_legacy_dialects; /* false by default */ +bool enable_gcm_256 = true; +bool require_gcm_256; /* false by default */ +bool enable_negotiate_signing; /* false by default */ +unsigned int global_secflags = CIFSSEC_DEF; +/* unsigned int ntlmv2_support = 0; */ +unsigned int sign_CIFS_PDUs = 1; + +/* + * Global transaction id (XID) information + */ +unsigned int GlobalCurrentXid; /* protected by GlobalMid_Sem */ +unsigned int GlobalTotalActiveXid; /* prot by GlobalMid_Sem */ +unsigned int GlobalMaxActiveXid; /* prot by GlobalMid_Sem */ +spinlock_t GlobalMid_Lock; /* protects above & list operations on midQ entries */ + +/* + * Global counters, updated atomically + */ +atomic_t sesInfoAllocCount; +atomic_t tconInfoAllocCount; +atomic_t tcpSesNextId; +atomic_t tcpSesAllocCount; +atomic_t tcpSesReconnectCount; +atomic_t tconInfoReconnectCount; + +atomic_t mid_count; +atomic_t buf_alloc_count; +atomic_t small_buf_alloc_count; +#ifdef CONFIG_CIFS_STATS2 +atomic_t total_buf_alloc_count; +atomic_t total_small_buf_alloc_count; +#endif/* STATS2 */ +struct list_head cifs_tcp_ses_list; +spinlock_t cifs_tcp_ses_lock; +static const struct super_operations cifs_super_ops; +unsigned int CIFSMaxBufSize = CIFS_MAX_MSGSIZE; +module_param(CIFSMaxBufSize, uint, 0444); +MODULE_PARM_DESC(CIFSMaxBufSize, "Network buffer size (not including header) " + "for CIFS requests. " + "Default: 16384 Range: 8192 to 130048"); +unsigned int cifs_min_rcv = CIFS_MIN_RCV_POOL; +module_param(cifs_min_rcv, uint, 0444); +MODULE_PARM_DESC(cifs_min_rcv, "Network buffers in pool. Default: 4 Range: " + "1 to 64"); +unsigned int cifs_min_small = 30; +module_param(cifs_min_small, uint, 0444); +MODULE_PARM_DESC(cifs_min_small, "Small network buffers in pool. Default: 30 " + "Range: 2 to 256"); +unsigned int cifs_max_pending = CIFS_MAX_REQ; +module_param(cifs_max_pending, uint, 0444); +MODULE_PARM_DESC(cifs_max_pending, "Simultaneous requests to server for " + "CIFS/SMB1 dialect (N/A for SMB3) " + "Default: 32767 Range: 2 to 32767."); +#ifdef CONFIG_CIFS_STATS2 +unsigned int slow_rsp_threshold = 1; +module_param(slow_rsp_threshold, uint, 0644); +MODULE_PARM_DESC(slow_rsp_threshold, "Amount of time (in seconds) to wait " + "before logging that a response is delayed. " + "Default: 1 (if set to 0 disables msg)."); +#endif /* STATS2 */ + +module_param(enable_oplocks, bool, 0644); +MODULE_PARM_DESC(enable_oplocks, "Enable or disable oplocks. Default: y/Y/1"); + +module_param(enable_gcm_256, bool, 0644); +MODULE_PARM_DESC(enable_gcm_256, "Enable requesting strongest (256 bit) GCM encryption. Default: n/N/0"); + +module_param(require_gcm_256, bool, 0644); +MODULE_PARM_DESC(require_gcm_256, "Require strongest (256 bit) GCM encryption. Default: n/N/0"); + +module_param(enable_negotiate_signing, bool, 0644); +MODULE_PARM_DESC(enable_negotiate_signing, "Enable negotiating packet signing algorithm with server. Default: n/N/0"); + +module_param(disable_legacy_dialects, bool, 0644); +MODULE_PARM_DESC(disable_legacy_dialects, "To improve security it may be " + "helpful to restrict the ability to " + "override the default dialects (SMB2.1, " + "SMB3 and SMB3.02) on mount with old " + "dialects (CIFS/SMB1 and SMB2) since " + "vers=1.0 (CIFS/SMB1) and vers=2.0 are weaker" + " and less secure. Default: n/N/0"); + +extern mempool_t *cifs_sm_req_poolp; +extern mempool_t *cifs_req_poolp; +extern mempool_t *cifs_mid_poolp; + +struct workqueue_struct *cifsiod_wq; +struct workqueue_struct *decrypt_wq; +struct workqueue_struct *fileinfo_put_wq; +struct workqueue_struct *cifsoplockd_wq; +struct workqueue_struct *deferredclose_wq; +__u32 cifs_lock_secret; + +/* + * Bumps refcount for cifs super block. + * Note that it should be only called if a referece to VFS super block is + * already held, e.g. in open-type syscalls context. Otherwise it can race with + * atomic_dec_and_test in deactivate_locked_super. + */ +void +cifs_sb_active(struct super_block *sb) +{ + struct cifs_sb_info *server = CIFS_SB(sb); + + if (atomic_inc_return(&server->active) == 1) + atomic_inc(&sb->s_active); +} + +void +cifs_sb_deactive(struct super_block *sb) +{ + struct cifs_sb_info *server = CIFS_SB(sb); + + if (atomic_dec_and_test(&server->active)) + deactivate_super(sb); +} + +static int +cifs_read_super(struct super_block *sb) +{ + struct inode *inode; + struct cifs_sb_info *cifs_sb; + struct cifs_tcon *tcon; + struct timespec64 ts; + int rc = 0; + + cifs_sb = CIFS_SB(sb); + tcon = cifs_sb_master_tcon(cifs_sb); + + if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_POSIXACL) + sb->s_flags |= SB_POSIXACL; + + if (tcon->snapshot_time) + sb->s_flags |= SB_RDONLY; + + if (tcon->ses->capabilities & tcon->ses->server->vals->cap_large_files) + sb->s_maxbytes = MAX_LFS_FILESIZE; + else + sb->s_maxbytes = MAX_NON_LFS; + + /* + * Some very old servers like DOS and OS/2 used 2 second granularity + * (while all current servers use 100ns granularity - see MS-DTYP) + * but 1 second is the maximum allowed granularity for the VFS + * so for old servers set time granularity to 1 second while for + * everything else (current servers) set it to 100ns. + */ + if ((tcon->ses->server->vals->protocol_id == SMB10_PROT_ID) && + ((tcon->ses->capabilities & + tcon->ses->server->vals->cap_nt_find) == 0) && + !tcon->unix_ext) { + sb->s_time_gran = 1000000000; /* 1 second is max allowed gran */ + ts = cnvrtDosUnixTm(cpu_to_le16(SMB_DATE_MIN), 0, 0); + sb->s_time_min = ts.tv_sec; + ts = cnvrtDosUnixTm(cpu_to_le16(SMB_DATE_MAX), + cpu_to_le16(SMB_TIME_MAX), 0); + sb->s_time_max = ts.tv_sec; + } else { + /* + * Almost every server, including all SMB2+, uses DCE TIME + * ie 100 nanosecond units, since 1601. See MS-DTYP and MS-FSCC + */ + sb->s_time_gran = 100; + ts = cifs_NTtimeToUnix(0); + sb->s_time_min = ts.tv_sec; + ts = cifs_NTtimeToUnix(cpu_to_le64(S64_MAX)); + sb->s_time_max = ts.tv_sec; + } + + sb->s_magic = CIFS_SUPER_MAGIC; + sb->s_op = &cifs_super_ops; + sb->s_xattr = cifs_xattr_handlers; + rc = super_setup_bdi(sb); + if (rc) + goto out_no_root; + /* tune readahead according to rsize if readahead size not set on mount */ + if (cifs_sb->ctx->rsize == 0) + cifs_sb->ctx->rsize = + tcon->ses->server->ops->negotiate_rsize(tcon, cifs_sb->ctx); + if (cifs_sb->ctx->rasize) + sb->s_bdi->ra_pages = cifs_sb->ctx->rasize / PAGE_SIZE; + else + sb->s_bdi->ra_pages = cifs_sb->ctx->rsize / PAGE_SIZE; + + sb->s_blocksize = CIFS_MAX_MSGSIZE; + sb->s_blocksize_bits = 14; /* default 2**14 = CIFS_MAX_MSGSIZE */ + inode = cifs_root_iget(sb); + + if (IS_ERR(inode)) { + rc = PTR_ERR(inode); + goto out_no_root; + } + + if (tcon->nocase) + sb->s_d_op = &cifs_ci_dentry_ops; + else + sb->s_d_op = &cifs_dentry_ops; + + sb->s_root = d_make_root(inode); + if (!sb->s_root) { + rc = -ENOMEM; + goto out_no_root; + } + +#ifdef CONFIG_CIFS_NFSD_EXPORT + if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_SERVER_INUM) { + cifs_dbg(FYI, "export ops supported\n"); + sb->s_export_op = &cifs_export_ops; + } +#endif /* CONFIG_CIFS_NFSD_EXPORT */ + + return 0; + +out_no_root: + cifs_dbg(VFS, "%s: get root inode failed\n", __func__); + return rc; +} + +static void cifs_kill_sb(struct super_block *sb) +{ + struct cifs_sb_info *cifs_sb = CIFS_SB(sb); + + /* + * We ned to release all dentries for the cached directories + * before we kill the sb. + */ + if (cifs_sb->root) { + close_all_cached_dirs(cifs_sb); + + /* finally release root dentry */ + dput(cifs_sb->root); + cifs_sb->root = NULL; + } + + kill_anon_super(sb); + cifs_umount(cifs_sb); +} + +static int +cifs_statfs(struct dentry *dentry, struct kstatfs *buf) +{ + struct super_block *sb = dentry->d_sb; + struct cifs_sb_info *cifs_sb = CIFS_SB(sb); + struct cifs_tcon *tcon = cifs_sb_master_tcon(cifs_sb); + struct TCP_Server_Info *server = tcon->ses->server; + unsigned int xid; + int rc = 0; + + xid = get_xid(); + + if (le32_to_cpu(tcon->fsAttrInfo.MaxPathNameComponentLength) > 0) + buf->f_namelen = + le32_to_cpu(tcon->fsAttrInfo.MaxPathNameComponentLength); + else + buf->f_namelen = PATH_MAX; + + buf->f_fsid.val[0] = tcon->vol_serial_number; + /* are using part of create time for more randomness, see man statfs */ + buf->f_fsid.val[1] = (int)le64_to_cpu(tcon->vol_create_time); + + buf->f_files = 0; /* undefined */ + buf->f_ffree = 0; /* unlimited */ + + if (server->ops->queryfs) + rc = server->ops->queryfs(xid, tcon, cifs_sb, buf); + + free_xid(xid); + return rc; +} + +static long cifs_fallocate(struct file *file, int mode, loff_t off, loff_t len) +{ + struct cifs_sb_info *cifs_sb = CIFS_FILE_SB(file); + struct cifs_tcon *tcon = cifs_sb_master_tcon(cifs_sb); + struct TCP_Server_Info *server = tcon->ses->server; + + if (server->ops->fallocate) + return server->ops->fallocate(file, tcon, mode, off, len); + + return -EOPNOTSUPP; +} + +static int cifs_permission(struct user_namespace *mnt_userns, + struct inode *inode, int mask) +{ + struct cifs_sb_info *cifs_sb; + + cifs_sb = CIFS_SB(inode->i_sb); + + if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_NO_PERM) { + if ((mask & MAY_EXEC) && !execute_ok(inode)) + return -EACCES; + else + return 0; + } else /* file mode might have been restricted at mount time + on the client (above and beyond ACL on servers) for + servers which do not support setting and viewing mode bits, + so allowing client to check permissions is useful */ + return generic_permission(&init_user_ns, inode, mask); +} + +static struct kmem_cache *cifs_inode_cachep; +static struct kmem_cache *cifs_req_cachep; +static struct kmem_cache *cifs_mid_cachep; +static struct kmem_cache *cifs_sm_req_cachep; +mempool_t *cifs_sm_req_poolp; +mempool_t *cifs_req_poolp; +mempool_t *cifs_mid_poolp; + +static struct inode * +cifs_alloc_inode(struct super_block *sb) +{ + struct cifsInodeInfo *cifs_inode; + cifs_inode = alloc_inode_sb(sb, cifs_inode_cachep, GFP_KERNEL); + if (!cifs_inode) + return NULL; + cifs_inode->cifsAttrs = 0x20; /* default */ + cifs_inode->time = 0; + /* + * Until the file is open and we have gotten oplock info back from the + * server, can not assume caching of file data or metadata. + */ + cifs_set_oplock_level(cifs_inode, 0); + cifs_inode->flags = 0; + spin_lock_init(&cifs_inode->writers_lock); + cifs_inode->writers = 0; + cifs_inode->netfs.inode.i_blkbits = 14; /* 2**14 = CIFS_MAX_MSGSIZE */ + cifs_inode->server_eof = 0; + cifs_inode->uniqueid = 0; + cifs_inode->createtime = 0; + cifs_inode->epoch = 0; + spin_lock_init(&cifs_inode->open_file_lock); + generate_random_uuid(cifs_inode->lease_key); + cifs_inode->symlink_target = NULL; + + /* + * Can not set i_flags here - they get immediately overwritten to zero + * by the VFS. + */ + /* cifs_inode->netfs.inode.i_flags = S_NOATIME | S_NOCMTIME; */ + INIT_LIST_HEAD(&cifs_inode->openFileList); + INIT_LIST_HEAD(&cifs_inode->llist); + INIT_LIST_HEAD(&cifs_inode->deferred_closes); + spin_lock_init(&cifs_inode->deferred_lock); + return &cifs_inode->netfs.inode; +} + +static void +cifs_free_inode(struct inode *inode) +{ + struct cifsInodeInfo *cinode = CIFS_I(inode); + + if (S_ISLNK(inode->i_mode)) + kfree(cinode->symlink_target); + kmem_cache_free(cifs_inode_cachep, cinode); +} + +static void +cifs_evict_inode(struct inode *inode) +{ + truncate_inode_pages_final(&inode->i_data); + if (inode->i_state & I_PINNING_FSCACHE_WB) + cifs_fscache_unuse_inode_cookie(inode, true); + cifs_fscache_release_inode_cookie(inode); + clear_inode(inode); +} + +static void +cifs_show_address(struct seq_file *s, struct TCP_Server_Info *server) +{ + struct sockaddr_in *sa = (struct sockaddr_in *) &server->dstaddr; + struct sockaddr_in6 *sa6 = (struct sockaddr_in6 *) &server->dstaddr; + + seq_puts(s, ",addr="); + + switch (server->dstaddr.ss_family) { + case AF_INET: + seq_printf(s, "%pI4", &sa->sin_addr.s_addr); + break; + case AF_INET6: + seq_printf(s, "%pI6", &sa6->sin6_addr.s6_addr); + if (sa6->sin6_scope_id) + seq_printf(s, "%%%u", sa6->sin6_scope_id); + break; + default: + seq_puts(s, "(unknown)"); + } + if (server->rdma) + seq_puts(s, ",rdma"); +} + +static void +cifs_show_security(struct seq_file *s, struct cifs_ses *ses) +{ + if (ses->sectype == Unspecified) { + if (ses->user_name == NULL) + seq_puts(s, ",sec=none"); + return; + } + + seq_puts(s, ",sec="); + + switch (ses->sectype) { + case NTLMv2: + seq_puts(s, "ntlmv2"); + break; + case Kerberos: + seq_puts(s, "krb5"); + break; + case RawNTLMSSP: + seq_puts(s, "ntlmssp"); + break; + default: + /* shouldn't ever happen */ + seq_puts(s, "unknown"); + break; + } + + if (ses->sign) + seq_puts(s, "i"); + + if (ses->sectype == Kerberos) + seq_printf(s, ",cruid=%u", + from_kuid_munged(&init_user_ns, ses->cred_uid)); +} + +static void +cifs_show_cache_flavor(struct seq_file *s, struct cifs_sb_info *cifs_sb) +{ + seq_puts(s, ",cache="); + + if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_STRICT_IO) + seq_puts(s, "strict"); + else if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_DIRECT_IO) + seq_puts(s, "none"); + else if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_RW_CACHE) + seq_puts(s, "singleclient"); /* assume only one client access */ + else if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_RO_CACHE) + seq_puts(s, "ro"); /* read only caching assumed */ + else + seq_puts(s, "loose"); +} + +/* + * cifs_show_devname() is used so we show the mount device name with correct + * format (e.g. forward slashes vs. back slashes) in /proc/mounts + */ +static int cifs_show_devname(struct seq_file *m, struct dentry *root) +{ + struct cifs_sb_info *cifs_sb = CIFS_SB(root->d_sb); + char *devname = kstrdup(cifs_sb->ctx->source, GFP_KERNEL); + + if (devname == NULL) + seq_puts(m, "none"); + else { + convert_delimiter(devname, '/'); + /* escape all spaces in share names */ + seq_escape(m, devname, " \t"); + kfree(devname); + } + return 0; +} + +/* + * cifs_show_options() is for displaying mount options in /proc/mounts. + * Not all settable options are displayed but most of the important + * ones are. + */ +static int +cifs_show_options(struct seq_file *s, struct dentry *root) +{ + struct cifs_sb_info *cifs_sb = CIFS_SB(root->d_sb); + struct cifs_tcon *tcon = cifs_sb_master_tcon(cifs_sb); + struct sockaddr *srcaddr; + srcaddr = (struct sockaddr *)&tcon->ses->server->srcaddr; + + seq_show_option(s, "vers", tcon->ses->server->vals->version_string); + cifs_show_security(s, tcon->ses); + cifs_show_cache_flavor(s, cifs_sb); + + if (tcon->no_lease) + seq_puts(s, ",nolease"); + if (cifs_sb->ctx->multiuser) + seq_puts(s, ",multiuser"); + else if (tcon->ses->user_name) + seq_show_option(s, "username", tcon->ses->user_name); + + if (tcon->ses->domainName && tcon->ses->domainName[0] != 0) + seq_show_option(s, "domain", tcon->ses->domainName); + + if (srcaddr->sa_family != AF_UNSPEC) { + struct sockaddr_in *saddr4; + struct sockaddr_in6 *saddr6; + saddr4 = (struct sockaddr_in *)srcaddr; + saddr6 = (struct sockaddr_in6 *)srcaddr; + if (srcaddr->sa_family == AF_INET6) + seq_printf(s, ",srcaddr=%pI6c", + &saddr6->sin6_addr); + else if (srcaddr->sa_family == AF_INET) + seq_printf(s, ",srcaddr=%pI4", + &saddr4->sin_addr.s_addr); + else + seq_printf(s, ",srcaddr=BAD-AF:%i", + (int)(srcaddr->sa_family)); + } + + seq_printf(s, ",uid=%u", + from_kuid_munged(&init_user_ns, cifs_sb->ctx->linux_uid)); + if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_OVERR_UID) + seq_puts(s, ",forceuid"); + else + seq_puts(s, ",noforceuid"); + + seq_printf(s, ",gid=%u", + from_kgid_munged(&init_user_ns, cifs_sb->ctx->linux_gid)); + if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_OVERR_GID) + seq_puts(s, ",forcegid"); + else + seq_puts(s, ",noforcegid"); + + cifs_show_address(s, tcon->ses->server); + + if (!tcon->unix_ext) + seq_printf(s, ",file_mode=0%ho,dir_mode=0%ho", + cifs_sb->ctx->file_mode, + cifs_sb->ctx->dir_mode); + if (cifs_sb->ctx->iocharset) + seq_printf(s, ",iocharset=%s", cifs_sb->ctx->iocharset); + if (tcon->seal) + seq_puts(s, ",seal"); + else if (tcon->ses->server->ignore_signature) + seq_puts(s, ",signloosely"); + if (tcon->nocase) + seq_puts(s, ",nocase"); + if (tcon->nodelete) + seq_puts(s, ",nodelete"); + if (cifs_sb->ctx->no_sparse) + seq_puts(s, ",nosparse"); + if (tcon->local_lease) + seq_puts(s, ",locallease"); + if (tcon->retry) + seq_puts(s, ",hard"); + else + seq_puts(s, ",soft"); + if (tcon->use_persistent) + seq_puts(s, ",persistenthandles"); + else if (tcon->use_resilient) + seq_puts(s, ",resilienthandles"); + if (tcon->posix_extensions) + seq_puts(s, ",posix"); + else if (tcon->unix_ext) + seq_puts(s, ",unix"); + else + seq_puts(s, ",nounix"); + if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_NO_DFS) + seq_puts(s, ",nodfs"); + if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_POSIX_PATHS) + seq_puts(s, ",posixpaths"); + if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_SET_UID) + seq_puts(s, ",setuids"); + if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_UID_FROM_ACL) + seq_puts(s, ",idsfromsid"); + if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_SERVER_INUM) + seq_puts(s, ",serverino"); + if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_RWPIDFORWARD) + seq_puts(s, ",rwpidforward"); + if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_NOPOSIXBRL) + seq_puts(s, ",forcemand"); + if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_NO_XATTR) + seq_puts(s, ",nouser_xattr"); + if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_MAP_SPECIAL_CHR) + seq_puts(s, ",mapchars"); + if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_MAP_SFM_CHR) + seq_puts(s, ",mapposix"); + if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_UNX_EMUL) + seq_puts(s, ",sfu"); + if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_NO_BRL) + seq_puts(s, ",nobrl"); + if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_NO_HANDLE_CACHE) + seq_puts(s, ",nohandlecache"); + if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_MODE_FROM_SID) + seq_puts(s, ",modefromsid"); + if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_CIFS_ACL) + seq_puts(s, ",cifsacl"); + if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_DYNPERM) + seq_puts(s, ",dynperm"); + if (root->d_sb->s_flags & SB_POSIXACL) + seq_puts(s, ",acl"); + if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_MF_SYMLINKS) + seq_puts(s, ",mfsymlinks"); + if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_FSCACHE) + seq_puts(s, ",fsc"); + if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_NOSSYNC) + seq_puts(s, ",nostrictsync"); + if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_NO_PERM) + seq_puts(s, ",noperm"); + if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_CIFS_BACKUPUID) + seq_printf(s, ",backupuid=%u", + from_kuid_munged(&init_user_ns, + cifs_sb->ctx->backupuid)); + if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_CIFS_BACKUPGID) + seq_printf(s, ",backupgid=%u", + from_kgid_munged(&init_user_ns, + cifs_sb->ctx->backupgid)); + + seq_printf(s, ",rsize=%u", cifs_sb->ctx->rsize); + seq_printf(s, ",wsize=%u", cifs_sb->ctx->wsize); + seq_printf(s, ",bsize=%u", cifs_sb->ctx->bsize); + if (cifs_sb->ctx->rasize) + seq_printf(s, ",rasize=%u", cifs_sb->ctx->rasize); + if (tcon->ses->server->min_offload) + seq_printf(s, ",esize=%u", tcon->ses->server->min_offload); + seq_printf(s, ",echo_interval=%lu", + tcon->ses->server->echo_interval / HZ); + + /* Only display the following if overridden on mount */ + if (tcon->ses->server->max_credits != SMB2_MAX_CREDITS_AVAILABLE) + seq_printf(s, ",max_credits=%u", tcon->ses->server->max_credits); + if (tcon->ses->server->tcp_nodelay) + seq_puts(s, ",tcpnodelay"); + if (tcon->ses->server->noautotune) + seq_puts(s, ",noautotune"); + if (tcon->ses->server->noblocksnd) + seq_puts(s, ",noblocksend"); + + if (tcon->snapshot_time) + seq_printf(s, ",snapshot=%llu", tcon->snapshot_time); + if (tcon->handle_timeout) + seq_printf(s, ",handletimeout=%u", tcon->handle_timeout); + + /* + * Display file and directory attribute timeout in seconds. + * If file and directory attribute timeout the same then actimeo + * was likely specified on mount + */ + if (cifs_sb->ctx->acdirmax == cifs_sb->ctx->acregmax) + seq_printf(s, ",actimeo=%lu", cifs_sb->ctx->acregmax / HZ); + else { + seq_printf(s, ",acdirmax=%lu", cifs_sb->ctx->acdirmax / HZ); + seq_printf(s, ",acregmax=%lu", cifs_sb->ctx->acregmax / HZ); + } + seq_printf(s, ",closetimeo=%lu", cifs_sb->ctx->closetimeo / HZ); + + if (tcon->ses->chan_max > 1) + seq_printf(s, ",multichannel,max_channels=%zu", + tcon->ses->chan_max); + + if (tcon->use_witness) + seq_puts(s, ",witness"); + + return 0; +} + +static void cifs_umount_begin(struct super_block *sb) +{ + struct cifs_sb_info *cifs_sb = CIFS_SB(sb); + struct cifs_tcon *tcon; + + if (cifs_sb == NULL) + return; + + tcon = cifs_sb_master_tcon(cifs_sb); + + spin_lock(&cifs_tcp_ses_lock); + spin_lock(&tcon->tc_lock); + if ((tcon->tc_count > 1) || (tcon->status == TID_EXITING)) { + /* we have other mounts to same share or we have + already tried to umount this and woken up + all waiting network requests, nothing to do */ + spin_unlock(&tcon->tc_lock); + spin_unlock(&cifs_tcp_ses_lock); + return; + } + /* + * can not set tcon->status to TID_EXITING yet since we don't know if umount -f will + * fail later (e.g. due to open files). TID_EXITING will be set just before tdis req sent + */ + spin_unlock(&tcon->tc_lock); + spin_unlock(&cifs_tcp_ses_lock); + + cifs_close_all_deferred_files(tcon); + /* cancel_brl_requests(tcon); */ /* BB mark all brl mids as exiting */ + /* cancel_notify_requests(tcon); */ + if (tcon->ses && tcon->ses->server) { + cifs_dbg(FYI, "wake up tasks now - umount begin not complete\n"); + wake_up_all(&tcon->ses->server->request_q); + wake_up_all(&tcon->ses->server->response_q); + msleep(1); /* yield */ + /* we have to kick the requests once more */ + wake_up_all(&tcon->ses->server->response_q); + msleep(1); + } + + return; +} + +static int cifs_freeze(struct super_block *sb) +{ + struct cifs_sb_info *cifs_sb = CIFS_SB(sb); + struct cifs_tcon *tcon; + + if (cifs_sb == NULL) + return 0; + + tcon = cifs_sb_master_tcon(cifs_sb); + + cifs_close_all_deferred_files(tcon); + return 0; +} + +#ifdef CONFIG_CIFS_STATS2 +static int cifs_show_stats(struct seq_file *s, struct dentry *root) +{ + /* BB FIXME */ + return 0; +} +#endif + +static int cifs_write_inode(struct inode *inode, struct writeback_control *wbc) +{ + fscache_unpin_writeback(wbc, cifs_inode_cookie(inode)); + return 0; +} + +static int cifs_drop_inode(struct inode *inode) +{ + struct cifs_sb_info *cifs_sb = CIFS_SB(inode->i_sb); + + /* no serverino => unconditional eviction */ + return !(cifs_sb->mnt_cifs_flags & CIFS_MOUNT_SERVER_INUM) || + generic_drop_inode(inode); +} + +static const struct super_operations cifs_super_ops = { + .statfs = cifs_statfs, + .alloc_inode = cifs_alloc_inode, + .write_inode = cifs_write_inode, + .free_inode = cifs_free_inode, + .drop_inode = cifs_drop_inode, + .evict_inode = cifs_evict_inode, +/* .show_path = cifs_show_path, */ /* Would we ever need show path? */ + .show_devname = cifs_show_devname, +/* .delete_inode = cifs_delete_inode, */ /* Do not need above + function 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, + .freeze_fs = cifs_freeze, +#ifdef CONFIG_CIFS_STATS2 + .show_stats = cifs_show_stats, +#endif +}; + +/* + * Get root dentry from superblock according to prefix path mount option. + * Return dentry with refcount + 1 on success and NULL otherwise. + */ +static struct dentry * +cifs_get_root(struct smb3_fs_context *ctx, struct super_block *sb) +{ + struct dentry *dentry; + struct cifs_sb_info *cifs_sb = CIFS_SB(sb); + char *full_path = NULL; + char *s, *p; + char sep; + + if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_USE_PREFIX_PATH) + return dget(sb->s_root); + + full_path = cifs_build_path_to_root(ctx, cifs_sb, + cifs_sb_master_tcon(cifs_sb), 0); + if (full_path == NULL) + return ERR_PTR(-ENOMEM); + + cifs_dbg(FYI, "Get root dentry for %s\n", full_path); + + sep = CIFS_DIR_SEP(cifs_sb); + dentry = dget(sb->s_root); + s = full_path; + + do { + struct inode *dir = d_inode(dentry); + struct dentry *child; + + if (!S_ISDIR(dir->i_mode)) { + dput(dentry); + dentry = ERR_PTR(-ENOTDIR); + break; + } + + /* skip separators */ + while (*s == sep) + s++; + if (!*s) + break; + p = s++; + /* next separator */ + while (*s && *s != sep) + s++; + + child = lookup_positive_unlocked(p, dentry, s - p); + dput(dentry); + dentry = child; + } while (!IS_ERR(dentry)); + kfree(full_path); + return dentry; +} + +static int cifs_set_super(struct super_block *sb, void *data) +{ + struct cifs_mnt_data *mnt_data = data; + sb->s_fs_info = mnt_data->cifs_sb; + return set_anon_super(sb, NULL); +} + +struct dentry * +cifs_smb3_do_mount(struct file_system_type *fs_type, + int flags, struct smb3_fs_context *old_ctx) +{ + int rc; + struct super_block *sb = NULL; + struct cifs_sb_info *cifs_sb = NULL; + struct cifs_mnt_data mnt_data; + struct dentry *root; + + /* + * Prints in Kernel / CIFS log the attempted mount operation + * If CIFS_DEBUG && cifs_FYI + */ + if (cifsFYI) + cifs_dbg(FYI, "Devname: %s flags: %d\n", old_ctx->UNC, flags); + else + cifs_info("Attempting to mount %s\n", old_ctx->UNC); + + cifs_sb = kzalloc(sizeof(struct cifs_sb_info), GFP_KERNEL); + if (cifs_sb == NULL) { + root = ERR_PTR(-ENOMEM); + goto out; + } + + cifs_sb->ctx = kzalloc(sizeof(struct smb3_fs_context), GFP_KERNEL); + if (!cifs_sb->ctx) { + root = ERR_PTR(-ENOMEM); + goto out; + } + rc = smb3_fs_context_dup(cifs_sb->ctx, old_ctx); + if (rc) { + root = ERR_PTR(rc); + goto out; + } + + rc = cifs_setup_volume_info(cifs_sb->ctx, NULL, NULL); + if (rc) { + root = ERR_PTR(rc); + goto out; + } + + rc = cifs_setup_cifs_sb(cifs_sb); + if (rc) { + root = ERR_PTR(rc); + goto out; + } + + rc = cifs_mount(cifs_sb, cifs_sb->ctx); + if (rc) { + if (!(flags & SB_SILENT)) + cifs_dbg(VFS, "cifs_mount failed w/return code = %d\n", + rc); + root = ERR_PTR(rc); + goto out; + } + + mnt_data.ctx = cifs_sb->ctx; + mnt_data.cifs_sb = cifs_sb; + mnt_data.flags = flags; + + /* BB should we make this contingent on mount parm? */ + flags |= SB_NODIRATIME | SB_NOATIME; + + sb = sget(fs_type, cifs_match_super, cifs_set_super, flags, &mnt_data); + if (IS_ERR(sb)) { + root = ERR_CAST(sb); + cifs_umount(cifs_sb); + cifs_sb = NULL; + goto out; + } + + if (sb->s_root) { + cifs_dbg(FYI, "Use existing superblock\n"); + cifs_umount(cifs_sb); + cifs_sb = NULL; + } else { + rc = cifs_read_super(sb); + if (rc) { + root = ERR_PTR(rc); + goto out_super; + } + + sb->s_flags |= SB_ACTIVE; + } + + root = cifs_get_root(cifs_sb ? cifs_sb->ctx : old_ctx, sb); + if (IS_ERR(root)) + goto out_super; + + if (cifs_sb) + cifs_sb->root = dget(root); + + cifs_dbg(FYI, "dentry root is: %p\n", root); + return root; + +out_super: + deactivate_locked_super(sb); + return root; +out: + if (cifs_sb) { + if (!sb || IS_ERR(sb)) { /* otherwise kill_sb will handle */ + kfree(cifs_sb->prepath); + smb3_cleanup_fs_context(cifs_sb->ctx); + kfree(cifs_sb); + } + } + return root; +} + + +static ssize_t +cifs_loose_read_iter(struct kiocb *iocb, struct iov_iter *iter) +{ + ssize_t rc; + struct inode *inode = file_inode(iocb->ki_filp); + + if (iocb->ki_flags & IOCB_DIRECT) + return cifs_user_readv(iocb, iter); + + rc = cifs_revalidate_mapping(inode); + if (rc) + return rc; + + return generic_file_read_iter(iocb, iter); +} + +static ssize_t cifs_file_write_iter(struct kiocb *iocb, struct iov_iter *from) +{ + struct inode *inode = file_inode(iocb->ki_filp); + struct cifsInodeInfo *cinode = CIFS_I(inode); + ssize_t written; + int rc; + + if (iocb->ki_filp->f_flags & O_DIRECT) { + written = cifs_user_writev(iocb, from); + if (written > 0 && CIFS_CACHE_READ(cinode)) { + cifs_zap_mapping(inode); + cifs_dbg(FYI, + "Set no oplock for inode=%p after a write operation\n", + inode); + cinode->oplock = 0; + } + return written; + } + + written = cifs_get_writer(cinode); + if (written) + return written; + + written = generic_file_write_iter(iocb, from); + + if (CIFS_CACHE_WRITE(CIFS_I(inode))) + goto out; + + rc = filemap_fdatawrite(inode->i_mapping); + if (rc) + cifs_dbg(FYI, "cifs_file_write_iter: %d rc on %p inode\n", + rc, inode); + +out: + cifs_put_writer(cinode); + return written; +} + +static loff_t cifs_llseek(struct file *file, loff_t offset, int whence) +{ + struct cifsFileInfo *cfile = file->private_data; + struct cifs_tcon *tcon; + + /* + * whence == SEEK_END || SEEK_DATA || SEEK_HOLE => we must revalidate + * the cached file length + */ + if (whence != SEEK_SET && whence != SEEK_CUR) { + int rc; + struct inode *inode = file_inode(file); + + /* + * We need to be sure that all dirty pages are written and the + * server has the newest file length. + */ + if (!CIFS_CACHE_READ(CIFS_I(inode)) && inode->i_mapping && + inode->i_mapping->nrpages != 0) { + rc = filemap_fdatawait(inode->i_mapping); + if (rc) { + mapping_set_error(inode->i_mapping, rc); + return rc; + } + } + /* + * Some applications poll for the file length in this strange + * way so we must seek to end on non-oplocked files by + * setting the revalidate time to zero. + */ + CIFS_I(inode)->time = 0; + + rc = cifs_revalidate_file_attr(file); + if (rc < 0) + return (loff_t)rc; + } + if (cfile && cfile->tlink) { + tcon = tlink_tcon(cfile->tlink); + if (tcon->ses->server->ops->llseek) + return tcon->ses->server->ops->llseek(file, tcon, + offset, whence); + } + return generic_file_llseek(file, offset, whence); +} + +static int +cifs_setlease(struct file *file, long arg, struct file_lock **lease, void **priv) +{ + /* + * Note that this is called by vfs setlease with i_lock held to + * protect *lease from going away. + */ + struct inode *inode = file_inode(file); + struct cifsFileInfo *cfile = file->private_data; + + if (!(S_ISREG(inode->i_mode))) + return -EINVAL; + + /* Check if file is oplocked if this is request for new lease */ + if (arg == F_UNLCK || + ((arg == F_RDLCK) && CIFS_CACHE_READ(CIFS_I(inode))) || + ((arg == F_WRLCK) && CIFS_CACHE_WRITE(CIFS_I(inode)))) + return generic_setlease(file, arg, lease, priv); + else if (tlink_tcon(cfile->tlink)->local_lease && + !CIFS_CACHE_READ(CIFS_I(inode))) + /* + * If the server claims to support oplock on this file, then we + * still need to check oplock even if the local_lease mount + * option is set, but there are servers which do not support + * oplock for which this mount option may be useful if the user + * knows that the file won't be changed on the server by anyone + * else. + */ + return generic_setlease(file, arg, lease, priv); + else + return -EAGAIN; +} + +struct file_system_type cifs_fs_type = { + .owner = THIS_MODULE, + .name = "cifs", + .init_fs_context = smb3_init_fs_context, + .parameters = smb3_fs_parameters, + .kill_sb = cifs_kill_sb, + .fs_flags = FS_RENAME_DOES_D_MOVE, +}; +MODULE_ALIAS_FS("cifs"); + +struct file_system_type smb3_fs_type = { + .owner = THIS_MODULE, + .name = "smb3", + .init_fs_context = smb3_init_fs_context, + .parameters = smb3_fs_parameters, + .kill_sb = cifs_kill_sb, + .fs_flags = FS_RENAME_DOES_D_MOVE, +}; +MODULE_ALIAS_FS("smb3"); +MODULE_ALIAS("smb3"); + +const struct inode_operations cifs_dir_inode_ops = { + .create = cifs_create, + .atomic_open = cifs_atomic_open, + .lookup = cifs_lookup, + .getattr = cifs_getattr, + .unlink = cifs_unlink, + .link = cifs_hardlink, + .mkdir = cifs_mkdir, + .rmdir = cifs_rmdir, + .rename = cifs_rename2, + .permission = cifs_permission, + .setattr = cifs_setattr, + .symlink = cifs_symlink, + .mknod = cifs_mknod, + .listxattr = cifs_listxattr, +}; + +const struct inode_operations cifs_file_inode_ops = { + .setattr = cifs_setattr, + .getattr = cifs_getattr, + .permission = cifs_permission, + .listxattr = cifs_listxattr, + .fiemap = cifs_fiemap, +}; + +const char *cifs_get_link(struct dentry *dentry, struct inode *inode, + struct delayed_call *done) +{ + char *target_path; + + target_path = kmalloc(PATH_MAX, GFP_KERNEL); + if (!target_path) + return ERR_PTR(-ENOMEM); + + spin_lock(&inode->i_lock); + if (likely(CIFS_I(inode)->symlink_target)) { + strscpy(target_path, CIFS_I(inode)->symlink_target, PATH_MAX); + } else { + kfree(target_path); + target_path = ERR_PTR(-EOPNOTSUPP); + } + spin_unlock(&inode->i_lock); + + if (!IS_ERR(target_path)) + set_delayed_call(done, kfree_link, target_path); + + return target_path; +} + +const struct inode_operations cifs_symlink_inode_ops = { + .get_link = cifs_get_link, + .permission = cifs_permission, + .listxattr = cifs_listxattr, +}; + +static loff_t cifs_remap_file_range(struct file *src_file, loff_t off, + struct file *dst_file, loff_t destoff, loff_t len, + unsigned int remap_flags) +{ + struct inode *src_inode = file_inode(src_file); + struct inode *target_inode = file_inode(dst_file); + struct cifsFileInfo *smb_file_src = src_file->private_data; + struct cifsFileInfo *smb_file_target; + struct cifs_tcon *target_tcon; + unsigned int xid; + int rc; + + if (remap_flags & ~(REMAP_FILE_DEDUP | REMAP_FILE_ADVISORY)) + return -EINVAL; + + cifs_dbg(FYI, "clone range\n"); + + xid = get_xid(); + + if (!src_file->private_data || !dst_file->private_data) { + rc = -EBADF; + cifs_dbg(VFS, "missing cifsFileInfo on copy range src file\n"); + goto out; + } + + smb_file_target = dst_file->private_data; + target_tcon = tlink_tcon(smb_file_target->tlink); + + /* + * Note: cifs case is easier than btrfs since server responsible for + * checks for proper open modes and file type and if it wants + * server could even support copy of range where source = target + */ + lock_two_nondirectories(target_inode, src_inode); + + if (len == 0) + len = src_inode->i_size - off; + + cifs_dbg(FYI, "about to flush pages\n"); + /* should we flush first and last page first */ + truncate_inode_pages_range(&target_inode->i_data, destoff, + PAGE_ALIGN(destoff + len)-1); + + if (target_tcon->ses->server->ops->duplicate_extents) + rc = target_tcon->ses->server->ops->duplicate_extents(xid, + smb_file_src, smb_file_target, off, len, destoff); + else + rc = -EOPNOTSUPP; + + /* force revalidate of size and timestamps of target file now + that target is updated on the server */ + CIFS_I(target_inode)->time = 0; + /* although unlocking in the reverse order from locking is not + strictly necessary here it is a little cleaner to be consistent */ + unlock_two_nondirectories(src_inode, target_inode); +out: + free_xid(xid); + return rc < 0 ? rc : len; +} + +ssize_t cifs_file_copychunk_range(unsigned int xid, + struct file *src_file, loff_t off, + struct file *dst_file, loff_t destoff, + size_t len, unsigned int flags) +{ + struct inode *src_inode = file_inode(src_file); + struct inode *target_inode = file_inode(dst_file); + struct cifsFileInfo *smb_file_src; + struct cifsFileInfo *smb_file_target; + struct cifs_tcon *src_tcon; + struct cifs_tcon *target_tcon; + ssize_t rc; + + cifs_dbg(FYI, "copychunk range\n"); + + if (!src_file->private_data || !dst_file->private_data) { + rc = -EBADF; + cifs_dbg(VFS, "missing cifsFileInfo on copy range src file\n"); + goto out; + } + + rc = -EXDEV; + smb_file_target = dst_file->private_data; + smb_file_src = src_file->private_data; + src_tcon = tlink_tcon(smb_file_src->tlink); + target_tcon = tlink_tcon(smb_file_target->tlink); + + if (src_tcon->ses != target_tcon->ses) { + cifs_dbg(VFS, "source and target of copy not on same server\n"); + goto out; + } + + rc = -EOPNOTSUPP; + if (!target_tcon->ses->server->ops->copychunk_range) + goto out; + + /* + * Note: cifs case is easier than btrfs since server responsible for + * checks for proper open modes and file type and if it wants + * server could even support copy of range where source = target + */ + lock_two_nondirectories(target_inode, src_inode); + + cifs_dbg(FYI, "about to flush pages\n"); + + rc = filemap_write_and_wait_range(src_inode->i_mapping, off, + off + len - 1); + if (rc) + goto unlock; + + /* should we flush first and last page first */ + truncate_inode_pages(&target_inode->i_data, 0); + + rc = file_modified(dst_file); + if (!rc) + rc = target_tcon->ses->server->ops->copychunk_range(xid, + smb_file_src, smb_file_target, off, len, destoff); + + file_accessed(src_file); + + /* force revalidate of size and timestamps of target file now + * that target is updated on the server + */ + CIFS_I(target_inode)->time = 0; + +unlock: + /* although unlocking in the reverse order from locking is not + * strictly necessary here it is a little cleaner to be consistent + */ + unlock_two_nondirectories(src_inode, target_inode); + +out: + return rc; +} + +/* + * Directory operations under CIFS/SMB2/SMB3 are synchronous, so fsync() + * is a dummy operation. + */ +static int cifs_dir_fsync(struct file *file, loff_t start, loff_t end, int datasync) +{ + cifs_dbg(FYI, "Sync directory - name: %pD datasync: 0x%x\n", + file, datasync); + + return 0; +} + +static ssize_t cifs_copy_file_range(struct file *src_file, loff_t off, + struct file *dst_file, loff_t destoff, + size_t len, unsigned int flags) +{ + unsigned int xid = get_xid(); + ssize_t rc; + struct cifsFileInfo *cfile = dst_file->private_data; + + if (cfile->swapfile) { + rc = -EOPNOTSUPP; + free_xid(xid); + return rc; + } + + rc = cifs_file_copychunk_range(xid, src_file, off, dst_file, destoff, + len, flags); + free_xid(xid); + + if (rc == -EOPNOTSUPP || rc == -EXDEV) + rc = generic_copy_file_range(src_file, off, dst_file, + destoff, len, flags); + return rc; +} + +const struct file_operations cifs_file_ops = { + .read_iter = cifs_loose_read_iter, + .write_iter = cifs_file_write_iter, + .open = cifs_open, + .release = cifs_close, + .lock = cifs_lock, + .flock = cifs_flock, + .fsync = cifs_fsync, + .flush = cifs_flush, + .mmap = cifs_file_mmap, + .splice_read = generic_file_splice_read, + .splice_write = iter_file_splice_write, + .llseek = cifs_llseek, + .unlocked_ioctl = cifs_ioctl, + .copy_file_range = cifs_copy_file_range, + .remap_file_range = cifs_remap_file_range, + .setlease = cifs_setlease, + .fallocate = cifs_fallocate, +}; + +const struct file_operations cifs_file_strict_ops = { + .read_iter = cifs_strict_readv, + .write_iter = cifs_strict_writev, + .open = cifs_open, + .release = cifs_close, + .lock = cifs_lock, + .flock = cifs_flock, + .fsync = cifs_strict_fsync, + .flush = cifs_flush, + .mmap = cifs_file_strict_mmap, + .splice_read = generic_file_splice_read, + .splice_write = iter_file_splice_write, + .llseek = cifs_llseek, + .unlocked_ioctl = cifs_ioctl, + .copy_file_range = cifs_copy_file_range, + .remap_file_range = cifs_remap_file_range, + .setlease = cifs_setlease, + .fallocate = cifs_fallocate, +}; + +const struct file_operations cifs_file_direct_ops = { + .read_iter = cifs_direct_readv, + .write_iter = cifs_direct_writev, + .open = cifs_open, + .release = cifs_close, + .lock = cifs_lock, + .flock = cifs_flock, + .fsync = cifs_fsync, + .flush = cifs_flush, + .mmap = cifs_file_mmap, + .splice_read = generic_file_splice_read, + .splice_write = iter_file_splice_write, + .unlocked_ioctl = cifs_ioctl, + .copy_file_range = cifs_copy_file_range, + .remap_file_range = cifs_remap_file_range, + .llseek = cifs_llseek, + .setlease = cifs_setlease, + .fallocate = cifs_fallocate, +}; + +const struct file_operations cifs_file_nobrl_ops = { + .read_iter = cifs_loose_read_iter, + .write_iter = cifs_file_write_iter, + .open = cifs_open, + .release = cifs_close, + .fsync = cifs_fsync, + .flush = cifs_flush, + .mmap = cifs_file_mmap, + .splice_read = generic_file_splice_read, + .splice_write = iter_file_splice_write, + .llseek = cifs_llseek, + .unlocked_ioctl = cifs_ioctl, + .copy_file_range = cifs_copy_file_range, + .remap_file_range = cifs_remap_file_range, + .setlease = cifs_setlease, + .fallocate = cifs_fallocate, +}; + +const struct file_operations cifs_file_strict_nobrl_ops = { + .read_iter = cifs_strict_readv, + .write_iter = cifs_strict_writev, + .open = cifs_open, + .release = cifs_close, + .fsync = cifs_strict_fsync, + .flush = cifs_flush, + .mmap = cifs_file_strict_mmap, + .splice_read = generic_file_splice_read, + .splice_write = iter_file_splice_write, + .llseek = cifs_llseek, + .unlocked_ioctl = cifs_ioctl, + .copy_file_range = cifs_copy_file_range, + .remap_file_range = cifs_remap_file_range, + .setlease = cifs_setlease, + .fallocate = cifs_fallocate, +}; + +const struct file_operations cifs_file_direct_nobrl_ops = { + .read_iter = cifs_direct_readv, + .write_iter = cifs_direct_writev, + .open = cifs_open, + .release = cifs_close, + .fsync = cifs_fsync, + .flush = cifs_flush, + .mmap = cifs_file_mmap, + .splice_read = generic_file_splice_read, + .splice_write = iter_file_splice_write, + .unlocked_ioctl = cifs_ioctl, + .copy_file_range = cifs_copy_file_range, + .remap_file_range = cifs_remap_file_range, + .llseek = cifs_llseek, + .setlease = cifs_setlease, + .fallocate = cifs_fallocate, +}; + +const struct file_operations cifs_dir_ops = { + .iterate_shared = cifs_readdir, + .release = cifs_closedir, + .read = generic_read_dir, + .unlocked_ioctl = cifs_ioctl, + .copy_file_range = cifs_copy_file_range, + .remap_file_range = cifs_remap_file_range, + .llseek = generic_file_llseek, + .fsync = cifs_dir_fsync, +}; + +static void +cifs_init_once(void *inode) +{ + struct cifsInodeInfo *cifsi = inode; + + inode_init_once(&cifsi->netfs.inode); + init_rwsem(&cifsi->lock_sem); +} + +static int __init +cifs_init_inodecache(void) +{ + cifs_inode_cachep = kmem_cache_create("cifs_inode_cache", + sizeof(struct cifsInodeInfo), + 0, (SLAB_RECLAIM_ACCOUNT| + SLAB_MEM_SPREAD|SLAB_ACCOUNT), + cifs_init_once); + if (cifs_inode_cachep == NULL) + return -ENOMEM; + + return 0; +} + +static void +cifs_destroy_inodecache(void) +{ + /* + * Make sure all delayed rcu free inodes are flushed before we + * destroy cache. + */ + rcu_barrier(); + kmem_cache_destroy(cifs_inode_cachep); +} + +static int +cifs_init_request_bufs(void) +{ + /* + * SMB2 maximum header size is bigger than CIFS one - no problems to + * allocate some more bytes for CIFS. + */ + size_t max_hdr_size = MAX_SMB2_HDR_SIZE; + + if (CIFSMaxBufSize < 8192) { + /* Buffer size can not be smaller than 2 * PATH_MAX since maximum + Unicode path name has to fit in any SMB/CIFS path based frames */ + CIFSMaxBufSize = 8192; + } else if (CIFSMaxBufSize > 1024*127) { + CIFSMaxBufSize = 1024 * 127; + } else { + CIFSMaxBufSize &= 0x1FE00; /* Round size to even 512 byte mult*/ + } +/* + cifs_dbg(VFS, "CIFSMaxBufSize %d 0x%x\n", + CIFSMaxBufSize, CIFSMaxBufSize); +*/ + cifs_req_cachep = kmem_cache_create_usercopy("cifs_request", + CIFSMaxBufSize + max_hdr_size, 0, + SLAB_HWCACHE_ALIGN, 0, + CIFSMaxBufSize + max_hdr_size, + NULL); + if (cifs_req_cachep == NULL) + return -ENOMEM; + + if (cifs_min_rcv < 1) + cifs_min_rcv = 1; + else if (cifs_min_rcv > 64) { + cifs_min_rcv = 64; + cifs_dbg(VFS, "cifs_min_rcv set to maximum (64)\n"); + } + + cifs_req_poolp = mempool_create_slab_pool(cifs_min_rcv, + cifs_req_cachep); + + if (cifs_req_poolp == NULL) { + kmem_cache_destroy(cifs_req_cachep); + return -ENOMEM; + } + /* MAX_CIFS_SMALL_BUFFER_SIZE bytes is enough for most SMB responses and + almost all handle based requests (but not write response, nor is it + sufficient for path based requests). A smaller size would have + been more efficient (compacting multiple slab items on one 4k page) + for the case in which debug was on, but this larger size allows + more SMBs to use small buffer alloc and is still much more + efficient to alloc 1 per page off the slab compared to 17K (5page) + alloc of large cifs buffers even when page debugging is on */ + cifs_sm_req_cachep = kmem_cache_create_usercopy("cifs_small_rq", + MAX_CIFS_SMALL_BUFFER_SIZE, 0, SLAB_HWCACHE_ALIGN, + 0, MAX_CIFS_SMALL_BUFFER_SIZE, NULL); + if (cifs_sm_req_cachep == NULL) { + mempool_destroy(cifs_req_poolp); + kmem_cache_destroy(cifs_req_cachep); + return -ENOMEM; + } + + if (cifs_min_small < 2) + cifs_min_small = 2; + else if (cifs_min_small > 256) { + cifs_min_small = 256; + cifs_dbg(FYI, "cifs_min_small set to maximum (256)\n"); + } + + cifs_sm_req_poolp = mempool_create_slab_pool(cifs_min_small, + cifs_sm_req_cachep); + + if (cifs_sm_req_poolp == NULL) { + mempool_destroy(cifs_req_poolp); + kmem_cache_destroy(cifs_req_cachep); + kmem_cache_destroy(cifs_sm_req_cachep); + return -ENOMEM; + } + + return 0; +} + +static void +cifs_destroy_request_bufs(void) +{ + mempool_destroy(cifs_req_poolp); + kmem_cache_destroy(cifs_req_cachep); + mempool_destroy(cifs_sm_req_poolp); + kmem_cache_destroy(cifs_sm_req_cachep); +} + +static int init_mids(void) +{ + cifs_mid_cachep = kmem_cache_create("cifs_mpx_ids", + sizeof(struct mid_q_entry), 0, + SLAB_HWCACHE_ALIGN, NULL); + if (cifs_mid_cachep == NULL) + return -ENOMEM; + + /* 3 is a reasonable minimum number of simultaneous operations */ + cifs_mid_poolp = mempool_create_slab_pool(3, cifs_mid_cachep); + if (cifs_mid_poolp == NULL) { + kmem_cache_destroy(cifs_mid_cachep); + return -ENOMEM; + } + + return 0; +} + +static void destroy_mids(void) +{ + mempool_destroy(cifs_mid_poolp); + kmem_cache_destroy(cifs_mid_cachep); +} + +static int __init +init_cifs(void) +{ + int rc = 0; + cifs_proc_init(); + INIT_LIST_HEAD(&cifs_tcp_ses_list); +/* + * Initialize Global counters + */ + atomic_set(&sesInfoAllocCount, 0); + atomic_set(&tconInfoAllocCount, 0); + atomic_set(&tcpSesNextId, 0); + atomic_set(&tcpSesAllocCount, 0); + atomic_set(&tcpSesReconnectCount, 0); + atomic_set(&tconInfoReconnectCount, 0); + + atomic_set(&buf_alloc_count, 0); + atomic_set(&small_buf_alloc_count, 0); +#ifdef CONFIG_CIFS_STATS2 + atomic_set(&total_buf_alloc_count, 0); + atomic_set(&total_small_buf_alloc_count, 0); + if (slow_rsp_threshold < 1) + cifs_dbg(FYI, "slow_response_threshold msgs disabled\n"); + else if (slow_rsp_threshold > 32767) + cifs_dbg(VFS, + "slow response threshold set higher than recommended (0 to 32767)\n"); +#endif /* CONFIG_CIFS_STATS2 */ + + atomic_set(&mid_count, 0); + GlobalCurrentXid = 0; + GlobalTotalActiveXid = 0; + GlobalMaxActiveXid = 0; + spin_lock_init(&cifs_tcp_ses_lock); + spin_lock_init(&GlobalMid_Lock); + + cifs_lock_secret = get_random_u32(); + + if (cifs_max_pending < 2) { + cifs_max_pending = 2; + cifs_dbg(FYI, "cifs_max_pending set to min of 2\n"); + } else if (cifs_max_pending > CIFS_MAX_REQ) { + cifs_max_pending = CIFS_MAX_REQ; + cifs_dbg(FYI, "cifs_max_pending set to max of %u\n", + CIFS_MAX_REQ); + } + + cifsiod_wq = alloc_workqueue("cifsiod", WQ_FREEZABLE|WQ_MEM_RECLAIM, 0); + if (!cifsiod_wq) { + rc = -ENOMEM; + goto out_clean_proc; + } + + /* + * Consider in future setting limit!=0 maybe to min(num_of_cores - 1, 3) + * so that we don't launch too many worker threads but + * Documentation/core-api/workqueue.rst recommends setting it to 0 + */ + + /* WQ_UNBOUND allows decrypt tasks to run on any CPU */ + decrypt_wq = alloc_workqueue("smb3decryptd", + WQ_UNBOUND|WQ_FREEZABLE|WQ_MEM_RECLAIM, 0); + if (!decrypt_wq) { + rc = -ENOMEM; + goto out_destroy_cifsiod_wq; + } + + fileinfo_put_wq = alloc_workqueue("cifsfileinfoput", + WQ_UNBOUND|WQ_FREEZABLE|WQ_MEM_RECLAIM, 0); + if (!fileinfo_put_wq) { + rc = -ENOMEM; + goto out_destroy_decrypt_wq; + } + + cifsoplockd_wq = alloc_workqueue("cifsoplockd", + WQ_FREEZABLE|WQ_MEM_RECLAIM, 0); + if (!cifsoplockd_wq) { + rc = -ENOMEM; + goto out_destroy_fileinfo_put_wq; + } + + deferredclose_wq = alloc_workqueue("deferredclose", + WQ_FREEZABLE|WQ_MEM_RECLAIM, 0); + if (!deferredclose_wq) { + rc = -ENOMEM; + goto out_destroy_cifsoplockd_wq; + } + + rc = cifs_init_inodecache(); + if (rc) + goto out_destroy_deferredclose_wq; + + rc = init_mids(); + if (rc) + goto out_destroy_inodecache; + + rc = cifs_init_request_bufs(); + if (rc) + goto out_destroy_mids; + +#ifdef CONFIG_CIFS_DFS_UPCALL + rc = dfs_cache_init(); + if (rc) + goto out_destroy_request_bufs; +#endif /* CONFIG_CIFS_DFS_UPCALL */ +#ifdef CONFIG_CIFS_UPCALL + rc = init_cifs_spnego(); + if (rc) + goto out_destroy_dfs_cache; +#endif /* CONFIG_CIFS_UPCALL */ +#ifdef CONFIG_CIFS_SWN_UPCALL + rc = cifs_genl_init(); + if (rc) + goto out_register_key_type; +#endif /* CONFIG_CIFS_SWN_UPCALL */ + + rc = init_cifs_idmap(); + if (rc) + goto out_cifs_swn_init; + + rc = register_filesystem(&cifs_fs_type); + if (rc) + goto out_init_cifs_idmap; + + rc = register_filesystem(&smb3_fs_type); + if (rc) { + unregister_filesystem(&cifs_fs_type); + goto out_init_cifs_idmap; + } + + return 0; + +out_init_cifs_idmap: + exit_cifs_idmap(); +out_cifs_swn_init: +#ifdef CONFIG_CIFS_SWN_UPCALL + cifs_genl_exit(); +out_register_key_type: +#endif +#ifdef CONFIG_CIFS_UPCALL + exit_cifs_spnego(); +out_destroy_dfs_cache: +#endif +#ifdef CONFIG_CIFS_DFS_UPCALL + dfs_cache_destroy(); +out_destroy_request_bufs: +#endif + cifs_destroy_request_bufs(); +out_destroy_mids: + destroy_mids(); +out_destroy_inodecache: + cifs_destroy_inodecache(); +out_destroy_deferredclose_wq: + destroy_workqueue(deferredclose_wq); +out_destroy_cifsoplockd_wq: + destroy_workqueue(cifsoplockd_wq); +out_destroy_fileinfo_put_wq: + destroy_workqueue(fileinfo_put_wq); +out_destroy_decrypt_wq: + destroy_workqueue(decrypt_wq); +out_destroy_cifsiod_wq: + destroy_workqueue(cifsiod_wq); +out_clean_proc: + cifs_proc_clean(); + return rc; +} + +static void __exit +exit_cifs(void) +{ + cifs_dbg(NOISY, "exit_smb3\n"); + unregister_filesystem(&cifs_fs_type); + unregister_filesystem(&smb3_fs_type); + cifs_dfs_release_automount_timer(); + exit_cifs_idmap(); +#ifdef CONFIG_CIFS_SWN_UPCALL + cifs_genl_exit(); +#endif +#ifdef CONFIG_CIFS_UPCALL + exit_cifs_spnego(); +#endif +#ifdef CONFIG_CIFS_DFS_UPCALL + dfs_cache_destroy(); +#endif + cifs_destroy_request_bufs(); + destroy_mids(); + cifs_destroy_inodecache(); + destroy_workqueue(deferredclose_wq); + destroy_workqueue(cifsoplockd_wq); + destroy_workqueue(decrypt_wq); + destroy_workqueue(fileinfo_put_wq); + destroy_workqueue(cifsiod_wq); + cifs_proc_clean(); +} + +MODULE_AUTHOR("Steve French"); +MODULE_LICENSE("GPL"); /* combination of LGPL + GPL source behaves as GPL */ +MODULE_DESCRIPTION + ("VFS to access SMB3 servers e.g. Samba, Macs, Azure and Windows (and " + "also older servers complying with the SNIA CIFS Specification)"); +MODULE_VERSION(CIFS_VERSION); +MODULE_SOFTDEP("ecb"); +MODULE_SOFTDEP("hmac"); +MODULE_SOFTDEP("md5"); +MODULE_SOFTDEP("nls"); +MODULE_SOFTDEP("aes"); +MODULE_SOFTDEP("cmac"); +MODULE_SOFTDEP("sha256"); +MODULE_SOFTDEP("sha512"); +MODULE_SOFTDEP("aead2"); +MODULE_SOFTDEP("ccm"); +MODULE_SOFTDEP("gcm"); +module_init(init_cifs) +module_exit(exit_cifs) diff --git a/fs/smb/client/cifsfs.h b/fs/smb/client/cifsfs.h new file mode 100644 index 0000000000000..b6c38896fb2db --- /dev/null +++ b/fs/smb/client/cifsfs.h @@ -0,0 +1,161 @@ +/* SPDX-License-Identifier: LGPL-2.1 */ +/* + * + * Copyright (c) International Business Machines Corp., 2002, 2007 + * Author(s): Steve French (sfrench@us.ibm.com) + * + */ + +#ifndef _CIFSFS_H +#define _CIFSFS_H + +#include + +#define ROOT_I 2 + +/* + * ino_t is 32-bits on 32-bit arch. We have to squash the 64-bit value down + * so that it will fit. We use hash_64 to convert the value to 31 bits, and + * then add 1, to ensure that we don't end up with a 0 as the value. + */ +static inline ino_t +cifs_uniqueid_to_ino_t(u64 fileid) +{ + if ((sizeof(ino_t)) < (sizeof(u64))) + return (ino_t)hash_64(fileid, (sizeof(ino_t) * 8) - 1) + 1; + + return (ino_t)fileid; + +} + +static inline void cifs_set_time(struct dentry *dentry, unsigned long time) +{ + dentry->d_fsdata = (void *) time; +} + +static inline unsigned long cifs_get_time(struct dentry *dentry) +{ + return (unsigned long) dentry->d_fsdata; +} + +extern struct file_system_type cifs_fs_type, smb3_fs_type; +extern const struct address_space_operations cifs_addr_ops; +extern const struct address_space_operations cifs_addr_ops_smallbuf; + +/* Functions related to super block operations */ +extern void cifs_sb_active(struct super_block *sb); +extern void cifs_sb_deactive(struct super_block *sb); + +/* Functions related to inodes */ +extern const struct inode_operations cifs_dir_inode_ops; +extern struct inode *cifs_root_iget(struct super_block *); +extern int cifs_create(struct user_namespace *, struct inode *, + struct dentry *, umode_t, bool excl); +extern int cifs_atomic_open(struct inode *, struct dentry *, + struct file *, unsigned, umode_t); +extern struct dentry *cifs_lookup(struct inode *, struct dentry *, + unsigned int); +extern int cifs_unlink(struct inode *dir, struct dentry *dentry); +extern int cifs_hardlink(struct dentry *, struct inode *, struct dentry *); +extern int cifs_mknod(struct user_namespace *, struct inode *, struct dentry *, + umode_t, dev_t); +extern int cifs_mkdir(struct user_namespace *, struct inode *, struct dentry *, + umode_t); +extern int cifs_rmdir(struct inode *, struct dentry *); +extern int cifs_rename2(struct user_namespace *, struct inode *, + struct dentry *, struct inode *, struct dentry *, + unsigned int); +extern int cifs_revalidate_file_attr(struct file *filp); +extern int cifs_revalidate_dentry_attr(struct dentry *); +extern int cifs_revalidate_file(struct file *filp); +extern int cifs_revalidate_dentry(struct dentry *); +extern int cifs_invalidate_mapping(struct inode *inode); +extern int cifs_revalidate_mapping(struct inode *inode); +extern int cifs_zap_mapping(struct inode *inode); +extern int cifs_getattr(struct user_namespace *, const struct path *, + struct kstat *, u32, unsigned int); +extern int cifs_setattr(struct user_namespace *, struct dentry *, + struct iattr *); +extern int cifs_fiemap(struct inode *, struct fiemap_extent_info *, u64 start, + u64 len); + +extern const struct inode_operations cifs_file_inode_ops; +extern const struct inode_operations cifs_symlink_inode_ops; +extern const struct inode_operations cifs_dfs_referral_inode_operations; + + +/* Functions related to files and directories */ +extern const struct file_operations cifs_file_ops; +extern const struct file_operations cifs_file_direct_ops; /* if directio mnt */ +extern const struct file_operations cifs_file_strict_ops; /* if strictio mnt */ +extern const struct file_operations cifs_file_nobrl_ops; /* no brlocks */ +extern const struct file_operations cifs_file_direct_nobrl_ops; +extern const struct file_operations cifs_file_strict_nobrl_ops; +extern int cifs_open(struct inode *inode, struct file *file); +extern int cifs_close(struct inode *inode, struct file *file); +extern int cifs_closedir(struct inode *inode, struct file *file); +extern ssize_t cifs_user_readv(struct kiocb *iocb, struct iov_iter *to); +extern ssize_t cifs_direct_readv(struct kiocb *iocb, struct iov_iter *to); +extern ssize_t cifs_strict_readv(struct kiocb *iocb, struct iov_iter *to); +extern ssize_t cifs_user_writev(struct kiocb *iocb, struct iov_iter *from); +extern ssize_t cifs_direct_writev(struct kiocb *iocb, struct iov_iter *from); +extern ssize_t cifs_strict_writev(struct kiocb *iocb, struct iov_iter *from); +extern int cifs_flock(struct file *pfile, int cmd, struct file_lock *plock); +extern int cifs_lock(struct file *, int, struct file_lock *); +extern int cifs_fsync(struct file *, loff_t, loff_t, int); +extern int cifs_strict_fsync(struct file *, loff_t, loff_t, int); +extern int cifs_flush(struct file *, fl_owner_t id); +extern int cifs_file_mmap(struct file * , struct vm_area_struct *); +extern int cifs_file_strict_mmap(struct file * , struct vm_area_struct *); +extern const struct file_operations cifs_dir_ops; +extern int cifs_dir_open(struct inode *inode, struct file *file); +extern int cifs_readdir(struct file *file, struct dir_context *ctx); + +/* Functions related to dir entries */ +extern const struct dentry_operations cifs_dentry_ops; +extern const struct dentry_operations cifs_ci_dentry_ops; + +#ifdef CONFIG_CIFS_DFS_UPCALL +extern struct vfsmount *cifs_dfs_d_automount(struct path *path); +#else +static inline struct vfsmount *cifs_dfs_d_automount(struct path *path) +{ + return ERR_PTR(-EREMOTE); +} +#endif + +/* Functions related to symlinks */ +extern const char *cifs_get_link(struct dentry *, struct inode *, + struct delayed_call *); +extern int cifs_symlink(struct user_namespace *mnt_userns, struct inode *inode, + struct dentry *direntry, const char *symname); + +#ifdef CONFIG_CIFS_XATTR +extern const struct xattr_handler *cifs_xattr_handlers[]; +extern ssize_t cifs_listxattr(struct dentry *, char *, size_t); +#else +# define cifs_xattr_handlers NULL +# define cifs_listxattr NULL +#endif + +extern ssize_t cifs_file_copychunk_range(unsigned int xid, + struct file *src_file, loff_t off, + struct file *dst_file, loff_t destoff, + size_t len, unsigned int flags); + +extern long cifs_ioctl(struct file *filep, unsigned int cmd, unsigned long arg); +extern void cifs_setsize(struct inode *inode, loff_t offset); +extern int cifs_truncate_page(struct address_space *mapping, loff_t from); + +struct smb3_fs_context; +extern struct dentry *cifs_smb3_do_mount(struct file_system_type *fs_type, + int flags, struct smb3_fs_context *ctx); + +#ifdef CONFIG_CIFS_NFSD_EXPORT +extern const struct export_operations cifs_export_ops; +#endif /* CONFIG_CIFS_NFSD_EXPORT */ + +/* when changing internal version - update following two lines at same time */ +#define SMB3_PRODUCT_BUILD 40 +#define CIFS_VERSION "2.40" +#endif /* _CIFSFS_H */ diff --git a/fs/smb/client/cifsglob.h b/fs/smb/client/cifsglob.h new file mode 100644 index 0000000000000..a37afbb7e399f --- /dev/null +++ b/fs/smb/client/cifsglob.h @@ -0,0 +1,2208 @@ +/* SPDX-License-Identifier: LGPL-2.1 */ +/* + * + * Copyright (C) International Business Machines Corp., 2002,2008 + * Author(s): Steve French (sfrench@us.ibm.com) + * Jeremy Allison (jra@samba.org) + * + */ +#ifndef _CIFS_GLOB_H +#define _CIFS_GLOB_H + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "cifs_fs_sb.h" +#include "cifsacl.h" +#include +#include +#include +#include "../common/smb2pdu.h" +#include "smb2pdu.h" + +#define SMB_PATH_MAX 260 +#define CIFS_PORT 445 +#define RFC1001_PORT 139 + +/* + * The sizes of various internal tables and strings + */ +#define MAX_UID_INFO 16 +#define MAX_SES_INFO 2 +#define MAX_TCON_INFO 4 + +#define MAX_TREE_SIZE (2 + CIFS_NI_MAXHOST + 1 + CIFS_MAX_SHARE_LEN + 1) + +#define CIFS_MIN_RCV_POOL 4 + +#define MAX_REOPEN_ATT 5 /* these many maximum attempts to reopen a file */ +/* + * default attribute cache timeout (jiffies) + */ +#define CIFS_DEF_ACTIMEO (1 * HZ) + +/* + * max attribute cache timeout (jiffies) - 2^30 + */ +#define CIFS_MAX_ACTIMEO (1 << 30) + +/* + * Max persistent and resilient handle timeout (milliseconds). + * Windows durable max was 960000 (16 minutes) + */ +#define SMB3_MAX_HANDLE_TIMEOUT 960000 + +/* + * MAX_REQ is the maximum number of requests that WE will send + * on one socket concurrently. + */ +#define CIFS_MAX_REQ 32767 + +#define RFC1001_NAME_LEN 15 +#define RFC1001_NAME_LEN_WITH_NULL (RFC1001_NAME_LEN + 1) + +/* maximum length of ip addr as a string (including ipv6 and sctp) */ +#define SERVER_NAME_LENGTH 80 +#define SERVER_NAME_LEN_WITH_NULL (SERVER_NAME_LENGTH + 1) + +/* echo interval in seconds */ +#define SMB_ECHO_INTERVAL_MIN 1 +#define SMB_ECHO_INTERVAL_MAX 600 +#define SMB_ECHO_INTERVAL_DEFAULT 60 + +/* dns resolution intervals in seconds */ +#define SMB_DNS_RESOLVE_INTERVAL_MIN 120 +#define SMB_DNS_RESOLVE_INTERVAL_DEFAULT 600 + +/* smb multichannel query server interfaces interval in seconds */ +#define SMB_INTERFACE_POLL_INTERVAL 600 + +/* maximum number of PDUs in one compound */ +#define MAX_COMPOUND 5 + +/* + * Default number of credits to keep available for SMB3. + * This value is chosen somewhat arbitrarily. The Windows client + * defaults to 128 credits, the Windows server allows clients up to + * 512 credits (or 8K for later versions), and the NetApp server + * does not limit clients at all. Choose a high enough default value + * such that the client shouldn't limit performance, but allow mount + * to override (until you approach 64K, where we limit credits to 65000 + * to reduce possibility of seeing more server credit overflow bugs. + */ +#define SMB2_MAX_CREDITS_AVAILABLE 32000 + +#include "cifspdu.h" + +#ifndef XATTR_DOS_ATTRIB +#define XATTR_DOS_ATTRIB "user.DOSATTRIB" +#endif + +#define CIFS_MAX_WORKSTATION_LEN (__NEW_UTS_LEN + 1) /* reasonable max for client */ + +/* + * CIFS vfs client Status information (based on what we know.) + */ + +/* associated with each connection */ +enum statusEnum { + CifsNew = 0, + CifsGood, + CifsExiting, + CifsNeedReconnect, + CifsNeedNegotiate, + CifsInNegotiate, +}; + +/* associated with each smb session */ +enum ses_status_enum { + SES_NEW = 0, + SES_GOOD, + SES_EXITING, + SES_NEED_RECON, + SES_IN_SETUP +}; + +/* associated with each tree connection to the server */ +enum tid_status_enum { + TID_NEW = 0, + TID_GOOD, + TID_EXITING, + TID_NEED_RECON, + TID_NEED_TCON, + TID_IN_TCON, + TID_NEED_FILES_INVALIDATE, /* currently unused */ + TID_IN_FILES_INVALIDATE +}; + +enum securityEnum { + Unspecified = 0, /* not specified */ + NTLMv2, /* Legacy NTLM auth with NTLMv2 hash */ + RawNTLMSSP, /* NTLMSSP without SPNEGO, NTLMv2 hash */ + Kerberos, /* Kerberos via SPNEGO */ +}; + +struct session_key { + unsigned int len; + char *response; +}; + +/* crypto hashing related structure/fields, not specific to a sec mech */ +struct cifs_secmech { + struct shash_desc *hmacmd5; /* hmacmd5 hash function, for NTLMv2/CR1 hashes */ + struct shash_desc *md5; /* md5 hash function, for CIFS/SMB1 signatures */ + struct shash_desc *hmacsha256; /* hmac-sha256 hash function, for SMB2 signatures */ + struct shash_desc *sha512; /* sha512 hash function, for SMB3.1.1 preauth hash */ + struct shash_desc *aes_cmac; /* block-cipher based MAC function, for SMB3 signatures */ + + struct crypto_aead *enc; /* smb3 encryption AEAD TFM (AES-CCM and AES-GCM) */ + struct crypto_aead *dec; /* smb3 decryption AEAD TFM (AES-CCM and AES-GCM) */ +}; + +/* per smb session structure/fields */ +struct ntlmssp_auth { + bool sesskey_per_smbsess; /* whether session key is per smb session */ + __u32 client_flags; /* sent by client in type 1 ntlmsssp exchange */ + __u32 server_flags; /* sent by server in type 2 ntlmssp exchange */ + unsigned char ciphertext[CIFS_CPHTXT_SIZE]; /* sent to server */ + char cryptkey[CIFS_CRYPTO_KEY_SIZE]; /* used by ntlmssp */ +}; + +struct cifs_cred { + int uid; + int gid; + int mode; + int cecount; + struct cifs_sid osid; + struct cifs_sid gsid; + struct cifs_ntace *ntaces; + struct cifs_ace *aces; +}; + +struct cifs_open_info_data { + char *symlink_target; + union { + struct smb2_file_all_info fi; + struct smb311_posix_qinfo posix_fi; + }; +}; + +static inline void cifs_free_open_info(struct cifs_open_info_data *data) +{ + kfree(data->symlink_target); +} + +/* + ***************************************************************** + * Except the CIFS PDUs themselves all the + * globally interesting structs should go here + ***************************************************************** + */ + +/* + * A smb_rqst represents a complete request to be issued to a server. It's + * formed by a kvec array, followed by an array of pages. Page data is assumed + * to start at the beginning of the first page. + */ +struct smb_rqst { + struct kvec *rq_iov; /* array of kvecs */ + unsigned int rq_nvec; /* number of kvecs in array */ + struct page **rq_pages; /* pointer to array of page ptrs */ + unsigned int rq_offset; /* the offset to the 1st page */ + unsigned int rq_npages; /* number pages in array */ + unsigned int rq_pagesz; /* page size to use */ + unsigned int rq_tailsz; /* length of last page */ +}; + +struct mid_q_entry; +struct TCP_Server_Info; +struct cifsFileInfo; +struct cifs_ses; +struct cifs_tcon; +struct dfs_info3_param; +struct cifs_fattr; +struct smb3_fs_context; +struct cifs_fid; +struct cifs_readdata; +struct cifs_writedata; +struct cifs_io_parms; +struct cifs_search_info; +struct cifsInodeInfo; +struct cifs_open_parms; +struct cifs_credits; + +struct smb_version_operations { + int (*send_cancel)(struct TCP_Server_Info *, struct smb_rqst *, + struct mid_q_entry *); + bool (*compare_fids)(struct cifsFileInfo *, struct cifsFileInfo *); + /* setup request: allocate mid, sign message */ + struct mid_q_entry *(*setup_request)(struct cifs_ses *, + struct TCP_Server_Info *, + struct smb_rqst *); + /* setup async request: allocate mid, sign message */ + struct mid_q_entry *(*setup_async_request)(struct TCP_Server_Info *, + struct smb_rqst *); + /* check response: verify signature, map error */ + int (*check_receive)(struct mid_q_entry *, struct TCP_Server_Info *, + bool); + void (*add_credits)(struct TCP_Server_Info *server, + const struct cifs_credits *credits, + const int optype); + void (*set_credits)(struct TCP_Server_Info *, const int); + int * (*get_credits_field)(struct TCP_Server_Info *, const int); + unsigned int (*get_credits)(struct mid_q_entry *); + __u64 (*get_next_mid)(struct TCP_Server_Info *); + void (*revert_current_mid)(struct TCP_Server_Info *server, + const unsigned int val); + /* data offset from read response message */ + unsigned int (*read_data_offset)(char *); + /* + * Data length from read response message + * When in_remaining is true, the returned data length is in + * message field DataRemaining for out-of-band data read (e.g through + * Memory Registration RDMA write in SMBD). + * Otherwise, the returned data length is in message field DataLength. + */ + unsigned int (*read_data_length)(char *, bool in_remaining); + /* map smb to linux error */ + int (*map_error)(char *, bool); + /* find mid corresponding to the response message */ + struct mid_q_entry * (*find_mid)(struct TCP_Server_Info *, char *); + void (*dump_detail)(void *buf, struct TCP_Server_Info *ptcp_info); + void (*clear_stats)(struct cifs_tcon *); + void (*print_stats)(struct seq_file *m, struct cifs_tcon *); + void (*dump_share_caps)(struct seq_file *, struct cifs_tcon *); + /* verify the message */ + int (*check_message)(char *, unsigned int, struct TCP_Server_Info *); + bool (*is_oplock_break)(char *, struct TCP_Server_Info *); + int (*handle_cancelled_mid)(struct mid_q_entry *, struct TCP_Server_Info *); + void (*downgrade_oplock)(struct TCP_Server_Info *server, + struct cifsInodeInfo *cinode, __u32 oplock, + unsigned int epoch, bool *purge_cache); + /* process transaction2 response */ + bool (*check_trans2)(struct mid_q_entry *, struct TCP_Server_Info *, + char *, int); + /* check if we need to negotiate */ + bool (*need_neg)(struct TCP_Server_Info *); + /* negotiate to the server */ + int (*negotiate)(const unsigned int xid, + struct cifs_ses *ses, + struct TCP_Server_Info *server); + /* set negotiated write size */ + unsigned int (*negotiate_wsize)(struct cifs_tcon *tcon, struct smb3_fs_context *ctx); + /* set negotiated read size */ + unsigned int (*negotiate_rsize)(struct cifs_tcon *tcon, struct smb3_fs_context *ctx); + /* setup smb sessionn */ + int (*sess_setup)(const unsigned int, struct cifs_ses *, + struct TCP_Server_Info *server, + const struct nls_table *); + /* close smb session */ + int (*logoff)(const unsigned int, struct cifs_ses *); + /* connect to a server share */ + int (*tree_connect)(const unsigned int, struct cifs_ses *, const char *, + struct cifs_tcon *, const struct nls_table *); + /* close tree connecion */ + int (*tree_disconnect)(const unsigned int, struct cifs_tcon *); + /* get DFS referrals */ + int (*get_dfs_refer)(const unsigned int, struct cifs_ses *, + const char *, struct dfs_info3_param **, + unsigned int *, const struct nls_table *, int); + /* informational QFS call */ + void (*qfs_tcon)(const unsigned int, struct cifs_tcon *, + struct cifs_sb_info *); + /* check if a path is accessible or not */ + int (*is_path_accessible)(const unsigned int, struct cifs_tcon *, + struct cifs_sb_info *, const char *); + /* query path data from the server */ + int (*query_path_info)(const unsigned int xid, struct cifs_tcon *tcon, + struct cifs_sb_info *cifs_sb, const char *full_path, + struct cifs_open_info_data *data, bool *adjust_tz, bool *reparse); + /* query file data from the server */ + int (*query_file_info)(const unsigned int xid, struct cifs_tcon *tcon, + struct cifsFileInfo *cfile, struct cifs_open_info_data *data); + /* query reparse tag from srv to determine which type of special file */ + int (*query_reparse_tag)(const unsigned int xid, struct cifs_tcon *tcon, + struct cifs_sb_info *cifs_sb, const char *path, + __u32 *reparse_tag); + /* get server index number */ + int (*get_srv_inum)(const unsigned int xid, struct cifs_tcon *tcon, + struct cifs_sb_info *cifs_sb, const char *full_path, u64 *uniqueid, + struct cifs_open_info_data *data); + /* set size by path */ + int (*set_path_size)(const unsigned int, struct cifs_tcon *, + const char *, __u64, struct cifs_sb_info *, bool); + /* set size by file handle */ + int (*set_file_size)(const unsigned int, struct cifs_tcon *, + struct cifsFileInfo *, __u64, bool); + /* set attributes */ + int (*set_file_info)(struct inode *, const char *, FILE_BASIC_INFO *, + const unsigned int); + int (*set_compression)(const unsigned int, struct cifs_tcon *, + struct cifsFileInfo *); + /* check if we can send an echo or nor */ + bool (*can_echo)(struct TCP_Server_Info *); + /* send echo request */ + int (*echo)(struct TCP_Server_Info *); + /* create directory */ + int (*posix_mkdir)(const unsigned int xid, struct inode *inode, + umode_t mode, struct cifs_tcon *tcon, + const char *full_path, + struct cifs_sb_info *cifs_sb); + int (*mkdir)(const unsigned int xid, struct inode *inode, umode_t mode, + struct cifs_tcon *tcon, const char *name, + struct cifs_sb_info *sb); + /* set info on created directory */ + void (*mkdir_setinfo)(struct inode *, const char *, + struct cifs_sb_info *, struct cifs_tcon *, + const unsigned int); + /* remove directory */ + int (*rmdir)(const unsigned int, struct cifs_tcon *, const char *, + struct cifs_sb_info *); + /* unlink file */ + int (*unlink)(const unsigned int, struct cifs_tcon *, const char *, + struct cifs_sb_info *); + /* open, rename and delete file */ + int (*rename_pending_delete)(const char *, struct dentry *, + const unsigned int); + /* send rename request */ + int (*rename)(const unsigned int, struct cifs_tcon *, const char *, + const char *, struct cifs_sb_info *); + /* send create hardlink request */ + int (*create_hardlink)(const unsigned int, struct cifs_tcon *, + const char *, const char *, + struct cifs_sb_info *); + /* query symlink target */ + int (*query_symlink)(const unsigned int, struct cifs_tcon *, + struct cifs_sb_info *, const char *, + char **, bool); + /* open a file for non-posix mounts */ + int (*open)(const unsigned int xid, struct cifs_open_parms *oparms, __u32 *oplock, + void *buf); + /* set fid protocol-specific info */ + void (*set_fid)(struct cifsFileInfo *, struct cifs_fid *, __u32); + /* close a file */ + void (*close)(const unsigned int, struct cifs_tcon *, + struct cifs_fid *); + /* close a file, returning file attributes and timestamps */ + void (*close_getattr)(const unsigned int xid, struct cifs_tcon *tcon, + struct cifsFileInfo *pfile_info); + /* send a flush request to the server */ + int (*flush)(const unsigned int, struct cifs_tcon *, struct cifs_fid *); + /* async read from the server */ + int (*async_readv)(struct cifs_readdata *); + /* async write to the server */ + int (*async_writev)(struct cifs_writedata *, + void (*release)(struct kref *)); + /* sync read from the server */ + int (*sync_read)(const unsigned int, struct cifs_fid *, + struct cifs_io_parms *, unsigned int *, char **, + int *); + /* sync write to the server */ + int (*sync_write)(const unsigned int, struct cifs_fid *, + struct cifs_io_parms *, unsigned int *, struct kvec *, + unsigned long); + /* open dir, start readdir */ + int (*query_dir_first)(const unsigned int, struct cifs_tcon *, + const char *, struct cifs_sb_info *, + struct cifs_fid *, __u16, + struct cifs_search_info *); + /* continue readdir */ + int (*query_dir_next)(const unsigned int, struct cifs_tcon *, + struct cifs_fid *, + __u16, struct cifs_search_info *srch_inf); + /* close dir */ + int (*close_dir)(const unsigned int, struct cifs_tcon *, + struct cifs_fid *); + /* calculate a size of SMB message */ + unsigned int (*calc_smb_size)(void *buf); + /* check for STATUS_PENDING and process the response if yes */ + bool (*is_status_pending)(char *buf, struct TCP_Server_Info *server); + /* check for STATUS_NETWORK_SESSION_EXPIRED */ + bool (*is_session_expired)(char *); + /* send oplock break response */ + int (*oplock_response)(struct cifs_tcon *tcon, __u64 persistent_fid, __u64 volatile_fid, + __u16 net_fid, struct cifsInodeInfo *cifs_inode); + /* query remote filesystem */ + int (*queryfs)(const unsigned int, struct cifs_tcon *, + struct cifs_sb_info *, struct kstatfs *); + /* send mandatory brlock to the server */ + int (*mand_lock)(const unsigned int, struct cifsFileInfo *, __u64, + __u64, __u32, int, int, bool); + /* unlock range of mandatory locks */ + int (*mand_unlock_range)(struct cifsFileInfo *, struct file_lock *, + const unsigned int); + /* push brlocks from the cache to the server */ + int (*push_mand_locks)(struct cifsFileInfo *); + /* get lease key of the inode */ + void (*get_lease_key)(struct inode *, struct cifs_fid *); + /* set lease key of the inode */ + void (*set_lease_key)(struct inode *, struct cifs_fid *); + /* generate new lease key */ + void (*new_lease_key)(struct cifs_fid *); + int (*generate_signingkey)(struct cifs_ses *ses, + struct TCP_Server_Info *server); + int (*calc_signature)(struct smb_rqst *, struct TCP_Server_Info *, + bool allocate_crypto); + int (*set_integrity)(const unsigned int, struct cifs_tcon *tcon, + struct cifsFileInfo *src_file); + int (*enum_snapshots)(const unsigned int xid, struct cifs_tcon *tcon, + struct cifsFileInfo *src_file, void __user *); + int (*notify)(const unsigned int xid, struct file *pfile, + void __user *pbuf, bool return_changes); + int (*query_mf_symlink)(unsigned int, struct cifs_tcon *, + struct cifs_sb_info *, const unsigned char *, + char *, unsigned int *); + int (*create_mf_symlink)(unsigned int, struct cifs_tcon *, + struct cifs_sb_info *, const unsigned char *, + char *, unsigned int *); + /* if we can do cache read operations */ + bool (*is_read_op)(__u32); + /* set oplock level for the inode */ + void (*set_oplock_level)(struct cifsInodeInfo *, __u32, unsigned int, + bool *); + /* create lease context buffer for CREATE request */ + char * (*create_lease_buf)(u8 *lease_key, u8 oplock); + /* parse lease context buffer and return oplock/epoch info */ + __u8 (*parse_lease_buf)(void *buf, unsigned int *epoch, char *lkey); + ssize_t (*copychunk_range)(const unsigned int, + struct cifsFileInfo *src_file, + struct cifsFileInfo *target_file, + u64 src_off, u64 len, u64 dest_off); + int (*duplicate_extents)(const unsigned int, struct cifsFileInfo *src, + struct cifsFileInfo *target_file, u64 src_off, u64 len, + u64 dest_off); + int (*validate_negotiate)(const unsigned int, struct cifs_tcon *); + ssize_t (*query_all_EAs)(const unsigned int, struct cifs_tcon *, + const unsigned char *, const unsigned char *, char *, + size_t, struct cifs_sb_info *); + int (*set_EA)(const unsigned int, struct cifs_tcon *, const char *, + const char *, const void *, const __u16, + const struct nls_table *, struct cifs_sb_info *); + struct cifs_ntsd * (*get_acl)(struct cifs_sb_info *, struct inode *, + const char *, u32 *, u32); + struct cifs_ntsd * (*get_acl_by_fid)(struct cifs_sb_info *, + const struct cifs_fid *, u32 *, u32); + int (*set_acl)(struct cifs_ntsd *, __u32, struct inode *, const char *, + int); + /* writepages retry size */ + unsigned int (*wp_retry_size)(struct inode *); + /* get mtu credits */ + int (*wait_mtu_credits)(struct TCP_Server_Info *, unsigned int, + unsigned int *, struct cifs_credits *); + /* adjust previously taken mtu credits to request size */ + int (*adjust_credits)(struct TCP_Server_Info *server, + struct cifs_credits *credits, + const unsigned int payload_size); + /* check if we need to issue closedir */ + bool (*dir_needs_close)(struct cifsFileInfo *); + long (*fallocate)(struct file *, struct cifs_tcon *, int, loff_t, + loff_t); + /* init transform request - used for encryption for now */ + int (*init_transform_rq)(struct TCP_Server_Info *, int num_rqst, + struct smb_rqst *, struct smb_rqst *); + int (*is_transform_hdr)(void *buf); + int (*receive_transform)(struct TCP_Server_Info *, + struct mid_q_entry **, char **, int *); + enum securityEnum (*select_sectype)(struct TCP_Server_Info *, + enum securityEnum); + int (*next_header)(char *); + /* ioctl passthrough for query_info */ + int (*ioctl_query_info)(const unsigned int xid, + struct cifs_tcon *tcon, + struct cifs_sb_info *cifs_sb, + __le16 *path, int is_dir, + unsigned long p); + /* make unix special files (block, char, fifo, socket) */ + int (*make_node)(unsigned int xid, + struct inode *inode, + struct dentry *dentry, + struct cifs_tcon *tcon, + const char *full_path, + umode_t mode, + dev_t device_number); + /* version specific fiemap implementation */ + int (*fiemap)(struct cifs_tcon *tcon, struct cifsFileInfo *, + struct fiemap_extent_info *, u64, u64); + /* version specific llseek implementation */ + loff_t (*llseek)(struct file *, struct cifs_tcon *, loff_t, int); + /* Check for STATUS_IO_TIMEOUT */ + bool (*is_status_io_timeout)(char *buf); + /* Check for STATUS_NETWORK_NAME_DELETED */ + void (*is_network_name_deleted)(char *buf, struct TCP_Server_Info *srv); +}; + +struct smb_version_values { + char *version_string; + __u16 protocol_id; + __u32 req_capabilities; + __u32 large_lock_type; + __u32 exclusive_lock_type; + __u32 shared_lock_type; + __u32 unlock_lock_type; + size_t header_preamble_size; + size_t header_size; + size_t max_header_size; + size_t read_rsp_size; + __le16 lock_cmd; + unsigned int cap_unix; + unsigned int cap_nt_find; + unsigned int cap_large_files; + __u16 signing_enabled; + __u16 signing_required; + size_t create_lease_size; +}; + +#define HEADER_SIZE(server) (server->vals->header_size) +#define MAX_HEADER_SIZE(server) (server->vals->max_header_size) +#define HEADER_PREAMBLE_SIZE(server) (server->vals->header_preamble_size) +#define MID_HEADER_SIZE(server) (HEADER_SIZE(server) - 1 - HEADER_PREAMBLE_SIZE(server)) + +/** + * CIFS superblock mount flags (mnt_cifs_flags) to consider when + * trying to reuse existing superblock for a new mount + */ +#define CIFS_MOUNT_MASK (CIFS_MOUNT_NO_PERM | CIFS_MOUNT_SET_UID | \ + CIFS_MOUNT_SERVER_INUM | CIFS_MOUNT_DIRECT_IO | \ + CIFS_MOUNT_NO_XATTR | CIFS_MOUNT_MAP_SPECIAL_CHR | \ + CIFS_MOUNT_MAP_SFM_CHR | \ + CIFS_MOUNT_UNX_EMUL | CIFS_MOUNT_NO_BRL | \ + CIFS_MOUNT_CIFS_ACL | CIFS_MOUNT_OVERR_UID | \ + CIFS_MOUNT_OVERR_GID | CIFS_MOUNT_DYNPERM | \ + CIFS_MOUNT_NOPOSIXBRL | CIFS_MOUNT_NOSSYNC | \ + CIFS_MOUNT_FSCACHE | CIFS_MOUNT_MF_SYMLINKS | \ + CIFS_MOUNT_MULTIUSER | CIFS_MOUNT_STRICT_IO | \ + CIFS_MOUNT_CIFS_BACKUPUID | CIFS_MOUNT_CIFS_BACKUPGID | \ + CIFS_MOUNT_UID_FROM_ACL | CIFS_MOUNT_NO_HANDLE_CACHE | \ + CIFS_MOUNT_NO_DFS | CIFS_MOUNT_MODE_FROM_SID | \ + CIFS_MOUNT_RO_CACHE | CIFS_MOUNT_RW_CACHE) + +/** + * Generic VFS superblock mount flags (s_flags) to consider when + * trying to reuse existing superblock for a new mount + */ +#define CIFS_MS_MASK (SB_RDONLY | SB_MANDLOCK | SB_NOEXEC | SB_NOSUID | \ + SB_NODEV | SB_SYNCHRONOUS) + +struct cifs_mnt_data { + struct cifs_sb_info *cifs_sb; + struct smb3_fs_context *ctx; + int flags; +}; + +static inline unsigned int +get_rfc1002_length(void *buf) +{ + return be32_to_cpu(*((__be32 *)buf)) & 0xffffff; +} + +static inline void +inc_rfc1001_len(void *buf, int count) +{ + be32_add_cpu((__be32 *)buf, count); +} + +struct TCP_Server_Info { + struct list_head tcp_ses_list; + struct list_head smb_ses_list; + spinlock_t srv_lock; /* protect anything here that is not protected */ + __u64 conn_id; /* connection identifier (useful for debugging) */ + int srv_count; /* reference counter */ + /* 15 character server name + 0x20 16th byte indicating type = srv */ + char server_RFC1001_name[RFC1001_NAME_LEN_WITH_NULL]; + struct smb_version_operations *ops; + struct smb_version_values *vals; + /* updates to tcpStatus protected by cifs_tcp_ses_lock */ + enum statusEnum tcpStatus; /* what we think the status is */ + char *hostname; /* hostname portion of UNC string */ + struct socket *ssocket; + struct sockaddr_storage dstaddr; + struct sockaddr_storage srcaddr; /* locally bind to this IP */ +#ifdef CONFIG_NET_NS + struct net *net; +#endif + wait_queue_head_t response_q; + wait_queue_head_t request_q; /* if more than maxmpx to srvr must block*/ + spinlock_t mid_lock; /* protect mid queue and it's entries */ + struct list_head pending_mid_q; + bool noblocksnd; /* use blocking sendmsg */ + bool noautotune; /* do not autotune send buf sizes */ + bool nosharesock; + bool tcp_nodelay; + unsigned int credits; /* send no more requests at once */ + unsigned int max_credits; /* can override large 32000 default at mnt */ + unsigned int in_flight; /* number of requests on the wire to server */ + unsigned int max_in_flight; /* max number of requests that were on wire */ + spinlock_t req_lock; /* protect the two values above */ + struct mutex _srv_mutex; + unsigned int nofs_flag; + struct task_struct *tsk; + char server_GUID[16]; + __u16 sec_mode; + bool sign; /* is signing enabled on this connection? */ + bool ignore_signature:1; /* skip validation of signatures in SMB2/3 rsp */ + bool session_estab; /* mark when very first sess is established */ + int echo_credits; /* echo reserved slots */ + int oplock_credits; /* oplock break reserved slots */ + bool echoes:1; /* enable echoes */ + __u8 client_guid[SMB2_CLIENT_GUID_SIZE]; /* Client GUID */ + u16 dialect; /* dialect index that server chose */ + bool oplocks:1; /* enable oplocks */ + unsigned int maxReq; /* Clients should submit no more */ + /* than maxReq distinct unanswered SMBs to the server when using */ + /* multiplexed reads or writes (for SMB1/CIFS only, not SMB2/SMB3) */ + unsigned int maxBuf; /* maxBuf specifies the maximum */ + /* message size the server can send or receive for non-raw SMBs */ + /* maxBuf is returned by SMB NegotiateProtocol so maxBuf is only 0 */ + /* when socket is setup (and during reconnect) before NegProt sent */ + unsigned int max_rw; /* maxRw specifies the maximum */ + /* message size the server can send or receive for */ + /* SMB_COM_WRITE_RAW or SMB_COM_READ_RAW. */ + unsigned int capabilities; /* selective disabling of caps by smb sess */ + int timeAdj; /* Adjust for difference in server time zone in sec */ + __u64 CurrentMid; /* multiplex id - rotating counter, protected by GlobalMid_Lock */ + char cryptkey[CIFS_CRYPTO_KEY_SIZE]; /* used by ntlm, ntlmv2 etc */ + /* 16th byte of RFC1001 workstation name is always null */ + char workstation_RFC1001_name[RFC1001_NAME_LEN_WITH_NULL]; + __u32 sequence_number; /* for signing, protected by srv_mutex */ + __u32 reconnect_instance; /* incremented on each reconnect */ + struct session_key session_key; + unsigned long lstrp; /* when we got last response from this server */ + struct cifs_secmech secmech; /* crypto sec mech functs, descriptors */ +#define CIFS_NEGFLAVOR_UNENCAP 1 /* wct == 17, but no ext_sec */ +#define CIFS_NEGFLAVOR_EXTENDED 2 /* wct == 17, ext_sec bit set */ + char negflavor; /* NEGOTIATE response flavor */ + /* extended security flavors that server supports */ + bool sec_ntlmssp; /* supports NTLMSSP */ + bool sec_kerberosu2u; /* supports U2U Kerberos */ + bool sec_kerberos; /* supports plain Kerberos */ + bool sec_mskerberos; /* supports legacy MS Kerberos */ + bool large_buf; /* is current buffer large? */ + /* use SMBD connection instead of socket */ + bool rdma; + /* point to the SMBD connection if RDMA is used instead of socket */ + struct smbd_connection *smbd_conn; + struct delayed_work echo; /* echo ping workqueue job */ + struct delayed_work resolve; /* dns resolution workqueue job */ + char *smallbuf; /* pointer to current "small" buffer */ + char *bigbuf; /* pointer to current "big" buffer */ + /* Total size of this PDU. Only valid from cifs_demultiplex_thread */ + unsigned int pdu_size; + unsigned int total_read; /* total amount of data read in this pass */ + atomic_t in_send; /* requests trying to send */ + atomic_t num_waiters; /* blocked waiting to get in sendrecv */ +#ifdef CONFIG_CIFS_STATS2 + atomic_t num_cmds[NUMBER_OF_SMB2_COMMANDS]; /* total requests by cmd */ + atomic_t smb2slowcmd[NUMBER_OF_SMB2_COMMANDS]; /* count resps > 1 sec */ + __u64 time_per_cmd[NUMBER_OF_SMB2_COMMANDS]; /* total time per cmd */ + __u32 slowest_cmd[NUMBER_OF_SMB2_COMMANDS]; + __u32 fastest_cmd[NUMBER_OF_SMB2_COMMANDS]; +#endif /* STATS2 */ + unsigned int max_read; + unsigned int max_write; + unsigned int min_offload; + __le16 compress_algorithm; + __u16 signing_algorithm; + __le16 cipher_type; + /* save initital negprot hash */ + __u8 preauth_sha_hash[SMB2_PREAUTH_HASH_SIZE]; + bool signing_negotiated; /* true if valid signing context rcvd from server */ + bool posix_ext_supported; + struct delayed_work reconnect; /* reconnect workqueue job */ + struct mutex reconnect_mutex; /* prevent simultaneous reconnects */ + unsigned long echo_interval; + + /* + * Number of targets available for reconnect. The more targets + * the more tasks have to wait to let the demultiplex thread + * reconnect. + */ + int nr_targets; + bool noblockcnt; /* use non-blocking connect() */ + + /* + * If this is a session channel, + * primary_server holds the ref-counted + * pointer to primary channel connection for the session. + */ +#define CIFS_SERVER_IS_CHAN(server) (!!(server)->primary_server) + struct TCP_Server_Info *primary_server; + +#ifdef CONFIG_CIFS_SWN_UPCALL + bool use_swn_dstaddr; + struct sockaddr_storage swn_dstaddr; +#endif +#ifdef CONFIG_CIFS_DFS_UPCALL + bool is_dfs_conn; /* if a dfs connection */ + struct mutex refpath_lock; /* protects leaf_fullpath */ + /* + * Canonical DFS full paths that were used to chase referrals in mount and reconnect. + * + * origin_fullpath: first or original referral path + * leaf_fullpath: last referral path (might be changed due to nested links in reconnect) + * + * current_fullpath: pointer to either origin_fullpath or leaf_fullpath + * NOTE: cannot be accessed outside cifs_reconnect() and smb2_reconnect() + * + * format: \\HOST\SHARE\[OPTIONAL PATH] + */ + char *origin_fullpath, *leaf_fullpath, *current_fullpath; +#endif +}; + +static inline bool is_smb1(struct TCP_Server_Info *server) +{ + return HEADER_PREAMBLE_SIZE(server) != 0; +} + +static inline void cifs_server_lock(struct TCP_Server_Info *server) +{ + unsigned int nofs_flag = memalloc_nofs_save(); + + mutex_lock(&server->_srv_mutex); + server->nofs_flag = nofs_flag; +} + +static inline void cifs_server_unlock(struct TCP_Server_Info *server) +{ + unsigned int nofs_flag = server->nofs_flag; + + mutex_unlock(&server->_srv_mutex); + memalloc_nofs_restore(nofs_flag); +} + +struct cifs_credits { + unsigned int value; + unsigned int instance; +}; + +static inline unsigned int +in_flight(struct TCP_Server_Info *server) +{ + unsigned int num; + spin_lock(&server->req_lock); + num = server->in_flight; + spin_unlock(&server->req_lock); + return num; +} + +static inline bool +has_credits(struct TCP_Server_Info *server, int *credits, int num_credits) +{ + int num; + spin_lock(&server->req_lock); + num = *credits; + spin_unlock(&server->req_lock); + return num >= num_credits; +} + +static inline void +add_credits(struct TCP_Server_Info *server, const struct cifs_credits *credits, + const int optype) +{ + server->ops->add_credits(server, credits, optype); +} + +static inline void +add_credits_and_wake_if(struct TCP_Server_Info *server, + const struct cifs_credits *credits, const int optype) +{ + if (credits->value) { + server->ops->add_credits(server, credits, optype); + wake_up(&server->request_q); + } +} + +static inline void +set_credits(struct TCP_Server_Info *server, const int val) +{ + server->ops->set_credits(server, val); +} + +static inline int +adjust_credits(struct TCP_Server_Info *server, struct cifs_credits *credits, + const unsigned int payload_size) +{ + return server->ops->adjust_credits ? + server->ops->adjust_credits(server, credits, payload_size) : 0; +} + +static inline __le64 +get_next_mid64(struct TCP_Server_Info *server) +{ + return cpu_to_le64(server->ops->get_next_mid(server)); +} + +static inline __le16 +get_next_mid(struct TCP_Server_Info *server) +{ + __u16 mid = server->ops->get_next_mid(server); + /* + * The value in the SMB header should be little endian for easy + * on-the-wire decoding. + */ + return cpu_to_le16(mid); +} + +static inline void +revert_current_mid(struct TCP_Server_Info *server, const unsigned int val) +{ + if (server->ops->revert_current_mid) + server->ops->revert_current_mid(server, val); +} + +static inline void +revert_current_mid_from_hdr(struct TCP_Server_Info *server, + const struct smb2_hdr *shdr) +{ + unsigned int num = le16_to_cpu(shdr->CreditCharge); + + return revert_current_mid(server, num > 0 ? num : 1); +} + +static inline __u16 +get_mid(const struct smb_hdr *smb) +{ + return le16_to_cpu(smb->Mid); +} + +static inline bool +compare_mid(__u16 mid, const struct smb_hdr *smb) +{ + return mid == le16_to_cpu(smb->Mid); +} + +/* + * When the server supports very large reads and writes via POSIX extensions, + * we can allow up to 2^24-1, minus the size of a READ/WRITE_AND_X header, not + * including the RFC1001 length. + * + * Note that this might make for "interesting" allocation problems during + * writeback however as we have to allocate an array of pointers for the + * pages. A 16M write means ~32kb page array with PAGE_SIZE == 4096. + * + * For reads, there is a similar problem as we need to allocate an array + * of kvecs to handle the receive, though that should only need to be done + * once. + */ +#define CIFS_MAX_WSIZE ((1<<24) - 1 - sizeof(WRITE_REQ) + 4) +#define CIFS_MAX_RSIZE ((1<<24) - sizeof(READ_RSP) + 4) + +/* + * When the server doesn't allow large posix writes, only allow a rsize/wsize + * of 2^17-1 minus the size of the call header. That allows for a read or + * write up to the maximum size described by RFC1002. + */ +#define CIFS_MAX_RFC1002_WSIZE ((1<<17) - 1 - sizeof(WRITE_REQ) + 4) +#define CIFS_MAX_RFC1002_RSIZE ((1<<17) - 1 - sizeof(READ_RSP) + 4) + +#define CIFS_DEFAULT_IOSIZE (1024 * 1024) + +/* + * Windows only supports a max of 60kb reads and 65535 byte writes. Default to + * those values when posix extensions aren't in force. In actuality here, we + * use 65536 to allow for a write that is a multiple of 4k. Most servers seem + * to be ok with the extra byte even though Windows doesn't send writes that + * are that large. + * + * Citation: + * + * https://blogs.msdn.com/b/openspecification/archive/2009/04/10/smb-maximum-transmit-buffer-size-and-performance-tuning.aspx + */ +#define CIFS_DEFAULT_NON_POSIX_RSIZE (60 * 1024) +#define CIFS_DEFAULT_NON_POSIX_WSIZE (65536) + +/* + * Macros to allow the TCP_Server_Info->net field and related code to drop out + * when CONFIG_NET_NS isn't set. + */ + +#ifdef CONFIG_NET_NS + +static inline struct net *cifs_net_ns(struct TCP_Server_Info *srv) +{ + return srv->net; +} + +static inline void cifs_set_net_ns(struct TCP_Server_Info *srv, struct net *net) +{ + srv->net = net; +} + +#else + +static inline struct net *cifs_net_ns(struct TCP_Server_Info *srv) +{ + return &init_net; +} + +static inline void cifs_set_net_ns(struct TCP_Server_Info *srv, struct net *net) +{ +} + +#endif + +struct cifs_server_iface { + struct list_head iface_head; + struct kref refcount; + size_t speed; + unsigned int rdma_capable : 1; + unsigned int rss_capable : 1; + unsigned int is_active : 1; /* unset if non existent */ + struct sockaddr_storage sockaddr; +}; + +/* release iface when last ref is dropped */ +static inline void +release_iface(struct kref *ref) +{ + struct cifs_server_iface *iface = container_of(ref, + struct cifs_server_iface, + refcount); + list_del_init(&iface->iface_head); + kfree(iface); +} + +/* + * compare two interfaces a and b + * return 0 if everything matches. + * return 1 if a has higher link speed, or rdma capable, or rss capable + * return -1 otherwise. + */ +static inline int +iface_cmp(struct cifs_server_iface *a, struct cifs_server_iface *b) +{ + int cmp_ret = 0; + + WARN_ON(!a || !b); + if (a->speed == b->speed) { + if (a->rdma_capable == b->rdma_capable) { + if (a->rss_capable == b->rss_capable) { + cmp_ret = memcmp(&a->sockaddr, &b->sockaddr, + sizeof(a->sockaddr)); + if (!cmp_ret) + return 0; + else if (cmp_ret > 0) + return 1; + else + return -1; + } else if (a->rss_capable > b->rss_capable) + return 1; + else + return -1; + } else if (a->rdma_capable > b->rdma_capable) + return 1; + else + return -1; + } else if (a->speed > b->speed) + return 1; + else + return -1; +} + +struct cifs_chan { + unsigned int in_reconnect : 1; /* if session setup in progress for this channel */ + struct TCP_Server_Info *server; + struct cifs_server_iface *iface; /* interface in use */ + __u8 signkey[SMB3_SIGN_KEY_SIZE]; +}; + +/* + * Session structure. One of these for each uid session with a particular host + */ +struct cifs_ses { + struct list_head smb_ses_list; + struct list_head rlist; /* reconnect list */ + struct list_head tcon_list; + struct cifs_tcon *tcon_ipc; + spinlock_t ses_lock; /* protect anything here that is not protected */ + struct mutex session_mutex; + struct TCP_Server_Info *server; /* pointer to server info */ + int ses_count; /* reference counter */ + enum ses_status_enum ses_status; /* updates protected by cifs_tcp_ses_lock */ + unsigned overrideSecFlg; /* if non-zero override global sec flags */ + char *serverOS; /* name of operating system underlying server */ + char *serverNOS; /* name of network operating system of server */ + char *serverDomain; /* security realm of server */ + __u64 Suid; /* remote smb uid */ + kuid_t linux_uid; /* overriding owner of files on the mount */ + kuid_t cred_uid; /* owner of credentials */ + unsigned int capabilities; + char ip_addr[INET6_ADDRSTRLEN + 1]; /* Max ipv6 (or v4) addr string len */ + char *user_name; /* must not be null except during init of sess + and after mount option parsing we fill it */ + char *domainName; + char *password; + char workstation_name[CIFS_MAX_WORKSTATION_LEN]; + struct session_key auth_key; + struct ntlmssp_auth *ntlmssp; /* ciphertext, flags, server challenge */ + enum securityEnum sectype; /* what security flavor was specified? */ + bool sign; /* is signing required? */ + bool domainAuto:1; + __u16 session_flags; + __u8 smb3signingkey[SMB3_SIGN_KEY_SIZE]; + __u8 smb3encryptionkey[SMB3_ENC_DEC_KEY_SIZE]; + __u8 smb3decryptionkey[SMB3_ENC_DEC_KEY_SIZE]; + __u8 preauth_sha_hash[SMB2_PREAUTH_HASH_SIZE]; + + /* + * Network interfaces available on the server this session is + * connected to. + * + * Other channels can be opened by connecting and binding this + * session to interfaces from this list. + * + * iface_lock should be taken when accessing any of these fields + */ + spinlock_t iface_lock; + /* ========= begin: protected by iface_lock ======== */ + struct list_head iface_list; + size_t iface_count; + unsigned long iface_last_update; /* jiffies */ + /* ========= end: protected by iface_lock ======== */ + + spinlock_t chan_lock; + /* ========= begin: protected by chan_lock ======== */ +#define CIFS_MAX_CHANNELS 16 +#define CIFS_ALL_CHANNELS_SET(ses) \ + ((1UL << (ses)->chan_count) - 1) +#define CIFS_ALL_CHANS_GOOD(ses) \ + (!(ses)->chans_need_reconnect) +#define CIFS_ALL_CHANS_NEED_RECONNECT(ses) \ + ((ses)->chans_need_reconnect == CIFS_ALL_CHANNELS_SET(ses)) +#define CIFS_SET_ALL_CHANS_NEED_RECONNECT(ses) \ + ((ses)->chans_need_reconnect = CIFS_ALL_CHANNELS_SET(ses)) +#define CIFS_CHAN_NEEDS_RECONNECT(ses, index) \ + test_bit((index), &(ses)->chans_need_reconnect) +#define CIFS_CHAN_IN_RECONNECT(ses, index) \ + ((ses)->chans[(index)].in_reconnect) + + struct cifs_chan chans[CIFS_MAX_CHANNELS]; + size_t chan_count; + size_t chan_max; + atomic_t chan_seq; /* round robin state */ + + /* + * chans_need_reconnect is a bitmap indicating which of the channels + * under this smb session needs to be reconnected. + * If not multichannel session, only one bit will be used. + * + * We will ask for sess and tcon reconnection only if all the + * channels are marked for needing reconnection. This will + * enable the sessions on top to continue to live till any + * of the channels below are active. + */ + unsigned long chans_need_reconnect; + /* ========= end: protected by chan_lock ======== */ +}; + +static inline bool +cap_unix(struct cifs_ses *ses) +{ + return ses->server->vals->cap_unix & ses->capabilities; +} + +/* + * common struct for holding inode info when searching for or updating an + * inode with new info + */ + +#define CIFS_FATTR_DFS_REFERRAL 0x1 +#define CIFS_FATTR_DELETE_PENDING 0x2 +#define CIFS_FATTR_NEED_REVAL 0x4 +#define CIFS_FATTR_INO_COLLISION 0x8 +#define CIFS_FATTR_UNKNOWN_NLINK 0x10 +#define CIFS_FATTR_FAKE_ROOT_INO 0x20 + +struct cifs_fattr { + u32 cf_flags; + u32 cf_cifsattrs; + u64 cf_uniqueid; + u64 cf_eof; + u64 cf_bytes; + u64 cf_createtime; + kuid_t cf_uid; + kgid_t cf_gid; + umode_t cf_mode; + dev_t cf_rdev; + unsigned int cf_nlink; + unsigned int cf_dtype; + struct timespec64 cf_atime; + struct timespec64 cf_mtime; + struct timespec64 cf_ctime; + u32 cf_cifstag; + char *cf_symlink_target; +}; + +/* + * there is one of these for each connection to a resource on a particular + * session + */ +struct cifs_tcon { + struct list_head tcon_list; + int tc_count; + struct list_head rlist; /* reconnect list */ + spinlock_t tc_lock; /* protect anything here that is not protected */ + atomic_t num_local_opens; /* num of all opens including disconnected */ + atomic_t num_remote_opens; /* num of all network opens on server */ + struct list_head openFileList; + spinlock_t open_file_lock; /* protects list above */ + struct cifs_ses *ses; /* pointer to session associated with */ + char tree_name[MAX_TREE_SIZE + 1]; /* UNC name of resource in ASCII */ + char *nativeFileSystem; + char *password; /* for share-level security */ + __u32 tid; /* The 4 byte tree id */ + __u16 Flags; /* optional support bits */ + enum tid_status_enum status; + atomic_t num_smbs_sent; + union { + struct { + atomic_t num_writes; + atomic_t num_reads; + atomic_t num_flushes; + atomic_t num_oplock_brks; + atomic_t num_opens; + atomic_t num_closes; + atomic_t num_deletes; + atomic_t num_mkdirs; + atomic_t num_posixopens; + atomic_t num_posixmkdirs; + 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; + atomic_t num_acl_get; + atomic_t num_acl_set; + } cifs_stats; + struct { + atomic_t smb2_com_sent[NUMBER_OF_SMB2_COMMANDS]; + atomic_t smb2_com_failed[NUMBER_OF_SMB2_COMMANDS]; + } smb2_stats; + } stats; + __u64 bytes_read; + __u64 bytes_written; + spinlock_t stat_lock; /* protects the two fields above */ + FILE_SYSTEM_DEVICE_INFO fsDevInfo; + FILE_SYSTEM_ATTRIBUTE_INFO fsAttrInfo; /* ok if fs name truncated */ + FILE_SYSTEM_UNIX_INFO fsUnixInfo; + bool ipc:1; /* set if connection to IPC$ share (always also pipe) */ + bool pipe:1; /* set if connection to pipe share */ + bool print:1; /* set if connection to printer share */ + bool retry:1; + bool nocase:1; + bool nohandlecache:1; /* if strange server resource prob can turn off */ + bool nodelete:1; + bool seal:1; /* transport encryption for this mounted share */ + bool unix_ext:1; /* if false disable Linux extensions to CIFS protocol + for this mount even if server would support */ + bool posix_extensions; /* if true SMB3.11 posix extensions enabled */ + bool local_lease:1; /* check leases (only) on local system not remote */ + bool broken_posix_open; /* e.g. Samba server versions < 3.3.2, 3.2.9 */ + bool broken_sparse_sup; /* if server or share does not support sparse */ + bool need_reconnect:1; /* connection reset, tid now invalid */ + bool need_reopen_files:1; /* need to reopen tcon file handles */ + bool use_resilient:1; /* use resilient instead of durable handles */ + bool use_persistent:1; /* use persistent instead of durable handles */ + bool no_lease:1; /* Do not request leases on files or directories */ + bool use_witness:1; /* use witness protocol */ + __le32 capabilities; + __u32 share_flags; + __u32 maximal_access; + __u32 vol_serial_number; + __le64 vol_create_time; + __u64 snapshot_time; /* for timewarp tokens - timestamp of snapshot */ + __u32 handle_timeout; /* persistent and durable handle timeout in ms */ + __u32 ss_flags; /* sector size flags */ + __u32 perf_sector_size; /* best sector size for perf */ + __u32 max_chunks; + __u32 max_bytes_chunk; + __u32 max_bytes_copy; +#ifdef CONFIG_CIFS_FSCACHE + u64 resource_id; /* server resource id */ + struct fscache_volume *fscache; /* cookie for share */ +#endif + struct list_head pending_opens; /* list of incomplete opens */ + struct cached_fids *cfids; + /* BB add field for back pointer to sb struct(s)? */ +#ifdef CONFIG_CIFS_DFS_UPCALL + struct list_head ulist; /* cache update list */ +#endif + struct delayed_work query_interfaces; /* query interfaces workqueue job */ +}; + +/* + * This is a refcounted and timestamped container for a tcon pointer. The + * container holds a tcon reference. It is considered safe to free one of + * these when the tl_count goes to 0. The tl_time is the time of the last + * "get" on the container. + */ +struct tcon_link { + struct rb_node tl_rbnode; + kuid_t tl_uid; + unsigned long tl_flags; +#define TCON_LINK_MASTER 0 +#define TCON_LINK_PENDING 1 +#define TCON_LINK_IN_TREE 2 + unsigned long tl_time; + atomic_t tl_count; + struct cifs_tcon *tl_tcon; +}; + +extern struct tcon_link *cifs_sb_tlink(struct cifs_sb_info *cifs_sb); +extern void smb3_free_compound_rqst(int num_rqst, struct smb_rqst *rqst); + +static inline struct cifs_tcon * +tlink_tcon(struct tcon_link *tlink) +{ + return tlink->tl_tcon; +} + +static inline struct tcon_link * +cifs_sb_master_tlink(struct cifs_sb_info *cifs_sb) +{ + return cifs_sb->master_tlink; +} + +extern void cifs_put_tlink(struct tcon_link *tlink); + +static inline struct tcon_link * +cifs_get_tlink(struct tcon_link *tlink) +{ + if (tlink && !IS_ERR(tlink)) + atomic_inc(&tlink->tl_count); + return tlink; +} + +/* This function is always expected to succeed */ +extern struct cifs_tcon *cifs_sb_master_tcon(struct cifs_sb_info *cifs_sb); + +#define CIFS_OPLOCK_NO_CHANGE 0xfe + +struct cifs_pending_open { + struct list_head olist; + struct tcon_link *tlink; + __u8 lease_key[16]; + __u32 oplock; +}; + +struct cifs_deferred_close { + struct list_head dlist; + struct tcon_link *tlink; + __u16 netfid; + __u64 persistent_fid; + __u64 volatile_fid; +}; + +/* + * This info hangs off the cifsFileInfo structure, pointed to by llist. + * This is used to track byte stream locks on the file + */ +struct cifsLockInfo { + struct list_head llist; /* pointer to next cifsLockInfo */ + struct list_head blist; /* pointer to locks blocked on this */ + wait_queue_head_t block_q; + __u64 offset; + __u64 length; + __u32 pid; + __u16 type; + __u16 flags; +}; + +/* + * One of these for each open instance of a file + */ +struct cifs_search_info { + loff_t index_of_last_entry; + __u16 entries_in_buffer; + __u16 info_level; + __u32 resume_key; + char *ntwrk_buf_start; + char *srch_entries_start; + char *last_entry; + const char *presume_name; + unsigned int resume_name_len; + bool endOfSearch:1; + bool emptyDir:1; + bool unicode:1; + bool smallBuf:1; /* so we know which buf_release function to call */ +}; + +#define ACL_NO_MODE ((umode_t)(-1)) +struct cifs_open_parms { + struct cifs_tcon *tcon; + struct cifs_sb_info *cifs_sb; + int disposition; + int desired_access; + int create_options; + const char *path; + struct cifs_fid *fid; + umode_t mode; + bool reconnect:1; +}; + +struct cifs_fid { + __u16 netfid; + __u64 persistent_fid; /* persist file id for smb2 */ + __u64 volatile_fid; /* volatile file id for smb2 */ + __u8 lease_key[SMB2_LEASE_KEY_SIZE]; /* lease key for smb2 */ + __u8 create_guid[16]; + __u32 access; + struct cifs_pending_open *pending_open; + unsigned int epoch; +#ifdef CONFIG_CIFS_DEBUG2 + __u64 mid; +#endif /* CIFS_DEBUG2 */ + bool purge_cache; +}; + +struct cifs_fid_locks { + struct list_head llist; + struct cifsFileInfo *cfile; /* fid that owns locks */ + struct list_head locks; /* locks held by fid above */ +}; + +struct cifsFileInfo { + /* following two lists are protected by tcon->open_file_lock */ + struct list_head tlist; /* pointer to next fid owned by tcon */ + struct list_head flist; /* next fid (file instance) for this inode */ + /* lock list below protected by cifsi->lock_sem */ + struct cifs_fid_locks *llist; /* brlocks held by this fid */ + kuid_t uid; /* allows finding which FileInfo structure */ + __u32 pid; /* process id who opened file */ + struct cifs_fid fid; /* file id from remote */ + struct list_head rlist; /* reconnect list */ + /* BB add lock scope info here if needed */ ; + /* lock scope id (0 if none) */ + struct dentry *dentry; + struct tcon_link *tlink; + unsigned int f_flags; + bool invalidHandle:1; /* file closed via session abend */ + bool swapfile:1; + bool oplock_break_cancelled:1; + unsigned int oplock_epoch; /* epoch from the lease break */ + __u32 oplock_level; /* oplock/lease level from the lease break */ + int count; + spinlock_t file_info_lock; /* protects four flag/count fields above */ + struct mutex fh_mutex; /* prevents reopen race after dead ses*/ + struct cifs_search_info srch_inf; + struct work_struct oplock_break; /* work for oplock breaks */ + struct work_struct put; /* work for the final part of _put */ + struct delayed_work deferred; + bool deferred_close_scheduled; /* Flag to indicate close is scheduled */ + char *symlink_target; +}; + +struct cifs_io_parms { + __u16 netfid; + __u64 persistent_fid; /* persist file id for smb2 */ + __u64 volatile_fid; /* volatile file id for smb2 */ + __u32 pid; + __u64 offset; + unsigned int length; + struct cifs_tcon *tcon; + struct TCP_Server_Info *server; +}; + +struct cifs_aio_ctx { + struct kref refcount; + struct list_head list; + struct mutex aio_mutex; + struct completion done; + struct iov_iter iter; + struct kiocb *iocb; + struct cifsFileInfo *cfile; + struct bio_vec *bv; + loff_t pos; + unsigned int npages; + ssize_t rc; + unsigned int len; + unsigned int total_len; + bool should_dirty; + /* + * Indicates if this aio_ctx is for direct_io, + * If yes, iter is a copy of the user passed iov_iter + */ + bool direct_io; +}; + +/* asynchronous read support */ +struct cifs_readdata { + struct kref refcount; + struct list_head list; + struct completion done; + struct cifsFileInfo *cfile; + struct address_space *mapping; + struct cifs_aio_ctx *ctx; + __u64 offset; + unsigned int bytes; + unsigned int got_bytes; + pid_t pid; + int result; + struct work_struct work; + int (*read_into_pages)(struct TCP_Server_Info *server, + struct cifs_readdata *rdata, + unsigned int len); + int (*copy_into_pages)(struct TCP_Server_Info *server, + struct cifs_readdata *rdata, + struct iov_iter *iter); + struct kvec iov[2]; + struct TCP_Server_Info *server; +#ifdef CONFIG_CIFS_SMB_DIRECT + struct smbd_mr *mr; +#endif + unsigned int pagesz; + unsigned int page_offset; + unsigned int tailsz; + struct cifs_credits credits; + unsigned int nr_pages; + struct page **pages; +}; + +/* asynchronous write support */ +struct cifs_writedata { + struct kref refcount; + struct list_head list; + struct completion done; + enum writeback_sync_modes sync_mode; + struct work_struct work; + struct cifsFileInfo *cfile; + struct cifs_aio_ctx *ctx; + __u64 offset; + pid_t pid; + unsigned int bytes; + int result; + struct TCP_Server_Info *server; +#ifdef CONFIG_CIFS_SMB_DIRECT + struct smbd_mr *mr; +#endif + unsigned int pagesz; + unsigned int page_offset; + unsigned int tailsz; + struct cifs_credits credits; + unsigned int nr_pages; + struct page **pages; +}; + +/* + * Take a reference on the file private data. Must be called with + * cfile->file_info_lock held. + */ +static inline void +cifsFileInfo_get_locked(struct cifsFileInfo *cifs_file) +{ + ++cifs_file->count; +} + +struct cifsFileInfo *cifsFileInfo_get(struct cifsFileInfo *cifs_file); +void _cifsFileInfo_put(struct cifsFileInfo *cifs_file, bool wait_oplock_hdlr, + bool offload); +void cifsFileInfo_put(struct cifsFileInfo *cifs_file); + +#define CIFS_CACHE_READ_FLG 1 +#define CIFS_CACHE_HANDLE_FLG 2 +#define CIFS_CACHE_RH_FLG (CIFS_CACHE_READ_FLG | CIFS_CACHE_HANDLE_FLG) +#define CIFS_CACHE_WRITE_FLG 4 +#define CIFS_CACHE_RW_FLG (CIFS_CACHE_READ_FLG | CIFS_CACHE_WRITE_FLG) +#define CIFS_CACHE_RHW_FLG (CIFS_CACHE_RW_FLG | CIFS_CACHE_HANDLE_FLG) + +#define CIFS_CACHE_READ(cinode) ((cinode->oplock & CIFS_CACHE_READ_FLG) || (CIFS_SB(cinode->netfs.inode.i_sb)->mnt_cifs_flags & CIFS_MOUNT_RO_CACHE)) +#define CIFS_CACHE_HANDLE(cinode) (cinode->oplock & CIFS_CACHE_HANDLE_FLG) +#define CIFS_CACHE_WRITE(cinode) ((cinode->oplock & CIFS_CACHE_WRITE_FLG) || (CIFS_SB(cinode->netfs.inode.i_sb)->mnt_cifs_flags & CIFS_MOUNT_RW_CACHE)) + +/* + * One of these for each file inode + */ + +struct cifsInodeInfo { + struct netfs_inode netfs; /* Netfslib context and vfs inode */ + bool can_cache_brlcks; + struct list_head llist; /* locks helb by this inode */ + /* + * NOTE: Some code paths call down_read(lock_sem) twice, so + * we must always use cifs_down_write() instead of down_write() + * for this semaphore to avoid deadlocks. + */ + struct rw_semaphore lock_sem; /* protect the fields above */ + /* BB add in lists for dirty pages i.e. write caching info for oplock */ + struct list_head openFileList; + spinlock_t open_file_lock; /* protects openFileList */ + __u32 cifsAttrs; /* e.g. DOS archive bit, sparse, compressed, system */ + unsigned int oplock; /* oplock/lease level we have */ + unsigned int epoch; /* used to track lease state changes */ +#define CIFS_INODE_PENDING_OPLOCK_BREAK (0) /* oplock break in progress */ +#define CIFS_INODE_PENDING_WRITERS (1) /* Writes in progress */ +#define CIFS_INODE_FLAG_UNUSED (2) /* Unused flag */ +#define CIFS_INO_DELETE_PENDING (3) /* delete pending on server */ +#define CIFS_INO_INVALID_MAPPING (4) /* pagecache is invalid */ +#define CIFS_INO_LOCK (5) /* lock bit for synchronization */ +#define CIFS_INO_MODIFIED_ATTR (6) /* Indicate change in mtime/ctime */ +#define CIFS_INO_CLOSE_ON_LOCK (7) /* Not to defer the close when lock is set */ + unsigned long flags; + spinlock_t writers_lock; + unsigned int writers; /* Number of writers on this inode */ + unsigned long time; /* jiffies of last update of inode */ + u64 server_eof; /* current file size on server -- protected by i_lock */ + u64 uniqueid; /* server inode number */ + u64 createtime; /* creation time on server */ + __u8 lease_key[SMB2_LEASE_KEY_SIZE]; /* lease key for this inode */ + struct list_head deferred_closes; /* list of deferred closes */ + spinlock_t deferred_lock; /* protection on deferred list */ + bool lease_granted; /* Flag to indicate whether lease or oplock is granted. */ + char *symlink_target; +}; + +static inline struct cifsInodeInfo * +CIFS_I(struct inode *inode) +{ + return container_of(inode, struct cifsInodeInfo, netfs.inode); +} + +static inline struct cifs_sb_info * +CIFS_SB(struct super_block *sb) +{ + return sb->s_fs_info; +} + +static inline struct cifs_sb_info * +CIFS_FILE_SB(struct file *file) +{ + return CIFS_SB(file_inode(file)->i_sb); +} + +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 '\\'; +} + +static inline void +convert_delimiter(char *path, char delim) +{ + char old_delim, *pos; + + if (delim == '/') + old_delim = '\\'; + else + old_delim = '/'; + + pos = path; + while ((pos = strchr(pos, old_delim))) + *pos = delim; +} + +#define cifs_stats_inc atomic_inc + +static inline void cifs_stats_bytes_written(struct cifs_tcon *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 cifs_tcon *tcon, + unsigned int bytes) +{ + spin_lock(&tcon->stat_lock); + tcon->bytes_read += bytes; + spin_unlock(&tcon->stat_lock); +} + + +/* + * This is the prototype for the mid receive function. This function is for + * receiving the rest of the SMB frame, starting with the WordCount (which is + * just after the MID in struct smb_hdr). Note: + * + * - This will be called by cifsd, with no locks held. + * - The mid will still be on the pending_mid_q. + * - mid->resp_buf will point to the current buffer. + * + * Returns zero on a successful receive, or an error. The receive state in + * the TCP_Server_Info will also be updated. + */ +typedef int (mid_receive_t)(struct TCP_Server_Info *server, + struct mid_q_entry *mid); + +/* + * This is the prototype for the mid callback function. This is called once the + * mid has been received off of the socket. When creating one, take special + * care to avoid deadlocks. Things to bear in mind: + * + * - it will be called by cifsd, with no locks held + * - the mid will be removed from any lists + */ +typedef void (mid_callback_t)(struct mid_q_entry *mid); + +/* + * This is the protopyte for mid handle function. This is called once the mid + * has been recognized after decryption of the message. + */ +typedef int (mid_handle_t)(struct TCP_Server_Info *server, + struct mid_q_entry *mid); + +/* one of these for every pending CIFS request to the server */ +struct mid_q_entry { + struct list_head qhead; /* mids waiting on reply from this server */ + struct kref refcount; + struct TCP_Server_Info *server; /* server corresponding to this mid */ + __u64 mid; /* multiplex id */ + __u16 credits; /* number of credits consumed by this mid */ + __u16 credits_received; /* number of credits from the response */ + __u32 pid; /* process id */ + __u32 sequence_number; /* for CIFS signing */ + 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 + mid_receive_t *receive; /* call receive callback */ + mid_callback_t *callback; /* call completion callback */ + mid_handle_t *handle; /* call handle mid callback */ + void *callback_data; /* general purpose pointer for callback */ + struct task_struct *creator; + void *resp_buf; /* pointer to received SMB header */ + unsigned int resp_buf_size; + int mid_state; /* wish this were enum but can not pass to wait_event */ + unsigned int mid_flags; + __le16 command; /* smb command code */ + unsigned int optype; /* operation type */ + bool large_buf:1; /* if valid response, is pointer to large buf */ + bool multiRsp:1; /* multiple trans2 responses for one request */ + bool multiEnd:1; /* both received */ + bool decrypted:1; /* decrypted entry */ +}; + +struct close_cancelled_open { + struct cifs_fid fid; + struct cifs_tcon *tcon; + struct work_struct work; + __u64 mid; + __u16 cmd; +}; + +/* Make code in transport.c a little cleaner by moving + update of optional stats into function below */ +static inline void cifs_in_send_inc(struct TCP_Server_Info *server) +{ + atomic_inc(&server->in_send); +} + +static inline void cifs_in_send_dec(struct TCP_Server_Info *server) +{ + atomic_dec(&server->in_send); +} + +static inline void cifs_num_waiters_inc(struct TCP_Server_Info *server) +{ + atomic_inc(&server->num_waiters); +} + +static inline void cifs_num_waiters_dec(struct TCP_Server_Info *server) +{ + atomic_dec(&server->num_waiters); +} + +#ifdef CONFIG_CIFS_STATS2 +static inline void cifs_save_when_sent(struct mid_q_entry *mid) +{ + mid->when_sent = jiffies; +} +#else +static inline void cifs_save_when_sent(struct mid_q_entry *mid) +{ +} +#endif + +/* 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; +}; + +struct dfs_info3_param { + int flags; /* DFSREF_REFERRAL_SERVER, DFSREF_STORAGE_SERVER*/ + int path_consumed; + int server_type; + int ref_flag; + char *path_name; + char *node_name; + int ttl; +}; + +struct file_list { + struct list_head list; + struct cifsFileInfo *cfile; +}; + +static inline void free_dfs_info_param(struct dfs_info3_param *param) +{ + if (param) { + kfree(param->path_name); + kfree(param->node_name); + } +} + +static inline void free_dfs_info_array(struct dfs_info3_param *param, + int number_of_items) +{ + int i; + if ((number_of_items == 0) || (param == NULL)) + return; + for (i = 0; i < number_of_items; i++) { + kfree(param[i].path_name); + kfree(param[i].node_name); + } + kfree(param); +} + +static inline bool is_interrupt_error(int error) +{ + switch (error) { + case -EINTR: + case -ERESTARTSYS: + case -ERESTARTNOHAND: + case -ERESTARTNOINTR: + return true; + } + return false; +} + +static inline bool is_retryable_error(int error) +{ + if (is_interrupt_error(error) || error == -EAGAIN) + return true; + return false; +} + + +/* cifs_get_writable_file() flags */ +#define FIND_WR_ANY 0 +#define FIND_WR_FSUID_ONLY 1 +#define FIND_WR_WITH_DELETE 2 + +#define MID_FREE 0 +#define MID_REQUEST_ALLOCATED 1 +#define MID_REQUEST_SUBMITTED 2 +#define MID_RESPONSE_RECEIVED 4 +#define MID_RETRY_NEEDED 8 /* session closed while this request out */ +#define MID_RESPONSE_MALFORMED 0x10 +#define MID_SHUTDOWN 0x20 + +/* Flags */ +#define MID_WAIT_CANCELLED 1 /* Cancelled while waiting for response */ +#define MID_DELETED 2 /* Mid has been dequeued/deleted */ + +/* Types of response buffer returned from SendReceive2 */ +#define CIFS_NO_BUFFER 0 /* Response buffer not returned */ +#define CIFS_SMALL_BUFFER 1 +#define CIFS_LARGE_BUFFER 2 +#define CIFS_IOVEC 4 /* array of response buffers */ + +/* Type of Request to SendReceive2 */ +#define CIFS_BLOCKING_OP 1 /* operation can block */ +#define CIFS_NON_BLOCKING 2 /* do not block waiting for credits */ +#define CIFS_TIMEOUT_MASK 0x003 /* only one of above set in req */ +#define CIFS_LOG_ERROR 0x010 /* log NT STATUS if non-zero */ +#define CIFS_LARGE_BUF_OP 0x020 /* large request buffer */ +#define CIFS_NO_RSP_BUF 0x040 /* no response buffer required */ + +/* Type of request operation */ +#define CIFS_ECHO_OP 0x080 /* echo request */ +#define CIFS_OBREAK_OP 0x0100 /* oplock break request */ +#define CIFS_NEG_OP 0x0200 /* negotiate request */ +#define CIFS_CP_CREATE_CLOSE_OP 0x0400 /* compound create+close request */ +/* Lower bitmask values are reserved by others below. */ +#define CIFS_SESS_OP 0x2000 /* session setup request */ +#define CIFS_OP_MASK 0x2780 /* mask request type */ + +#define CIFS_HAS_CREDITS 0x0400 /* already has credits */ +#define CIFS_TRANSFORM_REQ 0x0800 /* transform request before sending */ +#define CIFS_NO_SRV_RSP 0x1000 /* there is no server response */ + +/* Security Flags: indicate type of session setup needed */ +#define CIFSSEC_MAY_SIGN 0x00001 +#define CIFSSEC_MAY_NTLMV2 0x00004 +#define CIFSSEC_MAY_KRB5 0x00008 +#define CIFSSEC_MAY_SEAL 0x00040 /* not supported yet */ +#define CIFSSEC_MAY_NTLMSSP 0x00080 /* raw ntlmssp with ntlmv2 */ + +#define CIFSSEC_MUST_SIGN 0x01001 +/* note that only one of the following can be set so the +result of setting MUST flags more than once will be to +require use of the stronger protocol */ +#define CIFSSEC_MUST_NTLMV2 0x04004 +#define CIFSSEC_MUST_KRB5 0x08008 +#ifdef CONFIG_CIFS_UPCALL +#define CIFSSEC_MASK 0x8F08F /* flags supported if no weak allowed */ +#else +#define CIFSSEC_MASK 0x87087 /* flags supported if no weak allowed */ +#endif /* UPCALL */ +#define CIFSSEC_MUST_SEAL 0x40040 /* not supported yet */ +#define CIFSSEC_MUST_NTLMSSP 0x80080 /* raw ntlmssp with ntlmv2 */ + +#define CIFSSEC_DEF (CIFSSEC_MAY_SIGN | CIFSSEC_MAY_NTLMV2 | CIFSSEC_MAY_NTLMSSP) +#define CIFSSEC_MAX (CIFSSEC_MUST_NTLMV2) +#define CIFSSEC_AUTH_MASK (CIFSSEC_MAY_NTLMV2 | CIFSSEC_MAY_KRB5 | CIFSSEC_MAY_NTLMSSP) +/* + ***************************************************************** + * All constants go here + ***************************************************************** + */ + +#define UID_HASH (16) + +/* + * Note that ONE module should define _DECLARE_GLOBALS_HERE to cause the + * following to be declared. + */ + +/**************************************************************************** + * Here are all the locks (spinlock, mutex, semaphore) in cifs.ko, arranged according + * to the locking order. i.e. if two locks are to be held together, the lock that + * appears higher in this list needs to be taken before the other. + * + * If you hold a lock that is lower in this list, and you need to take a higher lock + * (or if you think that one of the functions that you're calling may need to), first + * drop the lock you hold, pick up the higher lock, then the lower one. This will + * ensure that locks are picked up only in one direction in the below table + * (top to bottom). + * + * Also, if you expect a function to be called with a lock held, explicitly document + * this in the comments on top of your function definition. + * + * And also, try to keep the critical sections (lock hold time) to be as minimal as + * possible. Blocking / calling other functions with a lock held always increase + * the risk of a possible deadlock. + * + * Following this rule will avoid unnecessary deadlocks, which can get really hard to + * debug. Also, any new lock that you introduce, please add to this list in the correct + * order. + * + * Please populate this list whenever you introduce new locks in your changes. Or in + * case I've missed some existing locks. Please ensure that it's added in the list + * based on the locking order expected. + * + * ===================================================================================== + * Lock Protects Initialization fn + * ===================================================================================== + * vol_list_lock + * vol_info->ctx_lock vol_info->ctx + * cifs_sb_info->tlink_tree_lock cifs_sb_info->tlink_tree cifs_setup_cifs_sb + * TCP_Server_Info-> TCP_Server_Info cifs_get_tcp_session + * reconnect_mutex + * TCP_Server_Info->srv_mutex TCP_Server_Info cifs_get_tcp_session + * cifs_ses->session_mutex cifs_ses sesInfoAlloc + * cifs_tcon + * cifs_tcon->open_file_lock cifs_tcon->openFileList tconInfoAlloc + * cifs_tcon->pending_opens + * cifs_tcon->stat_lock cifs_tcon->bytes_read tconInfoAlloc + * cifs_tcon->bytes_written + * cifs_tcp_ses_lock cifs_tcp_ses_list sesInfoAlloc + * GlobalMid_Lock GlobalMaxActiveXid init_cifs + * GlobalCurrentXid + * GlobalTotalActiveXid + * TCP_Server_Info->srv_lock (anything in struct not protected by another lock and can change) + * TCP_Server_Info->mid_lock TCP_Server_Info->pending_mid_q cifs_get_tcp_session + * ->CurrentMid + * (any changes in mid_q_entry fields) + * TCP_Server_Info->req_lock TCP_Server_Info->in_flight cifs_get_tcp_session + * ->credits + * ->echo_credits + * ->oplock_credits + * ->reconnect_instance + * cifs_ses->ses_lock (anything that is not protected by another lock and can change) + * cifs_ses->iface_lock cifs_ses->iface_list sesInfoAlloc + * ->iface_count + * ->iface_last_update + * cifs_ses->chan_lock cifs_ses->chans + * ->chans_need_reconnect + * ->chans_in_reconnect + * cifs_tcon->tc_lock (anything that is not protected by another lock and can change) + * cifsInodeInfo->open_file_lock cifsInodeInfo->openFileList cifs_alloc_inode + * cifsInodeInfo->writers_lock cifsInodeInfo->writers cifsInodeInfo_alloc + * cifsInodeInfo->lock_sem cifsInodeInfo->llist cifs_init_once + * ->can_cache_brlcks + * cifsInodeInfo->deferred_lock cifsInodeInfo->deferred_closes cifsInodeInfo_alloc + * cached_fid->fid_mutex cifs_tcon->crfid tconInfoAlloc + * cifsFileInfo->fh_mutex cifsFileInfo cifs_new_fileinfo + * cifsFileInfo->file_info_lock cifsFileInfo->count cifs_new_fileinfo + * ->invalidHandle initiate_cifs_search + * ->oplock_break_cancelled + * cifs_aio_ctx->aio_mutex cifs_aio_ctx cifs_aio_ctx_alloc + ****************************************************************************/ + +#ifdef DECLARE_GLOBALS_HERE +#define GLOBAL_EXTERN +#else +#define GLOBAL_EXTERN extern +#endif + +/* + * the list of TCP_Server_Info structures, ie each of the sockets + * connecting our client to a distinct server (ip address), is + * chained together by cifs_tcp_ses_list. The list of all our SMB + * sessions (and from that the tree connections) can be found + * by iterating over cifs_tcp_ses_list + */ +extern struct list_head cifs_tcp_ses_list; + +/* + * This lock protects the cifs_tcp_ses_list, the list of smb sessions per + * tcp session, and the list of tcon's per smb session. It also protects + * the reference counters for the server, smb session, and tcon. + * generally the locks should be taken in order tcp_ses_lock before + * tcon->open_file_lock and that before file->file_info_lock since the + * structure order is cifs_socket-->cifs_ses-->cifs_tcon-->cifs_file + */ +extern spinlock_t cifs_tcp_ses_lock; + +/* + * Global transaction id (XID) information + */ +extern unsigned int GlobalCurrentXid; /* protected by GlobalMid_Sem */ +extern unsigned int GlobalTotalActiveXid; /* prot by GlobalMid_Sem */ +extern unsigned int GlobalMaxActiveXid; /* prot by GlobalMid_Sem */ +extern spinlock_t GlobalMid_Lock; /* protects above & list operations on midQ entries */ + +/* + * Global counters, updated atomically + */ +extern atomic_t sesInfoAllocCount; +extern atomic_t tconInfoAllocCount; +extern atomic_t tcpSesNextId; +extern atomic_t tcpSesAllocCount; +extern atomic_t tcpSesReconnectCount; +extern atomic_t tconInfoReconnectCount; + +/* Various Debug counters */ +extern atomic_t buf_alloc_count; /* current number allocated */ +extern atomic_t small_buf_alloc_count; +#ifdef CONFIG_CIFS_STATS2 +extern atomic_t total_buf_alloc_count; /* total allocated over all time */ +extern atomic_t total_small_buf_alloc_count; +extern unsigned int slow_rsp_threshold; /* number of secs before logging */ +#endif + +/* Misc globals */ +extern bool enable_oplocks; /* enable or disable oplocks */ +extern bool lookupCacheEnabled; +extern unsigned int global_secflags; /* if on, session setup sent + with more secure ntlmssp2 challenge/resp */ +extern unsigned int sign_CIFS_PDUs; /* enable smb packet signing */ +extern bool enable_gcm_256; /* allow optional negotiate of strongest signing (aes-gcm-256) */ +extern bool require_gcm_256; /* require use of strongest signing (aes-gcm-256) */ +extern bool enable_negotiate_signing; /* request use of faster (GMAC) signing if available */ +extern bool linuxExtEnabled;/*enable Linux/Unix CIFS extensions*/ +extern unsigned int CIFSMaxBufSize; /* max size not including hdr */ +extern unsigned int cifs_min_rcv; /* min size of big ntwrk buf pool */ +extern unsigned int cifs_min_small; /* min size of small buf pool */ +extern unsigned int cifs_max_pending; /* MAX requests at once to server*/ +extern bool disable_legacy_dialects; /* forbid vers=1.0 and vers=2.0 mounts */ +extern atomic_t mid_count; + +void cifs_oplock_break(struct work_struct *work); +void cifs_queue_oplock_break(struct cifsFileInfo *cfile); +void smb2_deferred_work_close(struct work_struct *work); + +extern const struct slow_work_ops cifs_oplock_break_ops; +extern struct workqueue_struct *cifsiod_wq; +extern struct workqueue_struct *decrypt_wq; +extern struct workqueue_struct *fileinfo_put_wq; +extern struct workqueue_struct *cifsoplockd_wq; +extern struct workqueue_struct *deferredclose_wq; +extern __u32 cifs_lock_secret; + +extern mempool_t *cifs_mid_poolp; + +/* Operations for different SMB versions */ +#define SMB1_VERSION_STRING "1.0" +#define SMB20_VERSION_STRING "2.0" +#ifdef CONFIG_CIFS_ALLOW_INSECURE_LEGACY +extern struct smb_version_operations smb1_operations; +extern struct smb_version_values smb1_values; +extern struct smb_version_operations smb20_operations; +extern struct smb_version_values smb20_values; +#endif /* CIFS_ALLOW_INSECURE_LEGACY */ +#define SMB21_VERSION_STRING "2.1" +extern struct smb_version_operations smb21_operations; +extern struct smb_version_values smb21_values; +#define SMBDEFAULT_VERSION_STRING "default" +extern struct smb_version_values smbdefault_values; +#define SMB3ANY_VERSION_STRING "3" +extern struct smb_version_values smb3any_values; +#define SMB30_VERSION_STRING "3.0" +extern struct smb_version_operations smb30_operations; +extern struct smb_version_values smb30_values; +#define SMB302_VERSION_STRING "3.02" +#define ALT_SMB302_VERSION_STRING "3.0.2" +/*extern struct smb_version_operations smb302_operations;*/ /* not needed yet */ +extern struct smb_version_values smb302_values; +#define SMB311_VERSION_STRING "3.1.1" +#define ALT_SMB311_VERSION_STRING "3.11" +extern struct smb_version_operations smb311_operations; +extern struct smb_version_values smb311_values; + +static inline char *get_security_type_str(enum securityEnum sectype) +{ + switch (sectype) { + case RawNTLMSSP: + return "RawNTLMSSP"; + case Kerberos: + return "Kerberos"; + case NTLMv2: + return "NTLMv2"; + default: + return "Unknown"; + } +} + +static inline bool is_smb1_server(struct TCP_Server_Info *server) +{ + return strcmp(server->vals->version_string, SMB1_VERSION_STRING) == 0; +} + +static inline bool is_tcon_dfs(struct cifs_tcon *tcon) +{ + /* + * For SMB1, see MS-CIFS 2.4.55 SMB_COM_TREE_CONNECT_ANDX (0x75) and MS-CIFS 3.3.4.4 DFS + * Subsystem Notifies That a Share Is a DFS Share. + * + * For SMB2+, see MS-SMB2 2.2.10 SMB2 TREE_CONNECT Response and MS-SMB2 3.3.4.14 Server + * Application Updates a Share. + */ + if (!tcon || !tcon->ses || !tcon->ses->server) + return false; + return is_smb1_server(tcon->ses->server) ? tcon->Flags & SMB_SHARE_IS_IN_DFS : + tcon->share_flags & (SHI1005_FLAGS_DFS | SHI1005_FLAGS_DFS_ROOT); +} + +static inline bool cifs_is_referral_server(struct cifs_tcon *tcon, + const struct dfs_info3_param *ref) +{ + /* + * Check if all targets are capable of handling DFS referrals as per + * MS-DFSC 2.2.4 RESP_GET_DFS_REFERRAL. + */ + return is_tcon_dfs(tcon) || (ref && (ref->flags & DFSREF_REFERRAL_SERVER)); +} + +static inline u64 cifs_flock_len(const struct file_lock *fl) +{ + return (u64)fl->fl_end - fl->fl_start + 1; +} + +static inline size_t ntlmssp_workstation_name_size(const struct cifs_ses *ses) +{ + if (WARN_ON_ONCE(!ses || !ses->server)) + return 0; + /* + * Make workstation name no more than 15 chars when using insecure dialects as some legacy + * servers do require it during NTLMSSP. + */ + if (ses->server->dialect <= SMB20_PROT_ID) + return min_t(size_t, sizeof(ses->workstation_name), RFC1001_NAME_LEN_WITH_NULL); + return sizeof(ses->workstation_name); +} + +static inline void move_cifs_info_to_smb2(struct smb2_file_all_info *dst, const FILE_ALL_INFO *src) +{ + memcpy(dst, src, (size_t)((u8 *)&src->AccessFlags - (u8 *)src)); + dst->AccessFlags = src->AccessFlags; + dst->CurrentByteOffset = src->CurrentByteOffset; + dst->Mode = src->Mode; + dst->AlignmentRequirement = src->AlignmentRequirement; + dst->FileNameLength = src->FileNameLength; +} + +static inline unsigned int cifs_get_num_sgs(const struct smb_rqst *rqst, + int num_rqst, + const u8 *sig) +{ + unsigned int len, skip; + unsigned int nents = 0; + unsigned long addr; + int i, j; + + /* Assumes the first rqst has a transform header as the first iov. + * I.e. + * rqst[0].rq_iov[0] is transform header + * rqst[0].rq_iov[1+] data to be encrypted/decrypted + * rqst[1+].rq_iov[0+] data to be encrypted/decrypted + */ + for (i = 0; i < num_rqst; i++) { + /* + * The first rqst has a transform header where the + * first 20 bytes are not part of the encrypted blob. + */ + for (j = 0; j < rqst[i].rq_nvec; j++) { + struct kvec *iov = &rqst[i].rq_iov[j]; + + skip = (i == 0) && (j == 0) ? 20 : 0; + addr = (unsigned long)iov->iov_base + skip; + if (unlikely(is_vmalloc_addr((void *)addr))) { + len = iov->iov_len - skip; + nents += DIV_ROUND_UP(offset_in_page(addr) + len, + PAGE_SIZE); + } else { + nents++; + } + } + nents += rqst[i].rq_npages; + } + nents += DIV_ROUND_UP(offset_in_page(sig) + SMB2_SIGNATURE_SIZE, PAGE_SIZE); + return nents; +} + +/* We can not use the normal sg_set_buf() as we will sometimes pass a + * stack object as buf. + */ +static inline struct scatterlist *cifs_sg_set_buf(struct scatterlist *sg, + const void *buf, + unsigned int buflen) +{ + unsigned long addr = (unsigned long)buf; + unsigned int off = offset_in_page(addr); + + addr &= PAGE_MASK; + if (unlikely(is_vmalloc_addr((void *)addr))) { + do { + unsigned int len = min_t(unsigned int, buflen, PAGE_SIZE - off); + + sg_set_page(sg++, vmalloc_to_page((void *)addr), len, off); + + off = 0; + addr += PAGE_SIZE; + buflen -= len; + } while (buflen); + } else { + sg_set_page(sg++, virt_to_page(addr), buflen, off); + } + return sg; +} + +#endif /* _CIFS_GLOB_H */ diff --git a/fs/smb/client/cifspdu.h b/fs/smb/client/cifspdu.h new file mode 100644 index 0000000000000..cc458b98441c7 --- /dev/null +++ b/fs/smb/client/cifspdu.h @@ -0,0 +1,2730 @@ +/* SPDX-License-Identifier: LGPL-2.1 */ +/* + * + * Copyright (c) International Business Machines Corp., 2002,2009 + * Author(s): Steve French (sfrench@us.ibm.com) + * + */ + +#ifndef _CIFSPDU_H +#define _CIFSPDU_H + +#include +#include +#include "../common/smbfsctl.h" + +#define CIFS_PROT 0 +#define POSIX_PROT (CIFS_PROT+1) +#define BAD_PROT 0xFFFF + +/* SMB command codes: + * Note some commands have minimal (wct=0,bcc=0), or uninteresting, responses + * (ie which include no useful data other than the SMB error code itself). + * This can allow us to avoid response buffer allocations and copy in some cases + */ +#define SMB_COM_CREATE_DIRECTORY 0x00 /* trivial response */ +#define SMB_COM_DELETE_DIRECTORY 0x01 /* trivial response */ +#define SMB_COM_CLOSE 0x04 /* triv req/rsp, timestamp ignored */ +#define SMB_COM_FLUSH 0x05 /* triv req/rsp */ +#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_ECHO 0x2B /* echo request */ +#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 +#define SMB_COM_TRANSACTION2_SECONDARY 0x33 +#define SMB_COM_FIND_CLOSE2 0x34 /* trivial response */ +#define SMB_COM_TREE_DISCONNECT 0x71 /* trivial response */ +#define SMB_COM_NEGOTIATE 0x72 +#define SMB_COM_SESSION_SETUP_ANDX 0x73 +#define SMB_COM_LOGOFF_ANDX 0x74 /* trivial response */ +#define SMB_COM_TREE_CONNECT_ANDX 0x75 +#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 */ +#define TRANS2_OPEN 0x00 +#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 +#define TRANS2_SET_FILE_INFORMATION 0x08 +#define TRANS2_GET_DFS_REFERRAL 0x10 +#define TRANS2_REPORT_DFS_INCOSISTENCY 0x11 + +/* SMB Transact (Named Pipe) subcommand codes */ +#define TRANS_SET_NMPIPE_STATE 0x0001 +#define TRANS_RAW_READ_NMPIPE 0x0011 +#define TRANS_QUERY_NMPIPE_STATE 0x0021 +#define TRANS_QUERY_NMPIPE_INFO 0x0022 +#define TRANS_PEEK_NMPIPE 0x0023 +#define TRANS_TRANSACT_NMPIPE 0x0026 +#define TRANS_RAW_WRITE_NMPIPE 0x0031 +#define TRANS_READ_NMPIPE 0x0036 +#define TRANS_WRITE_NMPIPE 0x0037 +#define TRANS_WAIT_NMPIPE 0x0053 +#define TRANS_CALL_NMPIPE 0x0054 + +/* NT Transact subcommand codes */ +#define NT_TRANSACT_CREATE 0x01 +#define NT_TRANSACT_IOCTL 0x02 +#define NT_TRANSACT_SET_SECURITY_DESC 0x03 +#define NT_TRANSACT_NOTIFY_CHANGE 0x04 +#define NT_TRANSACT_RENAME 0x05 +#define NT_TRANSACT_QUERY_SECURITY_DESC 0x06 +#define NT_TRANSACT_GET_USER_QUOTA 0x07 +#define NT_TRANSACT_SET_USER_QUOTA 0x08 + +#define MAX_CIFS_SMALL_BUFFER_SIZE 448 /* big enough for most */ +/* future chained NTCreateXReadX bigger, but for time being NTCreateX biggest */ +/* among the requests (NTCreateX response is bigger with wct of 34) */ +#define MAX_CIFS_HDR_SIZE 0x58 /* 4 len + 32 hdr + (2*24 wct) + 2 bct + 2 pad */ +#define CIFS_SMALL_PATH 120 /* allows for (448-88)/3 */ + +/* internal cifs vfs structures */ +/***************************************************************** + * All constants go here + ***************************************************************** + */ + +/* + * Starting value for maximum SMB size negotiation + */ +#define CIFS_MAX_MSGSIZE (4*4096) + +/* + * Size of encrypted user password in bytes + */ +#define CIFS_ENCPWD_SIZE (16) + +/* + * Size of the crypto key returned on the negotiate SMB in bytes + */ +#define CIFS_CRYPTO_KEY_SIZE (8) + +/* + * Size of the ntlm client response + */ +#define CIFS_AUTH_RESP_SIZE (24) + +/* + * Size of the session key (crypto key encrypted with the password + */ +#define CIFS_SESS_KEY_SIZE (16) + +#define CIFS_SERVER_CHALLENGE_SIZE (8) +#define CIFS_HMAC_MD5_HASH_SIZE (16) +#define CIFS_CPHTXT_SIZE (16) +#define CIFS_NTHASH_SIZE (16) + +/* + * Maximum user name length + */ +#define CIFS_UNLEN (20) + +/* + * Flags on SMB open + */ +#define SMBOPEN_WRITE_THROUGH 0x4000 +#define SMBOPEN_DENY_ALL 0x0010 +#define SMBOPEN_DENY_WRITE 0x0020 +#define SMBOPEN_DENY_READ 0x0030 +#define SMBOPEN_DENY_NONE 0x0040 +#define SMBOPEN_READ 0x0000 +#define SMBOPEN_WRITE 0x0001 +#define SMBOPEN_READWRITE 0x0002 +#define SMBOPEN_EXECUTE 0x0003 + +#define SMBOPEN_OCREATE 0x0010 +#define SMBOPEN_OTRUNC 0x0002 +#define SMBOPEN_OAPPEND 0x0001 + +/* + * SMB flag definitions + */ +#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 request) */ +#define SMBFLG_CANONICAL_PATH_FORMAT 0x10 /* obsolete */ +#define SMBFLG_OLD_OPLOCK 0x20 /* obsolete */ +#define SMBFLG_OLD_OPLOCK_NOTIFY 0x40 /* obsolete */ +#define SMBFLG_RESPONSE 0x80 /* this PDU is a response from server */ + +/* + * 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_EAS cpu_to_le16(2) +#define SMBFLG2_SECURITY_SIGNATURE cpu_to_le16(4) +#define SMBFLG2_COMPRESSED (8) +#define SMBFLG2_SECURITY_SIGNATURE_REQUIRED (0x10) +#define SMBFLG2_IS_LONG_NAME cpu_to_le16(0x40) +#define SMBFLG2_REPARSE_PATH (0x400) +#define SMBFLG2_EXT_SEC cpu_to_le16(0x800) +#define SMBFLG2_DFS cpu_to_le16(0x1000) +#define SMBFLG2_PAGING_IO cpu_to_le16(0x2000) +#define SMBFLG2_ERR_STATUS cpu_to_le16(0x4000) +#define SMBFLG2_UNICODE cpu_to_le16(0x8000) + +/* + * These are the file access permission bits defined in CIFS for the + * NTCreateAndX as well as the level 0x107 + * TRANS2_QUERY_PATH_INFORMATION API. The level 0x107, SMB_QUERY_FILE_ALL_INFO + * responds with the AccessFlags. + * The AccessFlags specifies the access permissions a caller has to the + * 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_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 SYSTEM_SECURITY 0x01000000 /* The system access control list */ + /* can be read and changed */ +#define GENERIC_ALL 0x10000000 +#define GENERIC_EXECUTE 0x20000000 +#define GENERIC_WRITE 0x40000000 +#define GENERIC_READ 0x80000000 + /* In summary - Relevant file */ + /* access flags from CIFS are */ + /* file_read_data, file_write_data */ + /* file_execute, file_read_attributes*/ + /* write_dac, and delete. */ + +#define FILE_READ_RIGHTS (FILE_READ_DATA | FILE_READ_EA | FILE_READ_ATTRIBUTES) +#define FILE_WRITE_RIGHTS (FILE_WRITE_DATA | FILE_APPEND_DATA \ + | FILE_WRITE_EA | FILE_WRITE_ATTRIBUTES) +#define FILE_EXEC_RIGHTS (FILE_EXECUTE) + +#define SET_FILE_READ_RIGHTS (FILE_READ_DATA | FILE_READ_EA | FILE_WRITE_EA \ + | FILE_READ_ATTRIBUTES \ + | FILE_WRITE_ATTRIBUTES \ + | DELETE | READ_CONTROL | WRITE_DAC \ + | WRITE_OWNER | SYNCHRONIZE) +#define SET_FILE_WRITE_RIGHTS (FILE_WRITE_DATA | FILE_APPEND_DATA \ + | FILE_READ_EA | FILE_WRITE_EA \ + | FILE_READ_ATTRIBUTES \ + | FILE_WRITE_ATTRIBUTES \ + | DELETE | READ_CONTROL | WRITE_DAC \ + | WRITE_OWNER | SYNCHRONIZE) +#define SET_FILE_EXEC_RIGHTS (FILE_READ_EA | FILE_WRITE_EA | FILE_EXECUTE \ + | FILE_READ_ATTRIBUTES \ + | FILE_WRITE_ATTRIBUTES \ + | DELETE | READ_CONTROL | WRITE_DAC \ + | WRITE_OWNER | SYNCHRONIZE) + +#define SET_MINIMUM_RIGHTS (FILE_READ_EA | FILE_READ_ATTRIBUTES \ + | READ_CONTROL | SYNCHRONIZE) + + +/* + * Invalid readdir handle + */ +#define CIFS_NO_HANDLE 0xFFFF + +#define NO_CHANGE_64 0xFFFFFFFFFFFFFFFFULL + +/* IPC$ in ASCII */ +#define CIFS_IPC_RESOURCE "\x49\x50\x43\x24" + +/* IPC$ in Unicode */ +#define CIFS_IPC_UNICODE_RESOURCE "\x00\x49\x00\x50\x00\x43\x00\x24\x00\x00" + +/* Unicode Null terminate 2 bytes of 0 */ +#define UNICODE_NULL "\x00\x00" +#define ASCII_NULL 0x00 + +/* + * Server type values (returned on EnumServer API + */ +#define CIFS_SV_TYPE_DC 0x00000008 +#define CIFS_SV_TYPE_BACKDC 0x00000010 + +/* + * Alias type flags (From EnumAlias API call + */ +#define CIFS_ALIAS_TYPE_FILE 0x0001 +#define CIFS_SHARE_TYPE_FILE 0x0000 + +/* + * File Attribute flags + */ +#define ATTR_READONLY 0x0001 +#define ATTR_HIDDEN 0x0002 +#define ATTR_SYSTEM 0x0004 +#define ATTR_VOLUME 0x0008 +#define ATTR_DIRECTORY 0x0010 +#define ATTR_ARCHIVE 0x0020 +#define ATTR_DEVICE 0x0040 +#define ATTR_NORMAL 0x0080 +#define ATTR_TEMPORARY 0x0100 +#define ATTR_SPARSE 0x0200 +#define ATTR_REPARSE 0x0400 +#define ATTR_COMPRESSED 0x0800 +#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 +#define ATTR_BACKUP_SEMANTICS 0x02000000 +#define ATTR_DELETE_ON_CLOSE 0x04000000 +#define ATTR_SEQUENTIAL_SCAN 0x08000000 +#define ATTR_RANDOM_ACCESS 0x10000000 +#define ATTR_NO_BUFFERING 0x20000000 +#define ATTR_WRITE_THROUGH 0x80000000 + +/* ShareAccess flags */ +#define FILE_NO_SHARE 0x00000000 +#define FILE_SHARE_READ 0x00000001 +#define FILE_SHARE_WRITE 0x00000002 +#define FILE_SHARE_DELETE 0x00000004 +#define FILE_SHARE_ALL 0x00000007 + +/* CreateDisposition flags, similar to CreateAction as well */ +#define FILE_SUPERSEDE 0x00000000 +#define FILE_OPEN 0x00000001 +#define FILE_CREATE 0x00000002 +#define FILE_OPEN_IF 0x00000003 +#define FILE_OVERWRITE 0x00000004 +#define FILE_OVERWRITE_IF 0x00000005 + +/* CreateOptions */ +#define CREATE_NOT_FILE 0x00000001 /* if set must not be file */ +#define CREATE_WRITE_THROUGH 0x00000002 +#define CREATE_SEQUENTIAL 0x00000004 +#define CREATE_NO_BUFFER 0x00000008 /* should not buffer on srv */ +#define CREATE_SYNC_ALERT 0x00000010 /* MBZ */ +#define CREATE_ASYNC_ALERT 0x00000020 /* MBZ */ +#define CREATE_NOT_DIR 0x00000040 /* if set must not be directory */ +#define CREATE_TREE_CONNECTION 0x00000080 /* should be zero */ +#define CREATE_COMPLETE_IF_OPLK 0x00000100 /* should be zero */ +#define CREATE_NO_EA_KNOWLEDGE 0x00000200 +#define CREATE_EIGHT_DOT_THREE 0x00000400 /* doc says this is obsolete + "open for recovery" flag should + be zero in any case */ +#define CREATE_OPEN_FOR_RECOVERY 0x00000400 +#define CREATE_RANDOM_ACCESS 0x00000800 +#define CREATE_DELETE_ON_CLOSE 0x00001000 +#define CREATE_OPEN_BY_ID 0x00002000 +#define CREATE_OPEN_BACKUP_INTENT 0x00004000 +#define CREATE_NO_COMPRESSION 0x00008000 +#define CREATE_RESERVE_OPFILTER 0x00100000 /* should be zero */ +#define OPEN_REPARSE_POINT 0x00200000 +#define OPEN_NO_RECALL 0x00400000 +#define OPEN_FREE_SPACE_QUERY 0x00800000 /* should be zero */ +#define CREATE_OPTIONS_MASK 0x007FFFFF +#define CREATE_OPTION_READONLY 0x10000000 +#define CREATE_OPTION_SPECIAL 0x20000000 /* system. NB not sent over wire */ + +/* ImpersonationLevel flags */ +#define SECURITY_ANONYMOUS 0 +#define SECURITY_IDENTIFICATION 1 +#define SECURITY_IMPERSONATION 2 +#define SECURITY_DELEGATION 3 + +/* SecurityFlags */ +#define SECURITY_CONTEXT_TRACKING 0x01 +#define SECURITY_EFFECTIVE_ONLY 0x02 + +/* + * Default PID value, used in all SMBs where the PID is not important + */ +#define CIFS_DFT_PID 0x1234 + +/* + * We use the same routine for Copy and Move SMBs. This flag is used to + * distinguish + */ +#define CIFS_COPY_OP 1 +#define CIFS_RENAME_OP 2 + +#define GETU16(var) (*((__u16 *)var)) /* BB check for endian issues */ +#define GETU32(var) (*((__u32 *)var)) /* BB check for endian issues */ + +struct smb_hdr { + __be32 smb_buf_length; /* BB length is only two (rarely three) bytes, + with one or two byte "type" preceding it that will be + zero - we could mask the type byte off */ + __u8 Protocol[4]; + __u8 Command; + union { + struct { + __u8 ErrorClass; + __u8 Reserved; + __le16 Error; + } __attribute__((packed)) DosError; + __le32 CifsError; + } __attribute__((packed)) Status; + __u8 Flags; + __le16 Flags2; /* note: le */ + __le16 PidHigh; + union { + struct { + __le32 SequenceNumber; /* le */ + __u32 Reserved; /* zero */ + } __attribute__((packed)) Sequence; + __u8 SecuritySignature[8]; /* le */ + } __attribute__((packed)) Signature; + __u8 pad[2]; + __u16 Tid; + __le16 Pid; + __u16 Uid; + __le16 Mid; + __u8 WordCount; +} __attribute__((packed)); + +/* given a pointer to an smb_hdr, retrieve a void pointer to the ByteCount */ +static inline void * +BCC(struct smb_hdr *smb) +{ + return (void *)smb + sizeof(*smb) + 2 * smb->WordCount; +} + +/* given a pointer to an smb_hdr retrieve the pointer to the byte area */ +#define pByteArea(smb_var) (BCC(smb_var) + 2) + +/* get the unconverted ByteCount for a SMB packet and return it */ +static inline __u16 +get_bcc(struct smb_hdr *hdr) +{ + __le16 *bc_ptr = (__le16 *)BCC(hdr); + + return get_unaligned_le16(bc_ptr); +} + +/* set the ByteCount for a SMB packet in little-endian */ +static inline void +put_bcc(__u16 count, struct smb_hdr *hdr) +{ + __le16 *bc_ptr = (__le16 *)BCC(hdr); + + put_unaligned_le16(count, bc_ptr); +} + +/* + * Computer Name Length (since Netbios name was length 16 with last byte 0x20) + * No longer as important, now that TCP names are more commonly used to + * resolve hosts. + */ +#define CNLEN 15 + +/* + * Share Name Length (SNLEN) + * Note: This length was limited by the SMB used to get + * the Share info. NetShareEnum only returned 13 + * chars, including the null termination. + * This was removed because it no longer is limiting. + */ + +/* + * Comment Length + */ +#define MAXCOMMENTLEN 40 + +/* + * The OS/2 maximum path name + */ +#define MAX_PATHCONF 256 + +/* + * SMB frame definitions (following must be packed structs) + * See the SNIA CIFS Specification for details. + * + * The Naming convention is the lower case version of the + * smb command code name for the struct and this is typedef to the + * uppercase version of the same name with the prefix SMB_ removed + * for brevity. Although typedefs are not commonly used for + * structure definitions in the Linux kernel, their use in the + * CIFS standards document, which this code is based on, may + * make this one of the cases where typedefs for structures make + * sense to improve readability for readers of the standards doc. + * Typedefs can always be removed later if they are too distracting + * and they are only used for the CIFSs PDUs themselves, not + * internal cifs vfs structures + * + */ + +typedef struct negotiate_req { + struct smb_hdr hdr; /* wct = 0 */ + __le16 ByteCount; + unsigned char DialectsArray[]; +} __attribute__((packed)) NEGOTIATE_REQ; + +#define MIN_TZ_ADJ (15 * 60) /* minimum grid for timezones in seconds */ + +#define READ_RAW_ENABLE 1 +#define WRITE_RAW_ENABLE 2 +#define RAW_ENABLE (READ_RAW_ENABLE | WRITE_RAW_ENABLE) +#define SMB1_CLIENT_GUID_SIZE (16) +typedef struct negotiate_rsp { + struct smb_hdr hdr; /* wct = 17 */ + __le16 DialectIndex; /* 0xFFFF = no dialect acceptable */ + __u8 SecurityMode; + __le16 MaxMpxCount; + __le16 MaxNumberVcs; + __le32 MaxBufferSize; + __le32 MaxRawSize; + __le32 SessionKey; + __le32 Capabilities; /* see below */ + __le32 SystemTimeLow; + __le32 SystemTimeHigh; + __le16 ServerTimeZone; + __u8 EncryptionKeyLength; + __u16 ByteCount; + union { + /* cap extended security off */ + DECLARE_FLEX_ARRAY(unsigned char, EncryptionKey); + /* followed by Domain name - if extended security is off */ + /* followed by 16 bytes of server GUID */ + /* then security blob if cap_extended_security negotiated */ + struct { + unsigned char GUID[SMB1_CLIENT_GUID_SIZE]; + unsigned char SecurityBlob[]; + } __attribute__((packed)) extended_response; + } __attribute__((packed)) u; +} __attribute__((packed)) NEGOTIATE_RSP; + +/* SecurityMode bits */ +#define SECMODE_USER 0x01 /* off indicates share level security */ +#define SECMODE_PW_ENCRYPT 0x02 +#define SECMODE_SIGN_ENABLED 0x04 /* SMB security signatures enabled */ +#define SECMODE_SIGN_REQUIRED 0x08 /* SMB security signatures required */ + +/* Negotiate response Capabilities */ +#define CAP_RAW_MODE 0x00000001 +#define CAP_MPX_MODE 0x00000002 +#define CAP_UNICODE 0x00000004 +#define CAP_LARGE_FILES 0x00000008 +#define CAP_NT_SMBS 0x00000010 /* implies CAP_NT_FIND */ +#define CAP_RPC_REMOTE_APIS 0x00000020 +#define CAP_STATUS32 0x00000040 +#define CAP_LEVEL_II_OPLOCKS 0x00000080 +#define CAP_LOCK_AND_READ 0x00000100 +#define CAP_NT_FIND 0x00000200 +#define CAP_DFS 0x00001000 +#define CAP_INFOLEVEL_PASSTHRU 0x00002000 +#define CAP_LARGE_READ_X 0x00004000 +#define CAP_LARGE_WRITE_X 0x00008000 +#define CAP_LWIO 0x00010000 /* support fctl_srv_req_resume_key */ +#define CAP_UNIX 0x00800000 +#define CAP_COMPRESSED_DATA 0x02000000 +#define CAP_DYNAMIC_REAUTH 0x20000000 +#define CAP_PERSISTENT_HANDLES 0x40000000 +#define CAP_EXTENDED_SECURITY 0x80000000 + +typedef union smb_com_session_setup_andx { + struct { /* request format */ + struct smb_hdr hdr; /* wct = 12 */ + __u8 AndXCommand; + __u8 AndXReserved; + __le16 AndXOffset; + __le16 MaxBufferSize; + __le16 MaxMpxCount; + __le16 VcNumber; + __u32 SessionKey; + __le16 SecurityBlobLength; + __u32 Reserved; + __le32 Capabilities; /* see below */ + __le16 ByteCount; + unsigned char SecurityBlob[1]; /* followed by */ + /* STRING NativeOS */ + /* STRING NativeLanMan */ + } __attribute__((packed)) req; /* NTLM request format (with + extended security */ + + struct { /* request format */ + struct smb_hdr hdr; /* wct = 13 */ + __u8 AndXCommand; + __u8 AndXReserved; + __le16 AndXOffset; + __le16 MaxBufferSize; + __le16 MaxMpxCount; + __le16 VcNumber; + __u32 SessionKey; + __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 * CaseSensitivePassword; */ + /* STRING AccountName */ + /* STRING PrimaryDomain */ + /* STRING NativeOS */ + /* STRING NativeLanMan */ + } __attribute__((packed)) req_no_secext; /* NTLM request format (without + extended security */ + + struct { /* default (NTLM) response format */ + struct smb_hdr hdr; /* wct = 4 */ + __u8 AndXCommand; + __u8 AndXReserved; + __le16 AndXOffset; + __le16 Action; /* see below */ + __le16 SecurityBlobLength; + __u16 ByteCount; + unsigned char SecurityBlob[1]; /* followed by */ +/* unsigned char * NativeOS; */ +/* unsigned char * NativeLanMan; */ +/* unsigned char * PrimaryDomain; */ + } __attribute__((packed)) resp; /* NTLM response + (with or without extended sec) */ + + struct { /* request format */ + struct smb_hdr hdr; /* wct = 10 */ + __u8 AndXCommand; + __u8 AndXReserved; + __le16 AndXOffset; + __le16 MaxBufferSize; + __le16 MaxMpxCount; + __le16 VcNumber; + __u32 SessionKey; + __le16 PasswordLength; + __u32 Reserved; /* encrypt key len and offset */ + __le16 ByteCount; + unsigned char AccountPassword[1]; /* followed by */ + /* STRING AccountName */ + /* STRING PrimaryDomain */ + /* STRING NativeOS */ + /* STRING NativeLanMan */ + } __attribute__((packed)) old_req; /* pre-NTLM (LANMAN2.1) req format */ + + struct { /* default (NTLM) response format */ + struct smb_hdr hdr; /* wct = 3 */ + __u8 AndXCommand; + __u8 AndXReserved; + __le16 AndXOffset; + __le16 Action; /* see below */ + __u16 ByteCount; + unsigned char NativeOS[1]; /* followed by */ +/* unsigned char * NativeLanMan; */ +/* unsigned char * PrimaryDomain; */ + } __attribute__((packed)) old_resp; /* pre-NTLM (LANMAN2.1) response */ +} __attribute__((packed)) SESSION_SETUP_ANDX; + +/* format of NLTMv2 Response ie "case sensitive password" hash when NTLMv2 */ + +#define NTLMSSP_SERVER_TYPE 1 +#define NTLMSSP_DOMAIN_TYPE 2 +#define NTLMSSP_FQ_DOMAIN_TYPE 3 +#define NTLMSSP_DNS_DOMAIN_TYPE 4 +#define NTLMSSP_DNS_PARENT_TYPE 5 + +struct ntlmssp2_name { + __le16 type; + __le16 length; +/* char name[length]; */ +} __attribute__((packed)); + +struct ntlmv2_resp { + union { + char ntlmv2_hash[CIFS_ENCPWD_SIZE]; + struct { + __u8 reserved[8]; + __u8 key[CIFS_SERVER_CHALLENGE_SIZE]; + } __attribute__((packed)) challenge; + } __attribute__((packed)); + __le32 blob_signature; + __u32 reserved; + __le64 time; + __u64 client_chal; /* random */ + __u32 reserved2; + /* array of name entries could follow ending in minimum 4 byte struct */ +} __attribute__((packed)); + + +#define CIFS_NETWORK_OPSYS "CIFS VFS Client for Linux" + +/* Capabilities bits (for NTLM SessSetup request) */ +#define CAP_UNICODE 0x00000004 +#define CAP_LARGE_FILES 0x00000008 +#define CAP_NT_SMBS 0x00000010 +#define CAP_STATUS32 0x00000040 +#define CAP_LEVEL_II_OPLOCKS 0x00000080 +#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 + +/* Action bits */ +#define GUEST_LOGIN 1 + +typedef struct smb_com_tconx_req { + struct smb_hdr hdr; /* wct = 4 */ + __u8 AndXCommand; + __u8 AndXReserved; + __le16 AndXOffset; + __le16 Flags; /* see below */ + __le16 PasswordLength; + __le16 ByteCount; + unsigned char Password[1]; /* followed by */ +/* STRING Path *//* \\server\share name */ + /* STRING Service */ +} __attribute__((packed)) TCONX_REQ; + +typedef struct smb_com_tconx_rsp { + struct smb_hdr hdr; /* wct = 3 , not extended response */ + __u8 AndXCommand; + __u8 AndXReserved; + __le16 AndXOffset; + __le16 OptionalSupport; /* see below */ + __u16 ByteCount; + unsigned char Service[1]; /* always ASCII, not Unicode */ + /* STRING NativeFileSystem */ +} __attribute__((packed)) TCONX_RSP; + +typedef struct smb_com_tconx_rsp_ext { + struct smb_hdr hdr; /* wct = 7, extended response */ + __u8 AndXCommand; + __u8 AndXReserved; + __le16 AndXOffset; + __le16 OptionalSupport; /* see below */ + __le32 MaximalShareAccessRights; + __le32 GuestMaximalShareAccessRights; + __u16 ByteCount; + unsigned char Service[1]; /* always ASCII, not Unicode */ + /* STRING NativeFileSystem */ +} __attribute__((packed)) TCONX_RSP_EXT; + + +/* tree connect Flags */ +#define DISCONNECT_TID 0x0001 +#define TCON_EXTENDED_SIGNATURES 0x0004 +#define TCON_EXTENDED_SECINFO 0x0008 + +/* OptionalSupport bits */ +#define SMB_SUPPORT_SEARCH_BITS 0x0001 /* "must have" directory search bits + (exclusive searches supported) */ +#define SMB_SHARE_IS_IN_DFS 0x0002 +#define SMB_CSC_MASK 0x000C +/* CSC flags defined as follows */ +#define SMB_CSC_CACHE_MANUAL_REINT 0x0000 +#define SMB_CSC_CACHE_AUTO_REINT 0x0004 +#define SMB_CSC_CACHE_VDO 0x0008 +#define SMB_CSC_NO_CACHING 0x000C +#define SMB_UNIQUE_FILE_NAME 0x0010 +#define SMB_EXTENDED_SIGNATURES 0x0020 + +/* services + * + * A: ie disk + * LPT1: ie printer + * IPC ie named pipe + * COMM + * ????? ie any type + * + */ + +typedef struct smb_com_echo_req { + struct smb_hdr hdr; + __le16 EchoCount; + __le16 ByteCount; + char Data[1]; +} __attribute__((packed)) ECHO_REQ; + +typedef struct smb_com_echo_rsp { + struct smb_hdr hdr; + __le16 SequenceNumber; + __le16 ByteCount; + char Data[1]; +} __attribute__((packed)) ECHO_RSP; + +typedef struct smb_com_logoff_andx_req { + struct smb_hdr hdr; /* wct = 2 */ + __u8 AndXCommand; + __u8 AndXReserved; + __u16 AndXOffset; + __u16 ByteCount; +} __attribute__((packed)) LOGOFF_ANDX_REQ; + +typedef struct smb_com_logoff_andx_rsp { + struct smb_hdr hdr; /* wct = 2 */ + __u8 AndXCommand; + __u8 AndXReserved; + __u16 AndXOffset; + __u16 ByteCount; +} __attribute__((packed)) LOGOFF_ANDX_RSP; + +typedef union smb_com_tree_disconnect { /* as an altetnative can use flag on + tree_connect PDU to effect disconnect */ + /* tdis is probably simplest SMB PDU */ + struct { + struct smb_hdr hdr; /* wct = 0 */ + __u16 ByteCount; /* bcc = 0 */ + } __attribute__((packed)) req; + struct { + struct smb_hdr hdr; /* wct = 0 */ + __u16 ByteCount; /* bcc = 0 */ + } __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 or -1 */ + __u16 ByteCount; /* 0 */ +} __attribute__((packed)) CLOSE_REQ; + +typedef struct smb_com_close_rsp { + struct smb_hdr hdr; /* wct = 0 */ + __u16 ByteCount; /* bct = 0 */ +} __attribute__((packed)) CLOSE_RSP; + +typedef struct smb_com_flush_req { + struct smb_hdr hdr; /* wct = 1 */ + __u16 FileID; + __u16 ByteCount; /* 0 */ +} __attribute__((packed)) FLUSH_REQ; + +typedef struct smb_com_findclose_req { + struct smb_hdr hdr; /* wct = 1 */ + __u16 FileID; + __u16 ByteCount; /* 0 */ +} __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 +#define REQ_EXTENDED_INFO 0x00000010 + +/* File type */ +#define DISK_TYPE 0x0000 +#define BYTE_PIPE_TYPE 0x0001 +#define MESSAGE_PIPE_TYPE 0x0002 +#define PRINTER_TYPE 0x0003 +#define COMM_DEV_TYPE 0x0004 +#define UNKNOWN_TYPE 0xFFFF + +/* Device Type or File Status Flags */ +#define NO_EAS 0x0001 +#define NO_SUBSTREAMS 0x0002 +#define NO_REPARSETAG 0x0004 +/* following flags can apply if pipe */ +#define ICOUNT_MASK 0x00FF +#define PIPE_READ_MODE 0x0100 +#define NAMED_PIPE_TYPE 0x0400 +#define PIPE_END_POINT 0x4000 +#define BLOCKING_NAMED_PIPE 0x8000 + +typedef struct smb_com_open_req { /* also handles create */ + struct smb_hdr hdr; /* wct = 24 */ + __u8 AndXCommand; + __u8 AndXReserved; + __le16 AndXOffset; + __u8 Reserved; /* Must Be Zero */ + __le16 NameLength; + __le32 OpenFlags; + __u32 RootDirectoryFid; + __le32 DesiredAccess; + __le64 AllocationSize; + __le32 FileAttributes; + __le32 ShareAccess; + __le32 CreateDisposition; + __le32 CreateOptions; + __le32 ImpersonationLevel; + __u8 SecurityFlags; + __le16 ByteCount; + char fileName[1]; +} __attribute__((packed)) OPEN_REQ; + +/* open response: oplock levels */ +#define OPLOCK_NONE 0 +#define OPLOCK_EXCLUSIVE 1 +#define OPLOCK_BATCH 2 +#define OPLOCK_READ 3 /* level 2 oplock */ + +/* open response for CreateAction shifted left */ +#define CIFS_CREATE_ACTION 0x20000 /* file created */ + +typedef struct smb_com_open_rsp { + struct smb_hdr hdr; /* wct = 34 BB */ + __u8 AndXCommand; + __u8 AndXReserved; + __le16 AndXOffset; + __u8 OplockLevel; + __u16 Fid; + __le32 CreateAction; + __le64 CreationTime; + __le64 LastAccessTime; + __le64 LastWriteTime; + __le64 ChangeTime; + __le32 FileAttributes; + __le64 AllocationSize; + __le64 EndOfFile; + __le16 FileType; + __le16 DeviceState; + __u8 DirectoryFlag; + __u16 ByteCount; /* bct = 0 */ +} __attribute__((packed)) OPEN_RSP; + +typedef struct smb_com_open_rsp_ext { + struct smb_hdr hdr; /* wct = 42 but meaningless due to MS bug? */ + __u8 AndXCommand; + __u8 AndXReserved; + __le16 AndXOffset; + __u8 OplockLevel; + __u16 Fid; + __le32 CreateAction; + __le64 CreationTime; + __le64 LastAccessTime; + __le64 LastWriteTime; + __le64 ChangeTime; + __le32 FileAttributes; + __le64 AllocationSize; + __le64 EndOfFile; + __le16 FileType; + __le16 DeviceState; + __u8 DirectoryFlag; + __u8 VolumeGUID[16]; + __u64 FileId; /* note no endian conversion - is opaque UniqueID */ + __le32 MaximalAccessRights; + __le32 GuestMaximalAccessRights; + __u16 ByteCount; /* bct = 0 */ +} __attribute__((packed)) OPEN_RSP_EXT; + + +/* 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; + +/* For encoding of POSIX Open Request - see trans2 function 0x209 data struct */ + +/* 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[]; +} __attribute__((packed)) WRITEX_REQ; + +typedef struct smb_com_write_req { + struct smb_hdr hdr; /* wct = 14 */ + __u8 AndXCommand; + __u8 AndXReserved; + __le16 AndXOffset; + __u16 Fid; + __le32 OffsetLow; + __u32 Reserved; + __le16 WriteMode; + __le16 Remaining; + __le16 DataLengthHigh; + __le16 DataLengthLow; + __le16 DataOffset; + __le32 OffsetHigh; + __le16 ByteCount; + __u8 Pad; /* BB check for whether padded to DWORD + boundary and optimum performance here */ + char Data[]; +} __attribute__((packed)) WRITE_REQ; + +typedef struct smb_com_write_rsp { + struct smb_hdr hdr; /* wct = 6 */ + __u8 AndXCommand; + __u8 AndXReserved; + __le16 AndXOffset; + __le16 Count; + __le16 Remaining; + __le16 CountHigh; + __u16 Reserved; + __u16 ByteCount; +} __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 */ + __u8 AndXCommand; + __u8 AndXReserved; + __le16 AndXOffset; + __u16 Fid; + __le32 OffsetLow; + __le16 MaxCount; + __le16 MinCount; /* obsolete */ + __le32 MaxCountHigh; + __le16 Remaining; + __le32 OffsetHigh; + __le16 ByteCount; +} __attribute__((packed)) READ_REQ; + +typedef struct smb_com_read_rsp { + struct smb_hdr hdr; /* wct = 12 */ + __u8 AndXCommand; + __u8 AndXReserved; + __le16 AndXOffset; + __le16 Remaining; + __le16 DataCompactionMode; + __le16 Reserved; + __le16 DataLength; + __le16 DataOffset; + __le16 DataLengthHigh; + __u64 Reserved2; + __u16 ByteCount; + /* read response data immediately follows */ +} __attribute__((packed)) READ_RSP; + +typedef struct locking_andx_range { + __le16 Pid; + __le16 Pad; + __le32 OffsetHigh; + __le32 OffsetLow; + __le32 LengthHigh; + __le32 LengthLow; +} __attribute__((packed)) LOCKING_ANDX_RANGE; + +#define LOCKING_ANDX_SHARED_LOCK 0x01 +#define LOCKING_ANDX_OPLOCK_RELEASE 0x02 +#define LOCKING_ANDX_CHANGE_LOCKTYPE 0x04 +#define LOCKING_ANDX_CANCEL_LOCK 0x08 +#define LOCKING_ANDX_LARGE_FILES 0x10 /* always on for us */ + +typedef struct smb_com_lock_req { + struct smb_hdr hdr; /* wct = 8 */ + __u8 AndXCommand; + __u8 AndXReserved; + __le16 AndXOffset; + __u16 Fid; + __u8 LockType; + __u8 OplockLevel; + __le32 Timeout; + __le16 NumberOfUnlocks; + __le16 NumberOfLocks; + __le16 ByteCount; + LOCKING_ANDX_RANGE Locks[1]; +} __attribute__((packed)) LOCK_REQ; + +/* lock type */ +#define CIFS_RDLCK 0 +#define CIFS_WRLCK 1 +#define CIFS_UNLCK 2 +typedef struct cifs_posix_lock { + __le16 lock_type; /* 0 = Read, 1 = Write, 2 = Unlock */ + __le16 lock_flags; /* 1 = Wait (only valid for setlock) */ + __le32 pid; + __le64 start; + __le64 length; + /* BB what about additional owner info to identify network client */ +} __attribute__((packed)) CIFS_POSIX_LOCK; + +typedef struct smb_com_lock_rsp { + struct smb_hdr hdr; /* wct = 2 */ + __u8 AndXCommand; + __u8 AndXReserved; + __le16 AndXOffset; + __u16 ByteCount; +} __attribute__((packed)) LOCK_RSP; + +typedef struct smb_com_rename_req { + struct smb_hdr hdr; /* wct = 1 */ + __le16 SearchAttributes; /* target file attributes */ + __le16 ByteCount; + __u8 BufferFormat; /* 4 = ASCII or Unicode */ + unsigned char OldFileName[1]; + /* followed by __u8 BufferFormat2 */ + /* followed by NewFileName */ +} __attribute__((packed)) RENAME_REQ; + + /* copy request flags */ +#define COPY_MUST_BE_FILE 0x0001 +#define COPY_MUST_BE_DIR 0x0002 +#define COPY_TARGET_MODE_ASCII 0x0004 /* if not set, binary */ +#define COPY_SOURCE_MODE_ASCII 0x0008 /* if not set, binary */ +#define COPY_VERIFY_WRITES 0x0010 +#define COPY_TREE 0x0020 + +typedef struct smb_com_copy_req { + struct smb_hdr hdr; /* wct = 3 */ + __u16 Tid2; + __le16 OpenFunction; + __le16 Flags; + __le16 ByteCount; + __u8 BufferFormat; /* 4 = ASCII or Unicode */ + unsigned char OldFileName[1]; + /* followed by __u8 BufferFormat2 */ + /* followed by NewFileName string */ +} __attribute__((packed)) COPY_REQ; + +typedef struct smb_com_copy_rsp { + struct smb_hdr hdr; /* wct = 1 */ + __le16 CopyCount; /* number of files copied */ + __u16 ByteCount; /* may be zero */ + __u8 BufferFormat; /* 0x04 - only present if errored file follows */ + unsigned char ErrorFileName[1]; /* only present if error in copy */ +} __attribute__((packed)) COPY_RSP; + +#define CREATE_HARD_LINK 0x103 +#define MOVEFILE_COPY_ALLOWED 0x0002 +#define MOVEFILE_REPLACE_EXISTING 0x0001 + +typedef struct smb_com_nt_rename_req { /* A5 - also used for create hardlink */ + struct smb_hdr hdr; /* wct = 4 */ + __le16 SearchAttributes; /* target file attributes */ + __le16 Flags; /* spec says Information Level */ + __le32 ClusterCount; + __le16 ByteCount; + __u8 BufferFormat; /* 4 = ASCII or Unicode */ + unsigned char OldFileName[1]; + /* followed by __u8 BufferFormat2 */ + /* followed by NewFileName */ +} __attribute__((packed)) NT_RENAME_REQ; + +typedef struct smb_com_rename_rsp { + struct smb_hdr hdr; /* wct = 0 */ + __u16 ByteCount; /* bct = 0 */ +} __attribute__((packed)) RENAME_RSP; + +typedef struct smb_com_delete_file_req { + struct smb_hdr hdr; /* wct = 1 */ + __le16 SearchAttributes; + __le16 ByteCount; + __u8 BufferFormat; /* 4 = ASCII */ + unsigned char fileName[1]; +} __attribute__((packed)) DELETE_FILE_REQ; + +typedef struct smb_com_delete_file_rsp { + struct smb_hdr hdr; /* wct = 0 */ + __u16 ByteCount; /* bct = 0 */ +} __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]; +} __attribute__((packed)) DELETE_DIRECTORY_REQ; + +typedef struct smb_com_delete_directory_rsp { + struct smb_hdr hdr; /* wct = 0 */ + __u16 ByteCount; /* bct = 0 */ +} __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]; +} __attribute__((packed)) CREATE_DIRECTORY_REQ; + +typedef struct smb_com_create_directory_rsp { + struct smb_hdr hdr; /* wct = 0 */ + __u16 ByteCount; /* bct = 0 */ +} __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 */ + __le16 attr; + __le16 time_low; + __le16 time_high; + __le16 reserved[5]; /* must be zero */ + __u16 ByteCount; + __u8 BufferFormat; /* 4 = ASCII */ + unsigned char fileName[1]; +} __attribute__((packed)) SETATTR_REQ; + +typedef struct smb_com_setattr_rsp { + struct smb_hdr hdr; /* wct = 0 */ + __u16 ByteCount; /* bct = 0 */ +} __attribute__((packed)) SETATTR_RSP; + +/* empty wct response to setattr */ + +/*******************************************************/ +/* NT Transact structure definitions follow */ +/* Currently only ioctl, acl (get security descriptor) */ +/* and notify are implemented */ +/*******************************************************/ +typedef struct smb_com_ntransact_req { + struct smb_hdr hdr; /* wct >= 19 */ + __u8 MaxSetupCount; + __u16 Reserved; + __le32 TotalParameterCount; + __le32 TotalDataCount; + __le32 MaxParameterCount; + __le32 MaxDataCount; + __le32 ParameterCount; + __le32 ParameterOffset; + __le32 DataCount; + __le32 DataOffset; + __u8 SetupCount; /* four setup words follow subcommand */ + /* SNIA spec incorrectly included spurious pad here */ + __le16 SubCommand; /* 2 = IOCTL/FSCTL */ + /* SetupCount words follow then */ + __le16 ByteCount; + __u8 Pad[3]; + __u8 Parms[]; +} __attribute__((packed)) NTRANSACT_REQ; + +typedef struct smb_com_ntransact_rsp { + struct smb_hdr hdr; /* wct = 18 */ + __u8 Reserved[3]; + __le32 TotalParameterCount; + __le32 TotalDataCount; + __le32 ParameterCount; + __le32 ParameterOffset; + __le32 ParameterDisplacement; + __le32 DataCount; + __le32 DataOffset; + __le32 DataDisplacement; + __u8 SetupCount; /* 0 */ + __u16 ByteCount; + /* __u8 Pad[3]; */ + /* parms and data follow */ +} __attribute__((packed)) NTRANSACT_RSP; + +/* See MS-SMB 2.2.7.2.1.1 */ +struct srv_copychunk { + __le64 SourceOffset; + __le64 DestinationOffset; + __le32 CopyLength; + __u32 Reserved; +} __packed; + +typedef struct smb_com_transaction_ioctl_req { + struct smb_hdr hdr; /* wct = 23 */ + __u8 MaxSetupCount; + __u16 Reserved; + __le32 TotalParameterCount; + __le32 TotalDataCount; + __le32 MaxParameterCount; + __le32 MaxDataCount; + __le32 ParameterCount; + __le32 ParameterOffset; + __le32 DataCount; + __le32 DataOffset; + __u8 SetupCount; /* four setup words follow subcommand */ + /* SNIA spec incorrectly included spurious pad here */ + __le16 SubCommand; /* 2 = IOCTL/FSCTL */ + __le32 FunctionCode; + __u16 Fid; + __u8 IsFsctl; /* 1 = File System Control 0 = device control (IOCTL) */ + __u8 IsRootFlag; /* 1 = apply command to root of share (must be DFS) */ + __le16 ByteCount; + __u8 Pad[3]; + __u8 Data[1]; +} __attribute__((packed)) TRANSACT_IOCTL_REQ; + +typedef struct smb_com_transaction_compr_ioctl_req { + struct smb_hdr hdr; /* wct = 23 */ + __u8 MaxSetupCount; + __u16 Reserved; + __le32 TotalParameterCount; + __le32 TotalDataCount; + __le32 MaxParameterCount; + __le32 MaxDataCount; + __le32 ParameterCount; + __le32 ParameterOffset; + __le32 DataCount; + __le32 DataOffset; + __u8 SetupCount; /* four setup words follow subcommand */ + /* SNIA spec incorrectly included spurious pad here */ + __le16 SubCommand; /* 2 = IOCTL/FSCTL */ + __le32 FunctionCode; + __u16 Fid; + __u8 IsFsctl; /* 1 = File System Control 0 = device control (IOCTL) */ + __u8 IsRootFlag; /* 1 = apply command to root of share (must be DFS) */ + __le16 ByteCount; + __u8 Pad[3]; + __le16 compression_state; /* See below for valid flags */ +} __attribute__((packed)) TRANSACT_COMPR_IOCTL_REQ; + +/* compression state flags */ +#define COMPRESSION_FORMAT_NONE 0x0000 +#define COMPRESSION_FORMAT_DEFAULT 0x0001 +#define COMPRESSION_FORMAT_LZNT1 0x0002 + +typedef struct smb_com_transaction_ioctl_rsp { + struct smb_hdr hdr; /* wct = 19 */ + __u8 Reserved[3]; + __le32 TotalParameterCount; + __le32 TotalDataCount; + __le32 ParameterCount; + __le32 ParameterOffset; + __le32 ParameterDisplacement; + __le32 DataCount; + __le32 DataOffset; + __le32 DataDisplacement; + __u8 SetupCount; /* 1 */ + __le16 ReturnedDataLen; + __u16 ByteCount; +} __attribute__((packed)) TRANSACT_IOCTL_RSP; + +#define CIFS_ACL_OWNER 1 +#define CIFS_ACL_GROUP 2 +#define CIFS_ACL_DACL 4 +#define CIFS_ACL_SACL 8 + +typedef struct smb_com_transaction_qsec_req { + struct smb_hdr hdr; /* wct = 19 */ + __u8 MaxSetupCount; + __u16 Reserved; + __le32 TotalParameterCount; + __le32 TotalDataCount; + __le32 MaxParameterCount; + __le32 MaxDataCount; + __le32 ParameterCount; + __le32 ParameterOffset; + __le32 DataCount; + __le32 DataOffset; + __u8 SetupCount; /* no setup words follow subcommand */ + /* SNIA spec incorrectly included spurious pad here */ + __le16 SubCommand; /* 6 = QUERY_SECURITY_DESC */ + __le16 ByteCount; /* bcc = 3 + 8 */ + __u8 Pad[3]; + __u16 Fid; + __u16 Reserved2; + __le32 AclFlags; +} __attribute__((packed)) QUERY_SEC_DESC_REQ; + + +typedef struct smb_com_transaction_ssec_req { + struct smb_hdr hdr; /* wct = 19 */ + __u8 MaxSetupCount; + __u16 Reserved; + __le32 TotalParameterCount; + __le32 TotalDataCount; + __le32 MaxParameterCount; + __le32 MaxDataCount; + __le32 ParameterCount; + __le32 ParameterOffset; + __le32 DataCount; + __le32 DataOffset; + __u8 SetupCount; /* no setup words follow subcommand */ + /* SNIA spec incorrectly included spurious pad here */ + __le16 SubCommand; /* 3 = SET_SECURITY_DESC */ + __le16 ByteCount; /* bcc = 3 + 8 */ + __u8 Pad[3]; + __u16 Fid; + __u16 Reserved2; + __le32 AclFlags; +} __attribute__((packed)) SET_SEC_DESC_REQ; + +typedef struct smb_com_transaction_change_notify_req { + struct smb_hdr hdr; /* wct = 23 */ + __u8 MaxSetupCount; + __u16 Reserved; + __le32 TotalParameterCount; + __le32 TotalDataCount; + __le32 MaxParameterCount; + __le32 MaxDataCount; + __le32 ParameterCount; + __le32 ParameterOffset; + __le32 DataCount; + __le32 DataOffset; + __u8 SetupCount; /* four setup words follow subcommand */ + /* SNIA spec incorrectly included spurious pad here */ + __le16 SubCommand;/* 4 = Change Notify */ + __le32 CompletionFilter; /* operation to monitor */ + __u16 Fid; + __u8 WatchTree; /* 1 = Monitor subdirectories */ + __u8 Reserved2; + __le16 ByteCount; +/* __u8 Pad[3];*/ +/* __u8 Data[1];*/ +} __attribute__((packed)) TRANSACT_CHANGE_NOTIFY_REQ; + +/* BB eventually change to use generic ntransact rsp struct + and validation routine */ +typedef struct smb_com_transaction_change_notify_rsp { + struct smb_hdr hdr; /* wct = 18 */ + __u8 Reserved[3]; + __le32 TotalParameterCount; + __le32 TotalDataCount; + __le32 ParameterCount; + __le32 ParameterOffset; + __le32 ParameterDisplacement; + __le32 DataCount; + __le32 DataOffset; + __le32 DataDisplacement; + __u8 SetupCount; /* 0 */ + __u16 ByteCount; + /* __u8 Pad[3]; */ +} __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 +#define FILE_NOTIFY_CHANGE_NAME 0x00000003 +#define FILE_NOTIFY_CHANGE_ATTRIBUTES 0x00000004 +#define FILE_NOTIFY_CHANGE_SIZE 0x00000008 +#define FILE_NOTIFY_CHANGE_LAST_WRITE 0x00000010 +#define FILE_NOTIFY_CHANGE_LAST_ACCESS 0x00000020 +#define FILE_NOTIFY_CHANGE_CREATION 0x00000040 +#define FILE_NOTIFY_CHANGE_EA 0x00000080 +#define FILE_NOTIFY_CHANGE_SECURITY 0x00000100 +#define FILE_NOTIFY_CHANGE_STREAM_NAME 0x00000200 +#define FILE_NOTIFY_CHANGE_STREAM_SIZE 0x00000400 +#define FILE_NOTIFY_CHANGE_STREAM_WRITE 0x00000800 + +#define FILE_ACTION_ADDED 0x00000001 +#define FILE_ACTION_REMOVED 0x00000002 +#define FILE_ACTION_MODIFIED 0x00000003 +#define FILE_ACTION_RENAMED_OLD_NAME 0x00000004 +#define FILE_ACTION_RENAMED_NEW_NAME 0x00000005 +#define FILE_ACTION_ADDED_STREAM 0x00000006 +#define FILE_ACTION_REMOVED_STREAM 0x00000007 +#define FILE_ACTION_MODIFIED_STREAM 0x00000008 + +/* response contains array of the following structures */ +struct file_notify_information { + __le32 NextEntryOffset; + __le32 Action; + __le32 FileNameLength; + __u8 FileName[]; +} __attribute__((packed)); + +/* For IO_REPARSE_TAG_SYMLINK */ +struct reparse_symlink_data { + __le32 ReparseTag; + __le16 ReparseDataLength; + __u16 Reserved; + __le16 SubstituteNameOffset; + __le16 SubstituteNameLength; + __le16 PrintNameOffset; + __le16 PrintNameLength; + __le32 Flags; + char PathBuffer[]; +} __attribute__((packed)); + +/* Flag above */ +#define SYMLINK_FLAG_RELATIVE 0x00000001 + +/* For IO_REPARSE_TAG_NFS */ +#define NFS_SPECFILE_LNK 0x00000000014B4E4C +#define NFS_SPECFILE_CHR 0x0000000000524843 +#define NFS_SPECFILE_BLK 0x00000000004B4C42 +#define NFS_SPECFILE_FIFO 0x000000004F464946 +#define NFS_SPECFILE_SOCK 0x000000004B434F53 +struct reparse_posix_data { + __le32 ReparseTag; + __le16 ReparseDataLength; + __u16 Reserved; + __le64 InodeType; /* LNK, FIFO, CHR etc. */ + char PathBuffer[]; +} __attribute__((packed)); + +struct cifs_quota_data { + __u32 rsrvd1; /* 0 */ + __u32 sid_size; + __u64 rsrvd2; /* 0 */ + __u64 space_used; + __u64 soft_limit; + __u64 hard_limit; + char sid[1]; /* variable size? */ +} __attribute__((packed)); + +/* quota sub commands */ +#define QUOTA_LIST_CONTINUE 0 +#define QUOTA_LIST_START 0x100 +#define QUOTA_FOR_SID 0x101 + +struct trans2_req { + /* struct smb_hdr hdr precedes. Set wct = 14+ */ + __le16 TotalParameterCount; + __le16 TotalDataCount; + __le16 MaxParameterCount; + __le16 MaxDataCount; + __u8 MaxSetupCount; + __u8 Reserved; + __le16 Flags; + __le32 Timeout; + __u16 Reserved2; + __le16 ParameterCount; + __le16 ParameterOffset; + __le16 DataCount; + __le16 DataOffset; + __u8 SetupCount; + __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 */ + __le16 TotalParameterCount; + __le16 TotalDataCount; + __u16 Reserved; + __le16 ParameterCount; + __le16 ParameterOffset; + __le16 ParameterDisplacement; + __le16 DataCount; + __le16 DataOffset; + __le16 DataDisplacement; + __u8 SetupCount; + __u8 Reserved1; + /* SetupWords[SetupCount]; + __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 +#define SMB_SET_FILE_EA 2 +#define SMB_QUERY_FILE_EA_SIZE 2 +#define SMB_INFO_QUERY_EAS_FROM_LIST 3 +#define SMB_INFO_QUERY_ALL_EAS 4 +#define SMB_INFO_IS_NAME_VALID 6 +#define SMB_QUERY_FILE_BASIC_INFO 0x101 +#define SMB_QUERY_FILE_STANDARD_INFO 0x102 +#define SMB_QUERY_FILE_EA_INFO 0x103 +#define SMB_QUERY_FILE_NAME_INFO 0x104 +#define SMB_QUERY_FILE_ALLOCATION_INFO 0x105 +#define SMB_QUERY_FILE_END_OF_FILEINFO 0x106 +#define SMB_QUERY_FILE_ALL_INFO 0x107 +#define SMB_QUERY_ALT_NAME_INFO 0x108 +#define SMB_QUERY_FILE_STREAM_INFO 0x109 +#define SMB_QUERY_FILE_COMPRESSION_INFO 0x10B +#define SMB_QUERY_FILE_UNIX_BASIC 0x200 +#define SMB_QUERY_FILE_UNIX_LINK 0x201 +#define SMB_QUERY_POSIX_ACL 0x204 +#define SMB_QUERY_XATTR 0x205 /* e.g. system EA name space */ +#define SMB_QUERY_ATTR_FLAGS 0x206 /* append,immutable etc. */ +#define SMB_QUERY_POSIX_PERMISSION 0x207 +#define SMB_QUERY_POSIX_LOCK 0x208 +/* #define SMB_POSIX_OPEN 0x209 */ +/* #define SMB_POSIX_UNLINK 0x20a */ +#define SMB_QUERY_FILE__UNIX_INFO2 0x20b +#define SMB_QUERY_FILE_INTERNAL_INFO 0x3ee +#define SMB_QUERY_FILE_ACCESS_INFO 0x3f0 +#define SMB_QUERY_FILE_NAME_INFO2 0x3f1 /* 0x30 bytes */ +#define SMB_QUERY_FILE_POSITION_INFO 0x3f6 +#define SMB_QUERY_FILE_MODE_INFO 0x3f8 +#define SMB_QUERY_FILE_ALGN_INFO 0x3f9 + + +#define SMB_SET_FILE_BASIC_INFO 0x101 +#define SMB_SET_FILE_DISPOSITION_INFO 0x102 +#define SMB_SET_FILE_ALLOCATION_INFO 0x103 +#define SMB_SET_FILE_END_OF_FILE_INFO 0x104 +#define SMB_SET_FILE_UNIX_BASIC 0x200 +#define SMB_SET_FILE_UNIX_LINK 0x201 +#define SMB_SET_FILE_UNIX_HLINK 0x203 +#define SMB_SET_POSIX_ACL 0x204 +#define SMB_SET_XATTR 0x205 +#define SMB_SET_ATTR_FLAGS 0x206 /* append, immutable etc. */ +#define SMB_SET_POSIX_LOCK 0x208 +#define SMB_POSIX_OPEN 0x209 +#define SMB_POSIX_UNLINK 0x20a +#define SMB_SET_FILE_UNIX_INFO2 0x20b +#define SMB_SET_FILE_BASIC_INFO2 0x3ec +#define SMB_SET_FILE_RENAME_INFORMATION 0x3f2 /* BB check if qpathinfo too */ +#define SMB_FILE_ALL_INFO2 0x3fa +#define SMB_SET_FILE_ALLOCATION_INFO2 0x3fb +#define SMB_SET_FILE_END_OF_FILE_INFO2 0x3fc +#define SMB_FILE_MOVE_CLUSTER_INFO 0x407 +#define SMB_FILE_QUOTA_INFO 0x408 +#define SMB_FILE_REPARSEPOINT_INFO 0x409 +#define SMB_FILE_MAXIMUM_INFO 0x40d + +/* Find File infolevels */ +#define SMB_FIND_FILE_INFO_STANDARD 0x001 +#define SMB_FIND_FILE_QUERY_EA_SIZE 0x002 +#define SMB_FIND_FILE_QUERY_EAS_FROM_LIST 0x003 +#define SMB_FIND_FILE_DIRECTORY_INFO 0x101 +#define SMB_FIND_FILE_FULL_DIRECTORY_INFO 0x102 +#define SMB_FIND_FILE_NAMES_INFO 0x103 +#define SMB_FIND_FILE_BOTH_DIRECTORY_INFO 0x104 +#define SMB_FIND_FILE_ID_FULL_DIR_INFO 0x105 +#define SMB_FIND_FILE_ID_BOTH_DIR_INFO 0x106 +#define SMB_FIND_FILE_UNIX 0x202 +/* #define SMB_FIND_FILE_POSIX_INFO 0x064 */ + +typedef struct smb_com_transaction2_qpi_req { + struct smb_hdr hdr; /* wct = 14+ */ + __le16 TotalParameterCount; + __le16 TotalDataCount; + __le16 MaxParameterCount; + __le16 MaxDataCount; + __u8 MaxSetupCount; + __u8 Reserved; + __le16 Flags; + __le32 Timeout; + __u16 Reserved2; + __le16 ParameterCount; + __le16 ParameterOffset; + __le16 DataCount; + __le16 DataOffset; + __u8 SetupCount; + __u8 Reserved3; + __le16 SubCommand; /* one setup word */ + __le16 ByteCount; + __u8 Pad; + __le16 InformationLevel; + __u32 Reserved4; + char FileName[1]; +} __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 is present for infolevels > 100 */ +} __attribute__((packed)) TRANSACTION2_QPI_RSP; + +typedef struct smb_com_transaction2_spi_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; + __le16 ParameterOffset; + __le16 DataCount; + __le16 DataOffset; + __u8 SetupCount; + __u8 Reserved3; + __le16 SubCommand; /* one setup word */ + __le16 ByteCount; + __u8 Pad; + __u16 Pad1; + __le16 InformationLevel; + __u32 Reserved4; + char FileName[1]; +} __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 is present for infolevels > 100 */ +} __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[]; /* Must be unicode */ +} __attribute__((packed)); + +struct smb_com_transaction2_sfi_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; + __le16 ParameterOffset; + __le16 DataCount; + __le16 DataOffset; + __u8 SetupCount; + __u8 Reserved3; + __le16 SubCommand; /* one setup word */ + __le16 ByteCount; + __u8 Pad; + __u16 Pad1; + __u16 Fid; + __le16 InformationLevel; + __u16 Reserved4; + __u8 payload[]; +} __attribute__((packed)); + +struct smb_com_transaction2_sfi_rsp { + struct smb_hdr hdr; /* wct = 10 + SetupCount */ + struct trans2_resp t2; + __u16 ByteCount; + __u16 Reserved2; /* parameter word reserved - + present for infolevels > 100 */ +} __attribute__((packed)); + +struct smb_t2_qfi_req { + struct smb_hdr hdr; + struct trans2_req t2; + __u8 Pad; + __u16 Fid; + __le16 InformationLevel; +} __attribute__((packed)); + +struct smb_t2_qfi_rsp { + struct smb_hdr hdr; /* wct = 10 + SetupCount */ + struct trans2_resp t2; + __u16 ByteCount; + __u16 Reserved2; /* parameter word reserved - + present for infolevels > 100 */ +} __attribute__((packed)); + +/* + * Flags on T2 FINDFIRST and FINDNEXT + */ +#define CIFS_SEARCH_CLOSE_ALWAYS 0x0001 +#define CIFS_SEARCH_CLOSE_AT_END 0x0002 +#define CIFS_SEARCH_RETURN_RESUME 0x0004 +#define CIFS_SEARCH_CONTINUE_FROM_LAST 0x0008 +#define CIFS_SEARCH_BACKUP_SEARCH 0x0010 + +/* + * Size of the resume key on FINDFIRST and FINDNEXT calls + */ +#define CIFS_SMB_RESUME_KEY_SIZE 4 + +typedef struct smb_com_transaction2_ffirst_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; + __le16 ParameterOffset; + __le16 DataCount; + __le16 DataOffset; + __u8 SetupCount; /* one */ + __u8 Reserved3; + __le16 SubCommand; /* TRANS2_FIND_FIRST */ + __le16 ByteCount; + __u8 Pad; + __le16 SearchAttributes; + __le16 SearchCount; + __le16 SearchFlags; + __le16 InformationLevel; + __le32 SearchStorageType; + char FileName[1]; +} __attribute__((packed)) TRANSACTION2_FFIRST_REQ; + +typedef struct smb_com_transaction2_ffirst_rsp { + struct smb_hdr hdr; /* wct = 10 */ + struct trans2_resp t2; + __u16 ByteCount; +} __attribute__((packed)) TRANSACTION2_FFIRST_RSP; + +typedef struct smb_com_transaction2_ffirst_rsp_parms { + __u16 SearchHandle; + __le16 SearchCount; + __le16 EndofSearch; + __le16 EAErrorOffset; + __le16 LastNameOffset; +} __attribute__((packed)) T2_FFIRST_RSP_PARMS; + +typedef struct smb_com_transaction2_fnext_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; + __le16 ParameterOffset; + __le16 DataCount; + __le16 DataOffset; + __u8 SetupCount; /* one */ + __u8 Reserved3; + __le16 SubCommand; /* TRANS2_FIND_NEXT */ + __le16 ByteCount; + __u8 Pad; + __u16 SearchHandle; + __le16 SearchCount; + __le16 InformationLevel; + __u32 ResumeKey; + __le16 SearchFlags; + char ResumeFileName[]; +} __attribute__((packed)) TRANSACTION2_FNEXT_REQ; + +typedef struct smb_com_transaction2_fnext_rsp { + struct smb_hdr hdr; /* wct = 10 */ + struct trans2_resp t2; + __u16 ByteCount; +} __attribute__((packed)) TRANSACTION2_FNEXT_RSP; + +typedef struct smb_com_transaction2_fnext_rsp_parms { + __le16 SearchCount; + __le16 EndofSearch; + __le16 EAErrorOffset; + __le16 LastNameOffset; +} __attribute__((packed)) T2_FNEXT_RSP_PARMS; + +/* QFSInfo Levels */ +#define SMB_INFO_ALLOCATION 1 +#define SMB_INFO_VOLUME 2 +#define SMB_QUERY_FS_VOLUME_INFO 0x102 +#define SMB_QUERY_FS_SIZE_INFO 0x103 +#define SMB_QUERY_FS_DEVICE_INFO 0x104 +#define SMB_QUERY_FS_ATTRIBUTE_INFO 0x105 +#define SMB_QUERY_CIFS_UNIX_INFO 0x200 +#define SMB_QUERY_POSIX_FS_INFO 0x201 +#define SMB_QUERY_POSIX_WHO_AM_I 0x202 +#define SMB_REQUEST_TRANSPORT_ENCRYPTION 0x203 +#define SMB_QUERY_FS_PROXY 0x204 /* WAFS enabled. Returns structure + FILE_SYSTEM__UNIX_INFO to tell + whether new NTIOCTL available + (0xACE) for WAN friendly SMB + operations to be carried */ +#define SMB_QUERY_LABEL_INFO 0x3ea +#define SMB_QUERY_FS_QUOTA_INFO 0x3ee +#define SMB_QUERY_FS_FULL_SIZE_INFO 0x3ef +#define SMB_QUERY_OBJECTID_INFO 0x3f0 + +typedef struct smb_com_transaction2_qfsi_req { + struct smb_hdr hdr; /* wct = 14+ */ + __le16 TotalParameterCount; + __le16 TotalDataCount; + __le16 MaxParameterCount; + __le16 MaxDataCount; + __u8 MaxSetupCount; + __u8 Reserved; + __le16 Flags; + __le32 Timeout; + __u16 Reserved2; + __le16 ParameterCount; + __le16 ParameterOffset; + __le16 DataCount; + __le16 DataOffset; + __u8 SetupCount; + __u8 Reserved3; + __le16 SubCommand; /* one setup word */ + __le16 ByteCount; + __u8 Pad; + __le16 InformationLevel; +} __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 */ +} __attribute__((packed)) TRANSACTION2_QFSI_RSP; + +typedef struct whoami_rsp_data { /* Query level 0x202 */ + __u32 flags; /* 0 = Authenticated user 1 = GUEST */ + __u32 mask; /* which flags bits server understands ie 0x0001 */ + __u64 unix_user_id; + __u64 unix_user_gid; + __u32 number_of_supplementary_gids; /* may be zero */ + __u32 number_of_sids; /* may be zero */ + __u32 length_of_sid_array; /* in bytes - may be zero */ + __u32 pad; /* reserved - MBZ */ + /* __u64 gid_array[0]; */ /* may be empty */ + /* __u8 * psid_list */ /* may be empty */ +} __attribute__((packed)) WHOAMI_RSP_DATA; + +/* SETFSInfo Levels */ +#define SMB_SET_CIFS_UNIX_INFO 0x200 +/* level 0x203 is defined above in list of QFS info levels */ +/* #define SMB_REQUEST_TRANSPORT_ENCRYPTION 0x203 */ + +/* Level 0x200 request structure follows */ +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; + +/* level 0x203 request structure follows */ +typedef struct smb_com_transaction2_setfs_enc_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 Reserved4; /* Parameters start. */ + __le16 InformationLevel;/* Parameters end. */ + /* NTLMSSP Blob, Data start. */ +} __attribute__((packed)) TRANSACTION2_SETFSI_ENC_REQ; + +/* response for setfsinfo levels 0x200 and 0x203 */ +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 */ + __le16 TotalParameterCount; + __le16 TotalDataCount; + __le16 MaxParameterCount; + __le16 MaxDataCount; + __u8 MaxSetupCount; + __u8 Reserved; + __le16 Flags; + __le32 Timeout; + __u16 Reserved2; + __le16 ParameterCount; + __le16 ParameterOffset; + __le16 DataCount; + __le16 DataOffset; + __u8 SetupCount; + __u8 Reserved3; + __le16 SubCommand; /* one setup word */ + __le16 ByteCount; + __u8 Pad[3]; /* Win2K has sent 0x0F01 (max response length + perhaps?) followed by one byte pad - doesn't + seem to matter though */ + __le16 MaxReferralLevel; + char RequestFileName[1]; +} __attribute__((packed)) TRANSACTION2_GET_DFS_REFER_REQ; + +#define DFS_VERSION cpu_to_le16(0x0003) + +/* DFS server target type */ +#define DFS_TYPE_LINK 0x0000 /* also for sysvol targets */ +#define DFS_TYPE_ROOT 0x0001 + +/* Referral Entry Flags */ +#define DFS_NAME_LIST_REF 0x0200 /* set for domain or DC referral responses */ +#define DFS_TARGET_SET_BOUNDARY 0x0400 /* only valid with version 4 dfs req */ + +typedef struct dfs_referral_level_3 { /* version 4 is same, + one flag bit */ + __le16 VersionNumber; /* must be 3 or 4 */ + __le16 Size; + __le16 ServerType; /* 0x0001 = root targets; 0x0000 = link targets */ + __le16 ReferralEntryFlags; + __le32 TimeToLive; + __le16 DfsPathOffset; + __le16 DfsAlternatePathOffset; + __le16 NetworkAddressOffset; /* offset of the link target */ + __u8 ServiceSiteGuid[16]; /* MBZ, ignored */ +} __attribute__((packed)) REFERRAL3; + +struct get_dfs_referral_rsp { + __le16 PathConsumed; + __le16 NumberOfReferrals; + __le32 DFSFlags; + REFERRAL3 referrals[1]; /* array of level 3 dfs_referral structures */ + /* followed by the strings pointed to by the referral structures */ +} __packed; + +typedef struct smb_com_transaction_get_dfs_refer_rsp { + struct smb_hdr hdr; /* wct = 10 */ + struct trans2_resp t2; + __u16 ByteCount; + __u8 Pad; + struct get_dfs_referral_rsp dfs_data; +} __packed TRANSACTION2_GET_DFS_REFER_RSP; + +/* DFS Flags */ +#define DFSREF_REFERRAL_SERVER 0x00000001 /* all targets are DFS roots */ +#define DFSREF_STORAGE_SERVER 0x00000002 /* no further ref requests needed */ +#define DFSREF_TARGET_FAILBACK 0x00000004 /* only for DFS referral version 4 */ + +/* + ************************************************************************ + * All structs for everything above the SMB PDUs themselves + * (such as the T2 level specific data) go here + ************************************************************************ + */ + +/* + * Information on a server + */ + +struct serverInfo { + char name[16]; + unsigned char versionMajor; + unsigned char versionMinor; + unsigned long type; + unsigned int commentOffset; +} __attribute__((packed)); + +/* + * The following structure is the format of the data returned on a NetShareEnum + * with level "90" (x5A) + */ + +struct shareInfo { + char shareName[13]; + 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; +} __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; +} __attribute__((packed)) FILE_SYSTEM_UNIX_INFO; /* Unix extension 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 /* Allow POSIX path chars */ +#define CIFS_UNIX_POSIX_PATH_OPS_CAP 0x00000020 /* Allow new POSIX path based + calls including posix open + and posix unlink */ +#define CIFS_UNIX_LARGE_READ_CAP 0x00000040 /* support reads >128K (up + to 0xFFFF00 */ +#define CIFS_UNIX_LARGE_WRITE_CAP 0x00000080 +#define CIFS_UNIX_TRANSPORT_ENCRYPTION_CAP 0x00000100 /* can do SPNEGO crypt */ +#define CIFS_UNIX_TRANSPORT_ENCRYPTION_MANDATORY_CAP 0x00000200 /* must do */ +#define CIFS_UNIX_PROXY_CAP 0x00000400 /* Proxy cap: 0xACE ioctl and + QFS PROXY call */ +#ifdef CONFIG_CIFS_POSIX +/* presumably don't need the 0x20 POSIX_PATH_OPS_CAP since we never send + LockingX instead of posix locking call on unix sess (and we do not expect + LockingX to use different (ie Windows) semantics than posix locking on + the same session (if WINE needs to do this later, we can add this cap + back in later */ +/* #define CIFS_UNIX_CAP_MASK 0x000000fb */ +#define CIFS_UNIX_CAP_MASK 0x000003db +#else +#define CIFS_UNIX_CAP_MASK 0x00000013 +#endif /* CONFIG_CIFS_POSIX */ + + +#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 */ + __le32 BlockSize; + /* The next three fields are in terms of the block size. + (above). If block size is unknown, 4096 would be a + reasonable block size for a server to report. + Note that returning the blocks/blocksavail removes need + to make a second call (to QFSInfo level 0x103 to get this info. + UserBlockAvail is typically less than or equal to BlocksAvail, + if no distinction is made return the same value in each */ + __le64 TotalBlocks; + __le64 BlocksAvail; /* bfree */ + __le64 UserBlocksAvail; /* bavail */ + /* For undefined Node fields or FSID return -1 */ + __le64 TotalFileNodes; + __le64 FreeFileNodes; + __le64 FileSysIdentifier; /* fsid */ + /* NB Namelen comes from FILE_SYSTEM_ATTRIBUTE_INFO call */ + /* NB flags can come from FILE_SYSTEM_DEVICE_INFO call */ +} __attribute__((packed)) FILE_SYSTEM_POSIX_INFO; + +/* DeviceType Flags */ +#define FILE_DEVICE_CD_ROM 0x00000002 +#define FILE_DEVICE_CD_ROM_FILE_SYSTEM 0x00000003 +#define FILE_DEVICE_DFS 0x00000006 +#define FILE_DEVICE_DISK 0x00000007 +#define FILE_DEVICE_DISK_FILE_SYSTEM 0x00000008 +#define FILE_DEVICE_FILE_SYSTEM 0x00000009 +#define FILE_DEVICE_NAMED_PIPE 0x00000011 +#define FILE_DEVICE_NETWORK 0x00000012 +#define FILE_DEVICE_NETWORK_FILE_SYSTEM 0x00000014 +#define FILE_DEVICE_NULL 0x00000015 +#define FILE_DEVICE_PARALLEL_PORT 0x00000016 +#define FILE_DEVICE_PRINTER 0x00000018 +#define FILE_DEVICE_SERIAL_PORT 0x0000001b +#define FILE_DEVICE_STREAMS 0x0000001e +#define FILE_DEVICE_TAPE 0x0000001f +#define FILE_DEVICE_TAPE_FILE_SYSTEM 0x00000020 +#define FILE_DEVICE_VIRTUAL_DISK 0x00000024 +#define FILE_DEVICE_NETWORK_REDIRECTOR 0x00000028 + +/* Device Characteristics */ +#define FILE_REMOVABLE_MEDIA 0x00000001 +#define FILE_READ_ONLY_DEVICE 0x00000002 +#define FILE_FLOPPY_DISKETTE 0x00000004 +#define FILE_WRITE_ONCE_MEDIA 0x00000008 +#define FILE_REMOTE_DEVICE 0x00000010 +#define FILE_DEVICE_IS_MOUNTED 0x00000020 +#define FILE_VIRTUAL_VOLUME 0x00000040 +#define FILE_DEVICE_SECURE_OPEN 0x00000100 +#define FILE_CHARACTERISTIC_TS_DEVICE 0x00001000 +#define FILE_CHARACTERISTIC_WEBDAV_DEVICE 0x00002000 +#define FILE_PORTABLE_DEVICE 0x00004000 +#define FILE_DEVICE_ALLOW_APPCONTAINER_TRAVERSAL 0x00020000 + +typedef struct { + __le32 DeviceType; + __le32 DeviceCharacteristics; +} __attribute__((packed)) FILE_SYSTEM_DEVICE_INFO; /* device info level 0x104 */ + +/* minimum includes first three fields, and empty FS Name */ +#define MIN_FS_ATTR_INFO_SIZE 12 + + +/* List of FileSystemAttributes - see 2.5.1 of MS-FSCC */ +#define FILE_SUPPORTS_SPARSE_VDL 0x10000000 /* faster nonsparse extend */ +#define FILE_SUPPORTS_BLOCK_REFCOUNTING 0x08000000 /* allow ioctl dup extents */ +#define FILE_SUPPORT_INTEGRITY_STREAMS 0x04000000 +#define FILE_SUPPORTS_USN_JOURNAL 0x02000000 +#define FILE_SUPPORTS_OPEN_BY_FILE_ID 0x01000000 +#define FILE_SUPPORTS_EXTENDED_ATTRIBUTES 0x00800000 +#define FILE_SUPPORTS_HARD_LINKS 0x00400000 +#define FILE_SUPPORTS_TRANSACTIONS 0x00200000 +#define FILE_SEQUENTIAL_WRITE_ONCE 0x00100000 +#define FILE_READ_ONLY_VOLUME 0x00080000 +#define FILE_NAMED_STREAMS 0x00040000 +#define FILE_SUPPORTS_ENCRYPTION 0x00020000 +#define FILE_SUPPORTS_OBJECT_IDS 0x00010000 +#define FILE_VOLUME_IS_COMPRESSED 0x00008000 +#define FILE_SUPPORTS_REMOTE_STORAGE 0x00000100 +#define FILE_SUPPORTS_REPARSE_POINTS 0x00000080 +#define FILE_SUPPORTS_SPARSE_FILES 0x00000040 +#define FILE_VOLUME_QUOTAS 0x00000020 +#define FILE_FILE_COMPRESSION 0x00000010 +#define FILE_PERSISTENT_ACLS 0x00000008 +#define FILE_UNICODE_ON_DISK 0x00000004 +#define FILE_CASE_PRESERVED_NAMES 0x00000002 +#define FILE_CASE_SENSITIVE_SEARCH 0x00000001 +typedef struct { + __le32 Attributes; + __le32 MaxPathNameComponentLength; + __le32 FileSystemNameLen; + char FileSystemName[52]; /* do not have to save this - get subset? */ +} __attribute__((packed)) FILE_SYSTEM_ATTRIBUTE_INFO; + +/******************************************************************************/ +/* QueryFileInfo/QueryPathinfo (also for SetPath/SetFile) data buffer formats */ +/******************************************************************************/ +typedef struct { /* data block encoding of response to level 263 QPathInfo */ + __le64 CreationTime; + __le64 LastAccessTime; + __le64 LastWriteTime; + __le64 ChangeTime; + __le32 Attributes; + __u32 Pad1; + __le64 AllocationSize; + __le64 EndOfFile; /* size ie offset to first free byte in file */ + __le32 NumberOfLinks; /* hard links */ + __u8 DeletePending; + __u8 Directory; + __u16 Pad2; + __le64 IndexNumber; + __le32 EASize; + __le32 AccessFlags; + __u64 IndexNumber1; + __le64 CurrentByteOffset; + __le32 Mode; + __le32 AlignmentRequirement; + __le32 FileNameLength; + char FileName[1]; +} __attribute__((packed)) FILE_ALL_INFO; /* level 0x107 QPathInfo */ + +typedef struct { + __le64 AllocationSize; + __le64 EndOfFile; /* size ie offset to first free byte in file */ + __le32 NumberOfLinks; /* hard links */ + __u8 DeletePending; + __u8 Directory; + __u16 Pad; +} __attribute__((packed)) FILE_STANDARD_INFO; /* level 0x102 QPathInfo */ + + +/* defines for enumerating possible values of the Unix type field below */ +#define UNIX_FILE 0 +#define UNIX_DIR 1 +#define UNIX_SYMLINK 2 +#define UNIX_CHARDEV 3 +#define UNIX_BLOCKDEV 4 +#define UNIX_FIFO 5 +#define UNIX_SOCKET 6 +typedef struct { + __le64 EndOfFile; + __le64 NumOfBytes; + __le64 LastStatusChange; /*SNIA specs DCE time for the 3 time fields */ + __le64 LastAccessTime; + __le64 LastModificationTime; + __le64 Uid; + __le64 Gid; + __le32 Type; + __le64 DevMajor; + __le64 DevMinor; + __le64 UniqueId; + __le64 Permissions; + __le64 Nlinks; +} __attribute__((packed)) FILE_UNIX_BASIC_INFO; /* level 0x200 QPathInfo */ + +typedef struct { + char LinkDest[1]; +} __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 + the primitive DOS time format */ +typedef struct { + __u16 Day:5; + __u16 Month:4; + __u16 Year:7; +} __attribute__((packed)) SMB_DATE; + +typedef struct { + __u16 TwoSeconds:5; + __u16 Minutes:6; + __u16 Hours:5; +} __attribute__((packed)) SMB_TIME; + +typedef struct { + __le16 CreationDate; /* SMB Date see above */ + __le16 CreationTime; /* SMB Time */ + __le16 LastAccessDate; + __le16 LastAccessTime; + __le16 LastWriteDate; + __le16 LastWriteTime; + __le32 DataSize; /* File Size (EOF) */ + __le32 AllocationSize; + __le16 Attributes; /* verify not u32 */ + __le32 EASize; +} __attribute__((packed)) FILE_INFO_STANDARD; /* level 1 SetPath/FileInfo */ + +typedef struct { + __le64 CreationTime; + __le64 LastAccessTime; + __le64 LastWriteTime; + __le64 ChangeTime; + __le32 Attributes; + __u32 Pad; +} __attribute__((packed)) FILE_BASIC_INFO; /* size info, level 0x101 */ + +struct file_allocation_info { + __le64 AllocationSize; /* Note old Samba srvr rounds this up too much */ +} __attribute__((packed)); /* size used on disk, for level 0x103 for set, + 0x105 for query */ + +struct file_end_of_file_info { + __le64 FileSize; /* offset to end of file */ +} __attribute__((packed)); /* size info, level 0x104 for set, 0x106 for query */ + +struct file_alt_name_info { + __u8 alt_name[1]; +} __attribute__((packed)); /* level 0x0108 */ + +struct file_stream_info { + __le32 number_of_streams; /* BB check sizes and verify location */ + /* followed by info on streams themselves + u64 size; + u64 allocation_size + stream info */ +}; /* level 0x109 */ + +struct file_compression_info { + __le64 compressed_size; + __le16 format; + __u8 unit_shift; + __u8 ch_shift; + __u8 cl_shift; + __u8 pad[3]; +} __attribute__((packed)); /* level 0x10b */ + +/* POSIX ACL set/query path info structures */ +#define CIFS_ACL_VERSION 1 +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; + __le16 access_entry_count; /* access ACL - count of entries */ + __le16 default_entry_count; /* default ACL - count of entries */ + struct cifs_posix_ace ace_array[]; + /* followed by + struct cifs_posix_ace default_ace_arraay[] */ +} __attribute__((packed)); /* level 0x204 */ + +/* types of access control entries already defined in posix_acl.h */ +/* #define CIFS_POSIX_ACL_USER_OBJ 0x01 +#define CIFS_POSIX_ACL_USER 0x02 +#define CIFS_POSIX_ACL_GROUP_OBJ 0x04 +#define CIFS_POSIX_ACL_GROUP 0x08 +#define CIFS_POSIX_ACL_MASK 0x10 +#define CIFS_POSIX_ACL_OTHER 0x20 */ + +/* types of perms */ +/* #define CIFS_POSIX_ACL_EXECUTE 0x01 +#define CIFS_POSIX_ACL_WRITE 0x02 +#define CIFS_POSIX_ACL_READ 0x04 */ + +/* end of POSIX ACL definitions */ + +/* POSIX Open Flags */ +#define SMB_O_RDONLY 0x1 +#define SMB_O_WRONLY 0x2 +#define SMB_O_RDWR 0x4 +#define SMB_O_CREAT 0x10 +#define SMB_O_EXCL 0x20 +#define SMB_O_TRUNC 0x40 +#define SMB_O_APPEND 0x80 +#define SMB_O_SYNC 0x100 +#define SMB_O_DIRECTORY 0x200 +#define SMB_O_NOFOLLOW 0x400 +#define SMB_O_DIRECT 0x800 + +typedef struct { + __le32 OpenFlags; /* same as NT CreateX */ + __le32 PosixOpenFlags; + __le64 Permissions; + __le16 Level; /* reply level requested (see QPathInfo levels) */ +} __attribute__((packed)) OPEN_PSX_REQ; /* level 0x209 SetPathInfo data */ + +typedef struct { + __le16 OplockFlags; + __u16 Fid; + __le32 CreateAction; + __le16 ReturnedLevel; + __le16 Pad; + /* struct following varies based on requested level */ +} __attribute__((packed)) OPEN_PSX_RSP; /* level 0x209 SetPathInfo data */ + +#define SMB_POSIX_UNLINK_FILE_TARGET 0 +#define SMB_POSIX_UNLINK_DIRECTORY_TARGET 1 + +struct unlink_psx_rq { /* level 0x20a SetPathInfo */ + __le16 type; +} __attribute__((packed)); + +struct file_internal_info { + __le64 UniqueId; /* inode number */ +} __attribute__((packed)); /* level 0x3ee */ + +struct file_mode_info { + __le32 Mode; +} __attribute__((packed)); /* level 0x3f8 */ + +struct file_attrib_tag { + __le32 Attribute; + __le32 ReparseTag; +} __attribute__((packed)); /* level 0x40b */ + + +/********************************************************/ +/* FindFirst/FindNext transact2 data buffer formats */ +/********************************************************/ + +typedef struct { + __le32 NextEntryOffset; + __u32 ResumeKey; /* as with FileIndex - no need to convert */ + FILE_UNIX_BASIC_INFO basic; + char FileName[1]; +} __attribute__((packed)) FILE_UNIX_INFO; /* level 0x202 */ + +typedef struct { + __le32 NextEntryOffset; + __u32 FileIndex; + __le64 CreationTime; + __le64 LastAccessTime; + __le64 LastWriteTime; + __le64 ChangeTime; + __le64 EndOfFile; + __le64 AllocationSize; + __le32 ExtFileAttributes; + __le32 FileNameLength; + char FileName[1]; +} __attribute__((packed)) FILE_DIRECTORY_INFO; /* level 0x101 FF resp data */ + +typedef struct { + __le32 NextEntryOffset; + __u32 FileIndex; + __le64 CreationTime; + __le64 LastAccessTime; + __le64 LastWriteTime; + __le64 ChangeTime; + __le64 EndOfFile; + __le64 AllocationSize; + __le32 ExtFileAttributes; + __le32 FileNameLength; + __le32 EaSize; /* length of the xattrs */ + char FileName[1]; +} __attribute__((packed)) FILE_FULL_DIRECTORY_INFO; /* level 0x102 rsp data */ + +typedef struct { + __le32 NextEntryOffset; + __u32 FileIndex; + __le64 CreationTime; + __le64 LastAccessTime; + __le64 LastWriteTime; + __le64 ChangeTime; + __le64 EndOfFile; + __le64 AllocationSize; + __le32 ExtFileAttributes; + __le32 FileNameLength; + __le32 EaSize; /* EA size */ + __le32 Reserved; + __le64 UniqueId; /* inode num - le since Samba puts ino in low 32 bit*/ + char FileName[1]; +} __attribute__((packed)) SEARCH_ID_FULL_DIR_INFO; /* level 0x105 FF rsp data */ + +typedef struct { + __le32 NextEntryOffset; + __u32 FileIndex; + __le64 CreationTime; + __le64 LastAccessTime; + __le64 LastWriteTime; + __le64 ChangeTime; + __le64 EndOfFile; + __le64 AllocationSize; + __le32 ExtFileAttributes; + __le32 FileNameLength; + __le32 EaSize; /* length of the xattrs */ + __u8 ShortNameLength; + __u8 Reserved; + __u8 ShortName[24]; + char FileName[1]; +} __attribute__((packed)) FILE_BOTH_DIRECTORY_INFO; /* level 0x104 FFrsp data */ + +typedef struct { + __u32 ResumeKey; + __le16 CreationDate; /* SMB Date */ + __le16 CreationTime; /* SMB Time */ + __le16 LastAccessDate; + __le16 LastAccessTime; + __le16 LastWriteDate; + __le16 LastWriteTime; + __le32 DataSize; /* File Size (EOF) */ + __le32 AllocationSize; + __le16 Attributes; /* verify not u32 */ + __u8 FileNameLength; + char FileName[1]; +} __attribute__((packed)) FIND_FILE_STANDARD_INFO; /* level 0x1 FF resp data */ + + +struct win_dev { + unsigned char type[8]; /* IntxCHR or IntxBLK */ + __le64 major; + __le64 minor; +} __attribute__((packed)); + +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; + __u8 name_len; + __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 +/* + For better POSIX semantics from Linux client, (even better + than the existing CIFS Unix Extensions) we need updated PDUs for: + + 1) PosixCreateX - to set and return the mode, inode#, device info and + 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) 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: + 5) FindClose2 (return nanosecond timestamp ??) + 6) Use nanosecond timestamps throughout all time fields if + corresponding attribute flag is set + 7) sendfile - handle based copy + + what about fixing 64 bit alignment + + There are also various legacy SMB/CIFS requests used as is + + From existing Lanman and NTLM dialects: + -------------------------------------- + NEGOTIATE + SESSION_SETUP_ANDX (BB which?) + TREE_CONNECT_ANDX (BB which wct?) + TREE_DISCONNECT (BB add volume timestamp on response) + LOGOFF_ANDX + DELETE (note delete open file behavior) + DELETE_DIRECTORY + READ_AND_X + WRITE_AND_X + LOCKING_AND_X (note posix lock semantics) + RENAME (note rename across dirs and open file rename posix behaviors) + NT_RENAME (for hardlinks) Is this good enough for all features? + FIND_CLOSE2 + TRANSACTION2 (18 cases) + SMB_SET_FILE_END_OF_FILE_INFO2 SMB_SET_PATH_END_OF_FILE_INFO2 + (BB verify that never need to set allocation size) + SMB_SET_FILE_BASIC_INFO2 (setting times - BB can it be done via + Unix ext?) + + COPY (note support for copy across directories) - FUTURE, OPTIONAL + setting/getting OS/2 EAs - FUTURE (BB can this handle + setting Linux xattrs perfectly) - OPTIONAL + dnotify - FUTURE, OPTIONAL + quota - FUTURE, OPTIONAL + + Note that various requests implemented for NT interop such as + NT_TRANSACT (IOCTL) QueryReparseInfo + are unneeded to servers compliant with the CIFS POSIX extensions + + From CIFS Unix Extensions: + ------------------------- + T2 SET_PATH_INFO (SMB_SET_FILE_UNIX_LINK) for symlinks + T2 SET_PATH_INFO (SMB_SET_FILE_BASIC_INFO2) + T2 QUERY_PATH_INFO (SMB_QUERY_FILE_UNIX_LINK) + T2 QUERY_PATH_INFO (SMB_QUERY_FILE_UNIX_BASIC) BB check for missing + inode fields + Actually a need QUERY_FILE_UNIX_INFO + since has inode num + BB what about a) blksize/blkbits/blocks + b) i_version + c) i_rdev + d) notify mask? + e) generation + f) size_seqcount + T2 FIND_FIRST/FIND_NEXT FIND_FILE_UNIX + TRANS2_GET_DFS_REFERRAL - OPTIONAL but recommended + T2_QFS_INFO QueryDevice/AttributeInfo - OPTIONAL + */ + +/* 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 + based symlinks). For such a file to be recognized + as containing symlink data: + + 1) file size must be 1067, + 2) signature must begin file data, + 3) length field must be set to ASCII representation + of a number which is less than or equal to 1024, + 4) md5 must match that of the path data */ + +struct xsymlink { + /* 1067 bytes */ + char signature[4]; /* XSym */ /* not null terminated */ + char cr0; /* \n */ +/* ASCII representation of length (4 bytes decimal) terminated by \n not null */ + char length[4]; + char cr1; /* \n */ +/* md5 of valid subset of path ie path[0] through path[length-1] */ + __u8 md5[32]; + 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 */ + __u32 xattr_name_len; + __u32 xattr_value_len; + char xattr_name[]; + /* followed by xattr_value[xattr_value_len], no pad */ +} __attribute__((packed)) FILE_XATTR_INFO; /* extended attribute info + level 0x205 */ + +/* flags for lsattr and chflags commands removed arein uapi/linux/fs.h */ + +typedef struct file_chattr_info { + __le64 mask; /* list of all possible attribute bits */ + __le64 mode; /* list of actual attribute bits on this inode */ +} __attribute__((packed)) FILE_CHATTR_INFO; /* ext attributes + (chattr, chflags) level 0x206 */ +#endif /* POSIX */ +#endif /* _CIFSPDU_H */ diff --git a/fs/smb/client/cifsproto.h b/fs/smb/client/cifsproto.h new file mode 100644 index 0000000000000..98513f5af3f96 --- /dev/null +++ b/fs/smb/client/cifsproto.h @@ -0,0 +1,696 @@ +/* SPDX-License-Identifier: LGPL-2.1 */ +/* + * + * Copyright (c) International Business Machines Corp., 2002,2008 + * Author(s): Steve French (sfrench@us.ibm.com) + * + */ +#ifndef _CIFSPROTO_H +#define _CIFSPROTO_H +#include +#include "trace.h" +#ifdef CONFIG_CIFS_DFS_UPCALL +#include "dfs_cache.h" +#endif + +struct statfs; +struct smb_rqst; +struct smb3_fs_context; + +/* + ***************************************************************** + * All Prototypes + ***************************************************************** + */ + +extern struct smb_hdr *cifs_buf_get(void); +extern void cifs_buf_release(void *); +extern struct smb_hdr *cifs_small_buf_get(void); +extern void cifs_small_buf_release(void *); +extern void free_rsp_buf(int, void *); +extern int smb_send(struct TCP_Server_Info *, struct smb_hdr *, + unsigned int /* length */); +extern unsigned int _get_xid(void); +extern void _free_xid(unsigned int); +#define get_xid() \ +({ \ + unsigned int __xid = _get_xid(); \ + cifs_dbg(FYI, "VFS: in %s as Xid: %u with uid: %d\n", \ + __func__, __xid, \ + from_kuid(&init_user_ns, current_fsuid())); \ + trace_smb3_enter(__xid, __func__); \ + __xid; \ +}) + +#define free_xid(curr_xid) \ +do { \ + _free_xid(curr_xid); \ + cifs_dbg(FYI, "VFS: leaving %s (xid = %u) rc = %d\n", \ + __func__, curr_xid, (int)rc); \ + if (rc) \ + trace_smb3_exit_err(curr_xid, __func__, (int)rc); \ + else \ + trace_smb3_exit_done(curr_xid, __func__); \ +} while (0) +extern int init_cifs_idmap(void); +extern void exit_cifs_idmap(void); +extern int init_cifs_spnego(void); +extern void exit_cifs_spnego(void); +extern const char *build_path_from_dentry(struct dentry *, void *); +extern char *build_path_from_dentry_optional_prefix(struct dentry *direntry, + void *page, bool prefix); +static inline void *alloc_dentry_path(void) +{ + return __getname(); +} + +static inline void free_dentry_path(void *page) +{ + if (page) + __putname(page); +} + +extern char *cifs_build_path_to_root(struct smb3_fs_context *ctx, + struct cifs_sb_info *cifs_sb, + struct cifs_tcon *tcon, + int add_treename); +extern char *build_wildcard_path_from_dentry(struct dentry *direntry); +extern char *cifs_compose_mount_options(const char *sb_mountdata, + const char *fullpath, const struct dfs_info3_param *ref, + char **devname); +extern void delete_mid(struct mid_q_entry *mid); +extern void release_mid(struct mid_q_entry *mid); +extern void cifs_wake_up_task(struct mid_q_entry *mid); +extern int cifs_handle_standard(struct TCP_Server_Info *server, + struct mid_q_entry *mid); +extern int smb3_parse_devname(const char *devname, struct smb3_fs_context *ctx); +extern int smb3_parse_opt(const char *options, const char *key, char **val); +extern bool cifs_match_ipaddr(struct sockaddr *srcaddr, struct sockaddr *rhs); +extern int cifs_discard_remaining_data(struct TCP_Server_Info *server); +extern int cifs_call_async(struct TCP_Server_Info *server, + struct smb_rqst *rqst, + mid_receive_t *receive, mid_callback_t *callback, + mid_handle_t *handle, void *cbdata, const int flags, + const struct cifs_credits *exist_credits); +extern struct TCP_Server_Info *cifs_pick_channel(struct cifs_ses *ses); +extern int cifs_send_recv(const unsigned int xid, struct cifs_ses *ses, + struct TCP_Server_Info *server, + struct smb_rqst *rqst, int *resp_buf_type, + const int flags, struct kvec *resp_iov); +extern int compound_send_recv(const unsigned int xid, struct cifs_ses *ses, + struct TCP_Server_Info *server, + const int flags, const int num_rqst, + struct smb_rqst *rqst, int *resp_buf_type, + struct kvec *resp_iov); +extern int SendReceive(const unsigned int /* xid */ , struct cifs_ses *, + struct smb_hdr * /* input */ , + struct smb_hdr * /* out */ , + int * /* bytes returned */ , const int); +extern int SendReceiveNoRsp(const unsigned int xid, struct cifs_ses *ses, + char *in_buf, int flags); +extern struct mid_q_entry *cifs_setup_request(struct cifs_ses *, + struct TCP_Server_Info *, + struct smb_rqst *); +extern struct mid_q_entry *cifs_setup_async_request(struct TCP_Server_Info *, + struct smb_rqst *); +extern int cifs_check_receive(struct mid_q_entry *mid, + struct TCP_Server_Info *server, bool log_error); +extern int cifs_wait_mtu_credits(struct TCP_Server_Info *server, + unsigned int size, unsigned int *num, + struct cifs_credits *credits); +extern int SendReceive2(const unsigned int /* xid */ , struct cifs_ses *, + struct kvec *, int /* nvec to send */, + int * /* type of buf returned */, const int flags, + struct kvec * /* resp vec */); +extern int SendReceiveBlockingLock(const unsigned int xid, + struct cifs_tcon *ptcon, + struct smb_hdr *in_buf , + struct smb_hdr *out_buf, + int *bytes_returned); +void +cifs_signal_cifsd_for_reconnect(struct TCP_Server_Info *server, + bool all_channels); +void +cifs_mark_tcp_ses_conns_for_reconnect(struct TCP_Server_Info *server, + bool mark_smb_session); +extern int cifs_reconnect(struct TCP_Server_Info *server, + bool mark_smb_session); +extern int checkSMB(char *buf, unsigned int len, struct TCP_Server_Info *srvr); +extern bool is_valid_oplock_break(char *, struct TCP_Server_Info *); +extern bool backup_cred(struct cifs_sb_info *); +extern bool is_size_safe_to_change(struct cifsInodeInfo *, __u64 eof); +extern void cifs_update_eof(struct cifsInodeInfo *cifsi, loff_t offset, + unsigned int bytes_written); +extern struct cifsFileInfo *find_writable_file(struct cifsInodeInfo *, int); +extern int cifs_get_writable_file(struct cifsInodeInfo *cifs_inode, + int flags, + struct cifsFileInfo **ret_file); +extern int cifs_get_writable_path(struct cifs_tcon *tcon, const char *name, + int flags, + struct cifsFileInfo **ret_file); +extern struct cifsFileInfo *find_readable_file(struct cifsInodeInfo *, bool); +extern int cifs_get_readable_path(struct cifs_tcon *tcon, const char *name, + struct cifsFileInfo **ret_file); +extern unsigned int smbCalcSize(void *buf); +extern int decode_negTokenInit(unsigned char *security_blob, int length, + struct TCP_Server_Info *server); +extern int cifs_convert_address(struct sockaddr *dst, const char *src, int len); +extern void cifs_set_port(struct sockaddr *addr, const unsigned short int port); +extern int map_smb_to_linux_error(char *buf, bool logErr); +extern int map_and_check_smb_error(struct mid_q_entry *mid, bool logErr); +extern void header_assemble(struct smb_hdr *, char /* command */ , + const struct cifs_tcon *, int /* length of + fixed section (word count) in two byte units */); +extern int small_smb_init_no_tc(const int smb_cmd, const int wct, + struct cifs_ses *ses, + void **request_buf); +extern enum securityEnum select_sectype(struct TCP_Server_Info *server, + enum securityEnum requested); +extern int CIFS_SessSetup(const unsigned int xid, struct cifs_ses *ses, + struct TCP_Server_Info *server, + const struct nls_table *nls_cp); +extern struct timespec64 cifs_NTtimeToUnix(__le64 utc_nanoseconds_since_1601); +extern u64 cifs_UnixTimeToNT(struct timespec64); +extern struct timespec64 cnvrtDosUnixTm(__le16 le_date, __le16 le_time, + int offset); +extern void cifs_set_oplock_level(struct cifsInodeInfo *cinode, __u32 oplock); +extern int cifs_get_writer(struct cifsInodeInfo *cinode); +extern void cifs_put_writer(struct cifsInodeInfo *cinode); +extern void cifs_done_oplock_break(struct cifsInodeInfo *cinode); +extern int cifs_unlock_range(struct cifsFileInfo *cfile, + struct file_lock *flock, const unsigned int xid); +extern int cifs_push_mandatory_locks(struct cifsFileInfo *cfile); + +extern void cifs_down_write(struct rw_semaphore *sem); +struct cifsFileInfo *cifs_new_fileinfo(struct cifs_fid *fid, struct file *file, + struct tcon_link *tlink, __u32 oplock, + const char *symlink_target); +extern int cifs_posix_open(const char *full_path, struct inode **inode, + struct super_block *sb, int mode, + unsigned int f_flags, __u32 *oplock, __u16 *netfid, + unsigned int xid); +void cifs_fill_uniqueid(struct super_block *sb, struct cifs_fattr *fattr); +extern void cifs_unix_basic_to_fattr(struct cifs_fattr *fattr, + FILE_UNIX_BASIC_INFO *info, + struct cifs_sb_info *cifs_sb); +extern void cifs_dir_info_to_fattr(struct cifs_fattr *, FILE_DIRECTORY_INFO *, + struct cifs_sb_info *); +extern int cifs_fattr_to_inode(struct inode *inode, struct cifs_fattr *fattr); +extern struct inode *cifs_iget(struct super_block *sb, + struct cifs_fattr *fattr); + +int cifs_get_inode_info(struct inode **inode, const char *full_path, + struct cifs_open_info_data *data, struct super_block *sb, int xid, + const struct cifs_fid *fid); +extern int smb311_posix_get_inode_info(struct inode **pinode, const char *search_path, + struct super_block *sb, unsigned int xid); +extern int cifs_get_inode_info_unix(struct inode **pinode, + const unsigned char *search_path, + struct super_block *sb, unsigned int xid); +extern int cifs_set_file_info(struct inode *inode, struct iattr *attrs, + unsigned int xid, const char *full_path, __u32 dosattr); +extern int cifs_rename_pending_delete(const char *full_path, + struct dentry *dentry, + const unsigned int xid); +extern int sid_to_id(struct cifs_sb_info *cifs_sb, struct cifs_sid *psid, + struct cifs_fattr *fattr, uint sidtype); +extern int cifs_acl_to_fattr(struct cifs_sb_info *cifs_sb, + struct cifs_fattr *fattr, struct inode *inode, + bool get_mode_from_special_sid, + const char *path, const struct cifs_fid *pfid); +extern int id_mode_to_cifs_acl(struct inode *inode, const char *path, __u64 *pnmode, + kuid_t uid, kgid_t gid); +extern struct cifs_ntsd *get_cifs_acl(struct cifs_sb_info *, struct inode *, + const char *, u32 *, u32); +extern struct cifs_ntsd *get_cifs_acl_by_fid(struct cifs_sb_info *, + const struct cifs_fid *, u32 *, u32); +extern int set_cifs_acl(struct cifs_ntsd *, __u32, struct inode *, + const char *, int); +extern unsigned int setup_authusers_ACE(struct cifs_ace *pace); +extern unsigned int setup_special_mode_ACE(struct cifs_ace *pace, __u64 nmode); +extern unsigned int setup_special_user_owner_ACE(struct cifs_ace *pace); + +extern void dequeue_mid(struct mid_q_entry *mid, bool malformed); +extern int cifs_read_from_socket(struct TCP_Server_Info *server, char *buf, + unsigned int to_read); +extern ssize_t cifs_discard_from_socket(struct TCP_Server_Info *server, + size_t to_read); +extern int cifs_read_page_from_socket(struct TCP_Server_Info *server, + struct page *page, + unsigned int page_offset, + unsigned int to_read); +extern int cifs_setup_cifs_sb(struct cifs_sb_info *cifs_sb); +extern int cifs_match_super(struct super_block *, void *); +extern int cifs_mount(struct cifs_sb_info *cifs_sb, struct smb3_fs_context *ctx); +extern void cifs_umount(struct cifs_sb_info *); +extern void cifs_mark_open_files_invalid(struct cifs_tcon *tcon); +extern void cifs_reopen_persistent_handles(struct cifs_tcon *tcon); + +extern bool cifs_find_lock_conflict(struct cifsFileInfo *cfile, __u64 offset, + __u64 length, __u8 type, __u16 flags, + struct cifsLockInfo **conf_lock, + int rw_check); +extern void cifs_add_pending_open(struct cifs_fid *fid, + struct tcon_link *tlink, + struct cifs_pending_open *open); +extern void cifs_add_pending_open_locked(struct cifs_fid *fid, + struct tcon_link *tlink, + struct cifs_pending_open *open); +extern void cifs_del_pending_open(struct cifs_pending_open *open); + +extern bool cifs_is_deferred_close(struct cifsFileInfo *cfile, + struct cifs_deferred_close **dclose); + +extern void cifs_add_deferred_close(struct cifsFileInfo *cfile, + struct cifs_deferred_close *dclose); + +extern void cifs_del_deferred_close(struct cifsFileInfo *cfile); + +extern void cifs_close_deferred_file(struct cifsInodeInfo *cifs_inode); + +extern void cifs_close_all_deferred_files(struct cifs_tcon *cifs_tcon); + +extern void cifs_close_deferred_file_under_dentry(struct cifs_tcon *cifs_tcon, + const char *path); +extern struct TCP_Server_Info * +cifs_get_tcp_session(struct smb3_fs_context *ctx, + struct TCP_Server_Info *primary_server); +extern void cifs_put_tcp_session(struct TCP_Server_Info *server, + int from_reconnect); +extern void cifs_put_tcon(struct cifs_tcon *tcon); + +#if IS_ENABLED(CONFIG_CIFS_DFS_UPCALL) +extern void cifs_dfs_release_automount_timer(void); +#else /* ! IS_ENABLED(CONFIG_CIFS_DFS_UPCALL) */ +#define cifs_dfs_release_automount_timer() do { } while (0) +#endif /* ! IS_ENABLED(CONFIG_CIFS_DFS_UPCALL) */ + +void cifs_proc_init(void); +void cifs_proc_clean(void); + +extern void cifs_move_llist(struct list_head *source, struct list_head *dest); +extern void cifs_free_llist(struct list_head *llist); +extern void cifs_del_lock_waiters(struct cifsLockInfo *lock); + +extern int cifs_tree_connect(const unsigned int xid, struct cifs_tcon *tcon, + const struct nls_table *nlsc); + +extern int cifs_negotiate_protocol(const unsigned int xid, + struct cifs_ses *ses, + struct TCP_Server_Info *server); +extern int cifs_setup_session(const unsigned int xid, struct cifs_ses *ses, + struct TCP_Server_Info *server, + struct nls_table *nls_info); +extern int cifs_enable_signing(struct TCP_Server_Info *server, bool mnt_sign_required); +extern int CIFSSMBNegotiate(const unsigned int xid, + struct cifs_ses *ses, + struct TCP_Server_Info *server); + +extern int CIFSTCon(const unsigned int xid, struct cifs_ses *ses, + const char *tree, struct cifs_tcon *tcon, + const struct nls_table *); + +extern int CIFSFindFirst(const unsigned int xid, struct cifs_tcon *tcon, + const char *searchName, struct cifs_sb_info *cifs_sb, + __u16 *searchHandle, __u16 search_flags, + struct cifs_search_info *psrch_inf, + bool msearch); + +extern int CIFSFindNext(const unsigned int xid, struct cifs_tcon *tcon, + __u16 searchHandle, __u16 search_flags, + struct cifs_search_info *psrch_inf); + +extern int CIFSFindClose(const unsigned int xid, struct cifs_tcon *tcon, + const __u16 search_handle); + +extern int CIFSSMBQFileInfo(const unsigned int xid, struct cifs_tcon *tcon, + u16 netfid, FILE_ALL_INFO *pFindData); +extern int CIFSSMBQPathInfo(const unsigned int xid, struct cifs_tcon *tcon, + const char *search_Name, FILE_ALL_INFO *data, + int legacy /* whether to use old info level */, + const struct nls_table *nls_codepage, int remap); +extern int SMBQueryInformation(const unsigned int xid, struct cifs_tcon *tcon, + const char *search_name, FILE_ALL_INFO *data, + const struct nls_table *nls_codepage, int remap); + +extern int CIFSSMBUnixQFileInfo(const unsigned int xid, struct cifs_tcon *tcon, + u16 netfid, FILE_UNIX_BASIC_INFO *pFindData); +extern int CIFSSMBUnixQPathInfo(const unsigned int xid, + struct cifs_tcon *tcon, + const unsigned char *searchName, + FILE_UNIX_BASIC_INFO *pFindData, + const struct nls_table *nls_codepage, int remap); + +extern int CIFSGetDFSRefer(const unsigned int xid, struct cifs_ses *ses, + const char *search_name, + struct dfs_info3_param **target_nodes, + unsigned int *num_of_nodes, + const struct nls_table *nls_codepage, int remap); + +extern int parse_dfs_referrals(struct get_dfs_referral_rsp *rsp, u32 rsp_size, + unsigned int *num_of_nodes, + struct dfs_info3_param **target_nodes, + const struct nls_table *nls_codepage, int remap, + const char *searchName, bool is_unicode); +extern void reset_cifs_unix_caps(unsigned int xid, struct cifs_tcon *tcon, + struct cifs_sb_info *cifs_sb, + struct smb3_fs_context *ctx); +extern int CIFSSMBQFSInfo(const unsigned int xid, struct cifs_tcon *tcon, + struct kstatfs *FSData); +extern int SMBOldQFSInfo(const unsigned int xid, struct cifs_tcon *tcon, + struct kstatfs *FSData); +extern int CIFSSMBSetFSUnixInfo(const unsigned int xid, struct cifs_tcon *tcon, + __u64 cap); + +extern int CIFSSMBQFSAttributeInfo(const unsigned int xid, + struct cifs_tcon *tcon); +extern int CIFSSMBQFSDeviceInfo(const unsigned int xid, struct cifs_tcon *tcon); +extern int CIFSSMBQFSUnixInfo(const unsigned int xid, struct cifs_tcon *tcon); +extern int CIFSSMBQFSPosixInfo(const unsigned int xid, struct cifs_tcon *tcon, + struct kstatfs *FSData); + +extern int CIFSSMBSetPathInfo(const unsigned int xid, struct cifs_tcon *tcon, + const char *fileName, const FILE_BASIC_INFO *data, + const struct nls_table *nls_codepage, + struct cifs_sb_info *cifs_sb); +extern int CIFSSMBSetFileInfo(const unsigned int xid, struct cifs_tcon *tcon, + const FILE_BASIC_INFO *data, __u16 fid, + __u32 pid_of_opener); +extern int CIFSSMBSetFileDisposition(const unsigned int xid, + struct cifs_tcon *tcon, + bool delete_file, __u16 fid, + __u32 pid_of_opener); +extern int CIFSSMBSetEOF(const unsigned int xid, struct cifs_tcon *tcon, + const char *file_name, __u64 size, + struct cifs_sb_info *cifs_sb, bool set_allocation); +extern int CIFSSMBSetFileSize(const unsigned int xid, struct cifs_tcon *tcon, + struct cifsFileInfo *cfile, __u64 size, + bool set_allocation); + +struct cifs_unix_set_info_args { + __u64 ctime; + __u64 atime; + __u64 mtime; + __u64 mode; + kuid_t uid; + kgid_t gid; + dev_t device; +}; + +extern int CIFSSMBUnixSetFileInfo(const unsigned int xid, + struct cifs_tcon *tcon, + const struct cifs_unix_set_info_args *args, + u16 fid, u32 pid_of_opener); + +extern int CIFSSMBUnixSetPathInfo(const unsigned int xid, + struct cifs_tcon *tcon, const char *file_name, + const struct cifs_unix_set_info_args *args, + const struct nls_table *nls_codepage, + int remap); + +extern int CIFSSMBMkDir(const unsigned int xid, struct inode *inode, + umode_t mode, struct cifs_tcon *tcon, + const char *name, struct cifs_sb_info *cifs_sb); +extern int CIFSSMBRmDir(const unsigned int xid, struct cifs_tcon *tcon, + const char *name, struct cifs_sb_info *cifs_sb); +extern int CIFSPOSIXDelFile(const unsigned int xid, struct cifs_tcon *tcon, + const char *name, __u16 type, + const struct nls_table *nls_codepage, + int remap_special_chars); +extern int CIFSSMBDelFile(const unsigned int xid, struct cifs_tcon *tcon, + const char *name, struct cifs_sb_info *cifs_sb); +extern int CIFSSMBRename(const unsigned int xid, struct cifs_tcon *tcon, + const char *from_name, const char *to_name, + struct cifs_sb_info *cifs_sb); +extern int CIFSSMBRenameOpenFile(const unsigned int xid, struct cifs_tcon *tcon, + int netfid, const char *target_name, + const struct nls_table *nls_codepage, + int remap_special_chars); +extern int CIFSCreateHardLink(const unsigned int xid, struct cifs_tcon *tcon, + const char *from_name, const char *to_name, + struct cifs_sb_info *cifs_sb); +extern int CIFSUnixCreateHardLink(const unsigned int xid, + struct cifs_tcon *tcon, + const char *fromName, const char *toName, + const struct nls_table *nls_codepage, + int remap_special_chars); +extern int CIFSUnixCreateSymLink(const unsigned int xid, + struct cifs_tcon *tcon, + const char *fromName, const char *toName, + const struct nls_table *nls_codepage, int remap); +extern int CIFSSMBUnixQuerySymLink(const unsigned int xid, + struct cifs_tcon *tcon, + const unsigned char *searchName, char **syminfo, + const struct nls_table *nls_codepage, int remap); +extern int CIFSSMBQuerySymLink(const unsigned int xid, struct cifs_tcon *tcon, + __u16 fid, char **symlinkinfo, + const struct nls_table *nls_codepage); +extern int CIFSSMB_set_compression(const unsigned int xid, + struct cifs_tcon *tcon, __u16 fid); +extern int CIFS_open(const unsigned int xid, struct cifs_open_parms *oparms, + int *oplock, FILE_ALL_INFO *buf); +extern int SMBLegacyOpen(const unsigned int xid, struct cifs_tcon *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 CIFSPOSIXCreate(const unsigned int xid, struct cifs_tcon *tcon, + u32 posix_flags, __u64 mode, __u16 *netfid, + FILE_UNIX_BASIC_INFO *pRetData, + __u32 *pOplock, const char *name, + const struct nls_table *nls_codepage, int remap); +extern int CIFSSMBClose(const unsigned int xid, struct cifs_tcon *tcon, + const int smb_file_id); + +extern int CIFSSMBFlush(const unsigned int xid, struct cifs_tcon *tcon, + const int smb_file_id); + +extern int CIFSSMBRead(const unsigned int xid, struct cifs_io_parms *io_parms, + unsigned int *nbytes, char **buf, + int *return_buf_type); +extern int CIFSSMBWrite(const unsigned int xid, struct cifs_io_parms *io_parms, + unsigned int *nbytes, const char *buf); +extern int CIFSSMBWrite2(const unsigned int xid, struct cifs_io_parms *io_parms, + unsigned int *nbytes, struct kvec *iov, const int nvec); +extern int CIFSGetSrvInodeNumber(const unsigned int xid, struct cifs_tcon *tcon, + const char *search_name, __u64 *inode_number, + const struct nls_table *nls_codepage, + int remap); + +extern int cifs_lockv(const unsigned int xid, struct cifs_tcon *tcon, + const __u16 netfid, const __u8 lock_type, + const __u32 num_unlock, const __u32 num_lock, + LOCKING_ANDX_RANGE *buf); +extern int CIFSSMBLock(const unsigned int xid, struct cifs_tcon *tcon, + const __u16 netfid, const __u32 netpid, const __u64 len, + const __u64 offset, const __u32 numUnlock, + const __u32 numLock, const __u8 lockType, + const bool waitFlag, const __u8 oplock_level); +extern int CIFSSMBPosixLock(const unsigned int xid, struct cifs_tcon *tcon, + const __u16 smb_file_id, const __u32 netpid, + const loff_t start_offset, const __u64 len, + struct file_lock *, const __u16 lock_type, + const bool waitFlag); +extern int CIFSSMBTDis(const unsigned int xid, struct cifs_tcon *tcon); +extern int CIFSSMBEcho(struct TCP_Server_Info *server); +extern int CIFSSMBLogoff(const unsigned int xid, struct cifs_ses *ses); + +extern struct cifs_ses *sesInfoAlloc(void); +extern void sesInfoFree(struct cifs_ses *); +extern struct cifs_tcon *tconInfoAlloc(void); +extern void tconInfoFree(struct cifs_tcon *); + +extern int cifs_sign_rqst(struct smb_rqst *rqst, struct TCP_Server_Info *server, + __u32 *pexpected_response_sequence_number); +extern int cifs_sign_smbv(struct kvec *iov, int n_vec, struct TCP_Server_Info *, + __u32 *); +extern int cifs_sign_smb(struct smb_hdr *, struct TCP_Server_Info *, __u32 *); +extern int cifs_verify_signature(struct smb_rqst *rqst, + struct TCP_Server_Info *server, + __u32 expected_sequence_number); +extern int setup_ntlmv2_rsp(struct cifs_ses *, const struct nls_table *); +extern void cifs_crypto_secmech_release(struct TCP_Server_Info *server); +extern int calc_seckey(struct cifs_ses *); +extern int generate_smb30signingkey(struct cifs_ses *ses, + struct TCP_Server_Info *server); +extern int generate_smb311signingkey(struct cifs_ses *ses, + struct TCP_Server_Info *server); + +#ifdef CONFIG_CIFS_ALLOW_INSECURE_LEGACY +extern int CIFSSMBCopy(unsigned int xid, + struct cifs_tcon *source_tcon, + const char *fromName, + const __u16 target_tid, + const char *toName, const int flags, + const struct nls_table *nls_codepage, + int remap_special_chars); +extern ssize_t CIFSSMBQAllEAs(const unsigned int xid, struct cifs_tcon *tcon, + const unsigned char *searchName, + const unsigned char *ea_name, char *EAData, + size_t bufsize, struct cifs_sb_info *cifs_sb); +extern int CIFSSMBSetEA(const unsigned int xid, struct cifs_tcon *tcon, + const char *fileName, const char *ea_name, + const void *ea_value, const __u16 ea_value_len, + const struct nls_table *nls_codepage, + struct cifs_sb_info *cifs_sb); +extern int CIFSSMBGetCIFSACL(const unsigned int xid, struct cifs_tcon *tcon, + __u16 fid, struct cifs_ntsd **acl_inf, __u32 *buflen); +extern int CIFSSMBSetCIFSACL(const unsigned int, struct cifs_tcon *, __u16, + struct cifs_ntsd *, __u32, int); +extern int CIFSSMBGetPosixACL(const unsigned int xid, struct cifs_tcon *tcon, + const unsigned char *searchName, + char *acl_inf, const int buflen, const int acl_type, + const struct nls_table *nls_codepage, int remap_special_chars); +extern int CIFSSMBSetPosixACL(const unsigned int xid, struct cifs_tcon *tcon, + const unsigned char *fileName, + const char *local_acl, const int buflen, const int acl_type, + const struct nls_table *nls_codepage, int remap_special_chars); +extern int CIFSGetExtAttr(const unsigned int xid, struct cifs_tcon *tcon, + const int netfid, __u64 *pExtAttrBits, __u64 *pMask); +#endif /* CIFS_ALLOW_INSECURE_LEGACY */ +extern void cifs_autodisable_serverino(struct cifs_sb_info *cifs_sb); +extern bool couldbe_mf_symlink(const struct cifs_fattr *fattr); +extern int check_mf_symlink(unsigned int xid, struct cifs_tcon *tcon, + struct cifs_sb_info *cifs_sb, + struct cifs_fattr *fattr, + const unsigned char *path); +extern int E_md4hash(const unsigned char *passwd, unsigned char *p16, + const struct nls_table *codepage); + +extern int +cifs_setup_volume_info(struct smb3_fs_context *ctx, const char *mntopts, const char *devname); + +extern struct TCP_Server_Info * +cifs_find_tcp_session(struct smb3_fs_context *ctx); + +extern void cifs_put_smb_ses(struct cifs_ses *ses); + +extern struct cifs_ses * +cifs_get_smb_ses(struct TCP_Server_Info *server, struct smb3_fs_context *ctx); + +void cifs_readdata_release(struct kref *refcount); +int cifs_async_readv(struct cifs_readdata *rdata); +int cifs_readv_receive(struct TCP_Server_Info *server, struct mid_q_entry *mid); + +int cifs_async_writev(struct cifs_writedata *wdata, + void (*release)(struct kref *kref)); +void cifs_writev_complete(struct work_struct *work); +struct cifs_writedata *cifs_writedata_alloc(unsigned int nr_pages, + work_func_t complete); +struct cifs_writedata *cifs_writedata_direct_alloc(struct page **pages, + work_func_t complete); +void cifs_writedata_release(struct kref *refcount); +int cifs_query_mf_symlink(unsigned int xid, struct cifs_tcon *tcon, + struct cifs_sb_info *cifs_sb, + const unsigned char *path, char *pbuf, + unsigned int *pbytes_read); +int cifs_create_mf_symlink(unsigned int xid, struct cifs_tcon *tcon, + struct cifs_sb_info *cifs_sb, + const unsigned char *path, char *pbuf, + unsigned int *pbytes_written); +int __cifs_calc_signature(struct smb_rqst *rqst, + struct TCP_Server_Info *server, char *signature, + struct shash_desc *shash); +enum securityEnum cifs_select_sectype(struct TCP_Server_Info *, + enum securityEnum); +struct cifs_aio_ctx *cifs_aio_ctx_alloc(void); +void cifs_aio_ctx_release(struct kref *refcount); +int setup_aio_ctx_iter(struct cifs_aio_ctx *ctx, struct iov_iter *iter, int rw); + +int cifs_alloc_hash(const char *name, struct shash_desc **sdesc); +void cifs_free_hash(struct shash_desc **sdesc); + +void rqst_page_get_length(const struct smb_rqst *rqst, unsigned int page, + unsigned int *len, unsigned int *offset); +struct cifs_chan * +cifs_ses_find_chan(struct cifs_ses *ses, struct TCP_Server_Info *server); +int cifs_try_adding_channels(struct cifs_sb_info *cifs_sb, struct cifs_ses *ses); +bool is_server_using_iface(struct TCP_Server_Info *server, + struct cifs_server_iface *iface); +bool is_ses_using_iface(struct cifs_ses *ses, struct cifs_server_iface *iface); +void cifs_ses_mark_for_reconnect(struct cifs_ses *ses); + +unsigned int +cifs_ses_get_chan_index(struct cifs_ses *ses, + struct TCP_Server_Info *server); +void +cifs_chan_set_in_reconnect(struct cifs_ses *ses, + struct TCP_Server_Info *server); +void +cifs_chan_clear_in_reconnect(struct cifs_ses *ses, + struct TCP_Server_Info *server); +bool +cifs_chan_in_reconnect(struct cifs_ses *ses, + struct TCP_Server_Info *server); +void +cifs_chan_set_need_reconnect(struct cifs_ses *ses, + struct TCP_Server_Info *server); +void +cifs_chan_clear_need_reconnect(struct cifs_ses *ses, + struct TCP_Server_Info *server); +bool +cifs_chan_needs_reconnect(struct cifs_ses *ses, + struct TCP_Server_Info *server); +bool +cifs_chan_is_iface_active(struct cifs_ses *ses, + struct TCP_Server_Info *server); +int +cifs_chan_update_iface(struct cifs_ses *ses, struct TCP_Server_Info *server); +int +SMB3_request_interfaces(const unsigned int xid, struct cifs_tcon *tcon, bool in_mount); + +void extract_unc_hostname(const char *unc, const char **h, size_t *len); +int copy_path_name(char *dst, const char *src); +int smb2_parse_query_directory(struct cifs_tcon *tcon, struct kvec *rsp_iov, + int resp_buftype, + struct cifs_search_info *srch_inf); + +struct super_block *cifs_get_tcp_super(struct TCP_Server_Info *server); +void cifs_put_tcp_super(struct super_block *sb); +int cifs_update_super_prepath(struct cifs_sb_info *cifs_sb, char *prefix); +char *extract_hostname(const char *unc); +char *extract_sharename(const char *unc); + +#ifdef CONFIG_CIFS_DFS_UPCALL +static inline int get_dfs_path(const unsigned int xid, struct cifs_ses *ses, + const char *old_path, + const struct nls_table *nls_codepage, + struct dfs_info3_param *referral, int remap) +{ + return dfs_cache_find(xid, ses, nls_codepage, remap, old_path, + referral, NULL); +} + +int match_target_ip(struct TCP_Server_Info *server, + const char *share, size_t share_len, + bool *result); +int cifs_inval_name_dfs_link_error(const unsigned int xid, + struct cifs_tcon *tcon, + struct cifs_sb_info *cifs_sb, + const char *full_path, + bool *islink); +#else +static inline int cifs_inval_name_dfs_link_error(const unsigned int xid, + struct cifs_tcon *tcon, + struct cifs_sb_info *cifs_sb, + const char *full_path, + bool *islink) +{ + *islink = false; + return 0; +} +#endif + +static inline int cifs_create_options(struct cifs_sb_info *cifs_sb, int options) +{ + if (cifs_sb && (backup_cred(cifs_sb))) + return options | CREATE_OPEN_BACKUP_INTENT; + else + return options; +} + +struct super_block *cifs_get_tcon_super(struct cifs_tcon *tcon); +void cifs_put_tcon_super(struct super_block *sb); +int cifs_wait_for_server_reconnect(struct TCP_Server_Info *server, bool retry); + +#endif /* _CIFSPROTO_H */ diff --git a/fs/smb/client/cifsroot.c b/fs/smb/client/cifsroot.c new file mode 100644 index 0000000000000..56ec1b233f52e --- /dev/null +++ b/fs/smb/client/cifsroot.c @@ -0,0 +1,94 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * SMB root file system support + * + * Copyright (c) 2019 Paulo Alcantara + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define DEFAULT_MNT_OPTS \ + "vers=1.0,cifsacl,mfsymlinks,rsize=1048576,wsize=65536,uid=0,gid=0," \ + "hard,rootfs" + +static char root_dev[2048] __initdata = ""; +static char root_opts[1024] __initdata = DEFAULT_MNT_OPTS; + +static __be32 __init parse_srvaddr(char *start, char *end) +{ + /* TODO: ipv6 support */ + char addr[sizeof("aaa.bbb.ccc.ddd")]; + int i = 0; + + while (start < end && i < sizeof(addr) - 1) { + if (isdigit(*start) || *start == '.') + addr[i++] = *start; + start++; + } + addr[i] = '\0'; + return in_aton(addr); +} + +/* cifsroot=///[,options] */ +static int __init cifs_root_setup(char *line) +{ + char *s; + int len; + __be32 srvaddr = htonl(INADDR_NONE); + + ROOT_DEV = Root_CIFS; + + if (strlen(line) > 3 && line[0] == '/' && line[1] == '/') { + s = strchr(&line[2], '/'); + if (!s || s[1] == '\0') + return 1; + + /* make s point to ',' or '\0' at end of line */ + s = strchrnul(s, ','); + /* len is strlen(unc) + '\0' */ + len = s - line + 1; + if (len > sizeof(root_dev)) { + pr_err("Root-CIFS: UNC path too long\n"); + return 1; + } + strscpy(root_dev, line, len); + srvaddr = parse_srvaddr(&line[2], s); + if (*s) { + int n = snprintf(root_opts, + sizeof(root_opts), "%s,%s", + DEFAULT_MNT_OPTS, s + 1); + if (n >= sizeof(root_opts)) { + pr_err("Root-CIFS: mount options string too long\n"); + root_opts[sizeof(root_opts)-1] = '\0'; + return 1; + } + } + } + + root_server_addr = srvaddr; + + return 1; +} + +__setup("cifsroot=", cifs_root_setup); + +int __init cifs_root_data(char **dev, char **opts) +{ + if (!root_dev[0] || root_server_addr == htonl(INADDR_NONE)) { + pr_err("Root-CIFS: no SMB server address\n"); + return -1; + } + + *dev = root_dev; + *opts = root_opts; + + return 0; +} diff --git a/fs/smb/client/cifssmb.c b/fs/smb/client/cifssmb.c new file mode 100644 index 0000000000000..c90d4ec9292ca --- /dev/null +++ b/fs/smb/client/cifssmb.c @@ -0,0 +1,5873 @@ +// SPDX-License-Identifier: LGPL-2.1 +/* + * + * Copyright (C) International Business Machines Corp., 2002,2010 + * Author(s): Steve French (sfrench@us.ibm.com) + * + * Contains the routines for constructing the SMB PDUs themselves + * + */ + + /* SMB/CIFS PDU handling routines here - except for leftovers in connect.c */ + /* These are mostly routines that operate on a pathname, or on a tree id */ + /* (mounted volume), but there are eight handle based routines which must be */ + /* treated slightly differently for reconnection purposes since we never */ + /* want to reuse a stale file handle and only the caller knows the file info */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "cifspdu.h" +#include "cifsglob.h" +#include "cifsacl.h" +#include "cifsproto.h" +#include "cifs_unicode.h" +#include "cifs_debug.h" +#include "fscache.h" +#include "smbdirect.h" +#ifdef CONFIG_CIFS_DFS_UPCALL +#include "dfs_cache.h" +#endif + +#ifdef CONFIG_CIFS_POSIX +static struct { + int index; + char *name; +} protocols[] = { + {CIFS_PROT, "\2NT LM 0.12"}, + {POSIX_PROT, "\2POSIX 2"}, + {BAD_PROT, "\2"} +}; +#else +static struct { + int index; + char *name; +} protocols[] = { + {CIFS_PROT, "\2NT LM 0.12"}, + {BAD_PROT, "\2"} +}; +#endif + +/* define the number of elements in the cifs dialect array */ +#ifdef CONFIG_CIFS_POSIX +#define CIFS_NUM_PROT 2 +#else /* not posix */ +#define CIFS_NUM_PROT 1 +#endif /* CIFS_POSIX */ + + +/* reconnect the socket, tcon, and smb session if needed */ +static int +cifs_reconnect_tcon(struct cifs_tcon *tcon, int smb_command) +{ + int rc; + struct cifs_ses *ses; + struct TCP_Server_Info *server; + struct nls_table *nls_codepage; + + /* + * SMBs NegProt, SessSetup, uLogoff do not have tcon yet so check for + * tcp and smb session status done differently for those three - in the + * calling routine + */ + if (!tcon) + return 0; + + ses = tcon->ses; + server = ses->server; + + /* + * only tree disconnect, open, and write, (and ulogoff which does not + * have tcon) are allowed as we start umount + */ + spin_lock(&tcon->tc_lock); + if (tcon->status == TID_EXITING) { + if (smb_command != SMB_COM_TREE_DISCONNECT) { + spin_unlock(&tcon->tc_lock); + cifs_dbg(FYI, "can not send cmd %d while umounting\n", + smb_command); + return -ENODEV; + } + } + spin_unlock(&tcon->tc_lock); + + rc = cifs_wait_for_server_reconnect(server, tcon->retry); + if (rc) + return rc; + + spin_lock(&ses->chan_lock); + if (!cifs_chan_needs_reconnect(ses, server) && !tcon->need_reconnect) { + spin_unlock(&ses->chan_lock); + return 0; + } + spin_unlock(&ses->chan_lock); + + nls_codepage = load_nls_default(); + + /* + * Recheck after acquire mutex. If another thread is negotiating + * and the server never sends an answer the socket will be closed + * and tcpStatus set to reconnect. + */ + spin_lock(&server->srv_lock); + if (server->tcpStatus == CifsNeedReconnect) { + spin_unlock(&server->srv_lock); + rc = -EHOSTDOWN; + goto out; + } + spin_unlock(&server->srv_lock); + + /* + * need to prevent multiple threads trying to simultaneously + * reconnect the same SMB session + */ + spin_lock(&ses->chan_lock); + if (!cifs_chan_needs_reconnect(ses, server)) { + spin_unlock(&ses->chan_lock); + + /* this means that we only need to tree connect */ + if (tcon->need_reconnect) + goto skip_sess_setup; + + rc = -EHOSTDOWN; + goto out; + } + spin_unlock(&ses->chan_lock); + + mutex_lock(&ses->session_mutex); + rc = cifs_negotiate_protocol(0, ses, server); + if (!rc) + rc = cifs_setup_session(0, ses, server, nls_codepage); + + /* do we need to reconnect tcon? */ + if (rc || !tcon->need_reconnect) { + mutex_unlock(&ses->session_mutex); + goto out; + } + +skip_sess_setup: + cifs_mark_open_files_invalid(tcon); + rc = cifs_tree_connect(0, tcon, nls_codepage); + mutex_unlock(&ses->session_mutex); + cifs_dbg(FYI, "reconnect tcon rc = %d\n", rc); + + if (rc) { + pr_warn_once("reconnect tcon failed rc = %d\n", rc); + goto out; + } + + atomic_inc(&tconInfoReconnectCount); + + /* tell server Unix caps we support */ + if (cap_unix(ses)) + reset_cifs_unix_caps(0, tcon, NULL, NULL); + + /* + * Removed call to reopen open files here. It is safer (and faster) to + * reopen files one at a time as needed in read and write. + * + * FIXME: what about file locks? don't we need to reclaim them ASAP? + */ + +out: + /* + * Check if handle based operation so we know whether we can continue + * or not without returning to caller to reset file handle + */ + switch (smb_command) { + case SMB_COM_READ_ANDX: + case SMB_COM_WRITE_ANDX: + case SMB_COM_CLOSE: + case SMB_COM_FIND_CLOSE2: + case SMB_COM_LOCKING_ANDX: + rc = -EAGAIN; + } + + unload_nls(nls_codepage); + return rc; +} + +/* Allocate and return pointer to an SMB request buffer, and set basic + SMB information in the SMB header. If the return code is zero, this + function must have filled in request_buf pointer */ +static int +small_smb_init(int smb_command, int wct, struct cifs_tcon *tcon, + void **request_buf) +{ + int rc; + + rc = cifs_reconnect_tcon(tcon, smb_command); + if (rc) + return rc; + + *request_buf = cifs_small_buf_get(); + if (*request_buf == NULL) { + /* BB should we add a retry in here if not a writepage? */ + return -ENOMEM; + } + + header_assemble((struct smb_hdr *) *request_buf, smb_command, + tcon, wct); + + if (tcon != NULL) + cifs_stats_inc(&tcon->num_smbs_sent); + + return 0; +} + +int +small_smb_init_no_tc(const int smb_command, const int wct, + struct cifs_ses *ses, void **request_buf) +{ + int rc; + struct smb_hdr *buffer; + + rc = small_smb_init(smb_command, wct, NULL, request_buf); + if (rc) + return rc; + + buffer = (struct smb_hdr *)*request_buf; + buffer->Mid = get_next_mid(ses->server); + if (ses->capabilities & CAP_UNICODE) + buffer->Flags2 |= SMBFLG2_UNICODE; + if (ses->capabilities & CAP_STATUS32) + buffer->Flags2 |= SMBFLG2_ERR_STATUS; + + /* uid, tid can stay at zero as set in header assemble */ + + /* BB add support for turning on the signing when + this function is used after 1st of session setup requests */ + + return rc; +} + +/* If the return code is zero, this function must fill in request_buf pointer */ +static int +__smb_init(int smb_command, int wct, struct cifs_tcon *tcon, + void **request_buf, void **response_buf) +{ + *request_buf = cifs_buf_get(); + if (*request_buf == NULL) { + /* BB should we add a retry in here if not a writepage? */ + return -ENOMEM; + } + /* Although the original thought was we needed the response buf for */ + /* potential retries of smb operations it turns out we can determine */ + /* from the mid flags when the request buffer can be resent without */ + /* having to use a second distinct buffer for the response */ + if (response_buf) + *response_buf = *request_buf; + + header_assemble((struct smb_hdr *) *request_buf, smb_command, tcon, + wct); + + if (tcon != NULL) + cifs_stats_inc(&tcon->num_smbs_sent); + + return 0; +} + +/* If the return code is zero, this function must fill in request_buf pointer */ +static int +smb_init(int smb_command, int wct, struct cifs_tcon *tcon, + void **request_buf, void **response_buf) +{ + int rc; + + rc = cifs_reconnect_tcon(tcon, smb_command); + if (rc) + return rc; + + return __smb_init(smb_command, wct, tcon, request_buf, response_buf); +} + +static int +smb_init_no_reconnect(int smb_command, int wct, struct cifs_tcon *tcon, + void **request_buf, void **response_buf) +{ + spin_lock(&tcon->ses->chan_lock); + if (cifs_chan_needs_reconnect(tcon->ses, tcon->ses->server) || + tcon->need_reconnect) { + spin_unlock(&tcon->ses->chan_lock); + return -EHOSTDOWN; + } + spin_unlock(&tcon->ses->chan_lock); + + return __smb_init(smb_command, wct, tcon, request_buf, response_buf); +} + +static int validate_t2(struct smb_t2_rsp *pSMB) +{ + unsigned int total_size; + + /* check for plausible wct */ + if (pSMB->hdr.WordCount < 10) + goto vt2_err; + + /* check for parm and data offset going beyond end of smb */ + if (get_unaligned_le16(&pSMB->t2_rsp.ParameterOffset) > 1024 || + get_unaligned_le16(&pSMB->t2_rsp.DataOffset) > 1024) + goto vt2_err; + + total_size = get_unaligned_le16(&pSMB->t2_rsp.ParameterCount); + if (total_size >= 512) + goto vt2_err; + + /* check that bcc is at least as big as parms + data, and that it is + * less than negotiated smb buffer + */ + total_size += get_unaligned_le16(&pSMB->t2_rsp.DataCount); + if (total_size > get_bcc(&pSMB->hdr) || + total_size >= CIFSMaxBufSize + MAX_CIFS_HDR_SIZE) + goto vt2_err; + + return 0; +vt2_err: + cifs_dump_mem("Invalid transact2 SMB: ", (char *)pSMB, + sizeof(struct smb_t2_rsp) + 16); + return -EINVAL; +} + +static int +decode_ext_sec_blob(struct cifs_ses *ses, NEGOTIATE_RSP *pSMBr) +{ + int rc = 0; + u16 count; + char *guid = pSMBr->u.extended_response.GUID; + struct TCP_Server_Info *server = ses->server; + + count = get_bcc(&pSMBr->hdr); + if (count < SMB1_CLIENT_GUID_SIZE) + return -EIO; + + spin_lock(&cifs_tcp_ses_lock); + if (server->srv_count > 1) { + spin_unlock(&cifs_tcp_ses_lock); + if (memcmp(server->server_GUID, guid, SMB1_CLIENT_GUID_SIZE) != 0) { + cifs_dbg(FYI, "server UID changed\n"); + memcpy(server->server_GUID, guid, SMB1_CLIENT_GUID_SIZE); + } + } else { + spin_unlock(&cifs_tcp_ses_lock); + memcpy(server->server_GUID, guid, SMB1_CLIENT_GUID_SIZE); + } + + if (count == SMB1_CLIENT_GUID_SIZE) { + server->sec_ntlmssp = true; + } else { + count -= SMB1_CLIENT_GUID_SIZE; + rc = decode_negTokenInit( + pSMBr->u.extended_response.SecurityBlob, count, server); + if (rc != 1) + return -EINVAL; + } + + return 0; +} + +static bool +should_set_ext_sec_flag(enum securityEnum sectype) +{ + switch (sectype) { + case RawNTLMSSP: + case Kerberos: + return true; + case Unspecified: + if (global_secflags & + (CIFSSEC_MAY_KRB5 | CIFSSEC_MAY_NTLMSSP)) + return true; + fallthrough; + default: + return false; + } +} + +int +CIFSSMBNegotiate(const unsigned int xid, + struct cifs_ses *ses, + struct TCP_Server_Info *server) +{ + NEGOTIATE_REQ *pSMB; + NEGOTIATE_RSP *pSMBr; + int rc = 0; + int bytes_returned; + int i; + u16 count; + + if (!server) { + WARN(1, "%s: server is NULL!\n", __func__); + return -EIO; + } + + rc = smb_init(SMB_COM_NEGOTIATE, 0, NULL /* no tcon yet */ , + (void **) &pSMB, (void **) &pSMBr); + if (rc) + return rc; + + pSMB->hdr.Mid = get_next_mid(server); + pSMB->hdr.Flags2 |= (SMBFLG2_UNICODE | SMBFLG2_ERR_STATUS); + + if (should_set_ext_sec_flag(ses->sectype)) { + cifs_dbg(FYI, "Requesting extended security\n"); + pSMB->hdr.Flags2 |= SMBFLG2_EXT_SEC; + } + + count = 0; + /* + * We know that all the name entries in the protocols array + * are short (< 16 bytes anyway) and are NUL terminated. + */ + for (i = 0; i < CIFS_NUM_PROT; i++) { + size_t len = strlen(protocols[i].name) + 1; + + memcpy(&pSMB->DialectsArray[count], protocols[i].name, len); + count += len; + } + inc_rfc1001_len(pSMB, count); + pSMB->ByteCount = cpu_to_le16(count); + + rc = SendReceive(xid, ses, (struct smb_hdr *) pSMB, + (struct smb_hdr *) pSMBr, &bytes_returned, 0); + if (rc != 0) + goto neg_err_exit; + + server->dialect = le16_to_cpu(pSMBr->DialectIndex); + cifs_dbg(FYI, "Dialect: %d\n", server->dialect); + /* Check wct = 1 error case */ + if ((pSMBr->hdr.WordCount <= 13) || (server->dialect == BAD_PROT)) { + /* core returns wct = 1, but we do not ask for core - otherwise + small wct just comes when dialect index is -1 indicating we + could not negotiate a common dialect */ + rc = -EOPNOTSUPP; + goto neg_err_exit; + } else if (pSMBr->hdr.WordCount != 17) { + /* unknown wct */ + rc = -EOPNOTSUPP; + goto neg_err_exit; + } + /* else wct == 17, NTLM or better */ + + server->sec_mode = pSMBr->SecurityMode; + if ((server->sec_mode & SECMODE_USER) == 0) + cifs_dbg(FYI, "share mode security\n"); + + /* one byte, so no need to convert this or EncryptionKeyLen from + little endian */ + server->maxReq = min_t(unsigned int, le16_to_cpu(pSMBr->MaxMpxCount), + cifs_max_pending); + set_credits(server, server->maxReq); + /* probably no need to store and check maxvcs */ + server->maxBuf = le32_to_cpu(pSMBr->MaxBufferSize); + /* set up max_read for readahead check */ + server->max_read = server->maxBuf; + server->max_rw = le32_to_cpu(pSMBr->MaxRawSize); + cifs_dbg(NOISY, "Max buf = %d\n", ses->server->maxBuf); + server->capabilities = le32_to_cpu(pSMBr->Capabilities); + server->timeAdj = (int)(__s16)le16_to_cpu(pSMBr->ServerTimeZone); + server->timeAdj *= 60; + + if (pSMBr->EncryptionKeyLength == CIFS_CRYPTO_KEY_SIZE) { + server->negflavor = CIFS_NEGFLAVOR_UNENCAP; + memcpy(ses->server->cryptkey, pSMBr->u.EncryptionKey, + CIFS_CRYPTO_KEY_SIZE); + } else if (pSMBr->hdr.Flags2 & SMBFLG2_EXT_SEC || + server->capabilities & CAP_EXTENDED_SECURITY) { + server->negflavor = CIFS_NEGFLAVOR_EXTENDED; + rc = decode_ext_sec_blob(ses, pSMBr); + } else if (server->sec_mode & SECMODE_PW_ENCRYPT) { + rc = -EIO; /* no crypt key only if plain text pwd */ + } else { + server->negflavor = CIFS_NEGFLAVOR_UNENCAP; + server->capabilities &= ~CAP_EXTENDED_SECURITY; + } + + if (!rc) + rc = cifs_enable_signing(server, ses->sign); +neg_err_exit: + cifs_buf_release(pSMB); + + cifs_dbg(FYI, "negprot rc %d\n", rc); + return rc; +} + +int +CIFSSMBTDis(const unsigned int xid, struct cifs_tcon *tcon) +{ + struct smb_hdr *smb_buffer; + int rc = 0; + + cifs_dbg(FYI, "In tree disconnect\n"); + + /* BB: do we need to check this? These should never be NULL. */ + if ((tcon->ses == NULL) || (tcon->ses->server == NULL)) + return -EIO; + + /* + * No need to return error on this operation if tid invalidated and + * closed on server already e.g. due to tcp session crashing. Also, + * the tcon is no longer on the list, so no need to take lock before + * checking this. + */ + spin_lock(&tcon->ses->chan_lock); + if ((tcon->need_reconnect) || CIFS_ALL_CHANS_NEED_RECONNECT(tcon->ses)) { + spin_unlock(&tcon->ses->chan_lock); + return -EIO; + } + spin_unlock(&tcon->ses->chan_lock); + + rc = small_smb_init(SMB_COM_TREE_DISCONNECT, 0, tcon, + (void **)&smb_buffer); + if (rc) + return rc; + + rc = SendReceiveNoRsp(xid, tcon->ses, (char *)smb_buffer, 0); + cifs_small_buf_release(smb_buffer); + if (rc) + cifs_dbg(FYI, "Tree disconnect failed %d\n", rc); + + /* No need to return error on this operation if tid invalidated and + closed on server already e.g. due to tcp session crashing */ + if (rc == -EAGAIN) + rc = 0; + + return rc; +} + +/* + * This is a no-op for now. We're not really interested in the reply, but + * rather in the fact that the server sent one and that server->lstrp + * gets updated. + * + * FIXME: maybe we should consider checking that the reply matches request? + */ +static void +cifs_echo_callback(struct mid_q_entry *mid) +{ + struct TCP_Server_Info *server = mid->callback_data; + struct cifs_credits credits = { .value = 1, .instance = 0 }; + + release_mid(mid); + add_credits(server, &credits, CIFS_ECHO_OP); +} + +int +CIFSSMBEcho(struct TCP_Server_Info *server) +{ + ECHO_REQ *smb; + int rc = 0; + struct kvec iov[2]; + struct smb_rqst rqst = { .rq_iov = iov, + .rq_nvec = 2 }; + + cifs_dbg(FYI, "In echo request\n"); + + rc = small_smb_init(SMB_COM_ECHO, 0, NULL, (void **)&smb); + if (rc) + return rc; + + if (server->capabilities & CAP_UNICODE) + smb->hdr.Flags2 |= SMBFLG2_UNICODE; + + /* set up echo request */ + smb->hdr.Tid = 0xffff; + smb->hdr.WordCount = 1; + put_unaligned_le16(1, &smb->EchoCount); + put_bcc(1, &smb->hdr); + smb->Data[0] = 'a'; + inc_rfc1001_len(smb, 3); + + iov[0].iov_len = 4; + iov[0].iov_base = smb; + iov[1].iov_len = get_rfc1002_length(smb); + iov[1].iov_base = (char *)smb + 4; + + rc = cifs_call_async(server, &rqst, NULL, cifs_echo_callback, NULL, + server, CIFS_NON_BLOCKING | CIFS_ECHO_OP, NULL); + if (rc) + cifs_dbg(FYI, "Echo request failed: %d\n", rc); + + cifs_small_buf_release(smb); + + return rc; +} + +int +CIFSSMBLogoff(const unsigned int xid, struct cifs_ses *ses) +{ + LOGOFF_ANDX_REQ *pSMB; + int rc = 0; + + cifs_dbg(FYI, "In SMBLogoff for session disconnect\n"); + + /* + * BB: do we need to check validity of ses and server? They should + * always be valid since we have an active reference. If not, that + * should probably be a BUG() + */ + if (!ses || !ses->server) + return -EIO; + + mutex_lock(&ses->session_mutex); + spin_lock(&ses->chan_lock); + if (CIFS_ALL_CHANS_NEED_RECONNECT(ses)) { + spin_unlock(&ses->chan_lock); + goto session_already_dead; /* no need to send SMBlogoff if uid + already closed due to reconnect */ + } + spin_unlock(&ses->chan_lock); + + rc = small_smb_init(SMB_COM_LOGOFF_ANDX, 2, NULL, (void **)&pSMB); + if (rc) { + mutex_unlock(&ses->session_mutex); + return rc; + } + + pSMB->hdr.Mid = get_next_mid(ses->server); + + if (ses->server->sign) + pSMB->hdr.Flags2 |= SMBFLG2_SECURITY_SIGNATURE; + + pSMB->hdr.Uid = ses->Suid; + + pSMB->AndXCommand = 0xFF; + rc = SendReceiveNoRsp(xid, ses, (char *) pSMB, 0); + cifs_small_buf_release(pSMB); +session_already_dead: + mutex_unlock(&ses->session_mutex); + + /* if session dead then we do not need to do ulogoff, + since server closed smb session, no sense reporting + error */ + if (rc == -EAGAIN) + rc = 0; + return rc; +} + +int +CIFSPOSIXDelFile(const unsigned int xid, struct cifs_tcon *tcon, + const char *fileName, __u16 type, + const struct nls_table *nls_codepage, int remap) +{ + TRANSACTION2_SPI_REQ *pSMB = NULL; + TRANSACTION2_SPI_RSP *pSMBr = NULL; + struct unlink_psx_rq *pRqD; + int name_len; + int rc = 0; + int bytes_returned = 0; + __u16 params, param_offset, offset, byte_count; + + cifs_dbg(FYI, "In POSIX delete\n"); +PsxDelete: + rc = smb_init(SMB_COM_TRANSACTION2, 15, tcon, (void **) &pSMB, + (void **) &pSMBr); + if (rc) + return rc; + + if (pSMB->hdr.Flags2 & SMBFLG2_UNICODE) { + name_len = + cifsConvertToUTF16((__le16 *) pSMB->FileName, fileName, + PATH_MAX, nls_codepage, remap); + name_len++; /* trailing null */ + name_len *= 2; + } else { + name_len = copy_path_name(pSMB->FileName, fileName); + } + + params = 6 + name_len; + pSMB->MaxParameterCount = cpu_to_le16(2); + pSMB->MaxDataCount = 0; /* BB double check this with jra */ + pSMB->MaxSetupCount = 0; + pSMB->Reserved = 0; + pSMB->Flags = 0; + pSMB->Timeout = 0; + pSMB->Reserved2 = 0; + param_offset = offsetof(struct smb_com_transaction2_spi_req, + InformationLevel) - 4; + offset = param_offset + params; + + /* Setup pointer to Request Data (inode type). + * Note that SMB offsets are from the beginning of SMB which is 4 bytes + * in, after RFC1001 field + */ + pRqD = (struct unlink_psx_rq *)((char *)(pSMB) + offset + 4); + pRqD->type = cpu_to_le16(type); + pSMB->ParameterOffset = cpu_to_le16(param_offset); + pSMB->DataOffset = cpu_to_le16(offset); + pSMB->SetupCount = 1; + pSMB->Reserved3 = 0; + pSMB->SubCommand = cpu_to_le16(TRANS2_SET_PATH_INFORMATION); + byte_count = 3 /* pad */ + params + sizeof(struct unlink_psx_rq); + + pSMB->DataCount = cpu_to_le16(sizeof(struct unlink_psx_rq)); + pSMB->TotalDataCount = cpu_to_le16(sizeof(struct unlink_psx_rq)); + pSMB->ParameterCount = cpu_to_le16(params); + pSMB->TotalParameterCount = pSMB->ParameterCount; + pSMB->InformationLevel = cpu_to_le16(SMB_POSIX_UNLINK); + pSMB->Reserved4 = 0; + inc_rfc1001_len(pSMB, 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) + cifs_dbg(FYI, "Posix delete returned %d\n", rc); + cifs_buf_release(pSMB); + + cifs_stats_inc(&tcon->stats.cifs_stats.num_deletes); + + if (rc == -EAGAIN) + goto PsxDelete; + + return rc; +} + +int +CIFSSMBDelFile(const unsigned int xid, struct cifs_tcon *tcon, const char *name, + struct cifs_sb_info *cifs_sb) +{ + DELETE_FILE_REQ *pSMB = NULL; + DELETE_FILE_RSP *pSMBr = NULL; + int rc = 0; + int bytes_returned; + int name_len; + int remap = cifs_remap(cifs_sb); + +DelFileRetry: + rc = smb_init(SMB_COM_DELETE, 1, tcon, (void **) &pSMB, + (void **) &pSMBr); + if (rc) + return rc; + + if (pSMB->hdr.Flags2 & SMBFLG2_UNICODE) { + name_len = cifsConvertToUTF16((__le16 *) pSMB->fileName, name, + PATH_MAX, cifs_sb->local_nls, + remap); + name_len++; /* trailing null */ + name_len *= 2; + } else { + name_len = copy_path_name(pSMB->fileName, name); + } + pSMB->SearchAttributes = + cpu_to_le16(ATTR_READONLY | ATTR_HIDDEN | ATTR_SYSTEM); + pSMB->BufferFormat = 0x04; + inc_rfc1001_len(pSMB, name_len + 1); + 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->stats.cifs_stats.num_deletes); + if (rc) + cifs_dbg(FYI, "Error in RMFile = %d\n", rc); + + cifs_buf_release(pSMB); + if (rc == -EAGAIN) + goto DelFileRetry; + + return rc; +} + +int +CIFSSMBRmDir(const unsigned int xid, struct cifs_tcon *tcon, const char *name, + struct cifs_sb_info *cifs_sb) +{ + DELETE_DIRECTORY_REQ *pSMB = NULL; + DELETE_DIRECTORY_RSP *pSMBr = NULL; + int rc = 0; + int bytes_returned; + int name_len; + int remap = cifs_remap(cifs_sb); + + cifs_dbg(FYI, "In CIFSSMBRmDir\n"); +RmDirRetry: + rc = smb_init(SMB_COM_DELETE_DIRECTORY, 0, tcon, (void **) &pSMB, + (void **) &pSMBr); + if (rc) + return rc; + + if (pSMB->hdr.Flags2 & SMBFLG2_UNICODE) { + name_len = cifsConvertToUTF16((__le16 *) pSMB->DirName, name, + PATH_MAX, cifs_sb->local_nls, + remap); + name_len++; /* trailing null */ + name_len *= 2; + } else { + name_len = copy_path_name(pSMB->DirName, name); + } + + pSMB->BufferFormat = 0x04; + inc_rfc1001_len(pSMB, name_len + 1); + 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->stats.cifs_stats.num_rmdirs); + if (rc) + cifs_dbg(FYI, "Error in RMDir = %d\n", rc); + + cifs_buf_release(pSMB); + if (rc == -EAGAIN) + goto RmDirRetry; + return rc; +} + +int +CIFSSMBMkDir(const unsigned int xid, struct inode *inode, umode_t mode, + struct cifs_tcon *tcon, const char *name, + struct cifs_sb_info *cifs_sb) +{ + int rc = 0; + CREATE_DIRECTORY_REQ *pSMB = NULL; + CREATE_DIRECTORY_RSP *pSMBr = NULL; + int bytes_returned; + int name_len; + int remap = cifs_remap(cifs_sb); + + cifs_dbg(FYI, "In CIFSSMBMkDir\n"); +MkDirRetry: + rc = smb_init(SMB_COM_CREATE_DIRECTORY, 0, tcon, (void **) &pSMB, + (void **) &pSMBr); + if (rc) + return rc; + + if (pSMB->hdr.Flags2 & SMBFLG2_UNICODE) { + name_len = cifsConvertToUTF16((__le16 *) pSMB->DirName, name, + PATH_MAX, cifs_sb->local_nls, + remap); + name_len++; /* trailing null */ + name_len *= 2; + } else { + name_len = copy_path_name(pSMB->DirName, name); + } + + pSMB->BufferFormat = 0x04; + inc_rfc1001_len(pSMB, name_len + 1); + 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->stats.cifs_stats.num_mkdirs); + if (rc) + cifs_dbg(FYI, "Error in Mkdir = %d\n", rc); + + cifs_buf_release(pSMB); + if (rc == -EAGAIN) + goto MkDirRetry; + return rc; +} + +int +CIFSPOSIXCreate(const unsigned int xid, struct cifs_tcon *tcon, + __u32 posix_flags, __u64 mode, __u16 *netfid, + FILE_UNIX_BASIC_INFO *pRetData, __u32 *pOplock, + const char *name, const struct nls_table *nls_codepage, + int remap) +{ + TRANSACTION2_SPI_REQ *pSMB = NULL; + TRANSACTION2_SPI_RSP *pSMBr = NULL; + int name_len; + int rc = 0; + int bytes_returned = 0; + __u16 params, param_offset, offset, byte_count, count; + OPEN_PSX_REQ *pdata; + OPEN_PSX_RSP *psx_rsp; + + cifs_dbg(FYI, "In POSIX Create\n"); +PsxCreat: + rc = smb_init(SMB_COM_TRANSACTION2, 15, tcon, (void **) &pSMB, + (void **) &pSMBr); + if (rc) + return rc; + + if (pSMB->hdr.Flags2 & SMBFLG2_UNICODE) { + name_len = + cifsConvertToUTF16((__le16 *) pSMB->FileName, name, + PATH_MAX, nls_codepage, remap); + name_len++; /* trailing null */ + name_len *= 2; + } else { + name_len = copy_path_name(pSMB->FileName, name); + } + + params = 6 + name_len; + count = sizeof(OPEN_PSX_REQ); + pSMB->MaxParameterCount = cpu_to_le16(2); + pSMB->MaxDataCount = cpu_to_le16(1000); /* large enough */ + pSMB->MaxSetupCount = 0; + pSMB->Reserved = 0; + pSMB->Flags = 0; + pSMB->Timeout = 0; + pSMB->Reserved2 = 0; + param_offset = offsetof(struct smb_com_transaction2_spi_req, + InformationLevel) - 4; + offset = param_offset + params; + /* SMB offsets are from the beginning of SMB which is 4 bytes in, after RFC1001 field */ + pdata = (OPEN_PSX_REQ *)((char *)(pSMB) + offset + 4); + pdata->Level = cpu_to_le16(SMB_QUERY_FILE_UNIX_BASIC); + pdata->Permissions = cpu_to_le64(mode); + pdata->PosixOpenFlags = cpu_to_le32(posix_flags); + pdata->OpenFlags = cpu_to_le32(*pOplock); + pSMB->ParameterOffset = cpu_to_le16(param_offset); + pSMB->DataOffset = cpu_to_le16(offset); + pSMB->SetupCount = 1; + pSMB->Reserved3 = 0; + pSMB->SubCommand = cpu_to_le16(TRANS2_SET_PATH_INFORMATION); + byte_count = 3 /* pad */ + params + count; + + pSMB->DataCount = cpu_to_le16(count); + pSMB->ParameterCount = cpu_to_le16(params); + pSMB->TotalDataCount = pSMB->DataCount; + pSMB->TotalParameterCount = pSMB->ParameterCount; + pSMB->InformationLevel = cpu_to_le16(SMB_POSIX_OPEN); + pSMB->Reserved4 = 0; + inc_rfc1001_len(pSMB, 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) { + cifs_dbg(FYI, "Posix create returned %d\n", rc); + goto psx_create_err; + } + + cifs_dbg(FYI, "copying inode info\n"); + rc = validate_t2((struct smb_t2_rsp *)pSMBr); + + if (rc || get_bcc(&pSMBr->hdr) < sizeof(OPEN_PSX_RSP)) { + rc = -EIO; /* bad smb */ + goto psx_create_err; + } + + /* copy return information to pRetData */ + psx_rsp = (OPEN_PSX_RSP *)((char *) &pSMBr->hdr.Protocol + + le16_to_cpu(pSMBr->t2.DataOffset)); + + *pOplock = le16_to_cpu(psx_rsp->OplockFlags); + if (netfid) + *netfid = psx_rsp->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? */ + if (cpu_to_le32(FILE_CREATE) == psx_rsp->CreateAction) + *pOplock |= CIFS_CREATE_ACTION; + /* check to make sure response data is there */ + if (psx_rsp->ReturnedLevel != cpu_to_le16(SMB_QUERY_FILE_UNIX_BASIC)) { + pRetData->Type = cpu_to_le32(-1); /* unknown */ + cifs_dbg(NOISY, "unknown type\n"); + } else { + if (get_bcc(&pSMBr->hdr) < sizeof(OPEN_PSX_RSP) + + sizeof(FILE_UNIX_BASIC_INFO)) { + cifs_dbg(VFS, "Open response data too small\n"); + pRetData->Type = cpu_to_le32(-1); + goto psx_create_err; + } + memcpy((char *) pRetData, + (char *)psx_rsp + sizeof(OPEN_PSX_RSP), + sizeof(FILE_UNIX_BASIC_INFO)); + } + +psx_create_err: + cifs_buf_release(pSMB); + + if (posix_flags & SMB_O_DIRECTORY) + cifs_stats_inc(&tcon->stats.cifs_stats.num_posixmkdirs); + else + cifs_stats_inc(&tcon->stats.cifs_stats.num_posixopens); + + if (rc == -EAGAIN) + goto PsxCreat; + + 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: + cifs_dbg(FYI, "unknown disposition %d\n", disposition); + ofun = SMBOPEN_OAPPEND; /* regular open */ + } + return ofun; +} + +static int +access_flags_to_smbopen_mode(const int access_flags) +{ + int masked_flags = access_flags & (GENERIC_READ | GENERIC_WRITE); + + if (masked_flags == GENERIC_READ) + return SMBOPEN_READ; + else if (masked_flags == GENERIC_WRITE) + return SMBOPEN_WRITE; + + /* just go for read/write */ + return SMBOPEN_READWRITE; +} + +int +SMBLegacyOpen(const unsigned int xid, struct cifs_tcon *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; + 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 = + cifsConvertToUTF16((__le16 *) (pSMB->fileName + 1), + fileName, PATH_MAX, nls_codepage, remap); + name_len++; /* trailing null */ + name_len *= 2; + } else { + count = 0; /* no pad */ + name_len = copy_path_name(pSMB->fileName, fileName); + } + 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); + pSMB->Mode = cpu_to_le16(access_flags_to_smbopen_mode(access_flags)); + 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 /* BB FIXME BB */ + pSMB->FileAttributes = cpu_to_le16(0/*ATTR_NORMAL*/); + + if (create_options & CREATE_OPTION_READONLY) + pSMB->FileAttributes |= cpu_to_le16(ATTR_READONLY); + + /* 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; + inc_rfc1001_len(pSMB, count); + + pSMB->ByteCount = cpu_to_le16(count); + rc = SendReceive(xid, tcon->ses, (struct smb_hdr *) pSMB, + (struct smb_hdr *)pSMBr, &bytes_returned, 0); + cifs_stats_inc(&tcon->stats.cifs_stats.num_opens); + if (rc) { + cifs_dbg(FYI, "Error in Open = %d\n", rc); + } else { + /* BB verify if wct == 15 */ + +/* *pOplock = pSMBr->OplockLevel; */ /* BB take from action field*/ + + *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); + pfile_info->DeletePending = 0; + } + } + + cifs_buf_release(pSMB); + if (rc == -EAGAIN) + goto OldOpenRetry; + return rc; +} + +int +CIFS_open(const unsigned int xid, struct cifs_open_parms *oparms, int *oplock, + FILE_ALL_INFO *buf) +{ + int rc; + OPEN_REQ *req = NULL; + OPEN_RSP *rsp = NULL; + int bytes_returned; + int name_len; + __u16 count; + struct cifs_sb_info *cifs_sb = oparms->cifs_sb; + struct cifs_tcon *tcon = oparms->tcon; + int remap = cifs_remap(cifs_sb); + const struct nls_table *nls = cifs_sb->local_nls; + int create_options = oparms->create_options; + int desired_access = oparms->desired_access; + int disposition = oparms->disposition; + const char *path = oparms->path; + +openRetry: + rc = smb_init(SMB_COM_NT_CREATE_ANDX, 24, tcon, (void **)&req, + (void **)&rsp); + if (rc) + return rc; + + /* no commands go after this */ + req->AndXCommand = 0xFF; + + if (req->hdr.Flags2 & SMBFLG2_UNICODE) { + /* account for one byte pad to word boundary */ + count = 1; + name_len = cifsConvertToUTF16((__le16 *)(req->fileName + 1), + path, PATH_MAX, nls, remap); + /* trailing null */ + name_len++; + name_len *= 2; + req->NameLength = cpu_to_le16(name_len); + } else { + /* BB improve check for buffer overruns BB */ + /* no pad */ + count = 0; + name_len = copy_path_name(req->fileName, path); + req->NameLength = cpu_to_le16(name_len); + } + + if (*oplock & REQ_OPLOCK) + req->OpenFlags = cpu_to_le32(REQ_OPLOCK); + else if (*oplock & REQ_BATCHOPLOCK) + req->OpenFlags = cpu_to_le32(REQ_BATCHOPLOCK); + + req->DesiredAccess = cpu_to_le32(desired_access); + req->AllocationSize = 0; + + /* + * 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) + req->FileAttributes = cpu_to_le32(ATTR_SYSTEM); + else + req->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. + */ + if (tcon->ses->capabilities & CAP_UNIX) + req->FileAttributes |= cpu_to_le32(ATTR_POSIX_SEMANTICS); + + if (create_options & CREATE_OPTION_READONLY) + req->FileAttributes |= cpu_to_le32(ATTR_READONLY); + + req->ShareAccess = cpu_to_le32(FILE_SHARE_ALL); + req->CreateDisposition = cpu_to_le32(disposition); + req->CreateOptions = cpu_to_le32(create_options & CREATE_OPTIONS_MASK); + + /* BB Expirement with various impersonation levels and verify */ + req->ImpersonationLevel = cpu_to_le32(SECURITY_IMPERSONATION); + req->SecurityFlags = SECURITY_CONTEXT_TRACKING|SECURITY_EFFECTIVE_ONLY; + + count += name_len; + inc_rfc1001_len(req, count); + + req->ByteCount = cpu_to_le16(count); + rc = SendReceive(xid, tcon->ses, (struct smb_hdr *)req, + (struct smb_hdr *)rsp, &bytes_returned, 0); + cifs_stats_inc(&tcon->stats.cifs_stats.num_opens); + if (rc) { + cifs_dbg(FYI, "Error in Open = %d\n", rc); + cifs_buf_release(req); + if (rc == -EAGAIN) + goto openRetry; + return rc; + } + + /* 1 byte no need to le_to_cpu */ + *oplock = rsp->OplockLevel; + /* cifs fid stays in le */ + oparms->fid->netfid = rsp->Fid; + oparms->fid->access = desired_access; + + /* Let caller know file was created so we can set the mode. */ + /* Do we care about the CreateAction in any other cases? */ + if (cpu_to_le32(FILE_CREATE) == rsp->CreateAction) + *oplock |= CIFS_CREATE_ACTION; + + if (buf) { + /* copy from CreationTime to Attributes */ + memcpy((char *)buf, (char *)&rsp->CreationTime, 36); + /* the file_info buf is endian converted by caller */ + buf->AllocationSize = rsp->AllocationSize; + buf->EndOfFile = rsp->EndOfFile; + buf->NumberOfLinks = cpu_to_le32(1); + buf->DeletePending = 0; + } + + cifs_buf_release(req); + return rc; +} + +static void +cifs_readv_callback(struct mid_q_entry *mid) +{ + struct cifs_readdata *rdata = mid->callback_data; + struct cifs_tcon *tcon = tlink_tcon(rdata->cfile->tlink); + struct TCP_Server_Info *server = tcon->ses->server; + struct smb_rqst rqst = { .rq_iov = rdata->iov, + .rq_nvec = 2, + .rq_pages = rdata->pages, + .rq_offset = rdata->page_offset, + .rq_npages = rdata->nr_pages, + .rq_pagesz = rdata->pagesz, + .rq_tailsz = rdata->tailsz }; + struct cifs_credits credits = { .value = 1, .instance = 0 }; + + cifs_dbg(FYI, "%s: mid=%llu state=%d result=%d bytes=%u\n", + __func__, mid->mid, mid->mid_state, rdata->result, + rdata->bytes); + + switch (mid->mid_state) { + case MID_RESPONSE_RECEIVED: + /* result already set, check signature */ + if (server->sign) { + int rc = 0; + + rc = cifs_verify_signature(&rqst, server, + mid->sequence_number); + if (rc) + cifs_dbg(VFS, "SMB signature verification returned error = %d\n", + rc); + } + /* FIXME: should this be counted toward the initiating task? */ + task_io_account_read(rdata->got_bytes); + cifs_stats_bytes_read(tcon, rdata->got_bytes); + break; + case MID_REQUEST_SUBMITTED: + case MID_RETRY_NEEDED: + rdata->result = -EAGAIN; + if (server->sign && rdata->got_bytes) + /* reset bytes number since we can not check a sign */ + rdata->got_bytes = 0; + /* FIXME: should this be counted toward the initiating task? */ + task_io_account_read(rdata->got_bytes); + cifs_stats_bytes_read(tcon, rdata->got_bytes); + break; + default: + rdata->result = -EIO; + } + + queue_work(cifsiod_wq, &rdata->work); + release_mid(mid); + add_credits(server, &credits, 0); +} + +/* cifs_async_readv - send an async write, and set up mid to handle result */ +int +cifs_async_readv(struct cifs_readdata *rdata) +{ + int rc; + READ_REQ *smb = NULL; + int wct; + struct cifs_tcon *tcon = tlink_tcon(rdata->cfile->tlink); + struct smb_rqst rqst = { .rq_iov = rdata->iov, + .rq_nvec = 2 }; + + cifs_dbg(FYI, "%s: offset=%llu bytes=%u\n", + __func__, rdata->offset, rdata->bytes); + + if (tcon->ses->capabilities & CAP_LARGE_FILES) + wct = 12; + else { + wct = 10; /* old style read */ + if ((rdata->offset >> 32) > 0) { + /* can not handle this big offset for old */ + return -EIO; + } + } + + rc = small_smb_init(SMB_COM_READ_ANDX, wct, tcon, (void **)&smb); + if (rc) + return rc; + + smb->hdr.Pid = cpu_to_le16((__u16)rdata->pid); + smb->hdr.PidHigh = cpu_to_le16((__u16)(rdata->pid >> 16)); + + smb->AndXCommand = 0xFF; /* none */ + smb->Fid = rdata->cfile->fid.netfid; + smb->OffsetLow = cpu_to_le32(rdata->offset & 0xFFFFFFFF); + if (wct == 12) + smb->OffsetHigh = cpu_to_le32(rdata->offset >> 32); + smb->Remaining = 0; + smb->MaxCount = cpu_to_le16(rdata->bytes & 0xFFFF); + smb->MaxCountHigh = cpu_to_le32(rdata->bytes >> 16); + if (wct == 12) + smb->ByteCount = 0; + else { + /* old style read */ + struct smb_com_readx_req *smbr = + (struct smb_com_readx_req *)smb; + smbr->ByteCount = 0; + } + + /* 4 for RFC1001 length + 1 for BCC */ + rdata->iov[0].iov_base = smb; + rdata->iov[0].iov_len = 4; + rdata->iov[1].iov_base = (char *)smb + 4; + rdata->iov[1].iov_len = get_rfc1002_length(smb); + + kref_get(&rdata->refcount); + rc = cifs_call_async(tcon->ses->server, &rqst, cifs_readv_receive, + cifs_readv_callback, NULL, rdata, 0, NULL); + + if (rc == 0) + cifs_stats_inc(&tcon->stats.cifs_stats.num_reads); + else + kref_put(&rdata->refcount, cifs_readdata_release); + + cifs_small_buf_release(smb); + return rc; +} + +int +CIFSSMBRead(const unsigned int xid, struct cifs_io_parms *io_parms, + unsigned int *nbytes, char **buf, int *pbuf_type) +{ + int rc = -EACCES; + READ_REQ *pSMB = NULL; + READ_RSP *pSMBr = NULL; + char *pReadData = NULL; + int wct; + int resp_buf_type = 0; + struct kvec iov[1]; + struct kvec rsp_iov; + __u32 pid = io_parms->pid; + __u16 netfid = io_parms->netfid; + __u64 offset = io_parms->offset; + struct cifs_tcon *tcon = io_parms->tcon; + unsigned int count = io_parms->length; + + cifs_dbg(FYI, "Reading %d bytes on fid %d\n", count, netfid); + if (tcon->ses->capabilities & CAP_LARGE_FILES) + wct = 12; + else { + wct = 10; /* old style read */ + if ((offset >> 32) > 0) { + /* can not handle this big offset for old */ + return -EIO; + } + } + + *nbytes = 0; + rc = small_smb_init(SMB_COM_READ_ANDX, wct, tcon, (void **) &pSMB); + if (rc) + return rc; + + pSMB->hdr.Pid = cpu_to_le16((__u16)pid); + pSMB->hdr.PidHigh = cpu_to_le16((__u16)(pid >> 16)); + + /* tcon and ses pointer are checked in smb_init */ + if (tcon->ses->server == NULL) + return -ECONNABORTED; + + pSMB->AndXCommand = 0xFF; /* none */ + pSMB->Fid = netfid; + pSMB->OffsetLow = cpu_to_le32(offset & 0xFFFFFFFF); + if (wct == 12) + pSMB->OffsetHigh = cpu_to_le32(offset >> 32); + + pSMB->Remaining = 0; + pSMB->MaxCount = cpu_to_le16(count & 0xFFFF); + pSMB->MaxCountHigh = cpu_to_le32(count >> 16); + 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; + } + + iov[0].iov_base = (char *)pSMB; + iov[0].iov_len = be32_to_cpu(pSMB->hdr.smb_buf_length) + 4; + rc = SendReceive2(xid, tcon->ses, iov, 1, &resp_buf_type, + CIFS_LOG_ERROR, &rsp_iov); + cifs_small_buf_release(pSMB); + cifs_stats_inc(&tcon->stats.cifs_stats.num_reads); + pSMBr = (READ_RSP *)rsp_iov.iov_base; + if (rc) { + cifs_dbg(VFS, "Send error in read = %d\n", rc); + } else { + int data_length = le16_to_cpu(pSMBr->DataLengthHigh); + data_length = data_length << 16; + data_length += le16_to_cpu(pSMBr->DataLength); + *nbytes = data_length; + + /*check that DataLength would not go beyond end of SMB */ + if ((data_length > CIFSMaxBufSize) + || (data_length > count)) { + cifs_dbg(FYI, "bad length %d for count %d\n", + data_length, count); + rc = -EIO; + *nbytes = 0; + } else { + pReadData = (char *) (&pSMBr->hdr.Protocol) + + le16_to_cpu(pSMBr->DataOffset); +/* if (rc = copy_to_user(buf, pReadData, data_length)) { + cifs_dbg(VFS, "Faulting on read rc = %d\n",rc); + rc = -EFAULT; + }*/ /* can not use copy_to_user when using page cache*/ + if (*buf) + memcpy(*buf, pReadData, data_length); + } + } + + if (*buf) { + free_rsp_buf(resp_buf_type, rsp_iov.iov_base); + } else if (resp_buf_type != CIFS_NO_BUFFER) { + /* return buffer to caller to free */ + *buf = rsp_iov.iov_base; + if (resp_buf_type == CIFS_SMALL_BUFFER) + *pbuf_type = CIFS_SMALL_BUFFER; + else if (resp_buf_type == CIFS_LARGE_BUFFER) + *pbuf_type = CIFS_LARGE_BUFFER; + } /* else no valid buffer on return - leave as null */ + + /* Note: On -EAGAIN error only caller can retry on handle based calls + since file handle passed in no longer valid */ + return rc; +} + + +int +CIFSSMBWrite(const unsigned int xid, struct cifs_io_parms *io_parms, + unsigned int *nbytes, const char *buf) +{ + int rc = -EACCES; + WRITE_REQ *pSMB = NULL; + WRITE_RSP *pSMBr = NULL; + int bytes_returned, wct; + __u32 bytes_sent; + __u16 byte_count; + __u32 pid = io_parms->pid; + __u16 netfid = io_parms->netfid; + __u64 offset = io_parms->offset; + struct cifs_tcon *tcon = io_parms->tcon; + unsigned int count = io_parms->length; + + *nbytes = 0; + + /* cifs_dbg(FYI, "write at %lld %d bytes\n", offset, count);*/ + if (tcon->ses == NULL) + return -ECONNABORTED; + + if (tcon->ses->capabilities & CAP_LARGE_FILES) + wct = 14; + else { + wct = 12; + if ((offset >> 32) > 0) { + /* can not handle big offset for old srv */ + return -EIO; + } + } + + rc = smb_init(SMB_COM_WRITE_ANDX, wct, tcon, (void **) &pSMB, + (void **) &pSMBr); + if (rc) + return rc; + + pSMB->hdr.Pid = cpu_to_le16((__u16)pid); + pSMB->hdr.PidHigh = cpu_to_le16((__u16)(pid >> 16)); + + /* tcon and ses pointer are checked in smb_init */ + if (tcon->ses->server == NULL) + return -ECONNABORTED; + + pSMB->AndXCommand = 0xFF; /* none */ + pSMB->Fid = netfid; + pSMB->OffsetLow = cpu_to_le32(offset & 0xFFFFFFFF); + if (wct == 14) + pSMB->OffsetHigh = cpu_to_le32(offset >> 32); + + pSMB->Reserved = 0xFFFFFFFF; + pSMB->WriteMode = 0; + pSMB->Remaining = 0; + + /* Can increase buffer size if buffer is big enough in some cases ie we + can send more if LARGE_WRITE_X capability returned by the server and if + our buffer is big enough or if we convert to iovecs on socket writes + and eliminate the copy to the CIFS buffer */ + if (tcon->ses->capabilities & CAP_LARGE_WRITE_X) { + bytes_sent = min_t(const unsigned int, CIFSMaxBufSize, count); + } else { + bytes_sent = (tcon->ses->server->maxBuf - MAX_CIFS_HDR_SIZE) + & ~0xFF; + } + + if (bytes_sent > count) + bytes_sent = count; + pSMB->DataOffset = + cpu_to_le16(offsetof(struct smb_com_write_req, Data) - 4); + if (buf) + memcpy(pSMB->Data, buf, bytes_sent); + 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 */ + + pSMB->DataLengthLow = cpu_to_le16(bytes_sent & 0xFFFF); + pSMB->DataLengthHigh = cpu_to_le16(bytes_sent >> 16); + inc_rfc1001_len(pSMB, 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, 0); + cifs_stats_inc(&tcon->stats.cifs_stats.num_writes); + if (rc) { + cifs_dbg(FYI, "Send error in write = %d\n", rc); + } else { + *nbytes = le16_to_cpu(pSMBr->CountHigh); + *nbytes = (*nbytes) << 16; + *nbytes += le16_to_cpu(pSMBr->Count); + + /* + * Mask off high 16 bits when bytes written as returned by the + * server is greater than bytes requested by the client. Some + * OS/2 servers are known to set incorrect CountHigh values. + */ + if (*nbytes > count) + *nbytes &= 0xFFFF; + } + + cifs_buf_release(pSMB); + + /* Note: On -EAGAIN error only caller can retry on handle based calls + since file handle passed in no longer valid */ + + return rc; +} + +/* + * Check the mid_state and signature on received buffer (if any), and queue the + * workqueue completion task. + */ +static void +cifs_writev_callback(struct mid_q_entry *mid) +{ + struct cifs_writedata *wdata = mid->callback_data; + struct cifs_tcon *tcon = tlink_tcon(wdata->cfile->tlink); + unsigned int written; + WRITE_RSP *smb = (WRITE_RSP *)mid->resp_buf; + struct cifs_credits credits = { .value = 1, .instance = 0 }; + + switch (mid->mid_state) { + case MID_RESPONSE_RECEIVED: + wdata->result = cifs_check_receive(mid, tcon->ses->server, 0); + if (wdata->result != 0) + break; + + written = le16_to_cpu(smb->CountHigh); + written <<= 16; + written += le16_to_cpu(smb->Count); + /* + * Mask off high 16 bits when bytes written as returned + * by the server is greater than bytes requested by the + * client. OS/2 servers are known to set incorrect + * CountHigh values. + */ + if (written > wdata->bytes) + written &= 0xFFFF; + + if (written < wdata->bytes) + wdata->result = -ENOSPC; + else + wdata->bytes = written; + break; + case MID_REQUEST_SUBMITTED: + case MID_RETRY_NEEDED: + wdata->result = -EAGAIN; + break; + default: + wdata->result = -EIO; + break; + } + + queue_work(cifsiod_wq, &wdata->work); + release_mid(mid); + add_credits(tcon->ses->server, &credits, 0); +} + +/* cifs_async_writev - send an async write, and set up mid to handle result */ +int +cifs_async_writev(struct cifs_writedata *wdata, + void (*release)(struct kref *kref)) +{ + int rc = -EACCES; + WRITE_REQ *smb = NULL; + int wct; + struct cifs_tcon *tcon = tlink_tcon(wdata->cfile->tlink); + struct kvec iov[2]; + struct smb_rqst rqst = { }; + + if (tcon->ses->capabilities & CAP_LARGE_FILES) { + wct = 14; + } else { + wct = 12; + if (wdata->offset >> 32 > 0) { + /* can not handle big offset for old srv */ + return -EIO; + } + } + + rc = small_smb_init(SMB_COM_WRITE_ANDX, wct, tcon, (void **)&smb); + if (rc) + goto async_writev_out; + + smb->hdr.Pid = cpu_to_le16((__u16)wdata->pid); + smb->hdr.PidHigh = cpu_to_le16((__u16)(wdata->pid >> 16)); + + smb->AndXCommand = 0xFF; /* none */ + smb->Fid = wdata->cfile->fid.netfid; + smb->OffsetLow = cpu_to_le32(wdata->offset & 0xFFFFFFFF); + if (wct == 14) + smb->OffsetHigh = cpu_to_le32(wdata->offset >> 32); + smb->Reserved = 0xFFFFFFFF; + smb->WriteMode = 0; + smb->Remaining = 0; + + smb->DataOffset = + cpu_to_le16(offsetof(struct smb_com_write_req, Data) - 4); + + /* 4 for RFC1001 length + 1 for BCC */ + iov[0].iov_len = 4; + iov[0].iov_base = smb; + iov[1].iov_len = get_rfc1002_length(smb) + 1; + iov[1].iov_base = (char *)smb + 4; + + rqst.rq_iov = iov; + rqst.rq_nvec = 2; + rqst.rq_pages = wdata->pages; + rqst.rq_offset = wdata->page_offset; + rqst.rq_npages = wdata->nr_pages; + rqst.rq_pagesz = wdata->pagesz; + rqst.rq_tailsz = wdata->tailsz; + + cifs_dbg(FYI, "async write at %llu %u bytes\n", + wdata->offset, wdata->bytes); + + smb->DataLengthLow = cpu_to_le16(wdata->bytes & 0xFFFF); + smb->DataLengthHigh = cpu_to_le16(wdata->bytes >> 16); + + if (wct == 14) { + inc_rfc1001_len(&smb->hdr, wdata->bytes + 1); + put_bcc(wdata->bytes + 1, &smb->hdr); + } else { + /* wct == 12 */ + struct smb_com_writex_req *smbw = + (struct smb_com_writex_req *)smb; + inc_rfc1001_len(&smbw->hdr, wdata->bytes + 5); + put_bcc(wdata->bytes + 5, &smbw->hdr); + iov[1].iov_len += 4; /* pad bigger by four bytes */ + } + + kref_get(&wdata->refcount); + rc = cifs_call_async(tcon->ses->server, &rqst, NULL, + cifs_writev_callback, NULL, wdata, 0, NULL); + + if (rc == 0) + cifs_stats_inc(&tcon->stats.cifs_stats.num_writes); + else + kref_put(&wdata->refcount, release); + +async_writev_out: + cifs_small_buf_release(smb); + return rc; +} + +int +CIFSSMBWrite2(const unsigned int xid, struct cifs_io_parms *io_parms, + unsigned int *nbytes, struct kvec *iov, int n_vec) +{ + int rc; + WRITE_REQ *pSMB = NULL; + int wct; + int smb_hdr_len; + int resp_buf_type = 0; + __u32 pid = io_parms->pid; + __u16 netfid = io_parms->netfid; + __u64 offset = io_parms->offset; + struct cifs_tcon *tcon = io_parms->tcon; + unsigned int count = io_parms->length; + struct kvec rsp_iov; + + *nbytes = 0; + + cifs_dbg(FYI, "write2 at %lld %d bytes\n", (long long)offset, count); + + if (tcon->ses->capabilities & CAP_LARGE_FILES) { + wct = 14; + } else { + wct = 12; + if ((offset >> 32) > 0) { + /* can not handle big offset for old srv */ + return -EIO; + } + } + rc = small_smb_init(SMB_COM_WRITE_ANDX, wct, tcon, (void **) &pSMB); + if (rc) + return rc; + + pSMB->hdr.Pid = cpu_to_le16((__u16)pid); + pSMB->hdr.PidHigh = cpu_to_le16((__u16)(pid >> 16)); + + /* tcon and ses pointer are checked in smb_init */ + if (tcon->ses->server == NULL) + return -ECONNABORTED; + + pSMB->AndXCommand = 0xFF; /* none */ + pSMB->Fid = netfid; + pSMB->OffsetLow = cpu_to_le32(offset & 0xFFFFFFFF); + if (wct == 14) + pSMB->OffsetHigh = cpu_to_le32(offset >> 32); + pSMB->Reserved = 0xFFFFFFFF; + pSMB->WriteMode = 0; + pSMB->Remaining = 0; + + pSMB->DataOffset = + cpu_to_le16(offsetof(struct smb_com_write_req, Data) - 4); + + pSMB->DataLengthLow = cpu_to_le16(count & 0xFFFF); + pSMB->DataLengthHigh = cpu_to_le16(count >> 16); + /* header + 1 byte pad */ + smb_hdr_len = be32_to_cpu(pSMB->hdr.smb_buf_length) + 1; + if (wct == 14) + inc_rfc1001_len(pSMB, count + 1); + else /* wct == 12 */ + inc_rfc1001_len(pSMB, 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; + if (wct == 14) + iov[0].iov_len = smb_hdr_len + 4; + else /* wct == 12 pad bigger by four bytes */ + iov[0].iov_len = smb_hdr_len + 8; + + rc = SendReceive2(xid, tcon->ses, iov, n_vec + 1, &resp_buf_type, 0, + &rsp_iov); + cifs_small_buf_release(pSMB); + cifs_stats_inc(&tcon->stats.cifs_stats.num_writes); + if (rc) { + cifs_dbg(FYI, "Send error Write2 = %d\n", rc); + } else if (resp_buf_type == 0) { + /* presumably this can not happen, but best to be safe */ + rc = -EIO; + } else { + WRITE_RSP *pSMBr = (WRITE_RSP *)rsp_iov.iov_base; + *nbytes = le16_to_cpu(pSMBr->CountHigh); + *nbytes = (*nbytes) << 16; + *nbytes += le16_to_cpu(pSMBr->Count); + + /* + * Mask off high 16 bits when bytes written as returned by the + * server is greater than bytes requested by the client. OS/2 + * servers are known to set incorrect CountHigh values. + */ + if (*nbytes > count) + *nbytes &= 0xFFFF; + } + + free_rsp_buf(resp_buf_type, rsp_iov.iov_base); + + /* Note: On -EAGAIN error only caller can retry on handle based calls + since file handle passed in no longer valid */ + + return rc; +} + +int cifs_lockv(const unsigned int xid, struct cifs_tcon *tcon, + const __u16 netfid, const __u8 lock_type, const __u32 num_unlock, + const __u32 num_lock, LOCKING_ANDX_RANGE *buf) +{ + int rc = 0; + LOCK_REQ *pSMB = NULL; + struct kvec iov[2]; + struct kvec rsp_iov; + int resp_buf_type; + __u16 count; + + cifs_dbg(FYI, "cifs_lockv num lock %d num unlock %d\n", + num_lock, num_unlock); + + rc = small_smb_init(SMB_COM_LOCKING_ANDX, 8, tcon, (void **) &pSMB); + if (rc) + return rc; + + pSMB->Timeout = 0; + pSMB->NumberOfLocks = cpu_to_le16(num_lock); + pSMB->NumberOfUnlocks = cpu_to_le16(num_unlock); + pSMB->LockType = lock_type; + pSMB->AndXCommand = 0xFF; /* none */ + pSMB->Fid = netfid; /* netfid stays le */ + + count = (num_unlock + num_lock) * sizeof(LOCKING_ANDX_RANGE); + inc_rfc1001_len(pSMB, count); + pSMB->ByteCount = cpu_to_le16(count); + + iov[0].iov_base = (char *)pSMB; + iov[0].iov_len = be32_to_cpu(pSMB->hdr.smb_buf_length) + 4 - + (num_unlock + num_lock) * sizeof(LOCKING_ANDX_RANGE); + iov[1].iov_base = (char *)buf; + iov[1].iov_len = (num_unlock + num_lock) * sizeof(LOCKING_ANDX_RANGE); + + cifs_stats_inc(&tcon->stats.cifs_stats.num_locks); + rc = SendReceive2(xid, tcon->ses, iov, 2, &resp_buf_type, + CIFS_NO_RSP_BUF, &rsp_iov); + cifs_small_buf_release(pSMB); + if (rc) + cifs_dbg(FYI, "Send error in cifs_lockv = %d\n", rc); + + return rc; +} + +int +CIFSSMBLock(const unsigned int xid, struct cifs_tcon *tcon, + const __u16 smb_file_id, const __u32 netpid, const __u64 len, + const __u64 offset, const __u32 numUnlock, + const __u32 numLock, const __u8 lockType, + const bool waitFlag, const __u8 oplock_level) +{ + int rc = 0; + LOCK_REQ *pSMB = NULL; +/* LOCK_RSP *pSMBr = NULL; */ /* No response data other than rc to parse */ + int bytes_returned; + int flags = 0; + __u16 count; + + cifs_dbg(FYI, "CIFSSMBLock timeout %d numLock %d\n", + (int)waitFlag, numLock); + rc = small_smb_init(SMB_COM_LOCKING_ANDX, 8, tcon, (void **) &pSMB); + + if (rc) + return rc; + + if (lockType == LOCKING_ANDX_OPLOCK_RELEASE) { + /* no response expected */ + flags = CIFS_NO_SRV_RSP | CIFS_NON_BLOCKING | CIFS_OBREAK_OP; + pSMB->Timeout = 0; + } else if (waitFlag) { + flags = CIFS_BLOCKING_OP; /* blocking operation, no timeout */ + pSMB->Timeout = cpu_to_le32(-1);/* blocking - do not time out */ + } else { + pSMB->Timeout = 0; + } + + pSMB->NumberOfLocks = cpu_to_le16(numLock); + pSMB->NumberOfUnlocks = cpu_to_le16(numUnlock); + pSMB->LockType = lockType; + pSMB->OplockLevel = oplock_level; + pSMB->AndXCommand = 0xFF; /* none */ + pSMB->Fid = smb_file_id; /* netfid stays le */ + + if ((numLock != 0) || (numUnlock != 0)) { + pSMB->Locks[0].Pid = cpu_to_le16(netpid); + /* BB where to store pid high? */ + pSMB->Locks[0].LengthLow = cpu_to_le32((u32)len); + pSMB->Locks[0].LengthHigh = cpu_to_le32((u32)(len>>32)); + pSMB->Locks[0].OffsetLow = cpu_to_le32((u32)offset); + pSMB->Locks[0].OffsetHigh = cpu_to_le32((u32)(offset>>32)); + count = sizeof(LOCKING_ANDX_RANGE); + } else { + /* oplock break */ + count = 0; + } + inc_rfc1001_len(pSMB, count); + pSMB->ByteCount = cpu_to_le16(count); + + if (waitFlag) + rc = SendReceiveBlockingLock(xid, tcon, (struct smb_hdr *) pSMB, + (struct smb_hdr *) pSMB, &bytes_returned); + else + rc = SendReceiveNoRsp(xid, tcon->ses, (char *)pSMB, flags); + cifs_small_buf_release(pSMB); + cifs_stats_inc(&tcon->stats.cifs_stats.num_locks); + if (rc) + cifs_dbg(FYI, "Send error in Lock = %d\n", rc); + + /* Note: On -EAGAIN error only caller can retry on handle based calls + since file handle passed in no longer valid */ + return rc; +} + +int +CIFSSMBPosixLock(const unsigned int xid, struct cifs_tcon *tcon, + const __u16 smb_file_id, const __u32 netpid, + const loff_t start_offset, const __u64 len, + struct file_lock *pLockData, const __u16 lock_type, + const bool waitFlag) +{ + struct smb_com_transaction2_sfi_req *pSMB = NULL; + struct smb_com_transaction2_sfi_rsp *pSMBr = NULL; + struct cifs_posix_lock *parm_data; + int rc = 0; + int timeout = 0; + int bytes_returned = 0; + int resp_buf_type = 0; + __u16 params, param_offset, offset, byte_count, count; + struct kvec iov[1]; + struct kvec rsp_iov; + + cifs_dbg(FYI, "Posix Lock\n"); + + rc = small_smb_init(SMB_COM_TRANSACTION2, 15, tcon, (void **) &pSMB); + + if (rc) + return rc; + + pSMBr = (struct smb_com_transaction2_sfi_rsp *)pSMB; + + params = 6; + pSMB->MaxSetupCount = 0; + pSMB->Reserved = 0; + pSMB->Flags = 0; + pSMB->Reserved2 = 0; + param_offset = offsetof(struct smb_com_transaction2_sfi_req, Fid) - 4; + offset = param_offset + params; + + count = sizeof(struct cifs_posix_lock); + pSMB->MaxParameterCount = cpu_to_le16(2); + pSMB->MaxDataCount = cpu_to_le16(1000); /* BB find max SMB from sess */ + pSMB->SetupCount = 1; + pSMB->Reserved3 = 0; + if (pLockData) + pSMB->SubCommand = cpu_to_le16(TRANS2_QUERY_FILE_INFORMATION); + else + pSMB->SubCommand = cpu_to_le16(TRANS2_SET_FILE_INFORMATION); + byte_count = 3 /* pad */ + params + count; + pSMB->DataCount = cpu_to_le16(count); + pSMB->ParameterCount = cpu_to_le16(params); + pSMB->TotalDataCount = pSMB->DataCount; + pSMB->TotalParameterCount = pSMB->ParameterCount; + pSMB->ParameterOffset = cpu_to_le16(param_offset); + /* SMB offsets are from the beginning of SMB which is 4 bytes in, after RFC1001 field */ + parm_data = (struct cifs_posix_lock *) + (((char *)pSMB) + offset + 4); + + parm_data->lock_type = cpu_to_le16(lock_type); + if (waitFlag) { + timeout = CIFS_BLOCKING_OP; /* blocking operation, no timeout */ + parm_data->lock_flags = cpu_to_le16(1); + pSMB->Timeout = cpu_to_le32(-1); + } else + pSMB->Timeout = 0; + + parm_data->pid = cpu_to_le32(netpid); + parm_data->start = cpu_to_le64(start_offset); + parm_data->length = cpu_to_le64(len); /* normalize negative numbers */ + + pSMB->DataOffset = cpu_to_le16(offset); + pSMB->Fid = smb_file_id; + pSMB->InformationLevel = cpu_to_le16(SMB_SET_POSIX_LOCK); + pSMB->Reserved4 = 0; + inc_rfc1001_len(pSMB, byte_count); + pSMB->ByteCount = cpu_to_le16(byte_count); + if (waitFlag) { + rc = SendReceiveBlockingLock(xid, tcon, (struct smb_hdr *) pSMB, + (struct smb_hdr *) pSMBr, &bytes_returned); + } else { + iov[0].iov_base = (char *)pSMB; + iov[0].iov_len = be32_to_cpu(pSMB->hdr.smb_buf_length) + 4; + rc = SendReceive2(xid, tcon->ses, iov, 1 /* num iovecs */, + &resp_buf_type, timeout, &rsp_iov); + pSMBr = (struct smb_com_transaction2_sfi_rsp *)rsp_iov.iov_base; + } + cifs_small_buf_release(pSMB); + + if (rc) { + cifs_dbg(FYI, "Send error in Posix Lock = %d\n", rc); + } else if (pLockData) { + /* lock structure can be returned on get */ + __u16 data_offset; + __u16 data_count; + rc = validate_t2((struct smb_t2_rsp *)pSMBr); + + if (rc || get_bcc(&pSMBr->hdr) < sizeof(*parm_data)) { + rc = -EIO; /* bad smb */ + goto plk_err_exit; + } + data_offset = le16_to_cpu(pSMBr->t2.DataOffset); + data_count = le16_to_cpu(pSMBr->t2.DataCount); + if (data_count < sizeof(struct cifs_posix_lock)) { + rc = -EIO; + goto plk_err_exit; + } + parm_data = (struct cifs_posix_lock *) + ((char *)&pSMBr->hdr.Protocol + data_offset); + if (parm_data->lock_type == cpu_to_le16(CIFS_UNLCK)) + pLockData->fl_type = F_UNLCK; + else { + if (parm_data->lock_type == + cpu_to_le16(CIFS_RDLCK)) + pLockData->fl_type = F_RDLCK; + else if (parm_data->lock_type == + cpu_to_le16(CIFS_WRLCK)) + pLockData->fl_type = F_WRLCK; + + pLockData->fl_start = le64_to_cpu(parm_data->start); + pLockData->fl_end = pLockData->fl_start + + (le64_to_cpu(parm_data->length) ? + le64_to_cpu(parm_data->length) - 1 : 0); + pLockData->fl_pid = -le32_to_cpu(parm_data->pid); + } + } + +plk_err_exit: + free_rsp_buf(resp_buf_type, rsp_iov.iov_base); + + /* Note: On -EAGAIN error only caller can retry on handle based calls + since file handle passed in no longer valid */ + + return rc; +} + + +int +CIFSSMBClose(const unsigned int xid, struct cifs_tcon *tcon, int smb_file_id) +{ + int rc = 0; + CLOSE_REQ *pSMB = NULL; + cifs_dbg(FYI, "In CIFSSMBClose\n"); + +/* do not retry on dead session on close */ + rc = small_smb_init(SMB_COM_CLOSE, 3, tcon, (void **) &pSMB); + if (rc == -EAGAIN) + return 0; + if (rc) + return rc; + + pSMB->FileID = (__u16) smb_file_id; + pSMB->LastWriteTime = 0xFFFFFFFF; + pSMB->ByteCount = 0; + rc = SendReceiveNoRsp(xid, tcon->ses, (char *) pSMB, 0); + cifs_small_buf_release(pSMB); + cifs_stats_inc(&tcon->stats.cifs_stats.num_closes); + if (rc) { + if (rc != -EINTR) { + /* EINTR is expected when user ctl-c to kill app */ + cifs_dbg(VFS, "Send error in Close = %d\n", rc); + } + } + + /* Since session is dead, file will be closed on server already */ + if (rc == -EAGAIN) + rc = 0; + + return rc; +} + +int +CIFSSMBFlush(const unsigned int xid, struct cifs_tcon *tcon, int smb_file_id) +{ + int rc = 0; + FLUSH_REQ *pSMB = NULL; + cifs_dbg(FYI, "In CIFSSMBFlush\n"); + + rc = small_smb_init(SMB_COM_FLUSH, 1, tcon, (void **) &pSMB); + if (rc) + return rc; + + pSMB->FileID = (__u16) smb_file_id; + pSMB->ByteCount = 0; + rc = SendReceiveNoRsp(xid, tcon->ses, (char *) pSMB, 0); + cifs_small_buf_release(pSMB); + cifs_stats_inc(&tcon->stats.cifs_stats.num_flushes); + if (rc) + cifs_dbg(VFS, "Send error in Flush = %d\n", rc); + + return rc; +} + +int +CIFSSMBRename(const unsigned int xid, struct cifs_tcon *tcon, + const char *from_name, const char *to_name, + struct cifs_sb_info *cifs_sb) +{ + int rc = 0; + RENAME_REQ *pSMB = NULL; + RENAME_RSP *pSMBr = NULL; + int bytes_returned; + int name_len, name_len2; + __u16 count; + int remap = cifs_remap(cifs_sb); + + cifs_dbg(FYI, "In CIFSSMBRename\n"); +renameRetry: + rc = smb_init(SMB_COM_RENAME, 1, tcon, (void **) &pSMB, + (void **) &pSMBr); + if (rc) + return rc; + + pSMB->BufferFormat = 0x04; + pSMB->SearchAttributes = + cpu_to_le16(ATTR_READONLY | ATTR_HIDDEN | ATTR_SYSTEM | + ATTR_DIRECTORY); + + if (pSMB->hdr.Flags2 & SMBFLG2_UNICODE) { + name_len = cifsConvertToUTF16((__le16 *) pSMB->OldFileName, + from_name, PATH_MAX, + cifs_sb->local_nls, remap); + name_len++; /* trailing null */ + name_len *= 2; + pSMB->OldFileName[name_len] = 0x04; /* pad */ + /* protocol requires ASCII signature byte on Unicode string */ + pSMB->OldFileName[name_len + 1] = 0x00; + name_len2 = + cifsConvertToUTF16((__le16 *)&pSMB->OldFileName[name_len+2], + to_name, PATH_MAX, cifs_sb->local_nls, + remap); + name_len2 += 1 /* trailing null */ + 1 /* Signature word */ ; + name_len2 *= 2; /* convert to bytes */ + } else { + name_len = copy_path_name(pSMB->OldFileName, from_name); + name_len2 = copy_path_name(pSMB->OldFileName+name_len+1, to_name); + pSMB->OldFileName[name_len] = 0x04; /* 2nd buffer format */ + name_len2++; /* signature byte */ + } + + count = 1 /* 1st signature byte */ + name_len + name_len2; + inc_rfc1001_len(pSMB, count); + pSMB->ByteCount = cpu_to_le16(count); + + rc = SendReceive(xid, tcon->ses, (struct smb_hdr *) pSMB, + (struct smb_hdr *) pSMBr, &bytes_returned, 0); + cifs_stats_inc(&tcon->stats.cifs_stats.num_renames); + if (rc) + cifs_dbg(FYI, "Send error in rename = %d\n", rc); + + cifs_buf_release(pSMB); + + if (rc == -EAGAIN) + goto renameRetry; + + return rc; +} + +int CIFSSMBRenameOpenFile(const unsigned int xid, struct cifs_tcon *pTcon, + int netfid, const char *target_name, + const struct nls_table *nls_codepage, int remap) +{ + struct smb_com_transaction2_sfi_req *pSMB = NULL; + struct smb_com_transaction2_sfi_rsp *pSMBr = NULL; + struct set_file_rename *rename_info; + char *data_offset; + char dummy_string[30]; + int rc = 0; + int bytes_returned = 0; + int len_of_str; + __u16 params, param_offset, offset, count, byte_count; + + cifs_dbg(FYI, "Rename to File by handle\n"); + rc = smb_init(SMB_COM_TRANSACTION2, 15, pTcon, (void **) &pSMB, + (void **) &pSMBr); + if (rc) + return rc; + + params = 6; + pSMB->MaxSetupCount = 0; + pSMB->Reserved = 0; + pSMB->Flags = 0; + pSMB->Timeout = 0; + pSMB->Reserved2 = 0; + param_offset = offsetof(struct smb_com_transaction2_sfi_req, Fid) - 4; + offset = param_offset + params; + + /* SMB offsets are from the beginning of SMB which is 4 bytes in, after RFC1001 field */ + data_offset = (char *)(pSMB) + offset + 4; + rename_info = (struct set_file_rename *) data_offset; + pSMB->MaxParameterCount = cpu_to_le16(2); + pSMB->MaxDataCount = cpu_to_le16(1000); /* BB find max SMB from sess */ + pSMB->SetupCount = 1; + pSMB->Reserved3 = 0; + pSMB->SubCommand = cpu_to_le16(TRANS2_SET_FILE_INFORMATION); + byte_count = 3 /* pad */ + params; + pSMB->ParameterCount = cpu_to_le16(params); + pSMB->TotalParameterCount = pSMB->ParameterCount; + pSMB->ParameterOffset = cpu_to_le16(param_offset); + pSMB->DataOffset = cpu_to_le16(offset); + /* construct random name ".cifs_tmp" */ + rename_info->overwrite = cpu_to_le32(1); + rename_info->root_fid = 0; + /* unicode only call */ + if (target_name == NULL) { + sprintf(dummy_string, "cifs%x", pSMB->hdr.Mid); + len_of_str = + cifsConvertToUTF16((__le16 *)rename_info->target_name, + dummy_string, 24, nls_codepage, remap); + } else { + len_of_str = + cifsConvertToUTF16((__le16 *)rename_info->target_name, + target_name, PATH_MAX, nls_codepage, + remap); + } + rename_info->target_name_len = cpu_to_le32(2 * len_of_str); + count = sizeof(struct set_file_rename) + (2 * len_of_str); + byte_count += count; + pSMB->DataCount = cpu_to_le16(count); + pSMB->TotalDataCount = pSMB->DataCount; + pSMB->Fid = netfid; + pSMB->InformationLevel = + cpu_to_le16(SMB_SET_FILE_RENAME_INFORMATION); + pSMB->Reserved4 = 0; + inc_rfc1001_len(pSMB, byte_count); + 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->stats.cifs_stats.num_t2renames); + if (rc) + cifs_dbg(FYI, "Send error in Rename (by file handle) = %d\n", + rc); + + cifs_buf_release(pSMB); + + /* Note: On -EAGAIN error only caller can retry on handle based calls + since file handle passed in no longer valid */ + + return rc; +} + +int +CIFSSMBCopy(const unsigned int xid, struct cifs_tcon *tcon, + const char *fromName, const __u16 target_tid, const char *toName, + const int flags, const struct nls_table *nls_codepage, int remap) +{ + int rc = 0; + COPY_REQ *pSMB = NULL; + COPY_RSP *pSMBr = NULL; + int bytes_returned; + int name_len, name_len2; + __u16 count; + + cifs_dbg(FYI, "In CIFSSMBCopy\n"); +copyRetry: + rc = smb_init(SMB_COM_COPY, 1, tcon, (void **) &pSMB, + (void **) &pSMBr); + if (rc) + return rc; + + pSMB->BufferFormat = 0x04; + pSMB->Tid2 = target_tid; + + pSMB->Flags = cpu_to_le16(flags & COPY_TREE); + + if (pSMB->hdr.Flags2 & SMBFLG2_UNICODE) { + name_len = cifsConvertToUTF16((__le16 *) pSMB->OldFileName, + fromName, PATH_MAX, nls_codepage, + remap); + name_len++; /* trailing null */ + name_len *= 2; + pSMB->OldFileName[name_len] = 0x04; /* pad */ + /* protocol requires ASCII signature byte on Unicode string */ + pSMB->OldFileName[name_len + 1] = 0x00; + name_len2 = + cifsConvertToUTF16((__le16 *)&pSMB->OldFileName[name_len+2], + toName, PATH_MAX, nls_codepage, remap); + name_len2 += 1 /* trailing null */ + 1 /* Signature word */ ; + name_len2 *= 2; /* convert to bytes */ + } else { + name_len = copy_path_name(pSMB->OldFileName, fromName); + pSMB->OldFileName[name_len] = 0x04; /* 2nd buffer format */ + name_len2 = copy_path_name(pSMB->OldFileName+name_len+1, toName); + name_len2++; /* signature byte */ + } + + count = 1 /* 1st signature byte */ + name_len + name_len2; + inc_rfc1001_len(pSMB, count); + pSMB->ByteCount = cpu_to_le16(count); + + rc = SendReceive(xid, tcon->ses, (struct smb_hdr *) pSMB, + (struct smb_hdr *) pSMBr, &bytes_returned, 0); + if (rc) { + cifs_dbg(FYI, "Send error in copy = %d with %d files copied\n", + rc, le16_to_cpu(pSMBr->CopyCount)); + } + cifs_buf_release(pSMB); + + if (rc == -EAGAIN) + goto copyRetry; + + return rc; +} + +int +CIFSUnixCreateSymLink(const unsigned int xid, struct cifs_tcon *tcon, + const char *fromName, const char *toName, + const struct nls_table *nls_codepage, int remap) +{ + TRANSACTION2_SPI_REQ *pSMB = NULL; + TRANSACTION2_SPI_RSP *pSMBr = NULL; + char *data_offset; + int name_len; + int name_len_target; + int rc = 0; + int bytes_returned = 0; + __u16 params, param_offset, offset, byte_count; + + cifs_dbg(FYI, "In Symlink Unix style\n"); +createSymLinkRetry: + rc = smb_init(SMB_COM_TRANSACTION2, 15, tcon, (void **) &pSMB, + (void **) &pSMBr); + if (rc) + return rc; + + if (pSMB->hdr.Flags2 & SMBFLG2_UNICODE) { + name_len = + cifsConvertToUTF16((__le16 *) pSMB->FileName, fromName, + /* find define for this maxpathcomponent */ + PATH_MAX, nls_codepage, remap); + name_len++; /* trailing null */ + name_len *= 2; + + } else { + name_len = copy_path_name(pSMB->FileName, fromName); + } + params = 6 + name_len; + pSMB->MaxSetupCount = 0; + pSMB->Reserved = 0; + pSMB->Flags = 0; + pSMB->Timeout = 0; + pSMB->Reserved2 = 0; + param_offset = offsetof(struct smb_com_transaction2_spi_req, + InformationLevel) - 4; + offset = param_offset + params; + + /* SMB offsets are from the beginning of SMB which is 4 bytes in, after RFC1001 field */ + data_offset = (char *)pSMB + offset + 4; + if (pSMB->hdr.Flags2 & SMBFLG2_UNICODE) { + name_len_target = + cifsConvertToUTF16((__le16 *) data_offset, toName, + /* find define for this maxpathcomponent */ + PATH_MAX, nls_codepage, remap); + name_len_target++; /* trailing null */ + name_len_target *= 2; + } else { + name_len_target = copy_path_name(data_offset, toName); + } + + pSMB->MaxParameterCount = cpu_to_le16(2); + /* BB find exact max on data count below from sess */ + pSMB->MaxDataCount = cpu_to_le16(1000); + pSMB->SetupCount = 1; + pSMB->Reserved3 = 0; + pSMB->SubCommand = cpu_to_le16(TRANS2_SET_PATH_INFORMATION); + byte_count = 3 /* pad */ + params + name_len_target; + pSMB->DataCount = cpu_to_le16(name_len_target); + 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); + pSMB->InformationLevel = cpu_to_le16(SMB_SET_FILE_UNIX_LINK); + pSMB->Reserved4 = 0; + inc_rfc1001_len(pSMB, 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); + cifs_stats_inc(&tcon->stats.cifs_stats.num_symlinks); + if (rc) + cifs_dbg(FYI, "Send error in SetPathInfo create symlink = %d\n", + rc); + + cifs_buf_release(pSMB); + + if (rc == -EAGAIN) + goto createSymLinkRetry; + + return rc; +} + +int +CIFSUnixCreateHardLink(const unsigned int xid, struct cifs_tcon *tcon, + const char *fromName, const char *toName, + const struct nls_table *nls_codepage, int remap) +{ + TRANSACTION2_SPI_REQ *pSMB = NULL; + TRANSACTION2_SPI_RSP *pSMBr = NULL; + char *data_offset; + int name_len; + int name_len_target; + int rc = 0; + int bytes_returned = 0; + __u16 params, param_offset, offset, byte_count; + + cifs_dbg(FYI, "In Create Hard link Unix style\n"); +createHardLinkRetry: + rc = smb_init(SMB_COM_TRANSACTION2, 15, tcon, (void **) &pSMB, + (void **) &pSMBr); + if (rc) + return rc; + + if (pSMB->hdr.Flags2 & SMBFLG2_UNICODE) { + name_len = cifsConvertToUTF16((__le16 *) pSMB->FileName, toName, + PATH_MAX, nls_codepage, remap); + name_len++; /* trailing null */ + name_len *= 2; + + } else { + name_len = copy_path_name(pSMB->FileName, toName); + } + params = 6 + name_len; + pSMB->MaxSetupCount = 0; + pSMB->Reserved = 0; + pSMB->Flags = 0; + pSMB->Timeout = 0; + pSMB->Reserved2 = 0; + param_offset = offsetof(struct smb_com_transaction2_spi_req, + InformationLevel) - 4; + offset = param_offset + params; + + /* SMB offsets are from the beginning of SMB which is 4 bytes in, after RFC1001 field */ + data_offset = (char *)pSMB + offset + 4; + if (pSMB->hdr.Flags2 & SMBFLG2_UNICODE) { + name_len_target = + cifsConvertToUTF16((__le16 *) data_offset, fromName, + PATH_MAX, nls_codepage, remap); + name_len_target++; /* trailing null */ + name_len_target *= 2; + } else { + name_len_target = copy_path_name(data_offset, fromName); + } + + pSMB->MaxParameterCount = cpu_to_le16(2); + /* BB find exact max on data count below from sess*/ + pSMB->MaxDataCount = cpu_to_le16(1000); + pSMB->SetupCount = 1; + pSMB->Reserved3 = 0; + pSMB->SubCommand = cpu_to_le16(TRANS2_SET_PATH_INFORMATION); + byte_count = 3 /* pad */ + params + name_len_target; + pSMB->ParameterCount = cpu_to_le16(params); + pSMB->TotalParameterCount = pSMB->ParameterCount; + pSMB->DataCount = cpu_to_le16(name_len_target); + pSMB->TotalDataCount = pSMB->DataCount; + pSMB->ParameterOffset = cpu_to_le16(param_offset); + pSMB->DataOffset = cpu_to_le16(offset); + pSMB->InformationLevel = cpu_to_le16(SMB_SET_FILE_UNIX_HLINK); + pSMB->Reserved4 = 0; + inc_rfc1001_len(pSMB, 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); + cifs_stats_inc(&tcon->stats.cifs_stats.num_hardlinks); + if (rc) + cifs_dbg(FYI, "Send error in SetPathInfo (hard link) = %d\n", + rc); + + cifs_buf_release(pSMB); + if (rc == -EAGAIN) + goto createHardLinkRetry; + + return rc; +} + +int +CIFSCreateHardLink(const unsigned int xid, struct cifs_tcon *tcon, + const char *from_name, const char *to_name, + struct cifs_sb_info *cifs_sb) +{ + int rc = 0; + NT_RENAME_REQ *pSMB = NULL; + RENAME_RSP *pSMBr = NULL; + int bytes_returned; + int name_len, name_len2; + __u16 count; + int remap = cifs_remap(cifs_sb); + + cifs_dbg(FYI, "In CIFSCreateHardLink\n"); +winCreateHardLinkRetry: + + rc = smb_init(SMB_COM_NT_RENAME, 4, tcon, (void **) &pSMB, + (void **) &pSMBr); + if (rc) + return rc; + + pSMB->SearchAttributes = + cpu_to_le16(ATTR_READONLY | ATTR_HIDDEN | ATTR_SYSTEM | + ATTR_DIRECTORY); + pSMB->Flags = cpu_to_le16(CREATE_HARD_LINK); + pSMB->ClusterCount = 0; + + pSMB->BufferFormat = 0x04; + + if (pSMB->hdr.Flags2 & SMBFLG2_UNICODE) { + name_len = + cifsConvertToUTF16((__le16 *) pSMB->OldFileName, from_name, + PATH_MAX, cifs_sb->local_nls, remap); + name_len++; /* trailing null */ + name_len *= 2; + + /* protocol specifies ASCII buffer format (0x04) for unicode */ + pSMB->OldFileName[name_len] = 0x04; + pSMB->OldFileName[name_len + 1] = 0x00; /* pad */ + name_len2 = + cifsConvertToUTF16((__le16 *)&pSMB->OldFileName[name_len+2], + to_name, PATH_MAX, cifs_sb->local_nls, + remap); + name_len2 += 1 /* trailing null */ + 1 /* Signature word */ ; + name_len2 *= 2; /* convert to bytes */ + } else { + name_len = copy_path_name(pSMB->OldFileName, from_name); + pSMB->OldFileName[name_len] = 0x04; /* 2nd buffer format */ + name_len2 = copy_path_name(pSMB->OldFileName+name_len+1, to_name); + name_len2++; /* signature byte */ + } + + count = 1 /* string type byte */ + name_len + name_len2; + inc_rfc1001_len(pSMB, count); + pSMB->ByteCount = cpu_to_le16(count); + + rc = SendReceive(xid, tcon->ses, (struct smb_hdr *) pSMB, + (struct smb_hdr *) pSMBr, &bytes_returned, 0); + cifs_stats_inc(&tcon->stats.cifs_stats.num_hardlinks); + if (rc) + cifs_dbg(FYI, "Send error in hard link (NT rename) = %d\n", rc); + + cifs_buf_release(pSMB); + if (rc == -EAGAIN) + goto winCreateHardLinkRetry; + + return rc; +} + +int +CIFSSMBUnixQuerySymLink(const unsigned int xid, struct cifs_tcon *tcon, + const unsigned char *searchName, char **symlinkinfo, + const struct nls_table *nls_codepage, int remap) +{ +/* SMB_QUERY_FILE_UNIX_LINK */ + TRANSACTION2_QPI_REQ *pSMB = NULL; + TRANSACTION2_QPI_RSP *pSMBr = NULL; + int rc = 0; + int bytes_returned; + int name_len; + __u16 params, byte_count; + char *data_start; + + cifs_dbg(FYI, "In QPathSymLinkInfo (Unix) for path %s\n", searchName); + +querySymLinkRetry: + rc = smb_init(SMB_COM_TRANSACTION2, 15, tcon, (void **) &pSMB, + (void **) &pSMBr); + if (rc) + return rc; + + if (pSMB->hdr.Flags2 & SMBFLG2_UNICODE) { + name_len = + cifsConvertToUTF16((__le16 *) pSMB->FileName, + searchName, PATH_MAX, nls_codepage, + remap); + name_len++; /* trailing null */ + name_len *= 2; + } else { + name_len = copy_path_name(pSMB->FileName, searchName); + } + + params = 2 /* level */ + 4 /* rsrvd */ + name_len /* incl null */ ; + pSMB->TotalDataCount = 0; + pSMB->MaxParameterCount = cpu_to_le16(2); + pSMB->MaxDataCount = cpu_to_le16(CIFSMaxBufSize); + pSMB->MaxSetupCount = 0; + pSMB->Reserved = 0; + pSMB->Flags = 0; + pSMB->Timeout = 0; + pSMB->Reserved2 = 0; + pSMB->ParameterOffset = cpu_to_le16(offsetof( + struct smb_com_transaction2_qpi_req, InformationLevel) - 4); + pSMB->DataCount = 0; + pSMB->DataOffset = 0; + pSMB->SetupCount = 1; + pSMB->Reserved3 = 0; + pSMB->SubCommand = cpu_to_le16(TRANS2_QUERY_PATH_INFORMATION); + byte_count = params + 1 /* pad */ ; + pSMB->TotalParameterCount = cpu_to_le16(params); + pSMB->ParameterCount = pSMB->TotalParameterCount; + pSMB->InformationLevel = cpu_to_le16(SMB_QUERY_FILE_UNIX_LINK); + pSMB->Reserved4 = 0; + inc_rfc1001_len(pSMB, 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) { + cifs_dbg(FYI, "Send error in QuerySymLinkInfo = %d\n", rc); + } else { + /* decode response */ + + rc = validate_t2((struct smb_t2_rsp *)pSMBr); + /* BB also check enough total bytes returned */ + if (rc || get_bcc(&pSMBr->hdr) < 2) + rc = -EIO; + else { + bool is_unicode; + u16 count = le16_to_cpu(pSMBr->t2.DataCount); + + data_start = ((char *) &pSMBr->hdr.Protocol) + + le16_to_cpu(pSMBr->t2.DataOffset); + + if (pSMBr->hdr.Flags2 & SMBFLG2_UNICODE) + is_unicode = true; + else + is_unicode = false; + + /* BB FIXME investigate remapping reserved chars here */ + *symlinkinfo = cifs_strndup_from_utf16(data_start, + count, is_unicode, nls_codepage); + if (!*symlinkinfo) + rc = -ENOMEM; + } + } + cifs_buf_release(pSMB); + if (rc == -EAGAIN) + goto querySymLinkRetry; + return rc; +} + +/* + * Recent Windows versions now create symlinks more frequently + * and they use the "reparse point" mechanism below. We can of course + * do symlinks nicely to Samba and other servers which support the + * CIFS Unix Extensions and we can also do SFU symlinks and "client only" + * "MF" symlinks optionally, but for recent Windows we really need to + * reenable the code below and fix the cifs_symlink callers to handle this. + * In the interim this code has been moved to its own config option so + * it is not compiled in by default until callers fixed up and more tested. + */ +int +CIFSSMBQuerySymLink(const unsigned int xid, struct cifs_tcon *tcon, + __u16 fid, char **symlinkinfo, + const struct nls_table *nls_codepage) +{ + int rc = 0; + int bytes_returned; + struct smb_com_transaction_ioctl_req *pSMB; + struct smb_com_transaction_ioctl_rsp *pSMBr; + bool is_unicode; + unsigned int sub_len; + char *sub_start; + struct reparse_symlink_data *reparse_buf; + struct reparse_posix_data *posix_buf; + __u32 data_offset, data_count; + char *end_of_smb; + + cifs_dbg(FYI, "In Windows reparse style QueryLink for fid %u\n", fid); + rc = smb_init(SMB_COM_NT_TRANSACT, 23, tcon, (void **) &pSMB, + (void **) &pSMBr); + if (rc) + return rc; + + pSMB->TotalParameterCount = 0 ; + pSMB->TotalDataCount = 0; + pSMB->MaxParameterCount = cpu_to_le32(2); + /* BB find exact data count max from sess structure BB */ + pSMB->MaxDataCount = cpu_to_le32(CIFSMaxBufSize & 0xFFFFFF00); + pSMB->MaxSetupCount = 4; + pSMB->Reserved = 0; + pSMB->ParameterOffset = 0; + pSMB->DataCount = 0; + pSMB->DataOffset = 0; + pSMB->SetupCount = 4; + pSMB->SubCommand = cpu_to_le16(NT_TRANSACT_IOCTL); + pSMB->ParameterCount = pSMB->TotalParameterCount; + pSMB->FunctionCode = cpu_to_le32(FSCTL_GET_REPARSE_POINT); + pSMB->IsFsctl = 1; /* FSCTL */ + pSMB->IsRootFlag = 0; + pSMB->Fid = fid; /* file handle always le */ + pSMB->ByteCount = 0; + + rc = SendReceive(xid, tcon->ses, (struct smb_hdr *) pSMB, + (struct smb_hdr *) pSMBr, &bytes_returned, 0); + if (rc) { + cifs_dbg(FYI, "Send error in QueryReparseLinkInfo = %d\n", rc); + goto qreparse_out; + } + + data_offset = le32_to_cpu(pSMBr->DataOffset); + data_count = le32_to_cpu(pSMBr->DataCount); + if (get_bcc(&pSMBr->hdr) < 2 || data_offset > 512) { + /* BB also check enough total bytes returned */ + rc = -EIO; /* bad smb */ + goto qreparse_out; + } + if (!data_count || (data_count > 2048)) { + rc = -EIO; + cifs_dbg(FYI, "Invalid return data count on get reparse info ioctl\n"); + goto qreparse_out; + } + end_of_smb = 2 + get_bcc(&pSMBr->hdr) + (char *)&pSMBr->ByteCount; + reparse_buf = (struct reparse_symlink_data *) + ((char *)&pSMBr->hdr.Protocol + data_offset); + if ((char *)reparse_buf >= end_of_smb) { + rc = -EIO; + goto qreparse_out; + } + if (reparse_buf->ReparseTag == cpu_to_le32(IO_REPARSE_TAG_NFS)) { + cifs_dbg(FYI, "NFS style reparse tag\n"); + posix_buf = (struct reparse_posix_data *)reparse_buf; + + if (posix_buf->InodeType != cpu_to_le64(NFS_SPECFILE_LNK)) { + cifs_dbg(FYI, "unsupported file type 0x%llx\n", + le64_to_cpu(posix_buf->InodeType)); + rc = -EOPNOTSUPP; + goto qreparse_out; + } + is_unicode = true; + sub_len = le16_to_cpu(reparse_buf->ReparseDataLength); + if (posix_buf->PathBuffer + sub_len > end_of_smb) { + cifs_dbg(FYI, "reparse buf beyond SMB\n"); + rc = -EIO; + goto qreparse_out; + } + *symlinkinfo = cifs_strndup_from_utf16(posix_buf->PathBuffer, + sub_len, is_unicode, nls_codepage); + goto qreparse_out; + } else if (reparse_buf->ReparseTag != + cpu_to_le32(IO_REPARSE_TAG_SYMLINK)) { + rc = -EOPNOTSUPP; + goto qreparse_out; + } + + /* Reparse tag is NTFS symlink */ + sub_start = le16_to_cpu(reparse_buf->SubstituteNameOffset) + + reparse_buf->PathBuffer; + sub_len = le16_to_cpu(reparse_buf->SubstituteNameLength); + if (sub_start + sub_len > end_of_smb) { + cifs_dbg(FYI, "reparse buf beyond SMB\n"); + rc = -EIO; + goto qreparse_out; + } + if (pSMBr->hdr.Flags2 & SMBFLG2_UNICODE) + is_unicode = true; + else + is_unicode = false; + + /* BB FIXME investigate remapping reserved chars here */ + *symlinkinfo = cifs_strndup_from_utf16(sub_start, sub_len, is_unicode, + nls_codepage); + if (!*symlinkinfo) + rc = -ENOMEM; +qreparse_out: + cifs_buf_release(pSMB); + + /* + * Note: On -EAGAIN error only caller can retry on handle based calls + * since file handle passed in no longer valid. + */ + return rc; +} + +int +CIFSSMB_set_compression(const unsigned int xid, struct cifs_tcon *tcon, + __u16 fid) +{ + int rc = 0; + int bytes_returned; + struct smb_com_transaction_compr_ioctl_req *pSMB; + struct smb_com_transaction_ioctl_rsp *pSMBr; + + cifs_dbg(FYI, "Set compression for %u\n", fid); + rc = smb_init(SMB_COM_NT_TRANSACT, 23, tcon, (void **) &pSMB, + (void **) &pSMBr); + if (rc) + return rc; + + pSMB->compression_state = cpu_to_le16(COMPRESSION_FORMAT_DEFAULT); + + pSMB->TotalParameterCount = 0; + pSMB->TotalDataCount = cpu_to_le32(2); + pSMB->MaxParameterCount = 0; + pSMB->MaxDataCount = 0; + pSMB->MaxSetupCount = 4; + pSMB->Reserved = 0; + pSMB->ParameterOffset = 0; + pSMB->DataCount = cpu_to_le32(2); + pSMB->DataOffset = + cpu_to_le32(offsetof(struct smb_com_transaction_compr_ioctl_req, + compression_state) - 4); /* 84 */ + pSMB->SetupCount = 4; + pSMB->SubCommand = cpu_to_le16(NT_TRANSACT_IOCTL); + pSMB->ParameterCount = 0; + pSMB->FunctionCode = cpu_to_le32(FSCTL_SET_COMPRESSION); + pSMB->IsFsctl = 1; /* FSCTL */ + pSMB->IsRootFlag = 0; + pSMB->Fid = fid; /* file handle always le */ + /* 3 byte pad, followed by 2 byte compress state */ + pSMB->ByteCount = cpu_to_le16(5); + inc_rfc1001_len(pSMB, 5); + + rc = SendReceive(xid, tcon->ses, (struct smb_hdr *) pSMB, + (struct smb_hdr *) pSMBr, &bytes_returned, 0); + if (rc) + cifs_dbg(FYI, "Send error in SetCompression = %d\n", rc); + + cifs_buf_release(pSMB); + + /* + * Note: On -EAGAIN error only caller can retry on handle based calls + * since file handle passed in no longer valid. + */ + return rc; +} + + +#ifdef CONFIG_CIFS_POSIX + +/*Convert an Access Control Entry from wire format to local POSIX xattr format*/ +static void cifs_convert_ace(struct posix_acl_xattr_entry *ace, + struct cifs_posix_ace *cifs_ace) +{ + /* u8 cifs fields do not need le conversion */ + ace->e_perm = cpu_to_le16(cifs_ace->cifs_e_perm); + ace->e_tag = cpu_to_le16(cifs_ace->cifs_e_tag); + ace->e_id = cpu_to_le32(le64_to_cpu(cifs_ace->cifs_uid)); +/* + cifs_dbg(FYI, "perm %d tag %d id %d\n", + ace->e_perm, ace->e_tag, ace->e_id); +*/ + + return; +} + +/* Convert ACL from CIFS POSIX wire format to local Linux POSIX ACL xattr */ +static int cifs_copy_posix_acl(char *trgt, char *src, const int buflen, + const int acl_type, const int size_of_data_area) +{ + int size = 0; + int i; + __u16 count; + struct cifs_posix_ace *pACE; + struct cifs_posix_acl *cifs_acl = (struct cifs_posix_acl *)src; + struct posix_acl_xattr_header *local_acl = (void *)trgt; + + if (le16_to_cpu(cifs_acl->version) != CIFS_ACL_VERSION) + return -EOPNOTSUPP; + + if (acl_type == ACL_TYPE_ACCESS) { + count = le16_to_cpu(cifs_acl->access_entry_count); + pACE = &cifs_acl->ace_array[0]; + size = sizeof(struct cifs_posix_acl); + size += sizeof(struct cifs_posix_ace) * count; + /* check if we would go beyond end of SMB */ + if (size_of_data_area < size) { + cifs_dbg(FYI, "bad CIFS POSIX ACL size %d vs. %d\n", + size_of_data_area, size); + return -EINVAL; + } + } else if (acl_type == ACL_TYPE_DEFAULT) { + count = le16_to_cpu(cifs_acl->access_entry_count); + size = sizeof(struct cifs_posix_acl); + size += sizeof(struct cifs_posix_ace) * count; +/* skip past access ACEs to get to default ACEs */ + pACE = &cifs_acl->ace_array[count]; + count = le16_to_cpu(cifs_acl->default_entry_count); + size += sizeof(struct cifs_posix_ace) * count; + /* check if we would go beyond end of SMB */ + if (size_of_data_area < size) + return -EINVAL; + } else { + /* illegal type */ + return -EINVAL; + } + + size = posix_acl_xattr_size(count); + if ((buflen == 0) || (local_acl == NULL)) { + /* used to query ACL EA size */ + } else if (size > buflen) { + return -ERANGE; + } else /* buffer big enough */ { + struct posix_acl_xattr_entry *ace = (void *)(local_acl + 1); + + local_acl->a_version = cpu_to_le32(POSIX_ACL_XATTR_VERSION); + for (i = 0; i < count ; i++) { + cifs_convert_ace(&ace[i], pACE); + pACE++; + } + } + return size; +} + +static void convert_ace_to_cifs_ace(struct cifs_posix_ace *cifs_ace, + const struct posix_acl_xattr_entry *local_ace) +{ + cifs_ace->cifs_e_perm = le16_to_cpu(local_ace->e_perm); + cifs_ace->cifs_e_tag = le16_to_cpu(local_ace->e_tag); + /* BB is there a better way to handle the large uid? */ + if (local_ace->e_id == cpu_to_le32(-1)) { + /* Probably no need to le convert -1 on any arch but can not hurt */ + cifs_ace->cifs_uid = cpu_to_le64(-1); + } else + cifs_ace->cifs_uid = cpu_to_le64(le32_to_cpu(local_ace->e_id)); +/* + cifs_dbg(FYI, "perm %d tag %d id %d\n", + ace->e_perm, ace->e_tag, ace->e_id); +*/ +} + +/* Convert ACL from local Linux POSIX xattr to CIFS POSIX ACL wire format */ +static __u16 ACL_to_cifs_posix(char *parm_data, const char *pACL, + const int buflen, const int acl_type) +{ + __u16 rc = 0; + struct cifs_posix_acl *cifs_acl = (struct cifs_posix_acl *)parm_data; + struct posix_acl_xattr_header *local_acl = (void *)pACL; + struct posix_acl_xattr_entry *ace = (void *)(local_acl + 1); + int count; + int i; + + if ((buflen == 0) || (pACL == NULL) || (cifs_acl == NULL)) + return 0; + + count = posix_acl_xattr_count((size_t)buflen); + cifs_dbg(FYI, "setting acl with %d entries from buf of length %d and version of %d\n", + count, buflen, le32_to_cpu(local_acl->a_version)); + if (le32_to_cpu(local_acl->a_version) != 2) { + cifs_dbg(FYI, "unknown POSIX ACL version %d\n", + le32_to_cpu(local_acl->a_version)); + return 0; + } + cifs_acl->version = cpu_to_le16(1); + if (acl_type == ACL_TYPE_ACCESS) { + cifs_acl->access_entry_count = cpu_to_le16(count); + cifs_acl->default_entry_count = cpu_to_le16(0xFFFF); + } else if (acl_type == ACL_TYPE_DEFAULT) { + cifs_acl->default_entry_count = cpu_to_le16(count); + cifs_acl->access_entry_count = cpu_to_le16(0xFFFF); + } else { + cifs_dbg(FYI, "unknown ACL type %d\n", acl_type); + return 0; + } + for (i = 0; i < count; i++) + convert_ace_to_cifs_ace(&cifs_acl->ace_array[i], &ace[i]); + if (rc == 0) { + rc = (__u16)(count * sizeof(struct cifs_posix_ace)); + rc += sizeof(struct cifs_posix_acl); + /* BB add check to make sure ACL does not overflow SMB */ + } + return rc; +} + +int +CIFSSMBGetPosixACL(const unsigned int xid, struct cifs_tcon *tcon, + const unsigned char *searchName, + char *acl_inf, const int buflen, const int acl_type, + const struct nls_table *nls_codepage, int remap) +{ +/* SMB_QUERY_POSIX_ACL */ + TRANSACTION2_QPI_REQ *pSMB = NULL; + TRANSACTION2_QPI_RSP *pSMBr = NULL; + int rc = 0; + int bytes_returned; + int name_len; + __u16 params, byte_count; + + cifs_dbg(FYI, "In GetPosixACL (Unix) for path %s\n", searchName); + +queryAclRetry: + rc = smb_init(SMB_COM_TRANSACTION2, 15, tcon, (void **) &pSMB, + (void **) &pSMBr); + if (rc) + return rc; + + if (pSMB->hdr.Flags2 & SMBFLG2_UNICODE) { + name_len = + cifsConvertToUTF16((__le16 *) pSMB->FileName, + searchName, PATH_MAX, nls_codepage, + remap); + name_len++; /* trailing null */ + name_len *= 2; + pSMB->FileName[name_len] = 0; + pSMB->FileName[name_len+1] = 0; + } else { + name_len = copy_path_name(pSMB->FileName, searchName); + } + + params = 2 /* level */ + 4 /* rsrvd */ + name_len /* incl null */ ; + pSMB->TotalDataCount = 0; + pSMB->MaxParameterCount = cpu_to_le16(2); + /* BB find exact max data count below from sess structure BB */ + pSMB->MaxDataCount = cpu_to_le16(4000); + pSMB->MaxSetupCount = 0; + pSMB->Reserved = 0; + pSMB->Flags = 0; + pSMB->Timeout = 0; + pSMB->Reserved2 = 0; + pSMB->ParameterOffset = cpu_to_le16( + offsetof(struct smb_com_transaction2_qpi_req, + InformationLevel) - 4); + pSMB->DataCount = 0; + pSMB->DataOffset = 0; + pSMB->SetupCount = 1; + pSMB->Reserved3 = 0; + pSMB->SubCommand = cpu_to_le16(TRANS2_QUERY_PATH_INFORMATION); + byte_count = params + 1 /* pad */ ; + pSMB->TotalParameterCount = cpu_to_le16(params); + pSMB->ParameterCount = pSMB->TotalParameterCount; + pSMB->InformationLevel = cpu_to_le16(SMB_QUERY_POSIX_ACL); + pSMB->Reserved4 = 0; + inc_rfc1001_len(pSMB, 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); + cifs_stats_inc(&tcon->stats.cifs_stats.num_acl_get); + if (rc) { + cifs_dbg(FYI, "Send error in Query POSIX ACL = %d\n", rc); + } else { + /* decode response */ + + rc = validate_t2((struct smb_t2_rsp *)pSMBr); + /* BB also check enough total bytes returned */ + if (rc || get_bcc(&pSMBr->hdr) < 2) + rc = -EIO; /* bad smb */ + else { + __u16 data_offset = le16_to_cpu(pSMBr->t2.DataOffset); + __u16 count = le16_to_cpu(pSMBr->t2.DataCount); + rc = cifs_copy_posix_acl(acl_inf, + (char *)&pSMBr->hdr.Protocol+data_offset, + buflen, acl_type, count); + } + } + cifs_buf_release(pSMB); + if (rc == -EAGAIN) + goto queryAclRetry; + return rc; +} + +int +CIFSSMBSetPosixACL(const unsigned int xid, struct cifs_tcon *tcon, + const unsigned char *fileName, + const char *local_acl, const int buflen, + const int acl_type, + const struct nls_table *nls_codepage, int remap) +{ + struct smb_com_transaction2_spi_req *pSMB = NULL; + struct smb_com_transaction2_spi_rsp *pSMBr = NULL; + char *parm_data; + int name_len; + int rc = 0; + int bytes_returned = 0; + __u16 params, byte_count, data_count, param_offset, offset; + + cifs_dbg(FYI, "In SetPosixACL (Unix) for path %s\n", fileName); +setAclRetry: + rc = smb_init(SMB_COM_TRANSACTION2, 15, tcon, (void **) &pSMB, + (void **) &pSMBr); + if (rc) + return rc; + if (pSMB->hdr.Flags2 & SMBFLG2_UNICODE) { + name_len = + cifsConvertToUTF16((__le16 *) pSMB->FileName, fileName, + PATH_MAX, nls_codepage, remap); + name_len++; /* trailing null */ + name_len *= 2; + } else { + name_len = copy_path_name(pSMB->FileName, fileName); + } + params = 6 + name_len; + pSMB->MaxParameterCount = cpu_to_le16(2); + /* BB find max SMB size from sess */ + pSMB->MaxDataCount = cpu_to_le16(1000); + pSMB->MaxSetupCount = 0; + pSMB->Reserved = 0; + pSMB->Flags = 0; + pSMB->Timeout = 0; + pSMB->Reserved2 = 0; + param_offset = offsetof(struct smb_com_transaction2_spi_req, + InformationLevel) - 4; + offset = param_offset + params; + parm_data = ((char *) &pSMB->hdr.Protocol) + offset; + pSMB->ParameterOffset = cpu_to_le16(param_offset); + + /* convert to on the wire format for POSIX ACL */ + data_count = ACL_to_cifs_posix(parm_data, local_acl, buflen, acl_type); + + if (data_count == 0) { + rc = -EOPNOTSUPP; + goto setACLerrorExit; + } + pSMB->DataOffset = cpu_to_le16(offset); + pSMB->SetupCount = 1; + pSMB->Reserved3 = 0; + pSMB->SubCommand = cpu_to_le16(TRANS2_SET_PATH_INFORMATION); + pSMB->InformationLevel = cpu_to_le16(SMB_SET_POSIX_ACL); + byte_count = 3 /* pad */ + params + data_count; + pSMB->DataCount = cpu_to_le16(data_count); + pSMB->TotalDataCount = pSMB->DataCount; + pSMB->ParameterCount = cpu_to_le16(params); + pSMB->TotalParameterCount = pSMB->ParameterCount; + pSMB->Reserved4 = 0; + inc_rfc1001_len(pSMB, 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) + cifs_dbg(FYI, "Set POSIX ACL returned %d\n", rc); + +setACLerrorExit: + cifs_buf_release(pSMB); + if (rc == -EAGAIN) + goto setAclRetry; + return rc; +} + +int +CIFSGetExtAttr(const unsigned int xid, struct cifs_tcon *tcon, + const int netfid, __u64 *pExtAttrBits, __u64 *pMask) +{ + int rc = 0; + struct smb_t2_qfi_req *pSMB = NULL; + struct smb_t2_qfi_rsp *pSMBr = NULL; + int bytes_returned; + __u16 params, byte_count; + + cifs_dbg(FYI, "In GetExtAttr\n"); + if (tcon == NULL) + return -ENODEV; + +GetExtAttrRetry: + rc = smb_init(SMB_COM_TRANSACTION2, 15, tcon, (void **) &pSMB, + (void **) &pSMBr); + if (rc) + return rc; + + params = 2 /* level */ + 2 /* fid */; + pSMB->t2.TotalDataCount = 0; + pSMB->t2.MaxParameterCount = cpu_to_le16(4); + /* BB find exact max data count below from sess structure BB */ + pSMB->t2.MaxDataCount = cpu_to_le16(4000); + pSMB->t2.MaxSetupCount = 0; + pSMB->t2.Reserved = 0; + pSMB->t2.Flags = 0; + pSMB->t2.Timeout = 0; + pSMB->t2.Reserved2 = 0; + pSMB->t2.ParameterOffset = cpu_to_le16(offsetof(struct smb_t2_qfi_req, + Fid) - 4); + pSMB->t2.DataCount = 0; + pSMB->t2.DataOffset = 0; + pSMB->t2.SetupCount = 1; + pSMB->t2.Reserved3 = 0; + pSMB->t2.SubCommand = cpu_to_le16(TRANS2_QUERY_FILE_INFORMATION); + byte_count = params + 1 /* pad */ ; + pSMB->t2.TotalParameterCount = cpu_to_le16(params); + pSMB->t2.ParameterCount = pSMB->t2.TotalParameterCount; + pSMB->InformationLevel = cpu_to_le16(SMB_QUERY_ATTR_FLAGS); + pSMB->Pad = 0; + pSMB->Fid = netfid; + inc_rfc1001_len(pSMB, byte_count); + pSMB->t2.ByteCount = cpu_to_le16(byte_count); + + rc = SendReceive(xid, tcon->ses, (struct smb_hdr *) pSMB, + (struct smb_hdr *) pSMBr, &bytes_returned, 0); + if (rc) { + cifs_dbg(FYI, "error %d in GetExtAttr\n", rc); + } else { + /* decode response */ + rc = validate_t2((struct smb_t2_rsp *)pSMBr); + /* BB also check enough total bytes returned */ + if (rc || get_bcc(&pSMBr->hdr) < 2) + /* If rc should we check for EOPNOSUPP and + disable the srvino flag? or in caller? */ + rc = -EIO; /* bad smb */ + else { + __u16 data_offset = le16_to_cpu(pSMBr->t2.DataOffset); + __u16 count = le16_to_cpu(pSMBr->t2.DataCount); + struct file_chattr_info *pfinfo; + + if (count != 16) { + cifs_dbg(FYI, "Invalid size ret in GetExtAttr\n"); + rc = -EIO; + goto GetExtAttrOut; + } + pfinfo = (struct file_chattr_info *) + (data_offset + (char *) &pSMBr->hdr.Protocol); + *pExtAttrBits = le64_to_cpu(pfinfo->mode); + *pMask = le64_to_cpu(pfinfo->mask); + } + } +GetExtAttrOut: + cifs_buf_release(pSMB); + if (rc == -EAGAIN) + goto GetExtAttrRetry; + return rc; +} + +#endif /* CONFIG_POSIX */ + +/* + * Initialize NT TRANSACT SMB into small smb request buffer. This assumes that + * all NT TRANSACTS that we init here have total parm and data under about 400 + * bytes (to fit in small cifs buffer size), which is the case so far, it + * easily fits. NB: Setup words themselves and ByteCount MaxSetupCount (size of + * returned setup area) and MaxParameterCount (returned parms size) must be set + * by caller + */ +static int +smb_init_nttransact(const __u16 sub_command, const int setup_count, + const int parm_len, struct cifs_tcon *tcon, + void **ret_buf) +{ + int rc; + __u32 temp_offset; + struct smb_com_ntransact_req *pSMB; + + rc = small_smb_init(SMB_COM_NT_TRANSACT, 19 + setup_count, tcon, + (void **)&pSMB); + if (rc) + return rc; + *ret_buf = (void *)pSMB; + pSMB->Reserved = 0; + pSMB->TotalParameterCount = cpu_to_le32(parm_len); + pSMB->TotalDataCount = 0; + pSMB->MaxDataCount = cpu_to_le32(CIFSMaxBufSize & 0xFFFFFF00); + pSMB->ParameterCount = pSMB->TotalParameterCount; + pSMB->DataCount = pSMB->TotalDataCount; + temp_offset = offsetof(struct smb_com_ntransact_req, Parms) + + (setup_count * 2) - 4 /* for rfc1001 length itself */; + pSMB->ParameterOffset = cpu_to_le32(temp_offset); + pSMB->DataOffset = cpu_to_le32(temp_offset + parm_len); + pSMB->SetupCount = setup_count; /* no need to le convert byte fields */ + pSMB->SubCommand = cpu_to_le16(sub_command); + return 0; +} + +static int +validate_ntransact(char *buf, char **ppparm, char **ppdata, + __u32 *pparmlen, __u32 *pdatalen) +{ + char *end_of_smb; + __u32 data_count, data_offset, parm_count, parm_offset; + struct smb_com_ntransact_rsp *pSMBr; + u16 bcc; + + *pdatalen = 0; + *pparmlen = 0; + + if (buf == NULL) + return -EINVAL; + + pSMBr = (struct smb_com_ntransact_rsp *)buf; + + bcc = get_bcc(&pSMBr->hdr); + end_of_smb = 2 /* sizeof byte count */ + bcc + + (char *)&pSMBr->ByteCount; + + data_offset = le32_to_cpu(pSMBr->DataOffset); + data_count = le32_to_cpu(pSMBr->DataCount); + parm_offset = le32_to_cpu(pSMBr->ParameterOffset); + parm_count = le32_to_cpu(pSMBr->ParameterCount); + + *ppparm = (char *)&pSMBr->hdr.Protocol + parm_offset; + *ppdata = (char *)&pSMBr->hdr.Protocol + data_offset; + + /* should we also check that parm and data areas do not overlap? */ + if (*ppparm > end_of_smb) { + cifs_dbg(FYI, "parms start after end of smb\n"); + return -EINVAL; + } else if (parm_count + *ppparm > end_of_smb) { + cifs_dbg(FYI, "parm end after end of smb\n"); + return -EINVAL; + } else if (*ppdata > end_of_smb) { + cifs_dbg(FYI, "data starts after end of smb\n"); + return -EINVAL; + } else if (data_count + *ppdata > end_of_smb) { + cifs_dbg(FYI, "data %p + count %d (%p) past smb end %p start %p\n", + *ppdata, data_count, (data_count + *ppdata), + end_of_smb, pSMBr); + return -EINVAL; + } else if (parm_count + data_count > bcc) { + cifs_dbg(FYI, "parm count and data count larger than SMB\n"); + return -EINVAL; + } + *pdatalen = data_count; + *pparmlen = parm_count; + return 0; +} + +/* Get Security Descriptor (by handle) from remote server for a file or dir */ +int +CIFSSMBGetCIFSACL(const unsigned int xid, struct cifs_tcon *tcon, __u16 fid, + struct cifs_ntsd **acl_inf, __u32 *pbuflen) +{ + int rc = 0; + int buf_type = 0; + QUERY_SEC_DESC_REQ *pSMB; + struct kvec iov[1]; + struct kvec rsp_iov; + + cifs_dbg(FYI, "GetCifsACL\n"); + + *pbuflen = 0; + *acl_inf = NULL; + + rc = smb_init_nttransact(NT_TRANSACT_QUERY_SECURITY_DESC, 0, + 8 /* parm len */, tcon, (void **) &pSMB); + if (rc) + return rc; + + pSMB->MaxParameterCount = cpu_to_le32(4); + /* BB TEST with big acls that might need to be e.g. larger than 16K */ + pSMB->MaxSetupCount = 0; + pSMB->Fid = fid; /* file handle always le */ + pSMB->AclFlags = cpu_to_le32(CIFS_ACL_OWNER | CIFS_ACL_GROUP | + CIFS_ACL_DACL); + pSMB->ByteCount = cpu_to_le16(11); /* 3 bytes pad + 8 bytes parm */ + inc_rfc1001_len(pSMB, 11); + iov[0].iov_base = (char *)pSMB; + iov[0].iov_len = be32_to_cpu(pSMB->hdr.smb_buf_length) + 4; + + rc = SendReceive2(xid, tcon->ses, iov, 1 /* num iovec */, &buf_type, + 0, &rsp_iov); + cifs_small_buf_release(pSMB); + cifs_stats_inc(&tcon->stats.cifs_stats.num_acl_get); + if (rc) { + cifs_dbg(FYI, "Send error in QuerySecDesc = %d\n", rc); + } else { /* decode response */ + __le32 *parm; + __u32 parm_len; + __u32 acl_len; + struct smb_com_ntransact_rsp *pSMBr; + char *pdata; + +/* validate_nttransact */ + rc = validate_ntransact(rsp_iov.iov_base, (char **)&parm, + &pdata, &parm_len, pbuflen); + if (rc) + goto qsec_out; + pSMBr = (struct smb_com_ntransact_rsp *)rsp_iov.iov_base; + + cifs_dbg(FYI, "smb %p parm %p data %p\n", + pSMBr, parm, *acl_inf); + + if (le32_to_cpu(pSMBr->ParameterCount) != 4) { + rc = -EIO; /* bad smb */ + *pbuflen = 0; + goto qsec_out; + } + +/* BB check that data area is minimum length and as big as acl_len */ + + acl_len = le32_to_cpu(*parm); + if (acl_len != *pbuflen) { + cifs_dbg(VFS, "acl length %d does not match %d\n", + acl_len, *pbuflen); + if (*pbuflen > acl_len) + *pbuflen = acl_len; + } + + /* check if buffer is big enough for the acl + header followed by the smallest SID */ + if ((*pbuflen < sizeof(struct cifs_ntsd) + 8) || + (*pbuflen >= 64 * 1024)) { + cifs_dbg(VFS, "bad acl length %d\n", *pbuflen); + rc = -EINVAL; + *pbuflen = 0; + } else { + *acl_inf = kmemdup(pdata, *pbuflen, GFP_KERNEL); + if (*acl_inf == NULL) { + *pbuflen = 0; + rc = -ENOMEM; + } + } + } +qsec_out: + free_rsp_buf(buf_type, rsp_iov.iov_base); + return rc; +} + +int +CIFSSMBSetCIFSACL(const unsigned int xid, struct cifs_tcon *tcon, __u16 fid, + struct cifs_ntsd *pntsd, __u32 acllen, int aclflag) +{ + __u16 byte_count, param_count, data_count, param_offset, data_offset; + int rc = 0; + int bytes_returned = 0; + SET_SEC_DESC_REQ *pSMB = NULL; + void *pSMBr; + +setCifsAclRetry: + rc = smb_init(SMB_COM_NT_TRANSACT, 19, tcon, (void **) &pSMB, &pSMBr); + if (rc) + return rc; + + pSMB->MaxSetupCount = 0; + pSMB->Reserved = 0; + + param_count = 8; + param_offset = offsetof(struct smb_com_transaction_ssec_req, Fid) - 4; + data_count = acllen; + data_offset = param_offset + param_count; + byte_count = 3 /* pad */ + param_count; + + pSMB->DataCount = cpu_to_le32(data_count); + pSMB->TotalDataCount = pSMB->DataCount; + pSMB->MaxParameterCount = cpu_to_le32(4); + pSMB->MaxDataCount = cpu_to_le32(16384); + pSMB->ParameterCount = cpu_to_le32(param_count); + pSMB->ParameterOffset = cpu_to_le32(param_offset); + pSMB->TotalParameterCount = pSMB->ParameterCount; + pSMB->DataOffset = cpu_to_le32(data_offset); + pSMB->SetupCount = 0; + pSMB->SubCommand = cpu_to_le16(NT_TRANSACT_SET_SECURITY_DESC); + pSMB->ByteCount = cpu_to_le16(byte_count+data_count); + + pSMB->Fid = fid; /* file handle always le */ + pSMB->Reserved2 = 0; + pSMB->AclFlags = cpu_to_le32(aclflag); + + if (pntsd && acllen) { + memcpy((char *)pSMBr + offsetof(struct smb_hdr, Protocol) + + data_offset, pntsd, acllen); + inc_rfc1001_len(pSMB, byte_count + data_count); + } else + inc_rfc1001_len(pSMB, byte_count); + + rc = SendReceive(xid, tcon->ses, (struct smb_hdr *) pSMB, + (struct smb_hdr *) pSMBr, &bytes_returned, 0); + + cifs_dbg(FYI, "SetCIFSACL bytes_returned: %d, rc: %d\n", + bytes_returned, rc); + if (rc) + cifs_dbg(FYI, "Set CIFS ACL returned %d\n", rc); + cifs_buf_release(pSMB); + + if (rc == -EAGAIN) + goto setCifsAclRetry; + + return (rc); +} + + +/* Legacy Query Path Information call for lookup to old servers such + as Win9x/WinME */ +int +SMBQueryInformation(const unsigned int xid, struct cifs_tcon *tcon, + const char *search_name, FILE_ALL_INFO *data, + 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; + + cifs_dbg(FYI, "In SMBQPath path %s\n", search_name); +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 = + cifsConvertToUTF16((__le16 *) pSMB->FileName, + search_name, PATH_MAX, nls_codepage, + remap); + name_len++; /* trailing null */ + name_len *= 2; + } else { + name_len = copy_path_name(pSMB->FileName, search_name); + } + pSMB->BufferFormat = 0x04; + name_len++; /* account for buffer type byte */ + inc_rfc1001_len(pSMB, (__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) { + cifs_dbg(FYI, "Send error in QueryInfo = %d\n", rc); + } else if (data) { + struct timespec64 ts; + __u32 time = le32_to_cpu(pSMBr->last_write_time); + + /* decode response */ + /* BB FIXME - add time zone adjustment BB */ + memset(data, 0, sizeof(FILE_ALL_INFO)); + ts.tv_nsec = 0; + ts.tv_sec = time; + /* decode time fields */ + data->ChangeTime = cpu_to_le64(cifs_UnixTimeToNT(ts)); + data->LastWriteTime = data->ChangeTime; + data->LastAccessTime = 0; + data->AllocationSize = + cpu_to_le64(le32_to_cpu(pSMBr->size)); + data->EndOfFile = data->AllocationSize; + data->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 +CIFSSMBQFileInfo(const unsigned int xid, struct cifs_tcon *tcon, + u16 netfid, FILE_ALL_INFO *pFindData) +{ + struct smb_t2_qfi_req *pSMB = NULL; + struct smb_t2_qfi_rsp *pSMBr = NULL; + int rc = 0; + int bytes_returned; + __u16 params, byte_count; + +QFileInfoRetry: + rc = smb_init(SMB_COM_TRANSACTION2, 15, tcon, (void **) &pSMB, + (void **) &pSMBr); + if (rc) + return rc; + + params = 2 /* level */ + 2 /* fid */; + pSMB->t2.TotalDataCount = 0; + pSMB->t2.MaxParameterCount = cpu_to_le16(4); + /* BB find exact max data count below from sess structure BB */ + pSMB->t2.MaxDataCount = cpu_to_le16(CIFSMaxBufSize); + pSMB->t2.MaxSetupCount = 0; + pSMB->t2.Reserved = 0; + pSMB->t2.Flags = 0; + pSMB->t2.Timeout = 0; + pSMB->t2.Reserved2 = 0; + pSMB->t2.ParameterOffset = cpu_to_le16(offsetof(struct smb_t2_qfi_req, + Fid) - 4); + pSMB->t2.DataCount = 0; + pSMB->t2.DataOffset = 0; + pSMB->t2.SetupCount = 1; + pSMB->t2.Reserved3 = 0; + pSMB->t2.SubCommand = cpu_to_le16(TRANS2_QUERY_FILE_INFORMATION); + byte_count = params + 1 /* pad */ ; + pSMB->t2.TotalParameterCount = cpu_to_le16(params); + pSMB->t2.ParameterCount = pSMB->t2.TotalParameterCount; + pSMB->InformationLevel = cpu_to_le16(SMB_QUERY_FILE_ALL_INFO); + pSMB->Pad = 0; + pSMB->Fid = netfid; + inc_rfc1001_len(pSMB, byte_count); + pSMB->t2.ByteCount = cpu_to_le16(byte_count); + + rc = SendReceive(xid, tcon->ses, (struct smb_hdr *) pSMB, + (struct smb_hdr *) pSMBr, &bytes_returned, 0); + if (rc) { + cifs_dbg(FYI, "Send error in QFileInfo = %d\n", rc); + } else { /* decode response */ + rc = validate_t2((struct smb_t2_rsp *)pSMBr); + + if (rc) /* BB add auto retry on EOPNOTSUPP? */ + rc = -EIO; + else if (get_bcc(&pSMBr->hdr) < 40) + rc = -EIO; /* bad smb */ + else if (pFindData) { + __u16 data_offset = le16_to_cpu(pSMBr->t2.DataOffset); + memcpy((char *) pFindData, + (char *) &pSMBr->hdr.Protocol + + data_offset, sizeof(FILE_ALL_INFO)); + } else + rc = -ENOMEM; + } + cifs_buf_release(pSMB); + if (rc == -EAGAIN) + goto QFileInfoRetry; + + return rc; +} + +int +CIFSSMBQPathInfo(const unsigned int xid, struct cifs_tcon *tcon, + const char *search_name, FILE_ALL_INFO *data, + int legacy /* old style infolevel */, + const struct nls_table *nls_codepage, int remap) +{ + /* level 263 SMB_QUERY_FILE_ALL_INFO */ + TRANSACTION2_QPI_REQ *pSMB = NULL; + TRANSACTION2_QPI_RSP *pSMBr = NULL; + int rc = 0; + int bytes_returned; + int name_len; + __u16 params, byte_count; + + /* cifs_dbg(FYI, "In QPathInfo path %s\n", search_name); */ +QPathInfoRetry: + rc = smb_init(SMB_COM_TRANSACTION2, 15, tcon, (void **) &pSMB, + (void **) &pSMBr); + if (rc) + return rc; + + if (pSMB->hdr.Flags2 & SMBFLG2_UNICODE) { + name_len = + cifsConvertToUTF16((__le16 *) pSMB->FileName, search_name, + PATH_MAX, nls_codepage, remap); + name_len++; /* trailing null */ + name_len *= 2; + } else { + name_len = copy_path_name(pSMB->FileName, search_name); + } + + params = 2 /* level */ + 4 /* reserved */ + name_len /* includes NUL */; + pSMB->TotalDataCount = 0; + pSMB->MaxParameterCount = cpu_to_le16(2); + /* BB find exact max SMB PDU from sess structure BB */ + pSMB->MaxDataCount = cpu_to_le16(4000); + pSMB->MaxSetupCount = 0; + pSMB->Reserved = 0; + pSMB->Flags = 0; + pSMB->Timeout = 0; + pSMB->Reserved2 = 0; + pSMB->ParameterOffset = cpu_to_le16(offsetof( + struct smb_com_transaction2_qpi_req, InformationLevel) - 4); + pSMB->DataCount = 0; + pSMB->DataOffset = 0; + pSMB->SetupCount = 1; + pSMB->Reserved3 = 0; + pSMB->SubCommand = cpu_to_le16(TRANS2_QUERY_PATH_INFORMATION); + byte_count = params + 1 /* pad */ ; + pSMB->TotalParameterCount = cpu_to_le16(params); + pSMB->ParameterCount = pSMB->TotalParameterCount; + if (legacy) + pSMB->InformationLevel = cpu_to_le16(SMB_INFO_STANDARD); + else + pSMB->InformationLevel = cpu_to_le16(SMB_QUERY_FILE_ALL_INFO); + pSMB->Reserved4 = 0; + inc_rfc1001_len(pSMB, 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) { + cifs_dbg(FYI, "Send error in QPathInfo = %d\n", rc); + } else { /* decode response */ + rc = validate_t2((struct smb_t2_rsp *)pSMBr); + + if (rc) /* BB add auto retry on EOPNOTSUPP? */ + rc = -EIO; + else if (!legacy && get_bcc(&pSMBr->hdr) < 40) + rc = -EIO; /* bad smb */ + else if (legacy && get_bcc(&pSMBr->hdr) < 24) + rc = -EIO; /* 24 or 26 expected but we do not read + last field */ + else if (data) { + int size; + __u16 data_offset = le16_to_cpu(pSMBr->t2.DataOffset); + + /* + * On legacy responses we do not read the last field, + * EAsize, fortunately since it varies by subdialect and + * also note it differs on Set vs Get, ie two bytes or 4 + * bytes depending but we don't care here. + */ + if (legacy) + size = sizeof(FILE_INFO_STANDARD); + else + size = sizeof(FILE_ALL_INFO); + memcpy((char *) data, (char *) &pSMBr->hdr.Protocol + + data_offset, size); + } else + rc = -ENOMEM; + } + cifs_buf_release(pSMB); + if (rc == -EAGAIN) + goto QPathInfoRetry; + + return rc; +} + +int +CIFSSMBUnixQFileInfo(const unsigned int xid, struct cifs_tcon *tcon, + u16 netfid, FILE_UNIX_BASIC_INFO *pFindData) +{ + struct smb_t2_qfi_req *pSMB = NULL; + struct smb_t2_qfi_rsp *pSMBr = NULL; + int rc = 0; + int bytes_returned; + __u16 params, byte_count; + +UnixQFileInfoRetry: + rc = smb_init(SMB_COM_TRANSACTION2, 15, tcon, (void **) &pSMB, + (void **) &pSMBr); + if (rc) + return rc; + + params = 2 /* level */ + 2 /* fid */; + pSMB->t2.TotalDataCount = 0; + pSMB->t2.MaxParameterCount = cpu_to_le16(4); + /* BB find exact max data count below from sess structure BB */ + pSMB->t2.MaxDataCount = cpu_to_le16(CIFSMaxBufSize); + pSMB->t2.MaxSetupCount = 0; + pSMB->t2.Reserved = 0; + pSMB->t2.Flags = 0; + pSMB->t2.Timeout = 0; + pSMB->t2.Reserved2 = 0; + pSMB->t2.ParameterOffset = cpu_to_le16(offsetof(struct smb_t2_qfi_req, + Fid) - 4); + pSMB->t2.DataCount = 0; + pSMB->t2.DataOffset = 0; + pSMB->t2.SetupCount = 1; + pSMB->t2.Reserved3 = 0; + pSMB->t2.SubCommand = cpu_to_le16(TRANS2_QUERY_FILE_INFORMATION); + byte_count = params + 1 /* pad */ ; + pSMB->t2.TotalParameterCount = cpu_to_le16(params); + pSMB->t2.ParameterCount = pSMB->t2.TotalParameterCount; + pSMB->InformationLevel = cpu_to_le16(SMB_QUERY_FILE_UNIX_BASIC); + pSMB->Pad = 0; + pSMB->Fid = netfid; + inc_rfc1001_len(pSMB, byte_count); + pSMB->t2.ByteCount = cpu_to_le16(byte_count); + + rc = SendReceive(xid, tcon->ses, (struct smb_hdr *) pSMB, + (struct smb_hdr *) pSMBr, &bytes_returned, 0); + if (rc) { + cifs_dbg(FYI, "Send error in UnixQFileInfo = %d\n", rc); + } else { /* decode response */ + rc = validate_t2((struct smb_t2_rsp *)pSMBr); + + if (rc || get_bcc(&pSMBr->hdr) < sizeof(FILE_UNIX_BASIC_INFO)) { + cifs_dbg(VFS, "Malformed FILE_UNIX_BASIC_INFO response. Unix Extensions can be disabled on mount by specifying the nosfu mount option.\n"); + rc = -EIO; /* bad smb */ + } else { + __u16 data_offset = le16_to_cpu(pSMBr->t2.DataOffset); + memcpy((char *) pFindData, + (char *) &pSMBr->hdr.Protocol + + data_offset, + sizeof(FILE_UNIX_BASIC_INFO)); + } + } + + cifs_buf_release(pSMB); + if (rc == -EAGAIN) + goto UnixQFileInfoRetry; + + return rc; +} + +int +CIFSSMBUnixQPathInfo(const unsigned int xid, struct cifs_tcon *tcon, + const unsigned char *searchName, + FILE_UNIX_BASIC_INFO *pFindData, + const struct nls_table *nls_codepage, int remap) +{ +/* SMB_QUERY_FILE_UNIX_BASIC */ + TRANSACTION2_QPI_REQ *pSMB = NULL; + TRANSACTION2_QPI_RSP *pSMBr = NULL; + int rc = 0; + int bytes_returned = 0; + int name_len; + __u16 params, byte_count; + + cifs_dbg(FYI, "In QPathInfo (Unix) the path %s\n", searchName); +UnixQPathInfoRetry: + rc = smb_init(SMB_COM_TRANSACTION2, 15, tcon, (void **) &pSMB, + (void **) &pSMBr); + if (rc) + return rc; + + if (pSMB->hdr.Flags2 & SMBFLG2_UNICODE) { + name_len = + cifsConvertToUTF16((__le16 *) pSMB->FileName, searchName, + PATH_MAX, nls_codepage, remap); + name_len++; /* trailing null */ + name_len *= 2; + } else { + name_len = copy_path_name(pSMB->FileName, searchName); + } + + params = 2 /* level */ + 4 /* reserved */ + name_len /* includes NUL */; + pSMB->TotalDataCount = 0; + pSMB->MaxParameterCount = cpu_to_le16(2); + /* BB find exact max SMB PDU from sess structure BB */ + pSMB->MaxDataCount = cpu_to_le16(4000); + pSMB->MaxSetupCount = 0; + pSMB->Reserved = 0; + pSMB->Flags = 0; + pSMB->Timeout = 0; + pSMB->Reserved2 = 0; + pSMB->ParameterOffset = cpu_to_le16(offsetof( + struct smb_com_transaction2_qpi_req, InformationLevel) - 4); + pSMB->DataCount = 0; + pSMB->DataOffset = 0; + pSMB->SetupCount = 1; + pSMB->Reserved3 = 0; + pSMB->SubCommand = cpu_to_le16(TRANS2_QUERY_PATH_INFORMATION); + byte_count = params + 1 /* pad */ ; + pSMB->TotalParameterCount = cpu_to_le16(params); + pSMB->ParameterCount = pSMB->TotalParameterCount; + pSMB->InformationLevel = cpu_to_le16(SMB_QUERY_FILE_UNIX_BASIC); + pSMB->Reserved4 = 0; + inc_rfc1001_len(pSMB, 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) { + cifs_dbg(FYI, "Send error in UnixQPathInfo = %d\n", rc); + } else { /* decode response */ + rc = validate_t2((struct smb_t2_rsp *)pSMBr); + + if (rc || get_bcc(&pSMBr->hdr) < sizeof(FILE_UNIX_BASIC_INFO)) { + cifs_dbg(VFS, "Malformed FILE_UNIX_BASIC_INFO response. Unix Extensions can be disabled on mount by specifying the nosfu mount option.\n"); + rc = -EIO; /* bad smb */ + } else { + __u16 data_offset = le16_to_cpu(pSMBr->t2.DataOffset); + memcpy((char *) pFindData, + (char *) &pSMBr->hdr.Protocol + + data_offset, + sizeof(FILE_UNIX_BASIC_INFO)); + } + } + cifs_buf_release(pSMB); + if (rc == -EAGAIN) + goto UnixQPathInfoRetry; + + return rc; +} + +/* xid, tcon, searchName and codepage are input parms, rest are returned */ +int +CIFSFindFirst(const unsigned int xid, struct cifs_tcon *tcon, + const char *searchName, struct cifs_sb_info *cifs_sb, + __u16 *pnetfid, __u16 search_flags, + struct cifs_search_info *psrch_inf, bool msearch) +{ +/* level 257 SMB_ */ + TRANSACTION2_FFIRST_REQ *pSMB = NULL; + TRANSACTION2_FFIRST_RSP *pSMBr = NULL; + T2_FFIRST_RSP_PARMS *parms; + int rc = 0; + int bytes_returned = 0; + int name_len, remap; + __u16 params, byte_count; + struct nls_table *nls_codepage; + + cifs_dbg(FYI, "In FindFirst for %s\n", searchName); + +findFirstRetry: + rc = smb_init(SMB_COM_TRANSACTION2, 15, tcon, (void **) &pSMB, + (void **) &pSMBr); + if (rc) + return rc; + + nls_codepage = cifs_sb->local_nls; + remap = cifs_remap(cifs_sb); + + if (pSMB->hdr.Flags2 & SMBFLG2_UNICODE) { + name_len = + cifsConvertToUTF16((__le16 *) pSMB->FileName, searchName, + PATH_MAX, nls_codepage, remap); + /* We can not add the asterik earlier in case + it got remapped to 0xF03A as if it were part of the + directory name instead of a wildcard */ + name_len *= 2; + if (msearch) { + pSMB->FileName[name_len] = CIFS_DIR_SEP(cifs_sb); + pSMB->FileName[name_len+1] = 0; + pSMB->FileName[name_len+2] = '*'; + pSMB->FileName[name_len+3] = 0; + name_len += 4; /* now the trailing null */ + /* null terminate just in case */ + pSMB->FileName[name_len] = 0; + pSMB->FileName[name_len+1] = 0; + name_len += 2; + } + } else { + name_len = copy_path_name(pSMB->FileName, searchName); + if (msearch) { + if (WARN_ON_ONCE(name_len > PATH_MAX-2)) + name_len = PATH_MAX-2; + /* overwrite nul byte */ + pSMB->FileName[name_len-1] = CIFS_DIR_SEP(cifs_sb); + pSMB->FileName[name_len] = '*'; + pSMB->FileName[name_len+1] = 0; + name_len += 2; + } + } + + params = 12 + name_len /* includes null */ ; + pSMB->TotalDataCount = 0; /* no EAs */ + pSMB->MaxParameterCount = cpu_to_le16(10); + pSMB->MaxDataCount = cpu_to_le16(CIFSMaxBufSize & 0xFFFFFF00); + 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_ffirst_req, SearchAttributes) + - 4); + pSMB->DataCount = 0; + pSMB->DataOffset = 0; + pSMB->SetupCount = 1; /* one byte, no need to make endian neutral */ + pSMB->Reserved3 = 0; + pSMB->SubCommand = cpu_to_le16(TRANS2_FIND_FIRST); + pSMB->SearchAttributes = + cpu_to_le16(ATTR_READONLY | ATTR_HIDDEN | ATTR_SYSTEM | + ATTR_DIRECTORY); + pSMB->SearchCount = cpu_to_le16(CIFSMaxBufSize/sizeof(FILE_UNIX_INFO)); + pSMB->SearchFlags = cpu_to_le16(search_flags); + pSMB->InformationLevel = cpu_to_le16(psrch_inf->info_level); + + /* BB what should we set StorageType to? Does it matter? BB */ + pSMB->SearchStorageType = 0; + inc_rfc1001_len(pSMB, 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); + cifs_stats_inc(&tcon->stats.cifs_stats.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 */ + cifs_dbg(FYI, "Error in FindFirst = %d\n", rc); + + cifs_buf_release(pSMB); + + /* BB eventually could optimize out free and realloc of buf */ + /* for this case */ + if (rc == -EAGAIN) + goto findFirstRetry; + } else { /* decode response */ + /* BB remember to free buffer if error BB */ + rc = validate_t2((struct smb_t2_rsp *)pSMBr); + if (rc == 0) { + unsigned int lnoff; + + if (pSMBr->hdr.Flags2 & SMBFLG2_UNICODE) + psrch_inf->unicode = true; + else + psrch_inf->unicode = false; + + psrch_inf->ntwrk_buf_start = (char *)pSMBr; + psrch_inf->smallBuf = false; + psrch_inf->srch_entries_start = + (char *) &pSMBr->hdr.Protocol + + le16_to_cpu(pSMBr->t2.DataOffset); + parms = (T2_FFIRST_RSP_PARMS *)((char *) &pSMBr->hdr.Protocol + + le16_to_cpu(pSMBr->t2.ParameterOffset)); + + if (parms->EndofSearch) + psrch_inf->endOfSearch = true; + else + psrch_inf->endOfSearch = false; + + psrch_inf->entries_in_buffer = + le16_to_cpu(parms->SearchCount); + psrch_inf->index_of_last_entry = 2 /* skip . and .. */ + + psrch_inf->entries_in_buffer; + lnoff = le16_to_cpu(parms->LastNameOffset); + if (CIFSMaxBufSize < lnoff) { + cifs_dbg(VFS, "ignoring corrupt resume name\n"); + psrch_inf->last_entry = NULL; + return rc; + } + + psrch_inf->last_entry = psrch_inf->srch_entries_start + + lnoff; + + if (pnetfid) + *pnetfid = parms->SearchHandle; + } else { + cifs_buf_release(pSMB); + } + } + + return rc; +} + +int CIFSFindNext(const unsigned int xid, struct cifs_tcon *tcon, + __u16 searchHandle, __u16 search_flags, + struct cifs_search_info *psrch_inf) +{ + TRANSACTION2_FNEXT_REQ *pSMB = NULL; + TRANSACTION2_FNEXT_RSP *pSMBr = NULL; + T2_FNEXT_RSP_PARMS *parms; + char *response_data; + int rc = 0; + int bytes_returned; + unsigned int name_len; + __u16 params, byte_count; + + cifs_dbg(FYI, "In FindNext\n"); + + if (psrch_inf->endOfSearch) + return -ENOENT; + + rc = smb_init(SMB_COM_TRANSACTION2, 15, tcon, (void **) &pSMB, + (void **) &pSMBr); + if (rc) + return rc; + + params = 14; /* includes 2 bytes of null string, converted to LE below*/ + byte_count = 0; + pSMB->TotalDataCount = 0; /* no EAs */ + pSMB->MaxParameterCount = cpu_to_le16(8); + pSMB->MaxDataCount = cpu_to_le16(CIFSMaxBufSize & 0xFFFFFF00); + pSMB->MaxSetupCount = 0; + pSMB->Reserved = 0; + pSMB->Flags = 0; + pSMB->Timeout = 0; + pSMB->Reserved2 = 0; + pSMB->ParameterOffset = cpu_to_le16( + offsetof(struct smb_com_transaction2_fnext_req,SearchHandle) - 4); + pSMB->DataCount = 0; + pSMB->DataOffset = 0; + pSMB->SetupCount = 1; + pSMB->Reserved3 = 0; + pSMB->SubCommand = cpu_to_le16(TRANS2_FIND_NEXT); + pSMB->SearchHandle = searchHandle; /* always kept as le */ + pSMB->SearchCount = + cpu_to_le16(CIFSMaxBufSize / sizeof(FILE_UNIX_INFO)); + pSMB->InformationLevel = cpu_to_le16(psrch_inf->info_level); + pSMB->ResumeKey = psrch_inf->resume_key; + pSMB->SearchFlags = cpu_to_le16(search_flags); + + name_len = psrch_inf->resume_name_len; + params += name_len; + if (name_len < PATH_MAX) { + memcpy(pSMB->ResumeFileName, psrch_inf->presume_name, name_len); + byte_count += name_len; + /* 14 byte parm len above enough for 2 byte null terminator */ + pSMB->ResumeFileName[name_len] = 0; + pSMB->ResumeFileName[name_len+1] = 0; + } else { + rc = -EINVAL; + goto FNext2_err_exit; + } + byte_count = params + 1 /* pad */ ; + pSMB->TotalParameterCount = cpu_to_le16(params); + pSMB->ParameterCount = pSMB->TotalParameterCount; + inc_rfc1001_len(pSMB, 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); + cifs_stats_inc(&tcon->stats.cifs_stats.num_fnext); + if (rc) { + if (rc == -EBADF) { + psrch_inf->endOfSearch = true; + cifs_buf_release(pSMB); + rc = 0; /* search probably was closed at end of search*/ + } else + cifs_dbg(FYI, "FindNext returned = %d\n", rc); + } else { /* decode response */ + rc = validate_t2((struct smb_t2_rsp *)pSMBr); + + if (rc == 0) { + unsigned int lnoff; + + /* BB fixme add lock for file (srch_info) struct here */ + if (pSMBr->hdr.Flags2 & SMBFLG2_UNICODE) + psrch_inf->unicode = true; + else + psrch_inf->unicode = false; + response_data = (char *) &pSMBr->hdr.Protocol + + le16_to_cpu(pSMBr->t2.ParameterOffset); + parms = (T2_FNEXT_RSP_PARMS *)response_data; + response_data = (char *)&pSMBr->hdr.Protocol + + le16_to_cpu(pSMBr->t2.DataOffset); + if (psrch_inf->smallBuf) + cifs_small_buf_release( + psrch_inf->ntwrk_buf_start); + else + cifs_buf_release(psrch_inf->ntwrk_buf_start); + psrch_inf->srch_entries_start = response_data; + psrch_inf->ntwrk_buf_start = (char *)pSMB; + psrch_inf->smallBuf = false; + if (parms->EndofSearch) + psrch_inf->endOfSearch = true; + else + psrch_inf->endOfSearch = false; + psrch_inf->entries_in_buffer = + le16_to_cpu(parms->SearchCount); + psrch_inf->index_of_last_entry += + psrch_inf->entries_in_buffer; + lnoff = le16_to_cpu(parms->LastNameOffset); + if (CIFSMaxBufSize < lnoff) { + cifs_dbg(VFS, "ignoring corrupt resume name\n"); + psrch_inf->last_entry = NULL; + return rc; + } else + psrch_inf->last_entry = + psrch_inf->srch_entries_start + lnoff; + +/* cifs_dbg(FYI, "fnxt2 entries in buf %d index_of_last %d\n", + psrch_inf->entries_in_buffer, psrch_inf->index_of_last_entry); */ + + /* BB fixme add unlock here */ + } + + } + + /* BB On error, should we leave previous search buf (and count and + last entry fields) intact or free the previous one? */ + + /* Note: On -EAGAIN error only caller can retry on handle based calls + since file handle passed in no longer valid */ +FNext2_err_exit: + if (rc != 0) + cifs_buf_release(pSMB); + return rc; +} + +int +CIFSFindClose(const unsigned int xid, struct cifs_tcon *tcon, + const __u16 searchHandle) +{ + int rc = 0; + FINDCLOSE_REQ *pSMB = NULL; + + cifs_dbg(FYI, "In CIFSSMBFindClose\n"); + rc = small_smb_init(SMB_COM_FIND_CLOSE2, 1, tcon, (void **)&pSMB); + + /* no sense returning error if session restarted + as file handle has been closed */ + if (rc == -EAGAIN) + return 0; + if (rc) + return rc; + + pSMB->FileID = searchHandle; + pSMB->ByteCount = 0; + rc = SendReceiveNoRsp(xid, tcon->ses, (char *) pSMB, 0); + cifs_small_buf_release(pSMB); + if (rc) + cifs_dbg(VFS, "Send error in FindClose = %d\n", rc); + + cifs_stats_inc(&tcon->stats.cifs_stats.num_fclose); + + /* Since session is dead, search handle closed on server already */ + if (rc == -EAGAIN) + rc = 0; + + return rc; +} + +int +CIFSGetSrvInodeNumber(const unsigned int xid, struct cifs_tcon *tcon, + const char *search_name, __u64 *inode_number, + const struct nls_table *nls_codepage, int remap) +{ + int rc = 0; + TRANSACTION2_QPI_REQ *pSMB = NULL; + TRANSACTION2_QPI_RSP *pSMBr = NULL; + int name_len, bytes_returned; + __u16 params, byte_count; + + cifs_dbg(FYI, "In GetSrvInodeNum for %s\n", search_name); + if (tcon == NULL) + return -ENODEV; + +GetInodeNumberRetry: + rc = smb_init(SMB_COM_TRANSACTION2, 15, tcon, (void **) &pSMB, + (void **) &pSMBr); + if (rc) + return rc; + + if (pSMB->hdr.Flags2 & SMBFLG2_UNICODE) { + name_len = + cifsConvertToUTF16((__le16 *) pSMB->FileName, + search_name, PATH_MAX, nls_codepage, + remap); + name_len++; /* trailing null */ + name_len *= 2; + } else { + name_len = copy_path_name(pSMB->FileName, search_name); + } + + params = 2 /* level */ + 4 /* rsrvd */ + name_len /* incl null */ ; + pSMB->TotalDataCount = 0; + pSMB->MaxParameterCount = cpu_to_le16(2); + /* BB find exact max data count below from sess structure BB */ + pSMB->MaxDataCount = cpu_to_le16(4000); + pSMB->MaxSetupCount = 0; + pSMB->Reserved = 0; + pSMB->Flags = 0; + pSMB->Timeout = 0; + pSMB->Reserved2 = 0; + pSMB->ParameterOffset = cpu_to_le16(offsetof( + struct smb_com_transaction2_qpi_req, InformationLevel) - 4); + pSMB->DataCount = 0; + pSMB->DataOffset = 0; + pSMB->SetupCount = 1; + pSMB->Reserved3 = 0; + pSMB->SubCommand = cpu_to_le16(TRANS2_QUERY_PATH_INFORMATION); + byte_count = params + 1 /* pad */ ; + pSMB->TotalParameterCount = cpu_to_le16(params); + pSMB->ParameterCount = pSMB->TotalParameterCount; + pSMB->InformationLevel = cpu_to_le16(SMB_QUERY_FILE_INTERNAL_INFO); + pSMB->Reserved4 = 0; + inc_rfc1001_len(pSMB, 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) { + cifs_dbg(FYI, "error %d in QueryInternalInfo\n", rc); + } else { + /* decode response */ + rc = validate_t2((struct smb_t2_rsp *)pSMBr); + /* BB also check enough total bytes returned */ + if (rc || get_bcc(&pSMBr->hdr) < 2) + /* If rc should we check for EOPNOSUPP and + disable the srvino flag? or in caller? */ + rc = -EIO; /* bad smb */ + else { + __u16 data_offset = le16_to_cpu(pSMBr->t2.DataOffset); + __u16 count = le16_to_cpu(pSMBr->t2.DataCount); + struct file_internal_info *pfinfo; + /* BB Do we need a cast or hash here ? */ + if (count < 8) { + cifs_dbg(FYI, "Invalid size ret in QryIntrnlInf\n"); + rc = -EIO; + goto GetInodeNumOut; + } + pfinfo = (struct file_internal_info *) + (data_offset + (char *) &pSMBr->hdr.Protocol); + *inode_number = le64_to_cpu(pfinfo->UniqueId); + } + } +GetInodeNumOut: + cifs_buf_release(pSMB); + if (rc == -EAGAIN) + goto GetInodeNumberRetry; + return rc; +} + +int +CIFSGetDFSRefer(const unsigned int xid, struct cifs_ses *ses, + const char *search_name, struct dfs_info3_param **target_nodes, + unsigned int *num_of_nodes, + const struct nls_table *nls_codepage, int remap) +{ +/* TRANS2_GET_DFS_REFERRAL */ + TRANSACTION2_GET_DFS_REFER_REQ *pSMB = NULL; + TRANSACTION2_GET_DFS_REFER_RSP *pSMBr = NULL; + int rc = 0; + int bytes_returned; + int name_len; + __u16 params, byte_count; + *num_of_nodes = 0; + *target_nodes = NULL; + + cifs_dbg(FYI, "In GetDFSRefer the path %s\n", search_name); + if (ses == NULL || ses->tcon_ipc == NULL) + return -ENODEV; + +getDFSRetry: + /* + * Use smb_init_no_reconnect() instead of smb_init() as + * CIFSGetDFSRefer() may be called from cifs_reconnect_tcon() and thus + * causing an infinite recursion. + */ + rc = smb_init_no_reconnect(SMB_COM_TRANSACTION2, 15, ses->tcon_ipc, + (void **)&pSMB, (void **)&pSMBr); + if (rc) + return rc; + + /* server pointer checked in called function, + but should never be null here anyway */ + pSMB->hdr.Mid = get_next_mid(ses->server); + pSMB->hdr.Tid = ses->tcon_ipc->tid; + pSMB->hdr.Uid = ses->Suid; + if (ses->capabilities & CAP_STATUS32) + pSMB->hdr.Flags2 |= SMBFLG2_ERR_STATUS; + if (ses->capabilities & CAP_DFS) + pSMB->hdr.Flags2 |= SMBFLG2_DFS; + + if (ses->capabilities & CAP_UNICODE) { + pSMB->hdr.Flags2 |= SMBFLG2_UNICODE; + name_len = + cifsConvertToUTF16((__le16 *) pSMB->RequestFileName, + search_name, PATH_MAX, nls_codepage, + remap); + name_len++; /* trailing null */ + name_len *= 2; + } else { /* BB improve the check for buffer overruns BB */ + name_len = copy_path_name(pSMB->RequestFileName, search_name); + } + + if (ses->server->sign) + pSMB->hdr.Flags2 |= SMBFLG2_SECURITY_SIGNATURE; + + pSMB->hdr.Uid = ses->Suid; + + params = 2 /* level */ + name_len /*includes null */ ; + pSMB->TotalDataCount = 0; + pSMB->DataCount = 0; + pSMB->DataOffset = 0; + pSMB->MaxParameterCount = 0; + /* BB find exact max SMB PDU from sess structure BB */ + pSMB->MaxDataCount = cpu_to_le16(4000); + pSMB->MaxSetupCount = 0; + pSMB->Reserved = 0; + pSMB->Flags = 0; + pSMB->Timeout = 0; + pSMB->Reserved2 = 0; + pSMB->ParameterOffset = cpu_to_le16(offsetof( + struct smb_com_transaction2_get_dfs_refer_req, MaxReferralLevel) - 4); + pSMB->SetupCount = 1; + pSMB->Reserved3 = 0; + pSMB->SubCommand = cpu_to_le16(TRANS2_GET_DFS_REFERRAL); + byte_count = params + 3 /* pad */ ; + pSMB->ParameterCount = cpu_to_le16(params); + pSMB->TotalParameterCount = pSMB->ParameterCount; + pSMB->MaxReferralLevel = cpu_to_le16(3); + inc_rfc1001_len(pSMB, byte_count); + pSMB->ByteCount = cpu_to_le16(byte_count); + + rc = SendReceive(xid, ses, (struct smb_hdr *) pSMB, + (struct smb_hdr *) pSMBr, &bytes_returned, 0); + if (rc) { + cifs_dbg(FYI, "Send error in GetDFSRefer = %d\n", rc); + goto GetDFSRefExit; + } + rc = validate_t2((struct smb_t2_rsp *)pSMBr); + + /* BB Also check if enough total bytes returned? */ + if (rc || get_bcc(&pSMBr->hdr) < 17) { + rc = -EIO; /* bad smb */ + goto GetDFSRefExit; + } + + cifs_dbg(FYI, "Decoding GetDFSRefer response BCC: %d Offset %d\n", + get_bcc(&pSMBr->hdr), le16_to_cpu(pSMBr->t2.DataOffset)); + + /* parse returned result into more usable form */ + rc = parse_dfs_referrals(&pSMBr->dfs_data, + le16_to_cpu(pSMBr->t2.DataCount), + num_of_nodes, target_nodes, nls_codepage, + remap, search_name, + (pSMBr->hdr.Flags2 & SMBFLG2_UNICODE) != 0); + +GetDFSRefExit: + cifs_buf_release(pSMB); + + if (rc == -EAGAIN) + goto getDFSRetry; + + return rc; +} + +/* Query File System Info such as free space to old servers such as Win 9x */ +int +SMBOldQFSInfo(const unsigned int xid, struct cifs_tcon *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; + + cifs_dbg(FYI, "OldQFSInfo\n"); +oldQFSInfoRetry: + 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); + inc_rfc1001_len(pSMB, 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) { + cifs_dbg(FYI, "Send error in QFSInfo = %d\n", rc); + } else { /* decode response */ + rc = validate_t2((struct smb_t2_rsp *)pSMBr); + + if (rc || get_bcc(&pSMBr->hdr) < 18) + rc = -EIO; /* bad smb */ + else { + __u16 data_offset = le16_to_cpu(pSMBr->t2.DataOffset); + cifs_dbg(FYI, "qfsinf resp BCC: %d Offset %d\n", + get_bcc(&pSMBr->hdr), 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); + /* + * much prefer larger but if server doesn't report + * a valid size than 4K is a reasonable minimum + */ + if (FSData->f_bsize < 512) + FSData->f_bsize = 4096; + + FSData->f_blocks = + le32_to_cpu(response_data->TotalAllocationUnits); + FSData->f_bfree = FSData->f_bavail = + le32_to_cpu(response_data->FreeAllocationUnits); + cifs_dbg(FYI, "Blocks: %lld Free: %lld Block size %ld\n", + (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 unsigned int xid, struct cifs_tcon *tcon, + struct kstatfs *FSData) +{ +/* level 0x103 SMB_QUERY_FILE_SYSTEM_INFO */ + TRANSACTION2_QFSI_REQ *pSMB = NULL; + TRANSACTION2_QFSI_RSP *pSMBr = NULL; + FILE_SYSTEM_INFO *response_data; + int rc = 0; + int bytes_returned = 0; + __u16 params, byte_count; + + cifs_dbg(FYI, "In QFSInfo\n"); +QFSInfoRetry: + 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_QUERY_FS_SIZE_INFO); + inc_rfc1001_len(pSMB, 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) { + cifs_dbg(FYI, "Send error in QFSInfo = %d\n", rc); + } else { /* decode response */ + rc = validate_t2((struct smb_t2_rsp *)pSMBr); + + if (rc || get_bcc(&pSMBr->hdr) < 24) + rc = -EIO; /* bad smb */ + else { + __u16 data_offset = le16_to_cpu(pSMBr->t2.DataOffset); + + response_data = + (FILE_SYSTEM_INFO + *) (((char *) &pSMBr->hdr.Protocol) + + data_offset); + FSData->f_bsize = + le32_to_cpu(response_data->BytesPerSector) * + le32_to_cpu(response_data-> + SectorsPerAllocationUnit); + /* + * much prefer larger but if server doesn't report + * a valid size than 4K is a reasonable minimum + */ + if (FSData->f_bsize < 512) + FSData->f_bsize = 4096; + + FSData->f_blocks = + le64_to_cpu(response_data->TotalAllocationUnits); + FSData->f_bfree = FSData->f_bavail = + le64_to_cpu(response_data->FreeAllocationUnits); + cifs_dbg(FYI, "Blocks: %lld Free: %lld Block size %ld\n", + (unsigned long long)FSData->f_blocks, + (unsigned long long)FSData->f_bfree, + FSData->f_bsize); + } + } + cifs_buf_release(pSMB); + + if (rc == -EAGAIN) + goto QFSInfoRetry; + + return rc; +} + +int +CIFSSMBQFSAttributeInfo(const unsigned int xid, struct cifs_tcon *tcon) +{ +/* level 0x105 SMB_QUERY_FILE_SYSTEM_INFO */ + TRANSACTION2_QFSI_REQ *pSMB = NULL; + TRANSACTION2_QFSI_RSP *pSMBr = NULL; + FILE_SYSTEM_ATTRIBUTE_INFO *response_data; + int rc = 0; + int bytes_returned = 0; + __u16 params, byte_count; + + cifs_dbg(FYI, "In QFSAttributeInfo\n"); +QFSAttributeRetry: + 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); + /* 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; + 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_QUERY_FS_ATTRIBUTE_INFO); + inc_rfc1001_len(pSMB, 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) { + cifs_dbg(VFS, "Send error in QFSAttributeInfo = %d\n", rc); + } else { /* decode response */ + rc = validate_t2((struct smb_t2_rsp *)pSMBr); + + if (rc || get_bcc(&pSMBr->hdr) < 13) { + /* BB also check if enough bytes returned */ + rc = -EIO; /* bad smb */ + } else { + __u16 data_offset = le16_to_cpu(pSMBr->t2.DataOffset); + response_data = + (FILE_SYSTEM_ATTRIBUTE_INFO + *) (((char *) &pSMBr->hdr.Protocol) + + data_offset); + memcpy(&tcon->fsAttrInfo, response_data, + sizeof(FILE_SYSTEM_ATTRIBUTE_INFO)); + } + } + cifs_buf_release(pSMB); + + if (rc == -EAGAIN) + goto QFSAttributeRetry; + + return rc; +} + +int +CIFSSMBQFSDeviceInfo(const unsigned int xid, struct cifs_tcon *tcon) +{ +/* level 0x104 SMB_QUERY_FILE_SYSTEM_INFO */ + TRANSACTION2_QFSI_REQ *pSMB = NULL; + TRANSACTION2_QFSI_RSP *pSMBr = NULL; + FILE_SYSTEM_DEVICE_INFO *response_data; + int rc = 0; + int bytes_returned = 0; + __u16 params, byte_count; + + cifs_dbg(FYI, "In QFSDeviceInfo\n"); +QFSDeviceRetry: + 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); + /* 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; + 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_QUERY_FS_DEVICE_INFO); + inc_rfc1001_len(pSMB, 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) { + cifs_dbg(FYI, "Send error in QFSDeviceInfo = %d\n", rc); + } else { /* decode response */ + rc = validate_t2((struct smb_t2_rsp *)pSMBr); + + if (rc || get_bcc(&pSMBr->hdr) < + sizeof(FILE_SYSTEM_DEVICE_INFO)) + rc = -EIO; /* bad smb */ + else { + __u16 data_offset = le16_to_cpu(pSMBr->t2.DataOffset); + response_data = + (FILE_SYSTEM_DEVICE_INFO *) + (((char *) &pSMBr->hdr.Protocol) + + data_offset); + memcpy(&tcon->fsDevInfo, response_data, + sizeof(FILE_SYSTEM_DEVICE_INFO)); + } + } + cifs_buf_release(pSMB); + + if (rc == -EAGAIN) + goto QFSDeviceRetry; + + return rc; +} + +int +CIFSSMBQFSUnixInfo(const unsigned int xid, struct cifs_tcon *tcon) +{ +/* level 0x200 SMB_QUERY_CIFS_UNIX_INFO */ + TRANSACTION2_QFSI_REQ *pSMB = NULL; + TRANSACTION2_QFSI_RSP *pSMBr = NULL; + FILE_SYSTEM_UNIX_INFO *response_data; + int rc = 0; + int bytes_returned = 0; + __u16 params, byte_count; + + cifs_dbg(FYI, "In QFSUnixInfo\n"); +QFSUnixRetry: + rc = smb_init_no_reconnect(SMB_COM_TRANSACTION2, 15, tcon, + (void **) &pSMB, (void **) &pSMBr); + if (rc) + return rc; + + params = 2; /* level */ + pSMB->TotalDataCount = 0; + pSMB->DataCount = 0; + pSMB->DataOffset = 0; + pSMB->MaxParameterCount = cpu_to_le16(2); + /* BB find exact max SMB PDU from sess structure BB */ + pSMB->MaxDataCount = cpu_to_le16(100); + pSMB->MaxSetupCount = 0; + pSMB->Reserved = 0; + pSMB->Flags = 0; + pSMB->Timeout = 0; + pSMB->Reserved2 = 0; + byte_count = params + 1 /* pad */ ; + pSMB->ParameterCount = cpu_to_le16(params); + pSMB->TotalParameterCount = pSMB->ParameterCount; + pSMB->ParameterOffset = cpu_to_le16(offsetof(struct + smb_com_transaction2_qfsi_req, InformationLevel) - 4); + pSMB->SetupCount = 1; + pSMB->Reserved3 = 0; + pSMB->SubCommand = cpu_to_le16(TRANS2_QUERY_FS_INFORMATION); + pSMB->InformationLevel = cpu_to_le16(SMB_QUERY_CIFS_UNIX_INFO); + inc_rfc1001_len(pSMB, 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) { + cifs_dbg(VFS, "Send error in QFSUnixInfo = %d\n", rc); + } else { /* decode response */ + rc = validate_t2((struct smb_t2_rsp *)pSMBr); + + if (rc || get_bcc(&pSMBr->hdr) < 13) { + rc = -EIO; /* bad smb */ + } else { + __u16 data_offset = le16_to_cpu(pSMBr->t2.DataOffset); + response_data = + (FILE_SYSTEM_UNIX_INFO + *) (((char *) &pSMBr->hdr.Protocol) + + data_offset); + memcpy(&tcon->fsUnixInfo, response_data, + sizeof(FILE_SYSTEM_UNIX_INFO)); + } + } + cifs_buf_release(pSMB); + + if (rc == -EAGAIN) + goto QFSUnixRetry; + + + return rc; +} + +int +CIFSSMBSetFSUnixInfo(const unsigned int xid, struct cifs_tcon *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; + + cifs_dbg(FYI, "In SETFSUnixInfo\n"); +SETFSUnixRetry: + /* BB switch to small buf init to save memory */ + rc = smb_init_no_reconnect(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); + /* BB find exact max SMB PDU from sess structure BB */ + pSMB->MaxDataCount = cpu_to_le16(100); + 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); + + inc_rfc1001_len(pSMB, 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) { + cifs_dbg(VFS, "Send error in SETFSUnixInfo = %d\n", 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 unsigned int xid, struct cifs_tcon *tcon, + struct kstatfs *FSData) +{ +/* level 0x201 SMB_QUERY_CIFS_POSIX_INFO */ + TRANSACTION2_QFSI_REQ *pSMB = NULL; + TRANSACTION2_QFSI_RSP *pSMBr = NULL; + FILE_SYSTEM_POSIX_INFO *response_data; + int rc = 0; + int bytes_returned = 0; + __u16 params, byte_count; + + cifs_dbg(FYI, "In QFSPosixInfo\n"); +QFSPosixRetry: + rc = smb_init(SMB_COM_TRANSACTION2, 15, tcon, (void **) &pSMB, + (void **) &pSMBr); + if (rc) + return rc; + + params = 2; /* level */ + pSMB->TotalDataCount = 0; + pSMB->DataCount = 0; + pSMB->DataOffset = 0; + pSMB->MaxParameterCount = cpu_to_le16(2); + /* BB find exact max SMB PDU from sess structure BB */ + pSMB->MaxDataCount = cpu_to_le16(100); + pSMB->MaxSetupCount = 0; + pSMB->Reserved = 0; + pSMB->Flags = 0; + pSMB->Timeout = 0; + pSMB->Reserved2 = 0; + byte_count = params + 1 /* pad */ ; + pSMB->ParameterCount = cpu_to_le16(params); + pSMB->TotalParameterCount = pSMB->ParameterCount; + pSMB->ParameterOffset = cpu_to_le16(offsetof(struct + smb_com_transaction2_qfsi_req, InformationLevel) - 4); + pSMB->SetupCount = 1; + pSMB->Reserved3 = 0; + pSMB->SubCommand = cpu_to_le16(TRANS2_QUERY_FS_INFORMATION); + pSMB->InformationLevel = cpu_to_le16(SMB_QUERY_POSIX_FS_INFO); + inc_rfc1001_len(pSMB, 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) { + cifs_dbg(FYI, "Send error in QFSUnixInfo = %d\n", rc); + } else { /* decode response */ + rc = validate_t2((struct smb_t2_rsp *)pSMBr); + + if (rc || get_bcc(&pSMBr->hdr) < 13) { + rc = -EIO; /* bad smb */ + } else { + __u16 data_offset = le16_to_cpu(pSMBr->t2.DataOffset); + response_data = + (FILE_SYSTEM_POSIX_INFO + *) (((char *) &pSMBr->hdr.Protocol) + + data_offset); + FSData->f_bsize = + le32_to_cpu(response_data->BlockSize); + /* + * much prefer larger but if server doesn't report + * a valid size than 4K is a reasonable minimum + */ + if (FSData->f_bsize < 512) + FSData->f_bsize = 4096; + + FSData->f_blocks = + le64_to_cpu(response_data->TotalBlocks); + FSData->f_bfree = + le64_to_cpu(response_data->BlocksAvail); + 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 != cpu_to_le64(-1)) + FSData->f_files = + le64_to_cpu(response_data->TotalFileNodes); + if (response_data->FreeFileNodes != cpu_to_le64(-1)) + FSData->f_ffree = + le64_to_cpu(response_data->FreeFileNodes); + } + } + cifs_buf_release(pSMB); + + if (rc == -EAGAIN) + goto QFSPosixRetry; + + return rc; +} + + +/* + * We can not use write of zero bytes trick to set file size due to need for + * large file support. Also note that this SetPathInfo is preferred to + * SetFileInfo based method in next routine which is only needed to work around + * a sharing violation bugin Samba which this routine can run into. + */ +int +CIFSSMBSetEOF(const unsigned int xid, struct cifs_tcon *tcon, + const char *file_name, __u64 size, struct cifs_sb_info *cifs_sb, + bool set_allocation) +{ + struct smb_com_transaction2_spi_req *pSMB = NULL; + struct smb_com_transaction2_spi_rsp *pSMBr = NULL; + struct file_end_of_file_info *parm_data; + int name_len; + int rc = 0; + int bytes_returned = 0; + int remap = cifs_remap(cifs_sb); + + __u16 params, byte_count, data_count, param_offset, offset; + + cifs_dbg(FYI, "In SetEOF\n"); +SetEOFRetry: + rc = smb_init(SMB_COM_TRANSACTION2, 15, tcon, (void **) &pSMB, + (void **) &pSMBr); + if (rc) + return rc; + + if (pSMB->hdr.Flags2 & SMBFLG2_UNICODE) { + name_len = + cifsConvertToUTF16((__le16 *) pSMB->FileName, file_name, + PATH_MAX, cifs_sb->local_nls, remap); + name_len++; /* trailing null */ + name_len *= 2; + } else { + name_len = copy_path_name(pSMB->FileName, file_name); + } + params = 6 + name_len; + data_count = sizeof(struct file_end_of_file_info); + pSMB->MaxParameterCount = cpu_to_le16(2); + pSMB->MaxDataCount = cpu_to_le16(4100); + pSMB->MaxSetupCount = 0; + pSMB->Reserved = 0; + pSMB->Flags = 0; + pSMB->Timeout = 0; + pSMB->Reserved2 = 0; + param_offset = offsetof(struct smb_com_transaction2_spi_req, + InformationLevel) - 4; + offset = param_offset + params; + if (set_allocation) { + if (tcon->ses->capabilities & CAP_INFOLEVEL_PASSTHRU) + pSMB->InformationLevel = + cpu_to_le16(SMB_SET_FILE_ALLOCATION_INFO2); + else + pSMB->InformationLevel = + cpu_to_le16(SMB_SET_FILE_ALLOCATION_INFO); + } else /* Set File Size */ { + if (tcon->ses->capabilities & CAP_INFOLEVEL_PASSTHRU) + pSMB->InformationLevel = + cpu_to_le16(SMB_SET_FILE_END_OF_FILE_INFO2); + else + pSMB->InformationLevel = + cpu_to_le16(SMB_SET_FILE_END_OF_FILE_INFO); + } + + parm_data = + (struct file_end_of_file_info *) (((char *) &pSMB->hdr.Protocol) + + offset); + pSMB->ParameterOffset = cpu_to_le16(param_offset); + pSMB->DataOffset = cpu_to_le16(offset); + pSMB->SetupCount = 1; + pSMB->Reserved3 = 0; + pSMB->SubCommand = cpu_to_le16(TRANS2_SET_PATH_INFORMATION); + byte_count = 3 /* pad */ + params + data_count; + pSMB->DataCount = cpu_to_le16(data_count); + pSMB->TotalDataCount = pSMB->DataCount; + pSMB->ParameterCount = cpu_to_le16(params); + pSMB->TotalParameterCount = pSMB->ParameterCount; + pSMB->Reserved4 = 0; + inc_rfc1001_len(pSMB, byte_count); + parm_data->FileSize = cpu_to_le64(size); + 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) + cifs_dbg(FYI, "SetPathInfo (file size) returned %d\n", rc); + + cifs_buf_release(pSMB); + + if (rc == -EAGAIN) + goto SetEOFRetry; + + return rc; +} + +int +CIFSSMBSetFileSize(const unsigned int xid, struct cifs_tcon *tcon, + struct cifsFileInfo *cfile, __u64 size, bool set_allocation) +{ + struct smb_com_transaction2_sfi_req *pSMB = NULL; + struct file_end_of_file_info *parm_data; + int rc = 0; + __u16 params, param_offset, offset, byte_count, count; + + cifs_dbg(FYI, "SetFileSize (via SetFileInfo) %lld\n", + (long long)size); + rc = small_smb_init(SMB_COM_TRANSACTION2, 15, tcon, (void **) &pSMB); + + if (rc) + return rc; + + pSMB->hdr.Pid = cpu_to_le16((__u16)cfile->pid); + pSMB->hdr.PidHigh = cpu_to_le16((__u16)(cfile->pid >> 16)); + + params = 6; + pSMB->MaxSetupCount = 0; + pSMB->Reserved = 0; + pSMB->Flags = 0; + pSMB->Timeout = 0; + pSMB->Reserved2 = 0; + param_offset = offsetof(struct smb_com_transaction2_sfi_req, Fid) - 4; + offset = param_offset + params; + + count = sizeof(struct file_end_of_file_info); + pSMB->MaxParameterCount = cpu_to_le16(2); + /* BB find exact max SMB PDU from sess structure BB */ + pSMB->MaxDataCount = cpu_to_le16(1000); + pSMB->SetupCount = 1; + pSMB->Reserved3 = 0; + pSMB->SubCommand = cpu_to_le16(TRANS2_SET_FILE_INFORMATION); + byte_count = 3 /* pad */ + params + count; + pSMB->DataCount = cpu_to_le16(count); + pSMB->ParameterCount = cpu_to_le16(params); + pSMB->TotalDataCount = pSMB->DataCount; + pSMB->TotalParameterCount = pSMB->ParameterCount; + pSMB->ParameterOffset = cpu_to_le16(param_offset); + /* SMB offsets are from the beginning of SMB which is 4 bytes in, after RFC1001 field */ + parm_data = + (struct file_end_of_file_info *)(((char *)pSMB) + offset + 4); + pSMB->DataOffset = cpu_to_le16(offset); + parm_data->FileSize = cpu_to_le64(size); + pSMB->Fid = cfile->fid.netfid; + if (set_allocation) { + if (tcon->ses->capabilities & CAP_INFOLEVEL_PASSTHRU) + pSMB->InformationLevel = + cpu_to_le16(SMB_SET_FILE_ALLOCATION_INFO2); + else + pSMB->InformationLevel = + cpu_to_le16(SMB_SET_FILE_ALLOCATION_INFO); + } else /* Set File Size */ { + if (tcon->ses->capabilities & CAP_INFOLEVEL_PASSTHRU) + pSMB->InformationLevel = + cpu_to_le16(SMB_SET_FILE_END_OF_FILE_INFO2); + else + pSMB->InformationLevel = + cpu_to_le16(SMB_SET_FILE_END_OF_FILE_INFO); + } + pSMB->Reserved4 = 0; + inc_rfc1001_len(pSMB, byte_count); + pSMB->ByteCount = cpu_to_le16(byte_count); + rc = SendReceiveNoRsp(xid, tcon->ses, (char *) pSMB, 0); + cifs_small_buf_release(pSMB); + if (rc) { + cifs_dbg(FYI, "Send error in SetFileInfo (SetFileSize) = %d\n", + rc); + } + + /* Note: On -EAGAIN error only caller can retry on handle based calls + since file handle passed in no longer valid */ + + return rc; +} + +/* Some legacy servers such as NT4 require that the file times be set on + an open handle, rather than by pathname - this is awkward due to + potential access conflicts on the open, but it is unavoidable for these + old servers since the only other choice is to go from 100 nanosecond DCE + time and resort to the original setpathinfo level which takes the ancient + DOS time format with 2 second granularity */ +int +CIFSSMBSetFileInfo(const unsigned int xid, struct cifs_tcon *tcon, + const FILE_BASIC_INFO *data, __u16 fid, __u32 pid_of_opener) +{ + struct smb_com_transaction2_sfi_req *pSMB = NULL; + char *data_offset; + int rc = 0; + __u16 params, param_offset, offset, byte_count, count; + + cifs_dbg(FYI, "Set Times (via SetFileInfo)\n"); + rc = small_smb_init(SMB_COM_TRANSACTION2, 15, tcon, (void **) &pSMB); + + if (rc) + return rc; + + pSMB->hdr.Pid = cpu_to_le16((__u16)pid_of_opener); + pSMB->hdr.PidHigh = cpu_to_le16((__u16)(pid_of_opener >> 16)); + + params = 6; + pSMB->MaxSetupCount = 0; + pSMB->Reserved = 0; + pSMB->Flags = 0; + pSMB->Timeout = 0; + pSMB->Reserved2 = 0; + param_offset = offsetof(struct smb_com_transaction2_sfi_req, Fid) - 4; + offset = param_offset + params; + + data_offset = (char *)pSMB + + offsetof(struct smb_hdr, Protocol) + offset; + + count = sizeof(FILE_BASIC_INFO); + pSMB->MaxParameterCount = cpu_to_le16(2); + /* BB find max SMB PDU from sess */ + pSMB->MaxDataCount = cpu_to_le16(1000); + pSMB->SetupCount = 1; + pSMB->Reserved3 = 0; + pSMB->SubCommand = cpu_to_le16(TRANS2_SET_FILE_INFORMATION); + byte_count = 3 /* pad */ + params + count; + pSMB->DataCount = cpu_to_le16(count); + 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); + pSMB->Fid = fid; + if (tcon->ses->capabilities & CAP_INFOLEVEL_PASSTHRU) + pSMB->InformationLevel = cpu_to_le16(SMB_SET_FILE_BASIC_INFO2); + else + pSMB->InformationLevel = cpu_to_le16(SMB_SET_FILE_BASIC_INFO); + pSMB->Reserved4 = 0; + inc_rfc1001_len(pSMB, byte_count); + pSMB->ByteCount = cpu_to_le16(byte_count); + memcpy(data_offset, data, sizeof(FILE_BASIC_INFO)); + rc = SendReceiveNoRsp(xid, tcon->ses, (char *) pSMB, 0); + cifs_small_buf_release(pSMB); + if (rc) + cifs_dbg(FYI, "Send error in Set Time (SetFileInfo) = %d\n", + rc); + + /* Note: On -EAGAIN error only caller can retry on handle based calls + since file handle passed in no longer valid */ + + return rc; +} + +int +CIFSSMBSetFileDisposition(const unsigned int xid, struct cifs_tcon *tcon, + bool delete_file, __u16 fid, __u32 pid_of_opener) +{ + struct smb_com_transaction2_sfi_req *pSMB = NULL; + char *data_offset; + int rc = 0; + __u16 params, param_offset, offset, byte_count, count; + + cifs_dbg(FYI, "Set File Disposition (via SetFileInfo)\n"); + rc = small_smb_init(SMB_COM_TRANSACTION2, 15, tcon, (void **) &pSMB); + + if (rc) + return rc; + + pSMB->hdr.Pid = cpu_to_le16((__u16)pid_of_opener); + pSMB->hdr.PidHigh = cpu_to_le16((__u16)(pid_of_opener >> 16)); + + params = 6; + pSMB->MaxSetupCount = 0; + pSMB->Reserved = 0; + pSMB->Flags = 0; + pSMB->Timeout = 0; + pSMB->Reserved2 = 0; + param_offset = offsetof(struct smb_com_transaction2_sfi_req, Fid) - 4; + offset = param_offset + params; + + /* SMB offsets are from the beginning of SMB which is 4 bytes in, after RFC1001 field */ + data_offset = (char *)(pSMB) + offset + 4; + + count = 1; + pSMB->MaxParameterCount = cpu_to_le16(2); + /* BB find max SMB PDU from sess */ + pSMB->MaxDataCount = cpu_to_le16(1000); + pSMB->SetupCount = 1; + pSMB->Reserved3 = 0; + pSMB->SubCommand = cpu_to_le16(TRANS2_SET_FILE_INFORMATION); + byte_count = 3 /* pad */ + params + count; + pSMB->DataCount = cpu_to_le16(count); + 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); + pSMB->Fid = fid; + pSMB->InformationLevel = cpu_to_le16(SMB_SET_FILE_DISPOSITION_INFO); + pSMB->Reserved4 = 0; + inc_rfc1001_len(pSMB, byte_count); + pSMB->ByteCount = cpu_to_le16(byte_count); + *data_offset = delete_file ? 1 : 0; + rc = SendReceiveNoRsp(xid, tcon->ses, (char *) pSMB, 0); + cifs_small_buf_release(pSMB); + if (rc) + cifs_dbg(FYI, "Send error in SetFileDisposition = %d\n", rc); + + return rc; +} + +static int +CIFSSMBSetPathInfoFB(const unsigned int xid, struct cifs_tcon *tcon, + const char *fileName, const FILE_BASIC_INFO *data, + const struct nls_table *nls_codepage, + struct cifs_sb_info *cifs_sb) +{ + int oplock = 0; + struct cifs_open_parms oparms; + struct cifs_fid fid; + int rc; + + oparms = (struct cifs_open_parms) { + .tcon = tcon, + .cifs_sb = cifs_sb, + .desired_access = GENERIC_WRITE, + .create_options = cifs_create_options(cifs_sb, 0), + .disposition = FILE_OPEN, + .path = fileName, + .fid = &fid, + }; + + rc = CIFS_open(xid, &oparms, &oplock, NULL); + if (rc) + goto out; + + rc = CIFSSMBSetFileInfo(xid, tcon, data, fid.netfid, current->tgid); + CIFSSMBClose(xid, tcon, fid.netfid); +out: + + return rc; +} + +int +CIFSSMBSetPathInfo(const unsigned int xid, struct cifs_tcon *tcon, + const char *fileName, const FILE_BASIC_INFO *data, + const struct nls_table *nls_codepage, + struct cifs_sb_info *cifs_sb) +{ + TRANSACTION2_SPI_REQ *pSMB = NULL; + TRANSACTION2_SPI_RSP *pSMBr = NULL; + int name_len; + int rc = 0; + int bytes_returned = 0; + char *data_offset; + __u16 params, param_offset, offset, byte_count, count; + int remap = cifs_remap(cifs_sb); + + cifs_dbg(FYI, "In SetTimes\n"); + +SetTimesRetry: + rc = smb_init(SMB_COM_TRANSACTION2, 15, tcon, (void **) &pSMB, + (void **) &pSMBr); + if (rc) + return rc; + + if (pSMB->hdr.Flags2 & SMBFLG2_UNICODE) { + name_len = + cifsConvertToUTF16((__le16 *) pSMB->FileName, fileName, + PATH_MAX, nls_codepage, remap); + name_len++; /* trailing null */ + name_len *= 2; + } else { + name_len = copy_path_name(pSMB->FileName, fileName); + } + + params = 6 + name_len; + count = sizeof(FILE_BASIC_INFO); + pSMB->MaxParameterCount = cpu_to_le16(2); + /* BB find max SMB PDU from sess structure BB */ + pSMB->MaxDataCount = cpu_to_le16(1000); + pSMB->MaxSetupCount = 0; + pSMB->Reserved = 0; + pSMB->Flags = 0; + pSMB->Timeout = 0; + pSMB->Reserved2 = 0; + param_offset = offsetof(struct smb_com_transaction2_spi_req, + InformationLevel) - 4; + offset = param_offset + params; + data_offset = (char *) (&pSMB->hdr.Protocol) + offset; + pSMB->ParameterOffset = cpu_to_le16(param_offset); + pSMB->DataOffset = cpu_to_le16(offset); + pSMB->SetupCount = 1; + pSMB->Reserved3 = 0; + pSMB->SubCommand = cpu_to_le16(TRANS2_SET_PATH_INFORMATION); + byte_count = 3 /* pad */ + params + count; + + pSMB->DataCount = cpu_to_le16(count); + pSMB->ParameterCount = cpu_to_le16(params); + pSMB->TotalDataCount = pSMB->DataCount; + pSMB->TotalParameterCount = pSMB->ParameterCount; + if (tcon->ses->capabilities & CAP_INFOLEVEL_PASSTHRU) + pSMB->InformationLevel = cpu_to_le16(SMB_SET_FILE_BASIC_INFO2); + else + pSMB->InformationLevel = cpu_to_le16(SMB_SET_FILE_BASIC_INFO); + pSMB->Reserved4 = 0; + inc_rfc1001_len(pSMB, byte_count); + memcpy(data_offset, data, sizeof(FILE_BASIC_INFO)); + 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) + cifs_dbg(FYI, "SetPathInfo (times) returned %d\n", rc); + + cifs_buf_release(pSMB); + + if (rc == -EAGAIN) + goto SetTimesRetry; + + if (rc == -EOPNOTSUPP) + return CIFSSMBSetPathInfoFB(xid, tcon, fileName, data, + nls_codepage, cifs_sb); + + return rc; +} + +static void +cifs_fill_unix_set_info(FILE_UNIX_BASIC_INFO *data_offset, + const struct cifs_unix_set_info_args *args) +{ + u64 uid = NO_CHANGE_64, gid = NO_CHANGE_64; + u64 mode = args->mode; + + if (uid_valid(args->uid)) + uid = from_kuid(&init_user_ns, args->uid); + if (gid_valid(args->gid)) + gid = from_kgid(&init_user_ns, args->gid); + + /* + * Samba server ignores set of file size to zero due to bugs in some + * older clients, but we should be precise - we use SetFileSize to + * set file size and do not want to truncate file size to zero + * accidentally as happened on one Samba server beta by putting + * zero instead of -1 here + */ + data_offset->EndOfFile = cpu_to_le64(NO_CHANGE_64); + data_offset->NumOfBytes = cpu_to_le64(NO_CHANGE_64); + data_offset->LastStatusChange = cpu_to_le64(args->ctime); + data_offset->LastAccessTime = cpu_to_le64(args->atime); + data_offset->LastModificationTime = cpu_to_le64(args->mtime); + data_offset->Uid = cpu_to_le64(uid); + data_offset->Gid = cpu_to_le64(gid); + /* better to leave device as zero when it is */ + data_offset->DevMajor = cpu_to_le64(MAJOR(args->device)); + data_offset->DevMinor = cpu_to_le64(MINOR(args->device)); + data_offset->Permissions = cpu_to_le64(mode); + + if (S_ISREG(mode)) + data_offset->Type = cpu_to_le32(UNIX_FILE); + else if (S_ISDIR(mode)) + data_offset->Type = cpu_to_le32(UNIX_DIR); + else if (S_ISLNK(mode)) + data_offset->Type = cpu_to_le32(UNIX_SYMLINK); + else if (S_ISCHR(mode)) + data_offset->Type = cpu_to_le32(UNIX_CHARDEV); + else if (S_ISBLK(mode)) + data_offset->Type = cpu_to_le32(UNIX_BLOCKDEV); + else if (S_ISFIFO(mode)) + data_offset->Type = cpu_to_le32(UNIX_FIFO); + else if (S_ISSOCK(mode)) + data_offset->Type = cpu_to_le32(UNIX_SOCKET); +} + +int +CIFSSMBUnixSetFileInfo(const unsigned int xid, struct cifs_tcon *tcon, + const struct cifs_unix_set_info_args *args, + u16 fid, u32 pid_of_opener) +{ + struct smb_com_transaction2_sfi_req *pSMB = NULL; + char *data_offset; + int rc = 0; + u16 params, param_offset, offset, byte_count, count; + + cifs_dbg(FYI, "Set Unix Info (via SetFileInfo)\n"); + rc = small_smb_init(SMB_COM_TRANSACTION2, 15, tcon, (void **) &pSMB); + + if (rc) + return rc; + + pSMB->hdr.Pid = cpu_to_le16((__u16)pid_of_opener); + pSMB->hdr.PidHigh = cpu_to_le16((__u16)(pid_of_opener >> 16)); + + params = 6; + pSMB->MaxSetupCount = 0; + pSMB->Reserved = 0; + pSMB->Flags = 0; + pSMB->Timeout = 0; + pSMB->Reserved2 = 0; + param_offset = offsetof(struct smb_com_transaction2_sfi_req, Fid) - 4; + offset = param_offset + params; + + data_offset = (char *)pSMB + + offsetof(struct smb_hdr, Protocol) + offset; + + count = sizeof(FILE_UNIX_BASIC_INFO); + + pSMB->MaxParameterCount = cpu_to_le16(2); + /* BB find max SMB PDU from sess */ + pSMB->MaxDataCount = cpu_to_le16(1000); + pSMB->SetupCount = 1; + pSMB->Reserved3 = 0; + pSMB->SubCommand = cpu_to_le16(TRANS2_SET_FILE_INFORMATION); + byte_count = 3 /* pad */ + params + count; + pSMB->DataCount = cpu_to_le16(count); + 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); + pSMB->Fid = fid; + pSMB->InformationLevel = cpu_to_le16(SMB_SET_FILE_UNIX_BASIC); + pSMB->Reserved4 = 0; + inc_rfc1001_len(pSMB, byte_count); + pSMB->ByteCount = cpu_to_le16(byte_count); + + cifs_fill_unix_set_info((FILE_UNIX_BASIC_INFO *)data_offset, args); + + rc = SendReceiveNoRsp(xid, tcon->ses, (char *) pSMB, 0); + cifs_small_buf_release(pSMB); + if (rc) + cifs_dbg(FYI, "Send error in Set Time (SetFileInfo) = %d\n", + rc); + + /* Note: On -EAGAIN error only caller can retry on handle based calls + since file handle passed in no longer valid */ + + return rc; +} + +int +CIFSSMBUnixSetPathInfo(const unsigned int xid, struct cifs_tcon *tcon, + const char *file_name, + const struct cifs_unix_set_info_args *args, + const struct nls_table *nls_codepage, int remap) +{ + TRANSACTION2_SPI_REQ *pSMB = NULL; + TRANSACTION2_SPI_RSP *pSMBr = NULL; + int name_len; + int rc = 0; + int bytes_returned = 0; + FILE_UNIX_BASIC_INFO *data_offset; + __u16 params, param_offset, offset, count, byte_count; + + cifs_dbg(FYI, "In SetUID/GID/Mode\n"); +setPermsRetry: + rc = smb_init(SMB_COM_TRANSACTION2, 15, tcon, (void **) &pSMB, + (void **) &pSMBr); + if (rc) + return rc; + + if (pSMB->hdr.Flags2 & SMBFLG2_UNICODE) { + name_len = + cifsConvertToUTF16((__le16 *) pSMB->FileName, file_name, + PATH_MAX, nls_codepage, remap); + name_len++; /* trailing null */ + name_len *= 2; + } else { + name_len = copy_path_name(pSMB->FileName, file_name); + } + + params = 6 + name_len; + count = sizeof(FILE_UNIX_BASIC_INFO); + pSMB->MaxParameterCount = cpu_to_le16(2); + /* BB find max SMB PDU from sess structure BB */ + pSMB->MaxDataCount = cpu_to_le16(1000); + pSMB->MaxSetupCount = 0; + pSMB->Reserved = 0; + pSMB->Flags = 0; + pSMB->Timeout = 0; + pSMB->Reserved2 = 0; + param_offset = offsetof(struct smb_com_transaction2_spi_req, + InformationLevel) - 4; + offset = param_offset + params; + /* SMB offsets are from the beginning of SMB which is 4 bytes in, after RFC1001 field */ + data_offset = (FILE_UNIX_BASIC_INFO *)((char *) pSMB + offset + 4); + memset(data_offset, 0, count); + pSMB->DataOffset = cpu_to_le16(offset); + pSMB->ParameterOffset = cpu_to_le16(param_offset); + pSMB->SetupCount = 1; + pSMB->Reserved3 = 0; + pSMB->SubCommand = cpu_to_le16(TRANS2_SET_PATH_INFORMATION); + byte_count = 3 /* pad */ + params + count; + pSMB->ParameterCount = cpu_to_le16(params); + pSMB->DataCount = cpu_to_le16(count); + pSMB->TotalParameterCount = pSMB->ParameterCount; + pSMB->TotalDataCount = pSMB->DataCount; + pSMB->InformationLevel = cpu_to_le16(SMB_SET_FILE_UNIX_BASIC); + pSMB->Reserved4 = 0; + inc_rfc1001_len(pSMB, byte_count); + + cifs_fill_unix_set_info(data_offset, args); + + 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) + cifs_dbg(FYI, "SetPathInfo (perms) returned %d\n", rc); + + cifs_buf_release(pSMB); + if (rc == -EAGAIN) + goto setPermsRetry; + return rc; +} + +#ifdef CONFIG_CIFS_XATTR +/* + * Do a path-based QUERY_ALL_EAS call and parse the result. This is a common + * function used by listxattr and getxattr type calls. When ea_name is set, + * it looks for that attribute name and stuffs that value into the EAData + * buffer. When ea_name is NULL, it stuffs a list of attribute names into the + * buffer. In both cases, the return value is either the length of the + * resulting data or a negative error code. If EAData is a NULL pointer then + * the data isn't copied to it, but the length is returned. + */ +ssize_t +CIFSSMBQAllEAs(const unsigned int xid, struct cifs_tcon *tcon, + const unsigned char *searchName, const unsigned char *ea_name, + char *EAData, size_t buf_size, + struct cifs_sb_info *cifs_sb) +{ + /* BB assumes one setup word */ + TRANSACTION2_QPI_REQ *pSMB = NULL; + TRANSACTION2_QPI_RSP *pSMBr = NULL; + int remap = cifs_remap(cifs_sb); + struct nls_table *nls_codepage = cifs_sb->local_nls; + int rc = 0; + int bytes_returned; + int list_len; + struct fealist *ea_response_data; + struct fea *temp_fea; + char *temp_ptr; + char *end_of_smb; + __u16 params, byte_count, data_offset; + unsigned int ea_name_len = ea_name ? strlen(ea_name) : 0; + + cifs_dbg(FYI, "In Query All EAs path %s\n", searchName); +QAllEAsRetry: + rc = smb_init(SMB_COM_TRANSACTION2, 15, tcon, (void **) &pSMB, + (void **) &pSMBr); + if (rc) + return rc; + + if (pSMB->hdr.Flags2 & SMBFLG2_UNICODE) { + list_len = + cifsConvertToUTF16((__le16 *) pSMB->FileName, searchName, + PATH_MAX, nls_codepage, remap); + list_len++; /* trailing null */ + list_len *= 2; + } else { + list_len = copy_path_name(pSMB->FileName, searchName); + } + + params = 2 /* level */ + 4 /* reserved */ + list_len /* includes NUL */; + pSMB->TotalDataCount = 0; + pSMB->MaxParameterCount = cpu_to_le16(2); + /* BB find exact max SMB PDU from sess structure BB */ + pSMB->MaxDataCount = cpu_to_le16(CIFSMaxBufSize); + pSMB->MaxSetupCount = 0; + pSMB->Reserved = 0; + pSMB->Flags = 0; + pSMB->Timeout = 0; + pSMB->Reserved2 = 0; + pSMB->ParameterOffset = cpu_to_le16(offsetof( + struct smb_com_transaction2_qpi_req, InformationLevel) - 4); + pSMB->DataCount = 0; + pSMB->DataOffset = 0; + pSMB->SetupCount = 1; + pSMB->Reserved3 = 0; + pSMB->SubCommand = cpu_to_le16(TRANS2_QUERY_PATH_INFORMATION); + byte_count = params + 1 /* pad */ ; + pSMB->TotalParameterCount = cpu_to_le16(params); + pSMB->ParameterCount = pSMB->TotalParameterCount; + pSMB->InformationLevel = cpu_to_le16(SMB_INFO_QUERY_ALL_EAS); + pSMB->Reserved4 = 0; + inc_rfc1001_len(pSMB, 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) { + cifs_dbg(FYI, "Send error in QueryAllEAs = %d\n", rc); + goto QAllEAsOut; + } + + + /* BB also check enough total bytes returned */ + /* BB we need to improve the validity checking + of these trans2 responses */ + + rc = validate_t2((struct smb_t2_rsp *)pSMBr); + if (rc || get_bcc(&pSMBr->hdr) < 4) { + rc = -EIO; /* bad smb */ + goto QAllEAsOut; + } + + /* check that length of list is not more than bcc */ + /* check that each entry does not go beyond length + of list */ + /* check that each element of each entry does not + go beyond end of list */ + /* validate_trans2_offsets() */ + /* BB check if start of smb + data_offset > &bcc+ bcc */ + + data_offset = le16_to_cpu(pSMBr->t2.DataOffset); + ea_response_data = (struct fealist *) + (((char *) &pSMBr->hdr.Protocol) + data_offset); + + list_len = le32_to_cpu(ea_response_data->list_len); + cifs_dbg(FYI, "ea length %d\n", list_len); + if (list_len <= 8) { + cifs_dbg(FYI, "empty EA list returned from server\n"); + /* didn't find the named attribute */ + if (ea_name) + rc = -ENODATA; + goto QAllEAsOut; + } + + /* make sure list_len doesn't go past end of SMB */ + end_of_smb = (char *)pByteArea(&pSMBr->hdr) + get_bcc(&pSMBr->hdr); + if ((char *)ea_response_data + list_len > end_of_smb) { + cifs_dbg(FYI, "EA list appears to go beyond SMB\n"); + rc = -EIO; + goto QAllEAsOut; + } + + /* account for ea list len */ + list_len -= 4; + temp_fea = ea_response_data->list; + temp_ptr = (char *)temp_fea; + while (list_len > 0) { + unsigned int name_len; + __u16 value_len; + + list_len -= 4; + temp_ptr += 4; + /* make sure we can read name_len and value_len */ + if (list_len < 0) { + cifs_dbg(FYI, "EA entry goes beyond length of list\n"); + rc = -EIO; + goto QAllEAsOut; + } + + name_len = temp_fea->name_len; + value_len = le16_to_cpu(temp_fea->value_len); + list_len -= name_len + 1 + value_len; + if (list_len < 0) { + cifs_dbg(FYI, "EA entry goes beyond length of list\n"); + rc = -EIO; + goto QAllEAsOut; + } + + if (ea_name) { + if (ea_name_len == name_len && + memcmp(ea_name, temp_ptr, name_len) == 0) { + temp_ptr += name_len + 1; + rc = value_len; + if (buf_size == 0) + goto QAllEAsOut; + if ((size_t)value_len > buf_size) { + rc = -ERANGE; + goto QAllEAsOut; + } + memcpy(EAData, temp_ptr, value_len); + goto QAllEAsOut; + } + } else { + /* account for prefix user. and trailing null */ + rc += (5 + 1 + name_len); + if (rc < (int) buf_size) { + memcpy(EAData, "user.", 5); + EAData += 5; + memcpy(EAData, temp_ptr, name_len); + EAData += name_len; + /* null terminate name */ + *EAData = 0; + ++EAData; + } else if (buf_size == 0) { + /* skip copy - calc size only */ + } else { + /* stop before overrun buffer */ + rc = -ERANGE; + break; + } + } + temp_ptr += name_len + 1 + value_len; + temp_fea = (struct fea *)temp_ptr; + } + + /* didn't find the named attribute */ + if (ea_name) + rc = -ENODATA; + +QAllEAsOut: + cifs_buf_release(pSMB); + if (rc == -EAGAIN) + goto QAllEAsRetry; + + return (ssize_t)rc; +} + +int +CIFSSMBSetEA(const unsigned int xid, struct cifs_tcon *tcon, + const char *fileName, const char *ea_name, const void *ea_value, + const __u16 ea_value_len, const struct nls_table *nls_codepage, + struct cifs_sb_info *cifs_sb) +{ + struct smb_com_transaction2_spi_req *pSMB = NULL; + struct smb_com_transaction2_spi_rsp *pSMBr = NULL; + struct fealist *parm_data; + int name_len; + int rc = 0; + int bytes_returned = 0; + __u16 params, param_offset, byte_count, offset, count; + int remap = cifs_remap(cifs_sb); + + cifs_dbg(FYI, "In SetEA\n"); +SetEARetry: + rc = smb_init(SMB_COM_TRANSACTION2, 15, tcon, (void **) &pSMB, + (void **) &pSMBr); + if (rc) + return rc; + + if (pSMB->hdr.Flags2 & SMBFLG2_UNICODE) { + name_len = + cifsConvertToUTF16((__le16 *) pSMB->FileName, fileName, + PATH_MAX, nls_codepage, remap); + name_len++; /* trailing null */ + name_len *= 2; + } else { + name_len = copy_path_name(pSMB->FileName, fileName); + } + + params = 6 + name_len; + + /* done calculating parms using name_len of file name, + now use name_len to calculate length of ea name + we are going to create in the inode xattrs */ + if (ea_name == NULL) + name_len = 0; + else + name_len = strnlen(ea_name, 255); + + count = sizeof(*parm_data) + ea_value_len + name_len; + pSMB->MaxParameterCount = cpu_to_le16(2); + /* BB find max SMB PDU from sess */ + pSMB->MaxDataCount = cpu_to_le16(1000); + pSMB->MaxSetupCount = 0; + pSMB->Reserved = 0; + pSMB->Flags = 0; + pSMB->Timeout = 0; + pSMB->Reserved2 = 0; + param_offset = offsetof(struct smb_com_transaction2_spi_req, + InformationLevel) - 4; + offset = param_offset + params; + pSMB->InformationLevel = + cpu_to_le16(SMB_SET_FILE_EA); + + parm_data = (void *)pSMB + offsetof(struct smb_hdr, Protocol) + offset; + pSMB->ParameterOffset = cpu_to_le16(param_offset); + pSMB->DataOffset = cpu_to_le16(offset); + pSMB->SetupCount = 1; + pSMB->Reserved3 = 0; + pSMB->SubCommand = cpu_to_le16(TRANS2_SET_PATH_INFORMATION); + byte_count = 3 /* pad */ + params + count; + pSMB->DataCount = cpu_to_le16(count); + parm_data->list_len = cpu_to_le32(count); + parm_data->list[0].EA_flags = 0; + /* we checked above that name len is less than 255 */ + parm_data->list[0].name_len = (__u8)name_len; + /* EA names are always ASCII */ + if (ea_name) + strncpy(parm_data->list[0].name, ea_name, name_len); + parm_data->list[0].name[name_len] = 0; + parm_data->list[0].value_len = cpu_to_le16(ea_value_len); + /* caller ensures that ea_value_len is less than 64K but + we need to ensure that it fits within the smb */ + + /*BB add length check to see if it would fit in + negotiated SMB buffer size BB */ + /* if (ea_value_len > buffer_size - 512 (enough for header)) */ + if (ea_value_len) + memcpy(parm_data->list[0].name+name_len+1, + ea_value, ea_value_len); + + pSMB->TotalDataCount = pSMB->DataCount; + pSMB->ParameterCount = cpu_to_le16(params); + pSMB->TotalParameterCount = pSMB->ParameterCount; + pSMB->Reserved4 = 0; + inc_rfc1001_len(pSMB, 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) + cifs_dbg(FYI, "SetPathInfo (EA) returned %d\n", rc); + + cifs_buf_release(pSMB); + + if (rc == -EAGAIN) + goto SetEARetry; + + return rc; +} +#endif diff --git a/fs/smb/client/connect.c b/fs/smb/client/connect.c new file mode 100644 index 0000000000000..935fe198a4baf --- /dev/null +++ b/fs/smb/client/connect.c @@ -0,0 +1,4754 @@ +// SPDX-License-Identifier: LGPL-2.1 +/* + * + * Copyright (C) International Business Machines Corp., 2002,2011 + * Author(s): Steve French (sfrench@us.ibm.com) + * + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "cifspdu.h" +#include "cifsglob.h" +#include "cifsproto.h" +#include "cifs_unicode.h" +#include "cifs_debug.h" +#include "cifs_fs_sb.h" +#include "ntlmssp.h" +#include "nterr.h" +#include "rfc1002pdu.h" +#include "fscache.h" +#include "smb2proto.h" +#include "smbdirect.h" +#include "dns_resolve.h" +#ifdef CONFIG_CIFS_DFS_UPCALL +#include "dfs_cache.h" +#endif +#include "fs_context.h" +#include "cifs_swn.h" + +extern mempool_t *cifs_req_poolp; +extern bool disable_legacy_dialects; + +/* FIXME: should these be tunable? */ +#define TLINK_ERROR_EXPIRE (1 * HZ) +#define TLINK_IDLE_EXPIRE (600 * HZ) + +/* Drop the connection to not overload the server */ +#define NUM_STATUS_IO_TIMEOUT 5 + +struct mount_ctx { + struct cifs_sb_info *cifs_sb; + struct smb3_fs_context *fs_ctx; + unsigned int xid; + struct TCP_Server_Info *server; + struct cifs_ses *ses; + struct cifs_tcon *tcon; +#ifdef CONFIG_CIFS_DFS_UPCALL + struct cifs_ses *root_ses; + uuid_t mount_id; + char *origin_fullpath, *leaf_fullpath; +#endif +}; + +static int ip_connect(struct TCP_Server_Info *server); +static int generic_ip_connect(struct TCP_Server_Info *server); +static void tlink_rb_insert(struct rb_root *root, struct tcon_link *new_tlink); +static void cifs_prune_tlinks(struct work_struct *work); + +/* + * Resolve hostname and set ip addr in tcp ses. Useful for hostnames that may + * get their ip addresses changed at some point. + * + * This should be called with server->srv_mutex held. + */ +static int reconn_set_ipaddr_from_hostname(struct TCP_Server_Info *server) +{ + int rc; + int len; + char *unc, *ipaddr = NULL; + time64_t expiry, now; + unsigned long ttl = SMB_DNS_RESOLVE_INTERVAL_DEFAULT; + + if (!server->hostname) + return -EINVAL; + + /* if server hostname isn't populated, there's nothing to do here */ + if (server->hostname[0] == '\0') + return 0; + + len = strlen(server->hostname) + 3; + + unc = kmalloc(len, GFP_KERNEL); + if (!unc) { + cifs_dbg(FYI, "%s: failed to create UNC path\n", __func__); + return -ENOMEM; + } + scnprintf(unc, len, "\\\\%s", server->hostname); + + rc = dns_resolve_server_name_to_ip(unc, &ipaddr, &expiry); + kfree(unc); + + if (rc < 0) { + cifs_dbg(FYI, "%s: failed to resolve server part of %s to IP: %d\n", + __func__, server->hostname, rc); + goto requeue_resolve; + } + + spin_lock(&server->srv_lock); + rc = cifs_convert_address((struct sockaddr *)&server->dstaddr, ipaddr, + strlen(ipaddr)); + spin_unlock(&server->srv_lock); + kfree(ipaddr); + + /* rc == 1 means success here */ + if (rc) { + now = ktime_get_real_seconds(); + if (expiry && expiry > now) + /* + * To make sure we don't use the cached entry, retry 1s + * after expiry. + */ + ttl = max_t(unsigned long, expiry - now, SMB_DNS_RESOLVE_INTERVAL_MIN) + 1; + } + rc = !rc ? -1 : 0; + +requeue_resolve: + cifs_dbg(FYI, "%s: next dns resolution scheduled for %lu seconds in the future\n", + __func__, ttl); + mod_delayed_work(cifsiod_wq, &server->resolve, (ttl * HZ)); + + return rc; +} + +static void smb2_query_server_interfaces(struct work_struct *work) +{ + int rc; + struct cifs_tcon *tcon = container_of(work, + struct cifs_tcon, + query_interfaces.work); + + /* + * query server network interfaces, in case they change + */ + rc = SMB3_request_interfaces(0, tcon, false); + if (rc) { + cifs_dbg(FYI, "%s: failed to query server interfaces: %d\n", + __func__, rc); + } + + queue_delayed_work(cifsiod_wq, &tcon->query_interfaces, + (SMB_INTERFACE_POLL_INTERVAL * HZ)); +} + +static void cifs_resolve_server(struct work_struct *work) +{ + int rc; + struct TCP_Server_Info *server = container_of(work, + struct TCP_Server_Info, resolve.work); + + cifs_server_lock(server); + + /* + * Resolve the hostname again to make sure that IP address is up-to-date. + */ + rc = reconn_set_ipaddr_from_hostname(server); + if (rc) { + cifs_dbg(FYI, "%s: failed to resolve hostname: %d\n", + __func__, rc); + } + + cifs_server_unlock(server); +} + +/* + * Update the tcpStatus for the server. + * This is used to signal the cifsd thread to call cifs_reconnect + * ONLY cifsd thread should call cifs_reconnect. For any other + * thread, use this function + * + * @server: the tcp ses for which reconnect is needed + * @all_channels: if this needs to be done for all channels + */ +void +cifs_signal_cifsd_for_reconnect(struct TCP_Server_Info *server, + bool all_channels) +{ + struct TCP_Server_Info *pserver; + struct cifs_ses *ses; + int i; + + /* If server is a channel, select the primary channel */ + pserver = CIFS_SERVER_IS_CHAN(server) ? server->primary_server : server; + + spin_lock(&pserver->srv_lock); + if (!all_channels) { + pserver->tcpStatus = CifsNeedReconnect; + spin_unlock(&pserver->srv_lock); + return; + } + spin_unlock(&pserver->srv_lock); + + spin_lock(&cifs_tcp_ses_lock); + list_for_each_entry(ses, &pserver->smb_ses_list, smb_ses_list) { + spin_lock(&ses->chan_lock); + for (i = 0; i < ses->chan_count; i++) { + spin_lock(&ses->chans[i].server->srv_lock); + ses->chans[i].server->tcpStatus = CifsNeedReconnect; + spin_unlock(&ses->chans[i].server->srv_lock); + } + spin_unlock(&ses->chan_lock); + } + spin_unlock(&cifs_tcp_ses_lock); +} + +/* + * Mark all sessions and tcons for reconnect. + * IMPORTANT: make sure that this gets called only from + * cifsd thread. For any other thread, use + * cifs_signal_cifsd_for_reconnect + * + * @server: the tcp ses for which reconnect is needed + * @server needs to be previously set to CifsNeedReconnect. + * @mark_smb_session: whether even sessions need to be marked + */ +void +cifs_mark_tcp_ses_conns_for_reconnect(struct TCP_Server_Info *server, + bool mark_smb_session) +{ + struct TCP_Server_Info *pserver; + struct cifs_ses *ses, *nses; + struct cifs_tcon *tcon; + + /* + * before reconnecting the tcp session, mark the smb session (uid) and the tid bad so they + * are not used until reconnected. + */ + cifs_dbg(FYI, "%s: marking necessary sessions and tcons for reconnect\n", __func__); + + /* If server is a channel, select the primary channel */ + pserver = CIFS_SERVER_IS_CHAN(server) ? server->primary_server : server; + + + spin_lock(&cifs_tcp_ses_lock); + list_for_each_entry_safe(ses, nses, &pserver->smb_ses_list, smb_ses_list) { + /* check if iface is still active */ + if (!cifs_chan_is_iface_active(ses, server)) + cifs_chan_update_iface(ses, server); + + spin_lock(&ses->chan_lock); + if (!mark_smb_session && cifs_chan_needs_reconnect(ses, server)) { + spin_unlock(&ses->chan_lock); + continue; + } + + if (mark_smb_session) + CIFS_SET_ALL_CHANS_NEED_RECONNECT(ses); + else + cifs_chan_set_need_reconnect(ses, server); + + cifs_dbg(FYI, "%s: channel connect bitmap: 0x%lx\n", + __func__, ses->chans_need_reconnect); + + /* If all channels need reconnect, then tcon needs reconnect */ + if (!mark_smb_session && !CIFS_ALL_CHANS_NEED_RECONNECT(ses)) { + spin_unlock(&ses->chan_lock); + continue; + } + spin_unlock(&ses->chan_lock); + + spin_lock(&ses->ses_lock); + ses->ses_status = SES_NEED_RECON; + spin_unlock(&ses->ses_lock); + + list_for_each_entry(tcon, &ses->tcon_list, tcon_list) { + tcon->need_reconnect = true; + spin_lock(&tcon->tc_lock); + tcon->status = TID_NEED_RECON; + spin_unlock(&tcon->tc_lock); + } + if (ses->tcon_ipc) { + ses->tcon_ipc->need_reconnect = true; + spin_lock(&ses->tcon_ipc->tc_lock); + ses->tcon_ipc->status = TID_NEED_RECON; + spin_unlock(&ses->tcon_ipc->tc_lock); + } + } + spin_unlock(&cifs_tcp_ses_lock); +} + +static void +cifs_abort_connection(struct TCP_Server_Info *server) +{ + struct mid_q_entry *mid, *nmid; + struct list_head retry_list; + + server->maxBuf = 0; + server->max_read = 0; + + /* do not want to be sending data on a socket we are freeing */ + cifs_dbg(FYI, "%s: tearing down socket\n", __func__); + cifs_server_lock(server); + if (server->ssocket) { + cifs_dbg(FYI, "State: 0x%x Flags: 0x%lx\n", server->ssocket->state, + server->ssocket->flags); + kernel_sock_shutdown(server->ssocket, SHUT_WR); + cifs_dbg(FYI, "Post shutdown state: 0x%x Flags: 0x%lx\n", server->ssocket->state, + server->ssocket->flags); + sock_release(server->ssocket); + server->ssocket = NULL; + } + server->sequence_number = 0; + server->session_estab = false; + kfree_sensitive(server->session_key.response); + server->session_key.response = NULL; + server->session_key.len = 0; + server->lstrp = jiffies; + + /* mark submitted MIDs for retry and issue callback */ + INIT_LIST_HEAD(&retry_list); + cifs_dbg(FYI, "%s: moving mids to private list\n", __func__); + spin_lock(&server->mid_lock); + list_for_each_entry_safe(mid, nmid, &server->pending_mid_q, qhead) { + kref_get(&mid->refcount); + if (mid->mid_state == MID_REQUEST_SUBMITTED) + mid->mid_state = MID_RETRY_NEEDED; + list_move(&mid->qhead, &retry_list); + mid->mid_flags |= MID_DELETED; + } + spin_unlock(&server->mid_lock); + cifs_server_unlock(server); + + cifs_dbg(FYI, "%s: issuing mid callbacks\n", __func__); + list_for_each_entry_safe(mid, nmid, &retry_list, qhead) { + list_del_init(&mid->qhead); + mid->callback(mid); + release_mid(mid); + } + + if (cifs_rdma_enabled(server)) { + cifs_server_lock(server); + smbd_destroy(server); + cifs_server_unlock(server); + } +} + +static bool cifs_tcp_ses_needs_reconnect(struct TCP_Server_Info *server, int num_targets) +{ + spin_lock(&server->srv_lock); + server->nr_targets = num_targets; + if (server->tcpStatus == CifsExiting) { + /* the demux thread will exit normally next time through the loop */ + spin_unlock(&server->srv_lock); + wake_up(&server->response_q); + return false; + } + + cifs_dbg(FYI, "Mark tcp session as need reconnect\n"); + trace_smb3_reconnect(server->CurrentMid, server->conn_id, + server->hostname); + server->tcpStatus = CifsNeedReconnect; + + spin_unlock(&server->srv_lock); + return true; +} + +/* + * cifs tcp session reconnection + * + * mark tcp session as reconnecting so temporarily locked + * mark all smb sessions as reconnecting for tcp session + * reconnect tcp session + * wake up waiters on reconnection? - (not needed currently) + * + * if mark_smb_session is passed as true, unconditionally mark + * the smb session (and tcon) for reconnect as well. This value + * doesn't really matter for non-multichannel scenario. + * + */ +static int __cifs_reconnect(struct TCP_Server_Info *server, + bool mark_smb_session) +{ + int rc = 0; + + if (!cifs_tcp_ses_needs_reconnect(server, 1)) + return 0; + + cifs_mark_tcp_ses_conns_for_reconnect(server, mark_smb_session); + + cifs_abort_connection(server); + + do { + try_to_freeze(); + cifs_server_lock(server); + + if (!cifs_swn_set_server_dstaddr(server)) { + /* resolve the hostname again to make sure that IP address is up-to-date */ + rc = reconn_set_ipaddr_from_hostname(server); + cifs_dbg(FYI, "%s: reconn_set_ipaddr_from_hostname: rc=%d\n", __func__, rc); + } + + if (cifs_rdma_enabled(server)) + rc = smbd_reconnect(server); + else + rc = generic_ip_connect(server); + if (rc) { + cifs_server_unlock(server); + cifs_dbg(FYI, "%s: reconnect error %d\n", __func__, rc); + msleep(3000); + } else { + atomic_inc(&tcpSesReconnectCount); + set_credits(server, 1); + spin_lock(&server->srv_lock); + if (server->tcpStatus != CifsExiting) + server->tcpStatus = CifsNeedNegotiate; + spin_unlock(&server->srv_lock); + cifs_swn_reset_server_dstaddr(server); + cifs_server_unlock(server); + mod_delayed_work(cifsiod_wq, &server->reconnect, 0); + } + } while (server->tcpStatus == CifsNeedReconnect); + + spin_lock(&server->srv_lock); + if (server->tcpStatus == CifsNeedNegotiate) + mod_delayed_work(cifsiod_wq, &server->echo, 0); + spin_unlock(&server->srv_lock); + + wake_up(&server->response_q); + return rc; +} + +#ifdef CONFIG_CIFS_DFS_UPCALL +static int __reconnect_target_unlocked(struct TCP_Server_Info *server, const char *target) +{ + int rc; + char *hostname; + + if (!cifs_swn_set_server_dstaddr(server)) { + if (server->hostname != target) { + hostname = extract_hostname(target); + if (!IS_ERR(hostname)) { + spin_lock(&server->srv_lock); + kfree(server->hostname); + server->hostname = hostname; + spin_unlock(&server->srv_lock); + } else { + cifs_dbg(FYI, "%s: couldn't extract hostname or address from dfs target: %ld\n", + __func__, PTR_ERR(hostname)); + cifs_dbg(FYI, "%s: default to last target server: %s\n", __func__, + server->hostname); + } + } + /* resolve the hostname again to make sure that IP address is up-to-date. */ + rc = reconn_set_ipaddr_from_hostname(server); + cifs_dbg(FYI, "%s: reconn_set_ipaddr_from_hostname: rc=%d\n", __func__, rc); + } + /* Reconnect the socket */ + if (cifs_rdma_enabled(server)) + rc = smbd_reconnect(server); + else + rc = generic_ip_connect(server); + + return rc; +} + +static int reconnect_target_unlocked(struct TCP_Server_Info *server, struct dfs_cache_tgt_list *tl, + struct dfs_cache_tgt_iterator **target_hint) +{ + int rc; + struct dfs_cache_tgt_iterator *tit; + + *target_hint = NULL; + + /* If dfs target list is empty, then reconnect to last server */ + tit = dfs_cache_get_tgt_iterator(tl); + if (!tit) + return __reconnect_target_unlocked(server, server->hostname); + + /* Otherwise, try every dfs target in @tl */ + for (; tit; tit = dfs_cache_get_next_tgt(tl, tit)) { + rc = __reconnect_target_unlocked(server, dfs_cache_get_tgt_name(tit)); + if (!rc) { + *target_hint = tit; + break; + } + } + return rc; +} + +static int reconnect_dfs_server(struct TCP_Server_Info *server) +{ + int rc = 0; + const char *refpath = server->current_fullpath + 1; + struct dfs_cache_tgt_list tl = DFS_CACHE_TGT_LIST_INIT(tl); + struct dfs_cache_tgt_iterator *target_hint = NULL; + int num_targets = 0; + + /* + * Determine the number of dfs targets the referral path in @cifs_sb resolves to. + * + * smb2_reconnect() needs to know how long it should wait based upon the number of dfs + * targets (server->nr_targets). It's also possible that the cached referral was cleared + * through /proc/fs/cifs/dfscache or the target list is empty due to server settings after + * refreshing the referral, so, in this case, default it to 1. + */ + if (!dfs_cache_noreq_find(refpath, NULL, &tl)) + num_targets = dfs_cache_get_nr_tgts(&tl); + if (!num_targets) + num_targets = 1; + + if (!cifs_tcp_ses_needs_reconnect(server, num_targets)) + return 0; + + /* + * Unconditionally mark all sessions & tcons for reconnect as we might be connecting to a + * different server or share during failover. It could be improved by adding some logic to + * only do that in case it connects to a different server or share, though. + */ + cifs_mark_tcp_ses_conns_for_reconnect(server, true); + + cifs_abort_connection(server); + + do { + try_to_freeze(); + cifs_server_lock(server); + + rc = reconnect_target_unlocked(server, &tl, &target_hint); + if (rc) { + /* Failed to reconnect socket */ + cifs_server_unlock(server); + cifs_dbg(FYI, "%s: reconnect error %d\n", __func__, rc); + msleep(3000); + continue; + } + /* + * Socket was created. Update tcp session status to CifsNeedNegotiate so that a + * process waiting for reconnect will know it needs to re-establish session and tcon + * through the reconnected target server. + */ + atomic_inc(&tcpSesReconnectCount); + set_credits(server, 1); + spin_lock(&server->srv_lock); + if (server->tcpStatus != CifsExiting) + server->tcpStatus = CifsNeedNegotiate; + spin_unlock(&server->srv_lock); + cifs_swn_reset_server_dstaddr(server); + cifs_server_unlock(server); + mod_delayed_work(cifsiod_wq, &server->reconnect, 0); + } while (server->tcpStatus == CifsNeedReconnect); + + if (target_hint) + dfs_cache_noreq_update_tgthint(refpath, target_hint); + + dfs_cache_free_tgts(&tl); + + /* Need to set up echo worker again once connection has been established */ + spin_lock(&server->srv_lock); + if (server->tcpStatus == CifsNeedNegotiate) + mod_delayed_work(cifsiod_wq, &server->echo, 0); + spin_unlock(&server->srv_lock); + + wake_up(&server->response_q); + return rc; +} + +int cifs_reconnect(struct TCP_Server_Info *server, bool mark_smb_session) +{ + /* If tcp session is not an dfs connection, then reconnect to last target server */ + spin_lock(&server->srv_lock); + if (!server->is_dfs_conn) { + spin_unlock(&server->srv_lock); + return __cifs_reconnect(server, mark_smb_session); + } + spin_unlock(&server->srv_lock); + + mutex_lock(&server->refpath_lock); + if (!server->origin_fullpath || !server->leaf_fullpath) { + mutex_unlock(&server->refpath_lock); + return __cifs_reconnect(server, mark_smb_session); + } + mutex_unlock(&server->refpath_lock); + + return reconnect_dfs_server(server); +} +#else +int cifs_reconnect(struct TCP_Server_Info *server, bool mark_smb_session) +{ + return __cifs_reconnect(server, mark_smb_session); +} +#endif + +static void +cifs_echo_request(struct work_struct *work) +{ + int rc; + struct TCP_Server_Info *server = container_of(work, + struct TCP_Server_Info, echo.work); + + /* + * We cannot send an echo if it is disabled. + * Also, no need to ping if we got a response recently. + */ + + if (server->tcpStatus == CifsNeedReconnect || + server->tcpStatus == CifsExiting || + server->tcpStatus == CifsNew || + (server->ops->can_echo && !server->ops->can_echo(server)) || + time_before(jiffies, server->lstrp + server->echo_interval - HZ)) + goto requeue_echo; + + rc = server->ops->echo ? server->ops->echo(server) : -ENOSYS; + cifs_server_dbg(FYI, "send echo request: rc = %d\n", rc); + + /* Check witness registrations */ + cifs_swn_check(); + +requeue_echo: + queue_delayed_work(cifsiod_wq, &server->echo, server->echo_interval); +} + +static bool +allocate_buffers(struct TCP_Server_Info *server) +{ + if (!server->bigbuf) { + server->bigbuf = (char *)cifs_buf_get(); + if (!server->bigbuf) { + cifs_server_dbg(VFS, "No memory for large SMB response\n"); + msleep(3000); + /* retry will check if exiting */ + return false; + } + } else if (server->large_buf) { + /* we are reusing a dirty large buf, clear its start */ + memset(server->bigbuf, 0, HEADER_SIZE(server)); + } + + if (!server->smallbuf) { + server->smallbuf = (char *)cifs_small_buf_get(); + if (!server->smallbuf) { + cifs_server_dbg(VFS, "No memory for SMB response\n"); + msleep(1000); + /* retry will check if exiting */ + return false; + } + /* beginning of smb buffer is cleared in our buf_get */ + } else { + /* if existing small buf clear beginning */ + memset(server->smallbuf, 0, HEADER_SIZE(server)); + } + + return true; +} + +static bool +server_unresponsive(struct TCP_Server_Info *server) +{ + /* + * We need to wait 3 echo intervals to make sure we handle such + * situations right: + * 1s client sends a normal SMB request + * 2s client gets a response + * 30s echo workqueue job pops, and decides we got a response recently + * and don't need to send another + * ... + * 65s kernel_recvmsg times out, and we see that we haven't gotten + * a response in >60s. + */ + spin_lock(&server->srv_lock); + if ((server->tcpStatus == CifsGood || + server->tcpStatus == CifsNeedNegotiate) && + (!server->ops->can_echo || server->ops->can_echo(server)) && + time_after(jiffies, server->lstrp + 3 * server->echo_interval)) { + spin_unlock(&server->srv_lock); + cifs_server_dbg(VFS, "has not responded in %lu seconds. Reconnecting...\n", + (3 * server->echo_interval) / HZ); + cifs_reconnect(server, false); + return true; + } + spin_unlock(&server->srv_lock); + + return false; +} + +static inline bool +zero_credits(struct TCP_Server_Info *server) +{ + int val; + + spin_lock(&server->req_lock); + val = server->credits + server->echo_credits + server->oplock_credits; + if (server->in_flight == 0 && val == 0) { + spin_unlock(&server->req_lock); + return true; + } + spin_unlock(&server->req_lock); + return false; +} + +static int +cifs_readv_from_socket(struct TCP_Server_Info *server, struct msghdr *smb_msg) +{ + int length = 0; + int total_read; + + for (total_read = 0; msg_data_left(smb_msg); total_read += length) { + try_to_freeze(); + + /* reconnect if no credits and no requests in flight */ + if (zero_credits(server)) { + cifs_reconnect(server, false); + return -ECONNABORTED; + } + + if (server_unresponsive(server)) + return -ECONNABORTED; + if (cifs_rdma_enabled(server) && server->smbd_conn) + length = smbd_recv(server->smbd_conn, smb_msg); + else + length = sock_recvmsg(server->ssocket, smb_msg, 0); + + spin_lock(&server->srv_lock); + if (server->tcpStatus == CifsExiting) { + spin_unlock(&server->srv_lock); + return -ESHUTDOWN; + } + + if (server->tcpStatus == CifsNeedReconnect) { + spin_unlock(&server->srv_lock); + cifs_reconnect(server, false); + return -ECONNABORTED; + } + spin_unlock(&server->srv_lock); + + if (length == -ERESTARTSYS || + length == -EAGAIN || + length == -EINTR) { + /* + * Minimum sleep to prevent looping, allowing socket + * to clear and app threads to set tcpStatus + * CifsNeedReconnect if server hung. + */ + usleep_range(1000, 2000); + length = 0; + continue; + } + + if (length <= 0) { + cifs_dbg(FYI, "Received no data or error: %d\n", length); + cifs_reconnect(server, false); + return -ECONNABORTED; + } + } + return total_read; +} + +int +cifs_read_from_socket(struct TCP_Server_Info *server, char *buf, + unsigned int to_read) +{ + struct msghdr smb_msg = {}; + struct kvec iov = {.iov_base = buf, .iov_len = to_read}; + iov_iter_kvec(&smb_msg.msg_iter, ITER_DEST, &iov, 1, to_read); + + return cifs_readv_from_socket(server, &smb_msg); +} + +ssize_t +cifs_discard_from_socket(struct TCP_Server_Info *server, size_t to_read) +{ + struct msghdr smb_msg = {}; + + /* + * iov_iter_discard already sets smb_msg.type and count and iov_offset + * and cifs_readv_from_socket sets msg_control and msg_controllen + * so little to initialize in struct msghdr + */ + iov_iter_discard(&smb_msg.msg_iter, ITER_DEST, to_read); + + return cifs_readv_from_socket(server, &smb_msg); +} + +int +cifs_read_page_from_socket(struct TCP_Server_Info *server, struct page *page, + unsigned int page_offset, unsigned int to_read) +{ + struct msghdr smb_msg = {}; + struct bio_vec bv = { + .bv_page = page, .bv_len = to_read, .bv_offset = page_offset}; + iov_iter_bvec(&smb_msg.msg_iter, ITER_DEST, &bv, 1, to_read); + return cifs_readv_from_socket(server, &smb_msg); +} + +static bool +is_smb_response(struct TCP_Server_Info *server, unsigned char type) +{ + /* + * 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. + */ + switch (type) { + case RFC1002_SESSION_MESSAGE: + /* Regular SMB response */ + return true; + case RFC1002_SESSION_KEEP_ALIVE: + cifs_dbg(FYI, "RFC 1002 session keep alive\n"); + break; + case RFC1002_POSITIVE_SESSION_RESPONSE: + cifs_dbg(FYI, "RFC 1002 positive session response\n"); + break; + case RFC1002_NEGATIVE_SESSION_RESPONSE: + /* + * We get this from Windows 98 instead of an error on + * SMB negprot response. + */ + cifs_dbg(FYI, "RFC 1002 negative session response\n"); + /* give server a second to clean up */ + msleep(1000); + /* + * Always try 445 first on reconnect since we get NACK + * on some if we ever connected to port 139 (the NACK + * is since we do not begin with RFC1001 session + * initialize frame). + */ + cifs_set_port((struct sockaddr *)&server->dstaddr, CIFS_PORT); + cifs_reconnect(server, true); + break; + default: + cifs_server_dbg(VFS, "RFC 1002 unknown response type 0x%x\n", type); + cifs_reconnect(server, true); + } + + return false; +} + +void +dequeue_mid(struct mid_q_entry *mid, bool malformed) +{ +#ifdef CONFIG_CIFS_STATS2 + mid->when_received = jiffies; +#endif + spin_lock(&mid->server->mid_lock); + if (!malformed) + mid->mid_state = MID_RESPONSE_RECEIVED; + else + mid->mid_state = MID_RESPONSE_MALFORMED; + /* + * Trying to handle/dequeue a mid after the send_recv() + * function has finished processing it is a bug. + */ + if (mid->mid_flags & MID_DELETED) { + spin_unlock(&mid->server->mid_lock); + pr_warn_once("trying to dequeue a deleted mid\n"); + } else { + list_del_init(&mid->qhead); + mid->mid_flags |= MID_DELETED; + spin_unlock(&mid->server->mid_lock); + } +} + +static unsigned int +smb2_get_credits_from_hdr(char *buffer, struct TCP_Server_Info *server) +{ + struct smb2_hdr *shdr = (struct smb2_hdr *)buffer; + + /* + * SMB1 does not use credits. + */ + if (is_smb1(server)) + return 0; + + return le16_to_cpu(shdr->CreditRequest); +} + +static void +handle_mid(struct mid_q_entry *mid, struct TCP_Server_Info *server, + char *buf, int malformed) +{ + if (server->ops->check_trans2 && + server->ops->check_trans2(mid, server, buf, malformed)) + return; + mid->credits_received = smb2_get_credits_from_hdr(buf, server); + mid->resp_buf = buf; + mid->large_buf = server->large_buf; + /* Was previous buf put in mpx struct for multi-rsp? */ + if (!mid->multiRsp) { + /* smb buffer will be freed by user thread */ + if (server->large_buf) + server->bigbuf = NULL; + else + server->smallbuf = NULL; + } + dequeue_mid(mid, malformed); +} + +int +cifs_enable_signing(struct TCP_Server_Info *server, bool mnt_sign_required) +{ + bool srv_sign_required = server->sec_mode & server->vals->signing_required; + bool srv_sign_enabled = server->sec_mode & server->vals->signing_enabled; + bool mnt_sign_enabled; + + /* + * Is signing required by mnt options? If not then check + * global_secflags to see if it is there. + */ + if (!mnt_sign_required) + mnt_sign_required = ((global_secflags & CIFSSEC_MUST_SIGN) == + CIFSSEC_MUST_SIGN); + + /* + * If signing is required then it's automatically enabled too, + * otherwise, check to see if the secflags allow it. + */ + mnt_sign_enabled = mnt_sign_required ? mnt_sign_required : + (global_secflags & CIFSSEC_MAY_SIGN); + + /* If server requires signing, does client allow it? */ + if (srv_sign_required) { + if (!mnt_sign_enabled) { + cifs_dbg(VFS, "Server requires signing, but it's disabled in SecurityFlags!\n"); + return -EOPNOTSUPP; + } + server->sign = true; + } + + /* If client requires signing, does server allow it? */ + if (mnt_sign_required) { + if (!srv_sign_enabled) { + cifs_dbg(VFS, "Server does not support signing!\n"); + return -EOPNOTSUPP; + } + server->sign = true; + } + + if (cifs_rdma_enabled(server) && server->sign) + cifs_dbg(VFS, "Signing is enabled, and RDMA read/write will be disabled\n"); + + return 0; +} + + +static void clean_demultiplex_info(struct TCP_Server_Info *server) +{ + int length; + + /* take it off the list, if it's not already */ + spin_lock(&server->srv_lock); + list_del_init(&server->tcp_ses_list); + spin_unlock(&server->srv_lock); + + cancel_delayed_work_sync(&server->echo); + cancel_delayed_work_sync(&server->resolve); + + spin_lock(&server->srv_lock); + server->tcpStatus = CifsExiting; + spin_unlock(&server->srv_lock); + wake_up_all(&server->response_q); + + /* check if we have blocked requests that need to free */ + spin_lock(&server->req_lock); + if (server->credits <= 0) + server->credits = 1; + spin_unlock(&server->req_lock); + /* + * Although there should not be any requests blocked on this queue it + * can not hurt to be paranoid and try to wake up requests that may + * haven been blocked when more than 50 at time were on the wire to the + * same server - they now will see the session is in exit state and get + * out of SendReceive. + */ + wake_up_all(&server->request_q); + /* give those requests time to exit */ + msleep(125); + if (cifs_rdma_enabled(server)) + smbd_destroy(server); + if (server->ssocket) { + sock_release(server->ssocket); + server->ssocket = NULL; + } + + if (!list_empty(&server->pending_mid_q)) { + struct list_head dispose_list; + struct mid_q_entry *mid_entry; + struct list_head *tmp, *tmp2; + + INIT_LIST_HEAD(&dispose_list); + spin_lock(&server->mid_lock); + list_for_each_safe(tmp, tmp2, &server->pending_mid_q) { + mid_entry = list_entry(tmp, struct mid_q_entry, qhead); + cifs_dbg(FYI, "Clearing mid %llu\n", mid_entry->mid); + kref_get(&mid_entry->refcount); + mid_entry->mid_state = MID_SHUTDOWN; + list_move(&mid_entry->qhead, &dispose_list); + mid_entry->mid_flags |= MID_DELETED; + } + spin_unlock(&server->mid_lock); + + /* now walk dispose list and issue callbacks */ + list_for_each_safe(tmp, tmp2, &dispose_list) { + mid_entry = list_entry(tmp, struct mid_q_entry, qhead); + cifs_dbg(FYI, "Callback mid %llu\n", mid_entry->mid); + list_del_init(&mid_entry->qhead); + mid_entry->callback(mid_entry); + release_mid(mid_entry); + } + /* 1/8th of sec is more than enough time for them to exit */ + msleep(125); + } + + 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 to wait at + * least 45 seconds before giving up on a request getting a + * response and going ahead and killing cifsd. + */ + cifs_dbg(FYI, "Wait for exit from demultiplex thread\n"); + msleep(46000); + /* + * If threads still have not exited they are probably never + * coming home not much else we can do but free the memory. + */ + } + +#ifdef CONFIG_CIFS_DFS_UPCALL + kfree(server->origin_fullpath); + kfree(server->leaf_fullpath); +#endif + kfree(server); + + length = atomic_dec_return(&tcpSesAllocCount); + if (length > 0) + mempool_resize(cifs_req_poolp, length + cifs_min_rcv); +} + +static int +standard_receive3(struct TCP_Server_Info *server, struct mid_q_entry *mid) +{ + int length; + char *buf = server->smallbuf; + unsigned int pdu_length = server->pdu_size; + + /* make sure this will fit in a large buffer */ + if (pdu_length > CIFSMaxBufSize + MAX_HEADER_SIZE(server) - + HEADER_PREAMBLE_SIZE(server)) { + cifs_server_dbg(VFS, "SMB response too long (%u bytes)\n", pdu_length); + cifs_reconnect(server, true); + return -ECONNABORTED; + } + + /* switch to large buffer if too big for a small one */ + if (pdu_length > MAX_CIFS_SMALL_BUFFER_SIZE - 4) { + server->large_buf = true; + memcpy(server->bigbuf, buf, server->total_read); + buf = server->bigbuf; + } + + /* now read the rest */ + length = cifs_read_from_socket(server, buf + HEADER_SIZE(server) - 1, + pdu_length - MID_HEADER_SIZE(server)); + + if (length < 0) + return length; + server->total_read += length; + + dump_smb(buf, server->total_read); + + return cifs_handle_standard(server, mid); +} + +int +cifs_handle_standard(struct TCP_Server_Info *server, struct mid_q_entry *mid) +{ + char *buf = server->large_buf ? server->bigbuf : server->smallbuf; + int rc; + + /* + * We know that we received enough to get to the MID as we + * checked the pdu_length earlier. Now check to see + * if the rest of the header is OK. + * + * 48 bytes is enough to display the header and a little bit + * into the payload for debugging purposes. + */ + rc = server->ops->check_message(buf, server->total_read, server); + if (rc) + cifs_dump_mem("Bad SMB: ", buf, + min_t(unsigned int, server->total_read, 48)); + + if (server->ops->is_session_expired && + server->ops->is_session_expired(buf)) { + cifs_reconnect(server, true); + return -1; + } + + if (server->ops->is_status_pending && + server->ops->is_status_pending(buf, server)) + return -1; + + if (!mid) + return rc; + + handle_mid(mid, server, buf, rc); + return 0; +} + +static void +smb2_add_credits_from_hdr(char *buffer, struct TCP_Server_Info *server) +{ + struct smb2_hdr *shdr = (struct smb2_hdr *)buffer; + int scredits, in_flight; + + /* + * SMB1 does not use credits. + */ + if (is_smb1(server)) + return; + + if (shdr->CreditRequest) { + spin_lock(&server->req_lock); + server->credits += le16_to_cpu(shdr->CreditRequest); + scredits = server->credits; + in_flight = server->in_flight; + spin_unlock(&server->req_lock); + wake_up(&server->request_q); + + trace_smb3_hdr_credits(server->CurrentMid, + server->conn_id, server->hostname, scredits, + le16_to_cpu(shdr->CreditRequest), in_flight); + cifs_server_dbg(FYI, "%s: added %u credits total=%d\n", + __func__, le16_to_cpu(shdr->CreditRequest), + scredits); + } +} + + +static int +cifs_demultiplex_thread(void *p) +{ + int i, num_mids, length; + struct TCP_Server_Info *server = p; + unsigned int pdu_length; + unsigned int next_offset; + char *buf = NULL; + struct task_struct *task_to_wake = NULL; + struct mid_q_entry *mids[MAX_COMPOUND]; + char *bufs[MAX_COMPOUND]; + unsigned int noreclaim_flag, num_io_timeout = 0; + + noreclaim_flag = memalloc_noreclaim_save(); + cifs_dbg(FYI, "Demultiplex PID: %d\n", task_pid_nr(current)); + + length = atomic_inc_return(&tcpSesAllocCount); + if (length > 1) + mempool_resize(cifs_req_poolp, length + cifs_min_rcv); + + set_freezable(); + allow_kernel_signal(SIGKILL); + while (server->tcpStatus != CifsExiting) { + if (try_to_freeze()) + continue; + + if (!allocate_buffers(server)) + continue; + + server->large_buf = false; + buf = server->smallbuf; + pdu_length = 4; /* enough to get RFC1001 header */ + + length = cifs_read_from_socket(server, buf, pdu_length); + if (length < 0) + continue; + + if (is_smb1(server)) + server->total_read = length; + else + server->total_read = 0; + + /* + * The right amount was read from socket - 4 bytes, + * so we can now interpret the length field. + */ + pdu_length = get_rfc1002_length(buf); + + cifs_dbg(FYI, "RFC1002 header 0x%x\n", pdu_length); + if (!is_smb_response(server, buf[0])) + continue; +next_pdu: + server->pdu_size = pdu_length; + + /* make sure we have enough to get to the MID */ + if (server->pdu_size < MID_HEADER_SIZE(server)) { + cifs_server_dbg(VFS, "SMB response too short (%u bytes)\n", + server->pdu_size); + cifs_reconnect(server, true); + continue; + } + + /* read down to the MID */ + length = cifs_read_from_socket(server, + buf + HEADER_PREAMBLE_SIZE(server), + MID_HEADER_SIZE(server)); + if (length < 0) + continue; + server->total_read += length; + + if (server->ops->next_header) { + next_offset = server->ops->next_header(buf); + if (next_offset) + server->pdu_size = next_offset; + } + + memset(mids, 0, sizeof(mids)); + memset(bufs, 0, sizeof(bufs)); + num_mids = 0; + + if (server->ops->is_transform_hdr && + server->ops->receive_transform && + server->ops->is_transform_hdr(buf)) { + length = server->ops->receive_transform(server, + mids, + bufs, + &num_mids); + } else { + mids[0] = server->ops->find_mid(server, buf); + bufs[0] = buf; + num_mids = 1; + + if (!mids[0] || !mids[0]->receive) + length = standard_receive3(server, mids[0]); + else + length = mids[0]->receive(server, mids[0]); + } + + if (length < 0) { + for (i = 0; i < num_mids; i++) + if (mids[i]) + release_mid(mids[i]); + continue; + } + + if (server->ops->is_status_io_timeout && + server->ops->is_status_io_timeout(buf)) { + num_io_timeout++; + if (num_io_timeout > NUM_STATUS_IO_TIMEOUT) { + cifs_reconnect(server, false); + num_io_timeout = 0; + continue; + } + } + + server->lstrp = jiffies; + + for (i = 0; i < num_mids; i++) { + if (mids[i] != NULL) { + mids[i]->resp_buf_size = server->pdu_size; + + if (bufs[i] && server->ops->is_network_name_deleted) + server->ops->is_network_name_deleted(bufs[i], + server); + + if (!mids[i]->multiRsp || mids[i]->multiEnd) + mids[i]->callback(mids[i]); + + release_mid(mids[i]); + } else if (server->ops->is_oplock_break && + server->ops->is_oplock_break(bufs[i], + server)) { + smb2_add_credits_from_hdr(bufs[i], server); + cifs_dbg(FYI, "Received oplock break\n"); + } else { + cifs_server_dbg(VFS, "No task to wake, unknown frame received! NumMids %d\n", + atomic_read(&mid_count)); + cifs_dump_mem("Received Data is: ", bufs[i], + HEADER_SIZE(server)); + smb2_add_credits_from_hdr(bufs[i], server); +#ifdef CONFIG_CIFS_DEBUG2 + if (server->ops->dump_detail) + server->ops->dump_detail(bufs[i], + server); + cifs_dump_mids(server); +#endif /* CIFS_DEBUG2 */ + } + } + + if (pdu_length > server->pdu_size) { + if (!allocate_buffers(server)) + continue; + pdu_length -= server->pdu_size; + server->total_read = 0; + server->large_buf = false; + buf = server->smallbuf; + goto next_pdu; + } + } /* end while !EXITING */ + + /* buffer usually freed in free_mid - need to free it here on exit */ + cifs_buf_release(server->bigbuf); + if (server->smallbuf) /* no sense logging a debug message if NULL */ + cifs_small_buf_release(server->smallbuf); + + task_to_wake = xchg(&server->tsk, NULL); + clean_demultiplex_info(server); + + /* if server->tsk was NULL then wait for a signal before exiting */ + if (!task_to_wake) { + set_current_state(TASK_INTERRUPTIBLE); + while (!signal_pending(current)) { + schedule(); + set_current_state(TASK_INTERRUPTIBLE); + } + set_current_state(TASK_RUNNING); + } + + memalloc_noreclaim_restore(noreclaim_flag); + module_put_and_kthread_exit(0); +} + +/* + * Returns true if srcaddr isn't specified and rhs isn't specified, or + * if srcaddr is specified and matches the IP address of the rhs argument + */ +bool +cifs_match_ipaddr(struct sockaddr *srcaddr, struct sockaddr *rhs) +{ + switch (srcaddr->sa_family) { + case AF_UNSPEC: + return (rhs->sa_family == AF_UNSPEC); + case AF_INET: { + struct sockaddr_in *saddr4 = (struct sockaddr_in *)srcaddr; + struct sockaddr_in *vaddr4 = (struct sockaddr_in *)rhs; + return (saddr4->sin_addr.s_addr == vaddr4->sin_addr.s_addr); + } + case AF_INET6: { + struct sockaddr_in6 *saddr6 = (struct sockaddr_in6 *)srcaddr; + struct sockaddr_in6 *vaddr6 = (struct sockaddr_in6 *)rhs; + return ipv6_addr_equal(&saddr6->sin6_addr, &vaddr6->sin6_addr); + } + default: + WARN_ON(1); + return false; /* don't expect to be here */ + } +} + +/* + * If no port is specified in addr structure, we try to match with 445 port + * and if it fails - with 139 ports. It should be called only if address + * families of server and addr are equal. + */ +static bool +match_port(struct TCP_Server_Info *server, struct sockaddr *addr) +{ + __be16 port, *sport; + + /* SMBDirect manages its own ports, don't match it here */ + if (server->rdma) + return true; + + switch (addr->sa_family) { + case AF_INET: + sport = &((struct sockaddr_in *) &server->dstaddr)->sin_port; + port = ((struct sockaddr_in *) addr)->sin_port; + break; + case AF_INET6: + sport = &((struct sockaddr_in6 *) &server->dstaddr)->sin6_port; + port = ((struct sockaddr_in6 *) addr)->sin6_port; + break; + default: + WARN_ON(1); + return false; + } + + if (!port) { + port = htons(CIFS_PORT); + if (port == *sport) + return true; + + port = htons(RFC1001_PORT); + } + + return port == *sport; +} + +static bool +match_address(struct TCP_Server_Info *server, struct sockaddr *addr, + struct sockaddr *srcaddr) +{ + switch (addr->sa_family) { + case AF_INET: { + struct sockaddr_in *addr4 = (struct sockaddr_in *)addr; + struct sockaddr_in *srv_addr4 = + (struct sockaddr_in *)&server->dstaddr; + + if (addr4->sin_addr.s_addr != srv_addr4->sin_addr.s_addr) + return false; + break; + } + case AF_INET6: { + struct sockaddr_in6 *addr6 = (struct sockaddr_in6 *)addr; + struct sockaddr_in6 *srv_addr6 = + (struct sockaddr_in6 *)&server->dstaddr; + + if (!ipv6_addr_equal(&addr6->sin6_addr, + &srv_addr6->sin6_addr)) + return false; + if (addr6->sin6_scope_id != srv_addr6->sin6_scope_id) + return false; + break; + } + default: + WARN_ON(1); + return false; /* don't expect to be here */ + } + + if (!cifs_match_ipaddr(srcaddr, (struct sockaddr *)&server->srcaddr)) + return false; + + return true; +} + +static bool +match_security(struct TCP_Server_Info *server, struct smb3_fs_context *ctx) +{ + /* + * The select_sectype function should either return the ctx->sectype + * that was specified, or "Unspecified" if that sectype was not + * compatible with the given NEGOTIATE request. + */ + if (server->ops->select_sectype(server, ctx->sectype) + == Unspecified) + return false; + + /* + * Now check if signing mode is acceptable. No need to check + * global_secflags at this point since if MUST_SIGN is set then + * the server->sign had better be too. + */ + if (ctx->sign && !server->sign) + return false; + + return true; +} + +/* this function must be called with srv_lock held */ +static int match_server(struct TCP_Server_Info *server, struct smb3_fs_context *ctx) +{ + struct sockaddr *addr = (struct sockaddr *)&ctx->dstaddr; + + lockdep_assert_held(&server->srv_lock); + + if (ctx->nosharesock) + return 0; + + /* this server does not share socket */ + if (server->nosharesock) + return 0; + + /* If multidialect negotiation see if existing sessions match one */ + if (strcmp(ctx->vals->version_string, SMB3ANY_VERSION_STRING) == 0) { + if (server->vals->protocol_id < SMB30_PROT_ID) + return 0; + } else if (strcmp(ctx->vals->version_string, + SMBDEFAULT_VERSION_STRING) == 0) { + if (server->vals->protocol_id < SMB21_PROT_ID) + return 0; + } else if ((server->vals != ctx->vals) || (server->ops != ctx->ops)) + return 0; + + if (!net_eq(cifs_net_ns(server), current->nsproxy->net_ns)) + return 0; + + if (strcasecmp(server->hostname, ctx->server_hostname)) + return 0; + + if (!match_address(server, addr, + (struct sockaddr *)&ctx->srcaddr)) + return 0; + + if (!match_port(server, addr)) + return 0; + + if (!match_security(server, ctx)) + return 0; + + if (server->echo_interval != ctx->echo_interval * HZ) + return 0; + + if (server->rdma != ctx->rdma) + return 0; + + if (server->ignore_signature != ctx->ignore_signature) + return 0; + + if (server->min_offload != ctx->min_offload) + return 0; + + return 1; +} + +struct TCP_Server_Info * +cifs_find_tcp_session(struct smb3_fs_context *ctx) +{ + struct TCP_Server_Info *server; + + spin_lock(&cifs_tcp_ses_lock); + list_for_each_entry(server, &cifs_tcp_ses_list, tcp_ses_list) { + spin_lock(&server->srv_lock); +#ifdef CONFIG_CIFS_DFS_UPCALL + /* + * DFS failover implementation in cifs_reconnect() requires unique tcp sessions for + * DFS connections to do failover properly, so avoid sharing them with regular + * shares or even links that may connect to same server but having completely + * different failover targets. + */ + if (server->is_dfs_conn) { + spin_unlock(&server->srv_lock); + continue; + } +#endif + /* + * Skip ses channels since they're only handled in lower layers + * (e.g. cifs_send_recv). + */ + if (CIFS_SERVER_IS_CHAN(server) || !match_server(server, ctx)) { + spin_unlock(&server->srv_lock); + continue; + } + spin_unlock(&server->srv_lock); + + ++server->srv_count; + spin_unlock(&cifs_tcp_ses_lock); + cifs_dbg(FYI, "Existing tcp session with server found\n"); + return server; + } + spin_unlock(&cifs_tcp_ses_lock); + return NULL; +} + +void +cifs_put_tcp_session(struct TCP_Server_Info *server, int from_reconnect) +{ + struct task_struct *task; + + spin_lock(&cifs_tcp_ses_lock); + if (--server->srv_count > 0) { + spin_unlock(&cifs_tcp_ses_lock); + return; + } + + /* srv_count can never go negative */ + WARN_ON(server->srv_count < 0); + + put_net(cifs_net_ns(server)); + + list_del_init(&server->tcp_ses_list); + spin_unlock(&cifs_tcp_ses_lock); + + /* For secondary channels, we pick up ref-count on the primary server */ + if (CIFS_SERVER_IS_CHAN(server)) + cifs_put_tcp_session(server->primary_server, from_reconnect); + + cancel_delayed_work_sync(&server->echo); + cancel_delayed_work_sync(&server->resolve); + + if (from_reconnect) + /* + * Avoid deadlock here: reconnect work calls + * cifs_put_tcp_session() at its end. Need to be sure + * that reconnect work does nothing with server pointer after + * that step. + */ + cancel_delayed_work(&server->reconnect); + else + cancel_delayed_work_sync(&server->reconnect); + + spin_lock(&server->srv_lock); + server->tcpStatus = CifsExiting; + spin_unlock(&server->srv_lock); + + cifs_crypto_secmech_release(server); + + kfree_sensitive(server->session_key.response); + server->session_key.response = NULL; + server->session_key.len = 0; + kfree(server->hostname); + server->hostname = NULL; + + task = xchg(&server->tsk, NULL); + if (task) + send_sig(SIGKILL, task, 1); +} + +struct TCP_Server_Info * +cifs_get_tcp_session(struct smb3_fs_context *ctx, + struct TCP_Server_Info *primary_server) +{ + struct TCP_Server_Info *tcp_ses = NULL; + int rc; + + cifs_dbg(FYI, "UNC: %s\n", ctx->UNC); + + /* see if we already have a matching tcp_ses */ + tcp_ses = cifs_find_tcp_session(ctx); + if (tcp_ses) + return tcp_ses; + + tcp_ses = kzalloc(sizeof(struct TCP_Server_Info), GFP_KERNEL); + if (!tcp_ses) { + rc = -ENOMEM; + goto out_err; + } + + tcp_ses->hostname = kstrdup(ctx->server_hostname, GFP_KERNEL); + if (!tcp_ses->hostname) { + rc = -ENOMEM; + goto out_err; + } + + if (ctx->nosharesock) + tcp_ses->nosharesock = true; + + tcp_ses->ops = ctx->ops; + tcp_ses->vals = ctx->vals; + cifs_set_net_ns(tcp_ses, get_net(current->nsproxy->net_ns)); + + tcp_ses->conn_id = atomic_inc_return(&tcpSesNextId); + tcp_ses->noblockcnt = ctx->rootfs; + tcp_ses->noblocksnd = ctx->noblocksnd || ctx->rootfs; + tcp_ses->noautotune = ctx->noautotune; + tcp_ses->tcp_nodelay = ctx->sockopt_tcp_nodelay; + tcp_ses->rdma = ctx->rdma; + tcp_ses->in_flight = 0; + tcp_ses->max_in_flight = 0; + tcp_ses->credits = 1; + if (primary_server) { + spin_lock(&cifs_tcp_ses_lock); + ++primary_server->srv_count; + spin_unlock(&cifs_tcp_ses_lock); + tcp_ses->primary_server = primary_server; + } + init_waitqueue_head(&tcp_ses->response_q); + init_waitqueue_head(&tcp_ses->request_q); + INIT_LIST_HEAD(&tcp_ses->pending_mid_q); + mutex_init(&tcp_ses->_srv_mutex); + memcpy(tcp_ses->workstation_RFC1001_name, + ctx->source_rfc1001_name, RFC1001_NAME_LEN_WITH_NULL); + memcpy(tcp_ses->server_RFC1001_name, + ctx->target_rfc1001_name, RFC1001_NAME_LEN_WITH_NULL); + tcp_ses->session_estab = false; + tcp_ses->sequence_number = 0; + tcp_ses->reconnect_instance = 1; + tcp_ses->lstrp = jiffies; + tcp_ses->compress_algorithm = cpu_to_le16(ctx->compression); + spin_lock_init(&tcp_ses->req_lock); + spin_lock_init(&tcp_ses->srv_lock); + spin_lock_init(&tcp_ses->mid_lock); + INIT_LIST_HEAD(&tcp_ses->tcp_ses_list); + INIT_LIST_HEAD(&tcp_ses->smb_ses_list); + INIT_DELAYED_WORK(&tcp_ses->echo, cifs_echo_request); + INIT_DELAYED_WORK(&tcp_ses->resolve, cifs_resolve_server); + INIT_DELAYED_WORK(&tcp_ses->reconnect, smb2_reconnect_server); + mutex_init(&tcp_ses->reconnect_mutex); +#ifdef CONFIG_CIFS_DFS_UPCALL + mutex_init(&tcp_ses->refpath_lock); +#endif + memcpy(&tcp_ses->srcaddr, &ctx->srcaddr, + sizeof(tcp_ses->srcaddr)); + memcpy(&tcp_ses->dstaddr, &ctx->dstaddr, + sizeof(tcp_ses->dstaddr)); + if (ctx->use_client_guid) + memcpy(tcp_ses->client_guid, ctx->client_guid, + SMB2_CLIENT_GUID_SIZE); + else + generate_random_uuid(tcp_ses->client_guid); + /* + * at this point we are the only ones with the pointer + * to the struct since the kernel thread not created yet + * no need to spinlock this init of tcpStatus or srv_count + */ + tcp_ses->tcpStatus = CifsNew; + ++tcp_ses->srv_count; + + if (ctx->echo_interval >= SMB_ECHO_INTERVAL_MIN && + ctx->echo_interval <= SMB_ECHO_INTERVAL_MAX) + tcp_ses->echo_interval = ctx->echo_interval * HZ; + else + tcp_ses->echo_interval = SMB_ECHO_INTERVAL_DEFAULT * HZ; + if (tcp_ses->rdma) { +#ifndef CONFIG_CIFS_SMB_DIRECT + cifs_dbg(VFS, "CONFIG_CIFS_SMB_DIRECT is not enabled\n"); + rc = -ENOENT; + goto out_err_crypto_release; +#endif + tcp_ses->smbd_conn = smbd_get_connection( + tcp_ses, (struct sockaddr *)&ctx->dstaddr); + if (tcp_ses->smbd_conn) { + cifs_dbg(VFS, "RDMA transport established\n"); + rc = 0; + goto smbd_connected; + } else { + rc = -ENOENT; + goto out_err_crypto_release; + } + } + rc = ip_connect(tcp_ses); + if (rc < 0) { + cifs_dbg(VFS, "Error connecting to socket. Aborting operation.\n"); + goto out_err_crypto_release; + } +smbd_connected: + /* + * since we're in a cifs function already, we know that + * this will succeed. No need for try_module_get(). + */ + __module_get(THIS_MODULE); + tcp_ses->tsk = kthread_run(cifs_demultiplex_thread, + tcp_ses, "cifsd"); + if (IS_ERR(tcp_ses->tsk)) { + rc = PTR_ERR(tcp_ses->tsk); + cifs_dbg(VFS, "error %d create cifsd thread\n", rc); + module_put(THIS_MODULE); + goto out_err_crypto_release; + } + tcp_ses->min_offload = ctx->min_offload; + /* + * at this point we are the only ones with the pointer + * to the struct since the kernel thread not created yet + * no need to spinlock this update of tcpStatus + */ + spin_lock(&tcp_ses->srv_lock); + tcp_ses->tcpStatus = CifsNeedNegotiate; + spin_unlock(&tcp_ses->srv_lock); + + if ((ctx->max_credits < 20) || (ctx->max_credits > 60000)) + tcp_ses->max_credits = SMB2_MAX_CREDITS_AVAILABLE; + else + tcp_ses->max_credits = ctx->max_credits; + + tcp_ses->nr_targets = 1; + tcp_ses->ignore_signature = ctx->ignore_signature; + /* thread spawned, put it on the list */ + spin_lock(&cifs_tcp_ses_lock); + list_add(&tcp_ses->tcp_ses_list, &cifs_tcp_ses_list); + spin_unlock(&cifs_tcp_ses_lock); + + /* queue echo request delayed work */ + queue_delayed_work(cifsiod_wq, &tcp_ses->echo, tcp_ses->echo_interval); + + /* queue dns resolution delayed work */ + cifs_dbg(FYI, "%s: next dns resolution scheduled for %d seconds in the future\n", + __func__, SMB_DNS_RESOLVE_INTERVAL_DEFAULT); + + queue_delayed_work(cifsiod_wq, &tcp_ses->resolve, (SMB_DNS_RESOLVE_INTERVAL_DEFAULT * HZ)); + + return tcp_ses; + +out_err_crypto_release: + cifs_crypto_secmech_release(tcp_ses); + + put_net(cifs_net_ns(tcp_ses)); + +out_err: + if (tcp_ses) { + if (CIFS_SERVER_IS_CHAN(tcp_ses)) + cifs_put_tcp_session(tcp_ses->primary_server, false); + kfree(tcp_ses->hostname); + if (tcp_ses->ssocket) + sock_release(tcp_ses->ssocket); + kfree(tcp_ses); + } + return ERR_PTR(rc); +} + +/* this function must be called with ses_lock and chan_lock held */ +static int match_session(struct cifs_ses *ses, struct smb3_fs_context *ctx) +{ + if (ctx->sectype != Unspecified && + ctx->sectype != ses->sectype) + return 0; + + /* + * If an existing session is limited to less channels than + * requested, it should not be reused + */ + if (ses->chan_max < ctx->max_channels) + return 0; + + switch (ses->sectype) { + case Kerberos: + if (!uid_eq(ctx->cred_uid, ses->cred_uid)) + return 0; + break; + default: + /* NULL username means anonymous session */ + if (ses->user_name == NULL) { + if (!ctx->nullauth) + return 0; + break; + } + + /* anything else takes username/password */ + if (strncmp(ses->user_name, + ctx->username ? ctx->username : "", + CIFS_MAX_USERNAME_LEN)) + return 0; + if ((ctx->username && strlen(ctx->username) != 0) && + ses->password != NULL && + strncmp(ses->password, + ctx->password ? ctx->password : "", + CIFS_MAX_PASSWORD_LEN)) + return 0; + } + return 1; +} + +/** + * cifs_setup_ipc - helper to setup the IPC tcon for the session + * @ses: smb session to issue the request on + * @ctx: the superblock configuration context to use for building the + * new tree connection for the IPC (interprocess communication RPC) + * + * A new IPC connection is made and stored in the session + * tcon_ipc. The IPC tcon has the same lifetime as the session. + */ +static int +cifs_setup_ipc(struct cifs_ses *ses, struct smb3_fs_context *ctx) +{ + int rc = 0, xid; + struct cifs_tcon *tcon; + char unc[SERVER_NAME_LENGTH + sizeof("//x/IPC$")] = {0}; + bool seal = false; + struct TCP_Server_Info *server = ses->server; + + /* + * If the mount request that resulted in the creation of the + * session requires encryption, force IPC to be encrypted too. + */ + if (ctx->seal) { + if (server->capabilities & SMB2_GLOBAL_CAP_ENCRYPTION) + seal = true; + else { + cifs_server_dbg(VFS, + "IPC: server doesn't support encryption\n"); + return -EOPNOTSUPP; + } + } + + tcon = tconInfoAlloc(); + if (tcon == NULL) + return -ENOMEM; + + spin_lock(&server->srv_lock); + scnprintf(unc, sizeof(unc), "\\\\%s\\IPC$", server->hostname); + spin_unlock(&server->srv_lock); + + xid = get_xid(); + tcon->ses = ses; + tcon->ipc = true; + tcon->seal = seal; + rc = server->ops->tree_connect(xid, ses, unc, tcon, ctx->local_nls); + free_xid(xid); + + if (rc) { + cifs_server_dbg(VFS, "failed to connect to IPC (rc=%d)\n", rc); + tconInfoFree(tcon); + goto out; + } + + cifs_dbg(FYI, "IPC tcon rc=%d ipc tid=0x%x\n", rc, tcon->tid); + + spin_lock(&tcon->tc_lock); + tcon->status = TID_GOOD; + spin_unlock(&tcon->tc_lock); + ses->tcon_ipc = tcon; +out: + return rc; +} + +/** + * cifs_free_ipc - helper to release the session IPC tcon + * @ses: smb session to unmount the IPC from + * + * Needs to be called everytime a session is destroyed. + * + * On session close, the IPC is closed and the server must release all tcons of the session. + * No need to send a tree disconnect here. + * + * Besides, it will make the server to not close durable and resilient files on session close, as + * specified in MS-SMB2 3.3.5.6 Receiving an SMB2 LOGOFF Request. + */ +static int +cifs_free_ipc(struct cifs_ses *ses) +{ + struct cifs_tcon *tcon = ses->tcon_ipc; + + if (tcon == NULL) + return 0; + + tconInfoFree(tcon); + ses->tcon_ipc = NULL; + return 0; +} + +static struct cifs_ses * +cifs_find_smb_ses(struct TCP_Server_Info *server, struct smb3_fs_context *ctx) +{ + struct cifs_ses *ses; + + spin_lock(&cifs_tcp_ses_lock); + list_for_each_entry(ses, &server->smb_ses_list, smb_ses_list) { + spin_lock(&ses->ses_lock); + if (ses->ses_status == SES_EXITING) { + spin_unlock(&ses->ses_lock); + continue; + } + spin_lock(&ses->chan_lock); + if (!match_session(ses, ctx)) { + spin_unlock(&ses->chan_lock); + spin_unlock(&ses->ses_lock); + continue; + } + spin_unlock(&ses->chan_lock); + spin_unlock(&ses->ses_lock); + + ++ses->ses_count; + spin_unlock(&cifs_tcp_ses_lock); + return ses; + } + spin_unlock(&cifs_tcp_ses_lock); + return NULL; +} + +void cifs_put_smb_ses(struct cifs_ses *ses) +{ + unsigned int rc, xid; + unsigned int chan_count; + struct TCP_Server_Info *server = ses->server; + + spin_lock(&ses->ses_lock); + if (ses->ses_status == SES_EXITING) { + spin_unlock(&ses->ses_lock); + return; + } + spin_unlock(&ses->ses_lock); + + cifs_dbg(FYI, "%s: ses_count=%d\n", __func__, ses->ses_count); + cifs_dbg(FYI, + "%s: ses ipc: %s\n", __func__, ses->tcon_ipc ? ses->tcon_ipc->tree_name : "NONE"); + + spin_lock(&cifs_tcp_ses_lock); + if (--ses->ses_count > 0) { + spin_unlock(&cifs_tcp_ses_lock); + return; + } + spin_unlock(&cifs_tcp_ses_lock); + + /* ses_count can never go negative */ + WARN_ON(ses->ses_count < 0); + + if (ses->ses_status == SES_GOOD) + ses->ses_status = SES_EXITING; + + cifs_free_ipc(ses); + + if (ses->ses_status == SES_EXITING && server->ops->logoff) { + xid = get_xid(); + rc = server->ops->logoff(xid, ses); + if (rc) + cifs_server_dbg(VFS, "%s: Session Logoff failure rc=%d\n", + __func__, rc); + _free_xid(xid); + } + + spin_lock(&cifs_tcp_ses_lock); + list_del_init(&ses->smb_ses_list); + spin_unlock(&cifs_tcp_ses_lock); + + chan_count = ses->chan_count; + + /* close any extra channels */ + if (chan_count > 1) { + int i; + + for (i = 1; i < chan_count; i++) { + if (ses->chans[i].iface) { + kref_put(&ses->chans[i].iface->refcount, release_iface); + ses->chans[i].iface = NULL; + } + cifs_put_tcp_session(ses->chans[i].server, 0); + ses->chans[i].server = NULL; + } + } + + sesInfoFree(ses); + cifs_put_tcp_session(server, 0); +} + +#ifdef CONFIG_KEYS + +/* strlen("cifs:a:") + CIFS_MAX_DOMAINNAME_LEN + 1 */ +#define CIFSCREDS_DESC_SIZE (7 + CIFS_MAX_DOMAINNAME_LEN + 1) + +/* Populate username and pw fields from keyring if possible */ +static int +cifs_set_cifscreds(struct smb3_fs_context *ctx, struct cifs_ses *ses) +{ + int rc = 0; + int is_domain = 0; + const char *delim, *payload; + char *desc; + ssize_t len; + struct key *key; + struct TCP_Server_Info *server = ses->server; + struct sockaddr_in *sa; + struct sockaddr_in6 *sa6; + const struct user_key_payload *upayload; + + desc = kmalloc(CIFSCREDS_DESC_SIZE, GFP_KERNEL); + if (!desc) + return -ENOMEM; + + /* try to find an address key first */ + switch (server->dstaddr.ss_family) { + case AF_INET: + sa = (struct sockaddr_in *)&server->dstaddr; + sprintf(desc, "cifs:a:%pI4", &sa->sin_addr.s_addr); + break; + case AF_INET6: + sa6 = (struct sockaddr_in6 *)&server->dstaddr; + sprintf(desc, "cifs:a:%pI6c", &sa6->sin6_addr.s6_addr); + break; + default: + cifs_dbg(FYI, "Bad ss_family (%hu)\n", + server->dstaddr.ss_family); + rc = -EINVAL; + goto out_err; + } + + cifs_dbg(FYI, "%s: desc=%s\n", __func__, desc); + key = request_key(&key_type_logon, desc, ""); + if (IS_ERR(key)) { + if (!ses->domainName) { + cifs_dbg(FYI, "domainName is NULL\n"); + rc = PTR_ERR(key); + goto out_err; + } + + /* didn't work, try to find a domain key */ + sprintf(desc, "cifs:d:%s", ses->domainName); + cifs_dbg(FYI, "%s: desc=%s\n", __func__, desc); + key = request_key(&key_type_logon, desc, ""); + if (IS_ERR(key)) { + rc = PTR_ERR(key); + goto out_err; + } + is_domain = 1; + } + + down_read(&key->sem); + upayload = user_key_payload_locked(key); + if (IS_ERR_OR_NULL(upayload)) { + rc = upayload ? PTR_ERR(upayload) : -EINVAL; + goto out_key_put; + } + + /* find first : in payload */ + payload = upayload->data; + delim = strnchr(payload, upayload->datalen, ':'); + cifs_dbg(FYI, "payload=%s\n", payload); + if (!delim) { + cifs_dbg(FYI, "Unable to find ':' in payload (datalen=%d)\n", + upayload->datalen); + rc = -EINVAL; + goto out_key_put; + } + + len = delim - payload; + if (len > CIFS_MAX_USERNAME_LEN || len <= 0) { + cifs_dbg(FYI, "Bad value from username search (len=%zd)\n", + len); + rc = -EINVAL; + goto out_key_put; + } + + ctx->username = kstrndup(payload, len, GFP_KERNEL); + if (!ctx->username) { + cifs_dbg(FYI, "Unable to allocate %zd bytes for username\n", + len); + rc = -ENOMEM; + goto out_key_put; + } + cifs_dbg(FYI, "%s: username=%s\n", __func__, ctx->username); + + len = key->datalen - (len + 1); + if (len > CIFS_MAX_PASSWORD_LEN || len <= 0) { + cifs_dbg(FYI, "Bad len for password search (len=%zd)\n", len); + rc = -EINVAL; + kfree(ctx->username); + ctx->username = NULL; + goto out_key_put; + } + + ++delim; + ctx->password = kstrndup(delim, len, GFP_KERNEL); + if (!ctx->password) { + cifs_dbg(FYI, "Unable to allocate %zd bytes for password\n", + len); + rc = -ENOMEM; + kfree(ctx->username); + ctx->username = NULL; + goto out_key_put; + } + + /* + * If we have a domain key then we must set the domainName in the + * for the request. + */ + if (is_domain && ses->domainName) { + ctx->domainname = kstrdup(ses->domainName, GFP_KERNEL); + if (!ctx->domainname) { + cifs_dbg(FYI, "Unable to allocate %zd bytes for domain\n", + len); + rc = -ENOMEM; + kfree(ctx->username); + ctx->username = NULL; + kfree_sensitive(ctx->password); + ctx->password = NULL; + goto out_key_put; + } + } + + strscpy(ctx->workstation_name, ses->workstation_name, sizeof(ctx->workstation_name)); + +out_key_put: + up_read(&key->sem); + key_put(key); +out_err: + kfree(desc); + cifs_dbg(FYI, "%s: returning %d\n", __func__, rc); + return rc; +} +#else /* ! CONFIG_KEYS */ +static inline int +cifs_set_cifscreds(struct smb3_fs_context *ctx __attribute__((unused)), + struct cifs_ses *ses __attribute__((unused))) +{ + return -ENOSYS; +} +#endif /* CONFIG_KEYS */ + +/** + * cifs_get_smb_ses - get a session matching @ctx data from @server + * @server: server to setup the session to + * @ctx: superblock configuration context to use to setup the session + * + * This function assumes it is being called from cifs_mount() where we + * already got a server reference (server refcount +1). See + * cifs_get_tcon() for refcount explanations. + */ +struct cifs_ses * +cifs_get_smb_ses(struct TCP_Server_Info *server, struct smb3_fs_context *ctx) +{ + int rc = 0; + unsigned int xid; + struct cifs_ses *ses; + struct sockaddr_in *addr = (struct sockaddr_in *)&server->dstaddr; + struct sockaddr_in6 *addr6 = (struct sockaddr_in6 *)&server->dstaddr; + + xid = get_xid(); + + ses = cifs_find_smb_ses(server, ctx); + if (ses) { + cifs_dbg(FYI, "Existing smb sess found (status=%d)\n", + ses->ses_status); + + spin_lock(&ses->chan_lock); + if (cifs_chan_needs_reconnect(ses, server)) { + spin_unlock(&ses->chan_lock); + cifs_dbg(FYI, "Session needs reconnect\n"); + + mutex_lock(&ses->session_mutex); + rc = cifs_negotiate_protocol(xid, ses, server); + if (rc) { + mutex_unlock(&ses->session_mutex); + /* problem -- put our ses reference */ + cifs_put_smb_ses(ses); + free_xid(xid); + return ERR_PTR(rc); + } + + rc = cifs_setup_session(xid, ses, server, + ctx->local_nls); + if (rc) { + mutex_unlock(&ses->session_mutex); + /* problem -- put our reference */ + cifs_put_smb_ses(ses); + free_xid(xid); + return ERR_PTR(rc); + } + mutex_unlock(&ses->session_mutex); + + spin_lock(&ses->chan_lock); + } + spin_unlock(&ses->chan_lock); + + /* existing SMB ses has a server reference already */ + cifs_put_tcp_session(server, 0); + free_xid(xid); + return ses; + } + + rc = -ENOMEM; + + cifs_dbg(FYI, "Existing smb sess not found\n"); + ses = sesInfoAlloc(); + if (ses == NULL) + goto get_ses_fail; + + /* new SMB session uses our server ref */ + ses->server = server; + if (server->dstaddr.ss_family == AF_INET6) + sprintf(ses->ip_addr, "%pI6", &addr6->sin6_addr); + else + sprintf(ses->ip_addr, "%pI4", &addr->sin_addr); + + if (ctx->username) { + ses->user_name = kstrdup(ctx->username, GFP_KERNEL); + if (!ses->user_name) + goto get_ses_fail; + } + + /* ctx->password freed at unmount */ + if (ctx->password) { + ses->password = kstrdup(ctx->password, GFP_KERNEL); + if (!ses->password) + goto get_ses_fail; + } + if (ctx->domainname) { + ses->domainName = kstrdup(ctx->domainname, GFP_KERNEL); + if (!ses->domainName) + goto get_ses_fail; + } + + strscpy(ses->workstation_name, ctx->workstation_name, sizeof(ses->workstation_name)); + + if (ctx->domainauto) + ses->domainAuto = ctx->domainauto; + ses->cred_uid = ctx->cred_uid; + ses->linux_uid = ctx->linux_uid; + + ses->sectype = ctx->sectype; + ses->sign = ctx->sign; + + /* add server as first channel */ + spin_lock(&ses->chan_lock); + ses->chans[0].server = server; + ses->chan_count = 1; + ses->chan_max = ctx->multichannel ? ctx->max_channels:1; + ses->chans_need_reconnect = 1; + spin_unlock(&ses->chan_lock); + + mutex_lock(&ses->session_mutex); + rc = cifs_negotiate_protocol(xid, ses, server); + if (!rc) + rc = cifs_setup_session(xid, ses, server, ctx->local_nls); + mutex_unlock(&ses->session_mutex); + + /* each channel uses a different signing key */ + spin_lock(&ses->chan_lock); + memcpy(ses->chans[0].signkey, ses->smb3signingkey, + sizeof(ses->smb3signingkey)); + spin_unlock(&ses->chan_lock); + + if (rc) + goto get_ses_fail; + + /* + * success, put it on the list and add it as first channel + * note: the session becomes active soon after this. So you'll + * need to lock before changing something in the session. + */ + spin_lock(&cifs_tcp_ses_lock); + list_add(&ses->smb_ses_list, &server->smb_ses_list); + spin_unlock(&cifs_tcp_ses_lock); + + cifs_setup_ipc(ses, ctx); + + free_xid(xid); + + return ses; + +get_ses_fail: + sesInfoFree(ses); + free_xid(xid); + return ERR_PTR(rc); +} + +/* this function must be called with tc_lock held */ +static int match_tcon(struct cifs_tcon *tcon, struct smb3_fs_context *ctx) +{ + if (tcon->status == TID_EXITING) + return 0; + if (strncmp(tcon->tree_name, ctx->UNC, MAX_TREE_SIZE)) + return 0; + if (tcon->seal != ctx->seal) + return 0; + if (tcon->snapshot_time != ctx->snapshot_time) + return 0; + if (tcon->handle_timeout != ctx->handle_timeout) + return 0; + if (tcon->no_lease != ctx->no_lease) + return 0; + if (tcon->nodelete != ctx->nodelete) + return 0; + return 1; +} + +static struct cifs_tcon * +cifs_find_tcon(struct cifs_ses *ses, struct smb3_fs_context *ctx) +{ + struct cifs_tcon *tcon; + + spin_lock(&cifs_tcp_ses_lock); + list_for_each_entry(tcon, &ses->tcon_list, tcon_list) { + spin_lock(&tcon->tc_lock); + if (!match_tcon(tcon, ctx)) { + spin_unlock(&tcon->tc_lock); + continue; + } + ++tcon->tc_count; + spin_unlock(&tcon->tc_lock); + spin_unlock(&cifs_tcp_ses_lock); + return tcon; + } + spin_unlock(&cifs_tcp_ses_lock); + return NULL; +} + +void +cifs_put_tcon(struct cifs_tcon *tcon) +{ + unsigned int xid; + struct cifs_ses *ses; + + /* + * IPC tcon share the lifetime of their session and are + * destroyed in the session put function + */ + if (tcon == NULL || tcon->ipc) + return; + + ses = tcon->ses; + cifs_dbg(FYI, "%s: tc_count=%d\n", __func__, tcon->tc_count); + spin_lock(&cifs_tcp_ses_lock); + spin_lock(&tcon->tc_lock); + if (--tcon->tc_count > 0) { + spin_unlock(&tcon->tc_lock); + spin_unlock(&cifs_tcp_ses_lock); + return; + } + + /* tc_count can never go negative */ + WARN_ON(tcon->tc_count < 0); + + list_del_init(&tcon->tcon_list); + tcon->status = TID_EXITING; + spin_unlock(&tcon->tc_lock); + spin_unlock(&cifs_tcp_ses_lock); + + /* cancel polling of interfaces */ + cancel_delayed_work_sync(&tcon->query_interfaces); + + if (tcon->use_witness) { + int rc; + + rc = cifs_swn_unregister(tcon); + if (rc < 0) { + cifs_dbg(VFS, "%s: Failed to unregister for witness notifications: %d\n", + __func__, rc); + } + } + + xid = get_xid(); + if (ses->server->ops->tree_disconnect) + ses->server->ops->tree_disconnect(xid, tcon); + _free_xid(xid); + + cifs_fscache_release_super_cookie(tcon); + tconInfoFree(tcon); + cifs_put_smb_ses(ses); +} + +/** + * cifs_get_tcon - get a tcon matching @ctx data from @ses + * @ses: smb session to issue the request on + * @ctx: the superblock configuration context to use for building the + * + * - tcon refcount is the number of mount points using the tcon. + * - ses refcount is the number of tcon using the session. + * + * 1. This function assumes it is being called from cifs_mount() where + * we already got a session reference (ses refcount +1). + * + * 2. Since we're in the context of adding a mount point, the end + * result should be either: + * + * a) a new tcon already allocated with refcount=1 (1 mount point) and + * its session refcount incremented (1 new tcon). This +1 was + * already done in (1). + * + * b) an existing tcon with refcount+1 (add a mount point to it) and + * identical ses refcount (no new tcon). Because of (1) we need to + * decrement the ses refcount. + */ +static struct cifs_tcon * +cifs_get_tcon(struct cifs_ses *ses, struct smb3_fs_context *ctx) +{ + int rc, xid; + struct cifs_tcon *tcon; + + tcon = cifs_find_tcon(ses, ctx); + if (tcon) { + /* + * tcon has refcount already incremented but we need to + * decrement extra ses reference gotten by caller (case b) + */ + cifs_dbg(FYI, "Found match on UNC path\n"); + cifs_put_smb_ses(ses); + return tcon; + } + + if (!ses->server->ops->tree_connect) { + rc = -ENOSYS; + goto out_fail; + } + + tcon = tconInfoAlloc(); + if (tcon == NULL) { + rc = -ENOMEM; + goto out_fail; + } + + if (ctx->snapshot_time) { + if (ses->server->vals->protocol_id == 0) { + cifs_dbg(VFS, + "Use SMB2 or later for snapshot mount option\n"); + rc = -EOPNOTSUPP; + goto out_fail; + } else + tcon->snapshot_time = ctx->snapshot_time; + } + + if (ctx->handle_timeout) { + if (ses->server->vals->protocol_id == 0) { + cifs_dbg(VFS, + "Use SMB2.1 or later for handle timeout option\n"); + rc = -EOPNOTSUPP; + goto out_fail; + } else + tcon->handle_timeout = ctx->handle_timeout; + } + + tcon->ses = ses; + if (ctx->password) { + tcon->password = kstrdup(ctx->password, GFP_KERNEL); + if (!tcon->password) { + rc = -ENOMEM; + goto out_fail; + } + } + + if (ctx->seal) { + if (ses->server->vals->protocol_id == 0) { + cifs_dbg(VFS, + "SMB3 or later required for encryption\n"); + rc = -EOPNOTSUPP; + goto out_fail; + } else if (tcon->ses->server->capabilities & + SMB2_GLOBAL_CAP_ENCRYPTION) + tcon->seal = true; + else { + cifs_dbg(VFS, "Encryption is not supported on share\n"); + rc = -EOPNOTSUPP; + goto out_fail; + } + } + + if (ctx->linux_ext) { + if (ses->server->posix_ext_supported) { + tcon->posix_extensions = true; + pr_warn_once("SMB3.11 POSIX Extensions are experimental\n"); + } else if ((ses->server->vals->protocol_id == SMB311_PROT_ID) || + (strcmp(ses->server->vals->version_string, + SMB3ANY_VERSION_STRING) == 0) || + (strcmp(ses->server->vals->version_string, + SMBDEFAULT_VERSION_STRING) == 0)) { + cifs_dbg(VFS, "Server does not support mounting with posix SMB3.11 extensions\n"); + rc = -EOPNOTSUPP; + goto out_fail; + } else { + cifs_dbg(VFS, "Check vers= mount option. SMB3.11 " + "disabled but required for POSIX extensions\n"); + rc = -EOPNOTSUPP; + goto out_fail; + } + } + + xid = get_xid(); + rc = ses->server->ops->tree_connect(xid, ses, ctx->UNC, tcon, + ctx->local_nls); + free_xid(xid); + cifs_dbg(FYI, "Tcon rc = %d\n", rc); + if (rc) + goto out_fail; + + tcon->use_persistent = false; + /* check if SMB2 or later, CIFS does not support persistent handles */ + if (ctx->persistent) { + if (ses->server->vals->protocol_id == 0) { + cifs_dbg(VFS, + "SMB3 or later required for persistent handles\n"); + rc = -EOPNOTSUPP; + goto out_fail; + } else if (ses->server->capabilities & + SMB2_GLOBAL_CAP_PERSISTENT_HANDLES) + tcon->use_persistent = true; + else /* persistent handles requested but not supported */ { + cifs_dbg(VFS, + "Persistent handles not supported on share\n"); + rc = -EOPNOTSUPP; + goto out_fail; + } + } else if ((tcon->capabilities & SMB2_SHARE_CAP_CONTINUOUS_AVAILABILITY) + && (ses->server->capabilities & SMB2_GLOBAL_CAP_PERSISTENT_HANDLES) + && (ctx->nopersistent == false)) { + cifs_dbg(FYI, "enabling persistent handles\n"); + tcon->use_persistent = true; + } else if (ctx->resilient) { + if (ses->server->vals->protocol_id == 0) { + cifs_dbg(VFS, + "SMB2.1 or later required for resilient handles\n"); + rc = -EOPNOTSUPP; + goto out_fail; + } + tcon->use_resilient = true; + } + + tcon->use_witness = false; + if (IS_ENABLED(CONFIG_CIFS_SWN_UPCALL) && ctx->witness) { + if (ses->server->vals->protocol_id >= SMB30_PROT_ID) { + if (tcon->capabilities & SMB2_SHARE_CAP_CLUSTER) { + /* + * Set witness in use flag in first place + * to retry registration in the echo task + */ + tcon->use_witness = true; + /* And try to register immediately */ + rc = cifs_swn_register(tcon); + if (rc < 0) { + cifs_dbg(VFS, "Failed to register for witness notifications: %d\n", rc); + goto out_fail; + } + } else { + /* TODO: try to extend for non-cluster uses (eg multichannel) */ + cifs_dbg(VFS, "witness requested on mount but no CLUSTER capability on share\n"); + rc = -EOPNOTSUPP; + goto out_fail; + } + } else { + cifs_dbg(VFS, "SMB3 or later required for witness option\n"); + rc = -EOPNOTSUPP; + goto out_fail; + } + } + + /* If the user really knows what they are doing they can override */ + if (tcon->share_flags & SMB2_SHAREFLAG_NO_CACHING) { + if (ctx->cache_ro) + cifs_dbg(VFS, "cache=ro requested on mount but NO_CACHING flag set on share\n"); + else if (ctx->cache_rw) + cifs_dbg(VFS, "cache=singleclient requested on mount but NO_CACHING flag set on share\n"); + } + + if (ctx->no_lease) { + if (ses->server->vals->protocol_id == 0) { + cifs_dbg(VFS, + "SMB2 or later required for nolease option\n"); + rc = -EOPNOTSUPP; + goto out_fail; + } else + tcon->no_lease = ctx->no_lease; + } + + /* + * We can have only one retry value for a connection to a share so for + * resources mounted more than once to the same server share the last + * value passed in for the retry flag is used. + */ + tcon->retry = ctx->retry; + tcon->nocase = ctx->nocase; + tcon->broken_sparse_sup = ctx->no_sparse; + if (ses->server->capabilities & SMB2_GLOBAL_CAP_DIRECTORY_LEASING) + tcon->nohandlecache = ctx->nohandlecache; + else + tcon->nohandlecache = true; + tcon->nodelete = ctx->nodelete; + tcon->local_lease = ctx->local_lease; + INIT_LIST_HEAD(&tcon->pending_opens); + tcon->status = TID_GOOD; + + INIT_DELAYED_WORK(&tcon->query_interfaces, + smb2_query_server_interfaces); + if (ses->server->dialect >= SMB30_PROT_ID && + (ses->server->capabilities & SMB2_GLOBAL_CAP_MULTI_CHANNEL)) { + /* schedule query interfaces poll */ + queue_delayed_work(cifsiod_wq, &tcon->query_interfaces, + (SMB_INTERFACE_POLL_INTERVAL * HZ)); + } + + spin_lock(&cifs_tcp_ses_lock); + list_add(&tcon->tcon_list, &ses->tcon_list); + spin_unlock(&cifs_tcp_ses_lock); + + return tcon; + +out_fail: + tconInfoFree(tcon); + return ERR_PTR(rc); +} + +void +cifs_put_tlink(struct tcon_link *tlink) +{ + if (!tlink || IS_ERR(tlink)) + return; + + if (!atomic_dec_and_test(&tlink->tl_count) || + test_bit(TCON_LINK_IN_TREE, &tlink->tl_flags)) { + tlink->tl_time = jiffies; + return; + } + + if (!IS_ERR(tlink_tcon(tlink))) + cifs_put_tcon(tlink_tcon(tlink)); + kfree(tlink); + return; +} + +static int +compare_mount_options(struct super_block *sb, struct cifs_mnt_data *mnt_data) +{ + struct cifs_sb_info *old = CIFS_SB(sb); + struct cifs_sb_info *new = mnt_data->cifs_sb; + unsigned int oldflags = old->mnt_cifs_flags & CIFS_MOUNT_MASK; + unsigned int newflags = new->mnt_cifs_flags & CIFS_MOUNT_MASK; + + if ((sb->s_flags & CIFS_MS_MASK) != (mnt_data->flags & CIFS_MS_MASK)) + return 0; + + if (old->mnt_cifs_serverino_autodisabled) + newflags &= ~CIFS_MOUNT_SERVER_INUM; + + if (oldflags != newflags) + return 0; + + /* + * We want to share sb only if we don't specify an r/wsize or + * specified r/wsize is greater than or equal to existing one. + */ + if (new->ctx->wsize && new->ctx->wsize < old->ctx->wsize) + return 0; + + if (new->ctx->rsize && new->ctx->rsize < old->ctx->rsize) + return 0; + + if (!uid_eq(old->ctx->linux_uid, new->ctx->linux_uid) || + !gid_eq(old->ctx->linux_gid, new->ctx->linux_gid)) + return 0; + + if (old->ctx->file_mode != new->ctx->file_mode || + old->ctx->dir_mode != new->ctx->dir_mode) + return 0; + + if (strcmp(old->local_nls->charset, new->local_nls->charset)) + return 0; + + if (old->ctx->acregmax != new->ctx->acregmax) + return 0; + if (old->ctx->acdirmax != new->ctx->acdirmax) + return 0; + if (old->ctx->closetimeo != new->ctx->closetimeo) + return 0; + + return 1; +} + +static int +match_prepath(struct super_block *sb, struct cifs_mnt_data *mnt_data) +{ + struct cifs_sb_info *old = CIFS_SB(sb); + struct cifs_sb_info *new = mnt_data->cifs_sb; + bool old_set = (old->mnt_cifs_flags & CIFS_MOUNT_USE_PREFIX_PATH) && + old->prepath; + bool new_set = (new->mnt_cifs_flags & CIFS_MOUNT_USE_PREFIX_PATH) && + new->prepath; + + if (old_set && new_set && !strcmp(new->prepath, old->prepath)) + return 1; + else if (!old_set && !new_set) + return 1; + + return 0; +} + +int +cifs_match_super(struct super_block *sb, void *data) +{ + struct cifs_mnt_data *mnt_data = data; + struct smb3_fs_context *ctx; + struct cifs_sb_info *cifs_sb; + struct TCP_Server_Info *tcp_srv; + struct cifs_ses *ses; + struct cifs_tcon *tcon; + struct tcon_link *tlink; + int rc = 0; + + spin_lock(&cifs_tcp_ses_lock); + cifs_sb = CIFS_SB(sb); + + /* We do not want to use a superblock that has been shutdown */ + if (CIFS_MOUNT_SHUTDOWN & cifs_sb->mnt_cifs_flags) { + spin_unlock(&cifs_tcp_ses_lock); + return 0; + } + + tlink = cifs_get_tlink(cifs_sb_master_tlink(cifs_sb)); + if (tlink == NULL) { + /* can not match superblock if tlink were ever null */ + spin_unlock(&cifs_tcp_ses_lock); + return 0; + } + tcon = tlink_tcon(tlink); + ses = tcon->ses; + tcp_srv = ses->server; + + ctx = mnt_data->ctx; + + spin_lock(&tcp_srv->srv_lock); + spin_lock(&ses->ses_lock); + spin_lock(&ses->chan_lock); + spin_lock(&tcon->tc_lock); + if (!match_server(tcp_srv, ctx) || + !match_session(ses, ctx) || + !match_tcon(tcon, ctx) || + !match_prepath(sb, mnt_data)) { + rc = 0; + goto out; + } + + rc = compare_mount_options(sb, mnt_data); +out: + spin_unlock(&tcon->tc_lock); + spin_unlock(&ses->chan_lock); + spin_unlock(&ses->ses_lock); + spin_unlock(&tcp_srv->srv_lock); + + spin_unlock(&cifs_tcp_ses_lock); + cifs_put_tlink(tlink); + return rc; +} + +#ifdef CONFIG_DEBUG_LOCK_ALLOC +static struct lock_class_key cifs_key[2]; +static struct lock_class_key cifs_slock_key[2]; + +static inline void +cifs_reclassify_socket4(struct socket *sock) +{ + struct sock *sk = sock->sk; + BUG_ON(!sock_allow_reclassification(sk)); + sock_lock_init_class_and_name(sk, "slock-AF_INET-CIFS", + &cifs_slock_key[0], "sk_lock-AF_INET-CIFS", &cifs_key[0]); +} + +static inline void +cifs_reclassify_socket6(struct socket *sock) +{ + struct sock *sk = sock->sk; + BUG_ON(!sock_allow_reclassification(sk)); + sock_lock_init_class_and_name(sk, "slock-AF_INET6-CIFS", + &cifs_slock_key[1], "sk_lock-AF_INET6-CIFS", &cifs_key[1]); +} +#else +static inline void +cifs_reclassify_socket4(struct socket *sock) +{ +} + +static inline void +cifs_reclassify_socket6(struct socket *sock) +{ +} +#endif + +/* See RFC1001 section 14 on representation of Netbios names */ +static void rfc1002mangle(char *target, char *source, unsigned int length) +{ + unsigned int i, j; + + for (i = 0, j = 0; i < (length); i++) { + /* mask a nibble at a time and encode */ + target[j] = 'A' + (0x0F & (source[i] >> 4)); + target[j+1] = 'A' + (0x0F & source[i]); + j += 2; + } + +} + +static int +bind_socket(struct TCP_Server_Info *server) +{ + int rc = 0; + if (server->srcaddr.ss_family != AF_UNSPEC) { + /* Bind to the specified local IP address */ + struct socket *socket = server->ssocket; + rc = socket->ops->bind(socket, + (struct sockaddr *) &server->srcaddr, + sizeof(server->srcaddr)); + if (rc < 0) { + struct sockaddr_in *saddr4; + struct sockaddr_in6 *saddr6; + saddr4 = (struct sockaddr_in *)&server->srcaddr; + saddr6 = (struct sockaddr_in6 *)&server->srcaddr; + if (saddr6->sin6_family == AF_INET6) + cifs_server_dbg(VFS, "Failed to bind to: %pI6c, error: %d\n", + &saddr6->sin6_addr, rc); + else + cifs_server_dbg(VFS, "Failed to bind to: %pI4, error: %d\n", + &saddr4->sin_addr.s_addr, rc); + } + } + return rc; +} + +static int +ip_rfc1001_connect(struct TCP_Server_Info *server) +{ + int rc = 0; + /* + * some servers require RFC1001 sessinit before sending + * negprot - BB check reconnection in case where second + * sessinit is sent but no second negprot + */ + struct rfc1002_session_packet req = {}; + struct smb_hdr *smb_buf = (struct smb_hdr *)&req; + unsigned int len; + + req.trailer.session_req.called_len = sizeof(req.trailer.session_req.called_name); + + if (server->server_RFC1001_name[0] != 0) + rfc1002mangle(req.trailer.session_req.called_name, + server->server_RFC1001_name, + RFC1001_NAME_LEN_WITH_NULL); + else + rfc1002mangle(req.trailer.session_req.called_name, + DEFAULT_CIFS_CALLED_NAME, + RFC1001_NAME_LEN_WITH_NULL); + + req.trailer.session_req.calling_len = sizeof(req.trailer.session_req.calling_name); + + /* calling name ends in null (byte 16) from old smb convention */ + if (server->workstation_RFC1001_name[0] != 0) + rfc1002mangle(req.trailer.session_req.calling_name, + server->workstation_RFC1001_name, + RFC1001_NAME_LEN_WITH_NULL); + else + rfc1002mangle(req.trailer.session_req.calling_name, + "LINUX_CIFS_CLNT", + RFC1001_NAME_LEN_WITH_NULL); + + /* + * As per rfc1002, @len must be the number of bytes that follows the + * length field of a rfc1002 session request payload. + */ + len = sizeof(req) - offsetof(struct rfc1002_session_packet, trailer.session_req); + + smb_buf->smb_buf_length = cpu_to_be32((RFC1002_SESSION_REQUEST << 24) | len); + rc = smb_send(server, smb_buf, len); + /* + * RFC1001 layer in at least one server requires very short break before + * negprot presumably because not expecting negprot to follow so fast. + * This is a simple solution that works without complicating the code + * and causes no significant slowing down on mount for everyone else + */ + usleep_range(1000, 2000); + + return rc; +} + +static int +generic_ip_connect(struct TCP_Server_Info *server) +{ + int rc = 0; + __be16 sport; + int slen, sfamily; + struct socket *socket = server->ssocket; + struct sockaddr *saddr; + + saddr = (struct sockaddr *) &server->dstaddr; + + if (server->dstaddr.ss_family == AF_INET6) { + struct sockaddr_in6 *ipv6 = (struct sockaddr_in6 *)&server->dstaddr; + + sport = ipv6->sin6_port; + slen = sizeof(struct sockaddr_in6); + sfamily = AF_INET6; + cifs_dbg(FYI, "%s: connecting to [%pI6]:%d\n", __func__, &ipv6->sin6_addr, + ntohs(sport)); + } else { + struct sockaddr_in *ipv4 = (struct sockaddr_in *)&server->dstaddr; + + sport = ipv4->sin_port; + slen = sizeof(struct sockaddr_in); + sfamily = AF_INET; + cifs_dbg(FYI, "%s: connecting to %pI4:%d\n", __func__, &ipv4->sin_addr, + ntohs(sport)); + } + + if (socket == NULL) { + rc = __sock_create(cifs_net_ns(server), sfamily, SOCK_STREAM, + IPPROTO_TCP, &socket, 1); + if (rc < 0) { + cifs_server_dbg(VFS, "Error %d creating socket\n", rc); + server->ssocket = NULL; + return rc; + } + + /* BB other socket options to set KEEPALIVE, NODELAY? */ + cifs_dbg(FYI, "Socket created\n"); + server->ssocket = socket; + socket->sk->sk_allocation = GFP_NOFS; + if (sfamily == AF_INET6) + cifs_reclassify_socket6(socket); + else + cifs_reclassify_socket4(socket); + } + + rc = bind_socket(server); + if (rc < 0) + return rc; + + /* + * Eventually check for other socket options to change from + * the default. sock_setsockopt not used because it expects + * user space buffer + */ + socket->sk->sk_rcvtimeo = 7 * HZ; + socket->sk->sk_sndtimeo = 5 * HZ; + + /* make the bufsizes depend on wsize/rsize and max requests */ + if (server->noautotune) { + if (socket->sk->sk_sndbuf < (200 * 1024)) + socket->sk->sk_sndbuf = 200 * 1024; + if (socket->sk->sk_rcvbuf < (140 * 1024)) + socket->sk->sk_rcvbuf = 140 * 1024; + } + + if (server->tcp_nodelay) + tcp_sock_set_nodelay(socket->sk); + + cifs_dbg(FYI, "sndbuf %d rcvbuf %d rcvtimeo 0x%lx\n", + socket->sk->sk_sndbuf, + socket->sk->sk_rcvbuf, socket->sk->sk_rcvtimeo); + + rc = socket->ops->connect(socket, saddr, slen, + server->noblockcnt ? O_NONBLOCK : 0); + /* + * When mounting SMB root file systems, we do not want to block in + * connect. Otherwise bail out and then let cifs_reconnect() perform + * reconnect failover - if possible. + */ + if (server->noblockcnt && rc == -EINPROGRESS) + rc = 0; + if (rc < 0) { + cifs_dbg(FYI, "Error %d connecting to server\n", rc); + trace_smb3_connect_err(server->hostname, server->conn_id, &server->dstaddr, rc); + sock_release(socket); + server->ssocket = NULL; + return rc; + } + trace_smb3_connect_done(server->hostname, server->conn_id, &server->dstaddr); + if (sport == htons(RFC1001_PORT)) + rc = ip_rfc1001_connect(server); + + return rc; +} + +static int +ip_connect(struct TCP_Server_Info *server) +{ + __be16 *sport; + struct sockaddr_in6 *addr6 = (struct sockaddr_in6 *)&server->dstaddr; + struct sockaddr_in *addr = (struct sockaddr_in *)&server->dstaddr; + + if (server->dstaddr.ss_family == AF_INET6) + sport = &addr6->sin6_port; + else + sport = &addr->sin_port; + + if (*sport == 0) { + int rc; + + /* try with 445 port at first */ + *sport = htons(CIFS_PORT); + + rc = generic_ip_connect(server); + if (rc >= 0) + return rc; + + /* if it failed, try with 139 port */ + *sport = htons(RFC1001_PORT); + } + + return generic_ip_connect(server); +} + +#ifdef CONFIG_CIFS_ALLOW_INSECURE_LEGACY +void reset_cifs_unix_caps(unsigned int xid, struct cifs_tcon *tcon, + struct cifs_sb_info *cifs_sb, struct smb3_fs_context *ctx) +{ + /* + * If we are reconnecting then should we check to see if + * any requested capabilities changed locally e.g. via + * remount but we can not do much about it here + * if they have (even if we could detect it by the following) + * Perhaps we could add a backpointer to array of sb from tcon + * or if we change to make all sb to same share the same + * sb as NFS - then we only have one backpointer to sb. + * What if we wanted to mount the server share twice once with + * and once without posixacls or posix paths? + */ + __u64 saved_cap = le64_to_cpu(tcon->fsUnixInfo.Capability); + + if (ctx && ctx->no_linux_ext) { + tcon->fsUnixInfo.Capability = 0; + tcon->unix_ext = 0; /* Unix Extensions disabled */ + cifs_dbg(FYI, "Linux protocol extensions disabled\n"); + return; + } else if (ctx) + tcon->unix_ext = 1; /* Unix Extensions supported */ + + if (!tcon->unix_ext) { + cifs_dbg(FYI, "Unix extensions disabled so not set on reconnect\n"); + return; + } + + if (!CIFSSMBQFSUnixInfo(xid, tcon)) { + __u64 cap = le64_to_cpu(tcon->fsUnixInfo.Capability); + cifs_dbg(FYI, "unix caps which server supports %lld\n", cap); + /* + * check for reconnect case in which we do not + * want to change the mount behavior if we can avoid it + */ + if (ctx == NULL) { + /* + * turn off POSIX ACL and PATHNAMES if not set + * originally at mount time + */ + if ((saved_cap & CIFS_UNIX_POSIX_ACL_CAP) == 0) + cap &= ~CIFS_UNIX_POSIX_ACL_CAP; + if ((saved_cap & CIFS_UNIX_POSIX_PATHNAMES_CAP) == 0) { + if (cap & CIFS_UNIX_POSIX_PATHNAMES_CAP) + cifs_dbg(VFS, "POSIXPATH support change\n"); + cap &= ~CIFS_UNIX_POSIX_PATHNAMES_CAP; + } else if ((cap & CIFS_UNIX_POSIX_PATHNAMES_CAP) == 0) { + cifs_dbg(VFS, "possible reconnect error\n"); + cifs_dbg(VFS, "server disabled POSIX path support\n"); + } + } + + if (cap & CIFS_UNIX_TRANSPORT_ENCRYPTION_MANDATORY_CAP) + cifs_dbg(VFS, "per-share encryption not supported yet\n"); + + cap &= CIFS_UNIX_CAP_MASK; + if (ctx && ctx->no_psx_acl) + cap &= ~CIFS_UNIX_POSIX_ACL_CAP; + else if (CIFS_UNIX_POSIX_ACL_CAP & cap) { + cifs_dbg(FYI, "negotiated posix acl support\n"); + if (cifs_sb) + cifs_sb->mnt_cifs_flags |= + CIFS_MOUNT_POSIXACL; + } + + if (ctx && ctx->posix_paths == 0) + cap &= ~CIFS_UNIX_POSIX_PATHNAMES_CAP; + else if (cap & CIFS_UNIX_POSIX_PATHNAMES_CAP) { + cifs_dbg(FYI, "negotiate posix pathnames\n"); + if (cifs_sb) + cifs_sb->mnt_cifs_flags |= + CIFS_MOUNT_POSIX_PATHS; + } + + cifs_dbg(FYI, "Negotiate caps 0x%x\n", (int)cap); +#ifdef CONFIG_CIFS_DEBUG2 + if (cap & CIFS_UNIX_FCNTL_CAP) + cifs_dbg(FYI, "FCNTL cap\n"); + if (cap & CIFS_UNIX_EXTATTR_CAP) + cifs_dbg(FYI, "EXTATTR cap\n"); + if (cap & CIFS_UNIX_POSIX_PATHNAMES_CAP) + cifs_dbg(FYI, "POSIX path cap\n"); + if (cap & CIFS_UNIX_XATTR_CAP) + cifs_dbg(FYI, "XATTR cap\n"); + if (cap & CIFS_UNIX_POSIX_ACL_CAP) + cifs_dbg(FYI, "POSIX ACL cap\n"); + if (cap & CIFS_UNIX_LARGE_READ_CAP) + cifs_dbg(FYI, "very large read cap\n"); + if (cap & CIFS_UNIX_LARGE_WRITE_CAP) + cifs_dbg(FYI, "very large write cap\n"); + if (cap & CIFS_UNIX_TRANSPORT_ENCRYPTION_CAP) + cifs_dbg(FYI, "transport encryption cap\n"); + if (cap & CIFS_UNIX_TRANSPORT_ENCRYPTION_MANDATORY_CAP) + cifs_dbg(FYI, "mandatory transport encryption cap\n"); +#endif /* CIFS_DEBUG2 */ + if (CIFSSMBSetFSUnixInfo(xid, tcon, cap)) { + if (ctx == NULL) + cifs_dbg(FYI, "resetting capabilities failed\n"); + else + cifs_dbg(VFS, "Negotiating Unix capabilities with the server failed. Consider mounting with the Unix Extensions disabled if problems are found by specifying the nounix mount option.\n"); + + } + } +} +#endif /* CONFIG_CIFS_ALLOW_INSECURE_LEGACY */ + +int cifs_setup_cifs_sb(struct cifs_sb_info *cifs_sb) +{ + struct smb3_fs_context *ctx = cifs_sb->ctx; + + INIT_DELAYED_WORK(&cifs_sb->prune_tlinks, cifs_prune_tlinks); + + spin_lock_init(&cifs_sb->tlink_tree_lock); + cifs_sb->tlink_tree = RB_ROOT; + + cifs_dbg(FYI, "file mode: %04ho dir mode: %04ho\n", + ctx->file_mode, ctx->dir_mode); + + /* this is needed for ASCII cp to Unicode converts */ + if (ctx->iocharset == NULL) { + /* load_nls_default cannot return null */ + cifs_sb->local_nls = load_nls_default(); + } else { + cifs_sb->local_nls = load_nls(ctx->iocharset); + if (cifs_sb->local_nls == NULL) { + cifs_dbg(VFS, "CIFS mount error: iocharset %s not found\n", + ctx->iocharset); + return -ELIBACC; + } + } + ctx->local_nls = cifs_sb->local_nls; + + smb3_update_mnt_flags(cifs_sb); + + if (ctx->direct_io) + cifs_dbg(FYI, "mounting share using direct i/o\n"); + if (ctx->cache_ro) { + cifs_dbg(VFS, "mounting share with read only caching. Ensure that the share will not be modified while in use.\n"); + cifs_sb->mnt_cifs_flags |= CIFS_MOUNT_RO_CACHE; + } else if (ctx->cache_rw) { + cifs_dbg(VFS, "mounting share in single client RW caching mode. Ensure that no other systems will be accessing the share.\n"); + cifs_sb->mnt_cifs_flags |= (CIFS_MOUNT_RO_CACHE | + CIFS_MOUNT_RW_CACHE); + } + + if ((ctx->cifs_acl) && (ctx->dynperm)) + cifs_dbg(VFS, "mount option dynperm ignored if cifsacl mount option supported\n"); + + if (ctx->prepath) { + cifs_sb->prepath = kstrdup(ctx->prepath, GFP_KERNEL); + if (cifs_sb->prepath == NULL) + return -ENOMEM; + cifs_sb->mnt_cifs_flags |= CIFS_MOUNT_USE_PREFIX_PATH; + } + + return 0; +} + +/* Release all succeed connections */ +static inline void mount_put_conns(struct mount_ctx *mnt_ctx) +{ + int rc = 0; + + if (mnt_ctx->tcon) + cifs_put_tcon(mnt_ctx->tcon); + else if (mnt_ctx->ses) + cifs_put_smb_ses(mnt_ctx->ses); + else if (mnt_ctx->server) + cifs_put_tcp_session(mnt_ctx->server, 0); + mnt_ctx->cifs_sb->mnt_cifs_flags &= ~CIFS_MOUNT_POSIX_PATHS; + free_xid(mnt_ctx->xid); +} + +/* Get connections for tcp, ses and tcon */ +static int mount_get_conns(struct mount_ctx *mnt_ctx) +{ + int rc = 0; + struct TCP_Server_Info *server = NULL; + struct cifs_ses *ses = NULL; + struct cifs_tcon *tcon = NULL; + struct smb3_fs_context *ctx = mnt_ctx->fs_ctx; + struct cifs_sb_info *cifs_sb = mnt_ctx->cifs_sb; + unsigned int xid; + + xid = get_xid(); + + /* get a reference to a tcp session */ + server = cifs_get_tcp_session(ctx, NULL); + if (IS_ERR(server)) { + rc = PTR_ERR(server); + server = NULL; + goto out; + } + + /* get a reference to a SMB session */ + ses = cifs_get_smb_ses(server, ctx); + if (IS_ERR(ses)) { + rc = PTR_ERR(ses); + ses = NULL; + goto out; + } + + if ((ctx->persistent == true) && (!(ses->server->capabilities & + SMB2_GLOBAL_CAP_PERSISTENT_HANDLES))) { + cifs_server_dbg(VFS, "persistent handles not supported by server\n"); + rc = -EOPNOTSUPP; + goto out; + } + + /* search for existing tcon to this server share */ + tcon = cifs_get_tcon(ses, ctx); + if (IS_ERR(tcon)) { + rc = PTR_ERR(tcon); + tcon = NULL; + goto out; + } + + /* if new SMB3.11 POSIX extensions are supported do not remap / and \ */ + if (tcon->posix_extensions) + cifs_sb->mnt_cifs_flags |= CIFS_MOUNT_POSIX_PATHS; + +#ifdef CONFIG_CIFS_ALLOW_INSECURE_LEGACY + /* tell server which Unix caps we support */ + if (cap_unix(tcon->ses)) { + /* + * reset of caps checks mount to see if unix extensions disabled + * for just this mount. + */ + reset_cifs_unix_caps(xid, tcon, cifs_sb, ctx); + spin_lock(&tcon->ses->server->srv_lock); + if ((tcon->ses->server->tcpStatus == CifsNeedReconnect) && + (le64_to_cpu(tcon->fsUnixInfo.Capability) & + CIFS_UNIX_TRANSPORT_ENCRYPTION_MANDATORY_CAP)) { + spin_unlock(&tcon->ses->server->srv_lock); + rc = -EACCES; + goto out; + } + spin_unlock(&tcon->ses->server->srv_lock); + } else +#endif /* CONFIG_CIFS_ALLOW_INSECURE_LEGACY */ + tcon->unix_ext = 0; /* server does not support them */ + + /* do not care if a following call succeed - informational */ + if (!tcon->pipe && server->ops->qfs_tcon) { + server->ops->qfs_tcon(xid, tcon, cifs_sb); + if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_RO_CACHE) { + if (tcon->fsDevInfo.DeviceCharacteristics & + cpu_to_le32(FILE_READ_ONLY_DEVICE)) + cifs_dbg(VFS, "mounted to read only share\n"); + else if ((cifs_sb->mnt_cifs_flags & + CIFS_MOUNT_RW_CACHE) == 0) + cifs_dbg(VFS, "read only mount of RW share\n"); + /* no need to log a RW mount of a typical RW share */ + } + } + + /* + * Clamp the rsize/wsize mount arguments if they are too big for the server + * and set the rsize/wsize to the negotiated values if not passed in by + * the user on mount + */ + if ((cifs_sb->ctx->wsize == 0) || + (cifs_sb->ctx->wsize > server->ops->negotiate_wsize(tcon, ctx))) + cifs_sb->ctx->wsize = server->ops->negotiate_wsize(tcon, ctx); + if ((cifs_sb->ctx->rsize == 0) || + (cifs_sb->ctx->rsize > server->ops->negotiate_rsize(tcon, ctx))) + cifs_sb->ctx->rsize = server->ops->negotiate_rsize(tcon, ctx); + + /* + * The cookie is initialized from volume info returned above. + * Inside cifs_fscache_get_super_cookie it checks + * that we do not get super cookie twice. + */ + if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_FSCACHE) + cifs_fscache_get_super_cookie(tcon); + +out: + mnt_ctx->server = server; + mnt_ctx->ses = ses; + mnt_ctx->tcon = tcon; + mnt_ctx->xid = xid; + + return rc; +} + +static int mount_setup_tlink(struct cifs_sb_info *cifs_sb, struct cifs_ses *ses, + struct cifs_tcon *tcon) +{ + struct tcon_link *tlink; + + /* hang the tcon off of the superblock */ + tlink = kzalloc(sizeof(*tlink), GFP_KERNEL); + if (tlink == NULL) + return -ENOMEM; + + tlink->tl_uid = ses->linux_uid; + tlink->tl_tcon = tcon; + tlink->tl_time = jiffies; + set_bit(TCON_LINK_MASTER, &tlink->tl_flags); + set_bit(TCON_LINK_IN_TREE, &tlink->tl_flags); + + cifs_sb->master_tlink = tlink; + spin_lock(&cifs_sb->tlink_tree_lock); + tlink_rb_insert(&cifs_sb->tlink_tree, tlink); + spin_unlock(&cifs_sb->tlink_tree_lock); + + queue_delayed_work(cifsiod_wq, &cifs_sb->prune_tlinks, + TLINK_IDLE_EXPIRE); + return 0; +} + +#ifdef CONFIG_CIFS_DFS_UPCALL +/* Get unique dfs connections */ +static int mount_get_dfs_conns(struct mount_ctx *mnt_ctx) +{ + int rc; + + mnt_ctx->fs_ctx->nosharesock = true; + rc = mount_get_conns(mnt_ctx); + if (mnt_ctx->server) { + cifs_dbg(FYI, "%s: marking tcp session as a dfs connection\n", __func__); + spin_lock(&mnt_ctx->server->srv_lock); + mnt_ctx->server->is_dfs_conn = true; + spin_unlock(&mnt_ctx->server->srv_lock); + } + return rc; +} + +/* + * cifs_build_path_to_root returns full path to root when we do not have an + * existing connection (tcon) + */ +static char * +build_unc_path_to_root(const struct smb3_fs_context *ctx, + const struct cifs_sb_info *cifs_sb, bool useppath) +{ + char *full_path, *pos; + unsigned int pplen = useppath && ctx->prepath ? + strlen(ctx->prepath) + 1 : 0; + unsigned int unc_len = strnlen(ctx->UNC, MAX_TREE_SIZE + 1); + + if (unc_len > MAX_TREE_SIZE) + return ERR_PTR(-EINVAL); + + full_path = kmalloc(unc_len + pplen + 1, GFP_KERNEL); + if (full_path == NULL) + return ERR_PTR(-ENOMEM); + + memcpy(full_path, ctx->UNC, unc_len); + pos = full_path + unc_len; + + if (pplen) { + *pos = CIFS_DIR_SEP(cifs_sb); + memcpy(pos + 1, ctx->prepath, pplen); + pos += pplen; + } + + *pos = '\0'; /* add trailing null */ + convert_delimiter(full_path, CIFS_DIR_SEP(cifs_sb)); + cifs_dbg(FYI, "%s: full_path=%s\n", __func__, full_path); + return full_path; +} + +/* + * expand_dfs_referral - Update cifs_sb from dfs referral path + * + * cifs_sb->ctx->mount_options will be (re-)allocated to a string containing updated options for the + * submount. Otherwise it will be left untouched. + */ +static int expand_dfs_referral(struct mount_ctx *mnt_ctx, const char *full_path, + struct dfs_info3_param *referral) +{ + int rc; + struct cifs_sb_info *cifs_sb = mnt_ctx->cifs_sb; + struct smb3_fs_context *ctx = mnt_ctx->fs_ctx; + char *fake_devname = NULL, *mdata = NULL; + + mdata = cifs_compose_mount_options(cifs_sb->ctx->mount_options, full_path + 1, referral, + &fake_devname); + if (IS_ERR(mdata)) { + rc = PTR_ERR(mdata); + mdata = NULL; + } else { + /* + * We can not clear out the whole structure since we no longer have an explicit + * function to parse a mount-string. Instead we need to clear out the individual + * fields that are no longer valid. + */ + kfree(ctx->prepath); + ctx->prepath = NULL; + rc = cifs_setup_volume_info(ctx, mdata, fake_devname); + } + kfree(fake_devname); + kfree(cifs_sb->ctx->mount_options); + cifs_sb->ctx->mount_options = mdata; + + return rc; +} +#endif + +/* TODO: all callers to this are broken. We are not parsing mount_options here + * we should pass a clone of the original context? + */ +int +cifs_setup_volume_info(struct smb3_fs_context *ctx, const char *mntopts, const char *devname) +{ + int rc; + + if (devname) { + cifs_dbg(FYI, "%s: devname=%s\n", __func__, devname); + rc = smb3_parse_devname(devname, ctx); + if (rc) { + cifs_dbg(VFS, "%s: failed to parse %s: %d\n", __func__, devname, rc); + return rc; + } + } + + if (mntopts) { + char *ip; + + rc = smb3_parse_opt(mntopts, "ip", &ip); + if (rc) { + cifs_dbg(VFS, "%s: failed to parse ip options: %d\n", __func__, rc); + return rc; + } + + rc = cifs_convert_address((struct sockaddr *)&ctx->dstaddr, ip, strlen(ip)); + kfree(ip); + if (!rc) { + cifs_dbg(VFS, "%s: failed to convert ip address\n", __func__); + return -EINVAL; + } + } + + if (ctx->nullauth) { + cifs_dbg(FYI, "Anonymous login\n"); + kfree(ctx->username); + ctx->username = NULL; + } else if (ctx->username) { + /* BB fixme parse for domain name here */ + cifs_dbg(FYI, "Username: %s\n", ctx->username); + } else { + cifs_dbg(VFS, "No username specified\n"); + /* In userspace mount helper we can get user name from alternate + locations such as env variables and files on disk */ + return -EINVAL; + } + + return 0; +} + +static int +cifs_are_all_path_components_accessible(struct TCP_Server_Info *server, + unsigned int xid, + struct cifs_tcon *tcon, + struct cifs_sb_info *cifs_sb, + char *full_path, + int added_treename) +{ + int rc; + char *s; + char sep, tmp; + int skip = added_treename ? 1 : 0; + + sep = CIFS_DIR_SEP(cifs_sb); + s = full_path; + + rc = server->ops->is_path_accessible(xid, tcon, cifs_sb, ""); + while (rc == 0) { + /* skip separators */ + while (*s == sep) + s++; + if (!*s) + break; + /* next separator */ + while (*s && *s != sep) + s++; + /* + * if the treename is added, we then have to skip the first + * part within the separators + */ + if (skip) { + skip = 0; + continue; + } + /* + * temporarily null-terminate the path at the end of + * the current component + */ + tmp = *s; + *s = 0; + rc = server->ops->is_path_accessible(xid, tcon, cifs_sb, + full_path); + *s = tmp; + } + return rc; +} + +/* + * Check if path is remote (i.e. a DFS share). + * + * Return -EREMOTE if it is, otherwise 0 or -errno. + */ +static int is_path_remote(struct mount_ctx *mnt_ctx) +{ + int rc; + struct cifs_sb_info *cifs_sb = mnt_ctx->cifs_sb; + struct TCP_Server_Info *server = mnt_ctx->server; + unsigned int xid = mnt_ctx->xid; + struct cifs_tcon *tcon = mnt_ctx->tcon; + struct smb3_fs_context *ctx = mnt_ctx->fs_ctx; + char *full_path; + + if (!server->ops->is_path_accessible) + return -EOPNOTSUPP; + + /* + * cifs_build_path_to_root works only when we have a valid tcon + */ + full_path = cifs_build_path_to_root(ctx, cifs_sb, tcon, + tcon->Flags & SMB_SHARE_IS_IN_DFS); + if (full_path == NULL) + return -ENOMEM; + + cifs_dbg(FYI, "%s: full_path: %s\n", __func__, full_path); + + rc = server->ops->is_path_accessible(xid, tcon, cifs_sb, + full_path); + if (rc != 0 && rc != -EREMOTE) + goto out; + + if (rc != -EREMOTE) { + rc = cifs_are_all_path_components_accessible(server, xid, tcon, + cifs_sb, full_path, tcon->Flags & SMB_SHARE_IS_IN_DFS); + if (rc != 0) { + cifs_server_dbg(VFS, "cannot query dirs between root and final path, enabling CIFS_MOUNT_USE_PREFIX_PATH\n"); + cifs_sb->mnt_cifs_flags |= CIFS_MOUNT_USE_PREFIX_PATH; + rc = 0; + } + } + +out: + kfree(full_path); + return rc; +} + +#ifdef CONFIG_CIFS_DFS_UPCALL +static void set_root_ses(struct mount_ctx *mnt_ctx) +{ + if (mnt_ctx->ses) { + spin_lock(&cifs_tcp_ses_lock); + mnt_ctx->ses->ses_count++; + spin_unlock(&cifs_tcp_ses_lock); + dfs_cache_add_refsrv_session(&mnt_ctx->mount_id, mnt_ctx->ses); + } + mnt_ctx->root_ses = mnt_ctx->ses; +} + +static int is_dfs_mount(struct mount_ctx *mnt_ctx, bool *isdfs, struct dfs_cache_tgt_list *root_tl) +{ + int rc; + struct cifs_sb_info *cifs_sb = mnt_ctx->cifs_sb; + struct smb3_fs_context *ctx = mnt_ctx->fs_ctx; + + *isdfs = true; + + rc = mount_get_conns(mnt_ctx); + /* + * If called with 'nodfs' mount option, then skip DFS resolving. Otherwise unconditionally + * try to get an DFS referral (even cached) to determine whether it is an DFS mount. + * + * Skip prefix path to provide support for DFS referrals from w2k8 servers which don't seem + * to respond with PATH_NOT_COVERED to requests that include the prefix. + */ + if ((cifs_sb->mnt_cifs_flags & CIFS_MOUNT_NO_DFS) || + dfs_cache_find(mnt_ctx->xid, mnt_ctx->ses, cifs_sb->local_nls, cifs_remap(cifs_sb), + ctx->UNC + 1, NULL, root_tl)) { + if (rc) + return rc; + /* Check if it is fully accessible and then mount it */ + rc = is_path_remote(mnt_ctx); + if (!rc) + *isdfs = false; + else if (rc != -EREMOTE) + return rc; + } + return 0; +} + +static int connect_dfs_target(struct mount_ctx *mnt_ctx, const char *full_path, + const char *ref_path, struct dfs_cache_tgt_iterator *tit) +{ + int rc; + struct dfs_info3_param ref = {}; + struct cifs_sb_info *cifs_sb = mnt_ctx->cifs_sb; + char *oldmnt = cifs_sb->ctx->mount_options; + + cifs_dbg(FYI, "%s: full_path=%s ref_path=%s target=%s\n", __func__, full_path, ref_path, + dfs_cache_get_tgt_name(tit)); + + rc = dfs_cache_get_tgt_referral(ref_path, tit, &ref); + if (rc) + goto out; + + rc = expand_dfs_referral(mnt_ctx, full_path, &ref); + if (rc) + goto out; + + /* Connect to new target only if we were redirected (e.g. mount options changed) */ + if (oldmnt != cifs_sb->ctx->mount_options) { + mount_put_conns(mnt_ctx); + rc = mount_get_dfs_conns(mnt_ctx); + } + if (!rc) { + if (cifs_is_referral_server(mnt_ctx->tcon, &ref)) + set_root_ses(mnt_ctx); + rc = dfs_cache_update_tgthint(mnt_ctx->xid, mnt_ctx->root_ses, cifs_sb->local_nls, + cifs_remap(cifs_sb), ref_path, tit); + } + +out: + free_dfs_info_param(&ref); + return rc; +} + +static int connect_dfs_root(struct mount_ctx *mnt_ctx, struct dfs_cache_tgt_list *root_tl) +{ + int rc; + char *full_path; + struct cifs_sb_info *cifs_sb = mnt_ctx->cifs_sb; + struct smb3_fs_context *ctx = mnt_ctx->fs_ctx; + struct dfs_cache_tgt_iterator *tit; + + /* Put initial connections as they might be shared with other mounts. We need unique dfs + * connections per mount to properly failover, so mount_get_dfs_conns() must be used from + * now on. + */ + mount_put_conns(mnt_ctx); + mount_get_dfs_conns(mnt_ctx); + set_root_ses(mnt_ctx); + + full_path = build_unc_path_to_root(ctx, cifs_sb, true); + if (IS_ERR(full_path)) + return PTR_ERR(full_path); + + mnt_ctx->origin_fullpath = dfs_cache_canonical_path(ctx->UNC, cifs_sb->local_nls, + cifs_remap(cifs_sb)); + if (IS_ERR(mnt_ctx->origin_fullpath)) { + rc = PTR_ERR(mnt_ctx->origin_fullpath); + mnt_ctx->origin_fullpath = NULL; + goto out; + } + + /* Try all dfs root targets */ + for (rc = -ENOENT, tit = dfs_cache_get_tgt_iterator(root_tl); + tit; tit = dfs_cache_get_next_tgt(root_tl, tit)) { + rc = connect_dfs_target(mnt_ctx, full_path, mnt_ctx->origin_fullpath + 1, tit); + if (!rc) { + mnt_ctx->leaf_fullpath = kstrdup(mnt_ctx->origin_fullpath, GFP_KERNEL); + if (!mnt_ctx->leaf_fullpath) + rc = -ENOMEM; + break; + } + } + +out: + kfree(full_path); + return rc; +} + +static int __follow_dfs_link(struct mount_ctx *mnt_ctx) +{ + int rc; + struct cifs_sb_info *cifs_sb = mnt_ctx->cifs_sb; + struct smb3_fs_context *ctx = mnt_ctx->fs_ctx; + char *full_path; + struct dfs_cache_tgt_list tl = DFS_CACHE_TGT_LIST_INIT(tl); + struct dfs_cache_tgt_iterator *tit; + + full_path = build_unc_path_to_root(ctx, cifs_sb, true); + if (IS_ERR(full_path)) + return PTR_ERR(full_path); + + kfree(mnt_ctx->leaf_fullpath); + mnt_ctx->leaf_fullpath = dfs_cache_canonical_path(full_path, cifs_sb->local_nls, + cifs_remap(cifs_sb)); + if (IS_ERR(mnt_ctx->leaf_fullpath)) { + rc = PTR_ERR(mnt_ctx->leaf_fullpath); + mnt_ctx->leaf_fullpath = NULL; + goto out; + } + + /* Get referral from dfs link */ + rc = dfs_cache_find(mnt_ctx->xid, mnt_ctx->root_ses, cifs_sb->local_nls, + cifs_remap(cifs_sb), mnt_ctx->leaf_fullpath + 1, NULL, &tl); + if (rc) + goto out; + + /* Try all dfs link targets. If an I/O fails from currently connected DFS target with an + * error other than STATUS_PATH_NOT_COVERED (-EREMOTE), then retry it from other targets as + * specified in MS-DFSC "3.1.5.2 I/O Operation to Target Fails with an Error Other Than + * STATUS_PATH_NOT_COVERED." + */ + for (rc = -ENOENT, tit = dfs_cache_get_tgt_iterator(&tl); + tit; tit = dfs_cache_get_next_tgt(&tl, tit)) { + rc = connect_dfs_target(mnt_ctx, full_path, mnt_ctx->leaf_fullpath + 1, tit); + if (!rc) { + rc = is_path_remote(mnt_ctx); + if (!rc || rc == -EREMOTE) + break; + } + } + +out: + kfree(full_path); + dfs_cache_free_tgts(&tl); + return rc; +} + +static int follow_dfs_link(struct mount_ctx *mnt_ctx) +{ + int rc; + struct cifs_sb_info *cifs_sb = mnt_ctx->cifs_sb; + struct smb3_fs_context *ctx = mnt_ctx->fs_ctx; + char *full_path; + int num_links = 0; + + full_path = build_unc_path_to_root(ctx, cifs_sb, true); + if (IS_ERR(full_path)) + return PTR_ERR(full_path); + + kfree(mnt_ctx->origin_fullpath); + mnt_ctx->origin_fullpath = dfs_cache_canonical_path(full_path, cifs_sb->local_nls, + cifs_remap(cifs_sb)); + kfree(full_path); + + if (IS_ERR(mnt_ctx->origin_fullpath)) { + rc = PTR_ERR(mnt_ctx->origin_fullpath); + mnt_ctx->origin_fullpath = NULL; + return rc; + } + + do { + rc = __follow_dfs_link(mnt_ctx); + if (!rc || rc != -EREMOTE) + break; + } while (rc = -ELOOP, ++num_links < MAX_NESTED_LINKS); + + return rc; +} + +/* Set up DFS referral paths for failover */ +static void setup_server_referral_paths(struct mount_ctx *mnt_ctx) +{ + struct TCP_Server_Info *server = mnt_ctx->server; + + mutex_lock(&server->refpath_lock); + server->origin_fullpath = mnt_ctx->origin_fullpath; + server->leaf_fullpath = mnt_ctx->leaf_fullpath; + server->current_fullpath = mnt_ctx->leaf_fullpath; + mutex_unlock(&server->refpath_lock); + mnt_ctx->origin_fullpath = mnt_ctx->leaf_fullpath = NULL; +} + +int cifs_mount(struct cifs_sb_info *cifs_sb, struct smb3_fs_context *ctx) +{ + int rc; + struct mount_ctx mnt_ctx = { .cifs_sb = cifs_sb, .fs_ctx = ctx, }; + struct dfs_cache_tgt_list tl = DFS_CACHE_TGT_LIST_INIT(tl); + bool isdfs; + + rc = is_dfs_mount(&mnt_ctx, &isdfs, &tl); + if (rc) + goto error; + if (!isdfs) + goto out; + + /* proceed as DFS mount */ + uuid_gen(&mnt_ctx.mount_id); + rc = connect_dfs_root(&mnt_ctx, &tl); + dfs_cache_free_tgts(&tl); + + if (rc) + goto error; + + rc = is_path_remote(&mnt_ctx); + if (rc) + rc = follow_dfs_link(&mnt_ctx); + if (rc) + goto error; + + setup_server_referral_paths(&mnt_ctx); + /* + * After reconnecting to a different server, unique ids won't match anymore, so we disable + * serverino. This prevents dentry revalidation to think the dentry are stale (ESTALE). + */ + cifs_autodisable_serverino(cifs_sb); + /* + * Force the use of prefix path to support failover on DFS paths that resolve to targets + * that have different prefix paths. + */ + cifs_sb->mnt_cifs_flags |= CIFS_MOUNT_USE_PREFIX_PATH; + kfree(cifs_sb->prepath); + cifs_sb->prepath = ctx->prepath; + ctx->prepath = NULL; + uuid_copy(&cifs_sb->dfs_mount_id, &mnt_ctx.mount_id); + +out: + cifs_try_adding_channels(cifs_sb, mnt_ctx.ses); + rc = mount_setup_tlink(cifs_sb, mnt_ctx.ses, mnt_ctx.tcon); + if (rc) + goto error; + + free_xid(mnt_ctx.xid); + return rc; + +error: + dfs_cache_put_refsrv_sessions(&mnt_ctx.mount_id); + kfree(mnt_ctx.origin_fullpath); + kfree(mnt_ctx.leaf_fullpath); + mount_put_conns(&mnt_ctx); + return rc; +} +#else +int cifs_mount(struct cifs_sb_info *cifs_sb, struct smb3_fs_context *ctx) +{ + int rc = 0; + struct mount_ctx mnt_ctx = { .cifs_sb = cifs_sb, .fs_ctx = ctx, }; + + rc = mount_get_conns(&mnt_ctx); + if (rc) + goto error; + + if (mnt_ctx.tcon) { + rc = is_path_remote(&mnt_ctx); + if (rc == -EREMOTE) + rc = -EOPNOTSUPP; + if (rc) + goto error; + } + + rc = mount_setup_tlink(cifs_sb, mnt_ctx.ses, mnt_ctx.tcon); + if (rc) + goto error; + + free_xid(mnt_ctx.xid); + return rc; + +error: + mount_put_conns(&mnt_ctx); + return rc; +} +#endif + +/* + * Issue a TREE_CONNECT request. + */ +int +CIFSTCon(const unsigned int xid, struct cifs_ses *ses, + const char *tree, struct cifs_tcon *tcon, + const struct nls_table *nls_codepage) +{ + struct smb_hdr *smb_buffer; + struct smb_hdr *smb_buffer_response; + TCONX_REQ *pSMB; + TCONX_RSP *pSMBr; + unsigned char *bcc_ptr; + int rc = 0; + int length; + __u16 bytes_left, count; + + if (ses == NULL) + return -EIO; + + smb_buffer = cifs_buf_get(); + if (smb_buffer == NULL) + return -ENOMEM; + + smb_buffer_response = smb_buffer; + + header_assemble(smb_buffer, SMB_COM_TREE_CONNECT_ANDX, + NULL /*no tid */ , 4 /*wct */ ); + + smb_buffer->Mid = get_next_mid(ses->server); + smb_buffer->Uid = ses->Suid; + pSMB = (TCONX_REQ *) smb_buffer; + pSMBr = (TCONX_RSP *) smb_buffer_response; + + pSMB->AndXCommand = 0xFF; + pSMB->Flags = cpu_to_le16(TCON_EXTENDED_SECINFO); + bcc_ptr = &pSMB->Password[0]; + + pSMB->PasswordLength = cpu_to_le16(1); /* minimum */ + *bcc_ptr = 0; /* password is null byte */ + bcc_ptr++; /* skip password */ + /* already aligned so no need to do it below */ + + if (ses->server->sign) + smb_buffer->Flags2 |= SMBFLG2_SECURITY_SIGNATURE; + + if (ses->capabilities & CAP_STATUS32) { + smb_buffer->Flags2 |= SMBFLG2_ERR_STATUS; + } + if (ses->capabilities & CAP_DFS) { + smb_buffer->Flags2 |= SMBFLG2_DFS; + } + if (ses->capabilities & CAP_UNICODE) { + smb_buffer->Flags2 |= SMBFLG2_UNICODE; + length = + cifs_strtoUTF16((__le16 *) bcc_ptr, tree, + 6 /* max utf8 char length in bytes */ * + (/* server len*/ + 256 /* share len */), nls_codepage); + bcc_ptr += 2 * length; /* convert num 16 bit words to bytes */ + bcc_ptr += 2; /* skip trailing null */ + } else { /* ASCII */ + strcpy(bcc_ptr, tree); + bcc_ptr += strlen(tree) + 1; + } + strcpy(bcc_ptr, "?????"); + bcc_ptr += strlen("?????"); + bcc_ptr += 1; + count = bcc_ptr - &pSMB->Password[0]; + be32_add_cpu(&pSMB->hdr.smb_buf_length, count); + pSMB->ByteCount = cpu_to_le16(count); + + rc = SendReceive(xid, ses, smb_buffer, smb_buffer_response, &length, + 0); + + /* above now done in SendReceive */ + if (rc == 0) { + bool is_unicode; + + tcon->tid = smb_buffer_response->Tid; + bcc_ptr = pByteArea(smb_buffer_response); + bytes_left = get_bcc(smb_buffer_response); + length = strnlen(bcc_ptr, bytes_left - 2); + if (smb_buffer->Flags2 & SMBFLG2_UNICODE) + is_unicode = true; + else + is_unicode = false; + + + /* skip service field (NB: this field is always ASCII) */ + if (length == 3) { + if ((bcc_ptr[0] == 'I') && (bcc_ptr[1] == 'P') && + (bcc_ptr[2] == 'C')) { + cifs_dbg(FYI, "IPC connection\n"); + tcon->ipc = true; + tcon->pipe = true; + } + } else if (length == 2) { + if ((bcc_ptr[0] == 'A') && (bcc_ptr[1] == ':')) { + /* the most common case */ + cifs_dbg(FYI, "disk share connection\n"); + } + } + bcc_ptr += length + 1; + bytes_left -= (length + 1); + strscpy(tcon->tree_name, tree, sizeof(tcon->tree_name)); + + /* mostly informational -- no need to fail on error here */ + kfree(tcon->nativeFileSystem); + tcon->nativeFileSystem = cifs_strndup_from_utf16(bcc_ptr, + bytes_left, is_unicode, + nls_codepage); + + cifs_dbg(FYI, "nativeFileSystem=%s\n", tcon->nativeFileSystem); + + if ((smb_buffer_response->WordCount == 3) || + (smb_buffer_response->WordCount == 7)) + /* field is in same location */ + tcon->Flags = le16_to_cpu(pSMBr->OptionalSupport); + else + tcon->Flags = 0; + cifs_dbg(FYI, "Tcon flags: 0x%x\n", tcon->Flags); + } + + cifs_buf_release(smb_buffer); + return rc; +} + +static void delayed_free(struct rcu_head *p) +{ + struct cifs_sb_info *cifs_sb = container_of(p, struct cifs_sb_info, rcu); + + unload_nls(cifs_sb->local_nls); + smb3_cleanup_fs_context(cifs_sb->ctx); + kfree(cifs_sb); +} + +void +cifs_umount(struct cifs_sb_info *cifs_sb) +{ + struct rb_root *root = &cifs_sb->tlink_tree; + struct rb_node *node; + struct tcon_link *tlink; + + cancel_delayed_work_sync(&cifs_sb->prune_tlinks); + + spin_lock(&cifs_sb->tlink_tree_lock); + while ((node = rb_first(root))) { + tlink = rb_entry(node, struct tcon_link, tl_rbnode); + cifs_get_tlink(tlink); + clear_bit(TCON_LINK_IN_TREE, &tlink->tl_flags); + rb_erase(node, root); + + spin_unlock(&cifs_sb->tlink_tree_lock); + cifs_put_tlink(tlink); + spin_lock(&cifs_sb->tlink_tree_lock); + } + spin_unlock(&cifs_sb->tlink_tree_lock); + + kfree(cifs_sb->prepath); +#ifdef CONFIG_CIFS_DFS_UPCALL + dfs_cache_put_refsrv_sessions(&cifs_sb->dfs_mount_id); +#endif + call_rcu(&cifs_sb->rcu, delayed_free); +} + +int +cifs_negotiate_protocol(const unsigned int xid, struct cifs_ses *ses, + struct TCP_Server_Info *server) +{ + int rc = 0; + + if (!server->ops->need_neg || !server->ops->negotiate) + return -ENOSYS; + + /* only send once per connect */ + spin_lock(&server->srv_lock); + if (server->tcpStatus != CifsGood && + server->tcpStatus != CifsNew && + server->tcpStatus != CifsNeedNegotiate) { + spin_unlock(&server->srv_lock); + return -EHOSTDOWN; + } + + if (!server->ops->need_neg(server) && + server->tcpStatus == CifsGood) { + spin_unlock(&server->srv_lock); + return 0; + } + + server->tcpStatus = CifsInNegotiate; + spin_unlock(&server->srv_lock); + + rc = server->ops->negotiate(xid, ses, server); + if (rc == 0) { + spin_lock(&server->srv_lock); + if (server->tcpStatus == CifsInNegotiate) + server->tcpStatus = CifsGood; + else + rc = -EHOSTDOWN; + spin_unlock(&server->srv_lock); + } else { + spin_lock(&server->srv_lock); + if (server->tcpStatus == CifsInNegotiate) + server->tcpStatus = CifsNeedNegotiate; + spin_unlock(&server->srv_lock); + } + + return rc; +} + +int +cifs_setup_session(const unsigned int xid, struct cifs_ses *ses, + struct TCP_Server_Info *server, + struct nls_table *nls_info) +{ + int rc = -ENOSYS; + struct TCP_Server_Info *pserver = CIFS_SERVER_IS_CHAN(server) ? server->primary_server : server; + struct sockaddr_in6 *addr6 = (struct sockaddr_in6 *)&pserver->dstaddr; + struct sockaddr_in *addr = (struct sockaddr_in *)&pserver->dstaddr; + bool is_binding = false; + + spin_lock(&ses->ses_lock); + cifs_dbg(FYI, "%s: channel connect bitmap: 0x%lx\n", + __func__, ses->chans_need_reconnect); + + if (ses->ses_status != SES_GOOD && + ses->ses_status != SES_NEW && + ses->ses_status != SES_NEED_RECON) { + spin_unlock(&ses->ses_lock); + return -EHOSTDOWN; + } + + /* only send once per connect */ + spin_lock(&ses->chan_lock); + if (CIFS_ALL_CHANS_GOOD(ses)) { + if (ses->ses_status == SES_NEED_RECON) + ses->ses_status = SES_GOOD; + spin_unlock(&ses->chan_lock); + spin_unlock(&ses->ses_lock); + return 0; + } + + cifs_chan_set_in_reconnect(ses, server); + is_binding = !CIFS_ALL_CHANS_NEED_RECONNECT(ses); + spin_unlock(&ses->chan_lock); + + if (!is_binding) + ses->ses_status = SES_IN_SETUP; + spin_unlock(&ses->ses_lock); + + /* update ses ip_addr only for primary chan */ + if (server == pserver) { + if (server->dstaddr.ss_family == AF_INET6) + scnprintf(ses->ip_addr, sizeof(ses->ip_addr), "%pI6", &addr6->sin6_addr); + else + scnprintf(ses->ip_addr, sizeof(ses->ip_addr), "%pI4", &addr->sin_addr); + } + + if (!is_binding) { + ses->capabilities = server->capabilities; + if (!linuxExtEnabled) + ses->capabilities &= (~server->vals->cap_unix); + + if (ses->auth_key.response) { + cifs_dbg(FYI, "Free previous auth_key.response = %p\n", + ses->auth_key.response); + kfree_sensitive(ses->auth_key.response); + ses->auth_key.response = NULL; + ses->auth_key.len = 0; + } + } + + cifs_dbg(FYI, "Security Mode: 0x%x Capabilities: 0x%x TimeAdjust: %d\n", + server->sec_mode, server->capabilities, server->timeAdj); + + if (server->ops->sess_setup) + rc = server->ops->sess_setup(xid, ses, server, nls_info); + + if (rc) { + cifs_server_dbg(VFS, "Send error in SessSetup = %d\n", rc); + spin_lock(&ses->ses_lock); + if (ses->ses_status == SES_IN_SETUP) + ses->ses_status = SES_NEED_RECON; + spin_lock(&ses->chan_lock); + cifs_chan_clear_in_reconnect(ses, server); + spin_unlock(&ses->chan_lock); + spin_unlock(&ses->ses_lock); + } else { + spin_lock(&ses->ses_lock); + if (ses->ses_status == SES_IN_SETUP) + ses->ses_status = SES_GOOD; + spin_lock(&ses->chan_lock); + cifs_chan_clear_in_reconnect(ses, server); + cifs_chan_clear_need_reconnect(ses, server); + spin_unlock(&ses->chan_lock); + spin_unlock(&ses->ses_lock); + } + + return rc; +} + +static int +cifs_set_vol_auth(struct smb3_fs_context *ctx, struct cifs_ses *ses) +{ + ctx->sectype = ses->sectype; + + /* krb5 is special, since we don't need username or pw */ + if (ctx->sectype == Kerberos) + return 0; + + return cifs_set_cifscreds(ctx, ses); +} + +static struct cifs_tcon * +cifs_construct_tcon(struct cifs_sb_info *cifs_sb, kuid_t fsuid) +{ + int rc; + struct cifs_tcon *master_tcon = cifs_sb_master_tcon(cifs_sb); + struct cifs_ses *ses; + struct cifs_tcon *tcon = NULL; + struct smb3_fs_context *ctx; + + ctx = kzalloc(sizeof(*ctx), GFP_KERNEL); + if (ctx == NULL) + return ERR_PTR(-ENOMEM); + + ctx->local_nls = cifs_sb->local_nls; + ctx->linux_uid = fsuid; + ctx->cred_uid = fsuid; + ctx->UNC = master_tcon->tree_name; + ctx->retry = master_tcon->retry; + ctx->nocase = master_tcon->nocase; + ctx->nohandlecache = master_tcon->nohandlecache; + ctx->local_lease = master_tcon->local_lease; + ctx->no_lease = master_tcon->no_lease; + ctx->resilient = master_tcon->use_resilient; + ctx->persistent = master_tcon->use_persistent; + ctx->handle_timeout = master_tcon->handle_timeout; + ctx->no_linux_ext = !master_tcon->unix_ext; + ctx->linux_ext = master_tcon->posix_extensions; + ctx->sectype = master_tcon->ses->sectype; + ctx->sign = master_tcon->ses->sign; + ctx->seal = master_tcon->seal; + ctx->witness = master_tcon->use_witness; + + rc = cifs_set_vol_auth(ctx, master_tcon->ses); + if (rc) { + tcon = ERR_PTR(rc); + goto out; + } + + /* get a reference for the same TCP session */ + spin_lock(&cifs_tcp_ses_lock); + ++master_tcon->ses->server->srv_count; + spin_unlock(&cifs_tcp_ses_lock); + + ses = cifs_get_smb_ses(master_tcon->ses->server, ctx); + if (IS_ERR(ses)) { + tcon = (struct cifs_tcon *)ses; + cifs_put_tcp_session(master_tcon->ses->server, 0); + goto out; + } + + tcon = cifs_get_tcon(ses, ctx); + if (IS_ERR(tcon)) { + cifs_put_smb_ses(ses); + goto out; + } + +#ifdef CONFIG_CIFS_ALLOW_INSECURE_LEGACY + if (cap_unix(ses)) + reset_cifs_unix_caps(0, tcon, NULL, ctx); +#endif /* CONFIG_CIFS_ALLOW_INSECURE_LEGACY */ + +out: + kfree(ctx->username); + kfree_sensitive(ctx->password); + kfree(ctx); + + return tcon; +} + +struct cifs_tcon * +cifs_sb_master_tcon(struct cifs_sb_info *cifs_sb) +{ + return tlink_tcon(cifs_sb_master_tlink(cifs_sb)); +} + +/* find and return a tlink with given uid */ +static struct tcon_link * +tlink_rb_search(struct rb_root *root, kuid_t uid) +{ + struct rb_node *node = root->rb_node; + struct tcon_link *tlink; + + while (node) { + tlink = rb_entry(node, struct tcon_link, tl_rbnode); + + if (uid_gt(tlink->tl_uid, uid)) + node = node->rb_left; + else if (uid_lt(tlink->tl_uid, uid)) + node = node->rb_right; + else + return tlink; + } + return NULL; +} + +/* insert a tcon_link into the tree */ +static void +tlink_rb_insert(struct rb_root *root, struct tcon_link *new_tlink) +{ + struct rb_node **new = &(root->rb_node), *parent = NULL; + struct tcon_link *tlink; + + while (*new) { + tlink = rb_entry(*new, struct tcon_link, tl_rbnode); + parent = *new; + + if (uid_gt(tlink->tl_uid, new_tlink->tl_uid)) + new = &((*new)->rb_left); + else + new = &((*new)->rb_right); + } + + rb_link_node(&new_tlink->tl_rbnode, parent, new); + rb_insert_color(&new_tlink->tl_rbnode, root); +} + +/* + * Find or construct an appropriate tcon given a cifs_sb and the fsuid of the + * current task. + * + * If the superblock doesn't refer to a multiuser mount, then just return + * the master tcon for the mount. + * + * First, search the rbtree for an existing tcon for this fsuid. If one + * exists, then check to see if it's pending construction. If it is then wait + * for construction to complete. Once it's no longer pending, check to see if + * it failed and either return an error or retry construction, depending on + * the timeout. + * + * If one doesn't exist then insert a new tcon_link struct into the tree and + * try to construct a new one. + */ +struct tcon_link * +cifs_sb_tlink(struct cifs_sb_info *cifs_sb) +{ + int ret; + kuid_t fsuid = current_fsuid(); + struct tcon_link *tlink, *newtlink; + + if (!(cifs_sb->mnt_cifs_flags & CIFS_MOUNT_MULTIUSER)) + return cifs_get_tlink(cifs_sb_master_tlink(cifs_sb)); + + spin_lock(&cifs_sb->tlink_tree_lock); + tlink = tlink_rb_search(&cifs_sb->tlink_tree, fsuid); + if (tlink) + cifs_get_tlink(tlink); + spin_unlock(&cifs_sb->tlink_tree_lock); + + if (tlink == NULL) { + newtlink = kzalloc(sizeof(*tlink), GFP_KERNEL); + if (newtlink == NULL) + return ERR_PTR(-ENOMEM); + newtlink->tl_uid = fsuid; + newtlink->tl_tcon = ERR_PTR(-EACCES); + set_bit(TCON_LINK_PENDING, &newtlink->tl_flags); + set_bit(TCON_LINK_IN_TREE, &newtlink->tl_flags); + cifs_get_tlink(newtlink); + + spin_lock(&cifs_sb->tlink_tree_lock); + /* was one inserted after previous search? */ + tlink = tlink_rb_search(&cifs_sb->tlink_tree, fsuid); + if (tlink) { + cifs_get_tlink(tlink); + spin_unlock(&cifs_sb->tlink_tree_lock); + kfree(newtlink); + goto wait_for_construction; + } + tlink = newtlink; + tlink_rb_insert(&cifs_sb->tlink_tree, tlink); + spin_unlock(&cifs_sb->tlink_tree_lock); + } else { +wait_for_construction: + ret = wait_on_bit(&tlink->tl_flags, TCON_LINK_PENDING, + TASK_INTERRUPTIBLE); + if (ret) { + cifs_put_tlink(tlink); + return ERR_PTR(-ERESTARTSYS); + } + + /* if it's good, return it */ + if (!IS_ERR(tlink->tl_tcon)) + return tlink; + + /* return error if we tried this already recently */ + if (time_before(jiffies, tlink->tl_time + TLINK_ERROR_EXPIRE)) { + cifs_put_tlink(tlink); + return ERR_PTR(-EACCES); + } + + if (test_and_set_bit(TCON_LINK_PENDING, &tlink->tl_flags)) + goto wait_for_construction; + } + + tlink->tl_tcon = cifs_construct_tcon(cifs_sb, fsuid); + clear_bit(TCON_LINK_PENDING, &tlink->tl_flags); + wake_up_bit(&tlink->tl_flags, TCON_LINK_PENDING); + + if (IS_ERR(tlink->tl_tcon)) { + cifs_put_tlink(tlink); + return ERR_PTR(-EACCES); + } + + return tlink; +} + +/* + * periodic workqueue job that scans tcon_tree for a superblock and closes + * out tcons. + */ +static void +cifs_prune_tlinks(struct work_struct *work) +{ + struct cifs_sb_info *cifs_sb = container_of(work, struct cifs_sb_info, + prune_tlinks.work); + struct rb_root *root = &cifs_sb->tlink_tree; + struct rb_node *node; + struct rb_node *tmp; + struct tcon_link *tlink; + + /* + * Because we drop the spinlock in the loop in order to put the tlink + * it's not guarded against removal of links from the tree. The only + * places that remove entries from the tree are this function and + * umounts. Because this function is non-reentrant and is canceled + * before umount can proceed, this is safe. + */ + spin_lock(&cifs_sb->tlink_tree_lock); + node = rb_first(root); + while (node != NULL) { + tmp = node; + node = rb_next(tmp); + tlink = rb_entry(tmp, struct tcon_link, tl_rbnode); + + if (test_bit(TCON_LINK_MASTER, &tlink->tl_flags) || + atomic_read(&tlink->tl_count) != 0 || + time_after(tlink->tl_time + TLINK_IDLE_EXPIRE, jiffies)) + continue; + + cifs_get_tlink(tlink); + clear_bit(TCON_LINK_IN_TREE, &tlink->tl_flags); + rb_erase(tmp, root); + + spin_unlock(&cifs_sb->tlink_tree_lock); + cifs_put_tlink(tlink); + spin_lock(&cifs_sb->tlink_tree_lock); + } + spin_unlock(&cifs_sb->tlink_tree_lock); + + queue_delayed_work(cifsiod_wq, &cifs_sb->prune_tlinks, + TLINK_IDLE_EXPIRE); +} + +#ifdef CONFIG_CIFS_DFS_UPCALL +/* Update dfs referral path of superblock */ +static int update_server_fullpath(struct TCP_Server_Info *server, struct cifs_sb_info *cifs_sb, + const char *target) +{ + int rc = 0; + size_t len = strlen(target); + char *refpath, *npath; + + if (unlikely(len < 2 || *target != '\\')) + return -EINVAL; + + if (target[1] == '\\') { + len += 1; + refpath = kmalloc(len, GFP_KERNEL); + if (!refpath) + return -ENOMEM; + + scnprintf(refpath, len, "%s", target); + } else { + len += sizeof("\\"); + refpath = kmalloc(len, GFP_KERNEL); + if (!refpath) + return -ENOMEM; + + scnprintf(refpath, len, "\\%s", target); + } + + npath = dfs_cache_canonical_path(refpath, cifs_sb->local_nls, cifs_remap(cifs_sb)); + kfree(refpath); + + if (IS_ERR(npath)) { + rc = PTR_ERR(npath); + } else { + mutex_lock(&server->refpath_lock); + kfree(server->leaf_fullpath); + server->leaf_fullpath = npath; + mutex_unlock(&server->refpath_lock); + server->current_fullpath = server->leaf_fullpath; + } + return rc; +} + +static int target_share_matches_server(struct TCP_Server_Info *server, const char *tcp_host, + size_t tcp_host_len, char *share, bool *target_match) +{ + int rc = 0; + const char *dfs_host; + size_t dfs_host_len; + + *target_match = true; + extract_unc_hostname(share, &dfs_host, &dfs_host_len); + + /* Check if hostnames or addresses match */ + if (dfs_host_len != tcp_host_len || strncasecmp(dfs_host, tcp_host, dfs_host_len) != 0) { + cifs_dbg(FYI, "%s: %.*s doesn't match %.*s\n", __func__, (int)dfs_host_len, + dfs_host, (int)tcp_host_len, tcp_host); + rc = match_target_ip(server, dfs_host, dfs_host_len, target_match); + if (rc) + cifs_dbg(VFS, "%s: failed to match target ip: %d\n", __func__, rc); + } + return rc; +} + +static int __tree_connect_dfs_target(const unsigned int xid, struct cifs_tcon *tcon, + struct cifs_sb_info *cifs_sb, char *tree, bool islink, + struct dfs_cache_tgt_list *tl) +{ + int rc; + struct TCP_Server_Info *server = tcon->ses->server; + const struct smb_version_operations *ops = server->ops; + struct cifs_tcon *ipc = tcon->ses->tcon_ipc; + char *share = NULL, *prefix = NULL; + const char *tcp_host; + size_t tcp_host_len; + struct dfs_cache_tgt_iterator *tit; + bool target_match; + + extract_unc_hostname(server->hostname, &tcp_host, &tcp_host_len); + + tit = dfs_cache_get_tgt_iterator(tl); + if (!tit) { + rc = -ENOENT; + goto out; + } + + /* Try to tree connect to all dfs targets */ + for (; tit; tit = dfs_cache_get_next_tgt(tl, tit)) { + const char *target = dfs_cache_get_tgt_name(tit); + struct dfs_cache_tgt_list ntl = DFS_CACHE_TGT_LIST_INIT(ntl); + + kfree(share); + kfree(prefix); + share = prefix = NULL; + + /* Check if share matches with tcp ses */ + rc = dfs_cache_get_tgt_share(server->current_fullpath + 1, tit, &share, &prefix); + if (rc) { + cifs_dbg(VFS, "%s: failed to parse target share: %d\n", __func__, rc); + break; + } + + rc = target_share_matches_server(server, tcp_host, tcp_host_len, share, + &target_match); + if (rc) + break; + if (!target_match) { + rc = -EHOSTUNREACH; + continue; + } + + if (ipc->need_reconnect) { + scnprintf(tree, MAX_TREE_SIZE, "\\\\%s\\IPC$", server->hostname); + rc = ops->tree_connect(xid, ipc->ses, tree, ipc, cifs_sb->local_nls); + if (rc) + break; + } + + scnprintf(tree, MAX_TREE_SIZE, "\\%s", share); + if (!islink) { + rc = ops->tree_connect(xid, tcon->ses, tree, tcon, cifs_sb->local_nls); + break; + } + /* + * If no dfs referrals were returned from link target, then just do a TREE_CONNECT + * to it. Otherwise, cache the dfs referral and then mark current tcp ses for + * reconnect so either the demultiplex thread or the echo worker will reconnect to + * newly resolved target. + */ + if (dfs_cache_find(xid, tcon->ses, cifs_sb->local_nls, cifs_remap(cifs_sb), target, + NULL, &ntl)) { + rc = ops->tree_connect(xid, tcon->ses, tree, tcon, cifs_sb->local_nls); + if (rc) + continue; + rc = dfs_cache_noreq_update_tgthint(server->current_fullpath + 1, tit); + if (!rc) + rc = cifs_update_super_prepath(cifs_sb, prefix); + } else { + /* Target is another dfs share */ + rc = update_server_fullpath(server, cifs_sb, target); + dfs_cache_free_tgts(tl); + + if (!rc) { + rc = -EREMOTE; + list_replace_init(&ntl.tl_list, &tl->tl_list); + } else + dfs_cache_free_tgts(&ntl); + } + break; + } + +out: + kfree(share); + kfree(prefix); + + return rc; +} + +static int tree_connect_dfs_target(const unsigned int xid, struct cifs_tcon *tcon, + struct cifs_sb_info *cifs_sb, char *tree, bool islink, + struct dfs_cache_tgt_list *tl) +{ + int rc; + int num_links = 0; + struct TCP_Server_Info *server = tcon->ses->server; + + do { + rc = __tree_connect_dfs_target(xid, tcon, cifs_sb, tree, islink, tl); + if (!rc || rc != -EREMOTE) + break; + } while (rc = -ELOOP, ++num_links < MAX_NESTED_LINKS); + /* + * If we couldn't tree connect to any targets from last referral path, then retry from + * original referral path. + */ + if (rc && server->current_fullpath != server->origin_fullpath) { + server->current_fullpath = server->origin_fullpath; + cifs_signal_cifsd_for_reconnect(server, true); + } + + dfs_cache_free_tgts(tl); + return rc; +} + +int cifs_tree_connect(const unsigned int xid, struct cifs_tcon *tcon, const struct nls_table *nlsc) +{ + int rc; + struct TCP_Server_Info *server = tcon->ses->server; + const struct smb_version_operations *ops = server->ops; + struct super_block *sb = NULL; + struct cifs_sb_info *cifs_sb; + struct dfs_cache_tgt_list tl = DFS_CACHE_TGT_LIST_INIT(tl); + char *tree; + struct dfs_info3_param ref = {0}; + + /* only send once per connect */ + spin_lock(&tcon->tc_lock); + if (tcon->ses->ses_status != SES_GOOD || + (tcon->status != TID_NEW && + tcon->status != TID_NEED_TCON)) { + spin_unlock(&tcon->tc_lock); + return 0; + } + tcon->status = TID_IN_TCON; + spin_unlock(&tcon->tc_lock); + + tree = kzalloc(MAX_TREE_SIZE, GFP_KERNEL); + if (!tree) { + rc = -ENOMEM; + goto out; + } + + if (tcon->ipc) { + scnprintf(tree, MAX_TREE_SIZE, "\\\\%s\\IPC$", server->hostname); + rc = ops->tree_connect(xid, tcon->ses, tree, tcon, nlsc); + goto out; + } + + sb = cifs_get_tcp_super(server); + if (IS_ERR(sb)) { + rc = PTR_ERR(sb); + cifs_dbg(VFS, "%s: could not find superblock: %d\n", __func__, rc); + goto out; + } + + cifs_sb = CIFS_SB(sb); + + /* If it is not dfs or there was no cached dfs referral, then reconnect to same share */ + if (!server->current_fullpath || + dfs_cache_noreq_find(server->current_fullpath + 1, &ref, &tl)) { + rc = ops->tree_connect(xid, tcon->ses, tcon->tree_name, tcon, cifs_sb->local_nls); + goto out; + } + + rc = tree_connect_dfs_target(xid, tcon, cifs_sb, tree, ref.server_type == DFS_TYPE_LINK, + &tl); + free_dfs_info_param(&ref); + +out: + kfree(tree); + cifs_put_tcp_super(sb); + + if (rc) { + spin_lock(&tcon->tc_lock); + if (tcon->status == TID_IN_TCON) + tcon->status = TID_NEED_TCON; + spin_unlock(&tcon->tc_lock); + } else { + spin_lock(&tcon->tc_lock); + if (tcon->status == TID_IN_TCON) + tcon->status = TID_GOOD; + spin_unlock(&tcon->tc_lock); + tcon->need_reconnect = false; + } + + return rc; +} +#else +int cifs_tree_connect(const unsigned int xid, struct cifs_tcon *tcon, const struct nls_table *nlsc) +{ + int rc; + const struct smb_version_operations *ops = tcon->ses->server->ops; + + /* only send once per connect */ + spin_lock(&tcon->tc_lock); + if (tcon->ses->ses_status != SES_GOOD || + (tcon->status != TID_NEW && + tcon->status != TID_NEED_TCON)) { + spin_unlock(&tcon->tc_lock); + return 0; + } + tcon->status = TID_IN_TCON; + spin_unlock(&tcon->tc_lock); + + rc = ops->tree_connect(xid, tcon->ses, tcon->tree_name, tcon, nlsc); + if (rc) { + spin_lock(&tcon->tc_lock); + if (tcon->status == TID_IN_TCON) + tcon->status = TID_NEED_TCON; + spin_unlock(&tcon->tc_lock); + } else { + spin_lock(&tcon->tc_lock); + if (tcon->status == TID_IN_TCON) + tcon->status = TID_GOOD; + tcon->need_reconnect = false; + spin_unlock(&tcon->tc_lock); + } + + return rc; +} +#endif diff --git a/fs/smb/client/dfs_cache.c b/fs/smb/client/dfs_cache.c new file mode 100644 index 0000000000000..3bc1d3494be3a --- /dev/null +++ b/fs/smb/client/dfs_cache.c @@ -0,0 +1,1690 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * DFS referral cache routines + * + * Copyright (c) 2018-2019 Paulo Alcantara + */ + +#include +#include +#include +#include +#include +#include +#include +#include "cifsglob.h" +#include "smb2pdu.h" +#include "smb2proto.h" +#include "cifsproto.h" +#include "cifs_debug.h" +#include "cifs_unicode.h" +#include "smb2glob.h" +#include "dns_resolve.h" + +#include "dfs_cache.h" + +#define CACHE_HTABLE_SIZE 32 +#define CACHE_MAX_ENTRIES 64 +#define CACHE_MIN_TTL 120 /* 2 minutes */ + +#define IS_DFS_INTERLINK(v) (((v) & DFSREF_REFERRAL_SERVER) && !((v) & DFSREF_STORAGE_SERVER)) + +struct cache_dfs_tgt { + char *name; + int path_consumed; + struct list_head list; +}; + +struct cache_entry { + struct hlist_node hlist; + const char *path; + int hdr_flags; /* RESP_GET_DFS_REFERRAL.ReferralHeaderFlags */ + int ttl; /* DFS_REREFERRAL_V3.TimeToLive */ + int srvtype; /* DFS_REREFERRAL_V3.ServerType */ + int ref_flags; /* DFS_REREFERRAL_V3.ReferralEntryFlags */ + struct timespec64 etime; + int path_consumed; /* RESP_GET_DFS_REFERRAL.PathConsumed */ + int numtgts; + struct list_head tlist; + struct cache_dfs_tgt *tgthint; +}; + +/* List of referral server sessions per dfs mount */ +struct mount_group { + struct list_head list; + uuid_t id; + struct cifs_ses *sessions[CACHE_MAX_ENTRIES]; + int num_sessions; + spinlock_t lock; + struct list_head refresh_list; + struct kref refcount; +}; + +static struct kmem_cache *cache_slab __read_mostly; +static struct workqueue_struct *dfscache_wq __read_mostly; + +static int cache_ttl; +static DEFINE_SPINLOCK(cache_ttl_lock); + +static struct nls_table *cache_cp; + +/* + * Number of entries in the cache + */ +static atomic_t cache_count; + +static struct hlist_head cache_htable[CACHE_HTABLE_SIZE]; +static DECLARE_RWSEM(htable_rw_lock); + +static LIST_HEAD(mount_group_list); +static DEFINE_MUTEX(mount_group_list_lock); + +static void refresh_cache_worker(struct work_struct *work); + +static DECLARE_DELAYED_WORK(refresh_task, refresh_cache_worker); + +static void get_ipc_unc(const char *ref_path, char *ipc, size_t ipclen) +{ + const char *host; + size_t len; + + extract_unc_hostname(ref_path, &host, &len); + scnprintf(ipc, ipclen, "\\\\%.*s\\IPC$", (int)len, host); +} + +static struct cifs_ses *find_ipc_from_server_path(struct cifs_ses **ses, const char *path) +{ + char unc[SERVER_NAME_LENGTH + sizeof("//x/IPC$")] = {0}; + + get_ipc_unc(path, unc, sizeof(unc)); + for (; *ses; ses++) { + if (!strcasecmp(unc, (*ses)->tcon_ipc->tree_name)) + return *ses; + } + return ERR_PTR(-ENOENT); +} + +static void __mount_group_release(struct mount_group *mg) +{ + int i; + + for (i = 0; i < mg->num_sessions; i++) + cifs_put_smb_ses(mg->sessions[i]); + kfree(mg); +} + +static void mount_group_release(struct kref *kref) +{ + struct mount_group *mg = container_of(kref, struct mount_group, refcount); + + mutex_lock(&mount_group_list_lock); + list_del(&mg->list); + mutex_unlock(&mount_group_list_lock); + __mount_group_release(mg); +} + +static struct mount_group *find_mount_group_locked(const uuid_t *id) +{ + struct mount_group *mg; + + list_for_each_entry(mg, &mount_group_list, list) { + if (uuid_equal(&mg->id, id)) + return mg; + } + return ERR_PTR(-ENOENT); +} + +static struct mount_group *__get_mount_group_locked(const uuid_t *id) +{ + struct mount_group *mg; + + mg = find_mount_group_locked(id); + if (!IS_ERR(mg)) + return mg; + + mg = kmalloc(sizeof(*mg), GFP_KERNEL); + if (!mg) + return ERR_PTR(-ENOMEM); + kref_init(&mg->refcount); + uuid_copy(&mg->id, id); + mg->num_sessions = 0; + spin_lock_init(&mg->lock); + list_add(&mg->list, &mount_group_list); + return mg; +} + +static struct mount_group *get_mount_group(const uuid_t *id) +{ + struct mount_group *mg; + + mutex_lock(&mount_group_list_lock); + mg = __get_mount_group_locked(id); + if (!IS_ERR(mg)) + kref_get(&mg->refcount); + mutex_unlock(&mount_group_list_lock); + + return mg; +} + +static void free_mount_group_list(void) +{ + struct mount_group *mg, *tmp_mg; + + list_for_each_entry_safe(mg, tmp_mg, &mount_group_list, list) { + list_del_init(&mg->list); + __mount_group_release(mg); + } +} + +/** + * dfs_cache_canonical_path - get a canonical DFS path + * + * @path: DFS path + * @cp: codepage + * @remap: mapping type + * + * Return canonical path if success, otherwise error. + */ +char *dfs_cache_canonical_path(const char *path, const struct nls_table *cp, int remap) +{ + char *tmp; + int plen = 0; + char *npath; + + if (!path || strlen(path) < 3 || (*path != '\\' && *path != '/')) + return ERR_PTR(-EINVAL); + + if (unlikely(strcmp(cp->charset, cache_cp->charset))) { + tmp = (char *)cifs_strndup_to_utf16(path, strlen(path), &plen, cp, remap); + if (!tmp) { + cifs_dbg(VFS, "%s: failed to convert path to utf16\n", __func__); + return ERR_PTR(-EINVAL); + } + + npath = cifs_strndup_from_utf16(tmp, plen, true, cache_cp); + kfree(tmp); + + if (!npath) { + cifs_dbg(VFS, "%s: failed to convert path from utf16\n", __func__); + return ERR_PTR(-EINVAL); + } + } else { + npath = kstrdup(path, GFP_KERNEL); + if (!npath) + return ERR_PTR(-ENOMEM); + } + convert_delimiter(npath, '\\'); + return npath; +} + +static inline bool cache_entry_expired(const struct cache_entry *ce) +{ + struct timespec64 ts; + + ktime_get_coarse_real_ts64(&ts); + return timespec64_compare(&ts, &ce->etime) >= 0; +} + +static inline void free_tgts(struct cache_entry *ce) +{ + struct cache_dfs_tgt *t, *n; + + list_for_each_entry_safe(t, n, &ce->tlist, list) { + list_del(&t->list); + kfree(t->name); + kfree(t); + } +} + +static inline void flush_cache_ent(struct cache_entry *ce) +{ + hlist_del_init(&ce->hlist); + kfree(ce->path); + free_tgts(ce); + atomic_dec(&cache_count); + kmem_cache_free(cache_slab, ce); +} + +static void flush_cache_ents(void) +{ + int i; + + for (i = 0; i < CACHE_HTABLE_SIZE; i++) { + struct hlist_head *l = &cache_htable[i]; + struct hlist_node *n; + struct cache_entry *ce; + + hlist_for_each_entry_safe(ce, n, l, hlist) { + if (!hlist_unhashed(&ce->hlist)) + flush_cache_ent(ce); + } + } +} + +/* + * dfs cache /proc file + */ +static int dfscache_proc_show(struct seq_file *m, void *v) +{ + int i; + struct cache_entry *ce; + struct cache_dfs_tgt *t; + + seq_puts(m, "DFS cache\n---------\n"); + + down_read(&htable_rw_lock); + for (i = 0; i < CACHE_HTABLE_SIZE; i++) { + struct hlist_head *l = &cache_htable[i]; + + hlist_for_each_entry(ce, l, hlist) { + if (hlist_unhashed(&ce->hlist)) + continue; + + seq_printf(m, + "cache entry: path=%s,type=%s,ttl=%d,etime=%ld,hdr_flags=0x%x,ref_flags=0x%x,interlink=%s,path_consumed=%d,expired=%s\n", + ce->path, ce->srvtype == DFS_TYPE_ROOT ? "root" : "link", + ce->ttl, ce->etime.tv_nsec, ce->hdr_flags, ce->ref_flags, + IS_DFS_INTERLINK(ce->hdr_flags) ? "yes" : "no", + ce->path_consumed, cache_entry_expired(ce) ? "yes" : "no"); + + list_for_each_entry(t, &ce->tlist, list) { + seq_printf(m, " %s%s\n", + t->name, + ce->tgthint == t ? " (target hint)" : ""); + } + } + } + up_read(&htable_rw_lock); + + return 0; +} + +static ssize_t dfscache_proc_write(struct file *file, const char __user *buffer, + size_t count, loff_t *ppos) +{ + char c; + int rc; + + rc = get_user(c, buffer); + if (rc) + return rc; + + if (c != '0') + return -EINVAL; + + cifs_dbg(FYI, "clearing dfs cache\n"); + + down_write(&htable_rw_lock); + flush_cache_ents(); + up_write(&htable_rw_lock); + + return count; +} + +static int dfscache_proc_open(struct inode *inode, struct file *file) +{ + return single_open(file, dfscache_proc_show, NULL); +} + +const struct proc_ops dfscache_proc_ops = { + .proc_open = dfscache_proc_open, + .proc_read = seq_read, + .proc_lseek = seq_lseek, + .proc_release = single_release, + .proc_write = dfscache_proc_write, +}; + +#ifdef CONFIG_CIFS_DEBUG2 +static inline void dump_tgts(const struct cache_entry *ce) +{ + struct cache_dfs_tgt *t; + + cifs_dbg(FYI, "target list:\n"); + list_for_each_entry(t, &ce->tlist, list) { + cifs_dbg(FYI, " %s%s\n", t->name, + ce->tgthint == t ? " (target hint)" : ""); + } +} + +static inline void dump_ce(const struct cache_entry *ce) +{ + cifs_dbg(FYI, "cache entry: path=%s,type=%s,ttl=%d,etime=%ld,hdr_flags=0x%x,ref_flags=0x%x,interlink=%s,path_consumed=%d,expired=%s\n", + ce->path, + ce->srvtype == DFS_TYPE_ROOT ? "root" : "link", ce->ttl, + ce->etime.tv_nsec, + ce->hdr_flags, ce->ref_flags, + IS_DFS_INTERLINK(ce->hdr_flags) ? "yes" : "no", + ce->path_consumed, + cache_entry_expired(ce) ? "yes" : "no"); + dump_tgts(ce); +} + +static inline void dump_refs(const struct dfs_info3_param *refs, int numrefs) +{ + int i; + + cifs_dbg(FYI, "DFS referrals returned by the server:\n"); + for (i = 0; i < numrefs; i++) { + const struct dfs_info3_param *ref = &refs[i]; + + cifs_dbg(FYI, + "\n" + "flags: 0x%x\n" + "path_consumed: %d\n" + "server_type: 0x%x\n" + "ref_flag: 0x%x\n" + "path_name: %s\n" + "node_name: %s\n" + "ttl: %d (%dm)\n", + ref->flags, ref->path_consumed, ref->server_type, + ref->ref_flag, ref->path_name, ref->node_name, + ref->ttl, ref->ttl / 60); + } +} +#else +#define dump_tgts(e) +#define dump_ce(e) +#define dump_refs(r, n) +#endif + +/** + * dfs_cache_init - Initialize DFS referral cache. + * + * Return zero if initialized successfully, otherwise non-zero. + */ +int dfs_cache_init(void) +{ + int rc; + int i; + + dfscache_wq = alloc_workqueue("cifs-dfscache", WQ_FREEZABLE | WQ_UNBOUND, 1); + if (!dfscache_wq) + return -ENOMEM; + + cache_slab = kmem_cache_create("cifs_dfs_cache", + sizeof(struct cache_entry), 0, + SLAB_HWCACHE_ALIGN, NULL); + if (!cache_slab) { + rc = -ENOMEM; + goto out_destroy_wq; + } + + for (i = 0; i < CACHE_HTABLE_SIZE; i++) + INIT_HLIST_HEAD(&cache_htable[i]); + + atomic_set(&cache_count, 0); + cache_cp = load_nls("utf8"); + if (!cache_cp) + cache_cp = load_nls_default(); + + cifs_dbg(FYI, "%s: initialized DFS referral cache\n", __func__); + return 0; + +out_destroy_wq: + destroy_workqueue(dfscache_wq); + return rc; +} + +static int cache_entry_hash(const void *data, int size, unsigned int *hash) +{ + int i, clen; + const unsigned char *s = data; + wchar_t c; + unsigned int h = 0; + + for (i = 0; i < size; i += clen) { + clen = cache_cp->char2uni(&s[i], size - i, &c); + if (unlikely(clen < 0)) { + cifs_dbg(VFS, "%s: can't convert char\n", __func__); + return clen; + } + c = cifs_toupper(c); + h = jhash(&c, sizeof(c), h); + } + *hash = h % CACHE_HTABLE_SIZE; + return 0; +} + +/* Return target hint of a DFS cache entry */ +static inline char *get_tgt_name(const struct cache_entry *ce) +{ + struct cache_dfs_tgt *t = ce->tgthint; + + return t ? t->name : ERR_PTR(-ENOENT); +} + +/* Return expire time out of a new entry's TTL */ +static inline struct timespec64 get_expire_time(int ttl) +{ + struct timespec64 ts = { + .tv_sec = ttl, + .tv_nsec = 0, + }; + struct timespec64 now; + + ktime_get_coarse_real_ts64(&now); + return timespec64_add(now, ts); +} + +/* Allocate a new DFS target */ +static struct cache_dfs_tgt *alloc_target(const char *name, int path_consumed) +{ + struct cache_dfs_tgt *t; + + t = kmalloc(sizeof(*t), GFP_ATOMIC); + if (!t) + return ERR_PTR(-ENOMEM); + t->name = kstrdup(name, GFP_ATOMIC); + if (!t->name) { + kfree(t); + return ERR_PTR(-ENOMEM); + } + t->path_consumed = path_consumed; + INIT_LIST_HEAD(&t->list); + return t; +} + +/* + * Copy DFS referral information to a cache entry and conditionally update + * target hint. + */ +static int copy_ref_data(const struct dfs_info3_param *refs, int numrefs, + struct cache_entry *ce, const char *tgthint) +{ + int i; + + ce->ttl = max_t(int, refs[0].ttl, CACHE_MIN_TTL); + ce->etime = get_expire_time(ce->ttl); + ce->srvtype = refs[0].server_type; + ce->hdr_flags = refs[0].flags; + ce->ref_flags = refs[0].ref_flag; + ce->path_consumed = refs[0].path_consumed; + + for (i = 0; i < numrefs; i++) { + struct cache_dfs_tgt *t; + + t = alloc_target(refs[i].node_name, refs[i].path_consumed); + if (IS_ERR(t)) { + free_tgts(ce); + return PTR_ERR(t); + } + if (tgthint && !strcasecmp(t->name, tgthint)) { + list_add(&t->list, &ce->tlist); + tgthint = NULL; + } else { + list_add_tail(&t->list, &ce->tlist); + } + ce->numtgts++; + } + + ce->tgthint = list_first_entry_or_null(&ce->tlist, + struct cache_dfs_tgt, list); + + return 0; +} + +/* Allocate a new cache entry */ +static struct cache_entry *alloc_cache_entry(struct dfs_info3_param *refs, int numrefs) +{ + struct cache_entry *ce; + int rc; + + ce = kmem_cache_zalloc(cache_slab, GFP_KERNEL); + if (!ce) + return ERR_PTR(-ENOMEM); + + ce->path = refs[0].path_name; + refs[0].path_name = NULL; + + INIT_HLIST_NODE(&ce->hlist); + INIT_LIST_HEAD(&ce->tlist); + + rc = copy_ref_data(refs, numrefs, ce, NULL); + if (rc) { + kfree(ce->path); + kmem_cache_free(cache_slab, ce); + ce = ERR_PTR(rc); + } + return ce; +} + +static void remove_oldest_entry_locked(void) +{ + int i; + struct cache_entry *ce; + struct cache_entry *to_del = NULL; + + WARN_ON(!rwsem_is_locked(&htable_rw_lock)); + + for (i = 0; i < CACHE_HTABLE_SIZE; i++) { + struct hlist_head *l = &cache_htable[i]; + + hlist_for_each_entry(ce, l, hlist) { + if (hlist_unhashed(&ce->hlist)) + continue; + if (!to_del || timespec64_compare(&ce->etime, + &to_del->etime) < 0) + to_del = ce; + } + } + + if (!to_del) { + cifs_dbg(FYI, "%s: no entry to remove\n", __func__); + return; + } + + cifs_dbg(FYI, "%s: removing entry\n", __func__); + dump_ce(to_del); + flush_cache_ent(to_del); +} + +/* Add a new DFS cache entry */ +static int add_cache_entry_locked(struct dfs_info3_param *refs, int numrefs) +{ + int rc; + struct cache_entry *ce; + unsigned int hash; + + WARN_ON(!rwsem_is_locked(&htable_rw_lock)); + + if (atomic_read(&cache_count) >= CACHE_MAX_ENTRIES) { + cifs_dbg(FYI, "%s: reached max cache size (%d)\n", __func__, CACHE_MAX_ENTRIES); + remove_oldest_entry_locked(); + } + + rc = cache_entry_hash(refs[0].path_name, strlen(refs[0].path_name), &hash); + if (rc) + return rc; + + ce = alloc_cache_entry(refs, numrefs); + if (IS_ERR(ce)) + return PTR_ERR(ce); + + spin_lock(&cache_ttl_lock); + if (!cache_ttl) { + cache_ttl = ce->ttl; + queue_delayed_work(dfscache_wq, &refresh_task, cache_ttl * HZ); + } else { + cache_ttl = min_t(int, cache_ttl, ce->ttl); + mod_delayed_work(dfscache_wq, &refresh_task, cache_ttl * HZ); + } + spin_unlock(&cache_ttl_lock); + + hlist_add_head(&ce->hlist, &cache_htable[hash]); + dump_ce(ce); + + atomic_inc(&cache_count); + + return 0; +} + +/* Check if two DFS paths are equal. @s1 and @s2 are expected to be in @cache_cp's charset */ +static bool dfs_path_equal(const char *s1, int len1, const char *s2, int len2) +{ + int i, l1, l2; + wchar_t c1, c2; + + if (len1 != len2) + return false; + + for (i = 0; i < len1; i += l1) { + l1 = cache_cp->char2uni(&s1[i], len1 - i, &c1); + l2 = cache_cp->char2uni(&s2[i], len2 - i, &c2); + if (unlikely(l1 < 0 && l2 < 0)) { + if (s1[i] != s2[i]) + return false; + l1 = 1; + continue; + } + if (l1 != l2) + return false; + if (cifs_toupper(c1) != cifs_toupper(c2)) + return false; + } + return true; +} + +static struct cache_entry *__lookup_cache_entry(const char *path, unsigned int hash, int len) +{ + struct cache_entry *ce; + + hlist_for_each_entry(ce, &cache_htable[hash], hlist) { + if (dfs_path_equal(ce->path, strlen(ce->path), path, len)) { + dump_ce(ce); + return ce; + } + } + return ERR_PTR(-ENOENT); +} + +/* + * Find a DFS cache entry in hash table and optionally check prefix path against normalized @path. + * + * Use whole path components in the match. Must be called with htable_rw_lock held. + * + * Return ERR_PTR(-ENOENT) if the entry is not found. + */ +static struct cache_entry *lookup_cache_entry(const char *path) +{ + struct cache_entry *ce; + int cnt = 0; + const char *s = path, *e; + char sep = *s; + unsigned int hash; + int rc; + + while ((s = strchr(s, sep)) && ++cnt < 3) + s++; + + if (cnt < 3) { + rc = cache_entry_hash(path, strlen(path), &hash); + if (rc) + return ERR_PTR(rc); + return __lookup_cache_entry(path, hash, strlen(path)); + } + /* + * Handle paths that have more than two path components and are a complete prefix of the DFS + * referral request path (@path). + * + * See MS-DFSC 3.2.5.5 "Receiving a Root Referral Request or Link Referral Request". + */ + e = path + strlen(path) - 1; + while (e > s) { + int len; + + /* skip separators */ + while (e > s && *e == sep) + e--; + if (e == s) + break; + + len = e + 1 - path; + rc = cache_entry_hash(path, len, &hash); + if (rc) + return ERR_PTR(rc); + ce = __lookup_cache_entry(path, hash, len); + if (!IS_ERR(ce)) + return ce; + + /* backward until separator */ + while (e > s && *e != sep) + e--; + } + return ERR_PTR(-ENOENT); +} + +/** + * dfs_cache_destroy - destroy DFS referral cache + */ +void dfs_cache_destroy(void) +{ + cancel_delayed_work_sync(&refresh_task); + unload_nls(cache_cp); + free_mount_group_list(); + flush_cache_ents(); + kmem_cache_destroy(cache_slab); + destroy_workqueue(dfscache_wq); + + cifs_dbg(FYI, "%s: destroyed DFS referral cache\n", __func__); +} + +/* Update a cache entry with the new referral in @refs */ +static int update_cache_entry_locked(struct cache_entry *ce, const struct dfs_info3_param *refs, + int numrefs) +{ + int rc; + char *s, *th = NULL; + + WARN_ON(!rwsem_is_locked(&htable_rw_lock)); + + if (ce->tgthint) { + s = ce->tgthint->name; + th = kstrdup(s, GFP_ATOMIC); + if (!th) + return -ENOMEM; + } + + free_tgts(ce); + ce->numtgts = 0; + + rc = copy_ref_data(refs, numrefs, ce, th); + + kfree(th); + + return rc; +} + +static int get_dfs_referral(const unsigned int xid, struct cifs_ses *ses, const char *path, + struct dfs_info3_param **refs, int *numrefs) +{ + int rc; + int i; + + cifs_dbg(FYI, "%s: get an DFS referral for %s\n", __func__, path); + + *refs = NULL; + *numrefs = 0; + + if (!ses || !ses->server || !ses->server->ops->get_dfs_refer) + return -EOPNOTSUPP; + if (unlikely(!cache_cp)) + return -EINVAL; + + rc = ses->server->ops->get_dfs_refer(xid, ses, path, refs, numrefs, cache_cp, + NO_MAP_UNI_RSVD); + if (!rc) { + struct dfs_info3_param *ref = *refs; + + for (i = 0; i < *numrefs; i++) + convert_delimiter(ref[i].path_name, '\\'); + } + return rc; +} + +/* + * Find, create or update a DFS cache entry. + * + * If the entry wasn't found, it will create a new one. Or if it was found but + * expired, then it will update the entry accordingly. + * + * For interlinks, cifs_mount() and expand_dfs_referral() are supposed to + * handle them properly. + */ +static int cache_refresh_path(const unsigned int xid, struct cifs_ses *ses, const char *path) +{ + struct dfs_info3_param *refs = NULL; + struct cache_entry *ce; + int numrefs = 0; + int rc; + + cifs_dbg(FYI, "%s: search path: %s\n", __func__, path); + + down_read(&htable_rw_lock); + + ce = lookup_cache_entry(path); + if (!IS_ERR(ce) && !cache_entry_expired(ce)) { + up_read(&htable_rw_lock); + return 0; + } + /* + * Unlock shared access as we don't want to hold any locks while getting + * a new referral. The @ses used for performing the I/O could be + * reconnecting and it acquires @htable_rw_lock to look up the dfs cache + * in order to failover -- if necessary. + */ + up_read(&htable_rw_lock); + + /* + * Either the entry was not found, or it is expired. + * Request a new DFS referral in order to create or update a cache entry. + */ + rc = get_dfs_referral(xid, ses, path, &refs, &numrefs); + if (rc) + goto out; + + dump_refs(refs, numrefs); + + down_write(&htable_rw_lock); + /* Re-check as another task might have it added or refreshed already */ + ce = lookup_cache_entry(path); + if (!IS_ERR(ce)) { + if (cache_entry_expired(ce)) + rc = update_cache_entry_locked(ce, refs, numrefs); + } else { + rc = add_cache_entry_locked(refs, numrefs); + } + + up_write(&htable_rw_lock); +out: + free_dfs_info_array(refs, numrefs); + return rc; +} + +/* + * Set up a DFS referral from a given cache entry. + * + * Must be called with htable_rw_lock held. + */ +static int setup_referral(const char *path, struct cache_entry *ce, + struct dfs_info3_param *ref, const char *target) +{ + int rc; + + cifs_dbg(FYI, "%s: set up new ref\n", __func__); + + memset(ref, 0, sizeof(*ref)); + + ref->path_name = kstrdup(path, GFP_ATOMIC); + if (!ref->path_name) + return -ENOMEM; + + ref->node_name = kstrdup(target, GFP_ATOMIC); + if (!ref->node_name) { + rc = -ENOMEM; + goto err_free_path; + } + + ref->path_consumed = ce->path_consumed; + ref->ttl = ce->ttl; + ref->server_type = ce->srvtype; + ref->ref_flag = ce->ref_flags; + ref->flags = ce->hdr_flags; + + return 0; + +err_free_path: + kfree(ref->path_name); + ref->path_name = NULL; + return rc; +} + +/* Return target list of a DFS cache entry */ +static int get_targets(struct cache_entry *ce, struct dfs_cache_tgt_list *tl) +{ + int rc; + struct list_head *head = &tl->tl_list; + struct cache_dfs_tgt *t; + struct dfs_cache_tgt_iterator *it, *nit; + + memset(tl, 0, sizeof(*tl)); + INIT_LIST_HEAD(head); + + list_for_each_entry(t, &ce->tlist, list) { + it = kzalloc(sizeof(*it), GFP_ATOMIC); + if (!it) { + rc = -ENOMEM; + goto err_free_it; + } + + it->it_name = kstrdup(t->name, GFP_ATOMIC); + if (!it->it_name) { + kfree(it); + rc = -ENOMEM; + goto err_free_it; + } + it->it_path_consumed = t->path_consumed; + + if (ce->tgthint == t) + list_add(&it->it_list, head); + else + list_add_tail(&it->it_list, head); + } + + tl->tl_numtgts = ce->numtgts; + + return 0; + +err_free_it: + list_for_each_entry_safe(it, nit, head, it_list) { + list_del(&it->it_list); + kfree(it->it_name); + kfree(it); + } + return rc; +} + +/** + * dfs_cache_find - find a DFS cache entry + * + * If it doesn't find the cache entry, then it will get a DFS referral + * for @path and create a new entry. + * + * In case the cache entry exists but expired, it will get a DFS referral + * for @path and then update the respective cache entry. + * + * These parameters are passed down to the get_dfs_refer() call if it + * needs to be issued: + * @xid: syscall xid + * @ses: smb session to issue the request on + * @cp: codepage + * @remap: path character remapping type + * @path: path to lookup in DFS referral cache. + * + * @ref: when non-NULL, store single DFS referral result in it. + * @tgt_list: when non-NULL, store complete DFS target list in it. + * + * Return zero if the target was found, otherwise non-zero. + */ +int dfs_cache_find(const unsigned int xid, struct cifs_ses *ses, const struct nls_table *cp, + int remap, const char *path, struct dfs_info3_param *ref, + struct dfs_cache_tgt_list *tgt_list) +{ + int rc; + const char *npath; + struct cache_entry *ce; + + npath = dfs_cache_canonical_path(path, cp, remap); + if (IS_ERR(npath)) + return PTR_ERR(npath); + + rc = cache_refresh_path(xid, ses, npath); + if (rc) + goto out_free_path; + + down_read(&htable_rw_lock); + + ce = lookup_cache_entry(npath); + if (IS_ERR(ce)) { + up_read(&htable_rw_lock); + rc = PTR_ERR(ce); + goto out_free_path; + } + + if (ref) + rc = setup_referral(path, ce, ref, get_tgt_name(ce)); + else + rc = 0; + if (!rc && tgt_list) + rc = get_targets(ce, tgt_list); + + up_read(&htable_rw_lock); + +out_free_path: + kfree(npath); + return rc; +} + +/** + * dfs_cache_noreq_find - find a DFS cache entry without sending any requests to + * the currently connected server. + * + * NOTE: This function will neither update a cache entry in case it was + * expired, nor create a new cache entry if @path hasn't been found. It heavily + * relies on an existing cache entry. + * + * @path: canonical DFS path to lookup in the DFS referral cache. + * @ref: when non-NULL, store single DFS referral result in it. + * @tgt_list: when non-NULL, store complete DFS target list in it. + * + * Return 0 if successful. + * Return -ENOENT if the entry was not found. + * Return non-zero for other errors. + */ +int dfs_cache_noreq_find(const char *path, struct dfs_info3_param *ref, + struct dfs_cache_tgt_list *tgt_list) +{ + int rc; + struct cache_entry *ce; + + cifs_dbg(FYI, "%s: path: %s\n", __func__, path); + + down_read(&htable_rw_lock); + + ce = lookup_cache_entry(path); + if (IS_ERR(ce)) { + rc = PTR_ERR(ce); + goto out_unlock; + } + + if (ref) + rc = setup_referral(path, ce, ref, get_tgt_name(ce)); + else + rc = 0; + if (!rc && tgt_list) + rc = get_targets(ce, tgt_list); + +out_unlock: + up_read(&htable_rw_lock); + return rc; +} + +/** + * dfs_cache_update_tgthint - update target hint of a DFS cache entry + * + * If it doesn't find the cache entry, then it will get a DFS referral for @path + * and create a new entry. + * + * In case the cache entry exists but expired, it will get a DFS referral + * for @path and then update the respective cache entry. + * + * @xid: syscall id + * @ses: smb session + * @cp: codepage + * @remap: type of character remapping for paths + * @path: path to lookup in DFS referral cache + * @it: DFS target iterator + * + * Return zero if the target hint was updated successfully, otherwise non-zero. + */ +int dfs_cache_update_tgthint(const unsigned int xid, struct cifs_ses *ses, + const struct nls_table *cp, int remap, const char *path, + const struct dfs_cache_tgt_iterator *it) +{ + struct cache_dfs_tgt *t; + struct cache_entry *ce; + const char *npath; + int rc = 0; + + npath = dfs_cache_canonical_path(path, cp, remap); + if (IS_ERR(npath)) + return PTR_ERR(npath); + + cifs_dbg(FYI, "%s: update target hint - path: %s\n", __func__, npath); + + rc = cache_refresh_path(xid, ses, npath); + if (rc) + goto out_free_path; + + down_write(&htable_rw_lock); + + ce = lookup_cache_entry(npath); + if (IS_ERR(ce)) { + rc = PTR_ERR(ce); + goto out_unlock; + } + + t = ce->tgthint; + + if (likely(!strcasecmp(it->it_name, t->name))) + goto out_unlock; + + list_for_each_entry(t, &ce->tlist, list) { + if (!strcasecmp(t->name, it->it_name)) { + ce->tgthint = t; + cifs_dbg(FYI, "%s: new target hint: %s\n", __func__, + it->it_name); + break; + } + } + +out_unlock: + up_write(&htable_rw_lock); +out_free_path: + kfree(npath); + return rc; +} + +/** + * dfs_cache_noreq_update_tgthint - update target hint of a DFS cache entry + * without sending any requests to the currently connected server. + * + * NOTE: This function will neither update a cache entry in case it was + * expired, nor create a new cache entry if @path hasn't been found. It heavily + * relies on an existing cache entry. + * + * @path: canonical DFS path to lookup in DFS referral cache. + * @it: target iterator which contains the target hint to update the cache + * entry with. + * + * Return zero if the target hint was updated successfully, otherwise non-zero. + */ +int dfs_cache_noreq_update_tgthint(const char *path, const struct dfs_cache_tgt_iterator *it) +{ + int rc; + struct cache_entry *ce; + struct cache_dfs_tgt *t; + + if (!it) + return -EINVAL; + + cifs_dbg(FYI, "%s: path: %s\n", __func__, path); + + down_write(&htable_rw_lock); + + ce = lookup_cache_entry(path); + if (IS_ERR(ce)) { + rc = PTR_ERR(ce); + goto out_unlock; + } + + rc = 0; + t = ce->tgthint; + + if (unlikely(!strcasecmp(it->it_name, t->name))) + goto out_unlock; + + list_for_each_entry(t, &ce->tlist, list) { + if (!strcasecmp(t->name, it->it_name)) { + ce->tgthint = t; + cifs_dbg(FYI, "%s: new target hint: %s\n", __func__, + it->it_name); + break; + } + } + +out_unlock: + up_write(&htable_rw_lock); + return rc; +} + +/** + * dfs_cache_get_tgt_referral - returns a DFS referral (@ref) from a given + * target iterator (@it). + * + * @path: canonical DFS path to lookup in DFS referral cache. + * @it: DFS target iterator. + * @ref: DFS referral pointer to set up the gathered information. + * + * Return zero if the DFS referral was set up correctly, otherwise non-zero. + */ +int dfs_cache_get_tgt_referral(const char *path, const struct dfs_cache_tgt_iterator *it, + struct dfs_info3_param *ref) +{ + int rc; + struct cache_entry *ce; + + if (!it || !ref) + return -EINVAL; + + cifs_dbg(FYI, "%s: path: %s\n", __func__, path); + + down_read(&htable_rw_lock); + + ce = lookup_cache_entry(path); + if (IS_ERR(ce)) { + rc = PTR_ERR(ce); + goto out_unlock; + } + + cifs_dbg(FYI, "%s: target name: %s\n", __func__, it->it_name); + + rc = setup_referral(path, ce, ref, it->it_name); + +out_unlock: + up_read(&htable_rw_lock); + return rc; +} + +/** + * dfs_cache_add_refsrv_session - add SMB session of referral server + * + * @mount_id: mount group uuid to lookup. + * @ses: reference counted SMB session of referral server. + */ +void dfs_cache_add_refsrv_session(const uuid_t *mount_id, struct cifs_ses *ses) +{ + struct mount_group *mg; + + if (WARN_ON_ONCE(!mount_id || uuid_is_null(mount_id) || !ses)) + return; + + mg = get_mount_group(mount_id); + if (WARN_ON_ONCE(IS_ERR(mg))) + return; + + spin_lock(&mg->lock); + if (mg->num_sessions < ARRAY_SIZE(mg->sessions)) + mg->sessions[mg->num_sessions++] = ses; + spin_unlock(&mg->lock); + kref_put(&mg->refcount, mount_group_release); +} + +/** + * dfs_cache_put_refsrv_sessions - put all referral server sessions + * + * Put all SMB sessions from the given mount group id. + * + * @mount_id: mount group uuid to lookup. + */ +void dfs_cache_put_refsrv_sessions(const uuid_t *mount_id) +{ + struct mount_group *mg; + + if (!mount_id || uuid_is_null(mount_id)) + return; + + mutex_lock(&mount_group_list_lock); + mg = find_mount_group_locked(mount_id); + if (IS_ERR(mg)) { + mutex_unlock(&mount_group_list_lock); + return; + } + mutex_unlock(&mount_group_list_lock); + kref_put(&mg->refcount, mount_group_release); +} + +/* Extract share from DFS target and return a pointer to prefix path or NULL */ +static const char *parse_target_share(const char *target, char **share) +{ + const char *s, *seps = "/\\"; + size_t len; + + s = strpbrk(target + 1, seps); + if (!s) + return ERR_PTR(-EINVAL); + + len = strcspn(s + 1, seps); + if (!len) + return ERR_PTR(-EINVAL); + s += len; + + len = s - target + 1; + *share = kstrndup(target, len, GFP_KERNEL); + if (!*share) + return ERR_PTR(-ENOMEM); + + s = target + len; + return s + strspn(s, seps); +} + +/** + * dfs_cache_get_tgt_share - parse a DFS target + * + * @path: DFS full path + * @it: DFS target iterator. + * @share: tree name. + * @prefix: prefix path. + * + * Return zero if target was parsed correctly, otherwise non-zero. + */ +int dfs_cache_get_tgt_share(char *path, const struct dfs_cache_tgt_iterator *it, char **share, + char **prefix) +{ + char sep; + char *target_share; + char *ppath = NULL; + const char *target_ppath, *dfsref_ppath; + size_t target_pplen, dfsref_pplen; + size_t len, c; + + if (!it || !path || !share || !prefix || strlen(path) < it->it_path_consumed) + return -EINVAL; + + sep = it->it_name[0]; + if (sep != '\\' && sep != '/') + return -EINVAL; + + target_ppath = parse_target_share(it->it_name, &target_share); + if (IS_ERR(target_ppath)) + return PTR_ERR(target_ppath); + + /* point to prefix in DFS referral path */ + dfsref_ppath = path + it->it_path_consumed; + dfsref_ppath += strspn(dfsref_ppath, "/\\"); + + target_pplen = strlen(target_ppath); + dfsref_pplen = strlen(dfsref_ppath); + + /* merge prefix paths from DFS referral path and target node */ + if (target_pplen || dfsref_pplen) { + len = target_pplen + dfsref_pplen + 2; + ppath = kzalloc(len, GFP_KERNEL); + if (!ppath) { + kfree(target_share); + return -ENOMEM; + } + c = strscpy(ppath, target_ppath, len); + if (c && dfsref_pplen) + ppath[c] = sep; + strlcat(ppath, dfsref_ppath, len); + } + *share = target_share; + *prefix = ppath; + return 0; +} + +static bool target_share_equal(struct TCP_Server_Info *server, const char *s1, const char *s2) +{ + char unc[sizeof("\\\\") + SERVER_NAME_LENGTH] = {0}; + const char *host; + size_t hostlen; + char *ip = NULL; + struct sockaddr sa; + bool match; + int rc; + + if (strcasecmp(s1, s2)) + return false; + + /* + * Resolve share's hostname and check if server address matches. Otherwise just ignore it + * as we could not have upcall to resolve hostname or failed to convert ip address. + */ + match = true; + extract_unc_hostname(s1, &host, &hostlen); + scnprintf(unc, sizeof(unc), "\\\\%.*s", (int)hostlen, host); + + rc = dns_resolve_server_name_to_ip(unc, &ip, NULL); + if (rc < 0) { + cifs_dbg(FYI, "%s: could not resolve %.*s. assuming server address matches.\n", + __func__, (int)hostlen, host); + return true; + } + + if (!cifs_convert_address(&sa, ip, strlen(ip))) { + cifs_dbg(VFS, "%s: failed to convert address \'%s\'. skip address matching.\n", + __func__, ip); + } else { + cifs_server_lock(server); + match = cifs_match_ipaddr((struct sockaddr *)&server->dstaddr, &sa); + cifs_server_unlock(server); + } + + kfree(ip); + return match; +} + +/* + * Mark dfs tcon for reconnecting when the currently connected tcon does not match any of the new + * target shares in @refs. + */ +static void mark_for_reconnect_if_needed(struct cifs_tcon *tcon, struct dfs_cache_tgt_list *tl, + const struct dfs_info3_param *refs, int numrefs) +{ + struct dfs_cache_tgt_iterator *it; + int i; + + for (it = dfs_cache_get_tgt_iterator(tl); it; it = dfs_cache_get_next_tgt(tl, it)) { + for (i = 0; i < numrefs; i++) { + if (target_share_equal(tcon->ses->server, dfs_cache_get_tgt_name(it), + refs[i].node_name)) + return; + } + } + + cifs_dbg(FYI, "%s: no cached or matched targets. mark dfs share for reconnect.\n", __func__); + cifs_signal_cifsd_for_reconnect(tcon->ses->server, true); +} + +/* Refresh dfs referral of tcon and mark it for reconnect if needed */ +static int __refresh_tcon(const char *path, struct cifs_ses **sessions, struct cifs_tcon *tcon, + bool force_refresh) +{ + struct cifs_ses *ses; + struct cache_entry *ce; + struct dfs_info3_param *refs = NULL; + int numrefs = 0; + bool needs_refresh = false; + struct dfs_cache_tgt_list tl = DFS_CACHE_TGT_LIST_INIT(tl); + int rc = 0; + unsigned int xid; + + ses = find_ipc_from_server_path(sessions, path); + if (IS_ERR(ses)) { + cifs_dbg(FYI, "%s: could not find ipc session\n", __func__); + return PTR_ERR(ses); + } + + down_read(&htable_rw_lock); + ce = lookup_cache_entry(path); + needs_refresh = force_refresh || IS_ERR(ce) || cache_entry_expired(ce); + if (!IS_ERR(ce)) { + rc = get_targets(ce, &tl); + if (rc) + cifs_dbg(FYI, "%s: could not get dfs targets: %d\n", __func__, rc); + } + up_read(&htable_rw_lock); + + if (!needs_refresh) { + rc = 0; + goto out; + } + + xid = get_xid(); + rc = get_dfs_referral(xid, ses, path, &refs, &numrefs); + free_xid(xid); + + /* Create or update a cache entry with the new referral */ + if (!rc) { + dump_refs(refs, numrefs); + + down_write(&htable_rw_lock); + ce = lookup_cache_entry(path); + if (IS_ERR(ce)) + add_cache_entry_locked(refs, numrefs); + else if (force_refresh || cache_entry_expired(ce)) + update_cache_entry_locked(ce, refs, numrefs); + up_write(&htable_rw_lock); + + mark_for_reconnect_if_needed(tcon, &tl, refs, numrefs); + } + +out: + dfs_cache_free_tgts(&tl); + free_dfs_info_array(refs, numrefs); + return rc; +} + +static int refresh_tcon(struct cifs_ses **sessions, struct cifs_tcon *tcon, bool force_refresh) +{ + struct TCP_Server_Info *server = tcon->ses->server; + + mutex_lock(&server->refpath_lock); + if (server->origin_fullpath) { + if (server->leaf_fullpath && strcasecmp(server->leaf_fullpath, + server->origin_fullpath)) + __refresh_tcon(server->leaf_fullpath + 1, sessions, tcon, force_refresh); + __refresh_tcon(server->origin_fullpath + 1, sessions, tcon, force_refresh); + } + mutex_unlock(&server->refpath_lock); + + return 0; +} + +/** + * dfs_cache_remount_fs - remount a DFS share + * + * Reconfigure dfs mount by forcing a new DFS referral and if the currently cached targets do not + * match any of the new targets, mark it for reconnect. + * + * @cifs_sb: cifs superblock. + * + * Return zero if remounted, otherwise non-zero. + */ +int dfs_cache_remount_fs(struct cifs_sb_info *cifs_sb) +{ + struct cifs_tcon *tcon; + struct TCP_Server_Info *server; + struct mount_group *mg; + struct cifs_ses *sessions[CACHE_MAX_ENTRIES + 1] = {NULL}; + int rc; + + if (!cifs_sb || !cifs_sb->master_tlink) + return -EINVAL; + + tcon = cifs_sb_master_tcon(cifs_sb); + server = tcon->ses->server; + + if (!server->origin_fullpath) { + cifs_dbg(FYI, "%s: not a dfs mount\n", __func__); + return 0; + } + + if (uuid_is_null(&cifs_sb->dfs_mount_id)) { + cifs_dbg(FYI, "%s: no dfs mount group id\n", __func__); + return -EINVAL; + } + + mutex_lock(&mount_group_list_lock); + mg = find_mount_group_locked(&cifs_sb->dfs_mount_id); + if (IS_ERR(mg)) { + mutex_unlock(&mount_group_list_lock); + cifs_dbg(FYI, "%s: no ipc session for refreshing referral\n", __func__); + return PTR_ERR(mg); + } + kref_get(&mg->refcount); + mutex_unlock(&mount_group_list_lock); + + spin_lock(&mg->lock); + memcpy(&sessions, mg->sessions, mg->num_sessions * sizeof(mg->sessions[0])); + spin_unlock(&mg->lock); + + /* + * After reconnecting to a different server, unique ids won't match anymore, so we disable + * serverino. This prevents dentry revalidation to think the dentry are stale (ESTALE). + */ + cifs_autodisable_serverino(cifs_sb); + /* + * Force the use of prefix path to support failover on DFS paths that resolve to targets + * that have different prefix paths. + */ + cifs_sb->mnt_cifs_flags |= CIFS_MOUNT_USE_PREFIX_PATH; + rc = refresh_tcon(sessions, tcon, true); + + kref_put(&mg->refcount, mount_group_release); + return rc; +} + +/* + * Refresh all active dfs mounts regardless of whether they are in cache or not. + * (cache can be cleared) + */ +static void refresh_mounts(struct cifs_ses **sessions) +{ + struct TCP_Server_Info *server; + struct cifs_ses *ses; + struct cifs_tcon *tcon, *ntcon; + struct list_head tcons; + + INIT_LIST_HEAD(&tcons); + + spin_lock(&cifs_tcp_ses_lock); + list_for_each_entry(server, &cifs_tcp_ses_list, tcp_ses_list) { + spin_lock(&server->srv_lock); + if (!server->is_dfs_conn) { + spin_unlock(&server->srv_lock); + continue; + } + spin_unlock(&server->srv_lock); + + list_for_each_entry(ses, &server->smb_ses_list, smb_ses_list) { + list_for_each_entry(tcon, &ses->tcon_list, tcon_list) { + spin_lock(&tcon->tc_lock); + if (!tcon->ipc && !tcon->need_reconnect) { + tcon->tc_count++; + list_add_tail(&tcon->ulist, &tcons); + } + spin_unlock(&tcon->tc_lock); + } + } + } + spin_unlock(&cifs_tcp_ses_lock); + + list_for_each_entry_safe(tcon, ntcon, &tcons, ulist) { + struct TCP_Server_Info *server = tcon->ses->server; + + list_del_init(&tcon->ulist); + + mutex_lock(&server->refpath_lock); + if (server->origin_fullpath) { + if (server->leaf_fullpath && strcasecmp(server->leaf_fullpath, + server->origin_fullpath)) + __refresh_tcon(server->leaf_fullpath + 1, sessions, tcon, false); + __refresh_tcon(server->origin_fullpath + 1, sessions, tcon, false); + } + mutex_unlock(&server->refpath_lock); + + cifs_put_tcon(tcon); + } +} + +static void refresh_cache(struct cifs_ses **sessions) +{ + int i; + struct cifs_ses *ses; + unsigned int xid; + char *ref_paths[CACHE_MAX_ENTRIES]; + int count = 0; + struct cache_entry *ce; + + /* + * Refresh all cached entries. Get all new referrals outside critical section to avoid + * starvation while performing SMB2 IOCTL on broken or slow connections. + + * The cache entries may cover more paths than the active mounts + * (e.g. domain-based DFS referrals or multi tier DFS setups). + */ + down_read(&htable_rw_lock); + for (i = 0; i < CACHE_HTABLE_SIZE; i++) { + struct hlist_head *l = &cache_htable[i]; + + hlist_for_each_entry(ce, l, hlist) { + if (count == ARRAY_SIZE(ref_paths)) + goto out_unlock; + if (hlist_unhashed(&ce->hlist) || !cache_entry_expired(ce) || + IS_ERR(find_ipc_from_server_path(sessions, ce->path))) + continue; + ref_paths[count++] = kstrdup(ce->path, GFP_ATOMIC); + } + } + +out_unlock: + up_read(&htable_rw_lock); + + for (i = 0; i < count; i++) { + char *path = ref_paths[i]; + struct dfs_info3_param *refs = NULL; + int numrefs = 0; + int rc = 0; + + if (!path) + continue; + + ses = find_ipc_from_server_path(sessions, path); + if (IS_ERR(ses)) + goto next_referral; + + xid = get_xid(); + rc = get_dfs_referral(xid, ses, path, &refs, &numrefs); + free_xid(xid); + + if (!rc) { + down_write(&htable_rw_lock); + ce = lookup_cache_entry(path); + /* + * We need to re-check it because other tasks might have it deleted or + * updated. + */ + if (!IS_ERR(ce) && cache_entry_expired(ce)) + update_cache_entry_locked(ce, refs, numrefs); + up_write(&htable_rw_lock); + } + +next_referral: + kfree(path); + free_dfs_info_array(refs, numrefs); + } +} + +/* + * Worker that will refresh DFS cache and active mounts based on lowest TTL value from a DFS + * referral. + */ +static void refresh_cache_worker(struct work_struct *work) +{ + struct list_head mglist; + struct mount_group *mg, *tmp_mg; + struct cifs_ses *sessions[CACHE_MAX_ENTRIES + 1] = {NULL}; + int max_sessions = ARRAY_SIZE(sessions) - 1; + int i = 0, count; + + INIT_LIST_HEAD(&mglist); + + /* Get refereces of mount groups */ + mutex_lock(&mount_group_list_lock); + list_for_each_entry(mg, &mount_group_list, list) { + kref_get(&mg->refcount); + list_add(&mg->refresh_list, &mglist); + } + mutex_unlock(&mount_group_list_lock); + + /* Fill in local array with an NULL-terminated list of all referral server sessions */ + list_for_each_entry(mg, &mglist, refresh_list) { + if (i >= max_sessions) + break; + + spin_lock(&mg->lock); + if (i + mg->num_sessions > max_sessions) + count = max_sessions - i; + else + count = mg->num_sessions; + memcpy(&sessions[i], mg->sessions, count * sizeof(mg->sessions[0])); + spin_unlock(&mg->lock); + i += count; + } + + if (sessions[0]) { + /* Refresh all active mounts and cached entries */ + refresh_mounts(sessions); + refresh_cache(sessions); + } + + list_for_each_entry_safe(mg, tmp_mg, &mglist, refresh_list) { + list_del_init(&mg->refresh_list); + kref_put(&mg->refcount, mount_group_release); + } + + spin_lock(&cache_ttl_lock); + queue_delayed_work(dfscache_wq, &refresh_task, cache_ttl * HZ); + spin_unlock(&cache_ttl_lock); +} diff --git a/fs/smb/client/dfs_cache.h b/fs/smb/client/dfs_cache.h new file mode 100644 index 0000000000000..52070d1df1897 --- /dev/null +++ b/fs/smb/client/dfs_cache.h @@ -0,0 +1,97 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * DFS referral cache routines + * + * Copyright (c) 2018-2019 Paulo Alcantara + */ + +#ifndef _CIFS_DFS_CACHE_H +#define _CIFS_DFS_CACHE_H + +#include +#include +#include +#include "cifsglob.h" + +#define DFS_CACHE_TGT_LIST_INIT(var) { .tl_numtgts = 0, .tl_list = LIST_HEAD_INIT((var).tl_list), } + +struct dfs_cache_tgt_list { + int tl_numtgts; + struct list_head tl_list; +}; + +struct dfs_cache_tgt_iterator { + char *it_name; + int it_path_consumed; + struct list_head it_list; +}; + +int dfs_cache_init(void); +void dfs_cache_destroy(void); +extern const struct proc_ops dfscache_proc_ops; + +int dfs_cache_find(const unsigned int xid, struct cifs_ses *ses, const struct nls_table *cp, + int remap, const char *path, struct dfs_info3_param *ref, + struct dfs_cache_tgt_list *tgt_list); +int dfs_cache_noreq_find(const char *path, struct dfs_info3_param *ref, + struct dfs_cache_tgt_list *tgt_list); +int dfs_cache_update_tgthint(const unsigned int xid, struct cifs_ses *ses, + const struct nls_table *cp, int remap, const char *path, + const struct dfs_cache_tgt_iterator *it); +int dfs_cache_noreq_update_tgthint(const char *path, const struct dfs_cache_tgt_iterator *it); +int dfs_cache_get_tgt_referral(const char *path, const struct dfs_cache_tgt_iterator *it, + struct dfs_info3_param *ref); +int dfs_cache_get_tgt_share(char *path, const struct dfs_cache_tgt_iterator *it, char **share, + char **prefix); +void dfs_cache_put_refsrv_sessions(const uuid_t *mount_id); +void dfs_cache_add_refsrv_session(const uuid_t *mount_id, struct cifs_ses *ses); +char *dfs_cache_canonical_path(const char *path, const struct nls_table *cp, int remap); +int dfs_cache_remount_fs(struct cifs_sb_info *cifs_sb); + +static inline struct dfs_cache_tgt_iterator * +dfs_cache_get_next_tgt(struct dfs_cache_tgt_list *tl, + struct dfs_cache_tgt_iterator *it) +{ + if (!tl || list_empty(&tl->tl_list) || !it || + list_is_last(&it->it_list, &tl->tl_list)) + return NULL; + return list_next_entry(it, it_list); +} + +static inline struct dfs_cache_tgt_iterator * +dfs_cache_get_tgt_iterator(struct dfs_cache_tgt_list *tl) +{ + if (!tl) + return NULL; + return list_first_entry_or_null(&tl->tl_list, + struct dfs_cache_tgt_iterator, + it_list); +} + +static inline void dfs_cache_free_tgts(struct dfs_cache_tgt_list *tl) +{ + struct dfs_cache_tgt_iterator *it, *nit; + + if (!tl || list_empty(&tl->tl_list)) + return; + list_for_each_entry_safe(it, nit, &tl->tl_list, it_list) { + list_del(&it->it_list); + kfree(it->it_name); + kfree(it); + } + tl->tl_numtgts = 0; +} + +static inline const char * +dfs_cache_get_tgt_name(const struct dfs_cache_tgt_iterator *it) +{ + return it ? it->it_name : NULL; +} + +static inline int +dfs_cache_get_nr_tgts(const struct dfs_cache_tgt_list *tl) +{ + return tl ? tl->tl_numtgts : 0; +} + +#endif /* _CIFS_DFS_CACHE_H */ diff --git a/fs/smb/client/dir.c b/fs/smb/client/dir.c new file mode 100644 index 0000000000000..e382b794acbed --- /dev/null +++ b/fs/smb/client/dir.c @@ -0,0 +1,867 @@ +// SPDX-License-Identifier: LGPL-2.1 +/* + * + * vfs operations that deal with dentries + * + * Copyright (C) International Business Machines Corp., 2002,2009 + * Author(s): Steve French (sfrench@us.ibm.com) + * + */ +#include +#include +#include +#include +#include +#include +#include "cifsfs.h" +#include "cifspdu.h" +#include "cifsglob.h" +#include "cifsproto.h" +#include "cifs_debug.h" +#include "cifs_fs_sb.h" +#include "cifs_unicode.h" +#include "fs_context.h" +#include "cifs_ioctl.h" +#include "fscache.h" + +static void +renew_parental_timestamps(struct dentry *direntry) +{ + /* BB check if there is a way to get the kernel to do this or if we + really need this */ + do { + cifs_set_time(direntry, jiffies); + direntry = direntry->d_parent; + } while (!IS_ROOT(direntry)); +} + +char * +cifs_build_path_to_root(struct smb3_fs_context *ctx, struct cifs_sb_info *cifs_sb, + struct cifs_tcon *tcon, int add_treename) +{ + int pplen = ctx->prepath ? strlen(ctx->prepath) + 1 : 0; + int dfsplen; + char *full_path = NULL; + + /* if no prefix path, simply set path to the root of share to "" */ + if (pplen == 0) { + full_path = kzalloc(1, GFP_KERNEL); + return full_path; + } + + if (add_treename) + dfsplen = strnlen(tcon->tree_name, MAX_TREE_SIZE + 1); + else + dfsplen = 0; + + full_path = kmalloc(dfsplen + pplen + 1, GFP_KERNEL); + if (full_path == NULL) + return full_path; + + if (dfsplen) + memcpy(full_path, tcon->tree_name, dfsplen); + full_path[dfsplen] = CIFS_DIR_SEP(cifs_sb); + memcpy(full_path + dfsplen + 1, ctx->prepath, pplen); + convert_delimiter(full_path, CIFS_DIR_SEP(cifs_sb)); + return full_path; +} + +/* Note: caller must free return buffer */ +const char * +build_path_from_dentry(struct dentry *direntry, void *page) +{ + struct cifs_sb_info *cifs_sb = CIFS_SB(direntry->d_sb); + struct cifs_tcon *tcon = cifs_sb_master_tcon(cifs_sb); + bool prefix = tcon->Flags & SMB_SHARE_IS_IN_DFS; + + return build_path_from_dentry_optional_prefix(direntry, page, + prefix); +} + +char * +build_path_from_dentry_optional_prefix(struct dentry *direntry, void *page, + bool prefix) +{ + int dfsplen; + int pplen = 0; + struct cifs_sb_info *cifs_sb = CIFS_SB(direntry->d_sb); + struct cifs_tcon *tcon = cifs_sb_master_tcon(cifs_sb); + char dirsep = CIFS_DIR_SEP(cifs_sb); + char *s; + + if (unlikely(!page)) + return ERR_PTR(-ENOMEM); + + if (prefix) + dfsplen = strnlen(tcon->tree_name, MAX_TREE_SIZE + 1); + else + dfsplen = 0; + + if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_USE_PREFIX_PATH) + pplen = cifs_sb->prepath ? strlen(cifs_sb->prepath) + 1 : 0; + + s = dentry_path_raw(direntry, page, PATH_MAX); + if (IS_ERR(s)) + return s; + if (!s[1]) // for root we want "", not "/" + s++; + if (s < (char *)page + pplen + dfsplen) + return ERR_PTR(-ENAMETOOLONG); + if (pplen) { + cifs_dbg(FYI, "using cifs_sb prepath <%s>\n", cifs_sb->prepath); + s -= pplen; + memcpy(s + 1, cifs_sb->prepath, pplen - 1); + *s = '/'; + } + if (dirsep != '/') { + /* BB test paths to Windows with '/' in the midst of prepath */ + char *p; + + for (p = s; *p; p++) + if (*p == '/') + *p = dirsep; + } + if (dfsplen) { + s -= dfsplen; + memcpy(s, tcon->tree_name, dfsplen); + if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_POSIX_PATHS) { + int i; + for (i = 0; i < dfsplen; i++) { + if (s[i] == '\\') + s[i] = '/'; + } + } + } + return s; +} + +/* + * Don't allow path components longer than the server max. + * Don't allow the separator character in a path component. + * The VFS will not allow "/", but "\" is allowed by posix. + */ +static int +check_name(struct dentry *direntry, struct cifs_tcon *tcon) +{ + struct cifs_sb_info *cifs_sb = CIFS_SB(direntry->d_sb); + int i; + + if (unlikely(tcon->fsAttrInfo.MaxPathNameComponentLength && + direntry->d_name.len > + le32_to_cpu(tcon->fsAttrInfo.MaxPathNameComponentLength))) + return -ENAMETOOLONG; + + if (!(cifs_sb->mnt_cifs_flags & CIFS_MOUNT_POSIX_PATHS)) { + for (i = 0; i < direntry->d_name.len; i++) { + if (direntry->d_name.name[i] == '\\') { + cifs_dbg(FYI, "Invalid file name\n"); + return -EINVAL; + } + } + } + return 0; +} + + +/* Inode operations in similar order to how they appear in Linux file fs.h */ + +static int cifs_do_create(struct inode *inode, struct dentry *direntry, unsigned int xid, + struct tcon_link *tlink, unsigned int oflags, umode_t mode, __u32 *oplock, + struct cifs_fid *fid, struct cifs_open_info_data *buf) +{ + int rc = -ENOENT; + int create_options = CREATE_NOT_DIR; + int desired_access; + struct cifs_sb_info *cifs_sb = CIFS_SB(inode->i_sb); + struct cifs_tcon *tcon = tlink_tcon(tlink); + const char *full_path; + void *page = alloc_dentry_path(); + struct inode *newinode = NULL; + int disposition; + struct TCP_Server_Info *server = tcon->ses->server; + struct cifs_open_parms oparms; + + *oplock = 0; + if (tcon->ses->server->oplocks) + *oplock = REQ_OPLOCK; + + full_path = build_path_from_dentry(direntry, page); + if (IS_ERR(full_path)) { + free_dentry_path(page); + return PTR_ERR(full_path); + } + +#ifdef CONFIG_CIFS_ALLOW_INSECURE_LEGACY + if (tcon->unix_ext && cap_unix(tcon->ses) && !tcon->broken_posix_open && + (CIFS_UNIX_POSIX_PATH_OPS_CAP & + le64_to_cpu(tcon->fsUnixInfo.Capability))) { + rc = cifs_posix_open(full_path, &newinode, inode->i_sb, mode, + oflags, oplock, &fid->netfid, xid); + switch (rc) { + case 0: + if (newinode == NULL) { + /* query inode info */ + goto cifs_create_get_file_info; + } + + if (S_ISDIR(newinode->i_mode)) { + CIFSSMBClose(xid, tcon, fid->netfid); + iput(newinode); + rc = -EISDIR; + goto out; + } + + if (!S_ISREG(newinode->i_mode)) { + /* + * The server may allow us to open things like + * FIFOs, but the client isn't set up to deal + * with that. If it's not a regular file, just + * close it and proceed as if it were a normal + * lookup. + */ + CIFSSMBClose(xid, tcon, fid->netfid); + goto cifs_create_get_file_info; + } + /* success, no need to query */ + goto cifs_create_set_dentry; + + case -ENOENT: + goto cifs_create_get_file_info; + + case -EIO: + case -EINVAL: + /* + * EIO could indicate that (posix open) operation is not + * supported, despite what server claimed in capability + * negotiation. + * + * POSIX open in samba versions 3.3.1 and earlier could + * incorrectly fail with invalid parameter. + */ + tcon->broken_posix_open = true; + break; + + case -EREMOTE: + case -EOPNOTSUPP: + /* + * EREMOTE indicates DFS junction, which is not handled + * in posix open. If either that or op not supported + * returned, follow the normal lookup. + */ + break; + + default: + goto out; + } + /* + * fallthrough to retry, using older open call, this is case + * where server does not support this SMB level, and falsely + * claims capability (also get here for DFS case which should be + * rare for path not covered on files) + */ + } +#endif /* CONFIG_CIFS_ALLOW_INSECURE_LEGACY */ + + desired_access = 0; + if (OPEN_FMODE(oflags) & FMODE_READ) + desired_access |= GENERIC_READ; /* is this too little? */ + if (OPEN_FMODE(oflags) & FMODE_WRITE) + desired_access |= GENERIC_WRITE; + + disposition = FILE_OVERWRITE_IF; + if ((oflags & (O_CREAT | O_EXCL)) == (O_CREAT | O_EXCL)) + disposition = FILE_CREATE; + else if ((oflags & (O_CREAT | O_TRUNC)) == (O_CREAT | O_TRUNC)) + disposition = FILE_OVERWRITE_IF; + else if ((oflags & O_CREAT) == O_CREAT) + disposition = FILE_OPEN_IF; + else + cifs_dbg(FYI, "Create flag not set in create function\n"); + + /* + * BB add processing to set equivalent of mode - e.g. via CreateX with + * ACLs + */ + + if (!server->ops->open) { + rc = -ENOSYS; + goto out; + } + + /* + * if we're not using unix extensions, see if we need to set + * ATTR_READONLY on the create call + */ + if (!tcon->unix_ext && (mode & S_IWUGO) == 0) + create_options |= CREATE_OPTION_READONLY; + + oparms = (struct cifs_open_parms) { + .tcon = tcon, + .cifs_sb = cifs_sb, + .desired_access = desired_access, + .create_options = cifs_create_options(cifs_sb, create_options), + .disposition = disposition, + .path = full_path, + .fid = fid, + .mode = mode, + }; + rc = server->ops->open(xid, &oparms, oplock, buf); + if (rc) { + cifs_dbg(FYI, "cifs_create returned 0x%x\n", rc); + goto out; + } + +#ifdef CONFIG_CIFS_ALLOW_INSECURE_LEGACY + /* + * If Open reported that we actually created a file then we now have to + * set the mode if possible. + */ + if ((tcon->unix_ext) && (*oplock & CIFS_CREATE_ACTION)) { + struct cifs_unix_set_info_args args = { + .mode = mode, + .ctime = NO_CHANGE_64, + .atime = NO_CHANGE_64, + .mtime = NO_CHANGE_64, + .device = 0, + }; + + if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_SET_UID) { + args.uid = current_fsuid(); + if (inode->i_mode & S_ISGID) + args.gid = inode->i_gid; + else + args.gid = current_fsgid(); + } else { + args.uid = INVALID_UID; /* no change */ + args.gid = INVALID_GID; /* no change */ + } + CIFSSMBUnixSetFileInfo(xid, tcon, &args, fid->netfid, + current->tgid); + } else { + /* + * BB implement mode setting via Windows security + * descriptors e.g. + */ + /* CIFSSMBWinSetPerms(xid,tcon,path,mode,-1,-1,nls);*/ + + /* Could set r/o dos attribute if mode & 0222 == 0 */ + } + +cifs_create_get_file_info: + /* server might mask mode so we have to query for it */ + if (tcon->unix_ext) + rc = cifs_get_inode_info_unix(&newinode, full_path, inode->i_sb, + xid); + else { +#else + { +#endif /* CONFIG_CIFS_ALLOW_INSECURE_LEGACY */ + /* TODO: Add support for calling POSIX query info here, but passing in fid */ + rc = cifs_get_inode_info(&newinode, full_path, buf, inode->i_sb, xid, fid); + if (newinode) { + if (server->ops->set_lease_key) + server->ops->set_lease_key(newinode, fid); + if ((*oplock & CIFS_CREATE_ACTION) && S_ISREG(newinode->i_mode)) { + if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_DYNPERM) + newinode->i_mode = mode; + if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_SET_UID) { + newinode->i_uid = current_fsuid(); + if (inode->i_mode & S_ISGID) + newinode->i_gid = inode->i_gid; + else + newinode->i_gid = current_fsgid(); + } + } + } + } + +#ifdef CONFIG_CIFS_ALLOW_INSECURE_LEGACY +cifs_create_set_dentry: +#endif /* CONFIG_CIFS_ALLOW_INSECURE_LEGACY */ + if (rc != 0) { + cifs_dbg(FYI, "Create worked, get_inode_info failed rc = %d\n", + rc); + goto out_err; + } + + if (newinode) + if (S_ISDIR(newinode->i_mode)) { + rc = -EISDIR; + goto out_err; + } + + d_drop(direntry); + d_add(direntry, newinode); + +out: + free_dentry_path(page); + return rc; + +out_err: + if (server->ops->close) + server->ops->close(xid, tcon, fid); + if (newinode) + iput(newinode); + goto out; +} + +int +cifs_atomic_open(struct inode *inode, struct dentry *direntry, + struct file *file, unsigned oflags, umode_t mode) +{ + int rc; + unsigned int xid; + struct tcon_link *tlink; + struct cifs_tcon *tcon; + struct TCP_Server_Info *server; + struct cifs_fid fid = {}; + struct cifs_pending_open open; + __u32 oplock; + struct cifsFileInfo *file_info; + struct cifs_open_info_data buf = {}; + + if (unlikely(cifs_forced_shutdown(CIFS_SB(inode->i_sb)))) + return -EIO; + + /* + * Posix open is only called (at lookup time) for file create now. For + * opens (rather than creates), because we do not know if it is a file + * or directory yet, and current Samba no longer allows us to do posix + * open on dirs, we could end up wasting an open call on what turns out + * to be a dir. For file opens, we wait to call posix open till + * cifs_open. It could be added to atomic_open in the future but the + * performance tradeoff of the extra network request when EISDIR or + * EACCES is returned would have to be weighed against the 50% reduction + * in network traffic in the other paths. + */ + if (!(oflags & O_CREAT)) { + struct dentry *res; + + /* + * Check for hashed negative dentry. We have already revalidated + * the dentry and it is fine. No need to perform another lookup. + */ + if (!d_in_lookup(direntry)) + return -ENOENT; + + res = cifs_lookup(inode, direntry, 0); + if (IS_ERR(res)) + return PTR_ERR(res); + + return finish_no_open(file, res); + } + + xid = get_xid(); + + cifs_dbg(FYI, "parent inode = 0x%p name is: %pd and dentry = 0x%p\n", + inode, direntry, direntry); + + tlink = cifs_sb_tlink(CIFS_SB(inode->i_sb)); + if (IS_ERR(tlink)) { + rc = PTR_ERR(tlink); + goto out_free_xid; + } + + tcon = tlink_tcon(tlink); + + rc = check_name(direntry, tcon); + if (rc) + goto out; + + server = tcon->ses->server; + + if (server->ops->new_lease_key) + server->ops->new_lease_key(&fid); + + cifs_add_pending_open(&fid, tlink, &open); + + rc = cifs_do_create(inode, direntry, xid, tlink, oflags, mode, + &oplock, &fid, &buf); + if (rc) { + cifs_del_pending_open(&open); + goto out; + } + + if ((oflags & (O_CREAT | O_EXCL)) == (O_CREAT | O_EXCL)) + file->f_mode |= FMODE_CREATED; + + rc = finish_open(file, direntry, generic_file_open); + if (rc) { + if (server->ops->close) + server->ops->close(xid, tcon, &fid); + cifs_del_pending_open(&open); + goto out; + } + + if (file->f_flags & O_DIRECT && + CIFS_SB(inode->i_sb)->mnt_cifs_flags & CIFS_MOUNT_STRICT_IO) { + if (CIFS_SB(inode->i_sb)->mnt_cifs_flags & CIFS_MOUNT_NO_BRL) + file->f_op = &cifs_file_direct_nobrl_ops; + else + file->f_op = &cifs_file_direct_ops; + } + + file_info = cifs_new_fileinfo(&fid, file, tlink, oplock, buf.symlink_target); + if (file_info == NULL) { + if (server->ops->close) + server->ops->close(xid, tcon, &fid); + cifs_del_pending_open(&open); + rc = -ENOMEM; + goto out; + } + + fscache_use_cookie(cifs_inode_cookie(file_inode(file)), + file->f_mode & FMODE_WRITE); + +out: + cifs_put_tlink(tlink); +out_free_xid: + free_xid(xid); + cifs_free_open_info(&buf); + return rc; +} + +int cifs_create(struct user_namespace *mnt_userns, struct inode *inode, + struct dentry *direntry, umode_t mode, bool excl) +{ + int rc; + unsigned int xid = get_xid(); + /* + * BB below access is probably too much for mknod to request + * but we have to do query and setpathinfo so requesting + * less could fail (unless we want to request getatr and setatr + * permissions (only). At least for POSIX we do not have to + * request so much. + */ + unsigned oflags = O_EXCL | O_CREAT | O_RDWR; + struct tcon_link *tlink; + struct cifs_tcon *tcon; + struct TCP_Server_Info *server; + struct cifs_fid fid; + __u32 oplock; + struct cifs_open_info_data buf = {}; + + cifs_dbg(FYI, "cifs_create parent inode = 0x%p name is: %pd and dentry = 0x%p\n", + inode, direntry, direntry); + + if (unlikely(cifs_forced_shutdown(CIFS_SB(inode->i_sb)))) { + rc = -EIO; + goto out_free_xid; + } + + tlink = cifs_sb_tlink(CIFS_SB(inode->i_sb)); + rc = PTR_ERR(tlink); + if (IS_ERR(tlink)) + goto out_free_xid; + + tcon = tlink_tcon(tlink); + server = tcon->ses->server; + + if (server->ops->new_lease_key) + server->ops->new_lease_key(&fid); + + rc = cifs_do_create(inode, direntry, xid, tlink, oflags, mode, &oplock, &fid, &buf); + if (!rc && server->ops->close) + server->ops->close(xid, tcon, &fid); + + cifs_free_open_info(&buf); + cifs_put_tlink(tlink); +out_free_xid: + free_xid(xid); + return rc; +} + +int cifs_mknod(struct user_namespace *mnt_userns, struct inode *inode, + struct dentry *direntry, umode_t mode, dev_t device_number) +{ + int rc = -EPERM; + unsigned int xid; + struct cifs_sb_info *cifs_sb; + struct tcon_link *tlink; + struct cifs_tcon *tcon; + const char *full_path; + void *page; + + if (!old_valid_dev(device_number)) + return -EINVAL; + + cifs_sb = CIFS_SB(inode->i_sb); + if (unlikely(cifs_forced_shutdown(cifs_sb))) + return -EIO; + + tlink = cifs_sb_tlink(cifs_sb); + if (IS_ERR(tlink)) + return PTR_ERR(tlink); + + page = alloc_dentry_path(); + tcon = tlink_tcon(tlink); + xid = get_xid(); + + full_path = build_path_from_dentry(direntry, page); + if (IS_ERR(full_path)) { + rc = PTR_ERR(full_path); + goto mknod_out; + } + + rc = tcon->ses->server->ops->make_node(xid, inode, direntry, tcon, + full_path, mode, + device_number); + +mknod_out: + free_dentry_path(page); + free_xid(xid); + cifs_put_tlink(tlink); + return rc; +} + +struct dentry * +cifs_lookup(struct inode *parent_dir_inode, struct dentry *direntry, + unsigned int flags) +{ + unsigned int xid; + int rc = 0; /* to get around spurious gcc warning, set to zero here */ + struct cifs_sb_info *cifs_sb; + struct tcon_link *tlink; + struct cifs_tcon *pTcon; + struct inode *newInode = NULL; + const char *full_path; + void *page; + int retry_count = 0; + + xid = get_xid(); + + cifs_dbg(FYI, "parent inode = 0x%p name is: %pd and dentry = 0x%p\n", + parent_dir_inode, direntry, direntry); + + /* check whether path exists */ + + cifs_sb = CIFS_SB(parent_dir_inode->i_sb); + tlink = cifs_sb_tlink(cifs_sb); + if (IS_ERR(tlink)) { + free_xid(xid); + return ERR_CAST(tlink); + } + pTcon = tlink_tcon(tlink); + + rc = check_name(direntry, pTcon); + if (unlikely(rc)) { + cifs_put_tlink(tlink); + free_xid(xid); + return ERR_PTR(rc); + } + + /* can not grab the rename sem here since it would + deadlock in the cases (beginning of sys_rename itself) + in which we already have the sb rename sem */ + page = alloc_dentry_path(); + full_path = build_path_from_dentry(direntry, page); + if (IS_ERR(full_path)) { + cifs_put_tlink(tlink); + free_xid(xid); + free_dentry_path(page); + return ERR_CAST(full_path); + } + + if (d_really_is_positive(direntry)) { + cifs_dbg(FYI, "non-NULL inode in lookup\n"); + } else { + cifs_dbg(FYI, "NULL inode in lookup\n"); + } + cifs_dbg(FYI, "Full path: %s inode = 0x%p\n", + full_path, d_inode(direntry)); + +again: + if (pTcon->posix_extensions) + rc = smb311_posix_get_inode_info(&newInode, full_path, parent_dir_inode->i_sb, xid); + else if (pTcon->unix_ext) { + rc = cifs_get_inode_info_unix(&newInode, full_path, + parent_dir_inode->i_sb, xid); + } else { + rc = cifs_get_inode_info(&newInode, full_path, NULL, + parent_dir_inode->i_sb, xid, NULL); + } + + if (rc == 0) { + /* since paths are not looked up by component - the parent + directories are presumed to be good here */ + renew_parental_timestamps(direntry); + } else if (rc == -EAGAIN && retry_count++ < 10) { + goto again; + } else if (rc == -ENOENT) { + cifs_set_time(direntry, jiffies); + newInode = NULL; + } else { + if (rc != -EACCES) { + cifs_dbg(FYI, "Unexpected lookup error %d\n", rc); + /* We special case check for Access Denied - since that + is a common return code */ + } + newInode = ERR_PTR(rc); + } + free_dentry_path(page); + cifs_put_tlink(tlink); + free_xid(xid); + return d_splice_alias(newInode, direntry); +} + +static int +cifs_d_revalidate(struct dentry *direntry, unsigned int flags) +{ + struct inode *inode; + int rc; + + if (flags & LOOKUP_RCU) + return -ECHILD; + + if (d_really_is_positive(direntry)) { + inode = d_inode(direntry); + if ((flags & LOOKUP_REVAL) && !CIFS_CACHE_READ(CIFS_I(inode))) + CIFS_I(inode)->time = 0; /* force reval */ + + rc = cifs_revalidate_dentry(direntry); + if (rc) { + cifs_dbg(FYI, "cifs_revalidate_dentry failed with rc=%d", rc); + switch (rc) { + case -ENOENT: + case -ESTALE: + /* + * Those errors mean the dentry is invalid + * (file was deleted or recreated) + */ + return 0; + default: + /* + * Otherwise some unexpected error happened + * report it as-is to VFS layer + */ + return rc; + } + } + else { + /* + * If the inode wasn't known to be a dfs entry when + * the dentry was instantiated, such as when created + * via ->readdir(), it needs to be set now since the + * attributes will have been updated by + * cifs_revalidate_dentry(). + */ + if (IS_AUTOMOUNT(inode) && + !(direntry->d_flags & DCACHE_NEED_AUTOMOUNT)) { + spin_lock(&direntry->d_lock); + direntry->d_flags |= DCACHE_NEED_AUTOMOUNT; + spin_unlock(&direntry->d_lock); + } + + return 1; + } + } + + /* + * This may be nfsd (or something), anyway, we can't see the + * intent of this. So, since this can be for creation, drop it. + */ + if (!flags) + return 0; + + /* + * Drop the negative dentry, in order to make sure to use the + * case sensitive name which is specified by user if this is + * for creation. + */ + if (flags & (LOOKUP_CREATE | LOOKUP_RENAME_TARGET)) + return 0; + + if (time_after(jiffies, cifs_get_time(direntry) + HZ) || !lookupCacheEnabled) + return 0; + + return 1; +} + +/* static int cifs_d_delete(struct dentry *direntry) +{ + int rc = 0; + + cifs_dbg(FYI, "In cifs d_delete, name = %pd\n", direntry); + + return rc; +} */ + +const struct dentry_operations cifs_dentry_ops = { + .d_revalidate = cifs_d_revalidate, + .d_automount = cifs_dfs_d_automount, +/* d_delete: cifs_d_delete, */ /* not needed except for debugging */ +}; + +static int cifs_ci_hash(const struct dentry *dentry, struct qstr *q) +{ + struct nls_table *codepage = CIFS_SB(dentry->d_sb)->local_nls; + unsigned long hash; + wchar_t c; + int i, charlen; + + hash = init_name_hash(dentry); + for (i = 0; i < q->len; i += charlen) { + charlen = codepage->char2uni(&q->name[i], q->len - i, &c); + /* error out if we can't convert the character */ + if (unlikely(charlen < 0)) + return charlen; + hash = partial_name_hash(cifs_toupper(c), hash); + } + q->hash = end_name_hash(hash); + + return 0; +} + +static int cifs_ci_compare(const struct dentry *dentry, + unsigned int len, const char *str, const struct qstr *name) +{ + struct nls_table *codepage = CIFS_SB(dentry->d_sb)->local_nls; + wchar_t c1, c2; + int i, l1, l2; + + /* + * We make the assumption here that uppercase characters in the local + * codepage are always the same length as their lowercase counterparts. + * + * If that's ever not the case, then this will fail to match it. + */ + if (name->len != len) + return 1; + + for (i = 0; i < len; i += l1) { + /* Convert characters in both strings to UTF-16. */ + l1 = codepage->char2uni(&str[i], len - i, &c1); + l2 = codepage->char2uni(&name->name[i], name->len - i, &c2); + + /* + * If we can't convert either character, just declare it to + * be 1 byte long and compare the original byte. + */ + if (unlikely(l1 < 0 && l2 < 0)) { + if (str[i] != name->name[i]) + return 1; + l1 = 1; + continue; + } + + /* + * Here, we again ass|u|me that upper/lowercase versions of + * a character are the same length in the local NLS. + */ + if (l1 != l2) + return 1; + + /* Now compare uppercase versions of these characters */ + if (cifs_toupper(c1) != cifs_toupper(c2)) + return 1; + } + + return 0; +} + +const struct dentry_operations cifs_ci_dentry_ops = { + .d_revalidate = cifs_d_revalidate, + .d_hash = cifs_ci_hash, + .d_compare = cifs_ci_compare, + .d_automount = cifs_dfs_d_automount, +}; diff --git a/fs/smb/client/dns_resolve.c b/fs/smb/client/dns_resolve.c new file mode 100644 index 0000000000000..0458d28d71aa6 --- /dev/null +++ b/fs/smb/client/dns_resolve.c @@ -0,0 +1,89 @@ +// SPDX-License-Identifier: LGPL-2.1 +/* + * + * Copyright (c) 2007 Igor Mammedov + * Author(s): Igor Mammedov (niallain@gmail.com) + * Steve French (sfrench@us.ibm.com) + * Wang Lei (wang840925@gmail.com) + * David Howells (dhowells@redhat.com) + * + * Contains the CIFS DFS upcall routines used for hostname to + * IP address translation. + * + */ + +#include +#include +#include "dns_resolve.h" +#include "cifsglob.h" +#include "cifsproto.h" +#include "cifs_debug.h" + +/** + * dns_resolve_server_name_to_ip - Resolve UNC server name to ip address. + * @unc: UNC path specifying the server (with '/' as delimiter) + * @ip_addr: Where to return the IP address. + * @expiry: Where to return the expiry time for the dns record. + * + * The IP address will be returned in string form, and the caller is + * responsible for freeing it. + * + * Returns length of result on success, -ve on error. + */ +int +dns_resolve_server_name_to_ip(const char *unc, char **ip_addr, time64_t *expiry) +{ + struct sockaddr_storage ss; + const char *hostname, *sep; + char *name; + int len, rc; + + if (!ip_addr || !unc) + return -EINVAL; + + len = strlen(unc); + if (len < 3) { + cifs_dbg(FYI, "%s: unc is too short: %s\n", __func__, unc); + return -EINVAL; + } + + /* Discount leading slashes for cifs */ + len -= 2; + hostname = unc + 2; + + /* Search for server name delimiter */ + sep = memchr(hostname, '/', len); + if (sep) + len = sep - hostname; + else + cifs_dbg(FYI, "%s: probably server name is whole unc: %s\n", + __func__, unc); + + /* Try to interpret hostname as an IPv4 or IPv6 address */ + rc = cifs_convert_address((struct sockaddr *)&ss, hostname, len); + if (rc > 0) + goto name_is_IP_address; + + /* Perform the upcall */ + rc = dns_query(current->nsproxy->net_ns, NULL, hostname, len, + NULL, ip_addr, expiry, false); + if (rc < 0) + cifs_dbg(FYI, "%s: unable to resolve: %*.*s\n", + __func__, len, len, hostname); + else + cifs_dbg(FYI, "%s: resolved: %*.*s to %s expiry %llu\n", + __func__, len, len, hostname, *ip_addr, + expiry ? (*expiry) : 0); + return rc; + +name_is_IP_address: + name = kmalloc(len + 1, GFP_KERNEL); + if (!name) + return -ENOMEM; + memcpy(name, hostname, len); + name[len] = 0; + cifs_dbg(FYI, "%s: unc is IP, skipping dns upcall: %s\n", + __func__, name); + *ip_addr = name; + return 0; +} diff --git a/fs/smb/client/dns_resolve.h b/fs/smb/client/dns_resolve.h new file mode 100644 index 0000000000000..afc0df381246b --- /dev/null +++ b/fs/smb/client/dns_resolve.h @@ -0,0 +1,18 @@ +/* SPDX-License-Identifier: LGPL-2.1 */ +/* + * DNS Resolver upcall management for CIFS DFS + * Handles host name to IP address resolution + * + * Copyright (c) International Business Machines Corp., 2008 + * Author(s): Steve French (sfrench@us.ibm.com) + * + */ + +#ifndef _DNS_RESOLVE_H +#define _DNS_RESOLVE_H + +#ifdef __KERNEL__ +extern int dns_resolve_server_name_to_ip(const char *unc, char **ip_addr, time64_t *expiry); +#endif /* KERNEL */ + +#endif /* _DNS_RESOLVE_H */ diff --git a/fs/smb/client/export.c b/fs/smb/client/export.c new file mode 100644 index 0000000000000..37c28415df1e0 --- /dev/null +++ b/fs/smb/client/export.c @@ -0,0 +1,54 @@ +// SPDX-License-Identifier: LGPL-2.1 +/* + * + * Copyright (C) International Business Machines Corp., 2007 + * Author(s): Steve French (sfrench@us.ibm.com) + * + * Common Internet FileSystem (CIFS) client + * + * Operations related to support for exporting files via NFSD + * + */ + + /* + * See Documentation/filesystems/nfs/exporting.rst + * and examples in fs/exportfs + * + * Since cifs is a network file system, an "fsid" must be included for + * any nfs exports file entries which refer to cifs paths. In addition + * the cifs mount must be mounted with the "serverino" option (ie use stable + * server inode numbers instead of locally generated temporary ones). + * Although cifs inodes do not use generation numbers (have generation number + * of zero) - the inode number alone should be good enough for simple cases + * in which users want to export cifs shares with NFS. The decode and encode + * could be improved by using a new routine which expects 64 bit inode numbers + * instead of the default 32 bit routines in fs/exportfs + * + */ + +#include +#include +#include "cifsglob.h" +#include "cifs_debug.h" +#include "cifsfs.h" + +#ifdef CONFIG_CIFS_NFSD_EXPORT +static struct dentry *cifs_get_parent(struct dentry *dentry) +{ + /* BB need to add code here eventually to enable export via NFSD */ + cifs_dbg(FYI, "get parent for %p\n", dentry); + return ERR_PTR(-EACCES); +} + +const struct export_operations cifs_export_ops = { + .get_parent = cifs_get_parent, +/* Following five export operations are unneeded so far and can default: + .get_dentry = + .get_name = + .find_exported_dentry = + .decode_fh = + .encode_fs = */ +}; + +#endif /* CONFIG_CIFS_NFSD_EXPORT */ + diff --git a/fs/smb/client/file.c b/fs/smb/client/file.c new file mode 100644 index 0000000000000..87dcffece7623 --- /dev/null +++ b/fs/smb/client/file.c @@ -0,0 +1,5290 @@ +// SPDX-License-Identifier: LGPL-2.1 +/* + * + * vfs operations that deal with files + * + * Copyright (C) International Business Machines Corp., 2002,2010 + * Author(s): Steve French (sfrench@us.ibm.com) + * Jeremy Allison (jra@samba.org) + * + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "cifsfs.h" +#include "cifspdu.h" +#include "cifsglob.h" +#include "cifsproto.h" +#include "smb2proto.h" +#include "cifs_unicode.h" +#include "cifs_debug.h" +#include "cifs_fs_sb.h" +#include "fscache.h" +#include "smbdirect.h" +#include "fs_context.h" +#include "cifs_ioctl.h" +#include "cached_dir.h" + +/* + * Mark as invalid, all open files on tree connections since they + * were closed when session to server was lost. + */ +void +cifs_mark_open_files_invalid(struct cifs_tcon *tcon) +{ + struct cifsFileInfo *open_file = NULL; + struct list_head *tmp; + struct list_head *tmp1; + + /* only send once per connect */ + spin_lock(&tcon->ses->ses_lock); + if ((tcon->ses->ses_status != SES_GOOD) || (tcon->status != TID_NEED_RECON)) { + spin_unlock(&tcon->ses->ses_lock); + return; + } + tcon->status = TID_IN_FILES_INVALIDATE; + spin_unlock(&tcon->ses->ses_lock); + + /* list all files open on tree connection and mark them invalid */ + spin_lock(&tcon->open_file_lock); + list_for_each_safe(tmp, tmp1, &tcon->openFileList) { + open_file = list_entry(tmp, struct cifsFileInfo, tlist); + open_file->invalidHandle = true; + open_file->oplock_break_cancelled = true; + } + spin_unlock(&tcon->open_file_lock); + + invalidate_all_cached_dirs(tcon); + spin_lock(&tcon->tc_lock); + if (tcon->status == TID_IN_FILES_INVALIDATE) + tcon->status = TID_NEED_TCON; + spin_unlock(&tcon->tc_lock); + + /* + * BB Add call to invalidate_inodes(sb) for all superblocks mounted + * to this tcon. + */ +} + +static inline int cifs_convert_flags(unsigned int flags) +{ + if ((flags & O_ACCMODE) == O_RDONLY) + return GENERIC_READ; + else if ((flags & O_ACCMODE) == O_WRONLY) + return GENERIC_WRITE; + else if ((flags & O_ACCMODE) == O_RDWR) { + /* GENERIC_ALL is too much permission to request + can cause unnecessary access denied on create */ + /* return GENERIC_ALL; */ + return (GENERIC_READ | GENERIC_WRITE); + } + + return (READ_CONTROL | FILE_WRITE_ATTRIBUTES | FILE_READ_ATTRIBUTES | + FILE_WRITE_EA | FILE_APPEND_DATA | FILE_WRITE_DATA | + FILE_READ_DATA); +} + +#ifdef CONFIG_CIFS_ALLOW_INSECURE_LEGACY +static u32 cifs_posix_convert_flags(unsigned int flags) +{ + u32 posix_flags = 0; + + if ((flags & O_ACCMODE) == O_RDONLY) + posix_flags = SMB_O_RDONLY; + else if ((flags & O_ACCMODE) == O_WRONLY) + posix_flags = SMB_O_WRONLY; + else if ((flags & O_ACCMODE) == O_RDWR) + posix_flags = SMB_O_RDWR; + + if (flags & O_CREAT) { + posix_flags |= SMB_O_CREAT; + if (flags & O_EXCL) + posix_flags |= SMB_O_EXCL; + } else if (flags & O_EXCL) + cifs_dbg(FYI, "Application %s pid %d has incorrectly set O_EXCL flag but not O_CREAT on file open. Ignoring O_EXCL\n", + current->comm, current->tgid); + + if (flags & O_TRUNC) + posix_flags |= SMB_O_TRUNC; + /* be safe and imply O_SYNC for O_DSYNC */ + if (flags & O_DSYNC) + posix_flags |= SMB_O_SYNC; + if (flags & O_DIRECTORY) + posix_flags |= SMB_O_DIRECTORY; + if (flags & O_NOFOLLOW) + posix_flags |= SMB_O_NOFOLLOW; + if (flags & O_DIRECT) + posix_flags |= SMB_O_DIRECT; + + return posix_flags; +} +#endif /* CONFIG_CIFS_ALLOW_INSECURE_LEGACY */ + +static inline int cifs_get_disposition(unsigned int flags) +{ + if ((flags & (O_CREAT | O_EXCL)) == (O_CREAT | O_EXCL)) + return FILE_CREATE; + else if ((flags & (O_CREAT | O_TRUNC)) == (O_CREAT | O_TRUNC)) + return FILE_OVERWRITE_IF; + else if ((flags & O_CREAT) == O_CREAT) + return FILE_OPEN_IF; + else if ((flags & O_TRUNC) == O_TRUNC) + return FILE_OVERWRITE; + else + return FILE_OPEN; +} + +#ifdef CONFIG_CIFS_ALLOW_INSECURE_LEGACY +int cifs_posix_open(const char *full_path, struct inode **pinode, + struct super_block *sb, int mode, unsigned int f_flags, + __u32 *poplock, __u16 *pnetfid, unsigned int xid) +{ + int rc; + FILE_UNIX_BASIC_INFO *presp_data; + __u32 posix_flags = 0; + struct cifs_sb_info *cifs_sb = CIFS_SB(sb); + struct cifs_fattr fattr; + struct tcon_link *tlink; + struct cifs_tcon *tcon; + + cifs_dbg(FYI, "posix open %s\n", full_path); + + presp_data = kzalloc(sizeof(FILE_UNIX_BASIC_INFO), GFP_KERNEL); + if (presp_data == NULL) + return -ENOMEM; + + tlink = cifs_sb_tlink(cifs_sb); + if (IS_ERR(tlink)) { + rc = PTR_ERR(tlink); + goto posix_open_ret; + } + + tcon = tlink_tcon(tlink); + mode &= ~current_umask(); + + posix_flags = cifs_posix_convert_flags(f_flags); + rc = CIFSPOSIXCreate(xid, tcon, posix_flags, mode, pnetfid, presp_data, + poplock, full_path, cifs_sb->local_nls, + cifs_remap(cifs_sb)); + cifs_put_tlink(tlink); + + if (rc) + goto posix_open_ret; + + if (presp_data->Type == cpu_to_le32(-1)) + goto posix_open_ret; /* open ok, caller does qpathinfo */ + + if (!pinode) + goto posix_open_ret; /* caller does not need info */ + + cifs_unix_basic_to_fattr(&fattr, presp_data, cifs_sb); + + /* get new inode and set it up */ + if (*pinode == NULL) { + cifs_fill_uniqueid(sb, &fattr); + *pinode = cifs_iget(sb, &fattr); + if (!*pinode) { + rc = -ENOMEM; + goto posix_open_ret; + } + } else { + cifs_revalidate_mapping(*pinode); + rc = cifs_fattr_to_inode(*pinode, &fattr); + } + +posix_open_ret: + kfree(presp_data); + return rc; +} +#endif /* CONFIG_CIFS_ALLOW_INSECURE_LEGACY */ + +static int cifs_nt_open(const char *full_path, struct inode *inode, struct cifs_sb_info *cifs_sb, + struct cifs_tcon *tcon, unsigned int f_flags, __u32 *oplock, + struct cifs_fid *fid, unsigned int xid, struct cifs_open_info_data *buf) +{ + int rc; + int desired_access; + int disposition; + int create_options = CREATE_NOT_DIR; + struct TCP_Server_Info *server = tcon->ses->server; + struct cifs_open_parms oparms; + + if (!server->ops->open) + return -ENOSYS; + + desired_access = cifs_convert_flags(f_flags); + +/********************************************************************* + * open flag mapping table: + * + * POSIX Flag CIFS Disposition + * ---------- ---------------- + * O_CREAT FILE_OPEN_IF + * O_CREAT | O_EXCL FILE_CREATE + * O_CREAT | O_TRUNC FILE_OVERWRITE_IF + * O_TRUNC FILE_OVERWRITE + * none of the above FILE_OPEN + * + * Note that there is not a direct match between disposition + * FILE_SUPERSEDE (ie create whether or not file exists although + * O_CREAT | O_TRUNC is similar but truncates the existing + * file rather than creating a new file as FILE_SUPERSEDE does + * (which uses the attributes / metadata passed in on open call) + *? + *? O_SYNC is a reasonable match to CIFS writethrough flag + *? and the read write flags match reasonably. O_LARGEFILE + *? is irrelevant because largefile support is always used + *? by this client. Flags O_APPEND, O_DIRECT, O_DIRECTORY, + * O_FASYNC, O_NOFOLLOW, O_NONBLOCK need further investigation + *********************************************************************/ + + disposition = cifs_get_disposition(f_flags); + + /* BB pass O_SYNC flag through on file attributes .. BB */ + + /* O_SYNC also has bit for O_DSYNC so following check picks up either */ + if (f_flags & O_SYNC) + create_options |= CREATE_WRITE_THROUGH; + + if (f_flags & O_DIRECT) + create_options |= CREATE_NO_BUFFER; + + oparms = (struct cifs_open_parms) { + .tcon = tcon, + .cifs_sb = cifs_sb, + .desired_access = desired_access, + .create_options = cifs_create_options(cifs_sb, create_options), + .disposition = disposition, + .path = full_path, + .fid = fid, + }; + + rc = server->ops->open(xid, &oparms, oplock, buf); + if (rc) + return rc; + + /* TODO: Add support for calling posix query info but with passing in fid */ + if (tcon->unix_ext) + rc = cifs_get_inode_info_unix(&inode, full_path, inode->i_sb, + xid); + else + rc = cifs_get_inode_info(&inode, full_path, buf, inode->i_sb, + xid, fid); + + if (rc) { + server->ops->close(xid, tcon, fid); + if (rc == -ESTALE) + rc = -EOPENSTALE; + } + + return rc; +} + +static bool +cifs_has_mand_locks(struct cifsInodeInfo *cinode) +{ + struct cifs_fid_locks *cur; + bool has_locks = false; + + down_read(&cinode->lock_sem); + list_for_each_entry(cur, &cinode->llist, llist) { + if (!list_empty(&cur->locks)) { + has_locks = true; + break; + } + } + up_read(&cinode->lock_sem); + return has_locks; +} + +void +cifs_down_write(struct rw_semaphore *sem) +{ + while (!down_write_trylock(sem)) + msleep(10); +} + +static void cifsFileInfo_put_work(struct work_struct *work); + +struct cifsFileInfo *cifs_new_fileinfo(struct cifs_fid *fid, struct file *file, + struct tcon_link *tlink, __u32 oplock, + const char *symlink_target) +{ + struct dentry *dentry = file_dentry(file); + struct inode *inode = d_inode(dentry); + struct cifsInodeInfo *cinode = CIFS_I(inode); + struct cifsFileInfo *cfile; + struct cifs_fid_locks *fdlocks; + struct cifs_tcon *tcon = tlink_tcon(tlink); + struct TCP_Server_Info *server = tcon->ses->server; + + cfile = kzalloc(sizeof(struct cifsFileInfo), GFP_KERNEL); + if (cfile == NULL) + return cfile; + + fdlocks = kzalloc(sizeof(struct cifs_fid_locks), GFP_KERNEL); + if (!fdlocks) { + kfree(cfile); + return NULL; + } + + if (symlink_target) { + cfile->symlink_target = kstrdup(symlink_target, GFP_KERNEL); + if (!cfile->symlink_target) { + kfree(fdlocks); + kfree(cfile); + return NULL; + } + } + + INIT_LIST_HEAD(&fdlocks->locks); + fdlocks->cfile = cfile; + cfile->llist = fdlocks; + + cfile->count = 1; + cfile->pid = current->tgid; + cfile->uid = current_fsuid(); + cfile->dentry = dget(dentry); + cfile->f_flags = file->f_flags; + cfile->invalidHandle = false; + cfile->deferred_close_scheduled = false; + cfile->tlink = cifs_get_tlink(tlink); + INIT_WORK(&cfile->oplock_break, cifs_oplock_break); + INIT_WORK(&cfile->put, cifsFileInfo_put_work); + INIT_DELAYED_WORK(&cfile->deferred, smb2_deferred_work_close); + mutex_init(&cfile->fh_mutex); + spin_lock_init(&cfile->file_info_lock); + + cifs_sb_active(inode->i_sb); + + /* + * If the server returned a read oplock and we have mandatory brlocks, + * set oplock level to None. + */ + if (server->ops->is_read_op(oplock) && cifs_has_mand_locks(cinode)) { + cifs_dbg(FYI, "Reset oplock val from read to None due to mand locks\n"); + oplock = 0; + } + + cifs_down_write(&cinode->lock_sem); + list_add(&fdlocks->llist, &cinode->llist); + up_write(&cinode->lock_sem); + + spin_lock(&tcon->open_file_lock); + if (fid->pending_open->oplock != CIFS_OPLOCK_NO_CHANGE && oplock) + oplock = fid->pending_open->oplock; + list_del(&fid->pending_open->olist); + + fid->purge_cache = false; + server->ops->set_fid(cfile, fid, oplock); + + list_add(&cfile->tlist, &tcon->openFileList); + atomic_inc(&tcon->num_local_opens); + + /* if readable file instance put first in list*/ + spin_lock(&cinode->open_file_lock); + if (file->f_mode & FMODE_READ) + list_add(&cfile->flist, &cinode->openFileList); + else + list_add_tail(&cfile->flist, &cinode->openFileList); + spin_unlock(&cinode->open_file_lock); + spin_unlock(&tcon->open_file_lock); + + if (fid->purge_cache) + cifs_zap_mapping(inode); + + file->private_data = cfile; + return cfile; +} + +struct cifsFileInfo * +cifsFileInfo_get(struct cifsFileInfo *cifs_file) +{ + spin_lock(&cifs_file->file_info_lock); + cifsFileInfo_get_locked(cifs_file); + spin_unlock(&cifs_file->file_info_lock); + return cifs_file; +} + +static void cifsFileInfo_put_final(struct cifsFileInfo *cifs_file) +{ + struct inode *inode = d_inode(cifs_file->dentry); + struct cifsInodeInfo *cifsi = CIFS_I(inode); + struct cifsLockInfo *li, *tmp; + struct super_block *sb = inode->i_sb; + + /* + * Delete any outstanding lock records. We'll lose them when the file + * is closed anyway. + */ + cifs_down_write(&cifsi->lock_sem); + list_for_each_entry_safe(li, tmp, &cifs_file->llist->locks, llist) { + list_del(&li->llist); + cifs_del_lock_waiters(li); + kfree(li); + } + list_del(&cifs_file->llist->llist); + kfree(cifs_file->llist); + up_write(&cifsi->lock_sem); + + cifs_put_tlink(cifs_file->tlink); + dput(cifs_file->dentry); + cifs_sb_deactive(sb); + kfree(cifs_file->symlink_target); + kfree(cifs_file); +} + +static void cifsFileInfo_put_work(struct work_struct *work) +{ + struct cifsFileInfo *cifs_file = container_of(work, + struct cifsFileInfo, put); + + cifsFileInfo_put_final(cifs_file); +} + +/** + * cifsFileInfo_put - release a reference of file priv data + * + * Always potentially wait for oplock handler. See _cifsFileInfo_put(). + * + * @cifs_file: cifs/smb3 specific info (eg refcounts) for an open file + */ +void cifsFileInfo_put(struct cifsFileInfo *cifs_file) +{ + _cifsFileInfo_put(cifs_file, true, true); +} + +/** + * _cifsFileInfo_put - release a reference of file priv data + * + * This may involve closing the filehandle @cifs_file out on the + * server. Must be called without holding tcon->open_file_lock, + * cinode->open_file_lock and cifs_file->file_info_lock. + * + * If @wait_for_oplock_handler is true and we are releasing the last + * reference, wait for any running oplock break handler of the file + * and cancel any pending one. + * + * @cifs_file: cifs/smb3 specific info (eg refcounts) for an open file + * @wait_oplock_handler: must be false if called from oplock_break_handler + * @offload: not offloaded on close and oplock breaks + * + */ +void _cifsFileInfo_put(struct cifsFileInfo *cifs_file, + bool wait_oplock_handler, bool offload) +{ + struct inode *inode = d_inode(cifs_file->dentry); + struct cifs_tcon *tcon = tlink_tcon(cifs_file->tlink); + struct TCP_Server_Info *server = tcon->ses->server; + struct cifsInodeInfo *cifsi = CIFS_I(inode); + struct super_block *sb = inode->i_sb; + struct cifs_sb_info *cifs_sb = CIFS_SB(sb); + struct cifs_fid fid = {}; + struct cifs_pending_open open; + bool oplock_break_cancelled; + + spin_lock(&tcon->open_file_lock); + spin_lock(&cifsi->open_file_lock); + spin_lock(&cifs_file->file_info_lock); + if (--cifs_file->count > 0) { + spin_unlock(&cifs_file->file_info_lock); + spin_unlock(&cifsi->open_file_lock); + spin_unlock(&tcon->open_file_lock); + return; + } + spin_unlock(&cifs_file->file_info_lock); + + if (server->ops->get_lease_key) + server->ops->get_lease_key(inode, &fid); + + /* store open in pending opens to make sure we don't miss lease break */ + cifs_add_pending_open_locked(&fid, cifs_file->tlink, &open); + + /* remove it from the lists */ + list_del(&cifs_file->flist); + list_del(&cifs_file->tlist); + atomic_dec(&tcon->num_local_opens); + + if (list_empty(&cifsi->openFileList)) { + cifs_dbg(FYI, "closing last open instance for inode %p\n", + d_inode(cifs_file->dentry)); + /* + * In strict cache mode we need invalidate mapping on the last + * close because it may cause a error when we open this file + * again and get at least level II oplock. + */ + if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_STRICT_IO) + set_bit(CIFS_INO_INVALID_MAPPING, &cifsi->flags); + cifs_set_oplock_level(cifsi, 0); + } + + spin_unlock(&cifsi->open_file_lock); + spin_unlock(&tcon->open_file_lock); + + oplock_break_cancelled = wait_oplock_handler ? + cancel_work_sync(&cifs_file->oplock_break) : false; + + if (!tcon->need_reconnect && !cifs_file->invalidHandle) { + struct TCP_Server_Info *server = tcon->ses->server; + unsigned int xid; + + xid = get_xid(); + if (server->ops->close_getattr) + server->ops->close_getattr(xid, tcon, cifs_file); + else if (server->ops->close) + server->ops->close(xid, tcon, &cifs_file->fid); + _free_xid(xid); + } + + if (oplock_break_cancelled) + cifs_done_oplock_break(cifsi); + + cifs_del_pending_open(&open); + + if (offload) + queue_work(fileinfo_put_wq, &cifs_file->put); + else + cifsFileInfo_put_final(cifs_file); +} + +int cifs_open(struct inode *inode, struct file *file) + +{ + int rc = -EACCES; + unsigned int xid; + __u32 oplock; + struct cifs_sb_info *cifs_sb; + struct TCP_Server_Info *server; + struct cifs_tcon *tcon; + struct tcon_link *tlink; + struct cifsFileInfo *cfile = NULL; + void *page; + const char *full_path; + bool posix_open_ok = false; + struct cifs_fid fid = {}; + struct cifs_pending_open open; + struct cifs_open_info_data data = {}; + + xid = get_xid(); + + cifs_sb = CIFS_SB(inode->i_sb); + if (unlikely(cifs_forced_shutdown(cifs_sb))) { + free_xid(xid); + return -EIO; + } + + tlink = cifs_sb_tlink(cifs_sb); + if (IS_ERR(tlink)) { + free_xid(xid); + return PTR_ERR(tlink); + } + tcon = tlink_tcon(tlink); + server = tcon->ses->server; + + page = alloc_dentry_path(); + full_path = build_path_from_dentry(file_dentry(file), page); + if (IS_ERR(full_path)) { + rc = PTR_ERR(full_path); + goto out; + } + + cifs_dbg(FYI, "inode = 0x%p file flags are 0x%x for %s\n", + inode, file->f_flags, full_path); + + if (file->f_flags & O_DIRECT && + cifs_sb->mnt_cifs_flags & CIFS_MOUNT_STRICT_IO) { + if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_NO_BRL) + file->f_op = &cifs_file_direct_nobrl_ops; + else + file->f_op = &cifs_file_direct_ops; + } + + /* Get the cached handle as SMB2 close is deferred */ + rc = cifs_get_readable_path(tcon, full_path, &cfile); + if (rc == 0) { + if (file->f_flags == cfile->f_flags) { + file->private_data = cfile; + spin_lock(&CIFS_I(inode)->deferred_lock); + cifs_del_deferred_close(cfile); + spin_unlock(&CIFS_I(inode)->deferred_lock); + goto use_cache; + } else { + _cifsFileInfo_put(cfile, true, false); + } + } + + if (server->oplocks) + oplock = REQ_OPLOCK; + else + oplock = 0; + +#ifdef CONFIG_CIFS_ALLOW_INSECURE_LEGACY + if (!tcon->broken_posix_open && tcon->unix_ext && + cap_unix(tcon->ses) && (CIFS_UNIX_POSIX_PATH_OPS_CAP & + le64_to_cpu(tcon->fsUnixInfo.Capability))) { + /* can not refresh inode info since size could be stale */ + rc = cifs_posix_open(full_path, &inode, inode->i_sb, + cifs_sb->ctx->file_mode /* ignored */, + file->f_flags, &oplock, &fid.netfid, xid); + if (rc == 0) { + cifs_dbg(FYI, "posix open succeeded\n"); + posix_open_ok = true; + } else if ((rc == -EINVAL) || (rc == -EOPNOTSUPP)) { + if (tcon->ses->serverNOS) + cifs_dbg(VFS, "server %s of type %s returned unexpected error on SMB posix open, disabling posix open support. Check if server update available.\n", + tcon->ses->ip_addr, + tcon->ses->serverNOS); + tcon->broken_posix_open = true; + } else if ((rc != -EIO) && (rc != -EREMOTE) && + (rc != -EOPNOTSUPP)) /* path not found or net err */ + goto out; + /* + * Else fallthrough to retry open the old way on network i/o + * or DFS errors. + */ + } +#endif /* CONFIG_CIFS_ALLOW_INSECURE_LEGACY */ + + if (server->ops->get_lease_key) + server->ops->get_lease_key(inode, &fid); + + cifs_add_pending_open(&fid, tlink, &open); + + if (!posix_open_ok) { + if (server->ops->get_lease_key) + server->ops->get_lease_key(inode, &fid); + + rc = cifs_nt_open(full_path, inode, cifs_sb, tcon, file->f_flags, &oplock, &fid, + xid, &data); + if (rc) { + cifs_del_pending_open(&open); + goto out; + } + } + + cfile = cifs_new_fileinfo(&fid, file, tlink, oplock, data.symlink_target); + if (cfile == NULL) { + if (server->ops->close) + server->ops->close(xid, tcon, &fid); + cifs_del_pending_open(&open); + rc = -ENOMEM; + goto out; + } + +#ifdef CONFIG_CIFS_ALLOW_INSECURE_LEGACY + if ((oplock & CIFS_CREATE_ACTION) && !posix_open_ok && tcon->unix_ext) { + /* + * Time to set mode which we can not set earlier due to + * problems creating new read-only files. + */ + struct cifs_unix_set_info_args args = { + .mode = inode->i_mode, + .uid = INVALID_UID, /* no change */ + .gid = INVALID_GID, /* no change */ + .ctime = NO_CHANGE_64, + .atime = NO_CHANGE_64, + .mtime = NO_CHANGE_64, + .device = 0, + }; + CIFSSMBUnixSetFileInfo(xid, tcon, &args, fid.netfid, + cfile->pid); + } +#endif /* CONFIG_CIFS_ALLOW_INSECURE_LEGACY */ + +use_cache: + fscache_use_cookie(cifs_inode_cookie(file_inode(file)), + file->f_mode & FMODE_WRITE); + if (file->f_flags & O_DIRECT && + (!((file->f_flags & O_ACCMODE) != O_RDONLY) || + file->f_flags & O_APPEND)) + cifs_invalidate_cache(file_inode(file), + FSCACHE_INVAL_DIO_WRITE); + +out: + free_dentry_path(page); + free_xid(xid); + cifs_put_tlink(tlink); + cifs_free_open_info(&data); + return rc; +} + +#ifdef CONFIG_CIFS_ALLOW_INSECURE_LEGACY +static int cifs_push_posix_locks(struct cifsFileInfo *cfile); +#endif /* CONFIG_CIFS_ALLOW_INSECURE_LEGACY */ + +/* + * Try to reacquire byte range locks that were released when session + * to server was lost. + */ +static int +cifs_relock_file(struct cifsFileInfo *cfile) +{ + struct cifsInodeInfo *cinode = CIFS_I(d_inode(cfile->dentry)); + struct cifs_tcon *tcon = tlink_tcon(cfile->tlink); + int rc = 0; +#ifdef CONFIG_CIFS_ALLOW_INSECURE_LEGACY + struct cifs_sb_info *cifs_sb = CIFS_SB(cfile->dentry->d_sb); +#endif /* CONFIG_CIFS_ALLOW_INSECURE_LEGACY */ + + down_read_nested(&cinode->lock_sem, SINGLE_DEPTH_NESTING); + if (cinode->can_cache_brlcks) { + /* can cache locks - no need to relock */ + up_read(&cinode->lock_sem); + return rc; + } + +#ifdef CONFIG_CIFS_ALLOW_INSECURE_LEGACY + if (cap_unix(tcon->ses) && + (CIFS_UNIX_FCNTL_CAP & le64_to_cpu(tcon->fsUnixInfo.Capability)) && + ((cifs_sb->mnt_cifs_flags & CIFS_MOUNT_NOPOSIXBRL) == 0)) + rc = cifs_push_posix_locks(cfile); + else +#endif /* CONFIG_CIFS_ALLOW_INSECURE_LEGACY */ + rc = tcon->ses->server->ops->push_mand_locks(cfile); + + up_read(&cinode->lock_sem); + return rc; +} + +static int +cifs_reopen_file(struct cifsFileInfo *cfile, bool can_flush) +{ + int rc = -EACCES; + unsigned int xid; + __u32 oplock; + struct cifs_sb_info *cifs_sb; + struct cifs_tcon *tcon; + struct TCP_Server_Info *server; + struct cifsInodeInfo *cinode; + struct inode *inode; + void *page; + const char *full_path; + int desired_access; + int disposition = FILE_OPEN; + int create_options = CREATE_NOT_DIR; + struct cifs_open_parms oparms; + + xid = get_xid(); + mutex_lock(&cfile->fh_mutex); + if (!cfile->invalidHandle) { + mutex_unlock(&cfile->fh_mutex); + free_xid(xid); + return 0; + } + + inode = d_inode(cfile->dentry); + cifs_sb = CIFS_SB(inode->i_sb); + tcon = tlink_tcon(cfile->tlink); + server = tcon->ses->server; + + /* + * Can not grab rename sem here because various ops, including those + * that already have the rename sem can end up causing writepage to get + * called and if the server was down that means we end up here, and we + * can never tell if the caller already has the rename_sem. + */ + page = alloc_dentry_path(); + full_path = build_path_from_dentry(cfile->dentry, page); + if (IS_ERR(full_path)) { + mutex_unlock(&cfile->fh_mutex); + free_dentry_path(page); + free_xid(xid); + return PTR_ERR(full_path); + } + + cifs_dbg(FYI, "inode = 0x%p file flags 0x%x for %s\n", + inode, cfile->f_flags, full_path); + + if (tcon->ses->server->oplocks) + oplock = REQ_OPLOCK; + else + oplock = 0; + +#ifdef CONFIG_CIFS_ALLOW_INSECURE_LEGACY + if (tcon->unix_ext && cap_unix(tcon->ses) && + (CIFS_UNIX_POSIX_PATH_OPS_CAP & + le64_to_cpu(tcon->fsUnixInfo.Capability))) { + /* + * O_CREAT, O_EXCL and O_TRUNC already had their effect on the + * original open. Must mask them off for a reopen. + */ + unsigned int oflags = cfile->f_flags & + ~(O_CREAT | O_EXCL | O_TRUNC); + + rc = cifs_posix_open(full_path, NULL, inode->i_sb, + cifs_sb->ctx->file_mode /* ignored */, + oflags, &oplock, &cfile->fid.netfid, xid); + if (rc == 0) { + cifs_dbg(FYI, "posix reopen succeeded\n"); + oparms.reconnect = true; + goto reopen_success; + } + /* + * fallthrough to retry open the old way on errors, especially + * in the reconnect path it is important to retry hard + */ + } +#endif /* CONFIG_CIFS_ALLOW_INSECURE_LEGACY */ + + desired_access = cifs_convert_flags(cfile->f_flags); + + /* O_SYNC also has bit for O_DSYNC so following check picks up either */ + if (cfile->f_flags & O_SYNC) + create_options |= CREATE_WRITE_THROUGH; + + if (cfile->f_flags & O_DIRECT) + create_options |= CREATE_NO_BUFFER; + + if (server->ops->get_lease_key) + server->ops->get_lease_key(inode, &cfile->fid); + + oparms = (struct cifs_open_parms) { + .tcon = tcon, + .cifs_sb = cifs_sb, + .desired_access = desired_access, + .create_options = cifs_create_options(cifs_sb, create_options), + .disposition = disposition, + .path = full_path, + .fid = &cfile->fid, + .reconnect = true, + }; + + /* + * Can not refresh inode by passing in file_info buf to be returned by + * ops->open and then calling get_inode_info with returned buf since + * file might have write behind data that needs to be flushed and server + * version of file size can be stale. If we knew for sure that inode was + * not dirty locally we could do this. + */ + rc = server->ops->open(xid, &oparms, &oplock, NULL); + if (rc == -ENOENT && oparms.reconnect == false) { + /* durable handle timeout is expired - open the file again */ + rc = server->ops->open(xid, &oparms, &oplock, NULL); + /* indicate that we need to relock the file */ + oparms.reconnect = true; + } + + if (rc) { + mutex_unlock(&cfile->fh_mutex); + cifs_dbg(FYI, "cifs_reopen returned 0x%x\n", rc); + cifs_dbg(FYI, "oplock: %d\n", oplock); + goto reopen_error_exit; + } + +#ifdef CONFIG_CIFS_ALLOW_INSECURE_LEGACY +reopen_success: +#endif /* CONFIG_CIFS_ALLOW_INSECURE_LEGACY */ + cfile->invalidHandle = false; + mutex_unlock(&cfile->fh_mutex); + cinode = CIFS_I(inode); + + if (can_flush) { + rc = filemap_write_and_wait(inode->i_mapping); + if (!is_interrupt_error(rc)) + mapping_set_error(inode->i_mapping, rc); + + if (tcon->posix_extensions) + rc = smb311_posix_get_inode_info(&inode, full_path, inode->i_sb, xid); + else if (tcon->unix_ext) + rc = cifs_get_inode_info_unix(&inode, full_path, + inode->i_sb, xid); + else + rc = cifs_get_inode_info(&inode, full_path, NULL, + inode->i_sb, xid, NULL); + } + /* + * Else we are writing out data to server already and could deadlock if + * we tried to flush data, and since we do not know if we have data that + * would invalidate the current end of file on the server we can not go + * to the server to get the new inode info. + */ + + /* + * If the server returned a read oplock and we have mandatory brlocks, + * set oplock level to None. + */ + if (server->ops->is_read_op(oplock) && cifs_has_mand_locks(cinode)) { + cifs_dbg(FYI, "Reset oplock val from read to None due to mand locks\n"); + oplock = 0; + } + + server->ops->set_fid(cfile, &cfile->fid, oplock); + if (oparms.reconnect) + cifs_relock_file(cfile); + +reopen_error_exit: + free_dentry_path(page); + free_xid(xid); + return rc; +} + +void smb2_deferred_work_close(struct work_struct *work) +{ + struct cifsFileInfo *cfile = container_of(work, + struct cifsFileInfo, deferred.work); + + spin_lock(&CIFS_I(d_inode(cfile->dentry))->deferred_lock); + cifs_del_deferred_close(cfile); + cfile->deferred_close_scheduled = false; + spin_unlock(&CIFS_I(d_inode(cfile->dentry))->deferred_lock); + _cifsFileInfo_put(cfile, true, false); +} + +int cifs_close(struct inode *inode, struct file *file) +{ + struct cifsFileInfo *cfile; + struct cifsInodeInfo *cinode = CIFS_I(inode); + struct cifs_sb_info *cifs_sb = CIFS_SB(inode->i_sb); + struct cifs_deferred_close *dclose; + + cifs_fscache_unuse_inode_cookie(inode, file->f_mode & FMODE_WRITE); + + if (file->private_data != NULL) { + cfile = file->private_data; + file->private_data = NULL; + dclose = kmalloc(sizeof(struct cifs_deferred_close), GFP_KERNEL); + if ((cinode->oplock == CIFS_CACHE_RHW_FLG) && + cinode->lease_granted && + !test_bit(CIFS_INO_CLOSE_ON_LOCK, &cinode->flags) && + dclose) { + if (test_and_clear_bit(CIFS_INO_MODIFIED_ATTR, &cinode->flags)) { + inode->i_ctime = inode->i_mtime = current_time(inode); + } + spin_lock(&cinode->deferred_lock); + cifs_add_deferred_close(cfile, dclose); + if (cfile->deferred_close_scheduled && + delayed_work_pending(&cfile->deferred)) { + /* + * If there is no pending work, mod_delayed_work queues new work. + * So, Increase the ref count to avoid use-after-free. + */ + if (!mod_delayed_work(deferredclose_wq, + &cfile->deferred, cifs_sb->ctx->closetimeo)) + cifsFileInfo_get(cfile); + } else { + /* Deferred close for files */ + queue_delayed_work(deferredclose_wq, + &cfile->deferred, cifs_sb->ctx->closetimeo); + cfile->deferred_close_scheduled = true; + spin_unlock(&cinode->deferred_lock); + return 0; + } + spin_unlock(&cinode->deferred_lock); + _cifsFileInfo_put(cfile, true, false); + } else { + _cifsFileInfo_put(cfile, true, false); + kfree(dclose); + } + } + + /* return code from the ->release op is always ignored */ + return 0; +} + +void +cifs_reopen_persistent_handles(struct cifs_tcon *tcon) +{ + struct cifsFileInfo *open_file, *tmp; + struct list_head tmp_list; + + if (!tcon->use_persistent || !tcon->need_reopen_files) + return; + + tcon->need_reopen_files = false; + + cifs_dbg(FYI, "Reopen persistent handles\n"); + INIT_LIST_HEAD(&tmp_list); + + /* list all files open on tree connection, reopen resilient handles */ + spin_lock(&tcon->open_file_lock); + list_for_each_entry(open_file, &tcon->openFileList, tlist) { + if (!open_file->invalidHandle) + continue; + cifsFileInfo_get(open_file); + list_add_tail(&open_file->rlist, &tmp_list); + } + spin_unlock(&tcon->open_file_lock); + + list_for_each_entry_safe(open_file, tmp, &tmp_list, rlist) { + if (cifs_reopen_file(open_file, false /* do not flush */)) + tcon->need_reopen_files = true; + list_del_init(&open_file->rlist); + cifsFileInfo_put(open_file); + } +} + +int cifs_closedir(struct inode *inode, struct file *file) +{ + int rc = 0; + unsigned int xid; + struct cifsFileInfo *cfile = file->private_data; + struct cifs_tcon *tcon; + struct TCP_Server_Info *server; + char *buf; + + cifs_dbg(FYI, "Closedir inode = 0x%p\n", inode); + + if (cfile == NULL) + return rc; + + xid = get_xid(); + tcon = tlink_tcon(cfile->tlink); + server = tcon->ses->server; + + cifs_dbg(FYI, "Freeing private data in close dir\n"); + spin_lock(&cfile->file_info_lock); + if (server->ops->dir_needs_close(cfile)) { + cfile->invalidHandle = true; + spin_unlock(&cfile->file_info_lock); + if (server->ops->close_dir) + rc = server->ops->close_dir(xid, tcon, &cfile->fid); + else + rc = -ENOSYS; + cifs_dbg(FYI, "Closing uncompleted readdir with rc %d\n", rc); + /* not much we can do if it fails anyway, ignore rc */ + rc = 0; + } else + spin_unlock(&cfile->file_info_lock); + + buf = cfile->srch_inf.ntwrk_buf_start; + if (buf) { + cifs_dbg(FYI, "closedir free smb buf in srch struct\n"); + cfile->srch_inf.ntwrk_buf_start = NULL; + if (cfile->srch_inf.smallBuf) + cifs_small_buf_release(buf); + else + cifs_buf_release(buf); + } + + cifs_put_tlink(cfile->tlink); + kfree(file->private_data); + file->private_data = NULL; + /* BB can we lock the filestruct while this is going on? */ + free_xid(xid); + return rc; +} + +static struct cifsLockInfo * +cifs_lock_init(__u64 offset, __u64 length, __u8 type, __u16 flags) +{ + struct cifsLockInfo *lock = + kmalloc(sizeof(struct cifsLockInfo), GFP_KERNEL); + if (!lock) + return lock; + lock->offset = offset; + lock->length = length; + lock->type = type; + lock->pid = current->tgid; + lock->flags = flags; + INIT_LIST_HEAD(&lock->blist); + init_waitqueue_head(&lock->block_q); + return lock; +} + +void +cifs_del_lock_waiters(struct cifsLockInfo *lock) +{ + struct cifsLockInfo *li, *tmp; + list_for_each_entry_safe(li, tmp, &lock->blist, blist) { + list_del_init(&li->blist); + wake_up(&li->block_q); + } +} + +#define CIFS_LOCK_OP 0 +#define CIFS_READ_OP 1 +#define CIFS_WRITE_OP 2 + +/* @rw_check : 0 - no op, 1 - read, 2 - write */ +static bool +cifs_find_fid_lock_conflict(struct cifs_fid_locks *fdlocks, __u64 offset, + __u64 length, __u8 type, __u16 flags, + struct cifsFileInfo *cfile, + struct cifsLockInfo **conf_lock, int rw_check) +{ + struct cifsLockInfo *li; + struct cifsFileInfo *cur_cfile = fdlocks->cfile; + struct TCP_Server_Info *server = tlink_tcon(cfile->tlink)->ses->server; + + list_for_each_entry(li, &fdlocks->locks, llist) { + if (offset + length <= li->offset || + offset >= li->offset + li->length) + continue; + if (rw_check != CIFS_LOCK_OP && current->tgid == li->pid && + server->ops->compare_fids(cfile, cur_cfile)) { + /* shared lock prevents write op through the same fid */ + if (!(li->type & server->vals->shared_lock_type) || + rw_check != CIFS_WRITE_OP) + continue; + } + if ((type & server->vals->shared_lock_type) && + ((server->ops->compare_fids(cfile, cur_cfile) && + current->tgid == li->pid) || type == li->type)) + continue; + if (rw_check == CIFS_LOCK_OP && + (flags & FL_OFDLCK) && (li->flags & FL_OFDLCK) && + server->ops->compare_fids(cfile, cur_cfile)) + continue; + if (conf_lock) + *conf_lock = li; + return true; + } + return false; +} + +bool +cifs_find_lock_conflict(struct cifsFileInfo *cfile, __u64 offset, __u64 length, + __u8 type, __u16 flags, + struct cifsLockInfo **conf_lock, int rw_check) +{ + bool rc = false; + struct cifs_fid_locks *cur; + struct cifsInodeInfo *cinode = CIFS_I(d_inode(cfile->dentry)); + + list_for_each_entry(cur, &cinode->llist, llist) { + rc = cifs_find_fid_lock_conflict(cur, offset, length, type, + flags, cfile, conf_lock, + rw_check); + if (rc) + break; + } + + return rc; +} + +/* + * Check if there is another lock that prevents us to set the lock (mandatory + * style). If such a lock exists, update the flock structure with its + * properties. Otherwise, set the flock type to F_UNLCK if we can cache brlocks + * or leave it the same if we can't. Returns 0 if we don't need to request to + * the server or 1 otherwise. + */ +static int +cifs_lock_test(struct cifsFileInfo *cfile, __u64 offset, __u64 length, + __u8 type, struct file_lock *flock) +{ + int rc = 0; + struct cifsLockInfo *conf_lock; + struct cifsInodeInfo *cinode = CIFS_I(d_inode(cfile->dentry)); + struct TCP_Server_Info *server = tlink_tcon(cfile->tlink)->ses->server; + bool exist; + + down_read(&cinode->lock_sem); + + exist = cifs_find_lock_conflict(cfile, offset, length, type, + flock->fl_flags, &conf_lock, + CIFS_LOCK_OP); + if (exist) { + flock->fl_start = conf_lock->offset; + flock->fl_end = conf_lock->offset + conf_lock->length - 1; + flock->fl_pid = conf_lock->pid; + if (conf_lock->type & server->vals->shared_lock_type) + flock->fl_type = F_RDLCK; + else + flock->fl_type = F_WRLCK; + } else if (!cinode->can_cache_brlcks) + rc = 1; + else + flock->fl_type = F_UNLCK; + + up_read(&cinode->lock_sem); + return rc; +} + +static void +cifs_lock_add(struct cifsFileInfo *cfile, struct cifsLockInfo *lock) +{ + struct cifsInodeInfo *cinode = CIFS_I(d_inode(cfile->dentry)); + cifs_down_write(&cinode->lock_sem); + list_add_tail(&lock->llist, &cfile->llist->locks); + up_write(&cinode->lock_sem); +} + +/* + * Set the byte-range lock (mandatory style). Returns: + * 1) 0, if we set the lock and don't need to request to the server; + * 2) 1, if no locks prevent us but we need to request to the server; + * 3) -EACCES, if there is a lock that prevents us and wait is false. + */ +static int +cifs_lock_add_if(struct cifsFileInfo *cfile, struct cifsLockInfo *lock, + bool wait) +{ + struct cifsLockInfo *conf_lock; + struct cifsInodeInfo *cinode = CIFS_I(d_inode(cfile->dentry)); + bool exist; + int rc = 0; + +try_again: + exist = false; + cifs_down_write(&cinode->lock_sem); + + exist = cifs_find_lock_conflict(cfile, lock->offset, lock->length, + lock->type, lock->flags, &conf_lock, + CIFS_LOCK_OP); + if (!exist && cinode->can_cache_brlcks) { + list_add_tail(&lock->llist, &cfile->llist->locks); + up_write(&cinode->lock_sem); + return rc; + } + + if (!exist) + rc = 1; + else if (!wait) + rc = -EACCES; + else { + list_add_tail(&lock->blist, &conf_lock->blist); + up_write(&cinode->lock_sem); + rc = wait_event_interruptible(lock->block_q, + (lock->blist.prev == &lock->blist) && + (lock->blist.next == &lock->blist)); + if (!rc) + goto try_again; + cifs_down_write(&cinode->lock_sem); + list_del_init(&lock->blist); + } + + up_write(&cinode->lock_sem); + return rc; +} + +#ifdef CONFIG_CIFS_ALLOW_INSECURE_LEGACY +/* + * Check if there is another lock that prevents us to set the lock (posix + * style). If such a lock exists, update the flock structure with its + * properties. Otherwise, set the flock type to F_UNLCK if we can cache brlocks + * or leave it the same if we can't. Returns 0 if we don't need to request to + * the server or 1 otherwise. + */ +static int +cifs_posix_lock_test(struct file *file, struct file_lock *flock) +{ + int rc = 0; + struct cifsInodeInfo *cinode = CIFS_I(file_inode(file)); + unsigned char saved_type = flock->fl_type; + + if ((flock->fl_flags & FL_POSIX) == 0) + return 1; + + down_read(&cinode->lock_sem); + posix_test_lock(file, flock); + + if (flock->fl_type == F_UNLCK && !cinode->can_cache_brlcks) { + flock->fl_type = saved_type; + rc = 1; + } + + up_read(&cinode->lock_sem); + return rc; +} + +/* + * Set the byte-range lock (posix style). Returns: + * 1) <0, if the error occurs while setting the lock; + * 2) 0, if we set the lock and don't need to request to the server; + * 3) FILE_LOCK_DEFERRED, if we will wait for some other file_lock; + * 4) FILE_LOCK_DEFERRED + 1, if we need to request to the server. + */ +static int +cifs_posix_lock_set(struct file *file, struct file_lock *flock) +{ + struct cifsInodeInfo *cinode = CIFS_I(file_inode(file)); + int rc = FILE_LOCK_DEFERRED + 1; + + if ((flock->fl_flags & FL_POSIX) == 0) + return rc; + + cifs_down_write(&cinode->lock_sem); + if (!cinode->can_cache_brlcks) { + up_write(&cinode->lock_sem); + return rc; + } + + rc = posix_lock_file(file, flock, NULL); + up_write(&cinode->lock_sem); + return rc; +} + +int +cifs_push_mandatory_locks(struct cifsFileInfo *cfile) +{ + unsigned int xid; + int rc = 0, stored_rc; + struct cifsLockInfo *li, *tmp; + struct cifs_tcon *tcon; + unsigned int num, max_num, max_buf; + LOCKING_ANDX_RANGE *buf, *cur; + static const int types[] = { + LOCKING_ANDX_LARGE_FILES, + LOCKING_ANDX_SHARED_LOCK | LOCKING_ANDX_LARGE_FILES + }; + int i; + + xid = get_xid(); + tcon = tlink_tcon(cfile->tlink); + + /* + * Accessing maxBuf is racy with cifs_reconnect - need to store value + * and check it before using. + */ + max_buf = tcon->ses->server->maxBuf; + if (max_buf < (sizeof(struct smb_hdr) + sizeof(LOCKING_ANDX_RANGE))) { + free_xid(xid); + return -EINVAL; + } + + BUILD_BUG_ON(sizeof(struct smb_hdr) + sizeof(LOCKING_ANDX_RANGE) > + PAGE_SIZE); + max_buf = min_t(unsigned int, max_buf - sizeof(struct smb_hdr), + PAGE_SIZE); + max_num = (max_buf - sizeof(struct smb_hdr)) / + sizeof(LOCKING_ANDX_RANGE); + buf = kcalloc(max_num, sizeof(LOCKING_ANDX_RANGE), GFP_KERNEL); + if (!buf) { + free_xid(xid); + return -ENOMEM; + } + + for (i = 0; i < 2; i++) { + cur = buf; + num = 0; + list_for_each_entry_safe(li, tmp, &cfile->llist->locks, llist) { + if (li->type != types[i]) + continue; + cur->Pid = cpu_to_le16(li->pid); + cur->LengthLow = cpu_to_le32((u32)li->length); + cur->LengthHigh = cpu_to_le32((u32)(li->length>>32)); + cur->OffsetLow = cpu_to_le32((u32)li->offset); + cur->OffsetHigh = cpu_to_le32((u32)(li->offset>>32)); + if (++num == max_num) { + stored_rc = cifs_lockv(xid, tcon, + cfile->fid.netfid, + (__u8)li->type, 0, num, + buf); + if (stored_rc) + rc = stored_rc; + cur = buf; + num = 0; + } else + cur++; + } + + if (num) { + stored_rc = cifs_lockv(xid, tcon, cfile->fid.netfid, + (__u8)types[i], 0, num, buf); + if (stored_rc) + rc = stored_rc; + } + } + + kfree(buf); + free_xid(xid); + return rc; +} + +static __u32 +hash_lockowner(fl_owner_t owner) +{ + return cifs_lock_secret ^ hash32_ptr((const void *)owner); +} +#endif /* CONFIG_CIFS_ALLOW_INSECURE_LEGACY */ + +struct lock_to_push { + struct list_head llist; + __u64 offset; + __u64 length; + __u32 pid; + __u16 netfid; + __u8 type; +}; + +#ifdef CONFIG_CIFS_ALLOW_INSECURE_LEGACY +static int +cifs_push_posix_locks(struct cifsFileInfo *cfile) +{ + struct inode *inode = d_inode(cfile->dentry); + struct cifs_tcon *tcon = tlink_tcon(cfile->tlink); + struct file_lock *flock; + struct file_lock_context *flctx = inode->i_flctx; + unsigned int count = 0, i; + int rc = 0, xid, type; + struct list_head locks_to_send, *el; + struct lock_to_push *lck, *tmp; + __u64 length; + + xid = get_xid(); + + if (!flctx) + goto out; + + spin_lock(&flctx->flc_lock); + list_for_each(el, &flctx->flc_posix) { + count++; + } + spin_unlock(&flctx->flc_lock); + + INIT_LIST_HEAD(&locks_to_send); + + /* + * Allocating count locks is enough because no FL_POSIX locks can be + * added to the list while we are holding cinode->lock_sem that + * protects locking operations of this inode. + */ + for (i = 0; i < count; i++) { + lck = kmalloc(sizeof(struct lock_to_push), GFP_KERNEL); + if (!lck) { + rc = -ENOMEM; + goto err_out; + } + list_add_tail(&lck->llist, &locks_to_send); + } + + el = locks_to_send.next; + spin_lock(&flctx->flc_lock); + list_for_each_entry(flock, &flctx->flc_posix, fl_list) { + if (el == &locks_to_send) { + /* + * The list ended. We don't have enough allocated + * structures - something is really wrong. + */ + cifs_dbg(VFS, "Can't push all brlocks!\n"); + break; + } + length = cifs_flock_len(flock); + if (flock->fl_type == F_RDLCK || flock->fl_type == F_SHLCK) + type = CIFS_RDLCK; + else + type = CIFS_WRLCK; + lck = list_entry(el, struct lock_to_push, llist); + lck->pid = hash_lockowner(flock->fl_owner); + lck->netfid = cfile->fid.netfid; + lck->length = length; + lck->type = type; + lck->offset = flock->fl_start; + } + spin_unlock(&flctx->flc_lock); + + list_for_each_entry_safe(lck, tmp, &locks_to_send, llist) { + int stored_rc; + + stored_rc = CIFSSMBPosixLock(xid, tcon, lck->netfid, lck->pid, + lck->offset, lck->length, NULL, + lck->type, 0); + if (stored_rc) + rc = stored_rc; + list_del(&lck->llist); + kfree(lck); + } + +out: + free_xid(xid); + return rc; +err_out: + list_for_each_entry_safe(lck, tmp, &locks_to_send, llist) { + list_del(&lck->llist); + kfree(lck); + } + goto out; +} +#endif /* CONFIG_CIFS_ALLOW_INSECURE_LEGACY */ + +static int +cifs_push_locks(struct cifsFileInfo *cfile) +{ + struct cifsInodeInfo *cinode = CIFS_I(d_inode(cfile->dentry)); + struct cifs_tcon *tcon = tlink_tcon(cfile->tlink); + int rc = 0; +#ifdef CONFIG_CIFS_ALLOW_INSECURE_LEGACY + struct cifs_sb_info *cifs_sb = CIFS_SB(cfile->dentry->d_sb); +#endif /* CONFIG_CIFS_ALLOW_INSECURE_LEGACY */ + + /* we are going to update can_cache_brlcks here - need a write access */ + cifs_down_write(&cinode->lock_sem); + if (!cinode->can_cache_brlcks) { + up_write(&cinode->lock_sem); + return rc; + } + +#ifdef CONFIG_CIFS_ALLOW_INSECURE_LEGACY + if (cap_unix(tcon->ses) && + (CIFS_UNIX_FCNTL_CAP & le64_to_cpu(tcon->fsUnixInfo.Capability)) && + ((cifs_sb->mnt_cifs_flags & CIFS_MOUNT_NOPOSIXBRL) == 0)) + rc = cifs_push_posix_locks(cfile); + else +#endif /* CONFIG_CIFS_ALLOW_INSECURE_LEGACY */ + rc = tcon->ses->server->ops->push_mand_locks(cfile); + + cinode->can_cache_brlcks = false; + up_write(&cinode->lock_sem); + return rc; +} + +static void +cifs_read_flock(struct file_lock *flock, __u32 *type, int *lock, int *unlock, + bool *wait_flag, struct TCP_Server_Info *server) +{ + if (flock->fl_flags & FL_POSIX) + cifs_dbg(FYI, "Posix\n"); + if (flock->fl_flags & FL_FLOCK) + cifs_dbg(FYI, "Flock\n"); + if (flock->fl_flags & FL_SLEEP) { + cifs_dbg(FYI, "Blocking lock\n"); + *wait_flag = true; + } + if (flock->fl_flags & FL_ACCESS) + cifs_dbg(FYI, "Process suspended by mandatory locking - not implemented yet\n"); + if (flock->fl_flags & FL_LEASE) + cifs_dbg(FYI, "Lease on file - not implemented yet\n"); + if (flock->fl_flags & + (~(FL_POSIX | FL_FLOCK | FL_SLEEP | + FL_ACCESS | FL_LEASE | FL_CLOSE | FL_OFDLCK))) + cifs_dbg(FYI, "Unknown lock flags 0x%x\n", flock->fl_flags); + + *type = server->vals->large_lock_type; + if (flock->fl_type == F_WRLCK) { + cifs_dbg(FYI, "F_WRLCK\n"); + *type |= server->vals->exclusive_lock_type; + *lock = 1; + } else if (flock->fl_type == F_UNLCK) { + cifs_dbg(FYI, "F_UNLCK\n"); + *type |= server->vals->unlock_lock_type; + *unlock = 1; + /* Check if unlock includes more than one lock range */ + } else if (flock->fl_type == F_RDLCK) { + cifs_dbg(FYI, "F_RDLCK\n"); + *type |= server->vals->shared_lock_type; + *lock = 1; + } else if (flock->fl_type == F_EXLCK) { + cifs_dbg(FYI, "F_EXLCK\n"); + *type |= server->vals->exclusive_lock_type; + *lock = 1; + } else if (flock->fl_type == F_SHLCK) { + cifs_dbg(FYI, "F_SHLCK\n"); + *type |= server->vals->shared_lock_type; + *lock = 1; + } else + cifs_dbg(FYI, "Unknown type of lock\n"); +} + +static int +cifs_getlk(struct file *file, struct file_lock *flock, __u32 type, + bool wait_flag, bool posix_lck, unsigned int xid) +{ + int rc = 0; + __u64 length = cifs_flock_len(flock); + struct cifsFileInfo *cfile = (struct cifsFileInfo *)file->private_data; + struct cifs_tcon *tcon = tlink_tcon(cfile->tlink); + struct TCP_Server_Info *server = tcon->ses->server; +#ifdef CONFIG_CIFS_ALLOW_INSECURE_LEGACY + __u16 netfid = cfile->fid.netfid; + + if (posix_lck) { + int posix_lock_type; + + rc = cifs_posix_lock_test(file, flock); + if (!rc) + return rc; + + if (type & server->vals->shared_lock_type) + posix_lock_type = CIFS_RDLCK; + else + posix_lock_type = CIFS_WRLCK; + rc = CIFSSMBPosixLock(xid, tcon, netfid, + hash_lockowner(flock->fl_owner), + flock->fl_start, length, flock, + posix_lock_type, wait_flag); + return rc; + } +#endif /* CONFIG_CIFS_ALLOW_INSECURE_LEGACY */ + + rc = cifs_lock_test(cfile, flock->fl_start, length, type, flock); + if (!rc) + return rc; + + /* BB we could chain these into one lock request BB */ + rc = server->ops->mand_lock(xid, cfile, flock->fl_start, length, type, + 1, 0, false); + if (rc == 0) { + rc = server->ops->mand_lock(xid, cfile, flock->fl_start, length, + type, 0, 1, false); + flock->fl_type = F_UNLCK; + if (rc != 0) + cifs_dbg(VFS, "Error unlocking previously locked range %d during test of lock\n", + rc); + return 0; + } + + if (type & server->vals->shared_lock_type) { + flock->fl_type = F_WRLCK; + return 0; + } + + type &= ~server->vals->exclusive_lock_type; + + rc = server->ops->mand_lock(xid, cfile, flock->fl_start, length, + type | server->vals->shared_lock_type, + 1, 0, false); + if (rc == 0) { + rc = server->ops->mand_lock(xid, cfile, flock->fl_start, length, + type | server->vals->shared_lock_type, 0, 1, false); + flock->fl_type = F_RDLCK; + if (rc != 0) + cifs_dbg(VFS, "Error unlocking previously locked range %d during test of lock\n", + rc); + } else + flock->fl_type = F_WRLCK; + + return 0; +} + +void +cifs_move_llist(struct list_head *source, struct list_head *dest) +{ + struct list_head *li, *tmp; + list_for_each_safe(li, tmp, source) + list_move(li, dest); +} + +void +cifs_free_llist(struct list_head *llist) +{ + struct cifsLockInfo *li, *tmp; + list_for_each_entry_safe(li, tmp, llist, llist) { + cifs_del_lock_waiters(li); + list_del(&li->llist); + kfree(li); + } +} + +#ifdef CONFIG_CIFS_ALLOW_INSECURE_LEGACY +int +cifs_unlock_range(struct cifsFileInfo *cfile, struct file_lock *flock, + unsigned int xid) +{ + int rc = 0, stored_rc; + static const int types[] = { + LOCKING_ANDX_LARGE_FILES, + LOCKING_ANDX_SHARED_LOCK | LOCKING_ANDX_LARGE_FILES + }; + unsigned int i; + unsigned int max_num, num, max_buf; + LOCKING_ANDX_RANGE *buf, *cur; + struct cifs_tcon *tcon = tlink_tcon(cfile->tlink); + struct cifsInodeInfo *cinode = CIFS_I(d_inode(cfile->dentry)); + struct cifsLockInfo *li, *tmp; + __u64 length = cifs_flock_len(flock); + struct list_head tmp_llist; + + INIT_LIST_HEAD(&tmp_llist); + + /* + * Accessing maxBuf is racy with cifs_reconnect - need to store value + * and check it before using. + */ + max_buf = tcon->ses->server->maxBuf; + if (max_buf < (sizeof(struct smb_hdr) + sizeof(LOCKING_ANDX_RANGE))) + return -EINVAL; + + BUILD_BUG_ON(sizeof(struct smb_hdr) + sizeof(LOCKING_ANDX_RANGE) > + PAGE_SIZE); + max_buf = min_t(unsigned int, max_buf - sizeof(struct smb_hdr), + PAGE_SIZE); + max_num = (max_buf - sizeof(struct smb_hdr)) / + sizeof(LOCKING_ANDX_RANGE); + buf = kcalloc(max_num, sizeof(LOCKING_ANDX_RANGE), GFP_KERNEL); + if (!buf) + return -ENOMEM; + + cifs_down_write(&cinode->lock_sem); + for (i = 0; i < 2; i++) { + cur = buf; + num = 0; + list_for_each_entry_safe(li, tmp, &cfile->llist->locks, llist) { + if (flock->fl_start > li->offset || + (flock->fl_start + length) < + (li->offset + li->length)) + continue; + if (current->tgid != li->pid) + continue; + if (types[i] != li->type) + continue; + if (cinode->can_cache_brlcks) { + /* + * We can cache brlock requests - simply remove + * a lock from the file's list. + */ + list_del(&li->llist); + cifs_del_lock_waiters(li); + kfree(li); + continue; + } + cur->Pid = cpu_to_le16(li->pid); + cur->LengthLow = cpu_to_le32((u32)li->length); + cur->LengthHigh = cpu_to_le32((u32)(li->length>>32)); + cur->OffsetLow = cpu_to_le32((u32)li->offset); + cur->OffsetHigh = cpu_to_le32((u32)(li->offset>>32)); + /* + * We need to save a lock here to let us add it again to + * the file's list if the unlock range request fails on + * the server. + */ + list_move(&li->llist, &tmp_llist); + if (++num == max_num) { + stored_rc = cifs_lockv(xid, tcon, + cfile->fid.netfid, + li->type, num, 0, buf); + if (stored_rc) { + /* + * We failed on the unlock range + * request - add all locks from the tmp + * list to the head of the file's list. + */ + cifs_move_llist(&tmp_llist, + &cfile->llist->locks); + rc = stored_rc; + } else + /* + * The unlock range request succeed - + * free the tmp list. + */ + cifs_free_llist(&tmp_llist); + cur = buf; + num = 0; + } else + cur++; + } + if (num) { + stored_rc = cifs_lockv(xid, tcon, cfile->fid.netfid, + types[i], num, 0, buf); + if (stored_rc) { + cifs_move_llist(&tmp_llist, + &cfile->llist->locks); + rc = stored_rc; + } else + cifs_free_llist(&tmp_llist); + } + } + + up_write(&cinode->lock_sem); + kfree(buf); + return rc; +} +#endif /* CONFIG_CIFS_ALLOW_INSECURE_LEGACY */ + +static int +cifs_setlk(struct file *file, struct file_lock *flock, __u32 type, + bool wait_flag, bool posix_lck, int lock, int unlock, + unsigned int xid) +{ + int rc = 0; + __u64 length = cifs_flock_len(flock); + struct cifsFileInfo *cfile = (struct cifsFileInfo *)file->private_data; + struct cifs_tcon *tcon = tlink_tcon(cfile->tlink); + struct TCP_Server_Info *server = tcon->ses->server; + struct inode *inode = d_inode(cfile->dentry); + +#ifdef CONFIG_CIFS_ALLOW_INSECURE_LEGACY + if (posix_lck) { + int posix_lock_type; + + rc = cifs_posix_lock_set(file, flock); + if (rc <= FILE_LOCK_DEFERRED) + return rc; + + if (type & server->vals->shared_lock_type) + posix_lock_type = CIFS_RDLCK; + else + posix_lock_type = CIFS_WRLCK; + + if (unlock == 1) + posix_lock_type = CIFS_UNLCK; + + rc = CIFSSMBPosixLock(xid, tcon, cfile->fid.netfid, + hash_lockowner(flock->fl_owner), + flock->fl_start, length, + NULL, posix_lock_type, wait_flag); + goto out; + } +#endif /* CONFIG_CIFS_ALLOW_INSECURE_LEGACY */ + if (lock) { + struct cifsLockInfo *lock; + + lock = cifs_lock_init(flock->fl_start, length, type, + flock->fl_flags); + if (!lock) + return -ENOMEM; + + rc = cifs_lock_add_if(cfile, lock, wait_flag); + if (rc < 0) { + kfree(lock); + return rc; + } + if (!rc) + goto out; + + /* + * Windows 7 server can delay breaking lease from read to None + * if we set a byte-range lock on a file - break it explicitly + * before sending the lock to the server to be sure the next + * read won't conflict with non-overlapted locks due to + * pagereading. + */ + if (!CIFS_CACHE_WRITE(CIFS_I(inode)) && + CIFS_CACHE_READ(CIFS_I(inode))) { + cifs_zap_mapping(inode); + cifs_dbg(FYI, "Set no oplock for inode=%p due to mand locks\n", + inode); + CIFS_I(inode)->oplock = 0; + } + + rc = server->ops->mand_lock(xid, cfile, flock->fl_start, length, + type, 1, 0, wait_flag); + if (rc) { + kfree(lock); + return rc; + } + + cifs_lock_add(cfile, lock); + } else if (unlock) + rc = server->ops->mand_unlock_range(cfile, flock, xid); + +out: + if ((flock->fl_flags & FL_POSIX) || (flock->fl_flags & FL_FLOCK)) { + /* + * If this is a request to remove all locks because we + * are closing the file, it doesn't matter if the + * unlocking failed as both cifs.ko and the SMB server + * remove the lock on file close + */ + if (rc) { + cifs_dbg(VFS, "%s failed rc=%d\n", __func__, rc); + if (!(flock->fl_flags & FL_CLOSE)) + return rc; + } + rc = locks_lock_file_wait(file, flock); + } + return rc; +} + +int cifs_flock(struct file *file, int cmd, struct file_lock *fl) +{ + int rc, xid; + int lock = 0, unlock = 0; + bool wait_flag = false; + bool posix_lck = false; + struct cifs_sb_info *cifs_sb; + struct cifs_tcon *tcon; + struct cifsFileInfo *cfile; + __u32 type; + + xid = get_xid(); + + if (!(fl->fl_flags & FL_FLOCK)) { + rc = -ENOLCK; + free_xid(xid); + return rc; + } + + cfile = (struct cifsFileInfo *)file->private_data; + tcon = tlink_tcon(cfile->tlink); + + cifs_read_flock(fl, &type, &lock, &unlock, &wait_flag, + tcon->ses->server); + cifs_sb = CIFS_FILE_SB(file); + + if (cap_unix(tcon->ses) && + (CIFS_UNIX_FCNTL_CAP & le64_to_cpu(tcon->fsUnixInfo.Capability)) && + ((cifs_sb->mnt_cifs_flags & CIFS_MOUNT_NOPOSIXBRL) == 0)) + posix_lck = true; + + if (!lock && !unlock) { + /* + * if no lock or unlock then nothing to do since we do not + * know what it is + */ + rc = -EOPNOTSUPP; + free_xid(xid); + return rc; + } + + rc = cifs_setlk(file, fl, type, wait_flag, posix_lck, lock, unlock, + xid); + free_xid(xid); + return rc; + + +} + +int cifs_lock(struct file *file, int cmd, struct file_lock *flock) +{ + int rc, xid; + int lock = 0, unlock = 0; + bool wait_flag = false; + bool posix_lck = false; + struct cifs_sb_info *cifs_sb; + struct cifs_tcon *tcon; + struct cifsFileInfo *cfile; + __u32 type; + + rc = -EACCES; + xid = get_xid(); + + cifs_dbg(FYI, "%s: %pD2 cmd=0x%x type=0x%x flags=0x%x r=%lld:%lld\n", __func__, file, cmd, + flock->fl_flags, flock->fl_type, (long long)flock->fl_start, + (long long)flock->fl_end); + + cfile = (struct cifsFileInfo *)file->private_data; + tcon = tlink_tcon(cfile->tlink); + + cifs_read_flock(flock, &type, &lock, &unlock, &wait_flag, + tcon->ses->server); + cifs_sb = CIFS_FILE_SB(file); + set_bit(CIFS_INO_CLOSE_ON_LOCK, &CIFS_I(d_inode(cfile->dentry))->flags); + + if (cap_unix(tcon->ses) && + (CIFS_UNIX_FCNTL_CAP & le64_to_cpu(tcon->fsUnixInfo.Capability)) && + ((cifs_sb->mnt_cifs_flags & CIFS_MOUNT_NOPOSIXBRL) == 0)) + posix_lck = true; + /* + * BB add code here to normalize offset and length to account for + * negative length which we can not accept over the wire. + */ + if (IS_GETLK(cmd)) { + rc = cifs_getlk(file, flock, type, wait_flag, posix_lck, xid); + free_xid(xid); + return rc; + } + + if (!lock && !unlock) { + /* + * if no lock or unlock then nothing to do since we do not + * know what it is + */ + free_xid(xid); + return -EOPNOTSUPP; + } + + rc = cifs_setlk(file, flock, type, wait_flag, posix_lck, lock, unlock, + xid); + free_xid(xid); + return rc; +} + +/* + * update the file size (if needed) after a write. Should be called with + * the inode->i_lock held + */ +void +cifs_update_eof(struct cifsInodeInfo *cifsi, loff_t offset, + unsigned int bytes_written) +{ + loff_t end_of_write = offset + bytes_written; + + if (end_of_write > cifsi->server_eof) + cifsi->server_eof = end_of_write; +} + +static ssize_t +cifs_write(struct cifsFileInfo *open_file, __u32 pid, const char *write_data, + size_t write_size, loff_t *offset) +{ + int rc = 0; + unsigned int bytes_written = 0; + unsigned int total_written; + struct cifs_tcon *tcon; + struct TCP_Server_Info *server; + unsigned int xid; + struct dentry *dentry = open_file->dentry; + struct cifsInodeInfo *cifsi = CIFS_I(d_inode(dentry)); + struct cifs_io_parms io_parms = {0}; + + cifs_dbg(FYI, "write %zd bytes to offset %lld of %pd\n", + write_size, *offset, dentry); + + tcon = tlink_tcon(open_file->tlink); + server = tcon->ses->server; + + if (!server->ops->sync_write) + return -ENOSYS; + + xid = get_xid(); + + for (total_written = 0; write_size > total_written; + total_written += bytes_written) { + rc = -EAGAIN; + while (rc == -EAGAIN) { + struct kvec iov[2]; + unsigned int len; + + if (open_file->invalidHandle) { + /* we could deadlock if we called + filemap_fdatawait from here so tell + reopen_file not to flush data to + server now */ + rc = cifs_reopen_file(open_file, false); + if (rc != 0) + break; + } + + len = min(server->ops->wp_retry_size(d_inode(dentry)), + (unsigned int)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; + io_parms.pid = pid; + io_parms.tcon = tcon; + io_parms.offset = *offset; + io_parms.length = len; + rc = server->ops->sync_write(xid, &open_file->fid, + &io_parms, &bytes_written, iov, 1); + } + if (rc || (bytes_written == 0)) { + if (total_written) + break; + else { + free_xid(xid); + return rc; + } + } else { + spin_lock(&d_inode(dentry)->i_lock); + cifs_update_eof(cifsi, *offset, bytes_written); + spin_unlock(&d_inode(dentry)->i_lock); + *offset += bytes_written; + } + } + + cifs_stats_bytes_written(tcon, total_written); + + if (total_written > 0) { + spin_lock(&d_inode(dentry)->i_lock); + if (*offset > d_inode(dentry)->i_size) { + i_size_write(d_inode(dentry), *offset); + d_inode(dentry)->i_blocks = (512 - 1 + *offset) >> 9; + } + spin_unlock(&d_inode(dentry)->i_lock); + } + mark_inode_dirty_sync(d_inode(dentry)); + free_xid(xid); + return total_written; +} + +struct cifsFileInfo *find_readable_file(struct cifsInodeInfo *cifs_inode, + bool fsuid_only) +{ + struct cifsFileInfo *open_file = NULL; + struct cifs_sb_info *cifs_sb = CIFS_SB(cifs_inode->netfs.inode.i_sb); + + /* only filter by fsuid on multiuser mounts */ + if (!(cifs_sb->mnt_cifs_flags & CIFS_MOUNT_MULTIUSER)) + fsuid_only = false; + + spin_lock(&cifs_inode->open_file_lock); + /* we could simply get the first_list_entry since write-only entries + are always at the end of the list but since the first entry might + have a close pending, we go through the whole list */ + list_for_each_entry(open_file, &cifs_inode->openFileList, flist) { + if (fsuid_only && !uid_eq(open_file->uid, current_fsuid())) + continue; + if (OPEN_FMODE(open_file->f_flags) & FMODE_READ) { + if ((!open_file->invalidHandle)) { + /* found a good file */ + /* lock it so it will not be closed on us */ + cifsFileInfo_get(open_file); + spin_unlock(&cifs_inode->open_file_lock); + return open_file; + } /* else might as well continue, and look for + another, or simply have the caller reopen it + again rather than trying to fix this handle */ + } else /* write only file */ + break; /* write only files are last so must be done */ + } + spin_unlock(&cifs_inode->open_file_lock); + return NULL; +} + +/* Return -EBADF if no handle is found and general rc otherwise */ +int +cifs_get_writable_file(struct cifsInodeInfo *cifs_inode, int flags, + struct cifsFileInfo **ret_file) +{ + struct cifsFileInfo *open_file, *inv_file = NULL; + struct cifs_sb_info *cifs_sb; + bool any_available = false; + int rc = -EBADF; + unsigned int refind = 0; + bool fsuid_only = flags & FIND_WR_FSUID_ONLY; + bool with_delete = flags & FIND_WR_WITH_DELETE; + *ret_file = NULL; + + /* + * Having a null inode here (because mapping->host was set to zero by + * the VFS or MM) should not happen but we had reports of on oops (due + * to it being zero) during stress testcases so we need to check for it + */ + + if (cifs_inode == NULL) { + cifs_dbg(VFS, "Null inode passed to cifs_writeable_file\n"); + dump_stack(); + return rc; + } + + cifs_sb = CIFS_SB(cifs_inode->netfs.inode.i_sb); + + /* only filter by fsuid on multiuser mounts */ + if (!(cifs_sb->mnt_cifs_flags & CIFS_MOUNT_MULTIUSER)) + fsuid_only = false; + + spin_lock(&cifs_inode->open_file_lock); +refind_writable: + if (refind > MAX_REOPEN_ATT) { + spin_unlock(&cifs_inode->open_file_lock); + return rc; + } + list_for_each_entry(open_file, &cifs_inode->openFileList, flist) { + if (!any_available && open_file->pid != current->tgid) + continue; + if (fsuid_only && !uid_eq(open_file->uid, current_fsuid())) + continue; + if (with_delete && !(open_file->fid.access & DELETE)) + continue; + if (OPEN_FMODE(open_file->f_flags) & FMODE_WRITE) { + if (!open_file->invalidHandle) { + /* found a good writable file */ + cifsFileInfo_get(open_file); + spin_unlock(&cifs_inode->open_file_lock); + *ret_file = open_file; + return 0; + } else { + if (!inv_file) + inv_file = open_file; + } + } + } + /* couldn't find useable FH with same pid, try any available */ + if (!any_available) { + any_available = true; + goto refind_writable; + } + + if (inv_file) { + any_available = false; + cifsFileInfo_get(inv_file); + } + + spin_unlock(&cifs_inode->open_file_lock); + + if (inv_file) { + rc = cifs_reopen_file(inv_file, false); + if (!rc) { + *ret_file = inv_file; + return 0; + } + + spin_lock(&cifs_inode->open_file_lock); + list_move_tail(&inv_file->flist, &cifs_inode->openFileList); + spin_unlock(&cifs_inode->open_file_lock); + cifsFileInfo_put(inv_file); + ++refind; + inv_file = NULL; + spin_lock(&cifs_inode->open_file_lock); + goto refind_writable; + } + + return rc; +} + +struct cifsFileInfo * +find_writable_file(struct cifsInodeInfo *cifs_inode, int flags) +{ + struct cifsFileInfo *cfile; + int rc; + + rc = cifs_get_writable_file(cifs_inode, flags, &cfile); + if (rc) + cifs_dbg(FYI, "Couldn't find writable handle rc=%d\n", rc); + + return cfile; +} + +int +cifs_get_writable_path(struct cifs_tcon *tcon, const char *name, + int flags, + struct cifsFileInfo **ret_file) +{ + struct cifsFileInfo *cfile; + void *page = alloc_dentry_path(); + + *ret_file = NULL; + + spin_lock(&tcon->open_file_lock); + list_for_each_entry(cfile, &tcon->openFileList, tlist) { + struct cifsInodeInfo *cinode; + const char *full_path = build_path_from_dentry(cfile->dentry, page); + if (IS_ERR(full_path)) { + spin_unlock(&tcon->open_file_lock); + free_dentry_path(page); + return PTR_ERR(full_path); + } + if (strcmp(full_path, name)) + continue; + + cinode = CIFS_I(d_inode(cfile->dentry)); + spin_unlock(&tcon->open_file_lock); + free_dentry_path(page); + return cifs_get_writable_file(cinode, flags, ret_file); + } + + spin_unlock(&tcon->open_file_lock); + free_dentry_path(page); + return -ENOENT; +} + +int +cifs_get_readable_path(struct cifs_tcon *tcon, const char *name, + struct cifsFileInfo **ret_file) +{ + struct cifsFileInfo *cfile; + void *page = alloc_dentry_path(); + + *ret_file = NULL; + + spin_lock(&tcon->open_file_lock); + list_for_each_entry(cfile, &tcon->openFileList, tlist) { + struct cifsInodeInfo *cinode; + const char *full_path = build_path_from_dentry(cfile->dentry, page); + if (IS_ERR(full_path)) { + spin_unlock(&tcon->open_file_lock); + free_dentry_path(page); + return PTR_ERR(full_path); + } + if (strcmp(full_path, name)) + continue; + + cinode = CIFS_I(d_inode(cfile->dentry)); + spin_unlock(&tcon->open_file_lock); + free_dentry_path(page); + *ret_file = find_readable_file(cinode, 0); + return *ret_file ? 0 : -ENOENT; + } + + spin_unlock(&tcon->open_file_lock); + free_dentry_path(page); + return -ENOENT; +} + +void +cifs_writedata_release(struct kref *refcount) +{ + struct cifs_writedata *wdata = container_of(refcount, + struct cifs_writedata, refcount); +#ifdef CONFIG_CIFS_SMB_DIRECT + if (wdata->mr) { + smbd_deregister_mr(wdata->mr); + wdata->mr = NULL; + } +#endif + + if (wdata->cfile) + cifsFileInfo_put(wdata->cfile); + + kvfree(wdata->pages); + kfree(wdata); +} + +/* + * Write failed with a retryable error. Resend the write request. It's also + * possible that the page was redirtied so re-clean the page. + */ +static void +cifs_writev_requeue(struct cifs_writedata *wdata) +{ + int i, rc = 0; + struct inode *inode = d_inode(wdata->cfile->dentry); + struct TCP_Server_Info *server; + unsigned int rest_len; + + server = tlink_tcon(wdata->cfile->tlink)->ses->server; + i = 0; + rest_len = wdata->bytes; + do { + struct cifs_writedata *wdata2; + unsigned int j, nr_pages, wsize, tailsz, cur_len; + + wsize = server->ops->wp_retry_size(inode); + if (wsize < rest_len) { + nr_pages = wsize / PAGE_SIZE; + if (!nr_pages) { + rc = -EOPNOTSUPP; + break; + } + cur_len = nr_pages * PAGE_SIZE; + tailsz = PAGE_SIZE; + } else { + nr_pages = DIV_ROUND_UP(rest_len, PAGE_SIZE); + cur_len = rest_len; + tailsz = rest_len - (nr_pages - 1) * PAGE_SIZE; + } + + wdata2 = cifs_writedata_alloc(nr_pages, cifs_writev_complete); + if (!wdata2) { + rc = -ENOMEM; + break; + } + + for (j = 0; j < nr_pages; j++) { + wdata2->pages[j] = wdata->pages[i + j]; + lock_page(wdata2->pages[j]); + clear_page_dirty_for_io(wdata2->pages[j]); + } + + wdata2->sync_mode = wdata->sync_mode; + wdata2->nr_pages = nr_pages; + wdata2->offset = page_offset(wdata2->pages[0]); + wdata2->pagesz = PAGE_SIZE; + wdata2->tailsz = tailsz; + wdata2->bytes = cur_len; + + rc = cifs_get_writable_file(CIFS_I(inode), FIND_WR_ANY, + &wdata2->cfile); + if (!wdata2->cfile) { + cifs_dbg(VFS, "No writable handle to retry writepages rc=%d\n", + rc); + if (!is_retryable_error(rc)) + rc = -EBADF; + } else { + wdata2->pid = wdata2->cfile->pid; + rc = server->ops->async_writev(wdata2, + cifs_writedata_release); + } + + for (j = 0; j < nr_pages; j++) { + unlock_page(wdata2->pages[j]); + if (rc != 0 && !is_retryable_error(rc)) { + SetPageError(wdata2->pages[j]); + end_page_writeback(wdata2->pages[j]); + put_page(wdata2->pages[j]); + } + } + + kref_put(&wdata2->refcount, cifs_writedata_release); + if (rc) { + if (is_retryable_error(rc)) + continue; + i += nr_pages; + break; + } + + rest_len -= cur_len; + i += nr_pages; + } while (i < wdata->nr_pages); + + /* cleanup remaining pages from the original wdata */ + for (; i < wdata->nr_pages; i++) { + SetPageError(wdata->pages[i]); + end_page_writeback(wdata->pages[i]); + put_page(wdata->pages[i]); + } + + if (rc != 0 && !is_retryable_error(rc)) + mapping_set_error(inode->i_mapping, rc); + kref_put(&wdata->refcount, cifs_writedata_release); +} + +void +cifs_writev_complete(struct work_struct *work) +{ + struct cifs_writedata *wdata = container_of(work, + struct cifs_writedata, work); + struct inode *inode = d_inode(wdata->cfile->dentry); + int i = 0; + + if (wdata->result == 0) { + spin_lock(&inode->i_lock); + cifs_update_eof(CIFS_I(inode), wdata->offset, wdata->bytes); + spin_unlock(&inode->i_lock); + cifs_stats_bytes_written(tlink_tcon(wdata->cfile->tlink), + wdata->bytes); + } else if (wdata->sync_mode == WB_SYNC_ALL && wdata->result == -EAGAIN) + return cifs_writev_requeue(wdata); + + for (i = 0; i < wdata->nr_pages; i++) { + struct page *page = wdata->pages[i]; + + if (wdata->result == -EAGAIN) + __set_page_dirty_nobuffers(page); + else if (wdata->result < 0) + SetPageError(page); + end_page_writeback(page); + cifs_readpage_to_fscache(inode, page); + put_page(page); + } + if (wdata->result != -EAGAIN) + mapping_set_error(inode->i_mapping, wdata->result); + kref_put(&wdata->refcount, cifs_writedata_release); +} + +struct cifs_writedata * +cifs_writedata_alloc(unsigned int nr_pages, work_func_t complete) +{ + struct cifs_writedata *writedata = NULL; + struct page **pages = + kcalloc(nr_pages, sizeof(struct page *), GFP_NOFS); + if (pages) { + writedata = cifs_writedata_direct_alloc(pages, complete); + if (!writedata) + kvfree(pages); + } + + return writedata; +} + +struct cifs_writedata * +cifs_writedata_direct_alloc(struct page **pages, work_func_t complete) +{ + struct cifs_writedata *wdata; + + wdata = kzalloc(sizeof(*wdata), GFP_NOFS); + if (wdata != NULL) { + wdata->pages = pages; + kref_init(&wdata->refcount); + INIT_LIST_HEAD(&wdata->list); + init_completion(&wdata->done); + INIT_WORK(&wdata->work, complete); + } + return wdata; +} + + +static int cifs_partialpagewrite(struct page *page, unsigned from, unsigned to) +{ + struct address_space *mapping = page->mapping; + loff_t offset = (loff_t)page->index << PAGE_SHIFT; + char *write_data; + int rc = -EFAULT; + int bytes_written = 0; + struct inode *inode; + struct cifsFileInfo *open_file; + + if (!mapping || !mapping->host) + return -EFAULT; + + inode = page->mapping->host; + + offset += (loff_t)from; + write_data = kmap(page); + write_data += from; + + if ((to > PAGE_SIZE) || (from > to)) { + kunmap(page); + return -EIO; + } + + /* racing with truncate? */ + if (offset > mapping->host->i_size) { + kunmap(page); + return 0; /* don't care */ + } + + /* check to make sure that we are not extending the file */ + if (mapping->host->i_size - offset < (loff_t)to) + to = (unsigned)(mapping->host->i_size - offset); + + rc = cifs_get_writable_file(CIFS_I(mapping->host), FIND_WR_ANY, + &open_file); + if (!rc) { + bytes_written = cifs_write(open_file, open_file->pid, + write_data, to - from, &offset); + cifsFileInfo_put(open_file); + /* Does mm or vfs already set times? */ + inode->i_atime = inode->i_mtime = current_time(inode); + if ((bytes_written > 0) && (offset)) + rc = 0; + else if (bytes_written < 0) + rc = bytes_written; + else + rc = -EFAULT; + } else { + cifs_dbg(FYI, "No writable handle for write page rc=%d\n", rc); + if (!is_retryable_error(rc)) + rc = -EIO; + } + + kunmap(page); + return rc; +} + +static struct cifs_writedata * +wdata_alloc_and_fillpages(pgoff_t tofind, struct address_space *mapping, + pgoff_t end, pgoff_t *index, + unsigned int *found_pages) +{ + struct cifs_writedata *wdata; + + wdata = cifs_writedata_alloc((unsigned int)tofind, + cifs_writev_complete); + if (!wdata) + return NULL; + + *found_pages = find_get_pages_range_tag(mapping, index, end, + PAGECACHE_TAG_DIRTY, tofind, wdata->pages); + return wdata; +} + +static unsigned int +wdata_prepare_pages(struct cifs_writedata *wdata, unsigned int found_pages, + struct address_space *mapping, + struct writeback_control *wbc, + pgoff_t end, pgoff_t *index, pgoff_t *next, bool *done) +{ + unsigned int nr_pages = 0, i; + struct page *page; + + for (i = 0; i < found_pages; i++) { + page = wdata->pages[i]; + /* + * At this point we hold neither the i_pages lock nor the + * page lock: the page may be truncated or invalidated + * (changing page->mapping to NULL), or even swizzled + * back from swapper_space to tmpfs file mapping + */ + + if (nr_pages == 0) + lock_page(page); + else if (!trylock_page(page)) + break; + + if (unlikely(page->mapping != mapping)) { + unlock_page(page); + break; + } + + if (!wbc->range_cyclic && page->index > end) { + *done = true; + 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) || + !clear_page_dirty_for_io(page)) { + unlock_page(page); + break; + } + + /* + * This actually clears the dirty bit in the radix tree. + * See cifs_writepage() for more commentary. + */ + set_page_writeback(page); + if (page_offset(page) >= i_size_read(mapping->host)) { + *done = true; + unlock_page(page); + end_page_writeback(page); + break; + } + + wdata->pages[i] = page; + *next = page->index + 1; + ++nr_pages; + } + + /* reset index to refind any pages skipped */ + if (nr_pages == 0) + *index = wdata->pages[0]->index + 1; + + /* put any pages we aren't going to use */ + for (i = nr_pages; i < found_pages; i++) { + put_page(wdata->pages[i]); + wdata->pages[i] = NULL; + } + + return nr_pages; +} + +static int +wdata_send_pages(struct cifs_writedata *wdata, unsigned int nr_pages, + struct address_space *mapping, struct writeback_control *wbc) +{ + int rc; + + wdata->sync_mode = wbc->sync_mode; + wdata->nr_pages = nr_pages; + wdata->offset = page_offset(wdata->pages[0]); + wdata->pagesz = PAGE_SIZE; + wdata->tailsz = min(i_size_read(mapping->host) - + page_offset(wdata->pages[nr_pages - 1]), + (loff_t)PAGE_SIZE); + wdata->bytes = ((nr_pages - 1) * PAGE_SIZE) + wdata->tailsz; + wdata->pid = wdata->cfile->pid; + + rc = adjust_credits(wdata->server, &wdata->credits, wdata->bytes); + if (rc) + return rc; + + if (wdata->cfile->invalidHandle) + rc = -EAGAIN; + else + rc = wdata->server->ops->async_writev(wdata, + cifs_writedata_release); + + return rc; +} + +static int cifs_writepages(struct address_space *mapping, + struct writeback_control *wbc) +{ + struct inode *inode = mapping->host; + struct cifs_sb_info *cifs_sb = CIFS_SB(inode->i_sb); + struct TCP_Server_Info *server; + bool done = false, scanned = false, range_whole = false; + pgoff_t end, index; + struct cifs_writedata *wdata; + struct cifsFileInfo *cfile = NULL; + int rc = 0; + int saved_rc = 0; + unsigned int xid; + + /* + * If wsize is smaller than the page cache size, default to writing + * one page at a time via cifs_writepage + */ + if (cifs_sb->ctx->wsize < PAGE_SIZE) + return generic_writepages(mapping, wbc); + + xid = get_xid(); + if (wbc->range_cyclic) { + index = mapping->writeback_index; /* Start from prev offset */ + end = -1; + } else { + index = wbc->range_start >> PAGE_SHIFT; + end = wbc->range_end >> PAGE_SHIFT; + if (wbc->range_start == 0 && wbc->range_end == LLONG_MAX) + range_whole = true; + scanned = true; + } + server = cifs_pick_channel(cifs_sb_master_tcon(cifs_sb)->ses); + +retry: + while (!done && index <= end) { + unsigned int i, nr_pages, found_pages, wsize; + pgoff_t next = 0, tofind, saved_index = index; + struct cifs_credits credits_on_stack; + struct cifs_credits *credits = &credits_on_stack; + int get_file_rc = 0; + + if (cfile) + cifsFileInfo_put(cfile); + + rc = cifs_get_writable_file(CIFS_I(inode), FIND_WR_ANY, &cfile); + + /* in case of an error store it to return later */ + if (rc) + get_file_rc = rc; + + rc = server->ops->wait_mtu_credits(server, cifs_sb->ctx->wsize, + &wsize, credits); + if (rc != 0) { + done = true; + break; + } + + tofind = min((wsize / PAGE_SIZE) - 1, end - index) + 1; + + wdata = wdata_alloc_and_fillpages(tofind, mapping, end, &index, + &found_pages); + if (!wdata) { + rc = -ENOMEM; + done = true; + add_credits_and_wake_if(server, credits, 0); + break; + } + + if (found_pages == 0) { + kref_put(&wdata->refcount, cifs_writedata_release); + add_credits_and_wake_if(server, credits, 0); + break; + } + + nr_pages = wdata_prepare_pages(wdata, found_pages, mapping, wbc, + end, &index, &next, &done); + + /* nothing to write? */ + if (nr_pages == 0) { + kref_put(&wdata->refcount, cifs_writedata_release); + add_credits_and_wake_if(server, credits, 0); + continue; + } + + wdata->credits = credits_on_stack; + wdata->cfile = cfile; + wdata->server = server; + cfile = NULL; + + if (!wdata->cfile) { + cifs_dbg(VFS, "No writable handle in writepages rc=%d\n", + get_file_rc); + if (is_retryable_error(get_file_rc)) + rc = get_file_rc; + else + rc = -EBADF; + } else + rc = wdata_send_pages(wdata, nr_pages, mapping, wbc); + + for (i = 0; i < nr_pages; ++i) + unlock_page(wdata->pages[i]); + + /* send failure -- clean up the mess */ + if (rc != 0) { + add_credits_and_wake_if(server, &wdata->credits, 0); + for (i = 0; i < nr_pages; ++i) { + if (is_retryable_error(rc)) + redirty_page_for_writepage(wbc, + wdata->pages[i]); + else + SetPageError(wdata->pages[i]); + end_page_writeback(wdata->pages[i]); + put_page(wdata->pages[i]); + } + if (!is_retryable_error(rc)) + mapping_set_error(mapping, rc); + } + kref_put(&wdata->refcount, cifs_writedata_release); + + if (wbc->sync_mode == WB_SYNC_ALL && rc == -EAGAIN) { + index = saved_index; + continue; + } + + /* Return immediately if we received a signal during writing */ + if (is_interrupt_error(rc)) { + done = true; + break; + } + + if (rc != 0 && saved_rc == 0) + saved_rc = rc; + + wbc->nr_to_write -= nr_pages; + if (wbc->nr_to_write <= 0) + done = true; + + index = next; + } + + 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 = true; + index = 0; + goto retry; + } + + if (saved_rc != 0) + rc = saved_rc; + + if (wbc->range_cyclic || (range_whole && wbc->nr_to_write > 0)) + mapping->writeback_index = index; + + if (cfile) + cifsFileInfo_put(cfile); + free_xid(xid); + /* Indication to update ctime and mtime as close is deferred */ + set_bit(CIFS_INO_MODIFIED_ATTR, &CIFS_I(inode)->flags); + return rc; +} + +static int +cifs_writepage_locked(struct page *page, struct writeback_control *wbc) +{ + int rc; + unsigned int xid; + + xid = get_xid(); +/* BB add check for wbc flags */ + get_page(page); + if (!PageUptodate(page)) + cifs_dbg(FYI, "ppw - page not up to date\n"); + + /* + * Set the "writeback" flag, and clear "dirty" in the radix tree. + * + * A writepage() implementation always needs to do either this, + * or re-dirty the page with "redirty_page_for_writepage()" in + * the case of a failure. + * + * Just unlocking the page will cause the radix tree tag-bits + * to fail to update with the state of the page correctly. + */ + set_page_writeback(page); +retry_write: + rc = cifs_partialpagewrite(page, 0, PAGE_SIZE); + if (is_retryable_error(rc)) { + if (wbc->sync_mode == WB_SYNC_ALL && rc == -EAGAIN) + goto retry_write; + redirty_page_for_writepage(wbc, page); + } else if (rc != 0) { + SetPageError(page); + mapping_set_error(page->mapping, rc); + } else { + SetPageUptodate(page); + } + end_page_writeback(page); + put_page(page); + free_xid(xid); + return rc; +} + +static int cifs_writepage(struct page *page, struct writeback_control *wbc) +{ + int rc = cifs_writepage_locked(page, wbc); + unlock_page(page); + return rc; +} + +static int cifs_write_end(struct file *file, struct address_space *mapping, + loff_t pos, unsigned len, unsigned copied, + struct page *page, void *fsdata) +{ + int rc; + struct inode *inode = mapping->host; + struct cifsFileInfo *cfile = file->private_data; + struct cifs_sb_info *cifs_sb = CIFS_SB(cfile->dentry->d_sb); + __u32 pid; + + if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_RWPIDFORWARD) + pid = cfile->pid; + else + pid = current->tgid; + + cifs_dbg(FYI, "write_end for page %p from pos %lld with %d bytes\n", + page, pos, copied); + + if (PageChecked(page)) { + if (copied == len) + SetPageUptodate(page); + ClearPageChecked(page); + } else if (!PageUptodate(page) && copied == PAGE_SIZE) + SetPageUptodate(page); + + if (!PageUptodate(page)) { + char *page_data; + unsigned offset = pos & (PAGE_SIZE - 1); + unsigned int xid; + + xid = get_xid(); + /* this is probably better than directly calling + partialpage_write since in this function the file handle is + known which we might as well leverage */ + /* BB check if anything else missing out of ppw + such as updating last write time */ + page_data = kmap(page); + rc = cifs_write(cfile, pid, page_data + offset, copied, &pos); + /* if (rc < 0) should we set writebehind rc? */ + kunmap(page); + + free_xid(xid); + } else { + rc = copied; + pos += copied; + set_page_dirty(page); + } + + if (rc > 0) { + spin_lock(&inode->i_lock); + if (pos > inode->i_size) { + i_size_write(inode, pos); + inode->i_blocks = (512 - 1 + pos) >> 9; + } + spin_unlock(&inode->i_lock); + } + + unlock_page(page); + put_page(page); + /* Indication to update ctime and mtime as close is deferred */ + set_bit(CIFS_INO_MODIFIED_ATTR, &CIFS_I(inode)->flags); + + return rc; +} + +int cifs_strict_fsync(struct file *file, loff_t start, loff_t end, + int datasync) +{ + unsigned int xid; + int rc = 0; + struct cifs_tcon *tcon; + struct TCP_Server_Info *server; + struct cifsFileInfo *smbfile = file->private_data; + struct inode *inode = file_inode(file); + struct cifs_sb_info *cifs_sb = CIFS_SB(inode->i_sb); + + rc = file_write_and_wait_range(file, start, end); + if (rc) { + trace_cifs_fsync_err(inode->i_ino, rc); + return rc; + } + + xid = get_xid(); + + cifs_dbg(FYI, "Sync file - name: %pD datasync: 0x%x\n", + file, datasync); + + if (!CIFS_CACHE_READ(CIFS_I(inode))) { + rc = cifs_zap_mapping(inode); + if (rc) { + cifs_dbg(FYI, "rc: %d during invalidate phase\n", rc); + rc = 0; /* don't care about it in fsync */ + } + } + + tcon = tlink_tcon(smbfile->tlink); + if (!(cifs_sb->mnt_cifs_flags & CIFS_MOUNT_NOSSYNC)) { + server = tcon->ses->server; + if (server->ops->flush == NULL) { + rc = -ENOSYS; + goto strict_fsync_exit; + } + + if ((OPEN_FMODE(smbfile->f_flags) & FMODE_WRITE) == 0) { + smbfile = find_writable_file(CIFS_I(inode), FIND_WR_ANY); + if (smbfile) { + rc = server->ops->flush(xid, tcon, &smbfile->fid); + cifsFileInfo_put(smbfile); + } else + cifs_dbg(FYI, "ignore fsync for file not open for write\n"); + } else + rc = server->ops->flush(xid, tcon, &smbfile->fid); + } + +strict_fsync_exit: + free_xid(xid); + return rc; +} + +int cifs_fsync(struct file *file, loff_t start, loff_t end, int datasync) +{ + unsigned int xid; + int rc = 0; + struct cifs_tcon *tcon; + struct TCP_Server_Info *server; + struct cifsFileInfo *smbfile = file->private_data; + struct inode *inode = file_inode(file); + struct cifs_sb_info *cifs_sb = CIFS_FILE_SB(file); + + rc = file_write_and_wait_range(file, start, end); + if (rc) { + trace_cifs_fsync_err(file_inode(file)->i_ino, rc); + return rc; + } + + xid = get_xid(); + + cifs_dbg(FYI, "Sync file - name: %pD datasync: 0x%x\n", + file, datasync); + + tcon = tlink_tcon(smbfile->tlink); + if (!(cifs_sb->mnt_cifs_flags & CIFS_MOUNT_NOSSYNC)) { + server = tcon->ses->server; + if (server->ops->flush == NULL) { + rc = -ENOSYS; + goto fsync_exit; + } + + if ((OPEN_FMODE(smbfile->f_flags) & FMODE_WRITE) == 0) { + smbfile = find_writable_file(CIFS_I(inode), FIND_WR_ANY); + if (smbfile) { + rc = server->ops->flush(xid, tcon, &smbfile->fid); + cifsFileInfo_put(smbfile); + } else + cifs_dbg(FYI, "ignore fsync for file not open for write\n"); + } else + rc = server->ops->flush(xid, tcon, &smbfile->fid); + } + +fsync_exit: + free_xid(xid); + return rc; +} + +/* + * As file closes, flush all cached write data for this inode checking + * for write behind errors. + */ +int cifs_flush(struct file *file, fl_owner_t id) +{ + struct inode *inode = file_inode(file); + int rc = 0; + + if (file->f_mode & FMODE_WRITE) + rc = filemap_write_and_wait(inode->i_mapping); + + cifs_dbg(FYI, "Flush inode %p file %p rc %d\n", inode, file, rc); + if (rc) { + /* get more nuanced writeback errors */ + rc = filemap_check_wb_err(file->f_mapping, 0); + trace_cifs_flush_err(inode->i_ino, rc); + } + return rc; +} + +static int +cifs_write_allocate_pages(struct page **pages, unsigned long num_pages) +{ + int rc = 0; + unsigned long i; + + for (i = 0; i < num_pages; i++) { + pages[i] = alloc_page(GFP_KERNEL|__GFP_HIGHMEM); + if (!pages[i]) { + /* + * save number of pages we have already allocated and + * return with ENOMEM error + */ + num_pages = i; + rc = -ENOMEM; + break; + } + } + + if (rc) { + for (i = 0; i < num_pages; i++) + put_page(pages[i]); + } + return rc; +} + +static inline +size_t get_numpages(const size_t wsize, const size_t len, size_t *cur_len) +{ + size_t num_pages; + size_t clen; + + clen = min_t(const size_t, len, wsize); + num_pages = DIV_ROUND_UP(clen, PAGE_SIZE); + + if (cur_len) + *cur_len = clen; + + return num_pages; +} + +static void +cifs_uncached_writedata_release(struct kref *refcount) +{ + int i; + struct cifs_writedata *wdata = container_of(refcount, + struct cifs_writedata, refcount); + + kref_put(&wdata->ctx->refcount, cifs_aio_ctx_release); + for (i = 0; i < wdata->nr_pages; i++) + put_page(wdata->pages[i]); + cifs_writedata_release(refcount); +} + +static void collect_uncached_write_data(struct cifs_aio_ctx *ctx); + +static void +cifs_uncached_writev_complete(struct work_struct *work) +{ + struct cifs_writedata *wdata = container_of(work, + struct cifs_writedata, work); + struct inode *inode = d_inode(wdata->cfile->dentry); + struct cifsInodeInfo *cifsi = CIFS_I(inode); + + spin_lock(&inode->i_lock); + cifs_update_eof(cifsi, wdata->offset, wdata->bytes); + if (cifsi->server_eof > inode->i_size) + i_size_write(inode, cifsi->server_eof); + spin_unlock(&inode->i_lock); + + complete(&wdata->done); + collect_uncached_write_data(wdata->ctx); + /* the below call can possibly free the last ref to aio ctx */ + kref_put(&wdata->refcount, cifs_uncached_writedata_release); +} + +static int +wdata_fill_from_iovec(struct cifs_writedata *wdata, struct iov_iter *from, + size_t *len, unsigned long *num_pages) +{ + size_t save_len, copied, bytes, cur_len = *len; + unsigned long i, nr_pages = *num_pages; + + save_len = cur_len; + for (i = 0; i < nr_pages; i++) { + bytes = min_t(const size_t, cur_len, PAGE_SIZE); + copied = copy_page_from_iter(wdata->pages[i], 0, bytes, from); + cur_len -= copied; + /* + * If we didn't copy as much as we expected, then that + * may mean we trod into an unmapped area. Stop copying + * at that point. On the next pass through the big + * loop, we'll likely end up getting a zero-length + * write and bailing out of it. + */ + if (copied < bytes) + break; + } + cur_len = save_len - cur_len; + *len = cur_len; + + /* + * If we have no data to send, then that probably means that + * the copy above failed altogether. That's most likely because + * the address in the iovec was bogus. Return -EFAULT and let + * the caller free anything we allocated and bail out. + */ + if (!cur_len) + return -EFAULT; + + /* + * i + 1 now represents the number of pages we actually used in + * the copy phase above. + */ + *num_pages = i + 1; + return 0; +} + +static int +cifs_resend_wdata(struct cifs_writedata *wdata, struct list_head *wdata_list, + struct cifs_aio_ctx *ctx) +{ + unsigned int wsize; + struct cifs_credits credits; + int rc; + struct TCP_Server_Info *server = wdata->server; + + do { + if (wdata->cfile->invalidHandle) { + rc = cifs_reopen_file(wdata->cfile, false); + if (rc == -EAGAIN) + continue; + else if (rc) + break; + } + + + /* + * Wait for credits to resend this wdata. + * Note: we are attempting to resend the whole wdata not in + * segments + */ + do { + rc = server->ops->wait_mtu_credits(server, wdata->bytes, + &wsize, &credits); + if (rc) + goto fail; + + if (wsize < wdata->bytes) { + add_credits_and_wake_if(server, &credits, 0); + msleep(1000); + } + } while (wsize < wdata->bytes); + wdata->credits = credits; + + rc = adjust_credits(server, &wdata->credits, wdata->bytes); + + if (!rc) { + if (wdata->cfile->invalidHandle) + rc = -EAGAIN; + else { +#ifdef CONFIG_CIFS_SMB_DIRECT + if (wdata->mr) { + wdata->mr->need_invalidate = true; + smbd_deregister_mr(wdata->mr); + wdata->mr = NULL; + } +#endif + rc = server->ops->async_writev(wdata, + cifs_uncached_writedata_release); + } + } + + /* If the write was successfully sent, we are done */ + if (!rc) { + list_add_tail(&wdata->list, wdata_list); + return 0; + } + + /* Roll back credits and retry if needed */ + add_credits_and_wake_if(server, &wdata->credits, 0); + } while (rc == -EAGAIN); + +fail: + kref_put(&wdata->refcount, cifs_uncached_writedata_release); + return rc; +} + +static int +cifs_write_from_iter(loff_t offset, size_t len, struct iov_iter *from, + struct cifsFileInfo *open_file, + struct cifs_sb_info *cifs_sb, struct list_head *wdata_list, + struct cifs_aio_ctx *ctx) +{ + int rc = 0; + size_t cur_len; + unsigned long nr_pages, num_pages, i; + struct cifs_writedata *wdata; + struct iov_iter saved_from = *from; + loff_t saved_offset = offset; + pid_t pid; + struct TCP_Server_Info *server; + struct page **pagevec; + size_t start; + unsigned int xid; + + if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_RWPIDFORWARD) + pid = open_file->pid; + else + pid = current->tgid; + + server = cifs_pick_channel(tlink_tcon(open_file->tlink)->ses); + xid = get_xid(); + + do { + unsigned int wsize; + struct cifs_credits credits_on_stack; + struct cifs_credits *credits = &credits_on_stack; + + if (open_file->invalidHandle) { + rc = cifs_reopen_file(open_file, false); + if (rc == -EAGAIN) + continue; + else if (rc) + break; + } + + rc = server->ops->wait_mtu_credits(server, cifs_sb->ctx->wsize, + &wsize, credits); + if (rc) + break; + + cur_len = min_t(const size_t, len, wsize); + + if (ctx->direct_io) { + ssize_t result; + + result = iov_iter_get_pages_alloc2( + from, &pagevec, cur_len, &start); + if (result < 0) { + cifs_dbg(VFS, + "direct_writev couldn't get user pages (rc=%zd) iter type %d iov_offset %zd count %zd\n", + result, iov_iter_type(from), + from->iov_offset, from->count); + dump_stack(); + + rc = result; + add_credits_and_wake_if(server, credits, 0); + break; + } + cur_len = (size_t)result; + + nr_pages = + (cur_len + start + PAGE_SIZE - 1) / PAGE_SIZE; + + wdata = cifs_writedata_direct_alloc(pagevec, + cifs_uncached_writev_complete); + if (!wdata) { + rc = -ENOMEM; + for (i = 0; i < nr_pages; i++) + put_page(pagevec[i]); + kvfree(pagevec); + add_credits_and_wake_if(server, credits, 0); + break; + } + + + wdata->page_offset = start; + wdata->tailsz = + nr_pages > 1 ? + cur_len - (PAGE_SIZE - start) - + (nr_pages - 2) * PAGE_SIZE : + cur_len; + } else { + nr_pages = get_numpages(wsize, len, &cur_len); + wdata = cifs_writedata_alloc(nr_pages, + cifs_uncached_writev_complete); + if (!wdata) { + rc = -ENOMEM; + add_credits_and_wake_if(server, credits, 0); + break; + } + + rc = cifs_write_allocate_pages(wdata->pages, nr_pages); + if (rc) { + kvfree(wdata->pages); + kfree(wdata); + add_credits_and_wake_if(server, credits, 0); + break; + } + + num_pages = nr_pages; + rc = wdata_fill_from_iovec( + wdata, from, &cur_len, &num_pages); + if (rc) { + for (i = 0; i < nr_pages; i++) + put_page(wdata->pages[i]); + kvfree(wdata->pages); + kfree(wdata); + add_credits_and_wake_if(server, credits, 0); + break; + } + + /* + * Bring nr_pages down to the number of pages we + * actually used, and free any pages that we didn't use. + */ + for ( ; nr_pages > num_pages; nr_pages--) + put_page(wdata->pages[nr_pages - 1]); + + wdata->tailsz = cur_len - ((nr_pages - 1) * PAGE_SIZE); + } + + wdata->sync_mode = WB_SYNC_ALL; + wdata->nr_pages = nr_pages; + wdata->offset = (__u64)offset; + wdata->cfile = cifsFileInfo_get(open_file); + wdata->server = server; + wdata->pid = pid; + wdata->bytes = cur_len; + wdata->pagesz = PAGE_SIZE; + wdata->credits = credits_on_stack; + wdata->ctx = ctx; + kref_get(&ctx->refcount); + + rc = adjust_credits(server, &wdata->credits, wdata->bytes); + + if (!rc) { + if (wdata->cfile->invalidHandle) + rc = -EAGAIN; + else + rc = server->ops->async_writev(wdata, + cifs_uncached_writedata_release); + } + + if (rc) { + add_credits_and_wake_if(server, &wdata->credits, 0); + kref_put(&wdata->refcount, + cifs_uncached_writedata_release); + if (rc == -EAGAIN) { + *from = saved_from; + iov_iter_advance(from, offset - saved_offset); + continue; + } + break; + } + + list_add_tail(&wdata->list, wdata_list); + offset += cur_len; + len -= cur_len; + } while (len > 0); + + free_xid(xid); + return rc; +} + +static void collect_uncached_write_data(struct cifs_aio_ctx *ctx) +{ + struct cifs_writedata *wdata, *tmp; + struct cifs_tcon *tcon; + struct cifs_sb_info *cifs_sb; + struct dentry *dentry = ctx->cfile->dentry; + ssize_t rc; + + tcon = tlink_tcon(ctx->cfile->tlink); + cifs_sb = CIFS_SB(dentry->d_sb); + + mutex_lock(&ctx->aio_mutex); + + if (list_empty(&ctx->list)) { + mutex_unlock(&ctx->aio_mutex); + return; + } + + rc = ctx->rc; + /* + * Wait for and collect replies for any successful sends in order of + * increasing offset. Once an error is hit, then return without waiting + * for any more replies. + */ +restart_loop: + list_for_each_entry_safe(wdata, tmp, &ctx->list, list) { + if (!rc) { + if (!try_wait_for_completion(&wdata->done)) { + mutex_unlock(&ctx->aio_mutex); + return; + } + + if (wdata->result) + rc = wdata->result; + else + ctx->total_len += wdata->bytes; + + /* resend call if it's a retryable error */ + if (rc == -EAGAIN) { + struct list_head tmp_list; + struct iov_iter tmp_from = ctx->iter; + + INIT_LIST_HEAD(&tmp_list); + list_del_init(&wdata->list); + + if (ctx->direct_io) + rc = cifs_resend_wdata( + wdata, &tmp_list, ctx); + else { + iov_iter_advance(&tmp_from, + wdata->offset - ctx->pos); + + rc = cifs_write_from_iter(wdata->offset, + wdata->bytes, &tmp_from, + ctx->cfile, cifs_sb, &tmp_list, + ctx); + + kref_put(&wdata->refcount, + cifs_uncached_writedata_release); + } + + list_splice(&tmp_list, &ctx->list); + goto restart_loop; + } + } + list_del_init(&wdata->list); + kref_put(&wdata->refcount, cifs_uncached_writedata_release); + } + + cifs_stats_bytes_written(tcon, ctx->total_len); + set_bit(CIFS_INO_INVALID_MAPPING, &CIFS_I(dentry->d_inode)->flags); + + ctx->rc = (rc == 0) ? ctx->total_len : rc; + + mutex_unlock(&ctx->aio_mutex); + + if (ctx->iocb && ctx->iocb->ki_complete) + ctx->iocb->ki_complete(ctx->iocb, ctx->rc); + else + complete(&ctx->done); +} + +static ssize_t __cifs_writev( + struct kiocb *iocb, struct iov_iter *from, bool direct) +{ + struct file *file = iocb->ki_filp; + ssize_t total_written = 0; + struct cifsFileInfo *cfile; + struct cifs_tcon *tcon; + struct cifs_sb_info *cifs_sb; + struct cifs_aio_ctx *ctx; + struct iov_iter saved_from = *from; + size_t len = iov_iter_count(from); + int rc; + + /* + * iov_iter_get_pages_alloc doesn't work with ITER_KVEC. + * In this case, fall back to non-direct write function. + * this could be improved by getting pages directly in ITER_KVEC + */ + if (direct && iov_iter_is_kvec(from)) { + cifs_dbg(FYI, "use non-direct cifs_writev for kvec I/O\n"); + direct = false; + } + + rc = generic_write_checks(iocb, from); + if (rc <= 0) + return rc; + + cifs_sb = CIFS_FILE_SB(file); + cfile = file->private_data; + tcon = tlink_tcon(cfile->tlink); + + if (!tcon->ses->server->ops->async_writev) + return -ENOSYS; + + ctx = cifs_aio_ctx_alloc(); + if (!ctx) + return -ENOMEM; + + ctx->cfile = cifsFileInfo_get(cfile); + + if (!is_sync_kiocb(iocb)) + ctx->iocb = iocb; + + ctx->pos = iocb->ki_pos; + + if (direct) { + ctx->direct_io = true; + ctx->iter = *from; + ctx->len = len; + } else { + rc = setup_aio_ctx_iter(ctx, from, ITER_SOURCE); + if (rc) { + kref_put(&ctx->refcount, cifs_aio_ctx_release); + return rc; + } + } + + /* grab a lock here due to read response handlers can access ctx */ + mutex_lock(&ctx->aio_mutex); + + rc = cifs_write_from_iter(iocb->ki_pos, ctx->len, &saved_from, + cfile, cifs_sb, &ctx->list, ctx); + + /* + * If at least one write was successfully sent, then discard any rc + * value from the later writes. If the other write succeeds, then + * we'll end up returning whatever was written. If it fails, then + * we'll get a new rc value from that. + */ + if (!list_empty(&ctx->list)) + rc = 0; + + mutex_unlock(&ctx->aio_mutex); + + if (rc) { + kref_put(&ctx->refcount, cifs_aio_ctx_release); + return rc; + } + + if (!is_sync_kiocb(iocb)) { + kref_put(&ctx->refcount, cifs_aio_ctx_release); + return -EIOCBQUEUED; + } + + rc = wait_for_completion_killable(&ctx->done); + if (rc) { + mutex_lock(&ctx->aio_mutex); + ctx->rc = rc = -EINTR; + total_written = ctx->total_len; + mutex_unlock(&ctx->aio_mutex); + } else { + rc = ctx->rc; + total_written = ctx->total_len; + } + + kref_put(&ctx->refcount, cifs_aio_ctx_release); + + if (unlikely(!total_written)) + return rc; + + iocb->ki_pos += total_written; + return total_written; +} + +ssize_t cifs_direct_writev(struct kiocb *iocb, struct iov_iter *from) +{ + struct file *file = iocb->ki_filp; + + cifs_revalidate_mapping(file->f_inode); + return __cifs_writev(iocb, from, true); +} + +ssize_t cifs_user_writev(struct kiocb *iocb, struct iov_iter *from) +{ + return __cifs_writev(iocb, from, false); +} + +static ssize_t +cifs_writev(struct kiocb *iocb, struct iov_iter *from) +{ + struct file *file = iocb->ki_filp; + struct cifsFileInfo *cfile = (struct cifsFileInfo *)file->private_data; + struct inode *inode = file->f_mapping->host; + struct cifsInodeInfo *cinode = CIFS_I(inode); + struct TCP_Server_Info *server = tlink_tcon(cfile->tlink)->ses->server; + ssize_t rc; + + inode_lock(inode); + /* + * We need to hold the sem to be sure nobody modifies lock list + * with a brlock that prevents writing. + */ + down_read(&cinode->lock_sem); + + rc = generic_write_checks(iocb, from); + if (rc <= 0) + goto out; + + if (!cifs_find_lock_conflict(cfile, iocb->ki_pos, iov_iter_count(from), + server->vals->exclusive_lock_type, 0, + NULL, CIFS_WRITE_OP)) + rc = __generic_file_write_iter(iocb, from); + else + rc = -EACCES; +out: + up_read(&cinode->lock_sem); + inode_unlock(inode); + + if (rc > 0) + rc = generic_write_sync(iocb, rc); + return rc; +} + +ssize_t +cifs_strict_writev(struct kiocb *iocb, struct iov_iter *from) +{ + struct inode *inode = file_inode(iocb->ki_filp); + struct cifsInodeInfo *cinode = CIFS_I(inode); + struct cifs_sb_info *cifs_sb = CIFS_SB(inode->i_sb); + struct cifsFileInfo *cfile = (struct cifsFileInfo *) + iocb->ki_filp->private_data; + struct cifs_tcon *tcon = tlink_tcon(cfile->tlink); + ssize_t written; + + written = cifs_get_writer(cinode); + if (written) + return written; + + if (CIFS_CACHE_WRITE(cinode)) { + if (cap_unix(tcon->ses) && + (CIFS_UNIX_FCNTL_CAP & le64_to_cpu(tcon->fsUnixInfo.Capability)) + && ((cifs_sb->mnt_cifs_flags & CIFS_MOUNT_NOPOSIXBRL) == 0)) { + written = generic_file_write_iter(iocb, from); + goto out; + } + written = cifs_writev(iocb, from); + goto out; + } + /* + * For non-oplocked files in strict cache mode we need to write the data + * to the server exactly from the pos to pos+len-1 rather than flush all + * affected pages because it may cause a error with mandatory locks on + * these pages but not on the region from pos to ppos+len-1. + */ + written = cifs_user_writev(iocb, from); + if (CIFS_CACHE_READ(cinode)) { + /* + * We have read level caching and we have just sent a write + * request to the server thus making data in the cache stale. + * Zap the cache and set oplock/lease level to NONE to avoid + * reading stale data from the cache. All subsequent read + * operations will read new data from the server. + */ + cifs_zap_mapping(inode); + cifs_dbg(FYI, "Set Oplock/Lease to NONE for inode=%p after write\n", + inode); + cinode->oplock = 0; + } +out: + cifs_put_writer(cinode); + return written; +} + +static struct cifs_readdata * +cifs_readdata_direct_alloc(struct page **pages, work_func_t complete) +{ + struct cifs_readdata *rdata; + + rdata = kzalloc(sizeof(*rdata), GFP_KERNEL); + if (rdata != NULL) { + rdata->pages = pages; + kref_init(&rdata->refcount); + INIT_LIST_HEAD(&rdata->list); + init_completion(&rdata->done); + INIT_WORK(&rdata->work, complete); + } + + return rdata; +} + +static struct cifs_readdata * +cifs_readdata_alloc(unsigned int nr_pages, work_func_t complete) +{ + struct page **pages = + kcalloc(nr_pages, sizeof(struct page *), GFP_KERNEL); + struct cifs_readdata *ret = NULL; + + if (pages) { + ret = cifs_readdata_direct_alloc(pages, complete); + if (!ret) + kfree(pages); + } + + return ret; +} + +void +cifs_readdata_release(struct kref *refcount) +{ + struct cifs_readdata *rdata = container_of(refcount, + struct cifs_readdata, refcount); +#ifdef CONFIG_CIFS_SMB_DIRECT + if (rdata->mr) { + smbd_deregister_mr(rdata->mr); + rdata->mr = NULL; + } +#endif + if (rdata->cfile) + cifsFileInfo_put(rdata->cfile); + + kvfree(rdata->pages); + kfree(rdata); +} + +static int +cifs_read_allocate_pages(struct cifs_readdata *rdata, unsigned int nr_pages) +{ + int rc = 0; + struct page *page; + unsigned int i; + + for (i = 0; i < nr_pages; i++) { + page = alloc_page(GFP_KERNEL|__GFP_HIGHMEM); + if (!page) { + rc = -ENOMEM; + break; + } + rdata->pages[i] = page; + } + + if (rc) { + unsigned int nr_page_failed = i; + + for (i = 0; i < nr_page_failed; i++) { + put_page(rdata->pages[i]); + rdata->pages[i] = NULL; + } + } + return rc; +} + +static void +cifs_uncached_readdata_release(struct kref *refcount) +{ + struct cifs_readdata *rdata = container_of(refcount, + struct cifs_readdata, refcount); + unsigned int i; + + kref_put(&rdata->ctx->refcount, cifs_aio_ctx_release); + for (i = 0; i < rdata->nr_pages; i++) { + put_page(rdata->pages[i]); + } + cifs_readdata_release(refcount); +} + +/** + * cifs_readdata_to_iov - copy data from pages in response to an iovec + * @rdata: the readdata response with list of pages holding data + * @iter: destination for our data + * + * This function copies data from a list of pages in a readdata response into + * an array of iovecs. It will first calculate where the data should go + * based on the info in the readdata and then copy the data into that spot. + */ +static int +cifs_readdata_to_iov(struct cifs_readdata *rdata, struct iov_iter *iter) +{ + size_t remaining = rdata->got_bytes; + unsigned int i; + + for (i = 0; i < rdata->nr_pages; i++) { + struct page *page = rdata->pages[i]; + size_t copy = min_t(size_t, remaining, PAGE_SIZE); + size_t written; + + if (unlikely(iov_iter_is_pipe(iter))) { + void *addr = kmap_atomic(page); + + written = copy_to_iter(addr, copy, iter); + kunmap_atomic(addr); + } else + written = copy_page_to_iter(page, 0, copy, iter); + remaining -= written; + if (written < copy && iov_iter_count(iter) > 0) + break; + } + return remaining ? -EFAULT : 0; +} + +static void collect_uncached_read_data(struct cifs_aio_ctx *ctx); + +static void +cifs_uncached_readv_complete(struct work_struct *work) +{ + struct cifs_readdata *rdata = container_of(work, + struct cifs_readdata, work); + + complete(&rdata->done); + collect_uncached_read_data(rdata->ctx); + /* the below call can possibly free the last ref to aio ctx */ + kref_put(&rdata->refcount, cifs_uncached_readdata_release); +} + +static int +uncached_fill_pages(struct TCP_Server_Info *server, + struct cifs_readdata *rdata, struct iov_iter *iter, + unsigned int len) +{ + int result = 0; + unsigned int i; + unsigned int nr_pages = rdata->nr_pages; + unsigned int page_offset = rdata->page_offset; + + rdata->got_bytes = 0; + rdata->tailsz = PAGE_SIZE; + for (i = 0; i < nr_pages; i++) { + struct page *page = rdata->pages[i]; + size_t n; + unsigned int segment_size = rdata->pagesz; + + if (i == 0) + segment_size -= page_offset; + else + page_offset = 0; + + + if (len <= 0) { + /* no need to hold page hostage */ + rdata->pages[i] = NULL; + rdata->nr_pages--; + put_page(page); + continue; + } + + n = len; + if (len >= segment_size) + /* enough data to fill the page */ + n = segment_size; + else + rdata->tailsz = len; + len -= n; + + if (iter) + result = copy_page_from_iter( + page, page_offset, n, iter); +#ifdef CONFIG_CIFS_SMB_DIRECT + else if (rdata->mr) + result = n; +#endif + else + result = cifs_read_page_from_socket( + server, page, page_offset, n); + if (result < 0) + break; + + rdata->got_bytes += result; + } + + return result != -ECONNABORTED && rdata->got_bytes > 0 ? + rdata->got_bytes : result; +} + +static int +cifs_uncached_read_into_pages(struct TCP_Server_Info *server, + struct cifs_readdata *rdata, unsigned int len) +{ + return uncached_fill_pages(server, rdata, NULL, len); +} + +static int +cifs_uncached_copy_into_pages(struct TCP_Server_Info *server, + struct cifs_readdata *rdata, + struct iov_iter *iter) +{ + return uncached_fill_pages(server, rdata, iter, iter->count); +} + +static int cifs_resend_rdata(struct cifs_readdata *rdata, + struct list_head *rdata_list, + struct cifs_aio_ctx *ctx) +{ + unsigned int rsize; + struct cifs_credits credits; + int rc; + struct TCP_Server_Info *server; + + /* XXX: should we pick a new channel here? */ + server = rdata->server; + + do { + if (rdata->cfile->invalidHandle) { + rc = cifs_reopen_file(rdata->cfile, true); + if (rc == -EAGAIN) + continue; + else if (rc) + break; + } + + /* + * Wait for credits to resend this rdata. + * Note: we are attempting to resend the whole rdata not in + * segments + */ + do { + rc = server->ops->wait_mtu_credits(server, rdata->bytes, + &rsize, &credits); + + if (rc) + goto fail; + + if (rsize < rdata->bytes) { + add_credits_and_wake_if(server, &credits, 0); + msleep(1000); + } + } while (rsize < rdata->bytes); + rdata->credits = credits; + + rc = adjust_credits(server, &rdata->credits, rdata->bytes); + if (!rc) { + if (rdata->cfile->invalidHandle) + rc = -EAGAIN; + else { +#ifdef CONFIG_CIFS_SMB_DIRECT + if (rdata->mr) { + rdata->mr->need_invalidate = true; + smbd_deregister_mr(rdata->mr); + rdata->mr = NULL; + } +#endif + rc = server->ops->async_readv(rdata); + } + } + + /* If the read was successfully sent, we are done */ + if (!rc) { + /* Add to aio pending list */ + list_add_tail(&rdata->list, rdata_list); + return 0; + } + + /* Roll back credits and retry if needed */ + add_credits_and_wake_if(server, &rdata->credits, 0); + } while (rc == -EAGAIN); + +fail: + kref_put(&rdata->refcount, cifs_uncached_readdata_release); + return rc; +} + +static int +cifs_send_async_read(loff_t offset, size_t len, struct cifsFileInfo *open_file, + struct cifs_sb_info *cifs_sb, struct list_head *rdata_list, + struct cifs_aio_ctx *ctx) +{ + struct cifs_readdata *rdata; + unsigned int npages, rsize; + struct cifs_credits credits_on_stack; + struct cifs_credits *credits = &credits_on_stack; + size_t cur_len; + int rc; + pid_t pid; + struct TCP_Server_Info *server; + struct page **pagevec; + size_t start; + struct iov_iter direct_iov = ctx->iter; + + server = cifs_pick_channel(tlink_tcon(open_file->tlink)->ses); + + if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_RWPIDFORWARD) + pid = open_file->pid; + else + pid = current->tgid; + + if (ctx->direct_io) + iov_iter_advance(&direct_iov, offset - ctx->pos); + + do { + if (open_file->invalidHandle) { + rc = cifs_reopen_file(open_file, true); + if (rc == -EAGAIN) + continue; + else if (rc) + break; + } + + if (cifs_sb->ctx->rsize == 0) + cifs_sb->ctx->rsize = + server->ops->negotiate_rsize(tlink_tcon(open_file->tlink), + cifs_sb->ctx); + + rc = server->ops->wait_mtu_credits(server, cifs_sb->ctx->rsize, + &rsize, credits); + if (rc) + break; + + cur_len = min_t(const size_t, len, rsize); + + if (ctx->direct_io) { + ssize_t result; + + result = iov_iter_get_pages_alloc2( + &direct_iov, &pagevec, + cur_len, &start); + if (result < 0) { + cifs_dbg(VFS, + "Couldn't get user pages (rc=%zd) iter type %d iov_offset %zd count %zd\n", + result, iov_iter_type(&direct_iov), + direct_iov.iov_offset, + direct_iov.count); + dump_stack(); + + rc = result; + add_credits_and_wake_if(server, credits, 0); + break; + } + cur_len = (size_t)result; + + rdata = cifs_readdata_direct_alloc( + pagevec, cifs_uncached_readv_complete); + if (!rdata) { + add_credits_and_wake_if(server, credits, 0); + rc = -ENOMEM; + break; + } + + npages = (cur_len + start + PAGE_SIZE-1) / PAGE_SIZE; + rdata->page_offset = start; + rdata->tailsz = npages > 1 ? + cur_len-(PAGE_SIZE-start)-(npages-2)*PAGE_SIZE : + cur_len; + + } else { + + npages = DIV_ROUND_UP(cur_len, PAGE_SIZE); + /* allocate a readdata struct */ + rdata = cifs_readdata_alloc(npages, + cifs_uncached_readv_complete); + if (!rdata) { + add_credits_and_wake_if(server, credits, 0); + rc = -ENOMEM; + break; + } + + rc = cifs_read_allocate_pages(rdata, npages); + if (rc) { + kvfree(rdata->pages); + kfree(rdata); + add_credits_and_wake_if(server, credits, 0); + break; + } + + rdata->tailsz = PAGE_SIZE; + } + + rdata->server = server; + rdata->cfile = cifsFileInfo_get(open_file); + rdata->nr_pages = npages; + rdata->offset = offset; + rdata->bytes = cur_len; + rdata->pid = pid; + rdata->pagesz = PAGE_SIZE; + rdata->read_into_pages = cifs_uncached_read_into_pages; + rdata->copy_into_pages = cifs_uncached_copy_into_pages; + rdata->credits = credits_on_stack; + rdata->ctx = ctx; + kref_get(&ctx->refcount); + + rc = adjust_credits(server, &rdata->credits, rdata->bytes); + + if (!rc) { + if (rdata->cfile->invalidHandle) + rc = -EAGAIN; + else + rc = server->ops->async_readv(rdata); + } + + if (rc) { + add_credits_and_wake_if(server, &rdata->credits, 0); + kref_put(&rdata->refcount, + cifs_uncached_readdata_release); + if (rc == -EAGAIN) { + iov_iter_revert(&direct_iov, cur_len); + continue; + } + break; + } + + list_add_tail(&rdata->list, rdata_list); + offset += cur_len; + len -= cur_len; + } while (len > 0); + + return rc; +} + +static void +collect_uncached_read_data(struct cifs_aio_ctx *ctx) +{ + struct cifs_readdata *rdata, *tmp; + struct iov_iter *to = &ctx->iter; + struct cifs_sb_info *cifs_sb; + int rc; + + cifs_sb = CIFS_SB(ctx->cfile->dentry->d_sb); + + mutex_lock(&ctx->aio_mutex); + + if (list_empty(&ctx->list)) { + mutex_unlock(&ctx->aio_mutex); + return; + } + + rc = ctx->rc; + /* the loop below should proceed in the order of increasing offsets */ +again: + list_for_each_entry_safe(rdata, tmp, &ctx->list, list) { + if (!rc) { + if (!try_wait_for_completion(&rdata->done)) { + mutex_unlock(&ctx->aio_mutex); + return; + } + + if (rdata->result == -EAGAIN) { + /* resend call if it's a retryable error */ + struct list_head tmp_list; + unsigned int got_bytes = rdata->got_bytes; + + list_del_init(&rdata->list); + INIT_LIST_HEAD(&tmp_list); + + /* + * Got a part of data and then reconnect has + * happened -- fill the buffer and continue + * reading. + */ + if (got_bytes && got_bytes < rdata->bytes) { + rc = 0; + if (!ctx->direct_io) + rc = cifs_readdata_to_iov(rdata, to); + if (rc) { + kref_put(&rdata->refcount, + cifs_uncached_readdata_release); + continue; + } + } + + if (ctx->direct_io) { + /* + * Re-use rdata as this is a + * direct I/O + */ + rc = cifs_resend_rdata( + rdata, + &tmp_list, ctx); + } else { + rc = cifs_send_async_read( + rdata->offset + got_bytes, + rdata->bytes - got_bytes, + rdata->cfile, cifs_sb, + &tmp_list, ctx); + + kref_put(&rdata->refcount, + cifs_uncached_readdata_release); + } + + list_splice(&tmp_list, &ctx->list); + + goto again; + } else if (rdata->result) + rc = rdata->result; + else if (!ctx->direct_io) + rc = cifs_readdata_to_iov(rdata, to); + + /* if there was a short read -- discard anything left */ + if (rdata->got_bytes && rdata->got_bytes < rdata->bytes) + rc = -ENODATA; + + ctx->total_len += rdata->got_bytes; + } + list_del_init(&rdata->list); + kref_put(&rdata->refcount, cifs_uncached_readdata_release); + } + + if (!ctx->direct_io) + ctx->total_len = ctx->len - iov_iter_count(to); + + /* mask nodata case */ + if (rc == -ENODATA) + rc = 0; + + ctx->rc = (rc == 0) ? (ssize_t)ctx->total_len : rc; + + mutex_unlock(&ctx->aio_mutex); + + if (ctx->iocb && ctx->iocb->ki_complete) + ctx->iocb->ki_complete(ctx->iocb, ctx->rc); + else + complete(&ctx->done); +} + +static ssize_t __cifs_readv( + struct kiocb *iocb, struct iov_iter *to, bool direct) +{ + size_t len; + struct file *file = iocb->ki_filp; + struct cifs_sb_info *cifs_sb; + struct cifsFileInfo *cfile; + struct cifs_tcon *tcon; + ssize_t rc, total_read = 0; + loff_t offset = iocb->ki_pos; + struct cifs_aio_ctx *ctx; + + /* + * iov_iter_get_pages_alloc() doesn't work with ITER_KVEC, + * fall back to data copy read path + * this could be improved by getting pages directly in ITER_KVEC + */ + if (direct && iov_iter_is_kvec(to)) { + cifs_dbg(FYI, "use non-direct cifs_user_readv for kvec I/O\n"); + direct = false; + } + + len = iov_iter_count(to); + if (!len) + return 0; + + cifs_sb = CIFS_FILE_SB(file); + cfile = file->private_data; + tcon = tlink_tcon(cfile->tlink); + + if (!tcon->ses->server->ops->async_readv) + return -ENOSYS; + + if ((file->f_flags & O_ACCMODE) == O_WRONLY) + cifs_dbg(FYI, "attempting read on write only file instance\n"); + + ctx = cifs_aio_ctx_alloc(); + if (!ctx) + return -ENOMEM; + + ctx->cfile = cifsFileInfo_get(cfile); + + if (!is_sync_kiocb(iocb)) + ctx->iocb = iocb; + + if (user_backed_iter(to)) + ctx->should_dirty = true; + + if (direct) { + ctx->pos = offset; + ctx->direct_io = true; + ctx->iter = *to; + ctx->len = len; + } else { + rc = setup_aio_ctx_iter(ctx, to, ITER_DEST); + if (rc) { + kref_put(&ctx->refcount, cifs_aio_ctx_release); + return rc; + } + len = ctx->len; + } + + if (direct) { + rc = filemap_write_and_wait_range(file->f_inode->i_mapping, + offset, offset + len - 1); + if (rc) { + kref_put(&ctx->refcount, cifs_aio_ctx_release); + return -EAGAIN; + } + } + + /* grab a lock here due to read response handlers can access ctx */ + mutex_lock(&ctx->aio_mutex); + + rc = cifs_send_async_read(offset, len, cfile, cifs_sb, &ctx->list, ctx); + + /* if at least one read request send succeeded, then reset rc */ + if (!list_empty(&ctx->list)) + rc = 0; + + mutex_unlock(&ctx->aio_mutex); + + if (rc) { + kref_put(&ctx->refcount, cifs_aio_ctx_release); + return rc; + } + + if (!is_sync_kiocb(iocb)) { + kref_put(&ctx->refcount, cifs_aio_ctx_release); + return -EIOCBQUEUED; + } + + rc = wait_for_completion_killable(&ctx->done); + if (rc) { + mutex_lock(&ctx->aio_mutex); + ctx->rc = rc = -EINTR; + total_read = ctx->total_len; + mutex_unlock(&ctx->aio_mutex); + } else { + rc = ctx->rc; + total_read = ctx->total_len; + } + + kref_put(&ctx->refcount, cifs_aio_ctx_release); + + if (total_read) { + iocb->ki_pos += total_read; + return total_read; + } + return rc; +} + +ssize_t cifs_direct_readv(struct kiocb *iocb, struct iov_iter *to) +{ + return __cifs_readv(iocb, to, true); +} + +ssize_t cifs_user_readv(struct kiocb *iocb, struct iov_iter *to) +{ + return __cifs_readv(iocb, to, false); +} + +ssize_t +cifs_strict_readv(struct kiocb *iocb, struct iov_iter *to) +{ + struct inode *inode = file_inode(iocb->ki_filp); + struct cifsInodeInfo *cinode = CIFS_I(inode); + struct cifs_sb_info *cifs_sb = CIFS_SB(inode->i_sb); + struct cifsFileInfo *cfile = (struct cifsFileInfo *) + iocb->ki_filp->private_data; + struct cifs_tcon *tcon = tlink_tcon(cfile->tlink); + int rc = -EACCES; + + /* + * In strict cache mode we need to read from the server all the time + * if we don't have level II oplock because the server can delay mtime + * change - so we can't make a decision about inode invalidating. + * And we can also fail with pagereading if there are mandatory locks + * on pages affected by this read but not on the region from pos to + * pos+len-1. + */ + if (!CIFS_CACHE_READ(cinode)) + return cifs_user_readv(iocb, to); + + if (cap_unix(tcon->ses) && + (CIFS_UNIX_FCNTL_CAP & le64_to_cpu(tcon->fsUnixInfo.Capability)) && + ((cifs_sb->mnt_cifs_flags & CIFS_MOUNT_NOPOSIXBRL) == 0)) + return generic_file_read_iter(iocb, to); + + /* + * We need to hold the sem to be sure nobody modifies lock list + * with a brlock that prevents reading. + */ + down_read(&cinode->lock_sem); + if (!cifs_find_lock_conflict(cfile, iocb->ki_pos, iov_iter_count(to), + tcon->ses->server->vals->shared_lock_type, + 0, NULL, CIFS_READ_OP)) + rc = generic_file_read_iter(iocb, to); + up_read(&cinode->lock_sem); + return rc; +} + +static ssize_t +cifs_read(struct file *file, char *read_data, size_t read_size, loff_t *offset) +{ + int rc = -EACCES; + unsigned int bytes_read = 0; + unsigned int total_read; + unsigned int current_read_size; + unsigned int rsize; + struct cifs_sb_info *cifs_sb; + struct cifs_tcon *tcon; + struct TCP_Server_Info *server; + unsigned int xid; + char *cur_offset; + struct cifsFileInfo *open_file; + struct cifs_io_parms io_parms = {0}; + int buf_type = CIFS_NO_BUFFER; + __u32 pid; + + xid = get_xid(); + cifs_sb = CIFS_FILE_SB(file); + + /* FIXME: set up handlers for larger reads and/or convert to async */ + rsize = min_t(unsigned int, cifs_sb->ctx->rsize, CIFSMaxBufSize); + + if (file->private_data == NULL) { + rc = -EBADF; + free_xid(xid); + return rc; + } + open_file = file->private_data; + tcon = tlink_tcon(open_file->tlink); + server = cifs_pick_channel(tcon->ses); + + if (!server->ops->sync_read) { + free_xid(xid); + return -ENOSYS; + } + + if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_RWPIDFORWARD) + pid = open_file->pid; + else + pid = current->tgid; + + if ((file->f_flags & O_ACCMODE) == O_WRONLY) + cifs_dbg(FYI, "attempting read on write only file instance\n"); + + for (total_read = 0, cur_offset = read_data; read_size > total_read; + total_read += bytes_read, cur_offset += bytes_read) { + do { + current_read_size = min_t(uint, read_size - total_read, + rsize); + /* + * For windows me and 9x we do not want to request more + * than it negotiated since it will refuse the read + * then. + */ + if (!(tcon->ses->capabilities & + tcon->ses->server->vals->cap_large_files)) { + current_read_size = min_t(uint, + current_read_size, CIFSMaxBufSize); + } + if (open_file->invalidHandle) { + rc = cifs_reopen_file(open_file, true); + if (rc != 0) + break; + } + io_parms.pid = pid; + io_parms.tcon = tcon; + io_parms.offset = *offset; + io_parms.length = current_read_size; + io_parms.server = server; + rc = server->ops->sync_read(xid, &open_file->fid, &io_parms, + &bytes_read, &cur_offset, + &buf_type); + } while (rc == -EAGAIN); + + if (rc || (bytes_read == 0)) { + if (total_read) { + break; + } else { + free_xid(xid); + return rc; + } + } else { + cifs_stats_bytes_read(tcon, total_read); + *offset += bytes_read; + } + } + free_xid(xid); + return total_read; +} + +/* + * If the page is mmap'ed into a process' page tables, then we need to make + * sure that it doesn't change while being written back. + */ +static vm_fault_t +cifs_page_mkwrite(struct vm_fault *vmf) +{ + struct page *page = vmf->page; + + /* Wait for the page to be written to the cache before we allow it to + * be modified. We then assume the entire page will need writing back. + */ +#ifdef CONFIG_CIFS_FSCACHE + if (PageFsCache(page) && + wait_on_page_fscache_killable(page) < 0) + return VM_FAULT_RETRY; +#endif + + wait_on_page_writeback(page); + + if (lock_page_killable(page) < 0) + return VM_FAULT_RETRY; + return VM_FAULT_LOCKED; +} + +static const struct vm_operations_struct cifs_file_vm_ops = { + .fault = filemap_fault, + .map_pages = filemap_map_pages, + .page_mkwrite = cifs_page_mkwrite, +}; + +int cifs_file_strict_mmap(struct file *file, struct vm_area_struct *vma) +{ + int xid, rc = 0; + struct inode *inode = file_inode(file); + + xid = get_xid(); + + if (!CIFS_CACHE_READ(CIFS_I(inode))) + rc = cifs_zap_mapping(inode); + if (!rc) + rc = generic_file_mmap(file, vma); + if (!rc) + vma->vm_ops = &cifs_file_vm_ops; + + free_xid(xid); + return rc; +} + +int cifs_file_mmap(struct file *file, struct vm_area_struct *vma) +{ + int rc, xid; + + xid = get_xid(); + + rc = cifs_revalidate_file(file); + if (rc) + cifs_dbg(FYI, "Validation prior to mmap failed, error=%d\n", + rc); + if (!rc) + rc = generic_file_mmap(file, vma); + if (!rc) + vma->vm_ops = &cifs_file_vm_ops; + + free_xid(xid); + return rc; +} + +static void +cifs_readv_complete(struct work_struct *work) +{ + unsigned int i, got_bytes; + struct cifs_readdata *rdata = container_of(work, + struct cifs_readdata, work); + + got_bytes = rdata->got_bytes; + for (i = 0; i < rdata->nr_pages; i++) { + struct page *page = rdata->pages[i]; + + if (rdata->result == 0 || + (rdata->result == -EAGAIN && got_bytes)) { + flush_dcache_page(page); + SetPageUptodate(page); + } else + SetPageError(page); + + if (rdata->result == 0 || + (rdata->result == -EAGAIN && got_bytes)) + cifs_readpage_to_fscache(rdata->mapping->host, page); + + unlock_page(page); + + got_bytes -= min_t(unsigned int, PAGE_SIZE, got_bytes); + + put_page(page); + rdata->pages[i] = NULL; + } + kref_put(&rdata->refcount, cifs_readdata_release); +} + +static int +readpages_fill_pages(struct TCP_Server_Info *server, + struct cifs_readdata *rdata, struct iov_iter *iter, + unsigned int len) +{ + int result = 0; + unsigned int i; + u64 eof; + pgoff_t eof_index; + unsigned int nr_pages = rdata->nr_pages; + unsigned int page_offset = rdata->page_offset; + + /* determine the eof that the server (probably) has */ + eof = CIFS_I(rdata->mapping->host)->server_eof; + eof_index = eof ? (eof - 1) >> PAGE_SHIFT : 0; + cifs_dbg(FYI, "eof=%llu eof_index=%lu\n", eof, eof_index); + + rdata->got_bytes = 0; + rdata->tailsz = PAGE_SIZE; + for (i = 0; i < nr_pages; i++) { + struct page *page = rdata->pages[i]; + unsigned int to_read = rdata->pagesz; + size_t n; + + if (i == 0) + to_read -= page_offset; + else + page_offset = 0; + + n = to_read; + + if (len >= to_read) { + len -= to_read; + } else if (len > 0) { + /* enough for partial page, fill and zero the rest */ + zero_user(page, len + page_offset, to_read - len); + n = rdata->tailsz = len; + len = 0; + } else if (page->index > eof_index) { + /* + * The VFS will not try to do readahead past the + * i_size, but it's possible that we have outstanding + * writes with gaps in the middle and the i_size hasn't + * caught up yet. Populate those with zeroed out pages + * to prevent the VFS from repeatedly attempting to + * fill them until the writes are flushed. + */ + zero_user(page, 0, PAGE_SIZE); + flush_dcache_page(page); + SetPageUptodate(page); + unlock_page(page); + put_page(page); + rdata->pages[i] = NULL; + rdata->nr_pages--; + continue; + } else { + /* no need to hold page hostage */ + unlock_page(page); + put_page(page); + rdata->pages[i] = NULL; + rdata->nr_pages--; + continue; + } + + if (iter) + result = copy_page_from_iter( + page, page_offset, n, iter); +#ifdef CONFIG_CIFS_SMB_DIRECT + else if (rdata->mr) + result = n; +#endif + else + result = cifs_read_page_from_socket( + server, page, page_offset, n); + if (result < 0) + break; + + rdata->got_bytes += result; + } + + return result != -ECONNABORTED && rdata->got_bytes > 0 ? + rdata->got_bytes : result; +} + +static int +cifs_readpages_read_into_pages(struct TCP_Server_Info *server, + struct cifs_readdata *rdata, unsigned int len) +{ + return readpages_fill_pages(server, rdata, NULL, len); +} + +static int +cifs_readpages_copy_into_pages(struct TCP_Server_Info *server, + struct cifs_readdata *rdata, + struct iov_iter *iter) +{ + return readpages_fill_pages(server, rdata, iter, iter->count); +} + +static void cifs_readahead(struct readahead_control *ractl) +{ + int rc; + struct cifsFileInfo *open_file = ractl->file->private_data; + struct cifs_sb_info *cifs_sb = CIFS_FILE_SB(ractl->file); + struct TCP_Server_Info *server; + pid_t pid; + unsigned int xid, nr_pages, last_batch_size = 0, cache_nr_pages = 0; + pgoff_t next_cached = ULONG_MAX; + bool caching = fscache_cookie_enabled(cifs_inode_cookie(ractl->mapping->host)) && + cifs_inode_cookie(ractl->mapping->host)->cache_priv; + bool check_cache = caching; + + xid = get_xid(); + + if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_RWPIDFORWARD) + pid = open_file->pid; + else + pid = current->tgid; + + rc = 0; + server = cifs_pick_channel(tlink_tcon(open_file->tlink)->ses); + + cifs_dbg(FYI, "%s: file=%p mapping=%p num_pages=%u\n", + __func__, ractl->file, ractl->mapping, readahead_count(ractl)); + + /* + * Chop the readahead request up into rsize-sized read requests. + */ + while ((nr_pages = readahead_count(ractl) - last_batch_size)) { + unsigned int i, got, rsize; + struct page *page; + struct cifs_readdata *rdata; + struct cifs_credits credits_on_stack; + struct cifs_credits *credits = &credits_on_stack; + pgoff_t index = readahead_index(ractl) + last_batch_size; + + /* + * Find out if we have anything cached in the range of + * interest, and if so, where the next chunk of cached data is. + */ + if (caching) { + if (check_cache) { + rc = cifs_fscache_query_occupancy( + ractl->mapping->host, index, nr_pages, + &next_cached, &cache_nr_pages); + if (rc < 0) + caching = false; + check_cache = false; + } + + if (index == next_cached) { + /* + * TODO: Send a whole batch of pages to be read + * by the cache. + */ + struct folio *folio = readahead_folio(ractl); + + last_batch_size = folio_nr_pages(folio); + if (cifs_readpage_from_fscache(ractl->mapping->host, + &folio->page) < 0) { + /* + * TODO: Deal with cache read failure + * here, but for the moment, delegate + * that to readpage. + */ + caching = false; + } + folio_unlock(folio); + next_cached++; + cache_nr_pages--; + if (cache_nr_pages == 0) + check_cache = true; + continue; + } + } + + if (open_file->invalidHandle) { + rc = cifs_reopen_file(open_file, true); + if (rc) { + if (rc == -EAGAIN) + continue; + break; + } + } + + if (cifs_sb->ctx->rsize == 0) + cifs_sb->ctx->rsize = + server->ops->negotiate_rsize(tlink_tcon(open_file->tlink), + cifs_sb->ctx); + + rc = server->ops->wait_mtu_credits(server, cifs_sb->ctx->rsize, + &rsize, credits); + if (rc) + break; + nr_pages = min_t(size_t, rsize / PAGE_SIZE, readahead_count(ractl)); + nr_pages = min_t(size_t, nr_pages, next_cached - index); + + /* + * Give up immediately if rsize is too small to read an entire + * page. The VFS will fall back to readpage. We should never + * reach this point however since we set ra_pages to 0 when the + * rsize is smaller than a cache page. + */ + if (unlikely(!nr_pages)) { + add_credits_and_wake_if(server, credits, 0); + break; + } + + rdata = cifs_readdata_alloc(nr_pages, cifs_readv_complete); + if (!rdata) { + /* best to give up if we're out of mem */ + add_credits_and_wake_if(server, credits, 0); + break; + } + + got = __readahead_batch(ractl, rdata->pages, nr_pages); + if (got != nr_pages) { + pr_warn("__readahead_batch() returned %u/%u\n", + got, nr_pages); + nr_pages = got; + } + + rdata->nr_pages = nr_pages; + rdata->bytes = readahead_batch_length(ractl); + rdata->cfile = cifsFileInfo_get(open_file); + rdata->server = server; + rdata->mapping = ractl->mapping; + rdata->offset = readahead_pos(ractl); + rdata->pid = pid; + rdata->pagesz = PAGE_SIZE; + rdata->tailsz = PAGE_SIZE; + rdata->read_into_pages = cifs_readpages_read_into_pages; + rdata->copy_into_pages = cifs_readpages_copy_into_pages; + rdata->credits = credits_on_stack; + + rc = adjust_credits(server, &rdata->credits, rdata->bytes); + if (!rc) { + if (rdata->cfile->invalidHandle) + rc = -EAGAIN; + else + rc = server->ops->async_readv(rdata); + } + + if (rc) { + add_credits_and_wake_if(server, &rdata->credits, 0); + for (i = 0; i < rdata->nr_pages; i++) { + page = rdata->pages[i]; + unlock_page(page); + put_page(page); + } + /* Fallback to the readpage in error/reconnect cases */ + kref_put(&rdata->refcount, cifs_readdata_release); + break; + } + + kref_put(&rdata->refcount, cifs_readdata_release); + last_batch_size = nr_pages; + } + + free_xid(xid); +} + +/* + * cifs_readpage_worker must be called with the page pinned + */ +static int cifs_readpage_worker(struct file *file, struct page *page, + loff_t *poffset) +{ + char *read_data; + int rc; + + /* Is the page cached? */ + rc = cifs_readpage_from_fscache(file_inode(file), page); + if (rc == 0) + goto read_complete; + + read_data = kmap(page); + /* for reads over a certain size could initiate async read ahead */ + + rc = cifs_read(file, read_data, PAGE_SIZE, poffset); + + if (rc < 0) + goto io_error; + else + cifs_dbg(FYI, "Bytes read %d\n", rc); + + /* we do not want atime to be less than mtime, it broke some apps */ + file_inode(file)->i_atime = current_time(file_inode(file)); + if (timespec64_compare(&(file_inode(file)->i_atime), &(file_inode(file)->i_mtime))) + file_inode(file)->i_atime = file_inode(file)->i_mtime; + else + file_inode(file)->i_atime = current_time(file_inode(file)); + + if (PAGE_SIZE > rc) + memset(read_data + rc, 0, PAGE_SIZE - rc); + + flush_dcache_page(page); + SetPageUptodate(page); + + /* send this page to the cache */ + cifs_readpage_to_fscache(file_inode(file), page); + + rc = 0; + +io_error: + kunmap(page); + unlock_page(page); + +read_complete: + return rc; +} + +static int cifs_read_folio(struct file *file, struct folio *folio) +{ + struct page *page = &folio->page; + loff_t offset = page_file_offset(page); + int rc = -EACCES; + unsigned int xid; + + xid = get_xid(); + + if (file->private_data == NULL) { + rc = -EBADF; + free_xid(xid); + return rc; + } + + cifs_dbg(FYI, "read_folio %p at offset %d 0x%x\n", + page, (int)offset, (int)offset); + + rc = cifs_readpage_worker(file, page, &offset); + + free_xid(xid); + return rc; +} + +static int is_inode_writable(struct cifsInodeInfo *cifs_inode) +{ + struct cifsFileInfo *open_file; + + spin_lock(&cifs_inode->open_file_lock); + list_for_each_entry(open_file, &cifs_inode->openFileList, flist) { + if (OPEN_FMODE(open_file->f_flags) & FMODE_WRITE) { + spin_unlock(&cifs_inode->open_file_lock); + return 1; + } + } + spin_unlock(&cifs_inode->open_file_lock); + return 0; +} + +/* We do not want to update the file size from server for inodes + open for write - to avoid races with writepage extending + the file - in the future we could consider allowing + refreshing the inode only on increases in the file size + but this is tricky to do without racing with writebehind + page caching in the current Linux kernel design */ +bool is_size_safe_to_change(struct cifsInodeInfo *cifsInode, __u64 end_of_file) +{ + if (!cifsInode) + return true; + + if (is_inode_writable(cifsInode)) { + /* This inode is open for write at least once */ + struct cifs_sb_info *cifs_sb; + + cifs_sb = CIFS_SB(cifsInode->netfs.inode.i_sb); + if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_DIRECT_IO) { + /* since no page cache to corrupt on directio + we can change size safely */ + return true; + } + + if (i_size_read(&cifsInode->netfs.inode) < end_of_file) + return true; + + return false; + } else + return true; +} + +static int cifs_write_begin(struct file *file, struct address_space *mapping, + loff_t pos, unsigned len, + struct page **pagep, void **fsdata) +{ + int oncethru = 0; + pgoff_t index = pos >> PAGE_SHIFT; + loff_t offset = pos & (PAGE_SIZE - 1); + loff_t page_start = pos & PAGE_MASK; + loff_t i_size; + struct page *page; + int rc = 0; + + cifs_dbg(FYI, "write_begin from %lld len %d\n", (long long)pos, len); + +start: + page = grab_cache_page_write_begin(mapping, index); + if (!page) { + rc = -ENOMEM; + goto out; + } + + if (PageUptodate(page)) + goto out; + + /* + * If we write a full page it will be up to date, no need to read from + * the server. If the write is short, we'll end up doing a sync write + * instead. + */ + if (len == PAGE_SIZE) + goto out; + + /* + * optimize away the read when we have an oplock, and we're not + * expecting to use any of the data we'd be reading in. That + * is, when the page lies beyond the EOF, or straddles the EOF + * and the write will cover all of the existing data. + */ + if (CIFS_CACHE_READ(CIFS_I(mapping->host))) { + i_size = i_size_read(mapping->host); + if (page_start >= i_size || + (offset == 0 && (pos + len) >= i_size)) { + zero_user_segments(page, 0, offset, + offset + len, + PAGE_SIZE); + /* + * PageChecked means that the parts of the page + * to which we're not writing are considered up + * to date. Once the data is copied to the + * page, it can be set uptodate. + */ + SetPageChecked(page); + goto out; + } + } + + if ((file->f_flags & O_ACCMODE) != O_WRONLY && !oncethru) { + /* + * might as well read a page, it is fast enough. If we get + * an error, we don't need to return it. cifs_write_end will + * do a sync write instead since PG_uptodate isn't set. + */ + cifs_readpage_worker(file, page, &page_start); + put_page(page); + oncethru = 1; + goto start; + } else { + /* we could try using another file handle if there is one - + but how would we lock it to prevent close of that handle + racing with this read? In any case + this will be written out by write_end so is fine */ + } +out: + *pagep = page; + return rc; +} + +static bool cifs_release_folio(struct folio *folio, gfp_t gfp) +{ + if (folio_test_private(folio)) + return 0; + if (folio_test_fscache(folio)) { + if (current_is_kswapd() || !(gfp & __GFP_FS)) + return false; + folio_wait_fscache(folio); + } + fscache_note_page_release(cifs_inode_cookie(folio->mapping->host)); + return true; +} + +static void cifs_invalidate_folio(struct folio *folio, size_t offset, + size_t length) +{ + folio_wait_fscache(folio); +} + +static int cifs_launder_folio(struct folio *folio) +{ + int rc = 0; + loff_t range_start = folio_pos(folio); + loff_t range_end = range_start + folio_size(folio); + struct writeback_control wbc = { + .sync_mode = WB_SYNC_ALL, + .nr_to_write = 0, + .range_start = range_start, + .range_end = range_end, + }; + + cifs_dbg(FYI, "Launder page: %lu\n", folio->index); + + if (folio_clear_dirty_for_io(folio)) + rc = cifs_writepage_locked(&folio->page, &wbc); + + folio_wait_fscache(folio); + return rc; +} + +void cifs_oplock_break(struct work_struct *work) +{ + struct cifsFileInfo *cfile = container_of(work, struct cifsFileInfo, + oplock_break); + struct inode *inode = d_inode(cfile->dentry); + struct cifsInodeInfo *cinode = CIFS_I(inode); + struct cifs_tcon *tcon = tlink_tcon(cfile->tlink); + struct TCP_Server_Info *server = tcon->ses->server; + int rc = 0; + bool purge_cache = false, oplock_break_cancelled; + __u64 persistent_fid, volatile_fid; + __u16 net_fid; + + wait_on_bit(&cinode->flags, CIFS_INODE_PENDING_WRITERS, + TASK_UNINTERRUPTIBLE); + + server->ops->downgrade_oplock(server, cinode, cfile->oplock_level, + cfile->oplock_epoch, &purge_cache); + + if (!CIFS_CACHE_WRITE(cinode) && CIFS_CACHE_READ(cinode) && + cifs_has_mand_locks(cinode)) { + cifs_dbg(FYI, "Reset oplock to None for inode=%p due to mand locks\n", + inode); + cinode->oplock = 0; + } + + if (inode && S_ISREG(inode->i_mode)) { + if (CIFS_CACHE_READ(cinode)) + break_lease(inode, O_RDONLY); + else + break_lease(inode, O_WRONLY); + rc = filemap_fdatawrite(inode->i_mapping); + if (!CIFS_CACHE_READ(cinode) || purge_cache) { + rc = filemap_fdatawait(inode->i_mapping); + mapping_set_error(inode->i_mapping, rc); + cifs_zap_mapping(inode); + } + cifs_dbg(FYI, "Oplock flush inode %p rc %d\n", inode, rc); + if (CIFS_CACHE_WRITE(cinode)) + goto oplock_break_ack; + } + + rc = cifs_push_locks(cfile); + if (rc) + cifs_dbg(VFS, "Push locks rc = %d\n", rc); + +oplock_break_ack: + /* + * When oplock break is received and there are no active + * file handles but cached, then schedule deferred close immediately. + * So, new open will not use cached handle. + */ + + if (!CIFS_CACHE_HANDLE(cinode) && !list_empty(&cinode->deferred_closes)) + cifs_close_deferred_file(cinode); + + persistent_fid = cfile->fid.persistent_fid; + volatile_fid = cfile->fid.volatile_fid; + net_fid = cfile->fid.netfid; + oplock_break_cancelled = cfile->oplock_break_cancelled; + + _cifsFileInfo_put(cfile, false /* do not wait for ourself */, false); + /* + * releasing stale oplock after recent reconnect of smb session using + * a now incorrect file handle is not a data integrity issue but do + * not bother sending an oplock release if session to server still is + * disconnected since oplock already released by the server + */ + if (!oplock_break_cancelled) { + /* check for server null since can race with kill_sb calling tree disconnect */ + if (tcon->ses && tcon->ses->server) { + rc = tcon->ses->server->ops->oplock_response(tcon, persistent_fid, + volatile_fid, net_fid, cinode); + cifs_dbg(FYI, "Oplock release rc = %d\n", rc); + } else + pr_warn_once("lease break not sent for unmounted share\n"); + } + + cifs_done_oplock_break(cinode); +} + +/* + * The presence of cifs_direct_io() in the address space ops vector + * allowes open() O_DIRECT flags which would have failed otherwise. + * + * In the non-cached mode (mount with cache=none), we shunt off direct read and write requests + * so this method should never be called. + * + * Direct IO is not yet supported in the cached mode. + */ +static ssize_t +cifs_direct_io(struct kiocb *iocb, struct iov_iter *iter) +{ + /* + * FIXME + * Eventually need to support direct IO for non forcedirectio mounts + */ + return -EINVAL; +} + +static int cifs_swap_activate(struct swap_info_struct *sis, + struct file *swap_file, sector_t *span) +{ + struct cifsFileInfo *cfile = swap_file->private_data; + struct inode *inode = swap_file->f_mapping->host; + unsigned long blocks; + long long isize; + + cifs_dbg(FYI, "swap activate\n"); + + if (!swap_file->f_mapping->a_ops->swap_rw) + /* Cannot support swap */ + return -EINVAL; + + spin_lock(&inode->i_lock); + blocks = inode->i_blocks; + isize = inode->i_size; + spin_unlock(&inode->i_lock); + if (blocks*512 < isize) { + pr_warn("swap activate: swapfile has holes\n"); + return -EINVAL; + } + *span = sis->pages; + + pr_warn_once("Swap support over SMB3 is experimental\n"); + + /* + * TODO: consider adding ACL (or documenting how) to prevent other + * users (on this or other systems) from reading it + */ + + + /* TODO: add sk_set_memalloc(inet) or similar */ + + if (cfile) + cfile->swapfile = true; + /* + * TODO: Since file already open, we can't open with DENY_ALL here + * but we could add call to grab a byte range lock to prevent others + * from reading or writing the file + */ + + sis->flags |= SWP_FS_OPS; + return add_swap_extent(sis, 0, sis->max, 0); +} + +static void cifs_swap_deactivate(struct file *file) +{ + struct cifsFileInfo *cfile = file->private_data; + + cifs_dbg(FYI, "swap deactivate\n"); + + /* TODO: undo sk_set_memalloc(inet) will eventually be needed */ + + if (cfile) + cfile->swapfile = false; + + /* do we need to unpin (or unlock) the file */ +} + +/* + * Mark a page as having been made dirty and thus needing writeback. We also + * need to pin the cache object to write back to. + */ +#ifdef CONFIG_CIFS_FSCACHE +static bool cifs_dirty_folio(struct address_space *mapping, struct folio *folio) +{ + return fscache_dirty_folio(mapping, folio, + cifs_inode_cookie(mapping->host)); +} +#else +#define cifs_dirty_folio filemap_dirty_folio +#endif + +const struct address_space_operations cifs_addr_ops = { + .read_folio = cifs_read_folio, + .readahead = cifs_readahead, + .writepage = cifs_writepage, + .writepages = cifs_writepages, + .write_begin = cifs_write_begin, + .write_end = cifs_write_end, + .dirty_folio = cifs_dirty_folio, + .release_folio = cifs_release_folio, + .direct_IO = cifs_direct_io, + .invalidate_folio = cifs_invalidate_folio, + .launder_folio = cifs_launder_folio, + /* + * TODO: investigate and if useful we could add an cifs_migratePage + * helper (under an CONFIG_MIGRATION) in the future, and also + * investigate and add an is_dirty_writeback helper if needed + */ + .swap_activate = cifs_swap_activate, + .swap_deactivate = cifs_swap_deactivate, +}; + +/* + * cifs_readahead requires the server to support a buffer large enough to + * contain the header plus one complete page of data. Otherwise, we need + * to leave cifs_readahead out of the address space operations. + */ +const struct address_space_operations cifs_addr_ops_smallbuf = { + .read_folio = cifs_read_folio, + .writepage = cifs_writepage, + .writepages = cifs_writepages, + .write_begin = cifs_write_begin, + .write_end = cifs_write_end, + .dirty_folio = cifs_dirty_folio, + .release_folio = cifs_release_folio, + .invalidate_folio = cifs_invalidate_folio, + .launder_folio = cifs_launder_folio, +}; diff --git a/fs/smb/client/fs_context.c b/fs/smb/client/fs_context.c new file mode 100644 index 0000000000000..e2e2ef0fa9a0f --- /dev/null +++ b/fs/smb/client/fs_context.c @@ -0,0 +1,1773 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Copyright (C) 2020, Microsoft Corporation. + * + * Author(s): Steve French + * David Howells + */ + +/* +#include +#include +#include +#include +#include +#include +#ifdef CONFIG_CIFS_DFS_UPCALL +#include "dfs_cache.h" +#endif +*/ + +#include +#include +#include +#include +#include +#include +#include +#include "cifsfs.h" +#include "cifspdu.h" +#include "cifsglob.h" +#include "cifsproto.h" +#include "cifs_unicode.h" +#include "cifs_debug.h" +#include "cifs_fs_sb.h" +#include "ntlmssp.h" +#include "nterr.h" +#include "rfc1002pdu.h" +#include "fs_context.h" + +static DEFINE_MUTEX(cifs_mount_mutex); + +static const match_table_t cifs_smb_version_tokens = { + { Smb_1, SMB1_VERSION_STRING }, + { Smb_20, SMB20_VERSION_STRING}, + { Smb_21, SMB21_VERSION_STRING }, + { Smb_30, SMB30_VERSION_STRING }, + { Smb_302, SMB302_VERSION_STRING }, + { Smb_302, ALT_SMB302_VERSION_STRING }, + { Smb_311, SMB311_VERSION_STRING }, + { Smb_311, ALT_SMB311_VERSION_STRING }, + { Smb_3any, SMB3ANY_VERSION_STRING }, + { Smb_default, SMBDEFAULT_VERSION_STRING }, + { Smb_version_err, NULL } +}; + +static const match_table_t cifs_secflavor_tokens = { + { Opt_sec_krb5, "krb5" }, + { Opt_sec_krb5i, "krb5i" }, + { Opt_sec_krb5p, "krb5p" }, + { Opt_sec_ntlmsspi, "ntlmsspi" }, + { Opt_sec_ntlmssp, "ntlmssp" }, + { Opt_sec_ntlmv2, "nontlm" }, + { Opt_sec_ntlmv2, "ntlmv2" }, + { Opt_sec_ntlmv2i, "ntlmv2i" }, + { Opt_sec_none, "none" }, + + { Opt_sec_err, NULL } +}; + +const struct fs_parameter_spec smb3_fs_parameters[] = { + /* Mount options that take no arguments */ + fsparam_flag_no("user_xattr", Opt_user_xattr), + fsparam_flag_no("forceuid", Opt_forceuid), + fsparam_flag_no("multichannel", Opt_multichannel), + fsparam_flag_no("forcegid", Opt_forcegid), + fsparam_flag("noblocksend", Opt_noblocksend), + fsparam_flag("noautotune", Opt_noautotune), + fsparam_flag("nolease", Opt_nolease), + fsparam_flag_no("hard", Opt_hard), + fsparam_flag_no("soft", Opt_soft), + fsparam_flag_no("perm", Opt_perm), + fsparam_flag("nodelete", Opt_nodelete), + fsparam_flag_no("mapposix", Opt_mapposix), + fsparam_flag("mapchars", Opt_mapchars), + fsparam_flag("nomapchars", Opt_nomapchars), + fsparam_flag_no("sfu", Opt_sfu), + fsparam_flag("nodfs", Opt_nodfs), + fsparam_flag_no("posixpaths", Opt_posixpaths), + fsparam_flag_no("unix", Opt_unix), + fsparam_flag_no("linux", Opt_unix), + fsparam_flag_no("posix", Opt_unix), + fsparam_flag("nocase", Opt_nocase), + fsparam_flag("ignorecase", Opt_nocase), + fsparam_flag_no("brl", Opt_brl), + fsparam_flag_no("handlecache", Opt_handlecache), + fsparam_flag("forcemandatorylock", Opt_forcemandatorylock), + fsparam_flag("forcemand", Opt_forcemandatorylock), + fsparam_flag("setuidfromacl", Opt_setuidfromacl), + fsparam_flag("idsfromsid", Opt_setuidfromacl), + fsparam_flag_no("setuids", Opt_setuids), + fsparam_flag_no("dynperm", Opt_dynperm), + fsparam_flag_no("intr", Opt_intr), + fsparam_flag_no("strictsync", Opt_strictsync), + fsparam_flag_no("serverino", Opt_serverino), + fsparam_flag("rwpidforward", Opt_rwpidforward), + fsparam_flag("cifsacl", Opt_cifsacl), + fsparam_flag_no("acl", Opt_acl), + fsparam_flag("locallease", Opt_locallease), + fsparam_flag("sign", Opt_sign), + fsparam_flag("ignore_signature", Opt_ignore_signature), + fsparam_flag("signloosely", Opt_ignore_signature), + fsparam_flag("seal", Opt_seal), + fsparam_flag("noac", Opt_noac), + fsparam_flag("fsc", Opt_fsc), + fsparam_flag("mfsymlinks", Opt_mfsymlinks), + fsparam_flag("multiuser", Opt_multiuser), + fsparam_flag("sloppy", Opt_sloppy), + fsparam_flag("nosharesock", Opt_nosharesock), + fsparam_flag_no("persistenthandles", Opt_persistent), + fsparam_flag_no("resilienthandles", Opt_resilient), + fsparam_flag_no("tcpnodelay", Opt_tcp_nodelay), + fsparam_flag("nosparse", Opt_nosparse), + fsparam_flag("domainauto", Opt_domainauto), + fsparam_flag("rdma", Opt_rdma), + fsparam_flag("modesid", Opt_modesid), + fsparam_flag("modefromsid", Opt_modesid), + fsparam_flag("rootfs", Opt_rootfs), + fsparam_flag("compress", Opt_compress), + fsparam_flag("witness", Opt_witness), + + /* Mount options which take numeric value */ + fsparam_u32("backupuid", Opt_backupuid), + fsparam_u32("backupgid", Opt_backupgid), + fsparam_u32("uid", Opt_uid), + fsparam_u32("cruid", Opt_cruid), + fsparam_u32("gid", Opt_gid), + fsparam_u32("file_mode", Opt_file_mode), + fsparam_u32("dirmode", Opt_dirmode), + fsparam_u32("dir_mode", Opt_dirmode), + fsparam_u32("port", Opt_port), + fsparam_u32("min_enc_offload", Opt_min_enc_offload), + fsparam_u32("esize", Opt_min_enc_offload), + fsparam_u32("bsize", Opt_blocksize), + fsparam_u32("rasize", Opt_rasize), + fsparam_u32("rsize", Opt_rsize), + fsparam_u32("wsize", Opt_wsize), + fsparam_u32("actimeo", Opt_actimeo), + fsparam_u32("acdirmax", Opt_acdirmax), + fsparam_u32("acregmax", Opt_acregmax), + fsparam_u32("closetimeo", Opt_closetimeo), + fsparam_u32("echo_interval", Opt_echo_interval), + fsparam_u32("max_credits", Opt_max_credits), + fsparam_u32("handletimeout", Opt_handletimeout), + fsparam_u64("snapshot", Opt_snapshot), + fsparam_u32("max_channels", Opt_max_channels), + + /* Mount options which take string value */ + fsparam_string("source", Opt_source), + fsparam_string("user", Opt_user), + fsparam_string("username", Opt_user), + fsparam_string("pass", Opt_pass), + fsparam_string("password", Opt_pass), + fsparam_string("ip", Opt_ip), + fsparam_string("addr", Opt_ip), + fsparam_string("domain", Opt_domain), + fsparam_string("dom", Opt_domain), + fsparam_string("srcaddr", Opt_srcaddr), + fsparam_string("iocharset", Opt_iocharset), + fsparam_string("netbiosname", Opt_netbiosname), + fsparam_string("servern", Opt_servern), + fsparam_string("ver", Opt_ver), + fsparam_string("vers", Opt_vers), + fsparam_string("sec", Opt_sec), + fsparam_string("cache", Opt_cache), + + /* Arguments that should be ignored */ + fsparam_flag("guest", Opt_ignore), + fsparam_flag("noatime", Opt_ignore), + fsparam_flag("relatime", Opt_ignore), + fsparam_flag("_netdev", Opt_ignore), + fsparam_flag_no("suid", Opt_ignore), + fsparam_flag_no("exec", Opt_ignore), + fsparam_flag_no("dev", Opt_ignore), + fsparam_flag_no("mand", Opt_ignore), + fsparam_flag_no("auto", Opt_ignore), + fsparam_string("cred", Opt_ignore), + fsparam_string("credentials", Opt_ignore), + /* + * UNC and prefixpath is now extracted from Opt_source + * in the new mount API so we can just ignore them going forward. + */ + fsparam_string("unc", Opt_ignore), + fsparam_string("prefixpath", Opt_ignore), + {} +}; + +static int +cifs_parse_security_flavors(struct fs_context *fc, char *value, struct smb3_fs_context *ctx) +{ + + substring_t args[MAX_OPT_ARGS]; + + /* + * With mount options, the last one should win. Reset any existing + * settings back to default. + */ + ctx->sectype = Unspecified; + ctx->sign = false; + + switch (match_token(value, cifs_secflavor_tokens, args)) { + case Opt_sec_krb5p: + cifs_errorf(fc, "sec=krb5p is not supported!\n"); + return 1; + case Opt_sec_krb5i: + ctx->sign = true; + fallthrough; + case Opt_sec_krb5: + ctx->sectype = Kerberos; + break; + case Opt_sec_ntlmsspi: + ctx->sign = true; + fallthrough; + case Opt_sec_ntlmssp: + ctx->sectype = RawNTLMSSP; + break; + case Opt_sec_ntlmv2i: + ctx->sign = true; + fallthrough; + case Opt_sec_ntlmv2: + ctx->sectype = NTLMv2; + break; + case Opt_sec_none: + ctx->nullauth = 1; + break; + default: + cifs_errorf(fc, "bad security option: %s\n", value); + return 1; + } + + return 0; +} + +static const match_table_t cifs_cacheflavor_tokens = { + { Opt_cache_loose, "loose" }, + { Opt_cache_strict, "strict" }, + { Opt_cache_none, "none" }, + { Opt_cache_ro, "ro" }, + { Opt_cache_rw, "singleclient" }, + { Opt_cache_err, NULL } +}; + +static int +cifs_parse_cache_flavor(struct fs_context *fc, char *value, struct smb3_fs_context *ctx) +{ + substring_t args[MAX_OPT_ARGS]; + + switch (match_token(value, cifs_cacheflavor_tokens, args)) { + case Opt_cache_loose: + ctx->direct_io = false; + ctx->strict_io = false; + ctx->cache_ro = false; + ctx->cache_rw = false; + break; + case Opt_cache_strict: + ctx->direct_io = false; + ctx->strict_io = true; + ctx->cache_ro = false; + ctx->cache_rw = false; + break; + case Opt_cache_none: + ctx->direct_io = true; + ctx->strict_io = false; + ctx->cache_ro = false; + ctx->cache_rw = false; + break; + case Opt_cache_ro: + ctx->direct_io = false; + ctx->strict_io = false; + ctx->cache_ro = true; + ctx->cache_rw = false; + break; + case Opt_cache_rw: + ctx->direct_io = false; + ctx->strict_io = false; + ctx->cache_ro = false; + ctx->cache_rw = true; + break; + default: + cifs_errorf(fc, "bad cache= option: %s\n", value); + return 1; + } + return 0; +} + +#define DUP_CTX_STR(field) \ +do { \ + if (ctx->field) { \ + new_ctx->field = kstrdup(ctx->field, GFP_ATOMIC); \ + if (new_ctx->field == NULL) { \ + smb3_cleanup_fs_context_contents(new_ctx); \ + return -ENOMEM; \ + } \ + } \ +} while (0) + +int +smb3_fs_context_dup(struct smb3_fs_context *new_ctx, struct smb3_fs_context *ctx) +{ + memcpy(new_ctx, ctx, sizeof(*ctx)); + new_ctx->prepath = NULL; + new_ctx->mount_options = NULL; + new_ctx->nodename = NULL; + new_ctx->username = NULL; + new_ctx->password = NULL; + new_ctx->server_hostname = NULL; + new_ctx->domainname = NULL; + new_ctx->UNC = NULL; + new_ctx->source = NULL; + new_ctx->iocharset = NULL; + /* + * Make sure to stay in sync with smb3_cleanup_fs_context_contents() + */ + DUP_CTX_STR(prepath); + DUP_CTX_STR(mount_options); + DUP_CTX_STR(username); + DUP_CTX_STR(password); + DUP_CTX_STR(server_hostname); + DUP_CTX_STR(UNC); + DUP_CTX_STR(source); + DUP_CTX_STR(domainname); + DUP_CTX_STR(nodename); + DUP_CTX_STR(iocharset); + + return 0; +} + +static int +cifs_parse_smb_version(struct fs_context *fc, char *value, struct smb3_fs_context *ctx, bool is_smb3) +{ + substring_t args[MAX_OPT_ARGS]; + + switch (match_token(value, cifs_smb_version_tokens, args)) { +#ifdef CONFIG_CIFS_ALLOW_INSECURE_LEGACY + case Smb_1: + if (disable_legacy_dialects) { + cifs_errorf(fc, "mount with legacy dialect disabled\n"); + return 1; + } + if (is_smb3) { + cifs_errorf(fc, "vers=1.0 (cifs) not permitted when mounting with smb3\n"); + return 1; + } + cifs_errorf(fc, "Use of the less secure dialect vers=1.0 is not recommended unless required for access to very old servers\n"); + ctx->ops = &smb1_operations; + ctx->vals = &smb1_values; + break; + case Smb_20: + if (disable_legacy_dialects) { + cifs_errorf(fc, "mount with legacy dialect disabled\n"); + return 1; + } + if (is_smb3) { + cifs_errorf(fc, "vers=2.0 not permitted when mounting with smb3\n"); + return 1; + } + ctx->ops = &smb20_operations; + ctx->vals = &smb20_values; + break; +#else + case Smb_1: + cifs_errorf(fc, "vers=1.0 (cifs) mount not permitted when legacy dialects disabled\n"); + return 1; + case Smb_20: + cifs_errorf(fc, "vers=2.0 mount not permitted when legacy dialects disabled\n"); + return 1; +#endif /* CIFS_ALLOW_INSECURE_LEGACY */ + case Smb_21: + ctx->ops = &smb21_operations; + ctx->vals = &smb21_values; + break; + case Smb_30: + ctx->ops = &smb30_operations; + ctx->vals = &smb30_values; + break; + case Smb_302: + ctx->ops = &smb30_operations; /* currently identical with 3.0 */ + ctx->vals = &smb302_values; + break; + case Smb_311: + ctx->ops = &smb311_operations; + ctx->vals = &smb311_values; + break; + case Smb_3any: + ctx->ops = &smb30_operations; /* currently identical with 3.0 */ + ctx->vals = &smb3any_values; + break; + case Smb_default: + ctx->ops = &smb30_operations; + ctx->vals = &smbdefault_values; + break; + default: + cifs_errorf(fc, "Unknown vers= option specified: %s\n", value); + return 1; + } + return 0; +} + +int smb3_parse_opt(const char *options, const char *key, char **val) +{ + int rc = -ENOENT; + char *opts, *orig, *p; + + orig = opts = kstrdup(options, GFP_KERNEL); + if (!opts) + return -ENOMEM; + + while ((p = strsep(&opts, ","))) { + char *nval; + + if (!*p) + continue; + if (strncasecmp(p, key, strlen(key))) + continue; + nval = strchr(p, '='); + if (nval) { + if (nval == p) + continue; + *nval++ = 0; + *val = kstrdup(nval, GFP_KERNEL); + rc = !*val ? -ENOMEM : 0; + goto out; + } + } +out: + kfree(orig); + return rc; +} + +/* + * Remove duplicate path delimiters. Windows is supposed to do that + * but there are some bugs that prevent rename from working if there are + * multiple delimiters. + * + * Returns a sanitized duplicate of @path. @gfp indicates the GFP_* flags + * for kstrdup. + * The caller is responsible for freeing the original. + */ +#define IS_DELIM(c) ((c) == '/' || (c) == '\\') +char *cifs_sanitize_prepath(char *prepath, gfp_t gfp) +{ + char *cursor1 = prepath, *cursor2 = prepath; + + /* skip all prepended delimiters */ + while (IS_DELIM(*cursor1)) + cursor1++; + + /* copy the first letter */ + *cursor2 = *cursor1; + + /* copy the remainder... */ + while (*(cursor1++)) { + /* ... skipping all duplicated delimiters */ + if (IS_DELIM(*cursor1) && IS_DELIM(*cursor2)) + continue; + *(++cursor2) = *cursor1; + } + + /* if the last character is a delimiter, skip it */ + if (IS_DELIM(*(cursor2 - 1))) + cursor2--; + + *(cursor2) = '\0'; + return kstrdup(prepath, gfp); +} + +/* + * Parse a devname into substrings and populate the ctx->UNC and ctx->prepath + * fields with the result. Returns 0 on success and an error otherwise + * (e.g. ENOMEM or EINVAL) + */ +int +smb3_parse_devname(const char *devname, struct smb3_fs_context *ctx) +{ + char *pos; + const char *delims = "/\\"; + size_t len; + + if (unlikely(!devname || !*devname)) { + cifs_dbg(VFS, "Device name not specified\n"); + return -EINVAL; + } + + /* make sure we have a valid UNC double delimiter prefix */ + len = strspn(devname, delims); + if (len != 2) + return -EINVAL; + + /* find delimiter between host and sharename */ + pos = strpbrk(devname + 2, delims); + if (!pos) + return -EINVAL; + + /* record the server hostname */ + kfree(ctx->server_hostname); + ctx->server_hostname = kstrndup(devname + 2, pos - devname - 2, GFP_KERNEL); + if (!ctx->server_hostname) + return -ENOMEM; + + /* skip past delimiter */ + ++pos; + + /* now go until next delimiter or end of string */ + len = strcspn(pos, delims); + + /* move "pos" up to delimiter or NULL */ + pos += len; + kfree(ctx->UNC); + ctx->UNC = kstrndup(devname, pos - devname, GFP_KERNEL); + if (!ctx->UNC) + return -ENOMEM; + + convert_delimiter(ctx->UNC, '\\'); + + /* skip any delimiter */ + if (*pos == '/' || *pos == '\\') + pos++; + + kfree(ctx->prepath); + ctx->prepath = NULL; + + /* If pos is NULL then no prepath */ + if (!*pos) + return 0; + + ctx->prepath = cifs_sanitize_prepath(pos, GFP_KERNEL); + if (!ctx->prepath) + return -ENOMEM; + + return 0; +} + +static void smb3_fs_context_free(struct fs_context *fc); +static int smb3_fs_context_parse_param(struct fs_context *fc, + struct fs_parameter *param); +static int smb3_fs_context_parse_monolithic(struct fs_context *fc, + void *data); +static int smb3_get_tree(struct fs_context *fc); +static int smb3_reconfigure(struct fs_context *fc); + +static const struct fs_context_operations smb3_fs_context_ops = { + .free = smb3_fs_context_free, + .parse_param = smb3_fs_context_parse_param, + .parse_monolithic = smb3_fs_context_parse_monolithic, + .get_tree = smb3_get_tree, + .reconfigure = smb3_reconfigure, +}; + +/* + * Parse a monolithic block of data from sys_mount(). + * smb3_fs_context_parse_monolithic - Parse key[=val][,key[=val]]* mount data + * @ctx: The superblock configuration to fill in. + * @data: The data to parse + * + * Parse a blob of data that's in key[=val][,key[=val]]* form. This can be + * called from the ->monolithic_mount_data() fs_context operation. + * + * Returns 0 on success or the error returned by the ->parse_option() fs_context + * operation on failure. + */ +static int smb3_fs_context_parse_monolithic(struct fs_context *fc, + void *data) +{ + struct smb3_fs_context *ctx = smb3_fc2context(fc); + char *options = data, *key; + int ret = 0; + + if (!options) + return 0; + + ctx->mount_options = kstrdup(data, GFP_KERNEL); + if (ctx->mount_options == NULL) + return -ENOMEM; + + ret = security_sb_eat_lsm_opts(options, &fc->security); + if (ret) + return ret; + + /* BB Need to add support for sep= here TBD */ + while ((key = strsep(&options, ",")) != NULL) { + size_t len; + char *value; + + if (*key == 0) + break; + + /* Check if following character is the deliminator If yes, + * we have encountered a double deliminator reset the NULL + * character to the deliminator + */ + while (options && options[0] == ',') { + len = strlen(key); + strcpy(key + len, options); + options = strchr(options, ','); + if (options) + *options++ = 0; + } + + + len = 0; + value = strchr(key, '='); + if (value) { + if (value == key) + continue; + *value++ = 0; + len = strlen(value); + } + + ret = vfs_parse_fs_string(fc, key, value, len); + if (ret < 0) + break; + } + + return ret; +} + +/* + * Validate the preparsed information in the config. + */ +static int smb3_fs_context_validate(struct fs_context *fc) +{ + struct smb3_fs_context *ctx = smb3_fc2context(fc); + + if (ctx->rdma && ctx->vals->protocol_id < SMB30_PROT_ID) { + cifs_errorf(fc, "SMB Direct requires Version >=3.0\n"); + return -EOPNOTSUPP; + } + +#ifndef CONFIG_KEYS + /* Muliuser mounts require CONFIG_KEYS support */ + if (ctx->multiuser) { + cifs_errorf(fc, "Multiuser mounts require kernels with CONFIG_KEYS enabled\n"); + return -1; + } +#endif + + if (ctx->got_version == false) + pr_warn_once("No dialect specified on mount. Default has changed to a more secure dialect, SMB2.1 or later (e.g. SMB3.1.1), from CIFS (SMB1). To use the less secure SMB1 dialect to access old servers which do not support SMB3.1.1 (or even SMB3 or SMB2.1) specify vers=1.0 on mount.\n"); + + + if (!ctx->UNC) { + cifs_errorf(fc, "CIFS mount error: No usable UNC path provided in device string!\n"); + return -1; + } + + /* make sure UNC has a share name */ + if (strlen(ctx->UNC) < 3 || !strchr(ctx->UNC + 3, '\\')) { + cifs_errorf(fc, "Malformed UNC. Unable to find share name.\n"); + return -ENOENT; + } + + if (!ctx->got_ip) { + int len; + const char *slash; + + /* No ip= option specified? Try to get it from UNC */ + /* Use the address part of the UNC. */ + slash = strchr(&ctx->UNC[2], '\\'); + len = slash - &ctx->UNC[2]; + if (!cifs_convert_address((struct sockaddr *)&ctx->dstaddr, + &ctx->UNC[2], len)) { + pr_err("Unable to determine destination address\n"); + return -EHOSTUNREACH; + } + } + + /* set the port that we got earlier */ + cifs_set_port((struct sockaddr *)&ctx->dstaddr, ctx->port); + + if (ctx->override_uid && !ctx->uid_specified) { + ctx->override_uid = 0; + pr_notice("ignoring forceuid mount option specified with no uid= option\n"); + } + + if (ctx->override_gid && !ctx->gid_specified) { + ctx->override_gid = 0; + pr_notice("ignoring forcegid mount option specified with no gid= option\n"); + } + + return 0; +} + +static int smb3_get_tree_common(struct fs_context *fc) +{ + struct smb3_fs_context *ctx = smb3_fc2context(fc); + struct dentry *root; + int rc = 0; + + root = cifs_smb3_do_mount(fc->fs_type, 0, ctx); + if (IS_ERR(root)) + return PTR_ERR(root); + + fc->root = root; + + return rc; +} + +/* + * Create an SMB3 superblock from the parameters passed. + */ +static int smb3_get_tree(struct fs_context *fc) +{ + int err = smb3_fs_context_validate(fc); + int ret; + + if (err) + return err; + mutex_lock(&cifs_mount_mutex); + ret = smb3_get_tree_common(fc); + mutex_unlock(&cifs_mount_mutex); + return ret; +} + +static void smb3_fs_context_free(struct fs_context *fc) +{ + struct smb3_fs_context *ctx = smb3_fc2context(fc); + + smb3_cleanup_fs_context(ctx); +} + +/* + * Compare the old and new proposed context during reconfigure + * and check if the changes are compatible. + */ +static int smb3_verify_reconfigure_ctx(struct fs_context *fc, + struct smb3_fs_context *new_ctx, + struct smb3_fs_context *old_ctx) +{ + if (new_ctx->posix_paths != old_ctx->posix_paths) { + cifs_errorf(fc, "can not change posixpaths during remount\n"); + return -EINVAL; + } + if (new_ctx->sectype != old_ctx->sectype) { + cifs_errorf(fc, "can not change sec during remount\n"); + return -EINVAL; + } + if (new_ctx->multiuser != old_ctx->multiuser) { + cifs_errorf(fc, "can not change multiuser during remount\n"); + return -EINVAL; + } + if (new_ctx->UNC && + (!old_ctx->UNC || strcmp(new_ctx->UNC, old_ctx->UNC))) { + cifs_errorf(fc, "can not change UNC during remount\n"); + return -EINVAL; + } + if (new_ctx->username && + (!old_ctx->username || strcmp(new_ctx->username, old_ctx->username))) { + cifs_errorf(fc, "can not change username during remount\n"); + return -EINVAL; + } + if (new_ctx->password && + (!old_ctx->password || strcmp(new_ctx->password, old_ctx->password))) { + cifs_errorf(fc, "can not change password during remount\n"); + return -EINVAL; + } + if (new_ctx->domainname && + (!old_ctx->domainname || strcmp(new_ctx->domainname, old_ctx->domainname))) { + cifs_errorf(fc, "can not change domainname during remount\n"); + return -EINVAL; + } + if (strcmp(new_ctx->workstation_name, old_ctx->workstation_name)) { + cifs_errorf(fc, "can not change workstation_name during remount\n"); + return -EINVAL; + } + if (new_ctx->nodename && + (!old_ctx->nodename || strcmp(new_ctx->nodename, old_ctx->nodename))) { + cifs_errorf(fc, "can not change nodename during remount\n"); + return -EINVAL; + } + if (new_ctx->iocharset && + (!old_ctx->iocharset || strcmp(new_ctx->iocharset, old_ctx->iocharset))) { + cifs_errorf(fc, "can not change iocharset during remount\n"); + return -EINVAL; + } + + return 0; +} + +#define STEAL_STRING(cifs_sb, ctx, field) \ +do { \ + kfree(ctx->field); \ + ctx->field = cifs_sb->ctx->field; \ + cifs_sb->ctx->field = NULL; \ +} while (0) + +#define STEAL_STRING_SENSITIVE(cifs_sb, ctx, field) \ +do { \ + kfree_sensitive(ctx->field); \ + ctx->field = cifs_sb->ctx->field; \ + cifs_sb->ctx->field = NULL; \ +} while (0) + +static int smb3_reconfigure(struct fs_context *fc) +{ + struct smb3_fs_context *ctx = smb3_fc2context(fc); + struct dentry *root = fc->root; + struct cifs_sb_info *cifs_sb = CIFS_SB(root->d_sb); + int rc; + + rc = smb3_verify_reconfigure_ctx(fc, ctx, cifs_sb->ctx); + if (rc) + return rc; + + /* + * We can not change UNC/username/password/domainname/ + * workstation_name/nodename/iocharset + * during reconnect so ignore what we have in the new context and + * just use what we already have in cifs_sb->ctx. + */ + STEAL_STRING(cifs_sb, ctx, UNC); + STEAL_STRING(cifs_sb, ctx, source); + STEAL_STRING(cifs_sb, ctx, username); + STEAL_STRING_SENSITIVE(cifs_sb, ctx, password); + STEAL_STRING(cifs_sb, ctx, domainname); + STEAL_STRING(cifs_sb, ctx, nodename); + STEAL_STRING(cifs_sb, ctx, iocharset); + + /* if rsize or wsize not passed in on remount, use previous values */ + if (ctx->rsize == 0) + ctx->rsize = cifs_sb->ctx->rsize; + if (ctx->wsize == 0) + ctx->wsize = cifs_sb->ctx->wsize; + + + smb3_cleanup_fs_context_contents(cifs_sb->ctx); + rc = smb3_fs_context_dup(cifs_sb->ctx, ctx); + smb3_update_mnt_flags(cifs_sb); +#ifdef CONFIG_CIFS_DFS_UPCALL + if (!rc) + rc = dfs_cache_remount_fs(cifs_sb); +#endif + + return rc; +} + +static int smb3_fs_context_parse_param(struct fs_context *fc, + struct fs_parameter *param) +{ + struct fs_parse_result result; + struct smb3_fs_context *ctx = smb3_fc2context(fc); + int i, opt; + bool is_smb3 = !strcmp(fc->fs_type->name, "smb3"); + bool skip_parsing = false; + kuid_t uid; + kgid_t gid; + + cifs_dbg(FYI, "CIFS: parsing cifs mount option '%s'\n", param->key); + + /* + * fs_parse can not handle string options with an empty value so + * we will need special handling of them. + */ + if (param->type == fs_value_is_string && param->string[0] == 0) { + if (!strcmp("pass", param->key) || !strcmp("password", param->key)) { + skip_parsing = true; + opt = Opt_pass; + } else if (!strcmp("user", param->key) || !strcmp("username", param->key)) { + skip_parsing = true; + opt = Opt_user; + } + } + + if (!skip_parsing) { + opt = fs_parse(fc, smb3_fs_parameters, param, &result); + if (opt < 0) + return ctx->sloppy ? 1 : opt; + } + + switch (opt) { + case Opt_compress: + ctx->compression = UNKNOWN_TYPE; + cifs_dbg(VFS, + "SMB3 compression support is experimental\n"); + break; + case Opt_nodfs: + ctx->nodfs = 1; + break; + case Opt_hard: + if (result.negated) + ctx->retry = 0; + else + ctx->retry = 1; + break; + case Opt_soft: + if (result.negated) + ctx->retry = 1; + else + ctx->retry = 0; + break; + case Opt_mapposix: + if (result.negated) + ctx->remap = false; + else { + ctx->remap = true; + ctx->sfu_remap = false; /* disable SFU mapping */ + } + break; + case Opt_mapchars: + if (result.negated) + ctx->sfu_remap = false; + else { + ctx->sfu_remap = true; + ctx->remap = false; /* disable SFM (mapposix) mapping */ + } + break; + case Opt_user_xattr: + if (result.negated) + ctx->no_xattr = 1; + else + ctx->no_xattr = 0; + break; + case Opt_forceuid: + if (result.negated) + ctx->override_uid = 0; + else + ctx->override_uid = 1; + break; + case Opt_forcegid: + if (result.negated) + ctx->override_gid = 0; + else + ctx->override_gid = 1; + break; + case Opt_perm: + if (result.negated) + ctx->noperm = 1; + else + ctx->noperm = 0; + break; + case Opt_dynperm: + if (result.negated) + ctx->dynperm = 0; + else + ctx->dynperm = 1; + break; + case Opt_sfu: + if (result.negated) + ctx->sfu_emul = 0; + else + ctx->sfu_emul = 1; + break; + case Opt_noblocksend: + ctx->noblocksnd = 1; + break; + case Opt_noautotune: + ctx->noautotune = 1; + break; + case Opt_nolease: + ctx->no_lease = 1; + break; + case Opt_nosparse: + ctx->no_sparse = 1; + break; + case Opt_nodelete: + ctx->nodelete = 1; + break; + case Opt_multichannel: + if (result.negated) { + ctx->multichannel = false; + ctx->max_channels = 1; + } else { + ctx->multichannel = true; + /* if number of channels not specified, default to 2 */ + if (ctx->max_channels < 2) + ctx->max_channels = 2; + } + break; + case Opt_uid: + uid = make_kuid(current_user_ns(), result.uint_32); + if (!uid_valid(uid)) + goto cifs_parse_mount_err; + ctx->linux_uid = uid; + ctx->uid_specified = true; + break; + case Opt_cruid: + uid = make_kuid(current_user_ns(), result.uint_32); + if (!uid_valid(uid)) + goto cifs_parse_mount_err; + ctx->cred_uid = uid; + ctx->cruid_specified = true; + break; + case Opt_backupuid: + uid = make_kuid(current_user_ns(), result.uint_32); + if (!uid_valid(uid)) + goto cifs_parse_mount_err; + ctx->backupuid = uid; + ctx->backupuid_specified = true; + break; + case Opt_backupgid: + gid = make_kgid(current_user_ns(), result.uint_32); + if (!gid_valid(gid)) + goto cifs_parse_mount_err; + ctx->backupgid = gid; + ctx->backupgid_specified = true; + break; + case Opt_gid: + gid = make_kgid(current_user_ns(), result.uint_32); + if (!gid_valid(gid)) + goto cifs_parse_mount_err; + ctx->linux_gid = gid; + ctx->gid_specified = true; + break; + case Opt_port: + ctx->port = result.uint_32; + break; + case Opt_file_mode: + ctx->file_mode = result.uint_32; + break; + case Opt_dirmode: + ctx->dir_mode = result.uint_32; + break; + case Opt_min_enc_offload: + ctx->min_offload = result.uint_32; + break; + case Opt_blocksize: + /* + * inode blocksize realistically should never need to be + * less than 16K or greater than 16M and default is 1MB. + * Note that small inode block sizes (e.g. 64K) can lead + * to very poor performance of common tools like cp and scp + */ + if ((result.uint_32 < CIFS_MAX_MSGSIZE) || + (result.uint_32 > (4 * SMB3_DEFAULT_IOSIZE))) { + cifs_errorf(fc, "%s: Invalid blocksize\n", + __func__); + goto cifs_parse_mount_err; + } + ctx->bsize = result.uint_32; + ctx->got_bsize = true; + break; + case Opt_rasize: + /* + * readahead size realistically should never need to be + * less than 1M (CIFS_DEFAULT_IOSIZE) or greater than 32M + * (perhaps an exception should be considered in the + * for the case of a large number of channels + * when multichannel is negotiated) since that would lead + * to plenty of parallel I/O in flight to the server. + * Note that smaller read ahead sizes would + * hurt performance of common tools like cp and scp + * which often trigger sequential i/o with read ahead + */ + if ((result.uint_32 > (8 * SMB3_DEFAULT_IOSIZE)) || + (result.uint_32 < CIFS_DEFAULT_IOSIZE)) { + cifs_errorf(fc, "%s: Invalid rasize %d vs. %d\n", + __func__, result.uint_32, SMB3_DEFAULT_IOSIZE); + goto cifs_parse_mount_err; + } + ctx->rasize = result.uint_32; + break; + case Opt_rsize: + ctx->rsize = result.uint_32; + ctx->got_rsize = true; + break; + case Opt_wsize: + ctx->wsize = result.uint_32; + ctx->got_wsize = true; + break; + case Opt_acregmax: + ctx->acregmax = HZ * result.uint_32; + if (ctx->acregmax > CIFS_MAX_ACTIMEO) { + cifs_errorf(fc, "acregmax too large\n"); + goto cifs_parse_mount_err; + } + break; + case Opt_acdirmax: + ctx->acdirmax = HZ * result.uint_32; + if (ctx->acdirmax > CIFS_MAX_ACTIMEO) { + cifs_errorf(fc, "acdirmax too large\n"); + goto cifs_parse_mount_err; + } + break; + case Opt_actimeo: + if (HZ * result.uint_32 > CIFS_MAX_ACTIMEO) { + cifs_errorf(fc, "timeout too large\n"); + goto cifs_parse_mount_err; + } + if ((ctx->acdirmax != CIFS_DEF_ACTIMEO) || + (ctx->acregmax != CIFS_DEF_ACTIMEO)) { + cifs_errorf(fc, "actimeo ignored since acregmax or acdirmax specified\n"); + break; + } + ctx->acdirmax = ctx->acregmax = HZ * result.uint_32; + break; + case Opt_closetimeo: + ctx->closetimeo = HZ * result.uint_32; + if (ctx->closetimeo > SMB3_MAX_DCLOSETIMEO) { + cifs_errorf(fc, "closetimeo too large\n"); + goto cifs_parse_mount_err; + } + break; + case Opt_echo_interval: + ctx->echo_interval = result.uint_32; + break; + case Opt_snapshot: + ctx->snapshot_time = result.uint_64; + break; + case Opt_max_credits: + if (result.uint_32 < 20 || result.uint_32 > 60000) { + cifs_errorf(fc, "%s: Invalid max_credits value\n", + __func__); + goto cifs_parse_mount_err; + } + ctx->max_credits = result.uint_32; + break; + case Opt_max_channels: + if (result.uint_32 < 1 || result.uint_32 > CIFS_MAX_CHANNELS) { + cifs_errorf(fc, "%s: Invalid max_channels value, needs to be 1-%d\n", + __func__, CIFS_MAX_CHANNELS); + goto cifs_parse_mount_err; + } + ctx->max_channels = result.uint_32; + /* If more than one channel requested ... they want multichan */ + if (result.uint_32 > 1) + ctx->multichannel = true; + break; + case Opt_handletimeout: + ctx->handle_timeout = result.uint_32; + if (ctx->handle_timeout > SMB3_MAX_HANDLE_TIMEOUT) { + cifs_errorf(fc, "Invalid handle cache timeout, longer than 16 minutes\n"); + goto cifs_parse_mount_err; + } + break; + case Opt_source: + kfree(ctx->UNC); + ctx->UNC = NULL; + switch (smb3_parse_devname(param->string, ctx)) { + case 0: + break; + case -ENOMEM: + cifs_errorf(fc, "Unable to allocate memory for devname\n"); + goto cifs_parse_mount_err; + case -EINVAL: + cifs_errorf(fc, "Malformed UNC in devname\n"); + goto cifs_parse_mount_err; + default: + cifs_errorf(fc, "Unknown error parsing devname\n"); + goto cifs_parse_mount_err; + } + ctx->source = kstrdup(param->string, GFP_KERNEL); + if (ctx->source == NULL) { + cifs_errorf(fc, "OOM when copying UNC string\n"); + goto cifs_parse_mount_err; + } + fc->source = kstrdup(param->string, GFP_KERNEL); + if (fc->source == NULL) { + cifs_errorf(fc, "OOM when copying UNC string\n"); + goto cifs_parse_mount_err; + } + break; + case Opt_user: + kfree(ctx->username); + ctx->username = NULL; + if (strlen(param->string) == 0) { + /* null user, ie. anonymous authentication */ + ctx->nullauth = 1; + break; + } + + if (strnlen(param->string, CIFS_MAX_USERNAME_LEN) > + CIFS_MAX_USERNAME_LEN) { + pr_warn("username too long\n"); + goto cifs_parse_mount_err; + } + ctx->username = kstrdup(param->string, GFP_KERNEL); + if (ctx->username == NULL) { + cifs_errorf(fc, "OOM when copying username string\n"); + goto cifs_parse_mount_err; + } + break; + case Opt_pass: + kfree_sensitive(ctx->password); + ctx->password = NULL; + if (strlen(param->string) == 0) + break; + + ctx->password = kstrdup(param->string, GFP_KERNEL); + if (ctx->password == NULL) { + cifs_errorf(fc, "OOM when copying password string\n"); + goto cifs_parse_mount_err; + } + break; + case Opt_ip: + if (strlen(param->string) == 0) { + ctx->got_ip = false; + break; + } + if (!cifs_convert_address((struct sockaddr *)&ctx->dstaddr, + param->string, + strlen(param->string))) { + pr_err("bad ip= option (%s)\n", param->string); + goto cifs_parse_mount_err; + } + ctx->got_ip = true; + break; + case Opt_domain: + if (strnlen(param->string, CIFS_MAX_DOMAINNAME_LEN) + == CIFS_MAX_DOMAINNAME_LEN) { + pr_warn("domain name too long\n"); + goto cifs_parse_mount_err; + } + + kfree(ctx->domainname); + ctx->domainname = kstrdup(param->string, GFP_KERNEL); + if (ctx->domainname == NULL) { + cifs_errorf(fc, "OOM when copying domainname string\n"); + goto cifs_parse_mount_err; + } + cifs_dbg(FYI, "Domain name set\n"); + break; + case Opt_srcaddr: + if (!cifs_convert_address( + (struct sockaddr *)&ctx->srcaddr, + param->string, strlen(param->string))) { + pr_warn("Could not parse srcaddr: %s\n", + param->string); + goto cifs_parse_mount_err; + } + break; + case Opt_iocharset: + if (strnlen(param->string, 1024) >= 65) { + pr_warn("iocharset name too long\n"); + goto cifs_parse_mount_err; + } + + if (strncasecmp(param->string, "default", 7) != 0) { + kfree(ctx->iocharset); + ctx->iocharset = kstrdup(param->string, GFP_KERNEL); + if (ctx->iocharset == NULL) { + cifs_errorf(fc, "OOM when copying iocharset string\n"); + goto cifs_parse_mount_err; + } + } + /* if iocharset not set then load_nls_default + * is used by caller + */ + cifs_dbg(FYI, "iocharset set to %s\n", ctx->iocharset); + break; + case Opt_netbiosname: + memset(ctx->source_rfc1001_name, 0x20, + RFC1001_NAME_LEN); + /* + * FIXME: are there cases in which a comma can + * be valid in workstation netbios name (and + * need special handling)? + */ + for (i = 0; i < RFC1001_NAME_LEN; i++) { + /* don't ucase netbiosname for user */ + if (param->string[i] == 0) + break; + ctx->source_rfc1001_name[i] = param->string[i]; + } + /* The string has 16th byte zero still from + * set at top of the function + */ + if (i == RFC1001_NAME_LEN && param->string[i] != 0) + pr_warn("netbiosname longer than 15 truncated\n"); + break; + case Opt_servern: + /* last byte, type, is 0x20 for servr type */ + memset(ctx->target_rfc1001_name, 0x20, + RFC1001_NAME_LEN_WITH_NULL); + /* + * 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 the netbios name */ + for (i = 0; i < 15; i++) { + if (param->string[i] == 0) + break; + ctx->target_rfc1001_name[i] = param->string[i]; + } + + /* The string has 16th byte zero still from set at top of function */ + if (i == RFC1001_NAME_LEN && param->string[i] != 0) + pr_warn("server netbiosname longer than 15 truncated\n"); + break; + case Opt_ver: + /* version of mount userspace tools, not dialect */ + /* If interface changes in mount.cifs bump to new ver */ + if (strncasecmp(param->string, "1", 1) == 0) { + if (strlen(param->string) > 1) { + pr_warn("Bad mount helper ver=%s. Did you want SMB1 (CIFS) dialect and mean to type vers=1.0 instead?\n", + param->string); + goto cifs_parse_mount_err; + } + /* This is the default */ + break; + } + /* For all other value, error */ + pr_warn("Invalid mount helper version specified\n"); + goto cifs_parse_mount_err; + case Opt_vers: + /* protocol version (dialect) */ + if (cifs_parse_smb_version(fc, param->string, ctx, is_smb3) != 0) + goto cifs_parse_mount_err; + ctx->got_version = true; + break; + case Opt_sec: + if (cifs_parse_security_flavors(fc, param->string, ctx) != 0) + goto cifs_parse_mount_err; + break; + case Opt_cache: + if (cifs_parse_cache_flavor(fc, param->string, ctx) != 0) + goto cifs_parse_mount_err; + break; + case Opt_witness: +#ifndef CONFIG_CIFS_SWN_UPCALL + cifs_errorf(fc, "Witness support needs CONFIG_CIFS_SWN_UPCALL config option\n"); + goto cifs_parse_mount_err; +#endif + ctx->witness = true; + pr_warn_once("Witness protocol support is experimental\n"); + break; + case Opt_rootfs: +#ifndef CONFIG_CIFS_ROOT + cifs_dbg(VFS, "rootfs support requires CONFIG_CIFS_ROOT config option\n"); + goto cifs_parse_mount_err; +#endif + ctx->rootfs = true; + break; + case Opt_posixpaths: + if (result.negated) + ctx->posix_paths = 0; + else + ctx->posix_paths = 1; + break; + case Opt_unix: + if (result.negated) { + if (ctx->linux_ext == 1) + pr_warn_once("conflicting posix mount options specified\n"); + ctx->linux_ext = 0; + ctx->no_linux_ext = 1; + } else { + if (ctx->no_linux_ext == 1) + pr_warn_once("conflicting posix mount options specified\n"); + ctx->linux_ext = 1; + ctx->no_linux_ext = 0; + } + break; + case Opt_nocase: + ctx->nocase = 1; + break; + case Opt_brl: + if (result.negated) { + /* + * turn off mandatory locking in mode + * if remote locking is turned off since the + * local vfs will do advisory + */ + if (ctx->file_mode == + (S_IALLUGO & ~(S_ISUID | S_IXGRP))) + ctx->file_mode = S_IALLUGO; + ctx->nobrl = 1; + } else + ctx->nobrl = 0; + break; + case Opt_handlecache: + if (result.negated) + ctx->nohandlecache = 1; + else + ctx->nohandlecache = 0; + break; + case Opt_forcemandatorylock: + ctx->mand_lock = 1; + break; + case Opt_setuids: + ctx->setuids = result.negated; + break; + case Opt_intr: + ctx->intr = !result.negated; + break; + case Opt_setuidfromacl: + ctx->setuidfromacl = 1; + break; + case Opt_strictsync: + ctx->nostrictsync = result.negated; + break; + case Opt_serverino: + ctx->server_ino = !result.negated; + break; + case Opt_rwpidforward: + ctx->rwpidforward = 1; + break; + case Opt_modesid: + ctx->mode_ace = 1; + break; + case Opt_cifsacl: + ctx->cifs_acl = !result.negated; + break; + case Opt_acl: + ctx->no_psx_acl = result.negated; + break; + case Opt_locallease: + ctx->local_lease = 1; + break; + case Opt_sign: + ctx->sign = true; + break; + case Opt_ignore_signature: + ctx->sign = true; + ctx->ignore_signature = true; + break; + case Opt_seal: + /* we do not do the following in secFlags because seal + * is a per tree connection (mount) not a per socket + * or per-smb connection option in the protocol + * vol->secFlg |= CIFSSEC_MUST_SEAL; + */ + ctx->seal = 1; + break; + case Opt_noac: + pr_warn("Mount option noac not supported. Instead set /proc/fs/cifs/LookupCacheEnabled to 0\n"); + break; + case Opt_fsc: +#ifndef CONFIG_CIFS_FSCACHE + cifs_errorf(fc, "FS-Cache support needs CONFIG_CIFS_FSCACHE kernel config option set\n"); + goto cifs_parse_mount_err; +#endif + ctx->fsc = true; + break; + case Opt_mfsymlinks: + ctx->mfsymlinks = true; + break; + case Opt_multiuser: + ctx->multiuser = true; + break; + case Opt_sloppy: + ctx->sloppy = true; + break; + case Opt_nosharesock: + ctx->nosharesock = true; + break; + case Opt_persistent: + if (result.negated) { + ctx->nopersistent = true; + if (ctx->persistent) { + cifs_errorf(fc, "persistenthandles mount options conflict\n"); + goto cifs_parse_mount_err; + } + } else { + ctx->persistent = true; + if ((ctx->nopersistent) || (ctx->resilient)) { + cifs_errorf(fc, "persistenthandles mount options conflict\n"); + goto cifs_parse_mount_err; + } + } + break; + case Opt_resilient: + if (result.negated) { + ctx->resilient = false; /* already the default */ + } else { + ctx->resilient = true; + if (ctx->persistent) { + cifs_errorf(fc, "persistenthandles mount options conflict\n"); + goto cifs_parse_mount_err; + } + } + break; + case Opt_tcp_nodelay: + /* tcp nodelay should not usually be needed since we CORK/UNCORK the socket */ + if (result.negated) + ctx->sockopt_tcp_nodelay = false; + else + ctx->sockopt_tcp_nodelay = true; + break; + case Opt_domainauto: + ctx->domainauto = true; + break; + case Opt_rdma: + ctx->rdma = true; + break; + } + /* case Opt_ignore: - is ignored as expected ... */ + + return 0; + + cifs_parse_mount_err: + kfree_sensitive(ctx->password); + return -EINVAL; +} + +int smb3_init_fs_context(struct fs_context *fc) +{ + struct smb3_fs_context *ctx; + char *nodename = utsname()->nodename; + int i; + + ctx = kzalloc(sizeof(struct smb3_fs_context), GFP_KERNEL); + if (unlikely(!ctx)) + return -ENOMEM; + + strscpy(ctx->workstation_name, nodename, sizeof(ctx->workstation_name)); + + /* + * does not have to be perfect mapping since field is + * informational, only used for servers that do not support + * port 445 and it can be overridden at mount time + */ + memset(ctx->source_rfc1001_name, 0x20, RFC1001_NAME_LEN); + for (i = 0; i < strnlen(nodename, RFC1001_NAME_LEN); i++) + ctx->source_rfc1001_name[i] = toupper(nodename[i]); + + ctx->source_rfc1001_name[RFC1001_NAME_LEN] = 0; + /* + * null target name indicates to use *SMBSERVR default called name + * if we end up sending RFC1001 session initialize + */ + ctx->target_rfc1001_name[0] = 0; + ctx->cred_uid = current_uid(); + ctx->linux_uid = current_uid(); + ctx->linux_gid = current_gid(); + /* By default 4MB read ahead size, 1MB block size */ + ctx->bsize = CIFS_DEFAULT_IOSIZE; /* can improve cp performance significantly */ + ctx->rasize = 0; /* 0 = use default (ie negotiated rsize) for read ahead pages */ + + /* + * default to SFM style remapping of seven reserved characters + * unless user overrides it or we negotiate CIFS POSIX where + * it is unnecessary. Can not simultaneously use more than one mapping + * since then readdir could list files that open could not open + */ + ctx->remap = true; + + /* default to only allowing write access to owner of the mount */ + ctx->dir_mode = ctx->file_mode = S_IRUGO | S_IXUGO | S_IWUSR; + + /* ctx->retry default is 0 (i.e. "soft" limited retry not hard retry) */ + /* default is always to request posix paths. */ + ctx->posix_paths = 1; + /* default to using server inode numbers where available */ + ctx->server_ino = 1; + + /* default is to use strict cifs caching semantics */ + ctx->strict_io = true; + + ctx->acregmax = CIFS_DEF_ACTIMEO; + ctx->acdirmax = CIFS_DEF_ACTIMEO; + ctx->closetimeo = SMB3_DEF_DCLOSETIMEO; + + /* Most clients set timeout to 0, allows server to use its default */ + ctx->handle_timeout = 0; /* See MS-SMB2 spec section 2.2.14.2.12 */ + + /* offer SMB2.1 and later (SMB3 etc). Secure and widely accepted */ + ctx->ops = &smb30_operations; + ctx->vals = &smbdefault_values; + + ctx->echo_interval = SMB_ECHO_INTERVAL_DEFAULT; + + /* default to no multichannel (single server connection) */ + ctx->multichannel = false; + ctx->max_channels = 1; + + ctx->backupuid_specified = false; /* no backup intent for a user */ + ctx->backupgid_specified = false; /* no backup intent for a group */ + +/* + * short int override_uid = -1; + * short int override_gid = -1; + * char *nodename = strdup(utsname()->nodename); + * struct sockaddr *dstaddr = (struct sockaddr *)&vol->dstaddr; + */ + + fc->fs_private = ctx; + fc->ops = &smb3_fs_context_ops; + return 0; +} + +void +smb3_cleanup_fs_context_contents(struct smb3_fs_context *ctx) +{ + if (ctx == NULL) + return; + + /* + * Make sure this stays in sync with smb3_fs_context_dup() + */ + kfree(ctx->mount_options); + ctx->mount_options = NULL; + kfree(ctx->username); + ctx->username = NULL; + kfree_sensitive(ctx->password); + ctx->password = NULL; + kfree(ctx->server_hostname); + ctx->server_hostname = NULL; + kfree(ctx->UNC); + ctx->UNC = NULL; + kfree(ctx->source); + ctx->source = NULL; + kfree(ctx->domainname); + ctx->domainname = NULL; + kfree(ctx->nodename); + ctx->nodename = NULL; + kfree(ctx->iocharset); + ctx->iocharset = NULL; + kfree(ctx->prepath); + ctx->prepath = NULL; +} + +void +smb3_cleanup_fs_context(struct smb3_fs_context *ctx) +{ + if (!ctx) + return; + smb3_cleanup_fs_context_contents(ctx); + kfree(ctx); +} + +void smb3_update_mnt_flags(struct cifs_sb_info *cifs_sb) +{ + struct smb3_fs_context *ctx = cifs_sb->ctx; + + if (ctx->nodfs) + cifs_sb->mnt_cifs_flags |= CIFS_MOUNT_NO_DFS; + else + cifs_sb->mnt_cifs_flags &= ~CIFS_MOUNT_NO_DFS; + + if (ctx->noperm) + cifs_sb->mnt_cifs_flags |= CIFS_MOUNT_NO_PERM; + else + cifs_sb->mnt_cifs_flags &= ~CIFS_MOUNT_NO_PERM; + + if (ctx->setuids) + cifs_sb->mnt_cifs_flags |= CIFS_MOUNT_SET_UID; + else + cifs_sb->mnt_cifs_flags &= ~CIFS_MOUNT_SET_UID; + + if (ctx->setuidfromacl) + cifs_sb->mnt_cifs_flags |= CIFS_MOUNT_UID_FROM_ACL; + else + cifs_sb->mnt_cifs_flags &= ~CIFS_MOUNT_UID_FROM_ACL; + + if (ctx->server_ino) + cifs_sb->mnt_cifs_flags |= CIFS_MOUNT_SERVER_INUM; + else + cifs_sb->mnt_cifs_flags &= ~CIFS_MOUNT_SERVER_INUM; + + if (ctx->remap) + cifs_sb->mnt_cifs_flags |= CIFS_MOUNT_MAP_SFM_CHR; + else + cifs_sb->mnt_cifs_flags &= ~CIFS_MOUNT_MAP_SFM_CHR; + + if (ctx->sfu_remap) + cifs_sb->mnt_cifs_flags |= CIFS_MOUNT_MAP_SPECIAL_CHR; + else + cifs_sb->mnt_cifs_flags &= ~CIFS_MOUNT_MAP_SPECIAL_CHR; + + if (ctx->no_xattr) + cifs_sb->mnt_cifs_flags |= CIFS_MOUNT_NO_XATTR; + else + cifs_sb->mnt_cifs_flags &= ~CIFS_MOUNT_NO_XATTR; + + if (ctx->sfu_emul) + cifs_sb->mnt_cifs_flags |= CIFS_MOUNT_UNX_EMUL; + else + cifs_sb->mnt_cifs_flags &= ~CIFS_MOUNT_UNX_EMUL; + + if (ctx->nobrl) + cifs_sb->mnt_cifs_flags |= CIFS_MOUNT_NO_BRL; + else + cifs_sb->mnt_cifs_flags &= ~CIFS_MOUNT_NO_BRL; + + if (ctx->nohandlecache) + cifs_sb->mnt_cifs_flags |= CIFS_MOUNT_NO_HANDLE_CACHE; + else + cifs_sb->mnt_cifs_flags &= ~CIFS_MOUNT_NO_HANDLE_CACHE; + + if (ctx->nostrictsync) + cifs_sb->mnt_cifs_flags |= CIFS_MOUNT_NOSSYNC; + else + cifs_sb->mnt_cifs_flags &= ~CIFS_MOUNT_NOSSYNC; + + if (ctx->mand_lock) + cifs_sb->mnt_cifs_flags |= CIFS_MOUNT_NOPOSIXBRL; + else + cifs_sb->mnt_cifs_flags &= ~CIFS_MOUNT_NOPOSIXBRL; + + if (ctx->rwpidforward) + cifs_sb->mnt_cifs_flags |= CIFS_MOUNT_RWPIDFORWARD; + else + cifs_sb->mnt_cifs_flags &= ~CIFS_MOUNT_RWPIDFORWARD; + + if (ctx->mode_ace) + cifs_sb->mnt_cifs_flags |= CIFS_MOUNT_MODE_FROM_SID; + else + cifs_sb->mnt_cifs_flags &= ~CIFS_MOUNT_MODE_FROM_SID; + + if (ctx->cifs_acl) + cifs_sb->mnt_cifs_flags |= CIFS_MOUNT_CIFS_ACL; + else + cifs_sb->mnt_cifs_flags &= ~CIFS_MOUNT_CIFS_ACL; + + if (ctx->backupuid_specified) + cifs_sb->mnt_cifs_flags |= CIFS_MOUNT_CIFS_BACKUPUID; + else + cifs_sb->mnt_cifs_flags &= ~CIFS_MOUNT_CIFS_BACKUPUID; + + if (ctx->backupgid_specified) + cifs_sb->mnt_cifs_flags |= CIFS_MOUNT_CIFS_BACKUPGID; + else + cifs_sb->mnt_cifs_flags &= ~CIFS_MOUNT_CIFS_BACKUPGID; + + if (ctx->override_uid) + cifs_sb->mnt_cifs_flags |= CIFS_MOUNT_OVERR_UID; + else + cifs_sb->mnt_cifs_flags &= ~CIFS_MOUNT_OVERR_UID; + + if (ctx->override_gid) + cifs_sb->mnt_cifs_flags |= CIFS_MOUNT_OVERR_GID; + else + cifs_sb->mnt_cifs_flags &= ~CIFS_MOUNT_OVERR_GID; + + if (ctx->dynperm) + cifs_sb->mnt_cifs_flags |= CIFS_MOUNT_DYNPERM; + else + cifs_sb->mnt_cifs_flags &= ~CIFS_MOUNT_DYNPERM; + + if (ctx->fsc) + cifs_sb->mnt_cifs_flags |= CIFS_MOUNT_FSCACHE; + else + cifs_sb->mnt_cifs_flags &= ~CIFS_MOUNT_FSCACHE; + + if (ctx->multiuser) + cifs_sb->mnt_cifs_flags |= (CIFS_MOUNT_MULTIUSER | + CIFS_MOUNT_NO_PERM); + else + cifs_sb->mnt_cifs_flags &= ~CIFS_MOUNT_MULTIUSER; + + + if (ctx->strict_io) + cifs_sb->mnt_cifs_flags |= CIFS_MOUNT_STRICT_IO; + else + cifs_sb->mnt_cifs_flags &= ~CIFS_MOUNT_STRICT_IO; + + if (ctx->direct_io) + cifs_sb->mnt_cifs_flags |= CIFS_MOUNT_DIRECT_IO; + else + cifs_sb->mnt_cifs_flags &= ~CIFS_MOUNT_DIRECT_IO; + + if (ctx->mfsymlinks) + cifs_sb->mnt_cifs_flags |= CIFS_MOUNT_MF_SYMLINKS; + else + cifs_sb->mnt_cifs_flags &= ~CIFS_MOUNT_MF_SYMLINKS; + if (ctx->mfsymlinks) { + if (ctx->sfu_emul) { + /* + * Our SFU ("Services for Unix" emulation does not allow + * creating symlinks but does allow reading existing SFU + * symlinks (it does allow both creating and reading SFU + * style mknod and FIFOs though). When "mfsymlinks" and + * "sfu" are both enabled at the same time, it allows + * reading both types of symlinks, but will only create + * them with mfsymlinks format. This allows better + * Apple compatibility (probably better for Samba too) + * while still recognizing old Windows style symlinks. + */ + cifs_dbg(VFS, "mount options mfsymlinks and sfu both enabled\n"); + } + } + cifs_sb->mnt_cifs_flags &= ~CIFS_MOUNT_SHUTDOWN; + + return; +} diff --git a/fs/smb/client/fs_context.h b/fs/smb/client/fs_context.h new file mode 100644 index 0000000000000..26093f54d3e65 --- /dev/null +++ b/fs/smb/client/fs_context.h @@ -0,0 +1,293 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + * Copyright (C) 2020, Microsoft Corporation. + * + * Author(s): Steve French + * David Howells + */ + +#ifndef _FS_CONTEXT_H +#define _FS_CONTEXT_H + +#include "cifsglob.h" +#include +#include + +/* Log errors in fs_context (new mount api) but also in dmesg (old style) */ +#define cifs_errorf(fc, fmt, ...) \ + do { \ + errorf(fc, fmt, ## __VA_ARGS__); \ + cifs_dbg(VFS, fmt, ## __VA_ARGS__); \ + } while (0) + +enum smb_version { + Smb_1 = 1, + Smb_20, + Smb_21, + Smb_30, + Smb_302, + Smb_311, + Smb_3any, + Smb_default, + Smb_version_err +}; + +enum { + Opt_cache_loose, + Opt_cache_strict, + Opt_cache_none, + Opt_cache_ro, + Opt_cache_rw, + Opt_cache_err +}; + +enum cifs_sec_param { + Opt_sec_krb5, + Opt_sec_krb5i, + Opt_sec_krb5p, + Opt_sec_ntlmsspi, + Opt_sec_ntlmssp, + Opt_sec_ntlmv2, + Opt_sec_ntlmv2i, + Opt_sec_none, + + Opt_sec_err +}; + +enum cifs_param { + /* Mount options that take no arguments */ + Opt_user_xattr, + Opt_forceuid, + Opt_forcegid, + Opt_noblocksend, + Opt_noautotune, + Opt_nolease, + Opt_nosparse, + Opt_hard, + Opt_soft, + Opt_perm, + Opt_nodelete, + Opt_mapposix, + Opt_mapchars, + Opt_nomapchars, + Opt_sfu, + Opt_nodfs, + Opt_posixpaths, + Opt_unix, + Opt_nocase, + Opt_brl, + Opt_handlecache, + Opt_forcemandatorylock, + Opt_setuidfromacl, + Opt_setuids, + Opt_dynperm, + Opt_intr, + Opt_strictsync, + Opt_serverino, + Opt_rwpidforward, + Opt_cifsacl, + Opt_acl, + Opt_locallease, + Opt_sign, + Opt_ignore_signature, + Opt_seal, + Opt_noac, + Opt_fsc, + Opt_mfsymlinks, + Opt_multiuser, + Opt_sloppy, + Opt_nosharesock, + Opt_persistent, + Opt_resilient, + Opt_tcp_nodelay, + Opt_domainauto, + Opt_rdma, + Opt_modesid, + Opt_rootfs, + Opt_multichannel, + Opt_compress, + Opt_witness, + + /* Mount options which take numeric value */ + Opt_backupuid, + Opt_backupgid, + Opt_uid, + Opt_cruid, + Opt_gid, + Opt_port, + Opt_file_mode, + Opt_dirmode, + Opt_min_enc_offload, + Opt_blocksize, + Opt_rasize, + Opt_rsize, + Opt_wsize, + Opt_actimeo, + Opt_acdirmax, + Opt_acregmax, + Opt_closetimeo, + Opt_echo_interval, + Opt_max_credits, + Opt_snapshot, + Opt_max_channels, + Opt_handletimeout, + + /* Mount options which take string value */ + Opt_source, + Opt_user, + Opt_pass, + Opt_ip, + Opt_domain, + Opt_srcaddr, + Opt_iocharset, + Opt_netbiosname, + Opt_servern, + Opt_ver, + Opt_vers, + Opt_sec, + Opt_cache, + + /* Mount options to be ignored */ + Opt_ignore, + + Opt_err +}; + +struct smb3_fs_context { + bool uid_specified; + bool cruid_specified; + bool gid_specified; + bool sloppy; + bool got_ip; + bool got_version; + bool got_rsize; + bool got_wsize; + bool got_bsize; + unsigned short port; + + char *username; + char *password; + char *domainname; + char *source; + char *server_hostname; + char *UNC; + char *nodename; + char workstation_name[CIFS_MAX_WORKSTATION_LEN]; + char *iocharset; /* local code page for mapping to and from Unicode */ + char source_rfc1001_name[RFC1001_NAME_LEN_WITH_NULL]; /* clnt nb name */ + char target_rfc1001_name[RFC1001_NAME_LEN_WITH_NULL]; /* srvr nb name */ + kuid_t cred_uid; + kuid_t linux_uid; + kgid_t linux_gid; + kuid_t backupuid; + kgid_t backupgid; + umode_t file_mode; + umode_t dir_mode; + enum securityEnum sectype; /* sectype requested via mnt opts */ + bool sign; /* was signing requested via mnt opts? */ + bool ignore_signature:1; + bool retry:1; + bool intr:1; + bool setuids:1; + bool setuidfromacl:1; + bool override_uid:1; + bool override_gid:1; + bool dynperm:1; + bool noperm:1; + bool nodelete:1; + bool mode_ace:1; + bool no_psx_acl:1; /* set if posix acl support should be disabled */ + bool cifs_acl:1; + bool backupuid_specified; /* mount option backupuid is specified */ + bool backupgid_specified; /* mount option backupgid is specified */ + bool no_xattr:1; /* set if xattr (EA) support should be disabled*/ + bool server_ino:1; /* use inode numbers from server ie UniqueId */ + bool direct_io:1; + bool strict_io:1; /* strict cache behavior */ + bool cache_ro:1; + bool cache_rw:1; + bool remap:1; /* set to remap seven reserved chars in filenames */ + bool sfu_remap:1; /* remap seven reserved chars ala SFU */ + bool posix_paths:1; /* unset to not ask for posix pathnames. */ + bool no_linux_ext:1; + bool linux_ext:1; + bool sfu_emul:1; + bool nullauth:1; /* attempt to authenticate with null user */ + bool nocase:1; /* request case insensitive filenames */ + bool nobrl:1; /* disable sending byte range locks to srv */ + bool nohandlecache:1; /* disable caching dir handles if srvr probs */ + bool mand_lock:1; /* send mandatory not posix byte range lock reqs */ + bool seal:1; /* request transport encryption on share */ + bool nodfs:1; /* Do not request DFS, even if available */ + bool local_lease:1; /* check leases only on local system, not remote */ + bool noblocksnd:1; + bool noautotune:1; + bool nostrictsync:1; /* do not force expensive SMBflush on every sync */ + bool no_lease:1; /* disable requesting leases */ + bool no_sparse:1; /* do not attempt to set files sparse */ + bool fsc:1; /* enable fscache */ + bool mfsymlinks:1; /* use Minshall+French Symlinks */ + bool multiuser:1; + bool rwpidforward:1; /* pid forward for read/write operations */ + bool nosharesock:1; + bool persistent:1; + bool nopersistent:1; + bool resilient:1; /* noresilient not required since not fored for CA */ + bool domainauto:1; + bool rdma:1; + bool multichannel:1; + bool use_client_guid:1; + /* reuse existing guid for multichannel */ + u8 client_guid[SMB2_CLIENT_GUID_SIZE]; + unsigned int bsize; + unsigned int rasize; + unsigned int rsize; + unsigned int wsize; + unsigned int min_offload; + bool sockopt_tcp_nodelay:1; + /* attribute cache timemout for files and directories in jiffies */ + unsigned long acregmax; + unsigned long acdirmax; + /* timeout for deferred close of files in jiffies */ + unsigned long closetimeo; + struct smb_version_operations *ops; + struct smb_version_values *vals; + char *prepath; + struct sockaddr_storage dstaddr; /* destination address */ + struct sockaddr_storage srcaddr; /* allow binding to a local IP */ + struct nls_table *local_nls; /* This is a copy of the pointer in cifs_sb */ + unsigned int echo_interval; /* echo interval in secs */ + __u64 snapshot_time; /* needed for timewarp tokens */ + __u32 handle_timeout; /* persistent and durable handle timeout in ms */ + unsigned int max_credits; /* smb3 max_credits 10 < credits < 60000 */ + unsigned int max_channels; + __u16 compression; /* compression algorithm 0xFFFF default 0=disabled */ + bool rootfs:1; /* if it's a SMB root file system */ + bool witness:1; /* use witness protocol */ + + char *mount_options; +}; + +extern const struct fs_parameter_spec smb3_fs_parameters[]; + +extern int smb3_init_fs_context(struct fs_context *fc); +extern void smb3_cleanup_fs_context_contents(struct smb3_fs_context *ctx); +extern void smb3_cleanup_fs_context(struct smb3_fs_context *ctx); + +static inline struct smb3_fs_context *smb3_fc2context(const struct fs_context *fc) +{ + return fc->fs_private; +} + +extern int smb3_fs_context_dup(struct smb3_fs_context *new_ctx, struct smb3_fs_context *ctx); +extern void smb3_update_mnt_flags(struct cifs_sb_info *cifs_sb); + +/* + * max deferred close timeout (jiffies) - 2^30 + */ +#define SMB3_MAX_DCLOSETIMEO (1 << 30) +#define SMB3_DEF_DCLOSETIMEO (1 * HZ) /* even 1 sec enough to help eg open/write/close/open/read */ + +extern char *cifs_sanitize_prepath(char *prepath, gfp_t gfp); + +#endif diff --git a/fs/smb/client/fscache.c b/fs/smb/client/fscache.c new file mode 100644 index 0000000000000..f6f3a6b75601b --- /dev/null +++ b/fs/smb/client/fscache.c @@ -0,0 +1,253 @@ +// SPDX-License-Identifier: LGPL-2.1 +/* + * CIFS filesystem cache interface + * + * Copyright (c) 2010 Novell, Inc. + * Author(s): Suresh Jayaraman + * + */ +#include "fscache.h" +#include "cifsglob.h" +#include "cifs_debug.h" +#include "cifs_fs_sb.h" +#include "cifsproto.h" + +static void cifs_fscache_fill_volume_coherency( + struct cifs_tcon *tcon, + struct cifs_fscache_volume_coherency_data *cd) +{ + memset(cd, 0, sizeof(*cd)); + cd->resource_id = cpu_to_le64(tcon->resource_id); + cd->vol_create_time = tcon->vol_create_time; + cd->vol_serial_number = cpu_to_le32(tcon->vol_serial_number); +} + +int cifs_fscache_get_super_cookie(struct cifs_tcon *tcon) +{ + struct cifs_fscache_volume_coherency_data cd; + struct TCP_Server_Info *server = tcon->ses->server; + struct fscache_volume *vcookie; + const struct sockaddr *sa = (struct sockaddr *)&server->dstaddr; + size_t slen, i; + char *sharename; + char *key; + int ret = -ENOMEM; + + tcon->fscache = NULL; + switch (sa->sa_family) { + case AF_INET: + case AF_INET6: + break; + default: + cifs_dbg(VFS, "Unknown network family '%d'\n", sa->sa_family); + return -EINVAL; + } + + memset(&key, 0, sizeof(key)); + + sharename = extract_sharename(tcon->tree_name); + if (IS_ERR(sharename)) { + cifs_dbg(FYI, "%s: couldn't extract sharename\n", __func__); + return -EINVAL; + } + + slen = strlen(sharename); + for (i = 0; i < slen; i++) + if (sharename[i] == '/') + sharename[i] = ';'; + + key = kasprintf(GFP_KERNEL, "cifs,%pISpc,%s", sa, sharename); + if (!key) + goto out; + + cifs_fscache_fill_volume_coherency(tcon, &cd); + vcookie = fscache_acquire_volume(key, + NULL, /* preferred_cache */ + &cd, sizeof(cd)); + cifs_dbg(FYI, "%s: (%s/0x%p)\n", __func__, key, vcookie); + if (IS_ERR(vcookie)) { + if (vcookie != ERR_PTR(-EBUSY)) { + ret = PTR_ERR(vcookie); + goto out_2; + } + pr_err("Cache volume key already in use (%s)\n", key); + vcookie = NULL; + } + + tcon->fscache = vcookie; + ret = 0; +out_2: + kfree(key); +out: + kfree(sharename); + return ret; +} + +void cifs_fscache_release_super_cookie(struct cifs_tcon *tcon) +{ + struct cifs_fscache_volume_coherency_data cd; + + cifs_dbg(FYI, "%s: (0x%p)\n", __func__, tcon->fscache); + + cifs_fscache_fill_volume_coherency(tcon, &cd); + fscache_relinquish_volume(tcon->fscache, &cd, false); + tcon->fscache = NULL; +} + +void cifs_fscache_get_inode_cookie(struct inode *inode) +{ + struct cifs_fscache_inode_coherency_data cd; + struct cifsInodeInfo *cifsi = CIFS_I(inode); + struct cifs_sb_info *cifs_sb = CIFS_SB(inode->i_sb); + struct cifs_tcon *tcon = cifs_sb_master_tcon(cifs_sb); + + cifs_fscache_fill_coherency(&cifsi->netfs.inode, &cd); + + cifsi->netfs.cache = + fscache_acquire_cookie(tcon->fscache, 0, + &cifsi->uniqueid, sizeof(cifsi->uniqueid), + &cd, sizeof(cd), + i_size_read(&cifsi->netfs.inode)); +} + +void cifs_fscache_unuse_inode_cookie(struct inode *inode, bool update) +{ + if (update) { + struct cifs_fscache_inode_coherency_data cd; + loff_t i_size = i_size_read(inode); + + cifs_fscache_fill_coherency(inode, &cd); + fscache_unuse_cookie(cifs_inode_cookie(inode), &cd, &i_size); + } else { + fscache_unuse_cookie(cifs_inode_cookie(inode), NULL, NULL); + } +} + +void cifs_fscache_release_inode_cookie(struct inode *inode) +{ + struct cifsInodeInfo *cifsi = CIFS_I(inode); + struct fscache_cookie *cookie = cifs_inode_cookie(inode); + + if (cookie) { + cifs_dbg(FYI, "%s: (0x%p)\n", __func__, cookie); + fscache_relinquish_cookie(cookie, false); + cifsi->netfs.cache = NULL; + } +} + +/* + * Fallback page reading interface. + */ +static int fscache_fallback_read_page(struct inode *inode, struct page *page) +{ + struct netfs_cache_resources cres; + struct fscache_cookie *cookie = cifs_inode_cookie(inode); + struct iov_iter iter; + struct bio_vec bvec[1]; + int ret; + + memset(&cres, 0, sizeof(cres)); + bvec[0].bv_page = page; + bvec[0].bv_offset = 0; + bvec[0].bv_len = PAGE_SIZE; + iov_iter_bvec(&iter, ITER_DEST, bvec, ARRAY_SIZE(bvec), PAGE_SIZE); + + ret = fscache_begin_read_operation(&cres, cookie); + if (ret < 0) + return ret; + + ret = fscache_read(&cres, page_offset(page), &iter, NETFS_READ_HOLE_FAIL, + NULL, NULL); + fscache_end_operation(&cres); + return ret; +} + +/* + * Fallback page writing interface. + */ +static int fscache_fallback_write_page(struct inode *inode, struct page *page, + bool no_space_allocated_yet) +{ + struct netfs_cache_resources cres; + struct fscache_cookie *cookie = cifs_inode_cookie(inode); + struct iov_iter iter; + struct bio_vec bvec[1]; + loff_t start = page_offset(page); + size_t len = PAGE_SIZE; + int ret; + + memset(&cres, 0, sizeof(cres)); + bvec[0].bv_page = page; + bvec[0].bv_offset = 0; + bvec[0].bv_len = PAGE_SIZE; + iov_iter_bvec(&iter, ITER_SOURCE, bvec, ARRAY_SIZE(bvec), PAGE_SIZE); + + ret = fscache_begin_write_operation(&cres, cookie); + if (ret < 0) + return ret; + + ret = cres.ops->prepare_write(&cres, &start, &len, i_size_read(inode), + no_space_allocated_yet); + if (ret == 0) + ret = fscache_write(&cres, page_offset(page), &iter, NULL, NULL); + fscache_end_operation(&cres); + return ret; +} + +/* + * Retrieve a page from FS-Cache + */ +int __cifs_readpage_from_fscache(struct inode *inode, struct page *page) +{ + int ret; + + cifs_dbg(FYI, "%s: (fsc:%p, p:%p, i:0x%p\n", + __func__, cifs_inode_cookie(inode), page, inode); + + ret = fscache_fallback_read_page(inode, page); + if (ret < 0) + return ret; + + /* Read completed synchronously */ + SetPageUptodate(page); + return 0; +} + +void __cifs_readpage_to_fscache(struct inode *inode, struct page *page) +{ + cifs_dbg(FYI, "%s: (fsc: %p, p: %p, i: %p)\n", + __func__, cifs_inode_cookie(inode), page, inode); + + fscache_fallback_write_page(inode, page, true); +} + +/* + * Query the cache occupancy. + */ +int __cifs_fscache_query_occupancy(struct inode *inode, + pgoff_t first, unsigned int nr_pages, + pgoff_t *_data_first, + unsigned int *_data_nr_pages) +{ + struct netfs_cache_resources cres; + struct fscache_cookie *cookie = cifs_inode_cookie(inode); + loff_t start, data_start; + size_t len, data_len; + int ret; + + ret = fscache_begin_read_operation(&cres, cookie); + if (ret < 0) + return ret; + + start = first * PAGE_SIZE; + len = nr_pages * PAGE_SIZE; + ret = cres.ops->query_occupancy(&cres, start, len, PAGE_SIZE, + &data_start, &data_len); + if (ret == 0) { + *_data_first = data_start / PAGE_SIZE; + *_data_nr_pages = len / PAGE_SIZE; + } + + fscache_end_operation(&cres); + return ret; +} diff --git a/fs/smb/client/fscache.h b/fs/smb/client/fscache.h new file mode 100644 index 0000000000000..67b601041f0a3 --- /dev/null +++ b/fs/smb/client/fscache.h @@ -0,0 +1,148 @@ +/* SPDX-License-Identifier: LGPL-2.1 */ +/* + * CIFS filesystem cache interface definitions + * + * Copyright (c) 2010 Novell, Inc. + * Authors(s): Suresh Jayaraman (sjayaraman@suse.de> + * + */ +#ifndef _CIFS_FSCACHE_H +#define _CIFS_FSCACHE_H + +#include +#include + +#include "cifsglob.h" + +/* + * Coherency data attached to CIFS volume within the cache + */ +struct cifs_fscache_volume_coherency_data { + __le64 resource_id; /* unique server resource id */ + __le64 vol_create_time; + __le32 vol_serial_number; +} __packed; + +/* + * Coherency data attached to CIFS inode within the cache. + */ +struct cifs_fscache_inode_coherency_data { + __le64 last_write_time_sec; + __le64 last_change_time_sec; + __le32 last_write_time_nsec; + __le32 last_change_time_nsec; +}; + +#ifdef CONFIG_CIFS_FSCACHE + +/* + * fscache.c + */ +extern int cifs_fscache_get_super_cookie(struct cifs_tcon *); +extern void cifs_fscache_release_super_cookie(struct cifs_tcon *); + +extern void cifs_fscache_get_inode_cookie(struct inode *inode); +extern void cifs_fscache_release_inode_cookie(struct inode *); +extern void cifs_fscache_unuse_inode_cookie(struct inode *inode, bool update); + +static inline +void cifs_fscache_fill_coherency(struct inode *inode, + struct cifs_fscache_inode_coherency_data *cd) +{ + struct cifsInodeInfo *cifsi = CIFS_I(inode); + + memset(cd, 0, sizeof(*cd)); + cd->last_write_time_sec = cpu_to_le64(cifsi->netfs.inode.i_mtime.tv_sec); + cd->last_write_time_nsec = cpu_to_le32(cifsi->netfs.inode.i_mtime.tv_nsec); + cd->last_change_time_sec = cpu_to_le64(cifsi->netfs.inode.i_ctime.tv_sec); + cd->last_change_time_nsec = cpu_to_le32(cifsi->netfs.inode.i_ctime.tv_nsec); +} + + +static inline struct fscache_cookie *cifs_inode_cookie(struct inode *inode) +{ + return netfs_i_cookie(&CIFS_I(inode)->netfs); +} + +static inline void cifs_invalidate_cache(struct inode *inode, unsigned int flags) +{ + struct cifs_fscache_inode_coherency_data cd; + + cifs_fscache_fill_coherency(inode, &cd); + fscache_invalidate(cifs_inode_cookie(inode), &cd, + i_size_read(inode), flags); +} + +extern int __cifs_fscache_query_occupancy(struct inode *inode, + pgoff_t first, unsigned int nr_pages, + pgoff_t *_data_first, + unsigned int *_data_nr_pages); + +static inline int cifs_fscache_query_occupancy(struct inode *inode, + pgoff_t first, unsigned int nr_pages, + pgoff_t *_data_first, + unsigned int *_data_nr_pages) +{ + if (!cifs_inode_cookie(inode)) + return -ENOBUFS; + return __cifs_fscache_query_occupancy(inode, first, nr_pages, + _data_first, _data_nr_pages); +} + +extern int __cifs_readpage_from_fscache(struct inode *pinode, struct page *ppage); +extern void __cifs_readpage_to_fscache(struct inode *pinode, struct page *ppage); + + +static inline int cifs_readpage_from_fscache(struct inode *inode, + struct page *page) +{ + if (cifs_inode_cookie(inode)) + return __cifs_readpage_from_fscache(inode, page); + return -ENOBUFS; +} + +static inline void cifs_readpage_to_fscache(struct inode *inode, + struct page *page) +{ + if (cifs_inode_cookie(inode)) + __cifs_readpage_to_fscache(inode, page); +} + +#else /* CONFIG_CIFS_FSCACHE */ +static inline +void cifs_fscache_fill_coherency(struct inode *inode, + struct cifs_fscache_inode_coherency_data *cd) +{ +} + +static inline int cifs_fscache_get_super_cookie(struct cifs_tcon *tcon) { return 0; } +static inline void cifs_fscache_release_super_cookie(struct cifs_tcon *tcon) {} + +static inline void cifs_fscache_get_inode_cookie(struct inode *inode) {} +static inline void cifs_fscache_release_inode_cookie(struct inode *inode) {} +static inline void cifs_fscache_unuse_inode_cookie(struct inode *inode, bool update) {} +static inline struct fscache_cookie *cifs_inode_cookie(struct inode *inode) { return NULL; } +static inline void cifs_invalidate_cache(struct inode *inode, unsigned int flags) {} + +static inline int cifs_fscache_query_occupancy(struct inode *inode, + pgoff_t first, unsigned int nr_pages, + pgoff_t *_data_first, + unsigned int *_data_nr_pages) +{ + *_data_first = ULONG_MAX; + *_data_nr_pages = 0; + return -ENOBUFS; +} + +static inline int +cifs_readpage_from_fscache(struct inode *inode, struct page *page) +{ + return -ENOBUFS; +} + +static inline +void cifs_readpage_to_fscache(struct inode *inode, struct page *page) {} + +#endif /* CONFIG_CIFS_FSCACHE */ + +#endif /* _CIFS_FSCACHE_H */ diff --git a/fs/smb/client/inode.c b/fs/smb/client/inode.c new file mode 100644 index 0000000000000..92c1ed9304be7 --- /dev/null +++ b/fs/smb/client/inode.c @@ -0,0 +1,3093 @@ +// SPDX-License-Identifier: LGPL-2.1 +/* + * + * Copyright (C) International Business Machines Corp., 2002,2010 + * Author(s): Steve French (sfrench@us.ibm.com) + * + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "cifsfs.h" +#include "cifspdu.h" +#include "cifsglob.h" +#include "cifsproto.h" +#include "smb2proto.h" +#include "cifs_debug.h" +#include "cifs_fs_sb.h" +#include "cifs_unicode.h" +#include "fscache.h" +#include "fs_context.h" +#include "cifs_ioctl.h" +#include "cached_dir.h" + +static void cifs_set_ops(struct inode *inode) +{ + struct cifs_sb_info *cifs_sb = CIFS_SB(inode->i_sb); + + switch (inode->i_mode & S_IFMT) { + case S_IFREG: + inode->i_op = &cifs_file_inode_ops; + if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_DIRECT_IO) { + if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_NO_BRL) + inode->i_fop = &cifs_file_direct_nobrl_ops; + else + inode->i_fop = &cifs_file_direct_ops; + } else if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_STRICT_IO) { + if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_NO_BRL) + inode->i_fop = &cifs_file_strict_nobrl_ops; + else + inode->i_fop = &cifs_file_strict_ops; + } else if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_NO_BRL) + inode->i_fop = &cifs_file_nobrl_ops; + else { /* not direct, send byte range locks */ + inode->i_fop = &cifs_file_ops; + } + + /* check if server can support readahead */ + if (cifs_sb_master_tcon(cifs_sb)->ses->server->max_read < + PAGE_SIZE + MAX_CIFS_HDR_SIZE) + inode->i_data.a_ops = &cifs_addr_ops_smallbuf; + else + inode->i_data.a_ops = &cifs_addr_ops; + break; + case S_IFDIR: +#ifdef CONFIG_CIFS_DFS_UPCALL + if (IS_AUTOMOUNT(inode)) { + inode->i_op = &cifs_dfs_referral_inode_operations; + } else { +#else /* NO DFS support, treat as a directory */ + { +#endif + inode->i_op = &cifs_dir_inode_ops; + inode->i_fop = &cifs_dir_ops; + } + break; + case S_IFLNK: + inode->i_op = &cifs_symlink_inode_ops; + break; + default: + init_special_inode(inode, inode->i_mode, inode->i_rdev); + break; + } +} + +/* check inode attributes against fattr. If they don't match, tag the + * inode for cache invalidation + */ +static void +cifs_revalidate_cache(struct inode *inode, struct cifs_fattr *fattr) +{ + struct cifs_fscache_inode_coherency_data cd; + struct cifsInodeInfo *cifs_i = CIFS_I(inode); + + cifs_dbg(FYI, "%s: revalidating inode %llu\n", + __func__, cifs_i->uniqueid); + + if (inode->i_state & I_NEW) { + cifs_dbg(FYI, "%s: inode %llu is new\n", + __func__, cifs_i->uniqueid); + return; + } + + /* don't bother with revalidation if we have an oplock */ + if (CIFS_CACHE_READ(cifs_i)) { + cifs_dbg(FYI, "%s: inode %llu is oplocked\n", + __func__, cifs_i->uniqueid); + return; + } + + /* revalidate if mtime or size have changed */ + fattr->cf_mtime = timestamp_truncate(fattr->cf_mtime, inode); + if (timespec64_equal(&inode->i_mtime, &fattr->cf_mtime) && + cifs_i->server_eof == fattr->cf_eof) { + cifs_dbg(FYI, "%s: inode %llu is unchanged\n", + __func__, cifs_i->uniqueid); + return; + } + + cifs_dbg(FYI, "%s: invalidating inode %llu mapping\n", + __func__, cifs_i->uniqueid); + set_bit(CIFS_INO_INVALID_MAPPING, &cifs_i->flags); + /* Invalidate fscache cookie */ + cifs_fscache_fill_coherency(&cifs_i->netfs.inode, &cd); + fscache_invalidate(cifs_inode_cookie(inode), &cd, i_size_read(inode), 0); +} + +/* + * copy nlink to the inode, unless it wasn't provided. Provide + * sane values if we don't have an existing one and none was provided + */ +static void +cifs_nlink_fattr_to_inode(struct inode *inode, struct cifs_fattr *fattr) +{ + /* + * if we're in a situation where we can't trust what we + * got from the server (readdir, some non-unix cases) + * fake reasonable values + */ + if (fattr->cf_flags & CIFS_FATTR_UNKNOWN_NLINK) { + /* only provide fake values on a new inode */ + if (inode->i_state & I_NEW) { + if (fattr->cf_cifsattrs & ATTR_DIRECTORY) + set_nlink(inode, 2); + else + set_nlink(inode, 1); + } + return; + } + + /* we trust the server, so update it */ + set_nlink(inode, fattr->cf_nlink); +} + +/* populate an inode with info from a cifs_fattr struct */ +int +cifs_fattr_to_inode(struct inode *inode, struct cifs_fattr *fattr) +{ + struct cifsInodeInfo *cifs_i = CIFS_I(inode); + struct cifs_sb_info *cifs_sb = CIFS_SB(inode->i_sb); + + if (!(inode->i_state & I_NEW) && + unlikely(inode_wrong_type(inode, fattr->cf_mode))) { + CIFS_I(inode)->time = 0; /* force reval */ + return -ESTALE; + } + + cifs_revalidate_cache(inode, fattr); + + spin_lock(&inode->i_lock); + fattr->cf_mtime = timestamp_truncate(fattr->cf_mtime, inode); + fattr->cf_atime = timestamp_truncate(fattr->cf_atime, inode); + fattr->cf_ctime = timestamp_truncate(fattr->cf_ctime, inode); + /* we do not want atime to be less than mtime, it broke some apps */ + if (timespec64_compare(&fattr->cf_atime, &fattr->cf_mtime) < 0) + inode->i_atime = fattr->cf_mtime; + else + inode->i_atime = fattr->cf_atime; + inode->i_mtime = fattr->cf_mtime; + inode->i_ctime = fattr->cf_ctime; + inode->i_rdev = fattr->cf_rdev; + cifs_nlink_fattr_to_inode(inode, fattr); + inode->i_uid = fattr->cf_uid; + inode->i_gid = fattr->cf_gid; + + /* if dynperm is set, don't clobber existing mode */ + if (inode->i_state & I_NEW || + !(cifs_sb->mnt_cifs_flags & CIFS_MOUNT_DYNPERM)) + inode->i_mode = fattr->cf_mode; + + cifs_i->cifsAttrs = fattr->cf_cifsattrs; + + if (fattr->cf_flags & CIFS_FATTR_NEED_REVAL) + cifs_i->time = 0; + else + cifs_i->time = jiffies; + + if (fattr->cf_flags & CIFS_FATTR_DELETE_PENDING) + set_bit(CIFS_INO_DELETE_PENDING, &cifs_i->flags); + else + clear_bit(CIFS_INO_DELETE_PENDING, &cifs_i->flags); + + cifs_i->server_eof = fattr->cf_eof; + /* + * Can't safely change the file size here if the client is writing to + * it due to potential races. + */ + if (is_size_safe_to_change(cifs_i, fattr->cf_eof)) { + i_size_write(inode, fattr->cf_eof); + + /* + * i_blocks is not related to (i_size / i_blksize), + * but instead 512 byte (2**9) size is required for + * calculating num blocks. + */ + inode->i_blocks = (512 - 1 + fattr->cf_bytes) >> 9; + } + + if (S_ISLNK(fattr->cf_mode)) { + kfree(cifs_i->symlink_target); + cifs_i->symlink_target = fattr->cf_symlink_target; + fattr->cf_symlink_target = NULL; + } + spin_unlock(&inode->i_lock); + + if (fattr->cf_flags & CIFS_FATTR_DFS_REFERRAL) + inode->i_flags |= S_AUTOMOUNT; + if (inode->i_state & I_NEW) + cifs_set_ops(inode); + return 0; +} + +void +cifs_fill_uniqueid(struct super_block *sb, struct cifs_fattr *fattr) +{ + struct cifs_sb_info *cifs_sb = CIFS_SB(sb); + + if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_SERVER_INUM) + return; + + fattr->cf_uniqueid = iunique(sb, ROOT_I); +} + +/* Fill a cifs_fattr struct with info from FILE_UNIX_BASIC_INFO. */ +void +cifs_unix_basic_to_fattr(struct cifs_fattr *fattr, FILE_UNIX_BASIC_INFO *info, + struct cifs_sb_info *cifs_sb) +{ + memset(fattr, 0, sizeof(*fattr)); + fattr->cf_uniqueid = le64_to_cpu(info->UniqueId); + fattr->cf_bytes = le64_to_cpu(info->NumOfBytes); + fattr->cf_eof = le64_to_cpu(info->EndOfFile); + + fattr->cf_atime = cifs_NTtimeToUnix(info->LastAccessTime); + fattr->cf_mtime = cifs_NTtimeToUnix(info->LastModificationTime); + fattr->cf_ctime = cifs_NTtimeToUnix(info->LastStatusChange); + /* old POSIX extensions don't get create time */ + + fattr->cf_mode = le64_to_cpu(info->Permissions); + + /* + * Since we set the inode type below we need to mask off + * to avoid strange results if bits set above. + */ + fattr->cf_mode &= ~S_IFMT; + switch (le32_to_cpu(info->Type)) { + case UNIX_FILE: + fattr->cf_mode |= S_IFREG; + fattr->cf_dtype = DT_REG; + break; + case UNIX_SYMLINK: + fattr->cf_mode |= S_IFLNK; + fattr->cf_dtype = DT_LNK; + break; + case UNIX_DIR: + fattr->cf_mode |= S_IFDIR; + fattr->cf_dtype = DT_DIR; + break; + case UNIX_CHARDEV: + fattr->cf_mode |= S_IFCHR; + fattr->cf_dtype = DT_CHR; + fattr->cf_rdev = MKDEV(le64_to_cpu(info->DevMajor), + le64_to_cpu(info->DevMinor) & MINORMASK); + break; + case UNIX_BLOCKDEV: + fattr->cf_mode |= S_IFBLK; + fattr->cf_dtype = DT_BLK; + fattr->cf_rdev = MKDEV(le64_to_cpu(info->DevMajor), + le64_to_cpu(info->DevMinor) & MINORMASK); + break; + case UNIX_FIFO: + fattr->cf_mode |= S_IFIFO; + fattr->cf_dtype = DT_FIFO; + break; + case UNIX_SOCKET: + fattr->cf_mode |= S_IFSOCK; + fattr->cf_dtype = DT_SOCK; + break; + default: + /* safest to call it a file if we do not know */ + fattr->cf_mode |= S_IFREG; + fattr->cf_dtype = DT_REG; + cifs_dbg(FYI, "unknown type %d\n", le32_to_cpu(info->Type)); + break; + } + + fattr->cf_uid = cifs_sb->ctx->linux_uid; + if (!(cifs_sb->mnt_cifs_flags & CIFS_MOUNT_OVERR_UID)) { + u64 id = le64_to_cpu(info->Uid); + if (id < ((uid_t)-1)) { + kuid_t uid = make_kuid(&init_user_ns, id); + if (uid_valid(uid)) + fattr->cf_uid = uid; + } + } + + fattr->cf_gid = cifs_sb->ctx->linux_gid; + if (!(cifs_sb->mnt_cifs_flags & CIFS_MOUNT_OVERR_GID)) { + u64 id = le64_to_cpu(info->Gid); + if (id < ((gid_t)-1)) { + kgid_t gid = make_kgid(&init_user_ns, id); + if (gid_valid(gid)) + fattr->cf_gid = gid; + } + } + + fattr->cf_nlink = le64_to_cpu(info->Nlinks); +} + +/* + * Fill a cifs_fattr struct with fake inode info. + * + * Needed to setup cifs_fattr data for the directory which is the + * junction to the new submount (ie to setup the fake directory + * which represents a DFS referral). + */ +static void +cifs_create_dfs_fattr(struct cifs_fattr *fattr, struct super_block *sb) +{ + struct cifs_sb_info *cifs_sb = CIFS_SB(sb); + + cifs_dbg(FYI, "creating fake fattr for DFS referral\n"); + + memset(fattr, 0, sizeof(*fattr)); + fattr->cf_mode = S_IFDIR | S_IXUGO | S_IRWXU; + fattr->cf_uid = cifs_sb->ctx->linux_uid; + fattr->cf_gid = cifs_sb->ctx->linux_gid; + ktime_get_coarse_real_ts64(&fattr->cf_mtime); + fattr->cf_atime = fattr->cf_ctime = fattr->cf_mtime; + fattr->cf_nlink = 2; + fattr->cf_flags = CIFS_FATTR_DFS_REFERRAL; +} + +#ifdef CONFIG_CIFS_ALLOW_INSECURE_LEGACY +static int +cifs_get_file_info_unix(struct file *filp) +{ + int rc; + unsigned int xid; + FILE_UNIX_BASIC_INFO find_data; + struct cifs_fattr fattr = {}; + struct inode *inode = file_inode(filp); + struct cifs_sb_info *cifs_sb = CIFS_SB(inode->i_sb); + struct cifsFileInfo *cfile = filp->private_data; + struct cifs_tcon *tcon = tlink_tcon(cfile->tlink); + + xid = get_xid(); + + if (cfile->symlink_target) { + fattr.cf_symlink_target = kstrdup(cfile->symlink_target, GFP_KERNEL); + if (!fattr.cf_symlink_target) { + rc = -ENOMEM; + goto cifs_gfiunix_out; + } + } + + rc = CIFSSMBUnixQFileInfo(xid, tcon, cfile->fid.netfid, &find_data); + if (!rc) { + cifs_unix_basic_to_fattr(&fattr, &find_data, cifs_sb); + } else if (rc == -EREMOTE) { + cifs_create_dfs_fattr(&fattr, inode->i_sb); + rc = 0; + } else + goto cifs_gfiunix_out; + + rc = cifs_fattr_to_inode(inode, &fattr); + +cifs_gfiunix_out: + free_xid(xid); + return rc; +} + +int cifs_get_inode_info_unix(struct inode **pinode, + const unsigned char *full_path, + struct super_block *sb, unsigned int xid) +{ + int rc; + FILE_UNIX_BASIC_INFO find_data; + struct cifs_fattr fattr; + struct cifs_tcon *tcon; + struct TCP_Server_Info *server; + struct tcon_link *tlink; + struct cifs_sb_info *cifs_sb = CIFS_SB(sb); + + cifs_dbg(FYI, "Getting info on %s\n", full_path); + + tlink = cifs_sb_tlink(cifs_sb); + if (IS_ERR(tlink)) + return PTR_ERR(tlink); + tcon = tlink_tcon(tlink); + server = tcon->ses->server; + + /* could have done a find first instead but this returns more info */ + rc = CIFSSMBUnixQPathInfo(xid, tcon, full_path, &find_data, + cifs_sb->local_nls, cifs_remap(cifs_sb)); + cifs_dbg(FYI, "%s: query path info: rc = %d\n", __func__, rc); + cifs_put_tlink(tlink); + + if (!rc) { + cifs_unix_basic_to_fattr(&fattr, &find_data, cifs_sb); + } else if (rc == -EREMOTE) { + cifs_create_dfs_fattr(&fattr, sb); + rc = 0; + } else { + return rc; + } + + /* check for Minshall+French symlinks */ + if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_MF_SYMLINKS) { + int tmprc = check_mf_symlink(xid, tcon, cifs_sb, &fattr, + full_path); + if (tmprc) + cifs_dbg(FYI, "check_mf_symlink: %d\n", tmprc); + } + + if (S_ISLNK(fattr.cf_mode) && !fattr.cf_symlink_target) { + if (!server->ops->query_symlink) + return -EOPNOTSUPP; + rc = server->ops->query_symlink(xid, tcon, cifs_sb, full_path, + &fattr.cf_symlink_target, false); + if (rc) { + cifs_dbg(FYI, "%s: query_symlink: %d\n", __func__, rc); + goto cgiiu_exit; + } + } + + if (*pinode == NULL) { + /* get new inode */ + cifs_fill_uniqueid(sb, &fattr); + *pinode = cifs_iget(sb, &fattr); + if (!*pinode) + rc = -ENOMEM; + } else { + /* we already have inode, update it */ + + /* if uniqueid is different, return error */ + if (unlikely(cifs_sb->mnt_cifs_flags & CIFS_MOUNT_SERVER_INUM && + CIFS_I(*pinode)->uniqueid != fattr.cf_uniqueid)) { + CIFS_I(*pinode)->time = 0; /* force reval */ + rc = -ESTALE; + goto cgiiu_exit; + } + + /* if filetype is different, return error */ + rc = cifs_fattr_to_inode(*pinode, &fattr); + } + +cgiiu_exit: + kfree(fattr.cf_symlink_target); + return rc; +} +#else +int cifs_get_inode_info_unix(struct inode **pinode, + const unsigned char *full_path, + struct super_block *sb, unsigned int xid) +{ + return -EOPNOTSUPP; +} +#endif /* CONFIG_CIFS_ALLOW_INSECURE_LEGACY */ + +static int +cifs_sfu_type(struct cifs_fattr *fattr, const char *path, + struct cifs_sb_info *cifs_sb, unsigned int xid) +{ + int rc; + __u32 oplock; + struct tcon_link *tlink; + struct cifs_tcon *tcon; + struct cifs_fid fid; + struct cifs_open_parms oparms; + struct cifs_io_parms io_parms = {0}; + char buf[24]; + unsigned int bytes_read; + char *pbuf; + int buf_type = CIFS_NO_BUFFER; + + pbuf = buf; + + fattr->cf_mode &= ~S_IFMT; + + if (fattr->cf_eof == 0) { + fattr->cf_mode |= S_IFIFO; + fattr->cf_dtype = DT_FIFO; + return 0; + } else if (fattr->cf_eof < 8) { + fattr->cf_mode |= S_IFREG; + fattr->cf_dtype = DT_REG; + return -EINVAL; /* EOPNOTSUPP? */ + } + + tlink = cifs_sb_tlink(cifs_sb); + if (IS_ERR(tlink)) + return PTR_ERR(tlink); + tcon = tlink_tcon(tlink); + + oparms = (struct cifs_open_parms) { + .tcon = tcon, + .cifs_sb = cifs_sb, + .desired_access = GENERIC_READ, + .create_options = cifs_create_options(cifs_sb, CREATE_NOT_DIR), + .disposition = FILE_OPEN, + .path = path, + .fid = &fid, + }; + + if (tcon->ses->server->oplocks) + oplock = REQ_OPLOCK; + else + oplock = 0; + rc = tcon->ses->server->ops->open(xid, &oparms, &oplock, NULL); + if (rc) { + cifs_dbg(FYI, "check sfu type of %s, open rc = %d\n", path, rc); + cifs_put_tlink(tlink); + return rc; + } + + /* Read header */ + io_parms.netfid = fid.netfid; + io_parms.pid = current->tgid; + io_parms.tcon = tcon; + io_parms.offset = 0; + io_parms.length = 24; + + rc = tcon->ses->server->ops->sync_read(xid, &fid, &io_parms, + &bytes_read, &pbuf, &buf_type); + if ((rc == 0) && (bytes_read >= 8)) { + if (memcmp("IntxBLK", pbuf, 8) == 0) { + cifs_dbg(FYI, "Block device\n"); + fattr->cf_mode |= S_IFBLK; + fattr->cf_dtype = DT_BLK; + if (bytes_read == 24) { + /* we have enough to decode dev num */ + __u64 mjr; /* major */ + __u64 mnr; /* minor */ + mjr = le64_to_cpu(*(__le64 *)(pbuf+8)); + mnr = le64_to_cpu(*(__le64 *)(pbuf+16)); + fattr->cf_rdev = MKDEV(mjr, mnr); + } + } else if (memcmp("IntxCHR", pbuf, 8) == 0) { + cifs_dbg(FYI, "Char device\n"); + fattr->cf_mode |= S_IFCHR; + fattr->cf_dtype = DT_CHR; + if (bytes_read == 24) { + /* we have enough to decode dev num */ + __u64 mjr; /* major */ + __u64 mnr; /* minor */ + mjr = le64_to_cpu(*(__le64 *)(pbuf+8)); + mnr = le64_to_cpu(*(__le64 *)(pbuf+16)); + fattr->cf_rdev = MKDEV(mjr, mnr); + } + } else if (memcmp("IntxLNK", pbuf, 7) == 0) { + cifs_dbg(FYI, "Symlink\n"); + fattr->cf_mode |= S_IFLNK; + fattr->cf_dtype = DT_LNK; + } else { + fattr->cf_mode |= S_IFREG; /* file? */ + fattr->cf_dtype = DT_REG; + rc = -EOPNOTSUPP; + } + } else { + fattr->cf_mode |= S_IFREG; /* then it is a file */ + fattr->cf_dtype = DT_REG; + rc = -EOPNOTSUPP; /* or some unknown SFU type */ + } + + tcon->ses->server->ops->close(xid, tcon, &fid); + cifs_put_tlink(tlink); + return rc; +} + +#define SFBITS_MASK (S_ISVTX | S_ISGID | S_ISUID) /* SETFILEBITS valid bits */ + +/* + * Fetch mode bits as provided by SFU. + * + * FIXME: Doesn't this clobber the type bit we got from cifs_sfu_type ? + */ +static int cifs_sfu_mode(struct cifs_fattr *fattr, const unsigned char *path, + struct cifs_sb_info *cifs_sb, unsigned int xid) +{ +#ifdef CONFIG_CIFS_XATTR + ssize_t rc; + char ea_value[4]; + __u32 mode; + struct tcon_link *tlink; + struct cifs_tcon *tcon; + + tlink = cifs_sb_tlink(cifs_sb); + if (IS_ERR(tlink)) + return PTR_ERR(tlink); + tcon = tlink_tcon(tlink); + + if (tcon->ses->server->ops->query_all_EAs == NULL) { + cifs_put_tlink(tlink); + return -EOPNOTSUPP; + } + + rc = tcon->ses->server->ops->query_all_EAs(xid, tcon, path, + "SETFILEBITS", ea_value, 4 /* size of buf */, + cifs_sb); + cifs_put_tlink(tlink); + if (rc < 0) + return (int)rc; + else if (rc > 3) { + mode = le32_to_cpu(*((__le32 *)ea_value)); + fattr->cf_mode &= ~SFBITS_MASK; + cifs_dbg(FYI, "special bits 0%o org mode 0%o\n", + mode, fattr->cf_mode); + fattr->cf_mode = (mode & SFBITS_MASK) | fattr->cf_mode; + cifs_dbg(FYI, "special mode bits 0%o\n", mode); + } + + return 0; +#else + return -EOPNOTSUPP; +#endif +} + +/* Fill a cifs_fattr struct with info from POSIX info struct */ +static void smb311_posix_info_to_fattr(struct cifs_fattr *fattr, struct cifs_open_info_data *data, + struct super_block *sb, bool adjust_tz, bool symlink) +{ + struct smb311_posix_qinfo *info = &data->posix_fi; + struct cifs_sb_info *cifs_sb = CIFS_SB(sb); + struct cifs_tcon *tcon = cifs_sb_master_tcon(cifs_sb); + + memset(fattr, 0, sizeof(*fattr)); + + /* no fattr->flags to set */ + fattr->cf_cifsattrs = le32_to_cpu(info->DosAttributes); + fattr->cf_uniqueid = le64_to_cpu(info->Inode); + + if (info->LastAccessTime) + fattr->cf_atime = cifs_NTtimeToUnix(info->LastAccessTime); + else + ktime_get_coarse_real_ts64(&fattr->cf_atime); + + fattr->cf_ctime = cifs_NTtimeToUnix(info->ChangeTime); + fattr->cf_mtime = cifs_NTtimeToUnix(info->LastWriteTime); + + if (adjust_tz) { + fattr->cf_ctime.tv_sec += tcon->ses->server->timeAdj; + fattr->cf_mtime.tv_sec += tcon->ses->server->timeAdj; + } + + fattr->cf_eof = le64_to_cpu(info->EndOfFile); + fattr->cf_bytes = le64_to_cpu(info->AllocationSize); + fattr->cf_createtime = le64_to_cpu(info->CreationTime); + + fattr->cf_nlink = le32_to_cpu(info->HardLinks); + fattr->cf_mode = (umode_t) le32_to_cpu(info->Mode); + /* The srv fs device id is overridden on network mount so setting rdev isn't needed here */ + /* fattr->cf_rdev = le32_to_cpu(info->DeviceId); */ + + if (symlink) { + fattr->cf_mode |= S_IFLNK; + fattr->cf_dtype = DT_LNK; + fattr->cf_symlink_target = data->symlink_target; + data->symlink_target = NULL; + } else if (fattr->cf_cifsattrs & ATTR_DIRECTORY) { + fattr->cf_mode |= S_IFDIR; + fattr->cf_dtype = DT_DIR; + } else { /* file */ + fattr->cf_mode |= S_IFREG; + fattr->cf_dtype = DT_REG; + } + /* else if reparse point ... TODO: add support for FIFO and blk dev; special file types */ + + fattr->cf_uid = cifs_sb->ctx->linux_uid; /* TODO: map uid and gid from SID */ + fattr->cf_gid = cifs_sb->ctx->linux_gid; + + cifs_dbg(FYI, "POSIX query info: mode 0x%x uniqueid 0x%llx nlink %d\n", + fattr->cf_mode, fattr->cf_uniqueid, fattr->cf_nlink); +} + +static void cifs_open_info_to_fattr(struct cifs_fattr *fattr, struct cifs_open_info_data *data, + struct super_block *sb, bool adjust_tz, bool symlink, + u32 reparse_tag) +{ + struct smb2_file_all_info *info = &data->fi; + struct cifs_sb_info *cifs_sb = CIFS_SB(sb); + struct cifs_tcon *tcon = cifs_sb_master_tcon(cifs_sb); + + memset(fattr, 0, sizeof(*fattr)); + fattr->cf_cifsattrs = le32_to_cpu(info->Attributes); + if (info->DeletePending) + fattr->cf_flags |= CIFS_FATTR_DELETE_PENDING; + + if (info->LastAccessTime) + fattr->cf_atime = cifs_NTtimeToUnix(info->LastAccessTime); + else + ktime_get_coarse_real_ts64(&fattr->cf_atime); + + fattr->cf_ctime = cifs_NTtimeToUnix(info->ChangeTime); + fattr->cf_mtime = cifs_NTtimeToUnix(info->LastWriteTime); + + if (adjust_tz) { + fattr->cf_ctime.tv_sec += tcon->ses->server->timeAdj; + fattr->cf_mtime.tv_sec += tcon->ses->server->timeAdj; + } + + fattr->cf_eof = le64_to_cpu(info->EndOfFile); + fattr->cf_bytes = le64_to_cpu(info->AllocationSize); + fattr->cf_createtime = le64_to_cpu(info->CreationTime); + + fattr->cf_nlink = le32_to_cpu(info->NumberOfLinks); + if (reparse_tag == IO_REPARSE_TAG_LX_SYMLINK) { + fattr->cf_mode |= S_IFLNK | cifs_sb->ctx->file_mode; + fattr->cf_dtype = DT_LNK; + } else if (reparse_tag == IO_REPARSE_TAG_LX_FIFO) { + fattr->cf_mode |= S_IFIFO | cifs_sb->ctx->file_mode; + fattr->cf_dtype = DT_FIFO; + } else if (reparse_tag == IO_REPARSE_TAG_AF_UNIX) { + fattr->cf_mode |= S_IFSOCK | cifs_sb->ctx->file_mode; + fattr->cf_dtype = DT_SOCK; + } else if (reparse_tag == IO_REPARSE_TAG_LX_CHR) { + fattr->cf_mode |= S_IFCHR | cifs_sb->ctx->file_mode; + fattr->cf_dtype = DT_CHR; + } else if (reparse_tag == IO_REPARSE_TAG_LX_BLK) { + fattr->cf_mode |= S_IFBLK | cifs_sb->ctx->file_mode; + fattr->cf_dtype = DT_BLK; + } else if (symlink || reparse_tag == IO_REPARSE_TAG_SYMLINK || + reparse_tag == IO_REPARSE_TAG_NFS) { + fattr->cf_mode = S_IFLNK; + fattr->cf_dtype = DT_LNK; + } else if (fattr->cf_cifsattrs & ATTR_DIRECTORY) { + fattr->cf_mode = S_IFDIR | cifs_sb->ctx->dir_mode; + fattr->cf_dtype = DT_DIR; + /* + * Server can return wrong NumberOfLinks value for directories + * when Unix extensions are disabled - fake it. + */ + if (!tcon->unix_ext) + fattr->cf_flags |= CIFS_FATTR_UNKNOWN_NLINK; + } else { + fattr->cf_mode = S_IFREG | cifs_sb->ctx->file_mode; + fattr->cf_dtype = DT_REG; + + /* clear write bits if ATTR_READONLY is set */ + if (fattr->cf_cifsattrs & ATTR_READONLY) + fattr->cf_mode &= ~(S_IWUGO); + + /* + * Don't accept zero nlink from non-unix servers unless + * delete is pending. Instead mark it as unknown. + */ + if ((fattr->cf_nlink < 1) && !tcon->unix_ext && + !info->DeletePending) { + cifs_dbg(VFS, "bogus file nlink value %u\n", + fattr->cf_nlink); + fattr->cf_flags |= CIFS_FATTR_UNKNOWN_NLINK; + } + } + + if (S_ISLNK(fattr->cf_mode)) { + fattr->cf_symlink_target = data->symlink_target; + data->symlink_target = NULL; + } + + fattr->cf_uid = cifs_sb->ctx->linux_uid; + fattr->cf_gid = cifs_sb->ctx->linux_gid; +} + +static int +cifs_get_file_info(struct file *filp) +{ + int rc; + unsigned int xid; + struct cifs_open_info_data data = {}; + struct cifs_fattr fattr; + struct inode *inode = file_inode(filp); + struct cifsFileInfo *cfile = filp->private_data; + struct cifs_tcon *tcon = tlink_tcon(cfile->tlink); + struct TCP_Server_Info *server = tcon->ses->server; + bool symlink = false; + u32 tag = 0; + + if (!server->ops->query_file_info) + return -ENOSYS; + + xid = get_xid(); + rc = server->ops->query_file_info(xid, tcon, cfile, &data); + switch (rc) { + case 0: + /* TODO: add support to query reparse tag */ + if (data.symlink_target) { + symlink = true; + tag = IO_REPARSE_TAG_SYMLINK; + } + cifs_open_info_to_fattr(&fattr, &data, inode->i_sb, false, symlink, tag); + break; + case -EREMOTE: + cifs_create_dfs_fattr(&fattr, inode->i_sb); + rc = 0; + break; + case -EOPNOTSUPP: + case -EINVAL: + /* + * FIXME: legacy server -- fall back to path-based call? + * for now, just skip revalidating and mark inode for + * immediate reval. + */ + rc = 0; + CIFS_I(inode)->time = 0; + goto cgfi_exit; + default: + goto cgfi_exit; + } + + /* + * don't bother with SFU junk here -- just mark inode as needing + * revalidation. + */ + fattr.cf_uniqueid = CIFS_I(inode)->uniqueid; + fattr.cf_flags |= CIFS_FATTR_NEED_REVAL; + /* if filetype is different, return error */ + rc = cifs_fattr_to_inode(inode, &fattr); +cgfi_exit: + cifs_free_open_info(&data); + free_xid(xid); + return rc; +} + +/* Simple function to return a 64 bit hash of string. Rarely called */ +static __u64 simple_hashstr(const char *str) +{ + const __u64 hash_mult = 1125899906842597ULL; /* a big enough prime */ + __u64 hash = 0; + + while (*str) + hash = (hash + (__u64) *str++) * hash_mult; + + return hash; +} + +#ifdef CONFIG_CIFS_ALLOW_INSECURE_LEGACY +/** + * cifs_backup_query_path_info - SMB1 fallback code to get ino + * + * Fallback code to get file metadata when we don't have access to + * full_path (EACCES) and have backup creds. + * + * @xid: transaction id used to identify original request in logs + * @tcon: information about the server share we have mounted + * @sb: the superblock stores info such as disk space available + * @full_path: name of the file we are getting the metadata for + * @resp_buf: will be set to cifs resp buf and needs to be freed with + * cifs_buf_release() when done with @data + * @data: will be set to search info result buffer + */ +static int +cifs_backup_query_path_info(int xid, + struct cifs_tcon *tcon, + struct super_block *sb, + const char *full_path, + void **resp_buf, + FILE_ALL_INFO **data) +{ + struct cifs_sb_info *cifs_sb = CIFS_SB(sb); + struct cifs_search_info info = {0}; + u16 flags; + int rc; + + *resp_buf = NULL; + info.endOfSearch = false; + if (tcon->unix_ext) + info.info_level = SMB_FIND_FILE_UNIX; + else if ((tcon->ses->capabilities & + tcon->ses->server->vals->cap_nt_find) == 0) + info.info_level = SMB_FIND_FILE_INFO_STANDARD; + else if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_SERVER_INUM) + info.info_level = SMB_FIND_FILE_ID_FULL_DIR_INFO; + else /* no srvino useful for fallback to some netapp */ + info.info_level = SMB_FIND_FILE_DIRECTORY_INFO; + + flags = CIFS_SEARCH_CLOSE_ALWAYS | + CIFS_SEARCH_CLOSE_AT_END | + CIFS_SEARCH_BACKUP_SEARCH; + + rc = CIFSFindFirst(xid, tcon, full_path, + cifs_sb, NULL, flags, &info, false); + if (rc) + return rc; + + *resp_buf = (void *)info.ntwrk_buf_start; + *data = (FILE_ALL_INFO *)info.srch_entries_start; + return 0; +} +#endif /* CONFIG_CIFS_ALLOW_INSECURE_LEGACY */ + +static void cifs_set_fattr_ino(int xid, struct cifs_tcon *tcon, struct super_block *sb, + struct inode **inode, const char *full_path, + struct cifs_open_info_data *data, struct cifs_fattr *fattr) +{ + struct cifs_sb_info *cifs_sb = CIFS_SB(sb); + struct TCP_Server_Info *server = tcon->ses->server; + int rc; + + if (!(cifs_sb->mnt_cifs_flags & CIFS_MOUNT_SERVER_INUM)) { + if (*inode) + fattr->cf_uniqueid = CIFS_I(*inode)->uniqueid; + else + fattr->cf_uniqueid = iunique(sb, ROOT_I); + return; + } + + /* + * If we have an inode pass a NULL tcon to ensure we don't + * make a round trip to the server. This only works for SMB2+. + */ + rc = server->ops->get_srv_inum(xid, *inode ? NULL : tcon, cifs_sb, full_path, + &fattr->cf_uniqueid, data); + if (rc) { + /* + * If that fails reuse existing ino or generate one + * and disable server ones + */ + if (*inode) + fattr->cf_uniqueid = CIFS_I(*inode)->uniqueid; + else { + fattr->cf_uniqueid = iunique(sb, ROOT_I); + cifs_autodisable_serverino(cifs_sb); + } + return; + } + + /* If no errors, check for zero root inode (invalid) */ + if (fattr->cf_uniqueid == 0 && strlen(full_path) == 0) { + cifs_dbg(FYI, "Invalid (0) inodenum\n"); + if (*inode) { + /* reuse */ + fattr->cf_uniqueid = CIFS_I(*inode)->uniqueid; + } else { + /* make an ino by hashing the UNC */ + fattr->cf_flags |= CIFS_FATTR_FAKE_ROOT_INO; + fattr->cf_uniqueid = simple_hashstr(tcon->tree_name); + } + } +} + +static inline bool is_inode_cache_good(struct inode *ino) +{ + return ino && CIFS_CACHE_READ(CIFS_I(ino)) && CIFS_I(ino)->time != 0; +} + +int cifs_get_inode_info(struct inode **inode, const char *full_path, + struct cifs_open_info_data *data, struct super_block *sb, int xid, + const struct cifs_fid *fid) +{ + struct cifs_tcon *tcon; + struct TCP_Server_Info *server; + struct tcon_link *tlink; + struct cifs_sb_info *cifs_sb = CIFS_SB(sb); + bool adjust_tz = false; + struct cifs_fattr fattr = {0}; + bool is_reparse_point = false; + struct cifs_open_info_data tmp_data = {}; + void *smb1_backup_rsp_buf = NULL; + int rc = 0; + int tmprc = 0; + __u32 reparse_tag = 0; + + tlink = cifs_sb_tlink(cifs_sb); + if (IS_ERR(tlink)) + return PTR_ERR(tlink); + tcon = tlink_tcon(tlink); + server = tcon->ses->server; + + /* + * 1. Fetch file metadata if not provided (data) + */ + + if (!data) { + if (is_inode_cache_good(*inode)) { + cifs_dbg(FYI, "No need to revalidate cached inode sizes\n"); + goto out; + } + rc = server->ops->query_path_info(xid, tcon, cifs_sb, full_path, &tmp_data, + &adjust_tz, &is_reparse_point); + data = &tmp_data; + } + + /* + * 2. Convert it to internal cifs metadata (fattr) + */ + + switch (rc) { + case 0: + /* + * If the file is a reparse point, it is more complicated + * since we have to check if its reparse tag matches a known + * special file type e.g. symlink or fifo or char etc. + */ + if (is_reparse_point && data->symlink_target) { + reparse_tag = IO_REPARSE_TAG_SYMLINK; + } else if ((le32_to_cpu(data->fi.Attributes) & ATTR_REPARSE) && + server->ops->query_reparse_tag) { + tmprc = server->ops->query_reparse_tag(xid, tcon, cifs_sb, full_path, + &reparse_tag); + if (tmprc) + cifs_dbg(FYI, "%s: query_reparse_tag: rc = %d\n", __func__, tmprc); + if (server->ops->query_symlink) { + tmprc = server->ops->query_symlink(xid, tcon, cifs_sb, full_path, + &data->symlink_target, + is_reparse_point); + if (tmprc) + cifs_dbg(FYI, "%s: query_symlink: rc = %d\n", __func__, + tmprc); + } + } + cifs_open_info_to_fattr(&fattr, data, sb, adjust_tz, is_reparse_point, reparse_tag); + break; + case -EREMOTE: + /* DFS link, no metadata available on this server */ + cifs_create_dfs_fattr(&fattr, sb); + rc = 0; + break; + case -EACCES: +#ifdef CONFIG_CIFS_ALLOW_INSECURE_LEGACY + /* + * perm errors, try again with backup flags if possible + * + * For SMB2 and later the backup intent flag + * is already sent if needed on open and there + * is no path based FindFirst operation to use + * to retry with + */ + if (backup_cred(cifs_sb) && is_smb1_server(server)) { + /* for easier reading */ + FILE_ALL_INFO *fi; + FILE_DIRECTORY_INFO *fdi; + SEARCH_ID_FULL_DIR_INFO *si; + + rc = cifs_backup_query_path_info(xid, tcon, sb, + full_path, + &smb1_backup_rsp_buf, + &fi); + if (rc) + goto out; + + move_cifs_info_to_smb2(&data->fi, fi); + fdi = (FILE_DIRECTORY_INFO *)fi; + si = (SEARCH_ID_FULL_DIR_INFO *)fi; + + cifs_dir_info_to_fattr(&fattr, fdi, cifs_sb); + fattr.cf_uniqueid = le64_to_cpu(si->UniqueId); + /* uniqueid set, skip get inum step */ + goto handle_mnt_opt; + } else { + /* nothing we can do, bail out */ + goto out; + } +#else + goto out; +#endif /* CONFIG_CIFS_ALLOW_INSECURE_LEGACY */ + break; + default: + cifs_dbg(FYI, "%s: unhandled err rc %d\n", __func__, rc); + goto out; + } + + /* + * 3. Get or update inode number (fattr.cf_uniqueid) + */ + + cifs_set_fattr_ino(xid, tcon, sb, inode, full_path, data, &fattr); + + /* + * 4. Tweak fattr based on mount options + */ +#ifdef CONFIG_CIFS_ALLOW_INSECURE_LEGACY +handle_mnt_opt: +#endif /* CONFIG_CIFS_ALLOW_INSECURE_LEGACY */ + /* query for SFU type info if supported and needed */ + if (fattr.cf_cifsattrs & ATTR_SYSTEM && + cifs_sb->mnt_cifs_flags & CIFS_MOUNT_UNX_EMUL) { + tmprc = cifs_sfu_type(&fattr, full_path, cifs_sb, xid); + if (tmprc) + cifs_dbg(FYI, "cifs_sfu_type failed: %d\n", tmprc); + } + + /* fill in 0777 bits from ACL */ + if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_MODE_FROM_SID) { + rc = cifs_acl_to_fattr(cifs_sb, &fattr, *inode, true, + full_path, fid); + if (rc == -EREMOTE) + rc = 0; + if (rc) { + cifs_dbg(FYI, "%s: Get mode from SID failed. rc=%d\n", + __func__, rc); + goto out; + } + } else if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_CIFS_ACL) { + rc = cifs_acl_to_fattr(cifs_sb, &fattr, *inode, false, + full_path, fid); + if (rc == -EREMOTE) + rc = 0; + if (rc) { + cifs_dbg(FYI, "%s: Getting ACL failed with error: %d\n", + __func__, rc); + goto out; + } + } + + /* fill in remaining high mode bits e.g. SUID, VTX */ + if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_UNX_EMUL) + cifs_sfu_mode(&fattr, full_path, cifs_sb, xid); + + /* check for Minshall+French symlinks */ + if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_MF_SYMLINKS) { + tmprc = check_mf_symlink(xid, tcon, cifs_sb, &fattr, + full_path); + if (tmprc) + cifs_dbg(FYI, "check_mf_symlink: %d\n", tmprc); + } + + /* + * 5. Update inode with final fattr data + */ + + if (!*inode) { + *inode = cifs_iget(sb, &fattr); + if (!*inode) + rc = -ENOMEM; + } else { + /* we already have inode, update it */ + + /* if uniqueid is different, return error */ + if (unlikely(cifs_sb->mnt_cifs_flags & CIFS_MOUNT_SERVER_INUM && + CIFS_I(*inode)->uniqueid != fattr.cf_uniqueid)) { + CIFS_I(*inode)->time = 0; /* force reval */ + rc = -ESTALE; + goto out; + } + /* if filetype is different, return error */ + rc = cifs_fattr_to_inode(*inode, &fattr); + } +out: + cifs_buf_release(smb1_backup_rsp_buf); + cifs_put_tlink(tlink); + cifs_free_open_info(&tmp_data); + kfree(fattr.cf_symlink_target); + return rc; +} + +int +smb311_posix_get_inode_info(struct inode **inode, + const char *full_path, + struct super_block *sb, unsigned int xid) +{ + struct cifs_tcon *tcon; + struct tcon_link *tlink; + struct cifs_sb_info *cifs_sb = CIFS_SB(sb); + bool adjust_tz = false; + struct cifs_fattr fattr = {0}; + bool symlink = false; + struct cifs_open_info_data data = {}; + int rc = 0; + int tmprc = 0; + + tlink = cifs_sb_tlink(cifs_sb); + if (IS_ERR(tlink)) + return PTR_ERR(tlink); + tcon = tlink_tcon(tlink); + + /* + * 1. Fetch file metadata + */ + + if (is_inode_cache_good(*inode)) { + cifs_dbg(FYI, "No need to revalidate cached inode sizes\n"); + goto out; + } + + rc = smb311_posix_query_path_info(xid, tcon, cifs_sb, full_path, &data, &adjust_tz, + &symlink); + + /* + * 2. Convert it to internal cifs metadata (fattr) + */ + + switch (rc) { + case 0: + smb311_posix_info_to_fattr(&fattr, &data, sb, adjust_tz, symlink); + break; + case -EREMOTE: + /* DFS link, no metadata available on this server */ + cifs_create_dfs_fattr(&fattr, sb); + rc = 0; + break; + case -EACCES: + /* + * For SMB2 and later the backup intent flag + * is already sent if needed on open and there + * is no path based FindFirst operation to use + * to retry with so nothing we can do, bail out + */ + goto out; + default: + cifs_dbg(FYI, "%s: unhandled err rc %d\n", __func__, rc); + goto out; + } + + + /* + * 3. Tweak fattr based on mount options + */ + + /* check for Minshall+French symlinks */ + if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_MF_SYMLINKS) { + tmprc = check_mf_symlink(xid, tcon, cifs_sb, &fattr, + full_path); + if (tmprc) + cifs_dbg(FYI, "check_mf_symlink: %d\n", tmprc); + } + + /* + * 4. Update inode with final fattr data + */ + + if (!*inode) { + *inode = cifs_iget(sb, &fattr); + if (!*inode) + rc = -ENOMEM; + } else { + /* we already have inode, update it */ + + /* if uniqueid is different, return error */ + if (unlikely(cifs_sb->mnt_cifs_flags & CIFS_MOUNT_SERVER_INUM && + CIFS_I(*inode)->uniqueid != fattr.cf_uniqueid)) { + CIFS_I(*inode)->time = 0; /* force reval */ + rc = -ESTALE; + goto out; + } + + /* if filetype is different, return error */ + rc = cifs_fattr_to_inode(*inode, &fattr); + } +out: + cifs_put_tlink(tlink); + cifs_free_open_info(&data); + kfree(fattr.cf_symlink_target); + return rc; +} + + +static const struct inode_operations cifs_ipc_inode_ops = { + .lookup = cifs_lookup, +}; + +static int +cifs_find_inode(struct inode *inode, void *opaque) +{ + struct cifs_fattr *fattr = opaque; + + /* don't match inode with different uniqueid */ + if (CIFS_I(inode)->uniqueid != fattr->cf_uniqueid) + return 0; + + /* use createtime like an i_generation field */ + if (CIFS_I(inode)->createtime != fattr->cf_createtime) + return 0; + + /* don't match inode of different type */ + if (inode_wrong_type(inode, fattr->cf_mode)) + return 0; + + /* if it's not a directory or has no dentries, then flag it */ + if (S_ISDIR(inode->i_mode) && !hlist_empty(&inode->i_dentry)) + fattr->cf_flags |= CIFS_FATTR_INO_COLLISION; + + return 1; +} + +static int +cifs_init_inode(struct inode *inode, void *opaque) +{ + struct cifs_fattr *fattr = opaque; + + CIFS_I(inode)->uniqueid = fattr->cf_uniqueid; + CIFS_I(inode)->createtime = fattr->cf_createtime; + return 0; +} + +/* + * walk dentry list for an inode and report whether it has aliases that + * are hashed. We use this to determine if a directory inode can actually + * be used. + */ +static bool +inode_has_hashed_dentries(struct inode *inode) +{ + struct dentry *dentry; + + spin_lock(&inode->i_lock); + hlist_for_each_entry(dentry, &inode->i_dentry, d_u.d_alias) { + if (!d_unhashed(dentry) || IS_ROOT(dentry)) { + spin_unlock(&inode->i_lock); + return true; + } + } + spin_unlock(&inode->i_lock); + return false; +} + +/* Given fattrs, get a corresponding inode */ +struct inode * +cifs_iget(struct super_block *sb, struct cifs_fattr *fattr) +{ + unsigned long hash; + struct inode *inode; + +retry_iget5_locked: + cifs_dbg(FYI, "looking for uniqueid=%llu\n", fattr->cf_uniqueid); + + /* hash down to 32-bits on 32-bit arch */ + hash = cifs_uniqueid_to_ino_t(fattr->cf_uniqueid); + + inode = iget5_locked(sb, hash, cifs_find_inode, cifs_init_inode, fattr); + if (inode) { + /* was there a potentially problematic inode collision? */ + if (fattr->cf_flags & CIFS_FATTR_INO_COLLISION) { + fattr->cf_flags &= ~CIFS_FATTR_INO_COLLISION; + + if (inode_has_hashed_dentries(inode)) { + cifs_autodisable_serverino(CIFS_SB(sb)); + iput(inode); + fattr->cf_uniqueid = iunique(sb, ROOT_I); + goto retry_iget5_locked; + } + } + + /* can't fail - see cifs_find_inode() */ + cifs_fattr_to_inode(inode, fattr); + if (sb->s_flags & SB_NOATIME) + inode->i_flags |= S_NOATIME | S_NOCMTIME; + if (inode->i_state & I_NEW) { + inode->i_ino = hash; + cifs_fscache_get_inode_cookie(inode); + unlock_new_inode(inode); + } + } + + return inode; +} + +/* gets root inode */ +struct inode *cifs_root_iget(struct super_block *sb) +{ + unsigned int xid; + struct cifs_sb_info *cifs_sb = CIFS_SB(sb); + struct inode *inode = NULL; + long rc; + struct cifs_tcon *tcon = cifs_sb_master_tcon(cifs_sb); + char *path = NULL; + int len; + + if ((cifs_sb->mnt_cifs_flags & CIFS_MOUNT_USE_PREFIX_PATH) + && cifs_sb->prepath) { + len = strlen(cifs_sb->prepath); + path = kzalloc(len + 2 /* leading sep + null */, GFP_KERNEL); + if (path == NULL) + return ERR_PTR(-ENOMEM); + path[0] = '/'; + memcpy(path+1, cifs_sb->prepath, len); + } else { + path = kstrdup("", GFP_KERNEL); + if (path == NULL) + return ERR_PTR(-ENOMEM); + } + + xid = get_xid(); + if (tcon->unix_ext) { + rc = cifs_get_inode_info_unix(&inode, path, sb, xid); + /* some servers mistakenly claim POSIX support */ + if (rc != -EOPNOTSUPP) + goto iget_no_retry; + cifs_dbg(VFS, "server does not support POSIX extensions\n"); + tcon->unix_ext = false; + } + + convert_delimiter(path, CIFS_DIR_SEP(cifs_sb)); + if (tcon->posix_extensions) + rc = smb311_posix_get_inode_info(&inode, path, sb, xid); + else + rc = cifs_get_inode_info(&inode, path, NULL, sb, xid, NULL); + +iget_no_retry: + if (!inode) { + inode = ERR_PTR(rc); + goto out; + } + + if (rc && tcon->pipe) { + cifs_dbg(FYI, "ipc connection - fake read inode\n"); + spin_lock(&inode->i_lock); + inode->i_mode |= S_IFDIR; + set_nlink(inode, 2); + inode->i_op = &cifs_ipc_inode_ops; + inode->i_fop = &simple_dir_operations; + inode->i_uid = cifs_sb->ctx->linux_uid; + inode->i_gid = cifs_sb->ctx->linux_gid; + spin_unlock(&inode->i_lock); + } else if (rc) { + iget_failed(inode); + inode = ERR_PTR(rc); + } + +out: + kfree(path); + free_xid(xid); + return inode; +} + +int +cifs_set_file_info(struct inode *inode, struct iattr *attrs, unsigned int xid, + const char *full_path, __u32 dosattr) +{ + bool set_time = false; + struct cifs_sb_info *cifs_sb = CIFS_SB(inode->i_sb); + struct TCP_Server_Info *server; + FILE_BASIC_INFO info_buf; + + if (attrs == NULL) + return -EINVAL; + + server = cifs_sb_master_tcon(cifs_sb)->ses->server; + if (!server->ops->set_file_info) + return -ENOSYS; + + info_buf.Pad = 0; + + if (attrs->ia_valid & ATTR_ATIME) { + set_time = true; + info_buf.LastAccessTime = + cpu_to_le64(cifs_UnixTimeToNT(attrs->ia_atime)); + } else + info_buf.LastAccessTime = 0; + + if (attrs->ia_valid & ATTR_MTIME) { + set_time = true; + info_buf.LastWriteTime = + cpu_to_le64(cifs_UnixTimeToNT(attrs->ia_mtime)); + } else + info_buf.LastWriteTime = 0; + + /* + * Samba throws this field away, but windows may actually use it. + * Do not set ctime unless other time stamps are changed explicitly + * (i.e. by utimes()) since we would then have a mix of client and + * server times. + */ + if (set_time && (attrs->ia_valid & ATTR_CTIME)) { + cifs_dbg(FYI, "CIFS - CTIME changed\n"); + info_buf.ChangeTime = + cpu_to_le64(cifs_UnixTimeToNT(attrs->ia_ctime)); + } else + info_buf.ChangeTime = 0; + + info_buf.CreationTime = 0; /* don't change */ + info_buf.Attributes = cpu_to_le32(dosattr); + + return server->ops->set_file_info(inode, full_path, &info_buf, xid); +} + +#ifdef CONFIG_CIFS_ALLOW_INSECURE_LEGACY +/* + * Open the given file (if it isn't already), set the DELETE_ON_CLOSE bit + * and rename it to a random name that hopefully won't conflict with + * anything else. + */ +int +cifs_rename_pending_delete(const char *full_path, struct dentry *dentry, + const unsigned int xid) +{ + int oplock = 0; + int rc; + struct cifs_fid fid; + struct cifs_open_parms oparms; + struct inode *inode = d_inode(dentry); + struct cifsInodeInfo *cifsInode = CIFS_I(inode); + struct cifs_sb_info *cifs_sb = CIFS_SB(inode->i_sb); + struct tcon_link *tlink; + struct cifs_tcon *tcon; + __u32 dosattr, origattr; + FILE_BASIC_INFO *info_buf = NULL; + + tlink = cifs_sb_tlink(cifs_sb); + if (IS_ERR(tlink)) + return PTR_ERR(tlink); + tcon = tlink_tcon(tlink); + + /* + * We cannot rename the file if the server doesn't support + * CAP_INFOLEVEL_PASSTHRU + */ + if (!(tcon->ses->capabilities & CAP_INFOLEVEL_PASSTHRU)) { + rc = -EBUSY; + goto out; + } + + oparms = (struct cifs_open_parms) { + .tcon = tcon, + .cifs_sb = cifs_sb, + .desired_access = DELETE | FILE_WRITE_ATTRIBUTES, + .create_options = cifs_create_options(cifs_sb, CREATE_NOT_DIR), + .disposition = FILE_OPEN, + .path = full_path, + .fid = &fid, + }; + + rc = CIFS_open(xid, &oparms, &oplock, NULL); + if (rc != 0) + goto out; + + origattr = cifsInode->cifsAttrs; + if (origattr == 0) + origattr |= ATTR_NORMAL; + + dosattr = origattr & ~ATTR_READONLY; + if (dosattr == 0) + dosattr |= ATTR_NORMAL; + dosattr |= ATTR_HIDDEN; + + /* set ATTR_HIDDEN and clear ATTR_READONLY, but only if needed */ + if (dosattr != origattr) { + info_buf = kzalloc(sizeof(*info_buf), GFP_KERNEL); + if (info_buf == NULL) { + rc = -ENOMEM; + goto out_close; + } + info_buf->Attributes = cpu_to_le32(dosattr); + rc = CIFSSMBSetFileInfo(xid, tcon, info_buf, fid.netfid, + current->tgid); + /* although we would like to mark the file hidden + if that fails we will still try to rename it */ + if (!rc) + cifsInode->cifsAttrs = dosattr; + else + dosattr = origattr; /* since not able to change them */ + } + + /* rename the file */ + rc = CIFSSMBRenameOpenFile(xid, tcon, fid.netfid, NULL, + cifs_sb->local_nls, + cifs_remap(cifs_sb)); + if (rc != 0) { + rc = -EBUSY; + goto undo_setattr; + } + + /* try to set DELETE_ON_CLOSE */ + if (!test_bit(CIFS_INO_DELETE_PENDING, &cifsInode->flags)) { + rc = CIFSSMBSetFileDisposition(xid, tcon, true, fid.netfid, + current->tgid); + /* + * some samba versions return -ENOENT when we try to set the + * file disposition here. Likely a samba bug, but work around + * it for now. This means that some cifsXXX files may hang + * around after they shouldn't. + * + * BB: remove this hack after more servers have the fix + */ + if (rc == -ENOENT) + rc = 0; + else if (rc != 0) { + rc = -EBUSY; + goto undo_rename; + } + set_bit(CIFS_INO_DELETE_PENDING, &cifsInode->flags); + } + +out_close: + CIFSSMBClose(xid, tcon, fid.netfid); +out: + kfree(info_buf); + cifs_put_tlink(tlink); + return rc; + + /* + * reset everything back to the original state. Don't bother + * dealing with errors here since we can't do anything about + * them anyway. + */ +undo_rename: + CIFSSMBRenameOpenFile(xid, tcon, fid.netfid, dentry->d_name.name, + cifs_sb->local_nls, cifs_remap(cifs_sb)); +undo_setattr: + if (dosattr != origattr) { + info_buf->Attributes = cpu_to_le32(origattr); + if (!CIFSSMBSetFileInfo(xid, tcon, info_buf, fid.netfid, + current->tgid)) + cifsInode->cifsAttrs = origattr; + } + + goto out_close; +} +#endif /* CONFIG_CIFS_ALLOW_INSECURE_LEGACY */ + +/* copied from fs/nfs/dir.c with small changes */ +static void +cifs_drop_nlink(struct inode *inode) +{ + spin_lock(&inode->i_lock); + if (inode->i_nlink > 0) + drop_nlink(inode); + spin_unlock(&inode->i_lock); +} + +/* + * If d_inode(dentry) is null (usually meaning the cached dentry + * is a negative dentry) then we would attempt a standard SMB delete, but + * if that fails we can not attempt the fall back mechanisms on EACCES + * but will return the EACCES to the caller. Note that the VFS does not call + * unlink on negative dentries currently. + */ +int cifs_unlink(struct inode *dir, struct dentry *dentry) +{ + int rc = 0; + unsigned int xid; + const char *full_path; + void *page; + struct inode *inode = d_inode(dentry); + struct cifsInodeInfo *cifs_inode; + struct super_block *sb = dir->i_sb; + struct cifs_sb_info *cifs_sb = CIFS_SB(sb); + struct tcon_link *tlink; + struct cifs_tcon *tcon; + struct TCP_Server_Info *server; + struct iattr *attrs = NULL; + __u32 dosattr = 0, origattr = 0; + + cifs_dbg(FYI, "cifs_unlink, dir=0x%p, dentry=0x%p\n", dir, dentry); + + if (unlikely(cifs_forced_shutdown(cifs_sb))) + return -EIO; + + tlink = cifs_sb_tlink(cifs_sb); + if (IS_ERR(tlink)) + return PTR_ERR(tlink); + tcon = tlink_tcon(tlink); + server = tcon->ses->server; + + xid = get_xid(); + page = alloc_dentry_path(); + + if (tcon->nodelete) { + rc = -EACCES; + goto unlink_out; + } + + /* Unlink can be called from rename so we can not take the + * sb->s_vfs_rename_mutex here */ + full_path = build_path_from_dentry(dentry, page); + if (IS_ERR(full_path)) { + rc = PTR_ERR(full_path); + goto unlink_out; + } + + cifs_close_deferred_file_under_dentry(tcon, full_path); +#ifdef CONFIG_CIFS_ALLOW_INSECURE_LEGACY + if (cap_unix(tcon->ses) && (CIFS_UNIX_POSIX_PATH_OPS_CAP & + le64_to_cpu(tcon->fsUnixInfo.Capability))) { + rc = CIFSPOSIXDelFile(xid, tcon, full_path, + SMB_POSIX_UNLINK_FILE_TARGET, cifs_sb->local_nls, + cifs_remap(cifs_sb)); + cifs_dbg(FYI, "posix del rc %d\n", rc); + if ((rc == 0) || (rc == -ENOENT)) + goto psx_del_no_retry; + } +#endif /* CONFIG_CIFS_ALLOW_INSECURE_LEGACY */ + +retry_std_delete: + if (!server->ops->unlink) { + rc = -ENOSYS; + goto psx_del_no_retry; + } + + rc = server->ops->unlink(xid, tcon, full_path, cifs_sb); + +psx_del_no_retry: + if (!rc) { + if (inode) + cifs_drop_nlink(inode); + } else if (rc == -ENOENT) { + d_drop(dentry); + } else if (rc == -EBUSY) { + if (server->ops->rename_pending_delete) { + rc = server->ops->rename_pending_delete(full_path, + dentry, xid); + if (rc == 0) + cifs_drop_nlink(inode); + } + } else if ((rc == -EACCES) && (dosattr == 0) && inode) { + attrs = kzalloc(sizeof(*attrs), GFP_KERNEL); + if (attrs == NULL) { + rc = -ENOMEM; + goto out_reval; + } + + /* try to reset dos attributes */ + cifs_inode = CIFS_I(inode); + origattr = cifs_inode->cifsAttrs; + if (origattr == 0) + origattr |= ATTR_NORMAL; + dosattr = origattr & ~ATTR_READONLY; + if (dosattr == 0) + dosattr |= ATTR_NORMAL; + dosattr |= ATTR_HIDDEN; + + rc = cifs_set_file_info(inode, attrs, xid, full_path, dosattr); + if (rc != 0) + goto out_reval; + + goto retry_std_delete; + } + + /* undo the setattr if we errored out and it's needed */ + if (rc != 0 && dosattr != 0) + cifs_set_file_info(inode, attrs, xid, full_path, origattr); + +out_reval: + if (inode) { + cifs_inode = CIFS_I(inode); + cifs_inode->time = 0; /* will force revalidate to get info + when needed */ + inode->i_ctime = current_time(inode); + } + dir->i_ctime = dir->i_mtime = current_time(dir); + cifs_inode = CIFS_I(dir); + CIFS_I(dir)->time = 0; /* force revalidate of dir as well */ +unlink_out: + free_dentry_path(page); + kfree(attrs); + free_xid(xid); + cifs_put_tlink(tlink); + return rc; +} + +static int +cifs_mkdir_qinfo(struct inode *parent, struct dentry *dentry, umode_t mode, + const char *full_path, struct cifs_sb_info *cifs_sb, + struct cifs_tcon *tcon, const unsigned int xid) +{ + int rc = 0; + struct inode *inode = NULL; + + if (tcon->posix_extensions) + rc = smb311_posix_get_inode_info(&inode, full_path, parent->i_sb, xid); +#ifdef CONFIG_CIFS_ALLOW_INSECURE_LEGACY + else if (tcon->unix_ext) + rc = cifs_get_inode_info_unix(&inode, full_path, parent->i_sb, + xid); +#endif /* CONFIG_CIFS_ALLOW_INSECURE_LEGACY */ + else + rc = cifs_get_inode_info(&inode, full_path, NULL, parent->i_sb, + xid, NULL); + + if (rc) + return rc; + + if (!S_ISDIR(inode->i_mode)) { + /* + * mkdir succeeded, but another client has managed to remove the + * sucker and replace it with non-directory. Return success, + * but don't leave the child in dcache. + */ + iput(inode); + d_drop(dentry); + return 0; + } + /* + * setting nlink not necessary except in cases where we failed to get it + * from the server or was set bogus. Also, since this is a brand new + * inode, no need to grab the i_lock before setting the i_nlink. + */ + if (inode->i_nlink < 2) + set_nlink(inode, 2); + mode &= ~current_umask(); + /* must turn on setgid bit if parent dir has it */ + if (parent->i_mode & S_ISGID) + mode |= S_ISGID; + +#ifdef CONFIG_CIFS_ALLOW_INSECURE_LEGACY + if (tcon->unix_ext) { + struct cifs_unix_set_info_args args = { + .mode = mode, + .ctime = NO_CHANGE_64, + .atime = NO_CHANGE_64, + .mtime = NO_CHANGE_64, + .device = 0, + }; + if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_SET_UID) { + args.uid = current_fsuid(); + if (parent->i_mode & S_ISGID) + args.gid = parent->i_gid; + else + args.gid = current_fsgid(); + } else { + args.uid = INVALID_UID; /* no change */ + args.gid = INVALID_GID; /* no change */ + } + CIFSSMBUnixSetPathInfo(xid, tcon, full_path, &args, + cifs_sb->local_nls, + cifs_remap(cifs_sb)); + } else { +#else + { +#endif /* CONFIG_CIFS_ALLOW_INSECURE_LEGACY */ + struct TCP_Server_Info *server = tcon->ses->server; + if (!(cifs_sb->mnt_cifs_flags & CIFS_MOUNT_CIFS_ACL) && + (mode & S_IWUGO) == 0 && server->ops->mkdir_setinfo) + server->ops->mkdir_setinfo(inode, full_path, cifs_sb, + tcon, xid); + if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_DYNPERM) + inode->i_mode = (mode | S_IFDIR); + + if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_SET_UID) { + inode->i_uid = current_fsuid(); + if (inode->i_mode & S_ISGID) + inode->i_gid = parent->i_gid; + else + inode->i_gid = current_fsgid(); + } + } + d_instantiate(dentry, inode); + return 0; +} + +#ifdef CONFIG_CIFS_ALLOW_INSECURE_LEGACY +static int +cifs_posix_mkdir(struct inode *inode, struct dentry *dentry, umode_t mode, + const char *full_path, struct cifs_sb_info *cifs_sb, + struct cifs_tcon *tcon, const unsigned int xid) +{ + int rc = 0; + u32 oplock = 0; + FILE_UNIX_BASIC_INFO *info = NULL; + struct inode *newinode = NULL; + struct cifs_fattr fattr; + + info = kzalloc(sizeof(FILE_UNIX_BASIC_INFO), GFP_KERNEL); + if (info == NULL) { + rc = -ENOMEM; + goto posix_mkdir_out; + } + + mode &= ~current_umask(); + rc = CIFSPOSIXCreate(xid, tcon, SMB_O_DIRECTORY | SMB_O_CREAT, mode, + NULL /* netfid */, info, &oplock, full_path, + cifs_sb->local_nls, cifs_remap(cifs_sb)); + if (rc == -EOPNOTSUPP) + goto posix_mkdir_out; + else if (rc) { + cifs_dbg(FYI, "posix mkdir returned 0x%x\n", rc); + d_drop(dentry); + goto posix_mkdir_out; + } + + if (info->Type == cpu_to_le32(-1)) + /* no return info, go query for it */ + goto posix_mkdir_get_info; + /* + * BB check (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_SET_UID ) to see if + * need to set uid/gid. + */ + + cifs_unix_basic_to_fattr(&fattr, info, cifs_sb); + cifs_fill_uniqueid(inode->i_sb, &fattr); + newinode = cifs_iget(inode->i_sb, &fattr); + if (!newinode) + goto posix_mkdir_get_info; + + d_instantiate(dentry, newinode); + +#ifdef CONFIG_CIFS_DEBUG2 + cifs_dbg(FYI, "instantiated dentry %p %pd to inode %p\n", + dentry, dentry, newinode); + + if (newinode->i_nlink != 2) + cifs_dbg(FYI, "unexpected number of links %d\n", + newinode->i_nlink); +#endif + +posix_mkdir_out: + kfree(info); + return rc; +posix_mkdir_get_info: + rc = cifs_mkdir_qinfo(inode, dentry, mode, full_path, cifs_sb, tcon, + xid); + goto posix_mkdir_out; +} +#endif /* CONFIG_CIFS_ALLOW_INSECURE_LEGACY */ + +int cifs_mkdir(struct user_namespace *mnt_userns, struct inode *inode, + struct dentry *direntry, umode_t mode) +{ + int rc = 0; + unsigned int xid; + struct cifs_sb_info *cifs_sb; + struct tcon_link *tlink; + struct cifs_tcon *tcon; + struct TCP_Server_Info *server; + const char *full_path; + void *page; + + cifs_dbg(FYI, "In cifs_mkdir, mode = %04ho inode = 0x%p\n", + mode, inode); + + cifs_sb = CIFS_SB(inode->i_sb); + if (unlikely(cifs_forced_shutdown(cifs_sb))) + return -EIO; + tlink = cifs_sb_tlink(cifs_sb); + if (IS_ERR(tlink)) + return PTR_ERR(tlink); + tcon = tlink_tcon(tlink); + + xid = get_xid(); + + page = alloc_dentry_path(); + full_path = build_path_from_dentry(direntry, page); + if (IS_ERR(full_path)) { + rc = PTR_ERR(full_path); + goto mkdir_out; + } + + server = tcon->ses->server; + + if ((server->ops->posix_mkdir) && (tcon->posix_extensions)) { + rc = server->ops->posix_mkdir(xid, inode, mode, tcon, full_path, + cifs_sb); + d_drop(direntry); /* for time being always refresh inode info */ + goto mkdir_out; + } + +#ifdef CONFIG_CIFS_ALLOW_INSECURE_LEGACY + if (cap_unix(tcon->ses) && (CIFS_UNIX_POSIX_PATH_OPS_CAP & + le64_to_cpu(tcon->fsUnixInfo.Capability))) { + rc = cifs_posix_mkdir(inode, direntry, mode, full_path, cifs_sb, + tcon, xid); + if (rc != -EOPNOTSUPP) + goto mkdir_out; + } +#endif /* CONFIG_CIFS_ALLOW_INSECURE_LEGACY */ + + if (!server->ops->mkdir) { + rc = -ENOSYS; + goto mkdir_out; + } + + /* BB add setting the equivalent of mode via CreateX w/ACLs */ + rc = server->ops->mkdir(xid, inode, mode, tcon, full_path, cifs_sb); + if (rc) { + cifs_dbg(FYI, "cifs_mkdir returned 0x%x\n", rc); + d_drop(direntry); + goto mkdir_out; + } + + /* TODO: skip this for smb2/smb3 */ + rc = cifs_mkdir_qinfo(inode, direntry, mode, full_path, cifs_sb, tcon, + xid); +mkdir_out: + /* + * Force revalidate to get parent dir info when needed since cached + * attributes are invalid now. + */ + CIFS_I(inode)->time = 0; + free_dentry_path(page); + free_xid(xid); + cifs_put_tlink(tlink); + return rc; +} + +int cifs_rmdir(struct inode *inode, struct dentry *direntry) +{ + int rc = 0; + unsigned int xid; + struct cifs_sb_info *cifs_sb; + struct tcon_link *tlink; + struct cifs_tcon *tcon; + struct TCP_Server_Info *server; + const char *full_path; + void *page = alloc_dentry_path(); + struct cifsInodeInfo *cifsInode; + + cifs_dbg(FYI, "cifs_rmdir, inode = 0x%p\n", inode); + + xid = get_xid(); + + full_path = build_path_from_dentry(direntry, page); + if (IS_ERR(full_path)) { + rc = PTR_ERR(full_path); + goto rmdir_exit; + } + + cifs_sb = CIFS_SB(inode->i_sb); + if (unlikely(cifs_forced_shutdown(cifs_sb))) { + rc = -EIO; + goto rmdir_exit; + } + + tlink = cifs_sb_tlink(cifs_sb); + if (IS_ERR(tlink)) { + rc = PTR_ERR(tlink); + goto rmdir_exit; + } + tcon = tlink_tcon(tlink); + server = tcon->ses->server; + + if (!server->ops->rmdir) { + rc = -ENOSYS; + cifs_put_tlink(tlink); + goto rmdir_exit; + } + + if (tcon->nodelete) { + rc = -EACCES; + cifs_put_tlink(tlink); + goto rmdir_exit; + } + + rc = server->ops->rmdir(xid, tcon, full_path, cifs_sb); + cifs_put_tlink(tlink); + + if (!rc) { + spin_lock(&d_inode(direntry)->i_lock); + i_size_write(d_inode(direntry), 0); + clear_nlink(d_inode(direntry)); + spin_unlock(&d_inode(direntry)->i_lock); + } + + cifsInode = CIFS_I(d_inode(direntry)); + /* force revalidate to go get info when needed */ + cifsInode->time = 0; + + cifsInode = CIFS_I(inode); + /* + * Force revalidate to get parent dir info when needed since cached + * attributes are invalid now. + */ + cifsInode->time = 0; + + d_inode(direntry)->i_ctime = inode->i_ctime = inode->i_mtime = + current_time(inode); + +rmdir_exit: + free_dentry_path(page); + free_xid(xid); + return rc; +} + +static int +cifs_do_rename(const unsigned int xid, struct dentry *from_dentry, + const char *from_path, struct dentry *to_dentry, + const char *to_path) +{ + struct cifs_sb_info *cifs_sb = CIFS_SB(from_dentry->d_sb); + struct tcon_link *tlink; + struct cifs_tcon *tcon; + struct TCP_Server_Info *server; +#ifdef CONFIG_CIFS_ALLOW_INSECURE_LEGACY + struct cifs_fid fid; + struct cifs_open_parms oparms; + int oplock; +#endif /* CONFIG_CIFS_ALLOW_INSECURE_LEGACY */ + int rc; + + tlink = cifs_sb_tlink(cifs_sb); + if (IS_ERR(tlink)) + return PTR_ERR(tlink); + tcon = tlink_tcon(tlink); + server = tcon->ses->server; + + if (!server->ops->rename) + return -ENOSYS; + + /* try path-based rename first */ + rc = server->ops->rename(xid, tcon, from_path, to_path, cifs_sb); + + /* + * Don't bother with rename by filehandle unless file is busy and + * source. Note that cross directory moves do not work with + * rename by filehandle to various Windows servers. + */ + if (rc == 0 || rc != -EBUSY) + goto do_rename_exit; + + /* Don't fall back to using SMB on SMB 2+ mount */ + if (server->vals->protocol_id != 0) + goto do_rename_exit; + +#ifdef CONFIG_CIFS_ALLOW_INSECURE_LEGACY + /* open-file renames don't work across directories */ + if (to_dentry->d_parent != from_dentry->d_parent) + goto do_rename_exit; + + oparms = (struct cifs_open_parms) { + .tcon = tcon, + .cifs_sb = cifs_sb, + /* open the file to be renamed -- we need DELETE perms */ + .desired_access = DELETE, + .create_options = cifs_create_options(cifs_sb, CREATE_NOT_DIR), + .disposition = FILE_OPEN, + .path = from_path, + .fid = &fid, + }; + + rc = CIFS_open(xid, &oparms, &oplock, NULL); + if (rc == 0) { + rc = CIFSSMBRenameOpenFile(xid, tcon, fid.netfid, + (const char *) to_dentry->d_name.name, + cifs_sb->local_nls, cifs_remap(cifs_sb)); + CIFSSMBClose(xid, tcon, fid.netfid); + } +#endif /* CONFIG_CIFS_ALLOW_INSECURE_LEGACY */ +do_rename_exit: + if (rc == 0) + d_move(from_dentry, to_dentry); + cifs_put_tlink(tlink); + return rc; +} + +int +cifs_rename2(struct user_namespace *mnt_userns, struct inode *source_dir, + struct dentry *source_dentry, struct inode *target_dir, + struct dentry *target_dentry, unsigned int flags) +{ + const char *from_name, *to_name; + void *page1, *page2; + struct cifs_sb_info *cifs_sb; + struct tcon_link *tlink; + struct cifs_tcon *tcon; + unsigned int xid; + int rc, tmprc; + int retry_count = 0; + FILE_UNIX_BASIC_INFO *info_buf_source = NULL; +#ifdef CONFIG_CIFS_ALLOW_INSECURE_LEGACY + FILE_UNIX_BASIC_INFO *info_buf_target; +#endif /* CONFIG_CIFS_ALLOW_INSECURE_LEGACY */ + + if (flags & ~RENAME_NOREPLACE) + return -EINVAL; + + cifs_sb = CIFS_SB(source_dir->i_sb); + if (unlikely(cifs_forced_shutdown(cifs_sb))) + return -EIO; + + tlink = cifs_sb_tlink(cifs_sb); + if (IS_ERR(tlink)) + return PTR_ERR(tlink); + tcon = tlink_tcon(tlink); + + page1 = alloc_dentry_path(); + page2 = alloc_dentry_path(); + xid = get_xid(); + + from_name = build_path_from_dentry(source_dentry, page1); + if (IS_ERR(from_name)) { + rc = PTR_ERR(from_name); + goto cifs_rename_exit; + } + + to_name = build_path_from_dentry(target_dentry, page2); + if (IS_ERR(to_name)) { + rc = PTR_ERR(to_name); + goto cifs_rename_exit; + } + + cifs_close_deferred_file_under_dentry(tcon, from_name); + if (d_inode(target_dentry) != NULL) + cifs_close_deferred_file_under_dentry(tcon, to_name); + + rc = cifs_do_rename(xid, source_dentry, from_name, target_dentry, + to_name); + + if (rc == -EACCES) { + while (retry_count < 3) { + cifs_close_all_deferred_files(tcon); + rc = cifs_do_rename(xid, source_dentry, from_name, target_dentry, + to_name); + if (rc != -EACCES) + break; + retry_count++; + } + } + + /* + * No-replace is the natural behavior for CIFS, so skip unlink hacks. + */ + if (flags & RENAME_NOREPLACE) + goto cifs_rename_exit; + +#ifdef CONFIG_CIFS_ALLOW_INSECURE_LEGACY + if (rc == -EEXIST && tcon->unix_ext) { + /* + * Are src and dst hardlinks of same inode? We can only tell + * with unix extensions enabled. + */ + info_buf_source = + kmalloc_array(2, sizeof(FILE_UNIX_BASIC_INFO), + GFP_KERNEL); + if (info_buf_source == NULL) { + rc = -ENOMEM; + goto cifs_rename_exit; + } + + info_buf_target = info_buf_source + 1; + tmprc = CIFSSMBUnixQPathInfo(xid, tcon, from_name, + info_buf_source, + cifs_sb->local_nls, + cifs_remap(cifs_sb)); + if (tmprc != 0) + goto unlink_target; + + tmprc = CIFSSMBUnixQPathInfo(xid, tcon, to_name, + info_buf_target, + cifs_sb->local_nls, + cifs_remap(cifs_sb)); + + if (tmprc == 0 && (info_buf_source->UniqueId == + info_buf_target->UniqueId)) { + /* same file, POSIX says that this is a noop */ + rc = 0; + goto cifs_rename_exit; + } + } + /* + * else ... BB we could add the same check for Windows by + * checking the UniqueId via FILE_INTERNAL_INFO + */ + +unlink_target: +#endif /* CONFIG_CIFS_ALLOW_INSECURE_LEGACY */ + + /* Try unlinking the target dentry if it's not negative */ + if (d_really_is_positive(target_dentry) && (rc == -EACCES || rc == -EEXIST)) { + if (d_is_dir(target_dentry)) + tmprc = cifs_rmdir(target_dir, target_dentry); + else + tmprc = cifs_unlink(target_dir, target_dentry); + if (tmprc) + goto cifs_rename_exit; + rc = cifs_do_rename(xid, source_dentry, from_name, + target_dentry, to_name); + } + + /* force revalidate to go get info when needed */ + CIFS_I(source_dir)->time = CIFS_I(target_dir)->time = 0; + + source_dir->i_ctime = source_dir->i_mtime = target_dir->i_ctime = + target_dir->i_mtime = current_time(source_dir); + +cifs_rename_exit: + kfree(info_buf_source); + free_dentry_path(page2); + free_dentry_path(page1); + free_xid(xid); + cifs_put_tlink(tlink); + return rc; +} + +static bool +cifs_dentry_needs_reval(struct dentry *dentry) +{ + struct inode *inode = d_inode(dentry); + struct cifsInodeInfo *cifs_i = CIFS_I(inode); + struct cifs_sb_info *cifs_sb = CIFS_SB(inode->i_sb); + struct cifs_tcon *tcon = cifs_sb_master_tcon(cifs_sb); + struct cached_fid *cfid = NULL; + + if (cifs_i->time == 0) + return true; + + if (CIFS_CACHE_READ(cifs_i)) + return false; + + if (!lookupCacheEnabled) + return true; + + if (!open_cached_dir_by_dentry(tcon, dentry->d_parent, &cfid)) { + spin_lock(&cfid->fid_lock); + if (cfid->time && cifs_i->time > cfid->time) { + spin_unlock(&cfid->fid_lock); + close_cached_dir(cfid); + return false; + } + spin_unlock(&cfid->fid_lock); + close_cached_dir(cfid); + } + /* + * depending on inode type, check if attribute caching disabled for + * files or directories + */ + if (S_ISDIR(inode->i_mode)) { + if (!cifs_sb->ctx->acdirmax) + return true; + if (!time_in_range(jiffies, cifs_i->time, + cifs_i->time + cifs_sb->ctx->acdirmax)) + return true; + } else { /* file */ + if (!cifs_sb->ctx->acregmax) + return true; + if (!time_in_range(jiffies, cifs_i->time, + cifs_i->time + cifs_sb->ctx->acregmax)) + return true; + } + + /* hardlinked files w/ noserverino get "special" treatment */ + if (!(cifs_sb->mnt_cifs_flags & CIFS_MOUNT_SERVER_INUM) && + S_ISREG(inode->i_mode) && inode->i_nlink != 1) + return true; + + return false; +} + +/* + * Zap the cache. Called when invalid_mapping flag is set. + */ +int +cifs_invalidate_mapping(struct inode *inode) +{ + int rc = 0; + + if (inode->i_mapping && inode->i_mapping->nrpages != 0) { + rc = invalidate_inode_pages2(inode->i_mapping); + if (rc) + cifs_dbg(VFS, "%s: Could not invalidate inode %p\n", + __func__, inode); + } + + return rc; +} + +/** + * cifs_wait_bit_killable - helper for functions that are sleeping on bit locks + * + * @key: currently unused + * @mode: the task state to sleep in + */ +static int +cifs_wait_bit_killable(struct wait_bit_key *key, int mode) +{ + schedule(); + if (signal_pending_state(mode, current)) + return -ERESTARTSYS; + return 0; +} + +int +cifs_revalidate_mapping(struct inode *inode) +{ + int rc; + unsigned long *flags = &CIFS_I(inode)->flags; + struct cifs_sb_info *cifs_sb = CIFS_SB(inode->i_sb); + + /* swapfiles are not supposed to be shared */ + if (IS_SWAPFILE(inode)) + return 0; + + rc = wait_on_bit_lock_action(flags, CIFS_INO_LOCK, cifs_wait_bit_killable, + TASK_KILLABLE|TASK_FREEZABLE_UNSAFE); + if (rc) + return rc; + + if (test_and_clear_bit(CIFS_INO_INVALID_MAPPING, flags)) { + /* for cache=singleclient, do not invalidate */ + if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_RW_CACHE) + goto skip_invalidate; + + rc = cifs_invalidate_mapping(inode); + if (rc) + set_bit(CIFS_INO_INVALID_MAPPING, flags); + } + +skip_invalidate: + clear_bit_unlock(CIFS_INO_LOCK, flags); + smp_mb__after_atomic(); + wake_up_bit(flags, CIFS_INO_LOCK); + + return rc; +} + +int +cifs_zap_mapping(struct inode *inode) +{ + set_bit(CIFS_INO_INVALID_MAPPING, &CIFS_I(inode)->flags); + return cifs_revalidate_mapping(inode); +} + +int cifs_revalidate_file_attr(struct file *filp) +{ + int rc = 0; + struct dentry *dentry = file_dentry(filp); +#ifdef CONFIG_CIFS_ALLOW_INSECURE_LEGACY + struct cifsFileInfo *cfile = (struct cifsFileInfo *) filp->private_data; +#endif /* CONFIG_CIFS_ALLOW_INSECURE_LEGACY */ + + if (!cifs_dentry_needs_reval(dentry)) + return rc; + +#ifdef CONFIG_CIFS_ALLOW_INSECURE_LEGACY + if (tlink_tcon(cfile->tlink)->unix_ext) + rc = cifs_get_file_info_unix(filp); + else +#endif /* CONFIG_CIFS_ALLOW_INSECURE_LEGACY */ + rc = cifs_get_file_info(filp); + + return rc; +} + +int cifs_revalidate_dentry_attr(struct dentry *dentry) +{ + unsigned int xid; + int rc = 0; + struct inode *inode = d_inode(dentry); + struct super_block *sb = dentry->d_sb; + const char *full_path; + void *page; + int count = 0; + + if (inode == NULL) + return -ENOENT; + + if (!cifs_dentry_needs_reval(dentry)) + return rc; + + xid = get_xid(); + + page = alloc_dentry_path(); + full_path = build_path_from_dentry(dentry, page); + if (IS_ERR(full_path)) { + rc = PTR_ERR(full_path); + goto out; + } + + cifs_dbg(FYI, "Update attributes: %s inode 0x%p count %d dentry: 0x%p d_time %ld jiffies %ld\n", + full_path, inode, inode->i_count.counter, + dentry, cifs_get_time(dentry), jiffies); + +again: + if (cifs_sb_master_tcon(CIFS_SB(sb))->posix_extensions) + rc = smb311_posix_get_inode_info(&inode, full_path, sb, xid); + else if (cifs_sb_master_tcon(CIFS_SB(sb))->unix_ext) + rc = cifs_get_inode_info_unix(&inode, full_path, sb, xid); + else + rc = cifs_get_inode_info(&inode, full_path, NULL, sb, + xid, NULL); + if (rc == -EAGAIN && count++ < 10) + goto again; +out: + free_dentry_path(page); + free_xid(xid); + + return rc; +} + +int cifs_revalidate_file(struct file *filp) +{ + int rc; + struct inode *inode = file_inode(filp); + + rc = cifs_revalidate_file_attr(filp); + if (rc) + return rc; + + return cifs_revalidate_mapping(inode); +} + +/* revalidate a dentry's inode attributes */ +int cifs_revalidate_dentry(struct dentry *dentry) +{ + int rc; + struct inode *inode = d_inode(dentry); + + rc = cifs_revalidate_dentry_attr(dentry); + if (rc) + return rc; + + return cifs_revalidate_mapping(inode); +} + +int cifs_getattr(struct user_namespace *mnt_userns, const struct path *path, + struct kstat *stat, u32 request_mask, unsigned int flags) +{ + struct dentry *dentry = path->dentry; + struct cifs_sb_info *cifs_sb = CIFS_SB(dentry->d_sb); + struct cifs_tcon *tcon = cifs_sb_master_tcon(cifs_sb); + struct inode *inode = d_inode(dentry); + int rc; + + if (unlikely(cifs_forced_shutdown(CIFS_SB(inode->i_sb)))) + return -EIO; + + /* + * We need to be sure that all dirty pages are written and the server + * has actual ctime, mtime and file length. + */ + if ((request_mask & (STATX_CTIME | STATX_MTIME | STATX_SIZE | STATX_BLOCKS)) && + !CIFS_CACHE_READ(CIFS_I(inode)) && + inode->i_mapping && inode->i_mapping->nrpages != 0) { + rc = filemap_fdatawait(inode->i_mapping); + if (rc) { + mapping_set_error(inode->i_mapping, rc); + return rc; + } + } + + if ((flags & AT_STATX_SYNC_TYPE) == AT_STATX_FORCE_SYNC) + CIFS_I(inode)->time = 0; /* force revalidate */ + + /* + * If the caller doesn't require syncing, only sync if + * necessary (e.g. due to earlier truncate or setattr + * invalidating the cached metadata) + */ + if (((flags & AT_STATX_SYNC_TYPE) != AT_STATX_DONT_SYNC) || + (CIFS_I(inode)->time == 0)) { + rc = cifs_revalidate_dentry_attr(dentry); + if (rc) + return rc; + } + + generic_fillattr(&init_user_ns, inode, stat); + stat->blksize = cifs_sb->ctx->bsize; + stat->ino = CIFS_I(inode)->uniqueid; + + /* old CIFS Unix Extensions doesn't return create time */ + if (CIFS_I(inode)->createtime) { + stat->result_mask |= STATX_BTIME; + stat->btime = + cifs_NTtimeToUnix(cpu_to_le64(CIFS_I(inode)->createtime)); + } + + stat->attributes_mask |= (STATX_ATTR_COMPRESSED | STATX_ATTR_ENCRYPTED); + if (CIFS_I(inode)->cifsAttrs & FILE_ATTRIBUTE_COMPRESSED) + stat->attributes |= STATX_ATTR_COMPRESSED; + if (CIFS_I(inode)->cifsAttrs & FILE_ATTRIBUTE_ENCRYPTED) + stat->attributes |= STATX_ATTR_ENCRYPTED; + + /* + * If on a multiuser mount without unix extensions or cifsacl being + * enabled, and the admin hasn't overridden them, set the ownership + * to the fsuid/fsgid of the current process. + */ + if ((cifs_sb->mnt_cifs_flags & CIFS_MOUNT_MULTIUSER) && + !(cifs_sb->mnt_cifs_flags & CIFS_MOUNT_CIFS_ACL) && + !tcon->unix_ext) { + if (!(cifs_sb->mnt_cifs_flags & CIFS_MOUNT_OVERR_UID)) + stat->uid = current_fsuid(); + if (!(cifs_sb->mnt_cifs_flags & CIFS_MOUNT_OVERR_GID)) + stat->gid = current_fsgid(); + } + return 0; +} + +int cifs_fiemap(struct inode *inode, struct fiemap_extent_info *fei, u64 start, + u64 len) +{ + struct cifsInodeInfo *cifs_i = CIFS_I(inode); + struct cifs_sb_info *cifs_sb = CIFS_SB(cifs_i->netfs.inode.i_sb); + struct cifs_tcon *tcon = cifs_sb_master_tcon(cifs_sb); + struct TCP_Server_Info *server = tcon->ses->server; + struct cifsFileInfo *cfile; + int rc; + + if (unlikely(cifs_forced_shutdown(cifs_sb))) + return -EIO; + + /* + * We need to be sure that all dirty pages are written as they + * might fill holes on the server. + */ + if (!CIFS_CACHE_READ(CIFS_I(inode)) && inode->i_mapping && + inode->i_mapping->nrpages != 0) { + rc = filemap_fdatawait(inode->i_mapping); + if (rc) { + mapping_set_error(inode->i_mapping, rc); + return rc; + } + } + + cfile = find_readable_file(cifs_i, false); + if (cfile == NULL) + return -EINVAL; + + if (server->ops->fiemap) { + rc = server->ops->fiemap(tcon, cfile, fei, start, len); + cifsFileInfo_put(cfile); + return rc; + } + + cifsFileInfo_put(cfile); + return -ENOTSUPP; +} + +int cifs_truncate_page(struct address_space *mapping, loff_t from) +{ + pgoff_t index = from >> PAGE_SHIFT; + unsigned offset = from & (PAGE_SIZE - 1); + struct page *page; + int rc = 0; + + page = grab_cache_page(mapping, index); + if (!page) + return -ENOMEM; + + zero_user_segment(page, offset, PAGE_SIZE); + unlock_page(page); + put_page(page); + return rc; +} + +void cifs_setsize(struct inode *inode, loff_t offset) +{ + struct cifsInodeInfo *cifs_i = CIFS_I(inode); + + spin_lock(&inode->i_lock); + i_size_write(inode, offset); + spin_unlock(&inode->i_lock); + + /* Cached inode must be refreshed on truncate */ + cifs_i->time = 0; + truncate_pagecache(inode, offset); +} + +static int +cifs_set_file_size(struct inode *inode, struct iattr *attrs, + unsigned int xid, const char *full_path) +{ + int rc; + struct cifsFileInfo *open_file; + struct cifsInodeInfo *cifsInode = CIFS_I(inode); + struct cifs_sb_info *cifs_sb = CIFS_SB(inode->i_sb); + struct tcon_link *tlink = NULL; + struct cifs_tcon *tcon = NULL; + struct TCP_Server_Info *server; + + /* + * 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. + * This keeps our caching token (oplock) and avoids timeouts + * when the local oplock break takes longer to flush + * writebehind data than the SMB timeout for the SetPathInfo + * request would allow + */ + open_file = find_writable_file(cifsInode, FIND_WR_FSUID_ONLY); + if (open_file) { + tcon = tlink_tcon(open_file->tlink); + server = tcon->ses->server; + if (server->ops->set_file_size) + rc = server->ops->set_file_size(xid, tcon, open_file, + attrs->ia_size, false); + else + rc = -ENOSYS; + cifsFileInfo_put(open_file); + cifs_dbg(FYI, "SetFSize for attrs rc = %d\n", rc); + } else + rc = -EINVAL; + + if (!rc) + goto set_size_out; + + if (tcon == NULL) { + tlink = cifs_sb_tlink(cifs_sb); + if (IS_ERR(tlink)) + return PTR_ERR(tlink); + tcon = tlink_tcon(tlink); + server = tcon->ses->server; + } + + /* + * Set file size by pathname rather than by handle either because no + * valid, writeable file handle for it was found or because there was + * an error setting it by handle. + */ + if (server->ops->set_path_size) + rc = server->ops->set_path_size(xid, tcon, full_path, + attrs->ia_size, cifs_sb, false); + else + rc = -ENOSYS; + cifs_dbg(FYI, "SetEOF by path (setattrs) rc = %d\n", rc); + + if (tlink) + cifs_put_tlink(tlink); + +set_size_out: + if (rc == 0) { + cifsInode->server_eof = attrs->ia_size; + cifs_setsize(inode, attrs->ia_size); + /* + * i_blocks is not related to (i_size / i_blksize), but instead + * 512 byte (2**9) size is required for calculating num blocks. + * Until we can query the server for actual allocation size, + * this is best estimate we have for blocks allocated for a file + * Number of blocks must be rounded up so size 1 is not 0 blocks + */ + inode->i_blocks = (512 - 1 + attrs->ia_size) >> 9; + + /* + * The man page of truncate says if the size changed, + * then the st_ctime and st_mtime fields for the file + * are updated. + */ + attrs->ia_ctime = attrs->ia_mtime = current_time(inode); + attrs->ia_valid |= ATTR_CTIME | ATTR_MTIME; + + cifs_truncate_page(inode->i_mapping, inode->i_size); + } + + return rc; +} + +#ifdef CONFIG_CIFS_ALLOW_INSECURE_LEGACY +static int +cifs_setattr_unix(struct dentry *direntry, struct iattr *attrs) +{ + int rc; + unsigned int xid; + const char *full_path; + void *page = alloc_dentry_path(); + struct inode *inode = d_inode(direntry); + struct cifsInodeInfo *cifsInode = CIFS_I(inode); + struct cifs_sb_info *cifs_sb = CIFS_SB(inode->i_sb); + struct tcon_link *tlink; + struct cifs_tcon *pTcon; + struct cifs_unix_set_info_args *args = NULL; + struct cifsFileInfo *open_file; + + cifs_dbg(FYI, "setattr_unix on file %pd attrs->ia_valid=0x%x\n", + direntry, attrs->ia_valid); + + xid = get_xid(); + + if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_NO_PERM) + attrs->ia_valid |= ATTR_FORCE; + + rc = setattr_prepare(&init_user_ns, direntry, attrs); + if (rc < 0) + goto out; + + full_path = build_path_from_dentry(direntry, page); + if (IS_ERR(full_path)) { + rc = PTR_ERR(full_path); + goto out; + } + + /* + * Attempt to flush data before changing attributes. We need to do + * this for ATTR_SIZE and ATTR_MTIME for sure, and if we change the + * ownership or mode then we may also need to do this. Here, we take + * the safe way out and just do the flush on all setattr requests. If + * the flush returns error, store it to report later and continue. + * + * BB: This should be smarter. Why bother flushing pages that + * will be truncated anyway? Also, should we error out here if + * the flush returns error? + */ + rc = filemap_write_and_wait(inode->i_mapping); + if (is_interrupt_error(rc)) { + rc = -ERESTARTSYS; + goto out; + } + + mapping_set_error(inode->i_mapping, rc); + rc = 0; + + if (attrs->ia_valid & ATTR_SIZE) { + rc = cifs_set_file_size(inode, attrs, xid, full_path); + if (rc != 0) + goto out; + } + + /* skip mode change if it's just for clearing setuid/setgid */ + if (attrs->ia_valid & (ATTR_KILL_SUID|ATTR_KILL_SGID)) + attrs->ia_valid &= ~ATTR_MODE; + + args = kmalloc(sizeof(*args), GFP_KERNEL); + if (args == NULL) { + rc = -ENOMEM; + goto out; + } + + /* set up the struct */ + if (attrs->ia_valid & ATTR_MODE) + args->mode = attrs->ia_mode; + else + args->mode = NO_CHANGE_64; + + if (attrs->ia_valid & ATTR_UID) + args->uid = attrs->ia_uid; + else + args->uid = INVALID_UID; /* no change */ + + if (attrs->ia_valid & ATTR_GID) + args->gid = attrs->ia_gid; + else + args->gid = INVALID_GID; /* no change */ + + if (attrs->ia_valid & ATTR_ATIME) + args->atime = cifs_UnixTimeToNT(attrs->ia_atime); + else + args->atime = NO_CHANGE_64; + + if (attrs->ia_valid & ATTR_MTIME) + args->mtime = cifs_UnixTimeToNT(attrs->ia_mtime); + else + args->mtime = NO_CHANGE_64; + + if (attrs->ia_valid & ATTR_CTIME) + args->ctime = cifs_UnixTimeToNT(attrs->ia_ctime); + else + args->ctime = NO_CHANGE_64; + + args->device = 0; + open_file = find_writable_file(cifsInode, FIND_WR_FSUID_ONLY); + if (open_file) { + u16 nfid = open_file->fid.netfid; + u32 npid = open_file->pid; + pTcon = tlink_tcon(open_file->tlink); + rc = CIFSSMBUnixSetFileInfo(xid, pTcon, args, nfid, npid); + cifsFileInfo_put(open_file); + } else { + tlink = cifs_sb_tlink(cifs_sb); + if (IS_ERR(tlink)) { + rc = PTR_ERR(tlink); + goto out; + } + pTcon = tlink_tcon(tlink); + rc = CIFSSMBUnixSetPathInfo(xid, pTcon, full_path, args, + cifs_sb->local_nls, + cifs_remap(cifs_sb)); + cifs_put_tlink(tlink); + } + + if (rc) + goto out; + + if ((attrs->ia_valid & ATTR_SIZE) && + attrs->ia_size != i_size_read(inode)) { + truncate_setsize(inode, attrs->ia_size); + fscache_resize_cookie(cifs_inode_cookie(inode), attrs->ia_size); + } + + setattr_copy(&init_user_ns, inode, attrs); + mark_inode_dirty(inode); + + /* force revalidate when any of these times are set since some + of the fs types (eg ext3, fat) do not have fine enough + time granularity to match protocol, and we do not have a + a way (yet) to query the server fs's time granularity (and + whether it rounds times down). + */ + if (attrs->ia_valid & (ATTR_MTIME | ATTR_CTIME)) + cifsInode->time = 0; +out: + kfree(args); + free_dentry_path(page); + free_xid(xid); + return rc; +} +#endif /* CONFIG_CIFS_ALLOW_INSECURE_LEGACY */ + +static int +cifs_setattr_nounix(struct dentry *direntry, struct iattr *attrs) +{ + unsigned int xid; + kuid_t uid = INVALID_UID; + kgid_t gid = INVALID_GID; + struct inode *inode = d_inode(direntry); + struct cifs_sb_info *cifs_sb = CIFS_SB(inode->i_sb); + struct cifsInodeInfo *cifsInode = CIFS_I(inode); + struct cifsFileInfo *wfile; + struct cifs_tcon *tcon; + const char *full_path; + void *page = alloc_dentry_path(); + int rc = -EACCES; + __u32 dosattr = 0; + __u64 mode = NO_CHANGE_64; + + xid = get_xid(); + + cifs_dbg(FYI, "setattr on file %pd attrs->ia_valid 0x%x\n", + direntry, attrs->ia_valid); + + if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_NO_PERM) + attrs->ia_valid |= ATTR_FORCE; + + rc = setattr_prepare(&init_user_ns, direntry, attrs); + if (rc < 0) + goto cifs_setattr_exit; + + full_path = build_path_from_dentry(direntry, page); + if (IS_ERR(full_path)) { + rc = PTR_ERR(full_path); + goto cifs_setattr_exit; + } + + /* + * Attempt to flush data before changing attributes. We need to do + * this for ATTR_SIZE and ATTR_MTIME. If the flush of the data + * returns error, store it to report later and continue. + * + * BB: This should be smarter. Why bother flushing pages that + * will be truncated anyway? Also, should we error out here if + * the flush returns error? Do we need to check for ATTR_MTIME_SET flag? + */ + if (attrs->ia_valid & (ATTR_MTIME | ATTR_SIZE | ATTR_CTIME)) { + rc = filemap_write_and_wait(inode->i_mapping); + if (is_interrupt_error(rc)) { + rc = -ERESTARTSYS; + goto cifs_setattr_exit; + } + mapping_set_error(inode->i_mapping, rc); + } + + rc = 0; + + if ((attrs->ia_valid & ATTR_MTIME) && + !(cifs_sb->mnt_cifs_flags & CIFS_MOUNT_NOSSYNC)) { + rc = cifs_get_writable_file(cifsInode, FIND_WR_ANY, &wfile); + if (!rc) { + tcon = tlink_tcon(wfile->tlink); + rc = tcon->ses->server->ops->flush(xid, tcon, &wfile->fid); + cifsFileInfo_put(wfile); + if (rc) + goto cifs_setattr_exit; + } else if (rc != -EBADF) + goto cifs_setattr_exit; + else + rc = 0; + } + + if (attrs->ia_valid & ATTR_SIZE) { + rc = cifs_set_file_size(inode, attrs, xid, full_path); + if (rc != 0) + goto cifs_setattr_exit; + } + + if (attrs->ia_valid & ATTR_UID) + uid = attrs->ia_uid; + + if (attrs->ia_valid & ATTR_GID) + gid = attrs->ia_gid; + + if ((cifs_sb->mnt_cifs_flags & CIFS_MOUNT_CIFS_ACL) || + (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_MODE_FROM_SID)) { + if (uid_valid(uid) || gid_valid(gid)) { + mode = NO_CHANGE_64; + rc = id_mode_to_cifs_acl(inode, full_path, &mode, + uid, gid); + if (rc) { + cifs_dbg(FYI, "%s: Setting id failed with error: %d\n", + __func__, rc); + goto cifs_setattr_exit; + } + } + } else + if (!(cifs_sb->mnt_cifs_flags & CIFS_MOUNT_SET_UID)) + attrs->ia_valid &= ~(ATTR_UID | ATTR_GID); + + /* skip mode change if it's just for clearing setuid/setgid */ + if (attrs->ia_valid & (ATTR_KILL_SUID|ATTR_KILL_SGID)) + attrs->ia_valid &= ~ATTR_MODE; + + if (attrs->ia_valid & ATTR_MODE) { + mode = attrs->ia_mode; + rc = 0; + if ((cifs_sb->mnt_cifs_flags & CIFS_MOUNT_CIFS_ACL) || + (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_MODE_FROM_SID)) { + rc = id_mode_to_cifs_acl(inode, full_path, &mode, + INVALID_UID, INVALID_GID); + if (rc) { + cifs_dbg(FYI, "%s: Setting ACL failed with error: %d\n", + __func__, rc); + goto cifs_setattr_exit; + } + + /* + * In case of CIFS_MOUNT_CIFS_ACL, we cannot support all modes. + * Pick up the actual mode bits that were set. + */ + if (mode != attrs->ia_mode) + attrs->ia_mode = mode; + } else + if (((mode & S_IWUGO) == 0) && + (cifsInode->cifsAttrs & ATTR_READONLY) == 0) { + + dosattr = cifsInode->cifsAttrs | ATTR_READONLY; + + /* fix up mode if we're not using dynperm */ + if ((cifs_sb->mnt_cifs_flags & CIFS_MOUNT_DYNPERM) == 0) + attrs->ia_mode = inode->i_mode & ~S_IWUGO; + } else if ((mode & S_IWUGO) && + (cifsInode->cifsAttrs & ATTR_READONLY)) { + + dosattr = cifsInode->cifsAttrs & ~ATTR_READONLY; + /* Attributes of 0 are ignored */ + if (dosattr == 0) + dosattr |= ATTR_NORMAL; + + /* reset local inode permissions to normal */ + if (!(cifs_sb->mnt_cifs_flags & CIFS_MOUNT_DYNPERM)) { + attrs->ia_mode &= ~(S_IALLUGO); + if (S_ISDIR(inode->i_mode)) + attrs->ia_mode |= + cifs_sb->ctx->dir_mode; + else + attrs->ia_mode |= + cifs_sb->ctx->file_mode; + } + } else if (!(cifs_sb->mnt_cifs_flags & CIFS_MOUNT_DYNPERM)) { + /* ignore mode change - ATTR_READONLY hasn't changed */ + attrs->ia_valid &= ~ATTR_MODE; + } + } + + if (attrs->ia_valid & (ATTR_MTIME|ATTR_ATIME|ATTR_CTIME) || + ((attrs->ia_valid & ATTR_MODE) && dosattr)) { + rc = cifs_set_file_info(inode, attrs, xid, full_path, dosattr); + /* BB: check for rc = -EOPNOTSUPP and switch to legacy mode */ + + /* 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) + goto cifs_setattr_exit; + + if ((attrs->ia_valid & ATTR_SIZE) && + attrs->ia_size != i_size_read(inode)) { + truncate_setsize(inode, attrs->ia_size); + fscache_resize_cookie(cifs_inode_cookie(inode), attrs->ia_size); + } + + setattr_copy(&init_user_ns, inode, attrs); + mark_inode_dirty(inode); + +cifs_setattr_exit: + free_xid(xid); + free_dentry_path(page); + return rc; +} + +int +cifs_setattr(struct user_namespace *mnt_userns, struct dentry *direntry, + struct iattr *attrs) +{ + struct cifs_sb_info *cifs_sb = CIFS_SB(direntry->d_sb); + int rc, retries = 0; +#ifdef CONFIG_CIFS_ALLOW_INSECURE_LEGACY + struct cifs_tcon *pTcon = cifs_sb_master_tcon(cifs_sb); +#endif /* CONFIG_CIFS_ALLOW_INSECURE_LEGACY */ + + if (unlikely(cifs_forced_shutdown(cifs_sb))) + return -EIO; + + do { +#ifdef CONFIG_CIFS_ALLOW_INSECURE_LEGACY + if (pTcon->unix_ext) + rc = cifs_setattr_unix(direntry, attrs); + else +#endif /* CONFIG_CIFS_ALLOW_INSECURE_LEGACY */ + rc = cifs_setattr_nounix(direntry, attrs); + retries++; + } while (is_retryable_error(rc) && retries < 2); + + /* BB: add cifs_setattr_legacy for really old servers */ + return rc; +} diff --git a/fs/smb/client/ioctl.c b/fs/smb/client/ioctl.c new file mode 100644 index 0000000000000..6419ec47c2a85 --- /dev/null +++ b/fs/smb/client/ioctl.c @@ -0,0 +1,526 @@ +// SPDX-License-Identifier: LGPL-2.1 +/* + * + * vfs operations that deal with io control + * + * Copyright (C) International Business Machines Corp., 2005,2013 + * Author(s): Steve French (sfrench@us.ibm.com) + * + */ + +#include +#include +#include +#include +#include +#include "cifspdu.h" +#include "cifsglob.h" +#include "cifsproto.h" +#include "cifs_debug.h" +#include "cifsfs.h" +#include "cifs_ioctl.h" +#include "smb2proto.h" +#include "smb2glob.h" +#include + +static long cifs_ioctl_query_info(unsigned int xid, struct file *filep, + unsigned long p) +{ + struct inode *inode = file_inode(filep); + struct cifs_sb_info *cifs_sb = CIFS_SB(inode->i_sb); + struct cifs_tcon *tcon = cifs_sb_master_tcon(cifs_sb); + struct dentry *dentry = filep->f_path.dentry; + const unsigned char *path; + void *page = alloc_dentry_path(); + __le16 *utf16_path = NULL, root_path; + int rc = 0; + + path = build_path_from_dentry(dentry, page); + if (IS_ERR(path)) { + free_dentry_path(page); + return PTR_ERR(path); + } + + cifs_dbg(FYI, "%s %s\n", __func__, path); + + if (!path[0]) { + root_path = 0; + utf16_path = &root_path; + } else { + utf16_path = cifs_convert_path_to_utf16(path + 1, cifs_sb); + if (!utf16_path) { + rc = -ENOMEM; + goto ici_exit; + } + } + + if (tcon->ses->server->ops->ioctl_query_info) + rc = tcon->ses->server->ops->ioctl_query_info( + xid, tcon, cifs_sb, utf16_path, + filep->private_data ? 0 : 1, p); + else + rc = -EOPNOTSUPP; + + ici_exit: + if (utf16_path != &root_path) + kfree(utf16_path); + free_dentry_path(page); + return rc; +} + +static long cifs_ioctl_copychunk(unsigned int xid, struct file *dst_file, + unsigned long srcfd) +{ + int rc; + struct fd src_file; + struct inode *src_inode; + + cifs_dbg(FYI, "ioctl copychunk range\n"); + /* the destination must be opened for writing */ + if (!(dst_file->f_mode & FMODE_WRITE)) { + cifs_dbg(FYI, "file target not open for write\n"); + return -EINVAL; + } + + /* check if target volume is readonly and take reference */ + rc = mnt_want_write_file(dst_file); + if (rc) { + cifs_dbg(FYI, "mnt_want_write failed with rc %d\n", rc); + return rc; + } + + src_file = fdget(srcfd); + if (!src_file.file) { + rc = -EBADF; + goto out_drop_write; + } + + if (src_file.file->f_op->unlocked_ioctl != cifs_ioctl) { + rc = -EBADF; + cifs_dbg(VFS, "src file seems to be from a different filesystem type\n"); + goto out_fput; + } + + src_inode = file_inode(src_file.file); + rc = -EINVAL; + if (S_ISDIR(src_inode->i_mode)) + goto out_fput; + + rc = cifs_file_copychunk_range(xid, src_file.file, 0, dst_file, 0, + src_inode->i_size, 0); + if (rc > 0) + rc = 0; +out_fput: + fdput(src_file); +out_drop_write: + mnt_drop_write_file(dst_file); + return rc; +} + +static long smb_mnt_get_fsinfo(unsigned int xid, struct cifs_tcon *tcon, + void __user *arg) +{ + int rc = 0; + struct smb_mnt_fs_info *fsinf; + + fsinf = kzalloc(sizeof(struct smb_mnt_fs_info), GFP_KERNEL); + if (fsinf == NULL) + return -ENOMEM; + + fsinf->version = 1; + fsinf->protocol_id = tcon->ses->server->vals->protocol_id; + fsinf->device_characteristics = + le32_to_cpu(tcon->fsDevInfo.DeviceCharacteristics); + fsinf->device_type = le32_to_cpu(tcon->fsDevInfo.DeviceType); + fsinf->fs_attributes = le32_to_cpu(tcon->fsAttrInfo.Attributes); + fsinf->max_path_component = + le32_to_cpu(tcon->fsAttrInfo.MaxPathNameComponentLength); + fsinf->vol_serial_number = tcon->vol_serial_number; + fsinf->vol_create_time = le64_to_cpu(tcon->vol_create_time); + fsinf->share_flags = tcon->share_flags; + fsinf->share_caps = le32_to_cpu(tcon->capabilities); + fsinf->sector_flags = tcon->ss_flags; + fsinf->optimal_sector_size = tcon->perf_sector_size; + fsinf->max_bytes_chunk = tcon->max_bytes_chunk; + fsinf->maximal_access = tcon->maximal_access; + fsinf->cifs_posix_caps = le64_to_cpu(tcon->fsUnixInfo.Capability); + + if (copy_to_user(arg, fsinf, sizeof(struct smb_mnt_fs_info))) + rc = -EFAULT; + + kfree(fsinf); + return rc; +} + +static int cifs_shutdown(struct super_block *sb, unsigned long arg) +{ + struct cifs_sb_info *sbi = CIFS_SB(sb); + __u32 flags; + + if (!capable(CAP_SYS_ADMIN)) + return -EPERM; + + if (get_user(flags, (__u32 __user *)arg)) + return -EFAULT; + + if (flags > CIFS_GOING_FLAGS_NOLOGFLUSH) + return -EINVAL; + + if (cifs_forced_shutdown(sbi)) + return 0; + + cifs_dbg(VFS, "shut down requested (%d)", flags); +/* trace_cifs_shutdown(sb, flags);*/ + + /* + * see: + * https://man7.org/linux/man-pages/man2/ioctl_xfs_goingdown.2.html + * for more information and description of original intent of the flags + */ + switch (flags) { + /* + * We could add support later for default flag which requires: + * "Flush all dirty data and metadata to disk" + * would need to call syncfs or equivalent to flush page cache for + * the mount and then issue fsync to server (if nostrictsync not set) + */ + case CIFS_GOING_FLAGS_DEFAULT: + cifs_dbg(FYI, "shutdown with default flag not supported\n"); + return -EINVAL; + /* + * FLAGS_LOGFLUSH is easy since it asks to write out metadata (not + * data) but metadata writes are not cached on the client, so can treat + * it similarly to NOLOGFLUSH + */ + case CIFS_GOING_FLAGS_LOGFLUSH: + case CIFS_GOING_FLAGS_NOLOGFLUSH: + sbi->mnt_cifs_flags |= CIFS_MOUNT_SHUTDOWN; + return 0; + default: + return -EINVAL; + } + return 0; +} + +static int cifs_dump_full_key(struct cifs_tcon *tcon, struct smb3_full_key_debug_info __user *in) +{ + struct smb3_full_key_debug_info out; + struct cifs_ses *ses; + int rc = 0; + bool found = false; + u8 __user *end; + + if (!smb3_encryption_required(tcon)) { + rc = -EOPNOTSUPP; + goto out; + } + + /* copy user input into our output buffer */ + if (copy_from_user(&out, in, sizeof(out))) { + rc = -EINVAL; + goto out; + } + + if (!out.session_id) { + /* if ses id is 0, use current user session */ + ses = tcon->ses; + } else { + /* otherwise if a session id is given, look for it in all our sessions */ + struct cifs_ses *ses_it = NULL; + struct TCP_Server_Info *server_it = NULL; + + spin_lock(&cifs_tcp_ses_lock); + list_for_each_entry(server_it, &cifs_tcp_ses_list, tcp_ses_list) { + list_for_each_entry(ses_it, &server_it->smb_ses_list, smb_ses_list) { + if (ses_it->Suid == out.session_id) { + ses = ses_it; + /* + * since we are using the session outside the crit + * section, we need to make sure it won't be released + * so increment its refcount + */ + ses->ses_count++; + found = true; + goto search_end; + } + } + } +search_end: + spin_unlock(&cifs_tcp_ses_lock); + if (!found) { + rc = -ENOENT; + goto out; + } + } + + switch (ses->server->cipher_type) { + case SMB2_ENCRYPTION_AES128_CCM: + case SMB2_ENCRYPTION_AES128_GCM: + out.session_key_length = CIFS_SESS_KEY_SIZE; + out.server_in_key_length = out.server_out_key_length = SMB3_GCM128_CRYPTKEY_SIZE; + break; + case SMB2_ENCRYPTION_AES256_CCM: + case SMB2_ENCRYPTION_AES256_GCM: + out.session_key_length = CIFS_SESS_KEY_SIZE; + out.server_in_key_length = out.server_out_key_length = SMB3_GCM256_CRYPTKEY_SIZE; + break; + default: + rc = -EOPNOTSUPP; + goto out; + } + + /* check if user buffer is big enough to store all the keys */ + if (out.in_size < sizeof(out) + out.session_key_length + out.server_in_key_length + + out.server_out_key_length) { + rc = -ENOBUFS; + goto out; + } + + out.session_id = ses->Suid; + out.cipher_type = le16_to_cpu(ses->server->cipher_type); + + /* overwrite user input with our output */ + if (copy_to_user(in, &out, sizeof(out))) { + rc = -EINVAL; + goto out; + } + + /* append all the keys at the end of the user buffer */ + end = in->data; + if (copy_to_user(end, ses->auth_key.response, out.session_key_length)) { + rc = -EINVAL; + goto out; + } + end += out.session_key_length; + + if (copy_to_user(end, ses->smb3encryptionkey, out.server_in_key_length)) { + rc = -EINVAL; + goto out; + } + end += out.server_in_key_length; + + if (copy_to_user(end, ses->smb3decryptionkey, out.server_out_key_length)) { + rc = -EINVAL; + goto out; + } + +out: + if (found) + cifs_put_smb_ses(ses); + return rc; +} + +long cifs_ioctl(struct file *filep, unsigned int command, unsigned long arg) +{ + struct inode *inode = file_inode(filep); + struct smb3_key_debug_info pkey_inf; + int rc = -ENOTTY; /* strange error - but the precedent */ + unsigned int xid; + struct cifsFileInfo *pSMBFile = filep->private_data; + struct cifs_tcon *tcon; + struct tcon_link *tlink; + struct cifs_sb_info *cifs_sb; + __u64 ExtAttrBits = 0; + __u64 caps; + + xid = get_xid(); + + cifs_dbg(FYI, "cifs ioctl 0x%x\n", command); + switch (command) { + case FS_IOC_GETFLAGS: + if (pSMBFile == NULL) + break; + tcon = tlink_tcon(pSMBFile->tlink); + caps = le64_to_cpu(tcon->fsUnixInfo.Capability); +#ifdef CONFIG_CIFS_POSIX +#ifdef CONFIG_CIFS_ALLOW_INSECURE_LEGACY + if (CIFS_UNIX_EXTATTR_CAP & caps) { + __u64 ExtAttrMask = 0; + rc = CIFSGetExtAttr(xid, tcon, + pSMBFile->fid.netfid, + &ExtAttrBits, &ExtAttrMask); + if (rc == 0) + rc = put_user(ExtAttrBits & + FS_FL_USER_VISIBLE, + (int __user *)arg); + if (rc != -EOPNOTSUPP) + break; + } +#endif /* CONFIG_CIFS_ALLOW_INSECURE_LEGACY */ +#endif /* CONFIG_CIFS_POSIX */ + rc = 0; + if (CIFS_I(inode)->cifsAttrs & ATTR_COMPRESSED) { + /* add in the compressed bit */ + ExtAttrBits = FS_COMPR_FL; + rc = put_user(ExtAttrBits & FS_FL_USER_VISIBLE, + (int __user *)arg); + } + break; + case FS_IOC_SETFLAGS: + if (pSMBFile == NULL) + break; + tcon = tlink_tcon(pSMBFile->tlink); + /* caps = le64_to_cpu(tcon->fsUnixInfo.Capability); */ + + if (get_user(ExtAttrBits, (int __user *)arg)) { + rc = -EFAULT; + break; + } + + /* + * if (CIFS_UNIX_EXTATTR_CAP & caps) + * rc = CIFSSetExtAttr(xid, tcon, + * pSMBFile->fid.netfid, + * extAttrBits, + * &ExtAttrMask); + * if (rc != -EOPNOTSUPP) + * break; + */ + + /* Currently only flag we can set is compressed flag */ + if ((ExtAttrBits & FS_COMPR_FL) == 0) + break; + + /* Try to set compress flag */ + if (tcon->ses->server->ops->set_compression) { + rc = tcon->ses->server->ops->set_compression( + xid, tcon, pSMBFile); + cifs_dbg(FYI, "set compress flag rc %d\n", rc); + } + break; + case CIFS_IOC_COPYCHUNK_FILE: + rc = cifs_ioctl_copychunk(xid, filep, arg); + break; + case CIFS_QUERY_INFO: + rc = cifs_ioctl_query_info(xid, filep, arg); + break; + case CIFS_IOC_SET_INTEGRITY: + if (pSMBFile == NULL) + break; + tcon = tlink_tcon(pSMBFile->tlink); + if (tcon->ses->server->ops->set_integrity) + rc = tcon->ses->server->ops->set_integrity(xid, + tcon, pSMBFile); + else + rc = -EOPNOTSUPP; + break; + case CIFS_IOC_GET_MNT_INFO: + if (pSMBFile == NULL) + break; + tcon = tlink_tcon(pSMBFile->tlink); + rc = smb_mnt_get_fsinfo(xid, tcon, (void __user *)arg); + break; + case CIFS_ENUMERATE_SNAPSHOTS: + if (pSMBFile == NULL) + break; + if (arg == 0) { + rc = -EINVAL; + goto cifs_ioc_exit; + } + tcon = tlink_tcon(pSMBFile->tlink); + if (tcon->ses->server->ops->enum_snapshots) + rc = tcon->ses->server->ops->enum_snapshots(xid, tcon, + pSMBFile, (void __user *)arg); + else + rc = -EOPNOTSUPP; + break; + case CIFS_DUMP_KEY: + /* + * Dump encryption keys. This is an old ioctl that only + * handles AES-128-{CCM,GCM}. + */ + if (pSMBFile == NULL) + break; + if (!capable(CAP_SYS_ADMIN)) { + rc = -EACCES; + break; + } + + tcon = tlink_tcon(pSMBFile->tlink); + if (!smb3_encryption_required(tcon)) { + rc = -EOPNOTSUPP; + break; + } + pkey_inf.cipher_type = + le16_to_cpu(tcon->ses->server->cipher_type); + pkey_inf.Suid = tcon->ses->Suid; + memcpy(pkey_inf.auth_key, tcon->ses->auth_key.response, + 16 /* SMB2_NTLMV2_SESSKEY_SIZE */); + memcpy(pkey_inf.smb3decryptionkey, + tcon->ses->smb3decryptionkey, SMB3_SIGN_KEY_SIZE); + memcpy(pkey_inf.smb3encryptionkey, + tcon->ses->smb3encryptionkey, SMB3_SIGN_KEY_SIZE); + if (copy_to_user((void __user *)arg, &pkey_inf, + sizeof(struct smb3_key_debug_info))) + rc = -EFAULT; + else + rc = 0; + break; + case CIFS_DUMP_FULL_KEY: + /* + * Dump encryption keys (handles any key sizes) + */ + if (pSMBFile == NULL) + break; + if (!capable(CAP_SYS_ADMIN)) { + rc = -EACCES; + break; + } + tcon = tlink_tcon(pSMBFile->tlink); + rc = cifs_dump_full_key(tcon, (void __user *)arg); + break; + case CIFS_IOC_NOTIFY: + if (!S_ISDIR(inode->i_mode)) { + /* Notify can only be done on directories */ + rc = -EOPNOTSUPP; + break; + } + cifs_sb = CIFS_SB(inode->i_sb); + tlink = cifs_sb_tlink(cifs_sb); + if (IS_ERR(tlink)) { + rc = PTR_ERR(tlink); + break; + } + tcon = tlink_tcon(tlink); + if (tcon && tcon->ses->server->ops->notify) { + rc = tcon->ses->server->ops->notify(xid, + filep, (void __user *)arg, + false /* no ret data */); + cifs_dbg(FYI, "ioctl notify rc %d\n", rc); + } else + rc = -EOPNOTSUPP; + cifs_put_tlink(tlink); + break; + case CIFS_IOC_NOTIFY_INFO: + if (!S_ISDIR(inode->i_mode)) { + /* Notify can only be done on directories */ + rc = -EOPNOTSUPP; + break; + } + cifs_sb = CIFS_SB(inode->i_sb); + tlink = cifs_sb_tlink(cifs_sb); + if (IS_ERR(tlink)) { + rc = PTR_ERR(tlink); + break; + } + tcon = tlink_tcon(tlink); + if (tcon && tcon->ses->server->ops->notify) { + rc = tcon->ses->server->ops->notify(xid, + filep, (void __user *)arg, + true /* return details */); + cifs_dbg(FYI, "ioctl notify info rc %d\n", rc); + } else + rc = -EOPNOTSUPP; + cifs_put_tlink(tlink); + break; + case CIFS_IOC_SHUTDOWN: + rc = cifs_shutdown(inode->i_sb, arg); + break; + default: + cifs_dbg(FYI, "unsupported ioctl\n"); + break; + } +cifs_ioc_exit: + free_xid(xid); + return rc; +} diff --git a/fs/smb/client/link.c b/fs/smb/client/link.c new file mode 100644 index 0000000000000..c0f101fc1e5d0 --- /dev/null +++ b/fs/smb/client/link.c @@ -0,0 +1,650 @@ +// SPDX-License-Identifier: LGPL-2.1 +/* + * + * Copyright (C) International Business Machines Corp., 2002,2008 + * Author(s): Steve French (sfrench@us.ibm.com) + * + */ +#include +#include +#include +#include +#include "cifsfs.h" +#include "cifspdu.h" +#include "cifsglob.h" +#include "cifsproto.h" +#include "cifs_debug.h" +#include "cifs_fs_sb.h" +#include "cifs_unicode.h" +#include "smb2proto.h" +#include "cifs_ioctl.h" + +/* + * M-F Symlink Functions - Begin + */ + +#define CIFS_MF_SYMLINK_LEN_OFFSET (4+1) +#define CIFS_MF_SYMLINK_MD5_OFFSET (CIFS_MF_SYMLINK_LEN_OFFSET+(4+1)) +#define CIFS_MF_SYMLINK_LINK_OFFSET (CIFS_MF_SYMLINK_MD5_OFFSET+(32+1)) +#define CIFS_MF_SYMLINK_LINK_MAXLEN (1024) +#define CIFS_MF_SYMLINK_FILE_SIZE \ + (CIFS_MF_SYMLINK_LINK_OFFSET + CIFS_MF_SYMLINK_LINK_MAXLEN) + +#define CIFS_MF_SYMLINK_LEN_FORMAT "XSym\n%04u\n" +#define CIFS_MF_SYMLINK_MD5_FORMAT "%16phN\n" +#define CIFS_MF_SYMLINK_MD5_ARGS(md5_hash) md5_hash + +static int +symlink_hash(unsigned int link_len, const char *link_str, u8 *md5_hash) +{ + int rc; + struct shash_desc *md5 = NULL; + + rc = cifs_alloc_hash("md5", &md5); + if (rc) + goto symlink_hash_err; + + rc = crypto_shash_init(md5); + if (rc) { + cifs_dbg(VFS, "%s: Could not init md5 shash\n", __func__); + goto symlink_hash_err; + } + rc = crypto_shash_update(md5, link_str, link_len); + if (rc) { + cifs_dbg(VFS, "%s: Could not update with link_str\n", __func__); + goto symlink_hash_err; + } + rc = crypto_shash_final(md5, md5_hash); + if (rc) + cifs_dbg(VFS, "%s: Could not generate md5 hash\n", __func__); + +symlink_hash_err: + cifs_free_hash(&md5); + return rc; +} + +static int +parse_mf_symlink(const u8 *buf, unsigned int buf_len, unsigned int *_link_len, + char **_link_str) +{ + int rc; + unsigned int link_len; + const char *md5_str1; + const char *link_str; + u8 md5_hash[16]; + char md5_str2[34]; + + if (buf_len != CIFS_MF_SYMLINK_FILE_SIZE) + return -EINVAL; + + md5_str1 = (const char *)&buf[CIFS_MF_SYMLINK_MD5_OFFSET]; + link_str = (const char *)&buf[CIFS_MF_SYMLINK_LINK_OFFSET]; + + rc = sscanf(buf, CIFS_MF_SYMLINK_LEN_FORMAT, &link_len); + if (rc != 1) + return -EINVAL; + + if (link_len > CIFS_MF_SYMLINK_LINK_MAXLEN) + return -EINVAL; + + rc = symlink_hash(link_len, link_str, md5_hash); + if (rc) { + cifs_dbg(FYI, "%s: MD5 hash failure: %d\n", __func__, rc); + return rc; + } + + scnprintf(md5_str2, sizeof(md5_str2), + CIFS_MF_SYMLINK_MD5_FORMAT, + CIFS_MF_SYMLINK_MD5_ARGS(md5_hash)); + + if (strncmp(md5_str1, md5_str2, 17) != 0) + return -EINVAL; + + if (_link_str) { + *_link_str = kstrndup(link_str, link_len, GFP_KERNEL); + if (!*_link_str) + return -ENOMEM; + } + + *_link_len = link_len; + return 0; +} + +static int +format_mf_symlink(u8 *buf, unsigned int buf_len, const char *link_str) +{ + int rc; + unsigned int link_len; + unsigned int ofs; + u8 md5_hash[16]; + + if (buf_len != CIFS_MF_SYMLINK_FILE_SIZE) + return -EINVAL; + + link_len = strlen(link_str); + + if (link_len > CIFS_MF_SYMLINK_LINK_MAXLEN) + return -ENAMETOOLONG; + + rc = symlink_hash(link_len, link_str, md5_hash); + if (rc) { + cifs_dbg(FYI, "%s: MD5 hash failure: %d\n", __func__, rc); + return rc; + } + + scnprintf(buf, buf_len, + CIFS_MF_SYMLINK_LEN_FORMAT CIFS_MF_SYMLINK_MD5_FORMAT, + link_len, + CIFS_MF_SYMLINK_MD5_ARGS(md5_hash)); + + ofs = CIFS_MF_SYMLINK_LINK_OFFSET; + memcpy(buf + ofs, link_str, link_len); + + ofs += link_len; + if (ofs < CIFS_MF_SYMLINK_FILE_SIZE) { + buf[ofs] = '\n'; + ofs++; + } + + while (ofs < CIFS_MF_SYMLINK_FILE_SIZE) { + buf[ofs] = ' '; + ofs++; + } + + return 0; +} + +bool +couldbe_mf_symlink(const struct cifs_fattr *fattr) +{ + if (!S_ISREG(fattr->cf_mode)) + /* it's not a symlink */ + return false; + + if (fattr->cf_eof != CIFS_MF_SYMLINK_FILE_SIZE) + /* it's not a symlink */ + return false; + + return true; +} + +static int +create_mf_symlink(const unsigned int xid, struct cifs_tcon *tcon, + struct cifs_sb_info *cifs_sb, const char *fromName, + const char *toName) +{ + int rc; + u8 *buf; + unsigned int bytes_written = 0; + + buf = kmalloc(CIFS_MF_SYMLINK_FILE_SIZE, GFP_KERNEL); + if (!buf) + return -ENOMEM; + + rc = format_mf_symlink(buf, CIFS_MF_SYMLINK_FILE_SIZE, toName); + if (rc) + goto out; + + if (tcon->ses->server->ops->create_mf_symlink) + rc = tcon->ses->server->ops->create_mf_symlink(xid, tcon, + cifs_sb, fromName, buf, &bytes_written); + else + rc = -EOPNOTSUPP; + + if (rc) + goto out; + + if (bytes_written != CIFS_MF_SYMLINK_FILE_SIZE) + rc = -EIO; +out: + kfree(buf); + return rc; +} + +int +check_mf_symlink(unsigned int xid, struct cifs_tcon *tcon, + struct cifs_sb_info *cifs_sb, struct cifs_fattr *fattr, + const unsigned char *path) +{ + int rc; + u8 *buf = NULL; + unsigned int link_len = 0; + unsigned int bytes_read = 0; + char *symlink = NULL; + + if (!couldbe_mf_symlink(fattr)) + /* it's not a symlink */ + return 0; + + buf = kmalloc(CIFS_MF_SYMLINK_FILE_SIZE, GFP_KERNEL); + if (!buf) + return -ENOMEM; + + if (tcon->ses->server->ops->query_mf_symlink) + rc = tcon->ses->server->ops->query_mf_symlink(xid, tcon, + cifs_sb, path, buf, &bytes_read); + else + rc = -ENOSYS; + + if (rc) + goto out; + + if (bytes_read == 0) /* not a symlink */ + goto out; + + rc = parse_mf_symlink(buf, bytes_read, &link_len, &symlink); + if (rc == -EINVAL) { + /* it's not a symlink */ + rc = 0; + goto out; + } + + if (rc != 0) + goto out; + + /* it is a symlink */ + fattr->cf_eof = link_len; + fattr->cf_mode &= ~S_IFMT; + fattr->cf_mode |= S_IFLNK | S_IRWXU | S_IRWXG | S_IRWXO; + fattr->cf_dtype = DT_LNK; + fattr->cf_symlink_target = symlink; +out: + kfree(buf); + return rc; +} + +#ifdef CONFIG_CIFS_ALLOW_INSECURE_LEGACY +/* + * SMB 1.0 Protocol specific functions + */ + +int +cifs_query_mf_symlink(unsigned int xid, struct cifs_tcon *tcon, + struct cifs_sb_info *cifs_sb, const unsigned char *path, + char *pbuf, unsigned int *pbytes_read) +{ + int rc; + int oplock = 0; + struct cifs_fid fid; + struct cifs_open_parms oparms; + struct cifs_io_parms io_parms = {0}; + int buf_type = CIFS_NO_BUFFER; + FILE_ALL_INFO file_info; + + oparms = (struct cifs_open_parms) { + .tcon = tcon, + .cifs_sb = cifs_sb, + .desired_access = GENERIC_READ, + .create_options = cifs_create_options(cifs_sb, CREATE_NOT_DIR), + .disposition = FILE_OPEN, + .path = path, + .fid = &fid, + }; + + rc = CIFS_open(xid, &oparms, &oplock, &file_info); + if (rc) + return rc; + + if (file_info.EndOfFile != cpu_to_le64(CIFS_MF_SYMLINK_FILE_SIZE)) { + rc = -ENOENT; + /* it's not a symlink */ + goto out; + } + + io_parms.netfid = fid.netfid; + io_parms.pid = current->tgid; + io_parms.tcon = tcon; + io_parms.offset = 0; + io_parms.length = CIFS_MF_SYMLINK_FILE_SIZE; + + rc = CIFSSMBRead(xid, &io_parms, pbytes_read, &pbuf, &buf_type); +out: + CIFSSMBClose(xid, tcon, fid.netfid); + return rc; +} + +int +cifs_create_mf_symlink(unsigned int xid, struct cifs_tcon *tcon, + struct cifs_sb_info *cifs_sb, const unsigned char *path, + char *pbuf, unsigned int *pbytes_written) +{ + int rc; + int oplock = 0; + struct cifs_fid fid; + struct cifs_open_parms oparms; + struct cifs_io_parms io_parms = {0}; + + oparms = (struct cifs_open_parms) { + .tcon = tcon, + .cifs_sb = cifs_sb, + .desired_access = GENERIC_WRITE, + .create_options = cifs_create_options(cifs_sb, CREATE_NOT_DIR), + .disposition = FILE_CREATE, + .path = path, + .fid = &fid, + }; + + rc = CIFS_open(xid, &oparms, &oplock, NULL); + if (rc) + return rc; + + io_parms.netfid = fid.netfid; + io_parms.pid = current->tgid; + io_parms.tcon = tcon; + io_parms.offset = 0; + io_parms.length = CIFS_MF_SYMLINK_FILE_SIZE; + + rc = CIFSSMBWrite(xid, &io_parms, pbytes_written, pbuf); + CIFSSMBClose(xid, tcon, fid.netfid); + return rc; +} +#endif /* CONFIG_CIFS_ALLOW_INSECURE_LEGACY */ + +/* + * SMB 2.1/SMB3 Protocol specific functions + */ +int +smb3_query_mf_symlink(unsigned int xid, struct cifs_tcon *tcon, + struct cifs_sb_info *cifs_sb, const unsigned char *path, + char *pbuf, unsigned int *pbytes_read) +{ + int rc; + struct cifs_fid fid; + struct cifs_open_parms oparms; + struct cifs_io_parms io_parms = {0}; + int buf_type = CIFS_NO_BUFFER; + __le16 *utf16_path; + __u8 oplock = SMB2_OPLOCK_LEVEL_NONE; + struct smb2_file_all_info *pfile_info = NULL; + + oparms = (struct cifs_open_parms) { + .tcon = tcon, + .cifs_sb = cifs_sb, + .path = path, + .desired_access = GENERIC_READ, + .create_options = cifs_create_options(cifs_sb, CREATE_NOT_DIR), + .disposition = FILE_OPEN, + .fid = &fid, + }; + + utf16_path = cifs_convert_path_to_utf16(path, cifs_sb); + if (utf16_path == NULL) + return -ENOMEM; + + pfile_info = kzalloc(sizeof(struct smb2_file_all_info) + PATH_MAX * 2, + GFP_KERNEL); + + if (pfile_info == NULL) { + kfree(utf16_path); + return -ENOMEM; + } + + rc = SMB2_open(xid, &oparms, utf16_path, &oplock, pfile_info, NULL, + NULL, NULL); + if (rc) + goto qmf_out_open_fail; + + if (pfile_info->EndOfFile != cpu_to_le64(CIFS_MF_SYMLINK_FILE_SIZE)) { + /* it's not a symlink */ + rc = -ENOENT; /* Is there a better rc to return? */ + goto qmf_out; + } + + io_parms.netfid = fid.netfid; + io_parms.pid = current->tgid; + io_parms.tcon = tcon; + io_parms.offset = 0; + io_parms.length = CIFS_MF_SYMLINK_FILE_SIZE; + io_parms.persistent_fid = fid.persistent_fid; + io_parms.volatile_fid = fid.volatile_fid; + rc = SMB2_read(xid, &io_parms, pbytes_read, &pbuf, &buf_type); +qmf_out: + SMB2_close(xid, tcon, fid.persistent_fid, fid.volatile_fid); +qmf_out_open_fail: + kfree(utf16_path); + kfree(pfile_info); + return rc; +} + +int +smb3_create_mf_symlink(unsigned int xid, struct cifs_tcon *tcon, + struct cifs_sb_info *cifs_sb, const unsigned char *path, + char *pbuf, unsigned int *pbytes_written) +{ + int rc; + struct cifs_fid fid; + struct cifs_open_parms oparms; + struct cifs_io_parms io_parms = {0}; + __le16 *utf16_path; + __u8 oplock = SMB2_OPLOCK_LEVEL_NONE; + struct kvec iov[2]; + + cifs_dbg(FYI, "%s: path: %s\n", __func__, path); + + utf16_path = cifs_convert_path_to_utf16(path, cifs_sb); + if (!utf16_path) + return -ENOMEM; + + oparms = (struct cifs_open_parms) { + .tcon = tcon, + .cifs_sb = cifs_sb, + .path = path, + .desired_access = GENERIC_WRITE, + .create_options = cifs_create_options(cifs_sb, CREATE_NOT_DIR), + .disposition = FILE_CREATE, + .fid = &fid, + .mode = 0644, + }; + + rc = SMB2_open(xid, &oparms, utf16_path, &oplock, NULL, NULL, + NULL, NULL); + if (rc) { + kfree(utf16_path); + return rc; + } + + io_parms.netfid = fid.netfid; + io_parms.pid = current->tgid; + io_parms.tcon = tcon; + io_parms.offset = 0; + io_parms.length = CIFS_MF_SYMLINK_FILE_SIZE; + io_parms.persistent_fid = fid.persistent_fid; + io_parms.volatile_fid = fid.volatile_fid; + + /* iov[0] is reserved for smb header */ + iov[1].iov_base = pbuf; + iov[1].iov_len = CIFS_MF_SYMLINK_FILE_SIZE; + + rc = SMB2_write(xid, &io_parms, pbytes_written, iov, 1); + + /* Make sure we wrote all of the symlink data */ + if ((rc == 0) && (*pbytes_written != CIFS_MF_SYMLINK_FILE_SIZE)) + rc = -EIO; + + SMB2_close(xid, tcon, fid.persistent_fid, fid.volatile_fid); + + kfree(utf16_path); + return rc; +} + +/* + * M-F Symlink Functions - End + */ + +int +cifs_hardlink(struct dentry *old_file, struct inode *inode, + struct dentry *direntry) +{ + int rc = -EACCES; + unsigned int xid; + const char *from_name, *to_name; + void *page1, *page2; + struct cifs_sb_info *cifs_sb = CIFS_SB(inode->i_sb); + struct tcon_link *tlink; + struct cifs_tcon *tcon; + struct TCP_Server_Info *server; + struct cifsInodeInfo *cifsInode; + + if (unlikely(cifs_forced_shutdown(cifs_sb))) + return -EIO; + + tlink = cifs_sb_tlink(cifs_sb); + if (IS_ERR(tlink)) + return PTR_ERR(tlink); + tcon = tlink_tcon(tlink); + + xid = get_xid(); + page1 = alloc_dentry_path(); + page2 = alloc_dentry_path(); + + from_name = build_path_from_dentry(old_file, page1); + if (IS_ERR(from_name)) { + rc = PTR_ERR(from_name); + goto cifs_hl_exit; + } + to_name = build_path_from_dentry(direntry, page2); + if (IS_ERR(to_name)) { + rc = PTR_ERR(to_name); + goto cifs_hl_exit; + } + +#ifdef CONFIG_CIFS_ALLOW_INSECURE_LEGACY + if (tcon->unix_ext) + rc = CIFSUnixCreateHardLink(xid, tcon, from_name, to_name, + cifs_sb->local_nls, + cifs_remap(cifs_sb)); + else { +#else + { +#endif /* CONFIG_CIFS_ALLOW_INSECURE_LEGACY */ + server = tcon->ses->server; + if (!server->ops->create_hardlink) { + rc = -ENOSYS; + goto cifs_hl_exit; + } + rc = server->ops->create_hardlink(xid, tcon, from_name, to_name, + cifs_sb); + if ((rc == -EIO) || (rc == -EINVAL)) + rc = -EOPNOTSUPP; + } + + d_drop(direntry); /* force new lookup from server of target */ + + /* + * if source file is cached (oplocked) revalidate will not go to server + * until the file is closed or oplock broken so update nlinks locally + */ + if (d_really_is_positive(old_file)) { + cifsInode = CIFS_I(d_inode(old_file)); + if (rc == 0) { + spin_lock(&d_inode(old_file)->i_lock); + inc_nlink(d_inode(old_file)); + spin_unlock(&d_inode(old_file)->i_lock); + + /* + * parent dir timestamps will update from srv within a + * second, would it really be worth it to set the parent + * dir cifs inode time to zero to force revalidate + * (faster) for it too? + */ + } + /* + * if not oplocked will force revalidate to get info on source + * file from srv. Note Samba server prior to 4.2 has bug - + * not updating src file ctime on hardlinks but Windows servers + * handle it properly + */ + cifsInode->time = 0; + + /* + * Will update parent dir timestamps from srv within a second. + * Would it really be worth it to set the parent dir (cifs + * inode) time field to zero to force revalidate on parent + * directory faster ie + * + * CIFS_I(inode)->time = 0; + */ + } + +cifs_hl_exit: + free_dentry_path(page1); + free_dentry_path(page2); + free_xid(xid); + cifs_put_tlink(tlink); + return rc; +} + +int +cifs_symlink(struct user_namespace *mnt_userns, struct inode *inode, + struct dentry *direntry, const char *symname) +{ + int rc = -EOPNOTSUPP; + unsigned int xid; + struct cifs_sb_info *cifs_sb = CIFS_SB(inode->i_sb); + struct tcon_link *tlink; + struct cifs_tcon *pTcon; + const char *full_path; + void *page; + struct inode *newinode = NULL; + + if (unlikely(cifs_forced_shutdown(cifs_sb))) + return -EIO; + + page = alloc_dentry_path(); + if (!page) + return -ENOMEM; + + xid = get_xid(); + + tlink = cifs_sb_tlink(cifs_sb); + if (IS_ERR(tlink)) { + rc = PTR_ERR(tlink); + goto symlink_exit; + } + pTcon = tlink_tcon(tlink); + + full_path = build_path_from_dentry(direntry, page); + if (IS_ERR(full_path)) { + rc = PTR_ERR(full_path); + goto symlink_exit; + } + + cifs_dbg(FYI, "Full path: %s\n", full_path); + cifs_dbg(FYI, "symname is %s\n", symname); + + /* BB what if DFS and this volume is on different share? BB */ + if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_MF_SYMLINKS) + rc = create_mf_symlink(xid, pTcon, cifs_sb, full_path, symname); +#ifdef CONFIG_CIFS_ALLOW_INSECURE_LEGACY + else if (pTcon->unix_ext) + rc = CIFSUnixCreateSymLink(xid, pTcon, full_path, symname, + cifs_sb->local_nls, + cifs_remap(cifs_sb)); +#endif /* CONFIG_CIFS_ALLOW_INSECURE_LEGACY */ + /* else + rc = CIFSCreateReparseSymLink(xid, pTcon, fromName, toName, + cifs_sb_target->local_nls); */ + + if (rc == 0) { + if (pTcon->posix_extensions) + rc = smb311_posix_get_inode_info(&newinode, full_path, inode->i_sb, xid); + else if (pTcon->unix_ext) + rc = cifs_get_inode_info_unix(&newinode, full_path, + inode->i_sb, xid); + else + rc = cifs_get_inode_info(&newinode, full_path, NULL, + inode->i_sb, xid, NULL); + + if (rc != 0) { + cifs_dbg(FYI, "Create symlink ok, getinodeinfo fail rc = %d\n", + rc); + } else { + d_instantiate(direntry, newinode); + } + } +symlink_exit: + free_dentry_path(page); + cifs_put_tlink(tlink); + free_xid(xid); + return rc; +} diff --git a/fs/smb/client/misc.c b/fs/smb/client/misc.c new file mode 100644 index 0000000000000..31e06133acc3d --- /dev/null +++ b/fs/smb/client/misc.c @@ -0,0 +1,1434 @@ +// SPDX-License-Identifier: LGPL-2.1 +/* + * + * Copyright (C) International Business Machines Corp., 2002,2008 + * Author(s): Steve French (sfrench@us.ibm.com) + * + */ + +#include +#include +#include +#include +#include "cifspdu.h" +#include "cifsglob.h" +#include "cifsproto.h" +#include "cifs_debug.h" +#include "smberr.h" +#include "nterr.h" +#include "cifs_unicode.h" +#include "smb2pdu.h" +#include "cifsfs.h" +#ifdef CONFIG_CIFS_DFS_UPCALL +#include "dns_resolve.h" +#include "dfs_cache.h" +#endif +#include "fs_context.h" +#include "cached_dir.h" + +extern mempool_t *cifs_sm_req_poolp; +extern mempool_t *cifs_req_poolp; + +/* 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 + will eventually wrap past zero) of the total vfs operations handled + since the cifs fs was mounted */ + +unsigned int +_get_xid(void) +{ + unsigned int xid; + + spin_lock(&GlobalMid_Lock); + GlobalTotalActiveXid++; + + /* keep high water mark for number of simultaneous ops in filesystem */ + if (GlobalTotalActiveXid > GlobalMaxActiveXid) + GlobalMaxActiveXid = GlobalTotalActiveXid; + if (GlobalTotalActiveXid > 65000) + cifs_dbg(FYI, "warning: more than 65000 requests active\n"); + xid = GlobalCurrentXid++; + spin_unlock(&GlobalMid_Lock); + return xid; +} + +void +_free_xid(unsigned int xid) +{ + spin_lock(&GlobalMid_Lock); + /* if (GlobalTotalActiveXid == 0) + BUG(); */ + GlobalTotalActiveXid--; + spin_unlock(&GlobalMid_Lock); +} + +struct cifs_ses * +sesInfoAlloc(void) +{ + struct cifs_ses *ret_buf; + + ret_buf = kzalloc(sizeof(struct cifs_ses), GFP_KERNEL); + if (ret_buf) { + atomic_inc(&sesInfoAllocCount); + spin_lock_init(&ret_buf->ses_lock); + ret_buf->ses_status = SES_NEW; + ++ret_buf->ses_count; + INIT_LIST_HEAD(&ret_buf->smb_ses_list); + INIT_LIST_HEAD(&ret_buf->tcon_list); + mutex_init(&ret_buf->session_mutex); + spin_lock_init(&ret_buf->iface_lock); + INIT_LIST_HEAD(&ret_buf->iface_list); + spin_lock_init(&ret_buf->chan_lock); + } + return ret_buf; +} + +void +sesInfoFree(struct cifs_ses *buf_to_free) +{ + struct cifs_server_iface *iface = NULL, *niface = NULL; + + if (buf_to_free == NULL) { + cifs_dbg(FYI, "Null buffer passed to sesInfoFree\n"); + return; + } + + atomic_dec(&sesInfoAllocCount); + kfree(buf_to_free->serverOS); + kfree(buf_to_free->serverDomain); + kfree(buf_to_free->serverNOS); + kfree_sensitive(buf_to_free->password); + kfree(buf_to_free->user_name); + kfree(buf_to_free->domainName); + kfree_sensitive(buf_to_free->auth_key.response); + spin_lock(&buf_to_free->iface_lock); + list_for_each_entry_safe(iface, niface, &buf_to_free->iface_list, + iface_head) + kref_put(&iface->refcount, release_iface); + spin_unlock(&buf_to_free->iface_lock); + kfree_sensitive(buf_to_free); +} + +struct cifs_tcon * +tconInfoAlloc(void) +{ + struct cifs_tcon *ret_buf; + + ret_buf = kzalloc(sizeof(*ret_buf), GFP_KERNEL); + if (!ret_buf) + return NULL; + ret_buf->cfids = init_cached_dirs(); + if (!ret_buf->cfids) { + kfree(ret_buf); + return NULL; + } + + atomic_inc(&tconInfoAllocCount); + ret_buf->status = TID_NEW; + ++ret_buf->tc_count; + spin_lock_init(&ret_buf->tc_lock); + INIT_LIST_HEAD(&ret_buf->openFileList); + INIT_LIST_HEAD(&ret_buf->tcon_list); + spin_lock_init(&ret_buf->open_file_lock); + spin_lock_init(&ret_buf->stat_lock); + atomic_set(&ret_buf->num_local_opens, 0); + atomic_set(&ret_buf->num_remote_opens, 0); + + return ret_buf; +} + +void +tconInfoFree(struct cifs_tcon *tcon) +{ + if (tcon == NULL) { + cifs_dbg(FYI, "Null buffer passed to tconInfoFree\n"); + return; + } + free_cached_dirs(tcon->cfids); + atomic_dec(&tconInfoAllocCount); + kfree(tcon->nativeFileSystem); + kfree_sensitive(tcon->password); + kfree(tcon); +} + +struct smb_hdr * +cifs_buf_get(void) +{ + struct smb_hdr *ret_buf = NULL; + /* + * SMB2 header is bigger than CIFS one - no problems to clean some + * more bytes for CIFS. + */ + size_t buf_size = sizeof(struct smb2_hdr); + + /* + * We could use negotiated size instead of max_msgsize - + * but it may be more efficient to always alloc same size + * albeit slightly larger than necessary and maxbuffersize + * defaults to this and can not be bigger. + */ + ret_buf = mempool_alloc(cifs_req_poolp, GFP_NOFS); + + /* clear the first few header bytes */ + /* for most paths, more is cleared in header_assemble */ + memset(ret_buf, 0, buf_size + 3); + atomic_inc(&buf_alloc_count); +#ifdef CONFIG_CIFS_STATS2 + atomic_inc(&total_buf_alloc_count); +#endif /* CONFIG_CIFS_STATS2 */ + + return ret_buf; +} + +void +cifs_buf_release(void *buf_to_free) +{ + if (buf_to_free == NULL) { + /* cifs_dbg(FYI, "Null buffer passed to cifs_buf_release\n");*/ + return; + } + mempool_free(buf_to_free, cifs_req_poolp); + + atomic_dec(&buf_alloc_count); + return; +} + +struct smb_hdr * +cifs_small_buf_get(void) +{ + struct smb_hdr *ret_buf = NULL; + +/* We could use negotiated size instead of max_msgsize - + but it may be more efficient to always alloc same size + albeit slightly larger than necessary and maxbuffersize + defaults to this and can not be bigger */ + ret_buf = mempool_alloc(cifs_sm_req_poolp, GFP_NOFS); + /* No need to clear memory here, cleared in header assemble */ + /* memset(ret_buf, 0, sizeof(struct smb_hdr) + 27);*/ + atomic_inc(&small_buf_alloc_count); +#ifdef CONFIG_CIFS_STATS2 + atomic_inc(&total_small_buf_alloc_count); +#endif /* CONFIG_CIFS_STATS2 */ + + return ret_buf; +} + +void +cifs_small_buf_release(void *buf_to_free) +{ + + if (buf_to_free == NULL) { + cifs_dbg(FYI, "Null buffer passed to cifs_small_buf_release\n"); + return; + } + mempool_free(buf_to_free, cifs_sm_req_poolp); + + atomic_dec(&small_buf_alloc_count); + return; +} + +void +free_rsp_buf(int resp_buftype, void *rsp) +{ + if (resp_buftype == CIFS_SMALL_BUFFER) + cifs_small_buf_release(rsp); + else if (resp_buftype == CIFS_LARGE_BUFFER) + cifs_buf_release(rsp); +} + +/* 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 cifs_tcon *treeCon, int word_count + /* length of fixed section (word count) in two byte units */) +{ + char *temp = (char *) buffer; + + memset(temp, 0, 256); /* bigger than MAX_CIFS_HDR_SIZE */ + + buffer->smb_buf_length = cpu_to_be32( + (2 * word_count) + sizeof(struct smb_hdr) - + 4 /* RFC 1001 length field does not count */ + + 2 /* for bcc field itself */) ; + + buffer->Protocol[0] = 0xFF; + buffer->Protocol[1] = 'S'; + buffer->Protocol[2] = 'M'; + buffer->Protocol[3] = 'B'; + buffer->Command = smb_command; + buffer->Flags = 0x00; /* case sensitive */ + buffer->Flags2 = SMBFLG2_KNOWS_LONG_NAMES; + buffer->Pid = cpu_to_le16((__u16)current->tgid); + buffer->PidHigh = cpu_to_le16((__u16)(current->tgid >> 16)); + if (treeCon) { + buffer->Tid = treeCon->tid; + if (treeCon->ses) { + if (treeCon->ses->capabilities & CAP_UNICODE) + buffer->Flags2 |= SMBFLG2_UNICODE; + if (treeCon->ses->capabilities & CAP_STATUS32) + buffer->Flags2 |= SMBFLG2_ERR_STATUS; + + /* Uid is not converted */ + buffer->Uid = treeCon->ses->Suid; + if (treeCon->ses->server) + buffer->Mid = get_next_mid(treeCon->ses->server); + } + 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->sign) + buffer->Flags2 |= SMBFLG2_SECURITY_SIGNATURE; + } + +/* endian conversion of flags is now done just before sending */ + buffer->WordCount = (char) word_count; + return; +} + +static int +check_smb_hdr(struct smb_hdr *smb) +{ + /* does it have the right SMB "signature" ? */ + if (*(__le32 *) smb->Protocol != cpu_to_le32(0x424d53ff)) { + cifs_dbg(VFS, "Bad protocol string signature header 0x%x\n", + *(unsigned int *)smb->Protocol); + return 1; + } + + /* if it's a response then accept */ + if (smb->Flags & SMBFLG_RESPONSE) + return 0; + + /* only one valid case where server sends us request */ + if (smb->Command == SMB_COM_LOCKING_ANDX) + return 0; + + cifs_dbg(VFS, "Server sent request, not response. mid=%u\n", + get_mid(smb)); + return 1; +} + +int +checkSMB(char *buf, unsigned int total_read, struct TCP_Server_Info *server) +{ + struct smb_hdr *smb = (struct smb_hdr *)buf; + __u32 rfclen = be32_to_cpu(smb->smb_buf_length); + __u32 clc_len; /* calculated length */ + cifs_dbg(FYI, "checkSMB Length: 0x%x, smb_buf_length: 0x%x\n", + total_read, rfclen); + + /* is this frame too small to even get to a BCC? */ + if (total_read < 2 + sizeof(struct smb_hdr)) { + if ((total_read >= sizeof(struct smb_hdr) - 1) + && (smb->Status.CifsError != 0)) { + /* it's an error return */ + smb->WordCount = 0; + /* some error cases do not return wct and bcc */ + return 0; + } else if ((total_read == sizeof(struct smb_hdr) + 1) && + (smb->WordCount == 0)) { + char *tmp = (char *)smb; + /* Need to work around a bug in two servers here */ + /* First, check if the part of bcc they sent was zero */ + if (tmp[sizeof(struct smb_hdr)] == 0) { + /* some servers return only half of bcc + * on simple responses (wct, bcc both zero) + * in particular have seen this on + * ulogoffX and FindClose. This leaves + * one byte of bcc potentially unitialized + */ + /* zero rest of bcc */ + tmp[sizeof(struct smb_hdr)+1] = 0; + return 0; + } + cifs_dbg(VFS, "rcvd invalid byte count (bcc)\n"); + } else { + cifs_dbg(VFS, "Length less than smb header size\n"); + } + return -EIO; + } + + /* otherwise, there is enough to get to the BCC */ + if (check_smb_hdr(smb)) + return -EIO; + clc_len = smbCalcSize(smb); + + if (4 + rfclen != total_read) { + cifs_dbg(VFS, "Length read does not match RFC1001 length %d\n", + rfclen); + return -EIO; + } + + if (4 + rfclen != clc_len) { + __u16 mid = get_mid(smb); + /* check if bcc wrapped around for large read responses */ + if ((rfclen > 64 * 1024) && (rfclen > clc_len)) { + /* check if lengths match mod 64K */ + if (((4 + rfclen) & 0xFFFF) == (clc_len & 0xFFFF)) + return 0; /* bcc wrapped */ + } + cifs_dbg(FYI, "Calculated size %u vs length %u mismatch for mid=%u\n", + clc_len, 4 + rfclen, mid); + + if (4 + rfclen < clc_len) { + cifs_dbg(VFS, "RFC1001 size %u smaller than SMB for mid=%u\n", + rfclen, mid); + return -EIO; + } else if (rfclen > clc_len + 512) { + /* + * Some servers (Windows XP in particular) send more + * data than the lengths in the SMB packet would + * indicate on certain calls (byte range locks and + * trans2 find first calls in particular). While the + * client can handle such a frame by ignoring the + * trailing data, we choose limit the amount of extra + * data to 512 bytes. + */ + cifs_dbg(VFS, "RFC1001 size %u more than 512 bytes larger than SMB for mid=%u\n", + rfclen, mid); + return -EIO; + } + } + return 0; +} + +bool +is_valid_oplock_break(char *buffer, struct TCP_Server_Info *srv) +{ + struct smb_hdr *buf = (struct smb_hdr *)buffer; + struct smb_com_lock_req *pSMB = (struct smb_com_lock_req *)buf; + struct TCP_Server_Info *pserver; + struct cifs_ses *ses; + struct cifs_tcon *tcon; + struct cifsInodeInfo *pCifsInode; + struct cifsFileInfo *netfile; + + cifs_dbg(FYI, "Checking for oplock break or dnotify response\n"); + if ((pSMB->hdr.Command == SMB_COM_NT_TRANSACT) && + (pSMB->hdr.Flags & SMBFLG_RESPONSE)) { + struct smb_com_transaction_change_notify_rsp *pSMBr = + (struct smb_com_transaction_change_notify_rsp *)buf; + struct file_notify_information *pnotify; + __u32 data_offset = 0; + size_t len = srv->total_read - sizeof(pSMBr->hdr.smb_buf_length); + + if (get_bcc(buf) > sizeof(struct file_notify_information)) { + data_offset = le32_to_cpu(pSMBr->DataOffset); + + if (data_offset > + len - sizeof(struct file_notify_information)) { + cifs_dbg(FYI, "Invalid data_offset %u\n", + data_offset); + return true; + } + pnotify = (struct file_notify_information *) + ((char *)&pSMBr->hdr.Protocol + data_offset); + cifs_dbg(FYI, "dnotify on %s Action: 0x%x\n", + pnotify->FileName, pnotify->Action); + /* cifs_dump_mem("Rcvd notify Data: ",buf, + sizeof(struct smb_hdr)+60); */ + return true; + } + if (pSMBr->hdr.Status.CifsError) { + cifs_dbg(FYI, "notify err 0x%x\n", + pSMBr->hdr.Status.CifsError); + return true; + } + return false; + } + if (pSMB->hdr.Command != SMB_COM_LOCKING_ANDX) + return false; + if (pSMB->hdr.Flags & SMBFLG_RESPONSE) { + /* no sense logging error on invalid handle on oplock + break - harmless race between close request and oplock + break response is expected from time to time writing out + large dirty files cached on the client */ + if ((NT_STATUS_INVALID_HANDLE) == + le32_to_cpu(pSMB->hdr.Status.CifsError)) { + cifs_dbg(FYI, "Invalid handle on oplock break\n"); + return true; + } else if (ERRbadfid == + le16_to_cpu(pSMB->hdr.Status.DosError.Error)) { + return true; + } else { + return false; /* on valid oplock brk we get "request" */ + } + } + if (pSMB->hdr.WordCount != 8) + return false; + + cifs_dbg(FYI, "oplock type 0x%x level 0x%x\n", + pSMB->LockType, pSMB->OplockLevel); + if (!(pSMB->LockType & LOCKING_ANDX_OPLOCK_RELEASE)) + return false; + + /* If server is a channel, select the primary channel */ + pserver = CIFS_SERVER_IS_CHAN(srv) ? srv->primary_server : srv; + + /* look up tcon based on tid & uid */ + spin_lock(&cifs_tcp_ses_lock); + list_for_each_entry(ses, &pserver->smb_ses_list, smb_ses_list) { + list_for_each_entry(tcon, &ses->tcon_list, tcon_list) { + if (tcon->tid != buf->Tid) + continue; + + cifs_stats_inc(&tcon->stats.cifs_stats.num_oplock_brks); + spin_lock(&tcon->open_file_lock); + list_for_each_entry(netfile, &tcon->openFileList, tlist) { + if (pSMB->Fid != netfile->fid.netfid) + continue; + + cifs_dbg(FYI, "file id match, oplock break\n"); + pCifsInode = CIFS_I(d_inode(netfile->dentry)); + + set_bit(CIFS_INODE_PENDING_OPLOCK_BREAK, + &pCifsInode->flags); + + netfile->oplock_epoch = 0; + netfile->oplock_level = pSMB->OplockLevel; + netfile->oplock_break_cancelled = false; + cifs_queue_oplock_break(netfile); + + spin_unlock(&tcon->open_file_lock); + spin_unlock(&cifs_tcp_ses_lock); + return true; + } + spin_unlock(&tcon->open_file_lock); + spin_unlock(&cifs_tcp_ses_lock); + cifs_dbg(FYI, "No matching file for oplock break\n"); + return true; + } + } + spin_unlock(&cifs_tcp_ses_lock); + cifs_dbg(FYI, "Can not process oplock break for non-existent connection\n"); + return true; +} + +void +dump_smb(void *buf, int smb_buf_length) +{ + if (traceSMB == 0) + return; + + print_hex_dump(KERN_DEBUG, "", DUMP_PREFIX_NONE, 8, 2, buf, + smb_buf_length, true); +} + +void +cifs_autodisable_serverino(struct cifs_sb_info *cifs_sb) +{ + if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_SERVER_INUM) { + struct cifs_tcon *tcon = NULL; + + if (cifs_sb->master_tlink) + tcon = cifs_sb_master_tcon(cifs_sb); + + cifs_sb->mnt_cifs_flags &= ~CIFS_MOUNT_SERVER_INUM; + cifs_sb->mnt_cifs_serverino_autodisabled = true; + cifs_dbg(VFS, "Autodisabling the use of server inode numbers on %s\n", + tcon ? tcon->tree_name : "new server"); + cifs_dbg(VFS, "The server doesn't seem to support them properly or the files might be on different servers (DFS)\n"); + cifs_dbg(VFS, "Hardlinks will not be recognized on this mount. Consider mounting with the \"noserverino\" option to silence this message.\n"); + + } +} + +void cifs_set_oplock_level(struct cifsInodeInfo *cinode, __u32 oplock) +{ + oplock &= 0xF; + + if (oplock == OPLOCK_EXCLUSIVE) { + cinode->oplock = CIFS_CACHE_WRITE_FLG | CIFS_CACHE_READ_FLG; + cifs_dbg(FYI, "Exclusive Oplock granted on inode %p\n", + &cinode->netfs.inode); + } else if (oplock == OPLOCK_READ) { + cinode->oplock = CIFS_CACHE_READ_FLG; + cifs_dbg(FYI, "Level II Oplock granted on inode %p\n", + &cinode->netfs.inode); + } else + cinode->oplock = 0; +} + +/* + * We wait for oplock breaks to be processed before we attempt to perform + * writes. + */ +int cifs_get_writer(struct cifsInodeInfo *cinode) +{ + int rc; + +start: + rc = wait_on_bit(&cinode->flags, CIFS_INODE_PENDING_OPLOCK_BREAK, + TASK_KILLABLE); + if (rc) + return rc; + + spin_lock(&cinode->writers_lock); + if (!cinode->writers) + set_bit(CIFS_INODE_PENDING_WRITERS, &cinode->flags); + cinode->writers++; + /* Check to see if we have started servicing an oplock break */ + if (test_bit(CIFS_INODE_PENDING_OPLOCK_BREAK, &cinode->flags)) { + cinode->writers--; + if (cinode->writers == 0) { + clear_bit(CIFS_INODE_PENDING_WRITERS, &cinode->flags); + wake_up_bit(&cinode->flags, CIFS_INODE_PENDING_WRITERS); + } + spin_unlock(&cinode->writers_lock); + goto start; + } + spin_unlock(&cinode->writers_lock); + return 0; +} + +void cifs_put_writer(struct cifsInodeInfo *cinode) +{ + spin_lock(&cinode->writers_lock); + cinode->writers--; + if (cinode->writers == 0) { + clear_bit(CIFS_INODE_PENDING_WRITERS, &cinode->flags); + wake_up_bit(&cinode->flags, CIFS_INODE_PENDING_WRITERS); + } + spin_unlock(&cinode->writers_lock); +} + +/** + * cifs_queue_oplock_break - queue the oplock break handler for cfile + * @cfile: The file to break the oplock on + * + * This function is called from the demultiplex thread when it + * receives an oplock break for @cfile. + * + * Assumes the tcon->open_file_lock is held. + * Assumes cfile->file_info_lock is NOT held. + */ +void cifs_queue_oplock_break(struct cifsFileInfo *cfile) +{ + /* + * Bump the handle refcount now while we hold the + * open_file_lock to enforce the validity of it for the oplock + * break handler. The matching put is done at the end of the + * handler. + */ + cifsFileInfo_get(cfile); + + queue_work(cifsoplockd_wq, &cfile->oplock_break); +} + +void cifs_done_oplock_break(struct cifsInodeInfo *cinode) +{ + clear_bit(CIFS_INODE_PENDING_OPLOCK_BREAK, &cinode->flags); + wake_up_bit(&cinode->flags, CIFS_INODE_PENDING_OPLOCK_BREAK); +} + +bool +backup_cred(struct cifs_sb_info *cifs_sb) +{ + if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_CIFS_BACKUPUID) { + if (uid_eq(cifs_sb->ctx->backupuid, current_fsuid())) + return true; + } + if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_CIFS_BACKUPGID) { + if (in_group_p(cifs_sb->ctx->backupgid)) + return true; + } + + return false; +} + +void +cifs_del_pending_open(struct cifs_pending_open *open) +{ + spin_lock(&tlink_tcon(open->tlink)->open_file_lock); + list_del(&open->olist); + spin_unlock(&tlink_tcon(open->tlink)->open_file_lock); +} + +void +cifs_add_pending_open_locked(struct cifs_fid *fid, struct tcon_link *tlink, + struct cifs_pending_open *open) +{ + memcpy(open->lease_key, fid->lease_key, SMB2_LEASE_KEY_SIZE); + open->oplock = CIFS_OPLOCK_NO_CHANGE; + open->tlink = tlink; + fid->pending_open = open; + list_add_tail(&open->olist, &tlink_tcon(tlink)->pending_opens); +} + +void +cifs_add_pending_open(struct cifs_fid *fid, struct tcon_link *tlink, + struct cifs_pending_open *open) +{ + spin_lock(&tlink_tcon(tlink)->open_file_lock); + cifs_add_pending_open_locked(fid, tlink, open); + spin_unlock(&tlink_tcon(open->tlink)->open_file_lock); +} + +/* + * Critical section which runs after acquiring deferred_lock. + * As there is no reference count on cifs_deferred_close, pdclose + * should not be used outside deferred_lock. + */ +bool +cifs_is_deferred_close(struct cifsFileInfo *cfile, struct cifs_deferred_close **pdclose) +{ + struct cifs_deferred_close *dclose; + + list_for_each_entry(dclose, &CIFS_I(d_inode(cfile->dentry))->deferred_closes, dlist) { + if ((dclose->netfid == cfile->fid.netfid) && + (dclose->persistent_fid == cfile->fid.persistent_fid) && + (dclose->volatile_fid == cfile->fid.volatile_fid)) { + *pdclose = dclose; + return true; + } + } + return false; +} + +/* + * Critical section which runs after acquiring deferred_lock. + */ +void +cifs_add_deferred_close(struct cifsFileInfo *cfile, struct cifs_deferred_close *dclose) +{ + bool is_deferred = false; + struct cifs_deferred_close *pdclose; + + is_deferred = cifs_is_deferred_close(cfile, &pdclose); + if (is_deferred) { + kfree(dclose); + return; + } + + dclose->tlink = cfile->tlink; + dclose->netfid = cfile->fid.netfid; + dclose->persistent_fid = cfile->fid.persistent_fid; + dclose->volatile_fid = cfile->fid.volatile_fid; + list_add_tail(&dclose->dlist, &CIFS_I(d_inode(cfile->dentry))->deferred_closes); +} + +/* + * Critical section which runs after acquiring deferred_lock. + */ +void +cifs_del_deferred_close(struct cifsFileInfo *cfile) +{ + bool is_deferred = false; + struct cifs_deferred_close *dclose; + + is_deferred = cifs_is_deferred_close(cfile, &dclose); + if (!is_deferred) + return; + list_del(&dclose->dlist); + kfree(dclose); +} + +void +cifs_close_deferred_file(struct cifsInodeInfo *cifs_inode) +{ + struct cifsFileInfo *cfile = NULL; + struct file_list *tmp_list, *tmp_next_list; + struct list_head file_head; + + if (cifs_inode == NULL) + return; + + INIT_LIST_HEAD(&file_head); + spin_lock(&cifs_inode->open_file_lock); + list_for_each_entry(cfile, &cifs_inode->openFileList, flist) { + if (delayed_work_pending(&cfile->deferred)) { + if (cancel_delayed_work(&cfile->deferred)) { + spin_lock(&cifs_inode->deferred_lock); + cifs_del_deferred_close(cfile); + spin_unlock(&cifs_inode->deferred_lock); + + tmp_list = kmalloc(sizeof(struct file_list), GFP_ATOMIC); + if (tmp_list == NULL) + break; + tmp_list->cfile = cfile; + list_add_tail(&tmp_list->list, &file_head); + } + } + } + spin_unlock(&cifs_inode->open_file_lock); + + list_for_each_entry_safe(tmp_list, tmp_next_list, &file_head, list) { + _cifsFileInfo_put(tmp_list->cfile, false, false); + list_del(&tmp_list->list); + kfree(tmp_list); + } +} + +void +cifs_close_all_deferred_files(struct cifs_tcon *tcon) +{ + struct cifsFileInfo *cfile; + struct file_list *tmp_list, *tmp_next_list; + struct list_head file_head; + + INIT_LIST_HEAD(&file_head); + spin_lock(&tcon->open_file_lock); + list_for_each_entry(cfile, &tcon->openFileList, tlist) { + if (delayed_work_pending(&cfile->deferred)) { + if (cancel_delayed_work(&cfile->deferred)) { + spin_lock(&CIFS_I(d_inode(cfile->dentry))->deferred_lock); + cifs_del_deferred_close(cfile); + spin_unlock(&CIFS_I(d_inode(cfile->dentry))->deferred_lock); + + tmp_list = kmalloc(sizeof(struct file_list), GFP_ATOMIC); + if (tmp_list == NULL) + break; + tmp_list->cfile = cfile; + list_add_tail(&tmp_list->list, &file_head); + } + } + } + spin_unlock(&tcon->open_file_lock); + + list_for_each_entry_safe(tmp_list, tmp_next_list, &file_head, list) { + _cifsFileInfo_put(tmp_list->cfile, true, false); + list_del(&tmp_list->list); + kfree(tmp_list); + } +} +void +cifs_close_deferred_file_under_dentry(struct cifs_tcon *tcon, const char *path) +{ + struct cifsFileInfo *cfile; + struct file_list *tmp_list, *tmp_next_list; + struct list_head file_head; + void *page; + const char *full_path; + + INIT_LIST_HEAD(&file_head); + page = alloc_dentry_path(); + spin_lock(&tcon->open_file_lock); + list_for_each_entry(cfile, &tcon->openFileList, tlist) { + full_path = build_path_from_dentry(cfile->dentry, page); + if (strstr(full_path, path)) { + if (delayed_work_pending(&cfile->deferred)) { + if (cancel_delayed_work(&cfile->deferred)) { + spin_lock(&CIFS_I(d_inode(cfile->dentry))->deferred_lock); + cifs_del_deferred_close(cfile); + spin_unlock(&CIFS_I(d_inode(cfile->dentry))->deferred_lock); + + tmp_list = kmalloc(sizeof(struct file_list), GFP_ATOMIC); + if (tmp_list == NULL) + break; + tmp_list->cfile = cfile; + list_add_tail(&tmp_list->list, &file_head); + } + } + } + } + spin_unlock(&tcon->open_file_lock); + + list_for_each_entry_safe(tmp_list, tmp_next_list, &file_head, list) { + _cifsFileInfo_put(tmp_list->cfile, true, false); + list_del(&tmp_list->list); + kfree(tmp_list); + } + free_dentry_path(page); +} + +/* parses DFS referral V3 structure + * caller is responsible for freeing target_nodes + * returns: + * - on success - 0 + * - on failure - errno + */ +int +parse_dfs_referrals(struct get_dfs_referral_rsp *rsp, u32 rsp_size, + unsigned int *num_of_nodes, + struct dfs_info3_param **target_nodes, + const struct nls_table *nls_codepage, int remap, + const char *searchName, bool is_unicode) +{ + int i, rc = 0; + char *data_end; + struct dfs_referral_level_3 *ref; + + *num_of_nodes = le16_to_cpu(rsp->NumberOfReferrals); + + if (*num_of_nodes < 1) { + cifs_dbg(VFS, "num_referrals: must be at least > 0, but we get num_referrals = %d\n", + *num_of_nodes); + rc = -EINVAL; + goto parse_DFS_referrals_exit; + } + + ref = (struct dfs_referral_level_3 *) &(rsp->referrals); + if (ref->VersionNumber != cpu_to_le16(3)) { + cifs_dbg(VFS, "Referrals of V%d version are not supported, should be V3\n", + le16_to_cpu(ref->VersionNumber)); + rc = -EINVAL; + goto parse_DFS_referrals_exit; + } + + /* get the upper boundary of the resp buffer */ + data_end = (char *)rsp + rsp_size; + + cifs_dbg(FYI, "num_referrals: %d dfs flags: 0x%x ...\n", + *num_of_nodes, le32_to_cpu(rsp->DFSFlags)); + + *target_nodes = kcalloc(*num_of_nodes, sizeof(struct dfs_info3_param), + GFP_KERNEL); + if (*target_nodes == NULL) { + rc = -ENOMEM; + goto parse_DFS_referrals_exit; + } + + /* collect necessary data from referrals */ + for (i = 0; i < *num_of_nodes; i++) { + char *temp; + int max_len; + struct dfs_info3_param *node = (*target_nodes)+i; + + node->flags = le32_to_cpu(rsp->DFSFlags); + if (is_unicode) { + __le16 *tmp = kmalloc(strlen(searchName)*2 + 2, + GFP_KERNEL); + if (tmp == NULL) { + rc = -ENOMEM; + goto parse_DFS_referrals_exit; + } + cifsConvertToUTF16((__le16 *) tmp, searchName, + PATH_MAX, nls_codepage, remap); + node->path_consumed = cifs_utf16_bytes(tmp, + le16_to_cpu(rsp->PathConsumed), + nls_codepage); + kfree(tmp); + } else + node->path_consumed = le16_to_cpu(rsp->PathConsumed); + + node->server_type = le16_to_cpu(ref->ServerType); + node->ref_flag = le16_to_cpu(ref->ReferralEntryFlags); + + /* copy DfsPath */ + temp = (char *)ref + le16_to_cpu(ref->DfsPathOffset); + max_len = data_end - temp; + node->path_name = cifs_strndup_from_utf16(temp, max_len, + is_unicode, nls_codepage); + if (!node->path_name) { + rc = -ENOMEM; + goto parse_DFS_referrals_exit; + } + + /* copy link target UNC */ + temp = (char *)ref + le16_to_cpu(ref->NetworkAddressOffset); + max_len = data_end - temp; + node->node_name = cifs_strndup_from_utf16(temp, max_len, + is_unicode, nls_codepage); + if (!node->node_name) { + rc = -ENOMEM; + goto parse_DFS_referrals_exit; + } + + node->ttl = le32_to_cpu(ref->TimeToLive); + + ref++; + } + +parse_DFS_referrals_exit: + if (rc) { + free_dfs_info_array(*target_nodes, *num_of_nodes); + *target_nodes = NULL; + *num_of_nodes = 0; + } + return rc; +} + +struct cifs_aio_ctx * +cifs_aio_ctx_alloc(void) +{ + struct cifs_aio_ctx *ctx; + + /* + * Must use kzalloc to initialize ctx->bv to NULL and ctx->direct_io + * to false so that we know when we have to unreference pages within + * cifs_aio_ctx_release() + */ + ctx = kzalloc(sizeof(struct cifs_aio_ctx), GFP_KERNEL); + if (!ctx) + return NULL; + + INIT_LIST_HEAD(&ctx->list); + mutex_init(&ctx->aio_mutex); + init_completion(&ctx->done); + kref_init(&ctx->refcount); + return ctx; +} + +void +cifs_aio_ctx_release(struct kref *refcount) +{ + struct cifs_aio_ctx *ctx = container_of(refcount, + struct cifs_aio_ctx, refcount); + + cifsFileInfo_put(ctx->cfile); + + /* + * ctx->bv is only set if setup_aio_ctx_iter() was call successfuly + * which means that iov_iter_get_pages() was a success and thus that + * we have taken reference on pages. + */ + if (ctx->bv) { + unsigned i; + + for (i = 0; i < ctx->npages; i++) { + if (ctx->should_dirty) + set_page_dirty(ctx->bv[i].bv_page); + put_page(ctx->bv[i].bv_page); + } + kvfree(ctx->bv); + } + + kfree(ctx); +} + +#define CIFS_AIO_KMALLOC_LIMIT (1024 * 1024) + +int +setup_aio_ctx_iter(struct cifs_aio_ctx *ctx, struct iov_iter *iter, int rw) +{ + ssize_t rc; + unsigned int cur_npages; + unsigned int npages = 0; + unsigned int i; + size_t len; + size_t count = iov_iter_count(iter); + unsigned int saved_len; + size_t start; + unsigned int max_pages = iov_iter_npages(iter, INT_MAX); + struct page **pages = NULL; + struct bio_vec *bv = NULL; + + if (iov_iter_is_kvec(iter)) { + memcpy(&ctx->iter, iter, sizeof(*iter)); + ctx->len = count; + iov_iter_advance(iter, count); + return 0; + } + + if (array_size(max_pages, sizeof(*bv)) <= CIFS_AIO_KMALLOC_LIMIT) + bv = kmalloc_array(max_pages, sizeof(*bv), GFP_KERNEL); + + if (!bv) { + bv = vmalloc(array_size(max_pages, sizeof(*bv))); + if (!bv) + return -ENOMEM; + } + + if (array_size(max_pages, sizeof(*pages)) <= CIFS_AIO_KMALLOC_LIMIT) + pages = kmalloc_array(max_pages, sizeof(*pages), GFP_KERNEL); + + if (!pages) { + pages = vmalloc(array_size(max_pages, sizeof(*pages))); + if (!pages) { + kvfree(bv); + return -ENOMEM; + } + } + + saved_len = count; + + while (count && npages < max_pages) { + rc = iov_iter_get_pages2(iter, pages, count, max_pages, &start); + if (rc < 0) { + cifs_dbg(VFS, "Couldn't get user pages (rc=%zd)\n", rc); + break; + } + + if (rc > count) { + cifs_dbg(VFS, "get pages rc=%zd more than %zu\n", rc, + count); + break; + } + + count -= rc; + rc += start; + cur_npages = DIV_ROUND_UP(rc, PAGE_SIZE); + + if (npages + cur_npages > max_pages) { + cifs_dbg(VFS, "out of vec array capacity (%u vs %u)\n", + npages + cur_npages, max_pages); + break; + } + + for (i = 0; i < cur_npages; i++) { + len = rc > PAGE_SIZE ? PAGE_SIZE : rc; + bv[npages + i].bv_page = pages[i]; + bv[npages + i].bv_offset = start; + bv[npages + i].bv_len = len - start; + rc -= len; + start = 0; + } + + npages += cur_npages; + } + + kvfree(pages); + ctx->bv = bv; + ctx->len = saved_len - count; + ctx->npages = npages; + iov_iter_bvec(&ctx->iter, rw, ctx->bv, npages, ctx->len); + return 0; +} + +/** + * cifs_alloc_hash - allocate hash and hash context together + * @name: The name of the crypto hash algo + * @sdesc: SHASH descriptor where to put the pointer to the hash TFM + * + * The caller has to make sure @sdesc is initialized to either NULL or + * a valid context. It can be freed via cifs_free_hash(). + */ +int +cifs_alloc_hash(const char *name, struct shash_desc **sdesc) +{ + int rc = 0; + struct crypto_shash *alg = NULL; + + if (*sdesc) + return 0; + + alg = crypto_alloc_shash(name, 0, 0); + if (IS_ERR(alg)) { + cifs_dbg(VFS, "Could not allocate shash TFM '%s'\n", name); + rc = PTR_ERR(alg); + *sdesc = NULL; + return rc; + } + + *sdesc = kmalloc(sizeof(struct shash_desc) + crypto_shash_descsize(alg), GFP_KERNEL); + if (*sdesc == NULL) { + cifs_dbg(VFS, "no memory left to allocate shash TFM '%s'\n", name); + crypto_free_shash(alg); + return -ENOMEM; + } + + (*sdesc)->tfm = alg; + return 0; +} + +/** + * cifs_free_hash - free hash and hash context together + * @sdesc: Where to find the pointer to the hash TFM + * + * Freeing a NULL descriptor is safe. + */ +void +cifs_free_hash(struct shash_desc **sdesc) +{ + if (unlikely(!sdesc) || !*sdesc) + return; + + if ((*sdesc)->tfm) { + crypto_free_shash((*sdesc)->tfm); + (*sdesc)->tfm = NULL; + } + + kfree_sensitive(*sdesc); + *sdesc = NULL; +} + +/** + * rqst_page_get_length - obtain the length and offset for a page in smb_rqst + * @rqst: The request descriptor + * @page: The index of the page to query + * @len: Where to store the length for this page: + * @offset: Where to store the offset for this page + */ +void rqst_page_get_length(const struct smb_rqst *rqst, unsigned int page, + unsigned int *len, unsigned int *offset) +{ + *len = rqst->rq_pagesz; + *offset = (page == 0) ? rqst->rq_offset : 0; + + if (rqst->rq_npages == 1 || page == rqst->rq_npages-1) + *len = rqst->rq_tailsz; + else if (page == 0) + *len = rqst->rq_pagesz - rqst->rq_offset; +} + +void extract_unc_hostname(const char *unc, const char **h, size_t *len) +{ + const char *end; + + /* skip initial slashes */ + while (*unc && (*unc == '\\' || *unc == '/')) + unc++; + + end = unc; + + while (*end && !(*end == '\\' || *end == '/')) + end++; + + *h = unc; + *len = end - unc; +} + +/** + * copy_path_name - copy src path to dst, possibly truncating + * @dst: The destination buffer + * @src: The source name + * + * returns number of bytes written (including trailing nul) + */ +int copy_path_name(char *dst, const char *src) +{ + int name_len; + + /* + * PATH_MAX includes nul, so if strlen(src) >= PATH_MAX it + * will truncate and strlen(dst) will be PATH_MAX-1 + */ + name_len = strscpy(dst, src, PATH_MAX); + if (WARN_ON_ONCE(name_len < 0)) + name_len = PATH_MAX-1; + + /* we count the trailing nul */ + name_len++; + return name_len; +} + +struct super_cb_data { + void *data; + struct super_block *sb; +}; + +static void tcp_super_cb(struct super_block *sb, void *arg) +{ + struct super_cb_data *sd = arg; + struct TCP_Server_Info *server = sd->data; + struct cifs_sb_info *cifs_sb; + struct cifs_tcon *tcon; + + if (sd->sb) + return; + + cifs_sb = CIFS_SB(sb); + tcon = cifs_sb_master_tcon(cifs_sb); + if (tcon->ses->server == server) + sd->sb = sb; +} + +static struct super_block *__cifs_get_super(void (*f)(struct super_block *, void *), + void *data) +{ + struct super_cb_data sd = { + .data = data, + .sb = NULL, + }; + struct file_system_type **fs_type = (struct file_system_type *[]) { + &cifs_fs_type, &smb3_fs_type, NULL, + }; + + for (; *fs_type; fs_type++) { + iterate_supers_type(*fs_type, f, &sd); + if (sd.sb) { + /* + * Grab an active reference in order to prevent automounts (DFS links) + * of expiring and then freeing up our cifs superblock pointer while + * we're doing failover. + */ + cifs_sb_active(sd.sb); + return sd.sb; + } + } + return ERR_PTR(-EINVAL); +} + +static void __cifs_put_super(struct super_block *sb) +{ + if (!IS_ERR_OR_NULL(sb)) + cifs_sb_deactive(sb); +} + +struct super_block *cifs_get_tcp_super(struct TCP_Server_Info *server) +{ + return __cifs_get_super(tcp_super_cb, server); +} + +void cifs_put_tcp_super(struct super_block *sb) +{ + __cifs_put_super(sb); +} + +#ifdef CONFIG_CIFS_DFS_UPCALL +int match_target_ip(struct TCP_Server_Info *server, + const char *share, size_t share_len, + bool *result) +{ + int rc; + char *target, *tip = NULL; + struct sockaddr tipaddr; + + *result = false; + + target = kzalloc(share_len + 3, GFP_KERNEL); + if (!target) { + rc = -ENOMEM; + goto out; + } + + scnprintf(target, share_len + 3, "\\\\%.*s", (int)share_len, share); + + cifs_dbg(FYI, "%s: target name: %s\n", __func__, target + 2); + + rc = dns_resolve_server_name_to_ip(target, &tip, NULL); + if (rc < 0) + goto out; + + cifs_dbg(FYI, "%s: target ip: %s\n", __func__, tip); + + if (!cifs_convert_address(&tipaddr, tip, strlen(tip))) { + cifs_dbg(VFS, "%s: failed to convert target ip address\n", + __func__); + rc = -EINVAL; + goto out; + } + + *result = cifs_match_ipaddr((struct sockaddr *)&server->dstaddr, + &tipaddr); + cifs_dbg(FYI, "%s: ip addresses match: %u\n", __func__, *result); + rc = 0; + +out: + kfree(target); + kfree(tip); + + return rc; +} + +int cifs_update_super_prepath(struct cifs_sb_info *cifs_sb, char *prefix) +{ + kfree(cifs_sb->prepath); + + if (prefix && *prefix) { + cifs_sb->prepath = cifs_sanitize_prepath(prefix, GFP_ATOMIC); + if (!cifs_sb->prepath) + return -ENOMEM; + + convert_delimiter(cifs_sb->prepath, CIFS_DIR_SEP(cifs_sb)); + } else + cifs_sb->prepath = NULL; + + cifs_sb->mnt_cifs_flags |= CIFS_MOUNT_USE_PREFIX_PATH; + return 0; +} + +/* + * Handle weird Windows SMB server behaviour. It responds with + * STATUS_OBJECT_NAME_INVALID code to SMB2 QUERY_INFO request for + * "\\\" DFS reference, where contains + * non-ASCII unicode symbols. + */ +int cifs_inval_name_dfs_link_error(const unsigned int xid, + struct cifs_tcon *tcon, + struct cifs_sb_info *cifs_sb, + const char *full_path, + bool *islink) +{ + struct cifs_ses *ses = tcon->ses; + size_t len; + char *path; + char *ref_path; + + *islink = false; + + /* + * Fast path - skip check when @full_path doesn't have a prefix path to + * look up or tcon is not DFS. + */ + if (strlen(full_path) < 2 || !cifs_sb || + (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_NO_DFS) || + !is_tcon_dfs(tcon) || !ses->server->origin_fullpath) + return 0; + + /* + * Slow path - tcon is DFS and @full_path has prefix path, so attempt + * to get a referral to figure out whether it is an DFS link. + */ + len = strnlen(tcon->tree_name, MAX_TREE_SIZE + 1) + strlen(full_path) + 1; + path = kmalloc(len, GFP_KERNEL); + if (!path) + return -ENOMEM; + + scnprintf(path, len, "%s%s", tcon->tree_name, full_path); + ref_path = dfs_cache_canonical_path(path + 1, cifs_sb->local_nls, + cifs_remap(cifs_sb)); + kfree(path); + + if (IS_ERR(ref_path)) { + if (PTR_ERR(ref_path) != -EINVAL) + return PTR_ERR(ref_path); + } else { + struct dfs_info3_param *refs = NULL; + int num_refs = 0; + + /* + * XXX: we are not using dfs_cache_find() here because we might + * end filling all the DFS cache and thus potentially + * removing cached DFS targets that the client would eventually + * need during failover. + */ + if (ses->server->ops->get_dfs_refer && + !ses->server->ops->get_dfs_refer(xid, ses, ref_path, &refs, + &num_refs, cifs_sb->local_nls, + cifs_remap(cifs_sb))) + *islink = refs[0].server_type == DFS_TYPE_LINK; + free_dfs_info_array(refs, num_refs); + kfree(ref_path); + } + return 0; +} +#endif + +int cifs_wait_for_server_reconnect(struct TCP_Server_Info *server, bool retry) +{ + int timeout = 10; + int rc; + + spin_lock(&server->srv_lock); + if (server->tcpStatus != CifsNeedReconnect) { + spin_unlock(&server->srv_lock); + return 0; + } + timeout *= server->nr_targets; + spin_unlock(&server->srv_lock); + + /* + * Give demultiplex thread up to 10 seconds to each target available for + * reconnect -- should be greater than cifs socket timeout which is 7 + * seconds. + * + * On "soft" mounts we wait once. Hard mounts keep retrying until + * process is killed or server comes back on-line. + */ + do { + rc = wait_event_interruptible_timeout(server->response_q, + (server->tcpStatus != CifsNeedReconnect), + timeout * HZ); + if (rc < 0) { + cifs_dbg(FYI, "%s: aborting reconnect due to received signal\n", + __func__); + return -ERESTARTSYS; + } + + /* are we still trying to reconnect? */ + spin_lock(&server->srv_lock); + if (server->tcpStatus != CifsNeedReconnect) { + spin_unlock(&server->srv_lock); + return 0; + } + spin_unlock(&server->srv_lock); + } while (retry); + + cifs_dbg(FYI, "%s: gave up waiting on reconnect\n", __func__); + return -EHOSTDOWN; +} diff --git a/fs/smb/client/netlink.c b/fs/smb/client/netlink.c new file mode 100644 index 0000000000000..147d9409252cd --- /dev/null +++ b/fs/smb/client/netlink.c @@ -0,0 +1,90 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Netlink routines for CIFS + * + * Copyright (c) 2020 Samuel Cabrero + */ + +#include +#include + +#include "netlink.h" +#include "cifsglob.h" +#include "cifs_debug.h" +#include "cifs_swn.h" + +static const struct nla_policy cifs_genl_policy[CIFS_GENL_ATTR_MAX + 1] = { + [CIFS_GENL_ATTR_SWN_REGISTRATION_ID] = { .type = NLA_U32 }, + [CIFS_GENL_ATTR_SWN_NET_NAME] = { .type = NLA_STRING }, + [CIFS_GENL_ATTR_SWN_SHARE_NAME] = { .type = NLA_STRING }, + [CIFS_GENL_ATTR_SWN_IP] = { .len = sizeof(struct sockaddr_storage) }, + [CIFS_GENL_ATTR_SWN_NET_NAME_NOTIFY] = { .type = NLA_FLAG }, + [CIFS_GENL_ATTR_SWN_SHARE_NAME_NOTIFY] = { .type = NLA_FLAG }, + [CIFS_GENL_ATTR_SWN_IP_NOTIFY] = { .type = NLA_FLAG }, + [CIFS_GENL_ATTR_SWN_KRB_AUTH] = { .type = NLA_FLAG }, + [CIFS_GENL_ATTR_SWN_USER_NAME] = { .type = NLA_STRING }, + [CIFS_GENL_ATTR_SWN_PASSWORD] = { .type = NLA_STRING }, + [CIFS_GENL_ATTR_SWN_DOMAIN_NAME] = { .type = NLA_STRING }, + [CIFS_GENL_ATTR_SWN_NOTIFICATION_TYPE] = { .type = NLA_U32 }, + [CIFS_GENL_ATTR_SWN_RESOURCE_STATE] = { .type = NLA_U32 }, + [CIFS_GENL_ATTR_SWN_RESOURCE_NAME] = { .type = NLA_STRING}, +}; + +static const struct genl_ops cifs_genl_ops[] = { + { + .cmd = CIFS_GENL_CMD_SWN_NOTIFY, + .validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP, + .doit = cifs_swn_notify, + }, +}; + +static const struct genl_multicast_group cifs_genl_mcgrps[] = { + [CIFS_GENL_MCGRP_SWN] = { .name = CIFS_GENL_MCGRP_SWN_NAME }, +}; + +struct genl_family cifs_genl_family = { + .name = CIFS_GENL_NAME, + .version = CIFS_GENL_VERSION, + .hdrsize = 0, + .maxattr = CIFS_GENL_ATTR_MAX, + .module = THIS_MODULE, + .policy = cifs_genl_policy, + .ops = cifs_genl_ops, + .n_ops = ARRAY_SIZE(cifs_genl_ops), + .resv_start_op = CIFS_GENL_CMD_SWN_NOTIFY + 1, + .mcgrps = cifs_genl_mcgrps, + .n_mcgrps = ARRAY_SIZE(cifs_genl_mcgrps), +}; + +/** + * cifs_genl_init - Register generic netlink family + * + * Return zero if initialized successfully, otherwise non-zero. + */ +int cifs_genl_init(void) +{ + int ret; + + ret = genl_register_family(&cifs_genl_family); + if (ret < 0) { + cifs_dbg(VFS, "%s: failed to register netlink family\n", + __func__); + return ret; + } + + return 0; +} + +/** + * cifs_genl_exit - Unregister generic netlink family + */ +void cifs_genl_exit(void) +{ + int ret; + + ret = genl_unregister_family(&cifs_genl_family); + if (ret < 0) { + cifs_dbg(VFS, "%s: failed to unregister netlink family\n", + __func__); + } +} diff --git a/fs/smb/client/netlink.h b/fs/smb/client/netlink.h new file mode 100644 index 0000000000000..e2fa8ed24c546 --- /dev/null +++ b/fs/smb/client/netlink.h @@ -0,0 +1,16 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * Netlink routines for CIFS + * + * Copyright (c) 2020 Samuel Cabrero + */ + +#ifndef _CIFS_NETLINK_H +#define _CIFS_NETLINK_H + +extern struct genl_family cifs_genl_family; + +extern int cifs_genl_init(void); +extern void cifs_genl_exit(void); + +#endif /* _CIFS_NETLINK_H */ diff --git a/fs/smb/client/netmisc.c b/fs/smb/client/netmisc.c new file mode 100644 index 0000000000000..1b52e6ac431cb --- /dev/null +++ b/fs/smb/client/netmisc.c @@ -0,0 +1,1021 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * + * Copyright (c) International Business Machines Corp., 2002,2008 + * Author(s): Steve French (sfrench@us.ibm.com) + * + * Error mapping routines from Samba libsmb/errormap.c + * Copyright (C) Andrew Tridgell 2001 + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include "cifsfs.h" +#include "cifspdu.h" +#include "cifsglob.h" +#include "cifsproto.h" +#include "smberr.h" +#include "cifs_debug.h" +#include "nterr.h" + +struct smb_to_posix_error { + __u16 smb_err; + int posix_code; +}; + +static const struct smb_to_posix_error mapping_table_ERRDOS[] = { + {ERRbadfunc, -EINVAL}, + {ERRbadfile, -ENOENT}, + {ERRbadpath, -ENOTDIR}, + {ERRnofids, -EMFILE}, + {ERRnoaccess, -EACCES}, + {ERRbadfid, -EBADF}, + {ERRbadmcb, -EIO}, + {ERRnomem, -EREMOTEIO}, + {ERRbadmem, -EFAULT}, + {ERRbadenv, -EFAULT}, + {ERRbadformat, -EINVAL}, + {ERRbadaccess, -EACCES}, + {ERRbaddata, -EIO}, + {ERRbaddrive, -ENXIO}, + {ERRremcd, -EACCES}, + {ERRdiffdevice, -EXDEV}, + {ERRnofiles, -ENOENT}, + {ERRwriteprot, -EROFS}, + {ERRbadshare, -EBUSY}, + {ERRlock, -EACCES}, + {ERRunsup, -EINVAL}, + {ERRnosuchshare, -ENXIO}, + {ERRfilexists, -EEXIST}, + {ERRinvparm, -EINVAL}, + {ERRdiskfull, -ENOSPC}, + {ERRinvname, -ENOENT}, + {ERRinvlevel, -EOPNOTSUPP}, + {ERRdirnotempty, -ENOTEMPTY}, + {ERRnotlocked, -ENOLCK}, + {ERRcancelviolation, -ENOLCK}, + {ERRalreadyexists, -EEXIST}, + {ERRmoredata, -EOVERFLOW}, + {ERReasnotsupported, -EOPNOTSUPP}, + {ErrQuota, -EDQUOT}, + {ErrNotALink, -ENOLINK}, + {ERRnetlogonNotStarted, -ENOPROTOOPT}, + {ERRsymlink, -EOPNOTSUPP}, + {ErrTooManyLinks, -EMLINK}, + {0, 0} +}; + +static const struct smb_to_posix_error mapping_table_ERRSRV[] = { + {ERRerror, -EIO}, + {ERRbadpw, -EACCES}, /* was EPERM */ + {ERRbadtype, -EREMOTE}, + {ERRaccess, -EACCES}, + {ERRinvtid, -ENXIO}, + {ERRinvnetname, -ENXIO}, + {ERRinvdevice, -ENXIO}, + {ERRqfull, -ENOSPC}, + {ERRqtoobig, -ENOSPC}, + {ERRqeof, -EIO}, + {ERRinvpfid, -EBADF}, + {ERRsmbcmd, -EBADRQC}, + {ERRsrverror, -EIO}, + {ERRbadBID, -EIO}, + {ERRfilespecs, -EINVAL}, + {ERRbadLink, -EIO}, + {ERRbadpermits, -EINVAL}, + {ERRbadPID, -ESRCH}, + {ERRsetattrmode, -EINVAL}, + {ERRpaused, -EHOSTDOWN}, + {ERRmsgoff, -EHOSTDOWN}, + {ERRnoroom, -ENOSPC}, + {ERRrmuns, -EUSERS}, + {ERRtimeout, -ETIME}, + {ERRnoresource, -EREMOTEIO}, + {ERRtoomanyuids, -EUSERS}, + {ERRbaduid, -EACCES}, + {ERRusempx, -EIO}, + {ERRusestd, -EIO}, + {ERR_NOTIFY_ENUM_DIR, -ENOBUFS}, + {ERRnoSuchUser, -EACCES}, +/* {ERRaccountexpired, -EACCES}, + {ERRbadclient, -EACCES}, + {ERRbadLogonTime, -EACCES}, + {ERRpasswordExpired, -EACCES},*/ + {ERRaccountexpired, -EKEYEXPIRED}, + {ERRbadclient, -EACCES}, + {ERRbadLogonTime, -EACCES}, + {ERRpasswordExpired, -EKEYEXPIRED}, + + {ERRnosupport, -EINVAL}, + {0, 0} +}; + +/* + * Convert a string containing text IPv4 or IPv6 address to binary form. + * + * Returns 0 on failure. + */ +static int +cifs_inet_pton(const int address_family, const char *cp, int len, void *dst) +{ + int ret = 0; + + /* calculate length by finding first slash or NULL */ + if (address_family == AF_INET) + ret = in4_pton(cp, len, dst, '\\', NULL); + else if (address_family == AF_INET6) + ret = in6_pton(cp, len, dst , '\\', NULL); + + cifs_dbg(NOISY, "address conversion returned %d for %*.*s\n", + ret, len, len, cp); + if (ret > 0) + ret = 1; + return ret; +} + +/* + * Try to convert a string to an IPv4 address and then attempt to convert + * it to an IPv6 address if that fails. Set the family field if either + * succeeds. If it's an IPv6 address and it has a '%' sign in it, try to + * treat the part following it as a numeric sin6_scope_id. + * + * Returns 0 on failure. + */ +int +cifs_convert_address(struct sockaddr *dst, const char *src, int len) +{ + int rc, alen, slen; + const char *pct; + char scope_id[13]; + struct sockaddr_in *s4 = (struct sockaddr_in *) dst; + struct sockaddr_in6 *s6 = (struct sockaddr_in6 *) dst; + + /* IPv4 address */ + if (cifs_inet_pton(AF_INET, src, len, &s4->sin_addr.s_addr)) { + s4->sin_family = AF_INET; + return 1; + } + + /* attempt to exclude the scope ID from the address part */ + pct = memchr(src, '%', len); + alen = pct ? pct - src : len; + + rc = cifs_inet_pton(AF_INET6, src, alen, &s6->sin6_addr.s6_addr); + if (!rc) + return rc; + + s6->sin6_family = AF_INET6; + if (pct) { + /* grab the scope ID */ + slen = len - (alen + 1); + if (slen <= 0 || slen > 12) + return 0; + memcpy(scope_id, pct + 1, slen); + scope_id[slen] = '\0'; + + rc = kstrtouint(scope_id, 0, &s6->sin6_scope_id); + rc = (rc == 0) ? 1 : 0; + } + + return rc; +} + +void +cifs_set_port(struct sockaddr *addr, const unsigned short int port) +{ + switch (addr->sa_family) { + case AF_INET: + ((struct sockaddr_in *)addr)->sin_port = htons(port); + break; + case AF_INET6: + ((struct sockaddr_in6 *)addr)->sin6_port = htons(port); + break; + } +} + +/***************************************************************************** +convert a NT status code to a dos class/code + *****************************************************************************/ +/* NT status -> dos error map */ +static const struct { + __u8 dos_class; + __u16 dos_code; + __u32 ntstatus; +} ntstatus_to_dos_map[] = { + { + ERRDOS, ERRgeneral, NT_STATUS_UNSUCCESSFUL}, { + ERRDOS, ERRbadfunc, NT_STATUS_NOT_IMPLEMENTED}, { + ERRDOS, ERRinvlevel, NT_STATUS_INVALID_INFO_CLASS}, { + ERRDOS, 24, NT_STATUS_INFO_LENGTH_MISMATCH}, { + ERRHRD, ERRgeneral, NT_STATUS_ACCESS_VIOLATION}, { + ERRHRD, ERRgeneral, NT_STATUS_IN_PAGE_ERROR}, { + ERRHRD, ERRgeneral, NT_STATUS_PAGEFILE_QUOTA}, { + ERRDOS, ERRbadfid, NT_STATUS_INVALID_HANDLE}, { + ERRHRD, ERRgeneral, NT_STATUS_BAD_INITIAL_STACK}, { + ERRDOS, 193, NT_STATUS_BAD_INITIAL_PC}, { + ERRDOS, 87, NT_STATUS_INVALID_CID}, { + ERRHRD, ERRgeneral, NT_STATUS_TIMER_NOT_CANCELED}, { + ERRDOS, 87, NT_STATUS_INVALID_PARAMETER}, { + ERRDOS, ERRbadfile, NT_STATUS_NO_SUCH_DEVICE}, { + ERRDOS, ERRbadfile, NT_STATUS_NO_SUCH_FILE}, { + ERRDOS, ERRbadfunc, NT_STATUS_INVALID_DEVICE_REQUEST}, { + ERRDOS, 38, NT_STATUS_END_OF_FILE}, { + ERRDOS, 34, NT_STATUS_WRONG_VOLUME}, { + ERRDOS, 21, NT_STATUS_NO_MEDIA_IN_DEVICE}, { + ERRHRD, ERRgeneral, NT_STATUS_UNRECOGNIZED_MEDIA}, { + ERRDOS, 27, NT_STATUS_NONEXISTENT_SECTOR}, +/* { This NT error code was 'sqashed' + from NT_STATUS_MORE_PROCESSING_REQUIRED to NT_STATUS_OK + during the session setup } */ + { + ERRDOS, ERRnomem, NT_STATUS_NO_MEMORY}, { + ERRDOS, 487, NT_STATUS_CONFLICTING_ADDRESSES}, { + ERRDOS, 487, NT_STATUS_NOT_MAPPED_VIEW}, { + ERRDOS, 87, NT_STATUS_UNABLE_TO_FREE_VM}, { + ERRDOS, 87, NT_STATUS_UNABLE_TO_DELETE_SECTION}, { + ERRDOS, 2142, NT_STATUS_INVALID_SYSTEM_SERVICE}, { + ERRHRD, ERRgeneral, NT_STATUS_ILLEGAL_INSTRUCTION}, { + ERRDOS, ERRnoaccess, NT_STATUS_INVALID_LOCK_SEQUENCE}, { + ERRDOS, ERRnoaccess, NT_STATUS_INVALID_VIEW_SIZE}, { + ERRDOS, 193, NT_STATUS_INVALID_FILE_FOR_SECTION}, { + ERRDOS, ERRnoaccess, NT_STATUS_ALREADY_COMMITTED}, +/* { This NT error code was 'sqashed' + from NT_STATUS_ACCESS_DENIED to NT_STATUS_TRUSTED_RELATIONSHIP_FAILURE + during the session setup } */ + { + ERRDOS, ERRnoaccess, NT_STATUS_ACCESS_DENIED}, { + ERRDOS, 111, NT_STATUS_BUFFER_TOO_SMALL}, { + ERRDOS, ERRbadfid, NT_STATUS_OBJECT_TYPE_MISMATCH}, { + ERRHRD, ERRgeneral, NT_STATUS_NONCONTINUABLE_EXCEPTION}, { + ERRHRD, ERRgeneral, NT_STATUS_INVALID_DISPOSITION}, { + ERRHRD, ERRgeneral, NT_STATUS_UNWIND}, { + ERRHRD, ERRgeneral, NT_STATUS_BAD_STACK}, { + ERRHRD, ERRgeneral, NT_STATUS_INVALID_UNWIND_TARGET}, { + ERRDOS, 158, NT_STATUS_NOT_LOCKED}, { + ERRHRD, ERRgeneral, NT_STATUS_PARITY_ERROR}, { + ERRDOS, 487, NT_STATUS_UNABLE_TO_DECOMMIT_VM}, { + ERRDOS, 487, NT_STATUS_NOT_COMMITTED}, { + ERRHRD, ERRgeneral, NT_STATUS_INVALID_PORT_ATTRIBUTES}, { + ERRHRD, ERRgeneral, NT_STATUS_PORT_MESSAGE_TOO_LONG}, { + ERRDOS, 87, NT_STATUS_INVALID_PARAMETER_MIX}, { + ERRHRD, ERRgeneral, NT_STATUS_INVALID_QUOTA_LOWER}, { + ERRHRD, ERRgeneral, NT_STATUS_DISK_CORRUPT_ERROR}, { + /* mapping changed since shell does lookup on * expects FileNotFound */ + ERRDOS, ERRbadfile, NT_STATUS_OBJECT_NAME_INVALID}, { + ERRDOS, ERRbadfile, NT_STATUS_OBJECT_NAME_NOT_FOUND}, { + ERRDOS, ERRalreadyexists, NT_STATUS_OBJECT_NAME_COLLISION}, { + ERRHRD, ERRgeneral, NT_STATUS_HANDLE_NOT_WAITABLE}, { + ERRDOS, ERRbadfid, NT_STATUS_PORT_DISCONNECTED}, { + ERRHRD, ERRgeneral, NT_STATUS_DEVICE_ALREADY_ATTACHED}, { + ERRDOS, 161, NT_STATUS_OBJECT_PATH_INVALID}, { + ERRDOS, ERRbadpath, NT_STATUS_OBJECT_PATH_NOT_FOUND}, { + ERRDOS, 161, NT_STATUS_OBJECT_PATH_SYNTAX_BAD}, { + ERRHRD, ERRgeneral, NT_STATUS_DATA_OVERRUN}, { + ERRHRD, ERRgeneral, NT_STATUS_DATA_LATE_ERROR}, { + ERRDOS, 23, NT_STATUS_DATA_ERROR}, { + ERRDOS, 23, NT_STATUS_CRC_ERROR}, { + ERRDOS, ERRnomem, NT_STATUS_SECTION_TOO_BIG}, { + ERRDOS, ERRnoaccess, NT_STATUS_PORT_CONNECTION_REFUSED}, { + ERRDOS, ERRbadfid, NT_STATUS_INVALID_PORT_HANDLE}, { + ERRDOS, ERRbadshare, NT_STATUS_SHARING_VIOLATION}, { + ERRHRD, ERRgeneral, NT_STATUS_QUOTA_EXCEEDED}, { + ERRDOS, 87, NT_STATUS_INVALID_PAGE_PROTECTION}, { + ERRDOS, 288, NT_STATUS_MUTANT_NOT_OWNED}, { + ERRDOS, 298, NT_STATUS_SEMAPHORE_LIMIT_EXCEEDED}, { + ERRDOS, 87, NT_STATUS_PORT_ALREADY_SET}, { + ERRDOS, 87, NT_STATUS_SECTION_NOT_IMAGE}, { + ERRDOS, 156, NT_STATUS_SUSPEND_COUNT_EXCEEDED}, { + ERRDOS, ERRnoaccess, NT_STATUS_THREAD_IS_TERMINATING}, { + ERRDOS, 87, NT_STATUS_BAD_WORKING_SET_LIMIT}, { + ERRDOS, 87, NT_STATUS_INCOMPATIBLE_FILE_MAP}, { + ERRDOS, 87, NT_STATUS_SECTION_PROTECTION}, { + ERRDOS, ERReasnotsupported, NT_STATUS_EAS_NOT_SUPPORTED}, { + ERRDOS, 255, NT_STATUS_EA_TOO_LARGE}, { + ERRHRD, ERRgeneral, NT_STATUS_NONEXISTENT_EA_ENTRY}, { + ERRHRD, ERRgeneral, NT_STATUS_NO_EAS_ON_FILE}, { + ERRHRD, ERRgeneral, NT_STATUS_EA_CORRUPT_ERROR}, { + ERRDOS, ERRlock, NT_STATUS_FILE_LOCK_CONFLICT}, { + ERRDOS, ERRlock, NT_STATUS_LOCK_NOT_GRANTED}, { + ERRDOS, ERRbadfile, NT_STATUS_DELETE_PENDING}, { + ERRDOS, ERRunsup, NT_STATUS_CTL_FILE_NOT_SUPPORTED}, { + ERRHRD, ERRgeneral, NT_STATUS_UNKNOWN_REVISION}, { + ERRHRD, ERRgeneral, NT_STATUS_REVISION_MISMATCH}, { + ERRHRD, ERRgeneral, NT_STATUS_INVALID_OWNER}, { + ERRHRD, ERRgeneral, NT_STATUS_INVALID_PRIMARY_GROUP}, { + ERRHRD, ERRgeneral, NT_STATUS_NO_IMPERSONATION_TOKEN}, { + ERRHRD, ERRgeneral, NT_STATUS_CANT_DISABLE_MANDATORY}, { + ERRDOS, 2215, NT_STATUS_NO_LOGON_SERVERS}, { + ERRHRD, ERRgeneral, NT_STATUS_NO_SUCH_LOGON_SESSION}, { + ERRHRD, ERRgeneral, NT_STATUS_NO_SUCH_PRIVILEGE}, { + ERRDOS, ERRnoaccess, NT_STATUS_PRIVILEGE_NOT_HELD}, { + ERRHRD, ERRgeneral, NT_STATUS_INVALID_ACCOUNT_NAME}, { + ERRHRD, ERRgeneral, NT_STATUS_USER_EXISTS}, +/* { This NT error code was 'sqashed' + from NT_STATUS_NO_SUCH_USER to NT_STATUS_LOGON_FAILURE + during the session setup } */ + { + ERRDOS, ERRnoaccess, NT_STATUS_NO_SUCH_USER}, { /* could map to 2238 */ + ERRHRD, ERRgeneral, NT_STATUS_GROUP_EXISTS}, { + ERRHRD, ERRgeneral, NT_STATUS_NO_SUCH_GROUP}, { + ERRHRD, ERRgeneral, NT_STATUS_MEMBER_IN_GROUP}, { + ERRHRD, ERRgeneral, NT_STATUS_MEMBER_NOT_IN_GROUP}, { + ERRHRD, ERRgeneral, NT_STATUS_LAST_ADMIN}, +/* { This NT error code was 'sqashed' + from NT_STATUS_WRONG_PASSWORD to NT_STATUS_LOGON_FAILURE + during the session setup } */ + { + ERRSRV, ERRbadpw, NT_STATUS_WRONG_PASSWORD}, { + ERRHRD, ERRgeneral, NT_STATUS_ILL_FORMED_PASSWORD}, { + ERRHRD, ERRgeneral, NT_STATUS_PASSWORD_RESTRICTION}, { + ERRDOS, ERRnoaccess, NT_STATUS_LOGON_FAILURE}, { + ERRHRD, ERRgeneral, NT_STATUS_ACCOUNT_RESTRICTION}, { + ERRSRV, ERRbadLogonTime, NT_STATUS_INVALID_LOGON_HOURS}, { + ERRSRV, ERRbadclient, NT_STATUS_INVALID_WORKSTATION}, { + ERRSRV, ERRpasswordExpired, NT_STATUS_PASSWORD_EXPIRED}, { + ERRSRV, ERRaccountexpired, NT_STATUS_ACCOUNT_DISABLED}, { + ERRHRD, ERRgeneral, NT_STATUS_NONE_MAPPED}, { + ERRHRD, ERRgeneral, NT_STATUS_TOO_MANY_LUIDS_REQUESTED}, { + ERRHRD, ERRgeneral, NT_STATUS_LUIDS_EXHAUSTED}, { + ERRHRD, ERRgeneral, NT_STATUS_INVALID_SUB_AUTHORITY}, { + ERRHRD, ERRgeneral, NT_STATUS_INVALID_ACL}, { + ERRHRD, ERRgeneral, NT_STATUS_INVALID_SID}, { + ERRHRD, ERRgeneral, NT_STATUS_INVALID_SECURITY_DESCR}, { + ERRDOS, 127, NT_STATUS_PROCEDURE_NOT_FOUND}, { + ERRDOS, 193, NT_STATUS_INVALID_IMAGE_FORMAT}, { + ERRHRD, ERRgeneral, NT_STATUS_NO_TOKEN}, { + ERRHRD, ERRgeneral, NT_STATUS_BAD_INHERITANCE_ACL}, { + ERRDOS, 158, NT_STATUS_RANGE_NOT_LOCKED}, { + ERRDOS, 112, NT_STATUS_DISK_FULL}, { + ERRHRD, ERRgeneral, NT_STATUS_SERVER_DISABLED}, { + ERRHRD, ERRgeneral, NT_STATUS_SERVER_NOT_DISABLED}, { + ERRDOS, 68, NT_STATUS_TOO_MANY_GUIDS_REQUESTED}, { + ERRDOS, 259, NT_STATUS_GUIDS_EXHAUSTED}, { + ERRHRD, ERRgeneral, NT_STATUS_INVALID_ID_AUTHORITY}, { + ERRDOS, 259, NT_STATUS_AGENTS_EXHAUSTED}, { + ERRDOS, 154, NT_STATUS_INVALID_VOLUME_LABEL}, { + ERRDOS, 14, NT_STATUS_SECTION_NOT_EXTENDED}, { + ERRDOS, 487, NT_STATUS_NOT_MAPPED_DATA}, { + ERRHRD, ERRgeneral, NT_STATUS_RESOURCE_DATA_NOT_FOUND}, { + ERRHRD, ERRgeneral, NT_STATUS_RESOURCE_TYPE_NOT_FOUND}, { + ERRHRD, ERRgeneral, NT_STATUS_RESOURCE_NAME_NOT_FOUND}, { + ERRHRD, ERRgeneral, NT_STATUS_ARRAY_BOUNDS_EXCEEDED}, { + ERRHRD, ERRgeneral, NT_STATUS_FLOAT_DENORMAL_OPERAND}, { + ERRHRD, ERRgeneral, NT_STATUS_FLOAT_DIVIDE_BY_ZERO}, { + ERRHRD, ERRgeneral, NT_STATUS_FLOAT_INEXACT_RESULT}, { + ERRHRD, ERRgeneral, NT_STATUS_FLOAT_INVALID_OPERATION}, { + ERRHRD, ERRgeneral, NT_STATUS_FLOAT_OVERFLOW}, { + ERRHRD, ERRgeneral, NT_STATUS_FLOAT_STACK_CHECK}, { + ERRHRD, ERRgeneral, NT_STATUS_FLOAT_UNDERFLOW}, { + ERRHRD, ERRgeneral, NT_STATUS_INTEGER_DIVIDE_BY_ZERO}, { + ERRDOS, 534, NT_STATUS_INTEGER_OVERFLOW}, { + ERRHRD, ERRgeneral, NT_STATUS_PRIVILEGED_INSTRUCTION}, { + ERRDOS, ERRnomem, NT_STATUS_TOO_MANY_PAGING_FILES}, { + ERRHRD, ERRgeneral, NT_STATUS_FILE_INVALID}, { + ERRHRD, ERRgeneral, NT_STATUS_ALLOTTED_SPACE_EXCEEDED}, +/* { This NT error code was 'sqashed' + from NT_STATUS_INSUFFICIENT_RESOURCES to + NT_STATUS_INSUFF_SERVER_RESOURCES during the session setup } */ + { + ERRDOS, ERRnoresource, NT_STATUS_INSUFFICIENT_RESOURCES}, { + ERRDOS, ERRbadpath, NT_STATUS_DFS_EXIT_PATH_FOUND}, { + ERRDOS, 23, NT_STATUS_DEVICE_DATA_ERROR}, { + ERRHRD, ERRgeneral, NT_STATUS_DEVICE_NOT_CONNECTED}, { + ERRDOS, 21, NT_STATUS_DEVICE_POWER_FAILURE}, { + ERRDOS, 487, NT_STATUS_FREE_VM_NOT_AT_BASE}, { + ERRDOS, 487, NT_STATUS_MEMORY_NOT_ALLOCATED}, { + ERRHRD, ERRgeneral, NT_STATUS_WORKING_SET_QUOTA}, { + ERRDOS, 19, NT_STATUS_MEDIA_WRITE_PROTECTED}, { + ERRDOS, 21, NT_STATUS_DEVICE_NOT_READY}, { + ERRHRD, ERRgeneral, NT_STATUS_INVALID_GROUP_ATTRIBUTES}, { + ERRHRD, ERRgeneral, NT_STATUS_BAD_IMPERSONATION_LEVEL}, { + ERRHRD, ERRgeneral, NT_STATUS_CANT_OPEN_ANONYMOUS}, { + ERRHRD, ERRgeneral, NT_STATUS_BAD_VALIDATION_CLASS}, { + ERRHRD, ERRgeneral, NT_STATUS_BAD_TOKEN_TYPE}, { + ERRDOS, 87, NT_STATUS_BAD_MASTER_BOOT_RECORD}, { + ERRHRD, ERRgeneral, NT_STATUS_INSTRUCTION_MISALIGNMENT}, { + ERRDOS, ERRpipebusy, NT_STATUS_INSTANCE_NOT_AVAILABLE}, { + ERRDOS, ERRpipebusy, NT_STATUS_PIPE_NOT_AVAILABLE}, { + ERRDOS, ERRbadpipe, NT_STATUS_INVALID_PIPE_STATE}, { + ERRDOS, ERRpipebusy, NT_STATUS_PIPE_BUSY}, { + ERRDOS, ERRbadfunc, NT_STATUS_ILLEGAL_FUNCTION}, { + ERRDOS, ERRnotconnected, NT_STATUS_PIPE_DISCONNECTED}, { + ERRDOS, ERRpipeclosing, NT_STATUS_PIPE_CLOSING}, { + ERRHRD, ERRgeneral, NT_STATUS_PIPE_CONNECTED}, { + ERRHRD, ERRgeneral, NT_STATUS_PIPE_LISTENING}, { + ERRDOS, ERRbadpipe, NT_STATUS_INVALID_READ_MODE}, { + ERRDOS, 121, NT_STATUS_IO_TIMEOUT}, { + ERRDOS, 38, NT_STATUS_FILE_FORCED_CLOSED}, { + ERRHRD, ERRgeneral, NT_STATUS_PROFILING_NOT_STARTED}, { + ERRHRD, ERRgeneral, NT_STATUS_PROFILING_NOT_STOPPED}, { + ERRHRD, ERRgeneral, NT_STATUS_COULD_NOT_INTERPRET}, { + ERRDOS, ERRnoaccess, NT_STATUS_FILE_IS_A_DIRECTORY}, { + ERRDOS, ERRunsup, NT_STATUS_NOT_SUPPORTED}, { + ERRDOS, 51, NT_STATUS_REMOTE_NOT_LISTENING}, { + ERRDOS, 52, NT_STATUS_DUPLICATE_NAME}, { + ERRDOS, 53, NT_STATUS_BAD_NETWORK_PATH}, { + ERRDOS, 54, NT_STATUS_NETWORK_BUSY}, { + ERRDOS, 55, NT_STATUS_DEVICE_DOES_NOT_EXIST}, { + ERRDOS, 56, NT_STATUS_TOO_MANY_COMMANDS}, { + ERRDOS, 57, NT_STATUS_ADAPTER_HARDWARE_ERROR}, { + ERRDOS, 58, NT_STATUS_INVALID_NETWORK_RESPONSE}, { + ERRDOS, 59, NT_STATUS_UNEXPECTED_NETWORK_ERROR}, { + ERRDOS, 60, NT_STATUS_BAD_REMOTE_ADAPTER}, { + ERRDOS, 61, NT_STATUS_PRINT_QUEUE_FULL}, { + ERRDOS, 62, NT_STATUS_NO_SPOOL_SPACE}, { + ERRDOS, 63, NT_STATUS_PRINT_CANCELLED}, { + ERRDOS, 64, NT_STATUS_NETWORK_NAME_DELETED}, { + ERRDOS, 65, NT_STATUS_NETWORK_ACCESS_DENIED}, { + ERRDOS, 66, NT_STATUS_BAD_DEVICE_TYPE}, { + ERRDOS, ERRnosuchshare, NT_STATUS_BAD_NETWORK_NAME}, { + ERRDOS, 68, NT_STATUS_TOO_MANY_NAMES}, { + ERRDOS, 69, NT_STATUS_TOO_MANY_SESSIONS}, { + ERRDOS, 70, NT_STATUS_SHARING_PAUSED}, { + ERRDOS, 71, NT_STATUS_REQUEST_NOT_ACCEPTED}, { + ERRDOS, 72, NT_STATUS_REDIRECTOR_PAUSED}, { + ERRDOS, 88, NT_STATUS_NET_WRITE_FAULT}, { + ERRHRD, ERRgeneral, NT_STATUS_PROFILING_AT_LIMIT}, { + ERRDOS, ERRdiffdevice, NT_STATUS_NOT_SAME_DEVICE}, { + ERRDOS, ERRnoaccess, NT_STATUS_FILE_RENAMED}, { + ERRDOS, 240, NT_STATUS_VIRTUAL_CIRCUIT_CLOSED}, { + ERRHRD, ERRgeneral, NT_STATUS_NO_SECURITY_ON_OBJECT}, { + ERRHRD, ERRgeneral, NT_STATUS_CANT_WAIT}, { + ERRDOS, ERRpipeclosing, NT_STATUS_PIPE_EMPTY}, { + ERRHRD, ERRgeneral, NT_STATUS_CANT_ACCESS_DOMAIN_INFO}, { + ERRHRD, ERRgeneral, NT_STATUS_CANT_TERMINATE_SELF}, { + ERRHRD, ERRgeneral, NT_STATUS_INVALID_SERVER_STATE}, { + ERRHRD, ERRgeneral, NT_STATUS_INVALID_DOMAIN_STATE}, { + ERRHRD, ERRgeneral, NT_STATUS_INVALID_DOMAIN_ROLE}, { + ERRHRD, ERRgeneral, NT_STATUS_NO_SUCH_DOMAIN}, { + ERRHRD, ERRgeneral, NT_STATUS_DOMAIN_EXISTS}, { + ERRHRD, ERRgeneral, NT_STATUS_DOMAIN_LIMIT_EXCEEDED}, { + ERRDOS, 300, NT_STATUS_OPLOCK_NOT_GRANTED}, { + ERRDOS, 301, NT_STATUS_INVALID_OPLOCK_PROTOCOL}, { + ERRHRD, ERRgeneral, NT_STATUS_INTERNAL_DB_CORRUPTION}, { + ERRHRD, ERRgeneral, NT_STATUS_INTERNAL_ERROR}, { + ERRHRD, ERRgeneral, NT_STATUS_GENERIC_NOT_MAPPED}, { + ERRHRD, ERRgeneral, NT_STATUS_BAD_DESCRIPTOR_FORMAT}, { + ERRHRD, ERRgeneral, NT_STATUS_INVALID_USER_BUFFER}, { + ERRHRD, ERRgeneral, NT_STATUS_UNEXPECTED_IO_ERROR}, { + ERRHRD, ERRgeneral, NT_STATUS_UNEXPECTED_MM_CREATE_ERR}, { + ERRHRD, ERRgeneral, NT_STATUS_UNEXPECTED_MM_MAP_ERROR}, { + ERRHRD, ERRgeneral, NT_STATUS_UNEXPECTED_MM_EXTEND_ERR}, { + ERRHRD, ERRgeneral, NT_STATUS_NOT_LOGON_PROCESS}, { + ERRHRD, ERRgeneral, NT_STATUS_LOGON_SESSION_EXISTS}, { + ERRDOS, 87, NT_STATUS_INVALID_PARAMETER_1}, { + ERRDOS, 87, NT_STATUS_INVALID_PARAMETER_2}, { + ERRDOS, 87, NT_STATUS_INVALID_PARAMETER_3}, { + ERRDOS, 87, NT_STATUS_INVALID_PARAMETER_4}, { + ERRDOS, 87, NT_STATUS_INVALID_PARAMETER_5}, { + ERRDOS, 87, NT_STATUS_INVALID_PARAMETER_6}, { + ERRDOS, 87, NT_STATUS_INVALID_PARAMETER_7}, { + ERRDOS, 87, NT_STATUS_INVALID_PARAMETER_8}, { + ERRDOS, 87, NT_STATUS_INVALID_PARAMETER_9}, { + ERRDOS, 87, NT_STATUS_INVALID_PARAMETER_10}, { + ERRDOS, 87, NT_STATUS_INVALID_PARAMETER_11}, { + ERRDOS, 87, NT_STATUS_INVALID_PARAMETER_12}, { + ERRDOS, ERRbadpath, NT_STATUS_REDIRECTOR_NOT_STARTED}, { + ERRHRD, ERRgeneral, NT_STATUS_REDIRECTOR_STARTED}, { + ERRHRD, ERRgeneral, NT_STATUS_STACK_OVERFLOW}, { + ERRHRD, ERRgeneral, NT_STATUS_NO_SUCH_PACKAGE}, { + ERRHRD, ERRgeneral, NT_STATUS_BAD_FUNCTION_TABLE}, { + ERRDOS, 203, 0xc0000100}, { + ERRDOS, 145, NT_STATUS_DIRECTORY_NOT_EMPTY}, { + ERRHRD, ERRgeneral, NT_STATUS_FILE_CORRUPT_ERROR}, { + ERRDOS, 267, NT_STATUS_NOT_A_DIRECTORY}, { + ERRHRD, ERRgeneral, NT_STATUS_BAD_LOGON_SESSION_STATE}, { + ERRHRD, ERRgeneral, NT_STATUS_LOGON_SESSION_COLLISION}, { + ERRDOS, 206, NT_STATUS_NAME_TOO_LONG}, { + ERRDOS, 2401, NT_STATUS_FILES_OPEN}, { + ERRDOS, 2404, NT_STATUS_CONNECTION_IN_USE}, { + ERRHRD, ERRgeneral, NT_STATUS_MESSAGE_NOT_FOUND}, { + ERRDOS, ERRnoaccess, NT_STATUS_PROCESS_IS_TERMINATING}, { + ERRHRD, ERRgeneral, NT_STATUS_INVALID_LOGON_TYPE}, { + ERRHRD, ERRgeneral, NT_STATUS_NO_GUID_TRANSLATION}, { + ERRHRD, ERRgeneral, NT_STATUS_CANNOT_IMPERSONATE}, { + ERRHRD, ERRgeneral, NT_STATUS_IMAGE_ALREADY_LOADED}, { + ERRHRD, ERRgeneral, NT_STATUS_ABIOS_NOT_PRESENT}, { + ERRHRD, ERRgeneral, NT_STATUS_ABIOS_LID_NOT_EXIST}, { + ERRHRD, ERRgeneral, NT_STATUS_ABIOS_LID_ALREADY_OWNED}, { + ERRHRD, ERRgeneral, NT_STATUS_ABIOS_NOT_LID_OWNER}, { + ERRHRD, ERRgeneral, NT_STATUS_ABIOS_INVALID_COMMAND}, { + ERRHRD, ERRgeneral, NT_STATUS_ABIOS_INVALID_LID}, { + ERRHRD, ERRgeneral, NT_STATUS_ABIOS_SELECTOR_NOT_AVAILABLE}, { + ERRHRD, ERRgeneral, NT_STATUS_ABIOS_INVALID_SELECTOR}, { + ERRHRD, ERRgeneral, NT_STATUS_NO_LDT}, { + ERRHRD, ERRgeneral, NT_STATUS_INVALID_LDT_SIZE}, { + ERRHRD, ERRgeneral, NT_STATUS_INVALID_LDT_OFFSET}, { + ERRHRD, ERRgeneral, NT_STATUS_INVALID_LDT_DESCRIPTOR}, { + ERRDOS, 193, NT_STATUS_INVALID_IMAGE_NE_FORMAT}, { + ERRHRD, ERRgeneral, NT_STATUS_RXACT_INVALID_STATE}, { + ERRHRD, ERRgeneral, NT_STATUS_RXACT_COMMIT_FAILURE}, { + ERRHRD, ERRgeneral, NT_STATUS_MAPPED_FILE_SIZE_ZERO}, { + ERRDOS, ERRnofids, NT_STATUS_TOO_MANY_OPENED_FILES}, { + ERRHRD, ERRgeneral, NT_STATUS_CANCELLED}, { + ERRDOS, ERRnoaccess, NT_STATUS_CANNOT_DELETE}, { + ERRHRD, ERRgeneral, NT_STATUS_INVALID_COMPUTER_NAME}, { + ERRDOS, ERRnoaccess, NT_STATUS_FILE_DELETED}, { + ERRHRD, ERRgeneral, NT_STATUS_SPECIAL_ACCOUNT}, { + ERRHRD, ERRgeneral, NT_STATUS_SPECIAL_GROUP}, { + ERRHRD, ERRgeneral, NT_STATUS_SPECIAL_USER}, { + ERRHRD, ERRgeneral, NT_STATUS_MEMBERS_PRIMARY_GROUP}, { + ERRDOS, ERRbadfid, NT_STATUS_FILE_CLOSED}, { + ERRHRD, ERRgeneral, NT_STATUS_TOO_MANY_THREADS}, { + ERRHRD, ERRgeneral, NT_STATUS_THREAD_NOT_IN_PROCESS}, { + ERRHRD, ERRgeneral, NT_STATUS_TOKEN_ALREADY_IN_USE}, { + ERRHRD, ERRgeneral, NT_STATUS_PAGEFILE_QUOTA_EXCEEDED}, { + ERRHRD, ERRgeneral, NT_STATUS_COMMITMENT_LIMIT}, { + ERRDOS, 193, NT_STATUS_INVALID_IMAGE_LE_FORMAT}, { + ERRDOS, 193, NT_STATUS_INVALID_IMAGE_NOT_MZ}, { + ERRDOS, 193, NT_STATUS_INVALID_IMAGE_PROTECT}, { + ERRDOS, 193, NT_STATUS_INVALID_IMAGE_WIN_16}, { + ERRHRD, ERRgeneral, NT_STATUS_LOGON_SERVER_CONFLICT}, { + ERRHRD, ERRgeneral, NT_STATUS_TIME_DIFFERENCE_AT_DC}, { + ERRHRD, ERRgeneral, NT_STATUS_SYNCHRONIZATION_REQUIRED}, { + ERRDOS, 126, NT_STATUS_DLL_NOT_FOUND}, { + ERRHRD, ERRgeneral, NT_STATUS_OPEN_FAILED}, { + ERRHRD, ERRgeneral, NT_STATUS_IO_PRIVILEGE_FAILED}, { + ERRDOS, 182, NT_STATUS_ORDINAL_NOT_FOUND}, { + ERRDOS, 127, NT_STATUS_ENTRYPOINT_NOT_FOUND}, { + ERRHRD, ERRgeneral, NT_STATUS_CONTROL_C_EXIT}, { + ERRDOS, 64, NT_STATUS_LOCAL_DISCONNECT}, { + ERRDOS, 64, NT_STATUS_REMOTE_DISCONNECT}, { + ERRDOS, 51, NT_STATUS_REMOTE_RESOURCES}, { + ERRDOS, 59, NT_STATUS_LINK_FAILED}, { + ERRDOS, 59, NT_STATUS_LINK_TIMEOUT}, { + ERRDOS, 59, NT_STATUS_INVALID_CONNECTION}, { + ERRDOS, 59, NT_STATUS_INVALID_ADDRESS}, { + ERRHRD, ERRgeneral, NT_STATUS_DLL_INIT_FAILED}, { + ERRHRD, ERRgeneral, NT_STATUS_MISSING_SYSTEMFILE}, { + ERRHRD, ERRgeneral, NT_STATUS_UNHANDLED_EXCEPTION}, { + ERRHRD, ERRgeneral, NT_STATUS_APP_INIT_FAILURE}, { + ERRHRD, ERRgeneral, NT_STATUS_PAGEFILE_CREATE_FAILED}, { + ERRHRD, ERRgeneral, NT_STATUS_NO_PAGEFILE}, { + ERRDOS, 124, NT_STATUS_INVALID_LEVEL}, { + ERRDOS, 86, NT_STATUS_WRONG_PASSWORD_CORE}, { + ERRHRD, ERRgeneral, NT_STATUS_ILLEGAL_FLOAT_CONTEXT}, { + ERRDOS, 109, NT_STATUS_PIPE_BROKEN}, { + ERRHRD, ERRgeneral, NT_STATUS_REGISTRY_CORRUPT}, { + ERRHRD, ERRgeneral, NT_STATUS_REGISTRY_IO_FAILED}, { + ERRHRD, ERRgeneral, NT_STATUS_NO_EVENT_PAIR}, { + ERRHRD, ERRgeneral, NT_STATUS_UNRECOGNIZED_VOLUME}, { + ERRHRD, ERRgeneral, NT_STATUS_SERIAL_NO_DEVICE_INITED}, { + ERRHRD, ERRgeneral, NT_STATUS_NO_SUCH_ALIAS}, { + ERRHRD, ERRgeneral, NT_STATUS_MEMBER_NOT_IN_ALIAS}, { + ERRHRD, ERRgeneral, NT_STATUS_MEMBER_IN_ALIAS}, { + ERRHRD, ERRgeneral, NT_STATUS_ALIAS_EXISTS}, { + ERRHRD, ERRgeneral, NT_STATUS_LOGON_NOT_GRANTED}, { + ERRHRD, ERRgeneral, NT_STATUS_TOO_MANY_SECRETS}, { + ERRHRD, ERRgeneral, NT_STATUS_SECRET_TOO_LONG}, { + ERRHRD, ERRgeneral, NT_STATUS_INTERNAL_DB_ERROR}, { + ERRHRD, ERRgeneral, NT_STATUS_FULLSCREEN_MODE}, { + ERRHRD, ERRgeneral, NT_STATUS_TOO_MANY_CONTEXT_IDS}, { + ERRDOS, ERRnoaccess, NT_STATUS_LOGON_TYPE_NOT_GRANTED}, { + ERRHRD, ERRgeneral, NT_STATUS_NOT_REGISTRY_FILE}, { + ERRHRD, ERRgeneral, NT_STATUS_NT_CROSS_ENCRYPTION_REQUIRED}, { + ERRHRD, ERRgeneral, NT_STATUS_DOMAIN_CTRLR_CONFIG_ERROR}, { + ERRHRD, ERRgeneral, NT_STATUS_FT_MISSING_MEMBER}, { + ERRHRD, ERRgeneral, NT_STATUS_ILL_FORMED_SERVICE_ENTRY}, { + ERRHRD, ERRgeneral, NT_STATUS_ILLEGAL_CHARACTER}, { + ERRHRD, ERRgeneral, NT_STATUS_UNMAPPABLE_CHARACTER}, { + ERRHRD, ERRgeneral, NT_STATUS_UNDEFINED_CHARACTER}, { + ERRHRD, ERRgeneral, NT_STATUS_FLOPPY_VOLUME}, { + ERRHRD, ERRgeneral, NT_STATUS_FLOPPY_ID_MARK_NOT_FOUND}, { + ERRHRD, ERRgeneral, NT_STATUS_FLOPPY_WRONG_CYLINDER}, { + ERRHRD, ERRgeneral, NT_STATUS_FLOPPY_UNKNOWN_ERROR}, { + ERRHRD, ERRgeneral, NT_STATUS_FLOPPY_BAD_REGISTERS}, { + ERRHRD, ERRgeneral, NT_STATUS_DISK_RECALIBRATE_FAILED}, { + ERRHRD, ERRgeneral, NT_STATUS_DISK_OPERATION_FAILED}, { + ERRHRD, ERRgeneral, NT_STATUS_DISK_RESET_FAILED}, { + ERRHRD, ERRgeneral, NT_STATUS_SHARED_IRQ_BUSY}, { + ERRHRD, ERRgeneral, NT_STATUS_FT_ORPHANING}, { + ERRHRD, ERRgeneral, 0xc000016e}, { + ERRHRD, ERRgeneral, 0xc000016f}, { + ERRHRD, ERRgeneral, 0xc0000170}, { + ERRHRD, ERRgeneral, 0xc0000171}, { + ERRHRD, ERRgeneral, NT_STATUS_PARTITION_FAILURE}, { + ERRHRD, ERRgeneral, NT_STATUS_INVALID_BLOCK_LENGTH}, { + ERRHRD, ERRgeneral, NT_STATUS_DEVICE_NOT_PARTITIONED}, { + ERRHRD, ERRgeneral, NT_STATUS_UNABLE_TO_LOCK_MEDIA}, { + ERRHRD, ERRgeneral, NT_STATUS_UNABLE_TO_UNLOAD_MEDIA}, { + ERRHRD, ERRgeneral, NT_STATUS_EOM_OVERFLOW}, { + ERRHRD, ERRgeneral, NT_STATUS_NO_MEDIA}, { + ERRHRD, ERRgeneral, 0xc0000179}, { + ERRHRD, ERRgeneral, NT_STATUS_NO_SUCH_MEMBER}, { + ERRHRD, ERRgeneral, NT_STATUS_INVALID_MEMBER}, { + ERRHRD, ERRgeneral, NT_STATUS_KEY_DELETED}, { + ERRHRD, ERRgeneral, NT_STATUS_NO_LOG_SPACE}, { + ERRHRD, ERRgeneral, NT_STATUS_TOO_MANY_SIDS}, { + ERRHRD, ERRgeneral, NT_STATUS_LM_CROSS_ENCRYPTION_REQUIRED}, { + ERRHRD, ERRgeneral, NT_STATUS_KEY_HAS_CHILDREN}, { + ERRHRD, ERRgeneral, NT_STATUS_CHILD_MUST_BE_VOLATILE}, { + ERRDOS, 87, NT_STATUS_DEVICE_CONFIGURATION_ERROR}, { + ERRHRD, ERRgeneral, NT_STATUS_DRIVER_INTERNAL_ERROR}, { + ERRDOS, 22, NT_STATUS_INVALID_DEVICE_STATE}, { + ERRHRD, ERRgeneral, NT_STATUS_IO_DEVICE_ERROR}, { + ERRHRD, ERRgeneral, NT_STATUS_DEVICE_PROTOCOL_ERROR}, { + ERRHRD, ERRgeneral, NT_STATUS_BACKUP_CONTROLLER}, { + ERRHRD, ERRgeneral, NT_STATUS_LOG_FILE_FULL}, { + ERRDOS, 19, NT_STATUS_TOO_LATE}, { + ERRDOS, ERRnoaccess, NT_STATUS_NO_TRUST_LSA_SECRET}, +/* { This NT error code was 'sqashed' + from NT_STATUS_NO_TRUST_SAM_ACCOUNT to + NT_STATUS_TRUSTED_RELATIONSHIP_FAILURE during the session setup } */ + { + ERRDOS, ERRnoaccess, NT_STATUS_NO_TRUST_SAM_ACCOUNT}, { + ERRDOS, ERRnoaccess, NT_STATUS_TRUSTED_DOMAIN_FAILURE}, { + ERRDOS, ERRnoaccess, NT_STATUS_TRUSTED_RELATIONSHIP_FAILURE}, { + ERRHRD, ERRgeneral, NT_STATUS_EVENTLOG_FILE_CORRUPT}, { + ERRHRD, ERRgeneral, NT_STATUS_EVENTLOG_CANT_START}, { + ERRDOS, ERRnoaccess, NT_STATUS_TRUST_FAILURE}, { + ERRHRD, ERRgeneral, NT_STATUS_MUTANT_LIMIT_EXCEEDED}, { + ERRDOS, ERRnetlogonNotStarted, NT_STATUS_NETLOGON_NOT_STARTED}, { + ERRSRV, ERRaccountexpired, NT_STATUS_ACCOUNT_EXPIRED}, { + ERRHRD, ERRgeneral, NT_STATUS_POSSIBLE_DEADLOCK}, { + ERRHRD, ERRgeneral, NT_STATUS_NETWORK_CREDENTIAL_CONFLICT}, { + ERRHRD, ERRgeneral, NT_STATUS_REMOTE_SESSION_LIMIT}, { + ERRHRD, ERRgeneral, NT_STATUS_EVENTLOG_FILE_CHANGED}, { + ERRDOS, ERRnoaccess, NT_STATUS_NOLOGON_INTERDOMAIN_TRUST_ACCOUNT}, { + ERRDOS, ERRnoaccess, NT_STATUS_NOLOGON_WORKSTATION_TRUST_ACCOUNT}, { + ERRDOS, ERRnoaccess, NT_STATUS_NOLOGON_SERVER_TRUST_ACCOUNT}, +/* { This NT error code was 'sqashed' + from NT_STATUS_DOMAIN_TRUST_INCONSISTENT to NT_STATUS_LOGON_FAILURE + during the session setup } */ + { + ERRDOS, ERRnoaccess, NT_STATUS_DOMAIN_TRUST_INCONSISTENT}, { + ERRHRD, ERRgeneral, NT_STATUS_FS_DRIVER_REQUIRED}, { + ERRHRD, ERRgeneral, NT_STATUS_NO_USER_SESSION_KEY}, { + ERRDOS, 59, NT_STATUS_USER_SESSION_DELETED}, { + ERRHRD, ERRgeneral, NT_STATUS_RESOURCE_LANG_NOT_FOUND}, { + ERRDOS, ERRnoresource, NT_STATUS_INSUFF_SERVER_RESOURCES}, { + ERRHRD, ERRgeneral, NT_STATUS_INVALID_BUFFER_SIZE}, { + ERRHRD, ERRgeneral, NT_STATUS_INVALID_ADDRESS_COMPONENT}, { + ERRHRD, ERRgeneral, NT_STATUS_INVALID_ADDRESS_WILDCARD}, { + ERRDOS, 68, NT_STATUS_TOO_MANY_ADDRESSES}, { + ERRDOS, 52, NT_STATUS_ADDRESS_ALREADY_EXISTS}, { + ERRDOS, 64, NT_STATUS_ADDRESS_CLOSED}, { + ERRDOS, 64, NT_STATUS_CONNECTION_DISCONNECTED}, { + ERRDOS, 64, NT_STATUS_CONNECTION_RESET}, { + ERRDOS, 68, NT_STATUS_TOO_MANY_NODES}, { + ERRDOS, 59, NT_STATUS_TRANSACTION_ABORTED}, { + ERRDOS, 59, NT_STATUS_TRANSACTION_TIMED_OUT}, { + ERRDOS, 59, NT_STATUS_TRANSACTION_NO_RELEASE}, { + ERRDOS, 59, NT_STATUS_TRANSACTION_NO_MATCH}, { + ERRDOS, 59, NT_STATUS_TRANSACTION_RESPONDED}, { + ERRDOS, 59, NT_STATUS_TRANSACTION_INVALID_ID}, { + ERRDOS, 59, NT_STATUS_TRANSACTION_INVALID_TYPE}, { + ERRDOS, ERRunsup, NT_STATUS_NOT_SERVER_SESSION}, { + ERRDOS, ERRunsup, NT_STATUS_NOT_CLIENT_SESSION}, { + ERRHRD, ERRgeneral, NT_STATUS_CANNOT_LOAD_REGISTRY_FILE}, { + ERRHRD, ERRgeneral, NT_STATUS_DEBUG_ATTACH_FAILED}, { + ERRHRD, ERRgeneral, NT_STATUS_SYSTEM_PROCESS_TERMINATED}, { + ERRHRD, ERRgeneral, NT_STATUS_DATA_NOT_ACCEPTED}, { + ERRHRD, ERRgeneral, NT_STATUS_NO_BROWSER_SERVERS_FOUND}, { + ERRHRD, ERRgeneral, NT_STATUS_VDM_HARD_ERROR}, { + ERRHRD, ERRgeneral, NT_STATUS_DRIVER_CANCEL_TIMEOUT}, { + ERRHRD, ERRgeneral, NT_STATUS_REPLY_MESSAGE_MISMATCH}, { + ERRHRD, ERRgeneral, NT_STATUS_MAPPED_ALIGNMENT}, { + ERRDOS, 193, NT_STATUS_IMAGE_CHECKSUM_MISMATCH}, { + ERRHRD, ERRgeneral, NT_STATUS_LOST_WRITEBEHIND_DATA}, { + ERRHRD, ERRgeneral, NT_STATUS_CLIENT_SERVER_PARAMETERS_INVALID}, { + ERRSRV, ERRpasswordExpired, NT_STATUS_PASSWORD_MUST_CHANGE}, { + ERRHRD, ERRgeneral, NT_STATUS_NOT_FOUND}, { + ERRHRD, ERRgeneral, NT_STATUS_NOT_TINY_STREAM}, { + ERRHRD, ERRgeneral, NT_STATUS_RECOVERY_FAILURE}, { + ERRHRD, ERRgeneral, NT_STATUS_STACK_OVERFLOW_READ}, { + ERRHRD, ERRgeneral, NT_STATUS_FAIL_CHECK}, { + ERRHRD, ERRgeneral, NT_STATUS_DUPLICATE_OBJECTID}, { + ERRHRD, ERRgeneral, NT_STATUS_OBJECTID_EXISTS}, { + ERRHRD, ERRgeneral, NT_STATUS_CONVERT_TO_LARGE}, { + ERRHRD, ERRgeneral, NT_STATUS_RETRY}, { + ERRHRD, ERRgeneral, NT_STATUS_FOUND_OUT_OF_SCOPE}, { + ERRHRD, ERRgeneral, NT_STATUS_ALLOCATE_BUCKET}, { + ERRHRD, ERRgeneral, NT_STATUS_PROPSET_NOT_FOUND}, { + ERRHRD, ERRgeneral, NT_STATUS_MARSHALL_OVERFLOW}, { + ERRHRD, ERRgeneral, NT_STATUS_INVALID_VARIANT}, { + ERRHRD, ERRgeneral, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND}, { + ERRDOS, ERRnoaccess, NT_STATUS_ACCOUNT_LOCKED_OUT}, { + ERRDOS, ERRbadfid, NT_STATUS_HANDLE_NOT_CLOSABLE}, { + ERRHRD, ERRgeneral, NT_STATUS_CONNECTION_REFUSED}, { + ERRHRD, ERRgeneral, NT_STATUS_GRACEFUL_DISCONNECT}, { + ERRHRD, ERRgeneral, NT_STATUS_ADDRESS_ALREADY_ASSOCIATED}, { + ERRHRD, ERRgeneral, NT_STATUS_ADDRESS_NOT_ASSOCIATED}, { + ERRHRD, ERRgeneral, NT_STATUS_CONNECTION_INVALID}, { + ERRHRD, ERRgeneral, NT_STATUS_CONNECTION_ACTIVE}, { + ERRHRD, ERRgeneral, NT_STATUS_NETWORK_UNREACHABLE}, { + ERRHRD, ERRgeneral, NT_STATUS_HOST_UNREACHABLE}, { + ERRHRD, ERRgeneral, NT_STATUS_PROTOCOL_UNREACHABLE}, { + ERRHRD, ERRgeneral, NT_STATUS_PORT_UNREACHABLE}, { + ERRHRD, ERRgeneral, NT_STATUS_REQUEST_ABORTED}, { + ERRHRD, ERRgeneral, NT_STATUS_CONNECTION_ABORTED}, { + ERRHRD, ERRgeneral, NT_STATUS_BAD_COMPRESSION_BUFFER}, { + ERRHRD, ERRgeneral, NT_STATUS_USER_MAPPED_FILE}, { + ERRHRD, ERRgeneral, NT_STATUS_AUDIT_FAILED}, { + ERRHRD, ERRgeneral, NT_STATUS_TIMER_RESOLUTION_NOT_SET}, { + ERRHRD, ERRgeneral, NT_STATUS_CONNECTION_COUNT_LIMIT}, { + ERRHRD, ERRgeneral, NT_STATUS_LOGIN_TIME_RESTRICTION}, { + ERRHRD, ERRgeneral, NT_STATUS_LOGIN_WKSTA_RESTRICTION}, { + ERRDOS, 193, NT_STATUS_IMAGE_MP_UP_MISMATCH}, { + ERRHRD, ERRgeneral, 0xc000024a}, { + ERRHRD, ERRgeneral, 0xc000024b}, { + ERRHRD, ERRgeneral, 0xc000024c}, { + ERRHRD, ERRgeneral, 0xc000024d}, { + ERRHRD, ERRgeneral, 0xc000024e}, { + ERRHRD, ERRgeneral, 0xc000024f}, { + ERRHRD, ERRgeneral, NT_STATUS_INSUFFICIENT_LOGON_INFO}, { + ERRHRD, ERRgeneral, NT_STATUS_BAD_DLL_ENTRYPOINT}, { + ERRHRD, ERRgeneral, NT_STATUS_BAD_SERVICE_ENTRYPOINT}, { + ERRHRD, ERRgeneral, NT_STATUS_LPC_REPLY_LOST}, { + ERRHRD, ERRgeneral, NT_STATUS_IP_ADDRESS_CONFLICT1}, { + ERRHRD, ERRgeneral, NT_STATUS_IP_ADDRESS_CONFLICT2}, { + ERRHRD, ERRgeneral, NT_STATUS_REGISTRY_QUOTA_LIMIT}, { + ERRSRV, 3, NT_STATUS_PATH_NOT_COVERED}, { + ERRHRD, ERRgeneral, NT_STATUS_NO_CALLBACK_ACTIVE}, { + ERRHRD, ERRgeneral, NT_STATUS_LICENSE_QUOTA_EXCEEDED}, { + ERRHRD, ERRgeneral, NT_STATUS_PWD_TOO_SHORT}, { + ERRHRD, ERRgeneral, NT_STATUS_PWD_TOO_RECENT}, { + ERRHRD, ERRgeneral, NT_STATUS_PWD_HISTORY_CONFLICT}, { + ERRHRD, ERRgeneral, 0xc000025d}, { + ERRHRD, ERRgeneral, NT_STATUS_PLUGPLAY_NO_DEVICE}, { + ERRHRD, ERRgeneral, NT_STATUS_UNSUPPORTED_COMPRESSION}, { + ERRHRD, ERRgeneral, NT_STATUS_INVALID_HW_PROFILE}, { + ERRHRD, ERRgeneral, NT_STATUS_INVALID_PLUGPLAY_DEVICE_PATH}, { + ERRDOS, 182, NT_STATUS_DRIVER_ORDINAL_NOT_FOUND}, { + ERRDOS, 127, NT_STATUS_DRIVER_ENTRYPOINT_NOT_FOUND}, { + ERRDOS, 288, NT_STATUS_RESOURCE_NOT_OWNED}, { + ERRDOS, ErrTooManyLinks, NT_STATUS_TOO_MANY_LINKS}, { + ERRHRD, ERRgeneral, NT_STATUS_QUOTA_LIST_INCONSISTENT}, { + ERRHRD, ERRgeneral, NT_STATUS_FILE_IS_OFFLINE}, { + ERRDOS, 21, 0xc000026e}, { + ERRDOS, 161, 0xc0000281}, { + ERRDOS, ERRnoaccess, 0xc000028a}, { + ERRDOS, ERRnoaccess, 0xc000028b}, { + ERRHRD, ERRgeneral, 0xc000028c}, { + ERRDOS, ERRnoaccess, 0xc000028d}, { + ERRDOS, ERRnoaccess, 0xc000028e}, { + ERRDOS, ERRnoaccess, 0xc000028f}, { + ERRDOS, ERRnoaccess, 0xc0000290}, { + ERRDOS, ERRbadfunc, 0xc000029c}, { + ERRDOS, ERRsymlink, NT_STATUS_STOPPED_ON_SYMLINK}, { + ERRDOS, ERRinvlevel, 0x007c0001}, { + 0, 0, 0 } +}; + +/***************************************************************************** + Print an error message from the status code + *****************************************************************************/ +static void +cifs_print_status(__u32 status_code) +{ + int idx = 0; + + while (nt_errs[idx].nt_errstr != NULL) { + if (((nt_errs[idx].nt_errcode) & 0xFFFFFF) == + (status_code & 0xFFFFFF)) { + pr_notice("Status code returned 0x%08x %s\n", + status_code, nt_errs[idx].nt_errstr); + } + idx++; + } + return; +} + + +static void +ntstatus_to_dos(__u32 ntstatus, __u8 *eclass, __u16 *ecode) +{ + int i; + if (ntstatus == 0) { + *eclass = 0; + *ecode = 0; + return; + } + for (i = 0; ntstatus_to_dos_map[i].ntstatus; i++) { + if (ntstatus == ntstatus_to_dos_map[i].ntstatus) { + *eclass = ntstatus_to_dos_map[i].dos_class; + *ecode = ntstatus_to_dos_map[i].dos_code; + return; + } + } + *eclass = ERRHRD; + *ecode = ERRgeneral; +} + +int +map_smb_to_linux_error(char *buf, bool logErr) +{ + struct smb_hdr *smb = (struct smb_hdr *)buf; + unsigned int i; + int rc = -EIO; /* if transport error smb error may not be set */ + __u8 smberrclass; + __u16 smberrcode; + + /* BB if NT Status codes - map NT BB */ + + /* old style smb error codes */ + if (smb->Status.CifsError == 0) + return 0; + + if (smb->Flags2 & SMBFLG2_ERR_STATUS) { + /* translate the newer STATUS codes to old style SMB errors + * and then to POSIX errors */ + __u32 err = le32_to_cpu(smb->Status.CifsError); + if (logErr && (err != (NT_STATUS_MORE_PROCESSING_REQUIRED))) + cifs_print_status(err); + else if (cifsFYI & CIFS_RC) + cifs_print_status(err); + ntstatus_to_dos(err, &smberrclass, &smberrcode); + } else { + smberrclass = smb->Status.DosError.ErrorClass; + smberrcode = le16_to_cpu(smb->Status.DosError.Error); + } + + /* old style errors */ + + /* DOS class smb error codes - map DOS */ + if (smberrclass == ERRDOS) { + /* 1 byte field no need to byte reverse */ + for (i = 0; + i < + sizeof(mapping_table_ERRDOS) / + sizeof(struct smb_to_posix_error); i++) { + if (mapping_table_ERRDOS[i].smb_err == 0) + break; + else if (mapping_table_ERRDOS[i].smb_err == + smberrcode) { + rc = mapping_table_ERRDOS[i].posix_code; + break; + } + /* else try next error mapping one to see if match */ + } + } else if (smberrclass == ERRSRV) { + /* server class of error codes */ + for (i = 0; + i < + sizeof(mapping_table_ERRSRV) / + sizeof(struct smb_to_posix_error); i++) { + if (mapping_table_ERRSRV[i].smb_err == 0) + break; + else if (mapping_table_ERRSRV[i].smb_err == + smberrcode) { + rc = mapping_table_ERRSRV[i].posix_code; + break; + } + /* else try next error mapping to see if match */ + } + } + /* else ERRHRD class errors or junk - return EIO */ + + cifs_dbg(FYI, "Mapping smb error code 0x%x to POSIX err %d\n", + le32_to_cpu(smb->Status.CifsError), rc); + + /* generic corrective action e.g. reconnect SMB session on + * ERRbaduid could be added */ + + return rc; +} + +int +map_and_check_smb_error(struct mid_q_entry *mid, bool logErr) +{ + int rc; + struct smb_hdr *smb = (struct smb_hdr *)mid->resp_buf; + + rc = map_smb_to_linux_error((char *)smb, logErr); + if (rc == -EACCES && !(smb->Flags2 & SMBFLG2_ERR_STATUS)) { + /* possible ERRBaduid */ + __u8 class = smb->Status.DosError.ErrorClass; + __u16 code = le16_to_cpu(smb->Status.DosError.Error); + + /* switch can be used to handle different errors */ + if (class == ERRSRV && code == ERRbaduid) { + cifs_dbg(FYI, "Server returned 0x%x, reconnecting session...\n", + code); + cifs_signal_cifsd_for_reconnect(mid->server, false); + } + } + + return rc; +} + + +/* + * calculate the size of the SMB message based on the fixed header + * portion, the number of word parameters and the data portion of the message + */ +unsigned int +smbCalcSize(void *buf) +{ + struct smb_hdr *ptr = buf; + return (sizeof(struct smb_hdr) + (2 * ptr->WordCount) + + 2 /* size of the bcc field */ + get_bcc(ptr)); +} + +/* The following are taken from fs/ntfs/util.c */ + +#define NTFS_TIME_OFFSET ((u64)(369*365 + 89) * 24 * 3600 * 10000000) + +/* + * Convert the NT UTC (based 1601-01-01, in hundred nanosecond units) + * into Unix UTC (based 1970-01-01, in seconds). + */ +struct timespec64 +cifs_NTtimeToUnix(__le64 ntutc) +{ + struct timespec64 ts; + /* BB what about the timezone? BB */ + + /* Subtract the NTFS time offset, then convert to 1s intervals. */ + s64 t = le64_to_cpu(ntutc) - NTFS_TIME_OFFSET; + u64 abs_t; + + /* + * Unfortunately can not use normal 64 bit division on 32 bit arch, but + * the alternative, do_div, does not work with negative numbers so have + * to special case them + */ + if (t < 0) { + abs_t = -t; + ts.tv_nsec = (time64_t)(do_div(abs_t, 10000000) * 100); + ts.tv_nsec = -ts.tv_nsec; + ts.tv_sec = -abs_t; + } else { + abs_t = t; + ts.tv_nsec = (time64_t)do_div(abs_t, 10000000) * 100; + ts.tv_sec = abs_t; + } + + return ts; +} + +/* Convert the Unix UTC into NT UTC. */ +u64 +cifs_UnixTimeToNT(struct timespec64 t) +{ + /* Convert to 100ns intervals and then add the NTFS time offset. */ + return (u64) t.tv_sec * 10000000 + t.tv_nsec/100 + NTFS_TIME_OFFSET; +} + +static const int total_days_of_prev_months[] = { + 0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334 +}; + +struct timespec64 cnvrtDosUnixTm(__le16 le_date, __le16 le_time, int offset) +{ + struct timespec64 ts; + time64_t sec, days; + int min, day, month, year; + u16 date = le16_to_cpu(le_date); + u16 time = le16_to_cpu(le_time); + SMB_TIME *st = (SMB_TIME *)&time; + SMB_DATE *sd = (SMB_DATE *)&date; + + cifs_dbg(FYI, "date %d time %d\n", date, time); + + sec = 2 * st->TwoSeconds; + min = st->Minutes; + if ((sec > 59) || (min > 59)) + cifs_dbg(VFS, "Invalid time min %d sec %lld\n", min, sec); + sec += (min * 60); + sec += 60 * 60 * st->Hours; + if (st->Hours > 24) + cifs_dbg(VFS, "Invalid hours %d\n", st->Hours); + day = sd->Day; + month = sd->Month; + if (day < 1 || day > 31 || month < 1 || month > 12) { + cifs_dbg(VFS, "Invalid date, month %d day: %d\n", month, day); + day = clamp(day, 1, 31); + month = clamp(month, 1, 12); + } + month -= 1; + days = day + total_days_of_prev_months[month]; + days += 3652; /* account for difference in days between 1980 and 1970 */ + year = sd->Year; + days += year * 365; + days += (year/4); /* leap year */ + /* generalized leap year calculation is more complex, ie no leap year + for years/100 except for years/400, but since the maximum number for DOS + year is 2**7, the last year is 1980+127, which means we need only + consider 2 special case years, ie the years 2000 and 2100, and only + adjust for the lack of leap year for the year 2100, as 2000 was a + leap year (divisable by 400) */ + if (year >= 120) /* the year 2100 */ + days = days - 1; /* do not count leap year for the year 2100 */ + + /* adjust for leap year where we are still before leap day */ + if (year != 120) + days -= ((year & 0x03) == 0) && (month < 2 ? 1 : 0); + sec += 24 * 60 * 60 * days; + + ts.tv_sec = sec + offset; + + /* cifs_dbg(FYI, "sec after cnvrt dos to unix time %d\n",sec); */ + + ts.tv_nsec = 0; + return ts; +} diff --git a/fs/smb/client/nterr.c b/fs/smb/client/nterr.c new file mode 100644 index 0000000000000..358a766375b4a --- /dev/null +++ b/fs/smb/client/nterr.c @@ -0,0 +1,674 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Unix SMB/Netbios implementation. + * Version 1.9. + * RPC Pipe client / server routines + * Copyright (C) Luke Kenneth Casson Leighton 1997-2001. + */ + +/* NT error codes - see nterr.h */ +#include +#include +#include "nterr.h" + +const struct nt_err_code_struct nt_errs[] = { + {"NT_STATUS_OK", NT_STATUS_OK}, + {"NT_STATUS_UNSUCCESSFUL", NT_STATUS_UNSUCCESSFUL}, + {"NT_STATUS_NOT_IMPLEMENTED", NT_STATUS_NOT_IMPLEMENTED}, + {"NT_STATUS_INVALID_INFO_CLASS", NT_STATUS_INVALID_INFO_CLASS}, + {"NT_STATUS_INFO_LENGTH_MISMATCH", NT_STATUS_INFO_LENGTH_MISMATCH}, + {"NT_STATUS_ACCESS_VIOLATION", NT_STATUS_ACCESS_VIOLATION}, + {"NT_STATUS_BUFFER_OVERFLOW", NT_STATUS_BUFFER_OVERFLOW}, + {"NT_STATUS_IN_PAGE_ERROR", NT_STATUS_IN_PAGE_ERROR}, + {"NT_STATUS_PAGEFILE_QUOTA", NT_STATUS_PAGEFILE_QUOTA}, + {"NT_STATUS_INVALID_HANDLE", NT_STATUS_INVALID_HANDLE}, + {"NT_STATUS_BAD_INITIAL_STACK", NT_STATUS_BAD_INITIAL_STACK}, + {"NT_STATUS_BAD_INITIAL_PC", NT_STATUS_BAD_INITIAL_PC}, + {"NT_STATUS_INVALID_CID", NT_STATUS_INVALID_CID}, + {"NT_STATUS_TIMER_NOT_CANCELED", NT_STATUS_TIMER_NOT_CANCELED}, + {"NT_STATUS_INVALID_PARAMETER", NT_STATUS_INVALID_PARAMETER}, + {"NT_STATUS_NO_SUCH_DEVICE", NT_STATUS_NO_SUCH_DEVICE}, + {"NT_STATUS_NO_SUCH_FILE", NT_STATUS_NO_SUCH_FILE}, + {"NT_STATUS_INVALID_DEVICE_REQUEST", + NT_STATUS_INVALID_DEVICE_REQUEST}, + {"NT_STATUS_END_OF_FILE", NT_STATUS_END_OF_FILE}, + {"NT_STATUS_WRONG_VOLUME", NT_STATUS_WRONG_VOLUME}, + {"NT_STATUS_NO_MEDIA_IN_DEVICE", NT_STATUS_NO_MEDIA_IN_DEVICE}, + {"NT_STATUS_UNRECOGNIZED_MEDIA", NT_STATUS_UNRECOGNIZED_MEDIA}, + {"NT_STATUS_NONEXISTENT_SECTOR", NT_STATUS_NONEXISTENT_SECTOR}, + {"NT_STATUS_MORE_PROCESSING_REQUIRED", + NT_STATUS_MORE_PROCESSING_REQUIRED}, + {"NT_STATUS_NO_MEMORY", NT_STATUS_NO_MEMORY}, + {"NT_STATUS_CONFLICTING_ADDRESSES", + NT_STATUS_CONFLICTING_ADDRESSES}, + {"NT_STATUS_NOT_MAPPED_VIEW", NT_STATUS_NOT_MAPPED_VIEW}, + {"NT_STATUS_UNABLE_TO_FREE_VM", NT_STATUS_UNABLE_TO_FREE_VM}, + {"NT_STATUS_UNABLE_TO_DELETE_SECTION", + NT_STATUS_UNABLE_TO_DELETE_SECTION}, + {"NT_STATUS_INVALID_SYSTEM_SERVICE", + NT_STATUS_INVALID_SYSTEM_SERVICE}, + {"NT_STATUS_ILLEGAL_INSTRUCTION", NT_STATUS_ILLEGAL_INSTRUCTION}, + {"NT_STATUS_INVALID_LOCK_SEQUENCE", + NT_STATUS_INVALID_LOCK_SEQUENCE}, + {"NT_STATUS_INVALID_VIEW_SIZE", NT_STATUS_INVALID_VIEW_SIZE}, + {"NT_STATUS_INVALID_FILE_FOR_SECTION", + NT_STATUS_INVALID_FILE_FOR_SECTION}, + {"NT_STATUS_ALREADY_COMMITTED", NT_STATUS_ALREADY_COMMITTED}, + {"NT_STATUS_ACCESS_DENIED", NT_STATUS_ACCESS_DENIED}, + {"NT_STATUS_BUFFER_TOO_SMALL", NT_STATUS_BUFFER_TOO_SMALL}, + {"NT_STATUS_OBJECT_TYPE_MISMATCH", NT_STATUS_OBJECT_TYPE_MISMATCH}, + {"NT_STATUS_NONCONTINUABLE_EXCEPTION", + NT_STATUS_NONCONTINUABLE_EXCEPTION}, + {"NT_STATUS_INVALID_DISPOSITION", NT_STATUS_INVALID_DISPOSITION}, + {"NT_STATUS_UNWIND", NT_STATUS_UNWIND}, + {"NT_STATUS_BAD_STACK", NT_STATUS_BAD_STACK}, + {"NT_STATUS_INVALID_UNWIND_TARGET", + NT_STATUS_INVALID_UNWIND_TARGET}, + {"NT_STATUS_NOT_LOCKED", NT_STATUS_NOT_LOCKED}, + {"NT_STATUS_PARITY_ERROR", NT_STATUS_PARITY_ERROR}, + {"NT_STATUS_UNABLE_TO_DECOMMIT_VM", + NT_STATUS_UNABLE_TO_DECOMMIT_VM}, + {"NT_STATUS_NOT_COMMITTED", NT_STATUS_NOT_COMMITTED}, + {"NT_STATUS_INVALID_PORT_ATTRIBUTES", + NT_STATUS_INVALID_PORT_ATTRIBUTES}, + {"NT_STATUS_PORT_MESSAGE_TOO_LONG", + NT_STATUS_PORT_MESSAGE_TOO_LONG}, + {"NT_STATUS_INVALID_PARAMETER_MIX", + NT_STATUS_INVALID_PARAMETER_MIX}, + {"NT_STATUS_INVALID_QUOTA_LOWER", NT_STATUS_INVALID_QUOTA_LOWER}, + {"NT_STATUS_DISK_CORRUPT_ERROR", NT_STATUS_DISK_CORRUPT_ERROR}, + {"NT_STATUS_OBJECT_NAME_INVALID", NT_STATUS_OBJECT_NAME_INVALID}, + {"NT_STATUS_OBJECT_NAME_NOT_FOUND", + NT_STATUS_OBJECT_NAME_NOT_FOUND}, + {"NT_STATUS_OBJECT_NAME_COLLISION", + NT_STATUS_OBJECT_NAME_COLLISION}, + {"NT_STATUS_HANDLE_NOT_WAITABLE", NT_STATUS_HANDLE_NOT_WAITABLE}, + {"NT_STATUS_PORT_DISCONNECTED", NT_STATUS_PORT_DISCONNECTED}, + {"NT_STATUS_DEVICE_ALREADY_ATTACHED", + NT_STATUS_DEVICE_ALREADY_ATTACHED}, + {"NT_STATUS_OBJECT_PATH_INVALID", NT_STATUS_OBJECT_PATH_INVALID}, + {"NT_STATUS_OBJECT_PATH_NOT_FOUND", + NT_STATUS_OBJECT_PATH_NOT_FOUND}, + {"NT_STATUS_OBJECT_PATH_SYNTAX_BAD", + NT_STATUS_OBJECT_PATH_SYNTAX_BAD}, + {"NT_STATUS_DATA_OVERRUN", NT_STATUS_DATA_OVERRUN}, + {"NT_STATUS_DATA_LATE_ERROR", NT_STATUS_DATA_LATE_ERROR}, + {"NT_STATUS_DATA_ERROR", NT_STATUS_DATA_ERROR}, + {"NT_STATUS_CRC_ERROR", NT_STATUS_CRC_ERROR}, + {"NT_STATUS_SECTION_TOO_BIG", NT_STATUS_SECTION_TOO_BIG}, + {"NT_STATUS_PORT_CONNECTION_REFUSED", + NT_STATUS_PORT_CONNECTION_REFUSED}, + {"NT_STATUS_INVALID_PORT_HANDLE", NT_STATUS_INVALID_PORT_HANDLE}, + {"NT_STATUS_SHARING_VIOLATION", NT_STATUS_SHARING_VIOLATION}, + {"NT_STATUS_QUOTA_EXCEEDED", NT_STATUS_QUOTA_EXCEEDED}, + {"NT_STATUS_INVALID_PAGE_PROTECTION", + NT_STATUS_INVALID_PAGE_PROTECTION}, + {"NT_STATUS_MUTANT_NOT_OWNED", NT_STATUS_MUTANT_NOT_OWNED}, + {"NT_STATUS_SEMAPHORE_LIMIT_EXCEEDED", + NT_STATUS_SEMAPHORE_LIMIT_EXCEEDED}, + {"NT_STATUS_PORT_ALREADY_SET", NT_STATUS_PORT_ALREADY_SET}, + {"NT_STATUS_SECTION_NOT_IMAGE", NT_STATUS_SECTION_NOT_IMAGE}, + {"NT_STATUS_SUSPEND_COUNT_EXCEEDED", + NT_STATUS_SUSPEND_COUNT_EXCEEDED}, + {"NT_STATUS_THREAD_IS_TERMINATING", + NT_STATUS_THREAD_IS_TERMINATING}, + {"NT_STATUS_BAD_WORKING_SET_LIMIT", + NT_STATUS_BAD_WORKING_SET_LIMIT}, + {"NT_STATUS_INCOMPATIBLE_FILE_MAP", + NT_STATUS_INCOMPATIBLE_FILE_MAP}, + {"NT_STATUS_SECTION_PROTECTION", NT_STATUS_SECTION_PROTECTION}, + {"NT_STATUS_EAS_NOT_SUPPORTED", NT_STATUS_EAS_NOT_SUPPORTED}, + {"NT_STATUS_EA_TOO_LARGE", NT_STATUS_EA_TOO_LARGE}, + {"NT_STATUS_NONEXISTENT_EA_ENTRY", NT_STATUS_NONEXISTENT_EA_ENTRY}, + {"NT_STATUS_NO_EAS_ON_FILE", NT_STATUS_NO_EAS_ON_FILE}, + {"NT_STATUS_EA_CORRUPT_ERROR", NT_STATUS_EA_CORRUPT_ERROR}, + {"NT_STATUS_FILE_LOCK_CONFLICT", NT_STATUS_FILE_LOCK_CONFLICT}, + {"NT_STATUS_LOCK_NOT_GRANTED", NT_STATUS_LOCK_NOT_GRANTED}, + {"NT_STATUS_DELETE_PENDING", NT_STATUS_DELETE_PENDING}, + {"NT_STATUS_CTL_FILE_NOT_SUPPORTED", + NT_STATUS_CTL_FILE_NOT_SUPPORTED}, + {"NT_STATUS_UNKNOWN_REVISION", NT_STATUS_UNKNOWN_REVISION}, + {"NT_STATUS_REVISION_MISMATCH", NT_STATUS_REVISION_MISMATCH}, + {"NT_STATUS_INVALID_OWNER", NT_STATUS_INVALID_OWNER}, + {"NT_STATUS_INVALID_PRIMARY_GROUP", + NT_STATUS_INVALID_PRIMARY_GROUP}, + {"NT_STATUS_NO_IMPERSONATION_TOKEN", + NT_STATUS_NO_IMPERSONATION_TOKEN}, + {"NT_STATUS_CANT_DISABLE_MANDATORY", + NT_STATUS_CANT_DISABLE_MANDATORY}, + {"NT_STATUS_NO_LOGON_SERVERS", NT_STATUS_NO_LOGON_SERVERS}, + {"NT_STATUS_NO_SUCH_LOGON_SESSION", + NT_STATUS_NO_SUCH_LOGON_SESSION}, + {"NT_STATUS_NO_SUCH_PRIVILEGE", NT_STATUS_NO_SUCH_PRIVILEGE}, + {"NT_STATUS_PRIVILEGE_NOT_HELD", NT_STATUS_PRIVILEGE_NOT_HELD}, + {"NT_STATUS_INVALID_ACCOUNT_NAME", NT_STATUS_INVALID_ACCOUNT_NAME}, + {"NT_STATUS_USER_EXISTS", NT_STATUS_USER_EXISTS}, + {"NT_STATUS_NO_SUCH_USER", NT_STATUS_NO_SUCH_USER}, + {"NT_STATUS_GROUP_EXISTS", NT_STATUS_GROUP_EXISTS}, + {"NT_STATUS_NO_SUCH_GROUP", NT_STATUS_NO_SUCH_GROUP}, + {"NT_STATUS_MEMBER_IN_GROUP", NT_STATUS_MEMBER_IN_GROUP}, + {"NT_STATUS_MEMBER_NOT_IN_GROUP", NT_STATUS_MEMBER_NOT_IN_GROUP}, + {"NT_STATUS_LAST_ADMIN", NT_STATUS_LAST_ADMIN}, + {"NT_STATUS_WRONG_PASSWORD", NT_STATUS_WRONG_PASSWORD}, + {"NT_STATUS_ILL_FORMED_PASSWORD", NT_STATUS_ILL_FORMED_PASSWORD}, + {"NT_STATUS_PASSWORD_RESTRICTION", NT_STATUS_PASSWORD_RESTRICTION}, + {"NT_STATUS_LOGON_FAILURE", NT_STATUS_LOGON_FAILURE}, + {"NT_STATUS_ACCOUNT_RESTRICTION", NT_STATUS_ACCOUNT_RESTRICTION}, + {"NT_STATUS_INVALID_LOGON_HOURS", NT_STATUS_INVALID_LOGON_HOURS}, + {"NT_STATUS_INVALID_WORKSTATION", NT_STATUS_INVALID_WORKSTATION}, + {"NT_STATUS_PASSWORD_EXPIRED", NT_STATUS_PASSWORD_EXPIRED}, + {"NT_STATUS_ACCOUNT_DISABLED", NT_STATUS_ACCOUNT_DISABLED}, + {"NT_STATUS_NONE_MAPPED", NT_STATUS_NONE_MAPPED}, + {"NT_STATUS_TOO_MANY_LUIDS_REQUESTED", + NT_STATUS_TOO_MANY_LUIDS_REQUESTED}, + {"NT_STATUS_LUIDS_EXHAUSTED", NT_STATUS_LUIDS_EXHAUSTED}, + {"NT_STATUS_INVALID_SUB_AUTHORITY", + NT_STATUS_INVALID_SUB_AUTHORITY}, + {"NT_STATUS_INVALID_ACL", NT_STATUS_INVALID_ACL}, + {"NT_STATUS_INVALID_SID", NT_STATUS_INVALID_SID}, + {"NT_STATUS_INVALID_SECURITY_DESCR", + NT_STATUS_INVALID_SECURITY_DESCR}, + {"NT_STATUS_PROCEDURE_NOT_FOUND", NT_STATUS_PROCEDURE_NOT_FOUND}, + {"NT_STATUS_INVALID_IMAGE_FORMAT", NT_STATUS_INVALID_IMAGE_FORMAT}, + {"NT_STATUS_NO_TOKEN", NT_STATUS_NO_TOKEN}, + {"NT_STATUS_BAD_INHERITANCE_ACL", NT_STATUS_BAD_INHERITANCE_ACL}, + {"NT_STATUS_RANGE_NOT_LOCKED", NT_STATUS_RANGE_NOT_LOCKED}, + {"NT_STATUS_DISK_FULL", NT_STATUS_DISK_FULL}, + {"NT_STATUS_SERVER_DISABLED", NT_STATUS_SERVER_DISABLED}, + {"NT_STATUS_SERVER_NOT_DISABLED", NT_STATUS_SERVER_NOT_DISABLED}, + {"NT_STATUS_TOO_MANY_GUIDS_REQUESTED", + NT_STATUS_TOO_MANY_GUIDS_REQUESTED}, + {"NT_STATUS_GUIDS_EXHAUSTED", NT_STATUS_GUIDS_EXHAUSTED}, + {"NT_STATUS_INVALID_ID_AUTHORITY", NT_STATUS_INVALID_ID_AUTHORITY}, + {"NT_STATUS_AGENTS_EXHAUSTED", NT_STATUS_AGENTS_EXHAUSTED}, + {"NT_STATUS_INVALID_VOLUME_LABEL", NT_STATUS_INVALID_VOLUME_LABEL}, + {"NT_STATUS_SECTION_NOT_EXTENDED", NT_STATUS_SECTION_NOT_EXTENDED}, + {"NT_STATUS_NOT_MAPPED_DATA", NT_STATUS_NOT_MAPPED_DATA}, + {"NT_STATUS_RESOURCE_DATA_NOT_FOUND", + NT_STATUS_RESOURCE_DATA_NOT_FOUND}, + {"NT_STATUS_RESOURCE_TYPE_NOT_FOUND", + NT_STATUS_RESOURCE_TYPE_NOT_FOUND}, + {"NT_STATUS_RESOURCE_NAME_NOT_FOUND", + NT_STATUS_RESOURCE_NAME_NOT_FOUND}, + {"NT_STATUS_ARRAY_BOUNDS_EXCEEDED", + NT_STATUS_ARRAY_BOUNDS_EXCEEDED}, + {"NT_STATUS_FLOAT_DENORMAL_OPERAND", + NT_STATUS_FLOAT_DENORMAL_OPERAND}, + {"NT_STATUS_FLOAT_DIVIDE_BY_ZERO", NT_STATUS_FLOAT_DIVIDE_BY_ZERO}, + {"NT_STATUS_FLOAT_INEXACT_RESULT", NT_STATUS_FLOAT_INEXACT_RESULT}, + {"NT_STATUS_FLOAT_INVALID_OPERATION", + NT_STATUS_FLOAT_INVALID_OPERATION}, + {"NT_STATUS_FLOAT_OVERFLOW", NT_STATUS_FLOAT_OVERFLOW}, + {"NT_STATUS_FLOAT_STACK_CHECK", NT_STATUS_FLOAT_STACK_CHECK}, + {"NT_STATUS_FLOAT_UNDERFLOW", NT_STATUS_FLOAT_UNDERFLOW}, + {"NT_STATUS_INTEGER_DIVIDE_BY_ZERO", + NT_STATUS_INTEGER_DIVIDE_BY_ZERO}, + {"NT_STATUS_INTEGER_OVERFLOW", NT_STATUS_INTEGER_OVERFLOW}, + {"NT_STATUS_PRIVILEGED_INSTRUCTION", + NT_STATUS_PRIVILEGED_INSTRUCTION}, + {"NT_STATUS_TOO_MANY_PAGING_FILES", + NT_STATUS_TOO_MANY_PAGING_FILES}, + {"NT_STATUS_FILE_INVALID", NT_STATUS_FILE_INVALID}, + {"NT_STATUS_ALLOTTED_SPACE_EXCEEDED", + NT_STATUS_ALLOTTED_SPACE_EXCEEDED}, + {"NT_STATUS_INSUFFICIENT_RESOURCES", + NT_STATUS_INSUFFICIENT_RESOURCES}, + {"NT_STATUS_DFS_EXIT_PATH_FOUND", NT_STATUS_DFS_EXIT_PATH_FOUND}, + {"NT_STATUS_DEVICE_DATA_ERROR", NT_STATUS_DEVICE_DATA_ERROR}, + {"NT_STATUS_DEVICE_NOT_CONNECTED", NT_STATUS_DEVICE_NOT_CONNECTED}, + {"NT_STATUS_DEVICE_POWER_FAILURE", NT_STATUS_DEVICE_POWER_FAILURE}, + {"NT_STATUS_FREE_VM_NOT_AT_BASE", NT_STATUS_FREE_VM_NOT_AT_BASE}, + {"NT_STATUS_MEMORY_NOT_ALLOCATED", NT_STATUS_MEMORY_NOT_ALLOCATED}, + {"NT_STATUS_WORKING_SET_QUOTA", NT_STATUS_WORKING_SET_QUOTA}, + {"NT_STATUS_MEDIA_WRITE_PROTECTED", + NT_STATUS_MEDIA_WRITE_PROTECTED}, + {"NT_STATUS_DEVICE_NOT_READY", NT_STATUS_DEVICE_NOT_READY}, + {"NT_STATUS_INVALID_GROUP_ATTRIBUTES", + NT_STATUS_INVALID_GROUP_ATTRIBUTES}, + {"NT_STATUS_BAD_IMPERSONATION_LEVEL", + NT_STATUS_BAD_IMPERSONATION_LEVEL}, + {"NT_STATUS_CANT_OPEN_ANONYMOUS", NT_STATUS_CANT_OPEN_ANONYMOUS}, + {"NT_STATUS_BAD_VALIDATION_CLASS", NT_STATUS_BAD_VALIDATION_CLASS}, + {"NT_STATUS_BAD_TOKEN_TYPE", NT_STATUS_BAD_TOKEN_TYPE}, + {"NT_STATUS_BAD_MASTER_BOOT_RECORD", + NT_STATUS_BAD_MASTER_BOOT_RECORD}, + {"NT_STATUS_INSTRUCTION_MISALIGNMENT", + NT_STATUS_INSTRUCTION_MISALIGNMENT}, + {"NT_STATUS_INSTANCE_NOT_AVAILABLE", + NT_STATUS_INSTANCE_NOT_AVAILABLE}, + {"NT_STATUS_PIPE_NOT_AVAILABLE", NT_STATUS_PIPE_NOT_AVAILABLE}, + {"NT_STATUS_INVALID_PIPE_STATE", NT_STATUS_INVALID_PIPE_STATE}, + {"NT_STATUS_PIPE_BUSY", NT_STATUS_PIPE_BUSY}, + {"NT_STATUS_ILLEGAL_FUNCTION", NT_STATUS_ILLEGAL_FUNCTION}, + {"NT_STATUS_PIPE_DISCONNECTED", NT_STATUS_PIPE_DISCONNECTED}, + {"NT_STATUS_PIPE_CLOSING", NT_STATUS_PIPE_CLOSING}, + {"NT_STATUS_PIPE_CONNECTED", NT_STATUS_PIPE_CONNECTED}, + {"NT_STATUS_PIPE_LISTENING", NT_STATUS_PIPE_LISTENING}, + {"NT_STATUS_INVALID_READ_MODE", NT_STATUS_INVALID_READ_MODE}, + {"NT_STATUS_IO_TIMEOUT", NT_STATUS_IO_TIMEOUT}, + {"NT_STATUS_FILE_FORCED_CLOSED", NT_STATUS_FILE_FORCED_CLOSED}, + {"NT_STATUS_PROFILING_NOT_STARTED", + NT_STATUS_PROFILING_NOT_STARTED}, + {"NT_STATUS_PROFILING_NOT_STOPPED", + NT_STATUS_PROFILING_NOT_STOPPED}, + {"NT_STATUS_COULD_NOT_INTERPRET", NT_STATUS_COULD_NOT_INTERPRET}, + {"NT_STATUS_FILE_IS_A_DIRECTORY", NT_STATUS_FILE_IS_A_DIRECTORY}, + {"NT_STATUS_NOT_SUPPORTED", NT_STATUS_NOT_SUPPORTED}, + {"NT_STATUS_REMOTE_NOT_LISTENING", NT_STATUS_REMOTE_NOT_LISTENING}, + {"NT_STATUS_DUPLICATE_NAME", NT_STATUS_DUPLICATE_NAME}, + {"NT_STATUS_BAD_NETWORK_PATH", NT_STATUS_BAD_NETWORK_PATH}, + {"NT_STATUS_NETWORK_BUSY", NT_STATUS_NETWORK_BUSY}, + {"NT_STATUS_DEVICE_DOES_NOT_EXIST", + NT_STATUS_DEVICE_DOES_NOT_EXIST}, + {"NT_STATUS_TOO_MANY_COMMANDS", NT_STATUS_TOO_MANY_COMMANDS}, + {"NT_STATUS_ADAPTER_HARDWARE_ERROR", + NT_STATUS_ADAPTER_HARDWARE_ERROR}, + {"NT_STATUS_INVALID_NETWORK_RESPONSE", + NT_STATUS_INVALID_NETWORK_RESPONSE}, + {"NT_STATUS_UNEXPECTED_NETWORK_ERROR", + NT_STATUS_UNEXPECTED_NETWORK_ERROR}, + {"NT_STATUS_BAD_REMOTE_ADAPTER", NT_STATUS_BAD_REMOTE_ADAPTER}, + {"NT_STATUS_PRINT_QUEUE_FULL", NT_STATUS_PRINT_QUEUE_FULL}, + {"NT_STATUS_NO_SPOOL_SPACE", NT_STATUS_NO_SPOOL_SPACE}, + {"NT_STATUS_PRINT_CANCELLED", NT_STATUS_PRINT_CANCELLED}, + {"NT_STATUS_NETWORK_NAME_DELETED", NT_STATUS_NETWORK_NAME_DELETED}, + {"NT_STATUS_NETWORK_ACCESS_DENIED", + NT_STATUS_NETWORK_ACCESS_DENIED}, + {"NT_STATUS_BAD_DEVICE_TYPE", NT_STATUS_BAD_DEVICE_TYPE}, + {"NT_STATUS_BAD_NETWORK_NAME", NT_STATUS_BAD_NETWORK_NAME}, + {"NT_STATUS_TOO_MANY_NAMES", NT_STATUS_TOO_MANY_NAMES}, + {"NT_STATUS_TOO_MANY_SESSIONS", NT_STATUS_TOO_MANY_SESSIONS}, + {"NT_STATUS_SHARING_PAUSED", NT_STATUS_SHARING_PAUSED}, + {"NT_STATUS_REQUEST_NOT_ACCEPTED", NT_STATUS_REQUEST_NOT_ACCEPTED}, + {"NT_STATUS_REDIRECTOR_PAUSED", NT_STATUS_REDIRECTOR_PAUSED}, + {"NT_STATUS_NET_WRITE_FAULT", NT_STATUS_NET_WRITE_FAULT}, + {"NT_STATUS_PROFILING_AT_LIMIT", NT_STATUS_PROFILING_AT_LIMIT}, + {"NT_STATUS_NOT_SAME_DEVICE", NT_STATUS_NOT_SAME_DEVICE}, + {"NT_STATUS_FILE_RENAMED", NT_STATUS_FILE_RENAMED}, + {"NT_STATUS_VIRTUAL_CIRCUIT_CLOSED", + NT_STATUS_VIRTUAL_CIRCUIT_CLOSED}, + {"NT_STATUS_NO_SECURITY_ON_OBJECT", + NT_STATUS_NO_SECURITY_ON_OBJECT}, + {"NT_STATUS_CANT_WAIT", NT_STATUS_CANT_WAIT}, + {"NT_STATUS_PIPE_EMPTY", NT_STATUS_PIPE_EMPTY}, + {"NT_STATUS_CANT_ACCESS_DOMAIN_INFO", + NT_STATUS_CANT_ACCESS_DOMAIN_INFO}, + {"NT_STATUS_CANT_TERMINATE_SELF", NT_STATUS_CANT_TERMINATE_SELF}, + {"NT_STATUS_INVALID_SERVER_STATE", NT_STATUS_INVALID_SERVER_STATE}, + {"NT_STATUS_INVALID_DOMAIN_STATE", NT_STATUS_INVALID_DOMAIN_STATE}, + {"NT_STATUS_INVALID_DOMAIN_ROLE", NT_STATUS_INVALID_DOMAIN_ROLE}, + {"NT_STATUS_NO_SUCH_DOMAIN", NT_STATUS_NO_SUCH_DOMAIN}, + {"NT_STATUS_DOMAIN_EXISTS", NT_STATUS_DOMAIN_EXISTS}, + {"NT_STATUS_DOMAIN_LIMIT_EXCEEDED", + NT_STATUS_DOMAIN_LIMIT_EXCEEDED}, + {"NT_STATUS_OPLOCK_NOT_GRANTED", NT_STATUS_OPLOCK_NOT_GRANTED}, + {"NT_STATUS_INVALID_OPLOCK_PROTOCOL", + NT_STATUS_INVALID_OPLOCK_PROTOCOL}, + {"NT_STATUS_INTERNAL_DB_CORRUPTION", + NT_STATUS_INTERNAL_DB_CORRUPTION}, + {"NT_STATUS_INTERNAL_ERROR", NT_STATUS_INTERNAL_ERROR}, + {"NT_STATUS_GENERIC_NOT_MAPPED", NT_STATUS_GENERIC_NOT_MAPPED}, + {"NT_STATUS_BAD_DESCRIPTOR_FORMAT", + NT_STATUS_BAD_DESCRIPTOR_FORMAT}, + {"NT_STATUS_INVALID_USER_BUFFER", NT_STATUS_INVALID_USER_BUFFER}, + {"NT_STATUS_UNEXPECTED_IO_ERROR", NT_STATUS_UNEXPECTED_IO_ERROR}, + {"NT_STATUS_UNEXPECTED_MM_CREATE_ERR", + NT_STATUS_UNEXPECTED_MM_CREATE_ERR}, + {"NT_STATUS_UNEXPECTED_MM_MAP_ERROR", + NT_STATUS_UNEXPECTED_MM_MAP_ERROR}, + {"NT_STATUS_UNEXPECTED_MM_EXTEND_ERR", + NT_STATUS_UNEXPECTED_MM_EXTEND_ERR}, + {"NT_STATUS_NOT_LOGON_PROCESS", NT_STATUS_NOT_LOGON_PROCESS}, + {"NT_STATUS_LOGON_SESSION_EXISTS", NT_STATUS_LOGON_SESSION_EXISTS}, + {"NT_STATUS_INVALID_PARAMETER_1", NT_STATUS_INVALID_PARAMETER_1}, + {"NT_STATUS_INVALID_PARAMETER_2", NT_STATUS_INVALID_PARAMETER_2}, + {"NT_STATUS_INVALID_PARAMETER_3", NT_STATUS_INVALID_PARAMETER_3}, + {"NT_STATUS_INVALID_PARAMETER_4", NT_STATUS_INVALID_PARAMETER_4}, + {"NT_STATUS_INVALID_PARAMETER_5", NT_STATUS_INVALID_PARAMETER_5}, + {"NT_STATUS_INVALID_PARAMETER_6", NT_STATUS_INVALID_PARAMETER_6}, + {"NT_STATUS_INVALID_PARAMETER_7", NT_STATUS_INVALID_PARAMETER_7}, + {"NT_STATUS_INVALID_PARAMETER_8", NT_STATUS_INVALID_PARAMETER_8}, + {"NT_STATUS_INVALID_PARAMETER_9", NT_STATUS_INVALID_PARAMETER_9}, + {"NT_STATUS_INVALID_PARAMETER_10", NT_STATUS_INVALID_PARAMETER_10}, + {"NT_STATUS_INVALID_PARAMETER_11", NT_STATUS_INVALID_PARAMETER_11}, + {"NT_STATUS_INVALID_PARAMETER_12", NT_STATUS_INVALID_PARAMETER_12}, + {"NT_STATUS_REDIRECTOR_NOT_STARTED", + NT_STATUS_REDIRECTOR_NOT_STARTED}, + {"NT_STATUS_REDIRECTOR_STARTED", NT_STATUS_REDIRECTOR_STARTED}, + {"NT_STATUS_STACK_OVERFLOW", NT_STATUS_STACK_OVERFLOW}, + {"NT_STATUS_NO_SUCH_PACKAGE", NT_STATUS_NO_SUCH_PACKAGE}, + {"NT_STATUS_BAD_FUNCTION_TABLE", NT_STATUS_BAD_FUNCTION_TABLE}, + {"NT_STATUS_DIRECTORY_NOT_EMPTY", NT_STATUS_DIRECTORY_NOT_EMPTY}, + {"NT_STATUS_FILE_CORRUPT_ERROR", NT_STATUS_FILE_CORRUPT_ERROR}, + {"NT_STATUS_NOT_A_DIRECTORY", NT_STATUS_NOT_A_DIRECTORY}, + {"NT_STATUS_BAD_LOGON_SESSION_STATE", + NT_STATUS_BAD_LOGON_SESSION_STATE}, + {"NT_STATUS_LOGON_SESSION_COLLISION", + NT_STATUS_LOGON_SESSION_COLLISION}, + {"NT_STATUS_NAME_TOO_LONG", NT_STATUS_NAME_TOO_LONG}, + {"NT_STATUS_FILES_OPEN", NT_STATUS_FILES_OPEN}, + {"NT_STATUS_CONNECTION_IN_USE", NT_STATUS_CONNECTION_IN_USE}, + {"NT_STATUS_MESSAGE_NOT_FOUND", NT_STATUS_MESSAGE_NOT_FOUND}, + {"NT_STATUS_PROCESS_IS_TERMINATING", + NT_STATUS_PROCESS_IS_TERMINATING}, + {"NT_STATUS_INVALID_LOGON_TYPE", NT_STATUS_INVALID_LOGON_TYPE}, + {"NT_STATUS_NO_GUID_TRANSLATION", NT_STATUS_NO_GUID_TRANSLATION}, + {"NT_STATUS_CANNOT_IMPERSONATE", NT_STATUS_CANNOT_IMPERSONATE}, + {"NT_STATUS_IMAGE_ALREADY_LOADED", NT_STATUS_IMAGE_ALREADY_LOADED}, + {"NT_STATUS_ABIOS_NOT_PRESENT", NT_STATUS_ABIOS_NOT_PRESENT}, + {"NT_STATUS_ABIOS_LID_NOT_EXIST", NT_STATUS_ABIOS_LID_NOT_EXIST}, + {"NT_STATUS_ABIOS_LID_ALREADY_OWNED", + NT_STATUS_ABIOS_LID_ALREADY_OWNED}, + {"NT_STATUS_ABIOS_NOT_LID_OWNER", NT_STATUS_ABIOS_NOT_LID_OWNER}, + {"NT_STATUS_ABIOS_INVALID_COMMAND", + NT_STATUS_ABIOS_INVALID_COMMAND}, + {"NT_STATUS_ABIOS_INVALID_LID", NT_STATUS_ABIOS_INVALID_LID}, + {"NT_STATUS_ABIOS_SELECTOR_NOT_AVAILABLE", + NT_STATUS_ABIOS_SELECTOR_NOT_AVAILABLE}, + {"NT_STATUS_ABIOS_INVALID_SELECTOR", + NT_STATUS_ABIOS_INVALID_SELECTOR}, + {"NT_STATUS_NO_LDT", NT_STATUS_NO_LDT}, + {"NT_STATUS_INVALID_LDT_SIZE", NT_STATUS_INVALID_LDT_SIZE}, + {"NT_STATUS_INVALID_LDT_OFFSET", NT_STATUS_INVALID_LDT_OFFSET}, + {"NT_STATUS_INVALID_LDT_DESCRIPTOR", + NT_STATUS_INVALID_LDT_DESCRIPTOR}, + {"NT_STATUS_INVALID_IMAGE_NE_FORMAT", + NT_STATUS_INVALID_IMAGE_NE_FORMAT}, + {"NT_STATUS_RXACT_INVALID_STATE", NT_STATUS_RXACT_INVALID_STATE}, + {"NT_STATUS_RXACT_COMMIT_FAILURE", NT_STATUS_RXACT_COMMIT_FAILURE}, + {"NT_STATUS_MAPPED_FILE_SIZE_ZERO", + NT_STATUS_MAPPED_FILE_SIZE_ZERO}, + {"NT_STATUS_TOO_MANY_OPENED_FILES", + NT_STATUS_TOO_MANY_OPENED_FILES}, + {"NT_STATUS_CANCELLED", NT_STATUS_CANCELLED}, + {"NT_STATUS_CANNOT_DELETE", NT_STATUS_CANNOT_DELETE}, + {"NT_STATUS_INVALID_COMPUTER_NAME", + NT_STATUS_INVALID_COMPUTER_NAME}, + {"NT_STATUS_FILE_DELETED", NT_STATUS_FILE_DELETED}, + {"NT_STATUS_SPECIAL_ACCOUNT", NT_STATUS_SPECIAL_ACCOUNT}, + {"NT_STATUS_SPECIAL_GROUP", NT_STATUS_SPECIAL_GROUP}, + {"NT_STATUS_SPECIAL_USER", NT_STATUS_SPECIAL_USER}, + {"NT_STATUS_MEMBERS_PRIMARY_GROUP", + NT_STATUS_MEMBERS_PRIMARY_GROUP}, + {"NT_STATUS_FILE_CLOSED", NT_STATUS_FILE_CLOSED}, + {"NT_STATUS_TOO_MANY_THREADS", NT_STATUS_TOO_MANY_THREADS}, + {"NT_STATUS_THREAD_NOT_IN_PROCESS", + NT_STATUS_THREAD_NOT_IN_PROCESS}, + {"NT_STATUS_TOKEN_ALREADY_IN_USE", NT_STATUS_TOKEN_ALREADY_IN_USE}, + {"NT_STATUS_PAGEFILE_QUOTA_EXCEEDED", + NT_STATUS_PAGEFILE_QUOTA_EXCEEDED}, + {"NT_STATUS_COMMITMENT_LIMIT", NT_STATUS_COMMITMENT_LIMIT}, + {"NT_STATUS_INVALID_IMAGE_LE_FORMAT", + NT_STATUS_INVALID_IMAGE_LE_FORMAT}, + {"NT_STATUS_INVALID_IMAGE_NOT_MZ", NT_STATUS_INVALID_IMAGE_NOT_MZ}, + {"NT_STATUS_INVALID_IMAGE_PROTECT", + NT_STATUS_INVALID_IMAGE_PROTECT}, + {"NT_STATUS_INVALID_IMAGE_WIN_16", NT_STATUS_INVALID_IMAGE_WIN_16}, + {"NT_STATUS_LOGON_SERVER_CONFLICT", + NT_STATUS_LOGON_SERVER_CONFLICT}, + {"NT_STATUS_TIME_DIFFERENCE_AT_DC", + NT_STATUS_TIME_DIFFERENCE_AT_DC}, + {"NT_STATUS_SYNCHRONIZATION_REQUIRED", + NT_STATUS_SYNCHRONIZATION_REQUIRED}, + {"NT_STATUS_DLL_NOT_FOUND", NT_STATUS_DLL_NOT_FOUND}, + {"NT_STATUS_OPEN_FAILED", NT_STATUS_OPEN_FAILED}, + {"NT_STATUS_IO_PRIVILEGE_FAILED", NT_STATUS_IO_PRIVILEGE_FAILED}, + {"NT_STATUS_ORDINAL_NOT_FOUND", NT_STATUS_ORDINAL_NOT_FOUND}, + {"NT_STATUS_ENTRYPOINT_NOT_FOUND", NT_STATUS_ENTRYPOINT_NOT_FOUND}, + {"NT_STATUS_CONTROL_C_EXIT", NT_STATUS_CONTROL_C_EXIT}, + {"NT_STATUS_LOCAL_DISCONNECT", NT_STATUS_LOCAL_DISCONNECT}, + {"NT_STATUS_REMOTE_DISCONNECT", NT_STATUS_REMOTE_DISCONNECT}, + {"NT_STATUS_REMOTE_RESOURCES", NT_STATUS_REMOTE_RESOURCES}, + {"NT_STATUS_LINK_FAILED", NT_STATUS_LINK_FAILED}, + {"NT_STATUS_LINK_TIMEOUT", NT_STATUS_LINK_TIMEOUT}, + {"NT_STATUS_INVALID_CONNECTION", NT_STATUS_INVALID_CONNECTION}, + {"NT_STATUS_INVALID_ADDRESS", NT_STATUS_INVALID_ADDRESS}, + {"NT_STATUS_DLL_INIT_FAILED", NT_STATUS_DLL_INIT_FAILED}, + {"NT_STATUS_MISSING_SYSTEMFILE", NT_STATUS_MISSING_SYSTEMFILE}, + {"NT_STATUS_UNHANDLED_EXCEPTION", NT_STATUS_UNHANDLED_EXCEPTION}, + {"NT_STATUS_APP_INIT_FAILURE", NT_STATUS_APP_INIT_FAILURE}, + {"NT_STATUS_PAGEFILE_CREATE_FAILED", + NT_STATUS_PAGEFILE_CREATE_FAILED}, + {"NT_STATUS_NO_PAGEFILE", NT_STATUS_NO_PAGEFILE}, + {"NT_STATUS_INVALID_LEVEL", NT_STATUS_INVALID_LEVEL}, + {"NT_STATUS_WRONG_PASSWORD_CORE", NT_STATUS_WRONG_PASSWORD_CORE}, + {"NT_STATUS_ILLEGAL_FLOAT_CONTEXT", + NT_STATUS_ILLEGAL_FLOAT_CONTEXT}, + {"NT_STATUS_PIPE_BROKEN", NT_STATUS_PIPE_BROKEN}, + {"NT_STATUS_REGISTRY_CORRUPT", NT_STATUS_REGISTRY_CORRUPT}, + {"NT_STATUS_REGISTRY_IO_FAILED", NT_STATUS_REGISTRY_IO_FAILED}, + {"NT_STATUS_NO_EVENT_PAIR", NT_STATUS_NO_EVENT_PAIR}, + {"NT_STATUS_UNRECOGNIZED_VOLUME", NT_STATUS_UNRECOGNIZED_VOLUME}, + {"NT_STATUS_SERIAL_NO_DEVICE_INITED", + NT_STATUS_SERIAL_NO_DEVICE_INITED}, + {"NT_STATUS_NO_SUCH_ALIAS", NT_STATUS_NO_SUCH_ALIAS}, + {"NT_STATUS_MEMBER_NOT_IN_ALIAS", NT_STATUS_MEMBER_NOT_IN_ALIAS}, + {"NT_STATUS_MEMBER_IN_ALIAS", NT_STATUS_MEMBER_IN_ALIAS}, + {"NT_STATUS_ALIAS_EXISTS", NT_STATUS_ALIAS_EXISTS}, + {"NT_STATUS_LOGON_NOT_GRANTED", NT_STATUS_LOGON_NOT_GRANTED}, + {"NT_STATUS_TOO_MANY_SECRETS", NT_STATUS_TOO_MANY_SECRETS}, + {"NT_STATUS_SECRET_TOO_LONG", NT_STATUS_SECRET_TOO_LONG}, + {"NT_STATUS_INTERNAL_DB_ERROR", NT_STATUS_INTERNAL_DB_ERROR}, + {"NT_STATUS_FULLSCREEN_MODE", NT_STATUS_FULLSCREEN_MODE}, + {"NT_STATUS_TOO_MANY_CONTEXT_IDS", NT_STATUS_TOO_MANY_CONTEXT_IDS}, + {"NT_STATUS_LOGON_TYPE_NOT_GRANTED", + NT_STATUS_LOGON_TYPE_NOT_GRANTED}, + {"NT_STATUS_NOT_REGISTRY_FILE", NT_STATUS_NOT_REGISTRY_FILE}, + {"NT_STATUS_NT_CROSS_ENCRYPTION_REQUIRED", + NT_STATUS_NT_CROSS_ENCRYPTION_REQUIRED}, + {"NT_STATUS_DOMAIN_CTRLR_CONFIG_ERROR", + NT_STATUS_DOMAIN_CTRLR_CONFIG_ERROR}, + {"NT_STATUS_FT_MISSING_MEMBER", NT_STATUS_FT_MISSING_MEMBER}, + {"NT_STATUS_ILL_FORMED_SERVICE_ENTRY", + NT_STATUS_ILL_FORMED_SERVICE_ENTRY}, + {"NT_STATUS_ILLEGAL_CHARACTER", NT_STATUS_ILLEGAL_CHARACTER}, + {"NT_STATUS_UNMAPPABLE_CHARACTER", NT_STATUS_UNMAPPABLE_CHARACTER}, + {"NT_STATUS_UNDEFINED_CHARACTER", NT_STATUS_UNDEFINED_CHARACTER}, + {"NT_STATUS_FLOPPY_VOLUME", NT_STATUS_FLOPPY_VOLUME}, + {"NT_STATUS_FLOPPY_ID_MARK_NOT_FOUND", + NT_STATUS_FLOPPY_ID_MARK_NOT_FOUND}, + {"NT_STATUS_FLOPPY_WRONG_CYLINDER", + NT_STATUS_FLOPPY_WRONG_CYLINDER}, + {"NT_STATUS_FLOPPY_UNKNOWN_ERROR", NT_STATUS_FLOPPY_UNKNOWN_ERROR}, + {"NT_STATUS_FLOPPY_BAD_REGISTERS", NT_STATUS_FLOPPY_BAD_REGISTERS}, + {"NT_STATUS_DISK_RECALIBRATE_FAILED", + NT_STATUS_DISK_RECALIBRATE_FAILED}, + {"NT_STATUS_DISK_OPERATION_FAILED", + NT_STATUS_DISK_OPERATION_FAILED}, + {"NT_STATUS_DISK_RESET_FAILED", NT_STATUS_DISK_RESET_FAILED}, + {"NT_STATUS_SHARED_IRQ_BUSY", NT_STATUS_SHARED_IRQ_BUSY}, + {"NT_STATUS_FT_ORPHANING", NT_STATUS_FT_ORPHANING}, + {"NT_STATUS_PARTITION_FAILURE", NT_STATUS_PARTITION_FAILURE}, + {"NT_STATUS_INVALID_BLOCK_LENGTH", NT_STATUS_INVALID_BLOCK_LENGTH}, + {"NT_STATUS_DEVICE_NOT_PARTITIONED", + NT_STATUS_DEVICE_NOT_PARTITIONED}, + {"NT_STATUS_UNABLE_TO_LOCK_MEDIA", NT_STATUS_UNABLE_TO_LOCK_MEDIA}, + {"NT_STATUS_UNABLE_TO_UNLOAD_MEDIA", + NT_STATUS_UNABLE_TO_UNLOAD_MEDIA}, + {"NT_STATUS_EOM_OVERFLOW", NT_STATUS_EOM_OVERFLOW}, + {"NT_STATUS_NO_MEDIA", NT_STATUS_NO_MEDIA}, + {"NT_STATUS_NO_SUCH_MEMBER", NT_STATUS_NO_SUCH_MEMBER}, + {"NT_STATUS_INVALID_MEMBER", NT_STATUS_INVALID_MEMBER}, + {"NT_STATUS_KEY_DELETED", NT_STATUS_KEY_DELETED}, + {"NT_STATUS_NO_LOG_SPACE", NT_STATUS_NO_LOG_SPACE}, + {"NT_STATUS_TOO_MANY_SIDS", NT_STATUS_TOO_MANY_SIDS}, + {"NT_STATUS_LM_CROSS_ENCRYPTION_REQUIRED", + NT_STATUS_LM_CROSS_ENCRYPTION_REQUIRED}, + {"NT_STATUS_KEY_HAS_CHILDREN", NT_STATUS_KEY_HAS_CHILDREN}, + {"NT_STATUS_CHILD_MUST_BE_VOLATILE", + NT_STATUS_CHILD_MUST_BE_VOLATILE}, + {"NT_STATUS_DEVICE_CONFIGURATION_ERROR", + NT_STATUS_DEVICE_CONFIGURATION_ERROR}, + {"NT_STATUS_DRIVER_INTERNAL_ERROR", + NT_STATUS_DRIVER_INTERNAL_ERROR}, + {"NT_STATUS_INVALID_DEVICE_STATE", NT_STATUS_INVALID_DEVICE_STATE}, + {"NT_STATUS_IO_DEVICE_ERROR", NT_STATUS_IO_DEVICE_ERROR}, + {"NT_STATUS_DEVICE_PROTOCOL_ERROR", + NT_STATUS_DEVICE_PROTOCOL_ERROR}, + {"NT_STATUS_BACKUP_CONTROLLER", NT_STATUS_BACKUP_CONTROLLER}, + {"NT_STATUS_LOG_FILE_FULL", NT_STATUS_LOG_FILE_FULL}, + {"NT_STATUS_TOO_LATE", NT_STATUS_TOO_LATE}, + {"NT_STATUS_NO_TRUST_LSA_SECRET", NT_STATUS_NO_TRUST_LSA_SECRET}, + {"NT_STATUS_NO_TRUST_SAM_ACCOUNT", NT_STATUS_NO_TRUST_SAM_ACCOUNT}, + {"NT_STATUS_TRUSTED_DOMAIN_FAILURE", + NT_STATUS_TRUSTED_DOMAIN_FAILURE}, + {"NT_STATUS_TRUSTED_RELATIONSHIP_FAILURE", + NT_STATUS_TRUSTED_RELATIONSHIP_FAILURE}, + {"NT_STATUS_EVENTLOG_FILE_CORRUPT", + NT_STATUS_EVENTLOG_FILE_CORRUPT}, + {"NT_STATUS_EVENTLOG_CANT_START", NT_STATUS_EVENTLOG_CANT_START}, + {"NT_STATUS_TRUST_FAILURE", NT_STATUS_TRUST_FAILURE}, + {"NT_STATUS_MUTANT_LIMIT_EXCEEDED", + NT_STATUS_MUTANT_LIMIT_EXCEEDED}, + {"NT_STATUS_NETLOGON_NOT_STARTED", NT_STATUS_NETLOGON_NOT_STARTED}, + {"NT_STATUS_ACCOUNT_EXPIRED", NT_STATUS_ACCOUNT_EXPIRED}, + {"NT_STATUS_POSSIBLE_DEADLOCK", NT_STATUS_POSSIBLE_DEADLOCK}, + {"NT_STATUS_NETWORK_CREDENTIAL_CONFLICT", + NT_STATUS_NETWORK_CREDENTIAL_CONFLICT}, + {"NT_STATUS_REMOTE_SESSION_LIMIT", NT_STATUS_REMOTE_SESSION_LIMIT}, + {"NT_STATUS_EVENTLOG_FILE_CHANGED", + NT_STATUS_EVENTLOG_FILE_CHANGED}, + {"NT_STATUS_NOLOGON_INTERDOMAIN_TRUST_ACCOUNT", + NT_STATUS_NOLOGON_INTERDOMAIN_TRUST_ACCOUNT}, + {"NT_STATUS_NOLOGON_WORKSTATION_TRUST_ACCOUNT", + NT_STATUS_NOLOGON_WORKSTATION_TRUST_ACCOUNT}, + {"NT_STATUS_NOLOGON_SERVER_TRUST_ACCOUNT", + NT_STATUS_NOLOGON_SERVER_TRUST_ACCOUNT}, + {"NT_STATUS_DOMAIN_TRUST_INCONSISTENT", + NT_STATUS_DOMAIN_TRUST_INCONSISTENT}, + {"NT_STATUS_FS_DRIVER_REQUIRED", NT_STATUS_FS_DRIVER_REQUIRED}, + {"NT_STATUS_NO_USER_SESSION_KEY", NT_STATUS_NO_USER_SESSION_KEY}, + {"NT_STATUS_USER_SESSION_DELETED", NT_STATUS_USER_SESSION_DELETED}, + {"NT_STATUS_RESOURCE_LANG_NOT_FOUND", + NT_STATUS_RESOURCE_LANG_NOT_FOUND}, + {"NT_STATUS_INSUFF_SERVER_RESOURCES", + NT_STATUS_INSUFF_SERVER_RESOURCES}, + {"NT_STATUS_INVALID_BUFFER_SIZE", NT_STATUS_INVALID_BUFFER_SIZE}, + {"NT_STATUS_INVALID_ADDRESS_COMPONENT", + NT_STATUS_INVALID_ADDRESS_COMPONENT}, + {"NT_STATUS_INVALID_ADDRESS_WILDCARD", + NT_STATUS_INVALID_ADDRESS_WILDCARD}, + {"NT_STATUS_TOO_MANY_ADDRESSES", NT_STATUS_TOO_MANY_ADDRESSES}, + {"NT_STATUS_ADDRESS_ALREADY_EXISTS", + NT_STATUS_ADDRESS_ALREADY_EXISTS}, + {"NT_STATUS_ADDRESS_CLOSED", NT_STATUS_ADDRESS_CLOSED}, + {"NT_STATUS_CONNECTION_DISCONNECTED", + NT_STATUS_CONNECTION_DISCONNECTED}, + {"NT_STATUS_CONNECTION_RESET", NT_STATUS_CONNECTION_RESET}, + {"NT_STATUS_TOO_MANY_NODES", NT_STATUS_TOO_MANY_NODES}, + {"NT_STATUS_TRANSACTION_ABORTED", NT_STATUS_TRANSACTION_ABORTED}, + {"NT_STATUS_TRANSACTION_TIMED_OUT", + NT_STATUS_TRANSACTION_TIMED_OUT}, + {"NT_STATUS_TRANSACTION_NO_RELEASE", + NT_STATUS_TRANSACTION_NO_RELEASE}, + {"NT_STATUS_TRANSACTION_NO_MATCH", NT_STATUS_TRANSACTION_NO_MATCH}, + {"NT_STATUS_TRANSACTION_RESPONDED", + NT_STATUS_TRANSACTION_RESPONDED}, + {"NT_STATUS_TRANSACTION_INVALID_ID", + NT_STATUS_TRANSACTION_INVALID_ID}, + {"NT_STATUS_TRANSACTION_INVALID_TYPE", + NT_STATUS_TRANSACTION_INVALID_TYPE}, + {"NT_STATUS_NOT_SERVER_SESSION", NT_STATUS_NOT_SERVER_SESSION}, + {"NT_STATUS_NOT_CLIENT_SESSION", NT_STATUS_NOT_CLIENT_SESSION}, + {"NT_STATUS_CANNOT_LOAD_REGISTRY_FILE", + NT_STATUS_CANNOT_LOAD_REGISTRY_FILE}, + {"NT_STATUS_DEBUG_ATTACH_FAILED", NT_STATUS_DEBUG_ATTACH_FAILED}, + {"NT_STATUS_SYSTEM_PROCESS_TERMINATED", + NT_STATUS_SYSTEM_PROCESS_TERMINATED}, + {"NT_STATUS_DATA_NOT_ACCEPTED", NT_STATUS_DATA_NOT_ACCEPTED}, + {"NT_STATUS_NO_BROWSER_SERVERS_FOUND", + NT_STATUS_NO_BROWSER_SERVERS_FOUND}, + {"NT_STATUS_VDM_HARD_ERROR", NT_STATUS_VDM_HARD_ERROR}, + {"NT_STATUS_DRIVER_CANCEL_TIMEOUT", + NT_STATUS_DRIVER_CANCEL_TIMEOUT}, + {"NT_STATUS_REPLY_MESSAGE_MISMATCH", + NT_STATUS_REPLY_MESSAGE_MISMATCH}, + {"NT_STATUS_MAPPED_ALIGNMENT", NT_STATUS_MAPPED_ALIGNMENT}, + {"NT_STATUS_IMAGE_CHECKSUM_MISMATCH", + NT_STATUS_IMAGE_CHECKSUM_MISMATCH}, + {"NT_STATUS_LOST_WRITEBEHIND_DATA", + NT_STATUS_LOST_WRITEBEHIND_DATA}, + {"NT_STATUS_CLIENT_SERVER_PARAMETERS_INVALID", + NT_STATUS_CLIENT_SERVER_PARAMETERS_INVALID}, + {"NT_STATUS_PASSWORD_MUST_CHANGE", NT_STATUS_PASSWORD_MUST_CHANGE}, + {"NT_STATUS_NOT_FOUND", NT_STATUS_NOT_FOUND}, + {"NT_STATUS_NOT_TINY_STREAM", NT_STATUS_NOT_TINY_STREAM}, + {"NT_STATUS_RECOVERY_FAILURE", NT_STATUS_RECOVERY_FAILURE}, + {"NT_STATUS_STACK_OVERFLOW_READ", NT_STATUS_STACK_OVERFLOW_READ}, + {"NT_STATUS_FAIL_CHECK", NT_STATUS_FAIL_CHECK}, + {"NT_STATUS_DUPLICATE_OBJECTID", NT_STATUS_DUPLICATE_OBJECTID}, + {"NT_STATUS_OBJECTID_EXISTS", NT_STATUS_OBJECTID_EXISTS}, + {"NT_STATUS_CONVERT_TO_LARGE", NT_STATUS_CONVERT_TO_LARGE}, + {"NT_STATUS_RETRY", NT_STATUS_RETRY}, + {"NT_STATUS_FOUND_OUT_OF_SCOPE", NT_STATUS_FOUND_OUT_OF_SCOPE}, + {"NT_STATUS_ALLOCATE_BUCKET", NT_STATUS_ALLOCATE_BUCKET}, + {"NT_STATUS_PROPSET_NOT_FOUND", NT_STATUS_PROPSET_NOT_FOUND}, + {"NT_STATUS_MARSHALL_OVERFLOW", NT_STATUS_MARSHALL_OVERFLOW}, + {"NT_STATUS_INVALID_VARIANT", NT_STATUS_INVALID_VARIANT}, + {"NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND", + NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND}, + {"NT_STATUS_ACCOUNT_LOCKED_OUT", NT_STATUS_ACCOUNT_LOCKED_OUT}, + {"NT_STATUS_HANDLE_NOT_CLOSABLE", NT_STATUS_HANDLE_NOT_CLOSABLE}, + {"NT_STATUS_CONNECTION_REFUSED", NT_STATUS_CONNECTION_REFUSED}, + {"NT_STATUS_GRACEFUL_DISCONNECT", NT_STATUS_GRACEFUL_DISCONNECT}, + {"NT_STATUS_ADDRESS_ALREADY_ASSOCIATED", + NT_STATUS_ADDRESS_ALREADY_ASSOCIATED}, + {"NT_STATUS_ADDRESS_NOT_ASSOCIATED", + NT_STATUS_ADDRESS_NOT_ASSOCIATED}, + {"NT_STATUS_CONNECTION_INVALID", NT_STATUS_CONNECTION_INVALID}, + {"NT_STATUS_CONNECTION_ACTIVE", NT_STATUS_CONNECTION_ACTIVE}, + {"NT_STATUS_NETWORK_UNREACHABLE", NT_STATUS_NETWORK_UNREACHABLE}, + {"NT_STATUS_HOST_UNREACHABLE", NT_STATUS_HOST_UNREACHABLE}, + {"NT_STATUS_PROTOCOL_UNREACHABLE", NT_STATUS_PROTOCOL_UNREACHABLE}, + {"NT_STATUS_PORT_UNREACHABLE", NT_STATUS_PORT_UNREACHABLE}, + {"NT_STATUS_REQUEST_ABORTED", NT_STATUS_REQUEST_ABORTED}, + {"NT_STATUS_CONNECTION_ABORTED", NT_STATUS_CONNECTION_ABORTED}, + {"NT_STATUS_BAD_COMPRESSION_BUFFER", + NT_STATUS_BAD_COMPRESSION_BUFFER}, + {"NT_STATUS_USER_MAPPED_FILE", NT_STATUS_USER_MAPPED_FILE}, + {"NT_STATUS_AUDIT_FAILED", NT_STATUS_AUDIT_FAILED}, + {"NT_STATUS_TIMER_RESOLUTION_NOT_SET", + NT_STATUS_TIMER_RESOLUTION_NOT_SET}, + {"NT_STATUS_CONNECTION_COUNT_LIMIT", + NT_STATUS_CONNECTION_COUNT_LIMIT}, + {"NT_STATUS_LOGIN_TIME_RESTRICTION", + NT_STATUS_LOGIN_TIME_RESTRICTION}, + {"NT_STATUS_LOGIN_WKSTA_RESTRICTION", + NT_STATUS_LOGIN_WKSTA_RESTRICTION}, + {"NT_STATUS_IMAGE_MP_UP_MISMATCH", NT_STATUS_IMAGE_MP_UP_MISMATCH}, + {"NT_STATUS_INSUFFICIENT_LOGON_INFO", + NT_STATUS_INSUFFICIENT_LOGON_INFO}, + {"NT_STATUS_BAD_DLL_ENTRYPOINT", NT_STATUS_BAD_DLL_ENTRYPOINT}, + {"NT_STATUS_BAD_SERVICE_ENTRYPOINT", + NT_STATUS_BAD_SERVICE_ENTRYPOINT}, + {"NT_STATUS_LPC_REPLY_LOST", NT_STATUS_LPC_REPLY_LOST}, + {"NT_STATUS_IP_ADDRESS_CONFLICT1", NT_STATUS_IP_ADDRESS_CONFLICT1}, + {"NT_STATUS_IP_ADDRESS_CONFLICT2", NT_STATUS_IP_ADDRESS_CONFLICT2}, + {"NT_STATUS_REGISTRY_QUOTA_LIMIT", NT_STATUS_REGISTRY_QUOTA_LIMIT}, + {"NT_STATUS_PATH_NOT_COVERED", NT_STATUS_PATH_NOT_COVERED}, + {"NT_STATUS_NO_CALLBACK_ACTIVE", NT_STATUS_NO_CALLBACK_ACTIVE}, + {"NT_STATUS_LICENSE_QUOTA_EXCEEDED", + NT_STATUS_LICENSE_QUOTA_EXCEEDED}, + {"NT_STATUS_PWD_TOO_SHORT", NT_STATUS_PWD_TOO_SHORT}, + {"NT_STATUS_PWD_TOO_RECENT", NT_STATUS_PWD_TOO_RECENT}, + {"NT_STATUS_PWD_HISTORY_CONFLICT", NT_STATUS_PWD_HISTORY_CONFLICT}, + {"NT_STATUS_PLUGPLAY_NO_DEVICE", NT_STATUS_PLUGPLAY_NO_DEVICE}, + {"NT_STATUS_UNSUPPORTED_COMPRESSION", + NT_STATUS_UNSUPPORTED_COMPRESSION}, + {"NT_STATUS_INVALID_HW_PROFILE", NT_STATUS_INVALID_HW_PROFILE}, + {"NT_STATUS_INVALID_PLUGPLAY_DEVICE_PATH", + NT_STATUS_INVALID_PLUGPLAY_DEVICE_PATH}, + {"NT_STATUS_DRIVER_ORDINAL_NOT_FOUND", + NT_STATUS_DRIVER_ORDINAL_NOT_FOUND}, + {"NT_STATUS_DRIVER_ENTRYPOINT_NOT_FOUND", + NT_STATUS_DRIVER_ENTRYPOINT_NOT_FOUND}, + {"NT_STATUS_RESOURCE_NOT_OWNED", NT_STATUS_RESOURCE_NOT_OWNED}, + {"NT_STATUS_TOO_MANY_LINKS", NT_STATUS_TOO_MANY_LINKS}, + {"NT_STATUS_QUOTA_LIST_INCONSISTENT", + NT_STATUS_QUOTA_LIST_INCONSISTENT}, + {"NT_STATUS_FILE_IS_OFFLINE", NT_STATUS_FILE_IS_OFFLINE}, + {"NT_STATUS_NO_MORE_ENTRIES", NT_STATUS_NO_MORE_ENTRIES}, + {"NT_STATUS_MORE_ENTRIES", NT_STATUS_MORE_ENTRIES}, + {"NT_STATUS_SOME_UNMAPPED", NT_STATUS_SOME_UNMAPPED}, + {NULL, 0} +}; diff --git a/fs/smb/client/nterr.h b/fs/smb/client/nterr.h new file mode 100644 index 0000000000000..edd4741cab0a1 --- /dev/null +++ b/fs/smb/client/nterr.h @@ -0,0 +1,551 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + Unix SMB/Netbios implementation. + Version 1.9. + NT error code constants + Copyright (C) Andrew Tridgell 1992-2000 + Copyright (C) John H Terpstra 1996-2000 + Copyright (C) Luke Kenneth Casson Leighton 1996-2000 + Copyright (C) Paul Ashton 1998-2000 + +*/ + + + +#ifndef _NTERR_H +#define _NTERR_H + +struct nt_err_code_struct { + char *nt_errstr; + __u32 nt_errcode; +}; + +extern const struct nt_err_code_struct nt_errs[]; + +/* Win32 Status codes. */ +#define NT_STATUS_MORE_ENTRIES 0x0105 +#define NT_ERROR_INVALID_PARAMETER 0x0057 +#define NT_ERROR_INSUFFICIENT_BUFFER 0x007a +#define NT_STATUS_1804 0x070c +#define NT_STATUS_NOTIFY_ENUM_DIR 0x010c + +/* + * Win32 Error codes extracted using a loop in smbclient then printing a netmon + * sniff to a file. + */ + +#define NT_STATUS_OK 0x0000 +#define NT_STATUS_SOME_UNMAPPED 0x0107 +#define NT_STATUS_BUFFER_OVERFLOW 0x80000005 +#define NT_STATUS_NO_MORE_ENTRIES 0x8000001a +#define NT_STATUS_MEDIA_CHANGED 0x8000001c +#define NT_STATUS_END_OF_MEDIA 0x8000001e +#define NT_STATUS_MEDIA_CHECK 0x80000020 +#define NT_STATUS_NO_DATA_DETECTED 0x8000001c +#define NT_STATUS_STOPPED_ON_SYMLINK 0x8000002d +#define NT_STATUS_DEVICE_REQUIRES_CLEANING 0x80000288 +#define NT_STATUS_DEVICE_DOOR_OPEN 0x80000288 +#define NT_STATUS_UNSUCCESSFUL 0xC0000000 | 0x0001 +#define NT_STATUS_NOT_IMPLEMENTED 0xC0000000 | 0x0002 +#define NT_STATUS_INVALID_INFO_CLASS 0xC0000000 | 0x0003 +#define NT_STATUS_INFO_LENGTH_MISMATCH 0xC0000000 | 0x0004 +#define NT_STATUS_ACCESS_VIOLATION 0xC0000000 | 0x0005 +#define NT_STATUS_IN_PAGE_ERROR 0xC0000000 | 0x0006 +#define NT_STATUS_PAGEFILE_QUOTA 0xC0000000 | 0x0007 +#define NT_STATUS_INVALID_HANDLE 0xC0000000 | 0x0008 +#define NT_STATUS_BAD_INITIAL_STACK 0xC0000000 | 0x0009 +#define NT_STATUS_BAD_INITIAL_PC 0xC0000000 | 0x000a +#define NT_STATUS_INVALID_CID 0xC0000000 | 0x000b +#define NT_STATUS_TIMER_NOT_CANCELED 0xC0000000 | 0x000c +#define NT_STATUS_INVALID_PARAMETER 0xC0000000 | 0x000d +#define NT_STATUS_NO_SUCH_DEVICE 0xC0000000 | 0x000e +#define NT_STATUS_NO_SUCH_FILE 0xC0000000 | 0x000f +#define NT_STATUS_INVALID_DEVICE_REQUEST 0xC0000000 | 0x0010 +#define NT_STATUS_END_OF_FILE 0xC0000000 | 0x0011 +#define NT_STATUS_WRONG_VOLUME 0xC0000000 | 0x0012 +#define NT_STATUS_NO_MEDIA_IN_DEVICE 0xC0000000 | 0x0013 +#define NT_STATUS_UNRECOGNIZED_MEDIA 0xC0000000 | 0x0014 +#define NT_STATUS_NONEXISTENT_SECTOR 0xC0000000 | 0x0015 +#define NT_STATUS_MORE_PROCESSING_REQUIRED 0xC0000000 | 0x0016 +#define NT_STATUS_NO_MEMORY 0xC0000000 | 0x0017 +#define NT_STATUS_CONFLICTING_ADDRESSES 0xC0000000 | 0x0018 +#define NT_STATUS_NOT_MAPPED_VIEW 0xC0000000 | 0x0019 +#define NT_STATUS_UNABLE_TO_FREE_VM 0x80000000 | 0x001a +#define NT_STATUS_UNABLE_TO_DELETE_SECTION 0xC0000000 | 0x001b +#define NT_STATUS_INVALID_SYSTEM_SERVICE 0xC0000000 | 0x001c +#define NT_STATUS_ILLEGAL_INSTRUCTION 0xC0000000 | 0x001d +#define NT_STATUS_INVALID_LOCK_SEQUENCE 0xC0000000 | 0x001e +#define NT_STATUS_INVALID_VIEW_SIZE 0xC0000000 | 0x001f +#define NT_STATUS_INVALID_FILE_FOR_SECTION 0xC0000000 | 0x0020 +#define NT_STATUS_ALREADY_COMMITTED 0xC0000000 | 0x0021 +#define NT_STATUS_ACCESS_DENIED 0xC0000000 | 0x0022 +#define NT_STATUS_BUFFER_TOO_SMALL 0xC0000000 | 0x0023 +#define NT_STATUS_OBJECT_TYPE_MISMATCH 0xC0000000 | 0x0024 +#define NT_STATUS_NONCONTINUABLE_EXCEPTION 0xC0000000 | 0x0025 +#define NT_STATUS_INVALID_DISPOSITION 0xC0000000 | 0x0026 +#define NT_STATUS_UNWIND 0xC0000000 | 0x0027 +#define NT_STATUS_BAD_STACK 0xC0000000 | 0x0028 +#define NT_STATUS_INVALID_UNWIND_TARGET 0xC0000000 | 0x0029 +#define NT_STATUS_NOT_LOCKED 0xC0000000 | 0x002a +#define NT_STATUS_PARITY_ERROR 0xC0000000 | 0x002b +#define NT_STATUS_UNABLE_TO_DECOMMIT_VM 0xC0000000 | 0x002c +#define NT_STATUS_NOT_COMMITTED 0xC0000000 | 0x002d +#define NT_STATUS_INVALID_PORT_ATTRIBUTES 0xC0000000 | 0x002e +#define NT_STATUS_PORT_MESSAGE_TOO_LONG 0xC0000000 | 0x002f +#define NT_STATUS_INVALID_PARAMETER_MIX 0xC0000000 | 0x0030 +#define NT_STATUS_INVALID_QUOTA_LOWER 0xC0000000 | 0x0031 +#define NT_STATUS_DISK_CORRUPT_ERROR 0xC0000000 | 0x0032 +#define NT_STATUS_OBJECT_NAME_INVALID 0xC0000000 | 0x0033 +#define NT_STATUS_OBJECT_NAME_NOT_FOUND 0xC0000000 | 0x0034 +#define NT_STATUS_OBJECT_NAME_COLLISION 0xC0000000 | 0x0035 +#define NT_STATUS_HANDLE_NOT_WAITABLE 0xC0000000 | 0x0036 +#define NT_STATUS_PORT_DISCONNECTED 0xC0000000 | 0x0037 +#define NT_STATUS_DEVICE_ALREADY_ATTACHED 0xC0000000 | 0x0038 +#define NT_STATUS_OBJECT_PATH_INVALID 0xC0000000 | 0x0039 +#define NT_STATUS_OBJECT_PATH_NOT_FOUND 0xC0000000 | 0x003a +#define NT_STATUS_OBJECT_PATH_SYNTAX_BAD 0xC0000000 | 0x003b +#define NT_STATUS_DATA_OVERRUN 0xC0000000 | 0x003c +#define NT_STATUS_DATA_LATE_ERROR 0xC0000000 | 0x003d +#define NT_STATUS_DATA_ERROR 0xC0000000 | 0x003e +#define NT_STATUS_CRC_ERROR 0xC0000000 | 0x003f +#define NT_STATUS_SECTION_TOO_BIG 0xC0000000 | 0x0040 +#define NT_STATUS_PORT_CONNECTION_REFUSED 0xC0000000 | 0x0041 +#define NT_STATUS_INVALID_PORT_HANDLE 0xC0000000 | 0x0042 +#define NT_STATUS_SHARING_VIOLATION 0xC0000000 | 0x0043 +#define NT_STATUS_QUOTA_EXCEEDED 0xC0000000 | 0x0044 +#define NT_STATUS_INVALID_PAGE_PROTECTION 0xC0000000 | 0x0045 +#define NT_STATUS_MUTANT_NOT_OWNED 0xC0000000 | 0x0046 +#define NT_STATUS_SEMAPHORE_LIMIT_EXCEEDED 0xC0000000 | 0x0047 +#define NT_STATUS_PORT_ALREADY_SET 0xC0000000 | 0x0048 +#define NT_STATUS_SECTION_NOT_IMAGE 0xC0000000 | 0x0049 +#define NT_STATUS_SUSPEND_COUNT_EXCEEDED 0xC0000000 | 0x004a +#define NT_STATUS_THREAD_IS_TERMINATING 0xC0000000 | 0x004b +#define NT_STATUS_BAD_WORKING_SET_LIMIT 0xC0000000 | 0x004c +#define NT_STATUS_INCOMPATIBLE_FILE_MAP 0xC0000000 | 0x004d +#define NT_STATUS_SECTION_PROTECTION 0xC0000000 | 0x004e +#define NT_STATUS_EAS_NOT_SUPPORTED 0xC0000000 | 0x004f +#define NT_STATUS_EA_TOO_LARGE 0xC0000000 | 0x0050 +#define NT_STATUS_NONEXISTENT_EA_ENTRY 0xC0000000 | 0x0051 +#define NT_STATUS_NO_EAS_ON_FILE 0xC0000000 | 0x0052 +#define NT_STATUS_EA_CORRUPT_ERROR 0xC0000000 | 0x0053 +#define NT_STATUS_FILE_LOCK_CONFLICT 0xC0000000 | 0x0054 +#define NT_STATUS_LOCK_NOT_GRANTED 0xC0000000 | 0x0055 +#define NT_STATUS_DELETE_PENDING 0xC0000000 | 0x0056 +#define NT_STATUS_CTL_FILE_NOT_SUPPORTED 0xC0000000 | 0x0057 +#define NT_STATUS_UNKNOWN_REVISION 0xC0000000 | 0x0058 +#define NT_STATUS_REVISION_MISMATCH 0xC0000000 | 0x0059 +#define NT_STATUS_INVALID_OWNER 0xC0000000 | 0x005a +#define NT_STATUS_INVALID_PRIMARY_GROUP 0xC0000000 | 0x005b +#define NT_STATUS_NO_IMPERSONATION_TOKEN 0xC0000000 | 0x005c +#define NT_STATUS_CANT_DISABLE_MANDATORY 0xC0000000 | 0x005d +#define NT_STATUS_NO_LOGON_SERVERS 0xC0000000 | 0x005e +#define NT_STATUS_NO_SUCH_LOGON_SESSION 0xC0000000 | 0x005f +#define NT_STATUS_NO_SUCH_PRIVILEGE 0xC0000000 | 0x0060 +#define NT_STATUS_PRIVILEGE_NOT_HELD 0xC0000000 | 0x0061 +#define NT_STATUS_INVALID_ACCOUNT_NAME 0xC0000000 | 0x0062 +#define NT_STATUS_USER_EXISTS 0xC0000000 | 0x0063 +#define NT_STATUS_NO_SUCH_USER 0xC0000000 | 0x0064 +#define NT_STATUS_GROUP_EXISTS 0xC0000000 | 0x0065 +#define NT_STATUS_NO_SUCH_GROUP 0xC0000000 | 0x0066 +#define NT_STATUS_MEMBER_IN_GROUP 0xC0000000 | 0x0067 +#define NT_STATUS_MEMBER_NOT_IN_GROUP 0xC0000000 | 0x0068 +#define NT_STATUS_LAST_ADMIN 0xC0000000 | 0x0069 +#define NT_STATUS_WRONG_PASSWORD 0xC0000000 | 0x006a +#define NT_STATUS_ILL_FORMED_PASSWORD 0xC0000000 | 0x006b +#define NT_STATUS_PASSWORD_RESTRICTION 0xC0000000 | 0x006c +#define NT_STATUS_LOGON_FAILURE 0xC0000000 | 0x006d +#define NT_STATUS_ACCOUNT_RESTRICTION 0xC0000000 | 0x006e +#define NT_STATUS_INVALID_LOGON_HOURS 0xC0000000 | 0x006f +#define NT_STATUS_INVALID_WORKSTATION 0xC0000000 | 0x0070 +#define NT_STATUS_PASSWORD_EXPIRED 0xC0000000 | 0x0071 +#define NT_STATUS_ACCOUNT_DISABLED 0xC0000000 | 0x0072 +#define NT_STATUS_NONE_MAPPED 0xC0000000 | 0x0073 +#define NT_STATUS_TOO_MANY_LUIDS_REQUESTED 0xC0000000 | 0x0074 +#define NT_STATUS_LUIDS_EXHAUSTED 0xC0000000 | 0x0075 +#define NT_STATUS_INVALID_SUB_AUTHORITY 0xC0000000 | 0x0076 +#define NT_STATUS_INVALID_ACL 0xC0000000 | 0x0077 +#define NT_STATUS_INVALID_SID 0xC0000000 | 0x0078 +#define NT_STATUS_INVALID_SECURITY_DESCR 0xC0000000 | 0x0079 +#define NT_STATUS_PROCEDURE_NOT_FOUND 0xC0000000 | 0x007a +#define NT_STATUS_INVALID_IMAGE_FORMAT 0xC0000000 | 0x007b +#define NT_STATUS_NO_TOKEN 0xC0000000 | 0x007c +#define NT_STATUS_BAD_INHERITANCE_ACL 0xC0000000 | 0x007d +#define NT_STATUS_RANGE_NOT_LOCKED 0xC0000000 | 0x007e +#define NT_STATUS_DISK_FULL 0xC0000000 | 0x007f +#define NT_STATUS_SERVER_DISABLED 0xC0000000 | 0x0080 +#define NT_STATUS_SERVER_NOT_DISABLED 0xC0000000 | 0x0081 +#define NT_STATUS_TOO_MANY_GUIDS_REQUESTED 0xC0000000 | 0x0082 +#define NT_STATUS_GUIDS_EXHAUSTED 0xC0000000 | 0x0083 +#define NT_STATUS_INVALID_ID_AUTHORITY 0xC0000000 | 0x0084 +#define NT_STATUS_AGENTS_EXHAUSTED 0xC0000000 | 0x0085 +#define NT_STATUS_INVALID_VOLUME_LABEL 0xC0000000 | 0x0086 +#define NT_STATUS_SECTION_NOT_EXTENDED 0xC0000000 | 0x0087 +#define NT_STATUS_NOT_MAPPED_DATA 0xC0000000 | 0x0088 +#define NT_STATUS_RESOURCE_DATA_NOT_FOUND 0xC0000000 | 0x0089 +#define NT_STATUS_RESOURCE_TYPE_NOT_FOUND 0xC0000000 | 0x008a +#define NT_STATUS_RESOURCE_NAME_NOT_FOUND 0xC0000000 | 0x008b +#define NT_STATUS_ARRAY_BOUNDS_EXCEEDED 0xC0000000 | 0x008c +#define NT_STATUS_FLOAT_DENORMAL_OPERAND 0xC0000000 | 0x008d +#define NT_STATUS_FLOAT_DIVIDE_BY_ZERO 0xC0000000 | 0x008e +#define NT_STATUS_FLOAT_INEXACT_RESULT 0xC0000000 | 0x008f +#define NT_STATUS_FLOAT_INVALID_OPERATION 0xC0000000 | 0x0090 +#define NT_STATUS_FLOAT_OVERFLOW 0xC0000000 | 0x0091 +#define NT_STATUS_FLOAT_STACK_CHECK 0xC0000000 | 0x0092 +#define NT_STATUS_FLOAT_UNDERFLOW 0xC0000000 | 0x0093 +#define NT_STATUS_INTEGER_DIVIDE_BY_ZERO 0xC0000000 | 0x0094 +#define NT_STATUS_INTEGER_OVERFLOW 0xC0000000 | 0x0095 +#define NT_STATUS_PRIVILEGED_INSTRUCTION 0xC0000000 | 0x0096 +#define NT_STATUS_TOO_MANY_PAGING_FILES 0xC0000000 | 0x0097 +#define NT_STATUS_FILE_INVALID 0xC0000000 | 0x0098 +#define NT_STATUS_ALLOTTED_SPACE_EXCEEDED 0xC0000000 | 0x0099 +#define NT_STATUS_INSUFFICIENT_RESOURCES 0xC0000000 | 0x009a +#define NT_STATUS_DFS_EXIT_PATH_FOUND 0xC0000000 | 0x009b +#define NT_STATUS_DEVICE_DATA_ERROR 0xC0000000 | 0x009c +#define NT_STATUS_DEVICE_NOT_CONNECTED 0xC0000000 | 0x009d +#define NT_STATUS_DEVICE_POWER_FAILURE 0xC0000000 | 0x009e +#define NT_STATUS_FREE_VM_NOT_AT_BASE 0xC0000000 | 0x009f +#define NT_STATUS_MEMORY_NOT_ALLOCATED 0xC0000000 | 0x00a0 +#define NT_STATUS_WORKING_SET_QUOTA 0xC0000000 | 0x00a1 +#define NT_STATUS_MEDIA_WRITE_PROTECTED 0xC0000000 | 0x00a2 +#define NT_STATUS_DEVICE_NOT_READY 0xC0000000 | 0x00a3 +#define NT_STATUS_INVALID_GROUP_ATTRIBUTES 0xC0000000 | 0x00a4 +#define NT_STATUS_BAD_IMPERSONATION_LEVEL 0xC0000000 | 0x00a5 +#define NT_STATUS_CANT_OPEN_ANONYMOUS 0xC0000000 | 0x00a6 +#define NT_STATUS_BAD_VALIDATION_CLASS 0xC0000000 | 0x00a7 +#define NT_STATUS_BAD_TOKEN_TYPE 0xC0000000 | 0x00a8 +#define NT_STATUS_BAD_MASTER_BOOT_RECORD 0xC0000000 | 0x00a9 +#define NT_STATUS_INSTRUCTION_MISALIGNMENT 0xC0000000 | 0x00aa +#define NT_STATUS_INSTANCE_NOT_AVAILABLE 0xC0000000 | 0x00ab +#define NT_STATUS_PIPE_NOT_AVAILABLE 0xC0000000 | 0x00ac +#define NT_STATUS_INVALID_PIPE_STATE 0xC0000000 | 0x00ad +#define NT_STATUS_PIPE_BUSY 0xC0000000 | 0x00ae +#define NT_STATUS_ILLEGAL_FUNCTION 0xC0000000 | 0x00af +#define NT_STATUS_PIPE_DISCONNECTED 0xC0000000 | 0x00b0 +#define NT_STATUS_PIPE_CLOSING 0xC0000000 | 0x00b1 +#define NT_STATUS_PIPE_CONNECTED 0xC0000000 | 0x00b2 +#define NT_STATUS_PIPE_LISTENING 0xC0000000 | 0x00b3 +#define NT_STATUS_INVALID_READ_MODE 0xC0000000 | 0x00b4 +#define NT_STATUS_IO_TIMEOUT 0xC0000000 | 0x00b5 +#define NT_STATUS_FILE_FORCED_CLOSED 0xC0000000 | 0x00b6 +#define NT_STATUS_PROFILING_NOT_STARTED 0xC0000000 | 0x00b7 +#define NT_STATUS_PROFILING_NOT_STOPPED 0xC0000000 | 0x00b8 +#define NT_STATUS_COULD_NOT_INTERPRET 0xC0000000 | 0x00b9 +#define NT_STATUS_FILE_IS_A_DIRECTORY 0xC0000000 | 0x00ba +#define NT_STATUS_NOT_SUPPORTED 0xC0000000 | 0x00bb +#define NT_STATUS_REMOTE_NOT_LISTENING 0xC0000000 | 0x00bc +#define NT_STATUS_DUPLICATE_NAME 0xC0000000 | 0x00bd +#define NT_STATUS_BAD_NETWORK_PATH 0xC0000000 | 0x00be +#define NT_STATUS_NETWORK_BUSY 0xC0000000 | 0x00bf +#define NT_STATUS_DEVICE_DOES_NOT_EXIST 0xC0000000 | 0x00c0 +#define NT_STATUS_TOO_MANY_COMMANDS 0xC0000000 | 0x00c1 +#define NT_STATUS_ADAPTER_HARDWARE_ERROR 0xC0000000 | 0x00c2 +#define NT_STATUS_INVALID_NETWORK_RESPONSE 0xC0000000 | 0x00c3 +#define NT_STATUS_UNEXPECTED_NETWORK_ERROR 0xC0000000 | 0x00c4 +#define NT_STATUS_BAD_REMOTE_ADAPTER 0xC0000000 | 0x00c5 +#define NT_STATUS_PRINT_QUEUE_FULL 0xC0000000 | 0x00c6 +#define NT_STATUS_NO_SPOOL_SPACE 0xC0000000 | 0x00c7 +#define NT_STATUS_PRINT_CANCELLED 0xC0000000 | 0x00c8 +#define NT_STATUS_NETWORK_NAME_DELETED 0xC0000000 | 0x00c9 +#define NT_STATUS_NETWORK_ACCESS_DENIED 0xC0000000 | 0x00ca +#define NT_STATUS_BAD_DEVICE_TYPE 0xC0000000 | 0x00cb +#define NT_STATUS_BAD_NETWORK_NAME 0xC0000000 | 0x00cc +#define NT_STATUS_TOO_MANY_NAMES 0xC0000000 | 0x00cd +#define NT_STATUS_TOO_MANY_SESSIONS 0xC0000000 | 0x00ce +#define NT_STATUS_SHARING_PAUSED 0xC0000000 | 0x00cf +#define NT_STATUS_REQUEST_NOT_ACCEPTED 0xC0000000 | 0x00d0 +#define NT_STATUS_REDIRECTOR_PAUSED 0xC0000000 | 0x00d1 +#define NT_STATUS_NET_WRITE_FAULT 0xC0000000 | 0x00d2 +#define NT_STATUS_PROFILING_AT_LIMIT 0xC0000000 | 0x00d3 +#define NT_STATUS_NOT_SAME_DEVICE 0xC0000000 | 0x00d4 +#define NT_STATUS_FILE_RENAMED 0xC0000000 | 0x00d5 +#define NT_STATUS_VIRTUAL_CIRCUIT_CLOSED 0xC0000000 | 0x00d6 +#define NT_STATUS_NO_SECURITY_ON_OBJECT 0xC0000000 | 0x00d7 +#define NT_STATUS_CANT_WAIT 0xC0000000 | 0x00d8 +#define NT_STATUS_PIPE_EMPTY 0xC0000000 | 0x00d9 +#define NT_STATUS_CANT_ACCESS_DOMAIN_INFO 0xC0000000 | 0x00da +#define NT_STATUS_CANT_TERMINATE_SELF 0xC0000000 | 0x00db +#define NT_STATUS_INVALID_SERVER_STATE 0xC0000000 | 0x00dc +#define NT_STATUS_INVALID_DOMAIN_STATE 0xC0000000 | 0x00dd +#define NT_STATUS_INVALID_DOMAIN_ROLE 0xC0000000 | 0x00de +#define NT_STATUS_NO_SUCH_DOMAIN 0xC0000000 | 0x00df +#define NT_STATUS_DOMAIN_EXISTS 0xC0000000 | 0x00e0 +#define NT_STATUS_DOMAIN_LIMIT_EXCEEDED 0xC0000000 | 0x00e1 +#define NT_STATUS_OPLOCK_NOT_GRANTED 0xC0000000 | 0x00e2 +#define NT_STATUS_INVALID_OPLOCK_PROTOCOL 0xC0000000 | 0x00e3 +#define NT_STATUS_INTERNAL_DB_CORRUPTION 0xC0000000 | 0x00e4 +#define NT_STATUS_INTERNAL_ERROR 0xC0000000 | 0x00e5 +#define NT_STATUS_GENERIC_NOT_MAPPED 0xC0000000 | 0x00e6 +#define NT_STATUS_BAD_DESCRIPTOR_FORMAT 0xC0000000 | 0x00e7 +#define NT_STATUS_INVALID_USER_BUFFER 0xC0000000 | 0x00e8 +#define NT_STATUS_UNEXPECTED_IO_ERROR 0xC0000000 | 0x00e9 +#define NT_STATUS_UNEXPECTED_MM_CREATE_ERR 0xC0000000 | 0x00ea +#define NT_STATUS_UNEXPECTED_MM_MAP_ERROR 0xC0000000 | 0x00eb +#define NT_STATUS_UNEXPECTED_MM_EXTEND_ERR 0xC0000000 | 0x00ec +#define NT_STATUS_NOT_LOGON_PROCESS 0xC0000000 | 0x00ed +#define NT_STATUS_LOGON_SESSION_EXISTS 0xC0000000 | 0x00ee +#define NT_STATUS_INVALID_PARAMETER_1 0xC0000000 | 0x00ef +#define NT_STATUS_INVALID_PARAMETER_2 0xC0000000 | 0x00f0 +#define NT_STATUS_INVALID_PARAMETER_3 0xC0000000 | 0x00f1 +#define NT_STATUS_INVALID_PARAMETER_4 0xC0000000 | 0x00f2 +#define NT_STATUS_INVALID_PARAMETER_5 0xC0000000 | 0x00f3 +#define NT_STATUS_INVALID_PARAMETER_6 0xC0000000 | 0x00f4 +#define NT_STATUS_INVALID_PARAMETER_7 0xC0000000 | 0x00f5 +#define NT_STATUS_INVALID_PARAMETER_8 0xC0000000 | 0x00f6 +#define NT_STATUS_INVALID_PARAMETER_9 0xC0000000 | 0x00f7 +#define NT_STATUS_INVALID_PARAMETER_10 0xC0000000 | 0x00f8 +#define NT_STATUS_INVALID_PARAMETER_11 0xC0000000 | 0x00f9 +#define NT_STATUS_INVALID_PARAMETER_12 0xC0000000 | 0x00fa +#define NT_STATUS_REDIRECTOR_NOT_STARTED 0xC0000000 | 0x00fb +#define NT_STATUS_REDIRECTOR_STARTED 0xC0000000 | 0x00fc +#define NT_STATUS_STACK_OVERFLOW 0xC0000000 | 0x00fd +#define NT_STATUS_NO_SUCH_PACKAGE 0xC0000000 | 0x00fe +#define NT_STATUS_BAD_FUNCTION_TABLE 0xC0000000 | 0x00ff +#define NT_STATUS_DIRECTORY_NOT_EMPTY 0xC0000000 | 0x0101 +#define NT_STATUS_FILE_CORRUPT_ERROR 0xC0000000 | 0x0102 +#define NT_STATUS_NOT_A_DIRECTORY 0xC0000000 | 0x0103 +#define NT_STATUS_BAD_LOGON_SESSION_STATE 0xC0000000 | 0x0104 +#define NT_STATUS_LOGON_SESSION_COLLISION 0xC0000000 | 0x0105 +#define NT_STATUS_NAME_TOO_LONG 0xC0000000 | 0x0106 +#define NT_STATUS_FILES_OPEN 0xC0000000 | 0x0107 +#define NT_STATUS_CONNECTION_IN_USE 0xC0000000 | 0x0108 +#define NT_STATUS_MESSAGE_NOT_FOUND 0xC0000000 | 0x0109 +#define NT_STATUS_PROCESS_IS_TERMINATING 0xC0000000 | 0x010a +#define NT_STATUS_INVALID_LOGON_TYPE 0xC0000000 | 0x010b +#define NT_STATUS_NO_GUID_TRANSLATION 0xC0000000 | 0x010c +#define NT_STATUS_CANNOT_IMPERSONATE 0xC0000000 | 0x010d +#define NT_STATUS_IMAGE_ALREADY_LOADED 0xC0000000 | 0x010e +#define NT_STATUS_ABIOS_NOT_PRESENT 0xC0000000 | 0x010f +#define NT_STATUS_ABIOS_LID_NOT_EXIST 0xC0000000 | 0x0110 +#define NT_STATUS_ABIOS_LID_ALREADY_OWNED 0xC0000000 | 0x0111 +#define NT_STATUS_ABIOS_NOT_LID_OWNER 0xC0000000 | 0x0112 +#define NT_STATUS_ABIOS_INVALID_COMMAND 0xC0000000 | 0x0113 +#define NT_STATUS_ABIOS_INVALID_LID 0xC0000000 | 0x0114 +#define NT_STATUS_ABIOS_SELECTOR_NOT_AVAILABLE 0xC0000000 | 0x0115 +#define NT_STATUS_ABIOS_INVALID_SELECTOR 0xC0000000 | 0x0116 +#define NT_STATUS_NO_LDT 0xC0000000 | 0x0117 +#define NT_STATUS_INVALID_LDT_SIZE 0xC0000000 | 0x0118 +#define NT_STATUS_INVALID_LDT_OFFSET 0xC0000000 | 0x0119 +#define NT_STATUS_INVALID_LDT_DESCRIPTOR 0xC0000000 | 0x011a +#define NT_STATUS_INVALID_IMAGE_NE_FORMAT 0xC0000000 | 0x011b +#define NT_STATUS_RXACT_INVALID_STATE 0xC0000000 | 0x011c +#define NT_STATUS_RXACT_COMMIT_FAILURE 0xC0000000 | 0x011d +#define NT_STATUS_MAPPED_FILE_SIZE_ZERO 0xC0000000 | 0x011e +#define NT_STATUS_TOO_MANY_OPENED_FILES 0xC0000000 | 0x011f +#define NT_STATUS_CANCELLED 0xC0000000 | 0x0120 +#define NT_STATUS_CANNOT_DELETE 0xC0000000 | 0x0121 +#define NT_STATUS_INVALID_COMPUTER_NAME 0xC0000000 | 0x0122 +#define NT_STATUS_FILE_DELETED 0xC0000000 | 0x0123 +#define NT_STATUS_SPECIAL_ACCOUNT 0xC0000000 | 0x0124 +#define NT_STATUS_SPECIAL_GROUP 0xC0000000 | 0x0125 +#define NT_STATUS_SPECIAL_USER 0xC0000000 | 0x0126 +#define NT_STATUS_MEMBERS_PRIMARY_GROUP 0xC0000000 | 0x0127 +#define NT_STATUS_FILE_CLOSED 0xC0000000 | 0x0128 +#define NT_STATUS_TOO_MANY_THREADS 0xC0000000 | 0x0129 +#define NT_STATUS_THREAD_NOT_IN_PROCESS 0xC0000000 | 0x012a +#define NT_STATUS_TOKEN_ALREADY_IN_USE 0xC0000000 | 0x012b +#define NT_STATUS_PAGEFILE_QUOTA_EXCEEDED 0xC0000000 | 0x012c +#define NT_STATUS_COMMITMENT_LIMIT 0xC0000000 | 0x012d +#define NT_STATUS_INVALID_IMAGE_LE_FORMAT 0xC0000000 | 0x012e +#define NT_STATUS_INVALID_IMAGE_NOT_MZ 0xC0000000 | 0x012f +#define NT_STATUS_INVALID_IMAGE_PROTECT 0xC0000000 | 0x0130 +#define NT_STATUS_INVALID_IMAGE_WIN_16 0xC0000000 | 0x0131 +#define NT_STATUS_LOGON_SERVER_CONFLICT 0xC0000000 | 0x0132 +#define NT_STATUS_TIME_DIFFERENCE_AT_DC 0xC0000000 | 0x0133 +#define NT_STATUS_SYNCHRONIZATION_REQUIRED 0xC0000000 | 0x0134 +#define NT_STATUS_DLL_NOT_FOUND 0xC0000000 | 0x0135 +#define NT_STATUS_OPEN_FAILED 0xC0000000 | 0x0136 +#define NT_STATUS_IO_PRIVILEGE_FAILED 0xC0000000 | 0x0137 +#define NT_STATUS_ORDINAL_NOT_FOUND 0xC0000000 | 0x0138 +#define NT_STATUS_ENTRYPOINT_NOT_FOUND 0xC0000000 | 0x0139 +#define NT_STATUS_CONTROL_C_EXIT 0xC0000000 | 0x013a +#define NT_STATUS_LOCAL_DISCONNECT 0xC0000000 | 0x013b +#define NT_STATUS_REMOTE_DISCONNECT 0xC0000000 | 0x013c +#define NT_STATUS_REMOTE_RESOURCES 0xC0000000 | 0x013d +#define NT_STATUS_LINK_FAILED 0xC0000000 | 0x013e +#define NT_STATUS_LINK_TIMEOUT 0xC0000000 | 0x013f +#define NT_STATUS_INVALID_CONNECTION 0xC0000000 | 0x0140 +#define NT_STATUS_INVALID_ADDRESS 0xC0000000 | 0x0141 +#define NT_STATUS_DLL_INIT_FAILED 0xC0000000 | 0x0142 +#define NT_STATUS_MISSING_SYSTEMFILE 0xC0000000 | 0x0143 +#define NT_STATUS_UNHANDLED_EXCEPTION 0xC0000000 | 0x0144 +#define NT_STATUS_APP_INIT_FAILURE 0xC0000000 | 0x0145 +#define NT_STATUS_PAGEFILE_CREATE_FAILED 0xC0000000 | 0x0146 +#define NT_STATUS_NO_PAGEFILE 0xC0000000 | 0x0147 +#define NT_STATUS_INVALID_LEVEL 0xC0000000 | 0x0148 +#define NT_STATUS_WRONG_PASSWORD_CORE 0xC0000000 | 0x0149 +#define NT_STATUS_ILLEGAL_FLOAT_CONTEXT 0xC0000000 | 0x014a +#define NT_STATUS_PIPE_BROKEN 0xC0000000 | 0x014b +#define NT_STATUS_REGISTRY_CORRUPT 0xC0000000 | 0x014c +#define NT_STATUS_REGISTRY_IO_FAILED 0xC0000000 | 0x014d +#define NT_STATUS_NO_EVENT_PAIR 0xC0000000 | 0x014e +#define NT_STATUS_UNRECOGNIZED_VOLUME 0xC0000000 | 0x014f +#define NT_STATUS_SERIAL_NO_DEVICE_INITED 0xC0000000 | 0x0150 +#define NT_STATUS_NO_SUCH_ALIAS 0xC0000000 | 0x0151 +#define NT_STATUS_MEMBER_NOT_IN_ALIAS 0xC0000000 | 0x0152 +#define NT_STATUS_MEMBER_IN_ALIAS 0xC0000000 | 0x0153 +#define NT_STATUS_ALIAS_EXISTS 0xC0000000 | 0x0154 +#define NT_STATUS_LOGON_NOT_GRANTED 0xC0000000 | 0x0155 +#define NT_STATUS_TOO_MANY_SECRETS 0xC0000000 | 0x0156 +#define NT_STATUS_SECRET_TOO_LONG 0xC0000000 | 0x0157 +#define NT_STATUS_INTERNAL_DB_ERROR 0xC0000000 | 0x0158 +#define NT_STATUS_FULLSCREEN_MODE 0xC0000000 | 0x0159 +#define NT_STATUS_TOO_MANY_CONTEXT_IDS 0xC0000000 | 0x015a +#define NT_STATUS_LOGON_TYPE_NOT_GRANTED 0xC0000000 | 0x015b +#define NT_STATUS_NOT_REGISTRY_FILE 0xC0000000 | 0x015c +#define NT_STATUS_NT_CROSS_ENCRYPTION_REQUIRED 0xC0000000 | 0x015d +#define NT_STATUS_DOMAIN_CTRLR_CONFIG_ERROR 0xC0000000 | 0x015e +#define NT_STATUS_FT_MISSING_MEMBER 0xC0000000 | 0x015f +#define NT_STATUS_ILL_FORMED_SERVICE_ENTRY 0xC0000000 | 0x0160 +#define NT_STATUS_ILLEGAL_CHARACTER 0xC0000000 | 0x0161 +#define NT_STATUS_UNMAPPABLE_CHARACTER 0xC0000000 | 0x0162 +#define NT_STATUS_UNDEFINED_CHARACTER 0xC0000000 | 0x0163 +#define NT_STATUS_FLOPPY_VOLUME 0xC0000000 | 0x0164 +#define NT_STATUS_FLOPPY_ID_MARK_NOT_FOUND 0xC0000000 | 0x0165 +#define NT_STATUS_FLOPPY_WRONG_CYLINDER 0xC0000000 | 0x0166 +#define NT_STATUS_FLOPPY_UNKNOWN_ERROR 0xC0000000 | 0x0167 +#define NT_STATUS_FLOPPY_BAD_REGISTERS 0xC0000000 | 0x0168 +#define NT_STATUS_DISK_RECALIBRATE_FAILED 0xC0000000 | 0x0169 +#define NT_STATUS_DISK_OPERATION_FAILED 0xC0000000 | 0x016a +#define NT_STATUS_DISK_RESET_FAILED 0xC0000000 | 0x016b +#define NT_STATUS_SHARED_IRQ_BUSY 0xC0000000 | 0x016c +#define NT_STATUS_FT_ORPHANING 0xC0000000 | 0x016d +#define NT_STATUS_PARTITION_FAILURE 0xC0000000 | 0x0172 +#define NT_STATUS_INVALID_BLOCK_LENGTH 0xC0000000 | 0x0173 +#define NT_STATUS_DEVICE_NOT_PARTITIONED 0xC0000000 | 0x0174 +#define NT_STATUS_UNABLE_TO_LOCK_MEDIA 0xC0000000 | 0x0175 +#define NT_STATUS_UNABLE_TO_UNLOAD_MEDIA 0xC0000000 | 0x0176 +#define NT_STATUS_EOM_OVERFLOW 0xC0000000 | 0x0177 +#define NT_STATUS_NO_MEDIA 0xC0000000 | 0x0178 +#define NT_STATUS_NO_SUCH_MEMBER 0xC0000000 | 0x017a +#define NT_STATUS_INVALID_MEMBER 0xC0000000 | 0x017b +#define NT_STATUS_KEY_DELETED 0xC0000000 | 0x017c +#define NT_STATUS_NO_LOG_SPACE 0xC0000000 | 0x017d +#define NT_STATUS_TOO_MANY_SIDS 0xC0000000 | 0x017e +#define NT_STATUS_LM_CROSS_ENCRYPTION_REQUIRED 0xC0000000 | 0x017f +#define NT_STATUS_KEY_HAS_CHILDREN 0xC0000000 | 0x0180 +#define NT_STATUS_CHILD_MUST_BE_VOLATILE 0xC0000000 | 0x0181 +#define NT_STATUS_DEVICE_CONFIGURATION_ERROR 0xC0000000 | 0x0182 +#define NT_STATUS_DRIVER_INTERNAL_ERROR 0xC0000000 | 0x0183 +#define NT_STATUS_INVALID_DEVICE_STATE 0xC0000000 | 0x0184 +#define NT_STATUS_IO_DEVICE_ERROR 0xC0000000 | 0x0185 +#define NT_STATUS_DEVICE_PROTOCOL_ERROR 0xC0000000 | 0x0186 +#define NT_STATUS_BACKUP_CONTROLLER 0xC0000000 | 0x0187 +#define NT_STATUS_LOG_FILE_FULL 0xC0000000 | 0x0188 +#define NT_STATUS_TOO_LATE 0xC0000000 | 0x0189 +#define NT_STATUS_NO_TRUST_LSA_SECRET 0xC0000000 | 0x018a +#define NT_STATUS_NO_TRUST_SAM_ACCOUNT 0xC0000000 | 0x018b +#define NT_STATUS_TRUSTED_DOMAIN_FAILURE 0xC0000000 | 0x018c +#define NT_STATUS_TRUSTED_RELATIONSHIP_FAILURE 0xC0000000 | 0x018d +#define NT_STATUS_EVENTLOG_FILE_CORRUPT 0xC0000000 | 0x018e +#define NT_STATUS_EVENTLOG_CANT_START 0xC0000000 | 0x018f +#define NT_STATUS_TRUST_FAILURE 0xC0000000 | 0x0190 +#define NT_STATUS_MUTANT_LIMIT_EXCEEDED 0xC0000000 | 0x0191 +#define NT_STATUS_NETLOGON_NOT_STARTED 0xC0000000 | 0x0192 +#define NT_STATUS_ACCOUNT_EXPIRED 0xC0000000 | 0x0193 +#define NT_STATUS_POSSIBLE_DEADLOCK 0xC0000000 | 0x0194 +#define NT_STATUS_NETWORK_CREDENTIAL_CONFLICT 0xC0000000 | 0x0195 +#define NT_STATUS_REMOTE_SESSION_LIMIT 0xC0000000 | 0x0196 +#define NT_STATUS_EVENTLOG_FILE_CHANGED 0xC0000000 | 0x0197 +#define NT_STATUS_NOLOGON_INTERDOMAIN_TRUST_ACCOUNT 0xC0000000 | 0x0198 +#define NT_STATUS_NOLOGON_WORKSTATION_TRUST_ACCOUNT 0xC0000000 | 0x0199 +#define NT_STATUS_NOLOGON_SERVER_TRUST_ACCOUNT 0xC0000000 | 0x019a +#define NT_STATUS_DOMAIN_TRUST_INCONSISTENT 0xC0000000 | 0x019b +#define NT_STATUS_FS_DRIVER_REQUIRED 0xC0000000 | 0x019c +#define NT_STATUS_NO_USER_SESSION_KEY 0xC0000000 | 0x0202 +#define NT_STATUS_USER_SESSION_DELETED 0xC0000000 | 0x0203 +#define NT_STATUS_RESOURCE_LANG_NOT_FOUND 0xC0000000 | 0x0204 +#define NT_STATUS_INSUFF_SERVER_RESOURCES 0xC0000000 | 0x0205 +#define NT_STATUS_INVALID_BUFFER_SIZE 0xC0000000 | 0x0206 +#define NT_STATUS_INVALID_ADDRESS_COMPONENT 0xC0000000 | 0x0207 +#define NT_STATUS_INVALID_ADDRESS_WILDCARD 0xC0000000 | 0x0208 +#define NT_STATUS_TOO_MANY_ADDRESSES 0xC0000000 | 0x0209 +#define NT_STATUS_ADDRESS_ALREADY_EXISTS 0xC0000000 | 0x020a +#define NT_STATUS_ADDRESS_CLOSED 0xC0000000 | 0x020b +#define NT_STATUS_CONNECTION_DISCONNECTED 0xC0000000 | 0x020c +#define NT_STATUS_CONNECTION_RESET 0xC0000000 | 0x020d +#define NT_STATUS_TOO_MANY_NODES 0xC0000000 | 0x020e +#define NT_STATUS_TRANSACTION_ABORTED 0xC0000000 | 0x020f +#define NT_STATUS_TRANSACTION_TIMED_OUT 0xC0000000 | 0x0210 +#define NT_STATUS_TRANSACTION_NO_RELEASE 0xC0000000 | 0x0211 +#define NT_STATUS_TRANSACTION_NO_MATCH 0xC0000000 | 0x0212 +#define NT_STATUS_TRANSACTION_RESPONDED 0xC0000000 | 0x0213 +#define NT_STATUS_TRANSACTION_INVALID_ID 0xC0000000 | 0x0214 +#define NT_STATUS_TRANSACTION_INVALID_TYPE 0xC0000000 | 0x0215 +#define NT_STATUS_NOT_SERVER_SESSION 0xC0000000 | 0x0216 +#define NT_STATUS_NOT_CLIENT_SESSION 0xC0000000 | 0x0217 +#define NT_STATUS_CANNOT_LOAD_REGISTRY_FILE 0xC0000000 | 0x0218 +#define NT_STATUS_DEBUG_ATTACH_FAILED 0xC0000000 | 0x0219 +#define NT_STATUS_SYSTEM_PROCESS_TERMINATED 0xC0000000 | 0x021a +#define NT_STATUS_DATA_NOT_ACCEPTED 0xC0000000 | 0x021b +#define NT_STATUS_NO_BROWSER_SERVERS_FOUND 0xC0000000 | 0x021c +#define NT_STATUS_VDM_HARD_ERROR 0xC0000000 | 0x021d +#define NT_STATUS_DRIVER_CANCEL_TIMEOUT 0xC0000000 | 0x021e +#define NT_STATUS_REPLY_MESSAGE_MISMATCH 0xC0000000 | 0x021f +#define NT_STATUS_MAPPED_ALIGNMENT 0xC0000000 | 0x0220 +#define NT_STATUS_IMAGE_CHECKSUM_MISMATCH 0xC0000000 | 0x0221 +#define NT_STATUS_LOST_WRITEBEHIND_DATA 0xC0000000 | 0x0222 +#define NT_STATUS_CLIENT_SERVER_PARAMETERS_INVALID 0xC0000000 | 0x0223 +#define NT_STATUS_PASSWORD_MUST_CHANGE 0xC0000000 | 0x0224 +#define NT_STATUS_NOT_FOUND 0xC0000000 | 0x0225 +#define NT_STATUS_NOT_TINY_STREAM 0xC0000000 | 0x0226 +#define NT_STATUS_RECOVERY_FAILURE 0xC0000000 | 0x0227 +#define NT_STATUS_STACK_OVERFLOW_READ 0xC0000000 | 0x0228 +#define NT_STATUS_FAIL_CHECK 0xC0000000 | 0x0229 +#define NT_STATUS_DUPLICATE_OBJECTID 0xC0000000 | 0x022a +#define NT_STATUS_OBJECTID_EXISTS 0xC0000000 | 0x022b +#define NT_STATUS_CONVERT_TO_LARGE 0xC0000000 | 0x022c +#define NT_STATUS_RETRY 0xC0000000 | 0x022d +#define NT_STATUS_FOUND_OUT_OF_SCOPE 0xC0000000 | 0x022e +#define NT_STATUS_ALLOCATE_BUCKET 0xC0000000 | 0x022f +#define NT_STATUS_PROPSET_NOT_FOUND 0xC0000000 | 0x0230 +#define NT_STATUS_MARSHALL_OVERFLOW 0xC0000000 | 0x0231 +#define NT_STATUS_INVALID_VARIANT 0xC0000000 | 0x0232 +#define NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND 0xC0000000 | 0x0233 +#define NT_STATUS_ACCOUNT_LOCKED_OUT 0xC0000000 | 0x0234 +#define NT_STATUS_HANDLE_NOT_CLOSABLE 0xC0000000 | 0x0235 +#define NT_STATUS_CONNECTION_REFUSED 0xC0000000 | 0x0236 +#define NT_STATUS_GRACEFUL_DISCONNECT 0xC0000000 | 0x0237 +#define NT_STATUS_ADDRESS_ALREADY_ASSOCIATED 0xC0000000 | 0x0238 +#define NT_STATUS_ADDRESS_NOT_ASSOCIATED 0xC0000000 | 0x0239 +#define NT_STATUS_CONNECTION_INVALID 0xC0000000 | 0x023a +#define NT_STATUS_CONNECTION_ACTIVE 0xC0000000 | 0x023b +#define NT_STATUS_NETWORK_UNREACHABLE 0xC0000000 | 0x023c +#define NT_STATUS_HOST_UNREACHABLE 0xC0000000 | 0x023d +#define NT_STATUS_PROTOCOL_UNREACHABLE 0xC0000000 | 0x023e +#define NT_STATUS_PORT_UNREACHABLE 0xC0000000 | 0x023f +#define NT_STATUS_REQUEST_ABORTED 0xC0000000 | 0x0240 +#define NT_STATUS_CONNECTION_ABORTED 0xC0000000 | 0x0241 +#define NT_STATUS_BAD_COMPRESSION_BUFFER 0xC0000000 | 0x0242 +#define NT_STATUS_USER_MAPPED_FILE 0xC0000000 | 0x0243 +#define NT_STATUS_AUDIT_FAILED 0xC0000000 | 0x0244 +#define NT_STATUS_TIMER_RESOLUTION_NOT_SET 0xC0000000 | 0x0245 +#define NT_STATUS_CONNECTION_COUNT_LIMIT 0xC0000000 | 0x0246 +#define NT_STATUS_LOGIN_TIME_RESTRICTION 0xC0000000 | 0x0247 +#define NT_STATUS_LOGIN_WKSTA_RESTRICTION 0xC0000000 | 0x0248 +#define NT_STATUS_IMAGE_MP_UP_MISMATCH 0xC0000000 | 0x0249 +#define NT_STATUS_INSUFFICIENT_LOGON_INFO 0xC0000000 | 0x0250 +#define NT_STATUS_BAD_DLL_ENTRYPOINT 0xC0000000 | 0x0251 +#define NT_STATUS_BAD_SERVICE_ENTRYPOINT 0xC0000000 | 0x0252 +#define NT_STATUS_LPC_REPLY_LOST 0xC0000000 | 0x0253 +#define NT_STATUS_IP_ADDRESS_CONFLICT1 0xC0000000 | 0x0254 +#define NT_STATUS_IP_ADDRESS_CONFLICT2 0xC0000000 | 0x0255 +#define NT_STATUS_REGISTRY_QUOTA_LIMIT 0xC0000000 | 0x0256 +#define NT_STATUS_PATH_NOT_COVERED 0xC0000000 | 0x0257 +#define NT_STATUS_NO_CALLBACK_ACTIVE 0xC0000000 | 0x0258 +#define NT_STATUS_LICENSE_QUOTA_EXCEEDED 0xC0000000 | 0x0259 +#define NT_STATUS_PWD_TOO_SHORT 0xC0000000 | 0x025a +#define NT_STATUS_PWD_TOO_RECENT 0xC0000000 | 0x025b +#define NT_STATUS_PWD_HISTORY_CONFLICT 0xC0000000 | 0x025c +#define NT_STATUS_PLUGPLAY_NO_DEVICE 0xC0000000 | 0x025e +#define NT_STATUS_UNSUPPORTED_COMPRESSION 0xC0000000 | 0x025f +#define NT_STATUS_INVALID_HW_PROFILE 0xC0000000 | 0x0260 +#define NT_STATUS_INVALID_PLUGPLAY_DEVICE_PATH 0xC0000000 | 0x0261 +#define NT_STATUS_DRIVER_ORDINAL_NOT_FOUND 0xC0000000 | 0x0262 +#define NT_STATUS_DRIVER_ENTRYPOINT_NOT_FOUND 0xC0000000 | 0x0263 +#define NT_STATUS_RESOURCE_NOT_OWNED 0xC0000000 | 0x0264 +#define NT_STATUS_TOO_MANY_LINKS 0xC0000000 | 0x0265 +#define NT_STATUS_QUOTA_LIST_INCONSISTENT 0xC0000000 | 0x0266 +#define NT_STATUS_FILE_IS_OFFLINE 0xC0000000 | 0x0267 +#define NT_STATUS_NO_SUCH_JOB 0xC0000000 | 0xEDE /* scheduler */ + +#endif /* _NTERR_H */ diff --git a/fs/smb/client/ntlmssp.h b/fs/smb/client/ntlmssp.h new file mode 100644 index 0000000000000..55758b9ec877e --- /dev/null +++ b/fs/smb/client/ntlmssp.h @@ -0,0 +1,157 @@ +/* SPDX-License-Identifier: LGPL-2.1 */ +/* + * + * Copyright (c) International Business Machines Corp., 2002,2007 + * Author(s): Steve French (sfrench@us.ibm.com) + * + */ + +#define NTLMSSP_SIGNATURE "NTLMSSP" +/* Message Types */ +#define NtLmNegotiate cpu_to_le32(1) +#define NtLmChallenge cpu_to_le32(2) +#define NtLmAuthenticate cpu_to_le32(3) +#define UnknownMessage cpu_to_le32(8) + +/* Negotiate Flags */ +#define NTLMSSP_NEGOTIATE_UNICODE 0x01 /* Text strings are unicode */ +#define NTLMSSP_NEGOTIATE_OEM 0x02 /* Text strings are in OEM */ +#define NTLMSSP_REQUEST_TARGET 0x04 /* Srv returns its auth realm */ +/* define reserved9 0x08 */ +#define NTLMSSP_NEGOTIATE_SIGN 0x0010 /* Request signing capability */ +#define NTLMSSP_NEGOTIATE_SEAL 0x0020 /* Request confidentiality */ +#define NTLMSSP_NEGOTIATE_DGRAM 0x0040 +#define NTLMSSP_NEGOTIATE_LM_KEY 0x0080 /* Use LM session key */ +/* defined reserved 8 0x0100 */ +#define NTLMSSP_NEGOTIATE_NTLM 0x0200 /* NTLM authentication */ +#define NTLMSSP_NEGOTIATE_NT_ONLY 0x0400 /* Lanman not allowed */ +#define NTLMSSP_ANONYMOUS 0x0800 +#define NTLMSSP_NEGOTIATE_DOMAIN_SUPPLIED 0x1000 /* reserved6 */ +#define NTLMSSP_NEGOTIATE_WORKSTATION_SUPPLIED 0x2000 +#define NTLMSSP_NEGOTIATE_LOCAL_CALL 0x4000 /* client/server same machine */ +#define NTLMSSP_NEGOTIATE_ALWAYS_SIGN 0x8000 /* Sign. All security levels */ +#define NTLMSSP_TARGET_TYPE_DOMAIN 0x10000 +#define NTLMSSP_TARGET_TYPE_SERVER 0x20000 +#define NTLMSSP_TARGET_TYPE_SHARE 0x40000 +#define NTLMSSP_NEGOTIATE_EXTENDED_SEC 0x80000 /* NB:not related to NTLMv2 pwd*/ +/* #define NTLMSSP_REQUEST_INIT_RESP 0x100000 */ +#define NTLMSSP_NEGOTIATE_IDENTIFY 0x100000 +#define NTLMSSP_REQUEST_ACCEPT_RESP 0x200000 /* reserved5 */ +#define NTLMSSP_REQUEST_NON_NT_KEY 0x400000 +#define NTLMSSP_NEGOTIATE_TARGET_INFO 0x800000 +/* #define reserved4 0x1000000 */ +#define NTLMSSP_NEGOTIATE_VERSION 0x2000000 /* we only set for SMB2+ */ +/* #define reserved3 0x4000000 */ +/* #define reserved2 0x8000000 */ +/* #define reserved1 0x10000000 */ +#define NTLMSSP_NEGOTIATE_128 0x20000000 +#define NTLMSSP_NEGOTIATE_KEY_XCH 0x40000000 +#define NTLMSSP_NEGOTIATE_56 0x80000000 + +/* Define AV Pair Field IDs */ +enum av_field_type { + NTLMSSP_AV_EOL = 0, + NTLMSSP_AV_NB_COMPUTER_NAME, + NTLMSSP_AV_NB_DOMAIN_NAME, + NTLMSSP_AV_DNS_COMPUTER_NAME, + NTLMSSP_AV_DNS_DOMAIN_NAME, + NTLMSSP_AV_DNS_TREE_NAME, + NTLMSSP_AV_FLAGS, + NTLMSSP_AV_TIMESTAMP, + NTLMSSP_AV_RESTRICTION, + NTLMSSP_AV_TARGET_NAME, + NTLMSSP_AV_CHANNEL_BINDINGS +}; + +/* Although typedefs are not commonly used for structure definitions */ +/* in the Linux kernel, in this particular case they are useful */ +/* to more closely match the standards document for NTLMSSP from */ +/* OpenGroup and to make the code more closely match the standard in */ +/* appearance */ + +typedef struct _SECURITY_BUFFER { + __le16 Length; + __le16 MaximumLength; + __le32 BufferOffset; /* offset to buffer */ +} __attribute__((packed)) SECURITY_BUFFER; + +typedef struct _NEGOTIATE_MESSAGE { + __u8 Signature[sizeof(NTLMSSP_SIGNATURE)]; + __le32 MessageType; /* NtLmNegotiate = 1 */ + __le32 NegotiateFlags; + SECURITY_BUFFER DomainName; /* RFC 1001 style and ASCII */ + SECURITY_BUFFER WorkstationName; /* RFC 1001 and ASCII */ + /* SECURITY_BUFFER for version info not present since we + do not set the version is present flag */ + char DomainString[0]; + /* followed by WorkstationString */ +} __attribute__((packed)) NEGOTIATE_MESSAGE, *PNEGOTIATE_MESSAGE; + +#define NTLMSSP_REVISION_W2K3 0x0F + +/* See MS-NLMP section 2.2.2.10 */ +struct ntlmssp_version { + __u8 ProductMajorVersion; + __u8 ProductMinorVersion; + __le16 ProductBuild; /* we send the cifs.ko module version here */ + __u8 Reserved[3]; + __u8 NTLMRevisionCurrent; /* currently 0x0F */ +} __packed; + +/* see MS-NLMP section 2.2.1.1 */ +struct negotiate_message { + __u8 Signature[sizeof(NTLMSSP_SIGNATURE)]; + __le32 MessageType; /* NtLmNegotiate = 1 */ + __le32 NegotiateFlags; + SECURITY_BUFFER DomainName; /* RFC 1001 style and ASCII */ + SECURITY_BUFFER WorkstationName; /* RFC 1001 and ASCII */ + struct ntlmssp_version Version; + /* SECURITY_BUFFER */ + char DomainString[]; + /* followed by WorkstationString */ +} __packed; + +typedef struct _CHALLENGE_MESSAGE { + __u8 Signature[sizeof(NTLMSSP_SIGNATURE)]; + __le32 MessageType; /* NtLmChallenge = 2 */ + SECURITY_BUFFER TargetName; + __le32 NegotiateFlags; + __u8 Challenge[CIFS_CRYPTO_KEY_SIZE]; + __u8 Reserved[8]; + SECURITY_BUFFER TargetInfoArray; + /* SECURITY_BUFFER for version info not present since we + do not set the version is present flag */ +} __attribute__((packed)) CHALLENGE_MESSAGE, *PCHALLENGE_MESSAGE; + +typedef struct _AUTHENTICATE_MESSAGE { + __u8 Signature[sizeof(NTLMSSP_SIGNATURE)]; + __le32 MessageType; /* NtLmsAuthenticate = 3 */ + SECURITY_BUFFER LmChallengeResponse; + SECURITY_BUFFER NtChallengeResponse; + SECURITY_BUFFER DomainName; + SECURITY_BUFFER UserName; + SECURITY_BUFFER WorkstationName; + SECURITY_BUFFER SessionKey; + __le32 NegotiateFlags; + /* SECURITY_BUFFER for version info not present since we + do not set the version is present flag */ + char UserString[0]; +} __attribute__((packed)) AUTHENTICATE_MESSAGE, *PAUTHENTICATE_MESSAGE; + +/* + * Size of the session key (crypto key encrypted with the password + */ + +int decode_ntlmssp_challenge(char *bcc_ptr, int blob_len, struct cifs_ses *ses); +int build_ntlmssp_negotiate_blob(unsigned char **pbuffer, u16 *buflen, + struct cifs_ses *ses, + struct TCP_Server_Info *server, + const struct nls_table *nls_cp); +int build_ntlmssp_smb3_negotiate_blob(unsigned char **pbuffer, u16 *buflen, + struct cifs_ses *ses, + struct TCP_Server_Info *server, + const struct nls_table *nls_cp); +int build_ntlmssp_auth_blob(unsigned char **pbuffer, u16 *buflen, + struct cifs_ses *ses, + struct TCP_Server_Info *server, + const struct nls_table *nls_cp); diff --git a/fs/smb/client/readdir.c b/fs/smb/client/readdir.c new file mode 100644 index 0000000000000..2d75ba5aaa8ad --- /dev/null +++ b/fs/smb/client/readdir.c @@ -0,0 +1,1237 @@ +// SPDX-License-Identifier: LGPL-2.1 +/* + * + * Directory search handling + * + * Copyright (C) International Business Machines Corp., 2004, 2008 + * Copyright (C) Red Hat, Inc., 2011 + * Author(s): Steve French (sfrench@us.ibm.com) + * + */ +#include +#include +#include +#include +#include "cifspdu.h" +#include "cifsglob.h" +#include "cifsproto.h" +#include "cifs_unicode.h" +#include "cifs_debug.h" +#include "cifs_fs_sb.h" +#include "cifsfs.h" +#include "smb2proto.h" +#include "fs_context.h" +#include "cached_dir.h" + +/* + * To be safe - for UCS to UTF-8 with strings loaded with the rare long + * characters alloc more to account for such multibyte target UTF-8 + * characters. + */ +#define UNICODE_NAME_MAX ((4 * NAME_MAX) + 2) + +#ifdef CONFIG_CIFS_DEBUG2 +static void dump_cifs_file_struct(struct file *file, char *label) +{ + struct cifsFileInfo *cf; + + if (file) { + cf = file->private_data; + if (cf == NULL) { + cifs_dbg(FYI, "empty cifs private file data\n"); + return; + } + if (cf->invalidHandle) + cifs_dbg(FYI, "Invalid handle\n"); + if (cf->srch_inf.endOfSearch) + cifs_dbg(FYI, "end of search\n"); + if (cf->srch_inf.emptyDir) + cifs_dbg(FYI, "empty dir\n"); + } +} +#else +static inline void dump_cifs_file_struct(struct file *file, char *label) +{ +} +#endif /* DEBUG2 */ + +/* + * Attempt to preload the dcache with the results from the FIND_FIRST/NEXT + * + * Find the dentry that matches "name". If there isn't one, create one. If it's + * a negative dentry or the uniqueid or filetype(mode) changed, + * then drop it and recreate it. + */ +static void +cifs_prime_dcache(struct dentry *parent, struct qstr *name, + struct cifs_fattr *fattr) +{ + struct dentry *dentry, *alias; + struct inode *inode; + struct super_block *sb = parent->d_sb; + struct cifs_sb_info *cifs_sb = CIFS_SB(sb); + DECLARE_WAIT_QUEUE_HEAD_ONSTACK(wq); + + cifs_dbg(FYI, "%s: for %s\n", __func__, name->name); + + dentry = d_hash_and_lookup(parent, name); + if (!dentry) { + /* + * If we know that the inode will need to be revalidated + * immediately, then don't create a new dentry for it. + * We'll end up doing an on the wire call either way and + * this spares us an invalidation. + */ + if (fattr->cf_flags & CIFS_FATTR_NEED_REVAL) + return; +retry: + dentry = d_alloc_parallel(parent, name, &wq); + } + if (IS_ERR(dentry)) + return; + if (!d_in_lookup(dentry)) { + inode = d_inode(dentry); + if (inode) { + if (d_mountpoint(dentry)) { + dput(dentry); + return; + } + /* + * If we're generating inode numbers, then we don't + * want to clobber the existing one with the one that + * the readdir code created. + */ + if (!(cifs_sb->mnt_cifs_flags & CIFS_MOUNT_SERVER_INUM)) + fattr->cf_uniqueid = CIFS_I(inode)->uniqueid; + + /* update inode in place + * if both i_ino and i_mode didn't change */ + if (CIFS_I(inode)->uniqueid == fattr->cf_uniqueid && + cifs_fattr_to_inode(inode, fattr) == 0) { + dput(dentry); + return; + } + } + d_invalidate(dentry); + dput(dentry); + goto retry; + } else { + inode = cifs_iget(sb, fattr); + if (!inode) + inode = ERR_PTR(-ENOMEM); + alias = d_splice_alias(inode, dentry); + d_lookup_done(dentry); + if (alias && !IS_ERR(alias)) + dput(alias); + } + dput(dentry); +} + +static bool reparse_file_needs_reval(const struct cifs_fattr *fattr) +{ + if (!(fattr->cf_cifsattrs & ATTR_REPARSE)) + return false; + /* + * The DFS tags should be only intepreted by server side as per + * MS-FSCC 2.1.2.1, but let's include them anyway. + * + * Besides, if cf_cifstag is unset (0), then we still need it to be + * revalidated to know exactly what reparse point it is. + */ + switch (fattr->cf_cifstag) { + case IO_REPARSE_TAG_DFS: + case IO_REPARSE_TAG_DFSR: + case IO_REPARSE_TAG_SYMLINK: + case IO_REPARSE_TAG_NFS: + case 0: + return true; + } + return false; +} + +static void +cifs_fill_common_info(struct cifs_fattr *fattr, struct cifs_sb_info *cifs_sb) +{ + fattr->cf_uid = cifs_sb->ctx->linux_uid; + fattr->cf_gid = cifs_sb->ctx->linux_gid; + + /* + * The IO_REPARSE_TAG_LX_ tags originally were used by WSL but they + * are preferred by the Linux client in some cases since, unlike + * the NFS reparse tag (or EAs), they don't require an extra query + * to determine which type of special file they represent. + * TODO: go through all documented reparse tags to see if we can + * reasonably map some of them to directories vs. files vs. symlinks + */ + if (fattr->cf_cifsattrs & ATTR_DIRECTORY) { + fattr->cf_mode = S_IFDIR | cifs_sb->ctx->dir_mode; + fattr->cf_dtype = DT_DIR; + } else if (fattr->cf_cifstag == IO_REPARSE_TAG_LX_SYMLINK) { + fattr->cf_mode |= S_IFLNK | cifs_sb->ctx->file_mode; + fattr->cf_dtype = DT_LNK; + } else if (fattr->cf_cifstag == IO_REPARSE_TAG_LX_FIFO) { + fattr->cf_mode |= S_IFIFO | cifs_sb->ctx->file_mode; + fattr->cf_dtype = DT_FIFO; + } else if (fattr->cf_cifstag == IO_REPARSE_TAG_AF_UNIX) { + fattr->cf_mode |= S_IFSOCK | cifs_sb->ctx->file_mode; + fattr->cf_dtype = DT_SOCK; + } else if (fattr->cf_cifstag == IO_REPARSE_TAG_LX_CHR) { + fattr->cf_mode |= S_IFCHR | cifs_sb->ctx->file_mode; + fattr->cf_dtype = DT_CHR; + } else if (fattr->cf_cifstag == IO_REPARSE_TAG_LX_BLK) { + fattr->cf_mode |= S_IFBLK | cifs_sb->ctx->file_mode; + fattr->cf_dtype = DT_BLK; + } else { /* TODO: should we mark some other reparse points (like DFSR) as directories? */ + fattr->cf_mode = S_IFREG | cifs_sb->ctx->file_mode; + fattr->cf_dtype = DT_REG; + } + + /* + * We need to revalidate it further to make a decision about whether it + * is a symbolic link, DFS referral or a reparse point with a direct + * access like junctions, deduplicated files, NFS symlinks. + */ + if (reparse_file_needs_reval(fattr)) + fattr->cf_flags |= CIFS_FATTR_NEED_REVAL; + + /* non-unix readdir doesn't provide nlink */ + fattr->cf_flags |= CIFS_FATTR_UNKNOWN_NLINK; + + if (fattr->cf_cifsattrs & ATTR_READONLY) + fattr->cf_mode &= ~S_IWUGO; + + /* + * We of course don't get ACL info in FIND_FIRST/NEXT results, so + * mark it for revalidation so that "ls -l" will look right. It might + * be super-slow, but if we don't do this then the ownership of files + * may look wrong since the inodes may not have timed out by the time + * "ls" does a stat() call on them. + */ + if ((cifs_sb->mnt_cifs_flags & CIFS_MOUNT_CIFS_ACL) || + (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_MODE_FROM_SID)) + fattr->cf_flags |= CIFS_FATTR_NEED_REVAL; + + if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_UNX_EMUL && + fattr->cf_cifsattrs & ATTR_SYSTEM) { + if (fattr->cf_eof == 0) { + fattr->cf_mode &= ~S_IFMT; + fattr->cf_mode |= S_IFIFO; + fattr->cf_dtype = DT_FIFO; + } else { + /* + * trying to get the type and mode via SFU can be slow, + * so just call those regular files for now, and mark + * for reval + */ + fattr->cf_flags |= CIFS_FATTR_NEED_REVAL; + } + } +} + +/* Fill a cifs_fattr struct with info from SMB_FIND_FILE_POSIX_INFO. */ +static void +cifs_posix_to_fattr(struct cifs_fattr *fattr, struct smb2_posix_info *info, + struct cifs_sb_info *cifs_sb) +{ + struct smb2_posix_info_parsed parsed; + + posix_info_parse(info, NULL, &parsed); + + memset(fattr, 0, sizeof(*fattr)); + fattr->cf_uniqueid = le64_to_cpu(info->Inode); + fattr->cf_bytes = le64_to_cpu(info->AllocationSize); + fattr->cf_eof = le64_to_cpu(info->EndOfFile); + + fattr->cf_atime = cifs_NTtimeToUnix(info->LastAccessTime); + fattr->cf_mtime = cifs_NTtimeToUnix(info->LastWriteTime); + fattr->cf_ctime = cifs_NTtimeToUnix(info->CreationTime); + + fattr->cf_nlink = le32_to_cpu(info->HardLinks); + fattr->cf_cifsattrs = le32_to_cpu(info->DosAttributes); + + /* + * Since we set the inode type below we need to mask off + * to avoid strange results if bits set above. + * XXX: why not make server&client use the type bits? + */ + fattr->cf_mode = le32_to_cpu(info->Mode) & ~S_IFMT; + + cifs_dbg(FYI, "posix fattr: dev %d, reparse %d, mode %o\n", + le32_to_cpu(info->DeviceId), + le32_to_cpu(info->ReparseTag), + le32_to_cpu(info->Mode)); + + if (fattr->cf_cifsattrs & ATTR_DIRECTORY) { + fattr->cf_mode |= S_IFDIR; + fattr->cf_dtype = DT_DIR; + } else { + /* + * mark anything that is not a dir as regular + * file. special files should have the REPARSE + * attribute and will be marked as needing revaluation + */ + fattr->cf_mode |= S_IFREG; + fattr->cf_dtype = DT_REG; + } + + if (reparse_file_needs_reval(fattr)) + fattr->cf_flags |= CIFS_FATTR_NEED_REVAL; + + sid_to_id(cifs_sb, &parsed.owner, fattr, SIDOWNER); + sid_to_id(cifs_sb, &parsed.group, fattr, SIDGROUP); +} + +static void __dir_info_to_fattr(struct cifs_fattr *fattr, const void *info) +{ + const FILE_DIRECTORY_INFO *fi = info; + + memset(fattr, 0, sizeof(*fattr)); + fattr->cf_cifsattrs = le32_to_cpu(fi->ExtFileAttributes); + fattr->cf_eof = le64_to_cpu(fi->EndOfFile); + fattr->cf_bytes = le64_to_cpu(fi->AllocationSize); + fattr->cf_createtime = le64_to_cpu(fi->CreationTime); + fattr->cf_atime = cifs_NTtimeToUnix(fi->LastAccessTime); + fattr->cf_ctime = cifs_NTtimeToUnix(fi->ChangeTime); + fattr->cf_mtime = cifs_NTtimeToUnix(fi->LastWriteTime); +} + +void +cifs_dir_info_to_fattr(struct cifs_fattr *fattr, FILE_DIRECTORY_INFO *info, + struct cifs_sb_info *cifs_sb) +{ + __dir_info_to_fattr(fattr, info); + cifs_fill_common_info(fattr, cifs_sb); +} + +static void cifs_fulldir_info_to_fattr(struct cifs_fattr *fattr, + SEARCH_ID_FULL_DIR_INFO *info, + struct cifs_sb_info *cifs_sb) +{ + __dir_info_to_fattr(fattr, info); + + /* See MS-FSCC 2.4.19 FileIdFullDirectoryInformation */ + if (fattr->cf_cifsattrs & ATTR_REPARSE) + fattr->cf_cifstag = le32_to_cpu(info->EaSize); + cifs_fill_common_info(fattr, cifs_sb); +} + +static void +cifs_std_info_to_fattr(struct cifs_fattr *fattr, FIND_FILE_STANDARD_INFO *info, + struct cifs_sb_info *cifs_sb) +{ + int offset = cifs_sb_master_tcon(cifs_sb)->ses->server->timeAdj; + + memset(fattr, 0, sizeof(*fattr)); + fattr->cf_atime = cnvrtDosUnixTm(info->LastAccessDate, + info->LastAccessTime, offset); + fattr->cf_ctime = cnvrtDosUnixTm(info->LastWriteDate, + info->LastWriteTime, offset); + fattr->cf_mtime = cnvrtDosUnixTm(info->LastWriteDate, + info->LastWriteTime, offset); + + fattr->cf_cifsattrs = le16_to_cpu(info->Attributes); + fattr->cf_bytes = le32_to_cpu(info->AllocationSize); + fattr->cf_eof = le32_to_cpu(info->DataSize); + + cifs_fill_common_info(fattr, cifs_sb); +} + +/* BB eventually need to add the following helper function to + resolve NT_STATUS_STOPPED_ON_SYMLINK return code when + we try to do FindFirst on (NTFS) directory symlinks */ +/* +int get_symlink_reparse_path(char *full_path, struct cifs_sb_info *cifs_sb, + unsigned int xid) +{ + __u16 fid; + int len; + int oplock = 0; + int rc; + struct cifs_tcon *ptcon = cifs_sb_tcon(cifs_sb); + char *tmpbuffer; + + rc = CIFSSMBOpen(xid, ptcon, full_path, FILE_OPEN, GENERIC_READ, + OPEN_REPARSE_POINT, &fid, &oplock, NULL, + cifs_sb->local_nls, + cifs_remap(cifs_sb); + if (!rc) { + tmpbuffer = kmalloc(maxpath); + rc = CIFSSMBQueryReparseLinkInfo(xid, ptcon, full_path, + tmpbuffer, + maxpath -1, + fid, + cifs_sb->local_nls); + if (CIFSSMBClose(xid, ptcon, fid)) { + cifs_dbg(FYI, "Error closing temporary reparsepoint open\n"); + } + } +} + */ + +static int +_initiate_cifs_search(const unsigned int xid, struct file *file, + const char *full_path) +{ + __u16 search_flags; + int rc = 0; + struct cifsFileInfo *cifsFile; + struct cifs_sb_info *cifs_sb = CIFS_FILE_SB(file); + struct tcon_link *tlink = NULL; + struct cifs_tcon *tcon; + struct TCP_Server_Info *server; + + if (file->private_data == NULL) { + tlink = cifs_sb_tlink(cifs_sb); + if (IS_ERR(tlink)) + return PTR_ERR(tlink); + + cifsFile = kzalloc(sizeof(struct cifsFileInfo), GFP_KERNEL); + if (cifsFile == NULL) { + rc = -ENOMEM; + goto error_exit; + } + spin_lock_init(&cifsFile->file_info_lock); + file->private_data = cifsFile; + cifsFile->tlink = cifs_get_tlink(tlink); + tcon = tlink_tcon(tlink); + } else { + cifsFile = file->private_data; + tcon = tlink_tcon(cifsFile->tlink); + } + + server = tcon->ses->server; + + if (!server->ops->query_dir_first) { + rc = -ENOSYS; + goto error_exit; + } + + cifsFile->invalidHandle = true; + cifsFile->srch_inf.endOfSearch = false; + + cifs_dbg(FYI, "Full path: %s start at: %lld\n", full_path, file->f_pos); + +ffirst_retry: + /* test for Unix extensions */ + /* but now check for them on the share/mount not on the SMB session */ + /* if (cap_unix(tcon->ses) { */ + if (tcon->unix_ext) + cifsFile->srch_inf.info_level = SMB_FIND_FILE_UNIX; + else if (tcon->posix_extensions) + cifsFile->srch_inf.info_level = SMB_FIND_FILE_POSIX_INFO; + else if ((tcon->ses->capabilities & + tcon->ses->server->vals->cap_nt_find) == 0) { + cifsFile->srch_inf.info_level = SMB_FIND_FILE_INFO_STANDARD; + } else if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_SERVER_INUM) { + cifsFile->srch_inf.info_level = SMB_FIND_FILE_ID_FULL_DIR_INFO; + } else /* not srvinos - BB fixme add check for backlevel? */ { + cifsFile->srch_inf.info_level = SMB_FIND_FILE_DIRECTORY_INFO; + } + + search_flags = CIFS_SEARCH_CLOSE_AT_END | CIFS_SEARCH_RETURN_RESUME; + if (backup_cred(cifs_sb)) + search_flags |= CIFS_SEARCH_BACKUP_SEARCH; + + rc = server->ops->query_dir_first(xid, tcon, full_path, cifs_sb, + &cifsFile->fid, search_flags, + &cifsFile->srch_inf); + + if (rc == 0) + cifsFile->invalidHandle = false; + /* BB add following call to handle readdir on new NTFS symlink errors + else if STATUS_STOPPED_ON_SYMLINK + call get_symlink_reparse_path and retry with new path */ + else if ((rc == -EOPNOTSUPP) && + (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_SERVER_INUM)) { + cifs_sb->mnt_cifs_flags &= ~CIFS_MOUNT_SERVER_INUM; + goto ffirst_retry; + } +error_exit: + cifs_put_tlink(tlink); + return rc; +} + +static int +initiate_cifs_search(const unsigned int xid, struct file *file, + const char *full_path) +{ + int rc, retry_count = 0; + + do { + rc = _initiate_cifs_search(xid, file, full_path); + /* + * If we don't have enough credits to start reading the + * directory just try again after short wait. + */ + if (rc != -EDEADLK) + break; + + usleep_range(512, 2048); + } while (retry_count++ < 5); + + return rc; +} + +/* return length of unicode string in bytes */ +static int cifs_unicode_bytelen(const char *str) +{ + int len; + const __le16 *ustr = (const __le16 *)str; + + for (len = 0; len <= PATH_MAX; len++) { + if (ustr[len] == 0) + return len << 1; + } + cifs_dbg(FYI, "Unicode string longer than PATH_MAX found\n"); + return len << 1; +} + +static char *nxt_dir_entry(char *old_entry, char *end_of_smb, int level) +{ + char *new_entry; + FILE_DIRECTORY_INFO *pDirInfo = (FILE_DIRECTORY_INFO *)old_entry; + + if (level == SMB_FIND_FILE_INFO_STANDARD) { + FIND_FILE_STANDARD_INFO *pfData; + pfData = (FIND_FILE_STANDARD_INFO *)pDirInfo; + + new_entry = old_entry + sizeof(FIND_FILE_STANDARD_INFO) + + pfData->FileNameLength; + } else { + u32 next_offset = le32_to_cpu(pDirInfo->NextEntryOffset); + + if (old_entry + next_offset < old_entry) { + cifs_dbg(VFS, "Invalid offset %u\n", next_offset); + return NULL; + } + new_entry = old_entry + next_offset; + } + cifs_dbg(FYI, "new entry %p old entry %p\n", new_entry, old_entry); + /* validate that new_entry is not past end of SMB */ + if (new_entry >= end_of_smb) { + cifs_dbg(VFS, "search entry %p began after end of SMB %p old entry %p\n", + new_entry, end_of_smb, old_entry); + return NULL; + } else if (((level == SMB_FIND_FILE_INFO_STANDARD) && + (new_entry + sizeof(FIND_FILE_STANDARD_INFO) > end_of_smb)) + || ((level != SMB_FIND_FILE_INFO_STANDARD) && + (new_entry + sizeof(FILE_DIRECTORY_INFO) > end_of_smb))) { + cifs_dbg(VFS, "search entry %p extends after end of SMB %p\n", + new_entry, end_of_smb); + return NULL; + } else + return new_entry; + +} + +struct cifs_dirent { + const char *name; + size_t namelen; + u32 resume_key; + u64 ino; +}; + +static void cifs_fill_dirent_posix(struct cifs_dirent *de, + const struct smb2_posix_info *info) +{ + struct smb2_posix_info_parsed parsed; + + /* payload should have already been checked at this point */ + if (posix_info_parse(info, NULL, &parsed) < 0) { + cifs_dbg(VFS, "Invalid POSIX info payload\n"); + return; + } + + de->name = parsed.name; + de->namelen = parsed.name_len; + de->resume_key = info->Ignored; + de->ino = le64_to_cpu(info->Inode); +} + +static void cifs_fill_dirent_unix(struct cifs_dirent *de, + const FILE_UNIX_INFO *info, bool is_unicode) +{ + de->name = &info->FileName[0]; + if (is_unicode) + de->namelen = cifs_unicode_bytelen(de->name); + else + de->namelen = strnlen(de->name, PATH_MAX); + de->resume_key = info->ResumeKey; + de->ino = le64_to_cpu(info->basic.UniqueId); +} + +static void cifs_fill_dirent_dir(struct cifs_dirent *de, + const FILE_DIRECTORY_INFO *info) +{ + de->name = &info->FileName[0]; + de->namelen = le32_to_cpu(info->FileNameLength); + de->resume_key = info->FileIndex; +} + +static void cifs_fill_dirent_full(struct cifs_dirent *de, + const FILE_FULL_DIRECTORY_INFO *info) +{ + de->name = &info->FileName[0]; + de->namelen = le32_to_cpu(info->FileNameLength); + de->resume_key = info->FileIndex; +} + +static void cifs_fill_dirent_search(struct cifs_dirent *de, + const SEARCH_ID_FULL_DIR_INFO *info) +{ + de->name = &info->FileName[0]; + de->namelen = le32_to_cpu(info->FileNameLength); + de->resume_key = info->FileIndex; + de->ino = le64_to_cpu(info->UniqueId); +} + +static void cifs_fill_dirent_both(struct cifs_dirent *de, + const FILE_BOTH_DIRECTORY_INFO *info) +{ + de->name = &info->FileName[0]; + de->namelen = le32_to_cpu(info->FileNameLength); + de->resume_key = info->FileIndex; +} + +static void cifs_fill_dirent_std(struct cifs_dirent *de, + const FIND_FILE_STANDARD_INFO *info) +{ + de->name = &info->FileName[0]; + /* one byte length, no endianess conversion */ + de->namelen = info->FileNameLength; + de->resume_key = info->ResumeKey; +} + +static int cifs_fill_dirent(struct cifs_dirent *de, const void *info, + u16 level, bool is_unicode) +{ + memset(de, 0, sizeof(*de)); + + switch (level) { + case SMB_FIND_FILE_POSIX_INFO: + cifs_fill_dirent_posix(de, info); + break; + case SMB_FIND_FILE_UNIX: + cifs_fill_dirent_unix(de, info, is_unicode); + break; + case SMB_FIND_FILE_DIRECTORY_INFO: + cifs_fill_dirent_dir(de, info); + break; + case SMB_FIND_FILE_FULL_DIRECTORY_INFO: + cifs_fill_dirent_full(de, info); + break; + case SMB_FIND_FILE_ID_FULL_DIR_INFO: + cifs_fill_dirent_search(de, info); + break; + case SMB_FIND_FILE_BOTH_DIRECTORY_INFO: + cifs_fill_dirent_both(de, info); + break; + case SMB_FIND_FILE_INFO_STANDARD: + cifs_fill_dirent_std(de, info); + break; + default: + cifs_dbg(FYI, "Unknown findfirst level %d\n", level); + return -EINVAL; + } + + return 0; +} + +#define UNICODE_DOT cpu_to_le16(0x2e) + +/* return 0 if no match and 1 for . (current directory) and 2 for .. (parent) */ +static int cifs_entry_is_dot(struct cifs_dirent *de, bool is_unicode) +{ + int rc = 0; + + if (!de->name) + return 0; + + if (is_unicode) { + __le16 *ufilename = (__le16 *)de->name; + if (de->namelen == 2) { + /* check for . */ + if (ufilename[0] == UNICODE_DOT) + rc = 1; + } else if (de->namelen == 4) { + /* check for .. */ + if (ufilename[0] == UNICODE_DOT && + ufilename[1] == UNICODE_DOT) + rc = 2; + } + } else /* ASCII */ { + if (de->namelen == 1) { + if (de->name[0] == '.') + rc = 1; + } else if (de->namelen == 2) { + if (de->name[0] == '.' && de->name[1] == '.') + rc = 2; + } + } + + 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 = file_inode(file); + struct cifsInodeInfo *cifsInfo = CIFS_I(inode); + + if (cifsInfo->time == 0) + return 1; /* directory was changed, perhaps due to unlink */ + else + return 0; + +} + +static int cifs_save_resume_key(const char *current_entry, + struct cifsFileInfo *file_info) +{ + struct cifs_dirent de; + int rc; + + rc = cifs_fill_dirent(&de, current_entry, file_info->srch_inf.info_level, + file_info->srch_inf.unicode); + if (!rc) { + file_info->srch_inf.presume_name = de.name; + file_info->srch_inf.resume_name_len = de.namelen; + file_info->srch_inf.resume_key = de.resume_key; + } + return rc; +} + +/* + * 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 assume that they are located in the findfirst + * return buffer. We start counting in the buffer with entry 2 and increment for + * every entry (do not increment for . or .. entry). + */ +static int +find_cifs_entry(const unsigned int xid, struct cifs_tcon *tcon, loff_t pos, + struct file *file, const char *full_path, + char **current_entry, int *num_to_ret) +{ + __u16 search_flags; + int rc = 0; + int pos_in_buf = 0; + loff_t first_entry_in_buffer; + loff_t index_to_find = pos; + struct cifsFileInfo *cfile = file->private_data; + struct cifs_sb_info *cifs_sb = CIFS_FILE_SB(file); + struct TCP_Server_Info *server = tcon->ses->server; + /* check if index in the buffer */ + + if (!server->ops->query_dir_first || !server->ops->query_dir_next) + return -ENOSYS; + + if ((cfile == NULL) || (current_entry == NULL) || (num_to_ret == NULL)) + return -ENOENT; + + *current_entry = NULL; + first_entry_in_buffer = cfile->srch_inf.index_of_last_entry - + cfile->srch_inf.entries_in_buffer; + + /* + * If first entry in buf is zero then is first buffer + * in search response data which means it is likely . and .. + * will be in this buffer, although some servers do not return + * . and .. for the root of a drive and for those we need + * to start two entries earlier. + */ + + dump_cifs_file_struct(file, "In fce "); + if (((index_to_find < cfile->srch_inf.index_of_last_entry) && + is_dir_changed(file)) || (index_to_find < first_entry_in_buffer)) { + /* close and restart search */ + cifs_dbg(FYI, "search backing up - close and restart search\n"); + spin_lock(&cfile->file_info_lock); + if (server->ops->dir_needs_close(cfile)) { + cfile->invalidHandle = true; + spin_unlock(&cfile->file_info_lock); + if (server->ops->close_dir) + server->ops->close_dir(xid, tcon, &cfile->fid); + } else + spin_unlock(&cfile->file_info_lock); + if (cfile->srch_inf.ntwrk_buf_start) { + cifs_dbg(FYI, "freeing SMB ff cache buf on search rewind\n"); + if (cfile->srch_inf.smallBuf) + cifs_small_buf_release(cfile->srch_inf. + ntwrk_buf_start); + else + cifs_buf_release(cfile->srch_inf. + ntwrk_buf_start); + cfile->srch_inf.ntwrk_buf_start = NULL; + } + rc = initiate_cifs_search(xid, file, full_path); + if (rc) { + cifs_dbg(FYI, "error %d reinitiating a search on rewind\n", + rc); + return rc; + } + /* FindFirst/Next set last_entry to NULL on malformed reply */ + if (cfile->srch_inf.last_entry) + cifs_save_resume_key(cfile->srch_inf.last_entry, cfile); + } + + search_flags = CIFS_SEARCH_CLOSE_AT_END | CIFS_SEARCH_RETURN_RESUME; + if (backup_cred(cifs_sb)) + search_flags |= CIFS_SEARCH_BACKUP_SEARCH; + + while ((index_to_find >= cfile->srch_inf.index_of_last_entry) && + (rc == 0) && !cfile->srch_inf.endOfSearch) { + cifs_dbg(FYI, "calling findnext2\n"); + rc = server->ops->query_dir_next(xid, tcon, &cfile->fid, + search_flags, + &cfile->srch_inf); + /* FindFirst/Next set last_entry to NULL on malformed reply */ + if (cfile->srch_inf.last_entry) + cifs_save_resume_key(cfile->srch_inf.last_entry, cfile); + if (rc) + return -ENOENT; + } + if (index_to_find < cfile->srch_inf.index_of_last_entry) { + /* we found the buffer that contains the entry */ + /* scan and find it */ + int i; + char *cur_ent; + char *end_of_smb; + + if (cfile->srch_inf.ntwrk_buf_start == NULL) { + cifs_dbg(VFS, "ntwrk_buf_start is NULL during readdir\n"); + return -EIO; + } + + end_of_smb = cfile->srch_inf.ntwrk_buf_start + + server->ops->calc_smb_size( + cfile->srch_inf.ntwrk_buf_start); + + cur_ent = cfile->srch_inf.srch_entries_start; + first_entry_in_buffer = cfile->srch_inf.index_of_last_entry + - cfile->srch_inf.entries_in_buffer; + pos_in_buf = index_to_find - first_entry_in_buffer; + cifs_dbg(FYI, "found entry - pos_in_buf %d\n", pos_in_buf); + + for (i = 0; (i < (pos_in_buf)) && (cur_ent != NULL); i++) { + /* go entry by entry figuring out which is first */ + cur_ent = nxt_dir_entry(cur_ent, end_of_smb, + cfile->srch_inf.info_level); + } + if ((cur_ent == NULL) && (i < pos_in_buf)) { + /* BB fixme - check if we should flag this error */ + cifs_dbg(VFS, "reached end of buf searching for pos in buf %d index to find %lld rc %d\n", + pos_in_buf, index_to_find, rc); + } + rc = 0; + *current_entry = cur_ent; + } else { + cifs_dbg(FYI, "index not in buffer - could not findnext into it\n"); + return 0; + } + + if (pos_in_buf >= cfile->srch_inf.entries_in_buffer) { + cifs_dbg(FYI, "can not return entries pos_in_buf beyond last\n"); + *num_to_ret = 0; + } else + *num_to_ret = cfile->srch_inf.entries_in_buffer - pos_in_buf; + + return rc; +} + +static bool emit_cached_dirents(struct cached_dirents *cde, + struct dir_context *ctx) +{ + struct cached_dirent *dirent; + bool rc; + + list_for_each_entry(dirent, &cde->entries, entry) { + /* + * Skip all early entries prior to the current lseek() + * position. + */ + if (ctx->pos > dirent->pos) + continue; + /* + * We recorded the current ->pos value for the dirent + * when we stored it in the cache. + * However, this sequence of ->pos values may have holes + * in it, for example dot-dirs returned from the server + * are suppressed. + * Handle this bu forcing ctx->pos to be the same as the + * ->pos of the current dirent we emit from the cache. + * This means that when we emit these entries from the cache + * we now emit them with the same ->pos value as in the + * initial scan. + */ + ctx->pos = dirent->pos; + rc = dir_emit(ctx, dirent->name, dirent->namelen, + dirent->fattr.cf_uniqueid, + dirent->fattr.cf_dtype); + if (!rc) + return rc; + ctx->pos++; + } + return true; +} + +static void update_cached_dirents_count(struct cached_dirents *cde, + struct dir_context *ctx) +{ + if (cde->ctx != ctx) + return; + if (cde->is_valid || cde->is_failed) + return; + + cde->pos++; +} + +static void finished_cached_dirents_count(struct cached_dirents *cde, + struct dir_context *ctx) +{ + if (cde->ctx != ctx) + return; + if (cde->is_valid || cde->is_failed) + return; + if (ctx->pos != cde->pos) + return; + + cde->is_valid = 1; +} + +static void add_cached_dirent(struct cached_dirents *cde, + struct dir_context *ctx, + const char *name, int namelen, + struct cifs_fattr *fattr) +{ + struct cached_dirent *de; + + if (cde->ctx != ctx) + return; + if (cde->is_valid || cde->is_failed) + return; + if (ctx->pos != cde->pos) { + cde->is_failed = 1; + return; + } + de = kzalloc(sizeof(*de), GFP_ATOMIC); + if (de == NULL) { + cde->is_failed = 1; + return; + } + de->namelen = namelen; + de->name = kstrndup(name, namelen, GFP_ATOMIC); + if (de->name == NULL) { + kfree(de); + cde->is_failed = 1; + return; + } + de->pos = ctx->pos; + + memcpy(&de->fattr, fattr, sizeof(struct cifs_fattr)); + + list_add_tail(&de->entry, &cde->entries); +} + +static bool cifs_dir_emit(struct dir_context *ctx, + const char *name, int namelen, + struct cifs_fattr *fattr, + struct cached_fid *cfid) +{ + bool rc; + ino_t ino = cifs_uniqueid_to_ino_t(fattr->cf_uniqueid); + + rc = dir_emit(ctx, name, namelen, ino, fattr->cf_dtype); + if (!rc) + return rc; + + if (cfid) { + mutex_lock(&cfid->dirents.de_mutex); + add_cached_dirent(&cfid->dirents, ctx, name, namelen, + fattr); + mutex_unlock(&cfid->dirents.de_mutex); + } + + return rc; +} + +static int cifs_filldir(char *find_entry, struct file *file, + struct dir_context *ctx, + char *scratch_buf, unsigned int max_len, + struct cached_fid *cfid) +{ + struct cifsFileInfo *file_info = file->private_data; + struct super_block *sb = file_inode(file)->i_sb; + struct cifs_sb_info *cifs_sb = CIFS_SB(sb); + struct cifs_dirent de = { NULL, }; + struct cifs_fattr fattr; + struct qstr name; + int rc = 0; + + rc = cifs_fill_dirent(&de, find_entry, file_info->srch_inf.info_level, + file_info->srch_inf.unicode); + if (rc) + return rc; + + if (de.namelen > max_len) { + cifs_dbg(VFS, "bad search response length %zd past smb end\n", + de.namelen); + return -EINVAL; + } + + /* skip . and .. since we added them first */ + if (cifs_entry_is_dot(&de, file_info->srch_inf.unicode)) + return 0; + + if (file_info->srch_inf.unicode) { + struct nls_table *nlt = cifs_sb->local_nls; + int map_type; + + map_type = cifs_remap(cifs_sb); + name.name = scratch_buf; + name.len = + cifs_from_utf16((char *)name.name, (__le16 *)de.name, + UNICODE_NAME_MAX, + min_t(size_t, de.namelen, + (size_t)max_len), nlt, map_type); + name.len -= nls_nullsize(nlt); + } else { + name.name = de.name; + name.len = de.namelen; + } + + switch (file_info->srch_inf.info_level) { + case SMB_FIND_FILE_POSIX_INFO: + cifs_posix_to_fattr(&fattr, + (struct smb2_posix_info *)find_entry, + cifs_sb); + break; + case SMB_FIND_FILE_UNIX: + cifs_unix_basic_to_fattr(&fattr, + &((FILE_UNIX_INFO *)find_entry)->basic, + cifs_sb); + if (S_ISLNK(fattr.cf_mode)) + fattr.cf_flags |= CIFS_FATTR_NEED_REVAL; + break; + case SMB_FIND_FILE_INFO_STANDARD: + cifs_std_info_to_fattr(&fattr, + (FIND_FILE_STANDARD_INFO *)find_entry, + cifs_sb); + break; + case SMB_FIND_FILE_ID_FULL_DIR_INFO: + cifs_fulldir_info_to_fattr(&fattr, + (SEARCH_ID_FULL_DIR_INFO *)find_entry, + cifs_sb); + break; + default: + cifs_dir_info_to_fattr(&fattr, + (FILE_DIRECTORY_INFO *)find_entry, + cifs_sb); + break; + } + + if (de.ino && (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_SERVER_INUM)) { + fattr.cf_uniqueid = de.ino; + } else { + fattr.cf_uniqueid = iunique(sb, ROOT_I); + cifs_autodisable_serverino(cifs_sb); + } + + if ((cifs_sb->mnt_cifs_flags & CIFS_MOUNT_MF_SYMLINKS) && + couldbe_mf_symlink(&fattr)) + /* + * trying to get the type and mode can be slow, + * so just call those regular files for now, and mark + * for reval + */ + fattr.cf_flags |= CIFS_FATTR_NEED_REVAL; + + cifs_prime_dcache(file_dentry(file), &name, &fattr); + + return !cifs_dir_emit(ctx, name.name, name.len, + &fattr, cfid); +} + + +int cifs_readdir(struct file *file, struct dir_context *ctx) +{ + int rc = 0; + unsigned int xid; + int i; + struct tcon_link *tlink = NULL; + struct cifs_tcon *tcon; + struct cifsFileInfo *cifsFile; + char *current_entry; + int num_to_fill = 0; + char *tmp_buf = NULL; + char *end_of_smb; + unsigned int max_len; + const char *full_path; + void *page = alloc_dentry_path(); + struct cached_fid *cfid = NULL; + struct cifs_sb_info *cifs_sb = CIFS_FILE_SB(file); + + xid = get_xid(); + + full_path = build_path_from_dentry(file_dentry(file), page); + if (IS_ERR(full_path)) { + rc = PTR_ERR(full_path); + goto rddir2_exit; + } + + if (file->private_data == NULL) { + tlink = cifs_sb_tlink(cifs_sb); + if (IS_ERR(tlink)) + goto cache_not_found; + tcon = tlink_tcon(tlink); + } else { + cifsFile = file->private_data; + tcon = tlink_tcon(cifsFile->tlink); + } + + rc = open_cached_dir(xid, tcon, full_path, cifs_sb, false, &cfid); + cifs_put_tlink(tlink); + if (rc) + goto cache_not_found; + + mutex_lock(&cfid->dirents.de_mutex); + /* + * If this was reading from the start of the directory + * we need to initialize scanning and storing the + * directory content. + */ + if (ctx->pos == 0 && cfid->dirents.ctx == NULL) { + cfid->dirents.ctx = ctx; + cfid->dirents.pos = 2; + } + /* + * If we already have the entire directory cached then + * we can just serve the cache. + */ + if (cfid->dirents.is_valid) { + if (!dir_emit_dots(file, ctx)) { + mutex_unlock(&cfid->dirents.de_mutex); + goto rddir2_exit; + } + emit_cached_dirents(&cfid->dirents, ctx); + mutex_unlock(&cfid->dirents.de_mutex); + goto rddir2_exit; + } + mutex_unlock(&cfid->dirents.de_mutex); + + /* Drop the cache while calling initiate_cifs_search and + * find_cifs_entry in case there will be reconnects during + * query_directory. + */ + close_cached_dir(cfid); + cfid = NULL; + + cache_not_found: + /* + * Ensure FindFirst doesn't fail before doing filldir() for '.' and + * '..'. Otherwise we won't be able to notify VFS in case of failure. + */ + if (file->private_data == NULL) { + rc = initiate_cifs_search(xid, file, full_path); + cifs_dbg(FYI, "initiate cifs search rc %d\n", rc); + if (rc) + goto rddir2_exit; + } + + if (!dir_emit_dots(file, ctx)) + goto rddir2_exit; + + /* 1) If search is active, + is in current search buffer? + if it before then restart search + if after then keep searching till find it */ + cifsFile = file->private_data; + if (cifsFile->srch_inf.endOfSearch) { + if (cifsFile->srch_inf.emptyDir) { + cifs_dbg(FYI, "End of search, empty dir\n"); + rc = 0; + goto rddir2_exit; + } + } /* else { + cifsFile->invalidHandle = true; + tcon->ses->server->close(xid, tcon, &cifsFile->fid); + } */ + + tcon = tlink_tcon(cifsFile->tlink); + rc = find_cifs_entry(xid, tcon, ctx->pos, file, full_path, + ¤t_entry, &num_to_fill); + open_cached_dir(xid, tcon, full_path, cifs_sb, false, &cfid); + if (rc) { + cifs_dbg(FYI, "fce error %d\n", rc); + goto rddir2_exit; + } else if (current_entry != NULL) { + cifs_dbg(FYI, "entry %lld found\n", ctx->pos); + } else { + if (cfid) { + mutex_lock(&cfid->dirents.de_mutex); + finished_cached_dirents_count(&cfid->dirents, ctx); + mutex_unlock(&cfid->dirents.de_mutex); + } + cifs_dbg(FYI, "Could not find entry\n"); + goto rddir2_exit; + } + cifs_dbg(FYI, "loop through %d times filling dir for net buf %p\n", + num_to_fill, cifsFile->srch_inf.ntwrk_buf_start); + max_len = tcon->ses->server->ops->calc_smb_size( + cifsFile->srch_inf.ntwrk_buf_start); + end_of_smb = cifsFile->srch_inf.ntwrk_buf_start + max_len; + + tmp_buf = kmalloc(UNICODE_NAME_MAX, GFP_KERNEL); + if (tmp_buf == NULL) { + rc = -ENOMEM; + goto rddir2_exit; + } + + for (i = 0; i < num_to_fill; i++) { + if (current_entry == NULL) { + /* evaluate whether this case is an error */ + cifs_dbg(VFS, "past SMB end, num to fill %d i %d\n", + num_to_fill, i); + break; + } + /* + * if buggy server returns . and .. late do we want to + * check for that here? + */ + *tmp_buf = 0; + rc = cifs_filldir(current_entry, file, ctx, + tmp_buf, max_len, cfid); + if (rc) { + if (rc > 0) + rc = 0; + break; + } + + ctx->pos++; + if (cfid) { + mutex_lock(&cfid->dirents.de_mutex); + update_cached_dirents_count(&cfid->dirents, ctx); + mutex_unlock(&cfid->dirents.de_mutex); + } + + if (ctx->pos == + cifsFile->srch_inf.index_of_last_entry) { + cifs_dbg(FYI, "last entry in buf at pos %lld %s\n", + ctx->pos, tmp_buf); + cifs_save_resume_key(current_entry, cifsFile); + break; + } + current_entry = + nxt_dir_entry(current_entry, end_of_smb, + cifsFile->srch_inf.info_level); + } + kfree(tmp_buf); + +rddir2_exit: + if (cfid) + close_cached_dir(cfid); + free_dentry_path(page); + free_xid(xid); + return rc; +} diff --git a/fs/smb/client/rfc1002pdu.h b/fs/smb/client/rfc1002pdu.h new file mode 100644 index 0000000000000..ae1d025da294a --- /dev/null +++ b/fs/smb/client/rfc1002pdu.h @@ -0,0 +1,61 @@ +/* SPDX-License-Identifier: LGPL-2.1 */ +/* + * + * Protocol Data Unit definitions for RFC 1001/1002 support + * + * Copyright (c) International Business Machines Corp., 2004 + * Author(s): Steve French (sfrench@us.ibm.com) + * + */ + +/* NB: unlike smb/cifs packets, the RFC1002 structures are big endian */ + + /* RFC 1002 session packet types */ +#define RFC1002_SESSION_MESSAGE 0x00 +#define RFC1002_SESSION_REQUEST 0x81 +#define RFC1002_POSITIVE_SESSION_RESPONSE 0x82 +#define RFC1002_NEGATIVE_SESSION_RESPONSE 0x83 +#define RFC1002_RETARGET_SESSION_RESPONSE 0x84 +#define RFC1002_SESSION_KEEP_ALIVE 0x85 + + /* RFC 1002 flags (only one defined */ +#define RFC1002_LENGTH_EXTEND 0x80 /* high order bit of length (ie +64K) */ + +struct rfc1002_session_packet { + __u8 type; + __u8 flags; + __u16 length; + union { + struct { + __u8 called_len; + __u8 called_name[32]; + __u8 scope1; /* null */ + __u8 calling_len; + __u8 calling_name[32]; + __u8 scope2; /* null */ + } __attribute__((packed)) session_req; + struct { + __u32 retarget_ip_addr; + __u16 port; + } __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 */ + } __attribute__((packed)) trailer; +} __attribute__((packed)); + +/* Negative Session Response error codes */ +#define RFC1002_NOT_LISTENING_CALLED 0x80 /* not listening on called name */ +#define RFC1002_NOT_LISTENING_CALLING 0x81 /* not listening on calling name */ +#define RFC1002_NOT_PRESENT 0x82 /* called name not present */ +#define RFC1002_INSUFFICIENT_RESOURCE 0x83 +#define RFC1002_UNSPECIFIED_ERROR 0x8F + +/* RFC 1002 Datagram service packets are not defined here as they +are not needed for the network filesystem client unless we plan on +implementing broadcast resolution of the server ip address (from +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 " diff --git a/fs/smb/client/sess.c b/fs/smb/client/sess.c new file mode 100644 index 0000000000000..81be17845072a --- /dev/null +++ b/fs/smb/client/sess.c @@ -0,0 +1,1858 @@ +// SPDX-License-Identifier: LGPL-2.1 +/* + * + * SMB/CIFS session setup handling routines + * + * Copyright (c) International Business Machines Corp., 2006, 2009 + * Author(s): Steve French (sfrench@us.ibm.com) + * + */ + +#include "cifspdu.h" +#include "cifsglob.h" +#include "cifsproto.h" +#include "cifs_unicode.h" +#include "cifs_debug.h" +#include "ntlmssp.h" +#include "nterr.h" +#include +#include +#include +#include "cifsfs.h" +#include "cifs_spnego.h" +#include "smb2proto.h" +#include "fs_context.h" + +static int +cifs_ses_add_channel(struct cifs_sb_info *cifs_sb, struct cifs_ses *ses, + struct cifs_server_iface *iface); + +bool +is_server_using_iface(struct TCP_Server_Info *server, + struct cifs_server_iface *iface) +{ + struct sockaddr_in *i4 = (struct sockaddr_in *)&iface->sockaddr; + struct sockaddr_in6 *i6 = (struct sockaddr_in6 *)&iface->sockaddr; + struct sockaddr_in *s4 = (struct sockaddr_in *)&server->dstaddr; + struct sockaddr_in6 *s6 = (struct sockaddr_in6 *)&server->dstaddr; + + if (server->dstaddr.ss_family != iface->sockaddr.ss_family) + return false; + if (server->dstaddr.ss_family == AF_INET) { + if (s4->sin_addr.s_addr != i4->sin_addr.s_addr) + return false; + } else if (server->dstaddr.ss_family == AF_INET6) { + if (memcmp(&s6->sin6_addr, &i6->sin6_addr, + sizeof(i6->sin6_addr)) != 0) + return false; + } else { + /* unknown family.. */ + return false; + } + return true; +} + +bool is_ses_using_iface(struct cifs_ses *ses, struct cifs_server_iface *iface) +{ + int i; + + spin_lock(&ses->chan_lock); + for (i = 0; i < ses->chan_count; i++) { + if (ses->chans[i].iface == iface) { + spin_unlock(&ses->chan_lock); + return true; + } + } + spin_unlock(&ses->chan_lock); + return false; +} + +/* channel helper functions. assumed that chan_lock is held by caller. */ + +unsigned int +cifs_ses_get_chan_index(struct cifs_ses *ses, + struct TCP_Server_Info *server) +{ + unsigned int i; + + for (i = 0; i < ses->chan_count; i++) { + if (ses->chans[i].server == server) + return i; + } + + /* If we didn't find the channel, it is likely a bug */ + if (server) + cifs_dbg(VFS, "unable to get chan index for server: 0x%llx", + server->conn_id); + WARN_ON(1); + return 0; +} + +void +cifs_chan_set_in_reconnect(struct cifs_ses *ses, + struct TCP_Server_Info *server) +{ + unsigned int chan_index = cifs_ses_get_chan_index(ses, server); + + ses->chans[chan_index].in_reconnect = true; +} + +void +cifs_chan_clear_in_reconnect(struct cifs_ses *ses, + struct TCP_Server_Info *server) +{ + unsigned int chan_index = cifs_ses_get_chan_index(ses, server); + + ses->chans[chan_index].in_reconnect = false; +} + +bool +cifs_chan_in_reconnect(struct cifs_ses *ses, + struct TCP_Server_Info *server) +{ + unsigned int chan_index = cifs_ses_get_chan_index(ses, server); + + return CIFS_CHAN_IN_RECONNECT(ses, chan_index); +} + +void +cifs_chan_set_need_reconnect(struct cifs_ses *ses, + struct TCP_Server_Info *server) +{ + unsigned int chan_index = cifs_ses_get_chan_index(ses, server); + + set_bit(chan_index, &ses->chans_need_reconnect); + cifs_dbg(FYI, "Set reconnect bitmask for chan %u; now 0x%lx\n", + chan_index, ses->chans_need_reconnect); +} + +void +cifs_chan_clear_need_reconnect(struct cifs_ses *ses, + struct TCP_Server_Info *server) +{ + unsigned int chan_index = cifs_ses_get_chan_index(ses, server); + + clear_bit(chan_index, &ses->chans_need_reconnect); + cifs_dbg(FYI, "Cleared reconnect bitmask for chan %u; now 0x%lx\n", + chan_index, ses->chans_need_reconnect); +} + +bool +cifs_chan_needs_reconnect(struct cifs_ses *ses, + struct TCP_Server_Info *server) +{ + unsigned int chan_index = cifs_ses_get_chan_index(ses, server); + + return CIFS_CHAN_NEEDS_RECONNECT(ses, chan_index); +} + +bool +cifs_chan_is_iface_active(struct cifs_ses *ses, + struct TCP_Server_Info *server) +{ + unsigned int chan_index = cifs_ses_get_chan_index(ses, server); + + return ses->chans[chan_index].iface && + ses->chans[chan_index].iface->is_active; +} + +/* returns number of channels added */ +int cifs_try_adding_channels(struct cifs_sb_info *cifs_sb, struct cifs_ses *ses) +{ + struct TCP_Server_Info *server = ses->server; + int old_chan_count, new_chan_count; + int left; + int rc = 0; + int tries = 0; + struct cifs_server_iface *iface = NULL, *niface = NULL; + + spin_lock(&ses->chan_lock); + + new_chan_count = old_chan_count = ses->chan_count; + left = ses->chan_max - ses->chan_count; + + if (left <= 0) { + spin_unlock(&ses->chan_lock); + cifs_dbg(FYI, + "ses already at max_channels (%zu), nothing to open\n", + ses->chan_max); + return 0; + } + + if (server->dialect < SMB30_PROT_ID) { + spin_unlock(&ses->chan_lock); + cifs_dbg(VFS, "multichannel is not supported on this protocol version, use 3.0 or above\n"); + return 0; + } + + if (!(server->capabilities & SMB2_GLOBAL_CAP_MULTI_CHANNEL)) { + ses->chan_max = 1; + spin_unlock(&ses->chan_lock); + cifs_server_dbg(VFS, "no multichannel support\n"); + return 0; + } + spin_unlock(&ses->chan_lock); + + /* + * Keep connecting to same, fastest, iface for all channels as + * long as its RSS. Try next fastest one if not RSS or channel + * creation fails. + */ + spin_lock(&ses->iface_lock); + iface = list_first_entry(&ses->iface_list, struct cifs_server_iface, + iface_head); + spin_unlock(&ses->iface_lock); + + while (left > 0) { + + tries++; + if (tries > 3*ses->chan_max) { + cifs_dbg(FYI, "too many channel open attempts (%d channels left to open)\n", + left); + break; + } + + spin_lock(&ses->iface_lock); + if (!ses->iface_count) { + spin_unlock(&ses->iface_lock); + break; + } + + list_for_each_entry_safe_from(iface, niface, &ses->iface_list, + iface_head) { + /* skip ifaces that are unusable */ + if (!iface->is_active || + (is_ses_using_iface(ses, iface) && + !iface->rss_capable)) { + continue; + } + + /* take ref before unlock */ + kref_get(&iface->refcount); + + spin_unlock(&ses->iface_lock); + rc = cifs_ses_add_channel(cifs_sb, ses, iface); + spin_lock(&ses->iface_lock); + + if (rc) { + cifs_dbg(VFS, "failed to open extra channel on iface:%pIS rc=%d\n", + &iface->sockaddr, + rc); + kref_put(&iface->refcount, release_iface); + continue; + } + + cifs_dbg(FYI, "successfully opened new channel on iface:%pIS\n", + &iface->sockaddr); + break; + } + spin_unlock(&ses->iface_lock); + + left--; + new_chan_count++; + } + + return new_chan_count - old_chan_count; +} + +/* + * update the iface for the channel if necessary. + * will return 0 when iface is updated, 1 if removed, 2 otherwise + * Must be called with chan_lock held. + */ +int +cifs_chan_update_iface(struct cifs_ses *ses, struct TCP_Server_Info *server) +{ + unsigned int chan_index; + struct cifs_server_iface *iface = NULL; + struct cifs_server_iface *old_iface = NULL; + int rc = 0; + + spin_lock(&ses->chan_lock); + chan_index = cifs_ses_get_chan_index(ses, server); + if (!chan_index) { + spin_unlock(&ses->chan_lock); + return 0; + } + + if (ses->chans[chan_index].iface) { + old_iface = ses->chans[chan_index].iface; + if (old_iface->is_active) { + spin_unlock(&ses->chan_lock); + return 1; + } + } + spin_unlock(&ses->chan_lock); + + spin_lock(&ses->iface_lock); + /* then look for a new one */ + list_for_each_entry(iface, &ses->iface_list, iface_head) { + if (!iface->is_active || + (is_ses_using_iface(ses, iface) && + !iface->rss_capable)) { + continue; + } + kref_get(&iface->refcount); + break; + } + + if (list_entry_is_head(iface, &ses->iface_list, iface_head)) { + rc = 1; + iface = NULL; + cifs_dbg(FYI, "unable to find a suitable iface\n"); + } + + /* now drop the ref to the current iface */ + if (old_iface && iface) { + cifs_dbg(FYI, "replacing iface: %pIS with %pIS\n", + &old_iface->sockaddr, + &iface->sockaddr); + kref_put(&old_iface->refcount, release_iface); + } else if (old_iface) { + cifs_dbg(FYI, "releasing ref to iface: %pIS\n", + &old_iface->sockaddr); + kref_put(&old_iface->refcount, release_iface); + } else { + WARN_ON(!iface); + cifs_dbg(FYI, "adding new iface: %pIS\n", &iface->sockaddr); + } + spin_unlock(&ses->iface_lock); + + spin_lock(&ses->chan_lock); + chan_index = cifs_ses_get_chan_index(ses, server); + ses->chans[chan_index].iface = iface; + + /* No iface is found. if secondary chan, drop connection */ + if (!iface && CIFS_SERVER_IS_CHAN(server)) + ses->chans[chan_index].server = NULL; + + spin_unlock(&ses->chan_lock); + + if (!iface && CIFS_SERVER_IS_CHAN(server)) + cifs_put_tcp_session(server, false); + + return rc; +} + +/* + * If server is a channel of ses, return the corresponding enclosing + * cifs_chan otherwise return NULL. + */ +struct cifs_chan * +cifs_ses_find_chan(struct cifs_ses *ses, struct TCP_Server_Info *server) +{ + int i; + + spin_lock(&ses->chan_lock); + for (i = 0; i < ses->chan_count; i++) { + if (ses->chans[i].server == server) { + spin_unlock(&ses->chan_lock); + return &ses->chans[i]; + } + } + spin_unlock(&ses->chan_lock); + return NULL; +} + +static int +cifs_ses_add_channel(struct cifs_sb_info *cifs_sb, struct cifs_ses *ses, + struct cifs_server_iface *iface) +{ + struct TCP_Server_Info *chan_server; + struct cifs_chan *chan; + struct smb3_fs_context ctx = {NULL}; + static const char unc_fmt[] = "\\%s\\foo"; + char unc[sizeof(unc_fmt)+SERVER_NAME_LEN_WITH_NULL] = {0}; + struct sockaddr_in *ipv4 = (struct sockaddr_in *)&iface->sockaddr; + struct sockaddr_in6 *ipv6 = (struct sockaddr_in6 *)&iface->sockaddr; + int rc; + unsigned int xid = get_xid(); + + if (iface->sockaddr.ss_family == AF_INET) + cifs_dbg(FYI, "adding channel to ses %p (speed:%zu bps rdma:%s ip:%pI4)\n", + ses, iface->speed, iface->rdma_capable ? "yes" : "no", + &ipv4->sin_addr); + else + cifs_dbg(FYI, "adding channel to ses %p (speed:%zu bps rdma:%s ip:%pI6)\n", + ses, iface->speed, iface->rdma_capable ? "yes" : "no", + &ipv6->sin6_addr); + + /* + * Setup a ctx with mostly the same info as the existing + * session and overwrite it with the requested iface data. + * + * We need to setup at least the fields used for negprot and + * sesssetup. + * + * We only need the ctx here, so we can reuse memory from + * the session and server without caring about memory + * management. + */ + + /* Always make new connection for now (TODO?) */ + ctx.nosharesock = true; + + /* Auth */ + ctx.domainauto = ses->domainAuto; + ctx.domainname = ses->domainName; + + /* no hostname for extra channels */ + ctx.server_hostname = ""; + + ctx.username = ses->user_name; + ctx.password = ses->password; + ctx.sectype = ses->sectype; + ctx.sign = ses->sign; + + /* UNC and paths */ + /* XXX: Use ses->server->hostname? */ + sprintf(unc, unc_fmt, ses->ip_addr); + ctx.UNC = unc; + ctx.prepath = ""; + + /* Reuse same version as master connection */ + ctx.vals = ses->server->vals; + ctx.ops = ses->server->ops; + + ctx.noblocksnd = ses->server->noblocksnd; + ctx.noautotune = ses->server->noautotune; + ctx.sockopt_tcp_nodelay = ses->server->tcp_nodelay; + ctx.echo_interval = ses->server->echo_interval / HZ; + ctx.max_credits = ses->server->max_credits; + + /* + * This will be used for encoding/decoding user/domain/pw + * during sess setup auth. + */ + ctx.local_nls = cifs_sb->local_nls; + + /* Use RDMA if possible */ + ctx.rdma = iface->rdma_capable; + memcpy(&ctx.dstaddr, &iface->sockaddr, sizeof(struct sockaddr_storage)); + + /* reuse master con client guid */ + memcpy(&ctx.client_guid, ses->server->client_guid, + SMB2_CLIENT_GUID_SIZE); + ctx.use_client_guid = true; + + chan_server = cifs_get_tcp_session(&ctx, ses->server); + + spin_lock(&ses->chan_lock); + chan = &ses->chans[ses->chan_count]; + chan->server = chan_server; + if (IS_ERR(chan->server)) { + rc = PTR_ERR(chan->server); + chan->server = NULL; + spin_unlock(&ses->chan_lock); + goto out; + } + chan->iface = iface; + ses->chan_count++; + atomic_set(&ses->chan_seq, 0); + + /* Mark this channel as needing connect/setup */ + cifs_chan_set_need_reconnect(ses, chan->server); + + spin_unlock(&ses->chan_lock); + + mutex_lock(&ses->session_mutex); + /* + * We need to allocate the server crypto now as we will need + * to sign packets before we generate the channel signing key + * (we sign with the session key) + */ + rc = smb311_crypto_shash_allocate(chan->server); + if (rc) { + cifs_dbg(VFS, "%s: crypto alloc failed\n", __func__); + mutex_unlock(&ses->session_mutex); + goto out; + } + + rc = cifs_negotiate_protocol(xid, ses, chan->server); + if (!rc) + rc = cifs_setup_session(xid, ses, chan->server, cifs_sb->local_nls); + + mutex_unlock(&ses->session_mutex); + +out: + if (rc && chan->server) { + /* + * we should avoid race with these delayed works before we + * remove this channel + */ + cancel_delayed_work_sync(&chan->server->echo); + cancel_delayed_work_sync(&chan->server->resolve); + cancel_delayed_work_sync(&chan->server->reconnect); + + spin_lock(&ses->chan_lock); + /* we rely on all bits beyond chan_count to be clear */ + cifs_chan_clear_need_reconnect(ses, chan->server); + ses->chan_count--; + /* + * chan_count should never reach 0 as at least the primary + * channel is always allocated + */ + WARN_ON(ses->chan_count < 1); + spin_unlock(&ses->chan_lock); + + cifs_put_tcp_session(chan->server, 0); + } + + free_xid(xid); + return rc; +} + +#ifdef CONFIG_CIFS_ALLOW_INSECURE_LEGACY +static __u32 cifs_ssetup_hdr(struct cifs_ses *ses, + struct TCP_Server_Info *server, + SESSION_SETUP_ANDX *pSMB) +{ + __u32 capabilities = 0; + + /* init fields common to all four types of SessSetup */ + /* Note that offsets for first seven fields in req struct are same */ + /* in CIFS Specs so does not matter which of 3 forms of struct */ + /* that we use in next few lines */ + /* Note that header is initialized to zero in header_assemble */ + pSMB->req.AndXCommand = 0xFF; + pSMB->req.MaxBufferSize = cpu_to_le16(min_t(u32, + CIFSMaxBufSize + MAX_CIFS_HDR_SIZE - 4, + USHRT_MAX)); + pSMB->req.MaxMpxCount = cpu_to_le16(server->maxReq); + pSMB->req.VcNumber = cpu_to_le16(1); + + /* Now no need to set SMBFLG_CASELESS or obsolete CANONICAL PATH */ + + /* BB verify whether signing required on neg or just on auth frame + (and NTLM case) */ + + capabilities = CAP_LARGE_FILES | CAP_NT_SMBS | CAP_LEVEL_II_OPLOCKS | + CAP_LARGE_WRITE_X | CAP_LARGE_READ_X; + + if (server->sign) + pSMB->req.hdr.Flags2 |= SMBFLG2_SECURITY_SIGNATURE; + + if (ses->capabilities & CAP_UNICODE) { + pSMB->req.hdr.Flags2 |= SMBFLG2_UNICODE; + capabilities |= CAP_UNICODE; + } + if (ses->capabilities & CAP_STATUS32) { + pSMB->req.hdr.Flags2 |= SMBFLG2_ERR_STATUS; + capabilities |= CAP_STATUS32; + } + if (ses->capabilities & CAP_DFS) { + pSMB->req.hdr.Flags2 |= SMBFLG2_DFS; + capabilities |= CAP_DFS; + } + if (ses->capabilities & CAP_UNIX) + capabilities |= CAP_UNIX; + + return capabilities; +} + +static void +unicode_oslm_strings(char **pbcc_area, const struct nls_table *nls_cp) +{ + char *bcc_ptr = *pbcc_area; + int bytes_ret = 0; + + /* Copy OS version */ + bytes_ret = cifs_strtoUTF16((__le16 *)bcc_ptr, "Linux version ", 32, + nls_cp); + bcc_ptr += 2 * bytes_ret; + bytes_ret = cifs_strtoUTF16((__le16 *) bcc_ptr, init_utsname()->release, + 32, nls_cp); + bcc_ptr += 2 * bytes_ret; + bcc_ptr += 2; /* trailing null */ + + bytes_ret = cifs_strtoUTF16((__le16 *) bcc_ptr, CIFS_NETWORK_OPSYS, + 32, nls_cp); + bcc_ptr += 2 * bytes_ret; + bcc_ptr += 2; /* trailing null */ + + *pbcc_area = bcc_ptr; +} + +static void unicode_domain_string(char **pbcc_area, struct cifs_ses *ses, + const struct nls_table *nls_cp) +{ + char *bcc_ptr = *pbcc_area; + int bytes_ret = 0; + + /* copy domain */ + if (ses->domainName == NULL) { + /* Sending null domain better than using a bogus domain name (as + we did briefly in 2.6.18) since server will use its default */ + *bcc_ptr = 0; + *(bcc_ptr+1) = 0; + bytes_ret = 0; + } else + bytes_ret = cifs_strtoUTF16((__le16 *) bcc_ptr, ses->domainName, + CIFS_MAX_DOMAINNAME_LEN, nls_cp); + bcc_ptr += 2 * bytes_ret; + bcc_ptr += 2; /* account for null terminator */ + + *pbcc_area = bcc_ptr; +} + +static void unicode_ssetup_strings(char **pbcc_area, struct cifs_ses *ses, + const struct nls_table *nls_cp) +{ + char *bcc_ptr = *pbcc_area; + int bytes_ret = 0; + + /* BB FIXME add check that strings total less + than 335 or will need to send them as arrays */ + + /* copy user */ + if (ses->user_name == NULL) { + /* null user mount */ + *bcc_ptr = 0; + *(bcc_ptr+1) = 0; + } else { + bytes_ret = cifs_strtoUTF16((__le16 *) bcc_ptr, ses->user_name, + CIFS_MAX_USERNAME_LEN, nls_cp); + } + bcc_ptr += 2 * bytes_ret; + bcc_ptr += 2; /* account for null termination */ + + unicode_domain_string(&bcc_ptr, ses, nls_cp); + unicode_oslm_strings(&bcc_ptr, nls_cp); + + *pbcc_area = bcc_ptr; +} + +static void ascii_ssetup_strings(char **pbcc_area, struct cifs_ses *ses, + const struct nls_table *nls_cp) +{ + char *bcc_ptr = *pbcc_area; + int len; + + /* copy user */ + /* BB what about null user mounts - check that we do this BB */ + /* copy user */ + if (ses->user_name != NULL) { + len = strscpy(bcc_ptr, ses->user_name, CIFS_MAX_USERNAME_LEN); + if (WARN_ON_ONCE(len < 0)) + len = CIFS_MAX_USERNAME_LEN - 1; + bcc_ptr += len; + } + /* else null user mount */ + *bcc_ptr = 0; + bcc_ptr++; /* account for null termination */ + + /* copy domain */ + if (ses->domainName != NULL) { + len = strscpy(bcc_ptr, ses->domainName, CIFS_MAX_DOMAINNAME_LEN); + if (WARN_ON_ONCE(len < 0)) + len = CIFS_MAX_DOMAINNAME_LEN - 1; + bcc_ptr += len; + } /* else we will send a null domain name + so the server will default to its own domain */ + *bcc_ptr = 0; + bcc_ptr++; + + /* BB check for overflow here */ + + strcpy(bcc_ptr, "Linux version "); + bcc_ptr += strlen("Linux version "); + strcpy(bcc_ptr, init_utsname()->release); + bcc_ptr += strlen(init_utsname()->release) + 1; + + strcpy(bcc_ptr, CIFS_NETWORK_OPSYS); + bcc_ptr += strlen(CIFS_NETWORK_OPSYS) + 1; + + *pbcc_area = bcc_ptr; +} + +static void +decode_unicode_ssetup(char **pbcc_area, int bleft, struct cifs_ses *ses, + const struct nls_table *nls_cp) +{ + int len; + char *data = *pbcc_area; + + cifs_dbg(FYI, "bleft %d\n", bleft); + + kfree(ses->serverOS); + ses->serverOS = cifs_strndup_from_utf16(data, bleft, true, nls_cp); + cifs_dbg(FYI, "serverOS=%s\n", ses->serverOS); + len = (UniStrnlen((wchar_t *) data, bleft / 2) * 2) + 2; + data += len; + bleft -= len; + if (bleft <= 0) + return; + + kfree(ses->serverNOS); + ses->serverNOS = cifs_strndup_from_utf16(data, bleft, true, nls_cp); + cifs_dbg(FYI, "serverNOS=%s\n", ses->serverNOS); + len = (UniStrnlen((wchar_t *) data, bleft / 2) * 2) + 2; + data += len; + bleft -= len; + if (bleft <= 0) + return; + + kfree(ses->serverDomain); + ses->serverDomain = cifs_strndup_from_utf16(data, bleft, true, nls_cp); + cifs_dbg(FYI, "serverDomain=%s\n", ses->serverDomain); + + return; +} + +static void decode_ascii_ssetup(char **pbcc_area, __u16 bleft, + struct cifs_ses *ses, + const struct nls_table *nls_cp) +{ + int len; + char *bcc_ptr = *pbcc_area; + + cifs_dbg(FYI, "decode sessetup ascii. bleft %d\n", bleft); + + len = strnlen(bcc_ptr, bleft); + if (len >= bleft) + return; + + kfree(ses->serverOS); + + ses->serverOS = kmalloc(len + 1, GFP_KERNEL); + if (ses->serverOS) { + memcpy(ses->serverOS, bcc_ptr, len); + ses->serverOS[len] = 0; + if (strncmp(ses->serverOS, "OS/2", 4) == 0) + cifs_dbg(FYI, "OS/2 server\n"); + } + + bcc_ptr += len + 1; + bleft -= len + 1; + + len = strnlen(bcc_ptr, bleft); + if (len >= bleft) + return; + + kfree(ses->serverNOS); + + ses->serverNOS = kmalloc(len + 1, GFP_KERNEL); + if (ses->serverNOS) { + memcpy(ses->serverNOS, bcc_ptr, len); + ses->serverNOS[len] = 0; + } + + bcc_ptr += len + 1; + bleft -= len + 1; + + len = strnlen(bcc_ptr, bleft); + if (len > bleft) + return; + + /* No domain field in LANMAN case. Domain is + returned by old servers in the SMB negprot response */ + /* BB For newer servers which do not support Unicode, + but thus do return domain here we could add parsing + for it later, but it is not very important */ + cifs_dbg(FYI, "ascii: bytes left %d\n", bleft); +} +#endif /* CONFIG_CIFS_ALLOW_INSECURE_LEGACY */ + +int decode_ntlmssp_challenge(char *bcc_ptr, int blob_len, + struct cifs_ses *ses) +{ + unsigned int tioffset; /* challenge message target info area */ + unsigned int tilen; /* challenge message target info area length */ + CHALLENGE_MESSAGE *pblob = (CHALLENGE_MESSAGE *)bcc_ptr; + __u32 server_flags; + + if (blob_len < sizeof(CHALLENGE_MESSAGE)) { + cifs_dbg(VFS, "challenge blob len %d too small\n", blob_len); + return -EINVAL; + } + + if (memcmp(pblob->Signature, "NTLMSSP", 8)) { + cifs_dbg(VFS, "blob signature incorrect %s\n", + pblob->Signature); + return -EINVAL; + } + if (pblob->MessageType != NtLmChallenge) { + cifs_dbg(VFS, "Incorrect message type %d\n", + pblob->MessageType); + return -EINVAL; + } + + server_flags = le32_to_cpu(pblob->NegotiateFlags); + cifs_dbg(FYI, "%s: negotiate=0x%08x challenge=0x%08x\n", __func__, + ses->ntlmssp->client_flags, server_flags); + + if ((ses->ntlmssp->client_flags & (NTLMSSP_NEGOTIATE_SEAL | NTLMSSP_NEGOTIATE_SIGN)) && + (!(server_flags & NTLMSSP_NEGOTIATE_56) && !(server_flags & NTLMSSP_NEGOTIATE_128))) { + cifs_dbg(VFS, "%s: requested signing/encryption but server did not return either 56-bit or 128-bit session key size\n", + __func__); + return -EINVAL; + } + if (!(server_flags & NTLMSSP_NEGOTIATE_NTLM) && !(server_flags & NTLMSSP_NEGOTIATE_EXTENDED_SEC)) { + cifs_dbg(VFS, "%s: server does not seem to support either NTLMv1 or NTLMv2\n", __func__); + return -EINVAL; + } + if (ses->server->sign && !(server_flags & NTLMSSP_NEGOTIATE_SIGN)) { + cifs_dbg(VFS, "%s: forced packet signing but server does not seem to support it\n", + __func__); + return -EOPNOTSUPP; + } + if ((ses->ntlmssp->client_flags & NTLMSSP_NEGOTIATE_KEY_XCH) && + !(server_flags & NTLMSSP_NEGOTIATE_KEY_XCH)) + pr_warn_once("%s: authentication has been weakened as server does not support key exchange\n", + __func__); + + ses->ntlmssp->server_flags = server_flags; + + memcpy(ses->ntlmssp->cryptkey, pblob->Challenge, CIFS_CRYPTO_KEY_SIZE); + /* In particular we can examine sign flags */ + /* BB spec says that if AvId field of MsvAvTimestamp is populated then + we must set the MIC field of the AUTHENTICATE_MESSAGE */ + + tioffset = le32_to_cpu(pblob->TargetInfoArray.BufferOffset); + tilen = le16_to_cpu(pblob->TargetInfoArray.Length); + if (tioffset > blob_len || tioffset + tilen > blob_len) { + cifs_dbg(VFS, "tioffset + tilen too high %u + %u\n", + tioffset, tilen); + return -EINVAL; + } + if (tilen) { + kfree_sensitive(ses->auth_key.response); + ses->auth_key.response = kmemdup(bcc_ptr + tioffset, tilen, + GFP_KERNEL); + if (!ses->auth_key.response) { + cifs_dbg(VFS, "Challenge target info alloc failure\n"); + return -ENOMEM; + } + ses->auth_key.len = tilen; + } + + return 0; +} + +static int size_of_ntlmssp_blob(struct cifs_ses *ses, int base_size) +{ + int sz = base_size + ses->auth_key.len + - CIFS_SESS_KEY_SIZE + CIFS_CPHTXT_SIZE + 2; + + if (ses->domainName) + sz += sizeof(__le16) * strnlen(ses->domainName, CIFS_MAX_DOMAINNAME_LEN); + else + sz += sizeof(__le16); + + if (ses->user_name) + sz += sizeof(__le16) * strnlen(ses->user_name, CIFS_MAX_USERNAME_LEN); + else + sz += sizeof(__le16); + + if (ses->workstation_name[0]) + sz += sizeof(__le16) * strnlen(ses->workstation_name, + ntlmssp_workstation_name_size(ses)); + else + sz += sizeof(__le16); + + return sz; +} + +static inline void cifs_security_buffer_from_str(SECURITY_BUFFER *pbuf, + char *str_value, + int str_length, + unsigned char *pstart, + unsigned char **pcur, + const struct nls_table *nls_cp) +{ + unsigned char *tmp = pstart; + int len; + + if (!pbuf) + return; + + if (!pcur) + pcur = &tmp; + + if (!str_value) { + pbuf->BufferOffset = cpu_to_le32(*pcur - pstart); + pbuf->Length = 0; + pbuf->MaximumLength = 0; + *pcur += sizeof(__le16); + } else { + len = cifs_strtoUTF16((__le16 *)*pcur, + str_value, + str_length, + nls_cp); + len *= sizeof(__le16); + pbuf->BufferOffset = cpu_to_le32(*pcur - pstart); + pbuf->Length = cpu_to_le16(len); + pbuf->MaximumLength = cpu_to_le16(len); + *pcur += len; + } +} + +/* BB Move to ntlmssp.c eventually */ + +int build_ntlmssp_negotiate_blob(unsigned char **pbuffer, + u16 *buflen, + struct cifs_ses *ses, + struct TCP_Server_Info *server, + const struct nls_table *nls_cp) +{ + int rc = 0; + NEGOTIATE_MESSAGE *sec_blob; + __u32 flags; + unsigned char *tmp; + int len; + + len = size_of_ntlmssp_blob(ses, sizeof(NEGOTIATE_MESSAGE)); + *pbuffer = kmalloc(len, GFP_KERNEL); + if (!*pbuffer) { + rc = -ENOMEM; + cifs_dbg(VFS, "Error %d during NTLMSSP allocation\n", rc); + *buflen = 0; + goto setup_ntlm_neg_ret; + } + sec_blob = (NEGOTIATE_MESSAGE *)*pbuffer; + + memset(*pbuffer, 0, sizeof(NEGOTIATE_MESSAGE)); + memcpy(sec_blob->Signature, NTLMSSP_SIGNATURE, 8); + sec_blob->MessageType = NtLmNegotiate; + + /* BB is NTLMV2 session security format easier to use here? */ + flags = NTLMSSP_NEGOTIATE_56 | NTLMSSP_REQUEST_TARGET | + NTLMSSP_NEGOTIATE_128 | NTLMSSP_NEGOTIATE_UNICODE | + NTLMSSP_NEGOTIATE_NTLM | NTLMSSP_NEGOTIATE_EXTENDED_SEC | + NTLMSSP_NEGOTIATE_ALWAYS_SIGN | NTLMSSP_NEGOTIATE_SEAL | + NTLMSSP_NEGOTIATE_SIGN; + if (!server->session_estab || ses->ntlmssp->sesskey_per_smbsess) + flags |= NTLMSSP_NEGOTIATE_KEY_XCH; + + tmp = *pbuffer + sizeof(NEGOTIATE_MESSAGE); + ses->ntlmssp->client_flags = flags; + sec_blob->NegotiateFlags = cpu_to_le32(flags); + + /* these fields should be null in negotiate phase MS-NLMP 3.1.5.1.1 */ + cifs_security_buffer_from_str(&sec_blob->DomainName, + NULL, + CIFS_MAX_DOMAINNAME_LEN, + *pbuffer, &tmp, + nls_cp); + + cifs_security_buffer_from_str(&sec_blob->WorkstationName, + NULL, + CIFS_MAX_WORKSTATION_LEN, + *pbuffer, &tmp, + nls_cp); + + *buflen = tmp - *pbuffer; +setup_ntlm_neg_ret: + return rc; +} + +/* + * Build ntlmssp blob with additional fields, such as version, + * supported by modern servers. For safety limit to SMB3 or later + * See notes in MS-NLMP Section 2.2.2.1 e.g. + */ +int build_ntlmssp_smb3_negotiate_blob(unsigned char **pbuffer, + u16 *buflen, + struct cifs_ses *ses, + struct TCP_Server_Info *server, + const struct nls_table *nls_cp) +{ + int rc = 0; + struct negotiate_message *sec_blob; + __u32 flags; + unsigned char *tmp; + int len; + + len = size_of_ntlmssp_blob(ses, sizeof(struct negotiate_message)); + *pbuffer = kmalloc(len, GFP_KERNEL); + if (!*pbuffer) { + rc = -ENOMEM; + cifs_dbg(VFS, "Error %d during NTLMSSP allocation\n", rc); + *buflen = 0; + goto setup_ntlm_smb3_neg_ret; + } + sec_blob = (struct negotiate_message *)*pbuffer; + + memset(*pbuffer, 0, sizeof(struct negotiate_message)); + memcpy(sec_blob->Signature, NTLMSSP_SIGNATURE, 8); + sec_blob->MessageType = NtLmNegotiate; + + /* BB is NTLMV2 session security format easier to use here? */ + flags = NTLMSSP_NEGOTIATE_56 | NTLMSSP_REQUEST_TARGET | + NTLMSSP_NEGOTIATE_128 | NTLMSSP_NEGOTIATE_UNICODE | + NTLMSSP_NEGOTIATE_NTLM | NTLMSSP_NEGOTIATE_EXTENDED_SEC | + NTLMSSP_NEGOTIATE_ALWAYS_SIGN | NTLMSSP_NEGOTIATE_SEAL | + NTLMSSP_NEGOTIATE_SIGN | NTLMSSP_NEGOTIATE_VERSION; + if (!server->session_estab || ses->ntlmssp->sesskey_per_smbsess) + flags |= NTLMSSP_NEGOTIATE_KEY_XCH; + + sec_blob->Version.ProductMajorVersion = LINUX_VERSION_MAJOR; + sec_blob->Version.ProductMinorVersion = LINUX_VERSION_PATCHLEVEL; + sec_blob->Version.ProductBuild = cpu_to_le16(SMB3_PRODUCT_BUILD); + sec_blob->Version.NTLMRevisionCurrent = NTLMSSP_REVISION_W2K3; + + tmp = *pbuffer + sizeof(struct negotiate_message); + ses->ntlmssp->client_flags = flags; + sec_blob->NegotiateFlags = cpu_to_le32(flags); + + /* these fields should be null in negotiate phase MS-NLMP 3.1.5.1.1 */ + cifs_security_buffer_from_str(&sec_blob->DomainName, + NULL, + CIFS_MAX_DOMAINNAME_LEN, + *pbuffer, &tmp, + nls_cp); + + cifs_security_buffer_from_str(&sec_blob->WorkstationName, + NULL, + CIFS_MAX_WORKSTATION_LEN, + *pbuffer, &tmp, + nls_cp); + + *buflen = tmp - *pbuffer; +setup_ntlm_smb3_neg_ret: + return rc; +} + + +int build_ntlmssp_auth_blob(unsigned char **pbuffer, + u16 *buflen, + struct cifs_ses *ses, + struct TCP_Server_Info *server, + const struct nls_table *nls_cp) +{ + int rc; + AUTHENTICATE_MESSAGE *sec_blob; + __u32 flags; + unsigned char *tmp; + int len; + + rc = setup_ntlmv2_rsp(ses, nls_cp); + if (rc) { + cifs_dbg(VFS, "Error %d during NTLMSSP authentication\n", rc); + *buflen = 0; + goto setup_ntlmv2_ret; + } + + len = size_of_ntlmssp_blob(ses, sizeof(AUTHENTICATE_MESSAGE)); + *pbuffer = kmalloc(len, GFP_KERNEL); + if (!*pbuffer) { + rc = -ENOMEM; + cifs_dbg(VFS, "Error %d during NTLMSSP allocation\n", rc); + *buflen = 0; + goto setup_ntlmv2_ret; + } + sec_blob = (AUTHENTICATE_MESSAGE *)*pbuffer; + + memcpy(sec_blob->Signature, NTLMSSP_SIGNATURE, 8); + sec_blob->MessageType = NtLmAuthenticate; + + flags = ses->ntlmssp->server_flags | NTLMSSP_REQUEST_TARGET | + NTLMSSP_NEGOTIATE_TARGET_INFO | NTLMSSP_NEGOTIATE_WORKSTATION_SUPPLIED; + + tmp = *pbuffer + sizeof(AUTHENTICATE_MESSAGE); + sec_blob->NegotiateFlags = cpu_to_le32(flags); + + sec_blob->LmChallengeResponse.BufferOffset = + cpu_to_le32(sizeof(AUTHENTICATE_MESSAGE)); + sec_blob->LmChallengeResponse.Length = 0; + sec_blob->LmChallengeResponse.MaximumLength = 0; + + sec_blob->NtChallengeResponse.BufferOffset = + cpu_to_le32(tmp - *pbuffer); + if (ses->user_name != NULL) { + memcpy(tmp, ses->auth_key.response + CIFS_SESS_KEY_SIZE, + ses->auth_key.len - CIFS_SESS_KEY_SIZE); + tmp += ses->auth_key.len - CIFS_SESS_KEY_SIZE; + + sec_blob->NtChallengeResponse.Length = + cpu_to_le16(ses->auth_key.len - CIFS_SESS_KEY_SIZE); + sec_blob->NtChallengeResponse.MaximumLength = + cpu_to_le16(ses->auth_key.len - CIFS_SESS_KEY_SIZE); + } else { + /* + * don't send an NT Response for anonymous access + */ + sec_blob->NtChallengeResponse.Length = 0; + sec_blob->NtChallengeResponse.MaximumLength = 0; + } + + cifs_security_buffer_from_str(&sec_blob->DomainName, + ses->domainName, + CIFS_MAX_DOMAINNAME_LEN, + *pbuffer, &tmp, + nls_cp); + + cifs_security_buffer_from_str(&sec_blob->UserName, + ses->user_name, + CIFS_MAX_USERNAME_LEN, + *pbuffer, &tmp, + nls_cp); + + cifs_security_buffer_from_str(&sec_blob->WorkstationName, + ses->workstation_name, + ntlmssp_workstation_name_size(ses), + *pbuffer, &tmp, + nls_cp); + + if ((ses->ntlmssp->server_flags & NTLMSSP_NEGOTIATE_KEY_XCH) && + (!ses->server->session_estab || ses->ntlmssp->sesskey_per_smbsess) && + !calc_seckey(ses)) { + memcpy(tmp, ses->ntlmssp->ciphertext, CIFS_CPHTXT_SIZE); + sec_blob->SessionKey.BufferOffset = cpu_to_le32(tmp - *pbuffer); + sec_blob->SessionKey.Length = cpu_to_le16(CIFS_CPHTXT_SIZE); + sec_blob->SessionKey.MaximumLength = + cpu_to_le16(CIFS_CPHTXT_SIZE); + tmp += CIFS_CPHTXT_SIZE; + } else { + sec_blob->SessionKey.BufferOffset = cpu_to_le32(tmp - *pbuffer); + sec_blob->SessionKey.Length = 0; + sec_blob->SessionKey.MaximumLength = 0; + } + + *buflen = tmp - *pbuffer; +setup_ntlmv2_ret: + return rc; +} + +enum securityEnum +cifs_select_sectype(struct TCP_Server_Info *server, enum securityEnum requested) +{ + switch (server->negflavor) { + case CIFS_NEGFLAVOR_EXTENDED: + switch (requested) { + case Kerberos: + case RawNTLMSSP: + return requested; + case Unspecified: + if (server->sec_ntlmssp && + (global_secflags & CIFSSEC_MAY_NTLMSSP)) + return RawNTLMSSP; + if ((server->sec_kerberos || server->sec_mskerberos) && + (global_secflags & CIFSSEC_MAY_KRB5)) + return Kerberos; + fallthrough; + default: + return Unspecified; + } + case CIFS_NEGFLAVOR_UNENCAP: + switch (requested) { + case NTLMv2: + return requested; + case Unspecified: + if (global_secflags & CIFSSEC_MAY_NTLMV2) + return NTLMv2; + break; + default: + break; + } + fallthrough; + default: + return Unspecified; + } +} + +struct sess_data { + unsigned int xid; + struct cifs_ses *ses; + struct TCP_Server_Info *server; + struct nls_table *nls_cp; + void (*func)(struct sess_data *); + int result; + + /* we will send the SMB in three pieces: + * a fixed length beginning part, an optional + * SPNEGO blob (which can be zero length), and a + * last part which will include the strings + * and rest of bcc area. This allows us to avoid + * a large buffer 17K allocation + */ + int buf0_type; + struct kvec iov[3]; +}; + +#ifdef CONFIG_CIFS_ALLOW_INSECURE_LEGACY +static int +sess_alloc_buffer(struct sess_data *sess_data, int wct) +{ + int rc; + struct cifs_ses *ses = sess_data->ses; + struct smb_hdr *smb_buf; + + rc = small_smb_init_no_tc(SMB_COM_SESSION_SETUP_ANDX, wct, ses, + (void **)&smb_buf); + + if (rc) + return rc; + + sess_data->iov[0].iov_base = (char *)smb_buf; + sess_data->iov[0].iov_len = be32_to_cpu(smb_buf->smb_buf_length) + 4; + /* + * This variable will be used to clear the buffer + * allocated above in case of any error in the calling function. + */ + sess_data->buf0_type = CIFS_SMALL_BUFFER; + + /* 2000 big enough to fit max user, domain, NOS name etc. */ + sess_data->iov[2].iov_base = kmalloc(2000, GFP_KERNEL); + if (!sess_data->iov[2].iov_base) { + rc = -ENOMEM; + goto out_free_smb_buf; + } + + return 0; + +out_free_smb_buf: + cifs_small_buf_release(smb_buf); + sess_data->iov[0].iov_base = NULL; + sess_data->iov[0].iov_len = 0; + sess_data->buf0_type = CIFS_NO_BUFFER; + return rc; +} + +static void +sess_free_buffer(struct sess_data *sess_data) +{ + struct kvec *iov = sess_data->iov; + + /* + * Zero the session data before freeing, as it might contain sensitive info (keys, etc). + * Note that iov[1] is already freed by caller. + */ + if (sess_data->buf0_type != CIFS_NO_BUFFER && iov[0].iov_base) + memzero_explicit(iov[0].iov_base, iov[0].iov_len); + + free_rsp_buf(sess_data->buf0_type, iov[0].iov_base); + sess_data->buf0_type = CIFS_NO_BUFFER; + kfree_sensitive(iov[2].iov_base); +} + +static int +sess_establish_session(struct sess_data *sess_data) +{ + struct cifs_ses *ses = sess_data->ses; + struct TCP_Server_Info *server = sess_data->server; + + cifs_server_lock(server); + if (!server->session_estab) { + if (server->sign) { + server->session_key.response = + kmemdup(ses->auth_key.response, + ses->auth_key.len, GFP_KERNEL); + if (!server->session_key.response) { + cifs_server_unlock(server); + return -ENOMEM; + } + server->session_key.len = + ses->auth_key.len; + } + server->sequence_number = 0x2; + server->session_estab = true; + } + cifs_server_unlock(server); + + cifs_dbg(FYI, "CIFS session established successfully\n"); + return 0; +} + +static int +sess_sendreceive(struct sess_data *sess_data) +{ + int rc; + struct smb_hdr *smb_buf = (struct smb_hdr *) sess_data->iov[0].iov_base; + __u16 count; + struct kvec rsp_iov = { NULL, 0 }; + + count = sess_data->iov[1].iov_len + sess_data->iov[2].iov_len; + be32_add_cpu(&smb_buf->smb_buf_length, count); + put_bcc(count, smb_buf); + + rc = SendReceive2(sess_data->xid, sess_data->ses, + sess_data->iov, 3 /* num_iovecs */, + &sess_data->buf0_type, + CIFS_LOG_ERROR, &rsp_iov); + cifs_small_buf_release(sess_data->iov[0].iov_base); + memcpy(&sess_data->iov[0], &rsp_iov, sizeof(struct kvec)); + + return rc; +} + +static void +sess_auth_ntlmv2(struct sess_data *sess_data) +{ + int rc = 0; + struct smb_hdr *smb_buf; + SESSION_SETUP_ANDX *pSMB; + char *bcc_ptr; + struct cifs_ses *ses = sess_data->ses; + struct TCP_Server_Info *server = sess_data->server; + __u32 capabilities; + __u16 bytes_remaining; + + /* old style NTLM sessionsetup */ + /* wct = 13 */ + rc = sess_alloc_buffer(sess_data, 13); + if (rc) + goto out; + + pSMB = (SESSION_SETUP_ANDX *)sess_data->iov[0].iov_base; + bcc_ptr = sess_data->iov[2].iov_base; + capabilities = cifs_ssetup_hdr(ses, server, pSMB); + + pSMB->req_no_secext.Capabilities = cpu_to_le32(capabilities); + + /* LM2 password would be here if we supported it */ + pSMB->req_no_secext.CaseInsensitivePasswordLength = 0; + + if (ses->user_name != NULL) { + /* calculate nlmv2 response and session key */ + rc = setup_ntlmv2_rsp(ses, sess_data->nls_cp); + if (rc) { + cifs_dbg(VFS, "Error %d during NTLMv2 authentication\n", rc); + goto out; + } + + memcpy(bcc_ptr, ses->auth_key.response + CIFS_SESS_KEY_SIZE, + ses->auth_key.len - CIFS_SESS_KEY_SIZE); + bcc_ptr += ses->auth_key.len - CIFS_SESS_KEY_SIZE; + + /* set case sensitive password length after tilen may get + * assigned, tilen is 0 otherwise. + */ + pSMB->req_no_secext.CaseSensitivePasswordLength = + cpu_to_le16(ses->auth_key.len - CIFS_SESS_KEY_SIZE); + } else { + pSMB->req_no_secext.CaseSensitivePasswordLength = 0; + } + + if (ses->capabilities & CAP_UNICODE) { + if (!IS_ALIGNED(sess_data->iov[0].iov_len, 2)) { + *bcc_ptr = 0; + bcc_ptr++; + } + unicode_ssetup_strings(&bcc_ptr, ses, sess_data->nls_cp); + } else { + ascii_ssetup_strings(&bcc_ptr, ses, sess_data->nls_cp); + } + + + sess_data->iov[2].iov_len = (long) bcc_ptr - + (long) sess_data->iov[2].iov_base; + + rc = sess_sendreceive(sess_data); + if (rc) + goto out; + + pSMB = (SESSION_SETUP_ANDX *)sess_data->iov[0].iov_base; + smb_buf = (struct smb_hdr *)sess_data->iov[0].iov_base; + + if (smb_buf->WordCount != 3) { + rc = -EIO; + cifs_dbg(VFS, "bad word count %d\n", smb_buf->WordCount); + goto out; + } + + if (le16_to_cpu(pSMB->resp.Action) & GUEST_LOGIN) + cifs_dbg(FYI, "Guest login\n"); /* BB mark SesInfo struct? */ + + ses->Suid = smb_buf->Uid; /* UID left in wire format (le) */ + cifs_dbg(FYI, "UID = %llu\n", ses->Suid); + + bytes_remaining = get_bcc(smb_buf); + bcc_ptr = pByteArea(smb_buf); + + /* BB check if Unicode and decode strings */ + if (bytes_remaining == 0) { + /* no string area to decode, do nothing */ + } else if (smb_buf->Flags2 & SMBFLG2_UNICODE) { + /* unicode string area must be word-aligned */ + if (!IS_ALIGNED((unsigned long)bcc_ptr - (unsigned long)smb_buf, 2)) { + ++bcc_ptr; + --bytes_remaining; + } + decode_unicode_ssetup(&bcc_ptr, bytes_remaining, ses, + sess_data->nls_cp); + } else { + decode_ascii_ssetup(&bcc_ptr, bytes_remaining, ses, + sess_data->nls_cp); + } + + rc = sess_establish_session(sess_data); +out: + sess_data->result = rc; + sess_data->func = NULL; + sess_free_buffer(sess_data); + kfree_sensitive(ses->auth_key.response); + ses->auth_key.response = NULL; +} + +#ifdef CONFIG_CIFS_UPCALL +static void +sess_auth_kerberos(struct sess_data *sess_data) +{ + int rc = 0; + struct smb_hdr *smb_buf; + SESSION_SETUP_ANDX *pSMB; + char *bcc_ptr; + struct cifs_ses *ses = sess_data->ses; + struct TCP_Server_Info *server = sess_data->server; + __u32 capabilities; + __u16 bytes_remaining; + struct key *spnego_key = NULL; + struct cifs_spnego_msg *msg; + u16 blob_len; + + /* extended security */ + /* wct = 12 */ + rc = sess_alloc_buffer(sess_data, 12); + if (rc) + goto out; + + pSMB = (SESSION_SETUP_ANDX *)sess_data->iov[0].iov_base; + bcc_ptr = sess_data->iov[2].iov_base; + capabilities = cifs_ssetup_hdr(ses, server, pSMB); + + spnego_key = cifs_get_spnego_key(ses, server); + if (IS_ERR(spnego_key)) { + rc = PTR_ERR(spnego_key); + spnego_key = NULL; + goto out; + } + + msg = spnego_key->payload.data[0]; + /* + * check version field to make sure that cifs.upcall is + * sending us a response in an expected form + */ + if (msg->version != CIFS_SPNEGO_UPCALL_VERSION) { + cifs_dbg(VFS, "incorrect version of cifs.upcall (expected %d but got %d)\n", + CIFS_SPNEGO_UPCALL_VERSION, msg->version); + rc = -EKEYREJECTED; + goto out_put_spnego_key; + } + + kfree_sensitive(ses->auth_key.response); + ses->auth_key.response = kmemdup(msg->data, msg->sesskey_len, + GFP_KERNEL); + if (!ses->auth_key.response) { + cifs_dbg(VFS, "Kerberos can't allocate (%u bytes) memory\n", + msg->sesskey_len); + rc = -ENOMEM; + goto out_put_spnego_key; + } + ses->auth_key.len = msg->sesskey_len; + + pSMB->req.hdr.Flags2 |= SMBFLG2_EXT_SEC; + capabilities |= CAP_EXTENDED_SECURITY; + pSMB->req.Capabilities = cpu_to_le32(capabilities); + sess_data->iov[1].iov_base = msg->data + msg->sesskey_len; + sess_data->iov[1].iov_len = msg->secblob_len; + pSMB->req.SecurityBlobLength = cpu_to_le16(sess_data->iov[1].iov_len); + + if (ses->capabilities & CAP_UNICODE) { + /* unicode strings must be word aligned */ + if (!IS_ALIGNED(sess_data->iov[0].iov_len + sess_data->iov[1].iov_len, 2)) { + *bcc_ptr = 0; + bcc_ptr++; + } + unicode_oslm_strings(&bcc_ptr, sess_data->nls_cp); + unicode_domain_string(&bcc_ptr, ses, sess_data->nls_cp); + } else { + /* BB: is this right? */ + ascii_ssetup_strings(&bcc_ptr, ses, sess_data->nls_cp); + } + + sess_data->iov[2].iov_len = (long) bcc_ptr - + (long) sess_data->iov[2].iov_base; + + rc = sess_sendreceive(sess_data); + if (rc) + goto out_put_spnego_key; + + pSMB = (SESSION_SETUP_ANDX *)sess_data->iov[0].iov_base; + smb_buf = (struct smb_hdr *)sess_data->iov[0].iov_base; + + if (smb_buf->WordCount != 4) { + rc = -EIO; + cifs_dbg(VFS, "bad word count %d\n", smb_buf->WordCount); + goto out_put_spnego_key; + } + + if (le16_to_cpu(pSMB->resp.Action) & GUEST_LOGIN) + cifs_dbg(FYI, "Guest login\n"); /* BB mark SesInfo struct? */ + + ses->Suid = smb_buf->Uid; /* UID left in wire format (le) */ + cifs_dbg(FYI, "UID = %llu\n", ses->Suid); + + bytes_remaining = get_bcc(smb_buf); + bcc_ptr = pByteArea(smb_buf); + + blob_len = le16_to_cpu(pSMB->resp.SecurityBlobLength); + if (blob_len > bytes_remaining) { + cifs_dbg(VFS, "bad security blob length %d\n", + blob_len); + rc = -EINVAL; + goto out_put_spnego_key; + } + bcc_ptr += blob_len; + bytes_remaining -= blob_len; + + /* BB check if Unicode and decode strings */ + if (bytes_remaining == 0) { + /* no string area to decode, do nothing */ + } else if (smb_buf->Flags2 & SMBFLG2_UNICODE) { + /* unicode string area must be word-aligned */ + if (!IS_ALIGNED((unsigned long)bcc_ptr - (unsigned long)smb_buf, 2)) { + ++bcc_ptr; + --bytes_remaining; + } + decode_unicode_ssetup(&bcc_ptr, bytes_remaining, ses, + sess_data->nls_cp); + } else { + decode_ascii_ssetup(&bcc_ptr, bytes_remaining, ses, + sess_data->nls_cp); + } + + rc = sess_establish_session(sess_data); +out_put_spnego_key: + key_invalidate(spnego_key); + key_put(spnego_key); +out: + sess_data->result = rc; + sess_data->func = NULL; + sess_free_buffer(sess_data); + kfree_sensitive(ses->auth_key.response); + ses->auth_key.response = NULL; +} + +#endif /* ! CONFIG_CIFS_UPCALL */ + +/* + * The required kvec buffers have to be allocated before calling this + * function. + */ +static int +_sess_auth_rawntlmssp_assemble_req(struct sess_data *sess_data) +{ + SESSION_SETUP_ANDX *pSMB; + struct cifs_ses *ses = sess_data->ses; + struct TCP_Server_Info *server = sess_data->server; + __u32 capabilities; + char *bcc_ptr; + + pSMB = (SESSION_SETUP_ANDX *)sess_data->iov[0].iov_base; + + capabilities = cifs_ssetup_hdr(ses, server, pSMB); + if ((pSMB->req.hdr.Flags2 & SMBFLG2_UNICODE) == 0) { + cifs_dbg(VFS, "NTLMSSP requires Unicode support\n"); + return -ENOSYS; + } + + pSMB->req.hdr.Flags2 |= SMBFLG2_EXT_SEC; + capabilities |= CAP_EXTENDED_SECURITY; + pSMB->req.Capabilities |= cpu_to_le32(capabilities); + + bcc_ptr = sess_data->iov[2].iov_base; + /* unicode strings must be word aligned */ + if (!IS_ALIGNED(sess_data->iov[0].iov_len + sess_data->iov[1].iov_len, 2)) { + *bcc_ptr = 0; + bcc_ptr++; + } + unicode_oslm_strings(&bcc_ptr, sess_data->nls_cp); + + sess_data->iov[2].iov_len = (long) bcc_ptr - + (long) sess_data->iov[2].iov_base; + + return 0; +} + +static void +sess_auth_rawntlmssp_authenticate(struct sess_data *sess_data); + +static void +sess_auth_rawntlmssp_negotiate(struct sess_data *sess_data) +{ + int rc; + struct smb_hdr *smb_buf; + SESSION_SETUP_ANDX *pSMB; + struct cifs_ses *ses = sess_data->ses; + struct TCP_Server_Info *server = sess_data->server; + __u16 bytes_remaining; + char *bcc_ptr; + unsigned char *ntlmsspblob = NULL; + u16 blob_len; + + cifs_dbg(FYI, "rawntlmssp session setup negotiate phase\n"); + + /* + * if memory allocation is successful, caller of this function + * frees it. + */ + ses->ntlmssp = kmalloc(sizeof(struct ntlmssp_auth), GFP_KERNEL); + if (!ses->ntlmssp) { + rc = -ENOMEM; + goto out; + } + ses->ntlmssp->sesskey_per_smbsess = false; + + /* wct = 12 */ + rc = sess_alloc_buffer(sess_data, 12); + if (rc) + goto out; + + pSMB = (SESSION_SETUP_ANDX *)sess_data->iov[0].iov_base; + + /* Build security blob before we assemble the request */ + rc = build_ntlmssp_negotiate_blob(&ntlmsspblob, + &blob_len, ses, server, + sess_data->nls_cp); + if (rc) + goto out_free_ntlmsspblob; + + sess_data->iov[1].iov_len = blob_len; + sess_data->iov[1].iov_base = ntlmsspblob; + pSMB->req.SecurityBlobLength = cpu_to_le16(blob_len); + + rc = _sess_auth_rawntlmssp_assemble_req(sess_data); + if (rc) + goto out_free_ntlmsspblob; + + rc = sess_sendreceive(sess_data); + + pSMB = (SESSION_SETUP_ANDX *)sess_data->iov[0].iov_base; + smb_buf = (struct smb_hdr *)sess_data->iov[0].iov_base; + + /* If true, rc here is expected and not an error */ + if (sess_data->buf0_type != CIFS_NO_BUFFER && + smb_buf->Status.CifsError == + cpu_to_le32(NT_STATUS_MORE_PROCESSING_REQUIRED)) + rc = 0; + + if (rc) + goto out_free_ntlmsspblob; + + cifs_dbg(FYI, "rawntlmssp session setup challenge phase\n"); + + if (smb_buf->WordCount != 4) { + rc = -EIO; + cifs_dbg(VFS, "bad word count %d\n", smb_buf->WordCount); + goto out_free_ntlmsspblob; + } + + ses->Suid = smb_buf->Uid; /* UID left in wire format (le) */ + cifs_dbg(FYI, "UID = %llu\n", ses->Suid); + + bytes_remaining = get_bcc(smb_buf); + bcc_ptr = pByteArea(smb_buf); + + blob_len = le16_to_cpu(pSMB->resp.SecurityBlobLength); + if (blob_len > bytes_remaining) { + cifs_dbg(VFS, "bad security blob length %d\n", + blob_len); + rc = -EINVAL; + goto out_free_ntlmsspblob; + } + + rc = decode_ntlmssp_challenge(bcc_ptr, blob_len, ses); + +out_free_ntlmsspblob: + kfree_sensitive(ntlmsspblob); +out: + sess_free_buffer(sess_data); + + if (!rc) { + sess_data->func = sess_auth_rawntlmssp_authenticate; + return; + } + + /* Else error. Cleanup */ + kfree_sensitive(ses->auth_key.response); + ses->auth_key.response = NULL; + kfree_sensitive(ses->ntlmssp); + ses->ntlmssp = NULL; + + sess_data->func = NULL; + sess_data->result = rc; +} + +static void +sess_auth_rawntlmssp_authenticate(struct sess_data *sess_data) +{ + int rc; + struct smb_hdr *smb_buf; + SESSION_SETUP_ANDX *pSMB; + struct cifs_ses *ses = sess_data->ses; + struct TCP_Server_Info *server = sess_data->server; + __u16 bytes_remaining; + char *bcc_ptr; + unsigned char *ntlmsspblob = NULL; + u16 blob_len; + + cifs_dbg(FYI, "rawntlmssp session setup authenticate phase\n"); + + /* wct = 12 */ + rc = sess_alloc_buffer(sess_data, 12); + if (rc) + goto out; + + /* Build security blob before we assemble the request */ + pSMB = (SESSION_SETUP_ANDX *)sess_data->iov[0].iov_base; + smb_buf = (struct smb_hdr *)pSMB; + rc = build_ntlmssp_auth_blob(&ntlmsspblob, + &blob_len, ses, server, + sess_data->nls_cp); + if (rc) + goto out_free_ntlmsspblob; + sess_data->iov[1].iov_len = blob_len; + sess_data->iov[1].iov_base = ntlmsspblob; + pSMB->req.SecurityBlobLength = cpu_to_le16(blob_len); + /* + * Make sure that we tell the server that we are using + * the uid that it just gave us back on the response + * (challenge) + */ + smb_buf->Uid = ses->Suid; + + rc = _sess_auth_rawntlmssp_assemble_req(sess_data); + if (rc) + goto out_free_ntlmsspblob; + + rc = sess_sendreceive(sess_data); + if (rc) + goto out_free_ntlmsspblob; + + pSMB = (SESSION_SETUP_ANDX *)sess_data->iov[0].iov_base; + smb_buf = (struct smb_hdr *)sess_data->iov[0].iov_base; + if (smb_buf->WordCount != 4) { + rc = -EIO; + cifs_dbg(VFS, "bad word count %d\n", smb_buf->WordCount); + goto out_free_ntlmsspblob; + } + + if (le16_to_cpu(pSMB->resp.Action) & GUEST_LOGIN) + cifs_dbg(FYI, "Guest login\n"); /* BB mark SesInfo struct? */ + + if (ses->Suid != smb_buf->Uid) { + ses->Suid = smb_buf->Uid; + cifs_dbg(FYI, "UID changed! new UID = %llu\n", ses->Suid); + } + + bytes_remaining = get_bcc(smb_buf); + bcc_ptr = pByteArea(smb_buf); + blob_len = le16_to_cpu(pSMB->resp.SecurityBlobLength); + if (blob_len > bytes_remaining) { + cifs_dbg(VFS, "bad security blob length %d\n", + blob_len); + rc = -EINVAL; + goto out_free_ntlmsspblob; + } + bcc_ptr += blob_len; + bytes_remaining -= blob_len; + + + /* BB check if Unicode and decode strings */ + if (bytes_remaining == 0) { + /* no string area to decode, do nothing */ + } else if (smb_buf->Flags2 & SMBFLG2_UNICODE) { + /* unicode string area must be word-aligned */ + if (!IS_ALIGNED((unsigned long)bcc_ptr - (unsigned long)smb_buf, 2)) { + ++bcc_ptr; + --bytes_remaining; + } + decode_unicode_ssetup(&bcc_ptr, bytes_remaining, ses, + sess_data->nls_cp); + } else { + decode_ascii_ssetup(&bcc_ptr, bytes_remaining, ses, + sess_data->nls_cp); + } + +out_free_ntlmsspblob: + kfree_sensitive(ntlmsspblob); +out: + sess_free_buffer(sess_data); + + if (!rc) + rc = sess_establish_session(sess_data); + + /* Cleanup */ + kfree_sensitive(ses->auth_key.response); + ses->auth_key.response = NULL; + kfree_sensitive(ses->ntlmssp); + ses->ntlmssp = NULL; + + sess_data->func = NULL; + sess_data->result = rc; +} + +static int select_sec(struct sess_data *sess_data) +{ + int type; + struct cifs_ses *ses = sess_data->ses; + struct TCP_Server_Info *server = sess_data->server; + + type = cifs_select_sectype(server, ses->sectype); + cifs_dbg(FYI, "sess setup type %d\n", type); + if (type == Unspecified) { + cifs_dbg(VFS, "Unable to select appropriate authentication method!\n"); + return -EINVAL; + } + + switch (type) { + case NTLMv2: + sess_data->func = sess_auth_ntlmv2; + break; + case Kerberos: +#ifdef CONFIG_CIFS_UPCALL + sess_data->func = sess_auth_kerberos; + break; +#else + cifs_dbg(VFS, "Kerberos negotiated but upcall support disabled!\n"); + return -ENOSYS; +#endif /* CONFIG_CIFS_UPCALL */ + case RawNTLMSSP: + sess_data->func = sess_auth_rawntlmssp_negotiate; + break; + default: + cifs_dbg(VFS, "secType %d not supported!\n", type); + return -ENOSYS; + } + + return 0; +} + +int CIFS_SessSetup(const unsigned int xid, struct cifs_ses *ses, + struct TCP_Server_Info *server, + const struct nls_table *nls_cp) +{ + int rc = 0; + struct sess_data *sess_data; + + if (ses == NULL) { + WARN(1, "%s: ses == NULL!", __func__); + return -EINVAL; + } + + sess_data = kzalloc(sizeof(struct sess_data), GFP_KERNEL); + if (!sess_data) + return -ENOMEM; + + sess_data->xid = xid; + sess_data->ses = ses; + sess_data->server = server; + sess_data->buf0_type = CIFS_NO_BUFFER; + sess_data->nls_cp = (struct nls_table *) nls_cp; + + rc = select_sec(sess_data); + if (rc) + goto out; + + while (sess_data->func) + sess_data->func(sess_data); + + /* Store result before we free sess_data */ + rc = sess_data->result; + +out: + kfree_sensitive(sess_data); + return rc; +} +#endif /* CONFIG_CIFS_ALLOW_INSECURE_LEGACY */ diff --git a/fs/smb/client/smb1ops.c b/fs/smb/client/smb1ops.c new file mode 100644 index 0000000000000..7d1b3fc014d94 --- /dev/null +++ b/fs/smb/client/smb1ops.c @@ -0,0 +1,1276 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * SMB1 (CIFS) version specific operations + * + * Copyright (c) 2012, Jeff Layton + */ + +#include +#include +#include +#include "cifsglob.h" +#include "cifsproto.h" +#include "cifs_debug.h" +#include "cifspdu.h" +#include "cifs_unicode.h" +#include "fs_context.h" + +/* + * An NT cancel request header looks just like the original request except: + * + * The Command is SMB_COM_NT_CANCEL + * The WordCount is zeroed out + * The ByteCount is zeroed out + * + * This function mangles an existing request buffer into a + * SMB_COM_NT_CANCEL request and then sends it. + */ +static int +send_nt_cancel(struct TCP_Server_Info *server, struct smb_rqst *rqst, + struct mid_q_entry *mid) +{ + int rc = 0; + struct smb_hdr *in_buf = (struct smb_hdr *)rqst->rq_iov[0].iov_base; + + /* -4 for RFC1001 length and +2 for BCC field */ + in_buf->smb_buf_length = cpu_to_be32(sizeof(struct smb_hdr) - 4 + 2); + in_buf->Command = SMB_COM_NT_CANCEL; + in_buf->WordCount = 0; + put_bcc(0, in_buf); + + cifs_server_lock(server); + rc = cifs_sign_smb(in_buf, server, &mid->sequence_number); + if (rc) { + cifs_server_unlock(server); + return rc; + } + + /* + * The response to this call was already factored into the sequence + * number when the call went out, so we must adjust it back downward + * after signing here. + */ + --server->sequence_number; + rc = smb_send(server, in_buf, be32_to_cpu(in_buf->smb_buf_length)); + if (rc < 0) + server->sequence_number--; + + cifs_server_unlock(server); + + cifs_dbg(FYI, "issued NT_CANCEL for mid %u, rc = %d\n", + get_mid(in_buf), rc); + + return rc; +} + +static bool +cifs_compare_fids(struct cifsFileInfo *ob1, struct cifsFileInfo *ob2) +{ + return ob1->fid.netfid == ob2->fid.netfid; +} + +static unsigned int +cifs_read_data_offset(char *buf) +{ + READ_RSP *rsp = (READ_RSP *)buf; + return le16_to_cpu(rsp->DataOffset); +} + +static unsigned int +cifs_read_data_length(char *buf, bool in_remaining) +{ + READ_RSP *rsp = (READ_RSP *)buf; + /* It's a bug reading remaining data for SMB1 packets */ + WARN_ON(in_remaining); + return (le16_to_cpu(rsp->DataLengthHigh) << 16) + + le16_to_cpu(rsp->DataLength); +} + +static struct mid_q_entry * +cifs_find_mid(struct TCP_Server_Info *server, char *buffer) +{ + struct smb_hdr *buf = (struct smb_hdr *)buffer; + struct mid_q_entry *mid; + + spin_lock(&server->mid_lock); + list_for_each_entry(mid, &server->pending_mid_q, qhead) { + if (compare_mid(mid->mid, buf) && + mid->mid_state == MID_REQUEST_SUBMITTED && + le16_to_cpu(mid->command) == buf->Command) { + kref_get(&mid->refcount); + spin_unlock(&server->mid_lock); + return mid; + } + } + spin_unlock(&server->mid_lock); + return NULL; +} + +static void +cifs_add_credits(struct TCP_Server_Info *server, + const struct cifs_credits *credits, const int optype) +{ + spin_lock(&server->req_lock); + server->credits += credits->value; + server->in_flight--; + spin_unlock(&server->req_lock); + wake_up(&server->request_q); +} + +static void +cifs_set_credits(struct TCP_Server_Info *server, const int val) +{ + spin_lock(&server->req_lock); + server->credits = val; + server->oplocks = val > 1 ? enable_oplocks : false; + spin_unlock(&server->req_lock); +} + +static int * +cifs_get_credits_field(struct TCP_Server_Info *server, const int optype) +{ + return &server->credits; +} + +static unsigned int +cifs_get_credits(struct mid_q_entry *mid) +{ + return 1; +} + +/* + * 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. + */ +static __u64 +cifs_get_next_mid(struct TCP_Server_Info *server) +{ + __u64 mid = 0; + __u16 last_mid, cur_mid; + bool collision, reconnect = false; + + spin_lock(&server->mid_lock); + + /* mid is 16 bit only for CIFS/SMB */ + cur_mid = (__u16)((server->CurrentMid) & 0xffff); + /* we do not want to loop forever */ + last_mid = cur_mid; + cur_mid++; + /* avoid 0xFFFF MID */ + if (cur_mid == 0xffff) + cur_mid++; + + /* + * 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 (cur_mid != last_mid) { + struct mid_q_entry *mid_entry; + unsigned int num_mids; + + collision = false; + if (cur_mid == 0) + cur_mid++; + + num_mids = 0; + list_for_each_entry(mid_entry, &server->pending_mid_q, qhead) { + ++num_mids; + if (mid_entry->mid == cur_mid && + mid_entry->mid_state == MID_REQUEST_SUBMITTED) { + /* This mid is in use, try a different one */ + collision = true; + break; + } + } + + /* + * if we have more than 32k mids in the list, then something + * is very wrong. Possibly a local user is trying to DoS the + * box by issuing long-running calls and SIGKILL'ing them. If + * we get to 2^16 mids then we're in big trouble as this + * function could loop forever. + * + * Go ahead and assign out the mid in this situation, but force + * an eventual reconnect to clean out the pending_mid_q. + */ + if (num_mids > 32768) + reconnect = true; + + if (!collision) { + mid = (__u64)cur_mid; + server->CurrentMid = mid; + break; + } + cur_mid++; + } + spin_unlock(&server->mid_lock); + + if (reconnect) { + cifs_signal_cifsd_for_reconnect(server, false); + } + + return mid; +} + +/* + return codes: + 0 not a transact2, or all data present + >0 transact2 with that much data missing + -EINVAL invalid transact2 + */ +static int +check2ndT2(char *buf) +{ + struct smb_hdr *pSMB = (struct smb_hdr *)buf; + struct smb_t2_rsp *pSMBt; + int remaining; + __u16 total_data_size, data_in_this_rsp; + + if (pSMB->Command != SMB_COM_TRANSACTION2) + return 0; + + /* check for plausible wct, bcc and t2 data and parm sizes */ + /* check for parm and data offset going beyond end of smb */ + if (pSMB->WordCount != 10) { /* coalesce_t2 depends on this */ + cifs_dbg(FYI, "Invalid transact2 word count\n"); + return -EINVAL; + } + + pSMBt = (struct smb_t2_rsp *)pSMB; + + total_data_size = get_unaligned_le16(&pSMBt->t2_rsp.TotalDataCount); + data_in_this_rsp = get_unaligned_le16(&pSMBt->t2_rsp.DataCount); + + if (total_data_size == data_in_this_rsp) + return 0; + else if (total_data_size < data_in_this_rsp) { + cifs_dbg(FYI, "total data %d smaller than data in frame %d\n", + total_data_size, data_in_this_rsp); + return -EINVAL; + } + + remaining = total_data_size - data_in_this_rsp; + + cifs_dbg(FYI, "missing %d bytes from transact2, check next response\n", + remaining); + if (total_data_size > CIFSMaxBufSize) { + cifs_dbg(VFS, "TotalDataSize %d is over maximum buffer %d\n", + total_data_size, CIFSMaxBufSize); + return -EINVAL; + } + return remaining; +} + +static int +coalesce_t2(char *second_buf, struct smb_hdr *target_hdr) +{ + struct smb_t2_rsp *pSMBs = (struct smb_t2_rsp *)second_buf; + struct smb_t2_rsp *pSMBt = (struct smb_t2_rsp *)target_hdr; + char *data_area_of_tgt; + char *data_area_of_src; + int remaining; + unsigned int byte_count, total_in_tgt; + __u16 tgt_total_cnt, src_total_cnt, total_in_src; + + src_total_cnt = get_unaligned_le16(&pSMBs->t2_rsp.TotalDataCount); + tgt_total_cnt = get_unaligned_le16(&pSMBt->t2_rsp.TotalDataCount); + + if (tgt_total_cnt != src_total_cnt) + cifs_dbg(FYI, "total data count of primary and secondary t2 differ source=%hu target=%hu\n", + src_total_cnt, tgt_total_cnt); + + total_in_tgt = get_unaligned_le16(&pSMBt->t2_rsp.DataCount); + + remaining = tgt_total_cnt - total_in_tgt; + + if (remaining < 0) { + cifs_dbg(FYI, "Server sent too much data. tgt_total_cnt=%hu total_in_tgt=%u\n", + tgt_total_cnt, total_in_tgt); + return -EPROTO; + } + + if (remaining == 0) { + /* nothing to do, ignore */ + cifs_dbg(FYI, "no more data remains\n"); + return 0; + } + + total_in_src = get_unaligned_le16(&pSMBs->t2_rsp.DataCount); + if (remaining < total_in_src) + cifs_dbg(FYI, "transact2 2nd response contains too much data\n"); + + /* find end of first SMB data area */ + data_area_of_tgt = (char *)&pSMBt->hdr.Protocol + + get_unaligned_le16(&pSMBt->t2_rsp.DataOffset); + + /* validate target area */ + data_area_of_src = (char *)&pSMBs->hdr.Protocol + + get_unaligned_le16(&pSMBs->t2_rsp.DataOffset); + + data_area_of_tgt += total_in_tgt; + + total_in_tgt += total_in_src; + /* is the result too big for the field? */ + if (total_in_tgt > USHRT_MAX) { + cifs_dbg(FYI, "coalesced DataCount too large (%u)\n", + total_in_tgt); + return -EPROTO; + } + put_unaligned_le16(total_in_tgt, &pSMBt->t2_rsp.DataCount); + + /* fix up the BCC */ + byte_count = get_bcc(target_hdr); + byte_count += total_in_src; + /* is the result too big for the field? */ + if (byte_count > USHRT_MAX) { + cifs_dbg(FYI, "coalesced BCC too large (%u)\n", byte_count); + return -EPROTO; + } + put_bcc(byte_count, target_hdr); + + byte_count = be32_to_cpu(target_hdr->smb_buf_length); + byte_count += total_in_src; + /* don't allow buffer to overflow */ + if (byte_count > CIFSMaxBufSize + MAX_CIFS_HDR_SIZE - 4) { + cifs_dbg(FYI, "coalesced BCC exceeds buffer size (%u)\n", + byte_count); + return -ENOBUFS; + } + target_hdr->smb_buf_length = cpu_to_be32(byte_count); + + /* copy second buffer into end of first buffer */ + memcpy(data_area_of_tgt, data_area_of_src, total_in_src); + + if (remaining != total_in_src) { + /* more responses to go */ + cifs_dbg(FYI, "waiting for more secondary responses\n"); + return 1; + } + + /* we are done */ + cifs_dbg(FYI, "found the last secondary response\n"); + return 0; +} + +static void +cifs_downgrade_oplock(struct TCP_Server_Info *server, + struct cifsInodeInfo *cinode, __u32 oplock, + unsigned int epoch, bool *purge_cache) +{ + cifs_set_oplock_level(cinode, oplock); +} + +static bool +cifs_check_trans2(struct mid_q_entry *mid, struct TCP_Server_Info *server, + char *buf, int malformed) +{ + if (malformed) + return false; + if (check2ndT2(buf) <= 0) + return false; + mid->multiRsp = true; + if (mid->resp_buf) { + /* merge response - fix up 1st*/ + malformed = coalesce_t2(buf, mid->resp_buf); + if (malformed > 0) + return true; + /* All parts received or packet is malformed. */ + mid->multiEnd = true; + dequeue_mid(mid, malformed); + return true; + } + if (!server->large_buf) { + /*FIXME: switch to already allocated largebuf?*/ + cifs_dbg(VFS, "1st trans2 resp needs bigbuf\n"); + } else { + /* Have first buffer */ + mid->resp_buf = buf; + mid->large_buf = true; + server->bigbuf = NULL; + } + return true; +} + +static bool +cifs_need_neg(struct TCP_Server_Info *server) +{ + return server->maxBuf == 0; +} + +static int +cifs_negotiate(const unsigned int xid, + struct cifs_ses *ses, + struct TCP_Server_Info *server) +{ + int rc; + rc = CIFSSMBNegotiate(xid, ses, server); + if (rc == -EAGAIN) { + /* retry only once on 1st time connection */ + set_credits(server, 1); + rc = CIFSSMBNegotiate(xid, ses, server); + if (rc == -EAGAIN) + rc = -EHOSTDOWN; + } + return rc; +} + +static unsigned int +cifs_negotiate_wsize(struct cifs_tcon *tcon, struct smb3_fs_context *ctx) +{ + __u64 unix_cap = le64_to_cpu(tcon->fsUnixInfo.Capability); + struct TCP_Server_Info *server = tcon->ses->server; + unsigned int wsize; + + /* start with specified wsize, or default */ + if (ctx->wsize) + wsize = ctx->wsize; + else if (tcon->unix_ext && (unix_cap & CIFS_UNIX_LARGE_WRITE_CAP)) + wsize = CIFS_DEFAULT_IOSIZE; + else + wsize = CIFS_DEFAULT_NON_POSIX_WSIZE; + + /* can server support 24-bit write sizes? (via UNIX extensions) */ + if (!tcon->unix_ext || !(unix_cap & CIFS_UNIX_LARGE_WRITE_CAP)) + wsize = min_t(unsigned int, wsize, CIFS_MAX_RFC1002_WSIZE); + + /* + * no CAP_LARGE_WRITE_X or is signing enabled without CAP_UNIX set? + * Limit it to max buffer offered by the server, minus the size of the + * WRITEX header, not including the 4 byte RFC1001 length. + */ + if (!(server->capabilities & CAP_LARGE_WRITE_X) || + (!(server->capabilities & CAP_UNIX) && server->sign)) + wsize = min_t(unsigned int, wsize, + server->maxBuf - sizeof(WRITE_REQ) + 4); + + /* hard limit of CIFS_MAX_WSIZE */ + wsize = min_t(unsigned int, wsize, CIFS_MAX_WSIZE); + + return wsize; +} + +static unsigned int +cifs_negotiate_rsize(struct cifs_tcon *tcon, struct smb3_fs_context *ctx) +{ + __u64 unix_cap = le64_to_cpu(tcon->fsUnixInfo.Capability); + struct TCP_Server_Info *server = tcon->ses->server; + unsigned int rsize, defsize; + + /* + * Set default value... + * + * HACK alert! Ancient servers have very small buffers. Even though + * MS-CIFS indicates that servers are only limited by the client's + * bufsize for reads, testing against win98se shows that it throws + * INVALID_PARAMETER errors if you try to request too large a read. + * OS/2 just sends back short reads. + * + * If the server doesn't advertise CAP_LARGE_READ_X, then assume that + * it can't handle a read request larger than its MaxBufferSize either. + */ + if (tcon->unix_ext && (unix_cap & CIFS_UNIX_LARGE_READ_CAP)) + defsize = CIFS_DEFAULT_IOSIZE; + else if (server->capabilities & CAP_LARGE_READ_X) + defsize = CIFS_DEFAULT_NON_POSIX_RSIZE; + else + defsize = server->maxBuf - sizeof(READ_RSP); + + rsize = ctx->rsize ? ctx->rsize : defsize; + + /* + * no CAP_LARGE_READ_X? Then MS-CIFS states that we must limit this to + * the client's MaxBufferSize. + */ + if (!(server->capabilities & CAP_LARGE_READ_X)) + rsize = min_t(unsigned int, CIFSMaxBufSize, rsize); + + /* hard limit of CIFS_MAX_RSIZE */ + rsize = min_t(unsigned int, rsize, CIFS_MAX_RSIZE); + + return rsize; +} + +static void +cifs_qfs_tcon(const unsigned int xid, struct cifs_tcon *tcon, + struct cifs_sb_info *cifs_sb) +{ + CIFSSMBQFSDeviceInfo(xid, tcon); + CIFSSMBQFSAttributeInfo(xid, tcon); +} + +static int +cifs_is_path_accessible(const unsigned int xid, struct cifs_tcon *tcon, + struct cifs_sb_info *cifs_sb, const char *full_path) +{ + int rc; + FILE_ALL_INFO *file_info; + + file_info = kmalloc(sizeof(FILE_ALL_INFO), GFP_KERNEL); + if (file_info == NULL) + return -ENOMEM; + + rc = CIFSSMBQPathInfo(xid, tcon, full_path, file_info, + 0 /* not legacy */, cifs_sb->local_nls, + cifs_remap(cifs_sb)); + + if (rc == -EOPNOTSUPP || rc == -EINVAL) + rc = SMBQueryInformation(xid, tcon, full_path, file_info, + cifs_sb->local_nls, cifs_remap(cifs_sb)); + kfree(file_info); + return rc; +} + +static int cifs_query_path_info(const unsigned int xid, struct cifs_tcon *tcon, + struct cifs_sb_info *cifs_sb, const char *full_path, + struct cifs_open_info_data *data, bool *adjustTZ, bool *symlink) +{ + int rc; + FILE_ALL_INFO fi = {}; + + *symlink = false; + + /* could do find first instead but this returns more info */ + rc = CIFSSMBQPathInfo(xid, tcon, full_path, &fi, 0 /* not legacy */, cifs_sb->local_nls, + cifs_remap(cifs_sb)); + /* + * 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, tcon, full_path, &fi, cifs_sb->local_nls, + cifs_remap(cifs_sb)); + *adjustTZ = true; + } + + if (!rc) { + int tmprc; + int oplock = 0; + struct cifs_fid fid; + struct cifs_open_parms oparms; + + move_cifs_info_to_smb2(&data->fi, &fi); + + if (!(le32_to_cpu(fi.Attributes) & ATTR_REPARSE)) + return 0; + + oparms = (struct cifs_open_parms) { + .tcon = tcon, + .cifs_sb = cifs_sb, + .desired_access = FILE_READ_ATTRIBUTES, + .create_options = cifs_create_options(cifs_sb, 0), + .disposition = FILE_OPEN, + .path = full_path, + .fid = &fid, + }; + + /* Need to check if this is a symbolic link or not */ + tmprc = CIFS_open(xid, &oparms, &oplock, NULL); + if (tmprc == -EOPNOTSUPP) + *symlink = true; + else if (tmprc == 0) + CIFSSMBClose(xid, tcon, fid.netfid); + } + + return rc; +} + +static int cifs_get_srv_inum(const unsigned int xid, struct cifs_tcon *tcon, + struct cifs_sb_info *cifs_sb, const char *full_path, + u64 *uniqueid, struct cifs_open_info_data *unused) +{ + /* + * We can not use the IndexNumber field by default from Windows or + * Samba (in ALL_INFO buf) but we can request it explicitly. The SNIA + * CIFS spec claims that this value is unique within the scope of a + * share, and the windows docs hint that it's actually unique + * per-machine. + * + * There may be higher info levels that work but are there Windows + * server or network appliances for which IndexNumber field is not + * guaranteed unique? + */ + return CIFSGetSrvInodeNumber(xid, tcon, full_path, uniqueid, + cifs_sb->local_nls, + cifs_remap(cifs_sb)); +} + +static int cifs_query_file_info(const unsigned int xid, struct cifs_tcon *tcon, + struct cifsFileInfo *cfile, struct cifs_open_info_data *data) +{ + int rc; + FILE_ALL_INFO fi = {}; + + if (cfile->symlink_target) { + data->symlink_target = kstrdup(cfile->symlink_target, GFP_KERNEL); + if (!data->symlink_target) + return -ENOMEM; + } + + rc = CIFSSMBQFileInfo(xid, tcon, cfile->fid.netfid, &fi); + if (!rc) + move_cifs_info_to_smb2(&data->fi, &fi); + return rc; +} + +static void +cifs_clear_stats(struct cifs_tcon *tcon) +{ + atomic_set(&tcon->stats.cifs_stats.num_writes, 0); + atomic_set(&tcon->stats.cifs_stats.num_reads, 0); + atomic_set(&tcon->stats.cifs_stats.num_flushes, 0); + atomic_set(&tcon->stats.cifs_stats.num_oplock_brks, 0); + atomic_set(&tcon->stats.cifs_stats.num_opens, 0); + atomic_set(&tcon->stats.cifs_stats.num_posixopens, 0); + atomic_set(&tcon->stats.cifs_stats.num_posixmkdirs, 0); + atomic_set(&tcon->stats.cifs_stats.num_closes, 0); + atomic_set(&tcon->stats.cifs_stats.num_deletes, 0); + atomic_set(&tcon->stats.cifs_stats.num_mkdirs, 0); + atomic_set(&tcon->stats.cifs_stats.num_rmdirs, 0); + atomic_set(&tcon->stats.cifs_stats.num_renames, 0); + atomic_set(&tcon->stats.cifs_stats.num_t2renames, 0); + atomic_set(&tcon->stats.cifs_stats.num_ffirst, 0); + atomic_set(&tcon->stats.cifs_stats.num_fnext, 0); + atomic_set(&tcon->stats.cifs_stats.num_fclose, 0); + atomic_set(&tcon->stats.cifs_stats.num_hardlinks, 0); + atomic_set(&tcon->stats.cifs_stats.num_symlinks, 0); + atomic_set(&tcon->stats.cifs_stats.num_locks, 0); + atomic_set(&tcon->stats.cifs_stats.num_acl_get, 0); + atomic_set(&tcon->stats.cifs_stats.num_acl_set, 0); +} + +static void +cifs_print_stats(struct seq_file *m, struct cifs_tcon *tcon) +{ + seq_printf(m, " Oplocks breaks: %d", + atomic_read(&tcon->stats.cifs_stats.num_oplock_brks)); + seq_printf(m, "\nReads: %d Bytes: %llu", + atomic_read(&tcon->stats.cifs_stats.num_reads), + (long long)(tcon->bytes_read)); + seq_printf(m, "\nWrites: %d Bytes: %llu", + atomic_read(&tcon->stats.cifs_stats.num_writes), + (long long)(tcon->bytes_written)); + seq_printf(m, "\nFlushes: %d", + atomic_read(&tcon->stats.cifs_stats.num_flushes)); + seq_printf(m, "\nLocks: %d HardLinks: %d Symlinks: %d", + atomic_read(&tcon->stats.cifs_stats.num_locks), + atomic_read(&tcon->stats.cifs_stats.num_hardlinks), + atomic_read(&tcon->stats.cifs_stats.num_symlinks)); + seq_printf(m, "\nOpens: %d Closes: %d Deletes: %d", + atomic_read(&tcon->stats.cifs_stats.num_opens), + atomic_read(&tcon->stats.cifs_stats.num_closes), + atomic_read(&tcon->stats.cifs_stats.num_deletes)); + seq_printf(m, "\nPosix Opens: %d Posix Mkdirs: %d", + atomic_read(&tcon->stats.cifs_stats.num_posixopens), + atomic_read(&tcon->stats.cifs_stats.num_posixmkdirs)); + seq_printf(m, "\nMkdirs: %d Rmdirs: %d", + atomic_read(&tcon->stats.cifs_stats.num_mkdirs), + atomic_read(&tcon->stats.cifs_stats.num_rmdirs)); + seq_printf(m, "\nRenames: %d T2 Renames %d", + atomic_read(&tcon->stats.cifs_stats.num_renames), + atomic_read(&tcon->stats.cifs_stats.num_t2renames)); + seq_printf(m, "\nFindFirst: %d FNext %d FClose %d", + atomic_read(&tcon->stats.cifs_stats.num_ffirst), + atomic_read(&tcon->stats.cifs_stats.num_fnext), + atomic_read(&tcon->stats.cifs_stats.num_fclose)); +} + +static void +cifs_mkdir_setinfo(struct inode *inode, const char *full_path, + struct cifs_sb_info *cifs_sb, struct cifs_tcon *tcon, + const unsigned int xid) +{ + FILE_BASIC_INFO info; + struct cifsInodeInfo *cifsInode; + u32 dosattrs; + int rc; + + memset(&info, 0, sizeof(info)); + cifsInode = CIFS_I(inode); + dosattrs = cifsInode->cifsAttrs|ATTR_READONLY; + info.Attributes = cpu_to_le32(dosattrs); + rc = CIFSSMBSetPathInfo(xid, tcon, full_path, &info, cifs_sb->local_nls, + cifs_sb); + if (rc == 0) + cifsInode->cifsAttrs = dosattrs; +} + +static int cifs_open_file(const unsigned int xid, struct cifs_open_parms *oparms, __u32 *oplock, + void *buf) +{ + struct cifs_open_info_data *data = buf; + FILE_ALL_INFO fi = {}; + int rc; + + if (!(oparms->tcon->ses->capabilities & CAP_NT_SMBS)) + rc = SMBLegacyOpen(xid, oparms->tcon, oparms->path, + oparms->disposition, + oparms->desired_access, + oparms->create_options, + &oparms->fid->netfid, oplock, &fi, + oparms->cifs_sb->local_nls, + cifs_remap(oparms->cifs_sb)); + else + rc = CIFS_open(xid, oparms, oplock, &fi); + + if (!rc && data) + move_cifs_info_to_smb2(&data->fi, &fi); + + return rc; +} + +static void +cifs_set_fid(struct cifsFileInfo *cfile, struct cifs_fid *fid, __u32 oplock) +{ + struct cifsInodeInfo *cinode = CIFS_I(d_inode(cfile->dentry)); + cfile->fid.netfid = fid->netfid; + cifs_set_oplock_level(cinode, oplock); + cinode->can_cache_brlcks = CIFS_CACHE_WRITE(cinode); +} + +static void +cifs_close_file(const unsigned int xid, struct cifs_tcon *tcon, + struct cifs_fid *fid) +{ + CIFSSMBClose(xid, tcon, fid->netfid); +} + +static int +cifs_flush_file(const unsigned int xid, struct cifs_tcon *tcon, + struct cifs_fid *fid) +{ + return CIFSSMBFlush(xid, tcon, fid->netfid); +} + +static int +cifs_sync_read(const unsigned int xid, struct cifs_fid *pfid, + struct cifs_io_parms *parms, unsigned int *bytes_read, + char **buf, int *buf_type) +{ + parms->netfid = pfid->netfid; + return CIFSSMBRead(xid, parms, bytes_read, buf, buf_type); +} + +static int +cifs_sync_write(const unsigned int xid, struct cifs_fid *pfid, + struct cifs_io_parms *parms, unsigned int *written, + struct kvec *iov, unsigned long nr_segs) +{ + + parms->netfid = pfid->netfid; + return CIFSSMBWrite2(xid, parms, written, iov, nr_segs); +} + +static int +smb_set_file_info(struct inode *inode, const char *full_path, + FILE_BASIC_INFO *buf, const unsigned int xid) +{ + int oplock = 0; + int rc; + __u32 netpid; + struct cifs_fid fid; + struct cifs_open_parms oparms; + struct cifsFileInfo *open_file; + struct cifsInodeInfo *cinode = CIFS_I(inode); + struct cifs_sb_info *cifs_sb = CIFS_SB(inode->i_sb); + struct tcon_link *tlink = NULL; + struct cifs_tcon *tcon; + + /* if the file is already open for write, just use that fileid */ + open_file = find_writable_file(cinode, FIND_WR_FSUID_ONLY); + if (open_file) { + fid.netfid = open_file->fid.netfid; + netpid = open_file->pid; + tcon = tlink_tcon(open_file->tlink); + goto set_via_filehandle; + } + + tlink = cifs_sb_tlink(cifs_sb); + if (IS_ERR(tlink)) { + rc = PTR_ERR(tlink); + tlink = NULL; + goto out; + } + tcon = tlink_tcon(tlink); + + rc = CIFSSMBSetPathInfo(xid, tcon, full_path, buf, cifs_sb->local_nls, + cifs_sb); + if (rc == 0) { + cinode->cifsAttrs = le32_to_cpu(buf->Attributes); + goto out; + } else if (rc != -EOPNOTSUPP && rc != -EINVAL) { + goto out; + } + + oparms = (struct cifs_open_parms) { + .tcon = tcon, + .cifs_sb = cifs_sb, + .desired_access = SYNCHRONIZE | FILE_WRITE_ATTRIBUTES, + .create_options = cifs_create_options(cifs_sb, CREATE_NOT_DIR), + .disposition = FILE_OPEN, + .path = full_path, + .fid = &fid, + }; + + cifs_dbg(FYI, "calling SetFileInfo since SetPathInfo for times not supported by this server\n"); + rc = CIFS_open(xid, &oparms, &oplock, NULL); + if (rc != 0) { + if (rc == -EIO) + rc = -EINVAL; + goto out; + } + + netpid = current->tgid; + +set_via_filehandle: + rc = CIFSSMBSetFileInfo(xid, tcon, buf, fid.netfid, netpid); + if (!rc) + cinode->cifsAttrs = le32_to_cpu(buf->Attributes); + + if (open_file == NULL) + CIFSSMBClose(xid, tcon, fid.netfid); + else + cifsFileInfo_put(open_file); +out: + if (tlink != NULL) + cifs_put_tlink(tlink); + return rc; +} + +static int +cifs_set_compression(const unsigned int xid, struct cifs_tcon *tcon, + struct cifsFileInfo *cfile) +{ + return CIFSSMB_set_compression(xid, tcon, cfile->fid.netfid); +} + +static int +cifs_query_dir_first(const unsigned int xid, struct cifs_tcon *tcon, + const char *path, struct cifs_sb_info *cifs_sb, + struct cifs_fid *fid, __u16 search_flags, + struct cifs_search_info *srch_inf) +{ + int rc; + + rc = CIFSFindFirst(xid, tcon, path, cifs_sb, + &fid->netfid, search_flags, srch_inf, true); + if (rc) + cifs_dbg(FYI, "find first failed=%d\n", rc); + return rc; +} + +static int +cifs_query_dir_next(const unsigned int xid, struct cifs_tcon *tcon, + struct cifs_fid *fid, __u16 search_flags, + struct cifs_search_info *srch_inf) +{ + return CIFSFindNext(xid, tcon, fid->netfid, search_flags, srch_inf); +} + +static int +cifs_close_dir(const unsigned int xid, struct cifs_tcon *tcon, + struct cifs_fid *fid) +{ + return CIFSFindClose(xid, tcon, fid->netfid); +} + +static int +cifs_oplock_response(struct cifs_tcon *tcon, __u64 persistent_fid, + __u64 volatile_fid, __u16 net_fid, struct cifsInodeInfo *cinode) +{ + return CIFSSMBLock(0, tcon, net_fid, current->tgid, 0, 0, 0, 0, + LOCKING_ANDX_OPLOCK_RELEASE, false, CIFS_CACHE_READ(cinode) ? 1 : 0); +} + +static int +cifs_queryfs(const unsigned int xid, struct cifs_tcon *tcon, + struct cifs_sb_info *cifs_sb, struct kstatfs *buf) +{ + int rc = -EOPNOTSUPP; + + buf->f_type = CIFS_SUPER_MAGIC; + + /* + * We could add a second check for a QFS Unix capability bit + */ + if ((tcon->ses->capabilities & CAP_UNIX) && + (CIFS_POSIX_EXTENSIONS & le64_to_cpu(tcon->fsUnixInfo.Capability))) + rc = CIFSSMBQFSPosixInfo(xid, tcon, buf); + + /* + * Only need to call the old QFSInfo if failed on newer one, + * e.g. by OS/2. + **/ + if (rc && (tcon->ses->capabilities & CAP_NT_SMBS)) + rc = CIFSSMBQFSInfo(xid, tcon, buf); + + /* + * Some old Windows servers also do not support level 103, retry with + * older level one if old server failed the previous call or we + * bypassed it because we detected that this was an older LANMAN sess + */ + if (rc) + rc = SMBOldQFSInfo(xid, tcon, buf); + return rc; +} + +static int +cifs_mand_lock(const unsigned int xid, struct cifsFileInfo *cfile, __u64 offset, + __u64 length, __u32 type, int lock, int unlock, bool wait) +{ + return CIFSSMBLock(xid, tlink_tcon(cfile->tlink), cfile->fid.netfid, + current->tgid, length, offset, unlock, lock, + (__u8)type, wait, 0); +} + +static int +cifs_unix_dfs_readlink(const unsigned int xid, struct cifs_tcon *tcon, + const unsigned char *searchName, char **symlinkinfo, + const struct nls_table *nls_codepage) +{ +#ifdef CONFIG_CIFS_DFS_UPCALL + int rc; + struct dfs_info3_param referral = {0}; + + rc = get_dfs_path(xid, tcon->ses, searchName, nls_codepage, &referral, + 0); + + if (!rc) { + *symlinkinfo = kstrdup(referral.node_name, GFP_KERNEL); + free_dfs_info_param(&referral); + if (!*symlinkinfo) + rc = -ENOMEM; + } + return rc; +#else /* No DFS support */ + return -EREMOTE; +#endif +} + +static int +cifs_query_symlink(const unsigned int xid, struct cifs_tcon *tcon, + struct cifs_sb_info *cifs_sb, const char *full_path, + char **target_path, bool is_reparse_point) +{ + int rc; + int oplock = 0; + struct cifs_fid fid; + struct cifs_open_parms oparms; + + cifs_dbg(FYI, "%s: path: %s\n", __func__, full_path); + + if (is_reparse_point) { + cifs_dbg(VFS, "reparse points not handled for SMB1 symlinks\n"); + return -EOPNOTSUPP; + } + + /* Check for unix extensions */ + if (cap_unix(tcon->ses)) { + rc = CIFSSMBUnixQuerySymLink(xid, tcon, full_path, target_path, + cifs_sb->local_nls, + cifs_remap(cifs_sb)); + if (rc == -EREMOTE) + rc = cifs_unix_dfs_readlink(xid, tcon, full_path, + target_path, + cifs_sb->local_nls); + + goto out; + } + + oparms = (struct cifs_open_parms) { + .tcon = tcon, + .cifs_sb = cifs_sb, + .desired_access = FILE_READ_ATTRIBUTES, + .create_options = cifs_create_options(cifs_sb, + OPEN_REPARSE_POINT), + .disposition = FILE_OPEN, + .path = full_path, + .fid = &fid, + }; + + rc = CIFS_open(xid, &oparms, &oplock, NULL); + if (rc) + goto out; + + rc = CIFSSMBQuerySymLink(xid, tcon, fid.netfid, target_path, + cifs_sb->local_nls); + if (rc) + goto out_close; + + convert_delimiter(*target_path, '/'); +out_close: + CIFSSMBClose(xid, tcon, fid.netfid); +out: + if (!rc) + cifs_dbg(FYI, "%s: target path: %s\n", __func__, *target_path); + return rc; +} + +static bool +cifs_is_read_op(__u32 oplock) +{ + return oplock == OPLOCK_READ; +} + +static unsigned int +cifs_wp_retry_size(struct inode *inode) +{ + return CIFS_SB(inode->i_sb)->ctx->wsize; +} + +static bool +cifs_dir_needs_close(struct cifsFileInfo *cfile) +{ + return !cfile->srch_inf.endOfSearch && !cfile->invalidHandle; +} + +static bool +cifs_can_echo(struct TCP_Server_Info *server) +{ + if (server->tcpStatus == CifsGood) + return true; + + return false; +} + +static int +cifs_make_node(unsigned int xid, struct inode *inode, + struct dentry *dentry, struct cifs_tcon *tcon, + const char *full_path, umode_t mode, dev_t dev) +{ + struct cifs_sb_info *cifs_sb = CIFS_SB(inode->i_sb); + struct inode *newinode = NULL; + int rc = -EPERM; + struct cifs_open_info_data buf = {}; + struct cifs_io_parms io_parms; + __u32 oplock = 0; + struct cifs_fid fid; + struct cifs_open_parms oparms; + unsigned int bytes_written; + struct win_dev *pdev; + struct kvec iov[2]; + + if (tcon->unix_ext) { + /* + * SMB1 Unix Extensions: requires server support but + * works with all special files + */ + struct cifs_unix_set_info_args args = { + .mode = mode & ~current_umask(), + .ctime = NO_CHANGE_64, + .atime = NO_CHANGE_64, + .mtime = NO_CHANGE_64, + .device = dev, + }; + if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_SET_UID) { + args.uid = current_fsuid(); + args.gid = current_fsgid(); + } else { + args.uid = INVALID_UID; /* no change */ + args.gid = INVALID_GID; /* no change */ + } + rc = CIFSSMBUnixSetPathInfo(xid, tcon, full_path, &args, + cifs_sb->local_nls, + cifs_remap(cifs_sb)); + if (rc) + return rc; + + rc = cifs_get_inode_info_unix(&newinode, full_path, + inode->i_sb, xid); + + if (rc == 0) + d_instantiate(dentry, newinode); + return rc; + } + + /* + * SMB1 SFU emulation: should work with all servers, but only + * support block and char device (no socket & fifo) + */ + if (!(cifs_sb->mnt_cifs_flags & CIFS_MOUNT_UNX_EMUL)) + return rc; + + if (!S_ISCHR(mode) && !S_ISBLK(mode)) + return rc; + + cifs_dbg(FYI, "sfu compat create special file\n"); + + oparms = (struct cifs_open_parms) { + .tcon = tcon, + .cifs_sb = cifs_sb, + .desired_access = GENERIC_WRITE, + .create_options = cifs_create_options(cifs_sb, CREATE_NOT_DIR | + CREATE_OPTION_SPECIAL), + .disposition = FILE_CREATE, + .path = full_path, + .fid = &fid, + }; + + if (tcon->ses->server->oplocks) + oplock = REQ_OPLOCK; + else + oplock = 0; + rc = tcon->ses->server->ops->open(xid, &oparms, &oplock, &buf); + if (rc) + return rc; + + /* + * BB Do not bother to decode buf since no local inode yet to put + * timestamps in, but we can reuse it safely. + */ + + pdev = (struct win_dev *)&buf.fi; + io_parms.pid = current->tgid; + io_parms.tcon = tcon; + io_parms.offset = 0; + io_parms.length = sizeof(struct win_dev); + iov[1].iov_base = &buf.fi; + iov[1].iov_len = sizeof(struct win_dev); + if (S_ISCHR(mode)) { + memcpy(pdev->type, "IntxCHR", 8); + pdev->major = cpu_to_le64(MAJOR(dev)); + pdev->minor = cpu_to_le64(MINOR(dev)); + rc = tcon->ses->server->ops->sync_write(xid, &fid, &io_parms, + &bytes_written, iov, 1); + } else if (S_ISBLK(mode)) { + memcpy(pdev->type, "IntxBLK", 8); + pdev->major = cpu_to_le64(MAJOR(dev)); + pdev->minor = cpu_to_le64(MINOR(dev)); + rc = tcon->ses->server->ops->sync_write(xid, &fid, &io_parms, + &bytes_written, iov, 1); + } + tcon->ses->server->ops->close(xid, tcon, &fid); + d_drop(dentry); + + /* FIXME: add code here to set EAs */ + + cifs_free_open_info(&buf); + return rc; +} + + + +struct smb_version_operations smb1_operations = { + .send_cancel = send_nt_cancel, + .compare_fids = cifs_compare_fids, + .setup_request = cifs_setup_request, + .setup_async_request = cifs_setup_async_request, + .check_receive = cifs_check_receive, + .add_credits = cifs_add_credits, + .set_credits = cifs_set_credits, + .get_credits_field = cifs_get_credits_field, + .get_credits = cifs_get_credits, + .wait_mtu_credits = cifs_wait_mtu_credits, + .get_next_mid = cifs_get_next_mid, + .read_data_offset = cifs_read_data_offset, + .read_data_length = cifs_read_data_length, + .map_error = map_smb_to_linux_error, + .find_mid = cifs_find_mid, + .check_message = checkSMB, + .dump_detail = cifs_dump_detail, + .clear_stats = cifs_clear_stats, + .print_stats = cifs_print_stats, + .is_oplock_break = is_valid_oplock_break, + .downgrade_oplock = cifs_downgrade_oplock, + .check_trans2 = cifs_check_trans2, + .need_neg = cifs_need_neg, + .negotiate = cifs_negotiate, + .negotiate_wsize = cifs_negotiate_wsize, + .negotiate_rsize = cifs_negotiate_rsize, + .sess_setup = CIFS_SessSetup, + .logoff = CIFSSMBLogoff, + .tree_connect = CIFSTCon, + .tree_disconnect = CIFSSMBTDis, + .get_dfs_refer = CIFSGetDFSRefer, + .qfs_tcon = cifs_qfs_tcon, + .is_path_accessible = cifs_is_path_accessible, + .can_echo = cifs_can_echo, + .query_path_info = cifs_query_path_info, + .query_file_info = cifs_query_file_info, + .get_srv_inum = cifs_get_srv_inum, + .set_path_size = CIFSSMBSetEOF, + .set_file_size = CIFSSMBSetFileSize, + .set_file_info = smb_set_file_info, + .set_compression = cifs_set_compression, + .echo = CIFSSMBEcho, + .mkdir = CIFSSMBMkDir, + .mkdir_setinfo = cifs_mkdir_setinfo, + .rmdir = CIFSSMBRmDir, + .unlink = CIFSSMBDelFile, + .rename_pending_delete = cifs_rename_pending_delete, + .rename = CIFSSMBRename, + .create_hardlink = CIFSCreateHardLink, + .query_symlink = cifs_query_symlink, + .open = cifs_open_file, + .set_fid = cifs_set_fid, + .close = cifs_close_file, + .flush = cifs_flush_file, + .async_readv = cifs_async_readv, + .async_writev = cifs_async_writev, + .sync_read = cifs_sync_read, + .sync_write = cifs_sync_write, + .query_dir_first = cifs_query_dir_first, + .query_dir_next = cifs_query_dir_next, + .close_dir = cifs_close_dir, + .calc_smb_size = smbCalcSize, + .oplock_response = cifs_oplock_response, + .queryfs = cifs_queryfs, + .mand_lock = cifs_mand_lock, + .mand_unlock_range = cifs_unlock_range, + .push_mand_locks = cifs_push_mandatory_locks, + .query_mf_symlink = cifs_query_mf_symlink, + .create_mf_symlink = cifs_create_mf_symlink, + .is_read_op = cifs_is_read_op, + .wp_retry_size = cifs_wp_retry_size, + .dir_needs_close = cifs_dir_needs_close, + .select_sectype = cifs_select_sectype, +#ifdef CONFIG_CIFS_XATTR + .query_all_EAs = CIFSSMBQAllEAs, + .set_EA = CIFSSMBSetEA, +#endif /* CIFS_XATTR */ + .get_acl = get_cifs_acl, + .get_acl_by_fid = get_cifs_acl_by_fid, + .set_acl = set_cifs_acl, + .make_node = cifs_make_node, +}; + +struct smb_version_values smb1_values = { + .version_string = SMB1_VERSION_STRING, + .protocol_id = SMB10_PROT_ID, + .large_lock_type = LOCKING_ANDX_LARGE_FILES, + .exclusive_lock_type = 0, + .shared_lock_type = LOCKING_ANDX_SHARED_LOCK, + .unlock_lock_type = 0, + .header_preamble_size = 4, + .header_size = sizeof(struct smb_hdr), + .max_header_size = MAX_CIFS_HDR_SIZE, + .read_rsp_size = sizeof(READ_RSP), + .lock_cmd = cpu_to_le16(SMB_COM_LOCKING_ANDX), + .cap_unix = CAP_UNIX, + .cap_nt_find = CAP_NT_SMBS | CAP_NT_FIND, + .cap_large_files = CAP_LARGE_FILES, + .signing_enabled = SECMODE_SIGN_ENABLED, + .signing_required = SECMODE_SIGN_REQUIRED, +}; diff --git a/fs/smb/client/smb2file.c b/fs/smb/client/smb2file.c new file mode 100644 index 0000000000000..ba6cc50af390f --- /dev/null +++ b/fs/smb/client/smb2file.c @@ -0,0 +1,371 @@ +// SPDX-License-Identifier: LGPL-2.1 +/* + * + * Copyright (C) International Business Machines Corp., 2002, 2011 + * Author(s): Steve French (sfrench@us.ibm.com), + * Pavel Shilovsky ((pshilovsky@samba.org) 2012 + * + */ +#include +#include +#include +#include +#include +#include "cifsfs.h" +#include "cifspdu.h" +#include "cifsglob.h" +#include "cifsproto.h" +#include "cifs_debug.h" +#include "cifs_fs_sb.h" +#include "cifs_unicode.h" +#include "fscache.h" +#include "smb2proto.h" +#include "smb2status.h" + +static struct smb2_symlink_err_rsp *symlink_data(const struct kvec *iov) +{ + struct smb2_err_rsp *err = iov->iov_base; + struct smb2_symlink_err_rsp *sym = ERR_PTR(-EINVAL); + u32 len; + + if (err->ErrorContextCount) { + struct smb2_error_context_rsp *p, *end; + + len = (u32)err->ErrorContextCount * (offsetof(struct smb2_error_context_rsp, + ErrorContextData) + + sizeof(struct smb2_symlink_err_rsp)); + if (le32_to_cpu(err->ByteCount) < len || iov->iov_len < len + sizeof(*err)) + return ERR_PTR(-EINVAL); + + p = (struct smb2_error_context_rsp *)err->ErrorData; + end = (struct smb2_error_context_rsp *)((u8 *)err + iov->iov_len); + do { + if (le32_to_cpu(p->ErrorId) == SMB2_ERROR_ID_DEFAULT) { + sym = (struct smb2_symlink_err_rsp *)&p->ErrorContextData; + break; + } + cifs_dbg(FYI, "%s: skipping unhandled error context: 0x%x\n", + __func__, le32_to_cpu(p->ErrorId)); + + len = ALIGN(le32_to_cpu(p->ErrorDataLength), 8); + p = (struct smb2_error_context_rsp *)((u8 *)&p->ErrorContextData + len); + } while (p < end); + } else if (le32_to_cpu(err->ByteCount) >= sizeof(*sym) && + iov->iov_len >= SMB2_SYMLINK_STRUCT_SIZE) { + sym = (struct smb2_symlink_err_rsp *)err->ErrorData; + } + + if (!IS_ERR(sym) && (le32_to_cpu(sym->SymLinkErrorTag) != SYMLINK_ERROR_TAG || + le32_to_cpu(sym->ReparseTag) != IO_REPARSE_TAG_SYMLINK)) + sym = ERR_PTR(-EINVAL); + + return sym; +} + +int smb2_parse_symlink_response(struct cifs_sb_info *cifs_sb, const struct kvec *iov, char **path) +{ + struct smb2_symlink_err_rsp *sym; + unsigned int sub_offs, sub_len; + unsigned int print_offs, print_len; + char *s; + + if (!cifs_sb || !iov || !iov->iov_base || !iov->iov_len || !path) + return -EINVAL; + + sym = symlink_data(iov); + if (IS_ERR(sym)) + return PTR_ERR(sym); + + sub_len = le16_to_cpu(sym->SubstituteNameLength); + sub_offs = le16_to_cpu(sym->SubstituteNameOffset); + print_len = le16_to_cpu(sym->PrintNameLength); + print_offs = le16_to_cpu(sym->PrintNameOffset); + + if (iov->iov_len < SMB2_SYMLINK_STRUCT_SIZE + sub_offs + sub_len || + iov->iov_len < SMB2_SYMLINK_STRUCT_SIZE + print_offs + print_len) + return -EINVAL; + + s = cifs_strndup_from_utf16((char *)sym->PathBuffer + sub_offs, sub_len, true, + cifs_sb->local_nls); + if (!s) + return -ENOMEM; + convert_delimiter(s, '/'); + cifs_dbg(FYI, "%s: symlink target: %s\n", __func__, s); + + *path = s; + return 0; +} + +int smb2_open_file(const unsigned int xid, struct cifs_open_parms *oparms, __u32 *oplock, void *buf) +{ + int rc; + __le16 *smb2_path; + __u8 smb2_oplock; + struct cifs_open_info_data *data = buf; + struct smb2_file_all_info file_info = {}; + struct smb2_file_all_info *smb2_data = data ? &file_info : NULL; + struct kvec err_iov = {}; + int err_buftype = CIFS_NO_BUFFER; + struct cifs_fid *fid = oparms->fid; + struct network_resiliency_req nr_ioctl_req; + + smb2_path = cifs_convert_path_to_utf16(oparms->path, oparms->cifs_sb); + if (smb2_path == NULL) + return -ENOMEM; + + oparms->desired_access |= FILE_READ_ATTRIBUTES; + smb2_oplock = SMB2_OPLOCK_LEVEL_BATCH; + + rc = SMB2_open(xid, oparms, smb2_path, &smb2_oplock, smb2_data, NULL, &err_iov, + &err_buftype); + if (rc && data) { + struct smb2_hdr *hdr = err_iov.iov_base; + + if (unlikely(!err_iov.iov_base || err_buftype == CIFS_NO_BUFFER)) + goto out; + if (hdr->Status == STATUS_STOPPED_ON_SYMLINK) { + rc = smb2_parse_symlink_response(oparms->cifs_sb, &err_iov, + &data->symlink_target); + if (!rc) { + memset(smb2_data, 0, sizeof(*smb2_data)); + oparms->create_options |= OPEN_REPARSE_POINT; + rc = SMB2_open(xid, oparms, smb2_path, &smb2_oplock, smb2_data, + NULL, NULL, NULL); + oparms->create_options &= ~OPEN_REPARSE_POINT; + } + } + } + + if (rc) + goto out; + + if (oparms->tcon->use_resilient) { + /* default timeout is 0, servers pick default (120 seconds) */ + nr_ioctl_req.Timeout = + cpu_to_le32(oparms->tcon->handle_timeout); + nr_ioctl_req.Reserved = 0; + rc = SMB2_ioctl(xid, oparms->tcon, fid->persistent_fid, + fid->volatile_fid, FSCTL_LMR_REQUEST_RESILIENCY, + (char *)&nr_ioctl_req, sizeof(nr_ioctl_req), + CIFSMaxBufSize, NULL, NULL /* no return info */); + if (rc == -EOPNOTSUPP) { + cifs_dbg(VFS, + "resiliency not supported by server, disabling\n"); + oparms->tcon->use_resilient = false; + } else if (rc) + cifs_dbg(FYI, "error %d setting resiliency\n", rc); + + rc = 0; + } + + if (smb2_data) { + /* if open response does not have IndexNumber field - get it */ + if (smb2_data->IndexNumber == 0) { + rc = SMB2_get_srv_num(xid, oparms->tcon, + fid->persistent_fid, + fid->volatile_fid, + &smb2_data->IndexNumber); + if (rc) { + /* + * let get_inode_info disable server inode + * numbers + */ + smb2_data->IndexNumber = 0; + rc = 0; + } + } + memcpy(&data->fi, smb2_data, sizeof(data->fi)); + } + + *oplock = smb2_oplock; +out: + free_rsp_buf(err_buftype, err_iov.iov_base); + kfree(smb2_path); + return rc; +} + +int +smb2_unlock_range(struct cifsFileInfo *cfile, struct file_lock *flock, + const unsigned int xid) +{ + int rc = 0, stored_rc; + unsigned int max_num, num = 0, max_buf; + struct smb2_lock_element *buf, *cur; + struct cifs_tcon *tcon = tlink_tcon(cfile->tlink); + struct cifsInodeInfo *cinode = CIFS_I(d_inode(cfile->dentry)); + struct cifsLockInfo *li, *tmp; + __u64 length = 1 + flock->fl_end - flock->fl_start; + struct list_head tmp_llist; + + INIT_LIST_HEAD(&tmp_llist); + + /* + * Accessing maxBuf is racy with cifs_reconnect - need to store value + * and check it before using. + */ + max_buf = tcon->ses->server->maxBuf; + if (max_buf < sizeof(struct smb2_lock_element)) + return -EINVAL; + + BUILD_BUG_ON(sizeof(struct smb2_lock_element) > PAGE_SIZE); + max_buf = min_t(unsigned int, max_buf, PAGE_SIZE); + max_num = max_buf / sizeof(struct smb2_lock_element); + buf = kcalloc(max_num, sizeof(struct smb2_lock_element), GFP_KERNEL); + if (!buf) + return -ENOMEM; + + cur = buf; + + cifs_down_write(&cinode->lock_sem); + list_for_each_entry_safe(li, tmp, &cfile->llist->locks, llist) { + if (flock->fl_start > li->offset || + (flock->fl_start + length) < + (li->offset + li->length)) + continue; + if (current->tgid != li->pid) + /* + * flock and OFD lock are associated with an open + * file description, not the process. + */ + if (!(flock->fl_flags & (FL_FLOCK | FL_OFDLCK))) + continue; + if (cinode->can_cache_brlcks) { + /* + * We can cache brlock requests - simply remove a lock + * from the file's list. + */ + list_del(&li->llist); + cifs_del_lock_waiters(li); + kfree(li); + continue; + } + cur->Length = cpu_to_le64(li->length); + cur->Offset = cpu_to_le64(li->offset); + cur->Flags = cpu_to_le32(SMB2_LOCKFLAG_UNLOCK); + /* + * We need to save a lock here to let us add it again to the + * file's list if the unlock range request fails on the server. + */ + list_move(&li->llist, &tmp_llist); + if (++num == max_num) { + stored_rc = smb2_lockv(xid, tcon, + cfile->fid.persistent_fid, + cfile->fid.volatile_fid, + current->tgid, num, buf); + if (stored_rc) { + /* + * We failed on the unlock range request - add + * all locks from the tmp list to the head of + * the file's list. + */ + cifs_move_llist(&tmp_llist, + &cfile->llist->locks); + rc = stored_rc; + } else + /* + * The unlock range request succeed - free the + * tmp list. + */ + cifs_free_llist(&tmp_llist); + cur = buf; + num = 0; + } else + cur++; + } + if (num) { + stored_rc = smb2_lockv(xid, tcon, cfile->fid.persistent_fid, + cfile->fid.volatile_fid, current->tgid, + num, buf); + if (stored_rc) { + cifs_move_llist(&tmp_llist, &cfile->llist->locks); + rc = stored_rc; + } else + cifs_free_llist(&tmp_llist); + } + up_write(&cinode->lock_sem); + + kfree(buf); + return rc; +} + +static int +smb2_push_mand_fdlocks(struct cifs_fid_locks *fdlocks, const unsigned int xid, + struct smb2_lock_element *buf, unsigned int max_num) +{ + int rc = 0, stored_rc; + struct cifsFileInfo *cfile = fdlocks->cfile; + struct cifsLockInfo *li; + unsigned int num = 0; + struct smb2_lock_element *cur = buf; + struct cifs_tcon *tcon = tlink_tcon(cfile->tlink); + + list_for_each_entry(li, &fdlocks->locks, llist) { + cur->Length = cpu_to_le64(li->length); + cur->Offset = cpu_to_le64(li->offset); + cur->Flags = cpu_to_le32(li->type | + SMB2_LOCKFLAG_FAIL_IMMEDIATELY); + if (++num == max_num) { + stored_rc = smb2_lockv(xid, tcon, + cfile->fid.persistent_fid, + cfile->fid.volatile_fid, + current->tgid, num, buf); + if (stored_rc) + rc = stored_rc; + cur = buf; + num = 0; + } else + cur++; + } + if (num) { + stored_rc = smb2_lockv(xid, tcon, + cfile->fid.persistent_fid, + cfile->fid.volatile_fid, + current->tgid, num, buf); + if (stored_rc) + rc = stored_rc; + } + + return rc; +} + +int +smb2_push_mandatory_locks(struct cifsFileInfo *cfile) +{ + int rc = 0, stored_rc; + unsigned int xid; + unsigned int max_num, max_buf; + struct smb2_lock_element *buf; + struct cifsInodeInfo *cinode = CIFS_I(d_inode(cfile->dentry)); + struct cifs_fid_locks *fdlocks; + + xid = get_xid(); + + /* + * Accessing maxBuf is racy with cifs_reconnect - need to store value + * and check it for zero before using. + */ + max_buf = tlink_tcon(cfile->tlink)->ses->server->maxBuf; + if (max_buf < sizeof(struct smb2_lock_element)) { + free_xid(xid); + return -EINVAL; + } + + BUILD_BUG_ON(sizeof(struct smb2_lock_element) > PAGE_SIZE); + max_buf = min_t(unsigned int, max_buf, PAGE_SIZE); + max_num = max_buf / sizeof(struct smb2_lock_element); + buf = kcalloc(max_num, sizeof(struct smb2_lock_element), GFP_KERNEL); + if (!buf) { + free_xid(xid); + return -ENOMEM; + } + + list_for_each_entry(fdlocks, &cinode->llist, llist) { + stored_rc = smb2_push_mand_fdlocks(fdlocks, xid, buf, max_num); + if (stored_rc) + rc = stored_rc; + } + + kfree(buf); + free_xid(xid); + return rc; +} diff --git a/fs/smb/client/smb2glob.h b/fs/smb/client/smb2glob.h new file mode 100644 index 0000000000000..82e916ad167c0 --- /dev/null +++ b/fs/smb/client/smb2glob.h @@ -0,0 +1,44 @@ +/* SPDX-License-Identifier: LGPL-2.1 */ +/* + * + * Definitions for various global variables and structures + * + * Copyright (C) International Business Machines Corp., 2002, 2011 + * Etersoft, 2012 + * Author(s): Steve French (sfrench@us.ibm.com) + * Jeremy Allison (jra@samba.org) + * Pavel Shilovsky (pshilovsky@samba.org) 2012 + * + */ +#ifndef _SMB2_GLOB_H +#define _SMB2_GLOB_H + +/* + ***************************************************************** + * Constants go here + ***************************************************************** + */ + +/* + * Identifiers for functions that use the open, operation, close pattern + * in smb2inode.c:smb2_compound_op() + */ +#define SMB2_OP_SET_DELETE 1 +#define SMB2_OP_SET_INFO 2 +#define SMB2_OP_QUERY_INFO 3 +#define SMB2_OP_QUERY_DIR 4 +#define SMB2_OP_MKDIR 5 +#define SMB2_OP_RENAME 6 +#define SMB2_OP_DELETE 7 +#define SMB2_OP_HARDLINK 8 +#define SMB2_OP_SET_EOF 9 +#define SMB2_OP_RMDIR 10 +#define SMB2_OP_POSIX_QUERY_INFO 11 + +/* Used when constructing chained read requests. */ +#define CHAINED_REQUEST 1 +#define START_OF_CHAIN 2 +#define END_OF_CHAIN 4 +#define RELATED_REQUEST 8 + +#endif /* _SMB2_GLOB_H */ diff --git a/fs/smb/client/smb2inode.c b/fs/smb/client/smb2inode.c new file mode 100644 index 0000000000000..c97e049e29dd3 --- /dev/null +++ b/fs/smb/client/smb2inode.c @@ -0,0 +1,799 @@ +// SPDX-License-Identifier: LGPL-2.1 +/* + * + * Copyright (C) International Business Machines Corp., 2002, 2011 + * Etersoft, 2012 + * Author(s): Pavel Shilovsky (pshilovsky@samba.org), + * Steve French (sfrench@us.ibm.com) + * + */ +#include +#include +#include +#include +#include +#include "cifsfs.h" +#include "cifspdu.h" +#include "cifsglob.h" +#include "cifsproto.h" +#include "cifs_debug.h" +#include "cifs_fs_sb.h" +#include "cifs_unicode.h" +#include "fscache.h" +#include "smb2glob.h" +#include "smb2pdu.h" +#include "smb2proto.h" +#include "cached_dir.h" +#include "smb2status.h" + +static void +free_set_inf_compound(struct smb_rqst *rqst) +{ + if (rqst[1].rq_iov) + SMB2_set_info_free(&rqst[1]); + if (rqst[2].rq_iov) + SMB2_close_free(&rqst[2]); +} + + +struct cop_vars { + struct cifs_open_parms oparms; + struct kvec rsp_iov[3]; + struct smb_rqst rqst[3]; + struct kvec open_iov[SMB2_CREATE_IOV_SIZE]; + struct kvec qi_iov[1]; + struct kvec si_iov[SMB2_SET_INFO_IOV_SIZE]; + struct kvec close_iov[1]; + struct smb2_file_rename_info rename_info; + struct smb2_file_link_info link_info; +}; + +/* + * note: If cfile is passed, the reference to it is dropped here. + * So make sure that you do not reuse cfile after return from this func. + * + * If passing @err_iov and @err_buftype, ensure to make them both large enough (>= 3) to hold all + * error responses. Caller is also responsible for freeing them up. + */ +static int smb2_compound_op(const unsigned int xid, struct cifs_tcon *tcon, + struct cifs_sb_info *cifs_sb, const char *full_path, + __u32 desired_access, __u32 create_disposition, __u32 create_options, + umode_t mode, void *ptr, int command, struct cifsFileInfo *cfile, + struct kvec *err_iov, int *err_buftype) +{ + struct cop_vars *vars = NULL; + struct kvec *rsp_iov; + struct smb_rqst *rqst; + int rc; + __le16 *utf16_path = NULL; + __u8 oplock = SMB2_OPLOCK_LEVEL_NONE; + struct cifs_fid fid; + struct cifs_ses *ses = tcon->ses; + struct TCP_Server_Info *server; + int num_rqst = 0; + int resp_buftype[3]; + struct smb2_query_info_rsp *qi_rsp = NULL; + struct cifs_open_info_data *idata; + int flags = 0; + __u8 delete_pending[8] = {1, 0, 0, 0, 0, 0, 0, 0}; + unsigned int size[2]; + void *data[2]; + int len; + + vars = kzalloc(sizeof(*vars), GFP_ATOMIC); + if (vars == NULL) + return -ENOMEM; + rqst = &vars->rqst[0]; + rsp_iov = &vars->rsp_iov[0]; + + server = cifs_pick_channel(ses); + + if (smb3_encryption_required(tcon)) + flags |= CIFS_TRANSFORM_REQ; + + resp_buftype[0] = resp_buftype[1] = resp_buftype[2] = CIFS_NO_BUFFER; + + /* We already have a handle so we can skip the open */ + if (cfile) + goto after_open; + + /* Open */ + utf16_path = cifs_convert_path_to_utf16(full_path, cifs_sb); + if (!utf16_path) { + rc = -ENOMEM; + goto finished; + } + + vars->oparms = (struct cifs_open_parms) { + .tcon = tcon, + .path = full_path, + .desired_access = desired_access, + .disposition = create_disposition, + .create_options = cifs_create_options(cifs_sb, create_options), + .fid = &fid, + .mode = mode, + .cifs_sb = cifs_sb, + }; + + rqst[num_rqst].rq_iov = &vars->open_iov[0]; + rqst[num_rqst].rq_nvec = SMB2_CREATE_IOV_SIZE; + rc = SMB2_open_init(tcon, server, + &rqst[num_rqst], &oplock, &vars->oparms, + utf16_path); + kfree(utf16_path); + if (rc) + goto finished; + + smb2_set_next_command(tcon, &rqst[num_rqst]); + after_open: + num_rqst++; + rc = 0; + + /* Operation */ + switch (command) { + case SMB2_OP_QUERY_INFO: + rqst[num_rqst].rq_iov = &vars->qi_iov[0]; + rqst[num_rqst].rq_nvec = 1; + + if (cfile) + rc = SMB2_query_info_init(tcon, server, + &rqst[num_rqst], + cfile->fid.persistent_fid, + cfile->fid.volatile_fid, + FILE_ALL_INFORMATION, + SMB2_O_INFO_FILE, 0, + sizeof(struct smb2_file_all_info) + + PATH_MAX * 2, 0, NULL); + else { + rc = SMB2_query_info_init(tcon, server, + &rqst[num_rqst], + COMPOUND_FID, + COMPOUND_FID, + FILE_ALL_INFORMATION, + SMB2_O_INFO_FILE, 0, + sizeof(struct smb2_file_all_info) + + PATH_MAX * 2, 0, NULL); + if (!rc) { + smb2_set_next_command(tcon, &rqst[num_rqst]); + smb2_set_related(&rqst[num_rqst]); + } + } + + if (rc) + goto finished; + num_rqst++; + trace_smb3_query_info_compound_enter(xid, ses->Suid, tcon->tid, + full_path); + break; + case SMB2_OP_POSIX_QUERY_INFO: + rqst[num_rqst].rq_iov = &vars->qi_iov[0]; + rqst[num_rqst].rq_nvec = 1; + + if (cfile) + rc = SMB2_query_info_init(tcon, server, + &rqst[num_rqst], + cfile->fid.persistent_fid, + cfile->fid.volatile_fid, + SMB_FIND_FILE_POSIX_INFO, + SMB2_O_INFO_FILE, 0, + /* TBD: fix following to allow for longer SIDs */ + sizeof(struct smb311_posix_qinfo *) + (PATH_MAX * 2) + + (sizeof(struct cifs_sid) * 2), 0, NULL); + else { + rc = SMB2_query_info_init(tcon, server, + &rqst[num_rqst], + COMPOUND_FID, + COMPOUND_FID, + SMB_FIND_FILE_POSIX_INFO, + SMB2_O_INFO_FILE, 0, + sizeof(struct smb311_posix_qinfo *) + (PATH_MAX * 2) + + (sizeof(struct cifs_sid) * 2), 0, NULL); + if (!rc) { + smb2_set_next_command(tcon, &rqst[num_rqst]); + smb2_set_related(&rqst[num_rqst]); + } + } + + if (rc) + goto finished; + num_rqst++; + trace_smb3_posix_query_info_compound_enter(xid, ses->Suid, tcon->tid, full_path); + break; + case SMB2_OP_DELETE: + trace_smb3_delete_enter(xid, ses->Suid, tcon->tid, full_path); + break; + case SMB2_OP_MKDIR: + /* + * Directories are created through parameters in the + * SMB2_open() call. + */ + trace_smb3_mkdir_enter(xid, ses->Suid, tcon->tid, full_path); + break; + case SMB2_OP_RMDIR: + rqst[num_rqst].rq_iov = &vars->si_iov[0]; + rqst[num_rqst].rq_nvec = 1; + + size[0] = 1; /* sizeof __u8 See MS-FSCC section 2.4.11 */ + data[0] = &delete_pending[0]; + + rc = SMB2_set_info_init(tcon, server, + &rqst[num_rqst], COMPOUND_FID, + COMPOUND_FID, current->tgid, + FILE_DISPOSITION_INFORMATION, + SMB2_O_INFO_FILE, 0, data, size); + if (rc) + goto finished; + smb2_set_next_command(tcon, &rqst[num_rqst]); + smb2_set_related(&rqst[num_rqst++]); + trace_smb3_rmdir_enter(xid, ses->Suid, tcon->tid, full_path); + break; + case SMB2_OP_SET_EOF: + rqst[num_rqst].rq_iov = &vars->si_iov[0]; + rqst[num_rqst].rq_nvec = 1; + + size[0] = 8; /* sizeof __le64 */ + data[0] = ptr; + + if (cfile) { + rc = SMB2_set_info_init(tcon, server, + &rqst[num_rqst], + cfile->fid.persistent_fid, + cfile->fid.volatile_fid, + current->tgid, + FILE_END_OF_FILE_INFORMATION, + SMB2_O_INFO_FILE, 0, + data, size); + } else { + rc = SMB2_set_info_init(tcon, server, + &rqst[num_rqst], + COMPOUND_FID, + COMPOUND_FID, + current->tgid, + FILE_END_OF_FILE_INFORMATION, + SMB2_O_INFO_FILE, 0, + data, size); + if (!rc) { + smb2_set_next_command(tcon, &rqst[num_rqst]); + smb2_set_related(&rqst[num_rqst]); + } + } + if (rc) + goto finished; + num_rqst++; + trace_smb3_set_eof_enter(xid, ses->Suid, tcon->tid, full_path); + break; + case SMB2_OP_SET_INFO: + rqst[num_rqst].rq_iov = &vars->si_iov[0]; + rqst[num_rqst].rq_nvec = 1; + + + size[0] = sizeof(FILE_BASIC_INFO); + data[0] = ptr; + + if (cfile) + rc = SMB2_set_info_init(tcon, server, + &rqst[num_rqst], + cfile->fid.persistent_fid, + cfile->fid.volatile_fid, current->tgid, + FILE_BASIC_INFORMATION, + SMB2_O_INFO_FILE, 0, data, size); + else { + rc = SMB2_set_info_init(tcon, server, + &rqst[num_rqst], + COMPOUND_FID, + COMPOUND_FID, current->tgid, + FILE_BASIC_INFORMATION, + SMB2_O_INFO_FILE, 0, data, size); + if (!rc) { + smb2_set_next_command(tcon, &rqst[num_rqst]); + smb2_set_related(&rqst[num_rqst]); + } + } + + if (rc) + goto finished; + num_rqst++; + trace_smb3_set_info_compound_enter(xid, ses->Suid, tcon->tid, + full_path); + break; + case SMB2_OP_RENAME: + rqst[num_rqst].rq_iov = &vars->si_iov[0]; + rqst[num_rqst].rq_nvec = 2; + + len = (2 * UniStrnlen((wchar_t *)ptr, PATH_MAX)); + + vars->rename_info.ReplaceIfExists = 1; + vars->rename_info.RootDirectory = 0; + vars->rename_info.FileNameLength = cpu_to_le32(len); + + size[0] = sizeof(struct smb2_file_rename_info); + data[0] = &vars->rename_info; + + size[1] = len + 2 /* null */; + data[1] = (__le16 *)ptr; + + if (cfile) + rc = SMB2_set_info_init(tcon, server, + &rqst[num_rqst], + cfile->fid.persistent_fid, + cfile->fid.volatile_fid, + current->tgid, FILE_RENAME_INFORMATION, + SMB2_O_INFO_FILE, 0, data, size); + else { + rc = SMB2_set_info_init(tcon, server, + &rqst[num_rqst], + COMPOUND_FID, COMPOUND_FID, + current->tgid, FILE_RENAME_INFORMATION, + SMB2_O_INFO_FILE, 0, data, size); + if (!rc) { + smb2_set_next_command(tcon, &rqst[num_rqst]); + smb2_set_related(&rqst[num_rqst]); + } + } + if (rc) + goto finished; + num_rqst++; + trace_smb3_rename_enter(xid, ses->Suid, tcon->tid, full_path); + break; + case SMB2_OP_HARDLINK: + rqst[num_rqst].rq_iov = &vars->si_iov[0]; + rqst[num_rqst].rq_nvec = 2; + + len = (2 * UniStrnlen((wchar_t *)ptr, PATH_MAX)); + + vars->link_info.ReplaceIfExists = 0; + vars->link_info.RootDirectory = 0; + vars->link_info.FileNameLength = cpu_to_le32(len); + + size[0] = sizeof(struct smb2_file_link_info); + data[0] = &vars->link_info; + + size[1] = len + 2 /* null */; + data[1] = (__le16 *)ptr; + + rc = SMB2_set_info_init(tcon, server, + &rqst[num_rqst], COMPOUND_FID, + COMPOUND_FID, current->tgid, + FILE_LINK_INFORMATION, + SMB2_O_INFO_FILE, 0, data, size); + if (rc) + goto finished; + smb2_set_next_command(tcon, &rqst[num_rqst]); + smb2_set_related(&rqst[num_rqst++]); + trace_smb3_hardlink_enter(xid, ses->Suid, tcon->tid, full_path); + break; + default: + cifs_dbg(VFS, "Invalid command\n"); + rc = -EINVAL; + } + if (rc) + goto finished; + + /* We already have a handle so we can skip the close */ + if (cfile) + goto after_close; + /* Close */ + flags |= CIFS_CP_CREATE_CLOSE_OP; + rqst[num_rqst].rq_iov = &vars->close_iov[0]; + rqst[num_rqst].rq_nvec = 1; + rc = SMB2_close_init(tcon, server, + &rqst[num_rqst], COMPOUND_FID, + COMPOUND_FID, false); + smb2_set_related(&rqst[num_rqst]); + if (rc) + goto finished; + after_close: + num_rqst++; + + if (cfile) { + rc = compound_send_recv(xid, ses, server, + flags, num_rqst - 2, + &rqst[1], &resp_buftype[1], + &rsp_iov[1]); + } else + rc = compound_send_recv(xid, ses, server, + flags, num_rqst, + rqst, resp_buftype, + rsp_iov); + + finished: + if (cfile) + cifsFileInfo_put(cfile); + + SMB2_open_free(&rqst[0]); + if (rc == -EREMCHG) { + pr_warn_once("server share %s deleted\n", tcon->tree_name); + tcon->need_reconnect = true; + } + + switch (command) { + case SMB2_OP_QUERY_INFO: + idata = ptr; + if (rc == 0 && cfile && cfile->symlink_target) { + idata->symlink_target = kstrdup(cfile->symlink_target, GFP_KERNEL); + if (!idata->symlink_target) + rc = -ENOMEM; + } + if (rc == 0) { + qi_rsp = (struct smb2_query_info_rsp *) + rsp_iov[1].iov_base; + rc = smb2_validate_and_copy_iov( + le16_to_cpu(qi_rsp->OutputBufferOffset), + le32_to_cpu(qi_rsp->OutputBufferLength), + &rsp_iov[1], sizeof(idata->fi), (char *)&idata->fi); + } + if (rqst[1].rq_iov) + SMB2_query_info_free(&rqst[1]); + if (rqst[2].rq_iov) + SMB2_close_free(&rqst[2]); + if (rc) + trace_smb3_query_info_compound_err(xid, ses->Suid, + tcon->tid, rc); + else + trace_smb3_query_info_compound_done(xid, ses->Suid, + tcon->tid); + break; + case SMB2_OP_POSIX_QUERY_INFO: + idata = ptr; + if (rc == 0 && cfile && cfile->symlink_target) { + idata->symlink_target = kstrdup(cfile->symlink_target, GFP_KERNEL); + if (!idata->symlink_target) + rc = -ENOMEM; + } + if (rc == 0) { + qi_rsp = (struct smb2_query_info_rsp *) + rsp_iov[1].iov_base; + rc = smb2_validate_and_copy_iov( + le16_to_cpu(qi_rsp->OutputBufferOffset), + le32_to_cpu(qi_rsp->OutputBufferLength), + &rsp_iov[1], sizeof(idata->posix_fi) /* add SIDs */, + (char *)&idata->posix_fi); + } + if (rqst[1].rq_iov) + SMB2_query_info_free(&rqst[1]); + if (rqst[2].rq_iov) + SMB2_close_free(&rqst[2]); + if (rc) + trace_smb3_posix_query_info_compound_err(xid, ses->Suid, tcon->tid, rc); + else + trace_smb3_posix_query_info_compound_done(xid, ses->Suid, tcon->tid); + break; + case SMB2_OP_DELETE: + if (rc) + trace_smb3_delete_err(xid, ses->Suid, tcon->tid, rc); + else + trace_smb3_delete_done(xid, ses->Suid, tcon->tid); + if (rqst[1].rq_iov) + SMB2_close_free(&rqst[1]); + break; + case SMB2_OP_MKDIR: + if (rc) + trace_smb3_mkdir_err(xid, ses->Suid, tcon->tid, rc); + else + trace_smb3_mkdir_done(xid, ses->Suid, tcon->tid); + if (rqst[1].rq_iov) + SMB2_close_free(&rqst[1]); + break; + case SMB2_OP_HARDLINK: + if (rc) + trace_smb3_hardlink_err(xid, ses->Suid, tcon->tid, rc); + else + trace_smb3_hardlink_done(xid, ses->Suid, tcon->tid); + free_set_inf_compound(rqst); + break; + case SMB2_OP_RENAME: + if (rc) + trace_smb3_rename_err(xid, ses->Suid, tcon->tid, rc); + else + trace_smb3_rename_done(xid, ses->Suid, tcon->tid); + free_set_inf_compound(rqst); + break; + case SMB2_OP_RMDIR: + if (rc) + trace_smb3_rmdir_err(xid, ses->Suid, tcon->tid, rc); + else + trace_smb3_rmdir_done(xid, ses->Suid, tcon->tid); + free_set_inf_compound(rqst); + break; + case SMB2_OP_SET_EOF: + if (rc) + trace_smb3_set_eof_err(xid, ses->Suid, tcon->tid, rc); + else + trace_smb3_set_eof_done(xid, ses->Suid, tcon->tid); + free_set_inf_compound(rqst); + break; + case SMB2_OP_SET_INFO: + if (rc) + trace_smb3_set_info_compound_err(xid, ses->Suid, + tcon->tid, rc); + else + trace_smb3_set_info_compound_done(xid, ses->Suid, + tcon->tid); + free_set_inf_compound(rqst); + break; + } + + if (rc && err_iov && err_buftype) { + memcpy(err_iov, rsp_iov, 3 * sizeof(*err_iov)); + memcpy(err_buftype, resp_buftype, 3 * sizeof(*err_buftype)); + } else { + free_rsp_buf(resp_buftype[0], rsp_iov[0].iov_base); + free_rsp_buf(resp_buftype[1], rsp_iov[1].iov_base); + free_rsp_buf(resp_buftype[2], rsp_iov[2].iov_base); + } + kfree(vars); + return rc; +} + +int smb2_query_path_info(const unsigned int xid, struct cifs_tcon *tcon, + struct cifs_sb_info *cifs_sb, const char *full_path, + struct cifs_open_info_data *data, bool *adjust_tz, bool *reparse) +{ + __u32 create_options = 0; + struct cifsFileInfo *cfile; + struct cached_fid *cfid = NULL; + struct kvec err_iov[3] = {}; + int err_buftype[3] = {}; + bool islink; + int rc, rc2; + + *adjust_tz = false; + *reparse = false; + + if (strcmp(full_path, "")) + rc = -ENOENT; + else + rc = open_cached_dir(xid, tcon, full_path, cifs_sb, false, &cfid); + /* If it is a root and its handle is cached then use it */ + if (!rc) { + if (cfid->file_all_info_is_valid) { + memcpy(&data->fi, &cfid->file_all_info, sizeof(data->fi)); + } else { + rc = SMB2_query_info(xid, tcon, cfid->fid.persistent_fid, + cfid->fid.volatile_fid, &data->fi); + } + close_cached_dir(cfid); + return rc; + } + + cifs_get_readable_path(tcon, full_path, &cfile); + rc = smb2_compound_op(xid, tcon, cifs_sb, full_path, FILE_READ_ATTRIBUTES, FILE_OPEN, + create_options, ACL_NO_MODE, data, SMB2_OP_QUERY_INFO, cfile, + err_iov, err_buftype); + if (rc) { + struct smb2_hdr *hdr = err_iov[0].iov_base; + + if (unlikely(!hdr || err_buftype[0] == CIFS_NO_BUFFER)) + goto out; + if (rc == -EOPNOTSUPP && hdr->Command == SMB2_CREATE && + hdr->Status == STATUS_STOPPED_ON_SYMLINK) { + rc = smb2_parse_symlink_response(cifs_sb, err_iov, + &data->symlink_target); + if (rc) + goto out; + + *reparse = true; + create_options |= OPEN_REPARSE_POINT; + + /* Failed on a symbolic link - query a reparse point info */ + cifs_get_readable_path(tcon, full_path, &cfile); + rc = smb2_compound_op(xid, tcon, cifs_sb, full_path, + FILE_READ_ATTRIBUTES, FILE_OPEN, + create_options, ACL_NO_MODE, data, + SMB2_OP_QUERY_INFO, cfile, NULL, NULL); + goto out; + } else if (rc != -EREMOTE && hdr->Status == STATUS_OBJECT_NAME_INVALID) { + rc2 = cifs_inval_name_dfs_link_error(xid, tcon, cifs_sb, + full_path, &islink); + if (rc2) { + rc = rc2; + goto out; + } + if (islink) + rc = -EREMOTE; + } + if (rc == -EREMOTE && IS_ENABLED(CONFIG_CIFS_DFS_UPCALL) && cifs_sb && + (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_NO_DFS)) + rc = -EOPNOTSUPP; + } + +out: + free_rsp_buf(err_buftype[0], err_iov[0].iov_base); + free_rsp_buf(err_buftype[1], err_iov[1].iov_base); + free_rsp_buf(err_buftype[2], err_iov[2].iov_base); + return rc; +} + + +int smb311_posix_query_path_info(const unsigned int xid, struct cifs_tcon *tcon, + struct cifs_sb_info *cifs_sb, const char *full_path, + struct cifs_open_info_data *data, bool *adjust_tz, bool *reparse) +{ + int rc; + __u32 create_options = 0; + struct cifsFileInfo *cfile; + struct kvec err_iov[3] = {}; + int err_buftype[3] = {}; + + *adjust_tz = false; + *reparse = false; + + /* + * BB TODO: Add support for using the cached root handle. + * Create SMB2_query_posix_info worker function to do non-compounded query + * when we already have an open file handle for this. For now this is fast enough + * (always using the compounded version). + */ + + cifs_get_readable_path(tcon, full_path, &cfile); + rc = smb2_compound_op(xid, tcon, cifs_sb, full_path, FILE_READ_ATTRIBUTES, FILE_OPEN, + create_options, ACL_NO_MODE, data, SMB2_OP_POSIX_QUERY_INFO, cfile, + err_iov, err_buftype); + if (rc == -EOPNOTSUPP) { + /* BB TODO: When support for special files added to Samba re-verify this path */ + if (err_iov[0].iov_base && err_buftype[0] != CIFS_NO_BUFFER && + ((struct smb2_hdr *)err_iov[0].iov_base)->Command == SMB2_CREATE && + ((struct smb2_hdr *)err_iov[0].iov_base)->Status == STATUS_STOPPED_ON_SYMLINK) { + rc = smb2_parse_symlink_response(cifs_sb, err_iov, &data->symlink_target); + if (rc) + goto out; + } + *reparse = true; + create_options |= OPEN_REPARSE_POINT; + + /* Failed on a symbolic link - query a reparse point info */ + cifs_get_readable_path(tcon, full_path, &cfile); + rc = smb2_compound_op(xid, tcon, cifs_sb, full_path, FILE_READ_ATTRIBUTES, + FILE_OPEN, create_options, ACL_NO_MODE, data, + SMB2_OP_POSIX_QUERY_INFO, cfile, NULL, NULL); + } + +out: + free_rsp_buf(err_buftype[0], err_iov[0].iov_base); + free_rsp_buf(err_buftype[1], err_iov[1].iov_base); + free_rsp_buf(err_buftype[2], err_iov[2].iov_base); + return rc; +} + +int +smb2_mkdir(const unsigned int xid, struct inode *parent_inode, umode_t mode, + struct cifs_tcon *tcon, const char *name, + struct cifs_sb_info *cifs_sb) +{ + return smb2_compound_op(xid, tcon, cifs_sb, name, + FILE_WRITE_ATTRIBUTES, FILE_CREATE, + CREATE_NOT_FILE, mode, NULL, SMB2_OP_MKDIR, + NULL, NULL, NULL); +} + +void +smb2_mkdir_setinfo(struct inode *inode, const char *name, + struct cifs_sb_info *cifs_sb, struct cifs_tcon *tcon, + const unsigned int xid) +{ + FILE_BASIC_INFO data; + struct cifsInodeInfo *cifs_i; + struct cifsFileInfo *cfile; + u32 dosattrs; + int tmprc; + + memset(&data, 0, sizeof(data)); + cifs_i = CIFS_I(inode); + dosattrs = cifs_i->cifsAttrs | ATTR_READONLY; + data.Attributes = cpu_to_le32(dosattrs); + cifs_get_writable_path(tcon, name, FIND_WR_ANY, &cfile); + tmprc = smb2_compound_op(xid, tcon, cifs_sb, name, + FILE_WRITE_ATTRIBUTES, FILE_CREATE, + CREATE_NOT_FILE, ACL_NO_MODE, + &data, SMB2_OP_SET_INFO, cfile, NULL, NULL); + if (tmprc == 0) + cifs_i->cifsAttrs = dosattrs; +} + +int +smb2_rmdir(const unsigned int xid, struct cifs_tcon *tcon, const char *name, + struct cifs_sb_info *cifs_sb) +{ + drop_cached_dir_by_name(xid, tcon, name, cifs_sb); + return smb2_compound_op(xid, tcon, cifs_sb, name, DELETE, FILE_OPEN, + CREATE_NOT_FILE, ACL_NO_MODE, + NULL, SMB2_OP_RMDIR, NULL, NULL, NULL); +} + +int +smb2_unlink(const unsigned int xid, struct cifs_tcon *tcon, const char *name, + struct cifs_sb_info *cifs_sb) +{ + return smb2_compound_op(xid, tcon, cifs_sb, name, DELETE, FILE_OPEN, + CREATE_DELETE_ON_CLOSE | OPEN_REPARSE_POINT, + ACL_NO_MODE, NULL, SMB2_OP_DELETE, NULL, NULL, NULL); +} + +static int +smb2_set_path_attr(const unsigned int xid, struct cifs_tcon *tcon, + const char *from_name, const char *to_name, + struct cifs_sb_info *cifs_sb, __u32 access, int command, + struct cifsFileInfo *cfile) +{ + __le16 *smb2_to_name = NULL; + int rc; + + smb2_to_name = cifs_convert_path_to_utf16(to_name, cifs_sb); + if (smb2_to_name == NULL) { + rc = -ENOMEM; + goto smb2_rename_path; + } + rc = smb2_compound_op(xid, tcon, cifs_sb, from_name, access, + FILE_OPEN, 0, ACL_NO_MODE, smb2_to_name, + command, cfile, NULL, NULL); +smb2_rename_path: + kfree(smb2_to_name); + return rc; +} + +int +smb2_rename_path(const unsigned int xid, struct cifs_tcon *tcon, + const char *from_name, const char *to_name, + struct cifs_sb_info *cifs_sb) +{ + struct cifsFileInfo *cfile; + + drop_cached_dir_by_name(xid, tcon, from_name, cifs_sb); + cifs_get_writable_path(tcon, from_name, FIND_WR_WITH_DELETE, &cfile); + + return smb2_set_path_attr(xid, tcon, from_name, to_name, + cifs_sb, DELETE, SMB2_OP_RENAME, cfile); +} + +int +smb2_create_hardlink(const unsigned int xid, struct cifs_tcon *tcon, + const char *from_name, const char *to_name, + struct cifs_sb_info *cifs_sb) +{ + return smb2_set_path_attr(xid, tcon, from_name, to_name, cifs_sb, + FILE_READ_ATTRIBUTES, SMB2_OP_HARDLINK, + NULL); +} + +int +smb2_set_path_size(const unsigned int xid, struct cifs_tcon *tcon, + const char *full_path, __u64 size, + struct cifs_sb_info *cifs_sb, bool set_alloc) +{ + __le64 eof = cpu_to_le64(size); + struct cifsFileInfo *cfile; + + cifs_get_writable_path(tcon, full_path, FIND_WR_ANY, &cfile); + return smb2_compound_op(xid, tcon, cifs_sb, full_path, + FILE_WRITE_DATA, FILE_OPEN, 0, ACL_NO_MODE, + &eof, SMB2_OP_SET_EOF, cfile, NULL, NULL); +} + +int +smb2_set_file_info(struct inode *inode, const char *full_path, + FILE_BASIC_INFO *buf, const unsigned int xid) +{ + struct cifs_sb_info *cifs_sb = CIFS_SB(inode->i_sb); + struct tcon_link *tlink; + struct cifs_tcon *tcon; + struct cifsFileInfo *cfile; + int rc; + + if ((buf->CreationTime == 0) && (buf->LastAccessTime == 0) && + (buf->LastWriteTime == 0) && (buf->ChangeTime == 0) && + (buf->Attributes == 0)) + return 0; /* would be a no op, no sense sending this */ + + tlink = cifs_sb_tlink(cifs_sb); + if (IS_ERR(tlink)) + return PTR_ERR(tlink); + tcon = tlink_tcon(tlink); + + cifs_get_writable_path(tcon, full_path, FIND_WR_ANY, &cfile); + rc = smb2_compound_op(xid, tcon, cifs_sb, full_path, + FILE_WRITE_ATTRIBUTES, FILE_OPEN, + 0, ACL_NO_MODE, buf, SMB2_OP_SET_INFO, cfile, + NULL, NULL); + cifs_put_tlink(tlink); + return rc; +} diff --git a/fs/smb/client/smb2maperror.c b/fs/smb/client/smb2maperror.c new file mode 100644 index 0000000000000..194799ddd3828 --- /dev/null +++ b/fs/smb/client/smb2maperror.c @@ -0,0 +1,2481 @@ +// SPDX-License-Identifier: LGPL-2.1 +/* + * + * Functions which do error mapping of SMB2 status codes to POSIX errors + * + * Copyright (C) International Business Machines Corp., 2009 + * Author(s): Steve French (sfrench@us.ibm.com) + * + */ +#include +#include "cifsglob.h" +#include "cifs_debug.h" +#include "smb2pdu.h" +#include "smb2proto.h" +#include "smb2status.h" +#include "smb2glob.h" +#include "trace.h" + +struct status_to_posix_error { + __le32 smb2_status; + int posix_error; + char *status_string; +}; + +static const struct status_to_posix_error smb2_error_map_table[] = { + {STATUS_SUCCESS, 0, "STATUS_SUCCESS"}, + {STATUS_WAIT_0, 0, "STATUS_WAIT_0"}, + {STATUS_WAIT_1, -EIO, "STATUS_WAIT_1"}, + {STATUS_WAIT_2, -EIO, "STATUS_WAIT_2"}, + {STATUS_WAIT_3, -EIO, "STATUS_WAIT_3"}, + {STATUS_WAIT_63, -EIO, "STATUS_WAIT_63"}, + {STATUS_ABANDONED, -EIO, "STATUS_ABANDONED"}, + {STATUS_ABANDONED_WAIT_0, -EIO, "STATUS_ABANDONED_WAIT_0"}, + {STATUS_ABANDONED_WAIT_63, -EIO, "STATUS_ABANDONED_WAIT_63"}, + {STATUS_USER_APC, -EIO, "STATUS_USER_APC"}, + {STATUS_KERNEL_APC, -EIO, "STATUS_KERNEL_APC"}, + {STATUS_ALERTED, -EIO, "STATUS_ALERTED"}, + {STATUS_TIMEOUT, -ETIMEDOUT, "STATUS_TIMEOUT"}, + {STATUS_PENDING, -EIO, "STATUS_PENDING"}, + {STATUS_REPARSE, -EIO, "STATUS_REPARSE"}, + {STATUS_MORE_ENTRIES, -EIO, "STATUS_MORE_ENTRIES"}, + {STATUS_NOT_ALL_ASSIGNED, -EIO, "STATUS_NOT_ALL_ASSIGNED"}, + {STATUS_SOME_NOT_MAPPED, -EIO, "STATUS_SOME_NOT_MAPPED"}, + {STATUS_OPLOCK_BREAK_IN_PROGRESS, -EIO, + "STATUS_OPLOCK_BREAK_IN_PROGRESS"}, + {STATUS_VOLUME_MOUNTED, -EIO, "STATUS_VOLUME_MOUNTED"}, + {STATUS_RXACT_COMMITTED, -EIO, "STATUS_RXACT_COMMITTED"}, + {STATUS_NOTIFY_CLEANUP, -EIO, "STATUS_NOTIFY_CLEANUP"}, + {STATUS_NOTIFY_ENUM_DIR, -EIO, "STATUS_NOTIFY_ENUM_DIR"}, + {STATUS_NO_QUOTAS_FOR_ACCOUNT, -EIO, "STATUS_NO_QUOTAS_FOR_ACCOUNT"}, + {STATUS_PRIMARY_TRANSPORT_CONNECT_FAILED, -EIO, + "STATUS_PRIMARY_TRANSPORT_CONNECT_FAILED"}, + {STATUS_PAGE_FAULT_TRANSITION, -EIO, "STATUS_PAGE_FAULT_TRANSITION"}, + {STATUS_PAGE_FAULT_DEMAND_ZERO, -EIO, "STATUS_PAGE_FAULT_DEMAND_ZERO"}, + {STATUS_PAGE_FAULT_COPY_ON_WRITE, -EIO, + "STATUS_PAGE_FAULT_COPY_ON_WRITE"}, + {STATUS_PAGE_FAULT_GUARD_PAGE, -EIO, "STATUS_PAGE_FAULT_GUARD_PAGE"}, + {STATUS_PAGE_FAULT_PAGING_FILE, -EIO, "STATUS_PAGE_FAULT_PAGING_FILE"}, + {STATUS_CACHE_PAGE_LOCKED, -EIO, "STATUS_CACHE_PAGE_LOCKED"}, + {STATUS_CRASH_DUMP, -EIO, "STATUS_CRASH_DUMP"}, + {STATUS_BUFFER_ALL_ZEROS, -EIO, "STATUS_BUFFER_ALL_ZEROS"}, + {STATUS_REPARSE_OBJECT, -EIO, "STATUS_REPARSE_OBJECT"}, + {STATUS_RESOURCE_REQUIREMENTS_CHANGED, -EIO, + "STATUS_RESOURCE_REQUIREMENTS_CHANGED"}, + {STATUS_TRANSLATION_COMPLETE, -EIO, "STATUS_TRANSLATION_COMPLETE"}, + {STATUS_DS_MEMBERSHIP_EVALUATED_LOCALLY, -EIO, + "STATUS_DS_MEMBERSHIP_EVALUATED_LOCALLY"}, + {STATUS_NOTHING_TO_TERMINATE, -EIO, "STATUS_NOTHING_TO_TERMINATE"}, + {STATUS_PROCESS_NOT_IN_JOB, -EIO, "STATUS_PROCESS_NOT_IN_JOB"}, + {STATUS_PROCESS_IN_JOB, -EIO, "STATUS_PROCESS_IN_JOB"}, + {STATUS_VOLSNAP_HIBERNATE_READY, -EIO, + "STATUS_VOLSNAP_HIBERNATE_READY"}, + {STATUS_FSFILTER_OP_COMPLETED_SUCCESSFULLY, -EIO, + "STATUS_FSFILTER_OP_COMPLETED_SUCCESSFULLY"}, + {STATUS_INTERRUPT_VECTOR_ALREADY_CONNECTED, -EIO, + "STATUS_INTERRUPT_VECTOR_ALREADY_CONNECTED"}, + {STATUS_INTERRUPT_STILL_CONNECTED, -EIO, + "STATUS_INTERRUPT_STILL_CONNECTED"}, + {STATUS_PROCESS_CLONED, -EIO, "STATUS_PROCESS_CLONED"}, + {STATUS_FILE_LOCKED_WITH_ONLY_READERS, -EIO, + "STATUS_FILE_LOCKED_WITH_ONLY_READERS"}, + {STATUS_FILE_LOCKED_WITH_WRITERS, -EIO, + "STATUS_FILE_LOCKED_WITH_WRITERS"}, + {STATUS_RESOURCEMANAGER_READ_ONLY, -EROFS, + "STATUS_RESOURCEMANAGER_READ_ONLY"}, + {STATUS_WAIT_FOR_OPLOCK, -EIO, "STATUS_WAIT_FOR_OPLOCK"}, + {DBG_EXCEPTION_HANDLED, -EIO, "DBG_EXCEPTION_HANDLED"}, + {DBG_CONTINUE, -EIO, "DBG_CONTINUE"}, + {STATUS_FLT_IO_COMPLETE, -EIO, "STATUS_FLT_IO_COMPLETE"}, + {STATUS_OBJECT_NAME_EXISTS, -EIO, "STATUS_OBJECT_NAME_EXISTS"}, + {STATUS_THREAD_WAS_SUSPENDED, -EIO, "STATUS_THREAD_WAS_SUSPENDED"}, + {STATUS_WORKING_SET_LIMIT_RANGE, -EIO, + "STATUS_WORKING_SET_LIMIT_RANGE"}, + {STATUS_IMAGE_NOT_AT_BASE, -EIO, "STATUS_IMAGE_NOT_AT_BASE"}, + {STATUS_RXACT_STATE_CREATED, -EIO, "STATUS_RXACT_STATE_CREATED"}, + {STATUS_SEGMENT_NOTIFICATION, -EIO, "STATUS_SEGMENT_NOTIFICATION"}, + {STATUS_LOCAL_USER_SESSION_KEY, -EIO, "STATUS_LOCAL_USER_SESSION_KEY"}, + {STATUS_BAD_CURRENT_DIRECTORY, -EIO, "STATUS_BAD_CURRENT_DIRECTORY"}, + {STATUS_SERIAL_MORE_WRITES, -EIO, "STATUS_SERIAL_MORE_WRITES"}, + {STATUS_REGISTRY_RECOVERED, -EIO, "STATUS_REGISTRY_RECOVERED"}, + {STATUS_FT_READ_RECOVERY_FROM_BACKUP, -EIO, + "STATUS_FT_READ_RECOVERY_FROM_BACKUP"}, + {STATUS_FT_WRITE_RECOVERY, -EIO, "STATUS_FT_WRITE_RECOVERY"}, + {STATUS_SERIAL_COUNTER_TIMEOUT, -ETIMEDOUT, + "STATUS_SERIAL_COUNTER_TIMEOUT"}, + {STATUS_NULL_LM_PASSWORD, -EIO, "STATUS_NULL_LM_PASSWORD"}, + {STATUS_IMAGE_MACHINE_TYPE_MISMATCH, -EIO, + "STATUS_IMAGE_MACHINE_TYPE_MISMATCH"}, + {STATUS_RECEIVE_PARTIAL, -EIO, "STATUS_RECEIVE_PARTIAL"}, + {STATUS_RECEIVE_EXPEDITED, -EIO, "STATUS_RECEIVE_EXPEDITED"}, + {STATUS_RECEIVE_PARTIAL_EXPEDITED, -EIO, + "STATUS_RECEIVE_PARTIAL_EXPEDITED"}, + {STATUS_EVENT_DONE, -EIO, "STATUS_EVENT_DONE"}, + {STATUS_EVENT_PENDING, -EIO, "STATUS_EVENT_PENDING"}, + {STATUS_CHECKING_FILE_SYSTEM, -EIO, "STATUS_CHECKING_FILE_SYSTEM"}, + {STATUS_FATAL_APP_EXIT, -EIO, "STATUS_FATAL_APP_EXIT"}, + {STATUS_PREDEFINED_HANDLE, -EIO, "STATUS_PREDEFINED_HANDLE"}, + {STATUS_WAS_UNLOCKED, -EIO, "STATUS_WAS_UNLOCKED"}, + {STATUS_SERVICE_NOTIFICATION, -EIO, "STATUS_SERVICE_NOTIFICATION"}, + {STATUS_WAS_LOCKED, -EIO, "STATUS_WAS_LOCKED"}, + {STATUS_LOG_HARD_ERROR, -EIO, "STATUS_LOG_HARD_ERROR"}, + {STATUS_ALREADY_WIN32, -EIO, "STATUS_ALREADY_WIN32"}, + {STATUS_WX86_UNSIMULATE, -EIO, "STATUS_WX86_UNSIMULATE"}, + {STATUS_WX86_CONTINUE, -EIO, "STATUS_WX86_CONTINUE"}, + {STATUS_WX86_SINGLE_STEP, -EIO, "STATUS_WX86_SINGLE_STEP"}, + {STATUS_WX86_BREAKPOINT, -EIO, "STATUS_WX86_BREAKPOINT"}, + {STATUS_WX86_EXCEPTION_CONTINUE, -EIO, + "STATUS_WX86_EXCEPTION_CONTINUE"}, + {STATUS_WX86_EXCEPTION_LASTCHANCE, -EIO, + "STATUS_WX86_EXCEPTION_LASTCHANCE"}, + {STATUS_WX86_EXCEPTION_CHAIN, -EIO, "STATUS_WX86_EXCEPTION_CHAIN"}, + {STATUS_IMAGE_MACHINE_TYPE_MISMATCH_EXE, -EIO, + "STATUS_IMAGE_MACHINE_TYPE_MISMATCH_EXE"}, + {STATUS_NO_YIELD_PERFORMED, -EIO, "STATUS_NO_YIELD_PERFORMED"}, + {STATUS_TIMER_RESUME_IGNORED, -EIO, "STATUS_TIMER_RESUME_IGNORED"}, + {STATUS_ARBITRATION_UNHANDLED, -EIO, "STATUS_ARBITRATION_UNHANDLED"}, + {STATUS_CARDBUS_NOT_SUPPORTED, -ENOSYS, "STATUS_CARDBUS_NOT_SUPPORTED"}, + {STATUS_WX86_CREATEWX86TIB, -EIO, "STATUS_WX86_CREATEWX86TIB"}, + {STATUS_MP_PROCESSOR_MISMATCH, -EIO, "STATUS_MP_PROCESSOR_MISMATCH"}, + {STATUS_HIBERNATED, -EIO, "STATUS_HIBERNATED"}, + {STATUS_RESUME_HIBERNATION, -EIO, "STATUS_RESUME_HIBERNATION"}, + {STATUS_FIRMWARE_UPDATED, -EIO, "STATUS_FIRMWARE_UPDATED"}, + {STATUS_DRIVERS_LEAKING_LOCKED_PAGES, -EIO, + "STATUS_DRIVERS_LEAKING_LOCKED_PAGES"}, + {STATUS_MESSAGE_RETRIEVED, -EIO, "STATUS_MESSAGE_RETRIEVED"}, + {STATUS_SYSTEM_POWERSTATE_TRANSITION, -EIO, + "STATUS_SYSTEM_POWERSTATE_TRANSITION"}, + {STATUS_ALPC_CHECK_COMPLETION_LIST, -EIO, + "STATUS_ALPC_CHECK_COMPLETION_LIST"}, + {STATUS_SYSTEM_POWERSTATE_COMPLEX_TRANSITION, -EIO, + "STATUS_SYSTEM_POWERSTATE_COMPLEX_TRANSITION"}, + {STATUS_ACCESS_AUDIT_BY_POLICY, -EIO, "STATUS_ACCESS_AUDIT_BY_POLICY"}, + {STATUS_ABANDON_HIBERFILE, -EIO, "STATUS_ABANDON_HIBERFILE"}, + {STATUS_BIZRULES_NOT_ENABLED, -EIO, "STATUS_BIZRULES_NOT_ENABLED"}, + {STATUS_WAKE_SYSTEM, -EIO, "STATUS_WAKE_SYSTEM"}, + {STATUS_DS_SHUTTING_DOWN, -EIO, "STATUS_DS_SHUTTING_DOWN"}, + {DBG_REPLY_LATER, -EIO, "DBG_REPLY_LATER"}, + {DBG_UNABLE_TO_PROVIDE_HANDLE, -EIO, "DBG_UNABLE_TO_PROVIDE_HANDLE"}, + {DBG_TERMINATE_THREAD, -EIO, "DBG_TERMINATE_THREAD"}, + {DBG_TERMINATE_PROCESS, -EIO, "DBG_TERMINATE_PROCESS"}, + {DBG_CONTROL_C, -EIO, "DBG_CONTROL_C"}, + {DBG_PRINTEXCEPTION_C, -EIO, "DBG_PRINTEXCEPTION_C"}, + {DBG_RIPEXCEPTION, -EIO, "DBG_RIPEXCEPTION"}, + {DBG_CONTROL_BREAK, -EIO, "DBG_CONTROL_BREAK"}, + {DBG_COMMAND_EXCEPTION, -EIO, "DBG_COMMAND_EXCEPTION"}, + {RPC_NT_UUID_LOCAL_ONLY, -EIO, "RPC_NT_UUID_LOCAL_ONLY"}, + {RPC_NT_SEND_INCOMPLETE, -EIO, "RPC_NT_SEND_INCOMPLETE"}, + {STATUS_CTX_CDM_CONNECT, -EIO, "STATUS_CTX_CDM_CONNECT"}, + {STATUS_CTX_CDM_DISCONNECT, -EIO, "STATUS_CTX_CDM_DISCONNECT"}, + {STATUS_SXS_RELEASE_ACTIVATION_CONTEXT, -EIO, + "STATUS_SXS_RELEASE_ACTIVATION_CONTEXT"}, + {STATUS_RECOVERY_NOT_NEEDED, -EIO, "STATUS_RECOVERY_NOT_NEEDED"}, + {STATUS_RM_ALREADY_STARTED, -EIO, "STATUS_RM_ALREADY_STARTED"}, + {STATUS_LOG_NO_RESTART, -EIO, "STATUS_LOG_NO_RESTART"}, + {STATUS_VIDEO_DRIVER_DEBUG_REPORT_REQUEST, -EIO, + "STATUS_VIDEO_DRIVER_DEBUG_REPORT_REQUEST"}, + {STATUS_GRAPHICS_PARTIAL_DATA_POPULATED, -EIO, + "STATUS_GRAPHICS_PARTIAL_DATA_POPULATED"}, + {STATUS_GRAPHICS_DRIVER_MISMATCH, -EIO, + "STATUS_GRAPHICS_DRIVER_MISMATCH"}, + {STATUS_GRAPHICS_MODE_NOT_PINNED, -EIO, + "STATUS_GRAPHICS_MODE_NOT_PINNED"}, + {STATUS_GRAPHICS_NO_PREFERRED_MODE, -EIO, + "STATUS_GRAPHICS_NO_PREFERRED_MODE"}, + {STATUS_GRAPHICS_DATASET_IS_EMPTY, -EIO, + "STATUS_GRAPHICS_DATASET_IS_EMPTY"}, + {STATUS_GRAPHICS_NO_MORE_ELEMENTS_IN_DATASET, -EIO, + "STATUS_GRAPHICS_NO_MORE_ELEMENTS_IN_DATASET"}, + {STATUS_GRAPHICS_PATH_CONTENT_GEOMETRY_TRANSFORMATION_NOT_PINNED, -EIO, + "STATUS_GRAPHICS_PATH_CONTENT_GEOMETRY_TRANSFORMATION_NOT_PINNED"}, + {STATUS_GRAPHICS_UNKNOWN_CHILD_STATUS, -EIO, + "STATUS_GRAPHICS_UNKNOWN_CHILD_STATUS"}, + {STATUS_GRAPHICS_LEADLINK_START_DEFERRED, -EIO, + "STATUS_GRAPHICS_LEADLINK_START_DEFERRED"}, + {STATUS_GRAPHICS_POLLING_TOO_FREQUENTLY, -EIO, + "STATUS_GRAPHICS_POLLING_TOO_FREQUENTLY"}, + {STATUS_GRAPHICS_START_DEFERRED, -EIO, + "STATUS_GRAPHICS_START_DEFERRED"}, + {STATUS_NDIS_INDICATION_REQUIRED, -EIO, + "STATUS_NDIS_INDICATION_REQUIRED"}, + {STATUS_GUARD_PAGE_VIOLATION, -EIO, "STATUS_GUARD_PAGE_VIOLATION"}, + {STATUS_DATATYPE_MISALIGNMENT, -EIO, "STATUS_DATATYPE_MISALIGNMENT"}, + {STATUS_BREAKPOINT, -EIO, "STATUS_BREAKPOINT"}, + {STATUS_SINGLE_STEP, -EIO, "STATUS_SINGLE_STEP"}, + {STATUS_BUFFER_OVERFLOW, -E2BIG, "STATUS_BUFFER_OVERFLOW"}, + {STATUS_NO_MORE_FILES, -ENODATA, "STATUS_NO_MORE_FILES"}, + {STATUS_WAKE_SYSTEM_DEBUGGER, -EIO, "STATUS_WAKE_SYSTEM_DEBUGGER"}, + {STATUS_HANDLES_CLOSED, -EIO, "STATUS_HANDLES_CLOSED"}, + {STATUS_NO_INHERITANCE, -EIO, "STATUS_NO_INHERITANCE"}, + {STATUS_GUID_SUBSTITUTION_MADE, -EIO, "STATUS_GUID_SUBSTITUTION_MADE"}, + {STATUS_PARTIAL_COPY, -EIO, "STATUS_PARTIAL_COPY"}, + {STATUS_DEVICE_PAPER_EMPTY, -EIO, "STATUS_DEVICE_PAPER_EMPTY"}, + {STATUS_DEVICE_POWERED_OFF, -EIO, "STATUS_DEVICE_POWERED_OFF"}, + {STATUS_DEVICE_OFF_LINE, -EIO, "STATUS_DEVICE_OFF_LINE"}, + {STATUS_DEVICE_BUSY, -EBUSY, "STATUS_DEVICE_BUSY"}, + {STATUS_NO_MORE_EAS, -EIO, "STATUS_NO_MORE_EAS"}, + {STATUS_INVALID_EA_NAME, -EINVAL, "STATUS_INVALID_EA_NAME"}, + {STATUS_EA_LIST_INCONSISTENT, -EIO, "STATUS_EA_LIST_INCONSISTENT"}, + {STATUS_INVALID_EA_FLAG, -EINVAL, "STATUS_INVALID_EA_FLAG"}, + {STATUS_VERIFY_REQUIRED, -EIO, "STATUS_VERIFY_REQUIRED"}, + {STATUS_EXTRANEOUS_INFORMATION, -EIO, "STATUS_EXTRANEOUS_INFORMATION"}, + {STATUS_RXACT_COMMIT_NECESSARY, -EIO, "STATUS_RXACT_COMMIT_NECESSARY"}, + {STATUS_NO_MORE_ENTRIES, -EIO, "STATUS_NO_MORE_ENTRIES"}, + {STATUS_FILEMARK_DETECTED, -EIO, "STATUS_FILEMARK_DETECTED"}, + {STATUS_MEDIA_CHANGED, -EIO, "STATUS_MEDIA_CHANGED"}, + {STATUS_BUS_RESET, -EIO, "STATUS_BUS_RESET"}, + {STATUS_END_OF_MEDIA, -EIO, "STATUS_END_OF_MEDIA"}, + {STATUS_BEGINNING_OF_MEDIA, -EIO, "STATUS_BEGINNING_OF_MEDIA"}, + {STATUS_MEDIA_CHECK, -EIO, "STATUS_MEDIA_CHECK"}, + {STATUS_SETMARK_DETECTED, -EIO, "STATUS_SETMARK_DETECTED"}, + {STATUS_NO_DATA_DETECTED, -EIO, "STATUS_NO_DATA_DETECTED"}, + {STATUS_REDIRECTOR_HAS_OPEN_HANDLES, -EIO, + "STATUS_REDIRECTOR_HAS_OPEN_HANDLES"}, + {STATUS_SERVER_HAS_OPEN_HANDLES, -EIO, + "STATUS_SERVER_HAS_OPEN_HANDLES"}, + {STATUS_ALREADY_DISCONNECTED, -EIO, "STATUS_ALREADY_DISCONNECTED"}, + {STATUS_LONGJUMP, -EIO, "STATUS_LONGJUMP"}, + {STATUS_CLEANER_CARTRIDGE_INSTALLED, -EIO, + "STATUS_CLEANER_CARTRIDGE_INSTALLED"}, + {STATUS_PLUGPLAY_QUERY_VETOED, -EIO, "STATUS_PLUGPLAY_QUERY_VETOED"}, + {STATUS_UNWIND_CONSOLIDATE, -EIO, "STATUS_UNWIND_CONSOLIDATE"}, + {STATUS_REGISTRY_HIVE_RECOVERED, -EIO, + "STATUS_REGISTRY_HIVE_RECOVERED"}, + {STATUS_DLL_MIGHT_BE_INSECURE, -EIO, "STATUS_DLL_MIGHT_BE_INSECURE"}, + {STATUS_DLL_MIGHT_BE_INCOMPATIBLE, -EIO, + "STATUS_DLL_MIGHT_BE_INCOMPATIBLE"}, + {STATUS_STOPPED_ON_SYMLINK, -EOPNOTSUPP, "STATUS_STOPPED_ON_SYMLINK"}, + {STATUS_IO_REPARSE_TAG_NOT_HANDLED, -EOPNOTSUPP, + "STATUS_REPARSE_NOT_HANDLED"}, + {STATUS_DEVICE_REQUIRES_CLEANING, -EIO, + "STATUS_DEVICE_REQUIRES_CLEANING"}, + {STATUS_DEVICE_DOOR_OPEN, -EIO, "STATUS_DEVICE_DOOR_OPEN"}, + {STATUS_DATA_LOST_REPAIR, -EIO, "STATUS_DATA_LOST_REPAIR"}, + {DBG_EXCEPTION_NOT_HANDLED, -EIO, "DBG_EXCEPTION_NOT_HANDLED"}, + {STATUS_CLUSTER_NODE_ALREADY_UP, -EIO, + "STATUS_CLUSTER_NODE_ALREADY_UP"}, + {STATUS_CLUSTER_NODE_ALREADY_DOWN, -EIO, + "STATUS_CLUSTER_NODE_ALREADY_DOWN"}, + {STATUS_CLUSTER_NETWORK_ALREADY_ONLINE, -EIO, + "STATUS_CLUSTER_NETWORK_ALREADY_ONLINE"}, + {STATUS_CLUSTER_NETWORK_ALREADY_OFFLINE, -EIO, + "STATUS_CLUSTER_NETWORK_ALREADY_OFFLINE"}, + {STATUS_CLUSTER_NODE_ALREADY_MEMBER, -EIO, + "STATUS_CLUSTER_NODE_ALREADY_MEMBER"}, + {STATUS_COULD_NOT_RESIZE_LOG, -EIO, "STATUS_COULD_NOT_RESIZE_LOG"}, + {STATUS_NO_TXF_METADATA, -EIO, "STATUS_NO_TXF_METADATA"}, + {STATUS_CANT_RECOVER_WITH_HANDLE_OPEN, -EIO, + "STATUS_CANT_RECOVER_WITH_HANDLE_OPEN"}, + {STATUS_TXF_METADATA_ALREADY_PRESENT, -EIO, + "STATUS_TXF_METADATA_ALREADY_PRESENT"}, + {STATUS_TRANSACTION_SCOPE_CALLBACKS_NOT_SET, -EIO, + "STATUS_TRANSACTION_SCOPE_CALLBACKS_NOT_SET"}, + {STATUS_VIDEO_HUNG_DISPLAY_DRIVER_THREAD_RECOVERED, -EIO, + "STATUS_VIDEO_HUNG_DISPLAY_DRIVER_THREAD_RECOVERED"}, + {STATUS_FLT_BUFFER_TOO_SMALL, -ENOBUFS, "STATUS_FLT_BUFFER_TOO_SMALL"}, + {STATUS_FVE_PARTIAL_METADATA, -EIO, "STATUS_FVE_PARTIAL_METADATA"}, + {STATUS_UNSUCCESSFUL, -EIO, "STATUS_UNSUCCESSFUL"}, + {STATUS_NOT_IMPLEMENTED, -EOPNOTSUPP, "STATUS_NOT_IMPLEMENTED"}, + {STATUS_INVALID_INFO_CLASS, -EIO, "STATUS_INVALID_INFO_CLASS"}, + {STATUS_INFO_LENGTH_MISMATCH, -EIO, "STATUS_INFO_LENGTH_MISMATCH"}, + {STATUS_ACCESS_VIOLATION, -EACCES, "STATUS_ACCESS_VIOLATION"}, + {STATUS_IN_PAGE_ERROR, -EFAULT, "STATUS_IN_PAGE_ERROR"}, + {STATUS_PAGEFILE_QUOTA, -EDQUOT, "STATUS_PAGEFILE_QUOTA"}, + {STATUS_INVALID_HANDLE, -EBADF, "STATUS_INVALID_HANDLE"}, + {STATUS_BAD_INITIAL_STACK, -EIO, "STATUS_BAD_INITIAL_STACK"}, + {STATUS_BAD_INITIAL_PC, -EIO, "STATUS_BAD_INITIAL_PC"}, + {STATUS_INVALID_CID, -EIO, "STATUS_INVALID_CID"}, + {STATUS_TIMER_NOT_CANCELED, -EIO, "STATUS_TIMER_NOT_CANCELED"}, + {STATUS_INVALID_PARAMETER, -EINVAL, "STATUS_INVALID_PARAMETER"}, + {STATUS_NO_SUCH_DEVICE, -ENODEV, "STATUS_NO_SUCH_DEVICE"}, + {STATUS_NO_SUCH_FILE, -ENOENT, "STATUS_NO_SUCH_FILE"}, + {STATUS_INVALID_DEVICE_REQUEST, -EOPNOTSUPP, "STATUS_INVALID_DEVICE_REQUEST"}, + {STATUS_END_OF_FILE, -ENODATA, "STATUS_END_OF_FILE"}, + {STATUS_WRONG_VOLUME, -EIO, "STATUS_WRONG_VOLUME"}, + {STATUS_NO_MEDIA_IN_DEVICE, -EIO, "STATUS_NO_MEDIA_IN_DEVICE"}, + {STATUS_UNRECOGNIZED_MEDIA, -EIO, "STATUS_UNRECOGNIZED_MEDIA"}, + {STATUS_NONEXISTENT_SECTOR, -EIO, "STATUS_NONEXISTENT_SECTOR"}, + {STATUS_MORE_PROCESSING_REQUIRED, -EIO, + "STATUS_MORE_PROCESSING_REQUIRED"}, + {STATUS_NO_MEMORY, -EREMOTEIO, "STATUS_NO_MEMORY"}, + {STATUS_CONFLICTING_ADDRESSES, -EADDRINUSE, + "STATUS_CONFLICTING_ADDRESSES"}, + {STATUS_NOT_MAPPED_VIEW, -EIO, "STATUS_NOT_MAPPED_VIEW"}, + {STATUS_UNABLE_TO_FREE_VM, -EIO, "STATUS_UNABLE_TO_FREE_VM"}, + {STATUS_UNABLE_TO_DELETE_SECTION, -EIO, + "STATUS_UNABLE_TO_DELETE_SECTION"}, + {STATUS_INVALID_SYSTEM_SERVICE, -EIO, "STATUS_INVALID_SYSTEM_SERVICE"}, + {STATUS_ILLEGAL_INSTRUCTION, -EIO, "STATUS_ILLEGAL_INSTRUCTION"}, + {STATUS_INVALID_LOCK_SEQUENCE, -EIO, "STATUS_INVALID_LOCK_SEQUENCE"}, + {STATUS_INVALID_VIEW_SIZE, -EIO, "STATUS_INVALID_VIEW_SIZE"}, + {STATUS_INVALID_FILE_FOR_SECTION, -EIO, + "STATUS_INVALID_FILE_FOR_SECTION"}, + {STATUS_ALREADY_COMMITTED, -EIO, "STATUS_ALREADY_COMMITTED"}, + {STATUS_ACCESS_DENIED, -EACCES, "STATUS_ACCESS_DENIED"}, + {STATUS_BUFFER_TOO_SMALL, -EIO, "STATUS_BUFFER_TOO_SMALL"}, + {STATUS_OBJECT_TYPE_MISMATCH, -EIO, "STATUS_OBJECT_TYPE_MISMATCH"}, + {STATUS_NONCONTINUABLE_EXCEPTION, -EIO, + "STATUS_NONCONTINUABLE_EXCEPTION"}, + {STATUS_INVALID_DISPOSITION, -EIO, "STATUS_INVALID_DISPOSITION"}, + {STATUS_UNWIND, -EIO, "STATUS_UNWIND"}, + {STATUS_BAD_STACK, -EIO, "STATUS_BAD_STACK"}, + {STATUS_INVALID_UNWIND_TARGET, -EIO, "STATUS_INVALID_UNWIND_TARGET"}, + {STATUS_NOT_LOCKED, -EIO, "STATUS_NOT_LOCKED"}, + {STATUS_PARITY_ERROR, -EIO, "STATUS_PARITY_ERROR"}, + {STATUS_UNABLE_TO_DECOMMIT_VM, -EIO, "STATUS_UNABLE_TO_DECOMMIT_VM"}, + {STATUS_NOT_COMMITTED, -EIO, "STATUS_NOT_COMMITTED"}, + {STATUS_INVALID_PORT_ATTRIBUTES, -EIO, + "STATUS_INVALID_PORT_ATTRIBUTES"}, + {STATUS_PORT_MESSAGE_TOO_LONG, -EIO, "STATUS_PORT_MESSAGE_TOO_LONG"}, + {STATUS_INVALID_PARAMETER_MIX, -EINVAL, "STATUS_INVALID_PARAMETER_MIX"}, + {STATUS_INVALID_QUOTA_LOWER, -EIO, "STATUS_INVALID_QUOTA_LOWER"}, + {STATUS_DISK_CORRUPT_ERROR, -EIO, "STATUS_DISK_CORRUPT_ERROR"}, + {STATUS_OBJECT_NAME_INVALID, -ENOENT, "STATUS_OBJECT_NAME_INVALID"}, + {STATUS_OBJECT_NAME_NOT_FOUND, -ENOENT, "STATUS_OBJECT_NAME_NOT_FOUND"}, + {STATUS_OBJECT_NAME_COLLISION, -EEXIST, "STATUS_OBJECT_NAME_COLLISION"}, + {STATUS_PORT_DISCONNECTED, -EIO, "STATUS_PORT_DISCONNECTED"}, + {STATUS_DEVICE_ALREADY_ATTACHED, -EIO, + "STATUS_DEVICE_ALREADY_ATTACHED"}, + {STATUS_OBJECT_PATH_INVALID, -ENOTDIR, "STATUS_OBJECT_PATH_INVALID"}, + {STATUS_OBJECT_PATH_NOT_FOUND, -ENOENT, "STATUS_OBJECT_PATH_NOT_FOUND"}, + {STATUS_OBJECT_PATH_SYNTAX_BAD, -EIO, "STATUS_OBJECT_PATH_SYNTAX_BAD"}, + {STATUS_DATA_OVERRUN, -EIO, "STATUS_DATA_OVERRUN"}, + {STATUS_DATA_LATE_ERROR, -EIO, "STATUS_DATA_LATE_ERROR"}, + {STATUS_DATA_ERROR, -EIO, "STATUS_DATA_ERROR"}, + {STATUS_CRC_ERROR, -EIO, "STATUS_CRC_ERROR"}, + {STATUS_SECTION_TOO_BIG, -EIO, "STATUS_SECTION_TOO_BIG"}, + {STATUS_PORT_CONNECTION_REFUSED, -ECONNREFUSED, + "STATUS_PORT_CONNECTION_REFUSED"}, + {STATUS_INVALID_PORT_HANDLE, -EIO, "STATUS_INVALID_PORT_HANDLE"}, + {STATUS_SHARING_VIOLATION, -EBUSY, "STATUS_SHARING_VIOLATION"}, + {STATUS_QUOTA_EXCEEDED, -EDQUOT, "STATUS_QUOTA_EXCEEDED"}, + {STATUS_INVALID_PAGE_PROTECTION, -EIO, + "STATUS_INVALID_PAGE_PROTECTION"}, + {STATUS_MUTANT_NOT_OWNED, -EIO, "STATUS_MUTANT_NOT_OWNED"}, + {STATUS_SEMAPHORE_LIMIT_EXCEEDED, -EIO, + "STATUS_SEMAPHORE_LIMIT_EXCEEDED"}, + {STATUS_PORT_ALREADY_SET, -EIO, "STATUS_PORT_ALREADY_SET"}, + {STATUS_SECTION_NOT_IMAGE, -EIO, "STATUS_SECTION_NOT_IMAGE"}, + {STATUS_SUSPEND_COUNT_EXCEEDED, -EIO, "STATUS_SUSPEND_COUNT_EXCEEDED"}, + {STATUS_THREAD_IS_TERMINATING, -EIO, "STATUS_THREAD_IS_TERMINATING"}, + {STATUS_BAD_WORKING_SET_LIMIT, -EIO, "STATUS_BAD_WORKING_SET_LIMIT"}, + {STATUS_INCOMPATIBLE_FILE_MAP, -EIO, "STATUS_INCOMPATIBLE_FILE_MAP"}, + {STATUS_SECTION_PROTECTION, -EIO, "STATUS_SECTION_PROTECTION"}, + {STATUS_EAS_NOT_SUPPORTED, -EOPNOTSUPP, "STATUS_EAS_NOT_SUPPORTED"}, + {STATUS_EA_TOO_LARGE, -EIO, "STATUS_EA_TOO_LARGE"}, + {STATUS_NONEXISTENT_EA_ENTRY, -EIO, "STATUS_NONEXISTENT_EA_ENTRY"}, + {STATUS_NO_EAS_ON_FILE, -ENODATA, "STATUS_NO_EAS_ON_FILE"}, + {STATUS_EA_CORRUPT_ERROR, -EIO, "STATUS_EA_CORRUPT_ERROR"}, + {STATUS_FILE_LOCK_CONFLICT, -EACCES, "STATUS_FILE_LOCK_CONFLICT"}, + {STATUS_LOCK_NOT_GRANTED, -EACCES, "STATUS_LOCK_NOT_GRANTED"}, + {STATUS_DELETE_PENDING, -ENOENT, "STATUS_DELETE_PENDING"}, + {STATUS_CTL_FILE_NOT_SUPPORTED, -ENOSYS, + "STATUS_CTL_FILE_NOT_SUPPORTED"}, + {STATUS_UNKNOWN_REVISION, -EIO, "STATUS_UNKNOWN_REVISION"}, + {STATUS_REVISION_MISMATCH, -EIO, "STATUS_REVISION_MISMATCH"}, + {STATUS_INVALID_OWNER, -EIO, "STATUS_INVALID_OWNER"}, + {STATUS_INVALID_PRIMARY_GROUP, -EIO, "STATUS_INVALID_PRIMARY_GROUP"}, + {STATUS_NO_IMPERSONATION_TOKEN, -EIO, "STATUS_NO_IMPERSONATION_TOKEN"}, + {STATUS_CANT_DISABLE_MANDATORY, -EIO, "STATUS_CANT_DISABLE_MANDATORY"}, + {STATUS_NO_LOGON_SERVERS, -EIO, "STATUS_NO_LOGON_SERVERS"}, + {STATUS_NO_SUCH_LOGON_SESSION, -EIO, "STATUS_NO_SUCH_LOGON_SESSION"}, + {STATUS_NO_SUCH_PRIVILEGE, -EIO, "STATUS_NO_SUCH_PRIVILEGE"}, + {STATUS_PRIVILEGE_NOT_HELD, -EIO, "STATUS_PRIVILEGE_NOT_HELD"}, + {STATUS_INVALID_ACCOUNT_NAME, -EIO, "STATUS_INVALID_ACCOUNT_NAME"}, + {STATUS_USER_EXISTS, -EIO, "STATUS_USER_EXISTS"}, + {STATUS_NO_SUCH_USER, -EIO, "STATUS_NO_SUCH_USER"}, + {STATUS_GROUP_EXISTS, -EIO, "STATUS_GROUP_EXISTS"}, + {STATUS_NO_SUCH_GROUP, -EIO, "STATUS_NO_SUCH_GROUP"}, + {STATUS_MEMBER_IN_GROUP, -EIO, "STATUS_MEMBER_IN_GROUP"}, + {STATUS_MEMBER_NOT_IN_GROUP, -EIO, "STATUS_MEMBER_NOT_IN_GROUP"}, + {STATUS_LAST_ADMIN, -EIO, "STATUS_LAST_ADMIN"}, + {STATUS_WRONG_PASSWORD, -EACCES, "STATUS_WRONG_PASSWORD"}, + {STATUS_ILL_FORMED_PASSWORD, -EINVAL, "STATUS_ILL_FORMED_PASSWORD"}, + {STATUS_PASSWORD_RESTRICTION, -EACCES, "STATUS_PASSWORD_RESTRICTION"}, + {STATUS_LOGON_FAILURE, -EACCES, "STATUS_LOGON_FAILURE"}, + {STATUS_ACCOUNT_RESTRICTION, -EACCES, "STATUS_ACCOUNT_RESTRICTION"}, + {STATUS_INVALID_LOGON_HOURS, -EACCES, "STATUS_INVALID_LOGON_HOURS"}, + {STATUS_INVALID_WORKSTATION, -EACCES, "STATUS_INVALID_WORKSTATION"}, + {STATUS_PASSWORD_EXPIRED, -EKEYEXPIRED, "STATUS_PASSWORD_EXPIRED"}, + {STATUS_ACCOUNT_DISABLED, -EKEYREVOKED, "STATUS_ACCOUNT_DISABLED"}, + {STATUS_NONE_MAPPED, -EIO, "STATUS_NONE_MAPPED"}, + {STATUS_TOO_MANY_LUIDS_REQUESTED, -EIO, + "STATUS_TOO_MANY_LUIDS_REQUESTED"}, + {STATUS_LUIDS_EXHAUSTED, -EIO, "STATUS_LUIDS_EXHAUSTED"}, + {STATUS_INVALID_SUB_AUTHORITY, -EIO, "STATUS_INVALID_SUB_AUTHORITY"}, + {STATUS_INVALID_ACL, -EIO, "STATUS_INVALID_ACL"}, + {STATUS_INVALID_SID, -EIO, "STATUS_INVALID_SID"}, + {STATUS_INVALID_SECURITY_DESCR, -EIO, "STATUS_INVALID_SECURITY_DESCR"}, + {STATUS_PROCEDURE_NOT_FOUND, -EIO, "STATUS_PROCEDURE_NOT_FOUND"}, + {STATUS_INVALID_IMAGE_FORMAT, -EIO, "STATUS_INVALID_IMAGE_FORMAT"}, + {STATUS_NO_TOKEN, -EIO, "STATUS_NO_TOKEN"}, + {STATUS_BAD_INHERITANCE_ACL, -EIO, "STATUS_BAD_INHERITANCE_ACL"}, + {STATUS_RANGE_NOT_LOCKED, -EIO, "STATUS_RANGE_NOT_LOCKED"}, + {STATUS_DISK_FULL, -ENOSPC, "STATUS_DISK_FULL"}, + {STATUS_SERVER_DISABLED, -EIO, "STATUS_SERVER_DISABLED"}, + {STATUS_SERVER_NOT_DISABLED, -EIO, "STATUS_SERVER_NOT_DISABLED"}, + {STATUS_TOO_MANY_GUIDS_REQUESTED, -EIO, + "STATUS_TOO_MANY_GUIDS_REQUESTED"}, + {STATUS_GUIDS_EXHAUSTED, -EIO, "STATUS_GUIDS_EXHAUSTED"}, + {STATUS_INVALID_ID_AUTHORITY, -EIO, "STATUS_INVALID_ID_AUTHORITY"}, + {STATUS_AGENTS_EXHAUSTED, -EIO, "STATUS_AGENTS_EXHAUSTED"}, + {STATUS_INVALID_VOLUME_LABEL, -EIO, "STATUS_INVALID_VOLUME_LABEL"}, + {STATUS_SECTION_NOT_EXTENDED, -EIO, "STATUS_SECTION_NOT_EXTENDED"}, + {STATUS_NOT_MAPPED_DATA, -EIO, "STATUS_NOT_MAPPED_DATA"}, + {STATUS_RESOURCE_DATA_NOT_FOUND, -EIO, + "STATUS_RESOURCE_DATA_NOT_FOUND"}, + {STATUS_RESOURCE_TYPE_NOT_FOUND, -EIO, + "STATUS_RESOURCE_TYPE_NOT_FOUND"}, + {STATUS_RESOURCE_NAME_NOT_FOUND, -EIO, + "STATUS_RESOURCE_NAME_NOT_FOUND"}, + {STATUS_ARRAY_BOUNDS_EXCEEDED, -EIO, "STATUS_ARRAY_BOUNDS_EXCEEDED"}, + {STATUS_FLOAT_DENORMAL_OPERAND, -EIO, "STATUS_FLOAT_DENORMAL_OPERAND"}, + {STATUS_FLOAT_DIVIDE_BY_ZERO, -EIO, "STATUS_FLOAT_DIVIDE_BY_ZERO"}, + {STATUS_FLOAT_INEXACT_RESULT, -EIO, "STATUS_FLOAT_INEXACT_RESULT"}, + {STATUS_FLOAT_INVALID_OPERATION, -EIO, + "STATUS_FLOAT_INVALID_OPERATION"}, + {STATUS_FLOAT_OVERFLOW, -EIO, "STATUS_FLOAT_OVERFLOW"}, + {STATUS_FLOAT_STACK_CHECK, -EIO, "STATUS_FLOAT_STACK_CHECK"}, + {STATUS_FLOAT_UNDERFLOW, -EIO, "STATUS_FLOAT_UNDERFLOW"}, + {STATUS_INTEGER_DIVIDE_BY_ZERO, -EIO, "STATUS_INTEGER_DIVIDE_BY_ZERO"}, + {STATUS_INTEGER_OVERFLOW, -EIO, "STATUS_INTEGER_OVERFLOW"}, + {STATUS_PRIVILEGED_INSTRUCTION, -EIO, "STATUS_PRIVILEGED_INSTRUCTION"}, + {STATUS_TOO_MANY_PAGING_FILES, -EIO, "STATUS_TOO_MANY_PAGING_FILES"}, + {STATUS_FILE_INVALID, -EIO, "STATUS_FILE_INVALID"}, + {STATUS_ALLOTTED_SPACE_EXCEEDED, -EIO, + "STATUS_ALLOTTED_SPACE_EXCEEDED"}, + {STATUS_INSUFFICIENT_RESOURCES, -EAGAIN, + "STATUS_INSUFFICIENT_RESOURCES"}, + {STATUS_DFS_EXIT_PATH_FOUND, -EIO, "STATUS_DFS_EXIT_PATH_FOUND"}, + {STATUS_DEVICE_DATA_ERROR, -EIO, "STATUS_DEVICE_DATA_ERROR"}, + {STATUS_DEVICE_NOT_CONNECTED, -EIO, "STATUS_DEVICE_NOT_CONNECTED"}, + {STATUS_DEVICE_POWER_FAILURE, -EIO, "STATUS_DEVICE_POWER_FAILURE"}, + {STATUS_FREE_VM_NOT_AT_BASE, -EIO, "STATUS_FREE_VM_NOT_AT_BASE"}, + {STATUS_MEMORY_NOT_ALLOCATED, -EFAULT, "STATUS_MEMORY_NOT_ALLOCATED"}, + {STATUS_WORKING_SET_QUOTA, -EIO, "STATUS_WORKING_SET_QUOTA"}, + {STATUS_MEDIA_WRITE_PROTECTED, -EROFS, "STATUS_MEDIA_WRITE_PROTECTED"}, + {STATUS_DEVICE_NOT_READY, -EIO, "STATUS_DEVICE_NOT_READY"}, + {STATUS_INVALID_GROUP_ATTRIBUTES, -EIO, + "STATUS_INVALID_GROUP_ATTRIBUTES"}, + {STATUS_BAD_IMPERSONATION_LEVEL, -EIO, + "STATUS_BAD_IMPERSONATION_LEVEL"}, + {STATUS_CANT_OPEN_ANONYMOUS, -EIO, "STATUS_CANT_OPEN_ANONYMOUS"}, + {STATUS_BAD_VALIDATION_CLASS, -EIO, "STATUS_BAD_VALIDATION_CLASS"}, + {STATUS_BAD_TOKEN_TYPE, -EIO, "STATUS_BAD_TOKEN_TYPE"}, + {STATUS_BAD_MASTER_BOOT_RECORD, -EIO, "STATUS_BAD_MASTER_BOOT_RECORD"}, + {STATUS_INSTRUCTION_MISALIGNMENT, -EIO, + "STATUS_INSTRUCTION_MISALIGNMENT"}, + {STATUS_INSTANCE_NOT_AVAILABLE, -EIO, "STATUS_INSTANCE_NOT_AVAILABLE"}, + {STATUS_PIPE_NOT_AVAILABLE, -EIO, "STATUS_PIPE_NOT_AVAILABLE"}, + {STATUS_INVALID_PIPE_STATE, -EIO, "STATUS_INVALID_PIPE_STATE"}, + {STATUS_PIPE_BUSY, -EBUSY, "STATUS_PIPE_BUSY"}, + {STATUS_ILLEGAL_FUNCTION, -EIO, "STATUS_ILLEGAL_FUNCTION"}, + {STATUS_PIPE_DISCONNECTED, -EPIPE, "STATUS_PIPE_DISCONNECTED"}, + {STATUS_PIPE_CLOSING, -EIO, "STATUS_PIPE_CLOSING"}, + {STATUS_PIPE_CONNECTED, -EIO, "STATUS_PIPE_CONNECTED"}, + {STATUS_PIPE_LISTENING, -EIO, "STATUS_PIPE_LISTENING"}, + {STATUS_INVALID_READ_MODE, -EIO, "STATUS_INVALID_READ_MODE"}, + {STATUS_IO_TIMEOUT, -EAGAIN, "STATUS_IO_TIMEOUT"}, + {STATUS_FILE_FORCED_CLOSED, -EIO, "STATUS_FILE_FORCED_CLOSED"}, + {STATUS_PROFILING_NOT_STARTED, -EIO, "STATUS_PROFILING_NOT_STARTED"}, + {STATUS_PROFILING_NOT_STOPPED, -EIO, "STATUS_PROFILING_NOT_STOPPED"}, + {STATUS_COULD_NOT_INTERPRET, -EIO, "STATUS_COULD_NOT_INTERPRET"}, + {STATUS_FILE_IS_A_DIRECTORY, -EISDIR, "STATUS_FILE_IS_A_DIRECTORY"}, + {STATUS_NOT_SUPPORTED, -EOPNOTSUPP, "STATUS_NOT_SUPPORTED"}, + {STATUS_REMOTE_NOT_LISTENING, -EHOSTDOWN, + "STATUS_REMOTE_NOT_LISTENING"}, + {STATUS_DUPLICATE_NAME, -ENOTUNIQ, "STATUS_DUPLICATE_NAME"}, + {STATUS_BAD_NETWORK_PATH, -EINVAL, "STATUS_BAD_NETWORK_PATH"}, + {STATUS_NETWORK_BUSY, -EBUSY, "STATUS_NETWORK_BUSY"}, + {STATUS_DEVICE_DOES_NOT_EXIST, -ENODEV, "STATUS_DEVICE_DOES_NOT_EXIST"}, + {STATUS_TOO_MANY_COMMANDS, -EIO, "STATUS_TOO_MANY_COMMANDS"}, + {STATUS_ADAPTER_HARDWARE_ERROR, -EIO, "STATUS_ADAPTER_HARDWARE_ERROR"}, + {STATUS_INVALID_NETWORK_RESPONSE, -EIO, + "STATUS_INVALID_NETWORK_RESPONSE"}, + {STATUS_UNEXPECTED_NETWORK_ERROR, -EIO, + "STATUS_UNEXPECTED_NETWORK_ERROR"}, + {STATUS_BAD_REMOTE_ADAPTER, -EIO, "STATUS_BAD_REMOTE_ADAPTER"}, + {STATUS_PRINT_QUEUE_FULL, -EIO, "STATUS_PRINT_QUEUE_FULL"}, + {STATUS_NO_SPOOL_SPACE, -EIO, "STATUS_NO_SPOOL_SPACE"}, + {STATUS_PRINT_CANCELLED, -EIO, "STATUS_PRINT_CANCELLED"}, + {STATUS_NETWORK_NAME_DELETED, -EREMCHG, "STATUS_NETWORK_NAME_DELETED"}, + {STATUS_NETWORK_ACCESS_DENIED, -EACCES, "STATUS_NETWORK_ACCESS_DENIED"}, + {STATUS_BAD_DEVICE_TYPE, -EIO, "STATUS_BAD_DEVICE_TYPE"}, + {STATUS_BAD_NETWORK_NAME, -ENOENT, "STATUS_BAD_NETWORK_NAME"}, + {STATUS_TOO_MANY_NAMES, -EIO, "STATUS_TOO_MANY_NAMES"}, + {STATUS_TOO_MANY_SESSIONS, -EIO, "STATUS_TOO_MANY_SESSIONS"}, + {STATUS_SHARING_PAUSED, -EIO, "STATUS_SHARING_PAUSED"}, + {STATUS_REQUEST_NOT_ACCEPTED, -EIO, "STATUS_REQUEST_NOT_ACCEPTED"}, + {STATUS_REDIRECTOR_PAUSED, -EIO, "STATUS_REDIRECTOR_PAUSED"}, + {STATUS_NET_WRITE_FAULT, -EIO, "STATUS_NET_WRITE_FAULT"}, + {STATUS_PROFILING_AT_LIMIT, -EIO, "STATUS_PROFILING_AT_LIMIT"}, + {STATUS_NOT_SAME_DEVICE, -EXDEV, "STATUS_NOT_SAME_DEVICE"}, + {STATUS_FILE_RENAMED, -EIO, "STATUS_FILE_RENAMED"}, + {STATUS_VIRTUAL_CIRCUIT_CLOSED, -EIO, "STATUS_VIRTUAL_CIRCUIT_CLOSED"}, + {STATUS_NO_SECURITY_ON_OBJECT, -EIO, "STATUS_NO_SECURITY_ON_OBJECT"}, + {STATUS_CANT_WAIT, -EIO, "STATUS_CANT_WAIT"}, + {STATUS_PIPE_EMPTY, -EIO, "STATUS_PIPE_EMPTY"}, + {STATUS_CANT_ACCESS_DOMAIN_INFO, -EIO, + "STATUS_CANT_ACCESS_DOMAIN_INFO"}, + {STATUS_CANT_TERMINATE_SELF, -EIO, "STATUS_CANT_TERMINATE_SELF"}, + {STATUS_INVALID_SERVER_STATE, -EIO, "STATUS_INVALID_SERVER_STATE"}, + {STATUS_INVALID_DOMAIN_STATE, -EIO, "STATUS_INVALID_DOMAIN_STATE"}, + {STATUS_INVALID_DOMAIN_ROLE, -EIO, "STATUS_INVALID_DOMAIN_ROLE"}, + {STATUS_NO_SUCH_DOMAIN, -EIO, "STATUS_NO_SUCH_DOMAIN"}, + {STATUS_DOMAIN_EXISTS, -EIO, "STATUS_DOMAIN_EXISTS"}, + {STATUS_DOMAIN_LIMIT_EXCEEDED, -EIO, "STATUS_DOMAIN_LIMIT_EXCEEDED"}, + {STATUS_OPLOCK_NOT_GRANTED, -EIO, "STATUS_OPLOCK_NOT_GRANTED"}, + {STATUS_INVALID_OPLOCK_PROTOCOL, -EIO, + "STATUS_INVALID_OPLOCK_PROTOCOL"}, + {STATUS_INTERNAL_DB_CORRUPTION, -EIO, "STATUS_INTERNAL_DB_CORRUPTION"}, + {STATUS_INTERNAL_ERROR, -EIO, "STATUS_INTERNAL_ERROR"}, + {STATUS_GENERIC_NOT_MAPPED, -EIO, "STATUS_GENERIC_NOT_MAPPED"}, + {STATUS_BAD_DESCRIPTOR_FORMAT, -EIO, "STATUS_BAD_DESCRIPTOR_FORMAT"}, + {STATUS_INVALID_USER_BUFFER, -EIO, "STATUS_INVALID_USER_BUFFER"}, + {STATUS_UNEXPECTED_IO_ERROR, -EIO, "STATUS_UNEXPECTED_IO_ERROR"}, + {STATUS_UNEXPECTED_MM_CREATE_ERR, -EIO, + "STATUS_UNEXPECTED_MM_CREATE_ERR"}, + {STATUS_UNEXPECTED_MM_MAP_ERROR, -EIO, + "STATUS_UNEXPECTED_MM_MAP_ERROR"}, + {STATUS_UNEXPECTED_MM_EXTEND_ERR, -EIO, + "STATUS_UNEXPECTED_MM_EXTEND_ERR"}, + {STATUS_NOT_LOGON_PROCESS, -EIO, "STATUS_NOT_LOGON_PROCESS"}, + {STATUS_LOGON_SESSION_EXISTS, -EIO, "STATUS_LOGON_SESSION_EXISTS"}, + {STATUS_INVALID_PARAMETER_1, -EINVAL, "STATUS_INVALID_PARAMETER_1"}, + {STATUS_INVALID_PARAMETER_2, -EINVAL, "STATUS_INVALID_PARAMETER_2"}, + {STATUS_INVALID_PARAMETER_3, -EINVAL, "STATUS_INVALID_PARAMETER_3"}, + {STATUS_INVALID_PARAMETER_4, -EINVAL, "STATUS_INVALID_PARAMETER_4"}, + {STATUS_INVALID_PARAMETER_5, -EINVAL, "STATUS_INVALID_PARAMETER_5"}, + {STATUS_INVALID_PARAMETER_6, -EINVAL, "STATUS_INVALID_PARAMETER_6"}, + {STATUS_INVALID_PARAMETER_7, -EINVAL, "STATUS_INVALID_PARAMETER_7"}, + {STATUS_INVALID_PARAMETER_8, -EINVAL, "STATUS_INVALID_PARAMETER_8"}, + {STATUS_INVALID_PARAMETER_9, -EINVAL, "STATUS_INVALID_PARAMETER_9"}, + {STATUS_INVALID_PARAMETER_10, -EINVAL, "STATUS_INVALID_PARAMETER_10"}, + {STATUS_INVALID_PARAMETER_11, -EINVAL, "STATUS_INVALID_PARAMETER_11"}, + {STATUS_INVALID_PARAMETER_12, -EINVAL, "STATUS_INVALID_PARAMETER_12"}, + {STATUS_REDIRECTOR_NOT_STARTED, -EIO, "STATUS_REDIRECTOR_NOT_STARTED"}, + {STATUS_REDIRECTOR_STARTED, -EIO, "STATUS_REDIRECTOR_STARTED"}, + {STATUS_STACK_OVERFLOW, -EIO, "STATUS_STACK_OVERFLOW"}, + {STATUS_NO_SUCH_PACKAGE, -EIO, "STATUS_NO_SUCH_PACKAGE"}, + {STATUS_BAD_FUNCTION_TABLE, -EIO, "STATUS_BAD_FUNCTION_TABLE"}, + {STATUS_VARIABLE_NOT_FOUND, -EIO, "STATUS_VARIABLE_NOT_FOUND"}, + {STATUS_DIRECTORY_NOT_EMPTY, -ENOTEMPTY, "STATUS_DIRECTORY_NOT_EMPTY"}, + {STATUS_FILE_CORRUPT_ERROR, -EIO, "STATUS_FILE_CORRUPT_ERROR"}, + {STATUS_NOT_A_DIRECTORY, -ENOTDIR, "STATUS_NOT_A_DIRECTORY"}, + {STATUS_BAD_LOGON_SESSION_STATE, -EIO, + "STATUS_BAD_LOGON_SESSION_STATE"}, + {STATUS_LOGON_SESSION_COLLISION, -EIO, + "STATUS_LOGON_SESSION_COLLISION"}, + {STATUS_NAME_TOO_LONG, -ENAMETOOLONG, "STATUS_NAME_TOO_LONG"}, + {STATUS_FILES_OPEN, -EIO, "STATUS_FILES_OPEN"}, + {STATUS_CONNECTION_IN_USE, -EIO, "STATUS_CONNECTION_IN_USE"}, + {STATUS_MESSAGE_NOT_FOUND, -EIO, "STATUS_MESSAGE_NOT_FOUND"}, + {STATUS_PROCESS_IS_TERMINATING, -EIO, "STATUS_PROCESS_IS_TERMINATING"}, + {STATUS_INVALID_LOGON_TYPE, -EIO, "STATUS_INVALID_LOGON_TYPE"}, + {STATUS_NO_GUID_TRANSLATION, -EIO, "STATUS_NO_GUID_TRANSLATION"}, + {STATUS_CANNOT_IMPERSONATE, -EIO, "STATUS_CANNOT_IMPERSONATE"}, + {STATUS_IMAGE_ALREADY_LOADED, -EIO, "STATUS_IMAGE_ALREADY_LOADED"}, + {STATUS_ABIOS_NOT_PRESENT, -EIO, "STATUS_ABIOS_NOT_PRESENT"}, + {STATUS_ABIOS_LID_NOT_EXIST, -EIO, "STATUS_ABIOS_LID_NOT_EXIST"}, + {STATUS_ABIOS_LID_ALREADY_OWNED, -EIO, + "STATUS_ABIOS_LID_ALREADY_OWNED"}, + {STATUS_ABIOS_NOT_LID_OWNER, -EIO, "STATUS_ABIOS_NOT_LID_OWNER"}, + {STATUS_ABIOS_INVALID_COMMAND, -EIO, "STATUS_ABIOS_INVALID_COMMAND"}, + {STATUS_ABIOS_INVALID_LID, -EIO, "STATUS_ABIOS_INVALID_LID"}, + {STATUS_ABIOS_SELECTOR_NOT_AVAILABLE, -EIO, + "STATUS_ABIOS_SELECTOR_NOT_AVAILABLE"}, + {STATUS_ABIOS_INVALID_SELECTOR, -EIO, "STATUS_ABIOS_INVALID_SELECTOR"}, + {STATUS_NO_LDT, -EIO, "STATUS_NO_LDT"}, + {STATUS_INVALID_LDT_SIZE, -EIO, "STATUS_INVALID_LDT_SIZE"}, + {STATUS_INVALID_LDT_OFFSET, -EIO, "STATUS_INVALID_LDT_OFFSET"}, + {STATUS_INVALID_LDT_DESCRIPTOR, -EIO, "STATUS_INVALID_LDT_DESCRIPTOR"}, + {STATUS_INVALID_IMAGE_NE_FORMAT, -EIO, + "STATUS_INVALID_IMAGE_NE_FORMAT"}, + {STATUS_RXACT_INVALID_STATE, -EIO, "STATUS_RXACT_INVALID_STATE"}, + {STATUS_RXACT_COMMIT_FAILURE, -EIO, "STATUS_RXACT_COMMIT_FAILURE"}, + {STATUS_MAPPED_FILE_SIZE_ZERO, -EIO, "STATUS_MAPPED_FILE_SIZE_ZERO"}, + {STATUS_TOO_MANY_OPENED_FILES, -EMFILE, "STATUS_TOO_MANY_OPENED_FILES"}, + {STATUS_CANCELLED, -EIO, "STATUS_CANCELLED"}, + {STATUS_CANNOT_DELETE, -EACCES, "STATUS_CANNOT_DELETE"}, + {STATUS_INVALID_COMPUTER_NAME, -EIO, "STATUS_INVALID_COMPUTER_NAME"}, + {STATUS_FILE_DELETED, -EIO, "STATUS_FILE_DELETED"}, + {STATUS_SPECIAL_ACCOUNT, -EIO, "STATUS_SPECIAL_ACCOUNT"}, + {STATUS_SPECIAL_GROUP, -EIO, "STATUS_SPECIAL_GROUP"}, + {STATUS_SPECIAL_USER, -EIO, "STATUS_SPECIAL_USER"}, + {STATUS_MEMBERS_PRIMARY_GROUP, -EIO, "STATUS_MEMBERS_PRIMARY_GROUP"}, + {STATUS_FILE_CLOSED, -EBADF, "STATUS_FILE_CLOSED"}, + {STATUS_TOO_MANY_THREADS, -EIO, "STATUS_TOO_MANY_THREADS"}, + {STATUS_THREAD_NOT_IN_PROCESS, -EIO, "STATUS_THREAD_NOT_IN_PROCESS"}, + {STATUS_TOKEN_ALREADY_IN_USE, -EIO, "STATUS_TOKEN_ALREADY_IN_USE"}, + {STATUS_PAGEFILE_QUOTA_EXCEEDED, -EDQUOT, + "STATUS_PAGEFILE_QUOTA_EXCEEDED"}, + {STATUS_COMMITMENT_LIMIT, -EIO, "STATUS_COMMITMENT_LIMIT"}, + {STATUS_INVALID_IMAGE_LE_FORMAT, -EIO, + "STATUS_INVALID_IMAGE_LE_FORMAT"}, + {STATUS_INVALID_IMAGE_NOT_MZ, -EIO, "STATUS_INVALID_IMAGE_NOT_MZ"}, + {STATUS_INVALID_IMAGE_PROTECT, -EIO, "STATUS_INVALID_IMAGE_PROTECT"}, + {STATUS_INVALID_IMAGE_WIN_16, -EIO, "STATUS_INVALID_IMAGE_WIN_16"}, + {STATUS_LOGON_SERVER_CONFLICT, -EIO, "STATUS_LOGON_SERVER_CONFLICT"}, + {STATUS_TIME_DIFFERENCE_AT_DC, -EIO, "STATUS_TIME_DIFFERENCE_AT_DC"}, + {STATUS_SYNCHRONIZATION_REQUIRED, -EIO, + "STATUS_SYNCHRONIZATION_REQUIRED"}, + {STATUS_DLL_NOT_FOUND, -ENOENT, "STATUS_DLL_NOT_FOUND"}, + {STATUS_OPEN_FAILED, -EIO, "STATUS_OPEN_FAILED"}, + {STATUS_IO_PRIVILEGE_FAILED, -EIO, "STATUS_IO_PRIVILEGE_FAILED"}, + {STATUS_ORDINAL_NOT_FOUND, -EIO, "STATUS_ORDINAL_NOT_FOUND"}, + {STATUS_ENTRYPOINT_NOT_FOUND, -EIO, "STATUS_ENTRYPOINT_NOT_FOUND"}, + {STATUS_CONTROL_C_EXIT, -EIO, "STATUS_CONTROL_C_EXIT"}, + {STATUS_LOCAL_DISCONNECT, -EIO, "STATUS_LOCAL_DISCONNECT"}, + {STATUS_REMOTE_DISCONNECT, -ESHUTDOWN, "STATUS_REMOTE_DISCONNECT"}, + {STATUS_REMOTE_RESOURCES, -EIO, "STATUS_REMOTE_RESOURCES"}, + {STATUS_LINK_FAILED, -EXDEV, "STATUS_LINK_FAILED"}, + {STATUS_LINK_TIMEOUT, -ETIMEDOUT, "STATUS_LINK_TIMEOUT"}, + {STATUS_INVALID_CONNECTION, -EIO, "STATUS_INVALID_CONNECTION"}, + {STATUS_INVALID_ADDRESS, -EIO, "STATUS_INVALID_ADDRESS"}, + {STATUS_DLL_INIT_FAILED, -EIO, "STATUS_DLL_INIT_FAILED"}, + {STATUS_MISSING_SYSTEMFILE, -EIO, "STATUS_MISSING_SYSTEMFILE"}, + {STATUS_UNHANDLED_EXCEPTION, -EIO, "STATUS_UNHANDLED_EXCEPTION"}, + {STATUS_APP_INIT_FAILURE, -EIO, "STATUS_APP_INIT_FAILURE"}, + {STATUS_PAGEFILE_CREATE_FAILED, -EIO, "STATUS_PAGEFILE_CREATE_FAILED"}, + {STATUS_NO_PAGEFILE, -EIO, "STATUS_NO_PAGEFILE"}, + {STATUS_INVALID_LEVEL, -EIO, "STATUS_INVALID_LEVEL"}, + {STATUS_WRONG_PASSWORD_CORE, -EIO, "STATUS_WRONG_PASSWORD_CORE"}, + {STATUS_ILLEGAL_FLOAT_CONTEXT, -EIO, "STATUS_ILLEGAL_FLOAT_CONTEXT"}, + {STATUS_PIPE_BROKEN, -EPIPE, "STATUS_PIPE_BROKEN"}, + {STATUS_REGISTRY_CORRUPT, -EIO, "STATUS_REGISTRY_CORRUPT"}, + {STATUS_REGISTRY_IO_FAILED, -EIO, "STATUS_REGISTRY_IO_FAILED"}, + {STATUS_NO_EVENT_PAIR, -EIO, "STATUS_NO_EVENT_PAIR"}, + {STATUS_UNRECOGNIZED_VOLUME, -EIO, "STATUS_UNRECOGNIZED_VOLUME"}, + {STATUS_SERIAL_NO_DEVICE_INITED, -EIO, + "STATUS_SERIAL_NO_DEVICE_INITED"}, + {STATUS_NO_SUCH_ALIAS, -EIO, "STATUS_NO_SUCH_ALIAS"}, + {STATUS_MEMBER_NOT_IN_ALIAS, -EIO, "STATUS_MEMBER_NOT_IN_ALIAS"}, + {STATUS_MEMBER_IN_ALIAS, -EIO, "STATUS_MEMBER_IN_ALIAS"}, + {STATUS_ALIAS_EXISTS, -EIO, "STATUS_ALIAS_EXISTS"}, + {STATUS_LOGON_NOT_GRANTED, -EIO, "STATUS_LOGON_NOT_GRANTED"}, + {STATUS_TOO_MANY_SECRETS, -EIO, "STATUS_TOO_MANY_SECRETS"}, + {STATUS_SECRET_TOO_LONG, -EIO, "STATUS_SECRET_TOO_LONG"}, + {STATUS_INTERNAL_DB_ERROR, -EIO, "STATUS_INTERNAL_DB_ERROR"}, + {STATUS_FULLSCREEN_MODE, -EIO, "STATUS_FULLSCREEN_MODE"}, + {STATUS_TOO_MANY_CONTEXT_IDS, -EIO, "STATUS_TOO_MANY_CONTEXT_IDS"}, + {STATUS_LOGON_TYPE_NOT_GRANTED, -EIO, "STATUS_LOGON_TYPE_NOT_GRANTED"}, + {STATUS_NOT_REGISTRY_FILE, -EIO, "STATUS_NOT_REGISTRY_FILE"}, + {STATUS_NT_CROSS_ENCRYPTION_REQUIRED, -EIO, + "STATUS_NT_CROSS_ENCRYPTION_REQUIRED"}, + {STATUS_DOMAIN_CTRLR_CONFIG_ERROR, -EIO, + "STATUS_DOMAIN_CTRLR_CONFIG_ERROR"}, + {STATUS_FT_MISSING_MEMBER, -EIO, "STATUS_FT_MISSING_MEMBER"}, + {STATUS_ILL_FORMED_SERVICE_ENTRY, -EIO, + "STATUS_ILL_FORMED_SERVICE_ENTRY"}, + {STATUS_ILLEGAL_CHARACTER, -EIO, "STATUS_ILLEGAL_CHARACTER"}, + {STATUS_UNMAPPABLE_CHARACTER, -EIO, "STATUS_UNMAPPABLE_CHARACTER"}, + {STATUS_UNDEFINED_CHARACTER, -EIO, "STATUS_UNDEFINED_CHARACTER"}, + {STATUS_FLOPPY_VOLUME, -EIO, "STATUS_FLOPPY_VOLUME"}, + {STATUS_FLOPPY_ID_MARK_NOT_FOUND, -EIO, + "STATUS_FLOPPY_ID_MARK_NOT_FOUND"}, + {STATUS_FLOPPY_WRONG_CYLINDER, -EIO, "STATUS_FLOPPY_WRONG_CYLINDER"}, + {STATUS_FLOPPY_UNKNOWN_ERROR, -EIO, "STATUS_FLOPPY_UNKNOWN_ERROR"}, + {STATUS_FLOPPY_BAD_REGISTERS, -EIO, "STATUS_FLOPPY_BAD_REGISTERS"}, + {STATUS_DISK_RECALIBRATE_FAILED, -EIO, + "STATUS_DISK_RECALIBRATE_FAILED"}, + {STATUS_DISK_OPERATION_FAILED, -EIO, "STATUS_DISK_OPERATION_FAILED"}, + {STATUS_DISK_RESET_FAILED, -EIO, "STATUS_DISK_RESET_FAILED"}, + {STATUS_SHARED_IRQ_BUSY, -EBUSY, "STATUS_SHARED_IRQ_BUSY"}, + {STATUS_FT_ORPHANING, -EIO, "STATUS_FT_ORPHANING"}, + {STATUS_BIOS_FAILED_TO_CONNECT_INTERRUPT, -EIO, + "STATUS_BIOS_FAILED_TO_CONNECT_INTERRUPT"}, + {STATUS_PARTITION_FAILURE, -EIO, "STATUS_PARTITION_FAILURE"}, + {STATUS_INVALID_BLOCK_LENGTH, -EIO, "STATUS_INVALID_BLOCK_LENGTH"}, + {STATUS_DEVICE_NOT_PARTITIONED, -EIO, "STATUS_DEVICE_NOT_PARTITIONED"}, + {STATUS_UNABLE_TO_LOCK_MEDIA, -EIO, "STATUS_UNABLE_TO_LOCK_MEDIA"}, + {STATUS_UNABLE_TO_UNLOAD_MEDIA, -EIO, "STATUS_UNABLE_TO_UNLOAD_MEDIA"}, + {STATUS_EOM_OVERFLOW, -EIO, "STATUS_EOM_OVERFLOW"}, + {STATUS_NO_MEDIA, -EIO, "STATUS_NO_MEDIA"}, + {STATUS_NO_SUCH_MEMBER, -EIO, "STATUS_NO_SUCH_MEMBER"}, + {STATUS_INVALID_MEMBER, -EIO, "STATUS_INVALID_MEMBER"}, + {STATUS_KEY_DELETED, -EIO, "STATUS_KEY_DELETED"}, + {STATUS_NO_LOG_SPACE, -EIO, "STATUS_NO_LOG_SPACE"}, + {STATUS_TOO_MANY_SIDS, -EIO, "STATUS_TOO_MANY_SIDS"}, + {STATUS_LM_CROSS_ENCRYPTION_REQUIRED, -EIO, + "STATUS_LM_CROSS_ENCRYPTION_REQUIRED"}, + {STATUS_KEY_HAS_CHILDREN, -EIO, "STATUS_KEY_HAS_CHILDREN"}, + {STATUS_CHILD_MUST_BE_VOLATILE, -EIO, "STATUS_CHILD_MUST_BE_VOLATILE"}, + {STATUS_DEVICE_CONFIGURATION_ERROR, -EIO, + "STATUS_DEVICE_CONFIGURATION_ERROR"}, + {STATUS_DRIVER_INTERNAL_ERROR, -EIO, "STATUS_DRIVER_INTERNAL_ERROR"}, + {STATUS_INVALID_DEVICE_STATE, -EIO, "STATUS_INVALID_DEVICE_STATE"}, + {STATUS_IO_DEVICE_ERROR, -EIO, "STATUS_IO_DEVICE_ERROR"}, + {STATUS_DEVICE_PROTOCOL_ERROR, -EIO, "STATUS_DEVICE_PROTOCOL_ERROR"}, + {STATUS_BACKUP_CONTROLLER, -EIO, "STATUS_BACKUP_CONTROLLER"}, + {STATUS_LOG_FILE_FULL, -EIO, "STATUS_LOG_FILE_FULL"}, + {STATUS_TOO_LATE, -EIO, "STATUS_TOO_LATE"}, + {STATUS_NO_TRUST_LSA_SECRET, -EIO, "STATUS_NO_TRUST_LSA_SECRET"}, + {STATUS_NO_TRUST_SAM_ACCOUNT, -EIO, "STATUS_NO_TRUST_SAM_ACCOUNT"}, + {STATUS_TRUSTED_DOMAIN_FAILURE, -EIO, "STATUS_TRUSTED_DOMAIN_FAILURE"}, + {STATUS_TRUSTED_RELATIONSHIP_FAILURE, -EIO, + "STATUS_TRUSTED_RELATIONSHIP_FAILURE"}, + {STATUS_EVENTLOG_FILE_CORRUPT, -EIO, "STATUS_EVENTLOG_FILE_CORRUPT"}, + {STATUS_EVENTLOG_CANT_START, -EIO, "STATUS_EVENTLOG_CANT_START"}, + {STATUS_TRUST_FAILURE, -EIO, "STATUS_TRUST_FAILURE"}, + {STATUS_MUTANT_LIMIT_EXCEEDED, -EIO, "STATUS_MUTANT_LIMIT_EXCEEDED"}, + {STATUS_NETLOGON_NOT_STARTED, -EIO, "STATUS_NETLOGON_NOT_STARTED"}, + {STATUS_ACCOUNT_EXPIRED, -EKEYEXPIRED, "STATUS_ACCOUNT_EXPIRED"}, + {STATUS_POSSIBLE_DEADLOCK, -EIO, "STATUS_POSSIBLE_DEADLOCK"}, + {STATUS_NETWORK_CREDENTIAL_CONFLICT, -EIO, + "STATUS_NETWORK_CREDENTIAL_CONFLICT"}, + {STATUS_REMOTE_SESSION_LIMIT, -EIO, "STATUS_REMOTE_SESSION_LIMIT"}, + {STATUS_EVENTLOG_FILE_CHANGED, -EIO, "STATUS_EVENTLOG_FILE_CHANGED"}, + {STATUS_NOLOGON_INTERDOMAIN_TRUST_ACCOUNT, -EIO, + "STATUS_NOLOGON_INTERDOMAIN_TRUST_ACCOUNT"}, + {STATUS_NOLOGON_WORKSTATION_TRUST_ACCOUNT, -EIO, + "STATUS_NOLOGON_WORKSTATION_TRUST_ACCOUNT"}, + {STATUS_NOLOGON_SERVER_TRUST_ACCOUNT, -EIO, + "STATUS_NOLOGON_SERVER_TRUST_ACCOUNT"}, + {STATUS_DOMAIN_TRUST_INCONSISTENT, -EIO, + "STATUS_DOMAIN_TRUST_INCONSISTENT"}, + {STATUS_FS_DRIVER_REQUIRED, -EOPNOTSUPP, "STATUS_FS_DRIVER_REQUIRED"}, + {STATUS_IMAGE_ALREADY_LOADED_AS_DLL, -EIO, + "STATUS_IMAGE_ALREADY_LOADED_AS_DLL"}, + {STATUS_NETWORK_OPEN_RESTRICTION, -EIO, + "STATUS_NETWORK_OPEN_RESTRICTION"}, + {STATUS_NO_USER_SESSION_KEY, -EIO, "STATUS_NO_USER_SESSION_KEY"}, + {STATUS_USER_SESSION_DELETED, -EIO, "STATUS_USER_SESSION_DELETED"}, + {STATUS_RESOURCE_LANG_NOT_FOUND, -EIO, + "STATUS_RESOURCE_LANG_NOT_FOUND"}, + {STATUS_INSUFF_SERVER_RESOURCES, -EIO, + "STATUS_INSUFF_SERVER_RESOURCES"}, + {STATUS_INVALID_BUFFER_SIZE, -EIO, "STATUS_INVALID_BUFFER_SIZE"}, + {STATUS_INVALID_ADDRESS_COMPONENT, -EIO, + "STATUS_INVALID_ADDRESS_COMPONENT"}, + {STATUS_INVALID_ADDRESS_WILDCARD, -EIO, + "STATUS_INVALID_ADDRESS_WILDCARD"}, + {STATUS_TOO_MANY_ADDRESSES, -EIO, "STATUS_TOO_MANY_ADDRESSES"}, + {STATUS_ADDRESS_ALREADY_EXISTS, -EADDRINUSE, + "STATUS_ADDRESS_ALREADY_EXISTS"}, + {STATUS_ADDRESS_CLOSED, -EIO, "STATUS_ADDRESS_CLOSED"}, + {STATUS_CONNECTION_DISCONNECTED, -ECONNABORTED, + "STATUS_CONNECTION_DISCONNECTED"}, + {STATUS_CONNECTION_RESET, -ENETRESET, "STATUS_CONNECTION_RESET"}, + {STATUS_TOO_MANY_NODES, -EIO, "STATUS_TOO_MANY_NODES"}, + {STATUS_TRANSACTION_ABORTED, -EIO, "STATUS_TRANSACTION_ABORTED"}, + {STATUS_TRANSACTION_TIMED_OUT, -EIO, "STATUS_TRANSACTION_TIMED_OUT"}, + {STATUS_TRANSACTION_NO_RELEASE, -EIO, "STATUS_TRANSACTION_NO_RELEASE"}, + {STATUS_TRANSACTION_NO_MATCH, -EIO, "STATUS_TRANSACTION_NO_MATCH"}, + {STATUS_TRANSACTION_RESPONDED, -EIO, "STATUS_TRANSACTION_RESPONDED"}, + {STATUS_TRANSACTION_INVALID_ID, -EIO, "STATUS_TRANSACTION_INVALID_ID"}, + {STATUS_TRANSACTION_INVALID_TYPE, -EIO, + "STATUS_TRANSACTION_INVALID_TYPE"}, + {STATUS_NOT_SERVER_SESSION, -EIO, "STATUS_NOT_SERVER_SESSION"}, + {STATUS_NOT_CLIENT_SESSION, -EIO, "STATUS_NOT_CLIENT_SESSION"}, + {STATUS_CANNOT_LOAD_REGISTRY_FILE, -EIO, + "STATUS_CANNOT_LOAD_REGISTRY_FILE"}, + {STATUS_DEBUG_ATTACH_FAILED, -EIO, "STATUS_DEBUG_ATTACH_FAILED"}, + {STATUS_SYSTEM_PROCESS_TERMINATED, -EIO, + "STATUS_SYSTEM_PROCESS_TERMINATED"}, + {STATUS_DATA_NOT_ACCEPTED, -EIO, "STATUS_DATA_NOT_ACCEPTED"}, + {STATUS_NO_BROWSER_SERVERS_FOUND, -EIO, + "STATUS_NO_BROWSER_SERVERS_FOUND"}, + {STATUS_VDM_HARD_ERROR, -EIO, "STATUS_VDM_HARD_ERROR"}, + {STATUS_DRIVER_CANCEL_TIMEOUT, -EIO, "STATUS_DRIVER_CANCEL_TIMEOUT"}, + {STATUS_REPLY_MESSAGE_MISMATCH, -EIO, "STATUS_REPLY_MESSAGE_MISMATCH"}, + {STATUS_MAPPED_ALIGNMENT, -EIO, "STATUS_MAPPED_ALIGNMENT"}, + {STATUS_IMAGE_CHECKSUM_MISMATCH, -EIO, + "STATUS_IMAGE_CHECKSUM_MISMATCH"}, + {STATUS_LOST_WRITEBEHIND_DATA, -EIO, "STATUS_LOST_WRITEBEHIND_DATA"}, + {STATUS_CLIENT_SERVER_PARAMETERS_INVALID, -EIO, + "STATUS_CLIENT_SERVER_PARAMETERS_INVALID"}, + {STATUS_PASSWORD_MUST_CHANGE, -EIO, "STATUS_PASSWORD_MUST_CHANGE"}, + {STATUS_NOT_FOUND, -ENOENT, "STATUS_NOT_FOUND"}, + {STATUS_NOT_TINY_STREAM, -EIO, "STATUS_NOT_TINY_STREAM"}, + {STATUS_RECOVERY_FAILURE, -EIO, "STATUS_RECOVERY_FAILURE"}, + {STATUS_STACK_OVERFLOW_READ, -EIO, "STATUS_STACK_OVERFLOW_READ"}, + {STATUS_FAIL_CHECK, -EIO, "STATUS_FAIL_CHECK"}, + {STATUS_DUPLICATE_OBJECTID, -EIO, "STATUS_DUPLICATE_OBJECTID"}, + {STATUS_OBJECTID_EXISTS, -EIO, "STATUS_OBJECTID_EXISTS"}, + {STATUS_CONVERT_TO_LARGE, -EIO, "STATUS_CONVERT_TO_LARGE"}, + {STATUS_RETRY, -EAGAIN, "STATUS_RETRY"}, + {STATUS_FOUND_OUT_OF_SCOPE, -EIO, "STATUS_FOUND_OUT_OF_SCOPE"}, + {STATUS_ALLOCATE_BUCKET, -EIO, "STATUS_ALLOCATE_BUCKET"}, + {STATUS_PROPSET_NOT_FOUND, -EIO, "STATUS_PROPSET_NOT_FOUND"}, + {STATUS_MARSHALL_OVERFLOW, -EIO, "STATUS_MARSHALL_OVERFLOW"}, + {STATUS_INVALID_VARIANT, -EIO, "STATUS_INVALID_VARIANT"}, + {STATUS_DOMAIN_CONTROLLER_NOT_FOUND, -EIO, + "STATUS_DOMAIN_CONTROLLER_NOT_FOUND"}, + {STATUS_ACCOUNT_LOCKED_OUT, -EACCES, "STATUS_ACCOUNT_LOCKED_OUT"}, + {STATUS_HANDLE_NOT_CLOSABLE, -EIO, "STATUS_HANDLE_NOT_CLOSABLE"}, + {STATUS_CONNECTION_REFUSED, -EIO, "STATUS_CONNECTION_REFUSED"}, + {STATUS_GRACEFUL_DISCONNECT, -EIO, "STATUS_GRACEFUL_DISCONNECT"}, + {STATUS_ADDRESS_ALREADY_ASSOCIATED, -EIO, + "STATUS_ADDRESS_ALREADY_ASSOCIATED"}, + {STATUS_ADDRESS_NOT_ASSOCIATED, -EIO, "STATUS_ADDRESS_NOT_ASSOCIATED"}, + {STATUS_CONNECTION_INVALID, -EIO, "STATUS_CONNECTION_INVALID"}, + {STATUS_CONNECTION_ACTIVE, -EIO, "STATUS_CONNECTION_ACTIVE"}, + {STATUS_NETWORK_UNREACHABLE, -ENETUNREACH, + "STATUS_NETWORK_UNREACHABLE"}, + {STATUS_HOST_UNREACHABLE, -EHOSTDOWN, "STATUS_HOST_UNREACHABLE"}, + {STATUS_PROTOCOL_UNREACHABLE, -ENETUNREACH, + "STATUS_PROTOCOL_UNREACHABLE"}, + {STATUS_PORT_UNREACHABLE, -ENETUNREACH, "STATUS_PORT_UNREACHABLE"}, + {STATUS_REQUEST_ABORTED, -EIO, "STATUS_REQUEST_ABORTED"}, + {STATUS_CONNECTION_ABORTED, -ECONNABORTED, "STATUS_CONNECTION_ABORTED"}, + {STATUS_BAD_COMPRESSION_BUFFER, -EIO, "STATUS_BAD_COMPRESSION_BUFFER"}, + {STATUS_USER_MAPPED_FILE, -EIO, "STATUS_USER_MAPPED_FILE"}, + {STATUS_AUDIT_FAILED, -EIO, "STATUS_AUDIT_FAILED"}, + {STATUS_TIMER_RESOLUTION_NOT_SET, -EIO, + "STATUS_TIMER_RESOLUTION_NOT_SET"}, + {STATUS_CONNECTION_COUNT_LIMIT, -EIO, "STATUS_CONNECTION_COUNT_LIMIT"}, + {STATUS_LOGIN_TIME_RESTRICTION, -EACCES, + "STATUS_LOGIN_TIME_RESTRICTION"}, + {STATUS_LOGIN_WKSTA_RESTRICTION, -EACCES, + "STATUS_LOGIN_WKSTA_RESTRICTION"}, + {STATUS_IMAGE_MP_UP_MISMATCH, -EIO, "STATUS_IMAGE_MP_UP_MISMATCH"}, + {STATUS_INSUFFICIENT_LOGON_INFO, -EIO, + "STATUS_INSUFFICIENT_LOGON_INFO"}, + {STATUS_BAD_DLL_ENTRYPOINT, -EIO, "STATUS_BAD_DLL_ENTRYPOINT"}, + {STATUS_BAD_SERVICE_ENTRYPOINT, -EIO, "STATUS_BAD_SERVICE_ENTRYPOINT"}, + {STATUS_LPC_REPLY_LOST, -EIO, "STATUS_LPC_REPLY_LOST"}, + {STATUS_IP_ADDRESS_CONFLICT1, -EIO, "STATUS_IP_ADDRESS_CONFLICT1"}, + {STATUS_IP_ADDRESS_CONFLICT2, -EIO, "STATUS_IP_ADDRESS_CONFLICT2"}, + {STATUS_REGISTRY_QUOTA_LIMIT, -EDQUOT, "STATUS_REGISTRY_QUOTA_LIMIT"}, + {STATUS_PATH_NOT_COVERED, -EREMOTE, "STATUS_PATH_NOT_COVERED"}, + {STATUS_NO_CALLBACK_ACTIVE, -EIO, "STATUS_NO_CALLBACK_ACTIVE"}, + {STATUS_LICENSE_QUOTA_EXCEEDED, -EACCES, + "STATUS_LICENSE_QUOTA_EXCEEDED"}, + {STATUS_PWD_TOO_SHORT, -EIO, "STATUS_PWD_TOO_SHORT"}, + {STATUS_PWD_TOO_RECENT, -EIO, "STATUS_PWD_TOO_RECENT"}, + {STATUS_PWD_HISTORY_CONFLICT, -EIO, "STATUS_PWD_HISTORY_CONFLICT"}, + {STATUS_PLUGPLAY_NO_DEVICE, -EIO, "STATUS_PLUGPLAY_NO_DEVICE"}, + {STATUS_UNSUPPORTED_COMPRESSION, -EIO, + "STATUS_UNSUPPORTED_COMPRESSION"}, + {STATUS_INVALID_HW_PROFILE, -EIO, "STATUS_INVALID_HW_PROFILE"}, + {STATUS_INVALID_PLUGPLAY_DEVICE_PATH, -EIO, + "STATUS_INVALID_PLUGPLAY_DEVICE_PATH"}, + {STATUS_DRIVER_ORDINAL_NOT_FOUND, -EIO, + "STATUS_DRIVER_ORDINAL_NOT_FOUND"}, + {STATUS_DRIVER_ENTRYPOINT_NOT_FOUND, -EIO, + "STATUS_DRIVER_ENTRYPOINT_NOT_FOUND"}, + {STATUS_RESOURCE_NOT_OWNED, -EIO, "STATUS_RESOURCE_NOT_OWNED"}, + {STATUS_TOO_MANY_LINKS, -EMLINK, "STATUS_TOO_MANY_LINKS"}, + {STATUS_QUOTA_LIST_INCONSISTENT, -EIO, + "STATUS_QUOTA_LIST_INCONSISTENT"}, + {STATUS_FILE_IS_OFFLINE, -EIO, "STATUS_FILE_IS_OFFLINE"}, + {STATUS_EVALUATION_EXPIRATION, -EIO, "STATUS_EVALUATION_EXPIRATION"}, + {STATUS_ILLEGAL_DLL_RELOCATION, -EIO, "STATUS_ILLEGAL_DLL_RELOCATION"}, + {STATUS_LICENSE_VIOLATION, -EIO, "STATUS_LICENSE_VIOLATION"}, + {STATUS_DLL_INIT_FAILED_LOGOFF, -EIO, "STATUS_DLL_INIT_FAILED_LOGOFF"}, + {STATUS_DRIVER_UNABLE_TO_LOAD, -EIO, "STATUS_DRIVER_UNABLE_TO_LOAD"}, + {STATUS_DFS_UNAVAILABLE, -EIO, "STATUS_DFS_UNAVAILABLE"}, + {STATUS_VOLUME_DISMOUNTED, -EIO, "STATUS_VOLUME_DISMOUNTED"}, + {STATUS_WX86_INTERNAL_ERROR, -EIO, "STATUS_WX86_INTERNAL_ERROR"}, + {STATUS_WX86_FLOAT_STACK_CHECK, -EIO, "STATUS_WX86_FLOAT_STACK_CHECK"}, + {STATUS_VALIDATE_CONTINUE, -EIO, "STATUS_VALIDATE_CONTINUE"}, + {STATUS_NO_MATCH, -EIO, "STATUS_NO_MATCH"}, + {STATUS_NO_MORE_MATCHES, -EIO, "STATUS_NO_MORE_MATCHES"}, + {STATUS_NOT_A_REPARSE_POINT, -EIO, "STATUS_NOT_A_REPARSE_POINT"}, + {STATUS_IO_REPARSE_TAG_INVALID, -EIO, "STATUS_IO_REPARSE_TAG_INVALID"}, + {STATUS_IO_REPARSE_TAG_MISMATCH, -EIO, + "STATUS_IO_REPARSE_TAG_MISMATCH"}, + {STATUS_IO_REPARSE_DATA_INVALID, -EIO, + "STATUS_IO_REPARSE_DATA_INVALID"}, + {STATUS_IO_REPARSE_TAG_NOT_HANDLED, -EIO, + "STATUS_IO_REPARSE_TAG_NOT_HANDLED"}, + {STATUS_REPARSE_POINT_NOT_RESOLVED, -EIO, + "STATUS_REPARSE_POINT_NOT_RESOLVED"}, + {STATUS_DIRECTORY_IS_A_REPARSE_POINT, -EIO, + "STATUS_DIRECTORY_IS_A_REPARSE_POINT"}, + {STATUS_RANGE_LIST_CONFLICT, -EIO, "STATUS_RANGE_LIST_CONFLICT"}, + {STATUS_SOURCE_ELEMENT_EMPTY, -EIO, "STATUS_SOURCE_ELEMENT_EMPTY"}, + {STATUS_DESTINATION_ELEMENT_FULL, -EIO, + "STATUS_DESTINATION_ELEMENT_FULL"}, + {STATUS_ILLEGAL_ELEMENT_ADDRESS, -EIO, + "STATUS_ILLEGAL_ELEMENT_ADDRESS"}, + {STATUS_MAGAZINE_NOT_PRESENT, -EIO, "STATUS_MAGAZINE_NOT_PRESENT"}, + {STATUS_REINITIALIZATION_NEEDED, -EIO, + "STATUS_REINITIALIZATION_NEEDED"}, + {STATUS_ENCRYPTION_FAILED, -EIO, "STATUS_ENCRYPTION_FAILED"}, + {STATUS_DECRYPTION_FAILED, -EIO, "STATUS_DECRYPTION_FAILED"}, + {STATUS_RANGE_NOT_FOUND, -EIO, "STATUS_RANGE_NOT_FOUND"}, + {STATUS_NO_RECOVERY_POLICY, -EIO, "STATUS_NO_RECOVERY_POLICY"}, + {STATUS_NO_EFS, -EIO, "STATUS_NO_EFS"}, + {STATUS_WRONG_EFS, -EIO, "STATUS_WRONG_EFS"}, + {STATUS_NO_USER_KEYS, -EIO, "STATUS_NO_USER_KEYS"}, + {STATUS_FILE_NOT_ENCRYPTED, -EIO, "STATUS_FILE_NOT_ENCRYPTED"}, + {STATUS_NOT_EXPORT_FORMAT, -EIO, "STATUS_NOT_EXPORT_FORMAT"}, + {STATUS_FILE_ENCRYPTED, -EIO, "STATUS_FILE_ENCRYPTED"}, + {STATUS_WMI_GUID_NOT_FOUND, -EIO, "STATUS_WMI_GUID_NOT_FOUND"}, + {STATUS_WMI_INSTANCE_NOT_FOUND, -EIO, "STATUS_WMI_INSTANCE_NOT_FOUND"}, + {STATUS_WMI_ITEMID_NOT_FOUND, -EIO, "STATUS_WMI_ITEMID_NOT_FOUND"}, + {STATUS_WMI_TRY_AGAIN, -EIO, "STATUS_WMI_TRY_AGAIN"}, + {STATUS_SHARED_POLICY, -EIO, "STATUS_SHARED_POLICY"}, + {STATUS_POLICY_OBJECT_NOT_FOUND, -EIO, + "STATUS_POLICY_OBJECT_NOT_FOUND"}, + {STATUS_POLICY_ONLY_IN_DS, -EIO, "STATUS_POLICY_ONLY_IN_DS"}, + {STATUS_VOLUME_NOT_UPGRADED, -EIO, "STATUS_VOLUME_NOT_UPGRADED"}, + {STATUS_REMOTE_STORAGE_NOT_ACTIVE, -EIO, + "STATUS_REMOTE_STORAGE_NOT_ACTIVE"}, + {STATUS_REMOTE_STORAGE_MEDIA_ERROR, -EIO, + "STATUS_REMOTE_STORAGE_MEDIA_ERROR"}, + {STATUS_NO_TRACKING_SERVICE, -EIO, "STATUS_NO_TRACKING_SERVICE"}, + {STATUS_SERVER_SID_MISMATCH, -EIO, "STATUS_SERVER_SID_MISMATCH"}, + {STATUS_DS_NO_ATTRIBUTE_OR_VALUE, -EIO, + "STATUS_DS_NO_ATTRIBUTE_OR_VALUE"}, + {STATUS_DS_INVALID_ATTRIBUTE_SYNTAX, -EIO, + "STATUS_DS_INVALID_ATTRIBUTE_SYNTAX"}, + {STATUS_DS_ATTRIBUTE_TYPE_UNDEFINED, -EIO, + "STATUS_DS_ATTRIBUTE_TYPE_UNDEFINED"}, + {STATUS_DS_ATTRIBUTE_OR_VALUE_EXISTS, -EIO, + "STATUS_DS_ATTRIBUTE_OR_VALUE_EXISTS"}, + {STATUS_DS_BUSY, -EBUSY, "STATUS_DS_BUSY"}, + {STATUS_DS_UNAVAILABLE, -EIO, "STATUS_DS_UNAVAILABLE"}, + {STATUS_DS_NO_RIDS_ALLOCATED, -EIO, "STATUS_DS_NO_RIDS_ALLOCATED"}, + {STATUS_DS_NO_MORE_RIDS, -EIO, "STATUS_DS_NO_MORE_RIDS"}, + {STATUS_DS_INCORRECT_ROLE_OWNER, -EIO, + "STATUS_DS_INCORRECT_ROLE_OWNER"}, + {STATUS_DS_RIDMGR_INIT_ERROR, -EIO, "STATUS_DS_RIDMGR_INIT_ERROR"}, + {STATUS_DS_OBJ_CLASS_VIOLATION, -EIO, "STATUS_DS_OBJ_CLASS_VIOLATION"}, + {STATUS_DS_CANT_ON_NON_LEAF, -EIO, "STATUS_DS_CANT_ON_NON_LEAF"}, + {STATUS_DS_CANT_ON_RDN, -EIO, "STATUS_DS_CANT_ON_RDN"}, + {STATUS_DS_CANT_MOD_OBJ_CLASS, -EIO, "STATUS_DS_CANT_MOD_OBJ_CLASS"}, + {STATUS_DS_CROSS_DOM_MOVE_FAILED, -EIO, + "STATUS_DS_CROSS_DOM_MOVE_FAILED"}, + {STATUS_DS_GC_NOT_AVAILABLE, -EIO, "STATUS_DS_GC_NOT_AVAILABLE"}, + {STATUS_DIRECTORY_SERVICE_REQUIRED, -EIO, + "STATUS_DIRECTORY_SERVICE_REQUIRED"}, + {STATUS_REPARSE_ATTRIBUTE_CONFLICT, -EIO, + "STATUS_REPARSE_ATTRIBUTE_CONFLICT"}, + {STATUS_CANT_ENABLE_DENY_ONLY, -EIO, "STATUS_CANT_ENABLE_DENY_ONLY"}, + {STATUS_FLOAT_MULTIPLE_FAULTS, -EIO, "STATUS_FLOAT_MULTIPLE_FAULTS"}, + {STATUS_FLOAT_MULTIPLE_TRAPS, -EIO, "STATUS_FLOAT_MULTIPLE_TRAPS"}, + {STATUS_DEVICE_REMOVED, -EIO, "STATUS_DEVICE_REMOVED"}, + {STATUS_JOURNAL_DELETE_IN_PROGRESS, -EIO, + "STATUS_JOURNAL_DELETE_IN_PROGRESS"}, + {STATUS_JOURNAL_NOT_ACTIVE, -EIO, "STATUS_JOURNAL_NOT_ACTIVE"}, + {STATUS_NOINTERFACE, -EIO, "STATUS_NOINTERFACE"}, + {STATUS_DS_ADMIN_LIMIT_EXCEEDED, -EIO, + "STATUS_DS_ADMIN_LIMIT_EXCEEDED"}, + {STATUS_DRIVER_FAILED_SLEEP, -EIO, "STATUS_DRIVER_FAILED_SLEEP"}, + {STATUS_MUTUAL_AUTHENTICATION_FAILED, -EIO, + "STATUS_MUTUAL_AUTHENTICATION_FAILED"}, + {STATUS_CORRUPT_SYSTEM_FILE, -EIO, "STATUS_CORRUPT_SYSTEM_FILE"}, + {STATUS_DATATYPE_MISALIGNMENT_ERROR, -EIO, + "STATUS_DATATYPE_MISALIGNMENT_ERROR"}, + {STATUS_WMI_READ_ONLY, -EROFS, "STATUS_WMI_READ_ONLY"}, + {STATUS_WMI_SET_FAILURE, -EIO, "STATUS_WMI_SET_FAILURE"}, + {STATUS_COMMITMENT_MINIMUM, -EIO, "STATUS_COMMITMENT_MINIMUM"}, + {STATUS_REG_NAT_CONSUMPTION, -EIO, "STATUS_REG_NAT_CONSUMPTION"}, + {STATUS_TRANSPORT_FULL, -EIO, "STATUS_TRANSPORT_FULL"}, + {STATUS_DS_SAM_INIT_FAILURE, -EIO, "STATUS_DS_SAM_INIT_FAILURE"}, + {STATUS_ONLY_IF_CONNECTED, -EIO, "STATUS_ONLY_IF_CONNECTED"}, + {STATUS_DS_SENSITIVE_GROUP_VIOLATION, -EIO, + "STATUS_DS_SENSITIVE_GROUP_VIOLATION"}, + {STATUS_PNP_RESTART_ENUMERATION, -EIO, + "STATUS_PNP_RESTART_ENUMERATION"}, + {STATUS_JOURNAL_ENTRY_DELETED, -EIO, "STATUS_JOURNAL_ENTRY_DELETED"}, + {STATUS_DS_CANT_MOD_PRIMARYGROUPID, -EIO, + "STATUS_DS_CANT_MOD_PRIMARYGROUPID"}, + {STATUS_SYSTEM_IMAGE_BAD_SIGNATURE, -EIO, + "STATUS_SYSTEM_IMAGE_BAD_SIGNATURE"}, + {STATUS_PNP_REBOOT_REQUIRED, -EIO, "STATUS_PNP_REBOOT_REQUIRED"}, + {STATUS_POWER_STATE_INVALID, -EIO, "STATUS_POWER_STATE_INVALID"}, + {STATUS_DS_INVALID_GROUP_TYPE, -EIO, "STATUS_DS_INVALID_GROUP_TYPE"}, + {STATUS_DS_NO_NEST_GLOBALGROUP_IN_MIXEDDOMAIN, -EIO, + "STATUS_DS_NO_NEST_GLOBALGROUP_IN_MIXEDDOMAIN"}, + {STATUS_DS_NO_NEST_LOCALGROUP_IN_MIXEDDOMAIN, -EIO, + "STATUS_DS_NO_NEST_LOCALGROUP_IN_MIXEDDOMAIN"}, + {STATUS_DS_GLOBAL_CANT_HAVE_LOCAL_MEMBER, -EIO, + "STATUS_DS_GLOBAL_CANT_HAVE_LOCAL_MEMBER"}, + {STATUS_DS_GLOBAL_CANT_HAVE_UNIVERSAL_MEMBER, -EIO, + "STATUS_DS_GLOBAL_CANT_HAVE_UNIVERSAL_MEMBER"}, + {STATUS_DS_UNIVERSAL_CANT_HAVE_LOCAL_MEMBER, -EIO, + "STATUS_DS_UNIVERSAL_CANT_HAVE_LOCAL_MEMBER"}, + {STATUS_DS_GLOBAL_CANT_HAVE_CROSSDOMAIN_MEMBER, -EIO, + "STATUS_DS_GLOBAL_CANT_HAVE_CROSSDOMAIN_MEMBER"}, + {STATUS_DS_LOCAL_CANT_HAVE_CROSSDOMAIN_LOCAL_MEMBER, -EIO, + "STATUS_DS_LOCAL_CANT_HAVE_CROSSDOMAIN_LOCAL_MEMBER"}, + {STATUS_DS_HAVE_PRIMARY_MEMBERS, -EIO, + "STATUS_DS_HAVE_PRIMARY_MEMBERS"}, + {STATUS_WMI_NOT_SUPPORTED, -EOPNOTSUPP, "STATUS_WMI_NOT_SUPPORTED"}, + {STATUS_INSUFFICIENT_POWER, -EIO, "STATUS_INSUFFICIENT_POWER"}, + {STATUS_SAM_NEED_BOOTKEY_PASSWORD, -EIO, + "STATUS_SAM_NEED_BOOTKEY_PASSWORD"}, + {STATUS_SAM_NEED_BOOTKEY_FLOPPY, -EIO, + "STATUS_SAM_NEED_BOOTKEY_FLOPPY"}, + {STATUS_DS_CANT_START, -EIO, "STATUS_DS_CANT_START"}, + {STATUS_DS_INIT_FAILURE, -EIO, "STATUS_DS_INIT_FAILURE"}, + {STATUS_SAM_INIT_FAILURE, -EIO, "STATUS_SAM_INIT_FAILURE"}, + {STATUS_DS_GC_REQUIRED, -EIO, "STATUS_DS_GC_REQUIRED"}, + {STATUS_DS_LOCAL_MEMBER_OF_LOCAL_ONLY, -EIO, + "STATUS_DS_LOCAL_MEMBER_OF_LOCAL_ONLY"}, + {STATUS_DS_NO_FPO_IN_UNIVERSAL_GROUPS, -EIO, + "STATUS_DS_NO_FPO_IN_UNIVERSAL_GROUPS"}, + {STATUS_DS_MACHINE_ACCOUNT_QUOTA_EXCEEDED, -EDQUOT, + "STATUS_DS_MACHINE_ACCOUNT_QUOTA_EXCEEDED"}, + {STATUS_MULTIPLE_FAULT_VIOLATION, -EIO, + "STATUS_MULTIPLE_FAULT_VIOLATION"}, + {STATUS_CURRENT_DOMAIN_NOT_ALLOWED, -EIO, + "STATUS_CURRENT_DOMAIN_NOT_ALLOWED"}, + {STATUS_CANNOT_MAKE, -EIO, "STATUS_CANNOT_MAKE"}, + {STATUS_SYSTEM_SHUTDOWN, -EIO, "STATUS_SYSTEM_SHUTDOWN"}, + {STATUS_DS_INIT_FAILURE_CONSOLE, -EIO, + "STATUS_DS_INIT_FAILURE_CONSOLE"}, + {STATUS_DS_SAM_INIT_FAILURE_CONSOLE, -EIO, + "STATUS_DS_SAM_INIT_FAILURE_CONSOLE"}, + {STATUS_UNFINISHED_CONTEXT_DELETED, -EIO, + "STATUS_UNFINISHED_CONTEXT_DELETED"}, + {STATUS_NO_TGT_REPLY, -EIO, "STATUS_NO_TGT_REPLY"}, + /* Note that ENOATTTR and ENODATA are the same errno */ + {STATUS_OBJECTID_NOT_FOUND, -ENODATA, "STATUS_OBJECTID_NOT_FOUND"}, + {STATUS_NO_IP_ADDRESSES, -EIO, "STATUS_NO_IP_ADDRESSES"}, + {STATUS_WRONG_CREDENTIAL_HANDLE, -EIO, + "STATUS_WRONG_CREDENTIAL_HANDLE"}, + {STATUS_CRYPTO_SYSTEM_INVALID, -EIO, "STATUS_CRYPTO_SYSTEM_INVALID"}, + {STATUS_MAX_REFERRALS_EXCEEDED, -EIO, "STATUS_MAX_REFERRALS_EXCEEDED"}, + {STATUS_MUST_BE_KDC, -EIO, "STATUS_MUST_BE_KDC"}, + {STATUS_STRONG_CRYPTO_NOT_SUPPORTED, -EIO, + "STATUS_STRONG_CRYPTO_NOT_SUPPORTED"}, + {STATUS_TOO_MANY_PRINCIPALS, -EIO, "STATUS_TOO_MANY_PRINCIPALS"}, + {STATUS_NO_PA_DATA, -EIO, "STATUS_NO_PA_DATA"}, + {STATUS_PKINIT_NAME_MISMATCH, -EIO, "STATUS_PKINIT_NAME_MISMATCH"}, + {STATUS_SMARTCARD_LOGON_REQUIRED, -EIO, + "STATUS_SMARTCARD_LOGON_REQUIRED"}, + {STATUS_KDC_INVALID_REQUEST, -EIO, "STATUS_KDC_INVALID_REQUEST"}, + {STATUS_KDC_UNABLE_TO_REFER, -EIO, "STATUS_KDC_UNABLE_TO_REFER"}, + {STATUS_KDC_UNKNOWN_ETYPE, -EIO, "STATUS_KDC_UNKNOWN_ETYPE"}, + {STATUS_SHUTDOWN_IN_PROGRESS, -EIO, "STATUS_SHUTDOWN_IN_PROGRESS"}, + {STATUS_SERVER_SHUTDOWN_IN_PROGRESS, -EIO, + "STATUS_SERVER_SHUTDOWN_IN_PROGRESS"}, + {STATUS_NOT_SUPPORTED_ON_SBS, -EOPNOTSUPP, + "STATUS_NOT_SUPPORTED_ON_SBS"}, + {STATUS_WMI_GUID_DISCONNECTED, -EIO, "STATUS_WMI_GUID_DISCONNECTED"}, + {STATUS_WMI_ALREADY_DISABLED, -EIO, "STATUS_WMI_ALREADY_DISABLED"}, + {STATUS_WMI_ALREADY_ENABLED, -EIO, "STATUS_WMI_ALREADY_ENABLED"}, + {STATUS_MFT_TOO_FRAGMENTED, -EIO, "STATUS_MFT_TOO_FRAGMENTED"}, + {STATUS_COPY_PROTECTION_FAILURE, -EIO, + "STATUS_COPY_PROTECTION_FAILURE"}, + {STATUS_CSS_AUTHENTICATION_FAILURE, -EIO, + "STATUS_CSS_AUTHENTICATION_FAILURE"}, + {STATUS_CSS_KEY_NOT_PRESENT, -EIO, "STATUS_CSS_KEY_NOT_PRESENT"}, + {STATUS_CSS_KEY_NOT_ESTABLISHED, -EIO, + "STATUS_CSS_KEY_NOT_ESTABLISHED"}, + {STATUS_CSS_SCRAMBLED_SECTOR, -EIO, "STATUS_CSS_SCRAMBLED_SECTOR"}, + {STATUS_CSS_REGION_MISMATCH, -EIO, "STATUS_CSS_REGION_MISMATCH"}, + {STATUS_CSS_RESETS_EXHAUSTED, -EIO, "STATUS_CSS_RESETS_EXHAUSTED"}, + {STATUS_PKINIT_FAILURE, -EIO, "STATUS_PKINIT_FAILURE"}, + {STATUS_SMARTCARD_SUBSYSTEM_FAILURE, -EIO, + "STATUS_SMARTCARD_SUBSYSTEM_FAILURE"}, + {STATUS_NO_KERB_KEY, -EIO, "STATUS_NO_KERB_KEY"}, + {STATUS_HOST_DOWN, -EIO, "STATUS_HOST_DOWN"}, + {STATUS_UNSUPPORTED_PREAUTH, -EIO, "STATUS_UNSUPPORTED_PREAUTH"}, + {STATUS_EFS_ALG_BLOB_TOO_BIG, -EIO, "STATUS_EFS_ALG_BLOB_TOO_BIG"}, + {STATUS_PORT_NOT_SET, -EIO, "STATUS_PORT_NOT_SET"}, + {STATUS_DEBUGGER_INACTIVE, -EIO, "STATUS_DEBUGGER_INACTIVE"}, + {STATUS_DS_VERSION_CHECK_FAILURE, -EIO, + "STATUS_DS_VERSION_CHECK_FAILURE"}, + {STATUS_AUDITING_DISABLED, -EIO, "STATUS_AUDITING_DISABLED"}, + {STATUS_PRENT4_MACHINE_ACCOUNT, -EIO, "STATUS_PRENT4_MACHINE_ACCOUNT"}, + {STATUS_DS_AG_CANT_HAVE_UNIVERSAL_MEMBER, -EIO, + "STATUS_DS_AG_CANT_HAVE_UNIVERSAL_MEMBER"}, + {STATUS_INVALID_IMAGE_WIN_32, -EIO, "STATUS_INVALID_IMAGE_WIN_32"}, + {STATUS_INVALID_IMAGE_WIN_64, -EIO, "STATUS_INVALID_IMAGE_WIN_64"}, + {STATUS_BAD_BINDINGS, -EIO, "STATUS_BAD_BINDINGS"}, + {STATUS_NETWORK_SESSION_EXPIRED, -EIO, + "STATUS_NETWORK_SESSION_EXPIRED"}, + {STATUS_APPHELP_BLOCK, -EIO, "STATUS_APPHELP_BLOCK"}, + {STATUS_ALL_SIDS_FILTERED, -EIO, "STATUS_ALL_SIDS_FILTERED"}, + {STATUS_NOT_SAFE_MODE_DRIVER, -EIO, "STATUS_NOT_SAFE_MODE_DRIVER"}, + {STATUS_ACCESS_DISABLED_BY_POLICY_DEFAULT, -EACCES, + "STATUS_ACCESS_DISABLED_BY_POLICY_DEFAULT"}, + {STATUS_ACCESS_DISABLED_BY_POLICY_PATH, -EACCES, + "STATUS_ACCESS_DISABLED_BY_POLICY_PATH"}, + {STATUS_ACCESS_DISABLED_BY_POLICY_PUBLISHER, -EACCES, + "STATUS_ACCESS_DISABLED_BY_POLICY_PUBLISHER"}, + {STATUS_ACCESS_DISABLED_BY_POLICY_OTHER, -EACCES, + "STATUS_ACCESS_DISABLED_BY_POLICY_OTHER"}, + {STATUS_FAILED_DRIVER_ENTRY, -EIO, "STATUS_FAILED_DRIVER_ENTRY"}, + {STATUS_DEVICE_ENUMERATION_ERROR, -EIO, + "STATUS_DEVICE_ENUMERATION_ERROR"}, + {STATUS_MOUNT_POINT_NOT_RESOLVED, -EIO, + "STATUS_MOUNT_POINT_NOT_RESOLVED"}, + {STATUS_INVALID_DEVICE_OBJECT_PARAMETER, -EIO, + "STATUS_INVALID_DEVICE_OBJECT_PARAMETER"}, + {STATUS_MCA_OCCURED, -EIO, "STATUS_MCA_OCCURED"}, + {STATUS_DRIVER_BLOCKED_CRITICAL, -EIO, + "STATUS_DRIVER_BLOCKED_CRITICAL"}, + {STATUS_DRIVER_BLOCKED, -EIO, "STATUS_DRIVER_BLOCKED"}, + {STATUS_DRIVER_DATABASE_ERROR, -EIO, "STATUS_DRIVER_DATABASE_ERROR"}, + {STATUS_SYSTEM_HIVE_TOO_LARGE, -EIO, "STATUS_SYSTEM_HIVE_TOO_LARGE"}, + {STATUS_INVALID_IMPORT_OF_NON_DLL, -EIO, + "STATUS_INVALID_IMPORT_OF_NON_DLL"}, + {STATUS_NO_SECRETS, -EIO, "STATUS_NO_SECRETS"}, + {STATUS_ACCESS_DISABLED_NO_SAFER_UI_BY_POLICY, -EACCES, + "STATUS_ACCESS_DISABLED_NO_SAFER_UI_BY_POLICY"}, + {STATUS_FAILED_STACK_SWITCH, -EIO, "STATUS_FAILED_STACK_SWITCH"}, + {STATUS_HEAP_CORRUPTION, -EIO, "STATUS_HEAP_CORRUPTION"}, + {STATUS_SMARTCARD_WRONG_PIN, -EIO, "STATUS_SMARTCARD_WRONG_PIN"}, + {STATUS_SMARTCARD_CARD_BLOCKED, -EIO, "STATUS_SMARTCARD_CARD_BLOCKED"}, + {STATUS_SMARTCARD_CARD_NOT_AUTHENTICATED, -EIO, + "STATUS_SMARTCARD_CARD_NOT_AUTHENTICATED"}, + {STATUS_SMARTCARD_NO_CARD, -EIO, "STATUS_SMARTCARD_NO_CARD"}, + {STATUS_SMARTCARD_NO_KEY_CONTAINER, -EIO, + "STATUS_SMARTCARD_NO_KEY_CONTAINER"}, + {STATUS_SMARTCARD_NO_CERTIFICATE, -EIO, + "STATUS_SMARTCARD_NO_CERTIFICATE"}, + {STATUS_SMARTCARD_NO_KEYSET, -EIO, "STATUS_SMARTCARD_NO_KEYSET"}, + {STATUS_SMARTCARD_IO_ERROR, -EIO, "STATUS_SMARTCARD_IO_ERROR"}, + {STATUS_DOWNGRADE_DETECTED, -EIO, "STATUS_DOWNGRADE_DETECTED"}, + {STATUS_SMARTCARD_CERT_REVOKED, -EIO, "STATUS_SMARTCARD_CERT_REVOKED"}, + {STATUS_ISSUING_CA_UNTRUSTED, -EIO, "STATUS_ISSUING_CA_UNTRUSTED"}, + {STATUS_REVOCATION_OFFLINE_C, -EIO, "STATUS_REVOCATION_OFFLINE_C"}, + {STATUS_PKINIT_CLIENT_FAILURE, -EIO, "STATUS_PKINIT_CLIENT_FAILURE"}, + {STATUS_SMARTCARD_CERT_EXPIRED, -EIO, "STATUS_SMARTCARD_CERT_EXPIRED"}, + {STATUS_DRIVER_FAILED_PRIOR_UNLOAD, -EIO, + "STATUS_DRIVER_FAILED_PRIOR_UNLOAD"}, + {STATUS_SMARTCARD_SILENT_CONTEXT, -EIO, + "STATUS_SMARTCARD_SILENT_CONTEXT"}, + {STATUS_PER_USER_TRUST_QUOTA_EXCEEDED, -EDQUOT, + "STATUS_PER_USER_TRUST_QUOTA_EXCEEDED"}, + {STATUS_ALL_USER_TRUST_QUOTA_EXCEEDED, -EDQUOT, + "STATUS_ALL_USER_TRUST_QUOTA_EXCEEDED"}, + {STATUS_USER_DELETE_TRUST_QUOTA_EXCEEDED, -EDQUOT, + "STATUS_USER_DELETE_TRUST_QUOTA_EXCEEDED"}, + {STATUS_DS_NAME_NOT_UNIQUE, -EIO, "STATUS_DS_NAME_NOT_UNIQUE"}, + {STATUS_DS_DUPLICATE_ID_FOUND, -EIO, "STATUS_DS_DUPLICATE_ID_FOUND"}, + {STATUS_DS_GROUP_CONVERSION_ERROR, -EIO, + "STATUS_DS_GROUP_CONVERSION_ERROR"}, + {STATUS_VOLSNAP_PREPARE_HIBERNATE, -EIO, + "STATUS_VOLSNAP_PREPARE_HIBERNATE"}, + {STATUS_USER2USER_REQUIRED, -EIO, "STATUS_USER2USER_REQUIRED"}, + {STATUS_STACK_BUFFER_OVERRUN, -EIO, "STATUS_STACK_BUFFER_OVERRUN"}, + {STATUS_NO_S4U_PROT_SUPPORT, -EIO, "STATUS_NO_S4U_PROT_SUPPORT"}, + {STATUS_CROSSREALM_DELEGATION_FAILURE, -EIO, + "STATUS_CROSSREALM_DELEGATION_FAILURE"}, + {STATUS_REVOCATION_OFFLINE_KDC, -EIO, "STATUS_REVOCATION_OFFLINE_KDC"}, + {STATUS_ISSUING_CA_UNTRUSTED_KDC, -EIO, + "STATUS_ISSUING_CA_UNTRUSTED_KDC"}, + {STATUS_KDC_CERT_EXPIRED, -EIO, "STATUS_KDC_CERT_EXPIRED"}, + {STATUS_KDC_CERT_REVOKED, -EIO, "STATUS_KDC_CERT_REVOKED"}, + {STATUS_PARAMETER_QUOTA_EXCEEDED, -EDQUOT, + "STATUS_PARAMETER_QUOTA_EXCEEDED"}, + {STATUS_HIBERNATION_FAILURE, -EIO, "STATUS_HIBERNATION_FAILURE"}, + {STATUS_DELAY_LOAD_FAILED, -EIO, "STATUS_DELAY_LOAD_FAILED"}, + {STATUS_AUTHENTICATION_FIREWALL_FAILED, -EIO, + "STATUS_AUTHENTICATION_FIREWALL_FAILED"}, + {STATUS_VDM_DISALLOWED, -EIO, "STATUS_VDM_DISALLOWED"}, + {STATUS_HUNG_DISPLAY_DRIVER_THREAD, -EIO, + "STATUS_HUNG_DISPLAY_DRIVER_THREAD"}, + {STATUS_INSUFFICIENT_RESOURCE_FOR_SPECIFIED_SHARED_SECTION_SIZE, -EIO, + "STATUS_INSUFFICIENT_RESOURCE_FOR_SPECIFIED_SHARED_SECTION_SIZE"}, + {STATUS_INVALID_CRUNTIME_PARAMETER, -EIO, + "STATUS_INVALID_CRUNTIME_PARAMETER"}, + {STATUS_NTLM_BLOCKED, -EIO, "STATUS_NTLM_BLOCKED"}, + {STATUS_ASSERTION_FAILURE, -EIO, "STATUS_ASSERTION_FAILURE"}, + {STATUS_VERIFIER_STOP, -EIO, "STATUS_VERIFIER_STOP"}, + {STATUS_CALLBACK_POP_STACK, -EIO, "STATUS_CALLBACK_POP_STACK"}, + {STATUS_INCOMPATIBLE_DRIVER_BLOCKED, -EIO, + "STATUS_INCOMPATIBLE_DRIVER_BLOCKED"}, + {STATUS_HIVE_UNLOADED, -EIO, "STATUS_HIVE_UNLOADED"}, + {STATUS_COMPRESSION_DISABLED, -EIO, "STATUS_COMPRESSION_DISABLED"}, + {STATUS_FILE_SYSTEM_LIMITATION, -EIO, "STATUS_FILE_SYSTEM_LIMITATION"}, + {STATUS_INVALID_IMAGE_HASH, -EIO, "STATUS_INVALID_IMAGE_HASH"}, + {STATUS_NOT_CAPABLE, -EIO, "STATUS_NOT_CAPABLE"}, + {STATUS_REQUEST_OUT_OF_SEQUENCE, -EIO, + "STATUS_REQUEST_OUT_OF_SEQUENCE"}, + {STATUS_IMPLEMENTATION_LIMIT, -EIO, "STATUS_IMPLEMENTATION_LIMIT"}, + {STATUS_ELEVATION_REQUIRED, -EIO, "STATUS_ELEVATION_REQUIRED"}, + {STATUS_BEYOND_VDL, -EIO, "STATUS_BEYOND_VDL"}, + {STATUS_ENCOUNTERED_WRITE_IN_PROGRESS, -EIO, + "STATUS_ENCOUNTERED_WRITE_IN_PROGRESS"}, + {STATUS_PTE_CHANGED, -EIO, "STATUS_PTE_CHANGED"}, + {STATUS_PURGE_FAILED, -EIO, "STATUS_PURGE_FAILED"}, + {STATUS_CRED_REQUIRES_CONFIRMATION, -EIO, + "STATUS_CRED_REQUIRES_CONFIRMATION"}, + {STATUS_CS_ENCRYPTION_INVALID_SERVER_RESPONSE, -EIO, + "STATUS_CS_ENCRYPTION_INVALID_SERVER_RESPONSE"}, + {STATUS_CS_ENCRYPTION_UNSUPPORTED_SERVER, -EIO, + "STATUS_CS_ENCRYPTION_UNSUPPORTED_SERVER"}, + {STATUS_CS_ENCRYPTION_EXISTING_ENCRYPTED_FILE, -EIO, + "STATUS_CS_ENCRYPTION_EXISTING_ENCRYPTED_FILE"}, + {STATUS_CS_ENCRYPTION_NEW_ENCRYPTED_FILE, -EIO, + "STATUS_CS_ENCRYPTION_NEW_ENCRYPTED_FILE"}, + {STATUS_CS_ENCRYPTION_FILE_NOT_CSE, -EIO, + "STATUS_CS_ENCRYPTION_FILE_NOT_CSE"}, + {STATUS_INVALID_LABEL, -EIO, "STATUS_INVALID_LABEL"}, + {STATUS_DRIVER_PROCESS_TERMINATED, -EIO, + "STATUS_DRIVER_PROCESS_TERMINATED"}, + {STATUS_AMBIGUOUS_SYSTEM_DEVICE, -EIO, + "STATUS_AMBIGUOUS_SYSTEM_DEVICE"}, + {STATUS_SYSTEM_DEVICE_NOT_FOUND, -EIO, + "STATUS_SYSTEM_DEVICE_NOT_FOUND"}, + {STATUS_RESTART_BOOT_APPLICATION, -EIO, + "STATUS_RESTART_BOOT_APPLICATION"}, + {STATUS_INVALID_TASK_NAME, -EIO, "STATUS_INVALID_TASK_NAME"}, + {STATUS_INVALID_TASK_INDEX, -EIO, "STATUS_INVALID_TASK_INDEX"}, + {STATUS_THREAD_ALREADY_IN_TASK, -EIO, "STATUS_THREAD_ALREADY_IN_TASK"}, + {STATUS_CALLBACK_BYPASS, -EIO, "STATUS_CALLBACK_BYPASS"}, + {STATUS_PORT_CLOSED, -EIO, "STATUS_PORT_CLOSED"}, + {STATUS_MESSAGE_LOST, -EIO, "STATUS_MESSAGE_LOST"}, + {STATUS_INVALID_MESSAGE, -EIO, "STATUS_INVALID_MESSAGE"}, + {STATUS_REQUEST_CANCELED, -EIO, "STATUS_REQUEST_CANCELED"}, + {STATUS_RECURSIVE_DISPATCH, -EIO, "STATUS_RECURSIVE_DISPATCH"}, + {STATUS_LPC_RECEIVE_BUFFER_EXPECTED, -EIO, + "STATUS_LPC_RECEIVE_BUFFER_EXPECTED"}, + {STATUS_LPC_INVALID_CONNECTION_USAGE, -EIO, + "STATUS_LPC_INVALID_CONNECTION_USAGE"}, + {STATUS_LPC_REQUESTS_NOT_ALLOWED, -EIO, + "STATUS_LPC_REQUESTS_NOT_ALLOWED"}, + {STATUS_RESOURCE_IN_USE, -EIO, "STATUS_RESOURCE_IN_USE"}, + {STATUS_HARDWARE_MEMORY_ERROR, -EIO, "STATUS_HARDWARE_MEMORY_ERROR"}, + {STATUS_THREADPOOL_HANDLE_EXCEPTION, -EIO, + "STATUS_THREADPOOL_HANDLE_EXCEPTION"}, + {STATUS_THREADPOOL_SET_EVENT_ON_COMPLETION_FAILED, -EIO, + "STATUS_THREADPOOL_SET_EVENT_ON_COMPLETION_FAILED"}, + {STATUS_THREADPOOL_RELEASE_SEMAPHORE_ON_COMPLETION_FAILED, -EIO, + "STATUS_THREADPOOL_RELEASE_SEMAPHORE_ON_COMPLETION_FAILED"}, + {STATUS_THREADPOOL_RELEASE_MUTEX_ON_COMPLETION_FAILED, -EIO, + "STATUS_THREADPOOL_RELEASE_MUTEX_ON_COMPLETION_FAILED"}, + {STATUS_THREADPOOL_FREE_LIBRARY_ON_COMPLETION_FAILED, -EIO, + "STATUS_THREADPOOL_FREE_LIBRARY_ON_COMPLETION_FAILED"}, + {STATUS_THREADPOOL_RELEASED_DURING_OPERATION, -EIO, + "STATUS_THREADPOOL_RELEASED_DURING_OPERATION"}, + {STATUS_CALLBACK_RETURNED_WHILE_IMPERSONATING, -EIO, + "STATUS_CALLBACK_RETURNED_WHILE_IMPERSONATING"}, + {STATUS_APC_RETURNED_WHILE_IMPERSONATING, -EIO, + "STATUS_APC_RETURNED_WHILE_IMPERSONATING"}, + {STATUS_PROCESS_IS_PROTECTED, -EIO, "STATUS_PROCESS_IS_PROTECTED"}, + {STATUS_MCA_EXCEPTION, -EIO, "STATUS_MCA_EXCEPTION"}, + {STATUS_CERTIFICATE_MAPPING_NOT_UNIQUE, -EIO, + "STATUS_CERTIFICATE_MAPPING_NOT_UNIQUE"}, + {STATUS_SYMLINK_CLASS_DISABLED, -EIO, "STATUS_SYMLINK_CLASS_DISABLED"}, + {STATUS_INVALID_IDN_NORMALIZATION, -EIO, + "STATUS_INVALID_IDN_NORMALIZATION"}, + {STATUS_NO_UNICODE_TRANSLATION, -EIO, "STATUS_NO_UNICODE_TRANSLATION"}, + {STATUS_ALREADY_REGISTERED, -EIO, "STATUS_ALREADY_REGISTERED"}, + {STATUS_CONTEXT_MISMATCH, -EIO, "STATUS_CONTEXT_MISMATCH"}, + {STATUS_PORT_ALREADY_HAS_COMPLETION_LIST, -EIO, + "STATUS_PORT_ALREADY_HAS_COMPLETION_LIST"}, + {STATUS_CALLBACK_RETURNED_THREAD_PRIORITY, -EIO, + "STATUS_CALLBACK_RETURNED_THREAD_PRIORITY"}, + {STATUS_INVALID_THREAD, -EIO, "STATUS_INVALID_THREAD"}, + {STATUS_CALLBACK_RETURNED_TRANSACTION, -EIO, + "STATUS_CALLBACK_RETURNED_TRANSACTION"}, + {STATUS_CALLBACK_RETURNED_LDR_LOCK, -EIO, + "STATUS_CALLBACK_RETURNED_LDR_LOCK"}, + {STATUS_CALLBACK_RETURNED_LANG, -EIO, "STATUS_CALLBACK_RETURNED_LANG"}, + {STATUS_CALLBACK_RETURNED_PRI_BACK, -EIO, + "STATUS_CALLBACK_RETURNED_PRI_BACK"}, + {STATUS_CALLBACK_RETURNED_THREAD_AFFINITY, -EIO, + "STATUS_CALLBACK_RETURNED_THREAD_AFFINITY"}, + {STATUS_DISK_REPAIR_DISABLED, -EIO, "STATUS_DISK_REPAIR_DISABLED"}, + {STATUS_DS_DOMAIN_RENAME_IN_PROGRESS, -EIO, + "STATUS_DS_DOMAIN_RENAME_IN_PROGRESS"}, + {STATUS_DISK_QUOTA_EXCEEDED, -EDQUOT, "STATUS_DISK_QUOTA_EXCEEDED"}, + {STATUS_CONTENT_BLOCKED, -EIO, "STATUS_CONTENT_BLOCKED"}, + {STATUS_BAD_CLUSTERS, -EIO, "STATUS_BAD_CLUSTERS"}, + {STATUS_VOLUME_DIRTY, -EIO, "STATUS_VOLUME_DIRTY"}, + {STATUS_FILE_CHECKED_OUT, -EIO, "STATUS_FILE_CHECKED_OUT"}, + {STATUS_CHECKOUT_REQUIRED, -EIO, "STATUS_CHECKOUT_REQUIRED"}, + {STATUS_BAD_FILE_TYPE, -EIO, "STATUS_BAD_FILE_TYPE"}, + {STATUS_FILE_TOO_LARGE, -EIO, "STATUS_FILE_TOO_LARGE"}, + {STATUS_FORMS_AUTH_REQUIRED, -EIO, "STATUS_FORMS_AUTH_REQUIRED"}, + {STATUS_VIRUS_INFECTED, -EIO, "STATUS_VIRUS_INFECTED"}, + {STATUS_VIRUS_DELETED, -EIO, "STATUS_VIRUS_DELETED"}, + {STATUS_BAD_MCFG_TABLE, -EIO, "STATUS_BAD_MCFG_TABLE"}, + {STATUS_WOW_ASSERTION, -EIO, "STATUS_WOW_ASSERTION"}, + {STATUS_INVALID_SIGNATURE, -EIO, "STATUS_INVALID_SIGNATURE"}, + {STATUS_HMAC_NOT_SUPPORTED, -EIO, "STATUS_HMAC_NOT_SUPPORTED"}, + {STATUS_IPSEC_QUEUE_OVERFLOW, -EIO, "STATUS_IPSEC_QUEUE_OVERFLOW"}, + {STATUS_ND_QUEUE_OVERFLOW, -EIO, "STATUS_ND_QUEUE_OVERFLOW"}, + {STATUS_HOPLIMIT_EXCEEDED, -EIO, "STATUS_HOPLIMIT_EXCEEDED"}, + {STATUS_PROTOCOL_NOT_SUPPORTED, -EOPNOTSUPP, + "STATUS_PROTOCOL_NOT_SUPPORTED"}, + {STATUS_LOST_WRITEBEHIND_DATA_NETWORK_DISCONNECTED, -EIO, + "STATUS_LOST_WRITEBEHIND_DATA_NETWORK_DISCONNECTED"}, + {STATUS_LOST_WRITEBEHIND_DATA_NETWORK_SERVER_ERROR, -EIO, + "STATUS_LOST_WRITEBEHIND_DATA_NETWORK_SERVER_ERROR"}, + {STATUS_LOST_WRITEBEHIND_DATA_LOCAL_DISK_ERROR, -EIO, + "STATUS_LOST_WRITEBEHIND_DATA_LOCAL_DISK_ERROR"}, + {STATUS_XML_PARSE_ERROR, -EIO, "STATUS_XML_PARSE_ERROR"}, + {STATUS_XMLDSIG_ERROR, -EIO, "STATUS_XMLDSIG_ERROR"}, + {STATUS_WRONG_COMPARTMENT, -EIO, "STATUS_WRONG_COMPARTMENT"}, + {STATUS_AUTHIP_FAILURE, -EIO, "STATUS_AUTHIP_FAILURE"}, + {DBG_NO_STATE_CHANGE, -EIO, "DBG_NO_STATE_CHANGE"}, + {DBG_APP_NOT_IDLE, -EIO, "DBG_APP_NOT_IDLE"}, + {RPC_NT_INVALID_STRING_BINDING, -EIO, "RPC_NT_INVALID_STRING_BINDING"}, + {RPC_NT_WRONG_KIND_OF_BINDING, -EIO, "RPC_NT_WRONG_KIND_OF_BINDING"}, + {RPC_NT_INVALID_BINDING, -EIO, "RPC_NT_INVALID_BINDING"}, + {RPC_NT_PROTSEQ_NOT_SUPPORTED, -EOPNOTSUPP, + "RPC_NT_PROTSEQ_NOT_SUPPORTED"}, + {RPC_NT_INVALID_RPC_PROTSEQ, -EIO, "RPC_NT_INVALID_RPC_PROTSEQ"}, + {RPC_NT_INVALID_STRING_UUID, -EIO, "RPC_NT_INVALID_STRING_UUID"}, + {RPC_NT_INVALID_ENDPOINT_FORMAT, -EIO, + "RPC_NT_INVALID_ENDPOINT_FORMAT"}, + {RPC_NT_INVALID_NET_ADDR, -EIO, "RPC_NT_INVALID_NET_ADDR"}, + {RPC_NT_NO_ENDPOINT_FOUND, -EIO, "RPC_NT_NO_ENDPOINT_FOUND"}, + {RPC_NT_INVALID_TIMEOUT, -EINVAL, "RPC_NT_INVALID_TIMEOUT"}, + {RPC_NT_OBJECT_NOT_FOUND, -ENOENT, "RPC_NT_OBJECT_NOT_FOUND"}, + {RPC_NT_ALREADY_REGISTERED, -EIO, "RPC_NT_ALREADY_REGISTERED"}, + {RPC_NT_TYPE_ALREADY_REGISTERED, -EIO, + "RPC_NT_TYPE_ALREADY_REGISTERED"}, + {RPC_NT_ALREADY_LISTENING, -EIO, "RPC_NT_ALREADY_LISTENING"}, + {RPC_NT_NO_PROTSEQS_REGISTERED, -EIO, "RPC_NT_NO_PROTSEQS_REGISTERED"}, + {RPC_NT_NOT_LISTENING, -EIO, "RPC_NT_NOT_LISTENING"}, + {RPC_NT_UNKNOWN_MGR_TYPE, -EIO, "RPC_NT_UNKNOWN_MGR_TYPE"}, + {RPC_NT_UNKNOWN_IF, -EIO, "RPC_NT_UNKNOWN_IF"}, + {RPC_NT_NO_BINDINGS, -EIO, "RPC_NT_NO_BINDINGS"}, + {RPC_NT_NO_PROTSEQS, -EIO, "RPC_NT_NO_PROTSEQS"}, + {RPC_NT_CANT_CREATE_ENDPOINT, -EIO, "RPC_NT_CANT_CREATE_ENDPOINT"}, + {RPC_NT_OUT_OF_RESOURCES, -EIO, "RPC_NT_OUT_OF_RESOURCES"}, + {RPC_NT_SERVER_UNAVAILABLE, -EIO, "RPC_NT_SERVER_UNAVAILABLE"}, + {RPC_NT_SERVER_TOO_BUSY, -EBUSY, "RPC_NT_SERVER_TOO_BUSY"}, + {RPC_NT_INVALID_NETWORK_OPTIONS, -EIO, + "RPC_NT_INVALID_NETWORK_OPTIONS"}, + {RPC_NT_NO_CALL_ACTIVE, -EIO, "RPC_NT_NO_CALL_ACTIVE"}, + {RPC_NT_CALL_FAILED, -EIO, "RPC_NT_CALL_FAILED"}, + {RPC_NT_CALL_FAILED_DNE, -EIO, "RPC_NT_CALL_FAILED_DNE"}, + {RPC_NT_PROTOCOL_ERROR, -EIO, "RPC_NT_PROTOCOL_ERROR"}, + {RPC_NT_UNSUPPORTED_TRANS_SYN, -EIO, "RPC_NT_UNSUPPORTED_TRANS_SYN"}, + {RPC_NT_UNSUPPORTED_TYPE, -EIO, "RPC_NT_UNSUPPORTED_TYPE"}, + {RPC_NT_INVALID_TAG, -EIO, "RPC_NT_INVALID_TAG"}, + {RPC_NT_INVALID_BOUND, -EIO, "RPC_NT_INVALID_BOUND"}, + {RPC_NT_NO_ENTRY_NAME, -EIO, "RPC_NT_NO_ENTRY_NAME"}, + {RPC_NT_INVALID_NAME_SYNTAX, -EIO, "RPC_NT_INVALID_NAME_SYNTAX"}, + {RPC_NT_UNSUPPORTED_NAME_SYNTAX, -EIO, + "RPC_NT_UNSUPPORTED_NAME_SYNTAX"}, + {RPC_NT_UUID_NO_ADDRESS, -EIO, "RPC_NT_UUID_NO_ADDRESS"}, + {RPC_NT_DUPLICATE_ENDPOINT, -ENOTUNIQ, "RPC_NT_DUPLICATE_ENDPOINT"}, + {RPC_NT_UNKNOWN_AUTHN_TYPE, -EIO, "RPC_NT_UNKNOWN_AUTHN_TYPE"}, + {RPC_NT_MAX_CALLS_TOO_SMALL, -EIO, "RPC_NT_MAX_CALLS_TOO_SMALL"}, + {RPC_NT_STRING_TOO_LONG, -EIO, "RPC_NT_STRING_TOO_LONG"}, + {RPC_NT_PROTSEQ_NOT_FOUND, -EIO, "RPC_NT_PROTSEQ_NOT_FOUND"}, + {RPC_NT_PROCNUM_OUT_OF_RANGE, -EIO, "RPC_NT_PROCNUM_OUT_OF_RANGE"}, + {RPC_NT_BINDING_HAS_NO_AUTH, -EIO, "RPC_NT_BINDING_HAS_NO_AUTH"}, + {RPC_NT_UNKNOWN_AUTHN_SERVICE, -EIO, "RPC_NT_UNKNOWN_AUTHN_SERVICE"}, + {RPC_NT_UNKNOWN_AUTHN_LEVEL, -EIO, "RPC_NT_UNKNOWN_AUTHN_LEVEL"}, + {RPC_NT_INVALID_AUTH_IDENTITY, -EIO, "RPC_NT_INVALID_AUTH_IDENTITY"}, + {RPC_NT_UNKNOWN_AUTHZ_SERVICE, -EIO, "RPC_NT_UNKNOWN_AUTHZ_SERVICE"}, + {EPT_NT_INVALID_ENTRY, -EIO, "EPT_NT_INVALID_ENTRY"}, + {EPT_NT_CANT_PERFORM_OP, -EIO, "EPT_NT_CANT_PERFORM_OP"}, + {EPT_NT_NOT_REGISTERED, -EIO, "EPT_NT_NOT_REGISTERED"}, + {RPC_NT_NOTHING_TO_EXPORT, -EIO, "RPC_NT_NOTHING_TO_EXPORT"}, + {RPC_NT_INCOMPLETE_NAME, -EIO, "RPC_NT_INCOMPLETE_NAME"}, + {RPC_NT_INVALID_VERS_OPTION, -EIO, "RPC_NT_INVALID_VERS_OPTION"}, + {RPC_NT_NO_MORE_MEMBERS, -EIO, "RPC_NT_NO_MORE_MEMBERS"}, + {RPC_NT_NOT_ALL_OBJS_UNEXPORTED, -EIO, + "RPC_NT_NOT_ALL_OBJS_UNEXPORTED"}, + {RPC_NT_INTERFACE_NOT_FOUND, -EIO, "RPC_NT_INTERFACE_NOT_FOUND"}, + {RPC_NT_ENTRY_ALREADY_EXISTS, -EIO, "RPC_NT_ENTRY_ALREADY_EXISTS"}, + {RPC_NT_ENTRY_NOT_FOUND, -EIO, "RPC_NT_ENTRY_NOT_FOUND"}, + {RPC_NT_NAME_SERVICE_UNAVAILABLE, -EIO, + "RPC_NT_NAME_SERVICE_UNAVAILABLE"}, + {RPC_NT_INVALID_NAF_ID, -EIO, "RPC_NT_INVALID_NAF_ID"}, + {RPC_NT_CANNOT_SUPPORT, -EOPNOTSUPP, "RPC_NT_CANNOT_SUPPORT"}, + {RPC_NT_NO_CONTEXT_AVAILABLE, -EIO, "RPC_NT_NO_CONTEXT_AVAILABLE"}, + {RPC_NT_INTERNAL_ERROR, -EIO, "RPC_NT_INTERNAL_ERROR"}, + {RPC_NT_ZERO_DIVIDE, -EIO, "RPC_NT_ZERO_DIVIDE"}, + {RPC_NT_ADDRESS_ERROR, -EIO, "RPC_NT_ADDRESS_ERROR"}, + {RPC_NT_FP_DIV_ZERO, -EIO, "RPC_NT_FP_DIV_ZERO"}, + {RPC_NT_FP_UNDERFLOW, -EIO, "RPC_NT_FP_UNDERFLOW"}, + {RPC_NT_FP_OVERFLOW, -EIO, "RPC_NT_FP_OVERFLOW"}, + {RPC_NT_CALL_IN_PROGRESS, -EIO, "RPC_NT_CALL_IN_PROGRESS"}, + {RPC_NT_NO_MORE_BINDINGS, -EIO, "RPC_NT_NO_MORE_BINDINGS"}, + {RPC_NT_GROUP_MEMBER_NOT_FOUND, -EIO, "RPC_NT_GROUP_MEMBER_NOT_FOUND"}, + {EPT_NT_CANT_CREATE, -EIO, "EPT_NT_CANT_CREATE"}, + {RPC_NT_INVALID_OBJECT, -EIO, "RPC_NT_INVALID_OBJECT"}, + {RPC_NT_NO_INTERFACES, -EIO, "RPC_NT_NO_INTERFACES"}, + {RPC_NT_CALL_CANCELLED, -EIO, "RPC_NT_CALL_CANCELLED"}, + {RPC_NT_BINDING_INCOMPLETE, -EIO, "RPC_NT_BINDING_INCOMPLETE"}, + {RPC_NT_COMM_FAILURE, -EIO, "RPC_NT_COMM_FAILURE"}, + {RPC_NT_UNSUPPORTED_AUTHN_LEVEL, -EIO, + "RPC_NT_UNSUPPORTED_AUTHN_LEVEL"}, + {RPC_NT_NO_PRINC_NAME, -EIO, "RPC_NT_NO_PRINC_NAME"}, + {RPC_NT_NOT_RPC_ERROR, -EIO, "RPC_NT_NOT_RPC_ERROR"}, + {RPC_NT_SEC_PKG_ERROR, -EIO, "RPC_NT_SEC_PKG_ERROR"}, + {RPC_NT_NOT_CANCELLED, -EIO, "RPC_NT_NOT_CANCELLED"}, + {RPC_NT_INVALID_ASYNC_HANDLE, -EIO, "RPC_NT_INVALID_ASYNC_HANDLE"}, + {RPC_NT_INVALID_ASYNC_CALL, -EIO, "RPC_NT_INVALID_ASYNC_CALL"}, + {RPC_NT_PROXY_ACCESS_DENIED, -EACCES, "RPC_NT_PROXY_ACCESS_DENIED"}, + {RPC_NT_NO_MORE_ENTRIES, -EIO, "RPC_NT_NO_MORE_ENTRIES"}, + {RPC_NT_SS_CHAR_TRANS_OPEN_FAIL, -EIO, + "RPC_NT_SS_CHAR_TRANS_OPEN_FAIL"}, + {RPC_NT_SS_CHAR_TRANS_SHORT_FILE, -EIO, + "RPC_NT_SS_CHAR_TRANS_SHORT_FILE"}, + {RPC_NT_SS_IN_NULL_CONTEXT, -EIO, "RPC_NT_SS_IN_NULL_CONTEXT"}, + {RPC_NT_SS_CONTEXT_MISMATCH, -EIO, "RPC_NT_SS_CONTEXT_MISMATCH"}, + {RPC_NT_SS_CONTEXT_DAMAGED, -EIO, "RPC_NT_SS_CONTEXT_DAMAGED"}, + {RPC_NT_SS_HANDLES_MISMATCH, -EIO, "RPC_NT_SS_HANDLES_MISMATCH"}, + {RPC_NT_SS_CANNOT_GET_CALL_HANDLE, -EIO, + "RPC_NT_SS_CANNOT_GET_CALL_HANDLE"}, + {RPC_NT_NULL_REF_POINTER, -EIO, "RPC_NT_NULL_REF_POINTER"}, + {RPC_NT_ENUM_VALUE_OUT_OF_RANGE, -EIO, + "RPC_NT_ENUM_VALUE_OUT_OF_RANGE"}, + {RPC_NT_BYTE_COUNT_TOO_SMALL, -EIO, "RPC_NT_BYTE_COUNT_TOO_SMALL"}, + {RPC_NT_BAD_STUB_DATA, -EIO, "RPC_NT_BAD_STUB_DATA"}, + {RPC_NT_INVALID_ES_ACTION, -EIO, "RPC_NT_INVALID_ES_ACTION"}, + {RPC_NT_WRONG_ES_VERSION, -EIO, "RPC_NT_WRONG_ES_VERSION"}, + {RPC_NT_WRONG_STUB_VERSION, -EIO, "RPC_NT_WRONG_STUB_VERSION"}, + {RPC_NT_INVALID_PIPE_OBJECT, -EIO, "RPC_NT_INVALID_PIPE_OBJECT"}, + {RPC_NT_INVALID_PIPE_OPERATION, -EIO, "RPC_NT_INVALID_PIPE_OPERATION"}, + {RPC_NT_WRONG_PIPE_VERSION, -EIO, "RPC_NT_WRONG_PIPE_VERSION"}, + {RPC_NT_PIPE_CLOSED, -EIO, "RPC_NT_PIPE_CLOSED"}, + {RPC_NT_PIPE_DISCIPLINE_ERROR, -EIO, "RPC_NT_PIPE_DISCIPLINE_ERROR"}, + {RPC_NT_PIPE_EMPTY, -EIO, "RPC_NT_PIPE_EMPTY"}, + {STATUS_PNP_BAD_MPS_TABLE, -EIO, "STATUS_PNP_BAD_MPS_TABLE"}, + {STATUS_PNP_TRANSLATION_FAILED, -EIO, "STATUS_PNP_TRANSLATION_FAILED"}, + {STATUS_PNP_IRQ_TRANSLATION_FAILED, -EIO, + "STATUS_PNP_IRQ_TRANSLATION_FAILED"}, + {STATUS_PNP_INVALID_ID, -EIO, "STATUS_PNP_INVALID_ID"}, + {STATUS_IO_REISSUE_AS_CACHED, -EIO, "STATUS_IO_REISSUE_AS_CACHED"}, + {STATUS_CTX_WINSTATION_NAME_INVALID, -EIO, + "STATUS_CTX_WINSTATION_NAME_INVALID"}, + {STATUS_CTX_INVALID_PD, -EIO, "STATUS_CTX_INVALID_PD"}, + {STATUS_CTX_PD_NOT_FOUND, -EIO, "STATUS_CTX_PD_NOT_FOUND"}, + {STATUS_CTX_CLOSE_PENDING, -EIO, "STATUS_CTX_CLOSE_PENDING"}, + {STATUS_CTX_NO_OUTBUF, -EIO, "STATUS_CTX_NO_OUTBUF"}, + {STATUS_CTX_MODEM_INF_NOT_FOUND, -EIO, + "STATUS_CTX_MODEM_INF_NOT_FOUND"}, + {STATUS_CTX_INVALID_MODEMNAME, -EIO, "STATUS_CTX_INVALID_MODEMNAME"}, + {STATUS_CTX_RESPONSE_ERROR, -EIO, "STATUS_CTX_RESPONSE_ERROR"}, + {STATUS_CTX_MODEM_RESPONSE_TIMEOUT, -ETIMEDOUT, + "STATUS_CTX_MODEM_RESPONSE_TIMEOUT"}, + {STATUS_CTX_MODEM_RESPONSE_NO_CARRIER, -EIO, + "STATUS_CTX_MODEM_RESPONSE_NO_CARRIER"}, + {STATUS_CTX_MODEM_RESPONSE_NO_DIALTONE, -EIO, + "STATUS_CTX_MODEM_RESPONSE_NO_DIALTONE"}, + {STATUS_CTX_MODEM_RESPONSE_BUSY, -EBUSY, + "STATUS_CTX_MODEM_RESPONSE_BUSY"}, + {STATUS_CTX_MODEM_RESPONSE_VOICE, -EIO, + "STATUS_CTX_MODEM_RESPONSE_VOICE"}, + {STATUS_CTX_TD_ERROR, -EIO, "STATUS_CTX_TD_ERROR"}, + {STATUS_CTX_LICENSE_CLIENT_INVALID, -EIO, + "STATUS_CTX_LICENSE_CLIENT_INVALID"}, + {STATUS_CTX_LICENSE_NOT_AVAILABLE, -EIO, + "STATUS_CTX_LICENSE_NOT_AVAILABLE"}, + {STATUS_CTX_LICENSE_EXPIRED, -EIO, "STATUS_CTX_LICENSE_EXPIRED"}, + {STATUS_CTX_WINSTATION_NOT_FOUND, -EIO, + "STATUS_CTX_WINSTATION_NOT_FOUND"}, + {STATUS_CTX_WINSTATION_NAME_COLLISION, -EIO, + "STATUS_CTX_WINSTATION_NAME_COLLISION"}, + {STATUS_CTX_WINSTATION_BUSY, -EBUSY, "STATUS_CTX_WINSTATION_BUSY"}, + {STATUS_CTX_BAD_VIDEO_MODE, -EIO, "STATUS_CTX_BAD_VIDEO_MODE"}, + {STATUS_CTX_GRAPHICS_INVALID, -EIO, "STATUS_CTX_GRAPHICS_INVALID"}, + {STATUS_CTX_NOT_CONSOLE, -EIO, "STATUS_CTX_NOT_CONSOLE"}, + {STATUS_CTX_CLIENT_QUERY_TIMEOUT, -EIO, + "STATUS_CTX_CLIENT_QUERY_TIMEOUT"}, + {STATUS_CTX_CONSOLE_DISCONNECT, -EIO, "STATUS_CTX_CONSOLE_DISCONNECT"}, + {STATUS_CTX_CONSOLE_CONNECT, -EIO, "STATUS_CTX_CONSOLE_CONNECT"}, + {STATUS_CTX_SHADOW_DENIED, -EIO, "STATUS_CTX_SHADOW_DENIED"}, + {STATUS_CTX_WINSTATION_ACCESS_DENIED, -EACCES, + "STATUS_CTX_WINSTATION_ACCESS_DENIED"}, + {STATUS_CTX_INVALID_WD, -EIO, "STATUS_CTX_INVALID_WD"}, + {STATUS_CTX_WD_NOT_FOUND, -EIO, "STATUS_CTX_WD_NOT_FOUND"}, + {STATUS_CTX_SHADOW_INVALID, -EIO, "STATUS_CTX_SHADOW_INVALID"}, + {STATUS_CTX_SHADOW_DISABLED, -EIO, "STATUS_CTX_SHADOW_DISABLED"}, + {STATUS_RDP_PROTOCOL_ERROR, -EIO, "STATUS_RDP_PROTOCOL_ERROR"}, + {STATUS_CTX_CLIENT_LICENSE_NOT_SET, -EIO, + "STATUS_CTX_CLIENT_LICENSE_NOT_SET"}, + {STATUS_CTX_CLIENT_LICENSE_IN_USE, -EIO, + "STATUS_CTX_CLIENT_LICENSE_IN_USE"}, + {STATUS_CTX_SHADOW_ENDED_BY_MODE_CHANGE, -EIO, + "STATUS_CTX_SHADOW_ENDED_BY_MODE_CHANGE"}, + {STATUS_CTX_SHADOW_NOT_RUNNING, -EIO, "STATUS_CTX_SHADOW_NOT_RUNNING"}, + {STATUS_CTX_LOGON_DISABLED, -EIO, "STATUS_CTX_LOGON_DISABLED"}, + {STATUS_CTX_SECURITY_LAYER_ERROR, -EIO, + "STATUS_CTX_SECURITY_LAYER_ERROR"}, + {STATUS_TS_INCOMPATIBLE_SESSIONS, -EIO, + "STATUS_TS_INCOMPATIBLE_SESSIONS"}, + {STATUS_MUI_FILE_NOT_FOUND, -EIO, "STATUS_MUI_FILE_NOT_FOUND"}, + {STATUS_MUI_INVALID_FILE, -EIO, "STATUS_MUI_INVALID_FILE"}, + {STATUS_MUI_INVALID_RC_CONFIG, -EIO, "STATUS_MUI_INVALID_RC_CONFIG"}, + {STATUS_MUI_INVALID_LOCALE_NAME, -EIO, + "STATUS_MUI_INVALID_LOCALE_NAME"}, + {STATUS_MUI_INVALID_ULTIMATEFALLBACK_NAME, -EIO, + "STATUS_MUI_INVALID_ULTIMATEFALLBACK_NAME"}, + {STATUS_MUI_FILE_NOT_LOADED, -EIO, "STATUS_MUI_FILE_NOT_LOADED"}, + {STATUS_RESOURCE_ENUM_USER_STOP, -EIO, + "STATUS_RESOURCE_ENUM_USER_STOP"}, + {STATUS_CLUSTER_INVALID_NODE, -EIO, "STATUS_CLUSTER_INVALID_NODE"}, + {STATUS_CLUSTER_NODE_EXISTS, -EIO, "STATUS_CLUSTER_NODE_EXISTS"}, + {STATUS_CLUSTER_JOIN_IN_PROGRESS, -EIO, + "STATUS_CLUSTER_JOIN_IN_PROGRESS"}, + {STATUS_CLUSTER_NODE_NOT_FOUND, -EIO, "STATUS_CLUSTER_NODE_NOT_FOUND"}, + {STATUS_CLUSTER_LOCAL_NODE_NOT_FOUND, -EIO, + "STATUS_CLUSTER_LOCAL_NODE_NOT_FOUND"}, + {STATUS_CLUSTER_NETWORK_EXISTS, -EIO, "STATUS_CLUSTER_NETWORK_EXISTS"}, + {STATUS_CLUSTER_NETWORK_NOT_FOUND, -EIO, + "STATUS_CLUSTER_NETWORK_NOT_FOUND"}, + {STATUS_CLUSTER_NETINTERFACE_EXISTS, -EIO, + "STATUS_CLUSTER_NETINTERFACE_EXISTS"}, + {STATUS_CLUSTER_NETINTERFACE_NOT_FOUND, -EIO, + "STATUS_CLUSTER_NETINTERFACE_NOT_FOUND"}, + {STATUS_CLUSTER_INVALID_REQUEST, -EIO, + "STATUS_CLUSTER_INVALID_REQUEST"}, + {STATUS_CLUSTER_INVALID_NETWORK_PROVIDER, -EIO, + "STATUS_CLUSTER_INVALID_NETWORK_PROVIDER"}, + {STATUS_CLUSTER_NODE_DOWN, -EIO, "STATUS_CLUSTER_NODE_DOWN"}, + {STATUS_CLUSTER_NODE_UNREACHABLE, -EIO, + "STATUS_CLUSTER_NODE_UNREACHABLE"}, + {STATUS_CLUSTER_NODE_NOT_MEMBER, -EIO, + "STATUS_CLUSTER_NODE_NOT_MEMBER"}, + {STATUS_CLUSTER_JOIN_NOT_IN_PROGRESS, -EIO, + "STATUS_CLUSTER_JOIN_NOT_IN_PROGRESS"}, + {STATUS_CLUSTER_INVALID_NETWORK, -EIO, + "STATUS_CLUSTER_INVALID_NETWORK"}, + {STATUS_CLUSTER_NO_NET_ADAPTERS, -EIO, + "STATUS_CLUSTER_NO_NET_ADAPTERS"}, + {STATUS_CLUSTER_NODE_UP, -EIO, "STATUS_CLUSTER_NODE_UP"}, + {STATUS_CLUSTER_NODE_PAUSED, -EIO, "STATUS_CLUSTER_NODE_PAUSED"}, + {STATUS_CLUSTER_NODE_NOT_PAUSED, -EIO, + "STATUS_CLUSTER_NODE_NOT_PAUSED"}, + {STATUS_CLUSTER_NO_SECURITY_CONTEXT, -EIO, + "STATUS_CLUSTER_NO_SECURITY_CONTEXT"}, + {STATUS_CLUSTER_NETWORK_NOT_INTERNAL, -EIO, + "STATUS_CLUSTER_NETWORK_NOT_INTERNAL"}, + {STATUS_CLUSTER_POISONED, -EIO, "STATUS_CLUSTER_POISONED"}, + {STATUS_ACPI_INVALID_OPCODE, -EIO, "STATUS_ACPI_INVALID_OPCODE"}, + {STATUS_ACPI_STACK_OVERFLOW, -EIO, "STATUS_ACPI_STACK_OVERFLOW"}, + {STATUS_ACPI_ASSERT_FAILED, -EIO, "STATUS_ACPI_ASSERT_FAILED"}, + {STATUS_ACPI_INVALID_INDEX, -EIO, "STATUS_ACPI_INVALID_INDEX"}, + {STATUS_ACPI_INVALID_ARGUMENT, -EIO, "STATUS_ACPI_INVALID_ARGUMENT"}, + {STATUS_ACPI_FATAL, -EIO, "STATUS_ACPI_FATAL"}, + {STATUS_ACPI_INVALID_SUPERNAME, -EIO, "STATUS_ACPI_INVALID_SUPERNAME"}, + {STATUS_ACPI_INVALID_ARGTYPE, -EIO, "STATUS_ACPI_INVALID_ARGTYPE"}, + {STATUS_ACPI_INVALID_OBJTYPE, -EIO, "STATUS_ACPI_INVALID_OBJTYPE"}, + {STATUS_ACPI_INVALID_TARGETTYPE, -EIO, + "STATUS_ACPI_INVALID_TARGETTYPE"}, + {STATUS_ACPI_INCORRECT_ARGUMENT_COUNT, -EIO, + "STATUS_ACPI_INCORRECT_ARGUMENT_COUNT"}, + {STATUS_ACPI_ADDRESS_NOT_MAPPED, -EIO, + "STATUS_ACPI_ADDRESS_NOT_MAPPED"}, + {STATUS_ACPI_INVALID_EVENTTYPE, -EIO, "STATUS_ACPI_INVALID_EVENTTYPE"}, + {STATUS_ACPI_HANDLER_COLLISION, -EIO, "STATUS_ACPI_HANDLER_COLLISION"}, + {STATUS_ACPI_INVALID_DATA, -EIO, "STATUS_ACPI_INVALID_DATA"}, + {STATUS_ACPI_INVALID_REGION, -EIO, "STATUS_ACPI_INVALID_REGION"}, + {STATUS_ACPI_INVALID_ACCESS_SIZE, -EIO, + "STATUS_ACPI_INVALID_ACCESS_SIZE"}, + {STATUS_ACPI_ACQUIRE_GLOBAL_LOCK, -EIO, + "STATUS_ACPI_ACQUIRE_GLOBAL_LOCK"}, + {STATUS_ACPI_ALREADY_INITIALIZED, -EIO, + "STATUS_ACPI_ALREADY_INITIALIZED"}, + {STATUS_ACPI_NOT_INITIALIZED, -EIO, "STATUS_ACPI_NOT_INITIALIZED"}, + {STATUS_ACPI_INVALID_MUTEX_LEVEL, -EIO, + "STATUS_ACPI_INVALID_MUTEX_LEVEL"}, + {STATUS_ACPI_MUTEX_NOT_OWNED, -EIO, "STATUS_ACPI_MUTEX_NOT_OWNED"}, + {STATUS_ACPI_MUTEX_NOT_OWNER, -EIO, "STATUS_ACPI_MUTEX_NOT_OWNER"}, + {STATUS_ACPI_RS_ACCESS, -EIO, "STATUS_ACPI_RS_ACCESS"}, + {STATUS_ACPI_INVALID_TABLE, -EIO, "STATUS_ACPI_INVALID_TABLE"}, + {STATUS_ACPI_REG_HANDLER_FAILED, -EIO, + "STATUS_ACPI_REG_HANDLER_FAILED"}, + {STATUS_ACPI_POWER_REQUEST_FAILED, -EIO, + "STATUS_ACPI_POWER_REQUEST_FAILED"}, + {STATUS_SXS_SECTION_NOT_FOUND, -EIO, "STATUS_SXS_SECTION_NOT_FOUND"}, + {STATUS_SXS_CANT_GEN_ACTCTX, -EIO, "STATUS_SXS_CANT_GEN_ACTCTX"}, + {STATUS_SXS_INVALID_ACTCTXDATA_FORMAT, -EIO, + "STATUS_SXS_INVALID_ACTCTXDATA_FORMAT"}, + {STATUS_SXS_ASSEMBLY_NOT_FOUND, -EIO, "STATUS_SXS_ASSEMBLY_NOT_FOUND"}, + {STATUS_SXS_MANIFEST_FORMAT_ERROR, -EIO, + "STATUS_SXS_MANIFEST_FORMAT_ERROR"}, + {STATUS_SXS_MANIFEST_PARSE_ERROR, -EIO, + "STATUS_SXS_MANIFEST_PARSE_ERROR"}, + {STATUS_SXS_ACTIVATION_CONTEXT_DISABLED, -EIO, + "STATUS_SXS_ACTIVATION_CONTEXT_DISABLED"}, + {STATUS_SXS_KEY_NOT_FOUND, -EIO, "STATUS_SXS_KEY_NOT_FOUND"}, + {STATUS_SXS_VERSION_CONFLICT, -EIO, "STATUS_SXS_VERSION_CONFLICT"}, + {STATUS_SXS_WRONG_SECTION_TYPE, -EIO, "STATUS_SXS_WRONG_SECTION_TYPE"}, + {STATUS_SXS_THREAD_QUERIES_DISABLED, -EIO, + "STATUS_SXS_THREAD_QUERIES_DISABLED"}, + {STATUS_SXS_ASSEMBLY_MISSING, -EIO, "STATUS_SXS_ASSEMBLY_MISSING"}, + {STATUS_SXS_PROCESS_DEFAULT_ALREADY_SET, -EIO, + "STATUS_SXS_PROCESS_DEFAULT_ALREADY_SET"}, + {STATUS_SXS_EARLY_DEACTIVATION, -EIO, "STATUS_SXS_EARLY_DEACTIVATION"}, + {STATUS_SXS_INVALID_DEACTIVATION, -EIO, + "STATUS_SXS_INVALID_DEACTIVATION"}, + {STATUS_SXS_MULTIPLE_DEACTIVATION, -EIO, + "STATUS_SXS_MULTIPLE_DEACTIVATION"}, + {STATUS_SXS_SYSTEM_DEFAULT_ACTIVATION_CONTEXT_EMPTY, -EIO, + "STATUS_SXS_SYSTEM_DEFAULT_ACTIVATION_CONTEXT_EMPTY"}, + {STATUS_SXS_PROCESS_TERMINATION_REQUESTED, -EIO, + "STATUS_SXS_PROCESS_TERMINATION_REQUESTED"}, + {STATUS_SXS_CORRUPT_ACTIVATION_STACK, -EIO, + "STATUS_SXS_CORRUPT_ACTIVATION_STACK"}, + {STATUS_SXS_CORRUPTION, -EIO, "STATUS_SXS_CORRUPTION"}, + {STATUS_SXS_INVALID_IDENTITY_ATTRIBUTE_VALUE, -EIO, + "STATUS_SXS_INVALID_IDENTITY_ATTRIBUTE_VALUE"}, + {STATUS_SXS_INVALID_IDENTITY_ATTRIBUTE_NAME, -EIO, + "STATUS_SXS_INVALID_IDENTITY_ATTRIBUTE_NAME"}, + {STATUS_SXS_IDENTITY_DUPLICATE_ATTRIBUTE, -EIO, + "STATUS_SXS_IDENTITY_DUPLICATE_ATTRIBUTE"}, + {STATUS_SXS_IDENTITY_PARSE_ERROR, -EIO, + "STATUS_SXS_IDENTITY_PARSE_ERROR"}, + {STATUS_SXS_COMPONENT_STORE_CORRUPT, -EIO, + "STATUS_SXS_COMPONENT_STORE_CORRUPT"}, + {STATUS_SXS_FILE_HASH_MISMATCH, -EIO, "STATUS_SXS_FILE_HASH_MISMATCH"}, + {STATUS_SXS_MANIFEST_IDENTITY_SAME_BUT_CONTENTS_DIFFERENT, -EIO, + "STATUS_SXS_MANIFEST_IDENTITY_SAME_BUT_CONTENTS_DIFFERENT"}, + {STATUS_SXS_IDENTITIES_DIFFERENT, -EIO, + "STATUS_SXS_IDENTITIES_DIFFERENT"}, + {STATUS_SXS_ASSEMBLY_IS_NOT_A_DEPLOYMENT, -EIO, + "STATUS_SXS_ASSEMBLY_IS_NOT_A_DEPLOYMENT"}, + {STATUS_SXS_FILE_NOT_PART_OF_ASSEMBLY, -EIO, + "STATUS_SXS_FILE_NOT_PART_OF_ASSEMBLY"}, + {STATUS_ADVANCED_INSTALLER_FAILED, -EIO, + "STATUS_ADVANCED_INSTALLER_FAILED"}, + {STATUS_XML_ENCODING_MISMATCH, -EIO, "STATUS_XML_ENCODING_MISMATCH"}, + {STATUS_SXS_MANIFEST_TOO_BIG, -EIO, "STATUS_SXS_MANIFEST_TOO_BIG"}, + {STATUS_SXS_SETTING_NOT_REGISTERED, -EIO, + "STATUS_SXS_SETTING_NOT_REGISTERED"}, + {STATUS_SXS_TRANSACTION_CLOSURE_INCOMPLETE, -EIO, + "STATUS_SXS_TRANSACTION_CLOSURE_INCOMPLETE"}, + {STATUS_SMI_PRIMITIVE_INSTALLER_FAILED, -EIO, + "STATUS_SMI_PRIMITIVE_INSTALLER_FAILED"}, + {STATUS_GENERIC_COMMAND_FAILED, -EIO, "STATUS_GENERIC_COMMAND_FAILED"}, + {STATUS_SXS_FILE_HASH_MISSING, -EIO, "STATUS_SXS_FILE_HASH_MISSING"}, + {STATUS_TRANSACTIONAL_CONFLICT, -EIO, "STATUS_TRANSACTIONAL_CONFLICT"}, + {STATUS_INVALID_TRANSACTION, -EIO, "STATUS_INVALID_TRANSACTION"}, + {STATUS_TRANSACTION_NOT_ACTIVE, -EIO, "STATUS_TRANSACTION_NOT_ACTIVE"}, + {STATUS_TM_INITIALIZATION_FAILED, -EIO, + "STATUS_TM_INITIALIZATION_FAILED"}, + {STATUS_RM_NOT_ACTIVE, -EIO, "STATUS_RM_NOT_ACTIVE"}, + {STATUS_RM_METADATA_CORRUPT, -EIO, "STATUS_RM_METADATA_CORRUPT"}, + {STATUS_TRANSACTION_NOT_JOINED, -EIO, "STATUS_TRANSACTION_NOT_JOINED"}, + {STATUS_DIRECTORY_NOT_RM, -EIO, "STATUS_DIRECTORY_NOT_RM"}, + {STATUS_TRANSACTIONS_UNSUPPORTED_REMOTE, -EIO, + "STATUS_TRANSACTIONS_UNSUPPORTED_REMOTE"}, + {STATUS_LOG_RESIZE_INVALID_SIZE, -EIO, + "STATUS_LOG_RESIZE_INVALID_SIZE"}, + {STATUS_REMOTE_FILE_VERSION_MISMATCH, -EIO, + "STATUS_REMOTE_FILE_VERSION_MISMATCH"}, + {STATUS_CRM_PROTOCOL_ALREADY_EXISTS, -EIO, + "STATUS_CRM_PROTOCOL_ALREADY_EXISTS"}, + {STATUS_TRANSACTION_PROPAGATION_FAILED, -EIO, + "STATUS_TRANSACTION_PROPAGATION_FAILED"}, + {STATUS_CRM_PROTOCOL_NOT_FOUND, -EIO, "STATUS_CRM_PROTOCOL_NOT_FOUND"}, + {STATUS_TRANSACTION_SUPERIOR_EXISTS, -EIO, + "STATUS_TRANSACTION_SUPERIOR_EXISTS"}, + {STATUS_TRANSACTION_REQUEST_NOT_VALID, -EIO, + "STATUS_TRANSACTION_REQUEST_NOT_VALID"}, + {STATUS_TRANSACTION_NOT_REQUESTED, -EIO, + "STATUS_TRANSACTION_NOT_REQUESTED"}, + {STATUS_TRANSACTION_ALREADY_ABORTED, -EIO, + "STATUS_TRANSACTION_ALREADY_ABORTED"}, + {STATUS_TRANSACTION_ALREADY_COMMITTED, -EIO, + "STATUS_TRANSACTION_ALREADY_COMMITTED"}, + {STATUS_TRANSACTION_INVALID_MARSHALL_BUFFER, -EIO, + "STATUS_TRANSACTION_INVALID_MARSHALL_BUFFER"}, + {STATUS_CURRENT_TRANSACTION_NOT_VALID, -EIO, + "STATUS_CURRENT_TRANSACTION_NOT_VALID"}, + {STATUS_LOG_GROWTH_FAILED, -EIO, "STATUS_LOG_GROWTH_FAILED"}, + {STATUS_OBJECT_NO_LONGER_EXISTS, -EIO, + "STATUS_OBJECT_NO_LONGER_EXISTS"}, + {STATUS_STREAM_MINIVERSION_NOT_FOUND, -EIO, + "STATUS_STREAM_MINIVERSION_NOT_FOUND"}, + {STATUS_STREAM_MINIVERSION_NOT_VALID, -EIO, + "STATUS_STREAM_MINIVERSION_NOT_VALID"}, + {STATUS_MINIVERSION_INACCESSIBLE_FROM_SPECIFIED_TRANSACTION, -EIO, + "STATUS_MINIVERSION_INACCESSIBLE_FROM_SPECIFIED_TRANSACTION"}, + {STATUS_CANT_OPEN_MINIVERSION_WITH_MODIFY_INTENT, -EIO, + "STATUS_CANT_OPEN_MINIVERSION_WITH_MODIFY_INTENT"}, + {STATUS_CANT_CREATE_MORE_STREAM_MINIVERSIONS, -EIO, + "STATUS_CANT_CREATE_MORE_STREAM_MINIVERSIONS"}, + {STATUS_HANDLE_NO_LONGER_VALID, -EIO, "STATUS_HANDLE_NO_LONGER_VALID"}, + {STATUS_LOG_CORRUPTION_DETECTED, -EIO, + "STATUS_LOG_CORRUPTION_DETECTED"}, + {STATUS_RM_DISCONNECTED, -EIO, "STATUS_RM_DISCONNECTED"}, + {STATUS_ENLISTMENT_NOT_SUPERIOR, -EIO, + "STATUS_ENLISTMENT_NOT_SUPERIOR"}, + {STATUS_FILE_IDENTITY_NOT_PERSISTENT, -EIO, + "STATUS_FILE_IDENTITY_NOT_PERSISTENT"}, + {STATUS_CANT_BREAK_TRANSACTIONAL_DEPENDENCY, -EIO, + "STATUS_CANT_BREAK_TRANSACTIONAL_DEPENDENCY"}, + {STATUS_CANT_CROSS_RM_BOUNDARY, -EIO, "STATUS_CANT_CROSS_RM_BOUNDARY"}, + {STATUS_TXF_DIR_NOT_EMPTY, -EIO, "STATUS_TXF_DIR_NOT_EMPTY"}, + {STATUS_INDOUBT_TRANSACTIONS_EXIST, -EIO, + "STATUS_INDOUBT_TRANSACTIONS_EXIST"}, + {STATUS_TM_VOLATILE, -EIO, "STATUS_TM_VOLATILE"}, + {STATUS_ROLLBACK_TIMER_EXPIRED, -EIO, "STATUS_ROLLBACK_TIMER_EXPIRED"}, + {STATUS_TXF_ATTRIBUTE_CORRUPT, -EIO, "STATUS_TXF_ATTRIBUTE_CORRUPT"}, + {STATUS_EFS_NOT_ALLOWED_IN_TRANSACTION, -EIO, + "STATUS_EFS_NOT_ALLOWED_IN_TRANSACTION"}, + {STATUS_TRANSACTIONAL_OPEN_NOT_ALLOWED, -EIO, + "STATUS_TRANSACTIONAL_OPEN_NOT_ALLOWED"}, + {STATUS_TRANSACTED_MAPPING_UNSUPPORTED_REMOTE, -EIO, + "STATUS_TRANSACTED_MAPPING_UNSUPPORTED_REMOTE"}, + {STATUS_TRANSACTION_REQUIRED_PROMOTION, -EIO, + "STATUS_TRANSACTION_REQUIRED_PROMOTION"}, + {STATUS_CANNOT_EXECUTE_FILE_IN_TRANSACTION, -EIO, + "STATUS_CANNOT_EXECUTE_FILE_IN_TRANSACTION"}, + {STATUS_TRANSACTIONS_NOT_FROZEN, -EIO, + "STATUS_TRANSACTIONS_NOT_FROZEN"}, + {STATUS_TRANSACTION_FREEZE_IN_PROGRESS, -EIO, + "STATUS_TRANSACTION_FREEZE_IN_PROGRESS"}, + {STATUS_NOT_SNAPSHOT_VOLUME, -EIO, "STATUS_NOT_SNAPSHOT_VOLUME"}, + {STATUS_NO_SAVEPOINT_WITH_OPEN_FILES, -EIO, + "STATUS_NO_SAVEPOINT_WITH_OPEN_FILES"}, + {STATUS_SPARSE_NOT_ALLOWED_IN_TRANSACTION, -EIO, + "STATUS_SPARSE_NOT_ALLOWED_IN_TRANSACTION"}, + {STATUS_TM_IDENTITY_MISMATCH, -EIO, "STATUS_TM_IDENTITY_MISMATCH"}, + {STATUS_FLOATED_SECTION, -EIO, "STATUS_FLOATED_SECTION"}, + {STATUS_CANNOT_ACCEPT_TRANSACTED_WORK, -EIO, + "STATUS_CANNOT_ACCEPT_TRANSACTED_WORK"}, + {STATUS_CANNOT_ABORT_TRANSACTIONS, -EIO, + "STATUS_CANNOT_ABORT_TRANSACTIONS"}, + {STATUS_TRANSACTION_NOT_FOUND, -EIO, "STATUS_TRANSACTION_NOT_FOUND"}, + {STATUS_RESOURCEMANAGER_NOT_FOUND, -EIO, + "STATUS_RESOURCEMANAGER_NOT_FOUND"}, + {STATUS_ENLISTMENT_NOT_FOUND, -EIO, "STATUS_ENLISTMENT_NOT_FOUND"}, + {STATUS_TRANSACTIONMANAGER_NOT_FOUND, -EIO, + "STATUS_TRANSACTIONMANAGER_NOT_FOUND"}, + {STATUS_TRANSACTIONMANAGER_NOT_ONLINE, -EIO, + "STATUS_TRANSACTIONMANAGER_NOT_ONLINE"}, + {STATUS_TRANSACTIONMANAGER_RECOVERY_NAME_COLLISION, -EIO, + "STATUS_TRANSACTIONMANAGER_RECOVERY_NAME_COLLISION"}, + {STATUS_TRANSACTION_NOT_ROOT, -EIO, "STATUS_TRANSACTION_NOT_ROOT"}, + {STATUS_TRANSACTION_OBJECT_EXPIRED, -EIO, + "STATUS_TRANSACTION_OBJECT_EXPIRED"}, + {STATUS_COMPRESSION_NOT_ALLOWED_IN_TRANSACTION, -EIO, + "STATUS_COMPRESSION_NOT_ALLOWED_IN_TRANSACTION"}, + {STATUS_TRANSACTION_RESPONSE_NOT_ENLISTED, -EIO, + "STATUS_TRANSACTION_RESPONSE_NOT_ENLISTED"}, + {STATUS_TRANSACTION_RECORD_TOO_LONG, -EIO, + "STATUS_TRANSACTION_RECORD_TOO_LONG"}, + {STATUS_NO_LINK_TRACKING_IN_TRANSACTION, -EIO, + "STATUS_NO_LINK_TRACKING_IN_TRANSACTION"}, + {STATUS_OPERATION_NOT_SUPPORTED_IN_TRANSACTION, -EOPNOTSUPP, + "STATUS_OPERATION_NOT_SUPPORTED_IN_TRANSACTION"}, + {STATUS_TRANSACTION_INTEGRITY_VIOLATED, -EIO, + "STATUS_TRANSACTION_INTEGRITY_VIOLATED"}, + {STATUS_LOG_SECTOR_INVALID, -EIO, "STATUS_LOG_SECTOR_INVALID"}, + {STATUS_LOG_SECTOR_PARITY_INVALID, -EIO, + "STATUS_LOG_SECTOR_PARITY_INVALID"}, + {STATUS_LOG_SECTOR_REMAPPED, -EIO, "STATUS_LOG_SECTOR_REMAPPED"}, + {STATUS_LOG_BLOCK_INCOMPLETE, -EIO, "STATUS_LOG_BLOCK_INCOMPLETE"}, + {STATUS_LOG_INVALID_RANGE, -EIO, "STATUS_LOG_INVALID_RANGE"}, + {STATUS_LOG_BLOCKS_EXHAUSTED, -EIO, "STATUS_LOG_BLOCKS_EXHAUSTED"}, + {STATUS_LOG_READ_CONTEXT_INVALID, -EIO, + "STATUS_LOG_READ_CONTEXT_INVALID"}, + {STATUS_LOG_RESTART_INVALID, -EIO, "STATUS_LOG_RESTART_INVALID"}, + {STATUS_LOG_BLOCK_VERSION, -EIO, "STATUS_LOG_BLOCK_VERSION"}, + {STATUS_LOG_BLOCK_INVALID, -EIO, "STATUS_LOG_BLOCK_INVALID"}, + {STATUS_LOG_READ_MODE_INVALID, -EIO, "STATUS_LOG_READ_MODE_INVALID"}, + {STATUS_LOG_METADATA_CORRUPT, -EIO, "STATUS_LOG_METADATA_CORRUPT"}, + {STATUS_LOG_METADATA_INVALID, -EIO, "STATUS_LOG_METADATA_INVALID"}, + {STATUS_LOG_METADATA_INCONSISTENT, -EIO, + "STATUS_LOG_METADATA_INCONSISTENT"}, + {STATUS_LOG_RESERVATION_INVALID, -EIO, + "STATUS_LOG_RESERVATION_INVALID"}, + {STATUS_LOG_CANT_DELETE, -EIO, "STATUS_LOG_CANT_DELETE"}, + {STATUS_LOG_CONTAINER_LIMIT_EXCEEDED, -EIO, + "STATUS_LOG_CONTAINER_LIMIT_EXCEEDED"}, + {STATUS_LOG_START_OF_LOG, -EIO, "STATUS_LOG_START_OF_LOG"}, + {STATUS_LOG_POLICY_ALREADY_INSTALLED, -EIO, + "STATUS_LOG_POLICY_ALREADY_INSTALLED"}, + {STATUS_LOG_POLICY_NOT_INSTALLED, -EIO, + "STATUS_LOG_POLICY_NOT_INSTALLED"}, + {STATUS_LOG_POLICY_INVALID, -EIO, "STATUS_LOG_POLICY_INVALID"}, + {STATUS_LOG_POLICY_CONFLICT, -EIO, "STATUS_LOG_POLICY_CONFLICT"}, + {STATUS_LOG_PINNED_ARCHIVE_TAIL, -EIO, + "STATUS_LOG_PINNED_ARCHIVE_TAIL"}, + {STATUS_LOG_RECORD_NONEXISTENT, -EIO, "STATUS_LOG_RECORD_NONEXISTENT"}, + {STATUS_LOG_RECORDS_RESERVED_INVALID, -EIO, + "STATUS_LOG_RECORDS_RESERVED_INVALID"}, + {STATUS_LOG_SPACE_RESERVED_INVALID, -EIO, + "STATUS_LOG_SPACE_RESERVED_INVALID"}, + {STATUS_LOG_TAIL_INVALID, -EIO, "STATUS_LOG_TAIL_INVALID"}, + {STATUS_LOG_FULL, -EIO, "STATUS_LOG_FULL"}, + {STATUS_LOG_MULTIPLEXED, -EIO, "STATUS_LOG_MULTIPLEXED"}, + {STATUS_LOG_DEDICATED, -EIO, "STATUS_LOG_DEDICATED"}, + {STATUS_LOG_ARCHIVE_NOT_IN_PROGRESS, -EIO, + "STATUS_LOG_ARCHIVE_NOT_IN_PROGRESS"}, + {STATUS_LOG_ARCHIVE_IN_PROGRESS, -EIO, + "STATUS_LOG_ARCHIVE_IN_PROGRESS"}, + {STATUS_LOG_EPHEMERAL, -EIO, "STATUS_LOG_EPHEMERAL"}, + {STATUS_LOG_NOT_ENOUGH_CONTAINERS, -EIO, + "STATUS_LOG_NOT_ENOUGH_CONTAINERS"}, + {STATUS_LOG_CLIENT_ALREADY_REGISTERED, -EIO, + "STATUS_LOG_CLIENT_ALREADY_REGISTERED"}, + {STATUS_LOG_CLIENT_NOT_REGISTERED, -EIO, + "STATUS_LOG_CLIENT_NOT_REGISTERED"}, + {STATUS_LOG_FULL_HANDLER_IN_PROGRESS, -EIO, + "STATUS_LOG_FULL_HANDLER_IN_PROGRESS"}, + {STATUS_LOG_CONTAINER_READ_FAILED, -EIO, + "STATUS_LOG_CONTAINER_READ_FAILED"}, + {STATUS_LOG_CONTAINER_WRITE_FAILED, -EIO, + "STATUS_LOG_CONTAINER_WRITE_FAILED"}, + {STATUS_LOG_CONTAINER_OPEN_FAILED, -EIO, + "STATUS_LOG_CONTAINER_OPEN_FAILED"}, + {STATUS_LOG_CONTAINER_STATE_INVALID, -EIO, + "STATUS_LOG_CONTAINER_STATE_INVALID"}, + {STATUS_LOG_STATE_INVALID, -EIO, "STATUS_LOG_STATE_INVALID"}, + {STATUS_LOG_PINNED, -EIO, "STATUS_LOG_PINNED"}, + {STATUS_LOG_METADATA_FLUSH_FAILED, -EIO, + "STATUS_LOG_METADATA_FLUSH_FAILED"}, + {STATUS_LOG_INCONSISTENT_SECURITY, -EIO, + "STATUS_LOG_INCONSISTENT_SECURITY"}, + {STATUS_LOG_APPENDED_FLUSH_FAILED, -EIO, + "STATUS_LOG_APPENDED_FLUSH_FAILED"}, + {STATUS_LOG_PINNED_RESERVATION, -EIO, "STATUS_LOG_PINNED_RESERVATION"}, + {STATUS_VIDEO_HUNG_DISPLAY_DRIVER_THREAD, -EIO, + "STATUS_VIDEO_HUNG_DISPLAY_DRIVER_THREAD"}, + {STATUS_FLT_NO_HANDLER_DEFINED, -EIO, "STATUS_FLT_NO_HANDLER_DEFINED"}, + {STATUS_FLT_CONTEXT_ALREADY_DEFINED, -EIO, + "STATUS_FLT_CONTEXT_ALREADY_DEFINED"}, + {STATUS_FLT_INVALID_ASYNCHRONOUS_REQUEST, -EIO, + "STATUS_FLT_INVALID_ASYNCHRONOUS_REQUEST"}, + {STATUS_FLT_DISALLOW_FAST_IO, -EIO, "STATUS_FLT_DISALLOW_FAST_IO"}, + {STATUS_FLT_INVALID_NAME_REQUEST, -EIO, + "STATUS_FLT_INVALID_NAME_REQUEST"}, + {STATUS_FLT_NOT_SAFE_TO_POST_OPERATION, -EIO, + "STATUS_FLT_NOT_SAFE_TO_POST_OPERATION"}, + {STATUS_FLT_NOT_INITIALIZED, -EIO, "STATUS_FLT_NOT_INITIALIZED"}, + {STATUS_FLT_FILTER_NOT_READY, -EIO, "STATUS_FLT_FILTER_NOT_READY"}, + {STATUS_FLT_POST_OPERATION_CLEANUP, -EIO, + "STATUS_FLT_POST_OPERATION_CLEANUP"}, + {STATUS_FLT_INTERNAL_ERROR, -EIO, "STATUS_FLT_INTERNAL_ERROR"}, + {STATUS_FLT_DELETING_OBJECT, -EIO, "STATUS_FLT_DELETING_OBJECT"}, + {STATUS_FLT_MUST_BE_NONPAGED_POOL, -EIO, + "STATUS_FLT_MUST_BE_NONPAGED_POOL"}, + {STATUS_FLT_DUPLICATE_ENTRY, -EIO, "STATUS_FLT_DUPLICATE_ENTRY"}, + {STATUS_FLT_CBDQ_DISABLED, -EIO, "STATUS_FLT_CBDQ_DISABLED"}, + {STATUS_FLT_DO_NOT_ATTACH, -EIO, "STATUS_FLT_DO_NOT_ATTACH"}, + {STATUS_FLT_DO_NOT_DETACH, -EIO, "STATUS_FLT_DO_NOT_DETACH"}, + {STATUS_FLT_INSTANCE_ALTITUDE_COLLISION, -EIO, + "STATUS_FLT_INSTANCE_ALTITUDE_COLLISION"}, + {STATUS_FLT_INSTANCE_NAME_COLLISION, -EIO, + "STATUS_FLT_INSTANCE_NAME_COLLISION"}, + {STATUS_FLT_FILTER_NOT_FOUND, -EIO, "STATUS_FLT_FILTER_NOT_FOUND"}, + {STATUS_FLT_VOLUME_NOT_FOUND, -EIO, "STATUS_FLT_VOLUME_NOT_FOUND"}, + {STATUS_FLT_INSTANCE_NOT_FOUND, -EIO, "STATUS_FLT_INSTANCE_NOT_FOUND"}, + {STATUS_FLT_CONTEXT_ALLOCATION_NOT_FOUND, -EIO, + "STATUS_FLT_CONTEXT_ALLOCATION_NOT_FOUND"}, + {STATUS_FLT_INVALID_CONTEXT_REGISTRATION, -EIO, + "STATUS_FLT_INVALID_CONTEXT_REGISTRATION"}, + {STATUS_FLT_NAME_CACHE_MISS, -EIO, "STATUS_FLT_NAME_CACHE_MISS"}, + {STATUS_FLT_NO_DEVICE_OBJECT, -EIO, "STATUS_FLT_NO_DEVICE_OBJECT"}, + {STATUS_FLT_VOLUME_ALREADY_MOUNTED, -EIO, + "STATUS_FLT_VOLUME_ALREADY_MOUNTED"}, + {STATUS_FLT_ALREADY_ENLISTED, -EIO, "STATUS_FLT_ALREADY_ENLISTED"}, + {STATUS_FLT_CONTEXT_ALREADY_LINKED, -EIO, + "STATUS_FLT_CONTEXT_ALREADY_LINKED"}, + {STATUS_FLT_NO_WAITER_FOR_REPLY, -EIO, + "STATUS_FLT_NO_WAITER_FOR_REPLY"}, + {STATUS_MONITOR_NO_DESCRIPTOR, -EIO, "STATUS_MONITOR_NO_DESCRIPTOR"}, + {STATUS_MONITOR_UNKNOWN_DESCRIPTOR_FORMAT, -EIO, + "STATUS_MONITOR_UNKNOWN_DESCRIPTOR_FORMAT"}, + {STATUS_MONITOR_INVALID_DESCRIPTOR_CHECKSUM, -EIO, + "STATUS_MONITOR_INVALID_DESCRIPTOR_CHECKSUM"}, + {STATUS_MONITOR_INVALID_STANDARD_TIMING_BLOCK, -EIO, + "STATUS_MONITOR_INVALID_STANDARD_TIMING_BLOCK"}, + {STATUS_MONITOR_WMI_DATABLOCK_REGISTRATION_FAILED, -EIO, + "STATUS_MONITOR_WMI_DATABLOCK_REGISTRATION_FAILED"}, + {STATUS_MONITOR_INVALID_SERIAL_NUMBER_MONDSC_BLOCK, -EIO, + "STATUS_MONITOR_INVALID_SERIAL_NUMBER_MONDSC_BLOCK"}, + {STATUS_MONITOR_INVALID_USER_FRIENDLY_MONDSC_BLOCK, -EIO, + "STATUS_MONITOR_INVALID_USER_FRIENDLY_MONDSC_BLOCK"}, + {STATUS_MONITOR_NO_MORE_DESCRIPTOR_DATA, -EIO, + "STATUS_MONITOR_NO_MORE_DESCRIPTOR_DATA"}, + {STATUS_MONITOR_INVALID_DETAILED_TIMING_BLOCK, -EIO, + "STATUS_MONITOR_INVALID_DETAILED_TIMING_BLOCK"}, + {STATUS_GRAPHICS_NOT_EXCLUSIVE_MODE_OWNER, -EIO, + "STATUS_GRAPHICS_NOT_EXCLUSIVE_MODE_OWNER"}, + {STATUS_GRAPHICS_INSUFFICIENT_DMA_BUFFER, -EIO, + "STATUS_GRAPHICS_INSUFFICIENT_DMA_BUFFER"}, + {STATUS_GRAPHICS_INVALID_DISPLAY_ADAPTER, -EIO, + "STATUS_GRAPHICS_INVALID_DISPLAY_ADAPTER"}, + {STATUS_GRAPHICS_ADAPTER_WAS_RESET, -EIO, + "STATUS_GRAPHICS_ADAPTER_WAS_RESET"}, + {STATUS_GRAPHICS_INVALID_DRIVER_MODEL, -EIO, + "STATUS_GRAPHICS_INVALID_DRIVER_MODEL"}, + {STATUS_GRAPHICS_PRESENT_MODE_CHANGED, -EIO, + "STATUS_GRAPHICS_PRESENT_MODE_CHANGED"}, + {STATUS_GRAPHICS_PRESENT_OCCLUDED, -EIO, + "STATUS_GRAPHICS_PRESENT_OCCLUDED"}, + {STATUS_GRAPHICS_PRESENT_DENIED, -EIO, + "STATUS_GRAPHICS_PRESENT_DENIED"}, + {STATUS_GRAPHICS_CANNOTCOLORCONVERT, -EIO, + "STATUS_GRAPHICS_CANNOTCOLORCONVERT"}, + {STATUS_GRAPHICS_NO_VIDEO_MEMORY, -EIO, + "STATUS_GRAPHICS_NO_VIDEO_MEMORY"}, + {STATUS_GRAPHICS_CANT_LOCK_MEMORY, -EIO, + "STATUS_GRAPHICS_CANT_LOCK_MEMORY"}, + {STATUS_GRAPHICS_ALLOCATION_BUSY, -EBUSY, + "STATUS_GRAPHICS_ALLOCATION_BUSY"}, + {STATUS_GRAPHICS_TOO_MANY_REFERENCES, -EIO, + "STATUS_GRAPHICS_TOO_MANY_REFERENCES"}, + {STATUS_GRAPHICS_TRY_AGAIN_LATER, -EIO, + "STATUS_GRAPHICS_TRY_AGAIN_LATER"}, + {STATUS_GRAPHICS_TRY_AGAIN_NOW, -EIO, "STATUS_GRAPHICS_TRY_AGAIN_NOW"}, + {STATUS_GRAPHICS_ALLOCATION_INVALID, -EIO, + "STATUS_GRAPHICS_ALLOCATION_INVALID"}, + {STATUS_GRAPHICS_UNSWIZZLING_APERTURE_UNAVAILABLE, -EIO, + "STATUS_GRAPHICS_UNSWIZZLING_APERTURE_UNAVAILABLE"}, + {STATUS_GRAPHICS_UNSWIZZLING_APERTURE_UNSUPPORTED, -EIO, + "STATUS_GRAPHICS_UNSWIZZLING_APERTURE_UNSUPPORTED"}, + {STATUS_GRAPHICS_CANT_EVICT_PINNED_ALLOCATION, -EIO, + "STATUS_GRAPHICS_CANT_EVICT_PINNED_ALLOCATION"}, + {STATUS_GRAPHICS_INVALID_ALLOCATION_USAGE, -EIO, + "STATUS_GRAPHICS_INVALID_ALLOCATION_USAGE"}, + {STATUS_GRAPHICS_CANT_RENDER_LOCKED_ALLOCATION, -EIO, + "STATUS_GRAPHICS_CANT_RENDER_LOCKED_ALLOCATION"}, + {STATUS_GRAPHICS_ALLOCATION_CLOSED, -EIO, + "STATUS_GRAPHICS_ALLOCATION_CLOSED"}, + {STATUS_GRAPHICS_INVALID_ALLOCATION_INSTANCE, -EIO, + "STATUS_GRAPHICS_INVALID_ALLOCATION_INSTANCE"}, + {STATUS_GRAPHICS_INVALID_ALLOCATION_HANDLE, -EIO, + "STATUS_GRAPHICS_INVALID_ALLOCATION_HANDLE"}, + {STATUS_GRAPHICS_WRONG_ALLOCATION_DEVICE, -EIO, + "STATUS_GRAPHICS_WRONG_ALLOCATION_DEVICE"}, + {STATUS_GRAPHICS_ALLOCATION_CONTENT_LOST, -EIO, + "STATUS_GRAPHICS_ALLOCATION_CONTENT_LOST"}, + {STATUS_GRAPHICS_GPU_EXCEPTION_ON_DEVICE, -EIO, + "STATUS_GRAPHICS_GPU_EXCEPTION_ON_DEVICE"}, + {STATUS_GRAPHICS_INVALID_VIDPN_TOPOLOGY, -EIO, + "STATUS_GRAPHICS_INVALID_VIDPN_TOPOLOGY"}, + {STATUS_GRAPHICS_VIDPN_TOPOLOGY_NOT_SUPPORTED, -EIO, + "STATUS_GRAPHICS_VIDPN_TOPOLOGY_NOT_SUPPORTED"}, + {STATUS_GRAPHICS_VIDPN_TOPOLOGY_CURRENTLY_NOT_SUPPORTED, -EIO, + "STATUS_GRAPHICS_VIDPN_TOPOLOGY_CURRENTLY_NOT_SUPPORTED"}, + {STATUS_GRAPHICS_INVALID_VIDPN, -EIO, "STATUS_GRAPHICS_INVALID_VIDPN"}, + {STATUS_GRAPHICS_INVALID_VIDEO_PRESENT_SOURCE, -EIO, + "STATUS_GRAPHICS_INVALID_VIDEO_PRESENT_SOURCE"}, + {STATUS_GRAPHICS_INVALID_VIDEO_PRESENT_TARGET, -EIO, + "STATUS_GRAPHICS_INVALID_VIDEO_PRESENT_TARGET"}, + {STATUS_GRAPHICS_VIDPN_MODALITY_NOT_SUPPORTED, -EIO, + "STATUS_GRAPHICS_VIDPN_MODALITY_NOT_SUPPORTED"}, + {STATUS_GRAPHICS_INVALID_VIDPN_SOURCEMODESET, -EIO, + "STATUS_GRAPHICS_INVALID_VIDPN_SOURCEMODESET"}, + {STATUS_GRAPHICS_INVALID_VIDPN_TARGETMODESET, -EIO, + "STATUS_GRAPHICS_INVALID_VIDPN_TARGETMODESET"}, + {STATUS_GRAPHICS_INVALID_FREQUENCY, -EIO, + "STATUS_GRAPHICS_INVALID_FREQUENCY"}, + {STATUS_GRAPHICS_INVALID_ACTIVE_REGION, -EIO, + "STATUS_GRAPHICS_INVALID_ACTIVE_REGION"}, + {STATUS_GRAPHICS_INVALID_TOTAL_REGION, -EIO, + "STATUS_GRAPHICS_INVALID_TOTAL_REGION"}, + {STATUS_GRAPHICS_INVALID_VIDEO_PRESENT_SOURCE_MODE, -EIO, + "STATUS_GRAPHICS_INVALID_VIDEO_PRESENT_SOURCE_MODE"}, + {STATUS_GRAPHICS_INVALID_VIDEO_PRESENT_TARGET_MODE, -EIO, + "STATUS_GRAPHICS_INVALID_VIDEO_PRESENT_TARGET_MODE"}, + {STATUS_GRAPHICS_PINNED_MODE_MUST_REMAIN_IN_SET, -EIO, + "STATUS_GRAPHICS_PINNED_MODE_MUST_REMAIN_IN_SET"}, + {STATUS_GRAPHICS_PATH_ALREADY_IN_TOPOLOGY, -EIO, + "STATUS_GRAPHICS_PATH_ALREADY_IN_TOPOLOGY"}, + {STATUS_GRAPHICS_MODE_ALREADY_IN_MODESET, -EIO, + "STATUS_GRAPHICS_MODE_ALREADY_IN_MODESET"}, + {STATUS_GRAPHICS_INVALID_VIDEOPRESENTSOURCESET, -EIO, + "STATUS_GRAPHICS_INVALID_VIDEOPRESENTSOURCESET"}, + {STATUS_GRAPHICS_INVALID_VIDEOPRESENTTARGETSET, -EIO, + "STATUS_GRAPHICS_INVALID_VIDEOPRESENTTARGETSET"}, + {STATUS_GRAPHICS_SOURCE_ALREADY_IN_SET, -EIO, + "STATUS_GRAPHICS_SOURCE_ALREADY_IN_SET"}, + {STATUS_GRAPHICS_TARGET_ALREADY_IN_SET, -EIO, + "STATUS_GRAPHICS_TARGET_ALREADY_IN_SET"}, + {STATUS_GRAPHICS_INVALID_VIDPN_PRESENT_PATH, -EIO, + "STATUS_GRAPHICS_INVALID_VIDPN_PRESENT_PATH"}, + {STATUS_GRAPHICS_NO_RECOMMENDED_VIDPN_TOPOLOGY, -EIO, + "STATUS_GRAPHICS_NO_RECOMMENDED_VIDPN_TOPOLOGY"}, + {STATUS_GRAPHICS_INVALID_MONITOR_FREQUENCYRANGESET, -EIO, + "STATUS_GRAPHICS_INVALID_MONITOR_FREQUENCYRANGESET"}, + {STATUS_GRAPHICS_INVALID_MONITOR_FREQUENCYRANGE, -EIO, + "STATUS_GRAPHICS_INVALID_MONITOR_FREQUENCYRANGE"}, + {STATUS_GRAPHICS_FREQUENCYRANGE_NOT_IN_SET, -EIO, + "STATUS_GRAPHICS_FREQUENCYRANGE_NOT_IN_SET"}, + {STATUS_GRAPHICS_FREQUENCYRANGE_ALREADY_IN_SET, -EIO, + "STATUS_GRAPHICS_FREQUENCYRANGE_ALREADY_IN_SET"}, + {STATUS_GRAPHICS_STALE_MODESET, -EIO, "STATUS_GRAPHICS_STALE_MODESET"}, + {STATUS_GRAPHICS_INVALID_MONITOR_SOURCEMODESET, -EIO, + "STATUS_GRAPHICS_INVALID_MONITOR_SOURCEMODESET"}, + {STATUS_GRAPHICS_INVALID_MONITOR_SOURCE_MODE, -EIO, + "STATUS_GRAPHICS_INVALID_MONITOR_SOURCE_MODE"}, + {STATUS_GRAPHICS_NO_RECOMMENDED_FUNCTIONAL_VIDPN, -EIO, + "STATUS_GRAPHICS_NO_RECOMMENDED_FUNCTIONAL_VIDPN"}, + {STATUS_GRAPHICS_MODE_ID_MUST_BE_UNIQUE, -EIO, + "STATUS_GRAPHICS_MODE_ID_MUST_BE_UNIQUE"}, + {STATUS_GRAPHICS_EMPTY_ADAPTER_MONITOR_MODE_SUPPORT_INTERSECTION, -EIO, + "STATUS_GRAPHICS_EMPTY_ADAPTER_MONITOR_MODE_SUPPORT_INTERSECTION"}, + {STATUS_GRAPHICS_VIDEO_PRESENT_TARGETS_LESS_THAN_SOURCES, -EIO, + "STATUS_GRAPHICS_VIDEO_PRESENT_TARGETS_LESS_THAN_SOURCES"}, + {STATUS_GRAPHICS_PATH_NOT_IN_TOPOLOGY, -EIO, + "STATUS_GRAPHICS_PATH_NOT_IN_TOPOLOGY"}, + {STATUS_GRAPHICS_ADAPTER_MUST_HAVE_AT_LEAST_ONE_SOURCE, -EIO, + "STATUS_GRAPHICS_ADAPTER_MUST_HAVE_AT_LEAST_ONE_SOURCE"}, + {STATUS_GRAPHICS_ADAPTER_MUST_HAVE_AT_LEAST_ONE_TARGET, -EIO, + "STATUS_GRAPHICS_ADAPTER_MUST_HAVE_AT_LEAST_ONE_TARGET"}, + {STATUS_GRAPHICS_INVALID_MONITORDESCRIPTORSET, -EIO, + "STATUS_GRAPHICS_INVALID_MONITORDESCRIPTORSET"}, + {STATUS_GRAPHICS_INVALID_MONITORDESCRIPTOR, -EIO, + "STATUS_GRAPHICS_INVALID_MONITORDESCRIPTOR"}, + {STATUS_GRAPHICS_MONITORDESCRIPTOR_NOT_IN_SET, -EIO, + "STATUS_GRAPHICS_MONITORDESCRIPTOR_NOT_IN_SET"}, + {STATUS_GRAPHICS_MONITORDESCRIPTOR_ALREADY_IN_SET, -EIO, + "STATUS_GRAPHICS_MONITORDESCRIPTOR_ALREADY_IN_SET"}, + {STATUS_GRAPHICS_MONITORDESCRIPTOR_ID_MUST_BE_UNIQUE, -EIO, + "STATUS_GRAPHICS_MONITORDESCRIPTOR_ID_MUST_BE_UNIQUE"}, + {STATUS_GRAPHICS_INVALID_VIDPN_TARGET_SUBSET_TYPE, -EIO, + "STATUS_GRAPHICS_INVALID_VIDPN_TARGET_SUBSET_TYPE"}, + {STATUS_GRAPHICS_RESOURCES_NOT_RELATED, -EIO, + "STATUS_GRAPHICS_RESOURCES_NOT_RELATED"}, + {STATUS_GRAPHICS_SOURCE_ID_MUST_BE_UNIQUE, -EIO, + "STATUS_GRAPHICS_SOURCE_ID_MUST_BE_UNIQUE"}, + {STATUS_GRAPHICS_TARGET_ID_MUST_BE_UNIQUE, -EIO, + "STATUS_GRAPHICS_TARGET_ID_MUST_BE_UNIQUE"}, + {STATUS_GRAPHICS_NO_AVAILABLE_VIDPN_TARGET, -EIO, + "STATUS_GRAPHICS_NO_AVAILABLE_VIDPN_TARGET"}, + {STATUS_GRAPHICS_MONITOR_COULD_NOT_BE_ASSOCIATED_WITH_ADAPTER, -EIO, + "STATUS_GRAPHICS_MONITOR_COULD_NOT_BE_ASSOCIATED_WITH_ADAPTER"}, + {STATUS_GRAPHICS_NO_VIDPNMGR, -EIO, "STATUS_GRAPHICS_NO_VIDPNMGR"}, + {STATUS_GRAPHICS_NO_ACTIVE_VIDPN, -EIO, + "STATUS_GRAPHICS_NO_ACTIVE_VIDPN"}, + {STATUS_GRAPHICS_STALE_VIDPN_TOPOLOGY, -EIO, + "STATUS_GRAPHICS_STALE_VIDPN_TOPOLOGY"}, + {STATUS_GRAPHICS_MONITOR_NOT_CONNECTED, -EIO, + "STATUS_GRAPHICS_MONITOR_NOT_CONNECTED"}, + {STATUS_GRAPHICS_SOURCE_NOT_IN_TOPOLOGY, -EIO, + "STATUS_GRAPHICS_SOURCE_NOT_IN_TOPOLOGY"}, + {STATUS_GRAPHICS_INVALID_PRIMARYSURFACE_SIZE, -EIO, + "STATUS_GRAPHICS_INVALID_PRIMARYSURFACE_SIZE"}, + {STATUS_GRAPHICS_INVALID_VISIBLEREGION_SIZE, -EIO, + "STATUS_GRAPHICS_INVALID_VISIBLEREGION_SIZE"}, + {STATUS_GRAPHICS_INVALID_STRIDE, -EIO, + "STATUS_GRAPHICS_INVALID_STRIDE"}, + {STATUS_GRAPHICS_INVALID_PIXELFORMAT, -EIO, + "STATUS_GRAPHICS_INVALID_PIXELFORMAT"}, + {STATUS_GRAPHICS_INVALID_COLORBASIS, -EIO, + "STATUS_GRAPHICS_INVALID_COLORBASIS"}, + {STATUS_GRAPHICS_INVALID_PIXELVALUEACCESSMODE, -EIO, + "STATUS_GRAPHICS_INVALID_PIXELVALUEACCESSMODE"}, + {STATUS_GRAPHICS_TARGET_NOT_IN_TOPOLOGY, -EIO, + "STATUS_GRAPHICS_TARGET_NOT_IN_TOPOLOGY"}, + {STATUS_GRAPHICS_NO_DISPLAY_MODE_MANAGEMENT_SUPPORT, -EIO, + "STATUS_GRAPHICS_NO_DISPLAY_MODE_MANAGEMENT_SUPPORT"}, + {STATUS_GRAPHICS_VIDPN_SOURCE_IN_USE, -EIO, + "STATUS_GRAPHICS_VIDPN_SOURCE_IN_USE"}, + {STATUS_GRAPHICS_CANT_ACCESS_ACTIVE_VIDPN, -EIO, + "STATUS_GRAPHICS_CANT_ACCESS_ACTIVE_VIDPN"}, + {STATUS_GRAPHICS_INVALID_PATH_IMPORTANCE_ORDINAL, -EIO, + "STATUS_GRAPHICS_INVALID_PATH_IMPORTANCE_ORDINAL"}, + {STATUS_GRAPHICS_INVALID_PATH_CONTENT_GEOMETRY_TRANSFORMATION, -EIO, + "STATUS_GRAPHICS_INVALID_PATH_CONTENT_GEOMETRY_TRANSFORMATION"}, + {STATUS_GRAPHICS_PATH_CONTENT_GEOMETRY_TRANSFORMATION_NOT_SUPPORTED, + -EIO, + "STATUS_GRAPHICS_PATH_CONTENT_GEOMETRY_TRANSFORMATION_NOT_SUPPORTED"}, + {STATUS_GRAPHICS_INVALID_GAMMA_RAMP, -EIO, + "STATUS_GRAPHICS_INVALID_GAMMA_RAMP"}, + {STATUS_GRAPHICS_GAMMA_RAMP_NOT_SUPPORTED, -EIO, + "STATUS_GRAPHICS_GAMMA_RAMP_NOT_SUPPORTED"}, + {STATUS_GRAPHICS_MULTISAMPLING_NOT_SUPPORTED, -EIO, + "STATUS_GRAPHICS_MULTISAMPLING_NOT_SUPPORTED"}, + {STATUS_GRAPHICS_MODE_NOT_IN_MODESET, -EIO, + "STATUS_GRAPHICS_MODE_NOT_IN_MODESET"}, + {STATUS_GRAPHICS_INVALID_VIDPN_TOPOLOGY_RECOMMENDATION_REASON, -EIO, + "STATUS_GRAPHICS_INVALID_VIDPN_TOPOLOGY_RECOMMENDATION_REASON"}, + {STATUS_GRAPHICS_INVALID_PATH_CONTENT_TYPE, -EIO, + "STATUS_GRAPHICS_INVALID_PATH_CONTENT_TYPE"}, + {STATUS_GRAPHICS_INVALID_COPYPROTECTION_TYPE, -EIO, + "STATUS_GRAPHICS_INVALID_COPYPROTECTION_TYPE"}, + {STATUS_GRAPHICS_UNASSIGNED_MODESET_ALREADY_EXISTS, -EIO, + "STATUS_GRAPHICS_UNASSIGNED_MODESET_ALREADY_EXISTS"}, + {STATUS_GRAPHICS_INVALID_SCANLINE_ORDERING, -EIO, + "STATUS_GRAPHICS_INVALID_SCANLINE_ORDERING"}, + {STATUS_GRAPHICS_TOPOLOGY_CHANGES_NOT_ALLOWED, -EIO, + "STATUS_GRAPHICS_TOPOLOGY_CHANGES_NOT_ALLOWED"}, + {STATUS_GRAPHICS_NO_AVAILABLE_IMPORTANCE_ORDINALS, -EIO, + "STATUS_GRAPHICS_NO_AVAILABLE_IMPORTANCE_ORDINALS"}, + {STATUS_GRAPHICS_INCOMPATIBLE_PRIVATE_FORMAT, -EIO, + "STATUS_GRAPHICS_INCOMPATIBLE_PRIVATE_FORMAT"}, + {STATUS_GRAPHICS_INVALID_MODE_PRUNING_ALGORITHM, -EIO, + "STATUS_GRAPHICS_INVALID_MODE_PRUNING_ALGORITHM"}, + {STATUS_GRAPHICS_INVALID_MONITOR_CAPABILITY_ORIGIN, -EIO, + "STATUS_GRAPHICS_INVALID_MONITOR_CAPABILITY_ORIGIN"}, + {STATUS_GRAPHICS_INVALID_MONITOR_FREQUENCYRANGE_CONSTRAINT, -EIO, + "STATUS_GRAPHICS_INVALID_MONITOR_FREQUENCYRANGE_CONSTRAINT"}, + {STATUS_GRAPHICS_MAX_NUM_PATHS_REACHED, -EIO, + "STATUS_GRAPHICS_MAX_NUM_PATHS_REACHED"}, + {STATUS_GRAPHICS_CANCEL_VIDPN_TOPOLOGY_AUGMENTATION, -EIO, + "STATUS_GRAPHICS_CANCEL_VIDPN_TOPOLOGY_AUGMENTATION"}, + {STATUS_GRAPHICS_INVALID_CLIENT_TYPE, -EIO, + "STATUS_GRAPHICS_INVALID_CLIENT_TYPE"}, + {STATUS_GRAPHICS_CLIENTVIDPN_NOT_SET, -EIO, + "STATUS_GRAPHICS_CLIENTVIDPN_NOT_SET"}, + {STATUS_GRAPHICS_SPECIFIED_CHILD_ALREADY_CONNECTED, -EIO, + "STATUS_GRAPHICS_SPECIFIED_CHILD_ALREADY_CONNECTED"}, + {STATUS_GRAPHICS_CHILD_DESCRIPTOR_NOT_SUPPORTED, -EIO, + "STATUS_GRAPHICS_CHILD_DESCRIPTOR_NOT_SUPPORTED"}, + {STATUS_GRAPHICS_NOT_A_LINKED_ADAPTER, -EIO, + "STATUS_GRAPHICS_NOT_A_LINKED_ADAPTER"}, + {STATUS_GRAPHICS_LEADLINK_NOT_ENUMERATED, -EIO, + "STATUS_GRAPHICS_LEADLINK_NOT_ENUMERATED"}, + {STATUS_GRAPHICS_CHAINLINKS_NOT_ENUMERATED, -EIO, + "STATUS_GRAPHICS_CHAINLINKS_NOT_ENUMERATED"}, + {STATUS_GRAPHICS_ADAPTER_CHAIN_NOT_READY, -EIO, + "STATUS_GRAPHICS_ADAPTER_CHAIN_NOT_READY"}, + {STATUS_GRAPHICS_CHAINLINKS_NOT_STARTED, -EIO, + "STATUS_GRAPHICS_CHAINLINKS_NOT_STARTED"}, + {STATUS_GRAPHICS_CHAINLINKS_NOT_POWERED_ON, -EIO, + "STATUS_GRAPHICS_CHAINLINKS_NOT_POWERED_ON"}, + {STATUS_GRAPHICS_INCONSISTENT_DEVICE_LINK_STATE, -EIO, + "STATUS_GRAPHICS_INCONSISTENT_DEVICE_LINK_STATE"}, + {STATUS_GRAPHICS_NOT_POST_DEVICE_DRIVER, -EIO, + "STATUS_GRAPHICS_NOT_POST_DEVICE_DRIVER"}, + {STATUS_GRAPHICS_ADAPTER_ACCESS_NOT_EXCLUDED, -EIO, + "STATUS_GRAPHICS_ADAPTER_ACCESS_NOT_EXCLUDED"}, + {STATUS_GRAPHICS_OPM_PROTECTED_OUTPUT_DOES_NOT_HAVE_COPP_SEMANTICS, + -EIO, + "STATUS_GRAPHICS_OPM_PROTECTED_OUTPUT_DOES_NOT_HAVE_COPP_SEMANTICS"}, + {STATUS_GRAPHICS_OPM_INVALID_INFORMATION_REQUEST, -EIO, + "STATUS_GRAPHICS_OPM_INVALID_INFORMATION_REQUEST"}, + {STATUS_GRAPHICS_OPM_DRIVER_INTERNAL_ERROR, -EIO, + "STATUS_GRAPHICS_OPM_DRIVER_INTERNAL_ERROR"}, + {STATUS_GRAPHICS_OPM_PROTECTED_OUTPUT_DOES_NOT_HAVE_OPM_SEMANTICS, -EIO, + "STATUS_GRAPHICS_OPM_PROTECTED_OUTPUT_DOES_NOT_HAVE_OPM_SEMANTICS"}, + {STATUS_GRAPHICS_OPM_SIGNALING_NOT_SUPPORTED, -EIO, + "STATUS_GRAPHICS_OPM_SIGNALING_NOT_SUPPORTED"}, + {STATUS_GRAPHICS_OPM_INVALID_CONFIGURATION_REQUEST, -EIO, + "STATUS_GRAPHICS_OPM_INVALID_CONFIGURATION_REQUEST"}, + {STATUS_GRAPHICS_OPM_NOT_SUPPORTED, -EIO, + "STATUS_GRAPHICS_OPM_NOT_SUPPORTED"}, + {STATUS_GRAPHICS_COPP_NOT_SUPPORTED, -EIO, + "STATUS_GRAPHICS_COPP_NOT_SUPPORTED"}, + {STATUS_GRAPHICS_UAB_NOT_SUPPORTED, -EIO, + "STATUS_GRAPHICS_UAB_NOT_SUPPORTED"}, + {STATUS_GRAPHICS_OPM_INVALID_ENCRYPTED_PARAMETERS, -EIO, + "STATUS_GRAPHICS_OPM_INVALID_ENCRYPTED_PARAMETERS"}, + {STATUS_GRAPHICS_OPM_PARAMETER_ARRAY_TOO_SMALL, -EIO, + "STATUS_GRAPHICS_OPM_PARAMETER_ARRAY_TOO_SMALL"}, + {STATUS_GRAPHICS_OPM_NO_PROTECTED_OUTPUTS_EXIST, -EIO, + "STATUS_GRAPHICS_OPM_NO_PROTECTED_OUTPUTS_EXIST"}, + {STATUS_GRAPHICS_PVP_NO_DISPLAY_DEVICE_CORRESPONDS_TO_NAME, -EIO, + "STATUS_GRAPHICS_PVP_NO_DISPLAY_DEVICE_CORRESPONDS_TO_NAME"}, + {STATUS_GRAPHICS_PVP_DISPLAY_DEVICE_NOT_ATTACHED_TO_DESKTOP, -EIO, + "STATUS_GRAPHICS_PVP_DISPLAY_DEVICE_NOT_ATTACHED_TO_DESKTOP"}, + {STATUS_GRAPHICS_PVP_MIRRORING_DEVICES_NOT_SUPPORTED, -EIO, + "STATUS_GRAPHICS_PVP_MIRRORING_DEVICES_NOT_SUPPORTED"}, + {STATUS_GRAPHICS_OPM_INVALID_POINTER, -EIO, + "STATUS_GRAPHICS_OPM_INVALID_POINTER"}, + {STATUS_GRAPHICS_OPM_INTERNAL_ERROR, -EIO, + "STATUS_GRAPHICS_OPM_INTERNAL_ERROR"}, + {STATUS_GRAPHICS_OPM_INVALID_HANDLE, -EIO, + "STATUS_GRAPHICS_OPM_INVALID_HANDLE"}, + {STATUS_GRAPHICS_PVP_NO_MONITORS_CORRESPOND_TO_DISPLAY_DEVICE, -EIO, + "STATUS_GRAPHICS_PVP_NO_MONITORS_CORRESPOND_TO_DISPLAY_DEVICE"}, + {STATUS_GRAPHICS_PVP_INVALID_CERTIFICATE_LENGTH, -EIO, + "STATUS_GRAPHICS_PVP_INVALID_CERTIFICATE_LENGTH"}, + {STATUS_GRAPHICS_OPM_SPANNING_MODE_ENABLED, -EIO, + "STATUS_GRAPHICS_OPM_SPANNING_MODE_ENABLED"}, + {STATUS_GRAPHICS_OPM_THEATER_MODE_ENABLED, -EIO, + "STATUS_GRAPHICS_OPM_THEATER_MODE_ENABLED"}, + {STATUS_GRAPHICS_PVP_HFS_FAILED, -EIO, + "STATUS_GRAPHICS_PVP_HFS_FAILED"}, + {STATUS_GRAPHICS_OPM_INVALID_SRM, -EIO, + "STATUS_GRAPHICS_OPM_INVALID_SRM"}, + {STATUS_GRAPHICS_OPM_OUTPUT_DOES_NOT_SUPPORT_HDCP, -EIO, + "STATUS_GRAPHICS_OPM_OUTPUT_DOES_NOT_SUPPORT_HDCP"}, + {STATUS_GRAPHICS_OPM_OUTPUT_DOES_NOT_SUPPORT_ACP, -EIO, + "STATUS_GRAPHICS_OPM_OUTPUT_DOES_NOT_SUPPORT_ACP"}, + {STATUS_GRAPHICS_OPM_OUTPUT_DOES_NOT_SUPPORT_CGMSA, -EIO, + "STATUS_GRAPHICS_OPM_OUTPUT_DOES_NOT_SUPPORT_CGMSA"}, + {STATUS_GRAPHICS_OPM_HDCP_SRM_NEVER_SET, -EIO, + "STATUS_GRAPHICS_OPM_HDCP_SRM_NEVER_SET"}, + {STATUS_GRAPHICS_OPM_RESOLUTION_TOO_HIGH, -EIO, + "STATUS_GRAPHICS_OPM_RESOLUTION_TOO_HIGH"}, + {STATUS_GRAPHICS_OPM_ALL_HDCP_HARDWARE_ALREADY_IN_USE, -EIO, + "STATUS_GRAPHICS_OPM_ALL_HDCP_HARDWARE_ALREADY_IN_USE"}, + {STATUS_GRAPHICS_OPM_PROTECTED_OUTPUT_NO_LONGER_EXISTS, -EIO, + "STATUS_GRAPHICS_OPM_PROTECTED_OUTPUT_NO_LONGER_EXISTS"}, + {STATUS_GRAPHICS_OPM_SESSION_TYPE_CHANGE_IN_PROGRESS, -EIO, + "STATUS_GRAPHICS_OPM_SESSION_TYPE_CHANGE_IN_PROGRESS"}, + {STATUS_GRAPHICS_I2C_NOT_SUPPORTED, -EIO, + "STATUS_GRAPHICS_I2C_NOT_SUPPORTED"}, + {STATUS_GRAPHICS_I2C_DEVICE_DOES_NOT_EXIST, -EIO, + "STATUS_GRAPHICS_I2C_DEVICE_DOES_NOT_EXIST"}, + {STATUS_GRAPHICS_I2C_ERROR_TRANSMITTING_DATA, -EIO, + "STATUS_GRAPHICS_I2C_ERROR_TRANSMITTING_DATA"}, + {STATUS_GRAPHICS_I2C_ERROR_RECEIVING_DATA, -EIO, + "STATUS_GRAPHICS_I2C_ERROR_RECEIVING_DATA"}, + {STATUS_GRAPHICS_DDCCI_VCP_NOT_SUPPORTED, -EIO, + "STATUS_GRAPHICS_DDCCI_VCP_NOT_SUPPORTED"}, + {STATUS_GRAPHICS_DDCCI_INVALID_DATA, -EIO, + "STATUS_GRAPHICS_DDCCI_INVALID_DATA"}, + {STATUS_GRAPHICS_DDCCI_MONITOR_RETURNED_INVALID_TIMING_STATUS_BYTE, + -EIO, + "STATUS_GRAPHICS_DDCCI_MONITOR_RETURNED_INVALID_TIMING_STATUS_BYTE"}, + {STATUS_GRAPHICS_DDCCI_INVALID_CAPABILITIES_STRING, -EIO, + "STATUS_GRAPHICS_DDCCI_INVALID_CAPABILITIES_STRING"}, + {STATUS_GRAPHICS_MCA_INTERNAL_ERROR, -EIO, + "STATUS_GRAPHICS_MCA_INTERNAL_ERROR"}, + {STATUS_GRAPHICS_DDCCI_INVALID_MESSAGE_COMMAND, -EIO, + "STATUS_GRAPHICS_DDCCI_INVALID_MESSAGE_COMMAND"}, + {STATUS_GRAPHICS_DDCCI_INVALID_MESSAGE_LENGTH, -EIO, + "STATUS_GRAPHICS_DDCCI_INVALID_MESSAGE_LENGTH"}, + {STATUS_GRAPHICS_DDCCI_INVALID_MESSAGE_CHECKSUM, -EIO, + "STATUS_GRAPHICS_DDCCI_INVALID_MESSAGE_CHECKSUM"}, + {STATUS_GRAPHICS_INVALID_PHYSICAL_MONITOR_HANDLE, -EIO, + "STATUS_GRAPHICS_INVALID_PHYSICAL_MONITOR_HANDLE"}, + {STATUS_GRAPHICS_MONITOR_NO_LONGER_EXISTS, -EIO, + "STATUS_GRAPHICS_MONITOR_NO_LONGER_EXISTS"}, + {STATUS_GRAPHICS_ONLY_CONSOLE_SESSION_SUPPORTED, -EIO, + "STATUS_GRAPHICS_ONLY_CONSOLE_SESSION_SUPPORTED"}, + {STATUS_GRAPHICS_NO_DISPLAY_DEVICE_CORRESPONDS_TO_NAME, -EIO, + "STATUS_GRAPHICS_NO_DISPLAY_DEVICE_CORRESPONDS_TO_NAME"}, + {STATUS_GRAPHICS_DISPLAY_DEVICE_NOT_ATTACHED_TO_DESKTOP, -EIO, + "STATUS_GRAPHICS_DISPLAY_DEVICE_NOT_ATTACHED_TO_DESKTOP"}, + {STATUS_GRAPHICS_MIRRORING_DEVICES_NOT_SUPPORTED, -EIO, + "STATUS_GRAPHICS_MIRRORING_DEVICES_NOT_SUPPORTED"}, + {STATUS_GRAPHICS_INVALID_POINTER, -EIO, + "STATUS_GRAPHICS_INVALID_POINTER"}, + {STATUS_GRAPHICS_NO_MONITORS_CORRESPOND_TO_DISPLAY_DEVICE, -EIO, + "STATUS_GRAPHICS_NO_MONITORS_CORRESPOND_TO_DISPLAY_DEVICE"}, + {STATUS_GRAPHICS_PARAMETER_ARRAY_TOO_SMALL, -EIO, + "STATUS_GRAPHICS_PARAMETER_ARRAY_TOO_SMALL"}, + {STATUS_GRAPHICS_INTERNAL_ERROR, -EIO, + "STATUS_GRAPHICS_INTERNAL_ERROR"}, + {STATUS_GRAPHICS_SESSION_TYPE_CHANGE_IN_PROGRESS, -EIO, + "STATUS_GRAPHICS_SESSION_TYPE_CHANGE_IN_PROGRESS"}, + {STATUS_FVE_LOCKED_VOLUME, -EIO, "STATUS_FVE_LOCKED_VOLUME"}, + {STATUS_FVE_NOT_ENCRYPTED, -EIO, "STATUS_FVE_NOT_ENCRYPTED"}, + {STATUS_FVE_BAD_INFORMATION, -EIO, "STATUS_FVE_BAD_INFORMATION"}, + {STATUS_FVE_TOO_SMALL, -EIO, "STATUS_FVE_TOO_SMALL"}, + {STATUS_FVE_FAILED_WRONG_FS, -EIO, "STATUS_FVE_FAILED_WRONG_FS"}, + {STATUS_FVE_FAILED_BAD_FS, -EIO, "STATUS_FVE_FAILED_BAD_FS"}, + {STATUS_FVE_FS_NOT_EXTENDED, -EIO, "STATUS_FVE_FS_NOT_EXTENDED"}, + {STATUS_FVE_FS_MOUNTED, -EIO, "STATUS_FVE_FS_MOUNTED"}, + {STATUS_FVE_NO_LICENSE, -EIO, "STATUS_FVE_NO_LICENSE"}, + {STATUS_FVE_ACTION_NOT_ALLOWED, -EIO, "STATUS_FVE_ACTION_NOT_ALLOWED"}, + {STATUS_FVE_BAD_DATA, -EIO, "STATUS_FVE_BAD_DATA"}, + {STATUS_FVE_VOLUME_NOT_BOUND, -EIO, "STATUS_FVE_VOLUME_NOT_BOUND"}, + {STATUS_FVE_NOT_DATA_VOLUME, -EIO, "STATUS_FVE_NOT_DATA_VOLUME"}, + {STATUS_FVE_CONV_READ_ERROR, -EIO, "STATUS_FVE_CONV_READ_ERROR"}, + {STATUS_FVE_CONV_WRITE_ERROR, -EIO, "STATUS_FVE_CONV_WRITE_ERROR"}, + {STATUS_FVE_OVERLAPPED_UPDATE, -EIO, "STATUS_FVE_OVERLAPPED_UPDATE"}, + {STATUS_FVE_FAILED_SECTOR_SIZE, -EIO, "STATUS_FVE_FAILED_SECTOR_SIZE"}, + {STATUS_FVE_FAILED_AUTHENTICATION, -EIO, + "STATUS_FVE_FAILED_AUTHENTICATION"}, + {STATUS_FVE_NOT_OS_VOLUME, -EIO, "STATUS_FVE_NOT_OS_VOLUME"}, + {STATUS_FVE_KEYFILE_NOT_FOUND, -EIO, "STATUS_FVE_KEYFILE_NOT_FOUND"}, + {STATUS_FVE_KEYFILE_INVALID, -EIO, "STATUS_FVE_KEYFILE_INVALID"}, + {STATUS_FVE_KEYFILE_NO_VMK, -EIO, "STATUS_FVE_KEYFILE_NO_VMK"}, + {STATUS_FVE_TPM_DISABLED, -EIO, "STATUS_FVE_TPM_DISABLED"}, + {STATUS_FVE_TPM_SRK_AUTH_NOT_ZERO, -EIO, + "STATUS_FVE_TPM_SRK_AUTH_NOT_ZERO"}, + {STATUS_FVE_TPM_INVALID_PCR, -EIO, "STATUS_FVE_TPM_INVALID_PCR"}, + {STATUS_FVE_TPM_NO_VMK, -EIO, "STATUS_FVE_TPM_NO_VMK"}, + {STATUS_FVE_PIN_INVALID, -EIO, "STATUS_FVE_PIN_INVALID"}, + {STATUS_FVE_AUTH_INVALID_APPLICATION, -EIO, + "STATUS_FVE_AUTH_INVALID_APPLICATION"}, + {STATUS_FVE_AUTH_INVALID_CONFIG, -EIO, + "STATUS_FVE_AUTH_INVALID_CONFIG"}, + {STATUS_FVE_DEBUGGER_ENABLED, -EIO, "STATUS_FVE_DEBUGGER_ENABLED"}, + {STATUS_FVE_DRY_RUN_FAILED, -EIO, "STATUS_FVE_DRY_RUN_FAILED"}, + {STATUS_FVE_BAD_METADATA_POINTER, -EIO, + "STATUS_FVE_BAD_METADATA_POINTER"}, + {STATUS_FVE_OLD_METADATA_COPY, -EIO, "STATUS_FVE_OLD_METADATA_COPY"}, + {STATUS_FVE_REBOOT_REQUIRED, -EIO, "STATUS_FVE_REBOOT_REQUIRED"}, + {STATUS_FVE_RAW_ACCESS, -EIO, "STATUS_FVE_RAW_ACCESS"}, + {STATUS_FVE_RAW_BLOCKED, -EIO, "STATUS_FVE_RAW_BLOCKED"}, + {STATUS_FWP_CALLOUT_NOT_FOUND, -EIO, "STATUS_FWP_CALLOUT_NOT_FOUND"}, + {STATUS_FWP_CONDITION_NOT_FOUND, -EIO, + "STATUS_FWP_CONDITION_NOT_FOUND"}, + {STATUS_FWP_FILTER_NOT_FOUND, -EIO, "STATUS_FWP_FILTER_NOT_FOUND"}, + {STATUS_FWP_LAYER_NOT_FOUND, -EIO, "STATUS_FWP_LAYER_NOT_FOUND"}, + {STATUS_FWP_PROVIDER_NOT_FOUND, -EIO, "STATUS_FWP_PROVIDER_NOT_FOUND"}, + {STATUS_FWP_PROVIDER_CONTEXT_NOT_FOUND, -EIO, + "STATUS_FWP_PROVIDER_CONTEXT_NOT_FOUND"}, + {STATUS_FWP_SUBLAYER_NOT_FOUND, -EIO, "STATUS_FWP_SUBLAYER_NOT_FOUND"}, + {STATUS_FWP_NOT_FOUND, -EIO, "STATUS_FWP_NOT_FOUND"}, + {STATUS_FWP_ALREADY_EXISTS, -EIO, "STATUS_FWP_ALREADY_EXISTS"}, + {STATUS_FWP_IN_USE, -EIO, "STATUS_FWP_IN_USE"}, + {STATUS_FWP_DYNAMIC_SESSION_IN_PROGRESS, -EIO, + "STATUS_FWP_DYNAMIC_SESSION_IN_PROGRESS"}, + {STATUS_FWP_WRONG_SESSION, -EIO, "STATUS_FWP_WRONG_SESSION"}, + {STATUS_FWP_NO_TXN_IN_PROGRESS, -EIO, "STATUS_FWP_NO_TXN_IN_PROGRESS"}, + {STATUS_FWP_TXN_IN_PROGRESS, -EIO, "STATUS_FWP_TXN_IN_PROGRESS"}, + {STATUS_FWP_TXN_ABORTED, -EIO, "STATUS_FWP_TXN_ABORTED"}, + {STATUS_FWP_SESSION_ABORTED, -EIO, "STATUS_FWP_SESSION_ABORTED"}, + {STATUS_FWP_INCOMPATIBLE_TXN, -EIO, "STATUS_FWP_INCOMPATIBLE_TXN"}, + {STATUS_FWP_TIMEOUT, -ETIMEDOUT, "STATUS_FWP_TIMEOUT"}, + {STATUS_FWP_NET_EVENTS_DISABLED, -EIO, + "STATUS_FWP_NET_EVENTS_DISABLED"}, + {STATUS_FWP_INCOMPATIBLE_LAYER, -EIO, "STATUS_FWP_INCOMPATIBLE_LAYER"}, + {STATUS_FWP_KM_CLIENTS_ONLY, -EIO, "STATUS_FWP_KM_CLIENTS_ONLY"}, + {STATUS_FWP_LIFETIME_MISMATCH, -EIO, "STATUS_FWP_LIFETIME_MISMATCH"}, + {STATUS_FWP_BUILTIN_OBJECT, -EIO, "STATUS_FWP_BUILTIN_OBJECT"}, + {STATUS_FWP_TOO_MANY_BOOTTIME_FILTERS, -EIO, + "STATUS_FWP_TOO_MANY_BOOTTIME_FILTERS"}, + {STATUS_FWP_TOO_MANY_CALLOUTS, -EIO, "STATUS_FWP_TOO_MANY_CALLOUTS"}, + {STATUS_FWP_NOTIFICATION_DROPPED, -EIO, + "STATUS_FWP_NOTIFICATION_DROPPED"}, + {STATUS_FWP_TRAFFIC_MISMATCH, -EIO, "STATUS_FWP_TRAFFIC_MISMATCH"}, + {STATUS_FWP_INCOMPATIBLE_SA_STATE, -EIO, + "STATUS_FWP_INCOMPATIBLE_SA_STATE"}, + {STATUS_FWP_NULL_POINTER, -EIO, "STATUS_FWP_NULL_POINTER"}, + {STATUS_FWP_INVALID_ENUMERATOR, -EIO, "STATUS_FWP_INVALID_ENUMERATOR"}, + {STATUS_FWP_INVALID_FLAGS, -EIO, "STATUS_FWP_INVALID_FLAGS"}, + {STATUS_FWP_INVALID_NET_MASK, -EIO, "STATUS_FWP_INVALID_NET_MASK"}, + {STATUS_FWP_INVALID_RANGE, -EIO, "STATUS_FWP_INVALID_RANGE"}, + {STATUS_FWP_INVALID_INTERVAL, -EIO, "STATUS_FWP_INVALID_INTERVAL"}, + {STATUS_FWP_ZERO_LENGTH_ARRAY, -EIO, "STATUS_FWP_ZERO_LENGTH_ARRAY"}, + {STATUS_FWP_NULL_DISPLAY_NAME, -EIO, "STATUS_FWP_NULL_DISPLAY_NAME"}, + {STATUS_FWP_INVALID_ACTION_TYPE, -EIO, + "STATUS_FWP_INVALID_ACTION_TYPE"}, + {STATUS_FWP_INVALID_WEIGHT, -EIO, "STATUS_FWP_INVALID_WEIGHT"}, + {STATUS_FWP_MATCH_TYPE_MISMATCH, -EIO, + "STATUS_FWP_MATCH_TYPE_MISMATCH"}, + {STATUS_FWP_TYPE_MISMATCH, -EIO, "STATUS_FWP_TYPE_MISMATCH"}, + {STATUS_FWP_OUT_OF_BOUNDS, -EIO, "STATUS_FWP_OUT_OF_BOUNDS"}, + {STATUS_FWP_RESERVED, -EIO, "STATUS_FWP_RESERVED"}, + {STATUS_FWP_DUPLICATE_CONDITION, -EIO, + "STATUS_FWP_DUPLICATE_CONDITION"}, + {STATUS_FWP_DUPLICATE_KEYMOD, -EIO, "STATUS_FWP_DUPLICATE_KEYMOD"}, + {STATUS_FWP_ACTION_INCOMPATIBLE_WITH_LAYER, -EIO, + "STATUS_FWP_ACTION_INCOMPATIBLE_WITH_LAYER"}, + {STATUS_FWP_ACTION_INCOMPATIBLE_WITH_SUBLAYER, -EIO, + "STATUS_FWP_ACTION_INCOMPATIBLE_WITH_SUBLAYER"}, + {STATUS_FWP_CONTEXT_INCOMPATIBLE_WITH_LAYER, -EIO, + "STATUS_FWP_CONTEXT_INCOMPATIBLE_WITH_LAYER"}, + {STATUS_FWP_CONTEXT_INCOMPATIBLE_WITH_CALLOUT, -EIO, + "STATUS_FWP_CONTEXT_INCOMPATIBLE_WITH_CALLOUT"}, + {STATUS_FWP_INCOMPATIBLE_AUTH_METHOD, -EIO, + "STATUS_FWP_INCOMPATIBLE_AUTH_METHOD"}, + {STATUS_FWP_INCOMPATIBLE_DH_GROUP, -EIO, + "STATUS_FWP_INCOMPATIBLE_DH_GROUP"}, + {STATUS_FWP_EM_NOT_SUPPORTED, -EOPNOTSUPP, + "STATUS_FWP_EM_NOT_SUPPORTED"}, + {STATUS_FWP_NEVER_MATCH, -EIO, "STATUS_FWP_NEVER_MATCH"}, + {STATUS_FWP_PROVIDER_CONTEXT_MISMATCH, -EIO, + "STATUS_FWP_PROVIDER_CONTEXT_MISMATCH"}, + {STATUS_FWP_INVALID_PARAMETER, -EIO, "STATUS_FWP_INVALID_PARAMETER"}, + {STATUS_FWP_TOO_MANY_SUBLAYERS, -EIO, "STATUS_FWP_TOO_MANY_SUBLAYERS"}, + {STATUS_FWP_CALLOUT_NOTIFICATION_FAILED, -EIO, + "STATUS_FWP_CALLOUT_NOTIFICATION_FAILED"}, + {STATUS_FWP_INCOMPATIBLE_AUTH_CONFIG, -EIO, + "STATUS_FWP_INCOMPATIBLE_AUTH_CONFIG"}, + {STATUS_FWP_INCOMPATIBLE_CIPHER_CONFIG, -EIO, + "STATUS_FWP_INCOMPATIBLE_CIPHER_CONFIG"}, + {STATUS_FWP_TCPIP_NOT_READY, -EIO, "STATUS_FWP_TCPIP_NOT_READY"}, + {STATUS_FWP_INJECT_HANDLE_CLOSING, -EIO, + "STATUS_FWP_INJECT_HANDLE_CLOSING"}, + {STATUS_FWP_INJECT_HANDLE_STALE, -EIO, + "STATUS_FWP_INJECT_HANDLE_STALE"}, + {STATUS_FWP_CANNOT_PEND, -EIO, "STATUS_FWP_CANNOT_PEND"}, + {STATUS_NDIS_CLOSING, -EIO, "STATUS_NDIS_CLOSING"}, + {STATUS_NDIS_BAD_VERSION, -EIO, "STATUS_NDIS_BAD_VERSION"}, + {STATUS_NDIS_BAD_CHARACTERISTICS, -EIO, + "STATUS_NDIS_BAD_CHARACTERISTICS"}, + {STATUS_NDIS_ADAPTER_NOT_FOUND, -EIO, "STATUS_NDIS_ADAPTER_NOT_FOUND"}, + {STATUS_NDIS_OPEN_FAILED, -EIO, "STATUS_NDIS_OPEN_FAILED"}, + {STATUS_NDIS_DEVICE_FAILED, -EIO, "STATUS_NDIS_DEVICE_FAILED"}, + {STATUS_NDIS_MULTICAST_FULL, -EIO, "STATUS_NDIS_MULTICAST_FULL"}, + {STATUS_NDIS_MULTICAST_EXISTS, -EIO, "STATUS_NDIS_MULTICAST_EXISTS"}, + {STATUS_NDIS_MULTICAST_NOT_FOUND, -EIO, + "STATUS_NDIS_MULTICAST_NOT_FOUND"}, + {STATUS_NDIS_REQUEST_ABORTED, -EIO, "STATUS_NDIS_REQUEST_ABORTED"}, + {STATUS_NDIS_RESET_IN_PROGRESS, -EIO, "STATUS_NDIS_RESET_IN_PROGRESS"}, + {STATUS_NDIS_INVALID_PACKET, -EIO, "STATUS_NDIS_INVALID_PACKET"}, + {STATUS_NDIS_INVALID_DEVICE_REQUEST, -EIO, + "STATUS_NDIS_INVALID_DEVICE_REQUEST"}, + {STATUS_NDIS_ADAPTER_NOT_READY, -EIO, "STATUS_NDIS_ADAPTER_NOT_READY"}, + {STATUS_NDIS_INVALID_LENGTH, -EIO, "STATUS_NDIS_INVALID_LENGTH"}, + {STATUS_NDIS_INVALID_DATA, -EIO, "STATUS_NDIS_INVALID_DATA"}, + {STATUS_NDIS_BUFFER_TOO_SHORT, -ENOBUFS, + "STATUS_NDIS_BUFFER_TOO_SHORT"}, + {STATUS_NDIS_INVALID_OID, -EIO, "STATUS_NDIS_INVALID_OID"}, + {STATUS_NDIS_ADAPTER_REMOVED, -EIO, "STATUS_NDIS_ADAPTER_REMOVED"}, + {STATUS_NDIS_UNSUPPORTED_MEDIA, -EIO, "STATUS_NDIS_UNSUPPORTED_MEDIA"}, + {STATUS_NDIS_GROUP_ADDRESS_IN_USE, -EIO, + "STATUS_NDIS_GROUP_ADDRESS_IN_USE"}, + {STATUS_NDIS_FILE_NOT_FOUND, -EIO, "STATUS_NDIS_FILE_NOT_FOUND"}, + {STATUS_NDIS_ERROR_READING_FILE, -EIO, + "STATUS_NDIS_ERROR_READING_FILE"}, + {STATUS_NDIS_ALREADY_MAPPED, -EIO, "STATUS_NDIS_ALREADY_MAPPED"}, + {STATUS_NDIS_RESOURCE_CONFLICT, -EIO, "STATUS_NDIS_RESOURCE_CONFLICT"}, + {STATUS_NDIS_MEDIA_DISCONNECTED, -EIO, + "STATUS_NDIS_MEDIA_DISCONNECTED"}, + {STATUS_NDIS_INVALID_ADDRESS, -EIO, "STATUS_NDIS_INVALID_ADDRESS"}, + {STATUS_NDIS_PAUSED, -EIO, "STATUS_NDIS_PAUSED"}, + {STATUS_NDIS_INTERFACE_NOT_FOUND, -EIO, + "STATUS_NDIS_INTERFACE_NOT_FOUND"}, + {STATUS_NDIS_UNSUPPORTED_REVISION, -EIO, + "STATUS_NDIS_UNSUPPORTED_REVISION"}, + {STATUS_NDIS_INVALID_PORT, -EIO, "STATUS_NDIS_INVALID_PORT"}, + {STATUS_NDIS_INVALID_PORT_STATE, -EIO, + "STATUS_NDIS_INVALID_PORT_STATE"}, + {STATUS_NDIS_LOW_POWER_STATE, -EIO, "STATUS_NDIS_LOW_POWER_STATE"}, + {STATUS_NDIS_NOT_SUPPORTED, -ENOSYS, "STATUS_NDIS_NOT_SUPPORTED"}, + {STATUS_NDIS_DOT11_AUTO_CONFIG_ENABLED, -EIO, + "STATUS_NDIS_DOT11_AUTO_CONFIG_ENABLED"}, + {STATUS_NDIS_DOT11_MEDIA_IN_USE, -EIO, + "STATUS_NDIS_DOT11_MEDIA_IN_USE"}, + {STATUS_NDIS_DOT11_POWER_STATE_INVALID, -EIO, + "STATUS_NDIS_DOT11_POWER_STATE_INVALID"}, + {STATUS_IPSEC_BAD_SPI, -EIO, "STATUS_IPSEC_BAD_SPI"}, + {STATUS_IPSEC_SA_LIFETIME_EXPIRED, -EIO, + "STATUS_IPSEC_SA_LIFETIME_EXPIRED"}, + {STATUS_IPSEC_WRONG_SA, -EIO, "STATUS_IPSEC_WRONG_SA"}, + {STATUS_IPSEC_REPLAY_CHECK_FAILED, -EIO, + "STATUS_IPSEC_REPLAY_CHECK_FAILED"}, + {STATUS_IPSEC_INVALID_PACKET, -EIO, "STATUS_IPSEC_INVALID_PACKET"}, + {STATUS_IPSEC_INTEGRITY_CHECK_FAILED, -EIO, + "STATUS_IPSEC_INTEGRITY_CHECK_FAILED"}, + {STATUS_IPSEC_CLEAR_TEXT_DROP, -EIO, "STATUS_IPSEC_CLEAR_TEXT_DROP"}, + {0, 0, NULL} +}; + +/***************************************************************************** + Print an error message from the status code + *****************************************************************************/ +static void +smb2_print_status(__le32 status) +{ + int idx = 0; + + while (smb2_error_map_table[idx].status_string != NULL) { + if ((smb2_error_map_table[idx].smb2_status) == status) { + pr_notice("Status code returned 0x%08x %s\n", status, + smb2_error_map_table[idx].status_string); + } + idx++; + } + return; +} + +int +map_smb2_to_linux_error(char *buf, bool log_err) +{ + struct smb2_hdr *shdr = (struct smb2_hdr *)buf; + unsigned int i; + int rc = -EIO; + __le32 smb2err = shdr->Status; + + if (smb2err == 0) { + trace_smb3_cmd_done(le32_to_cpu(shdr->Id.SyncId.TreeId), + le64_to_cpu(shdr->SessionId), + le16_to_cpu(shdr->Command), + le64_to_cpu(shdr->MessageId)); + return 0; + } + + /* mask facility */ + if (log_err && (smb2err != STATUS_MORE_PROCESSING_REQUIRED) && + (smb2err != STATUS_END_OF_FILE)) + smb2_print_status(smb2err); + else if (cifsFYI & CIFS_RC) + smb2_print_status(smb2err); + + for (i = 0; i < sizeof(smb2_error_map_table) / + sizeof(struct status_to_posix_error); i++) { + if (smb2_error_map_table[i].smb2_status == smb2err) { + rc = smb2_error_map_table[i].posix_error; + break; + } + } + + /* on error mapping not found - return EIO */ + + cifs_dbg(FYI, "Mapping SMB2 status code 0x%08x to POSIX err %d\n", + __le32_to_cpu(smb2err), rc); + + trace_smb3_cmd_err(le32_to_cpu(shdr->Id.SyncId.TreeId), + le64_to_cpu(shdr->SessionId), + le16_to_cpu(shdr->Command), + le64_to_cpu(shdr->MessageId), + le32_to_cpu(smb2err), rc); + return rc; +} diff --git a/fs/smb/client/smb2misc.c b/fs/smb/client/smb2misc.c new file mode 100644 index 0000000000000..572293c18e16f --- /dev/null +++ b/fs/smb/client/smb2misc.c @@ -0,0 +1,944 @@ +// SPDX-License-Identifier: LGPL-2.1 +/* + * + * Copyright (C) International Business Machines Corp., 2002,2011 + * Etersoft, 2012 + * Author(s): Steve French (sfrench@us.ibm.com) + * Pavel Shilovsky (pshilovsky@samba.org) 2012 + * + */ +#include +#include "cifsglob.h" +#include "cifsproto.h" +#include "smb2proto.h" +#include "cifs_debug.h" +#include "cifs_unicode.h" +#include "smb2status.h" +#include "smb2glob.h" +#include "nterr.h" +#include "cached_dir.h" + +static int +check_smb2_hdr(struct smb2_hdr *shdr, __u64 mid) +{ + __u64 wire_mid = le64_to_cpu(shdr->MessageId); + + /* + * Make sure that this really is an SMB, that it is a response, + * and that the message ids match. + */ + if ((shdr->ProtocolId == SMB2_PROTO_NUMBER) && + (mid == wire_mid)) { + if (shdr->Flags & SMB2_FLAGS_SERVER_TO_REDIR) + return 0; + else { + /* only one valid case where server sends us request */ + if (shdr->Command == SMB2_OPLOCK_BREAK) + return 0; + else + cifs_dbg(VFS, "Received Request not response\n"); + } + } else { /* bad signature or mid */ + if (shdr->ProtocolId != SMB2_PROTO_NUMBER) + cifs_dbg(VFS, "Bad protocol string signature header %x\n", + le32_to_cpu(shdr->ProtocolId)); + if (mid != wire_mid) + cifs_dbg(VFS, "Mids do not match: %llu and %llu\n", + mid, wire_mid); + } + cifs_dbg(VFS, "Bad SMB detected. The Mid=%llu\n", wire_mid); + return 1; +} + +/* + * The following table defines the expected "StructureSize" of SMB2 responses + * in order by SMB2 command. This is similar to "wct" in SMB/CIFS responses. + * + * Note that commands are defined in smb2pdu.h in le16 but the array below is + * indexed by command in host byte order + */ +static const __le16 smb2_rsp_struct_sizes[NUMBER_OF_SMB2_COMMANDS] = { + /* SMB2_NEGOTIATE */ cpu_to_le16(65), + /* SMB2_SESSION_SETUP */ cpu_to_le16(9), + /* SMB2_LOGOFF */ cpu_to_le16(4), + /* SMB2_TREE_CONNECT */ cpu_to_le16(16), + /* SMB2_TREE_DISCONNECT */ cpu_to_le16(4), + /* SMB2_CREATE */ cpu_to_le16(89), + /* SMB2_CLOSE */ cpu_to_le16(60), + /* SMB2_FLUSH */ cpu_to_le16(4), + /* SMB2_READ */ cpu_to_le16(17), + /* SMB2_WRITE */ cpu_to_le16(17), + /* SMB2_LOCK */ cpu_to_le16(4), + /* SMB2_IOCTL */ cpu_to_le16(49), + /* BB CHECK this ... not listed in documentation */ + /* SMB2_CANCEL */ cpu_to_le16(0), + /* SMB2_ECHO */ cpu_to_le16(4), + /* SMB2_QUERY_DIRECTORY */ cpu_to_le16(9), + /* SMB2_CHANGE_NOTIFY */ cpu_to_le16(9), + /* SMB2_QUERY_INFO */ cpu_to_le16(9), + /* SMB2_SET_INFO */ cpu_to_le16(2), + /* BB FIXME can also be 44 for lease break */ + /* SMB2_OPLOCK_BREAK */ cpu_to_le16(24) +}; + +#define SMB311_NEGPROT_BASE_SIZE (sizeof(struct smb2_hdr) + sizeof(struct smb2_negotiate_rsp)) + +static __u32 get_neg_ctxt_len(struct smb2_hdr *hdr, __u32 len, + __u32 non_ctxlen) +{ + __u16 neg_count; + __u32 nc_offset, size_of_pad_before_neg_ctxts; + struct smb2_negotiate_rsp *pneg_rsp = (struct smb2_negotiate_rsp *)hdr; + + /* Negotiate contexts are only valid for latest dialect SMB3.11 */ + neg_count = le16_to_cpu(pneg_rsp->NegotiateContextCount); + if ((neg_count == 0) || + (pneg_rsp->DialectRevision != cpu_to_le16(SMB311_PROT_ID))) + return 0; + + /* + * if SPNEGO blob present (ie the RFC2478 GSS info which indicates + * which security mechanisms the server supports) make sure that + * the negotiate contexts start after it + */ + nc_offset = le32_to_cpu(pneg_rsp->NegotiateContextOffset); + /* + * non_ctxlen is at least shdr->StructureSize + pdu->StructureSize2 + * and the latter is 1 byte bigger than the fix-sized area of the + * NEGOTIATE response + */ + if (nc_offset + 1 < non_ctxlen) { + pr_warn_once("Invalid negotiate context offset %d\n", nc_offset); + return 0; + } else if (nc_offset + 1 == non_ctxlen) { + cifs_dbg(FYI, "no SPNEGO security blob in negprot rsp\n"); + size_of_pad_before_neg_ctxts = 0; + } else if (non_ctxlen == SMB311_NEGPROT_BASE_SIZE) + /* has padding, but no SPNEGO blob */ + size_of_pad_before_neg_ctxts = nc_offset - non_ctxlen + 1; + else + size_of_pad_before_neg_ctxts = nc_offset - non_ctxlen; + + /* Verify that at least minimal negotiate contexts fit within frame */ + if (len < nc_offset + (neg_count * sizeof(struct smb2_neg_context))) { + pr_warn_once("negotiate context goes beyond end\n"); + return 0; + } + + cifs_dbg(FYI, "length of negcontexts %d pad %d\n", + len - nc_offset, size_of_pad_before_neg_ctxts); + + /* length of negcontexts including pad from end of sec blob to them */ + return (len - nc_offset) + size_of_pad_before_neg_ctxts; +} + +int +smb2_check_message(char *buf, unsigned int len, struct TCP_Server_Info *server) +{ + struct TCP_Server_Info *pserver; + struct smb2_hdr *shdr = (struct smb2_hdr *)buf; + struct smb2_pdu *pdu = (struct smb2_pdu *)shdr; + int hdr_size = sizeof(struct smb2_hdr); + int pdu_size = sizeof(struct smb2_pdu); + int command; + __u32 calc_len; /* calculated length */ + __u64 mid; + + /* If server is a channel, select the primary channel */ + pserver = CIFS_SERVER_IS_CHAN(server) ? server->primary_server : server; + + /* + * Add function to do table lookup of StructureSize by command + * ie Validate the wct via smb2_struct_sizes table above + */ + if (shdr->ProtocolId == SMB2_TRANSFORM_PROTO_NUM) { + struct smb2_transform_hdr *thdr = + (struct smb2_transform_hdr *)buf; + struct cifs_ses *ses = NULL; + struct cifs_ses *iter; + + /* decrypt frame now that it is completely read in */ + spin_lock(&cifs_tcp_ses_lock); + list_for_each_entry(iter, &pserver->smb_ses_list, smb_ses_list) { + if (iter->Suid == le64_to_cpu(thdr->SessionId)) { + ses = iter; + break; + } + } + spin_unlock(&cifs_tcp_ses_lock); + if (!ses) { + cifs_dbg(VFS, "no decryption - session id not found\n"); + return 1; + } + } + + mid = le64_to_cpu(shdr->MessageId); + if (len < pdu_size) { + if ((len >= hdr_size) + && (shdr->Status != 0)) { + pdu->StructureSize2 = 0; + /* + * As with SMB/CIFS, on some error cases servers may + * not return wct properly + */ + return 0; + } else { + cifs_dbg(VFS, "Length less than SMB header size\n"); + } + return 1; + } + if (len > CIFSMaxBufSize + MAX_SMB2_HDR_SIZE) { + cifs_dbg(VFS, "SMB length greater than maximum, mid=%llu\n", + mid); + return 1; + } + + if (check_smb2_hdr(shdr, mid)) + return 1; + + if (shdr->StructureSize != SMB2_HEADER_STRUCTURE_SIZE) { + cifs_dbg(VFS, "Invalid structure size %u\n", + le16_to_cpu(shdr->StructureSize)); + return 1; + } + + command = le16_to_cpu(shdr->Command); + if (command >= NUMBER_OF_SMB2_COMMANDS) { + cifs_dbg(VFS, "Invalid SMB2 command %d\n", command); + return 1; + } + + if (smb2_rsp_struct_sizes[command] != pdu->StructureSize2) { + if (command != SMB2_OPLOCK_BREAK_HE && (shdr->Status == 0 || + pdu->StructureSize2 != SMB2_ERROR_STRUCTURE_SIZE2_LE)) { + /* error packets have 9 byte structure size */ + cifs_dbg(VFS, "Invalid response size %u for command %d\n", + le16_to_cpu(pdu->StructureSize2), command); + return 1; + } else if (command == SMB2_OPLOCK_BREAK_HE + && (shdr->Status == 0) + && (le16_to_cpu(pdu->StructureSize2) != 44) + && (le16_to_cpu(pdu->StructureSize2) != 36)) { + /* special case for SMB2.1 lease break message */ + cifs_dbg(VFS, "Invalid response size %d for oplock break\n", + le16_to_cpu(pdu->StructureSize2)); + return 1; + } + } + + calc_len = smb2_calc_size(buf); + + /* For SMB2_IOCTL, OutputOffset and OutputLength are optional, so might + * be 0, and not a real miscalculation */ + if (command == SMB2_IOCTL_HE && calc_len == 0) + return 0; + + if (command == SMB2_NEGOTIATE_HE) + calc_len += get_neg_ctxt_len(shdr, len, calc_len); + + if (len != calc_len) { + /* create failed on symlink */ + if (command == SMB2_CREATE_HE && + shdr->Status == STATUS_STOPPED_ON_SYMLINK) + return 0; + /* Windows 7 server returns 24 bytes more */ + if (calc_len + 24 == len && command == SMB2_OPLOCK_BREAK_HE) + return 0; + /* server can return one byte more due to implied bcc[0] */ + if (calc_len == len + 1) + return 0; + + /* + * Some windows servers (win2016) will pad also the final + * PDU in a compound to 8 bytes. + */ + if (ALIGN(calc_len, 8) == len) + return 0; + + /* + * MacOS server pads after SMB2.1 write response with 3 bytes + * of junk. Other servers match RFC1001 len to actual + * SMB2/SMB3 frame length (header + smb2 response specific data) + * Some windows servers also pad up to 8 bytes when compounding. + */ + if (calc_len < len) + return 0; + + /* Only log a message if len was really miscalculated */ + if (unlikely(cifsFYI)) + cifs_dbg(FYI, "Server response too short: calculated " + "length %u doesn't match read length %u (cmd=%d, mid=%llu)\n", + calc_len, len, command, mid); + else + pr_warn("Server response too short: calculated length " + "%u doesn't match read length %u (cmd=%d, mid=%llu)\n", + calc_len, len, command, mid); + + return 1; + } + return 0; +} + +/* + * The size of the variable area depends on the offset and length fields + * located in different fields for various SMB2 responses. SMB2 responses + * with no variable length info, show an offset of zero for the offset field. + */ +static const bool has_smb2_data_area[NUMBER_OF_SMB2_COMMANDS] = { + /* SMB2_NEGOTIATE */ true, + /* SMB2_SESSION_SETUP */ true, + /* SMB2_LOGOFF */ false, + /* SMB2_TREE_CONNECT */ false, + /* SMB2_TREE_DISCONNECT */ false, + /* SMB2_CREATE */ true, + /* SMB2_CLOSE */ false, + /* SMB2_FLUSH */ false, + /* SMB2_READ */ true, + /* SMB2_WRITE */ false, + /* SMB2_LOCK */ false, + /* SMB2_IOCTL */ true, + /* SMB2_CANCEL */ false, /* BB CHECK this not listed in documentation */ + /* SMB2_ECHO */ false, + /* SMB2_QUERY_DIRECTORY */ true, + /* SMB2_CHANGE_NOTIFY */ true, + /* SMB2_QUERY_INFO */ true, + /* SMB2_SET_INFO */ false, + /* SMB2_OPLOCK_BREAK */ false +}; + +/* + * Returns the pointer to the beginning of the data area. Length of the data + * area and the offset to it (from the beginning of the smb are also returned. + */ +char * +smb2_get_data_area_len(int *off, int *len, struct smb2_hdr *shdr) +{ + *off = 0; + *len = 0; + + /* error responses do not have data area */ + if (shdr->Status && shdr->Status != STATUS_MORE_PROCESSING_REQUIRED && + (((struct smb2_err_rsp *)shdr)->StructureSize) == + SMB2_ERROR_STRUCTURE_SIZE2_LE) + return NULL; + + /* + * Following commands have data areas so we have to get the location + * of the data buffer offset and data buffer length for the particular + * command. + */ + switch (shdr->Command) { + case SMB2_NEGOTIATE: + *off = le16_to_cpu( + ((struct smb2_negotiate_rsp *)shdr)->SecurityBufferOffset); + *len = le16_to_cpu( + ((struct smb2_negotiate_rsp *)shdr)->SecurityBufferLength); + break; + case SMB2_SESSION_SETUP: + *off = le16_to_cpu( + ((struct smb2_sess_setup_rsp *)shdr)->SecurityBufferOffset); + *len = le16_to_cpu( + ((struct smb2_sess_setup_rsp *)shdr)->SecurityBufferLength); + break; + case SMB2_CREATE: + *off = le32_to_cpu( + ((struct smb2_create_rsp *)shdr)->CreateContextsOffset); + *len = le32_to_cpu( + ((struct smb2_create_rsp *)shdr)->CreateContextsLength); + break; + case SMB2_QUERY_INFO: + *off = le16_to_cpu( + ((struct smb2_query_info_rsp *)shdr)->OutputBufferOffset); + *len = le32_to_cpu( + ((struct smb2_query_info_rsp *)shdr)->OutputBufferLength); + break; + case SMB2_READ: + /* TODO: is this a bug ? */ + *off = ((struct smb2_read_rsp *)shdr)->DataOffset; + *len = le32_to_cpu(((struct smb2_read_rsp *)shdr)->DataLength); + break; + case SMB2_QUERY_DIRECTORY: + *off = le16_to_cpu( + ((struct smb2_query_directory_rsp *)shdr)->OutputBufferOffset); + *len = le32_to_cpu( + ((struct smb2_query_directory_rsp *)shdr)->OutputBufferLength); + break; + case SMB2_IOCTL: + *off = le32_to_cpu( + ((struct smb2_ioctl_rsp *)shdr)->OutputOffset); + *len = le32_to_cpu( + ((struct smb2_ioctl_rsp *)shdr)->OutputCount); + break; + case SMB2_CHANGE_NOTIFY: + *off = le16_to_cpu( + ((struct smb2_change_notify_rsp *)shdr)->OutputBufferOffset); + *len = le32_to_cpu( + ((struct smb2_change_notify_rsp *)shdr)->OutputBufferLength); + break; + default: + cifs_dbg(VFS, "no length check for command %d\n", le16_to_cpu(shdr->Command)); + break; + } + + /* + * Invalid length or offset probably means data area is invalid, but + * we have little choice but to ignore the data area in this case. + */ + if (*off > 4096) { + cifs_dbg(VFS, "offset %d too large, data area ignored\n", *off); + *len = 0; + *off = 0; + } else if (*off < 0) { + cifs_dbg(VFS, "negative offset %d to data invalid ignore data area\n", + *off); + *off = 0; + *len = 0; + } else if (*len < 0) { + cifs_dbg(VFS, "negative data length %d invalid, data area ignored\n", + *len); + *len = 0; + } else if (*len > 128 * 1024) { + cifs_dbg(VFS, "data area larger than 128K: %d\n", *len); + *len = 0; + } + + /* return pointer to beginning of data area, ie offset from SMB start */ + if ((*off != 0) && (*len != 0)) + return (char *)shdr + *off; + else + return NULL; +} + +/* + * Calculate the size of the SMB message based on the fixed header + * portion, the number of word parameters and the data portion of the message. + */ +unsigned int +smb2_calc_size(void *buf) +{ + struct smb2_pdu *pdu = buf; + struct smb2_hdr *shdr = &pdu->hdr; + int offset; /* the offset from the beginning of SMB to data area */ + int data_length; /* the length of the variable length data area */ + /* Structure Size has already been checked to make sure it is 64 */ + int len = le16_to_cpu(shdr->StructureSize); + + /* + * StructureSize2, ie length of fixed parameter area has already + * been checked to make sure it is the correct length. + */ + len += le16_to_cpu(pdu->StructureSize2); + + if (has_smb2_data_area[le16_to_cpu(shdr->Command)] == false) + goto calc_size_exit; + + smb2_get_data_area_len(&offset, &data_length, shdr); + cifs_dbg(FYI, "SMB2 data length %d offset %d\n", data_length, offset); + + if (data_length > 0) { + /* + * Check to make sure that data area begins after fixed area, + * Note that last byte of the fixed area is part of data area + * for some commands, typically those with odd StructureSize, + * so we must add one to the calculation. + */ + if (offset + 1 < len) { + cifs_dbg(VFS, "data area offset %d overlaps SMB2 header %d\n", + offset + 1, len); + data_length = 0; + } else { + len = offset + data_length; + } + } +calc_size_exit: + cifs_dbg(FYI, "SMB2 len %d\n", len); + return len; +} + +/* Note: caller must free return buffer */ +__le16 * +cifs_convert_path_to_utf16(const char *from, struct cifs_sb_info *cifs_sb) +{ + int len; + const char *start_of_path; + __le16 *to; + int map_type; + + if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_MAP_SFM_CHR) + map_type = SFM_MAP_UNI_RSVD; + else if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_MAP_SPECIAL_CHR) + map_type = SFU_MAP_UNI_RSVD; + else + map_type = NO_MAP_UNI_RSVD; + + /* Windows doesn't allow paths beginning with \ */ + if (from[0] == '\\') + start_of_path = from + 1; + + /* SMB311 POSIX extensions paths do not include leading slash */ + else if (cifs_sb_master_tlink(cifs_sb) && + cifs_sb_master_tcon(cifs_sb)->posix_extensions && + (from[0] == '/')) { + start_of_path = from + 1; + } else + start_of_path = from; + + to = cifs_strndup_to_utf16(start_of_path, PATH_MAX, &len, + cifs_sb->local_nls, map_type); + return to; +} + +__le32 +smb2_get_lease_state(struct cifsInodeInfo *cinode) +{ + __le32 lease = 0; + + if (CIFS_CACHE_WRITE(cinode)) + lease |= SMB2_LEASE_WRITE_CACHING_LE; + if (CIFS_CACHE_HANDLE(cinode)) + lease |= SMB2_LEASE_HANDLE_CACHING_LE; + if (CIFS_CACHE_READ(cinode)) + lease |= SMB2_LEASE_READ_CACHING_LE; + return lease; +} + +struct smb2_lease_break_work { + struct work_struct lease_break; + struct tcon_link *tlink; + __u8 lease_key[16]; + __le32 lease_state; +}; + +static void +cifs_ses_oplock_break(struct work_struct *work) +{ + struct smb2_lease_break_work *lw = container_of(work, + struct smb2_lease_break_work, lease_break); + int rc = 0; + + rc = SMB2_lease_break(0, tlink_tcon(lw->tlink), lw->lease_key, + lw->lease_state); + + cifs_dbg(FYI, "Lease release rc %d\n", rc); + cifs_put_tlink(lw->tlink); + kfree(lw); +} + +static void +smb2_queue_pending_open_break(struct tcon_link *tlink, __u8 *lease_key, + __le32 new_lease_state) +{ + struct smb2_lease_break_work *lw; + + lw = kmalloc(sizeof(struct smb2_lease_break_work), GFP_KERNEL); + if (!lw) { + cifs_put_tlink(tlink); + return; + } + + INIT_WORK(&lw->lease_break, cifs_ses_oplock_break); + lw->tlink = tlink; + lw->lease_state = new_lease_state; + memcpy(lw->lease_key, lease_key, SMB2_LEASE_KEY_SIZE); + queue_work(cifsiod_wq, &lw->lease_break); +} + +static bool +smb2_tcon_has_lease(struct cifs_tcon *tcon, struct smb2_lease_break *rsp) +{ + __u8 lease_state; + struct cifsFileInfo *cfile; + struct cifsInodeInfo *cinode; + int ack_req = le32_to_cpu(rsp->Flags & + SMB2_NOTIFY_BREAK_LEASE_FLAG_ACK_REQUIRED); + + lease_state = le32_to_cpu(rsp->NewLeaseState); + + list_for_each_entry(cfile, &tcon->openFileList, tlist) { + cinode = CIFS_I(d_inode(cfile->dentry)); + + if (memcmp(cinode->lease_key, rsp->LeaseKey, + SMB2_LEASE_KEY_SIZE)) + continue; + + cifs_dbg(FYI, "found in the open list\n"); + cifs_dbg(FYI, "lease key match, lease break 0x%x\n", + lease_state); + + if (ack_req) + cfile->oplock_break_cancelled = false; + else + cfile->oplock_break_cancelled = true; + + set_bit(CIFS_INODE_PENDING_OPLOCK_BREAK, &cinode->flags); + + cfile->oplock_epoch = le16_to_cpu(rsp->Epoch); + cfile->oplock_level = lease_state; + + cifs_queue_oplock_break(cfile); + return true; + } + + return false; +} + +static struct cifs_pending_open * +smb2_tcon_find_pending_open_lease(struct cifs_tcon *tcon, + struct smb2_lease_break *rsp) +{ + __u8 lease_state = le32_to_cpu(rsp->NewLeaseState); + int ack_req = le32_to_cpu(rsp->Flags & + SMB2_NOTIFY_BREAK_LEASE_FLAG_ACK_REQUIRED); + struct cifs_pending_open *open; + struct cifs_pending_open *found = NULL; + + list_for_each_entry(open, &tcon->pending_opens, olist) { + if (memcmp(open->lease_key, rsp->LeaseKey, + SMB2_LEASE_KEY_SIZE)) + continue; + + if (!found && ack_req) { + found = open; + } + + cifs_dbg(FYI, "found in the pending open list\n"); + cifs_dbg(FYI, "lease key match, lease break 0x%x\n", + lease_state); + + open->oplock = lease_state; + } + + return found; +} + +static bool +smb2_is_valid_lease_break(char *buffer, struct TCP_Server_Info *server) +{ + struct smb2_lease_break *rsp = (struct smb2_lease_break *)buffer; + struct TCP_Server_Info *pserver; + struct cifs_ses *ses; + struct cifs_tcon *tcon; + struct cifs_pending_open *open; + + cifs_dbg(FYI, "Checking for lease break\n"); + + /* If server is a channel, select the primary channel */ + pserver = CIFS_SERVER_IS_CHAN(server) ? server->primary_server : server; + + /* look up tcon based on tid & uid */ + spin_lock(&cifs_tcp_ses_lock); + list_for_each_entry(ses, &pserver->smb_ses_list, smb_ses_list) { + list_for_each_entry(tcon, &ses->tcon_list, tcon_list) { + spin_lock(&tcon->open_file_lock); + cifs_stats_inc( + &tcon->stats.cifs_stats.num_oplock_brks); + if (smb2_tcon_has_lease(tcon, rsp)) { + spin_unlock(&tcon->open_file_lock); + spin_unlock(&cifs_tcp_ses_lock); + return true; + } + open = smb2_tcon_find_pending_open_lease(tcon, + rsp); + if (open) { + __u8 lease_key[SMB2_LEASE_KEY_SIZE]; + struct tcon_link *tlink; + + tlink = cifs_get_tlink(open->tlink); + memcpy(lease_key, open->lease_key, + SMB2_LEASE_KEY_SIZE); + spin_unlock(&tcon->open_file_lock); + spin_unlock(&cifs_tcp_ses_lock); + smb2_queue_pending_open_break(tlink, + lease_key, + rsp->NewLeaseState); + return true; + } + spin_unlock(&tcon->open_file_lock); + + if (cached_dir_lease_break(tcon, rsp->LeaseKey)) { + spin_unlock(&cifs_tcp_ses_lock); + return true; + } + } + } + spin_unlock(&cifs_tcp_ses_lock); + cifs_dbg(FYI, "Can not process lease break - no lease matched\n"); + trace_smb3_lease_not_found(le32_to_cpu(rsp->CurrentLeaseState), + le32_to_cpu(rsp->hdr.Id.SyncId.TreeId), + le64_to_cpu(rsp->hdr.SessionId), + *((u64 *)rsp->LeaseKey), + *((u64 *)&rsp->LeaseKey[8])); + + return false; +} + +bool +smb2_is_valid_oplock_break(char *buffer, struct TCP_Server_Info *server) +{ + struct smb2_oplock_break *rsp = (struct smb2_oplock_break *)buffer; + struct TCP_Server_Info *pserver; + struct cifs_ses *ses; + struct cifs_tcon *tcon; + struct cifsInodeInfo *cinode; + struct cifsFileInfo *cfile; + + cifs_dbg(FYI, "Checking for oplock break\n"); + + if (rsp->hdr.Command != SMB2_OPLOCK_BREAK) + return false; + + if (rsp->StructureSize != + smb2_rsp_struct_sizes[SMB2_OPLOCK_BREAK_HE]) { + if (le16_to_cpu(rsp->StructureSize) == 44) + return smb2_is_valid_lease_break(buffer, server); + else + return false; + } + + cifs_dbg(FYI, "oplock level 0x%x\n", rsp->OplockLevel); + + /* If server is a channel, select the primary channel */ + pserver = CIFS_SERVER_IS_CHAN(server) ? server->primary_server : server; + + /* look up tcon based on tid & uid */ + spin_lock(&cifs_tcp_ses_lock); + list_for_each_entry(ses, &pserver->smb_ses_list, smb_ses_list) { + list_for_each_entry(tcon, &ses->tcon_list, tcon_list) { + + spin_lock(&tcon->open_file_lock); + list_for_each_entry(cfile, &tcon->openFileList, tlist) { + if (rsp->PersistentFid != + cfile->fid.persistent_fid || + rsp->VolatileFid != + cfile->fid.volatile_fid) + continue; + + cifs_dbg(FYI, "file id match, oplock break\n"); + cifs_stats_inc( + &tcon->stats.cifs_stats.num_oplock_brks); + cinode = CIFS_I(d_inode(cfile->dentry)); + spin_lock(&cfile->file_info_lock); + if (!CIFS_CACHE_WRITE(cinode) && + rsp->OplockLevel == SMB2_OPLOCK_LEVEL_NONE) + cfile->oplock_break_cancelled = true; + else + cfile->oplock_break_cancelled = false; + + set_bit(CIFS_INODE_PENDING_OPLOCK_BREAK, + &cinode->flags); + + cfile->oplock_epoch = 0; + cfile->oplock_level = rsp->OplockLevel; + + spin_unlock(&cfile->file_info_lock); + + cifs_queue_oplock_break(cfile); + + spin_unlock(&tcon->open_file_lock); + spin_unlock(&cifs_tcp_ses_lock); + return true; + } + spin_unlock(&tcon->open_file_lock); + } + } + spin_unlock(&cifs_tcp_ses_lock); + cifs_dbg(FYI, "No file id matched, oplock break ignored\n"); + trace_smb3_oplock_not_found(0 /* no xid */, rsp->PersistentFid, + le32_to_cpu(rsp->hdr.Id.SyncId.TreeId), + le64_to_cpu(rsp->hdr.SessionId)); + + return true; +} + +void +smb2_cancelled_close_fid(struct work_struct *work) +{ + struct close_cancelled_open *cancelled = container_of(work, + struct close_cancelled_open, work); + struct cifs_tcon *tcon = cancelled->tcon; + int rc; + + if (cancelled->mid) + cifs_tcon_dbg(VFS, "Close unmatched open for MID:%llu\n", + cancelled->mid); + else + cifs_tcon_dbg(VFS, "Close interrupted close\n"); + + rc = SMB2_close(0, tcon, cancelled->fid.persistent_fid, + cancelled->fid.volatile_fid); + if (rc) + cifs_tcon_dbg(VFS, "Close cancelled mid failed rc:%d\n", rc); + + cifs_put_tcon(tcon); + kfree(cancelled); +} + +/* + * Caller should already has an extra reference to @tcon + * This function is used to queue work to close a handle to prevent leaks + * on the server. + * We handle two cases. If an open was interrupted after we sent the + * SMB2_CREATE to the server but before we processed the reply, and second + * if a close was interrupted before we sent the SMB2_CLOSE to the server. + */ +static int +__smb2_handle_cancelled_cmd(struct cifs_tcon *tcon, __u16 cmd, __u64 mid, + __u64 persistent_fid, __u64 volatile_fid) +{ + struct close_cancelled_open *cancelled; + + cancelled = kzalloc(sizeof(*cancelled), GFP_ATOMIC); + if (!cancelled) + return -ENOMEM; + + cancelled->fid.persistent_fid = persistent_fid; + cancelled->fid.volatile_fid = volatile_fid; + cancelled->tcon = tcon; + cancelled->cmd = cmd; + cancelled->mid = mid; + INIT_WORK(&cancelled->work, smb2_cancelled_close_fid); + WARN_ON(queue_work(cifsiod_wq, &cancelled->work) == false); + + return 0; +} + +int +smb2_handle_cancelled_close(struct cifs_tcon *tcon, __u64 persistent_fid, + __u64 volatile_fid) +{ + int rc; + + cifs_dbg(FYI, "%s: tc_count=%d\n", __func__, tcon->tc_count); + spin_lock(&cifs_tcp_ses_lock); + if (tcon->tc_count <= 0) { + struct TCP_Server_Info *server = NULL; + + WARN_ONCE(tcon->tc_count < 0, "tcon refcount is negative"); + spin_unlock(&cifs_tcp_ses_lock); + + if (tcon->ses) + server = tcon->ses->server; + + cifs_server_dbg(FYI, "tid=0x%x: tcon is closing, skipping async close retry of fid %llu %llu\n", + tcon->tid, persistent_fid, volatile_fid); + + return 0; + } + tcon->tc_count++; + spin_unlock(&cifs_tcp_ses_lock); + + rc = __smb2_handle_cancelled_cmd(tcon, SMB2_CLOSE_HE, 0, + persistent_fid, volatile_fid); + if (rc) + cifs_put_tcon(tcon); + + return rc; +} + +int +smb2_handle_cancelled_mid(struct mid_q_entry *mid, struct TCP_Server_Info *server) +{ + struct smb2_hdr *hdr = mid->resp_buf; + struct smb2_create_rsp *rsp = mid->resp_buf; + struct cifs_tcon *tcon; + int rc; + + if ((mid->optype & CIFS_CP_CREATE_CLOSE_OP) || hdr->Command != SMB2_CREATE || + hdr->Status != STATUS_SUCCESS) + return 0; + + tcon = smb2_find_smb_tcon(server, le64_to_cpu(hdr->SessionId), + le32_to_cpu(hdr->Id.SyncId.TreeId)); + if (!tcon) + return -ENOENT; + + rc = __smb2_handle_cancelled_cmd(tcon, + le16_to_cpu(hdr->Command), + le64_to_cpu(hdr->MessageId), + rsp->PersistentFileId, + rsp->VolatileFileId); + if (rc) + cifs_put_tcon(tcon); + + return rc; +} + +/** + * smb311_update_preauth_hash - update @ses hash with the packet data in @iov + * + * Assumes @iov does not contain the rfc1002 length and iov[0] has the + * SMB2 header. + * + * @ses: server session structure + * @server: pointer to server info + * @iov: array containing the SMB request we will send to the server + * @nvec: number of array entries for the iov + */ +int +smb311_update_preauth_hash(struct cifs_ses *ses, struct TCP_Server_Info *server, + struct kvec *iov, int nvec) +{ + int i, rc; + struct smb2_hdr *hdr; + struct shash_desc *sha512 = NULL; + + hdr = (struct smb2_hdr *)iov[0].iov_base; + /* neg prot are always taken */ + if (hdr->Command == SMB2_NEGOTIATE) + goto ok; + + /* + * If we process a command which wasn't a negprot it means the + * neg prot was already done, so the server dialect was set + * and we can test it. Preauth requires 3.1.1 for now. + */ + if (server->dialect != SMB311_PROT_ID) + return 0; + + if (hdr->Command != SMB2_SESSION_SETUP) + return 0; + + /* skip last sess setup response */ + if ((hdr->Flags & SMB2_FLAGS_SERVER_TO_REDIR) + && (hdr->Status == NT_STATUS_OK + || (hdr->Status != + cpu_to_le32(NT_STATUS_MORE_PROCESSING_REQUIRED)))) + return 0; + +ok: + rc = smb311_crypto_shash_allocate(server); + if (rc) + return rc; + + sha512 = server->secmech.sha512; + rc = crypto_shash_init(sha512); + if (rc) { + cifs_dbg(VFS, "%s: Could not init sha512 shash\n", __func__); + return rc; + } + + rc = crypto_shash_update(sha512, ses->preauth_sha_hash, + SMB2_PREAUTH_HASH_SIZE); + if (rc) { + cifs_dbg(VFS, "%s: Could not update sha512 shash\n", __func__); + return rc; + } + + for (i = 0; i < nvec; i++) { + rc = crypto_shash_update(sha512, iov[i].iov_base, iov[i].iov_len); + if (rc) { + cifs_dbg(VFS, "%s: Could not update sha512 shash\n", + __func__); + return rc; + } + } + + rc = crypto_shash_final(sha512, ses->preauth_sha_hash); + if (rc) { + cifs_dbg(VFS, "%s: Could not finalize sha512 shash\n", + __func__); + return rc; + } + + return 0; +} diff --git a/fs/smb/client/smb2ops.c b/fs/smb/client/smb2ops.c new file mode 100644 index 0000000000000..d512440d35b6f --- /dev/null +++ b/fs/smb/client/smb2ops.c @@ -0,0 +1,5805 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * SMB2 version specific operations + * + * Copyright (c) 2012, Jeff Layton + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "cifsfs.h" +#include "cifsglob.h" +#include "smb2pdu.h" +#include "smb2proto.h" +#include "cifsproto.h" +#include "cifs_debug.h" +#include "cifs_unicode.h" +#include "smb2status.h" +#include "smb2glob.h" +#include "cifs_ioctl.h" +#include "smbdirect.h" +#include "fscache.h" +#include "fs_context.h" +#include "cached_dir.h" + +/* Change credits for different ops and return the total number of credits */ +static int +change_conf(struct TCP_Server_Info *server) +{ + server->credits += server->echo_credits + server->oplock_credits; + server->oplock_credits = server->echo_credits = 0; + switch (server->credits) { + case 0: + return 0; + case 1: + server->echoes = false; + server->oplocks = false; + break; + case 2: + server->echoes = true; + server->oplocks = false; + server->echo_credits = 1; + break; + default: + server->echoes = true; + if (enable_oplocks) { + server->oplocks = true; + server->oplock_credits = 1; + } else + server->oplocks = false; + + server->echo_credits = 1; + } + server->credits -= server->echo_credits + server->oplock_credits; + return server->credits + server->echo_credits + server->oplock_credits; +} + +static void +smb2_add_credits(struct TCP_Server_Info *server, + const struct cifs_credits *credits, const int optype) +{ + int *val, rc = -1; + int scredits, in_flight; + unsigned int add = credits->value; + unsigned int instance = credits->instance; + bool reconnect_detected = false; + bool reconnect_with_invalid_credits = false; + + spin_lock(&server->req_lock); + val = server->ops->get_credits_field(server, optype); + + /* eg found case where write overlapping reconnect messed up credits */ + if (((optype & CIFS_OP_MASK) == CIFS_NEG_OP) && (*val != 0)) + reconnect_with_invalid_credits = true; + + if ((instance == 0) || (instance == server->reconnect_instance)) + *val += add; + else + reconnect_detected = true; + + if (*val > 65000) { + *val = 65000; /* Don't get near 64K credits, avoid srv bugs */ + pr_warn_once("server overflowed SMB3 credits\n"); + trace_smb3_overflow_credits(server->CurrentMid, + server->conn_id, server->hostname, *val, + add, server->in_flight); + } + server->in_flight--; + if (server->in_flight == 0 && + ((optype & CIFS_OP_MASK) != CIFS_NEG_OP) && + ((optype & CIFS_OP_MASK) != CIFS_SESS_OP)) + rc = change_conf(server); + /* + * Sometimes server returns 0 credits on oplock break ack - we need to + * rebalance credits in this case. + */ + else if (server->in_flight > 0 && server->oplock_credits == 0 && + server->oplocks) { + if (server->credits > 1) { + server->credits--; + server->oplock_credits++; + } + } + scredits = *val; + in_flight = server->in_flight; + spin_unlock(&server->req_lock); + wake_up(&server->request_q); + + if (reconnect_detected) { + trace_smb3_reconnect_detected(server->CurrentMid, + server->conn_id, server->hostname, scredits, add, in_flight); + + cifs_dbg(FYI, "trying to put %d credits from the old server instance %d\n", + add, instance); + } + + if (reconnect_with_invalid_credits) { + trace_smb3_reconnect_with_invalid_credits(server->CurrentMid, + server->conn_id, server->hostname, scredits, add, in_flight); + cifs_dbg(FYI, "Negotiate operation when server credits is non-zero. Optype: %d, server credits: %d, credits added: %d\n", + optype, scredits, add); + } + + spin_lock(&server->srv_lock); + if (server->tcpStatus == CifsNeedReconnect + || server->tcpStatus == CifsExiting) { + spin_unlock(&server->srv_lock); + return; + } + spin_unlock(&server->srv_lock); + + switch (rc) { + case -1: + /* change_conf hasn't been executed */ + break; + case 0: + cifs_server_dbg(VFS, "Possible client or server bug - zero credits\n"); + break; + case 1: + cifs_server_dbg(VFS, "disabling echoes and oplocks\n"); + break; + case 2: + cifs_dbg(FYI, "disabling oplocks\n"); + break; + default: + /* change_conf rebalanced credits for different types */ + break; + } + + trace_smb3_add_credits(server->CurrentMid, + server->conn_id, server->hostname, scredits, add, in_flight); + cifs_dbg(FYI, "%s: added %u credits total=%d\n", __func__, add, scredits); +} + +static void +smb2_set_credits(struct TCP_Server_Info *server, const int val) +{ + int scredits, in_flight; + + spin_lock(&server->req_lock); + server->credits = val; + if (val == 1) + server->reconnect_instance++; + scredits = server->credits; + in_flight = server->in_flight; + spin_unlock(&server->req_lock); + + trace_smb3_set_credits(server->CurrentMid, + server->conn_id, server->hostname, scredits, val, in_flight); + cifs_dbg(FYI, "%s: set %u credits\n", __func__, val); + + /* don't log while holding the lock */ + if (val == 1) + cifs_dbg(FYI, "set credits to 1 due to smb2 reconnect\n"); +} + +static int * +smb2_get_credits_field(struct TCP_Server_Info *server, const int optype) +{ + switch (optype) { + case CIFS_ECHO_OP: + return &server->echo_credits; + case CIFS_OBREAK_OP: + return &server->oplock_credits; + default: + return &server->credits; + } +} + +static unsigned int +smb2_get_credits(struct mid_q_entry *mid) +{ + return mid->credits_received; +} + +static int +smb2_wait_mtu_credits(struct TCP_Server_Info *server, unsigned int size, + unsigned int *num, struct cifs_credits *credits) +{ + int rc = 0; + unsigned int scredits, in_flight; + + spin_lock(&server->req_lock); + while (1) { + if (server->credits <= 0) { + spin_unlock(&server->req_lock); + cifs_num_waiters_inc(server); + rc = wait_event_killable(server->request_q, + has_credits(server, &server->credits, 1)); + cifs_num_waiters_dec(server); + if (rc) + return rc; + spin_lock(&server->req_lock); + } else { + spin_unlock(&server->req_lock); + spin_lock(&server->srv_lock); + if (server->tcpStatus == CifsExiting) { + spin_unlock(&server->srv_lock); + return -ENOENT; + } + spin_unlock(&server->srv_lock); + + spin_lock(&server->req_lock); + scredits = server->credits; + /* can deadlock with reopen */ + if (scredits <= 8) { + *num = SMB2_MAX_BUFFER_SIZE; + credits->value = 0; + credits->instance = 0; + break; + } + + /* leave some credits for reopen and other ops */ + scredits -= 8; + *num = min_t(unsigned int, size, + scredits * SMB2_MAX_BUFFER_SIZE); + + credits->value = + DIV_ROUND_UP(*num, SMB2_MAX_BUFFER_SIZE); + credits->instance = server->reconnect_instance; + server->credits -= credits->value; + server->in_flight++; + if (server->in_flight > server->max_in_flight) + server->max_in_flight = server->in_flight; + break; + } + } + scredits = server->credits; + in_flight = server->in_flight; + spin_unlock(&server->req_lock); + + trace_smb3_wait_credits(server->CurrentMid, + server->conn_id, server->hostname, scredits, -(credits->value), in_flight); + cifs_dbg(FYI, "%s: removed %u credits total=%d\n", + __func__, credits->value, scredits); + + return rc; +} + +static int +smb2_adjust_credits(struct TCP_Server_Info *server, + struct cifs_credits *credits, + const unsigned int payload_size) +{ + int new_val = DIV_ROUND_UP(payload_size, SMB2_MAX_BUFFER_SIZE); + int scredits, in_flight; + + if (!credits->value || credits->value == new_val) + return 0; + + if (credits->value < new_val) { + trace_smb3_too_many_credits(server->CurrentMid, + server->conn_id, server->hostname, 0, credits->value - new_val, 0); + cifs_server_dbg(VFS, "request has less credits (%d) than required (%d)", + credits->value, new_val); + + return -ENOTSUPP; + } + + spin_lock(&server->req_lock); + + if (server->reconnect_instance != credits->instance) { + scredits = server->credits; + in_flight = server->in_flight; + spin_unlock(&server->req_lock); + + trace_smb3_reconnect_detected(server->CurrentMid, + server->conn_id, server->hostname, scredits, + credits->value - new_val, in_flight); + cifs_server_dbg(VFS, "trying to return %d credits to old session\n", + credits->value - new_val); + return -EAGAIN; + } + + server->credits += credits->value - new_val; + scredits = server->credits; + in_flight = server->in_flight; + spin_unlock(&server->req_lock); + wake_up(&server->request_q); + + trace_smb3_adj_credits(server->CurrentMid, + server->conn_id, server->hostname, scredits, + credits->value - new_val, in_flight); + cifs_dbg(FYI, "%s: adjust added %u credits total=%d\n", + __func__, credits->value - new_val, scredits); + + credits->value = new_val; + + return 0; +} + +static __u64 +smb2_get_next_mid(struct TCP_Server_Info *server) +{ + __u64 mid; + /* for SMB2 we need the current value */ + spin_lock(&server->mid_lock); + mid = server->CurrentMid++; + spin_unlock(&server->mid_lock); + return mid; +} + +static void +smb2_revert_current_mid(struct TCP_Server_Info *server, const unsigned int val) +{ + spin_lock(&server->mid_lock); + if (server->CurrentMid >= val) + server->CurrentMid -= val; + spin_unlock(&server->mid_lock); +} + +static struct mid_q_entry * +__smb2_find_mid(struct TCP_Server_Info *server, char *buf, bool dequeue) +{ + struct mid_q_entry *mid; + struct smb2_hdr *shdr = (struct smb2_hdr *)buf; + __u64 wire_mid = le64_to_cpu(shdr->MessageId); + + if (shdr->ProtocolId == SMB2_TRANSFORM_PROTO_NUM) { + cifs_server_dbg(VFS, "Encrypted frame parsing not supported yet\n"); + return NULL; + } + + spin_lock(&server->mid_lock); + list_for_each_entry(mid, &server->pending_mid_q, qhead) { + if ((mid->mid == wire_mid) && + (mid->mid_state == MID_REQUEST_SUBMITTED) && + (mid->command == shdr->Command)) { + kref_get(&mid->refcount); + if (dequeue) { + list_del_init(&mid->qhead); + mid->mid_flags |= MID_DELETED; + } + spin_unlock(&server->mid_lock); + return mid; + } + } + spin_unlock(&server->mid_lock); + return NULL; +} + +static struct mid_q_entry * +smb2_find_mid(struct TCP_Server_Info *server, char *buf) +{ + return __smb2_find_mid(server, buf, false); +} + +static struct mid_q_entry * +smb2_find_dequeue_mid(struct TCP_Server_Info *server, char *buf) +{ + return __smb2_find_mid(server, buf, true); +} + +static void +smb2_dump_detail(void *buf, struct TCP_Server_Info *server) +{ +#ifdef CONFIG_CIFS_DEBUG2 + struct smb2_hdr *shdr = (struct smb2_hdr *)buf; + + cifs_server_dbg(VFS, "Cmd: %d Err: 0x%x Flags: 0x%x Mid: %llu Pid: %d\n", + shdr->Command, shdr->Status, shdr->Flags, shdr->MessageId, + shdr->Id.SyncId.ProcessId); + cifs_server_dbg(VFS, "smb buf %p len %u\n", buf, + server->ops->calc_smb_size(buf)); +#endif +} + +static bool +smb2_need_neg(struct TCP_Server_Info *server) +{ + return server->max_read == 0; +} + +static int +smb2_negotiate(const unsigned int xid, + struct cifs_ses *ses, + struct TCP_Server_Info *server) +{ + int rc; + + spin_lock(&server->mid_lock); + server->CurrentMid = 0; + spin_unlock(&server->mid_lock); + rc = SMB2_negotiate(xid, ses, server); + /* BB we probably don't need to retry with modern servers */ + if (rc == -EAGAIN) + rc = -EHOSTDOWN; + return rc; +} + +static unsigned int +smb2_negotiate_wsize(struct cifs_tcon *tcon, struct smb3_fs_context *ctx) +{ + struct TCP_Server_Info *server = tcon->ses->server; + unsigned int wsize; + + /* start with specified wsize, or default */ + wsize = ctx->wsize ? ctx->wsize : CIFS_DEFAULT_IOSIZE; + wsize = min_t(unsigned int, wsize, server->max_write); + if (!(server->capabilities & SMB2_GLOBAL_CAP_LARGE_MTU)) + wsize = min_t(unsigned int, wsize, SMB2_MAX_BUFFER_SIZE); + + return wsize; +} + +static unsigned int +smb3_negotiate_wsize(struct cifs_tcon *tcon, struct smb3_fs_context *ctx) +{ + struct TCP_Server_Info *server = tcon->ses->server; + unsigned int wsize; + + /* start with specified wsize, or default */ + wsize = ctx->wsize ? ctx->wsize : SMB3_DEFAULT_IOSIZE; + wsize = min_t(unsigned int, wsize, server->max_write); +#ifdef CONFIG_CIFS_SMB_DIRECT + if (server->rdma) { + if (server->sign) + /* + * Account for SMB2 data transfer packet header and + * possible encryption header + */ + wsize = min_t(unsigned int, + wsize, + server->smbd_conn->max_fragmented_send_size - + SMB2_READWRITE_PDU_HEADER_SIZE - + sizeof(struct smb2_transform_hdr)); + else + wsize = min_t(unsigned int, + wsize, server->smbd_conn->max_readwrite_size); + } +#endif + if (!(server->capabilities & SMB2_GLOBAL_CAP_LARGE_MTU)) + wsize = min_t(unsigned int, wsize, SMB2_MAX_BUFFER_SIZE); + + return wsize; +} + +static unsigned int +smb2_negotiate_rsize(struct cifs_tcon *tcon, struct smb3_fs_context *ctx) +{ + struct TCP_Server_Info *server = tcon->ses->server; + unsigned int rsize; + + /* start with specified rsize, or default */ + rsize = ctx->rsize ? ctx->rsize : CIFS_DEFAULT_IOSIZE; + rsize = min_t(unsigned int, rsize, server->max_read); + + if (!(server->capabilities & SMB2_GLOBAL_CAP_LARGE_MTU)) + rsize = min_t(unsigned int, rsize, SMB2_MAX_BUFFER_SIZE); + + return rsize; +} + +static unsigned int +smb3_negotiate_rsize(struct cifs_tcon *tcon, struct smb3_fs_context *ctx) +{ + struct TCP_Server_Info *server = tcon->ses->server; + unsigned int rsize; + + /* start with specified rsize, or default */ + rsize = ctx->rsize ? ctx->rsize : SMB3_DEFAULT_IOSIZE; + rsize = min_t(unsigned int, rsize, server->max_read); +#ifdef CONFIG_CIFS_SMB_DIRECT + if (server->rdma) { + if (server->sign) + /* + * Account for SMB2 data transfer packet header and + * possible encryption header + */ + rsize = min_t(unsigned int, + rsize, + server->smbd_conn->max_fragmented_recv_size - + SMB2_READWRITE_PDU_HEADER_SIZE - + sizeof(struct smb2_transform_hdr)); + else + rsize = min_t(unsigned int, + rsize, server->smbd_conn->max_readwrite_size); + } +#endif + + if (!(server->capabilities & SMB2_GLOBAL_CAP_LARGE_MTU)) + rsize = min_t(unsigned int, rsize, SMB2_MAX_BUFFER_SIZE); + + return rsize; +} + +static int +parse_server_interfaces(struct network_interface_info_ioctl_rsp *buf, + size_t buf_len, struct cifs_ses *ses, bool in_mount) +{ + struct network_interface_info_ioctl_rsp *p; + struct sockaddr_in *addr4; + struct sockaddr_in6 *addr6; + struct iface_info_ipv4 *p4; + struct iface_info_ipv6 *p6; + struct cifs_server_iface *info = NULL, *iface = NULL, *niface = NULL; + struct cifs_server_iface tmp_iface; + ssize_t bytes_left; + size_t next = 0; + int nb_iface = 0; + int rc = 0, ret = 0; + + bytes_left = buf_len; + p = buf; + + spin_lock(&ses->iface_lock); + /* do not query too frequently, this time with lock held */ + if (ses->iface_last_update && + time_before(jiffies, ses->iface_last_update + + (SMB_INTERFACE_POLL_INTERVAL * HZ))) { + spin_unlock(&ses->iface_lock); + return 0; + } + + /* + * Go through iface_list and do kref_put to remove + * any unused ifaces. ifaces in use will be removed + * when the last user calls a kref_put on it + */ + list_for_each_entry_safe(iface, niface, &ses->iface_list, + iface_head) { + iface->is_active = 0; + kref_put(&iface->refcount, release_iface); + ses->iface_count--; + } + spin_unlock(&ses->iface_lock); + + /* + * Samba server e.g. can return an empty interface list in some cases, + * which would only be a problem if we were requesting multichannel + */ + if (bytes_left == 0) { + /* avoid spamming logs every 10 minutes, so log only in mount */ + if ((ses->chan_max > 1) && in_mount) + cifs_dbg(VFS, + "multichannel not available\n" + "Empty network interface list returned by server %s\n", + ses->server->hostname); + rc = -EINVAL; + goto out; + } + + while (bytes_left >= sizeof(*p)) { + memset(&tmp_iface, 0, sizeof(tmp_iface)); + tmp_iface.speed = le64_to_cpu(p->LinkSpeed); + tmp_iface.rdma_capable = le32_to_cpu(p->Capability & RDMA_CAPABLE) ? 1 : 0; + tmp_iface.rss_capable = le32_to_cpu(p->Capability & RSS_CAPABLE) ? 1 : 0; + + switch (p->Family) { + /* + * The kernel and wire socket structures have the same + * layout and use network byte order but make the + * conversion explicit in case either one changes. + */ + case INTERNETWORK: + addr4 = (struct sockaddr_in *)&tmp_iface.sockaddr; + p4 = (struct iface_info_ipv4 *)p->Buffer; + addr4->sin_family = AF_INET; + memcpy(&addr4->sin_addr, &p4->IPv4Address, 4); + + /* [MS-SMB2] 2.2.32.5.1.1 Clients MUST ignore these */ + addr4->sin_port = cpu_to_be16(CIFS_PORT); + + cifs_dbg(FYI, "%s: ipv4 %pI4\n", __func__, + &addr4->sin_addr); + break; + case INTERNETWORKV6: + addr6 = (struct sockaddr_in6 *)&tmp_iface.sockaddr; + p6 = (struct iface_info_ipv6 *)p->Buffer; + addr6->sin6_family = AF_INET6; + memcpy(&addr6->sin6_addr, &p6->IPv6Address, 16); + + /* [MS-SMB2] 2.2.32.5.1.2 Clients MUST ignore these */ + addr6->sin6_flowinfo = 0; + addr6->sin6_scope_id = 0; + addr6->sin6_port = cpu_to_be16(CIFS_PORT); + + cifs_dbg(FYI, "%s: ipv6 %pI6\n", __func__, + &addr6->sin6_addr); + break; + default: + cifs_dbg(VFS, + "%s: skipping unsupported socket family\n", + __func__); + goto next_iface; + } + + /* + * The iface_list is assumed to be sorted by speed. + * Check if the new interface exists in that list. + * NEVER change iface. it could be in use. + * Add a new one instead + */ + spin_lock(&ses->iface_lock); + iface = niface = NULL; + list_for_each_entry_safe(iface, niface, &ses->iface_list, + iface_head) { + ret = iface_cmp(iface, &tmp_iface); + if (!ret) { + /* just get a ref so that it doesn't get picked/freed */ + iface->is_active = 1; + kref_get(&iface->refcount); + ses->iface_count++; + spin_unlock(&ses->iface_lock); + goto next_iface; + } else if (ret < 0) { + /* all remaining ifaces are slower */ + kref_get(&iface->refcount); + break; + } + } + spin_unlock(&ses->iface_lock); + + /* no match. insert the entry in the list */ + info = kmalloc(sizeof(struct cifs_server_iface), + GFP_KERNEL); + if (!info) { + rc = -ENOMEM; + goto out; + } + memcpy(info, &tmp_iface, sizeof(tmp_iface)); + + /* add this new entry to the list */ + kref_init(&info->refcount); + info->is_active = 1; + + cifs_dbg(FYI, "%s: adding iface %zu\n", __func__, ses->iface_count); + cifs_dbg(FYI, "%s: speed %zu bps\n", __func__, info->speed); + cifs_dbg(FYI, "%s: capabilities 0x%08x\n", __func__, + le32_to_cpu(p->Capability)); + + spin_lock(&ses->iface_lock); + if (!list_entry_is_head(iface, &ses->iface_list, iface_head)) { + list_add_tail(&info->iface_head, &iface->iface_head); + kref_put(&iface->refcount, release_iface); + } else + list_add_tail(&info->iface_head, &ses->iface_list); + + ses->iface_count++; + spin_unlock(&ses->iface_lock); + ses->iface_last_update = jiffies; +next_iface: + nb_iface++; + next = le32_to_cpu(p->Next); + if (!next) { + bytes_left -= sizeof(*p); + break; + } + p = (struct network_interface_info_ioctl_rsp *)((u8 *)p+next); + bytes_left -= next; + } + + if (!nb_iface) { + cifs_dbg(VFS, "%s: malformed interface info\n", __func__); + rc = -EINVAL; + goto out; + } + + /* Azure rounds the buffer size up 8, to a 16 byte boundary */ + if ((bytes_left > 8) || p->Next) + cifs_dbg(VFS, "%s: incomplete interface info\n", __func__); + + + if (!ses->iface_count) { + rc = -EINVAL; + goto out; + } + +out: + return rc; +} + +int +SMB3_request_interfaces(const unsigned int xid, struct cifs_tcon *tcon, bool in_mount) +{ + int rc; + unsigned int ret_data_len = 0; + struct network_interface_info_ioctl_rsp *out_buf = NULL; + struct cifs_ses *ses = tcon->ses; + + /* do not query too frequently */ + if (ses->iface_last_update && + time_before(jiffies, ses->iface_last_update + + (SMB_INTERFACE_POLL_INTERVAL * HZ))) + return 0; + + rc = SMB2_ioctl(xid, tcon, NO_FILE_ID, NO_FILE_ID, + FSCTL_QUERY_NETWORK_INTERFACE_INFO, + NULL /* no data input */, 0 /* no data input */, + CIFSMaxBufSize, (char **)&out_buf, &ret_data_len); + if (rc == -EOPNOTSUPP) { + cifs_dbg(FYI, + "server does not support query network interfaces\n"); + ret_data_len = 0; + } else if (rc != 0) { + cifs_tcon_dbg(VFS, "error %d on ioctl to get interface list\n", rc); + goto out; + } + + rc = parse_server_interfaces(out_buf, ret_data_len, ses, in_mount); + if (rc) + goto out; + +out: + kfree(out_buf); + return rc; +} + +static void +smb3_qfs_tcon(const unsigned int xid, struct cifs_tcon *tcon, + struct cifs_sb_info *cifs_sb) +{ + int rc; + __le16 srch_path = 0; /* Null - open root of share */ + u8 oplock = SMB2_OPLOCK_LEVEL_NONE; + struct cifs_open_parms oparms; + struct cifs_fid fid; + struct cached_fid *cfid = NULL; + + oparms = (struct cifs_open_parms) { + .tcon = tcon, + .path = "", + .desired_access = FILE_READ_ATTRIBUTES, + .disposition = FILE_OPEN, + .create_options = cifs_create_options(cifs_sb, 0), + .fid = &fid, + }; + + rc = open_cached_dir(xid, tcon, "", cifs_sb, false, &cfid); + if (rc == 0) + memcpy(&fid, &cfid->fid, sizeof(struct cifs_fid)); + else + rc = SMB2_open(xid, &oparms, &srch_path, &oplock, NULL, NULL, + NULL, NULL); + if (rc) + return; + + SMB3_request_interfaces(xid, tcon, true /* called during mount */); + + SMB2_QFS_attr(xid, tcon, fid.persistent_fid, fid.volatile_fid, + FS_ATTRIBUTE_INFORMATION); + SMB2_QFS_attr(xid, tcon, fid.persistent_fid, fid.volatile_fid, + FS_DEVICE_INFORMATION); + SMB2_QFS_attr(xid, tcon, fid.persistent_fid, fid.volatile_fid, + FS_VOLUME_INFORMATION); + SMB2_QFS_attr(xid, tcon, fid.persistent_fid, fid.volatile_fid, + FS_SECTOR_SIZE_INFORMATION); /* SMB3 specific */ + if (cfid == NULL) + SMB2_close(xid, tcon, fid.persistent_fid, fid.volatile_fid); + else + close_cached_dir(cfid); +} + +static void +smb2_qfs_tcon(const unsigned int xid, struct cifs_tcon *tcon, + struct cifs_sb_info *cifs_sb) +{ + int rc; + __le16 srch_path = 0; /* Null - open root of share */ + u8 oplock = SMB2_OPLOCK_LEVEL_NONE; + struct cifs_open_parms oparms; + struct cifs_fid fid; + + oparms = (struct cifs_open_parms) { + .tcon = tcon, + .path = "", + .desired_access = FILE_READ_ATTRIBUTES, + .disposition = FILE_OPEN, + .create_options = cifs_create_options(cifs_sb, 0), + .fid = &fid, + }; + + rc = SMB2_open(xid, &oparms, &srch_path, &oplock, NULL, NULL, + NULL, NULL); + if (rc) + return; + + SMB2_QFS_attr(xid, tcon, fid.persistent_fid, fid.volatile_fid, + FS_ATTRIBUTE_INFORMATION); + SMB2_QFS_attr(xid, tcon, fid.persistent_fid, fid.volatile_fid, + FS_DEVICE_INFORMATION); + SMB2_close(xid, tcon, fid.persistent_fid, fid.volatile_fid); +} + +static int +smb2_is_path_accessible(const unsigned int xid, struct cifs_tcon *tcon, + struct cifs_sb_info *cifs_sb, const char *full_path) +{ + __le16 *utf16_path; + __u8 oplock = SMB2_OPLOCK_LEVEL_NONE; + int err_buftype = CIFS_NO_BUFFER; + struct cifs_open_parms oparms; + struct kvec err_iov = {}; + struct cifs_fid fid; + struct cached_fid *cfid; + bool islink; + int rc, rc2; + + rc = open_cached_dir(xid, tcon, full_path, cifs_sb, true, &cfid); + if (!rc) { + if (cfid->has_lease) { + close_cached_dir(cfid); + return 0; + } + close_cached_dir(cfid); + } + + utf16_path = cifs_convert_path_to_utf16(full_path, cifs_sb); + if (!utf16_path) + return -ENOMEM; + + oparms = (struct cifs_open_parms) { + .tcon = tcon, + .path = full_path, + .desired_access = FILE_READ_ATTRIBUTES, + .disposition = FILE_OPEN, + .create_options = cifs_create_options(cifs_sb, 0), + .fid = &fid, + }; + + rc = SMB2_open(xid, &oparms, utf16_path, &oplock, NULL, NULL, + &err_iov, &err_buftype); + if (rc) { + struct smb2_hdr *hdr = err_iov.iov_base; + + if (unlikely(!hdr || err_buftype == CIFS_NO_BUFFER)) + goto out; + + if (rc != -EREMOTE && hdr->Status == STATUS_OBJECT_NAME_INVALID) { + rc2 = cifs_inval_name_dfs_link_error(xid, tcon, cifs_sb, + full_path, &islink); + if (rc2) { + rc = rc2; + goto out; + } + if (islink) + rc = -EREMOTE; + } + if (rc == -EREMOTE && IS_ENABLED(CONFIG_CIFS_DFS_UPCALL) && cifs_sb && + (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_NO_DFS)) + rc = -EOPNOTSUPP; + goto out; + } + + rc = SMB2_close(xid, tcon, fid.persistent_fid, fid.volatile_fid); + +out: + free_rsp_buf(err_buftype, err_iov.iov_base); + kfree(utf16_path); + return rc; +} + +static int smb2_get_srv_inum(const unsigned int xid, struct cifs_tcon *tcon, + struct cifs_sb_info *cifs_sb, const char *full_path, + u64 *uniqueid, struct cifs_open_info_data *data) +{ + *uniqueid = le64_to_cpu(data->fi.IndexNumber); + return 0; +} + +static int smb2_query_file_info(const unsigned int xid, struct cifs_tcon *tcon, + struct cifsFileInfo *cfile, struct cifs_open_info_data *data) +{ + struct cifs_fid *fid = &cfile->fid; + + if (cfile->symlink_target) { + data->symlink_target = kstrdup(cfile->symlink_target, GFP_KERNEL); + if (!data->symlink_target) + return -ENOMEM; + } + return SMB2_query_info(xid, tcon, fid->persistent_fid, fid->volatile_fid, &data->fi); +} + +#ifdef CONFIG_CIFS_XATTR +static ssize_t +move_smb2_ea_to_cifs(char *dst, size_t dst_size, + struct smb2_file_full_ea_info *src, size_t src_size, + const unsigned char *ea_name) +{ + int rc = 0; + unsigned int ea_name_len = ea_name ? strlen(ea_name) : 0; + char *name, *value; + size_t buf_size = dst_size; + size_t name_len, value_len, user_name_len; + + while (src_size > 0) { + name_len = (size_t)src->ea_name_length; + value_len = (size_t)le16_to_cpu(src->ea_value_length); + + if (name_len == 0) + break; + + if (src_size < 8 + name_len + 1 + value_len) { + cifs_dbg(FYI, "EA entry goes beyond length of list\n"); + rc = -EIO; + goto out; + } + + name = &src->ea_data[0]; + value = &src->ea_data[src->ea_name_length + 1]; + + if (ea_name) { + if (ea_name_len == name_len && + memcmp(ea_name, name, name_len) == 0) { + rc = value_len; + if (dst_size == 0) + goto out; + if (dst_size < value_len) { + rc = -ERANGE; + goto out; + } + memcpy(dst, value, value_len); + goto out; + } + } else { + /* 'user.' plus a terminating null */ + user_name_len = 5 + 1 + name_len; + + if (buf_size == 0) { + /* skip copy - calc size only */ + rc += user_name_len; + } else if (dst_size >= user_name_len) { + dst_size -= user_name_len; + memcpy(dst, "user.", 5); + dst += 5; + memcpy(dst, src->ea_data, name_len); + dst += name_len; + *dst = 0; + ++dst; + rc += user_name_len; + } else { + /* stop before overrun buffer */ + rc = -ERANGE; + break; + } + } + + if (!src->next_entry_offset) + break; + + if (src_size < le32_to_cpu(src->next_entry_offset)) { + /* stop before overrun buffer */ + rc = -ERANGE; + break; + } + src_size -= le32_to_cpu(src->next_entry_offset); + src = (void *)((char *)src + + le32_to_cpu(src->next_entry_offset)); + } + + /* didn't find the named attribute */ + if (ea_name) + rc = -ENODATA; + +out: + return (ssize_t)rc; +} + +static ssize_t +smb2_query_eas(const unsigned int xid, struct cifs_tcon *tcon, + const unsigned char *path, const unsigned char *ea_name, + char *ea_data, size_t buf_size, + struct cifs_sb_info *cifs_sb) +{ + int rc; + struct kvec rsp_iov = {NULL, 0}; + int buftype = CIFS_NO_BUFFER; + struct smb2_query_info_rsp *rsp; + struct smb2_file_full_ea_info *info = NULL; + + rc = smb2_query_info_compound(xid, tcon, path, + FILE_READ_EA, + FILE_FULL_EA_INFORMATION, + SMB2_O_INFO_FILE, + CIFSMaxBufSize - + MAX_SMB2_CREATE_RESPONSE_SIZE - + MAX_SMB2_CLOSE_RESPONSE_SIZE, + &rsp_iov, &buftype, cifs_sb); + if (rc) { + /* + * If ea_name is NULL (listxattr) and there are no EAs, + * return 0 as it's not an error. Otherwise, the specified + * ea_name was not found. + */ + if (!ea_name && rc == -ENODATA) + rc = 0; + goto qeas_exit; + } + + rsp = (struct smb2_query_info_rsp *)rsp_iov.iov_base; + rc = smb2_validate_iov(le16_to_cpu(rsp->OutputBufferOffset), + le32_to_cpu(rsp->OutputBufferLength), + &rsp_iov, + sizeof(struct smb2_file_full_ea_info)); + if (rc) + goto qeas_exit; + + info = (struct smb2_file_full_ea_info *)( + le16_to_cpu(rsp->OutputBufferOffset) + (char *)rsp); + rc = move_smb2_ea_to_cifs(ea_data, buf_size, info, + le32_to_cpu(rsp->OutputBufferLength), ea_name); + + qeas_exit: + free_rsp_buf(buftype, rsp_iov.iov_base); + return rc; +} + + +static int +smb2_set_ea(const unsigned int xid, struct cifs_tcon *tcon, + const char *path, const char *ea_name, const void *ea_value, + const __u16 ea_value_len, const struct nls_table *nls_codepage, + struct cifs_sb_info *cifs_sb) +{ + struct cifs_ses *ses = tcon->ses; + struct TCP_Server_Info *server = cifs_pick_channel(ses); + __le16 *utf16_path = NULL; + int ea_name_len = strlen(ea_name); + int flags = CIFS_CP_CREATE_CLOSE_OP; + int len; + struct smb_rqst rqst[3]; + int resp_buftype[3]; + struct kvec rsp_iov[3]; + struct kvec open_iov[SMB2_CREATE_IOV_SIZE]; + struct cifs_open_parms oparms; + __u8 oplock = SMB2_OPLOCK_LEVEL_NONE; + struct cifs_fid fid; + struct kvec si_iov[SMB2_SET_INFO_IOV_SIZE]; + unsigned int size[1]; + void *data[1]; + struct smb2_file_full_ea_info *ea = NULL; + struct kvec close_iov[1]; + struct smb2_query_info_rsp *rsp; + int rc, used_len = 0; + + if (smb3_encryption_required(tcon)) + flags |= CIFS_TRANSFORM_REQ; + + if (ea_name_len > 255) + return -EINVAL; + + utf16_path = cifs_convert_path_to_utf16(path, cifs_sb); + if (!utf16_path) + return -ENOMEM; + + memset(rqst, 0, sizeof(rqst)); + resp_buftype[0] = resp_buftype[1] = resp_buftype[2] = CIFS_NO_BUFFER; + memset(rsp_iov, 0, sizeof(rsp_iov)); + + if (ses->server->ops->query_all_EAs) { + if (!ea_value) { + rc = ses->server->ops->query_all_EAs(xid, tcon, path, + ea_name, NULL, 0, + cifs_sb); + if (rc == -ENODATA) + goto sea_exit; + } else { + /* If we are adding a attribute we should first check + * if there will be enough space available to store + * the new EA. If not we should not add it since we + * would not be able to even read the EAs back. + */ + rc = smb2_query_info_compound(xid, tcon, path, + FILE_READ_EA, + FILE_FULL_EA_INFORMATION, + SMB2_O_INFO_FILE, + CIFSMaxBufSize - + MAX_SMB2_CREATE_RESPONSE_SIZE - + MAX_SMB2_CLOSE_RESPONSE_SIZE, + &rsp_iov[1], &resp_buftype[1], cifs_sb); + if (rc == 0) { + rsp = (struct smb2_query_info_rsp *)rsp_iov[1].iov_base; + used_len = le32_to_cpu(rsp->OutputBufferLength); + } + free_rsp_buf(resp_buftype[1], rsp_iov[1].iov_base); + resp_buftype[1] = CIFS_NO_BUFFER; + memset(&rsp_iov[1], 0, sizeof(rsp_iov[1])); + rc = 0; + + /* Use a fudge factor of 256 bytes in case we collide + * with a different set_EAs command. + */ + if(CIFSMaxBufSize - MAX_SMB2_CREATE_RESPONSE_SIZE - + MAX_SMB2_CLOSE_RESPONSE_SIZE - 256 < + used_len + ea_name_len + ea_value_len + 1) { + rc = -ENOSPC; + goto sea_exit; + } + } + } + + /* Open */ + memset(&open_iov, 0, sizeof(open_iov)); + rqst[0].rq_iov = open_iov; + rqst[0].rq_nvec = SMB2_CREATE_IOV_SIZE; + + oparms = (struct cifs_open_parms) { + .tcon = tcon, + .path = path, + .desired_access = FILE_WRITE_EA, + .disposition = FILE_OPEN, + .create_options = cifs_create_options(cifs_sb, 0), + .fid = &fid, + }; + + rc = SMB2_open_init(tcon, server, + &rqst[0], &oplock, &oparms, utf16_path); + if (rc) + goto sea_exit; + smb2_set_next_command(tcon, &rqst[0]); + + + /* Set Info */ + memset(&si_iov, 0, sizeof(si_iov)); + rqst[1].rq_iov = si_iov; + rqst[1].rq_nvec = 1; + + len = sizeof(*ea) + ea_name_len + ea_value_len + 1; + ea = kzalloc(len, GFP_KERNEL); + if (ea == NULL) { + rc = -ENOMEM; + goto sea_exit; + } + + ea->ea_name_length = ea_name_len; + ea->ea_value_length = cpu_to_le16(ea_value_len); + memcpy(ea->ea_data, ea_name, ea_name_len + 1); + memcpy(ea->ea_data + ea_name_len + 1, ea_value, ea_value_len); + + size[0] = len; + data[0] = ea; + + rc = SMB2_set_info_init(tcon, server, + &rqst[1], COMPOUND_FID, + COMPOUND_FID, current->tgid, + FILE_FULL_EA_INFORMATION, + SMB2_O_INFO_FILE, 0, data, size); + if (rc) + goto sea_exit; + smb2_set_next_command(tcon, &rqst[1]); + smb2_set_related(&rqst[1]); + + + /* Close */ + memset(&close_iov, 0, sizeof(close_iov)); + rqst[2].rq_iov = close_iov; + rqst[2].rq_nvec = 1; + rc = SMB2_close_init(tcon, server, + &rqst[2], COMPOUND_FID, COMPOUND_FID, false); + if (rc) + goto sea_exit; + smb2_set_related(&rqst[2]); + + rc = compound_send_recv(xid, ses, server, + flags, 3, rqst, + resp_buftype, rsp_iov); + /* no need to bump num_remote_opens because handle immediately closed */ + + sea_exit: + kfree(ea); + kfree(utf16_path); + SMB2_open_free(&rqst[0]); + SMB2_set_info_free(&rqst[1]); + SMB2_close_free(&rqst[2]); + free_rsp_buf(resp_buftype[0], rsp_iov[0].iov_base); + free_rsp_buf(resp_buftype[1], rsp_iov[1].iov_base); + free_rsp_buf(resp_buftype[2], rsp_iov[2].iov_base); + return rc; +} +#endif + +static bool +smb2_can_echo(struct TCP_Server_Info *server) +{ + return server->echoes; +} + +static void +smb2_clear_stats(struct cifs_tcon *tcon) +{ + int i; + + for (i = 0; i < NUMBER_OF_SMB2_COMMANDS; i++) { + atomic_set(&tcon->stats.smb2_stats.smb2_com_sent[i], 0); + atomic_set(&tcon->stats.smb2_stats.smb2_com_failed[i], 0); + } +} + +static void +smb2_dump_share_caps(struct seq_file *m, struct cifs_tcon *tcon) +{ + seq_puts(m, "\n\tShare Capabilities:"); + if (tcon->capabilities & SMB2_SHARE_CAP_DFS) + seq_puts(m, " DFS,"); + if (tcon->capabilities & SMB2_SHARE_CAP_CONTINUOUS_AVAILABILITY) + seq_puts(m, " CONTINUOUS AVAILABILITY,"); + if (tcon->capabilities & SMB2_SHARE_CAP_SCALEOUT) + seq_puts(m, " SCALEOUT,"); + if (tcon->capabilities & SMB2_SHARE_CAP_CLUSTER) + seq_puts(m, " CLUSTER,"); + if (tcon->capabilities & SMB2_SHARE_CAP_ASYMMETRIC) + seq_puts(m, " ASYMMETRIC,"); + if (tcon->capabilities == 0) + seq_puts(m, " None"); + if (tcon->ss_flags & SSINFO_FLAGS_ALIGNED_DEVICE) + seq_puts(m, " Aligned,"); + if (tcon->ss_flags & SSINFO_FLAGS_PARTITION_ALIGNED_ON_DEVICE) + seq_puts(m, " Partition Aligned,"); + if (tcon->ss_flags & SSINFO_FLAGS_NO_SEEK_PENALTY) + seq_puts(m, " SSD,"); + if (tcon->ss_flags & SSINFO_FLAGS_TRIM_ENABLED) + seq_puts(m, " TRIM-support,"); + + seq_printf(m, "\tShare Flags: 0x%x", tcon->share_flags); + seq_printf(m, "\n\ttid: 0x%x", tcon->tid); + if (tcon->perf_sector_size) + seq_printf(m, "\tOptimal sector size: 0x%x", + tcon->perf_sector_size); + seq_printf(m, "\tMaximal Access: 0x%x", tcon->maximal_access); +} + +static void +smb2_print_stats(struct seq_file *m, struct cifs_tcon *tcon) +{ + atomic_t *sent = tcon->stats.smb2_stats.smb2_com_sent; + atomic_t *failed = tcon->stats.smb2_stats.smb2_com_failed; + + /* + * Can't display SMB2_NEGOTIATE, SESSION_SETUP, LOGOFF, CANCEL and ECHO + * totals (requests sent) since those SMBs are per-session not per tcon + */ + seq_printf(m, "\nBytes read: %llu Bytes written: %llu", + (long long)(tcon->bytes_read), + (long long)(tcon->bytes_written)); + seq_printf(m, "\nOpen files: %d total (local), %d open on server", + atomic_read(&tcon->num_local_opens), + atomic_read(&tcon->num_remote_opens)); + seq_printf(m, "\nTreeConnects: %d total %d failed", + atomic_read(&sent[SMB2_TREE_CONNECT_HE]), + atomic_read(&failed[SMB2_TREE_CONNECT_HE])); + seq_printf(m, "\nTreeDisconnects: %d total %d failed", + atomic_read(&sent[SMB2_TREE_DISCONNECT_HE]), + atomic_read(&failed[SMB2_TREE_DISCONNECT_HE])); + seq_printf(m, "\nCreates: %d total %d failed", + atomic_read(&sent[SMB2_CREATE_HE]), + atomic_read(&failed[SMB2_CREATE_HE])); + seq_printf(m, "\nCloses: %d total %d failed", + atomic_read(&sent[SMB2_CLOSE_HE]), + atomic_read(&failed[SMB2_CLOSE_HE])); + seq_printf(m, "\nFlushes: %d total %d failed", + atomic_read(&sent[SMB2_FLUSH_HE]), + atomic_read(&failed[SMB2_FLUSH_HE])); + seq_printf(m, "\nReads: %d total %d failed", + atomic_read(&sent[SMB2_READ_HE]), + atomic_read(&failed[SMB2_READ_HE])); + seq_printf(m, "\nWrites: %d total %d failed", + atomic_read(&sent[SMB2_WRITE_HE]), + atomic_read(&failed[SMB2_WRITE_HE])); + seq_printf(m, "\nLocks: %d total %d failed", + atomic_read(&sent[SMB2_LOCK_HE]), + atomic_read(&failed[SMB2_LOCK_HE])); + seq_printf(m, "\nIOCTLs: %d total %d failed", + atomic_read(&sent[SMB2_IOCTL_HE]), + atomic_read(&failed[SMB2_IOCTL_HE])); + seq_printf(m, "\nQueryDirectories: %d total %d failed", + atomic_read(&sent[SMB2_QUERY_DIRECTORY_HE]), + atomic_read(&failed[SMB2_QUERY_DIRECTORY_HE])); + seq_printf(m, "\nChangeNotifies: %d total %d failed", + atomic_read(&sent[SMB2_CHANGE_NOTIFY_HE]), + atomic_read(&failed[SMB2_CHANGE_NOTIFY_HE])); + seq_printf(m, "\nQueryInfos: %d total %d failed", + atomic_read(&sent[SMB2_QUERY_INFO_HE]), + atomic_read(&failed[SMB2_QUERY_INFO_HE])); + seq_printf(m, "\nSetInfos: %d total %d failed", + atomic_read(&sent[SMB2_SET_INFO_HE]), + atomic_read(&failed[SMB2_SET_INFO_HE])); + seq_printf(m, "\nOplockBreaks: %d sent %d failed", + atomic_read(&sent[SMB2_OPLOCK_BREAK_HE]), + atomic_read(&failed[SMB2_OPLOCK_BREAK_HE])); +} + +static void +smb2_set_fid(struct cifsFileInfo *cfile, struct cifs_fid *fid, __u32 oplock) +{ + struct cifsInodeInfo *cinode = CIFS_I(d_inode(cfile->dentry)); + struct TCP_Server_Info *server = tlink_tcon(cfile->tlink)->ses->server; + + cfile->fid.persistent_fid = fid->persistent_fid; + cfile->fid.volatile_fid = fid->volatile_fid; + cfile->fid.access = fid->access; +#ifdef CONFIG_CIFS_DEBUG2 + cfile->fid.mid = fid->mid; +#endif /* CIFS_DEBUG2 */ + server->ops->set_oplock_level(cinode, oplock, fid->epoch, + &fid->purge_cache); + cinode->can_cache_brlcks = CIFS_CACHE_WRITE(cinode); + memcpy(cfile->fid.create_guid, fid->create_guid, 16); +} + +static void +smb2_close_file(const unsigned int xid, struct cifs_tcon *tcon, + struct cifs_fid *fid) +{ + SMB2_close(xid, tcon, fid->persistent_fid, fid->volatile_fid); +} + +static void +smb2_close_getattr(const unsigned int xid, struct cifs_tcon *tcon, + struct cifsFileInfo *cfile) +{ + struct smb2_file_network_open_info file_inf; + struct inode *inode; + int rc; + + rc = __SMB2_close(xid, tcon, cfile->fid.persistent_fid, + cfile->fid.volatile_fid, &file_inf); + if (rc) + return; + + inode = d_inode(cfile->dentry); + + spin_lock(&inode->i_lock); + CIFS_I(inode)->time = jiffies; + + /* Creation time should not need to be updated on close */ + if (file_inf.LastWriteTime) + inode->i_mtime = cifs_NTtimeToUnix(file_inf.LastWriteTime); + if (file_inf.ChangeTime) + inode->i_ctime = cifs_NTtimeToUnix(file_inf.ChangeTime); + if (file_inf.LastAccessTime) + inode->i_atime = cifs_NTtimeToUnix(file_inf.LastAccessTime); + + /* + * i_blocks is not related to (i_size / i_blksize), + * but instead 512 byte (2**9) size is required for + * calculating num blocks. + */ + if (le64_to_cpu(file_inf.AllocationSize) > 4096) + inode->i_blocks = + (512 - 1 + le64_to_cpu(file_inf.AllocationSize)) >> 9; + + /* End of file and Attributes should not have to be updated on close */ + spin_unlock(&inode->i_lock); +} + +static int +SMB2_request_res_key(const unsigned int xid, struct cifs_tcon *tcon, + u64 persistent_fid, u64 volatile_fid, + struct copychunk_ioctl *pcchunk) +{ + int rc; + unsigned int ret_data_len; + struct resume_key_req *res_key; + + rc = SMB2_ioctl(xid, tcon, persistent_fid, volatile_fid, + FSCTL_SRV_REQUEST_RESUME_KEY, NULL, 0 /* no input */, + CIFSMaxBufSize, (char **)&res_key, &ret_data_len); + + if (rc == -EOPNOTSUPP) { + pr_warn_once("Server share %s does not support copy range\n", tcon->tree_name); + goto req_res_key_exit; + } else if (rc) { + cifs_tcon_dbg(VFS, "refcpy ioctl error %d getting resume key\n", rc); + goto req_res_key_exit; + } + if (ret_data_len < sizeof(struct resume_key_req)) { + cifs_tcon_dbg(VFS, "Invalid refcopy resume key length\n"); + rc = -EINVAL; + goto req_res_key_exit; + } + memcpy(pcchunk->SourceKey, res_key->ResumeKey, COPY_CHUNK_RES_KEY_SIZE); + +req_res_key_exit: + kfree(res_key); + return rc; +} + +struct iqi_vars { + struct smb_rqst rqst[3]; + struct kvec rsp_iov[3]; + struct kvec open_iov[SMB2_CREATE_IOV_SIZE]; + struct kvec qi_iov[1]; + struct kvec io_iov[SMB2_IOCTL_IOV_SIZE]; + struct kvec si_iov[SMB2_SET_INFO_IOV_SIZE]; + struct kvec close_iov[1]; +}; + +static int +smb2_ioctl_query_info(const unsigned int xid, + struct cifs_tcon *tcon, + struct cifs_sb_info *cifs_sb, + __le16 *path, int is_dir, + unsigned long p) +{ + struct iqi_vars *vars; + struct smb_rqst *rqst; + struct kvec *rsp_iov; + struct cifs_ses *ses = tcon->ses; + struct TCP_Server_Info *server = cifs_pick_channel(ses); + char __user *arg = (char __user *)p; + struct smb_query_info qi; + struct smb_query_info __user *pqi; + int rc = 0; + int flags = CIFS_CP_CREATE_CLOSE_OP; + struct smb2_query_info_rsp *qi_rsp = NULL; + struct smb2_ioctl_rsp *io_rsp = NULL; + void *buffer = NULL; + int resp_buftype[3]; + struct cifs_open_parms oparms; + u8 oplock = SMB2_OPLOCK_LEVEL_NONE; + struct cifs_fid fid; + unsigned int size[2]; + void *data[2]; + int create_options = is_dir ? CREATE_NOT_FILE : CREATE_NOT_DIR; + void (*free_req1_func)(struct smb_rqst *r); + + vars = kzalloc(sizeof(*vars), GFP_ATOMIC); + if (vars == NULL) + return -ENOMEM; + rqst = &vars->rqst[0]; + rsp_iov = &vars->rsp_iov[0]; + + resp_buftype[0] = resp_buftype[1] = resp_buftype[2] = CIFS_NO_BUFFER; + + if (copy_from_user(&qi, arg, sizeof(struct smb_query_info))) { + rc = -EFAULT; + goto free_vars; + } + if (qi.output_buffer_length > 1024) { + rc = -EINVAL; + goto free_vars; + } + + if (!ses || !server) { + rc = -EIO; + goto free_vars; + } + + if (smb3_encryption_required(tcon)) + flags |= CIFS_TRANSFORM_REQ; + + if (qi.output_buffer_length) { + buffer = memdup_user(arg + sizeof(struct smb_query_info), qi.output_buffer_length); + if (IS_ERR(buffer)) { + rc = PTR_ERR(buffer); + goto free_vars; + } + } + + /* Open */ + rqst[0].rq_iov = &vars->open_iov[0]; + rqst[0].rq_nvec = SMB2_CREATE_IOV_SIZE; + + oparms = (struct cifs_open_parms) { + .tcon = tcon, + .disposition = FILE_OPEN, + .create_options = cifs_create_options(cifs_sb, create_options), + .fid = &fid, + }; + + if (qi.flags & PASSTHRU_FSCTL) { + switch (qi.info_type & FSCTL_DEVICE_ACCESS_MASK) { + case FSCTL_DEVICE_ACCESS_FILE_READ_WRITE_ACCESS: + oparms.desired_access = FILE_READ_DATA | FILE_WRITE_DATA | FILE_READ_ATTRIBUTES | SYNCHRONIZE; + break; + case FSCTL_DEVICE_ACCESS_FILE_ANY_ACCESS: + oparms.desired_access = GENERIC_ALL; + break; + case FSCTL_DEVICE_ACCESS_FILE_READ_ACCESS: + oparms.desired_access = GENERIC_READ; + break; + case FSCTL_DEVICE_ACCESS_FILE_WRITE_ACCESS: + oparms.desired_access = GENERIC_WRITE; + break; + } + } else if (qi.flags & PASSTHRU_SET_INFO) { + oparms.desired_access = GENERIC_WRITE; + } else { + oparms.desired_access = FILE_READ_ATTRIBUTES | READ_CONTROL; + } + + rc = SMB2_open_init(tcon, server, + &rqst[0], &oplock, &oparms, path); + if (rc) + goto free_output_buffer; + smb2_set_next_command(tcon, &rqst[0]); + + /* Query */ + if (qi.flags & PASSTHRU_FSCTL) { + /* Can eventually relax perm check since server enforces too */ + if (!capable(CAP_SYS_ADMIN)) { + rc = -EPERM; + goto free_open_req; + } + rqst[1].rq_iov = &vars->io_iov[0]; + rqst[1].rq_nvec = SMB2_IOCTL_IOV_SIZE; + + rc = SMB2_ioctl_init(tcon, server, &rqst[1], COMPOUND_FID, COMPOUND_FID, + qi.info_type, buffer, qi.output_buffer_length, + CIFSMaxBufSize - MAX_SMB2_CREATE_RESPONSE_SIZE - + MAX_SMB2_CLOSE_RESPONSE_SIZE); + free_req1_func = SMB2_ioctl_free; + } else if (qi.flags == PASSTHRU_SET_INFO) { + /* Can eventually relax perm check since server enforces too */ + if (!capable(CAP_SYS_ADMIN)) { + rc = -EPERM; + goto free_open_req; + } + if (qi.output_buffer_length < 8) { + rc = -EINVAL; + goto free_open_req; + } + rqst[1].rq_iov = &vars->si_iov[0]; + rqst[1].rq_nvec = 1; + + /* MS-FSCC 2.4.13 FileEndOfFileInformation */ + size[0] = 8; + data[0] = buffer; + + rc = SMB2_set_info_init(tcon, server, &rqst[1], COMPOUND_FID, COMPOUND_FID, + current->tgid, FILE_END_OF_FILE_INFORMATION, + SMB2_O_INFO_FILE, 0, data, size); + free_req1_func = SMB2_set_info_free; + } else if (qi.flags == PASSTHRU_QUERY_INFO) { + rqst[1].rq_iov = &vars->qi_iov[0]; + rqst[1].rq_nvec = 1; + + rc = SMB2_query_info_init(tcon, server, + &rqst[1], COMPOUND_FID, + COMPOUND_FID, qi.file_info_class, + qi.info_type, qi.additional_information, + qi.input_buffer_length, + qi.output_buffer_length, buffer); + free_req1_func = SMB2_query_info_free; + } else { /* unknown flags */ + cifs_tcon_dbg(VFS, "Invalid passthru query flags: 0x%x\n", + qi.flags); + rc = -EINVAL; + } + + if (rc) + goto free_open_req; + smb2_set_next_command(tcon, &rqst[1]); + smb2_set_related(&rqst[1]); + + /* Close */ + rqst[2].rq_iov = &vars->close_iov[0]; + rqst[2].rq_nvec = 1; + + rc = SMB2_close_init(tcon, server, + &rqst[2], COMPOUND_FID, COMPOUND_FID, false); + if (rc) + goto free_req_1; + smb2_set_related(&rqst[2]); + + rc = compound_send_recv(xid, ses, server, + flags, 3, rqst, + resp_buftype, rsp_iov); + if (rc) + goto out; + + /* No need to bump num_remote_opens since handle immediately closed */ + if (qi.flags & PASSTHRU_FSCTL) { + pqi = (struct smb_query_info __user *)arg; + io_rsp = (struct smb2_ioctl_rsp *)rsp_iov[1].iov_base; + if (le32_to_cpu(io_rsp->OutputCount) < qi.input_buffer_length) + qi.input_buffer_length = le32_to_cpu(io_rsp->OutputCount); + if (qi.input_buffer_length > 0 && + le32_to_cpu(io_rsp->OutputOffset) + qi.input_buffer_length + > rsp_iov[1].iov_len) { + rc = -EFAULT; + goto out; + } + + if (copy_to_user(&pqi->input_buffer_length, + &qi.input_buffer_length, + sizeof(qi.input_buffer_length))) { + rc = -EFAULT; + goto out; + } + + if (copy_to_user((void __user *)pqi + sizeof(struct smb_query_info), + (const void *)io_rsp + le32_to_cpu(io_rsp->OutputOffset), + qi.input_buffer_length)) + rc = -EFAULT; + } else { + pqi = (struct smb_query_info __user *)arg; + qi_rsp = (struct smb2_query_info_rsp *)rsp_iov[1].iov_base; + if (le32_to_cpu(qi_rsp->OutputBufferLength) < qi.input_buffer_length) + qi.input_buffer_length = le32_to_cpu(qi_rsp->OutputBufferLength); + if (copy_to_user(&pqi->input_buffer_length, + &qi.input_buffer_length, + sizeof(qi.input_buffer_length))) { + rc = -EFAULT; + goto out; + } + + if (copy_to_user(pqi + 1, qi_rsp->Buffer, + qi.input_buffer_length)) + rc = -EFAULT; + } + +out: + free_rsp_buf(resp_buftype[0], rsp_iov[0].iov_base); + free_rsp_buf(resp_buftype[1], rsp_iov[1].iov_base); + free_rsp_buf(resp_buftype[2], rsp_iov[2].iov_base); + SMB2_close_free(&rqst[2]); +free_req_1: + free_req1_func(&rqst[1]); +free_open_req: + SMB2_open_free(&rqst[0]); +free_output_buffer: + kfree(buffer); +free_vars: + kfree(vars); + return rc; +} + +static ssize_t +smb2_copychunk_range(const unsigned int xid, + struct cifsFileInfo *srcfile, + struct cifsFileInfo *trgtfile, u64 src_off, + u64 len, u64 dest_off) +{ + int rc; + unsigned int ret_data_len; + struct copychunk_ioctl *pcchunk; + struct copychunk_ioctl_rsp *retbuf = NULL; + struct cifs_tcon *tcon; + int chunks_copied = 0; + bool chunk_sizes_updated = false; + ssize_t bytes_written, total_bytes_written = 0; + + pcchunk = kmalloc(sizeof(struct copychunk_ioctl), GFP_KERNEL); + if (pcchunk == NULL) + return -ENOMEM; + + cifs_dbg(FYI, "%s: about to call request res key\n", __func__); + /* Request a key from the server to identify the source of the copy */ + rc = SMB2_request_res_key(xid, tlink_tcon(srcfile->tlink), + srcfile->fid.persistent_fid, + srcfile->fid.volatile_fid, pcchunk); + + /* Note: request_res_key sets res_key null only if rc !=0 */ + if (rc) + goto cchunk_out; + + /* For now array only one chunk long, will make more flexible later */ + pcchunk->ChunkCount = cpu_to_le32(1); + pcchunk->Reserved = 0; + pcchunk->Reserved2 = 0; + + tcon = tlink_tcon(trgtfile->tlink); + + while (len > 0) { + pcchunk->SourceOffset = cpu_to_le64(src_off); + pcchunk->TargetOffset = cpu_to_le64(dest_off); + pcchunk->Length = + cpu_to_le32(min_t(u64, len, tcon->max_bytes_chunk)); + + /* Request server copy to target from src identified by key */ + kfree(retbuf); + retbuf = NULL; + rc = SMB2_ioctl(xid, tcon, trgtfile->fid.persistent_fid, + trgtfile->fid.volatile_fid, FSCTL_SRV_COPYCHUNK_WRITE, + (char *)pcchunk, sizeof(struct copychunk_ioctl), + CIFSMaxBufSize, (char **)&retbuf, &ret_data_len); + if (rc == 0) { + if (ret_data_len != + sizeof(struct copychunk_ioctl_rsp)) { + cifs_tcon_dbg(VFS, "Invalid cchunk response size\n"); + rc = -EIO; + goto cchunk_out; + } + if (retbuf->TotalBytesWritten == 0) { + cifs_dbg(FYI, "no bytes copied\n"); + rc = -EIO; + goto cchunk_out; + } + /* + * Check if server claimed to write more than we asked + */ + if (le32_to_cpu(retbuf->TotalBytesWritten) > + le32_to_cpu(pcchunk->Length)) { + cifs_tcon_dbg(VFS, "Invalid copy chunk response\n"); + rc = -EIO; + goto cchunk_out; + } + if (le32_to_cpu(retbuf->ChunksWritten) != 1) { + cifs_tcon_dbg(VFS, "Invalid num chunks written\n"); + rc = -EIO; + goto cchunk_out; + } + chunks_copied++; + + bytes_written = le32_to_cpu(retbuf->TotalBytesWritten); + src_off += bytes_written; + dest_off += bytes_written; + len -= bytes_written; + total_bytes_written += bytes_written; + + cifs_dbg(FYI, "Chunks %d PartialChunk %d Total %zu\n", + le32_to_cpu(retbuf->ChunksWritten), + le32_to_cpu(retbuf->ChunkBytesWritten), + bytes_written); + } else if (rc == -EINVAL) { + if (ret_data_len != sizeof(struct copychunk_ioctl_rsp)) + goto cchunk_out; + + cifs_dbg(FYI, "MaxChunks %d BytesChunk %d MaxCopy %d\n", + le32_to_cpu(retbuf->ChunksWritten), + le32_to_cpu(retbuf->ChunkBytesWritten), + le32_to_cpu(retbuf->TotalBytesWritten)); + + /* + * Check if this is the first request using these sizes, + * (ie check if copy succeed once with original sizes + * and check if the server gave us different sizes after + * we already updated max sizes on previous request). + * if not then why is the server returning an error now + */ + if ((chunks_copied != 0) || chunk_sizes_updated) + goto cchunk_out; + + /* Check that server is not asking us to grow size */ + if (le32_to_cpu(retbuf->ChunkBytesWritten) < + tcon->max_bytes_chunk) + tcon->max_bytes_chunk = + le32_to_cpu(retbuf->ChunkBytesWritten); + else + goto cchunk_out; /* server gave us bogus size */ + + /* No need to change MaxChunks since already set to 1 */ + chunk_sizes_updated = true; + } else + goto cchunk_out; + } + +cchunk_out: + kfree(pcchunk); + kfree(retbuf); + if (rc) + return rc; + else + return total_bytes_written; +} + +static int +smb2_flush_file(const unsigned int xid, struct cifs_tcon *tcon, + struct cifs_fid *fid) +{ + return SMB2_flush(xid, tcon, fid->persistent_fid, fid->volatile_fid); +} + +static unsigned int +smb2_read_data_offset(char *buf) +{ + struct smb2_read_rsp *rsp = (struct smb2_read_rsp *)buf; + + return rsp->DataOffset; +} + +static unsigned int +smb2_read_data_length(char *buf, bool in_remaining) +{ + struct smb2_read_rsp *rsp = (struct smb2_read_rsp *)buf; + + if (in_remaining) + return le32_to_cpu(rsp->DataRemaining); + + return le32_to_cpu(rsp->DataLength); +} + + +static int +smb2_sync_read(const unsigned int xid, struct cifs_fid *pfid, + struct cifs_io_parms *parms, unsigned int *bytes_read, + char **buf, int *buf_type) +{ + parms->persistent_fid = pfid->persistent_fid; + parms->volatile_fid = pfid->volatile_fid; + return SMB2_read(xid, parms, bytes_read, buf, buf_type); +} + +static int +smb2_sync_write(const unsigned int xid, struct cifs_fid *pfid, + struct cifs_io_parms *parms, unsigned int *written, + struct kvec *iov, unsigned long nr_segs) +{ + + parms->persistent_fid = pfid->persistent_fid; + parms->volatile_fid = pfid->volatile_fid; + return SMB2_write(xid, parms, written, iov, nr_segs); +} + +/* Set or clear the SPARSE_FILE attribute based on value passed in setsparse */ +static bool smb2_set_sparse(const unsigned int xid, struct cifs_tcon *tcon, + struct cifsFileInfo *cfile, struct inode *inode, __u8 setsparse) +{ + struct cifsInodeInfo *cifsi; + int rc; + + cifsi = CIFS_I(inode); + + /* if file already sparse don't bother setting sparse again */ + if ((cifsi->cifsAttrs & FILE_ATTRIBUTE_SPARSE_FILE) && setsparse) + return true; /* already sparse */ + + if (!(cifsi->cifsAttrs & FILE_ATTRIBUTE_SPARSE_FILE) && !setsparse) + return true; /* already not sparse */ + + /* + * Can't check for sparse support on share the usual way via the + * FS attribute info (FILE_SUPPORTS_SPARSE_FILES) on the share + * since Samba server doesn't set the flag on the share, yet + * supports the set sparse FSCTL and returns sparse correctly + * in the file attributes. If we fail setting sparse though we + * mark that server does not support sparse files for this share + * to avoid repeatedly sending the unsupported fsctl to server + * if the file is repeatedly extended. + */ + if (tcon->broken_sparse_sup) + return false; + + rc = SMB2_ioctl(xid, tcon, cfile->fid.persistent_fid, + cfile->fid.volatile_fid, FSCTL_SET_SPARSE, + &setsparse, 1, CIFSMaxBufSize, NULL, NULL); + if (rc) { + tcon->broken_sparse_sup = true; + cifs_dbg(FYI, "set sparse rc = %d\n", rc); + return false; + } + + if (setsparse) + cifsi->cifsAttrs |= FILE_ATTRIBUTE_SPARSE_FILE; + else + cifsi->cifsAttrs &= (~FILE_ATTRIBUTE_SPARSE_FILE); + + return true; +} + +static int +smb2_set_file_size(const unsigned int xid, struct cifs_tcon *tcon, + struct cifsFileInfo *cfile, __u64 size, bool set_alloc) +{ + __le64 eof = cpu_to_le64(size); + struct inode *inode; + + /* + * If extending file more than one page make sparse. Many Linux fs + * make files sparse by default when extending via ftruncate + */ + inode = d_inode(cfile->dentry); + + if (!set_alloc && (size > inode->i_size + 8192)) { + __u8 set_sparse = 1; + + /* whether set sparse succeeds or not, extend the file */ + smb2_set_sparse(xid, tcon, cfile, inode, set_sparse); + } + + return SMB2_set_eof(xid, tcon, cfile->fid.persistent_fid, + cfile->fid.volatile_fid, cfile->pid, &eof); +} + +static int +smb2_duplicate_extents(const unsigned int xid, + struct cifsFileInfo *srcfile, + struct cifsFileInfo *trgtfile, u64 src_off, + u64 len, u64 dest_off) +{ + int rc; + unsigned int ret_data_len; + struct inode *inode; + struct duplicate_extents_to_file dup_ext_buf; + struct cifs_tcon *tcon = tlink_tcon(trgtfile->tlink); + + /* server fileays advertise duplicate extent support with this flag */ + if ((le32_to_cpu(tcon->fsAttrInfo.Attributes) & + FILE_SUPPORTS_BLOCK_REFCOUNTING) == 0) + return -EOPNOTSUPP; + + dup_ext_buf.VolatileFileHandle = srcfile->fid.volatile_fid; + dup_ext_buf.PersistentFileHandle = srcfile->fid.persistent_fid; + dup_ext_buf.SourceFileOffset = cpu_to_le64(src_off); + dup_ext_buf.TargetFileOffset = cpu_to_le64(dest_off); + dup_ext_buf.ByteCount = cpu_to_le64(len); + cifs_dbg(FYI, "Duplicate extents: src off %lld dst off %lld len %lld\n", + src_off, dest_off, len); + + inode = d_inode(trgtfile->dentry); + if (inode->i_size < dest_off + len) { + rc = smb2_set_file_size(xid, tcon, trgtfile, dest_off + len, false); + if (rc) + goto duplicate_extents_out; + + /* + * Although also could set plausible allocation size (i_blocks) + * here in addition to setting the file size, in reflink + * it is likely that the target file is sparse. Its allocation + * size will be queried on next revalidate, but it is important + * to make sure that file's cached size is updated immediately + */ + cifs_setsize(inode, dest_off + len); + } + rc = SMB2_ioctl(xid, tcon, trgtfile->fid.persistent_fid, + trgtfile->fid.volatile_fid, + FSCTL_DUPLICATE_EXTENTS_TO_FILE, + (char *)&dup_ext_buf, + sizeof(struct duplicate_extents_to_file), + CIFSMaxBufSize, NULL, + &ret_data_len); + + if (ret_data_len > 0) + cifs_dbg(FYI, "Non-zero response length in duplicate extents\n"); + +duplicate_extents_out: + return rc; +} + +static int +smb2_set_compression(const unsigned int xid, struct cifs_tcon *tcon, + struct cifsFileInfo *cfile) +{ + return SMB2_set_compression(xid, tcon, cfile->fid.persistent_fid, + cfile->fid.volatile_fid); +} + +static int +smb3_set_integrity(const unsigned int xid, struct cifs_tcon *tcon, + struct cifsFileInfo *cfile) +{ + struct fsctl_set_integrity_information_req integr_info; + unsigned int ret_data_len; + + integr_info.ChecksumAlgorithm = cpu_to_le16(CHECKSUM_TYPE_UNCHANGED); + integr_info.Flags = 0; + integr_info.Reserved = 0; + + return SMB2_ioctl(xid, tcon, cfile->fid.persistent_fid, + cfile->fid.volatile_fid, + FSCTL_SET_INTEGRITY_INFORMATION, + (char *)&integr_info, + sizeof(struct fsctl_set_integrity_information_req), + CIFSMaxBufSize, NULL, + &ret_data_len); + +} + +/* GMT Token is @GMT-YYYY.MM.DD-HH.MM.SS Unicode which is 48 bytes + null */ +#define GMT_TOKEN_SIZE 50 + +#define MIN_SNAPSHOT_ARRAY_SIZE 16 /* See MS-SMB2 section 3.3.5.15.1 */ + +/* + * Input buffer contains (empty) struct smb_snapshot array with size filled in + * For output see struct SRV_SNAPSHOT_ARRAY in MS-SMB2 section 2.2.32.2 + */ +static int +smb3_enum_snapshots(const unsigned int xid, struct cifs_tcon *tcon, + struct cifsFileInfo *cfile, void __user *ioc_buf) +{ + char *retbuf = NULL; + unsigned int ret_data_len = 0; + int rc; + u32 max_response_size; + struct smb_snapshot_array snapshot_in; + + /* + * On the first query to enumerate the list of snapshots available + * for this volume the buffer begins with 0 (number of snapshots + * which can be returned is zero since at that point we do not know + * how big the buffer needs to be). On the second query, + * it (ret_data_len) is set to number of snapshots so we can + * know to set the maximum response size larger (see below). + */ + if (get_user(ret_data_len, (unsigned int __user *)ioc_buf)) + return -EFAULT; + + /* + * Note that for snapshot queries that servers like Azure expect that + * the first query be minimal size (and just used to get the number/size + * of previous versions) so response size must be specified as EXACTLY + * sizeof(struct snapshot_array) which is 16 when rounded up to multiple + * of eight bytes. + */ + if (ret_data_len == 0) + max_response_size = MIN_SNAPSHOT_ARRAY_SIZE; + else + max_response_size = CIFSMaxBufSize; + + rc = SMB2_ioctl(xid, tcon, cfile->fid.persistent_fid, + cfile->fid.volatile_fid, + FSCTL_SRV_ENUMERATE_SNAPSHOTS, + NULL, 0 /* no input data */, max_response_size, + (char **)&retbuf, + &ret_data_len); + cifs_dbg(FYI, "enum snaphots ioctl returned %d and ret buflen is %d\n", + rc, ret_data_len); + if (rc) + return rc; + + if (ret_data_len && (ioc_buf != NULL) && (retbuf != NULL)) { + /* Fixup buffer */ + if (copy_from_user(&snapshot_in, ioc_buf, + sizeof(struct smb_snapshot_array))) { + rc = -EFAULT; + kfree(retbuf); + return rc; + } + + /* + * Check for min size, ie not large enough to fit even one GMT + * token (snapshot). On the first ioctl some users may pass in + * smaller size (or zero) to simply get the size of the array + * so the user space caller can allocate sufficient memory + * and retry the ioctl again with larger array size sufficient + * to hold all of the snapshot GMT tokens on the second try. + */ + if (snapshot_in.snapshot_array_size < GMT_TOKEN_SIZE) + ret_data_len = sizeof(struct smb_snapshot_array); + + /* + * We return struct SRV_SNAPSHOT_ARRAY, followed by + * the snapshot array (of 50 byte GMT tokens) each + * representing an available previous version of the data + */ + if (ret_data_len > (snapshot_in.snapshot_array_size + + sizeof(struct smb_snapshot_array))) + ret_data_len = snapshot_in.snapshot_array_size + + sizeof(struct smb_snapshot_array); + + if (copy_to_user(ioc_buf, retbuf, ret_data_len)) + rc = -EFAULT; + } + + kfree(retbuf); + return rc; +} + + + +static int +smb3_notify(const unsigned int xid, struct file *pfile, + void __user *ioc_buf, bool return_changes) +{ + struct smb3_notify_info notify; + struct smb3_notify_info __user *pnotify_buf; + struct dentry *dentry = pfile->f_path.dentry; + struct inode *inode = file_inode(pfile); + struct cifs_sb_info *cifs_sb = CIFS_SB(inode->i_sb); + struct cifs_open_parms oparms; + struct cifs_fid fid; + struct cifs_tcon *tcon; + const unsigned char *path; + char *returned_ioctl_info = NULL; + void *page = alloc_dentry_path(); + __le16 *utf16_path = NULL; + u8 oplock = SMB2_OPLOCK_LEVEL_NONE; + int rc = 0; + __u32 ret_len = 0; + + path = build_path_from_dentry(dentry, page); + if (IS_ERR(path)) { + rc = PTR_ERR(path); + goto notify_exit; + } + + utf16_path = cifs_convert_path_to_utf16(path, cifs_sb); + if (utf16_path == NULL) { + rc = -ENOMEM; + goto notify_exit; + } + + if (return_changes) { + if (copy_from_user(¬ify, ioc_buf, sizeof(struct smb3_notify_info))) { + rc = -EFAULT; + goto notify_exit; + } + } else { + if (copy_from_user(¬ify, ioc_buf, sizeof(struct smb3_notify))) { + rc = -EFAULT; + goto notify_exit; + } + notify.data_len = 0; + } + + tcon = cifs_sb_master_tcon(cifs_sb); + oparms = (struct cifs_open_parms) { + .tcon = tcon, + .path = path, + .desired_access = FILE_READ_ATTRIBUTES | FILE_READ_DATA, + .disposition = FILE_OPEN, + .create_options = cifs_create_options(cifs_sb, 0), + .fid = &fid, + }; + + rc = SMB2_open(xid, &oparms, utf16_path, &oplock, NULL, NULL, NULL, + NULL); + if (rc) + goto notify_exit; + + rc = SMB2_change_notify(xid, tcon, fid.persistent_fid, fid.volatile_fid, + notify.watch_tree, notify.completion_filter, + notify.data_len, &returned_ioctl_info, &ret_len); + + SMB2_close(xid, tcon, fid.persistent_fid, fid.volatile_fid); + + cifs_dbg(FYI, "change notify for path %s rc %d\n", path, rc); + if (return_changes && (ret_len > 0) && (notify.data_len > 0)) { + if (ret_len > notify.data_len) + ret_len = notify.data_len; + pnotify_buf = (struct smb3_notify_info __user *)ioc_buf; + if (copy_to_user(pnotify_buf->notify_data, returned_ioctl_info, ret_len)) + rc = -EFAULT; + else if (copy_to_user(&pnotify_buf->data_len, &ret_len, sizeof(ret_len))) + rc = -EFAULT; + } + kfree(returned_ioctl_info); +notify_exit: + free_dentry_path(page); + kfree(utf16_path); + return rc; +} + +static int +smb2_query_dir_first(const unsigned int xid, struct cifs_tcon *tcon, + const char *path, struct cifs_sb_info *cifs_sb, + struct cifs_fid *fid, __u16 search_flags, + struct cifs_search_info *srch_inf) +{ + __le16 *utf16_path; + struct smb_rqst rqst[2]; + struct kvec rsp_iov[2]; + int resp_buftype[2]; + struct kvec open_iov[SMB2_CREATE_IOV_SIZE]; + struct kvec qd_iov[SMB2_QUERY_DIRECTORY_IOV_SIZE]; + int rc, flags = 0; + u8 oplock = SMB2_OPLOCK_LEVEL_NONE; + struct cifs_open_parms oparms; + struct smb2_query_directory_rsp *qd_rsp = NULL; + struct smb2_create_rsp *op_rsp = NULL; + struct TCP_Server_Info *server = cifs_pick_channel(tcon->ses); + int retry_count = 0; + + utf16_path = cifs_convert_path_to_utf16(path, cifs_sb); + if (!utf16_path) + return -ENOMEM; + + if (smb3_encryption_required(tcon)) + flags |= CIFS_TRANSFORM_REQ; + + memset(rqst, 0, sizeof(rqst)); + resp_buftype[0] = resp_buftype[1] = CIFS_NO_BUFFER; + memset(rsp_iov, 0, sizeof(rsp_iov)); + + /* Open */ + memset(&open_iov, 0, sizeof(open_iov)); + rqst[0].rq_iov = open_iov; + rqst[0].rq_nvec = SMB2_CREATE_IOV_SIZE; + + oparms = (struct cifs_open_parms) { + .tcon = tcon, + .path = path, + .desired_access = FILE_READ_ATTRIBUTES | FILE_READ_DATA, + .disposition = FILE_OPEN, + .create_options = cifs_create_options(cifs_sb, 0), + .fid = fid, + }; + + rc = SMB2_open_init(tcon, server, + &rqst[0], &oplock, &oparms, utf16_path); + if (rc) + goto qdf_free; + smb2_set_next_command(tcon, &rqst[0]); + + /* Query directory */ + srch_inf->entries_in_buffer = 0; + srch_inf->index_of_last_entry = 2; + + memset(&qd_iov, 0, sizeof(qd_iov)); + rqst[1].rq_iov = qd_iov; + rqst[1].rq_nvec = SMB2_QUERY_DIRECTORY_IOV_SIZE; + + rc = SMB2_query_directory_init(xid, tcon, server, + &rqst[1], + COMPOUND_FID, COMPOUND_FID, + 0, srch_inf->info_level); + if (rc) + goto qdf_free; + + smb2_set_related(&rqst[1]); + +again: + rc = compound_send_recv(xid, tcon->ses, server, + flags, 2, rqst, + resp_buftype, rsp_iov); + + if (rc == -EAGAIN && retry_count++ < 10) + goto again; + + /* If the open failed there is nothing to do */ + op_rsp = (struct smb2_create_rsp *)rsp_iov[0].iov_base; + if (op_rsp == NULL || op_rsp->hdr.Status != STATUS_SUCCESS) { + cifs_dbg(FYI, "query_dir_first: open failed rc=%d\n", rc); + goto qdf_free; + } + fid->persistent_fid = op_rsp->PersistentFileId; + fid->volatile_fid = op_rsp->VolatileFileId; + + /* Anything else than ENODATA means a genuine error */ + if (rc && rc != -ENODATA) { + SMB2_close(xid, tcon, fid->persistent_fid, fid->volatile_fid); + cifs_dbg(FYI, "query_dir_first: query directory failed rc=%d\n", rc); + trace_smb3_query_dir_err(xid, fid->persistent_fid, + tcon->tid, tcon->ses->Suid, 0, 0, rc); + goto qdf_free; + } + + atomic_inc(&tcon->num_remote_opens); + + qd_rsp = (struct smb2_query_directory_rsp *)rsp_iov[1].iov_base; + if (qd_rsp->hdr.Status == STATUS_NO_MORE_FILES) { + trace_smb3_query_dir_done(xid, fid->persistent_fid, + tcon->tid, tcon->ses->Suid, 0, 0); + srch_inf->endOfSearch = true; + rc = 0; + goto qdf_free; + } + + rc = smb2_parse_query_directory(tcon, &rsp_iov[1], resp_buftype[1], + srch_inf); + if (rc) { + trace_smb3_query_dir_err(xid, fid->persistent_fid, tcon->tid, + tcon->ses->Suid, 0, 0, rc); + goto qdf_free; + } + resp_buftype[1] = CIFS_NO_BUFFER; + + trace_smb3_query_dir_done(xid, fid->persistent_fid, tcon->tid, + tcon->ses->Suid, 0, srch_inf->entries_in_buffer); + + qdf_free: + kfree(utf16_path); + SMB2_open_free(&rqst[0]); + SMB2_query_directory_free(&rqst[1]); + free_rsp_buf(resp_buftype[0], rsp_iov[0].iov_base); + free_rsp_buf(resp_buftype[1], rsp_iov[1].iov_base); + return rc; +} + +static int +smb2_query_dir_next(const unsigned int xid, struct cifs_tcon *tcon, + struct cifs_fid *fid, __u16 search_flags, + struct cifs_search_info *srch_inf) +{ + return SMB2_query_directory(xid, tcon, fid->persistent_fid, + fid->volatile_fid, 0, srch_inf); +} + +static int +smb2_close_dir(const unsigned int xid, struct cifs_tcon *tcon, + struct cifs_fid *fid) +{ + return SMB2_close(xid, tcon, fid->persistent_fid, fid->volatile_fid); +} + +/* + * If we negotiate SMB2 protocol and get STATUS_PENDING - update + * the number of credits and return true. Otherwise - return false. + */ +static bool +smb2_is_status_pending(char *buf, struct TCP_Server_Info *server) +{ + struct smb2_hdr *shdr = (struct smb2_hdr *)buf; + int scredits, in_flight; + + if (shdr->Status != STATUS_PENDING) + return false; + + if (shdr->CreditRequest) { + spin_lock(&server->req_lock); + server->credits += le16_to_cpu(shdr->CreditRequest); + scredits = server->credits; + in_flight = server->in_flight; + spin_unlock(&server->req_lock); + wake_up(&server->request_q); + + trace_smb3_pend_credits(server->CurrentMid, + server->conn_id, server->hostname, scredits, + le16_to_cpu(shdr->CreditRequest), in_flight); + cifs_dbg(FYI, "%s: status pending add %u credits total=%d\n", + __func__, le16_to_cpu(shdr->CreditRequest), scredits); + } + + return true; +} + +static bool +smb2_is_session_expired(char *buf) +{ + struct smb2_hdr *shdr = (struct smb2_hdr *)buf; + + if (shdr->Status != STATUS_NETWORK_SESSION_EXPIRED && + shdr->Status != STATUS_USER_SESSION_DELETED) + return false; + + trace_smb3_ses_expired(le32_to_cpu(shdr->Id.SyncId.TreeId), + le64_to_cpu(shdr->SessionId), + le16_to_cpu(shdr->Command), + le64_to_cpu(shdr->MessageId)); + cifs_dbg(FYI, "Session expired or deleted\n"); + + return true; +} + +static bool +smb2_is_status_io_timeout(char *buf) +{ + struct smb2_hdr *shdr = (struct smb2_hdr *)buf; + + if (shdr->Status == STATUS_IO_TIMEOUT) + return true; + else + return false; +} + +static void +smb2_is_network_name_deleted(char *buf, struct TCP_Server_Info *server) +{ + struct smb2_hdr *shdr = (struct smb2_hdr *)buf; + struct TCP_Server_Info *pserver; + struct cifs_ses *ses; + struct cifs_tcon *tcon; + + if (shdr->Status != STATUS_NETWORK_NAME_DELETED) + return; + + /* If server is a channel, select the primary channel */ + pserver = CIFS_SERVER_IS_CHAN(server) ? server->primary_server : server; + + spin_lock(&cifs_tcp_ses_lock); + list_for_each_entry(ses, &pserver->smb_ses_list, smb_ses_list) { + list_for_each_entry(tcon, &ses->tcon_list, tcon_list) { + if (tcon->tid == le32_to_cpu(shdr->Id.SyncId.TreeId)) { + spin_lock(&tcon->tc_lock); + tcon->need_reconnect = true; + spin_unlock(&tcon->tc_lock); + spin_unlock(&cifs_tcp_ses_lock); + pr_warn_once("Server share %s deleted.\n", + tcon->tree_name); + return; + } + } + } + spin_unlock(&cifs_tcp_ses_lock); +} + +static int +smb2_oplock_response(struct cifs_tcon *tcon, __u64 persistent_fid, + __u64 volatile_fid, __u16 net_fid, struct cifsInodeInfo *cinode) +{ + if (tcon->ses->server->capabilities & SMB2_GLOBAL_CAP_LEASING) + return SMB2_lease_break(0, tcon, cinode->lease_key, + smb2_get_lease_state(cinode)); + + return SMB2_oplock_break(0, tcon, persistent_fid, volatile_fid, + CIFS_CACHE_READ(cinode) ? 1 : 0); +} + +void +smb2_set_related(struct smb_rqst *rqst) +{ + struct smb2_hdr *shdr; + + shdr = (struct smb2_hdr *)(rqst->rq_iov[0].iov_base); + if (shdr == NULL) { + cifs_dbg(FYI, "shdr NULL in smb2_set_related\n"); + return; + } + shdr->Flags |= SMB2_FLAGS_RELATED_OPERATIONS; +} + +char smb2_padding[7] = {0, 0, 0, 0, 0, 0, 0}; + +void +smb2_set_next_command(struct cifs_tcon *tcon, struct smb_rqst *rqst) +{ + struct smb2_hdr *shdr; + struct cifs_ses *ses = tcon->ses; + struct TCP_Server_Info *server = ses->server; + unsigned long len = smb_rqst_len(server, rqst); + int i, num_padding; + + shdr = (struct smb2_hdr *)(rqst->rq_iov[0].iov_base); + if (shdr == NULL) { + cifs_dbg(FYI, "shdr NULL in smb2_set_next_command\n"); + return; + } + + /* SMB headers in a compound are 8 byte aligned. */ + + /* No padding needed */ + if (!(len & 7)) + goto finished; + + num_padding = 8 - (len & 7); + if (!smb3_encryption_required(tcon)) { + /* + * If we do not have encryption then we can just add an extra + * iov for the padding. + */ + rqst->rq_iov[rqst->rq_nvec].iov_base = smb2_padding; + rqst->rq_iov[rqst->rq_nvec].iov_len = num_padding; + rqst->rq_nvec++; + len += num_padding; + } else { + /* + * We can not add a small padding iov for the encryption case + * because the encryption framework can not handle the padding + * iovs. + * We have to flatten this into a single buffer and add + * the padding to it. + */ + for (i = 1; i < rqst->rq_nvec; i++) { + memcpy(rqst->rq_iov[0].iov_base + + rqst->rq_iov[0].iov_len, + rqst->rq_iov[i].iov_base, + rqst->rq_iov[i].iov_len); + rqst->rq_iov[0].iov_len += rqst->rq_iov[i].iov_len; + } + memset(rqst->rq_iov[0].iov_base + rqst->rq_iov[0].iov_len, + 0, num_padding); + rqst->rq_iov[0].iov_len += num_padding; + len += num_padding; + rqst->rq_nvec = 1; + } + + finished: + shdr->NextCommand = cpu_to_le32(len); +} + +/* + * Passes the query info response back to the caller on success. + * Caller need to free this with free_rsp_buf(). + */ +int +smb2_query_info_compound(const unsigned int xid, struct cifs_tcon *tcon, + const char *path, u32 desired_access, + u32 class, u32 type, u32 output_len, + struct kvec *rsp, int *buftype, + struct cifs_sb_info *cifs_sb) +{ + struct cifs_ses *ses = tcon->ses; + struct TCP_Server_Info *server = cifs_pick_channel(ses); + int flags = CIFS_CP_CREATE_CLOSE_OP; + struct smb_rqst rqst[3]; + int resp_buftype[3]; + struct kvec rsp_iov[3]; + struct kvec open_iov[SMB2_CREATE_IOV_SIZE]; + struct kvec qi_iov[1]; + struct kvec close_iov[1]; + u8 oplock = SMB2_OPLOCK_LEVEL_NONE; + struct cifs_open_parms oparms; + struct cifs_fid fid; + int rc; + __le16 *utf16_path; + struct cached_fid *cfid = NULL; + + if (!path) + path = ""; + utf16_path = cifs_convert_path_to_utf16(path, cifs_sb); + if (!utf16_path) + return -ENOMEM; + + if (smb3_encryption_required(tcon)) + flags |= CIFS_TRANSFORM_REQ; + + memset(rqst, 0, sizeof(rqst)); + resp_buftype[0] = resp_buftype[1] = resp_buftype[2] = CIFS_NO_BUFFER; + memset(rsp_iov, 0, sizeof(rsp_iov)); + + /* + * We can only call this for things we know are directories. + */ + if (!strcmp(path, "")) + open_cached_dir(xid, tcon, path, cifs_sb, false, + &cfid); /* cfid null if open dir failed */ + + memset(&open_iov, 0, sizeof(open_iov)); + rqst[0].rq_iov = open_iov; + rqst[0].rq_nvec = SMB2_CREATE_IOV_SIZE; + + oparms = (struct cifs_open_parms) { + .tcon = tcon, + .path = path, + .desired_access = desired_access, + .disposition = FILE_OPEN, + .create_options = cifs_create_options(cifs_sb, 0), + .fid = &fid, + }; + + rc = SMB2_open_init(tcon, server, + &rqst[0], &oplock, &oparms, utf16_path); + if (rc) + goto qic_exit; + smb2_set_next_command(tcon, &rqst[0]); + + memset(&qi_iov, 0, sizeof(qi_iov)); + rqst[1].rq_iov = qi_iov; + rqst[1].rq_nvec = 1; + + if (cfid) { + rc = SMB2_query_info_init(tcon, server, + &rqst[1], + cfid->fid.persistent_fid, + cfid->fid.volatile_fid, + class, type, 0, + output_len, 0, + NULL); + } else { + rc = SMB2_query_info_init(tcon, server, + &rqst[1], + COMPOUND_FID, + COMPOUND_FID, + class, type, 0, + output_len, 0, + NULL); + } + if (rc) + goto qic_exit; + if (!cfid) { + smb2_set_next_command(tcon, &rqst[1]); + smb2_set_related(&rqst[1]); + } + + memset(&close_iov, 0, sizeof(close_iov)); + rqst[2].rq_iov = close_iov; + rqst[2].rq_nvec = 1; + + rc = SMB2_close_init(tcon, server, + &rqst[2], COMPOUND_FID, COMPOUND_FID, false); + if (rc) + goto qic_exit; + smb2_set_related(&rqst[2]); + + if (cfid) { + rc = compound_send_recv(xid, ses, server, + flags, 1, &rqst[1], + &resp_buftype[1], &rsp_iov[1]); + } else { + rc = compound_send_recv(xid, ses, server, + flags, 3, rqst, + resp_buftype, rsp_iov); + } + if (rc) { + free_rsp_buf(resp_buftype[1], rsp_iov[1].iov_base); + if (rc == -EREMCHG) { + tcon->need_reconnect = true; + pr_warn_once("server share %s deleted\n", + tcon->tree_name); + } + goto qic_exit; + } + *rsp = rsp_iov[1]; + *buftype = resp_buftype[1]; + + qic_exit: + kfree(utf16_path); + SMB2_open_free(&rqst[0]); + SMB2_query_info_free(&rqst[1]); + SMB2_close_free(&rqst[2]); + free_rsp_buf(resp_buftype[0], rsp_iov[0].iov_base); + free_rsp_buf(resp_buftype[2], rsp_iov[2].iov_base); + if (cfid) + close_cached_dir(cfid); + return rc; +} + +static int +smb2_queryfs(const unsigned int xid, struct cifs_tcon *tcon, + struct cifs_sb_info *cifs_sb, struct kstatfs *buf) +{ + struct smb2_query_info_rsp *rsp; + struct smb2_fs_full_size_info *info = NULL; + struct kvec rsp_iov = {NULL, 0}; + int buftype = CIFS_NO_BUFFER; + int rc; + + + rc = smb2_query_info_compound(xid, tcon, "", + FILE_READ_ATTRIBUTES, + FS_FULL_SIZE_INFORMATION, + SMB2_O_INFO_FILESYSTEM, + sizeof(struct smb2_fs_full_size_info), + &rsp_iov, &buftype, cifs_sb); + if (rc) + goto qfs_exit; + + rsp = (struct smb2_query_info_rsp *)rsp_iov.iov_base; + buf->f_type = SMB2_SUPER_MAGIC; + info = (struct smb2_fs_full_size_info *)( + le16_to_cpu(rsp->OutputBufferOffset) + (char *)rsp); + rc = smb2_validate_iov(le16_to_cpu(rsp->OutputBufferOffset), + le32_to_cpu(rsp->OutputBufferLength), + &rsp_iov, + sizeof(struct smb2_fs_full_size_info)); + if (!rc) + smb2_copy_fs_info_to_kstatfs(info, buf); + +qfs_exit: + free_rsp_buf(buftype, rsp_iov.iov_base); + return rc; +} + +static int +smb311_queryfs(const unsigned int xid, struct cifs_tcon *tcon, + struct cifs_sb_info *cifs_sb, struct kstatfs *buf) +{ + int rc; + __le16 srch_path = 0; /* Null - open root of share */ + u8 oplock = SMB2_OPLOCK_LEVEL_NONE; + struct cifs_open_parms oparms; + struct cifs_fid fid; + + if (!tcon->posix_extensions) + return smb2_queryfs(xid, tcon, cifs_sb, buf); + + oparms = (struct cifs_open_parms) { + .tcon = tcon, + .path = "", + .desired_access = FILE_READ_ATTRIBUTES, + .disposition = FILE_OPEN, + .create_options = cifs_create_options(cifs_sb, 0), + .fid = &fid, + }; + + rc = SMB2_open(xid, &oparms, &srch_path, &oplock, NULL, NULL, + NULL, NULL); + if (rc) + return rc; + + rc = SMB311_posix_qfs_info(xid, tcon, fid.persistent_fid, + fid.volatile_fid, buf); + buf->f_type = SMB2_SUPER_MAGIC; + SMB2_close(xid, tcon, fid.persistent_fid, fid.volatile_fid); + return rc; +} + +static bool +smb2_compare_fids(struct cifsFileInfo *ob1, struct cifsFileInfo *ob2) +{ + return ob1->fid.persistent_fid == ob2->fid.persistent_fid && + ob1->fid.volatile_fid == ob2->fid.volatile_fid; +} + +static int +smb2_mand_lock(const unsigned int xid, struct cifsFileInfo *cfile, __u64 offset, + __u64 length, __u32 type, int lock, int unlock, bool wait) +{ + if (unlock && !lock) + type = SMB2_LOCKFLAG_UNLOCK; + return SMB2_lock(xid, tlink_tcon(cfile->tlink), + cfile->fid.persistent_fid, cfile->fid.volatile_fid, + current->tgid, length, offset, type, wait); +} + +static void +smb2_get_lease_key(struct inode *inode, struct cifs_fid *fid) +{ + memcpy(fid->lease_key, CIFS_I(inode)->lease_key, SMB2_LEASE_KEY_SIZE); +} + +static void +smb2_set_lease_key(struct inode *inode, struct cifs_fid *fid) +{ + memcpy(CIFS_I(inode)->lease_key, fid->lease_key, SMB2_LEASE_KEY_SIZE); +} + +static void +smb2_new_lease_key(struct cifs_fid *fid) +{ + generate_random_uuid(fid->lease_key); +} + +static int +smb2_get_dfs_refer(const unsigned int xid, struct cifs_ses *ses, + const char *search_name, + struct dfs_info3_param **target_nodes, + unsigned int *num_of_nodes, + const struct nls_table *nls_codepage, int remap) +{ + int rc; + __le16 *utf16_path = NULL; + int utf16_path_len = 0; + struct cifs_tcon *tcon; + struct fsctl_get_dfs_referral_req *dfs_req = NULL; + struct get_dfs_referral_rsp *dfs_rsp = NULL; + u32 dfs_req_size = 0, dfs_rsp_size = 0; + int retry_count = 0; + + cifs_dbg(FYI, "%s: path: %s\n", __func__, search_name); + + /* + * Try to use the IPC tcon, otherwise just use any + */ + tcon = ses->tcon_ipc; + if (tcon == NULL) { + spin_lock(&cifs_tcp_ses_lock); + tcon = list_first_entry_or_null(&ses->tcon_list, + struct cifs_tcon, + tcon_list); + if (tcon) + tcon->tc_count++; + spin_unlock(&cifs_tcp_ses_lock); + } + + if (tcon == NULL) { + cifs_dbg(VFS, "session %p has no tcon available for a dfs referral request\n", + ses); + rc = -ENOTCONN; + goto out; + } + + utf16_path = cifs_strndup_to_utf16(search_name, PATH_MAX, + &utf16_path_len, + nls_codepage, remap); + if (!utf16_path) { + rc = -ENOMEM; + goto out; + } + + dfs_req_size = sizeof(*dfs_req) + utf16_path_len; + dfs_req = kzalloc(dfs_req_size, GFP_KERNEL); + if (!dfs_req) { + rc = -ENOMEM; + goto out; + } + + /* Highest DFS referral version understood */ + dfs_req->MaxReferralLevel = DFS_VERSION; + + /* Path to resolve in an UTF-16 null-terminated string */ + memcpy(dfs_req->RequestFileName, utf16_path, utf16_path_len); + + do { + rc = SMB2_ioctl(xid, tcon, NO_FILE_ID, NO_FILE_ID, + FSCTL_DFS_GET_REFERRALS, + (char *)dfs_req, dfs_req_size, CIFSMaxBufSize, + (char **)&dfs_rsp, &dfs_rsp_size); + if (!is_retryable_error(rc)) + break; + usleep_range(512, 2048); + } while (++retry_count < 5); + + if (rc) { + if (!is_retryable_error(rc) && rc != -ENOENT && rc != -EOPNOTSUPP) + cifs_tcon_dbg(VFS, "%s: ioctl error: rc=%d\n", __func__, rc); + goto out; + } + + rc = parse_dfs_referrals(dfs_rsp, dfs_rsp_size, + num_of_nodes, target_nodes, + nls_codepage, remap, search_name, + true /* is_unicode */); + if (rc) { + cifs_tcon_dbg(VFS, "parse error in %s rc=%d\n", __func__, rc); + goto out; + } + + out: + if (tcon && !tcon->ipc) { + /* ipc tcons are not refcounted */ + spin_lock(&cifs_tcp_ses_lock); + tcon->tc_count--; + /* tc_count can never go negative */ + WARN_ON(tcon->tc_count < 0); + spin_unlock(&cifs_tcp_ses_lock); + } + kfree(utf16_path); + kfree(dfs_req); + kfree(dfs_rsp); + return rc; +} + +static int +parse_reparse_posix(struct reparse_posix_data *symlink_buf, + u32 plen, char **target_path, + struct cifs_sb_info *cifs_sb) +{ + unsigned int len; + + /* See MS-FSCC 2.1.2.6 for the 'NFS' style reparse tags */ + len = le16_to_cpu(symlink_buf->ReparseDataLength); + + if (le64_to_cpu(symlink_buf->InodeType) != NFS_SPECFILE_LNK) { + cifs_dbg(VFS, "%lld not a supported symlink type\n", + le64_to_cpu(symlink_buf->InodeType)); + return -EOPNOTSUPP; + } + + *target_path = cifs_strndup_from_utf16( + symlink_buf->PathBuffer, + len, true, cifs_sb->local_nls); + if (!(*target_path)) + return -ENOMEM; + + convert_delimiter(*target_path, '/'); + cifs_dbg(FYI, "%s: target path: %s\n", __func__, *target_path); + + return 0; +} + +static int +parse_reparse_symlink(struct reparse_symlink_data_buffer *symlink_buf, + u32 plen, char **target_path, + struct cifs_sb_info *cifs_sb) +{ + unsigned int sub_len; + unsigned int sub_offset; + + /* We handle Symbolic Link reparse tag here. See: MS-FSCC 2.1.2.4 */ + + sub_offset = le16_to_cpu(symlink_buf->SubstituteNameOffset); + sub_len = le16_to_cpu(symlink_buf->SubstituteNameLength); + if (sub_offset + 20 > plen || + sub_offset + sub_len + 20 > plen) { + cifs_dbg(VFS, "srv returned malformed symlink buffer\n"); + return -EIO; + } + + *target_path = cifs_strndup_from_utf16( + symlink_buf->PathBuffer + sub_offset, + sub_len, true, cifs_sb->local_nls); + if (!(*target_path)) + return -ENOMEM; + + convert_delimiter(*target_path, '/'); + cifs_dbg(FYI, "%s: target path: %s\n", __func__, *target_path); + + return 0; +} + +static int +parse_reparse_point(struct reparse_data_buffer *buf, + u32 plen, char **target_path, + struct cifs_sb_info *cifs_sb) +{ + if (plen < sizeof(struct reparse_data_buffer)) { + cifs_dbg(VFS, "reparse buffer is too small. Must be at least 8 bytes but was %d\n", + plen); + return -EIO; + } + + if (plen < le16_to_cpu(buf->ReparseDataLength) + + sizeof(struct reparse_data_buffer)) { + cifs_dbg(VFS, "srv returned invalid reparse buf length: %d\n", + plen); + return -EIO; + } + + /* See MS-FSCC 2.1.2 */ + switch (le32_to_cpu(buf->ReparseTag)) { + case IO_REPARSE_TAG_NFS: + return parse_reparse_posix( + (struct reparse_posix_data *)buf, + plen, target_path, cifs_sb); + case IO_REPARSE_TAG_SYMLINK: + return parse_reparse_symlink( + (struct reparse_symlink_data_buffer *)buf, + plen, target_path, cifs_sb); + default: + cifs_dbg(VFS, "srv returned unknown symlink buffer tag:0x%08x\n", + le32_to_cpu(buf->ReparseTag)); + return -EOPNOTSUPP; + } +} + +static int +smb2_query_symlink(const unsigned int xid, struct cifs_tcon *tcon, + struct cifs_sb_info *cifs_sb, const char *full_path, + char **target_path, bool is_reparse_point) +{ + int rc; + __le16 *utf16_path = NULL; + __u8 oplock = SMB2_OPLOCK_LEVEL_NONE; + struct cifs_open_parms oparms; + struct cifs_fid fid; + struct kvec err_iov = {NULL, 0}; + struct TCP_Server_Info *server = cifs_pick_channel(tcon->ses); + int flags = CIFS_CP_CREATE_CLOSE_OP; + struct smb_rqst rqst[3]; + int resp_buftype[3]; + struct kvec rsp_iov[3]; + struct kvec open_iov[SMB2_CREATE_IOV_SIZE]; + struct kvec io_iov[SMB2_IOCTL_IOV_SIZE]; + struct kvec close_iov[1]; + struct smb2_create_rsp *create_rsp; + struct smb2_ioctl_rsp *ioctl_rsp; + struct reparse_data_buffer *reparse_buf; + int create_options = is_reparse_point ? OPEN_REPARSE_POINT : 0; + u32 plen; + + cifs_dbg(FYI, "%s: path: %s\n", __func__, full_path); + + *target_path = NULL; + + if (smb3_encryption_required(tcon)) + flags |= CIFS_TRANSFORM_REQ; + + memset(rqst, 0, sizeof(rqst)); + resp_buftype[0] = resp_buftype[1] = resp_buftype[2] = CIFS_NO_BUFFER; + memset(rsp_iov, 0, sizeof(rsp_iov)); + + utf16_path = cifs_convert_path_to_utf16(full_path, cifs_sb); + if (!utf16_path) + return -ENOMEM; + + /* Open */ + memset(&open_iov, 0, sizeof(open_iov)); + rqst[0].rq_iov = open_iov; + rqst[0].rq_nvec = SMB2_CREATE_IOV_SIZE; + + oparms = (struct cifs_open_parms) { + .tcon = tcon, + .path = full_path, + .desired_access = FILE_READ_ATTRIBUTES, + .disposition = FILE_OPEN, + .create_options = cifs_create_options(cifs_sb, create_options), + .fid = &fid, + }; + + rc = SMB2_open_init(tcon, server, + &rqst[0], &oplock, &oparms, utf16_path); + if (rc) + goto querty_exit; + smb2_set_next_command(tcon, &rqst[0]); + + + /* IOCTL */ + memset(&io_iov, 0, sizeof(io_iov)); + rqst[1].rq_iov = io_iov; + rqst[1].rq_nvec = SMB2_IOCTL_IOV_SIZE; + + rc = SMB2_ioctl_init(tcon, server, + &rqst[1], fid.persistent_fid, + fid.volatile_fid, FSCTL_GET_REPARSE_POINT, NULL, 0, + CIFSMaxBufSize - + MAX_SMB2_CREATE_RESPONSE_SIZE - + MAX_SMB2_CLOSE_RESPONSE_SIZE); + if (rc) + goto querty_exit; + + smb2_set_next_command(tcon, &rqst[1]); + smb2_set_related(&rqst[1]); + + + /* Close */ + memset(&close_iov, 0, sizeof(close_iov)); + rqst[2].rq_iov = close_iov; + rqst[2].rq_nvec = 1; + + rc = SMB2_close_init(tcon, server, + &rqst[2], COMPOUND_FID, COMPOUND_FID, false); + if (rc) + goto querty_exit; + + smb2_set_related(&rqst[2]); + + rc = compound_send_recv(xid, tcon->ses, server, + flags, 3, rqst, + resp_buftype, rsp_iov); + + create_rsp = rsp_iov[0].iov_base; + if (create_rsp && create_rsp->hdr.Status) + err_iov = rsp_iov[0]; + ioctl_rsp = rsp_iov[1].iov_base; + + /* + * Open was successful and we got an ioctl response. + */ + if ((rc == 0) && (is_reparse_point)) { + /* See MS-FSCC 2.3.23 */ + + reparse_buf = (struct reparse_data_buffer *) + ((char *)ioctl_rsp + + le32_to_cpu(ioctl_rsp->OutputOffset)); + plen = le32_to_cpu(ioctl_rsp->OutputCount); + + if (plen + le32_to_cpu(ioctl_rsp->OutputOffset) > + rsp_iov[1].iov_len) { + cifs_tcon_dbg(VFS, "srv returned invalid ioctl len: %d\n", + plen); + rc = -EIO; + goto querty_exit; + } + + rc = parse_reparse_point(reparse_buf, plen, target_path, + cifs_sb); + goto querty_exit; + } + + if (!rc || !err_iov.iov_base) { + rc = -ENOENT; + goto querty_exit; + } + + rc = smb2_parse_symlink_response(cifs_sb, &err_iov, target_path); + + querty_exit: + cifs_dbg(FYI, "query symlink rc %d\n", rc); + kfree(utf16_path); + SMB2_open_free(&rqst[0]); + SMB2_ioctl_free(&rqst[1]); + SMB2_close_free(&rqst[2]); + free_rsp_buf(resp_buftype[0], rsp_iov[0].iov_base); + free_rsp_buf(resp_buftype[1], rsp_iov[1].iov_base); + free_rsp_buf(resp_buftype[2], rsp_iov[2].iov_base); + return rc; +} + +int +smb2_query_reparse_tag(const unsigned int xid, struct cifs_tcon *tcon, + struct cifs_sb_info *cifs_sb, const char *full_path, + __u32 *tag) +{ + int rc; + __le16 *utf16_path = NULL; + __u8 oplock = SMB2_OPLOCK_LEVEL_NONE; + struct cifs_open_parms oparms; + struct cifs_fid fid; + struct TCP_Server_Info *server = cifs_pick_channel(tcon->ses); + int flags = CIFS_CP_CREATE_CLOSE_OP; + struct smb_rqst rqst[3]; + int resp_buftype[3]; + struct kvec rsp_iov[3]; + struct kvec open_iov[SMB2_CREATE_IOV_SIZE]; + struct kvec io_iov[SMB2_IOCTL_IOV_SIZE]; + struct kvec close_iov[1]; + struct smb2_ioctl_rsp *ioctl_rsp; + struct reparse_data_buffer *reparse_buf; + u32 plen; + + cifs_dbg(FYI, "%s: path: %s\n", __func__, full_path); + + if (smb3_encryption_required(tcon)) + flags |= CIFS_TRANSFORM_REQ; + + memset(rqst, 0, sizeof(rqst)); + resp_buftype[0] = resp_buftype[1] = resp_buftype[2] = CIFS_NO_BUFFER; + memset(rsp_iov, 0, sizeof(rsp_iov)); + + utf16_path = cifs_convert_path_to_utf16(full_path, cifs_sb); + if (!utf16_path) + return -ENOMEM; + + /* + * setup smb2open - TODO add optimization to call cifs_get_readable_path + * to see if there is a handle already open that we can use + */ + memset(&open_iov, 0, sizeof(open_iov)); + rqst[0].rq_iov = open_iov; + rqst[0].rq_nvec = SMB2_CREATE_IOV_SIZE; + + oparms = (struct cifs_open_parms) { + .tcon = tcon, + .path = full_path, + .desired_access = FILE_READ_ATTRIBUTES, + .disposition = FILE_OPEN, + .create_options = cifs_create_options(cifs_sb, OPEN_REPARSE_POINT), + .fid = &fid, + }; + + rc = SMB2_open_init(tcon, server, + &rqst[0], &oplock, &oparms, utf16_path); + if (rc) + goto query_rp_exit; + smb2_set_next_command(tcon, &rqst[0]); + + + /* IOCTL */ + memset(&io_iov, 0, sizeof(io_iov)); + rqst[1].rq_iov = io_iov; + rqst[1].rq_nvec = SMB2_IOCTL_IOV_SIZE; + + rc = SMB2_ioctl_init(tcon, server, + &rqst[1], COMPOUND_FID, + COMPOUND_FID, FSCTL_GET_REPARSE_POINT, NULL, 0, + CIFSMaxBufSize - + MAX_SMB2_CREATE_RESPONSE_SIZE - + MAX_SMB2_CLOSE_RESPONSE_SIZE); + if (rc) + goto query_rp_exit; + + smb2_set_next_command(tcon, &rqst[1]); + smb2_set_related(&rqst[1]); + + + /* Close */ + memset(&close_iov, 0, sizeof(close_iov)); + rqst[2].rq_iov = close_iov; + rqst[2].rq_nvec = 1; + + rc = SMB2_close_init(tcon, server, + &rqst[2], COMPOUND_FID, COMPOUND_FID, false); + if (rc) + goto query_rp_exit; + + smb2_set_related(&rqst[2]); + + rc = compound_send_recv(xid, tcon->ses, server, + flags, 3, rqst, + resp_buftype, rsp_iov); + + ioctl_rsp = rsp_iov[1].iov_base; + + /* + * Open was successful and we got an ioctl response. + */ + if (rc == 0) { + /* See MS-FSCC 2.3.23 */ + + reparse_buf = (struct reparse_data_buffer *) + ((char *)ioctl_rsp + + le32_to_cpu(ioctl_rsp->OutputOffset)); + plen = le32_to_cpu(ioctl_rsp->OutputCount); + + if (plen + le32_to_cpu(ioctl_rsp->OutputOffset) > + rsp_iov[1].iov_len) { + cifs_tcon_dbg(FYI, "srv returned invalid ioctl len: %d\n", + plen); + rc = -EIO; + goto query_rp_exit; + } + *tag = le32_to_cpu(reparse_buf->ReparseTag); + } + + query_rp_exit: + kfree(utf16_path); + SMB2_open_free(&rqst[0]); + SMB2_ioctl_free(&rqst[1]); + SMB2_close_free(&rqst[2]); + free_rsp_buf(resp_buftype[0], rsp_iov[0].iov_base); + free_rsp_buf(resp_buftype[1], rsp_iov[1].iov_base); + free_rsp_buf(resp_buftype[2], rsp_iov[2].iov_base); + return rc; +} + +static struct cifs_ntsd * +get_smb2_acl_by_fid(struct cifs_sb_info *cifs_sb, + const struct cifs_fid *cifsfid, u32 *pacllen, u32 info) +{ + struct cifs_ntsd *pntsd = NULL; + unsigned int xid; + int rc = -EOPNOTSUPP; + struct tcon_link *tlink = cifs_sb_tlink(cifs_sb); + + if (IS_ERR(tlink)) + return ERR_CAST(tlink); + + xid = get_xid(); + cifs_dbg(FYI, "trying to get acl\n"); + + rc = SMB2_query_acl(xid, tlink_tcon(tlink), cifsfid->persistent_fid, + cifsfid->volatile_fid, (void **)&pntsd, pacllen, + info); + free_xid(xid); + + cifs_put_tlink(tlink); + + cifs_dbg(FYI, "%s: rc = %d ACL len %d\n", __func__, rc, *pacllen); + if (rc) + return ERR_PTR(rc); + return pntsd; + +} + +static struct cifs_ntsd * +get_smb2_acl_by_path(struct cifs_sb_info *cifs_sb, + const char *path, u32 *pacllen, u32 info) +{ + struct cifs_ntsd *pntsd = NULL; + u8 oplock = SMB2_OPLOCK_LEVEL_NONE; + unsigned int xid; + int rc; + struct cifs_tcon *tcon; + struct tcon_link *tlink = cifs_sb_tlink(cifs_sb); + struct cifs_fid fid; + struct cifs_open_parms oparms; + __le16 *utf16_path; + + cifs_dbg(FYI, "get smb3 acl for path %s\n", path); + if (IS_ERR(tlink)) + return ERR_CAST(tlink); + + tcon = tlink_tcon(tlink); + xid = get_xid(); + + utf16_path = cifs_convert_path_to_utf16(path, cifs_sb); + if (!utf16_path) { + rc = -ENOMEM; + free_xid(xid); + return ERR_PTR(rc); + } + + oparms = (struct cifs_open_parms) { + .tcon = tcon, + .path = path, + .desired_access = READ_CONTROL, + .disposition = FILE_OPEN, + /* + * When querying an ACL, even if the file is a symlink + * we want to open the source not the target, and so + * the protocol requires that the client specify this + * flag when opening a reparse point + */ + .create_options = cifs_create_options(cifs_sb, 0) | + OPEN_REPARSE_POINT, + .fid = &fid, + }; + + if (info & SACL_SECINFO) + oparms.desired_access |= SYSTEM_SECURITY; + + rc = SMB2_open(xid, &oparms, utf16_path, &oplock, NULL, NULL, NULL, + NULL); + kfree(utf16_path); + if (!rc) { + rc = SMB2_query_acl(xid, tlink_tcon(tlink), fid.persistent_fid, + fid.volatile_fid, (void **)&pntsd, pacllen, + info); + SMB2_close(xid, tcon, fid.persistent_fid, fid.volatile_fid); + } + + cifs_put_tlink(tlink); + free_xid(xid); + + cifs_dbg(FYI, "%s: rc = %d ACL len %d\n", __func__, rc, *pacllen); + if (rc) + return ERR_PTR(rc); + return pntsd; +} + +static int +set_smb2_acl(struct cifs_ntsd *pnntsd, __u32 acllen, + struct inode *inode, const char *path, int aclflag) +{ + u8 oplock = SMB2_OPLOCK_LEVEL_NONE; + unsigned int xid; + int rc, access_flags = 0; + struct cifs_tcon *tcon; + struct cifs_sb_info *cifs_sb = CIFS_SB(inode->i_sb); + struct tcon_link *tlink = cifs_sb_tlink(cifs_sb); + struct cifs_fid fid; + struct cifs_open_parms oparms; + __le16 *utf16_path; + + cifs_dbg(FYI, "set smb3 acl for path %s\n", path); + if (IS_ERR(tlink)) + return PTR_ERR(tlink); + + tcon = tlink_tcon(tlink); + xid = get_xid(); + + if (aclflag & CIFS_ACL_OWNER || aclflag & CIFS_ACL_GROUP) + access_flags |= WRITE_OWNER; + if (aclflag & CIFS_ACL_SACL) + access_flags |= SYSTEM_SECURITY; + if (aclflag & CIFS_ACL_DACL) + access_flags |= WRITE_DAC; + + utf16_path = cifs_convert_path_to_utf16(path, cifs_sb); + if (!utf16_path) { + rc = -ENOMEM; + free_xid(xid); + return rc; + } + + oparms = (struct cifs_open_parms) { + .tcon = tcon, + .desired_access = access_flags, + .create_options = cifs_create_options(cifs_sb, 0), + .disposition = FILE_OPEN, + .path = path, + .fid = &fid, + }; + + rc = SMB2_open(xid, &oparms, utf16_path, &oplock, NULL, NULL, + NULL, NULL); + kfree(utf16_path); + if (!rc) { + rc = SMB2_set_acl(xid, tlink_tcon(tlink), fid.persistent_fid, + fid.volatile_fid, pnntsd, acllen, aclflag); + SMB2_close(xid, tcon, fid.persistent_fid, fid.volatile_fid); + } + + cifs_put_tlink(tlink); + free_xid(xid); + return rc; +} + +/* Retrieve an ACL from the server */ +static struct cifs_ntsd * +get_smb2_acl(struct cifs_sb_info *cifs_sb, + struct inode *inode, const char *path, + u32 *pacllen, u32 info) +{ + struct cifs_ntsd *pntsd = NULL; + struct cifsFileInfo *open_file = NULL; + + if (inode && !(info & SACL_SECINFO)) + open_file = find_readable_file(CIFS_I(inode), true); + if (!open_file || (info & SACL_SECINFO)) + return get_smb2_acl_by_path(cifs_sb, path, pacllen, info); + + pntsd = get_smb2_acl_by_fid(cifs_sb, &open_file->fid, pacllen, info); + cifsFileInfo_put(open_file); + return pntsd; +} + +static long smb3_zero_data(struct file *file, struct cifs_tcon *tcon, + loff_t offset, loff_t len, unsigned int xid) +{ + struct cifsFileInfo *cfile = file->private_data; + struct file_zero_data_information fsctl_buf; + + cifs_dbg(FYI, "Offset %lld len %lld\n", offset, len); + + fsctl_buf.FileOffset = cpu_to_le64(offset); + fsctl_buf.BeyondFinalZero = cpu_to_le64(offset + len); + + return SMB2_ioctl(xid, tcon, cfile->fid.persistent_fid, + cfile->fid.volatile_fid, FSCTL_SET_ZERO_DATA, + (char *)&fsctl_buf, + sizeof(struct file_zero_data_information), + 0, NULL, NULL); +} + +static long smb3_zero_range(struct file *file, struct cifs_tcon *tcon, + loff_t offset, loff_t len, bool keep_size) +{ + struct cifs_ses *ses = tcon->ses; + struct inode *inode = file_inode(file); + struct cifsInodeInfo *cifsi = CIFS_I(inode); + struct cifsFileInfo *cfile = file->private_data; + long rc; + unsigned int xid; + __le64 eof; + + xid = get_xid(); + + trace_smb3_zero_enter(xid, cfile->fid.persistent_fid, tcon->tid, + ses->Suid, offset, len); + + inode_lock(inode); + filemap_invalidate_lock(inode->i_mapping); + + /* + * We zero the range through ioctl, so we need remove the page caches + * first, otherwise the data may be inconsistent with the server. + */ + truncate_pagecache_range(inode, offset, offset + len - 1); + + /* if file not oplocked can't be sure whether asking to extend size */ + rc = -EOPNOTSUPP; + if (keep_size == false && !CIFS_CACHE_READ(cifsi)) + goto zero_range_exit; + + rc = smb3_zero_data(file, tcon, offset, len, xid); + if (rc < 0) + goto zero_range_exit; + + /* + * do we also need to change the size of the file? + */ + if (keep_size == false && i_size_read(inode) < offset + len) { + eof = cpu_to_le64(offset + len); + rc = SMB2_set_eof(xid, tcon, cfile->fid.persistent_fid, + cfile->fid.volatile_fid, cfile->pid, &eof); + } + + zero_range_exit: + filemap_invalidate_unlock(inode->i_mapping); + inode_unlock(inode); + free_xid(xid); + if (rc) + trace_smb3_zero_err(xid, cfile->fid.persistent_fid, tcon->tid, + ses->Suid, offset, len, rc); + else + trace_smb3_zero_done(xid, cfile->fid.persistent_fid, tcon->tid, + ses->Suid, offset, len); + return rc; +} + +static long smb3_punch_hole(struct file *file, struct cifs_tcon *tcon, + loff_t offset, loff_t len) +{ + struct inode *inode = file_inode(file); + struct cifsFileInfo *cfile = file->private_data; + struct file_zero_data_information fsctl_buf; + long rc; + unsigned int xid; + __u8 set_sparse = 1; + + xid = get_xid(); + + inode_lock(inode); + /* Need to make file sparse, if not already, before freeing range. */ + /* Consider adding equivalent for compressed since it could also work */ + if (!smb2_set_sparse(xid, tcon, cfile, inode, set_sparse)) { + rc = -EOPNOTSUPP; + goto out; + } + + filemap_invalidate_lock(inode->i_mapping); + /* + * We implement the punch hole through ioctl, so we need remove the page + * caches first, otherwise the data may be inconsistent with the server. + */ + truncate_pagecache_range(inode, offset, offset + len - 1); + + cifs_dbg(FYI, "Offset %lld len %lld\n", offset, len); + + fsctl_buf.FileOffset = cpu_to_le64(offset); + fsctl_buf.BeyondFinalZero = cpu_to_le64(offset + len); + + rc = SMB2_ioctl(xid, tcon, cfile->fid.persistent_fid, + cfile->fid.volatile_fid, FSCTL_SET_ZERO_DATA, + (char *)&fsctl_buf, + sizeof(struct file_zero_data_information), + CIFSMaxBufSize, NULL, NULL); + filemap_invalidate_unlock(inode->i_mapping); +out: + inode_unlock(inode); + free_xid(xid); + return rc; +} + +static int smb3_simple_fallocate_write_range(unsigned int xid, + struct cifs_tcon *tcon, + struct cifsFileInfo *cfile, + loff_t off, loff_t len, + char *buf) +{ + struct cifs_io_parms io_parms = {0}; + int nbytes; + int rc = 0; + struct kvec iov[2]; + + io_parms.netfid = cfile->fid.netfid; + io_parms.pid = current->tgid; + io_parms.tcon = tcon; + io_parms.persistent_fid = cfile->fid.persistent_fid; + io_parms.volatile_fid = cfile->fid.volatile_fid; + + while (len) { + io_parms.offset = off; + io_parms.length = len; + if (io_parms.length > SMB2_MAX_BUFFER_SIZE) + io_parms.length = SMB2_MAX_BUFFER_SIZE; + /* iov[0] is reserved for smb header */ + iov[1].iov_base = buf; + iov[1].iov_len = io_parms.length; + rc = SMB2_write(xid, &io_parms, &nbytes, iov, 1); + if (rc) + break; + if (nbytes > len) + return -EINVAL; + buf += nbytes; + off += nbytes; + len -= nbytes; + } + return rc; +} + +static int smb3_simple_fallocate_range(unsigned int xid, + struct cifs_tcon *tcon, + struct cifsFileInfo *cfile, + loff_t off, loff_t len) +{ + struct file_allocated_range_buffer in_data, *out_data = NULL, *tmp_data; + u32 out_data_len; + char *buf = NULL; + loff_t l; + int rc; + + in_data.file_offset = cpu_to_le64(off); + in_data.length = cpu_to_le64(len); + rc = SMB2_ioctl(xid, tcon, cfile->fid.persistent_fid, + cfile->fid.volatile_fid, + FSCTL_QUERY_ALLOCATED_RANGES, + (char *)&in_data, sizeof(in_data), + 1024 * sizeof(struct file_allocated_range_buffer), + (char **)&out_data, &out_data_len); + if (rc) + goto out; + + buf = kzalloc(1024 * 1024, GFP_KERNEL); + if (buf == NULL) { + rc = -ENOMEM; + goto out; + } + + tmp_data = out_data; + while (len) { + /* + * The rest of the region is unmapped so write it all. + */ + if (out_data_len == 0) { + rc = smb3_simple_fallocate_write_range(xid, tcon, + cfile, off, len, buf); + goto out; + } + + if (out_data_len < sizeof(struct file_allocated_range_buffer)) { + rc = -EINVAL; + goto out; + } + + if (off < le64_to_cpu(tmp_data->file_offset)) { + /* + * We are at a hole. Write until the end of the region + * or until the next allocated data, + * whichever comes next. + */ + l = le64_to_cpu(tmp_data->file_offset) - off; + if (len < l) + l = len; + rc = smb3_simple_fallocate_write_range(xid, tcon, + cfile, off, l, buf); + if (rc) + goto out; + off = off + l; + len = len - l; + if (len == 0) + goto out; + } + /* + * We are at a section of allocated data, just skip forward + * until the end of the data or the end of the region + * we are supposed to fallocate, whichever comes first. + */ + l = le64_to_cpu(tmp_data->length); + if (len < l) + l = len; + off += l; + len -= l; + + tmp_data = &tmp_data[1]; + out_data_len -= sizeof(struct file_allocated_range_buffer); + } + + out: + kfree(out_data); + kfree(buf); + return rc; +} + + +static long smb3_simple_falloc(struct file *file, struct cifs_tcon *tcon, + loff_t off, loff_t len, bool keep_size) +{ + struct inode *inode; + struct cifsInodeInfo *cifsi; + struct cifsFileInfo *cfile = file->private_data; + long rc = -EOPNOTSUPP; + unsigned int xid; + __le64 eof; + + xid = get_xid(); + + inode = d_inode(cfile->dentry); + cifsi = CIFS_I(inode); + + trace_smb3_falloc_enter(xid, cfile->fid.persistent_fid, tcon->tid, + tcon->ses->Suid, off, len); + /* if file not oplocked can't be sure whether asking to extend size */ + if (!CIFS_CACHE_READ(cifsi)) + if (keep_size == false) { + trace_smb3_falloc_err(xid, cfile->fid.persistent_fid, + tcon->tid, tcon->ses->Suid, off, len, rc); + free_xid(xid); + return rc; + } + + /* + * Extending the file + */ + if ((keep_size == false) && i_size_read(inode) < off + len) { + rc = inode_newsize_ok(inode, off + len); + if (rc) + goto out; + + if (cifsi->cifsAttrs & FILE_ATTRIBUTE_SPARSE_FILE) + smb2_set_sparse(xid, tcon, cfile, inode, false); + + eof = cpu_to_le64(off + len); + rc = SMB2_set_eof(xid, tcon, cfile->fid.persistent_fid, + cfile->fid.volatile_fid, cfile->pid, &eof); + if (rc == 0) { + cifsi->server_eof = off + len; + cifs_setsize(inode, off + len); + cifs_truncate_page(inode->i_mapping, inode->i_size); + truncate_setsize(inode, off + len); + } + goto out; + } + + /* + * Files are non-sparse by default so falloc may be a no-op + * Must check if file sparse. If not sparse, and since we are not + * extending then no need to do anything since file already allocated + */ + if ((cifsi->cifsAttrs & FILE_ATTRIBUTE_SPARSE_FILE) == 0) { + rc = 0; + goto out; + } + + if (keep_size == true) { + /* + * We can not preallocate pages beyond the end of the file + * in SMB2 + */ + if (off >= i_size_read(inode)) { + rc = 0; + goto out; + } + /* + * For fallocates that are partially beyond the end of file, + * clamp len so we only fallocate up to the end of file. + */ + if (off + len > i_size_read(inode)) { + len = i_size_read(inode) - off; + } + } + + if ((keep_size == true) || (i_size_read(inode) >= off + len)) { + /* + * At this point, we are trying to fallocate an internal + * regions of a sparse file. Since smb2 does not have a + * fallocate command we have two otions on how to emulate this. + * We can either turn the entire file to become non-sparse + * which we only do if the fallocate is for virtually + * the whole file, or we can overwrite the region with zeroes + * using SMB2_write, which could be prohibitevly expensive + * if len is large. + */ + /* + * We are only trying to fallocate a small region so + * just write it with zero. + */ + if (len <= 1024 * 1024) { + rc = smb3_simple_fallocate_range(xid, tcon, cfile, + off, len); + goto out; + } + + /* + * Check if falloc starts within first few pages of file + * and ends within a few pages of the end of file to + * ensure that most of file is being forced to be + * fallocated now. If so then setting whole file sparse + * ie potentially making a few extra pages at the beginning + * or end of the file non-sparse via set_sparse is harmless. + */ + if ((off > 8192) || (off + len + 8192 < i_size_read(inode))) { + rc = -EOPNOTSUPP; + goto out; + } + } + + smb2_set_sparse(xid, tcon, cfile, inode, false); + rc = 0; + +out: + if (rc) + trace_smb3_falloc_err(xid, cfile->fid.persistent_fid, tcon->tid, + tcon->ses->Suid, off, len, rc); + else + trace_smb3_falloc_done(xid, cfile->fid.persistent_fid, tcon->tid, + tcon->ses->Suid, off, len); + + free_xid(xid); + return rc; +} + +static long smb3_collapse_range(struct file *file, struct cifs_tcon *tcon, + loff_t off, loff_t len) +{ + int rc; + unsigned int xid; + struct inode *inode = file_inode(file); + struct cifsFileInfo *cfile = file->private_data; + struct cifsInodeInfo *cifsi = CIFS_I(inode); + __le64 eof; + loff_t old_eof; + + xid = get_xid(); + + inode_lock(inode); + + old_eof = i_size_read(inode); + if ((off >= old_eof) || + off + len >= old_eof) { + rc = -EINVAL; + goto out; + } + + filemap_invalidate_lock(inode->i_mapping); + rc = filemap_write_and_wait_range(inode->i_mapping, off, old_eof - 1); + if (rc < 0) + goto out_2; + + truncate_pagecache_range(inode, off, old_eof); + + rc = smb2_copychunk_range(xid, cfile, cfile, off + len, + old_eof - off - len, off); + if (rc < 0) + goto out_2; + + eof = cpu_to_le64(old_eof - len); + rc = SMB2_set_eof(xid, tcon, cfile->fid.persistent_fid, + cfile->fid.volatile_fid, cfile->pid, &eof); + if (rc < 0) + goto out_2; + + rc = 0; + + cifsi->server_eof = i_size_read(inode) - len; + truncate_setsize(inode, cifsi->server_eof); + fscache_resize_cookie(cifs_inode_cookie(inode), cifsi->server_eof); +out_2: + filemap_invalidate_unlock(inode->i_mapping); + out: + inode_unlock(inode); + free_xid(xid); + return rc; +} + +static long smb3_insert_range(struct file *file, struct cifs_tcon *tcon, + loff_t off, loff_t len) +{ + int rc; + unsigned int xid; + struct cifsFileInfo *cfile = file->private_data; + struct inode *inode = file_inode(file); + __le64 eof; + __u64 count, old_eof; + + xid = get_xid(); + + inode_lock(inode); + + old_eof = i_size_read(inode); + if (off >= old_eof) { + rc = -EINVAL; + goto out; + } + + count = old_eof - off; + eof = cpu_to_le64(old_eof + len); + + filemap_invalidate_lock(inode->i_mapping); + rc = filemap_write_and_wait_range(inode->i_mapping, off, old_eof + len - 1); + if (rc < 0) + goto out_2; + truncate_pagecache_range(inode, off, old_eof); + + rc = SMB2_set_eof(xid, tcon, cfile->fid.persistent_fid, + cfile->fid.volatile_fid, cfile->pid, &eof); + if (rc < 0) + goto out_2; + + rc = smb2_copychunk_range(xid, cfile, cfile, off, count, off + len); + if (rc < 0) + goto out_2; + + rc = smb3_zero_data(file, tcon, off, len, xid); + if (rc < 0) + goto out_2; + + rc = 0; +out_2: + filemap_invalidate_unlock(inode->i_mapping); + out: + inode_unlock(inode); + free_xid(xid); + return rc; +} + +static loff_t smb3_llseek(struct file *file, struct cifs_tcon *tcon, loff_t offset, int whence) +{ + struct cifsFileInfo *wrcfile, *cfile = file->private_data; + struct cifsInodeInfo *cifsi; + struct inode *inode; + int rc = 0; + struct file_allocated_range_buffer in_data, *out_data = NULL; + u32 out_data_len; + unsigned int xid; + + if (whence != SEEK_HOLE && whence != SEEK_DATA) + return generic_file_llseek(file, offset, whence); + + inode = d_inode(cfile->dentry); + cifsi = CIFS_I(inode); + + if (offset < 0 || offset >= i_size_read(inode)) + return -ENXIO; + + xid = get_xid(); + /* + * We need to be sure that all dirty pages are written as they + * might fill holes on the server. + * Note that we also MUST flush any written pages since at least + * some servers (Windows2016) will not reflect recent writes in + * QUERY_ALLOCATED_RANGES until SMB2_flush is called. + */ + wrcfile = find_writable_file(cifsi, FIND_WR_ANY); + if (wrcfile) { + filemap_write_and_wait(inode->i_mapping); + smb2_flush_file(xid, tcon, &wrcfile->fid); + cifsFileInfo_put(wrcfile); + } + + if (!(cifsi->cifsAttrs & FILE_ATTRIBUTE_SPARSE_FILE)) { + if (whence == SEEK_HOLE) + offset = i_size_read(inode); + goto lseek_exit; + } + + in_data.file_offset = cpu_to_le64(offset); + in_data.length = cpu_to_le64(i_size_read(inode)); + + rc = SMB2_ioctl(xid, tcon, cfile->fid.persistent_fid, + cfile->fid.volatile_fid, + FSCTL_QUERY_ALLOCATED_RANGES, + (char *)&in_data, sizeof(in_data), + sizeof(struct file_allocated_range_buffer), + (char **)&out_data, &out_data_len); + if (rc == -E2BIG) + rc = 0; + if (rc) + goto lseek_exit; + + if (whence == SEEK_HOLE && out_data_len == 0) + goto lseek_exit; + + if (whence == SEEK_DATA && out_data_len == 0) { + rc = -ENXIO; + goto lseek_exit; + } + + if (out_data_len < sizeof(struct file_allocated_range_buffer)) { + rc = -EINVAL; + goto lseek_exit; + } + if (whence == SEEK_DATA) { + offset = le64_to_cpu(out_data->file_offset); + goto lseek_exit; + } + if (offset < le64_to_cpu(out_data->file_offset)) + goto lseek_exit; + + offset = le64_to_cpu(out_data->file_offset) + le64_to_cpu(out_data->length); + + lseek_exit: + free_xid(xid); + kfree(out_data); + if (!rc) + return vfs_setpos(file, offset, inode->i_sb->s_maxbytes); + else + return rc; +} + +static int smb3_fiemap(struct cifs_tcon *tcon, + struct cifsFileInfo *cfile, + struct fiemap_extent_info *fei, u64 start, u64 len) +{ + unsigned int xid; + struct file_allocated_range_buffer in_data, *out_data; + u32 out_data_len; + int i, num, rc, flags, last_blob; + u64 next; + + rc = fiemap_prep(d_inode(cfile->dentry), fei, start, &len, 0); + if (rc) + return rc; + + xid = get_xid(); + again: + in_data.file_offset = cpu_to_le64(start); + in_data.length = cpu_to_le64(len); + + rc = SMB2_ioctl(xid, tcon, cfile->fid.persistent_fid, + cfile->fid.volatile_fid, + FSCTL_QUERY_ALLOCATED_RANGES, + (char *)&in_data, sizeof(in_data), + 1024 * sizeof(struct file_allocated_range_buffer), + (char **)&out_data, &out_data_len); + if (rc == -E2BIG) { + last_blob = 0; + rc = 0; + } else + last_blob = 1; + if (rc) + goto out; + + if (out_data_len && out_data_len < sizeof(struct file_allocated_range_buffer)) { + rc = -EINVAL; + goto out; + } + if (out_data_len % sizeof(struct file_allocated_range_buffer)) { + rc = -EINVAL; + goto out; + } + + num = out_data_len / sizeof(struct file_allocated_range_buffer); + for (i = 0; i < num; i++) { + flags = 0; + if (i == num - 1 && last_blob) + flags |= FIEMAP_EXTENT_LAST; + + rc = fiemap_fill_next_extent(fei, + le64_to_cpu(out_data[i].file_offset), + le64_to_cpu(out_data[i].file_offset), + le64_to_cpu(out_data[i].length), + flags); + if (rc < 0) + goto out; + if (rc == 1) { + rc = 0; + goto out; + } + } + + if (!last_blob) { + next = le64_to_cpu(out_data[num - 1].file_offset) + + le64_to_cpu(out_data[num - 1].length); + len = len - (next - start); + start = next; + goto again; + } + + out: + free_xid(xid); + kfree(out_data); + return rc; +} + +static long smb3_fallocate(struct file *file, struct cifs_tcon *tcon, int mode, + loff_t off, loff_t len) +{ + /* KEEP_SIZE already checked for by do_fallocate */ + if (mode & FALLOC_FL_PUNCH_HOLE) + return smb3_punch_hole(file, tcon, off, len); + else if (mode & FALLOC_FL_ZERO_RANGE) { + if (mode & FALLOC_FL_KEEP_SIZE) + return smb3_zero_range(file, tcon, off, len, true); + return smb3_zero_range(file, tcon, off, len, false); + } else if (mode == FALLOC_FL_KEEP_SIZE) + return smb3_simple_falloc(file, tcon, off, len, true); + else if (mode == FALLOC_FL_COLLAPSE_RANGE) + return smb3_collapse_range(file, tcon, off, len); + else if (mode == FALLOC_FL_INSERT_RANGE) + return smb3_insert_range(file, tcon, off, len); + else if (mode == 0) + return smb3_simple_falloc(file, tcon, off, len, false); + + return -EOPNOTSUPP; +} + +static void +smb2_downgrade_oplock(struct TCP_Server_Info *server, + struct cifsInodeInfo *cinode, __u32 oplock, + unsigned int epoch, bool *purge_cache) +{ + server->ops->set_oplock_level(cinode, oplock, 0, NULL); +} + +static void +smb21_set_oplock_level(struct cifsInodeInfo *cinode, __u32 oplock, + unsigned int epoch, bool *purge_cache); + +static void +smb3_downgrade_oplock(struct TCP_Server_Info *server, + struct cifsInodeInfo *cinode, __u32 oplock, + unsigned int epoch, bool *purge_cache) +{ + unsigned int old_state = cinode->oplock; + unsigned int old_epoch = cinode->epoch; + unsigned int new_state; + + if (epoch > old_epoch) { + smb21_set_oplock_level(cinode, oplock, 0, NULL); + cinode->epoch = epoch; + } + + new_state = cinode->oplock; + *purge_cache = false; + + if ((old_state & CIFS_CACHE_READ_FLG) != 0 && + (new_state & CIFS_CACHE_READ_FLG) == 0) + *purge_cache = true; + else if (old_state == new_state && (epoch - old_epoch > 1)) + *purge_cache = true; +} + +static void +smb2_set_oplock_level(struct cifsInodeInfo *cinode, __u32 oplock, + unsigned int epoch, bool *purge_cache) +{ + oplock &= 0xFF; + cinode->lease_granted = false; + if (oplock == SMB2_OPLOCK_LEVEL_NOCHANGE) + return; + if (oplock == SMB2_OPLOCK_LEVEL_BATCH) { + cinode->oplock = CIFS_CACHE_RHW_FLG; + cifs_dbg(FYI, "Batch Oplock granted on inode %p\n", + &cinode->netfs.inode); + } else if (oplock == SMB2_OPLOCK_LEVEL_EXCLUSIVE) { + cinode->oplock = CIFS_CACHE_RW_FLG; + cifs_dbg(FYI, "Exclusive Oplock granted on inode %p\n", + &cinode->netfs.inode); + } else if (oplock == SMB2_OPLOCK_LEVEL_II) { + cinode->oplock = CIFS_CACHE_READ_FLG; + cifs_dbg(FYI, "Level II Oplock granted on inode %p\n", + &cinode->netfs.inode); + } else + cinode->oplock = 0; +} + +static void +smb21_set_oplock_level(struct cifsInodeInfo *cinode, __u32 oplock, + unsigned int epoch, bool *purge_cache) +{ + char message[5] = {0}; + unsigned int new_oplock = 0; + + oplock &= 0xFF; + cinode->lease_granted = true; + if (oplock == SMB2_OPLOCK_LEVEL_NOCHANGE) + return; + + /* Check if the server granted an oplock rather than a lease */ + if (oplock & SMB2_OPLOCK_LEVEL_EXCLUSIVE) + return smb2_set_oplock_level(cinode, oplock, epoch, + purge_cache); + + if (oplock & SMB2_LEASE_READ_CACHING_HE) { + new_oplock |= CIFS_CACHE_READ_FLG; + strcat(message, "R"); + } + if (oplock & SMB2_LEASE_HANDLE_CACHING_HE) { + new_oplock |= CIFS_CACHE_HANDLE_FLG; + strcat(message, "H"); + } + if (oplock & SMB2_LEASE_WRITE_CACHING_HE) { + new_oplock |= CIFS_CACHE_WRITE_FLG; + strcat(message, "W"); + } + if (!new_oplock) + strncpy(message, "None", sizeof(message)); + + cinode->oplock = new_oplock; + cifs_dbg(FYI, "%s Lease granted on inode %p\n", message, + &cinode->netfs.inode); +} + +static void +smb3_set_oplock_level(struct cifsInodeInfo *cinode, __u32 oplock, + unsigned int epoch, bool *purge_cache) +{ + unsigned int old_oplock = cinode->oplock; + + smb21_set_oplock_level(cinode, oplock, epoch, purge_cache); + + if (purge_cache) { + *purge_cache = false; + if (old_oplock == CIFS_CACHE_READ_FLG) { + if (cinode->oplock == CIFS_CACHE_READ_FLG && + (epoch - cinode->epoch > 0)) + *purge_cache = true; + else if (cinode->oplock == CIFS_CACHE_RH_FLG && + (epoch - cinode->epoch > 1)) + *purge_cache = true; + else if (cinode->oplock == CIFS_CACHE_RHW_FLG && + (epoch - cinode->epoch > 1)) + *purge_cache = true; + else if (cinode->oplock == 0 && + (epoch - cinode->epoch > 0)) + *purge_cache = true; + } else if (old_oplock == CIFS_CACHE_RH_FLG) { + if (cinode->oplock == CIFS_CACHE_RH_FLG && + (epoch - cinode->epoch > 0)) + *purge_cache = true; + else if (cinode->oplock == CIFS_CACHE_RHW_FLG && + (epoch - cinode->epoch > 1)) + *purge_cache = true; + } + cinode->epoch = epoch; + } +} + +#ifdef CONFIG_CIFS_ALLOW_INSECURE_LEGACY +static bool +smb2_is_read_op(__u32 oplock) +{ + return oplock == SMB2_OPLOCK_LEVEL_II; +} +#endif /* CIFS_ALLOW_INSECURE_LEGACY */ + +static bool +smb21_is_read_op(__u32 oplock) +{ + return (oplock & SMB2_LEASE_READ_CACHING_HE) && + !(oplock & SMB2_LEASE_WRITE_CACHING_HE); +} + +static __le32 +map_oplock_to_lease(u8 oplock) +{ + if (oplock == SMB2_OPLOCK_LEVEL_EXCLUSIVE) + return SMB2_LEASE_WRITE_CACHING_LE | SMB2_LEASE_READ_CACHING_LE; + else if (oplock == SMB2_OPLOCK_LEVEL_II) + return SMB2_LEASE_READ_CACHING_LE; + else if (oplock == SMB2_OPLOCK_LEVEL_BATCH) + return SMB2_LEASE_HANDLE_CACHING_LE | SMB2_LEASE_READ_CACHING_LE | + SMB2_LEASE_WRITE_CACHING_LE; + return 0; +} + +static char * +smb2_create_lease_buf(u8 *lease_key, u8 oplock) +{ + struct create_lease *buf; + + buf = kzalloc(sizeof(struct create_lease), GFP_KERNEL); + if (!buf) + return NULL; + + memcpy(&buf->lcontext.LeaseKey, lease_key, SMB2_LEASE_KEY_SIZE); + buf->lcontext.LeaseState = map_oplock_to_lease(oplock); + + buf->ccontext.DataOffset = cpu_to_le16(offsetof + (struct create_lease, lcontext)); + buf->ccontext.DataLength = cpu_to_le32(sizeof(struct lease_context)); + buf->ccontext.NameOffset = cpu_to_le16(offsetof + (struct create_lease, Name)); + buf->ccontext.NameLength = cpu_to_le16(4); + /* SMB2_CREATE_REQUEST_LEASE is "RqLs" */ + buf->Name[0] = 'R'; + buf->Name[1] = 'q'; + buf->Name[2] = 'L'; + buf->Name[3] = 's'; + return (char *)buf; +} + +static char * +smb3_create_lease_buf(u8 *lease_key, u8 oplock) +{ + struct create_lease_v2 *buf; + + buf = kzalloc(sizeof(struct create_lease_v2), GFP_KERNEL); + if (!buf) + return NULL; + + memcpy(&buf->lcontext.LeaseKey, lease_key, SMB2_LEASE_KEY_SIZE); + buf->lcontext.LeaseState = map_oplock_to_lease(oplock); + + buf->ccontext.DataOffset = cpu_to_le16(offsetof + (struct create_lease_v2, lcontext)); + buf->ccontext.DataLength = cpu_to_le32(sizeof(struct lease_context_v2)); + buf->ccontext.NameOffset = cpu_to_le16(offsetof + (struct create_lease_v2, Name)); + buf->ccontext.NameLength = cpu_to_le16(4); + /* SMB2_CREATE_REQUEST_LEASE is "RqLs" */ + buf->Name[0] = 'R'; + buf->Name[1] = 'q'; + buf->Name[2] = 'L'; + buf->Name[3] = 's'; + return (char *)buf; +} + +static __u8 +smb2_parse_lease_buf(void *buf, unsigned int *epoch, char *lease_key) +{ + struct create_lease *lc = (struct create_lease *)buf; + + *epoch = 0; /* not used */ + if (lc->lcontext.LeaseFlags & SMB2_LEASE_FLAG_BREAK_IN_PROGRESS_LE) + return SMB2_OPLOCK_LEVEL_NOCHANGE; + return le32_to_cpu(lc->lcontext.LeaseState); +} + +static __u8 +smb3_parse_lease_buf(void *buf, unsigned int *epoch, char *lease_key) +{ + struct create_lease_v2 *lc = (struct create_lease_v2 *)buf; + + *epoch = le16_to_cpu(lc->lcontext.Epoch); + if (lc->lcontext.LeaseFlags & SMB2_LEASE_FLAG_BREAK_IN_PROGRESS_LE) + return SMB2_OPLOCK_LEVEL_NOCHANGE; + if (lease_key) + memcpy(lease_key, &lc->lcontext.LeaseKey, SMB2_LEASE_KEY_SIZE); + return le32_to_cpu(lc->lcontext.LeaseState); +} + +static unsigned int +smb2_wp_retry_size(struct inode *inode) +{ + return min_t(unsigned int, CIFS_SB(inode->i_sb)->ctx->wsize, + SMB2_MAX_BUFFER_SIZE); +} + +static bool +smb2_dir_needs_close(struct cifsFileInfo *cfile) +{ + return !cfile->invalidHandle; +} + +static void +fill_transform_hdr(struct smb2_transform_hdr *tr_hdr, unsigned int orig_len, + struct smb_rqst *old_rq, __le16 cipher_type) +{ + struct smb2_hdr *shdr = + (struct smb2_hdr *)old_rq->rq_iov[0].iov_base; + + memset(tr_hdr, 0, sizeof(struct smb2_transform_hdr)); + tr_hdr->ProtocolId = SMB2_TRANSFORM_PROTO_NUM; + tr_hdr->OriginalMessageSize = cpu_to_le32(orig_len); + tr_hdr->Flags = cpu_to_le16(0x01); + if ((cipher_type == SMB2_ENCRYPTION_AES128_GCM) || + (cipher_type == SMB2_ENCRYPTION_AES256_GCM)) + get_random_bytes(&tr_hdr->Nonce, SMB3_AES_GCM_NONCE); + else + get_random_bytes(&tr_hdr->Nonce, SMB3_AES_CCM_NONCE); + memcpy(&tr_hdr->SessionId, &shdr->SessionId, 8); +} + +static void *smb2_aead_req_alloc(struct crypto_aead *tfm, const struct smb_rqst *rqst, + int num_rqst, const u8 *sig, u8 **iv, + struct aead_request **req, struct scatterlist **sgl, + unsigned int *num_sgs) +{ + unsigned int req_size = sizeof(**req) + crypto_aead_reqsize(tfm); + unsigned int iv_size = crypto_aead_ivsize(tfm); + unsigned int len; + u8 *p; + + *num_sgs = cifs_get_num_sgs(rqst, num_rqst, sig); + + len = iv_size; + len += crypto_aead_alignmask(tfm) & ~(crypto_tfm_ctx_alignment() - 1); + len = ALIGN(len, crypto_tfm_ctx_alignment()); + len += req_size; + len = ALIGN(len, __alignof__(struct scatterlist)); + len += *num_sgs * sizeof(**sgl); + + p = kmalloc(len, GFP_ATOMIC); + if (!p) + return NULL; + + *iv = (u8 *)PTR_ALIGN(p, crypto_aead_alignmask(tfm) + 1); + *req = (struct aead_request *)PTR_ALIGN(*iv + iv_size, + crypto_tfm_ctx_alignment()); + *sgl = (struct scatterlist *)PTR_ALIGN((u8 *)*req + req_size, + __alignof__(struct scatterlist)); + return p; +} + +static void *smb2_get_aead_req(struct crypto_aead *tfm, const struct smb_rqst *rqst, + int num_rqst, const u8 *sig, u8 **iv, + struct aead_request **req, struct scatterlist **sgl) +{ + unsigned int off, len, skip; + struct scatterlist *sg; + unsigned int num_sgs; + unsigned long addr; + int i, j; + void *p; + + p = smb2_aead_req_alloc(tfm, rqst, num_rqst, sig, iv, req, sgl, &num_sgs); + if (!p) + return NULL; + + sg_init_table(*sgl, num_sgs); + sg = *sgl; + + /* Assumes the first rqst has a transform header as the first iov. + * I.e. + * rqst[0].rq_iov[0] is transform header + * rqst[0].rq_iov[1+] data to be encrypted/decrypted + * rqst[1+].rq_iov[0+] data to be encrypted/decrypted + */ + for (i = 0; i < num_rqst; i++) { + /* + * The first rqst has a transform header where the + * first 20 bytes are not part of the encrypted blob. + */ + for (j = 0; j < rqst[i].rq_nvec; j++) { + struct kvec *iov = &rqst[i].rq_iov[j]; + + skip = (i == 0) && (j == 0) ? 20 : 0; + addr = (unsigned long)iov->iov_base + skip; + len = iov->iov_len - skip; + sg = cifs_sg_set_buf(sg, (void *)addr, len); + } + for (j = 0; j < rqst[i].rq_npages; j++) { + rqst_page_get_length(&rqst[i], j, &len, &off); + sg_set_page(sg++, rqst[i].rq_pages[j], len, off); + } + } + cifs_sg_set_buf(sg, sig, SMB2_SIGNATURE_SIZE); + + return p; +} + +static int +smb2_get_enc_key(struct TCP_Server_Info *server, __u64 ses_id, int enc, u8 *key) +{ + struct TCP_Server_Info *pserver; + struct cifs_ses *ses; + u8 *ses_enc_key; + + /* If server is a channel, select the primary channel */ + pserver = CIFS_SERVER_IS_CHAN(server) ? server->primary_server : server; + + spin_lock(&cifs_tcp_ses_lock); + list_for_each_entry(ses, &pserver->smb_ses_list, smb_ses_list) { + if (ses->Suid == ses_id) { + spin_lock(&ses->ses_lock); + ses_enc_key = enc ? ses->smb3encryptionkey : + ses->smb3decryptionkey; + memcpy(key, ses_enc_key, SMB3_ENC_DEC_KEY_SIZE); + spin_unlock(&ses->ses_lock); + spin_unlock(&cifs_tcp_ses_lock); + return 0; + } + } + spin_unlock(&cifs_tcp_ses_lock); + + return -EAGAIN; +} +/* + * Encrypt or decrypt @rqst message. @rqst[0] has the following format: + * iov[0] - transform header (associate data), + * iov[1-N] - SMB2 header and pages - data to encrypt. + * On success return encrypted data in iov[1-N] and pages, leave iov[0] + * untouched. + */ +static int +crypt_message(struct TCP_Server_Info *server, int num_rqst, + struct smb_rqst *rqst, int enc) +{ + struct smb2_transform_hdr *tr_hdr = + (struct smb2_transform_hdr *)rqst[0].rq_iov[0].iov_base; + unsigned int assoc_data_len = sizeof(struct smb2_transform_hdr) - 20; + int rc = 0; + struct scatterlist *sg; + u8 sign[SMB2_SIGNATURE_SIZE] = {}; + u8 key[SMB3_ENC_DEC_KEY_SIZE]; + struct aead_request *req; + u8 *iv; + DECLARE_CRYPTO_WAIT(wait); + struct crypto_aead *tfm; + unsigned int crypt_len = le32_to_cpu(tr_hdr->OriginalMessageSize); + void *creq; + + rc = smb2_get_enc_key(server, le64_to_cpu(tr_hdr->SessionId), enc, key); + if (rc) { + cifs_server_dbg(VFS, "%s: Could not get %scryption key\n", __func__, + enc ? "en" : "de"); + return rc; + } + + rc = smb3_crypto_aead_allocate(server); + if (rc) { + cifs_server_dbg(VFS, "%s: crypto alloc failed\n", __func__); + return rc; + } + + tfm = enc ? server->secmech.enc : server->secmech.dec; + + if ((server->cipher_type == SMB2_ENCRYPTION_AES256_CCM) || + (server->cipher_type == SMB2_ENCRYPTION_AES256_GCM)) + rc = crypto_aead_setkey(tfm, key, SMB3_GCM256_CRYPTKEY_SIZE); + else + rc = crypto_aead_setkey(tfm, key, SMB3_GCM128_CRYPTKEY_SIZE); + + if (rc) { + cifs_server_dbg(VFS, "%s: Failed to set aead key %d\n", __func__, rc); + return rc; + } + + rc = crypto_aead_setauthsize(tfm, SMB2_SIGNATURE_SIZE); + if (rc) { + cifs_server_dbg(VFS, "%s: Failed to set authsize %d\n", __func__, rc); + return rc; + } + + creq = smb2_get_aead_req(tfm, rqst, num_rqst, sign, &iv, &req, &sg); + if (unlikely(!creq)) + return -ENOMEM; + + if (!enc) { + memcpy(sign, &tr_hdr->Signature, SMB2_SIGNATURE_SIZE); + crypt_len += SMB2_SIGNATURE_SIZE; + } + + if ((server->cipher_type == SMB2_ENCRYPTION_AES128_GCM) || + (server->cipher_type == SMB2_ENCRYPTION_AES256_GCM)) + memcpy(iv, (char *)tr_hdr->Nonce, SMB3_AES_GCM_NONCE); + else { + iv[0] = 3; + memcpy(iv + 1, (char *)tr_hdr->Nonce, SMB3_AES_CCM_NONCE); + } + + aead_request_set_tfm(req, tfm); + aead_request_set_crypt(req, sg, sg, crypt_len, iv); + aead_request_set_ad(req, assoc_data_len); + + aead_request_set_callback(req, CRYPTO_TFM_REQ_MAY_BACKLOG, + crypto_req_done, &wait); + + rc = crypto_wait_req(enc ? crypto_aead_encrypt(req) + : crypto_aead_decrypt(req), &wait); + + if (!rc && enc) + memcpy(&tr_hdr->Signature, sign, SMB2_SIGNATURE_SIZE); + + kfree_sensitive(creq); + return rc; +} + +void +smb3_free_compound_rqst(int num_rqst, struct smb_rqst *rqst) +{ + int i, j; + + for (i = 0; i < num_rqst; i++) { + if (rqst[i].rq_pages) { + for (j = rqst[i].rq_npages - 1; j >= 0; j--) + put_page(rqst[i].rq_pages[j]); + kfree(rqst[i].rq_pages); + } + } +} + +/* + * This function will initialize new_rq and encrypt the content. + * The first entry, new_rq[0], only contains a single iov which contains + * a smb2_transform_hdr and is pre-allocated by the caller. + * This function then populates new_rq[1+] with the content from olq_rq[0+]. + * + * The end result is an array of smb_rqst structures where the first structure + * only contains a single iov for the transform header which we then can pass + * to crypt_message(). + * + * new_rq[0].rq_iov[0] : smb2_transform_hdr pre-allocated by the caller + * new_rq[1+].rq_iov[*] == old_rq[0+].rq_iov[*] : SMB2/3 requests + */ +static int +smb3_init_transform_rq(struct TCP_Server_Info *server, int num_rqst, + struct smb_rqst *new_rq, struct smb_rqst *old_rq) +{ + struct page **pages; + struct smb2_transform_hdr *tr_hdr = new_rq[0].rq_iov[0].iov_base; + unsigned int npages; + unsigned int orig_len = 0; + int i, j; + int rc = -ENOMEM; + + for (i = 1; i < num_rqst; i++) { + npages = old_rq[i - 1].rq_npages; + pages = kmalloc_array(npages, sizeof(struct page *), + GFP_KERNEL); + if (!pages) + goto err_free; + + new_rq[i].rq_pages = pages; + new_rq[i].rq_npages = npages; + new_rq[i].rq_offset = old_rq[i - 1].rq_offset; + new_rq[i].rq_pagesz = old_rq[i - 1].rq_pagesz; + new_rq[i].rq_tailsz = old_rq[i - 1].rq_tailsz; + new_rq[i].rq_iov = old_rq[i - 1].rq_iov; + new_rq[i].rq_nvec = old_rq[i - 1].rq_nvec; + + orig_len += smb_rqst_len(server, &old_rq[i - 1]); + + for (j = 0; j < npages; j++) { + pages[j] = alloc_page(GFP_KERNEL|__GFP_HIGHMEM); + if (!pages[j]) + goto err_free; + } + + /* copy pages form the old */ + for (j = 0; j < npages; j++) { + char *dst, *src; + unsigned int offset, len; + + rqst_page_get_length(&new_rq[i], j, &len, &offset); + + dst = (char *) kmap(new_rq[i].rq_pages[j]) + offset; + src = (char *) kmap(old_rq[i - 1].rq_pages[j]) + offset; + + memcpy(dst, src, len); + kunmap(new_rq[i].rq_pages[j]); + kunmap(old_rq[i - 1].rq_pages[j]); + } + } + + /* fill the 1st iov with a transform header */ + fill_transform_hdr(tr_hdr, orig_len, old_rq, server->cipher_type); + + rc = crypt_message(server, num_rqst, new_rq, 1); + cifs_dbg(FYI, "Encrypt message returned %d\n", rc); + if (rc) + goto err_free; + + return rc; + +err_free: + smb3_free_compound_rqst(num_rqst - 1, &new_rq[1]); + return rc; +} + +static int +smb3_is_transform_hdr(void *buf) +{ + struct smb2_transform_hdr *trhdr = buf; + + return trhdr->ProtocolId == SMB2_TRANSFORM_PROTO_NUM; +} + +static int +decrypt_raw_data(struct TCP_Server_Info *server, char *buf, + unsigned int buf_data_size, struct page **pages, + unsigned int npages, unsigned int page_data_size, + bool is_offloaded) +{ + struct kvec iov[2]; + struct smb_rqst rqst = {NULL}; + int rc; + + iov[0].iov_base = buf; + iov[0].iov_len = sizeof(struct smb2_transform_hdr); + iov[1].iov_base = buf + sizeof(struct smb2_transform_hdr); + iov[1].iov_len = buf_data_size; + + rqst.rq_iov = iov; + rqst.rq_nvec = 2; + rqst.rq_pages = pages; + rqst.rq_npages = npages; + rqst.rq_pagesz = PAGE_SIZE; + rqst.rq_tailsz = (page_data_size % PAGE_SIZE) ? : PAGE_SIZE; + + rc = crypt_message(server, 1, &rqst, 0); + cifs_dbg(FYI, "Decrypt message returned %d\n", rc); + + if (rc) + return rc; + + memmove(buf, iov[1].iov_base, buf_data_size); + + if (!is_offloaded) + server->total_read = buf_data_size + page_data_size; + + return rc; +} + +static int +read_data_into_pages(struct TCP_Server_Info *server, struct page **pages, + unsigned int npages, unsigned int len) +{ + int i; + int length; + + for (i = 0; i < npages; i++) { + struct page *page = pages[i]; + size_t n; + + n = len; + if (len >= PAGE_SIZE) { + /* enough data to fill the page */ + n = PAGE_SIZE; + len -= n; + } else { + zero_user(page, len, PAGE_SIZE - len); + len = 0; + } + length = cifs_read_page_from_socket(server, page, 0, n); + if (length < 0) + return length; + server->total_read += length; + } + + return 0; +} + +static int +init_read_bvec(struct page **pages, unsigned int npages, unsigned int data_size, + unsigned int cur_off, struct bio_vec **page_vec) +{ + struct bio_vec *bvec; + int i; + + bvec = kcalloc(npages, sizeof(struct bio_vec), GFP_KERNEL); + if (!bvec) + return -ENOMEM; + + for (i = 0; i < npages; i++) { + bvec[i].bv_page = pages[i]; + bvec[i].bv_offset = (i == 0) ? cur_off : 0; + bvec[i].bv_len = min_t(unsigned int, PAGE_SIZE, data_size); + data_size -= bvec[i].bv_len; + } + + if (data_size != 0) { + cifs_dbg(VFS, "%s: something went wrong\n", __func__); + kfree(bvec); + return -EIO; + } + + *page_vec = bvec; + return 0; +} + +static int +handle_read_data(struct TCP_Server_Info *server, struct mid_q_entry *mid, + char *buf, unsigned int buf_len, struct page **pages, + unsigned int npages, unsigned int page_data_size, + bool is_offloaded) +{ + unsigned int data_offset; + unsigned int data_len; + unsigned int cur_off; + unsigned int cur_page_idx; + unsigned int pad_len; + struct cifs_readdata *rdata = mid->callback_data; + struct smb2_hdr *shdr = (struct smb2_hdr *)buf; + struct bio_vec *bvec = NULL; + struct iov_iter iter; + struct kvec iov; + int length; + bool use_rdma_mr = false; + + if (shdr->Command != SMB2_READ) { + cifs_server_dbg(VFS, "only big read responses are supported\n"); + return -ENOTSUPP; + } + + if (server->ops->is_session_expired && + server->ops->is_session_expired(buf)) { + if (!is_offloaded) + cifs_reconnect(server, true); + return -1; + } + + if (server->ops->is_status_pending && + server->ops->is_status_pending(buf, server)) + return -1; + + /* set up first two iov to get credits */ + rdata->iov[0].iov_base = buf; + rdata->iov[0].iov_len = 0; + rdata->iov[1].iov_base = buf; + rdata->iov[1].iov_len = + min_t(unsigned int, buf_len, server->vals->read_rsp_size); + cifs_dbg(FYI, "0: iov_base=%p iov_len=%zu\n", + rdata->iov[0].iov_base, rdata->iov[0].iov_len); + cifs_dbg(FYI, "1: iov_base=%p iov_len=%zu\n", + rdata->iov[1].iov_base, rdata->iov[1].iov_len); + + rdata->result = server->ops->map_error(buf, true); + if (rdata->result != 0) { + cifs_dbg(FYI, "%s: server returned error %d\n", + __func__, rdata->result); + /* normal error on read response */ + if (is_offloaded) + mid->mid_state = MID_RESPONSE_RECEIVED; + else + dequeue_mid(mid, false); + return 0; + } + + data_offset = server->ops->read_data_offset(buf); +#ifdef CONFIG_CIFS_SMB_DIRECT + use_rdma_mr = rdata->mr; +#endif + data_len = server->ops->read_data_length(buf, use_rdma_mr); + + if (data_offset < server->vals->read_rsp_size) { + /* + * win2k8 sometimes sends an offset of 0 when the read + * is beyond the EOF. Treat it as if the data starts just after + * the header. + */ + cifs_dbg(FYI, "%s: data offset (%u) inside read response header\n", + __func__, data_offset); + data_offset = server->vals->read_rsp_size; + } else if (data_offset > MAX_CIFS_SMALL_BUFFER_SIZE) { + /* data_offset is beyond the end of smallbuf */ + cifs_dbg(FYI, "%s: data offset (%u) beyond end of smallbuf\n", + __func__, data_offset); + rdata->result = -EIO; + if (is_offloaded) + mid->mid_state = MID_RESPONSE_MALFORMED; + else + dequeue_mid(mid, rdata->result); + return 0; + } + + pad_len = data_offset - server->vals->read_rsp_size; + + if (buf_len <= data_offset) { + /* read response payload is in pages */ + cur_page_idx = pad_len / PAGE_SIZE; + cur_off = pad_len % PAGE_SIZE; + + if (cur_page_idx != 0) { + /* data offset is beyond the 1st page of response */ + cifs_dbg(FYI, "%s: data offset (%u) beyond 1st page of response\n", + __func__, data_offset); + rdata->result = -EIO; + if (is_offloaded) + mid->mid_state = MID_RESPONSE_MALFORMED; + else + dequeue_mid(mid, rdata->result); + return 0; + } + + if (data_len > page_data_size - pad_len) { + /* data_len is corrupt -- discard frame */ + rdata->result = -EIO; + if (is_offloaded) + mid->mid_state = MID_RESPONSE_MALFORMED; + else + dequeue_mid(mid, rdata->result); + return 0; + } + + rdata->result = init_read_bvec(pages, npages, page_data_size, + cur_off, &bvec); + if (rdata->result != 0) { + if (is_offloaded) + mid->mid_state = MID_RESPONSE_MALFORMED; + else + dequeue_mid(mid, rdata->result); + return 0; + } + + iov_iter_bvec(&iter, ITER_SOURCE, bvec, npages, data_len); + } else if (buf_len >= data_offset + data_len) { + /* read response payload is in buf */ + WARN_ONCE(npages > 0, "read data can be either in buf or in pages"); + iov.iov_base = buf + data_offset; + iov.iov_len = data_len; + iov_iter_kvec(&iter, ITER_SOURCE, &iov, 1, data_len); + } else { + /* read response payload cannot be in both buf and pages */ + WARN_ONCE(1, "buf can not contain only a part of read data"); + rdata->result = -EIO; + if (is_offloaded) + mid->mid_state = MID_RESPONSE_MALFORMED; + else + dequeue_mid(mid, rdata->result); + return 0; + } + + length = rdata->copy_into_pages(server, rdata, &iter); + + kfree(bvec); + + if (length < 0) + return length; + + if (is_offloaded) + mid->mid_state = MID_RESPONSE_RECEIVED; + else + dequeue_mid(mid, false); + return length; +} + +struct smb2_decrypt_work { + struct work_struct decrypt; + struct TCP_Server_Info *server; + struct page **ppages; + char *buf; + unsigned int npages; + unsigned int len; +}; + + +static void smb2_decrypt_offload(struct work_struct *work) +{ + struct smb2_decrypt_work *dw = container_of(work, + struct smb2_decrypt_work, decrypt); + int i, rc; + struct mid_q_entry *mid; + + rc = decrypt_raw_data(dw->server, dw->buf, dw->server->vals->read_rsp_size, + dw->ppages, dw->npages, dw->len, true); + if (rc) { + cifs_dbg(VFS, "error decrypting rc=%d\n", rc); + goto free_pages; + } + + dw->server->lstrp = jiffies; + mid = smb2_find_dequeue_mid(dw->server, dw->buf); + if (mid == NULL) + cifs_dbg(FYI, "mid not found\n"); + else { + mid->decrypted = true; + rc = handle_read_data(dw->server, mid, dw->buf, + dw->server->vals->read_rsp_size, + dw->ppages, dw->npages, dw->len, + true); + if (rc >= 0) { +#ifdef CONFIG_CIFS_STATS2 + mid->when_received = jiffies; +#endif + if (dw->server->ops->is_network_name_deleted) + dw->server->ops->is_network_name_deleted(dw->buf, + dw->server); + + mid->callback(mid); + } else { + spin_lock(&dw->server->srv_lock); + if (dw->server->tcpStatus == CifsNeedReconnect) { + spin_lock(&dw->server->mid_lock); + mid->mid_state = MID_RETRY_NEEDED; + spin_unlock(&dw->server->mid_lock); + spin_unlock(&dw->server->srv_lock); + mid->callback(mid); + } else { + spin_lock(&dw->server->mid_lock); + mid->mid_state = MID_REQUEST_SUBMITTED; + mid->mid_flags &= ~(MID_DELETED); + list_add_tail(&mid->qhead, + &dw->server->pending_mid_q); + spin_unlock(&dw->server->mid_lock); + spin_unlock(&dw->server->srv_lock); + } + } + release_mid(mid); + } + +free_pages: + for (i = dw->npages-1; i >= 0; i--) + put_page(dw->ppages[i]); + + kfree(dw->ppages); + cifs_small_buf_release(dw->buf); + kfree(dw); +} + + +static int +receive_encrypted_read(struct TCP_Server_Info *server, struct mid_q_entry **mid, + int *num_mids) +{ + char *buf = server->smallbuf; + struct smb2_transform_hdr *tr_hdr = (struct smb2_transform_hdr *)buf; + unsigned int npages; + struct page **pages; + unsigned int len; + unsigned int buflen = server->pdu_size; + int rc; + int i = 0; + struct smb2_decrypt_work *dw; + + *num_mids = 1; + len = min_t(unsigned int, buflen, server->vals->read_rsp_size + + sizeof(struct smb2_transform_hdr)) - HEADER_SIZE(server) + 1; + + rc = cifs_read_from_socket(server, buf + HEADER_SIZE(server) - 1, len); + if (rc < 0) + return rc; + server->total_read += rc; + + len = le32_to_cpu(tr_hdr->OriginalMessageSize) - + server->vals->read_rsp_size; + npages = DIV_ROUND_UP(len, PAGE_SIZE); + + pages = kmalloc_array(npages, sizeof(struct page *), GFP_KERNEL); + if (!pages) { + rc = -ENOMEM; + goto discard_data; + } + + for (; i < npages; i++) { + pages[i] = alloc_page(GFP_KERNEL|__GFP_HIGHMEM); + if (!pages[i]) { + rc = -ENOMEM; + goto discard_data; + } + } + + /* read read data into pages */ + rc = read_data_into_pages(server, pages, npages, len); + if (rc) + goto free_pages; + + rc = cifs_discard_remaining_data(server); + if (rc) + goto free_pages; + + /* + * For large reads, offload to different thread for better performance, + * use more cores decrypting which can be expensive + */ + + if ((server->min_offload) && (server->in_flight > 1) && + (server->pdu_size >= server->min_offload)) { + dw = kmalloc(sizeof(struct smb2_decrypt_work), GFP_KERNEL); + if (dw == NULL) + goto non_offloaded_decrypt; + + dw->buf = server->smallbuf; + server->smallbuf = (char *)cifs_small_buf_get(); + + INIT_WORK(&dw->decrypt, smb2_decrypt_offload); + + dw->npages = npages; + dw->server = server; + dw->ppages = pages; + dw->len = len; + queue_work(decrypt_wq, &dw->decrypt); + *num_mids = 0; /* worker thread takes care of finding mid */ + return -1; + } + +non_offloaded_decrypt: + rc = decrypt_raw_data(server, buf, server->vals->read_rsp_size, + pages, npages, len, false); + if (rc) + goto free_pages; + + *mid = smb2_find_mid(server, buf); + if (*mid == NULL) + cifs_dbg(FYI, "mid not found\n"); + else { + cifs_dbg(FYI, "mid found\n"); + (*mid)->decrypted = true; + rc = handle_read_data(server, *mid, buf, + server->vals->read_rsp_size, + pages, npages, len, false); + if (rc >= 0) { + if (server->ops->is_network_name_deleted) { + server->ops->is_network_name_deleted(buf, + server); + } + } + } + +free_pages: + for (i = i - 1; i >= 0; i--) + put_page(pages[i]); + kfree(pages); + return rc; +discard_data: + cifs_discard_remaining_data(server); + goto free_pages; +} + +static int +receive_encrypted_standard(struct TCP_Server_Info *server, + struct mid_q_entry **mids, char **bufs, + int *num_mids) +{ + int ret, length; + char *buf = server->smallbuf; + struct smb2_hdr *shdr; + unsigned int pdu_length = server->pdu_size; + unsigned int buf_size; + struct mid_q_entry *mid_entry; + int next_is_large; + char *next_buffer = NULL; + + *num_mids = 0; + + /* switch to large buffer if too big for a small one */ + if (pdu_length > MAX_CIFS_SMALL_BUFFER_SIZE) { + server->large_buf = true; + memcpy(server->bigbuf, buf, server->total_read); + buf = server->bigbuf; + } + + /* now read the rest */ + length = cifs_read_from_socket(server, buf + HEADER_SIZE(server) - 1, + pdu_length - HEADER_SIZE(server) + 1); + if (length < 0) + return length; + server->total_read += length; + + buf_size = pdu_length - sizeof(struct smb2_transform_hdr); + length = decrypt_raw_data(server, buf, buf_size, NULL, 0, 0, false); + if (length) + return length; + + next_is_large = server->large_buf; +one_more: + shdr = (struct smb2_hdr *)buf; + if (shdr->NextCommand) { + if (next_is_large) + next_buffer = (char *)cifs_buf_get(); + else + next_buffer = (char *)cifs_small_buf_get(); + memcpy(next_buffer, + buf + le32_to_cpu(shdr->NextCommand), + pdu_length - le32_to_cpu(shdr->NextCommand)); + } + + mid_entry = smb2_find_mid(server, buf); + if (mid_entry == NULL) + cifs_dbg(FYI, "mid not found\n"); + else { + cifs_dbg(FYI, "mid found\n"); + mid_entry->decrypted = true; + mid_entry->resp_buf_size = server->pdu_size; + } + + if (*num_mids >= MAX_COMPOUND) { + cifs_server_dbg(VFS, "too many PDUs in compound\n"); + return -1; + } + bufs[*num_mids] = buf; + mids[(*num_mids)++] = mid_entry; + + if (mid_entry && mid_entry->handle) + ret = mid_entry->handle(server, mid_entry); + else + ret = cifs_handle_standard(server, mid_entry); + + if (ret == 0 && shdr->NextCommand) { + pdu_length -= le32_to_cpu(shdr->NextCommand); + server->large_buf = next_is_large; + if (next_is_large) + server->bigbuf = buf = next_buffer; + else + server->smallbuf = buf = next_buffer; + goto one_more; + } else if (ret != 0) { + /* + * ret != 0 here means that we didn't get to handle_mid() thus + * server->smallbuf and server->bigbuf are still valid. We need + * to free next_buffer because it is not going to be used + * anywhere. + */ + if (next_is_large) + free_rsp_buf(CIFS_LARGE_BUFFER, next_buffer); + else + free_rsp_buf(CIFS_SMALL_BUFFER, next_buffer); + } + + return ret; +} + +static int +smb3_receive_transform(struct TCP_Server_Info *server, + struct mid_q_entry **mids, char **bufs, int *num_mids) +{ + char *buf = server->smallbuf; + unsigned int pdu_length = server->pdu_size; + struct smb2_transform_hdr *tr_hdr = (struct smb2_transform_hdr *)buf; + unsigned int orig_len = le32_to_cpu(tr_hdr->OriginalMessageSize); + + if (pdu_length < sizeof(struct smb2_transform_hdr) + + sizeof(struct smb2_hdr)) { + cifs_server_dbg(VFS, "Transform message is too small (%u)\n", + pdu_length); + cifs_reconnect(server, true); + return -ECONNABORTED; + } + + if (pdu_length < orig_len + sizeof(struct smb2_transform_hdr)) { + cifs_server_dbg(VFS, "Transform message is broken\n"); + cifs_reconnect(server, true); + return -ECONNABORTED; + } + + /* TODO: add support for compounds containing READ. */ + if (pdu_length > CIFSMaxBufSize + MAX_HEADER_SIZE(server)) { + return receive_encrypted_read(server, &mids[0], num_mids); + } + + return receive_encrypted_standard(server, mids, bufs, num_mids); +} + +int +smb3_handle_read_data(struct TCP_Server_Info *server, struct mid_q_entry *mid) +{ + char *buf = server->large_buf ? server->bigbuf : server->smallbuf; + + return handle_read_data(server, mid, buf, server->pdu_size, + NULL, 0, 0, false); +} + +static int +smb2_next_header(char *buf) +{ + struct smb2_hdr *hdr = (struct smb2_hdr *)buf; + struct smb2_transform_hdr *t_hdr = (struct smb2_transform_hdr *)buf; + + if (hdr->ProtocolId == SMB2_TRANSFORM_PROTO_NUM) + return sizeof(struct smb2_transform_hdr) + + le32_to_cpu(t_hdr->OriginalMessageSize); + + return le32_to_cpu(hdr->NextCommand); +} + +static int +smb2_make_node(unsigned int xid, struct inode *inode, + struct dentry *dentry, struct cifs_tcon *tcon, + const char *full_path, umode_t mode, dev_t dev) +{ + struct cifs_sb_info *cifs_sb = CIFS_SB(inode->i_sb); + int rc = -EPERM; + struct cifs_open_info_data buf = {}; + struct cifs_io_parms io_parms = {0}; + __u32 oplock = 0; + struct cifs_fid fid; + struct cifs_open_parms oparms; + unsigned int bytes_written; + struct win_dev *pdev; + struct kvec iov[2]; + + /* + * Check if mounted with mount parm 'sfu' mount parm. + * SFU emulation should work with all servers, but only + * supports block and char device (no socket & fifo), + * and was used by default in earlier versions of Windows + */ + if (!(cifs_sb->mnt_cifs_flags & CIFS_MOUNT_UNX_EMUL)) + return rc; + + /* + * TODO: Add ability to create instead via reparse point. Windows (e.g. + * their current NFS server) uses this approach to expose special files + * over SMB2/SMB3 and Samba will do this with SMB3.1.1 POSIX Extensions + */ + + if (!S_ISCHR(mode) && !S_ISBLK(mode)) + return rc; + + cifs_dbg(FYI, "sfu compat create special file\n"); + + oparms = (struct cifs_open_parms) { + .tcon = tcon, + .cifs_sb = cifs_sb, + .desired_access = GENERIC_WRITE, + .create_options = cifs_create_options(cifs_sb, CREATE_NOT_DIR | + CREATE_OPTION_SPECIAL), + .disposition = FILE_CREATE, + .path = full_path, + .fid = &fid, + }; + + if (tcon->ses->server->oplocks) + oplock = REQ_OPLOCK; + else + oplock = 0; + rc = tcon->ses->server->ops->open(xid, &oparms, &oplock, &buf); + if (rc) + return rc; + + /* + * BB Do not bother to decode buf since no local inode yet to put + * timestamps in, but we can reuse it safely. + */ + + pdev = (struct win_dev *)&buf.fi; + io_parms.pid = current->tgid; + io_parms.tcon = tcon; + io_parms.offset = 0; + io_parms.length = sizeof(struct win_dev); + iov[1].iov_base = &buf.fi; + iov[1].iov_len = sizeof(struct win_dev); + if (S_ISCHR(mode)) { + memcpy(pdev->type, "IntxCHR", 8); + pdev->major = cpu_to_le64(MAJOR(dev)); + pdev->minor = cpu_to_le64(MINOR(dev)); + rc = tcon->ses->server->ops->sync_write(xid, &fid, &io_parms, + &bytes_written, iov, 1); + } else if (S_ISBLK(mode)) { + memcpy(pdev->type, "IntxBLK", 8); + pdev->major = cpu_to_le64(MAJOR(dev)); + pdev->minor = cpu_to_le64(MINOR(dev)); + rc = tcon->ses->server->ops->sync_write(xid, &fid, &io_parms, + &bytes_written, iov, 1); + } + tcon->ses->server->ops->close(xid, tcon, &fid); + d_drop(dentry); + + /* FIXME: add code here to set EAs */ + + cifs_free_open_info(&buf); + return rc; +} + +#ifdef CONFIG_CIFS_ALLOW_INSECURE_LEGACY +struct smb_version_operations smb20_operations = { + .compare_fids = smb2_compare_fids, + .setup_request = smb2_setup_request, + .setup_async_request = smb2_setup_async_request, + .check_receive = smb2_check_receive, + .add_credits = smb2_add_credits, + .set_credits = smb2_set_credits, + .get_credits_field = smb2_get_credits_field, + .get_credits = smb2_get_credits, + .wait_mtu_credits = cifs_wait_mtu_credits, + .get_next_mid = smb2_get_next_mid, + .revert_current_mid = smb2_revert_current_mid, + .read_data_offset = smb2_read_data_offset, + .read_data_length = smb2_read_data_length, + .map_error = map_smb2_to_linux_error, + .find_mid = smb2_find_mid, + .check_message = smb2_check_message, + .dump_detail = smb2_dump_detail, + .clear_stats = smb2_clear_stats, + .print_stats = smb2_print_stats, + .is_oplock_break = smb2_is_valid_oplock_break, + .handle_cancelled_mid = smb2_handle_cancelled_mid, + .downgrade_oplock = smb2_downgrade_oplock, + .need_neg = smb2_need_neg, + .negotiate = smb2_negotiate, + .negotiate_wsize = smb2_negotiate_wsize, + .negotiate_rsize = smb2_negotiate_rsize, + .sess_setup = SMB2_sess_setup, + .logoff = SMB2_logoff, + .tree_connect = SMB2_tcon, + .tree_disconnect = SMB2_tdis, + .qfs_tcon = smb2_qfs_tcon, + .is_path_accessible = smb2_is_path_accessible, + .can_echo = smb2_can_echo, + .echo = SMB2_echo, + .query_path_info = smb2_query_path_info, + .get_srv_inum = smb2_get_srv_inum, + .query_file_info = smb2_query_file_info, + .set_path_size = smb2_set_path_size, + .set_file_size = smb2_set_file_size, + .set_file_info = smb2_set_file_info, + .set_compression = smb2_set_compression, + .mkdir = smb2_mkdir, + .mkdir_setinfo = smb2_mkdir_setinfo, + .rmdir = smb2_rmdir, + .unlink = smb2_unlink, + .rename = smb2_rename_path, + .create_hardlink = smb2_create_hardlink, + .query_symlink = smb2_query_symlink, + .query_mf_symlink = smb3_query_mf_symlink, + .create_mf_symlink = smb3_create_mf_symlink, + .open = smb2_open_file, + .set_fid = smb2_set_fid, + .close = smb2_close_file, + .flush = smb2_flush_file, + .async_readv = smb2_async_readv, + .async_writev = smb2_async_writev, + .sync_read = smb2_sync_read, + .sync_write = smb2_sync_write, + .query_dir_first = smb2_query_dir_first, + .query_dir_next = smb2_query_dir_next, + .close_dir = smb2_close_dir, + .calc_smb_size = smb2_calc_size, + .is_status_pending = smb2_is_status_pending, + .is_session_expired = smb2_is_session_expired, + .oplock_response = smb2_oplock_response, + .queryfs = smb2_queryfs, + .mand_lock = smb2_mand_lock, + .mand_unlock_range = smb2_unlock_range, + .push_mand_locks = smb2_push_mandatory_locks, + .get_lease_key = smb2_get_lease_key, + .set_lease_key = smb2_set_lease_key, + .new_lease_key = smb2_new_lease_key, + .calc_signature = smb2_calc_signature, + .is_read_op = smb2_is_read_op, + .set_oplock_level = smb2_set_oplock_level, + .create_lease_buf = smb2_create_lease_buf, + .parse_lease_buf = smb2_parse_lease_buf, + .copychunk_range = smb2_copychunk_range, + .wp_retry_size = smb2_wp_retry_size, + .dir_needs_close = smb2_dir_needs_close, + .get_dfs_refer = smb2_get_dfs_refer, + .select_sectype = smb2_select_sectype, +#ifdef CONFIG_CIFS_XATTR + .query_all_EAs = smb2_query_eas, + .set_EA = smb2_set_ea, +#endif /* CIFS_XATTR */ + .get_acl = get_smb2_acl, + .get_acl_by_fid = get_smb2_acl_by_fid, + .set_acl = set_smb2_acl, + .next_header = smb2_next_header, + .ioctl_query_info = smb2_ioctl_query_info, + .make_node = smb2_make_node, + .fiemap = smb3_fiemap, + .llseek = smb3_llseek, + .is_status_io_timeout = smb2_is_status_io_timeout, + .is_network_name_deleted = smb2_is_network_name_deleted, +}; +#endif /* CIFS_ALLOW_INSECURE_LEGACY */ + +struct smb_version_operations smb21_operations = { + .compare_fids = smb2_compare_fids, + .setup_request = smb2_setup_request, + .setup_async_request = smb2_setup_async_request, + .check_receive = smb2_check_receive, + .add_credits = smb2_add_credits, + .set_credits = smb2_set_credits, + .get_credits_field = smb2_get_credits_field, + .get_credits = smb2_get_credits, + .wait_mtu_credits = smb2_wait_mtu_credits, + .adjust_credits = smb2_adjust_credits, + .get_next_mid = smb2_get_next_mid, + .revert_current_mid = smb2_revert_current_mid, + .read_data_offset = smb2_read_data_offset, + .read_data_length = smb2_read_data_length, + .map_error = map_smb2_to_linux_error, + .find_mid = smb2_find_mid, + .check_message = smb2_check_message, + .dump_detail = smb2_dump_detail, + .clear_stats = smb2_clear_stats, + .print_stats = smb2_print_stats, + .is_oplock_break = smb2_is_valid_oplock_break, + .handle_cancelled_mid = smb2_handle_cancelled_mid, + .downgrade_oplock = smb2_downgrade_oplock, + .need_neg = smb2_need_neg, + .negotiate = smb2_negotiate, + .negotiate_wsize = smb2_negotiate_wsize, + .negotiate_rsize = smb2_negotiate_rsize, + .sess_setup = SMB2_sess_setup, + .logoff = SMB2_logoff, + .tree_connect = SMB2_tcon, + .tree_disconnect = SMB2_tdis, + .qfs_tcon = smb2_qfs_tcon, + .is_path_accessible = smb2_is_path_accessible, + .can_echo = smb2_can_echo, + .echo = SMB2_echo, + .query_path_info = smb2_query_path_info, + .get_srv_inum = smb2_get_srv_inum, + .query_file_info = smb2_query_file_info, + .set_path_size = smb2_set_path_size, + .set_file_size = smb2_set_file_size, + .set_file_info = smb2_set_file_info, + .set_compression = smb2_set_compression, + .mkdir = smb2_mkdir, + .mkdir_setinfo = smb2_mkdir_setinfo, + .rmdir = smb2_rmdir, + .unlink = smb2_unlink, + .rename = smb2_rename_path, + .create_hardlink = smb2_create_hardlink, + .query_symlink = smb2_query_symlink, + .query_mf_symlink = smb3_query_mf_symlink, + .create_mf_symlink = smb3_create_mf_symlink, + .open = smb2_open_file, + .set_fid = smb2_set_fid, + .close = smb2_close_file, + .flush = smb2_flush_file, + .async_readv = smb2_async_readv, + .async_writev = smb2_async_writev, + .sync_read = smb2_sync_read, + .sync_write = smb2_sync_write, + .query_dir_first = smb2_query_dir_first, + .query_dir_next = smb2_query_dir_next, + .close_dir = smb2_close_dir, + .calc_smb_size = smb2_calc_size, + .is_status_pending = smb2_is_status_pending, + .is_session_expired = smb2_is_session_expired, + .oplock_response = smb2_oplock_response, + .queryfs = smb2_queryfs, + .mand_lock = smb2_mand_lock, + .mand_unlock_range = smb2_unlock_range, + .push_mand_locks = smb2_push_mandatory_locks, + .get_lease_key = smb2_get_lease_key, + .set_lease_key = smb2_set_lease_key, + .new_lease_key = smb2_new_lease_key, + .calc_signature = smb2_calc_signature, + .is_read_op = smb21_is_read_op, + .set_oplock_level = smb21_set_oplock_level, + .create_lease_buf = smb2_create_lease_buf, + .parse_lease_buf = smb2_parse_lease_buf, + .copychunk_range = smb2_copychunk_range, + .wp_retry_size = smb2_wp_retry_size, + .dir_needs_close = smb2_dir_needs_close, + .enum_snapshots = smb3_enum_snapshots, + .notify = smb3_notify, + .get_dfs_refer = smb2_get_dfs_refer, + .select_sectype = smb2_select_sectype, +#ifdef CONFIG_CIFS_XATTR + .query_all_EAs = smb2_query_eas, + .set_EA = smb2_set_ea, +#endif /* CIFS_XATTR */ + .get_acl = get_smb2_acl, + .get_acl_by_fid = get_smb2_acl_by_fid, + .set_acl = set_smb2_acl, + .next_header = smb2_next_header, + .ioctl_query_info = smb2_ioctl_query_info, + .make_node = smb2_make_node, + .fiemap = smb3_fiemap, + .llseek = smb3_llseek, + .is_status_io_timeout = smb2_is_status_io_timeout, + .is_network_name_deleted = smb2_is_network_name_deleted, +}; + +struct smb_version_operations smb30_operations = { + .compare_fids = smb2_compare_fids, + .setup_request = smb2_setup_request, + .setup_async_request = smb2_setup_async_request, + .check_receive = smb2_check_receive, + .add_credits = smb2_add_credits, + .set_credits = smb2_set_credits, + .get_credits_field = smb2_get_credits_field, + .get_credits = smb2_get_credits, + .wait_mtu_credits = smb2_wait_mtu_credits, + .adjust_credits = smb2_adjust_credits, + .get_next_mid = smb2_get_next_mid, + .revert_current_mid = smb2_revert_current_mid, + .read_data_offset = smb2_read_data_offset, + .read_data_length = smb2_read_data_length, + .map_error = map_smb2_to_linux_error, + .find_mid = smb2_find_mid, + .check_message = smb2_check_message, + .dump_detail = smb2_dump_detail, + .clear_stats = smb2_clear_stats, + .print_stats = smb2_print_stats, + .dump_share_caps = smb2_dump_share_caps, + .is_oplock_break = smb2_is_valid_oplock_break, + .handle_cancelled_mid = smb2_handle_cancelled_mid, + .downgrade_oplock = smb3_downgrade_oplock, + .need_neg = smb2_need_neg, + .negotiate = smb2_negotiate, + .negotiate_wsize = smb3_negotiate_wsize, + .negotiate_rsize = smb3_negotiate_rsize, + .sess_setup = SMB2_sess_setup, + .logoff = SMB2_logoff, + .tree_connect = SMB2_tcon, + .tree_disconnect = SMB2_tdis, + .qfs_tcon = smb3_qfs_tcon, + .is_path_accessible = smb2_is_path_accessible, + .can_echo = smb2_can_echo, + .echo = SMB2_echo, + .query_path_info = smb2_query_path_info, + /* WSL tags introduced long after smb2.1, enable for SMB3, 3.11 only */ + .query_reparse_tag = smb2_query_reparse_tag, + .get_srv_inum = smb2_get_srv_inum, + .query_file_info = smb2_query_file_info, + .set_path_size = smb2_set_path_size, + .set_file_size = smb2_set_file_size, + .set_file_info = smb2_set_file_info, + .set_compression = smb2_set_compression, + .mkdir = smb2_mkdir, + .mkdir_setinfo = smb2_mkdir_setinfo, + .rmdir = smb2_rmdir, + .unlink = smb2_unlink, + .rename = smb2_rename_path, + .create_hardlink = smb2_create_hardlink, + .query_symlink = smb2_query_symlink, + .query_mf_symlink = smb3_query_mf_symlink, + .create_mf_symlink = smb3_create_mf_symlink, + .open = smb2_open_file, + .set_fid = smb2_set_fid, + .close = smb2_close_file, + .close_getattr = smb2_close_getattr, + .flush = smb2_flush_file, + .async_readv = smb2_async_readv, + .async_writev = smb2_async_writev, + .sync_read = smb2_sync_read, + .sync_write = smb2_sync_write, + .query_dir_first = smb2_query_dir_first, + .query_dir_next = smb2_query_dir_next, + .close_dir = smb2_close_dir, + .calc_smb_size = smb2_calc_size, + .is_status_pending = smb2_is_status_pending, + .is_session_expired = smb2_is_session_expired, + .oplock_response = smb2_oplock_response, + .queryfs = smb2_queryfs, + .mand_lock = smb2_mand_lock, + .mand_unlock_range = smb2_unlock_range, + .push_mand_locks = smb2_push_mandatory_locks, + .get_lease_key = smb2_get_lease_key, + .set_lease_key = smb2_set_lease_key, + .new_lease_key = smb2_new_lease_key, + .generate_signingkey = generate_smb30signingkey, + .calc_signature = smb3_calc_signature, + .set_integrity = smb3_set_integrity, + .is_read_op = smb21_is_read_op, + .set_oplock_level = smb3_set_oplock_level, + .create_lease_buf = smb3_create_lease_buf, + .parse_lease_buf = smb3_parse_lease_buf, + .copychunk_range = smb2_copychunk_range, + .duplicate_extents = smb2_duplicate_extents, + .validate_negotiate = smb3_validate_negotiate, + .wp_retry_size = smb2_wp_retry_size, + .dir_needs_close = smb2_dir_needs_close, + .fallocate = smb3_fallocate, + .enum_snapshots = smb3_enum_snapshots, + .notify = smb3_notify, + .init_transform_rq = smb3_init_transform_rq, + .is_transform_hdr = smb3_is_transform_hdr, + .receive_transform = smb3_receive_transform, + .get_dfs_refer = smb2_get_dfs_refer, + .select_sectype = smb2_select_sectype, +#ifdef CONFIG_CIFS_XATTR + .query_all_EAs = smb2_query_eas, + .set_EA = smb2_set_ea, +#endif /* CIFS_XATTR */ + .get_acl = get_smb2_acl, + .get_acl_by_fid = get_smb2_acl_by_fid, + .set_acl = set_smb2_acl, + .next_header = smb2_next_header, + .ioctl_query_info = smb2_ioctl_query_info, + .make_node = smb2_make_node, + .fiemap = smb3_fiemap, + .llseek = smb3_llseek, + .is_status_io_timeout = smb2_is_status_io_timeout, + .is_network_name_deleted = smb2_is_network_name_deleted, +}; + +struct smb_version_operations smb311_operations = { + .compare_fids = smb2_compare_fids, + .setup_request = smb2_setup_request, + .setup_async_request = smb2_setup_async_request, + .check_receive = smb2_check_receive, + .add_credits = smb2_add_credits, + .set_credits = smb2_set_credits, + .get_credits_field = smb2_get_credits_field, + .get_credits = smb2_get_credits, + .wait_mtu_credits = smb2_wait_mtu_credits, + .adjust_credits = smb2_adjust_credits, + .get_next_mid = smb2_get_next_mid, + .revert_current_mid = smb2_revert_current_mid, + .read_data_offset = smb2_read_data_offset, + .read_data_length = smb2_read_data_length, + .map_error = map_smb2_to_linux_error, + .find_mid = smb2_find_mid, + .check_message = smb2_check_message, + .dump_detail = smb2_dump_detail, + .clear_stats = smb2_clear_stats, + .print_stats = smb2_print_stats, + .dump_share_caps = smb2_dump_share_caps, + .is_oplock_break = smb2_is_valid_oplock_break, + .handle_cancelled_mid = smb2_handle_cancelled_mid, + .downgrade_oplock = smb3_downgrade_oplock, + .need_neg = smb2_need_neg, + .negotiate = smb2_negotiate, + .negotiate_wsize = smb3_negotiate_wsize, + .negotiate_rsize = smb3_negotiate_rsize, + .sess_setup = SMB2_sess_setup, + .logoff = SMB2_logoff, + .tree_connect = SMB2_tcon, + .tree_disconnect = SMB2_tdis, + .qfs_tcon = smb3_qfs_tcon, + .is_path_accessible = smb2_is_path_accessible, + .can_echo = smb2_can_echo, + .echo = SMB2_echo, + .query_path_info = smb2_query_path_info, + .query_reparse_tag = smb2_query_reparse_tag, + .get_srv_inum = smb2_get_srv_inum, + .query_file_info = smb2_query_file_info, + .set_path_size = smb2_set_path_size, + .set_file_size = smb2_set_file_size, + .set_file_info = smb2_set_file_info, + .set_compression = smb2_set_compression, + .mkdir = smb2_mkdir, + .mkdir_setinfo = smb2_mkdir_setinfo, + .posix_mkdir = smb311_posix_mkdir, + .rmdir = smb2_rmdir, + .unlink = smb2_unlink, + .rename = smb2_rename_path, + .create_hardlink = smb2_create_hardlink, + .query_symlink = smb2_query_symlink, + .query_mf_symlink = smb3_query_mf_symlink, + .create_mf_symlink = smb3_create_mf_symlink, + .open = smb2_open_file, + .set_fid = smb2_set_fid, + .close = smb2_close_file, + .close_getattr = smb2_close_getattr, + .flush = smb2_flush_file, + .async_readv = smb2_async_readv, + .async_writev = smb2_async_writev, + .sync_read = smb2_sync_read, + .sync_write = smb2_sync_write, + .query_dir_first = smb2_query_dir_first, + .query_dir_next = smb2_query_dir_next, + .close_dir = smb2_close_dir, + .calc_smb_size = smb2_calc_size, + .is_status_pending = smb2_is_status_pending, + .is_session_expired = smb2_is_session_expired, + .oplock_response = smb2_oplock_response, + .queryfs = smb311_queryfs, + .mand_lock = smb2_mand_lock, + .mand_unlock_range = smb2_unlock_range, + .push_mand_locks = smb2_push_mandatory_locks, + .get_lease_key = smb2_get_lease_key, + .set_lease_key = smb2_set_lease_key, + .new_lease_key = smb2_new_lease_key, + .generate_signingkey = generate_smb311signingkey, + .calc_signature = smb3_calc_signature, + .set_integrity = smb3_set_integrity, + .is_read_op = smb21_is_read_op, + .set_oplock_level = smb3_set_oplock_level, + .create_lease_buf = smb3_create_lease_buf, + .parse_lease_buf = smb3_parse_lease_buf, + .copychunk_range = smb2_copychunk_range, + .duplicate_extents = smb2_duplicate_extents, +/* .validate_negotiate = smb3_validate_negotiate, */ /* not used in 3.11 */ + .wp_retry_size = smb2_wp_retry_size, + .dir_needs_close = smb2_dir_needs_close, + .fallocate = smb3_fallocate, + .enum_snapshots = smb3_enum_snapshots, + .notify = smb3_notify, + .init_transform_rq = smb3_init_transform_rq, + .is_transform_hdr = smb3_is_transform_hdr, + .receive_transform = smb3_receive_transform, + .get_dfs_refer = smb2_get_dfs_refer, + .select_sectype = smb2_select_sectype, +#ifdef CONFIG_CIFS_XATTR + .query_all_EAs = smb2_query_eas, + .set_EA = smb2_set_ea, +#endif /* CIFS_XATTR */ + .get_acl = get_smb2_acl, + .get_acl_by_fid = get_smb2_acl_by_fid, + .set_acl = set_smb2_acl, + .next_header = smb2_next_header, + .ioctl_query_info = smb2_ioctl_query_info, + .make_node = smb2_make_node, + .fiemap = smb3_fiemap, + .llseek = smb3_llseek, + .is_status_io_timeout = smb2_is_status_io_timeout, + .is_network_name_deleted = smb2_is_network_name_deleted, +}; + +#ifdef CONFIG_CIFS_ALLOW_INSECURE_LEGACY +struct smb_version_values smb20_values = { + .version_string = SMB20_VERSION_STRING, + .protocol_id = SMB20_PROT_ID, + .req_capabilities = 0, /* MBZ */ + .large_lock_type = 0, + .exclusive_lock_type = SMB2_LOCKFLAG_EXCLUSIVE, + .shared_lock_type = SMB2_LOCKFLAG_SHARED, + .unlock_lock_type = SMB2_LOCKFLAG_UNLOCK, + .header_size = sizeof(struct smb2_hdr), + .header_preamble_size = 0, + .max_header_size = MAX_SMB2_HDR_SIZE, + .read_rsp_size = sizeof(struct smb2_read_rsp) - 1, + .lock_cmd = SMB2_LOCK, + .cap_unix = 0, + .cap_nt_find = SMB2_NT_FIND, + .cap_large_files = SMB2_LARGE_FILES, + .signing_enabled = SMB2_NEGOTIATE_SIGNING_ENABLED | SMB2_NEGOTIATE_SIGNING_REQUIRED, + .signing_required = SMB2_NEGOTIATE_SIGNING_REQUIRED, + .create_lease_size = sizeof(struct create_lease), +}; +#endif /* ALLOW_INSECURE_LEGACY */ + +struct smb_version_values smb21_values = { + .version_string = SMB21_VERSION_STRING, + .protocol_id = SMB21_PROT_ID, + .req_capabilities = 0, /* MBZ on negotiate req until SMB3 dialect */ + .large_lock_type = 0, + .exclusive_lock_type = SMB2_LOCKFLAG_EXCLUSIVE, + .shared_lock_type = SMB2_LOCKFLAG_SHARED, + .unlock_lock_type = SMB2_LOCKFLAG_UNLOCK, + .header_size = sizeof(struct smb2_hdr), + .header_preamble_size = 0, + .max_header_size = MAX_SMB2_HDR_SIZE, + .read_rsp_size = sizeof(struct smb2_read_rsp) - 1, + .lock_cmd = SMB2_LOCK, + .cap_unix = 0, + .cap_nt_find = SMB2_NT_FIND, + .cap_large_files = SMB2_LARGE_FILES, + .signing_enabled = SMB2_NEGOTIATE_SIGNING_ENABLED | SMB2_NEGOTIATE_SIGNING_REQUIRED, + .signing_required = SMB2_NEGOTIATE_SIGNING_REQUIRED, + .create_lease_size = sizeof(struct create_lease), +}; + +struct smb_version_values smb3any_values = { + .version_string = SMB3ANY_VERSION_STRING, + .protocol_id = SMB302_PROT_ID, /* doesn't matter, send protocol array */ + .req_capabilities = SMB2_GLOBAL_CAP_DFS | SMB2_GLOBAL_CAP_LEASING | SMB2_GLOBAL_CAP_LARGE_MTU | SMB2_GLOBAL_CAP_PERSISTENT_HANDLES | SMB2_GLOBAL_CAP_ENCRYPTION | SMB2_GLOBAL_CAP_DIRECTORY_LEASING, + .large_lock_type = 0, + .exclusive_lock_type = SMB2_LOCKFLAG_EXCLUSIVE, + .shared_lock_type = SMB2_LOCKFLAG_SHARED, + .unlock_lock_type = SMB2_LOCKFLAG_UNLOCK, + .header_size = sizeof(struct smb2_hdr), + .header_preamble_size = 0, + .max_header_size = MAX_SMB2_HDR_SIZE, + .read_rsp_size = sizeof(struct smb2_read_rsp) - 1, + .lock_cmd = SMB2_LOCK, + .cap_unix = 0, + .cap_nt_find = SMB2_NT_FIND, + .cap_large_files = SMB2_LARGE_FILES, + .signing_enabled = SMB2_NEGOTIATE_SIGNING_ENABLED | SMB2_NEGOTIATE_SIGNING_REQUIRED, + .signing_required = SMB2_NEGOTIATE_SIGNING_REQUIRED, + .create_lease_size = sizeof(struct create_lease_v2), +}; + +struct smb_version_values smbdefault_values = { + .version_string = SMBDEFAULT_VERSION_STRING, + .protocol_id = SMB302_PROT_ID, /* doesn't matter, send protocol array */ + .req_capabilities = SMB2_GLOBAL_CAP_DFS | SMB2_GLOBAL_CAP_LEASING | SMB2_GLOBAL_CAP_LARGE_MTU | SMB2_GLOBAL_CAP_PERSISTENT_HANDLES | SMB2_GLOBAL_CAP_ENCRYPTION | SMB2_GLOBAL_CAP_DIRECTORY_LEASING, + .large_lock_type = 0, + .exclusive_lock_type = SMB2_LOCKFLAG_EXCLUSIVE, + .shared_lock_type = SMB2_LOCKFLAG_SHARED, + .unlock_lock_type = SMB2_LOCKFLAG_UNLOCK, + .header_size = sizeof(struct smb2_hdr), + .header_preamble_size = 0, + .max_header_size = MAX_SMB2_HDR_SIZE, + .read_rsp_size = sizeof(struct smb2_read_rsp) - 1, + .lock_cmd = SMB2_LOCK, + .cap_unix = 0, + .cap_nt_find = SMB2_NT_FIND, + .cap_large_files = SMB2_LARGE_FILES, + .signing_enabled = SMB2_NEGOTIATE_SIGNING_ENABLED | SMB2_NEGOTIATE_SIGNING_REQUIRED, + .signing_required = SMB2_NEGOTIATE_SIGNING_REQUIRED, + .create_lease_size = sizeof(struct create_lease_v2), +}; + +struct smb_version_values smb30_values = { + .version_string = SMB30_VERSION_STRING, + .protocol_id = SMB30_PROT_ID, + .req_capabilities = SMB2_GLOBAL_CAP_DFS | SMB2_GLOBAL_CAP_LEASING | SMB2_GLOBAL_CAP_LARGE_MTU | SMB2_GLOBAL_CAP_PERSISTENT_HANDLES | SMB2_GLOBAL_CAP_ENCRYPTION | SMB2_GLOBAL_CAP_DIRECTORY_LEASING, + .large_lock_type = 0, + .exclusive_lock_type = SMB2_LOCKFLAG_EXCLUSIVE, + .shared_lock_type = SMB2_LOCKFLAG_SHARED, + .unlock_lock_type = SMB2_LOCKFLAG_UNLOCK, + .header_size = sizeof(struct smb2_hdr), + .header_preamble_size = 0, + .max_header_size = MAX_SMB2_HDR_SIZE, + .read_rsp_size = sizeof(struct smb2_read_rsp) - 1, + .lock_cmd = SMB2_LOCK, + .cap_unix = 0, + .cap_nt_find = SMB2_NT_FIND, + .cap_large_files = SMB2_LARGE_FILES, + .signing_enabled = SMB2_NEGOTIATE_SIGNING_ENABLED | SMB2_NEGOTIATE_SIGNING_REQUIRED, + .signing_required = SMB2_NEGOTIATE_SIGNING_REQUIRED, + .create_lease_size = sizeof(struct create_lease_v2), +}; + +struct smb_version_values smb302_values = { + .version_string = SMB302_VERSION_STRING, + .protocol_id = SMB302_PROT_ID, + .req_capabilities = SMB2_GLOBAL_CAP_DFS | SMB2_GLOBAL_CAP_LEASING | SMB2_GLOBAL_CAP_LARGE_MTU | SMB2_GLOBAL_CAP_PERSISTENT_HANDLES | SMB2_GLOBAL_CAP_ENCRYPTION | SMB2_GLOBAL_CAP_DIRECTORY_LEASING, + .large_lock_type = 0, + .exclusive_lock_type = SMB2_LOCKFLAG_EXCLUSIVE, + .shared_lock_type = SMB2_LOCKFLAG_SHARED, + .unlock_lock_type = SMB2_LOCKFLAG_UNLOCK, + .header_size = sizeof(struct smb2_hdr), + .header_preamble_size = 0, + .max_header_size = MAX_SMB2_HDR_SIZE, + .read_rsp_size = sizeof(struct smb2_read_rsp) - 1, + .lock_cmd = SMB2_LOCK, + .cap_unix = 0, + .cap_nt_find = SMB2_NT_FIND, + .cap_large_files = SMB2_LARGE_FILES, + .signing_enabled = SMB2_NEGOTIATE_SIGNING_ENABLED | SMB2_NEGOTIATE_SIGNING_REQUIRED, + .signing_required = SMB2_NEGOTIATE_SIGNING_REQUIRED, + .create_lease_size = sizeof(struct create_lease_v2), +}; + +struct smb_version_values smb311_values = { + .version_string = SMB311_VERSION_STRING, + .protocol_id = SMB311_PROT_ID, + .req_capabilities = SMB2_GLOBAL_CAP_DFS | SMB2_GLOBAL_CAP_LEASING | SMB2_GLOBAL_CAP_LARGE_MTU | SMB2_GLOBAL_CAP_PERSISTENT_HANDLES | SMB2_GLOBAL_CAP_ENCRYPTION | SMB2_GLOBAL_CAP_DIRECTORY_LEASING, + .large_lock_type = 0, + .exclusive_lock_type = SMB2_LOCKFLAG_EXCLUSIVE, + .shared_lock_type = SMB2_LOCKFLAG_SHARED, + .unlock_lock_type = SMB2_LOCKFLAG_UNLOCK, + .header_size = sizeof(struct smb2_hdr), + .header_preamble_size = 0, + .max_header_size = MAX_SMB2_HDR_SIZE, + .read_rsp_size = sizeof(struct smb2_read_rsp) - 1, + .lock_cmd = SMB2_LOCK, + .cap_unix = 0, + .cap_nt_find = SMB2_NT_FIND, + .cap_large_files = SMB2_LARGE_FILES, + .signing_enabled = SMB2_NEGOTIATE_SIGNING_ENABLED | SMB2_NEGOTIATE_SIGNING_REQUIRED, + .signing_required = SMB2_NEGOTIATE_SIGNING_REQUIRED, + .create_lease_size = sizeof(struct create_lease_v2), +}; diff --git a/fs/smb/client/smb2pdu.c b/fs/smb/client/smb2pdu.c new file mode 100644 index 0000000000000..3ca593cdda76e --- /dev/null +++ b/fs/smb/client/smb2pdu.c @@ -0,0 +1,5722 @@ +// SPDX-License-Identifier: LGPL-2.1 +/* + * + * Copyright (C) International Business Machines Corp., 2009, 2013 + * Etersoft, 2012 + * Author(s): Steve French (sfrench@us.ibm.com) + * Pavel Shilovsky (pshilovsky@samba.org) 2012 + * + * Contains the routines for constructing the SMB2 PDUs themselves + * + */ + + /* SMB2 PDU handling routines here - except for leftovers (eg session setup) */ + /* Note that there are handle based routines which must be */ + /* treated slightly differently for reconnection purposes since we never */ + /* want to reuse a stale file handle and only the caller knows the file info */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include "cifsglob.h" +#include "cifsacl.h" +#include "cifsproto.h" +#include "smb2proto.h" +#include "cifs_unicode.h" +#include "cifs_debug.h" +#include "ntlmssp.h" +#include "smb2status.h" +#include "smb2glob.h" +#include "cifspdu.h" +#include "cifs_spnego.h" +#include "smbdirect.h" +#include "trace.h" +#ifdef CONFIG_CIFS_DFS_UPCALL +#include "dfs_cache.h" +#endif +#include "cached_dir.h" + +/* + * The following table defines the expected "StructureSize" of SMB2 requests + * in order by SMB2 command. This is similar to "wct" in SMB/CIFS requests. + * + * Note that commands are defined in smb2pdu.h in le16 but the array below is + * indexed by command in host byte order. + */ +static const int smb2_req_struct_sizes[NUMBER_OF_SMB2_COMMANDS] = { + /* SMB2_NEGOTIATE */ 36, + /* SMB2_SESSION_SETUP */ 25, + /* SMB2_LOGOFF */ 4, + /* SMB2_TREE_CONNECT */ 9, + /* SMB2_TREE_DISCONNECT */ 4, + /* SMB2_CREATE */ 57, + /* SMB2_CLOSE */ 24, + /* SMB2_FLUSH */ 24, + /* SMB2_READ */ 49, + /* SMB2_WRITE */ 49, + /* SMB2_LOCK */ 48, + /* SMB2_IOCTL */ 57, + /* SMB2_CANCEL */ 4, + /* SMB2_ECHO */ 4, + /* SMB2_QUERY_DIRECTORY */ 33, + /* SMB2_CHANGE_NOTIFY */ 32, + /* SMB2_QUERY_INFO */ 41, + /* SMB2_SET_INFO */ 33, + /* SMB2_OPLOCK_BREAK */ 24 /* BB this is 36 for LEASE_BREAK variant */ +}; + +int smb3_encryption_required(const struct cifs_tcon *tcon) +{ + if (!tcon || !tcon->ses) + return 0; + if ((tcon->ses->session_flags & SMB2_SESSION_FLAG_ENCRYPT_DATA) || + (tcon->share_flags & SHI1005_FLAGS_ENCRYPT_DATA)) + return 1; + if (tcon->seal && + (tcon->ses->server->capabilities & SMB2_GLOBAL_CAP_ENCRYPTION)) + return 1; + return 0; +} + +static void +smb2_hdr_assemble(struct smb2_hdr *shdr, __le16 smb2_cmd, + const struct cifs_tcon *tcon, + struct TCP_Server_Info *server) +{ + shdr->ProtocolId = SMB2_PROTO_NUMBER; + shdr->StructureSize = cpu_to_le16(64); + shdr->Command = smb2_cmd; + if (server) { + spin_lock(&server->req_lock); + /* Request up to 10 credits but don't go over the limit. */ + if (server->credits >= server->max_credits) + shdr->CreditRequest = cpu_to_le16(0); + else + shdr->CreditRequest = cpu_to_le16( + min_t(int, server->max_credits - + server->credits, 10)); + spin_unlock(&server->req_lock); + } else { + shdr->CreditRequest = cpu_to_le16(2); + } + shdr->Id.SyncId.ProcessId = cpu_to_le32((__u16)current->tgid); + + if (!tcon) + goto out; + + /* GLOBAL_CAP_LARGE_MTU will only be set if dialect > SMB2.02 */ + /* See sections 2.2.4 and 3.2.4.1.5 of MS-SMB2 */ + if (server && (server->capabilities & SMB2_GLOBAL_CAP_LARGE_MTU)) + shdr->CreditCharge = cpu_to_le16(1); + /* else CreditCharge MBZ */ + + shdr->Id.SyncId.TreeId = cpu_to_le32(tcon->tid); + /* Uid is not converted */ + if (tcon->ses) + shdr->SessionId = cpu_to_le64(tcon->ses->Suid); + + /* + * If we would set SMB2_FLAGS_DFS_OPERATIONS on open we also would have + * to pass the path on the Open SMB prefixed by \\server\share. + * Not sure when we would need to do the augmented path (if ever) and + * setting this flag breaks the SMB2 open operation since it is + * illegal to send an empty path name (without \\server\share prefix) + * when the DFS flag is set in the SMB open header. We could + * consider setting the flag on all operations other than open + * but it is safer to net set it for now. + */ +/* if (tcon->share_flags & SHI1005_FLAGS_DFS) + shdr->Flags |= SMB2_FLAGS_DFS_OPERATIONS; */ + + if (server && server->sign && !smb3_encryption_required(tcon)) + shdr->Flags |= SMB2_FLAGS_SIGNED; +out: + return; +} + +static int +smb2_reconnect(__le16 smb2_command, struct cifs_tcon *tcon, + struct TCP_Server_Info *server) +{ + int rc = 0; + struct nls_table *nls_codepage = NULL; + struct cifs_ses *ses; + + /* + * SMB2s NegProt, SessSetup, Logoff do not have tcon yet so + * check for tcp and smb session status done differently + * for those three - in the calling routine. + */ + if (tcon == NULL) + return 0; + + /* + * Need to also skip SMB2_IOCTL because it is used for checking nested dfs links in + * cifs_tree_connect(). + */ + if (smb2_command == SMB2_TREE_CONNECT || smb2_command == SMB2_IOCTL) + return 0; + + spin_lock(&tcon->tc_lock); + if (tcon->status == TID_EXITING) { + /* + * only tree disconnect allowed when disconnecting ... + */ + if (smb2_command != SMB2_TREE_DISCONNECT) { + spin_unlock(&tcon->tc_lock); + cifs_dbg(FYI, "can not send cmd %d while umounting\n", + smb2_command); + return -ENODEV; + } + } + spin_unlock(&tcon->tc_lock); + + ses = tcon->ses; + if (!ses) + return -EIO; + spin_lock(&ses->ses_lock); + if (ses->ses_status == SES_EXITING) { + spin_unlock(&ses->ses_lock); + return -EIO; + } + spin_unlock(&ses->ses_lock); + if (!ses->server || !server) + return -EIO; + + spin_lock(&server->srv_lock); + if (server->tcpStatus == CifsNeedReconnect) { + /* + * Return to caller for TREE_DISCONNECT and LOGOFF and CLOSE + * here since they are implicitly done when session drops. + */ + switch (smb2_command) { + /* + * BB Should we keep oplock break and add flush to exceptions? + */ + case SMB2_TREE_DISCONNECT: + case SMB2_CANCEL: + case SMB2_CLOSE: + case SMB2_OPLOCK_BREAK: + spin_unlock(&server->srv_lock); + return -EAGAIN; + } + } + spin_unlock(&server->srv_lock); + +again: + rc = cifs_wait_for_server_reconnect(server, tcon->retry); + if (rc) + return rc; + + spin_lock(&ses->chan_lock); + if (!cifs_chan_needs_reconnect(ses, server) && !tcon->need_reconnect) { + spin_unlock(&ses->chan_lock); + return 0; + } + spin_unlock(&ses->chan_lock); + cifs_dbg(FYI, "sess reconnect mask: 0x%lx, tcon reconnect: %d", + tcon->ses->chans_need_reconnect, + tcon->need_reconnect); + + mutex_lock(&ses->session_mutex); + /* + * Recheck after acquire mutex. If another thread is negotiating + * and the server never sends an answer the socket will be closed + * and tcpStatus set to reconnect. + */ + spin_lock(&server->srv_lock); + if (server->tcpStatus == CifsNeedReconnect) { + spin_unlock(&server->srv_lock); + mutex_unlock(&ses->session_mutex); + + if (tcon->retry) + goto again; + + rc = -EHOSTDOWN; + goto out; + } + spin_unlock(&server->srv_lock); + + nls_codepage = load_nls_default(); + + /* + * need to prevent multiple threads trying to simultaneously + * reconnect the same SMB session + */ + spin_lock(&ses->ses_lock); + spin_lock(&ses->chan_lock); + if (!cifs_chan_needs_reconnect(ses, server) && + ses->ses_status == SES_GOOD) { + spin_unlock(&ses->chan_lock); + spin_unlock(&ses->ses_lock); + /* this means that we only need to tree connect */ + if (tcon->need_reconnect) + goto skip_sess_setup; + + mutex_unlock(&ses->session_mutex); + goto out; + } + spin_unlock(&ses->chan_lock); + spin_unlock(&ses->ses_lock); + + rc = cifs_negotiate_protocol(0, ses, server); + if (!rc) { + rc = cifs_setup_session(0, ses, server, nls_codepage); + if ((rc == -EACCES) && !tcon->retry) { + mutex_unlock(&ses->session_mutex); + rc = -EHOSTDOWN; + goto failed; + } else if (rc) { + mutex_unlock(&ses->session_mutex); + goto out; + } + } else { + mutex_unlock(&ses->session_mutex); + goto out; + } + +skip_sess_setup: + if (!tcon->need_reconnect) { + mutex_unlock(&ses->session_mutex); + goto out; + } + cifs_mark_open_files_invalid(tcon); + if (tcon->use_persistent) + tcon->need_reopen_files = true; + + rc = cifs_tree_connect(0, tcon, nls_codepage); + mutex_unlock(&ses->session_mutex); + + cifs_dbg(FYI, "reconnect tcon rc = %d\n", rc); + if (rc) { + /* If sess reconnected but tcon didn't, something strange ... */ + cifs_dbg(VFS, "reconnect tcon failed rc = %d\n", rc); + goto out; + } + + if (smb2_command != SMB2_INTERNAL_CMD) + mod_delayed_work(cifsiod_wq, &server->reconnect, 0); + + atomic_inc(&tconInfoReconnectCount); +out: + /* + * Check if handle based operation so we know whether we can continue + * or not without returning to caller to reset file handle. + */ + /* + * BB Is flush done by server on drop of tcp session? Should we special + * case it and skip above? + */ + switch (smb2_command) { + case SMB2_FLUSH: + case SMB2_READ: + case SMB2_WRITE: + case SMB2_LOCK: + case SMB2_IOCTL: + case SMB2_QUERY_DIRECTORY: + case SMB2_CHANGE_NOTIFY: + case SMB2_QUERY_INFO: + case SMB2_SET_INFO: + rc = -EAGAIN; + } +failed: + unload_nls(nls_codepage); + return rc; +} + +static void +fill_small_buf(__le16 smb2_command, struct cifs_tcon *tcon, + struct TCP_Server_Info *server, + void *buf, + unsigned int *total_len) +{ + struct smb2_pdu *spdu = buf; + /* lookup word count ie StructureSize from table */ + __u16 parmsize = smb2_req_struct_sizes[le16_to_cpu(smb2_command)]; + + /* + * smaller than SMALL_BUFFER_SIZE but bigger than fixed area of + * largest operations (Create) + */ + memset(buf, 0, 256); + + smb2_hdr_assemble(&spdu->hdr, smb2_command, tcon, server); + spdu->StructureSize2 = cpu_to_le16(parmsize); + + *total_len = parmsize + sizeof(struct smb2_hdr); +} + +/* + * Allocate and return pointer to an SMB request hdr, and set basic + * SMB information in the SMB header. If the return code is zero, this + * function must have filled in request_buf pointer. + */ +static int __smb2_plain_req_init(__le16 smb2_command, struct cifs_tcon *tcon, + struct TCP_Server_Info *server, + void **request_buf, unsigned int *total_len) +{ + /* BB eventually switch this to SMB2 specific small buf size */ + if (smb2_command == SMB2_SET_INFO) + *request_buf = cifs_buf_get(); + else + *request_buf = cifs_small_buf_get(); + if (*request_buf == NULL) { + /* BB should we add a retry in here if not a writepage? */ + return -ENOMEM; + } + + fill_small_buf(smb2_command, tcon, server, + (struct smb2_hdr *)(*request_buf), + total_len); + + if (tcon != NULL) { + uint16_t com_code = le16_to_cpu(smb2_command); + cifs_stats_inc(&tcon->stats.smb2_stats.smb2_com_sent[com_code]); + cifs_stats_inc(&tcon->num_smbs_sent); + } + + return 0; +} + +static int smb2_plain_req_init(__le16 smb2_command, struct cifs_tcon *tcon, + struct TCP_Server_Info *server, + void **request_buf, unsigned int *total_len) +{ + int rc; + + rc = smb2_reconnect(smb2_command, tcon, server); + if (rc) + return rc; + + return __smb2_plain_req_init(smb2_command, tcon, server, request_buf, + total_len); +} + +static int smb2_ioctl_req_init(u32 opcode, struct cifs_tcon *tcon, + struct TCP_Server_Info *server, + void **request_buf, unsigned int *total_len) +{ + /* Skip reconnect only for FSCTL_VALIDATE_NEGOTIATE_INFO IOCTLs */ + if (opcode == FSCTL_VALIDATE_NEGOTIATE_INFO) { + return __smb2_plain_req_init(SMB2_IOCTL, tcon, server, + request_buf, total_len); + } + return smb2_plain_req_init(SMB2_IOCTL, tcon, server, + request_buf, total_len); +} + +/* For explanation of negotiate contexts see MS-SMB2 section 2.2.3.1 */ + +static void +build_preauth_ctxt(struct smb2_preauth_neg_context *pneg_ctxt) +{ + pneg_ctxt->ContextType = SMB2_PREAUTH_INTEGRITY_CAPABILITIES; + pneg_ctxt->DataLength = cpu_to_le16(38); + pneg_ctxt->HashAlgorithmCount = cpu_to_le16(1); + pneg_ctxt->SaltLength = cpu_to_le16(SMB311_SALT_SIZE); + get_random_bytes(pneg_ctxt->Salt, SMB311_SALT_SIZE); + pneg_ctxt->HashAlgorithms = SMB2_PREAUTH_INTEGRITY_SHA512; +} + +static void +build_compression_ctxt(struct smb2_compression_capabilities_context *pneg_ctxt) +{ + pneg_ctxt->ContextType = SMB2_COMPRESSION_CAPABILITIES; + pneg_ctxt->DataLength = + cpu_to_le16(sizeof(struct smb2_compression_capabilities_context) + - sizeof(struct smb2_neg_context)); + pneg_ctxt->CompressionAlgorithmCount = cpu_to_le16(3); + pneg_ctxt->CompressionAlgorithms[0] = SMB3_COMPRESS_LZ77; + pneg_ctxt->CompressionAlgorithms[1] = SMB3_COMPRESS_LZ77_HUFF; + pneg_ctxt->CompressionAlgorithms[2] = SMB3_COMPRESS_LZNT1; +} + +static unsigned int +build_signing_ctxt(struct smb2_signing_capabilities *pneg_ctxt) +{ + unsigned int ctxt_len = sizeof(struct smb2_signing_capabilities); + unsigned short num_algs = 1; /* number of signing algorithms sent */ + + pneg_ctxt->ContextType = SMB2_SIGNING_CAPABILITIES; + /* + * Context Data length must be rounded to multiple of 8 for some servers + */ + pneg_ctxt->DataLength = cpu_to_le16(ALIGN(sizeof(struct smb2_signing_capabilities) - + sizeof(struct smb2_neg_context) + + (num_algs * sizeof(u16)), 8)); + pneg_ctxt->SigningAlgorithmCount = cpu_to_le16(num_algs); + pneg_ctxt->SigningAlgorithms[0] = cpu_to_le16(SIGNING_ALG_AES_CMAC); + + ctxt_len += sizeof(__le16) * num_algs; + ctxt_len = ALIGN(ctxt_len, 8); + return ctxt_len; + /* TBD add SIGNING_ALG_AES_GMAC and/or SIGNING_ALG_HMAC_SHA256 */ +} + +static void +build_encrypt_ctxt(struct smb2_encryption_neg_context *pneg_ctxt) +{ + pneg_ctxt->ContextType = SMB2_ENCRYPTION_CAPABILITIES; + if (require_gcm_256) { + pneg_ctxt->DataLength = cpu_to_le16(4); /* Cipher Count + 1 cipher */ + pneg_ctxt->CipherCount = cpu_to_le16(1); + pneg_ctxt->Ciphers[0] = SMB2_ENCRYPTION_AES256_GCM; + } else if (enable_gcm_256) { + pneg_ctxt->DataLength = cpu_to_le16(8); /* Cipher Count + 3 ciphers */ + pneg_ctxt->CipherCount = cpu_to_le16(3); + pneg_ctxt->Ciphers[0] = SMB2_ENCRYPTION_AES128_GCM; + pneg_ctxt->Ciphers[1] = SMB2_ENCRYPTION_AES256_GCM; + pneg_ctxt->Ciphers[2] = SMB2_ENCRYPTION_AES128_CCM; + } else { + pneg_ctxt->DataLength = cpu_to_le16(6); /* Cipher Count + 2 ciphers */ + pneg_ctxt->CipherCount = cpu_to_le16(2); + pneg_ctxt->Ciphers[0] = SMB2_ENCRYPTION_AES128_GCM; + pneg_ctxt->Ciphers[1] = SMB2_ENCRYPTION_AES128_CCM; + } +} + +static unsigned int +build_netname_ctxt(struct smb2_netname_neg_context *pneg_ctxt, char *hostname) +{ + struct nls_table *cp = load_nls_default(); + + pneg_ctxt->ContextType = SMB2_NETNAME_NEGOTIATE_CONTEXT_ID; + + /* copy up to max of first 100 bytes of server name to NetName field */ + pneg_ctxt->DataLength = cpu_to_le16(2 * cifs_strtoUTF16(pneg_ctxt->NetName, hostname, 100, cp)); + /* context size is DataLength + minimal smb2_neg_context */ + return ALIGN(le16_to_cpu(pneg_ctxt->DataLength) + sizeof(struct smb2_neg_context), 8); +} + +static void +build_posix_ctxt(struct smb2_posix_neg_context *pneg_ctxt) +{ + pneg_ctxt->ContextType = SMB2_POSIX_EXTENSIONS_AVAILABLE; + pneg_ctxt->DataLength = cpu_to_le16(POSIX_CTXT_DATA_LEN); + /* SMB2_CREATE_TAG_POSIX is "0x93AD25509CB411E7B42383DE968BCD7C" */ + pneg_ctxt->Name[0] = 0x93; + pneg_ctxt->Name[1] = 0xAD; + pneg_ctxt->Name[2] = 0x25; + pneg_ctxt->Name[3] = 0x50; + pneg_ctxt->Name[4] = 0x9C; + pneg_ctxt->Name[5] = 0xB4; + pneg_ctxt->Name[6] = 0x11; + pneg_ctxt->Name[7] = 0xE7; + pneg_ctxt->Name[8] = 0xB4; + pneg_ctxt->Name[9] = 0x23; + pneg_ctxt->Name[10] = 0x83; + pneg_ctxt->Name[11] = 0xDE; + pneg_ctxt->Name[12] = 0x96; + pneg_ctxt->Name[13] = 0x8B; + pneg_ctxt->Name[14] = 0xCD; + pneg_ctxt->Name[15] = 0x7C; +} + +static void +assemble_neg_contexts(struct smb2_negotiate_req *req, + struct TCP_Server_Info *server, unsigned int *total_len) +{ + unsigned int ctxt_len, neg_context_count; + struct TCP_Server_Info *pserver; + char *pneg_ctxt; + char *hostname; + + if (*total_len > 200) { + /* In case length corrupted don't want to overrun smb buffer */ + cifs_server_dbg(VFS, "Bad frame length assembling neg contexts\n"); + return; + } + + /* + * round up total_len of fixed part of SMB3 negotiate request to 8 + * byte boundary before adding negotiate contexts + */ + *total_len = ALIGN(*total_len, 8); + + pneg_ctxt = (*total_len) + (char *)req; + req->NegotiateContextOffset = cpu_to_le32(*total_len); + + build_preauth_ctxt((struct smb2_preauth_neg_context *)pneg_ctxt); + ctxt_len = ALIGN(sizeof(struct smb2_preauth_neg_context), 8); + *total_len += ctxt_len; + pneg_ctxt += ctxt_len; + + build_encrypt_ctxt((struct smb2_encryption_neg_context *)pneg_ctxt); + ctxt_len = ALIGN(sizeof(struct smb2_encryption_neg_context), 8); + *total_len += ctxt_len; + pneg_ctxt += ctxt_len; + + /* + * secondary channels don't have the hostname field populated + * use the hostname field in the primary channel instead + */ + pserver = CIFS_SERVER_IS_CHAN(server) ? server->primary_server : server; + cifs_server_lock(pserver); + hostname = pserver->hostname; + if (hostname && (hostname[0] != 0)) { + ctxt_len = build_netname_ctxt((struct smb2_netname_neg_context *)pneg_ctxt, + hostname); + *total_len += ctxt_len; + pneg_ctxt += ctxt_len; + neg_context_count = 3; + } else + neg_context_count = 2; + cifs_server_unlock(pserver); + + build_posix_ctxt((struct smb2_posix_neg_context *)pneg_ctxt); + *total_len += sizeof(struct smb2_posix_neg_context); + pneg_ctxt += sizeof(struct smb2_posix_neg_context); + neg_context_count++; + + if (server->compress_algorithm) { + build_compression_ctxt((struct smb2_compression_capabilities_context *) + pneg_ctxt); + ctxt_len = ALIGN(sizeof(struct smb2_compression_capabilities_context), 8); + *total_len += ctxt_len; + pneg_ctxt += ctxt_len; + neg_context_count++; + } + + if (enable_negotiate_signing) { + ctxt_len = build_signing_ctxt((struct smb2_signing_capabilities *) + pneg_ctxt); + *total_len += ctxt_len; + pneg_ctxt += ctxt_len; + neg_context_count++; + } + + /* check for and add transport_capabilities and signing capabilities */ + req->NegotiateContextCount = cpu_to_le16(neg_context_count); + +} + +/* If invalid preauth context warn but use what we requested, SHA-512 */ +static void decode_preauth_context(struct smb2_preauth_neg_context *ctxt) +{ + unsigned int len = le16_to_cpu(ctxt->DataLength); + + /* + * Caller checked that DataLength remains within SMB boundary. We still + * need to confirm that one HashAlgorithms member is accounted for. + */ + if (len < MIN_PREAUTH_CTXT_DATA_LEN) { + pr_warn_once("server sent bad preauth context\n"); + return; + } else if (len < MIN_PREAUTH_CTXT_DATA_LEN + le16_to_cpu(ctxt->SaltLength)) { + pr_warn_once("server sent invalid SaltLength\n"); + return; + } + if (le16_to_cpu(ctxt->HashAlgorithmCount) != 1) + pr_warn_once("Invalid SMB3 hash algorithm count\n"); + if (ctxt->HashAlgorithms != SMB2_PREAUTH_INTEGRITY_SHA512) + pr_warn_once("unknown SMB3 hash algorithm\n"); +} + +static void decode_compress_ctx(struct TCP_Server_Info *server, + struct smb2_compression_capabilities_context *ctxt) +{ + unsigned int len = le16_to_cpu(ctxt->DataLength); + + /* + * Caller checked that DataLength remains within SMB boundary. We still + * need to confirm that one CompressionAlgorithms member is accounted + * for. + */ + if (len < 10) { + pr_warn_once("server sent bad compression cntxt\n"); + return; + } + if (le16_to_cpu(ctxt->CompressionAlgorithmCount) != 1) { + pr_warn_once("Invalid SMB3 compress algorithm count\n"); + return; + } + if (le16_to_cpu(ctxt->CompressionAlgorithms[0]) > 3) { + pr_warn_once("unknown compression algorithm\n"); + return; + } + server->compress_algorithm = ctxt->CompressionAlgorithms[0]; +} + +static int decode_encrypt_ctx(struct TCP_Server_Info *server, + struct smb2_encryption_neg_context *ctxt) +{ + unsigned int len = le16_to_cpu(ctxt->DataLength); + + cifs_dbg(FYI, "decode SMB3.11 encryption neg context of len %d\n", len); + /* + * Caller checked that DataLength remains within SMB boundary. We still + * need to confirm that one Cipher flexible array member is accounted + * for. + */ + if (len < MIN_ENCRYPT_CTXT_DATA_LEN) { + pr_warn_once("server sent bad crypto ctxt len\n"); + return -EINVAL; + } + + if (le16_to_cpu(ctxt->CipherCount) != 1) { + pr_warn_once("Invalid SMB3.11 cipher count\n"); + return -EINVAL; + } + cifs_dbg(FYI, "SMB311 cipher type:%d\n", le16_to_cpu(ctxt->Ciphers[0])); + if (require_gcm_256) { + if (ctxt->Ciphers[0] != SMB2_ENCRYPTION_AES256_GCM) { + cifs_dbg(VFS, "Server does not support requested encryption type (AES256 GCM)\n"); + return -EOPNOTSUPP; + } + } else if (ctxt->Ciphers[0] == 0) { + /* + * e.g. if server only supported AES256_CCM (very unlikely) + * or server supported no encryption types or had all disabled. + * Since GLOBAL_CAP_ENCRYPTION will be not set, in the case + * in which mount requested encryption ("seal") checks later + * on during tree connection will return proper rc, but if + * seal not requested by client, since server is allowed to + * return 0 to indicate no supported cipher, we can't fail here + */ + server->cipher_type = 0; + server->capabilities &= ~SMB2_GLOBAL_CAP_ENCRYPTION; + pr_warn_once("Server does not support requested encryption types\n"); + return 0; + } else if ((ctxt->Ciphers[0] != SMB2_ENCRYPTION_AES128_CCM) && + (ctxt->Ciphers[0] != SMB2_ENCRYPTION_AES128_GCM) && + (ctxt->Ciphers[0] != SMB2_ENCRYPTION_AES256_GCM)) { + /* server returned a cipher we didn't ask for */ + pr_warn_once("Invalid SMB3.11 cipher returned\n"); + return -EINVAL; + } + server->cipher_type = ctxt->Ciphers[0]; + server->capabilities |= SMB2_GLOBAL_CAP_ENCRYPTION; + return 0; +} + +static void decode_signing_ctx(struct TCP_Server_Info *server, + struct smb2_signing_capabilities *pctxt) +{ + unsigned int len = le16_to_cpu(pctxt->DataLength); + + /* + * Caller checked that DataLength remains within SMB boundary. We still + * need to confirm that one SigningAlgorithms flexible array member is + * accounted for. + */ + if ((len < 4) || (len > 16)) { + pr_warn_once("server sent bad signing negcontext\n"); + return; + } + if (le16_to_cpu(pctxt->SigningAlgorithmCount) != 1) { + pr_warn_once("Invalid signing algorithm count\n"); + return; + } + if (le16_to_cpu(pctxt->SigningAlgorithms[0]) > 2) { + pr_warn_once("unknown signing algorithm\n"); + return; + } + + server->signing_negotiated = true; + server->signing_algorithm = le16_to_cpu(pctxt->SigningAlgorithms[0]); + cifs_dbg(FYI, "signing algorithm %d chosen\n", + server->signing_algorithm); +} + + +static int smb311_decode_neg_context(struct smb2_negotiate_rsp *rsp, + struct TCP_Server_Info *server, + unsigned int len_of_smb) +{ + struct smb2_neg_context *pctx; + unsigned int offset = le32_to_cpu(rsp->NegotiateContextOffset); + unsigned int ctxt_cnt = le16_to_cpu(rsp->NegotiateContextCount); + unsigned int len_of_ctxts, i; + int rc = 0; + + cifs_dbg(FYI, "decoding %d negotiate contexts\n", ctxt_cnt); + if (len_of_smb <= offset) { + cifs_server_dbg(VFS, "Invalid response: negotiate context offset\n"); + return -EINVAL; + } + + len_of_ctxts = len_of_smb - offset; + + for (i = 0; i < ctxt_cnt; i++) { + int clen; + /* check that offset is not beyond end of SMB */ + if (len_of_ctxts < sizeof(struct smb2_neg_context)) + break; + + pctx = (struct smb2_neg_context *)(offset + (char *)rsp); + clen = sizeof(struct smb2_neg_context) + + le16_to_cpu(pctx->DataLength); + /* + * 2.2.4 SMB2 NEGOTIATE Response + * Subsequent negotiate contexts MUST appear at the first 8-byte + * aligned offset following the previous negotiate context. + */ + if (i + 1 != ctxt_cnt) + clen = ALIGN(clen, 8); + if (clen > len_of_ctxts) + break; + + if (pctx->ContextType == SMB2_PREAUTH_INTEGRITY_CAPABILITIES) + decode_preauth_context( + (struct smb2_preauth_neg_context *)pctx); + else if (pctx->ContextType == SMB2_ENCRYPTION_CAPABILITIES) + rc = decode_encrypt_ctx(server, + (struct smb2_encryption_neg_context *)pctx); + else if (pctx->ContextType == SMB2_COMPRESSION_CAPABILITIES) + decode_compress_ctx(server, + (struct smb2_compression_capabilities_context *)pctx); + else if (pctx->ContextType == SMB2_POSIX_EXTENSIONS_AVAILABLE) + server->posix_ext_supported = true; + else if (pctx->ContextType == SMB2_SIGNING_CAPABILITIES) + decode_signing_ctx(server, + (struct smb2_signing_capabilities *)pctx); + else + cifs_server_dbg(VFS, "unknown negcontext of type %d ignored\n", + le16_to_cpu(pctx->ContextType)); + if (rc) + break; + + offset += clen; + len_of_ctxts -= clen; + } + return rc; +} + +static struct create_posix * +create_posix_buf(umode_t mode) +{ + struct create_posix *buf; + + buf = kzalloc(sizeof(struct create_posix), + GFP_KERNEL); + if (!buf) + return NULL; + + buf->ccontext.DataOffset = + cpu_to_le16(offsetof(struct create_posix, Mode)); + buf->ccontext.DataLength = cpu_to_le32(4); + buf->ccontext.NameOffset = + cpu_to_le16(offsetof(struct create_posix, Name)); + buf->ccontext.NameLength = cpu_to_le16(16); + + /* SMB2_CREATE_TAG_POSIX is "0x93AD25509CB411E7B42383DE968BCD7C" */ + buf->Name[0] = 0x93; + buf->Name[1] = 0xAD; + buf->Name[2] = 0x25; + buf->Name[3] = 0x50; + buf->Name[4] = 0x9C; + buf->Name[5] = 0xB4; + buf->Name[6] = 0x11; + buf->Name[7] = 0xE7; + buf->Name[8] = 0xB4; + buf->Name[9] = 0x23; + buf->Name[10] = 0x83; + buf->Name[11] = 0xDE; + buf->Name[12] = 0x96; + buf->Name[13] = 0x8B; + buf->Name[14] = 0xCD; + buf->Name[15] = 0x7C; + buf->Mode = cpu_to_le32(mode); + cifs_dbg(FYI, "mode on posix create 0%o\n", mode); + return buf; +} + +static int +add_posix_context(struct kvec *iov, unsigned int *num_iovec, umode_t mode) +{ + struct smb2_create_req *req = iov[0].iov_base; + unsigned int num = *num_iovec; + + iov[num].iov_base = create_posix_buf(mode); + if (mode == ACL_NO_MODE) + cifs_dbg(FYI, "Invalid mode\n"); + if (iov[num].iov_base == NULL) + return -ENOMEM; + iov[num].iov_len = sizeof(struct create_posix); + if (!req->CreateContextsOffset) + req->CreateContextsOffset = cpu_to_le32( + sizeof(struct smb2_create_req) + + iov[num - 1].iov_len); + le32_add_cpu(&req->CreateContextsLength, sizeof(struct create_posix)); + *num_iovec = num + 1; + return 0; +} + + +/* + * + * SMB2 Worker functions follow: + * + * The general structure of the worker functions is: + * 1) Call smb2_init (assembles SMB2 header) + * 2) Initialize SMB2 command specific fields in fixed length area of SMB + * 3) Call smb_sendrcv2 (sends request on socket and waits for response) + * 4) Decode SMB2 command specific fields in the fixed length area + * 5) Decode variable length data area (if any for this SMB2 command type) + * 6) Call free smb buffer + * 7) return + * + */ + +int +SMB2_negotiate(const unsigned int xid, + struct cifs_ses *ses, + struct TCP_Server_Info *server) +{ + struct smb_rqst rqst; + struct smb2_negotiate_req *req; + struct smb2_negotiate_rsp *rsp; + struct kvec iov[1]; + struct kvec rsp_iov; + int rc; + int resp_buftype; + int blob_offset, blob_length; + char *security_blob; + int flags = CIFS_NEG_OP; + unsigned int total_len; + + cifs_dbg(FYI, "Negotiate protocol\n"); + + if (!server) { + WARN(1, "%s: server is NULL!\n", __func__); + return -EIO; + } + + rc = smb2_plain_req_init(SMB2_NEGOTIATE, NULL, server, + (void **) &req, &total_len); + if (rc) + return rc; + + req->hdr.SessionId = 0; + + memset(server->preauth_sha_hash, 0, SMB2_PREAUTH_HASH_SIZE); + memset(ses->preauth_sha_hash, 0, SMB2_PREAUTH_HASH_SIZE); + + if (strcmp(server->vals->version_string, + SMB3ANY_VERSION_STRING) == 0) { + req->Dialects[0] = cpu_to_le16(SMB30_PROT_ID); + req->Dialects[1] = cpu_to_le16(SMB302_PROT_ID); + req->Dialects[2] = cpu_to_le16(SMB311_PROT_ID); + req->DialectCount = cpu_to_le16(3); + total_len += 6; + } else if (strcmp(server->vals->version_string, + SMBDEFAULT_VERSION_STRING) == 0) { + req->Dialects[0] = cpu_to_le16(SMB21_PROT_ID); + req->Dialects[1] = cpu_to_le16(SMB30_PROT_ID); + req->Dialects[2] = cpu_to_le16(SMB302_PROT_ID); + req->Dialects[3] = cpu_to_le16(SMB311_PROT_ID); + req->DialectCount = cpu_to_le16(4); + total_len += 8; + } else { + /* otherwise send specific dialect */ + req->Dialects[0] = cpu_to_le16(server->vals->protocol_id); + req->DialectCount = cpu_to_le16(1); + total_len += 2; + } + + /* only one of SMB2 signing flags may be set in SMB2 request */ + if (ses->sign) + req->SecurityMode = cpu_to_le16(SMB2_NEGOTIATE_SIGNING_REQUIRED); + else if (global_secflags & CIFSSEC_MAY_SIGN) + req->SecurityMode = cpu_to_le16(SMB2_NEGOTIATE_SIGNING_ENABLED); + else + req->SecurityMode = 0; + + req->Capabilities = cpu_to_le32(server->vals->req_capabilities); + if (ses->chan_max > 1) + req->Capabilities |= cpu_to_le32(SMB2_GLOBAL_CAP_MULTI_CHANNEL); + + /* ClientGUID must be zero for SMB2.02 dialect */ + if (server->vals->protocol_id == SMB20_PROT_ID) + memset(req->ClientGUID, 0, SMB2_CLIENT_GUID_SIZE); + else { + memcpy(req->ClientGUID, server->client_guid, + SMB2_CLIENT_GUID_SIZE); + if ((server->vals->protocol_id == SMB311_PROT_ID) || + (strcmp(server->vals->version_string, + SMB3ANY_VERSION_STRING) == 0) || + (strcmp(server->vals->version_string, + SMBDEFAULT_VERSION_STRING) == 0)) + assemble_neg_contexts(req, server, &total_len); + } + iov[0].iov_base = (char *)req; + iov[0].iov_len = total_len; + + memset(&rqst, 0, sizeof(struct smb_rqst)); + rqst.rq_iov = iov; + rqst.rq_nvec = 1; + + rc = cifs_send_recv(xid, ses, server, + &rqst, &resp_buftype, flags, &rsp_iov); + cifs_small_buf_release(req); + rsp = (struct smb2_negotiate_rsp *)rsp_iov.iov_base; + /* + * No tcon so can't do + * cifs_stats_inc(&tcon->stats.smb2_stats.smb2_com_fail[SMB2...]); + */ + if (rc == -EOPNOTSUPP) { + cifs_server_dbg(VFS, "Dialect not supported by server. Consider specifying vers=1.0 or vers=2.0 on mount for accessing older servers\n"); + goto neg_exit; + } else if (rc != 0) + goto neg_exit; + + rc = -EIO; + if (strcmp(server->vals->version_string, + SMB3ANY_VERSION_STRING) == 0) { + if (rsp->DialectRevision == cpu_to_le16(SMB20_PROT_ID)) { + cifs_server_dbg(VFS, + "SMB2 dialect returned but not requested\n"); + goto neg_exit; + } else if (rsp->DialectRevision == cpu_to_le16(SMB21_PROT_ID)) { + cifs_server_dbg(VFS, + "SMB2.1 dialect returned but not requested\n"); + goto neg_exit; + } else if (rsp->DialectRevision == cpu_to_le16(SMB311_PROT_ID)) { + /* ops set to 3.0 by default for default so update */ + server->ops = &smb311_operations; + server->vals = &smb311_values; + } + } else if (strcmp(server->vals->version_string, + SMBDEFAULT_VERSION_STRING) == 0) { + if (rsp->DialectRevision == cpu_to_le16(SMB20_PROT_ID)) { + cifs_server_dbg(VFS, + "SMB2 dialect returned but not requested\n"); + goto neg_exit; + } else if (rsp->DialectRevision == cpu_to_le16(SMB21_PROT_ID)) { + /* ops set to 3.0 by default for default so update */ + server->ops = &smb21_operations; + server->vals = &smb21_values; + } else if (rsp->DialectRevision == cpu_to_le16(SMB311_PROT_ID)) { + server->ops = &smb311_operations; + server->vals = &smb311_values; + } + } else if (le16_to_cpu(rsp->DialectRevision) != + server->vals->protocol_id) { + /* if requested single dialect ensure returned dialect matched */ + cifs_server_dbg(VFS, "Invalid 0x%x dialect returned: not requested\n", + le16_to_cpu(rsp->DialectRevision)); + goto neg_exit; + } + + cifs_dbg(FYI, "mode 0x%x\n", rsp->SecurityMode); + + if (rsp->DialectRevision == cpu_to_le16(SMB20_PROT_ID)) + cifs_dbg(FYI, "negotiated smb2.0 dialect\n"); + else if (rsp->DialectRevision == cpu_to_le16(SMB21_PROT_ID)) + cifs_dbg(FYI, "negotiated smb2.1 dialect\n"); + else if (rsp->DialectRevision == cpu_to_le16(SMB30_PROT_ID)) + cifs_dbg(FYI, "negotiated smb3.0 dialect\n"); + else if (rsp->DialectRevision == cpu_to_le16(SMB302_PROT_ID)) + cifs_dbg(FYI, "negotiated smb3.02 dialect\n"); + else if (rsp->DialectRevision == cpu_to_le16(SMB311_PROT_ID)) + cifs_dbg(FYI, "negotiated smb3.1.1 dialect\n"); + else { + cifs_server_dbg(VFS, "Invalid dialect returned by server 0x%x\n", + le16_to_cpu(rsp->DialectRevision)); + goto neg_exit; + } + + rc = 0; + server->dialect = le16_to_cpu(rsp->DialectRevision); + + /* + * Keep a copy of the hash after negprot. This hash will be + * the starting hash value for all sessions made from this + * server. + */ + memcpy(server->preauth_sha_hash, ses->preauth_sha_hash, + SMB2_PREAUTH_HASH_SIZE); + + /* SMB2 only has an extended negflavor */ + server->negflavor = CIFS_NEGFLAVOR_EXTENDED; + /* set it to the maximum buffer size value we can send with 1 credit */ + server->maxBuf = min_t(unsigned int, le32_to_cpu(rsp->MaxTransactSize), + SMB2_MAX_BUFFER_SIZE); + server->max_read = le32_to_cpu(rsp->MaxReadSize); + server->max_write = le32_to_cpu(rsp->MaxWriteSize); + server->sec_mode = le16_to_cpu(rsp->SecurityMode); + if ((server->sec_mode & SMB2_SEC_MODE_FLAGS_ALL) != server->sec_mode) + cifs_dbg(FYI, "Server returned unexpected security mode 0x%x\n", + server->sec_mode); + server->capabilities = le32_to_cpu(rsp->Capabilities); + /* Internal types */ + server->capabilities |= SMB2_NT_FIND | SMB2_LARGE_FILES; + + /* + * SMB3.0 supports only 1 cipher and doesn't have a encryption neg context + * Set the cipher type manually. + */ + if (server->dialect == SMB30_PROT_ID && (server->capabilities & SMB2_GLOBAL_CAP_ENCRYPTION)) + server->cipher_type = SMB2_ENCRYPTION_AES128_CCM; + + security_blob = smb2_get_data_area_len(&blob_offset, &blob_length, + (struct smb2_hdr *)rsp); + /* + * See MS-SMB2 section 2.2.4: if no blob, client picks default which + * for us will be + * ses->sectype = RawNTLMSSP; + * but for time being this is our only auth choice so doesn't matter. + * We just found a server which sets blob length to zero expecting raw. + */ + if (blob_length == 0) { + cifs_dbg(FYI, "missing security blob on negprot\n"); + server->sec_ntlmssp = true; + } + + rc = cifs_enable_signing(server, ses->sign); + if (rc) + goto neg_exit; + if (blob_length) { + rc = decode_negTokenInit(security_blob, blob_length, server); + if (rc == 1) + rc = 0; + else if (rc == 0) + rc = -EIO; + } + + if (rsp->DialectRevision == cpu_to_le16(SMB311_PROT_ID)) { + if (rsp->NegotiateContextCount) + rc = smb311_decode_neg_context(rsp, server, + rsp_iov.iov_len); + else + cifs_server_dbg(VFS, "Missing expected negotiate contexts\n"); + } +neg_exit: + free_rsp_buf(resp_buftype, rsp); + return rc; +} + +int smb3_validate_negotiate(const unsigned int xid, struct cifs_tcon *tcon) +{ + int rc; + struct validate_negotiate_info_req *pneg_inbuf; + struct validate_negotiate_info_rsp *pneg_rsp = NULL; + u32 rsplen; + u32 inbuflen; /* max of 4 dialects */ + struct TCP_Server_Info *server = tcon->ses->server; + + cifs_dbg(FYI, "validate negotiate\n"); + + /* In SMB3.11 preauth integrity supersedes validate negotiate */ + if (server->dialect == SMB311_PROT_ID) + return 0; + + /* + * validation ioctl must be signed, so no point sending this if we + * can not sign it (ie are not known user). Even if signing is not + * required (enabled but not negotiated), in those cases we selectively + * sign just this, the first and only signed request on a connection. + * Having validation of negotiate info helps reduce attack vectors. + */ + if (tcon->ses->session_flags & SMB2_SESSION_FLAG_IS_GUEST) + return 0; /* validation requires signing */ + + if (tcon->ses->user_name == NULL) { + cifs_dbg(FYI, "Can't validate negotiate: null user mount\n"); + return 0; /* validation requires signing */ + } + + if (tcon->ses->session_flags & SMB2_SESSION_FLAG_IS_NULL) + cifs_tcon_dbg(VFS, "Unexpected null user (anonymous) auth flag sent by server\n"); + + pneg_inbuf = kmalloc(sizeof(*pneg_inbuf), GFP_NOFS); + if (!pneg_inbuf) + return -ENOMEM; + + pneg_inbuf->Capabilities = + cpu_to_le32(server->vals->req_capabilities); + if (tcon->ses->chan_max > 1) + pneg_inbuf->Capabilities |= cpu_to_le32(SMB2_GLOBAL_CAP_MULTI_CHANNEL); + + memcpy(pneg_inbuf->Guid, server->client_guid, + SMB2_CLIENT_GUID_SIZE); + + if (tcon->ses->sign) + pneg_inbuf->SecurityMode = + cpu_to_le16(SMB2_NEGOTIATE_SIGNING_REQUIRED); + else if (global_secflags & CIFSSEC_MAY_SIGN) + pneg_inbuf->SecurityMode = + cpu_to_le16(SMB2_NEGOTIATE_SIGNING_ENABLED); + else + pneg_inbuf->SecurityMode = 0; + + + if (strcmp(server->vals->version_string, + SMB3ANY_VERSION_STRING) == 0) { + pneg_inbuf->Dialects[0] = cpu_to_le16(SMB30_PROT_ID); + pneg_inbuf->Dialects[1] = cpu_to_le16(SMB302_PROT_ID); + pneg_inbuf->Dialects[2] = cpu_to_le16(SMB311_PROT_ID); + pneg_inbuf->DialectCount = cpu_to_le16(3); + /* SMB 2.1 not included so subtract one dialect from len */ + inbuflen = sizeof(*pneg_inbuf) - + (sizeof(pneg_inbuf->Dialects[0])); + } else if (strcmp(server->vals->version_string, + SMBDEFAULT_VERSION_STRING) == 0) { + pneg_inbuf->Dialects[0] = cpu_to_le16(SMB21_PROT_ID); + pneg_inbuf->Dialects[1] = cpu_to_le16(SMB30_PROT_ID); + pneg_inbuf->Dialects[2] = cpu_to_le16(SMB302_PROT_ID); + pneg_inbuf->Dialects[3] = cpu_to_le16(SMB311_PROT_ID); + pneg_inbuf->DialectCount = cpu_to_le16(4); + /* structure is big enough for 4 dialects */ + inbuflen = sizeof(*pneg_inbuf); + } else { + /* otherwise specific dialect was requested */ + pneg_inbuf->Dialects[0] = + cpu_to_le16(server->vals->protocol_id); + pneg_inbuf->DialectCount = cpu_to_le16(1); + /* structure is big enough for 4 dialects, sending only 1 */ + inbuflen = sizeof(*pneg_inbuf) - + sizeof(pneg_inbuf->Dialects[0]) * 3; + } + + rc = SMB2_ioctl(xid, tcon, NO_FILE_ID, NO_FILE_ID, + FSCTL_VALIDATE_NEGOTIATE_INFO, + (char *)pneg_inbuf, inbuflen, CIFSMaxBufSize, + (char **)&pneg_rsp, &rsplen); + if (rc == -EOPNOTSUPP) { + /* + * Old Windows versions or Netapp SMB server can return + * not supported error. Client should accept it. + */ + cifs_tcon_dbg(VFS, "Server does not support validate negotiate\n"); + rc = 0; + goto out_free_inbuf; + } else if (rc != 0) { + cifs_tcon_dbg(VFS, "validate protocol negotiate failed: %d\n", + rc); + rc = -EIO; + goto out_free_inbuf; + } + + rc = -EIO; + if (rsplen != sizeof(*pneg_rsp)) { + cifs_tcon_dbg(VFS, "Invalid protocol negotiate response size: %d\n", + rsplen); + + /* relax check since Mac returns max bufsize allowed on ioctl */ + if (rsplen > CIFSMaxBufSize || rsplen < sizeof(*pneg_rsp)) + goto out_free_rsp; + } + + /* check validate negotiate info response matches what we got earlier */ + if (pneg_rsp->Dialect != cpu_to_le16(server->dialect)) + goto vneg_out; + + if (pneg_rsp->SecurityMode != cpu_to_le16(server->sec_mode)) + goto vneg_out; + + /* do not validate server guid because not saved at negprot time yet */ + + if ((le32_to_cpu(pneg_rsp->Capabilities) | SMB2_NT_FIND | + SMB2_LARGE_FILES) != server->capabilities) + goto vneg_out; + + /* validate negotiate successful */ + rc = 0; + cifs_dbg(FYI, "validate negotiate info successful\n"); + goto out_free_rsp; + +vneg_out: + cifs_tcon_dbg(VFS, "protocol revalidation - security settings mismatch\n"); +out_free_rsp: + kfree(pneg_rsp); +out_free_inbuf: + kfree(pneg_inbuf); + return rc; +} + +enum securityEnum +smb2_select_sectype(struct TCP_Server_Info *server, enum securityEnum requested) +{ + switch (requested) { + case Kerberos: + case RawNTLMSSP: + return requested; + case NTLMv2: + return RawNTLMSSP; + case Unspecified: + if (server->sec_ntlmssp && + (global_secflags & CIFSSEC_MAY_NTLMSSP)) + return RawNTLMSSP; + if ((server->sec_kerberos || server->sec_mskerberos) && + (global_secflags & CIFSSEC_MAY_KRB5)) + return Kerberos; + fallthrough; + default: + return Unspecified; + } +} + +struct SMB2_sess_data { + unsigned int xid; + struct cifs_ses *ses; + struct TCP_Server_Info *server; + struct nls_table *nls_cp; + void (*func)(struct SMB2_sess_data *); + int result; + u64 previous_session; + + /* we will send the SMB in three pieces: + * a fixed length beginning part, an optional + * SPNEGO blob (which can be zero length), and a + * last part which will include the strings + * and rest of bcc area. This allows us to avoid + * a large buffer 17K allocation + */ + int buf0_type; + struct kvec iov[2]; +}; + +static int +SMB2_sess_alloc_buffer(struct SMB2_sess_data *sess_data) +{ + int rc; + struct cifs_ses *ses = sess_data->ses; + struct TCP_Server_Info *server = sess_data->server; + struct smb2_sess_setup_req *req; + unsigned int total_len; + bool is_binding = false; + + rc = smb2_plain_req_init(SMB2_SESSION_SETUP, NULL, server, + (void **) &req, + &total_len); + if (rc) + return rc; + + spin_lock(&ses->ses_lock); + is_binding = (ses->ses_status == SES_GOOD); + spin_unlock(&ses->ses_lock); + + if (is_binding) { + req->hdr.SessionId = cpu_to_le64(ses->Suid); + req->hdr.Flags |= SMB2_FLAGS_SIGNED; + req->PreviousSessionId = 0; + req->Flags = SMB2_SESSION_REQ_FLAG_BINDING; + cifs_dbg(FYI, "Binding to sess id: %llx\n", ses->Suid); + } else { + /* First session, not a reauthenticate */ + req->hdr.SessionId = 0; + /* + * if reconnect, we need to send previous sess id + * otherwise it is 0 + */ + req->PreviousSessionId = cpu_to_le64(sess_data->previous_session); + req->Flags = 0; /* MBZ */ + cifs_dbg(FYI, "Fresh session. Previous: %llx\n", + sess_data->previous_session); + } + + /* enough to enable echos and oplocks and one max size write */ + req->hdr.CreditRequest = cpu_to_le16(130); + + /* only one of SMB2 signing flags may be set in SMB2 request */ + if (server->sign) + req->SecurityMode = SMB2_NEGOTIATE_SIGNING_REQUIRED; + else if (global_secflags & CIFSSEC_MAY_SIGN) /* one flag unlike MUST_ */ + req->SecurityMode = SMB2_NEGOTIATE_SIGNING_ENABLED; + else + req->SecurityMode = 0; + +#ifdef CONFIG_CIFS_DFS_UPCALL + req->Capabilities = cpu_to_le32(SMB2_GLOBAL_CAP_DFS); +#else + req->Capabilities = 0; +#endif /* DFS_UPCALL */ + + req->Channel = 0; /* MBZ */ + + sess_data->iov[0].iov_base = (char *)req; + /* 1 for pad */ + sess_data->iov[0].iov_len = total_len - 1; + /* + * This variable will be used to clear the buffer + * allocated above in case of any error in the calling function. + */ + sess_data->buf0_type = CIFS_SMALL_BUFFER; + + return 0; +} + +static void +SMB2_sess_free_buffer(struct SMB2_sess_data *sess_data) +{ + struct kvec *iov = sess_data->iov; + + /* iov[1] is already freed by caller */ + if (sess_data->buf0_type != CIFS_NO_BUFFER && iov[0].iov_base) + memzero_explicit(iov[0].iov_base, iov[0].iov_len); + + free_rsp_buf(sess_data->buf0_type, iov[0].iov_base); + sess_data->buf0_type = CIFS_NO_BUFFER; +} + +static int +SMB2_sess_sendreceive(struct SMB2_sess_data *sess_data) +{ + int rc; + struct smb_rqst rqst; + struct smb2_sess_setup_req *req = sess_data->iov[0].iov_base; + struct kvec rsp_iov = { NULL, 0 }; + + /* Testing shows that buffer offset must be at location of Buffer[0] */ + req->SecurityBufferOffset = + cpu_to_le16(sizeof(struct smb2_sess_setup_req) - 1 /* pad */); + req->SecurityBufferLength = cpu_to_le16(sess_data->iov[1].iov_len); + + memset(&rqst, 0, sizeof(struct smb_rqst)); + rqst.rq_iov = sess_data->iov; + rqst.rq_nvec = 2; + + /* BB add code to build os and lm fields */ + rc = cifs_send_recv(sess_data->xid, sess_data->ses, + sess_data->server, + &rqst, + &sess_data->buf0_type, + CIFS_LOG_ERROR | CIFS_SESS_OP, &rsp_iov); + cifs_small_buf_release(sess_data->iov[0].iov_base); + memcpy(&sess_data->iov[0], &rsp_iov, sizeof(struct kvec)); + + return rc; +} + +static int +SMB2_sess_establish_session(struct SMB2_sess_data *sess_data) +{ + int rc = 0; + struct cifs_ses *ses = sess_data->ses; + struct TCP_Server_Info *server = sess_data->server; + + cifs_server_lock(server); + if (server->ops->generate_signingkey) { + rc = server->ops->generate_signingkey(ses, server); + if (rc) { + cifs_dbg(FYI, + "SMB3 session key generation failed\n"); + cifs_server_unlock(server); + return rc; + } + } + if (!server->session_estab) { + server->sequence_number = 0x2; + server->session_estab = true; + } + cifs_server_unlock(server); + + cifs_dbg(FYI, "SMB2/3 session established successfully\n"); + return rc; +} + +#ifdef CONFIG_CIFS_UPCALL +static void +SMB2_auth_kerberos(struct SMB2_sess_data *sess_data) +{ + int rc; + struct cifs_ses *ses = sess_data->ses; + struct TCP_Server_Info *server = sess_data->server; + struct cifs_spnego_msg *msg; + struct key *spnego_key = NULL; + struct smb2_sess_setup_rsp *rsp = NULL; + bool is_binding = false; + + rc = SMB2_sess_alloc_buffer(sess_data); + if (rc) + goto out; + + spnego_key = cifs_get_spnego_key(ses, server); + if (IS_ERR(spnego_key)) { + rc = PTR_ERR(spnego_key); + if (rc == -ENOKEY) + cifs_dbg(VFS, "Verify user has a krb5 ticket and keyutils is installed\n"); + spnego_key = NULL; + goto out; + } + + msg = spnego_key->payload.data[0]; + /* + * check version field to make sure that cifs.upcall is + * sending us a response in an expected form + */ + if (msg->version != CIFS_SPNEGO_UPCALL_VERSION) { + cifs_dbg(VFS, "bad cifs.upcall version. Expected %d got %d\n", + CIFS_SPNEGO_UPCALL_VERSION, msg->version); + rc = -EKEYREJECTED; + goto out_put_spnego_key; + } + + spin_lock(&ses->ses_lock); + is_binding = (ses->ses_status == SES_GOOD); + spin_unlock(&ses->ses_lock); + + /* keep session key if binding */ + if (!is_binding) { + kfree_sensitive(ses->auth_key.response); + ses->auth_key.response = kmemdup(msg->data, msg->sesskey_len, + GFP_KERNEL); + if (!ses->auth_key.response) { + cifs_dbg(VFS, "Kerberos can't allocate (%u bytes) memory\n", + msg->sesskey_len); + rc = -ENOMEM; + goto out_put_spnego_key; + } + ses->auth_key.len = msg->sesskey_len; + } + + sess_data->iov[1].iov_base = msg->data + msg->sesskey_len; + sess_data->iov[1].iov_len = msg->secblob_len; + + rc = SMB2_sess_sendreceive(sess_data); + if (rc) + goto out_put_spnego_key; + + rsp = (struct smb2_sess_setup_rsp *)sess_data->iov[0].iov_base; + /* keep session id and flags if binding */ + if (!is_binding) { + ses->Suid = le64_to_cpu(rsp->hdr.SessionId); + ses->session_flags = le16_to_cpu(rsp->SessionFlags); + } + + rc = SMB2_sess_establish_session(sess_data); +out_put_spnego_key: + key_invalidate(spnego_key); + key_put(spnego_key); + if (rc) { + kfree_sensitive(ses->auth_key.response); + ses->auth_key.response = NULL; + ses->auth_key.len = 0; + } +out: + sess_data->result = rc; + sess_data->func = NULL; + SMB2_sess_free_buffer(sess_data); +} +#else +static void +SMB2_auth_kerberos(struct SMB2_sess_data *sess_data) +{ + cifs_dbg(VFS, "Kerberos negotiated but upcall support disabled!\n"); + sess_data->result = -EOPNOTSUPP; + sess_data->func = NULL; +} +#endif + +static void +SMB2_sess_auth_rawntlmssp_authenticate(struct SMB2_sess_data *sess_data); + +static void +SMB2_sess_auth_rawntlmssp_negotiate(struct SMB2_sess_data *sess_data) +{ + int rc; + struct cifs_ses *ses = sess_data->ses; + struct TCP_Server_Info *server = sess_data->server; + struct smb2_sess_setup_rsp *rsp = NULL; + unsigned char *ntlmssp_blob = NULL; + bool use_spnego = false; /* else use raw ntlmssp */ + u16 blob_length = 0; + bool is_binding = false; + + /* + * If memory allocation is successful, caller of this function + * frees it. + */ + ses->ntlmssp = kmalloc(sizeof(struct ntlmssp_auth), GFP_KERNEL); + if (!ses->ntlmssp) { + rc = -ENOMEM; + goto out_err; + } + ses->ntlmssp->sesskey_per_smbsess = true; + + rc = SMB2_sess_alloc_buffer(sess_data); + if (rc) + goto out_err; + + rc = build_ntlmssp_smb3_negotiate_blob(&ntlmssp_blob, + &blob_length, ses, server, + sess_data->nls_cp); + if (rc) + goto out; + + if (use_spnego) { + /* BB eventually need to add this */ + cifs_dbg(VFS, "spnego not supported for SMB2 yet\n"); + rc = -EOPNOTSUPP; + goto out; + } + sess_data->iov[1].iov_base = ntlmssp_blob; + sess_data->iov[1].iov_len = blob_length; + + rc = SMB2_sess_sendreceive(sess_data); + rsp = (struct smb2_sess_setup_rsp *)sess_data->iov[0].iov_base; + + /* If true, rc here is expected and not an error */ + if (sess_data->buf0_type != CIFS_NO_BUFFER && + rsp->hdr.Status == STATUS_MORE_PROCESSING_REQUIRED) + rc = 0; + + if (rc) + goto out; + + if (offsetof(struct smb2_sess_setup_rsp, Buffer) != + le16_to_cpu(rsp->SecurityBufferOffset)) { + cifs_dbg(VFS, "Invalid security buffer offset %d\n", + le16_to_cpu(rsp->SecurityBufferOffset)); + rc = -EIO; + goto out; + } + rc = decode_ntlmssp_challenge(rsp->Buffer, + le16_to_cpu(rsp->SecurityBufferLength), ses); + if (rc) + goto out; + + cifs_dbg(FYI, "rawntlmssp session setup challenge phase\n"); + + spin_lock(&ses->ses_lock); + is_binding = (ses->ses_status == SES_GOOD); + spin_unlock(&ses->ses_lock); + + /* keep existing ses id and flags if binding */ + if (!is_binding) { + ses->Suid = le64_to_cpu(rsp->hdr.SessionId); + ses->session_flags = le16_to_cpu(rsp->SessionFlags); + } + +out: + kfree_sensitive(ntlmssp_blob); + SMB2_sess_free_buffer(sess_data); + if (!rc) { + sess_data->result = 0; + sess_data->func = SMB2_sess_auth_rawntlmssp_authenticate; + return; + } +out_err: + kfree_sensitive(ses->ntlmssp); + ses->ntlmssp = NULL; + sess_data->result = rc; + sess_data->func = NULL; +} + +static void +SMB2_sess_auth_rawntlmssp_authenticate(struct SMB2_sess_data *sess_data) +{ + int rc; + struct cifs_ses *ses = sess_data->ses; + struct TCP_Server_Info *server = sess_data->server; + struct smb2_sess_setup_req *req; + struct smb2_sess_setup_rsp *rsp = NULL; + unsigned char *ntlmssp_blob = NULL; + bool use_spnego = false; /* else use raw ntlmssp */ + u16 blob_length = 0; + bool is_binding = false; + + rc = SMB2_sess_alloc_buffer(sess_data); + if (rc) + goto out; + + req = (struct smb2_sess_setup_req *) sess_data->iov[0].iov_base; + req->hdr.SessionId = cpu_to_le64(ses->Suid); + + rc = build_ntlmssp_auth_blob(&ntlmssp_blob, &blob_length, + ses, server, + sess_data->nls_cp); + if (rc) { + cifs_dbg(FYI, "build_ntlmssp_auth_blob failed %d\n", rc); + goto out; + } + + if (use_spnego) { + /* BB eventually need to add this */ + cifs_dbg(VFS, "spnego not supported for SMB2 yet\n"); + rc = -EOPNOTSUPP; + goto out; + } + sess_data->iov[1].iov_base = ntlmssp_blob; + sess_data->iov[1].iov_len = blob_length; + + rc = SMB2_sess_sendreceive(sess_data); + if (rc) + goto out; + + rsp = (struct smb2_sess_setup_rsp *)sess_data->iov[0].iov_base; + + spin_lock(&ses->ses_lock); + is_binding = (ses->ses_status == SES_GOOD); + spin_unlock(&ses->ses_lock); + + /* keep existing ses id and flags if binding */ + if (!is_binding) { + ses->Suid = le64_to_cpu(rsp->hdr.SessionId); + ses->session_flags = le16_to_cpu(rsp->SessionFlags); + } + + rc = SMB2_sess_establish_session(sess_data); +#ifdef CONFIG_CIFS_DEBUG_DUMP_KEYS + if (ses->server->dialect < SMB30_PROT_ID) { + cifs_dbg(VFS, "%s: dumping generated SMB2 session keys\n", __func__); + /* + * The session id is opaque in terms of endianness, so we can't + * print it as a long long. we dump it as we got it on the wire + */ + cifs_dbg(VFS, "Session Id %*ph\n", (int)sizeof(ses->Suid), + &ses->Suid); + cifs_dbg(VFS, "Session Key %*ph\n", + SMB2_NTLMV2_SESSKEY_SIZE, ses->auth_key.response); + cifs_dbg(VFS, "Signing Key %*ph\n", + SMB3_SIGN_KEY_SIZE, ses->auth_key.response); + } +#endif +out: + kfree_sensitive(ntlmssp_blob); + SMB2_sess_free_buffer(sess_data); + kfree_sensitive(ses->ntlmssp); + ses->ntlmssp = NULL; + sess_data->result = rc; + sess_data->func = NULL; +} + +static int +SMB2_select_sec(struct SMB2_sess_data *sess_data) +{ + int type; + struct cifs_ses *ses = sess_data->ses; + struct TCP_Server_Info *server = sess_data->server; + + type = smb2_select_sectype(server, ses->sectype); + cifs_dbg(FYI, "sess setup type %d\n", type); + if (type == Unspecified) { + cifs_dbg(VFS, "Unable to select appropriate authentication method!\n"); + return -EINVAL; + } + + switch (type) { + case Kerberos: + sess_data->func = SMB2_auth_kerberos; + break; + case RawNTLMSSP: + sess_data->func = SMB2_sess_auth_rawntlmssp_negotiate; + break; + default: + cifs_dbg(VFS, "secType %d not supported!\n", type); + return -EOPNOTSUPP; + } + + return 0; +} + +int +SMB2_sess_setup(const unsigned int xid, struct cifs_ses *ses, + struct TCP_Server_Info *server, + const struct nls_table *nls_cp) +{ + int rc = 0; + struct SMB2_sess_data *sess_data; + + cifs_dbg(FYI, "Session Setup\n"); + + if (!server) { + WARN(1, "%s: server is NULL!\n", __func__); + return -EIO; + } + + sess_data = kzalloc(sizeof(struct SMB2_sess_data), GFP_KERNEL); + if (!sess_data) + return -ENOMEM; + + sess_data->xid = xid; + sess_data->ses = ses; + sess_data->server = server; + sess_data->buf0_type = CIFS_NO_BUFFER; + sess_data->nls_cp = (struct nls_table *) nls_cp; + sess_data->previous_session = ses->Suid; + + rc = SMB2_select_sec(sess_data); + if (rc) + goto out; + + /* + * Initialize the session hash with the server one. + */ + memcpy(ses->preauth_sha_hash, server->preauth_sha_hash, + SMB2_PREAUTH_HASH_SIZE); + + while (sess_data->func) + sess_data->func(sess_data); + + if ((ses->session_flags & SMB2_SESSION_FLAG_IS_GUEST) && (ses->sign)) + cifs_server_dbg(VFS, "signing requested but authenticated as guest\n"); + rc = sess_data->result; +out: + kfree_sensitive(sess_data); + return rc; +} + +int +SMB2_logoff(const unsigned int xid, struct cifs_ses *ses) +{ + struct smb_rqst rqst; + struct smb2_logoff_req *req; /* response is also trivial struct */ + int rc = 0; + struct TCP_Server_Info *server; + int flags = 0; + unsigned int total_len; + struct kvec iov[1]; + struct kvec rsp_iov; + int resp_buf_type; + + cifs_dbg(FYI, "disconnect session %p\n", ses); + + if (ses && (ses->server)) + server = ses->server; + else + return -EIO; + + /* no need to send SMB logoff if uid already closed due to reconnect */ + spin_lock(&ses->chan_lock); + if (CIFS_ALL_CHANS_NEED_RECONNECT(ses)) { + spin_unlock(&ses->chan_lock); + goto smb2_session_already_dead; + } + spin_unlock(&ses->chan_lock); + + rc = smb2_plain_req_init(SMB2_LOGOFF, NULL, ses->server, + (void **) &req, &total_len); + if (rc) + return rc; + + /* since no tcon, smb2_init can not do this, so do here */ + req->hdr.SessionId = cpu_to_le64(ses->Suid); + + if (ses->session_flags & SMB2_SESSION_FLAG_ENCRYPT_DATA) + flags |= CIFS_TRANSFORM_REQ; + else if (server->sign) + req->hdr.Flags |= SMB2_FLAGS_SIGNED; + + flags |= CIFS_NO_RSP_BUF; + + iov[0].iov_base = (char *)req; + iov[0].iov_len = total_len; + + memset(&rqst, 0, sizeof(struct smb_rqst)); + rqst.rq_iov = iov; + rqst.rq_nvec = 1; + + rc = cifs_send_recv(xid, ses, ses->server, + &rqst, &resp_buf_type, flags, &rsp_iov); + cifs_small_buf_release(req); + /* + * No tcon so can't do + * cifs_stats_inc(&tcon->stats.smb2_stats.smb2_com_fail[SMB2...]); + */ + +smb2_session_already_dead: + return rc; +} + +static inline void cifs_stats_fail_inc(struct cifs_tcon *tcon, uint16_t code) +{ + cifs_stats_inc(&tcon->stats.smb2_stats.smb2_com_failed[code]); +} + +#define MAX_SHARENAME_LENGTH (255 /* server */ + 80 /* share */ + 1 /* NULL */) + +/* These are similar values to what Windows uses */ +static inline void init_copy_chunk_defaults(struct cifs_tcon *tcon) +{ + tcon->max_chunks = 256; + tcon->max_bytes_chunk = 1048576; + tcon->max_bytes_copy = 16777216; +} + +int +SMB2_tcon(const unsigned int xid, struct cifs_ses *ses, const char *tree, + struct cifs_tcon *tcon, const struct nls_table *cp) +{ + struct smb_rqst rqst; + struct smb2_tree_connect_req *req; + struct smb2_tree_connect_rsp *rsp = NULL; + struct kvec iov[2]; + struct kvec rsp_iov = { NULL, 0 }; + int rc = 0; + int resp_buftype; + int unc_path_len; + __le16 *unc_path = NULL; + int flags = 0; + unsigned int total_len; + struct TCP_Server_Info *server; + + /* always use master channel */ + server = ses->server; + + cifs_dbg(FYI, "TCON\n"); + + if (!server || !tree) + return -EIO; + + unc_path = kmalloc(MAX_SHARENAME_LENGTH * 2, GFP_KERNEL); + if (unc_path == NULL) + return -ENOMEM; + + unc_path_len = cifs_strtoUTF16(unc_path, tree, strlen(tree), cp) + 1; + unc_path_len *= 2; + if (unc_path_len < 2) { + kfree(unc_path); + return -EINVAL; + } + + /* SMB2 TREE_CONNECT request must be called with TreeId == 0 */ + tcon->tid = 0; + atomic_set(&tcon->num_remote_opens, 0); + rc = smb2_plain_req_init(SMB2_TREE_CONNECT, tcon, server, + (void **) &req, &total_len); + if (rc) { + kfree(unc_path); + return rc; + } + + if (smb3_encryption_required(tcon)) + flags |= CIFS_TRANSFORM_REQ; + + iov[0].iov_base = (char *)req; + /* 1 for pad */ + iov[0].iov_len = total_len - 1; + + /* Testing shows that buffer offset must be at location of Buffer[0] */ + req->PathOffset = cpu_to_le16(sizeof(struct smb2_tree_connect_req) + - 1 /* pad */); + req->PathLength = cpu_to_le16(unc_path_len - 2); + iov[1].iov_base = unc_path; + iov[1].iov_len = unc_path_len; + + /* + * 3.11 tcon req must be signed if not encrypted. See MS-SMB2 3.2.4.1.1 + * unless it is guest or anonymous user. See MS-SMB2 3.2.5.3.1 + * (Samba servers don't always set the flag so also check if null user) + */ + if ((server->dialect == SMB311_PROT_ID) && + !smb3_encryption_required(tcon) && + !(ses->session_flags & + (SMB2_SESSION_FLAG_IS_GUEST|SMB2_SESSION_FLAG_IS_NULL)) && + ((ses->user_name != NULL) || (ses->sectype == Kerberos))) + req->hdr.Flags |= SMB2_FLAGS_SIGNED; + + memset(&rqst, 0, sizeof(struct smb_rqst)); + rqst.rq_iov = iov; + rqst.rq_nvec = 2; + + /* Need 64 for max size write so ask for more in case not there yet */ + req->hdr.CreditRequest = cpu_to_le16(64); + + rc = cifs_send_recv(xid, ses, server, + &rqst, &resp_buftype, flags, &rsp_iov); + cifs_small_buf_release(req); + rsp = (struct smb2_tree_connect_rsp *)rsp_iov.iov_base; + trace_smb3_tcon(xid, tcon->tid, ses->Suid, tree, rc); + if ((rc != 0) || (rsp == NULL)) { + cifs_stats_fail_inc(tcon, SMB2_TREE_CONNECT_HE); + tcon->need_reconnect = true; + goto tcon_error_exit; + } + + switch (rsp->ShareType) { + case SMB2_SHARE_TYPE_DISK: + cifs_dbg(FYI, "connection to disk share\n"); + break; + case SMB2_SHARE_TYPE_PIPE: + tcon->pipe = true; + cifs_dbg(FYI, "connection to pipe share\n"); + break; + case SMB2_SHARE_TYPE_PRINT: + tcon->print = true; + cifs_dbg(FYI, "connection to printer\n"); + break; + default: + cifs_server_dbg(VFS, "unknown share type %d\n", rsp->ShareType); + rc = -EOPNOTSUPP; + goto tcon_error_exit; + } + + tcon->share_flags = le32_to_cpu(rsp->ShareFlags); + tcon->capabilities = rsp->Capabilities; /* we keep caps little endian */ + tcon->maximal_access = le32_to_cpu(rsp->MaximalAccess); + tcon->tid = le32_to_cpu(rsp->hdr.Id.SyncId.TreeId); + strscpy(tcon->tree_name, tree, sizeof(tcon->tree_name)); + + if ((rsp->Capabilities & SMB2_SHARE_CAP_DFS) && + ((tcon->share_flags & SHI1005_FLAGS_DFS) == 0)) + cifs_tcon_dbg(VFS, "DFS capability contradicts DFS flag\n"); + + if (tcon->seal && + !(server->capabilities & SMB2_GLOBAL_CAP_ENCRYPTION)) + cifs_tcon_dbg(VFS, "Encryption is requested but not supported\n"); + + init_copy_chunk_defaults(tcon); + if (server->ops->validate_negotiate) + rc = server->ops->validate_negotiate(xid, tcon); +tcon_exit: + + free_rsp_buf(resp_buftype, rsp); + kfree(unc_path); + return rc; + +tcon_error_exit: + if (rsp && rsp->hdr.Status == STATUS_BAD_NETWORK_NAME) + cifs_tcon_dbg(VFS, "BAD_NETWORK_NAME: %s\n", tree); + goto tcon_exit; +} + +int +SMB2_tdis(const unsigned int xid, struct cifs_tcon *tcon) +{ + struct smb_rqst rqst; + struct smb2_tree_disconnect_req *req; /* response is trivial */ + int rc = 0; + struct cifs_ses *ses = tcon->ses; + int flags = 0; + unsigned int total_len; + struct kvec iov[1]; + struct kvec rsp_iov; + int resp_buf_type; + + cifs_dbg(FYI, "Tree Disconnect\n"); + + if (!ses || !(ses->server)) + return -EIO; + + trace_smb3_tdis_enter(xid, tcon->tid, ses->Suid, tcon->tree_name); + spin_lock(&ses->chan_lock); + if ((tcon->need_reconnect) || + (CIFS_ALL_CHANS_NEED_RECONNECT(tcon->ses))) { + spin_unlock(&ses->chan_lock); + return 0; + } + spin_unlock(&ses->chan_lock); + + invalidate_all_cached_dirs(tcon); + + rc = smb2_plain_req_init(SMB2_TREE_DISCONNECT, tcon, ses->server, + (void **) &req, + &total_len); + if (rc) + return rc; + + if (smb3_encryption_required(tcon)) + flags |= CIFS_TRANSFORM_REQ; + + flags |= CIFS_NO_RSP_BUF; + + iov[0].iov_base = (char *)req; + iov[0].iov_len = total_len; + + memset(&rqst, 0, sizeof(struct smb_rqst)); + rqst.rq_iov = iov; + rqst.rq_nvec = 1; + + rc = cifs_send_recv(xid, ses, ses->server, + &rqst, &resp_buf_type, flags, &rsp_iov); + cifs_small_buf_release(req); + if (rc) { + cifs_stats_fail_inc(tcon, SMB2_TREE_DISCONNECT_HE); + trace_smb3_tdis_err(xid, tcon->tid, ses->Suid, rc); + } + trace_smb3_tdis_done(xid, tcon->tid, ses->Suid); + + return rc; +} + + +static struct create_durable * +create_durable_buf(void) +{ + struct create_durable *buf; + + buf = kzalloc(sizeof(struct create_durable), GFP_KERNEL); + if (!buf) + return NULL; + + buf->ccontext.DataOffset = cpu_to_le16(offsetof + (struct create_durable, Data)); + buf->ccontext.DataLength = cpu_to_le32(16); + buf->ccontext.NameOffset = cpu_to_le16(offsetof + (struct create_durable, Name)); + buf->ccontext.NameLength = cpu_to_le16(4); + /* SMB2_CREATE_DURABLE_HANDLE_REQUEST is "DHnQ" */ + buf->Name[0] = 'D'; + buf->Name[1] = 'H'; + buf->Name[2] = 'n'; + buf->Name[3] = 'Q'; + return buf; +} + +static struct create_durable * +create_reconnect_durable_buf(struct cifs_fid *fid) +{ + struct create_durable *buf; + + buf = kzalloc(sizeof(struct create_durable), GFP_KERNEL); + if (!buf) + return NULL; + + buf->ccontext.DataOffset = cpu_to_le16(offsetof + (struct create_durable, Data)); + buf->ccontext.DataLength = cpu_to_le32(16); + buf->ccontext.NameOffset = cpu_to_le16(offsetof + (struct create_durable, Name)); + buf->ccontext.NameLength = cpu_to_le16(4); + buf->Data.Fid.PersistentFileId = fid->persistent_fid; + buf->Data.Fid.VolatileFileId = fid->volatile_fid; + /* SMB2_CREATE_DURABLE_HANDLE_RECONNECT is "DHnC" */ + buf->Name[0] = 'D'; + buf->Name[1] = 'H'; + buf->Name[2] = 'n'; + buf->Name[3] = 'C'; + return buf; +} + +static void +parse_query_id_ctxt(struct create_context *cc, struct smb2_file_all_info *buf) +{ + struct create_on_disk_id *pdisk_id = (struct create_on_disk_id *)cc; + + cifs_dbg(FYI, "parse query id context 0x%llx 0x%llx\n", + pdisk_id->DiskFileId, pdisk_id->VolumeId); + buf->IndexNumber = pdisk_id->DiskFileId; +} + +static void +parse_posix_ctxt(struct create_context *cc, struct smb2_file_all_info *info, + struct create_posix_rsp *posix) +{ + int sid_len; + u8 *beg = (u8 *)cc + le16_to_cpu(cc->DataOffset); + u8 *end = beg + le32_to_cpu(cc->DataLength); + u8 *sid; + + memset(posix, 0, sizeof(*posix)); + + posix->nlink = le32_to_cpu(*(__le32 *)(beg + 0)); + posix->reparse_tag = le32_to_cpu(*(__le32 *)(beg + 4)); + posix->mode = le32_to_cpu(*(__le32 *)(beg + 8)); + + sid = beg + 12; + sid_len = posix_info_sid_size(sid, end); + if (sid_len < 0) { + cifs_dbg(VFS, "bad owner sid in posix create response\n"); + return; + } + memcpy(&posix->owner, sid, sid_len); + + sid = sid + sid_len; + sid_len = posix_info_sid_size(sid, end); + if (sid_len < 0) { + cifs_dbg(VFS, "bad group sid in posix create response\n"); + return; + } + memcpy(&posix->group, sid, sid_len); + + cifs_dbg(FYI, "nlink=%d mode=%o reparse_tag=%x\n", + posix->nlink, posix->mode, posix->reparse_tag); +} + +void +smb2_parse_contexts(struct TCP_Server_Info *server, + struct smb2_create_rsp *rsp, + unsigned int *epoch, char *lease_key, __u8 *oplock, + struct smb2_file_all_info *buf, + struct create_posix_rsp *posix) +{ + char *data_offset; + struct create_context *cc; + unsigned int next; + unsigned int remaining; + char *name; + static const char smb3_create_tag_posix[] = { + 0x93, 0xAD, 0x25, 0x50, 0x9C, + 0xB4, 0x11, 0xE7, 0xB4, 0x23, 0x83, + 0xDE, 0x96, 0x8B, 0xCD, 0x7C + }; + + *oplock = 0; + data_offset = (char *)rsp + le32_to_cpu(rsp->CreateContextsOffset); + remaining = le32_to_cpu(rsp->CreateContextsLength); + cc = (struct create_context *)data_offset; + + /* Initialize inode number to 0 in case no valid data in qfid context */ + if (buf) + buf->IndexNumber = 0; + + while (remaining >= sizeof(struct create_context)) { + name = le16_to_cpu(cc->NameOffset) + (char *)cc; + if (le16_to_cpu(cc->NameLength) == 4 && + strncmp(name, SMB2_CREATE_REQUEST_LEASE, 4) == 0) + *oplock = server->ops->parse_lease_buf(cc, epoch, + lease_key); + else if (buf && (le16_to_cpu(cc->NameLength) == 4) && + strncmp(name, SMB2_CREATE_QUERY_ON_DISK_ID, 4) == 0) + parse_query_id_ctxt(cc, buf); + else if ((le16_to_cpu(cc->NameLength) == 16)) { + if (posix && + memcmp(name, smb3_create_tag_posix, 16) == 0) + parse_posix_ctxt(cc, buf, posix); + } + /* else { + cifs_dbg(FYI, "Context not matched with len %d\n", + le16_to_cpu(cc->NameLength)); + cifs_dump_mem("Cctxt name: ", name, 4); + } */ + + next = le32_to_cpu(cc->Next); + if (!next) + break; + remaining -= next; + cc = (struct create_context *)((char *)cc + next); + } + + if (rsp->OplockLevel != SMB2_OPLOCK_LEVEL_LEASE) + *oplock = rsp->OplockLevel; + + return; +} + +static int +add_lease_context(struct TCP_Server_Info *server, struct kvec *iov, + unsigned int *num_iovec, u8 *lease_key, __u8 *oplock) +{ + struct smb2_create_req *req = iov[0].iov_base; + unsigned int num = *num_iovec; + + iov[num].iov_base = server->ops->create_lease_buf(lease_key, *oplock); + if (iov[num].iov_base == NULL) + return -ENOMEM; + iov[num].iov_len = server->vals->create_lease_size; + req->RequestedOplockLevel = SMB2_OPLOCK_LEVEL_LEASE; + if (!req->CreateContextsOffset) + req->CreateContextsOffset = cpu_to_le32( + sizeof(struct smb2_create_req) + + iov[num - 1].iov_len); + le32_add_cpu(&req->CreateContextsLength, + server->vals->create_lease_size); + *num_iovec = num + 1; + return 0; +} + +static struct create_durable_v2 * +create_durable_v2_buf(struct cifs_open_parms *oparms) +{ + struct cifs_fid *pfid = oparms->fid; + struct create_durable_v2 *buf; + + buf = kzalloc(sizeof(struct create_durable_v2), GFP_KERNEL); + if (!buf) + return NULL; + + buf->ccontext.DataOffset = cpu_to_le16(offsetof + (struct create_durable_v2, dcontext)); + buf->ccontext.DataLength = cpu_to_le32(sizeof(struct durable_context_v2)); + buf->ccontext.NameOffset = cpu_to_le16(offsetof + (struct create_durable_v2, Name)); + buf->ccontext.NameLength = cpu_to_le16(4); + + /* + * NB: Handle timeout defaults to 0, which allows server to choose + * (most servers default to 120 seconds) and most clients default to 0. + * This can be overridden at mount ("handletimeout=") if the user wants + * a different persistent (or resilient) handle timeout for all opens + * opens on a particular SMB3 mount. + */ + buf->dcontext.Timeout = cpu_to_le32(oparms->tcon->handle_timeout); + buf->dcontext.Flags = cpu_to_le32(SMB2_DHANDLE_FLAG_PERSISTENT); + generate_random_uuid(buf->dcontext.CreateGuid); + memcpy(pfid->create_guid, buf->dcontext.CreateGuid, 16); + + /* SMB2_CREATE_DURABLE_HANDLE_REQUEST is "DH2Q" */ + buf->Name[0] = 'D'; + buf->Name[1] = 'H'; + buf->Name[2] = '2'; + buf->Name[3] = 'Q'; + return buf; +} + +static struct create_durable_handle_reconnect_v2 * +create_reconnect_durable_v2_buf(struct cifs_fid *fid) +{ + struct create_durable_handle_reconnect_v2 *buf; + + buf = kzalloc(sizeof(struct create_durable_handle_reconnect_v2), + GFP_KERNEL); + if (!buf) + return NULL; + + buf->ccontext.DataOffset = + cpu_to_le16(offsetof(struct create_durable_handle_reconnect_v2, + dcontext)); + buf->ccontext.DataLength = + cpu_to_le32(sizeof(struct durable_reconnect_context_v2)); + buf->ccontext.NameOffset = + cpu_to_le16(offsetof(struct create_durable_handle_reconnect_v2, + Name)); + buf->ccontext.NameLength = cpu_to_le16(4); + + buf->dcontext.Fid.PersistentFileId = fid->persistent_fid; + buf->dcontext.Fid.VolatileFileId = fid->volatile_fid; + buf->dcontext.Flags = cpu_to_le32(SMB2_DHANDLE_FLAG_PERSISTENT); + memcpy(buf->dcontext.CreateGuid, fid->create_guid, 16); + + /* SMB2_CREATE_DURABLE_HANDLE_RECONNECT_V2 is "DH2C" */ + buf->Name[0] = 'D'; + buf->Name[1] = 'H'; + buf->Name[2] = '2'; + buf->Name[3] = 'C'; + return buf; +} + +static int +add_durable_v2_context(struct kvec *iov, unsigned int *num_iovec, + struct cifs_open_parms *oparms) +{ + struct smb2_create_req *req = iov[0].iov_base; + unsigned int num = *num_iovec; + + iov[num].iov_base = create_durable_v2_buf(oparms); + if (iov[num].iov_base == NULL) + return -ENOMEM; + iov[num].iov_len = sizeof(struct create_durable_v2); + if (!req->CreateContextsOffset) + req->CreateContextsOffset = + cpu_to_le32(sizeof(struct smb2_create_req) + + iov[1].iov_len); + le32_add_cpu(&req->CreateContextsLength, sizeof(struct create_durable_v2)); + *num_iovec = num + 1; + return 0; +} + +static int +add_durable_reconnect_v2_context(struct kvec *iov, unsigned int *num_iovec, + struct cifs_open_parms *oparms) +{ + struct smb2_create_req *req = iov[0].iov_base; + unsigned int num = *num_iovec; + + /* indicate that we don't need to relock the file */ + oparms->reconnect = false; + + iov[num].iov_base = create_reconnect_durable_v2_buf(oparms->fid); + if (iov[num].iov_base == NULL) + return -ENOMEM; + iov[num].iov_len = sizeof(struct create_durable_handle_reconnect_v2); + if (!req->CreateContextsOffset) + req->CreateContextsOffset = + cpu_to_le32(sizeof(struct smb2_create_req) + + iov[1].iov_len); + le32_add_cpu(&req->CreateContextsLength, + sizeof(struct create_durable_handle_reconnect_v2)); + *num_iovec = num + 1; + return 0; +} + +static int +add_durable_context(struct kvec *iov, unsigned int *num_iovec, + struct cifs_open_parms *oparms, bool use_persistent) +{ + struct smb2_create_req *req = iov[0].iov_base; + unsigned int num = *num_iovec; + + if (use_persistent) { + if (oparms->reconnect) + return add_durable_reconnect_v2_context(iov, num_iovec, + oparms); + else + return add_durable_v2_context(iov, num_iovec, oparms); + } + + if (oparms->reconnect) { + iov[num].iov_base = create_reconnect_durable_buf(oparms->fid); + /* indicate that we don't need to relock the file */ + oparms->reconnect = false; + } else + iov[num].iov_base = create_durable_buf(); + if (iov[num].iov_base == NULL) + return -ENOMEM; + iov[num].iov_len = sizeof(struct create_durable); + if (!req->CreateContextsOffset) + req->CreateContextsOffset = + cpu_to_le32(sizeof(struct smb2_create_req) + + iov[1].iov_len); + le32_add_cpu(&req->CreateContextsLength, sizeof(struct create_durable)); + *num_iovec = num + 1; + return 0; +} + +/* See MS-SMB2 2.2.13.2.7 */ +static struct crt_twarp_ctxt * +create_twarp_buf(__u64 timewarp) +{ + struct crt_twarp_ctxt *buf; + + buf = kzalloc(sizeof(struct crt_twarp_ctxt), GFP_KERNEL); + if (!buf) + return NULL; + + buf->ccontext.DataOffset = cpu_to_le16(offsetof + (struct crt_twarp_ctxt, Timestamp)); + buf->ccontext.DataLength = cpu_to_le32(8); + buf->ccontext.NameOffset = cpu_to_le16(offsetof + (struct crt_twarp_ctxt, Name)); + buf->ccontext.NameLength = cpu_to_le16(4); + /* SMB2_CREATE_TIMEWARP_TOKEN is "TWrp" */ + buf->Name[0] = 'T'; + buf->Name[1] = 'W'; + buf->Name[2] = 'r'; + buf->Name[3] = 'p'; + buf->Timestamp = cpu_to_le64(timewarp); + return buf; +} + +/* See MS-SMB2 2.2.13.2.7 */ +static int +add_twarp_context(struct kvec *iov, unsigned int *num_iovec, __u64 timewarp) +{ + struct smb2_create_req *req = iov[0].iov_base; + unsigned int num = *num_iovec; + + iov[num].iov_base = create_twarp_buf(timewarp); + if (iov[num].iov_base == NULL) + return -ENOMEM; + iov[num].iov_len = sizeof(struct crt_twarp_ctxt); + if (!req->CreateContextsOffset) + req->CreateContextsOffset = cpu_to_le32( + sizeof(struct smb2_create_req) + + iov[num - 1].iov_len); + le32_add_cpu(&req->CreateContextsLength, sizeof(struct crt_twarp_ctxt)); + *num_iovec = num + 1; + return 0; +} + +/* See See http://technet.microsoft.com/en-us/library/hh509017(v=ws.10).aspx */ +static void setup_owner_group_sids(char *buf) +{ + struct owner_group_sids *sids = (struct owner_group_sids *)buf; + + /* Populate the user ownership fields S-1-5-88-1 */ + sids->owner.Revision = 1; + sids->owner.NumAuth = 3; + sids->owner.Authority[5] = 5; + sids->owner.SubAuthorities[0] = cpu_to_le32(88); + sids->owner.SubAuthorities[1] = cpu_to_le32(1); + sids->owner.SubAuthorities[2] = cpu_to_le32(current_fsuid().val); + + /* Populate the group ownership fields S-1-5-88-2 */ + sids->group.Revision = 1; + sids->group.NumAuth = 3; + sids->group.Authority[5] = 5; + sids->group.SubAuthorities[0] = cpu_to_le32(88); + sids->group.SubAuthorities[1] = cpu_to_le32(2); + sids->group.SubAuthorities[2] = cpu_to_le32(current_fsgid().val); + + cifs_dbg(FYI, "owner S-1-5-88-1-%d, group S-1-5-88-2-%d\n", current_fsuid().val, current_fsgid().val); +} + +/* See MS-SMB2 2.2.13.2.2 and MS-DTYP 2.4.6 */ +static struct crt_sd_ctxt * +create_sd_buf(umode_t mode, bool set_owner, unsigned int *len) +{ + struct crt_sd_ctxt *buf; + __u8 *ptr, *aclptr; + unsigned int acelen, acl_size, ace_count; + unsigned int owner_offset = 0; + unsigned int group_offset = 0; + struct smb3_acl acl = {}; + + *len = round_up(sizeof(struct crt_sd_ctxt) + (sizeof(struct cifs_ace) * 4), 8); + + if (set_owner) { + /* sizeof(struct owner_group_sids) is already multiple of 8 so no need to round */ + *len += sizeof(struct owner_group_sids); + } + + buf = kzalloc(*len, GFP_KERNEL); + if (buf == NULL) + return buf; + + ptr = (__u8 *)&buf[1]; + if (set_owner) { + /* offset fields are from beginning of security descriptor not of create context */ + owner_offset = ptr - (__u8 *)&buf->sd; + buf->sd.OffsetOwner = cpu_to_le32(owner_offset); + group_offset = owner_offset + offsetof(struct owner_group_sids, group); + buf->sd.OffsetGroup = cpu_to_le32(group_offset); + + setup_owner_group_sids(ptr); + ptr += sizeof(struct owner_group_sids); + } else { + buf->sd.OffsetOwner = 0; + buf->sd.OffsetGroup = 0; + } + + buf->ccontext.DataOffset = cpu_to_le16(offsetof(struct crt_sd_ctxt, sd)); + buf->ccontext.NameOffset = cpu_to_le16(offsetof(struct crt_sd_ctxt, Name)); + buf->ccontext.NameLength = cpu_to_le16(4); + /* SMB2_CREATE_SD_BUFFER_TOKEN is "SecD" */ + buf->Name[0] = 'S'; + buf->Name[1] = 'e'; + buf->Name[2] = 'c'; + buf->Name[3] = 'D'; + buf->sd.Revision = 1; /* Must be one see MS-DTYP 2.4.6 */ + + /* + * ACL is "self relative" ie ACL is stored in contiguous block of memory + * and "DP" ie the DACL is present + */ + buf->sd.Control = cpu_to_le16(ACL_CONTROL_SR | ACL_CONTROL_DP); + + /* offset owner, group and Sbz1 and SACL are all zero */ + buf->sd.OffsetDacl = cpu_to_le32(ptr - (__u8 *)&buf->sd); + /* Ship the ACL for now. we will copy it into buf later. */ + aclptr = ptr; + ptr += sizeof(struct smb3_acl); + + /* create one ACE to hold the mode embedded in reserved special SID */ + acelen = setup_special_mode_ACE((struct cifs_ace *)ptr, (__u64)mode); + ptr += acelen; + acl_size = acelen + sizeof(struct smb3_acl); + ace_count = 1; + + if (set_owner) { + /* we do not need to reallocate buffer to add the two more ACEs. plenty of space */ + acelen = setup_special_user_owner_ACE((struct cifs_ace *)ptr); + ptr += acelen; + acl_size += acelen; + ace_count += 1; + } + + /* and one more ACE to allow access for authenticated users */ + acelen = setup_authusers_ACE((struct cifs_ace *)ptr); + ptr += acelen; + acl_size += acelen; + ace_count += 1; + + acl.AclRevision = ACL_REVISION; /* See 2.4.4.1 of MS-DTYP */ + acl.AclSize = cpu_to_le16(acl_size); + acl.AceCount = cpu_to_le16(ace_count); + /* acl.Sbz1 and Sbz2 MBZ so are not set here, but initialized above */ + memcpy(aclptr, &acl, sizeof(struct smb3_acl)); + + buf->ccontext.DataLength = cpu_to_le32(ptr - (__u8 *)&buf->sd); + *len = round_up((unsigned int)(ptr - (__u8 *)buf), 8); + + return buf; +} + +static int +add_sd_context(struct kvec *iov, unsigned int *num_iovec, umode_t mode, bool set_owner) +{ + struct smb2_create_req *req = iov[0].iov_base; + unsigned int num = *num_iovec; + unsigned int len = 0; + + iov[num].iov_base = create_sd_buf(mode, set_owner, &len); + if (iov[num].iov_base == NULL) + return -ENOMEM; + iov[num].iov_len = len; + if (!req->CreateContextsOffset) + req->CreateContextsOffset = cpu_to_le32( + sizeof(struct smb2_create_req) + + iov[num - 1].iov_len); + le32_add_cpu(&req->CreateContextsLength, len); + *num_iovec = num + 1; + return 0; +} + +static struct crt_query_id_ctxt * +create_query_id_buf(void) +{ + struct crt_query_id_ctxt *buf; + + buf = kzalloc(sizeof(struct crt_query_id_ctxt), GFP_KERNEL); + if (!buf) + return NULL; + + buf->ccontext.DataOffset = cpu_to_le16(0); + buf->ccontext.DataLength = cpu_to_le32(0); + buf->ccontext.NameOffset = cpu_to_le16(offsetof + (struct crt_query_id_ctxt, Name)); + buf->ccontext.NameLength = cpu_to_le16(4); + /* SMB2_CREATE_QUERY_ON_DISK_ID is "QFid" */ + buf->Name[0] = 'Q'; + buf->Name[1] = 'F'; + buf->Name[2] = 'i'; + buf->Name[3] = 'd'; + return buf; +} + +/* See MS-SMB2 2.2.13.2.9 */ +static int +add_query_id_context(struct kvec *iov, unsigned int *num_iovec) +{ + struct smb2_create_req *req = iov[0].iov_base; + unsigned int num = *num_iovec; + + iov[num].iov_base = create_query_id_buf(); + if (iov[num].iov_base == NULL) + return -ENOMEM; + iov[num].iov_len = sizeof(struct crt_query_id_ctxt); + if (!req->CreateContextsOffset) + req->CreateContextsOffset = cpu_to_le32( + sizeof(struct smb2_create_req) + + iov[num - 1].iov_len); + le32_add_cpu(&req->CreateContextsLength, sizeof(struct crt_query_id_ctxt)); + *num_iovec = num + 1; + return 0; +} + +static int +alloc_path_with_tree_prefix(__le16 **out_path, int *out_size, int *out_len, + const char *treename, const __le16 *path) +{ + int treename_len, path_len; + struct nls_table *cp; + const __le16 sep[] = {cpu_to_le16('\\'), cpu_to_le16(0x0000)}; + + /* + * skip leading "\\" + */ + treename_len = strlen(treename); + if (treename_len < 2 || !(treename[0] == '\\' && treename[1] == '\\')) + return -EINVAL; + + treename += 2; + treename_len -= 2; + + path_len = UniStrnlen((wchar_t *)path, PATH_MAX); + + /* make room for one path separator only if @path isn't empty */ + *out_len = treename_len + (path[0] ? 1 : 0) + path_len; + + /* + * final path needs to be 8-byte aligned as specified in + * MS-SMB2 2.2.13 SMB2 CREATE Request. + */ + *out_size = round_up(*out_len * sizeof(__le16), 8); + *out_path = kzalloc(*out_size + sizeof(__le16) /* null */, GFP_KERNEL); + if (!*out_path) + return -ENOMEM; + + cp = load_nls_default(); + cifs_strtoUTF16(*out_path, treename, treename_len, cp); + + /* Do not append the separator if the path is empty */ + if (path[0] != cpu_to_le16(0x0000)) { + UniStrcat(*out_path, sep); + UniStrcat(*out_path, path); + } + + unload_nls(cp); + + return 0; +} + +int smb311_posix_mkdir(const unsigned int xid, struct inode *inode, + umode_t mode, struct cifs_tcon *tcon, + const char *full_path, + struct cifs_sb_info *cifs_sb) +{ + struct smb_rqst rqst; + struct smb2_create_req *req; + struct smb2_create_rsp *rsp = NULL; + struct cifs_ses *ses = tcon->ses; + struct kvec iov[3]; /* make sure at least one for each open context */ + struct kvec rsp_iov = {NULL, 0}; + int resp_buftype; + int uni_path_len; + __le16 *copy_path = NULL; + int copy_size; + int rc = 0; + unsigned int n_iov = 2; + __u32 file_attributes = 0; + char *pc_buf = NULL; + int flags = 0; + unsigned int total_len; + __le16 *utf16_path = NULL; + struct TCP_Server_Info *server = cifs_pick_channel(ses); + + cifs_dbg(FYI, "mkdir\n"); + + /* resource #1: path allocation */ + utf16_path = cifs_convert_path_to_utf16(full_path, cifs_sb); + if (!utf16_path) + return -ENOMEM; + + if (!ses || !server) { + rc = -EIO; + goto err_free_path; + } + + /* resource #2: request */ + rc = smb2_plain_req_init(SMB2_CREATE, tcon, server, + (void **) &req, &total_len); + if (rc) + goto err_free_path; + + + if (smb3_encryption_required(tcon)) + flags |= CIFS_TRANSFORM_REQ; + + req->ImpersonationLevel = IL_IMPERSONATION; + req->DesiredAccess = cpu_to_le32(FILE_WRITE_ATTRIBUTES); + /* File attributes ignored on open (used in create though) */ + req->FileAttributes = cpu_to_le32(file_attributes); + req->ShareAccess = FILE_SHARE_ALL_LE; + req->CreateDisposition = cpu_to_le32(FILE_CREATE); + req->CreateOptions = cpu_to_le32(CREATE_NOT_FILE); + + iov[0].iov_base = (char *)req; + /* -1 since last byte is buf[0] which is sent below (path) */ + iov[0].iov_len = total_len - 1; + + req->NameOffset = cpu_to_le16(sizeof(struct smb2_create_req)); + + /* [MS-SMB2] 2.2.13 NameOffset: + * If SMB2_FLAGS_DFS_OPERATIONS is set in the Flags field of + * the SMB2 header, the file name includes a prefix that will + * be processed during DFS name normalization as specified in + * section 3.3.5.9. Otherwise, the file name is relative to + * the share that is identified by the TreeId in the SMB2 + * header. + */ + if (tcon->share_flags & SHI1005_FLAGS_DFS) { + int name_len; + + req->hdr.Flags |= SMB2_FLAGS_DFS_OPERATIONS; + rc = alloc_path_with_tree_prefix(©_path, ©_size, + &name_len, + tcon->tree_name, utf16_path); + if (rc) + goto err_free_req; + + req->NameLength = cpu_to_le16(name_len * 2); + uni_path_len = copy_size; + /* free before overwriting resource */ + kfree(utf16_path); + utf16_path = copy_path; + } else { + uni_path_len = (2 * UniStrnlen((wchar_t *)utf16_path, PATH_MAX)) + 2; + /* MUST set path len (NameLength) to 0 opening root of share */ + req->NameLength = cpu_to_le16(uni_path_len - 2); + if (uni_path_len % 8 != 0) { + copy_size = roundup(uni_path_len, 8); + copy_path = kzalloc(copy_size, GFP_KERNEL); + if (!copy_path) { + rc = -ENOMEM; + goto err_free_req; + } + memcpy((char *)copy_path, (const char *)utf16_path, + uni_path_len); + uni_path_len = copy_size; + /* free before overwriting resource */ + kfree(utf16_path); + utf16_path = copy_path; + } + } + + iov[1].iov_len = uni_path_len; + iov[1].iov_base = utf16_path; + req->RequestedOplockLevel = SMB2_OPLOCK_LEVEL_NONE; + + if (tcon->posix_extensions) { + /* resource #3: posix buf */ + rc = add_posix_context(iov, &n_iov, mode); + if (rc) + goto err_free_req; + pc_buf = iov[n_iov-1].iov_base; + } + + + memset(&rqst, 0, sizeof(struct smb_rqst)); + rqst.rq_iov = iov; + rqst.rq_nvec = n_iov; + + /* no need to inc num_remote_opens because we close it just below */ + trace_smb3_posix_mkdir_enter(xid, tcon->tid, ses->Suid, full_path, CREATE_NOT_FILE, + FILE_WRITE_ATTRIBUTES); + /* resource #4: response buffer */ + rc = cifs_send_recv(xid, ses, server, + &rqst, &resp_buftype, flags, &rsp_iov); + if (rc) { + cifs_stats_fail_inc(tcon, SMB2_CREATE_HE); + trace_smb3_posix_mkdir_err(xid, tcon->tid, ses->Suid, + CREATE_NOT_FILE, + FILE_WRITE_ATTRIBUTES, rc); + goto err_free_rsp_buf; + } + + /* + * Although unlikely to be possible for rsp to be null and rc not set, + * adding check below is slightly safer long term (and quiets Coverity + * warning) + */ + rsp = (struct smb2_create_rsp *)rsp_iov.iov_base; + if (rsp == NULL) { + rc = -EIO; + kfree(pc_buf); + goto err_free_req; + } + + trace_smb3_posix_mkdir_done(xid, rsp->PersistentFileId, tcon->tid, ses->Suid, + CREATE_NOT_FILE, FILE_WRITE_ATTRIBUTES); + + SMB2_close(xid, tcon, rsp->PersistentFileId, rsp->VolatileFileId); + + /* Eventually save off posix specific response info and timestaps */ + +err_free_rsp_buf: + free_rsp_buf(resp_buftype, rsp); + kfree(pc_buf); +err_free_req: + cifs_small_buf_release(req); +err_free_path: + kfree(utf16_path); + return rc; +} + +int +SMB2_open_init(struct cifs_tcon *tcon, struct TCP_Server_Info *server, + struct smb_rqst *rqst, __u8 *oplock, + struct cifs_open_parms *oparms, __le16 *path) +{ + struct smb2_create_req *req; + unsigned int n_iov = 2; + __u32 file_attributes = 0; + int copy_size; + int uni_path_len; + unsigned int total_len; + struct kvec *iov = rqst->rq_iov; + __le16 *copy_path; + int rc; + + rc = smb2_plain_req_init(SMB2_CREATE, tcon, server, + (void **) &req, &total_len); + if (rc) + return rc; + + iov[0].iov_base = (char *)req; + /* -1 since last byte is buf[0] which is sent below (path) */ + iov[0].iov_len = total_len - 1; + + if (oparms->create_options & CREATE_OPTION_READONLY) + file_attributes |= ATTR_READONLY; + if (oparms->create_options & CREATE_OPTION_SPECIAL) + file_attributes |= ATTR_SYSTEM; + + req->ImpersonationLevel = IL_IMPERSONATION; + req->DesiredAccess = cpu_to_le32(oparms->desired_access); + /* File attributes ignored on open (used in create though) */ + req->FileAttributes = cpu_to_le32(file_attributes); + req->ShareAccess = FILE_SHARE_ALL_LE; + + req->CreateDisposition = cpu_to_le32(oparms->disposition); + req->CreateOptions = cpu_to_le32(oparms->create_options & CREATE_OPTIONS_MASK); + req->NameOffset = cpu_to_le16(sizeof(struct smb2_create_req)); + + /* [MS-SMB2] 2.2.13 NameOffset: + * If SMB2_FLAGS_DFS_OPERATIONS is set in the Flags field of + * the SMB2 header, the file name includes a prefix that will + * be processed during DFS name normalization as specified in + * section 3.3.5.9. Otherwise, the file name is relative to + * the share that is identified by the TreeId in the SMB2 + * header. + */ + if (tcon->share_flags & SHI1005_FLAGS_DFS) { + int name_len; + + req->hdr.Flags |= SMB2_FLAGS_DFS_OPERATIONS; + rc = alloc_path_with_tree_prefix(©_path, ©_size, + &name_len, + tcon->tree_name, path); + if (rc) + return rc; + req->NameLength = cpu_to_le16(name_len * 2); + uni_path_len = copy_size; + path = copy_path; + } else { + uni_path_len = (2 * UniStrnlen((wchar_t *)path, PATH_MAX)) + 2; + /* MUST set path len (NameLength) to 0 opening root of share */ + req->NameLength = cpu_to_le16(uni_path_len - 2); + copy_size = round_up(uni_path_len, 8); + copy_path = kzalloc(copy_size, GFP_KERNEL); + if (!copy_path) + return -ENOMEM; + memcpy((char *)copy_path, (const char *)path, + uni_path_len); + uni_path_len = copy_size; + path = copy_path; + } + + iov[1].iov_len = uni_path_len; + iov[1].iov_base = path; + + if ((!server->oplocks) || (tcon->no_lease)) + *oplock = SMB2_OPLOCK_LEVEL_NONE; + + if (!(server->capabilities & SMB2_GLOBAL_CAP_LEASING) || + *oplock == SMB2_OPLOCK_LEVEL_NONE) + req->RequestedOplockLevel = *oplock; + else if (!(server->capabilities & SMB2_GLOBAL_CAP_DIRECTORY_LEASING) && + (oparms->create_options & CREATE_NOT_FILE)) + req->RequestedOplockLevel = *oplock; /* no srv lease support */ + else { + rc = add_lease_context(server, iov, &n_iov, + oparms->fid->lease_key, oplock); + if (rc) + return rc; + } + + if (*oplock == SMB2_OPLOCK_LEVEL_BATCH) { + /* need to set Next field of lease context if we request it */ + if (server->capabilities & SMB2_GLOBAL_CAP_LEASING) { + struct create_context *ccontext = + (struct create_context *)iov[n_iov-1].iov_base; + ccontext->Next = + cpu_to_le32(server->vals->create_lease_size); + } + + rc = add_durable_context(iov, &n_iov, oparms, + tcon->use_persistent); + if (rc) + return rc; + } + + if (tcon->posix_extensions) { + if (n_iov > 2) { + struct create_context *ccontext = + (struct create_context *)iov[n_iov-1].iov_base; + ccontext->Next = + cpu_to_le32(iov[n_iov-1].iov_len); + } + + rc = add_posix_context(iov, &n_iov, oparms->mode); + if (rc) + return rc; + } + + if (tcon->snapshot_time) { + cifs_dbg(FYI, "adding snapshot context\n"); + if (n_iov > 2) { + struct create_context *ccontext = + (struct create_context *)iov[n_iov-1].iov_base; + ccontext->Next = + cpu_to_le32(iov[n_iov-1].iov_len); + } + + rc = add_twarp_context(iov, &n_iov, tcon->snapshot_time); + if (rc) + return rc; + } + + if ((oparms->disposition != FILE_OPEN) && (oparms->cifs_sb)) { + bool set_mode; + bool set_owner; + + if ((oparms->cifs_sb->mnt_cifs_flags & CIFS_MOUNT_MODE_FROM_SID) && + (oparms->mode != ACL_NO_MODE)) + set_mode = true; + else { + set_mode = false; + oparms->mode = ACL_NO_MODE; + } + + if (oparms->cifs_sb->mnt_cifs_flags & CIFS_MOUNT_UID_FROM_ACL) + set_owner = true; + else + set_owner = false; + + if (set_owner | set_mode) { + if (n_iov > 2) { + struct create_context *ccontext = + (struct create_context *)iov[n_iov-1].iov_base; + ccontext->Next = cpu_to_le32(iov[n_iov-1].iov_len); + } + + cifs_dbg(FYI, "add sd with mode 0x%x\n", oparms->mode); + rc = add_sd_context(iov, &n_iov, oparms->mode, set_owner); + if (rc) + return rc; + } + } + + if (n_iov > 2) { + struct create_context *ccontext = + (struct create_context *)iov[n_iov-1].iov_base; + ccontext->Next = cpu_to_le32(iov[n_iov-1].iov_len); + } + add_query_id_context(iov, &n_iov); + + rqst->rq_nvec = n_iov; + return 0; +} + +/* rq_iov[0] is the request and is released by cifs_small_buf_release(). + * All other vectors are freed by kfree(). + */ +void +SMB2_open_free(struct smb_rqst *rqst) +{ + int i; + + if (rqst && rqst->rq_iov) { + cifs_small_buf_release(rqst->rq_iov[0].iov_base); + for (i = 1; i < rqst->rq_nvec; i++) + if (rqst->rq_iov[i].iov_base != smb2_padding) + kfree(rqst->rq_iov[i].iov_base); + } +} + +int +SMB2_open(const unsigned int xid, struct cifs_open_parms *oparms, __le16 *path, + __u8 *oplock, struct smb2_file_all_info *buf, + struct create_posix_rsp *posix, + struct kvec *err_iov, int *buftype) +{ + struct smb_rqst rqst; + struct smb2_create_rsp *rsp = NULL; + struct cifs_tcon *tcon = oparms->tcon; + struct cifs_ses *ses = tcon->ses; + struct TCP_Server_Info *server = cifs_pick_channel(ses); + struct kvec iov[SMB2_CREATE_IOV_SIZE]; + struct kvec rsp_iov = {NULL, 0}; + int resp_buftype = CIFS_NO_BUFFER; + int rc = 0; + int flags = 0; + + cifs_dbg(FYI, "create/open\n"); + if (!ses || !server) + return -EIO; + + if (smb3_encryption_required(tcon)) + flags |= CIFS_TRANSFORM_REQ; + + memset(&rqst, 0, sizeof(struct smb_rqst)); + memset(&iov, 0, sizeof(iov)); + rqst.rq_iov = iov; + rqst.rq_nvec = SMB2_CREATE_IOV_SIZE; + + rc = SMB2_open_init(tcon, server, + &rqst, oplock, oparms, path); + if (rc) + goto creat_exit; + + trace_smb3_open_enter(xid, tcon->tid, tcon->ses->Suid, oparms->path, + oparms->create_options, oparms->desired_access); + + rc = cifs_send_recv(xid, ses, server, + &rqst, &resp_buftype, flags, + &rsp_iov); + rsp = (struct smb2_create_rsp *)rsp_iov.iov_base; + + if (rc != 0) { + cifs_stats_fail_inc(tcon, SMB2_CREATE_HE); + if (err_iov && rsp) { + *err_iov = rsp_iov; + *buftype = resp_buftype; + resp_buftype = CIFS_NO_BUFFER; + rsp = NULL; + } + trace_smb3_open_err(xid, tcon->tid, ses->Suid, + oparms->create_options, oparms->desired_access, rc); + if (rc == -EREMCHG) { + pr_warn_once("server share %s deleted\n", + tcon->tree_name); + tcon->need_reconnect = true; + } + goto creat_exit; + } else if (rsp == NULL) /* unlikely to happen, but safer to check */ + goto creat_exit; + else + trace_smb3_open_done(xid, rsp->PersistentFileId, tcon->tid, ses->Suid, + oparms->create_options, oparms->desired_access); + + atomic_inc(&tcon->num_remote_opens); + oparms->fid->persistent_fid = rsp->PersistentFileId; + oparms->fid->volatile_fid = rsp->VolatileFileId; + oparms->fid->access = oparms->desired_access; +#ifdef CONFIG_CIFS_DEBUG2 + oparms->fid->mid = le64_to_cpu(rsp->hdr.MessageId); +#endif /* CIFS_DEBUG2 */ + + if (buf) { + buf->CreationTime = rsp->CreationTime; + buf->LastAccessTime = rsp->LastAccessTime; + buf->LastWriteTime = rsp->LastWriteTime; + buf->ChangeTime = rsp->ChangeTime; + buf->AllocationSize = rsp->AllocationSize; + buf->EndOfFile = rsp->EndofFile; + buf->Attributes = rsp->FileAttributes; + buf->NumberOfLinks = cpu_to_le32(1); + buf->DeletePending = 0; + } + + + smb2_parse_contexts(server, rsp, &oparms->fid->epoch, + oparms->fid->lease_key, oplock, buf, posix); +creat_exit: + SMB2_open_free(&rqst); + free_rsp_buf(resp_buftype, rsp); + return rc; +} + +int +SMB2_ioctl_init(struct cifs_tcon *tcon, struct TCP_Server_Info *server, + struct smb_rqst *rqst, + u64 persistent_fid, u64 volatile_fid, u32 opcode, + char *in_data, u32 indatalen, + __u32 max_response_size) +{ + struct smb2_ioctl_req *req; + struct kvec *iov = rqst->rq_iov; + unsigned int total_len; + int rc; + char *in_data_buf; + + rc = smb2_ioctl_req_init(opcode, tcon, server, + (void **) &req, &total_len); + if (rc) + return rc; + + if (indatalen) { + /* + * indatalen is usually small at a couple of bytes max, so + * just allocate through generic pool + */ + in_data_buf = kmemdup(in_data, indatalen, GFP_NOFS); + if (!in_data_buf) { + cifs_small_buf_release(req); + return -ENOMEM; + } + } + + req->CtlCode = cpu_to_le32(opcode); + req->PersistentFileId = persistent_fid; + req->VolatileFileId = volatile_fid; + + iov[0].iov_base = (char *)req; + /* + * If no input data, the size of ioctl struct in + * protocol spec still includes a 1 byte data buffer, + * but if input data passed to ioctl, we do not + * want to double count this, so we do not send + * the dummy one byte of data in iovec[0] if sending + * input data (in iovec[1]). + */ + if (indatalen) { + req->InputCount = cpu_to_le32(indatalen); + /* do not set InputOffset if no input data */ + req->InputOffset = + cpu_to_le32(offsetof(struct smb2_ioctl_req, Buffer)); + rqst->rq_nvec = 2; + iov[0].iov_len = total_len - 1; + iov[1].iov_base = in_data_buf; + iov[1].iov_len = indatalen; + } else { + rqst->rq_nvec = 1; + iov[0].iov_len = total_len; + } + + req->OutputOffset = 0; + req->OutputCount = 0; /* MBZ */ + + /* + * In most cases max_response_size is set to 16K (CIFSMaxBufSize) + * We Could increase default MaxOutputResponse, but that could require + * more credits. Windows typically sets this smaller, but for some + * ioctls it may be useful to allow server to send more. No point + * limiting what the server can send as long as fits in one credit + * We can not handle more than CIFS_MAX_BUF_SIZE yet but may want + * to increase this limit up in the future. + * Note that for snapshot queries that servers like Azure expect that + * the first query be minimal size (and just used to get the number/size + * of previous versions) so response size must be specified as EXACTLY + * sizeof(struct snapshot_array) which is 16 when rounded up to multiple + * of eight bytes. Currently that is the only case where we set max + * response size smaller. + */ + req->MaxOutputResponse = cpu_to_le32(max_response_size); + req->hdr.CreditCharge = + cpu_to_le16(DIV_ROUND_UP(max(indatalen, max_response_size), + SMB2_MAX_BUFFER_SIZE)); + /* always an FSCTL (for now) */ + req->Flags = cpu_to_le32(SMB2_0_IOCTL_IS_FSCTL); + + /* validate negotiate request must be signed - see MS-SMB2 3.2.5.5 */ + if (opcode == FSCTL_VALIDATE_NEGOTIATE_INFO) + req->hdr.Flags |= SMB2_FLAGS_SIGNED; + + return 0; +} + +void +SMB2_ioctl_free(struct smb_rqst *rqst) +{ + int i; + if (rqst && rqst->rq_iov) { + cifs_small_buf_release(rqst->rq_iov[0].iov_base); /* request */ + for (i = 1; i < rqst->rq_nvec; i++) + if (rqst->rq_iov[i].iov_base != smb2_padding) + kfree(rqst->rq_iov[i].iov_base); + } +} + + +/* + * SMB2 IOCTL is used for both IOCTLs and FSCTLs + */ +int +SMB2_ioctl(const unsigned int xid, struct cifs_tcon *tcon, u64 persistent_fid, + u64 volatile_fid, u32 opcode, char *in_data, u32 indatalen, + u32 max_out_data_len, char **out_data, + u32 *plen /* returned data len */) +{ + struct smb_rqst rqst; + struct smb2_ioctl_rsp *rsp = NULL; + struct cifs_ses *ses; + struct TCP_Server_Info *server; + struct kvec iov[SMB2_IOCTL_IOV_SIZE]; + struct kvec rsp_iov = {NULL, 0}; + int resp_buftype = CIFS_NO_BUFFER; + int rc = 0; + int flags = 0; + + cifs_dbg(FYI, "SMB2 IOCTL\n"); + + if (out_data != NULL) + *out_data = NULL; + + /* zero out returned data len, in case of error */ + if (plen) + *plen = 0; + + if (!tcon) + return -EIO; + + ses = tcon->ses; + if (!ses) + return -EIO; + + server = cifs_pick_channel(ses); + if (!server) + return -EIO; + + if (smb3_encryption_required(tcon)) + flags |= CIFS_TRANSFORM_REQ; + + memset(&rqst, 0, sizeof(struct smb_rqst)); + memset(&iov, 0, sizeof(iov)); + rqst.rq_iov = iov; + rqst.rq_nvec = SMB2_IOCTL_IOV_SIZE; + + rc = SMB2_ioctl_init(tcon, server, + &rqst, persistent_fid, volatile_fid, opcode, + in_data, indatalen, max_out_data_len); + if (rc) + goto ioctl_exit; + + rc = cifs_send_recv(xid, ses, server, + &rqst, &resp_buftype, flags, + &rsp_iov); + rsp = (struct smb2_ioctl_rsp *)rsp_iov.iov_base; + + if (rc != 0) + trace_smb3_fsctl_err(xid, persistent_fid, tcon->tid, + ses->Suid, 0, opcode, rc); + + if ((rc != 0) && (rc != -EINVAL) && (rc != -E2BIG)) { + cifs_stats_fail_inc(tcon, SMB2_IOCTL_HE); + goto ioctl_exit; + } else if (rc == -EINVAL) { + if ((opcode != FSCTL_SRV_COPYCHUNK_WRITE) && + (opcode != FSCTL_SRV_COPYCHUNK)) { + cifs_stats_fail_inc(tcon, SMB2_IOCTL_HE); + goto ioctl_exit; + } + } else if (rc == -E2BIG) { + if (opcode != FSCTL_QUERY_ALLOCATED_RANGES) { + cifs_stats_fail_inc(tcon, SMB2_IOCTL_HE); + goto ioctl_exit; + } + } + + /* check if caller wants to look at return data or just return rc */ + if ((plen == NULL) || (out_data == NULL)) + goto ioctl_exit; + + /* + * Although unlikely to be possible for rsp to be null and rc not set, + * adding check below is slightly safer long term (and quiets Coverity + * warning) + */ + if (rsp == NULL) { + rc = -EIO; + goto ioctl_exit; + } + + *plen = le32_to_cpu(rsp->OutputCount); + + /* We check for obvious errors in the output buffer length and offset */ + if (*plen == 0) + goto ioctl_exit; /* server returned no data */ + else if (*plen > rsp_iov.iov_len || *plen > 0xFF00) { + cifs_tcon_dbg(VFS, "srv returned invalid ioctl length: %d\n", *plen); + *plen = 0; + rc = -EIO; + goto ioctl_exit; + } + + if (rsp_iov.iov_len - *plen < le32_to_cpu(rsp->OutputOffset)) { + cifs_tcon_dbg(VFS, "Malformed ioctl resp: len %d offset %d\n", *plen, + le32_to_cpu(rsp->OutputOffset)); + *plen = 0; + rc = -EIO; + goto ioctl_exit; + } + + *out_data = kmemdup((char *)rsp + le32_to_cpu(rsp->OutputOffset), + *plen, GFP_KERNEL); + if (*out_data == NULL) { + rc = -ENOMEM; + goto ioctl_exit; + } + +ioctl_exit: + SMB2_ioctl_free(&rqst); + free_rsp_buf(resp_buftype, rsp); + return rc; +} + +/* + * Individual callers to ioctl worker function follow + */ + +int +SMB2_set_compression(const unsigned int xid, struct cifs_tcon *tcon, + u64 persistent_fid, u64 volatile_fid) +{ + int rc; + struct compress_ioctl fsctl_input; + char *ret_data = NULL; + + fsctl_input.CompressionState = + cpu_to_le16(COMPRESSION_FORMAT_DEFAULT); + + rc = SMB2_ioctl(xid, tcon, persistent_fid, volatile_fid, + FSCTL_SET_COMPRESSION, + (char *)&fsctl_input /* data input */, + 2 /* in data len */, CIFSMaxBufSize /* max out data */, + &ret_data /* out data */, NULL); + + cifs_dbg(FYI, "set compression rc %d\n", rc); + + return rc; +} + +int +SMB2_close_init(struct cifs_tcon *tcon, struct TCP_Server_Info *server, + struct smb_rqst *rqst, + u64 persistent_fid, u64 volatile_fid, bool query_attrs) +{ + struct smb2_close_req *req; + struct kvec *iov = rqst->rq_iov; + unsigned int total_len; + int rc; + + rc = smb2_plain_req_init(SMB2_CLOSE, tcon, server, + (void **) &req, &total_len); + if (rc) + return rc; + + req->PersistentFileId = persistent_fid; + req->VolatileFileId = volatile_fid; + if (query_attrs) + req->Flags = SMB2_CLOSE_FLAG_POSTQUERY_ATTRIB; + else + req->Flags = 0; + iov[0].iov_base = (char *)req; + iov[0].iov_len = total_len; + + return 0; +} + +void +SMB2_close_free(struct smb_rqst *rqst) +{ + if (rqst && rqst->rq_iov) + cifs_small_buf_release(rqst->rq_iov[0].iov_base); /* request */ +} + +int +__SMB2_close(const unsigned int xid, struct cifs_tcon *tcon, + u64 persistent_fid, u64 volatile_fid, + struct smb2_file_network_open_info *pbuf) +{ + struct smb_rqst rqst; + struct smb2_close_rsp *rsp = NULL; + struct cifs_ses *ses = tcon->ses; + struct TCP_Server_Info *server = cifs_pick_channel(ses); + struct kvec iov[1]; + struct kvec rsp_iov; + int resp_buftype = CIFS_NO_BUFFER; + int rc = 0; + int flags = 0; + bool query_attrs = false; + + cifs_dbg(FYI, "Close\n"); + + if (!ses || !server) + return -EIO; + + if (smb3_encryption_required(tcon)) + flags |= CIFS_TRANSFORM_REQ; + + memset(&rqst, 0, sizeof(struct smb_rqst)); + memset(&iov, 0, sizeof(iov)); + rqst.rq_iov = iov; + rqst.rq_nvec = 1; + + /* check if need to ask server to return timestamps in close response */ + if (pbuf) + query_attrs = true; + + trace_smb3_close_enter(xid, persistent_fid, tcon->tid, ses->Suid); + rc = SMB2_close_init(tcon, server, + &rqst, persistent_fid, volatile_fid, + query_attrs); + if (rc) + goto close_exit; + + rc = cifs_send_recv(xid, ses, server, + &rqst, &resp_buftype, flags, &rsp_iov); + rsp = (struct smb2_close_rsp *)rsp_iov.iov_base; + + if (rc != 0) { + cifs_stats_fail_inc(tcon, SMB2_CLOSE_HE); + trace_smb3_close_err(xid, persistent_fid, tcon->tid, ses->Suid, + rc); + goto close_exit; + } else { + trace_smb3_close_done(xid, persistent_fid, tcon->tid, + ses->Suid); + /* + * Note that have to subtract 4 since struct network_open_info + * has a final 4 byte pad that close response does not have + */ + if (pbuf) + memcpy(pbuf, (char *)&rsp->CreationTime, sizeof(*pbuf) - 4); + } + + atomic_dec(&tcon->num_remote_opens); +close_exit: + SMB2_close_free(&rqst); + free_rsp_buf(resp_buftype, rsp); + + /* retry close in a worker thread if this one is interrupted */ + if (is_interrupt_error(rc)) { + int tmp_rc; + + tmp_rc = smb2_handle_cancelled_close(tcon, persistent_fid, + volatile_fid); + if (tmp_rc) + cifs_dbg(VFS, "handle cancelled close fid 0x%llx returned error %d\n", + persistent_fid, tmp_rc); + } + return rc; +} + +int +SMB2_close(const unsigned int xid, struct cifs_tcon *tcon, + u64 persistent_fid, u64 volatile_fid) +{ + return __SMB2_close(xid, tcon, persistent_fid, volatile_fid, NULL); +} + +int +smb2_validate_iov(unsigned int offset, unsigned int buffer_length, + struct kvec *iov, unsigned int min_buf_size) +{ + unsigned int smb_len = iov->iov_len; + char *end_of_smb = smb_len + (char *)iov->iov_base; + char *begin_of_buf = offset + (char *)iov->iov_base; + char *end_of_buf = begin_of_buf + buffer_length; + + + if (buffer_length < min_buf_size) { + cifs_dbg(VFS, "buffer length %d smaller than minimum size %d\n", + buffer_length, min_buf_size); + return -EINVAL; + } + + /* check if beyond RFC1001 maximum length */ + if ((smb_len > 0x7FFFFF) || (buffer_length > 0x7FFFFF)) { + cifs_dbg(VFS, "buffer length %d or smb length %d too large\n", + buffer_length, smb_len); + return -EINVAL; + } + + if ((begin_of_buf > end_of_smb) || (end_of_buf > end_of_smb)) { + cifs_dbg(VFS, "Invalid server response, bad offset to data\n"); + return -EINVAL; + } + + return 0; +} + +/* + * If SMB buffer fields are valid, copy into temporary buffer to hold result. + * Caller must free buffer. + */ +int +smb2_validate_and_copy_iov(unsigned int offset, unsigned int buffer_length, + struct kvec *iov, unsigned int minbufsize, + char *data) +{ + char *begin_of_buf = offset + (char *)iov->iov_base; + int rc; + + if (!data) + return -EINVAL; + + rc = smb2_validate_iov(offset, buffer_length, iov, minbufsize); + if (rc) + return rc; + + memcpy(data, begin_of_buf, minbufsize); + + return 0; +} + +int +SMB2_query_info_init(struct cifs_tcon *tcon, struct TCP_Server_Info *server, + struct smb_rqst *rqst, + u64 persistent_fid, u64 volatile_fid, + u8 info_class, u8 info_type, u32 additional_info, + size_t output_len, size_t input_len, void *input) +{ + struct smb2_query_info_req *req; + struct kvec *iov = rqst->rq_iov; + unsigned int total_len; + int rc; + + rc = smb2_plain_req_init(SMB2_QUERY_INFO, tcon, server, + (void **) &req, &total_len); + if (rc) + return rc; + + req->InfoType = info_type; + req->FileInfoClass = info_class; + req->PersistentFileId = persistent_fid; + req->VolatileFileId = volatile_fid; + req->AdditionalInformation = cpu_to_le32(additional_info); + + req->OutputBufferLength = cpu_to_le32(output_len); + if (input_len) { + req->InputBufferLength = cpu_to_le32(input_len); + /* total_len for smb query request never close to le16 max */ + req->InputBufferOffset = cpu_to_le16(total_len - 1); + memcpy(req->Buffer, input, input_len); + } + + iov[0].iov_base = (char *)req; + /* 1 for Buffer */ + iov[0].iov_len = total_len - 1 + input_len; + return 0; +} + +void +SMB2_query_info_free(struct smb_rqst *rqst) +{ + if (rqst && rqst->rq_iov) + cifs_small_buf_release(rqst->rq_iov[0].iov_base); /* request */ +} + +static int +query_info(const unsigned int xid, struct cifs_tcon *tcon, + u64 persistent_fid, u64 volatile_fid, u8 info_class, u8 info_type, + u32 additional_info, size_t output_len, size_t min_len, void **data, + u32 *dlen) +{ + struct smb_rqst rqst; + struct smb2_query_info_rsp *rsp = NULL; + struct kvec iov[1]; + struct kvec rsp_iov; + int rc = 0; + int resp_buftype = CIFS_NO_BUFFER; + struct cifs_ses *ses = tcon->ses; + struct TCP_Server_Info *server; + int flags = 0; + bool allocated = false; + + cifs_dbg(FYI, "Query Info\n"); + + if (!ses) + return -EIO; + server = cifs_pick_channel(ses); + if (!server) + return -EIO; + + if (smb3_encryption_required(tcon)) + flags |= CIFS_TRANSFORM_REQ; + + memset(&rqst, 0, sizeof(struct smb_rqst)); + memset(&iov, 0, sizeof(iov)); + rqst.rq_iov = iov; + rqst.rq_nvec = 1; + + rc = SMB2_query_info_init(tcon, server, + &rqst, persistent_fid, volatile_fid, + info_class, info_type, additional_info, + output_len, 0, NULL); + if (rc) + goto qinf_exit; + + trace_smb3_query_info_enter(xid, persistent_fid, tcon->tid, + ses->Suid, info_class, (__u32)info_type); + + rc = cifs_send_recv(xid, ses, server, + &rqst, &resp_buftype, flags, &rsp_iov); + rsp = (struct smb2_query_info_rsp *)rsp_iov.iov_base; + + if (rc) { + cifs_stats_fail_inc(tcon, SMB2_QUERY_INFO_HE); + trace_smb3_query_info_err(xid, persistent_fid, tcon->tid, + ses->Suid, info_class, (__u32)info_type, rc); + goto qinf_exit; + } + + trace_smb3_query_info_done(xid, persistent_fid, tcon->tid, + ses->Suid, info_class, (__u32)info_type); + + if (dlen) { + *dlen = le32_to_cpu(rsp->OutputBufferLength); + if (!*data) { + *data = kmalloc(*dlen, GFP_KERNEL); + if (!*data) { + cifs_tcon_dbg(VFS, + "Error %d allocating memory for acl\n", + rc); + *dlen = 0; + rc = -ENOMEM; + goto qinf_exit; + } + allocated = true; + } + } + + rc = smb2_validate_and_copy_iov(le16_to_cpu(rsp->OutputBufferOffset), + le32_to_cpu(rsp->OutputBufferLength), + &rsp_iov, dlen ? *dlen : min_len, *data); + if (rc && allocated) { + kfree(*data); + *data = NULL; + *dlen = 0; + } + +qinf_exit: + SMB2_query_info_free(&rqst); + free_rsp_buf(resp_buftype, rsp); + return rc; +} + +int SMB2_query_info(const unsigned int xid, struct cifs_tcon *tcon, + u64 persistent_fid, u64 volatile_fid, struct smb2_file_all_info *data) +{ + return query_info(xid, tcon, persistent_fid, volatile_fid, + FILE_ALL_INFORMATION, SMB2_O_INFO_FILE, 0, + sizeof(struct smb2_file_all_info) + PATH_MAX * 2, + sizeof(struct smb2_file_all_info), (void **)&data, + NULL); +} + +#if 0 +/* currently unused, as now we are doing compounding instead (see smb311_posix_query_path_info) */ +int +SMB311_posix_query_info(const unsigned int xid, struct cifs_tcon *tcon, + u64 persistent_fid, u64 volatile_fid, struct smb311_posix_qinfo *data, u32 *plen) +{ + size_t output_len = sizeof(struct smb311_posix_qinfo *) + + (sizeof(struct cifs_sid) * 2) + (PATH_MAX * 2); + *plen = 0; + + return query_info(xid, tcon, persistent_fid, volatile_fid, + SMB_FIND_FILE_POSIX_INFO, SMB2_O_INFO_FILE, 0, + output_len, sizeof(struct smb311_posix_qinfo), (void **)&data, plen); + /* Note caller must free "data" (passed in above). It may be allocated in query_info call */ +} +#endif + +int +SMB2_query_acl(const unsigned int xid, struct cifs_tcon *tcon, + u64 persistent_fid, u64 volatile_fid, + void **data, u32 *plen, u32 extra_info) +{ + __u32 additional_info = OWNER_SECINFO | GROUP_SECINFO | DACL_SECINFO | + extra_info; + *plen = 0; + + return query_info(xid, tcon, persistent_fid, volatile_fid, + 0, SMB2_O_INFO_SECURITY, additional_info, + SMB2_MAX_BUFFER_SIZE, MIN_SEC_DESC_LEN, data, plen); +} + +int +SMB2_get_srv_num(const unsigned int xid, struct cifs_tcon *tcon, + u64 persistent_fid, u64 volatile_fid, __le64 *uniqueid) +{ + return query_info(xid, tcon, persistent_fid, volatile_fid, + FILE_INTERNAL_INFORMATION, SMB2_O_INFO_FILE, 0, + sizeof(struct smb2_file_internal_info), + sizeof(struct smb2_file_internal_info), + (void **)&uniqueid, NULL); +} + +/* + * CHANGE_NOTIFY Request is sent to get notifications on changes to a directory + * See MS-SMB2 2.2.35 and 2.2.36 + */ + +static int +SMB2_notify_init(const unsigned int xid, struct smb_rqst *rqst, + struct cifs_tcon *tcon, struct TCP_Server_Info *server, + u64 persistent_fid, u64 volatile_fid, + u32 completion_filter, bool watch_tree) +{ + struct smb2_change_notify_req *req; + struct kvec *iov = rqst->rq_iov; + unsigned int total_len; + int rc; + + rc = smb2_plain_req_init(SMB2_CHANGE_NOTIFY, tcon, server, + (void **) &req, &total_len); + if (rc) + return rc; + + req->PersistentFileId = persistent_fid; + req->VolatileFileId = volatile_fid; + /* See note 354 of MS-SMB2, 64K max */ + req->OutputBufferLength = + cpu_to_le32(SMB2_MAX_BUFFER_SIZE - MAX_SMB2_HDR_SIZE); + req->CompletionFilter = cpu_to_le32(completion_filter); + if (watch_tree) + req->Flags = cpu_to_le16(SMB2_WATCH_TREE); + else + req->Flags = 0; + + iov[0].iov_base = (char *)req; + iov[0].iov_len = total_len; + + return 0; +} + +int +SMB2_change_notify(const unsigned int xid, struct cifs_tcon *tcon, + u64 persistent_fid, u64 volatile_fid, bool watch_tree, + u32 completion_filter, u32 max_out_data_len, char **out_data, + u32 *plen /* returned data len */) +{ + struct cifs_ses *ses = tcon->ses; + struct TCP_Server_Info *server = cifs_pick_channel(ses); + struct smb_rqst rqst; + struct smb2_change_notify_rsp *smb_rsp; + struct kvec iov[1]; + struct kvec rsp_iov = {NULL, 0}; + int resp_buftype = CIFS_NO_BUFFER; + int flags = 0; + int rc = 0; + + cifs_dbg(FYI, "change notify\n"); + if (!ses || !server) + return -EIO; + + if (smb3_encryption_required(tcon)) + flags |= CIFS_TRANSFORM_REQ; + + memset(&rqst, 0, sizeof(struct smb_rqst)); + memset(&iov, 0, sizeof(iov)); + if (plen) + *plen = 0; + + rqst.rq_iov = iov; + rqst.rq_nvec = 1; + + rc = SMB2_notify_init(xid, &rqst, tcon, server, + persistent_fid, volatile_fid, + completion_filter, watch_tree); + if (rc) + goto cnotify_exit; + + trace_smb3_notify_enter(xid, persistent_fid, tcon->tid, ses->Suid, + (u8)watch_tree, completion_filter); + rc = cifs_send_recv(xid, ses, server, + &rqst, &resp_buftype, flags, &rsp_iov); + + if (rc != 0) { + cifs_stats_fail_inc(tcon, SMB2_CHANGE_NOTIFY_HE); + trace_smb3_notify_err(xid, persistent_fid, tcon->tid, ses->Suid, + (u8)watch_tree, completion_filter, rc); + } else { + trace_smb3_notify_done(xid, persistent_fid, tcon->tid, + ses->Suid, (u8)watch_tree, completion_filter); + /* validate that notify information is plausible */ + if ((rsp_iov.iov_base == NULL) || + (rsp_iov.iov_len < sizeof(struct smb2_change_notify_rsp))) + goto cnotify_exit; + + smb_rsp = (struct smb2_change_notify_rsp *)rsp_iov.iov_base; + + smb2_validate_iov(le16_to_cpu(smb_rsp->OutputBufferOffset), + le32_to_cpu(smb_rsp->OutputBufferLength), &rsp_iov, + sizeof(struct file_notify_information)); + + *out_data = kmemdup((char *)smb_rsp + le16_to_cpu(smb_rsp->OutputBufferOffset), + le32_to_cpu(smb_rsp->OutputBufferLength), GFP_KERNEL); + if (*out_data == NULL) { + rc = -ENOMEM; + goto cnotify_exit; + } else if (plen) + *plen = le32_to_cpu(smb_rsp->OutputBufferLength); + } + + cnotify_exit: + if (rqst.rq_iov) + cifs_small_buf_release(rqst.rq_iov[0].iov_base); /* request */ + free_rsp_buf(resp_buftype, rsp_iov.iov_base); + return rc; +} + + + +/* + * This is a no-op for now. We're not really interested in the reply, but + * rather in the fact that the server sent one and that server->lstrp + * gets updated. + * + * FIXME: maybe we should consider checking that the reply matches request? + */ +static void +smb2_echo_callback(struct mid_q_entry *mid) +{ + struct TCP_Server_Info *server = mid->callback_data; + struct smb2_echo_rsp *rsp = (struct smb2_echo_rsp *)mid->resp_buf; + struct cifs_credits credits = { .value = 0, .instance = 0 }; + + if (mid->mid_state == MID_RESPONSE_RECEIVED + || mid->mid_state == MID_RESPONSE_MALFORMED) { + credits.value = le16_to_cpu(rsp->hdr.CreditRequest); + credits.instance = server->reconnect_instance; + } + + release_mid(mid); + add_credits(server, &credits, CIFS_ECHO_OP); +} + +void smb2_reconnect_server(struct work_struct *work) +{ + struct TCP_Server_Info *server = container_of(work, + struct TCP_Server_Info, reconnect.work); + struct TCP_Server_Info *pserver; + struct cifs_ses *ses, *ses2; + struct cifs_tcon *tcon, *tcon2; + struct list_head tmp_list, tmp_ses_list; + bool tcon_exist = false, ses_exist = false; + bool tcon_selected = false; + int rc; + bool resched = false; + + /* If server is a channel, select the primary channel */ + pserver = CIFS_SERVER_IS_CHAN(server) ? server->primary_server : server; + + /* Prevent simultaneous reconnects that can corrupt tcon->rlist list */ + mutex_lock(&pserver->reconnect_mutex); + + INIT_LIST_HEAD(&tmp_list); + INIT_LIST_HEAD(&tmp_ses_list); + cifs_dbg(FYI, "Reconnecting tcons and channels\n"); + + spin_lock(&cifs_tcp_ses_lock); + list_for_each_entry(ses, &pserver->smb_ses_list, smb_ses_list) { + + tcon_selected = false; + + list_for_each_entry(tcon, &ses->tcon_list, tcon_list) { + if (tcon->need_reconnect || tcon->need_reopen_files) { + tcon->tc_count++; + list_add_tail(&tcon->rlist, &tmp_list); + tcon_selected = tcon_exist = true; + } + } + /* + * IPC has the same lifetime as its session and uses its + * refcount. + */ + if (ses->tcon_ipc && ses->tcon_ipc->need_reconnect) { + list_add_tail(&ses->tcon_ipc->rlist, &tmp_list); + tcon_selected = tcon_exist = true; + ses->ses_count++; + } + /* + * handle the case where channel needs to reconnect + * binding session, but tcon is healthy (some other channel + * is active) + */ + spin_lock(&ses->chan_lock); + if (!tcon_selected && cifs_chan_needs_reconnect(ses, server)) { + list_add_tail(&ses->rlist, &tmp_ses_list); + ses_exist = true; + ses->ses_count++; + } + spin_unlock(&ses->chan_lock); + } + /* + * Get the reference to server struct to be sure that the last call of + * cifs_put_tcon() in the loop below won't release the server pointer. + */ + if (tcon_exist || ses_exist) + server->srv_count++; + + spin_unlock(&cifs_tcp_ses_lock); + + list_for_each_entry_safe(tcon, tcon2, &tmp_list, rlist) { + rc = smb2_reconnect(SMB2_INTERNAL_CMD, tcon, server); + if (!rc) + cifs_reopen_persistent_handles(tcon); + else + resched = true; + list_del_init(&tcon->rlist); + if (tcon->ipc) + cifs_put_smb_ses(tcon->ses); + else + cifs_put_tcon(tcon); + } + + if (!ses_exist) + goto done; + + /* allocate a dummy tcon struct used for reconnect */ + tcon = tconInfoAlloc(); + if (!tcon) { + resched = true; + list_for_each_entry_safe(ses, ses2, &tmp_ses_list, rlist) { + list_del_init(&ses->rlist); + cifs_put_smb_ses(ses); + } + goto done; + } + + tcon->status = TID_GOOD; + tcon->retry = false; + tcon->need_reconnect = false; + + /* now reconnect sessions for necessary channels */ + list_for_each_entry_safe(ses, ses2, &tmp_ses_list, rlist) { + tcon->ses = ses; + rc = smb2_reconnect(SMB2_INTERNAL_CMD, tcon, server); + if (rc) + resched = true; + list_del_init(&ses->rlist); + cifs_put_smb_ses(ses); + } + tconInfoFree(tcon); + +done: + cifs_dbg(FYI, "Reconnecting tcons and channels finished\n"); + if (resched) + queue_delayed_work(cifsiod_wq, &server->reconnect, 2 * HZ); + mutex_unlock(&pserver->reconnect_mutex); + + /* now we can safely release srv struct */ + if (tcon_exist || ses_exist) + cifs_put_tcp_session(server, 1); +} + +int +SMB2_echo(struct TCP_Server_Info *server) +{ + struct smb2_echo_req *req; + int rc = 0; + struct kvec iov[1]; + struct smb_rqst rqst = { .rq_iov = iov, + .rq_nvec = 1 }; + unsigned int total_len; + + cifs_dbg(FYI, "In echo request for conn_id %lld\n", server->conn_id); + + spin_lock(&server->srv_lock); + if (server->ops->need_neg && + server->ops->need_neg(server)) { + spin_unlock(&server->srv_lock); + /* No need to send echo on newly established connections */ + mod_delayed_work(cifsiod_wq, &server->reconnect, 0); + return rc; + } + spin_unlock(&server->srv_lock); + + rc = smb2_plain_req_init(SMB2_ECHO, NULL, server, + (void **)&req, &total_len); + if (rc) + return rc; + + req->hdr.CreditRequest = cpu_to_le16(1); + + iov[0].iov_len = total_len; + iov[0].iov_base = (char *)req; + + rc = cifs_call_async(server, &rqst, NULL, smb2_echo_callback, NULL, + server, CIFS_ECHO_OP, NULL); + if (rc) + cifs_dbg(FYI, "Echo request failed: %d\n", rc); + + cifs_small_buf_release(req); + return rc; +} + +void +SMB2_flush_free(struct smb_rqst *rqst) +{ + if (rqst && rqst->rq_iov) + cifs_small_buf_release(rqst->rq_iov[0].iov_base); /* request */ +} + +int +SMB2_flush_init(const unsigned int xid, struct smb_rqst *rqst, + struct cifs_tcon *tcon, struct TCP_Server_Info *server, + u64 persistent_fid, u64 volatile_fid) +{ + struct smb2_flush_req *req; + struct kvec *iov = rqst->rq_iov; + unsigned int total_len; + int rc; + + rc = smb2_plain_req_init(SMB2_FLUSH, tcon, server, + (void **) &req, &total_len); + if (rc) + return rc; + + req->PersistentFileId = persistent_fid; + req->VolatileFileId = volatile_fid; + + iov[0].iov_base = (char *)req; + iov[0].iov_len = total_len; + + return 0; +} + +int +SMB2_flush(const unsigned int xid, struct cifs_tcon *tcon, u64 persistent_fid, + u64 volatile_fid) +{ + struct cifs_ses *ses = tcon->ses; + struct smb_rqst rqst; + struct kvec iov[1]; + struct kvec rsp_iov = {NULL, 0}; + struct TCP_Server_Info *server = cifs_pick_channel(ses); + int resp_buftype = CIFS_NO_BUFFER; + int flags = 0; + int rc = 0; + + cifs_dbg(FYI, "flush\n"); + if (!ses || !(ses->server)) + return -EIO; + + if (smb3_encryption_required(tcon)) + flags |= CIFS_TRANSFORM_REQ; + + memset(&rqst, 0, sizeof(struct smb_rqst)); + memset(&iov, 0, sizeof(iov)); + rqst.rq_iov = iov; + rqst.rq_nvec = 1; + + rc = SMB2_flush_init(xid, &rqst, tcon, server, + persistent_fid, volatile_fid); + if (rc) + goto flush_exit; + + trace_smb3_flush_enter(xid, persistent_fid, tcon->tid, ses->Suid); + rc = cifs_send_recv(xid, ses, server, + &rqst, &resp_buftype, flags, &rsp_iov); + + if (rc != 0) { + cifs_stats_fail_inc(tcon, SMB2_FLUSH_HE); + trace_smb3_flush_err(xid, persistent_fid, tcon->tid, ses->Suid, + rc); + } else + trace_smb3_flush_done(xid, persistent_fid, tcon->tid, + ses->Suid); + + flush_exit: + SMB2_flush_free(&rqst); + free_rsp_buf(resp_buftype, rsp_iov.iov_base); + return rc; +} + +#ifdef CONFIG_CIFS_SMB_DIRECT +static inline bool smb3_use_rdma_offload(struct cifs_io_parms *io_parms) +{ + struct TCP_Server_Info *server = io_parms->server; + struct cifs_tcon *tcon = io_parms->tcon; + + /* we can only offload if we're connected */ + if (!server || !tcon) + return false; + + /* we can only offload on an rdma connection */ + if (!server->rdma || !server->smbd_conn) + return false; + + /* we don't support signed offload yet */ + if (server->sign) + return false; + + /* we don't support encrypted offload yet */ + if (smb3_encryption_required(tcon)) + return false; + + /* offload also has its overhead, so only do it if desired */ + if (io_parms->length < server->smbd_conn->rdma_readwrite_threshold) + return false; + + return true; +} +#endif /* CONFIG_CIFS_SMB_DIRECT */ + +/* + * To form a chain of read requests, any read requests after the first should + * have the end_of_chain boolean set to true. + */ +static int +smb2_new_read_req(void **buf, unsigned int *total_len, + struct cifs_io_parms *io_parms, struct cifs_readdata *rdata, + unsigned int remaining_bytes, int request_type) +{ + int rc = -EACCES; + struct smb2_read_req *req = NULL; + struct smb2_hdr *shdr; + struct TCP_Server_Info *server = io_parms->server; + + rc = smb2_plain_req_init(SMB2_READ, io_parms->tcon, server, + (void **) &req, total_len); + if (rc) + return rc; + + if (server == NULL) + return -ECONNABORTED; + + shdr = &req->hdr; + shdr->Id.SyncId.ProcessId = cpu_to_le32(io_parms->pid); + + req->PersistentFileId = io_parms->persistent_fid; + req->VolatileFileId = io_parms->volatile_fid; + req->ReadChannelInfoOffset = 0; /* reserved */ + req->ReadChannelInfoLength = 0; /* reserved */ + req->Channel = 0; /* reserved */ + req->MinimumCount = 0; + req->Length = cpu_to_le32(io_parms->length); + req->Offset = cpu_to_le64(io_parms->offset); + + trace_smb3_read_enter(0 /* xid */, + io_parms->persistent_fid, + io_parms->tcon->tid, io_parms->tcon->ses->Suid, + io_parms->offset, io_parms->length); +#ifdef CONFIG_CIFS_SMB_DIRECT + /* + * If we want to do a RDMA write, fill in and append + * smbd_buffer_descriptor_v1 to the end of read request + */ + if (smb3_use_rdma_offload(io_parms)) { + struct smbd_buffer_descriptor_v1 *v1; + bool need_invalidate = server->dialect == SMB30_PROT_ID; + + rdata->mr = smbd_register_mr( + server->smbd_conn, rdata->pages, + rdata->nr_pages, rdata->page_offset, + rdata->tailsz, true, need_invalidate); + if (!rdata->mr) + return -EAGAIN; + + req->Channel = SMB2_CHANNEL_RDMA_V1_INVALIDATE; + if (need_invalidate) + req->Channel = SMB2_CHANNEL_RDMA_V1; + req->ReadChannelInfoOffset = + cpu_to_le16(offsetof(struct smb2_read_req, Buffer)); + req->ReadChannelInfoLength = + cpu_to_le16(sizeof(struct smbd_buffer_descriptor_v1)); + v1 = (struct smbd_buffer_descriptor_v1 *) &req->Buffer[0]; + v1->offset = cpu_to_le64(rdata->mr->mr->iova); + v1->token = cpu_to_le32(rdata->mr->mr->rkey); + v1->length = cpu_to_le32(rdata->mr->mr->length); + + *total_len += sizeof(*v1) - 1; + } +#endif + if (request_type & CHAINED_REQUEST) { + if (!(request_type & END_OF_CHAIN)) { + /* next 8-byte aligned request */ + *total_len = ALIGN(*total_len, 8); + shdr->NextCommand = cpu_to_le32(*total_len); + } else /* END_OF_CHAIN */ + shdr->NextCommand = 0; + if (request_type & RELATED_REQUEST) { + shdr->Flags |= SMB2_FLAGS_RELATED_OPERATIONS; + /* + * Related requests use info from previous read request + * in chain. + */ + shdr->SessionId = cpu_to_le64(0xFFFFFFFFFFFFFFFF); + shdr->Id.SyncId.TreeId = cpu_to_le32(0xFFFFFFFF); + req->PersistentFileId = (u64)-1; + req->VolatileFileId = (u64)-1; + } + } + if (remaining_bytes > io_parms->length) + req->RemainingBytes = cpu_to_le32(remaining_bytes); + else + req->RemainingBytes = 0; + + *buf = req; + return rc; +} + +static void +smb2_readv_callback(struct mid_q_entry *mid) +{ + struct cifs_readdata *rdata = mid->callback_data; + struct cifs_tcon *tcon = tlink_tcon(rdata->cfile->tlink); + struct TCP_Server_Info *server = rdata->server; + struct smb2_hdr *shdr = + (struct smb2_hdr *)rdata->iov[0].iov_base; + struct cifs_credits credits = { .value = 0, .instance = 0 }; + struct smb_rqst rqst = { .rq_iov = &rdata->iov[1], + .rq_nvec = 1, }; + + if (rdata->got_bytes) { + rqst.rq_pages = rdata->pages; + rqst.rq_offset = rdata->page_offset; + rqst.rq_npages = rdata->nr_pages; + rqst.rq_pagesz = rdata->pagesz; + rqst.rq_tailsz = rdata->tailsz; + } + + WARN_ONCE(rdata->server != mid->server, + "rdata server %p != mid server %p", + rdata->server, mid->server); + + cifs_dbg(FYI, "%s: mid=%llu state=%d result=%d bytes=%u\n", + __func__, mid->mid, mid->mid_state, rdata->result, + rdata->bytes); + + switch (mid->mid_state) { + case MID_RESPONSE_RECEIVED: + credits.value = le16_to_cpu(shdr->CreditRequest); + credits.instance = server->reconnect_instance; + /* result already set, check signature */ + if (server->sign && !mid->decrypted) { + int rc; + + rc = smb2_verify_signature(&rqst, server); + if (rc) + cifs_tcon_dbg(VFS, "SMB signature verification returned error = %d\n", + rc); + } + /* FIXME: should this be counted toward the initiating task? */ + task_io_account_read(rdata->got_bytes); + cifs_stats_bytes_read(tcon, rdata->got_bytes); + break; + case MID_REQUEST_SUBMITTED: + case MID_RETRY_NEEDED: + rdata->result = -EAGAIN; + if (server->sign && rdata->got_bytes) + /* reset bytes number since we can not check a sign */ + rdata->got_bytes = 0; + /* FIXME: should this be counted toward the initiating task? */ + task_io_account_read(rdata->got_bytes); + cifs_stats_bytes_read(tcon, rdata->got_bytes); + break; + case MID_RESPONSE_MALFORMED: + credits.value = le16_to_cpu(shdr->CreditRequest); + credits.instance = server->reconnect_instance; + fallthrough; + default: + rdata->result = -EIO; + } +#ifdef CONFIG_CIFS_SMB_DIRECT + /* + * If this rdata has a memmory registered, the MR can be freed + * MR needs to be freed as soon as I/O finishes to prevent deadlock + * because they have limited number and are used for future I/Os + */ + if (rdata->mr) { + smbd_deregister_mr(rdata->mr); + rdata->mr = NULL; + } +#endif + if (rdata->result && rdata->result != -ENODATA) { + cifs_stats_fail_inc(tcon, SMB2_READ_HE); + trace_smb3_read_err(0 /* xid */, + rdata->cfile->fid.persistent_fid, + tcon->tid, tcon->ses->Suid, rdata->offset, + rdata->bytes, rdata->result); + } else + trace_smb3_read_done(0 /* xid */, + rdata->cfile->fid.persistent_fid, + tcon->tid, tcon->ses->Suid, + rdata->offset, rdata->got_bytes); + + queue_work(cifsiod_wq, &rdata->work); + release_mid(mid); + add_credits(server, &credits, 0); +} + +/* smb2_async_readv - send an async read, and set up mid to handle result */ +int +smb2_async_readv(struct cifs_readdata *rdata) +{ + int rc, flags = 0; + char *buf; + struct smb2_hdr *shdr; + struct cifs_io_parms io_parms; + struct smb_rqst rqst = { .rq_iov = rdata->iov, + .rq_nvec = 1 }; + struct TCP_Server_Info *server; + struct cifs_tcon *tcon = tlink_tcon(rdata->cfile->tlink); + unsigned int total_len; + + cifs_dbg(FYI, "%s: offset=%llu bytes=%u\n", + __func__, rdata->offset, rdata->bytes); + + if (!rdata->server) + rdata->server = cifs_pick_channel(tcon->ses); + + io_parms.tcon = tlink_tcon(rdata->cfile->tlink); + io_parms.server = server = rdata->server; + io_parms.offset = rdata->offset; + io_parms.length = rdata->bytes; + io_parms.persistent_fid = rdata->cfile->fid.persistent_fid; + io_parms.volatile_fid = rdata->cfile->fid.volatile_fid; + io_parms.pid = rdata->pid; + + rc = smb2_new_read_req( + (void **) &buf, &total_len, &io_parms, rdata, 0, 0); + if (rc) + return rc; + + if (smb3_encryption_required(io_parms.tcon)) + flags |= CIFS_TRANSFORM_REQ; + + rdata->iov[0].iov_base = buf; + rdata->iov[0].iov_len = total_len; + + shdr = (struct smb2_hdr *)buf; + + if (rdata->credits.value > 0) { + shdr->CreditCharge = cpu_to_le16(DIV_ROUND_UP(rdata->bytes, + SMB2_MAX_BUFFER_SIZE)); + shdr->CreditRequest = cpu_to_le16(le16_to_cpu(shdr->CreditCharge) + 8); + + rc = adjust_credits(server, &rdata->credits, rdata->bytes); + if (rc) + goto async_readv_out; + + flags |= CIFS_HAS_CREDITS; + } + + kref_get(&rdata->refcount); + rc = cifs_call_async(server, &rqst, + cifs_readv_receive, smb2_readv_callback, + smb3_handle_read_data, rdata, flags, + &rdata->credits); + if (rc) { + kref_put(&rdata->refcount, cifs_readdata_release); + cifs_stats_fail_inc(io_parms.tcon, SMB2_READ_HE); + trace_smb3_read_err(0 /* xid */, io_parms.persistent_fid, + io_parms.tcon->tid, + io_parms.tcon->ses->Suid, + io_parms.offset, io_parms.length, rc); + } + +async_readv_out: + cifs_small_buf_release(buf); + return rc; +} + +int +SMB2_read(const unsigned int xid, struct cifs_io_parms *io_parms, + unsigned int *nbytes, char **buf, int *buf_type) +{ + struct smb_rqst rqst; + int resp_buftype, rc; + struct smb2_read_req *req = NULL; + struct smb2_read_rsp *rsp = NULL; + struct kvec iov[1]; + struct kvec rsp_iov; + unsigned int total_len; + int flags = CIFS_LOG_ERROR; + struct cifs_ses *ses = io_parms->tcon->ses; + + if (!io_parms->server) + io_parms->server = cifs_pick_channel(io_parms->tcon->ses); + + *nbytes = 0; + rc = smb2_new_read_req((void **)&req, &total_len, io_parms, NULL, 0, 0); + if (rc) + return rc; + + if (smb3_encryption_required(io_parms->tcon)) + flags |= CIFS_TRANSFORM_REQ; + + iov[0].iov_base = (char *)req; + iov[0].iov_len = total_len; + + memset(&rqst, 0, sizeof(struct smb_rqst)); + rqst.rq_iov = iov; + rqst.rq_nvec = 1; + + rc = cifs_send_recv(xid, ses, io_parms->server, + &rqst, &resp_buftype, flags, &rsp_iov); + rsp = (struct smb2_read_rsp *)rsp_iov.iov_base; + + if (rc) { + if (rc != -ENODATA) { + cifs_stats_fail_inc(io_parms->tcon, SMB2_READ_HE); + cifs_dbg(VFS, "Send error in read = %d\n", rc); + trace_smb3_read_err(xid, + req->PersistentFileId, + io_parms->tcon->tid, ses->Suid, + io_parms->offset, io_parms->length, + rc); + } else + trace_smb3_read_done(xid, req->PersistentFileId, io_parms->tcon->tid, + ses->Suid, io_parms->offset, 0); + free_rsp_buf(resp_buftype, rsp_iov.iov_base); + cifs_small_buf_release(req); + return rc == -ENODATA ? 0 : rc; + } else + trace_smb3_read_done(xid, + req->PersistentFileId, + io_parms->tcon->tid, ses->Suid, + io_parms->offset, io_parms->length); + + cifs_small_buf_release(req); + + *nbytes = le32_to_cpu(rsp->DataLength); + if ((*nbytes > CIFS_MAX_MSGSIZE) || + (*nbytes > io_parms->length)) { + cifs_dbg(FYI, "bad length %d for count %d\n", + *nbytes, io_parms->length); + rc = -EIO; + *nbytes = 0; + } + + if (*buf) { + memcpy(*buf, (char *)rsp + rsp->DataOffset, *nbytes); + free_rsp_buf(resp_buftype, rsp_iov.iov_base); + } else if (resp_buftype != CIFS_NO_BUFFER) { + *buf = rsp_iov.iov_base; + if (resp_buftype == CIFS_SMALL_BUFFER) + *buf_type = CIFS_SMALL_BUFFER; + else if (resp_buftype == CIFS_LARGE_BUFFER) + *buf_type = CIFS_LARGE_BUFFER; + } + return rc; +} + +/* + * Check the mid_state and signature on received buffer (if any), and queue the + * workqueue completion task. + */ +static void +smb2_writev_callback(struct mid_q_entry *mid) +{ + struct cifs_writedata *wdata = mid->callback_data; + struct cifs_tcon *tcon = tlink_tcon(wdata->cfile->tlink); + struct TCP_Server_Info *server = wdata->server; + unsigned int written; + struct smb2_write_rsp *rsp = (struct smb2_write_rsp *)mid->resp_buf; + struct cifs_credits credits = { .value = 0, .instance = 0 }; + + WARN_ONCE(wdata->server != mid->server, + "wdata server %p != mid server %p", + wdata->server, mid->server); + + switch (mid->mid_state) { + case MID_RESPONSE_RECEIVED: + credits.value = le16_to_cpu(rsp->hdr.CreditRequest); + credits.instance = server->reconnect_instance; + wdata->result = smb2_check_receive(mid, server, 0); + if (wdata->result != 0) + break; + + written = le32_to_cpu(rsp->DataLength); + /* + * Mask off high 16 bits when bytes written as returned + * by the server is greater than bytes requested by the + * client. OS/2 servers are known to set incorrect + * CountHigh values. + */ + if (written > wdata->bytes) + written &= 0xFFFF; + + if (written < wdata->bytes) + wdata->result = -ENOSPC; + else + wdata->bytes = written; + break; + case MID_REQUEST_SUBMITTED: + case MID_RETRY_NEEDED: + wdata->result = -EAGAIN; + break; + case MID_RESPONSE_MALFORMED: + credits.value = le16_to_cpu(rsp->hdr.CreditRequest); + credits.instance = server->reconnect_instance; + fallthrough; + default: + wdata->result = -EIO; + break; + } +#ifdef CONFIG_CIFS_SMB_DIRECT + /* + * If this wdata has a memory registered, the MR can be freed + * The number of MRs available is limited, it's important to recover + * used MR as soon as I/O is finished. Hold MR longer in the later + * I/O process can possibly result in I/O deadlock due to lack of MR + * to send request on I/O retry + */ + if (wdata->mr) { + smbd_deregister_mr(wdata->mr); + wdata->mr = NULL; + } +#endif + if (wdata->result) { + cifs_stats_fail_inc(tcon, SMB2_WRITE_HE); + trace_smb3_write_err(0 /* no xid */, + wdata->cfile->fid.persistent_fid, + tcon->tid, tcon->ses->Suid, wdata->offset, + wdata->bytes, wdata->result); + if (wdata->result == -ENOSPC) + pr_warn_once("Out of space writing to %s\n", + tcon->tree_name); + } else + trace_smb3_write_done(0 /* no xid */, + wdata->cfile->fid.persistent_fid, + tcon->tid, tcon->ses->Suid, + wdata->offset, wdata->bytes); + + queue_work(cifsiod_wq, &wdata->work); + release_mid(mid); + add_credits(server, &credits, 0); +} + +/* smb2_async_writev - send an async write, and set up mid to handle result */ +int +smb2_async_writev(struct cifs_writedata *wdata, + void (*release)(struct kref *kref)) +{ + int rc = -EACCES, flags = 0; + struct smb2_write_req *req = NULL; + struct smb2_hdr *shdr; + struct cifs_tcon *tcon = tlink_tcon(wdata->cfile->tlink); + struct TCP_Server_Info *server = wdata->server; + struct kvec iov[1]; + struct smb_rqst rqst = { }; + unsigned int total_len; + struct cifs_io_parms _io_parms; + struct cifs_io_parms *io_parms = NULL; + + if (!wdata->server) + server = wdata->server = cifs_pick_channel(tcon->ses); + + /* + * in future we may get cifs_io_parms passed in from the caller, + * but for now we construct it here... + */ + _io_parms = (struct cifs_io_parms) { + .tcon = tcon, + .server = server, + .offset = wdata->offset, + .length = wdata->bytes, + .persistent_fid = wdata->cfile->fid.persistent_fid, + .volatile_fid = wdata->cfile->fid.volatile_fid, + .pid = wdata->pid, + }; + io_parms = &_io_parms; + + rc = smb2_plain_req_init(SMB2_WRITE, tcon, server, + (void **) &req, &total_len); + if (rc) + return rc; + + if (smb3_encryption_required(tcon)) + flags |= CIFS_TRANSFORM_REQ; + + shdr = (struct smb2_hdr *)req; + shdr->Id.SyncId.ProcessId = cpu_to_le32(io_parms->pid); + + req->PersistentFileId = io_parms->persistent_fid; + req->VolatileFileId = io_parms->volatile_fid; + req->WriteChannelInfoOffset = 0; + req->WriteChannelInfoLength = 0; + req->Channel = 0; + req->Offset = cpu_to_le64(io_parms->offset); + req->DataOffset = cpu_to_le16( + offsetof(struct smb2_write_req, Buffer)); + req->RemainingBytes = 0; + + trace_smb3_write_enter(0 /* xid */, + io_parms->persistent_fid, + io_parms->tcon->tid, + io_parms->tcon->ses->Suid, + io_parms->offset, + io_parms->length); + +#ifdef CONFIG_CIFS_SMB_DIRECT + /* + * If we want to do a server RDMA read, fill in and append + * smbd_buffer_descriptor_v1 to the end of write request + */ + if (smb3_use_rdma_offload(io_parms)) { + struct smbd_buffer_descriptor_v1 *v1; + bool need_invalidate = server->dialect == SMB30_PROT_ID; + + wdata->mr = smbd_register_mr( + server->smbd_conn, wdata->pages, + wdata->nr_pages, wdata->page_offset, + wdata->tailsz, false, need_invalidate); + if (!wdata->mr) { + rc = -EAGAIN; + goto async_writev_out; + } + req->Length = 0; + req->DataOffset = 0; + if (wdata->nr_pages > 1) + req->RemainingBytes = + cpu_to_le32( + (wdata->nr_pages - 1) * wdata->pagesz - + wdata->page_offset + wdata->tailsz + ); + else + req->RemainingBytes = cpu_to_le32(wdata->tailsz); + req->Channel = SMB2_CHANNEL_RDMA_V1_INVALIDATE; + if (need_invalidate) + req->Channel = SMB2_CHANNEL_RDMA_V1; + req->WriteChannelInfoOffset = + cpu_to_le16(offsetof(struct smb2_write_req, Buffer)); + req->WriteChannelInfoLength = + cpu_to_le16(sizeof(struct smbd_buffer_descriptor_v1)); + v1 = (struct smbd_buffer_descriptor_v1 *) &req->Buffer[0]; + v1->offset = cpu_to_le64(wdata->mr->mr->iova); + v1->token = cpu_to_le32(wdata->mr->mr->rkey); + v1->length = cpu_to_le32(wdata->mr->mr->length); + } +#endif + iov[0].iov_len = total_len - 1; + iov[0].iov_base = (char *)req; + + rqst.rq_iov = iov; + rqst.rq_nvec = 1; + rqst.rq_pages = wdata->pages; + rqst.rq_offset = wdata->page_offset; + rqst.rq_npages = wdata->nr_pages; + rqst.rq_pagesz = wdata->pagesz; + rqst.rq_tailsz = wdata->tailsz; +#ifdef CONFIG_CIFS_SMB_DIRECT + if (wdata->mr) { + iov[0].iov_len += sizeof(struct smbd_buffer_descriptor_v1); + rqst.rq_npages = 0; + } +#endif + cifs_dbg(FYI, "async write at %llu %u bytes\n", + io_parms->offset, io_parms->length); + +#ifdef CONFIG_CIFS_SMB_DIRECT + /* For RDMA read, I/O size is in RemainingBytes not in Length */ + if (!wdata->mr) + req->Length = cpu_to_le32(io_parms->length); +#else + req->Length = cpu_to_le32(io_parms->length); +#endif + + if (wdata->credits.value > 0) { + shdr->CreditCharge = cpu_to_le16(DIV_ROUND_UP(wdata->bytes, + SMB2_MAX_BUFFER_SIZE)); + shdr->CreditRequest = cpu_to_le16(le16_to_cpu(shdr->CreditCharge) + 8); + + rc = adjust_credits(server, &wdata->credits, io_parms->length); + if (rc) + goto async_writev_out; + + flags |= CIFS_HAS_CREDITS; + } + + kref_get(&wdata->refcount); + rc = cifs_call_async(server, &rqst, NULL, smb2_writev_callback, NULL, + wdata, flags, &wdata->credits); + + if (rc) { + trace_smb3_write_err(0 /* no xid */, + io_parms->persistent_fid, + io_parms->tcon->tid, + io_parms->tcon->ses->Suid, + io_parms->offset, + io_parms->length, + rc); + kref_put(&wdata->refcount, release); + cifs_stats_fail_inc(tcon, SMB2_WRITE_HE); + } + +async_writev_out: + cifs_small_buf_release(req); + return rc; +} + +/* + * SMB2_write function gets iov pointer to kvec array with n_vec as a length. + * The length field from io_parms must be at least 1 and indicates a number of + * elements with data to write that begins with position 1 in iov array. All + * data length is specified by count. + */ +int +SMB2_write(const unsigned int xid, struct cifs_io_parms *io_parms, + unsigned int *nbytes, struct kvec *iov, int n_vec) +{ + struct smb_rqst rqst; + int rc = 0; + struct smb2_write_req *req = NULL; + struct smb2_write_rsp *rsp = NULL; + int resp_buftype; + struct kvec rsp_iov; + int flags = 0; + unsigned int total_len; + struct TCP_Server_Info *server; + + *nbytes = 0; + + if (n_vec < 1) + return rc; + + if (!io_parms->server) + io_parms->server = cifs_pick_channel(io_parms->tcon->ses); + server = io_parms->server; + if (server == NULL) + return -ECONNABORTED; + + rc = smb2_plain_req_init(SMB2_WRITE, io_parms->tcon, server, + (void **) &req, &total_len); + if (rc) + return rc; + + if (smb3_encryption_required(io_parms->tcon)) + flags |= CIFS_TRANSFORM_REQ; + + req->hdr.Id.SyncId.ProcessId = cpu_to_le32(io_parms->pid); + + req->PersistentFileId = io_parms->persistent_fid; + req->VolatileFileId = io_parms->volatile_fid; + req->WriteChannelInfoOffset = 0; + req->WriteChannelInfoLength = 0; + req->Channel = 0; + req->Length = cpu_to_le32(io_parms->length); + req->Offset = cpu_to_le64(io_parms->offset); + req->DataOffset = cpu_to_le16( + offsetof(struct smb2_write_req, Buffer)); + req->RemainingBytes = 0; + + trace_smb3_write_enter(xid, io_parms->persistent_fid, + io_parms->tcon->tid, io_parms->tcon->ses->Suid, + io_parms->offset, io_parms->length); + + iov[0].iov_base = (char *)req; + /* 1 for Buffer */ + iov[0].iov_len = total_len - 1; + + memset(&rqst, 0, sizeof(struct smb_rqst)); + rqst.rq_iov = iov; + rqst.rq_nvec = n_vec + 1; + + rc = cifs_send_recv(xid, io_parms->tcon->ses, server, + &rqst, + &resp_buftype, flags, &rsp_iov); + rsp = (struct smb2_write_rsp *)rsp_iov.iov_base; + + if (rc) { + trace_smb3_write_err(xid, + req->PersistentFileId, + io_parms->tcon->tid, + io_parms->tcon->ses->Suid, + io_parms->offset, io_parms->length, rc); + cifs_stats_fail_inc(io_parms->tcon, SMB2_WRITE_HE); + cifs_dbg(VFS, "Send error in write = %d\n", rc); + } else { + *nbytes = le32_to_cpu(rsp->DataLength); + trace_smb3_write_done(xid, + req->PersistentFileId, + io_parms->tcon->tid, + io_parms->tcon->ses->Suid, + io_parms->offset, *nbytes); + } + + cifs_small_buf_release(req); + free_rsp_buf(resp_buftype, rsp); + return rc; +} + +int posix_info_sid_size(const void *beg, const void *end) +{ + size_t subauth; + int total; + + if (beg + 1 > end) + return -1; + + subauth = *(u8 *)(beg+1); + if (subauth < 1 || subauth > 15) + return -1; + + total = 1 + 1 + 6 + 4*subauth; + if (beg + total > end) + return -1; + + return total; +} + +int posix_info_parse(const void *beg, const void *end, + struct smb2_posix_info_parsed *out) + +{ + int total_len = 0; + int owner_len, group_len; + int name_len; + const void *owner_sid; + const void *group_sid; + const void *name; + + /* if no end bound given, assume payload to be correct */ + if (!end) { + const struct smb2_posix_info *p = beg; + + end = beg + le32_to_cpu(p->NextEntryOffset); + /* last element will have a 0 offset, pick a sensible bound */ + if (end == beg) + end += 0xFFFF; + } + + /* check base buf */ + if (beg + sizeof(struct smb2_posix_info) > end) + return -1; + total_len = sizeof(struct smb2_posix_info); + + /* check owner sid */ + owner_sid = beg + total_len; + owner_len = posix_info_sid_size(owner_sid, end); + if (owner_len < 0) + return -1; + total_len += owner_len; + + /* check group sid */ + group_sid = beg + total_len; + group_len = posix_info_sid_size(group_sid, end); + if (group_len < 0) + return -1; + total_len += group_len; + + /* check name len */ + if (beg + total_len + 4 > end) + return -1; + name_len = le32_to_cpu(*(__le32 *)(beg + total_len)); + if (name_len < 1 || name_len > 0xFFFF) + return -1; + total_len += 4; + + /* check name */ + name = beg + total_len; + if (name + name_len > end) + return -1; + total_len += name_len; + + if (out) { + out->base = beg; + out->size = total_len; + out->name_len = name_len; + out->name = name; + memcpy(&out->owner, owner_sid, owner_len); + memcpy(&out->group, group_sid, group_len); + } + return total_len; +} + +static int posix_info_extra_size(const void *beg, const void *end) +{ + int len = posix_info_parse(beg, end, NULL); + + if (len < 0) + return -1; + return len - sizeof(struct smb2_posix_info); +} + +static unsigned int +num_entries(int infotype, char *bufstart, char *end_of_buf, char **lastentry, + size_t size) +{ + int len; + unsigned int entrycount = 0; + unsigned int next_offset = 0; + char *entryptr; + FILE_DIRECTORY_INFO *dir_info; + + if (bufstart == NULL) + return 0; + + entryptr = bufstart; + + while (1) { + if (entryptr + next_offset < entryptr || + entryptr + next_offset > end_of_buf || + entryptr + next_offset + size > end_of_buf) { + cifs_dbg(VFS, "malformed search entry would overflow\n"); + break; + } + + entryptr = entryptr + next_offset; + dir_info = (FILE_DIRECTORY_INFO *)entryptr; + + if (infotype == SMB_FIND_FILE_POSIX_INFO) + len = posix_info_extra_size(entryptr, end_of_buf); + else + len = le32_to_cpu(dir_info->FileNameLength); + + if (len < 0 || + entryptr + len < entryptr || + entryptr + len > end_of_buf || + entryptr + len + size > end_of_buf) { + cifs_dbg(VFS, "directory entry name would overflow frame end of buf %p\n", + end_of_buf); + break; + } + + *lastentry = entryptr; + entrycount++; + + next_offset = le32_to_cpu(dir_info->NextEntryOffset); + if (!next_offset) + break; + } + + return entrycount; +} + +/* + * Readdir/FindFirst + */ +int SMB2_query_directory_init(const unsigned int xid, + struct cifs_tcon *tcon, + struct TCP_Server_Info *server, + struct smb_rqst *rqst, + u64 persistent_fid, u64 volatile_fid, + int index, int info_level) +{ + struct smb2_query_directory_req *req; + unsigned char *bufptr; + __le16 asteriks = cpu_to_le16('*'); + unsigned int output_size = CIFSMaxBufSize - + MAX_SMB2_CREATE_RESPONSE_SIZE - + MAX_SMB2_CLOSE_RESPONSE_SIZE; + unsigned int total_len; + struct kvec *iov = rqst->rq_iov; + int len, rc; + + rc = smb2_plain_req_init(SMB2_QUERY_DIRECTORY, tcon, server, + (void **) &req, &total_len); + if (rc) + return rc; + + switch (info_level) { + case SMB_FIND_FILE_DIRECTORY_INFO: + req->FileInformationClass = FILE_DIRECTORY_INFORMATION; + break; + case SMB_FIND_FILE_ID_FULL_DIR_INFO: + req->FileInformationClass = FILEID_FULL_DIRECTORY_INFORMATION; + break; + case SMB_FIND_FILE_POSIX_INFO: + req->FileInformationClass = SMB_FIND_FILE_POSIX_INFO; + break; + default: + cifs_tcon_dbg(VFS, "info level %u isn't supported\n", + info_level); + return -EINVAL; + } + + req->FileIndex = cpu_to_le32(index); + req->PersistentFileId = persistent_fid; + req->VolatileFileId = volatile_fid; + + len = 0x2; + bufptr = req->Buffer; + memcpy(bufptr, &asteriks, len); + + req->FileNameOffset = + cpu_to_le16(sizeof(struct smb2_query_directory_req) - 1); + req->FileNameLength = cpu_to_le16(len); + /* + * BB could be 30 bytes or so longer if we used SMB2 specific + * buffer lengths, but this is safe and close enough. + */ + output_size = min_t(unsigned int, output_size, server->maxBuf); + output_size = min_t(unsigned int, output_size, 2 << 15); + req->OutputBufferLength = cpu_to_le32(output_size); + + iov[0].iov_base = (char *)req; + /* 1 for Buffer */ + iov[0].iov_len = total_len - 1; + + iov[1].iov_base = (char *)(req->Buffer); + iov[1].iov_len = len; + + trace_smb3_query_dir_enter(xid, persistent_fid, tcon->tid, + tcon->ses->Suid, index, output_size); + + return 0; +} + +void SMB2_query_directory_free(struct smb_rqst *rqst) +{ + if (rqst && rqst->rq_iov) { + cifs_small_buf_release(rqst->rq_iov[0].iov_base); /* request */ + } +} + +int +smb2_parse_query_directory(struct cifs_tcon *tcon, + struct kvec *rsp_iov, + int resp_buftype, + struct cifs_search_info *srch_inf) +{ + struct smb2_query_directory_rsp *rsp; + size_t info_buf_size; + char *end_of_smb; + int rc; + + rsp = (struct smb2_query_directory_rsp *)rsp_iov->iov_base; + + switch (srch_inf->info_level) { + case SMB_FIND_FILE_DIRECTORY_INFO: + info_buf_size = sizeof(FILE_DIRECTORY_INFO) - 1; + break; + case SMB_FIND_FILE_ID_FULL_DIR_INFO: + info_buf_size = sizeof(SEARCH_ID_FULL_DIR_INFO) - 1; + break; + case SMB_FIND_FILE_POSIX_INFO: + /* note that posix payload are variable size */ + info_buf_size = sizeof(struct smb2_posix_info); + break; + default: + cifs_tcon_dbg(VFS, "info level %u isn't supported\n", + srch_inf->info_level); + return -EINVAL; + } + + rc = smb2_validate_iov(le16_to_cpu(rsp->OutputBufferOffset), + le32_to_cpu(rsp->OutputBufferLength), rsp_iov, + info_buf_size); + if (rc) { + cifs_tcon_dbg(VFS, "bad info payload"); + return rc; + } + + srch_inf->unicode = true; + + if (srch_inf->ntwrk_buf_start) { + if (srch_inf->smallBuf) + cifs_small_buf_release(srch_inf->ntwrk_buf_start); + else + cifs_buf_release(srch_inf->ntwrk_buf_start); + } + srch_inf->ntwrk_buf_start = (char *)rsp; + srch_inf->srch_entries_start = srch_inf->last_entry = + (char *)rsp + le16_to_cpu(rsp->OutputBufferOffset); + end_of_smb = rsp_iov->iov_len + (char *)rsp; + + srch_inf->entries_in_buffer = num_entries( + srch_inf->info_level, + srch_inf->srch_entries_start, + end_of_smb, + &srch_inf->last_entry, + info_buf_size); + + srch_inf->index_of_last_entry += srch_inf->entries_in_buffer; + cifs_dbg(FYI, "num entries %d last_index %lld srch start %p srch end %p\n", + srch_inf->entries_in_buffer, srch_inf->index_of_last_entry, + srch_inf->srch_entries_start, srch_inf->last_entry); + if (resp_buftype == CIFS_LARGE_BUFFER) + srch_inf->smallBuf = false; + else if (resp_buftype == CIFS_SMALL_BUFFER) + srch_inf->smallBuf = true; + else + cifs_tcon_dbg(VFS, "Invalid search buffer type\n"); + + return 0; +} + +int +SMB2_query_directory(const unsigned int xid, struct cifs_tcon *tcon, + u64 persistent_fid, u64 volatile_fid, int index, + struct cifs_search_info *srch_inf) +{ + struct smb_rqst rqst; + struct kvec iov[SMB2_QUERY_DIRECTORY_IOV_SIZE]; + struct smb2_query_directory_rsp *rsp = NULL; + int resp_buftype = CIFS_NO_BUFFER; + struct kvec rsp_iov; + int rc = 0; + struct cifs_ses *ses = tcon->ses; + struct TCP_Server_Info *server = cifs_pick_channel(ses); + int flags = 0; + + if (!ses || !(ses->server)) + return -EIO; + + if (smb3_encryption_required(tcon)) + flags |= CIFS_TRANSFORM_REQ; + + memset(&rqst, 0, sizeof(struct smb_rqst)); + memset(&iov, 0, sizeof(iov)); + rqst.rq_iov = iov; + rqst.rq_nvec = SMB2_QUERY_DIRECTORY_IOV_SIZE; + + rc = SMB2_query_directory_init(xid, tcon, server, + &rqst, persistent_fid, + volatile_fid, index, + srch_inf->info_level); + if (rc) + goto qdir_exit; + + rc = cifs_send_recv(xid, ses, server, + &rqst, &resp_buftype, flags, &rsp_iov); + rsp = (struct smb2_query_directory_rsp *)rsp_iov.iov_base; + + if (rc) { + if (rc == -ENODATA && + rsp->hdr.Status == STATUS_NO_MORE_FILES) { + trace_smb3_query_dir_done(xid, persistent_fid, + tcon->tid, tcon->ses->Suid, index, 0); + srch_inf->endOfSearch = true; + rc = 0; + } else { + trace_smb3_query_dir_err(xid, persistent_fid, tcon->tid, + tcon->ses->Suid, index, 0, rc); + cifs_stats_fail_inc(tcon, SMB2_QUERY_DIRECTORY_HE); + } + goto qdir_exit; + } + + rc = smb2_parse_query_directory(tcon, &rsp_iov, resp_buftype, + srch_inf); + if (rc) { + trace_smb3_query_dir_err(xid, persistent_fid, tcon->tid, + tcon->ses->Suid, index, 0, rc); + goto qdir_exit; + } + resp_buftype = CIFS_NO_BUFFER; + + trace_smb3_query_dir_done(xid, persistent_fid, tcon->tid, + tcon->ses->Suid, index, srch_inf->entries_in_buffer); + +qdir_exit: + SMB2_query_directory_free(&rqst); + free_rsp_buf(resp_buftype, rsp); + return rc; +} + +int +SMB2_set_info_init(struct cifs_tcon *tcon, struct TCP_Server_Info *server, + struct smb_rqst *rqst, + u64 persistent_fid, u64 volatile_fid, u32 pid, + u8 info_class, u8 info_type, u32 additional_info, + void **data, unsigned int *size) +{ + struct smb2_set_info_req *req; + struct kvec *iov = rqst->rq_iov; + unsigned int i, total_len; + int rc; + + rc = smb2_plain_req_init(SMB2_SET_INFO, tcon, server, + (void **) &req, &total_len); + if (rc) + return rc; + + req->hdr.Id.SyncId.ProcessId = cpu_to_le32(pid); + req->InfoType = info_type; + req->FileInfoClass = info_class; + req->PersistentFileId = persistent_fid; + req->VolatileFileId = volatile_fid; + req->AdditionalInformation = cpu_to_le32(additional_info); + + req->BufferOffset = + cpu_to_le16(sizeof(struct smb2_set_info_req) - 1); + req->BufferLength = cpu_to_le32(*size); + + memcpy(req->Buffer, *data, *size); + total_len += *size; + + iov[0].iov_base = (char *)req; + /* 1 for Buffer */ + iov[0].iov_len = total_len - 1; + + for (i = 1; i < rqst->rq_nvec; i++) { + le32_add_cpu(&req->BufferLength, size[i]); + iov[i].iov_base = (char *)data[i]; + iov[i].iov_len = size[i]; + } + + return 0; +} + +void +SMB2_set_info_free(struct smb_rqst *rqst) +{ + if (rqst && rqst->rq_iov) + cifs_buf_release(rqst->rq_iov[0].iov_base); /* request */ +} + +static int +send_set_info(const unsigned int xid, struct cifs_tcon *tcon, + u64 persistent_fid, u64 volatile_fid, u32 pid, u8 info_class, + u8 info_type, u32 additional_info, unsigned int num, + void **data, unsigned int *size) +{ + struct smb_rqst rqst; + struct smb2_set_info_rsp *rsp = NULL; + struct kvec *iov; + struct kvec rsp_iov; + int rc = 0; + int resp_buftype; + struct cifs_ses *ses = tcon->ses; + struct TCP_Server_Info *server = cifs_pick_channel(ses); + int flags = 0; + + if (!ses || !server) + return -EIO; + + if (!num) + return -EINVAL; + + if (smb3_encryption_required(tcon)) + flags |= CIFS_TRANSFORM_REQ; + + iov = kmalloc_array(num, sizeof(struct kvec), GFP_KERNEL); + if (!iov) + return -ENOMEM; + + memset(&rqst, 0, sizeof(struct smb_rqst)); + rqst.rq_iov = iov; + rqst.rq_nvec = num; + + rc = SMB2_set_info_init(tcon, server, + &rqst, persistent_fid, volatile_fid, pid, + info_class, info_type, additional_info, + data, size); + if (rc) { + kfree(iov); + return rc; + } + + + rc = cifs_send_recv(xid, ses, server, + &rqst, &resp_buftype, flags, + &rsp_iov); + SMB2_set_info_free(&rqst); + rsp = (struct smb2_set_info_rsp *)rsp_iov.iov_base; + + if (rc != 0) { + cifs_stats_fail_inc(tcon, SMB2_SET_INFO_HE); + trace_smb3_set_info_err(xid, persistent_fid, tcon->tid, + ses->Suid, info_class, (__u32)info_type, rc); + } + + free_rsp_buf(resp_buftype, rsp); + kfree(iov); + return rc; +} + +int +SMB2_set_eof(const unsigned int xid, struct cifs_tcon *tcon, u64 persistent_fid, + u64 volatile_fid, u32 pid, __le64 *eof) +{ + struct smb2_file_eof_info info; + void *data; + unsigned int size; + + info.EndOfFile = *eof; + + data = &info; + size = sizeof(struct smb2_file_eof_info); + + trace_smb3_set_eof(xid, persistent_fid, tcon->tid, tcon->ses->Suid, le64_to_cpu(*eof)); + + return send_set_info(xid, tcon, persistent_fid, volatile_fid, + pid, FILE_END_OF_FILE_INFORMATION, SMB2_O_INFO_FILE, + 0, 1, &data, &size); +} + +int +SMB2_set_acl(const unsigned int xid, struct cifs_tcon *tcon, + u64 persistent_fid, u64 volatile_fid, + struct cifs_ntsd *pnntsd, int pacllen, int aclflag) +{ + return send_set_info(xid, tcon, persistent_fid, volatile_fid, + current->tgid, 0, SMB2_O_INFO_SECURITY, aclflag, + 1, (void **)&pnntsd, &pacllen); +} + +int +SMB2_set_ea(const unsigned int xid, struct cifs_tcon *tcon, + u64 persistent_fid, u64 volatile_fid, + struct smb2_file_full_ea_info *buf, int len) +{ + return send_set_info(xid, tcon, persistent_fid, volatile_fid, + current->tgid, FILE_FULL_EA_INFORMATION, SMB2_O_INFO_FILE, + 0, 1, (void **)&buf, &len); +} + +int +SMB2_oplock_break(const unsigned int xid, struct cifs_tcon *tcon, + const u64 persistent_fid, const u64 volatile_fid, + __u8 oplock_level) +{ + struct smb_rqst rqst; + int rc; + struct smb2_oplock_break *req = NULL; + struct cifs_ses *ses = tcon->ses; + struct TCP_Server_Info *server = cifs_pick_channel(ses); + int flags = CIFS_OBREAK_OP; + unsigned int total_len; + struct kvec iov[1]; + struct kvec rsp_iov; + int resp_buf_type; + + cifs_dbg(FYI, "SMB2_oplock_break\n"); + rc = smb2_plain_req_init(SMB2_OPLOCK_BREAK, tcon, server, + (void **) &req, &total_len); + if (rc) + return rc; + + if (smb3_encryption_required(tcon)) + flags |= CIFS_TRANSFORM_REQ; + + req->VolatileFid = volatile_fid; + req->PersistentFid = persistent_fid; + req->OplockLevel = oplock_level; + req->hdr.CreditRequest = cpu_to_le16(1); + + flags |= CIFS_NO_RSP_BUF; + + iov[0].iov_base = (char *)req; + iov[0].iov_len = total_len; + + memset(&rqst, 0, sizeof(struct smb_rqst)); + rqst.rq_iov = iov; + rqst.rq_nvec = 1; + + rc = cifs_send_recv(xid, ses, server, + &rqst, &resp_buf_type, flags, &rsp_iov); + cifs_small_buf_release(req); + + if (rc) { + cifs_stats_fail_inc(tcon, SMB2_OPLOCK_BREAK_HE); + cifs_dbg(FYI, "Send error in Oplock Break = %d\n", rc); + } + + return rc; +} + +void +smb2_copy_fs_info_to_kstatfs(struct smb2_fs_full_size_info *pfs_inf, + struct kstatfs *kst) +{ + kst->f_bsize = le32_to_cpu(pfs_inf->BytesPerSector) * + le32_to_cpu(pfs_inf->SectorsPerAllocationUnit); + kst->f_blocks = le64_to_cpu(pfs_inf->TotalAllocationUnits); + kst->f_bfree = kst->f_bavail = + le64_to_cpu(pfs_inf->CallerAvailableAllocationUnits); + return; +} + +static void +copy_posix_fs_info_to_kstatfs(FILE_SYSTEM_POSIX_INFO *response_data, + struct kstatfs *kst) +{ + kst->f_bsize = le32_to_cpu(response_data->BlockSize); + kst->f_blocks = le64_to_cpu(response_data->TotalBlocks); + kst->f_bfree = le64_to_cpu(response_data->BlocksAvail); + if (response_data->UserBlocksAvail == cpu_to_le64(-1)) + kst->f_bavail = kst->f_bfree; + else + kst->f_bavail = le64_to_cpu(response_data->UserBlocksAvail); + if (response_data->TotalFileNodes != cpu_to_le64(-1)) + kst->f_files = le64_to_cpu(response_data->TotalFileNodes); + if (response_data->FreeFileNodes != cpu_to_le64(-1)) + kst->f_ffree = le64_to_cpu(response_data->FreeFileNodes); + + return; +} + +static int +build_qfs_info_req(struct kvec *iov, struct cifs_tcon *tcon, + struct TCP_Server_Info *server, + int level, int outbuf_len, u64 persistent_fid, + u64 volatile_fid) +{ + int rc; + struct smb2_query_info_req *req; + unsigned int total_len; + + cifs_dbg(FYI, "Query FSInfo level %d\n", level); + + if ((tcon->ses == NULL) || server == NULL) + return -EIO; + + rc = smb2_plain_req_init(SMB2_QUERY_INFO, tcon, server, + (void **) &req, &total_len); + if (rc) + return rc; + + req->InfoType = SMB2_O_INFO_FILESYSTEM; + req->FileInfoClass = level; + req->PersistentFileId = persistent_fid; + req->VolatileFileId = volatile_fid; + /* 1 for pad */ + req->InputBufferOffset = + cpu_to_le16(sizeof(struct smb2_query_info_req) - 1); + req->OutputBufferLength = cpu_to_le32( + outbuf_len + sizeof(struct smb2_query_info_rsp) - 1); + + iov->iov_base = (char *)req; + iov->iov_len = total_len; + return 0; +} + +int +SMB311_posix_qfs_info(const unsigned int xid, struct cifs_tcon *tcon, + u64 persistent_fid, u64 volatile_fid, struct kstatfs *fsdata) +{ + struct smb_rqst rqst; + struct smb2_query_info_rsp *rsp = NULL; + struct kvec iov; + struct kvec rsp_iov; + int rc = 0; + int resp_buftype; + struct cifs_ses *ses = tcon->ses; + struct TCP_Server_Info *server = cifs_pick_channel(ses); + FILE_SYSTEM_POSIX_INFO *info = NULL; + int flags = 0; + + rc = build_qfs_info_req(&iov, tcon, server, + FS_POSIX_INFORMATION, + sizeof(FILE_SYSTEM_POSIX_INFO), + persistent_fid, volatile_fid); + if (rc) + return rc; + + if (smb3_encryption_required(tcon)) + flags |= CIFS_TRANSFORM_REQ; + + memset(&rqst, 0, sizeof(struct smb_rqst)); + rqst.rq_iov = &iov; + rqst.rq_nvec = 1; + + rc = cifs_send_recv(xid, ses, server, + &rqst, &resp_buftype, flags, &rsp_iov); + cifs_small_buf_release(iov.iov_base); + if (rc) { + cifs_stats_fail_inc(tcon, SMB2_QUERY_INFO_HE); + goto posix_qfsinf_exit; + } + rsp = (struct smb2_query_info_rsp *)rsp_iov.iov_base; + + info = (FILE_SYSTEM_POSIX_INFO *)( + le16_to_cpu(rsp->OutputBufferOffset) + (char *)rsp); + rc = smb2_validate_iov(le16_to_cpu(rsp->OutputBufferOffset), + le32_to_cpu(rsp->OutputBufferLength), &rsp_iov, + sizeof(FILE_SYSTEM_POSIX_INFO)); + if (!rc) + copy_posix_fs_info_to_kstatfs(info, fsdata); + +posix_qfsinf_exit: + free_rsp_buf(resp_buftype, rsp_iov.iov_base); + return rc; +} + +int +SMB2_QFS_info(const unsigned int xid, struct cifs_tcon *tcon, + u64 persistent_fid, u64 volatile_fid, struct kstatfs *fsdata) +{ + struct smb_rqst rqst; + struct smb2_query_info_rsp *rsp = NULL; + struct kvec iov; + struct kvec rsp_iov; + int rc = 0; + int resp_buftype; + struct cifs_ses *ses = tcon->ses; + struct TCP_Server_Info *server = cifs_pick_channel(ses); + struct smb2_fs_full_size_info *info = NULL; + int flags = 0; + + rc = build_qfs_info_req(&iov, tcon, server, + FS_FULL_SIZE_INFORMATION, + sizeof(struct smb2_fs_full_size_info), + persistent_fid, volatile_fid); + if (rc) + return rc; + + if (smb3_encryption_required(tcon)) + flags |= CIFS_TRANSFORM_REQ; + + memset(&rqst, 0, sizeof(struct smb_rqst)); + rqst.rq_iov = &iov; + rqst.rq_nvec = 1; + + rc = cifs_send_recv(xid, ses, server, + &rqst, &resp_buftype, flags, &rsp_iov); + cifs_small_buf_release(iov.iov_base); + if (rc) { + cifs_stats_fail_inc(tcon, SMB2_QUERY_INFO_HE); + goto qfsinf_exit; + } + rsp = (struct smb2_query_info_rsp *)rsp_iov.iov_base; + + info = (struct smb2_fs_full_size_info *)( + le16_to_cpu(rsp->OutputBufferOffset) + (char *)rsp); + rc = smb2_validate_iov(le16_to_cpu(rsp->OutputBufferOffset), + le32_to_cpu(rsp->OutputBufferLength), &rsp_iov, + sizeof(struct smb2_fs_full_size_info)); + if (!rc) + smb2_copy_fs_info_to_kstatfs(info, fsdata); + +qfsinf_exit: + free_rsp_buf(resp_buftype, rsp_iov.iov_base); + return rc; +} + +int +SMB2_QFS_attr(const unsigned int xid, struct cifs_tcon *tcon, + u64 persistent_fid, u64 volatile_fid, int level) +{ + struct smb_rqst rqst; + struct smb2_query_info_rsp *rsp = NULL; + struct kvec iov; + struct kvec rsp_iov; + int rc = 0; + int resp_buftype, max_len, min_len; + struct cifs_ses *ses = tcon->ses; + struct TCP_Server_Info *server = cifs_pick_channel(ses); + unsigned int rsp_len, offset; + int flags = 0; + + if (level == FS_DEVICE_INFORMATION) { + max_len = sizeof(FILE_SYSTEM_DEVICE_INFO); + min_len = sizeof(FILE_SYSTEM_DEVICE_INFO); + } else if (level == FS_ATTRIBUTE_INFORMATION) { + max_len = sizeof(FILE_SYSTEM_ATTRIBUTE_INFO); + min_len = MIN_FS_ATTR_INFO_SIZE; + } else if (level == FS_SECTOR_SIZE_INFORMATION) { + max_len = sizeof(struct smb3_fs_ss_info); + min_len = sizeof(struct smb3_fs_ss_info); + } else if (level == FS_VOLUME_INFORMATION) { + max_len = sizeof(struct smb3_fs_vol_info) + MAX_VOL_LABEL_LEN; + min_len = sizeof(struct smb3_fs_vol_info); + } else { + cifs_dbg(FYI, "Invalid qfsinfo level %d\n", level); + return -EINVAL; + } + + rc = build_qfs_info_req(&iov, tcon, server, + level, max_len, + persistent_fid, volatile_fid); + if (rc) + return rc; + + if (smb3_encryption_required(tcon)) + flags |= CIFS_TRANSFORM_REQ; + + memset(&rqst, 0, sizeof(struct smb_rqst)); + rqst.rq_iov = &iov; + rqst.rq_nvec = 1; + + rc = cifs_send_recv(xid, ses, server, + &rqst, &resp_buftype, flags, &rsp_iov); + cifs_small_buf_release(iov.iov_base); + if (rc) { + cifs_stats_fail_inc(tcon, SMB2_QUERY_INFO_HE); + goto qfsattr_exit; + } + rsp = (struct smb2_query_info_rsp *)rsp_iov.iov_base; + + rsp_len = le32_to_cpu(rsp->OutputBufferLength); + offset = le16_to_cpu(rsp->OutputBufferOffset); + rc = smb2_validate_iov(offset, rsp_len, &rsp_iov, min_len); + if (rc) + goto qfsattr_exit; + + if (level == FS_ATTRIBUTE_INFORMATION) + memcpy(&tcon->fsAttrInfo, offset + + (char *)rsp, min_t(unsigned int, + rsp_len, max_len)); + else if (level == FS_DEVICE_INFORMATION) + memcpy(&tcon->fsDevInfo, offset + + (char *)rsp, sizeof(FILE_SYSTEM_DEVICE_INFO)); + else if (level == FS_SECTOR_SIZE_INFORMATION) { + struct smb3_fs_ss_info *ss_info = (struct smb3_fs_ss_info *) + (offset + (char *)rsp); + tcon->ss_flags = le32_to_cpu(ss_info->Flags); + tcon->perf_sector_size = + le32_to_cpu(ss_info->PhysicalBytesPerSectorForPerf); + } else if (level == FS_VOLUME_INFORMATION) { + struct smb3_fs_vol_info *vol_info = (struct smb3_fs_vol_info *) + (offset + (char *)rsp); + tcon->vol_serial_number = vol_info->VolumeSerialNumber; + tcon->vol_create_time = vol_info->VolumeCreationTime; + } + +qfsattr_exit: + free_rsp_buf(resp_buftype, rsp_iov.iov_base); + return rc; +} + +int +smb2_lockv(const unsigned int xid, struct cifs_tcon *tcon, + const __u64 persist_fid, const __u64 volatile_fid, const __u32 pid, + const __u32 num_lock, struct smb2_lock_element *buf) +{ + struct smb_rqst rqst; + int rc = 0; + struct smb2_lock_req *req = NULL; + struct kvec iov[2]; + struct kvec rsp_iov; + int resp_buf_type; + unsigned int count; + int flags = CIFS_NO_RSP_BUF; + unsigned int total_len; + struct TCP_Server_Info *server = cifs_pick_channel(tcon->ses); + + cifs_dbg(FYI, "smb2_lockv num lock %d\n", num_lock); + + rc = smb2_plain_req_init(SMB2_LOCK, tcon, server, + (void **) &req, &total_len); + if (rc) + return rc; + + if (smb3_encryption_required(tcon)) + flags |= CIFS_TRANSFORM_REQ; + + req->hdr.Id.SyncId.ProcessId = cpu_to_le32(pid); + req->LockCount = cpu_to_le16(num_lock); + + req->PersistentFileId = persist_fid; + req->VolatileFileId = volatile_fid; + + count = num_lock * sizeof(struct smb2_lock_element); + + iov[0].iov_base = (char *)req; + iov[0].iov_len = total_len - sizeof(struct smb2_lock_element); + iov[1].iov_base = (char *)buf; + iov[1].iov_len = count; + + cifs_stats_inc(&tcon->stats.cifs_stats.num_locks); + + memset(&rqst, 0, sizeof(struct smb_rqst)); + rqst.rq_iov = iov; + rqst.rq_nvec = 2; + + rc = cifs_send_recv(xid, tcon->ses, server, + &rqst, &resp_buf_type, flags, + &rsp_iov); + cifs_small_buf_release(req); + if (rc) { + cifs_dbg(FYI, "Send error in smb2_lockv = %d\n", rc); + cifs_stats_fail_inc(tcon, SMB2_LOCK_HE); + trace_smb3_lock_err(xid, persist_fid, tcon->tid, + tcon->ses->Suid, rc); + } + + return rc; +} + +int +SMB2_lock(const unsigned int xid, struct cifs_tcon *tcon, + const __u64 persist_fid, const __u64 volatile_fid, const __u32 pid, + const __u64 length, const __u64 offset, const __u32 lock_flags, + const bool wait) +{ + struct smb2_lock_element lock; + + lock.Offset = cpu_to_le64(offset); + lock.Length = cpu_to_le64(length); + lock.Flags = cpu_to_le32(lock_flags); + if (!wait && lock_flags != SMB2_LOCKFLAG_UNLOCK) + lock.Flags |= cpu_to_le32(SMB2_LOCKFLAG_FAIL_IMMEDIATELY); + + return smb2_lockv(xid, tcon, persist_fid, volatile_fid, pid, 1, &lock); +} + +int +SMB2_lease_break(const unsigned int xid, struct cifs_tcon *tcon, + __u8 *lease_key, const __le32 lease_state) +{ + struct smb_rqst rqst; + int rc; + struct smb2_lease_ack *req = NULL; + struct cifs_ses *ses = tcon->ses; + int flags = CIFS_OBREAK_OP; + unsigned int total_len; + struct kvec iov[1]; + struct kvec rsp_iov; + int resp_buf_type; + __u64 *please_key_high; + __u64 *please_key_low; + struct TCP_Server_Info *server = cifs_pick_channel(tcon->ses); + + cifs_dbg(FYI, "SMB2_lease_break\n"); + rc = smb2_plain_req_init(SMB2_OPLOCK_BREAK, tcon, server, + (void **) &req, &total_len); + if (rc) + return rc; + + if (smb3_encryption_required(tcon)) + flags |= CIFS_TRANSFORM_REQ; + + req->hdr.CreditRequest = cpu_to_le16(1); + req->StructureSize = cpu_to_le16(36); + total_len += 12; + + memcpy(req->LeaseKey, lease_key, 16); + req->LeaseState = lease_state; + + flags |= CIFS_NO_RSP_BUF; + + iov[0].iov_base = (char *)req; + iov[0].iov_len = total_len; + + memset(&rqst, 0, sizeof(struct smb_rqst)); + rqst.rq_iov = iov; + rqst.rq_nvec = 1; + + rc = cifs_send_recv(xid, ses, server, + &rqst, &resp_buf_type, flags, &rsp_iov); + cifs_small_buf_release(req); + + please_key_low = (__u64 *)lease_key; + please_key_high = (__u64 *)(lease_key+8); + if (rc) { + cifs_stats_fail_inc(tcon, SMB2_OPLOCK_BREAK_HE); + trace_smb3_lease_err(le32_to_cpu(lease_state), tcon->tid, + ses->Suid, *please_key_low, *please_key_high, rc); + cifs_dbg(FYI, "Send error in Lease Break = %d\n", rc); + } else + trace_smb3_lease_done(le32_to_cpu(lease_state), tcon->tid, + ses->Suid, *please_key_low, *please_key_high); + + return rc; +} diff --git a/fs/smb/client/smb2pdu.h b/fs/smb/client/smb2pdu.h new file mode 100644 index 0000000000000..1237bb86e93a8 --- /dev/null +++ b/fs/smb/client/smb2pdu.h @@ -0,0 +1,434 @@ +/* SPDX-License-Identifier: LGPL-2.1 */ +/* + * + * Copyright (c) International Business Machines Corp., 2009, 2013 + * Etersoft, 2012 + * Author(s): Steve French (sfrench@us.ibm.com) + * Pavel Shilovsky (pshilovsky@samba.org) 2012 + * + */ + +#ifndef _SMB2PDU_H +#define _SMB2PDU_H + +#include +#include "cifsacl.h" + +/* 52 transform hdr + 64 hdr + 88 create rsp */ +#define SMB2_TRANSFORM_HEADER_SIZE 52 +#define MAX_SMB2_HDR_SIZE 204 + +/* The total header size for SMB2 read and write */ +#define SMB2_READWRITE_PDU_HEADER_SIZE (48 + sizeof(struct smb2_hdr)) + +/* See MS-SMB2 2.2.43 */ +struct smb2_rdma_transform { + __le16 RdmaDescriptorOffset; + __le16 RdmaDescriptorLength; + __le32 Channel; /* for values see channel description in smb2 read above */ + __le16 TransformCount; + __le16 Reserved1; + __le32 Reserved2; +} __packed; + +/* TransformType */ +#define SMB2_RDMA_TRANSFORM_TYPE_ENCRYPTION 0x0001 +#define SMB2_RDMA_TRANSFORM_TYPE_SIGNING 0x0002 + +struct smb2_rdma_crypto_transform { + __le16 TransformType; + __le16 SignatureLength; + __le16 NonceLength; + __u16 Reserved; + __u8 Signature[]; /* variable length */ + /* u8 Nonce[] */ + /* followed by padding */ +} __packed; + +/* + * Definitions for SMB2 Protocol Data Units (network frames) + * + * See MS-SMB2.PDF specification for protocol details. + * The Naming convention is the lower case version of the SMB2 + * command code name for the struct. Note that structures must be packed. + * + */ + +#define COMPOUND_FID 0xFFFFFFFFFFFFFFFFULL + +#define SMB2_SYMLINK_STRUCT_SIZE \ + (sizeof(struct smb2_err_rsp) - 1 + sizeof(struct smb2_symlink_err_rsp)) + +#define SYMLINK_ERROR_TAG 0x4c4d5953 + +struct smb2_symlink_err_rsp { + __le32 SymLinkLength; + __le32 SymLinkErrorTag; + __le32 ReparseTag; + __le16 ReparseDataLength; + __le16 UnparsedPathLength; + __le16 SubstituteNameOffset; + __le16 SubstituteNameLength; + __le16 PrintNameOffset; + __le16 PrintNameLength; + __le32 Flags; + __u8 PathBuffer[]; +} __packed; + +/* SMB 3.1.1 and later dialects. See MS-SMB2 section 2.2.2.1 */ +struct smb2_error_context_rsp { + __le32 ErrorDataLength; + __le32 ErrorId; + __u8 ErrorContextData; /* ErrorDataLength long array */ +} __packed; + +/* ErrorId values */ +#define SMB2_ERROR_ID_DEFAULT 0x00000000 +#define SMB2_ERROR_ID_SHARE_REDIRECT cpu_to_le32(0x72645253) /* "rdRS" */ + +/* Defines for Type field below (see MS-SMB2 2.2.2.2.2.1) */ +#define MOVE_DST_IPADDR_V4 cpu_to_le32(0x00000001) +#define MOVE_DST_IPADDR_V6 cpu_to_le32(0x00000002) + +struct move_dst_ipaddr { + __le32 Type; + __u32 Reserved; + __u8 address[16]; /* IPv4 followed by 12 bytes rsvd or IPv6 address */ +} __packed; + +struct share_redirect_error_context_rsp { + __le32 StructureSize; + __le32 NotificationType; + __le32 ResourceNameOffset; + __le32 ResourceNameLength; + __le16 Reserved; + __le16 TargetType; + __le32 IPAddrCount; + struct move_dst_ipaddr IpAddrMoveList[]; + /* __u8 ResourceName[] */ /* Name of share as counted Unicode string */ +} __packed; + +/* + * Maximum number of iovs we need for an open/create request. + * [0] : struct smb2_create_req + * [1] : path + * [2] : lease context + * [3] : durable context + * [4] : posix context + * [5] : time warp context + * [6] : query id context + * [7] : compound padding + */ +#define SMB2_CREATE_IOV_SIZE 8 + +/* + * Maximum size of a SMB2_CREATE response is 64 (smb2 header) + + * 88 (fixed part of create response) + 520 (path) + 208 (contexts) + + * 2 bytes of padding. + */ +#define MAX_SMB2_CREATE_RESPONSE_SIZE 880 + +#define SMB2_LEASE_READ_CACHING_HE 0x01 +#define SMB2_LEASE_HANDLE_CACHING_HE 0x02 +#define SMB2_LEASE_WRITE_CACHING_HE 0x04 + +struct create_durable { + struct create_context ccontext; + __u8 Name[8]; + union { + __u8 Reserved[16]; + struct { + __u64 PersistentFileId; + __u64 VolatileFileId; + } Fid; + } Data; +} __packed; + +/* See MS-SMB2 2.2.13.2.11 */ +/* Flags */ +#define SMB2_DHANDLE_FLAG_PERSISTENT 0x00000002 +struct durable_context_v2 { + __le32 Timeout; + __le32 Flags; + __u64 Reserved; + __u8 CreateGuid[16]; +} __packed; + +struct create_durable_v2 { + struct create_context ccontext; + __u8 Name[8]; + struct durable_context_v2 dcontext; +} __packed; + +/* See MS-SMB2 2.2.13.2.12 */ +struct durable_reconnect_context_v2 { + struct { + __u64 PersistentFileId; + __u64 VolatileFileId; + } Fid; + __u8 CreateGuid[16]; + __le32 Flags; /* see above DHANDLE_FLAG_PERSISTENT */ +} __packed; + +/* See MS-SMB2 2.2.14.2.9 */ +struct create_on_disk_id { + struct create_context ccontext; + __u8 Name[8]; + __le64 DiskFileId; + __le64 VolumeId; + __u32 Reserved[4]; +} __packed; + +/* See MS-SMB2 2.2.14.2.12 */ +struct durable_reconnect_context_v2_rsp { + __le32 Timeout; + __le32 Flags; /* see above DHANDLE_FLAG_PERSISTENT */ +} __packed; + +struct create_durable_handle_reconnect_v2 { + struct create_context ccontext; + __u8 Name[8]; + struct durable_reconnect_context_v2 dcontext; + __u8 Pad[4]; +} __packed; + +/* See MS-SMB2 2.2.13.2.5 */ +struct crt_twarp_ctxt { + struct create_context ccontext; + __u8 Name[8]; + __le64 Timestamp; + +} __packed; + +/* See MS-SMB2 2.2.13.2.9 */ +struct crt_query_id_ctxt { + struct create_context ccontext; + __u8 Name[8]; +} __packed; + +struct crt_sd_ctxt { + struct create_context ccontext; + __u8 Name[8]; + struct smb3_sd sd; +} __packed; + + +#define COPY_CHUNK_RES_KEY_SIZE 24 +struct resume_key_req { + char ResumeKey[COPY_CHUNK_RES_KEY_SIZE]; + __le32 ContextLength; /* MBZ */ + char Context[]; /* ignored, Windows sets to 4 bytes of zero */ +} __packed; + +/* this goes in the ioctl buffer when doing a copychunk request */ +struct copychunk_ioctl { + char SourceKey[COPY_CHUNK_RES_KEY_SIZE]; + __le32 ChunkCount; /* we are only sending 1 */ + __le32 Reserved; + /* array will only be one chunk long for us */ + __le64 SourceOffset; + __le64 TargetOffset; + __le32 Length; /* how many bytes to copy */ + __u32 Reserved2; +} __packed; + +struct copychunk_ioctl_rsp { + __le32 ChunksWritten; + __le32 ChunkBytesWritten; + __le32 TotalBytesWritten; +} __packed; + +/* See MS-FSCC 2.3.29 and 2.3.30 */ +struct get_retrieval_pointer_count_req { + __le64 StartingVcn; /* virtual cluster number (signed) */ +} __packed; + +struct get_retrieval_pointer_count_rsp { + __le32 ExtentCount; +} __packed; + +/* + * See MS-FSCC 2.3.33 and 2.3.34 + * request is the same as get_retrieval_point_count_req struct above + */ +struct smb3_extents { + __le64 NextVcn; + __le64 Lcn; /* logical cluster number */ +} __packed; + +struct get_retrieval_pointers_refcount_rsp { + __le32 ExtentCount; + __u32 Reserved; + __le64 StartingVcn; + struct smb3_extents extents[]; +} __packed; + +/* See MS-DFSC 2.2.2 */ +struct fsctl_get_dfs_referral_req { + __le16 MaxReferralLevel; + __u8 RequestFileName[]; +} __packed; + +/* DFS response is struct get_dfs_refer_rsp */ + +/* See MS-SMB2 2.2.31.3 */ +struct network_resiliency_req { + __le32 Timeout; + __le32 Reserved; +} __packed; +/* There is no buffer for the response ie no struct network_resiliency_rsp */ + +#define RSS_CAPABLE cpu_to_le32(0x00000001) +#define RDMA_CAPABLE cpu_to_le32(0x00000002) + +#define INTERNETWORK cpu_to_le16(0x0002) +#define INTERNETWORKV6 cpu_to_le16(0x0017) + +struct network_interface_info_ioctl_rsp { + __le32 Next; /* next interface. zero if this is last one */ + __le32 IfIndex; + __le32 Capability; /* RSS or RDMA Capable */ + __le32 Reserved; + __le64 LinkSpeed; + __le16 Family; + __u8 Buffer[126]; +} __packed; + +struct iface_info_ipv4 { + __be16 Port; + __be32 IPv4Address; + __be64 Reserved; +} __packed; + +struct iface_info_ipv6 { + __be16 Port; + __be32 FlowInfo; + __u8 IPv6Address[16]; + __be32 ScopeId; +} __packed; + +#define NO_FILE_ID 0xFFFFFFFFFFFFFFFFULL /* general ioctls to srv not to file */ + +struct compress_ioctl { + __le16 CompressionState; /* See cifspdu.h for possible flag values */ +} __packed; + +/* + * Maximum number of iovs we need for an ioctl request. + * [0] : struct smb2_ioctl_req + * [1] : in_data + */ +#define SMB2_IOCTL_IOV_SIZE 2 + +/* + * PDU query infolevel structure definitions + * BB consider moving to a different header + */ + +struct smb2_file_full_ea_info { /* encoding of response for level 15 */ + __le32 next_entry_offset; + __u8 flags; + __u8 ea_name_length; + __le16 ea_value_length; + char ea_data[]; /* \0 terminated name plus value */ +} __packed; /* level 15 Set */ + +struct smb2_file_reparse_point_info { + __le64 IndexNumber; + __le32 Tag; +} __packed; + +struct smb2_file_network_open_info { + __le64 CreationTime; + __le64 LastAccessTime; + __le64 LastWriteTime; + __le64 ChangeTime; + __le64 AllocationSize; + __le64 EndOfFile; + __le32 Attributes; + __le32 Reserved; +} __packed; /* level 34 Query also similar returned in close rsp and open rsp */ + +/* See MS-FSCC 2.4.21 */ +struct smb2_file_id_information { + __le64 VolumeSerialNumber; + __u64 PersistentFileId; /* opaque endianness */ + __u64 VolatileFileId; /* opaque endianness */ +} __packed; /* level 59 */ + +/* See MS-FSCC 2.4.18 */ +struct smb2_file_id_extd_directory_info { + __le32 NextEntryOffset; + __u32 FileIndex; + __le64 CreationTime; + __le64 LastAccessTime; + __le64 LastWriteTime; + __le64 ChangeTime; + __le64 EndOfFile; + __le64 AllocationSize; + __le32 FileAttributes; + __le32 FileNameLength; + __le32 EaSize; /* EA size */ + __le32 ReparsePointTag; /* valid if FILE_ATTR_REPARSE_POINT set in FileAttributes */ + __le64 UniqueId; /* inode num - le since Samba puts ino in low 32 bit */ + char FileName[1]; +} __packed; /* level 60 */ + +extern char smb2_padding[7]; + +/* equivalent of the contents of SMB3.1.1 POSIX open context response */ +struct create_posix_rsp { + u32 nlink; + u32 reparse_tag; + u32 mode; + struct cifs_sid owner; /* var-sized on the wire */ + struct cifs_sid group; /* var-sized on the wire */ +} __packed; + +#define SMB2_QUERY_DIRECTORY_IOV_SIZE 2 + +/* + * SMB2-only POSIX info level for query dir + * + * See posix_info_sid_size(), posix_info_extra_size() and + * posix_info_parse() to help with the handling of this struct. + */ +struct smb2_posix_info { + __le32 NextEntryOffset; + __u32 Ignored; + __le64 CreationTime; + __le64 LastAccessTime; + __le64 LastWriteTime; + __le64 ChangeTime; + __le64 EndOfFile; + __le64 AllocationSize; + __le32 DosAttributes; + __le64 Inode; + __le32 DeviceId; + __le32 Zero; + /* beginning of POSIX Create Context Response */ + __le32 HardLinks; + __le32 ReparseTag; + __le32 Mode; + /* + * var sized owner SID + * var sized group SID + * le32 filenamelength + * u8 filename[] + */ +} __packed; + +/* + * Parsed version of the above struct. Allows direct access to the + * variable length fields + */ +struct smb2_posix_info_parsed { + const struct smb2_posix_info *base; + size_t size; + struct cifs_sid owner; + struct cifs_sid group; + int name_len; + const u8 *name; +}; + +#endif /* _SMB2PDU_H */ diff --git a/fs/smb/client/smb2proto.h b/fs/smb/client/smb2proto.h new file mode 100644 index 0000000000000..be21b5d26f67e --- /dev/null +++ b/fs/smb/client/smb2proto.h @@ -0,0 +1,284 @@ +/* SPDX-License-Identifier: LGPL-2.1 */ +/* + * + * Copyright (c) International Business Machines Corp., 2002, 2011 + * Etersoft, 2012 + * Author(s): Steve French (sfrench@us.ibm.com) + * Pavel Shilovsky (pshilovsky@samba.org) 2012 + * + */ +#ifndef _SMB2PROTO_H +#define _SMB2PROTO_H +#include +#include + +struct statfs; +struct smb_rqst; + +/* + ***************************************************************** + * All Prototypes + ***************************************************************** + */ +extern int map_smb2_to_linux_error(char *buf, bool log_err); +extern int smb2_check_message(char *buf, unsigned int length, + struct TCP_Server_Info *server); +extern unsigned int smb2_calc_size(void *buf); +extern char *smb2_get_data_area_len(int *off, int *len, + struct smb2_hdr *shdr); +extern __le16 *cifs_convert_path_to_utf16(const char *from, + struct cifs_sb_info *cifs_sb); + +extern int smb2_verify_signature(struct smb_rqst *, struct TCP_Server_Info *); +extern int smb2_check_receive(struct mid_q_entry *mid, + struct TCP_Server_Info *server, bool log_error); +extern struct mid_q_entry *smb2_setup_request(struct cifs_ses *ses, + struct TCP_Server_Info *, + struct smb_rqst *rqst); +extern struct mid_q_entry *smb2_setup_async_request( + struct TCP_Server_Info *server, struct smb_rqst *rqst); +extern struct cifs_ses *smb2_find_smb_ses(struct TCP_Server_Info *server, + __u64 ses_id); +extern struct cifs_tcon *smb2_find_smb_tcon(struct TCP_Server_Info *server, + __u64 ses_id, __u32 tid); +extern int smb2_calc_signature(struct smb_rqst *rqst, + struct TCP_Server_Info *server, + bool allocate_crypto); +extern int smb3_calc_signature(struct smb_rqst *rqst, + struct TCP_Server_Info *server, + bool allocate_crypto); +extern void smb2_echo_request(struct work_struct *work); +extern __le32 smb2_get_lease_state(struct cifsInodeInfo *cinode); +extern bool smb2_is_valid_oplock_break(char *buffer, + struct TCP_Server_Info *srv); +extern int smb3_handle_read_data(struct TCP_Server_Info *server, + struct mid_q_entry *mid); +extern int smb2_query_reparse_tag(const unsigned int xid, struct cifs_tcon *tcon, + struct cifs_sb_info *cifs_sb, const char *path, + __u32 *reparse_tag); +int smb2_query_path_info(const unsigned int xid, struct cifs_tcon *tcon, + struct cifs_sb_info *cifs_sb, const char *full_path, + struct cifs_open_info_data *data, bool *adjust_tz, bool *reparse); +extern int smb2_set_path_size(const unsigned int xid, struct cifs_tcon *tcon, + const char *full_path, __u64 size, + struct cifs_sb_info *cifs_sb, bool set_alloc); +extern int smb2_set_file_info(struct inode *inode, const char *full_path, + FILE_BASIC_INFO *buf, const unsigned int xid); +extern int smb311_posix_mkdir(const unsigned int xid, struct inode *inode, + umode_t mode, struct cifs_tcon *tcon, + const char *full_path, + struct cifs_sb_info *cifs_sb); +extern int smb2_mkdir(const unsigned int xid, struct inode *inode, + umode_t mode, struct cifs_tcon *tcon, + const char *name, struct cifs_sb_info *cifs_sb); +extern void smb2_mkdir_setinfo(struct inode *inode, const char *full_path, + struct cifs_sb_info *cifs_sb, + struct cifs_tcon *tcon, const unsigned int xid); +extern int smb2_rmdir(const unsigned int xid, struct cifs_tcon *tcon, + const char *name, struct cifs_sb_info *cifs_sb); +extern int smb2_unlink(const unsigned int xid, struct cifs_tcon *tcon, + const char *name, struct cifs_sb_info *cifs_sb); +extern int smb2_rename_path(const unsigned int xid, struct cifs_tcon *tcon, + const char *from_name, const char *to_name, + struct cifs_sb_info *cifs_sb); +extern int smb2_create_hardlink(const unsigned int xid, struct cifs_tcon *tcon, + const char *from_name, const char *to_name, + struct cifs_sb_info *cifs_sb); +extern int smb3_create_mf_symlink(unsigned int xid, struct cifs_tcon *tcon, + struct cifs_sb_info *cifs_sb, const unsigned char *path, + char *pbuf, unsigned int *pbytes_written); +extern int smb3_query_mf_symlink(unsigned int xid, struct cifs_tcon *tcon, + struct cifs_sb_info *cifs_sb, + const unsigned char *path, char *pbuf, + unsigned int *pbytes_read); +int smb2_parse_symlink_response(struct cifs_sb_info *cifs_sb, const struct kvec *iov, char **path); +int smb2_open_file(const unsigned int xid, struct cifs_open_parms *oparms, __u32 *oplock, + void *buf); +extern int smb2_unlock_range(struct cifsFileInfo *cfile, + struct file_lock *flock, const unsigned int xid); +extern int smb2_push_mandatory_locks(struct cifsFileInfo *cfile); +extern void smb2_reconnect_server(struct work_struct *work); +extern int smb3_crypto_aead_allocate(struct TCP_Server_Info *server); +extern unsigned long smb_rqst_len(struct TCP_Server_Info *server, + struct smb_rqst *rqst); +extern void smb2_set_next_command(struct cifs_tcon *tcon, + struct smb_rqst *rqst); +extern void smb2_set_related(struct smb_rqst *rqst); + +/* + * SMB2 Worker functions - most of protocol specific implementation details + * are contained within these calls. + */ +extern int SMB2_negotiate(const unsigned int xid, + struct cifs_ses *ses, + struct TCP_Server_Info *server); +extern int SMB2_sess_setup(const unsigned int xid, struct cifs_ses *ses, + struct TCP_Server_Info *server, + const struct nls_table *nls_cp); +extern int SMB2_logoff(const unsigned int xid, struct cifs_ses *ses); +extern int SMB2_tcon(const unsigned int xid, struct cifs_ses *ses, + const char *tree, struct cifs_tcon *tcon, + const struct nls_table *); +extern int SMB2_tdis(const unsigned int xid, struct cifs_tcon *tcon); +extern int SMB2_open(const unsigned int xid, struct cifs_open_parms *oparms, + __le16 *path, __u8 *oplock, + struct smb2_file_all_info *buf, + struct create_posix_rsp *posix, + struct kvec *err_iov, int *resp_buftype); +extern int SMB2_open_init(struct cifs_tcon *tcon, + struct TCP_Server_Info *server, + struct smb_rqst *rqst, + __u8 *oplock, struct cifs_open_parms *oparms, + __le16 *path); +extern void SMB2_open_free(struct smb_rqst *rqst); +extern int SMB2_ioctl(const unsigned int xid, struct cifs_tcon *tcon, + u64 persistent_fid, u64 volatile_fid, u32 opcode, + char *in_data, u32 indatalen, u32 maxoutlen, + char **out_data, u32 *plen /* returned data len */); +extern int SMB2_ioctl_init(struct cifs_tcon *tcon, + struct TCP_Server_Info *server, + struct smb_rqst *rqst, + u64 persistent_fid, u64 volatile_fid, u32 opcode, + char *in_data, u32 indatalen, + __u32 max_response_size); +extern void SMB2_ioctl_free(struct smb_rqst *rqst); +extern int SMB2_change_notify(const unsigned int xid, struct cifs_tcon *tcon, + u64 persistent_fid, u64 volatile_fid, bool watch_tree, + u32 completion_filter, u32 max_out_data_len, + char **out_data, u32 *plen /* returned data len */); + +extern int __SMB2_close(const unsigned int xid, struct cifs_tcon *tcon, + u64 persistent_fid, u64 volatile_fid, + struct smb2_file_network_open_info *pbuf); +extern int SMB2_close(const unsigned int xid, struct cifs_tcon *tcon, + u64 persistent_file_id, u64 volatile_file_id); +extern int SMB2_close_init(struct cifs_tcon *tcon, + struct TCP_Server_Info *server, + struct smb_rqst *rqst, + u64 persistent_fid, u64 volatile_fid, + bool query_attrs); +extern void SMB2_close_free(struct smb_rqst *rqst); +extern int SMB2_flush(const unsigned int xid, struct cifs_tcon *tcon, + u64 persistent_file_id, u64 volatile_file_id); +extern int SMB2_flush_init(const unsigned int xid, struct smb_rqst *rqst, + struct cifs_tcon *tcon, + struct TCP_Server_Info *server, + u64 persistent_file_id, u64 volatile_file_id); +extern void SMB2_flush_free(struct smb_rqst *rqst); +extern int SMB311_posix_query_info(const unsigned int xid, struct cifs_tcon *tcon, + u64 persistent_fid, u64 volatile_fid, struct smb311_posix_qinfo *data, u32 *plen); +extern int SMB2_query_info(const unsigned int xid, struct cifs_tcon *tcon, + u64 persistent_file_id, u64 volatile_file_id, + struct smb2_file_all_info *data); +extern int SMB2_query_info_init(struct cifs_tcon *tcon, + struct TCP_Server_Info *server, + struct smb_rqst *rqst, + u64 persistent_fid, u64 volatile_fid, + u8 info_class, u8 info_type, + u32 additional_info, size_t output_len, + size_t input_len, void *input); +extern void SMB2_query_info_free(struct smb_rqst *rqst); +extern int SMB2_query_acl(const unsigned int xid, struct cifs_tcon *tcon, + u64 persistent_file_id, u64 volatile_file_id, + void **data, unsigned int *plen, u32 info); +extern int SMB2_get_srv_num(const unsigned int xid, struct cifs_tcon *tcon, + u64 persistent_fid, u64 volatile_fid, + __le64 *uniqueid); +extern int smb2_async_readv(struct cifs_readdata *rdata); +extern int SMB2_read(const unsigned int xid, struct cifs_io_parms *io_parms, + unsigned int *nbytes, char **buf, int *buf_type); +extern int smb2_async_writev(struct cifs_writedata *wdata, + void (*release)(struct kref *kref)); +extern int SMB2_write(const unsigned int xid, struct cifs_io_parms *io_parms, + unsigned int *nbytes, struct kvec *iov, int n_vec); +extern int SMB2_echo(struct TCP_Server_Info *server); +extern int SMB2_query_directory(const unsigned int xid, struct cifs_tcon *tcon, + u64 persistent_fid, u64 volatile_fid, int index, + struct cifs_search_info *srch_inf); +extern int SMB2_query_directory_init(unsigned int xid, struct cifs_tcon *tcon, + struct TCP_Server_Info *server, + struct smb_rqst *rqst, + u64 persistent_fid, u64 volatile_fid, + int index, int info_level); +extern void SMB2_query_directory_free(struct smb_rqst *rqst); +extern int SMB2_set_eof(const unsigned int xid, struct cifs_tcon *tcon, + u64 persistent_fid, u64 volatile_fid, u32 pid, + __le64 *eof); +extern int SMB2_set_info_init(struct cifs_tcon *tcon, + struct TCP_Server_Info *server, + struct smb_rqst *rqst, + u64 persistent_fid, u64 volatile_fid, u32 pid, + u8 info_class, u8 info_type, u32 additional_info, + void **data, unsigned int *size); +extern void SMB2_set_info_free(struct smb_rqst *rqst); +extern int SMB2_set_acl(const unsigned int xid, struct cifs_tcon *tcon, + u64 persistent_fid, u64 volatile_fid, + struct cifs_ntsd *pnntsd, int pacllen, int aclflag); +extern int SMB2_set_ea(const unsigned int xid, struct cifs_tcon *tcon, + u64 persistent_fid, u64 volatile_fid, + struct smb2_file_full_ea_info *buf, int len); +extern int SMB2_set_compression(const unsigned int xid, struct cifs_tcon *tcon, + u64 persistent_fid, u64 volatile_fid); +extern int SMB2_oplock_break(const unsigned int xid, struct cifs_tcon *tcon, + const u64 persistent_fid, const u64 volatile_fid, + const __u8 oplock_level); +extern int smb2_handle_cancelled_close(struct cifs_tcon *tcon, + __u64 persistent_fid, + __u64 volatile_fid); +extern int smb2_handle_cancelled_mid(struct mid_q_entry *mid, struct TCP_Server_Info *server); +void smb2_cancelled_close_fid(struct work_struct *work); +extern int SMB2_QFS_info(const unsigned int xid, struct cifs_tcon *tcon, + u64 persistent_file_id, u64 volatile_file_id, + struct kstatfs *FSData); +extern int SMB311_posix_qfs_info(const unsigned int xid, struct cifs_tcon *tcon, + u64 persistent_file_id, u64 volatile_file_id, + struct kstatfs *FSData); +extern int SMB2_QFS_attr(const unsigned int xid, struct cifs_tcon *tcon, + u64 persistent_file_id, u64 volatile_file_id, int lvl); +extern int SMB2_lock(const unsigned int xid, struct cifs_tcon *tcon, + const __u64 persist_fid, const __u64 volatile_fid, + const __u32 pid, const __u64 length, const __u64 offset, + const __u32 lockFlags, const bool wait); +extern int smb2_lockv(const unsigned int xid, struct cifs_tcon *tcon, + const __u64 persist_fid, const __u64 volatile_fid, + const __u32 pid, const __u32 num_lock, + struct smb2_lock_element *buf); +extern int SMB2_lease_break(const unsigned int xid, struct cifs_tcon *tcon, + __u8 *lease_key, const __le32 lease_state); +extern int smb3_validate_negotiate(const unsigned int, struct cifs_tcon *); + +extern enum securityEnum smb2_select_sectype(struct TCP_Server_Info *, + enum securityEnum); +extern void smb2_parse_contexts(struct TCP_Server_Info *server, + struct smb2_create_rsp *rsp, + unsigned int *epoch, char *lease_key, + __u8 *oplock, struct smb2_file_all_info *buf, + struct create_posix_rsp *posix); +extern int smb3_encryption_required(const struct cifs_tcon *tcon); +extern int smb2_validate_iov(unsigned int offset, unsigned int buffer_length, + struct kvec *iov, unsigned int min_buf_size); +extern int smb2_validate_and_copy_iov(unsigned int offset, + unsigned int buffer_length, + struct kvec *iov, + unsigned int minbufsize, char *data); +extern void smb2_copy_fs_info_to_kstatfs( + struct smb2_fs_full_size_info *pfs_inf, + struct kstatfs *kst); +extern int smb311_crypto_shash_allocate(struct TCP_Server_Info *server); +extern int smb311_update_preauth_hash(struct cifs_ses *ses, + struct TCP_Server_Info *server, + struct kvec *iov, int nvec); +extern int smb2_query_info_compound(const unsigned int xid, + struct cifs_tcon *tcon, + const char *path, u32 desired_access, + u32 class, u32 type, u32 output_len, + struct kvec *rsp, int *buftype, + struct cifs_sb_info *cifs_sb); +/* query path info from the server using SMB311 POSIX extensions*/ +int smb311_posix_query_path_info(const unsigned int xid, struct cifs_tcon *tcon, + struct cifs_sb_info *cifs_sb, const char *full_path, + struct cifs_open_info_data *data, bool *adjust_tz, bool *reparse); +int posix_info_parse(const void *beg, const void *end, + struct smb2_posix_info_parsed *out); +int posix_info_sid_size(const void *beg, const void *end); +#endif /* _SMB2PROTO_H */ diff --git a/fs/smb/client/smb2status.h b/fs/smb/client/smb2status.h new file mode 100644 index 0000000000000..a9e958166fc53 --- /dev/null +++ b/fs/smb/client/smb2status.h @@ -0,0 +1,1769 @@ +/* SPDX-License-Identifier: LGPL-2.1 */ +/* + * + * SMB2 Status code (network error) definitions + * Definitions are from MS-ERREF + * + * Copyright (c) International Business Machines Corp., 2009,2011 + * Author(s): Steve French (sfrench@us.ibm.com) + * + */ + +/* + * 0 1 2 3 4 5 6 7 8 9 0 A B C D E F 0 1 2 3 4 5 6 7 8 9 A B C D E F + * SEV C N <-------Facility--------> <------Error Status Code------> + * + * C is set if "customer defined" error, N bit is reserved and MBZ + */ + +#define STATUS_SEVERITY_SUCCESS __constant_cpu_to_le32(0x0000) +#define STATUS_SEVERITY_INFORMATIONAL cpu_to_le32(0x0001) +#define STATUS_SEVERITY_WARNING cpu_to_le32(0x0002) +#define STATUS_SEVERITY_ERROR cpu_to_le32(0x0003) + +struct ntstatus { + /* Facility is the high 12 bits of the following field */ + __le32 Facility; /* low 2 bits Severity, next is Customer, then rsrvd */ + __le32 Code; +}; + +#define STATUS_SUCCESS cpu_to_le32(0x00000000) +#define STATUS_WAIT_0 cpu_to_le32(0x00000000) +#define STATUS_WAIT_1 cpu_to_le32(0x00000001) +#define STATUS_WAIT_2 cpu_to_le32(0x00000002) +#define STATUS_WAIT_3 cpu_to_le32(0x00000003) +#define STATUS_WAIT_63 cpu_to_le32(0x0000003F) +#define STATUS_ABANDONED cpu_to_le32(0x00000080) +#define STATUS_ABANDONED_WAIT_0 cpu_to_le32(0x00000080) +#define STATUS_ABANDONED_WAIT_63 cpu_to_le32(0x000000BF) +#define STATUS_USER_APC cpu_to_le32(0x000000C0) +#define STATUS_KERNEL_APC cpu_to_le32(0x00000100) +#define STATUS_ALERTED cpu_to_le32(0x00000101) +#define STATUS_TIMEOUT cpu_to_le32(0x00000102) +#define STATUS_PENDING cpu_to_le32(0x00000103) +#define STATUS_REPARSE cpu_to_le32(0x00000104) +#define STATUS_MORE_ENTRIES cpu_to_le32(0x00000105) +#define STATUS_NOT_ALL_ASSIGNED cpu_to_le32(0x00000106) +#define STATUS_SOME_NOT_MAPPED cpu_to_le32(0x00000107) +#define STATUS_OPLOCK_BREAK_IN_PROGRESS cpu_to_le32(0x00000108) +#define STATUS_VOLUME_MOUNTED cpu_to_le32(0x00000109) +#define STATUS_RXACT_COMMITTED cpu_to_le32(0x0000010A) +#define STATUS_NOTIFY_CLEANUP cpu_to_le32(0x0000010B) +#define STATUS_NOTIFY_ENUM_DIR cpu_to_le32(0x0000010C) +#define STATUS_NO_QUOTAS_FOR_ACCOUNT cpu_to_le32(0x0000010D) +#define STATUS_PRIMARY_TRANSPORT_CONNECT_FAILED cpu_to_le32(0x0000010E) +#define STATUS_PAGE_FAULT_TRANSITION cpu_to_le32(0x00000110) +#define STATUS_PAGE_FAULT_DEMAND_ZERO cpu_to_le32(0x00000111) +#define STATUS_PAGE_FAULT_COPY_ON_WRITE cpu_to_le32(0x00000112) +#define STATUS_PAGE_FAULT_GUARD_PAGE cpu_to_le32(0x00000113) +#define STATUS_PAGE_FAULT_PAGING_FILE cpu_to_le32(0x00000114) +#define STATUS_CACHE_PAGE_LOCKED cpu_to_le32(0x00000115) +#define STATUS_CRASH_DUMP cpu_to_le32(0x00000116) +#define STATUS_BUFFER_ALL_ZEROS cpu_to_le32(0x00000117) +#define STATUS_REPARSE_OBJECT cpu_to_le32(0x00000118) +#define STATUS_RESOURCE_REQUIREMENTS_CHANGED cpu_to_le32(0x00000119) +#define STATUS_TRANSLATION_COMPLETE cpu_to_le32(0x00000120) +#define STATUS_DS_MEMBERSHIP_EVALUATED_LOCALLY cpu_to_le32(0x00000121) +#define STATUS_NOTHING_TO_TERMINATE cpu_to_le32(0x00000122) +#define STATUS_PROCESS_NOT_IN_JOB cpu_to_le32(0x00000123) +#define STATUS_PROCESS_IN_JOB cpu_to_le32(0x00000124) +#define STATUS_VOLSNAP_HIBERNATE_READY cpu_to_le32(0x00000125) +#define STATUS_FSFILTER_OP_COMPLETED_SUCCESSFULLY cpu_to_le32(0x00000126) +#define STATUS_INTERRUPT_VECTOR_ALREADY_CONNECTED cpu_to_le32(0x00000127) +#define STATUS_INTERRUPT_STILL_CONNECTED cpu_to_le32(0x00000128) +#define STATUS_PROCESS_CLONED cpu_to_le32(0x00000129) +#define STATUS_FILE_LOCKED_WITH_ONLY_READERS cpu_to_le32(0x0000012A) +#define STATUS_FILE_LOCKED_WITH_WRITERS cpu_to_le32(0x0000012B) +#define STATUS_RESOURCEMANAGER_READ_ONLY cpu_to_le32(0x00000202) +#define STATUS_WAIT_FOR_OPLOCK cpu_to_le32(0x00000367) +#define DBG_EXCEPTION_HANDLED cpu_to_le32(0x00010001) +#define DBG_CONTINUE cpu_to_le32(0x00010002) +#define STATUS_FLT_IO_COMPLETE cpu_to_le32(0x001C0001) +#define STATUS_OBJECT_NAME_EXISTS cpu_to_le32(0x40000000) +#define STATUS_THREAD_WAS_SUSPENDED cpu_to_le32(0x40000001) +#define STATUS_WORKING_SET_LIMIT_RANGE cpu_to_le32(0x40000002) +#define STATUS_IMAGE_NOT_AT_BASE cpu_to_le32(0x40000003) +#define STATUS_RXACT_STATE_CREATED cpu_to_le32(0x40000004) +#define STATUS_SEGMENT_NOTIFICATION cpu_to_le32(0x40000005) +#define STATUS_LOCAL_USER_SESSION_KEY cpu_to_le32(0x40000006) +#define STATUS_BAD_CURRENT_DIRECTORY cpu_to_le32(0x40000007) +#define STATUS_SERIAL_MORE_WRITES cpu_to_le32(0x40000008) +#define STATUS_REGISTRY_RECOVERED cpu_to_le32(0x40000009) +#define STATUS_FT_READ_RECOVERY_FROM_BACKUP cpu_to_le32(0x4000000A) +#define STATUS_FT_WRITE_RECOVERY cpu_to_le32(0x4000000B) +#define STATUS_SERIAL_COUNTER_TIMEOUT cpu_to_le32(0x4000000C) +#define STATUS_NULL_LM_PASSWORD cpu_to_le32(0x4000000D) +#define STATUS_IMAGE_MACHINE_TYPE_MISMATCH cpu_to_le32(0x4000000E) +#define STATUS_RECEIVE_PARTIAL cpu_to_le32(0x4000000F) +#define STATUS_RECEIVE_EXPEDITED cpu_to_le32(0x40000010) +#define STATUS_RECEIVE_PARTIAL_EXPEDITED cpu_to_le32(0x40000011) +#define STATUS_EVENT_DONE cpu_to_le32(0x40000012) +#define STATUS_EVENT_PENDING cpu_to_le32(0x40000013) +#define STATUS_CHECKING_FILE_SYSTEM cpu_to_le32(0x40000014) +#define STATUS_FATAL_APP_EXIT cpu_to_le32(0x40000015) +#define STATUS_PREDEFINED_HANDLE cpu_to_le32(0x40000016) +#define STATUS_WAS_UNLOCKED cpu_to_le32(0x40000017) +#define STATUS_SERVICE_NOTIFICATION cpu_to_le32(0x40000018) +#define STATUS_WAS_LOCKED cpu_to_le32(0x40000019) +#define STATUS_LOG_HARD_ERROR cpu_to_le32(0x4000001A) +#define STATUS_ALREADY_WIN32 cpu_to_le32(0x4000001B) +#define STATUS_WX86_UNSIMULATE cpu_to_le32(0x4000001C) +#define STATUS_WX86_CONTINUE cpu_to_le32(0x4000001D) +#define STATUS_WX86_SINGLE_STEP cpu_to_le32(0x4000001E) +#define STATUS_WX86_BREAKPOINT cpu_to_le32(0x4000001F) +#define STATUS_WX86_EXCEPTION_CONTINUE cpu_to_le32(0x40000020) +#define STATUS_WX86_EXCEPTION_LASTCHANCE cpu_to_le32(0x40000021) +#define STATUS_WX86_EXCEPTION_CHAIN cpu_to_le32(0x40000022) +#define STATUS_IMAGE_MACHINE_TYPE_MISMATCH_EXE cpu_to_le32(0x40000023) +#define STATUS_NO_YIELD_PERFORMED cpu_to_le32(0x40000024) +#define STATUS_TIMER_RESUME_IGNORED cpu_to_le32(0x40000025) +#define STATUS_ARBITRATION_UNHANDLED cpu_to_le32(0x40000026) +#define STATUS_CARDBUS_NOT_SUPPORTED cpu_to_le32(0x40000027) +#define STATUS_WX86_CREATEWX86TIB cpu_to_le32(0x40000028) +#define STATUS_MP_PROCESSOR_MISMATCH cpu_to_le32(0x40000029) +#define STATUS_HIBERNATED cpu_to_le32(0x4000002A) +#define STATUS_RESUME_HIBERNATION cpu_to_le32(0x4000002B) +#define STATUS_FIRMWARE_UPDATED cpu_to_le32(0x4000002C) +#define STATUS_DRIVERS_LEAKING_LOCKED_PAGES cpu_to_le32(0x4000002D) +#define STATUS_MESSAGE_RETRIEVED cpu_to_le32(0x4000002E) +#define STATUS_SYSTEM_POWERSTATE_TRANSITION cpu_to_le32(0x4000002F) +#define STATUS_ALPC_CHECK_COMPLETION_LIST cpu_to_le32(0x40000030) +#define STATUS_SYSTEM_POWERSTATE_COMPLEX_TRANSITION cpu_to_le32(0x40000031) +#define STATUS_ACCESS_AUDIT_BY_POLICY cpu_to_le32(0x40000032) +#define STATUS_ABANDON_HIBERFILE cpu_to_le32(0x40000033) +#define STATUS_BIZRULES_NOT_ENABLED cpu_to_le32(0x40000034) +#define STATUS_WAKE_SYSTEM cpu_to_le32(0x40000294) +#define STATUS_DS_SHUTTING_DOWN cpu_to_le32(0x40000370) +#define DBG_REPLY_LATER cpu_to_le32(0x40010001) +#define DBG_UNABLE_TO_PROVIDE_HANDLE cpu_to_le32(0x40010002) +#define DBG_TERMINATE_THREAD cpu_to_le32(0x40010003) +#define DBG_TERMINATE_PROCESS cpu_to_le32(0x40010004) +#define DBG_CONTROL_C cpu_to_le32(0x40010005) +#define DBG_PRINTEXCEPTION_C cpu_to_le32(0x40010006) +#define DBG_RIPEXCEPTION cpu_to_le32(0x40010007) +#define DBG_CONTROL_BREAK cpu_to_le32(0x40010008) +#define DBG_COMMAND_EXCEPTION cpu_to_le32(0x40010009) +#define RPC_NT_UUID_LOCAL_ONLY cpu_to_le32(0x40020056) +#define RPC_NT_SEND_INCOMPLETE cpu_to_le32(0x400200AF) +#define STATUS_CTX_CDM_CONNECT cpu_to_le32(0x400A0004) +#define STATUS_CTX_CDM_DISCONNECT cpu_to_le32(0x400A0005) +#define STATUS_SXS_RELEASE_ACTIVATION_CONTEXT cpu_to_le32(0x4015000D) +#define STATUS_RECOVERY_NOT_NEEDED cpu_to_le32(0x40190034) +#define STATUS_RM_ALREADY_STARTED cpu_to_le32(0x40190035) +#define STATUS_LOG_NO_RESTART cpu_to_le32(0x401A000C) +#define STATUS_VIDEO_DRIVER_DEBUG_REPORT_REQUEST cpu_to_le32(0x401B00EC) +#define STATUS_GRAPHICS_PARTIAL_DATA_POPULATED cpu_to_le32(0x401E000A) +#define STATUS_GRAPHICS_DRIVER_MISMATCH cpu_to_le32(0x401E0117) +#define STATUS_GRAPHICS_MODE_NOT_PINNED cpu_to_le32(0x401E0307) +#define STATUS_GRAPHICS_NO_PREFERRED_MODE cpu_to_le32(0x401E031E) +#define STATUS_GRAPHICS_DATASET_IS_EMPTY cpu_to_le32(0x401E034B) +#define STATUS_GRAPHICS_NO_MORE_ELEMENTS_IN_DATASET cpu_to_le32(0x401E034C) +#define STATUS_GRAPHICS_PATH_CONTENT_GEOMETRY_TRANSFORMATION_NOT_PINNED cpu_to_le32(0x401E0351) +#define STATUS_GRAPHICS_UNKNOWN_CHILD_STATUS cpu_to_le32(0x401E042F) +#define STATUS_GRAPHICS_LEADLINK_START_DEFERRED cpu_to_le32(0x401E0437) +#define STATUS_GRAPHICS_POLLING_TOO_FREQUENTLY cpu_to_le32(0x401E0439) +#define STATUS_GRAPHICS_START_DEFERRED cpu_to_le32(0x401E043A) +#define STATUS_NDIS_INDICATION_REQUIRED cpu_to_le32(0x40230001) +#define STATUS_GUARD_PAGE_VIOLATION cpu_to_le32(0x80000001) +#define STATUS_DATATYPE_MISALIGNMENT cpu_to_le32(0x80000002) +#define STATUS_BREAKPOINT cpu_to_le32(0x80000003) +#define STATUS_SINGLE_STEP cpu_to_le32(0x80000004) +#define STATUS_BUFFER_OVERFLOW cpu_to_le32(0x80000005) +#define STATUS_NO_MORE_FILES cpu_to_le32(0x80000006) +#define STATUS_WAKE_SYSTEM_DEBUGGER cpu_to_le32(0x80000007) +#define STATUS_HANDLES_CLOSED cpu_to_le32(0x8000000A) +#define STATUS_NO_INHERITANCE cpu_to_le32(0x8000000B) +#define STATUS_GUID_SUBSTITUTION_MADE cpu_to_le32(0x8000000C) +#define STATUS_PARTIAL_COPY cpu_to_le32(0x8000000D) +#define STATUS_DEVICE_PAPER_EMPTY cpu_to_le32(0x8000000E) +#define STATUS_DEVICE_POWERED_OFF cpu_to_le32(0x8000000F) +#define STATUS_DEVICE_OFF_LINE cpu_to_le32(0x80000010) +#define STATUS_DEVICE_BUSY cpu_to_le32(0x80000011) +#define STATUS_NO_MORE_EAS cpu_to_le32(0x80000012) +#define STATUS_INVALID_EA_NAME cpu_to_le32(0x80000013) +#define STATUS_EA_LIST_INCONSISTENT cpu_to_le32(0x80000014) +#define STATUS_INVALID_EA_FLAG cpu_to_le32(0x80000015) +#define STATUS_VERIFY_REQUIRED cpu_to_le32(0x80000016) +#define STATUS_EXTRANEOUS_INFORMATION cpu_to_le32(0x80000017) +#define STATUS_RXACT_COMMIT_NECESSARY cpu_to_le32(0x80000018) +#define STATUS_NO_MORE_ENTRIES cpu_to_le32(0x8000001A) +#define STATUS_FILEMARK_DETECTED cpu_to_le32(0x8000001B) +#define STATUS_MEDIA_CHANGED cpu_to_le32(0x8000001C) +#define STATUS_BUS_RESET cpu_to_le32(0x8000001D) +#define STATUS_END_OF_MEDIA cpu_to_le32(0x8000001E) +#define STATUS_BEGINNING_OF_MEDIA cpu_to_le32(0x8000001F) +#define STATUS_MEDIA_CHECK cpu_to_le32(0x80000020) +#define STATUS_SETMARK_DETECTED cpu_to_le32(0x80000021) +#define STATUS_NO_DATA_DETECTED cpu_to_le32(0x80000022) +#define STATUS_REDIRECTOR_HAS_OPEN_HANDLES cpu_to_le32(0x80000023) +#define STATUS_SERVER_HAS_OPEN_HANDLES cpu_to_le32(0x80000024) +#define STATUS_ALREADY_DISCONNECTED cpu_to_le32(0x80000025) +#define STATUS_LONGJUMP cpu_to_le32(0x80000026) +#define STATUS_CLEANER_CARTRIDGE_INSTALLED cpu_to_le32(0x80000027) +#define STATUS_PLUGPLAY_QUERY_VETOED cpu_to_le32(0x80000028) +#define STATUS_UNWIND_CONSOLIDATE cpu_to_le32(0x80000029) +#define STATUS_REGISTRY_HIVE_RECOVERED cpu_to_le32(0x8000002A) +#define STATUS_DLL_MIGHT_BE_INSECURE cpu_to_le32(0x8000002B) +#define STATUS_DLL_MIGHT_BE_INCOMPATIBLE cpu_to_le32(0x8000002C) +#define STATUS_STOPPED_ON_SYMLINK cpu_to_le32(0x8000002D) +#define STATUS_DEVICE_REQUIRES_CLEANING cpu_to_le32(0x80000288) +#define STATUS_DEVICE_DOOR_OPEN cpu_to_le32(0x80000289) +#define STATUS_DATA_LOST_REPAIR cpu_to_le32(0x80000803) +#define DBG_EXCEPTION_NOT_HANDLED cpu_to_le32(0x80010001) +#define STATUS_CLUSTER_NODE_ALREADY_UP cpu_to_le32(0x80130001) +#define STATUS_CLUSTER_NODE_ALREADY_DOWN cpu_to_le32(0x80130002) +#define STATUS_CLUSTER_NETWORK_ALREADY_ONLINE cpu_to_le32(0x80130003) +#define STATUS_CLUSTER_NETWORK_ALREADY_OFFLINE cpu_to_le32(0x80130004) +#define STATUS_CLUSTER_NODE_ALREADY_MEMBER cpu_to_le32(0x80130005) +#define STATUS_COULD_NOT_RESIZE_LOG cpu_to_le32(0x80190009) +#define STATUS_NO_TXF_METADATA cpu_to_le32(0x80190029) +#define STATUS_CANT_RECOVER_WITH_HANDLE_OPEN cpu_to_le32(0x80190031) +#define STATUS_TXF_METADATA_ALREADY_PRESENT cpu_to_le32(0x80190041) +#define STATUS_TRANSACTION_SCOPE_CALLBACKS_NOT_SET cpu_to_le32(0x80190042) +#define STATUS_VIDEO_HUNG_DISPLAY_DRIVER_THREAD_RECOVERED cpu_to_le32(0x801B00EB) +#define STATUS_FLT_BUFFER_TOO_SMALL cpu_to_le32(0x801C0001) +#define STATUS_FVE_PARTIAL_METADATA cpu_to_le32(0x80210001) +#define STATUS_UNSUCCESSFUL cpu_to_le32(0xC0000001) +#define STATUS_NOT_IMPLEMENTED cpu_to_le32(0xC0000002) +#define STATUS_INVALID_INFO_CLASS cpu_to_le32(0xC0000003) +#define STATUS_INFO_LENGTH_MISMATCH cpu_to_le32(0xC0000004) +#define STATUS_ACCESS_VIOLATION cpu_to_le32(0xC0000005) +#define STATUS_IN_PAGE_ERROR cpu_to_le32(0xC0000006) +#define STATUS_PAGEFILE_QUOTA cpu_to_le32(0xC0000007) +#define STATUS_INVALID_HANDLE cpu_to_le32(0xC0000008) +#define STATUS_BAD_INITIAL_STACK cpu_to_le32(0xC0000009) +#define STATUS_BAD_INITIAL_PC cpu_to_le32(0xC000000A) +#define STATUS_INVALID_CID cpu_to_le32(0xC000000B) +#define STATUS_TIMER_NOT_CANCELED cpu_to_le32(0xC000000C) +#define STATUS_INVALID_PARAMETER cpu_to_le32(0xC000000D) +#define STATUS_NO_SUCH_DEVICE cpu_to_le32(0xC000000E) +#define STATUS_NO_SUCH_FILE cpu_to_le32(0xC000000F) +#define STATUS_INVALID_DEVICE_REQUEST cpu_to_le32(0xC0000010) +#define STATUS_END_OF_FILE cpu_to_le32(0xC0000011) +#define STATUS_WRONG_VOLUME cpu_to_le32(0xC0000012) +#define STATUS_NO_MEDIA_IN_DEVICE cpu_to_le32(0xC0000013) +#define STATUS_UNRECOGNIZED_MEDIA cpu_to_le32(0xC0000014) +#define STATUS_NONEXISTENT_SECTOR cpu_to_le32(0xC0000015) +#define STATUS_MORE_PROCESSING_REQUIRED cpu_to_le32(0xC0000016) +#define STATUS_NO_MEMORY cpu_to_le32(0xC0000017) +#define STATUS_CONFLICTING_ADDRESSES cpu_to_le32(0xC0000018) +#define STATUS_NOT_MAPPED_VIEW cpu_to_le32(0xC0000019) +#define STATUS_UNABLE_TO_FREE_VM cpu_to_le32(0xC000001A) +#define STATUS_UNABLE_TO_DELETE_SECTION cpu_to_le32(0xC000001B) +#define STATUS_INVALID_SYSTEM_SERVICE cpu_to_le32(0xC000001C) +#define STATUS_ILLEGAL_INSTRUCTION cpu_to_le32(0xC000001D) +#define STATUS_INVALID_LOCK_SEQUENCE cpu_to_le32(0xC000001E) +#define STATUS_INVALID_VIEW_SIZE cpu_to_le32(0xC000001F) +#define STATUS_INVALID_FILE_FOR_SECTION cpu_to_le32(0xC0000020) +#define STATUS_ALREADY_COMMITTED cpu_to_le32(0xC0000021) +#define STATUS_ACCESS_DENIED cpu_to_le32(0xC0000022) +#define STATUS_BUFFER_TOO_SMALL cpu_to_le32(0xC0000023) +#define STATUS_OBJECT_TYPE_MISMATCH cpu_to_le32(0xC0000024) +#define STATUS_NONCONTINUABLE_EXCEPTION cpu_to_le32(0xC0000025) +#define STATUS_INVALID_DISPOSITION cpu_to_le32(0xC0000026) +#define STATUS_UNWIND cpu_to_le32(0xC0000027) +#define STATUS_BAD_STACK cpu_to_le32(0xC0000028) +#define STATUS_INVALID_UNWIND_TARGET cpu_to_le32(0xC0000029) +#define STATUS_NOT_LOCKED cpu_to_le32(0xC000002A) +#define STATUS_PARITY_ERROR cpu_to_le32(0xC000002B) +#define STATUS_UNABLE_TO_DECOMMIT_VM cpu_to_le32(0xC000002C) +#define STATUS_NOT_COMMITTED cpu_to_le32(0xC000002D) +#define STATUS_INVALID_PORT_ATTRIBUTES cpu_to_le32(0xC000002E) +#define STATUS_PORT_MESSAGE_TOO_LONG cpu_to_le32(0xC000002F) +#define STATUS_INVALID_PARAMETER_MIX cpu_to_le32(0xC0000030) +#define STATUS_INVALID_QUOTA_LOWER cpu_to_le32(0xC0000031) +#define STATUS_DISK_CORRUPT_ERROR cpu_to_le32(0xC0000032) +#define STATUS_OBJECT_NAME_INVALID cpu_to_le32(0xC0000033) +#define STATUS_OBJECT_NAME_NOT_FOUND cpu_to_le32(0xC0000034) +#define STATUS_OBJECT_NAME_COLLISION cpu_to_le32(0xC0000035) +#define STATUS_PORT_DISCONNECTED cpu_to_le32(0xC0000037) +#define STATUS_DEVICE_ALREADY_ATTACHED cpu_to_le32(0xC0000038) +#define STATUS_OBJECT_PATH_INVALID cpu_to_le32(0xC0000039) +#define STATUS_OBJECT_PATH_NOT_FOUND cpu_to_le32(0xC000003A) +#define STATUS_OBJECT_PATH_SYNTAX_BAD cpu_to_le32(0xC000003B) +#define STATUS_DATA_OVERRUN cpu_to_le32(0xC000003C) +#define STATUS_DATA_LATE_ERROR cpu_to_le32(0xC000003D) +#define STATUS_DATA_ERROR cpu_to_le32(0xC000003E) +#define STATUS_CRC_ERROR cpu_to_le32(0xC000003F) +#define STATUS_SECTION_TOO_BIG cpu_to_le32(0xC0000040) +#define STATUS_PORT_CONNECTION_REFUSED cpu_to_le32(0xC0000041) +#define STATUS_INVALID_PORT_HANDLE cpu_to_le32(0xC0000042) +#define STATUS_SHARING_VIOLATION cpu_to_le32(0xC0000043) +#define STATUS_QUOTA_EXCEEDED cpu_to_le32(0xC0000044) +#define STATUS_INVALID_PAGE_PROTECTION cpu_to_le32(0xC0000045) +#define STATUS_MUTANT_NOT_OWNED cpu_to_le32(0xC0000046) +#define STATUS_SEMAPHORE_LIMIT_EXCEEDED cpu_to_le32(0xC0000047) +#define STATUS_PORT_ALREADY_SET cpu_to_le32(0xC0000048) +#define STATUS_SECTION_NOT_IMAGE cpu_to_le32(0xC0000049) +#define STATUS_SUSPEND_COUNT_EXCEEDED cpu_to_le32(0xC000004A) +#define STATUS_THREAD_IS_TERMINATING cpu_to_le32(0xC000004B) +#define STATUS_BAD_WORKING_SET_LIMIT cpu_to_le32(0xC000004C) +#define STATUS_INCOMPATIBLE_FILE_MAP cpu_to_le32(0xC000004D) +#define STATUS_SECTION_PROTECTION cpu_to_le32(0xC000004E) +#define STATUS_EAS_NOT_SUPPORTED cpu_to_le32(0xC000004F) +#define STATUS_EA_TOO_LARGE cpu_to_le32(0xC0000050) +#define STATUS_NONEXISTENT_EA_ENTRY cpu_to_le32(0xC0000051) +#define STATUS_NO_EAS_ON_FILE cpu_to_le32(0xC0000052) +#define STATUS_EA_CORRUPT_ERROR cpu_to_le32(0xC0000053) +#define STATUS_FILE_LOCK_CONFLICT cpu_to_le32(0xC0000054) +#define STATUS_LOCK_NOT_GRANTED cpu_to_le32(0xC0000055) +#define STATUS_DELETE_PENDING cpu_to_le32(0xC0000056) +#define STATUS_CTL_FILE_NOT_SUPPORTED cpu_to_le32(0xC0000057) +#define STATUS_UNKNOWN_REVISION cpu_to_le32(0xC0000058) +#define STATUS_REVISION_MISMATCH cpu_to_le32(0xC0000059) +#define STATUS_INVALID_OWNER cpu_to_le32(0xC000005A) +#define STATUS_INVALID_PRIMARY_GROUP cpu_to_le32(0xC000005B) +#define STATUS_NO_IMPERSONATION_TOKEN cpu_to_le32(0xC000005C) +#define STATUS_CANT_DISABLE_MANDATORY cpu_to_le32(0xC000005D) +#define STATUS_NO_LOGON_SERVERS cpu_to_le32(0xC000005E) +#define STATUS_NO_SUCH_LOGON_SESSION cpu_to_le32(0xC000005F) +#define STATUS_NO_SUCH_PRIVILEGE cpu_to_le32(0xC0000060) +#define STATUS_PRIVILEGE_NOT_HELD cpu_to_le32(0xC0000061) +#define STATUS_INVALID_ACCOUNT_NAME cpu_to_le32(0xC0000062) +#define STATUS_USER_EXISTS cpu_to_le32(0xC0000063) +#define STATUS_NO_SUCH_USER cpu_to_le32(0xC0000064) +#define STATUS_GROUP_EXISTS cpu_to_le32(0xC0000065) +#define STATUS_NO_SUCH_GROUP cpu_to_le32(0xC0000066) +#define STATUS_MEMBER_IN_GROUP cpu_to_le32(0xC0000067) +#define STATUS_MEMBER_NOT_IN_GROUP cpu_to_le32(0xC0000068) +#define STATUS_LAST_ADMIN cpu_to_le32(0xC0000069) +#define STATUS_WRONG_PASSWORD cpu_to_le32(0xC000006A) +#define STATUS_ILL_FORMED_PASSWORD cpu_to_le32(0xC000006B) +#define STATUS_PASSWORD_RESTRICTION cpu_to_le32(0xC000006C) +#define STATUS_LOGON_FAILURE cpu_to_le32(0xC000006D) +#define STATUS_ACCOUNT_RESTRICTION cpu_to_le32(0xC000006E) +#define STATUS_INVALID_LOGON_HOURS cpu_to_le32(0xC000006F) +#define STATUS_INVALID_WORKSTATION cpu_to_le32(0xC0000070) +#define STATUS_PASSWORD_EXPIRED cpu_to_le32(0xC0000071) +#define STATUS_ACCOUNT_DISABLED cpu_to_le32(0xC0000072) +#define STATUS_NONE_MAPPED cpu_to_le32(0xC0000073) +#define STATUS_TOO_MANY_LUIDS_REQUESTED cpu_to_le32(0xC0000074) +#define STATUS_LUIDS_EXHAUSTED cpu_to_le32(0xC0000075) +#define STATUS_INVALID_SUB_AUTHORITY cpu_to_le32(0xC0000076) +#define STATUS_INVALID_ACL cpu_to_le32(0xC0000077) +#define STATUS_INVALID_SID cpu_to_le32(0xC0000078) +#define STATUS_INVALID_SECURITY_DESCR cpu_to_le32(0xC0000079) +#define STATUS_PROCEDURE_NOT_FOUND cpu_to_le32(0xC000007A) +#define STATUS_INVALID_IMAGE_FORMAT cpu_to_le32(0xC000007B) +#define STATUS_NO_TOKEN cpu_to_le32(0xC000007C) +#define STATUS_BAD_INHERITANCE_ACL cpu_to_le32(0xC000007D) +#define STATUS_RANGE_NOT_LOCKED cpu_to_le32(0xC000007E) +#define STATUS_DISK_FULL cpu_to_le32(0xC000007F) +#define STATUS_SERVER_DISABLED cpu_to_le32(0xC0000080) +#define STATUS_SERVER_NOT_DISABLED cpu_to_le32(0xC0000081) +#define STATUS_TOO_MANY_GUIDS_REQUESTED cpu_to_le32(0xC0000082) +#define STATUS_GUIDS_EXHAUSTED cpu_to_le32(0xC0000083) +#define STATUS_INVALID_ID_AUTHORITY cpu_to_le32(0xC0000084) +#define STATUS_AGENTS_EXHAUSTED cpu_to_le32(0xC0000085) +#define STATUS_INVALID_VOLUME_LABEL cpu_to_le32(0xC0000086) +#define STATUS_SECTION_NOT_EXTENDED cpu_to_le32(0xC0000087) +#define STATUS_NOT_MAPPED_DATA cpu_to_le32(0xC0000088) +#define STATUS_RESOURCE_DATA_NOT_FOUND cpu_to_le32(0xC0000089) +#define STATUS_RESOURCE_TYPE_NOT_FOUND cpu_to_le32(0xC000008A) +#define STATUS_RESOURCE_NAME_NOT_FOUND cpu_to_le32(0xC000008B) +#define STATUS_ARRAY_BOUNDS_EXCEEDED cpu_to_le32(0xC000008C) +#define STATUS_FLOAT_DENORMAL_OPERAND cpu_to_le32(0xC000008D) +#define STATUS_FLOAT_DIVIDE_BY_ZERO cpu_to_le32(0xC000008E) +#define STATUS_FLOAT_INEXACT_RESULT cpu_to_le32(0xC000008F) +#define STATUS_FLOAT_INVALID_OPERATION cpu_to_le32(0xC0000090) +#define STATUS_FLOAT_OVERFLOW cpu_to_le32(0xC0000091) +#define STATUS_FLOAT_STACK_CHECK cpu_to_le32(0xC0000092) +#define STATUS_FLOAT_UNDERFLOW cpu_to_le32(0xC0000093) +#define STATUS_INTEGER_DIVIDE_BY_ZERO cpu_to_le32(0xC0000094) +#define STATUS_INTEGER_OVERFLOW cpu_to_le32(0xC0000095) +#define STATUS_PRIVILEGED_INSTRUCTION cpu_to_le32(0xC0000096) +#define STATUS_TOO_MANY_PAGING_FILES cpu_to_le32(0xC0000097) +#define STATUS_FILE_INVALID cpu_to_le32(0xC0000098) +#define STATUS_ALLOTTED_SPACE_EXCEEDED cpu_to_le32(0xC0000099) +#define STATUS_INSUFFICIENT_RESOURCES cpu_to_le32(0xC000009A) +#define STATUS_DFS_EXIT_PATH_FOUND cpu_to_le32(0xC000009B) +#define STATUS_DEVICE_DATA_ERROR cpu_to_le32(0xC000009C) +#define STATUS_DEVICE_NOT_CONNECTED cpu_to_le32(0xC000009D) +#define STATUS_DEVICE_POWER_FAILURE cpu_to_le32(0xC000009E) +#define STATUS_FREE_VM_NOT_AT_BASE cpu_to_le32(0xC000009F) +#define STATUS_MEMORY_NOT_ALLOCATED cpu_to_le32(0xC00000A0) +#define STATUS_WORKING_SET_QUOTA cpu_to_le32(0xC00000A1) +#define STATUS_MEDIA_WRITE_PROTECTED cpu_to_le32(0xC00000A2) +#define STATUS_DEVICE_NOT_READY cpu_to_le32(0xC00000A3) +#define STATUS_INVALID_GROUP_ATTRIBUTES cpu_to_le32(0xC00000A4) +#define STATUS_BAD_IMPERSONATION_LEVEL cpu_to_le32(0xC00000A5) +#define STATUS_CANT_OPEN_ANONYMOUS cpu_to_le32(0xC00000A6) +#define STATUS_BAD_VALIDATION_CLASS cpu_to_le32(0xC00000A7) +#define STATUS_BAD_TOKEN_TYPE cpu_to_le32(0xC00000A8) +#define STATUS_BAD_MASTER_BOOT_RECORD cpu_to_le32(0xC00000A9) +#define STATUS_INSTRUCTION_MISALIGNMENT cpu_to_le32(0xC00000AA) +#define STATUS_INSTANCE_NOT_AVAILABLE cpu_to_le32(0xC00000AB) +#define STATUS_PIPE_NOT_AVAILABLE cpu_to_le32(0xC00000AC) +#define STATUS_INVALID_PIPE_STATE cpu_to_le32(0xC00000AD) +#define STATUS_PIPE_BUSY cpu_to_le32(0xC00000AE) +#define STATUS_ILLEGAL_FUNCTION cpu_to_le32(0xC00000AF) +#define STATUS_PIPE_DISCONNECTED cpu_to_le32(0xC00000B0) +#define STATUS_PIPE_CLOSING cpu_to_le32(0xC00000B1) +#define STATUS_PIPE_CONNECTED cpu_to_le32(0xC00000B2) +#define STATUS_PIPE_LISTENING cpu_to_le32(0xC00000B3) +#define STATUS_INVALID_READ_MODE cpu_to_le32(0xC00000B4) +#define STATUS_IO_TIMEOUT cpu_to_le32(0xC00000B5) +#define STATUS_FILE_FORCED_CLOSED cpu_to_le32(0xC00000B6) +#define STATUS_PROFILING_NOT_STARTED cpu_to_le32(0xC00000B7) +#define STATUS_PROFILING_NOT_STOPPED cpu_to_le32(0xC00000B8) +#define STATUS_COULD_NOT_INTERPRET cpu_to_le32(0xC00000B9) +#define STATUS_FILE_IS_A_DIRECTORY cpu_to_le32(0xC00000BA) +#define STATUS_NOT_SUPPORTED cpu_to_le32(0xC00000BB) +#define STATUS_REMOTE_NOT_LISTENING cpu_to_le32(0xC00000BC) +#define STATUS_DUPLICATE_NAME cpu_to_le32(0xC00000BD) +#define STATUS_BAD_NETWORK_PATH cpu_to_le32(0xC00000BE) +#define STATUS_NETWORK_BUSY cpu_to_le32(0xC00000BF) +#define STATUS_DEVICE_DOES_NOT_EXIST cpu_to_le32(0xC00000C0) +#define STATUS_TOO_MANY_COMMANDS cpu_to_le32(0xC00000C1) +#define STATUS_ADAPTER_HARDWARE_ERROR cpu_to_le32(0xC00000C2) +#define STATUS_INVALID_NETWORK_RESPONSE cpu_to_le32(0xC00000C3) +#define STATUS_UNEXPECTED_NETWORK_ERROR cpu_to_le32(0xC00000C4) +#define STATUS_BAD_REMOTE_ADAPTER cpu_to_le32(0xC00000C5) +#define STATUS_PRINT_QUEUE_FULL cpu_to_le32(0xC00000C6) +#define STATUS_NO_SPOOL_SPACE cpu_to_le32(0xC00000C7) +#define STATUS_PRINT_CANCELLED cpu_to_le32(0xC00000C8) +#define STATUS_NETWORK_NAME_DELETED cpu_to_le32(0xC00000C9) +#define STATUS_NETWORK_ACCESS_DENIED cpu_to_le32(0xC00000CA) +#define STATUS_BAD_DEVICE_TYPE cpu_to_le32(0xC00000CB) +#define STATUS_BAD_NETWORK_NAME cpu_to_le32(0xC00000CC) +#define STATUS_TOO_MANY_NAMES cpu_to_le32(0xC00000CD) +#define STATUS_TOO_MANY_SESSIONS cpu_to_le32(0xC00000CE) +#define STATUS_SHARING_PAUSED cpu_to_le32(0xC00000CF) +#define STATUS_REQUEST_NOT_ACCEPTED cpu_to_le32(0xC00000D0) +#define STATUS_REDIRECTOR_PAUSED cpu_to_le32(0xC00000D1) +#define STATUS_NET_WRITE_FAULT cpu_to_le32(0xC00000D2) +#define STATUS_PROFILING_AT_LIMIT cpu_to_le32(0xC00000D3) +#define STATUS_NOT_SAME_DEVICE cpu_to_le32(0xC00000D4) +#define STATUS_FILE_RENAMED cpu_to_le32(0xC00000D5) +#define STATUS_VIRTUAL_CIRCUIT_CLOSED cpu_to_le32(0xC00000D6) +#define STATUS_NO_SECURITY_ON_OBJECT cpu_to_le32(0xC00000D7) +#define STATUS_CANT_WAIT cpu_to_le32(0xC00000D8) +#define STATUS_PIPE_EMPTY cpu_to_le32(0xC00000D9) +#define STATUS_CANT_ACCESS_DOMAIN_INFO cpu_to_le32(0xC00000DA) +#define STATUS_CANT_TERMINATE_SELF cpu_to_le32(0xC00000DB) +#define STATUS_INVALID_SERVER_STATE cpu_to_le32(0xC00000DC) +#define STATUS_INVALID_DOMAIN_STATE cpu_to_le32(0xC00000DD) +#define STATUS_INVALID_DOMAIN_ROLE cpu_to_le32(0xC00000DE) +#define STATUS_NO_SUCH_DOMAIN cpu_to_le32(0xC00000DF) +#define STATUS_DOMAIN_EXISTS cpu_to_le32(0xC00000E0) +#define STATUS_DOMAIN_LIMIT_EXCEEDED cpu_to_le32(0xC00000E1) +#define STATUS_OPLOCK_NOT_GRANTED cpu_to_le32(0xC00000E2) +#define STATUS_INVALID_OPLOCK_PROTOCOL cpu_to_le32(0xC00000E3) +#define STATUS_INTERNAL_DB_CORRUPTION cpu_to_le32(0xC00000E4) +#define STATUS_INTERNAL_ERROR cpu_to_le32(0xC00000E5) +#define STATUS_GENERIC_NOT_MAPPED cpu_to_le32(0xC00000E6) +#define STATUS_BAD_DESCRIPTOR_FORMAT cpu_to_le32(0xC00000E7) +#define STATUS_INVALID_USER_BUFFER cpu_to_le32(0xC00000E8) +#define STATUS_UNEXPECTED_IO_ERROR cpu_to_le32(0xC00000E9) +#define STATUS_UNEXPECTED_MM_CREATE_ERR cpu_to_le32(0xC00000EA) +#define STATUS_UNEXPECTED_MM_MAP_ERROR cpu_to_le32(0xC00000EB) +#define STATUS_UNEXPECTED_MM_EXTEND_ERR cpu_to_le32(0xC00000EC) +#define STATUS_NOT_LOGON_PROCESS cpu_to_le32(0xC00000ED) +#define STATUS_LOGON_SESSION_EXISTS cpu_to_le32(0xC00000EE) +#define STATUS_INVALID_PARAMETER_1 cpu_to_le32(0xC00000EF) +#define STATUS_INVALID_PARAMETER_2 cpu_to_le32(0xC00000F0) +#define STATUS_INVALID_PARAMETER_3 cpu_to_le32(0xC00000F1) +#define STATUS_INVALID_PARAMETER_4 cpu_to_le32(0xC00000F2) +#define STATUS_INVALID_PARAMETER_5 cpu_to_le32(0xC00000F3) +#define STATUS_INVALID_PARAMETER_6 cpu_to_le32(0xC00000F4) +#define STATUS_INVALID_PARAMETER_7 cpu_to_le32(0xC00000F5) +#define STATUS_INVALID_PARAMETER_8 cpu_to_le32(0xC00000F6) +#define STATUS_INVALID_PARAMETER_9 cpu_to_le32(0xC00000F7) +#define STATUS_INVALID_PARAMETER_10 cpu_to_le32(0xC00000F8) +#define STATUS_INVALID_PARAMETER_11 cpu_to_le32(0xC00000F9) +#define STATUS_INVALID_PARAMETER_12 cpu_to_le32(0xC00000FA) +#define STATUS_REDIRECTOR_NOT_STARTED cpu_to_le32(0xC00000FB) +#define STATUS_REDIRECTOR_STARTED cpu_to_le32(0xC00000FC) +#define STATUS_STACK_OVERFLOW cpu_to_le32(0xC00000FD) +#define STATUS_NO_SUCH_PACKAGE cpu_to_le32(0xC00000FE) +#define STATUS_BAD_FUNCTION_TABLE cpu_to_le32(0xC00000FF) +#define STATUS_VARIABLE_NOT_FOUND cpu_to_le32(0xC0000100) +#define STATUS_DIRECTORY_NOT_EMPTY cpu_to_le32(0xC0000101) +#define STATUS_FILE_CORRUPT_ERROR cpu_to_le32(0xC0000102) +#define STATUS_NOT_A_DIRECTORY cpu_to_le32(0xC0000103) +#define STATUS_BAD_LOGON_SESSION_STATE cpu_to_le32(0xC0000104) +#define STATUS_LOGON_SESSION_COLLISION cpu_to_le32(0xC0000105) +#define STATUS_NAME_TOO_LONG cpu_to_le32(0xC0000106) +#define STATUS_FILES_OPEN cpu_to_le32(0xC0000107) +#define STATUS_CONNECTION_IN_USE cpu_to_le32(0xC0000108) +#define STATUS_MESSAGE_NOT_FOUND cpu_to_le32(0xC0000109) +#define STATUS_PROCESS_IS_TERMINATING cpu_to_le32(0xC000010A) +#define STATUS_INVALID_LOGON_TYPE cpu_to_le32(0xC000010B) +#define STATUS_NO_GUID_TRANSLATION cpu_to_le32(0xC000010C) +#define STATUS_CANNOT_IMPERSONATE cpu_to_le32(0xC000010D) +#define STATUS_IMAGE_ALREADY_LOADED cpu_to_le32(0xC000010E) +#define STATUS_ABIOS_NOT_PRESENT cpu_to_le32(0xC000010F) +#define STATUS_ABIOS_LID_NOT_EXIST cpu_to_le32(0xC0000110) +#define STATUS_ABIOS_LID_ALREADY_OWNED cpu_to_le32(0xC0000111) +#define STATUS_ABIOS_NOT_LID_OWNER cpu_to_le32(0xC0000112) +#define STATUS_ABIOS_INVALID_COMMAND cpu_to_le32(0xC0000113) +#define STATUS_ABIOS_INVALID_LID cpu_to_le32(0xC0000114) +#define STATUS_ABIOS_SELECTOR_NOT_AVAILABLE cpu_to_le32(0xC0000115) +#define STATUS_ABIOS_INVALID_SELECTOR cpu_to_le32(0xC0000116) +#define STATUS_NO_LDT cpu_to_le32(0xC0000117) +#define STATUS_INVALID_LDT_SIZE cpu_to_le32(0xC0000118) +#define STATUS_INVALID_LDT_OFFSET cpu_to_le32(0xC0000119) +#define STATUS_INVALID_LDT_DESCRIPTOR cpu_to_le32(0xC000011A) +#define STATUS_INVALID_IMAGE_NE_FORMAT cpu_to_le32(0xC000011B) +#define STATUS_RXACT_INVALID_STATE cpu_to_le32(0xC000011C) +#define STATUS_RXACT_COMMIT_FAILURE cpu_to_le32(0xC000011D) +#define STATUS_MAPPED_FILE_SIZE_ZERO cpu_to_le32(0xC000011E) +#define STATUS_TOO_MANY_OPENED_FILES cpu_to_le32(0xC000011F) +#define STATUS_CANCELLED cpu_to_le32(0xC0000120) +#define STATUS_CANNOT_DELETE cpu_to_le32(0xC0000121) +#define STATUS_INVALID_COMPUTER_NAME cpu_to_le32(0xC0000122) +#define STATUS_FILE_DELETED cpu_to_le32(0xC0000123) +#define STATUS_SPECIAL_ACCOUNT cpu_to_le32(0xC0000124) +#define STATUS_SPECIAL_GROUP cpu_to_le32(0xC0000125) +#define STATUS_SPECIAL_USER cpu_to_le32(0xC0000126) +#define STATUS_MEMBERS_PRIMARY_GROUP cpu_to_le32(0xC0000127) +#define STATUS_FILE_CLOSED cpu_to_le32(0xC0000128) +#define STATUS_TOO_MANY_THREADS cpu_to_le32(0xC0000129) +#define STATUS_THREAD_NOT_IN_PROCESS cpu_to_le32(0xC000012A) +#define STATUS_TOKEN_ALREADY_IN_USE cpu_to_le32(0xC000012B) +#define STATUS_PAGEFILE_QUOTA_EXCEEDED cpu_to_le32(0xC000012C) +#define STATUS_COMMITMENT_LIMIT cpu_to_le32(0xC000012D) +#define STATUS_INVALID_IMAGE_LE_FORMAT cpu_to_le32(0xC000012E) +#define STATUS_INVALID_IMAGE_NOT_MZ cpu_to_le32(0xC000012F) +#define STATUS_INVALID_IMAGE_PROTECT cpu_to_le32(0xC0000130) +#define STATUS_INVALID_IMAGE_WIN_16 cpu_to_le32(0xC0000131) +#define STATUS_LOGON_SERVER_CONFLICT cpu_to_le32(0xC0000132) +#define STATUS_TIME_DIFFERENCE_AT_DC cpu_to_le32(0xC0000133) +#define STATUS_SYNCHRONIZATION_REQUIRED cpu_to_le32(0xC0000134) +#define STATUS_DLL_NOT_FOUND cpu_to_le32(0xC0000135) +#define STATUS_OPEN_FAILED cpu_to_le32(0xC0000136) +#define STATUS_IO_PRIVILEGE_FAILED cpu_to_le32(0xC0000137) +#define STATUS_ORDINAL_NOT_FOUND cpu_to_le32(0xC0000138) +#define STATUS_ENTRYPOINT_NOT_FOUND cpu_to_le32(0xC0000139) +#define STATUS_CONTROL_C_EXIT cpu_to_le32(0xC000013A) +#define STATUS_LOCAL_DISCONNECT cpu_to_le32(0xC000013B) +#define STATUS_REMOTE_DISCONNECT cpu_to_le32(0xC000013C) +#define STATUS_REMOTE_RESOURCES cpu_to_le32(0xC000013D) +#define STATUS_LINK_FAILED cpu_to_le32(0xC000013E) +#define STATUS_LINK_TIMEOUT cpu_to_le32(0xC000013F) +#define STATUS_INVALID_CONNECTION cpu_to_le32(0xC0000140) +#define STATUS_INVALID_ADDRESS cpu_to_le32(0xC0000141) +#define STATUS_DLL_INIT_FAILED cpu_to_le32(0xC0000142) +#define STATUS_MISSING_SYSTEMFILE cpu_to_le32(0xC0000143) +#define STATUS_UNHANDLED_EXCEPTION cpu_to_le32(0xC0000144) +#define STATUS_APP_INIT_FAILURE cpu_to_le32(0xC0000145) +#define STATUS_PAGEFILE_CREATE_FAILED cpu_to_le32(0xC0000146) +#define STATUS_NO_PAGEFILE cpu_to_le32(0xC0000147) +#define STATUS_INVALID_LEVEL cpu_to_le32(0xC0000148) +#define STATUS_WRONG_PASSWORD_CORE cpu_to_le32(0xC0000149) +#define STATUS_ILLEGAL_FLOAT_CONTEXT cpu_to_le32(0xC000014A) +#define STATUS_PIPE_BROKEN cpu_to_le32(0xC000014B) +#define STATUS_REGISTRY_CORRUPT cpu_to_le32(0xC000014C) +#define STATUS_REGISTRY_IO_FAILED cpu_to_le32(0xC000014D) +#define STATUS_NO_EVENT_PAIR cpu_to_le32(0xC000014E) +#define STATUS_UNRECOGNIZED_VOLUME cpu_to_le32(0xC000014F) +#define STATUS_SERIAL_NO_DEVICE_INITED cpu_to_le32(0xC0000150) +#define STATUS_NO_SUCH_ALIAS cpu_to_le32(0xC0000151) +#define STATUS_MEMBER_NOT_IN_ALIAS cpu_to_le32(0xC0000152) +#define STATUS_MEMBER_IN_ALIAS cpu_to_le32(0xC0000153) +#define STATUS_ALIAS_EXISTS cpu_to_le32(0xC0000154) +#define STATUS_LOGON_NOT_GRANTED cpu_to_le32(0xC0000155) +#define STATUS_TOO_MANY_SECRETS cpu_to_le32(0xC0000156) +#define STATUS_SECRET_TOO_LONG cpu_to_le32(0xC0000157) +#define STATUS_INTERNAL_DB_ERROR cpu_to_le32(0xC0000158) +#define STATUS_FULLSCREEN_MODE cpu_to_le32(0xC0000159) +#define STATUS_TOO_MANY_CONTEXT_IDS cpu_to_le32(0xC000015A) +#define STATUS_LOGON_TYPE_NOT_GRANTED cpu_to_le32(0xC000015B) +#define STATUS_NOT_REGISTRY_FILE cpu_to_le32(0xC000015C) +#define STATUS_NT_CROSS_ENCRYPTION_REQUIRED cpu_to_le32(0xC000015D) +#define STATUS_DOMAIN_CTRLR_CONFIG_ERROR cpu_to_le32(0xC000015E) +#define STATUS_FT_MISSING_MEMBER cpu_to_le32(0xC000015F) +#define STATUS_ILL_FORMED_SERVICE_ENTRY cpu_to_le32(0xC0000160) +#define STATUS_ILLEGAL_CHARACTER cpu_to_le32(0xC0000161) +#define STATUS_UNMAPPABLE_CHARACTER cpu_to_le32(0xC0000162) +#define STATUS_UNDEFINED_CHARACTER cpu_to_le32(0xC0000163) +#define STATUS_FLOPPY_VOLUME cpu_to_le32(0xC0000164) +#define STATUS_FLOPPY_ID_MARK_NOT_FOUND cpu_to_le32(0xC0000165) +#define STATUS_FLOPPY_WRONG_CYLINDER cpu_to_le32(0xC0000166) +#define STATUS_FLOPPY_UNKNOWN_ERROR cpu_to_le32(0xC0000167) +#define STATUS_FLOPPY_BAD_REGISTERS cpu_to_le32(0xC0000168) +#define STATUS_DISK_RECALIBRATE_FAILED cpu_to_le32(0xC0000169) +#define STATUS_DISK_OPERATION_FAILED cpu_to_le32(0xC000016A) +#define STATUS_DISK_RESET_FAILED cpu_to_le32(0xC000016B) +#define STATUS_SHARED_IRQ_BUSY cpu_to_le32(0xC000016C) +#define STATUS_FT_ORPHANING cpu_to_le32(0xC000016D) +#define STATUS_BIOS_FAILED_TO_CONNECT_INTERRUPT cpu_to_le32(0xC000016E) +#define STATUS_PARTITION_FAILURE cpu_to_le32(0xC0000172) +#define STATUS_INVALID_BLOCK_LENGTH cpu_to_le32(0xC0000173) +#define STATUS_DEVICE_NOT_PARTITIONED cpu_to_le32(0xC0000174) +#define STATUS_UNABLE_TO_LOCK_MEDIA cpu_to_le32(0xC0000175) +#define STATUS_UNABLE_TO_UNLOAD_MEDIA cpu_to_le32(0xC0000176) +#define STATUS_EOM_OVERFLOW cpu_to_le32(0xC0000177) +#define STATUS_NO_MEDIA cpu_to_le32(0xC0000178) +#define STATUS_NO_SUCH_MEMBER cpu_to_le32(0xC000017A) +#define STATUS_INVALID_MEMBER cpu_to_le32(0xC000017B) +#define STATUS_KEY_DELETED cpu_to_le32(0xC000017C) +#define STATUS_NO_LOG_SPACE cpu_to_le32(0xC000017D) +#define STATUS_TOO_MANY_SIDS cpu_to_le32(0xC000017E) +#define STATUS_LM_CROSS_ENCRYPTION_REQUIRED cpu_to_le32(0xC000017F) +#define STATUS_KEY_HAS_CHILDREN cpu_to_le32(0xC0000180) +#define STATUS_CHILD_MUST_BE_VOLATILE cpu_to_le32(0xC0000181) +#define STATUS_DEVICE_CONFIGURATION_ERROR cpu_to_le32(0xC0000182) +#define STATUS_DRIVER_INTERNAL_ERROR cpu_to_le32(0xC0000183) +#define STATUS_INVALID_DEVICE_STATE cpu_to_le32(0xC0000184) +#define STATUS_IO_DEVICE_ERROR cpu_to_le32(0xC0000185) +#define STATUS_DEVICE_PROTOCOL_ERROR cpu_to_le32(0xC0000186) +#define STATUS_BACKUP_CONTROLLER cpu_to_le32(0xC0000187) +#define STATUS_LOG_FILE_FULL cpu_to_le32(0xC0000188) +#define STATUS_TOO_LATE cpu_to_le32(0xC0000189) +#define STATUS_NO_TRUST_LSA_SECRET cpu_to_le32(0xC000018A) +#define STATUS_NO_TRUST_SAM_ACCOUNT cpu_to_le32(0xC000018B) +#define STATUS_TRUSTED_DOMAIN_FAILURE cpu_to_le32(0xC000018C) +#define STATUS_TRUSTED_RELATIONSHIP_FAILURE cpu_to_le32(0xC000018D) +#define STATUS_EVENTLOG_FILE_CORRUPT cpu_to_le32(0xC000018E) +#define STATUS_EVENTLOG_CANT_START cpu_to_le32(0xC000018F) +#define STATUS_TRUST_FAILURE cpu_to_le32(0xC0000190) +#define STATUS_MUTANT_LIMIT_EXCEEDED cpu_to_le32(0xC0000191) +#define STATUS_NETLOGON_NOT_STARTED cpu_to_le32(0xC0000192) +#define STATUS_ACCOUNT_EXPIRED cpu_to_le32(0xC0000193) +#define STATUS_POSSIBLE_DEADLOCK cpu_to_le32(0xC0000194) +#define STATUS_NETWORK_CREDENTIAL_CONFLICT cpu_to_le32(0xC0000195) +#define STATUS_REMOTE_SESSION_LIMIT cpu_to_le32(0xC0000196) +#define STATUS_EVENTLOG_FILE_CHANGED cpu_to_le32(0xC0000197) +#define STATUS_NOLOGON_INTERDOMAIN_TRUST_ACCOUNT cpu_to_le32(0xC0000198) +#define STATUS_NOLOGON_WORKSTATION_TRUST_ACCOUNT cpu_to_le32(0xC0000199) +#define STATUS_NOLOGON_SERVER_TRUST_ACCOUNT cpu_to_le32(0xC000019A) +#define STATUS_DOMAIN_TRUST_INCONSISTENT cpu_to_le32(0xC000019B) +#define STATUS_FS_DRIVER_REQUIRED cpu_to_le32(0xC000019C) +#define STATUS_IMAGE_ALREADY_LOADED_AS_DLL cpu_to_le32(0xC000019D) +#define STATUS_NETWORK_OPEN_RESTRICTION cpu_to_le32(0xC0000201) +#define STATUS_NO_USER_SESSION_KEY cpu_to_le32(0xC0000202) +#define STATUS_USER_SESSION_DELETED cpu_to_le32(0xC0000203) +#define STATUS_RESOURCE_LANG_NOT_FOUND cpu_to_le32(0xC0000204) +#define STATUS_INSUFF_SERVER_RESOURCES cpu_to_le32(0xC0000205) +#define STATUS_INVALID_BUFFER_SIZE cpu_to_le32(0xC0000206) +#define STATUS_INVALID_ADDRESS_COMPONENT cpu_to_le32(0xC0000207) +#define STATUS_INVALID_ADDRESS_WILDCARD cpu_to_le32(0xC0000208) +#define STATUS_TOO_MANY_ADDRESSES cpu_to_le32(0xC0000209) +#define STATUS_ADDRESS_ALREADY_EXISTS cpu_to_le32(0xC000020A) +#define STATUS_ADDRESS_CLOSED cpu_to_le32(0xC000020B) +#define STATUS_CONNECTION_DISCONNECTED cpu_to_le32(0xC000020C) +#define STATUS_CONNECTION_RESET cpu_to_le32(0xC000020D) +#define STATUS_TOO_MANY_NODES cpu_to_le32(0xC000020E) +#define STATUS_TRANSACTION_ABORTED cpu_to_le32(0xC000020F) +#define STATUS_TRANSACTION_TIMED_OUT cpu_to_le32(0xC0000210) +#define STATUS_TRANSACTION_NO_RELEASE cpu_to_le32(0xC0000211) +#define STATUS_TRANSACTION_NO_MATCH cpu_to_le32(0xC0000212) +#define STATUS_TRANSACTION_RESPONDED cpu_to_le32(0xC0000213) +#define STATUS_TRANSACTION_INVALID_ID cpu_to_le32(0xC0000214) +#define STATUS_TRANSACTION_INVALID_TYPE cpu_to_le32(0xC0000215) +#define STATUS_NOT_SERVER_SESSION cpu_to_le32(0xC0000216) +#define STATUS_NOT_CLIENT_SESSION cpu_to_le32(0xC0000217) +#define STATUS_CANNOT_LOAD_REGISTRY_FILE cpu_to_le32(0xC0000218) +#define STATUS_DEBUG_ATTACH_FAILED cpu_to_le32(0xC0000219) +#define STATUS_SYSTEM_PROCESS_TERMINATED cpu_to_le32(0xC000021A) +#define STATUS_DATA_NOT_ACCEPTED cpu_to_le32(0xC000021B) +#define STATUS_NO_BROWSER_SERVERS_FOUND cpu_to_le32(0xC000021C) +#define STATUS_VDM_HARD_ERROR cpu_to_le32(0xC000021D) +#define STATUS_DRIVER_CANCEL_TIMEOUT cpu_to_le32(0xC000021E) +#define STATUS_REPLY_MESSAGE_MISMATCH cpu_to_le32(0xC000021F) +#define STATUS_MAPPED_ALIGNMENT cpu_to_le32(0xC0000220) +#define STATUS_IMAGE_CHECKSUM_MISMATCH cpu_to_le32(0xC0000221) +#define STATUS_LOST_WRITEBEHIND_DATA cpu_to_le32(0xC0000222) +#define STATUS_CLIENT_SERVER_PARAMETERS_INVALID cpu_to_le32(0xC0000223) +#define STATUS_PASSWORD_MUST_CHANGE cpu_to_le32(0xC0000224) +#define STATUS_NOT_FOUND cpu_to_le32(0xC0000225) +#define STATUS_NOT_TINY_STREAM cpu_to_le32(0xC0000226) +#define STATUS_RECOVERY_FAILURE cpu_to_le32(0xC0000227) +#define STATUS_STACK_OVERFLOW_READ cpu_to_le32(0xC0000228) +#define STATUS_FAIL_CHECK cpu_to_le32(0xC0000229) +#define STATUS_DUPLICATE_OBJECTID cpu_to_le32(0xC000022A) +#define STATUS_OBJECTID_EXISTS cpu_to_le32(0xC000022B) +#define STATUS_CONVERT_TO_LARGE cpu_to_le32(0xC000022C) +#define STATUS_RETRY cpu_to_le32(0xC000022D) +#define STATUS_FOUND_OUT_OF_SCOPE cpu_to_le32(0xC000022E) +#define STATUS_ALLOCATE_BUCKET cpu_to_le32(0xC000022F) +#define STATUS_PROPSET_NOT_FOUND cpu_to_le32(0xC0000230) +#define STATUS_MARSHALL_OVERFLOW cpu_to_le32(0xC0000231) +#define STATUS_INVALID_VARIANT cpu_to_le32(0xC0000232) +#define STATUS_DOMAIN_CONTROLLER_NOT_FOUND cpu_to_le32(0xC0000233) +#define STATUS_ACCOUNT_LOCKED_OUT cpu_to_le32(0xC0000234) +#define STATUS_HANDLE_NOT_CLOSABLE cpu_to_le32(0xC0000235) +#define STATUS_CONNECTION_REFUSED cpu_to_le32(0xC0000236) +#define STATUS_GRACEFUL_DISCONNECT cpu_to_le32(0xC0000237) +#define STATUS_ADDRESS_ALREADY_ASSOCIATED cpu_to_le32(0xC0000238) +#define STATUS_ADDRESS_NOT_ASSOCIATED cpu_to_le32(0xC0000239) +#define STATUS_CONNECTION_INVALID cpu_to_le32(0xC000023A) +#define STATUS_CONNECTION_ACTIVE cpu_to_le32(0xC000023B) +#define STATUS_NETWORK_UNREACHABLE cpu_to_le32(0xC000023C) +#define STATUS_HOST_UNREACHABLE cpu_to_le32(0xC000023D) +#define STATUS_PROTOCOL_UNREACHABLE cpu_to_le32(0xC000023E) +#define STATUS_PORT_UNREACHABLE cpu_to_le32(0xC000023F) +#define STATUS_REQUEST_ABORTED cpu_to_le32(0xC0000240) +#define STATUS_CONNECTION_ABORTED cpu_to_le32(0xC0000241) +#define STATUS_BAD_COMPRESSION_BUFFER cpu_to_le32(0xC0000242) +#define STATUS_USER_MAPPED_FILE cpu_to_le32(0xC0000243) +#define STATUS_AUDIT_FAILED cpu_to_le32(0xC0000244) +#define STATUS_TIMER_RESOLUTION_NOT_SET cpu_to_le32(0xC0000245) +#define STATUS_CONNECTION_COUNT_LIMIT cpu_to_le32(0xC0000246) +#define STATUS_LOGIN_TIME_RESTRICTION cpu_to_le32(0xC0000247) +#define STATUS_LOGIN_WKSTA_RESTRICTION cpu_to_le32(0xC0000248) +#define STATUS_IMAGE_MP_UP_MISMATCH cpu_to_le32(0xC0000249) +#define STATUS_INSUFFICIENT_LOGON_INFO cpu_to_le32(0xC0000250) +#define STATUS_BAD_DLL_ENTRYPOINT cpu_to_le32(0xC0000251) +#define STATUS_BAD_SERVICE_ENTRYPOINT cpu_to_le32(0xC0000252) +#define STATUS_LPC_REPLY_LOST cpu_to_le32(0xC0000253) +#define STATUS_IP_ADDRESS_CONFLICT1 cpu_to_le32(0xC0000254) +#define STATUS_IP_ADDRESS_CONFLICT2 cpu_to_le32(0xC0000255) +#define STATUS_REGISTRY_QUOTA_LIMIT cpu_to_le32(0xC0000256) +#define STATUS_PATH_NOT_COVERED cpu_to_le32(0xC0000257) +#define STATUS_NO_CALLBACK_ACTIVE cpu_to_le32(0xC0000258) +#define STATUS_LICENSE_QUOTA_EXCEEDED cpu_to_le32(0xC0000259) +#define STATUS_PWD_TOO_SHORT cpu_to_le32(0xC000025A) +#define STATUS_PWD_TOO_RECENT cpu_to_le32(0xC000025B) +#define STATUS_PWD_HISTORY_CONFLICT cpu_to_le32(0xC000025C) +#define STATUS_PLUGPLAY_NO_DEVICE cpu_to_le32(0xC000025E) +#define STATUS_UNSUPPORTED_COMPRESSION cpu_to_le32(0xC000025F) +#define STATUS_INVALID_HW_PROFILE cpu_to_le32(0xC0000260) +#define STATUS_INVALID_PLUGPLAY_DEVICE_PATH cpu_to_le32(0xC0000261) +#define STATUS_DRIVER_ORDINAL_NOT_FOUND cpu_to_le32(0xC0000262) +#define STATUS_DRIVER_ENTRYPOINT_NOT_FOUND cpu_to_le32(0xC0000263) +#define STATUS_RESOURCE_NOT_OWNED cpu_to_le32(0xC0000264) +#define STATUS_TOO_MANY_LINKS cpu_to_le32(0xC0000265) +#define STATUS_QUOTA_LIST_INCONSISTENT cpu_to_le32(0xC0000266) +#define STATUS_FILE_IS_OFFLINE cpu_to_le32(0xC0000267) +#define STATUS_EVALUATION_EXPIRATION cpu_to_le32(0xC0000268) +#define STATUS_ILLEGAL_DLL_RELOCATION cpu_to_le32(0xC0000269) +#define STATUS_LICENSE_VIOLATION cpu_to_le32(0xC000026A) +#define STATUS_DLL_INIT_FAILED_LOGOFF cpu_to_le32(0xC000026B) +#define STATUS_DRIVER_UNABLE_TO_LOAD cpu_to_le32(0xC000026C) +#define STATUS_DFS_UNAVAILABLE cpu_to_le32(0xC000026D) +#define STATUS_VOLUME_DISMOUNTED cpu_to_le32(0xC000026E) +#define STATUS_WX86_INTERNAL_ERROR cpu_to_le32(0xC000026F) +#define STATUS_WX86_FLOAT_STACK_CHECK cpu_to_le32(0xC0000270) +#define STATUS_VALIDATE_CONTINUE cpu_to_le32(0xC0000271) +#define STATUS_NO_MATCH cpu_to_le32(0xC0000272) +#define STATUS_NO_MORE_MATCHES cpu_to_le32(0xC0000273) +#define STATUS_NOT_A_REPARSE_POINT cpu_to_le32(0xC0000275) +#define STATUS_IO_REPARSE_TAG_INVALID cpu_to_le32(0xC0000276) +#define STATUS_IO_REPARSE_TAG_MISMATCH cpu_to_le32(0xC0000277) +#define STATUS_IO_REPARSE_DATA_INVALID cpu_to_le32(0xC0000278) +#define STATUS_IO_REPARSE_TAG_NOT_HANDLED cpu_to_le32(0xC0000279) +#define STATUS_REPARSE_POINT_NOT_RESOLVED cpu_to_le32(0xC0000280) +#define STATUS_DIRECTORY_IS_A_REPARSE_POINT cpu_to_le32(0xC0000281) +#define STATUS_RANGE_LIST_CONFLICT cpu_to_le32(0xC0000282) +#define STATUS_SOURCE_ELEMENT_EMPTY cpu_to_le32(0xC0000283) +#define STATUS_DESTINATION_ELEMENT_FULL cpu_to_le32(0xC0000284) +#define STATUS_ILLEGAL_ELEMENT_ADDRESS cpu_to_le32(0xC0000285) +#define STATUS_MAGAZINE_NOT_PRESENT cpu_to_le32(0xC0000286) +#define STATUS_REINITIALIZATION_NEEDED cpu_to_le32(0xC0000287) +#define STATUS_ENCRYPTION_FAILED cpu_to_le32(0xC000028A) +#define STATUS_DECRYPTION_FAILED cpu_to_le32(0xC000028B) +#define STATUS_RANGE_NOT_FOUND cpu_to_le32(0xC000028C) +#define STATUS_NO_RECOVERY_POLICY cpu_to_le32(0xC000028D) +#define STATUS_NO_EFS cpu_to_le32(0xC000028E) +#define STATUS_WRONG_EFS cpu_to_le32(0xC000028F) +#define STATUS_NO_USER_KEYS cpu_to_le32(0xC0000290) +#define STATUS_FILE_NOT_ENCRYPTED cpu_to_le32(0xC0000291) +#define STATUS_NOT_EXPORT_FORMAT cpu_to_le32(0xC0000292) +#define STATUS_FILE_ENCRYPTED cpu_to_le32(0xC0000293) +#define STATUS_WMI_GUID_NOT_FOUND cpu_to_le32(0xC0000295) +#define STATUS_WMI_INSTANCE_NOT_FOUND cpu_to_le32(0xC0000296) +#define STATUS_WMI_ITEMID_NOT_FOUND cpu_to_le32(0xC0000297) +#define STATUS_WMI_TRY_AGAIN cpu_to_le32(0xC0000298) +#define STATUS_SHARED_POLICY cpu_to_le32(0xC0000299) +#define STATUS_POLICY_OBJECT_NOT_FOUND cpu_to_le32(0xC000029A) +#define STATUS_POLICY_ONLY_IN_DS cpu_to_le32(0xC000029B) +#define STATUS_VOLUME_NOT_UPGRADED cpu_to_le32(0xC000029C) +#define STATUS_REMOTE_STORAGE_NOT_ACTIVE cpu_to_le32(0xC000029D) +#define STATUS_REMOTE_STORAGE_MEDIA_ERROR cpu_to_le32(0xC000029E) +#define STATUS_NO_TRACKING_SERVICE cpu_to_le32(0xC000029F) +#define STATUS_SERVER_SID_MISMATCH cpu_to_le32(0xC00002A0) +#define STATUS_DS_NO_ATTRIBUTE_OR_VALUE cpu_to_le32(0xC00002A1) +#define STATUS_DS_INVALID_ATTRIBUTE_SYNTAX cpu_to_le32(0xC00002A2) +#define STATUS_DS_ATTRIBUTE_TYPE_UNDEFINED cpu_to_le32(0xC00002A3) +#define STATUS_DS_ATTRIBUTE_OR_VALUE_EXISTS cpu_to_le32(0xC00002A4) +#define STATUS_DS_BUSY cpu_to_le32(0xC00002A5) +#define STATUS_DS_UNAVAILABLE cpu_to_le32(0xC00002A6) +#define STATUS_DS_NO_RIDS_ALLOCATED cpu_to_le32(0xC00002A7) +#define STATUS_DS_NO_MORE_RIDS cpu_to_le32(0xC00002A8) +#define STATUS_DS_INCORRECT_ROLE_OWNER cpu_to_le32(0xC00002A9) +#define STATUS_DS_RIDMGR_INIT_ERROR cpu_to_le32(0xC00002AA) +#define STATUS_DS_OBJ_CLASS_VIOLATION cpu_to_le32(0xC00002AB) +#define STATUS_DS_CANT_ON_NON_LEAF cpu_to_le32(0xC00002AC) +#define STATUS_DS_CANT_ON_RDN cpu_to_le32(0xC00002AD) +#define STATUS_DS_CANT_MOD_OBJ_CLASS cpu_to_le32(0xC00002AE) +#define STATUS_DS_CROSS_DOM_MOVE_FAILED cpu_to_le32(0xC00002AF) +#define STATUS_DS_GC_NOT_AVAILABLE cpu_to_le32(0xC00002B0) +#define STATUS_DIRECTORY_SERVICE_REQUIRED cpu_to_le32(0xC00002B1) +#define STATUS_REPARSE_ATTRIBUTE_CONFLICT cpu_to_le32(0xC00002B2) +#define STATUS_CANT_ENABLE_DENY_ONLY cpu_to_le32(0xC00002B3) +#define STATUS_FLOAT_MULTIPLE_FAULTS cpu_to_le32(0xC00002B4) +#define STATUS_FLOAT_MULTIPLE_TRAPS cpu_to_le32(0xC00002B5) +#define STATUS_DEVICE_REMOVED cpu_to_le32(0xC00002B6) +#define STATUS_JOURNAL_DELETE_IN_PROGRESS cpu_to_le32(0xC00002B7) +#define STATUS_JOURNAL_NOT_ACTIVE cpu_to_le32(0xC00002B8) +#define STATUS_NOINTERFACE cpu_to_le32(0xC00002B9) +#define STATUS_DS_ADMIN_LIMIT_EXCEEDED cpu_to_le32(0xC00002C1) +#define STATUS_DRIVER_FAILED_SLEEP cpu_to_le32(0xC00002C2) +#define STATUS_MUTUAL_AUTHENTICATION_FAILED cpu_to_le32(0xC00002C3) +#define STATUS_CORRUPT_SYSTEM_FILE cpu_to_le32(0xC00002C4) +#define STATUS_DATATYPE_MISALIGNMENT_ERROR cpu_to_le32(0xC00002C5) +#define STATUS_WMI_READ_ONLY cpu_to_le32(0xC00002C6) +#define STATUS_WMI_SET_FAILURE cpu_to_le32(0xC00002C7) +#define STATUS_COMMITMENT_MINIMUM cpu_to_le32(0xC00002C8) +#define STATUS_REG_NAT_CONSUMPTION cpu_to_le32(0xC00002C9) +#define STATUS_TRANSPORT_FULL cpu_to_le32(0xC00002CA) +#define STATUS_DS_SAM_INIT_FAILURE cpu_to_le32(0xC00002CB) +#define STATUS_ONLY_IF_CONNECTED cpu_to_le32(0xC00002CC) +#define STATUS_DS_SENSITIVE_GROUP_VIOLATION cpu_to_le32(0xC00002CD) +#define STATUS_PNP_RESTART_ENUMERATION cpu_to_le32(0xC00002CE) +#define STATUS_JOURNAL_ENTRY_DELETED cpu_to_le32(0xC00002CF) +#define STATUS_DS_CANT_MOD_PRIMARYGROUPID cpu_to_le32(0xC00002D0) +#define STATUS_SYSTEM_IMAGE_BAD_SIGNATURE cpu_to_le32(0xC00002D1) +#define STATUS_PNP_REBOOT_REQUIRED cpu_to_le32(0xC00002D2) +#define STATUS_POWER_STATE_INVALID cpu_to_le32(0xC00002D3) +#define STATUS_DS_INVALID_GROUP_TYPE cpu_to_le32(0xC00002D4) +#define STATUS_DS_NO_NEST_GLOBALGROUP_IN_MIXEDDOMAIN cpu_to_le32(0xC00002D5) +#define STATUS_DS_NO_NEST_LOCALGROUP_IN_MIXEDDOMAIN cpu_to_le32(0xC00002D6) +#define STATUS_DS_GLOBAL_CANT_HAVE_LOCAL_MEMBER cpu_to_le32(0xC00002D7) +#define STATUS_DS_GLOBAL_CANT_HAVE_UNIVERSAL_MEMBER cpu_to_le32(0xC00002D8) +#define STATUS_DS_UNIVERSAL_CANT_HAVE_LOCAL_MEMBER cpu_to_le32(0xC00002D9) +#define STATUS_DS_GLOBAL_CANT_HAVE_CROSSDOMAIN_MEMBER cpu_to_le32(0xC00002DA) +#define STATUS_DS_LOCAL_CANT_HAVE_CROSSDOMAIN_LOCAL_MEMBER cpu_to_le32(0xC00002DB) +#define STATUS_DS_HAVE_PRIMARY_MEMBERS cpu_to_le32(0xC00002DC) +#define STATUS_WMI_NOT_SUPPORTED cpu_to_le32(0xC00002DD) +#define STATUS_INSUFFICIENT_POWER cpu_to_le32(0xC00002DE) +#define STATUS_SAM_NEED_BOOTKEY_PASSWORD cpu_to_le32(0xC00002DF) +#define STATUS_SAM_NEED_BOOTKEY_FLOPPY cpu_to_le32(0xC00002E0) +#define STATUS_DS_CANT_START cpu_to_le32(0xC00002E1) +#define STATUS_DS_INIT_FAILURE cpu_to_le32(0xC00002E2) +#define STATUS_SAM_INIT_FAILURE cpu_to_le32(0xC00002E3) +#define STATUS_DS_GC_REQUIRED cpu_to_le32(0xC00002E4) +#define STATUS_DS_LOCAL_MEMBER_OF_LOCAL_ONLY cpu_to_le32(0xC00002E5) +#define STATUS_DS_NO_FPO_IN_UNIVERSAL_GROUPS cpu_to_le32(0xC00002E6) +#define STATUS_DS_MACHINE_ACCOUNT_QUOTA_EXCEEDED cpu_to_le32(0xC00002E7) +#define STATUS_MULTIPLE_FAULT_VIOLATION cpu_to_le32(0xC00002E8) +#define STATUS_CURRENT_DOMAIN_NOT_ALLOWED cpu_to_le32(0xC00002E9) +#define STATUS_CANNOT_MAKE cpu_to_le32(0xC00002EA) +#define STATUS_SYSTEM_SHUTDOWN cpu_to_le32(0xC00002EB) +#define STATUS_DS_INIT_FAILURE_CONSOLE cpu_to_le32(0xC00002EC) +#define STATUS_DS_SAM_INIT_FAILURE_CONSOLE cpu_to_le32(0xC00002ED) +#define STATUS_UNFINISHED_CONTEXT_DELETED cpu_to_le32(0xC00002EE) +#define STATUS_NO_TGT_REPLY cpu_to_le32(0xC00002EF) +#define STATUS_OBJECTID_NOT_FOUND cpu_to_le32(0xC00002F0) +#define STATUS_NO_IP_ADDRESSES cpu_to_le32(0xC00002F1) +#define STATUS_WRONG_CREDENTIAL_HANDLE cpu_to_le32(0xC00002F2) +#define STATUS_CRYPTO_SYSTEM_INVALID cpu_to_le32(0xC00002F3) +#define STATUS_MAX_REFERRALS_EXCEEDED cpu_to_le32(0xC00002F4) +#define STATUS_MUST_BE_KDC cpu_to_le32(0xC00002F5) +#define STATUS_STRONG_CRYPTO_NOT_SUPPORTED cpu_to_le32(0xC00002F6) +#define STATUS_TOO_MANY_PRINCIPALS cpu_to_le32(0xC00002F7) +#define STATUS_NO_PA_DATA cpu_to_le32(0xC00002F8) +#define STATUS_PKINIT_NAME_MISMATCH cpu_to_le32(0xC00002F9) +#define STATUS_SMARTCARD_LOGON_REQUIRED cpu_to_le32(0xC00002FA) +#define STATUS_KDC_INVALID_REQUEST cpu_to_le32(0xC00002FB) +#define STATUS_KDC_UNABLE_TO_REFER cpu_to_le32(0xC00002FC) +#define STATUS_KDC_UNKNOWN_ETYPE cpu_to_le32(0xC00002FD) +#define STATUS_SHUTDOWN_IN_PROGRESS cpu_to_le32(0xC00002FE) +#define STATUS_SERVER_SHUTDOWN_IN_PROGRESS cpu_to_le32(0xC00002FF) +#define STATUS_NOT_SUPPORTED_ON_SBS cpu_to_le32(0xC0000300) +#define STATUS_WMI_GUID_DISCONNECTED cpu_to_le32(0xC0000301) +#define STATUS_WMI_ALREADY_DISABLED cpu_to_le32(0xC0000302) +#define STATUS_WMI_ALREADY_ENABLED cpu_to_le32(0xC0000303) +#define STATUS_MFT_TOO_FRAGMENTED cpu_to_le32(0xC0000304) +#define STATUS_COPY_PROTECTION_FAILURE cpu_to_le32(0xC0000305) +#define STATUS_CSS_AUTHENTICATION_FAILURE cpu_to_le32(0xC0000306) +#define STATUS_CSS_KEY_NOT_PRESENT cpu_to_le32(0xC0000307) +#define STATUS_CSS_KEY_NOT_ESTABLISHED cpu_to_le32(0xC0000308) +#define STATUS_CSS_SCRAMBLED_SECTOR cpu_to_le32(0xC0000309) +#define STATUS_CSS_REGION_MISMATCH cpu_to_le32(0xC000030A) +#define STATUS_CSS_RESETS_EXHAUSTED cpu_to_le32(0xC000030B) +#define STATUS_PKINIT_FAILURE cpu_to_le32(0xC0000320) +#define STATUS_SMARTCARD_SUBSYSTEM_FAILURE cpu_to_le32(0xC0000321) +#define STATUS_NO_KERB_KEY cpu_to_le32(0xC0000322) +#define STATUS_HOST_DOWN cpu_to_le32(0xC0000350) +#define STATUS_UNSUPPORTED_PREAUTH cpu_to_le32(0xC0000351) +#define STATUS_EFS_ALG_BLOB_TOO_BIG cpu_to_le32(0xC0000352) +#define STATUS_PORT_NOT_SET cpu_to_le32(0xC0000353) +#define STATUS_DEBUGGER_INACTIVE cpu_to_le32(0xC0000354) +#define STATUS_DS_VERSION_CHECK_FAILURE cpu_to_le32(0xC0000355) +#define STATUS_AUDITING_DISABLED cpu_to_le32(0xC0000356) +#define STATUS_PRENT4_MACHINE_ACCOUNT cpu_to_le32(0xC0000357) +#define STATUS_DS_AG_CANT_HAVE_UNIVERSAL_MEMBER cpu_to_le32(0xC0000358) +#define STATUS_INVALID_IMAGE_WIN_32 cpu_to_le32(0xC0000359) +#define STATUS_INVALID_IMAGE_WIN_64 cpu_to_le32(0xC000035A) +#define STATUS_BAD_BINDINGS cpu_to_le32(0xC000035B) +#define STATUS_NETWORK_SESSION_EXPIRED cpu_to_le32(0xC000035C) +#define STATUS_APPHELP_BLOCK cpu_to_le32(0xC000035D) +#define STATUS_ALL_SIDS_FILTERED cpu_to_le32(0xC000035E) +#define STATUS_NOT_SAFE_MODE_DRIVER cpu_to_le32(0xC000035F) +#define STATUS_ACCESS_DISABLED_BY_POLICY_DEFAULT cpu_to_le32(0xC0000361) +#define STATUS_ACCESS_DISABLED_BY_POLICY_PATH cpu_to_le32(0xC0000362) +#define STATUS_ACCESS_DISABLED_BY_POLICY_PUBLISHER cpu_to_le32(0xC0000363) +#define STATUS_ACCESS_DISABLED_BY_POLICY_OTHER cpu_to_le32(0xC0000364) +#define STATUS_FAILED_DRIVER_ENTRY cpu_to_le32(0xC0000365) +#define STATUS_DEVICE_ENUMERATION_ERROR cpu_to_le32(0xC0000366) +#define STATUS_MOUNT_POINT_NOT_RESOLVED cpu_to_le32(0xC0000368) +#define STATUS_INVALID_DEVICE_OBJECT_PARAMETER cpu_to_le32(0xC0000369) +#define STATUS_MCA_OCCURED cpu_to_le32(0xC000036A) +#define STATUS_DRIVER_BLOCKED_CRITICAL cpu_to_le32(0xC000036B) +#define STATUS_DRIVER_BLOCKED cpu_to_le32(0xC000036C) +#define STATUS_DRIVER_DATABASE_ERROR cpu_to_le32(0xC000036D) +#define STATUS_SYSTEM_HIVE_TOO_LARGE cpu_to_le32(0xC000036E) +#define STATUS_INVALID_IMPORT_OF_NON_DLL cpu_to_le32(0xC000036F) +#define STATUS_NO_SECRETS cpu_to_le32(0xC0000371) +#define STATUS_ACCESS_DISABLED_NO_SAFER_UI_BY_POLICY cpu_to_le32(0xC0000372) +#define STATUS_FAILED_STACK_SWITCH cpu_to_le32(0xC0000373) +#define STATUS_HEAP_CORRUPTION cpu_to_le32(0xC0000374) +#define STATUS_SMARTCARD_WRONG_PIN cpu_to_le32(0xC0000380) +#define STATUS_SMARTCARD_CARD_BLOCKED cpu_to_le32(0xC0000381) +#define STATUS_SMARTCARD_CARD_NOT_AUTHENTICATED cpu_to_le32(0xC0000382) +#define STATUS_SMARTCARD_NO_CARD cpu_to_le32(0xC0000383) +#define STATUS_SMARTCARD_NO_KEY_CONTAINER cpu_to_le32(0xC0000384) +#define STATUS_SMARTCARD_NO_CERTIFICATE cpu_to_le32(0xC0000385) +#define STATUS_SMARTCARD_NO_KEYSET cpu_to_le32(0xC0000386) +#define STATUS_SMARTCARD_IO_ERROR cpu_to_le32(0xC0000387) +#define STATUS_DOWNGRADE_DETECTED cpu_to_le32(0xC0000388) +#define STATUS_SMARTCARD_CERT_REVOKED cpu_to_le32(0xC0000389) +#define STATUS_ISSUING_CA_UNTRUSTED cpu_to_le32(0xC000038A) +#define STATUS_REVOCATION_OFFLINE_C cpu_to_le32(0xC000038B) +#define STATUS_PKINIT_CLIENT_FAILURE cpu_to_le32(0xC000038C) +#define STATUS_SMARTCARD_CERT_EXPIRED cpu_to_le32(0xC000038D) +#define STATUS_DRIVER_FAILED_PRIOR_UNLOAD cpu_to_le32(0xC000038E) +#define STATUS_SMARTCARD_SILENT_CONTEXT cpu_to_le32(0xC000038F) +#define STATUS_PER_USER_TRUST_QUOTA_EXCEEDED cpu_to_le32(0xC0000401) +#define STATUS_ALL_USER_TRUST_QUOTA_EXCEEDED cpu_to_le32(0xC0000402) +#define STATUS_USER_DELETE_TRUST_QUOTA_EXCEEDED cpu_to_le32(0xC0000403) +#define STATUS_DS_NAME_NOT_UNIQUE cpu_to_le32(0xC0000404) +#define STATUS_DS_DUPLICATE_ID_FOUND cpu_to_le32(0xC0000405) +#define STATUS_DS_GROUP_CONVERSION_ERROR cpu_to_le32(0xC0000406) +#define STATUS_VOLSNAP_PREPARE_HIBERNATE cpu_to_le32(0xC0000407) +#define STATUS_USER2USER_REQUIRED cpu_to_le32(0xC0000408) +#define STATUS_STACK_BUFFER_OVERRUN cpu_to_le32(0xC0000409) +#define STATUS_NO_S4U_PROT_SUPPORT cpu_to_le32(0xC000040A) +#define STATUS_CROSSREALM_DELEGATION_FAILURE cpu_to_le32(0xC000040B) +#define STATUS_REVOCATION_OFFLINE_KDC cpu_to_le32(0xC000040C) +#define STATUS_ISSUING_CA_UNTRUSTED_KDC cpu_to_le32(0xC000040D) +#define STATUS_KDC_CERT_EXPIRED cpu_to_le32(0xC000040E) +#define STATUS_KDC_CERT_REVOKED cpu_to_le32(0xC000040F) +#define STATUS_PARAMETER_QUOTA_EXCEEDED cpu_to_le32(0xC0000410) +#define STATUS_HIBERNATION_FAILURE cpu_to_le32(0xC0000411) +#define STATUS_DELAY_LOAD_FAILED cpu_to_le32(0xC0000412) +#define STATUS_AUTHENTICATION_FIREWALL_FAILED cpu_to_le32(0xC0000413) +#define STATUS_VDM_DISALLOWED cpu_to_le32(0xC0000414) +#define STATUS_HUNG_DISPLAY_DRIVER_THREAD cpu_to_le32(0xC0000415) +#define STATUS_INSUFFICIENT_RESOURCE_FOR_SPECIFIED_SHARED_SECTION_SIZE cpu_to_le32(0xC0000416) +#define STATUS_INVALID_CRUNTIME_PARAMETER cpu_to_le32(0xC0000417) +#define STATUS_NTLM_BLOCKED cpu_to_le32(0xC0000418) +#define STATUS_ASSERTION_FAILURE cpu_to_le32(0xC0000420) +#define STATUS_VERIFIER_STOP cpu_to_le32(0xC0000421) +#define STATUS_CALLBACK_POP_STACK cpu_to_le32(0xC0000423) +#define STATUS_INCOMPATIBLE_DRIVER_BLOCKED cpu_to_le32(0xC0000424) +#define STATUS_HIVE_UNLOADED cpu_to_le32(0xC0000425) +#define STATUS_COMPRESSION_DISABLED cpu_to_le32(0xC0000426) +#define STATUS_FILE_SYSTEM_LIMITATION cpu_to_le32(0xC0000427) +#define STATUS_INVALID_IMAGE_HASH cpu_to_le32(0xC0000428) +#define STATUS_NOT_CAPABLE cpu_to_le32(0xC0000429) +#define STATUS_REQUEST_OUT_OF_SEQUENCE cpu_to_le32(0xC000042A) +#define STATUS_IMPLEMENTATION_LIMIT cpu_to_le32(0xC000042B) +#define STATUS_ELEVATION_REQUIRED cpu_to_le32(0xC000042C) +#define STATUS_BEYOND_VDL cpu_to_le32(0xC0000432) +#define STATUS_ENCOUNTERED_WRITE_IN_PROGRESS cpu_to_le32(0xC0000433) +#define STATUS_PTE_CHANGED cpu_to_le32(0xC0000434) +#define STATUS_PURGE_FAILED cpu_to_le32(0xC0000435) +#define STATUS_CRED_REQUIRES_CONFIRMATION cpu_to_le32(0xC0000440) +#define STATUS_CS_ENCRYPTION_INVALID_SERVER_RESPONSE cpu_to_le32(0xC0000441) +#define STATUS_CS_ENCRYPTION_UNSUPPORTED_SERVER cpu_to_le32(0xC0000442) +#define STATUS_CS_ENCRYPTION_EXISTING_ENCRYPTED_FILE cpu_to_le32(0xC0000443) +#define STATUS_CS_ENCRYPTION_NEW_ENCRYPTED_FILE cpu_to_le32(0xC0000444) +#define STATUS_CS_ENCRYPTION_FILE_NOT_CSE cpu_to_le32(0xC0000445) +#define STATUS_INVALID_LABEL cpu_to_le32(0xC0000446) +#define STATUS_DRIVER_PROCESS_TERMINATED cpu_to_le32(0xC0000450) +#define STATUS_AMBIGUOUS_SYSTEM_DEVICE cpu_to_le32(0xC0000451) +#define STATUS_SYSTEM_DEVICE_NOT_FOUND cpu_to_le32(0xC0000452) +#define STATUS_RESTART_BOOT_APPLICATION cpu_to_le32(0xC0000453) +#define STATUS_INVALID_TASK_NAME cpu_to_le32(0xC0000500) +#define STATUS_INVALID_TASK_INDEX cpu_to_le32(0xC0000501) +#define STATUS_THREAD_ALREADY_IN_TASK cpu_to_le32(0xC0000502) +#define STATUS_CALLBACK_BYPASS cpu_to_le32(0xC0000503) +#define STATUS_PORT_CLOSED cpu_to_le32(0xC0000700) +#define STATUS_MESSAGE_LOST cpu_to_le32(0xC0000701) +#define STATUS_INVALID_MESSAGE cpu_to_le32(0xC0000702) +#define STATUS_REQUEST_CANCELED cpu_to_le32(0xC0000703) +#define STATUS_RECURSIVE_DISPATCH cpu_to_le32(0xC0000704) +#define STATUS_LPC_RECEIVE_BUFFER_EXPECTED cpu_to_le32(0xC0000705) +#define STATUS_LPC_INVALID_CONNECTION_USAGE cpu_to_le32(0xC0000706) +#define STATUS_LPC_REQUESTS_NOT_ALLOWED cpu_to_le32(0xC0000707) +#define STATUS_RESOURCE_IN_USE cpu_to_le32(0xC0000708) +#define STATUS_HARDWARE_MEMORY_ERROR cpu_to_le32(0xC0000709) +#define STATUS_THREADPOOL_HANDLE_EXCEPTION cpu_to_le32(0xC000070A) +#define STATUS_THREADPOOL_SET_EVENT_ON_COMPLETION_FAILED cpu_to_le32(0xC000070B) +#define STATUS_THREADPOOL_RELEASE_SEMAPHORE_ON_COMPLETION_FAILED cpu_to_le32(0xC000070C) +#define STATUS_THREADPOOL_RELEASE_MUTEX_ON_COMPLETION_FAILED cpu_to_le32(0xC000070D) +#define STATUS_THREADPOOL_FREE_LIBRARY_ON_COMPLETION_FAILED cpu_to_le32(0xC000070E) +#define STATUS_THREADPOOL_RELEASED_DURING_OPERATION cpu_to_le32(0xC000070F) +#define STATUS_CALLBACK_RETURNED_WHILE_IMPERSONATING cpu_to_le32(0xC0000710) +#define STATUS_APC_RETURNED_WHILE_IMPERSONATING cpu_to_le32(0xC0000711) +#define STATUS_PROCESS_IS_PROTECTED cpu_to_le32(0xC0000712) +#define STATUS_MCA_EXCEPTION cpu_to_le32(0xC0000713) +#define STATUS_CERTIFICATE_MAPPING_NOT_UNIQUE cpu_to_le32(0xC0000714) +#define STATUS_SYMLINK_CLASS_DISABLED cpu_to_le32(0xC0000715) +#define STATUS_INVALID_IDN_NORMALIZATION cpu_to_le32(0xC0000716) +#define STATUS_NO_UNICODE_TRANSLATION cpu_to_le32(0xC0000717) +#define STATUS_ALREADY_REGISTERED cpu_to_le32(0xC0000718) +#define STATUS_CONTEXT_MISMATCH cpu_to_le32(0xC0000719) +#define STATUS_PORT_ALREADY_HAS_COMPLETION_LIST cpu_to_le32(0xC000071A) +#define STATUS_CALLBACK_RETURNED_THREAD_PRIORITY cpu_to_le32(0xC000071B) +#define STATUS_INVALID_THREAD cpu_to_le32(0xC000071C) +#define STATUS_CALLBACK_RETURNED_TRANSACTION cpu_to_le32(0xC000071D) +#define STATUS_CALLBACK_RETURNED_LDR_LOCK cpu_to_le32(0xC000071E) +#define STATUS_CALLBACK_RETURNED_LANG cpu_to_le32(0xC000071F) +#define STATUS_CALLBACK_RETURNED_PRI_BACK cpu_to_le32(0xC0000720) +#define STATUS_CALLBACK_RETURNED_THREAD_AFFINITY cpu_to_le32(0xC0000721) +#define STATUS_DISK_REPAIR_DISABLED cpu_to_le32(0xC0000800) +#define STATUS_DS_DOMAIN_RENAME_IN_PROGRESS cpu_to_le32(0xC0000801) +#define STATUS_DISK_QUOTA_EXCEEDED cpu_to_le32(0xC0000802) +#define STATUS_CONTENT_BLOCKED cpu_to_le32(0xC0000804) +#define STATUS_BAD_CLUSTERS cpu_to_le32(0xC0000805) +#define STATUS_VOLUME_DIRTY cpu_to_le32(0xC0000806) +#define STATUS_FILE_CHECKED_OUT cpu_to_le32(0xC0000901) +#define STATUS_CHECKOUT_REQUIRED cpu_to_le32(0xC0000902) +#define STATUS_BAD_FILE_TYPE cpu_to_le32(0xC0000903) +#define STATUS_FILE_TOO_LARGE cpu_to_le32(0xC0000904) +#define STATUS_FORMS_AUTH_REQUIRED cpu_to_le32(0xC0000905) +#define STATUS_VIRUS_INFECTED cpu_to_le32(0xC0000906) +#define STATUS_VIRUS_DELETED cpu_to_le32(0xC0000907) +#define STATUS_BAD_MCFG_TABLE cpu_to_le32(0xC0000908) +#define STATUS_WOW_ASSERTION cpu_to_le32(0xC0009898) +#define STATUS_INVALID_SIGNATURE cpu_to_le32(0xC000A000) +#define STATUS_HMAC_NOT_SUPPORTED cpu_to_le32(0xC000A001) +#define STATUS_IPSEC_QUEUE_OVERFLOW cpu_to_le32(0xC000A010) +#define STATUS_ND_QUEUE_OVERFLOW cpu_to_le32(0xC000A011) +#define STATUS_HOPLIMIT_EXCEEDED cpu_to_le32(0xC000A012) +#define STATUS_PROTOCOL_NOT_SUPPORTED cpu_to_le32(0xC000A013) +#define STATUS_LOST_WRITEBEHIND_DATA_NETWORK_DISCONNECTED cpu_to_le32(0xC000A080) +#define STATUS_LOST_WRITEBEHIND_DATA_NETWORK_SERVER_ERROR cpu_to_le32(0xC000A081) +#define STATUS_LOST_WRITEBEHIND_DATA_LOCAL_DISK_ERROR cpu_to_le32(0xC000A082) +#define STATUS_XML_PARSE_ERROR cpu_to_le32(0xC000A083) +#define STATUS_XMLDSIG_ERROR cpu_to_le32(0xC000A084) +#define STATUS_WRONG_COMPARTMENT cpu_to_le32(0xC000A085) +#define STATUS_AUTHIP_FAILURE cpu_to_le32(0xC000A086) +#define DBG_NO_STATE_CHANGE cpu_to_le32(0xC0010001) +#define DBG_APP_NOT_IDLE cpu_to_le32(0xC0010002) +#define RPC_NT_INVALID_STRING_BINDING cpu_to_le32(0xC0020001) +#define RPC_NT_WRONG_KIND_OF_BINDING cpu_to_le32(0xC0020002) +#define RPC_NT_INVALID_BINDING cpu_to_le32(0xC0020003) +#define RPC_NT_PROTSEQ_NOT_SUPPORTED cpu_to_le32(0xC0020004) +#define RPC_NT_INVALID_RPC_PROTSEQ cpu_to_le32(0xC0020005) +#define RPC_NT_INVALID_STRING_UUID cpu_to_le32(0xC0020006) +#define RPC_NT_INVALID_ENDPOINT_FORMAT cpu_to_le32(0xC0020007) +#define RPC_NT_INVALID_NET_ADDR cpu_to_le32(0xC0020008) +#define RPC_NT_NO_ENDPOINT_FOUND cpu_to_le32(0xC0020009) +#define RPC_NT_INVALID_TIMEOUT cpu_to_le32(0xC002000A) +#define RPC_NT_OBJECT_NOT_FOUND cpu_to_le32(0xC002000B) +#define RPC_NT_ALREADY_REGISTERED cpu_to_le32(0xC002000C) +#define RPC_NT_TYPE_ALREADY_REGISTERED cpu_to_le32(0xC002000D) +#define RPC_NT_ALREADY_LISTENING cpu_to_le32(0xC002000E) +#define RPC_NT_NO_PROTSEQS_REGISTERED cpu_to_le32(0xC002000F) +#define RPC_NT_NOT_LISTENING cpu_to_le32(0xC0020010) +#define RPC_NT_UNKNOWN_MGR_TYPE cpu_to_le32(0xC0020011) +#define RPC_NT_UNKNOWN_IF cpu_to_le32(0xC0020012) +#define RPC_NT_NO_BINDINGS cpu_to_le32(0xC0020013) +#define RPC_NT_NO_PROTSEQS cpu_to_le32(0xC0020014) +#define RPC_NT_CANT_CREATE_ENDPOINT cpu_to_le32(0xC0020015) +#define RPC_NT_OUT_OF_RESOURCES cpu_to_le32(0xC0020016) +#define RPC_NT_SERVER_UNAVAILABLE cpu_to_le32(0xC0020017) +#define RPC_NT_SERVER_TOO_BUSY cpu_to_le32(0xC0020018) +#define RPC_NT_INVALID_NETWORK_OPTIONS cpu_to_le32(0xC0020019) +#define RPC_NT_NO_CALL_ACTIVE cpu_to_le32(0xC002001A) +#define RPC_NT_CALL_FAILED cpu_to_le32(0xC002001B) +#define RPC_NT_CALL_FAILED_DNE cpu_to_le32(0xC002001C) +#define RPC_NT_PROTOCOL_ERROR cpu_to_le32(0xC002001D) +#define RPC_NT_UNSUPPORTED_TRANS_SYN cpu_to_le32(0xC002001F) +#define RPC_NT_UNSUPPORTED_TYPE cpu_to_le32(0xC0020021) +#define RPC_NT_INVALID_TAG cpu_to_le32(0xC0020022) +#define RPC_NT_INVALID_BOUND cpu_to_le32(0xC0020023) +#define RPC_NT_NO_ENTRY_NAME cpu_to_le32(0xC0020024) +#define RPC_NT_INVALID_NAME_SYNTAX cpu_to_le32(0xC0020025) +#define RPC_NT_UNSUPPORTED_NAME_SYNTAX cpu_to_le32(0xC0020026) +#define RPC_NT_UUID_NO_ADDRESS cpu_to_le32(0xC0020028) +#define RPC_NT_DUPLICATE_ENDPOINT cpu_to_le32(0xC0020029) +#define RPC_NT_UNKNOWN_AUTHN_TYPE cpu_to_le32(0xC002002A) +#define RPC_NT_MAX_CALLS_TOO_SMALL cpu_to_le32(0xC002002B) +#define RPC_NT_STRING_TOO_LONG cpu_to_le32(0xC002002C) +#define RPC_NT_PROTSEQ_NOT_FOUND cpu_to_le32(0xC002002D) +#define RPC_NT_PROCNUM_OUT_OF_RANGE cpu_to_le32(0xC002002E) +#define RPC_NT_BINDING_HAS_NO_AUTH cpu_to_le32(0xC002002F) +#define RPC_NT_UNKNOWN_AUTHN_SERVICE cpu_to_le32(0xC0020030) +#define RPC_NT_UNKNOWN_AUTHN_LEVEL cpu_to_le32(0xC0020031) +#define RPC_NT_INVALID_AUTH_IDENTITY cpu_to_le32(0xC0020032) +#define RPC_NT_UNKNOWN_AUTHZ_SERVICE cpu_to_le32(0xC0020033) +#define EPT_NT_INVALID_ENTRY cpu_to_le32(0xC0020034) +#define EPT_NT_CANT_PERFORM_OP cpu_to_le32(0xC0020035) +#define EPT_NT_NOT_REGISTERED cpu_to_le32(0xC0020036) +#define RPC_NT_NOTHING_TO_EXPORT cpu_to_le32(0xC0020037) +#define RPC_NT_INCOMPLETE_NAME cpu_to_le32(0xC0020038) +#define RPC_NT_INVALID_VERS_OPTION cpu_to_le32(0xC0020039) +#define RPC_NT_NO_MORE_MEMBERS cpu_to_le32(0xC002003A) +#define RPC_NT_NOT_ALL_OBJS_UNEXPORTED cpu_to_le32(0xC002003B) +#define RPC_NT_INTERFACE_NOT_FOUND cpu_to_le32(0xC002003C) +#define RPC_NT_ENTRY_ALREADY_EXISTS cpu_to_le32(0xC002003D) +#define RPC_NT_ENTRY_NOT_FOUND cpu_to_le32(0xC002003E) +#define RPC_NT_NAME_SERVICE_UNAVAILABLE cpu_to_le32(0xC002003F) +#define RPC_NT_INVALID_NAF_ID cpu_to_le32(0xC0020040) +#define RPC_NT_CANNOT_SUPPORT cpu_to_le32(0xC0020041) +#define RPC_NT_NO_CONTEXT_AVAILABLE cpu_to_le32(0xC0020042) +#define RPC_NT_INTERNAL_ERROR cpu_to_le32(0xC0020043) +#define RPC_NT_ZERO_DIVIDE cpu_to_le32(0xC0020044) +#define RPC_NT_ADDRESS_ERROR cpu_to_le32(0xC0020045) +#define RPC_NT_FP_DIV_ZERO cpu_to_le32(0xC0020046) +#define RPC_NT_FP_UNDERFLOW cpu_to_le32(0xC0020047) +#define RPC_NT_FP_OVERFLOW cpu_to_le32(0xC0020048) +#define RPC_NT_CALL_IN_PROGRESS cpu_to_le32(0xC0020049) +#define RPC_NT_NO_MORE_BINDINGS cpu_to_le32(0xC002004A) +#define RPC_NT_GROUP_MEMBER_NOT_FOUND cpu_to_le32(0xC002004B) +#define EPT_NT_CANT_CREATE cpu_to_le32(0xC002004C) +#define RPC_NT_INVALID_OBJECT cpu_to_le32(0xC002004D) +#define RPC_NT_NO_INTERFACES cpu_to_le32(0xC002004F) +#define RPC_NT_CALL_CANCELLED cpu_to_le32(0xC0020050) +#define RPC_NT_BINDING_INCOMPLETE cpu_to_le32(0xC0020051) +#define RPC_NT_COMM_FAILURE cpu_to_le32(0xC0020052) +#define RPC_NT_UNSUPPORTED_AUTHN_LEVEL cpu_to_le32(0xC0020053) +#define RPC_NT_NO_PRINC_NAME cpu_to_le32(0xC0020054) +#define RPC_NT_NOT_RPC_ERROR cpu_to_le32(0xC0020055) +#define RPC_NT_SEC_PKG_ERROR cpu_to_le32(0xC0020057) +#define RPC_NT_NOT_CANCELLED cpu_to_le32(0xC0020058) +#define RPC_NT_INVALID_ASYNC_HANDLE cpu_to_le32(0xC0020062) +#define RPC_NT_INVALID_ASYNC_CALL cpu_to_le32(0xC0020063) +#define RPC_NT_PROXY_ACCESS_DENIED cpu_to_le32(0xC0020064) +#define RPC_NT_NO_MORE_ENTRIES cpu_to_le32(0xC0030001) +#define RPC_NT_SS_CHAR_TRANS_OPEN_FAIL cpu_to_le32(0xC0030002) +#define RPC_NT_SS_CHAR_TRANS_SHORT_FILE cpu_to_le32(0xC0030003) +#define RPC_NT_SS_IN_NULL_CONTEXT cpu_to_le32(0xC0030004) +#define RPC_NT_SS_CONTEXT_MISMATCH cpu_to_le32(0xC0030005) +#define RPC_NT_SS_CONTEXT_DAMAGED cpu_to_le32(0xC0030006) +#define RPC_NT_SS_HANDLES_MISMATCH cpu_to_le32(0xC0030007) +#define RPC_NT_SS_CANNOT_GET_CALL_HANDLE cpu_to_le32(0xC0030008) +#define RPC_NT_NULL_REF_POINTER cpu_to_le32(0xC0030009) +#define RPC_NT_ENUM_VALUE_OUT_OF_RANGE cpu_to_le32(0xC003000A) +#define RPC_NT_BYTE_COUNT_TOO_SMALL cpu_to_le32(0xC003000B) +#define RPC_NT_BAD_STUB_DATA cpu_to_le32(0xC003000C) +#define RPC_NT_INVALID_ES_ACTION cpu_to_le32(0xC0030059) +#define RPC_NT_WRONG_ES_VERSION cpu_to_le32(0xC003005A) +#define RPC_NT_WRONG_STUB_VERSION cpu_to_le32(0xC003005B) +#define RPC_NT_INVALID_PIPE_OBJECT cpu_to_le32(0xC003005C) +#define RPC_NT_INVALID_PIPE_OPERATION cpu_to_le32(0xC003005D) +#define RPC_NT_WRONG_PIPE_VERSION cpu_to_le32(0xC003005E) +#define RPC_NT_PIPE_CLOSED cpu_to_le32(0xC003005F) +#define RPC_NT_PIPE_DISCIPLINE_ERROR cpu_to_le32(0xC0030060) +#define RPC_NT_PIPE_EMPTY cpu_to_le32(0xC0030061) +#define STATUS_PNP_BAD_MPS_TABLE cpu_to_le32(0xC0040035) +#define STATUS_PNP_TRANSLATION_FAILED cpu_to_le32(0xC0040036) +#define STATUS_PNP_IRQ_TRANSLATION_FAILED cpu_to_le32(0xC0040037) +#define STATUS_PNP_INVALID_ID cpu_to_le32(0xC0040038) +#define STATUS_IO_REISSUE_AS_CACHED cpu_to_le32(0xC0040039) +#define STATUS_CTX_WINSTATION_NAME_INVALID cpu_to_le32(0xC00A0001) +#define STATUS_CTX_INVALID_PD cpu_to_le32(0xC00A0002) +#define STATUS_CTX_PD_NOT_FOUND cpu_to_le32(0xC00A0003) +#define STATUS_CTX_CLOSE_PENDING cpu_to_le32(0xC00A0006) +#define STATUS_CTX_NO_OUTBUF cpu_to_le32(0xC00A0007) +#define STATUS_CTX_MODEM_INF_NOT_FOUND cpu_to_le32(0xC00A0008) +#define STATUS_CTX_INVALID_MODEMNAME cpu_to_le32(0xC00A0009) +#define STATUS_CTX_RESPONSE_ERROR cpu_to_le32(0xC00A000A) +#define STATUS_CTX_MODEM_RESPONSE_TIMEOUT cpu_to_le32(0xC00A000B) +#define STATUS_CTX_MODEM_RESPONSE_NO_CARRIER cpu_to_le32(0xC00A000C) +#define STATUS_CTX_MODEM_RESPONSE_NO_DIALTONE cpu_to_le32(0xC00A000D) +#define STATUS_CTX_MODEM_RESPONSE_BUSY cpu_to_le32(0xC00A000E) +#define STATUS_CTX_MODEM_RESPONSE_VOICE cpu_to_le32(0xC00A000F) +#define STATUS_CTX_TD_ERROR cpu_to_le32(0xC00A0010) +#define STATUS_CTX_LICENSE_CLIENT_INVALID cpu_to_le32(0xC00A0012) +#define STATUS_CTX_LICENSE_NOT_AVAILABLE cpu_to_le32(0xC00A0013) +#define STATUS_CTX_LICENSE_EXPIRED cpu_to_le32(0xC00A0014) +#define STATUS_CTX_WINSTATION_NOT_FOUND cpu_to_le32(0xC00A0015) +#define STATUS_CTX_WINSTATION_NAME_COLLISION cpu_to_le32(0xC00A0016) +#define STATUS_CTX_WINSTATION_BUSY cpu_to_le32(0xC00A0017) +#define STATUS_CTX_BAD_VIDEO_MODE cpu_to_le32(0xC00A0018) +#define STATUS_CTX_GRAPHICS_INVALID cpu_to_le32(0xC00A0022) +#define STATUS_CTX_NOT_CONSOLE cpu_to_le32(0xC00A0024) +#define STATUS_CTX_CLIENT_QUERY_TIMEOUT cpu_to_le32(0xC00A0026) +#define STATUS_CTX_CONSOLE_DISCONNECT cpu_to_le32(0xC00A0027) +#define STATUS_CTX_CONSOLE_CONNECT cpu_to_le32(0xC00A0028) +#define STATUS_CTX_SHADOW_DENIED cpu_to_le32(0xC00A002A) +#define STATUS_CTX_WINSTATION_ACCESS_DENIED cpu_to_le32(0xC00A002B) +#define STATUS_CTX_INVALID_WD cpu_to_le32(0xC00A002E) +#define STATUS_CTX_WD_NOT_FOUND cpu_to_le32(0xC00A002F) +#define STATUS_CTX_SHADOW_INVALID cpu_to_le32(0xC00A0030) +#define STATUS_CTX_SHADOW_DISABLED cpu_to_le32(0xC00A0031) +#define STATUS_RDP_PROTOCOL_ERROR cpu_to_le32(0xC00A0032) +#define STATUS_CTX_CLIENT_LICENSE_NOT_SET cpu_to_le32(0xC00A0033) +#define STATUS_CTX_CLIENT_LICENSE_IN_USE cpu_to_le32(0xC00A0034) +#define STATUS_CTX_SHADOW_ENDED_BY_MODE_CHANGE cpu_to_le32(0xC00A0035) +#define STATUS_CTX_SHADOW_NOT_RUNNING cpu_to_le32(0xC00A0036) +#define STATUS_CTX_LOGON_DISABLED cpu_to_le32(0xC00A0037) +#define STATUS_CTX_SECURITY_LAYER_ERROR cpu_to_le32(0xC00A0038) +#define STATUS_TS_INCOMPATIBLE_SESSIONS cpu_to_le32(0xC00A0039) +#define STATUS_MUI_FILE_NOT_FOUND cpu_to_le32(0xC00B0001) +#define STATUS_MUI_INVALID_FILE cpu_to_le32(0xC00B0002) +#define STATUS_MUI_INVALID_RC_CONFIG cpu_to_le32(0xC00B0003) +#define STATUS_MUI_INVALID_LOCALE_NAME cpu_to_le32(0xC00B0004) +#define STATUS_MUI_INVALID_ULTIMATEFALLBACK_NAME cpu_to_le32(0xC00B0005) +#define STATUS_MUI_FILE_NOT_LOADED cpu_to_le32(0xC00B0006) +#define STATUS_RESOURCE_ENUM_USER_STOP cpu_to_le32(0xC00B0007) +#define STATUS_CLUSTER_INVALID_NODE cpu_to_le32(0xC0130001) +#define STATUS_CLUSTER_NODE_EXISTS cpu_to_le32(0xC0130002) +#define STATUS_CLUSTER_JOIN_IN_PROGRESS cpu_to_le32(0xC0130003) +#define STATUS_CLUSTER_NODE_NOT_FOUND cpu_to_le32(0xC0130004) +#define STATUS_CLUSTER_LOCAL_NODE_NOT_FOUND cpu_to_le32(0xC0130005) +#define STATUS_CLUSTER_NETWORK_EXISTS cpu_to_le32(0xC0130006) +#define STATUS_CLUSTER_NETWORK_NOT_FOUND cpu_to_le32(0xC0130007) +#define STATUS_CLUSTER_NETINTERFACE_EXISTS cpu_to_le32(0xC0130008) +#define STATUS_CLUSTER_NETINTERFACE_NOT_FOUND cpu_to_le32(0xC0130009) +#define STATUS_CLUSTER_INVALID_REQUEST cpu_to_le32(0xC013000A) +#define STATUS_CLUSTER_INVALID_NETWORK_PROVIDER cpu_to_le32(0xC013000B) +#define STATUS_CLUSTER_NODE_DOWN cpu_to_le32(0xC013000C) +#define STATUS_CLUSTER_NODE_UNREACHABLE cpu_to_le32(0xC013000D) +#define STATUS_CLUSTER_NODE_NOT_MEMBER cpu_to_le32(0xC013000E) +#define STATUS_CLUSTER_JOIN_NOT_IN_PROGRESS cpu_to_le32(0xC013000F) +#define STATUS_CLUSTER_INVALID_NETWORK cpu_to_le32(0xC0130010) +#define STATUS_CLUSTER_NO_NET_ADAPTERS cpu_to_le32(0xC0130011) +#define STATUS_CLUSTER_NODE_UP cpu_to_le32(0xC0130012) +#define STATUS_CLUSTER_NODE_PAUSED cpu_to_le32(0xC0130013) +#define STATUS_CLUSTER_NODE_NOT_PAUSED cpu_to_le32(0xC0130014) +#define STATUS_CLUSTER_NO_SECURITY_CONTEXT cpu_to_le32(0xC0130015) +#define STATUS_CLUSTER_NETWORK_NOT_INTERNAL cpu_to_le32(0xC0130016) +#define STATUS_CLUSTER_POISONED cpu_to_le32(0xC0130017) +#define STATUS_ACPI_INVALID_OPCODE cpu_to_le32(0xC0140001) +#define STATUS_ACPI_STACK_OVERFLOW cpu_to_le32(0xC0140002) +#define STATUS_ACPI_ASSERT_FAILED cpu_to_le32(0xC0140003) +#define STATUS_ACPI_INVALID_INDEX cpu_to_le32(0xC0140004) +#define STATUS_ACPI_INVALID_ARGUMENT cpu_to_le32(0xC0140005) +#define STATUS_ACPI_FATAL cpu_to_le32(0xC0140006) +#define STATUS_ACPI_INVALID_SUPERNAME cpu_to_le32(0xC0140007) +#define STATUS_ACPI_INVALID_ARGTYPE cpu_to_le32(0xC0140008) +#define STATUS_ACPI_INVALID_OBJTYPE cpu_to_le32(0xC0140009) +#define STATUS_ACPI_INVALID_TARGETTYPE cpu_to_le32(0xC014000A) +#define STATUS_ACPI_INCORRECT_ARGUMENT_COUNT cpu_to_le32(0xC014000B) +#define STATUS_ACPI_ADDRESS_NOT_MAPPED cpu_to_le32(0xC014000C) +#define STATUS_ACPI_INVALID_EVENTTYPE cpu_to_le32(0xC014000D) +#define STATUS_ACPI_HANDLER_COLLISION cpu_to_le32(0xC014000E) +#define STATUS_ACPI_INVALID_DATA cpu_to_le32(0xC014000F) +#define STATUS_ACPI_INVALID_REGION cpu_to_le32(0xC0140010) +#define STATUS_ACPI_INVALID_ACCESS_SIZE cpu_to_le32(0xC0140011) +#define STATUS_ACPI_ACQUIRE_GLOBAL_LOCK cpu_to_le32(0xC0140012) +#define STATUS_ACPI_ALREADY_INITIALIZED cpu_to_le32(0xC0140013) +#define STATUS_ACPI_NOT_INITIALIZED cpu_to_le32(0xC0140014) +#define STATUS_ACPI_INVALID_MUTEX_LEVEL cpu_to_le32(0xC0140015) +#define STATUS_ACPI_MUTEX_NOT_OWNED cpu_to_le32(0xC0140016) +#define STATUS_ACPI_MUTEX_NOT_OWNER cpu_to_le32(0xC0140017) +#define STATUS_ACPI_RS_ACCESS cpu_to_le32(0xC0140018) +#define STATUS_ACPI_INVALID_TABLE cpu_to_le32(0xC0140019) +#define STATUS_ACPI_REG_HANDLER_FAILED cpu_to_le32(0xC0140020) +#define STATUS_ACPI_POWER_REQUEST_FAILED cpu_to_le32(0xC0140021) +#define STATUS_SXS_SECTION_NOT_FOUND cpu_to_le32(0xC0150001) +#define STATUS_SXS_CANT_GEN_ACTCTX cpu_to_le32(0xC0150002) +#define STATUS_SXS_INVALID_ACTCTXDATA_FORMAT cpu_to_le32(0xC0150003) +#define STATUS_SXS_ASSEMBLY_NOT_FOUND cpu_to_le32(0xC0150004) +#define STATUS_SXS_MANIFEST_FORMAT_ERROR cpu_to_le32(0xC0150005) +#define STATUS_SXS_MANIFEST_PARSE_ERROR cpu_to_le32(0xC0150006) +#define STATUS_SXS_ACTIVATION_CONTEXT_DISABLED cpu_to_le32(0xC0150007) +#define STATUS_SXS_KEY_NOT_FOUND cpu_to_le32(0xC0150008) +#define STATUS_SXS_VERSION_CONFLICT cpu_to_le32(0xC0150009) +#define STATUS_SXS_WRONG_SECTION_TYPE cpu_to_le32(0xC015000A) +#define STATUS_SXS_THREAD_QUERIES_DISABLED cpu_to_le32(0xC015000B) +#define STATUS_SXS_ASSEMBLY_MISSING cpu_to_le32(0xC015000C) +#define STATUS_SXS_PROCESS_DEFAULT_ALREADY_SET cpu_to_le32(0xC015000E) +#define STATUS_SXS_EARLY_DEACTIVATION cpu_to_le32(0xC015000F) +#define STATUS_SXS_INVALID_DEACTIVATION cpu_to_le32(0xC0150010) +#define STATUS_SXS_MULTIPLE_DEACTIVATION cpu_to_le32(0xC0150011) +#define STATUS_SXS_SYSTEM_DEFAULT_ACTIVATION_CONTEXT_EMPTY cpu_to_le32(0xC0150012) +#define STATUS_SXS_PROCESS_TERMINATION_REQUESTED cpu_to_le32(0xC0150013) +#define STATUS_SXS_CORRUPT_ACTIVATION_STACK cpu_to_le32(0xC0150014) +#define STATUS_SXS_CORRUPTION cpu_to_le32(0xC0150015) +#define STATUS_SXS_INVALID_IDENTITY_ATTRIBUTE_VALUE cpu_to_le32(0xC0150016) +#define STATUS_SXS_INVALID_IDENTITY_ATTRIBUTE_NAME cpu_to_le32(0xC0150017) +#define STATUS_SXS_IDENTITY_DUPLICATE_ATTRIBUTE cpu_to_le32(0xC0150018) +#define STATUS_SXS_IDENTITY_PARSE_ERROR cpu_to_le32(0xC0150019) +#define STATUS_SXS_COMPONENT_STORE_CORRUPT cpu_to_le32(0xC015001A) +#define STATUS_SXS_FILE_HASH_MISMATCH cpu_to_le32(0xC015001B) +#define STATUS_SXS_MANIFEST_IDENTITY_SAME_BUT_CONTENTS_DIFFERENT cpu_to_le32(0xC015001C) +#define STATUS_SXS_IDENTITIES_DIFFERENT cpu_to_le32(0xC015001D) +#define STATUS_SXS_ASSEMBLY_IS_NOT_A_DEPLOYMENT cpu_to_le32(0xC015001E) +#define STATUS_SXS_FILE_NOT_PART_OF_ASSEMBLY cpu_to_le32(0xC015001F) +#define STATUS_ADVANCED_INSTALLER_FAILED cpu_to_le32(0xC0150020) +#define STATUS_XML_ENCODING_MISMATCH cpu_to_le32(0xC0150021) +#define STATUS_SXS_MANIFEST_TOO_BIG cpu_to_le32(0xC0150022) +#define STATUS_SXS_SETTING_NOT_REGISTERED cpu_to_le32(0xC0150023) +#define STATUS_SXS_TRANSACTION_CLOSURE_INCOMPLETE cpu_to_le32(0xC0150024) +#define STATUS_SMI_PRIMITIVE_INSTALLER_FAILED cpu_to_le32(0xC0150025) +#define STATUS_GENERIC_COMMAND_FAILED cpu_to_le32(0xC0150026) +#define STATUS_SXS_FILE_HASH_MISSING cpu_to_le32(0xC0150027) +#define STATUS_TRANSACTIONAL_CONFLICT cpu_to_le32(0xC0190001) +#define STATUS_INVALID_TRANSACTION cpu_to_le32(0xC0190002) +#define STATUS_TRANSACTION_NOT_ACTIVE cpu_to_le32(0xC0190003) +#define STATUS_TM_INITIALIZATION_FAILED cpu_to_le32(0xC0190004) +#define STATUS_RM_NOT_ACTIVE cpu_to_le32(0xC0190005) +#define STATUS_RM_METADATA_CORRUPT cpu_to_le32(0xC0190006) +#define STATUS_TRANSACTION_NOT_JOINED cpu_to_le32(0xC0190007) +#define STATUS_DIRECTORY_NOT_RM cpu_to_le32(0xC0190008) +#define STATUS_TRANSACTIONS_UNSUPPORTED_REMOTE cpu_to_le32(0xC019000A) +#define STATUS_LOG_RESIZE_INVALID_SIZE cpu_to_le32(0xC019000B) +#define STATUS_REMOTE_FILE_VERSION_MISMATCH cpu_to_le32(0xC019000C) +#define STATUS_CRM_PROTOCOL_ALREADY_EXISTS cpu_to_le32(0xC019000F) +#define STATUS_TRANSACTION_PROPAGATION_FAILED cpu_to_le32(0xC0190010) +#define STATUS_CRM_PROTOCOL_NOT_FOUND cpu_to_le32(0xC0190011) +#define STATUS_TRANSACTION_SUPERIOR_EXISTS cpu_to_le32(0xC0190012) +#define STATUS_TRANSACTION_REQUEST_NOT_VALID cpu_to_le32(0xC0190013) +#define STATUS_TRANSACTION_NOT_REQUESTED cpu_to_le32(0xC0190014) +#define STATUS_TRANSACTION_ALREADY_ABORTED cpu_to_le32(0xC0190015) +#define STATUS_TRANSACTION_ALREADY_COMMITTED cpu_to_le32(0xC0190016) +#define STATUS_TRANSACTION_INVALID_MARSHALL_BUFFER cpu_to_le32(0xC0190017) +#define STATUS_CURRENT_TRANSACTION_NOT_VALID cpu_to_le32(0xC0190018) +#define STATUS_LOG_GROWTH_FAILED cpu_to_le32(0xC0190019) +#define STATUS_OBJECT_NO_LONGER_EXISTS cpu_to_le32(0xC0190021) +#define STATUS_STREAM_MINIVERSION_NOT_FOUND cpu_to_le32(0xC0190022) +#define STATUS_STREAM_MINIVERSION_NOT_VALID cpu_to_le32(0xC0190023) +#define STATUS_MINIVERSION_INACCESSIBLE_FROM_SPECIFIED_TRANSACTION cpu_to_le32(0xC0190024) +#define STATUS_CANT_OPEN_MINIVERSION_WITH_MODIFY_INTENT cpu_to_le32(0xC0190025) +#define STATUS_CANT_CREATE_MORE_STREAM_MINIVERSIONS cpu_to_le32(0xC0190026) +#define STATUS_HANDLE_NO_LONGER_VALID cpu_to_le32(0xC0190028) +#define STATUS_LOG_CORRUPTION_DETECTED cpu_to_le32(0xC0190030) +#define STATUS_RM_DISCONNECTED cpu_to_le32(0xC0190032) +#define STATUS_ENLISTMENT_NOT_SUPERIOR cpu_to_le32(0xC0190033) +#define STATUS_FILE_IDENTITY_NOT_PERSISTENT cpu_to_le32(0xC0190036) +#define STATUS_CANT_BREAK_TRANSACTIONAL_DEPENDENCY cpu_to_le32(0xC0190037) +#define STATUS_CANT_CROSS_RM_BOUNDARY cpu_to_le32(0xC0190038) +#define STATUS_TXF_DIR_NOT_EMPTY cpu_to_le32(0xC0190039) +#define STATUS_INDOUBT_TRANSACTIONS_EXIST cpu_to_le32(0xC019003A) +#define STATUS_TM_VOLATILE cpu_to_le32(0xC019003B) +#define STATUS_ROLLBACK_TIMER_EXPIRED cpu_to_le32(0xC019003C) +#define STATUS_TXF_ATTRIBUTE_CORRUPT cpu_to_le32(0xC019003D) +#define STATUS_EFS_NOT_ALLOWED_IN_TRANSACTION cpu_to_le32(0xC019003E) +#define STATUS_TRANSACTIONAL_OPEN_NOT_ALLOWED cpu_to_le32(0xC019003F) +#define STATUS_TRANSACTED_MAPPING_UNSUPPORTED_REMOTE cpu_to_le32(0xC0190040) +#define STATUS_TRANSACTION_REQUIRED_PROMOTION cpu_to_le32(0xC0190043) +#define STATUS_CANNOT_EXECUTE_FILE_IN_TRANSACTION cpu_to_le32(0xC0190044) +#define STATUS_TRANSACTIONS_NOT_FROZEN cpu_to_le32(0xC0190045) +#define STATUS_TRANSACTION_FREEZE_IN_PROGRESS cpu_to_le32(0xC0190046) +#define STATUS_NOT_SNAPSHOT_VOLUME cpu_to_le32(0xC0190047) +#define STATUS_NO_SAVEPOINT_WITH_OPEN_FILES cpu_to_le32(0xC0190048) +#define STATUS_SPARSE_NOT_ALLOWED_IN_TRANSACTION cpu_to_le32(0xC0190049) +#define STATUS_TM_IDENTITY_MISMATCH cpu_to_le32(0xC019004A) +#define STATUS_FLOATED_SECTION cpu_to_le32(0xC019004B) +#define STATUS_CANNOT_ACCEPT_TRANSACTED_WORK cpu_to_le32(0xC019004C) +#define STATUS_CANNOT_ABORT_TRANSACTIONS cpu_to_le32(0xC019004D) +#define STATUS_TRANSACTION_NOT_FOUND cpu_to_le32(0xC019004E) +#define STATUS_RESOURCEMANAGER_NOT_FOUND cpu_to_le32(0xC019004F) +#define STATUS_ENLISTMENT_NOT_FOUND cpu_to_le32(0xC0190050) +#define STATUS_TRANSACTIONMANAGER_NOT_FOUND cpu_to_le32(0xC0190051) +#define STATUS_TRANSACTIONMANAGER_NOT_ONLINE cpu_to_le32(0xC0190052) +#define STATUS_TRANSACTIONMANAGER_RECOVERY_NAME_COLLISION cpu_to_le32(0xC0190053) +#define STATUS_TRANSACTION_NOT_ROOT cpu_to_le32(0xC0190054) +#define STATUS_TRANSACTION_OBJECT_EXPIRED cpu_to_le32(0xC0190055) +#define STATUS_COMPRESSION_NOT_ALLOWED_IN_TRANSACTION cpu_to_le32(0xC0190056) +#define STATUS_TRANSACTION_RESPONSE_NOT_ENLISTED cpu_to_le32(0xC0190057) +#define STATUS_TRANSACTION_RECORD_TOO_LONG cpu_to_le32(0xC0190058) +#define STATUS_NO_LINK_TRACKING_IN_TRANSACTION cpu_to_le32(0xC0190059) +#define STATUS_OPERATION_NOT_SUPPORTED_IN_TRANSACTION cpu_to_le32(0xC019005A) +#define STATUS_TRANSACTION_INTEGRITY_VIOLATED cpu_to_le32(0xC019005B) +#define STATUS_LOG_SECTOR_INVALID cpu_to_le32(0xC01A0001) +#define STATUS_LOG_SECTOR_PARITY_INVALID cpu_to_le32(0xC01A0002) +#define STATUS_LOG_SECTOR_REMAPPED cpu_to_le32(0xC01A0003) +#define STATUS_LOG_BLOCK_INCOMPLETE cpu_to_le32(0xC01A0004) +#define STATUS_LOG_INVALID_RANGE cpu_to_le32(0xC01A0005) +#define STATUS_LOG_BLOCKS_EXHAUSTED cpu_to_le32(0xC01A0006) +#define STATUS_LOG_READ_CONTEXT_INVALID cpu_to_le32(0xC01A0007) +#define STATUS_LOG_RESTART_INVALID cpu_to_le32(0xC01A0008) +#define STATUS_LOG_BLOCK_VERSION cpu_to_le32(0xC01A0009) +#define STATUS_LOG_BLOCK_INVALID cpu_to_le32(0xC01A000A) +#define STATUS_LOG_READ_MODE_INVALID cpu_to_le32(0xC01A000B) +#define STATUS_LOG_METADATA_CORRUPT cpu_to_le32(0xC01A000D) +#define STATUS_LOG_METADATA_INVALID cpu_to_le32(0xC01A000E) +#define STATUS_LOG_METADATA_INCONSISTENT cpu_to_le32(0xC01A000F) +#define STATUS_LOG_RESERVATION_INVALID cpu_to_le32(0xC01A0010) +#define STATUS_LOG_CANT_DELETE cpu_to_le32(0xC01A0011) +#define STATUS_LOG_CONTAINER_LIMIT_EXCEEDED cpu_to_le32(0xC01A0012) +#define STATUS_LOG_START_OF_LOG cpu_to_le32(0xC01A0013) +#define STATUS_LOG_POLICY_ALREADY_INSTALLED cpu_to_le32(0xC01A0014) +#define STATUS_LOG_POLICY_NOT_INSTALLED cpu_to_le32(0xC01A0015) +#define STATUS_LOG_POLICY_INVALID cpu_to_le32(0xC01A0016) +#define STATUS_LOG_POLICY_CONFLICT cpu_to_le32(0xC01A0017) +#define STATUS_LOG_PINNED_ARCHIVE_TAIL cpu_to_le32(0xC01A0018) +#define STATUS_LOG_RECORD_NONEXISTENT cpu_to_le32(0xC01A0019) +#define STATUS_LOG_RECORDS_RESERVED_INVALID cpu_to_le32(0xC01A001A) +#define STATUS_LOG_SPACE_RESERVED_INVALID cpu_to_le32(0xC01A001B) +#define STATUS_LOG_TAIL_INVALID cpu_to_le32(0xC01A001C) +#define STATUS_LOG_FULL cpu_to_le32(0xC01A001D) +#define STATUS_LOG_MULTIPLEXED cpu_to_le32(0xC01A001E) +#define STATUS_LOG_DEDICATED cpu_to_le32(0xC01A001F) +#define STATUS_LOG_ARCHIVE_NOT_IN_PROGRESS cpu_to_le32(0xC01A0020) +#define STATUS_LOG_ARCHIVE_IN_PROGRESS cpu_to_le32(0xC01A0021) +#define STATUS_LOG_EPHEMERAL cpu_to_le32(0xC01A0022) +#define STATUS_LOG_NOT_ENOUGH_CONTAINERS cpu_to_le32(0xC01A0023) +#define STATUS_LOG_CLIENT_ALREADY_REGISTERED cpu_to_le32(0xC01A0024) +#define STATUS_LOG_CLIENT_NOT_REGISTERED cpu_to_le32(0xC01A0025) +#define STATUS_LOG_FULL_HANDLER_IN_PROGRESS cpu_to_le32(0xC01A0026) +#define STATUS_LOG_CONTAINER_READ_FAILED cpu_to_le32(0xC01A0027) +#define STATUS_LOG_CONTAINER_WRITE_FAILED cpu_to_le32(0xC01A0028) +#define STATUS_LOG_CONTAINER_OPEN_FAILED cpu_to_le32(0xC01A0029) +#define STATUS_LOG_CONTAINER_STATE_INVALID cpu_to_le32(0xC01A002A) +#define STATUS_LOG_STATE_INVALID cpu_to_le32(0xC01A002B) +#define STATUS_LOG_PINNED cpu_to_le32(0xC01A002C) +#define STATUS_LOG_METADATA_FLUSH_FAILED cpu_to_le32(0xC01A002D) +#define STATUS_LOG_INCONSISTENT_SECURITY cpu_to_le32(0xC01A002E) +#define STATUS_LOG_APPENDED_FLUSH_FAILED cpu_to_le32(0xC01A002F) +#define STATUS_LOG_PINNED_RESERVATION cpu_to_le32(0xC01A0030) +#define STATUS_VIDEO_HUNG_DISPLAY_DRIVER_THREAD cpu_to_le32(0xC01B00EA) +#define STATUS_FLT_NO_HANDLER_DEFINED cpu_to_le32(0xC01C0001) +#define STATUS_FLT_CONTEXT_ALREADY_DEFINED cpu_to_le32(0xC01C0002) +#define STATUS_FLT_INVALID_ASYNCHRONOUS_REQUEST cpu_to_le32(0xC01C0003) +#define STATUS_FLT_DISALLOW_FAST_IO cpu_to_le32(0xC01C0004) +#define STATUS_FLT_INVALID_NAME_REQUEST cpu_to_le32(0xC01C0005) +#define STATUS_FLT_NOT_SAFE_TO_POST_OPERATION cpu_to_le32(0xC01C0006) +#define STATUS_FLT_NOT_INITIALIZED cpu_to_le32(0xC01C0007) +#define STATUS_FLT_FILTER_NOT_READY cpu_to_le32(0xC01C0008) +#define STATUS_FLT_POST_OPERATION_CLEANUP cpu_to_le32(0xC01C0009) +#define STATUS_FLT_INTERNAL_ERROR cpu_to_le32(0xC01C000A) +#define STATUS_FLT_DELETING_OBJECT cpu_to_le32(0xC01C000B) +#define STATUS_FLT_MUST_BE_NONPAGED_POOL cpu_to_le32(0xC01C000C) +#define STATUS_FLT_DUPLICATE_ENTRY cpu_to_le32(0xC01C000D) +#define STATUS_FLT_CBDQ_DISABLED cpu_to_le32(0xC01C000E) +#define STATUS_FLT_DO_NOT_ATTACH cpu_to_le32(0xC01C000F) +#define STATUS_FLT_DO_NOT_DETACH cpu_to_le32(0xC01C0010) +#define STATUS_FLT_INSTANCE_ALTITUDE_COLLISION cpu_to_le32(0xC01C0011) +#define STATUS_FLT_INSTANCE_NAME_COLLISION cpu_to_le32(0xC01C0012) +#define STATUS_FLT_FILTER_NOT_FOUND cpu_to_le32(0xC01C0013) +#define STATUS_FLT_VOLUME_NOT_FOUND cpu_to_le32(0xC01C0014) +#define STATUS_FLT_INSTANCE_NOT_FOUND cpu_to_le32(0xC01C0015) +#define STATUS_FLT_CONTEXT_ALLOCATION_NOT_FOUND cpu_to_le32(0xC01C0016) +#define STATUS_FLT_INVALID_CONTEXT_REGISTRATION cpu_to_le32(0xC01C0017) +#define STATUS_FLT_NAME_CACHE_MISS cpu_to_le32(0xC01C0018) +#define STATUS_FLT_NO_DEVICE_OBJECT cpu_to_le32(0xC01C0019) +#define STATUS_FLT_VOLUME_ALREADY_MOUNTED cpu_to_le32(0xC01C001A) +#define STATUS_FLT_ALREADY_ENLISTED cpu_to_le32(0xC01C001B) +#define STATUS_FLT_CONTEXT_ALREADY_LINKED cpu_to_le32(0xC01C001C) +#define STATUS_FLT_NO_WAITER_FOR_REPLY cpu_to_le32(0xC01C0020) +#define STATUS_MONITOR_NO_DESCRIPTOR cpu_to_le32(0xC01D0001) +#define STATUS_MONITOR_UNKNOWN_DESCRIPTOR_FORMAT cpu_to_le32(0xC01D0002) +#define STATUS_MONITOR_INVALID_DESCRIPTOR_CHECKSUM cpu_to_le32(0xC01D0003) +#define STATUS_MONITOR_INVALID_STANDARD_TIMING_BLOCK cpu_to_le32(0xC01D0004) +#define STATUS_MONITOR_WMI_DATABLOCK_REGISTRATION_FAILED cpu_to_le32(0xC01D0005) +#define STATUS_MONITOR_INVALID_SERIAL_NUMBER_MONDSC_BLOCK cpu_to_le32(0xC01D0006) +#define STATUS_MONITOR_INVALID_USER_FRIENDLY_MONDSC_BLOCK cpu_to_le32(0xC01D0007) +#define STATUS_MONITOR_NO_MORE_DESCRIPTOR_DATA cpu_to_le32(0xC01D0008) +#define STATUS_MONITOR_INVALID_DETAILED_TIMING_BLOCK cpu_to_le32(0xC01D0009) +#define STATUS_GRAPHICS_NOT_EXCLUSIVE_MODE_OWNER cpu_to_le32(0xC01E0000) +#define STATUS_GRAPHICS_INSUFFICIENT_DMA_BUFFER cpu_to_le32(0xC01E0001) +#define STATUS_GRAPHICS_INVALID_DISPLAY_ADAPTER cpu_to_le32(0xC01E0002) +#define STATUS_GRAPHICS_ADAPTER_WAS_RESET cpu_to_le32(0xC01E0003) +#define STATUS_GRAPHICS_INVALID_DRIVER_MODEL cpu_to_le32(0xC01E0004) +#define STATUS_GRAPHICS_PRESENT_MODE_CHANGED cpu_to_le32(0xC01E0005) +#define STATUS_GRAPHICS_PRESENT_OCCLUDED cpu_to_le32(0xC01E0006) +#define STATUS_GRAPHICS_PRESENT_DENIED cpu_to_le32(0xC01E0007) +#define STATUS_GRAPHICS_CANNOTCOLORCONVERT cpu_to_le32(0xC01E0008) +#define STATUS_GRAPHICS_NO_VIDEO_MEMORY cpu_to_le32(0xC01E0100) +#define STATUS_GRAPHICS_CANT_LOCK_MEMORY cpu_to_le32(0xC01E0101) +#define STATUS_GRAPHICS_ALLOCATION_BUSY cpu_to_le32(0xC01E0102) +#define STATUS_GRAPHICS_TOO_MANY_REFERENCES cpu_to_le32(0xC01E0103) +#define STATUS_GRAPHICS_TRY_AGAIN_LATER cpu_to_le32(0xC01E0104) +#define STATUS_GRAPHICS_TRY_AGAIN_NOW cpu_to_le32(0xC01E0105) +#define STATUS_GRAPHICS_ALLOCATION_INVALID cpu_to_le32(0xC01E0106) +#define STATUS_GRAPHICS_UNSWIZZLING_APERTURE_UNAVAILABLE cpu_to_le32(0xC01E0107) +#define STATUS_GRAPHICS_UNSWIZZLING_APERTURE_UNSUPPORTED cpu_to_le32(0xC01E0108) +#define STATUS_GRAPHICS_CANT_EVICT_PINNED_ALLOCATION cpu_to_le32(0xC01E0109) +#define STATUS_GRAPHICS_INVALID_ALLOCATION_USAGE cpu_to_le32(0xC01E0110) +#define STATUS_GRAPHICS_CANT_RENDER_LOCKED_ALLOCATION cpu_to_le32(0xC01E0111) +#define STATUS_GRAPHICS_ALLOCATION_CLOSED cpu_to_le32(0xC01E0112) +#define STATUS_GRAPHICS_INVALID_ALLOCATION_INSTANCE cpu_to_le32(0xC01E0113) +#define STATUS_GRAPHICS_INVALID_ALLOCATION_HANDLE cpu_to_le32(0xC01E0114) +#define STATUS_GRAPHICS_WRONG_ALLOCATION_DEVICE cpu_to_le32(0xC01E0115) +#define STATUS_GRAPHICS_ALLOCATION_CONTENT_LOST cpu_to_le32(0xC01E0116) +#define STATUS_GRAPHICS_GPU_EXCEPTION_ON_DEVICE cpu_to_le32(0xC01E0200) +#define STATUS_GRAPHICS_INVALID_VIDPN_TOPOLOGY cpu_to_le32(0xC01E0300) +#define STATUS_GRAPHICS_VIDPN_TOPOLOGY_NOT_SUPPORTED cpu_to_le32(0xC01E0301) +#define STATUS_GRAPHICS_VIDPN_TOPOLOGY_CURRENTLY_NOT_SUPPORTED cpu_to_le32(0xC01E0302) +#define STATUS_GRAPHICS_INVALID_VIDPN cpu_to_le32(0xC01E0303) +#define STATUS_GRAPHICS_INVALID_VIDEO_PRESENT_SOURCE cpu_to_le32(0xC01E0304) +#define STATUS_GRAPHICS_INVALID_VIDEO_PRESENT_TARGET cpu_to_le32(0xC01E0305) +#define STATUS_GRAPHICS_VIDPN_MODALITY_NOT_SUPPORTED cpu_to_le32(0xC01E0306) +#define STATUS_GRAPHICS_INVALID_VIDPN_SOURCEMODESET cpu_to_le32(0xC01E0308) +#define STATUS_GRAPHICS_INVALID_VIDPN_TARGETMODESET cpu_to_le32(0xC01E0309) +#define STATUS_GRAPHICS_INVALID_FREQUENCY cpu_to_le32(0xC01E030A) +#define STATUS_GRAPHICS_INVALID_ACTIVE_REGION cpu_to_le32(0xC01E030B) +#define STATUS_GRAPHICS_INVALID_TOTAL_REGION cpu_to_le32(0xC01E030C) +#define STATUS_GRAPHICS_INVALID_VIDEO_PRESENT_SOURCE_MODE cpu_to_le32(0xC01E0310) +#define STATUS_GRAPHICS_INVALID_VIDEO_PRESENT_TARGET_MODE cpu_to_le32(0xC01E0311) +#define STATUS_GRAPHICS_PINNED_MODE_MUST_REMAIN_IN_SET cpu_to_le32(0xC01E0312) +#define STATUS_GRAPHICS_PATH_ALREADY_IN_TOPOLOGY cpu_to_le32(0xC01E0313) +#define STATUS_GRAPHICS_MODE_ALREADY_IN_MODESET cpu_to_le32(0xC01E0314) +#define STATUS_GRAPHICS_INVALID_VIDEOPRESENTSOURCESET cpu_to_le32(0xC01E0315) +#define STATUS_GRAPHICS_INVALID_VIDEOPRESENTTARGETSET cpu_to_le32(0xC01E0316) +#define STATUS_GRAPHICS_SOURCE_ALREADY_IN_SET cpu_to_le32(0xC01E0317) +#define STATUS_GRAPHICS_TARGET_ALREADY_IN_SET cpu_to_le32(0xC01E0318) +#define STATUS_GRAPHICS_INVALID_VIDPN_PRESENT_PATH cpu_to_le32(0xC01E0319) +#define STATUS_GRAPHICS_NO_RECOMMENDED_VIDPN_TOPOLOGY cpu_to_le32(0xC01E031A) +#define STATUS_GRAPHICS_INVALID_MONITOR_FREQUENCYRANGESET cpu_to_le32(0xC01E031B) +#define STATUS_GRAPHICS_INVALID_MONITOR_FREQUENCYRANGE cpu_to_le32(0xC01E031C) +#define STATUS_GRAPHICS_FREQUENCYRANGE_NOT_IN_SET cpu_to_le32(0xC01E031D) +#define STATUS_GRAPHICS_FREQUENCYRANGE_ALREADY_IN_SET cpu_to_le32(0xC01E031F) +#define STATUS_GRAPHICS_STALE_MODESET cpu_to_le32(0xC01E0320) +#define STATUS_GRAPHICS_INVALID_MONITOR_SOURCEMODESET cpu_to_le32(0xC01E0321) +#define STATUS_GRAPHICS_INVALID_MONITOR_SOURCE_MODE cpu_to_le32(0xC01E0322) +#define STATUS_GRAPHICS_NO_RECOMMENDED_FUNCTIONAL_VIDPN cpu_to_le32(0xC01E0323) +#define STATUS_GRAPHICS_MODE_ID_MUST_BE_UNIQUE cpu_to_le32(0xC01E0324) +#define STATUS_GRAPHICS_EMPTY_ADAPTER_MONITOR_MODE_SUPPORT_INTERSECTION cpu_to_le32(0xC01E0325) +#define STATUS_GRAPHICS_VIDEO_PRESENT_TARGETS_LESS_THAN_SOURCES cpu_to_le32(0xC01E0326) +#define STATUS_GRAPHICS_PATH_NOT_IN_TOPOLOGY cpu_to_le32(0xC01E0327) +#define STATUS_GRAPHICS_ADAPTER_MUST_HAVE_AT_LEAST_ONE_SOURCE cpu_to_le32(0xC01E0328) +#define STATUS_GRAPHICS_ADAPTER_MUST_HAVE_AT_LEAST_ONE_TARGET cpu_to_le32(0xC01E0329) +#define STATUS_GRAPHICS_INVALID_MONITORDESCRIPTORSET cpu_to_le32(0xC01E032A) +#define STATUS_GRAPHICS_INVALID_MONITORDESCRIPTOR cpu_to_le32(0xC01E032B) +#define STATUS_GRAPHICS_MONITORDESCRIPTOR_NOT_IN_SET cpu_to_le32(0xC01E032C) +#define STATUS_GRAPHICS_MONITORDESCRIPTOR_ALREADY_IN_SET cpu_to_le32(0xC01E032D) +#define STATUS_GRAPHICS_MONITORDESCRIPTOR_ID_MUST_BE_UNIQUE cpu_to_le32(0xC01E032E) +#define STATUS_GRAPHICS_INVALID_VIDPN_TARGET_SUBSET_TYPE cpu_to_le32(0xC01E032F) +#define STATUS_GRAPHICS_RESOURCES_NOT_RELATED cpu_to_le32(0xC01E0330) +#define STATUS_GRAPHICS_SOURCE_ID_MUST_BE_UNIQUE cpu_to_le32(0xC01E0331) +#define STATUS_GRAPHICS_TARGET_ID_MUST_BE_UNIQUE cpu_to_le32(0xC01E0332) +#define STATUS_GRAPHICS_NO_AVAILABLE_VIDPN_TARGET cpu_to_le32(0xC01E0333) +#define STATUS_GRAPHICS_MONITOR_COULD_NOT_BE_ASSOCIATED_WITH_ADAPTER cpu_to_le32(0xC01E0334) +#define STATUS_GRAPHICS_NO_VIDPNMGR cpu_to_le32(0xC01E0335) +#define STATUS_GRAPHICS_NO_ACTIVE_VIDPN cpu_to_le32(0xC01E0336) +#define STATUS_GRAPHICS_STALE_VIDPN_TOPOLOGY cpu_to_le32(0xC01E0337) +#define STATUS_GRAPHICS_MONITOR_NOT_CONNECTED cpu_to_le32(0xC01E0338) +#define STATUS_GRAPHICS_SOURCE_NOT_IN_TOPOLOGY cpu_to_le32(0xC01E0339) +#define STATUS_GRAPHICS_INVALID_PRIMARYSURFACE_SIZE cpu_to_le32(0xC01E033A) +#define STATUS_GRAPHICS_INVALID_VISIBLEREGION_SIZE cpu_to_le32(0xC01E033B) +#define STATUS_GRAPHICS_INVALID_STRIDE cpu_to_le32(0xC01E033C) +#define STATUS_GRAPHICS_INVALID_PIXELFORMAT cpu_to_le32(0xC01E033D) +#define STATUS_GRAPHICS_INVALID_COLORBASIS cpu_to_le32(0xC01E033E) +#define STATUS_GRAPHICS_INVALID_PIXELVALUEACCESSMODE cpu_to_le32(0xC01E033F) +#define STATUS_GRAPHICS_TARGET_NOT_IN_TOPOLOGY cpu_to_le32(0xC01E0340) +#define STATUS_GRAPHICS_NO_DISPLAY_MODE_MANAGEMENT_SUPPORT cpu_to_le32(0xC01E0341) +#define STATUS_GRAPHICS_VIDPN_SOURCE_IN_USE cpu_to_le32(0xC01E0342) +#define STATUS_GRAPHICS_CANT_ACCESS_ACTIVE_VIDPN cpu_to_le32(0xC01E0343) +#define STATUS_GRAPHICS_INVALID_PATH_IMPORTANCE_ORDINAL cpu_to_le32(0xC01E0344) +#define STATUS_GRAPHICS_INVALID_PATH_CONTENT_GEOMETRY_TRANSFORMATION cpu_to_le32(0xC01E0345) +#define STATUS_GRAPHICS_PATH_CONTENT_GEOMETRY_TRANSFORMATION_NOT_SUPPORTED cpu_to_le32(0xC01E0346) +#define STATUS_GRAPHICS_INVALID_GAMMA_RAMP cpu_to_le32(0xC01E0347) +#define STATUS_GRAPHICS_GAMMA_RAMP_NOT_SUPPORTED cpu_to_le32(0xC01E0348) +#define STATUS_GRAPHICS_MULTISAMPLING_NOT_SUPPORTED cpu_to_le32(0xC01E0349) +#define STATUS_GRAPHICS_MODE_NOT_IN_MODESET cpu_to_le32(0xC01E034A) +#define STATUS_GRAPHICS_INVALID_VIDPN_TOPOLOGY_RECOMMENDATION_REASON cpu_to_le32(0xC01E034D) +#define STATUS_GRAPHICS_INVALID_PATH_CONTENT_TYPE cpu_to_le32(0xC01E034E) +#define STATUS_GRAPHICS_INVALID_COPYPROTECTION_TYPE cpu_to_le32(0xC01E034F) +#define STATUS_GRAPHICS_UNASSIGNED_MODESET_ALREADY_EXISTS cpu_to_le32(0xC01E0350) +#define STATUS_GRAPHICS_INVALID_SCANLINE_ORDERING cpu_to_le32(0xC01E0352) +#define STATUS_GRAPHICS_TOPOLOGY_CHANGES_NOT_ALLOWED cpu_to_le32(0xC01E0353) +#define STATUS_GRAPHICS_NO_AVAILABLE_IMPORTANCE_ORDINALS cpu_to_le32(0xC01E0354) +#define STATUS_GRAPHICS_INCOMPATIBLE_PRIVATE_FORMAT cpu_to_le32(0xC01E0355) +#define STATUS_GRAPHICS_INVALID_MODE_PRUNING_ALGORITHM cpu_to_le32(0xC01E0356) +#define STATUS_GRAPHICS_INVALID_MONITOR_CAPABILITY_ORIGIN cpu_to_le32(0xC01E0357) +#define STATUS_GRAPHICS_INVALID_MONITOR_FREQUENCYRANGE_CONSTRAINT cpu_to_le32(0xC01E0358) +#define STATUS_GRAPHICS_MAX_NUM_PATHS_REACHED cpu_to_le32(0xC01E0359) +#define STATUS_GRAPHICS_CANCEL_VIDPN_TOPOLOGY_AUGMENTATION cpu_to_le32(0xC01E035A) +#define STATUS_GRAPHICS_INVALID_CLIENT_TYPE cpu_to_le32(0xC01E035B) +#define STATUS_GRAPHICS_CLIENTVIDPN_NOT_SET cpu_to_le32(0xC01E035C) +#define STATUS_GRAPHICS_SPECIFIED_CHILD_ALREADY_CONNECTED cpu_to_le32(0xC01E0400) +#define STATUS_GRAPHICS_CHILD_DESCRIPTOR_NOT_SUPPORTED cpu_to_le32(0xC01E0401) +#define STATUS_GRAPHICS_NOT_A_LINKED_ADAPTER cpu_to_le32(0xC01E0430) +#define STATUS_GRAPHICS_LEADLINK_NOT_ENUMERATED cpu_to_le32(0xC01E0431) +#define STATUS_GRAPHICS_CHAINLINKS_NOT_ENUMERATED cpu_to_le32(0xC01E0432) +#define STATUS_GRAPHICS_ADAPTER_CHAIN_NOT_READY cpu_to_le32(0xC01E0433) +#define STATUS_GRAPHICS_CHAINLINKS_NOT_STARTED cpu_to_le32(0xC01E0434) +#define STATUS_GRAPHICS_CHAINLINKS_NOT_POWERED_ON cpu_to_le32(0xC01E0435) +#define STATUS_GRAPHICS_INCONSISTENT_DEVICE_LINK_STATE cpu_to_le32(0xC01E0436) +#define STATUS_GRAPHICS_NOT_POST_DEVICE_DRIVER cpu_to_le32(0xC01E0438) +#define STATUS_GRAPHICS_ADAPTER_ACCESS_NOT_EXCLUDED cpu_to_le32(0xC01E043B) +#define STATUS_GRAPHICS_OPM_PROTECTED_OUTPUT_DOES_NOT_HAVE_COPP_SEMANTICS cpu_to_le32(0xC01E051C) +#define STATUS_GRAPHICS_OPM_INVALID_INFORMATION_REQUEST cpu_to_le32(0xC01E051D) +#define STATUS_GRAPHICS_OPM_DRIVER_INTERNAL_ERROR cpu_to_le32(0xC01E051E) +#define STATUS_GRAPHICS_OPM_PROTECTED_OUTPUT_DOES_NOT_HAVE_OPM_SEMANTICS cpu_to_le32(0xC01E051F) +#define STATUS_GRAPHICS_OPM_SIGNALING_NOT_SUPPORTED cpu_to_le32(0xC01E0520) +#define STATUS_GRAPHICS_OPM_INVALID_CONFIGURATION_REQUEST cpu_to_le32(0xC01E0521) +#define STATUS_GRAPHICS_OPM_NOT_SUPPORTED cpu_to_le32(0xC01E0500) +#define STATUS_GRAPHICS_COPP_NOT_SUPPORTED cpu_to_le32(0xC01E0501) +#define STATUS_GRAPHICS_UAB_NOT_SUPPORTED cpu_to_le32(0xC01E0502) +#define STATUS_GRAPHICS_OPM_INVALID_ENCRYPTED_PARAMETERS cpu_to_le32(0xC01E0503) +#define STATUS_GRAPHICS_OPM_PARAMETER_ARRAY_TOO_SMALL cpu_to_le32(0xC01E0504) +#define STATUS_GRAPHICS_OPM_NO_PROTECTED_OUTPUTS_EXIST cpu_to_le32(0xC01E0505) +#define STATUS_GRAPHICS_PVP_NO_DISPLAY_DEVICE_CORRESPONDS_TO_NAME cpu_to_le32(0xC01E0506) +#define STATUS_GRAPHICS_PVP_DISPLAY_DEVICE_NOT_ATTACHED_TO_DESKTOP cpu_to_le32(0xC01E0507) +#define STATUS_GRAPHICS_PVP_MIRRORING_DEVICES_NOT_SUPPORTED cpu_to_le32(0xC01E0508) +#define STATUS_GRAPHICS_OPM_INVALID_POINTER cpu_to_le32(0xC01E050A) +#define STATUS_GRAPHICS_OPM_INTERNAL_ERROR cpu_to_le32(0xC01E050B) +#define STATUS_GRAPHICS_OPM_INVALID_HANDLE cpu_to_le32(0xC01E050C) +#define STATUS_GRAPHICS_PVP_NO_MONITORS_CORRESPOND_TO_DISPLAY_DEVICE cpu_to_le32(0xC01E050D) +#define STATUS_GRAPHICS_PVP_INVALID_CERTIFICATE_LENGTH cpu_to_le32(0xC01E050E) +#define STATUS_GRAPHICS_OPM_SPANNING_MODE_ENABLED cpu_to_le32(0xC01E050F) +#define STATUS_GRAPHICS_OPM_THEATER_MODE_ENABLED cpu_to_le32(0xC01E0510) +#define STATUS_GRAPHICS_PVP_HFS_FAILED cpu_to_le32(0xC01E0511) +#define STATUS_GRAPHICS_OPM_INVALID_SRM cpu_to_le32(0xC01E0512) +#define STATUS_GRAPHICS_OPM_OUTPUT_DOES_NOT_SUPPORT_HDCP cpu_to_le32(0xC01E0513) +#define STATUS_GRAPHICS_OPM_OUTPUT_DOES_NOT_SUPPORT_ACP cpu_to_le32(0xC01E0514) +#define STATUS_GRAPHICS_OPM_OUTPUT_DOES_NOT_SUPPORT_CGMSA cpu_to_le32(0xC01E0515) +#define STATUS_GRAPHICS_OPM_HDCP_SRM_NEVER_SET cpu_to_le32(0xC01E0516) +#define STATUS_GRAPHICS_OPM_RESOLUTION_TOO_HIGH cpu_to_le32(0xC01E0517) +#define STATUS_GRAPHICS_OPM_ALL_HDCP_HARDWARE_ALREADY_IN_USE cpu_to_le32(0xC01E0518) +#define STATUS_GRAPHICS_OPM_PROTECTED_OUTPUT_NO_LONGER_EXISTS cpu_to_le32(0xC01E051A) +#define STATUS_GRAPHICS_OPM_SESSION_TYPE_CHANGE_IN_PROGRESS cpu_to_le32(0xC01E051B) +#define STATUS_GRAPHICS_I2C_NOT_SUPPORTED cpu_to_le32(0xC01E0580) +#define STATUS_GRAPHICS_I2C_DEVICE_DOES_NOT_EXIST cpu_to_le32(0xC01E0581) +#define STATUS_GRAPHICS_I2C_ERROR_TRANSMITTING_DATA cpu_to_le32(0xC01E0582) +#define STATUS_GRAPHICS_I2C_ERROR_RECEIVING_DATA cpu_to_le32(0xC01E0583) +#define STATUS_GRAPHICS_DDCCI_VCP_NOT_SUPPORTED cpu_to_le32(0xC01E0584) +#define STATUS_GRAPHICS_DDCCI_INVALID_DATA cpu_to_le32(0xC01E0585) +#define STATUS_GRAPHICS_DDCCI_MONITOR_RETURNED_INVALID_TIMING_STATUS_BYTE cpu_to_le32(0xC01E0586) +#define STATUS_GRAPHICS_DDCCI_INVALID_CAPABILITIES_STRING cpu_to_le32(0xC01E0587) +#define STATUS_GRAPHICS_MCA_INTERNAL_ERROR cpu_to_le32(0xC01E0588) +#define STATUS_GRAPHICS_DDCCI_INVALID_MESSAGE_COMMAND cpu_to_le32(0xC01E0589) +#define STATUS_GRAPHICS_DDCCI_INVALID_MESSAGE_LENGTH cpu_to_le32(0xC01E058A) +#define STATUS_GRAPHICS_DDCCI_INVALID_MESSAGE_CHECKSUM cpu_to_le32(0xC01E058B) +#define STATUS_GRAPHICS_INVALID_PHYSICAL_MONITOR_HANDLE cpu_to_le32(0xC01E058C) +#define STATUS_GRAPHICS_MONITOR_NO_LONGER_EXISTS cpu_to_le32(0xC01E058D) +#define STATUS_GRAPHICS_ONLY_CONSOLE_SESSION_SUPPORTED cpu_to_le32(0xC01E05E0) +#define STATUS_GRAPHICS_NO_DISPLAY_DEVICE_CORRESPONDS_TO_NAME cpu_to_le32(0xC01E05E1) +#define STATUS_GRAPHICS_DISPLAY_DEVICE_NOT_ATTACHED_TO_DESKTOP cpu_to_le32(0xC01E05E2) +#define STATUS_GRAPHICS_MIRRORING_DEVICES_NOT_SUPPORTED cpu_to_le32(0xC01E05E3) +#define STATUS_GRAPHICS_INVALID_POINTER cpu_to_le32(0xC01E05E4) +#define STATUS_GRAPHICS_NO_MONITORS_CORRESPOND_TO_DISPLAY_DEVICE cpu_to_le32(0xC01E05E5) +#define STATUS_GRAPHICS_PARAMETER_ARRAY_TOO_SMALL cpu_to_le32(0xC01E05E6) +#define STATUS_GRAPHICS_INTERNAL_ERROR cpu_to_le32(0xC01E05E7) +#define STATUS_GRAPHICS_SESSION_TYPE_CHANGE_IN_PROGRESS cpu_to_le32(0xC01E05E8) +#define STATUS_FVE_LOCKED_VOLUME cpu_to_le32(0xC0210000) +#define STATUS_FVE_NOT_ENCRYPTED cpu_to_le32(0xC0210001) +#define STATUS_FVE_BAD_INFORMATION cpu_to_le32(0xC0210002) +#define STATUS_FVE_TOO_SMALL cpu_to_le32(0xC0210003) +#define STATUS_FVE_FAILED_WRONG_FS cpu_to_le32(0xC0210004) +#define STATUS_FVE_FAILED_BAD_FS cpu_to_le32(0xC0210005) +#define STATUS_FVE_FS_NOT_EXTENDED cpu_to_le32(0xC0210006) +#define STATUS_FVE_FS_MOUNTED cpu_to_le32(0xC0210007) +#define STATUS_FVE_NO_LICENSE cpu_to_le32(0xC0210008) +#define STATUS_FVE_ACTION_NOT_ALLOWED cpu_to_le32(0xC0210009) +#define STATUS_FVE_BAD_DATA cpu_to_le32(0xC021000A) +#define STATUS_FVE_VOLUME_NOT_BOUND cpu_to_le32(0xC021000B) +#define STATUS_FVE_NOT_DATA_VOLUME cpu_to_le32(0xC021000C) +#define STATUS_FVE_CONV_READ_ERROR cpu_to_le32(0xC021000D) +#define STATUS_FVE_CONV_WRITE_ERROR cpu_to_le32(0xC021000E) +#define STATUS_FVE_OVERLAPPED_UPDATE cpu_to_le32(0xC021000F) +#define STATUS_FVE_FAILED_SECTOR_SIZE cpu_to_le32(0xC0210010) +#define STATUS_FVE_FAILED_AUTHENTICATION cpu_to_le32(0xC0210011) +#define STATUS_FVE_NOT_OS_VOLUME cpu_to_le32(0xC0210012) +#define STATUS_FVE_KEYFILE_NOT_FOUND cpu_to_le32(0xC0210013) +#define STATUS_FVE_KEYFILE_INVALID cpu_to_le32(0xC0210014) +#define STATUS_FVE_KEYFILE_NO_VMK cpu_to_le32(0xC0210015) +#define STATUS_FVE_TPM_DISABLED cpu_to_le32(0xC0210016) +#define STATUS_FVE_TPM_SRK_AUTH_NOT_ZERO cpu_to_le32(0xC0210017) +#define STATUS_FVE_TPM_INVALID_PCR cpu_to_le32(0xC0210018) +#define STATUS_FVE_TPM_NO_VMK cpu_to_le32(0xC0210019) +#define STATUS_FVE_PIN_INVALID cpu_to_le32(0xC021001A) +#define STATUS_FVE_AUTH_INVALID_APPLICATION cpu_to_le32(0xC021001B) +#define STATUS_FVE_AUTH_INVALID_CONFIG cpu_to_le32(0xC021001C) +#define STATUS_FVE_DEBUGGER_ENABLED cpu_to_le32(0xC021001D) +#define STATUS_FVE_DRY_RUN_FAILED cpu_to_le32(0xC021001E) +#define STATUS_FVE_BAD_METADATA_POINTER cpu_to_le32(0xC021001F) +#define STATUS_FVE_OLD_METADATA_COPY cpu_to_le32(0xC0210020) +#define STATUS_FVE_REBOOT_REQUIRED cpu_to_le32(0xC0210021) +#define STATUS_FVE_RAW_ACCESS cpu_to_le32(0xC0210022) +#define STATUS_FVE_RAW_BLOCKED cpu_to_le32(0xC0210023) +#define STATUS_FWP_CALLOUT_NOT_FOUND cpu_to_le32(0xC0220001) +#define STATUS_FWP_CONDITION_NOT_FOUND cpu_to_le32(0xC0220002) +#define STATUS_FWP_FILTER_NOT_FOUND cpu_to_le32(0xC0220003) +#define STATUS_FWP_LAYER_NOT_FOUND cpu_to_le32(0xC0220004) +#define STATUS_FWP_PROVIDER_NOT_FOUND cpu_to_le32(0xC0220005) +#define STATUS_FWP_PROVIDER_CONTEXT_NOT_FOUND cpu_to_le32(0xC0220006) +#define STATUS_FWP_SUBLAYER_NOT_FOUND cpu_to_le32(0xC0220007) +#define STATUS_FWP_NOT_FOUND cpu_to_le32(0xC0220008) +#define STATUS_FWP_ALREADY_EXISTS cpu_to_le32(0xC0220009) +#define STATUS_FWP_IN_USE cpu_to_le32(0xC022000A) +#define STATUS_FWP_DYNAMIC_SESSION_IN_PROGRESS cpu_to_le32(0xC022000B) +#define STATUS_FWP_WRONG_SESSION cpu_to_le32(0xC022000C) +#define STATUS_FWP_NO_TXN_IN_PROGRESS cpu_to_le32(0xC022000D) +#define STATUS_FWP_TXN_IN_PROGRESS cpu_to_le32(0xC022000E) +#define STATUS_FWP_TXN_ABORTED cpu_to_le32(0xC022000F) +#define STATUS_FWP_SESSION_ABORTED cpu_to_le32(0xC0220010) +#define STATUS_FWP_INCOMPATIBLE_TXN cpu_to_le32(0xC0220011) +#define STATUS_FWP_TIMEOUT cpu_to_le32(0xC0220012) +#define STATUS_FWP_NET_EVENTS_DISABLED cpu_to_le32(0xC0220013) +#define STATUS_FWP_INCOMPATIBLE_LAYER cpu_to_le32(0xC0220014) +#define STATUS_FWP_KM_CLIENTS_ONLY cpu_to_le32(0xC0220015) +#define STATUS_FWP_LIFETIME_MISMATCH cpu_to_le32(0xC0220016) +#define STATUS_FWP_BUILTIN_OBJECT cpu_to_le32(0xC0220017) +#define STATUS_FWP_TOO_MANY_BOOTTIME_FILTERS cpu_to_le32(0xC0220018) +#define STATUS_FWP_TOO_MANY_CALLOUTS cpu_to_le32(0xC0220018) +#define STATUS_FWP_NOTIFICATION_DROPPED cpu_to_le32(0xC0220019) +#define STATUS_FWP_TRAFFIC_MISMATCH cpu_to_le32(0xC022001A) +#define STATUS_FWP_INCOMPATIBLE_SA_STATE cpu_to_le32(0xC022001B) +#define STATUS_FWP_NULL_POINTER cpu_to_le32(0xC022001C) +#define STATUS_FWP_INVALID_ENUMERATOR cpu_to_le32(0xC022001D) +#define STATUS_FWP_INVALID_FLAGS cpu_to_le32(0xC022001E) +#define STATUS_FWP_INVALID_NET_MASK cpu_to_le32(0xC022001F) +#define STATUS_FWP_INVALID_RANGE cpu_to_le32(0xC0220020) +#define STATUS_FWP_INVALID_INTERVAL cpu_to_le32(0xC0220021) +#define STATUS_FWP_ZERO_LENGTH_ARRAY cpu_to_le32(0xC0220022) +#define STATUS_FWP_NULL_DISPLAY_NAME cpu_to_le32(0xC0220023) +#define STATUS_FWP_INVALID_ACTION_TYPE cpu_to_le32(0xC0220024) +#define STATUS_FWP_INVALID_WEIGHT cpu_to_le32(0xC0220025) +#define STATUS_FWP_MATCH_TYPE_MISMATCH cpu_to_le32(0xC0220026) +#define STATUS_FWP_TYPE_MISMATCH cpu_to_le32(0xC0220027) +#define STATUS_FWP_OUT_OF_BOUNDS cpu_to_le32(0xC0220028) +#define STATUS_FWP_RESERVED cpu_to_le32(0xC0220029) +#define STATUS_FWP_DUPLICATE_CONDITION cpu_to_le32(0xC022002A) +#define STATUS_FWP_DUPLICATE_KEYMOD cpu_to_le32(0xC022002B) +#define STATUS_FWP_ACTION_INCOMPATIBLE_WITH_LAYER cpu_to_le32(0xC022002C) +#define STATUS_FWP_ACTION_INCOMPATIBLE_WITH_SUBLAYER cpu_to_le32(0xC022002D) +#define STATUS_FWP_CONTEXT_INCOMPATIBLE_WITH_LAYER cpu_to_le32(0xC022002E) +#define STATUS_FWP_CONTEXT_INCOMPATIBLE_WITH_CALLOUT cpu_to_le32(0xC022002F) +#define STATUS_FWP_INCOMPATIBLE_AUTH_METHOD cpu_to_le32(0xC0220030) +#define STATUS_FWP_INCOMPATIBLE_DH_GROUP cpu_to_le32(0xC0220031) +#define STATUS_FWP_EM_NOT_SUPPORTED cpu_to_le32(0xC0220032) +#define STATUS_FWP_NEVER_MATCH cpu_to_le32(0xC0220033) +#define STATUS_FWP_PROVIDER_CONTEXT_MISMATCH cpu_to_le32(0xC0220034) +#define STATUS_FWP_INVALID_PARAMETER cpu_to_le32(0xC0220035) +#define STATUS_FWP_TOO_MANY_SUBLAYERS cpu_to_le32(0xC0220036) +#define STATUS_FWP_CALLOUT_NOTIFICATION_FAILED cpu_to_le32(0xC0220037) +#define STATUS_FWP_INCOMPATIBLE_AUTH_CONFIG cpu_to_le32(0xC0220038) +#define STATUS_FWP_INCOMPATIBLE_CIPHER_CONFIG cpu_to_le32(0xC0220039) +#define STATUS_FWP_TCPIP_NOT_READY cpu_to_le32(0xC0220100) +#define STATUS_FWP_INJECT_HANDLE_CLOSING cpu_to_le32(0xC0220101) +#define STATUS_FWP_INJECT_HANDLE_STALE cpu_to_le32(0xC0220102) +#define STATUS_FWP_CANNOT_PEND cpu_to_le32(0xC0220103) +#define STATUS_NDIS_CLOSING cpu_to_le32(0xC0230002) +#define STATUS_NDIS_BAD_VERSION cpu_to_le32(0xC0230004) +#define STATUS_NDIS_BAD_CHARACTERISTICS cpu_to_le32(0xC0230005) +#define STATUS_NDIS_ADAPTER_NOT_FOUND cpu_to_le32(0xC0230006) +#define STATUS_NDIS_OPEN_FAILED cpu_to_le32(0xC0230007) +#define STATUS_NDIS_DEVICE_FAILED cpu_to_le32(0xC0230008) +#define STATUS_NDIS_MULTICAST_FULL cpu_to_le32(0xC0230009) +#define STATUS_NDIS_MULTICAST_EXISTS cpu_to_le32(0xC023000A) +#define STATUS_NDIS_MULTICAST_NOT_FOUND cpu_to_le32(0xC023000B) +#define STATUS_NDIS_REQUEST_ABORTED cpu_to_le32(0xC023000C) +#define STATUS_NDIS_RESET_IN_PROGRESS cpu_to_le32(0xC023000D) +#define STATUS_NDIS_INVALID_PACKET cpu_to_le32(0xC023000F) +#define STATUS_NDIS_INVALID_DEVICE_REQUEST cpu_to_le32(0xC0230010) +#define STATUS_NDIS_ADAPTER_NOT_READY cpu_to_le32(0xC0230011) +#define STATUS_NDIS_INVALID_LENGTH cpu_to_le32(0xC0230014) +#define STATUS_NDIS_INVALID_DATA cpu_to_le32(0xC0230015) +#define STATUS_NDIS_BUFFER_TOO_SHORT cpu_to_le32(0xC0230016) +#define STATUS_NDIS_INVALID_OID cpu_to_le32(0xC0230017) +#define STATUS_NDIS_ADAPTER_REMOVED cpu_to_le32(0xC0230018) +#define STATUS_NDIS_UNSUPPORTED_MEDIA cpu_to_le32(0xC0230019) +#define STATUS_NDIS_GROUP_ADDRESS_IN_USE cpu_to_le32(0xC023001A) +#define STATUS_NDIS_FILE_NOT_FOUND cpu_to_le32(0xC023001B) +#define STATUS_NDIS_ERROR_READING_FILE cpu_to_le32(0xC023001C) +#define STATUS_NDIS_ALREADY_MAPPED cpu_to_le32(0xC023001D) +#define STATUS_NDIS_RESOURCE_CONFLICT cpu_to_le32(0xC023001E) +#define STATUS_NDIS_MEDIA_DISCONNECTED cpu_to_le32(0xC023001F) +#define STATUS_NDIS_INVALID_ADDRESS cpu_to_le32(0xC0230022) +#define STATUS_NDIS_PAUSED cpu_to_le32(0xC023002A) +#define STATUS_NDIS_INTERFACE_NOT_FOUND cpu_to_le32(0xC023002B) +#define STATUS_NDIS_UNSUPPORTED_REVISION cpu_to_le32(0xC023002C) +#define STATUS_NDIS_INVALID_PORT cpu_to_le32(0xC023002D) +#define STATUS_NDIS_INVALID_PORT_STATE cpu_to_le32(0xC023002E) +#define STATUS_NDIS_LOW_POWER_STATE cpu_to_le32(0xC023002F) +#define STATUS_NDIS_NOT_SUPPORTED cpu_to_le32(0xC02300BB) +#define STATUS_NDIS_DOT11_AUTO_CONFIG_ENABLED cpu_to_le32(0xC0232000) +#define STATUS_NDIS_DOT11_MEDIA_IN_USE cpu_to_le32(0xC0232001) +#define STATUS_NDIS_DOT11_POWER_STATE_INVALID cpu_to_le32(0xC0232002) +#define STATUS_IPSEC_BAD_SPI cpu_to_le32(0xC0360001) +#define STATUS_IPSEC_SA_LIFETIME_EXPIRED cpu_to_le32(0xC0360002) +#define STATUS_IPSEC_WRONG_SA cpu_to_le32(0xC0360003) +#define STATUS_IPSEC_REPLAY_CHECK_FAILED cpu_to_le32(0xC0360004) +#define STATUS_IPSEC_INVALID_PACKET cpu_to_le32(0xC0360005) +#define STATUS_IPSEC_INTEGRITY_CHECK_FAILED cpu_to_le32(0xC0360006) +#define STATUS_IPSEC_CLEAR_TEXT_DROP cpu_to_le32(0xC0360007) diff --git a/fs/smb/client/smb2transport.c b/fs/smb/client/smb2transport.c new file mode 100644 index 0000000000000..790acf65a0926 --- /dev/null +++ b/fs/smb/client/smb2transport.c @@ -0,0 +1,934 @@ +// SPDX-License-Identifier: LGPL-2.1 +/* + * + * Copyright (C) International Business Machines Corp., 2002, 2011 + * Etersoft, 2012 + * Author(s): Steve French (sfrench@us.ibm.com) + * Jeremy Allison (jra@samba.org) 2006 + * Pavel Shilovsky (pshilovsky@samba.org) 2012 + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "cifsglob.h" +#include "cifsproto.h" +#include "smb2proto.h" +#include "cifs_debug.h" +#include "smb2status.h" +#include "smb2glob.h" + +static int +smb3_crypto_shash_allocate(struct TCP_Server_Info *server) +{ + struct cifs_secmech *p = &server->secmech; + int rc; + + rc = cifs_alloc_hash("hmac(sha256)", &p->hmacsha256); + if (rc) + goto err; + + rc = cifs_alloc_hash("cmac(aes)", &p->aes_cmac); + if (rc) + goto err; + + return 0; +err: + cifs_free_hash(&p->hmacsha256); + return rc; +} + +int +smb311_crypto_shash_allocate(struct TCP_Server_Info *server) +{ + struct cifs_secmech *p = &server->secmech; + int rc = 0; + + rc = cifs_alloc_hash("hmac(sha256)", &p->hmacsha256); + if (rc) + return rc; + + rc = cifs_alloc_hash("cmac(aes)", &p->aes_cmac); + if (rc) + goto err; + + rc = cifs_alloc_hash("sha512", &p->sha512); + if (rc) + goto err; + + return 0; + +err: + cifs_free_hash(&p->aes_cmac); + cifs_free_hash(&p->hmacsha256); + return rc; +} + + +static +int smb2_get_sign_key(__u64 ses_id, struct TCP_Server_Info *server, u8 *key) +{ + struct cifs_chan *chan; + struct TCP_Server_Info *pserver; + struct cifs_ses *ses = NULL; + int i; + int rc = 0; + bool is_binding = false; + + spin_lock(&cifs_tcp_ses_lock); + + /* If server is a channel, select the primary channel */ + pserver = CIFS_SERVER_IS_CHAN(server) ? server->primary_server : server; + + list_for_each_entry(ses, &pserver->smb_ses_list, smb_ses_list) { + if (ses->Suid == ses_id) + goto found; + } + cifs_server_dbg(VFS, "%s: Could not find session 0x%llx\n", + __func__, ses_id); + rc = -ENOENT; + goto out; + +found: + spin_lock(&ses->ses_lock); + spin_lock(&ses->chan_lock); + + is_binding = (cifs_chan_needs_reconnect(ses, server) && + ses->ses_status == SES_GOOD); + if (is_binding) { + /* + * If we are in the process of binding a new channel + * to an existing session, use the master connection + * session key + */ + memcpy(key, ses->smb3signingkey, SMB3_SIGN_KEY_SIZE); + spin_unlock(&ses->chan_lock); + spin_unlock(&ses->ses_lock); + goto out; + } + + /* + * Otherwise, use the channel key. + */ + + for (i = 0; i < ses->chan_count; i++) { + chan = ses->chans + i; + if (chan->server == server) { + memcpy(key, chan->signkey, SMB3_SIGN_KEY_SIZE); + spin_unlock(&ses->chan_lock); + spin_unlock(&ses->ses_lock); + goto out; + } + } + spin_unlock(&ses->chan_lock); + spin_unlock(&ses->ses_lock); + + cifs_dbg(VFS, + "%s: Could not find channel signing key for session 0x%llx\n", + __func__, ses_id); + rc = -ENOENT; + +out: + spin_unlock(&cifs_tcp_ses_lock); + return rc; +} + +static struct cifs_ses * +smb2_find_smb_ses_unlocked(struct TCP_Server_Info *server, __u64 ses_id) +{ + struct TCP_Server_Info *pserver; + struct cifs_ses *ses; + + /* If server is a channel, select the primary channel */ + pserver = CIFS_SERVER_IS_CHAN(server) ? server->primary_server : server; + + list_for_each_entry(ses, &pserver->smb_ses_list, smb_ses_list) { + if (ses->Suid != ses_id) + continue; + ++ses->ses_count; + return ses; + } + + return NULL; +} + +struct cifs_ses * +smb2_find_smb_ses(struct TCP_Server_Info *server, __u64 ses_id) +{ + struct cifs_ses *ses; + + spin_lock(&cifs_tcp_ses_lock); + ses = smb2_find_smb_ses_unlocked(server, ses_id); + spin_unlock(&cifs_tcp_ses_lock); + + return ses; +} + +static struct cifs_tcon * +smb2_find_smb_sess_tcon_unlocked(struct cifs_ses *ses, __u32 tid) +{ + struct cifs_tcon *tcon; + + list_for_each_entry(tcon, &ses->tcon_list, tcon_list) { + if (tcon->tid != tid) + continue; + ++tcon->tc_count; + return tcon; + } + + return NULL; +} + +/* + * Obtain tcon corresponding to the tid in the given + * cifs_ses + */ + +struct cifs_tcon * +smb2_find_smb_tcon(struct TCP_Server_Info *server, __u64 ses_id, __u32 tid) +{ + struct cifs_ses *ses; + struct cifs_tcon *tcon; + + spin_lock(&cifs_tcp_ses_lock); + ses = smb2_find_smb_ses_unlocked(server, ses_id); + if (!ses) { + spin_unlock(&cifs_tcp_ses_lock); + return NULL; + } + tcon = smb2_find_smb_sess_tcon_unlocked(ses, tid); + if (!tcon) { + cifs_put_smb_ses(ses); + spin_unlock(&cifs_tcp_ses_lock); + return NULL; + } + spin_unlock(&cifs_tcp_ses_lock); + /* tcon already has a ref to ses, so we don't need ses anymore */ + cifs_put_smb_ses(ses); + + return tcon; +} + +int +smb2_calc_signature(struct smb_rqst *rqst, struct TCP_Server_Info *server, + bool allocate_crypto) +{ + int rc; + unsigned char smb2_signature[SMB2_HMACSHA256_SIZE]; + unsigned char *sigptr = smb2_signature; + struct kvec *iov = rqst->rq_iov; + struct smb2_hdr *shdr = (struct smb2_hdr *)iov[0].iov_base; + struct cifs_ses *ses; + struct shash_desc *shash = NULL; + struct smb_rqst drqst; + + ses = smb2_find_smb_ses(server, le64_to_cpu(shdr->SessionId)); + if (unlikely(!ses)) { + cifs_server_dbg(VFS, "%s: Could not find session\n", __func__); + return -ENOENT; + } + + memset(smb2_signature, 0x0, SMB2_HMACSHA256_SIZE); + memset(shdr->Signature, 0x0, SMB2_SIGNATURE_SIZE); + + if (allocate_crypto) { + rc = cifs_alloc_hash("hmac(sha256)", &shash); + if (rc) { + cifs_server_dbg(VFS, + "%s: sha256 alloc failed\n", __func__); + goto out; + } + } else { + shash = server->secmech.hmacsha256; + } + + rc = crypto_shash_setkey(shash->tfm, ses->auth_key.response, + SMB2_NTLMV2_SESSKEY_SIZE); + if (rc) { + cifs_server_dbg(VFS, + "%s: Could not update with response\n", + __func__); + goto out; + } + + rc = crypto_shash_init(shash); + if (rc) { + cifs_server_dbg(VFS, "%s: Could not init sha256", __func__); + goto out; + } + + /* + * For SMB2+, __cifs_calc_signature() expects to sign only the actual + * data, that is, iov[0] should not contain a rfc1002 length. + * + * Sign the rfc1002 length prior to passing the data (iov[1-N]) down to + * __cifs_calc_signature(). + */ + drqst = *rqst; + if (drqst.rq_nvec >= 2 && iov[0].iov_len == 4) { + rc = crypto_shash_update(shash, iov[0].iov_base, + iov[0].iov_len); + if (rc) { + cifs_server_dbg(VFS, + "%s: Could not update with payload\n", + __func__); + goto out; + } + drqst.rq_iov++; + drqst.rq_nvec--; + } + + rc = __cifs_calc_signature(&drqst, server, sigptr, shash); + if (!rc) + memcpy(shdr->Signature, sigptr, SMB2_SIGNATURE_SIZE); + +out: + if (allocate_crypto) + cifs_free_hash(&shash); + if (ses) + cifs_put_smb_ses(ses); + return rc; +} + +static int generate_key(struct cifs_ses *ses, struct kvec label, + struct kvec context, __u8 *key, unsigned int key_size) +{ + unsigned char zero = 0x0; + __u8 i[4] = {0, 0, 0, 1}; + __u8 L128[4] = {0, 0, 0, 128}; + __u8 L256[4] = {0, 0, 1, 0}; + int rc = 0; + unsigned char prfhash[SMB2_HMACSHA256_SIZE]; + unsigned char *hashptr = prfhash; + struct TCP_Server_Info *server = ses->server; + + memset(prfhash, 0x0, SMB2_HMACSHA256_SIZE); + memset(key, 0x0, key_size); + + rc = smb3_crypto_shash_allocate(server); + if (rc) { + cifs_server_dbg(VFS, "%s: crypto alloc failed\n", __func__); + goto smb3signkey_ret; + } + + rc = crypto_shash_setkey(server->secmech.hmacsha256->tfm, + ses->auth_key.response, SMB2_NTLMV2_SESSKEY_SIZE); + if (rc) { + cifs_server_dbg(VFS, "%s: Could not set with session key\n", __func__); + goto smb3signkey_ret; + } + + rc = crypto_shash_init(server->secmech.hmacsha256); + if (rc) { + cifs_server_dbg(VFS, "%s: Could not init sign hmac\n", __func__); + goto smb3signkey_ret; + } + + rc = crypto_shash_update(server->secmech.hmacsha256, i, 4); + if (rc) { + cifs_server_dbg(VFS, "%s: Could not update with n\n", __func__); + goto smb3signkey_ret; + } + + rc = crypto_shash_update(server->secmech.hmacsha256, label.iov_base, label.iov_len); + if (rc) { + cifs_server_dbg(VFS, "%s: Could not update with label\n", __func__); + goto smb3signkey_ret; + } + + rc = crypto_shash_update(server->secmech.hmacsha256, &zero, 1); + if (rc) { + cifs_server_dbg(VFS, "%s: Could not update with zero\n", __func__); + goto smb3signkey_ret; + } + + rc = crypto_shash_update(server->secmech.hmacsha256, context.iov_base, context.iov_len); + if (rc) { + cifs_server_dbg(VFS, "%s: Could not update with context\n", __func__); + goto smb3signkey_ret; + } + + if ((server->cipher_type == SMB2_ENCRYPTION_AES256_CCM) || + (server->cipher_type == SMB2_ENCRYPTION_AES256_GCM)) { + rc = crypto_shash_update(server->secmech.hmacsha256, L256, 4); + } else { + rc = crypto_shash_update(server->secmech.hmacsha256, L128, 4); + } + if (rc) { + cifs_server_dbg(VFS, "%s: Could not update with L\n", __func__); + goto smb3signkey_ret; + } + + rc = crypto_shash_final(server->secmech.hmacsha256, hashptr); + if (rc) { + cifs_server_dbg(VFS, "%s: Could not generate sha256 hash\n", __func__); + goto smb3signkey_ret; + } + + memcpy(key, hashptr, key_size); + +smb3signkey_ret: + return rc; +} + +struct derivation { + struct kvec label; + struct kvec context; +}; + +struct derivation_triplet { + struct derivation signing; + struct derivation encryption; + struct derivation decryption; +}; + +static int +generate_smb3signingkey(struct cifs_ses *ses, + struct TCP_Server_Info *server, + const struct derivation_triplet *ptriplet) +{ + int rc; + bool is_binding = false; + int chan_index = 0; + + spin_lock(&ses->ses_lock); + spin_lock(&ses->chan_lock); + is_binding = (cifs_chan_needs_reconnect(ses, server) && + ses->ses_status == SES_GOOD); + + chan_index = cifs_ses_get_chan_index(ses, server); + /* TODO: introduce ref counting for channels when the can be freed */ + spin_unlock(&ses->chan_lock); + spin_unlock(&ses->ses_lock); + + /* + * All channels use the same encryption/decryption keys but + * they have their own signing key. + * + * When we generate the keys, check if it is for a new channel + * (binding) in which case we only need to generate a signing + * key and store it in the channel as to not overwrite the + * master connection signing key stored in the session + */ + + if (is_binding) { + rc = generate_key(ses, ptriplet->signing.label, + ptriplet->signing.context, + ses->chans[chan_index].signkey, + SMB3_SIGN_KEY_SIZE); + if (rc) + return rc; + } else { + rc = generate_key(ses, ptriplet->signing.label, + ptriplet->signing.context, + ses->smb3signingkey, + SMB3_SIGN_KEY_SIZE); + if (rc) + return rc; + + /* safe to access primary channel, since it will never go away */ + spin_lock(&ses->chan_lock); + memcpy(ses->chans[chan_index].signkey, ses->smb3signingkey, + SMB3_SIGN_KEY_SIZE); + spin_unlock(&ses->chan_lock); + + rc = generate_key(ses, ptriplet->encryption.label, + ptriplet->encryption.context, + ses->smb3encryptionkey, + SMB3_ENC_DEC_KEY_SIZE); + rc = generate_key(ses, ptriplet->decryption.label, + ptriplet->decryption.context, + ses->smb3decryptionkey, + SMB3_ENC_DEC_KEY_SIZE); + if (rc) + return rc; + } + + if (rc) + return rc; + +#ifdef CONFIG_CIFS_DEBUG_DUMP_KEYS + cifs_dbg(VFS, "%s: dumping generated AES session keys\n", __func__); + /* + * The session id is opaque in terms of endianness, so we can't + * print it as a long long. we dump it as we got it on the wire + */ + cifs_dbg(VFS, "Session Id %*ph\n", (int)sizeof(ses->Suid), + &ses->Suid); + cifs_dbg(VFS, "Cipher type %d\n", server->cipher_type); + cifs_dbg(VFS, "Session Key %*ph\n", + SMB2_NTLMV2_SESSKEY_SIZE, ses->auth_key.response); + cifs_dbg(VFS, "Signing Key %*ph\n", + SMB3_SIGN_KEY_SIZE, ses->smb3signingkey); + if ((server->cipher_type == SMB2_ENCRYPTION_AES256_CCM) || + (server->cipher_type == SMB2_ENCRYPTION_AES256_GCM)) { + cifs_dbg(VFS, "ServerIn Key %*ph\n", + SMB3_GCM256_CRYPTKEY_SIZE, ses->smb3encryptionkey); + cifs_dbg(VFS, "ServerOut Key %*ph\n", + SMB3_GCM256_CRYPTKEY_SIZE, ses->smb3decryptionkey); + } else { + cifs_dbg(VFS, "ServerIn Key %*ph\n", + SMB3_GCM128_CRYPTKEY_SIZE, ses->smb3encryptionkey); + cifs_dbg(VFS, "ServerOut Key %*ph\n", + SMB3_GCM128_CRYPTKEY_SIZE, ses->smb3decryptionkey); + } +#endif + return rc; +} + +int +generate_smb30signingkey(struct cifs_ses *ses, + struct TCP_Server_Info *server) + +{ + struct derivation_triplet triplet; + struct derivation *d; + + d = &triplet.signing; + d->label.iov_base = "SMB2AESCMAC"; + d->label.iov_len = 12; + d->context.iov_base = "SmbSign"; + d->context.iov_len = 8; + + d = &triplet.encryption; + d->label.iov_base = "SMB2AESCCM"; + d->label.iov_len = 11; + d->context.iov_base = "ServerIn "; + d->context.iov_len = 10; + + d = &triplet.decryption; + d->label.iov_base = "SMB2AESCCM"; + d->label.iov_len = 11; + d->context.iov_base = "ServerOut"; + d->context.iov_len = 10; + + return generate_smb3signingkey(ses, server, &triplet); +} + +int +generate_smb311signingkey(struct cifs_ses *ses, + struct TCP_Server_Info *server) + +{ + struct derivation_triplet triplet; + struct derivation *d; + + d = &triplet.signing; + d->label.iov_base = "SMBSigningKey"; + d->label.iov_len = 14; + d->context.iov_base = ses->preauth_sha_hash; + d->context.iov_len = 64; + + d = &triplet.encryption; + d->label.iov_base = "SMBC2SCipherKey"; + d->label.iov_len = 16; + d->context.iov_base = ses->preauth_sha_hash; + d->context.iov_len = 64; + + d = &triplet.decryption; + d->label.iov_base = "SMBS2CCipherKey"; + d->label.iov_len = 16; + d->context.iov_base = ses->preauth_sha_hash; + d->context.iov_len = 64; + + return generate_smb3signingkey(ses, server, &triplet); +} + +int +smb3_calc_signature(struct smb_rqst *rqst, struct TCP_Server_Info *server, + bool allocate_crypto) +{ + int rc; + unsigned char smb3_signature[SMB2_CMACAES_SIZE]; + unsigned char *sigptr = smb3_signature; + struct kvec *iov = rqst->rq_iov; + struct smb2_hdr *shdr = (struct smb2_hdr *)iov[0].iov_base; + struct shash_desc *shash = NULL; + struct smb_rqst drqst; + u8 key[SMB3_SIGN_KEY_SIZE]; + + rc = smb2_get_sign_key(le64_to_cpu(shdr->SessionId), server, key); + if (unlikely(rc)) { + cifs_server_dbg(VFS, "%s: Could not get signing key\n", __func__); + return rc; + } + + if (allocate_crypto) { + rc = cifs_alloc_hash("cmac(aes)", &shash); + if (rc) + return rc; + } else { + shash = server->secmech.aes_cmac; + } + + memset(smb3_signature, 0x0, SMB2_CMACAES_SIZE); + memset(shdr->Signature, 0x0, SMB2_SIGNATURE_SIZE); + + rc = crypto_shash_setkey(shash->tfm, key, SMB2_CMACAES_SIZE); + if (rc) { + cifs_server_dbg(VFS, "%s: Could not set key for cmac aes\n", __func__); + goto out; + } + + /* + * we already allocate aes_cmac when we init smb3 signing key, + * so unlike smb2 case we do not have to check here if secmech are + * initialized + */ + rc = crypto_shash_init(shash); + if (rc) { + cifs_server_dbg(VFS, "%s: Could not init cmac aes\n", __func__); + goto out; + } + + /* + * For SMB2+, __cifs_calc_signature() expects to sign only the actual + * data, that is, iov[0] should not contain a rfc1002 length. + * + * Sign the rfc1002 length prior to passing the data (iov[1-N]) down to + * __cifs_calc_signature(). + */ + drqst = *rqst; + if (drqst.rq_nvec >= 2 && iov[0].iov_len == 4) { + rc = crypto_shash_update(shash, iov[0].iov_base, + iov[0].iov_len); + if (rc) { + cifs_server_dbg(VFS, "%s: Could not update with payload\n", + __func__); + goto out; + } + drqst.rq_iov++; + drqst.rq_nvec--; + } + + rc = __cifs_calc_signature(&drqst, server, sigptr, shash); + if (!rc) + memcpy(shdr->Signature, sigptr, SMB2_SIGNATURE_SIZE); + +out: + if (allocate_crypto) + cifs_free_hash(&shash); + return rc; +} + +/* must be called with server->srv_mutex held */ +static int +smb2_sign_rqst(struct smb_rqst *rqst, struct TCP_Server_Info *server) +{ + int rc = 0; + struct smb2_hdr *shdr; + struct smb2_sess_setup_req *ssr; + bool is_binding; + bool is_signed; + + shdr = (struct smb2_hdr *)rqst->rq_iov[0].iov_base; + ssr = (struct smb2_sess_setup_req *)shdr; + + is_binding = shdr->Command == SMB2_SESSION_SETUP && + (ssr->Flags & SMB2_SESSION_REQ_FLAG_BINDING); + is_signed = shdr->Flags & SMB2_FLAGS_SIGNED; + + if (!is_signed) + return 0; + spin_lock(&server->srv_lock); + if (server->ops->need_neg && + server->ops->need_neg(server)) { + spin_unlock(&server->srv_lock); + return 0; + } + spin_unlock(&server->srv_lock); + if (!is_binding && !server->session_estab) { + strncpy(shdr->Signature, "BSRSPYL", 8); + return 0; + } + + rc = server->ops->calc_signature(rqst, server, false); + + return rc; +} + +int +smb2_verify_signature(struct smb_rqst *rqst, struct TCP_Server_Info *server) +{ + unsigned int rc; + char server_response_sig[SMB2_SIGNATURE_SIZE]; + struct smb2_hdr *shdr = + (struct smb2_hdr *)rqst->rq_iov[0].iov_base; + + if ((shdr->Command == SMB2_NEGOTIATE) || + (shdr->Command == SMB2_SESSION_SETUP) || + (shdr->Command == SMB2_OPLOCK_BREAK) || + server->ignore_signature || + (!server->session_estab)) + return 0; + + /* + * BB what if signatures are supposed to be on for session but + * server does not send one? BB + */ + + /* Do not need to verify session setups with signature "BSRSPYL " */ + if (memcmp(shdr->Signature, "BSRSPYL ", 8) == 0) + cifs_dbg(FYI, "dummy signature received for smb command 0x%x\n", + shdr->Command); + + /* + * Save off the origiginal signature so we can modify the smb and check + * our calculated signature against what the server sent. + */ + memcpy(server_response_sig, shdr->Signature, SMB2_SIGNATURE_SIZE); + + memset(shdr->Signature, 0, SMB2_SIGNATURE_SIZE); + + rc = server->ops->calc_signature(rqst, server, true); + + if (rc) + return rc; + + if (memcmp(server_response_sig, shdr->Signature, SMB2_SIGNATURE_SIZE)) { + cifs_dbg(VFS, "sign fail cmd 0x%x message id 0x%llx\n", + shdr->Command, shdr->MessageId); + return -EACCES; + } else + return 0; +} + +/* + * Set message id for the request. Should be called after wait_for_free_request + * and when srv_mutex is held. + */ +static inline void +smb2_seq_num_into_buf(struct TCP_Server_Info *server, + struct smb2_hdr *shdr) +{ + unsigned int i, num = le16_to_cpu(shdr->CreditCharge); + + shdr->MessageId = get_next_mid64(server); + /* skip message numbers according to CreditCharge field */ + for (i = 1; i < num; i++) + get_next_mid(server); +} + +static struct mid_q_entry * +smb2_mid_entry_alloc(const struct smb2_hdr *shdr, + struct TCP_Server_Info *server) +{ + struct mid_q_entry *temp; + unsigned int credits = le16_to_cpu(shdr->CreditCharge); + + if (server == NULL) { + cifs_dbg(VFS, "Null TCP session in smb2_mid_entry_alloc\n"); + return NULL; + } + + temp = mempool_alloc(cifs_mid_poolp, GFP_NOFS); + memset(temp, 0, sizeof(struct mid_q_entry)); + kref_init(&temp->refcount); + temp->mid = le64_to_cpu(shdr->MessageId); + temp->credits = credits > 0 ? credits : 1; + temp->pid = current->pid; + temp->command = shdr->Command; /* Always LE */ + temp->when_alloc = jiffies; + temp->server = server; + + /* + * The default is for the mid to be synchronous, so the + * default callback just wakes up the current task. + */ + get_task_struct(current); + temp->creator = current; + temp->callback = cifs_wake_up_task; + temp->callback_data = current; + + atomic_inc(&mid_count); + temp->mid_state = MID_REQUEST_ALLOCATED; + trace_smb3_cmd_enter(le32_to_cpu(shdr->Id.SyncId.TreeId), + le64_to_cpu(shdr->SessionId), + le16_to_cpu(shdr->Command), temp->mid); + return temp; +} + +static int +smb2_get_mid_entry(struct cifs_ses *ses, struct TCP_Server_Info *server, + struct smb2_hdr *shdr, struct mid_q_entry **mid) +{ + spin_lock(&server->srv_lock); + if (server->tcpStatus == CifsExiting) { + spin_unlock(&server->srv_lock); + return -ENOENT; + } + + if (server->tcpStatus == CifsNeedReconnect) { + spin_unlock(&server->srv_lock); + cifs_dbg(FYI, "tcp session dead - return to caller to retry\n"); + return -EAGAIN; + } + + if (server->tcpStatus == CifsNeedNegotiate && + shdr->Command != SMB2_NEGOTIATE) { + spin_unlock(&server->srv_lock); + return -EAGAIN; + } + spin_unlock(&server->srv_lock); + + spin_lock(&ses->ses_lock); + if (ses->ses_status == SES_NEW) { + if ((shdr->Command != SMB2_SESSION_SETUP) && + (shdr->Command != SMB2_NEGOTIATE)) { + spin_unlock(&ses->ses_lock); + return -EAGAIN; + } + /* else ok - we are setting up session */ + } + + if (ses->ses_status == SES_EXITING) { + if (shdr->Command != SMB2_LOGOFF) { + spin_unlock(&ses->ses_lock); + return -EAGAIN; + } + /* else ok - we are shutting down the session */ + } + spin_unlock(&ses->ses_lock); + + *mid = smb2_mid_entry_alloc(shdr, server); + if (*mid == NULL) + return -ENOMEM; + spin_lock(&server->mid_lock); + list_add_tail(&(*mid)->qhead, &server->pending_mid_q); + spin_unlock(&server->mid_lock); + + return 0; +} + +int +smb2_check_receive(struct mid_q_entry *mid, struct TCP_Server_Info *server, + bool log_error) +{ + unsigned int len = mid->resp_buf_size; + struct kvec iov[1]; + struct smb_rqst rqst = { .rq_iov = iov, + .rq_nvec = 1 }; + + iov[0].iov_base = (char *)mid->resp_buf; + iov[0].iov_len = len; + + dump_smb(mid->resp_buf, min_t(u32, 80, len)); + /* convert the length into a more usable form */ + if (len > 24 && server->sign && !mid->decrypted) { + int rc; + + rc = smb2_verify_signature(&rqst, server); + if (rc) + cifs_server_dbg(VFS, "SMB signature verification returned error = %d\n", + rc); + } + + return map_smb2_to_linux_error(mid->resp_buf, log_error); +} + +struct mid_q_entry * +smb2_setup_request(struct cifs_ses *ses, struct TCP_Server_Info *server, + struct smb_rqst *rqst) +{ + int rc; + struct smb2_hdr *shdr = + (struct smb2_hdr *)rqst->rq_iov[0].iov_base; + struct mid_q_entry *mid; + + smb2_seq_num_into_buf(server, shdr); + + rc = smb2_get_mid_entry(ses, server, shdr, &mid); + if (rc) { + revert_current_mid_from_hdr(server, shdr); + return ERR_PTR(rc); + } + + rc = smb2_sign_rqst(rqst, server); + if (rc) { + revert_current_mid_from_hdr(server, shdr); + delete_mid(mid); + return ERR_PTR(rc); + } + + return mid; +} + +struct mid_q_entry * +smb2_setup_async_request(struct TCP_Server_Info *server, struct smb_rqst *rqst) +{ + int rc; + struct smb2_hdr *shdr = + (struct smb2_hdr *)rqst->rq_iov[0].iov_base; + struct mid_q_entry *mid; + + spin_lock(&server->srv_lock); + if (server->tcpStatus == CifsNeedNegotiate && + shdr->Command != SMB2_NEGOTIATE) { + spin_unlock(&server->srv_lock); + return ERR_PTR(-EAGAIN); + } + spin_unlock(&server->srv_lock); + + smb2_seq_num_into_buf(server, shdr); + + mid = smb2_mid_entry_alloc(shdr, server); + if (mid == NULL) { + revert_current_mid_from_hdr(server, shdr); + return ERR_PTR(-ENOMEM); + } + + rc = smb2_sign_rqst(rqst, server); + if (rc) { + revert_current_mid_from_hdr(server, shdr); + release_mid(mid); + return ERR_PTR(rc); + } + + return mid; +} + +int +smb3_crypto_aead_allocate(struct TCP_Server_Info *server) +{ + struct crypto_aead *tfm; + + if (!server->secmech.enc) { + if ((server->cipher_type == SMB2_ENCRYPTION_AES128_GCM) || + (server->cipher_type == SMB2_ENCRYPTION_AES256_GCM)) + tfm = crypto_alloc_aead("gcm(aes)", 0, 0); + else + tfm = crypto_alloc_aead("ccm(aes)", 0, 0); + if (IS_ERR(tfm)) { + cifs_server_dbg(VFS, "%s: Failed alloc encrypt aead\n", + __func__); + return PTR_ERR(tfm); + } + server->secmech.enc = tfm; + } + + if (!server->secmech.dec) { + if ((server->cipher_type == SMB2_ENCRYPTION_AES128_GCM) || + (server->cipher_type == SMB2_ENCRYPTION_AES256_GCM)) + tfm = crypto_alloc_aead("gcm(aes)", 0, 0); + else + tfm = crypto_alloc_aead("ccm(aes)", 0, 0); + if (IS_ERR(tfm)) { + crypto_free_aead(server->secmech.enc); + server->secmech.enc = NULL; + cifs_server_dbg(VFS, "%s: Failed to alloc decrypt aead\n", + __func__); + return PTR_ERR(tfm); + } + server->secmech.dec = tfm; + } + + return 0; +} diff --git a/fs/smb/client/smbdirect.c b/fs/smb/client/smbdirect.c new file mode 100644 index 0000000000000..cf923f211c512 --- /dev/null +++ b/fs/smb/client/smbdirect.c @@ -0,0 +1,2494 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Copyright (C) 2017, Microsoft Corporation. + * + * Author(s): Long Li + */ +#include +#include +#include "smbdirect.h" +#include "cifs_debug.h" +#include "cifsproto.h" +#include "smb2proto.h" + +static struct smbd_response *get_empty_queue_buffer( + struct smbd_connection *info); +static struct smbd_response *get_receive_buffer( + struct smbd_connection *info); +static void put_receive_buffer( + struct smbd_connection *info, + struct smbd_response *response); +static int allocate_receive_buffers(struct smbd_connection *info, int num_buf); +static void destroy_receive_buffers(struct smbd_connection *info); + +static void put_empty_packet( + struct smbd_connection *info, struct smbd_response *response); +static void enqueue_reassembly( + struct smbd_connection *info, + struct smbd_response *response, int data_length); +static struct smbd_response *_get_first_reassembly( + struct smbd_connection *info); + +static int smbd_post_recv( + struct smbd_connection *info, + struct smbd_response *response); + +static int smbd_post_send_empty(struct smbd_connection *info); +static int smbd_post_send_data( + struct smbd_connection *info, + struct kvec *iov, int n_vec, int remaining_data_length); +static int smbd_post_send_page(struct smbd_connection *info, + struct page *page, unsigned long offset, + size_t size, int remaining_data_length); + +static void destroy_mr_list(struct smbd_connection *info); +static int allocate_mr_list(struct smbd_connection *info); + +/* SMBD version number */ +#define SMBD_V1 0x0100 + +/* Port numbers for SMBD transport */ +#define SMB_PORT 445 +#define SMBD_PORT 5445 + +/* Address lookup and resolve timeout in ms */ +#define RDMA_RESOLVE_TIMEOUT 5000 + +/* SMBD negotiation timeout in seconds */ +#define SMBD_NEGOTIATE_TIMEOUT 120 + +/* SMBD minimum receive size and fragmented sized defined in [MS-SMBD] */ +#define SMBD_MIN_RECEIVE_SIZE 128 +#define SMBD_MIN_FRAGMENTED_SIZE 131072 + +/* + * Default maximum number of RDMA read/write outstanding on this connection + * This value is possibly decreased during QP creation on hardware limit + */ +#define SMBD_CM_RESPONDER_RESOURCES 32 + +/* Maximum number of retries on data transfer operations */ +#define SMBD_CM_RETRY 6 +/* No need to retry on Receiver Not Ready since SMBD manages credits */ +#define SMBD_CM_RNR_RETRY 0 + +/* + * User configurable initial values per SMBD transport connection + * as defined in [MS-SMBD] 3.1.1.1 + * Those may change after a SMBD negotiation + */ +/* The local peer's maximum number of credits to grant to the peer */ +int smbd_receive_credit_max = 255; + +/* The remote peer's credit request of local peer */ +int smbd_send_credit_target = 255; + +/* The maximum single message size can be sent to remote peer */ +int smbd_max_send_size = 1364; + +/* The maximum fragmented upper-layer payload receive size supported */ +int smbd_max_fragmented_recv_size = 1024 * 1024; + +/* The maximum single-message size which can be received */ +int smbd_max_receive_size = 1364; + +/* The timeout to initiate send of a keepalive message on idle */ +int smbd_keep_alive_interval = 120; + +/* + * User configurable initial values for RDMA transport + * The actual values used may be lower and are limited to hardware capabilities + */ +/* Default maximum number of pages in a single RDMA write/read */ +int smbd_max_frmr_depth = 2048; + +/* If payload is less than this byte, use RDMA send/recv not read/write */ +int rdma_readwrite_threshold = 4096; + +/* Transport logging functions + * Logging are defined as classes. They can be OR'ed to define the actual + * logging level via module parameter smbd_logging_class + * e.g. cifs.smbd_logging_class=0xa0 will log all log_rdma_recv() and + * log_rdma_event() + */ +#define LOG_OUTGOING 0x1 +#define LOG_INCOMING 0x2 +#define LOG_READ 0x4 +#define LOG_WRITE 0x8 +#define LOG_RDMA_SEND 0x10 +#define LOG_RDMA_RECV 0x20 +#define LOG_KEEP_ALIVE 0x40 +#define LOG_RDMA_EVENT 0x80 +#define LOG_RDMA_MR 0x100 +static unsigned int smbd_logging_class; +module_param(smbd_logging_class, uint, 0644); +MODULE_PARM_DESC(smbd_logging_class, + "Logging class for SMBD transport 0x0 to 0x100"); + +#define ERR 0x0 +#define INFO 0x1 +static unsigned int smbd_logging_level = ERR; +module_param(smbd_logging_level, uint, 0644); +MODULE_PARM_DESC(smbd_logging_level, + "Logging level for SMBD transport, 0 (default): error, 1: info"); + +#define log_rdma(level, class, fmt, args...) \ +do { \ + if (level <= smbd_logging_level || class & smbd_logging_class) \ + cifs_dbg(VFS, "%s:%d " fmt, __func__, __LINE__, ##args);\ +} while (0) + +#define log_outgoing(level, fmt, args...) \ + log_rdma(level, LOG_OUTGOING, fmt, ##args) +#define log_incoming(level, fmt, args...) \ + log_rdma(level, LOG_INCOMING, fmt, ##args) +#define log_read(level, fmt, args...) log_rdma(level, LOG_READ, fmt, ##args) +#define log_write(level, fmt, args...) log_rdma(level, LOG_WRITE, fmt, ##args) +#define log_rdma_send(level, fmt, args...) \ + log_rdma(level, LOG_RDMA_SEND, fmt, ##args) +#define log_rdma_recv(level, fmt, args...) \ + log_rdma(level, LOG_RDMA_RECV, fmt, ##args) +#define log_keep_alive(level, fmt, args...) \ + log_rdma(level, LOG_KEEP_ALIVE, fmt, ##args) +#define log_rdma_event(level, fmt, args...) \ + log_rdma(level, LOG_RDMA_EVENT, fmt, ##args) +#define log_rdma_mr(level, fmt, args...) \ + log_rdma(level, LOG_RDMA_MR, fmt, ##args) + +static void smbd_disconnect_rdma_work(struct work_struct *work) +{ + struct smbd_connection *info = + container_of(work, struct smbd_connection, disconnect_work); + + if (info->transport_status == SMBD_CONNECTED) { + info->transport_status = SMBD_DISCONNECTING; + rdma_disconnect(info->id); + } +} + +static void smbd_disconnect_rdma_connection(struct smbd_connection *info) +{ + queue_work(info->workqueue, &info->disconnect_work); +} + +/* Upcall from RDMA CM */ +static int smbd_conn_upcall( + struct rdma_cm_id *id, struct rdma_cm_event *event) +{ + struct smbd_connection *info = id->context; + + log_rdma_event(INFO, "event=%d status=%d\n", + event->event, event->status); + + switch (event->event) { + case RDMA_CM_EVENT_ADDR_RESOLVED: + case RDMA_CM_EVENT_ROUTE_RESOLVED: + info->ri_rc = 0; + complete(&info->ri_done); + break; + + case RDMA_CM_EVENT_ADDR_ERROR: + info->ri_rc = -EHOSTUNREACH; + complete(&info->ri_done); + break; + + case RDMA_CM_EVENT_ROUTE_ERROR: + info->ri_rc = -ENETUNREACH; + complete(&info->ri_done); + break; + + case RDMA_CM_EVENT_ESTABLISHED: + log_rdma_event(INFO, "connected event=%d\n", event->event); + info->transport_status = SMBD_CONNECTED; + wake_up_interruptible(&info->conn_wait); + break; + + case RDMA_CM_EVENT_CONNECT_ERROR: + case RDMA_CM_EVENT_UNREACHABLE: + case RDMA_CM_EVENT_REJECTED: + log_rdma_event(INFO, "connecting failed event=%d\n", event->event); + info->transport_status = SMBD_DISCONNECTED; + wake_up_interruptible(&info->conn_wait); + break; + + case RDMA_CM_EVENT_DEVICE_REMOVAL: + case RDMA_CM_EVENT_DISCONNECTED: + /* This happenes when we fail the negotiation */ + if (info->transport_status == SMBD_NEGOTIATE_FAILED) { + info->transport_status = SMBD_DISCONNECTED; + wake_up(&info->conn_wait); + break; + } + + info->transport_status = SMBD_DISCONNECTED; + wake_up_interruptible(&info->disconn_wait); + wake_up_interruptible(&info->wait_reassembly_queue); + wake_up_interruptible_all(&info->wait_send_queue); + break; + + default: + break; + } + + return 0; +} + +/* Upcall from RDMA QP */ +static void +smbd_qp_async_error_upcall(struct ib_event *event, void *context) +{ + struct smbd_connection *info = context; + + log_rdma_event(ERR, "%s on device %s info %p\n", + ib_event_msg(event->event), event->device->name, info); + + switch (event->event) { + case IB_EVENT_CQ_ERR: + case IB_EVENT_QP_FATAL: + smbd_disconnect_rdma_connection(info); + break; + + default: + break; + } +} + +static inline void *smbd_request_payload(struct smbd_request *request) +{ + return (void *)request->packet; +} + +static inline void *smbd_response_payload(struct smbd_response *response) +{ + return (void *)response->packet; +} + +/* Called when a RDMA send is done */ +static void send_done(struct ib_cq *cq, struct ib_wc *wc) +{ + int i; + struct smbd_request *request = + container_of(wc->wr_cqe, struct smbd_request, cqe); + + log_rdma_send(INFO, "smbd_request 0x%p completed wc->status=%d\n", + request, wc->status); + + if (wc->status != IB_WC_SUCCESS || wc->opcode != IB_WC_SEND) { + log_rdma_send(ERR, "wc->status=%d wc->opcode=%d\n", + wc->status, wc->opcode); + smbd_disconnect_rdma_connection(request->info); + } + + for (i = 0; i < request->num_sge; i++) + ib_dma_unmap_single(request->info->id->device, + request->sge[i].addr, + request->sge[i].length, + DMA_TO_DEVICE); + + if (atomic_dec_and_test(&request->info->send_pending)) + wake_up(&request->info->wait_send_pending); + + wake_up(&request->info->wait_post_send); + + mempool_free(request, request->info->request_mempool); +} + +static void dump_smbd_negotiate_resp(struct smbd_negotiate_resp *resp) +{ + log_rdma_event(INFO, "resp message min_version %u max_version %u negotiated_version %u credits_requested %u credits_granted %u status %u max_readwrite_size %u preferred_send_size %u max_receive_size %u max_fragmented_size %u\n", + resp->min_version, resp->max_version, + resp->negotiated_version, resp->credits_requested, + resp->credits_granted, resp->status, + resp->max_readwrite_size, resp->preferred_send_size, + resp->max_receive_size, resp->max_fragmented_size); +} + +/* + * Process a negotiation response message, according to [MS-SMBD]3.1.5.7 + * response, packet_length: the negotiation response message + * return value: true if negotiation is a success, false if failed + */ +static bool process_negotiation_response( + struct smbd_response *response, int packet_length) +{ + struct smbd_connection *info = response->info; + struct smbd_negotiate_resp *packet = smbd_response_payload(response); + + if (packet_length < sizeof(struct smbd_negotiate_resp)) { + log_rdma_event(ERR, + "error: packet_length=%d\n", packet_length); + return false; + } + + if (le16_to_cpu(packet->negotiated_version) != SMBD_V1) { + log_rdma_event(ERR, "error: negotiated_version=%x\n", + le16_to_cpu(packet->negotiated_version)); + return false; + } + info->protocol = le16_to_cpu(packet->negotiated_version); + + if (packet->credits_requested == 0) { + log_rdma_event(ERR, "error: credits_requested==0\n"); + return false; + } + info->receive_credit_target = le16_to_cpu(packet->credits_requested); + + if (packet->credits_granted == 0) { + log_rdma_event(ERR, "error: credits_granted==0\n"); + return false; + } + atomic_set(&info->send_credits, le16_to_cpu(packet->credits_granted)); + + atomic_set(&info->receive_credits, 0); + + if (le32_to_cpu(packet->preferred_send_size) > info->max_receive_size) { + log_rdma_event(ERR, "error: preferred_send_size=%d\n", + le32_to_cpu(packet->preferred_send_size)); + return false; + } + info->max_receive_size = le32_to_cpu(packet->preferred_send_size); + + if (le32_to_cpu(packet->max_receive_size) < SMBD_MIN_RECEIVE_SIZE) { + log_rdma_event(ERR, "error: max_receive_size=%d\n", + le32_to_cpu(packet->max_receive_size)); + return false; + } + info->max_send_size = min_t(int, info->max_send_size, + le32_to_cpu(packet->max_receive_size)); + + if (le32_to_cpu(packet->max_fragmented_size) < + SMBD_MIN_FRAGMENTED_SIZE) { + log_rdma_event(ERR, "error: max_fragmented_size=%d\n", + le32_to_cpu(packet->max_fragmented_size)); + return false; + } + info->max_fragmented_send_size = + le32_to_cpu(packet->max_fragmented_size); + info->rdma_readwrite_threshold = + rdma_readwrite_threshold > info->max_fragmented_send_size ? + info->max_fragmented_send_size : + rdma_readwrite_threshold; + + + info->max_readwrite_size = min_t(u32, + le32_to_cpu(packet->max_readwrite_size), + info->max_frmr_depth * PAGE_SIZE); + info->max_frmr_depth = info->max_readwrite_size / PAGE_SIZE; + + return true; +} + +static void smbd_post_send_credits(struct work_struct *work) +{ + int ret = 0; + int use_receive_queue = 1; + int rc; + struct smbd_response *response; + struct smbd_connection *info = + container_of(work, struct smbd_connection, + post_send_credits_work); + + if (info->transport_status != SMBD_CONNECTED) { + wake_up(&info->wait_receive_queues); + return; + } + + if (info->receive_credit_target > + atomic_read(&info->receive_credits)) { + while (true) { + if (use_receive_queue) + response = get_receive_buffer(info); + else + response = get_empty_queue_buffer(info); + if (!response) { + /* now switch to emtpy packet queue */ + if (use_receive_queue) { + use_receive_queue = 0; + continue; + } else + break; + } + + response->type = SMBD_TRANSFER_DATA; + response->first_segment = false; + rc = smbd_post_recv(info, response); + if (rc) { + log_rdma_recv(ERR, + "post_recv failed rc=%d\n", rc); + put_receive_buffer(info, response); + break; + } + + ret++; + } + } + + spin_lock(&info->lock_new_credits_offered); + info->new_credits_offered += ret; + spin_unlock(&info->lock_new_credits_offered); + + /* Promptly send an immediate packet as defined in [MS-SMBD] 3.1.1.1 */ + info->send_immediate = true; + if (atomic_read(&info->receive_credits) < + info->receive_credit_target - 1) { + if (info->keep_alive_requested == KEEP_ALIVE_PENDING || + info->send_immediate) { + log_keep_alive(INFO, "send an empty message\n"); + smbd_post_send_empty(info); + } + } +} + +/* Called from softirq, when recv is done */ +static void recv_done(struct ib_cq *cq, struct ib_wc *wc) +{ + struct smbd_data_transfer *data_transfer; + struct smbd_response *response = + container_of(wc->wr_cqe, struct smbd_response, cqe); + struct smbd_connection *info = response->info; + int data_length = 0; + + log_rdma_recv(INFO, "response=0x%p type=%d wc status=%d wc opcode %d byte_len=%d pkey_index=%u\n", + response, response->type, wc->status, wc->opcode, + wc->byte_len, wc->pkey_index); + + if (wc->status != IB_WC_SUCCESS || wc->opcode != IB_WC_RECV) { + log_rdma_recv(INFO, "wc->status=%d opcode=%d\n", + wc->status, wc->opcode); + smbd_disconnect_rdma_connection(info); + goto error; + } + + ib_dma_sync_single_for_cpu( + wc->qp->device, + response->sge.addr, + response->sge.length, + DMA_FROM_DEVICE); + + switch (response->type) { + /* SMBD negotiation response */ + case SMBD_NEGOTIATE_RESP: + dump_smbd_negotiate_resp(smbd_response_payload(response)); + info->full_packet_received = true; + info->negotiate_done = + process_negotiation_response(response, wc->byte_len); + complete(&info->negotiate_completion); + break; + + /* SMBD data transfer packet */ + case SMBD_TRANSFER_DATA: + data_transfer = smbd_response_payload(response); + data_length = le32_to_cpu(data_transfer->data_length); + + /* + * If this is a packet with data playload place the data in + * reassembly queue and wake up the reading thread + */ + if (data_length) { + if (info->full_packet_received) + response->first_segment = true; + + if (le32_to_cpu(data_transfer->remaining_data_length)) + info->full_packet_received = false; + else + info->full_packet_received = true; + + enqueue_reassembly( + info, + response, + data_length); + } else + put_empty_packet(info, response); + + if (data_length) + wake_up_interruptible(&info->wait_reassembly_queue); + + atomic_dec(&info->receive_credits); + info->receive_credit_target = + le16_to_cpu(data_transfer->credits_requested); + if (le16_to_cpu(data_transfer->credits_granted)) { + atomic_add(le16_to_cpu(data_transfer->credits_granted), + &info->send_credits); + /* + * We have new send credits granted from remote peer + * If any sender is waiting for credits, unblock it + */ + wake_up_interruptible(&info->wait_send_queue); + } + + log_incoming(INFO, "data flags %d data_offset %d data_length %d remaining_data_length %d\n", + le16_to_cpu(data_transfer->flags), + le32_to_cpu(data_transfer->data_offset), + le32_to_cpu(data_transfer->data_length), + le32_to_cpu(data_transfer->remaining_data_length)); + + /* Send a KEEP_ALIVE response right away if requested */ + info->keep_alive_requested = KEEP_ALIVE_NONE; + if (le16_to_cpu(data_transfer->flags) & + SMB_DIRECT_RESPONSE_REQUESTED) { + info->keep_alive_requested = KEEP_ALIVE_PENDING; + } + + return; + + default: + log_rdma_recv(ERR, + "unexpected response type=%d\n", response->type); + } + +error: + put_receive_buffer(info, response); +} + +static struct rdma_cm_id *smbd_create_id( + struct smbd_connection *info, + struct sockaddr *dstaddr, int port) +{ + struct rdma_cm_id *id; + int rc; + __be16 *sport; + + id = rdma_create_id(&init_net, smbd_conn_upcall, info, + RDMA_PS_TCP, IB_QPT_RC); + if (IS_ERR(id)) { + rc = PTR_ERR(id); + log_rdma_event(ERR, "rdma_create_id() failed %i\n", rc); + return id; + } + + if (dstaddr->sa_family == AF_INET6) + sport = &((struct sockaddr_in6 *)dstaddr)->sin6_port; + else + sport = &((struct sockaddr_in *)dstaddr)->sin_port; + + *sport = htons(port); + + init_completion(&info->ri_done); + info->ri_rc = -ETIMEDOUT; + + rc = rdma_resolve_addr(id, NULL, (struct sockaddr *)dstaddr, + RDMA_RESOLVE_TIMEOUT); + if (rc) { + log_rdma_event(ERR, "rdma_resolve_addr() failed %i\n", rc); + goto out; + } + rc = wait_for_completion_interruptible_timeout( + &info->ri_done, msecs_to_jiffies(RDMA_RESOLVE_TIMEOUT)); + /* e.g. if interrupted returns -ERESTARTSYS */ + if (rc < 0) { + log_rdma_event(ERR, "rdma_resolve_addr timeout rc: %i\n", rc); + goto out; + } + rc = info->ri_rc; + if (rc) { + log_rdma_event(ERR, "rdma_resolve_addr() completed %i\n", rc); + goto out; + } + + info->ri_rc = -ETIMEDOUT; + rc = rdma_resolve_route(id, RDMA_RESOLVE_TIMEOUT); + if (rc) { + log_rdma_event(ERR, "rdma_resolve_route() failed %i\n", rc); + goto out; + } + rc = wait_for_completion_interruptible_timeout( + &info->ri_done, msecs_to_jiffies(RDMA_RESOLVE_TIMEOUT)); + /* e.g. if interrupted returns -ERESTARTSYS */ + if (rc < 0) { + log_rdma_event(ERR, "rdma_resolve_addr timeout rc: %i\n", rc); + goto out; + } + rc = info->ri_rc; + if (rc) { + log_rdma_event(ERR, "rdma_resolve_route() completed %i\n", rc); + goto out; + } + + return id; + +out: + rdma_destroy_id(id); + return ERR_PTR(rc); +} + +/* + * Test if FRWR (Fast Registration Work Requests) is supported on the device + * This implementation requries FRWR on RDMA read/write + * return value: true if it is supported + */ +static bool frwr_is_supported(struct ib_device_attr *attrs) +{ + if (!(attrs->device_cap_flags & IB_DEVICE_MEM_MGT_EXTENSIONS)) + return false; + if (attrs->max_fast_reg_page_list_len == 0) + return false; + return true; +} + +static int smbd_ia_open( + struct smbd_connection *info, + struct sockaddr *dstaddr, int port) +{ + int rc; + + info->id = smbd_create_id(info, dstaddr, port); + if (IS_ERR(info->id)) { + rc = PTR_ERR(info->id); + goto out1; + } + + if (!frwr_is_supported(&info->id->device->attrs)) { + log_rdma_event(ERR, "Fast Registration Work Requests (FRWR) is not supported\n"); + log_rdma_event(ERR, "Device capability flags = %llx max_fast_reg_page_list_len = %u\n", + info->id->device->attrs.device_cap_flags, + info->id->device->attrs.max_fast_reg_page_list_len); + rc = -EPROTONOSUPPORT; + goto out2; + } + info->max_frmr_depth = min_t(int, + smbd_max_frmr_depth, + info->id->device->attrs.max_fast_reg_page_list_len); + info->mr_type = IB_MR_TYPE_MEM_REG; + if (info->id->device->attrs.kernel_cap_flags & IBK_SG_GAPS_REG) + info->mr_type = IB_MR_TYPE_SG_GAPS; + + info->pd = ib_alloc_pd(info->id->device, 0); + if (IS_ERR(info->pd)) { + rc = PTR_ERR(info->pd); + log_rdma_event(ERR, "ib_alloc_pd() returned %d\n", rc); + goto out2; + } + + return 0; + +out2: + rdma_destroy_id(info->id); + info->id = NULL; + +out1: + return rc; +} + +/* + * Send a negotiation request message to the peer + * The negotiation procedure is in [MS-SMBD] 3.1.5.2 and 3.1.5.3 + * After negotiation, the transport is connected and ready for + * carrying upper layer SMB payload + */ +static int smbd_post_send_negotiate_req(struct smbd_connection *info) +{ + struct ib_send_wr send_wr; + int rc = -ENOMEM; + struct smbd_request *request; + struct smbd_negotiate_req *packet; + + request = mempool_alloc(info->request_mempool, GFP_KERNEL); + if (!request) + return rc; + + request->info = info; + + packet = smbd_request_payload(request); + packet->min_version = cpu_to_le16(SMBD_V1); + packet->max_version = cpu_to_le16(SMBD_V1); + packet->reserved = 0; + packet->credits_requested = cpu_to_le16(info->send_credit_target); + packet->preferred_send_size = cpu_to_le32(info->max_send_size); + packet->max_receive_size = cpu_to_le32(info->max_receive_size); + packet->max_fragmented_size = + cpu_to_le32(info->max_fragmented_recv_size); + + request->num_sge = 1; + request->sge[0].addr = ib_dma_map_single( + info->id->device, (void *)packet, + sizeof(*packet), DMA_TO_DEVICE); + if (ib_dma_mapping_error(info->id->device, request->sge[0].addr)) { + rc = -EIO; + goto dma_mapping_failed; + } + + request->sge[0].length = sizeof(*packet); + request->sge[0].lkey = info->pd->local_dma_lkey; + + ib_dma_sync_single_for_device( + info->id->device, request->sge[0].addr, + request->sge[0].length, DMA_TO_DEVICE); + + request->cqe.done = send_done; + + send_wr.next = NULL; + send_wr.wr_cqe = &request->cqe; + send_wr.sg_list = request->sge; + send_wr.num_sge = request->num_sge; + send_wr.opcode = IB_WR_SEND; + send_wr.send_flags = IB_SEND_SIGNALED; + + log_rdma_send(INFO, "sge addr=0x%llx length=%u lkey=0x%x\n", + request->sge[0].addr, + request->sge[0].length, request->sge[0].lkey); + + atomic_inc(&info->send_pending); + rc = ib_post_send(info->id->qp, &send_wr, NULL); + if (!rc) + return 0; + + /* if we reach here, post send failed */ + log_rdma_send(ERR, "ib_post_send failed rc=%d\n", rc); + atomic_dec(&info->send_pending); + ib_dma_unmap_single(info->id->device, request->sge[0].addr, + request->sge[0].length, DMA_TO_DEVICE); + + smbd_disconnect_rdma_connection(info); + +dma_mapping_failed: + mempool_free(request, info->request_mempool); + return rc; +} + +/* + * Extend the credits to remote peer + * This implements [MS-SMBD] 3.1.5.9 + * The idea is that we should extend credits to remote peer as quickly as + * it's allowed, to maintain data flow. We allocate as much receive + * buffer as possible, and extend the receive credits to remote peer + * return value: the new credtis being granted. + */ +static int manage_credits_prior_sending(struct smbd_connection *info) +{ + int new_credits; + + spin_lock(&info->lock_new_credits_offered); + new_credits = info->new_credits_offered; + info->new_credits_offered = 0; + spin_unlock(&info->lock_new_credits_offered); + + return new_credits; +} + +/* + * Check if we need to send a KEEP_ALIVE message + * The idle connection timer triggers a KEEP_ALIVE message when expires + * SMB_DIRECT_RESPONSE_REQUESTED is set in the message flag to have peer send + * back a response. + * return value: + * 1 if SMB_DIRECT_RESPONSE_REQUESTED needs to be set + * 0: otherwise + */ +static int manage_keep_alive_before_sending(struct smbd_connection *info) +{ + if (info->keep_alive_requested == KEEP_ALIVE_PENDING) { + info->keep_alive_requested = KEEP_ALIVE_SENT; + return 1; + } + return 0; +} + +/* Post the send request */ +static int smbd_post_send(struct smbd_connection *info, + struct smbd_request *request) +{ + struct ib_send_wr send_wr; + int rc, i; + + for (i = 0; i < request->num_sge; i++) { + log_rdma_send(INFO, + "rdma_request sge[%d] addr=0x%llx length=%u\n", + i, request->sge[i].addr, request->sge[i].length); + ib_dma_sync_single_for_device( + info->id->device, + request->sge[i].addr, + request->sge[i].length, + DMA_TO_DEVICE); + } + + request->cqe.done = send_done; + + send_wr.next = NULL; + send_wr.wr_cqe = &request->cqe; + send_wr.sg_list = request->sge; + send_wr.num_sge = request->num_sge; + send_wr.opcode = IB_WR_SEND; + send_wr.send_flags = IB_SEND_SIGNALED; + + rc = ib_post_send(info->id->qp, &send_wr, NULL); + if (rc) { + log_rdma_send(ERR, "ib_post_send failed rc=%d\n", rc); + smbd_disconnect_rdma_connection(info); + rc = -EAGAIN; + } else + /* Reset timer for idle connection after packet is sent */ + mod_delayed_work(info->workqueue, &info->idle_timer_work, + info->keep_alive_interval*HZ); + + return rc; +} + +static int smbd_post_send_sgl(struct smbd_connection *info, + struct scatterlist *sgl, int data_length, int remaining_data_length) +{ + int num_sgs; + int i, rc; + int header_length; + struct smbd_request *request; + struct smbd_data_transfer *packet; + int new_credits; + struct scatterlist *sg; + +wait_credit: + /* Wait for send credits. A SMBD packet needs one credit */ + rc = wait_event_interruptible(info->wait_send_queue, + atomic_read(&info->send_credits) > 0 || + info->transport_status != SMBD_CONNECTED); + if (rc) + goto err_wait_credit; + + if (info->transport_status != SMBD_CONNECTED) { + log_outgoing(ERR, "disconnected not sending on wait_credit\n"); + rc = -EAGAIN; + goto err_wait_credit; + } + if (unlikely(atomic_dec_return(&info->send_credits) < 0)) { + atomic_inc(&info->send_credits); + goto wait_credit; + } + +wait_send_queue: + wait_event(info->wait_post_send, + atomic_read(&info->send_pending) < info->send_credit_target || + info->transport_status != SMBD_CONNECTED); + + if (info->transport_status != SMBD_CONNECTED) { + log_outgoing(ERR, "disconnected not sending on wait_send_queue\n"); + rc = -EAGAIN; + goto err_wait_send_queue; + } + + if (unlikely(atomic_inc_return(&info->send_pending) > + info->send_credit_target)) { + atomic_dec(&info->send_pending); + goto wait_send_queue; + } + + request = mempool_alloc(info->request_mempool, GFP_KERNEL); + if (!request) { + rc = -ENOMEM; + goto err_alloc; + } + + request->info = info; + + /* Fill in the packet header */ + packet = smbd_request_payload(request); + packet->credits_requested = cpu_to_le16(info->send_credit_target); + + new_credits = manage_credits_prior_sending(info); + atomic_add(new_credits, &info->receive_credits); + packet->credits_granted = cpu_to_le16(new_credits); + + info->send_immediate = false; + + packet->flags = 0; + if (manage_keep_alive_before_sending(info)) + packet->flags |= cpu_to_le16(SMB_DIRECT_RESPONSE_REQUESTED); + + packet->reserved = 0; + if (!data_length) + packet->data_offset = 0; + else + packet->data_offset = cpu_to_le32(24); + packet->data_length = cpu_to_le32(data_length); + packet->remaining_data_length = cpu_to_le32(remaining_data_length); + packet->padding = 0; + + log_outgoing(INFO, "credits_requested=%d credits_granted=%d data_offset=%d data_length=%d remaining_data_length=%d\n", + le16_to_cpu(packet->credits_requested), + le16_to_cpu(packet->credits_granted), + le32_to_cpu(packet->data_offset), + le32_to_cpu(packet->data_length), + le32_to_cpu(packet->remaining_data_length)); + + /* Map the packet to DMA */ + header_length = sizeof(struct smbd_data_transfer); + /* If this is a packet without payload, don't send padding */ + if (!data_length) + header_length = offsetof(struct smbd_data_transfer, padding); + + request->num_sge = 1; + request->sge[0].addr = ib_dma_map_single(info->id->device, + (void *)packet, + header_length, + DMA_TO_DEVICE); + if (ib_dma_mapping_error(info->id->device, request->sge[0].addr)) { + rc = -EIO; + request->sge[0].addr = 0; + goto err_dma; + } + + request->sge[0].length = header_length; + request->sge[0].lkey = info->pd->local_dma_lkey; + + /* Fill in the packet data payload */ + num_sgs = sgl ? sg_nents(sgl) : 0; + for_each_sg(sgl, sg, num_sgs, i) { + request->sge[i+1].addr = + ib_dma_map_page(info->id->device, sg_page(sg), + sg->offset, sg->length, DMA_TO_DEVICE); + if (ib_dma_mapping_error( + info->id->device, request->sge[i+1].addr)) { + rc = -EIO; + request->sge[i+1].addr = 0; + goto err_dma; + } + request->sge[i+1].length = sg->length; + request->sge[i+1].lkey = info->pd->local_dma_lkey; + request->num_sge++; + } + + rc = smbd_post_send(info, request); + if (!rc) + return 0; + +err_dma: + for (i = 0; i < request->num_sge; i++) + if (request->sge[i].addr) + ib_dma_unmap_single(info->id->device, + request->sge[i].addr, + request->sge[i].length, + DMA_TO_DEVICE); + mempool_free(request, info->request_mempool); + + /* roll back receive credits and credits to be offered */ + spin_lock(&info->lock_new_credits_offered); + info->new_credits_offered += new_credits; + spin_unlock(&info->lock_new_credits_offered); + atomic_sub(new_credits, &info->receive_credits); + +err_alloc: + if (atomic_dec_and_test(&info->send_pending)) + wake_up(&info->wait_send_pending); + +err_wait_send_queue: + /* roll back send credits and pending */ + atomic_inc(&info->send_credits); + +err_wait_credit: + return rc; +} + +/* + * Send a page + * page: the page to send + * offset: offset in the page to send + * size: length in the page to send + * remaining_data_length: remaining data to send in this payload + */ +static int smbd_post_send_page(struct smbd_connection *info, struct page *page, + unsigned long offset, size_t size, int remaining_data_length) +{ + struct scatterlist sgl; + + sg_init_table(&sgl, 1); + sg_set_page(&sgl, page, size, offset); + + return smbd_post_send_sgl(info, &sgl, size, remaining_data_length); +} + +/* + * Send an empty message + * Empty message is used to extend credits to peer to for keep live + * while there is no upper layer payload to send at the time + */ +static int smbd_post_send_empty(struct smbd_connection *info) +{ + info->count_send_empty++; + return smbd_post_send_sgl(info, NULL, 0, 0); +} + +/* + * Send a data buffer + * iov: the iov array describing the data buffers + * n_vec: number of iov array + * remaining_data_length: remaining data to send following this packet + * in segmented SMBD packet + */ +static int smbd_post_send_data( + struct smbd_connection *info, struct kvec *iov, int n_vec, + int remaining_data_length) +{ + int i; + u32 data_length = 0; + struct scatterlist sgl[SMBDIRECT_MAX_SEND_SGE - 1]; + + if (n_vec > SMBDIRECT_MAX_SEND_SGE - 1) { + cifs_dbg(VFS, "Can't fit data to SGL, n_vec=%d\n", n_vec); + return -EINVAL; + } + + sg_init_table(sgl, n_vec); + for (i = 0; i < n_vec; i++) { + data_length += iov[i].iov_len; + sg_set_buf(&sgl[i], iov[i].iov_base, iov[i].iov_len); + } + + return smbd_post_send_sgl(info, sgl, data_length, remaining_data_length); +} + +/* + * Post a receive request to the transport + * The remote peer can only send data when a receive request is posted + * The interaction is controlled by send/receive credit system + */ +static int smbd_post_recv( + struct smbd_connection *info, struct smbd_response *response) +{ + struct ib_recv_wr recv_wr; + int rc = -EIO; + + response->sge.addr = ib_dma_map_single( + info->id->device, response->packet, + info->max_receive_size, DMA_FROM_DEVICE); + if (ib_dma_mapping_error(info->id->device, response->sge.addr)) + return rc; + + response->sge.length = info->max_receive_size; + response->sge.lkey = info->pd->local_dma_lkey; + + response->cqe.done = recv_done; + + recv_wr.wr_cqe = &response->cqe; + recv_wr.next = NULL; + recv_wr.sg_list = &response->sge; + recv_wr.num_sge = 1; + + rc = ib_post_recv(info->id->qp, &recv_wr, NULL); + if (rc) { + ib_dma_unmap_single(info->id->device, response->sge.addr, + response->sge.length, DMA_FROM_DEVICE); + smbd_disconnect_rdma_connection(info); + log_rdma_recv(ERR, "ib_post_recv failed rc=%d\n", rc); + } + + return rc; +} + +/* Perform SMBD negotiate according to [MS-SMBD] 3.1.5.2 */ +static int smbd_negotiate(struct smbd_connection *info) +{ + int rc; + struct smbd_response *response = get_receive_buffer(info); + + response->type = SMBD_NEGOTIATE_RESP; + rc = smbd_post_recv(info, response); + log_rdma_event(INFO, "smbd_post_recv rc=%d iov.addr=0x%llx iov.length=%u iov.lkey=0x%x\n", + rc, response->sge.addr, + response->sge.length, response->sge.lkey); + if (rc) + return rc; + + init_completion(&info->negotiate_completion); + info->negotiate_done = false; + rc = smbd_post_send_negotiate_req(info); + if (rc) + return rc; + + rc = wait_for_completion_interruptible_timeout( + &info->negotiate_completion, SMBD_NEGOTIATE_TIMEOUT * HZ); + log_rdma_event(INFO, "wait_for_completion_timeout rc=%d\n", rc); + + if (info->negotiate_done) + return 0; + + if (rc == 0) + rc = -ETIMEDOUT; + else if (rc == -ERESTARTSYS) + rc = -EINTR; + else + rc = -ENOTCONN; + + return rc; +} + +static void put_empty_packet( + struct smbd_connection *info, struct smbd_response *response) +{ + spin_lock(&info->empty_packet_queue_lock); + list_add_tail(&response->list, &info->empty_packet_queue); + info->count_empty_packet_queue++; + spin_unlock(&info->empty_packet_queue_lock); + + queue_work(info->workqueue, &info->post_send_credits_work); +} + +/* + * Implement Connection.FragmentReassemblyBuffer defined in [MS-SMBD] 3.1.1.1 + * This is a queue for reassembling upper layer payload and present to upper + * layer. All the inncoming payload go to the reassembly queue, regardless of + * if reassembly is required. The uuper layer code reads from the queue for all + * incoming payloads. + * Put a received packet to the reassembly queue + * response: the packet received + * data_length: the size of payload in this packet + */ +static void enqueue_reassembly( + struct smbd_connection *info, + struct smbd_response *response, + int data_length) +{ + spin_lock(&info->reassembly_queue_lock); + list_add_tail(&response->list, &info->reassembly_queue); + info->reassembly_queue_length++; + /* + * Make sure reassembly_data_length is updated after list and + * reassembly_queue_length are updated. On the dequeue side + * reassembly_data_length is checked without a lock to determine + * if reassembly_queue_length and list is up to date + */ + virt_wmb(); + info->reassembly_data_length += data_length; + spin_unlock(&info->reassembly_queue_lock); + info->count_reassembly_queue++; + info->count_enqueue_reassembly_queue++; +} + +/* + * Get the first entry at the front of reassembly queue + * Caller is responsible for locking + * return value: the first entry if any, NULL if queue is empty + */ +static struct smbd_response *_get_first_reassembly(struct smbd_connection *info) +{ + struct smbd_response *ret = NULL; + + if (!list_empty(&info->reassembly_queue)) { + ret = list_first_entry( + &info->reassembly_queue, + struct smbd_response, list); + } + return ret; +} + +static struct smbd_response *get_empty_queue_buffer( + struct smbd_connection *info) +{ + struct smbd_response *ret = NULL; + unsigned long flags; + + spin_lock_irqsave(&info->empty_packet_queue_lock, flags); + if (!list_empty(&info->empty_packet_queue)) { + ret = list_first_entry( + &info->empty_packet_queue, + struct smbd_response, list); + list_del(&ret->list); + info->count_empty_packet_queue--; + } + spin_unlock_irqrestore(&info->empty_packet_queue_lock, flags); + + return ret; +} + +/* + * Get a receive buffer + * For each remote send, we need to post a receive. The receive buffers are + * pre-allocated in advance. + * return value: the receive buffer, NULL if none is available + */ +static struct smbd_response *get_receive_buffer(struct smbd_connection *info) +{ + struct smbd_response *ret = NULL; + unsigned long flags; + + spin_lock_irqsave(&info->receive_queue_lock, flags); + if (!list_empty(&info->receive_queue)) { + ret = list_first_entry( + &info->receive_queue, + struct smbd_response, list); + list_del(&ret->list); + info->count_receive_queue--; + info->count_get_receive_buffer++; + } + spin_unlock_irqrestore(&info->receive_queue_lock, flags); + + return ret; +} + +/* + * Return a receive buffer + * Upon returning of a receive buffer, we can post new receive and extend + * more receive credits to remote peer. This is done immediately after a + * receive buffer is returned. + */ +static void put_receive_buffer( + struct smbd_connection *info, struct smbd_response *response) +{ + unsigned long flags; + + ib_dma_unmap_single(info->id->device, response->sge.addr, + response->sge.length, DMA_FROM_DEVICE); + + spin_lock_irqsave(&info->receive_queue_lock, flags); + list_add_tail(&response->list, &info->receive_queue); + info->count_receive_queue++; + info->count_put_receive_buffer++; + spin_unlock_irqrestore(&info->receive_queue_lock, flags); + + queue_work(info->workqueue, &info->post_send_credits_work); +} + +/* Preallocate all receive buffer on transport establishment */ +static int allocate_receive_buffers(struct smbd_connection *info, int num_buf) +{ + int i; + struct smbd_response *response; + + INIT_LIST_HEAD(&info->reassembly_queue); + spin_lock_init(&info->reassembly_queue_lock); + info->reassembly_data_length = 0; + info->reassembly_queue_length = 0; + + INIT_LIST_HEAD(&info->receive_queue); + spin_lock_init(&info->receive_queue_lock); + info->count_receive_queue = 0; + + INIT_LIST_HEAD(&info->empty_packet_queue); + spin_lock_init(&info->empty_packet_queue_lock); + info->count_empty_packet_queue = 0; + + init_waitqueue_head(&info->wait_receive_queues); + + for (i = 0; i < num_buf; i++) { + response = mempool_alloc(info->response_mempool, GFP_KERNEL); + if (!response) + goto allocate_failed; + + response->info = info; + list_add_tail(&response->list, &info->receive_queue); + info->count_receive_queue++; + } + + return 0; + +allocate_failed: + while (!list_empty(&info->receive_queue)) { + response = list_first_entry( + &info->receive_queue, + struct smbd_response, list); + list_del(&response->list); + info->count_receive_queue--; + + mempool_free(response, info->response_mempool); + } + return -ENOMEM; +} + +static void destroy_receive_buffers(struct smbd_connection *info) +{ + struct smbd_response *response; + + while ((response = get_receive_buffer(info))) + mempool_free(response, info->response_mempool); + + while ((response = get_empty_queue_buffer(info))) + mempool_free(response, info->response_mempool); +} + +/* Implement idle connection timer [MS-SMBD] 3.1.6.2 */ +static void idle_connection_timer(struct work_struct *work) +{ + struct smbd_connection *info = container_of( + work, struct smbd_connection, + idle_timer_work.work); + + if (info->keep_alive_requested != KEEP_ALIVE_NONE) { + log_keep_alive(ERR, + "error status info->keep_alive_requested=%d\n", + info->keep_alive_requested); + smbd_disconnect_rdma_connection(info); + return; + } + + log_keep_alive(INFO, "about to send an empty idle message\n"); + smbd_post_send_empty(info); + + /* Setup the next idle timeout work */ + queue_delayed_work(info->workqueue, &info->idle_timer_work, + info->keep_alive_interval*HZ); +} + +/* + * Destroy the transport and related RDMA and memory resources + * Need to go through all the pending counters and make sure on one is using + * the transport while it is destroyed + */ +void smbd_destroy(struct TCP_Server_Info *server) +{ + struct smbd_connection *info = server->smbd_conn; + struct smbd_response *response; + unsigned long flags; + + if (!info) { + log_rdma_event(INFO, "rdma session already destroyed\n"); + return; + } + + log_rdma_event(INFO, "destroying rdma session\n"); + if (info->transport_status != SMBD_DISCONNECTED) { + rdma_disconnect(server->smbd_conn->id); + log_rdma_event(INFO, "wait for transport being disconnected\n"); + wait_event_interruptible( + info->disconn_wait, + info->transport_status == SMBD_DISCONNECTED); + } + + log_rdma_event(INFO, "destroying qp\n"); + ib_drain_qp(info->id->qp); + rdma_destroy_qp(info->id); + + log_rdma_event(INFO, "cancelling idle timer\n"); + cancel_delayed_work_sync(&info->idle_timer_work); + + log_rdma_event(INFO, "wait for all send posted to IB to finish\n"); + wait_event(info->wait_send_pending, + atomic_read(&info->send_pending) == 0); + + /* It's not possible for upper layer to get to reassembly */ + log_rdma_event(INFO, "drain the reassembly queue\n"); + do { + spin_lock_irqsave(&info->reassembly_queue_lock, flags); + response = _get_first_reassembly(info); + if (response) { + list_del(&response->list); + spin_unlock_irqrestore( + &info->reassembly_queue_lock, flags); + put_receive_buffer(info, response); + } else + spin_unlock_irqrestore( + &info->reassembly_queue_lock, flags); + } while (response); + info->reassembly_data_length = 0; + + log_rdma_event(INFO, "free receive buffers\n"); + wait_event(info->wait_receive_queues, + info->count_receive_queue + info->count_empty_packet_queue + == info->receive_credit_max); + destroy_receive_buffers(info); + + /* + * For performance reasons, memory registration and deregistration + * are not locked by srv_mutex. It is possible some processes are + * blocked on transport srv_mutex while holding memory registration. + * Release the transport srv_mutex to allow them to hit the failure + * path when sending data, and then release memory registartions. + */ + log_rdma_event(INFO, "freeing mr list\n"); + wake_up_interruptible_all(&info->wait_mr); + while (atomic_read(&info->mr_used_count)) { + cifs_server_unlock(server); + msleep(1000); + cifs_server_lock(server); + } + destroy_mr_list(info); + + ib_free_cq(info->send_cq); + ib_free_cq(info->recv_cq); + ib_dealloc_pd(info->pd); + rdma_destroy_id(info->id); + + /* free mempools */ + mempool_destroy(info->request_mempool); + kmem_cache_destroy(info->request_cache); + + mempool_destroy(info->response_mempool); + kmem_cache_destroy(info->response_cache); + + info->transport_status = SMBD_DESTROYED; + + destroy_workqueue(info->workqueue); + log_rdma_event(INFO, "rdma session destroyed\n"); + kfree(info); + server->smbd_conn = NULL; +} + +/* + * Reconnect this SMBD connection, called from upper layer + * return value: 0 on success, or actual error code + */ +int smbd_reconnect(struct TCP_Server_Info *server) +{ + log_rdma_event(INFO, "reconnecting rdma session\n"); + + if (!server->smbd_conn) { + log_rdma_event(INFO, "rdma session already destroyed\n"); + goto create_conn; + } + + /* + * This is possible if transport is disconnected and we haven't received + * notification from RDMA, but upper layer has detected timeout + */ + if (server->smbd_conn->transport_status == SMBD_CONNECTED) { + log_rdma_event(INFO, "disconnecting transport\n"); + smbd_destroy(server); + } + +create_conn: + log_rdma_event(INFO, "creating rdma session\n"); + server->smbd_conn = smbd_get_connection( + server, (struct sockaddr *) &server->dstaddr); + + if (server->smbd_conn) + cifs_dbg(VFS, "RDMA transport re-established\n"); + + return server->smbd_conn ? 0 : -ENOENT; +} + +static void destroy_caches_and_workqueue(struct smbd_connection *info) +{ + destroy_receive_buffers(info); + destroy_workqueue(info->workqueue); + mempool_destroy(info->response_mempool); + kmem_cache_destroy(info->response_cache); + mempool_destroy(info->request_mempool); + kmem_cache_destroy(info->request_cache); +} + +#define MAX_NAME_LEN 80 +static int allocate_caches_and_workqueue(struct smbd_connection *info) +{ + char name[MAX_NAME_LEN]; + int rc; + + scnprintf(name, MAX_NAME_LEN, "smbd_request_%p", info); + info->request_cache = + kmem_cache_create( + name, + sizeof(struct smbd_request) + + sizeof(struct smbd_data_transfer), + 0, SLAB_HWCACHE_ALIGN, NULL); + if (!info->request_cache) + return -ENOMEM; + + info->request_mempool = + mempool_create(info->send_credit_target, mempool_alloc_slab, + mempool_free_slab, info->request_cache); + if (!info->request_mempool) + goto out1; + + scnprintf(name, MAX_NAME_LEN, "smbd_response_%p", info); + info->response_cache = + kmem_cache_create( + name, + sizeof(struct smbd_response) + + info->max_receive_size, + 0, SLAB_HWCACHE_ALIGN, NULL); + if (!info->response_cache) + goto out2; + + info->response_mempool = + mempool_create(info->receive_credit_max, mempool_alloc_slab, + mempool_free_slab, info->response_cache); + if (!info->response_mempool) + goto out3; + + scnprintf(name, MAX_NAME_LEN, "smbd_%p", info); + info->workqueue = create_workqueue(name); + if (!info->workqueue) + goto out4; + + rc = allocate_receive_buffers(info, info->receive_credit_max); + if (rc) { + log_rdma_event(ERR, "failed to allocate receive buffers\n"); + goto out5; + } + + return 0; + +out5: + destroy_workqueue(info->workqueue); +out4: + mempool_destroy(info->response_mempool); +out3: + kmem_cache_destroy(info->response_cache); +out2: + mempool_destroy(info->request_mempool); +out1: + kmem_cache_destroy(info->request_cache); + return -ENOMEM; +} + +/* Create a SMBD connection, called by upper layer */ +static struct smbd_connection *_smbd_get_connection( + struct TCP_Server_Info *server, struct sockaddr *dstaddr, int port) +{ + int rc; + struct smbd_connection *info; + struct rdma_conn_param conn_param; + struct ib_qp_init_attr qp_attr; + struct sockaddr_in *addr_in = (struct sockaddr_in *) dstaddr; + struct ib_port_immutable port_immutable; + u32 ird_ord_hdr[2]; + + info = kzalloc(sizeof(struct smbd_connection), GFP_KERNEL); + if (!info) + return NULL; + + info->transport_status = SMBD_CONNECTING; + rc = smbd_ia_open(info, dstaddr, port); + if (rc) { + log_rdma_event(INFO, "smbd_ia_open rc=%d\n", rc); + goto create_id_failed; + } + + if (smbd_send_credit_target > info->id->device->attrs.max_cqe || + smbd_send_credit_target > info->id->device->attrs.max_qp_wr) { + log_rdma_event(ERR, "consider lowering send_credit_target = %d. Possible CQE overrun, device reporting max_cqe %d max_qp_wr %d\n", + smbd_send_credit_target, + info->id->device->attrs.max_cqe, + info->id->device->attrs.max_qp_wr); + goto config_failed; + } + + if (smbd_receive_credit_max > info->id->device->attrs.max_cqe || + smbd_receive_credit_max > info->id->device->attrs.max_qp_wr) { + log_rdma_event(ERR, "consider lowering receive_credit_max = %d. Possible CQE overrun, device reporting max_cqe %d max_qp_wr %d\n", + smbd_receive_credit_max, + info->id->device->attrs.max_cqe, + info->id->device->attrs.max_qp_wr); + goto config_failed; + } + + info->receive_credit_max = smbd_receive_credit_max; + info->send_credit_target = smbd_send_credit_target; + info->max_send_size = smbd_max_send_size; + info->max_fragmented_recv_size = smbd_max_fragmented_recv_size; + info->max_receive_size = smbd_max_receive_size; + info->keep_alive_interval = smbd_keep_alive_interval; + + if (info->id->device->attrs.max_send_sge < SMBDIRECT_MAX_SEND_SGE || + info->id->device->attrs.max_recv_sge < SMBDIRECT_MAX_RECV_SGE) { + log_rdma_event(ERR, + "device %.*s max_send_sge/max_recv_sge = %d/%d too small\n", + IB_DEVICE_NAME_MAX, + info->id->device->name, + info->id->device->attrs.max_send_sge, + info->id->device->attrs.max_recv_sge); + goto config_failed; + } + + info->send_cq = NULL; + info->recv_cq = NULL; + info->send_cq = + ib_alloc_cq_any(info->id->device, info, + info->send_credit_target, IB_POLL_SOFTIRQ); + if (IS_ERR(info->send_cq)) { + info->send_cq = NULL; + goto alloc_cq_failed; + } + + info->recv_cq = + ib_alloc_cq_any(info->id->device, info, + info->receive_credit_max, IB_POLL_SOFTIRQ); + if (IS_ERR(info->recv_cq)) { + info->recv_cq = NULL; + goto alloc_cq_failed; + } + + memset(&qp_attr, 0, sizeof(qp_attr)); + qp_attr.event_handler = smbd_qp_async_error_upcall; + qp_attr.qp_context = info; + qp_attr.cap.max_send_wr = info->send_credit_target; + qp_attr.cap.max_recv_wr = info->receive_credit_max; + qp_attr.cap.max_send_sge = SMBDIRECT_MAX_SEND_SGE; + qp_attr.cap.max_recv_sge = SMBDIRECT_MAX_RECV_SGE; + qp_attr.cap.max_inline_data = 0; + qp_attr.sq_sig_type = IB_SIGNAL_REQ_WR; + qp_attr.qp_type = IB_QPT_RC; + qp_attr.send_cq = info->send_cq; + qp_attr.recv_cq = info->recv_cq; + qp_attr.port_num = ~0; + + rc = rdma_create_qp(info->id, info->pd, &qp_attr); + if (rc) { + log_rdma_event(ERR, "rdma_create_qp failed %i\n", rc); + goto create_qp_failed; + } + + memset(&conn_param, 0, sizeof(conn_param)); + conn_param.initiator_depth = 0; + + conn_param.responder_resources = + info->id->device->attrs.max_qp_rd_atom + < SMBD_CM_RESPONDER_RESOURCES ? + info->id->device->attrs.max_qp_rd_atom : + SMBD_CM_RESPONDER_RESOURCES; + info->responder_resources = conn_param.responder_resources; + log_rdma_mr(INFO, "responder_resources=%d\n", + info->responder_resources); + + /* Need to send IRD/ORD in private data for iWARP */ + info->id->device->ops.get_port_immutable( + info->id->device, info->id->port_num, &port_immutable); + if (port_immutable.core_cap_flags & RDMA_CORE_PORT_IWARP) { + ird_ord_hdr[0] = info->responder_resources; + ird_ord_hdr[1] = 1; + conn_param.private_data = ird_ord_hdr; + conn_param.private_data_len = sizeof(ird_ord_hdr); + } else { + conn_param.private_data = NULL; + conn_param.private_data_len = 0; + } + + conn_param.retry_count = SMBD_CM_RETRY; + conn_param.rnr_retry_count = SMBD_CM_RNR_RETRY; + conn_param.flow_control = 0; + + log_rdma_event(INFO, "connecting to IP %pI4 port %d\n", + &addr_in->sin_addr, port); + + init_waitqueue_head(&info->conn_wait); + init_waitqueue_head(&info->disconn_wait); + init_waitqueue_head(&info->wait_reassembly_queue); + rc = rdma_connect(info->id, &conn_param); + if (rc) { + log_rdma_event(ERR, "rdma_connect() failed with %i\n", rc); + goto rdma_connect_failed; + } + + wait_event_interruptible( + info->conn_wait, info->transport_status != SMBD_CONNECTING); + + if (info->transport_status != SMBD_CONNECTED) { + log_rdma_event(ERR, "rdma_connect failed port=%d\n", port); + goto rdma_connect_failed; + } + + log_rdma_event(INFO, "rdma_connect connected\n"); + + rc = allocate_caches_and_workqueue(info); + if (rc) { + log_rdma_event(ERR, "cache allocation failed\n"); + goto allocate_cache_failed; + } + + init_waitqueue_head(&info->wait_send_queue); + INIT_DELAYED_WORK(&info->idle_timer_work, idle_connection_timer); + queue_delayed_work(info->workqueue, &info->idle_timer_work, + info->keep_alive_interval*HZ); + + init_waitqueue_head(&info->wait_send_pending); + atomic_set(&info->send_pending, 0); + + init_waitqueue_head(&info->wait_post_send); + + INIT_WORK(&info->disconnect_work, smbd_disconnect_rdma_work); + INIT_WORK(&info->post_send_credits_work, smbd_post_send_credits); + info->new_credits_offered = 0; + spin_lock_init(&info->lock_new_credits_offered); + + rc = smbd_negotiate(info); + if (rc) { + log_rdma_event(ERR, "smbd_negotiate rc=%d\n", rc); + goto negotiation_failed; + } + + rc = allocate_mr_list(info); + if (rc) { + log_rdma_mr(ERR, "memory registration allocation failed\n"); + goto allocate_mr_failed; + } + + return info; + +allocate_mr_failed: + /* At this point, need to a full transport shutdown */ + server->smbd_conn = info; + smbd_destroy(server); + return NULL; + +negotiation_failed: + cancel_delayed_work_sync(&info->idle_timer_work); + destroy_caches_and_workqueue(info); + info->transport_status = SMBD_NEGOTIATE_FAILED; + init_waitqueue_head(&info->conn_wait); + rdma_disconnect(info->id); + wait_event(info->conn_wait, + info->transport_status == SMBD_DISCONNECTED); + +allocate_cache_failed: +rdma_connect_failed: + rdma_destroy_qp(info->id); + +create_qp_failed: +alloc_cq_failed: + if (info->send_cq) + ib_free_cq(info->send_cq); + if (info->recv_cq) + ib_free_cq(info->recv_cq); + +config_failed: + ib_dealloc_pd(info->pd); + rdma_destroy_id(info->id); + +create_id_failed: + kfree(info); + return NULL; +} + +struct smbd_connection *smbd_get_connection( + struct TCP_Server_Info *server, struct sockaddr *dstaddr) +{ + struct smbd_connection *ret; + int port = SMBD_PORT; + +try_again: + ret = _smbd_get_connection(server, dstaddr, port); + + /* Try SMB_PORT if SMBD_PORT doesn't work */ + if (!ret && port == SMBD_PORT) { + port = SMB_PORT; + goto try_again; + } + return ret; +} + +/* + * Receive data from receive reassembly queue + * All the incoming data packets are placed in reassembly queue + * buf: the buffer to read data into + * size: the length of data to read + * return value: actual data read + * Note: this implementation copies the data from reassebmly queue to receive + * buffers used by upper layer. This is not the optimal code path. A better way + * to do it is to not have upper layer allocate its receive buffers but rather + * borrow the buffer from reassembly queue, and return it after data is + * consumed. But this will require more changes to upper layer code, and also + * need to consider packet boundaries while they still being reassembled. + */ +static int smbd_recv_buf(struct smbd_connection *info, char *buf, + unsigned int size) +{ + struct smbd_response *response; + struct smbd_data_transfer *data_transfer; + int to_copy, to_read, data_read, offset; + u32 data_length, remaining_data_length, data_offset; + int rc; + +again: + /* + * No need to hold the reassembly queue lock all the time as we are + * the only one reading from the front of the queue. The transport + * may add more entries to the back of the queue at the same time + */ + log_read(INFO, "size=%d info->reassembly_data_length=%d\n", size, + info->reassembly_data_length); + if (info->reassembly_data_length >= size) { + int queue_length; + int queue_removed = 0; + + /* + * Need to make sure reassembly_data_length is read before + * reading reassembly_queue_length and calling + * _get_first_reassembly. This call is lock free + * as we never read at the end of the queue which are being + * updated in SOFTIRQ as more data is received + */ + virt_rmb(); + queue_length = info->reassembly_queue_length; + data_read = 0; + to_read = size; + offset = info->first_entry_offset; + while (data_read < size) { + response = _get_first_reassembly(info); + data_transfer = smbd_response_payload(response); + data_length = le32_to_cpu(data_transfer->data_length); + remaining_data_length = + le32_to_cpu( + data_transfer->remaining_data_length); + data_offset = le32_to_cpu(data_transfer->data_offset); + + /* + * The upper layer expects RFC1002 length at the + * beginning of the payload. Return it to indicate + * the total length of the packet. This minimize the + * change to upper layer packet processing logic. This + * will be eventually remove when an intermediate + * transport layer is added + */ + if (response->first_segment && size == 4) { + unsigned int rfc1002_len = + data_length + remaining_data_length; + *((__be32 *)buf) = cpu_to_be32(rfc1002_len); + data_read = 4; + response->first_segment = false; + log_read(INFO, "returning rfc1002 length %d\n", + rfc1002_len); + goto read_rfc1002_done; + } + + to_copy = min_t(int, data_length - offset, to_read); + memcpy( + buf + data_read, + (char *)data_transfer + data_offset + offset, + to_copy); + + /* move on to the next buffer? */ + if (to_copy == data_length - offset) { + queue_length--; + /* + * No need to lock if we are not at the + * end of the queue + */ + if (queue_length) + list_del(&response->list); + else { + spin_lock_irq( + &info->reassembly_queue_lock); + list_del(&response->list); + spin_unlock_irq( + &info->reassembly_queue_lock); + } + queue_removed++; + info->count_reassembly_queue--; + info->count_dequeue_reassembly_queue++; + put_receive_buffer(info, response); + offset = 0; + log_read(INFO, "put_receive_buffer offset=0\n"); + } else + offset += to_copy; + + to_read -= to_copy; + data_read += to_copy; + + log_read(INFO, "_get_first_reassembly memcpy %d bytes data_transfer_length-offset=%d after that to_read=%d data_read=%d offset=%d\n", + to_copy, data_length - offset, + to_read, data_read, offset); + } + + spin_lock_irq(&info->reassembly_queue_lock); + info->reassembly_data_length -= data_read; + info->reassembly_queue_length -= queue_removed; + spin_unlock_irq(&info->reassembly_queue_lock); + + info->first_entry_offset = offset; + log_read(INFO, "returning to thread data_read=%d reassembly_data_length=%d first_entry_offset=%d\n", + data_read, info->reassembly_data_length, + info->first_entry_offset); +read_rfc1002_done: + return data_read; + } + + log_read(INFO, "wait_event on more data\n"); + rc = wait_event_interruptible( + info->wait_reassembly_queue, + info->reassembly_data_length >= size || + info->transport_status != SMBD_CONNECTED); + /* Don't return any data if interrupted */ + if (rc) + return rc; + + if (info->transport_status != SMBD_CONNECTED) { + log_read(ERR, "disconnected\n"); + return -ECONNABORTED; + } + + goto again; +} + +/* + * Receive a page from receive reassembly queue + * page: the page to read data into + * to_read: the length of data to read + * return value: actual data read + */ +static int smbd_recv_page(struct smbd_connection *info, + struct page *page, unsigned int page_offset, + unsigned int to_read) +{ + int ret; + char *to_address; + void *page_address; + + /* make sure we have the page ready for read */ + ret = wait_event_interruptible( + info->wait_reassembly_queue, + info->reassembly_data_length >= to_read || + info->transport_status != SMBD_CONNECTED); + if (ret) + return ret; + + /* now we can read from reassembly queue and not sleep */ + page_address = kmap_atomic(page); + to_address = (char *) page_address + page_offset; + + log_read(INFO, "reading from page=%p address=%p to_read=%d\n", + page, to_address, to_read); + + ret = smbd_recv_buf(info, to_address, to_read); + kunmap_atomic(page_address); + + return ret; +} + +/* + * Receive data from transport + * msg: a msghdr point to the buffer, can be ITER_KVEC or ITER_BVEC + * return: total bytes read, or 0. SMB Direct will not do partial read. + */ +int smbd_recv(struct smbd_connection *info, struct msghdr *msg) +{ + char *buf; + struct page *page; + unsigned int to_read, page_offset; + int rc; + + if (iov_iter_rw(&msg->msg_iter) == WRITE) { + /* It's a bug in upper layer to get there */ + cifs_dbg(VFS, "Invalid msg iter dir %u\n", + iov_iter_rw(&msg->msg_iter)); + rc = -EINVAL; + goto out; + } + + switch (iov_iter_type(&msg->msg_iter)) { + case ITER_KVEC: + buf = msg->msg_iter.kvec->iov_base; + to_read = msg->msg_iter.kvec->iov_len; + rc = smbd_recv_buf(info, buf, to_read); + break; + + case ITER_BVEC: + page = msg->msg_iter.bvec->bv_page; + page_offset = msg->msg_iter.bvec->bv_offset; + to_read = msg->msg_iter.bvec->bv_len; + rc = smbd_recv_page(info, page, page_offset, to_read); + break; + + default: + /* It's a bug in upper layer to get there */ + cifs_dbg(VFS, "Invalid msg type %d\n", + iov_iter_type(&msg->msg_iter)); + rc = -EINVAL; + } + +out: + /* SMBDirect will read it all or nothing */ + if (rc > 0) + msg->msg_iter.count = 0; + return rc; +} + +/* + * Send data to transport + * Each rqst is transported as a SMBDirect payload + * rqst: the data to write + * return value: 0 if successfully write, otherwise error code + */ +int smbd_send(struct TCP_Server_Info *server, + int num_rqst, struct smb_rqst *rqst_array) +{ + struct smbd_connection *info = server->smbd_conn; + struct kvec vecs[SMBDIRECT_MAX_SEND_SGE - 1]; + int nvecs; + int size; + unsigned int buflen, remaining_data_length; + unsigned int offset, remaining_vec_data_length; + int start, i, j; + int max_iov_size = + info->max_send_size - sizeof(struct smbd_data_transfer); + struct kvec *iov; + int rc; + struct smb_rqst *rqst; + int rqst_idx; + + if (info->transport_status != SMBD_CONNECTED) + return -EAGAIN; + + /* + * Add in the page array if there is one. The caller needs to set + * rq_tailsz to PAGE_SIZE when the buffer has multiple pages and + * ends at page boundary + */ + remaining_data_length = 0; + for (i = 0; i < num_rqst; i++) + remaining_data_length += smb_rqst_len(server, &rqst_array[i]); + + if (unlikely(remaining_data_length > info->max_fragmented_send_size)) { + /* assertion: payload never exceeds negotiated maximum */ + log_write(ERR, "payload size %d > max size %d\n", + remaining_data_length, info->max_fragmented_send_size); + return -EINVAL; + } + + log_write(INFO, "num_rqst=%d total length=%u\n", + num_rqst, remaining_data_length); + + rqst_idx = 0; + do { + rqst = &rqst_array[rqst_idx]; + iov = rqst->rq_iov; + + cifs_dbg(FYI, "Sending smb (RDMA): idx=%d smb_len=%lu\n", + rqst_idx, smb_rqst_len(server, rqst)); + remaining_vec_data_length = 0; + for (i = 0; i < rqst->rq_nvec; i++) { + remaining_vec_data_length += iov[i].iov_len; + dump_smb(iov[i].iov_base, iov[i].iov_len); + } + + log_write(INFO, "rqst_idx=%d nvec=%d rqst->rq_npages=%d rq_pagesz=%d rq_tailsz=%d buflen=%lu\n", + rqst_idx, rqst->rq_nvec, + rqst->rq_npages, rqst->rq_pagesz, + rqst->rq_tailsz, smb_rqst_len(server, rqst)); + + start = 0; + offset = 0; + do { + buflen = 0; + i = start; + j = 0; + while (i < rqst->rq_nvec && + j < SMBDIRECT_MAX_SEND_SGE - 1 && + buflen < max_iov_size) { + + vecs[j].iov_base = iov[i].iov_base + offset; + if (buflen + iov[i].iov_len > max_iov_size) { + vecs[j].iov_len = + max_iov_size - iov[i].iov_len; + buflen = max_iov_size; + offset = vecs[j].iov_len; + } else { + vecs[j].iov_len = + iov[i].iov_len - offset; + buflen += vecs[j].iov_len; + offset = 0; + ++i; + } + ++j; + } + + remaining_vec_data_length -= buflen; + remaining_data_length -= buflen; + log_write(INFO, "sending %s iov[%d] from start=%d nvecs=%d remaining_data_length=%d\n", + remaining_vec_data_length > 0 ? + "partial" : "complete", + rqst->rq_nvec, start, j, + remaining_data_length); + + start = i; + rc = smbd_post_send_data(info, vecs, j, remaining_data_length); + if (rc) + goto done; + } while (remaining_vec_data_length > 0); + + /* now sending pages if there are any */ + for (i = 0; i < rqst->rq_npages; i++) { + rqst_page_get_length(rqst, i, &buflen, &offset); + nvecs = (buflen + max_iov_size - 1) / max_iov_size; + log_write(INFO, "sending pages buflen=%d nvecs=%d\n", + buflen, nvecs); + for (j = 0; j < nvecs; j++) { + size = min_t(unsigned int, max_iov_size, remaining_data_length); + remaining_data_length -= size; + log_write(INFO, "sending pages i=%d offset=%d size=%d remaining_data_length=%d\n", + i, j * max_iov_size + offset, size, + remaining_data_length); + rc = smbd_post_send_page( + info, rqst->rq_pages[i], + j*max_iov_size + offset, + size, remaining_data_length); + if (rc) + goto done; + } + } + } while (++rqst_idx < num_rqst); + +done: + /* + * As an optimization, we don't wait for individual I/O to finish + * before sending the next one. + * Send them all and wait for pending send count to get to 0 + * that means all the I/Os have been out and we are good to return + */ + + wait_event(info->wait_send_pending, + atomic_read(&info->send_pending) == 0); + + return rc; +} + +static void register_mr_done(struct ib_cq *cq, struct ib_wc *wc) +{ + struct smbd_mr *mr; + struct ib_cqe *cqe; + + if (wc->status) { + log_rdma_mr(ERR, "status=%d\n", wc->status); + cqe = wc->wr_cqe; + mr = container_of(cqe, struct smbd_mr, cqe); + smbd_disconnect_rdma_connection(mr->conn); + } +} + +/* + * The work queue function that recovers MRs + * We need to call ib_dereg_mr() and ib_alloc_mr() before this MR can be used + * again. Both calls are slow, so finish them in a workqueue. This will not + * block I/O path. + * There is one workqueue that recovers MRs, there is no need to lock as the + * I/O requests calling smbd_register_mr will never update the links in the + * mr_list. + */ +static void smbd_mr_recovery_work(struct work_struct *work) +{ + struct smbd_connection *info = + container_of(work, struct smbd_connection, mr_recovery_work); + struct smbd_mr *smbdirect_mr; + int rc; + + list_for_each_entry(smbdirect_mr, &info->mr_list, list) { + if (smbdirect_mr->state == MR_ERROR) { + + /* recover this MR entry */ + rc = ib_dereg_mr(smbdirect_mr->mr); + if (rc) { + log_rdma_mr(ERR, + "ib_dereg_mr failed rc=%x\n", + rc); + smbd_disconnect_rdma_connection(info); + continue; + } + + smbdirect_mr->mr = ib_alloc_mr( + info->pd, info->mr_type, + info->max_frmr_depth); + if (IS_ERR(smbdirect_mr->mr)) { + log_rdma_mr(ERR, "ib_alloc_mr failed mr_type=%x max_frmr_depth=%x\n", + info->mr_type, + info->max_frmr_depth); + smbd_disconnect_rdma_connection(info); + continue; + } + } else + /* This MR is being used, don't recover it */ + continue; + + smbdirect_mr->state = MR_READY; + + /* smbdirect_mr->state is updated by this function + * and is read and updated by I/O issuing CPUs trying + * to get a MR, the call to atomic_inc_return + * implicates a memory barrier and guarantees this + * value is updated before waking up any calls to + * get_mr() from the I/O issuing CPUs + */ + if (atomic_inc_return(&info->mr_ready_count) == 1) + wake_up_interruptible(&info->wait_mr); + } +} + +static void destroy_mr_list(struct smbd_connection *info) +{ + struct smbd_mr *mr, *tmp; + + cancel_work_sync(&info->mr_recovery_work); + list_for_each_entry_safe(mr, tmp, &info->mr_list, list) { + if (mr->state == MR_INVALIDATED) + ib_dma_unmap_sg(info->id->device, mr->sgl, + mr->sgl_count, mr->dir); + ib_dereg_mr(mr->mr); + kfree(mr->sgl); + kfree(mr); + } +} + +/* + * Allocate MRs used for RDMA read/write + * The number of MRs will not exceed hardware capability in responder_resources + * All MRs are kept in mr_list. The MR can be recovered after it's used + * Recovery is done in smbd_mr_recovery_work. The content of list entry changes + * as MRs are used and recovered for I/O, but the list links will not change + */ +static int allocate_mr_list(struct smbd_connection *info) +{ + int i; + struct smbd_mr *smbdirect_mr, *tmp; + + INIT_LIST_HEAD(&info->mr_list); + init_waitqueue_head(&info->wait_mr); + spin_lock_init(&info->mr_list_lock); + atomic_set(&info->mr_ready_count, 0); + atomic_set(&info->mr_used_count, 0); + init_waitqueue_head(&info->wait_for_mr_cleanup); + INIT_WORK(&info->mr_recovery_work, smbd_mr_recovery_work); + /* Allocate more MRs (2x) than hardware responder_resources */ + for (i = 0; i < info->responder_resources * 2; i++) { + smbdirect_mr = kzalloc(sizeof(*smbdirect_mr), GFP_KERNEL); + if (!smbdirect_mr) + goto out; + smbdirect_mr->mr = ib_alloc_mr(info->pd, info->mr_type, + info->max_frmr_depth); + if (IS_ERR(smbdirect_mr->mr)) { + log_rdma_mr(ERR, "ib_alloc_mr failed mr_type=%x max_frmr_depth=%x\n", + info->mr_type, info->max_frmr_depth); + goto out; + } + smbdirect_mr->sgl = kcalloc( + info->max_frmr_depth, + sizeof(struct scatterlist), + GFP_KERNEL); + if (!smbdirect_mr->sgl) { + log_rdma_mr(ERR, "failed to allocate sgl\n"); + ib_dereg_mr(smbdirect_mr->mr); + goto out; + } + smbdirect_mr->state = MR_READY; + smbdirect_mr->conn = info; + + list_add_tail(&smbdirect_mr->list, &info->mr_list); + atomic_inc(&info->mr_ready_count); + } + return 0; + +out: + kfree(smbdirect_mr); + + list_for_each_entry_safe(smbdirect_mr, tmp, &info->mr_list, list) { + list_del(&smbdirect_mr->list); + ib_dereg_mr(smbdirect_mr->mr); + kfree(smbdirect_mr->sgl); + kfree(smbdirect_mr); + } + return -ENOMEM; +} + +/* + * Get a MR from mr_list. This function waits until there is at least one + * MR available in the list. It may access the list while the + * smbd_mr_recovery_work is recovering the MR list. This doesn't need a lock + * as they never modify the same places. However, there may be several CPUs + * issueing I/O trying to get MR at the same time, mr_list_lock is used to + * protect this situation. + */ +static struct smbd_mr *get_mr(struct smbd_connection *info) +{ + struct smbd_mr *ret; + int rc; +again: + rc = wait_event_interruptible(info->wait_mr, + atomic_read(&info->mr_ready_count) || + info->transport_status != SMBD_CONNECTED); + if (rc) { + log_rdma_mr(ERR, "wait_event_interruptible rc=%x\n", rc); + return NULL; + } + + if (info->transport_status != SMBD_CONNECTED) { + log_rdma_mr(ERR, "info->transport_status=%x\n", + info->transport_status); + return NULL; + } + + spin_lock(&info->mr_list_lock); + list_for_each_entry(ret, &info->mr_list, list) { + if (ret->state == MR_READY) { + ret->state = MR_REGISTERED; + spin_unlock(&info->mr_list_lock); + atomic_dec(&info->mr_ready_count); + atomic_inc(&info->mr_used_count); + return ret; + } + } + + spin_unlock(&info->mr_list_lock); + /* + * It is possible that we could fail to get MR because other processes may + * try to acquire a MR at the same time. If this is the case, retry it. + */ + goto again; +} + +/* + * Register memory for RDMA read/write + * pages[]: the list of pages to register memory with + * num_pages: the number of pages to register + * tailsz: if non-zero, the bytes to register in the last page + * writing: true if this is a RDMA write (SMB read), false for RDMA read + * need_invalidate: true if this MR needs to be locally invalidated after I/O + * return value: the MR registered, NULL if failed. + */ +struct smbd_mr *smbd_register_mr( + struct smbd_connection *info, struct page *pages[], int num_pages, + int offset, int tailsz, bool writing, bool need_invalidate) +{ + struct smbd_mr *smbdirect_mr; + int rc, i; + enum dma_data_direction dir; + struct ib_reg_wr *reg_wr; + + if (num_pages > info->max_frmr_depth) { + log_rdma_mr(ERR, "num_pages=%d max_frmr_depth=%d\n", + num_pages, info->max_frmr_depth); + return NULL; + } + + smbdirect_mr = get_mr(info); + if (!smbdirect_mr) { + log_rdma_mr(ERR, "get_mr returning NULL\n"); + return NULL; + } + smbdirect_mr->need_invalidate = need_invalidate; + smbdirect_mr->sgl_count = num_pages; + sg_init_table(smbdirect_mr->sgl, num_pages); + + log_rdma_mr(INFO, "num_pages=0x%x offset=0x%x tailsz=0x%x\n", + num_pages, offset, tailsz); + + if (num_pages == 1) { + sg_set_page(&smbdirect_mr->sgl[0], pages[0], tailsz, offset); + goto skip_multiple_pages; + } + + /* We have at least two pages to register */ + sg_set_page( + &smbdirect_mr->sgl[0], pages[0], PAGE_SIZE - offset, offset); + i = 1; + while (i < num_pages - 1) { + sg_set_page(&smbdirect_mr->sgl[i], pages[i], PAGE_SIZE, 0); + i++; + } + sg_set_page(&smbdirect_mr->sgl[i], pages[i], + tailsz ? tailsz : PAGE_SIZE, 0); + +skip_multiple_pages: + dir = writing ? DMA_FROM_DEVICE : DMA_TO_DEVICE; + smbdirect_mr->dir = dir; + rc = ib_dma_map_sg(info->id->device, smbdirect_mr->sgl, num_pages, dir); + if (!rc) { + log_rdma_mr(ERR, "ib_dma_map_sg num_pages=%x dir=%x rc=%x\n", + num_pages, dir, rc); + goto dma_map_error; + } + + rc = ib_map_mr_sg(smbdirect_mr->mr, smbdirect_mr->sgl, num_pages, + NULL, PAGE_SIZE); + if (rc != num_pages) { + log_rdma_mr(ERR, + "ib_map_mr_sg failed rc = %d num_pages = %x\n", + rc, num_pages); + goto map_mr_error; + } + + ib_update_fast_reg_key(smbdirect_mr->mr, + ib_inc_rkey(smbdirect_mr->mr->rkey)); + reg_wr = &smbdirect_mr->wr; + reg_wr->wr.opcode = IB_WR_REG_MR; + smbdirect_mr->cqe.done = register_mr_done; + reg_wr->wr.wr_cqe = &smbdirect_mr->cqe; + reg_wr->wr.num_sge = 0; + reg_wr->wr.send_flags = IB_SEND_SIGNALED; + reg_wr->mr = smbdirect_mr->mr; + reg_wr->key = smbdirect_mr->mr->rkey; + reg_wr->access = writing ? + IB_ACCESS_REMOTE_WRITE | IB_ACCESS_LOCAL_WRITE : + IB_ACCESS_REMOTE_READ; + + /* + * There is no need for waiting for complemtion on ib_post_send + * on IB_WR_REG_MR. Hardware enforces a barrier and order of execution + * on the next ib_post_send when we actaully send I/O to remote peer + */ + rc = ib_post_send(info->id->qp, ®_wr->wr, NULL); + if (!rc) + return smbdirect_mr; + + log_rdma_mr(ERR, "ib_post_send failed rc=%x reg_wr->key=%x\n", + rc, reg_wr->key); + + /* If all failed, attempt to recover this MR by setting it MR_ERROR*/ +map_mr_error: + ib_dma_unmap_sg(info->id->device, smbdirect_mr->sgl, + smbdirect_mr->sgl_count, smbdirect_mr->dir); + +dma_map_error: + smbdirect_mr->state = MR_ERROR; + if (atomic_dec_and_test(&info->mr_used_count)) + wake_up(&info->wait_for_mr_cleanup); + + smbd_disconnect_rdma_connection(info); + + return NULL; +} + +static void local_inv_done(struct ib_cq *cq, struct ib_wc *wc) +{ + struct smbd_mr *smbdirect_mr; + struct ib_cqe *cqe; + + cqe = wc->wr_cqe; + smbdirect_mr = container_of(cqe, struct smbd_mr, cqe); + smbdirect_mr->state = MR_INVALIDATED; + if (wc->status != IB_WC_SUCCESS) { + log_rdma_mr(ERR, "invalidate failed status=%x\n", wc->status); + smbdirect_mr->state = MR_ERROR; + } + complete(&smbdirect_mr->invalidate_done); +} + +/* + * Deregister a MR after I/O is done + * This function may wait if remote invalidation is not used + * and we have to locally invalidate the buffer to prevent data is being + * modified by remote peer after upper layer consumes it + */ +int smbd_deregister_mr(struct smbd_mr *smbdirect_mr) +{ + struct ib_send_wr *wr; + struct smbd_connection *info = smbdirect_mr->conn; + int rc = 0; + + if (smbdirect_mr->need_invalidate) { + /* Need to finish local invalidation before returning */ + wr = &smbdirect_mr->inv_wr; + wr->opcode = IB_WR_LOCAL_INV; + smbdirect_mr->cqe.done = local_inv_done; + wr->wr_cqe = &smbdirect_mr->cqe; + wr->num_sge = 0; + wr->ex.invalidate_rkey = smbdirect_mr->mr->rkey; + wr->send_flags = IB_SEND_SIGNALED; + + init_completion(&smbdirect_mr->invalidate_done); + rc = ib_post_send(info->id->qp, wr, NULL); + if (rc) { + log_rdma_mr(ERR, "ib_post_send failed rc=%x\n", rc); + smbd_disconnect_rdma_connection(info); + goto done; + } + wait_for_completion(&smbdirect_mr->invalidate_done); + smbdirect_mr->need_invalidate = false; + } else + /* + * For remote invalidation, just set it to MR_INVALIDATED + * and defer to mr_recovery_work to recover the MR for next use + */ + smbdirect_mr->state = MR_INVALIDATED; + + if (smbdirect_mr->state == MR_INVALIDATED) { + ib_dma_unmap_sg( + info->id->device, smbdirect_mr->sgl, + smbdirect_mr->sgl_count, + smbdirect_mr->dir); + smbdirect_mr->state = MR_READY; + if (atomic_inc_return(&info->mr_ready_count) == 1) + wake_up_interruptible(&info->wait_mr); + } else + /* + * Schedule the work to do MR recovery for future I/Os MR + * recovery is slow and don't want it to block current I/O + */ + queue_work(info->workqueue, &info->mr_recovery_work); + +done: + if (atomic_dec_and_test(&info->mr_used_count)) + wake_up(&info->wait_for_mr_cleanup); + + return rc; +} diff --git a/fs/smb/client/smbdirect.h b/fs/smb/client/smbdirect.h new file mode 100644 index 0000000000000..207ef979cd51c --- /dev/null +++ b/fs/smb/client/smbdirect.h @@ -0,0 +1,320 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + * Copyright (C) 2017, Microsoft Corporation. + * + * Author(s): Long Li + */ +#ifndef _SMBDIRECT_H +#define _SMBDIRECT_H + +#ifdef CONFIG_CIFS_SMB_DIRECT +#define cifs_rdma_enabled(server) ((server)->rdma) + +#include "cifsglob.h" +#include +#include +#include + +extern int rdma_readwrite_threshold; +extern int smbd_max_frmr_depth; +extern int smbd_keep_alive_interval; +extern int smbd_max_receive_size; +extern int smbd_max_fragmented_recv_size; +extern int smbd_max_send_size; +extern int smbd_send_credit_target; +extern int smbd_receive_credit_max; + +enum keep_alive_status { + KEEP_ALIVE_NONE, + KEEP_ALIVE_PENDING, + KEEP_ALIVE_SENT, +}; + +enum smbd_connection_status { + SMBD_CREATED, + SMBD_CONNECTING, + SMBD_CONNECTED, + SMBD_NEGOTIATE_FAILED, + SMBD_DISCONNECTING, + SMBD_DISCONNECTED, + SMBD_DESTROYED +}; + +/* + * The context for the SMBDirect transport + * Everything related to the transport is here. It has several logical parts + * 1. RDMA related structures + * 2. SMBDirect connection parameters + * 3. Memory registrations + * 4. Receive and reassembly queues for data receive path + * 5. mempools for allocating packets + */ +struct smbd_connection { + enum smbd_connection_status transport_status; + + /* RDMA related */ + struct rdma_cm_id *id; + struct ib_qp_init_attr qp_attr; + struct ib_pd *pd; + struct ib_cq *send_cq, *recv_cq; + struct ib_device_attr dev_attr; + int ri_rc; + struct completion ri_done; + wait_queue_head_t conn_wait; + wait_queue_head_t disconn_wait; + + struct completion negotiate_completion; + bool negotiate_done; + + struct work_struct disconnect_work; + struct work_struct post_send_credits_work; + + spinlock_t lock_new_credits_offered; + int new_credits_offered; + + /* Connection parameters defined in [MS-SMBD] 3.1.1.1 */ + int receive_credit_max; + int send_credit_target; + int max_send_size; + int max_fragmented_recv_size; + int max_fragmented_send_size; + int max_receive_size; + int keep_alive_interval; + int max_readwrite_size; + enum keep_alive_status keep_alive_requested; + int protocol; + atomic_t send_credits; + atomic_t receive_credits; + int receive_credit_target; + int fragment_reassembly_remaining; + + /* Memory registrations */ + /* Maximum number of RDMA read/write outstanding on this connection */ + int responder_resources; + /* Maximum number of pages in a single RDMA write/read on this connection */ + int max_frmr_depth; + /* + * If payload is less than or equal to the threshold, + * use RDMA send/recv to send upper layer I/O. + * If payload is more than the threshold, + * use RDMA read/write through memory registration for I/O. + */ + int rdma_readwrite_threshold; + enum ib_mr_type mr_type; + struct list_head mr_list; + spinlock_t mr_list_lock; + /* The number of available MRs ready for memory registration */ + atomic_t mr_ready_count; + atomic_t mr_used_count; + wait_queue_head_t wait_mr; + struct work_struct mr_recovery_work; + /* Used by transport to wait until all MRs are returned */ + wait_queue_head_t wait_for_mr_cleanup; + + /* Activity accoutning */ + atomic_t send_pending; + wait_queue_head_t wait_send_pending; + wait_queue_head_t wait_post_send; + + /* Receive queue */ + struct list_head receive_queue; + int count_receive_queue; + spinlock_t receive_queue_lock; + + struct list_head empty_packet_queue; + int count_empty_packet_queue; + spinlock_t empty_packet_queue_lock; + + wait_queue_head_t wait_receive_queues; + + /* Reassembly queue */ + struct list_head reassembly_queue; + spinlock_t reassembly_queue_lock; + wait_queue_head_t wait_reassembly_queue; + + /* total data length of reassembly queue */ + int reassembly_data_length; + int reassembly_queue_length; + /* the offset to first buffer in reassembly queue */ + int first_entry_offset; + + bool send_immediate; + + wait_queue_head_t wait_send_queue; + + /* + * Indicate if we have received a full packet on the connection + * This is used to identify the first SMBD packet of a assembled + * payload (SMB packet) in reassembly queue so we can return a + * RFC1002 length to upper layer to indicate the length of the SMB + * packet received + */ + bool full_packet_received; + + struct workqueue_struct *workqueue; + struct delayed_work idle_timer_work; + + /* Memory pool for preallocating buffers */ + /* request pool for RDMA send */ + struct kmem_cache *request_cache; + mempool_t *request_mempool; + + /* response pool for RDMA receive */ + struct kmem_cache *response_cache; + mempool_t *response_mempool; + + /* for debug purposes */ + unsigned int count_get_receive_buffer; + unsigned int count_put_receive_buffer; + unsigned int count_reassembly_queue; + unsigned int count_enqueue_reassembly_queue; + unsigned int count_dequeue_reassembly_queue; + unsigned int count_send_empty; +}; + +enum smbd_message_type { + SMBD_NEGOTIATE_RESP, + SMBD_TRANSFER_DATA, +}; + +#define SMB_DIRECT_RESPONSE_REQUESTED 0x0001 + +/* SMBD negotiation request packet [MS-SMBD] 2.2.1 */ +struct smbd_negotiate_req { + __le16 min_version; + __le16 max_version; + __le16 reserved; + __le16 credits_requested; + __le32 preferred_send_size; + __le32 max_receive_size; + __le32 max_fragmented_size; +} __packed; + +/* SMBD negotiation response packet [MS-SMBD] 2.2.2 */ +struct smbd_negotiate_resp { + __le16 min_version; + __le16 max_version; + __le16 negotiated_version; + __le16 reserved; + __le16 credits_requested; + __le16 credits_granted; + __le32 status; + __le32 max_readwrite_size; + __le32 preferred_send_size; + __le32 max_receive_size; + __le32 max_fragmented_size; +} __packed; + +/* SMBD data transfer packet with payload [MS-SMBD] 2.2.3 */ +struct smbd_data_transfer { + __le16 credits_requested; + __le16 credits_granted; + __le16 flags; + __le16 reserved; + __le32 remaining_data_length; + __le32 data_offset; + __le32 data_length; + __le32 padding; + __u8 buffer[]; +} __packed; + +/* The packet fields for a registered RDMA buffer */ +struct smbd_buffer_descriptor_v1 { + __le64 offset; + __le32 token; + __le32 length; +} __packed; + +/* Maximum number of SGEs used by smbdirect.c in any send work request */ +#define SMBDIRECT_MAX_SEND_SGE 6 + +/* The context for a SMBD request */ +struct smbd_request { + struct smbd_connection *info; + struct ib_cqe cqe; + + /* the SGE entries for this work request */ + struct ib_sge sge[SMBDIRECT_MAX_SEND_SGE]; + int num_sge; + + /* SMBD packet header follows this structure */ + u8 packet[]; +}; + +/* Maximum number of SGEs used by smbdirect.c in any receive work request */ +#define SMBDIRECT_MAX_RECV_SGE 1 + +/* The context for a SMBD response */ +struct smbd_response { + struct smbd_connection *info; + struct ib_cqe cqe; + struct ib_sge sge; + + enum smbd_message_type type; + + /* Link to receive queue or reassembly queue */ + struct list_head list; + + /* Indicate if this is the 1st packet of a payload */ + bool first_segment; + + /* SMBD packet header and payload follows this structure */ + u8 packet[]; +}; + +/* Create a SMBDirect session */ +struct smbd_connection *smbd_get_connection( + struct TCP_Server_Info *server, struct sockaddr *dstaddr); + +/* Reconnect SMBDirect session */ +int smbd_reconnect(struct TCP_Server_Info *server); +/* Destroy SMBDirect session */ +void smbd_destroy(struct TCP_Server_Info *server); + +/* Interface for carrying upper layer I/O through send/recv */ +int smbd_recv(struct smbd_connection *info, struct msghdr *msg); +int smbd_send(struct TCP_Server_Info *server, + int num_rqst, struct smb_rqst *rqst); + +enum mr_state { + MR_READY, + MR_REGISTERED, + MR_INVALIDATED, + MR_ERROR +}; + +struct smbd_mr { + struct smbd_connection *conn; + struct list_head list; + enum mr_state state; + struct ib_mr *mr; + struct scatterlist *sgl; + int sgl_count; + enum dma_data_direction dir; + union { + struct ib_reg_wr wr; + struct ib_send_wr inv_wr; + }; + struct ib_cqe cqe; + bool need_invalidate; + struct completion invalidate_done; +}; + +/* Interfaces to register and deregister MR for RDMA read/write */ +struct smbd_mr *smbd_register_mr( + struct smbd_connection *info, struct page *pages[], int num_pages, + int offset, int tailsz, bool writing, bool need_invalidate); +int smbd_deregister_mr(struct smbd_mr *mr); + +#else +#define cifs_rdma_enabled(server) 0 +struct smbd_connection {}; +static inline void *smbd_get_connection( + struct TCP_Server_Info *server, struct sockaddr *dstaddr) {return NULL;} +static inline int smbd_reconnect(struct TCP_Server_Info *server) {return -1; } +static inline void smbd_destroy(struct TCP_Server_Info *server) {} +static inline int smbd_recv(struct smbd_connection *info, struct msghdr *msg) {return -1; } +static inline int smbd_send(struct TCP_Server_Info *server, int num_rqst, struct smb_rqst *rqst) {return -1; } +#endif + +#endif diff --git a/fs/smb/client/smbencrypt.c b/fs/smb/client/smbencrypt.c new file mode 100644 index 0000000000000..f0ce26414f173 --- /dev/null +++ b/fs/smb/client/smbencrypt.c @@ -0,0 +1,91 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + Unix SMB/Netbios implementation. + Version 1.9. + SMB parameters and setup + Copyright (C) Andrew Tridgell 1992-2000 + Copyright (C) Luke Kenneth Casson Leighton 1996-2000 + Modified by Jeremy Allison 1995. + Copyright (C) Andrew Bartlett 2002-2003 + Modified by Steve French (sfrench@us.ibm.com) 2002-2003 + +*/ + +#include +#include +#include +#include +#include +#include +#include +#include "cifs_fs_sb.h" +#include "cifs_unicode.h" +#include "cifspdu.h" +#include "cifsglob.h" +#include "cifs_debug.h" +#include "cifsproto.h" +#include "../common/md4.h" + +#ifndef false +#define false 0 +#endif +#ifndef true +#define true 1 +#endif + +/* following came from the other byteorder.h to avoid include conflicts */ +#define CVAL(buf,pos) (((unsigned char *)(buf))[pos]) +#define SSVALX(buf,pos,val) (CVAL(buf,pos)=(val)&0xFF,CVAL(buf,pos+1)=(val)>>8) +#define SSVAL(buf,pos,val) SSVALX((buf),(pos),((__u16)(val))) + +/* produce a md4 message digest from data of length n bytes */ +static int +mdfour(unsigned char *md4_hash, unsigned char *link_str, int link_len) +{ + int rc; + struct md4_ctx mctx; + + rc = cifs_md4_init(&mctx); + if (rc) { + cifs_dbg(VFS, "%s: Could not init MD4\n", __func__); + goto mdfour_err; + } + rc = cifs_md4_update(&mctx, link_str, link_len); + if (rc) { + cifs_dbg(VFS, "%s: Could not update MD4\n", __func__); + goto mdfour_err; + } + rc = cifs_md4_final(&mctx, md4_hash); + if (rc) + cifs_dbg(VFS, "%s: Could not finalize MD4\n", __func__); + + +mdfour_err: + return rc; +} + +/* + * Creates the MD4 Hash of the users password in NT UNICODE. + */ + +int +E_md4hash(const unsigned char *passwd, unsigned char *p16, + const struct nls_table *codepage) +{ + int rc; + int len; + __le16 wpwd[129]; + + /* Password cannot be longer than 128 characters */ + if (passwd) /* Password must be converted to NT unicode */ + len = cifs_strtoUTF16(wpwd, passwd, 128, codepage); + else { + len = 0; + *wpwd = 0; /* Ensure string is null terminated */ + } + + rc = mdfour(p16, (unsigned char *) wpwd, len * sizeof(__le16)); + memzero_explicit(wpwd, sizeof(wpwd)); + + return rc; +} diff --git a/fs/smb/client/smberr.h b/fs/smb/client/smberr.h new file mode 100644 index 0000000000000..aeffdad829e2e --- /dev/null +++ b/fs/smb/client/smberr.h @@ -0,0 +1,171 @@ +/* SPDX-License-Identifier: LGPL-2.1 */ +/* + * + * Copyright (c) International Business Machines Corp., 2002,2004 + * Author(s): Steve French (sfrench@us.ibm.com) + * + * See Error Codes section of the SNIA CIFS Specification + * for more information + * + */ + +#define SUCCESS 0x00 /* The request was successful. */ +#define ERRDOS 0x01 /* Error is from the core DOS operating system set */ +#define ERRSRV 0x02 /* Error is generated by the file server daemon */ +#define ERRHRD 0x03 /* Error is a hardware error. */ +#define ERRCMD 0xFF /* Command was not in the "SMB" format. */ + +/* The following error codes may be generated with the SUCCESS error class.*/ + +/*#define SUCCESS 0 The request was successful. */ + +/* The following error codes may be generated with the ERRDOS error class.*/ + +#define ERRbadfunc 1 /* Invalid function. The server did not + recognize or could not perform a + system call generated by the server, + e.g., set the DIRECTORY attribute on + a data file, invalid seek mode. */ +#define ERRbadfile 2 /* File not found. The last component + of a file's pathname could not be + found. */ +#define ERRbadpath 3 /* Directory invalid. A directory + component in a pathname could not be + found. */ +#define ERRnofids 4 /* Too many open files. The server has + no file handles available. */ +#define ERRnoaccess 5 /* Access denied, the client's context + does not permit the requested + function. This includes the + following conditions: invalid rename + command, write to Fid open for read + only, read on Fid open for write + only, attempt to delete a non-empty + directory */ +#define ERRbadfid 6 /* Invalid file handle. The file handle + specified was not recognized by the + server. */ +#define ERRbadmcb 7 /* Memory control blocks destroyed. */ +#define ERRnomem 8 /* Insufficient server memory to + perform the requested function. */ +#define ERRbadmem 9 /* Invalid memory block address. */ +#define ERRbadenv 10 /* Invalid environment. */ +#define ERRbadformat 11 /* Invalid format. */ +#define ERRbadaccess 12 /* Invalid open mode. */ +#define ERRbaddata 13 /* Invalid data (generated only by + IOCTL calls within the server). */ +#define ERRbaddrive 15 /* Invalid drive specified. */ +#define ERRremcd 16 /* A Delete Directory request attempted + to remove the server's current + directory. */ +#define ERRdiffdevice 17 /* Not same device (e.g., a cross + volume rename was attempted */ +#define ERRnofiles 18 /* A File Search command can find no + more files matching the specified + criteria. */ +#define ERRwriteprot 19 /* media is write protected */ +#define ERRgeneral 31 +#define ERRbadshare 32 /* The sharing mode specified for an + Open conflicts with existing FIDs on + the file. */ +#define ERRlock 33 /* A Lock request conflicted with an + existing lock or specified an + invalid mode, or an Unlock requested + attempted to remove a lock held by + another process. */ +#define ERRunsup 50 +#define ERRnosuchshare 67 +#define ERRfilexists 80 /* The file named in the request + already exists. */ +#define ERRinvparm 87 +#define ERRdiskfull 112 +#define ERRinvname 123 +#define ERRinvlevel 124 +#define ERRdirnotempty 145 +#define ERRnotlocked 158 +#define ERRcancelviolation 173 +#define ERRalreadyexists 183 +#define ERRbadpipe 230 +#define ERRpipebusy 231 +#define ERRpipeclosing 232 +#define ERRnotconnected 233 +#define ERRmoredata 234 +#define ERReasnotsupported 282 +#define ErrQuota 0x200 /* The operation would cause a quota + limit to be exceeded. */ +#define ErrNotALink 0x201 /* A link operation was performed on a + pathname that was not a link. */ + +/* Below errors are used internally (do not come over the wire) for passthrough + from STATUS codes to POSIX only */ +#define ERRsymlink 0xFFFD +#define ErrTooManyLinks 0xFFFE + +/* Following error codes may be generated with the ERRSRV error class.*/ + +#define ERRerror 1 /* Non-specific error code. It is + returned under the following + conditions: resource other than disk + space exhausted (e.g. TIDs), first + SMB command was not negotiate, + multiple negotiates attempted, and + internal server error. */ +#define ERRbadpw 2 /* Bad password - name/password pair in + a TreeConnect or Session Setup are + invalid. */ +#define ERRbadtype 3 /* used for indicating DFS referral + needed */ +#define ERRaccess 4 /* The client does not have the + necessary access rights within the + specified context for requested + function. */ +#define ERRinvtid 5 /* The Tid specified in a command was + invalid. */ +#define ERRinvnetname 6 /* Invalid network name in tree + connect. */ +#define ERRinvdevice 7 /* Invalid device - printer request + made to non-printer connection or + non-printer request made to printer + connection. */ +#define ERRqfull 49 /* Print queue full (files) -- returned + by open print file. */ +#define ERRqtoobig 50 /* Print queue full -- no space. */ +#define ERRqeof 51 /* EOF on print queue dump */ +#define ERRinvpfid 52 /* Invalid print file FID. */ +#define ERRsmbcmd 64 /* The server did not recognize the + command received. */ +#define ERRsrverror 65 /* The server encountered an internal + error, e.g., system file + unavailable. */ +#define ERRbadBID 66 /* (obsolete) */ +#define ERRfilespecs 67 /* The Fid and pathname parameters + contained an invalid combination of + values. */ +#define ERRbadLink 68 /* (obsolete) */ +#define ERRbadpermits 69 /* The access permissions specified for + a file or directory are not a valid + combination. */ +#define ERRbadPID 70 +#define ERRsetattrmode 71 /* attribute (mode) is invalid */ +#define ERRpaused 81 /* Server is paused */ +#define ERRmsgoff 82 /* reserved - messaging off */ +#define ERRnoroom 83 /* reserved - no room for message */ +#define ERRrmuns 87 /* reserved - too many remote names */ +#define ERRtimeout 88 /* operation timed out */ +#define ERRnoresource 89 /* No resources available for request + */ +#define ERRtoomanyuids 90 /* Too many UIDs active on this session + */ +#define ERRbaduid 91 /* The UID is not known as a valid user + */ +#define ERRusempx 250 /* temporarily unable to use raw */ +#define ERRusestd 251 /* temporarily unable to use either raw + or mpx */ +#define ERR_NOTIFY_ENUM_DIR 1024 +#define ERRnoSuchUser 2238 /* user account does not exist */ +#define ERRaccountexpired 2239 +#define ERRbadclient 2240 /* can not logon from this client */ +#define ERRbadLogonTime 2241 /* logon hours do not allow this */ +#define ERRpasswordExpired 2242 +#define ERRnetlogonNotStarted 2455 +#define ERRnosupport 0xFFFF diff --git a/fs/smb/client/trace.c b/fs/smb/client/trace.c new file mode 100644 index 0000000000000..4654837871934 --- /dev/null +++ b/fs/smb/client/trace.c @@ -0,0 +1,8 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (C) 2018, Microsoft Corporation. + * + * Author(s): Steve French + */ +#define CREATE_TRACE_POINTS +#include "trace.h" diff --git a/fs/smb/client/trace.h b/fs/smb/client/trace.h new file mode 100644 index 0000000000000..d3053bd8ae731 --- /dev/null +++ b/fs/smb/client/trace.h @@ -0,0 +1,1070 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * Copyright (C) 2018, Microsoft Corporation. + * + * Author(s): Steve French + */ +#undef TRACE_SYSTEM +#define TRACE_SYSTEM cifs + +#if !defined(_CIFS_TRACE_H) || defined(TRACE_HEADER_MULTI_READ) +#define _CIFS_TRACE_H + +#include +#include +#include + +/* + * Please use this 3-part article as a reference for writing new tracepoints: + * https://lwn.net/Articles/379903/ + */ + +/* For logging errors in read or write */ +DECLARE_EVENT_CLASS(smb3_rw_err_class, + TP_PROTO(unsigned int xid, + __u64 fid, + __u32 tid, + __u64 sesid, + __u64 offset, + __u32 len, + int rc), + TP_ARGS(xid, fid, tid, sesid, offset, len, rc), + TP_STRUCT__entry( + __field(unsigned int, xid) + __field(__u64, fid) + __field(__u32, tid) + __field(__u64, sesid) + __field(__u64, offset) + __field(__u32, len) + __field(int, rc) + ), + TP_fast_assign( + __entry->xid = xid; + __entry->fid = fid; + __entry->tid = tid; + __entry->sesid = sesid; + __entry->offset = offset; + __entry->len = len; + __entry->rc = rc; + ), + TP_printk("\txid=%u sid=0x%llx tid=0x%x fid=0x%llx offset=0x%llx len=0x%x rc=%d", + __entry->xid, __entry->sesid, __entry->tid, __entry->fid, + __entry->offset, __entry->len, __entry->rc) +) + +#define DEFINE_SMB3_RW_ERR_EVENT(name) \ +DEFINE_EVENT(smb3_rw_err_class, smb3_##name, \ + TP_PROTO(unsigned int xid, \ + __u64 fid, \ + __u32 tid, \ + __u64 sesid, \ + __u64 offset, \ + __u32 len, \ + int rc), \ + TP_ARGS(xid, fid, tid, sesid, offset, len, rc)) + +DEFINE_SMB3_RW_ERR_EVENT(write_err); +DEFINE_SMB3_RW_ERR_EVENT(read_err); +DEFINE_SMB3_RW_ERR_EVENT(query_dir_err); +DEFINE_SMB3_RW_ERR_EVENT(zero_err); +DEFINE_SMB3_RW_ERR_EVENT(falloc_err); + + +/* For logging successful read or write */ +DECLARE_EVENT_CLASS(smb3_rw_done_class, + TP_PROTO(unsigned int xid, + __u64 fid, + __u32 tid, + __u64 sesid, + __u64 offset, + __u32 len), + TP_ARGS(xid, fid, tid, sesid, offset, len), + TP_STRUCT__entry( + __field(unsigned int, xid) + __field(__u64, fid) + __field(__u32, tid) + __field(__u64, sesid) + __field(__u64, offset) + __field(__u32, len) + ), + TP_fast_assign( + __entry->xid = xid; + __entry->fid = fid; + __entry->tid = tid; + __entry->sesid = sesid; + __entry->offset = offset; + __entry->len = len; + ), + TP_printk("xid=%u sid=0x%llx tid=0x%x fid=0x%llx offset=0x%llx len=0x%x", + __entry->xid, __entry->sesid, __entry->tid, __entry->fid, + __entry->offset, __entry->len) +) + +#define DEFINE_SMB3_RW_DONE_EVENT(name) \ +DEFINE_EVENT(smb3_rw_done_class, smb3_##name, \ + TP_PROTO(unsigned int xid, \ + __u64 fid, \ + __u32 tid, \ + __u64 sesid, \ + __u64 offset, \ + __u32 len), \ + TP_ARGS(xid, fid, tid, sesid, offset, len)) + +DEFINE_SMB3_RW_DONE_EVENT(write_enter); +DEFINE_SMB3_RW_DONE_EVENT(read_enter); +DEFINE_SMB3_RW_DONE_EVENT(query_dir_enter); +DEFINE_SMB3_RW_DONE_EVENT(zero_enter); +DEFINE_SMB3_RW_DONE_EVENT(falloc_enter); +DEFINE_SMB3_RW_DONE_EVENT(write_done); +DEFINE_SMB3_RW_DONE_EVENT(read_done); +DEFINE_SMB3_RW_DONE_EVENT(query_dir_done); +DEFINE_SMB3_RW_DONE_EVENT(zero_done); +DEFINE_SMB3_RW_DONE_EVENT(falloc_done); + +/* For logging successful set EOF (truncate) */ +DECLARE_EVENT_CLASS(smb3_eof_class, + TP_PROTO(unsigned int xid, + __u64 fid, + __u32 tid, + __u64 sesid, + __u64 offset), + TP_ARGS(xid, fid, tid, sesid, offset), + TP_STRUCT__entry( + __field(unsigned int, xid) + __field(__u64, fid) + __field(__u32, tid) + __field(__u64, sesid) + __field(__u64, offset) + ), + TP_fast_assign( + __entry->xid = xid; + __entry->fid = fid; + __entry->tid = tid; + __entry->sesid = sesid; + __entry->offset = offset; + ), + TP_printk("xid=%u sid=0x%llx tid=0x%x fid=0x%llx offset=0x%llx", + __entry->xid, __entry->sesid, __entry->tid, __entry->fid, + __entry->offset) +) + +#define DEFINE_SMB3_EOF_EVENT(name) \ +DEFINE_EVENT(smb3_eof_class, smb3_##name, \ + TP_PROTO(unsigned int xid, \ + __u64 fid, \ + __u32 tid, \ + __u64 sesid, \ + __u64 offset), \ + TP_ARGS(xid, fid, tid, sesid, offset)) + +DEFINE_SMB3_EOF_EVENT(set_eof); + +/* + * For handle based calls other than read and write, and get/set info + */ +DECLARE_EVENT_CLASS(smb3_fd_class, + TP_PROTO(unsigned int xid, + __u64 fid, + __u32 tid, + __u64 sesid), + TP_ARGS(xid, fid, tid, sesid), + TP_STRUCT__entry( + __field(unsigned int, xid) + __field(__u64, fid) + __field(__u32, tid) + __field(__u64, sesid) + ), + TP_fast_assign( + __entry->xid = xid; + __entry->fid = fid; + __entry->tid = tid; + __entry->sesid = sesid; + ), + TP_printk("\txid=%u sid=0x%llx tid=0x%x fid=0x%llx", + __entry->xid, __entry->sesid, __entry->tid, __entry->fid) +) + +#define DEFINE_SMB3_FD_EVENT(name) \ +DEFINE_EVENT(smb3_fd_class, smb3_##name, \ + TP_PROTO(unsigned int xid, \ + __u64 fid, \ + __u32 tid, \ + __u64 sesid), \ + TP_ARGS(xid, fid, tid, sesid)) + +DEFINE_SMB3_FD_EVENT(flush_enter); +DEFINE_SMB3_FD_EVENT(flush_done); +DEFINE_SMB3_FD_EVENT(close_enter); +DEFINE_SMB3_FD_EVENT(close_done); +DEFINE_SMB3_FD_EVENT(oplock_not_found); + +DECLARE_EVENT_CLASS(smb3_fd_err_class, + TP_PROTO(unsigned int xid, + __u64 fid, + __u32 tid, + __u64 sesid, + int rc), + TP_ARGS(xid, fid, tid, sesid, rc), + TP_STRUCT__entry( + __field(unsigned int, xid) + __field(__u64, fid) + __field(__u32, tid) + __field(__u64, sesid) + __field(int, rc) + ), + TP_fast_assign( + __entry->xid = xid; + __entry->fid = fid; + __entry->tid = tid; + __entry->sesid = sesid; + __entry->rc = rc; + ), + TP_printk("\txid=%u sid=0x%llx tid=0x%x fid=0x%llx rc=%d", + __entry->xid, __entry->sesid, __entry->tid, __entry->fid, + __entry->rc) +) + +#define DEFINE_SMB3_FD_ERR_EVENT(name) \ +DEFINE_EVENT(smb3_fd_err_class, smb3_##name, \ + TP_PROTO(unsigned int xid, \ + __u64 fid, \ + __u32 tid, \ + __u64 sesid, \ + int rc), \ + TP_ARGS(xid, fid, tid, sesid, rc)) + +DEFINE_SMB3_FD_ERR_EVENT(flush_err); +DEFINE_SMB3_FD_ERR_EVENT(lock_err); +DEFINE_SMB3_FD_ERR_EVENT(close_err); + +/* + * For handle based query/set info calls + */ +DECLARE_EVENT_CLASS(smb3_inf_enter_class, + TP_PROTO(unsigned int xid, + __u64 fid, + __u32 tid, + __u64 sesid, + __u8 infclass, + __u32 type), + TP_ARGS(xid, fid, tid, sesid, infclass, type), + TP_STRUCT__entry( + __field(unsigned int, xid) + __field(__u64, fid) + __field(__u32, tid) + __field(__u64, sesid) + __field(__u8, infclass) + __field(__u32, type) + ), + TP_fast_assign( + __entry->xid = xid; + __entry->fid = fid; + __entry->tid = tid; + __entry->sesid = sesid; + __entry->infclass = infclass; + __entry->type = type; + ), + TP_printk("xid=%u sid=0x%llx tid=0x%x fid=0x%llx class=%u type=0x%x", + __entry->xid, __entry->sesid, __entry->tid, __entry->fid, + __entry->infclass, __entry->type) +) + +#define DEFINE_SMB3_INF_ENTER_EVENT(name) \ +DEFINE_EVENT(smb3_inf_enter_class, smb3_##name, \ + TP_PROTO(unsigned int xid, \ + __u64 fid, \ + __u32 tid, \ + __u64 sesid, \ + __u8 infclass, \ + __u32 type), \ + TP_ARGS(xid, fid, tid, sesid, infclass, type)) + +DEFINE_SMB3_INF_ENTER_EVENT(query_info_enter); +DEFINE_SMB3_INF_ENTER_EVENT(query_info_done); +DEFINE_SMB3_INF_ENTER_EVENT(notify_enter); +DEFINE_SMB3_INF_ENTER_EVENT(notify_done); + +DECLARE_EVENT_CLASS(smb3_inf_err_class, + TP_PROTO(unsigned int xid, + __u64 fid, + __u32 tid, + __u64 sesid, + __u8 infclass, + __u32 type, + int rc), + TP_ARGS(xid, fid, tid, sesid, infclass, type, rc), + TP_STRUCT__entry( + __field(unsigned int, xid) + __field(__u64, fid) + __field(__u32, tid) + __field(__u64, sesid) + __field(__u8, infclass) + __field(__u32, type) + __field(int, rc) + ), + TP_fast_assign( + __entry->xid = xid; + __entry->fid = fid; + __entry->tid = tid; + __entry->sesid = sesid; + __entry->infclass = infclass; + __entry->type = type; + __entry->rc = rc; + ), + TP_printk("xid=%u sid=0x%llx tid=0x%x fid=0x%llx class=%u type=0x%x rc=%d", + __entry->xid, __entry->sesid, __entry->tid, __entry->fid, + __entry->infclass, __entry->type, __entry->rc) +) + +#define DEFINE_SMB3_INF_ERR_EVENT(name) \ +DEFINE_EVENT(smb3_inf_err_class, smb3_##name, \ + TP_PROTO(unsigned int xid, \ + __u64 fid, \ + __u32 tid, \ + __u64 sesid, \ + __u8 infclass, \ + __u32 type, \ + int rc), \ + TP_ARGS(xid, fid, tid, sesid, infclass, type, rc)) + +DEFINE_SMB3_INF_ERR_EVENT(query_info_err); +DEFINE_SMB3_INF_ERR_EVENT(set_info_err); +DEFINE_SMB3_INF_ERR_EVENT(notify_err); +DEFINE_SMB3_INF_ERR_EVENT(fsctl_err); + +DECLARE_EVENT_CLASS(smb3_inf_compound_enter_class, + TP_PROTO(unsigned int xid, + __u32 tid, + __u64 sesid, + const char *full_path), + TP_ARGS(xid, tid, sesid, full_path), + TP_STRUCT__entry( + __field(unsigned int, xid) + __field(__u32, tid) + __field(__u64, sesid) + __string(path, full_path) + ), + TP_fast_assign( + __entry->xid = xid; + __entry->tid = tid; + __entry->sesid = sesid; + __assign_str(path, full_path); + ), + TP_printk("xid=%u sid=0x%llx tid=0x%x path=%s", + __entry->xid, __entry->sesid, __entry->tid, + __get_str(path)) +) + +#define DEFINE_SMB3_INF_COMPOUND_ENTER_EVENT(name) \ +DEFINE_EVENT(smb3_inf_compound_enter_class, smb3_##name, \ + TP_PROTO(unsigned int xid, \ + __u32 tid, \ + __u64 sesid, \ + const char *full_path), \ + TP_ARGS(xid, tid, sesid, full_path)) + +DEFINE_SMB3_INF_COMPOUND_ENTER_EVENT(query_info_compound_enter); +DEFINE_SMB3_INF_COMPOUND_ENTER_EVENT(posix_query_info_compound_enter); +DEFINE_SMB3_INF_COMPOUND_ENTER_EVENT(hardlink_enter); +DEFINE_SMB3_INF_COMPOUND_ENTER_EVENT(rename_enter); +DEFINE_SMB3_INF_COMPOUND_ENTER_EVENT(rmdir_enter); +DEFINE_SMB3_INF_COMPOUND_ENTER_EVENT(set_eof_enter); +DEFINE_SMB3_INF_COMPOUND_ENTER_EVENT(set_info_compound_enter); +DEFINE_SMB3_INF_COMPOUND_ENTER_EVENT(delete_enter); +DEFINE_SMB3_INF_COMPOUND_ENTER_EVENT(mkdir_enter); +DEFINE_SMB3_INF_COMPOUND_ENTER_EVENT(tdis_enter); + + +DECLARE_EVENT_CLASS(smb3_inf_compound_done_class, + TP_PROTO(unsigned int xid, + __u32 tid, + __u64 sesid), + TP_ARGS(xid, tid, sesid), + TP_STRUCT__entry( + __field(unsigned int, xid) + __field(__u32, tid) + __field(__u64, sesid) + ), + TP_fast_assign( + __entry->xid = xid; + __entry->tid = tid; + __entry->sesid = sesid; + ), + TP_printk("xid=%u sid=0x%llx tid=0x%x", + __entry->xid, __entry->sesid, __entry->tid) +) + +#define DEFINE_SMB3_INF_COMPOUND_DONE_EVENT(name) \ +DEFINE_EVENT(smb3_inf_compound_done_class, smb3_##name, \ + TP_PROTO(unsigned int xid, \ + __u32 tid, \ + __u64 sesid), \ + TP_ARGS(xid, tid, sesid)) + +DEFINE_SMB3_INF_COMPOUND_DONE_EVENT(query_info_compound_done); +DEFINE_SMB3_INF_COMPOUND_DONE_EVENT(posix_query_info_compound_done); +DEFINE_SMB3_INF_COMPOUND_DONE_EVENT(hardlink_done); +DEFINE_SMB3_INF_COMPOUND_DONE_EVENT(rename_done); +DEFINE_SMB3_INF_COMPOUND_DONE_EVENT(rmdir_done); +DEFINE_SMB3_INF_COMPOUND_DONE_EVENT(set_eof_done); +DEFINE_SMB3_INF_COMPOUND_DONE_EVENT(set_info_compound_done); +DEFINE_SMB3_INF_COMPOUND_DONE_EVENT(delete_done); +DEFINE_SMB3_INF_COMPOUND_DONE_EVENT(mkdir_done); +DEFINE_SMB3_INF_COMPOUND_DONE_EVENT(tdis_done); + + +DECLARE_EVENT_CLASS(smb3_inf_compound_err_class, + TP_PROTO(unsigned int xid, + __u32 tid, + __u64 sesid, + int rc), + TP_ARGS(xid, tid, sesid, rc), + TP_STRUCT__entry( + __field(unsigned int, xid) + __field(__u32, tid) + __field(__u64, sesid) + __field(int, rc) + ), + TP_fast_assign( + __entry->xid = xid; + __entry->tid = tid; + __entry->sesid = sesid; + __entry->rc = rc; + ), + TP_printk("xid=%u sid=0x%llx tid=0x%x rc=%d", + __entry->xid, __entry->sesid, __entry->tid, + __entry->rc) +) + +#define DEFINE_SMB3_INF_COMPOUND_ERR_EVENT(name) \ +DEFINE_EVENT(smb3_inf_compound_err_class, smb3_##name, \ + TP_PROTO(unsigned int xid, \ + __u32 tid, \ + __u64 sesid, \ + int rc), \ + TP_ARGS(xid, tid, sesid, rc)) + +DEFINE_SMB3_INF_COMPOUND_ERR_EVENT(query_info_compound_err); +DEFINE_SMB3_INF_COMPOUND_ERR_EVENT(posix_query_info_compound_err); +DEFINE_SMB3_INF_COMPOUND_ERR_EVENT(hardlink_err); +DEFINE_SMB3_INF_COMPOUND_ERR_EVENT(rename_err); +DEFINE_SMB3_INF_COMPOUND_ERR_EVENT(rmdir_err); +DEFINE_SMB3_INF_COMPOUND_ERR_EVENT(set_eof_err); +DEFINE_SMB3_INF_COMPOUND_ERR_EVENT(set_info_compound_err); +DEFINE_SMB3_INF_COMPOUND_ERR_EVENT(mkdir_err); +DEFINE_SMB3_INF_COMPOUND_ERR_EVENT(delete_err); +DEFINE_SMB3_INF_COMPOUND_ERR_EVENT(tdis_err); + +/* + * For logging SMB3 Status code and Command for responses which return errors + */ +DECLARE_EVENT_CLASS(smb3_cmd_err_class, + TP_PROTO(__u32 tid, + __u64 sesid, + __u16 cmd, + __u64 mid, + __u32 status, + int rc), + TP_ARGS(tid, sesid, cmd, mid, status, rc), + TP_STRUCT__entry( + __field(__u32, tid) + __field(__u64, sesid) + __field(__u16, cmd) + __field(__u64, mid) + __field(__u32, status) + __field(int, rc) + ), + TP_fast_assign( + __entry->tid = tid; + __entry->sesid = sesid; + __entry->cmd = cmd; + __entry->mid = mid; + __entry->status = status; + __entry->rc = rc; + ), + TP_printk("\tsid=0x%llx tid=0x%x cmd=%u mid=%llu status=0x%x rc=%d", + __entry->sesid, __entry->tid, __entry->cmd, __entry->mid, + __entry->status, __entry->rc) +) + +#define DEFINE_SMB3_CMD_ERR_EVENT(name) \ +DEFINE_EVENT(smb3_cmd_err_class, smb3_##name, \ + TP_PROTO(__u32 tid, \ + __u64 sesid, \ + __u16 cmd, \ + __u64 mid, \ + __u32 status, \ + int rc), \ + TP_ARGS(tid, sesid, cmd, mid, status, rc)) + +DEFINE_SMB3_CMD_ERR_EVENT(cmd_err); + +DECLARE_EVENT_CLASS(smb3_cmd_done_class, + TP_PROTO(__u32 tid, + __u64 sesid, + __u16 cmd, + __u64 mid), + TP_ARGS(tid, sesid, cmd, mid), + TP_STRUCT__entry( + __field(__u32, tid) + __field(__u64, sesid) + __field(__u16, cmd) + __field(__u64, mid) + ), + TP_fast_assign( + __entry->tid = tid; + __entry->sesid = sesid; + __entry->cmd = cmd; + __entry->mid = mid; + ), + TP_printk("\tsid=0x%llx tid=0x%x cmd=%u mid=%llu", + __entry->sesid, __entry->tid, + __entry->cmd, __entry->mid) +) + +#define DEFINE_SMB3_CMD_DONE_EVENT(name) \ +DEFINE_EVENT(smb3_cmd_done_class, smb3_##name, \ + TP_PROTO(__u32 tid, \ + __u64 sesid, \ + __u16 cmd, \ + __u64 mid), \ + TP_ARGS(tid, sesid, cmd, mid)) + +DEFINE_SMB3_CMD_DONE_EVENT(cmd_enter); +DEFINE_SMB3_CMD_DONE_EVENT(cmd_done); +DEFINE_SMB3_CMD_DONE_EVENT(ses_expired); + +DECLARE_EVENT_CLASS(smb3_mid_class, + TP_PROTO(__u16 cmd, + __u64 mid, + __u32 pid, + unsigned long when_sent, + unsigned long when_received), + TP_ARGS(cmd, mid, pid, when_sent, when_received), + TP_STRUCT__entry( + __field(__u16, cmd) + __field(__u64, mid) + __field(__u32, pid) + __field(unsigned long, when_sent) + __field(unsigned long, when_received) + ), + TP_fast_assign( + __entry->cmd = cmd; + __entry->mid = mid; + __entry->pid = pid; + __entry->when_sent = when_sent; + __entry->when_received = when_received; + ), + TP_printk("\tcmd=%u mid=%llu pid=%u, when_sent=%lu when_rcv=%lu", + __entry->cmd, __entry->mid, __entry->pid, __entry->when_sent, + __entry->when_received) +) + +#define DEFINE_SMB3_MID_EVENT(name) \ +DEFINE_EVENT(smb3_mid_class, smb3_##name, \ + TP_PROTO(__u16 cmd, \ + __u64 mid, \ + __u32 pid, \ + unsigned long when_sent, \ + unsigned long when_received), \ + TP_ARGS(cmd, mid, pid, when_sent, when_received)) + +DEFINE_SMB3_MID_EVENT(slow_rsp); + +DECLARE_EVENT_CLASS(smb3_exit_err_class, + TP_PROTO(unsigned int xid, + const char *func_name, + int rc), + TP_ARGS(xid, func_name, rc), + TP_STRUCT__entry( + __field(unsigned int, xid) + __string(func_name, func_name) + __field(int, rc) + ), + TP_fast_assign( + __entry->xid = xid; + __assign_str(func_name, func_name); + __entry->rc = rc; + ), + TP_printk("\t%s: xid=%u rc=%d", + __get_str(func_name), __entry->xid, __entry->rc) +) + +#define DEFINE_SMB3_EXIT_ERR_EVENT(name) \ +DEFINE_EVENT(smb3_exit_err_class, smb3_##name, \ + TP_PROTO(unsigned int xid, \ + const char *func_name, \ + int rc), \ + TP_ARGS(xid, func_name, rc)) + +DEFINE_SMB3_EXIT_ERR_EVENT(exit_err); + + +DECLARE_EVENT_CLASS(smb3_sync_err_class, + TP_PROTO(unsigned long ino, + int rc), + TP_ARGS(ino, rc), + TP_STRUCT__entry( + __field(unsigned long, ino) + __field(int, rc) + ), + TP_fast_assign( + __entry->ino = ino; + __entry->rc = rc; + ), + TP_printk("\tino=%lu rc=%d", + __entry->ino, __entry->rc) +) + +#define DEFINE_SMB3_SYNC_ERR_EVENT(name) \ +DEFINE_EVENT(smb3_sync_err_class, cifs_##name, \ + TP_PROTO(unsigned long ino, \ + int rc), \ + TP_ARGS(ino, rc)) + +DEFINE_SMB3_SYNC_ERR_EVENT(fsync_err); +DEFINE_SMB3_SYNC_ERR_EVENT(flush_err); + + +DECLARE_EVENT_CLASS(smb3_enter_exit_class, + TP_PROTO(unsigned int xid, + const char *func_name), + TP_ARGS(xid, func_name), + TP_STRUCT__entry( + __field(unsigned int, xid) + __string(func_name, func_name) + ), + TP_fast_assign( + __entry->xid = xid; + __assign_str(func_name, func_name); + ), + TP_printk("\t%s: xid=%u", + __get_str(func_name), __entry->xid) +) + +#define DEFINE_SMB3_ENTER_EXIT_EVENT(name) \ +DEFINE_EVENT(smb3_enter_exit_class, smb3_##name, \ + TP_PROTO(unsigned int xid, \ + const char *func_name), \ + TP_ARGS(xid, func_name)) + +DEFINE_SMB3_ENTER_EXIT_EVENT(enter); +DEFINE_SMB3_ENTER_EXIT_EVENT(exit_done); + +/* + * For SMB2/SMB3 tree connect + */ + +DECLARE_EVENT_CLASS(smb3_tcon_class, + TP_PROTO(unsigned int xid, + __u32 tid, + __u64 sesid, + const char *unc_name, + int rc), + TP_ARGS(xid, tid, sesid, unc_name, rc), + TP_STRUCT__entry( + __field(unsigned int, xid) + __field(__u32, tid) + __field(__u64, sesid) + __string(name, unc_name) + __field(int, rc) + ), + TP_fast_assign( + __entry->xid = xid; + __entry->tid = tid; + __entry->sesid = sesid; + __assign_str(name, unc_name); + __entry->rc = rc; + ), + TP_printk("xid=%u sid=0x%llx tid=0x%x unc_name=%s rc=%d", + __entry->xid, __entry->sesid, __entry->tid, + __get_str(name), __entry->rc) +) + +#define DEFINE_SMB3_TCON_EVENT(name) \ +DEFINE_EVENT(smb3_tcon_class, smb3_##name, \ + TP_PROTO(unsigned int xid, \ + __u32 tid, \ + __u64 sesid, \ + const char *unc_name, \ + int rc), \ + TP_ARGS(xid, tid, sesid, unc_name, rc)) + +DEFINE_SMB3_TCON_EVENT(tcon); + + +/* + * For smb2/smb3 open (including create and mkdir) calls + */ + +DECLARE_EVENT_CLASS(smb3_open_enter_class, + TP_PROTO(unsigned int xid, + __u32 tid, + __u64 sesid, + const char *full_path, + int create_options, + int desired_access), + TP_ARGS(xid, tid, sesid, full_path, create_options, desired_access), + TP_STRUCT__entry( + __field(unsigned int, xid) + __field(__u32, tid) + __field(__u64, sesid) + __string(path, full_path) + __field(int, create_options) + __field(int, desired_access) + ), + TP_fast_assign( + __entry->xid = xid; + __entry->tid = tid; + __entry->sesid = sesid; + __assign_str(path, full_path); + __entry->create_options = create_options; + __entry->desired_access = desired_access; + ), + TP_printk("xid=%u sid=0x%llx tid=0x%x path=%s cr_opts=0x%x des_access=0x%x", + __entry->xid, __entry->sesid, __entry->tid, __get_str(path), + __entry->create_options, __entry->desired_access) +) + +#define DEFINE_SMB3_OPEN_ENTER_EVENT(name) \ +DEFINE_EVENT(smb3_open_enter_class, smb3_##name, \ + TP_PROTO(unsigned int xid, \ + __u32 tid, \ + __u64 sesid, \ + const char *full_path, \ + int create_options, \ + int desired_access), \ + TP_ARGS(xid, tid, sesid, full_path, create_options, desired_access)) + +DEFINE_SMB3_OPEN_ENTER_EVENT(open_enter); +DEFINE_SMB3_OPEN_ENTER_EVENT(posix_mkdir_enter); + +DECLARE_EVENT_CLASS(smb3_open_err_class, + TP_PROTO(unsigned int xid, + __u32 tid, + __u64 sesid, + int create_options, + int desired_access, + int rc), + TP_ARGS(xid, tid, sesid, create_options, desired_access, rc), + TP_STRUCT__entry( + __field(unsigned int, xid) + __field(__u32, tid) + __field(__u64, sesid) + __field(int, create_options) + __field(int, desired_access) + __field(int, rc) + ), + TP_fast_assign( + __entry->xid = xid; + __entry->tid = tid; + __entry->sesid = sesid; + __entry->create_options = create_options; + __entry->desired_access = desired_access; + __entry->rc = rc; + ), + TP_printk("xid=%u sid=0x%llx tid=0x%x cr_opts=0x%x des_access=0x%x rc=%d", + __entry->xid, __entry->sesid, __entry->tid, + __entry->create_options, __entry->desired_access, __entry->rc) +) + +#define DEFINE_SMB3_OPEN_ERR_EVENT(name) \ +DEFINE_EVENT(smb3_open_err_class, smb3_##name, \ + TP_PROTO(unsigned int xid, \ + __u32 tid, \ + __u64 sesid, \ + int create_options, \ + int desired_access, \ + int rc), \ + TP_ARGS(xid, tid, sesid, create_options, desired_access, rc)) + +DEFINE_SMB3_OPEN_ERR_EVENT(open_err); +DEFINE_SMB3_OPEN_ERR_EVENT(posix_mkdir_err); + +DECLARE_EVENT_CLASS(smb3_open_done_class, + TP_PROTO(unsigned int xid, + __u64 fid, + __u32 tid, + __u64 sesid, + int create_options, + int desired_access), + TP_ARGS(xid, fid, tid, sesid, create_options, desired_access), + TP_STRUCT__entry( + __field(unsigned int, xid) + __field(__u64, fid) + __field(__u32, tid) + __field(__u64, sesid) + __field(int, create_options) + __field(int, desired_access) + ), + TP_fast_assign( + __entry->xid = xid; + __entry->fid = fid; + __entry->tid = tid; + __entry->sesid = sesid; + __entry->create_options = create_options; + __entry->desired_access = desired_access; + ), + TP_printk("xid=%u sid=0x%llx tid=0x%x fid=0x%llx cr_opts=0x%x des_access=0x%x", + __entry->xid, __entry->sesid, __entry->tid, __entry->fid, + __entry->create_options, __entry->desired_access) +) + +#define DEFINE_SMB3_OPEN_DONE_EVENT(name) \ +DEFINE_EVENT(smb3_open_done_class, smb3_##name, \ + TP_PROTO(unsigned int xid, \ + __u64 fid, \ + __u32 tid, \ + __u64 sesid, \ + int create_options, \ + int desired_access), \ + TP_ARGS(xid, fid, tid, sesid, create_options, desired_access)) + +DEFINE_SMB3_OPEN_DONE_EVENT(open_done); +DEFINE_SMB3_OPEN_DONE_EVENT(posix_mkdir_done); + + +DECLARE_EVENT_CLASS(smb3_lease_done_class, + TP_PROTO(__u32 lease_state, + __u32 tid, + __u64 sesid, + __u64 lease_key_low, + __u64 lease_key_high), + TP_ARGS(lease_state, tid, sesid, lease_key_low, lease_key_high), + TP_STRUCT__entry( + __field(__u32, lease_state) + __field(__u32, tid) + __field(__u64, sesid) + __field(__u64, lease_key_low) + __field(__u64, lease_key_high) + ), + TP_fast_assign( + __entry->lease_state = lease_state; + __entry->tid = tid; + __entry->sesid = sesid; + __entry->lease_key_low = lease_key_low; + __entry->lease_key_high = lease_key_high; + ), + TP_printk("sid=0x%llx tid=0x%x lease_key=0x%llx%llx lease_state=0x%x", + __entry->sesid, __entry->tid, __entry->lease_key_high, + __entry->lease_key_low, __entry->lease_state) +) + +#define DEFINE_SMB3_LEASE_DONE_EVENT(name) \ +DEFINE_EVENT(smb3_lease_done_class, smb3_##name, \ + TP_PROTO(__u32 lease_state, \ + __u32 tid, \ + __u64 sesid, \ + __u64 lease_key_low, \ + __u64 lease_key_high), \ + TP_ARGS(lease_state, tid, sesid, lease_key_low, lease_key_high)) + +DEFINE_SMB3_LEASE_DONE_EVENT(lease_done); +DEFINE_SMB3_LEASE_DONE_EVENT(lease_not_found); + +DECLARE_EVENT_CLASS(smb3_lease_err_class, + TP_PROTO(__u32 lease_state, + __u32 tid, + __u64 sesid, + __u64 lease_key_low, + __u64 lease_key_high, + int rc), + TP_ARGS(lease_state, tid, sesid, lease_key_low, lease_key_high, rc), + TP_STRUCT__entry( + __field(__u32, lease_state) + __field(__u32, tid) + __field(__u64, sesid) + __field(__u64, lease_key_low) + __field(__u64, lease_key_high) + __field(int, rc) + ), + TP_fast_assign( + __entry->lease_state = lease_state; + __entry->tid = tid; + __entry->sesid = sesid; + __entry->lease_key_low = lease_key_low; + __entry->lease_key_high = lease_key_high; + __entry->rc = rc; + ), + TP_printk("sid=0x%llx tid=0x%x lease_key=0x%llx%llx lease_state=0x%x rc=%d", + __entry->sesid, __entry->tid, __entry->lease_key_high, + __entry->lease_key_low, __entry->lease_state, __entry->rc) +) + +#define DEFINE_SMB3_LEASE_ERR_EVENT(name) \ +DEFINE_EVENT(smb3_lease_err_class, smb3_##name, \ + TP_PROTO(__u32 lease_state, \ + __u32 tid, \ + __u64 sesid, \ + __u64 lease_key_low, \ + __u64 lease_key_high, \ + int rc), \ + TP_ARGS(lease_state, tid, sesid, lease_key_low, lease_key_high, rc)) + +DEFINE_SMB3_LEASE_ERR_EVENT(lease_err); + +DECLARE_EVENT_CLASS(smb3_connect_class, + TP_PROTO(char *hostname, + __u64 conn_id, + const struct __kernel_sockaddr_storage *dst_addr), + TP_ARGS(hostname, conn_id, dst_addr), + TP_STRUCT__entry( + __string(hostname, hostname) + __field(__u64, conn_id) + __array(__u8, dst_addr, sizeof(struct sockaddr_storage)) + ), + TP_fast_assign( + struct sockaddr_storage *pss = NULL; + + __entry->conn_id = conn_id; + pss = (struct sockaddr_storage *)__entry->dst_addr; + *pss = *dst_addr; + __assign_str(hostname, hostname); + ), + TP_printk("conn_id=0x%llx server=%s addr=%pISpsfc", + __entry->conn_id, + __get_str(hostname), + __entry->dst_addr) +) + +#define DEFINE_SMB3_CONNECT_EVENT(name) \ +DEFINE_EVENT(smb3_connect_class, smb3_##name, \ + TP_PROTO(char *hostname, \ + __u64 conn_id, \ + const struct __kernel_sockaddr_storage *addr), \ + TP_ARGS(hostname, conn_id, addr)) + +DEFINE_SMB3_CONNECT_EVENT(connect_done); + +DECLARE_EVENT_CLASS(smb3_connect_err_class, + TP_PROTO(char *hostname, __u64 conn_id, + const struct __kernel_sockaddr_storage *dst_addr, int rc), + TP_ARGS(hostname, conn_id, dst_addr, rc), + TP_STRUCT__entry( + __string(hostname, hostname) + __field(__u64, conn_id) + __array(__u8, dst_addr, sizeof(struct sockaddr_storage)) + __field(int, rc) + ), + TP_fast_assign( + struct sockaddr_storage *pss = NULL; + + __entry->conn_id = conn_id; + __entry->rc = rc; + pss = (struct sockaddr_storage *)__entry->dst_addr; + *pss = *dst_addr; + __assign_str(hostname, hostname); + ), + TP_printk("rc=%d conn_id=0x%llx server=%s addr=%pISpsfc", + __entry->rc, + __entry->conn_id, + __get_str(hostname), + __entry->dst_addr) +) + +#define DEFINE_SMB3_CONNECT_ERR_EVENT(name) \ +DEFINE_EVENT(smb3_connect_err_class, smb3_##name, \ + TP_PROTO(char *hostname, \ + __u64 conn_id, \ + const struct __kernel_sockaddr_storage *addr, \ + int rc), \ + TP_ARGS(hostname, conn_id, addr, rc)) + +DEFINE_SMB3_CONNECT_ERR_EVENT(connect_err); + +DECLARE_EVENT_CLASS(smb3_reconnect_class, + TP_PROTO(__u64 currmid, + __u64 conn_id, + char *hostname), + TP_ARGS(currmid, conn_id, hostname), + TP_STRUCT__entry( + __field(__u64, currmid) + __field(__u64, conn_id) + __string(hostname, hostname) + ), + TP_fast_assign( + __entry->currmid = currmid; + __entry->conn_id = conn_id; + __assign_str(hostname, hostname); + ), + TP_printk("conn_id=0x%llx server=%s current_mid=%llu", + __entry->conn_id, + __get_str(hostname), + __entry->currmid) +) + +#define DEFINE_SMB3_RECONNECT_EVENT(name) \ +DEFINE_EVENT(smb3_reconnect_class, smb3_##name, \ + TP_PROTO(__u64 currmid, \ + __u64 conn_id, \ + char *hostname), \ + TP_ARGS(currmid, conn_id, hostname)) + +DEFINE_SMB3_RECONNECT_EVENT(reconnect); +DEFINE_SMB3_RECONNECT_EVENT(partial_send_reconnect); + +DECLARE_EVENT_CLASS(smb3_credit_class, + TP_PROTO(__u64 currmid, + __u64 conn_id, + char *hostname, + int credits, + int credits_to_add, + int in_flight), + TP_ARGS(currmid, conn_id, hostname, credits, credits_to_add, in_flight), + TP_STRUCT__entry( + __field(__u64, currmid) + __field(__u64, conn_id) + __string(hostname, hostname) + __field(int, credits) + __field(int, credits_to_add) + __field(int, in_flight) + ), + TP_fast_assign( + __entry->currmid = currmid; + __entry->conn_id = conn_id; + __assign_str(hostname, hostname); + __entry->credits = credits; + __entry->credits_to_add = credits_to_add; + __entry->in_flight = in_flight; + ), + TP_printk("conn_id=0x%llx server=%s current_mid=%llu " + "credits=%d credit_change=%d in_flight=%d", + __entry->conn_id, + __get_str(hostname), + __entry->currmid, + __entry->credits, + __entry->credits_to_add, + __entry->in_flight) +) + +#define DEFINE_SMB3_CREDIT_EVENT(name) \ +DEFINE_EVENT(smb3_credit_class, smb3_##name, \ + TP_PROTO(__u64 currmid, \ + __u64 conn_id, \ + char *hostname, \ + int credits, \ + int credits_to_add, \ + int in_flight), \ + TP_ARGS(currmid, conn_id, hostname, credits, credits_to_add, in_flight)) + +DEFINE_SMB3_CREDIT_EVENT(reconnect_with_invalid_credits); +DEFINE_SMB3_CREDIT_EVENT(reconnect_detected); +DEFINE_SMB3_CREDIT_EVENT(credit_timeout); +DEFINE_SMB3_CREDIT_EVENT(insufficient_credits); +DEFINE_SMB3_CREDIT_EVENT(too_many_credits); +DEFINE_SMB3_CREDIT_EVENT(add_credits); +DEFINE_SMB3_CREDIT_EVENT(adj_credits); +DEFINE_SMB3_CREDIT_EVENT(hdr_credits); +DEFINE_SMB3_CREDIT_EVENT(nblk_credits); +DEFINE_SMB3_CREDIT_EVENT(pend_credits); +DEFINE_SMB3_CREDIT_EVENT(wait_credits); +DEFINE_SMB3_CREDIT_EVENT(waitff_credits); +DEFINE_SMB3_CREDIT_EVENT(overflow_credits); +DEFINE_SMB3_CREDIT_EVENT(set_credits); + +#endif /* _CIFS_TRACE_H */ + +#undef TRACE_INCLUDE_PATH +#define TRACE_INCLUDE_PATH . +#define TRACE_INCLUDE_FILE trace +#include diff --git a/fs/smb/client/transport.c b/fs/smb/client/transport.c new file mode 100644 index 0000000000000..c961b90f92b9f --- /dev/null +++ b/fs/smb/client/transport.c @@ -0,0 +1,1807 @@ +// SPDX-License-Identifier: LGPL-2.1 +/* + * + * Copyright (C) International Business Machines Corp., 2002,2008 + * Author(s): Steve French (sfrench@us.ibm.com) + * Jeremy Allison (jra@samba.org) 2006. + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "cifspdu.h" +#include "cifsglob.h" +#include "cifsproto.h" +#include "cifs_debug.h" +#include "smb2proto.h" +#include "smbdirect.h" + +/* Max number of iovectors we can use off the stack when sending requests. */ +#define CIFS_MAX_IOV_SIZE 8 + +void +cifs_wake_up_task(struct mid_q_entry *mid) +{ + wake_up_process(mid->callback_data); +} + +static struct mid_q_entry * +alloc_mid(const struct smb_hdr *smb_buffer, struct TCP_Server_Info *server) +{ + struct mid_q_entry *temp; + + if (server == NULL) { + cifs_dbg(VFS, "%s: null TCP session\n", __func__); + return NULL; + } + + temp = mempool_alloc(cifs_mid_poolp, GFP_NOFS); + memset(temp, 0, sizeof(struct mid_q_entry)); + kref_init(&temp->refcount); + temp->mid = get_mid(smb_buffer); + temp->pid = current->pid; + temp->command = cpu_to_le16(smb_buffer->Command); + cifs_dbg(FYI, "For smb_command %d\n", smb_buffer->Command); + /* do_gettimeofday(&temp->when_sent);*/ /* easier to use jiffies */ + /* when mid allocated can be before when sent */ + temp->when_alloc = jiffies; + temp->server = server; + + /* + * The default is for the mid to be synchronous, so the + * default callback just wakes up the current task. + */ + get_task_struct(current); + temp->creator = current; + temp->callback = cifs_wake_up_task; + temp->callback_data = current; + + atomic_inc(&mid_count); + temp->mid_state = MID_REQUEST_ALLOCATED; + return temp; +} + +static void __release_mid(struct kref *refcount) +{ + struct mid_q_entry *midEntry = + container_of(refcount, struct mid_q_entry, refcount); +#ifdef CONFIG_CIFS_STATS2 + __le16 command = midEntry->server->vals->lock_cmd; + __u16 smb_cmd = le16_to_cpu(midEntry->command); + unsigned long now; + unsigned long roundtrip_time; +#endif + struct TCP_Server_Info *server = midEntry->server; + + if (midEntry->resp_buf && (midEntry->mid_flags & MID_WAIT_CANCELLED) && + midEntry->mid_state == MID_RESPONSE_RECEIVED && + server->ops->handle_cancelled_mid) + server->ops->handle_cancelled_mid(midEntry, server); + + midEntry->mid_state = MID_FREE; + atomic_dec(&mid_count); + if (midEntry->large_buf) + cifs_buf_release(midEntry->resp_buf); + else + cifs_small_buf_release(midEntry->resp_buf); +#ifdef CONFIG_CIFS_STATS2 + now = jiffies; + if (now < midEntry->when_alloc) + cifs_server_dbg(VFS, "Invalid mid allocation time\n"); + roundtrip_time = now - midEntry->when_alloc; + + if (smb_cmd < NUMBER_OF_SMB2_COMMANDS) { + if (atomic_read(&server->num_cmds[smb_cmd]) == 0) { + server->slowest_cmd[smb_cmd] = roundtrip_time; + server->fastest_cmd[smb_cmd] = roundtrip_time; + } else { + if (server->slowest_cmd[smb_cmd] < roundtrip_time) + server->slowest_cmd[smb_cmd] = roundtrip_time; + else if (server->fastest_cmd[smb_cmd] > roundtrip_time) + server->fastest_cmd[smb_cmd] = roundtrip_time; + } + cifs_stats_inc(&server->num_cmds[smb_cmd]); + server->time_per_cmd[smb_cmd] += roundtrip_time; + } + /* + * commands taking longer than one second (default) can be indications + * that something is wrong, unless it is quite a slow link or a very + * busy server. Note that this calc is unlikely or impossible to wrap + * as long as slow_rsp_threshold is not set way above recommended max + * value (32767 ie 9 hours) and is generally harmless even if wrong + * since only affects debug counters - so leaving the calc as simple + * comparison rather than doing multiple conversions and overflow + * checks + */ + if ((slow_rsp_threshold != 0) && + time_after(now, midEntry->when_alloc + (slow_rsp_threshold * HZ)) && + (midEntry->command != command)) { + /* + * smb2slowcmd[NUMBER_OF_SMB2_COMMANDS] counts by command + * NB: le16_to_cpu returns unsigned so can not be negative below + */ + if (smb_cmd < NUMBER_OF_SMB2_COMMANDS) + cifs_stats_inc(&server->smb2slowcmd[smb_cmd]); + + trace_smb3_slow_rsp(smb_cmd, midEntry->mid, midEntry->pid, + midEntry->when_sent, midEntry->when_received); + if (cifsFYI & CIFS_TIMER) { + pr_debug("slow rsp: cmd %d mid %llu", + midEntry->command, midEntry->mid); + cifs_info("A: 0x%lx S: 0x%lx R: 0x%lx\n", + now - midEntry->when_alloc, + now - midEntry->when_sent, + now - midEntry->when_received); + } + } +#endif + put_task_struct(midEntry->creator); + + mempool_free(midEntry, cifs_mid_poolp); +} + +void release_mid(struct mid_q_entry *mid) +{ + struct TCP_Server_Info *server = mid->server; + + spin_lock(&server->mid_lock); + kref_put(&mid->refcount, __release_mid); + spin_unlock(&server->mid_lock); +} + +void +delete_mid(struct mid_q_entry *mid) +{ + spin_lock(&mid->server->mid_lock); + if (!(mid->mid_flags & MID_DELETED)) { + list_del_init(&mid->qhead); + mid->mid_flags |= MID_DELETED; + } + spin_unlock(&mid->server->mid_lock); + + release_mid(mid); +} + +/* + * smb_send_kvec - send an array of kvecs to the server + * @server: Server to send the data to + * @smb_msg: Message to send + * @sent: amount of data sent on socket is stored here + * + * Our basic "send data to server" function. Should be called with srv_mutex + * held. The caller is responsible for handling the results. + */ +static int +smb_send_kvec(struct TCP_Server_Info *server, struct msghdr *smb_msg, + size_t *sent) +{ + int rc = 0; + int retries = 0; + struct socket *ssocket = server->ssocket; + + *sent = 0; + + if (server->noblocksnd) + smb_msg->msg_flags = MSG_DONTWAIT + MSG_NOSIGNAL; + else + smb_msg->msg_flags = MSG_NOSIGNAL; + + while (msg_data_left(smb_msg)) { + /* + * If blocking send, we try 3 times, since each can block + * for 5 seconds. For nonblocking we have to try more + * but wait increasing amounts of time allowing time for + * socket to clear. The overall time we wait in either + * case to send on the socket is about 15 seconds. + * Similarly we wait for 15 seconds for a response from + * the server in SendReceive[2] for the server to send + * a response back for most types of requests (except + * SMB Write past end of file which can be slow, and + * blocking lock operations). NFS waits slightly longer + * than CIFS, but this can make it take longer for + * nonresponsive servers to be detected and 15 seconds + * is more than enough time for modern networks to + * send a packet. In most cases if we fail to send + * after the retries we will kill the socket and + * reconnect which may clear the network problem. + */ + rc = sock_sendmsg(ssocket, smb_msg); + if (rc == -EAGAIN) { + retries++; + if (retries >= 14 || + (!server->noblocksnd && (retries > 2))) { + cifs_server_dbg(VFS, "sends on sock %p stuck for 15 seconds\n", + ssocket); + return -EAGAIN; + } + msleep(1 << retries); + continue; + } + + if (rc < 0) + return rc; + + if (rc == 0) { + /* should never happen, letting socket clear before + retrying is our only obvious option here */ + cifs_server_dbg(VFS, "tcp sent no data\n"); + msleep(500); + continue; + } + + /* send was at least partially successful */ + *sent += rc; + retries = 0; /* in case we get ENOSPC on the next send */ + } + return 0; +} + +unsigned long +smb_rqst_len(struct TCP_Server_Info *server, struct smb_rqst *rqst) +{ + unsigned int i; + struct kvec *iov; + int nvec; + unsigned long buflen = 0; + + if (!is_smb1(server) && rqst->rq_nvec >= 2 && + rqst->rq_iov[0].iov_len == 4) { + iov = &rqst->rq_iov[1]; + nvec = rqst->rq_nvec - 1; + } else { + iov = rqst->rq_iov; + nvec = rqst->rq_nvec; + } + + /* total up iov array first */ + for (i = 0; i < nvec; i++) + buflen += iov[i].iov_len; + + /* + * Add in the page array if there is one. The caller needs to make + * sure rq_offset and rq_tailsz are set correctly. If a buffer of + * multiple pages ends at page boundary, rq_tailsz needs to be set to + * PAGE_SIZE. + */ + if (rqst->rq_npages) { + if (rqst->rq_npages == 1) + buflen += rqst->rq_tailsz; + else { + /* + * If there is more than one page, calculate the + * buffer length based on rq_offset and rq_tailsz + */ + buflen += rqst->rq_pagesz * (rqst->rq_npages - 1) - + rqst->rq_offset; + buflen += rqst->rq_tailsz; + } + } + + return buflen; +} + +static int +__smb_send_rqst(struct TCP_Server_Info *server, int num_rqst, + struct smb_rqst *rqst) +{ + int rc; + struct kvec *iov; + int n_vec; + unsigned int send_length = 0; + unsigned int i, j; + sigset_t mask, oldmask; + size_t total_len = 0, sent, size; + struct socket *ssocket = server->ssocket; + struct msghdr smb_msg = {}; + __be32 rfc1002_marker; + + cifs_in_send_inc(server); + if (cifs_rdma_enabled(server)) { + /* return -EAGAIN when connecting or reconnecting */ + rc = -EAGAIN; + if (server->smbd_conn) + rc = smbd_send(server, num_rqst, rqst); + goto smbd_done; + } + + rc = -EAGAIN; + if (ssocket == NULL) + goto out; + + rc = -ERESTARTSYS; + if (fatal_signal_pending(current)) { + cifs_dbg(FYI, "signal pending before send request\n"); + goto out; + } + + rc = 0; + /* cork the socket */ + tcp_sock_set_cork(ssocket->sk, true); + + for (j = 0; j < num_rqst; j++) + send_length += smb_rqst_len(server, &rqst[j]); + rfc1002_marker = cpu_to_be32(send_length); + + /* + * We should not allow signals to interrupt the network send because + * any partial send will cause session reconnects thus increasing + * latency of system calls and overload a server with unnecessary + * requests. + */ + + sigfillset(&mask); + sigprocmask(SIG_BLOCK, &mask, &oldmask); + + /* Generate a rfc1002 marker for SMB2+ */ + if (!is_smb1(server)) { + struct kvec hiov = { + .iov_base = &rfc1002_marker, + .iov_len = 4 + }; + iov_iter_kvec(&smb_msg.msg_iter, ITER_SOURCE, &hiov, 1, 4); + rc = smb_send_kvec(server, &smb_msg, &sent); + if (rc < 0) + goto unmask; + + total_len += sent; + send_length += 4; + } + + cifs_dbg(FYI, "Sending smb: smb_len=%u\n", send_length); + + for (j = 0; j < num_rqst; j++) { + iov = rqst[j].rq_iov; + n_vec = rqst[j].rq_nvec; + + size = 0; + for (i = 0; i < n_vec; i++) { + dump_smb(iov[i].iov_base, iov[i].iov_len); + size += iov[i].iov_len; + } + + iov_iter_kvec(&smb_msg.msg_iter, ITER_SOURCE, iov, n_vec, size); + + rc = smb_send_kvec(server, &smb_msg, &sent); + if (rc < 0) + goto unmask; + + total_len += sent; + + /* now walk the page array and send each page in it */ + for (i = 0; i < rqst[j].rq_npages; i++) { + struct bio_vec bvec; + + bvec.bv_page = rqst[j].rq_pages[i]; + rqst_page_get_length(&rqst[j], i, &bvec.bv_len, + &bvec.bv_offset); + + iov_iter_bvec(&smb_msg.msg_iter, ITER_SOURCE, + &bvec, 1, bvec.bv_len); + rc = smb_send_kvec(server, &smb_msg, &sent); + if (rc < 0) + break; + + total_len += sent; + } + } + +unmask: + sigprocmask(SIG_SETMASK, &oldmask, NULL); + + /* + * If signal is pending but we have already sent the whole packet to + * the server we need to return success status to allow a corresponding + * mid entry to be kept in the pending requests queue thus allowing + * to handle responses from the server by the client. + * + * If only part of the packet has been sent there is no need to hide + * interrupt because the session will be reconnected anyway, so there + * won't be any response from the server to handle. + */ + + if (signal_pending(current) && (total_len != send_length)) { + cifs_dbg(FYI, "signal is pending after attempt to send\n"); + rc = -ERESTARTSYS; + } + + /* uncork it */ + tcp_sock_set_cork(ssocket->sk, false); + + if ((total_len > 0) && (total_len != send_length)) { + cifs_dbg(FYI, "partial send (wanted=%u sent=%zu): terminating session\n", + send_length, total_len); + /* + * If we have only sent part of an SMB then the next SMB could + * be taken as the remainder of this one. We need to kill the + * socket so the server throws away the partial SMB + */ + cifs_signal_cifsd_for_reconnect(server, false); + trace_smb3_partial_send_reconnect(server->CurrentMid, + server->conn_id, server->hostname); + } +smbd_done: + if (rc < 0 && rc != -EINTR) + cifs_server_dbg(VFS, "Error %d sending data on socket to server\n", + rc); + else if (rc > 0) + rc = 0; +out: + cifs_in_send_dec(server); + return rc; +} + +static int +smb_send_rqst(struct TCP_Server_Info *server, int num_rqst, + struct smb_rqst *rqst, int flags) +{ + struct kvec iov; + struct smb2_transform_hdr *tr_hdr; + struct smb_rqst cur_rqst[MAX_COMPOUND]; + int rc; + + if (!(flags & CIFS_TRANSFORM_REQ)) + return __smb_send_rqst(server, num_rqst, rqst); + + if (num_rqst > MAX_COMPOUND - 1) + return -ENOMEM; + + if (!server->ops->init_transform_rq) { + cifs_server_dbg(VFS, "Encryption requested but transform callback is missing\n"); + return -EIO; + } + + tr_hdr = kzalloc(sizeof(*tr_hdr), GFP_NOFS); + if (!tr_hdr) + return -ENOMEM; + + memset(&cur_rqst[0], 0, sizeof(cur_rqst)); + memset(&iov, 0, sizeof(iov)); + + iov.iov_base = tr_hdr; + iov.iov_len = sizeof(*tr_hdr); + cur_rqst[0].rq_iov = &iov; + cur_rqst[0].rq_nvec = 1; + + rc = server->ops->init_transform_rq(server, num_rqst + 1, + &cur_rqst[0], rqst); + if (rc) + goto out; + + rc = __smb_send_rqst(server, num_rqst + 1, &cur_rqst[0]); + smb3_free_compound_rqst(num_rqst, &cur_rqst[1]); +out: + kfree(tr_hdr); + return rc; +} + +int +smb_send(struct TCP_Server_Info *server, struct smb_hdr *smb_buffer, + unsigned int smb_buf_length) +{ + struct kvec iov[2]; + struct smb_rqst rqst = { .rq_iov = iov, + .rq_nvec = 2 }; + + iov[0].iov_base = smb_buffer; + iov[0].iov_len = 4; + iov[1].iov_base = (char *)smb_buffer + 4; + iov[1].iov_len = smb_buf_length; + + return __smb_send_rqst(server, 1, &rqst); +} + +static int +wait_for_free_credits(struct TCP_Server_Info *server, const int num_credits, + const int timeout, const int flags, + unsigned int *instance) +{ + long rc; + int *credits; + int optype; + long int t; + int scredits, in_flight; + + if (timeout < 0) + t = MAX_JIFFY_OFFSET; + else + t = msecs_to_jiffies(timeout); + + optype = flags & CIFS_OP_MASK; + + *instance = 0; + + credits = server->ops->get_credits_field(server, optype); + /* Since an echo is already inflight, no need to wait to send another */ + if (*credits <= 0 && optype == CIFS_ECHO_OP) + return -EAGAIN; + + spin_lock(&server->req_lock); + if ((flags & CIFS_TIMEOUT_MASK) == CIFS_NON_BLOCKING) { + /* oplock breaks must not be held up */ + server->in_flight++; + if (server->in_flight > server->max_in_flight) + server->max_in_flight = server->in_flight; + *credits -= 1; + *instance = server->reconnect_instance; + scredits = *credits; + in_flight = server->in_flight; + spin_unlock(&server->req_lock); + + trace_smb3_nblk_credits(server->CurrentMid, + server->conn_id, server->hostname, scredits, -1, in_flight); + cifs_dbg(FYI, "%s: remove %u credits total=%d\n", + __func__, 1, scredits); + + return 0; + } + + while (1) { + if (*credits < num_credits) { + scredits = *credits; + spin_unlock(&server->req_lock); + + cifs_num_waiters_inc(server); + rc = wait_event_killable_timeout(server->request_q, + has_credits(server, credits, num_credits), t); + cifs_num_waiters_dec(server); + if (!rc) { + spin_lock(&server->req_lock); + scredits = *credits; + in_flight = server->in_flight; + spin_unlock(&server->req_lock); + + trace_smb3_credit_timeout(server->CurrentMid, + server->conn_id, server->hostname, scredits, + num_credits, in_flight); + cifs_server_dbg(VFS, "wait timed out after %d ms\n", + timeout); + return -EBUSY; + } + if (rc == -ERESTARTSYS) + return -ERESTARTSYS; + spin_lock(&server->req_lock); + } else { + spin_unlock(&server->req_lock); + + spin_lock(&server->srv_lock); + if (server->tcpStatus == CifsExiting) { + spin_unlock(&server->srv_lock); + return -ENOENT; + } + spin_unlock(&server->srv_lock); + + /* + * For normal commands, reserve the last MAX_COMPOUND + * credits to compound requests. + * Otherwise these compounds could be permanently + * starved for credits by single-credit requests. + * + * To prevent spinning CPU, block this thread until + * there are >MAX_COMPOUND credits available. + * But only do this is we already have a lot of + * credits in flight to avoid triggering this check + * for servers that are slow to hand out credits on + * new sessions. + */ + spin_lock(&server->req_lock); + if (!optype && num_credits == 1 && + server->in_flight > 2 * MAX_COMPOUND && + *credits <= MAX_COMPOUND) { + spin_unlock(&server->req_lock); + + cifs_num_waiters_inc(server); + rc = wait_event_killable_timeout( + server->request_q, + has_credits(server, credits, + MAX_COMPOUND + 1), + t); + cifs_num_waiters_dec(server); + if (!rc) { + spin_lock(&server->req_lock); + scredits = *credits; + in_flight = server->in_flight; + spin_unlock(&server->req_lock); + + trace_smb3_credit_timeout( + server->CurrentMid, + server->conn_id, server->hostname, + scredits, num_credits, in_flight); + cifs_server_dbg(VFS, "wait timed out after %d ms\n", + timeout); + return -EBUSY; + } + if (rc == -ERESTARTSYS) + return -ERESTARTSYS; + spin_lock(&server->req_lock); + continue; + } + + /* + * Can not count locking commands against total + * as they are allowed to block on server. + */ + + /* update # of requests on the wire to server */ + if ((flags & CIFS_TIMEOUT_MASK) != CIFS_BLOCKING_OP) { + *credits -= num_credits; + server->in_flight += num_credits; + if (server->in_flight > server->max_in_flight) + server->max_in_flight = server->in_flight; + *instance = server->reconnect_instance; + } + scredits = *credits; + in_flight = server->in_flight; + spin_unlock(&server->req_lock); + + trace_smb3_waitff_credits(server->CurrentMid, + server->conn_id, server->hostname, scredits, + -(num_credits), in_flight); + cifs_dbg(FYI, "%s: remove %u credits total=%d\n", + __func__, num_credits, scredits); + break; + } + } + return 0; +} + +static int +wait_for_free_request(struct TCP_Server_Info *server, const int flags, + unsigned int *instance) +{ + return wait_for_free_credits(server, 1, -1, flags, + instance); +} + +static int +wait_for_compound_request(struct TCP_Server_Info *server, int num, + const int flags, unsigned int *instance) +{ + int *credits; + int scredits, in_flight; + + credits = server->ops->get_credits_field(server, flags & CIFS_OP_MASK); + + spin_lock(&server->req_lock); + scredits = *credits; + in_flight = server->in_flight; + + if (*credits < num) { + /* + * If the server is tight on resources or just gives us less + * credits for other reasons (e.g. requests are coming out of + * order and the server delays granting more credits until it + * processes a missing mid) and we exhausted most available + * credits there may be situations when we try to send + * a compound request but we don't have enough credits. At this + * point the client needs to decide if it should wait for + * additional credits or fail the request. If at least one + * request is in flight there is a high probability that the + * server will return enough credits to satisfy this compound + * request. + * + * Return immediately if no requests in flight since we will be + * stuck on waiting for credits. + */ + if (server->in_flight == 0) { + spin_unlock(&server->req_lock); + trace_smb3_insufficient_credits(server->CurrentMid, + server->conn_id, server->hostname, scredits, + num, in_flight); + cifs_dbg(FYI, "%s: %d requests in flight, needed %d total=%d\n", + __func__, in_flight, num, scredits); + return -EDEADLK; + } + } + spin_unlock(&server->req_lock); + + return wait_for_free_credits(server, num, 60000, flags, + instance); +} + +int +cifs_wait_mtu_credits(struct TCP_Server_Info *server, unsigned int size, + unsigned int *num, struct cifs_credits *credits) +{ + *num = size; + credits->value = 0; + credits->instance = server->reconnect_instance; + return 0; +} + +static int allocate_mid(struct cifs_ses *ses, struct smb_hdr *in_buf, + struct mid_q_entry **ppmidQ) +{ + spin_lock(&ses->ses_lock); + if (ses->ses_status == SES_NEW) { + if ((in_buf->Command != SMB_COM_SESSION_SETUP_ANDX) && + (in_buf->Command != SMB_COM_NEGOTIATE)) { + spin_unlock(&ses->ses_lock); + return -EAGAIN; + } + /* else ok - we are setting up session */ + } + + if (ses->ses_status == SES_EXITING) { + /* check if SMB session is bad because we are setting it up */ + if (in_buf->Command != SMB_COM_LOGOFF_ANDX) { + spin_unlock(&ses->ses_lock); + return -EAGAIN; + } + /* else ok - we are shutting down session */ + } + spin_unlock(&ses->ses_lock); + + *ppmidQ = alloc_mid(in_buf, ses->server); + if (*ppmidQ == NULL) + return -ENOMEM; + spin_lock(&ses->server->mid_lock); + list_add_tail(&(*ppmidQ)->qhead, &ses->server->pending_mid_q); + spin_unlock(&ses->server->mid_lock); + return 0; +} + +static int +wait_for_response(struct TCP_Server_Info *server, struct mid_q_entry *midQ) +{ + int error; + + error = wait_event_state(server->response_q, + midQ->mid_state != MID_REQUEST_SUBMITTED, + (TASK_KILLABLE|TASK_FREEZABLE_UNSAFE)); + if (error < 0) + return -ERESTARTSYS; + + return 0; +} + +struct mid_q_entry * +cifs_setup_async_request(struct TCP_Server_Info *server, struct smb_rqst *rqst) +{ + int rc; + struct smb_hdr *hdr = (struct smb_hdr *)rqst->rq_iov[0].iov_base; + struct mid_q_entry *mid; + + if (rqst->rq_iov[0].iov_len != 4 || + rqst->rq_iov[0].iov_base + 4 != rqst->rq_iov[1].iov_base) + return ERR_PTR(-EIO); + + /* enable signing if server requires it */ + if (server->sign) + hdr->Flags2 |= SMBFLG2_SECURITY_SIGNATURE; + + mid = alloc_mid(hdr, server); + if (mid == NULL) + return ERR_PTR(-ENOMEM); + + rc = cifs_sign_rqst(rqst, server, &mid->sequence_number); + if (rc) { + release_mid(mid); + return ERR_PTR(rc); + } + + return mid; +} + +/* + * Send a SMB request and set the callback function in the mid to handle + * the result. Caller is responsible for dealing with timeouts. + */ +int +cifs_call_async(struct TCP_Server_Info *server, struct smb_rqst *rqst, + mid_receive_t *receive, mid_callback_t *callback, + mid_handle_t *handle, void *cbdata, const int flags, + const struct cifs_credits *exist_credits) +{ + int rc; + struct mid_q_entry *mid; + struct cifs_credits credits = { .value = 0, .instance = 0 }; + unsigned int instance; + int optype; + + optype = flags & CIFS_OP_MASK; + + if ((flags & CIFS_HAS_CREDITS) == 0) { + rc = wait_for_free_request(server, flags, &instance); + if (rc) + return rc; + credits.value = 1; + credits.instance = instance; + } else + instance = exist_credits->instance; + + cifs_server_lock(server); + + /* + * We can't use credits obtained from the previous session to send this + * request. Check if there were reconnects after we obtained credits and + * return -EAGAIN in such cases to let callers handle it. + */ + if (instance != server->reconnect_instance) { + cifs_server_unlock(server); + add_credits_and_wake_if(server, &credits, optype); + return -EAGAIN; + } + + mid = server->ops->setup_async_request(server, rqst); + if (IS_ERR(mid)) { + cifs_server_unlock(server); + add_credits_and_wake_if(server, &credits, optype); + return PTR_ERR(mid); + } + + mid->receive = receive; + mid->callback = callback; + mid->callback_data = cbdata; + mid->handle = handle; + mid->mid_state = MID_REQUEST_SUBMITTED; + + /* put it on the pending_mid_q */ + spin_lock(&server->mid_lock); + list_add_tail(&mid->qhead, &server->pending_mid_q); + spin_unlock(&server->mid_lock); + + /* + * Need to store the time in mid before calling I/O. For call_async, + * I/O response may come back and free the mid entry on another thread. + */ + cifs_save_when_sent(mid); + rc = smb_send_rqst(server, 1, rqst, flags); + + if (rc < 0) { + revert_current_mid(server, mid->credits); + server->sequence_number -= 2; + delete_mid(mid); + } + + cifs_server_unlock(server); + + if (rc == 0) + return 0; + + add_credits_and_wake_if(server, &credits, optype); + return rc; +} + +/* + * + * Send an SMB Request. No response info (other than return code) + * needs to be parsed. + * + * flags indicate the type of request buffer and how long to wait + * and whether to log NT STATUS code (error) before mapping it to POSIX error + * + */ +int +SendReceiveNoRsp(const unsigned int xid, struct cifs_ses *ses, + char *in_buf, int flags) +{ + int rc; + struct kvec iov[1]; + struct kvec rsp_iov; + int resp_buf_type; + + iov[0].iov_base = in_buf; + iov[0].iov_len = get_rfc1002_length(in_buf) + 4; + flags |= CIFS_NO_RSP_BUF; + rc = SendReceive2(xid, ses, iov, 1, &resp_buf_type, flags, &rsp_iov); + cifs_dbg(NOISY, "SendRcvNoRsp flags %d rc %d\n", flags, rc); + + return rc; +} + +static int +cifs_sync_mid_result(struct mid_q_entry *mid, struct TCP_Server_Info *server) +{ + int rc = 0; + + cifs_dbg(FYI, "%s: cmd=%d mid=%llu state=%d\n", + __func__, le16_to_cpu(mid->command), mid->mid, mid->mid_state); + + spin_lock(&server->mid_lock); + switch (mid->mid_state) { + case MID_RESPONSE_RECEIVED: + spin_unlock(&server->mid_lock); + return rc; + case MID_RETRY_NEEDED: + rc = -EAGAIN; + break; + case MID_RESPONSE_MALFORMED: + rc = -EIO; + break; + case MID_SHUTDOWN: + rc = -EHOSTDOWN; + break; + default: + if (!(mid->mid_flags & MID_DELETED)) { + list_del_init(&mid->qhead); + mid->mid_flags |= MID_DELETED; + } + cifs_server_dbg(VFS, "%s: invalid mid state mid=%llu state=%d\n", + __func__, mid->mid, mid->mid_state); + rc = -EIO; + } + spin_unlock(&server->mid_lock); + + release_mid(mid); + return rc; +} + +static inline int +send_cancel(struct TCP_Server_Info *server, struct smb_rqst *rqst, + struct mid_q_entry *mid) +{ + return server->ops->send_cancel ? + server->ops->send_cancel(server, rqst, mid) : 0; +} + +int +cifs_check_receive(struct mid_q_entry *mid, struct TCP_Server_Info *server, + bool log_error) +{ + unsigned int len = get_rfc1002_length(mid->resp_buf) + 4; + + dump_smb(mid->resp_buf, min_t(u32, 92, len)); + + /* convert the length into a more usable form */ + if (server->sign) { + struct kvec iov[2]; + int rc = 0; + struct smb_rqst rqst = { .rq_iov = iov, + .rq_nvec = 2 }; + + iov[0].iov_base = mid->resp_buf; + iov[0].iov_len = 4; + iov[1].iov_base = (char *)mid->resp_buf + 4; + iov[1].iov_len = len - 4; + /* FIXME: add code to kill session */ + rc = cifs_verify_signature(&rqst, server, + mid->sequence_number); + if (rc) + cifs_server_dbg(VFS, "SMB signature verification returned error = %d\n", + rc); + } + + /* BB special case reconnect tid and uid here? */ + return map_and_check_smb_error(mid, log_error); +} + +struct mid_q_entry * +cifs_setup_request(struct cifs_ses *ses, struct TCP_Server_Info *ignored, + struct smb_rqst *rqst) +{ + int rc; + struct smb_hdr *hdr = (struct smb_hdr *)rqst->rq_iov[0].iov_base; + struct mid_q_entry *mid; + + if (rqst->rq_iov[0].iov_len != 4 || + rqst->rq_iov[0].iov_base + 4 != rqst->rq_iov[1].iov_base) + return ERR_PTR(-EIO); + + rc = allocate_mid(ses, hdr, &mid); + if (rc) + return ERR_PTR(rc); + rc = cifs_sign_rqst(rqst, ses->server, &mid->sequence_number); + if (rc) { + delete_mid(mid); + return ERR_PTR(rc); + } + return mid; +} + +static void +cifs_compound_callback(struct mid_q_entry *mid) +{ + struct TCP_Server_Info *server = mid->server; + struct cifs_credits credits; + + credits.value = server->ops->get_credits(mid); + credits.instance = server->reconnect_instance; + + add_credits(server, &credits, mid->optype); +} + +static void +cifs_compound_last_callback(struct mid_q_entry *mid) +{ + cifs_compound_callback(mid); + cifs_wake_up_task(mid); +} + +static void +cifs_cancelled_callback(struct mid_q_entry *mid) +{ + cifs_compound_callback(mid); + release_mid(mid); +} + +/* + * Return a channel (master if none) of @ses that can be used to send + * regular requests. + * + * If we are currently binding a new channel (negprot/sess.setup), + * return the new incomplete channel. + */ +struct TCP_Server_Info *cifs_pick_channel(struct cifs_ses *ses) +{ + uint index = 0; + + if (!ses) + return NULL; + + /* round robin */ + index = (uint)atomic_inc_return(&ses->chan_seq); + + spin_lock(&ses->chan_lock); + index %= ses->chan_count; + spin_unlock(&ses->chan_lock); + + return ses->chans[index].server; +} + +int +compound_send_recv(const unsigned int xid, struct cifs_ses *ses, + struct TCP_Server_Info *server, + const int flags, const int num_rqst, struct smb_rqst *rqst, + int *resp_buf_type, struct kvec *resp_iov) +{ + int i, j, optype, rc = 0; + struct mid_q_entry *midQ[MAX_COMPOUND]; + bool cancelled_mid[MAX_COMPOUND] = {false}; + struct cifs_credits credits[MAX_COMPOUND] = { + { .value = 0, .instance = 0 } + }; + unsigned int instance; + char *buf; + + optype = flags & CIFS_OP_MASK; + + for (i = 0; i < num_rqst; i++) + resp_buf_type[i] = CIFS_NO_BUFFER; /* no response buf yet */ + + if (!ses || !ses->server || !server) { + cifs_dbg(VFS, "Null session\n"); + return -EIO; + } + + spin_lock(&server->srv_lock); + if (server->tcpStatus == CifsExiting) { + spin_unlock(&server->srv_lock); + return -ENOENT; + } + spin_unlock(&server->srv_lock); + + /* + * Wait for all the requests to become available. + * This approach still leaves the possibility to be stuck waiting for + * credits if the server doesn't grant credits to the outstanding + * requests and if the client is completely idle, not generating any + * other requests. + * This can be handled by the eventual session reconnect. + */ + rc = wait_for_compound_request(server, num_rqst, flags, + &instance); + if (rc) + return rc; + + for (i = 0; i < num_rqst; i++) { + credits[i].value = 1; + credits[i].instance = instance; + } + + /* + * Make sure that we sign in the same order that we send on this socket + * and avoid races inside tcp sendmsg code that could cause corruption + * of smb data. + */ + + cifs_server_lock(server); + + /* + * All the parts of the compound chain belong obtained credits from the + * same session. We can not use credits obtained from the previous + * session to send this request. Check if there were reconnects after + * we obtained credits and return -EAGAIN in such cases to let callers + * handle it. + */ + if (instance != server->reconnect_instance) { + cifs_server_unlock(server); + for (j = 0; j < num_rqst; j++) + add_credits(server, &credits[j], optype); + return -EAGAIN; + } + + for (i = 0; i < num_rqst; i++) { + midQ[i] = server->ops->setup_request(ses, server, &rqst[i]); + if (IS_ERR(midQ[i])) { + revert_current_mid(server, i); + for (j = 0; j < i; j++) + delete_mid(midQ[j]); + cifs_server_unlock(server); + + /* Update # of requests on wire to server */ + for (j = 0; j < num_rqst; j++) + add_credits(server, &credits[j], optype); + return PTR_ERR(midQ[i]); + } + + midQ[i]->mid_state = MID_REQUEST_SUBMITTED; + midQ[i]->optype = optype; + /* + * Invoke callback for every part of the compound chain + * to calculate credits properly. Wake up this thread only when + * the last element is received. + */ + if (i < num_rqst - 1) + midQ[i]->callback = cifs_compound_callback; + else + midQ[i]->callback = cifs_compound_last_callback; + } + rc = smb_send_rqst(server, num_rqst, rqst, flags); + + for (i = 0; i < num_rqst; i++) + cifs_save_when_sent(midQ[i]); + + if (rc < 0) { + revert_current_mid(server, num_rqst); + server->sequence_number -= 2; + } + + cifs_server_unlock(server); + + /* + * If sending failed for some reason or it is an oplock break that we + * will not receive a response to - return credits back + */ + if (rc < 0 || (flags & CIFS_NO_SRV_RSP)) { + for (i = 0; i < num_rqst; i++) + add_credits(server, &credits[i], optype); + goto out; + } + + /* + * At this point the request is passed to the network stack - we assume + * that any credits taken from the server structure on the client have + * been spent and we can't return them back. Once we receive responses + * we will collect credits granted by the server in the mid callbacks + * and add those credits to the server structure. + */ + + /* + * Compounding is never used during session establish. + */ + spin_lock(&ses->ses_lock); + if ((ses->ses_status == SES_NEW) || (optype & CIFS_NEG_OP) || (optype & CIFS_SESS_OP)) { + spin_unlock(&ses->ses_lock); + + cifs_server_lock(server); + smb311_update_preauth_hash(ses, server, rqst[0].rq_iov, rqst[0].rq_nvec); + cifs_server_unlock(server); + + spin_lock(&ses->ses_lock); + } + spin_unlock(&ses->ses_lock); + + for (i = 0; i < num_rqst; i++) { + rc = wait_for_response(server, midQ[i]); + if (rc != 0) + break; + } + if (rc != 0) { + for (; i < num_rqst; i++) { + cifs_server_dbg(FYI, "Cancelling wait for mid %llu cmd: %d\n", + midQ[i]->mid, le16_to_cpu(midQ[i]->command)); + send_cancel(server, &rqst[i], midQ[i]); + spin_lock(&server->mid_lock); + midQ[i]->mid_flags |= MID_WAIT_CANCELLED; + if (midQ[i]->mid_state == MID_REQUEST_SUBMITTED) { + midQ[i]->callback = cifs_cancelled_callback; + cancelled_mid[i] = true; + credits[i].value = 0; + } + spin_unlock(&server->mid_lock); + } + } + + for (i = 0; i < num_rqst; i++) { + if (rc < 0) + goto out; + + rc = cifs_sync_mid_result(midQ[i], server); + if (rc != 0) { + /* mark this mid as cancelled to not free it below */ + cancelled_mid[i] = true; + goto out; + } + + if (!midQ[i]->resp_buf || + midQ[i]->mid_state != MID_RESPONSE_RECEIVED) { + rc = -EIO; + cifs_dbg(FYI, "Bad MID state?\n"); + goto out; + } + + buf = (char *)midQ[i]->resp_buf; + resp_iov[i].iov_base = buf; + resp_iov[i].iov_len = midQ[i]->resp_buf_size + + HEADER_PREAMBLE_SIZE(server); + + if (midQ[i]->large_buf) + resp_buf_type[i] = CIFS_LARGE_BUFFER; + else + resp_buf_type[i] = CIFS_SMALL_BUFFER; + + rc = server->ops->check_receive(midQ[i], server, + flags & CIFS_LOG_ERROR); + + /* mark it so buf will not be freed by delete_mid */ + if ((flags & CIFS_NO_RSP_BUF) == 0) + midQ[i]->resp_buf = NULL; + + } + + /* + * Compounding is never used during session establish. + */ + spin_lock(&ses->ses_lock); + if ((ses->ses_status == SES_NEW) || (optype & CIFS_NEG_OP) || (optype & CIFS_SESS_OP)) { + struct kvec iov = { + .iov_base = resp_iov[0].iov_base, + .iov_len = resp_iov[0].iov_len + }; + spin_unlock(&ses->ses_lock); + cifs_server_lock(server); + smb311_update_preauth_hash(ses, server, &iov, 1); + cifs_server_unlock(server); + spin_lock(&ses->ses_lock); + } + spin_unlock(&ses->ses_lock); + +out: + /* + * This will dequeue all mids. After this it is important that the + * demultiplex_thread will not process any of these mids any futher. + * This is prevented above by using a noop callback that will not + * wake this thread except for the very last PDU. + */ + for (i = 0; i < num_rqst; i++) { + if (!cancelled_mid[i]) + delete_mid(midQ[i]); + } + + return rc; +} + +int +cifs_send_recv(const unsigned int xid, struct cifs_ses *ses, + struct TCP_Server_Info *server, + struct smb_rqst *rqst, int *resp_buf_type, const int flags, + struct kvec *resp_iov) +{ + return compound_send_recv(xid, ses, server, flags, 1, + rqst, resp_buf_type, resp_iov); +} + +int +SendReceive2(const unsigned int xid, struct cifs_ses *ses, + struct kvec *iov, int n_vec, int *resp_buf_type /* ret */, + const int flags, struct kvec *resp_iov) +{ + struct smb_rqst rqst; + struct kvec s_iov[CIFS_MAX_IOV_SIZE], *new_iov; + int rc; + + if (n_vec + 1 > CIFS_MAX_IOV_SIZE) { + new_iov = kmalloc_array(n_vec + 1, sizeof(struct kvec), + GFP_KERNEL); + if (!new_iov) { + /* otherwise cifs_send_recv below sets resp_buf_type */ + *resp_buf_type = CIFS_NO_BUFFER; + return -ENOMEM; + } + } else + new_iov = s_iov; + + /* 1st iov is a RFC1001 length followed by the rest of the packet */ + memcpy(new_iov + 1, iov, (sizeof(struct kvec) * n_vec)); + + new_iov[0].iov_base = new_iov[1].iov_base; + new_iov[0].iov_len = 4; + new_iov[1].iov_base += 4; + new_iov[1].iov_len -= 4; + + memset(&rqst, 0, sizeof(struct smb_rqst)); + rqst.rq_iov = new_iov; + rqst.rq_nvec = n_vec + 1; + + rc = cifs_send_recv(xid, ses, ses->server, + &rqst, resp_buf_type, flags, resp_iov); + if (n_vec + 1 > CIFS_MAX_IOV_SIZE) + kfree(new_iov); + return rc; +} + +int +SendReceive(const unsigned int xid, struct cifs_ses *ses, + struct smb_hdr *in_buf, struct smb_hdr *out_buf, + int *pbytes_returned, const int flags) +{ + int rc = 0; + struct mid_q_entry *midQ; + unsigned int len = be32_to_cpu(in_buf->smb_buf_length); + struct kvec iov = { .iov_base = in_buf, .iov_len = len }; + struct smb_rqst rqst = { .rq_iov = &iov, .rq_nvec = 1 }; + struct cifs_credits credits = { .value = 1, .instance = 0 }; + struct TCP_Server_Info *server; + + if (ses == NULL) { + cifs_dbg(VFS, "Null smb session\n"); + return -EIO; + } + server = ses->server; + if (server == NULL) { + cifs_dbg(VFS, "Null tcp session\n"); + return -EIO; + } + + spin_lock(&server->srv_lock); + if (server->tcpStatus == CifsExiting) { + spin_unlock(&server->srv_lock); + return -ENOENT; + } + spin_unlock(&server->srv_lock); + + /* Ensure that we do not send more than 50 overlapping requests + to the same server. We may make this configurable later or + use ses->maxReq */ + + if (len > CIFSMaxBufSize + MAX_CIFS_HDR_SIZE - 4) { + cifs_server_dbg(VFS, "Invalid length, greater than maximum frame, %d\n", + len); + return -EIO; + } + + rc = wait_for_free_request(server, flags, &credits.instance); + if (rc) + return rc; + + /* make sure that we sign in the same order that we send on this socket + and avoid races inside tcp sendmsg code that could cause corruption + of smb data */ + + cifs_server_lock(server); + + rc = allocate_mid(ses, in_buf, &midQ); + if (rc) { + cifs_server_unlock(server); + /* Update # of requests on wire to server */ + add_credits(server, &credits, 0); + return rc; + } + + rc = cifs_sign_smb(in_buf, server, &midQ->sequence_number); + if (rc) { + cifs_server_unlock(server); + goto out; + } + + midQ->mid_state = MID_REQUEST_SUBMITTED; + + rc = smb_send(server, in_buf, len); + cifs_save_when_sent(midQ); + + if (rc < 0) + server->sequence_number -= 2; + + cifs_server_unlock(server); + + if (rc < 0) + goto out; + + rc = wait_for_response(server, midQ); + if (rc != 0) { + send_cancel(server, &rqst, midQ); + spin_lock(&server->mid_lock); + if (midQ->mid_state == MID_REQUEST_SUBMITTED) { + /* no longer considered to be "in-flight" */ + midQ->callback = release_mid; + spin_unlock(&server->mid_lock); + add_credits(server, &credits, 0); + return rc; + } + spin_unlock(&server->mid_lock); + } + + rc = cifs_sync_mid_result(midQ, server); + if (rc != 0) { + add_credits(server, &credits, 0); + return rc; + } + + if (!midQ->resp_buf || !out_buf || + midQ->mid_state != MID_RESPONSE_RECEIVED) { + rc = -EIO; + cifs_server_dbg(VFS, "Bad MID state?\n"); + goto out; + } + + *pbytes_returned = get_rfc1002_length(midQ->resp_buf); + memcpy(out_buf, midQ->resp_buf, *pbytes_returned + 4); + rc = cifs_check_receive(midQ, server, 0); +out: + delete_mid(midQ); + add_credits(server, &credits, 0); + + return rc; +} + +/* We send a LOCKINGX_CANCEL_LOCK to cause the Windows + blocking lock to return. */ + +static int +send_lock_cancel(const unsigned int xid, struct cifs_tcon *tcon, + struct smb_hdr *in_buf, + struct smb_hdr *out_buf) +{ + int bytes_returned; + struct cifs_ses *ses = tcon->ses; + LOCK_REQ *pSMB = (LOCK_REQ *)in_buf; + + /* We just modify the current in_buf to change + the type of lock from LOCKING_ANDX_SHARED_LOCK + or LOCKING_ANDX_EXCLUSIVE_LOCK to + LOCKING_ANDX_CANCEL_LOCK. */ + + pSMB->LockType = LOCKING_ANDX_CANCEL_LOCK|LOCKING_ANDX_LARGE_FILES; + pSMB->Timeout = 0; + pSMB->hdr.Mid = get_next_mid(ses->server); + + return SendReceive(xid, ses, in_buf, out_buf, + &bytes_returned, 0); +} + +int +SendReceiveBlockingLock(const unsigned int xid, struct cifs_tcon *tcon, + struct smb_hdr *in_buf, struct smb_hdr *out_buf, + int *pbytes_returned) +{ + int rc = 0; + int rstart = 0; + struct mid_q_entry *midQ; + struct cifs_ses *ses; + unsigned int len = be32_to_cpu(in_buf->smb_buf_length); + struct kvec iov = { .iov_base = in_buf, .iov_len = len }; + struct smb_rqst rqst = { .rq_iov = &iov, .rq_nvec = 1 }; + unsigned int instance; + struct TCP_Server_Info *server; + + if (tcon == NULL || tcon->ses == NULL) { + cifs_dbg(VFS, "Null smb session\n"); + return -EIO; + } + ses = tcon->ses; + server = ses->server; + + if (server == NULL) { + cifs_dbg(VFS, "Null tcp session\n"); + return -EIO; + } + + spin_lock(&server->srv_lock); + if (server->tcpStatus == CifsExiting) { + spin_unlock(&server->srv_lock); + return -ENOENT; + } + spin_unlock(&server->srv_lock); + + /* Ensure that we do not send more than 50 overlapping requests + to the same server. We may make this configurable later or + use ses->maxReq */ + + if (len > CIFSMaxBufSize + MAX_CIFS_HDR_SIZE - 4) { + cifs_tcon_dbg(VFS, "Invalid length, greater than maximum frame, %d\n", + len); + return -EIO; + } + + rc = wait_for_free_request(server, CIFS_BLOCKING_OP, &instance); + if (rc) + return rc; + + /* make sure that we sign in the same order that we send on this socket + and avoid races inside tcp sendmsg code that could cause corruption + of smb data */ + + cifs_server_lock(server); + + rc = allocate_mid(ses, in_buf, &midQ); + if (rc) { + cifs_server_unlock(server); + return rc; + } + + rc = cifs_sign_smb(in_buf, server, &midQ->sequence_number); + if (rc) { + delete_mid(midQ); + cifs_server_unlock(server); + return rc; + } + + midQ->mid_state = MID_REQUEST_SUBMITTED; + rc = smb_send(server, in_buf, len); + cifs_save_when_sent(midQ); + + if (rc < 0) + server->sequence_number -= 2; + + cifs_server_unlock(server); + + if (rc < 0) { + delete_mid(midQ); + return rc; + } + + /* Wait for a reply - allow signals to interrupt. */ + rc = wait_event_interruptible(server->response_q, + (!(midQ->mid_state == MID_REQUEST_SUBMITTED)) || + ((server->tcpStatus != CifsGood) && + (server->tcpStatus != CifsNew))); + + /* Were we interrupted by a signal ? */ + spin_lock(&server->srv_lock); + if ((rc == -ERESTARTSYS) && + (midQ->mid_state == MID_REQUEST_SUBMITTED) && + ((server->tcpStatus == CifsGood) || + (server->tcpStatus == CifsNew))) { + spin_unlock(&server->srv_lock); + + if (in_buf->Command == SMB_COM_TRANSACTION2) { + /* POSIX lock. We send a NT_CANCEL SMB to cause the + blocking lock to return. */ + rc = send_cancel(server, &rqst, midQ); + if (rc) { + delete_mid(midQ); + return rc; + } + } else { + /* Windows lock. We send a LOCKINGX_CANCEL_LOCK + to cause the blocking lock to return. */ + + rc = send_lock_cancel(xid, tcon, in_buf, out_buf); + + /* If we get -ENOLCK back the lock may have + already been removed. Don't exit in this case. */ + if (rc && rc != -ENOLCK) { + delete_mid(midQ); + return rc; + } + } + + rc = wait_for_response(server, midQ); + if (rc) { + send_cancel(server, &rqst, midQ); + spin_lock(&server->mid_lock); + if (midQ->mid_state == MID_REQUEST_SUBMITTED) { + /* no longer considered to be "in-flight" */ + midQ->callback = release_mid; + spin_unlock(&server->mid_lock); + return rc; + } + spin_unlock(&server->mid_lock); + } + + /* We got the response - restart system call. */ + rstart = 1; + spin_lock(&server->srv_lock); + } + spin_unlock(&server->srv_lock); + + rc = cifs_sync_mid_result(midQ, server); + if (rc != 0) + return rc; + + /* rcvd frame is ok */ + if (out_buf == NULL || midQ->mid_state != MID_RESPONSE_RECEIVED) { + rc = -EIO; + cifs_tcon_dbg(VFS, "Bad MID state?\n"); + goto out; + } + + *pbytes_returned = get_rfc1002_length(midQ->resp_buf); + memcpy(out_buf, midQ->resp_buf, *pbytes_returned + 4); + rc = cifs_check_receive(midQ, server, 0); +out: + delete_mid(midQ); + if (rstart && rc == -EACCES) + return -ERESTARTSYS; + return rc; +} + +/* + * Discard any remaining data in the current SMB. To do this, we borrow the + * current bigbuf. + */ +int +cifs_discard_remaining_data(struct TCP_Server_Info *server) +{ + unsigned int rfclen = server->pdu_size; + int remaining = rfclen + HEADER_PREAMBLE_SIZE(server) - + server->total_read; + + while (remaining > 0) { + int length; + + length = cifs_discard_from_socket(server, + min_t(size_t, remaining, + CIFSMaxBufSize + MAX_HEADER_SIZE(server))); + if (length < 0) + return length; + server->total_read += length; + remaining -= length; + } + + return 0; +} + +static int +__cifs_readv_discard(struct TCP_Server_Info *server, struct mid_q_entry *mid, + bool malformed) +{ + int length; + + length = cifs_discard_remaining_data(server); + dequeue_mid(mid, malformed); + mid->resp_buf = server->smallbuf; + server->smallbuf = NULL; + return length; +} + +static int +cifs_readv_discard(struct TCP_Server_Info *server, struct mid_q_entry *mid) +{ + struct cifs_readdata *rdata = mid->callback_data; + + return __cifs_readv_discard(server, mid, rdata->result); +} + +int +cifs_readv_receive(struct TCP_Server_Info *server, struct mid_q_entry *mid) +{ + int length, len; + unsigned int data_offset, data_len; + struct cifs_readdata *rdata = mid->callback_data; + char *buf = server->smallbuf; + unsigned int buflen = server->pdu_size + HEADER_PREAMBLE_SIZE(server); + bool use_rdma_mr = false; + + cifs_dbg(FYI, "%s: mid=%llu offset=%llu bytes=%u\n", + __func__, mid->mid, rdata->offset, rdata->bytes); + + /* + * read the rest of READ_RSP header (sans Data array), or whatever we + * can if there's not enough data. At this point, we've read down to + * the Mid. + */ + len = min_t(unsigned int, buflen, server->vals->read_rsp_size) - + HEADER_SIZE(server) + 1; + + length = cifs_read_from_socket(server, + buf + HEADER_SIZE(server) - 1, len); + if (length < 0) + return length; + server->total_read += length; + + if (server->ops->is_session_expired && + server->ops->is_session_expired(buf)) { + cifs_reconnect(server, true); + return -1; + } + + if (server->ops->is_status_pending && + server->ops->is_status_pending(buf, server)) { + cifs_discard_remaining_data(server); + return -1; + } + + /* set up first two iov for signature check and to get credits */ + rdata->iov[0].iov_base = buf; + rdata->iov[0].iov_len = HEADER_PREAMBLE_SIZE(server); + rdata->iov[1].iov_base = buf + HEADER_PREAMBLE_SIZE(server); + rdata->iov[1].iov_len = + server->total_read - HEADER_PREAMBLE_SIZE(server); + cifs_dbg(FYI, "0: iov_base=%p iov_len=%zu\n", + rdata->iov[0].iov_base, rdata->iov[0].iov_len); + cifs_dbg(FYI, "1: iov_base=%p iov_len=%zu\n", + rdata->iov[1].iov_base, rdata->iov[1].iov_len); + + /* Was the SMB read successful? */ + rdata->result = server->ops->map_error(buf, false); + if (rdata->result != 0) { + cifs_dbg(FYI, "%s: server returned error %d\n", + __func__, rdata->result); + /* normal error on read response */ + return __cifs_readv_discard(server, mid, false); + } + + /* Is there enough to get to the rest of the READ_RSP header? */ + if (server->total_read < server->vals->read_rsp_size) { + cifs_dbg(FYI, "%s: server returned short header. got=%u expected=%zu\n", + __func__, server->total_read, + server->vals->read_rsp_size); + rdata->result = -EIO; + return cifs_readv_discard(server, mid); + } + + data_offset = server->ops->read_data_offset(buf) + + HEADER_PREAMBLE_SIZE(server); + if (data_offset < server->total_read) { + /* + * win2k8 sometimes sends an offset of 0 when the read + * is beyond the EOF. Treat it as if the data starts just after + * the header. + */ + cifs_dbg(FYI, "%s: data offset (%u) inside read response header\n", + __func__, data_offset); + data_offset = server->total_read; + } else if (data_offset > MAX_CIFS_SMALL_BUFFER_SIZE) { + /* data_offset is beyond the end of smallbuf */ + cifs_dbg(FYI, "%s: data offset (%u) beyond end of smallbuf\n", + __func__, data_offset); + rdata->result = -EIO; + return cifs_readv_discard(server, mid); + } + + cifs_dbg(FYI, "%s: total_read=%u data_offset=%u\n", + __func__, server->total_read, data_offset); + + len = data_offset - server->total_read; + if (len > 0) { + /* read any junk before data into the rest of smallbuf */ + length = cifs_read_from_socket(server, + buf + server->total_read, len); + if (length < 0) + return length; + server->total_read += length; + } + + /* how much data is in the response? */ +#ifdef CONFIG_CIFS_SMB_DIRECT + use_rdma_mr = rdata->mr; +#endif + data_len = server->ops->read_data_length(buf, use_rdma_mr); + if (!use_rdma_mr && (data_offset + data_len > buflen)) { + /* data_len is corrupt -- discard frame */ + rdata->result = -EIO; + return cifs_readv_discard(server, mid); + } + + length = rdata->read_into_pages(server, rdata, data_len); + if (length < 0) + return length; + + server->total_read += length; + + cifs_dbg(FYI, "total_read=%u buflen=%u remaining=%u\n", + server->total_read, buflen, data_len); + + /* discard anything left over */ + if (server->total_read < buflen) + return cifs_readv_discard(server, mid); + + dequeue_mid(mid, false); + mid->resp_buf = server->smallbuf; + server->smallbuf = NULL; + return length; +} diff --git a/fs/smb/client/unc.c b/fs/smb/client/unc.c new file mode 100644 index 0000000000000..f6fc5e343ea42 --- /dev/null +++ b/fs/smb/client/unc.c @@ -0,0 +1,69 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Copyright (C) 2020, Microsoft Corporation. + * + * Author(s): Steve French + * Suresh Jayaraman + * Jeff Layton + */ + +#include +#include +#include +#include +#include "cifsglob.h" +#include "cifsproto.h" + +/* extract the host portion of the UNC string */ +char *extract_hostname(const char *unc) +{ + const char *src; + char *dst, *delim; + unsigned int len; + + /* skip double chars at beginning of string */ + /* BB: check validity of these bytes? */ + if (strlen(unc) < 3) + return ERR_PTR(-EINVAL); + for (src = unc; *src && *src == '\\'; src++) + ; + if (!*src) + return ERR_PTR(-EINVAL); + + /* delimiter between hostname and sharename is always '\\' now */ + delim = strchr(src, '\\'); + if (!delim) + return ERR_PTR(-EINVAL); + + len = delim - src; + dst = kmalloc((len + 1), GFP_KERNEL); + if (dst == NULL) + return ERR_PTR(-ENOMEM); + + memcpy(dst, src, len); + dst[len] = '\0'; + + return dst; +} + +char *extract_sharename(const char *unc) +{ + const char *src; + char *delim, *dst; + + /* skip double chars at the beginning */ + src = unc + 2; + + /* share name is always preceded by '\\' now */ + delim = strchr(src, '\\'); + if (!delim) + return ERR_PTR(-EINVAL); + delim++; + + /* caller has to free the memory */ + dst = kstrdup(delim, GFP_KERNEL); + if (!dst) + return ERR_PTR(-ENOMEM); + + return dst; +} diff --git a/fs/smb/client/winucase.c b/fs/smb/client/winucase.c new file mode 100644 index 0000000000000..2f075b5b50df0 --- /dev/null +++ b/fs/smb/client/winucase.c @@ -0,0 +1,649 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * + * Copyright (c) Jeffrey Layton , 2013 + * + * The const tables in this file were converted from the following info + * provided by Microsoft: + * + * 3.1.5.3 Mapping UTF-16 Strings to Upper Case: + * + * https://msdn.microsoft.com/en-us/library/hh877830.aspx + * http://www.microsoft.com/en-us/download/details.aspx?displaylang=en&id=10921 + * + * In particular, the table in "Windows 8 Upper Case Mapping Table.txt" was + * post-processed using the winucase_convert.pl script. + */ + +#include + +wchar_t cifs_toupper(wchar_t in); /* quiet sparse */ + +static const wchar_t t2_00[256] = { + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0041, 0x0042, 0x0043, 0x0044, 0x0045, 0x0046, 0x0047, + 0x0048, 0x0049, 0x004a, 0x004b, 0x004c, 0x004d, 0x004e, 0x004f, + 0x0050, 0x0051, 0x0052, 0x0053, 0x0054, 0x0055, 0x0056, 0x0057, + 0x0058, 0x0059, 0x005a, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x00c0, 0x00c1, 0x00c2, 0x00c3, 0x00c4, 0x00c5, 0x00c6, 0x00c7, + 0x00c8, 0x00c9, 0x00ca, 0x00cb, 0x00cc, 0x00cd, 0x00ce, 0x00cf, + 0x00d0, 0x00d1, 0x00d2, 0x00d3, 0x00d4, 0x00d5, 0x00d6, 0x0000, + 0x00d8, 0x00d9, 0x00da, 0x00db, 0x00dc, 0x00dd, 0x00de, 0x0178, +}; + +static const wchar_t t2_01[256] = { + 0x0000, 0x0100, 0x0000, 0x0102, 0x0000, 0x0104, 0x0000, 0x0106, + 0x0000, 0x0108, 0x0000, 0x010a, 0x0000, 0x010c, 0x0000, 0x010e, + 0x0000, 0x0110, 0x0000, 0x0112, 0x0000, 0x0114, 0x0000, 0x0116, + 0x0000, 0x0118, 0x0000, 0x011a, 0x0000, 0x011c, 0x0000, 0x011e, + 0x0000, 0x0120, 0x0000, 0x0122, 0x0000, 0x0124, 0x0000, 0x0126, + 0x0000, 0x0128, 0x0000, 0x012a, 0x0000, 0x012c, 0x0000, 0x012e, + 0x0000, 0x0000, 0x0000, 0x0132, 0x0000, 0x0134, 0x0000, 0x0136, + 0x0000, 0x0000, 0x0139, 0x0000, 0x013b, 0x0000, 0x013d, 0x0000, + 0x013f, 0x0000, 0x0141, 0x0000, 0x0143, 0x0000, 0x0145, 0x0000, + 0x0147, 0x0000, 0x0000, 0x014a, 0x0000, 0x014c, 0x0000, 0x014e, + 0x0000, 0x0150, 0x0000, 0x0152, 0x0000, 0x0154, 0x0000, 0x0156, + 0x0000, 0x0158, 0x0000, 0x015a, 0x0000, 0x015c, 0x0000, 0x015e, + 0x0000, 0x0160, 0x0000, 0x0162, 0x0000, 0x0164, 0x0000, 0x0166, + 0x0000, 0x0168, 0x0000, 0x016a, 0x0000, 0x016c, 0x0000, 0x016e, + 0x0000, 0x0170, 0x0000, 0x0172, 0x0000, 0x0174, 0x0000, 0x0176, + 0x0000, 0x0000, 0x0179, 0x0000, 0x017b, 0x0000, 0x017d, 0x0000, + 0x0243, 0x0000, 0x0000, 0x0182, 0x0000, 0x0184, 0x0000, 0x0000, + 0x0187, 0x0000, 0x0000, 0x0000, 0x018b, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0191, 0x0000, 0x0000, 0x01f6, 0x0000, 0x0000, + 0x0000, 0x0198, 0x023d, 0x0000, 0x0000, 0x0000, 0x0220, 0x0000, + 0x0000, 0x01a0, 0x0000, 0x01a2, 0x0000, 0x01a4, 0x0000, 0x0000, + 0x01a7, 0x0000, 0x0000, 0x0000, 0x0000, 0x01ac, 0x0000, 0x0000, + 0x01af, 0x0000, 0x0000, 0x0000, 0x01b3, 0x0000, 0x01b5, 0x0000, + 0x0000, 0x01b8, 0x0000, 0x0000, 0x0000, 0x01bc, 0x0000, 0x01f7, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x01c4, 0x0000, + 0x0000, 0x01c7, 0x0000, 0x0000, 0x01ca, 0x0000, 0x01cd, 0x0000, + 0x01cf, 0x0000, 0x01d1, 0x0000, 0x01d3, 0x0000, 0x01d5, 0x0000, + 0x01d7, 0x0000, 0x01d9, 0x0000, 0x01db, 0x018e, 0x0000, 0x01de, + 0x0000, 0x01e0, 0x0000, 0x01e2, 0x0000, 0x01e4, 0x0000, 0x01e6, + 0x0000, 0x01e8, 0x0000, 0x01ea, 0x0000, 0x01ec, 0x0000, 0x01ee, + 0x0000, 0x0000, 0x0000, 0x01f1, 0x0000, 0x01f4, 0x0000, 0x0000, + 0x0000, 0x01f8, 0x0000, 0x01fa, 0x0000, 0x01fc, 0x0000, 0x01fe, +}; + +static const wchar_t t2_02[256] = { + 0x0000, 0x0200, 0x0000, 0x0202, 0x0000, 0x0204, 0x0000, 0x0206, + 0x0000, 0x0208, 0x0000, 0x020a, 0x0000, 0x020c, 0x0000, 0x020e, + 0x0000, 0x0210, 0x0000, 0x0212, 0x0000, 0x0214, 0x0000, 0x0216, + 0x0000, 0x0218, 0x0000, 0x021a, 0x0000, 0x021c, 0x0000, 0x021e, + 0x0000, 0x0000, 0x0000, 0x0222, 0x0000, 0x0224, 0x0000, 0x0226, + 0x0000, 0x0228, 0x0000, 0x022a, 0x0000, 0x022c, 0x0000, 0x022e, + 0x0000, 0x0230, 0x0000, 0x0232, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x023b, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0241, 0x0000, 0x0000, 0x0000, 0x0000, 0x0246, + 0x0000, 0x0248, 0x0000, 0x024a, 0x0000, 0x024c, 0x0000, 0x024e, + 0x2c6f, 0x2c6d, 0x0000, 0x0181, 0x0186, 0x0000, 0x0189, 0x018a, + 0x0000, 0x018f, 0x0000, 0x0190, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0193, 0x0000, 0x0000, 0x0194, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0197, 0x0196, 0x0000, 0x2c62, 0x0000, 0x0000, 0x0000, 0x019c, + 0x0000, 0x2c6e, 0x019d, 0x0000, 0x0000, 0x019f, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x2c64, 0x0000, 0x0000, + 0x01a6, 0x0000, 0x0000, 0x01a9, 0x0000, 0x0000, 0x0000, 0x0000, + 0x01ae, 0x0244, 0x01b1, 0x01b2, 0x0245, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x01b7, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, +}; + +static const wchar_t t2_03[256] = { + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0370, 0x0000, 0x0372, 0x0000, 0x0000, 0x0000, 0x0376, + 0x0000, 0x0000, 0x0000, 0x03fd, 0x03fe, 0x03ff, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0386, 0x0388, 0x0389, 0x038a, + 0x0000, 0x0391, 0x0392, 0x0393, 0x0394, 0x0395, 0x0396, 0x0397, + 0x0398, 0x0399, 0x039a, 0x039b, 0x039c, 0x039d, 0x039e, 0x039f, + 0x03a0, 0x03a1, 0x0000, 0x03a3, 0x03a4, 0x03a5, 0x03a6, 0x03a7, + 0x03a8, 0x03a9, 0x03aa, 0x03ab, 0x038c, 0x038e, 0x038f, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x03cf, + 0x0000, 0x03d8, 0x0000, 0x03da, 0x0000, 0x03dc, 0x0000, 0x03de, + 0x0000, 0x03e0, 0x0000, 0x03e2, 0x0000, 0x03e4, 0x0000, 0x03e6, + 0x0000, 0x03e8, 0x0000, 0x03ea, 0x0000, 0x03ec, 0x0000, 0x03ee, + 0x0000, 0x0000, 0x03f9, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x03f7, 0x0000, 0x0000, 0x03fa, 0x0000, 0x0000, 0x0000, 0x0000, +}; + +static const wchar_t t2_04[256] = { + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0410, 0x0411, 0x0412, 0x0413, 0x0414, 0x0415, 0x0416, 0x0417, + 0x0418, 0x0419, 0x041a, 0x041b, 0x041c, 0x041d, 0x041e, 0x041f, + 0x0420, 0x0421, 0x0422, 0x0423, 0x0424, 0x0425, 0x0426, 0x0427, + 0x0428, 0x0429, 0x042a, 0x042b, 0x042c, 0x042d, 0x042e, 0x042f, + 0x0400, 0x0401, 0x0402, 0x0403, 0x0404, 0x0405, 0x0406, 0x0407, + 0x0408, 0x0409, 0x040a, 0x040b, 0x040c, 0x040d, 0x040e, 0x040f, + 0x0000, 0x0460, 0x0000, 0x0462, 0x0000, 0x0464, 0x0000, 0x0466, + 0x0000, 0x0468, 0x0000, 0x046a, 0x0000, 0x046c, 0x0000, 0x046e, + 0x0000, 0x0470, 0x0000, 0x0472, 0x0000, 0x0474, 0x0000, 0x0476, + 0x0000, 0x0478, 0x0000, 0x047a, 0x0000, 0x047c, 0x0000, 0x047e, + 0x0000, 0x0480, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x048a, 0x0000, 0x048c, 0x0000, 0x048e, + 0x0000, 0x0490, 0x0000, 0x0492, 0x0000, 0x0494, 0x0000, 0x0496, + 0x0000, 0x0498, 0x0000, 0x049a, 0x0000, 0x049c, 0x0000, 0x049e, + 0x0000, 0x04a0, 0x0000, 0x04a2, 0x0000, 0x04a4, 0x0000, 0x04a6, + 0x0000, 0x04a8, 0x0000, 0x04aa, 0x0000, 0x04ac, 0x0000, 0x04ae, + 0x0000, 0x04b0, 0x0000, 0x04b2, 0x0000, 0x04b4, 0x0000, 0x04b6, + 0x0000, 0x04b8, 0x0000, 0x04ba, 0x0000, 0x04bc, 0x0000, 0x04be, + 0x0000, 0x0000, 0x04c1, 0x0000, 0x04c3, 0x0000, 0x04c5, 0x0000, + 0x04c7, 0x0000, 0x04c9, 0x0000, 0x04cb, 0x0000, 0x04cd, 0x04c0, + 0x0000, 0x04d0, 0x0000, 0x04d2, 0x0000, 0x04d4, 0x0000, 0x04d6, + 0x0000, 0x04d8, 0x0000, 0x04da, 0x0000, 0x04dc, 0x0000, 0x04de, + 0x0000, 0x04e0, 0x0000, 0x04e2, 0x0000, 0x04e4, 0x0000, 0x04e6, + 0x0000, 0x04e8, 0x0000, 0x04ea, 0x0000, 0x04ec, 0x0000, 0x04ee, + 0x0000, 0x04f0, 0x0000, 0x04f2, 0x0000, 0x04f4, 0x0000, 0x04f6, + 0x0000, 0x04f8, 0x0000, 0x04fa, 0x0000, 0x04fc, 0x0000, 0x04fe, +}; + +static const wchar_t t2_05[256] = { + 0x0000, 0x0500, 0x0000, 0x0502, 0x0000, 0x0504, 0x0000, 0x0506, + 0x0000, 0x0508, 0x0000, 0x050a, 0x0000, 0x050c, 0x0000, 0x050e, + 0x0000, 0x0510, 0x0000, 0x0512, 0x0000, 0x0514, 0x0000, 0x0516, + 0x0000, 0x0518, 0x0000, 0x051a, 0x0000, 0x051c, 0x0000, 0x051e, + 0x0000, 0x0520, 0x0000, 0x0522, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0531, 0x0532, 0x0533, 0x0534, 0x0535, 0x0536, 0x0537, + 0x0538, 0x0539, 0x053a, 0x053b, 0x053c, 0x053d, 0x053e, 0x053f, + 0x0540, 0x0541, 0x0542, 0x0543, 0x0544, 0x0545, 0x0546, 0x0547, + 0x0548, 0x0549, 0x054a, 0x054b, 0x054c, 0x054d, 0x054e, 0x054f, + 0x0550, 0x0551, 0x0552, 0x0553, 0x0554, 0x0555, 0x0556, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, +}; + +static const wchar_t t2_1d[256] = { + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0xa77d, 0x0000, 0x0000, 0x0000, 0x2c63, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, +}; + +static const wchar_t t2_1e[256] = { + 0x0000, 0x1e00, 0x0000, 0x1e02, 0x0000, 0x1e04, 0x0000, 0x1e06, + 0x0000, 0x1e08, 0x0000, 0x1e0a, 0x0000, 0x1e0c, 0x0000, 0x1e0e, + 0x0000, 0x1e10, 0x0000, 0x1e12, 0x0000, 0x1e14, 0x0000, 0x1e16, + 0x0000, 0x1e18, 0x0000, 0x1e1a, 0x0000, 0x1e1c, 0x0000, 0x1e1e, + 0x0000, 0x1e20, 0x0000, 0x1e22, 0x0000, 0x1e24, 0x0000, 0x1e26, + 0x0000, 0x1e28, 0x0000, 0x1e2a, 0x0000, 0x1e2c, 0x0000, 0x1e2e, + 0x0000, 0x1e30, 0x0000, 0x1e32, 0x0000, 0x1e34, 0x0000, 0x1e36, + 0x0000, 0x1e38, 0x0000, 0x1e3a, 0x0000, 0x1e3c, 0x0000, 0x1e3e, + 0x0000, 0x1e40, 0x0000, 0x1e42, 0x0000, 0x1e44, 0x0000, 0x1e46, + 0x0000, 0x1e48, 0x0000, 0x1e4a, 0x0000, 0x1e4c, 0x0000, 0x1e4e, + 0x0000, 0x1e50, 0x0000, 0x1e52, 0x0000, 0x1e54, 0x0000, 0x1e56, + 0x0000, 0x1e58, 0x0000, 0x1e5a, 0x0000, 0x1e5c, 0x0000, 0x1e5e, + 0x0000, 0x1e60, 0x0000, 0x1e62, 0x0000, 0x1e64, 0x0000, 0x1e66, + 0x0000, 0x1e68, 0x0000, 0x1e6a, 0x0000, 0x1e6c, 0x0000, 0x1e6e, + 0x0000, 0x1e70, 0x0000, 0x1e72, 0x0000, 0x1e74, 0x0000, 0x1e76, + 0x0000, 0x1e78, 0x0000, 0x1e7a, 0x0000, 0x1e7c, 0x0000, 0x1e7e, + 0x0000, 0x1e80, 0x0000, 0x1e82, 0x0000, 0x1e84, 0x0000, 0x1e86, + 0x0000, 0x1e88, 0x0000, 0x1e8a, 0x0000, 0x1e8c, 0x0000, 0x1e8e, + 0x0000, 0x1e90, 0x0000, 0x1e92, 0x0000, 0x1e94, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x1ea0, 0x0000, 0x1ea2, 0x0000, 0x1ea4, 0x0000, 0x1ea6, + 0x0000, 0x1ea8, 0x0000, 0x1eaa, 0x0000, 0x1eac, 0x0000, 0x1eae, + 0x0000, 0x1eb0, 0x0000, 0x1eb2, 0x0000, 0x1eb4, 0x0000, 0x1eb6, + 0x0000, 0x1eb8, 0x0000, 0x1eba, 0x0000, 0x1ebc, 0x0000, 0x1ebe, + 0x0000, 0x1ec0, 0x0000, 0x1ec2, 0x0000, 0x1ec4, 0x0000, 0x1ec6, + 0x0000, 0x1ec8, 0x0000, 0x1eca, 0x0000, 0x1ecc, 0x0000, 0x1ece, + 0x0000, 0x1ed0, 0x0000, 0x1ed2, 0x0000, 0x1ed4, 0x0000, 0x1ed6, + 0x0000, 0x1ed8, 0x0000, 0x1eda, 0x0000, 0x1edc, 0x0000, 0x1ede, + 0x0000, 0x1ee0, 0x0000, 0x1ee2, 0x0000, 0x1ee4, 0x0000, 0x1ee6, + 0x0000, 0x1ee8, 0x0000, 0x1eea, 0x0000, 0x1eec, 0x0000, 0x1eee, + 0x0000, 0x1ef0, 0x0000, 0x1ef2, 0x0000, 0x1ef4, 0x0000, 0x1ef6, + 0x0000, 0x1ef8, 0x0000, 0x1efa, 0x0000, 0x1efc, 0x0000, 0x1efe, +}; + +static const wchar_t t2_1f[256] = { + 0x1f08, 0x1f09, 0x1f0a, 0x1f0b, 0x1f0c, 0x1f0d, 0x1f0e, 0x1f0f, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x1f18, 0x1f19, 0x1f1a, 0x1f1b, 0x1f1c, 0x1f1d, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x1f28, 0x1f29, 0x1f2a, 0x1f2b, 0x1f2c, 0x1f2d, 0x1f2e, 0x1f2f, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x1f38, 0x1f39, 0x1f3a, 0x1f3b, 0x1f3c, 0x1f3d, 0x1f3e, 0x1f3f, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x1f48, 0x1f49, 0x1f4a, 0x1f4b, 0x1f4c, 0x1f4d, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x1f59, 0x0000, 0x1f5b, 0x0000, 0x1f5d, 0x0000, 0x1f5f, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x1f68, 0x1f69, 0x1f6a, 0x1f6b, 0x1f6c, 0x1f6d, 0x1f6e, 0x1f6f, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x1fba, 0x1fbb, 0x1fc8, 0x1fc9, 0x1fca, 0x1fcb, 0x1fda, 0x1fdb, + 0x1ff8, 0x1ff9, 0x1fea, 0x1feb, 0x1ffa, 0x1ffb, 0x0000, 0x0000, + 0x1f88, 0x1f89, 0x1f8a, 0x1f8b, 0x1f8c, 0x1f8d, 0x1f8e, 0x1f8f, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x1f98, 0x1f99, 0x1f9a, 0x1f9b, 0x1f9c, 0x1f9d, 0x1f9e, 0x1f9f, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x1fa8, 0x1fa9, 0x1faa, 0x1fab, 0x1fac, 0x1fad, 0x1fae, 0x1faf, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x1fb8, 0x1fb9, 0x0000, 0x1fbc, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x1fcc, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x1fd8, 0x1fd9, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x1fe8, 0x1fe9, 0x0000, 0x0000, 0x0000, 0x1fec, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x1ffc, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, +}; + +static const wchar_t t2_21[256] = { + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x2132, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x2160, 0x2161, 0x2162, 0x2163, 0x2164, 0x2165, 0x2166, 0x2167, + 0x2168, 0x2169, 0x216a, 0x216b, 0x216c, 0x216d, 0x216e, 0x216f, + 0x0000, 0x0000, 0x0000, 0x0000, 0x2183, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, +}; + +static const wchar_t t2_24[256] = { + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x24b6, 0x24b7, 0x24b8, 0x24b9, 0x24ba, 0x24bb, 0x24bc, 0x24bd, + 0x24be, 0x24bf, 0x24c0, 0x24c1, 0x24c2, 0x24c3, 0x24c4, 0x24c5, + 0x24c6, 0x24c7, 0x24c8, 0x24c9, 0x24ca, 0x24cb, 0x24cc, 0x24cd, + 0x24ce, 0x24cf, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, +}; + +static const wchar_t t2_2c[256] = { + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x2c00, 0x2c01, 0x2c02, 0x2c03, 0x2c04, 0x2c05, 0x2c06, 0x2c07, + 0x2c08, 0x2c09, 0x2c0a, 0x2c0b, 0x2c0c, 0x2c0d, 0x2c0e, 0x2c0f, + 0x2c10, 0x2c11, 0x2c12, 0x2c13, 0x2c14, 0x2c15, 0x2c16, 0x2c17, + 0x2c18, 0x2c19, 0x2c1a, 0x2c1b, 0x2c1c, 0x2c1d, 0x2c1e, 0x2c1f, + 0x2c20, 0x2c21, 0x2c22, 0x2c23, 0x2c24, 0x2c25, 0x2c26, 0x2c27, + 0x2c28, 0x2c29, 0x2c2a, 0x2c2b, 0x2c2c, 0x2c2d, 0x2c2e, 0x0000, + 0x0000, 0x2c60, 0x0000, 0x0000, 0x0000, 0x023a, 0x023e, 0x0000, + 0x2c67, 0x0000, 0x2c69, 0x0000, 0x2c6b, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x2c72, 0x0000, 0x0000, 0x2c75, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x2c80, 0x0000, 0x2c82, 0x0000, 0x2c84, 0x0000, 0x2c86, + 0x0000, 0x2c88, 0x0000, 0x2c8a, 0x0000, 0x2c8c, 0x0000, 0x2c8e, + 0x0000, 0x2c90, 0x0000, 0x2c92, 0x0000, 0x2c94, 0x0000, 0x2c96, + 0x0000, 0x2c98, 0x0000, 0x2c9a, 0x0000, 0x2c9c, 0x0000, 0x2c9e, + 0x0000, 0x2ca0, 0x0000, 0x2ca2, 0x0000, 0x2ca4, 0x0000, 0x2ca6, + 0x0000, 0x2ca8, 0x0000, 0x2caa, 0x0000, 0x2cac, 0x0000, 0x2cae, + 0x0000, 0x2cb0, 0x0000, 0x2cb2, 0x0000, 0x2cb4, 0x0000, 0x2cb6, + 0x0000, 0x2cb8, 0x0000, 0x2cba, 0x0000, 0x2cbc, 0x0000, 0x2cbe, + 0x0000, 0x2cc0, 0x0000, 0x2cc2, 0x0000, 0x2cc4, 0x0000, 0x2cc6, + 0x0000, 0x2cc8, 0x0000, 0x2cca, 0x0000, 0x2ccc, 0x0000, 0x2cce, + 0x0000, 0x2cd0, 0x0000, 0x2cd2, 0x0000, 0x2cd4, 0x0000, 0x2cd6, + 0x0000, 0x2cd8, 0x0000, 0x2cda, 0x0000, 0x2cdc, 0x0000, 0x2cde, + 0x0000, 0x2ce0, 0x0000, 0x2ce2, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, +}; + +static const wchar_t t2_2d[256] = { + 0x10a0, 0x10a1, 0x10a2, 0x10a3, 0x10a4, 0x10a5, 0x10a6, 0x10a7, + 0x10a8, 0x10a9, 0x10aa, 0x10ab, 0x10ac, 0x10ad, 0x10ae, 0x10af, + 0x10b0, 0x10b1, 0x10b2, 0x10b3, 0x10b4, 0x10b5, 0x10b6, 0x10b7, + 0x10b8, 0x10b9, 0x10ba, 0x10bb, 0x10bc, 0x10bd, 0x10be, 0x10bf, + 0x10c0, 0x10c1, 0x10c2, 0x10c3, 0x10c4, 0x10c5, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, +}; + +static const wchar_t t2_a6[256] = { + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0xa640, 0x0000, 0xa642, 0x0000, 0xa644, 0x0000, 0xa646, + 0x0000, 0xa648, 0x0000, 0xa64a, 0x0000, 0xa64c, 0x0000, 0xa64e, + 0x0000, 0xa650, 0x0000, 0xa652, 0x0000, 0xa654, 0x0000, 0xa656, + 0x0000, 0xa658, 0x0000, 0xa65a, 0x0000, 0xa65c, 0x0000, 0xa65e, + 0x0000, 0x0000, 0x0000, 0xa662, 0x0000, 0xa664, 0x0000, 0xa666, + 0x0000, 0xa668, 0x0000, 0xa66a, 0x0000, 0xa66c, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0xa680, 0x0000, 0xa682, 0x0000, 0xa684, 0x0000, 0xa686, + 0x0000, 0xa688, 0x0000, 0xa68a, 0x0000, 0xa68c, 0x0000, 0xa68e, + 0x0000, 0xa690, 0x0000, 0xa692, 0x0000, 0xa694, 0x0000, 0xa696, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, +}; + +static const wchar_t t2_a7[256] = { + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0xa722, 0x0000, 0xa724, 0x0000, 0xa726, + 0x0000, 0xa728, 0x0000, 0xa72a, 0x0000, 0xa72c, 0x0000, 0xa72e, + 0x0000, 0x0000, 0x0000, 0xa732, 0x0000, 0xa734, 0x0000, 0xa736, + 0x0000, 0xa738, 0x0000, 0xa73a, 0x0000, 0xa73c, 0x0000, 0xa73e, + 0x0000, 0xa740, 0x0000, 0xa742, 0x0000, 0xa744, 0x0000, 0xa746, + 0x0000, 0xa748, 0x0000, 0xa74a, 0x0000, 0xa74c, 0x0000, 0xa74e, + 0x0000, 0xa750, 0x0000, 0xa752, 0x0000, 0xa754, 0x0000, 0xa756, + 0x0000, 0xa758, 0x0000, 0xa75a, 0x0000, 0xa75c, 0x0000, 0xa75e, + 0x0000, 0xa760, 0x0000, 0xa762, 0x0000, 0xa764, 0x0000, 0xa766, + 0x0000, 0xa768, 0x0000, 0xa76a, 0x0000, 0xa76c, 0x0000, 0xa76e, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0xa779, 0x0000, 0xa77b, 0x0000, 0x0000, 0xa77e, + 0x0000, 0xa780, 0x0000, 0xa782, 0x0000, 0xa784, 0x0000, 0xa786, + 0x0000, 0x0000, 0x0000, 0x0000, 0xa78b, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, +}; + +static const wchar_t t2_ff[256] = { + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0xff21, 0xff22, 0xff23, 0xff24, 0xff25, 0xff26, 0xff27, + 0xff28, 0xff29, 0xff2a, 0xff2b, 0xff2c, 0xff2d, 0xff2e, 0xff2f, + 0xff30, 0xff31, 0xff32, 0xff33, 0xff34, 0xff35, 0xff36, 0xff37, + 0xff38, 0xff39, 0xff3a, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, +}; + +static const wchar_t *const toplevel[256] = { + t2_00, t2_01, t2_02, t2_03, t2_04, t2_05, NULL, NULL, + NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, + NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, + NULL, NULL, NULL, NULL, NULL, t2_1d, t2_1e, t2_1f, + NULL, t2_21, NULL, NULL, t2_24, NULL, NULL, NULL, + NULL, NULL, NULL, NULL, t2_2c, t2_2d, NULL, NULL, + NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, + NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, + NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, + NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, + NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, + NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, + NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, + NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, + NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, + NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, + NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, + NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, + NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, + NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, + NULL, NULL, NULL, NULL, NULL, NULL, t2_a6, t2_a7, + NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, + NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, + NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, + NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, + NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, + NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, + NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, + NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, + NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, + NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, + NULL, NULL, NULL, NULL, NULL, NULL, NULL, t2_ff, +}; + +/** + * cifs_toupper - convert a wchar_t from lower to uppercase + * @in: character to convert from lower to uppercase + * + * This function consults the static tables above to convert a wchar_t from + * lower to uppercase. In the event that there is no mapping, the original + * "in" character is returned. + */ +wchar_t +cifs_toupper(wchar_t in) +{ + unsigned char idx; + const wchar_t *tbl; + wchar_t out; + + /* grab upper byte */ + idx = (in & 0xff00) >> 8; + + /* find pointer to 2nd layer table */ + tbl = toplevel[idx]; + if (!tbl) + return in; + + /* grab lower byte */ + idx = in & 0xff; + + /* look up character in table */ + out = tbl[idx]; + if (out) + return out; + + return in; +} diff --git a/fs/smb/client/xattr.c b/fs/smb/client/xattr.c new file mode 100644 index 0000000000000..998fa51f9b684 --- /dev/null +++ b/fs/smb/client/xattr.c @@ -0,0 +1,555 @@ +// SPDX-License-Identifier: LGPL-2.1 +/* + * + * Copyright (c) International Business Machines Corp., 2003, 2007 + * Author(s): Steve French (sfrench@us.ibm.com) + * + */ + +#include +#include +#include +#include +#include "cifsfs.h" +#include "cifspdu.h" +#include "cifsglob.h" +#include "cifsproto.h" +#include "cifs_debug.h" +#include "cifs_fs_sb.h" +#include "cifs_unicode.h" +#include "cifs_ioctl.h" + +#define MAX_EA_VALUE_SIZE CIFSMaxBufSize +#define CIFS_XATTR_CIFS_ACL "system.cifs_acl" /* DACL only */ +#define CIFS_XATTR_CIFS_NTSD "system.cifs_ntsd" /* owner plus DACL */ +#define CIFS_XATTR_CIFS_NTSD_FULL "system.cifs_ntsd_full" /* owner/DACL/SACL */ +#define CIFS_XATTR_ATTRIB "cifs.dosattrib" /* full name: user.cifs.dosattrib */ +#define CIFS_XATTR_CREATETIME "cifs.creationtime" /* user.cifs.creationtime */ +/* + * Although these three are just aliases for the above, need to move away from + * confusing users and using the 20+ year old term 'cifs' when it is no longer + * secure, replaced by SMB2 (then even more highly secure SMB3) many years ago + */ +#define SMB3_XATTR_CIFS_ACL "system.smb3_acl" /* DACL only */ +#define SMB3_XATTR_CIFS_NTSD "system.smb3_ntsd" /* owner plus DACL */ +#define SMB3_XATTR_CIFS_NTSD_FULL "system.smb3_ntsd_full" /* owner/DACL/SACL */ +#define SMB3_XATTR_ATTRIB "smb3.dosattrib" /* full name: user.smb3.dosattrib */ +#define SMB3_XATTR_CREATETIME "smb3.creationtime" /* user.smb3.creationtime */ +/* BB need to add server (Samba e.g) support for security and trusted prefix */ + +enum { XATTR_USER, XATTR_CIFS_ACL, XATTR_ACL_ACCESS, XATTR_ACL_DEFAULT, + XATTR_CIFS_NTSD, XATTR_CIFS_NTSD_FULL }; + +static int cifs_attrib_set(unsigned int xid, struct cifs_tcon *pTcon, + struct inode *inode, const char *full_path, + const void *value, size_t size) +{ + ssize_t rc = -EOPNOTSUPP; + __u32 *pattrib = (__u32 *)value; + __u32 attrib; + FILE_BASIC_INFO info_buf; + + if ((value == NULL) || (size != sizeof(__u32))) + return -ERANGE; + + memset(&info_buf, 0, sizeof(info_buf)); + attrib = *pattrib; + info_buf.Attributes = cpu_to_le32(attrib); + if (pTcon->ses->server->ops->set_file_info) + rc = pTcon->ses->server->ops->set_file_info(inode, full_path, + &info_buf, xid); + if (rc == 0) + CIFS_I(inode)->cifsAttrs = attrib; + + return rc; +} + +static int cifs_creation_time_set(unsigned int xid, struct cifs_tcon *pTcon, + struct inode *inode, const char *full_path, + const void *value, size_t size) +{ + ssize_t rc = -EOPNOTSUPP; + __u64 *pcreation_time = (__u64 *)value; + __u64 creation_time; + FILE_BASIC_INFO info_buf; + + if ((value == NULL) || (size != sizeof(__u64))) + return -ERANGE; + + memset(&info_buf, 0, sizeof(info_buf)); + creation_time = *pcreation_time; + info_buf.CreationTime = cpu_to_le64(creation_time); + if (pTcon->ses->server->ops->set_file_info) + rc = pTcon->ses->server->ops->set_file_info(inode, full_path, + &info_buf, xid); + if (rc == 0) + CIFS_I(inode)->createtime = creation_time; + + return rc; +} + +static int cifs_xattr_set(const struct xattr_handler *handler, + struct user_namespace *mnt_userns, + struct dentry *dentry, struct inode *inode, + const char *name, const void *value, + size_t size, int flags) +{ + int rc = -EOPNOTSUPP; + unsigned int xid; + struct super_block *sb = dentry->d_sb; + struct cifs_sb_info *cifs_sb = CIFS_SB(sb); + struct tcon_link *tlink; + struct cifs_tcon *pTcon; + const char *full_path; + void *page; + + tlink = cifs_sb_tlink(cifs_sb); + if (IS_ERR(tlink)) + return PTR_ERR(tlink); + pTcon = tlink_tcon(tlink); + + xid = get_xid(); + page = alloc_dentry_path(); + + full_path = build_path_from_dentry(dentry, page); + if (IS_ERR(full_path)) { + rc = PTR_ERR(full_path); + goto out; + } + /* return dos attributes as pseudo xattr */ + /* return alt name if available as pseudo attr */ + + /* if proc/fs/cifs/streamstoxattr is set then + search server for EAs or streams to + returns as xattrs */ + if (size > MAX_EA_VALUE_SIZE) { + cifs_dbg(FYI, "size of EA value too large\n"); + rc = -EOPNOTSUPP; + goto out; + } + + switch (handler->flags) { + case XATTR_USER: + cifs_dbg(FYI, "%s:setting user xattr %s\n", __func__, name); + if ((strcmp(name, CIFS_XATTR_ATTRIB) == 0) || + (strcmp(name, SMB3_XATTR_ATTRIB) == 0)) { + rc = cifs_attrib_set(xid, pTcon, inode, full_path, + value, size); + if (rc == 0) /* force revalidate of the inode */ + CIFS_I(inode)->time = 0; + break; + } else if ((strcmp(name, CIFS_XATTR_CREATETIME) == 0) || + (strcmp(name, SMB3_XATTR_CREATETIME) == 0)) { + rc = cifs_creation_time_set(xid, pTcon, inode, + full_path, value, size); + if (rc == 0) /* force revalidate of the inode */ + CIFS_I(inode)->time = 0; + break; + } + + if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_NO_XATTR) + goto out; + + if (pTcon->ses->server->ops->set_EA) + rc = pTcon->ses->server->ops->set_EA(xid, pTcon, + full_path, name, value, (__u16)size, + cifs_sb->local_nls, cifs_sb); + break; + + case XATTR_CIFS_ACL: + case XATTR_CIFS_NTSD: + case XATTR_CIFS_NTSD_FULL: { + struct cifs_ntsd *pacl; + + if (!value) + goto out; + pacl = kmalloc(size, GFP_KERNEL); + if (!pacl) { + rc = -ENOMEM; + } else { + memcpy(pacl, value, size); + if (pTcon->ses->server->ops->set_acl) { + int aclflags = 0; + rc = 0; + + switch (handler->flags) { + case XATTR_CIFS_NTSD_FULL: + aclflags = (CIFS_ACL_OWNER | + CIFS_ACL_GROUP | + CIFS_ACL_DACL | + CIFS_ACL_SACL); + break; + case XATTR_CIFS_NTSD: + aclflags = (CIFS_ACL_OWNER | + CIFS_ACL_GROUP | + CIFS_ACL_DACL); + break; + case XATTR_CIFS_ACL: + default: + aclflags = CIFS_ACL_DACL; + } + + rc = pTcon->ses->server->ops->set_acl(pacl, + size, inode, full_path, aclflags); + } else { + rc = -EOPNOTSUPP; + } + if (rc == 0) /* force revalidate of the inode */ + CIFS_I(inode)->time = 0; + kfree(pacl); + } + break; + } + +#ifdef CONFIG_CIFS_ALLOW_INSECURE_LEGACY + case XATTR_ACL_ACCESS: +#ifdef CONFIG_CIFS_POSIX + if (!value) + goto out; + if (sb->s_flags & SB_POSIXACL) + rc = CIFSSMBSetPosixACL(xid, pTcon, full_path, + value, (const int)size, + ACL_TYPE_ACCESS, cifs_sb->local_nls, + cifs_remap(cifs_sb)); +#endif /* CONFIG_CIFS_POSIX */ + break; + + case XATTR_ACL_DEFAULT: +#ifdef CONFIG_CIFS_POSIX + if (!value) + goto out; + if (sb->s_flags & SB_POSIXACL) + rc = CIFSSMBSetPosixACL(xid, pTcon, full_path, + value, (const int)size, + ACL_TYPE_DEFAULT, cifs_sb->local_nls, + cifs_remap(cifs_sb)); +#endif /* CONFIG_CIFS_POSIX */ + break; +#endif /* CONFIG_CIFS_ALLOW_INSECURE_LEGACY */ + } + +out: + free_dentry_path(page); + free_xid(xid); + cifs_put_tlink(tlink); + return rc; +} + +static int cifs_attrib_get(struct dentry *dentry, + struct inode *inode, void *value, + size_t size) +{ + ssize_t rc; + __u32 *pattribute; + + rc = cifs_revalidate_dentry_attr(dentry); + + if (rc) + return rc; + + if ((value == NULL) || (size == 0)) + return sizeof(__u32); + else if (size < sizeof(__u32)) + return -ERANGE; + + /* return dos attributes as pseudo xattr */ + pattribute = (__u32 *)value; + *pattribute = CIFS_I(inode)->cifsAttrs; + + return sizeof(__u32); +} + +static int cifs_creation_time_get(struct dentry *dentry, struct inode *inode, + void *value, size_t size) +{ + ssize_t rc; + __u64 *pcreatetime; + + rc = cifs_revalidate_dentry_attr(dentry); + if (rc) + return rc; + + if ((value == NULL) || (size == 0)) + return sizeof(__u64); + else if (size < sizeof(__u64)) + return -ERANGE; + + /* return dos attributes as pseudo xattr */ + pcreatetime = (__u64 *)value; + *pcreatetime = CIFS_I(inode)->createtime; + return sizeof(__u64); +} + + +static int cifs_xattr_get(const struct xattr_handler *handler, + struct dentry *dentry, struct inode *inode, + const char *name, void *value, size_t size) +{ + ssize_t rc = -EOPNOTSUPP; + unsigned int xid; + struct super_block *sb = dentry->d_sb; + struct cifs_sb_info *cifs_sb = CIFS_SB(sb); + struct tcon_link *tlink; + struct cifs_tcon *pTcon; + const char *full_path; + void *page; + + tlink = cifs_sb_tlink(cifs_sb); + if (IS_ERR(tlink)) + return PTR_ERR(tlink); + pTcon = tlink_tcon(tlink); + + xid = get_xid(); + page = alloc_dentry_path(); + + full_path = build_path_from_dentry(dentry, page); + if (IS_ERR(full_path)) { + rc = PTR_ERR(full_path); + goto out; + } + + /* return alt name if available as pseudo attr */ + switch (handler->flags) { + case XATTR_USER: + cifs_dbg(FYI, "%s:querying user xattr %s\n", __func__, name); + if ((strcmp(name, CIFS_XATTR_ATTRIB) == 0) || + (strcmp(name, SMB3_XATTR_ATTRIB) == 0)) { + rc = cifs_attrib_get(dentry, inode, value, size); + break; + } else if ((strcmp(name, CIFS_XATTR_CREATETIME) == 0) || + (strcmp(name, SMB3_XATTR_CREATETIME) == 0)) { + rc = cifs_creation_time_get(dentry, inode, value, size); + break; + } + + if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_NO_XATTR) + goto out; + + if (pTcon->ses->server->ops->query_all_EAs) + rc = pTcon->ses->server->ops->query_all_EAs(xid, pTcon, + full_path, name, value, size, cifs_sb); + break; + + case XATTR_CIFS_ACL: + case XATTR_CIFS_NTSD: + case XATTR_CIFS_NTSD_FULL: { + /* + * fetch owner, DACL, and SACL if asked for full descriptor, + * fetch owner and DACL otherwise + */ + u32 acllen, extra_info; + struct cifs_ntsd *pacl; + + if (pTcon->ses->server->ops->get_acl == NULL) + goto out; /* rc already EOPNOTSUPP */ + + if (handler->flags == XATTR_CIFS_NTSD_FULL) { + extra_info = SACL_SECINFO; + } else { + extra_info = 0; + } + pacl = pTcon->ses->server->ops->get_acl(cifs_sb, + inode, full_path, &acllen, extra_info); + if (IS_ERR(pacl)) { + rc = PTR_ERR(pacl); + cifs_dbg(VFS, "%s: error %zd getting sec desc\n", + __func__, rc); + } else { + if (value) { + if (acllen > size) + acllen = -ERANGE; + else + memcpy(value, pacl, acllen); + } + rc = acllen; + kfree(pacl); + } + break; + } +#ifdef CONFIG_CIFS_ALLOW_INSECURE_LEGACY + case XATTR_ACL_ACCESS: +#ifdef CONFIG_CIFS_POSIX + if (sb->s_flags & SB_POSIXACL) + rc = CIFSSMBGetPosixACL(xid, pTcon, full_path, + value, size, ACL_TYPE_ACCESS, + cifs_sb->local_nls, + cifs_remap(cifs_sb)); +#endif /* CONFIG_CIFS_POSIX */ + break; + + case XATTR_ACL_DEFAULT: +#ifdef CONFIG_CIFS_POSIX + if (sb->s_flags & SB_POSIXACL) + rc = CIFSSMBGetPosixACL(xid, pTcon, full_path, + value, size, ACL_TYPE_DEFAULT, + cifs_sb->local_nls, + cifs_remap(cifs_sb)); +#endif /* CONFIG_CIFS_POSIX */ + break; +#endif /* ifdef CONFIG_CIFS_ALLOW_INSECURE_LEGACY */ + } + + /* We could add an additional check for streams ie + if proc/fs/cifs/streamstoxattr is set then + search server for EAs or streams to + returns as xattrs */ + + if (rc == -EINVAL) + rc = -EOPNOTSUPP; + +out: + free_dentry_path(page); + free_xid(xid); + cifs_put_tlink(tlink); + return rc; +} + +ssize_t cifs_listxattr(struct dentry *direntry, char *data, size_t buf_size) +{ + ssize_t rc = -EOPNOTSUPP; + unsigned int xid; + struct cifs_sb_info *cifs_sb = CIFS_SB(direntry->d_sb); + struct tcon_link *tlink; + struct cifs_tcon *pTcon; + const char *full_path; + void *page; + + if (unlikely(cifs_forced_shutdown(cifs_sb))) + return -EIO; + + if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_NO_XATTR) + return -EOPNOTSUPP; + + tlink = cifs_sb_tlink(cifs_sb); + if (IS_ERR(tlink)) + return PTR_ERR(tlink); + pTcon = tlink_tcon(tlink); + + xid = get_xid(); + page = alloc_dentry_path(); + + full_path = build_path_from_dentry(direntry, page); + if (IS_ERR(full_path)) { + rc = PTR_ERR(full_path); + goto list_ea_exit; + } + /* return dos attributes as pseudo xattr */ + /* return alt name if available as pseudo attr */ + + /* if proc/fs/cifs/streamstoxattr is set then + search server for EAs or streams to + returns as xattrs */ + + if (pTcon->ses->server->ops->query_all_EAs) + rc = pTcon->ses->server->ops->query_all_EAs(xid, pTcon, + full_path, NULL, data, buf_size, cifs_sb); +list_ea_exit: + free_dentry_path(page); + free_xid(xid); + cifs_put_tlink(tlink); + return rc; +} + +static const struct xattr_handler cifs_user_xattr_handler = { + .prefix = XATTR_USER_PREFIX, + .flags = XATTR_USER, + .get = cifs_xattr_get, + .set = cifs_xattr_set, +}; + +/* os2.* attributes are treated like user.* attributes */ +static const struct xattr_handler cifs_os2_xattr_handler = { + .prefix = XATTR_OS2_PREFIX, + .flags = XATTR_USER, + .get = cifs_xattr_get, + .set = cifs_xattr_set, +}; + +static const struct xattr_handler cifs_cifs_acl_xattr_handler = { + .name = CIFS_XATTR_CIFS_ACL, + .flags = XATTR_CIFS_ACL, + .get = cifs_xattr_get, + .set = cifs_xattr_set, +}; + +/* + * Although this is just an alias for the above, need to move away from + * confusing users and using the 20 year old term 'cifs' when it is no + * longer secure and was replaced by SMB2/SMB3 a long time ago, and + * SMB3 and later are highly secure. + */ +static const struct xattr_handler smb3_acl_xattr_handler = { + .name = SMB3_XATTR_CIFS_ACL, + .flags = XATTR_CIFS_ACL, + .get = cifs_xattr_get, + .set = cifs_xattr_set, +}; + +static const struct xattr_handler cifs_cifs_ntsd_xattr_handler = { + .name = CIFS_XATTR_CIFS_NTSD, + .flags = XATTR_CIFS_NTSD, + .get = cifs_xattr_get, + .set = cifs_xattr_set, +}; + +/* + * Although this is just an alias for the above, need to move away from + * confusing users and using the 20 year old term 'cifs' when it is no + * longer secure and was replaced by SMB2/SMB3 a long time ago, and + * SMB3 and later are highly secure. + */ +static const struct xattr_handler smb3_ntsd_xattr_handler = { + .name = SMB3_XATTR_CIFS_NTSD, + .flags = XATTR_CIFS_NTSD, + .get = cifs_xattr_get, + .set = cifs_xattr_set, +}; + +static const struct xattr_handler cifs_cifs_ntsd_full_xattr_handler = { + .name = CIFS_XATTR_CIFS_NTSD_FULL, + .flags = XATTR_CIFS_NTSD_FULL, + .get = cifs_xattr_get, + .set = cifs_xattr_set, +}; + +/* + * Although this is just an alias for the above, need to move away from + * confusing users and using the 20 year old term 'cifs' when it is no + * longer secure and was replaced by SMB2/SMB3 a long time ago, and + * SMB3 and later are highly secure. + */ +static const struct xattr_handler smb3_ntsd_full_xattr_handler = { + .name = SMB3_XATTR_CIFS_NTSD_FULL, + .flags = XATTR_CIFS_NTSD_FULL, + .get = cifs_xattr_get, + .set = cifs_xattr_set, +}; + + +static const struct xattr_handler cifs_posix_acl_access_xattr_handler = { + .name = XATTR_NAME_POSIX_ACL_ACCESS, + .flags = XATTR_ACL_ACCESS, + .get = cifs_xattr_get, + .set = cifs_xattr_set, +}; + +static const struct xattr_handler cifs_posix_acl_default_xattr_handler = { + .name = XATTR_NAME_POSIX_ACL_DEFAULT, + .flags = XATTR_ACL_DEFAULT, + .get = cifs_xattr_get, + .set = cifs_xattr_set, +}; + +const struct xattr_handler *cifs_xattr_handlers[] = { + &cifs_user_xattr_handler, + &cifs_os2_xattr_handler, + &cifs_cifs_acl_xattr_handler, + &smb3_acl_xattr_handler, /* alias for above since avoiding "cifs" */ + &cifs_cifs_ntsd_xattr_handler, + &smb3_ntsd_xattr_handler, /* alias for above since avoiding "cifs" */ + &cifs_cifs_ntsd_full_xattr_handler, + &smb3_ntsd_full_xattr_handler, /* alias for above since avoiding "cifs" */ + &cifs_posix_acl_access_xattr_handler, + &cifs_posix_acl_default_xattr_handler, + NULL +}; diff --git a/fs/smb/common/Makefile b/fs/smb/common/Makefile new file mode 100644 index 0000000000000..c66dbbc1469c3 --- /dev/null +++ b/fs/smb/common/Makefile @@ -0,0 +1,7 @@ +# SPDX-License-Identifier: GPL-2.0-only +# +# Makefile for Linux filesystem routines that are shared by client and server. +# + +obj-$(CONFIG_SMBFS) += cifs_arc4.o +obj-$(CONFIG_SMBFS) += cifs_md4.o diff --git a/fs/smb/common/arc4.h b/fs/smb/common/arc4.h new file mode 100644 index 0000000000000..12e71ec033a18 --- /dev/null +++ b/fs/smb/common/arc4.h @@ -0,0 +1,23 @@ +/* SPDX-License-Identifier: GPL-2.0+ */ +/* + * Common values for ARC4 Cipher Algorithm + */ + +#ifndef _CRYPTO_ARC4_H +#define _CRYPTO_ARC4_H + +#include + +#define ARC4_MIN_KEY_SIZE 1 +#define ARC4_MAX_KEY_SIZE 256 +#define ARC4_BLOCK_SIZE 1 + +struct arc4_ctx { + u32 S[256]; + u32 x, y; +}; + +int cifs_arc4_setkey(struct arc4_ctx *ctx, const u8 *in_key, unsigned int key_len); +void cifs_arc4_crypt(struct arc4_ctx *ctx, u8 *out, const u8 *in, unsigned int len); + +#endif /* _CRYPTO_ARC4_H */ diff --git a/fs/smb/common/cifs_arc4.c b/fs/smb/common/cifs_arc4.c new file mode 100644 index 0000000000000..043e4cb839fa2 --- /dev/null +++ b/fs/smb/common/cifs_arc4.c @@ -0,0 +1,74 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Cryptographic API + * + * ARC4 Cipher Algorithm + * + * Jon Oberheide + */ + +#include +#include "arc4.h" + +MODULE_LICENSE("GPL"); + +int cifs_arc4_setkey(struct arc4_ctx *ctx, const u8 *in_key, unsigned int key_len) +{ + int i, j = 0, k = 0; + + ctx->x = 1; + ctx->y = 0; + + for (i = 0; i < 256; i++) + ctx->S[i] = i; + + for (i = 0; i < 256; i++) { + u32 a = ctx->S[i]; + + j = (j + in_key[k] + a) & 0xff; + ctx->S[i] = ctx->S[j]; + ctx->S[j] = a; + if (++k >= key_len) + k = 0; + } + + return 0; +} +EXPORT_SYMBOL_GPL(cifs_arc4_setkey); + +void cifs_arc4_crypt(struct arc4_ctx *ctx, u8 *out, const u8 *in, unsigned int len) +{ + u32 *const S = ctx->S; + u32 x, y, a, b; + u32 ty, ta, tb; + + if (len == 0) + return; + + x = ctx->x; + y = ctx->y; + + a = S[x]; + y = (y + a) & 0xff; + b = S[y]; + + do { + S[y] = a; + a = (a + b) & 0xff; + S[x] = b; + x = (x + 1) & 0xff; + ta = S[x]; + ty = (y + ta) & 0xff; + tb = S[ty]; + *out++ = *in++ ^ S[a]; + if (--len == 0) + break; + y = ty; + a = ta; + b = tb; + } while (true); + + ctx->x = x; + ctx->y = y; +} +EXPORT_SYMBOL_GPL(cifs_arc4_crypt); diff --git a/fs/smb/common/cifs_md4.c b/fs/smb/common/cifs_md4.c new file mode 100644 index 0000000000000..50f78cfc6ce92 --- /dev/null +++ b/fs/smb/common/cifs_md4.c @@ -0,0 +1,197 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Cryptographic API. + * + * MD4 Message Digest Algorithm (RFC1320). + * + * Implementation derived from Andrew Tridgell and Steve French's + * CIFS MD4 implementation, and the cryptoapi implementation + * originally based on the public domain implementation written + * by Colin Plumb in 1993. + * + * Copyright (c) Andrew Tridgell 1997-1998. + * Modified by Steve French (sfrench@us.ibm.com) 2002 + * Copyright (c) Cryptoapi developers. + * Copyright (c) 2002 David S. Miller (davem@redhat.com) + * Copyright (c) 2002 James Morris + * + */ +#include +#include +#include +#include +#include +#include +#include "md4.h" + +MODULE_LICENSE("GPL"); + +static inline u32 lshift(u32 x, unsigned int s) +{ + x &= 0xFFFFFFFF; + return ((x << s) & 0xFFFFFFFF) | (x >> (32 - s)); +} + +static inline u32 F(u32 x, u32 y, u32 z) +{ + return (x & y) | ((~x) & z); +} + +static inline u32 G(u32 x, u32 y, u32 z) +{ + return (x & y) | (x & z) | (y & z); +} + +static inline u32 H(u32 x, u32 y, u32 z) +{ + return x ^ y ^ z; +} + +#define ROUND1(a,b,c,d,k,s) (a = lshift(a + F(b,c,d) + k, s)) +#define ROUND2(a,b,c,d,k,s) (a = lshift(a + G(b,c,d) + k + (u32)0x5A827999,s)) +#define ROUND3(a,b,c,d,k,s) (a = lshift(a + H(b,c,d) + k + (u32)0x6ED9EBA1,s)) + +static void md4_transform(u32 *hash, u32 const *in) +{ + u32 a, b, c, d; + + a = hash[0]; + b = hash[1]; + c = hash[2]; + d = hash[3]; + + ROUND1(a, b, c, d, in[0], 3); + ROUND1(d, a, b, c, in[1], 7); + ROUND1(c, d, a, b, in[2], 11); + ROUND1(b, c, d, a, in[3], 19); + ROUND1(a, b, c, d, in[4], 3); + ROUND1(d, a, b, c, in[5], 7); + ROUND1(c, d, a, b, in[6], 11); + ROUND1(b, c, d, a, in[7], 19); + ROUND1(a, b, c, d, in[8], 3); + ROUND1(d, a, b, c, in[9], 7); + ROUND1(c, d, a, b, in[10], 11); + ROUND1(b, c, d, a, in[11], 19); + ROUND1(a, b, c, d, in[12], 3); + ROUND1(d, a, b, c, in[13], 7); + ROUND1(c, d, a, b, in[14], 11); + ROUND1(b, c, d, a, in[15], 19); + + ROUND2(a, b, c, d, in[0], 3); + ROUND2(d, a, b, c, in[4], 5); + ROUND2(c, d, a, b, in[8], 9); + ROUND2(b, c, d, a, in[12], 13); + ROUND2(a, b, c, d, in[1], 3); + ROUND2(d, a, b, c, in[5], 5); + ROUND2(c, d, a, b, in[9], 9); + ROUND2(b, c, d, a, in[13], 13); + ROUND2(a, b, c, d, in[2], 3); + ROUND2(d, a, b, c, in[6], 5); + ROUND2(c, d, a, b, in[10], 9); + ROUND2(b, c, d, a, in[14], 13); + ROUND2(a, b, c, d, in[3], 3); + ROUND2(d, a, b, c, in[7], 5); + ROUND2(c, d, a, b, in[11], 9); + ROUND2(b, c, d, a, in[15], 13); + + ROUND3(a, b, c, d, in[0], 3); + ROUND3(d, a, b, c, in[8], 9); + ROUND3(c, d, a, b, in[4], 11); + ROUND3(b, c, d, a, in[12], 15); + ROUND3(a, b, c, d, in[2], 3); + ROUND3(d, a, b, c, in[10], 9); + ROUND3(c, d, a, b, in[6], 11); + ROUND3(b, c, d, a, in[14], 15); + ROUND3(a, b, c, d, in[1], 3); + ROUND3(d, a, b, c, in[9], 9); + ROUND3(c, d, a, b, in[5], 11); + ROUND3(b, c, d, a, in[13], 15); + ROUND3(a, b, c, d, in[3], 3); + ROUND3(d, a, b, c, in[11], 9); + ROUND3(c, d, a, b, in[7], 11); + ROUND3(b, c, d, a, in[15], 15); + + hash[0] += a; + hash[1] += b; + hash[2] += c; + hash[3] += d; +} + +static inline void md4_transform_helper(struct md4_ctx *ctx) +{ + le32_to_cpu_array(ctx->block, ARRAY_SIZE(ctx->block)); + md4_transform(ctx->hash, ctx->block); +} + +int cifs_md4_init(struct md4_ctx *mctx) +{ + memset(mctx, 0, sizeof(struct md4_ctx)); + mctx->hash[0] = 0x67452301; + mctx->hash[1] = 0xefcdab89; + mctx->hash[2] = 0x98badcfe; + mctx->hash[3] = 0x10325476; + mctx->byte_count = 0; + + return 0; +} +EXPORT_SYMBOL_GPL(cifs_md4_init); + +int cifs_md4_update(struct md4_ctx *mctx, const u8 *data, unsigned int len) +{ + const u32 avail = sizeof(mctx->block) - (mctx->byte_count & 0x3f); + + mctx->byte_count += len; + + if (avail > len) { + memcpy((char *)mctx->block + (sizeof(mctx->block) - avail), + data, len); + return 0; + } + + memcpy((char *)mctx->block + (sizeof(mctx->block) - avail), + data, avail); + + md4_transform_helper(mctx); + data += avail; + len -= avail; + + while (len >= sizeof(mctx->block)) { + memcpy(mctx->block, data, sizeof(mctx->block)); + md4_transform_helper(mctx); + data += sizeof(mctx->block); + len -= sizeof(mctx->block); + } + + memcpy(mctx->block, data, len); + + return 0; +} +EXPORT_SYMBOL_GPL(cifs_md4_update); + +int cifs_md4_final(struct md4_ctx *mctx, u8 *out) +{ + const unsigned int offset = mctx->byte_count & 0x3f; + char *p = (char *)mctx->block + offset; + int padding = 56 - (offset + 1); + + *p++ = 0x80; + if (padding < 0) { + memset(p, 0x00, padding + sizeof(u64)); + md4_transform_helper(mctx); + p = (char *)mctx->block; + padding = 56; + } + + memset(p, 0, padding); + mctx->block[14] = mctx->byte_count << 3; + mctx->block[15] = mctx->byte_count >> 29; + le32_to_cpu_array(mctx->block, (sizeof(mctx->block) - + sizeof(u64)) / sizeof(u32)); + md4_transform(mctx->hash, mctx->block); + cpu_to_le32_array(mctx->hash, ARRAY_SIZE(mctx->hash)); + memcpy(out, mctx->hash, sizeof(mctx->hash)); + memset(mctx, 0, sizeof(*mctx)); + + return 0; +} +EXPORT_SYMBOL_GPL(cifs_md4_final); diff --git a/fs/smb/common/md4.h b/fs/smb/common/md4.h new file mode 100644 index 0000000000000..5337becc699ab --- /dev/null +++ b/fs/smb/common/md4.h @@ -0,0 +1,27 @@ +/* SPDX-License-Identifier: GPL-2.0+ */ +/* + * Common values for ARC4 Cipher Algorithm + */ + +#ifndef _CIFS_MD4_H +#define _CIFS_MD4_H + +#include + +#define MD4_DIGEST_SIZE 16 +#define MD4_HMAC_BLOCK_SIZE 64 +#define MD4_BLOCK_WORDS 16 +#define MD4_HASH_WORDS 4 + +struct md4_ctx { + u32 hash[MD4_HASH_WORDS]; + u32 block[MD4_BLOCK_WORDS]; + u64 byte_count; +}; + + +int cifs_md4_init(struct md4_ctx *mctx); +int cifs_md4_update(struct md4_ctx *mctx, const u8 *data, unsigned int len); +int cifs_md4_final(struct md4_ctx *mctx, u8 *out); + +#endif /* _CIFS_MD4_H */ diff --git a/fs/smb/common/smb2pdu.h b/fs/smb/common/smb2pdu.h new file mode 100644 index 0000000000000..7d605db3bb3b9 --- /dev/null +++ b/fs/smb/common/smb2pdu.h @@ -0,0 +1,1702 @@ +/* SPDX-License-Identifier: LGPL-2.1 */ +#ifndef _COMMON_SMB2PDU_H +#define _COMMON_SMB2PDU_H + +/* + * Note that, due to trying to use names similar to the protocol specifications, + * there are many mixed case field names in the structures below. Although + * this does not match typical Linux kernel style, it is necessary to be + * able to match against the protocol specfication. + * + * SMB2 commands + * Some commands have minimal (wct=0,bcc=0), or uninteresting, responses + * (ie no useful data other than the SMB error code itself) and are marked such. + * Knowing this helps avoid response buffer allocations and copy in some cases. + */ + +/* List of commands in host endian */ +#define SMB2_NEGOTIATE_HE 0x0000 +#define SMB2_SESSION_SETUP_HE 0x0001 +#define SMB2_LOGOFF_HE 0x0002 /* trivial request/resp */ +#define SMB2_TREE_CONNECT_HE 0x0003 +#define SMB2_TREE_DISCONNECT_HE 0x0004 /* trivial req/resp */ +#define SMB2_CREATE_HE 0x0005 +#define SMB2_CLOSE_HE 0x0006 +#define SMB2_FLUSH_HE 0x0007 /* trivial resp */ +#define SMB2_READ_HE 0x0008 +#define SMB2_WRITE_HE 0x0009 +#define SMB2_LOCK_HE 0x000A +#define SMB2_IOCTL_HE 0x000B +#define SMB2_CANCEL_HE 0x000C +#define SMB2_ECHO_HE 0x000D +#define SMB2_QUERY_DIRECTORY_HE 0x000E +#define SMB2_CHANGE_NOTIFY_HE 0x000F +#define SMB2_QUERY_INFO_HE 0x0010 +#define SMB2_SET_INFO_HE 0x0011 +#define SMB2_OPLOCK_BREAK_HE 0x0012 + +/* The same list in little endian */ +#define SMB2_NEGOTIATE cpu_to_le16(SMB2_NEGOTIATE_HE) +#define SMB2_SESSION_SETUP cpu_to_le16(SMB2_SESSION_SETUP_HE) +#define SMB2_LOGOFF cpu_to_le16(SMB2_LOGOFF_HE) +#define SMB2_TREE_CONNECT cpu_to_le16(SMB2_TREE_CONNECT_HE) +#define SMB2_TREE_DISCONNECT cpu_to_le16(SMB2_TREE_DISCONNECT_HE) +#define SMB2_CREATE cpu_to_le16(SMB2_CREATE_HE) +#define SMB2_CLOSE cpu_to_le16(SMB2_CLOSE_HE) +#define SMB2_FLUSH cpu_to_le16(SMB2_FLUSH_HE) +#define SMB2_READ cpu_to_le16(SMB2_READ_HE) +#define SMB2_WRITE cpu_to_le16(SMB2_WRITE_HE) +#define SMB2_LOCK cpu_to_le16(SMB2_LOCK_HE) +#define SMB2_IOCTL cpu_to_le16(SMB2_IOCTL_HE) +#define SMB2_CANCEL cpu_to_le16(SMB2_CANCEL_HE) +#define SMB2_ECHO cpu_to_le16(SMB2_ECHO_HE) +#define SMB2_QUERY_DIRECTORY cpu_to_le16(SMB2_QUERY_DIRECTORY_HE) +#define SMB2_CHANGE_NOTIFY cpu_to_le16(SMB2_CHANGE_NOTIFY_HE) +#define SMB2_QUERY_INFO cpu_to_le16(SMB2_QUERY_INFO_HE) +#define SMB2_SET_INFO cpu_to_le16(SMB2_SET_INFO_HE) +#define SMB2_OPLOCK_BREAK cpu_to_le16(SMB2_OPLOCK_BREAK_HE) + +#define SMB2_INTERNAL_CMD cpu_to_le16(0xFFFF) + +#define NUMBER_OF_SMB2_COMMANDS 0x0013 + +/* + * Size of the session key (crypto key encrypted with the password + */ +#define SMB2_NTLMV2_SESSKEY_SIZE 16 +#define SMB2_SIGNATURE_SIZE 16 +#define SMB2_HMACSHA256_SIZE 32 +#define SMB2_CMACAES_SIZE 16 +#define SMB3_GCM128_CRYPTKEY_SIZE 16 +#define SMB3_GCM256_CRYPTKEY_SIZE 32 + +/* + * Size of the smb3 encryption/decryption keys + * This size is big enough to store any cipher key types. + */ +#define SMB3_ENC_DEC_KEY_SIZE 32 + +/* + * Size of the smb3 signing key + */ +#define SMB3_SIGN_KEY_SIZE 16 + +#define CIFS_CLIENT_CHALLENGE_SIZE 8 + +/* Maximum buffer size value we can send with 1 credit */ +#define SMB2_MAX_BUFFER_SIZE 65536 + +/* + * The default wsize is 1M for SMB2 (and for some CIFS cases). + * find_get_pages seems to return a maximum of 256 + * pages in a single call. With PAGE_SIZE == 4k, this means we can + * fill a single wsize request with a single call. + */ +#define SMB3_DEFAULT_IOSIZE (4 * 1024 * 1024) + +/* + * SMB2 Header Definition + * + * "MBZ" : Must be Zero + * "BB" : BugBug, Something to check/review/analyze later + * "PDU" : "Protocol Data Unit" (ie a network "frame") + * + */ + +#define __SMB2_HEADER_STRUCTURE_SIZE 64 +#define SMB2_HEADER_STRUCTURE_SIZE \ + cpu_to_le16(__SMB2_HEADER_STRUCTURE_SIZE) + +#define SMB2_PROTO_NUMBER cpu_to_le32(0x424d53fe) +#define SMB2_TRANSFORM_PROTO_NUM cpu_to_le32(0x424d53fd) +#define SMB2_COMPRESSION_TRANSFORM_ID cpu_to_le32(0x424d53fc) + +/* + * SMB2 flag definitions + */ +#define SMB2_FLAGS_SERVER_TO_REDIR cpu_to_le32(0x00000001) +#define SMB2_FLAGS_ASYNC_COMMAND cpu_to_le32(0x00000002) +#define SMB2_FLAGS_RELATED_OPERATIONS cpu_to_le32(0x00000004) +#define SMB2_FLAGS_SIGNED cpu_to_le32(0x00000008) +#define SMB2_FLAGS_PRIORITY_MASK cpu_to_le32(0x00000070) /* SMB3.1.1 */ +#define SMB2_FLAGS_DFS_OPERATIONS cpu_to_le32(0x10000000) +#define SMB2_FLAGS_REPLAY_OPERATION cpu_to_le32(0x20000000) /* SMB3 & up */ + +/* + * Definitions for SMB2 Protocol Data Units (network frames) + * + * See MS-SMB2.PDF specification for protocol details. + * The Naming convention is the lower case version of the SMB2 + * command code name for the struct. Note that structures must be packed. + * + */ + +/* See MS-SMB2 section 2.2.1 */ +struct smb2_hdr { + __le32 ProtocolId; /* 0xFE 'S' 'M' 'B' */ + __le16 StructureSize; /* 64 */ + __le16 CreditCharge; /* MBZ */ + __le32 Status; /* Error from server */ + __le16 Command; + __le16 CreditRequest; /* CreditResponse */ + __le32 Flags; + __le32 NextCommand; + __le64 MessageId; + union { + struct { + __le32 ProcessId; + __le32 TreeId; + } __packed SyncId; + __le64 AsyncId; + } __packed Id; + __le64 SessionId; + __u8 Signature[16]; +} __packed; + +struct smb2_pdu { + struct smb2_hdr hdr; + __le16 StructureSize2; /* size of wct area (varies, request specific) */ +} __packed; + +#define SMB2_ERROR_STRUCTURE_SIZE2 9 +#define SMB2_ERROR_STRUCTURE_SIZE2_LE cpu_to_le16(SMB2_ERROR_STRUCTURE_SIZE2) + +struct smb2_err_rsp { + struct smb2_hdr hdr; + __le16 StructureSize; + __u8 ErrorContextCount; + __u8 Reserved; + __le32 ByteCount; /* even if zero, at least one byte follows */ + __u8 ErrorData[1]; /* variable length */ +} __packed; + +#define SMB3_AES_CCM_NONCE 11 +#define SMB3_AES_GCM_NONCE 12 + +/* Transform flags (for 3.0 dialect this flag indicates CCM */ +#define TRANSFORM_FLAG_ENCRYPTED 0x0001 +struct smb2_transform_hdr { + __le32 ProtocolId; /* 0xFD 'S' 'M' 'B' */ + __u8 Signature[16]; + __u8 Nonce[16]; + __le32 OriginalMessageSize; + __u16 Reserved1; + __le16 Flags; /* EncryptionAlgorithm for 3.0, enc enabled for 3.1.1 */ + __le64 SessionId; +} __packed; + + +/* See MS-SMB2 2.2.42 */ +struct smb2_compression_transform_hdr_unchained { + __le32 ProtocolId; /* 0xFC 'S' 'M' 'B' */ + __le32 OriginalCompressedSegmentSize; + __le16 CompressionAlgorithm; + __le16 Flags; + __le16 Length; /* if chained it is length, else offset */ +} __packed; + +/* See MS-SMB2 2.2.42.1 */ +#define SMB2_COMPRESSION_FLAG_NONE 0x0000 +#define SMB2_COMPRESSION_FLAG_CHAINED 0x0001 + +struct compression_payload_header { + __le16 CompressionAlgorithm; + __le16 Flags; + __le32 Length; /* length of compressed playload including field below if present */ + /* __le32 OriginalPayloadSize; */ /* optional, present when LZNT1, LZ77, LZ77+Huffman */ +} __packed; + +/* See MS-SMB2 2.2.42.2 */ +struct smb2_compression_transform_hdr_chained { + __le32 ProtocolId; /* 0xFC 'S' 'M' 'B' */ + __le32 OriginalCompressedSegmentSize; + /* struct compression_payload_header[] */ +} __packed; + +/* See MS-SMB2 2.2.42.2.2 */ +struct compression_pattern_payload_v1 { + __le16 Pattern; + __le16 Reserved1; + __le16 Reserved2; + __le32 Repetitions; +} __packed; + +/* See MS-SMB2 section 2.2.9.2 */ +/* Context Types */ +#define SMB2_RESERVED_TREE_CONNECT_CONTEXT_ID 0x0000 +#define SMB2_REMOTED_IDENTITY_TREE_CONNECT_CONTEXT_ID cpu_to_le16(0x0001) + +struct tree_connect_contexts { + __le16 ContextType; + __le16 DataLength; + __le32 Reserved; + __u8 Data[]; +} __packed; + +/* Remoted identity tree connect context structures - see MS-SMB2 2.2.9.2.1 */ +struct smb3_blob_data { + __le16 BlobSize; + __u8 BlobData[]; +} __packed; + +/* Valid values for Attr */ +#define SE_GROUP_MANDATORY 0x00000001 +#define SE_GROUP_ENABLED_BY_DEFAULT 0x00000002 +#define SE_GROUP_ENABLED 0x00000004 +#define SE_GROUP_OWNER 0x00000008 +#define SE_GROUP_USE_FOR_DENY_ONLY 0x00000010 +#define SE_GROUP_INTEGRITY 0x00000020 +#define SE_GROUP_INTEGRITY_ENABLED 0x00000040 +#define SE_GROUP_RESOURCE 0x20000000 +#define SE_GROUP_LOGON_ID 0xC0000000 + +/* struct sid_attr_data is SidData array in BlobData format then le32 Attr */ + +struct sid_array_data { + __le16 SidAttrCount; + /* SidAttrList - array of sid_attr_data structs */ +} __packed; + +struct luid_attr_data { + +} __packed; + +/* + * struct privilege_data is the same as BLOB_DATA - see MS-SMB2 2.2.9.2.1.5 + * but with size of LUID_ATTR_DATA struct and BlobData set to LUID_ATTR DATA + */ + +struct privilege_array_data { + __le16 PrivilegeCount; + /* array of privilege_data structs */ +} __packed; + +struct remoted_identity_tcon_context { + __le16 TicketType; /* must be 0x0001 */ + __le16 TicketSize; /* total size of this struct */ + __le16 User; /* offset to SID_ATTR_DATA struct with user info */ + __le16 UserName; /* offset to null terminated Unicode username string */ + __le16 Domain; /* offset to null terminated Unicode domain name */ + __le16 Groups; /* offset to SID_ARRAY_DATA struct with group info */ + __le16 RestrictedGroups; /* similar to above */ + __le16 Privileges; /* offset to PRIVILEGE_ARRAY_DATA struct */ + __le16 PrimaryGroup; /* offset to SID_ARRAY_DATA struct */ + __le16 Owner; /* offset to BLOB_DATA struct */ + __le16 DefaultDacl; /* offset to BLOB_DATA struct */ + __le16 DeviceGroups; /* offset to SID_ARRAY_DATA struct */ + __le16 UserClaims; /* offset to BLOB_DATA struct */ + __le16 DeviceClaims; /* offset to BLOB_DATA struct */ + __u8 TicketInfo[]; /* variable length buf - remoted identity data */ +} __packed; + +struct smb2_tree_connect_req_extension { + __le32 TreeConnectContextOffset; + __le16 TreeConnectContextCount; + __u8 Reserved[10]; + __u8 PathName[]; /* variable sized array */ + /* followed by array of TreeConnectContexts */ +} __packed; + +/* Flags/Reserved for SMB3.1.1 */ +#define SMB2_TREE_CONNECT_FLAG_CLUSTER_RECONNECT cpu_to_le16(0x0001) +#define SMB2_TREE_CONNECT_FLAG_REDIRECT_TO_OWNER cpu_to_le16(0x0002) +#define SMB2_TREE_CONNECT_FLAG_EXTENSION_PRESENT cpu_to_le16(0x0004) + +struct smb2_tree_connect_req { + struct smb2_hdr hdr; + __le16 StructureSize; /* Must be 9 */ + __le16 Flags; /* Flags in SMB3.1.1 */ + __le16 PathOffset; + __le16 PathLength; + __u8 Buffer[1]; /* variable length */ +} __packed; + +/* Possible ShareType values */ +#define SMB2_SHARE_TYPE_DISK 0x01 +#define SMB2_SHARE_TYPE_PIPE 0x02 +#define SMB2_SHARE_TYPE_PRINT 0x03 + +/* + * Possible ShareFlags - exactly one and only one of the first 4 caching flags + * must be set (any of the remaining, SHI1005, flags may be set individually + * or in combination. + */ +#define SMB2_SHAREFLAG_MANUAL_CACHING 0x00000000 +#define SMB2_SHAREFLAG_AUTO_CACHING 0x00000010 +#define SMB2_SHAREFLAG_VDO_CACHING 0x00000020 +#define SMB2_SHAREFLAG_NO_CACHING 0x00000030 +#define SHI1005_FLAGS_DFS 0x00000001 +#define SHI1005_FLAGS_DFS_ROOT 0x00000002 +#define SHI1005_FLAGS_RESTRICT_EXCLUSIVE_OPENS 0x00000100 +#define SHI1005_FLAGS_FORCE_SHARED_DELETE 0x00000200 +#define SHI1005_FLAGS_ALLOW_NAMESPACE_CACHING 0x00000400 +#define SHI1005_FLAGS_ACCESS_BASED_DIRECTORY_ENUM 0x00000800 +#define SHI1005_FLAGS_FORCE_LEVELII_OPLOCK 0x00001000 +#define SHI1005_FLAGS_ENABLE_HASH_V1 0x00002000 +#define SHI1005_FLAGS_ENABLE_HASH_V2 0x00004000 +#define SHI1005_FLAGS_ENCRYPT_DATA 0x00008000 +#define SMB2_SHAREFLAG_IDENTITY_REMOTING 0x00040000 /* 3.1.1 */ +#define SMB2_SHAREFLAG_COMPRESS_DATA 0x00100000 /* 3.1.1 */ +#define SHI1005_FLAGS_ALL 0x0014FF33 + +/* Possible share capabilities */ +#define SMB2_SHARE_CAP_DFS cpu_to_le32(0x00000008) /* all dialects */ +#define SMB2_SHARE_CAP_CONTINUOUS_AVAILABILITY cpu_to_le32(0x00000010) /* 3.0 */ +#define SMB2_SHARE_CAP_SCALEOUT cpu_to_le32(0x00000020) /* 3.0 */ +#define SMB2_SHARE_CAP_CLUSTER cpu_to_le32(0x00000040) /* 3.0 */ +#define SMB2_SHARE_CAP_ASYMMETRIC cpu_to_le32(0x00000080) /* 3.02 */ +#define SMB2_SHARE_CAP_REDIRECT_TO_OWNER cpu_to_le32(0x00000100) /* 3.1.1 */ + +struct smb2_tree_connect_rsp { + struct smb2_hdr hdr; + __le16 StructureSize; /* Must be 16 */ + __u8 ShareType; /* see below */ + __u8 Reserved; + __le32 ShareFlags; /* see below */ + __le32 Capabilities; /* see below */ + __le32 MaximalAccess; +} __packed; + +struct smb2_tree_disconnect_req { + struct smb2_hdr hdr; + __le16 StructureSize; /* Must be 4 */ + __le16 Reserved; +} __packed; + +struct smb2_tree_disconnect_rsp { + struct smb2_hdr hdr; + __le16 StructureSize; /* Must be 4 */ + __le16 Reserved; +} __packed; + + +/* + * SMB2_NEGOTIATE_PROTOCOL See MS-SMB2 section 2.2.3 + */ +/* SecurityMode flags */ +#define SMB2_NEGOTIATE_SIGNING_ENABLED 0x0001 +#define SMB2_NEGOTIATE_SIGNING_ENABLED_LE cpu_to_le16(0x0001) +#define SMB2_NEGOTIATE_SIGNING_REQUIRED 0x0002 +#define SMB2_NEGOTIATE_SIGNING_REQUIRED_LE cpu_to_le16(0x0002) +#define SMB2_SEC_MODE_FLAGS_ALL 0x0003 + +/* Capabilities flags */ +#define SMB2_GLOBAL_CAP_DFS 0x00000001 +#define SMB2_GLOBAL_CAP_LEASING 0x00000002 /* Resp only New to SMB2.1 */ +#define SMB2_GLOBAL_CAP_LARGE_MTU 0X00000004 /* Resp only New to SMB2.1 */ +#define SMB2_GLOBAL_CAP_MULTI_CHANNEL 0x00000008 /* New to SMB3 */ +#define SMB2_GLOBAL_CAP_PERSISTENT_HANDLES 0x00000010 /* New to SMB3 */ +#define SMB2_GLOBAL_CAP_DIRECTORY_LEASING 0x00000020 /* New to SMB3 */ +#define SMB2_GLOBAL_CAP_ENCRYPTION 0x00000040 /* New to SMB3 */ +/* Internal types */ +#define SMB2_NT_FIND 0x00100000 +#define SMB2_LARGE_FILES 0x00200000 + +#define SMB2_CLIENT_GUID_SIZE 16 +#define SMB2_CREATE_GUID_SIZE 16 + +/* Dialects */ +#define SMB10_PROT_ID 0x0000 /* local only, not sent on wire w/CIFS negprot */ +#define SMB20_PROT_ID 0x0202 +#define SMB21_PROT_ID 0x0210 +#define SMB2X_PROT_ID 0x02FF +#define SMB30_PROT_ID 0x0300 +#define SMB302_PROT_ID 0x0302 +#define SMB311_PROT_ID 0x0311 +#define BAD_PROT_ID 0xFFFF + +#define SMB311_SALT_SIZE 32 +/* Hash Algorithm Types */ +#define SMB2_PREAUTH_INTEGRITY_SHA512 cpu_to_le16(0x0001) +#define SMB2_PREAUTH_HASH_SIZE 64 + +/* Negotiate Contexts - ContextTypes. See MS-SMB2 section 2.2.3.1 for details */ +#define SMB2_PREAUTH_INTEGRITY_CAPABILITIES cpu_to_le16(1) +#define SMB2_ENCRYPTION_CAPABILITIES cpu_to_le16(2) +#define SMB2_COMPRESSION_CAPABILITIES cpu_to_le16(3) +#define SMB2_NETNAME_NEGOTIATE_CONTEXT_ID cpu_to_le16(5) +#define SMB2_TRANSPORT_CAPABILITIES cpu_to_le16(6) +#define SMB2_RDMA_TRANSFORM_CAPABILITIES cpu_to_le16(7) +#define SMB2_SIGNING_CAPABILITIES cpu_to_le16(8) +#define SMB2_POSIX_EXTENSIONS_AVAILABLE cpu_to_le16(0x100) + +struct smb2_neg_context { + __le16 ContextType; + __le16 DataLength; + __le32 Reserved; + /* Followed by array of data. NOTE: some servers require padding to 8 byte boundary */ +} __packed; + +/* + * SaltLength that the server send can be zero, so the only three required + * fields (all __le16) end up six bytes total, so the minimum context data len + * in the response is six bytes which accounts for + * + * HashAlgorithmCount, SaltLength, and 1 HashAlgorithm. + */ +#define MIN_PREAUTH_CTXT_DATA_LEN 6 + +struct smb2_preauth_neg_context { + __le16 ContextType; /* 1 */ + __le16 DataLength; + __le32 Reserved; + __le16 HashAlgorithmCount; /* 1 */ + __le16 SaltLength; + __le16 HashAlgorithms; /* HashAlgorithms[0] since only one defined */ + __u8 Salt[SMB311_SALT_SIZE]; +} __packed; + +/* Encryption Algorithms Ciphers */ +#define SMB2_ENCRYPTION_AES128_CCM cpu_to_le16(0x0001) +#define SMB2_ENCRYPTION_AES128_GCM cpu_to_le16(0x0002) +#define SMB2_ENCRYPTION_AES256_CCM cpu_to_le16(0x0003) +#define SMB2_ENCRYPTION_AES256_GCM cpu_to_le16(0x0004) + +/* Min encrypt context data is one cipher so 2 bytes + 2 byte count field */ +#define MIN_ENCRYPT_CTXT_DATA_LEN 4 +struct smb2_encryption_neg_context { + __le16 ContextType; /* 2 */ + __le16 DataLength; + __le32 Reserved; + /* CipherCount usally 2, but can be 3 when AES256-GCM enabled */ + __le16 CipherCount; /* AES128-GCM and AES128-CCM by default */ + __le16 Ciphers[]; +} __packed; + +/* See MS-SMB2 2.2.3.1.3 */ +#define SMB3_COMPRESS_NONE cpu_to_le16(0x0000) +#define SMB3_COMPRESS_LZNT1 cpu_to_le16(0x0001) +#define SMB3_COMPRESS_LZ77 cpu_to_le16(0x0002) +#define SMB3_COMPRESS_LZ77_HUFF cpu_to_le16(0x0003) +/* Pattern scanning algorithm See MS-SMB2 3.1.4.4.1 */ +#define SMB3_COMPRESS_PATTERN cpu_to_le16(0x0004) /* Pattern_V1 */ + +/* Compression Flags */ +#define SMB2_COMPRESSION_CAPABILITIES_FLAG_NONE cpu_to_le32(0x00000000) +#define SMB2_COMPRESSION_CAPABILITIES_FLAG_CHAINED cpu_to_le32(0x00000001) + +struct smb2_compression_capabilities_context { + __le16 ContextType; /* 3 */ + __le16 DataLength; + __le32 Reserved; + __le16 CompressionAlgorithmCount; + __le16 Padding; + __le32 Flags; + __le16 CompressionAlgorithms[3]; + __u16 Pad; /* Some servers require pad to DataLen multiple of 8 */ + /* Check if pad needed */ +} __packed; + +/* + * For smb2_netname_negotiate_context_id See MS-SMB2 2.2.3.1.4. + * Its struct simply contains NetName, an array of Unicode characters + */ +struct smb2_netname_neg_context { + __le16 ContextType; /* 5 */ + __le16 DataLength; + __le32 Reserved; + __le16 NetName[]; /* hostname of target converted to UCS-2 */ +} __packed; + +/* + * For smb2_transport_capabilities context see MS-SMB2 2.2.3.1.5 + * and 2.2.4.1.5 + */ + +/* Flags */ +#define SMB2_ACCEPT_TRANSPORT_LEVEL_SECURITY 0x00000001 + +struct smb2_transport_capabilities_context { + __le16 ContextType; /* 6 */ + __le16 DataLength; + __u32 Reserved; + __le32 Flags; + __u32 Pad; +} __packed; + +/* + * For rdma transform capabilities context see MS-SMB2 2.2.3.1.6 + * and 2.2.4.1.6 + */ + +/* RDMA Transform IDs */ +#define SMB2_RDMA_TRANSFORM_NONE 0x0000 +#define SMB2_RDMA_TRANSFORM_ENCRYPTION 0x0001 +#define SMB2_RDMA_TRANSFORM_SIGNING 0x0002 + +struct smb2_rdma_transform_capabilities_context { + __le16 ContextType; /* 7 */ + __le16 DataLength; + __u32 Reserved; + __le16 TransformCount; + __u16 Reserved1; + __u32 Reserved2; + __le16 RDMATransformIds[]; +} __packed; + +/* + * For signing capabilities context see MS-SMB2 2.2.3.1.7 + * and 2.2.4.1.7 + */ + +/* Signing algorithms */ +#define SIGNING_ALG_HMAC_SHA256 0 +#define SIGNING_ALG_HMAC_SHA256_LE cpu_to_le16(0) +#define SIGNING_ALG_AES_CMAC 1 +#define SIGNING_ALG_AES_CMAC_LE cpu_to_le16(1) +#define SIGNING_ALG_AES_GMAC 2 +#define SIGNING_ALG_AES_GMAC_LE cpu_to_le16(2) + +struct smb2_signing_capabilities { + __le16 ContextType; /* 8 */ + __le16 DataLength; + __le32 Reserved; + __le16 SigningAlgorithmCount; + __le16 SigningAlgorithms[]; + /* Followed by padding to 8 byte boundary (required by some servers) */ +} __packed; + +#define POSIX_CTXT_DATA_LEN 16 +struct smb2_posix_neg_context { + __le16 ContextType; /* 0x100 */ + __le16 DataLength; + __le32 Reserved; + __u8 Name[16]; /* POSIX ctxt GUID 93AD25509CB411E7B42383DE968BCD7C */ +} __packed; + +struct smb2_negotiate_req { + struct smb2_hdr hdr; + __le16 StructureSize; /* Must be 36 */ + __le16 DialectCount; + __le16 SecurityMode; + __le16 Reserved; /* MBZ */ + __le32 Capabilities; + __u8 ClientGUID[SMB2_CLIENT_GUID_SIZE]; + /* In SMB3.02 and earlier next three were MBZ le64 ClientStartTime */ + __le32 NegotiateContextOffset; /* SMB3.1.1 only. MBZ earlier */ + __le16 NegotiateContextCount; /* SMB3.1.1 only. MBZ earlier */ + __le16 Reserved2; + __le16 Dialects[]; +} __packed; + +struct smb2_negotiate_rsp { + struct smb2_hdr hdr; + __le16 StructureSize; /* Must be 65 */ + __le16 SecurityMode; + __le16 DialectRevision; + __le16 NegotiateContextCount; /* Prior to SMB3.1.1 was Reserved & MBZ */ + __u8 ServerGUID[16]; + __le32 Capabilities; + __le32 MaxTransactSize; + __le32 MaxReadSize; + __le32 MaxWriteSize; + __le64 SystemTime; /* MBZ */ + __le64 ServerStartTime; + __le16 SecurityBufferOffset; + __le16 SecurityBufferLength; + __le32 NegotiateContextOffset; /* Pre:SMB3.1.1 was reserved/ignored */ + __u8 Buffer[1]; /* variable length GSS security buffer */ +} __packed; + + +/* + * SMB2_SESSION_SETUP See MS-SMB2 section 2.2.5 + */ +/* Flags */ +#define SMB2_SESSION_REQ_FLAG_BINDING 0x01 +#define SMB2_SESSION_REQ_FLAG_ENCRYPT_DATA 0x04 + +struct smb2_sess_setup_req { + struct smb2_hdr hdr; + __le16 StructureSize; /* Must be 25 */ + __u8 Flags; + __u8 SecurityMode; + __le32 Capabilities; + __le32 Channel; + __le16 SecurityBufferOffset; + __le16 SecurityBufferLength; + __le64 PreviousSessionId; + __u8 Buffer[1]; /* variable length GSS security buffer */ +} __packed; + +/* Currently defined SessionFlags */ +#define SMB2_SESSION_FLAG_IS_GUEST 0x0001 +#define SMB2_SESSION_FLAG_IS_GUEST_LE cpu_to_le16(0x0001) +#define SMB2_SESSION_FLAG_IS_NULL 0x0002 +#define SMB2_SESSION_FLAG_IS_NULL_LE cpu_to_le16(0x0002) +#define SMB2_SESSION_FLAG_ENCRYPT_DATA 0x0004 +#define SMB2_SESSION_FLAG_ENCRYPT_DATA_LE cpu_to_le16(0x0004) + +struct smb2_sess_setup_rsp { + struct smb2_hdr hdr; + __le16 StructureSize; /* Must be 9 */ + __le16 SessionFlags; + __le16 SecurityBufferOffset; + __le16 SecurityBufferLength; + __u8 Buffer[1]; /* variable length GSS security buffer */ +} __packed; + + +/* + * SMB2_LOGOFF See MS-SMB2 section 2.2.7 + */ +struct smb2_logoff_req { + struct smb2_hdr hdr; + __le16 StructureSize; /* Must be 4 */ + __le16 Reserved; +} __packed; + +struct smb2_logoff_rsp { + struct smb2_hdr hdr; + __le16 StructureSize; /* Must be 4 */ + __le16 Reserved; +} __packed; + + +/* + * SMB2_CLOSE See MS-SMB2 section 2.2.15 + */ +/* Currently defined values for close flags */ +#define SMB2_CLOSE_FLAG_POSTQUERY_ATTRIB cpu_to_le16(0x0001) +struct smb2_close_req { + struct smb2_hdr hdr; + __le16 StructureSize; /* Must be 24 */ + __le16 Flags; + __le32 Reserved; + __u64 PersistentFileId; /* opaque endianness */ + __u64 VolatileFileId; /* opaque endianness */ +} __packed; + +/* + * Maximum size of a SMB2_CLOSE response is 64 (smb2 header) + 60 (data) + */ +#define MAX_SMB2_CLOSE_RESPONSE_SIZE 124 + +struct smb2_close_rsp { + struct smb2_hdr hdr; + __le16 StructureSize; /* 60 */ + __le16 Flags; + __le32 Reserved; + __le64 CreationTime; + __le64 LastAccessTime; + __le64 LastWriteTime; + __le64 ChangeTime; + __le64 AllocationSize; /* Beginning of FILE_STANDARD_INFO equivalent */ + __le64 EndOfFile; + __le32 Attributes; +} __packed; + + +/* + * SMB2_READ See MS-SMB2 section 2.2.19 + */ +/* For read request Flags field below, following flag is defined for SMB3.02 */ +#define SMB2_READFLAG_READ_UNBUFFERED 0x01 +#define SMB2_READFLAG_REQUEST_COMPRESSED 0x02 /* See MS-SMB2 2.2.19 */ + +/* Channel field for read and write: exactly one of following flags can be set*/ +#define SMB2_CHANNEL_NONE cpu_to_le32(0x00000000) +#define SMB2_CHANNEL_RDMA_V1 cpu_to_le32(0x00000001) +#define SMB2_CHANNEL_RDMA_V1_INVALIDATE cpu_to_le32(0x00000002) +#define SMB2_CHANNEL_RDMA_TRANSFORM cpu_to_le32(0x00000003) + +/* SMB2 read request without RFC1001 length at the beginning */ +struct smb2_read_req { + struct smb2_hdr hdr; + __le16 StructureSize; /* Must be 49 */ + __u8 Padding; /* offset from start of SMB2 header to place read */ + __u8 Flags; /* MBZ unless SMB3.02 or later */ + __le32 Length; + __le64 Offset; + __u64 PersistentFileId; + __u64 VolatileFileId; + __le32 MinimumCount; + __le32 Channel; /* MBZ except for SMB3 or later */ + __le32 RemainingBytes; + __le16 ReadChannelInfoOffset; + __le16 ReadChannelInfoLength; + __u8 Buffer[1]; +} __packed; + +/* Read flags */ +#define SMB2_READFLAG_RESPONSE_NONE cpu_to_le32(0x00000000) +#define SMB2_READFLAG_RESPONSE_RDMA_TRANSFORM cpu_to_le32(0x00000001) + +struct smb2_read_rsp { + struct smb2_hdr hdr; + __le16 StructureSize; /* Must be 17 */ + __u8 DataOffset; + __u8 Reserved; + __le32 DataLength; + __le32 DataRemaining; + __le32 Flags; + __u8 Buffer[1]; +} __packed; + + +/* + * SMB2_WRITE See MS-SMB2 section 2.2.21 + */ +/* For write request Flags field below the following flags are defined: */ +#define SMB2_WRITEFLAG_WRITE_THROUGH 0x00000001 /* SMB2.1 or later */ +#define SMB2_WRITEFLAG_WRITE_UNBUFFERED 0x00000002 /* SMB3.02 or later */ + +struct smb2_write_req { + struct smb2_hdr hdr; + __le16 StructureSize; /* Must be 49 */ + __le16 DataOffset; /* offset from start of SMB2 header to write data */ + __le32 Length; + __le64 Offset; + __u64 PersistentFileId; /* opaque endianness */ + __u64 VolatileFileId; /* opaque endianness */ + __le32 Channel; /* MBZ unless SMB3.02 or later */ + __le32 RemainingBytes; + __le16 WriteChannelInfoOffset; + __le16 WriteChannelInfoLength; + __le32 Flags; + __u8 Buffer[1]; +} __packed; + +struct smb2_write_rsp { + struct smb2_hdr hdr; + __le16 StructureSize; /* Must be 17 */ + __u8 DataOffset; + __u8 Reserved; + __le32 DataLength; + __le32 DataRemaining; + __u32 Reserved2; + __u8 Buffer[1]; +} __packed; + + +/* + * SMB2_FLUSH See MS-SMB2 section 2.2.17 + */ +struct smb2_flush_req { + struct smb2_hdr hdr; + __le16 StructureSize; /* Must be 24 */ + __le16 Reserved1; + __le32 Reserved2; + __u64 PersistentFileId; + __u64 VolatileFileId; +} __packed; + +struct smb2_flush_rsp { + struct smb2_hdr hdr; + __le16 StructureSize; + __le16 Reserved; +} __packed; + +#define SMB2_LOCKFLAG_SHARED 0x0001 +#define SMB2_LOCKFLAG_EXCLUSIVE 0x0002 +#define SMB2_LOCKFLAG_UNLOCK 0x0004 +#define SMB2_LOCKFLAG_FAIL_IMMEDIATELY 0x0010 +#define SMB2_LOCKFLAG_MASK 0x0007 + +struct smb2_lock_element { + __le64 Offset; + __le64 Length; + __le32 Flags; + __le32 Reserved; +} __packed; + +struct smb2_lock_req { + struct smb2_hdr hdr; + __le16 StructureSize; /* Must be 48 */ + __le16 LockCount; + /* + * The least significant four bits are the index, the other 28 bits are + * the lock sequence number (0 to 64). See MS-SMB2 2.2.26 + */ + __le32 LockSequenceNumber; + __u64 PersistentFileId; + __u64 VolatileFileId; + /* Followed by at least one */ + struct smb2_lock_element locks[1]; +} __packed; + +struct smb2_lock_rsp { + struct smb2_hdr hdr; + __le16 StructureSize; /* Must be 4 */ + __le16 Reserved; +} __packed; + +struct smb2_echo_req { + struct smb2_hdr hdr; + __le16 StructureSize; /* Must be 4 */ + __u16 Reserved; +} __packed; + +struct smb2_echo_rsp { + struct smb2_hdr hdr; + __le16 StructureSize; /* Must be 4 */ + __u16 Reserved; +} __packed; + +/* + * Valid FileInformation classes for query directory + * + * Note that these are a subset of the (file) QUERY_INFO levels defined + * later in this file (but since QUERY_DIRECTORY uses equivalent numbers + * we do not redefine them here) + * + * FileDirectoryInfomation 0x01 + * FileFullDirectoryInformation 0x02 + * FileIdFullDirectoryInformation 0x26 + * FileBothDirectoryInformation 0x03 + * FileIdBothDirectoryInformation 0x25 + * FileNamesInformation 0x0C + * FileIdExtdDirectoryInformation 0x3C + */ + +/* search (query_directory) Flags field */ +#define SMB2_RESTART_SCANS 0x01 +#define SMB2_RETURN_SINGLE_ENTRY 0x02 +#define SMB2_INDEX_SPECIFIED 0x04 +#define SMB2_REOPEN 0x10 + +struct smb2_query_directory_req { + struct smb2_hdr hdr; + __le16 StructureSize; /* Must be 33 */ + __u8 FileInformationClass; + __u8 Flags; + __le32 FileIndex; + __u64 PersistentFileId; + __u64 VolatileFileId; + __le16 FileNameOffset; + __le16 FileNameLength; + __le32 OutputBufferLength; + __u8 Buffer[1]; +} __packed; + +struct smb2_query_directory_rsp { + struct smb2_hdr hdr; + __le16 StructureSize; /* Must be 9 */ + __le16 OutputBufferOffset; + __le32 OutputBufferLength; + __u8 Buffer[1]; +} __packed; + +/* + * Maximum number of iovs we need for a set-info request. + * The largest one is rename/hardlink + * [0] : struct smb2_set_info_req + smb2_file_[rename|link]_info + * [1] : path + * [2] : compound padding + */ +#define SMB2_SET_INFO_IOV_SIZE 3 + +struct smb2_set_info_req { + struct smb2_hdr hdr; + __le16 StructureSize; /* Must be 33 */ + __u8 InfoType; + __u8 FileInfoClass; + __le32 BufferLength; + __le16 BufferOffset; + __u16 Reserved; + __le32 AdditionalInformation; + __u64 PersistentFileId; + __u64 VolatileFileId; + __u8 Buffer[1]; +} __packed; + +struct smb2_set_info_rsp { + struct smb2_hdr hdr; + __le16 StructureSize; /* Must be 2 */ +} __packed; + +/* + * SMB2_NOTIFY See MS-SMB2 section 2.2.35 + */ +/* notify flags */ +#define SMB2_WATCH_TREE 0x0001 + +/* notify completion filter flags. See MS-FSCC 2.6 and MS-SMB2 2.2.35 */ +#define FILE_NOTIFY_CHANGE_FILE_NAME 0x00000001 +#define FILE_NOTIFY_CHANGE_DIR_NAME 0x00000002 +#define FILE_NOTIFY_CHANGE_ATTRIBUTES 0x00000004 +#define FILE_NOTIFY_CHANGE_SIZE 0x00000008 +#define FILE_NOTIFY_CHANGE_LAST_WRITE 0x00000010 +#define FILE_NOTIFY_CHANGE_LAST_ACCESS 0x00000020 +#define FILE_NOTIFY_CHANGE_CREATION 0x00000040 +#define FILE_NOTIFY_CHANGE_EA 0x00000080 +#define FILE_NOTIFY_CHANGE_SECURITY 0x00000100 +#define FILE_NOTIFY_CHANGE_STREAM_NAME 0x00000200 +#define FILE_NOTIFY_CHANGE_STREAM_SIZE 0x00000400 +#define FILE_NOTIFY_CHANGE_STREAM_WRITE 0x00000800 + +/* SMB2 Notify Action Flags */ +#define FILE_ACTION_ADDED 0x00000001 +#define FILE_ACTION_REMOVED 0x00000002 +#define FILE_ACTION_MODIFIED 0x00000003 +#define FILE_ACTION_RENAMED_OLD_NAME 0x00000004 +#define FILE_ACTION_RENAMED_NEW_NAME 0x00000005 +#define FILE_ACTION_ADDED_STREAM 0x00000006 +#define FILE_ACTION_REMOVED_STREAM 0x00000007 +#define FILE_ACTION_MODIFIED_STREAM 0x00000008 +#define FILE_ACTION_REMOVED_BY_DELETE 0x00000009 + +struct smb2_change_notify_req { + struct smb2_hdr hdr; + __le16 StructureSize; + __le16 Flags; + __le32 OutputBufferLength; + __u64 PersistentFileId; /* opaque endianness */ + __u64 VolatileFileId; /* opaque endianness */ + __le32 CompletionFilter; + __u32 Reserved; +} __packed; + +struct smb2_change_notify_rsp { + struct smb2_hdr hdr; + __le16 StructureSize; /* Must be 9 */ + __le16 OutputBufferOffset; + __le32 OutputBufferLength; + __u8 Buffer[1]; /* array of file notify structs */ +} __packed; + + +/* + * SMB2_CREATE See MS-SMB2 section 2.2.13 + */ +/* Oplock levels */ +#define SMB2_OPLOCK_LEVEL_NONE 0x00 +#define SMB2_OPLOCK_LEVEL_II 0x01 +#define SMB2_OPLOCK_LEVEL_EXCLUSIVE 0x08 +#define SMB2_OPLOCK_LEVEL_BATCH 0x09 +#define SMB2_OPLOCK_LEVEL_LEASE 0xFF +/* Non-spec internal type */ +#define SMB2_OPLOCK_LEVEL_NOCHANGE 0x99 + +/* Impersonation Levels. See MS-WPO section 9.7 and MSDN-IMPERS */ +#define IL_ANONYMOUS cpu_to_le32(0x00000000) +#define IL_IDENTIFICATION cpu_to_le32(0x00000001) +#define IL_IMPERSONATION cpu_to_le32(0x00000002) +#define IL_DELEGATE cpu_to_le32(0x00000003) + +/* File Attrubutes */ +#define FILE_ATTRIBUTE_READONLY 0x00000001 +#define FILE_ATTRIBUTE_HIDDEN 0x00000002 +#define FILE_ATTRIBUTE_SYSTEM 0x00000004 +#define FILE_ATTRIBUTE_DIRECTORY 0x00000010 +#define FILE_ATTRIBUTE_ARCHIVE 0x00000020 +#define FILE_ATTRIBUTE_NORMAL 0x00000080 +#define FILE_ATTRIBUTE_TEMPORARY 0x00000100 +#define FILE_ATTRIBUTE_SPARSE_FILE 0x00000200 +#define FILE_ATTRIBUTE_REPARSE_POINT 0x00000400 +#define FILE_ATTRIBUTE_COMPRESSED 0x00000800 +#define FILE_ATTRIBUTE_OFFLINE 0x00001000 +#define FILE_ATTRIBUTE_NOT_CONTENT_INDEXED 0x00002000 +#define FILE_ATTRIBUTE_ENCRYPTED 0x00004000 +#define FILE_ATTRIBUTE_INTEGRITY_STREAM 0x00008000 +#define FILE_ATTRIBUTE_NO_SCRUB_DATA 0x00020000 +#define FILE_ATTRIBUTE__MASK 0x00007FB7 + +#define FILE_ATTRIBUTE_READONLY_LE cpu_to_le32(0x00000001) +#define FILE_ATTRIBUTE_HIDDEN_LE cpu_to_le32(0x00000002) +#define FILE_ATTRIBUTE_SYSTEM_LE cpu_to_le32(0x00000004) +#define FILE_ATTRIBUTE_DIRECTORY_LE cpu_to_le32(0x00000010) +#define FILE_ATTRIBUTE_ARCHIVE_LE cpu_to_le32(0x00000020) +#define FILE_ATTRIBUTE_NORMAL_LE cpu_to_le32(0x00000080) +#define FILE_ATTRIBUTE_TEMPORARY_LE cpu_to_le32(0x00000100) +#define FILE_ATTRIBUTE_SPARSE_FILE_LE cpu_to_le32(0x00000200) +#define FILE_ATTRIBUTE_REPARSE_POINT_LE cpu_to_le32(0x00000400) +#define FILE_ATTRIBUTE_COMPRESSED_LE cpu_to_le32(0x00000800) +#define FILE_ATTRIBUTE_OFFLINE_LE cpu_to_le32(0x00001000) +#define FILE_ATTRIBUTE_NOT_CONTENT_INDEXED_LE cpu_to_le32(0x00002000) +#define FILE_ATTRIBUTE_ENCRYPTED_LE cpu_to_le32(0x00004000) +#define FILE_ATTRIBUTE_INTEGRITY_STREAM_LE cpu_to_le32(0x00008000) +#define FILE_ATTRIBUTE_NO_SCRUB_DATA_LE cpu_to_le32(0x00020000) +#define FILE_ATTRIBUTE_MASK_LE cpu_to_le32(0x00007FB7) + +/* Desired Access Flags */ +#define FILE_READ_DATA_LE cpu_to_le32(0x00000001) +#define FILE_LIST_DIRECTORY_LE cpu_to_le32(0x00000001) +#define FILE_WRITE_DATA_LE cpu_to_le32(0x00000002) +#define FILE_APPEND_DATA_LE cpu_to_le32(0x00000004) +#define FILE_ADD_SUBDIRECTORY_LE cpu_to_le32(0x00000004) +#define FILE_READ_EA_LE cpu_to_le32(0x00000008) +#define FILE_WRITE_EA_LE cpu_to_le32(0x00000010) +#define FILE_EXECUTE_LE cpu_to_le32(0x00000020) +#define FILE_DELETE_CHILD_LE cpu_to_le32(0x00000040) +#define FILE_READ_ATTRIBUTES_LE cpu_to_le32(0x00000080) +#define FILE_WRITE_ATTRIBUTES_LE cpu_to_le32(0x00000100) +#define FILE_DELETE_LE cpu_to_le32(0x00010000) +#define FILE_READ_CONTROL_LE cpu_to_le32(0x00020000) +#define FILE_WRITE_DAC_LE cpu_to_le32(0x00040000) +#define FILE_WRITE_OWNER_LE cpu_to_le32(0x00080000) +#define FILE_SYNCHRONIZE_LE cpu_to_le32(0x00100000) +#define FILE_ACCESS_SYSTEM_SECURITY_LE cpu_to_le32(0x01000000) +#define FILE_MAXIMAL_ACCESS_LE cpu_to_le32(0x02000000) +#define FILE_GENERIC_ALL_LE cpu_to_le32(0x10000000) +#define FILE_GENERIC_EXECUTE_LE cpu_to_le32(0x20000000) +#define FILE_GENERIC_WRITE_LE cpu_to_le32(0x40000000) +#define FILE_GENERIC_READ_LE cpu_to_le32(0x80000000) +#define DESIRED_ACCESS_MASK cpu_to_le32(0xF21F01FF) + + +#define FILE_READ_DESIRED_ACCESS_LE (FILE_READ_DATA_LE | \ + FILE_READ_EA_LE | \ + FILE_GENERIC_READ_LE) +#define FILE_WRITE_DESIRE_ACCESS_LE (FILE_WRITE_DATA_LE | \ + FILE_APPEND_DATA_LE | \ + FILE_WRITE_EA_LE | \ + FILE_WRITE_ATTRIBUTES_LE | \ + FILE_GENERIC_WRITE_LE) + +/* ShareAccess Flags */ +#define FILE_SHARE_READ_LE cpu_to_le32(0x00000001) +#define FILE_SHARE_WRITE_LE cpu_to_le32(0x00000002) +#define FILE_SHARE_DELETE_LE cpu_to_le32(0x00000004) +#define FILE_SHARE_ALL_LE cpu_to_le32(0x00000007) + +/* CreateDisposition Flags */ +#define FILE_SUPERSEDE_LE cpu_to_le32(0x00000000) +#define FILE_OPEN_LE cpu_to_le32(0x00000001) +#define FILE_CREATE_LE cpu_to_le32(0x00000002) +#define FILE_OPEN_IF_LE cpu_to_le32(0x00000003) +#define FILE_OVERWRITE_LE cpu_to_le32(0x00000004) +#define FILE_OVERWRITE_IF_LE cpu_to_le32(0x00000005) +#define FILE_CREATE_MASK_LE cpu_to_le32(0x00000007) + +#define FILE_READ_RIGHTS (FILE_READ_DATA | FILE_READ_EA \ + | FILE_READ_ATTRIBUTES) +#define FILE_WRITE_RIGHTS (FILE_WRITE_DATA | FILE_APPEND_DATA \ + | FILE_WRITE_EA | FILE_WRITE_ATTRIBUTES) +#define FILE_EXEC_RIGHTS (FILE_EXECUTE) + +/* CreateOptions Flags */ +#define FILE_DIRECTORY_FILE_LE cpu_to_le32(0x00000001) +/* same as #define CREATE_NOT_FILE_LE cpu_to_le32(0x00000001) */ +#define FILE_WRITE_THROUGH_LE cpu_to_le32(0x00000002) +#define FILE_SEQUENTIAL_ONLY_LE cpu_to_le32(0x00000004) +#define FILE_NO_INTERMEDIATE_BUFFERING_LE cpu_to_le32(0x00000008) +#define FILE_NON_DIRECTORY_FILE_LE cpu_to_le32(0x00000040) +#define FILE_COMPLETE_IF_OPLOCKED_LE cpu_to_le32(0x00000100) +#define FILE_NO_EA_KNOWLEDGE_LE cpu_to_le32(0x00000200) +#define FILE_RANDOM_ACCESS_LE cpu_to_le32(0x00000800) +#define FILE_DELETE_ON_CLOSE_LE cpu_to_le32(0x00001000) +#define FILE_OPEN_BY_FILE_ID_LE cpu_to_le32(0x00002000) +#define FILE_OPEN_FOR_BACKUP_INTENT_LE cpu_to_le32(0x00004000) +#define FILE_NO_COMPRESSION_LE cpu_to_le32(0x00008000) +#define FILE_OPEN_REPARSE_POINT_LE cpu_to_le32(0x00200000) +#define FILE_OPEN_NO_RECALL_LE cpu_to_le32(0x00400000) +#define CREATE_OPTIONS_MASK_LE cpu_to_le32(0x00FFFFFF) + +#define FILE_READ_RIGHTS_LE (FILE_READ_DATA_LE | FILE_READ_EA_LE \ + | FILE_READ_ATTRIBUTES_LE) +#define FILE_WRITE_RIGHTS_LE (FILE_WRITE_DATA_LE | FILE_APPEND_DATA_LE \ + | FILE_WRITE_EA_LE | FILE_WRITE_ATTRIBUTES_LE) +#define FILE_EXEC_RIGHTS_LE (FILE_EXECUTE_LE) + +/* Create Context Values */ +#define SMB2_CREATE_EA_BUFFER "ExtA" /* extended attributes */ +#define SMB2_CREATE_SD_BUFFER "SecD" /* security descriptor */ +#define SMB2_CREATE_DURABLE_HANDLE_REQUEST "DHnQ" +#define SMB2_CREATE_DURABLE_HANDLE_RECONNECT "DHnC" +#define SMB2_CREATE_ALLOCATION_SIZE "AISi" +#define SMB2_CREATE_QUERY_MAXIMAL_ACCESS_REQUEST "MxAc" +#define SMB2_CREATE_TIMEWARP_REQUEST "TWrp" +#define SMB2_CREATE_QUERY_ON_DISK_ID "QFid" +#define SMB2_CREATE_REQUEST_LEASE "RqLs" +#define SMB2_CREATE_DURABLE_HANDLE_REQUEST_V2 "DH2Q" +#define SMB2_CREATE_DURABLE_HANDLE_RECONNECT_V2 "DH2C" +#define SMB2_CREATE_TAG_POSIX "\x93\xAD\x25\x50\x9C\xB4\x11\xE7\xB4\x23\x83\xDE\x96\x8B\xCD\x7C" +#define SMB2_CREATE_APP_INSTANCE_ID "\x45\xBC\xA6\x6A\xEF\xA7\xF7\x4A\x90\x08\xFA\x46\x2E\x14\x4D\x74" +#define SMB2_CREATE_APP_INSTANCE_VERSION "\xB9\x82\xD0\xB7\x3B\x56\x07\x4F\xA0\x7B\x52\x4A\x81\x16\xA0\x10" +#define SVHDX_OPEN_DEVICE_CONTEXT "\x9C\xCB\xCF\x9E\x04\xC1\xE6\x43\x98\x0E\x15\x8D\xA1\xF6\xEC\x83" +#define SMB2_CREATE_TAG_AAPL "AAPL" + +/* Flag (SMB3 open response) values */ +#define SMB2_CREATE_FLAG_REPARSEPOINT 0x01 + +struct create_context { + __le32 Next; + __le16 NameOffset; + __le16 NameLength; + __le16 Reserved; + __le16 DataOffset; + __le32 DataLength; + __u8 Buffer[]; +} __packed; + +struct smb2_create_req { + struct smb2_hdr hdr; + __le16 StructureSize; /* Must be 57 */ + __u8 SecurityFlags; + __u8 RequestedOplockLevel; + __le32 ImpersonationLevel; + __le64 SmbCreateFlags; + __le64 Reserved; + __le32 DesiredAccess; + __le32 FileAttributes; + __le32 ShareAccess; + __le32 CreateDisposition; + __le32 CreateOptions; + __le16 NameOffset; + __le16 NameLength; + __le32 CreateContextsOffset; + __le32 CreateContextsLength; + __u8 Buffer[]; +} __packed; + +struct smb2_create_rsp { + struct smb2_hdr hdr; + __le16 StructureSize; /* Must be 89 */ + __u8 OplockLevel; + __u8 Flags; /* 0x01 if reparse point */ + __le32 CreateAction; + __le64 CreationTime; + __le64 LastAccessTime; + __le64 LastWriteTime; + __le64 ChangeTime; + __le64 AllocationSize; + __le64 EndofFile; + __le32 FileAttributes; + __le32 Reserved2; + __u64 PersistentFileId; + __u64 VolatileFileId; + __le32 CreateContextsOffset; + __le32 CreateContextsLength; + __u8 Buffer[1]; +} __packed; + +struct create_posix { + struct create_context ccontext; + __u8 Name[16]; + __le32 Mode; + __u32 Reserved; +} __packed; + +#define SMB2_LEASE_NONE_LE cpu_to_le32(0x00) +#define SMB2_LEASE_READ_CACHING_LE cpu_to_le32(0x01) +#define SMB2_LEASE_HANDLE_CACHING_LE cpu_to_le32(0x02) +#define SMB2_LEASE_WRITE_CACHING_LE cpu_to_le32(0x04) + +#define SMB2_LEASE_FLAG_BREAK_IN_PROGRESS_LE cpu_to_le32(0x02) + +#define SMB2_LEASE_KEY_SIZE 16 + +struct lease_context { + __u8 LeaseKey[SMB2_LEASE_KEY_SIZE]; + __le32 LeaseState; + __le32 LeaseFlags; + __le64 LeaseDuration; +} __packed; + +struct lease_context_v2 { + __u8 LeaseKey[SMB2_LEASE_KEY_SIZE]; + __le32 LeaseState; + __le32 LeaseFlags; + __le64 LeaseDuration; + __u8 ParentLeaseKey[SMB2_LEASE_KEY_SIZE]; + __le16 Epoch; + __le16 Reserved; +} __packed; + +struct create_lease { + struct create_context ccontext; + __u8 Name[8]; + struct lease_context lcontext; +} __packed; + +struct create_lease_v2 { + struct create_context ccontext; + __u8 Name[8]; + struct lease_context_v2 lcontext; + __u8 Pad[4]; +} __packed; + +/* See MS-SMB2 2.2.31 and 2.2.32 */ +struct smb2_ioctl_req { + struct smb2_hdr hdr; + __le16 StructureSize; /* Must be 57 */ + __le16 Reserved; /* offset from start of SMB2 header to write data */ + __le32 CtlCode; + __u64 PersistentFileId; + __u64 VolatileFileId; + __le32 InputOffset; /* Reserved MBZ */ + __le32 InputCount; + __le32 MaxInputResponse; + __le32 OutputOffset; + __le32 OutputCount; + __le32 MaxOutputResponse; + __le32 Flags; + __le32 Reserved2; + __u8 Buffer[]; +} __packed; + +struct smb2_ioctl_rsp { + struct smb2_hdr hdr; + __le16 StructureSize; /* Must be 49 */ + __le16 Reserved; + __le32 CtlCode; + __u64 PersistentFileId; + __u64 VolatileFileId; + __le32 InputOffset; /* Reserved MBZ */ + __le32 InputCount; + __le32 OutputOffset; + __le32 OutputCount; + __le32 Flags; + __le32 Reserved2; + __u8 Buffer[]; +} __packed; + +/* this goes in the ioctl buffer when doing FSCTL_SET_ZERO_DATA */ +struct file_zero_data_information { + __le64 FileOffset; + __le64 BeyondFinalZero; +} __packed; + +/* See MS-FSCC 2.3.7 */ +struct duplicate_extents_to_file { + __u64 PersistentFileHandle; /* source file handle, opaque endianness */ + __u64 VolatileFileHandle; + __le64 SourceFileOffset; + __le64 TargetFileOffset; + __le64 ByteCount; /* Bytes to be copied */ +} __packed; + +/* See MS-FSCC 2.3.8 */ +#define DUPLICATE_EXTENTS_DATA_EX_SOURCE_ATOMIC 0x00000001 +struct duplicate_extents_to_file_ex { + __u64 PersistentFileHandle; /* source file handle, opaque endianness */ + __u64 VolatileFileHandle; + __le64 SourceFileOffset; + __le64 TargetFileOffset; + __le64 ByteCount; /* Bytes to be copied */ + __le32 Flags; + __le32 Reserved; +} __packed; + + +/* See MS-FSCC 2.3.20 */ +struct fsctl_get_integrity_information_rsp { + __le16 ChecksumAlgorithm; + __le16 Reserved; + __le32 Flags; + __le32 ChecksumChunkSizeInBytes; + __le32 ClusterSizeInBytes; +} __packed; + +/* See MS-FSCC 2.3.55 */ +struct fsctl_query_file_regions_req { + __le64 FileOffset; + __le64 Length; + __le32 DesiredUsage; + __le32 Reserved; +} __packed; + +/* DesiredUsage flags see MS-FSCC 2.3.56.1 */ +#define FILE_USAGE_INVALID_RANGE 0x00000000 +#define FILE_USAGE_VALID_CACHED_DATA 0x00000001 +#define FILE_USAGE_NONCACHED_DATA 0x00000002 + +struct file_region_info { + __le64 FileOffset; + __le64 Length; + __le32 DesiredUsage; + __le32 Reserved; +} __packed; + +/* See MS-FSCC 2.3.56 */ +struct fsctl_query_file_region_rsp { + __le32 Flags; + __le32 TotalRegionEntryCount; + __le32 RegionEntryCount; + __u32 Reserved; + struct file_region_info Regions[]; +} __packed; + +/* See MS-FSCC 2.3.58 */ +struct fsctl_query_on_disk_vol_info_rsp { + __le64 DirectoryCount; + __le64 FileCount; + __le16 FsFormatMajVersion; + __le16 FsFormatMinVersion; + __u8 FsFormatName[24]; + __le64 FormatTime; + __le64 LastUpdateTime; + __u8 CopyrightInfo[68]; + __u8 AbstractInfo[68]; + __u8 FormatImplInfo[68]; + __u8 LastModifyImplInfo[68]; +} __packed; + +/* See MS-FSCC 2.3.73 */ +struct fsctl_set_integrity_information_req { + __le16 ChecksumAlgorithm; + __le16 Reserved; + __le32 Flags; +} __packed; + +/* See MS-FSCC 2.3.75 */ +struct fsctl_set_integrity_info_ex_req { + __u8 EnableIntegrity; + __u8 KeepState; + __u16 Reserved; + __le32 Flags; + __u8 Version; + __u8 Reserved2[7]; +} __packed; + +/* Integrity ChecksumAlgorithm choices for above */ +#define CHECKSUM_TYPE_NONE 0x0000 +#define CHECKSUM_TYPE_CRC64 0x0002 +#define CHECKSUM_TYPE_UNCHANGED 0xFFFF /* set only */ + +/* Integrity flags for above */ +#define FSCTL_INTEGRITY_FLAG_CHECKSUM_ENFORCEMENT_OFF 0x00000001 + +/* Reparse structures - see MS-FSCC 2.1.2 */ + +/* struct fsctl_reparse_info_req is empty, only response structs (see below) */ +struct reparse_data_buffer { + __le32 ReparseTag; + __le16 ReparseDataLength; + __u16 Reserved; + __u8 DataBuffer[]; /* Variable Length */ +} __packed; + +struct reparse_guid_data_buffer { + __le32 ReparseTag; + __le16 ReparseDataLength; + __u16 Reserved; + __u8 ReparseGuid[16]; + __u8 DataBuffer[]; /* Variable Length */ +} __packed; + +struct reparse_mount_point_data_buffer { + __le32 ReparseTag; + __le16 ReparseDataLength; + __u16 Reserved; + __le16 SubstituteNameOffset; + __le16 SubstituteNameLength; + __le16 PrintNameOffset; + __le16 PrintNameLength; + __u8 PathBuffer[]; /* Variable Length */ +} __packed; + +#define SYMLINK_FLAG_RELATIVE 0x00000001 + +struct reparse_symlink_data_buffer { + __le32 ReparseTag; + __le16 ReparseDataLength; + __u16 Reserved; + __le16 SubstituteNameOffset; + __le16 SubstituteNameLength; + __le16 PrintNameOffset; + __le16 PrintNameLength; + __le32 Flags; + __u8 PathBuffer[]; /* Variable Length */ +} __packed; + +/* See MS-FSCC 2.1.2.6 and cifspdu.h for struct reparse_posix_data */ + +struct validate_negotiate_info_req { + __le32 Capabilities; + __u8 Guid[SMB2_CLIENT_GUID_SIZE]; + __le16 SecurityMode; + __le16 DialectCount; + __le16 Dialects[4]; /* BB expand this if autonegotiate > 4 dialects */ +} __packed; + +struct validate_negotiate_info_rsp { + __le32 Capabilities; + __u8 Guid[SMB2_CLIENT_GUID_SIZE]; + __le16 SecurityMode; + __le16 Dialect; /* Dialect in use for the connection */ +} __packed; + + +/* Possible InfoType values */ +#define SMB2_O_INFO_FILE 0x01 +#define SMB2_O_INFO_FILESYSTEM 0x02 +#define SMB2_O_INFO_SECURITY 0x03 +#define SMB2_O_INFO_QUOTA 0x04 + +/* SMB2 Query Info see MS-SMB2 (2.2.37) or MS-DTYP */ + +/* List of QUERY INFO levels (those also valid for QUERY_DIR are noted below */ +#define FILE_DIRECTORY_INFORMATION 1 /* also for QUERY_DIR */ +#define FILE_FULL_DIRECTORY_INFORMATION 2 /* also for QUERY_DIR */ +#define FILE_BOTH_DIRECTORY_INFORMATION 3 /* also for QUERY_DIR */ +#define FILE_BASIC_INFORMATION 4 +#define FILE_STANDARD_INFORMATION 5 +#define FILE_INTERNAL_INFORMATION 6 +#define FILE_EA_INFORMATION 7 +#define FILE_ACCESS_INFORMATION 8 +#define FILE_NAME_INFORMATION 9 +#define FILE_RENAME_INFORMATION 10 +#define FILE_LINK_INFORMATION 11 +#define FILE_NAMES_INFORMATION 12 /* also for QUERY_DIR */ +#define FILE_DISPOSITION_INFORMATION 13 +#define FILE_POSITION_INFORMATION 14 +#define FILE_FULL_EA_INFORMATION 15 +#define FILE_MODE_INFORMATION 16 +#define FILE_ALIGNMENT_INFORMATION 17 +#define FILE_ALL_INFORMATION 18 +#define FILE_ALLOCATION_INFORMATION 19 +#define FILE_END_OF_FILE_INFORMATION 20 +#define FILE_ALTERNATE_NAME_INFORMATION 21 +#define FILE_STREAM_INFORMATION 22 +#define FILE_PIPE_INFORMATION 23 +#define FILE_PIPE_LOCAL_INFORMATION 24 +#define FILE_PIPE_REMOTE_INFORMATION 25 +#define FILE_MAILSLOT_QUERY_INFORMATION 26 +#define FILE_MAILSLOT_SET_INFORMATION 27 +#define FILE_COMPRESSION_INFORMATION 28 +#define FILE_OBJECT_ID_INFORMATION 29 +/* Number 30 not defined in documents */ +#define FILE_MOVE_CLUSTER_INFORMATION 31 +#define FILE_QUOTA_INFORMATION 32 +#define FILE_REPARSE_POINT_INFORMATION 33 +#define FILE_NETWORK_OPEN_INFORMATION 34 +#define FILE_ATTRIBUTE_TAG_INFORMATION 35 +#define FILE_TRACKING_INFORMATION 36 +#define FILEID_BOTH_DIRECTORY_INFORMATION 37 /* also for QUERY_DIR */ +#define FILEID_FULL_DIRECTORY_INFORMATION 38 /* also for QUERY_DIR */ +#define FILE_VALID_DATA_LENGTH_INFORMATION 39 +#define FILE_SHORT_NAME_INFORMATION 40 +#define FILE_SFIO_RESERVE_INFORMATION 44 +#define FILE_SFIO_VOLUME_INFORMATION 45 +#define FILE_HARD_LINK_INFORMATION 46 +#define FILE_NORMALIZED_NAME_INFORMATION 48 +#define FILEID_GLOBAL_TX_DIRECTORY_INFORMATION 50 +#define FILE_STANDARD_LINK_INFORMATION 54 +#define FILE_ID_INFORMATION 59 +#define FILE_ID_EXTD_DIRECTORY_INFORMATION 60 /* also for QUERY_DIR */ +/* Used for Query Info and Find File POSIX Info for SMB3.1.1 and SMB1 */ +#define SMB_FIND_FILE_POSIX_INFO 0x064 + +/* Security info type additionalinfo flags. */ +#define OWNER_SECINFO 0x00000001 +#define GROUP_SECINFO 0x00000002 +#define DACL_SECINFO 0x00000004 +#define SACL_SECINFO 0x00000008 +#define LABEL_SECINFO 0x00000010 +#define ATTRIBUTE_SECINFO 0x00000020 +#define SCOPE_SECINFO 0x00000040 +#define BACKUP_SECINFO 0x00010000 +#define UNPROTECTED_SACL_SECINFO 0x10000000 +#define UNPROTECTED_DACL_SECINFO 0x20000000 +#define PROTECTED_SACL_SECINFO 0x40000000 +#define PROTECTED_DACL_SECINFO 0x80000000 + +/* Flags used for FileFullEAinfo */ +#define SL_RESTART_SCAN 0x00000001 +#define SL_RETURN_SINGLE_ENTRY 0x00000002 +#define SL_INDEX_SPECIFIED 0x00000004 + +struct smb2_query_info_req { + struct smb2_hdr hdr; + __le16 StructureSize; /* Must be 41 */ + __u8 InfoType; + __u8 FileInfoClass; + __le32 OutputBufferLength; + __le16 InputBufferOffset; + __u16 Reserved; + __le32 InputBufferLength; + __le32 AdditionalInformation; + __le32 Flags; + __u64 PersistentFileId; + __u64 VolatileFileId; + __u8 Buffer[1]; +} __packed; + +struct smb2_query_info_rsp { + struct smb2_hdr hdr; + __le16 StructureSize; /* Must be 9 */ + __le16 OutputBufferOffset; + __le32 OutputBufferLength; + __u8 Buffer[1]; +} __packed; + +/* + * PDU query infolevel structure definitions + */ + +/* See MS-FSCC 2.3.52 */ +struct file_allocated_range_buffer { + __le64 file_offset; + __le64 length; +} __packed; + +struct smb2_file_internal_info { + __le64 IndexNumber; +} __packed; /* level 6 Query */ + +struct smb2_file_rename_info { /* encoding of request for level 10 */ + __u8 ReplaceIfExists; /* 1 = replace existing target with new */ + /* 0 = fail if target already exists */ + __u8 Reserved[7]; + __u64 RootDirectory; /* MBZ for network operations (why says spec?) */ + __le32 FileNameLength; + char FileName[]; /* New name to be assigned */ + /* padding - overall struct size must be >= 24 so filename + pad >= 6 */ +} __packed; /* level 10 Set */ + +struct smb2_file_link_info { /* encoding of request for level 11 */ + __u8 ReplaceIfExists; /* 1 = replace existing link with new */ + /* 0 = fail if link already exists */ + __u8 Reserved[7]; + __u64 RootDirectory; /* MBZ for network operations (why says spec?) */ + __le32 FileNameLength; + char FileName[]; /* Name to be assigned to new link */ +} __packed; /* level 11 Set */ + +/* + * This level 18, although with struct with same name is different from cifs + * level 0x107. Level 0x107 has an extra u64 between AccessFlags and + * CurrentByteOffset. + */ +struct smb2_file_all_info { /* data block encoding of response to level 18 */ + __le64 CreationTime; /* Beginning of FILE_BASIC_INFO equivalent */ + __le64 LastAccessTime; + __le64 LastWriteTime; + __le64 ChangeTime; + __le32 Attributes; + __u32 Pad1; /* End of FILE_BASIC_INFO_INFO equivalent */ + __le64 AllocationSize; /* Beginning of FILE_STANDARD_INFO equivalent */ + __le64 EndOfFile; /* size ie offset to first free byte in file */ + __le32 NumberOfLinks; /* hard links */ + __u8 DeletePending; + __u8 Directory; + __u16 Pad2; /* End of FILE_STANDARD_INFO equivalent */ + __le64 IndexNumber; + __le32 EASize; + __le32 AccessFlags; + __le64 CurrentByteOffset; + __le32 Mode; + __le32 AlignmentRequirement; + __le32 FileNameLength; + char FileName[1]; +} __packed; /* level 18 Query */ + +struct smb2_file_eof_info { /* encoding of request for level 10 */ + __le64 EndOfFile; /* new end of file value */ +} __packed; /* level 20 Set */ + +/* Level 100 query info */ +struct smb311_posix_qinfo { + __le64 CreationTime; + __le64 LastAccessTime; + __le64 LastWriteTime; + __le64 ChangeTime; + __le64 EndOfFile; + __le64 AllocationSize; + __le32 DosAttributes; + __le64 Inode; + __le32 DeviceId; + __le32 Zero; + /* beginning of POSIX Create Context Response */ + __le32 HardLinks; + __le32 ReparseTag; + __le32 Mode; + u8 Sids[]; + /* + * var sized owner SID + * var sized group SID + * le32 filenamelength + * u8 filename[] + */ +} __packed; + +/* File System Information Classes */ +#define FS_VOLUME_INFORMATION 1 /* Query */ +#define FS_LABEL_INFORMATION 2 /* Set */ +#define FS_SIZE_INFORMATION 3 /* Query */ +#define FS_DEVICE_INFORMATION 4 /* Query */ +#define FS_ATTRIBUTE_INFORMATION 5 /* Query */ +#define FS_CONTROL_INFORMATION 6 /* Query, Set */ +#define FS_FULL_SIZE_INFORMATION 7 /* Query */ +#define FS_OBJECT_ID_INFORMATION 8 /* Query, Set */ +#define FS_DRIVER_PATH_INFORMATION 9 /* Query */ +#define FS_SECTOR_SIZE_INFORMATION 11 /* SMB3 or later. Query */ +#define FS_POSIX_INFORMATION 100 /* SMB3.1.1 POSIX. Query */ + +struct smb2_fs_full_size_info { + __le64 TotalAllocationUnits; + __le64 CallerAvailableAllocationUnits; + __le64 ActualAvailableAllocationUnits; + __le32 SectorsPerAllocationUnit; + __le32 BytesPerSector; +} __packed; + +#define SSINFO_FLAGS_ALIGNED_DEVICE 0x00000001 +#define SSINFO_FLAGS_PARTITION_ALIGNED_ON_DEVICE 0x00000002 +#define SSINFO_FLAGS_NO_SEEK_PENALTY 0x00000004 +#define SSINFO_FLAGS_TRIM_ENABLED 0x00000008 + +/* sector size info struct */ +struct smb3_fs_ss_info { + __le32 LogicalBytesPerSector; + __le32 PhysicalBytesPerSectorForAtomicity; + __le32 PhysicalBytesPerSectorForPerf; + __le32 FSEffPhysicalBytesPerSectorForAtomicity; + __le32 Flags; + __le32 ByteOffsetForSectorAlignment; + __le32 ByteOffsetForPartitionAlignment; +} __packed; + +/* File System Control Information */ +struct smb2_fs_control_info { + __le64 FreeSpaceStartFiltering; + __le64 FreeSpaceThreshold; + __le64 FreeSpaceStopFiltering; + __le64 DefaultQuotaThreshold; + __le64 DefaultQuotaLimit; + __le32 FileSystemControlFlags; + __le32 Padding; +} __packed; + +/* volume info struct - see MS-FSCC 2.5.9 */ +#define MAX_VOL_LABEL_LEN 32 +struct smb3_fs_vol_info { + __le64 VolumeCreationTime; + __u32 VolumeSerialNumber; + __le32 VolumeLabelLength; /* includes trailing null */ + __u8 SupportsObjects; /* True if eg like NTFS, supports objects */ + __u8 Reserved; + __u8 VolumeLabel[]; /* variable len */ +} __packed; + +/* See MS-SMB2 2.2.23 through 2.2.25 */ +struct smb2_oplock_break { + struct smb2_hdr hdr; + __le16 StructureSize; /* Must be 24 */ + __u8 OplockLevel; + __u8 Reserved; + __le32 Reserved2; + __u64 PersistentFid; + __u64 VolatileFid; +} __packed; + +#define SMB2_NOTIFY_BREAK_LEASE_FLAG_ACK_REQUIRED cpu_to_le32(0x01) + +struct smb2_lease_break { + struct smb2_hdr hdr; + __le16 StructureSize; /* Must be 44 */ + __le16 Epoch; + __le32 Flags; + __u8 LeaseKey[16]; + __le32 CurrentLeaseState; + __le32 NewLeaseState; + __le32 BreakReason; + __le32 AccessMaskHint; + __le32 ShareMaskHint; +} __packed; + +struct smb2_lease_ack { + struct smb2_hdr hdr; + __le16 StructureSize; /* Must be 36 */ + __le16 Reserved; + __le32 Flags; + __u8 LeaseKey[16]; + __le32 LeaseState; + __le64 LeaseDuration; +} __packed; + +#define OP_BREAK_STRUCT_SIZE_20 24 +#define OP_BREAK_STRUCT_SIZE_21 36 +#endif /* _COMMON_SMB2PDU_H */ diff --git a/fs/smb/common/smbfsctl.h b/fs/smb/common/smbfsctl.h new file mode 100644 index 0000000000000..edd7fc2a7921b --- /dev/null +++ b/fs/smb/common/smbfsctl.h @@ -0,0 +1,170 @@ +/* SPDX-License-Identifier: LGPL-2.1+ */ +/* + * SMB, CIFS, SMB2 FSCTL definitions + * + * Copyright (c) International Business Machines Corp., 2002,2013 + * Author(s): Steve French (sfrench@us.ibm.com) + * + */ + +/* IOCTL information */ +/* + * List of ioctl/fsctl function codes that are or could be useful in the + * future to remote clients like cifs or SMB2/SMB3 client. This is probably + * a slightly larger set of fsctls that NTFS local filesystem could handle, + * including the seven below that we do not have struct definitions for. + * Even with protocol definitions for most of these now available, we still + * need to do some experimentation to identify which are practical to do + * remotely. Some of the following, such as the encryption/compression ones + * could be invoked from tools via a specialized hook into the VFS rather + * than via the standard vfs entry points + * + * See MS-SMB2 Section 2.2.31 (last checked September 2021, all of that list are + * below). Additional detail on less common ones can be found in MS-FSCC + * section 2.3. + */ + +#ifndef __SMBFSCTL_H +#define __SMBFSCTL_H + +/* + * FSCTL values are 32 bits and are constructed as + * + */ +/* Device */ +#define FSCTL_DEVICE_DFS (0x0006 << 16) +#define FSCTL_DEVICE_FILE_SYSTEM (0x0009 << 16) +#define FSCTL_DEVICE_NAMED_PIPE (0x0011 << 16) +#define FSCTL_DEVICE_NETWORK_FILE_SYSTEM (0x0014 << 16) +#define FSCTL_DEVICE_MASK 0xffff0000 +/* Access */ +#define FSCTL_DEVICE_ACCESS_FILE_ANY_ACCESS (0x00 << 14) +#define FSCTL_DEVICE_ACCESS_FILE_READ_ACCESS (0x01 << 14) +#define FSCTL_DEVICE_ACCESS_FILE_WRITE_ACCESS (0x02 << 14) +#define FSCTL_DEVICE_ACCESS_FILE_READ_WRITE_ACCESS (0x03 << 14) +#define FSCTL_DEVICE_ACCESS_MASK 0x0000c000 +/* Function */ +#define FSCTL_DEVICE_FUNCTION_MASK 0x00003ffc +/* Method */ +#define FSCTL_DEVICE_METHOD_BUFFERED 0x00 +#define FSCTL_DEVICE_METHOD_IN_DIRECT 0x01 +#define FSCTL_DEVICE_METHOD_OUT_DIRECT 0x02 +#define FSCTL_DEVICE_METHOD_NEITHER 0x03 +#define FSCTL_DEVICE_METHOD_MASK 0x00000003 + + +#define FSCTL_DFS_GET_REFERRALS 0x00060194 +#define FSCTL_DFS_GET_REFERRALS_EX 0x000601B0 +#define FSCTL_REQUEST_OPLOCK_LEVEL_1 0x00090000 +#define FSCTL_REQUEST_OPLOCK_LEVEL_2 0x00090004 +#define FSCTL_REQUEST_BATCH_OPLOCK 0x00090008 +#define FSCTL_LOCK_VOLUME 0x00090018 +#define FSCTL_UNLOCK_VOLUME 0x0009001C +#define FSCTL_IS_PATHNAME_VALID 0x0009002C /* BB add struct */ +#define FSCTL_GET_COMPRESSION 0x0009003C /* BB add struct */ +#define FSCTL_SET_COMPRESSION 0x0009C040 /* BB add struct */ +#define FSCTL_QUERY_FAT_BPB 0x00090058 /* BB add struct */ +/* Verify the next FSCTL number, we had it as 0x00090090 before */ +#define FSCTL_FILESYSTEM_GET_STATS 0x00090060 /* BB add struct */ +#define FSCTL_GET_NTFS_VOLUME_DATA 0x00090064 /* BB add struct */ +#define FSCTL_GET_RETRIEVAL_POINTERS 0x00090073 /* BB add struct */ +#define FSCTL_IS_VOLUME_DIRTY 0x00090078 /* BB add struct */ +#define FSCTL_ALLOW_EXTENDED_DASD_IO 0x00090083 /* BB add struct */ +#define FSCTL_REQUEST_FILTER_OPLOCK 0x0009008C +#define FSCTL_FIND_FILES_BY_SID 0x0009008F /* BB add struct */ +#define FSCTL_SET_OBJECT_ID 0x00090098 /* BB add struct */ +#define FSCTL_GET_OBJECT_ID 0x0009009C /* BB add struct */ +#define FSCTL_DELETE_OBJECT_ID 0x000900A0 /* BB add struct */ +#define FSCTL_SET_REPARSE_POINT 0x000900A4 /* BB add struct */ +#define FSCTL_GET_REPARSE_POINT 0x000900A8 /* BB add struct */ +#define FSCTL_DELETE_REPARSE_POINT 0x000900AC /* BB add struct */ +#define FSCTL_SET_OBJECT_ID_EXTENDED 0x000900BC /* BB add struct */ +#define FSCTL_CREATE_OR_GET_OBJECT_ID 0x000900C0 /* BB add struct */ +#define FSCTL_SET_SPARSE 0x000900C4 /* BB add struct */ +#define FSCTL_SET_ZERO_DATA 0x000980C8 +#define FSCTL_SET_ENCRYPTION 0x000900D7 /* BB add struct */ +#define FSCTL_ENCRYPTION_FSCTL_IO 0x000900DB /* BB add struct */ +#define FSCTL_WRITE_RAW_ENCRYPTED 0x000900DF /* BB add struct */ +#define FSCTL_READ_RAW_ENCRYPTED 0x000900E3 /* BB add struct */ +#define FSCTL_READ_FILE_USN_DATA 0x000900EB /* BB add struct */ +#define FSCTL_WRITE_USN_CLOSE_RECORD 0x000900EF /* BB add struct */ +#define FSCTL_MARK_HANDLE 0x000900FC /* BB add struct */ +#define FSCTL_SIS_COPYFILE 0x00090100 /* BB add struct */ +#define FSCTL_RECALL_FILE 0x00090117 /* BB add struct */ +#define FSCTL_QUERY_SPARING_INFO 0x00090138 /* BB add struct */ +#define FSCTL_QUERY_ON_DISK_VOLUME_INFO 0x0009013C +#define FSCTL_SET_ZERO_ON_DEALLOC 0x00090194 /* BB add struct */ +#define FSCTL_SET_SHORT_NAME_BEHAVIOR 0x000901B4 /* BB add struct */ +#define FSCTL_GET_INTEGRITY_INFORMATION 0x0009027C +#define FSCTL_QUERY_FILE_REGIONS 0x00090284 +#define FSCTL_GET_REFS_VOLUME_DATA 0x000902D8 /* See MS-FSCC 2.3.24 */ +#define FSCTL_SET_INTEGRITY_INFORMATION_EXT 0x00090380 +#define FSCTL_GET_RETRIEVAL_POINTERS_AND_REFCOUNT 0x000903d3 +#define FSCTL_GET_RETRIEVAL_POINTER_COUNT 0x0009042b +#define FSCTL_REFS_STREAM_SNAPSHOT_MANAGEMENT 0x00090440 +#define FSCTL_QUERY_ALLOCATED_RANGES 0x000940CF +#define FSCTL_OFFLOAD_READ 0x00094264 /* BB add struct */ +#define FSCTL_OFFLOAD_WRITE 0x00098268 /* BB add struct */ +#define FSCTL_SET_DEFECT_MANAGEMENT 0x00098134 /* BB add struct */ +#define FSCTL_FILE_LEVEL_TRIM 0x00098208 /* BB add struct */ +#define FSCTL_DUPLICATE_EXTENTS_TO_FILE 0x00098344 +#define FSCTL_DUPLICATE_EXTENTS_TO_FILE_EX 0x000983E8 +#define FSCTL_SIS_LINK_FILES 0x0009C104 +#define FSCTL_SET_INTEGRITY_INFORMATION 0x0009C280 +#define FSCTL_PIPE_PEEK 0x0011400C /* BB add struct */ +#define FSCTL_PIPE_TRANSCEIVE 0x0011C017 /* BB add struct */ +/* strange that the number for this op is not sequential with previous op */ +#define FSCTL_PIPE_WAIT 0x00110018 /* BB add struct */ +/* Enumerate previous versions of a file */ +#define FSCTL_SRV_ENUMERATE_SNAPSHOTS 0x00144064 +/* Retrieve an opaque file reference for server-side data movement ie copy */ +#define FSCTL_SRV_REQUEST_RESUME_KEY 0x00140078 +#define FSCTL_LMR_REQUEST_RESILIENCY 0x001401D4 +#define FSCTL_LMR_GET_LINK_TRACK_INF 0x001400E8 /* BB add struct */ +#define FSCTL_LMR_SET_LINK_TRACK_INF 0x001400EC /* BB add struct */ +#define FSCTL_VALIDATE_NEGOTIATE_INFO 0x00140204 +/* Perform server-side data movement */ +#define FSCTL_SRV_COPYCHUNK 0x001440F2 +#define FSCTL_SRV_COPYCHUNK_WRITE 0x001480F2 +#define FSCTL_QUERY_NETWORK_INTERFACE_INFO 0x001401FC /* BB add struct */ +#define FSCTL_SRV_READ_HASH 0x001441BB /* BB add struct */ + +/* See FSCC 2.1.2.5 */ +#define IO_REPARSE_TAG_MOUNT_POINT 0xA0000003 +#define IO_REPARSE_TAG_HSM 0xC0000004 +#define IO_REPARSE_TAG_SIS 0x80000007 +#define IO_REPARSE_TAG_HSM2 0x80000006 +#define IO_REPARSE_TAG_DRIVER_EXTENDER 0x80000005 +/* Used by the DFS filter. See MS-DFSC */ +#define IO_REPARSE_TAG_DFS 0x8000000A +/* Used by the DFS filter See MS-DFSC */ +#define IO_REPARSE_TAG_DFSR 0x80000012 +#define IO_REPARSE_TAG_FILTER_MANAGER 0x8000000B +/* See section MS-FSCC 2.1.2.4 */ +#define IO_REPARSE_TAG_SYMLINK 0xA000000C +#define IO_REPARSE_TAG_DEDUP 0x80000013 +#define IO_REPARSE_APPXSTREAM 0xC0000014 +/* NFS symlinks, Win 8/SMB3 and later */ +#define IO_REPARSE_TAG_NFS 0x80000014 +/* + * AzureFileSync - see + * https://docs.microsoft.com/en-us/azure/storage/files/storage-sync-cloud-tiering + */ +#define IO_REPARSE_TAG_AZ_FILE_SYNC 0x8000001e +/* WSL reparse tags */ +#define IO_REPARSE_TAG_LX_SYMLINK 0xA000001D +#define IO_REPARSE_TAG_AF_UNIX 0x80000023 +#define IO_REPARSE_TAG_LX_FIFO 0x80000024 +#define IO_REPARSE_TAG_LX_CHR 0x80000025 +#define IO_REPARSE_TAG_LX_BLK 0x80000026 + +#define IO_REPARSE_TAG_LX_SYMLINK_LE cpu_to_le32(0xA000001D) +#define IO_REPARSE_TAG_AF_UNIX_LE cpu_to_le32(0x80000023) +#define IO_REPARSE_TAG_LX_FIFO_LE cpu_to_le32(0x80000024) +#define IO_REPARSE_TAG_LX_CHR_LE cpu_to_le32(0x80000025) +#define IO_REPARSE_TAG_LX_BLK_LE cpu_to_le32(0x80000026) + +/* fsctl flags */ +/* If Flags is set to this value, the request is an FSCTL not ioctl request */ +#define SMB2_0_IOCTL_IS_FSCTL 0x00000001 +#endif /* __SMBFSCTL_H */ diff --git a/fs/smb/server/Kconfig b/fs/smb/server/Kconfig new file mode 100644 index 0000000000000..e1fe17747ed69 --- /dev/null +++ b/fs/smb/server/Kconfig @@ -0,0 +1,68 @@ +config SMB_SERVER + tristate "SMB3 server support (EXPERIMENTAL)" + depends on INET + depends on MULTIUSER + depends on FILE_LOCKING + select NLS + select NLS_UTF8 + select CRYPTO + select CRYPTO_MD5 + select CRYPTO_HMAC + select CRYPTO_ECB + select CRYPTO_LIB_DES + select CRYPTO_SHA256 + select CRYPTO_CMAC + select CRYPTO_SHA512 + select CRYPTO_AEAD2 + select CRYPTO_CCM + select CRYPTO_GCM + select ASN1 + select OID_REGISTRY + select CRC32 + default n + help + Choose Y here if you want to allow SMB3 compliant clients + to access files residing on this system using SMB3 protocol. + To compile the SMB3 server support as a module, + choose M here: the module will be called ksmbd. + + You may choose to use a samba server instead, in which + case you can choose N here. + + You also need to install user space programs which can be found + in ksmbd-tools, available from + https://github.com/cifsd-team/ksmbd-tools. + More detail about how to run the ksmbd kernel server is + available via README file + (https://github.com/cifsd-team/ksmbd-tools/blob/master/README). + + ksmbd kernel server includes support for auto-negotiation, + Secure negotiate, Pre-authentication integrity, oplock/lease, + compound requests, multi-credit, packet signing, RDMA(smbdirect), + smb3 encryption, copy-offload, secure per-user session + establishment via NTLM or NTLMv2. + +config SMB_SERVER_SMBDIRECT + bool "Support for SMB Direct protocol" + depends on SMB_SERVER=m && INFINIBAND && INFINIBAND_ADDR_TRANS || SMB_SERVER=y && INFINIBAND=y && INFINIBAND_ADDR_TRANS=y + select SG_POOL + default n + + help + Enables SMB Direct support for SMB 3.0, 3.02 and 3.1.1. + + SMB Direct allows transferring SMB packets over RDMA. If unsure, + say N. + +config SMB_SERVER_CHECK_CAP_NET_ADMIN + bool "Enable check network administration capability" + depends on SMB_SERVER + default y + + help + Prevent unprivileged processes to start the ksmbd kernel server. + +config SMB_SERVER_KERBEROS5 + bool "Support for Kerberos 5" + depends on SMB_SERVER + default n diff --git a/fs/smb/server/Makefile b/fs/smb/server/Makefile new file mode 100644 index 0000000000000..7d6337a7dee40 --- /dev/null +++ b/fs/smb/server/Makefile @@ -0,0 +1,20 @@ +# SPDX-License-Identifier: GPL-2.0-or-later +# +# Makefile for Linux SMB3 kernel server +# +obj-$(CONFIG_SMB_SERVER) += ksmbd.o + +ksmbd-y := unicode.o auth.o vfs.o vfs_cache.o server.o ndr.o \ + misc.o oplock.o connection.o ksmbd_work.o crypto_ctx.o \ + mgmt/ksmbd_ida.o mgmt/user_config.o mgmt/share_config.o \ + mgmt/tree_connect.o mgmt/user_session.o smb_common.o \ + transport_tcp.o transport_ipc.o smbacl.o smb2pdu.o \ + smb2ops.o smb2misc.o ksmbd_spnego_negtokeninit.asn1.o \ + ksmbd_spnego_negtokentarg.asn1.o asn1.o + +$(obj)/asn1.o: $(obj)/ksmbd_spnego_negtokeninit.asn1.h $(obj)/ksmbd_spnego_negtokentarg.asn1.h + +$(obj)/ksmbd_spnego_negtokeninit.asn1.o: $(obj)/ksmbd_spnego_negtokeninit.asn1.c $(obj)/ksmbd_spnego_negtokeninit.asn1.h +$(obj)/ksmbd_spnego_negtokentarg.asn1.o: $(obj)/ksmbd_spnego_negtokentarg.asn1.c $(obj)/ksmbd_spnego_negtokentarg.asn1.h + +ksmbd-$(CONFIG_SMB_SERVER_SMBDIRECT) += transport_rdma.o diff --git a/fs/smb/server/asn1.c b/fs/smb/server/asn1.c new file mode 100644 index 0000000000000..c03eba0903682 --- /dev/null +++ b/fs/smb/server/asn1.c @@ -0,0 +1,239 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * The ASB.1/BER parsing code is derived from ip_nat_snmp_basic.c which was in + * turn derived from the gxsnmp package by Gregory McLean & Jochen Friedrich + * + * Copyright (c) 2000 RP Internet (www.rpi.net.au). + */ + +#include +#include +#include +#include +#include +#include + +#include "glob.h" + +#include "asn1.h" +#include "connection.h" +#include "auth.h" +#include "ksmbd_spnego_negtokeninit.asn1.h" +#include "ksmbd_spnego_negtokentarg.asn1.h" + +#define NTLMSSP_OID_LEN 10 + +static char NTLMSSP_OID_STR[NTLMSSP_OID_LEN] = { 0x2b, 0x06, 0x01, 0x04, 0x01, + 0x82, 0x37, 0x02, 0x02, 0x0a }; + +int +ksmbd_decode_negTokenInit(unsigned char *security_blob, int length, + struct ksmbd_conn *conn) +{ + return asn1_ber_decoder(&ksmbd_spnego_negtokeninit_decoder, conn, + security_blob, length); +} + +int +ksmbd_decode_negTokenTarg(unsigned char *security_blob, int length, + struct ksmbd_conn *conn) +{ + return asn1_ber_decoder(&ksmbd_spnego_negtokentarg_decoder, conn, + security_blob, length); +} + +static int compute_asn_hdr_len_bytes(int len) +{ + if (len > 0xFFFFFF) + return 4; + else if (len > 0xFFFF) + return 3; + else if (len > 0xFF) + return 2; + else if (len > 0x7F) + return 1; + else + return 0; +} + +static void encode_asn_tag(char *buf, unsigned int *ofs, char tag, char seq, + int length) +{ + int i; + int index = *ofs; + char hdr_len = compute_asn_hdr_len_bytes(length); + int len = length + 2 + hdr_len; + + /* insert tag */ + buf[index++] = tag; + + if (!hdr_len) { + buf[index++] = len; + } else { + buf[index++] = 0x80 | hdr_len; + for (i = hdr_len - 1; i >= 0; i--) + buf[index++] = (len >> (i * 8)) & 0xFF; + } + + /* insert seq */ + len = len - (index - *ofs); + buf[index++] = seq; + + if (!hdr_len) { + buf[index++] = len; + } else { + buf[index++] = 0x80 | hdr_len; + for (i = hdr_len - 1; i >= 0; i--) + buf[index++] = (len >> (i * 8)) & 0xFF; + } + + *ofs += (index - *ofs); +} + +int build_spnego_ntlmssp_neg_blob(unsigned char **pbuffer, u16 *buflen, + char *ntlm_blob, int ntlm_blob_len) +{ + char *buf; + unsigned int ofs = 0; + int neg_result_len = 4 + compute_asn_hdr_len_bytes(1) * 2 + 1; + int oid_len = 4 + compute_asn_hdr_len_bytes(NTLMSSP_OID_LEN) * 2 + + NTLMSSP_OID_LEN; + int ntlmssp_len = 4 + compute_asn_hdr_len_bytes(ntlm_blob_len) * 2 + + ntlm_blob_len; + int total_len = 4 + compute_asn_hdr_len_bytes(neg_result_len + + oid_len + ntlmssp_len) * 2 + + neg_result_len + oid_len + ntlmssp_len; + + buf = kmalloc(total_len, GFP_KERNEL); + if (!buf) + return -ENOMEM; + + /* insert main gss header */ + encode_asn_tag(buf, &ofs, 0xa1, 0x30, neg_result_len + oid_len + + ntlmssp_len); + + /* insert neg result */ + encode_asn_tag(buf, &ofs, 0xa0, 0x0a, 1); + buf[ofs++] = 1; + + /* insert oid */ + encode_asn_tag(buf, &ofs, 0xa1, 0x06, NTLMSSP_OID_LEN); + memcpy(buf + ofs, NTLMSSP_OID_STR, NTLMSSP_OID_LEN); + ofs += NTLMSSP_OID_LEN; + + /* insert response token - ntlmssp blob */ + encode_asn_tag(buf, &ofs, 0xa2, 0x04, ntlm_blob_len); + memcpy(buf + ofs, ntlm_blob, ntlm_blob_len); + ofs += ntlm_blob_len; + + *pbuffer = buf; + *buflen = total_len; + return 0; +} + +int build_spnego_ntlmssp_auth_blob(unsigned char **pbuffer, u16 *buflen, + int neg_result) +{ + char *buf; + unsigned int ofs = 0; + int neg_result_len = 4 + compute_asn_hdr_len_bytes(1) * 2 + 1; + int total_len = 4 + compute_asn_hdr_len_bytes(neg_result_len) * 2 + + neg_result_len; + + buf = kmalloc(total_len, GFP_KERNEL); + if (!buf) + return -ENOMEM; + + /* insert main gss header */ + encode_asn_tag(buf, &ofs, 0xa1, 0x30, neg_result_len); + + /* insert neg result */ + encode_asn_tag(buf, &ofs, 0xa0, 0x0a, 1); + if (neg_result) + buf[ofs++] = 2; + else + buf[ofs++] = 0; + + *pbuffer = buf; + *buflen = total_len; + return 0; +} + +int ksmbd_gssapi_this_mech(void *context, size_t hdrlen, unsigned char tag, + const void *value, size_t vlen) +{ + enum OID oid; + + oid = look_up_OID(value, vlen); + if (oid != OID_spnego) { + char buf[50]; + + sprint_oid(value, vlen, buf, sizeof(buf)); + ksmbd_debug(AUTH, "Unexpected OID: %s\n", buf); + return -EBADMSG; + } + + return 0; +} + +int ksmbd_neg_token_init_mech_type(void *context, size_t hdrlen, + unsigned char tag, const void *value, + size_t vlen) +{ + struct ksmbd_conn *conn = context; + enum OID oid; + int mech_type; + + oid = look_up_OID(value, vlen); + if (oid == OID_ntlmssp) { + mech_type = KSMBD_AUTH_NTLMSSP; + } else if (oid == OID_mskrb5) { + mech_type = KSMBD_AUTH_MSKRB5; + } else if (oid == OID_krb5) { + mech_type = KSMBD_AUTH_KRB5; + } else if (oid == OID_krb5u2u) { + mech_type = KSMBD_AUTH_KRB5U2U; + } else { + char buf[50]; + + sprint_oid(value, vlen, buf, sizeof(buf)); + ksmbd_debug(AUTH, "Unexpected OID: %s\n", buf); + return -EBADMSG; + } + + conn->auth_mechs |= mech_type; + if (conn->preferred_auth_mech == 0) + conn->preferred_auth_mech = mech_type; + + return 0; +} + +int ksmbd_neg_token_init_mech_token(void *context, size_t hdrlen, + unsigned char tag, const void *value, + size_t vlen) +{ + struct ksmbd_conn *conn = context; + + conn->mechToken = kmalloc(vlen + 1, GFP_KERNEL); + if (!conn->mechToken) + return -ENOMEM; + + memcpy(conn->mechToken, value, vlen); + conn->mechToken[vlen] = '\0'; + return 0; +} + +int ksmbd_neg_token_targ_resp_token(void *context, size_t hdrlen, + unsigned char tag, const void *value, + size_t vlen) +{ + struct ksmbd_conn *conn = context; + + conn->mechToken = kmalloc(vlen + 1, GFP_KERNEL); + if (!conn->mechToken) + return -ENOMEM; + + memcpy(conn->mechToken, value, vlen); + conn->mechToken[vlen] = '\0'; + return 0; +} diff --git a/fs/smb/server/asn1.h b/fs/smb/server/asn1.h new file mode 100644 index 0000000000000..ce105f4ce305a --- /dev/null +++ b/fs/smb/server/asn1.h @@ -0,0 +1,21 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + * The ASB.1/BER parsing code is derived from ip_nat_snmp_basic.c which was in + * turn derived from the gxsnmp package by Gregory McLean & Jochen Friedrich + * + * Copyright (c) 2000 RP Internet (www.rpi.net.au). + * Copyright (C) 2018 Samsung Electronics Co., Ltd. + */ + +#ifndef __ASN1_H__ +#define __ASN1_H__ + +int ksmbd_decode_negTokenInit(unsigned char *security_blob, int length, + struct ksmbd_conn *conn); +int ksmbd_decode_negTokenTarg(unsigned char *security_blob, int length, + struct ksmbd_conn *conn); +int build_spnego_ntlmssp_neg_blob(unsigned char **pbuffer, u16 *buflen, + char *ntlm_blob, int ntlm_blob_len); +int build_spnego_ntlmssp_auth_blob(unsigned char **pbuffer, u16 *buflen, + int neg_result); +#endif /* __ASN1_H__ */ diff --git a/fs/smb/server/auth.c b/fs/smb/server/auth.c new file mode 100644 index 0000000000000..5e5e120edcc22 --- /dev/null +++ b/fs/smb/server/auth.c @@ -0,0 +1,1206 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Copyright (C) 2016 Namjae Jeon + * Copyright (C) 2018 Samsung Electronics Co., Ltd. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "auth.h" +#include "glob.h" + +#include +#include + +#include "server.h" +#include "smb_common.h" +#include "connection.h" +#include "mgmt/user_session.h" +#include "mgmt/user_config.h" +#include "crypto_ctx.h" +#include "transport_ipc.h" +#include "../common/arc4.h" + +/* + * Fixed format data defining GSS header and fixed string + * "not_defined_in_RFC4178@please_ignore". + * So sec blob data in neg phase could be generated statically. + */ +static char NEGOTIATE_GSS_HEADER[AUTH_GSS_LENGTH] = { +#ifdef CONFIG_SMB_SERVER_KERBEROS5 + 0x60, 0x5e, 0x06, 0x06, 0x2b, 0x06, 0x01, 0x05, + 0x05, 0x02, 0xa0, 0x54, 0x30, 0x52, 0xa0, 0x24, + 0x30, 0x22, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, + 0xf7, 0x12, 0x01, 0x02, 0x02, 0x06, 0x09, 0x2a, + 0x86, 0x48, 0x82, 0xf7, 0x12, 0x01, 0x02, 0x02, + 0x06, 0x0a, 0x2b, 0x06, 0x01, 0x04, 0x01, 0x82, + 0x37, 0x02, 0x02, 0x0a, 0xa3, 0x2a, 0x30, 0x28, + 0xa0, 0x26, 0x1b, 0x24, 0x6e, 0x6f, 0x74, 0x5f, + 0x64, 0x65, 0x66, 0x69, 0x6e, 0x65, 0x64, 0x5f, + 0x69, 0x6e, 0x5f, 0x52, 0x46, 0x43, 0x34, 0x31, + 0x37, 0x38, 0x40, 0x70, 0x6c, 0x65, 0x61, 0x73, + 0x65, 0x5f, 0x69, 0x67, 0x6e, 0x6f, 0x72, 0x65 +#else + 0x60, 0x48, 0x06, 0x06, 0x2b, 0x06, 0x01, 0x05, + 0x05, 0x02, 0xa0, 0x3e, 0x30, 0x3c, 0xa0, 0x0e, + 0x30, 0x0c, 0x06, 0x0a, 0x2b, 0x06, 0x01, 0x04, + 0x01, 0x82, 0x37, 0x02, 0x02, 0x0a, 0xa3, 0x2a, + 0x30, 0x28, 0xa0, 0x26, 0x1b, 0x24, 0x6e, 0x6f, + 0x74, 0x5f, 0x64, 0x65, 0x66, 0x69, 0x6e, 0x65, + 0x64, 0x5f, 0x69, 0x6e, 0x5f, 0x52, 0x46, 0x43, + 0x34, 0x31, 0x37, 0x38, 0x40, 0x70, 0x6c, 0x65, + 0x61, 0x73, 0x65, 0x5f, 0x69, 0x67, 0x6e, 0x6f, + 0x72, 0x65 +#endif +}; + +void ksmbd_copy_gss_neg_header(void *buf) +{ + memcpy(buf, NEGOTIATE_GSS_HEADER, AUTH_GSS_LENGTH); +} + +/** + * ksmbd_gen_sess_key() - function to generate session key + * @sess: session of connection + * @hash: source hash value to be used for find session key + * @hmac: source hmac value to be used for finding session key + * + */ +static int ksmbd_gen_sess_key(struct ksmbd_session *sess, char *hash, + char *hmac) +{ + struct ksmbd_crypto_ctx *ctx; + int rc; + + ctx = ksmbd_crypto_ctx_find_hmacmd5(); + if (!ctx) { + ksmbd_debug(AUTH, "could not crypto alloc hmacmd5\n"); + return -ENOMEM; + } + + rc = crypto_shash_setkey(CRYPTO_HMACMD5_TFM(ctx), + hash, + CIFS_HMAC_MD5_HASH_SIZE); + if (rc) { + ksmbd_debug(AUTH, "hmacmd5 set key fail error %d\n", rc); + goto out; + } + + rc = crypto_shash_init(CRYPTO_HMACMD5(ctx)); + if (rc) { + ksmbd_debug(AUTH, "could not init hmacmd5 error %d\n", rc); + goto out; + } + + rc = crypto_shash_update(CRYPTO_HMACMD5(ctx), + hmac, + SMB2_NTLMV2_SESSKEY_SIZE); + if (rc) { + ksmbd_debug(AUTH, "Could not update with response error %d\n", rc); + goto out; + } + + rc = crypto_shash_final(CRYPTO_HMACMD5(ctx), sess->sess_key); + if (rc) { + ksmbd_debug(AUTH, "Could not generate hmacmd5 hash error %d\n", rc); + goto out; + } + +out: + ksmbd_release_crypto_ctx(ctx); + return rc; +} + +static int calc_ntlmv2_hash(struct ksmbd_conn *conn, struct ksmbd_session *sess, + char *ntlmv2_hash, char *dname) +{ + int ret, len, conv_len; + wchar_t *domain = NULL; + __le16 *uniname = NULL; + struct ksmbd_crypto_ctx *ctx; + + ctx = ksmbd_crypto_ctx_find_hmacmd5(); + if (!ctx) { + ksmbd_debug(AUTH, "can't generate ntlmv2 hash\n"); + return -ENOMEM; + } + + ret = crypto_shash_setkey(CRYPTO_HMACMD5_TFM(ctx), + user_passkey(sess->user), + CIFS_ENCPWD_SIZE); + if (ret) { + ksmbd_debug(AUTH, "Could not set NT Hash as a key\n"); + goto out; + } + + ret = crypto_shash_init(CRYPTO_HMACMD5(ctx)); + if (ret) { + ksmbd_debug(AUTH, "could not init hmacmd5\n"); + goto out; + } + + /* convert user_name to unicode */ + len = strlen(user_name(sess->user)); + uniname = kzalloc(2 + UNICODE_LEN(len), GFP_KERNEL); + if (!uniname) { + ret = -ENOMEM; + goto out; + } + + conv_len = smb_strtoUTF16(uniname, user_name(sess->user), len, + conn->local_nls); + if (conv_len < 0 || conv_len > len) { + ret = -EINVAL; + goto out; + } + UniStrupr(uniname); + + ret = crypto_shash_update(CRYPTO_HMACMD5(ctx), + (char *)uniname, + UNICODE_LEN(conv_len)); + if (ret) { + ksmbd_debug(AUTH, "Could not update with user\n"); + goto out; + } + + /* Convert domain name or conn name to unicode and uppercase */ + len = strlen(dname); + domain = kzalloc(2 + UNICODE_LEN(len), GFP_KERNEL); + if (!domain) { + ret = -ENOMEM; + goto out; + } + + conv_len = smb_strtoUTF16((__le16 *)domain, dname, len, + conn->local_nls); + if (conv_len < 0 || conv_len > len) { + ret = -EINVAL; + goto out; + } + + ret = crypto_shash_update(CRYPTO_HMACMD5(ctx), + (char *)domain, + UNICODE_LEN(conv_len)); + if (ret) { + ksmbd_debug(AUTH, "Could not update with domain\n"); + goto out; + } + + ret = crypto_shash_final(CRYPTO_HMACMD5(ctx), ntlmv2_hash); + if (ret) + ksmbd_debug(AUTH, "Could not generate md5 hash\n"); +out: + kfree(uniname); + kfree(domain); + ksmbd_release_crypto_ctx(ctx); + return ret; +} + +/** + * ksmbd_auth_ntlmv2() - NTLMv2 authentication handler + * @sess: session of connection + * @ntlmv2: NTLMv2 challenge response + * @blen: NTLMv2 blob length + * @domain_name: domain name + * + * Return: 0 on success, error number on error + */ +int ksmbd_auth_ntlmv2(struct ksmbd_conn *conn, struct ksmbd_session *sess, + struct ntlmv2_resp *ntlmv2, int blen, char *domain_name, + char *cryptkey) +{ + char ntlmv2_hash[CIFS_ENCPWD_SIZE]; + char ntlmv2_rsp[CIFS_HMAC_MD5_HASH_SIZE]; + struct ksmbd_crypto_ctx *ctx = NULL; + char *construct = NULL; + int rc, len; + + rc = calc_ntlmv2_hash(conn, sess, ntlmv2_hash, domain_name); + if (rc) { + ksmbd_debug(AUTH, "could not get v2 hash rc %d\n", rc); + goto out; + } + + ctx = ksmbd_crypto_ctx_find_hmacmd5(); + if (!ctx) { + ksmbd_debug(AUTH, "could not crypto alloc hmacmd5\n"); + return -ENOMEM; + } + + rc = crypto_shash_setkey(CRYPTO_HMACMD5_TFM(ctx), + ntlmv2_hash, + CIFS_HMAC_MD5_HASH_SIZE); + if (rc) { + ksmbd_debug(AUTH, "Could not set NTLMV2 Hash as a key\n"); + goto out; + } + + rc = crypto_shash_init(CRYPTO_HMACMD5(ctx)); + if (rc) { + ksmbd_debug(AUTH, "Could not init hmacmd5\n"); + goto out; + } + + len = CIFS_CRYPTO_KEY_SIZE + blen; + construct = kzalloc(len, GFP_KERNEL); + if (!construct) { + rc = -ENOMEM; + goto out; + } + + memcpy(construct, cryptkey, CIFS_CRYPTO_KEY_SIZE); + memcpy(construct + CIFS_CRYPTO_KEY_SIZE, &ntlmv2->blob_signature, blen); + + rc = crypto_shash_update(CRYPTO_HMACMD5(ctx), construct, len); + if (rc) { + ksmbd_debug(AUTH, "Could not update with response\n"); + goto out; + } + + rc = crypto_shash_final(CRYPTO_HMACMD5(ctx), ntlmv2_rsp); + if (rc) { + ksmbd_debug(AUTH, "Could not generate md5 hash\n"); + goto out; + } + ksmbd_release_crypto_ctx(ctx); + ctx = NULL; + + rc = ksmbd_gen_sess_key(sess, ntlmv2_hash, ntlmv2_rsp); + if (rc) { + ksmbd_debug(AUTH, "Could not generate sess key\n"); + goto out; + } + + if (memcmp(ntlmv2->ntlmv2_hash, ntlmv2_rsp, CIFS_HMAC_MD5_HASH_SIZE) != 0) + rc = -EINVAL; +out: + if (ctx) + ksmbd_release_crypto_ctx(ctx); + kfree(construct); + return rc; +} + +/** + * ksmbd_decode_ntlmssp_auth_blob() - helper function to construct + * authenticate blob + * @authblob: authenticate blob source pointer + * @usr: user details + * @sess: session of connection + * + * Return: 0 on success, error number on error + */ +int ksmbd_decode_ntlmssp_auth_blob(struct authenticate_message *authblob, + int blob_len, struct ksmbd_conn *conn, + struct ksmbd_session *sess) +{ + char *domain_name; + unsigned int nt_off, dn_off; + unsigned short nt_len, dn_len; + int ret; + + if (blob_len < sizeof(struct authenticate_message)) { + ksmbd_debug(AUTH, "negotiate blob len %d too small\n", + blob_len); + return -EINVAL; + } + + if (memcmp(authblob->Signature, "NTLMSSP", 8)) { + ksmbd_debug(AUTH, "blob signature incorrect %s\n", + authblob->Signature); + return -EINVAL; + } + + nt_off = le32_to_cpu(authblob->NtChallengeResponse.BufferOffset); + nt_len = le16_to_cpu(authblob->NtChallengeResponse.Length); + dn_off = le32_to_cpu(authblob->DomainName.BufferOffset); + dn_len = le16_to_cpu(authblob->DomainName.Length); + + if (blob_len < (u64)dn_off + dn_len || blob_len < (u64)nt_off + nt_len || + nt_len < CIFS_ENCPWD_SIZE) + return -EINVAL; + + /* TODO : use domain name that imported from configuration file */ + domain_name = smb_strndup_from_utf16((const char *)authblob + dn_off, + dn_len, true, conn->local_nls); + if (IS_ERR(domain_name)) + return PTR_ERR(domain_name); + + /* process NTLMv2 authentication */ + ksmbd_debug(AUTH, "decode_ntlmssp_authenticate_blob dname%s\n", + domain_name); + ret = ksmbd_auth_ntlmv2(conn, sess, + (struct ntlmv2_resp *)((char *)authblob + nt_off), + nt_len - CIFS_ENCPWD_SIZE, + domain_name, conn->ntlmssp.cryptkey); + kfree(domain_name); + + /* The recovered secondary session key */ + if (conn->ntlmssp.client_flags & NTLMSSP_NEGOTIATE_KEY_XCH) { + struct arc4_ctx *ctx_arc4; + unsigned int sess_key_off, sess_key_len; + + sess_key_off = le32_to_cpu(authblob->SessionKey.BufferOffset); + sess_key_len = le16_to_cpu(authblob->SessionKey.Length); + + if (blob_len < (u64)sess_key_off + sess_key_len) + return -EINVAL; + + ctx_arc4 = kmalloc(sizeof(*ctx_arc4), GFP_KERNEL); + if (!ctx_arc4) + return -ENOMEM; + + cifs_arc4_setkey(ctx_arc4, sess->sess_key, + SMB2_NTLMV2_SESSKEY_SIZE); + cifs_arc4_crypt(ctx_arc4, sess->sess_key, + (char *)authblob + sess_key_off, sess_key_len); + kfree_sensitive(ctx_arc4); + } + + return ret; +} + +/** + * ksmbd_decode_ntlmssp_neg_blob() - helper function to construct + * negotiate blob + * @negblob: negotiate blob source pointer + * @rsp: response header pointer to be updated + * @sess: session of connection + * + */ +int ksmbd_decode_ntlmssp_neg_blob(struct negotiate_message *negblob, + int blob_len, struct ksmbd_conn *conn) +{ + if (blob_len < sizeof(struct negotiate_message)) { + ksmbd_debug(AUTH, "negotiate blob len %d too small\n", + blob_len); + return -EINVAL; + } + + if (memcmp(negblob->Signature, "NTLMSSP", 8)) { + ksmbd_debug(AUTH, "blob signature incorrect %s\n", + negblob->Signature); + return -EINVAL; + } + + conn->ntlmssp.client_flags = le32_to_cpu(negblob->NegotiateFlags); + return 0; +} + +/** + * ksmbd_build_ntlmssp_challenge_blob() - helper function to construct + * challenge blob + * @chgblob: challenge blob source pointer to initialize + * @rsp: response header pointer to be updated + * @sess: session of connection + * + */ +unsigned int +ksmbd_build_ntlmssp_challenge_blob(struct challenge_message *chgblob, + struct ksmbd_conn *conn) +{ + struct target_info *tinfo; + wchar_t *name; + __u8 *target_name; + unsigned int flags, blob_off, blob_len, type, target_info_len = 0; + int len, uni_len, conv_len; + int cflags = conn->ntlmssp.client_flags; + + memcpy(chgblob->Signature, NTLMSSP_SIGNATURE, 8); + chgblob->MessageType = NtLmChallenge; + + flags = NTLMSSP_NEGOTIATE_UNICODE | + NTLMSSP_NEGOTIATE_NTLM | NTLMSSP_TARGET_TYPE_SERVER | + NTLMSSP_NEGOTIATE_TARGET_INFO; + + if (cflags & NTLMSSP_NEGOTIATE_SIGN) { + flags |= NTLMSSP_NEGOTIATE_SIGN; + flags |= cflags & (NTLMSSP_NEGOTIATE_128 | + NTLMSSP_NEGOTIATE_56); + } + + if (cflags & NTLMSSP_NEGOTIATE_SEAL && smb3_encryption_negotiated(conn)) + flags |= NTLMSSP_NEGOTIATE_SEAL; + + if (cflags & NTLMSSP_NEGOTIATE_ALWAYS_SIGN) + flags |= NTLMSSP_NEGOTIATE_ALWAYS_SIGN; + + if (cflags & NTLMSSP_REQUEST_TARGET) + flags |= NTLMSSP_REQUEST_TARGET; + + if (conn->use_spnego && + (cflags & NTLMSSP_NEGOTIATE_EXTENDED_SEC)) + flags |= NTLMSSP_NEGOTIATE_EXTENDED_SEC; + + if (cflags & NTLMSSP_NEGOTIATE_KEY_XCH) + flags |= NTLMSSP_NEGOTIATE_KEY_XCH; + + chgblob->NegotiateFlags = cpu_to_le32(flags); + len = strlen(ksmbd_netbios_name()); + name = kmalloc(2 + UNICODE_LEN(len), GFP_KERNEL); + if (!name) + return -ENOMEM; + + conv_len = smb_strtoUTF16((__le16 *)name, ksmbd_netbios_name(), len, + conn->local_nls); + if (conv_len < 0 || conv_len > len) { + kfree(name); + return -EINVAL; + } + + uni_len = UNICODE_LEN(conv_len); + + blob_off = sizeof(struct challenge_message); + blob_len = blob_off + uni_len; + + chgblob->TargetName.Length = cpu_to_le16(uni_len); + chgblob->TargetName.MaximumLength = cpu_to_le16(uni_len); + chgblob->TargetName.BufferOffset = cpu_to_le32(blob_off); + + /* Initialize random conn challenge */ + get_random_bytes(conn->ntlmssp.cryptkey, sizeof(__u64)); + memcpy(chgblob->Challenge, conn->ntlmssp.cryptkey, + CIFS_CRYPTO_KEY_SIZE); + + /* Add Target Information to security buffer */ + chgblob->TargetInfoArray.BufferOffset = cpu_to_le32(blob_len); + + target_name = (__u8 *)chgblob + blob_off; + memcpy(target_name, name, uni_len); + tinfo = (struct target_info *)(target_name + uni_len); + + chgblob->TargetInfoArray.Length = 0; + /* Add target info list for NetBIOS/DNS settings */ + for (type = NTLMSSP_AV_NB_COMPUTER_NAME; + type <= NTLMSSP_AV_DNS_DOMAIN_NAME; type++) { + tinfo->Type = cpu_to_le16(type); + tinfo->Length = cpu_to_le16(uni_len); + memcpy(tinfo->Content, name, uni_len); + tinfo = (struct target_info *)((char *)tinfo + 4 + uni_len); + target_info_len += 4 + uni_len; + } + + /* Add terminator subblock */ + tinfo->Type = 0; + tinfo->Length = 0; + target_info_len += 4; + + chgblob->TargetInfoArray.Length = cpu_to_le16(target_info_len); + chgblob->TargetInfoArray.MaximumLength = cpu_to_le16(target_info_len); + blob_len += target_info_len; + kfree(name); + ksmbd_debug(AUTH, "NTLMSSP SecurityBufferLength %d\n", blob_len); + return blob_len; +} + +#ifdef CONFIG_SMB_SERVER_KERBEROS5 +int ksmbd_krb5_authenticate(struct ksmbd_session *sess, char *in_blob, + int in_len, char *out_blob, int *out_len) +{ + struct ksmbd_spnego_authen_response *resp; + struct ksmbd_user *user = NULL; + int retval; + + resp = ksmbd_ipc_spnego_authen_request(in_blob, in_len); + if (!resp) { + ksmbd_debug(AUTH, "SPNEGO_AUTHEN_REQUEST failure\n"); + return -EINVAL; + } + + if (!(resp->login_response.status & KSMBD_USER_FLAG_OK)) { + ksmbd_debug(AUTH, "krb5 authentication failure\n"); + retval = -EPERM; + goto out; + } + + if (*out_len <= resp->spnego_blob_len) { + ksmbd_debug(AUTH, "buf len %d, but blob len %d\n", + *out_len, resp->spnego_blob_len); + retval = -EINVAL; + goto out; + } + + if (resp->session_key_len > sizeof(sess->sess_key)) { + ksmbd_debug(AUTH, "session key is too long\n"); + retval = -EINVAL; + goto out; + } + + user = ksmbd_alloc_user(&resp->login_response); + if (!user) { + ksmbd_debug(AUTH, "login failure\n"); + retval = -ENOMEM; + goto out; + } + sess->user = user; + + memcpy(sess->sess_key, resp->payload, resp->session_key_len); + memcpy(out_blob, resp->payload + resp->session_key_len, + resp->spnego_blob_len); + *out_len = resp->spnego_blob_len; + retval = 0; +out: + kvfree(resp); + return retval; +} +#else +int ksmbd_krb5_authenticate(struct ksmbd_session *sess, char *in_blob, + int in_len, char *out_blob, int *out_len) +{ + return -EOPNOTSUPP; +} +#endif + +/** + * ksmbd_sign_smb2_pdu() - function to generate packet signing + * @conn: connection + * @key: signing key + * @iov: buffer iov array + * @n_vec: number of iovecs + * @sig: signature value generated for client request packet + * + */ +int ksmbd_sign_smb2_pdu(struct ksmbd_conn *conn, char *key, struct kvec *iov, + int n_vec, char *sig) +{ + struct ksmbd_crypto_ctx *ctx; + int rc, i; + + ctx = ksmbd_crypto_ctx_find_hmacsha256(); + if (!ctx) { + ksmbd_debug(AUTH, "could not crypto alloc hmacmd5\n"); + return -ENOMEM; + } + + rc = crypto_shash_setkey(CRYPTO_HMACSHA256_TFM(ctx), + key, + SMB2_NTLMV2_SESSKEY_SIZE); + if (rc) + goto out; + + rc = crypto_shash_init(CRYPTO_HMACSHA256(ctx)); + if (rc) { + ksmbd_debug(AUTH, "hmacsha256 init error %d\n", rc); + goto out; + } + + for (i = 0; i < n_vec; i++) { + rc = crypto_shash_update(CRYPTO_HMACSHA256(ctx), + iov[i].iov_base, + iov[i].iov_len); + if (rc) { + ksmbd_debug(AUTH, "hmacsha256 update error %d\n", rc); + goto out; + } + } + + rc = crypto_shash_final(CRYPTO_HMACSHA256(ctx), sig); + if (rc) + ksmbd_debug(AUTH, "hmacsha256 generation error %d\n", rc); +out: + ksmbd_release_crypto_ctx(ctx); + return rc; +} + +/** + * ksmbd_sign_smb3_pdu() - function to generate packet signing + * @conn: connection + * @key: signing key + * @iov: buffer iov array + * @n_vec: number of iovecs + * @sig: signature value generated for client request packet + * + */ +int ksmbd_sign_smb3_pdu(struct ksmbd_conn *conn, char *key, struct kvec *iov, + int n_vec, char *sig) +{ + struct ksmbd_crypto_ctx *ctx; + int rc, i; + + ctx = ksmbd_crypto_ctx_find_cmacaes(); + if (!ctx) { + ksmbd_debug(AUTH, "could not crypto alloc cmac\n"); + return -ENOMEM; + } + + rc = crypto_shash_setkey(CRYPTO_CMACAES_TFM(ctx), + key, + SMB2_CMACAES_SIZE); + if (rc) + goto out; + + rc = crypto_shash_init(CRYPTO_CMACAES(ctx)); + if (rc) { + ksmbd_debug(AUTH, "cmaces init error %d\n", rc); + goto out; + } + + for (i = 0; i < n_vec; i++) { + rc = crypto_shash_update(CRYPTO_CMACAES(ctx), + iov[i].iov_base, + iov[i].iov_len); + if (rc) { + ksmbd_debug(AUTH, "cmaces update error %d\n", rc); + goto out; + } + } + + rc = crypto_shash_final(CRYPTO_CMACAES(ctx), sig); + if (rc) + ksmbd_debug(AUTH, "cmaces generation error %d\n", rc); +out: + ksmbd_release_crypto_ctx(ctx); + return rc; +} + +struct derivation { + struct kvec label; + struct kvec context; + bool binding; +}; + +static int generate_key(struct ksmbd_conn *conn, struct ksmbd_session *sess, + struct kvec label, struct kvec context, __u8 *key, + unsigned int key_size) +{ + unsigned char zero = 0x0; + __u8 i[4] = {0, 0, 0, 1}; + __u8 L128[4] = {0, 0, 0, 128}; + __u8 L256[4] = {0, 0, 1, 0}; + int rc; + unsigned char prfhash[SMB2_HMACSHA256_SIZE]; + unsigned char *hashptr = prfhash; + struct ksmbd_crypto_ctx *ctx; + + memset(prfhash, 0x0, SMB2_HMACSHA256_SIZE); + memset(key, 0x0, key_size); + + ctx = ksmbd_crypto_ctx_find_hmacsha256(); + if (!ctx) { + ksmbd_debug(AUTH, "could not crypto alloc hmacmd5\n"); + return -ENOMEM; + } + + rc = crypto_shash_setkey(CRYPTO_HMACSHA256_TFM(ctx), + sess->sess_key, + SMB2_NTLMV2_SESSKEY_SIZE); + if (rc) + goto smb3signkey_ret; + + rc = crypto_shash_init(CRYPTO_HMACSHA256(ctx)); + if (rc) { + ksmbd_debug(AUTH, "hmacsha256 init error %d\n", rc); + goto smb3signkey_ret; + } + + rc = crypto_shash_update(CRYPTO_HMACSHA256(ctx), i, 4); + if (rc) { + ksmbd_debug(AUTH, "could not update with n\n"); + goto smb3signkey_ret; + } + + rc = crypto_shash_update(CRYPTO_HMACSHA256(ctx), + label.iov_base, + label.iov_len); + if (rc) { + ksmbd_debug(AUTH, "could not update with label\n"); + goto smb3signkey_ret; + } + + rc = crypto_shash_update(CRYPTO_HMACSHA256(ctx), &zero, 1); + if (rc) { + ksmbd_debug(AUTH, "could not update with zero\n"); + goto smb3signkey_ret; + } + + rc = crypto_shash_update(CRYPTO_HMACSHA256(ctx), + context.iov_base, + context.iov_len); + if (rc) { + ksmbd_debug(AUTH, "could not update with context\n"); + goto smb3signkey_ret; + } + + if (key_size == SMB3_ENC_DEC_KEY_SIZE && + (conn->cipher_type == SMB2_ENCRYPTION_AES256_CCM || + conn->cipher_type == SMB2_ENCRYPTION_AES256_GCM)) + rc = crypto_shash_update(CRYPTO_HMACSHA256(ctx), L256, 4); + else + rc = crypto_shash_update(CRYPTO_HMACSHA256(ctx), L128, 4); + if (rc) { + ksmbd_debug(AUTH, "could not update with L\n"); + goto smb3signkey_ret; + } + + rc = crypto_shash_final(CRYPTO_HMACSHA256(ctx), hashptr); + if (rc) { + ksmbd_debug(AUTH, "Could not generate hmacmd5 hash error %d\n", + rc); + goto smb3signkey_ret; + } + + memcpy(key, hashptr, key_size); + +smb3signkey_ret: + ksmbd_release_crypto_ctx(ctx); + return rc; +} + +static int generate_smb3signingkey(struct ksmbd_session *sess, + struct ksmbd_conn *conn, + const struct derivation *signing) +{ + int rc; + struct channel *chann; + char *key; + + chann = lookup_chann_list(sess, conn); + if (!chann) + return 0; + + if (conn->dialect >= SMB30_PROT_ID && signing->binding) + key = chann->smb3signingkey; + else + key = sess->smb3signingkey; + + rc = generate_key(conn, sess, signing->label, signing->context, key, + SMB3_SIGN_KEY_SIZE); + if (rc) + return rc; + + if (!(conn->dialect >= SMB30_PROT_ID && signing->binding)) + memcpy(chann->smb3signingkey, key, SMB3_SIGN_KEY_SIZE); + + ksmbd_debug(AUTH, "dumping generated AES signing keys\n"); + ksmbd_debug(AUTH, "Session Id %llu\n", sess->id); + ksmbd_debug(AUTH, "Session Key %*ph\n", + SMB2_NTLMV2_SESSKEY_SIZE, sess->sess_key); + ksmbd_debug(AUTH, "Signing Key %*ph\n", + SMB3_SIGN_KEY_SIZE, key); + return 0; +} + +int ksmbd_gen_smb30_signingkey(struct ksmbd_session *sess, + struct ksmbd_conn *conn) +{ + struct derivation d; + + d.label.iov_base = "SMB2AESCMAC"; + d.label.iov_len = 12; + d.context.iov_base = "SmbSign"; + d.context.iov_len = 8; + d.binding = conn->binding; + + return generate_smb3signingkey(sess, conn, &d); +} + +int ksmbd_gen_smb311_signingkey(struct ksmbd_session *sess, + struct ksmbd_conn *conn) +{ + struct derivation d; + + d.label.iov_base = "SMBSigningKey"; + d.label.iov_len = 14; + if (conn->binding) { + struct preauth_session *preauth_sess; + + preauth_sess = ksmbd_preauth_session_lookup(conn, sess->id); + if (!preauth_sess) + return -ENOENT; + d.context.iov_base = preauth_sess->Preauth_HashValue; + } else { + d.context.iov_base = sess->Preauth_HashValue; + } + d.context.iov_len = 64; + d.binding = conn->binding; + + return generate_smb3signingkey(sess, conn, &d); +} + +struct derivation_twin { + struct derivation encryption; + struct derivation decryption; +}; + +static int generate_smb3encryptionkey(struct ksmbd_conn *conn, + struct ksmbd_session *sess, + const struct derivation_twin *ptwin) +{ + int rc; + + rc = generate_key(conn, sess, ptwin->encryption.label, + ptwin->encryption.context, sess->smb3encryptionkey, + SMB3_ENC_DEC_KEY_SIZE); + if (rc) + return rc; + + rc = generate_key(conn, sess, ptwin->decryption.label, + ptwin->decryption.context, + sess->smb3decryptionkey, SMB3_ENC_DEC_KEY_SIZE); + if (rc) + return rc; + + ksmbd_debug(AUTH, "dumping generated AES encryption keys\n"); + ksmbd_debug(AUTH, "Cipher type %d\n", conn->cipher_type); + ksmbd_debug(AUTH, "Session Id %llu\n", sess->id); + ksmbd_debug(AUTH, "Session Key %*ph\n", + SMB2_NTLMV2_SESSKEY_SIZE, sess->sess_key); + if (conn->cipher_type == SMB2_ENCRYPTION_AES256_CCM || + conn->cipher_type == SMB2_ENCRYPTION_AES256_GCM) { + ksmbd_debug(AUTH, "ServerIn Key %*ph\n", + SMB3_GCM256_CRYPTKEY_SIZE, sess->smb3encryptionkey); + ksmbd_debug(AUTH, "ServerOut Key %*ph\n", + SMB3_GCM256_CRYPTKEY_SIZE, sess->smb3decryptionkey); + } else { + ksmbd_debug(AUTH, "ServerIn Key %*ph\n", + SMB3_GCM128_CRYPTKEY_SIZE, sess->smb3encryptionkey); + ksmbd_debug(AUTH, "ServerOut Key %*ph\n", + SMB3_GCM128_CRYPTKEY_SIZE, sess->smb3decryptionkey); + } + return 0; +} + +int ksmbd_gen_smb30_encryptionkey(struct ksmbd_conn *conn, + struct ksmbd_session *sess) +{ + struct derivation_twin twin; + struct derivation *d; + + d = &twin.encryption; + d->label.iov_base = "SMB2AESCCM"; + d->label.iov_len = 11; + d->context.iov_base = "ServerOut"; + d->context.iov_len = 10; + + d = &twin.decryption; + d->label.iov_base = "SMB2AESCCM"; + d->label.iov_len = 11; + d->context.iov_base = "ServerIn "; + d->context.iov_len = 10; + + return generate_smb3encryptionkey(conn, sess, &twin); +} + +int ksmbd_gen_smb311_encryptionkey(struct ksmbd_conn *conn, + struct ksmbd_session *sess) +{ + struct derivation_twin twin; + struct derivation *d; + + d = &twin.encryption; + d->label.iov_base = "SMBS2CCipherKey"; + d->label.iov_len = 16; + d->context.iov_base = sess->Preauth_HashValue; + d->context.iov_len = 64; + + d = &twin.decryption; + d->label.iov_base = "SMBC2SCipherKey"; + d->label.iov_len = 16; + d->context.iov_base = sess->Preauth_HashValue; + d->context.iov_len = 64; + + return generate_smb3encryptionkey(conn, sess, &twin); +} + +int ksmbd_gen_preauth_integrity_hash(struct ksmbd_conn *conn, char *buf, + __u8 *pi_hash) +{ + int rc; + struct smb2_hdr *rcv_hdr = smb2_get_msg(buf); + char *all_bytes_msg = (char *)&rcv_hdr->ProtocolId; + int msg_size = get_rfc1002_len(buf); + struct ksmbd_crypto_ctx *ctx = NULL; + + if (conn->preauth_info->Preauth_HashId != + SMB2_PREAUTH_INTEGRITY_SHA512) + return -EINVAL; + + ctx = ksmbd_crypto_ctx_find_sha512(); + if (!ctx) { + ksmbd_debug(AUTH, "could not alloc sha512\n"); + return -ENOMEM; + } + + rc = crypto_shash_init(CRYPTO_SHA512(ctx)); + if (rc) { + ksmbd_debug(AUTH, "could not init shashn"); + goto out; + } + + rc = crypto_shash_update(CRYPTO_SHA512(ctx), pi_hash, 64); + if (rc) { + ksmbd_debug(AUTH, "could not update with n\n"); + goto out; + } + + rc = crypto_shash_update(CRYPTO_SHA512(ctx), all_bytes_msg, msg_size); + if (rc) { + ksmbd_debug(AUTH, "could not update with n\n"); + goto out; + } + + rc = crypto_shash_final(CRYPTO_SHA512(ctx), pi_hash); + if (rc) { + ksmbd_debug(AUTH, "Could not generate hash err : %d\n", rc); + goto out; + } +out: + ksmbd_release_crypto_ctx(ctx); + return rc; +} + +int ksmbd_gen_sd_hash(struct ksmbd_conn *conn, char *sd_buf, int len, + __u8 *pi_hash) +{ + int rc; + struct ksmbd_crypto_ctx *ctx = NULL; + + ctx = ksmbd_crypto_ctx_find_sha256(); + if (!ctx) { + ksmbd_debug(AUTH, "could not alloc sha256\n"); + return -ENOMEM; + } + + rc = crypto_shash_init(CRYPTO_SHA256(ctx)); + if (rc) { + ksmbd_debug(AUTH, "could not init shashn"); + goto out; + } + + rc = crypto_shash_update(CRYPTO_SHA256(ctx), sd_buf, len); + if (rc) { + ksmbd_debug(AUTH, "could not update with n\n"); + goto out; + } + + rc = crypto_shash_final(CRYPTO_SHA256(ctx), pi_hash); + if (rc) { + ksmbd_debug(AUTH, "Could not generate hash err : %d\n", rc); + goto out; + } +out: + ksmbd_release_crypto_ctx(ctx); + return rc; +} + +static int ksmbd_get_encryption_key(struct ksmbd_work *work, __u64 ses_id, + int enc, u8 *key) +{ + struct ksmbd_session *sess; + u8 *ses_enc_key; + + if (enc) + sess = work->sess; + else + sess = ksmbd_session_lookup_all(work->conn, ses_id); + if (!sess) + return -EINVAL; + + ses_enc_key = enc ? sess->smb3encryptionkey : + sess->smb3decryptionkey; + memcpy(key, ses_enc_key, SMB3_ENC_DEC_KEY_SIZE); + + return 0; +} + +static inline void smb2_sg_set_buf(struct scatterlist *sg, const void *buf, + unsigned int buflen) +{ + void *addr; + + if (is_vmalloc_addr(buf)) + addr = vmalloc_to_page(buf); + else + addr = virt_to_page(buf); + sg_set_page(sg, addr, buflen, offset_in_page(buf)); +} + +static struct scatterlist *ksmbd_init_sg(struct kvec *iov, unsigned int nvec, + u8 *sign) +{ + struct scatterlist *sg; + unsigned int assoc_data_len = sizeof(struct smb2_transform_hdr) - 20; + int i, nr_entries[3] = {0}, total_entries = 0, sg_idx = 0; + + if (!nvec) + return NULL; + + for (i = 0; i < nvec - 1; i++) { + unsigned long kaddr = (unsigned long)iov[i + 1].iov_base; + + if (is_vmalloc_addr(iov[i + 1].iov_base)) { + nr_entries[i] = ((kaddr + iov[i + 1].iov_len + + PAGE_SIZE - 1) >> PAGE_SHIFT) - + (kaddr >> PAGE_SHIFT); + } else { + nr_entries[i]++; + } + total_entries += nr_entries[i]; + } + + /* Add two entries for transform header and signature */ + total_entries += 2; + + sg = kmalloc_array(total_entries, sizeof(struct scatterlist), GFP_KERNEL); + if (!sg) + return NULL; + + sg_init_table(sg, total_entries); + smb2_sg_set_buf(&sg[sg_idx++], iov[0].iov_base + 24, assoc_data_len); + for (i = 0; i < nvec - 1; i++) { + void *data = iov[i + 1].iov_base; + int len = iov[i + 1].iov_len; + + if (is_vmalloc_addr(data)) { + int j, offset = offset_in_page(data); + + for (j = 0; j < nr_entries[i]; j++) { + unsigned int bytes = PAGE_SIZE - offset; + + if (!len) + break; + + if (bytes > len) + bytes = len; + + sg_set_page(&sg[sg_idx++], + vmalloc_to_page(data), bytes, + offset_in_page(data)); + + data += bytes; + len -= bytes; + offset = 0; + } + } else { + sg_set_page(&sg[sg_idx++], virt_to_page(data), len, + offset_in_page(data)); + } + } + smb2_sg_set_buf(&sg[sg_idx], sign, SMB2_SIGNATURE_SIZE); + return sg; +} + +int ksmbd_crypt_message(struct ksmbd_work *work, struct kvec *iov, + unsigned int nvec, int enc) +{ + struct ksmbd_conn *conn = work->conn; + struct smb2_transform_hdr *tr_hdr = smb2_get_msg(iov[0].iov_base); + unsigned int assoc_data_len = sizeof(struct smb2_transform_hdr) - 20; + int rc; + struct scatterlist *sg; + u8 sign[SMB2_SIGNATURE_SIZE] = {}; + u8 key[SMB3_ENC_DEC_KEY_SIZE]; + struct aead_request *req; + char *iv; + unsigned int iv_len; + struct crypto_aead *tfm; + unsigned int crypt_len = le32_to_cpu(tr_hdr->OriginalMessageSize); + struct ksmbd_crypto_ctx *ctx; + + rc = ksmbd_get_encryption_key(work, + le64_to_cpu(tr_hdr->SessionId), + enc, + key); + if (rc) { + pr_err("Could not get %scryption key\n", enc ? "en" : "de"); + return rc; + } + + if (conn->cipher_type == SMB2_ENCRYPTION_AES128_GCM || + conn->cipher_type == SMB2_ENCRYPTION_AES256_GCM) + ctx = ksmbd_crypto_ctx_find_gcm(); + else + ctx = ksmbd_crypto_ctx_find_ccm(); + if (!ctx) { + pr_err("crypto alloc failed\n"); + return -ENOMEM; + } + + if (conn->cipher_type == SMB2_ENCRYPTION_AES128_GCM || + conn->cipher_type == SMB2_ENCRYPTION_AES256_GCM) + tfm = CRYPTO_GCM(ctx); + else + tfm = CRYPTO_CCM(ctx); + + if (conn->cipher_type == SMB2_ENCRYPTION_AES256_CCM || + conn->cipher_type == SMB2_ENCRYPTION_AES256_GCM) + rc = crypto_aead_setkey(tfm, key, SMB3_GCM256_CRYPTKEY_SIZE); + else + rc = crypto_aead_setkey(tfm, key, SMB3_GCM128_CRYPTKEY_SIZE); + if (rc) { + pr_err("Failed to set aead key %d\n", rc); + goto free_ctx; + } + + rc = crypto_aead_setauthsize(tfm, SMB2_SIGNATURE_SIZE); + if (rc) { + pr_err("Failed to set authsize %d\n", rc); + goto free_ctx; + } + + req = aead_request_alloc(tfm, GFP_KERNEL); + if (!req) { + rc = -ENOMEM; + goto free_ctx; + } + + if (!enc) { + memcpy(sign, &tr_hdr->Signature, SMB2_SIGNATURE_SIZE); + crypt_len += SMB2_SIGNATURE_SIZE; + } + + sg = ksmbd_init_sg(iov, nvec, sign); + if (!sg) { + pr_err("Failed to init sg\n"); + rc = -ENOMEM; + goto free_req; + } + + iv_len = crypto_aead_ivsize(tfm); + iv = kzalloc(iv_len, GFP_KERNEL); + if (!iv) { + rc = -ENOMEM; + goto free_sg; + } + + if (conn->cipher_type == SMB2_ENCRYPTION_AES128_GCM || + conn->cipher_type == SMB2_ENCRYPTION_AES256_GCM) { + memcpy(iv, (char *)tr_hdr->Nonce, SMB3_AES_GCM_NONCE); + } else { + iv[0] = 3; + memcpy(iv + 1, (char *)tr_hdr->Nonce, SMB3_AES_CCM_NONCE); + } + + aead_request_set_crypt(req, sg, sg, crypt_len, iv); + aead_request_set_ad(req, assoc_data_len); + aead_request_set_callback(req, CRYPTO_TFM_REQ_MAY_SLEEP, NULL, NULL); + + if (enc) + rc = crypto_aead_encrypt(req); + else + rc = crypto_aead_decrypt(req); + if (rc) + goto free_iv; + + if (enc) + memcpy(&tr_hdr->Signature, sign, SMB2_SIGNATURE_SIZE); + +free_iv: + kfree(iv); +free_sg: + kfree(sg); +free_req: + kfree(req); +free_ctx: + ksmbd_release_crypto_ctx(ctx); + return rc; +} diff --git a/fs/smb/server/auth.h b/fs/smb/server/auth.h new file mode 100644 index 0000000000000..362b6159a6cff --- /dev/null +++ b/fs/smb/server/auth.h @@ -0,0 +1,71 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + * Copyright (C) 2018 Samsung Electronics Co., Ltd. + */ + +#ifndef __AUTH_H__ +#define __AUTH_H__ + +#include "ntlmssp.h" + +#ifdef CONFIG_SMB_SERVER_KERBEROS5 +#define AUTH_GSS_LENGTH 96 +#define AUTH_GSS_PADDING 0 +#else +#define AUTH_GSS_LENGTH 74 +#define AUTH_GSS_PADDING 6 +#endif + +#define CIFS_HMAC_MD5_HASH_SIZE (16) +#define CIFS_NTHASH_SIZE (16) + +/* + * Size of the ntlm client response + */ +#define CIFS_AUTH_RESP_SIZE 24 +#define CIFS_SMB1_SIGNATURE_SIZE 8 +#define CIFS_SMB1_SESSKEY_SIZE 16 + +#define KSMBD_AUTH_NTLMSSP 0x0001 +#define KSMBD_AUTH_KRB5 0x0002 +#define KSMBD_AUTH_MSKRB5 0x0004 +#define KSMBD_AUTH_KRB5U2U 0x0008 + +struct ksmbd_session; +struct ksmbd_conn; +struct ksmbd_work; +struct kvec; + +int ksmbd_crypt_message(struct ksmbd_work *work, struct kvec *iov, + unsigned int nvec, int enc); +void ksmbd_copy_gss_neg_header(void *buf); +int ksmbd_auth_ntlmv2(struct ksmbd_conn *conn, struct ksmbd_session *sess, + struct ntlmv2_resp *ntlmv2, int blen, char *domain_name, + char *cryptkey); +int ksmbd_decode_ntlmssp_auth_blob(struct authenticate_message *authblob, + int blob_len, struct ksmbd_conn *conn, + struct ksmbd_session *sess); +int ksmbd_decode_ntlmssp_neg_blob(struct negotiate_message *negblob, + int blob_len, struct ksmbd_conn *conn); +unsigned int +ksmbd_build_ntlmssp_challenge_blob(struct challenge_message *chgblob, + struct ksmbd_conn *conn); +int ksmbd_krb5_authenticate(struct ksmbd_session *sess, char *in_blob, + int in_len, char *out_blob, int *out_len); +int ksmbd_sign_smb2_pdu(struct ksmbd_conn *conn, char *key, struct kvec *iov, + int n_vec, char *sig); +int ksmbd_sign_smb3_pdu(struct ksmbd_conn *conn, char *key, struct kvec *iov, + int n_vec, char *sig); +int ksmbd_gen_smb30_signingkey(struct ksmbd_session *sess, + struct ksmbd_conn *conn); +int ksmbd_gen_smb311_signingkey(struct ksmbd_session *sess, + struct ksmbd_conn *conn); +int ksmbd_gen_smb30_encryptionkey(struct ksmbd_conn *conn, + struct ksmbd_session *sess); +int ksmbd_gen_smb311_encryptionkey(struct ksmbd_conn *conn, + struct ksmbd_session *sess); +int ksmbd_gen_preauth_integrity_hash(struct ksmbd_conn *conn, char *buf, + __u8 *pi_hash); +int ksmbd_gen_sd_hash(struct ksmbd_conn *conn, char *sd_buf, int len, + __u8 *pi_hash); +#endif diff --git a/fs/smb/server/connection.c b/fs/smb/server/connection.c new file mode 100644 index 0000000000000..e1d2be19cddfa --- /dev/null +++ b/fs/smb/server/connection.c @@ -0,0 +1,485 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Copyright (C) 2016 Namjae Jeon + * Copyright (C) 2018 Samsung Electronics Co., Ltd. + */ + +#include +#include +#include + +#include "server.h" +#include "smb_common.h" +#include "mgmt/ksmbd_ida.h" +#include "connection.h" +#include "transport_tcp.h" +#include "transport_rdma.h" + +static DEFINE_MUTEX(init_lock); + +static struct ksmbd_conn_ops default_conn_ops; + +LIST_HEAD(conn_list); +DECLARE_RWSEM(conn_list_lock); + +/** + * ksmbd_conn_free() - free resources of the connection instance + * + * @conn: connection instance to be cleand up + * + * During the thread termination, the corresponding conn instance + * resources(sock/memory) are released and finally the conn object is freed. + */ +void ksmbd_conn_free(struct ksmbd_conn *conn) +{ + down_write(&conn_list_lock); + list_del(&conn->conns_list); + up_write(&conn_list_lock); + + xa_destroy(&conn->sessions); + kvfree(conn->request_buf); + kfree(conn->preauth_info); + kfree(conn); +} + +/** + * ksmbd_conn_alloc() - initialize a new connection instance + * + * Return: ksmbd_conn struct on success, otherwise NULL + */ +struct ksmbd_conn *ksmbd_conn_alloc(void) +{ + struct ksmbd_conn *conn; + + conn = kzalloc(sizeof(struct ksmbd_conn), GFP_KERNEL); + if (!conn) + return NULL; + + conn->need_neg = true; + ksmbd_conn_set_new(conn); + conn->local_nls = load_nls("utf8"); + if (!conn->local_nls) + conn->local_nls = load_nls_default(); + if (IS_ENABLED(CONFIG_UNICODE)) + conn->um = utf8_load(UNICODE_AGE(12, 1, 0)); + else + conn->um = ERR_PTR(-EOPNOTSUPP); + if (IS_ERR(conn->um)) + conn->um = NULL; + atomic_set(&conn->req_running, 0); + atomic_set(&conn->r_count, 0); + conn->total_credits = 1; + conn->outstanding_credits = 0; + + init_waitqueue_head(&conn->req_running_q); + init_waitqueue_head(&conn->r_count_q); + INIT_LIST_HEAD(&conn->conns_list); + INIT_LIST_HEAD(&conn->requests); + INIT_LIST_HEAD(&conn->async_requests); + spin_lock_init(&conn->request_lock); + spin_lock_init(&conn->credits_lock); + ida_init(&conn->async_ida); + xa_init(&conn->sessions); + + spin_lock_init(&conn->llist_lock); + INIT_LIST_HEAD(&conn->lock_list); + + down_write(&conn_list_lock); + list_add(&conn->conns_list, &conn_list); + up_write(&conn_list_lock); + return conn; +} + +bool ksmbd_conn_lookup_dialect(struct ksmbd_conn *c) +{ + struct ksmbd_conn *t; + bool ret = false; + + down_read(&conn_list_lock); + list_for_each_entry(t, &conn_list, conns_list) { + if (memcmp(t->ClientGUID, c->ClientGUID, SMB2_CLIENT_GUID_SIZE)) + continue; + + ret = true; + break; + } + up_read(&conn_list_lock); + return ret; +} + +void ksmbd_conn_enqueue_request(struct ksmbd_work *work) +{ + struct ksmbd_conn *conn = work->conn; + struct list_head *requests_queue = NULL; + + if (conn->ops->get_cmd_val(work) != SMB2_CANCEL_HE) { + requests_queue = &conn->requests; + work->syncronous = true; + } + + if (requests_queue) { + atomic_inc(&conn->req_running); + spin_lock(&conn->request_lock); + list_add_tail(&work->request_entry, requests_queue); + spin_unlock(&conn->request_lock); + } +} + +int ksmbd_conn_try_dequeue_request(struct ksmbd_work *work) +{ + struct ksmbd_conn *conn = work->conn; + int ret = 1; + + if (list_empty(&work->request_entry) && + list_empty(&work->async_request_entry)) + return 0; + + if (!work->multiRsp) + atomic_dec(&conn->req_running); + spin_lock(&conn->request_lock); + if (!work->multiRsp) { + list_del_init(&work->request_entry); + if (work->syncronous == false) + list_del_init(&work->async_request_entry); + ret = 0; + } + spin_unlock(&conn->request_lock); + + wake_up_all(&conn->req_running_q); + return ret; +} + +void ksmbd_conn_lock(struct ksmbd_conn *conn) +{ + mutex_lock(&conn->srv_mutex); +} + +void ksmbd_conn_unlock(struct ksmbd_conn *conn) +{ + mutex_unlock(&conn->srv_mutex); +} + +void ksmbd_all_conn_set_status(u64 sess_id, u32 status) +{ + struct ksmbd_conn *conn; + + down_read(&conn_list_lock); + list_for_each_entry(conn, &conn_list, conns_list) { + if (conn->binding || xa_load(&conn->sessions, sess_id)) + WRITE_ONCE(conn->status, status); + } + up_read(&conn_list_lock); +} + +void ksmbd_conn_wait_idle(struct ksmbd_conn *conn, u64 sess_id) +{ + struct ksmbd_conn *bind_conn; + + wait_event(conn->req_running_q, atomic_read(&conn->req_running) < 2); + + down_read(&conn_list_lock); + list_for_each_entry(bind_conn, &conn_list, conns_list) { + if (bind_conn == conn) + continue; + + if ((bind_conn->binding || xa_load(&bind_conn->sessions, sess_id)) && + !ksmbd_conn_releasing(bind_conn) && + atomic_read(&bind_conn->req_running)) { + wait_event(bind_conn->req_running_q, + atomic_read(&bind_conn->req_running) == 0); + } + } + up_read(&conn_list_lock); +} + +int ksmbd_conn_write(struct ksmbd_work *work) +{ + struct ksmbd_conn *conn = work->conn; + size_t len = 0; + int sent; + struct kvec iov[3]; + int iov_idx = 0; + + if (!work->response_buf) { + pr_err("NULL response header\n"); + return -EINVAL; + } + + if (work->tr_buf) { + iov[iov_idx] = (struct kvec) { work->tr_buf, + sizeof(struct smb2_transform_hdr) + 4 }; + len += iov[iov_idx++].iov_len; + } + + if (work->aux_payload_sz) { + iov[iov_idx] = (struct kvec) { work->response_buf, work->resp_hdr_sz }; + len += iov[iov_idx++].iov_len; + iov[iov_idx] = (struct kvec) { work->aux_payload_buf, work->aux_payload_sz }; + len += iov[iov_idx++].iov_len; + } else { + if (work->tr_buf) + iov[iov_idx].iov_len = work->resp_hdr_sz; + else + iov[iov_idx].iov_len = get_rfc1002_len(work->response_buf) + 4; + iov[iov_idx].iov_base = work->response_buf; + len += iov[iov_idx++].iov_len; + } + + ksmbd_conn_lock(conn); + sent = conn->transport->ops->writev(conn->transport, &iov[0], + iov_idx, len, + work->need_invalidate_rkey, + work->remote_key); + ksmbd_conn_unlock(conn); + + if (sent < 0) { + pr_err("Failed to send message: %d\n", sent); + return sent; + } + + return 0; +} + +int ksmbd_conn_rdma_read(struct ksmbd_conn *conn, + void *buf, unsigned int buflen, + struct smb2_buffer_desc_v1 *desc, + unsigned int desc_len) +{ + int ret = -EINVAL; + + if (conn->transport->ops->rdma_read) + ret = conn->transport->ops->rdma_read(conn->transport, + buf, buflen, + desc, desc_len); + return ret; +} + +int ksmbd_conn_rdma_write(struct ksmbd_conn *conn, + void *buf, unsigned int buflen, + struct smb2_buffer_desc_v1 *desc, + unsigned int desc_len) +{ + int ret = -EINVAL; + + if (conn->transport->ops->rdma_write) + ret = conn->transport->ops->rdma_write(conn->transport, + buf, buflen, + desc, desc_len); + return ret; +} + +bool ksmbd_conn_alive(struct ksmbd_conn *conn) +{ + if (!ksmbd_server_running()) + return false; + + if (ksmbd_conn_exiting(conn)) + return false; + + if (kthread_should_stop()) + return false; + + if (atomic_read(&conn->stats.open_files_count) > 0) + return true; + + /* + * Stop current session if the time that get last request from client + * is bigger than deadtime user configured and opening file count is + * zero. + */ + if (server_conf.deadtime > 0 && + time_after(jiffies, conn->last_active + server_conf.deadtime)) { + ksmbd_debug(CONN, "No response from client in %lu minutes\n", + server_conf.deadtime / SMB_ECHO_INTERVAL); + return false; + } + return true; +} + +#define SMB1_MIN_SUPPORTED_HEADER_SIZE (sizeof(struct smb_hdr)) +#define SMB2_MIN_SUPPORTED_HEADER_SIZE (sizeof(struct smb2_hdr) + 4) + +/** + * ksmbd_conn_handler_loop() - session thread to listen on new smb requests + * @p: connection instance + * + * One thread each per connection + * + * Return: 0 on success + */ +int ksmbd_conn_handler_loop(void *p) +{ + struct ksmbd_conn *conn = (struct ksmbd_conn *)p; + struct ksmbd_transport *t = conn->transport; + unsigned int pdu_size, max_allowed_pdu_size; + char hdr_buf[4] = {0,}; + int size; + + mutex_init(&conn->srv_mutex); + __module_get(THIS_MODULE); + + if (t->ops->prepare && t->ops->prepare(t)) + goto out; + + conn->last_active = jiffies; + while (ksmbd_conn_alive(conn)) { + if (try_to_freeze()) + continue; + + kvfree(conn->request_buf); + conn->request_buf = NULL; + + size = t->ops->read(t, hdr_buf, sizeof(hdr_buf), -1); + if (size != sizeof(hdr_buf)) + break; + + pdu_size = get_rfc1002_len(hdr_buf); + ksmbd_debug(CONN, "RFC1002 header %u bytes\n", pdu_size); + + if (ksmbd_conn_good(conn)) + max_allowed_pdu_size = + SMB3_MAX_MSGSIZE + conn->vals->max_write_size; + else + max_allowed_pdu_size = SMB3_MAX_MSGSIZE; + + if (pdu_size > max_allowed_pdu_size) { + pr_err_ratelimited("PDU length(%u) excceed maximum allowed pdu size(%u) on connection(%d)\n", + pdu_size, max_allowed_pdu_size, + READ_ONCE(conn->status)); + break; + } + + /* + * Check maximum pdu size(0x00FFFFFF). + */ + if (pdu_size > MAX_STREAM_PROT_LEN) + break; + + if (pdu_size < SMB1_MIN_SUPPORTED_HEADER_SIZE) + break; + + /* 4 for rfc1002 length field */ + /* 1 for implied bcc[0] */ + size = pdu_size + 4 + 1; + conn->request_buf = kvmalloc(size, GFP_KERNEL); + if (!conn->request_buf) + break; + + memcpy(conn->request_buf, hdr_buf, sizeof(hdr_buf)); + + /* + * We already read 4 bytes to find out PDU size, now + * read in PDU + */ + size = t->ops->read(t, conn->request_buf + 4, pdu_size, 2); + if (size < 0) { + pr_err("sock_read failed: %d\n", size); + break; + } + + if (size != pdu_size) { + pr_err("PDU error. Read: %d, Expected: %d\n", + size, pdu_size); + continue; + } + + if (!ksmbd_smb_request(conn)) + break; + + if (((struct smb2_hdr *)smb2_get_msg(conn->request_buf))->ProtocolId == + SMB2_PROTO_NUMBER) { + if (pdu_size < SMB2_MIN_SUPPORTED_HEADER_SIZE) + break; + } + + if (!default_conn_ops.process_fn) { + pr_err("No connection request callback\n"); + break; + } + + if (default_conn_ops.process_fn(conn)) { + pr_err("Cannot handle request\n"); + break; + } + } + +out: + ksmbd_conn_set_releasing(conn); + /* Wait till all reference dropped to the Server object*/ + wait_event(conn->r_count_q, atomic_read(&conn->r_count) == 0); + + if (IS_ENABLED(CONFIG_UNICODE)) + utf8_unload(conn->um); + unload_nls(conn->local_nls); + if (default_conn_ops.terminate_fn) + default_conn_ops.terminate_fn(conn); + t->ops->disconnect(t); + module_put(THIS_MODULE); + return 0; +} + +void ksmbd_conn_init_server_callbacks(struct ksmbd_conn_ops *ops) +{ + default_conn_ops.process_fn = ops->process_fn; + default_conn_ops.terminate_fn = ops->terminate_fn; +} + +int ksmbd_conn_transport_init(void) +{ + int ret; + + mutex_lock(&init_lock); + ret = ksmbd_tcp_init(); + if (ret) { + pr_err("Failed to init TCP subsystem: %d\n", ret); + goto out; + } + + ret = ksmbd_rdma_init(); + if (ret) { + pr_err("Failed to init RDMA subsystem: %d\n", ret); + goto out; + } +out: + mutex_unlock(&init_lock); + return ret; +} + +static void stop_sessions(void) +{ + struct ksmbd_conn *conn; + struct ksmbd_transport *t; + +again: + down_read(&conn_list_lock); + list_for_each_entry(conn, &conn_list, conns_list) { + struct task_struct *task; + + t = conn->transport; + task = t->handler; + if (task) + ksmbd_debug(CONN, "Stop session handler %s/%d\n", + task->comm, task_pid_nr(task)); + ksmbd_conn_set_exiting(conn); + if (t->ops->shutdown) { + up_read(&conn_list_lock); + t->ops->shutdown(t); + down_read(&conn_list_lock); + } + } + up_read(&conn_list_lock); + + if (!list_empty(&conn_list)) { + schedule_timeout_interruptible(HZ / 10); /* 100ms */ + goto again; + } +} + +void ksmbd_conn_transport_destroy(void) +{ + mutex_lock(&init_lock); + ksmbd_tcp_destroy(); + ksmbd_rdma_destroy(); + stop_sessions(); + mutex_unlock(&init_lock); +} diff --git a/fs/smb/server/connection.h b/fs/smb/server/connection.h new file mode 100644 index 0000000000000..ad8dfaa48ffb3 --- /dev/null +++ b/fs/smb/server/connection.h @@ -0,0 +1,231 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + * Copyright (C) 2018 Samsung Electronics Co., Ltd. + */ + +#ifndef __KSMBD_CONNECTION_H__ +#define __KSMBD_CONNECTION_H__ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "smb_common.h" +#include "ksmbd_work.h" + +#define KSMBD_SOCKET_BACKLOG 16 + +enum { + KSMBD_SESS_NEW = 0, + KSMBD_SESS_GOOD, + KSMBD_SESS_EXITING, + KSMBD_SESS_NEED_RECONNECT, + KSMBD_SESS_NEED_NEGOTIATE, + KSMBD_SESS_RELEASING +}; + +struct ksmbd_stats { + atomic_t open_files_count; + atomic64_t request_served; +}; + +struct ksmbd_transport; + +struct ksmbd_conn { + struct smb_version_values *vals; + struct smb_version_ops *ops; + struct smb_version_cmds *cmds; + unsigned int max_cmds; + struct mutex srv_mutex; + int status; + unsigned int cli_cap; + char *request_buf; + struct ksmbd_transport *transport; + struct nls_table *local_nls; + struct unicode_map *um; + struct list_head conns_list; + /* smb session 1 per user */ + struct xarray sessions; + unsigned long last_active; + /* How many request are running currently */ + atomic_t req_running; + /* References which are made for this Server object*/ + atomic_t r_count; + unsigned int total_credits; + unsigned int outstanding_credits; + spinlock_t credits_lock; + wait_queue_head_t req_running_q; + wait_queue_head_t r_count_q; + /* Lock to protect requests list*/ + spinlock_t request_lock; + struct list_head requests; + struct list_head async_requests; + int connection_type; + struct ksmbd_stats stats; + char ClientGUID[SMB2_CLIENT_GUID_SIZE]; + struct ntlmssp_auth ntlmssp; + + spinlock_t llist_lock; + struct list_head lock_list; + + struct preauth_integrity_info *preauth_info; + + bool need_neg; + unsigned int auth_mechs; + unsigned int preferred_auth_mech; + bool sign; + bool use_spnego:1; + __u16 cli_sec_mode; + __u16 srv_sec_mode; + /* dialect index that server chose */ + __u16 dialect; + + char *mechToken; + + struct ksmbd_conn_ops *conn_ops; + + /* Preauth Session Table */ + struct list_head preauth_sess_table; + + struct sockaddr_storage peer_addr; + + /* Identifier for async message */ + struct ida async_ida; + + __le16 cipher_type; + __le16 compress_algorithm; + bool posix_ext_supported; + bool signing_negotiated; + __le16 signing_algorithm; + bool binding; +}; + +struct ksmbd_conn_ops { + int (*process_fn)(struct ksmbd_conn *conn); + int (*terminate_fn)(struct ksmbd_conn *conn); +}; + +struct ksmbd_transport_ops { + int (*prepare)(struct ksmbd_transport *t); + void (*disconnect)(struct ksmbd_transport *t); + void (*shutdown)(struct ksmbd_transport *t); + int (*read)(struct ksmbd_transport *t, char *buf, + unsigned int size, int max_retries); + int (*writev)(struct ksmbd_transport *t, struct kvec *iovs, int niov, + int size, bool need_invalidate_rkey, + unsigned int remote_key); + int (*rdma_read)(struct ksmbd_transport *t, + void *buf, unsigned int len, + struct smb2_buffer_desc_v1 *desc, + unsigned int desc_len); + int (*rdma_write)(struct ksmbd_transport *t, + void *buf, unsigned int len, + struct smb2_buffer_desc_v1 *desc, + unsigned int desc_len); +}; + +struct ksmbd_transport { + struct ksmbd_conn *conn; + struct ksmbd_transport_ops *ops; + struct task_struct *handler; +}; + +#define KSMBD_TCP_RECV_TIMEOUT (7 * HZ) +#define KSMBD_TCP_SEND_TIMEOUT (5 * HZ) +#define KSMBD_TCP_PEER_SOCKADDR(c) ((struct sockaddr *)&((c)->peer_addr)) + +extern struct list_head conn_list; +extern struct rw_semaphore conn_list_lock; + +bool ksmbd_conn_alive(struct ksmbd_conn *conn); +void ksmbd_conn_wait_idle(struct ksmbd_conn *conn, u64 sess_id); +struct ksmbd_conn *ksmbd_conn_alloc(void); +void ksmbd_conn_free(struct ksmbd_conn *conn); +bool ksmbd_conn_lookup_dialect(struct ksmbd_conn *c); +int ksmbd_conn_write(struct ksmbd_work *work); +int ksmbd_conn_rdma_read(struct ksmbd_conn *conn, + void *buf, unsigned int buflen, + struct smb2_buffer_desc_v1 *desc, + unsigned int desc_len); +int ksmbd_conn_rdma_write(struct ksmbd_conn *conn, + void *buf, unsigned int buflen, + struct smb2_buffer_desc_v1 *desc, + unsigned int desc_len); +void ksmbd_conn_enqueue_request(struct ksmbd_work *work); +int ksmbd_conn_try_dequeue_request(struct ksmbd_work *work); +void ksmbd_conn_init_server_callbacks(struct ksmbd_conn_ops *ops); +int ksmbd_conn_handler_loop(void *p); +int ksmbd_conn_transport_init(void); +void ksmbd_conn_transport_destroy(void); +void ksmbd_conn_lock(struct ksmbd_conn *conn); +void ksmbd_conn_unlock(struct ksmbd_conn *conn); + +/* + * WARNING + * + * This is a hack. We will move status to a proper place once we land + * a multi-sessions support. + */ +static inline bool ksmbd_conn_good(struct ksmbd_conn *conn) +{ + return READ_ONCE(conn->status) == KSMBD_SESS_GOOD; +} + +static inline bool ksmbd_conn_need_negotiate(struct ksmbd_conn *conn) +{ + return READ_ONCE(conn->status) == KSMBD_SESS_NEED_NEGOTIATE; +} + +static inline bool ksmbd_conn_need_reconnect(struct ksmbd_conn *conn) +{ + return READ_ONCE(conn->status) == KSMBD_SESS_NEED_RECONNECT; +} + +static inline bool ksmbd_conn_exiting(struct ksmbd_conn *conn) +{ + return READ_ONCE(conn->status) == KSMBD_SESS_EXITING; +} + +static inline bool ksmbd_conn_releasing(struct ksmbd_conn *conn) +{ + return READ_ONCE(conn->status) == KSMBD_SESS_RELEASING; +} + +static inline void ksmbd_conn_set_new(struct ksmbd_conn *conn) +{ + WRITE_ONCE(conn->status, KSMBD_SESS_NEW); +} + +static inline void ksmbd_conn_set_good(struct ksmbd_conn *conn) +{ + WRITE_ONCE(conn->status, KSMBD_SESS_GOOD); +} + +static inline void ksmbd_conn_set_need_negotiate(struct ksmbd_conn *conn) +{ + WRITE_ONCE(conn->status, KSMBD_SESS_NEED_NEGOTIATE); +} + +static inline void ksmbd_conn_set_need_reconnect(struct ksmbd_conn *conn) +{ + WRITE_ONCE(conn->status, KSMBD_SESS_NEED_RECONNECT); +} + +static inline void ksmbd_conn_set_exiting(struct ksmbd_conn *conn) +{ + WRITE_ONCE(conn->status, KSMBD_SESS_EXITING); +} + +static inline void ksmbd_conn_set_releasing(struct ksmbd_conn *conn) +{ + WRITE_ONCE(conn->status, KSMBD_SESS_RELEASING); +} + +void ksmbd_all_conn_set_status(u64 sess_id, u32 status); +#endif /* __CONNECTION_H__ */ diff --git a/fs/smb/server/crypto_ctx.c b/fs/smb/server/crypto_ctx.c new file mode 100644 index 0000000000000..81488d04199da --- /dev/null +++ b/fs/smb/server/crypto_ctx.c @@ -0,0 +1,266 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Copyright (C) 2019 Samsung Electronics Co., Ltd. + */ + +#include +#include +#include +#include +#include +#include + +#include "glob.h" +#include "crypto_ctx.h" + +struct crypto_ctx_list { + spinlock_t ctx_lock; + int avail_ctx; + struct list_head idle_ctx; + wait_queue_head_t ctx_wait; +}; + +static struct crypto_ctx_list ctx_list; + +static inline void free_aead(struct crypto_aead *aead) +{ + if (aead) + crypto_free_aead(aead); +} + +static void free_shash(struct shash_desc *shash) +{ + if (shash) { + crypto_free_shash(shash->tfm); + kfree(shash); + } +} + +static struct crypto_aead *alloc_aead(int id) +{ + struct crypto_aead *tfm = NULL; + + switch (id) { + case CRYPTO_AEAD_AES_GCM: + tfm = crypto_alloc_aead("gcm(aes)", 0, 0); + break; + case CRYPTO_AEAD_AES_CCM: + tfm = crypto_alloc_aead("ccm(aes)", 0, 0); + break; + default: + pr_err("Does not support encrypt ahead(id : %d)\n", id); + return NULL; + } + + if (IS_ERR(tfm)) { + pr_err("Failed to alloc encrypt aead : %ld\n", PTR_ERR(tfm)); + return NULL; + } + + return tfm; +} + +static struct shash_desc *alloc_shash_desc(int id) +{ + struct crypto_shash *tfm = NULL; + struct shash_desc *shash; + + switch (id) { + case CRYPTO_SHASH_HMACMD5: + tfm = crypto_alloc_shash("hmac(md5)", 0, 0); + break; + case CRYPTO_SHASH_HMACSHA256: + tfm = crypto_alloc_shash("hmac(sha256)", 0, 0); + break; + case CRYPTO_SHASH_CMACAES: + tfm = crypto_alloc_shash("cmac(aes)", 0, 0); + break; + case CRYPTO_SHASH_SHA256: + tfm = crypto_alloc_shash("sha256", 0, 0); + break; + case CRYPTO_SHASH_SHA512: + tfm = crypto_alloc_shash("sha512", 0, 0); + break; + default: + return NULL; + } + + if (IS_ERR(tfm)) + return NULL; + + shash = kzalloc(sizeof(*shash) + crypto_shash_descsize(tfm), + GFP_KERNEL); + if (!shash) + crypto_free_shash(tfm); + else + shash->tfm = tfm; + return shash; +} + +static void ctx_free(struct ksmbd_crypto_ctx *ctx) +{ + int i; + + for (i = 0; i < CRYPTO_SHASH_MAX; i++) + free_shash(ctx->desc[i]); + for (i = 0; i < CRYPTO_AEAD_MAX; i++) + free_aead(ctx->ccmaes[i]); + kfree(ctx); +} + +static struct ksmbd_crypto_ctx *ksmbd_find_crypto_ctx(void) +{ + struct ksmbd_crypto_ctx *ctx; + + while (1) { + spin_lock(&ctx_list.ctx_lock); + if (!list_empty(&ctx_list.idle_ctx)) { + ctx = list_entry(ctx_list.idle_ctx.next, + struct ksmbd_crypto_ctx, + list); + list_del(&ctx->list); + spin_unlock(&ctx_list.ctx_lock); + return ctx; + } + + if (ctx_list.avail_ctx > num_online_cpus()) { + spin_unlock(&ctx_list.ctx_lock); + wait_event(ctx_list.ctx_wait, + !list_empty(&ctx_list.idle_ctx)); + continue; + } + + ctx_list.avail_ctx++; + spin_unlock(&ctx_list.ctx_lock); + + ctx = kzalloc(sizeof(struct ksmbd_crypto_ctx), GFP_KERNEL); + if (!ctx) { + spin_lock(&ctx_list.ctx_lock); + ctx_list.avail_ctx--; + spin_unlock(&ctx_list.ctx_lock); + wait_event(ctx_list.ctx_wait, + !list_empty(&ctx_list.idle_ctx)); + continue; + } + break; + } + return ctx; +} + +void ksmbd_release_crypto_ctx(struct ksmbd_crypto_ctx *ctx) +{ + if (!ctx) + return; + + spin_lock(&ctx_list.ctx_lock); + if (ctx_list.avail_ctx <= num_online_cpus()) { + list_add(&ctx->list, &ctx_list.idle_ctx); + spin_unlock(&ctx_list.ctx_lock); + wake_up(&ctx_list.ctx_wait); + return; + } + + ctx_list.avail_ctx--; + spin_unlock(&ctx_list.ctx_lock); + ctx_free(ctx); +} + +static struct ksmbd_crypto_ctx *____crypto_shash_ctx_find(int id) +{ + struct ksmbd_crypto_ctx *ctx; + + if (id >= CRYPTO_SHASH_MAX) + return NULL; + + ctx = ksmbd_find_crypto_ctx(); + if (ctx->desc[id]) + return ctx; + + ctx->desc[id] = alloc_shash_desc(id); + if (ctx->desc[id]) + return ctx; + ksmbd_release_crypto_ctx(ctx); + return NULL; +} + +struct ksmbd_crypto_ctx *ksmbd_crypto_ctx_find_hmacmd5(void) +{ + return ____crypto_shash_ctx_find(CRYPTO_SHASH_HMACMD5); +} + +struct ksmbd_crypto_ctx *ksmbd_crypto_ctx_find_hmacsha256(void) +{ + return ____crypto_shash_ctx_find(CRYPTO_SHASH_HMACSHA256); +} + +struct ksmbd_crypto_ctx *ksmbd_crypto_ctx_find_cmacaes(void) +{ + return ____crypto_shash_ctx_find(CRYPTO_SHASH_CMACAES); +} + +struct ksmbd_crypto_ctx *ksmbd_crypto_ctx_find_sha256(void) +{ + return ____crypto_shash_ctx_find(CRYPTO_SHASH_SHA256); +} + +struct ksmbd_crypto_ctx *ksmbd_crypto_ctx_find_sha512(void) +{ + return ____crypto_shash_ctx_find(CRYPTO_SHASH_SHA512); +} + +static struct ksmbd_crypto_ctx *____crypto_aead_ctx_find(int id) +{ + struct ksmbd_crypto_ctx *ctx; + + if (id >= CRYPTO_AEAD_MAX) + return NULL; + + ctx = ksmbd_find_crypto_ctx(); + if (ctx->ccmaes[id]) + return ctx; + + ctx->ccmaes[id] = alloc_aead(id); + if (ctx->ccmaes[id]) + return ctx; + ksmbd_release_crypto_ctx(ctx); + return NULL; +} + +struct ksmbd_crypto_ctx *ksmbd_crypto_ctx_find_gcm(void) +{ + return ____crypto_aead_ctx_find(CRYPTO_AEAD_AES_GCM); +} + +struct ksmbd_crypto_ctx *ksmbd_crypto_ctx_find_ccm(void) +{ + return ____crypto_aead_ctx_find(CRYPTO_AEAD_AES_CCM); +} + +void ksmbd_crypto_destroy(void) +{ + struct ksmbd_crypto_ctx *ctx; + + while (!list_empty(&ctx_list.idle_ctx)) { + ctx = list_entry(ctx_list.idle_ctx.next, + struct ksmbd_crypto_ctx, + list); + list_del(&ctx->list); + ctx_free(ctx); + } +} + +int ksmbd_crypto_create(void) +{ + struct ksmbd_crypto_ctx *ctx; + + spin_lock_init(&ctx_list.ctx_lock); + INIT_LIST_HEAD(&ctx_list.idle_ctx); + init_waitqueue_head(&ctx_list.ctx_wait); + ctx_list.avail_ctx = 1; + + ctx = kzalloc(sizeof(struct ksmbd_crypto_ctx), GFP_KERNEL); + if (!ctx) + return -ENOMEM; + list_add(&ctx->list, &ctx_list.idle_ctx); + return 0; +} diff --git a/fs/smb/server/crypto_ctx.h b/fs/smb/server/crypto_ctx.h new file mode 100644 index 0000000000000..4a367c62f6536 --- /dev/null +++ b/fs/smb/server/crypto_ctx.h @@ -0,0 +1,66 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + * Copyright (C) 2019 Samsung Electronics Co., Ltd. + */ + +#ifndef __CRYPTO_CTX_H__ +#define __CRYPTO_CTX_H__ + +#include +#include + +enum { + CRYPTO_SHASH_HMACMD5 = 0, + CRYPTO_SHASH_HMACSHA256, + CRYPTO_SHASH_CMACAES, + CRYPTO_SHASH_SHA256, + CRYPTO_SHASH_SHA512, + CRYPTO_SHASH_MAX, +}; + +enum { + CRYPTO_AEAD_AES_GCM = 16, + CRYPTO_AEAD_AES_CCM, + CRYPTO_AEAD_MAX, +}; + +enum { + CRYPTO_BLK_ECBDES = 32, + CRYPTO_BLK_MAX, +}; + +struct ksmbd_crypto_ctx { + struct list_head list; + + struct shash_desc *desc[CRYPTO_SHASH_MAX]; + struct crypto_aead *ccmaes[CRYPTO_AEAD_MAX]; +}; + +#define CRYPTO_HMACMD5(c) ((c)->desc[CRYPTO_SHASH_HMACMD5]) +#define CRYPTO_HMACSHA256(c) ((c)->desc[CRYPTO_SHASH_HMACSHA256]) +#define CRYPTO_CMACAES(c) ((c)->desc[CRYPTO_SHASH_CMACAES]) +#define CRYPTO_SHA256(c) ((c)->desc[CRYPTO_SHASH_SHA256]) +#define CRYPTO_SHA512(c) ((c)->desc[CRYPTO_SHASH_SHA512]) + +#define CRYPTO_HMACMD5_TFM(c) ((c)->desc[CRYPTO_SHASH_HMACMD5]->tfm) +#define CRYPTO_HMACSHA256_TFM(c)\ + ((c)->desc[CRYPTO_SHASH_HMACSHA256]->tfm) +#define CRYPTO_CMACAES_TFM(c) ((c)->desc[CRYPTO_SHASH_CMACAES]->tfm) +#define CRYPTO_SHA256_TFM(c) ((c)->desc[CRYPTO_SHASH_SHA256]->tfm) +#define CRYPTO_SHA512_TFM(c) ((c)->desc[CRYPTO_SHASH_SHA512]->tfm) + +#define CRYPTO_GCM(c) ((c)->ccmaes[CRYPTO_AEAD_AES_GCM]) +#define CRYPTO_CCM(c) ((c)->ccmaes[CRYPTO_AEAD_AES_CCM]) + +void ksmbd_release_crypto_ctx(struct ksmbd_crypto_ctx *ctx); +struct ksmbd_crypto_ctx *ksmbd_crypto_ctx_find_hmacmd5(void); +struct ksmbd_crypto_ctx *ksmbd_crypto_ctx_find_hmacsha256(void); +struct ksmbd_crypto_ctx *ksmbd_crypto_ctx_find_cmacaes(void); +struct ksmbd_crypto_ctx *ksmbd_crypto_ctx_find_sha512(void); +struct ksmbd_crypto_ctx *ksmbd_crypto_ctx_find_sha256(void); +struct ksmbd_crypto_ctx *ksmbd_crypto_ctx_find_gcm(void); +struct ksmbd_crypto_ctx *ksmbd_crypto_ctx_find_ccm(void); +void ksmbd_crypto_destroy(void); +int ksmbd_crypto_create(void); + +#endif /* __CRYPTO_CTX_H__ */ diff --git a/fs/smb/server/glob.h b/fs/smb/server/glob.h new file mode 100644 index 0000000000000..5b8f3e0ebdb36 --- /dev/null +++ b/fs/smb/server/glob.h @@ -0,0 +1,49 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + * Copyright (C) 2016 Namjae Jeon + * Copyright (C) 2018 Samsung Electronics Co., Ltd. + */ + +#ifndef __KSMBD_GLOB_H +#define __KSMBD_GLOB_H + +#include + +#include "unicode.h" +#include "vfs_cache.h" + +#define KSMBD_VERSION "3.4.2" + +extern int ksmbd_debug_types; + +#define KSMBD_DEBUG_SMB BIT(0) +#define KSMBD_DEBUG_AUTH BIT(1) +#define KSMBD_DEBUG_VFS BIT(2) +#define KSMBD_DEBUG_OPLOCK BIT(3) +#define KSMBD_DEBUG_IPC BIT(4) +#define KSMBD_DEBUG_CONN BIT(5) +#define KSMBD_DEBUG_RDMA BIT(6) +#define KSMBD_DEBUG_ALL (KSMBD_DEBUG_SMB | KSMBD_DEBUG_AUTH | \ + KSMBD_DEBUG_VFS | KSMBD_DEBUG_OPLOCK | \ + KSMBD_DEBUG_IPC | KSMBD_DEBUG_CONN | \ + KSMBD_DEBUG_RDMA) + +#ifdef pr_fmt +#undef pr_fmt +#endif + +#ifdef SUBMOD_NAME +#define pr_fmt(fmt) "ksmbd: " SUBMOD_NAME ": " fmt +#else +#define pr_fmt(fmt) "ksmbd: " fmt +#endif + +#define ksmbd_debug(type, fmt, ...) \ + do { \ + if (ksmbd_debug_types & KSMBD_DEBUG_##type) \ + pr_info(fmt, ##__VA_ARGS__); \ + } while (0) + +#define UNICODE_LEN(x) ((x) * 2) + +#endif /* __KSMBD_GLOB_H */ diff --git a/fs/smb/server/ksmbd_netlink.h b/fs/smb/server/ksmbd_netlink.h new file mode 100644 index 0000000000000..ce866ff159bfe --- /dev/null +++ b/fs/smb/server/ksmbd_netlink.h @@ -0,0 +1,412 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + * Copyright (C) 2018 Samsung Electronics Co., Ltd. + * + * linux-ksmbd-devel@lists.sourceforge.net + */ + +#ifndef _LINUX_KSMBD_SERVER_H +#define _LINUX_KSMBD_SERVER_H + +#include + +/* + * This is a userspace ABI to communicate data between ksmbd and user IPC + * daemon using netlink. This is added to track and cache user account DB + * and share configuration info from userspace. + * + * - KSMBD_EVENT_HEARTBEAT_REQUEST(ksmbd_heartbeat) + * This event is to check whether user IPC daemon is alive. If user IPC + * daemon is dead, ksmbd keep existing connection till disconnecting and + * new connection will be denied. + * + * - KSMBD_EVENT_STARTING_UP(ksmbd_startup_request) + * This event is to receive the information that initializes the ksmbd + * server from the user IPC daemon and to start the server. The global + * section parameters are given from smb.conf as initialization + * information. + * + * - KSMBD_EVENT_SHUTTING_DOWN(ksmbd_shutdown_request) + * This event is to shutdown ksmbd server. + * + * - KSMBD_EVENT_LOGIN_REQUEST/RESPONSE(ksmbd_login_request/response) + * This event is to get user account info to user IPC daemon. + * + * - KSMBD_EVENT_SHARE_CONFIG_REQUEST/RESPONSE(ksmbd_share_config_request/response) + * This event is to get net share configuration info. + * + * - KSMBD_EVENT_TREE_CONNECT_REQUEST/RESPONSE(ksmbd_tree_connect_request/response) + * This event is to get session and tree connect info. + * + * - KSMBD_EVENT_TREE_DISCONNECT_REQUEST(ksmbd_tree_disconnect_request) + * This event is to send tree disconnect info to user IPC daemon. + * + * - KSMBD_EVENT_LOGOUT_REQUEST(ksmbd_logout_request) + * This event is to send logout request to user IPC daemon. + * + * - KSMBD_EVENT_RPC_REQUEST/RESPONSE(ksmbd_rpc_command) + * This event is to make DCE/RPC request like srvsvc, wkssvc, lsarpc, + * samr to be processed in userspace. + * + * - KSMBD_EVENT_SPNEGO_AUTHEN_REQUEST/RESPONSE(ksmbd_spnego_authen_request/response) + * This event is to make kerberos authentication to be processed in + * userspace. + */ + +#define KSMBD_GENL_NAME "SMBD_GENL" +#define KSMBD_GENL_VERSION 0x01 + +#define KSMBD_REQ_MAX_ACCOUNT_NAME_SZ 48 +#define KSMBD_REQ_MAX_HASH_SZ 18 +#define KSMBD_REQ_MAX_SHARE_NAME 64 + +/* + * IPC heartbeat frame to check whether user IPC daemon is alive. + */ +struct ksmbd_heartbeat { + __u32 handle; +}; + +/* + * Global config flags. + */ +#define KSMBD_GLOBAL_FLAG_INVALID (0) +#define KSMBD_GLOBAL_FLAG_SMB2_LEASES BIT(0) +#define KSMBD_GLOBAL_FLAG_SMB2_ENCRYPTION BIT(1) +#define KSMBD_GLOBAL_FLAG_SMB3_MULTICHANNEL BIT(2) + +/* + * IPC request for ksmbd server startup + */ +struct ksmbd_startup_request { + __u32 flags; /* Flags for global config */ + __s32 signing; /* Signing enabled */ + __s8 min_prot[16]; /* The minimum SMB protocol version */ + __s8 max_prot[16]; /* The maximum SMB protocol version */ + __s8 netbios_name[16]; + __s8 work_group[64]; /* Workgroup */ + __s8 server_string[64]; /* Server string */ + __u16 tcp_port; /* tcp port */ + __u16 ipc_timeout; /* + * specifies the number of seconds + * server will wait for the userspace to + * reply to heartbeat frames. + */ + __u32 deadtime; /* Number of minutes of inactivity */ + __u32 file_max; /* Limits the maximum number of open files */ + __u32 smb2_max_write; /* MAX write size */ + __u32 smb2_max_read; /* MAX read size */ + __u32 smb2_max_trans; /* MAX trans size */ + __u32 share_fake_fscaps; /* + * Support some special application that + * makes QFSINFO calls to check whether + * we set the SPARSE_FILES bit (0x40). + */ + __u32 sub_auth[3]; /* Subauth value for Security ID */ + __u32 smb2_max_credits; /* MAX credits */ + __u32 smbd_max_io_size; /* smbd read write size */ + __u32 max_connections; /* Number of maximum simultaneous connections */ + __u32 reserved[126]; /* Reserved room */ + __u32 ifc_list_sz; /* interfaces list size */ + __s8 ____payload[]; +}; + +#define KSMBD_STARTUP_CONFIG_INTERFACES(s) ((s)->____payload) + +/* + * IPC request to shutdown ksmbd server. + */ +struct ksmbd_shutdown_request { + __s32 reserved[16]; +}; + +/* + * IPC user login request. + */ +struct ksmbd_login_request { + __u32 handle; + __s8 account[KSMBD_REQ_MAX_ACCOUNT_NAME_SZ]; /* user account name */ + __u32 reserved[16]; /* Reserved room */ +}; + +/* + * IPC user login response. + */ +struct ksmbd_login_response { + __u32 handle; + __u32 gid; /* group id */ + __u32 uid; /* user id */ + __s8 account[KSMBD_REQ_MAX_ACCOUNT_NAME_SZ]; /* user account name */ + __u16 status; + __u16 hash_sz; /* hash size */ + __s8 hash[KSMBD_REQ_MAX_HASH_SZ]; /* password hash */ + __u32 reserved[16]; /* Reserved room */ +}; + +/* + * IPC request to fetch net share config. + */ +struct ksmbd_share_config_request { + __u32 handle; + __s8 share_name[KSMBD_REQ_MAX_SHARE_NAME]; /* share name */ + __u32 reserved[16]; /* Reserved room */ +}; + +/* + * IPC response to the net share config request. + */ +struct ksmbd_share_config_response { + __u32 handle; + __u32 flags; + __u16 create_mask; + __u16 directory_mask; + __u16 force_create_mode; + __u16 force_directory_mode; + __u16 force_uid; + __u16 force_gid; + __s8 share_name[KSMBD_REQ_MAX_SHARE_NAME]; + __u32 reserved[112]; /* Reserved room */ + __u32 veto_list_sz; + __s8 ____payload[]; +}; + +#define KSMBD_SHARE_CONFIG_VETO_LIST(s) ((s)->____payload) + +static inline char * +ksmbd_share_config_path(struct ksmbd_share_config_response *sc) +{ + char *p = sc->____payload; + + if (sc->veto_list_sz) + p += sc->veto_list_sz + 1; + + return p; +} + +/* + * IPC request for tree connection. This request include session and tree + * connect info from client. + */ +struct ksmbd_tree_connect_request { + __u32 handle; + __u16 account_flags; + __u16 flags; + __u64 session_id; + __u64 connect_id; + __s8 account[KSMBD_REQ_MAX_ACCOUNT_NAME_SZ]; + __s8 share[KSMBD_REQ_MAX_SHARE_NAME]; + __s8 peer_addr[64]; + __u32 reserved[16]; /* Reserved room */ +}; + +/* + * IPC Response structure for tree connection. + */ +struct ksmbd_tree_connect_response { + __u32 handle; + __u16 status; + __u16 connection_flags; + __u32 reserved[16]; /* Reserved room */ +}; + +/* + * IPC Request struture to disconnect tree connection. + */ +struct ksmbd_tree_disconnect_request { + __u64 session_id; /* session id */ + __u64 connect_id; /* tree connection id */ + __u32 reserved[16]; /* Reserved room */ +}; + +/* + * IPC Response structure to logout user account. + */ +struct ksmbd_logout_request { + __s8 account[KSMBD_REQ_MAX_ACCOUNT_NAME_SZ]; /* user account name */ + __u32 account_flags; + __u32 reserved[16]; /* Reserved room */ +}; + +/* + * RPC command structure to send rpc request like srvsvc or wkssvc to + * IPC user daemon. + */ +struct ksmbd_rpc_command { + __u32 handle; + __u32 flags; + __u32 payload_sz; + __u8 payload[]; +}; + +/* + * IPC Request Kerberos authentication + */ +struct ksmbd_spnego_authen_request { + __u32 handle; + __u16 spnego_blob_len; /* the length of spnego_blob */ + __u8 spnego_blob[]; /* + * the GSS token from SecurityBuffer of + * SMB2 SESSION SETUP request + */ +}; + +/* + * Response data which includes the GSS token and the session key generated by + * user daemon. + */ +struct ksmbd_spnego_authen_response { + __u32 handle; + struct ksmbd_login_response login_response; /* + * the login response with + * a user identified by the + * GSS token from a client + */ + __u16 session_key_len; /* the length of the session key */ + __u16 spnego_blob_len; /* + * the length of the GSS token which will be + * stored in SecurityBuffer of SMB2 SESSION + * SETUP response + */ + __u8 payload[]; /* session key + AP_REP */ +}; + +/* + * This also used as NETLINK attribute type value. + * + * NOTE: + * Response message type value should be equal to + * request message type value + 1. + */ +enum ksmbd_event { + KSMBD_EVENT_UNSPEC = 0, + KSMBD_EVENT_HEARTBEAT_REQUEST, + + KSMBD_EVENT_STARTING_UP, + KSMBD_EVENT_SHUTTING_DOWN, + + KSMBD_EVENT_LOGIN_REQUEST, + KSMBD_EVENT_LOGIN_RESPONSE = 5, + + KSMBD_EVENT_SHARE_CONFIG_REQUEST, + KSMBD_EVENT_SHARE_CONFIG_RESPONSE, + + KSMBD_EVENT_TREE_CONNECT_REQUEST, + KSMBD_EVENT_TREE_CONNECT_RESPONSE, + + KSMBD_EVENT_TREE_DISCONNECT_REQUEST = 10, + + KSMBD_EVENT_LOGOUT_REQUEST, + + KSMBD_EVENT_RPC_REQUEST, + KSMBD_EVENT_RPC_RESPONSE, + + KSMBD_EVENT_SPNEGO_AUTHEN_REQUEST, + KSMBD_EVENT_SPNEGO_AUTHEN_RESPONSE = 15, + + KSMBD_EVENT_MAX +}; + +/* + * Enumeration for IPC tree connect status. + */ +enum KSMBD_TREE_CONN_STATUS { + KSMBD_TREE_CONN_STATUS_OK = 0, + KSMBD_TREE_CONN_STATUS_NOMEM, + KSMBD_TREE_CONN_STATUS_NO_SHARE, + KSMBD_TREE_CONN_STATUS_NO_USER, + KSMBD_TREE_CONN_STATUS_INVALID_USER, + KSMBD_TREE_CONN_STATUS_HOST_DENIED = 5, + KSMBD_TREE_CONN_STATUS_CONN_EXIST, + KSMBD_TREE_CONN_STATUS_TOO_MANY_CONNS, + KSMBD_TREE_CONN_STATUS_TOO_MANY_SESSIONS, + KSMBD_TREE_CONN_STATUS_ERROR, +}; + +/* + * User config flags. + */ +#define KSMBD_USER_FLAG_INVALID (0) +#define KSMBD_USER_FLAG_OK BIT(0) +#define KSMBD_USER_FLAG_BAD_PASSWORD BIT(1) +#define KSMBD_USER_FLAG_BAD_UID BIT(2) +#define KSMBD_USER_FLAG_BAD_USER BIT(3) +#define KSMBD_USER_FLAG_GUEST_ACCOUNT BIT(4) +#define KSMBD_USER_FLAG_DELAY_SESSION BIT(5) + +/* + * Share config flags. + */ +#define KSMBD_SHARE_FLAG_INVALID (0) +#define KSMBD_SHARE_FLAG_AVAILABLE BIT(0) +#define KSMBD_SHARE_FLAG_BROWSEABLE BIT(1) +#define KSMBD_SHARE_FLAG_WRITEABLE BIT(2) +#define KSMBD_SHARE_FLAG_READONLY BIT(3) +#define KSMBD_SHARE_FLAG_GUEST_OK BIT(4) +#define KSMBD_SHARE_FLAG_GUEST_ONLY BIT(5) +#define KSMBD_SHARE_FLAG_STORE_DOS_ATTRS BIT(6) +#define KSMBD_SHARE_FLAG_OPLOCKS BIT(7) +#define KSMBD_SHARE_FLAG_PIPE BIT(8) +#define KSMBD_SHARE_FLAG_HIDE_DOT_FILES BIT(9) +#define KSMBD_SHARE_FLAG_INHERIT_OWNER BIT(10) +#define KSMBD_SHARE_FLAG_STREAMS BIT(11) +#define KSMBD_SHARE_FLAG_FOLLOW_SYMLINKS BIT(12) +#define KSMBD_SHARE_FLAG_ACL_XATTR BIT(13) +#define KSMBD_SHARE_FLAG_UPDATE BIT(14) + +/* + * Tree connect request flags. + */ +#define KSMBD_TREE_CONN_FLAG_REQUEST_SMB1 (0) +#define KSMBD_TREE_CONN_FLAG_REQUEST_IPV6 BIT(0) +#define KSMBD_TREE_CONN_FLAG_REQUEST_SMB2 BIT(1) + +/* + * Tree connect flags. + */ +#define KSMBD_TREE_CONN_FLAG_GUEST_ACCOUNT BIT(0) +#define KSMBD_TREE_CONN_FLAG_READ_ONLY BIT(1) +#define KSMBD_TREE_CONN_FLAG_WRITABLE BIT(2) +#define KSMBD_TREE_CONN_FLAG_ADMIN_ACCOUNT BIT(3) +#define KSMBD_TREE_CONN_FLAG_UPDATE BIT(4) + +/* + * RPC over IPC. + */ +#define KSMBD_RPC_METHOD_RETURN BIT(0) +#define KSMBD_RPC_SRVSVC_METHOD_INVOKE BIT(1) +#define KSMBD_RPC_SRVSVC_METHOD_RETURN (KSMBD_RPC_SRVSVC_METHOD_INVOKE | KSMBD_RPC_METHOD_RETURN) +#define KSMBD_RPC_WKSSVC_METHOD_INVOKE BIT(2) +#define KSMBD_RPC_WKSSVC_METHOD_RETURN (KSMBD_RPC_WKSSVC_METHOD_INVOKE | KSMBD_RPC_METHOD_RETURN) +#define KSMBD_RPC_IOCTL_METHOD (BIT(3) | KSMBD_RPC_METHOD_RETURN) +#define KSMBD_RPC_OPEN_METHOD BIT(4) +#define KSMBD_RPC_WRITE_METHOD BIT(5) +#define KSMBD_RPC_READ_METHOD (BIT(6) | KSMBD_RPC_METHOD_RETURN) +#define KSMBD_RPC_CLOSE_METHOD BIT(7) +#define KSMBD_RPC_RAP_METHOD (BIT(8) | KSMBD_RPC_METHOD_RETURN) +#define KSMBD_RPC_RESTRICTED_CONTEXT BIT(9) +#define KSMBD_RPC_SAMR_METHOD_INVOKE BIT(10) +#define KSMBD_RPC_SAMR_METHOD_RETURN (KSMBD_RPC_SAMR_METHOD_INVOKE | KSMBD_RPC_METHOD_RETURN) +#define KSMBD_RPC_LSARPC_METHOD_INVOKE BIT(11) +#define KSMBD_RPC_LSARPC_METHOD_RETURN (KSMBD_RPC_LSARPC_METHOD_INVOKE | KSMBD_RPC_METHOD_RETURN) + +/* + * RPC status definitions. + */ +#define KSMBD_RPC_OK 0 +#define KSMBD_RPC_EBAD_FUNC 0x00000001 +#define KSMBD_RPC_EACCESS_DENIED 0x00000005 +#define KSMBD_RPC_EBAD_FID 0x00000006 +#define KSMBD_RPC_ENOMEM 0x00000008 +#define KSMBD_RPC_EBAD_DATA 0x0000000D +#define KSMBD_RPC_ENOTIMPLEMENTED 0x00000040 +#define KSMBD_RPC_EINVALID_PARAMETER 0x00000057 +#define KSMBD_RPC_EMORE_DATA 0x000000EA +#define KSMBD_RPC_EINVALID_LEVEL 0x0000007C +#define KSMBD_RPC_SOME_NOT_MAPPED 0x00000107 + +#define KSMBD_CONFIG_OPT_DISABLED 0 +#define KSMBD_CONFIG_OPT_ENABLED 1 +#define KSMBD_CONFIG_OPT_AUTO 2 +#define KSMBD_CONFIG_OPT_MANDATORY 3 + +#endif /* _LINUX_KSMBD_SERVER_H */ diff --git a/fs/smb/server/ksmbd_spnego_negtokeninit.asn1 b/fs/smb/server/ksmbd_spnego_negtokeninit.asn1 new file mode 100644 index 0000000000000..0065f191b54b7 --- /dev/null +++ b/fs/smb/server/ksmbd_spnego_negtokeninit.asn1 @@ -0,0 +1,31 @@ +GSSAPI ::= + [APPLICATION 0] IMPLICIT SEQUENCE { + thisMech + OBJECT IDENTIFIER ({ksmbd_gssapi_this_mech}), + negotiationToken + NegotiationToken + } + +MechType ::= OBJECT IDENTIFIER ({ksmbd_neg_token_init_mech_type}) + +MechTypeList ::= SEQUENCE OF MechType + +NegTokenInit ::= + SEQUENCE { + mechTypes + [0] MechTypeList, + reqFlags + [1] BIT STRING OPTIONAL, + mechToken + [2] OCTET STRING OPTIONAL ({ksmbd_neg_token_init_mech_token}), + mechListMIC + [3] OCTET STRING OPTIONAL + } + +NegotiationToken ::= + CHOICE { + negTokenInit + [0] NegTokenInit, + negTokenTarg + [1] ANY + } diff --git a/fs/smb/server/ksmbd_spnego_negtokentarg.asn1 b/fs/smb/server/ksmbd_spnego_negtokentarg.asn1 new file mode 100644 index 0000000000000..1151933e7b9c5 --- /dev/null +++ b/fs/smb/server/ksmbd_spnego_negtokentarg.asn1 @@ -0,0 +1,19 @@ +GSSAPI ::= + CHOICE { + negTokenInit + [0] ANY, + negTokenTarg + [1] NegTokenTarg + } + +NegTokenTarg ::= + SEQUENCE { + negResult + [0] ENUMERATED OPTIONAL, + supportedMech + [1] OBJECT IDENTIFIER OPTIONAL, + responseToken + [2] OCTET STRING OPTIONAL ({ksmbd_neg_token_targ_resp_token}), + mechListMIC + [3] OCTET STRING OPTIONAL + } diff --git a/fs/smb/server/ksmbd_work.c b/fs/smb/server/ksmbd_work.c new file mode 100644 index 0000000000000..14b9caebf7a4f --- /dev/null +++ b/fs/smb/server/ksmbd_work.c @@ -0,0 +1,79 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Copyright (C) 2019 Samsung Electronics Co., Ltd. + */ + +#include +#include +#include +#include + +#include "server.h" +#include "connection.h" +#include "ksmbd_work.h" +#include "mgmt/ksmbd_ida.h" + +static struct kmem_cache *work_cache; +static struct workqueue_struct *ksmbd_wq; + +struct ksmbd_work *ksmbd_alloc_work_struct(void) +{ + struct ksmbd_work *work = kmem_cache_zalloc(work_cache, GFP_KERNEL); + + if (work) { + work->compound_fid = KSMBD_NO_FID; + work->compound_pfid = KSMBD_NO_FID; + INIT_LIST_HEAD(&work->request_entry); + INIT_LIST_HEAD(&work->async_request_entry); + INIT_LIST_HEAD(&work->fp_entry); + INIT_LIST_HEAD(&work->interim_entry); + } + return work; +} + +void ksmbd_free_work_struct(struct ksmbd_work *work) +{ + WARN_ON(work->saved_cred != NULL); + + kvfree(work->response_buf); + kvfree(work->aux_payload_buf); + kfree(work->tr_buf); + kvfree(work->request_buf); + if (work->async_id) + ksmbd_release_id(&work->conn->async_ida, work->async_id); + kmem_cache_free(work_cache, work); +} + +void ksmbd_work_pool_destroy(void) +{ + kmem_cache_destroy(work_cache); +} + +int ksmbd_work_pool_init(void) +{ + work_cache = kmem_cache_create("ksmbd_work_cache", + sizeof(struct ksmbd_work), 0, + SLAB_HWCACHE_ALIGN, NULL); + if (!work_cache) + return -ENOMEM; + return 0; +} + +int ksmbd_workqueue_init(void) +{ + ksmbd_wq = alloc_workqueue("ksmbd-io", 0, 0); + if (!ksmbd_wq) + return -ENOMEM; + return 0; +} + +void ksmbd_workqueue_destroy(void) +{ + destroy_workqueue(ksmbd_wq); + ksmbd_wq = NULL; +} + +bool ksmbd_queue_work(struct ksmbd_work *work) +{ + return queue_work(ksmbd_wq, &work->work); +} diff --git a/fs/smb/server/ksmbd_work.h b/fs/smb/server/ksmbd_work.h new file mode 100644 index 0000000000000..5ece58e40c979 --- /dev/null +++ b/fs/smb/server/ksmbd_work.h @@ -0,0 +1,117 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + * Copyright (C) 2019 Samsung Electronics Co., Ltd. + */ + +#ifndef __KSMBD_WORK_H__ +#define __KSMBD_WORK_H__ + +#include +#include + +struct ksmbd_conn; +struct ksmbd_session; +struct ksmbd_tree_connect; + +enum { + KSMBD_WORK_ACTIVE = 0, + KSMBD_WORK_CANCELLED, + KSMBD_WORK_CLOSED, +}; + +/* one of these for every pending CIFS request at the connection */ +struct ksmbd_work { + /* Server corresponding to this mid */ + struct ksmbd_conn *conn; + struct ksmbd_session *sess; + struct ksmbd_tree_connect *tcon; + + /* Pointer to received SMB header */ + void *request_buf; + /* Response buffer */ + void *response_buf; + + /* Read data buffer */ + void *aux_payload_buf; + + /* Next cmd hdr in compound req buf*/ + int next_smb2_rcv_hdr_off; + /* Next cmd hdr in compound rsp buf*/ + int next_smb2_rsp_hdr_off; + + /* + * Current Local FID assigned compound response if SMB2 CREATE + * command is present in compound request + */ + u64 compound_fid; + u64 compound_pfid; + u64 compound_sid; + + const struct cred *saved_cred; + + /* Number of granted credits */ + unsigned int credits_granted; + + /* response smb header size */ + unsigned int resp_hdr_sz; + unsigned int response_sz; + /* Read data count */ + unsigned int aux_payload_sz; + + void *tr_buf; + + unsigned char state; + /* Multiple responses for one request e.g. SMB ECHO */ + bool multiRsp:1; + /* No response for cancelled request */ + bool send_no_response:1; + /* Request is encrypted */ + bool encrypted:1; + /* Is this SYNC or ASYNC ksmbd_work */ + bool syncronous:1; + bool need_invalidate_rkey:1; + + unsigned int remote_key; + /* cancel works */ + int async_id; + void **cancel_argv; + void (*cancel_fn)(void **argv); + + struct work_struct work; + /* List head at conn->requests */ + struct list_head request_entry; + /* List head at conn->async_requests */ + struct list_head async_request_entry; + struct list_head fp_entry; + struct list_head interim_entry; +}; + +/** + * ksmbd_resp_buf_next - Get next buffer on compound response. + * @work: smb work containing response buffer + */ +static inline void *ksmbd_resp_buf_next(struct ksmbd_work *work) +{ + return work->response_buf + work->next_smb2_rsp_hdr_off + 4; +} + +/** + * ksmbd_req_buf_next - Get next buffer on compound request. + * @work: smb work containing response buffer + */ +static inline void *ksmbd_req_buf_next(struct ksmbd_work *work) +{ + return work->request_buf + work->next_smb2_rcv_hdr_off + 4; +} + +struct ksmbd_work *ksmbd_alloc_work_struct(void); +void ksmbd_free_work_struct(struct ksmbd_work *work); + +void ksmbd_work_pool_destroy(void); +int ksmbd_work_pool_init(void); + +int ksmbd_workqueue_init(void); +void ksmbd_workqueue_destroy(void); +bool ksmbd_queue_work(struct ksmbd_work *work); + +#endif /* __KSMBD_WORK_H__ */ diff --git a/fs/smb/server/mgmt/ksmbd_ida.c b/fs/smb/server/mgmt/ksmbd_ida.c new file mode 100644 index 0000000000000..54194d959a5ef --- /dev/null +++ b/fs/smb/server/mgmt/ksmbd_ida.c @@ -0,0 +1,46 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Copyright (C) 2018 Samsung Electronics Co., Ltd. + */ + +#include "ksmbd_ida.h" + +static inline int __acquire_id(struct ida *ida, int from, int to) +{ + return ida_simple_get(ida, from, to, GFP_KERNEL); +} + +int ksmbd_acquire_smb2_tid(struct ida *ida) +{ + int id; + + id = __acquire_id(ida, 1, 0xFFFFFFFF); + + return id; +} + +int ksmbd_acquire_smb2_uid(struct ida *ida) +{ + int id; + + id = __acquire_id(ida, 1, 0); + if (id == 0xFFFE) + id = __acquire_id(ida, 1, 0); + + return id; +} + +int ksmbd_acquire_async_msg_id(struct ida *ida) +{ + return __acquire_id(ida, 1, 0); +} + +int ksmbd_acquire_id(struct ida *ida) +{ + return __acquire_id(ida, 0, 0); +} + +void ksmbd_release_id(struct ida *ida, int id) +{ + ida_simple_remove(ida, id); +} diff --git a/fs/smb/server/mgmt/ksmbd_ida.h b/fs/smb/server/mgmt/ksmbd_ida.h new file mode 100644 index 0000000000000..2bc07b16cfde9 --- /dev/null +++ b/fs/smb/server/mgmt/ksmbd_ida.h @@ -0,0 +1,34 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + * Copyright (C) 2018 Samsung Electronics Co., Ltd. + */ + +#ifndef __KSMBD_IDA_MANAGEMENT_H__ +#define __KSMBD_IDA_MANAGEMENT_H__ + +#include +#include + +/* + * 2.2.1.6.7 TID Generation + * The value 0xFFFF MUST NOT be used as a valid TID. All other + * possible values for TID, including zero (0x0000), are valid. + * The value 0xFFFF is used to specify all TIDs or no TID, + * depending upon the context in which it is used. + */ +int ksmbd_acquire_smb2_tid(struct ida *ida); + +/* + * 2.2.1.6.8 UID Generation + * The value 0xFFFE was declared reserved in the LAN Manager 1.0 + * documentation, so a value of 0xFFFE SHOULD NOT be used as a + * valid UID.<21> All other possible values for a UID, excluding + * zero (0x0000), are valid. + */ +int ksmbd_acquire_smb2_uid(struct ida *ida); +int ksmbd_acquire_async_msg_id(struct ida *ida); + +int ksmbd_acquire_id(struct ida *ida); + +void ksmbd_release_id(struct ida *ida, int id); +#endif /* __KSMBD_IDA_MANAGEMENT_H__ */ diff --git a/fs/smb/server/mgmt/share_config.c b/fs/smb/server/mgmt/share_config.c new file mode 100644 index 0000000000000..328a412259dc1 --- /dev/null +++ b/fs/smb/server/mgmt/share_config.c @@ -0,0 +1,234 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Copyright (C) 2018 Samsung Electronics Co., Ltd. + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "share_config.h" +#include "user_config.h" +#include "user_session.h" +#include "../transport_ipc.h" +#include "../misc.h" + +#define SHARE_HASH_BITS 3 +static DEFINE_HASHTABLE(shares_table, SHARE_HASH_BITS); +static DECLARE_RWSEM(shares_table_lock); + +struct ksmbd_veto_pattern { + char *pattern; + struct list_head list; +}; + +static unsigned int share_name_hash(const char *name) +{ + return jhash(name, strlen(name), 0); +} + +static void kill_share(struct ksmbd_share_config *share) +{ + while (!list_empty(&share->veto_list)) { + struct ksmbd_veto_pattern *p; + + p = list_entry(share->veto_list.next, + struct ksmbd_veto_pattern, + list); + list_del(&p->list); + kfree(p->pattern); + kfree(p); + } + + if (share->path) + path_put(&share->vfs_path); + kfree(share->name); + kfree(share->path); + kfree(share); +} + +void ksmbd_share_config_del(struct ksmbd_share_config *share) +{ + down_write(&shares_table_lock); + hash_del(&share->hlist); + up_write(&shares_table_lock); +} + +void __ksmbd_share_config_put(struct ksmbd_share_config *share) +{ + ksmbd_share_config_del(share); + kill_share(share); +} + +static struct ksmbd_share_config * +__get_share_config(struct ksmbd_share_config *share) +{ + if (!atomic_inc_not_zero(&share->refcount)) + return NULL; + return share; +} + +static struct ksmbd_share_config *__share_lookup(const char *name) +{ + struct ksmbd_share_config *share; + unsigned int key = share_name_hash(name); + + hash_for_each_possible(shares_table, share, hlist, key) { + if (!strcmp(name, share->name)) + return share; + } + return NULL; +} + +static int parse_veto_list(struct ksmbd_share_config *share, + char *veto_list, + int veto_list_sz) +{ + int sz = 0; + + if (!veto_list_sz) + return 0; + + while (veto_list_sz > 0) { + struct ksmbd_veto_pattern *p; + + sz = strlen(veto_list); + if (!sz) + break; + + p = kzalloc(sizeof(struct ksmbd_veto_pattern), GFP_KERNEL); + if (!p) + return -ENOMEM; + + p->pattern = kstrdup(veto_list, GFP_KERNEL); + if (!p->pattern) { + kfree(p); + return -ENOMEM; + } + + list_add(&p->list, &share->veto_list); + + veto_list += sz + 1; + veto_list_sz -= (sz + 1); + } + + return 0; +} + +static struct ksmbd_share_config *share_config_request(struct unicode_map *um, + const char *name) +{ + struct ksmbd_share_config_response *resp; + struct ksmbd_share_config *share = NULL; + struct ksmbd_share_config *lookup; + int ret; + + resp = ksmbd_ipc_share_config_request(name); + if (!resp) + return NULL; + + if (resp->flags == KSMBD_SHARE_FLAG_INVALID) + goto out; + + if (*resp->share_name) { + char *cf_resp_name; + bool equal; + + cf_resp_name = ksmbd_casefold_sharename(um, resp->share_name); + if (IS_ERR(cf_resp_name)) + goto out; + equal = !strcmp(cf_resp_name, name); + kfree(cf_resp_name); + if (!equal) + goto out; + } + + share = kzalloc(sizeof(struct ksmbd_share_config), GFP_KERNEL); + if (!share) + goto out; + + share->flags = resp->flags; + atomic_set(&share->refcount, 1); + INIT_LIST_HEAD(&share->veto_list); + share->name = kstrdup(name, GFP_KERNEL); + + if (!test_share_config_flag(share, KSMBD_SHARE_FLAG_PIPE)) { + share->path = kstrdup(ksmbd_share_config_path(resp), + GFP_KERNEL); + if (share->path) + share->path_sz = strlen(share->path); + share->create_mask = resp->create_mask; + share->directory_mask = resp->directory_mask; + share->force_create_mode = resp->force_create_mode; + share->force_directory_mode = resp->force_directory_mode; + share->force_uid = resp->force_uid; + share->force_gid = resp->force_gid; + ret = parse_veto_list(share, + KSMBD_SHARE_CONFIG_VETO_LIST(resp), + resp->veto_list_sz); + if (!ret && share->path) { + ret = kern_path(share->path, 0, &share->vfs_path); + if (ret) { + ksmbd_debug(SMB, "failed to access '%s'\n", + share->path); + /* Avoid put_path() */ + kfree(share->path); + share->path = NULL; + } + } + if (ret || !share->name) { + kill_share(share); + share = NULL; + goto out; + } + } + + down_write(&shares_table_lock); + lookup = __share_lookup(name); + if (lookup) + lookup = __get_share_config(lookup); + if (!lookup) { + hash_add(shares_table, &share->hlist, share_name_hash(name)); + } else { + kill_share(share); + share = lookup; + } + up_write(&shares_table_lock); + +out: + kvfree(resp); + return share; +} + +struct ksmbd_share_config *ksmbd_share_config_get(struct unicode_map *um, + const char *name) +{ + struct ksmbd_share_config *share; + + down_read(&shares_table_lock); + share = __share_lookup(name); + if (share) + share = __get_share_config(share); + up_read(&shares_table_lock); + + if (share) + return share; + return share_config_request(um, name); +} + +bool ksmbd_share_veto_filename(struct ksmbd_share_config *share, + const char *filename) +{ + struct ksmbd_veto_pattern *p; + + list_for_each_entry(p, &share->veto_list, list) { + if (match_wildcard(p->pattern, filename)) + return true; + } + return false; +} diff --git a/fs/smb/server/mgmt/share_config.h b/fs/smb/server/mgmt/share_config.h new file mode 100644 index 0000000000000..3fd3382939421 --- /dev/null +++ b/fs/smb/server/mgmt/share_config.h @@ -0,0 +1,82 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + * Copyright (C) 2018 Samsung Electronics Co., Ltd. + */ + +#ifndef __SHARE_CONFIG_MANAGEMENT_H__ +#define __SHARE_CONFIG_MANAGEMENT_H__ + +#include +#include +#include +#include + +struct ksmbd_share_config { + char *name; + char *path; + + unsigned int path_sz; + unsigned int flags; + struct list_head veto_list; + + struct path vfs_path; + + atomic_t refcount; + struct hlist_node hlist; + unsigned short create_mask; + unsigned short directory_mask; + unsigned short force_create_mode; + unsigned short force_directory_mode; + unsigned short force_uid; + unsigned short force_gid; +}; + +#define KSMBD_SHARE_INVALID_UID ((__u16)-1) +#define KSMBD_SHARE_INVALID_GID ((__u16)-1) + +static inline int share_config_create_mode(struct ksmbd_share_config *share, + umode_t posix_mode) +{ + if (!share->force_create_mode) { + if (!posix_mode) + return share->create_mask; + else + return posix_mode & share->create_mask; + } + return share->force_create_mode & share->create_mask; +} + +static inline int share_config_directory_mode(struct ksmbd_share_config *share, + umode_t posix_mode) +{ + if (!share->force_directory_mode) { + if (!posix_mode) + return share->directory_mask; + else + return posix_mode & share->directory_mask; + } + + return share->force_directory_mode & share->directory_mask; +} + +static inline int test_share_config_flag(struct ksmbd_share_config *share, + int flag) +{ + return share->flags & flag; +} + +void ksmbd_share_config_del(struct ksmbd_share_config *share); +void __ksmbd_share_config_put(struct ksmbd_share_config *share); + +static inline void ksmbd_share_config_put(struct ksmbd_share_config *share) +{ + if (!atomic_dec_and_test(&share->refcount)) + return; + __ksmbd_share_config_put(share); +} + +struct ksmbd_share_config *ksmbd_share_config_get(struct unicode_map *um, + const char *name); +bool ksmbd_share_veto_filename(struct ksmbd_share_config *share, + const char *filename); +#endif /* __SHARE_CONFIG_MANAGEMENT_H__ */ diff --git a/fs/smb/server/mgmt/tree_connect.c b/fs/smb/server/mgmt/tree_connect.c new file mode 100644 index 0000000000000..f07a05f376513 --- /dev/null +++ b/fs/smb/server/mgmt/tree_connect.c @@ -0,0 +1,147 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Copyright (C) 2018 Samsung Electronics Co., Ltd. + */ + +#include +#include +#include + +#include "../transport_ipc.h" +#include "../connection.h" + +#include "tree_connect.h" +#include "user_config.h" +#include "share_config.h" +#include "user_session.h" + +struct ksmbd_tree_conn_status +ksmbd_tree_conn_connect(struct ksmbd_conn *conn, struct ksmbd_session *sess, + const char *share_name) +{ + struct ksmbd_tree_conn_status status = {-ENOENT, NULL}; + struct ksmbd_tree_connect_response *resp = NULL; + struct ksmbd_share_config *sc; + struct ksmbd_tree_connect *tree_conn = NULL; + struct sockaddr *peer_addr; + int ret; + + sc = ksmbd_share_config_get(conn->um, share_name); + if (!sc) + return status; + + tree_conn = kzalloc(sizeof(struct ksmbd_tree_connect), GFP_KERNEL); + if (!tree_conn) { + status.ret = -ENOMEM; + goto out_error; + } + + tree_conn->id = ksmbd_acquire_tree_conn_id(sess); + if (tree_conn->id < 0) { + status.ret = -EINVAL; + goto out_error; + } + + peer_addr = KSMBD_TCP_PEER_SOCKADDR(conn); + resp = ksmbd_ipc_tree_connect_request(sess, + sc, + tree_conn, + peer_addr); + if (!resp) { + status.ret = -EINVAL; + goto out_error; + } + + status.ret = resp->status; + if (status.ret != KSMBD_TREE_CONN_STATUS_OK) + goto out_error; + + tree_conn->flags = resp->connection_flags; + if (test_tree_conn_flag(tree_conn, KSMBD_TREE_CONN_FLAG_UPDATE)) { + struct ksmbd_share_config *new_sc; + + ksmbd_share_config_del(sc); + new_sc = ksmbd_share_config_get(conn->um, share_name); + if (!new_sc) { + pr_err("Failed to update stale share config\n"); + status.ret = -ESTALE; + goto out_error; + } + ksmbd_share_config_put(sc); + sc = new_sc; + } + + tree_conn->user = sess->user; + tree_conn->share_conf = sc; + status.tree_conn = tree_conn; + + ret = xa_err(xa_store(&sess->tree_conns, tree_conn->id, tree_conn, + GFP_KERNEL)); + if (ret) { + status.ret = -ENOMEM; + goto out_error; + } + kvfree(resp); + return status; + +out_error: + if (tree_conn) + ksmbd_release_tree_conn_id(sess, tree_conn->id); + ksmbd_share_config_put(sc); + kfree(tree_conn); + kvfree(resp); + return status; +} + +int ksmbd_tree_conn_disconnect(struct ksmbd_session *sess, + struct ksmbd_tree_connect *tree_conn) +{ + int ret; + + ret = ksmbd_ipc_tree_disconnect_request(sess->id, tree_conn->id); + ksmbd_release_tree_conn_id(sess, tree_conn->id); + xa_erase(&sess->tree_conns, tree_conn->id); + ksmbd_share_config_put(tree_conn->share_conf); + kfree(tree_conn); + return ret; +} + +struct ksmbd_tree_connect *ksmbd_tree_conn_lookup(struct ksmbd_session *sess, + unsigned int id) +{ + struct ksmbd_tree_connect *tcon; + + tcon = xa_load(&sess->tree_conns, id); + if (tcon) { + if (test_bit(TREE_CONN_EXPIRE, &tcon->status)) + tcon = NULL; + } + + return tcon; +} + +struct ksmbd_share_config *ksmbd_tree_conn_share(struct ksmbd_session *sess, + unsigned int id) +{ + struct ksmbd_tree_connect *tc; + + tc = ksmbd_tree_conn_lookup(sess, id); + if (tc) + return tc->share_conf; + return NULL; +} + +int ksmbd_tree_conn_session_logoff(struct ksmbd_session *sess) +{ + int ret = 0; + struct ksmbd_tree_connect *tc; + unsigned long id; + + if (!sess) + return -EINVAL; + + xa_for_each(&sess->tree_conns, id, tc) + ret |= ksmbd_tree_conn_disconnect(sess, tc); + xa_destroy(&sess->tree_conns); + return ret; +} diff --git a/fs/smb/server/mgmt/tree_connect.h b/fs/smb/server/mgmt/tree_connect.h new file mode 100644 index 0000000000000..700df36cf3e30 --- /dev/null +++ b/fs/smb/server/mgmt/tree_connect.h @@ -0,0 +1,61 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + * Copyright (C) 2018 Samsung Electronics Co., Ltd. + */ + +#ifndef __TREE_CONNECT_MANAGEMENT_H__ +#define __TREE_CONNECT_MANAGEMENT_H__ + +#include + +#include "../ksmbd_netlink.h" + +struct ksmbd_share_config; +struct ksmbd_user; +struct ksmbd_conn; + +#define TREE_CONN_EXPIRE 1 + +struct ksmbd_tree_connect { + int id; + + unsigned int flags; + struct ksmbd_share_config *share_conf; + struct ksmbd_user *user; + + struct list_head list; + + int maximal_access; + bool posix_extensions; + unsigned long status; +}; + +struct ksmbd_tree_conn_status { + unsigned int ret; + struct ksmbd_tree_connect *tree_conn; +}; + +static inline int test_tree_conn_flag(struct ksmbd_tree_connect *tree_conn, + int flag) +{ + return tree_conn->flags & flag; +} + +struct ksmbd_session; + +struct ksmbd_tree_conn_status +ksmbd_tree_conn_connect(struct ksmbd_conn *conn, struct ksmbd_session *sess, + const char *share_name); + +int ksmbd_tree_conn_disconnect(struct ksmbd_session *sess, + struct ksmbd_tree_connect *tree_conn); + +struct ksmbd_tree_connect *ksmbd_tree_conn_lookup(struct ksmbd_session *sess, + unsigned int id); + +struct ksmbd_share_config *ksmbd_tree_conn_share(struct ksmbd_session *sess, + unsigned int id); + +int ksmbd_tree_conn_session_logoff(struct ksmbd_session *sess); + +#endif /* __TREE_CONNECT_MANAGEMENT_H__ */ diff --git a/fs/smb/server/mgmt/user_config.c b/fs/smb/server/mgmt/user_config.c new file mode 100644 index 0000000000000..279d00feff216 --- /dev/null +++ b/fs/smb/server/mgmt/user_config.c @@ -0,0 +1,79 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Copyright (C) 2018 Samsung Electronics Co., Ltd. + */ + +#include +#include + +#include "user_config.h" +#include "../transport_ipc.h" + +struct ksmbd_user *ksmbd_login_user(const char *account) +{ + struct ksmbd_login_response *resp; + struct ksmbd_user *user = NULL; + + resp = ksmbd_ipc_login_request(account); + if (!resp) + return NULL; + + if (!(resp->status & KSMBD_USER_FLAG_OK)) + goto out; + + user = ksmbd_alloc_user(resp); +out: + kvfree(resp); + return user; +} + +struct ksmbd_user *ksmbd_alloc_user(struct ksmbd_login_response *resp) +{ + struct ksmbd_user *user = NULL; + + user = kmalloc(sizeof(struct ksmbd_user), GFP_KERNEL); + if (!user) + return NULL; + + user->name = kstrdup(resp->account, GFP_KERNEL); + user->flags = resp->status; + user->gid = resp->gid; + user->uid = resp->uid; + user->passkey_sz = resp->hash_sz; + user->passkey = kmalloc(resp->hash_sz, GFP_KERNEL); + if (user->passkey) + memcpy(user->passkey, resp->hash, resp->hash_sz); + + if (!user->name || !user->passkey) { + kfree(user->name); + kfree(user->passkey); + kfree(user); + user = NULL; + } + return user; +} + +void ksmbd_free_user(struct ksmbd_user *user) +{ + ksmbd_ipc_logout_request(user->name, user->flags); + kfree(user->name); + kfree(user->passkey); + kfree(user); +} + +int ksmbd_anonymous_user(struct ksmbd_user *user) +{ + if (user->name[0] == '\0') + return 1; + return 0; +} + +bool ksmbd_compare_user(struct ksmbd_user *u1, struct ksmbd_user *u2) +{ + if (strcmp(u1->name, u2->name)) + return false; + if (memcmp(u1->passkey, u2->passkey, u1->passkey_sz)) + return false; + + return true; +} diff --git a/fs/smb/server/mgmt/user_config.h b/fs/smb/server/mgmt/user_config.h new file mode 100644 index 0000000000000..6a44109617f14 --- /dev/null +++ b/fs/smb/server/mgmt/user_config.h @@ -0,0 +1,68 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + * Copyright (C) 2018 Samsung Electronics Co., Ltd. + */ + +#ifndef __USER_CONFIG_MANAGEMENT_H__ +#define __USER_CONFIG_MANAGEMENT_H__ + +#include "../glob.h" + +struct ksmbd_user { + unsigned short flags; + + unsigned int uid; + unsigned int gid; + + char *name; + + size_t passkey_sz; + char *passkey; + unsigned int failed_login_count; +}; + +static inline bool user_guest(struct ksmbd_user *user) +{ + return user->flags & KSMBD_USER_FLAG_GUEST_ACCOUNT; +} + +static inline void set_user_flag(struct ksmbd_user *user, int flag) +{ + user->flags |= flag; +} + +static inline int test_user_flag(struct ksmbd_user *user, int flag) +{ + return user->flags & flag; +} + +static inline void set_user_guest(struct ksmbd_user *user) +{ +} + +static inline char *user_passkey(struct ksmbd_user *user) +{ + return user->passkey; +} + +static inline char *user_name(struct ksmbd_user *user) +{ + return user->name; +} + +static inline unsigned int user_uid(struct ksmbd_user *user) +{ + return user->uid; +} + +static inline unsigned int user_gid(struct ksmbd_user *user) +{ + return user->gid; +} + +struct ksmbd_user *ksmbd_login_user(const char *account); +struct ksmbd_user *ksmbd_alloc_user(struct ksmbd_login_response *resp); +void ksmbd_free_user(struct ksmbd_user *user); +int ksmbd_anonymous_user(struct ksmbd_user *user); +bool ksmbd_compare_user(struct ksmbd_user *u1, struct ksmbd_user *u2); +#endif /* __USER_CONFIG_MANAGEMENT_H__ */ diff --git a/fs/smb/server/mgmt/user_session.c b/fs/smb/server/mgmt/user_session.c new file mode 100644 index 0000000000000..ea4b56d570fbb --- /dev/null +++ b/fs/smb/server/mgmt/user_session.c @@ -0,0 +1,400 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Copyright (C) 2018 Samsung Electronics Co., Ltd. + */ + +#include +#include +#include +#include + +#include "ksmbd_ida.h" +#include "user_session.h" +#include "user_config.h" +#include "tree_connect.h" +#include "../transport_ipc.h" +#include "../connection.h" +#include "../vfs_cache.h" + +static DEFINE_IDA(session_ida); + +#define SESSION_HASH_BITS 3 +static DEFINE_HASHTABLE(sessions_table, SESSION_HASH_BITS); +static DECLARE_RWSEM(sessions_table_lock); + +struct ksmbd_session_rpc { + int id; + unsigned int method; + struct list_head list; +}; + +static void free_channel_list(struct ksmbd_session *sess) +{ + struct channel *chann; + unsigned long index; + + xa_for_each(&sess->ksmbd_chann_list, index, chann) { + xa_erase(&sess->ksmbd_chann_list, index); + kfree(chann); + } + + xa_destroy(&sess->ksmbd_chann_list); +} + +static void __session_rpc_close(struct ksmbd_session *sess, + struct ksmbd_session_rpc *entry) +{ + struct ksmbd_rpc_command *resp; + + resp = ksmbd_rpc_close(sess, entry->id); + if (!resp) + pr_err("Unable to close RPC pipe %d\n", entry->id); + + kvfree(resp); + ksmbd_rpc_id_free(entry->id); + kfree(entry); +} + +static void ksmbd_session_rpc_clear_list(struct ksmbd_session *sess) +{ + struct ksmbd_session_rpc *entry; + + while (!list_empty(&sess->rpc_handle_list)) { + entry = list_entry(sess->rpc_handle_list.next, + struct ksmbd_session_rpc, + list); + + list_del(&entry->list); + __session_rpc_close(sess, entry); + } +} + +static int __rpc_method(char *rpc_name) +{ + if (!strcmp(rpc_name, "\\srvsvc") || !strcmp(rpc_name, "srvsvc")) + return KSMBD_RPC_SRVSVC_METHOD_INVOKE; + + if (!strcmp(rpc_name, "\\wkssvc") || !strcmp(rpc_name, "wkssvc")) + return KSMBD_RPC_WKSSVC_METHOD_INVOKE; + + if (!strcmp(rpc_name, "LANMAN") || !strcmp(rpc_name, "lanman")) + return KSMBD_RPC_RAP_METHOD; + + if (!strcmp(rpc_name, "\\samr") || !strcmp(rpc_name, "samr")) + return KSMBD_RPC_SAMR_METHOD_INVOKE; + + if (!strcmp(rpc_name, "\\lsarpc") || !strcmp(rpc_name, "lsarpc")) + return KSMBD_RPC_LSARPC_METHOD_INVOKE; + + pr_err("Unsupported RPC: %s\n", rpc_name); + return 0; +} + +int ksmbd_session_rpc_open(struct ksmbd_session *sess, char *rpc_name) +{ + struct ksmbd_session_rpc *entry; + struct ksmbd_rpc_command *resp; + int method; + + method = __rpc_method(rpc_name); + if (!method) + return -EINVAL; + + entry = kzalloc(sizeof(struct ksmbd_session_rpc), GFP_KERNEL); + if (!entry) + return -EINVAL; + + list_add(&entry->list, &sess->rpc_handle_list); + entry->method = method; + entry->id = ksmbd_ipc_id_alloc(); + if (entry->id < 0) + goto free_entry; + + resp = ksmbd_rpc_open(sess, entry->id); + if (!resp) + goto free_id; + + kvfree(resp); + return entry->id; +free_id: + ksmbd_rpc_id_free(entry->id); +free_entry: + list_del(&entry->list); + kfree(entry); + return -EINVAL; +} + +void ksmbd_session_rpc_close(struct ksmbd_session *sess, int id) +{ + struct ksmbd_session_rpc *entry; + + list_for_each_entry(entry, &sess->rpc_handle_list, list) { + if (entry->id == id) { + list_del(&entry->list); + __session_rpc_close(sess, entry); + break; + } + } +} + +int ksmbd_session_rpc_method(struct ksmbd_session *sess, int id) +{ + struct ksmbd_session_rpc *entry; + + list_for_each_entry(entry, &sess->rpc_handle_list, list) { + if (entry->id == id) + return entry->method; + } + return 0; +} + +void ksmbd_session_destroy(struct ksmbd_session *sess) +{ + if (!sess) + return; + + if (sess->user) + ksmbd_free_user(sess->user); + + ksmbd_tree_conn_session_logoff(sess); + ksmbd_destroy_file_table(&sess->file_table); + ksmbd_session_rpc_clear_list(sess); + free_channel_list(sess); + kfree(sess->Preauth_HashValue); + ksmbd_release_id(&session_ida, sess->id); + kfree(sess); +} + +static struct ksmbd_session *__session_lookup(unsigned long long id) +{ + struct ksmbd_session *sess; + + hash_for_each_possible(sessions_table, sess, hlist, id) { + if (id == sess->id) { + sess->last_active = jiffies; + return sess; + } + } + return NULL; +} + +static void ksmbd_expire_session(struct ksmbd_conn *conn) +{ + unsigned long id; + struct ksmbd_session *sess; + + down_write(&sessions_table_lock); + xa_for_each(&conn->sessions, id, sess) { + if (sess->state != SMB2_SESSION_VALID || + time_after(jiffies, + sess->last_active + SMB2_SESSION_TIMEOUT)) { + xa_erase(&conn->sessions, sess->id); + hash_del(&sess->hlist); + ksmbd_session_destroy(sess); + continue; + } + } + up_write(&sessions_table_lock); +} + +int ksmbd_session_register(struct ksmbd_conn *conn, + struct ksmbd_session *sess) +{ + sess->dialect = conn->dialect; + memcpy(sess->ClientGUID, conn->ClientGUID, SMB2_CLIENT_GUID_SIZE); + ksmbd_expire_session(conn); + return xa_err(xa_store(&conn->sessions, sess->id, sess, GFP_KERNEL)); +} + +static int ksmbd_chann_del(struct ksmbd_conn *conn, struct ksmbd_session *sess) +{ + struct channel *chann; + + chann = xa_erase(&sess->ksmbd_chann_list, (long)conn); + if (!chann) + return -ENOENT; + + kfree(chann); + return 0; +} + +void ksmbd_sessions_deregister(struct ksmbd_conn *conn) +{ + struct ksmbd_session *sess; + unsigned long id; + + down_write(&sessions_table_lock); + if (conn->binding) { + int bkt; + struct hlist_node *tmp; + + hash_for_each_safe(sessions_table, bkt, tmp, sess, hlist) { + if (!ksmbd_chann_del(conn, sess) && + xa_empty(&sess->ksmbd_chann_list)) { + hash_del(&sess->hlist); + ksmbd_session_destroy(sess); + } + } + } + + xa_for_each(&conn->sessions, id, sess) { + unsigned long chann_id; + struct channel *chann; + + xa_for_each(&sess->ksmbd_chann_list, chann_id, chann) { + if (chann->conn != conn) + ksmbd_conn_set_exiting(chann->conn); + } + + ksmbd_chann_del(conn, sess); + if (xa_empty(&sess->ksmbd_chann_list)) { + xa_erase(&conn->sessions, sess->id); + hash_del(&sess->hlist); + ksmbd_session_destroy(sess); + } + } + up_write(&sessions_table_lock); +} + +struct ksmbd_session *ksmbd_session_lookup(struct ksmbd_conn *conn, + unsigned long long id) +{ + struct ksmbd_session *sess; + + sess = xa_load(&conn->sessions, id); + if (sess) + sess->last_active = jiffies; + return sess; +} + +struct ksmbd_session *ksmbd_session_lookup_slowpath(unsigned long long id) +{ + struct ksmbd_session *sess; + + down_read(&sessions_table_lock); + sess = __session_lookup(id); + if (sess) + sess->last_active = jiffies; + up_read(&sessions_table_lock); + + return sess; +} + +struct ksmbd_session *ksmbd_session_lookup_all(struct ksmbd_conn *conn, + unsigned long long id) +{ + struct ksmbd_session *sess; + + sess = ksmbd_session_lookup(conn, id); + if (!sess && conn->binding) + sess = ksmbd_session_lookup_slowpath(id); + if (sess && sess->state != SMB2_SESSION_VALID) + sess = NULL; + return sess; +} + +struct preauth_session *ksmbd_preauth_session_alloc(struct ksmbd_conn *conn, + u64 sess_id) +{ + struct preauth_session *sess; + + sess = kmalloc(sizeof(struct preauth_session), GFP_KERNEL); + if (!sess) + return NULL; + + sess->id = sess_id; + memcpy(sess->Preauth_HashValue, conn->preauth_info->Preauth_HashValue, + PREAUTH_HASHVALUE_SIZE); + list_add(&sess->preauth_entry, &conn->preauth_sess_table); + + return sess; +} + +static bool ksmbd_preauth_session_id_match(struct preauth_session *sess, + unsigned long long id) +{ + return sess->id == id; +} + +struct preauth_session *ksmbd_preauth_session_lookup(struct ksmbd_conn *conn, + unsigned long long id) +{ + struct preauth_session *sess = NULL; + + list_for_each_entry(sess, &conn->preauth_sess_table, preauth_entry) { + if (ksmbd_preauth_session_id_match(sess, id)) + return sess; + } + return NULL; +} + +static int __init_smb2_session(struct ksmbd_session *sess) +{ + int id = ksmbd_acquire_smb2_uid(&session_ida); + + if (id < 0) + return -EINVAL; + sess->id = id; + return 0; +} + +static struct ksmbd_session *__session_create(int protocol) +{ + struct ksmbd_session *sess; + int ret; + + if (protocol != CIFDS_SESSION_FLAG_SMB2) + return NULL; + + sess = kzalloc(sizeof(struct ksmbd_session), GFP_KERNEL); + if (!sess) + return NULL; + + if (ksmbd_init_file_table(&sess->file_table)) + goto error; + + sess->last_active = jiffies; + sess->state = SMB2_SESSION_IN_PROGRESS; + set_session_flag(sess, protocol); + xa_init(&sess->tree_conns); + xa_init(&sess->ksmbd_chann_list); + INIT_LIST_HEAD(&sess->rpc_handle_list); + sess->sequence_number = 1; + + ret = __init_smb2_session(sess); + if (ret) + goto error; + + ida_init(&sess->tree_conn_ida); + + down_write(&sessions_table_lock); + hash_add(sessions_table, &sess->hlist, sess->id); + up_write(&sessions_table_lock); + + return sess; + +error: + ksmbd_session_destroy(sess); + return NULL; +} + +struct ksmbd_session *ksmbd_smb2_session_create(void) +{ + return __session_create(CIFDS_SESSION_FLAG_SMB2); +} + +int ksmbd_acquire_tree_conn_id(struct ksmbd_session *sess) +{ + int id = -EINVAL; + + if (test_session_flag(sess, CIFDS_SESSION_FLAG_SMB2)) + id = ksmbd_acquire_smb2_tid(&sess->tree_conn_ida); + + return id; +} + +void ksmbd_release_tree_conn_id(struct ksmbd_session *sess, int id) +{ + if (id >= 0) + ksmbd_release_id(&sess->tree_conn_ida, id); +} diff --git a/fs/smb/server/mgmt/user_session.h b/fs/smb/server/mgmt/user_session.h new file mode 100644 index 0000000000000..51f38e5b61abb --- /dev/null +++ b/fs/smb/server/mgmt/user_session.h @@ -0,0 +1,103 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + * Copyright (C) 2018 Samsung Electronics Co., Ltd. + */ + +#ifndef __USER_SESSION_MANAGEMENT_H__ +#define __USER_SESSION_MANAGEMENT_H__ + +#include +#include + +#include "../smb_common.h" +#include "../ntlmssp.h" + +#define CIFDS_SESSION_FLAG_SMB2 BIT(1) + +#define PREAUTH_HASHVALUE_SIZE 64 + +struct ksmbd_file_table; + +struct channel { + __u8 smb3signingkey[SMB3_SIGN_KEY_SIZE]; + struct ksmbd_conn *conn; +}; + +struct preauth_session { + __u8 Preauth_HashValue[PREAUTH_HASHVALUE_SIZE]; + u64 id; + struct list_head preauth_entry; +}; + +struct ksmbd_session { + u64 id; + + __u16 dialect; + char ClientGUID[SMB2_CLIENT_GUID_SIZE]; + + struct ksmbd_user *user; + unsigned int sequence_number; + unsigned int flags; + + bool sign; + bool enc; + bool is_anonymous; + + int state; + __u8 *Preauth_HashValue; + + char sess_key[CIFS_KEY_SIZE]; + + struct hlist_node hlist; + struct xarray ksmbd_chann_list; + struct xarray tree_conns; + struct ida tree_conn_ida; + struct list_head rpc_handle_list; + + __u8 smb3encryptionkey[SMB3_ENC_DEC_KEY_SIZE]; + __u8 smb3decryptionkey[SMB3_ENC_DEC_KEY_SIZE]; + __u8 smb3signingkey[SMB3_SIGN_KEY_SIZE]; + + struct ksmbd_file_table file_table; + unsigned long last_active; +}; + +static inline int test_session_flag(struct ksmbd_session *sess, int bit) +{ + return sess->flags & bit; +} + +static inline void set_session_flag(struct ksmbd_session *sess, int bit) +{ + sess->flags |= bit; +} + +static inline void clear_session_flag(struct ksmbd_session *sess, int bit) +{ + sess->flags &= ~bit; +} + +struct ksmbd_session *ksmbd_smb2_session_create(void); + +void ksmbd_session_destroy(struct ksmbd_session *sess); + +struct ksmbd_session *ksmbd_session_lookup_slowpath(unsigned long long id); +struct ksmbd_session *ksmbd_session_lookup(struct ksmbd_conn *conn, + unsigned long long id); +int ksmbd_session_register(struct ksmbd_conn *conn, + struct ksmbd_session *sess); +void ksmbd_sessions_deregister(struct ksmbd_conn *conn); +struct ksmbd_session *ksmbd_session_lookup_all(struct ksmbd_conn *conn, + unsigned long long id); +struct preauth_session *ksmbd_preauth_session_alloc(struct ksmbd_conn *conn, + u64 sess_id); +struct preauth_session *ksmbd_preauth_session_lookup(struct ksmbd_conn *conn, + unsigned long long id); + +int ksmbd_acquire_tree_conn_id(struct ksmbd_session *sess); +void ksmbd_release_tree_conn_id(struct ksmbd_session *sess, int id); + +int ksmbd_session_rpc_open(struct ksmbd_session *sess, char *rpc_name); +void ksmbd_session_rpc_close(struct ksmbd_session *sess, int id); +int ksmbd_session_rpc_method(struct ksmbd_session *sess, int id); +#endif /* __USER_SESSION_MANAGEMENT_H__ */ diff --git a/fs/smb/server/misc.c b/fs/smb/server/misc.c new file mode 100644 index 0000000000000..9e8afaa686e3a --- /dev/null +++ b/fs/smb/server/misc.c @@ -0,0 +1,381 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Copyright (C) 2016 Namjae Jeon + * Copyright (C) 2018 Samsung Electronics Co., Ltd. + */ + +#include +#include +#include +#include + +#include "misc.h" +#include "smb_common.h" +#include "connection.h" +#include "vfs.h" + +#include "mgmt/share_config.h" + +/** + * match_pattern() - compare a string with a pattern which might include + * wildcard '*' and '?' + * TODO : implement consideration about DOS_DOT, DOS_QM and DOS_STAR + * + * @str: string to compare with a pattern + * @len: string length + * @pattern: pattern string which might include wildcard '*' and '?' + * + * Return: 0 if pattern matched with the string, otherwise non zero value + */ +int match_pattern(const char *str, size_t len, const char *pattern) +{ + const char *s = str; + const char *p = pattern; + bool star = false; + + while (*s && len) { + switch (*p) { + case '?': + s++; + len--; + p++; + break; + case '*': + star = true; + str = s; + if (!*++p) + return true; + pattern = p; + break; + default: + if (tolower(*s) == tolower(*p)) { + s++; + len--; + p++; + } else { + if (!star) + return false; + str++; + s = str; + p = pattern; + } + break; + } + } + + if (*p == '*') + ++p; + return !*p; +} + +/* + * is_char_allowed() - check for valid character + * @ch: input character to be checked + * + * Return: 1 if char is allowed, otherwise 0 + */ +static inline int is_char_allowed(char ch) +{ + /* check for control chars, wildcards etc. */ + if (!(ch & 0x80) && + (ch <= 0x1f || + ch == '?' || ch == '"' || ch == '<' || + ch == '>' || ch == '|' || ch == '*')) + return 0; + + return 1; +} + +int ksmbd_validate_filename(char *filename) +{ + while (*filename) { + char c = *filename; + + filename++; + if (!is_char_allowed(c)) { + ksmbd_debug(VFS, "File name validation failed: 0x%x\n", c); + return -ENOENT; + } + } + + return 0; +} + +static int ksmbd_validate_stream_name(char *stream_name) +{ + while (*stream_name) { + char c = *stream_name; + + stream_name++; + if (c == '/' || c == ':' || c == '\\') { + pr_err("Stream name validation failed: %c\n", c); + return -ENOENT; + } + } + + return 0; +} + +int parse_stream_name(char *filename, char **stream_name, int *s_type) +{ + char *stream_type; + char *s_name; + int rc = 0; + + s_name = filename; + filename = strsep(&s_name, ":"); + ksmbd_debug(SMB, "filename : %s, streams : %s\n", filename, s_name); + if (strchr(s_name, ':')) { + stream_type = s_name; + s_name = strsep(&stream_type, ":"); + + rc = ksmbd_validate_stream_name(s_name); + if (rc < 0) { + rc = -ENOENT; + goto out; + } + + ksmbd_debug(SMB, "stream name : %s, stream type : %s\n", s_name, + stream_type); + if (!strncasecmp("$data", stream_type, 5)) + *s_type = DATA_STREAM; + else if (!strncasecmp("$index_allocation", stream_type, 17)) + *s_type = DIR_STREAM; + else + rc = -ENOENT; + } + + *stream_name = s_name; +out: + return rc; +} + +/** + * convert_to_nt_pathname() - extract and return windows path string + * whose share directory prefix was removed from file path + * @share: ksmbd_share_config pointer + * @path: path to report + * + * Return : windows path string or error + */ + +char *convert_to_nt_pathname(struct ksmbd_share_config *share, + const struct path *path) +{ + char *pathname, *ab_pathname, *nt_pathname; + int share_path_len = share->path_sz; + + pathname = kmalloc(PATH_MAX, GFP_KERNEL); + if (!pathname) + return ERR_PTR(-EACCES); + + ab_pathname = d_path(path, pathname, PATH_MAX); + if (IS_ERR(ab_pathname)) { + nt_pathname = ERR_PTR(-EACCES); + goto free_pathname; + } + + if (strncmp(ab_pathname, share->path, share_path_len)) { + nt_pathname = ERR_PTR(-EACCES); + goto free_pathname; + } + + nt_pathname = kzalloc(strlen(&ab_pathname[share_path_len]) + 2, GFP_KERNEL); + if (!nt_pathname) { + nt_pathname = ERR_PTR(-ENOMEM); + goto free_pathname; + } + if (ab_pathname[share_path_len] == '\0') + strcpy(nt_pathname, "/"); + strcat(nt_pathname, &ab_pathname[share_path_len]); + + ksmbd_conv_path_to_windows(nt_pathname); + +free_pathname: + kfree(pathname); + return nt_pathname; +} + +int get_nlink(struct kstat *st) +{ + int nlink; + + nlink = st->nlink; + if (S_ISDIR(st->mode)) + nlink--; + + return nlink; +} + +void ksmbd_conv_path_to_unix(char *path) +{ + strreplace(path, '\\', '/'); +} + +void ksmbd_strip_last_slash(char *path) +{ + int len = strlen(path); + + while (len && path[len - 1] == '/') { + path[len - 1] = '\0'; + len--; + } +} + +void ksmbd_conv_path_to_windows(char *path) +{ + strreplace(path, '/', '\\'); +} + +char *ksmbd_casefold_sharename(struct unicode_map *um, const char *name) +{ + char *cf_name; + int cf_len; + + cf_name = kzalloc(KSMBD_REQ_MAX_SHARE_NAME, GFP_KERNEL); + if (!cf_name) + return ERR_PTR(-ENOMEM); + + if (IS_ENABLED(CONFIG_UNICODE) && um) { + const struct qstr q_name = {.name = name, .len = strlen(name)}; + + cf_len = utf8_casefold(um, &q_name, cf_name, + KSMBD_REQ_MAX_SHARE_NAME); + if (cf_len < 0) + goto out_ascii; + + return cf_name; + } + +out_ascii: + cf_len = strscpy(cf_name, name, KSMBD_REQ_MAX_SHARE_NAME); + if (cf_len < 0) { + kfree(cf_name); + return ERR_PTR(-E2BIG); + } + + for (; *cf_name; ++cf_name) + *cf_name = isascii(*cf_name) ? tolower(*cf_name) : *cf_name; + return cf_name - cf_len; +} + +/** + * ksmbd_extract_sharename() - get share name from tree connect request + * @treename: buffer containing tree name and share name + * + * Return: share name on success, otherwise error + */ +char *ksmbd_extract_sharename(struct unicode_map *um, const char *treename) +{ + const char *name = treename, *pos = strrchr(name, '\\'); + + if (pos) + name = (pos + 1); + + /* caller has to free the memory */ + return ksmbd_casefold_sharename(um, name); +} + +/** + * convert_to_unix_name() - convert windows name to unix format + * @share: ksmbd_share_config pointer + * @name: file name that is relative to share + * + * Return: converted name on success, otherwise NULL + */ +char *convert_to_unix_name(struct ksmbd_share_config *share, const char *name) +{ + int no_slash = 0, name_len, path_len; + char *new_name; + + if (name[0] == '/') + name++; + + path_len = share->path_sz; + name_len = strlen(name); + new_name = kmalloc(path_len + name_len + 2, GFP_KERNEL); + if (!new_name) + return new_name; + + memcpy(new_name, share->path, path_len); + if (new_name[path_len - 1] != '/') { + new_name[path_len] = '/'; + no_slash = 1; + } + + memcpy(new_name + path_len + no_slash, name, name_len); + path_len += name_len + no_slash; + new_name[path_len] = 0x00; + return new_name; +} + +char *ksmbd_convert_dir_info_name(struct ksmbd_dir_info *d_info, + const struct nls_table *local_nls, + int *conv_len) +{ + char *conv; + int sz = min(4 * d_info->name_len, PATH_MAX); + + if (!sz) + return NULL; + + conv = kmalloc(sz, GFP_KERNEL); + if (!conv) + return NULL; + + /* XXX */ + *conv_len = smbConvertToUTF16((__le16 *)conv, d_info->name, + d_info->name_len, local_nls, 0); + *conv_len *= 2; + + /* We allocate buffer twice bigger than needed. */ + conv[*conv_len] = 0x00; + conv[*conv_len + 1] = 0x00; + return conv; +} + +/* + * Convert the NT UTC (based 1601-01-01, in hundred nanosecond units) + * into Unix UTC (based 1970-01-01, in seconds). + */ +struct timespec64 ksmbd_NTtimeToUnix(__le64 ntutc) +{ + struct timespec64 ts; + + /* Subtract the NTFS time offset, then convert to 1s intervals. */ + s64 t = le64_to_cpu(ntutc) - NTFS_TIME_OFFSET; + u64 abs_t; + + /* + * Unfortunately can not use normal 64 bit division on 32 bit arch, but + * the alternative, do_div, does not work with negative numbers so have + * to special case them + */ + if (t < 0) { + abs_t = -t; + ts.tv_nsec = do_div(abs_t, 10000000) * 100; + ts.tv_nsec = -ts.tv_nsec; + ts.tv_sec = -abs_t; + } else { + abs_t = t; + ts.tv_nsec = do_div(abs_t, 10000000) * 100; + ts.tv_sec = abs_t; + } + + return ts; +} + +/* Convert the Unix UTC into NT UTC. */ +inline u64 ksmbd_UnixTimeToNT(struct timespec64 t) +{ + /* Convert to 100ns intervals and then add the NTFS time offset. */ + return (u64)t.tv_sec * 10000000 + t.tv_nsec / 100 + NTFS_TIME_OFFSET; +} + +inline long long ksmbd_systime(void) +{ + struct timespec64 ts; + + ktime_get_real_ts64(&ts); + return ksmbd_UnixTimeToNT(ts); +} diff --git a/fs/smb/server/misc.h b/fs/smb/server/misc.h new file mode 100644 index 0000000000000..1facfcd21200f --- /dev/null +++ b/fs/smb/server/misc.h @@ -0,0 +1,37 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + * Copyright (C) 2018 Samsung Electronics Co., Ltd. + */ + +#ifndef __KSMBD_MISC_H__ +#define __KSMBD_MISC_H__ + +struct ksmbd_share_config; +struct nls_table; +struct kstat; +struct ksmbd_file; + +int match_pattern(const char *str, size_t len, const char *pattern); +int ksmbd_validate_filename(char *filename); +int parse_stream_name(char *filename, char **stream_name, int *s_type); +char *convert_to_nt_pathname(struct ksmbd_share_config *share, + const struct path *path); +int get_nlink(struct kstat *st); +void ksmbd_conv_path_to_unix(char *path); +void ksmbd_strip_last_slash(char *path); +void ksmbd_conv_path_to_windows(char *path); +char *ksmbd_casefold_sharename(struct unicode_map *um, const char *name); +char *ksmbd_extract_sharename(struct unicode_map *um, const char *treename); +char *convert_to_unix_name(struct ksmbd_share_config *share, const char *name); + +#define KSMBD_DIR_INFO_ALIGNMENT 8 +struct ksmbd_dir_info; +char *ksmbd_convert_dir_info_name(struct ksmbd_dir_info *d_info, + const struct nls_table *local_nls, + int *conv_len); + +#define NTFS_TIME_OFFSET ((u64)(369 * 365 + 89) * 24 * 3600 * 10000000) +struct timespec64 ksmbd_NTtimeToUnix(__le64 ntutc); +u64 ksmbd_UnixTimeToNT(struct timespec64 t); +long long ksmbd_systime(void); +#endif /* __KSMBD_MISC_H__ */ diff --git a/fs/smb/server/ndr.c b/fs/smb/server/ndr.c new file mode 100644 index 0000000000000..4d9e0b54e3dbf --- /dev/null +++ b/fs/smb/server/ndr.c @@ -0,0 +1,514 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Copyright (C) 2021 Samsung Electronics Co., Ltd. + * Author(s): Namjae Jeon + */ + +#include + +#include "glob.h" +#include "ndr.h" + +static inline char *ndr_get_field(struct ndr *n) +{ + return n->data + n->offset; +} + +static int try_to_realloc_ndr_blob(struct ndr *n, size_t sz) +{ + char *data; + + data = krealloc(n->data, n->offset + sz + 1024, GFP_KERNEL); + if (!data) + return -ENOMEM; + + n->data = data; + n->length += 1024; + memset(n->data + n->offset, 0, 1024); + return 0; +} + +static int ndr_write_int16(struct ndr *n, __u16 value) +{ + if (n->length <= n->offset + sizeof(value)) { + int ret; + + ret = try_to_realloc_ndr_blob(n, sizeof(value)); + if (ret) + return ret; + } + + *(__le16 *)ndr_get_field(n) = cpu_to_le16(value); + n->offset += sizeof(value); + return 0; +} + +static int ndr_write_int32(struct ndr *n, __u32 value) +{ + if (n->length <= n->offset + sizeof(value)) { + int ret; + + ret = try_to_realloc_ndr_blob(n, sizeof(value)); + if (ret) + return ret; + } + + *(__le32 *)ndr_get_field(n) = cpu_to_le32(value); + n->offset += sizeof(value); + return 0; +} + +static int ndr_write_int64(struct ndr *n, __u64 value) +{ + if (n->length <= n->offset + sizeof(value)) { + int ret; + + ret = try_to_realloc_ndr_blob(n, sizeof(value)); + if (ret) + return ret; + } + + *(__le64 *)ndr_get_field(n) = cpu_to_le64(value); + n->offset += sizeof(value); + return 0; +} + +static int ndr_write_bytes(struct ndr *n, void *value, size_t sz) +{ + if (n->length <= n->offset + sz) { + int ret; + + ret = try_to_realloc_ndr_blob(n, sz); + if (ret) + return ret; + } + + memcpy(ndr_get_field(n), value, sz); + n->offset += sz; + return 0; +} + +static int ndr_write_string(struct ndr *n, char *value) +{ + size_t sz; + + sz = strlen(value) + 1; + if (n->length <= n->offset + sz) { + int ret; + + ret = try_to_realloc_ndr_blob(n, sz); + if (ret) + return ret; + } + + memcpy(ndr_get_field(n), value, sz); + n->offset += sz; + n->offset = ALIGN(n->offset, 2); + return 0; +} + +static int ndr_read_string(struct ndr *n, void *value, size_t sz) +{ + int len; + + if (n->offset + sz > n->length) + return -EINVAL; + + len = strnlen(ndr_get_field(n), sz); + if (value) + memcpy(value, ndr_get_field(n), len); + len++; + n->offset += len; + n->offset = ALIGN(n->offset, 2); + return 0; +} + +static int ndr_read_bytes(struct ndr *n, void *value, size_t sz) +{ + if (n->offset + sz > n->length) + return -EINVAL; + + if (value) + memcpy(value, ndr_get_field(n), sz); + n->offset += sz; + return 0; +} + +static int ndr_read_int16(struct ndr *n, __u16 *value) +{ + if (n->offset + sizeof(__u16) > n->length) + return -EINVAL; + + if (value) + *value = le16_to_cpu(*(__le16 *)ndr_get_field(n)); + n->offset += sizeof(__u16); + return 0; +} + +static int ndr_read_int32(struct ndr *n, __u32 *value) +{ + if (n->offset + sizeof(__u32) > n->length) + return -EINVAL; + + if (value) + *value = le32_to_cpu(*(__le32 *)ndr_get_field(n)); + n->offset += sizeof(__u32); + return 0; +} + +static int ndr_read_int64(struct ndr *n, __u64 *value) +{ + if (n->offset + sizeof(__u64) > n->length) + return -EINVAL; + + if (value) + *value = le64_to_cpu(*(__le64 *)ndr_get_field(n)); + n->offset += sizeof(__u64); + return 0; +} + +int ndr_encode_dos_attr(struct ndr *n, struct xattr_dos_attrib *da) +{ + char hex_attr[12] = {0}; + int ret; + + n->offset = 0; + n->length = 1024; + n->data = kzalloc(n->length, GFP_KERNEL); + if (!n->data) + return -ENOMEM; + + if (da->version == 3) { + snprintf(hex_attr, 10, "0x%x", da->attr); + ret = ndr_write_string(n, hex_attr); + } else { + ret = ndr_write_string(n, ""); + } + if (ret) + return ret; + + ret = ndr_write_int16(n, da->version); + if (ret) + return ret; + + ret = ndr_write_int32(n, da->version); + if (ret) + return ret; + + ret = ndr_write_int32(n, da->flags); + if (ret) + return ret; + + ret = ndr_write_int32(n, da->attr); + if (ret) + return ret; + + if (da->version == 3) { + ret = ndr_write_int32(n, da->ea_size); + if (ret) + return ret; + ret = ndr_write_int64(n, da->size); + if (ret) + return ret; + ret = ndr_write_int64(n, da->alloc_size); + } else { + ret = ndr_write_int64(n, da->itime); + } + if (ret) + return ret; + + ret = ndr_write_int64(n, da->create_time); + if (ret) + return ret; + + if (da->version == 3) + ret = ndr_write_int64(n, da->change_time); + return ret; +} + +int ndr_decode_dos_attr(struct ndr *n, struct xattr_dos_attrib *da) +{ + char hex_attr[12]; + unsigned int version2; + int ret; + + n->offset = 0; + ret = ndr_read_string(n, hex_attr, sizeof(hex_attr)); + if (ret) + return ret; + + ret = ndr_read_int16(n, &da->version); + if (ret) + return ret; + + if (da->version != 3 && da->version != 4) { + ksmbd_debug(VFS, "v%d version is not supported\n", da->version); + return -EINVAL; + } + + ret = ndr_read_int32(n, &version2); + if (ret) + return ret; + + if (da->version != version2) { + ksmbd_debug(VFS, "ndr version mismatched(version: %d, version2: %d)\n", + da->version, version2); + return -EINVAL; + } + + ret = ndr_read_int32(n, NULL); + if (ret) + return ret; + + ret = ndr_read_int32(n, &da->attr); + if (ret) + return ret; + + if (da->version == 4) { + ret = ndr_read_int64(n, &da->itime); + if (ret) + return ret; + + ret = ndr_read_int64(n, &da->create_time); + } else { + ret = ndr_read_int32(n, NULL); + if (ret) + return ret; + + ret = ndr_read_int64(n, NULL); + if (ret) + return ret; + + ret = ndr_read_int64(n, NULL); + if (ret) + return ret; + + ret = ndr_read_int64(n, &da->create_time); + if (ret) + return ret; + + ret = ndr_read_int64(n, NULL); + } + + return ret; +} + +static int ndr_encode_posix_acl_entry(struct ndr *n, struct xattr_smb_acl *acl) +{ + int i, ret; + + ret = ndr_write_int32(n, acl->count); + if (ret) + return ret; + + n->offset = ALIGN(n->offset, 8); + ret = ndr_write_int32(n, acl->count); + if (ret) + return ret; + + ret = ndr_write_int32(n, 0); + if (ret) + return ret; + + for (i = 0; i < acl->count; i++) { + n->offset = ALIGN(n->offset, 8); + ret = ndr_write_int16(n, acl->entries[i].type); + if (ret) + return ret; + + ret = ndr_write_int16(n, acl->entries[i].type); + if (ret) + return ret; + + if (acl->entries[i].type == SMB_ACL_USER) { + n->offset = ALIGN(n->offset, 8); + ret = ndr_write_int64(n, acl->entries[i].uid); + } else if (acl->entries[i].type == SMB_ACL_GROUP) { + n->offset = ALIGN(n->offset, 8); + ret = ndr_write_int64(n, acl->entries[i].gid); + } + if (ret) + return ret; + + /* push permission */ + ret = ndr_write_int32(n, acl->entries[i].perm); + } + + return ret; +} + +int ndr_encode_posix_acl(struct ndr *n, + struct user_namespace *user_ns, + struct inode *inode, + struct xattr_smb_acl *acl, + struct xattr_smb_acl *def_acl) +{ + unsigned int ref_id = 0x00020000; + int ret; + vfsuid_t vfsuid; + vfsgid_t vfsgid; + + n->offset = 0; + n->length = 1024; + n->data = kzalloc(n->length, GFP_KERNEL); + if (!n->data) + return -ENOMEM; + + if (acl) { + /* ACL ACCESS */ + ret = ndr_write_int32(n, ref_id); + ref_id += 4; + } else { + ret = ndr_write_int32(n, 0); + } + if (ret) + return ret; + + if (def_acl) { + /* DEFAULT ACL ACCESS */ + ret = ndr_write_int32(n, ref_id); + ref_id += 4; + } else { + ret = ndr_write_int32(n, 0); + } + if (ret) + return ret; + + vfsuid = i_uid_into_vfsuid(user_ns, inode); + ret = ndr_write_int64(n, from_kuid(&init_user_ns, vfsuid_into_kuid(vfsuid))); + if (ret) + return ret; + vfsgid = i_gid_into_vfsgid(user_ns, inode); + ret = ndr_write_int64(n, from_kgid(&init_user_ns, vfsgid_into_kgid(vfsgid))); + if (ret) + return ret; + ret = ndr_write_int32(n, inode->i_mode); + if (ret) + return ret; + + if (acl) { + ret = ndr_encode_posix_acl_entry(n, acl); + if (def_acl && !ret) + ret = ndr_encode_posix_acl_entry(n, def_acl); + } + return ret; +} + +int ndr_encode_v4_ntacl(struct ndr *n, struct xattr_ntacl *acl) +{ + unsigned int ref_id = 0x00020004; + int ret; + + n->offset = 0; + n->length = 2048; + n->data = kzalloc(n->length, GFP_KERNEL); + if (!n->data) + return -ENOMEM; + + ret = ndr_write_int16(n, acl->version); + if (ret) + return ret; + + ret = ndr_write_int32(n, acl->version); + if (ret) + return ret; + + ret = ndr_write_int16(n, 2); + if (ret) + return ret; + + ret = ndr_write_int32(n, ref_id); + if (ret) + return ret; + + /* push hash type and hash 64bytes */ + ret = ndr_write_int16(n, acl->hash_type); + if (ret) + return ret; + + ret = ndr_write_bytes(n, acl->hash, XATTR_SD_HASH_SIZE); + if (ret) + return ret; + + ret = ndr_write_bytes(n, acl->desc, acl->desc_len); + if (ret) + return ret; + + ret = ndr_write_int64(n, acl->current_time); + if (ret) + return ret; + + ret = ndr_write_bytes(n, acl->posix_acl_hash, XATTR_SD_HASH_SIZE); + if (ret) + return ret; + + /* push ndr for security descriptor */ + ret = ndr_write_bytes(n, acl->sd_buf, acl->sd_size); + return ret; +} + +int ndr_decode_v4_ntacl(struct ndr *n, struct xattr_ntacl *acl) +{ + unsigned int version2; + int ret; + + n->offset = 0; + ret = ndr_read_int16(n, &acl->version); + if (ret) + return ret; + if (acl->version != 4) { + ksmbd_debug(VFS, "v%d version is not supported\n", acl->version); + return -EINVAL; + } + + ret = ndr_read_int32(n, &version2); + if (ret) + return ret; + if (acl->version != version2) { + ksmbd_debug(VFS, "ndr version mismatched(version: %d, version2: %d)\n", + acl->version, version2); + return -EINVAL; + } + + /* Read Level */ + ret = ndr_read_int16(n, NULL); + if (ret) + return ret; + + /* Read Ref Id */ + ret = ndr_read_int32(n, NULL); + if (ret) + return ret; + + ret = ndr_read_int16(n, &acl->hash_type); + if (ret) + return ret; + + ret = ndr_read_bytes(n, acl->hash, XATTR_SD_HASH_SIZE); + if (ret) + return ret; + + ndr_read_bytes(n, acl->desc, 10); + if (strncmp(acl->desc, "posix_acl", 9)) { + pr_err("Invalid acl description : %s\n", acl->desc); + return -EINVAL; + } + + /* Read Time */ + ret = ndr_read_int64(n, NULL); + if (ret) + return ret; + + /* Read Posix ACL hash */ + ret = ndr_read_bytes(n, acl->posix_acl_hash, XATTR_SD_HASH_SIZE); + if (ret) + return ret; + + acl->sd_size = n->length - n->offset; + acl->sd_buf = kzalloc(acl->sd_size, GFP_KERNEL); + if (!acl->sd_buf) + return -ENOMEM; + + ret = ndr_read_bytes(n, acl->sd_buf, acl->sd_size); + return ret; +} diff --git a/fs/smb/server/ndr.h b/fs/smb/server/ndr.h new file mode 100644 index 0000000000000..60ca265d1bb01 --- /dev/null +++ b/fs/smb/server/ndr.h @@ -0,0 +1,22 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + * Copyright (C) 2020 Samsung Electronics Co., Ltd. + * Author(s): Namjae Jeon + */ + +struct ndr { + char *data; + int offset; + int length; +}; + +#define NDR_NTSD_OFFSETOF 0xA0 + +int ndr_encode_dos_attr(struct ndr *n, struct xattr_dos_attrib *da); +int ndr_decode_dos_attr(struct ndr *n, struct xattr_dos_attrib *da); +int ndr_encode_posix_acl(struct ndr *n, struct user_namespace *user_ns, + struct inode *inode, struct xattr_smb_acl *acl, + struct xattr_smb_acl *def_acl); +int ndr_encode_v4_ntacl(struct ndr *n, struct xattr_ntacl *acl); +int ndr_encode_v3_ntacl(struct ndr *n, struct xattr_ntacl *acl); +int ndr_decode_v4_ntacl(struct ndr *n, struct xattr_ntacl *acl); diff --git a/fs/smb/server/nterr.h b/fs/smb/server/nterr.h new file mode 100644 index 0000000000000..2f358f88a0188 --- /dev/null +++ b/fs/smb/server/nterr.h @@ -0,0 +1,543 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + * Unix SMB/Netbios implementation. + * Version 1.9. + * NT error code constants + * Copyright (C) Andrew Tridgell 1992-2000 + * Copyright (C) John H Terpstra 1996-2000 + * Copyright (C) Luke Kenneth Casson Leighton 1996-2000 + * Copyright (C) Paul Ashton 1998-2000 + */ + +#ifndef _NTERR_H +#define _NTERR_H + +/* Win32 Status codes. */ +#define NT_STATUS_MORE_ENTRIES 0x0105 +#define NT_ERROR_INVALID_PARAMETER 0x0057 +#define NT_ERROR_INSUFFICIENT_BUFFER 0x007a +#define NT_STATUS_1804 0x070c +#define NT_STATUS_NOTIFY_ENUM_DIR 0x010c +#define NT_STATUS_INVALID_LOCK_RANGE (0xC0000000 | 0x01a1) +/* + * Win32 Error codes extracted using a loop in smbclient then printing a netmon + * sniff to a file. + */ + +#define NT_STATUS_OK 0x0000 +#define NT_STATUS_SOME_UNMAPPED 0x0107 +#define NT_STATUS_BUFFER_OVERFLOW 0x80000005 +#define NT_STATUS_NO_MORE_ENTRIES 0x8000001a +#define NT_STATUS_MEDIA_CHANGED 0x8000001c +#define NT_STATUS_END_OF_MEDIA 0x8000001e +#define NT_STATUS_MEDIA_CHECK 0x80000020 +#define NT_STATUS_NO_DATA_DETECTED 0x8000001c +#define NT_STATUS_STOPPED_ON_SYMLINK 0x8000002d +#define NT_STATUS_DEVICE_REQUIRES_CLEANING 0x80000288 +#define NT_STATUS_DEVICE_DOOR_OPEN 0x80000288 +#define NT_STATUS_UNSUCCESSFUL (0xC0000000 | 0x0001) +#define NT_STATUS_NOT_IMPLEMENTED (0xC0000000 | 0x0002) +#define NT_STATUS_INVALID_INFO_CLASS (0xC0000000 | 0x0003) +#define NT_STATUS_INFO_LENGTH_MISMATCH (0xC0000000 | 0x0004) +#define NT_STATUS_ACCESS_VIOLATION (0xC0000000 | 0x0005) +#define NT_STATUS_IN_PAGE_ERROR (0xC0000000 | 0x0006) +#define NT_STATUS_PAGEFILE_QUOTA (0xC0000000 | 0x0007) +#define NT_STATUS_INVALID_HANDLE (0xC0000000 | 0x0008) +#define NT_STATUS_BAD_INITIAL_STACK (0xC0000000 | 0x0009) +#define NT_STATUS_BAD_INITIAL_PC (0xC0000000 | 0x000a) +#define NT_STATUS_INVALID_CID (0xC0000000 | 0x000b) +#define NT_STATUS_TIMER_NOT_CANCELED (0xC0000000 | 0x000c) +#define NT_STATUS_INVALID_PARAMETER (0xC0000000 | 0x000d) +#define NT_STATUS_NO_SUCH_DEVICE (0xC0000000 | 0x000e) +#define NT_STATUS_NO_SUCH_FILE (0xC0000000 | 0x000f) +#define NT_STATUS_INVALID_DEVICE_REQUEST (0xC0000000 | 0x0010) +#define NT_STATUS_END_OF_FILE (0xC0000000 | 0x0011) +#define NT_STATUS_WRONG_VOLUME (0xC0000000 | 0x0012) +#define NT_STATUS_NO_MEDIA_IN_DEVICE (0xC0000000 | 0x0013) +#define NT_STATUS_UNRECOGNIZED_MEDIA (0xC0000000 | 0x0014) +#define NT_STATUS_NONEXISTENT_SECTOR (0xC0000000 | 0x0015) +#define NT_STATUS_MORE_PROCESSING_REQUIRED (0xC0000000 | 0x0016) +#define NT_STATUS_NO_MEMORY (0xC0000000 | 0x0017) +#define NT_STATUS_CONFLICTING_ADDRESSES (0xC0000000 | 0x0018) +#define NT_STATUS_NOT_MAPPED_VIEW (0xC0000000 | 0x0019) +#define NT_STATUS_UNABLE_TO_FREE_VM (0x80000000 | 0x001a) +#define NT_STATUS_UNABLE_TO_DELETE_SECTION (0xC0000000 | 0x001b) +#define NT_STATUS_INVALID_SYSTEM_SERVICE (0xC0000000 | 0x001c) +#define NT_STATUS_ILLEGAL_INSTRUCTION (0xC0000000 | 0x001d) +#define NT_STATUS_INVALID_LOCK_SEQUENCE (0xC0000000 | 0x001e) +#define NT_STATUS_INVALID_VIEW_SIZE (0xC0000000 | 0x001f) +#define NT_STATUS_INVALID_FILE_FOR_SECTION (0xC0000000 | 0x0020) +#define NT_STATUS_ALREADY_COMMITTED (0xC0000000 | 0x0021) +#define NT_STATUS_ACCESS_DENIED (0xC0000000 | 0x0022) +#define NT_STATUS_BUFFER_TOO_SMALL (0xC0000000 | 0x0023) +#define NT_STATUS_OBJECT_TYPE_MISMATCH (0xC0000000 | 0x0024) +#define NT_STATUS_NONCONTINUABLE_EXCEPTION (0xC0000000 | 0x0025) +#define NT_STATUS_INVALID_DISPOSITION (0xC0000000 | 0x0026) +#define NT_STATUS_UNWIND (0xC0000000 | 0x0027) +#define NT_STATUS_BAD_STACK (0xC0000000 | 0x0028) +#define NT_STATUS_INVALID_UNWIND_TARGET (0xC0000000 | 0x0029) +#define NT_STATUS_NOT_LOCKED (0xC0000000 | 0x002a) +#define NT_STATUS_PARITY_ERROR (0xC0000000 | 0x002b) +#define NT_STATUS_UNABLE_TO_DECOMMIT_VM (0xC0000000 | 0x002c) +#define NT_STATUS_NOT_COMMITTED (0xC0000000 | 0x002d) +#define NT_STATUS_INVALID_PORT_ATTRIBUTES (0xC0000000 | 0x002e) +#define NT_STATUS_PORT_MESSAGE_TOO_LONG (0xC0000000 | 0x002f) +#define NT_STATUS_INVALID_PARAMETER_MIX (0xC0000000 | 0x0030) +#define NT_STATUS_INVALID_QUOTA_LOWER (0xC0000000 | 0x0031) +#define NT_STATUS_DISK_CORRUPT_ERROR (0xC0000000 | 0x0032) +#define NT_STATUS_OBJECT_NAME_INVALID (0xC0000000 | 0x0033) +#define NT_STATUS_OBJECT_NAME_NOT_FOUND (0xC0000000 | 0x0034) +#define NT_STATUS_OBJECT_NAME_COLLISION (0xC0000000 | 0x0035) +#define NT_STATUS_HANDLE_NOT_WAITABLE (0xC0000000 | 0x0036) +#define NT_STATUS_PORT_DISCONNECTED (0xC0000000 | 0x0037) +#define NT_STATUS_DEVICE_ALREADY_ATTACHED (0xC0000000 | 0x0038) +#define NT_STATUS_OBJECT_PATH_INVALID (0xC0000000 | 0x0039) +#define NT_STATUS_OBJECT_PATH_NOT_FOUND (0xC0000000 | 0x003a) +#define NT_STATUS_OBJECT_PATH_SYNTAX_BAD (0xC0000000 | 0x003b) +#define NT_STATUS_DATA_OVERRUN (0xC0000000 | 0x003c) +#define NT_STATUS_DATA_LATE_ERROR (0xC0000000 | 0x003d) +#define NT_STATUS_DATA_ERROR (0xC0000000 | 0x003e) +#define NT_STATUS_CRC_ERROR (0xC0000000 | 0x003f) +#define NT_STATUS_SECTION_TOO_BIG (0xC0000000 | 0x0040) +#define NT_STATUS_PORT_CONNECTION_REFUSED (0xC0000000 | 0x0041) +#define NT_STATUS_INVALID_PORT_HANDLE (0xC0000000 | 0x0042) +#define NT_STATUS_SHARING_VIOLATION (0xC0000000 | 0x0043) +#define NT_STATUS_QUOTA_EXCEEDED (0xC0000000 | 0x0044) +#define NT_STATUS_INVALID_PAGE_PROTECTION (0xC0000000 | 0x0045) +#define NT_STATUS_MUTANT_NOT_OWNED (0xC0000000 | 0x0046) +#define NT_STATUS_SEMAPHORE_LIMIT_EXCEEDED (0xC0000000 | 0x0047) +#define NT_STATUS_PORT_ALREADY_SET (0xC0000000 | 0x0048) +#define NT_STATUS_SECTION_NOT_IMAGE (0xC0000000 | 0x0049) +#define NT_STATUS_SUSPEND_COUNT_EXCEEDED (0xC0000000 | 0x004a) +#define NT_STATUS_THREAD_IS_TERMINATING (0xC0000000 | 0x004b) +#define NT_STATUS_BAD_WORKING_SET_LIMIT (0xC0000000 | 0x004c) +#define NT_STATUS_INCOMPATIBLE_FILE_MAP (0xC0000000 | 0x004d) +#define NT_STATUS_SECTION_PROTECTION (0xC0000000 | 0x004e) +#define NT_STATUS_EAS_NOT_SUPPORTED (0xC0000000 | 0x004f) +#define NT_STATUS_EA_TOO_LARGE (0xC0000000 | 0x0050) +#define NT_STATUS_NONEXISTENT_EA_ENTRY (0xC0000000 | 0x0051) +#define NT_STATUS_NO_EAS_ON_FILE (0xC0000000 | 0x0052) +#define NT_STATUS_EA_CORRUPT_ERROR (0xC0000000 | 0x0053) +#define NT_STATUS_FILE_LOCK_CONFLICT (0xC0000000 | 0x0054) +#define NT_STATUS_LOCK_NOT_GRANTED (0xC0000000 | 0x0055) +#define NT_STATUS_DELETE_PENDING (0xC0000000 | 0x0056) +#define NT_STATUS_CTL_FILE_NOT_SUPPORTED (0xC0000000 | 0x0057) +#define NT_STATUS_UNKNOWN_REVISION (0xC0000000 | 0x0058) +#define NT_STATUS_REVISION_MISMATCH (0xC0000000 | 0x0059) +#define NT_STATUS_INVALID_OWNER (0xC0000000 | 0x005a) +#define NT_STATUS_INVALID_PRIMARY_GROUP (0xC0000000 | 0x005b) +#define NT_STATUS_NO_IMPERSONATION_TOKEN (0xC0000000 | 0x005c) +#define NT_STATUS_CANT_DISABLE_MANDATORY (0xC0000000 | 0x005d) +#define NT_STATUS_NO_LOGON_SERVERS (0xC0000000 | 0x005e) +#define NT_STATUS_NO_SUCH_LOGON_SESSION (0xC0000000 | 0x005f) +#define NT_STATUS_NO_SUCH_PRIVILEGE (0xC0000000 | 0x0060) +#define NT_STATUS_PRIVILEGE_NOT_HELD (0xC0000000 | 0x0061) +#define NT_STATUS_INVALID_ACCOUNT_NAME (0xC0000000 | 0x0062) +#define NT_STATUS_USER_EXISTS (0xC0000000 | 0x0063) +#define NT_STATUS_NO_SUCH_USER (0xC0000000 | 0x0064) +#define NT_STATUS_GROUP_EXISTS (0xC0000000 | 0x0065) +#define NT_STATUS_NO_SUCH_GROUP (0xC0000000 | 0x0066) +#define NT_STATUS_MEMBER_IN_GROUP (0xC0000000 | 0x0067) +#define NT_STATUS_MEMBER_NOT_IN_GROUP (0xC0000000 | 0x0068) +#define NT_STATUS_LAST_ADMIN (0xC0000000 | 0x0069) +#define NT_STATUS_WRONG_PASSWORD (0xC0000000 | 0x006a) +#define NT_STATUS_ILL_FORMED_PASSWORD (0xC0000000 | 0x006b) +#define NT_STATUS_PASSWORD_RESTRICTION (0xC0000000 | 0x006c) +#define NT_STATUS_LOGON_FAILURE (0xC0000000 | 0x006d) +#define NT_STATUS_ACCOUNT_RESTRICTION (0xC0000000 | 0x006e) +#define NT_STATUS_INVALID_LOGON_HOURS (0xC0000000 | 0x006f) +#define NT_STATUS_INVALID_WORKSTATION (0xC0000000 | 0x0070) +#define NT_STATUS_PASSWORD_EXPIRED (0xC0000000 | 0x0071) +#define NT_STATUS_ACCOUNT_DISABLED (0xC0000000 | 0x0072) +#define NT_STATUS_NONE_MAPPED (0xC0000000 | 0x0073) +#define NT_STATUS_TOO_MANY_LUIDS_REQUESTED (0xC0000000 | 0x0074) +#define NT_STATUS_LUIDS_EXHAUSTED (0xC0000000 | 0x0075) +#define NT_STATUS_INVALID_SUB_AUTHORITY (0xC0000000 | 0x0076) +#define NT_STATUS_INVALID_ACL (0xC0000000 | 0x0077) +#define NT_STATUS_INVALID_SID (0xC0000000 | 0x0078) +#define NT_STATUS_INVALID_SECURITY_DESCR (0xC0000000 | 0x0079) +#define NT_STATUS_PROCEDURE_NOT_FOUND (0xC0000000 | 0x007a) +#define NT_STATUS_INVALID_IMAGE_FORMAT (0xC0000000 | 0x007b) +#define NT_STATUS_NO_TOKEN (0xC0000000 | 0x007c) +#define NT_STATUS_BAD_INHERITANCE_ACL (0xC0000000 | 0x007d) +#define NT_STATUS_RANGE_NOT_LOCKED (0xC0000000 | 0x007e) +#define NT_STATUS_DISK_FULL (0xC0000000 | 0x007f) +#define NT_STATUS_SERVER_DISABLED (0xC0000000 | 0x0080) +#define NT_STATUS_SERVER_NOT_DISABLED (0xC0000000 | 0x0081) +#define NT_STATUS_TOO_MANY_GUIDS_REQUESTED (0xC0000000 | 0x0082) +#define NT_STATUS_GUIDS_EXHAUSTED (0xC0000000 | 0x0083) +#define NT_STATUS_INVALID_ID_AUTHORITY (0xC0000000 | 0x0084) +#define NT_STATUS_AGENTS_EXHAUSTED (0xC0000000 | 0x0085) +#define NT_STATUS_INVALID_VOLUME_LABEL (0xC0000000 | 0x0086) +#define NT_STATUS_SECTION_NOT_EXTENDED (0xC0000000 | 0x0087) +#define NT_STATUS_NOT_MAPPED_DATA (0xC0000000 | 0x0088) +#define NT_STATUS_RESOURCE_DATA_NOT_FOUND (0xC0000000 | 0x0089) +#define NT_STATUS_RESOURCE_TYPE_NOT_FOUND (0xC0000000 | 0x008a) +#define NT_STATUS_RESOURCE_NAME_NOT_FOUND (0xC0000000 | 0x008b) +#define NT_STATUS_ARRAY_BOUNDS_EXCEEDED (0xC0000000 | 0x008c) +#define NT_STATUS_FLOAT_DENORMAL_OPERAND (0xC0000000 | 0x008d) +#define NT_STATUS_FLOAT_DIVIDE_BY_ZERO (0xC0000000 | 0x008e) +#define NT_STATUS_FLOAT_INEXACT_RESULT (0xC0000000 | 0x008f) +#define NT_STATUS_FLOAT_INVALID_OPERATION (0xC0000000 | 0x0090) +#define NT_STATUS_FLOAT_OVERFLOW (0xC0000000 | 0x0091) +#define NT_STATUS_FLOAT_STACK_CHECK (0xC0000000 | 0x0092) +#define NT_STATUS_FLOAT_UNDERFLOW (0xC0000000 | 0x0093) +#define NT_STATUS_INTEGER_DIVIDE_BY_ZERO (0xC0000000 | 0x0094) +#define NT_STATUS_INTEGER_OVERFLOW (0xC0000000 | 0x0095) +#define NT_STATUS_PRIVILEGED_INSTRUCTION (0xC0000000 | 0x0096) +#define NT_STATUS_TOO_MANY_PAGING_FILES (0xC0000000 | 0x0097) +#define NT_STATUS_FILE_INVALID (0xC0000000 | 0x0098) +#define NT_STATUS_ALLOTTED_SPACE_EXCEEDED (0xC0000000 | 0x0099) +#define NT_STATUS_INSUFFICIENT_RESOURCES (0xC0000000 | 0x009a) +#define NT_STATUS_DFS_EXIT_PATH_FOUND (0xC0000000 | 0x009b) +#define NT_STATUS_DEVICE_DATA_ERROR (0xC0000000 | 0x009c) +#define NT_STATUS_DEVICE_NOT_CONNECTED (0xC0000000 | 0x009d) +#define NT_STATUS_DEVICE_POWER_FAILURE (0xC0000000 | 0x009e) +#define NT_STATUS_FREE_VM_NOT_AT_BASE (0xC0000000 | 0x009f) +#define NT_STATUS_MEMORY_NOT_ALLOCATED (0xC0000000 | 0x00a0) +#define NT_STATUS_WORKING_SET_QUOTA (0xC0000000 | 0x00a1) +#define NT_STATUS_MEDIA_WRITE_PROTECTED (0xC0000000 | 0x00a2) +#define NT_STATUS_DEVICE_NOT_READY (0xC0000000 | 0x00a3) +#define NT_STATUS_INVALID_GROUP_ATTRIBUTES (0xC0000000 | 0x00a4) +#define NT_STATUS_BAD_IMPERSONATION_LEVEL (0xC0000000 | 0x00a5) +#define NT_STATUS_CANT_OPEN_ANONYMOUS (0xC0000000 | 0x00a6) +#define NT_STATUS_BAD_VALIDATION_CLASS (0xC0000000 | 0x00a7) +#define NT_STATUS_BAD_TOKEN_TYPE (0xC0000000 | 0x00a8) +#define NT_STATUS_BAD_MASTER_BOOT_RECORD (0xC0000000 | 0x00a9) +#define NT_STATUS_INSTRUCTION_MISALIGNMENT (0xC0000000 | 0x00aa) +#define NT_STATUS_INSTANCE_NOT_AVAILABLE (0xC0000000 | 0x00ab) +#define NT_STATUS_PIPE_NOT_AVAILABLE (0xC0000000 | 0x00ac) +#define NT_STATUS_INVALID_PIPE_STATE (0xC0000000 | 0x00ad) +#define NT_STATUS_PIPE_BUSY (0xC0000000 | 0x00ae) +#define NT_STATUS_ILLEGAL_FUNCTION (0xC0000000 | 0x00af) +#define NT_STATUS_PIPE_DISCONNECTED (0xC0000000 | 0x00b0) +#define NT_STATUS_PIPE_CLOSING (0xC0000000 | 0x00b1) +#define NT_STATUS_PIPE_CONNECTED (0xC0000000 | 0x00b2) +#define NT_STATUS_PIPE_LISTENING (0xC0000000 | 0x00b3) +#define NT_STATUS_INVALID_READ_MODE (0xC0000000 | 0x00b4) +#define NT_STATUS_IO_TIMEOUT (0xC0000000 | 0x00b5) +#define NT_STATUS_FILE_FORCED_CLOSED (0xC0000000 | 0x00b6) +#define NT_STATUS_PROFILING_NOT_STARTED (0xC0000000 | 0x00b7) +#define NT_STATUS_PROFILING_NOT_STOPPED (0xC0000000 | 0x00b8) +#define NT_STATUS_COULD_NOT_INTERPRET (0xC0000000 | 0x00b9) +#define NT_STATUS_FILE_IS_A_DIRECTORY (0xC0000000 | 0x00ba) +#define NT_STATUS_NOT_SUPPORTED (0xC0000000 | 0x00bb) +#define NT_STATUS_REMOTE_NOT_LISTENING (0xC0000000 | 0x00bc) +#define NT_STATUS_DUPLICATE_NAME (0xC0000000 | 0x00bd) +#define NT_STATUS_BAD_NETWORK_PATH (0xC0000000 | 0x00be) +#define NT_STATUS_NETWORK_BUSY (0xC0000000 | 0x00bf) +#define NT_STATUS_DEVICE_DOES_NOT_EXIST (0xC0000000 | 0x00c0) +#define NT_STATUS_TOO_MANY_COMMANDS (0xC0000000 | 0x00c1) +#define NT_STATUS_ADAPTER_HARDWARE_ERROR (0xC0000000 | 0x00c2) +#define NT_STATUS_INVALID_NETWORK_RESPONSE (0xC0000000 | 0x00c3) +#define NT_STATUS_UNEXPECTED_NETWORK_ERROR (0xC0000000 | 0x00c4) +#define NT_STATUS_BAD_REMOTE_ADAPTER (0xC0000000 | 0x00c5) +#define NT_STATUS_PRINT_QUEUE_FULL (0xC0000000 | 0x00c6) +#define NT_STATUS_NO_SPOOL_SPACE (0xC0000000 | 0x00c7) +#define NT_STATUS_PRINT_CANCELLED (0xC0000000 | 0x00c8) +#define NT_STATUS_NETWORK_NAME_DELETED (0xC0000000 | 0x00c9) +#define NT_STATUS_NETWORK_ACCESS_DENIED (0xC0000000 | 0x00ca) +#define NT_STATUS_BAD_DEVICE_TYPE (0xC0000000 | 0x00cb) +#define NT_STATUS_BAD_NETWORK_NAME (0xC0000000 | 0x00cc) +#define NT_STATUS_TOO_MANY_NAMES (0xC0000000 | 0x00cd) +#define NT_STATUS_TOO_MANY_SESSIONS (0xC0000000 | 0x00ce) +#define NT_STATUS_SHARING_PAUSED (0xC0000000 | 0x00cf) +#define NT_STATUS_REQUEST_NOT_ACCEPTED (0xC0000000 | 0x00d0) +#define NT_STATUS_REDIRECTOR_PAUSED (0xC0000000 | 0x00d1) +#define NT_STATUS_NET_WRITE_FAULT (0xC0000000 | 0x00d2) +#define NT_STATUS_PROFILING_AT_LIMIT (0xC0000000 | 0x00d3) +#define NT_STATUS_NOT_SAME_DEVICE (0xC0000000 | 0x00d4) +#define NT_STATUS_FILE_RENAMED (0xC0000000 | 0x00d5) +#define NT_STATUS_VIRTUAL_CIRCUIT_CLOSED (0xC0000000 | 0x00d6) +#define NT_STATUS_NO_SECURITY_ON_OBJECT (0xC0000000 | 0x00d7) +#define NT_STATUS_CANT_WAIT (0xC0000000 | 0x00d8) +#define NT_STATUS_PIPE_EMPTY (0xC0000000 | 0x00d9) +#define NT_STATUS_CANT_ACCESS_DOMAIN_INFO (0xC0000000 | 0x00da) +#define NT_STATUS_CANT_TERMINATE_SELF (0xC0000000 | 0x00db) +#define NT_STATUS_INVALID_SERVER_STATE (0xC0000000 | 0x00dc) +#define NT_STATUS_INVALID_DOMAIN_STATE (0xC0000000 | 0x00dd) +#define NT_STATUS_INVALID_DOMAIN_ROLE (0xC0000000 | 0x00de) +#define NT_STATUS_NO_SUCH_DOMAIN (0xC0000000 | 0x00df) +#define NT_STATUS_DOMAIN_EXISTS (0xC0000000 | 0x00e0) +#define NT_STATUS_DOMAIN_LIMIT_EXCEEDED (0xC0000000 | 0x00e1) +#define NT_STATUS_OPLOCK_NOT_GRANTED (0xC0000000 | 0x00e2) +#define NT_STATUS_INVALID_OPLOCK_PROTOCOL (0xC0000000 | 0x00e3) +#define NT_STATUS_INTERNAL_DB_CORRUPTION (0xC0000000 | 0x00e4) +#define NT_STATUS_INTERNAL_ERROR (0xC0000000 | 0x00e5) +#define NT_STATUS_GENERIC_NOT_MAPPED (0xC0000000 | 0x00e6) +#define NT_STATUS_BAD_DESCRIPTOR_FORMAT (0xC0000000 | 0x00e7) +#define NT_STATUS_INVALID_USER_BUFFER (0xC0000000 | 0x00e8) +#define NT_STATUS_UNEXPECTED_IO_ERROR (0xC0000000 | 0x00e9) +#define NT_STATUS_UNEXPECTED_MM_CREATE_ERR (0xC0000000 | 0x00ea) +#define NT_STATUS_UNEXPECTED_MM_MAP_ERROR (0xC0000000 | 0x00eb) +#define NT_STATUS_UNEXPECTED_MM_EXTEND_ERR (0xC0000000 | 0x00ec) +#define NT_STATUS_NOT_LOGON_PROCESS (0xC0000000 | 0x00ed) +#define NT_STATUS_LOGON_SESSION_EXISTS (0xC0000000 | 0x00ee) +#define NT_STATUS_INVALID_PARAMETER_1 (0xC0000000 | 0x00ef) +#define NT_STATUS_INVALID_PARAMETER_2 (0xC0000000 | 0x00f0) +#define NT_STATUS_INVALID_PARAMETER_3 (0xC0000000 | 0x00f1) +#define NT_STATUS_INVALID_PARAMETER_4 (0xC0000000 | 0x00f2) +#define NT_STATUS_INVALID_PARAMETER_5 (0xC0000000 | 0x00f3) +#define NT_STATUS_INVALID_PARAMETER_6 (0xC0000000 | 0x00f4) +#define NT_STATUS_INVALID_PARAMETER_7 (0xC0000000 | 0x00f5) +#define NT_STATUS_INVALID_PARAMETER_8 (0xC0000000 | 0x00f6) +#define NT_STATUS_INVALID_PARAMETER_9 (0xC0000000 | 0x00f7) +#define NT_STATUS_INVALID_PARAMETER_10 (0xC0000000 | 0x00f8) +#define NT_STATUS_INVALID_PARAMETER_11 (0xC0000000 | 0x00f9) +#define NT_STATUS_INVALID_PARAMETER_12 (0xC0000000 | 0x00fa) +#define NT_STATUS_REDIRECTOR_NOT_STARTED (0xC0000000 | 0x00fb) +#define NT_STATUS_REDIRECTOR_STARTED (0xC0000000 | 0x00fc) +#define NT_STATUS_STACK_OVERFLOW (0xC0000000 | 0x00fd) +#define NT_STATUS_NO_SUCH_PACKAGE (0xC0000000 | 0x00fe) +#define NT_STATUS_BAD_FUNCTION_TABLE (0xC0000000 | 0x00ff) +#define NT_STATUS_DIRECTORY_NOT_EMPTY (0xC0000000 | 0x0101) +#define NT_STATUS_FILE_CORRUPT_ERROR (0xC0000000 | 0x0102) +#define NT_STATUS_NOT_A_DIRECTORY (0xC0000000 | 0x0103) +#define NT_STATUS_BAD_LOGON_SESSION_STATE (0xC0000000 | 0x0104) +#define NT_STATUS_LOGON_SESSION_COLLISION (0xC0000000 | 0x0105) +#define NT_STATUS_NAME_TOO_LONG (0xC0000000 | 0x0106) +#define NT_STATUS_FILES_OPEN (0xC0000000 | 0x0107) +#define NT_STATUS_CONNECTION_IN_USE (0xC0000000 | 0x0108) +#define NT_STATUS_MESSAGE_NOT_FOUND (0xC0000000 | 0x0109) +#define NT_STATUS_PROCESS_IS_TERMINATING (0xC0000000 | 0x010a) +#define NT_STATUS_INVALID_LOGON_TYPE (0xC0000000 | 0x010b) +#define NT_STATUS_NO_GUID_TRANSLATION (0xC0000000 | 0x010c) +#define NT_STATUS_CANNOT_IMPERSONATE (0xC0000000 | 0x010d) +#define NT_STATUS_IMAGE_ALREADY_LOADED (0xC0000000 | 0x010e) +#define NT_STATUS_ABIOS_NOT_PRESENT (0xC0000000 | 0x010f) +#define NT_STATUS_ABIOS_LID_NOT_EXIST (0xC0000000 | 0x0110) +#define NT_STATUS_ABIOS_LID_ALREADY_OWNED (0xC0000000 | 0x0111) +#define NT_STATUS_ABIOS_NOT_LID_OWNER (0xC0000000 | 0x0112) +#define NT_STATUS_ABIOS_INVALID_COMMAND (0xC0000000 | 0x0113) +#define NT_STATUS_ABIOS_INVALID_LID (0xC0000000 | 0x0114) +#define NT_STATUS_ABIOS_SELECTOR_NOT_AVAILABLE (0xC0000000 | 0x0115) +#define NT_STATUS_ABIOS_INVALID_SELECTOR (0xC0000000 | 0x0116) +#define NT_STATUS_NO_LDT (0xC0000000 | 0x0117) +#define NT_STATUS_INVALID_LDT_SIZE (0xC0000000 | 0x0118) +#define NT_STATUS_INVALID_LDT_OFFSET (0xC0000000 | 0x0119) +#define NT_STATUS_INVALID_LDT_DESCRIPTOR (0xC0000000 | 0x011a) +#define NT_STATUS_INVALID_IMAGE_NE_FORMAT (0xC0000000 | 0x011b) +#define NT_STATUS_RXACT_INVALID_STATE (0xC0000000 | 0x011c) +#define NT_STATUS_RXACT_COMMIT_FAILURE (0xC0000000 | 0x011d) +#define NT_STATUS_MAPPED_FILE_SIZE_ZERO (0xC0000000 | 0x011e) +#define NT_STATUS_TOO_MANY_OPENED_FILES (0xC0000000 | 0x011f) +#define NT_STATUS_CANCELLED (0xC0000000 | 0x0120) +#define NT_STATUS_CANNOT_DELETE (0xC0000000 | 0x0121) +#define NT_STATUS_INVALID_COMPUTER_NAME (0xC0000000 | 0x0122) +#define NT_STATUS_FILE_DELETED (0xC0000000 | 0x0123) +#define NT_STATUS_SPECIAL_ACCOUNT (0xC0000000 | 0x0124) +#define NT_STATUS_SPECIAL_GROUP (0xC0000000 | 0x0125) +#define NT_STATUS_SPECIAL_USER (0xC0000000 | 0x0126) +#define NT_STATUS_MEMBERS_PRIMARY_GROUP (0xC0000000 | 0x0127) +#define NT_STATUS_FILE_CLOSED (0xC0000000 | 0x0128) +#define NT_STATUS_TOO_MANY_THREADS (0xC0000000 | 0x0129) +#define NT_STATUS_THREAD_NOT_IN_PROCESS (0xC0000000 | 0x012a) +#define NT_STATUS_TOKEN_ALREADY_IN_USE (0xC0000000 | 0x012b) +#define NT_STATUS_PAGEFILE_QUOTA_EXCEEDED (0xC0000000 | 0x012c) +#define NT_STATUS_COMMITMENT_LIMIT (0xC0000000 | 0x012d) +#define NT_STATUS_INVALID_IMAGE_LE_FORMAT (0xC0000000 | 0x012e) +#define NT_STATUS_INVALID_IMAGE_NOT_MZ (0xC0000000 | 0x012f) +#define NT_STATUS_INVALID_IMAGE_PROTECT (0xC0000000 | 0x0130) +#define NT_STATUS_INVALID_IMAGE_WIN_16 (0xC0000000 | 0x0131) +#define NT_STATUS_LOGON_SERVER_CONFLICT (0xC0000000 | 0x0132) +#define NT_STATUS_TIME_DIFFERENCE_AT_DC (0xC0000000 | 0x0133) +#define NT_STATUS_SYNCHRONIZATION_REQUIRED (0xC0000000 | 0x0134) +#define NT_STATUS_DLL_NOT_FOUND (0xC0000000 | 0x0135) +#define NT_STATUS_OPEN_FAILED (0xC0000000 | 0x0136) +#define NT_STATUS_IO_PRIVILEGE_FAILED (0xC0000000 | 0x0137) +#define NT_STATUS_ORDINAL_NOT_FOUND (0xC0000000 | 0x0138) +#define NT_STATUS_ENTRYPOINT_NOT_FOUND (0xC0000000 | 0x0139) +#define NT_STATUS_CONTROL_C_EXIT (0xC0000000 | 0x013a) +#define NT_STATUS_LOCAL_DISCONNECT (0xC0000000 | 0x013b) +#define NT_STATUS_REMOTE_DISCONNECT (0xC0000000 | 0x013c) +#define NT_STATUS_REMOTE_RESOURCES (0xC0000000 | 0x013d) +#define NT_STATUS_LINK_FAILED (0xC0000000 | 0x013e) +#define NT_STATUS_LINK_TIMEOUT (0xC0000000 | 0x013f) +#define NT_STATUS_INVALID_CONNECTION (0xC0000000 | 0x0140) +#define NT_STATUS_INVALID_ADDRESS (0xC0000000 | 0x0141) +#define NT_STATUS_DLL_INIT_FAILED (0xC0000000 | 0x0142) +#define NT_STATUS_MISSING_SYSTEMFILE (0xC0000000 | 0x0143) +#define NT_STATUS_UNHANDLED_EXCEPTION (0xC0000000 | 0x0144) +#define NT_STATUS_APP_INIT_FAILURE (0xC0000000 | 0x0145) +#define NT_STATUS_PAGEFILE_CREATE_FAILED (0xC0000000 | 0x0146) +#define NT_STATUS_NO_PAGEFILE (0xC0000000 | 0x0147) +#define NT_STATUS_INVALID_LEVEL (0xC0000000 | 0x0148) +#define NT_STATUS_WRONG_PASSWORD_CORE (0xC0000000 | 0x0149) +#define NT_STATUS_ILLEGAL_FLOAT_CONTEXT (0xC0000000 | 0x014a) +#define NT_STATUS_PIPE_BROKEN (0xC0000000 | 0x014b) +#define NT_STATUS_REGISTRY_CORRUPT (0xC0000000 | 0x014c) +#define NT_STATUS_REGISTRY_IO_FAILED (0xC0000000 | 0x014d) +#define NT_STATUS_NO_EVENT_PAIR (0xC0000000 | 0x014e) +#define NT_STATUS_UNRECOGNIZED_VOLUME (0xC0000000 | 0x014f) +#define NT_STATUS_SERIAL_NO_DEVICE_INITED (0xC0000000 | 0x0150) +#define NT_STATUS_NO_SUCH_ALIAS (0xC0000000 | 0x0151) +#define NT_STATUS_MEMBER_NOT_IN_ALIAS (0xC0000000 | 0x0152) +#define NT_STATUS_MEMBER_IN_ALIAS (0xC0000000 | 0x0153) +#define NT_STATUS_ALIAS_EXISTS (0xC0000000 | 0x0154) +#define NT_STATUS_LOGON_NOT_GRANTED (0xC0000000 | 0x0155) +#define NT_STATUS_TOO_MANY_SECRETS (0xC0000000 | 0x0156) +#define NT_STATUS_SECRET_TOO_LONG (0xC0000000 | 0x0157) +#define NT_STATUS_INTERNAL_DB_ERROR (0xC0000000 | 0x0158) +#define NT_STATUS_FULLSCREEN_MODE (0xC0000000 | 0x0159) +#define NT_STATUS_TOO_MANY_CONTEXT_IDS (0xC0000000 | 0x015a) +#define NT_STATUS_LOGON_TYPE_NOT_GRANTED (0xC0000000 | 0x015b) +#define NT_STATUS_NOT_REGISTRY_FILE (0xC0000000 | 0x015c) +#define NT_STATUS_NT_CROSS_ENCRYPTION_REQUIRED (0xC0000000 | 0x015d) +#define NT_STATUS_DOMAIN_CTRLR_CONFIG_ERROR (0xC0000000 | 0x015e) +#define NT_STATUS_FT_MISSING_MEMBER (0xC0000000 | 0x015f) +#define NT_STATUS_ILL_FORMED_SERVICE_ENTRY (0xC0000000 | 0x0160) +#define NT_STATUS_ILLEGAL_CHARACTER (0xC0000000 | 0x0161) +#define NT_STATUS_UNMAPPABLE_CHARACTER (0xC0000000 | 0x0162) +#define NT_STATUS_UNDEFINED_CHARACTER (0xC0000000 | 0x0163) +#define NT_STATUS_FLOPPY_VOLUME (0xC0000000 | 0x0164) +#define NT_STATUS_FLOPPY_ID_MARK_NOT_FOUND (0xC0000000 | 0x0165) +#define NT_STATUS_FLOPPY_WRONG_CYLINDER (0xC0000000 | 0x0166) +#define NT_STATUS_FLOPPY_UNKNOWN_ERROR (0xC0000000 | 0x0167) +#define NT_STATUS_FLOPPY_BAD_REGISTERS (0xC0000000 | 0x0168) +#define NT_STATUS_DISK_RECALIBRATE_FAILED (0xC0000000 | 0x0169) +#define NT_STATUS_DISK_OPERATION_FAILED (0xC0000000 | 0x016a) +#define NT_STATUS_DISK_RESET_FAILED (0xC0000000 | 0x016b) +#define NT_STATUS_SHARED_IRQ_BUSY (0xC0000000 | 0x016c) +#define NT_STATUS_FT_ORPHANING (0xC0000000 | 0x016d) +#define NT_STATUS_PARTITION_FAILURE (0xC0000000 | 0x0172) +#define NT_STATUS_INVALID_BLOCK_LENGTH (0xC0000000 | 0x0173) +#define NT_STATUS_DEVICE_NOT_PARTITIONED (0xC0000000 | 0x0174) +#define NT_STATUS_UNABLE_TO_LOCK_MEDIA (0xC0000000 | 0x0175) +#define NT_STATUS_UNABLE_TO_UNLOAD_MEDIA (0xC0000000 | 0x0176) +#define NT_STATUS_EOM_OVERFLOW (0xC0000000 | 0x0177) +#define NT_STATUS_NO_MEDIA (0xC0000000 | 0x0178) +#define NT_STATUS_NO_SUCH_MEMBER (0xC0000000 | 0x017a) +#define NT_STATUS_INVALID_MEMBER (0xC0000000 | 0x017b) +#define NT_STATUS_KEY_DELETED (0xC0000000 | 0x017c) +#define NT_STATUS_NO_LOG_SPACE (0xC0000000 | 0x017d) +#define NT_STATUS_TOO_MANY_SIDS (0xC0000000 | 0x017e) +#define NT_STATUS_LM_CROSS_ENCRYPTION_REQUIRED (0xC0000000 | 0x017f) +#define NT_STATUS_KEY_HAS_CHILDREN (0xC0000000 | 0x0180) +#define NT_STATUS_CHILD_MUST_BE_VOLATILE (0xC0000000 | 0x0181) +#define NT_STATUS_DEVICE_CONFIGURATION_ERROR (0xC0000000 | 0x0182) +#define NT_STATUS_DRIVER_INTERNAL_ERROR (0xC0000000 | 0x0183) +#define NT_STATUS_INVALID_DEVICE_STATE (0xC0000000 | 0x0184) +#define NT_STATUS_IO_DEVICE_ERROR (0xC0000000 | 0x0185) +#define NT_STATUS_DEVICE_PROTOCOL_ERROR (0xC0000000 | 0x0186) +#define NT_STATUS_BACKUP_CONTROLLER (0xC0000000 | 0x0187) +#define NT_STATUS_LOG_FILE_FULL (0xC0000000 | 0x0188) +#define NT_STATUS_TOO_LATE (0xC0000000 | 0x0189) +#define NT_STATUS_NO_TRUST_LSA_SECRET (0xC0000000 | 0x018a) +#define NT_STATUS_NO_TRUST_SAM_ACCOUNT (0xC0000000 | 0x018b) +#define NT_STATUS_TRUSTED_DOMAIN_FAILURE (0xC0000000 | 0x018c) +#define NT_STATUS_TRUSTED_RELATIONSHIP_FAILURE (0xC0000000 | 0x018d) +#define NT_STATUS_EVENTLOG_FILE_CORRUPT (0xC0000000 | 0x018e) +#define NT_STATUS_EVENTLOG_CANT_START (0xC0000000 | 0x018f) +#define NT_STATUS_TRUST_FAILURE (0xC0000000 | 0x0190) +#define NT_STATUS_MUTANT_LIMIT_EXCEEDED (0xC0000000 | 0x0191) +#define NT_STATUS_NETLOGON_NOT_STARTED (0xC0000000 | 0x0192) +#define NT_STATUS_ACCOUNT_EXPIRED (0xC0000000 | 0x0193) +#define NT_STATUS_POSSIBLE_DEADLOCK (0xC0000000 | 0x0194) +#define NT_STATUS_NETWORK_CREDENTIAL_CONFLICT (0xC0000000 | 0x0195) +#define NT_STATUS_REMOTE_SESSION_LIMIT (0xC0000000 | 0x0196) +#define NT_STATUS_EVENTLOG_FILE_CHANGED (0xC0000000 | 0x0197) +#define NT_STATUS_NOLOGON_INTERDOMAIN_TRUST_ACCOUNT (0xC0000000 | 0x0198) +#define NT_STATUS_NOLOGON_WORKSTATION_TRUST_ACCOUNT (0xC0000000 | 0x0199) +#define NT_STATUS_NOLOGON_SERVER_TRUST_ACCOUNT (0xC0000000 | 0x019a) +#define NT_STATUS_DOMAIN_TRUST_INCONSISTENT (0xC0000000 | 0x019b) +#define NT_STATUS_FS_DRIVER_REQUIRED (0xC0000000 | 0x019c) +#define NT_STATUS_NO_USER_SESSION_KEY (0xC0000000 | 0x0202) +#define NT_STATUS_USER_SESSION_DELETED (0xC0000000 | 0x0203) +#define NT_STATUS_RESOURCE_LANG_NOT_FOUND (0xC0000000 | 0x0204) +#define NT_STATUS_INSUFF_SERVER_RESOURCES (0xC0000000 | 0x0205) +#define NT_STATUS_INVALID_BUFFER_SIZE (0xC0000000 | 0x0206) +#define NT_STATUS_INVALID_ADDRESS_COMPONENT (0xC0000000 | 0x0207) +#define NT_STATUS_INVALID_ADDRESS_WILDCARD (0xC0000000 | 0x0208) +#define NT_STATUS_TOO_MANY_ADDRESSES (0xC0000000 | 0x0209) +#define NT_STATUS_ADDRESS_ALREADY_EXISTS (0xC0000000 | 0x020a) +#define NT_STATUS_ADDRESS_CLOSED (0xC0000000 | 0x020b) +#define NT_STATUS_CONNECTION_DISCONNECTED (0xC0000000 | 0x020c) +#define NT_STATUS_CONNECTION_RESET (0xC0000000 | 0x020d) +#define NT_STATUS_TOO_MANY_NODES (0xC0000000 | 0x020e) +#define NT_STATUS_TRANSACTION_ABORTED (0xC0000000 | 0x020f) +#define NT_STATUS_TRANSACTION_TIMED_OUT (0xC0000000 | 0x0210) +#define NT_STATUS_TRANSACTION_NO_RELEASE (0xC0000000 | 0x0211) +#define NT_STATUS_TRANSACTION_NO_MATCH (0xC0000000 | 0x0212) +#define NT_STATUS_TRANSACTION_RESPONDED (0xC0000000 | 0x0213) +#define NT_STATUS_TRANSACTION_INVALID_ID (0xC0000000 | 0x0214) +#define NT_STATUS_TRANSACTION_INVALID_TYPE (0xC0000000 | 0x0215) +#define NT_STATUS_NOT_SERVER_SESSION (0xC0000000 | 0x0216) +#define NT_STATUS_NOT_CLIENT_SESSION (0xC0000000 | 0x0217) +#define NT_STATUS_CANNOT_LOAD_REGISTRY_FILE (0xC0000000 | 0x0218) +#define NT_STATUS_DEBUG_ATTACH_FAILED (0xC0000000 | 0x0219) +#define NT_STATUS_SYSTEM_PROCESS_TERMINATED (0xC0000000 | 0x021a) +#define NT_STATUS_DATA_NOT_ACCEPTED (0xC0000000 | 0x021b) +#define NT_STATUS_NO_BROWSER_SERVERS_FOUND (0xC0000000 | 0x021c) +#define NT_STATUS_VDM_HARD_ERROR (0xC0000000 | 0x021d) +#define NT_STATUS_DRIVER_CANCEL_TIMEOUT (0xC0000000 | 0x021e) +#define NT_STATUS_REPLY_MESSAGE_MISMATCH (0xC0000000 | 0x021f) +#define NT_STATUS_MAPPED_ALIGNMENT (0xC0000000 | 0x0220) +#define NT_STATUS_IMAGE_CHECKSUM_MISMATCH (0xC0000000 | 0x0221) +#define NT_STATUS_LOST_WRITEBEHIND_DATA (0xC0000000 | 0x0222) +#define NT_STATUS_CLIENT_SERVER_PARAMETERS_INVALID (0xC0000000 | 0x0223) +#define NT_STATUS_PASSWORD_MUST_CHANGE (0xC0000000 | 0x0224) +#define NT_STATUS_NOT_FOUND (0xC0000000 | 0x0225) +#define NT_STATUS_NOT_TINY_STREAM (0xC0000000 | 0x0226) +#define NT_STATUS_RECOVERY_FAILURE (0xC0000000 | 0x0227) +#define NT_STATUS_STACK_OVERFLOW_READ (0xC0000000 | 0x0228) +#define NT_STATUS_FAIL_CHECK (0xC0000000 | 0x0229) +#define NT_STATUS_DUPLICATE_OBJECTID (0xC0000000 | 0x022a) +#define NT_STATUS_OBJECTID_EXISTS (0xC0000000 | 0x022b) +#define NT_STATUS_CONVERT_TO_LARGE (0xC0000000 | 0x022c) +#define NT_STATUS_RETRY (0xC0000000 | 0x022d) +#define NT_STATUS_FOUND_OUT_OF_SCOPE (0xC0000000 | 0x022e) +#define NT_STATUS_ALLOCATE_BUCKET (0xC0000000 | 0x022f) +#define NT_STATUS_PROPSET_NOT_FOUND (0xC0000000 | 0x0230) +#define NT_STATUS_MARSHALL_OVERFLOW (0xC0000000 | 0x0231) +#define NT_STATUS_INVALID_VARIANT (0xC0000000 | 0x0232) +#define NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND (0xC0000000 | 0x0233) +#define NT_STATUS_ACCOUNT_LOCKED_OUT (0xC0000000 | 0x0234) +#define NT_STATUS_HANDLE_NOT_CLOSABLE (0xC0000000 | 0x0235) +#define NT_STATUS_CONNECTION_REFUSED (0xC0000000 | 0x0236) +#define NT_STATUS_GRACEFUL_DISCONNECT (0xC0000000 | 0x0237) +#define NT_STATUS_ADDRESS_ALREADY_ASSOCIATED (0xC0000000 | 0x0238) +#define NT_STATUS_ADDRESS_NOT_ASSOCIATED (0xC0000000 | 0x0239) +#define NT_STATUS_CONNECTION_INVALID (0xC0000000 | 0x023a) +#define NT_STATUS_CONNECTION_ACTIVE (0xC0000000 | 0x023b) +#define NT_STATUS_NETWORK_UNREACHABLE (0xC0000000 | 0x023c) +#define NT_STATUS_HOST_UNREACHABLE (0xC0000000 | 0x023d) +#define NT_STATUS_PROTOCOL_UNREACHABLE (0xC0000000 | 0x023e) +#define NT_STATUS_PORT_UNREACHABLE (0xC0000000 | 0x023f) +#define NT_STATUS_REQUEST_ABORTED (0xC0000000 | 0x0240) +#define NT_STATUS_CONNECTION_ABORTED (0xC0000000 | 0x0241) +#define NT_STATUS_BAD_COMPRESSION_BUFFER (0xC0000000 | 0x0242) +#define NT_STATUS_USER_MAPPED_FILE (0xC0000000 | 0x0243) +#define NT_STATUS_AUDIT_FAILED (0xC0000000 | 0x0244) +#define NT_STATUS_TIMER_RESOLUTION_NOT_SET (0xC0000000 | 0x0245) +#define NT_STATUS_CONNECTION_COUNT_LIMIT (0xC0000000 | 0x0246) +#define NT_STATUS_LOGIN_TIME_RESTRICTION (0xC0000000 | 0x0247) +#define NT_STATUS_LOGIN_WKSTA_RESTRICTION (0xC0000000 | 0x0248) +#define NT_STATUS_IMAGE_MP_UP_MISMATCH (0xC0000000 | 0x0249) +#define NT_STATUS_INSUFFICIENT_LOGON_INFO (0xC0000000 | 0x0250) +#define NT_STATUS_BAD_DLL_ENTRYPOINT (0xC0000000 | 0x0251) +#define NT_STATUS_BAD_SERVICE_ENTRYPOINT (0xC0000000 | 0x0252) +#define NT_STATUS_LPC_REPLY_LOST (0xC0000000 | 0x0253) +#define NT_STATUS_IP_ADDRESS_CONFLICT1 (0xC0000000 | 0x0254) +#define NT_STATUS_IP_ADDRESS_CONFLICT2 (0xC0000000 | 0x0255) +#define NT_STATUS_REGISTRY_QUOTA_LIMIT (0xC0000000 | 0x0256) +#define NT_STATUS_PATH_NOT_COVERED (0xC0000000 | 0x0257) +#define NT_STATUS_NO_CALLBACK_ACTIVE (0xC0000000 | 0x0258) +#define NT_STATUS_LICENSE_QUOTA_EXCEEDED (0xC0000000 | 0x0259) +#define NT_STATUS_PWD_TOO_SHORT (0xC0000000 | 0x025a) +#define NT_STATUS_PWD_TOO_RECENT (0xC0000000 | 0x025b) +#define NT_STATUS_PWD_HISTORY_CONFLICT (0xC0000000 | 0x025c) +#define NT_STATUS_PLUGPLAY_NO_DEVICE (0xC0000000 | 0x025e) +#define NT_STATUS_UNSUPPORTED_COMPRESSION (0xC0000000 | 0x025f) +#define NT_STATUS_INVALID_HW_PROFILE (0xC0000000 | 0x0260) +#define NT_STATUS_INVALID_PLUGPLAY_DEVICE_PATH (0xC0000000 | 0x0261) +#define NT_STATUS_DRIVER_ORDINAL_NOT_FOUND (0xC0000000 | 0x0262) +#define NT_STATUS_DRIVER_ENTRYPOINT_NOT_FOUND (0xC0000000 | 0x0263) +#define NT_STATUS_RESOURCE_NOT_OWNED (0xC0000000 | 0x0264) +#define NT_STATUS_TOO_MANY_LINKS (0xC0000000 | 0x0265) +#define NT_STATUS_QUOTA_LIST_INCONSISTENT (0xC0000000 | 0x0266) +#define NT_STATUS_FILE_IS_OFFLINE (0xC0000000 | 0x0267) +#define NT_STATUS_NETWORK_SESSION_EXPIRED (0xC0000000 | 0x035c) +#define NT_STATUS_NO_SUCH_JOB (0xC0000000 | 0xEDE) /* scheduler */ +#define NT_STATUS_NO_PREAUTH_INTEGRITY_HASH_OVERLAP (0xC0000000 | 0x5D0000) +#define NT_STATUS_PENDING 0x00000103 +#endif /* _NTERR_H */ diff --git a/fs/smb/server/ntlmssp.h b/fs/smb/server/ntlmssp.h new file mode 100644 index 0000000000000..f13153c18b4e9 --- /dev/null +++ b/fs/smb/server/ntlmssp.h @@ -0,0 +1,169 @@ +/* SPDX-License-Identifier: LGPL-2.1+ */ +/* + * Copyright (c) International Business Machines Corp., 2002,2007 + * Author(s): Steve French (sfrench@us.ibm.com) + */ + +#ifndef __KSMBD_NTLMSSP_H +#define __KSMBD_NTLMSSP_H + +#define NTLMSSP_SIGNATURE "NTLMSSP" + +/* Security blob target info data */ +#define TGT_Name "KSMBD" + +/* + * Size of the crypto key returned on the negotiate SMB in bytes + */ +#define CIFS_CRYPTO_KEY_SIZE (8) +#define CIFS_KEY_SIZE (40) + +/* + * Size of encrypted user password in bytes + */ +#define CIFS_ENCPWD_SIZE (16) +#define CIFS_CPHTXT_SIZE (16) + +/* Message Types */ +#define NtLmNegotiate cpu_to_le32(1) +#define NtLmChallenge cpu_to_le32(2) +#define NtLmAuthenticate cpu_to_le32(3) +#define UnknownMessage cpu_to_le32(8) + +/* Negotiate Flags */ +#define NTLMSSP_NEGOTIATE_UNICODE 0x01 /* Text strings are unicode */ +#define NTLMSSP_NEGOTIATE_OEM 0x02 /* Text strings are in OEM */ +#define NTLMSSP_REQUEST_TARGET 0x04 /* Srv returns its auth realm */ +/* define reserved9 0x08 */ +#define NTLMSSP_NEGOTIATE_SIGN 0x0010 /* Request signing capability */ +#define NTLMSSP_NEGOTIATE_SEAL 0x0020 /* Request confidentiality */ +#define NTLMSSP_NEGOTIATE_DGRAM 0x0040 +#define NTLMSSP_NEGOTIATE_LM_KEY 0x0080 /* Use LM session key */ +/* defined reserved 8 0x0100 */ +#define NTLMSSP_NEGOTIATE_NTLM 0x0200 /* NTLM authentication */ +#define NTLMSSP_NEGOTIATE_NT_ONLY 0x0400 /* Lanman not allowed */ +#define NTLMSSP_ANONYMOUS 0x0800 +#define NTLMSSP_NEGOTIATE_DOMAIN_SUPPLIED 0x1000 /* reserved6 */ +#define NTLMSSP_NEGOTIATE_WORKSTATION_SUPPLIED 0x2000 +#define NTLMSSP_NEGOTIATE_LOCAL_CALL 0x4000 /* client/server same machine */ +#define NTLMSSP_NEGOTIATE_ALWAYS_SIGN 0x8000 /* Sign. All security levels */ +#define NTLMSSP_TARGET_TYPE_DOMAIN 0x10000 +#define NTLMSSP_TARGET_TYPE_SERVER 0x20000 +#define NTLMSSP_TARGET_TYPE_SHARE 0x40000 +#define NTLMSSP_NEGOTIATE_EXTENDED_SEC 0x80000 /* NB:not related to NTLMv2 pwd*/ +/* #define NTLMSSP_REQUEST_INIT_RESP 0x100000 */ +#define NTLMSSP_NEGOTIATE_IDENTIFY 0x100000 +#define NTLMSSP_REQUEST_ACCEPT_RESP 0x200000 /* reserved5 */ +#define NTLMSSP_REQUEST_NON_NT_KEY 0x400000 +#define NTLMSSP_NEGOTIATE_TARGET_INFO 0x800000 +/* #define reserved4 0x1000000 */ +#define NTLMSSP_NEGOTIATE_VERSION 0x2000000 /* we do not set */ +/* #define reserved3 0x4000000 */ +/* #define reserved2 0x8000000 */ +/* #define reserved1 0x10000000 */ +#define NTLMSSP_NEGOTIATE_128 0x20000000 +#define NTLMSSP_NEGOTIATE_KEY_XCH 0x40000000 +#define NTLMSSP_NEGOTIATE_56 0x80000000 + +/* Define AV Pair Field IDs */ +enum av_field_type { + NTLMSSP_AV_EOL = 0, + NTLMSSP_AV_NB_COMPUTER_NAME, + NTLMSSP_AV_NB_DOMAIN_NAME, + NTLMSSP_AV_DNS_COMPUTER_NAME, + NTLMSSP_AV_DNS_DOMAIN_NAME, + NTLMSSP_AV_DNS_TREE_NAME, + NTLMSSP_AV_FLAGS, + NTLMSSP_AV_TIMESTAMP, + NTLMSSP_AV_RESTRICTION, + NTLMSSP_AV_TARGET_NAME, + NTLMSSP_AV_CHANNEL_BINDINGS +}; + +/* Although typedefs are not commonly used for structure definitions */ +/* in the Linux kernel, in this particular case they are useful */ +/* to more closely match the standards document for NTLMSSP from */ +/* OpenGroup and to make the code more closely match the standard in */ +/* appearance */ + +struct security_buffer { + __le16 Length; + __le16 MaximumLength; + __le32 BufferOffset; /* offset to buffer */ +} __packed; + +struct target_info { + __le16 Type; + __le16 Length; + __u8 Content[]; +} __packed; + +struct negotiate_message { + __u8 Signature[sizeof(NTLMSSP_SIGNATURE)]; + __le32 MessageType; /* NtLmNegotiate = 1 */ + __le32 NegotiateFlags; + struct security_buffer DomainName; /* RFC 1001 style and ASCII */ + struct security_buffer WorkstationName; /* RFC 1001 and ASCII */ + /* + * struct security_buffer for version info not present since we + * do not set the version is present flag + */ + char DomainString[]; + /* followed by WorkstationString */ +} __packed; + +struct challenge_message { + __u8 Signature[sizeof(NTLMSSP_SIGNATURE)]; + __le32 MessageType; /* NtLmChallenge = 2 */ + struct security_buffer TargetName; + __le32 NegotiateFlags; + __u8 Challenge[CIFS_CRYPTO_KEY_SIZE]; + __u8 Reserved[8]; + struct security_buffer TargetInfoArray; + /* + * struct security_buffer for version info not present since we + * do not set the version is present flag + */ +} __packed; + +struct authenticate_message { + __u8 Signature[sizeof(NTLMSSP_SIGNATURE)]; + __le32 MessageType; /* NtLmsAuthenticate = 3 */ + struct security_buffer LmChallengeResponse; + struct security_buffer NtChallengeResponse; + struct security_buffer DomainName; + struct security_buffer UserName; + struct security_buffer WorkstationName; + struct security_buffer SessionKey; + __le32 NegotiateFlags; + /* + * struct security_buffer for version info not present since we + * do not set the version is present flag + */ + char UserString[]; +} __packed; + +struct ntlmv2_resp { + char ntlmv2_hash[CIFS_ENCPWD_SIZE]; + __le32 blob_signature; + __u32 reserved; + __le64 time; + __u64 client_chal; /* random */ + __u32 reserved2; + /* array of name entries could follow ending in minimum 4 byte struct */ +} __packed; + +/* per smb session structure/fields */ +struct ntlmssp_auth { + /* whether session key is per smb session */ + bool sesskey_per_smbsess; + /* sent by client in type 1 ntlmsssp exchange */ + __u32 client_flags; + /* sent by server in type 2 ntlmssp exchange */ + __u32 conn_flags; + /* sent to server */ + unsigned char ciphertext[CIFS_CPHTXT_SIZE]; + /* used by ntlmssp */ + char cryptkey[CIFS_CRYPTO_KEY_SIZE]; +}; +#endif /* __KSMBD_NTLMSSP_H */ diff --git a/fs/smb/server/oplock.c b/fs/smb/server/oplock.c new file mode 100644 index 0000000000000..4b210cdd75569 --- /dev/null +++ b/fs/smb/server/oplock.c @@ -0,0 +1,1722 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Copyright (C) 2016 Namjae Jeon + * Copyright (C) 2018 Samsung Electronics Co., Ltd. + */ + +#include + +#include "glob.h" +#include "oplock.h" + +#include "smb_common.h" +#include "smbstatus.h" +#include "connection.h" +#include "mgmt/user_session.h" +#include "mgmt/share_config.h" +#include "mgmt/tree_connect.h" + +static LIST_HEAD(lease_table_list); +static DEFINE_RWLOCK(lease_list_lock); + +/** + * alloc_opinfo() - allocate a new opinfo object for oplock info + * @work: smb work + * @id: fid of open file + * @Tid: tree id of connection + * + * Return: allocated opinfo object on success, otherwise NULL + */ +static struct oplock_info *alloc_opinfo(struct ksmbd_work *work, + u64 id, __u16 Tid) +{ + struct ksmbd_conn *conn = work->conn; + struct ksmbd_session *sess = work->sess; + struct oplock_info *opinfo; + + opinfo = kzalloc(sizeof(struct oplock_info), GFP_KERNEL); + if (!opinfo) + return NULL; + + opinfo->sess = sess; + opinfo->conn = conn; + opinfo->level = SMB2_OPLOCK_LEVEL_NONE; + opinfo->op_state = OPLOCK_STATE_NONE; + opinfo->pending_break = 0; + opinfo->fid = id; + opinfo->Tid = Tid; + INIT_LIST_HEAD(&opinfo->op_entry); + INIT_LIST_HEAD(&opinfo->interim_list); + init_waitqueue_head(&opinfo->oplock_q); + init_waitqueue_head(&opinfo->oplock_brk); + atomic_set(&opinfo->refcount, 1); + atomic_set(&opinfo->breaking_cnt, 0); + + return opinfo; +} + +static void lease_add_list(struct oplock_info *opinfo) +{ + struct lease_table *lb = opinfo->o_lease->l_lb; + + spin_lock(&lb->lb_lock); + list_add_rcu(&opinfo->lease_entry, &lb->lease_list); + spin_unlock(&lb->lb_lock); +} + +static void lease_del_list(struct oplock_info *opinfo) +{ + struct lease_table *lb = opinfo->o_lease->l_lb; + + if (!lb) + return; + + spin_lock(&lb->lb_lock); + if (list_empty(&opinfo->lease_entry)) { + spin_unlock(&lb->lb_lock); + return; + } + + list_del_init(&opinfo->lease_entry); + opinfo->o_lease->l_lb = NULL; + spin_unlock(&lb->lb_lock); +} + +static void lb_add(struct lease_table *lb) +{ + write_lock(&lease_list_lock); + list_add(&lb->l_entry, &lease_table_list); + write_unlock(&lease_list_lock); +} + +static int alloc_lease(struct oplock_info *opinfo, struct lease_ctx_info *lctx) +{ + struct lease *lease; + + lease = kmalloc(sizeof(struct lease), GFP_KERNEL); + if (!lease) + return -ENOMEM; + + memcpy(lease->lease_key, lctx->lease_key, SMB2_LEASE_KEY_SIZE); + lease->state = lctx->req_state; + lease->new_state = 0; + lease->flags = lctx->flags; + lease->duration = lctx->duration; + memcpy(lease->parent_lease_key, lctx->parent_lease_key, SMB2_LEASE_KEY_SIZE); + lease->version = lctx->version; + lease->epoch = 0; + INIT_LIST_HEAD(&opinfo->lease_entry); + opinfo->o_lease = lease; + + return 0; +} + +static void free_lease(struct oplock_info *opinfo) +{ + struct lease *lease; + + lease = opinfo->o_lease; + kfree(lease); +} + +static void free_opinfo(struct oplock_info *opinfo) +{ + if (opinfo->is_lease) + free_lease(opinfo); + kfree(opinfo); +} + +static inline void opinfo_free_rcu(struct rcu_head *rcu_head) +{ + struct oplock_info *opinfo; + + opinfo = container_of(rcu_head, struct oplock_info, rcu_head); + free_opinfo(opinfo); +} + +struct oplock_info *opinfo_get(struct ksmbd_file *fp) +{ + struct oplock_info *opinfo; + + rcu_read_lock(); + opinfo = rcu_dereference(fp->f_opinfo); + if (opinfo && !atomic_inc_not_zero(&opinfo->refcount)) + opinfo = NULL; + rcu_read_unlock(); + + return opinfo; +} + +static struct oplock_info *opinfo_get_list(struct ksmbd_inode *ci) +{ + struct oplock_info *opinfo; + + if (list_empty(&ci->m_op_list)) + return NULL; + + rcu_read_lock(); + opinfo = list_first_or_null_rcu(&ci->m_op_list, struct oplock_info, + op_entry); + if (opinfo) { + if (!atomic_inc_not_zero(&opinfo->refcount)) + opinfo = NULL; + else { + atomic_inc(&opinfo->conn->r_count); + if (ksmbd_conn_releasing(opinfo->conn)) { + atomic_dec(&opinfo->conn->r_count); + atomic_dec(&opinfo->refcount); + opinfo = NULL; + } + } + } + + rcu_read_unlock(); + + return opinfo; +} + +static void opinfo_conn_put(struct oplock_info *opinfo) +{ + struct ksmbd_conn *conn; + + if (!opinfo) + return; + + conn = opinfo->conn; + /* + * Checking waitqueue to dropping pending requests on + * disconnection. waitqueue_active is safe because it + * uses atomic operation for condition. + */ + if (!atomic_dec_return(&conn->r_count) && waitqueue_active(&conn->r_count_q)) + wake_up(&conn->r_count_q); + opinfo_put(opinfo); +} + +void opinfo_put(struct oplock_info *opinfo) +{ + if (!atomic_dec_and_test(&opinfo->refcount)) + return; + + call_rcu(&opinfo->rcu_head, opinfo_free_rcu); +} + +static void opinfo_add(struct oplock_info *opinfo) +{ + struct ksmbd_inode *ci = opinfo->o_fp->f_ci; + + write_lock(&ci->m_lock); + list_add_rcu(&opinfo->op_entry, &ci->m_op_list); + write_unlock(&ci->m_lock); +} + +static void opinfo_del(struct oplock_info *opinfo) +{ + struct ksmbd_inode *ci = opinfo->o_fp->f_ci; + + if (opinfo->is_lease) { + write_lock(&lease_list_lock); + lease_del_list(opinfo); + write_unlock(&lease_list_lock); + } + write_lock(&ci->m_lock); + list_del_rcu(&opinfo->op_entry); + write_unlock(&ci->m_lock); +} + +static unsigned long opinfo_count(struct ksmbd_file *fp) +{ + if (ksmbd_stream_fd(fp)) + return atomic_read(&fp->f_ci->sop_count); + else + return atomic_read(&fp->f_ci->op_count); +} + +static void opinfo_count_inc(struct ksmbd_file *fp) +{ + if (ksmbd_stream_fd(fp)) + return atomic_inc(&fp->f_ci->sop_count); + else + return atomic_inc(&fp->f_ci->op_count); +} + +static void opinfo_count_dec(struct ksmbd_file *fp) +{ + if (ksmbd_stream_fd(fp)) + return atomic_dec(&fp->f_ci->sop_count); + else + return atomic_dec(&fp->f_ci->op_count); +} + +/** + * opinfo_write_to_read() - convert a write oplock to read oplock + * @opinfo: current oplock info + * + * Return: 0 on success, otherwise -EINVAL + */ +int opinfo_write_to_read(struct oplock_info *opinfo) +{ + struct lease *lease = opinfo->o_lease; + + if (!(opinfo->level == SMB2_OPLOCK_LEVEL_BATCH || + opinfo->level == SMB2_OPLOCK_LEVEL_EXCLUSIVE)) { + pr_err("bad oplock(0x%x)\n", opinfo->level); + if (opinfo->is_lease) + pr_err("lease state(0x%x)\n", lease->state); + return -EINVAL; + } + opinfo->level = SMB2_OPLOCK_LEVEL_II; + + if (opinfo->is_lease) + lease->state = lease->new_state; + return 0; +} + +/** + * opinfo_read_handle_to_read() - convert a read/handle oplock to read oplock + * @opinfo: current oplock info + * + * Return: 0 on success, otherwise -EINVAL + */ +int opinfo_read_handle_to_read(struct oplock_info *opinfo) +{ + struct lease *lease = opinfo->o_lease; + + lease->state = lease->new_state; + opinfo->level = SMB2_OPLOCK_LEVEL_II; + return 0; +} + +/** + * opinfo_write_to_none() - convert a write oplock to none + * @opinfo: current oplock info + * + * Return: 0 on success, otherwise -EINVAL + */ +int opinfo_write_to_none(struct oplock_info *opinfo) +{ + struct lease *lease = opinfo->o_lease; + + if (!(opinfo->level == SMB2_OPLOCK_LEVEL_BATCH || + opinfo->level == SMB2_OPLOCK_LEVEL_EXCLUSIVE)) { + pr_err("bad oplock(0x%x)\n", opinfo->level); + if (opinfo->is_lease) + pr_err("lease state(0x%x)\n", lease->state); + return -EINVAL; + } + opinfo->level = SMB2_OPLOCK_LEVEL_NONE; + if (opinfo->is_lease) + lease->state = lease->new_state; + return 0; +} + +/** + * opinfo_read_to_none() - convert a write read to none + * @opinfo: current oplock info + * + * Return: 0 on success, otherwise -EINVAL + */ +int opinfo_read_to_none(struct oplock_info *opinfo) +{ + struct lease *lease = opinfo->o_lease; + + if (opinfo->level != SMB2_OPLOCK_LEVEL_II) { + pr_err("bad oplock(0x%x)\n", opinfo->level); + if (opinfo->is_lease) + pr_err("lease state(0x%x)\n", lease->state); + return -EINVAL; + } + opinfo->level = SMB2_OPLOCK_LEVEL_NONE; + if (opinfo->is_lease) + lease->state = lease->new_state; + return 0; +} + +/** + * lease_read_to_write() - upgrade lease state from read to write + * @opinfo: current lease info + * + * Return: 0 on success, otherwise -EINVAL + */ +int lease_read_to_write(struct oplock_info *opinfo) +{ + struct lease *lease = opinfo->o_lease; + + if (!(lease->state & SMB2_LEASE_READ_CACHING_LE)) { + ksmbd_debug(OPLOCK, "bad lease state(0x%x)\n", lease->state); + return -EINVAL; + } + + lease->new_state = SMB2_LEASE_NONE_LE; + lease->state |= SMB2_LEASE_WRITE_CACHING_LE; + if (lease->state & SMB2_LEASE_HANDLE_CACHING_LE) + opinfo->level = SMB2_OPLOCK_LEVEL_BATCH; + else + opinfo->level = SMB2_OPLOCK_LEVEL_EXCLUSIVE; + return 0; +} + +/** + * lease_none_upgrade() - upgrade lease state from none + * @opinfo: current lease info + * @new_state: new lease state + * + * Return: 0 on success, otherwise -EINVAL + */ +static int lease_none_upgrade(struct oplock_info *opinfo, __le32 new_state) +{ + struct lease *lease = opinfo->o_lease; + + if (!(lease->state == SMB2_LEASE_NONE_LE)) { + ksmbd_debug(OPLOCK, "bad lease state(0x%x)\n", lease->state); + return -EINVAL; + } + + lease->new_state = SMB2_LEASE_NONE_LE; + lease->state = new_state; + if (lease->state & SMB2_LEASE_HANDLE_CACHING_LE) + if (lease->state & SMB2_LEASE_WRITE_CACHING_LE) + opinfo->level = SMB2_OPLOCK_LEVEL_BATCH; + else + opinfo->level = SMB2_OPLOCK_LEVEL_II; + else if (lease->state & SMB2_LEASE_WRITE_CACHING_LE) + opinfo->level = SMB2_OPLOCK_LEVEL_EXCLUSIVE; + else if (lease->state & SMB2_LEASE_READ_CACHING_LE) + opinfo->level = SMB2_OPLOCK_LEVEL_II; + + return 0; +} + +/** + * close_id_del_oplock() - release oplock object at file close time + * @fp: ksmbd file pointer + */ +void close_id_del_oplock(struct ksmbd_file *fp) +{ + struct oplock_info *opinfo; + + if (S_ISDIR(file_inode(fp->filp)->i_mode)) + return; + + opinfo = opinfo_get(fp); + if (!opinfo) + return; + + opinfo_del(opinfo); + + rcu_assign_pointer(fp->f_opinfo, NULL); + if (opinfo->op_state == OPLOCK_ACK_WAIT) { + opinfo->op_state = OPLOCK_CLOSING; + wake_up_interruptible_all(&opinfo->oplock_q); + if (opinfo->is_lease) { + atomic_set(&opinfo->breaking_cnt, 0); + wake_up_interruptible_all(&opinfo->oplock_brk); + } + } + + opinfo_count_dec(fp); + atomic_dec(&opinfo->refcount); + opinfo_put(opinfo); +} + +/** + * grant_write_oplock() - grant exclusive/batch oplock or write lease + * @opinfo_new: new oplock info object + * @req_oplock: request oplock + * @lctx: lease context information + * + * Return: 0 + */ +static void grant_write_oplock(struct oplock_info *opinfo_new, int req_oplock, + struct lease_ctx_info *lctx) +{ + struct lease *lease = opinfo_new->o_lease; + + if (req_oplock == SMB2_OPLOCK_LEVEL_BATCH) + opinfo_new->level = SMB2_OPLOCK_LEVEL_BATCH; + else + opinfo_new->level = SMB2_OPLOCK_LEVEL_EXCLUSIVE; + + if (lctx) { + lease->state = lctx->req_state; + memcpy(lease->lease_key, lctx->lease_key, SMB2_LEASE_KEY_SIZE); + } +} + +/** + * grant_read_oplock() - grant level2 oplock or read lease + * @opinfo_new: new oplock info object + * @lctx: lease context information + * + * Return: 0 + */ +static void grant_read_oplock(struct oplock_info *opinfo_new, + struct lease_ctx_info *lctx) +{ + struct lease *lease = opinfo_new->o_lease; + + opinfo_new->level = SMB2_OPLOCK_LEVEL_II; + + if (lctx) { + lease->state = SMB2_LEASE_READ_CACHING_LE; + if (lctx->req_state & SMB2_LEASE_HANDLE_CACHING_LE) + lease->state |= SMB2_LEASE_HANDLE_CACHING_LE; + memcpy(lease->lease_key, lctx->lease_key, SMB2_LEASE_KEY_SIZE); + } +} + +/** + * grant_none_oplock() - grant none oplock or none lease + * @opinfo_new: new oplock info object + * @lctx: lease context information + * + * Return: 0 + */ +static void grant_none_oplock(struct oplock_info *opinfo_new, + struct lease_ctx_info *lctx) +{ + struct lease *lease = opinfo_new->o_lease; + + opinfo_new->level = SMB2_OPLOCK_LEVEL_NONE; + + if (lctx) { + lease->state = 0; + memcpy(lease->lease_key, lctx->lease_key, SMB2_LEASE_KEY_SIZE); + } +} + +static inline int compare_guid_key(struct oplock_info *opinfo, + const char *guid1, const char *key1) +{ + const char *guid2, *key2; + + guid2 = opinfo->conn->ClientGUID; + key2 = opinfo->o_lease->lease_key; + if (!memcmp(guid1, guid2, SMB2_CLIENT_GUID_SIZE) && + !memcmp(key1, key2, SMB2_LEASE_KEY_SIZE)) + return 1; + + return 0; +} + +/** + * same_client_has_lease() - check whether current lease request is + * from lease owner of file + * @ci: master file pointer + * @client_guid: Client GUID + * @lctx: lease context information + * + * Return: oplock(lease) object on success, otherwise NULL + */ +static struct oplock_info *same_client_has_lease(struct ksmbd_inode *ci, + char *client_guid, + struct lease_ctx_info *lctx) +{ + int ret; + struct lease *lease; + struct oplock_info *opinfo; + struct oplock_info *m_opinfo = NULL; + + if (!lctx) + return NULL; + + /* + * Compare lease key and client_guid to know request from same owner + * of same client + */ + read_lock(&ci->m_lock); + list_for_each_entry(opinfo, &ci->m_op_list, op_entry) { + if (!opinfo->is_lease) + continue; + read_unlock(&ci->m_lock); + lease = opinfo->o_lease; + + ret = compare_guid_key(opinfo, client_guid, lctx->lease_key); + if (ret) { + m_opinfo = opinfo; + /* skip upgrading lease about breaking lease */ + if (atomic_read(&opinfo->breaking_cnt)) { + read_lock(&ci->m_lock); + continue; + } + + /* upgrading lease */ + if ((atomic_read(&ci->op_count) + + atomic_read(&ci->sop_count)) == 1) { + if (lease->state == + (lctx->req_state & lease->state)) { + lease->state |= lctx->req_state; + if (lctx->req_state & + SMB2_LEASE_WRITE_CACHING_LE) + lease_read_to_write(opinfo); + } + } else if ((atomic_read(&ci->op_count) + + atomic_read(&ci->sop_count)) > 1) { + if (lctx->req_state == + (SMB2_LEASE_READ_CACHING_LE | + SMB2_LEASE_HANDLE_CACHING_LE)) + lease->state = lctx->req_state; + } + + if (lctx->req_state && lease->state == + SMB2_LEASE_NONE_LE) + lease_none_upgrade(opinfo, lctx->req_state); + } + read_lock(&ci->m_lock); + } + read_unlock(&ci->m_lock); + + return m_opinfo; +} + +static void wait_for_break_ack(struct oplock_info *opinfo) +{ + int rc = 0; + + rc = wait_event_interruptible_timeout(opinfo->oplock_q, + opinfo->op_state == OPLOCK_STATE_NONE || + opinfo->op_state == OPLOCK_CLOSING, + OPLOCK_WAIT_TIME); + + /* is this a timeout ? */ + if (!rc) { + if (opinfo->is_lease) + opinfo->o_lease->state = SMB2_LEASE_NONE_LE; + opinfo->level = SMB2_OPLOCK_LEVEL_NONE; + opinfo->op_state = OPLOCK_STATE_NONE; + } +} + +static void wake_up_oplock_break(struct oplock_info *opinfo) +{ + clear_bit_unlock(0, &opinfo->pending_break); + /* memory barrier is needed for wake_up_bit() */ + smp_mb__after_atomic(); + wake_up_bit(&opinfo->pending_break, 0); +} + +static int oplock_break_pending(struct oplock_info *opinfo, int req_op_level) +{ + while (test_and_set_bit(0, &opinfo->pending_break)) { + wait_on_bit(&opinfo->pending_break, 0, TASK_UNINTERRUPTIBLE); + + /* Not immediately break to none. */ + opinfo->open_trunc = 0; + + if (opinfo->op_state == OPLOCK_CLOSING) + return -ENOENT; + else if (!opinfo->is_lease && opinfo->level <= req_op_level) + return 1; + } + + if (!opinfo->is_lease && opinfo->level <= req_op_level) { + wake_up_oplock_break(opinfo); + return 1; + } + return 0; +} + +static inline int allocate_oplock_break_buf(struct ksmbd_work *work) +{ + work->response_buf = kzalloc(MAX_CIFS_SMALL_BUFFER_SIZE, GFP_KERNEL); + if (!work->response_buf) + return -ENOMEM; + work->response_sz = MAX_CIFS_SMALL_BUFFER_SIZE; + return 0; +} + +/** + * __smb2_oplock_break_noti() - send smb2 oplock break cmd from conn + * to client + * @wk: smb work object + * + * There are two ways this function can be called. 1- while file open we break + * from exclusive/batch lock to levelII oplock and 2- while file write/truncate + * we break from levelII oplock no oplock. + * work->request_buf contains oplock_info. + */ +static void __smb2_oplock_break_noti(struct work_struct *wk) +{ + struct smb2_oplock_break *rsp = NULL; + struct ksmbd_work *work = container_of(wk, struct ksmbd_work, work); + struct ksmbd_conn *conn = work->conn; + struct oplock_break_info *br_info = work->request_buf; + struct smb2_hdr *rsp_hdr; + struct ksmbd_file *fp; + + fp = ksmbd_lookup_durable_fd(br_info->fid); + if (!fp) + goto out; + + if (allocate_oplock_break_buf(work)) { + pr_err("smb2_allocate_rsp_buf failed! "); + ksmbd_fd_put(work, fp); + goto out; + } + + rsp_hdr = smb2_get_msg(work->response_buf); + memset(rsp_hdr, 0, sizeof(struct smb2_hdr) + 2); + *(__be32 *)work->response_buf = + cpu_to_be32(conn->vals->header_size); + rsp_hdr->ProtocolId = SMB2_PROTO_NUMBER; + rsp_hdr->StructureSize = SMB2_HEADER_STRUCTURE_SIZE; + rsp_hdr->CreditRequest = cpu_to_le16(0); + rsp_hdr->Command = SMB2_OPLOCK_BREAK; + rsp_hdr->Flags = (SMB2_FLAGS_SERVER_TO_REDIR); + rsp_hdr->NextCommand = 0; + rsp_hdr->MessageId = cpu_to_le64(-1); + rsp_hdr->Id.SyncId.ProcessId = 0; + rsp_hdr->Id.SyncId.TreeId = 0; + rsp_hdr->SessionId = 0; + memset(rsp_hdr->Signature, 0, 16); + + rsp = smb2_get_msg(work->response_buf); + + rsp->StructureSize = cpu_to_le16(24); + if (!br_info->open_trunc && + (br_info->level == SMB2_OPLOCK_LEVEL_BATCH || + br_info->level == SMB2_OPLOCK_LEVEL_EXCLUSIVE)) + rsp->OplockLevel = SMB2_OPLOCK_LEVEL_II; + else + rsp->OplockLevel = SMB2_OPLOCK_LEVEL_NONE; + rsp->Reserved = 0; + rsp->Reserved2 = 0; + rsp->PersistentFid = fp->persistent_id; + rsp->VolatileFid = fp->volatile_id; + + inc_rfc1001_len(work->response_buf, 24); + + ksmbd_debug(OPLOCK, + "sending oplock break v_id %llu p_id = %llu lock level = %d\n", + rsp->VolatileFid, rsp->PersistentFid, rsp->OplockLevel); + + ksmbd_fd_put(work, fp); + ksmbd_conn_write(work); + +out: + ksmbd_free_work_struct(work); +} + +/** + * smb2_oplock_break_noti() - send smb2 exclusive/batch to level2 oplock + * break command from server to client + * @opinfo: oplock info object + * + * Return: 0 on success, otherwise error + */ +static int smb2_oplock_break_noti(struct oplock_info *opinfo) +{ + struct ksmbd_conn *conn = opinfo->conn; + struct oplock_break_info *br_info; + int ret = 0; + struct ksmbd_work *work = ksmbd_alloc_work_struct(); + + if (!work) + return -ENOMEM; + + br_info = kmalloc(sizeof(struct oplock_break_info), GFP_KERNEL); + if (!br_info) { + ksmbd_free_work_struct(work); + return -ENOMEM; + } + + br_info->level = opinfo->level; + br_info->fid = opinfo->fid; + br_info->open_trunc = opinfo->open_trunc; + + work->request_buf = (char *)br_info; + work->conn = conn; + work->sess = opinfo->sess; + + if (opinfo->op_state == OPLOCK_ACK_WAIT) { + INIT_WORK(&work->work, __smb2_oplock_break_noti); + ksmbd_queue_work(work); + + wait_for_break_ack(opinfo); + } else { + __smb2_oplock_break_noti(&work->work); + if (opinfo->level == SMB2_OPLOCK_LEVEL_II) + opinfo->level = SMB2_OPLOCK_LEVEL_NONE; + } + return ret; +} + +/** + * __smb2_lease_break_noti() - send lease break command from server + * to client + * @wk: smb work object + */ +static void __smb2_lease_break_noti(struct work_struct *wk) +{ + struct smb2_lease_break *rsp = NULL; + struct ksmbd_work *work = container_of(wk, struct ksmbd_work, work); + struct lease_break_info *br_info = work->request_buf; + struct ksmbd_conn *conn = work->conn; + struct smb2_hdr *rsp_hdr; + + if (allocate_oplock_break_buf(work)) { + ksmbd_debug(OPLOCK, "smb2_allocate_rsp_buf failed! "); + goto out; + } + + rsp_hdr = smb2_get_msg(work->response_buf); + memset(rsp_hdr, 0, sizeof(struct smb2_hdr) + 2); + *(__be32 *)work->response_buf = + cpu_to_be32(conn->vals->header_size); + rsp_hdr->ProtocolId = SMB2_PROTO_NUMBER; + rsp_hdr->StructureSize = SMB2_HEADER_STRUCTURE_SIZE; + rsp_hdr->CreditRequest = cpu_to_le16(0); + rsp_hdr->Command = SMB2_OPLOCK_BREAK; + rsp_hdr->Flags = (SMB2_FLAGS_SERVER_TO_REDIR); + rsp_hdr->NextCommand = 0; + rsp_hdr->MessageId = cpu_to_le64(-1); + rsp_hdr->Id.SyncId.ProcessId = 0; + rsp_hdr->Id.SyncId.TreeId = 0; + rsp_hdr->SessionId = 0; + memset(rsp_hdr->Signature, 0, 16); + + rsp = smb2_get_msg(work->response_buf); + rsp->StructureSize = cpu_to_le16(44); + rsp->Epoch = br_info->epoch; + rsp->Flags = 0; + + if (br_info->curr_state & (SMB2_LEASE_WRITE_CACHING_LE | + SMB2_LEASE_HANDLE_CACHING_LE)) + rsp->Flags = SMB2_NOTIFY_BREAK_LEASE_FLAG_ACK_REQUIRED; + + memcpy(rsp->LeaseKey, br_info->lease_key, SMB2_LEASE_KEY_SIZE); + rsp->CurrentLeaseState = br_info->curr_state; + rsp->NewLeaseState = br_info->new_state; + rsp->BreakReason = 0; + rsp->AccessMaskHint = 0; + rsp->ShareMaskHint = 0; + + inc_rfc1001_len(work->response_buf, 44); + + ksmbd_conn_write(work); + +out: + ksmbd_free_work_struct(work); +} + +/** + * smb2_lease_break_noti() - break lease when a new client request + * write lease + * @opinfo: conains lease state information + * + * Return: 0 on success, otherwise error + */ +static int smb2_lease_break_noti(struct oplock_info *opinfo) +{ + struct ksmbd_conn *conn = opinfo->conn; + struct list_head *tmp, *t; + struct ksmbd_work *work; + struct lease_break_info *br_info; + struct lease *lease = opinfo->o_lease; + + work = ksmbd_alloc_work_struct(); + if (!work) + return -ENOMEM; + + br_info = kmalloc(sizeof(struct lease_break_info), GFP_KERNEL); + if (!br_info) { + ksmbd_free_work_struct(work); + return -ENOMEM; + } + + br_info->curr_state = lease->state; + br_info->new_state = lease->new_state; + if (lease->version == 2) + br_info->epoch = cpu_to_le16(++lease->epoch); + else + br_info->epoch = 0; + memcpy(br_info->lease_key, lease->lease_key, SMB2_LEASE_KEY_SIZE); + + work->request_buf = (char *)br_info; + work->conn = conn; + work->sess = opinfo->sess; + + if (opinfo->op_state == OPLOCK_ACK_WAIT) { + list_for_each_safe(tmp, t, &opinfo->interim_list) { + struct ksmbd_work *in_work; + + in_work = list_entry(tmp, struct ksmbd_work, + interim_entry); + setup_async_work(in_work, NULL, NULL); + smb2_send_interim_resp(in_work, STATUS_PENDING); + list_del(&in_work->interim_entry); + } + INIT_WORK(&work->work, __smb2_lease_break_noti); + ksmbd_queue_work(work); + wait_for_break_ack(opinfo); + } else { + __smb2_lease_break_noti(&work->work); + if (opinfo->o_lease->new_state == SMB2_LEASE_NONE_LE) { + opinfo->level = SMB2_OPLOCK_LEVEL_NONE; + opinfo->o_lease->state = SMB2_LEASE_NONE_LE; + } + } + return 0; +} + +static void wait_lease_breaking(struct oplock_info *opinfo) +{ + if (!opinfo->is_lease) + return; + + wake_up_interruptible_all(&opinfo->oplock_brk); + if (atomic_read(&opinfo->breaking_cnt)) { + int ret = 0; + + ret = wait_event_interruptible_timeout(opinfo->oplock_brk, + atomic_read(&opinfo->breaking_cnt) == 0, + HZ); + if (!ret) + atomic_set(&opinfo->breaking_cnt, 0); + } +} + +static int oplock_break(struct oplock_info *brk_opinfo, int req_op_level) +{ + int err = 0; + + /* Need to break exclusive/batch oplock, write lease or overwrite_if */ + ksmbd_debug(OPLOCK, + "request to send oplock(level : 0x%x) break notification\n", + brk_opinfo->level); + + if (brk_opinfo->is_lease) { + struct lease *lease = brk_opinfo->o_lease; + + atomic_inc(&brk_opinfo->breaking_cnt); + + err = oplock_break_pending(brk_opinfo, req_op_level); + if (err) + return err < 0 ? err : 0; + + if (brk_opinfo->open_trunc) { + /* + * Create overwrite break trigger the lease break to + * none. + */ + lease->new_state = SMB2_LEASE_NONE_LE; + } else { + if (lease->state & SMB2_LEASE_WRITE_CACHING_LE) { + if (lease->state & SMB2_LEASE_HANDLE_CACHING_LE) + lease->new_state = + SMB2_LEASE_READ_CACHING_LE | + SMB2_LEASE_HANDLE_CACHING_LE; + else + lease->new_state = + SMB2_LEASE_READ_CACHING_LE; + } else { + if (lease->state & SMB2_LEASE_HANDLE_CACHING_LE) + lease->new_state = + SMB2_LEASE_READ_CACHING_LE; + else + lease->new_state = SMB2_LEASE_NONE_LE; + } + } + + if (lease->state & (SMB2_LEASE_WRITE_CACHING_LE | + SMB2_LEASE_HANDLE_CACHING_LE)) + brk_opinfo->op_state = OPLOCK_ACK_WAIT; + else + atomic_dec(&brk_opinfo->breaking_cnt); + } else { + err = oplock_break_pending(brk_opinfo, req_op_level); + if (err) + return err < 0 ? err : 0; + + if (brk_opinfo->level == SMB2_OPLOCK_LEVEL_BATCH || + brk_opinfo->level == SMB2_OPLOCK_LEVEL_EXCLUSIVE) + brk_opinfo->op_state = OPLOCK_ACK_WAIT; + } + + if (brk_opinfo->is_lease) + err = smb2_lease_break_noti(brk_opinfo); + else + err = smb2_oplock_break_noti(brk_opinfo); + + ksmbd_debug(OPLOCK, "oplock granted = %d\n", brk_opinfo->level); + if (brk_opinfo->op_state == OPLOCK_CLOSING) + err = -ENOENT; + wake_up_oplock_break(brk_opinfo); + + wait_lease_breaking(brk_opinfo); + + return err; +} + +void destroy_lease_table(struct ksmbd_conn *conn) +{ + struct lease_table *lb, *lbtmp; + struct oplock_info *opinfo; + + write_lock(&lease_list_lock); + if (list_empty(&lease_table_list)) { + write_unlock(&lease_list_lock); + return; + } + + list_for_each_entry_safe(lb, lbtmp, &lease_table_list, l_entry) { + if (conn && memcmp(lb->client_guid, conn->ClientGUID, + SMB2_CLIENT_GUID_SIZE)) + continue; +again: + rcu_read_lock(); + list_for_each_entry_rcu(opinfo, &lb->lease_list, + lease_entry) { + rcu_read_unlock(); + lease_del_list(opinfo); + goto again; + } + rcu_read_unlock(); + list_del(&lb->l_entry); + kfree(lb); + } + write_unlock(&lease_list_lock); +} + +int find_same_lease_key(struct ksmbd_session *sess, struct ksmbd_inode *ci, + struct lease_ctx_info *lctx) +{ + struct oplock_info *opinfo; + int err = 0; + struct lease_table *lb; + + if (!lctx) + return err; + + read_lock(&lease_list_lock); + if (list_empty(&lease_table_list)) { + read_unlock(&lease_list_lock); + return 0; + } + + list_for_each_entry(lb, &lease_table_list, l_entry) { + if (!memcmp(lb->client_guid, sess->ClientGUID, + SMB2_CLIENT_GUID_SIZE)) + goto found; + } + read_unlock(&lease_list_lock); + + return 0; + +found: + rcu_read_lock(); + list_for_each_entry_rcu(opinfo, &lb->lease_list, lease_entry) { + if (!atomic_inc_not_zero(&opinfo->refcount)) + continue; + rcu_read_unlock(); + if (opinfo->o_fp->f_ci == ci) + goto op_next; + err = compare_guid_key(opinfo, sess->ClientGUID, + lctx->lease_key); + if (err) { + err = -EINVAL; + ksmbd_debug(OPLOCK, + "found same lease key is already used in other files\n"); + opinfo_put(opinfo); + goto out; + } +op_next: + opinfo_put(opinfo); + rcu_read_lock(); + } + rcu_read_unlock(); + +out: + read_unlock(&lease_list_lock); + return err; +} + +static void copy_lease(struct oplock_info *op1, struct oplock_info *op2) +{ + struct lease *lease1 = op1->o_lease; + struct lease *lease2 = op2->o_lease; + + op2->level = op1->level; + lease2->state = lease1->state; + memcpy(lease2->lease_key, lease1->lease_key, + SMB2_LEASE_KEY_SIZE); + lease2->duration = lease1->duration; + lease2->flags = lease1->flags; +} + +static int add_lease_global_list(struct oplock_info *opinfo) +{ + struct lease_table *lb; + + read_lock(&lease_list_lock); + list_for_each_entry(lb, &lease_table_list, l_entry) { + if (!memcmp(lb->client_guid, opinfo->conn->ClientGUID, + SMB2_CLIENT_GUID_SIZE)) { + opinfo->o_lease->l_lb = lb; + lease_add_list(opinfo); + read_unlock(&lease_list_lock); + return 0; + } + } + read_unlock(&lease_list_lock); + + lb = kmalloc(sizeof(struct lease_table), GFP_KERNEL); + if (!lb) + return -ENOMEM; + + memcpy(lb->client_guid, opinfo->conn->ClientGUID, + SMB2_CLIENT_GUID_SIZE); + INIT_LIST_HEAD(&lb->lease_list); + spin_lock_init(&lb->lb_lock); + opinfo->o_lease->l_lb = lb; + lease_add_list(opinfo); + lb_add(lb); + return 0; +} + +static void set_oplock_level(struct oplock_info *opinfo, int level, + struct lease_ctx_info *lctx) +{ + switch (level) { + case SMB2_OPLOCK_LEVEL_BATCH: + case SMB2_OPLOCK_LEVEL_EXCLUSIVE: + grant_write_oplock(opinfo, level, lctx); + break; + case SMB2_OPLOCK_LEVEL_II: + grant_read_oplock(opinfo, lctx); + break; + default: + grant_none_oplock(opinfo, lctx); + break; + } +} + +/** + * smb_grant_oplock() - handle oplock/lease request on file open + * @work: smb work + * @req_op_level: oplock level + * @pid: id of open file + * @fp: ksmbd file pointer + * @tid: Tree id of connection + * @lctx: lease context information on file open + * @share_ret: share mode + * + * Return: 0 on success, otherwise error + */ +int smb_grant_oplock(struct ksmbd_work *work, int req_op_level, u64 pid, + struct ksmbd_file *fp, __u16 tid, + struct lease_ctx_info *lctx, int share_ret) +{ + struct ksmbd_session *sess = work->sess; + int err = 0; + struct oplock_info *opinfo = NULL, *prev_opinfo = NULL; + struct ksmbd_inode *ci = fp->f_ci; + bool prev_op_has_lease; + __le32 prev_op_state = 0; + + /* not support directory lease */ + if (S_ISDIR(file_inode(fp->filp)->i_mode)) + return 0; + + opinfo = alloc_opinfo(work, pid, tid); + if (!opinfo) + return -ENOMEM; + + if (lctx) { + err = alloc_lease(opinfo, lctx); + if (err) + goto err_out; + opinfo->is_lease = 1; + } + + /* ci does not have any oplock */ + if (!opinfo_count(fp)) + goto set_lev; + + /* grant none-oplock if second open is trunc */ + if (fp->attrib_only && fp->cdoption != FILE_OVERWRITE_IF_LE && + fp->cdoption != FILE_OVERWRITE_LE && + fp->cdoption != FILE_SUPERSEDE_LE) { + req_op_level = SMB2_OPLOCK_LEVEL_NONE; + goto set_lev; + } + + if (lctx) { + struct oplock_info *m_opinfo; + + /* is lease already granted ? */ + m_opinfo = same_client_has_lease(ci, sess->ClientGUID, + lctx); + if (m_opinfo) { + copy_lease(m_opinfo, opinfo); + if (atomic_read(&m_opinfo->breaking_cnt)) + opinfo->o_lease->flags = + SMB2_LEASE_FLAG_BREAK_IN_PROGRESS_LE; + goto out; + } + } + prev_opinfo = opinfo_get_list(ci); + if (!prev_opinfo || + (prev_opinfo->level == SMB2_OPLOCK_LEVEL_NONE && lctx)) { + opinfo_conn_put(prev_opinfo); + goto set_lev; + } + prev_op_has_lease = prev_opinfo->is_lease; + if (prev_op_has_lease) + prev_op_state = prev_opinfo->o_lease->state; + + if (share_ret < 0 && + prev_opinfo->level == SMB2_OPLOCK_LEVEL_EXCLUSIVE) { + err = share_ret; + opinfo_conn_put(prev_opinfo); + goto err_out; + } + + if (prev_opinfo->level != SMB2_OPLOCK_LEVEL_BATCH && + prev_opinfo->level != SMB2_OPLOCK_LEVEL_EXCLUSIVE) { + opinfo_conn_put(prev_opinfo); + goto op_break_not_needed; + } + + list_add(&work->interim_entry, &prev_opinfo->interim_list); + err = oplock_break(prev_opinfo, SMB2_OPLOCK_LEVEL_II); + opinfo_conn_put(prev_opinfo); + if (err == -ENOENT) + goto set_lev; + /* Check all oplock was freed by close */ + else if (err < 0) + goto err_out; + +op_break_not_needed: + if (share_ret < 0) { + err = share_ret; + goto err_out; + } + + if (req_op_level != SMB2_OPLOCK_LEVEL_NONE) + req_op_level = SMB2_OPLOCK_LEVEL_II; + + /* grant fixed oplock on stacked locking between lease and oplock */ + if (prev_op_has_lease && !lctx) + if (prev_op_state & SMB2_LEASE_HANDLE_CACHING_LE) + req_op_level = SMB2_OPLOCK_LEVEL_NONE; + + if (!prev_op_has_lease && lctx) { + req_op_level = SMB2_OPLOCK_LEVEL_II; + lctx->req_state = SMB2_LEASE_READ_CACHING_LE; + } + +set_lev: + set_oplock_level(opinfo, req_op_level, lctx); + +out: + rcu_assign_pointer(fp->f_opinfo, opinfo); + opinfo->o_fp = fp; + + opinfo_count_inc(fp); + opinfo_add(opinfo); + if (opinfo->is_lease) { + err = add_lease_global_list(opinfo); + if (err) + goto err_out; + } + + return 0; +err_out: + free_opinfo(opinfo); + return err; +} + +/** + * smb_break_all_write_oplock() - break batch/exclusive oplock to level2 + * @work: smb work + * @fp: ksmbd file pointer + * @is_trunc: truncate on open + */ +static void smb_break_all_write_oplock(struct ksmbd_work *work, + struct ksmbd_file *fp, int is_trunc) +{ + struct oplock_info *brk_opinfo; + + brk_opinfo = opinfo_get_list(fp->f_ci); + if (!brk_opinfo) + return; + if (brk_opinfo->level != SMB2_OPLOCK_LEVEL_BATCH && + brk_opinfo->level != SMB2_OPLOCK_LEVEL_EXCLUSIVE) { + opinfo_conn_put(brk_opinfo); + return; + } + + brk_opinfo->open_trunc = is_trunc; + list_add(&work->interim_entry, &brk_opinfo->interim_list); + oplock_break(brk_opinfo, SMB2_OPLOCK_LEVEL_II); + opinfo_conn_put(brk_opinfo); +} + +/** + * smb_break_all_levII_oplock() - send level2 oplock or read lease break command + * from server to client + * @work: smb work + * @fp: ksmbd file pointer + * @is_trunc: truncate on open + */ +void smb_break_all_levII_oplock(struct ksmbd_work *work, struct ksmbd_file *fp, + int is_trunc) +{ + struct oplock_info *op, *brk_op; + struct ksmbd_inode *ci; + struct ksmbd_conn *conn = work->conn; + + if (!test_share_config_flag(work->tcon->share_conf, + KSMBD_SHARE_FLAG_OPLOCKS)) + return; + + ci = fp->f_ci; + op = opinfo_get(fp); + + rcu_read_lock(); + list_for_each_entry_rcu(brk_op, &ci->m_op_list, op_entry) { + if (!atomic_inc_not_zero(&brk_op->refcount)) + continue; + + atomic_inc(&brk_op->conn->r_count); + if (ksmbd_conn_releasing(brk_op->conn)) { + atomic_dec(&brk_op->conn->r_count); + continue; + } + + rcu_read_unlock(); + if (brk_op->is_lease && (brk_op->o_lease->state & + (~(SMB2_LEASE_READ_CACHING_LE | + SMB2_LEASE_HANDLE_CACHING_LE)))) { + ksmbd_debug(OPLOCK, "unexpected lease state(0x%x)\n", + brk_op->o_lease->state); + goto next; + } else if (brk_op->level != + SMB2_OPLOCK_LEVEL_II) { + ksmbd_debug(OPLOCK, "unexpected oplock(0x%x)\n", + brk_op->level); + goto next; + } + + /* Skip oplock being break to none */ + if (brk_op->is_lease && + brk_op->o_lease->new_state == SMB2_LEASE_NONE_LE && + atomic_read(&brk_op->breaking_cnt)) + goto next; + + if (op && op->is_lease && brk_op->is_lease && + !memcmp(conn->ClientGUID, brk_op->conn->ClientGUID, + SMB2_CLIENT_GUID_SIZE) && + !memcmp(op->o_lease->lease_key, brk_op->o_lease->lease_key, + SMB2_LEASE_KEY_SIZE)) + goto next; + brk_op->open_trunc = is_trunc; + oplock_break(brk_op, SMB2_OPLOCK_LEVEL_NONE); +next: + opinfo_conn_put(brk_op); + rcu_read_lock(); + } + rcu_read_unlock(); + + if (op) + opinfo_put(op); +} + +/** + * smb_break_all_oplock() - break both batch/exclusive and level2 oplock + * @work: smb work + * @fp: ksmbd file pointer + */ +void smb_break_all_oplock(struct ksmbd_work *work, struct ksmbd_file *fp) +{ + if (!test_share_config_flag(work->tcon->share_conf, + KSMBD_SHARE_FLAG_OPLOCKS)) + return; + + smb_break_all_write_oplock(work, fp, 1); + smb_break_all_levII_oplock(work, fp, 1); +} + +/** + * smb2_map_lease_to_oplock() - map lease state to corresponding oplock type + * @lease_state: lease type + * + * Return: 0 if no mapping, otherwise corresponding oplock type + */ +__u8 smb2_map_lease_to_oplock(__le32 lease_state) +{ + if (lease_state == (SMB2_LEASE_HANDLE_CACHING_LE | + SMB2_LEASE_READ_CACHING_LE | + SMB2_LEASE_WRITE_CACHING_LE)) { + return SMB2_OPLOCK_LEVEL_BATCH; + } else if (lease_state != SMB2_LEASE_WRITE_CACHING_LE && + lease_state & SMB2_LEASE_WRITE_CACHING_LE) { + if (!(lease_state & SMB2_LEASE_HANDLE_CACHING_LE)) + return SMB2_OPLOCK_LEVEL_EXCLUSIVE; + } else if (lease_state & SMB2_LEASE_READ_CACHING_LE) { + return SMB2_OPLOCK_LEVEL_II; + } + return 0; +} + +/** + * create_lease_buf() - create lease context for open cmd response + * @rbuf: buffer to create lease context response + * @lease: buffer to stored parsed lease state information + */ +void create_lease_buf(u8 *rbuf, struct lease *lease) +{ + if (lease->version == 2) { + struct create_lease_v2 *buf = (struct create_lease_v2 *)rbuf; + + memset(buf, 0, sizeof(struct create_lease_v2)); + memcpy(buf->lcontext.LeaseKey, lease->lease_key, + SMB2_LEASE_KEY_SIZE); + buf->lcontext.LeaseFlags = lease->flags; + buf->lcontext.LeaseState = lease->state; + memcpy(buf->lcontext.ParentLeaseKey, lease->parent_lease_key, + SMB2_LEASE_KEY_SIZE); + buf->ccontext.DataOffset = cpu_to_le16(offsetof + (struct create_lease_v2, lcontext)); + buf->ccontext.DataLength = cpu_to_le32(sizeof(struct lease_context_v2)); + buf->ccontext.NameOffset = cpu_to_le16(offsetof + (struct create_lease_v2, Name)); + buf->ccontext.NameLength = cpu_to_le16(4); + buf->Name[0] = 'R'; + buf->Name[1] = 'q'; + buf->Name[2] = 'L'; + buf->Name[3] = 's'; + } else { + struct create_lease *buf = (struct create_lease *)rbuf; + + memset(buf, 0, sizeof(struct create_lease)); + memcpy(buf->lcontext.LeaseKey, lease->lease_key, SMB2_LEASE_KEY_SIZE); + buf->lcontext.LeaseFlags = lease->flags; + buf->lcontext.LeaseState = lease->state; + buf->ccontext.DataOffset = cpu_to_le16(offsetof + (struct create_lease, lcontext)); + buf->ccontext.DataLength = cpu_to_le32(sizeof(struct lease_context)); + buf->ccontext.NameOffset = cpu_to_le16(offsetof + (struct create_lease, Name)); + buf->ccontext.NameLength = cpu_to_le16(4); + buf->Name[0] = 'R'; + buf->Name[1] = 'q'; + buf->Name[2] = 'L'; + buf->Name[3] = 's'; + } +} + +/** + * parse_lease_state() - parse lease context containted in file open request + * @open_req: buffer containing smb2 file open(create) request + * + * Return: oplock state, -ENOENT if create lease context not found + */ +struct lease_ctx_info *parse_lease_state(void *open_req) +{ + struct create_context *cc; + struct smb2_create_req *req = (struct smb2_create_req *)open_req; + struct lease_ctx_info *lreq; + + cc = smb2_find_context_vals(req, SMB2_CREATE_REQUEST_LEASE, 4); + if (IS_ERR_OR_NULL(cc)) + return NULL; + + lreq = kzalloc(sizeof(struct lease_ctx_info), GFP_KERNEL); + if (!lreq) + return NULL; + + if (sizeof(struct lease_context_v2) == le32_to_cpu(cc->DataLength)) { + struct create_lease_v2 *lc = (struct create_lease_v2 *)cc; + + memcpy(lreq->lease_key, lc->lcontext.LeaseKey, SMB2_LEASE_KEY_SIZE); + lreq->req_state = lc->lcontext.LeaseState; + lreq->flags = lc->lcontext.LeaseFlags; + lreq->duration = lc->lcontext.LeaseDuration; + memcpy(lreq->parent_lease_key, lc->lcontext.ParentLeaseKey, + SMB2_LEASE_KEY_SIZE); + lreq->version = 2; + } else { + struct create_lease *lc = (struct create_lease *)cc; + + memcpy(lreq->lease_key, lc->lcontext.LeaseKey, SMB2_LEASE_KEY_SIZE); + lreq->req_state = lc->lcontext.LeaseState; + lreq->flags = lc->lcontext.LeaseFlags; + lreq->duration = lc->lcontext.LeaseDuration; + lreq->version = 1; + } + return lreq; +} + +/** + * smb2_find_context_vals() - find a particular context info in open request + * @open_req: buffer containing smb2 file open(create) request + * @tag: context name to search for + * @tag_len: the length of tag + * + * Return: pointer to requested context, NULL if @str context not found + * or error pointer if name length is invalid. + */ +struct create_context *smb2_find_context_vals(void *open_req, const char *tag, int tag_len) +{ + struct create_context *cc; + unsigned int next = 0; + char *name; + struct smb2_create_req *req = (struct smb2_create_req *)open_req; + unsigned int remain_len, name_off, name_len, value_off, value_len, + cc_len; + + /* + * CreateContextsOffset and CreateContextsLength are guaranteed to + * be valid because of ksmbd_smb2_check_message(). + */ + cc = (struct create_context *)((char *)req + + le32_to_cpu(req->CreateContextsOffset)); + remain_len = le32_to_cpu(req->CreateContextsLength); + do { + cc = (struct create_context *)((char *)cc + next); + if (remain_len < offsetof(struct create_context, Buffer)) + return ERR_PTR(-EINVAL); + + next = le32_to_cpu(cc->Next); + name_off = le16_to_cpu(cc->NameOffset); + name_len = le16_to_cpu(cc->NameLength); + value_off = le16_to_cpu(cc->DataOffset); + value_len = le32_to_cpu(cc->DataLength); + cc_len = next ? next : remain_len; + + if ((next & 0x7) != 0 || + next > remain_len || + name_off != offsetof(struct create_context, Buffer) || + name_len < 4 || + name_off + name_len > cc_len || + (value_off & 0x7) != 0 || + (value_off && (value_off < name_off + name_len)) || + ((u64)value_off + value_len > cc_len)) + return ERR_PTR(-EINVAL); + + name = (char *)cc + name_off; + if (name_len == tag_len && !memcmp(name, tag, name_len)) + return cc; + + remain_len -= next; + } while (next != 0); + + return NULL; +} + +/** + * create_durable_rsp_buf() - create durable handle context + * @cc: buffer to create durable context response + */ +void create_durable_rsp_buf(char *cc) +{ + struct create_durable_rsp *buf; + + buf = (struct create_durable_rsp *)cc; + memset(buf, 0, sizeof(struct create_durable_rsp)); + buf->ccontext.DataOffset = cpu_to_le16(offsetof + (struct create_durable_rsp, Data)); + buf->ccontext.DataLength = cpu_to_le32(8); + buf->ccontext.NameOffset = cpu_to_le16(offsetof + (struct create_durable_rsp, Name)); + buf->ccontext.NameLength = cpu_to_le16(4); + /* SMB2_CREATE_DURABLE_HANDLE_RESPONSE is "DHnQ" */ + buf->Name[0] = 'D'; + buf->Name[1] = 'H'; + buf->Name[2] = 'n'; + buf->Name[3] = 'Q'; +} + +/** + * create_durable_v2_rsp_buf() - create durable handle v2 context + * @cc: buffer to create durable context response + * @fp: ksmbd file pointer + */ +void create_durable_v2_rsp_buf(char *cc, struct ksmbd_file *fp) +{ + struct create_durable_v2_rsp *buf; + + buf = (struct create_durable_v2_rsp *)cc; + memset(buf, 0, sizeof(struct create_durable_rsp)); + buf->ccontext.DataOffset = cpu_to_le16(offsetof + (struct create_durable_rsp, Data)); + buf->ccontext.DataLength = cpu_to_le32(8); + buf->ccontext.NameOffset = cpu_to_le16(offsetof + (struct create_durable_rsp, Name)); + buf->ccontext.NameLength = cpu_to_le16(4); + /* SMB2_CREATE_DURABLE_HANDLE_RESPONSE_V2 is "DH2Q" */ + buf->Name[0] = 'D'; + buf->Name[1] = 'H'; + buf->Name[2] = '2'; + buf->Name[3] = 'Q'; + + buf->Timeout = cpu_to_le32(fp->durable_timeout); +} + +/** + * create_mxac_rsp_buf() - create query maximal access context + * @cc: buffer to create maximal access context response + * @maximal_access: maximal access + */ +void create_mxac_rsp_buf(char *cc, int maximal_access) +{ + struct create_mxac_rsp *buf; + + buf = (struct create_mxac_rsp *)cc; + memset(buf, 0, sizeof(struct create_mxac_rsp)); + buf->ccontext.DataOffset = cpu_to_le16(offsetof + (struct create_mxac_rsp, QueryStatus)); + buf->ccontext.DataLength = cpu_to_le32(8); + buf->ccontext.NameOffset = cpu_to_le16(offsetof + (struct create_mxac_rsp, Name)); + buf->ccontext.NameLength = cpu_to_le16(4); + /* SMB2_CREATE_QUERY_MAXIMAL_ACCESS_RESPONSE is "MxAc" */ + buf->Name[0] = 'M'; + buf->Name[1] = 'x'; + buf->Name[2] = 'A'; + buf->Name[3] = 'c'; + + buf->QueryStatus = STATUS_SUCCESS; + buf->MaximalAccess = cpu_to_le32(maximal_access); +} + +void create_disk_id_rsp_buf(char *cc, __u64 file_id, __u64 vol_id) +{ + struct create_disk_id_rsp *buf; + + buf = (struct create_disk_id_rsp *)cc; + memset(buf, 0, sizeof(struct create_disk_id_rsp)); + buf->ccontext.DataOffset = cpu_to_le16(offsetof + (struct create_disk_id_rsp, DiskFileId)); + buf->ccontext.DataLength = cpu_to_le32(32); + buf->ccontext.NameOffset = cpu_to_le16(offsetof + (struct create_mxac_rsp, Name)); + buf->ccontext.NameLength = cpu_to_le16(4); + /* SMB2_CREATE_QUERY_ON_DISK_ID_RESPONSE is "QFid" */ + buf->Name[0] = 'Q'; + buf->Name[1] = 'F'; + buf->Name[2] = 'i'; + buf->Name[3] = 'd'; + + buf->DiskFileId = cpu_to_le64(file_id); + buf->VolumeId = cpu_to_le64(vol_id); +} + +/** + * create_posix_rsp_buf() - create posix extension context + * @cc: buffer to create posix on posix response + * @fp: ksmbd file pointer + */ +void create_posix_rsp_buf(char *cc, struct ksmbd_file *fp) +{ + struct create_posix_rsp *buf; + struct inode *inode = file_inode(fp->filp); + struct user_namespace *user_ns = file_mnt_user_ns(fp->filp); + vfsuid_t vfsuid = i_uid_into_vfsuid(user_ns, inode); + vfsgid_t vfsgid = i_gid_into_vfsgid(user_ns, inode); + + buf = (struct create_posix_rsp *)cc; + memset(buf, 0, sizeof(struct create_posix_rsp)); + buf->ccontext.DataOffset = cpu_to_le16(offsetof + (struct create_posix_rsp, nlink)); + /* + * DataLength = nlink(4) + reparse_tag(4) + mode(4) + + * domain sid(28) + unix group sid(16). + */ + buf->ccontext.DataLength = cpu_to_le32(56); + buf->ccontext.NameOffset = cpu_to_le16(offsetof + (struct create_posix_rsp, Name)); + buf->ccontext.NameLength = cpu_to_le16(POSIX_CTXT_DATA_LEN); + /* SMB2_CREATE_TAG_POSIX is "0x93AD25509CB411E7B42383DE968BCD7C" */ + buf->Name[0] = 0x93; + buf->Name[1] = 0xAD; + buf->Name[2] = 0x25; + buf->Name[3] = 0x50; + buf->Name[4] = 0x9C; + buf->Name[5] = 0xB4; + buf->Name[6] = 0x11; + buf->Name[7] = 0xE7; + buf->Name[8] = 0xB4; + buf->Name[9] = 0x23; + buf->Name[10] = 0x83; + buf->Name[11] = 0xDE; + buf->Name[12] = 0x96; + buf->Name[13] = 0x8B; + buf->Name[14] = 0xCD; + buf->Name[15] = 0x7C; + + buf->nlink = cpu_to_le32(inode->i_nlink); + buf->reparse_tag = cpu_to_le32(fp->volatile_id); + buf->mode = cpu_to_le32(inode->i_mode & 0777); + /* + * SidBuffer(44) contain two sids(Domain sid(28), UNIX group sid(16)). + * Domain sid(28) = revision(1) + num_subauth(1) + authority(6) + + * sub_auth(4 * 4(num_subauth)) + RID(4). + * UNIX group id(16) = revision(1) + num_subauth(1) + authority(6) + + * sub_auth(4 * 1(num_subauth)) + RID(4). + */ + id_to_sid(from_kuid_munged(&init_user_ns, vfsuid_into_kuid(vfsuid)), + SIDOWNER, (struct smb_sid *)&buf->SidBuffer[0]); + id_to_sid(from_kgid_munged(&init_user_ns, vfsgid_into_kgid(vfsgid)), + SIDUNIX_GROUP, (struct smb_sid *)&buf->SidBuffer[28]); +} + +/* + * Find lease object(opinfo) for given lease key/fid from lease + * break/file close path. + */ +/** + * lookup_lease_in_table() - find a matching lease info object + * @conn: connection instance + * @lease_key: lease key to be searched for + * + * Return: opinfo if found matching opinfo, otherwise NULL + */ +struct oplock_info *lookup_lease_in_table(struct ksmbd_conn *conn, + char *lease_key) +{ + struct oplock_info *opinfo = NULL, *ret_op = NULL; + struct lease_table *lt; + int ret; + + read_lock(&lease_list_lock); + list_for_each_entry(lt, &lease_table_list, l_entry) { + if (!memcmp(lt->client_guid, conn->ClientGUID, + SMB2_CLIENT_GUID_SIZE)) + goto found; + } + + read_unlock(&lease_list_lock); + return NULL; + +found: + rcu_read_lock(); + list_for_each_entry_rcu(opinfo, <->lease_list, lease_entry) { + if (!atomic_inc_not_zero(&opinfo->refcount)) + continue; + rcu_read_unlock(); + if (!opinfo->op_state || opinfo->op_state == OPLOCK_CLOSING) + goto op_next; + if (!(opinfo->o_lease->state & + (SMB2_LEASE_HANDLE_CACHING_LE | + SMB2_LEASE_WRITE_CACHING_LE))) + goto op_next; + ret = compare_guid_key(opinfo, conn->ClientGUID, + lease_key); + if (ret) { + ksmbd_debug(OPLOCK, "found opinfo\n"); + ret_op = opinfo; + goto out; + } +op_next: + opinfo_put(opinfo); + rcu_read_lock(); + } + rcu_read_unlock(); + +out: + read_unlock(&lease_list_lock); + return ret_op; +} diff --git a/fs/smb/server/oplock.h b/fs/smb/server/oplock.h new file mode 100644 index 0000000000000..4b0fe6da76940 --- /dev/null +++ b/fs/smb/server/oplock.h @@ -0,0 +1,127 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + * Copyright (C) 2016 Namjae Jeon + * Copyright (C) 2018 Samsung Electronics Co., Ltd. + */ + +#ifndef __KSMBD_OPLOCK_H +#define __KSMBD_OPLOCK_H + +#include "smb_common.h" + +#define OPLOCK_WAIT_TIME (35 * HZ) + +/* SMB2 Oplock levels */ +#define SMB2_OPLOCK_LEVEL_NONE 0x00 +#define SMB2_OPLOCK_LEVEL_II 0x01 +#define SMB2_OPLOCK_LEVEL_EXCLUSIVE 0x08 +#define SMB2_OPLOCK_LEVEL_BATCH 0x09 +#define SMB2_OPLOCK_LEVEL_LEASE 0xFF + +/* Oplock states */ +#define OPLOCK_STATE_NONE 0x00 +#define OPLOCK_ACK_WAIT 0x01 +#define OPLOCK_CLOSING 0x02 + +#define OPLOCK_WRITE_TO_READ 0x01 +#define OPLOCK_READ_HANDLE_TO_READ 0x02 +#define OPLOCK_WRITE_TO_NONE 0x04 +#define OPLOCK_READ_TO_NONE 0x08 + +struct lease_ctx_info { + __u8 lease_key[SMB2_LEASE_KEY_SIZE]; + __le32 req_state; + __le32 flags; + __le64 duration; + __u8 parent_lease_key[SMB2_LEASE_KEY_SIZE]; + int version; +}; + +struct lease_table { + char client_guid[SMB2_CLIENT_GUID_SIZE]; + struct list_head lease_list; + struct list_head l_entry; + spinlock_t lb_lock; +}; + +struct lease { + __u8 lease_key[SMB2_LEASE_KEY_SIZE]; + __le32 state; + __le32 new_state; + __le32 flags; + __le64 duration; + __u8 parent_lease_key[SMB2_LEASE_KEY_SIZE]; + int version; + unsigned short epoch; + struct lease_table *l_lb; +}; + +struct oplock_info { + struct ksmbd_conn *conn; + struct ksmbd_session *sess; + struct ksmbd_work *work; + struct ksmbd_file *o_fp; + int level; + int op_state; + unsigned long pending_break; + u64 fid; + atomic_t breaking_cnt; + atomic_t refcount; + __u16 Tid; + bool is_lease; + bool open_trunc; /* truncate on open */ + struct lease *o_lease; + struct list_head interim_list; + struct list_head op_entry; + struct list_head lease_entry; + wait_queue_head_t oplock_q; /* Other server threads */ + wait_queue_head_t oplock_brk; /* oplock breaking wait */ + struct rcu_head rcu_head; +}; + +struct lease_break_info { + __le32 curr_state; + __le32 new_state; + __le16 epoch; + char lease_key[SMB2_LEASE_KEY_SIZE]; +}; + +struct oplock_break_info { + int level; + int open_trunc; + int fid; +}; + +int smb_grant_oplock(struct ksmbd_work *work, int req_op_level, + u64 pid, struct ksmbd_file *fp, __u16 tid, + struct lease_ctx_info *lctx, int share_ret); +void smb_break_all_levII_oplock(struct ksmbd_work *work, + struct ksmbd_file *fp, int is_trunc); +int opinfo_write_to_read(struct oplock_info *opinfo); +int opinfo_read_handle_to_read(struct oplock_info *opinfo); +int opinfo_write_to_none(struct oplock_info *opinfo); +int opinfo_read_to_none(struct oplock_info *opinfo); +void close_id_del_oplock(struct ksmbd_file *fp); +void smb_break_all_oplock(struct ksmbd_work *work, struct ksmbd_file *fp); +struct oplock_info *opinfo_get(struct ksmbd_file *fp); +void opinfo_put(struct oplock_info *opinfo); + +/* Lease related functions */ +void create_lease_buf(u8 *rbuf, struct lease *lease); +struct lease_ctx_info *parse_lease_state(void *open_req); +__u8 smb2_map_lease_to_oplock(__le32 lease_state); +int lease_read_to_write(struct oplock_info *opinfo); + +/* Durable related functions */ +void create_durable_rsp_buf(char *cc); +void create_durable_v2_rsp_buf(char *cc, struct ksmbd_file *fp); +void create_mxac_rsp_buf(char *cc, int maximal_access); +void create_disk_id_rsp_buf(char *cc, __u64 file_id, __u64 vol_id); +void create_posix_rsp_buf(char *cc, struct ksmbd_file *fp); +struct create_context *smb2_find_context_vals(void *open_req, const char *tag, int tag_len); +struct oplock_info *lookup_lease_in_table(struct ksmbd_conn *conn, + char *lease_key); +int find_same_lease_key(struct ksmbd_session *sess, struct ksmbd_inode *ci, + struct lease_ctx_info *lctx); +void destroy_lease_table(struct ksmbd_conn *conn); +#endif /* __KSMBD_OPLOCK_H */ diff --git a/fs/smb/server/server.c b/fs/smb/server/server.c new file mode 100644 index 0000000000000..847ee62afb8a1 --- /dev/null +++ b/fs/smb/server/server.c @@ -0,0 +1,646 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Copyright (C) 2016 Namjae Jeon + * Copyright (C) 2018 Samsung Electronics Co., Ltd. + */ + +#include "glob.h" +#include "oplock.h" +#include "misc.h" +#include +#include +#include +#include +#include + +#include "server.h" +#include "smb_common.h" +#include "smbstatus.h" +#include "connection.h" +#include "transport_ipc.h" +#include "mgmt/user_session.h" +#include "crypto_ctx.h" +#include "auth.h" + +int ksmbd_debug_types; + +struct ksmbd_server_config server_conf; + +enum SERVER_CTRL_TYPE { + SERVER_CTRL_TYPE_INIT, + SERVER_CTRL_TYPE_RESET, +}; + +struct server_ctrl_struct { + int type; + struct work_struct ctrl_work; +}; + +static DEFINE_MUTEX(ctrl_lock); + +static int ___server_conf_set(int idx, char *val) +{ + if (idx >= ARRAY_SIZE(server_conf.conf)) + return -EINVAL; + + if (!val || val[0] == 0x00) + return -EINVAL; + + kfree(server_conf.conf[idx]); + server_conf.conf[idx] = kstrdup(val, GFP_KERNEL); + if (!server_conf.conf[idx]) + return -ENOMEM; + return 0; +} + +int ksmbd_set_netbios_name(char *v) +{ + return ___server_conf_set(SERVER_CONF_NETBIOS_NAME, v); +} + +int ksmbd_set_server_string(char *v) +{ + return ___server_conf_set(SERVER_CONF_SERVER_STRING, v); +} + +int ksmbd_set_work_group(char *v) +{ + return ___server_conf_set(SERVER_CONF_WORK_GROUP, v); +} + +char *ksmbd_netbios_name(void) +{ + return server_conf.conf[SERVER_CONF_NETBIOS_NAME]; +} + +char *ksmbd_server_string(void) +{ + return server_conf.conf[SERVER_CONF_SERVER_STRING]; +} + +char *ksmbd_work_group(void) +{ + return server_conf.conf[SERVER_CONF_WORK_GROUP]; +} + +/** + * check_conn_state() - check state of server thread connection + * @work: smb work containing server thread information + * + * Return: 0 on valid connection, otherwise 1 to reconnect + */ +static inline int check_conn_state(struct ksmbd_work *work) +{ + struct smb_hdr *rsp_hdr; + + if (ksmbd_conn_exiting(work->conn) || + ksmbd_conn_need_reconnect(work->conn)) { + rsp_hdr = work->response_buf; + rsp_hdr->Status.CifsError = STATUS_CONNECTION_DISCONNECTED; + return 1; + } + return 0; +} + +#define SERVER_HANDLER_CONTINUE 0 +#define SERVER_HANDLER_ABORT 1 + +static int __process_request(struct ksmbd_work *work, struct ksmbd_conn *conn, + u16 *cmd) +{ + struct smb_version_cmds *cmds; + u16 command; + int ret; + + if (check_conn_state(work)) + return SERVER_HANDLER_CONTINUE; + + if (ksmbd_verify_smb_message(work)) + return SERVER_HANDLER_ABORT; + + command = conn->ops->get_cmd_val(work); + *cmd = command; + +andx_again: + if (command >= conn->max_cmds) { + conn->ops->set_rsp_status(work, STATUS_INVALID_PARAMETER); + return SERVER_HANDLER_CONTINUE; + } + + cmds = &conn->cmds[command]; + if (!cmds->proc) { + ksmbd_debug(SMB, "*** not implemented yet cmd = %x\n", command); + conn->ops->set_rsp_status(work, STATUS_NOT_IMPLEMENTED); + return SERVER_HANDLER_CONTINUE; + } + + if (work->sess && conn->ops->is_sign_req(work, command)) { + ret = conn->ops->check_sign_req(work); + if (!ret) { + conn->ops->set_rsp_status(work, STATUS_ACCESS_DENIED); + return SERVER_HANDLER_CONTINUE; + } + } + + ret = cmds->proc(work); + + if (ret < 0) + ksmbd_debug(CONN, "Failed to process %u [%d]\n", command, ret); + /* AndX commands - chained request can return positive values */ + else if (ret > 0) { + command = ret; + *cmd = command; + goto andx_again; + } + + if (work->send_no_response) + return SERVER_HANDLER_ABORT; + return SERVER_HANDLER_CONTINUE; +} + +static void __handle_ksmbd_work(struct ksmbd_work *work, + struct ksmbd_conn *conn) +{ + u16 command = 0; + int rc; + + if (conn->ops->allocate_rsp_buf(work)) + return; + + if (conn->ops->is_transform_hdr && + conn->ops->is_transform_hdr(work->request_buf)) { + rc = conn->ops->decrypt_req(work); + if (rc < 0) { + conn->ops->set_rsp_status(work, STATUS_DATA_ERROR); + goto send; + } + + work->encrypted = true; + } + + rc = conn->ops->init_rsp_hdr(work); + if (rc) { + /* either uid or tid is not correct */ + conn->ops->set_rsp_status(work, STATUS_INVALID_HANDLE); + goto send; + } + + do { + if (conn->ops->check_user_session) { + rc = conn->ops->check_user_session(work); + if (rc < 0) { + if (rc == -EINVAL) + conn->ops->set_rsp_status(work, + STATUS_INVALID_PARAMETER); + else + conn->ops->set_rsp_status(work, + STATUS_USER_SESSION_DELETED); + goto send; + } else if (rc > 0) { + rc = conn->ops->get_ksmbd_tcon(work); + if (rc < 0) { + if (rc == -EINVAL) + conn->ops->set_rsp_status(work, + STATUS_INVALID_PARAMETER); + else + conn->ops->set_rsp_status(work, + STATUS_NETWORK_NAME_DELETED); + goto send; + } + } + } + + rc = __process_request(work, conn, &command); + if (rc == SERVER_HANDLER_ABORT) + break; + + /* + * Call smb2_set_rsp_credits() function to set number of credits + * granted in hdr of smb2 response. + */ + if (conn->ops->set_rsp_credits) { + spin_lock(&conn->credits_lock); + rc = conn->ops->set_rsp_credits(work); + spin_unlock(&conn->credits_lock); + if (rc < 0) { + conn->ops->set_rsp_status(work, + STATUS_INVALID_PARAMETER); + goto send; + } + } + + if (work->sess && + (work->sess->sign || smb3_11_final_sess_setup_resp(work) || + conn->ops->is_sign_req(work, command))) + conn->ops->set_sign_rsp(work); + } while (is_chained_smb2_message(work)); + + if (work->send_no_response) + return; + +send: + smb3_preauth_hash_rsp(work); + if (work->sess && work->sess->enc && work->encrypted && + conn->ops->encrypt_resp) { + rc = conn->ops->encrypt_resp(work); + if (rc < 0) + conn->ops->set_rsp_status(work, STATUS_DATA_ERROR); + } + + ksmbd_conn_write(work); +} + +/** + * handle_ksmbd_work() - process pending smb work requests + * @wk: smb work containing request command buffer + * + * called by kworker threads to processing remaining smb work requests + */ +static void handle_ksmbd_work(struct work_struct *wk) +{ + struct ksmbd_work *work = container_of(wk, struct ksmbd_work, work); + struct ksmbd_conn *conn = work->conn; + + atomic64_inc(&conn->stats.request_served); + + __handle_ksmbd_work(work, conn); + + ksmbd_conn_try_dequeue_request(work); + ksmbd_free_work_struct(work); + /* + * Checking waitqueue to dropping pending requests on + * disconnection. waitqueue_active is safe because it + * uses atomic operation for condition. + */ + if (!atomic_dec_return(&conn->r_count) && waitqueue_active(&conn->r_count_q)) + wake_up(&conn->r_count_q); +} + +/** + * queue_ksmbd_work() - queue a smb request to worker thread queue + * for proccessing smb command and sending response + * @conn: connection instance + * + * read remaining data from socket create and submit work. + */ +static int queue_ksmbd_work(struct ksmbd_conn *conn) +{ + struct ksmbd_work *work; + + work = ksmbd_alloc_work_struct(); + if (!work) { + pr_err("allocation for work failed\n"); + return -ENOMEM; + } + + work->conn = conn; + work->request_buf = conn->request_buf; + conn->request_buf = NULL; + + ksmbd_init_smb_server(work); + + ksmbd_conn_enqueue_request(work); + atomic_inc(&conn->r_count); + /* update activity on connection */ + conn->last_active = jiffies; + INIT_WORK(&work->work, handle_ksmbd_work); + ksmbd_queue_work(work); + return 0; +} + +static int ksmbd_server_process_request(struct ksmbd_conn *conn) +{ + return queue_ksmbd_work(conn); +} + +static int ksmbd_server_terminate_conn(struct ksmbd_conn *conn) +{ + ksmbd_sessions_deregister(conn); + destroy_lease_table(conn); + return 0; +} + +static void ksmbd_server_tcp_callbacks_init(void) +{ + struct ksmbd_conn_ops ops; + + ops.process_fn = ksmbd_server_process_request; + ops.terminate_fn = ksmbd_server_terminate_conn; + + ksmbd_conn_init_server_callbacks(&ops); +} + +static void server_conf_free(void) +{ + int i; + + for (i = 0; i < ARRAY_SIZE(server_conf.conf); i++) { + kfree(server_conf.conf[i]); + server_conf.conf[i] = NULL; + } +} + +static int server_conf_init(void) +{ + WRITE_ONCE(server_conf.state, SERVER_STATE_STARTING_UP); + server_conf.enforced_signing = 0; + server_conf.min_protocol = ksmbd_min_protocol(); + server_conf.max_protocol = ksmbd_max_protocol(); + server_conf.auth_mechs = KSMBD_AUTH_NTLMSSP; +#ifdef CONFIG_SMB_SERVER_KERBEROS5 + server_conf.auth_mechs |= KSMBD_AUTH_KRB5 | + KSMBD_AUTH_MSKRB5; +#endif + return 0; +} + +static void server_ctrl_handle_init(struct server_ctrl_struct *ctrl) +{ + int ret; + + ret = ksmbd_conn_transport_init(); + if (ret) { + server_queue_ctrl_reset_work(); + return; + } + + WRITE_ONCE(server_conf.state, SERVER_STATE_RUNNING); +} + +static void server_ctrl_handle_reset(struct server_ctrl_struct *ctrl) +{ + ksmbd_ipc_soft_reset(); + ksmbd_conn_transport_destroy(); + server_conf_free(); + server_conf_init(); + WRITE_ONCE(server_conf.state, SERVER_STATE_STARTING_UP); +} + +static void server_ctrl_handle_work(struct work_struct *work) +{ + struct server_ctrl_struct *ctrl; + + ctrl = container_of(work, struct server_ctrl_struct, ctrl_work); + + mutex_lock(&ctrl_lock); + switch (ctrl->type) { + case SERVER_CTRL_TYPE_INIT: + server_ctrl_handle_init(ctrl); + break; + case SERVER_CTRL_TYPE_RESET: + server_ctrl_handle_reset(ctrl); + break; + default: + pr_err("Unknown server work type: %d\n", ctrl->type); + } + mutex_unlock(&ctrl_lock); + kfree(ctrl); + module_put(THIS_MODULE); +} + +static int __queue_ctrl_work(int type) +{ + struct server_ctrl_struct *ctrl; + + ctrl = kmalloc(sizeof(struct server_ctrl_struct), GFP_KERNEL); + if (!ctrl) + return -ENOMEM; + + __module_get(THIS_MODULE); + ctrl->type = type; + INIT_WORK(&ctrl->ctrl_work, server_ctrl_handle_work); + queue_work(system_long_wq, &ctrl->ctrl_work); + return 0; +} + +int server_queue_ctrl_init_work(void) +{ + return __queue_ctrl_work(SERVER_CTRL_TYPE_INIT); +} + +int server_queue_ctrl_reset_work(void) +{ + return __queue_ctrl_work(SERVER_CTRL_TYPE_RESET); +} + +static ssize_t stats_show(struct class *class, struct class_attribute *attr, + char *buf) +{ + /* + * Inc this each time you change stats output format, + * so user space will know what to do. + */ + static int stats_version = 2; + static const char * const state[] = { + "startup", + "running", + "reset", + "shutdown" + }; + + ssize_t sz = scnprintf(buf, PAGE_SIZE, "%d %s %d %lu\n", stats_version, + state[server_conf.state], server_conf.tcp_port, + server_conf.ipc_last_active / HZ); + return sz; +} + +static ssize_t kill_server_store(struct class *class, + struct class_attribute *attr, const char *buf, + size_t len) +{ + if (!sysfs_streq(buf, "hard")) + return len; + + pr_info("kill command received\n"); + mutex_lock(&ctrl_lock); + WRITE_ONCE(server_conf.state, SERVER_STATE_RESETTING); + __module_get(THIS_MODULE); + server_ctrl_handle_reset(NULL); + module_put(THIS_MODULE); + mutex_unlock(&ctrl_lock); + return len; +} + +static const char * const debug_type_strings[] = {"smb", "auth", "vfs", + "oplock", "ipc", "conn", + "rdma"}; + +static ssize_t debug_show(struct class *class, struct class_attribute *attr, + char *buf) +{ + ssize_t sz = 0; + int i, pos = 0; + + for (i = 0; i < ARRAY_SIZE(debug_type_strings); i++) { + if ((ksmbd_debug_types >> i) & 1) { + pos = scnprintf(buf + sz, + PAGE_SIZE - sz, + "[%s] ", + debug_type_strings[i]); + } else { + pos = scnprintf(buf + sz, + PAGE_SIZE - sz, + "%s ", + debug_type_strings[i]); + } + sz += pos; + } + sz += scnprintf(buf + sz, PAGE_SIZE - sz, "\n"); + return sz; +} + +static ssize_t debug_store(struct class *class, struct class_attribute *attr, + const char *buf, size_t len) +{ + int i; + + for (i = 0; i < ARRAY_SIZE(debug_type_strings); i++) { + if (sysfs_streq(buf, "all")) { + if (ksmbd_debug_types == KSMBD_DEBUG_ALL) + ksmbd_debug_types = 0; + else + ksmbd_debug_types = KSMBD_DEBUG_ALL; + break; + } + + if (sysfs_streq(buf, debug_type_strings[i])) { + if (ksmbd_debug_types & (1 << i)) + ksmbd_debug_types &= ~(1 << i); + else + ksmbd_debug_types |= (1 << i); + break; + } + } + + return len; +} + +static CLASS_ATTR_RO(stats); +static CLASS_ATTR_WO(kill_server); +static CLASS_ATTR_RW(debug); + +static struct attribute *ksmbd_control_class_attrs[] = { + &class_attr_stats.attr, + &class_attr_kill_server.attr, + &class_attr_debug.attr, + NULL, +}; +ATTRIBUTE_GROUPS(ksmbd_control_class); + +static struct class ksmbd_control_class = { + .name = "ksmbd-control", + .owner = THIS_MODULE, + .class_groups = ksmbd_control_class_groups, +}; + +static int ksmbd_server_shutdown(void) +{ + WRITE_ONCE(server_conf.state, SERVER_STATE_SHUTTING_DOWN); + + class_unregister(&ksmbd_control_class); + ksmbd_workqueue_destroy(); + ksmbd_ipc_release(); + ksmbd_conn_transport_destroy(); + ksmbd_crypto_destroy(); + ksmbd_free_global_file_table(); + destroy_lease_table(NULL); + ksmbd_work_pool_destroy(); + ksmbd_exit_file_cache(); + server_conf_free(); + return 0; +} + +static int __init ksmbd_server_init(void) +{ + int ret; + + ret = class_register(&ksmbd_control_class); + if (ret) { + pr_err("Unable to register ksmbd-control class\n"); + return ret; + } + + ksmbd_server_tcp_callbacks_init(); + + ret = server_conf_init(); + if (ret) + goto err_unregister; + + ret = ksmbd_work_pool_init(); + if (ret) + goto err_unregister; + + ret = ksmbd_init_file_cache(); + if (ret) + goto err_destroy_work_pools; + + ret = ksmbd_ipc_init(); + if (ret) + goto err_exit_file_cache; + + ret = ksmbd_init_global_file_table(); + if (ret) + goto err_ipc_release; + + ret = ksmbd_inode_hash_init(); + if (ret) + goto err_destroy_file_table; + + ret = ksmbd_crypto_create(); + if (ret) + goto err_release_inode_hash; + + ret = ksmbd_workqueue_init(); + if (ret) + goto err_crypto_destroy; + + pr_warn_once("The ksmbd server is experimental\n"); + + return 0; + +err_crypto_destroy: + ksmbd_crypto_destroy(); +err_release_inode_hash: + ksmbd_release_inode_hash(); +err_destroy_file_table: + ksmbd_free_global_file_table(); +err_ipc_release: + ksmbd_ipc_release(); +err_exit_file_cache: + ksmbd_exit_file_cache(); +err_destroy_work_pools: + ksmbd_work_pool_destroy(); +err_unregister: + class_unregister(&ksmbd_control_class); + + return ret; +} + +/** + * ksmbd_server_exit() - shutdown forker thread and free memory at module exit + */ +static void __exit ksmbd_server_exit(void) +{ + ksmbd_server_shutdown(); + rcu_barrier(); + ksmbd_release_inode_hash(); +} + +MODULE_AUTHOR("Namjae Jeon "); +MODULE_VERSION(KSMBD_VERSION); +MODULE_DESCRIPTION("Linux kernel CIFS/SMB SERVER"); +MODULE_LICENSE("GPL"); +MODULE_SOFTDEP("pre: ecb"); +MODULE_SOFTDEP("pre: hmac"); +MODULE_SOFTDEP("pre: md5"); +MODULE_SOFTDEP("pre: nls"); +MODULE_SOFTDEP("pre: aes"); +MODULE_SOFTDEP("pre: cmac"); +MODULE_SOFTDEP("pre: sha256"); +MODULE_SOFTDEP("pre: sha512"); +MODULE_SOFTDEP("pre: aead2"); +MODULE_SOFTDEP("pre: ccm"); +MODULE_SOFTDEP("pre: gcm"); +MODULE_SOFTDEP("pre: crc32"); +module_init(ksmbd_server_init) +module_exit(ksmbd_server_exit) diff --git a/fs/smb/server/server.h b/fs/smb/server/server.h new file mode 100644 index 0000000000000..db72781817603 --- /dev/null +++ b/fs/smb/server/server.h @@ -0,0 +1,71 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + * Copyright (C) 2018 Samsung Electronics Co., Ltd. + */ + +#ifndef __SERVER_H__ +#define __SERVER_H__ + +#include "smbacl.h" + +/* + * Server state type + */ +enum { + SERVER_STATE_STARTING_UP, + SERVER_STATE_RUNNING, + SERVER_STATE_RESETTING, + SERVER_STATE_SHUTTING_DOWN, +}; + +/* + * Server global config string index + */ +enum { + SERVER_CONF_NETBIOS_NAME, + SERVER_CONF_SERVER_STRING, + SERVER_CONF_WORK_GROUP, +}; + +struct ksmbd_server_config { + unsigned int flags; + unsigned int state; + short signing; + short enforced_signing; + short min_protocol; + short max_protocol; + unsigned short tcp_port; + unsigned short ipc_timeout; + unsigned long ipc_last_active; + unsigned long deadtime; + unsigned int share_fake_fscaps; + struct smb_sid domain_sid; + unsigned int auth_mechs; + unsigned int max_connections; + + char *conf[SERVER_CONF_WORK_GROUP + 1]; +}; + +extern struct ksmbd_server_config server_conf; + +int ksmbd_set_netbios_name(char *v); +int ksmbd_set_server_string(char *v); +int ksmbd_set_work_group(char *v); + +char *ksmbd_netbios_name(void); +char *ksmbd_server_string(void); +char *ksmbd_work_group(void); + +static inline int ksmbd_server_running(void) +{ + return READ_ONCE(server_conf.state) == SERVER_STATE_RUNNING; +} + +static inline int ksmbd_server_configurable(void) +{ + return READ_ONCE(server_conf.state) < SERVER_STATE_RESETTING; +} + +int server_queue_ctrl_init_work(void); +int server_queue_ctrl_reset_work(void); +#endif /* __SERVER_H__ */ diff --git a/fs/smb/server/smb2misc.c b/fs/smb/server/smb2misc.c new file mode 100644 index 0000000000000..33b7e6c4ceffb --- /dev/null +++ b/fs/smb/server/smb2misc.c @@ -0,0 +1,454 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Copyright (C) 2016 Namjae Jeon + * Copyright (C) 2018 Samsung Electronics Co., Ltd. + */ + +#include "glob.h" +#include "nterr.h" +#include "smb_common.h" +#include "smbstatus.h" +#include "mgmt/user_session.h" +#include "connection.h" + +static int check_smb2_hdr(struct smb2_hdr *hdr) +{ + /* + * Make sure that this really is an SMB, that it is a response. + */ + if (hdr->Flags & SMB2_FLAGS_SERVER_TO_REDIR) + return 1; + return 0; +} + +/* + * The following table defines the expected "StructureSize" of SMB2 requests + * in order by SMB2 command. This is similar to "wct" in SMB/CIFS requests. + * + * Note that commands are defined in smb2pdu.h in le16 but the array below is + * indexed by command in host byte order + */ +static const __le16 smb2_req_struct_sizes[NUMBER_OF_SMB2_COMMANDS] = { + /* SMB2_NEGOTIATE */ cpu_to_le16(36), + /* SMB2_SESSION_SETUP */ cpu_to_le16(25), + /* SMB2_LOGOFF */ cpu_to_le16(4), + /* SMB2_TREE_CONNECT */ cpu_to_le16(9), + /* SMB2_TREE_DISCONNECT */ cpu_to_le16(4), + /* SMB2_CREATE */ cpu_to_le16(57), + /* SMB2_CLOSE */ cpu_to_le16(24), + /* SMB2_FLUSH */ cpu_to_le16(24), + /* SMB2_READ */ cpu_to_le16(49), + /* SMB2_WRITE */ cpu_to_le16(49), + /* SMB2_LOCK */ cpu_to_le16(48), + /* SMB2_IOCTL */ cpu_to_le16(57), + /* SMB2_CANCEL */ cpu_to_le16(4), + /* SMB2_ECHO */ cpu_to_le16(4), + /* SMB2_QUERY_DIRECTORY */ cpu_to_le16(33), + /* SMB2_CHANGE_NOTIFY */ cpu_to_le16(32), + /* SMB2_QUERY_INFO */ cpu_to_le16(41), + /* SMB2_SET_INFO */ cpu_to_le16(33), + /* use 44 for lease break */ + /* SMB2_OPLOCK_BREAK */ cpu_to_le16(36) +}; + +/* + * The size of the variable area depends on the offset and length fields + * located in different fields for various SMB2 requests. SMB2 requests + * with no variable length info, show an offset of zero for the offset field. + */ +static const bool has_smb2_data_area[NUMBER_OF_SMB2_COMMANDS] = { + /* SMB2_NEGOTIATE */ true, + /* SMB2_SESSION_SETUP */ true, + /* SMB2_LOGOFF */ false, + /* SMB2_TREE_CONNECT */ true, + /* SMB2_TREE_DISCONNECT */ false, + /* SMB2_CREATE */ true, + /* SMB2_CLOSE */ false, + /* SMB2_FLUSH */ false, + /* SMB2_READ */ true, + /* SMB2_WRITE */ true, + /* SMB2_LOCK */ true, + /* SMB2_IOCTL */ true, + /* SMB2_CANCEL */ false, /* BB CHECK this not listed in documentation */ + /* SMB2_ECHO */ false, + /* SMB2_QUERY_DIRECTORY */ true, + /* SMB2_CHANGE_NOTIFY */ false, + /* SMB2_QUERY_INFO */ true, + /* SMB2_SET_INFO */ true, + /* SMB2_OPLOCK_BREAK */ false +}; + +/* + * Set length of the data area and the offset to arguments. + * if they are invalid, return error. + */ +static int smb2_get_data_area_len(unsigned int *off, unsigned int *len, + struct smb2_hdr *hdr) +{ + int ret = 0; + + *off = 0; + *len = 0; + + /* + * Following commands have data areas so we have to get the location + * of the data buffer offset and data buffer length for the particular + * command. + */ + switch (hdr->Command) { + case SMB2_SESSION_SETUP: + *off = le16_to_cpu(((struct smb2_sess_setup_req *)hdr)->SecurityBufferOffset); + *len = le16_to_cpu(((struct smb2_sess_setup_req *)hdr)->SecurityBufferLength); + break; + case SMB2_TREE_CONNECT: + *off = le16_to_cpu(((struct smb2_tree_connect_req *)hdr)->PathOffset); + *len = le16_to_cpu(((struct smb2_tree_connect_req *)hdr)->PathLength); + break; + case SMB2_CREATE: + { + if (((struct smb2_create_req *)hdr)->CreateContextsLength) { + *off = le32_to_cpu(((struct smb2_create_req *) + hdr)->CreateContextsOffset); + *len = le32_to_cpu(((struct smb2_create_req *) + hdr)->CreateContextsLength); + break; + } + + *off = le16_to_cpu(((struct smb2_create_req *)hdr)->NameOffset); + *len = le16_to_cpu(((struct smb2_create_req *)hdr)->NameLength); + break; + } + case SMB2_QUERY_INFO: + *off = le16_to_cpu(((struct smb2_query_info_req *)hdr)->InputBufferOffset); + *len = le32_to_cpu(((struct smb2_query_info_req *)hdr)->InputBufferLength); + break; + case SMB2_SET_INFO: + *off = le16_to_cpu(((struct smb2_set_info_req *)hdr)->BufferOffset); + *len = le32_to_cpu(((struct smb2_set_info_req *)hdr)->BufferLength); + break; + case SMB2_READ: + *off = le16_to_cpu(((struct smb2_read_req *)hdr)->ReadChannelInfoOffset); + *len = le16_to_cpu(((struct smb2_read_req *)hdr)->ReadChannelInfoLength); + break; + case SMB2_WRITE: + if (((struct smb2_write_req *)hdr)->DataOffset || + ((struct smb2_write_req *)hdr)->Length) { + *off = max_t(unsigned int, + le16_to_cpu(((struct smb2_write_req *)hdr)->DataOffset), + offsetof(struct smb2_write_req, Buffer)); + *len = le32_to_cpu(((struct smb2_write_req *)hdr)->Length); + break; + } + + *off = le16_to_cpu(((struct smb2_write_req *)hdr)->WriteChannelInfoOffset); + *len = le16_to_cpu(((struct smb2_write_req *)hdr)->WriteChannelInfoLength); + break; + case SMB2_QUERY_DIRECTORY: + *off = le16_to_cpu(((struct smb2_query_directory_req *)hdr)->FileNameOffset); + *len = le16_to_cpu(((struct smb2_query_directory_req *)hdr)->FileNameLength); + break; + case SMB2_LOCK: + { + unsigned short lock_count; + + lock_count = le16_to_cpu(((struct smb2_lock_req *)hdr)->LockCount); + if (lock_count > 0) { + *off = offsetof(struct smb2_lock_req, locks); + *len = sizeof(struct smb2_lock_element) * lock_count; + } + break; + } + case SMB2_IOCTL: + *off = le32_to_cpu(((struct smb2_ioctl_req *)hdr)->InputOffset); + *len = le32_to_cpu(((struct smb2_ioctl_req *)hdr)->InputCount); + break; + default: + ksmbd_debug(SMB, "no length check for command\n"); + break; + } + + if (*off > 4096) { + ksmbd_debug(SMB, "offset %d too large\n", *off); + ret = -EINVAL; + } else if ((u64)*off + *len > MAX_STREAM_PROT_LEN) { + ksmbd_debug(SMB, "Request is larger than maximum stream protocol length(%u): %llu\n", + MAX_STREAM_PROT_LEN, (u64)*off + *len); + ret = -EINVAL; + } + + return ret; +} + +/* + * Calculate the size of the SMB message based on the fixed header + * portion, the number of word parameters and the data portion of the message. + */ +static int smb2_calc_size(void *buf, unsigned int *len) +{ + struct smb2_pdu *pdu = (struct smb2_pdu *)buf; + struct smb2_hdr *hdr = &pdu->hdr; + unsigned int offset; /* the offset from the beginning of SMB to data area */ + unsigned int data_length; /* the length of the variable length data area */ + int ret; + + /* Structure Size has already been checked to make sure it is 64 */ + *len = le16_to_cpu(hdr->StructureSize); + + /* + * StructureSize2, ie length of fixed parameter area has already + * been checked to make sure it is the correct length. + */ + *len += le16_to_cpu(pdu->StructureSize2); + /* + * StructureSize2 of smb2_lock pdu is set to 48, indicating + * the size of smb2 lock request with single smb2_lock_element + * regardless of number of locks. Subtract single + * smb2_lock_element for correct buffer size check. + */ + if (hdr->Command == SMB2_LOCK) + *len -= sizeof(struct smb2_lock_element); + + if (has_smb2_data_area[le16_to_cpu(hdr->Command)] == false) + goto calc_size_exit; + + ret = smb2_get_data_area_len(&offset, &data_length, hdr); + if (ret) + return ret; + ksmbd_debug(SMB, "SMB2 data length %u offset %u\n", data_length, + offset); + + if (data_length > 0) { + /* + * Check to make sure that data area begins after fixed area, + * Note that last byte of the fixed area is part of data area + * for some commands, typically those with odd StructureSize, + * so we must add one to the calculation. + */ + if (offset + 1 < *len) { + ksmbd_debug(SMB, + "data area offset %d overlaps SMB2 header %u\n", + offset + 1, *len); + return -EINVAL; + } + + *len = offset + data_length; + } + +calc_size_exit: + ksmbd_debug(SMB, "SMB2 len %u\n", *len); + return 0; +} + +static inline int smb2_query_info_req_len(struct smb2_query_info_req *h) +{ + return le32_to_cpu(h->InputBufferLength) + + le32_to_cpu(h->OutputBufferLength); +} + +static inline int smb2_set_info_req_len(struct smb2_set_info_req *h) +{ + return le32_to_cpu(h->BufferLength); +} + +static inline int smb2_read_req_len(struct smb2_read_req *h) +{ + return le32_to_cpu(h->Length); +} + +static inline int smb2_write_req_len(struct smb2_write_req *h) +{ + return le32_to_cpu(h->Length); +} + +static inline int smb2_query_dir_req_len(struct smb2_query_directory_req *h) +{ + return le32_to_cpu(h->OutputBufferLength); +} + +static inline int smb2_ioctl_req_len(struct smb2_ioctl_req *h) +{ + return le32_to_cpu(h->InputCount) + + le32_to_cpu(h->OutputCount); +} + +static inline int smb2_ioctl_resp_len(struct smb2_ioctl_req *h) +{ + return le32_to_cpu(h->MaxInputResponse) + + le32_to_cpu(h->MaxOutputResponse); +} + +static int smb2_validate_credit_charge(struct ksmbd_conn *conn, + struct smb2_hdr *hdr) +{ + unsigned int req_len = 0, expect_resp_len = 0, calc_credit_num, max_len; + unsigned short credit_charge = le16_to_cpu(hdr->CreditCharge); + void *__hdr = hdr; + int ret = 0; + + switch (hdr->Command) { + case SMB2_QUERY_INFO: + req_len = smb2_query_info_req_len(__hdr); + break; + case SMB2_SET_INFO: + req_len = smb2_set_info_req_len(__hdr); + break; + case SMB2_READ: + req_len = smb2_read_req_len(__hdr); + break; + case SMB2_WRITE: + req_len = smb2_write_req_len(__hdr); + break; + case SMB2_QUERY_DIRECTORY: + req_len = smb2_query_dir_req_len(__hdr); + break; + case SMB2_IOCTL: + req_len = smb2_ioctl_req_len(__hdr); + expect_resp_len = smb2_ioctl_resp_len(__hdr); + break; + case SMB2_CANCEL: + return 0; + default: + req_len = 1; + break; + } + + credit_charge = max_t(unsigned short, credit_charge, 1); + max_len = max_t(unsigned int, req_len, expect_resp_len); + calc_credit_num = DIV_ROUND_UP(max_len, SMB2_MAX_BUFFER_SIZE); + + if (credit_charge < calc_credit_num) { + ksmbd_debug(SMB, "Insufficient credit charge, given: %d, needed: %d\n", + credit_charge, calc_credit_num); + return 1; + } else if (credit_charge > conn->vals->max_credits) { + ksmbd_debug(SMB, "Too large credit charge: %d\n", credit_charge); + return 1; + } + + spin_lock(&conn->credits_lock); + if (credit_charge > conn->total_credits) { + ksmbd_debug(SMB, "Insufficient credits granted, given: %u, granted: %u\n", + credit_charge, conn->total_credits); + ret = 1; + } + + if ((u64)conn->outstanding_credits + credit_charge > conn->total_credits) { + ksmbd_debug(SMB, "Limits exceeding the maximum allowable outstanding requests, given : %u, pending : %u\n", + credit_charge, conn->outstanding_credits); + ret = 1; + } else + conn->outstanding_credits += credit_charge; + + spin_unlock(&conn->credits_lock); + + return ret; +} + +int ksmbd_smb2_check_message(struct ksmbd_work *work) +{ + struct smb2_pdu *pdu = ksmbd_req_buf_next(work); + struct smb2_hdr *hdr = &pdu->hdr; + int command; + __u32 clc_len; /* calculated length */ + __u32 len = get_rfc1002_len(work->request_buf); + __u32 req_struct_size, next_cmd = le32_to_cpu(hdr->NextCommand); + + if ((u64)work->next_smb2_rcv_hdr_off + next_cmd > len) { + pr_err("next command(%u) offset exceeds smb msg size\n", + next_cmd); + return 1; + } + + if (next_cmd > 0) + len = next_cmd; + else if (work->next_smb2_rcv_hdr_off) + len -= work->next_smb2_rcv_hdr_off; + + if (check_smb2_hdr(hdr)) + return 1; + + if (hdr->StructureSize != SMB2_HEADER_STRUCTURE_SIZE) { + ksmbd_debug(SMB, "Illegal structure size %u\n", + le16_to_cpu(hdr->StructureSize)); + return 1; + } + + command = le16_to_cpu(hdr->Command); + if (command >= NUMBER_OF_SMB2_COMMANDS) { + ksmbd_debug(SMB, "Illegal SMB2 command %d\n", command); + return 1; + } + + if (smb2_req_struct_sizes[command] != pdu->StructureSize2) { + if (command == SMB2_OPLOCK_BREAK_HE && + le16_to_cpu(pdu->StructureSize2) != OP_BREAK_STRUCT_SIZE_20 && + le16_to_cpu(pdu->StructureSize2) != OP_BREAK_STRUCT_SIZE_21) { + /* special case for SMB2.1 lease break message */ + ksmbd_debug(SMB, + "Illegal request size %d for oplock break\n", + le16_to_cpu(pdu->StructureSize2)); + return 1; + } + } + + req_struct_size = le16_to_cpu(pdu->StructureSize2) + + __SMB2_HEADER_STRUCTURE_SIZE; + if (command == SMB2_LOCK_HE) + req_struct_size -= sizeof(struct smb2_lock_element); + + if (req_struct_size > len + 1) + return 1; + + if (smb2_calc_size(hdr, &clc_len)) + return 1; + + if (len != clc_len) { + /* client can return one byte more due to implied bcc[0] */ + if (clc_len == len + 1) + goto validate_credit; + + /* + * Some windows servers (win2016) will pad also the final + * PDU in a compound to 8 bytes. + */ + if (ALIGN(clc_len, 8) == len) + goto validate_credit; + + /* + * SMB2 NEGOTIATE request will be validated when message + * handling proceeds. + */ + if (command == SMB2_NEGOTIATE_HE) + goto validate_credit; + + /* + * Allow a message that padded to 8byte boundary. + * Linux 4.19.217 with smb 3.0.2 are sometimes + * sending messages where the cls_len is exactly + * 8 bytes less than len. + */ + if (clc_len < len && (len - clc_len) <= 8) + goto validate_credit; + + pr_err_ratelimited( + "cli req too short, len %d not %d. cmd:%d mid:%llu\n", + len, clc_len, command, + le64_to_cpu(hdr->MessageId)); + + return 1; + } + +validate_credit: + if ((work->conn->vals->capabilities & SMB2_GLOBAL_CAP_LARGE_MTU) && + smb2_validate_credit_charge(work->conn, hdr)) { + work->conn->ops->set_rsp_status(work, STATUS_INVALID_PARAMETER); + return 1; + } + + return 0; +} + +int smb2_negotiate_request(struct ksmbd_work *work) +{ + return ksmbd_smb_negotiate_common(work, SMB2_NEGOTIATE_HE); +} diff --git a/fs/smb/server/smb2ops.c b/fs/smb/server/smb2ops.c new file mode 100644 index 0000000000000..ab23da2120b94 --- /dev/null +++ b/fs/smb/server/smb2ops.c @@ -0,0 +1,314 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Copyright (C) 2016 Namjae Jeon + * Copyright (C) 2018 Samsung Electronics Co., Ltd. + */ + +#include +#include "glob.h" + +#include "auth.h" +#include "connection.h" +#include "smb_common.h" +#include "server.h" + +static struct smb_version_values smb21_server_values = { + .version_string = SMB21_VERSION_STRING, + .protocol_id = SMB21_PROT_ID, + .capabilities = SMB2_GLOBAL_CAP_LARGE_MTU, + .max_read_size = SMB21_DEFAULT_IOSIZE, + .max_write_size = SMB21_DEFAULT_IOSIZE, + .max_trans_size = SMB21_DEFAULT_IOSIZE, + .max_credits = SMB2_MAX_CREDITS, + .large_lock_type = 0, + .exclusive_lock_type = SMB2_LOCKFLAG_EXCLUSIVE, + .shared_lock_type = SMB2_LOCKFLAG_SHARED, + .unlock_lock_type = SMB2_LOCKFLAG_UNLOCK, + .header_size = sizeof(struct smb2_hdr), + .max_header_size = MAX_SMB2_HDR_SIZE, + .read_rsp_size = sizeof(struct smb2_read_rsp) - 1, + .lock_cmd = SMB2_LOCK, + .cap_unix = 0, + .cap_nt_find = SMB2_NT_FIND, + .cap_large_files = SMB2_LARGE_FILES, + .create_lease_size = sizeof(struct create_lease), + .create_durable_size = sizeof(struct create_durable_rsp), + .create_mxac_size = sizeof(struct create_mxac_rsp), + .create_disk_id_size = sizeof(struct create_disk_id_rsp), + .create_posix_size = sizeof(struct create_posix_rsp), +}; + +static struct smb_version_values smb30_server_values = { + .version_string = SMB30_VERSION_STRING, + .protocol_id = SMB30_PROT_ID, + .capabilities = SMB2_GLOBAL_CAP_LARGE_MTU, + .max_read_size = SMB3_DEFAULT_IOSIZE, + .max_write_size = SMB3_DEFAULT_IOSIZE, + .max_trans_size = SMB3_DEFAULT_TRANS_SIZE, + .max_credits = SMB2_MAX_CREDITS, + .large_lock_type = 0, + .exclusive_lock_type = SMB2_LOCKFLAG_EXCLUSIVE, + .shared_lock_type = SMB2_LOCKFLAG_SHARED, + .unlock_lock_type = SMB2_LOCKFLAG_UNLOCK, + .header_size = sizeof(struct smb2_hdr), + .max_header_size = MAX_SMB2_HDR_SIZE, + .read_rsp_size = sizeof(struct smb2_read_rsp) - 1, + .lock_cmd = SMB2_LOCK, + .cap_unix = 0, + .cap_nt_find = SMB2_NT_FIND, + .cap_large_files = SMB2_LARGE_FILES, + .create_lease_size = sizeof(struct create_lease_v2), + .create_durable_size = sizeof(struct create_durable_rsp), + .create_durable_v2_size = sizeof(struct create_durable_v2_rsp), + .create_mxac_size = sizeof(struct create_mxac_rsp), + .create_disk_id_size = sizeof(struct create_disk_id_rsp), + .create_posix_size = sizeof(struct create_posix_rsp), +}; + +static struct smb_version_values smb302_server_values = { + .version_string = SMB302_VERSION_STRING, + .protocol_id = SMB302_PROT_ID, + .capabilities = SMB2_GLOBAL_CAP_LARGE_MTU, + .max_read_size = SMB3_DEFAULT_IOSIZE, + .max_write_size = SMB3_DEFAULT_IOSIZE, + .max_trans_size = SMB3_DEFAULT_TRANS_SIZE, + .max_credits = SMB2_MAX_CREDITS, + .large_lock_type = 0, + .exclusive_lock_type = SMB2_LOCKFLAG_EXCLUSIVE, + .shared_lock_type = SMB2_LOCKFLAG_SHARED, + .unlock_lock_type = SMB2_LOCKFLAG_UNLOCK, + .header_size = sizeof(struct smb2_hdr), + .max_header_size = MAX_SMB2_HDR_SIZE, + .read_rsp_size = sizeof(struct smb2_read_rsp) - 1, + .lock_cmd = SMB2_LOCK, + .cap_unix = 0, + .cap_nt_find = SMB2_NT_FIND, + .cap_large_files = SMB2_LARGE_FILES, + .create_lease_size = sizeof(struct create_lease_v2), + .create_durable_size = sizeof(struct create_durable_rsp), + .create_durable_v2_size = sizeof(struct create_durable_v2_rsp), + .create_mxac_size = sizeof(struct create_mxac_rsp), + .create_disk_id_size = sizeof(struct create_disk_id_rsp), + .create_posix_size = sizeof(struct create_posix_rsp), +}; + +static struct smb_version_values smb311_server_values = { + .version_string = SMB311_VERSION_STRING, + .protocol_id = SMB311_PROT_ID, + .capabilities = SMB2_GLOBAL_CAP_LARGE_MTU, + .max_read_size = SMB3_DEFAULT_IOSIZE, + .max_write_size = SMB3_DEFAULT_IOSIZE, + .max_trans_size = SMB3_DEFAULT_TRANS_SIZE, + .max_credits = SMB2_MAX_CREDITS, + .large_lock_type = 0, + .exclusive_lock_type = SMB2_LOCKFLAG_EXCLUSIVE, + .shared_lock_type = SMB2_LOCKFLAG_SHARED, + .unlock_lock_type = SMB2_LOCKFLAG_UNLOCK, + .header_size = sizeof(struct smb2_hdr), + .max_header_size = MAX_SMB2_HDR_SIZE, + .read_rsp_size = sizeof(struct smb2_read_rsp) - 1, + .lock_cmd = SMB2_LOCK, + .cap_unix = 0, + .cap_nt_find = SMB2_NT_FIND, + .cap_large_files = SMB2_LARGE_FILES, + .create_lease_size = sizeof(struct create_lease_v2), + .create_durable_size = sizeof(struct create_durable_rsp), + .create_durable_v2_size = sizeof(struct create_durable_v2_rsp), + .create_mxac_size = sizeof(struct create_mxac_rsp), + .create_disk_id_size = sizeof(struct create_disk_id_rsp), + .create_posix_size = sizeof(struct create_posix_rsp), +}; + +static struct smb_version_ops smb2_0_server_ops = { + .get_cmd_val = get_smb2_cmd_val, + .init_rsp_hdr = init_smb2_rsp_hdr, + .set_rsp_status = set_smb2_rsp_status, + .allocate_rsp_buf = smb2_allocate_rsp_buf, + .set_rsp_credits = smb2_set_rsp_credits, + .check_user_session = smb2_check_user_session, + .get_ksmbd_tcon = smb2_get_ksmbd_tcon, + .is_sign_req = smb2_is_sign_req, + .check_sign_req = smb2_check_sign_req, + .set_sign_rsp = smb2_set_sign_rsp +}; + +static struct smb_version_ops smb3_0_server_ops = { + .get_cmd_val = get_smb2_cmd_val, + .init_rsp_hdr = init_smb2_rsp_hdr, + .set_rsp_status = set_smb2_rsp_status, + .allocate_rsp_buf = smb2_allocate_rsp_buf, + .set_rsp_credits = smb2_set_rsp_credits, + .check_user_session = smb2_check_user_session, + .get_ksmbd_tcon = smb2_get_ksmbd_tcon, + .is_sign_req = smb2_is_sign_req, + .check_sign_req = smb3_check_sign_req, + .set_sign_rsp = smb3_set_sign_rsp, + .generate_signingkey = ksmbd_gen_smb30_signingkey, + .generate_encryptionkey = ksmbd_gen_smb30_encryptionkey, + .is_transform_hdr = smb3_is_transform_hdr, + .decrypt_req = smb3_decrypt_req, + .encrypt_resp = smb3_encrypt_resp +}; + +static struct smb_version_ops smb3_11_server_ops = { + .get_cmd_val = get_smb2_cmd_val, + .init_rsp_hdr = init_smb2_rsp_hdr, + .set_rsp_status = set_smb2_rsp_status, + .allocate_rsp_buf = smb2_allocate_rsp_buf, + .set_rsp_credits = smb2_set_rsp_credits, + .check_user_session = smb2_check_user_session, + .get_ksmbd_tcon = smb2_get_ksmbd_tcon, + .is_sign_req = smb2_is_sign_req, + .check_sign_req = smb3_check_sign_req, + .set_sign_rsp = smb3_set_sign_rsp, + .generate_signingkey = ksmbd_gen_smb311_signingkey, + .generate_encryptionkey = ksmbd_gen_smb311_encryptionkey, + .is_transform_hdr = smb3_is_transform_hdr, + .decrypt_req = smb3_decrypt_req, + .encrypt_resp = smb3_encrypt_resp +}; + +static struct smb_version_cmds smb2_0_server_cmds[NUMBER_OF_SMB2_COMMANDS] = { + [SMB2_NEGOTIATE_HE] = { .proc = smb2_negotiate_request, }, + [SMB2_SESSION_SETUP_HE] = { .proc = smb2_sess_setup, }, + [SMB2_TREE_CONNECT_HE] = { .proc = smb2_tree_connect,}, + [SMB2_TREE_DISCONNECT_HE] = { .proc = smb2_tree_disconnect,}, + [SMB2_LOGOFF_HE] = { .proc = smb2_session_logoff,}, + [SMB2_CREATE_HE] = { .proc = smb2_open}, + [SMB2_QUERY_INFO_HE] = { .proc = smb2_query_info}, + [SMB2_QUERY_DIRECTORY_HE] = { .proc = smb2_query_dir}, + [SMB2_CLOSE_HE] = { .proc = smb2_close}, + [SMB2_ECHO_HE] = { .proc = smb2_echo}, + [SMB2_SET_INFO_HE] = { .proc = smb2_set_info}, + [SMB2_READ_HE] = { .proc = smb2_read}, + [SMB2_WRITE_HE] = { .proc = smb2_write}, + [SMB2_FLUSH_HE] = { .proc = smb2_flush}, + [SMB2_CANCEL_HE] = { .proc = smb2_cancel}, + [SMB2_LOCK_HE] = { .proc = smb2_lock}, + [SMB2_IOCTL_HE] = { .proc = smb2_ioctl}, + [SMB2_OPLOCK_BREAK_HE] = { .proc = smb2_oplock_break}, + [SMB2_CHANGE_NOTIFY_HE] = { .proc = smb2_notify}, +}; + +/** + * init_smb2_1_server() - initialize a smb server connection with smb2.1 + * command dispatcher + * @conn: connection instance + */ +void init_smb2_1_server(struct ksmbd_conn *conn) +{ + conn->vals = &smb21_server_values; + conn->ops = &smb2_0_server_ops; + conn->cmds = smb2_0_server_cmds; + conn->max_cmds = ARRAY_SIZE(smb2_0_server_cmds); + conn->signing_algorithm = SIGNING_ALG_HMAC_SHA256_LE; + + if (server_conf.flags & KSMBD_GLOBAL_FLAG_SMB2_LEASES) + conn->vals->capabilities |= SMB2_GLOBAL_CAP_LEASING; +} + +/** + * init_smb3_0_server() - initialize a smb server connection with smb3.0 + * command dispatcher + * @conn: connection instance + */ +void init_smb3_0_server(struct ksmbd_conn *conn) +{ + conn->vals = &smb30_server_values; + conn->ops = &smb3_0_server_ops; + conn->cmds = smb2_0_server_cmds; + conn->max_cmds = ARRAY_SIZE(smb2_0_server_cmds); + conn->signing_algorithm = SIGNING_ALG_AES_CMAC_LE; + + if (server_conf.flags & KSMBD_GLOBAL_FLAG_SMB2_LEASES) + conn->vals->capabilities |= SMB2_GLOBAL_CAP_LEASING; + + if (server_conf.flags & KSMBD_GLOBAL_FLAG_SMB2_ENCRYPTION && + conn->cli_cap & SMB2_GLOBAL_CAP_ENCRYPTION) + conn->vals->capabilities |= SMB2_GLOBAL_CAP_ENCRYPTION; + + if (server_conf.flags & KSMBD_GLOBAL_FLAG_SMB3_MULTICHANNEL) + conn->vals->capabilities |= SMB2_GLOBAL_CAP_MULTI_CHANNEL; +} + +/** + * init_smb3_02_server() - initialize a smb server connection with smb3.02 + * command dispatcher + * @conn: connection instance + */ +void init_smb3_02_server(struct ksmbd_conn *conn) +{ + conn->vals = &smb302_server_values; + conn->ops = &smb3_0_server_ops; + conn->cmds = smb2_0_server_cmds; + conn->max_cmds = ARRAY_SIZE(smb2_0_server_cmds); + conn->signing_algorithm = SIGNING_ALG_AES_CMAC_LE; + + if (server_conf.flags & KSMBD_GLOBAL_FLAG_SMB2_LEASES) + conn->vals->capabilities |= SMB2_GLOBAL_CAP_LEASING; + + if (server_conf.flags & KSMBD_GLOBAL_FLAG_SMB2_ENCRYPTION && + conn->cli_cap & SMB2_GLOBAL_CAP_ENCRYPTION) + conn->vals->capabilities |= SMB2_GLOBAL_CAP_ENCRYPTION; + + if (server_conf.flags & KSMBD_GLOBAL_FLAG_SMB3_MULTICHANNEL) + conn->vals->capabilities |= SMB2_GLOBAL_CAP_MULTI_CHANNEL; +} + +/** + * init_smb3_11_server() - initialize a smb server connection with smb3.11 + * command dispatcher + * @conn: connection instance + */ +int init_smb3_11_server(struct ksmbd_conn *conn) +{ + conn->vals = &smb311_server_values; + conn->ops = &smb3_11_server_ops; + conn->cmds = smb2_0_server_cmds; + conn->max_cmds = ARRAY_SIZE(smb2_0_server_cmds); + conn->signing_algorithm = SIGNING_ALG_AES_CMAC_LE; + + if (server_conf.flags & KSMBD_GLOBAL_FLAG_SMB2_LEASES) + conn->vals->capabilities |= SMB2_GLOBAL_CAP_LEASING; + + if (server_conf.flags & KSMBD_GLOBAL_FLAG_SMB3_MULTICHANNEL) + conn->vals->capabilities |= SMB2_GLOBAL_CAP_MULTI_CHANNEL; + + INIT_LIST_HEAD(&conn->preauth_sess_table); + return 0; +} + +void init_smb2_max_read_size(unsigned int sz) +{ + sz = clamp_val(sz, SMB3_MIN_IOSIZE, SMB3_MAX_IOSIZE); + smb21_server_values.max_read_size = sz; + smb30_server_values.max_read_size = sz; + smb302_server_values.max_read_size = sz; + smb311_server_values.max_read_size = sz; +} + +void init_smb2_max_write_size(unsigned int sz) +{ + sz = clamp_val(sz, SMB3_MIN_IOSIZE, SMB3_MAX_IOSIZE); + smb21_server_values.max_write_size = sz; + smb30_server_values.max_write_size = sz; + smb302_server_values.max_write_size = sz; + smb311_server_values.max_write_size = sz; +} + +void init_smb2_max_trans_size(unsigned int sz) +{ + sz = clamp_val(sz, SMB3_MIN_IOSIZE, SMB3_MAX_IOSIZE); + smb21_server_values.max_trans_size = sz; + smb30_server_values.max_trans_size = sz; + smb302_server_values.max_trans_size = sz; + smb311_server_values.max_trans_size = sz; +} + +void init_smb2_max_credits(unsigned int sz) +{ + smb21_server_values.max_credits = sz; + smb30_server_values.max_credits = sz; + smb302_server_values.max_credits = sz; + smb311_server_values.max_credits = sz; +} diff --git a/fs/smb/server/smb2pdu.c b/fs/smb/server/smb2pdu.c new file mode 100644 index 0000000000000..2b7c0ba6a77de --- /dev/null +++ b/fs/smb/server/smb2pdu.c @@ -0,0 +1,8727 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Copyright (C) 2016 Namjae Jeon + * Copyright (C) 2018 Samsung Electronics Co., Ltd. + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "glob.h" +#include "smbfsctl.h" +#include "oplock.h" +#include "smbacl.h" + +#include "auth.h" +#include "asn1.h" +#include "connection.h" +#include "transport_ipc.h" +#include "transport_rdma.h" +#include "vfs.h" +#include "vfs_cache.h" +#include "misc.h" + +#include "server.h" +#include "smb_common.h" +#include "smbstatus.h" +#include "ksmbd_work.h" +#include "mgmt/user_config.h" +#include "mgmt/share_config.h" +#include "mgmt/tree_connect.h" +#include "mgmt/user_session.h" +#include "mgmt/ksmbd_ida.h" +#include "ndr.h" + +static void __wbuf(struct ksmbd_work *work, void **req, void **rsp) +{ + if (work->next_smb2_rcv_hdr_off) { + *req = ksmbd_req_buf_next(work); + *rsp = ksmbd_resp_buf_next(work); + } else { + *req = smb2_get_msg(work->request_buf); + *rsp = smb2_get_msg(work->response_buf); + } +} + +#define WORK_BUFFERS(w, rq, rs) __wbuf((w), (void **)&(rq), (void **)&(rs)) + +/** + * check_session_id() - check for valid session id in smb header + * @conn: connection instance + * @id: session id from smb header + * + * Return: 1 if valid session id, otherwise 0 + */ +static inline bool check_session_id(struct ksmbd_conn *conn, u64 id) +{ + struct ksmbd_session *sess; + + if (id == 0 || id == -1) + return false; + + sess = ksmbd_session_lookup_all(conn, id); + if (sess) + return true; + pr_err("Invalid user session id: %llu\n", id); + return false; +} + +struct channel *lookup_chann_list(struct ksmbd_session *sess, struct ksmbd_conn *conn) +{ + return xa_load(&sess->ksmbd_chann_list, (long)conn); +} + +/** + * smb2_get_ksmbd_tcon() - get tree connection information using a tree id. + * @work: smb work + * + * Return: 0 if there is a tree connection matched or these are + * skipable commands, otherwise error + */ +int smb2_get_ksmbd_tcon(struct ksmbd_work *work) +{ + struct smb2_hdr *req_hdr = smb2_get_msg(work->request_buf); + unsigned int cmd = le16_to_cpu(req_hdr->Command); + int tree_id; + + if (cmd == SMB2_TREE_CONNECT_HE || + cmd == SMB2_CANCEL_HE || + cmd == SMB2_LOGOFF_HE) { + ksmbd_debug(SMB, "skip to check tree connect request\n"); + return 0; + } + + if (xa_empty(&work->sess->tree_conns)) { + ksmbd_debug(SMB, "NO tree connected\n"); + return -ENOENT; + } + + tree_id = le32_to_cpu(req_hdr->Id.SyncId.TreeId); + + /* + * If request is not the first in Compound request, + * Just validate tree id in header with work->tcon->id. + */ + if (work->next_smb2_rcv_hdr_off) { + if (!work->tcon) { + pr_err("The first operation in the compound does not have tcon\n"); + return -EINVAL; + } + if (work->tcon->id != tree_id) { + pr_err("tree id(%u) is different with id(%u) in first operation\n", + tree_id, work->tcon->id); + return -EINVAL; + } + return 1; + } + + work->tcon = ksmbd_tree_conn_lookup(work->sess, tree_id); + if (!work->tcon) { + pr_err("Invalid tid %d\n", tree_id); + return -ENOENT; + } + + return 1; +} + +/** + * smb2_set_err_rsp() - set error response code on smb response + * @work: smb work containing response buffer + */ +void smb2_set_err_rsp(struct ksmbd_work *work) +{ + struct smb2_err_rsp *err_rsp; + + if (work->next_smb2_rcv_hdr_off) + err_rsp = ksmbd_resp_buf_next(work); + else + err_rsp = smb2_get_msg(work->response_buf); + + if (err_rsp->hdr.Status != STATUS_STOPPED_ON_SYMLINK) { + err_rsp->StructureSize = SMB2_ERROR_STRUCTURE_SIZE2_LE; + err_rsp->ErrorContextCount = 0; + err_rsp->Reserved = 0; + err_rsp->ByteCount = 0; + err_rsp->ErrorData[0] = 0; + inc_rfc1001_len(work->response_buf, SMB2_ERROR_STRUCTURE_SIZE2); + } +} + +/** + * is_smb2_neg_cmd() - is it smb2 negotiation command + * @work: smb work containing smb header + * + * Return: true if smb2 negotiation command, otherwise false + */ +bool is_smb2_neg_cmd(struct ksmbd_work *work) +{ + struct smb2_hdr *hdr = smb2_get_msg(work->request_buf); + + /* is it SMB2 header ? */ + if (hdr->ProtocolId != SMB2_PROTO_NUMBER) + return false; + + /* make sure it is request not response message */ + if (hdr->Flags & SMB2_FLAGS_SERVER_TO_REDIR) + return false; + + if (hdr->Command != SMB2_NEGOTIATE) + return false; + + return true; +} + +/** + * is_smb2_rsp() - is it smb2 response + * @work: smb work containing smb response buffer + * + * Return: true if smb2 response, otherwise false + */ +bool is_smb2_rsp(struct ksmbd_work *work) +{ + struct smb2_hdr *hdr = smb2_get_msg(work->response_buf); + + /* is it SMB2 header ? */ + if (hdr->ProtocolId != SMB2_PROTO_NUMBER) + return false; + + /* make sure it is response not request message */ + if (!(hdr->Flags & SMB2_FLAGS_SERVER_TO_REDIR)) + return false; + + return true; +} + +/** + * get_smb2_cmd_val() - get smb command code from smb header + * @work: smb work containing smb request buffer + * + * Return: smb2 request command value + */ +u16 get_smb2_cmd_val(struct ksmbd_work *work) +{ + struct smb2_hdr *rcv_hdr; + + if (work->next_smb2_rcv_hdr_off) + rcv_hdr = ksmbd_req_buf_next(work); + else + rcv_hdr = smb2_get_msg(work->request_buf); + return le16_to_cpu(rcv_hdr->Command); +} + +/** + * set_smb2_rsp_status() - set error response code on smb2 header + * @work: smb work containing response buffer + * @err: error response code + */ +void set_smb2_rsp_status(struct ksmbd_work *work, __le32 err) +{ + struct smb2_hdr *rsp_hdr; + + if (work->next_smb2_rcv_hdr_off) + rsp_hdr = ksmbd_resp_buf_next(work); + else + rsp_hdr = smb2_get_msg(work->response_buf); + rsp_hdr->Status = err; + smb2_set_err_rsp(work); +} + +/** + * init_smb2_neg_rsp() - initialize smb2 response for negotiate command + * @work: smb work containing smb request buffer + * + * smb2 negotiate response is sent in reply of smb1 negotiate command for + * dialect auto-negotiation. + */ +int init_smb2_neg_rsp(struct ksmbd_work *work) +{ + struct smb2_hdr *rsp_hdr; + struct smb2_negotiate_rsp *rsp; + struct ksmbd_conn *conn = work->conn; + + *(__be32 *)work->response_buf = + cpu_to_be32(conn->vals->header_size); + + rsp_hdr = smb2_get_msg(work->response_buf); + memset(rsp_hdr, 0, sizeof(struct smb2_hdr) + 2); + rsp_hdr->ProtocolId = SMB2_PROTO_NUMBER; + rsp_hdr->StructureSize = SMB2_HEADER_STRUCTURE_SIZE; + rsp_hdr->CreditRequest = cpu_to_le16(2); + rsp_hdr->Command = SMB2_NEGOTIATE; + rsp_hdr->Flags = (SMB2_FLAGS_SERVER_TO_REDIR); + rsp_hdr->NextCommand = 0; + rsp_hdr->MessageId = 0; + rsp_hdr->Id.SyncId.ProcessId = 0; + rsp_hdr->Id.SyncId.TreeId = 0; + rsp_hdr->SessionId = 0; + memset(rsp_hdr->Signature, 0, 16); + + rsp = smb2_get_msg(work->response_buf); + + WARN_ON(ksmbd_conn_good(conn)); + + rsp->StructureSize = cpu_to_le16(65); + ksmbd_debug(SMB, "conn->dialect 0x%x\n", conn->dialect); + rsp->DialectRevision = cpu_to_le16(conn->dialect); + /* Not setting conn guid rsp->ServerGUID, as it + * not used by client for identifying connection + */ + rsp->Capabilities = cpu_to_le32(conn->vals->capabilities); + /* Default Max Message Size till SMB2.0, 64K*/ + rsp->MaxTransactSize = cpu_to_le32(conn->vals->max_trans_size); + rsp->MaxReadSize = cpu_to_le32(conn->vals->max_read_size); + rsp->MaxWriteSize = cpu_to_le32(conn->vals->max_write_size); + + rsp->SystemTime = cpu_to_le64(ksmbd_systime()); + rsp->ServerStartTime = 0; + + rsp->SecurityBufferOffset = cpu_to_le16(128); + rsp->SecurityBufferLength = cpu_to_le16(AUTH_GSS_LENGTH); + ksmbd_copy_gss_neg_header((char *)(&rsp->hdr) + + le16_to_cpu(rsp->SecurityBufferOffset)); + inc_rfc1001_len(work->response_buf, + sizeof(struct smb2_negotiate_rsp) - + sizeof(struct smb2_hdr) - sizeof(rsp->Buffer) + + AUTH_GSS_LENGTH); + rsp->SecurityMode = SMB2_NEGOTIATE_SIGNING_ENABLED_LE; + if (server_conf.signing == KSMBD_CONFIG_OPT_MANDATORY) + rsp->SecurityMode |= SMB2_NEGOTIATE_SIGNING_REQUIRED_LE; + conn->use_spnego = true; + + ksmbd_conn_set_need_negotiate(conn); + return 0; +} + +/** + * smb2_set_rsp_credits() - set number of credits in response buffer + * @work: smb work containing smb response buffer + */ +int smb2_set_rsp_credits(struct ksmbd_work *work) +{ + struct smb2_hdr *req_hdr = ksmbd_req_buf_next(work); + struct smb2_hdr *hdr = ksmbd_resp_buf_next(work); + struct ksmbd_conn *conn = work->conn; + unsigned short credits_requested, aux_max; + unsigned short credit_charge, credits_granted = 0; + + if (work->send_no_response) + return 0; + + hdr->CreditCharge = req_hdr->CreditCharge; + + if (conn->total_credits > conn->vals->max_credits) { + hdr->CreditRequest = 0; + pr_err("Total credits overflow: %d\n", conn->total_credits); + return -EINVAL; + } + + credit_charge = max_t(unsigned short, + le16_to_cpu(req_hdr->CreditCharge), 1); + if (credit_charge > conn->total_credits) { + ksmbd_debug(SMB, "Insufficient credits granted, given: %u, granted: %u\n", + credit_charge, conn->total_credits); + return -EINVAL; + } + + conn->total_credits -= credit_charge; + conn->outstanding_credits -= credit_charge; + credits_requested = max_t(unsigned short, + le16_to_cpu(req_hdr->CreditRequest), 1); + + /* according to smb2.credits smbtorture, Windows server + * 2016 or later grant up to 8192 credits at once. + * + * TODO: Need to adjuct CreditRequest value according to + * current cpu load + */ + if (hdr->Command == SMB2_NEGOTIATE) + aux_max = 1; + else + aux_max = conn->vals->max_credits - conn->total_credits; + credits_granted = min_t(unsigned short, credits_requested, aux_max); + + conn->total_credits += credits_granted; + work->credits_granted += credits_granted; + + if (!req_hdr->NextCommand) { + /* Update CreditRequest in last request */ + hdr->CreditRequest = cpu_to_le16(work->credits_granted); + } + ksmbd_debug(SMB, + "credits: requested[%d] granted[%d] total_granted[%d]\n", + credits_requested, credits_granted, + conn->total_credits); + return 0; +} + +/** + * init_chained_smb2_rsp() - initialize smb2 chained response + * @work: smb work containing smb response buffer + */ +static void init_chained_smb2_rsp(struct ksmbd_work *work) +{ + struct smb2_hdr *req = ksmbd_req_buf_next(work); + struct smb2_hdr *rsp = ksmbd_resp_buf_next(work); + struct smb2_hdr *rsp_hdr; + struct smb2_hdr *rcv_hdr; + int next_hdr_offset = 0; + int len, new_len; + + /* Len of this response = updated RFC len - offset of previous cmd + * in the compound rsp + */ + + /* Storing the current local FID which may be needed by subsequent + * command in the compound request + */ + if (req->Command == SMB2_CREATE && rsp->Status == STATUS_SUCCESS) { + work->compound_fid = ((struct smb2_create_rsp *)rsp)->VolatileFileId; + work->compound_pfid = ((struct smb2_create_rsp *)rsp)->PersistentFileId; + work->compound_sid = le64_to_cpu(rsp->SessionId); + } + + len = get_rfc1002_len(work->response_buf) - work->next_smb2_rsp_hdr_off; + next_hdr_offset = le32_to_cpu(req->NextCommand); + + new_len = ALIGN(len, 8); + inc_rfc1001_len(work->response_buf, + sizeof(struct smb2_hdr) + new_len - len); + rsp->NextCommand = cpu_to_le32(new_len); + + work->next_smb2_rcv_hdr_off += next_hdr_offset; + work->next_smb2_rsp_hdr_off += new_len; + ksmbd_debug(SMB, + "Compound req new_len = %d rcv off = %d rsp off = %d\n", + new_len, work->next_smb2_rcv_hdr_off, + work->next_smb2_rsp_hdr_off); + + rsp_hdr = ksmbd_resp_buf_next(work); + rcv_hdr = ksmbd_req_buf_next(work); + + if (!(rcv_hdr->Flags & SMB2_FLAGS_RELATED_OPERATIONS)) { + ksmbd_debug(SMB, "related flag should be set\n"); + work->compound_fid = KSMBD_NO_FID; + work->compound_pfid = KSMBD_NO_FID; + } + memset((char *)rsp_hdr, 0, sizeof(struct smb2_hdr) + 2); + rsp_hdr->ProtocolId = SMB2_PROTO_NUMBER; + rsp_hdr->StructureSize = SMB2_HEADER_STRUCTURE_SIZE; + rsp_hdr->Command = rcv_hdr->Command; + + /* + * Message is response. We don't grant oplock yet. + */ + rsp_hdr->Flags = (SMB2_FLAGS_SERVER_TO_REDIR | + SMB2_FLAGS_RELATED_OPERATIONS); + rsp_hdr->NextCommand = 0; + rsp_hdr->MessageId = rcv_hdr->MessageId; + rsp_hdr->Id.SyncId.ProcessId = rcv_hdr->Id.SyncId.ProcessId; + rsp_hdr->Id.SyncId.TreeId = rcv_hdr->Id.SyncId.TreeId; + rsp_hdr->SessionId = rcv_hdr->SessionId; + memcpy(rsp_hdr->Signature, rcv_hdr->Signature, 16); +} + +/** + * is_chained_smb2_message() - check for chained command + * @work: smb work containing smb request buffer + * + * Return: true if chained request, otherwise false + */ +bool is_chained_smb2_message(struct ksmbd_work *work) +{ + struct smb2_hdr *hdr = smb2_get_msg(work->request_buf); + unsigned int len, next_cmd; + + if (hdr->ProtocolId != SMB2_PROTO_NUMBER) + return false; + + hdr = ksmbd_req_buf_next(work); + next_cmd = le32_to_cpu(hdr->NextCommand); + if (next_cmd > 0) { + if ((u64)work->next_smb2_rcv_hdr_off + next_cmd + + __SMB2_HEADER_STRUCTURE_SIZE > + get_rfc1002_len(work->request_buf)) { + pr_err("next command(%u) offset exceeds smb msg size\n", + next_cmd); + return false; + } + + if ((u64)get_rfc1002_len(work->response_buf) + MAX_CIFS_SMALL_BUFFER_SIZE > + work->response_sz) { + pr_err("next response offset exceeds response buffer size\n"); + return false; + } + + ksmbd_debug(SMB, "got SMB2 chained command\n"); + init_chained_smb2_rsp(work); + return true; + } else if (work->next_smb2_rcv_hdr_off) { + /* + * This is last request in chained command, + * align response to 8 byte + */ + len = ALIGN(get_rfc1002_len(work->response_buf), 8); + len = len - get_rfc1002_len(work->response_buf); + if (len) { + ksmbd_debug(SMB, "padding len %u\n", len); + inc_rfc1001_len(work->response_buf, len); + if (work->aux_payload_sz) + work->aux_payload_sz += len; + } + } + return false; +} + +/** + * init_smb2_rsp_hdr() - initialize smb2 response + * @work: smb work containing smb request buffer + * + * Return: 0 + */ +int init_smb2_rsp_hdr(struct ksmbd_work *work) +{ + struct smb2_hdr *rsp_hdr = smb2_get_msg(work->response_buf); + struct smb2_hdr *rcv_hdr = smb2_get_msg(work->request_buf); + struct ksmbd_conn *conn = work->conn; + + memset(rsp_hdr, 0, sizeof(struct smb2_hdr) + 2); + *(__be32 *)work->response_buf = + cpu_to_be32(conn->vals->header_size); + rsp_hdr->ProtocolId = rcv_hdr->ProtocolId; + rsp_hdr->StructureSize = SMB2_HEADER_STRUCTURE_SIZE; + rsp_hdr->Command = rcv_hdr->Command; + + /* + * Message is response. We don't grant oplock yet. + */ + rsp_hdr->Flags = (SMB2_FLAGS_SERVER_TO_REDIR); + rsp_hdr->NextCommand = 0; + rsp_hdr->MessageId = rcv_hdr->MessageId; + rsp_hdr->Id.SyncId.ProcessId = rcv_hdr->Id.SyncId.ProcessId; + rsp_hdr->Id.SyncId.TreeId = rcv_hdr->Id.SyncId.TreeId; + rsp_hdr->SessionId = rcv_hdr->SessionId; + memcpy(rsp_hdr->Signature, rcv_hdr->Signature, 16); + + work->syncronous = true; + if (work->async_id) { + ksmbd_release_id(&conn->async_ida, work->async_id); + work->async_id = 0; + } + + return 0; +} + +/** + * smb2_allocate_rsp_buf() - allocate smb2 response buffer + * @work: smb work containing smb request buffer + * + * Return: 0 on success, otherwise -ENOMEM + */ +int smb2_allocate_rsp_buf(struct ksmbd_work *work) +{ + struct smb2_hdr *hdr = smb2_get_msg(work->request_buf); + size_t small_sz = MAX_CIFS_SMALL_BUFFER_SIZE; + size_t large_sz = small_sz + work->conn->vals->max_trans_size; + size_t sz = small_sz; + int cmd = le16_to_cpu(hdr->Command); + + if (cmd == SMB2_IOCTL_HE || cmd == SMB2_QUERY_DIRECTORY_HE) + sz = large_sz; + + if (cmd == SMB2_QUERY_INFO_HE) { + struct smb2_query_info_req *req; + + req = smb2_get_msg(work->request_buf); + if ((req->InfoType == SMB2_O_INFO_FILE && + (req->FileInfoClass == FILE_FULL_EA_INFORMATION || + req->FileInfoClass == FILE_ALL_INFORMATION)) || + req->InfoType == SMB2_O_INFO_SECURITY) + sz = large_sz; + } + + /* allocate large response buf for chained commands */ + if (le32_to_cpu(hdr->NextCommand) > 0) + sz = large_sz; + + work->response_buf = kvmalloc(sz, GFP_KERNEL | __GFP_ZERO); + if (!work->response_buf) + return -ENOMEM; + + work->response_sz = sz; + return 0; +} + +/** + * smb2_check_user_session() - check for valid session for a user + * @work: smb work containing smb request buffer + * + * Return: 0 on success, otherwise error + */ +int smb2_check_user_session(struct ksmbd_work *work) +{ + struct smb2_hdr *req_hdr = smb2_get_msg(work->request_buf); + struct ksmbd_conn *conn = work->conn; + unsigned int cmd = conn->ops->get_cmd_val(work); + unsigned long long sess_id; + + /* + * SMB2_ECHO, SMB2_NEGOTIATE, SMB2_SESSION_SETUP command do not + * require a session id, so no need to validate user session's for + * these commands. + */ + if (cmd == SMB2_ECHO_HE || cmd == SMB2_NEGOTIATE_HE || + cmd == SMB2_SESSION_SETUP_HE) + return 0; + + if (!ksmbd_conn_good(conn)) + return -EIO; + + sess_id = le64_to_cpu(req_hdr->SessionId); + + /* + * If request is not the first in Compound request, + * Just validate session id in header with work->sess->id. + */ + if (work->next_smb2_rcv_hdr_off) { + if (!work->sess) { + pr_err("The first operation in the compound does not have sess\n"); + return -EINVAL; + } + if (work->sess->id != sess_id) { + pr_err("session id(%llu) is different with the first operation(%lld)\n", + sess_id, work->sess->id); + return -EINVAL; + } + return 1; + } + + /* Check for validity of user session */ + work->sess = ksmbd_session_lookup_all(conn, sess_id); + if (work->sess) + return 1; + ksmbd_debug(SMB, "Invalid user session, Uid %llu\n", sess_id); + return -ENOENT; +} + +static void destroy_previous_session(struct ksmbd_conn *conn, + struct ksmbd_user *user, u64 id) +{ + struct ksmbd_session *prev_sess = ksmbd_session_lookup_slowpath(id); + struct ksmbd_user *prev_user; + struct channel *chann; + long index; + + if (!prev_sess) + return; + + prev_user = prev_sess->user; + + if (!prev_user || + strcmp(user->name, prev_user->name) || + user->passkey_sz != prev_user->passkey_sz || + memcmp(user->passkey, prev_user->passkey, user->passkey_sz)) + return; + + prev_sess->state = SMB2_SESSION_EXPIRED; + xa_for_each(&prev_sess->ksmbd_chann_list, index, chann) + ksmbd_conn_set_exiting(chann->conn); +} + +/** + * smb2_get_name() - get filename string from on the wire smb format + * @src: source buffer + * @maxlen: maxlen of source string + * @local_nls: nls_table pointer + * + * Return: matching converted filename on success, otherwise error ptr + */ +static char * +smb2_get_name(const char *src, const int maxlen, struct nls_table *local_nls) +{ + char *name; + + name = smb_strndup_from_utf16(src, maxlen, 1, local_nls); + if (IS_ERR(name)) { + pr_err("failed to get name %ld\n", PTR_ERR(name)); + return name; + } + + ksmbd_conv_path_to_unix(name); + ksmbd_strip_last_slash(name); + return name; +} + +int setup_async_work(struct ksmbd_work *work, void (*fn)(void **), void **arg) +{ + struct smb2_hdr *rsp_hdr; + struct ksmbd_conn *conn = work->conn; + int id; + + rsp_hdr = smb2_get_msg(work->response_buf); + rsp_hdr->Flags |= SMB2_FLAGS_ASYNC_COMMAND; + + id = ksmbd_acquire_async_msg_id(&conn->async_ida); + if (id < 0) { + pr_err("Failed to alloc async message id\n"); + return id; + } + work->syncronous = false; + work->async_id = id; + rsp_hdr->Id.AsyncId = cpu_to_le64(id); + + ksmbd_debug(SMB, + "Send interim Response to inform async request id : %d\n", + work->async_id); + + work->cancel_fn = fn; + work->cancel_argv = arg; + + if (list_empty(&work->async_request_entry)) { + spin_lock(&conn->request_lock); + list_add_tail(&work->async_request_entry, &conn->async_requests); + spin_unlock(&conn->request_lock); + } + + return 0; +} + +void smb2_send_interim_resp(struct ksmbd_work *work, __le32 status) +{ + struct smb2_hdr *rsp_hdr; + + rsp_hdr = smb2_get_msg(work->response_buf); + smb2_set_err_rsp(work); + rsp_hdr->Status = status; + + work->multiRsp = 1; + ksmbd_conn_write(work); + rsp_hdr->Status = 0; + work->multiRsp = 0; +} + +static __le32 smb2_get_reparse_tag_special_file(umode_t mode) +{ + if (S_ISDIR(mode) || S_ISREG(mode)) + return 0; + + if (S_ISLNK(mode)) + return IO_REPARSE_TAG_LX_SYMLINK_LE; + else if (S_ISFIFO(mode)) + return IO_REPARSE_TAG_LX_FIFO_LE; + else if (S_ISSOCK(mode)) + return IO_REPARSE_TAG_AF_UNIX_LE; + else if (S_ISCHR(mode)) + return IO_REPARSE_TAG_LX_CHR_LE; + else if (S_ISBLK(mode)) + return IO_REPARSE_TAG_LX_BLK_LE; + + return 0; +} + +/** + * smb2_get_dos_mode() - get file mode in dos format from unix mode + * @stat: kstat containing file mode + * @attribute: attribute flags + * + * Return: converted dos mode + */ +static int smb2_get_dos_mode(struct kstat *stat, int attribute) +{ + int attr = 0; + + if (S_ISDIR(stat->mode)) { + attr = FILE_ATTRIBUTE_DIRECTORY | + (attribute & (FILE_ATTRIBUTE_HIDDEN | FILE_ATTRIBUTE_SYSTEM)); + } else { + attr = (attribute & 0x00005137) | FILE_ATTRIBUTE_ARCHIVE; + attr &= ~(FILE_ATTRIBUTE_DIRECTORY); + if (S_ISREG(stat->mode) && (server_conf.share_fake_fscaps & + FILE_SUPPORTS_SPARSE_FILES)) + attr |= FILE_ATTRIBUTE_SPARSE_FILE; + + if (smb2_get_reparse_tag_special_file(stat->mode)) + attr |= FILE_ATTRIBUTE_REPARSE_POINT; + } + + return attr; +} + +static void build_preauth_ctxt(struct smb2_preauth_neg_context *pneg_ctxt, + __le16 hash_id) +{ + pneg_ctxt->ContextType = SMB2_PREAUTH_INTEGRITY_CAPABILITIES; + pneg_ctxt->DataLength = cpu_to_le16(38); + pneg_ctxt->HashAlgorithmCount = cpu_to_le16(1); + pneg_ctxt->Reserved = cpu_to_le32(0); + pneg_ctxt->SaltLength = cpu_to_le16(SMB311_SALT_SIZE); + get_random_bytes(pneg_ctxt->Salt, SMB311_SALT_SIZE); + pneg_ctxt->HashAlgorithms = hash_id; +} + +static void build_encrypt_ctxt(struct smb2_encryption_neg_context *pneg_ctxt, + __le16 cipher_type) +{ + pneg_ctxt->ContextType = SMB2_ENCRYPTION_CAPABILITIES; + pneg_ctxt->DataLength = cpu_to_le16(4); + pneg_ctxt->Reserved = cpu_to_le32(0); + pneg_ctxt->CipherCount = cpu_to_le16(1); + pneg_ctxt->Ciphers[0] = cipher_type; +} + +static void build_compression_ctxt(struct smb2_compression_capabilities_context *pneg_ctxt, + __le16 comp_algo) +{ + pneg_ctxt->ContextType = SMB2_COMPRESSION_CAPABILITIES; + pneg_ctxt->DataLength = + cpu_to_le16(sizeof(struct smb2_compression_capabilities_context) + - sizeof(struct smb2_neg_context)); + pneg_ctxt->Reserved = cpu_to_le32(0); + pneg_ctxt->CompressionAlgorithmCount = cpu_to_le16(1); + pneg_ctxt->Flags = cpu_to_le32(0); + pneg_ctxt->CompressionAlgorithms[0] = comp_algo; +} + +static void build_sign_cap_ctxt(struct smb2_signing_capabilities *pneg_ctxt, + __le16 sign_algo) +{ + pneg_ctxt->ContextType = SMB2_SIGNING_CAPABILITIES; + pneg_ctxt->DataLength = + cpu_to_le16((sizeof(struct smb2_signing_capabilities) + 2) + - sizeof(struct smb2_neg_context)); + pneg_ctxt->Reserved = cpu_to_le32(0); + pneg_ctxt->SigningAlgorithmCount = cpu_to_le16(1); + pneg_ctxt->SigningAlgorithms[0] = sign_algo; +} + +static void build_posix_ctxt(struct smb2_posix_neg_context *pneg_ctxt) +{ + pneg_ctxt->ContextType = SMB2_POSIX_EXTENSIONS_AVAILABLE; + pneg_ctxt->DataLength = cpu_to_le16(POSIX_CTXT_DATA_LEN); + /* SMB2_CREATE_TAG_POSIX is "0x93AD25509CB411E7B42383DE968BCD7C" */ + pneg_ctxt->Name[0] = 0x93; + pneg_ctxt->Name[1] = 0xAD; + pneg_ctxt->Name[2] = 0x25; + pneg_ctxt->Name[3] = 0x50; + pneg_ctxt->Name[4] = 0x9C; + pneg_ctxt->Name[5] = 0xB4; + pneg_ctxt->Name[6] = 0x11; + pneg_ctxt->Name[7] = 0xE7; + pneg_ctxt->Name[8] = 0xB4; + pneg_ctxt->Name[9] = 0x23; + pneg_ctxt->Name[10] = 0x83; + pneg_ctxt->Name[11] = 0xDE; + pneg_ctxt->Name[12] = 0x96; + pneg_ctxt->Name[13] = 0x8B; + pneg_ctxt->Name[14] = 0xCD; + pneg_ctxt->Name[15] = 0x7C; +} + +static void assemble_neg_contexts(struct ksmbd_conn *conn, + struct smb2_negotiate_rsp *rsp, + void *smb2_buf_len) +{ + char *pneg_ctxt = (char *)rsp + + le32_to_cpu(rsp->NegotiateContextOffset); + int neg_ctxt_cnt = 1; + int ctxt_size; + + ksmbd_debug(SMB, + "assemble SMB2_PREAUTH_INTEGRITY_CAPABILITIES context\n"); + build_preauth_ctxt((struct smb2_preauth_neg_context *)pneg_ctxt, + conn->preauth_info->Preauth_HashId); + rsp->NegotiateContextCount = cpu_to_le16(neg_ctxt_cnt); + inc_rfc1001_len(smb2_buf_len, AUTH_GSS_PADDING); + ctxt_size = sizeof(struct smb2_preauth_neg_context); + /* Round to 8 byte boundary */ + pneg_ctxt += round_up(sizeof(struct smb2_preauth_neg_context), 8); + + if (conn->cipher_type) { + ctxt_size = round_up(ctxt_size, 8); + ksmbd_debug(SMB, + "assemble SMB2_ENCRYPTION_CAPABILITIES context\n"); + build_encrypt_ctxt((struct smb2_encryption_neg_context *)pneg_ctxt, + conn->cipher_type); + rsp->NegotiateContextCount = cpu_to_le16(++neg_ctxt_cnt); + ctxt_size += sizeof(struct smb2_encryption_neg_context) + 2; + /* Round to 8 byte boundary */ + pneg_ctxt += + round_up(sizeof(struct smb2_encryption_neg_context) + 2, + 8); + } + + if (conn->compress_algorithm) { + ctxt_size = round_up(ctxt_size, 8); + ksmbd_debug(SMB, + "assemble SMB2_COMPRESSION_CAPABILITIES context\n"); + /* Temporarily set to SMB3_COMPRESS_NONE */ + build_compression_ctxt((struct smb2_compression_capabilities_context *)pneg_ctxt, + conn->compress_algorithm); + rsp->NegotiateContextCount = cpu_to_le16(++neg_ctxt_cnt); + ctxt_size += sizeof(struct smb2_compression_capabilities_context) + 2; + /* Round to 8 byte boundary */ + pneg_ctxt += round_up(sizeof(struct smb2_compression_capabilities_context) + 2, + 8); + } + + if (conn->posix_ext_supported) { + ctxt_size = round_up(ctxt_size, 8); + ksmbd_debug(SMB, + "assemble SMB2_POSIX_EXTENSIONS_AVAILABLE context\n"); + build_posix_ctxt((struct smb2_posix_neg_context *)pneg_ctxt); + rsp->NegotiateContextCount = cpu_to_le16(++neg_ctxt_cnt); + ctxt_size += sizeof(struct smb2_posix_neg_context); + /* Round to 8 byte boundary */ + pneg_ctxt += round_up(sizeof(struct smb2_posix_neg_context), 8); + } + + if (conn->signing_negotiated) { + ctxt_size = round_up(ctxt_size, 8); + ksmbd_debug(SMB, + "assemble SMB2_SIGNING_CAPABILITIES context\n"); + build_sign_cap_ctxt((struct smb2_signing_capabilities *)pneg_ctxt, + conn->signing_algorithm); + rsp->NegotiateContextCount = cpu_to_le16(++neg_ctxt_cnt); + ctxt_size += sizeof(struct smb2_signing_capabilities) + 2; + } + + inc_rfc1001_len(smb2_buf_len, ctxt_size); +} + +static __le32 decode_preauth_ctxt(struct ksmbd_conn *conn, + struct smb2_preauth_neg_context *pneg_ctxt, + int ctxt_len) +{ + /* + * sizeof(smb2_preauth_neg_context) assumes SMB311_SALT_SIZE Salt, + * which may not be present. Only check for used HashAlgorithms[1]. + */ + if (ctxt_len < + sizeof(struct smb2_neg_context) + MIN_PREAUTH_CTXT_DATA_LEN) + return STATUS_INVALID_PARAMETER; + + if (pneg_ctxt->HashAlgorithms != SMB2_PREAUTH_INTEGRITY_SHA512) + return STATUS_NO_PREAUTH_INTEGRITY_HASH_OVERLAP; + + conn->preauth_info->Preauth_HashId = SMB2_PREAUTH_INTEGRITY_SHA512; + return STATUS_SUCCESS; +} + +static void decode_encrypt_ctxt(struct ksmbd_conn *conn, + struct smb2_encryption_neg_context *pneg_ctxt, + int ctxt_len) +{ + int cph_cnt; + int i, cphs_size; + + if (sizeof(struct smb2_encryption_neg_context) > ctxt_len) { + pr_err("Invalid SMB2_ENCRYPTION_CAPABILITIES context size\n"); + return; + } + + conn->cipher_type = 0; + + cph_cnt = le16_to_cpu(pneg_ctxt->CipherCount); + cphs_size = cph_cnt * sizeof(__le16); + + if (sizeof(struct smb2_encryption_neg_context) + cphs_size > + ctxt_len) { + pr_err("Invalid cipher count(%d)\n", cph_cnt); + return; + } + + if (!(server_conf.flags & KSMBD_GLOBAL_FLAG_SMB2_ENCRYPTION)) + return; + + for (i = 0; i < cph_cnt; i++) { + if (pneg_ctxt->Ciphers[i] == SMB2_ENCRYPTION_AES128_GCM || + pneg_ctxt->Ciphers[i] == SMB2_ENCRYPTION_AES128_CCM || + pneg_ctxt->Ciphers[i] == SMB2_ENCRYPTION_AES256_CCM || + pneg_ctxt->Ciphers[i] == SMB2_ENCRYPTION_AES256_GCM) { + ksmbd_debug(SMB, "Cipher ID = 0x%x\n", + pneg_ctxt->Ciphers[i]); + conn->cipher_type = pneg_ctxt->Ciphers[i]; + break; + } + } +} + +/** + * smb3_encryption_negotiated() - checks if server and client agreed on enabling encryption + * @conn: smb connection + * + * Return: true if connection should be encrypted, else false + */ +bool smb3_encryption_negotiated(struct ksmbd_conn *conn) +{ + if (!conn->ops->generate_encryptionkey) + return false; + + /* + * SMB 3.0 and 3.0.2 dialects use the SMB2_GLOBAL_CAP_ENCRYPTION flag. + * SMB 3.1.1 uses the cipher_type field. + */ + return (conn->vals->capabilities & SMB2_GLOBAL_CAP_ENCRYPTION) || + conn->cipher_type; +} + +static void decode_compress_ctxt(struct ksmbd_conn *conn, + struct smb2_compression_capabilities_context *pneg_ctxt) +{ + conn->compress_algorithm = SMB3_COMPRESS_NONE; +} + +static void decode_sign_cap_ctxt(struct ksmbd_conn *conn, + struct smb2_signing_capabilities *pneg_ctxt, + int ctxt_len) +{ + int sign_algo_cnt; + int i, sign_alos_size; + + if (sizeof(struct smb2_signing_capabilities) > ctxt_len) { + pr_err("Invalid SMB2_SIGNING_CAPABILITIES context length\n"); + return; + } + + conn->signing_negotiated = false; + sign_algo_cnt = le16_to_cpu(pneg_ctxt->SigningAlgorithmCount); + sign_alos_size = sign_algo_cnt * sizeof(__le16); + + if (sizeof(struct smb2_signing_capabilities) + sign_alos_size > + ctxt_len) { + pr_err("Invalid signing algorithm count(%d)\n", sign_algo_cnt); + return; + } + + for (i = 0; i < sign_algo_cnt; i++) { + if (pneg_ctxt->SigningAlgorithms[i] == SIGNING_ALG_HMAC_SHA256_LE || + pneg_ctxt->SigningAlgorithms[i] == SIGNING_ALG_AES_CMAC_LE) { + ksmbd_debug(SMB, "Signing Algorithm ID = 0x%x\n", + pneg_ctxt->SigningAlgorithms[i]); + conn->signing_negotiated = true; + conn->signing_algorithm = + pneg_ctxt->SigningAlgorithms[i]; + break; + } + } +} + +static __le32 deassemble_neg_contexts(struct ksmbd_conn *conn, + struct smb2_negotiate_req *req, + unsigned int len_of_smb) +{ + /* +4 is to account for the RFC1001 len field */ + struct smb2_neg_context *pctx = (struct smb2_neg_context *)req; + int i = 0, len_of_ctxts; + unsigned int offset = le32_to_cpu(req->NegotiateContextOffset); + unsigned int neg_ctxt_cnt = le16_to_cpu(req->NegotiateContextCount); + __le32 status = STATUS_INVALID_PARAMETER; + + ksmbd_debug(SMB, "decoding %d negotiate contexts\n", neg_ctxt_cnt); + if (len_of_smb <= offset) { + ksmbd_debug(SMB, "Invalid response: negotiate context offset\n"); + return status; + } + + len_of_ctxts = len_of_smb - offset; + + while (i++ < neg_ctxt_cnt) { + int clen, ctxt_len; + + if (len_of_ctxts < (int)sizeof(struct smb2_neg_context)) + break; + + pctx = (struct smb2_neg_context *)((char *)pctx + offset); + clen = le16_to_cpu(pctx->DataLength); + ctxt_len = clen + sizeof(struct smb2_neg_context); + + if (ctxt_len > len_of_ctxts) + break; + + if (pctx->ContextType == SMB2_PREAUTH_INTEGRITY_CAPABILITIES) { + ksmbd_debug(SMB, + "deassemble SMB2_PREAUTH_INTEGRITY_CAPABILITIES context\n"); + if (conn->preauth_info->Preauth_HashId) + break; + + status = decode_preauth_ctxt(conn, + (struct smb2_preauth_neg_context *)pctx, + ctxt_len); + if (status != STATUS_SUCCESS) + break; + } else if (pctx->ContextType == SMB2_ENCRYPTION_CAPABILITIES) { + ksmbd_debug(SMB, + "deassemble SMB2_ENCRYPTION_CAPABILITIES context\n"); + if (conn->cipher_type) + break; + + decode_encrypt_ctxt(conn, + (struct smb2_encryption_neg_context *)pctx, + ctxt_len); + } else if (pctx->ContextType == SMB2_COMPRESSION_CAPABILITIES) { + ksmbd_debug(SMB, + "deassemble SMB2_COMPRESSION_CAPABILITIES context\n"); + if (conn->compress_algorithm) + break; + + decode_compress_ctxt(conn, + (struct smb2_compression_capabilities_context *)pctx); + } else if (pctx->ContextType == SMB2_NETNAME_NEGOTIATE_CONTEXT_ID) { + ksmbd_debug(SMB, + "deassemble SMB2_NETNAME_NEGOTIATE_CONTEXT_ID context\n"); + } else if (pctx->ContextType == SMB2_POSIX_EXTENSIONS_AVAILABLE) { + ksmbd_debug(SMB, + "deassemble SMB2_POSIX_EXTENSIONS_AVAILABLE context\n"); + conn->posix_ext_supported = true; + } else if (pctx->ContextType == SMB2_SIGNING_CAPABILITIES) { + ksmbd_debug(SMB, + "deassemble SMB2_SIGNING_CAPABILITIES context\n"); + + decode_sign_cap_ctxt(conn, + (struct smb2_signing_capabilities *)pctx, + ctxt_len); + } + + /* offsets must be 8 byte aligned */ + offset = (ctxt_len + 7) & ~0x7; + len_of_ctxts -= offset; + } + return status; +} + +/** + * smb2_handle_negotiate() - handler for smb2 negotiate command + * @work: smb work containing smb request buffer + * + * Return: 0 + */ +int smb2_handle_negotiate(struct ksmbd_work *work) +{ + struct ksmbd_conn *conn = work->conn; + struct smb2_negotiate_req *req = smb2_get_msg(work->request_buf); + struct smb2_negotiate_rsp *rsp = smb2_get_msg(work->response_buf); + int rc = 0; + unsigned int smb2_buf_len, smb2_neg_size; + __le32 status; + + ksmbd_debug(SMB, "Received negotiate request\n"); + conn->need_neg = false; + if (ksmbd_conn_good(conn)) { + pr_err("conn->tcp_status is already in CifsGood State\n"); + work->send_no_response = 1; + return rc; + } + + smb2_buf_len = get_rfc1002_len(work->request_buf); + smb2_neg_size = offsetof(struct smb2_negotiate_req, Dialects); + if (smb2_neg_size > smb2_buf_len) { + rsp->hdr.Status = STATUS_INVALID_PARAMETER; + rc = -EINVAL; + goto err_out; + } + + if (req->DialectCount == 0) { + pr_err("malformed packet\n"); + rsp->hdr.Status = STATUS_INVALID_PARAMETER; + rc = -EINVAL; + goto err_out; + } + + if (conn->dialect == SMB311_PROT_ID) { + unsigned int nego_ctxt_off = le32_to_cpu(req->NegotiateContextOffset); + + if (smb2_buf_len < nego_ctxt_off) { + rsp->hdr.Status = STATUS_INVALID_PARAMETER; + rc = -EINVAL; + goto err_out; + } + + if (smb2_neg_size > nego_ctxt_off) { + rsp->hdr.Status = STATUS_INVALID_PARAMETER; + rc = -EINVAL; + goto err_out; + } + + if (smb2_neg_size + le16_to_cpu(req->DialectCount) * sizeof(__le16) > + nego_ctxt_off) { + rsp->hdr.Status = STATUS_INVALID_PARAMETER; + rc = -EINVAL; + goto err_out; + } + } else { + if (smb2_neg_size + le16_to_cpu(req->DialectCount) * sizeof(__le16) > + smb2_buf_len) { + rsp->hdr.Status = STATUS_INVALID_PARAMETER; + rc = -EINVAL; + goto err_out; + } + } + + conn->cli_cap = le32_to_cpu(req->Capabilities); + switch (conn->dialect) { + case SMB311_PROT_ID: + conn->preauth_info = + kzalloc(sizeof(struct preauth_integrity_info), + GFP_KERNEL); + if (!conn->preauth_info) { + rc = -ENOMEM; + rsp->hdr.Status = STATUS_INVALID_PARAMETER; + goto err_out; + } + + status = deassemble_neg_contexts(conn, req, + get_rfc1002_len(work->request_buf)); + if (status != STATUS_SUCCESS) { + pr_err("deassemble_neg_contexts error(0x%x)\n", + status); + rsp->hdr.Status = status; + rc = -EINVAL; + kfree(conn->preauth_info); + conn->preauth_info = NULL; + goto err_out; + } + + rc = init_smb3_11_server(conn); + if (rc < 0) { + rsp->hdr.Status = STATUS_INVALID_PARAMETER; + kfree(conn->preauth_info); + conn->preauth_info = NULL; + goto err_out; + } + + ksmbd_gen_preauth_integrity_hash(conn, + work->request_buf, + conn->preauth_info->Preauth_HashValue); + rsp->NegotiateContextOffset = + cpu_to_le32(OFFSET_OF_NEG_CONTEXT); + assemble_neg_contexts(conn, rsp, work->response_buf); + break; + case SMB302_PROT_ID: + init_smb3_02_server(conn); + break; + case SMB30_PROT_ID: + init_smb3_0_server(conn); + break; + case SMB21_PROT_ID: + init_smb2_1_server(conn); + break; + case SMB2X_PROT_ID: + case BAD_PROT_ID: + default: + ksmbd_debug(SMB, "Server dialect :0x%x not supported\n", + conn->dialect); + rsp->hdr.Status = STATUS_NOT_SUPPORTED; + rc = -EINVAL; + goto err_out; + } + rsp->Capabilities = cpu_to_le32(conn->vals->capabilities); + + /* For stats */ + conn->connection_type = conn->dialect; + + rsp->MaxTransactSize = cpu_to_le32(conn->vals->max_trans_size); + rsp->MaxReadSize = cpu_to_le32(conn->vals->max_read_size); + rsp->MaxWriteSize = cpu_to_le32(conn->vals->max_write_size); + + memcpy(conn->ClientGUID, req->ClientGUID, + SMB2_CLIENT_GUID_SIZE); + conn->cli_sec_mode = le16_to_cpu(req->SecurityMode); + + rsp->StructureSize = cpu_to_le16(65); + rsp->DialectRevision = cpu_to_le16(conn->dialect); + /* Not setting conn guid rsp->ServerGUID, as it + * not used by client for identifying server + */ + memset(rsp->ServerGUID, 0, SMB2_CLIENT_GUID_SIZE); + + rsp->SystemTime = cpu_to_le64(ksmbd_systime()); + rsp->ServerStartTime = 0; + ksmbd_debug(SMB, "negotiate context offset %d, count %d\n", + le32_to_cpu(rsp->NegotiateContextOffset), + le16_to_cpu(rsp->NegotiateContextCount)); + + rsp->SecurityBufferOffset = cpu_to_le16(128); + rsp->SecurityBufferLength = cpu_to_le16(AUTH_GSS_LENGTH); + ksmbd_copy_gss_neg_header((char *)(&rsp->hdr) + + le16_to_cpu(rsp->SecurityBufferOffset)); + inc_rfc1001_len(work->response_buf, sizeof(struct smb2_negotiate_rsp) - + sizeof(struct smb2_hdr) - sizeof(rsp->Buffer) + + AUTH_GSS_LENGTH); + rsp->SecurityMode = SMB2_NEGOTIATE_SIGNING_ENABLED_LE; + conn->use_spnego = true; + + if ((server_conf.signing == KSMBD_CONFIG_OPT_AUTO || + server_conf.signing == KSMBD_CONFIG_OPT_DISABLED) && + req->SecurityMode & SMB2_NEGOTIATE_SIGNING_REQUIRED_LE) + conn->sign = true; + else if (server_conf.signing == KSMBD_CONFIG_OPT_MANDATORY) { + server_conf.enforced_signing = true; + rsp->SecurityMode |= SMB2_NEGOTIATE_SIGNING_REQUIRED_LE; + conn->sign = true; + } + + conn->srv_sec_mode = le16_to_cpu(rsp->SecurityMode); + ksmbd_conn_set_need_negotiate(conn); + +err_out: + if (rc < 0) + smb2_set_err_rsp(work); + + return rc; +} + +static int alloc_preauth_hash(struct ksmbd_session *sess, + struct ksmbd_conn *conn) +{ + if (sess->Preauth_HashValue) + return 0; + + sess->Preauth_HashValue = kmemdup(conn->preauth_info->Preauth_HashValue, + PREAUTH_HASHVALUE_SIZE, GFP_KERNEL); + if (!sess->Preauth_HashValue) + return -ENOMEM; + + return 0; +} + +static int generate_preauth_hash(struct ksmbd_work *work) +{ + struct ksmbd_conn *conn = work->conn; + struct ksmbd_session *sess = work->sess; + u8 *preauth_hash; + + if (conn->dialect != SMB311_PROT_ID) + return 0; + + if (conn->binding) { + struct preauth_session *preauth_sess; + + preauth_sess = ksmbd_preauth_session_lookup(conn, sess->id); + if (!preauth_sess) { + preauth_sess = ksmbd_preauth_session_alloc(conn, sess->id); + if (!preauth_sess) + return -ENOMEM; + } + + preauth_hash = preauth_sess->Preauth_HashValue; + } else { + if (!sess->Preauth_HashValue) + if (alloc_preauth_hash(sess, conn)) + return -ENOMEM; + preauth_hash = sess->Preauth_HashValue; + } + + ksmbd_gen_preauth_integrity_hash(conn, work->request_buf, preauth_hash); + return 0; +} + +static int decode_negotiation_token(struct ksmbd_conn *conn, + struct negotiate_message *negblob, + size_t sz) +{ + if (!conn->use_spnego) + return -EINVAL; + + if (ksmbd_decode_negTokenInit((char *)negblob, sz, conn)) { + if (ksmbd_decode_negTokenTarg((char *)negblob, sz, conn)) { + conn->auth_mechs |= KSMBD_AUTH_NTLMSSP; + conn->preferred_auth_mech = KSMBD_AUTH_NTLMSSP; + conn->use_spnego = false; + } + } + return 0; +} + +static int ntlm_negotiate(struct ksmbd_work *work, + struct negotiate_message *negblob, + size_t negblob_len) +{ + struct smb2_sess_setup_rsp *rsp = smb2_get_msg(work->response_buf); + struct challenge_message *chgblob; + unsigned char *spnego_blob = NULL; + u16 spnego_blob_len; + char *neg_blob; + int sz, rc; + + ksmbd_debug(SMB, "negotiate phase\n"); + rc = ksmbd_decode_ntlmssp_neg_blob(negblob, negblob_len, work->conn); + if (rc) + return rc; + + sz = le16_to_cpu(rsp->SecurityBufferOffset); + chgblob = + (struct challenge_message *)((char *)&rsp->hdr.ProtocolId + sz); + memset(chgblob, 0, sizeof(struct challenge_message)); + + if (!work->conn->use_spnego) { + sz = ksmbd_build_ntlmssp_challenge_blob(chgblob, work->conn); + if (sz < 0) + return -ENOMEM; + + rsp->SecurityBufferLength = cpu_to_le16(sz); + return 0; + } + + sz = sizeof(struct challenge_message); + sz += (strlen(ksmbd_netbios_name()) * 2 + 1 + 4) * 6; + + neg_blob = kzalloc(sz, GFP_KERNEL); + if (!neg_blob) + return -ENOMEM; + + chgblob = (struct challenge_message *)neg_blob; + sz = ksmbd_build_ntlmssp_challenge_blob(chgblob, work->conn); + if (sz < 0) { + rc = -ENOMEM; + goto out; + } + + rc = build_spnego_ntlmssp_neg_blob(&spnego_blob, &spnego_blob_len, + neg_blob, sz); + if (rc) { + rc = -ENOMEM; + goto out; + } + + sz = le16_to_cpu(rsp->SecurityBufferOffset); + memcpy((char *)&rsp->hdr.ProtocolId + sz, spnego_blob, spnego_blob_len); + rsp->SecurityBufferLength = cpu_to_le16(spnego_blob_len); + +out: + kfree(spnego_blob); + kfree(neg_blob); + return rc; +} + +static struct authenticate_message *user_authblob(struct ksmbd_conn *conn, + struct smb2_sess_setup_req *req) +{ + int sz; + + if (conn->use_spnego && conn->mechToken) + return (struct authenticate_message *)conn->mechToken; + + sz = le16_to_cpu(req->SecurityBufferOffset); + return (struct authenticate_message *)((char *)&req->hdr.ProtocolId + + sz); +} + +static struct ksmbd_user *session_user(struct ksmbd_conn *conn, + struct smb2_sess_setup_req *req) +{ + struct authenticate_message *authblob; + struct ksmbd_user *user; + char *name; + unsigned int name_off, name_len, secbuf_len; + + secbuf_len = le16_to_cpu(req->SecurityBufferLength); + if (secbuf_len < sizeof(struct authenticate_message)) { + ksmbd_debug(SMB, "blob len %d too small\n", secbuf_len); + return NULL; + } + authblob = user_authblob(conn, req); + name_off = le32_to_cpu(authblob->UserName.BufferOffset); + name_len = le16_to_cpu(authblob->UserName.Length); + + if (secbuf_len < (u64)name_off + name_len) + return NULL; + + name = smb_strndup_from_utf16((const char *)authblob + name_off, + name_len, + true, + conn->local_nls); + if (IS_ERR(name)) { + pr_err("cannot allocate memory\n"); + return NULL; + } + + ksmbd_debug(SMB, "session setup request for user %s\n", name); + user = ksmbd_login_user(name); + kfree(name); + return user; +} + +static int ntlm_authenticate(struct ksmbd_work *work) +{ + struct smb2_sess_setup_req *req = smb2_get_msg(work->request_buf); + struct smb2_sess_setup_rsp *rsp = smb2_get_msg(work->response_buf); + struct ksmbd_conn *conn = work->conn; + struct ksmbd_session *sess = work->sess; + struct channel *chann = NULL; + struct ksmbd_user *user; + u64 prev_id; + int sz, rc; + + ksmbd_debug(SMB, "authenticate phase\n"); + if (conn->use_spnego) { + unsigned char *spnego_blob; + u16 spnego_blob_len; + + rc = build_spnego_ntlmssp_auth_blob(&spnego_blob, + &spnego_blob_len, + 0); + if (rc) + return -ENOMEM; + + sz = le16_to_cpu(rsp->SecurityBufferOffset); + memcpy((char *)&rsp->hdr.ProtocolId + sz, spnego_blob, spnego_blob_len); + rsp->SecurityBufferLength = cpu_to_le16(spnego_blob_len); + kfree(spnego_blob); + inc_rfc1001_len(work->response_buf, spnego_blob_len - 1); + } + + user = session_user(conn, req); + if (!user) { + ksmbd_debug(SMB, "Unknown user name or an error\n"); + return -EPERM; + } + + /* Check for previous session */ + prev_id = le64_to_cpu(req->PreviousSessionId); + if (prev_id && prev_id != sess->id) + destroy_previous_session(conn, user, prev_id); + + if (sess->state == SMB2_SESSION_VALID) { + /* + * Reuse session if anonymous try to connect + * on reauthetication. + */ + if (conn->binding == false && ksmbd_anonymous_user(user)) { + ksmbd_free_user(user); + return 0; + } + + if (!ksmbd_compare_user(sess->user, user)) { + ksmbd_free_user(user); + return -EPERM; + } + ksmbd_free_user(user); + } else { + sess->user = user; + } + + if (conn->binding == false && user_guest(sess->user)) { + rsp->SessionFlags = SMB2_SESSION_FLAG_IS_GUEST_LE; + } else { + struct authenticate_message *authblob; + + authblob = user_authblob(conn, req); + sz = le16_to_cpu(req->SecurityBufferLength); + rc = ksmbd_decode_ntlmssp_auth_blob(authblob, sz, conn, sess); + if (rc) { + set_user_flag(sess->user, KSMBD_USER_FLAG_BAD_PASSWORD); + ksmbd_debug(SMB, "authentication failed\n"); + return -EPERM; + } + } + + /* + * If session state is SMB2_SESSION_VALID, We can assume + * that it is reauthentication. And the user/password + * has been verified, so return it here. + */ + if (sess->state == SMB2_SESSION_VALID) { + if (conn->binding) + goto binding_session; + return 0; + } + + if ((rsp->SessionFlags != SMB2_SESSION_FLAG_IS_GUEST_LE && + (conn->sign || server_conf.enforced_signing)) || + (req->SecurityMode & SMB2_NEGOTIATE_SIGNING_REQUIRED)) + sess->sign = true; + + if (smb3_encryption_negotiated(conn) && + !(req->Flags & SMB2_SESSION_REQ_FLAG_BINDING)) { + rc = conn->ops->generate_encryptionkey(conn, sess); + if (rc) { + ksmbd_debug(SMB, + "SMB3 encryption key generation failed\n"); + return -EINVAL; + } + sess->enc = true; + rsp->SessionFlags = SMB2_SESSION_FLAG_ENCRYPT_DATA_LE; + /* + * signing is disable if encryption is enable + * on this session + */ + sess->sign = false; + } + +binding_session: + if (conn->dialect >= SMB30_PROT_ID) { + chann = lookup_chann_list(sess, conn); + if (!chann) { + chann = kmalloc(sizeof(struct channel), GFP_KERNEL); + if (!chann) + return -ENOMEM; + + chann->conn = conn; + xa_store(&sess->ksmbd_chann_list, (long)conn, chann, GFP_KERNEL); + } + } + + if (conn->ops->generate_signingkey) { + rc = conn->ops->generate_signingkey(sess, conn); + if (rc) { + ksmbd_debug(SMB, "SMB3 signing key generation failed\n"); + return -EINVAL; + } + } + + if (!ksmbd_conn_lookup_dialect(conn)) { + pr_err("fail to verify the dialect\n"); + return -ENOENT; + } + return 0; +} + +#ifdef CONFIG_SMB_SERVER_KERBEROS5 +static int krb5_authenticate(struct ksmbd_work *work) +{ + struct smb2_sess_setup_req *req = smb2_get_msg(work->request_buf); + struct smb2_sess_setup_rsp *rsp = smb2_get_msg(work->response_buf); + struct ksmbd_conn *conn = work->conn; + struct ksmbd_session *sess = work->sess; + char *in_blob, *out_blob; + struct channel *chann = NULL; + u64 prev_sess_id; + int in_len, out_len; + int retval; + + in_blob = (char *)&req->hdr.ProtocolId + + le16_to_cpu(req->SecurityBufferOffset); + in_len = le16_to_cpu(req->SecurityBufferLength); + out_blob = (char *)&rsp->hdr.ProtocolId + + le16_to_cpu(rsp->SecurityBufferOffset); + out_len = work->response_sz - + (le16_to_cpu(rsp->SecurityBufferOffset) + 4); + + /* Check previous session */ + prev_sess_id = le64_to_cpu(req->PreviousSessionId); + if (prev_sess_id && prev_sess_id != sess->id) + destroy_previous_session(conn, sess->user, prev_sess_id); + + if (sess->state == SMB2_SESSION_VALID) + ksmbd_free_user(sess->user); + + retval = ksmbd_krb5_authenticate(sess, in_blob, in_len, + out_blob, &out_len); + if (retval) { + ksmbd_debug(SMB, "krb5 authentication failed\n"); + return -EINVAL; + } + rsp->SecurityBufferLength = cpu_to_le16(out_len); + inc_rfc1001_len(work->response_buf, out_len - 1); + + if ((conn->sign || server_conf.enforced_signing) || + (req->SecurityMode & SMB2_NEGOTIATE_SIGNING_REQUIRED)) + sess->sign = true; + + if (smb3_encryption_negotiated(conn)) { + retval = conn->ops->generate_encryptionkey(conn, sess); + if (retval) { + ksmbd_debug(SMB, + "SMB3 encryption key generation failed\n"); + return -EINVAL; + } + sess->enc = true; + rsp->SessionFlags = SMB2_SESSION_FLAG_ENCRYPT_DATA_LE; + sess->sign = false; + } + + if (conn->dialect >= SMB30_PROT_ID) { + chann = lookup_chann_list(sess, conn); + if (!chann) { + chann = kmalloc(sizeof(struct channel), GFP_KERNEL); + if (!chann) + return -ENOMEM; + + chann->conn = conn; + xa_store(&sess->ksmbd_chann_list, (long)conn, chann, GFP_KERNEL); + } + } + + if (conn->ops->generate_signingkey) { + retval = conn->ops->generate_signingkey(sess, conn); + if (retval) { + ksmbd_debug(SMB, "SMB3 signing key generation failed\n"); + return -EINVAL; + } + } + + if (!ksmbd_conn_lookup_dialect(conn)) { + pr_err("fail to verify the dialect\n"); + return -ENOENT; + } + return 0; +} +#else +static int krb5_authenticate(struct ksmbd_work *work) +{ + return -EOPNOTSUPP; +} +#endif + +int smb2_sess_setup(struct ksmbd_work *work) +{ + struct ksmbd_conn *conn = work->conn; + struct smb2_sess_setup_req *req = smb2_get_msg(work->request_buf); + struct smb2_sess_setup_rsp *rsp = smb2_get_msg(work->response_buf); + struct ksmbd_session *sess; + struct negotiate_message *negblob; + unsigned int negblob_len, negblob_off; + int rc = 0; + + ksmbd_debug(SMB, "Received request for session setup\n"); + + rsp->StructureSize = cpu_to_le16(9); + rsp->SessionFlags = 0; + rsp->SecurityBufferOffset = cpu_to_le16(72); + rsp->SecurityBufferLength = 0; + inc_rfc1001_len(work->response_buf, 9); + + ksmbd_conn_lock(conn); + if (!req->hdr.SessionId) { + sess = ksmbd_smb2_session_create(); + if (!sess) { + rc = -ENOMEM; + goto out_err; + } + rsp->hdr.SessionId = cpu_to_le64(sess->id); + rc = ksmbd_session_register(conn, sess); + if (rc) + goto out_err; + } else if (conn->dialect >= SMB30_PROT_ID && + (server_conf.flags & KSMBD_GLOBAL_FLAG_SMB3_MULTICHANNEL) && + req->Flags & SMB2_SESSION_REQ_FLAG_BINDING) { + u64 sess_id = le64_to_cpu(req->hdr.SessionId); + + sess = ksmbd_session_lookup_slowpath(sess_id); + if (!sess) { + rc = -ENOENT; + goto out_err; + } + + if (conn->dialect != sess->dialect) { + rc = -EINVAL; + goto out_err; + } + + if (!(req->hdr.Flags & SMB2_FLAGS_SIGNED)) { + rc = -EINVAL; + goto out_err; + } + + if (strncmp(conn->ClientGUID, sess->ClientGUID, + SMB2_CLIENT_GUID_SIZE)) { + rc = -ENOENT; + goto out_err; + } + + if (sess->state == SMB2_SESSION_IN_PROGRESS) { + rc = -EACCES; + goto out_err; + } + + if (sess->state == SMB2_SESSION_EXPIRED) { + rc = -EFAULT; + goto out_err; + } + + if (ksmbd_conn_need_reconnect(conn)) { + rc = -EFAULT; + sess = NULL; + goto out_err; + } + + if (ksmbd_session_lookup(conn, sess_id)) { + rc = -EACCES; + goto out_err; + } + + if (user_guest(sess->user)) { + rc = -EOPNOTSUPP; + goto out_err; + } + + conn->binding = true; + } else if ((conn->dialect < SMB30_PROT_ID || + server_conf.flags & KSMBD_GLOBAL_FLAG_SMB3_MULTICHANNEL) && + (req->Flags & SMB2_SESSION_REQ_FLAG_BINDING)) { + sess = NULL; + rc = -EACCES; + goto out_err; + } else { + sess = ksmbd_session_lookup(conn, + le64_to_cpu(req->hdr.SessionId)); + if (!sess) { + rc = -ENOENT; + goto out_err; + } + + if (sess->state == SMB2_SESSION_EXPIRED) { + rc = -EFAULT; + goto out_err; + } + + if (ksmbd_conn_need_reconnect(conn)) { + rc = -EFAULT; + sess = NULL; + goto out_err; + } + } + work->sess = sess; + + negblob_off = le16_to_cpu(req->SecurityBufferOffset); + negblob_len = le16_to_cpu(req->SecurityBufferLength); + if (negblob_off < offsetof(struct smb2_sess_setup_req, Buffer) || + negblob_len < offsetof(struct negotiate_message, NegotiateFlags)) { + rc = -EINVAL; + goto out_err; + } + + negblob = (struct negotiate_message *)((char *)&req->hdr.ProtocolId + + negblob_off); + + if (decode_negotiation_token(conn, negblob, negblob_len) == 0) { + if (conn->mechToken) + negblob = (struct negotiate_message *)conn->mechToken; + } + + if (server_conf.auth_mechs & conn->auth_mechs) { + rc = generate_preauth_hash(work); + if (rc) + goto out_err; + + if (conn->preferred_auth_mech & + (KSMBD_AUTH_KRB5 | KSMBD_AUTH_MSKRB5)) { + rc = krb5_authenticate(work); + if (rc) { + rc = -EINVAL; + goto out_err; + } + + if (!ksmbd_conn_need_reconnect(conn)) { + ksmbd_conn_set_good(conn); + sess->state = SMB2_SESSION_VALID; + } + kfree(sess->Preauth_HashValue); + sess->Preauth_HashValue = NULL; + } else if (conn->preferred_auth_mech == KSMBD_AUTH_NTLMSSP) { + if (negblob->MessageType == NtLmNegotiate) { + rc = ntlm_negotiate(work, negblob, negblob_len); + if (rc) + goto out_err; + rsp->hdr.Status = + STATUS_MORE_PROCESSING_REQUIRED; + /* + * Note: here total size -1 is done as an + * adjustment for 0 size blob + */ + inc_rfc1001_len(work->response_buf, + le16_to_cpu(rsp->SecurityBufferLength) - 1); + + } else if (negblob->MessageType == NtLmAuthenticate) { + rc = ntlm_authenticate(work); + if (rc) + goto out_err; + + if (!ksmbd_conn_need_reconnect(conn)) { + ksmbd_conn_set_good(conn); + sess->state = SMB2_SESSION_VALID; + } + if (conn->binding) { + struct preauth_session *preauth_sess; + + preauth_sess = + ksmbd_preauth_session_lookup(conn, sess->id); + if (preauth_sess) { + list_del(&preauth_sess->preauth_entry); + kfree(preauth_sess); + } + } + kfree(sess->Preauth_HashValue); + sess->Preauth_HashValue = NULL; + } else { + pr_info_ratelimited("Unknown NTLMSSP message type : 0x%x\n", + le32_to_cpu(negblob->MessageType)); + rc = -EINVAL; + } + } else { + /* TODO: need one more negotiation */ + pr_err("Not support the preferred authentication\n"); + rc = -EINVAL; + } + } else { + pr_err("Not support authentication\n"); + rc = -EINVAL; + } + +out_err: + if (rc == -EINVAL) + rsp->hdr.Status = STATUS_INVALID_PARAMETER; + else if (rc == -ENOENT) + rsp->hdr.Status = STATUS_USER_SESSION_DELETED; + else if (rc == -EACCES) + rsp->hdr.Status = STATUS_REQUEST_NOT_ACCEPTED; + else if (rc == -EFAULT) + rsp->hdr.Status = STATUS_NETWORK_SESSION_EXPIRED; + else if (rc == -ENOMEM) + rsp->hdr.Status = STATUS_INSUFFICIENT_RESOURCES; + else if (rc == -EOPNOTSUPP) + rsp->hdr.Status = STATUS_NOT_SUPPORTED; + else if (rc) + rsp->hdr.Status = STATUS_LOGON_FAILURE; + + if (conn->use_spnego && conn->mechToken) { + kfree(conn->mechToken); + conn->mechToken = NULL; + } + + if (rc < 0) { + /* + * SecurityBufferOffset should be set to zero + * in session setup error response. + */ + rsp->SecurityBufferOffset = 0; + + if (sess) { + bool try_delay = false; + + /* + * To avoid dictionary attacks (repeated session setups rapidly sent) to + * connect to server, ksmbd make a delay of a 5 seconds on session setup + * failure to make it harder to send enough random connection requests + * to break into a server. + */ + if (sess->user && sess->user->flags & KSMBD_USER_FLAG_DELAY_SESSION) + try_delay = true; + + sess->last_active = jiffies; + sess->state = SMB2_SESSION_EXPIRED; + if (try_delay) { + ksmbd_conn_set_need_reconnect(conn); + ssleep(5); + ksmbd_conn_set_need_negotiate(conn); + } + } + } + + ksmbd_conn_unlock(conn); + return rc; +} + +/** + * smb2_tree_connect() - handler for smb2 tree connect command + * @work: smb work containing smb request buffer + * + * Return: 0 on success, otherwise error + */ +int smb2_tree_connect(struct ksmbd_work *work) +{ + struct ksmbd_conn *conn = work->conn; + struct smb2_tree_connect_req *req = smb2_get_msg(work->request_buf); + struct smb2_tree_connect_rsp *rsp = smb2_get_msg(work->response_buf); + struct ksmbd_session *sess = work->sess; + char *treename = NULL, *name = NULL; + struct ksmbd_tree_conn_status status; + struct ksmbd_share_config *share; + int rc = -EINVAL; + + treename = smb_strndup_from_utf16(req->Buffer, + le16_to_cpu(req->PathLength), true, + conn->local_nls); + if (IS_ERR(treename)) { + pr_err("treename is NULL\n"); + status.ret = KSMBD_TREE_CONN_STATUS_ERROR; + goto out_err1; + } + + name = ksmbd_extract_sharename(conn->um, treename); + if (IS_ERR(name)) { + status.ret = KSMBD_TREE_CONN_STATUS_ERROR; + goto out_err1; + } + + ksmbd_debug(SMB, "tree connect request for tree %s treename %s\n", + name, treename); + + status = ksmbd_tree_conn_connect(conn, sess, name); + if (status.ret == KSMBD_TREE_CONN_STATUS_OK) + rsp->hdr.Id.SyncId.TreeId = cpu_to_le32(status.tree_conn->id); + else + goto out_err1; + + share = status.tree_conn->share_conf; + if (test_share_config_flag(share, KSMBD_SHARE_FLAG_PIPE)) { + ksmbd_debug(SMB, "IPC share path request\n"); + rsp->ShareType = SMB2_SHARE_TYPE_PIPE; + rsp->MaximalAccess = FILE_READ_DATA_LE | FILE_READ_EA_LE | + FILE_EXECUTE_LE | FILE_READ_ATTRIBUTES_LE | + FILE_DELETE_LE | FILE_READ_CONTROL_LE | + FILE_WRITE_DAC_LE | FILE_WRITE_OWNER_LE | + FILE_SYNCHRONIZE_LE; + } else { + rsp->ShareType = SMB2_SHARE_TYPE_DISK; + rsp->MaximalAccess = FILE_READ_DATA_LE | FILE_READ_EA_LE | + FILE_EXECUTE_LE | FILE_READ_ATTRIBUTES_LE; + if (test_tree_conn_flag(status.tree_conn, + KSMBD_TREE_CONN_FLAG_WRITABLE)) { + rsp->MaximalAccess |= FILE_WRITE_DATA_LE | + FILE_APPEND_DATA_LE | FILE_WRITE_EA_LE | + FILE_DELETE_LE | FILE_WRITE_ATTRIBUTES_LE | + FILE_DELETE_CHILD_LE | FILE_READ_CONTROL_LE | + FILE_WRITE_DAC_LE | FILE_WRITE_OWNER_LE | + FILE_SYNCHRONIZE_LE; + } + } + + status.tree_conn->maximal_access = le32_to_cpu(rsp->MaximalAccess); + if (conn->posix_ext_supported) + status.tree_conn->posix_extensions = true; + + rsp->StructureSize = cpu_to_le16(16); + inc_rfc1001_len(work->response_buf, 16); +out_err1: + rsp->Capabilities = 0; + rsp->Reserved = 0; + /* default manual caching */ + rsp->ShareFlags = SMB2_SHAREFLAG_MANUAL_CACHING; + + if (!IS_ERR(treename)) + kfree(treename); + if (!IS_ERR(name)) + kfree(name); + + switch (status.ret) { + case KSMBD_TREE_CONN_STATUS_OK: + rsp->hdr.Status = STATUS_SUCCESS; + rc = 0; + break; + case -ESTALE: + case -ENOENT: + case KSMBD_TREE_CONN_STATUS_NO_SHARE: + rsp->hdr.Status = STATUS_BAD_NETWORK_NAME; + break; + case -ENOMEM: + case KSMBD_TREE_CONN_STATUS_NOMEM: + rsp->hdr.Status = STATUS_NO_MEMORY; + break; + case KSMBD_TREE_CONN_STATUS_ERROR: + case KSMBD_TREE_CONN_STATUS_TOO_MANY_CONNS: + case KSMBD_TREE_CONN_STATUS_TOO_MANY_SESSIONS: + rsp->hdr.Status = STATUS_ACCESS_DENIED; + break; + case -EINVAL: + rsp->hdr.Status = STATUS_INVALID_PARAMETER; + break; + default: + rsp->hdr.Status = STATUS_ACCESS_DENIED; + } + + if (status.ret != KSMBD_TREE_CONN_STATUS_OK) + smb2_set_err_rsp(work); + + return rc; +} + +/** + * smb2_create_open_flags() - convert smb open flags to unix open flags + * @file_present: is file already present + * @access: file access flags + * @disposition: file disposition flags + * @may_flags: set with MAY_ flags + * + * Return: file open flags + */ +static int smb2_create_open_flags(bool file_present, __le32 access, + __le32 disposition, + int *may_flags) +{ + int oflags = O_NONBLOCK | O_LARGEFILE; + + if (access & FILE_READ_DESIRED_ACCESS_LE && + access & FILE_WRITE_DESIRE_ACCESS_LE) { + oflags |= O_RDWR; + *may_flags = MAY_OPEN | MAY_READ | MAY_WRITE; + } else if (access & FILE_WRITE_DESIRE_ACCESS_LE) { + oflags |= O_WRONLY; + *may_flags = MAY_OPEN | MAY_WRITE; + } else { + oflags |= O_RDONLY; + *may_flags = MAY_OPEN | MAY_READ; + } + + if (access == FILE_READ_ATTRIBUTES_LE) + oflags |= O_PATH; + + if (file_present) { + switch (disposition & FILE_CREATE_MASK_LE) { + case FILE_OPEN_LE: + case FILE_CREATE_LE: + break; + case FILE_SUPERSEDE_LE: + case FILE_OVERWRITE_LE: + case FILE_OVERWRITE_IF_LE: + oflags |= O_TRUNC; + break; + default: + break; + } + } else { + switch (disposition & FILE_CREATE_MASK_LE) { + case FILE_SUPERSEDE_LE: + case FILE_CREATE_LE: + case FILE_OPEN_IF_LE: + case FILE_OVERWRITE_IF_LE: + oflags |= O_CREAT; + break; + case FILE_OPEN_LE: + case FILE_OVERWRITE_LE: + oflags &= ~O_CREAT; + break; + default: + break; + } + } + + return oflags; +} + +/** + * smb2_tree_disconnect() - handler for smb tree connect request + * @work: smb work containing request buffer + * + * Return: 0 + */ +int smb2_tree_disconnect(struct ksmbd_work *work) +{ + struct smb2_tree_disconnect_rsp *rsp = smb2_get_msg(work->response_buf); + struct ksmbd_session *sess = work->sess; + struct ksmbd_tree_connect *tcon = work->tcon; + + rsp->StructureSize = cpu_to_le16(4); + inc_rfc1001_len(work->response_buf, 4); + + ksmbd_debug(SMB, "request\n"); + + if (!tcon || test_and_set_bit(TREE_CONN_EXPIRE, &tcon->status)) { + struct smb2_tree_disconnect_req *req = + smb2_get_msg(work->request_buf); + + ksmbd_debug(SMB, "Invalid tid %d\n", req->hdr.Id.SyncId.TreeId); + + rsp->hdr.Status = STATUS_NETWORK_NAME_DELETED; + smb2_set_err_rsp(work); + return 0; + } + + ksmbd_close_tree_conn_fds(work); + ksmbd_tree_conn_disconnect(sess, tcon); + work->tcon = NULL; + return 0; +} + +/** + * smb2_session_logoff() - handler for session log off request + * @work: smb work containing request buffer + * + * Return: 0 + */ +int smb2_session_logoff(struct ksmbd_work *work) +{ + struct ksmbd_conn *conn = work->conn; + struct smb2_logoff_rsp *rsp = smb2_get_msg(work->response_buf); + struct ksmbd_session *sess; + struct smb2_logoff_req *req = smb2_get_msg(work->request_buf); + u64 sess_id = le64_to_cpu(req->hdr.SessionId); + + rsp->StructureSize = cpu_to_le16(4); + inc_rfc1001_len(work->response_buf, 4); + + ksmbd_debug(SMB, "request\n"); + + ksmbd_all_conn_set_status(sess_id, KSMBD_SESS_NEED_RECONNECT); + ksmbd_close_session_fds(work); + ksmbd_conn_wait_idle(conn, sess_id); + + /* + * Re-lookup session to validate if session is deleted + * while waiting request complete + */ + sess = ksmbd_session_lookup_all(conn, sess_id); + if (ksmbd_tree_conn_session_logoff(sess)) { + ksmbd_debug(SMB, "Invalid tid %d\n", req->hdr.Id.SyncId.TreeId); + rsp->hdr.Status = STATUS_NETWORK_NAME_DELETED; + smb2_set_err_rsp(work); + return 0; + } + + ksmbd_destroy_file_table(&sess->file_table); + sess->state = SMB2_SESSION_EXPIRED; + + ksmbd_free_user(sess->user); + sess->user = NULL; + ksmbd_all_conn_set_status(sess_id, KSMBD_SESS_NEED_NEGOTIATE); + return 0; +} + +/** + * create_smb2_pipe() - create IPC pipe + * @work: smb work containing request buffer + * + * Return: 0 on success, otherwise error + */ +static noinline int create_smb2_pipe(struct ksmbd_work *work) +{ + struct smb2_create_rsp *rsp = smb2_get_msg(work->response_buf); + struct smb2_create_req *req = smb2_get_msg(work->request_buf); + int id; + int err; + char *name; + + name = smb_strndup_from_utf16(req->Buffer, le16_to_cpu(req->NameLength), + 1, work->conn->local_nls); + if (IS_ERR(name)) { + rsp->hdr.Status = STATUS_NO_MEMORY; + err = PTR_ERR(name); + goto out; + } + + id = ksmbd_session_rpc_open(work->sess, name); + if (id < 0) { + pr_err("Unable to open RPC pipe: %d\n", id); + err = id; + goto out; + } + + rsp->hdr.Status = STATUS_SUCCESS; + rsp->StructureSize = cpu_to_le16(89); + rsp->OplockLevel = SMB2_OPLOCK_LEVEL_NONE; + rsp->Flags = 0; + rsp->CreateAction = cpu_to_le32(FILE_OPENED); + + rsp->CreationTime = cpu_to_le64(0); + rsp->LastAccessTime = cpu_to_le64(0); + rsp->ChangeTime = cpu_to_le64(0); + rsp->AllocationSize = cpu_to_le64(0); + rsp->EndofFile = cpu_to_le64(0); + rsp->FileAttributes = FILE_ATTRIBUTE_NORMAL_LE; + rsp->Reserved2 = 0; + rsp->VolatileFileId = id; + rsp->PersistentFileId = 0; + rsp->CreateContextsOffset = 0; + rsp->CreateContextsLength = 0; + + inc_rfc1001_len(work->response_buf, 88); /* StructureSize - 1*/ + kfree(name); + return 0; + +out: + switch (err) { + case -EINVAL: + rsp->hdr.Status = STATUS_INVALID_PARAMETER; + break; + case -ENOSPC: + case -ENOMEM: + rsp->hdr.Status = STATUS_NO_MEMORY; + break; + } + + if (!IS_ERR(name)) + kfree(name); + + smb2_set_err_rsp(work); + return err; +} + +/** + * smb2_set_ea() - handler for setting extended attributes using set + * info command + * @eabuf: set info command buffer + * @buf_len: set info command buffer length + * @path: dentry path for get ea + * + * Return: 0 on success, otherwise error + */ +static int smb2_set_ea(struct smb2_ea_info *eabuf, unsigned int buf_len, + const struct path *path) +{ + struct user_namespace *user_ns = mnt_user_ns(path->mnt); + char *attr_name = NULL, *value; + int rc = 0; + unsigned int next = 0; + + if (buf_len < sizeof(struct smb2_ea_info) + eabuf->EaNameLength + + le16_to_cpu(eabuf->EaValueLength)) + return -EINVAL; + + attr_name = kmalloc(XATTR_NAME_MAX + 1, GFP_KERNEL); + if (!attr_name) + return -ENOMEM; + + do { + if (!eabuf->EaNameLength) + goto next; + + ksmbd_debug(SMB, + "name : <%s>, name_len : %u, value_len : %u, next : %u\n", + eabuf->name, eabuf->EaNameLength, + le16_to_cpu(eabuf->EaValueLength), + le32_to_cpu(eabuf->NextEntryOffset)); + + if (eabuf->EaNameLength > + (XATTR_NAME_MAX - XATTR_USER_PREFIX_LEN)) { + rc = -EINVAL; + break; + } + + memcpy(attr_name, XATTR_USER_PREFIX, XATTR_USER_PREFIX_LEN); + memcpy(&attr_name[XATTR_USER_PREFIX_LEN], eabuf->name, + eabuf->EaNameLength); + attr_name[XATTR_USER_PREFIX_LEN + eabuf->EaNameLength] = '\0'; + value = (char *)&eabuf->name + eabuf->EaNameLength + 1; + + if (!eabuf->EaValueLength) { + rc = ksmbd_vfs_casexattr_len(user_ns, + path->dentry, + attr_name, + XATTR_USER_PREFIX_LEN + + eabuf->EaNameLength); + + /* delete the EA only when it exits */ + if (rc > 0) { + rc = ksmbd_vfs_remove_xattr(user_ns, + path->dentry, + attr_name); + + if (rc < 0) { + ksmbd_debug(SMB, + "remove xattr failed(%d)\n", + rc); + break; + } + } + + /* if the EA doesn't exist, just do nothing. */ + rc = 0; + } else { + rc = ksmbd_vfs_setxattr(user_ns, + path->dentry, attr_name, value, + le16_to_cpu(eabuf->EaValueLength), 0); + if (rc < 0) { + ksmbd_debug(SMB, + "ksmbd_vfs_setxattr is failed(%d)\n", + rc); + break; + } + } + +next: + next = le32_to_cpu(eabuf->NextEntryOffset); + if (next == 0 || buf_len < next) + break; + buf_len -= next; + eabuf = (struct smb2_ea_info *)((char *)eabuf + next); + if (next < (u32)eabuf->EaNameLength + le16_to_cpu(eabuf->EaValueLength)) + break; + + } while (next != 0); + + kfree(attr_name); + return rc; +} + +static noinline int smb2_set_stream_name_xattr(const struct path *path, + struct ksmbd_file *fp, + char *stream_name, int s_type) +{ + struct user_namespace *user_ns = mnt_user_ns(path->mnt); + size_t xattr_stream_size; + char *xattr_stream_name; + int rc; + + rc = ksmbd_vfs_xattr_stream_name(stream_name, + &xattr_stream_name, + &xattr_stream_size, + s_type); + if (rc) + return rc; + + fp->stream.name = xattr_stream_name; + fp->stream.size = xattr_stream_size; + + /* Check if there is stream prefix in xattr space */ + rc = ksmbd_vfs_casexattr_len(user_ns, + path->dentry, + xattr_stream_name, + xattr_stream_size); + if (rc >= 0) + return 0; + + if (fp->cdoption == FILE_OPEN_LE) { + ksmbd_debug(SMB, "XATTR stream name lookup failed: %d\n", rc); + return -EBADF; + } + + rc = ksmbd_vfs_setxattr(user_ns, path->dentry, + xattr_stream_name, NULL, 0, 0); + if (rc < 0) + pr_err("Failed to store XATTR stream name :%d\n", rc); + return 0; +} + +static int smb2_remove_smb_xattrs(const struct path *path) +{ + struct user_namespace *user_ns = mnt_user_ns(path->mnt); + char *name, *xattr_list = NULL; + ssize_t xattr_list_len; + int err = 0; + + xattr_list_len = ksmbd_vfs_listxattr(path->dentry, &xattr_list); + if (xattr_list_len < 0) { + goto out; + } else if (!xattr_list_len) { + ksmbd_debug(SMB, "empty xattr in the file\n"); + goto out; + } + + for (name = xattr_list; name - xattr_list < xattr_list_len; + name += strlen(name) + 1) { + ksmbd_debug(SMB, "%s, len %zd\n", name, strlen(name)); + + if (!strncmp(name, XATTR_USER_PREFIX, XATTR_USER_PREFIX_LEN) && + !strncmp(&name[XATTR_USER_PREFIX_LEN], STREAM_PREFIX, + STREAM_PREFIX_LEN)) { + err = ksmbd_vfs_remove_xattr(user_ns, path->dentry, + name); + if (err) + ksmbd_debug(SMB, "remove xattr failed : %s\n", + name); + } + } +out: + kvfree(xattr_list); + return err; +} + +static int smb2_create_truncate(const struct path *path) +{ + int rc = vfs_truncate(path, 0); + + if (rc) { + pr_err("vfs_truncate failed, rc %d\n", rc); + return rc; + } + + rc = smb2_remove_smb_xattrs(path); + if (rc == -EOPNOTSUPP) + rc = 0; + if (rc) + ksmbd_debug(SMB, + "ksmbd_truncate_stream_name_xattr failed, rc %d\n", + rc); + return rc; +} + +static void smb2_new_xattrs(struct ksmbd_tree_connect *tcon, const struct path *path, + struct ksmbd_file *fp) +{ + struct xattr_dos_attrib da = {0}; + int rc; + + if (!test_share_config_flag(tcon->share_conf, + KSMBD_SHARE_FLAG_STORE_DOS_ATTRS)) + return; + + da.version = 4; + da.attr = le32_to_cpu(fp->f_ci->m_fattr); + da.itime = da.create_time = fp->create_time; + da.flags = XATTR_DOSINFO_ATTRIB | XATTR_DOSINFO_CREATE_TIME | + XATTR_DOSINFO_ITIME; + + rc = ksmbd_vfs_set_dos_attrib_xattr(mnt_user_ns(path->mnt), + path->dentry, &da); + if (rc) + ksmbd_debug(SMB, "failed to store file attribute into xattr\n"); +} + +static void smb2_update_xattrs(struct ksmbd_tree_connect *tcon, + const struct path *path, struct ksmbd_file *fp) +{ + struct xattr_dos_attrib da; + int rc; + + fp->f_ci->m_fattr &= ~(FILE_ATTRIBUTE_HIDDEN_LE | FILE_ATTRIBUTE_SYSTEM_LE); + + /* get FileAttributes from XATTR_NAME_DOS_ATTRIBUTE */ + if (!test_share_config_flag(tcon->share_conf, + KSMBD_SHARE_FLAG_STORE_DOS_ATTRS)) + return; + + rc = ksmbd_vfs_get_dos_attrib_xattr(mnt_user_ns(path->mnt), + path->dentry, &da); + if (rc > 0) { + fp->f_ci->m_fattr = cpu_to_le32(da.attr); + fp->create_time = da.create_time; + fp->itime = da.itime; + } +} + +static int smb2_creat(struct ksmbd_work *work, struct path *path, char *name, + int open_flags, umode_t posix_mode, bool is_dir) +{ + struct ksmbd_tree_connect *tcon = work->tcon; + struct ksmbd_share_config *share = tcon->share_conf; + umode_t mode; + int rc; + + if (!(open_flags & O_CREAT)) + return -EBADF; + + ksmbd_debug(SMB, "file does not exist, so creating\n"); + if (is_dir == true) { + ksmbd_debug(SMB, "creating directory\n"); + + mode = share_config_directory_mode(share, posix_mode); + rc = ksmbd_vfs_mkdir(work, name, mode); + if (rc) + return rc; + } else { + ksmbd_debug(SMB, "creating regular file\n"); + + mode = share_config_create_mode(share, posix_mode); + rc = ksmbd_vfs_create(work, name, mode); + if (rc) + return rc; + } + + rc = ksmbd_vfs_kern_path(work, name, 0, path, 0); + if (rc) { + pr_err("cannot get linux path (%s), err = %d\n", + name, rc); + return rc; + } + return 0; +} + +static int smb2_create_sd_buffer(struct ksmbd_work *work, + struct smb2_create_req *req, + const struct path *path) +{ + struct create_context *context; + struct create_sd_buf_req *sd_buf; + + if (!req->CreateContextsOffset) + return -ENOENT; + + /* Parse SD BUFFER create contexts */ + context = smb2_find_context_vals(req, SMB2_CREATE_SD_BUFFER, 4); + if (!context) + return -ENOENT; + else if (IS_ERR(context)) + return PTR_ERR(context); + + ksmbd_debug(SMB, + "Set ACLs using SMB2_CREATE_SD_BUFFER context\n"); + sd_buf = (struct create_sd_buf_req *)context; + if (le16_to_cpu(context->DataOffset) + + le32_to_cpu(context->DataLength) < + sizeof(struct create_sd_buf_req)) + return -EINVAL; + return set_info_sec(work->conn, work->tcon, path, &sd_buf->ntsd, + le32_to_cpu(sd_buf->ccontext.DataLength), true); +} + +static void ksmbd_acls_fattr(struct smb_fattr *fattr, + struct user_namespace *mnt_userns, + struct inode *inode) +{ + vfsuid_t vfsuid = i_uid_into_vfsuid(mnt_userns, inode); + vfsgid_t vfsgid = i_gid_into_vfsgid(mnt_userns, inode); + + fattr->cf_uid = vfsuid_into_kuid(vfsuid); + fattr->cf_gid = vfsgid_into_kgid(vfsgid); + fattr->cf_mode = inode->i_mode; + fattr->cf_acls = NULL; + fattr->cf_dacls = NULL; + + if (IS_ENABLED(CONFIG_FS_POSIX_ACL)) { + fattr->cf_acls = get_acl(inode, ACL_TYPE_ACCESS); + if (S_ISDIR(inode->i_mode)) + fattr->cf_dacls = get_acl(inode, ACL_TYPE_DEFAULT); + } +} + +/** + * smb2_open() - handler for smb file open request + * @work: smb work containing request buffer + * + * Return: 0 on success, otherwise error + */ +int smb2_open(struct ksmbd_work *work) +{ + struct ksmbd_conn *conn = work->conn; + struct ksmbd_session *sess = work->sess; + struct ksmbd_tree_connect *tcon = work->tcon; + struct smb2_create_req *req; + struct smb2_create_rsp *rsp; + struct path path; + struct ksmbd_share_config *share = tcon->share_conf; + struct ksmbd_file *fp = NULL; + struct file *filp = NULL; + struct user_namespace *user_ns = NULL; + struct kstat stat; + struct create_context *context; + struct lease_ctx_info *lc = NULL; + struct create_ea_buf_req *ea_buf = NULL; + struct oplock_info *opinfo; + __le32 *next_ptr = NULL; + int req_op_level = 0, open_flags = 0, may_flags = 0, file_info = 0; + int rc = 0; + int contxt_cnt = 0, query_disk_id = 0; + int maximal_access_ctxt = 0, posix_ctxt = 0; + int s_type = 0; + int next_off = 0; + char *name = NULL; + char *stream_name = NULL; + bool file_present = false, created = false, already_permitted = false; + int share_ret, need_truncate = 0; + u64 time; + umode_t posix_mode = 0; + __le32 daccess, maximal_access = 0; + + WORK_BUFFERS(work, req, rsp); + + if (req->hdr.NextCommand && !work->next_smb2_rcv_hdr_off && + (req->hdr.Flags & SMB2_FLAGS_RELATED_OPERATIONS)) { + ksmbd_debug(SMB, "invalid flag in chained command\n"); + rsp->hdr.Status = STATUS_INVALID_PARAMETER; + smb2_set_err_rsp(work); + return -EINVAL; + } + + if (test_share_config_flag(share, KSMBD_SHARE_FLAG_PIPE)) { + ksmbd_debug(SMB, "IPC pipe create request\n"); + return create_smb2_pipe(work); + } + + if (req->NameLength) { + if ((req->CreateOptions & FILE_DIRECTORY_FILE_LE) && + *(char *)req->Buffer == '\\') { + pr_err("not allow directory name included leading slash\n"); + rc = -EINVAL; + goto err_out1; + } + + name = smb2_get_name(req->Buffer, + le16_to_cpu(req->NameLength), + work->conn->local_nls); + if (IS_ERR(name)) { + rc = PTR_ERR(name); + if (rc != -ENOMEM) + rc = -ENOENT; + name = NULL; + goto err_out1; + } + + ksmbd_debug(SMB, "converted name = %s\n", name); + if (strchr(name, ':')) { + if (!test_share_config_flag(work->tcon->share_conf, + KSMBD_SHARE_FLAG_STREAMS)) { + rc = -EBADF; + goto err_out1; + } + rc = parse_stream_name(name, &stream_name, &s_type); + if (rc < 0) + goto err_out1; + } + + rc = ksmbd_validate_filename(name); + if (rc < 0) + goto err_out1; + + if (ksmbd_share_veto_filename(share, name)) { + rc = -ENOENT; + ksmbd_debug(SMB, "Reject open(), vetoed file: %s\n", + name); + goto err_out1; + } + } else { + name = kstrdup("", GFP_KERNEL); + if (!name) { + rc = -ENOMEM; + goto err_out1; + } + } + + req_op_level = req->RequestedOplockLevel; + if (req_op_level == SMB2_OPLOCK_LEVEL_LEASE) + lc = parse_lease_state(req); + + if (le32_to_cpu(req->ImpersonationLevel) > le32_to_cpu(IL_DELEGATE)) { + pr_err("Invalid impersonationlevel : 0x%x\n", + le32_to_cpu(req->ImpersonationLevel)); + rc = -EIO; + rsp->hdr.Status = STATUS_BAD_IMPERSONATION_LEVEL; + goto err_out1; + } + + if (req->CreateOptions && !(req->CreateOptions & CREATE_OPTIONS_MASK_LE)) { + pr_err("Invalid create options : 0x%x\n", + le32_to_cpu(req->CreateOptions)); + rc = -EINVAL; + goto err_out1; + } else { + if (req->CreateOptions & FILE_SEQUENTIAL_ONLY_LE && + req->CreateOptions & FILE_RANDOM_ACCESS_LE) + req->CreateOptions = ~(FILE_SEQUENTIAL_ONLY_LE); + + if (req->CreateOptions & + (FILE_OPEN_BY_FILE_ID_LE | CREATE_TREE_CONNECTION | + FILE_RESERVE_OPFILTER_LE)) { + rc = -EOPNOTSUPP; + goto err_out1; + } + + if (req->CreateOptions & FILE_DIRECTORY_FILE_LE) { + if (req->CreateOptions & FILE_NON_DIRECTORY_FILE_LE) { + rc = -EINVAL; + goto err_out1; + } else if (req->CreateOptions & FILE_NO_COMPRESSION_LE) { + req->CreateOptions = ~(FILE_NO_COMPRESSION_LE); + } + } + } + + if (le32_to_cpu(req->CreateDisposition) > + le32_to_cpu(FILE_OVERWRITE_IF_LE)) { + pr_err("Invalid create disposition : 0x%x\n", + le32_to_cpu(req->CreateDisposition)); + rc = -EINVAL; + goto err_out1; + } + + if (!(req->DesiredAccess & DESIRED_ACCESS_MASK)) { + pr_err("Invalid desired access : 0x%x\n", + le32_to_cpu(req->DesiredAccess)); + rc = -EACCES; + goto err_out1; + } + + if (req->FileAttributes && !(req->FileAttributes & FILE_ATTRIBUTE_MASK_LE)) { + pr_err("Invalid file attribute : 0x%x\n", + le32_to_cpu(req->FileAttributes)); + rc = -EINVAL; + goto err_out1; + } + + if (req->CreateContextsOffset) { + /* Parse non-durable handle create contexts */ + context = smb2_find_context_vals(req, SMB2_CREATE_EA_BUFFER, 4); + if (IS_ERR(context)) { + rc = PTR_ERR(context); + goto err_out1; + } else if (context) { + ea_buf = (struct create_ea_buf_req *)context; + if (le16_to_cpu(context->DataOffset) + + le32_to_cpu(context->DataLength) < + sizeof(struct create_ea_buf_req)) { + rc = -EINVAL; + goto err_out1; + } + if (req->CreateOptions & FILE_NO_EA_KNOWLEDGE_LE) { + rsp->hdr.Status = STATUS_ACCESS_DENIED; + rc = -EACCES; + goto err_out1; + } + } + + context = smb2_find_context_vals(req, + SMB2_CREATE_QUERY_MAXIMAL_ACCESS_REQUEST, 4); + if (IS_ERR(context)) { + rc = PTR_ERR(context); + goto err_out1; + } else if (context) { + ksmbd_debug(SMB, + "get query maximal access context\n"); + maximal_access_ctxt = 1; + } + + context = smb2_find_context_vals(req, + SMB2_CREATE_TIMEWARP_REQUEST, 4); + if (IS_ERR(context)) { + rc = PTR_ERR(context); + goto err_out1; + } else if (context) { + ksmbd_debug(SMB, "get timewarp context\n"); + rc = -EBADF; + goto err_out1; + } + + if (tcon->posix_extensions) { + context = smb2_find_context_vals(req, + SMB2_CREATE_TAG_POSIX, 16); + if (IS_ERR(context)) { + rc = PTR_ERR(context); + goto err_out1; + } else if (context) { + struct create_posix *posix = + (struct create_posix *)context; + if (le16_to_cpu(context->DataOffset) + + le32_to_cpu(context->DataLength) < + sizeof(struct create_posix) - 4) { + rc = -EINVAL; + goto err_out1; + } + ksmbd_debug(SMB, "get posix context\n"); + + posix_mode = le32_to_cpu(posix->Mode); + posix_ctxt = 1; + } + } + } + + if (ksmbd_override_fsids(work)) { + rc = -ENOMEM; + goto err_out1; + } + + rc = ksmbd_vfs_kern_path(work, name, LOOKUP_NO_SYMLINKS, &path, 1); + if (!rc) { + if (req->CreateOptions & FILE_DELETE_ON_CLOSE_LE) { + /* + * If file exists with under flags, return access + * denied error. + */ + if (req->CreateDisposition == FILE_OVERWRITE_IF_LE || + req->CreateDisposition == FILE_OPEN_IF_LE) { + rc = -EACCES; + path_put(&path); + goto err_out; + } + + if (!test_tree_conn_flag(tcon, KSMBD_TREE_CONN_FLAG_WRITABLE)) { + ksmbd_debug(SMB, + "User does not have write permission\n"); + rc = -EACCES; + path_put(&path); + goto err_out; + } + } else if (d_is_symlink(path.dentry)) { + rc = -EACCES; + path_put(&path); + goto err_out; + } + } + + if (rc) { + if (rc != -ENOENT) + goto err_out; + ksmbd_debug(SMB, "can not get linux path for %s, rc = %d\n", + name, rc); + rc = 0; + } else { + file_present = true; + user_ns = mnt_user_ns(path.mnt); + } + if (stream_name) { + if (req->CreateOptions & FILE_DIRECTORY_FILE_LE) { + if (s_type == DATA_STREAM) { + rc = -EIO; + rsp->hdr.Status = STATUS_NOT_A_DIRECTORY; + } + } else { + if (file_present && S_ISDIR(d_inode(path.dentry)->i_mode) && + s_type == DATA_STREAM) { + rc = -EIO; + rsp->hdr.Status = STATUS_FILE_IS_A_DIRECTORY; + } + } + + if (req->CreateOptions & FILE_DIRECTORY_FILE_LE && + req->FileAttributes & FILE_ATTRIBUTE_NORMAL_LE) { + rsp->hdr.Status = STATUS_NOT_A_DIRECTORY; + rc = -EIO; + } + + if (rc < 0) + goto err_out; + } + + if (file_present && req->CreateOptions & FILE_NON_DIRECTORY_FILE_LE && + S_ISDIR(d_inode(path.dentry)->i_mode) && + !(req->CreateOptions & FILE_DELETE_ON_CLOSE_LE)) { + ksmbd_debug(SMB, "open() argument is a directory: %s, %x\n", + name, req->CreateOptions); + rsp->hdr.Status = STATUS_FILE_IS_A_DIRECTORY; + rc = -EIO; + goto err_out; + } + + if (file_present && (req->CreateOptions & FILE_DIRECTORY_FILE_LE) && + !(req->CreateDisposition == FILE_CREATE_LE) && + !S_ISDIR(d_inode(path.dentry)->i_mode)) { + rsp->hdr.Status = STATUS_NOT_A_DIRECTORY; + rc = -EIO; + goto err_out; + } + + if (!stream_name && file_present && + req->CreateDisposition == FILE_CREATE_LE) { + rc = -EEXIST; + goto err_out; + } + + daccess = smb_map_generic_desired_access(req->DesiredAccess); + + if (file_present && !(req->CreateOptions & FILE_DELETE_ON_CLOSE_LE)) { + rc = smb_check_perm_dacl(conn, &path, &daccess, + sess->user->uid); + if (rc) + goto err_out; + } + + if (daccess & FILE_MAXIMAL_ACCESS_LE) { + if (!file_present) { + daccess = cpu_to_le32(GENERIC_ALL_FLAGS); + } else { + rc = ksmbd_vfs_query_maximal_access(user_ns, + path.dentry, + &daccess); + if (rc) + goto err_out; + already_permitted = true; + } + maximal_access = daccess; + } + + open_flags = smb2_create_open_flags(file_present, daccess, + req->CreateDisposition, + &may_flags); + + if (!test_tree_conn_flag(tcon, KSMBD_TREE_CONN_FLAG_WRITABLE)) { + if (open_flags & O_CREAT) { + ksmbd_debug(SMB, + "User does not have write permission\n"); + rc = -EACCES; + goto err_out; + } + } + + /*create file if not present */ + if (!file_present) { + rc = smb2_creat(work, &path, name, open_flags, posix_mode, + req->CreateOptions & FILE_DIRECTORY_FILE_LE); + if (rc) { + if (rc == -ENOENT) { + rc = -EIO; + rsp->hdr.Status = STATUS_OBJECT_PATH_NOT_FOUND; + } + goto err_out; + } + + created = true; + user_ns = mnt_user_ns(path.mnt); + if (ea_buf) { + if (le32_to_cpu(ea_buf->ccontext.DataLength) < + sizeof(struct smb2_ea_info)) { + rc = -EINVAL; + goto err_out; + } + + rc = smb2_set_ea(&ea_buf->ea, + le32_to_cpu(ea_buf->ccontext.DataLength), + &path); + if (rc == -EOPNOTSUPP) + rc = 0; + else if (rc) + goto err_out; + } + } else if (!already_permitted) { + /* FILE_READ_ATTRIBUTE is allowed without inode_permission, + * because execute(search) permission on a parent directory, + * is already granted. + */ + if (daccess & ~(FILE_READ_ATTRIBUTES_LE | FILE_READ_CONTROL_LE)) { + rc = inode_permission(user_ns, + d_inode(path.dentry), + may_flags); + if (rc) + goto err_out; + + if ((daccess & FILE_DELETE_LE) || + (req->CreateOptions & FILE_DELETE_ON_CLOSE_LE)) { + rc = ksmbd_vfs_may_delete(user_ns, + path.dentry); + if (rc) + goto err_out; + } + } + } + + rc = ksmbd_query_inode_status(d_inode(path.dentry->d_parent)); + if (rc == KSMBD_INODE_STATUS_PENDING_DELETE) { + rc = -EBUSY; + goto err_out; + } + + rc = 0; + filp = dentry_open(&path, open_flags, current_cred()); + if (IS_ERR(filp)) { + rc = PTR_ERR(filp); + pr_err("dentry open for dir failed, rc %d\n", rc); + goto err_out; + } + + if (file_present) { + if (!(open_flags & O_TRUNC)) + file_info = FILE_OPENED; + else + file_info = FILE_OVERWRITTEN; + + if ((req->CreateDisposition & FILE_CREATE_MASK_LE) == + FILE_SUPERSEDE_LE) + file_info = FILE_SUPERSEDED; + } else if (open_flags & O_CREAT) { + file_info = FILE_CREATED; + } + + ksmbd_vfs_set_fadvise(filp, req->CreateOptions); + + /* Obtain Volatile-ID */ + fp = ksmbd_open_fd(work, filp); + if (IS_ERR(fp)) { + fput(filp); + rc = PTR_ERR(fp); + fp = NULL; + goto err_out; + } + + /* Get Persistent-ID */ + ksmbd_open_durable_fd(fp); + if (!has_file_id(fp->persistent_id)) { + rc = -ENOMEM; + goto err_out; + } + + fp->cdoption = req->CreateDisposition; + fp->daccess = daccess; + fp->saccess = req->ShareAccess; + fp->coption = req->CreateOptions; + + /* Set default windows and posix acls if creating new file */ + if (created) { + int posix_acl_rc; + struct inode *inode = d_inode(path.dentry); + + posix_acl_rc = ksmbd_vfs_inherit_posix_acl(user_ns, + inode, + d_inode(path.dentry->d_parent)); + if (posix_acl_rc) + ksmbd_debug(SMB, "inherit posix acl failed : %d\n", posix_acl_rc); + + if (test_share_config_flag(work->tcon->share_conf, + KSMBD_SHARE_FLAG_ACL_XATTR)) { + rc = smb_inherit_dacl(conn, &path, sess->user->uid, + sess->user->gid); + } + + if (rc) { + rc = smb2_create_sd_buffer(work, req, &path); + if (rc) { + if (posix_acl_rc) + ksmbd_vfs_set_init_posix_acl(user_ns, + inode); + + if (test_share_config_flag(work->tcon->share_conf, + KSMBD_SHARE_FLAG_ACL_XATTR)) { + struct smb_fattr fattr; + struct smb_ntsd *pntsd; + int pntsd_size, ace_num = 0; + + ksmbd_acls_fattr(&fattr, user_ns, inode); + if (fattr.cf_acls) + ace_num = fattr.cf_acls->a_count; + if (fattr.cf_dacls) + ace_num += fattr.cf_dacls->a_count; + + pntsd = kmalloc(sizeof(struct smb_ntsd) + + sizeof(struct smb_sid) * 3 + + sizeof(struct smb_acl) + + sizeof(struct smb_ace) * ace_num * 2, + GFP_KERNEL); + if (!pntsd) { + posix_acl_release(fattr.cf_acls); + posix_acl_release(fattr.cf_dacls); + goto err_out; + } + + rc = build_sec_desc(user_ns, + pntsd, NULL, 0, + OWNER_SECINFO | + GROUP_SECINFO | + DACL_SECINFO, + &pntsd_size, &fattr); + posix_acl_release(fattr.cf_acls); + posix_acl_release(fattr.cf_dacls); + if (rc) { + kfree(pntsd); + goto err_out; + } + + rc = ksmbd_vfs_set_sd_xattr(conn, + user_ns, + path.dentry, + pntsd, + pntsd_size); + kfree(pntsd); + if (rc) + pr_err("failed to store ntacl in xattr : %d\n", + rc); + } + } + } + rc = 0; + } + + if (stream_name) { + rc = smb2_set_stream_name_xattr(&path, + fp, + stream_name, + s_type); + if (rc) + goto err_out; + file_info = FILE_CREATED; + } + + fp->attrib_only = !(req->DesiredAccess & ~(FILE_READ_ATTRIBUTES_LE | + FILE_WRITE_ATTRIBUTES_LE | FILE_SYNCHRONIZE_LE)); + if (!S_ISDIR(file_inode(filp)->i_mode) && open_flags & O_TRUNC && + !fp->attrib_only && !stream_name) { + smb_break_all_oplock(work, fp); + need_truncate = 1; + } + + /* fp should be searchable through ksmbd_inode.m_fp_list + * after daccess, saccess, attrib_only, and stream are + * initialized. + */ + write_lock(&fp->f_ci->m_lock); + list_add(&fp->node, &fp->f_ci->m_fp_list); + write_unlock(&fp->f_ci->m_lock); + + /* Check delete pending among previous fp before oplock break */ + if (ksmbd_inode_pending_delete(fp)) { + rc = -EBUSY; + goto err_out; + } + + share_ret = ksmbd_smb_check_shared_mode(fp->filp, fp); + if (!test_share_config_flag(work->tcon->share_conf, KSMBD_SHARE_FLAG_OPLOCKS) || + (req_op_level == SMB2_OPLOCK_LEVEL_LEASE && + !(conn->vals->capabilities & SMB2_GLOBAL_CAP_LEASING))) { + if (share_ret < 0 && !S_ISDIR(file_inode(fp->filp)->i_mode)) { + rc = share_ret; + goto err_out; + } + } else { + if (req_op_level == SMB2_OPLOCK_LEVEL_LEASE) { + req_op_level = smb2_map_lease_to_oplock(lc->req_state); + ksmbd_debug(SMB, + "lease req for(%s) req oplock state 0x%x, lease state 0x%x\n", + name, req_op_level, lc->req_state); + rc = find_same_lease_key(sess, fp->f_ci, lc); + if (rc) + goto err_out; + } else if (open_flags == O_RDONLY && + (req_op_level == SMB2_OPLOCK_LEVEL_BATCH || + req_op_level == SMB2_OPLOCK_LEVEL_EXCLUSIVE)) + req_op_level = SMB2_OPLOCK_LEVEL_II; + + rc = smb_grant_oplock(work, req_op_level, + fp->persistent_id, fp, + le32_to_cpu(req->hdr.Id.SyncId.TreeId), + lc, share_ret); + if (rc < 0) + goto err_out; + } + + if (req->CreateOptions & FILE_DELETE_ON_CLOSE_LE) + ksmbd_fd_set_delete_on_close(fp, file_info); + + if (need_truncate) { + rc = smb2_create_truncate(&path); + if (rc) + goto err_out; + } + + if (req->CreateContextsOffset) { + struct create_alloc_size_req *az_req; + + az_req = (struct create_alloc_size_req *)smb2_find_context_vals(req, + SMB2_CREATE_ALLOCATION_SIZE, 4); + if (IS_ERR(az_req)) { + rc = PTR_ERR(az_req); + goto err_out; + } else if (az_req) { + loff_t alloc_size; + int err; + + if (le16_to_cpu(az_req->ccontext.DataOffset) + + le32_to_cpu(az_req->ccontext.DataLength) < + sizeof(struct create_alloc_size_req)) { + rc = -EINVAL; + goto err_out; + } + alloc_size = le64_to_cpu(az_req->AllocationSize); + ksmbd_debug(SMB, + "request smb2 create allocate size : %llu\n", + alloc_size); + smb_break_all_levII_oplock(work, fp, 1); + err = vfs_fallocate(fp->filp, FALLOC_FL_KEEP_SIZE, 0, + alloc_size); + if (err < 0) + ksmbd_debug(SMB, + "vfs_fallocate is failed : %d\n", + err); + } + + context = smb2_find_context_vals(req, SMB2_CREATE_QUERY_ON_DISK_ID, 4); + if (IS_ERR(context)) { + rc = PTR_ERR(context); + goto err_out; + } else if (context) { + ksmbd_debug(SMB, "get query on disk id context\n"); + query_disk_id = 1; + } + } + + rc = ksmbd_vfs_getattr(&path, &stat); + if (rc) + goto err_out; + + if (stat.result_mask & STATX_BTIME) + fp->create_time = ksmbd_UnixTimeToNT(stat.btime); + else + fp->create_time = ksmbd_UnixTimeToNT(stat.ctime); + if (req->FileAttributes || fp->f_ci->m_fattr == 0) + fp->f_ci->m_fattr = + cpu_to_le32(smb2_get_dos_mode(&stat, le32_to_cpu(req->FileAttributes))); + + if (!created) + smb2_update_xattrs(tcon, &path, fp); + else + smb2_new_xattrs(tcon, &path, fp); + + memcpy(fp->client_guid, conn->ClientGUID, SMB2_CLIENT_GUID_SIZE); + + rsp->StructureSize = cpu_to_le16(89); + rcu_read_lock(); + opinfo = rcu_dereference(fp->f_opinfo); + rsp->OplockLevel = opinfo != NULL ? opinfo->level : 0; + rcu_read_unlock(); + rsp->Flags = 0; + rsp->CreateAction = cpu_to_le32(file_info); + rsp->CreationTime = cpu_to_le64(fp->create_time); + time = ksmbd_UnixTimeToNT(stat.atime); + rsp->LastAccessTime = cpu_to_le64(time); + time = ksmbd_UnixTimeToNT(stat.mtime); + rsp->LastWriteTime = cpu_to_le64(time); + time = ksmbd_UnixTimeToNT(stat.ctime); + rsp->ChangeTime = cpu_to_le64(time); + rsp->AllocationSize = S_ISDIR(stat.mode) ? 0 : + cpu_to_le64(stat.blocks << 9); + rsp->EndofFile = S_ISDIR(stat.mode) ? 0 : cpu_to_le64(stat.size); + rsp->FileAttributes = fp->f_ci->m_fattr; + + rsp->Reserved2 = 0; + + rsp->PersistentFileId = fp->persistent_id; + rsp->VolatileFileId = fp->volatile_id; + + rsp->CreateContextsOffset = 0; + rsp->CreateContextsLength = 0; + inc_rfc1001_len(work->response_buf, 88); /* StructureSize - 1*/ + + /* If lease is request send lease context response */ + if (opinfo && opinfo->is_lease) { + struct create_context *lease_ccontext; + + ksmbd_debug(SMB, "lease granted on(%s) lease state 0x%x\n", + name, opinfo->o_lease->state); + rsp->OplockLevel = SMB2_OPLOCK_LEVEL_LEASE; + + lease_ccontext = (struct create_context *)rsp->Buffer; + contxt_cnt++; + create_lease_buf(rsp->Buffer, opinfo->o_lease); + le32_add_cpu(&rsp->CreateContextsLength, + conn->vals->create_lease_size); + inc_rfc1001_len(work->response_buf, + conn->vals->create_lease_size); + next_ptr = &lease_ccontext->Next; + next_off = conn->vals->create_lease_size; + } + + if (maximal_access_ctxt) { + struct create_context *mxac_ccontext; + + if (maximal_access == 0) + ksmbd_vfs_query_maximal_access(user_ns, + path.dentry, + &maximal_access); + mxac_ccontext = (struct create_context *)(rsp->Buffer + + le32_to_cpu(rsp->CreateContextsLength)); + contxt_cnt++; + create_mxac_rsp_buf(rsp->Buffer + + le32_to_cpu(rsp->CreateContextsLength), + le32_to_cpu(maximal_access)); + le32_add_cpu(&rsp->CreateContextsLength, + conn->vals->create_mxac_size); + inc_rfc1001_len(work->response_buf, + conn->vals->create_mxac_size); + if (next_ptr) + *next_ptr = cpu_to_le32(next_off); + next_ptr = &mxac_ccontext->Next; + next_off = conn->vals->create_mxac_size; + } + + if (query_disk_id) { + struct create_context *disk_id_ccontext; + + disk_id_ccontext = (struct create_context *)(rsp->Buffer + + le32_to_cpu(rsp->CreateContextsLength)); + contxt_cnt++; + create_disk_id_rsp_buf(rsp->Buffer + + le32_to_cpu(rsp->CreateContextsLength), + stat.ino, tcon->id); + le32_add_cpu(&rsp->CreateContextsLength, + conn->vals->create_disk_id_size); + inc_rfc1001_len(work->response_buf, + conn->vals->create_disk_id_size); + if (next_ptr) + *next_ptr = cpu_to_le32(next_off); + next_ptr = &disk_id_ccontext->Next; + next_off = conn->vals->create_disk_id_size; + } + + if (posix_ctxt) { + contxt_cnt++; + create_posix_rsp_buf(rsp->Buffer + + le32_to_cpu(rsp->CreateContextsLength), + fp); + le32_add_cpu(&rsp->CreateContextsLength, + conn->vals->create_posix_size); + inc_rfc1001_len(work->response_buf, + conn->vals->create_posix_size); + if (next_ptr) + *next_ptr = cpu_to_le32(next_off); + } + + if (contxt_cnt > 0) { + rsp->CreateContextsOffset = + cpu_to_le32(offsetof(struct smb2_create_rsp, Buffer)); + } + +err_out: + if (file_present || created) + path_put(&path); + ksmbd_revert_fsids(work); +err_out1: + if (rc) { + if (rc == -EINVAL) + rsp->hdr.Status = STATUS_INVALID_PARAMETER; + else if (rc == -EOPNOTSUPP) + rsp->hdr.Status = STATUS_NOT_SUPPORTED; + else if (rc == -EACCES || rc == -ESTALE || rc == -EXDEV) + rsp->hdr.Status = STATUS_ACCESS_DENIED; + else if (rc == -ENOENT) + rsp->hdr.Status = STATUS_OBJECT_NAME_INVALID; + else if (rc == -EPERM) + rsp->hdr.Status = STATUS_SHARING_VIOLATION; + else if (rc == -EBUSY) + rsp->hdr.Status = STATUS_DELETE_PENDING; + else if (rc == -EBADF) + rsp->hdr.Status = STATUS_OBJECT_NAME_NOT_FOUND; + else if (rc == -ENOEXEC) + rsp->hdr.Status = STATUS_DUPLICATE_OBJECTID; + else if (rc == -ENXIO) + rsp->hdr.Status = STATUS_NO_SUCH_DEVICE; + else if (rc == -EEXIST) + rsp->hdr.Status = STATUS_OBJECT_NAME_COLLISION; + else if (rc == -EMFILE) + rsp->hdr.Status = STATUS_INSUFFICIENT_RESOURCES; + if (!rsp->hdr.Status) + rsp->hdr.Status = STATUS_UNEXPECTED_IO_ERROR; + + if (fp) + ksmbd_fd_put(work, fp); + smb2_set_err_rsp(work); + ksmbd_debug(SMB, "Error response: %x\n", rsp->hdr.Status); + } + + kfree(name); + kfree(lc); + + return 0; +} + +static int readdir_info_level_struct_sz(int info_level) +{ + switch (info_level) { + case FILE_FULL_DIRECTORY_INFORMATION: + return sizeof(struct file_full_directory_info); + case FILE_BOTH_DIRECTORY_INFORMATION: + return sizeof(struct file_both_directory_info); + case FILE_DIRECTORY_INFORMATION: + return sizeof(struct file_directory_info); + case FILE_NAMES_INFORMATION: + return sizeof(struct file_names_info); + case FILEID_FULL_DIRECTORY_INFORMATION: + return sizeof(struct file_id_full_dir_info); + case FILEID_BOTH_DIRECTORY_INFORMATION: + return sizeof(struct file_id_both_directory_info); + case SMB_FIND_FILE_POSIX_INFO: + return sizeof(struct smb2_posix_info); + default: + return -EOPNOTSUPP; + } +} + +static int dentry_name(struct ksmbd_dir_info *d_info, int info_level) +{ + switch (info_level) { + case FILE_FULL_DIRECTORY_INFORMATION: + { + struct file_full_directory_info *ffdinfo; + + ffdinfo = (struct file_full_directory_info *)d_info->rptr; + d_info->rptr += le32_to_cpu(ffdinfo->NextEntryOffset); + d_info->name = ffdinfo->FileName; + d_info->name_len = le32_to_cpu(ffdinfo->FileNameLength); + return 0; + } + case FILE_BOTH_DIRECTORY_INFORMATION: + { + struct file_both_directory_info *fbdinfo; + + fbdinfo = (struct file_both_directory_info *)d_info->rptr; + d_info->rptr += le32_to_cpu(fbdinfo->NextEntryOffset); + d_info->name = fbdinfo->FileName; + d_info->name_len = le32_to_cpu(fbdinfo->FileNameLength); + return 0; + } + case FILE_DIRECTORY_INFORMATION: + { + struct file_directory_info *fdinfo; + + fdinfo = (struct file_directory_info *)d_info->rptr; + d_info->rptr += le32_to_cpu(fdinfo->NextEntryOffset); + d_info->name = fdinfo->FileName; + d_info->name_len = le32_to_cpu(fdinfo->FileNameLength); + return 0; + } + case FILE_NAMES_INFORMATION: + { + struct file_names_info *fninfo; + + fninfo = (struct file_names_info *)d_info->rptr; + d_info->rptr += le32_to_cpu(fninfo->NextEntryOffset); + d_info->name = fninfo->FileName; + d_info->name_len = le32_to_cpu(fninfo->FileNameLength); + return 0; + } + case FILEID_FULL_DIRECTORY_INFORMATION: + { + struct file_id_full_dir_info *dinfo; + + dinfo = (struct file_id_full_dir_info *)d_info->rptr; + d_info->rptr += le32_to_cpu(dinfo->NextEntryOffset); + d_info->name = dinfo->FileName; + d_info->name_len = le32_to_cpu(dinfo->FileNameLength); + return 0; + } + case FILEID_BOTH_DIRECTORY_INFORMATION: + { + struct file_id_both_directory_info *fibdinfo; + + fibdinfo = (struct file_id_both_directory_info *)d_info->rptr; + d_info->rptr += le32_to_cpu(fibdinfo->NextEntryOffset); + d_info->name = fibdinfo->FileName; + d_info->name_len = le32_to_cpu(fibdinfo->FileNameLength); + return 0; + } + case SMB_FIND_FILE_POSIX_INFO: + { + struct smb2_posix_info *posix_info; + + posix_info = (struct smb2_posix_info *)d_info->rptr; + d_info->rptr += le32_to_cpu(posix_info->NextEntryOffset); + d_info->name = posix_info->name; + d_info->name_len = le32_to_cpu(posix_info->name_len); + return 0; + } + default: + return -EINVAL; + } +} + +/** + * smb2_populate_readdir_entry() - encode directory entry in smb2 response + * buffer + * @conn: connection instance + * @info_level: smb information level + * @d_info: structure included variables for query dir + * @ksmbd_kstat: ksmbd wrapper of dirent stat information + * + * if directory has many entries, find first can't read it fully. + * find next might be called multiple times to read remaining dir entries + * + * Return: 0 on success, otherwise error + */ +static int smb2_populate_readdir_entry(struct ksmbd_conn *conn, int info_level, + struct ksmbd_dir_info *d_info, + struct ksmbd_kstat *ksmbd_kstat) +{ + int next_entry_offset = 0; + char *conv_name; + int conv_len; + void *kstat; + int struct_sz, rc = 0; + + conv_name = ksmbd_convert_dir_info_name(d_info, + conn->local_nls, + &conv_len); + if (!conv_name) + return -ENOMEM; + + /* Somehow the name has only terminating NULL bytes */ + if (conv_len < 0) { + rc = -EINVAL; + goto free_conv_name; + } + + struct_sz = readdir_info_level_struct_sz(info_level) - 1 + conv_len; + next_entry_offset = ALIGN(struct_sz, KSMBD_DIR_INFO_ALIGNMENT); + d_info->last_entry_off_align = next_entry_offset - struct_sz; + + if (next_entry_offset > d_info->out_buf_len) { + d_info->out_buf_len = 0; + rc = -ENOSPC; + goto free_conv_name; + } + + kstat = d_info->wptr; + if (info_level != FILE_NAMES_INFORMATION) + kstat = ksmbd_vfs_init_kstat(&d_info->wptr, ksmbd_kstat); + + switch (info_level) { + case FILE_FULL_DIRECTORY_INFORMATION: + { + struct file_full_directory_info *ffdinfo; + + ffdinfo = (struct file_full_directory_info *)kstat; + ffdinfo->FileNameLength = cpu_to_le32(conv_len); + ffdinfo->EaSize = + smb2_get_reparse_tag_special_file(ksmbd_kstat->kstat->mode); + if (ffdinfo->EaSize) + ffdinfo->ExtFileAttributes = FILE_ATTRIBUTE_REPARSE_POINT_LE; + if (d_info->hide_dot_file && d_info->name[0] == '.') + ffdinfo->ExtFileAttributes |= FILE_ATTRIBUTE_HIDDEN_LE; + memcpy(ffdinfo->FileName, conv_name, conv_len); + ffdinfo->NextEntryOffset = cpu_to_le32(next_entry_offset); + break; + } + case FILE_BOTH_DIRECTORY_INFORMATION: + { + struct file_both_directory_info *fbdinfo; + + fbdinfo = (struct file_both_directory_info *)kstat; + fbdinfo->FileNameLength = cpu_to_le32(conv_len); + fbdinfo->EaSize = + smb2_get_reparse_tag_special_file(ksmbd_kstat->kstat->mode); + if (fbdinfo->EaSize) + fbdinfo->ExtFileAttributes = FILE_ATTRIBUTE_REPARSE_POINT_LE; + fbdinfo->ShortNameLength = 0; + fbdinfo->Reserved = 0; + if (d_info->hide_dot_file && d_info->name[0] == '.') + fbdinfo->ExtFileAttributes |= FILE_ATTRIBUTE_HIDDEN_LE; + memcpy(fbdinfo->FileName, conv_name, conv_len); + fbdinfo->NextEntryOffset = cpu_to_le32(next_entry_offset); + break; + } + case FILE_DIRECTORY_INFORMATION: + { + struct file_directory_info *fdinfo; + + fdinfo = (struct file_directory_info *)kstat; + fdinfo->FileNameLength = cpu_to_le32(conv_len); + if (d_info->hide_dot_file && d_info->name[0] == '.') + fdinfo->ExtFileAttributes |= FILE_ATTRIBUTE_HIDDEN_LE; + memcpy(fdinfo->FileName, conv_name, conv_len); + fdinfo->NextEntryOffset = cpu_to_le32(next_entry_offset); + break; + } + case FILE_NAMES_INFORMATION: + { + struct file_names_info *fninfo; + + fninfo = (struct file_names_info *)kstat; + fninfo->FileNameLength = cpu_to_le32(conv_len); + memcpy(fninfo->FileName, conv_name, conv_len); + fninfo->NextEntryOffset = cpu_to_le32(next_entry_offset); + break; + } + case FILEID_FULL_DIRECTORY_INFORMATION: + { + struct file_id_full_dir_info *dinfo; + + dinfo = (struct file_id_full_dir_info *)kstat; + dinfo->FileNameLength = cpu_to_le32(conv_len); + dinfo->EaSize = + smb2_get_reparse_tag_special_file(ksmbd_kstat->kstat->mode); + if (dinfo->EaSize) + dinfo->ExtFileAttributes = FILE_ATTRIBUTE_REPARSE_POINT_LE; + dinfo->Reserved = 0; + dinfo->UniqueId = cpu_to_le64(ksmbd_kstat->kstat->ino); + if (d_info->hide_dot_file && d_info->name[0] == '.') + dinfo->ExtFileAttributes |= FILE_ATTRIBUTE_HIDDEN_LE; + memcpy(dinfo->FileName, conv_name, conv_len); + dinfo->NextEntryOffset = cpu_to_le32(next_entry_offset); + break; + } + case FILEID_BOTH_DIRECTORY_INFORMATION: + { + struct file_id_both_directory_info *fibdinfo; + + fibdinfo = (struct file_id_both_directory_info *)kstat; + fibdinfo->FileNameLength = cpu_to_le32(conv_len); + fibdinfo->EaSize = + smb2_get_reparse_tag_special_file(ksmbd_kstat->kstat->mode); + if (fibdinfo->EaSize) + fibdinfo->ExtFileAttributes = FILE_ATTRIBUTE_REPARSE_POINT_LE; + fibdinfo->UniqueId = cpu_to_le64(ksmbd_kstat->kstat->ino); + fibdinfo->ShortNameLength = 0; + fibdinfo->Reserved = 0; + fibdinfo->Reserved2 = cpu_to_le16(0); + if (d_info->hide_dot_file && d_info->name[0] == '.') + fibdinfo->ExtFileAttributes |= FILE_ATTRIBUTE_HIDDEN_LE; + memcpy(fibdinfo->FileName, conv_name, conv_len); + fibdinfo->NextEntryOffset = cpu_to_le32(next_entry_offset); + break; + } + case SMB_FIND_FILE_POSIX_INFO: + { + struct smb2_posix_info *posix_info; + u64 time; + + posix_info = (struct smb2_posix_info *)kstat; + posix_info->Ignored = 0; + posix_info->CreationTime = cpu_to_le64(ksmbd_kstat->create_time); + time = ksmbd_UnixTimeToNT(ksmbd_kstat->kstat->ctime); + posix_info->ChangeTime = cpu_to_le64(time); + time = ksmbd_UnixTimeToNT(ksmbd_kstat->kstat->atime); + posix_info->LastAccessTime = cpu_to_le64(time); + time = ksmbd_UnixTimeToNT(ksmbd_kstat->kstat->mtime); + posix_info->LastWriteTime = cpu_to_le64(time); + posix_info->EndOfFile = cpu_to_le64(ksmbd_kstat->kstat->size); + posix_info->AllocationSize = cpu_to_le64(ksmbd_kstat->kstat->blocks << 9); + posix_info->DeviceId = cpu_to_le32(ksmbd_kstat->kstat->rdev); + posix_info->HardLinks = cpu_to_le32(ksmbd_kstat->kstat->nlink); + posix_info->Mode = cpu_to_le32(ksmbd_kstat->kstat->mode & 0777); + posix_info->Inode = cpu_to_le64(ksmbd_kstat->kstat->ino); + posix_info->DosAttributes = + S_ISDIR(ksmbd_kstat->kstat->mode) ? + FILE_ATTRIBUTE_DIRECTORY_LE : FILE_ATTRIBUTE_ARCHIVE_LE; + if (d_info->hide_dot_file && d_info->name[0] == '.') + posix_info->DosAttributes |= FILE_ATTRIBUTE_HIDDEN_LE; + /* + * SidBuffer(32) contain two sids(Domain sid(16), UNIX group sid(16)). + * UNIX sid(16) = revision(1) + num_subauth(1) + authority(6) + + * sub_auth(4 * 1(num_subauth)) + RID(4). + */ + id_to_sid(from_kuid_munged(&init_user_ns, ksmbd_kstat->kstat->uid), + SIDUNIX_USER, (struct smb_sid *)&posix_info->SidBuffer[0]); + id_to_sid(from_kgid_munged(&init_user_ns, ksmbd_kstat->kstat->gid), + SIDUNIX_GROUP, (struct smb_sid *)&posix_info->SidBuffer[16]); + memcpy(posix_info->name, conv_name, conv_len); + posix_info->name_len = cpu_to_le32(conv_len); + posix_info->NextEntryOffset = cpu_to_le32(next_entry_offset); + break; + } + + } /* switch (info_level) */ + + d_info->last_entry_offset = d_info->data_count; + d_info->data_count += next_entry_offset; + d_info->out_buf_len -= next_entry_offset; + d_info->wptr += next_entry_offset; + + ksmbd_debug(SMB, + "info_level : %d, buf_len :%d, next_offset : %d, data_count : %d\n", + info_level, d_info->out_buf_len, + next_entry_offset, d_info->data_count); + +free_conv_name: + kfree(conv_name); + return rc; +} + +struct smb2_query_dir_private { + struct ksmbd_work *work; + char *search_pattern; + struct ksmbd_file *dir_fp; + + struct ksmbd_dir_info *d_info; + int info_level; +}; + +static void lock_dir(struct ksmbd_file *dir_fp) +{ + struct dentry *dir = dir_fp->filp->f_path.dentry; + + inode_lock_nested(d_inode(dir), I_MUTEX_PARENT); +} + +static void unlock_dir(struct ksmbd_file *dir_fp) +{ + struct dentry *dir = dir_fp->filp->f_path.dentry; + + inode_unlock(d_inode(dir)); +} + +static int process_query_dir_entries(struct smb2_query_dir_private *priv) +{ + struct user_namespace *user_ns = file_mnt_user_ns(priv->dir_fp->filp); + struct kstat kstat; + struct ksmbd_kstat ksmbd_kstat; + int rc; + int i; + + for (i = 0; i < priv->d_info->num_entry; i++) { + struct dentry *dent; + + if (dentry_name(priv->d_info, priv->info_level)) + return -EINVAL; + + lock_dir(priv->dir_fp); + dent = lookup_one(user_ns, priv->d_info->name, + priv->dir_fp->filp->f_path.dentry, + priv->d_info->name_len); + unlock_dir(priv->dir_fp); + + if (IS_ERR(dent)) { + ksmbd_debug(SMB, "Cannot lookup `%s' [%ld]\n", + priv->d_info->name, + PTR_ERR(dent)); + continue; + } + if (unlikely(d_is_negative(dent))) { + dput(dent); + ksmbd_debug(SMB, "Negative dentry `%s'\n", + priv->d_info->name); + continue; + } + + ksmbd_kstat.kstat = &kstat; + if (priv->info_level != FILE_NAMES_INFORMATION) + ksmbd_vfs_fill_dentry_attrs(priv->work, + user_ns, + dent, + &ksmbd_kstat); + + rc = smb2_populate_readdir_entry(priv->work->conn, + priv->info_level, + priv->d_info, + &ksmbd_kstat); + dput(dent); + if (rc) + return rc; + } + return 0; +} + +static int reserve_populate_dentry(struct ksmbd_dir_info *d_info, + int info_level) +{ + int struct_sz; + int conv_len; + int next_entry_offset; + + struct_sz = readdir_info_level_struct_sz(info_level); + if (struct_sz == -EOPNOTSUPP) + return -EOPNOTSUPP; + + conv_len = (d_info->name_len + 1) * 2; + next_entry_offset = ALIGN(struct_sz - 1 + conv_len, + KSMBD_DIR_INFO_ALIGNMENT); + + if (next_entry_offset > d_info->out_buf_len) { + d_info->out_buf_len = 0; + return -ENOSPC; + } + + switch (info_level) { + case FILE_FULL_DIRECTORY_INFORMATION: + { + struct file_full_directory_info *ffdinfo; + + ffdinfo = (struct file_full_directory_info *)d_info->wptr; + memcpy(ffdinfo->FileName, d_info->name, d_info->name_len); + ffdinfo->FileName[d_info->name_len] = 0x00; + ffdinfo->FileNameLength = cpu_to_le32(d_info->name_len); + ffdinfo->NextEntryOffset = cpu_to_le32(next_entry_offset); + break; + } + case FILE_BOTH_DIRECTORY_INFORMATION: + { + struct file_both_directory_info *fbdinfo; + + fbdinfo = (struct file_both_directory_info *)d_info->wptr; + memcpy(fbdinfo->FileName, d_info->name, d_info->name_len); + fbdinfo->FileName[d_info->name_len] = 0x00; + fbdinfo->FileNameLength = cpu_to_le32(d_info->name_len); + fbdinfo->NextEntryOffset = cpu_to_le32(next_entry_offset); + break; + } + case FILE_DIRECTORY_INFORMATION: + { + struct file_directory_info *fdinfo; + + fdinfo = (struct file_directory_info *)d_info->wptr; + memcpy(fdinfo->FileName, d_info->name, d_info->name_len); + fdinfo->FileName[d_info->name_len] = 0x00; + fdinfo->FileNameLength = cpu_to_le32(d_info->name_len); + fdinfo->NextEntryOffset = cpu_to_le32(next_entry_offset); + break; + } + case FILE_NAMES_INFORMATION: + { + struct file_names_info *fninfo; + + fninfo = (struct file_names_info *)d_info->wptr; + memcpy(fninfo->FileName, d_info->name, d_info->name_len); + fninfo->FileName[d_info->name_len] = 0x00; + fninfo->FileNameLength = cpu_to_le32(d_info->name_len); + fninfo->NextEntryOffset = cpu_to_le32(next_entry_offset); + break; + } + case FILEID_FULL_DIRECTORY_INFORMATION: + { + struct file_id_full_dir_info *dinfo; + + dinfo = (struct file_id_full_dir_info *)d_info->wptr; + memcpy(dinfo->FileName, d_info->name, d_info->name_len); + dinfo->FileName[d_info->name_len] = 0x00; + dinfo->FileNameLength = cpu_to_le32(d_info->name_len); + dinfo->NextEntryOffset = cpu_to_le32(next_entry_offset); + break; + } + case FILEID_BOTH_DIRECTORY_INFORMATION: + { + struct file_id_both_directory_info *fibdinfo; + + fibdinfo = (struct file_id_both_directory_info *)d_info->wptr; + memcpy(fibdinfo->FileName, d_info->name, d_info->name_len); + fibdinfo->FileName[d_info->name_len] = 0x00; + fibdinfo->FileNameLength = cpu_to_le32(d_info->name_len); + fibdinfo->NextEntryOffset = cpu_to_le32(next_entry_offset); + break; + } + case SMB_FIND_FILE_POSIX_INFO: + { + struct smb2_posix_info *posix_info; + + posix_info = (struct smb2_posix_info *)d_info->wptr; + memcpy(posix_info->name, d_info->name, d_info->name_len); + posix_info->name[d_info->name_len] = 0x00; + posix_info->name_len = cpu_to_le32(d_info->name_len); + posix_info->NextEntryOffset = + cpu_to_le32(next_entry_offset); + break; + } + } /* switch (info_level) */ + + d_info->num_entry++; + d_info->out_buf_len -= next_entry_offset; + d_info->wptr += next_entry_offset; + return 0; +} + +static bool __query_dir(struct dir_context *ctx, const char *name, int namlen, + loff_t offset, u64 ino, unsigned int d_type) +{ + struct ksmbd_readdir_data *buf; + struct smb2_query_dir_private *priv; + struct ksmbd_dir_info *d_info; + int rc; + + buf = container_of(ctx, struct ksmbd_readdir_data, ctx); + priv = buf->private; + d_info = priv->d_info; + + /* dot and dotdot entries are already reserved */ + if (!strcmp(".", name) || !strcmp("..", name)) + return true; + if (ksmbd_share_veto_filename(priv->work->tcon->share_conf, name)) + return true; + if (!match_pattern(name, namlen, priv->search_pattern)) + return true; + + d_info->name = name; + d_info->name_len = namlen; + rc = reserve_populate_dentry(d_info, priv->info_level); + if (rc) + return false; + if (d_info->flags & SMB2_RETURN_SINGLE_ENTRY) + d_info->out_buf_len = 0; + return true; +} + +static int verify_info_level(int info_level) +{ + switch (info_level) { + case FILE_FULL_DIRECTORY_INFORMATION: + case FILE_BOTH_DIRECTORY_INFORMATION: + case FILE_DIRECTORY_INFORMATION: + case FILE_NAMES_INFORMATION: + case FILEID_FULL_DIRECTORY_INFORMATION: + case FILEID_BOTH_DIRECTORY_INFORMATION: + case SMB_FIND_FILE_POSIX_INFO: + break; + default: + return -EOPNOTSUPP; + } + + return 0; +} + +static int smb2_resp_buf_len(struct ksmbd_work *work, unsigned short hdr2_len) +{ + int free_len; + + free_len = (int)(work->response_sz - + (get_rfc1002_len(work->response_buf) + 4)) - hdr2_len; + return free_len; +} + +static int smb2_calc_max_out_buf_len(struct ksmbd_work *work, + unsigned short hdr2_len, + unsigned int out_buf_len) +{ + int free_len; + + if (out_buf_len > work->conn->vals->max_trans_size) + return -EINVAL; + + free_len = smb2_resp_buf_len(work, hdr2_len); + if (free_len < 0) + return -EINVAL; + + return min_t(int, out_buf_len, free_len); +} + +int smb2_query_dir(struct ksmbd_work *work) +{ + struct ksmbd_conn *conn = work->conn; + struct smb2_query_directory_req *req; + struct smb2_query_directory_rsp *rsp; + struct ksmbd_share_config *share = work->tcon->share_conf; + struct ksmbd_file *dir_fp = NULL; + struct ksmbd_dir_info d_info; + int rc = 0; + char *srch_ptr = NULL; + unsigned char srch_flag; + int buffer_sz; + struct smb2_query_dir_private query_dir_private = {NULL, }; + + WORK_BUFFERS(work, req, rsp); + + if (ksmbd_override_fsids(work)) { + rsp->hdr.Status = STATUS_NO_MEMORY; + smb2_set_err_rsp(work); + return -ENOMEM; + } + + rc = verify_info_level(req->FileInformationClass); + if (rc) { + rc = -EFAULT; + goto err_out2; + } + + dir_fp = ksmbd_lookup_fd_slow(work, req->VolatileFileId, req->PersistentFileId); + if (!dir_fp) { + rc = -EBADF; + goto err_out2; + } + + if (!(dir_fp->daccess & FILE_LIST_DIRECTORY_LE) || + inode_permission(file_mnt_user_ns(dir_fp->filp), + file_inode(dir_fp->filp), + MAY_READ | MAY_EXEC)) { + pr_err("no right to enumerate directory (%pD)\n", dir_fp->filp); + rc = -EACCES; + goto err_out2; + } + + if (!S_ISDIR(file_inode(dir_fp->filp)->i_mode)) { + pr_err("can't do query dir for a file\n"); + rc = -EINVAL; + goto err_out2; + } + + srch_flag = req->Flags; + srch_ptr = smb_strndup_from_utf16(req->Buffer, + le16_to_cpu(req->FileNameLength), 1, + conn->local_nls); + if (IS_ERR(srch_ptr)) { + ksmbd_debug(SMB, "Search Pattern not found\n"); + rc = -EINVAL; + goto err_out2; + } else { + ksmbd_debug(SMB, "Search pattern is %s\n", srch_ptr); + } + + if (srch_flag & SMB2_REOPEN || srch_flag & SMB2_RESTART_SCANS) { + ksmbd_debug(SMB, "Restart directory scan\n"); + generic_file_llseek(dir_fp->filp, 0, SEEK_SET); + } + + memset(&d_info, 0, sizeof(struct ksmbd_dir_info)); + d_info.wptr = (char *)rsp->Buffer; + d_info.rptr = (char *)rsp->Buffer; + d_info.out_buf_len = + smb2_calc_max_out_buf_len(work, 8, + le32_to_cpu(req->OutputBufferLength)); + if (d_info.out_buf_len < 0) { + rc = -EINVAL; + goto err_out; + } + d_info.flags = srch_flag; + + /* + * reserve dot and dotdot entries in head of buffer + * in first response + */ + rc = ksmbd_populate_dot_dotdot_entries(work, req->FileInformationClass, + dir_fp, &d_info, srch_ptr, + smb2_populate_readdir_entry); + if (rc == -ENOSPC) + rc = 0; + else if (rc) + goto err_out; + + if (test_share_config_flag(share, KSMBD_SHARE_FLAG_HIDE_DOT_FILES)) + d_info.hide_dot_file = true; + + buffer_sz = d_info.out_buf_len; + d_info.rptr = d_info.wptr; + query_dir_private.work = work; + query_dir_private.search_pattern = srch_ptr; + query_dir_private.dir_fp = dir_fp; + query_dir_private.d_info = &d_info; + query_dir_private.info_level = req->FileInformationClass; + dir_fp->readdir_data.private = &query_dir_private; + set_ctx_actor(&dir_fp->readdir_data.ctx, __query_dir); + + rc = iterate_dir(dir_fp->filp, &dir_fp->readdir_data.ctx); + /* + * req->OutputBufferLength is too small to contain even one entry. + * In this case, it immediately returns OutputBufferLength 0 to client. + */ + if (!d_info.out_buf_len && !d_info.num_entry) + goto no_buf_len; + if (rc > 0 || rc == -ENOSPC) + rc = 0; + else if (rc) + goto err_out; + + d_info.wptr = d_info.rptr; + d_info.out_buf_len = buffer_sz; + rc = process_query_dir_entries(&query_dir_private); + if (rc) + goto err_out; + + if (!d_info.data_count && d_info.out_buf_len >= 0) { + if (srch_flag & SMB2_RETURN_SINGLE_ENTRY && !is_asterisk(srch_ptr)) { + rsp->hdr.Status = STATUS_NO_SUCH_FILE; + } else { + dir_fp->dot_dotdot[0] = dir_fp->dot_dotdot[1] = 0; + rsp->hdr.Status = STATUS_NO_MORE_FILES; + } + rsp->StructureSize = cpu_to_le16(9); + rsp->OutputBufferOffset = cpu_to_le16(0); + rsp->OutputBufferLength = cpu_to_le32(0); + rsp->Buffer[0] = 0; + inc_rfc1001_len(work->response_buf, 9); + } else { +no_buf_len: + ((struct file_directory_info *) + ((char *)rsp->Buffer + d_info.last_entry_offset)) + ->NextEntryOffset = 0; + if (d_info.data_count >= d_info.last_entry_off_align) + d_info.data_count -= d_info.last_entry_off_align; + + rsp->StructureSize = cpu_to_le16(9); + rsp->OutputBufferOffset = cpu_to_le16(72); + rsp->OutputBufferLength = cpu_to_le32(d_info.data_count); + inc_rfc1001_len(work->response_buf, 8 + d_info.data_count); + } + + kfree(srch_ptr); + ksmbd_fd_put(work, dir_fp); + ksmbd_revert_fsids(work); + return 0; + +err_out: + pr_err("error while processing smb2 query dir rc = %d\n", rc); + kfree(srch_ptr); + +err_out2: + if (rc == -EINVAL) + rsp->hdr.Status = STATUS_INVALID_PARAMETER; + else if (rc == -EACCES) + rsp->hdr.Status = STATUS_ACCESS_DENIED; + else if (rc == -ENOENT) + rsp->hdr.Status = STATUS_NO_SUCH_FILE; + else if (rc == -EBADF) + rsp->hdr.Status = STATUS_FILE_CLOSED; + else if (rc == -ENOMEM) + rsp->hdr.Status = STATUS_NO_MEMORY; + else if (rc == -EFAULT) + rsp->hdr.Status = STATUS_INVALID_INFO_CLASS; + else if (rc == -EIO) + rsp->hdr.Status = STATUS_FILE_CORRUPT_ERROR; + if (!rsp->hdr.Status) + rsp->hdr.Status = STATUS_UNEXPECTED_IO_ERROR; + + smb2_set_err_rsp(work); + ksmbd_fd_put(work, dir_fp); + ksmbd_revert_fsids(work); + return 0; +} + +/** + * buffer_check_err() - helper function to check buffer errors + * @reqOutputBufferLength: max buffer length expected in command response + * @rsp: query info response buffer contains output buffer length + * @rsp_org: base response buffer pointer in case of chained response + * @infoclass_size: query info class response buffer size + * + * Return: 0 on success, otherwise error + */ +static int buffer_check_err(int reqOutputBufferLength, + struct smb2_query_info_rsp *rsp, + void *rsp_org, int infoclass_size) +{ + if (reqOutputBufferLength < le32_to_cpu(rsp->OutputBufferLength)) { + if (reqOutputBufferLength < infoclass_size) { + pr_err("Invalid Buffer Size Requested\n"); + rsp->hdr.Status = STATUS_INFO_LENGTH_MISMATCH; + *(__be32 *)rsp_org = cpu_to_be32(sizeof(struct smb2_hdr)); + return -EINVAL; + } + + ksmbd_debug(SMB, "Buffer Overflow\n"); + rsp->hdr.Status = STATUS_BUFFER_OVERFLOW; + *(__be32 *)rsp_org = cpu_to_be32(sizeof(struct smb2_hdr) + + reqOutputBufferLength); + rsp->OutputBufferLength = cpu_to_le32(reqOutputBufferLength); + } + return 0; +} + +static void get_standard_info_pipe(struct smb2_query_info_rsp *rsp, + void *rsp_org) +{ + struct smb2_file_standard_info *sinfo; + + sinfo = (struct smb2_file_standard_info *)rsp->Buffer; + + sinfo->AllocationSize = cpu_to_le64(4096); + sinfo->EndOfFile = cpu_to_le64(0); + sinfo->NumberOfLinks = cpu_to_le32(1); + sinfo->DeletePending = 1; + sinfo->Directory = 0; + rsp->OutputBufferLength = + cpu_to_le32(sizeof(struct smb2_file_standard_info)); + inc_rfc1001_len(rsp_org, sizeof(struct smb2_file_standard_info)); +} + +static void get_internal_info_pipe(struct smb2_query_info_rsp *rsp, u64 num, + void *rsp_org) +{ + struct smb2_file_internal_info *file_info; + + file_info = (struct smb2_file_internal_info *)rsp->Buffer; + + /* any unique number */ + file_info->IndexNumber = cpu_to_le64(num | (1ULL << 63)); + rsp->OutputBufferLength = + cpu_to_le32(sizeof(struct smb2_file_internal_info)); + inc_rfc1001_len(rsp_org, sizeof(struct smb2_file_internal_info)); +} + +static int smb2_get_info_file_pipe(struct ksmbd_session *sess, + struct smb2_query_info_req *req, + struct smb2_query_info_rsp *rsp, + void *rsp_org) +{ + u64 id; + int rc; + + /* + * Windows can sometime send query file info request on + * pipe without opening it, checking error condition here + */ + id = req->VolatileFileId; + if (!ksmbd_session_rpc_method(sess, id)) + return -ENOENT; + + ksmbd_debug(SMB, "FileInfoClass %u, FileId 0x%llx\n", + req->FileInfoClass, req->VolatileFileId); + + switch (req->FileInfoClass) { + case FILE_STANDARD_INFORMATION: + get_standard_info_pipe(rsp, rsp_org); + rc = buffer_check_err(le32_to_cpu(req->OutputBufferLength), + rsp, rsp_org, + FILE_STANDARD_INFORMATION_SIZE); + break; + case FILE_INTERNAL_INFORMATION: + get_internal_info_pipe(rsp, id, rsp_org); + rc = buffer_check_err(le32_to_cpu(req->OutputBufferLength), + rsp, rsp_org, + FILE_INTERNAL_INFORMATION_SIZE); + break; + default: + ksmbd_debug(SMB, "smb2_info_file_pipe for %u not supported\n", + req->FileInfoClass); + rc = -EOPNOTSUPP; + } + return rc; +} + +/** + * smb2_get_ea() - handler for smb2 get extended attribute command + * @work: smb work containing query info command buffer + * @fp: ksmbd_file pointer + * @req: get extended attribute request + * @rsp: response buffer pointer + * @rsp_org: base response buffer pointer in case of chained response + * + * Return: 0 on success, otherwise error + */ +static int smb2_get_ea(struct ksmbd_work *work, struct ksmbd_file *fp, + struct smb2_query_info_req *req, + struct smb2_query_info_rsp *rsp, void *rsp_org) +{ + struct smb2_ea_info *eainfo, *prev_eainfo; + char *name, *ptr, *xattr_list = NULL, *buf; + int rc, name_len, value_len, xattr_list_len, idx; + ssize_t buf_free_len, alignment_bytes, next_offset, rsp_data_cnt = 0; + struct smb2_ea_info_req *ea_req = NULL; + const struct path *path; + struct user_namespace *user_ns = file_mnt_user_ns(fp->filp); + + if (!(fp->daccess & FILE_READ_EA_LE)) { + pr_err("Not permitted to read ext attr : 0x%x\n", + fp->daccess); + return -EACCES; + } + + path = &fp->filp->f_path; + /* single EA entry is requested with given user.* name */ + if (req->InputBufferLength) { + if (le32_to_cpu(req->InputBufferLength) < + sizeof(struct smb2_ea_info_req)) + return -EINVAL; + + ea_req = (struct smb2_ea_info_req *)req->Buffer; + } else { + /* need to send all EAs, if no specific EA is requested*/ + if (le32_to_cpu(req->Flags) & SL_RETURN_SINGLE_ENTRY) + ksmbd_debug(SMB, + "All EAs are requested but need to send single EA entry in rsp flags 0x%x\n", + le32_to_cpu(req->Flags)); + } + + buf_free_len = + smb2_calc_max_out_buf_len(work, 8, + le32_to_cpu(req->OutputBufferLength)); + if (buf_free_len < 0) + return -EINVAL; + + rc = ksmbd_vfs_listxattr(path->dentry, &xattr_list); + if (rc < 0) { + rsp->hdr.Status = STATUS_INVALID_HANDLE; + goto out; + } else if (!rc) { /* there is no EA in the file */ + ksmbd_debug(SMB, "no ea data in the file\n"); + goto done; + } + xattr_list_len = rc; + + ptr = (char *)rsp->Buffer; + eainfo = (struct smb2_ea_info *)ptr; + prev_eainfo = eainfo; + idx = 0; + + while (idx < xattr_list_len) { + name = xattr_list + idx; + name_len = strlen(name); + + ksmbd_debug(SMB, "%s, len %d\n", name, name_len); + idx += name_len + 1; + + /* + * CIFS does not support EA other than user.* namespace, + * still keep the framework generic, to list other attrs + * in future. + */ + if (strncmp(name, XATTR_USER_PREFIX, XATTR_USER_PREFIX_LEN)) + continue; + + if (!strncmp(&name[XATTR_USER_PREFIX_LEN], STREAM_PREFIX, + STREAM_PREFIX_LEN)) + continue; + + if (req->InputBufferLength && + strncmp(&name[XATTR_USER_PREFIX_LEN], ea_req->name, + ea_req->EaNameLength)) + continue; + + if (!strncmp(&name[XATTR_USER_PREFIX_LEN], + DOS_ATTRIBUTE_PREFIX, DOS_ATTRIBUTE_PREFIX_LEN)) + continue; + + if (!strncmp(name, XATTR_USER_PREFIX, XATTR_USER_PREFIX_LEN)) + name_len -= XATTR_USER_PREFIX_LEN; + + ptr = (char *)(&eainfo->name + name_len + 1); + buf_free_len -= (offsetof(struct smb2_ea_info, name) + + name_len + 1); + /* bailout if xattr can't fit in buf_free_len */ + value_len = ksmbd_vfs_getxattr(user_ns, path->dentry, + name, &buf); + if (value_len <= 0) { + rc = -ENOENT; + rsp->hdr.Status = STATUS_INVALID_HANDLE; + goto out; + } + + buf_free_len -= value_len; + if (buf_free_len < 0) { + kfree(buf); + break; + } + + memcpy(ptr, buf, value_len); + kfree(buf); + + ptr += value_len; + eainfo->Flags = 0; + eainfo->EaNameLength = name_len; + + if (!strncmp(name, XATTR_USER_PREFIX, XATTR_USER_PREFIX_LEN)) + memcpy(eainfo->name, &name[XATTR_USER_PREFIX_LEN], + name_len); + else + memcpy(eainfo->name, name, name_len); + + eainfo->name[name_len] = '\0'; + eainfo->EaValueLength = cpu_to_le16(value_len); + next_offset = offsetof(struct smb2_ea_info, name) + + name_len + 1 + value_len; + + /* align next xattr entry at 4 byte bundary */ + alignment_bytes = ((next_offset + 3) & ~3) - next_offset; + if (alignment_bytes) { + memset(ptr, '\0', alignment_bytes); + ptr += alignment_bytes; + next_offset += alignment_bytes; + buf_free_len -= alignment_bytes; + } + eainfo->NextEntryOffset = cpu_to_le32(next_offset); + prev_eainfo = eainfo; + eainfo = (struct smb2_ea_info *)ptr; + rsp_data_cnt += next_offset; + + if (req->InputBufferLength) { + ksmbd_debug(SMB, "single entry requested\n"); + break; + } + } + + /* no more ea entries */ + prev_eainfo->NextEntryOffset = 0; +done: + rc = 0; + if (rsp_data_cnt == 0) + rsp->hdr.Status = STATUS_NO_EAS_ON_FILE; + rsp->OutputBufferLength = cpu_to_le32(rsp_data_cnt); + inc_rfc1001_len(rsp_org, rsp_data_cnt); +out: + kvfree(xattr_list); + return rc; +} + +static void get_file_access_info(struct smb2_query_info_rsp *rsp, + struct ksmbd_file *fp, void *rsp_org) +{ + struct smb2_file_access_info *file_info; + + file_info = (struct smb2_file_access_info *)rsp->Buffer; + file_info->AccessFlags = fp->daccess; + rsp->OutputBufferLength = + cpu_to_le32(sizeof(struct smb2_file_access_info)); + inc_rfc1001_len(rsp_org, sizeof(struct smb2_file_access_info)); +} + +static int get_file_basic_info(struct smb2_query_info_rsp *rsp, + struct ksmbd_file *fp, void *rsp_org) +{ + struct smb2_file_basic_info *basic_info; + struct kstat stat; + u64 time; + + if (!(fp->daccess & FILE_READ_ATTRIBUTES_LE)) { + pr_err("no right to read the attributes : 0x%x\n", + fp->daccess); + return -EACCES; + } + + basic_info = (struct smb2_file_basic_info *)rsp->Buffer; + generic_fillattr(file_mnt_user_ns(fp->filp), file_inode(fp->filp), + &stat); + basic_info->CreationTime = cpu_to_le64(fp->create_time); + time = ksmbd_UnixTimeToNT(stat.atime); + basic_info->LastAccessTime = cpu_to_le64(time); + time = ksmbd_UnixTimeToNT(stat.mtime); + basic_info->LastWriteTime = cpu_to_le64(time); + time = ksmbd_UnixTimeToNT(stat.ctime); + basic_info->ChangeTime = cpu_to_le64(time); + basic_info->Attributes = fp->f_ci->m_fattr; + basic_info->Pad1 = 0; + rsp->OutputBufferLength = + cpu_to_le32(sizeof(struct smb2_file_basic_info)); + inc_rfc1001_len(rsp_org, sizeof(struct smb2_file_basic_info)); + return 0; +} + +static void get_file_standard_info(struct smb2_query_info_rsp *rsp, + struct ksmbd_file *fp, void *rsp_org) +{ + struct smb2_file_standard_info *sinfo; + unsigned int delete_pending; + struct inode *inode; + struct kstat stat; + + inode = file_inode(fp->filp); + generic_fillattr(file_mnt_user_ns(fp->filp), inode, &stat); + + sinfo = (struct smb2_file_standard_info *)rsp->Buffer; + delete_pending = ksmbd_inode_pending_delete(fp); + + sinfo->AllocationSize = cpu_to_le64(inode->i_blocks << 9); + sinfo->EndOfFile = S_ISDIR(stat.mode) ? 0 : cpu_to_le64(stat.size); + sinfo->NumberOfLinks = cpu_to_le32(get_nlink(&stat) - delete_pending); + sinfo->DeletePending = delete_pending; + sinfo->Directory = S_ISDIR(stat.mode) ? 1 : 0; + rsp->OutputBufferLength = + cpu_to_le32(sizeof(struct smb2_file_standard_info)); + inc_rfc1001_len(rsp_org, + sizeof(struct smb2_file_standard_info)); +} + +static void get_file_alignment_info(struct smb2_query_info_rsp *rsp, + void *rsp_org) +{ + struct smb2_file_alignment_info *file_info; + + file_info = (struct smb2_file_alignment_info *)rsp->Buffer; + file_info->AlignmentRequirement = 0; + rsp->OutputBufferLength = + cpu_to_le32(sizeof(struct smb2_file_alignment_info)); + inc_rfc1001_len(rsp_org, + sizeof(struct smb2_file_alignment_info)); +} + +static int get_file_all_info(struct ksmbd_work *work, + struct smb2_query_info_rsp *rsp, + struct ksmbd_file *fp, + void *rsp_org) +{ + struct ksmbd_conn *conn = work->conn; + struct smb2_file_all_info *file_info; + unsigned int delete_pending; + struct inode *inode; + struct kstat stat; + int conv_len; + char *filename; + u64 time; + + if (!(fp->daccess & FILE_READ_ATTRIBUTES_LE)) { + ksmbd_debug(SMB, "no right to read the attributes : 0x%x\n", + fp->daccess); + return -EACCES; + } + + filename = convert_to_nt_pathname(work->tcon->share_conf, &fp->filp->f_path); + if (IS_ERR(filename)) + return PTR_ERR(filename); + + inode = file_inode(fp->filp); + generic_fillattr(file_mnt_user_ns(fp->filp), inode, &stat); + + ksmbd_debug(SMB, "filename = %s\n", filename); + delete_pending = ksmbd_inode_pending_delete(fp); + file_info = (struct smb2_file_all_info *)rsp->Buffer; + + file_info->CreationTime = cpu_to_le64(fp->create_time); + time = ksmbd_UnixTimeToNT(stat.atime); + file_info->LastAccessTime = cpu_to_le64(time); + time = ksmbd_UnixTimeToNT(stat.mtime); + file_info->LastWriteTime = cpu_to_le64(time); + time = ksmbd_UnixTimeToNT(stat.ctime); + file_info->ChangeTime = cpu_to_le64(time); + file_info->Attributes = fp->f_ci->m_fattr; + file_info->Pad1 = 0; + file_info->AllocationSize = + cpu_to_le64(inode->i_blocks << 9); + file_info->EndOfFile = S_ISDIR(stat.mode) ? 0 : cpu_to_le64(stat.size); + file_info->NumberOfLinks = + cpu_to_le32(get_nlink(&stat) - delete_pending); + file_info->DeletePending = delete_pending; + file_info->Directory = S_ISDIR(stat.mode) ? 1 : 0; + file_info->Pad2 = 0; + file_info->IndexNumber = cpu_to_le64(stat.ino); + file_info->EASize = 0; + file_info->AccessFlags = fp->daccess; + file_info->CurrentByteOffset = cpu_to_le64(fp->filp->f_pos); + file_info->Mode = fp->coption; + file_info->AlignmentRequirement = 0; + conv_len = smbConvertToUTF16((__le16 *)file_info->FileName, filename, + PATH_MAX, conn->local_nls, 0); + conv_len *= 2; + file_info->FileNameLength = cpu_to_le32(conv_len); + rsp->OutputBufferLength = + cpu_to_le32(sizeof(struct smb2_file_all_info) + conv_len - 1); + kfree(filename); + inc_rfc1001_len(rsp_org, le32_to_cpu(rsp->OutputBufferLength)); + return 0; +} + +static void get_file_alternate_info(struct ksmbd_work *work, + struct smb2_query_info_rsp *rsp, + struct ksmbd_file *fp, + void *rsp_org) +{ + struct ksmbd_conn *conn = work->conn; + struct smb2_file_alt_name_info *file_info; + struct dentry *dentry = fp->filp->f_path.dentry; + int conv_len; + + spin_lock(&dentry->d_lock); + file_info = (struct smb2_file_alt_name_info *)rsp->Buffer; + conv_len = ksmbd_extract_shortname(conn, + dentry->d_name.name, + file_info->FileName); + spin_unlock(&dentry->d_lock); + file_info->FileNameLength = cpu_to_le32(conv_len); + rsp->OutputBufferLength = + cpu_to_le32(sizeof(struct smb2_file_alt_name_info) + conv_len); + inc_rfc1001_len(rsp_org, le32_to_cpu(rsp->OutputBufferLength)); +} + +static void get_file_stream_info(struct ksmbd_work *work, + struct smb2_query_info_rsp *rsp, + struct ksmbd_file *fp, + void *rsp_org) +{ + struct ksmbd_conn *conn = work->conn; + struct smb2_file_stream_info *file_info; + char *stream_name, *xattr_list = NULL, *stream_buf; + struct kstat stat; + const struct path *path = &fp->filp->f_path; + ssize_t xattr_list_len; + int nbytes = 0, streamlen, stream_name_len, next, idx = 0; + int buf_free_len; + struct smb2_query_info_req *req = ksmbd_req_buf_next(work); + + generic_fillattr(file_mnt_user_ns(fp->filp), file_inode(fp->filp), + &stat); + file_info = (struct smb2_file_stream_info *)rsp->Buffer; + + buf_free_len = + smb2_calc_max_out_buf_len(work, 8, + le32_to_cpu(req->OutputBufferLength)); + if (buf_free_len < 0) + goto out; + + xattr_list_len = ksmbd_vfs_listxattr(path->dentry, &xattr_list); + if (xattr_list_len < 0) { + goto out; + } else if (!xattr_list_len) { + ksmbd_debug(SMB, "empty xattr in the file\n"); + goto out; + } + + while (idx < xattr_list_len) { + stream_name = xattr_list + idx; + streamlen = strlen(stream_name); + idx += streamlen + 1; + + ksmbd_debug(SMB, "%s, len %d\n", stream_name, streamlen); + + if (strncmp(&stream_name[XATTR_USER_PREFIX_LEN], + STREAM_PREFIX, STREAM_PREFIX_LEN)) + continue; + + stream_name_len = streamlen - (XATTR_USER_PREFIX_LEN + + STREAM_PREFIX_LEN); + streamlen = stream_name_len; + + /* plus : size */ + streamlen += 1; + stream_buf = kmalloc(streamlen + 1, GFP_KERNEL); + if (!stream_buf) + break; + + streamlen = snprintf(stream_buf, streamlen + 1, + ":%s", &stream_name[XATTR_NAME_STREAM_LEN]); + + next = sizeof(struct smb2_file_stream_info) + streamlen * 2; + if (next > buf_free_len) { + kfree(stream_buf); + break; + } + + file_info = (struct smb2_file_stream_info *)&rsp->Buffer[nbytes]; + streamlen = smbConvertToUTF16((__le16 *)file_info->StreamName, + stream_buf, streamlen, + conn->local_nls, 0); + streamlen *= 2; + kfree(stream_buf); + file_info->StreamNameLength = cpu_to_le32(streamlen); + file_info->StreamSize = cpu_to_le64(stream_name_len); + file_info->StreamAllocationSize = cpu_to_le64(stream_name_len); + + nbytes += next; + buf_free_len -= next; + file_info->NextEntryOffset = cpu_to_le32(next); + } + +out: + if (!S_ISDIR(stat.mode) && + buf_free_len >= sizeof(struct smb2_file_stream_info) + 7 * 2) { + file_info = (struct smb2_file_stream_info *) + &rsp->Buffer[nbytes]; + streamlen = smbConvertToUTF16((__le16 *)file_info->StreamName, + "::$DATA", 7, conn->local_nls, 0); + streamlen *= 2; + file_info->StreamNameLength = cpu_to_le32(streamlen); + file_info->StreamSize = cpu_to_le64(stat.size); + file_info->StreamAllocationSize = cpu_to_le64(stat.blocks << 9); + nbytes += sizeof(struct smb2_file_stream_info) + streamlen; + } + + /* last entry offset should be 0 */ + file_info->NextEntryOffset = 0; + kvfree(xattr_list); + + rsp->OutputBufferLength = cpu_to_le32(nbytes); + inc_rfc1001_len(rsp_org, nbytes); +} + +static void get_file_internal_info(struct smb2_query_info_rsp *rsp, + struct ksmbd_file *fp, void *rsp_org) +{ + struct smb2_file_internal_info *file_info; + struct kstat stat; + + generic_fillattr(file_mnt_user_ns(fp->filp), file_inode(fp->filp), + &stat); + file_info = (struct smb2_file_internal_info *)rsp->Buffer; + file_info->IndexNumber = cpu_to_le64(stat.ino); + rsp->OutputBufferLength = + cpu_to_le32(sizeof(struct smb2_file_internal_info)); + inc_rfc1001_len(rsp_org, sizeof(struct smb2_file_internal_info)); +} + +static int get_file_network_open_info(struct smb2_query_info_rsp *rsp, + struct ksmbd_file *fp, void *rsp_org) +{ + struct smb2_file_ntwrk_info *file_info; + struct inode *inode; + struct kstat stat; + u64 time; + + if (!(fp->daccess & FILE_READ_ATTRIBUTES_LE)) { + pr_err("no right to read the attributes : 0x%x\n", + fp->daccess); + return -EACCES; + } + + file_info = (struct smb2_file_ntwrk_info *)rsp->Buffer; + + inode = file_inode(fp->filp); + generic_fillattr(file_mnt_user_ns(fp->filp), inode, &stat); + + file_info->CreationTime = cpu_to_le64(fp->create_time); + time = ksmbd_UnixTimeToNT(stat.atime); + file_info->LastAccessTime = cpu_to_le64(time); + time = ksmbd_UnixTimeToNT(stat.mtime); + file_info->LastWriteTime = cpu_to_le64(time); + time = ksmbd_UnixTimeToNT(stat.ctime); + file_info->ChangeTime = cpu_to_le64(time); + file_info->Attributes = fp->f_ci->m_fattr; + file_info->AllocationSize = + cpu_to_le64(inode->i_blocks << 9); + file_info->EndOfFile = S_ISDIR(stat.mode) ? 0 : cpu_to_le64(stat.size); + file_info->Reserved = cpu_to_le32(0); + rsp->OutputBufferLength = + cpu_to_le32(sizeof(struct smb2_file_ntwrk_info)); + inc_rfc1001_len(rsp_org, sizeof(struct smb2_file_ntwrk_info)); + return 0; +} + +static void get_file_ea_info(struct smb2_query_info_rsp *rsp, void *rsp_org) +{ + struct smb2_file_ea_info *file_info; + + file_info = (struct smb2_file_ea_info *)rsp->Buffer; + file_info->EASize = 0; + rsp->OutputBufferLength = + cpu_to_le32(sizeof(struct smb2_file_ea_info)); + inc_rfc1001_len(rsp_org, sizeof(struct smb2_file_ea_info)); +} + +static void get_file_position_info(struct smb2_query_info_rsp *rsp, + struct ksmbd_file *fp, void *rsp_org) +{ + struct smb2_file_pos_info *file_info; + + file_info = (struct smb2_file_pos_info *)rsp->Buffer; + file_info->CurrentByteOffset = cpu_to_le64(fp->filp->f_pos); + rsp->OutputBufferLength = + cpu_to_le32(sizeof(struct smb2_file_pos_info)); + inc_rfc1001_len(rsp_org, sizeof(struct smb2_file_pos_info)); +} + +static void get_file_mode_info(struct smb2_query_info_rsp *rsp, + struct ksmbd_file *fp, void *rsp_org) +{ + struct smb2_file_mode_info *file_info; + + file_info = (struct smb2_file_mode_info *)rsp->Buffer; + file_info->Mode = fp->coption & FILE_MODE_INFO_MASK; + rsp->OutputBufferLength = + cpu_to_le32(sizeof(struct smb2_file_mode_info)); + inc_rfc1001_len(rsp_org, sizeof(struct smb2_file_mode_info)); +} + +static void get_file_compression_info(struct smb2_query_info_rsp *rsp, + struct ksmbd_file *fp, void *rsp_org) +{ + struct smb2_file_comp_info *file_info; + struct kstat stat; + + generic_fillattr(file_mnt_user_ns(fp->filp), file_inode(fp->filp), + &stat); + + file_info = (struct smb2_file_comp_info *)rsp->Buffer; + file_info->CompressedFileSize = cpu_to_le64(stat.blocks << 9); + file_info->CompressionFormat = COMPRESSION_FORMAT_NONE; + file_info->CompressionUnitShift = 0; + file_info->ChunkShift = 0; + file_info->ClusterShift = 0; + memset(&file_info->Reserved[0], 0, 3); + + rsp->OutputBufferLength = + cpu_to_le32(sizeof(struct smb2_file_comp_info)); + inc_rfc1001_len(rsp_org, sizeof(struct smb2_file_comp_info)); +} + +static int get_file_attribute_tag_info(struct smb2_query_info_rsp *rsp, + struct ksmbd_file *fp, void *rsp_org) +{ + struct smb2_file_attr_tag_info *file_info; + + if (!(fp->daccess & FILE_READ_ATTRIBUTES_LE)) { + pr_err("no right to read the attributes : 0x%x\n", + fp->daccess); + return -EACCES; + } + + file_info = (struct smb2_file_attr_tag_info *)rsp->Buffer; + file_info->FileAttributes = fp->f_ci->m_fattr; + file_info->ReparseTag = 0; + rsp->OutputBufferLength = + cpu_to_le32(sizeof(struct smb2_file_attr_tag_info)); + inc_rfc1001_len(rsp_org, sizeof(struct smb2_file_attr_tag_info)); + return 0; +} + +static int find_file_posix_info(struct smb2_query_info_rsp *rsp, + struct ksmbd_file *fp, void *rsp_org) +{ + struct smb311_posix_qinfo *file_info; + struct inode *inode = file_inode(fp->filp); + struct user_namespace *user_ns = file_mnt_user_ns(fp->filp); + vfsuid_t vfsuid = i_uid_into_vfsuid(user_ns, inode); + vfsgid_t vfsgid = i_gid_into_vfsgid(user_ns, inode); + u64 time; + int out_buf_len = sizeof(struct smb311_posix_qinfo) + 32; + + file_info = (struct smb311_posix_qinfo *)rsp->Buffer; + file_info->CreationTime = cpu_to_le64(fp->create_time); + time = ksmbd_UnixTimeToNT(inode->i_atime); + file_info->LastAccessTime = cpu_to_le64(time); + time = ksmbd_UnixTimeToNT(inode->i_mtime); + file_info->LastWriteTime = cpu_to_le64(time); + time = ksmbd_UnixTimeToNT(inode->i_ctime); + file_info->ChangeTime = cpu_to_le64(time); + file_info->DosAttributes = fp->f_ci->m_fattr; + file_info->Inode = cpu_to_le64(inode->i_ino); + file_info->EndOfFile = cpu_to_le64(inode->i_size); + file_info->AllocationSize = cpu_to_le64(inode->i_blocks << 9); + file_info->HardLinks = cpu_to_le32(inode->i_nlink); + file_info->Mode = cpu_to_le32(inode->i_mode & 0777); + file_info->DeviceId = cpu_to_le32(inode->i_rdev); + + /* + * Sids(32) contain two sids(Domain sid(16), UNIX group sid(16)). + * UNIX sid(16) = revision(1) + num_subauth(1) + authority(6) + + * sub_auth(4 * 1(num_subauth)) + RID(4). + */ + id_to_sid(from_kuid_munged(&init_user_ns, vfsuid_into_kuid(vfsuid)), + SIDUNIX_USER, (struct smb_sid *)&file_info->Sids[0]); + id_to_sid(from_kgid_munged(&init_user_ns, vfsgid_into_kgid(vfsgid)), + SIDUNIX_GROUP, (struct smb_sid *)&file_info->Sids[16]); + + rsp->OutputBufferLength = cpu_to_le32(out_buf_len); + inc_rfc1001_len(rsp_org, out_buf_len); + return out_buf_len; +} + +static int smb2_get_info_file(struct ksmbd_work *work, + struct smb2_query_info_req *req, + struct smb2_query_info_rsp *rsp) +{ + struct ksmbd_file *fp; + int fileinfoclass = 0; + int rc = 0; + int file_infoclass_size; + unsigned int id = KSMBD_NO_FID, pid = KSMBD_NO_FID; + + if (test_share_config_flag(work->tcon->share_conf, + KSMBD_SHARE_FLAG_PIPE)) { + /* smb2 info file called for pipe */ + return smb2_get_info_file_pipe(work->sess, req, rsp, + work->response_buf); + } + + if (work->next_smb2_rcv_hdr_off) { + if (!has_file_id(req->VolatileFileId)) { + ksmbd_debug(SMB, "Compound request set FID = %llu\n", + work->compound_fid); + id = work->compound_fid; + pid = work->compound_pfid; + } + } + + if (!has_file_id(id)) { + id = req->VolatileFileId; + pid = req->PersistentFileId; + } + + fp = ksmbd_lookup_fd_slow(work, id, pid); + if (!fp) + return -ENOENT; + + fileinfoclass = req->FileInfoClass; + + switch (fileinfoclass) { + case FILE_ACCESS_INFORMATION: + get_file_access_info(rsp, fp, work->response_buf); + file_infoclass_size = FILE_ACCESS_INFORMATION_SIZE; + break; + + case FILE_BASIC_INFORMATION: + rc = get_file_basic_info(rsp, fp, work->response_buf); + file_infoclass_size = FILE_BASIC_INFORMATION_SIZE; + break; + + case FILE_STANDARD_INFORMATION: + get_file_standard_info(rsp, fp, work->response_buf); + file_infoclass_size = FILE_STANDARD_INFORMATION_SIZE; + break; + + case FILE_ALIGNMENT_INFORMATION: + get_file_alignment_info(rsp, work->response_buf); + file_infoclass_size = FILE_ALIGNMENT_INFORMATION_SIZE; + break; + + case FILE_ALL_INFORMATION: + rc = get_file_all_info(work, rsp, fp, work->response_buf); + file_infoclass_size = FILE_ALL_INFORMATION_SIZE; + break; + + case FILE_ALTERNATE_NAME_INFORMATION: + get_file_alternate_info(work, rsp, fp, work->response_buf); + file_infoclass_size = FILE_ALTERNATE_NAME_INFORMATION_SIZE; + break; + + case FILE_STREAM_INFORMATION: + get_file_stream_info(work, rsp, fp, work->response_buf); + file_infoclass_size = FILE_STREAM_INFORMATION_SIZE; + break; + + case FILE_INTERNAL_INFORMATION: + get_file_internal_info(rsp, fp, work->response_buf); + file_infoclass_size = FILE_INTERNAL_INFORMATION_SIZE; + break; + + case FILE_NETWORK_OPEN_INFORMATION: + rc = get_file_network_open_info(rsp, fp, work->response_buf); + file_infoclass_size = FILE_NETWORK_OPEN_INFORMATION_SIZE; + break; + + case FILE_EA_INFORMATION: + get_file_ea_info(rsp, work->response_buf); + file_infoclass_size = FILE_EA_INFORMATION_SIZE; + break; + + case FILE_FULL_EA_INFORMATION: + rc = smb2_get_ea(work, fp, req, rsp, work->response_buf); + file_infoclass_size = FILE_FULL_EA_INFORMATION_SIZE; + break; + + case FILE_POSITION_INFORMATION: + get_file_position_info(rsp, fp, work->response_buf); + file_infoclass_size = FILE_POSITION_INFORMATION_SIZE; + break; + + case FILE_MODE_INFORMATION: + get_file_mode_info(rsp, fp, work->response_buf); + file_infoclass_size = FILE_MODE_INFORMATION_SIZE; + break; + + case FILE_COMPRESSION_INFORMATION: + get_file_compression_info(rsp, fp, work->response_buf); + file_infoclass_size = FILE_COMPRESSION_INFORMATION_SIZE; + break; + + case FILE_ATTRIBUTE_TAG_INFORMATION: + rc = get_file_attribute_tag_info(rsp, fp, work->response_buf); + file_infoclass_size = FILE_ATTRIBUTE_TAG_INFORMATION_SIZE; + break; + case SMB_FIND_FILE_POSIX_INFO: + if (!work->tcon->posix_extensions) { + pr_err("client doesn't negotiate with SMB3.1.1 POSIX Extensions\n"); + rc = -EOPNOTSUPP; + } else { + file_infoclass_size = find_file_posix_info(rsp, fp, + work->response_buf); + } + break; + default: + ksmbd_debug(SMB, "fileinfoclass %d not supported yet\n", + fileinfoclass); + rc = -EOPNOTSUPP; + } + if (!rc) + rc = buffer_check_err(le32_to_cpu(req->OutputBufferLength), + rsp, work->response_buf, + file_infoclass_size); + ksmbd_fd_put(work, fp); + return rc; +} + +static int smb2_get_info_filesystem(struct ksmbd_work *work, + struct smb2_query_info_req *req, + struct smb2_query_info_rsp *rsp) +{ + struct ksmbd_session *sess = work->sess; + struct ksmbd_conn *conn = work->conn; + struct ksmbd_share_config *share = work->tcon->share_conf; + int fsinfoclass = 0; + struct kstatfs stfs; + struct path path; + int rc = 0, len; + int fs_infoclass_size = 0; + + if (!share->path) + return -EIO; + + rc = kern_path(share->path, LOOKUP_NO_SYMLINKS, &path); + if (rc) { + pr_err("cannot create vfs path\n"); + return -EIO; + } + + rc = vfs_statfs(&path, &stfs); + if (rc) { + pr_err("cannot do stat of path %s\n", share->path); + path_put(&path); + return -EIO; + } + + fsinfoclass = req->FileInfoClass; + + switch (fsinfoclass) { + case FS_DEVICE_INFORMATION: + { + struct filesystem_device_info *info; + + info = (struct filesystem_device_info *)rsp->Buffer; + + info->DeviceType = cpu_to_le32(stfs.f_type); + info->DeviceCharacteristics = cpu_to_le32(0x00000020); + rsp->OutputBufferLength = cpu_to_le32(8); + inc_rfc1001_len(work->response_buf, 8); + fs_infoclass_size = FS_DEVICE_INFORMATION_SIZE; + break; + } + case FS_ATTRIBUTE_INFORMATION: + { + struct filesystem_attribute_info *info; + size_t sz; + + info = (struct filesystem_attribute_info *)rsp->Buffer; + info->Attributes = cpu_to_le32(FILE_SUPPORTS_OBJECT_IDS | + FILE_PERSISTENT_ACLS | + FILE_UNICODE_ON_DISK | + FILE_CASE_PRESERVED_NAMES | + FILE_CASE_SENSITIVE_SEARCH | + FILE_SUPPORTS_BLOCK_REFCOUNTING); + + info->Attributes |= cpu_to_le32(server_conf.share_fake_fscaps); + + if (test_share_config_flag(work->tcon->share_conf, + KSMBD_SHARE_FLAG_STREAMS)) + info->Attributes |= cpu_to_le32(FILE_NAMED_STREAMS); + + info->MaxPathNameComponentLength = cpu_to_le32(stfs.f_namelen); + len = smbConvertToUTF16((__le16 *)info->FileSystemName, + "NTFS", PATH_MAX, conn->local_nls, 0); + len = len * 2; + info->FileSystemNameLen = cpu_to_le32(len); + sz = sizeof(struct filesystem_attribute_info) - 2 + len; + rsp->OutputBufferLength = cpu_to_le32(sz); + inc_rfc1001_len(work->response_buf, sz); + fs_infoclass_size = FS_ATTRIBUTE_INFORMATION_SIZE; + break; + } + case FS_VOLUME_INFORMATION: + { + struct filesystem_vol_info *info; + size_t sz; + unsigned int serial_crc = 0; + + info = (struct filesystem_vol_info *)(rsp->Buffer); + info->VolumeCreationTime = 0; + serial_crc = crc32_le(serial_crc, share->name, + strlen(share->name)); + serial_crc = crc32_le(serial_crc, share->path, + strlen(share->path)); + serial_crc = crc32_le(serial_crc, ksmbd_netbios_name(), + strlen(ksmbd_netbios_name())); + /* Taking dummy value of serial number*/ + info->SerialNumber = cpu_to_le32(serial_crc); + len = smbConvertToUTF16((__le16 *)info->VolumeLabel, + share->name, PATH_MAX, + conn->local_nls, 0); + len = len * 2; + info->VolumeLabelSize = cpu_to_le32(len); + info->Reserved = 0; + sz = sizeof(struct filesystem_vol_info) - 2 + len; + rsp->OutputBufferLength = cpu_to_le32(sz); + inc_rfc1001_len(work->response_buf, sz); + fs_infoclass_size = FS_VOLUME_INFORMATION_SIZE; + break; + } + case FS_SIZE_INFORMATION: + { + struct filesystem_info *info; + + info = (struct filesystem_info *)(rsp->Buffer); + info->TotalAllocationUnits = cpu_to_le64(stfs.f_blocks); + info->FreeAllocationUnits = cpu_to_le64(stfs.f_bfree); + info->SectorsPerAllocationUnit = cpu_to_le32(1); + info->BytesPerSector = cpu_to_le32(stfs.f_bsize); + rsp->OutputBufferLength = cpu_to_le32(24); + inc_rfc1001_len(work->response_buf, 24); + fs_infoclass_size = FS_SIZE_INFORMATION_SIZE; + break; + } + case FS_FULL_SIZE_INFORMATION: + { + struct smb2_fs_full_size_info *info; + + info = (struct smb2_fs_full_size_info *)(rsp->Buffer); + info->TotalAllocationUnits = cpu_to_le64(stfs.f_blocks); + info->CallerAvailableAllocationUnits = + cpu_to_le64(stfs.f_bavail); + info->ActualAvailableAllocationUnits = + cpu_to_le64(stfs.f_bfree); + info->SectorsPerAllocationUnit = cpu_to_le32(1); + info->BytesPerSector = cpu_to_le32(stfs.f_bsize); + rsp->OutputBufferLength = cpu_to_le32(32); + inc_rfc1001_len(work->response_buf, 32); + fs_infoclass_size = FS_FULL_SIZE_INFORMATION_SIZE; + break; + } + case FS_OBJECT_ID_INFORMATION: + { + struct object_id_info *info; + + info = (struct object_id_info *)(rsp->Buffer); + + if (!user_guest(sess->user)) + memcpy(info->objid, user_passkey(sess->user), 16); + else + memset(info->objid, 0, 16); + + info->extended_info.magic = cpu_to_le32(EXTENDED_INFO_MAGIC); + info->extended_info.version = cpu_to_le32(1); + info->extended_info.release = cpu_to_le32(1); + info->extended_info.rel_date = 0; + memcpy(info->extended_info.version_string, "1.1.0", strlen("1.1.0")); + rsp->OutputBufferLength = cpu_to_le32(64); + inc_rfc1001_len(work->response_buf, 64); + fs_infoclass_size = FS_OBJECT_ID_INFORMATION_SIZE; + break; + } + case FS_SECTOR_SIZE_INFORMATION: + { + struct smb3_fs_ss_info *info; + unsigned int sector_size = + min_t(unsigned int, path.mnt->mnt_sb->s_blocksize, 4096); + + info = (struct smb3_fs_ss_info *)(rsp->Buffer); + + info->LogicalBytesPerSector = cpu_to_le32(sector_size); + info->PhysicalBytesPerSectorForAtomicity = + cpu_to_le32(sector_size); + info->PhysicalBytesPerSectorForPerf = cpu_to_le32(sector_size); + info->FSEffPhysicalBytesPerSectorForAtomicity = + cpu_to_le32(sector_size); + info->Flags = cpu_to_le32(SSINFO_FLAGS_ALIGNED_DEVICE | + SSINFO_FLAGS_PARTITION_ALIGNED_ON_DEVICE); + info->ByteOffsetForSectorAlignment = 0; + info->ByteOffsetForPartitionAlignment = 0; + rsp->OutputBufferLength = cpu_to_le32(28); + inc_rfc1001_len(work->response_buf, 28); + fs_infoclass_size = FS_SECTOR_SIZE_INFORMATION_SIZE; + break; + } + case FS_CONTROL_INFORMATION: + { + /* + * TODO : The current implementation is based on + * test result with win7(NTFS) server. It's need to + * modify this to get valid Quota values + * from Linux kernel + */ + struct smb2_fs_control_info *info; + + info = (struct smb2_fs_control_info *)(rsp->Buffer); + info->FreeSpaceStartFiltering = 0; + info->FreeSpaceThreshold = 0; + info->FreeSpaceStopFiltering = 0; + info->DefaultQuotaThreshold = cpu_to_le64(SMB2_NO_FID); + info->DefaultQuotaLimit = cpu_to_le64(SMB2_NO_FID); + info->Padding = 0; + rsp->OutputBufferLength = cpu_to_le32(48); + inc_rfc1001_len(work->response_buf, 48); + fs_infoclass_size = FS_CONTROL_INFORMATION_SIZE; + break; + } + case FS_POSIX_INFORMATION: + { + struct filesystem_posix_info *info; + + if (!work->tcon->posix_extensions) { + pr_err("client doesn't negotiate with SMB3.1.1 POSIX Extensions\n"); + rc = -EOPNOTSUPP; + } else { + info = (struct filesystem_posix_info *)(rsp->Buffer); + info->OptimalTransferSize = cpu_to_le32(stfs.f_bsize); + info->BlockSize = cpu_to_le32(stfs.f_bsize); + info->TotalBlocks = cpu_to_le64(stfs.f_blocks); + info->BlocksAvail = cpu_to_le64(stfs.f_bfree); + info->UserBlocksAvail = cpu_to_le64(stfs.f_bavail); + info->TotalFileNodes = cpu_to_le64(stfs.f_files); + info->FreeFileNodes = cpu_to_le64(stfs.f_ffree); + rsp->OutputBufferLength = cpu_to_le32(56); + inc_rfc1001_len(work->response_buf, 56); + fs_infoclass_size = FS_POSIX_INFORMATION_SIZE; + } + break; + } + default: + path_put(&path); + return -EOPNOTSUPP; + } + rc = buffer_check_err(le32_to_cpu(req->OutputBufferLength), + rsp, work->response_buf, + fs_infoclass_size); + path_put(&path); + return rc; +} + +static int smb2_get_info_sec(struct ksmbd_work *work, + struct smb2_query_info_req *req, + struct smb2_query_info_rsp *rsp) +{ + struct ksmbd_file *fp; + struct user_namespace *user_ns; + struct smb_ntsd *pntsd = (struct smb_ntsd *)rsp->Buffer, *ppntsd = NULL; + struct smb_fattr fattr = {{0}}; + struct inode *inode; + __u32 secdesclen = 0; + unsigned int id = KSMBD_NO_FID, pid = KSMBD_NO_FID; + int addition_info = le32_to_cpu(req->AdditionalInformation); + int rc = 0, ppntsd_size = 0; + + if (addition_info & ~(OWNER_SECINFO | GROUP_SECINFO | DACL_SECINFO | + PROTECTED_DACL_SECINFO | + UNPROTECTED_DACL_SECINFO)) { + ksmbd_debug(SMB, "Unsupported addition info: 0x%x)\n", + addition_info); + + pntsd->revision = cpu_to_le16(1); + pntsd->type = cpu_to_le16(SELF_RELATIVE | DACL_PROTECTED); + pntsd->osidoffset = 0; + pntsd->gsidoffset = 0; + pntsd->sacloffset = 0; + pntsd->dacloffset = 0; + + secdesclen = sizeof(struct smb_ntsd); + rsp->OutputBufferLength = cpu_to_le32(secdesclen); + inc_rfc1001_len(work->response_buf, secdesclen); + + return 0; + } + + if (work->next_smb2_rcv_hdr_off) { + if (!has_file_id(req->VolatileFileId)) { + ksmbd_debug(SMB, "Compound request set FID = %llu\n", + work->compound_fid); + id = work->compound_fid; + pid = work->compound_pfid; + } + } + + if (!has_file_id(id)) { + id = req->VolatileFileId; + pid = req->PersistentFileId; + } + + fp = ksmbd_lookup_fd_slow(work, id, pid); + if (!fp) + return -ENOENT; + + user_ns = file_mnt_user_ns(fp->filp); + inode = file_inode(fp->filp); + ksmbd_acls_fattr(&fattr, user_ns, inode); + + if (test_share_config_flag(work->tcon->share_conf, + KSMBD_SHARE_FLAG_ACL_XATTR)) + ppntsd_size = ksmbd_vfs_get_sd_xattr(work->conn, user_ns, + fp->filp->f_path.dentry, + &ppntsd); + + /* Check if sd buffer size exceeds response buffer size */ + if (smb2_resp_buf_len(work, 8) > ppntsd_size) + rc = build_sec_desc(user_ns, pntsd, ppntsd, ppntsd_size, + addition_info, &secdesclen, &fattr); + posix_acl_release(fattr.cf_acls); + posix_acl_release(fattr.cf_dacls); + kfree(ppntsd); + ksmbd_fd_put(work, fp); + if (rc) + return rc; + + rsp->OutputBufferLength = cpu_to_le32(secdesclen); + inc_rfc1001_len(work->response_buf, secdesclen); + return 0; +} + +/** + * smb2_query_info() - handler for smb2 query info command + * @work: smb work containing query info request buffer + * + * Return: 0 on success, otherwise error + */ +int smb2_query_info(struct ksmbd_work *work) +{ + struct smb2_query_info_req *req; + struct smb2_query_info_rsp *rsp; + int rc = 0; + + WORK_BUFFERS(work, req, rsp); + + ksmbd_debug(SMB, "GOT query info request\n"); + + switch (req->InfoType) { + case SMB2_O_INFO_FILE: + ksmbd_debug(SMB, "GOT SMB2_O_INFO_FILE\n"); + rc = smb2_get_info_file(work, req, rsp); + break; + case SMB2_O_INFO_FILESYSTEM: + ksmbd_debug(SMB, "GOT SMB2_O_INFO_FILESYSTEM\n"); + rc = smb2_get_info_filesystem(work, req, rsp); + break; + case SMB2_O_INFO_SECURITY: + ksmbd_debug(SMB, "GOT SMB2_O_INFO_SECURITY\n"); + rc = smb2_get_info_sec(work, req, rsp); + break; + default: + ksmbd_debug(SMB, "InfoType %d not supported yet\n", + req->InfoType); + rc = -EOPNOTSUPP; + } + + if (rc < 0) { + if (rc == -EACCES) + rsp->hdr.Status = STATUS_ACCESS_DENIED; + else if (rc == -ENOENT) + rsp->hdr.Status = STATUS_FILE_CLOSED; + else if (rc == -EIO) + rsp->hdr.Status = STATUS_UNEXPECTED_IO_ERROR; + else if (rc == -EOPNOTSUPP || rsp->hdr.Status == 0) + rsp->hdr.Status = STATUS_INVALID_INFO_CLASS; + smb2_set_err_rsp(work); + + ksmbd_debug(SMB, "error while processing smb2 query rc = %d\n", + rc); + return rc; + } + rsp->StructureSize = cpu_to_le16(9); + rsp->OutputBufferOffset = cpu_to_le16(72); + inc_rfc1001_len(work->response_buf, 8); + return 0; +} + +/** + * smb2_close_pipe() - handler for closing IPC pipe + * @work: smb work containing close request buffer + * + * Return: 0 + */ +static noinline int smb2_close_pipe(struct ksmbd_work *work) +{ + u64 id; + struct smb2_close_req *req = smb2_get_msg(work->request_buf); + struct smb2_close_rsp *rsp = smb2_get_msg(work->response_buf); + + id = req->VolatileFileId; + ksmbd_session_rpc_close(work->sess, id); + + rsp->StructureSize = cpu_to_le16(60); + rsp->Flags = 0; + rsp->Reserved = 0; + rsp->CreationTime = 0; + rsp->LastAccessTime = 0; + rsp->LastWriteTime = 0; + rsp->ChangeTime = 0; + rsp->AllocationSize = 0; + rsp->EndOfFile = 0; + rsp->Attributes = 0; + inc_rfc1001_len(work->response_buf, 60); + return 0; +} + +/** + * smb2_close() - handler for smb2 close file command + * @work: smb work containing close request buffer + * + * Return: 0 + */ +int smb2_close(struct ksmbd_work *work) +{ + u64 volatile_id = KSMBD_NO_FID; + u64 sess_id; + struct smb2_close_req *req; + struct smb2_close_rsp *rsp; + struct ksmbd_conn *conn = work->conn; + struct ksmbd_file *fp; + struct inode *inode; + u64 time; + int err = 0; + + WORK_BUFFERS(work, req, rsp); + + if (test_share_config_flag(work->tcon->share_conf, + KSMBD_SHARE_FLAG_PIPE)) { + ksmbd_debug(SMB, "IPC pipe close request\n"); + return smb2_close_pipe(work); + } + + sess_id = le64_to_cpu(req->hdr.SessionId); + if (req->hdr.Flags & SMB2_FLAGS_RELATED_OPERATIONS) + sess_id = work->compound_sid; + + work->compound_sid = 0; + if (check_session_id(conn, sess_id)) { + work->compound_sid = sess_id; + } else { + rsp->hdr.Status = STATUS_USER_SESSION_DELETED; + if (req->hdr.Flags & SMB2_FLAGS_RELATED_OPERATIONS) + rsp->hdr.Status = STATUS_INVALID_PARAMETER; + err = -EBADF; + goto out; + } + + if (work->next_smb2_rcv_hdr_off && + !has_file_id(req->VolatileFileId)) { + if (!has_file_id(work->compound_fid)) { + /* file already closed, return FILE_CLOSED */ + ksmbd_debug(SMB, "file already closed\n"); + rsp->hdr.Status = STATUS_FILE_CLOSED; + err = -EBADF; + goto out; + } else { + ksmbd_debug(SMB, + "Compound request set FID = %llu:%llu\n", + work->compound_fid, + work->compound_pfid); + volatile_id = work->compound_fid; + + /* file closed, stored id is not valid anymore */ + work->compound_fid = KSMBD_NO_FID; + work->compound_pfid = KSMBD_NO_FID; + } + } else { + volatile_id = req->VolatileFileId; + } + ksmbd_debug(SMB, "volatile_id = %llu\n", volatile_id); + + rsp->StructureSize = cpu_to_le16(60); + rsp->Reserved = 0; + + if (req->Flags == SMB2_CLOSE_FLAG_POSTQUERY_ATTRIB) { + fp = ksmbd_lookup_fd_fast(work, volatile_id); + if (!fp) { + err = -ENOENT; + goto out; + } + + inode = file_inode(fp->filp); + rsp->Flags = SMB2_CLOSE_FLAG_POSTQUERY_ATTRIB; + rsp->AllocationSize = S_ISDIR(inode->i_mode) ? 0 : + cpu_to_le64(inode->i_blocks << 9); + rsp->EndOfFile = cpu_to_le64(inode->i_size); + rsp->Attributes = fp->f_ci->m_fattr; + rsp->CreationTime = cpu_to_le64(fp->create_time); + time = ksmbd_UnixTimeToNT(inode->i_atime); + rsp->LastAccessTime = cpu_to_le64(time); + time = ksmbd_UnixTimeToNT(inode->i_mtime); + rsp->LastWriteTime = cpu_to_le64(time); + time = ksmbd_UnixTimeToNT(inode->i_ctime); + rsp->ChangeTime = cpu_to_le64(time); + ksmbd_fd_put(work, fp); + } else { + rsp->Flags = 0; + rsp->AllocationSize = 0; + rsp->EndOfFile = 0; + rsp->Attributes = 0; + rsp->CreationTime = 0; + rsp->LastAccessTime = 0; + rsp->LastWriteTime = 0; + rsp->ChangeTime = 0; + } + + err = ksmbd_close_fd(work, volatile_id); +out: + if (err) { + if (rsp->hdr.Status == 0) + rsp->hdr.Status = STATUS_FILE_CLOSED; + smb2_set_err_rsp(work); + } else { + inc_rfc1001_len(work->response_buf, 60); + } + + return 0; +} + +/** + * smb2_echo() - handler for smb2 echo(ping) command + * @work: smb work containing echo request buffer + * + * Return: 0 + */ +int smb2_echo(struct ksmbd_work *work) +{ + struct smb2_echo_rsp *rsp = smb2_get_msg(work->response_buf); + + rsp->StructureSize = cpu_to_le16(4); + rsp->Reserved = 0; + inc_rfc1001_len(work->response_buf, 4); + return 0; +} + +static int smb2_rename(struct ksmbd_work *work, + struct ksmbd_file *fp, + struct user_namespace *user_ns, + struct smb2_file_rename_info *file_info, + struct nls_table *local_nls) +{ + struct ksmbd_share_config *share = fp->tcon->share_conf; + char *new_name = NULL, *abs_oldname = NULL, *old_name = NULL; + char *pathname = NULL; + struct path path; + bool file_present = true; + int rc; + + ksmbd_debug(SMB, "setting FILE_RENAME_INFO\n"); + pathname = kmalloc(PATH_MAX, GFP_KERNEL); + if (!pathname) + return -ENOMEM; + + abs_oldname = file_path(fp->filp, pathname, PATH_MAX); + if (IS_ERR(abs_oldname)) { + rc = -EINVAL; + goto out; + } + old_name = strrchr(abs_oldname, '/'); + if (old_name && old_name[1] != '\0') { + old_name++; + } else { + ksmbd_debug(SMB, "can't get last component in path %s\n", + abs_oldname); + rc = -ENOENT; + goto out; + } + + new_name = smb2_get_name(file_info->FileName, + le32_to_cpu(file_info->FileNameLength), + local_nls); + if (IS_ERR(new_name)) { + rc = PTR_ERR(new_name); + goto out; + } + + if (strchr(new_name, ':')) { + int s_type; + char *xattr_stream_name, *stream_name = NULL; + size_t xattr_stream_size; + int len; + + rc = parse_stream_name(new_name, &stream_name, &s_type); + if (rc < 0) + goto out; + + len = strlen(new_name); + if (len > 0 && new_name[len - 1] != '/') { + pr_err("not allow base filename in rename\n"); + rc = -ESHARE; + goto out; + } + + rc = ksmbd_vfs_xattr_stream_name(stream_name, + &xattr_stream_name, + &xattr_stream_size, + s_type); + if (rc) + goto out; + + rc = ksmbd_vfs_setxattr(user_ns, + fp->filp->f_path.dentry, + xattr_stream_name, + NULL, 0, 0); + if (rc < 0) { + pr_err("failed to store stream name in xattr: %d\n", + rc); + rc = -EINVAL; + goto out; + } + + goto out; + } + + ksmbd_debug(SMB, "new name %s\n", new_name); + rc = ksmbd_vfs_kern_path(work, new_name, LOOKUP_NO_SYMLINKS, &path, 1); + if (rc) { + if (rc != -ENOENT) + goto out; + file_present = false; + } else { + path_put(&path); + } + + if (ksmbd_share_veto_filename(share, new_name)) { + rc = -ENOENT; + ksmbd_debug(SMB, "Can't rename vetoed file: %s\n", new_name); + goto out; + } + + if (file_info->ReplaceIfExists) { + if (file_present) { + rc = ksmbd_vfs_remove_file(work, new_name); + if (rc) { + if (rc != -ENOTEMPTY) + rc = -EINVAL; + ksmbd_debug(SMB, "cannot delete %s, rc %d\n", + new_name, rc); + goto out; + } + } + } else { + if (file_present && + strncmp(old_name, path.dentry->d_name.name, strlen(old_name))) { + rc = -EEXIST; + ksmbd_debug(SMB, + "cannot rename already existing file\n"); + goto out; + } + } + + rc = ksmbd_vfs_fp_rename(work, fp, new_name); +out: + kfree(pathname); + if (!IS_ERR(new_name)) + kfree(new_name); + return rc; +} + +static int smb2_create_link(struct ksmbd_work *work, + struct ksmbd_share_config *share, + struct smb2_file_link_info *file_info, + unsigned int buf_len, struct file *filp, + struct nls_table *local_nls) +{ + char *link_name = NULL, *target_name = NULL, *pathname = NULL; + struct path path; + bool file_present = true; + int rc; + + if (buf_len < (u64)sizeof(struct smb2_file_link_info) + + le32_to_cpu(file_info->FileNameLength)) + return -EINVAL; + + ksmbd_debug(SMB, "setting FILE_LINK_INFORMATION\n"); + pathname = kmalloc(PATH_MAX, GFP_KERNEL); + if (!pathname) + return -ENOMEM; + + link_name = smb2_get_name(file_info->FileName, + le32_to_cpu(file_info->FileNameLength), + local_nls); + if (IS_ERR(link_name) || S_ISDIR(file_inode(filp)->i_mode)) { + rc = -EINVAL; + goto out; + } + + ksmbd_debug(SMB, "link name is %s\n", link_name); + target_name = file_path(filp, pathname, PATH_MAX); + if (IS_ERR(target_name)) { + rc = -EINVAL; + goto out; + } + + ksmbd_debug(SMB, "target name is %s\n", target_name); + rc = ksmbd_vfs_kern_path(work, link_name, LOOKUP_NO_SYMLINKS, &path, 0); + if (rc) { + if (rc != -ENOENT) + goto out; + file_present = false; + } else { + path_put(&path); + } + + if (file_info->ReplaceIfExists) { + if (file_present) { + rc = ksmbd_vfs_remove_file(work, link_name); + if (rc) { + rc = -EINVAL; + ksmbd_debug(SMB, "cannot delete %s\n", + link_name); + goto out; + } + } + } else { + if (file_present) { + rc = -EEXIST; + ksmbd_debug(SMB, "link already exists\n"); + goto out; + } + } + + rc = ksmbd_vfs_link(work, target_name, link_name); + if (rc) + rc = -EINVAL; +out: + if (!IS_ERR(link_name)) + kfree(link_name); + kfree(pathname); + return rc; +} + +static int set_file_basic_info(struct ksmbd_file *fp, + struct smb2_file_basic_info *file_info, + struct ksmbd_share_config *share) +{ + struct iattr attrs; + struct file *filp; + struct inode *inode; + struct user_namespace *user_ns; + int rc = 0; + + if (!(fp->daccess & FILE_WRITE_ATTRIBUTES_LE)) + return -EACCES; + + attrs.ia_valid = 0; + filp = fp->filp; + inode = file_inode(filp); + user_ns = file_mnt_user_ns(filp); + + if (file_info->CreationTime) + fp->create_time = le64_to_cpu(file_info->CreationTime); + + if (file_info->LastAccessTime) { + attrs.ia_atime = ksmbd_NTtimeToUnix(file_info->LastAccessTime); + attrs.ia_valid |= (ATTR_ATIME | ATTR_ATIME_SET); + } + + attrs.ia_valid |= ATTR_CTIME; + if (file_info->ChangeTime) + attrs.ia_ctime = ksmbd_NTtimeToUnix(file_info->ChangeTime); + else + attrs.ia_ctime = inode->i_ctime; + + if (file_info->LastWriteTime) { + attrs.ia_mtime = ksmbd_NTtimeToUnix(file_info->LastWriteTime); + attrs.ia_valid |= (ATTR_MTIME | ATTR_MTIME_SET); + } + + if (file_info->Attributes) { + if (!S_ISDIR(inode->i_mode) && + file_info->Attributes & FILE_ATTRIBUTE_DIRECTORY_LE) { + pr_err("can't change a file to a directory\n"); + return -EINVAL; + } + + if (!(S_ISDIR(inode->i_mode) && file_info->Attributes == FILE_ATTRIBUTE_NORMAL_LE)) + fp->f_ci->m_fattr = file_info->Attributes | + (fp->f_ci->m_fattr & FILE_ATTRIBUTE_DIRECTORY_LE); + } + + if (test_share_config_flag(share, KSMBD_SHARE_FLAG_STORE_DOS_ATTRS) && + (file_info->CreationTime || file_info->Attributes)) { + struct xattr_dos_attrib da = {0}; + + da.version = 4; + da.itime = fp->itime; + da.create_time = fp->create_time; + da.attr = le32_to_cpu(fp->f_ci->m_fattr); + da.flags = XATTR_DOSINFO_ATTRIB | XATTR_DOSINFO_CREATE_TIME | + XATTR_DOSINFO_ITIME; + + rc = ksmbd_vfs_set_dos_attrib_xattr(user_ns, + filp->f_path.dentry, &da); + if (rc) + ksmbd_debug(SMB, + "failed to restore file attribute in EA\n"); + rc = 0; + } + + if (attrs.ia_valid) { + struct dentry *dentry = filp->f_path.dentry; + struct inode *inode = d_inode(dentry); + + if (IS_IMMUTABLE(inode) || IS_APPEND(inode)) + return -EACCES; + + inode_lock(inode); + inode->i_ctime = attrs.ia_ctime; + attrs.ia_valid &= ~ATTR_CTIME; + rc = notify_change(user_ns, dentry, &attrs, NULL); + inode_unlock(inode); + } + return rc; +} + +static int set_file_allocation_info(struct ksmbd_work *work, + struct ksmbd_file *fp, + struct smb2_file_alloc_info *file_alloc_info) +{ + /* + * TODO : It's working fine only when store dos attributes + * is not yes. need to implement a logic which works + * properly with any smb.conf option + */ + + loff_t alloc_blks; + struct inode *inode; + int rc; + + if (!(fp->daccess & FILE_WRITE_DATA_LE)) + return -EACCES; + + alloc_blks = (le64_to_cpu(file_alloc_info->AllocationSize) + 511) >> 9; + inode = file_inode(fp->filp); + + if (alloc_blks > inode->i_blocks) { + smb_break_all_levII_oplock(work, fp, 1); + rc = vfs_fallocate(fp->filp, FALLOC_FL_KEEP_SIZE, 0, + alloc_blks * 512); + if (rc && rc != -EOPNOTSUPP) { + pr_err("vfs_fallocate is failed : %d\n", rc); + return rc; + } + } else if (alloc_blks < inode->i_blocks) { + loff_t size; + + /* + * Allocation size could be smaller than original one + * which means allocated blocks in file should be + * deallocated. use truncate to cut out it, but inode + * size is also updated with truncate offset. + * inode size is retained by backup inode size. + */ + size = i_size_read(inode); + rc = ksmbd_vfs_truncate(work, fp, alloc_blks * 512); + if (rc) { + pr_err("truncate failed!, err %d\n", rc); + return rc; + } + if (size < alloc_blks * 512) + i_size_write(inode, size); + } + return 0; +} + +static int set_end_of_file_info(struct ksmbd_work *work, struct ksmbd_file *fp, + struct smb2_file_eof_info *file_eof_info) +{ + loff_t newsize; + struct inode *inode; + int rc; + + if (!(fp->daccess & FILE_WRITE_DATA_LE)) + return -EACCES; + + newsize = le64_to_cpu(file_eof_info->EndOfFile); + inode = file_inode(fp->filp); + + /* + * If FILE_END_OF_FILE_INFORMATION of set_info_file is called + * on FAT32 shared device, truncate execution time is too long + * and network error could cause from windows client. because + * truncate of some filesystem like FAT32 fill zero data in + * truncated range. + */ + if (inode->i_sb->s_magic != MSDOS_SUPER_MAGIC) { + ksmbd_debug(SMB, "truncated to newsize %lld\n", newsize); + rc = ksmbd_vfs_truncate(work, fp, newsize); + if (rc) { + ksmbd_debug(SMB, "truncate failed!, err %d\n", rc); + if (rc != -EAGAIN) + rc = -EBADF; + return rc; + } + } + return 0; +} + +static int set_rename_info(struct ksmbd_work *work, struct ksmbd_file *fp, + struct smb2_file_rename_info *rename_info, + unsigned int buf_len) +{ + struct user_namespace *user_ns; + struct ksmbd_file *parent_fp; + struct dentry *parent; + struct dentry *dentry = fp->filp->f_path.dentry; + int ret; + + if (!(fp->daccess & FILE_DELETE_LE)) { + pr_err("no right to delete : 0x%x\n", fp->daccess); + return -EACCES; + } + + if (buf_len < (u64)sizeof(struct smb2_file_rename_info) + + le32_to_cpu(rename_info->FileNameLength)) + return -EINVAL; + + user_ns = file_mnt_user_ns(fp->filp); + if (ksmbd_stream_fd(fp)) + goto next; + + parent = dget_parent(dentry); + ret = ksmbd_vfs_lock_parent(user_ns, parent, dentry); + if (ret) { + dput(parent); + return ret; + } + + parent_fp = ksmbd_lookup_fd_inode(d_inode(parent)); + inode_unlock(d_inode(parent)); + dput(parent); + + if (parent_fp) { + if (parent_fp->daccess & FILE_DELETE_LE) { + pr_err("parent dir is opened with delete access\n"); + ksmbd_fd_put(work, parent_fp); + return -ESHARE; + } + ksmbd_fd_put(work, parent_fp); + } +next: + return smb2_rename(work, fp, user_ns, rename_info, + work->conn->local_nls); +} + +static int set_file_disposition_info(struct ksmbd_file *fp, + struct smb2_file_disposition_info *file_info) +{ + struct inode *inode; + + if (!(fp->daccess & FILE_DELETE_LE)) { + pr_err("no right to delete : 0x%x\n", fp->daccess); + return -EACCES; + } + + inode = file_inode(fp->filp); + if (file_info->DeletePending) { + if (S_ISDIR(inode->i_mode) && + ksmbd_vfs_empty_dir(fp) == -ENOTEMPTY) + return -EBUSY; + ksmbd_set_inode_pending_delete(fp); + } else { + ksmbd_clear_inode_pending_delete(fp); + } + return 0; +} + +static int set_file_position_info(struct ksmbd_file *fp, + struct smb2_file_pos_info *file_info) +{ + loff_t current_byte_offset; + unsigned long sector_size; + struct inode *inode; + + inode = file_inode(fp->filp); + current_byte_offset = le64_to_cpu(file_info->CurrentByteOffset); + sector_size = inode->i_sb->s_blocksize; + + if (current_byte_offset < 0 || + (fp->coption == FILE_NO_INTERMEDIATE_BUFFERING_LE && + current_byte_offset & (sector_size - 1))) { + pr_err("CurrentByteOffset is not valid : %llu\n", + current_byte_offset); + return -EINVAL; + } + + fp->filp->f_pos = current_byte_offset; + return 0; +} + +static int set_file_mode_info(struct ksmbd_file *fp, + struct smb2_file_mode_info *file_info) +{ + __le32 mode; + + mode = file_info->Mode; + + if ((mode & ~FILE_MODE_INFO_MASK)) { + pr_err("Mode is not valid : 0x%x\n", le32_to_cpu(mode)); + return -EINVAL; + } + + /* + * TODO : need to implement consideration for + * FILE_SYNCHRONOUS_IO_ALERT and FILE_SYNCHRONOUS_IO_NONALERT + */ + ksmbd_vfs_set_fadvise(fp->filp, mode); + fp->coption = mode; + return 0; +} + +/** + * smb2_set_info_file() - handler for smb2 set info command + * @work: smb work containing set info command buffer + * @fp: ksmbd_file pointer + * @req: request buffer pointer + * @share: ksmbd_share_config pointer + * + * Return: 0 on success, otherwise error + * TODO: need to implement an error handling for STATUS_INFO_LENGTH_MISMATCH + */ +static int smb2_set_info_file(struct ksmbd_work *work, struct ksmbd_file *fp, + struct smb2_set_info_req *req, + struct ksmbd_share_config *share) +{ + unsigned int buf_len = le32_to_cpu(req->BufferLength); + + switch (req->FileInfoClass) { + case FILE_BASIC_INFORMATION: + { + if (buf_len < sizeof(struct smb2_file_basic_info)) + return -EINVAL; + + return set_file_basic_info(fp, (struct smb2_file_basic_info *)req->Buffer, share); + } + case FILE_ALLOCATION_INFORMATION: + { + if (buf_len < sizeof(struct smb2_file_alloc_info)) + return -EINVAL; + + return set_file_allocation_info(work, fp, + (struct smb2_file_alloc_info *)req->Buffer); + } + case FILE_END_OF_FILE_INFORMATION: + { + if (buf_len < sizeof(struct smb2_file_eof_info)) + return -EINVAL; + + return set_end_of_file_info(work, fp, + (struct smb2_file_eof_info *)req->Buffer); + } + case FILE_RENAME_INFORMATION: + { + if (!test_tree_conn_flag(work->tcon, KSMBD_TREE_CONN_FLAG_WRITABLE)) { + ksmbd_debug(SMB, + "User does not have write permission\n"); + return -EACCES; + } + + if (buf_len < sizeof(struct smb2_file_rename_info)) + return -EINVAL; + + return set_rename_info(work, fp, + (struct smb2_file_rename_info *)req->Buffer, + buf_len); + } + case FILE_LINK_INFORMATION: + { + if (buf_len < sizeof(struct smb2_file_link_info)) + return -EINVAL; + + return smb2_create_link(work, work->tcon->share_conf, + (struct smb2_file_link_info *)req->Buffer, + buf_len, fp->filp, + work->conn->local_nls); + } + case FILE_DISPOSITION_INFORMATION: + { + if (!test_tree_conn_flag(work->tcon, KSMBD_TREE_CONN_FLAG_WRITABLE)) { + ksmbd_debug(SMB, + "User does not have write permission\n"); + return -EACCES; + } + + if (buf_len < sizeof(struct smb2_file_disposition_info)) + return -EINVAL; + + return set_file_disposition_info(fp, + (struct smb2_file_disposition_info *)req->Buffer); + } + case FILE_FULL_EA_INFORMATION: + { + if (!(fp->daccess & FILE_WRITE_EA_LE)) { + pr_err("Not permitted to write ext attr: 0x%x\n", + fp->daccess); + return -EACCES; + } + + if (buf_len < sizeof(struct smb2_ea_info)) + return -EINVAL; + + return smb2_set_ea((struct smb2_ea_info *)req->Buffer, + buf_len, &fp->filp->f_path); + } + case FILE_POSITION_INFORMATION: + { + if (buf_len < sizeof(struct smb2_file_pos_info)) + return -EINVAL; + + return set_file_position_info(fp, (struct smb2_file_pos_info *)req->Buffer); + } + case FILE_MODE_INFORMATION: + { + if (buf_len < sizeof(struct smb2_file_mode_info)) + return -EINVAL; + + return set_file_mode_info(fp, (struct smb2_file_mode_info *)req->Buffer); + } + } + + pr_err("Unimplemented Fileinfoclass :%d\n", req->FileInfoClass); + return -EOPNOTSUPP; +} + +static int smb2_set_info_sec(struct ksmbd_file *fp, int addition_info, + char *buffer, int buf_len) +{ + struct smb_ntsd *pntsd = (struct smb_ntsd *)buffer; + + fp->saccess |= FILE_SHARE_DELETE_LE; + + return set_info_sec(fp->conn, fp->tcon, &fp->filp->f_path, pntsd, + buf_len, false); +} + +/** + * smb2_set_info() - handler for smb2 set info command handler + * @work: smb work containing set info request buffer + * + * Return: 0 on success, otherwise error + */ +int smb2_set_info(struct ksmbd_work *work) +{ + struct smb2_set_info_req *req; + struct smb2_set_info_rsp *rsp; + struct ksmbd_file *fp; + int rc = 0; + unsigned int id = KSMBD_NO_FID, pid = KSMBD_NO_FID; + + ksmbd_debug(SMB, "Received set info request\n"); + + if (work->next_smb2_rcv_hdr_off) { + req = ksmbd_req_buf_next(work); + rsp = ksmbd_resp_buf_next(work); + if (!has_file_id(req->VolatileFileId)) { + ksmbd_debug(SMB, "Compound request set FID = %llu\n", + work->compound_fid); + id = work->compound_fid; + pid = work->compound_pfid; + } + } else { + req = smb2_get_msg(work->request_buf); + rsp = smb2_get_msg(work->response_buf); + } + + if (!has_file_id(id)) { + id = req->VolatileFileId; + pid = req->PersistentFileId; + } + + fp = ksmbd_lookup_fd_slow(work, id, pid); + if (!fp) { + ksmbd_debug(SMB, "Invalid id for close: %u\n", id); + rc = -ENOENT; + goto err_out; + } + + switch (req->InfoType) { + case SMB2_O_INFO_FILE: + ksmbd_debug(SMB, "GOT SMB2_O_INFO_FILE\n"); + rc = smb2_set_info_file(work, fp, req, work->tcon->share_conf); + break; + case SMB2_O_INFO_SECURITY: + ksmbd_debug(SMB, "GOT SMB2_O_INFO_SECURITY\n"); + if (ksmbd_override_fsids(work)) { + rc = -ENOMEM; + goto err_out; + } + rc = smb2_set_info_sec(fp, + le32_to_cpu(req->AdditionalInformation), + req->Buffer, + le32_to_cpu(req->BufferLength)); + ksmbd_revert_fsids(work); + break; + default: + rc = -EOPNOTSUPP; + } + + if (rc < 0) + goto err_out; + + rsp->StructureSize = cpu_to_le16(2); + inc_rfc1001_len(work->response_buf, 2); + ksmbd_fd_put(work, fp); + return 0; + +err_out: + if (rc == -EACCES || rc == -EPERM || rc == -EXDEV) + rsp->hdr.Status = STATUS_ACCESS_DENIED; + else if (rc == -EINVAL) + rsp->hdr.Status = STATUS_INVALID_PARAMETER; + else if (rc == -ESHARE) + rsp->hdr.Status = STATUS_SHARING_VIOLATION; + else if (rc == -ENOENT) + rsp->hdr.Status = STATUS_OBJECT_NAME_INVALID; + else if (rc == -EBUSY || rc == -ENOTEMPTY) + rsp->hdr.Status = STATUS_DIRECTORY_NOT_EMPTY; + else if (rc == -EAGAIN) + rsp->hdr.Status = STATUS_FILE_LOCK_CONFLICT; + else if (rc == -EBADF || rc == -ESTALE) + rsp->hdr.Status = STATUS_INVALID_HANDLE; + else if (rc == -EEXIST) + rsp->hdr.Status = STATUS_OBJECT_NAME_COLLISION; + else if (rsp->hdr.Status == 0 || rc == -EOPNOTSUPP) + rsp->hdr.Status = STATUS_INVALID_INFO_CLASS; + smb2_set_err_rsp(work); + ksmbd_fd_put(work, fp); + ksmbd_debug(SMB, "error while processing smb2 query rc = %d\n", rc); + return rc; +} + +/** + * smb2_read_pipe() - handler for smb2 read from IPC pipe + * @work: smb work containing read IPC pipe command buffer + * + * Return: 0 on success, otherwise error + */ +static noinline int smb2_read_pipe(struct ksmbd_work *work) +{ + int nbytes = 0, err; + u64 id; + struct ksmbd_rpc_command *rpc_resp; + struct smb2_read_req *req = smb2_get_msg(work->request_buf); + struct smb2_read_rsp *rsp = smb2_get_msg(work->response_buf); + + id = req->VolatileFileId; + + inc_rfc1001_len(work->response_buf, 16); + rpc_resp = ksmbd_rpc_read(work->sess, id); + if (rpc_resp) { + if (rpc_resp->flags != KSMBD_RPC_OK) { + err = -EINVAL; + goto out; + } + + work->aux_payload_buf = + kvmalloc(rpc_resp->payload_sz, GFP_KERNEL | __GFP_ZERO); + if (!work->aux_payload_buf) { + err = -ENOMEM; + goto out; + } + + memcpy(work->aux_payload_buf, rpc_resp->payload, + rpc_resp->payload_sz); + + nbytes = rpc_resp->payload_sz; + work->resp_hdr_sz = get_rfc1002_len(work->response_buf) + 4; + work->aux_payload_sz = nbytes; + kvfree(rpc_resp); + } + + rsp->StructureSize = cpu_to_le16(17); + rsp->DataOffset = 80; + rsp->Reserved = 0; + rsp->DataLength = cpu_to_le32(nbytes); + rsp->DataRemaining = 0; + rsp->Flags = 0; + inc_rfc1001_len(work->response_buf, nbytes); + return 0; + +out: + rsp->hdr.Status = STATUS_UNEXPECTED_IO_ERROR; + smb2_set_err_rsp(work); + kvfree(rpc_resp); + return err; +} + +static int smb2_set_remote_key_for_rdma(struct ksmbd_work *work, + struct smb2_buffer_desc_v1 *desc, + __le32 Channel, + __le16 ChannelInfoLength) +{ + unsigned int i, ch_count; + + if (work->conn->dialect == SMB30_PROT_ID && + Channel != SMB2_CHANNEL_RDMA_V1) + return -EINVAL; + + ch_count = le16_to_cpu(ChannelInfoLength) / sizeof(*desc); + if (ksmbd_debug_types & KSMBD_DEBUG_RDMA) { + for (i = 0; i < ch_count; i++) { + pr_info("RDMA r/w request %#x: token %#x, length %#x\n", + i, + le32_to_cpu(desc[i].token), + le32_to_cpu(desc[i].length)); + } + } + if (!ch_count) + return -EINVAL; + + work->need_invalidate_rkey = + (Channel == SMB2_CHANNEL_RDMA_V1_INVALIDATE); + if (Channel == SMB2_CHANNEL_RDMA_V1_INVALIDATE) + work->remote_key = le32_to_cpu(desc->token); + return 0; +} + +static ssize_t smb2_read_rdma_channel(struct ksmbd_work *work, + struct smb2_read_req *req, void *data_buf, + size_t length) +{ + int err; + + err = ksmbd_conn_rdma_write(work->conn, data_buf, length, + (struct smb2_buffer_desc_v1 *) + ((char *)req + le16_to_cpu(req->ReadChannelInfoOffset)), + le16_to_cpu(req->ReadChannelInfoLength)); + if (err) + return err; + + return length; +} + +/** + * smb2_read() - handler for smb2 read from file + * @work: smb work containing read command buffer + * + * Return: 0 on success, otherwise error + */ +int smb2_read(struct ksmbd_work *work) +{ + struct ksmbd_conn *conn = work->conn; + struct smb2_read_req *req; + struct smb2_read_rsp *rsp; + struct ksmbd_file *fp = NULL; + loff_t offset; + size_t length, mincount; + ssize_t nbytes = 0, remain_bytes = 0; + int err = 0; + bool is_rdma_channel = false; + unsigned int max_read_size = conn->vals->max_read_size; + + WORK_BUFFERS(work, req, rsp); + + if (test_share_config_flag(work->tcon->share_conf, + KSMBD_SHARE_FLAG_PIPE)) { + ksmbd_debug(SMB, "IPC pipe read request\n"); + return smb2_read_pipe(work); + } + + if (req->Channel == SMB2_CHANNEL_RDMA_V1_INVALIDATE || + req->Channel == SMB2_CHANNEL_RDMA_V1) { + is_rdma_channel = true; + max_read_size = get_smbd_max_read_write_size(); + } + + if (is_rdma_channel == true) { + unsigned int ch_offset = le16_to_cpu(req->ReadChannelInfoOffset); + + if (ch_offset < offsetof(struct smb2_read_req, Buffer)) { + err = -EINVAL; + goto out; + } + err = smb2_set_remote_key_for_rdma(work, + (struct smb2_buffer_desc_v1 *) + ((char *)req + ch_offset), + req->Channel, + req->ReadChannelInfoLength); + if (err) + goto out; + } + + fp = ksmbd_lookup_fd_slow(work, req->VolatileFileId, req->PersistentFileId); + if (!fp) { + err = -ENOENT; + goto out; + } + + if (!(fp->daccess & (FILE_READ_DATA_LE | FILE_READ_ATTRIBUTES_LE))) { + pr_err("Not permitted to read : 0x%x\n", fp->daccess); + err = -EACCES; + goto out; + } + + offset = le64_to_cpu(req->Offset); + length = le32_to_cpu(req->Length); + mincount = le32_to_cpu(req->MinimumCount); + + if (length > max_read_size) { + ksmbd_debug(SMB, "limiting read size to max size(%u)\n", + max_read_size); + err = -EINVAL; + goto out; + } + + ksmbd_debug(SMB, "filename %pD, offset %lld, len %zu\n", + fp->filp, offset, length); + + work->aux_payload_buf = kvmalloc(length, GFP_KERNEL | __GFP_ZERO); + if (!work->aux_payload_buf) { + err = -ENOMEM; + goto out; + } + + nbytes = ksmbd_vfs_read(work, fp, length, &offset); + if (nbytes < 0) { + err = nbytes; + goto out; + } + + if ((nbytes == 0 && length != 0) || nbytes < mincount) { + kvfree(work->aux_payload_buf); + work->aux_payload_buf = NULL; + rsp->hdr.Status = STATUS_END_OF_FILE; + smb2_set_err_rsp(work); + ksmbd_fd_put(work, fp); + return 0; + } + + ksmbd_debug(SMB, "nbytes %zu, offset %lld mincount %zu\n", + nbytes, offset, mincount); + + if (is_rdma_channel == true) { + /* write data to the client using rdma channel */ + remain_bytes = smb2_read_rdma_channel(work, req, + work->aux_payload_buf, + nbytes); + kvfree(work->aux_payload_buf); + work->aux_payload_buf = NULL; + + nbytes = 0; + if (remain_bytes < 0) { + err = (int)remain_bytes; + goto out; + } + } + + rsp->StructureSize = cpu_to_le16(17); + rsp->DataOffset = 80; + rsp->Reserved = 0; + rsp->DataLength = cpu_to_le32(nbytes); + rsp->DataRemaining = cpu_to_le32(remain_bytes); + rsp->Flags = 0; + inc_rfc1001_len(work->response_buf, 16); + work->resp_hdr_sz = get_rfc1002_len(work->response_buf) + 4; + work->aux_payload_sz = nbytes; + inc_rfc1001_len(work->response_buf, nbytes); + ksmbd_fd_put(work, fp); + return 0; + +out: + if (err) { + if (err == -EISDIR) + rsp->hdr.Status = STATUS_INVALID_DEVICE_REQUEST; + else if (err == -EAGAIN) + rsp->hdr.Status = STATUS_FILE_LOCK_CONFLICT; + else if (err == -ENOENT) + rsp->hdr.Status = STATUS_FILE_CLOSED; + else if (err == -EACCES) + rsp->hdr.Status = STATUS_ACCESS_DENIED; + else if (err == -ESHARE) + rsp->hdr.Status = STATUS_SHARING_VIOLATION; + else if (err == -EINVAL) + rsp->hdr.Status = STATUS_INVALID_PARAMETER; + else + rsp->hdr.Status = STATUS_INVALID_HANDLE; + + smb2_set_err_rsp(work); + } + ksmbd_fd_put(work, fp); + return err; +} + +/** + * smb2_write_pipe() - handler for smb2 write on IPC pipe + * @work: smb work containing write IPC pipe command buffer + * + * Return: 0 on success, otherwise error + */ +static noinline int smb2_write_pipe(struct ksmbd_work *work) +{ + struct smb2_write_req *req = smb2_get_msg(work->request_buf); + struct smb2_write_rsp *rsp = smb2_get_msg(work->response_buf); + struct ksmbd_rpc_command *rpc_resp; + u64 id = 0; + int err = 0, ret = 0; + char *data_buf; + size_t length; + + length = le32_to_cpu(req->Length); + id = req->VolatileFileId; + + if ((u64)le16_to_cpu(req->DataOffset) + length > + get_rfc1002_len(work->request_buf)) { + pr_err("invalid write data offset %u, smb_len %u\n", + le16_to_cpu(req->DataOffset), + get_rfc1002_len(work->request_buf)); + err = -EINVAL; + goto out; + } + + data_buf = (char *)(((char *)&req->hdr.ProtocolId) + + le16_to_cpu(req->DataOffset)); + + rpc_resp = ksmbd_rpc_write(work->sess, id, data_buf, length); + if (rpc_resp) { + if (rpc_resp->flags == KSMBD_RPC_ENOTIMPLEMENTED) { + rsp->hdr.Status = STATUS_NOT_SUPPORTED; + kvfree(rpc_resp); + smb2_set_err_rsp(work); + return -EOPNOTSUPP; + } + if (rpc_resp->flags != KSMBD_RPC_OK) { + rsp->hdr.Status = STATUS_INVALID_HANDLE; + smb2_set_err_rsp(work); + kvfree(rpc_resp); + return ret; + } + kvfree(rpc_resp); + } + + rsp->StructureSize = cpu_to_le16(17); + rsp->DataOffset = 0; + rsp->Reserved = 0; + rsp->DataLength = cpu_to_le32(length); + rsp->DataRemaining = 0; + rsp->Reserved2 = 0; + inc_rfc1001_len(work->response_buf, 16); + return 0; +out: + if (err) { + rsp->hdr.Status = STATUS_INVALID_HANDLE; + smb2_set_err_rsp(work); + } + + return err; +} + +static ssize_t smb2_write_rdma_channel(struct ksmbd_work *work, + struct smb2_write_req *req, + struct ksmbd_file *fp, + loff_t offset, size_t length, bool sync) +{ + char *data_buf; + int ret; + ssize_t nbytes; + + data_buf = kvmalloc(length, GFP_KERNEL | __GFP_ZERO); + if (!data_buf) + return -ENOMEM; + + ret = ksmbd_conn_rdma_read(work->conn, data_buf, length, + (struct smb2_buffer_desc_v1 *) + ((char *)req + le16_to_cpu(req->WriteChannelInfoOffset)), + le16_to_cpu(req->WriteChannelInfoLength)); + if (ret < 0) { + kvfree(data_buf); + return ret; + } + + ret = ksmbd_vfs_write(work, fp, data_buf, length, &offset, sync, &nbytes); + kvfree(data_buf); + if (ret < 0) + return ret; + + return nbytes; +} + +/** + * smb2_write() - handler for smb2 write from file + * @work: smb work containing write command buffer + * + * Return: 0 on success, otherwise error + */ +int smb2_write(struct ksmbd_work *work) +{ + struct smb2_write_req *req; + struct smb2_write_rsp *rsp; + struct ksmbd_file *fp = NULL; + loff_t offset; + size_t length; + ssize_t nbytes; + char *data_buf; + bool writethrough = false, is_rdma_channel = false; + int err = 0; + unsigned int max_write_size = work->conn->vals->max_write_size; + + WORK_BUFFERS(work, req, rsp); + + if (test_share_config_flag(work->tcon->share_conf, KSMBD_SHARE_FLAG_PIPE)) { + ksmbd_debug(SMB, "IPC pipe write request\n"); + return smb2_write_pipe(work); + } + + offset = le64_to_cpu(req->Offset); + length = le32_to_cpu(req->Length); + + if (req->Channel == SMB2_CHANNEL_RDMA_V1 || + req->Channel == SMB2_CHANNEL_RDMA_V1_INVALIDATE) { + is_rdma_channel = true; + max_write_size = get_smbd_max_read_write_size(); + length = le32_to_cpu(req->RemainingBytes); + } + + if (is_rdma_channel == true) { + unsigned int ch_offset = le16_to_cpu(req->WriteChannelInfoOffset); + + if (req->Length != 0 || req->DataOffset != 0 || + ch_offset < offsetof(struct smb2_write_req, Buffer)) { + err = -EINVAL; + goto out; + } + err = smb2_set_remote_key_for_rdma(work, + (struct smb2_buffer_desc_v1 *) + ((char *)req + ch_offset), + req->Channel, + req->WriteChannelInfoLength); + if (err) + goto out; + } + + if (!test_tree_conn_flag(work->tcon, KSMBD_TREE_CONN_FLAG_WRITABLE)) { + ksmbd_debug(SMB, "User does not have write permission\n"); + err = -EACCES; + goto out; + } + + fp = ksmbd_lookup_fd_slow(work, req->VolatileFileId, req->PersistentFileId); + if (!fp) { + err = -ENOENT; + goto out; + } + + if (!(fp->daccess & (FILE_WRITE_DATA_LE | FILE_READ_ATTRIBUTES_LE))) { + pr_err("Not permitted to write : 0x%x\n", fp->daccess); + err = -EACCES; + goto out; + } + + if (length > max_write_size) { + ksmbd_debug(SMB, "limiting write size to max size(%u)\n", + max_write_size); + err = -EINVAL; + goto out; + } + + ksmbd_debug(SMB, "flags %u\n", le32_to_cpu(req->Flags)); + if (le32_to_cpu(req->Flags) & SMB2_WRITEFLAG_WRITE_THROUGH) + writethrough = true; + + if (is_rdma_channel == false) { + if (le16_to_cpu(req->DataOffset) < + offsetof(struct smb2_write_req, Buffer)) { + err = -EINVAL; + goto out; + } + + data_buf = (char *)(((char *)&req->hdr.ProtocolId) + + le16_to_cpu(req->DataOffset)); + + ksmbd_debug(SMB, "filename %pD, offset %lld, len %zu\n", + fp->filp, offset, length); + err = ksmbd_vfs_write(work, fp, data_buf, length, &offset, + writethrough, &nbytes); + if (err < 0) + goto out; + } else { + /* read data from the client using rdma channel, and + * write the data. + */ + nbytes = smb2_write_rdma_channel(work, req, fp, offset, length, + writethrough); + if (nbytes < 0) { + err = (int)nbytes; + goto out; + } + } + + rsp->StructureSize = cpu_to_le16(17); + rsp->DataOffset = 0; + rsp->Reserved = 0; + rsp->DataLength = cpu_to_le32(nbytes); + rsp->DataRemaining = 0; + rsp->Reserved2 = 0; + inc_rfc1001_len(work->response_buf, 16); + ksmbd_fd_put(work, fp); + return 0; + +out: + if (err == -EAGAIN) + rsp->hdr.Status = STATUS_FILE_LOCK_CONFLICT; + else if (err == -ENOSPC || err == -EFBIG) + rsp->hdr.Status = STATUS_DISK_FULL; + else if (err == -ENOENT) + rsp->hdr.Status = STATUS_FILE_CLOSED; + else if (err == -EACCES) + rsp->hdr.Status = STATUS_ACCESS_DENIED; + else if (err == -ESHARE) + rsp->hdr.Status = STATUS_SHARING_VIOLATION; + else if (err == -EINVAL) + rsp->hdr.Status = STATUS_INVALID_PARAMETER; + else + rsp->hdr.Status = STATUS_INVALID_HANDLE; + + smb2_set_err_rsp(work); + ksmbd_fd_put(work, fp); + return err; +} + +/** + * smb2_flush() - handler for smb2 flush file - fsync + * @work: smb work containing flush command buffer + * + * Return: 0 on success, otherwise error + */ +int smb2_flush(struct ksmbd_work *work) +{ + struct smb2_flush_req *req; + struct smb2_flush_rsp *rsp; + int err; + + WORK_BUFFERS(work, req, rsp); + + ksmbd_debug(SMB, "SMB2_FLUSH called for fid %llu\n", req->VolatileFileId); + + err = ksmbd_vfs_fsync(work, req->VolatileFileId, req->PersistentFileId); + if (err) + goto out; + + rsp->StructureSize = cpu_to_le16(4); + rsp->Reserved = 0; + inc_rfc1001_len(work->response_buf, 4); + return 0; + +out: + if (err) { + rsp->hdr.Status = STATUS_INVALID_HANDLE; + smb2_set_err_rsp(work); + } + + return err; +} + +/** + * smb2_cancel() - handler for smb2 cancel command + * @work: smb work containing cancel command buffer + * + * Return: 0 on success, otherwise error + */ +int smb2_cancel(struct ksmbd_work *work) +{ + struct ksmbd_conn *conn = work->conn; + struct smb2_hdr *hdr = smb2_get_msg(work->request_buf); + struct smb2_hdr *chdr; + struct ksmbd_work *iter; + struct list_head *command_list; + + ksmbd_debug(SMB, "smb2 cancel called on mid %llu, async flags 0x%x\n", + hdr->MessageId, hdr->Flags); + + if (hdr->Flags & SMB2_FLAGS_ASYNC_COMMAND) { + command_list = &conn->async_requests; + + spin_lock(&conn->request_lock); + list_for_each_entry(iter, command_list, + async_request_entry) { + chdr = smb2_get_msg(iter->request_buf); + + if (iter->async_id != + le64_to_cpu(hdr->Id.AsyncId)) + continue; + + ksmbd_debug(SMB, + "smb2 with AsyncId %llu cancelled command = 0x%x\n", + le64_to_cpu(hdr->Id.AsyncId), + le16_to_cpu(chdr->Command)); + iter->state = KSMBD_WORK_CANCELLED; + if (iter->cancel_fn) + iter->cancel_fn(iter->cancel_argv); + break; + } + spin_unlock(&conn->request_lock); + } else { + command_list = &conn->requests; + + spin_lock(&conn->request_lock); + list_for_each_entry(iter, command_list, request_entry) { + chdr = smb2_get_msg(iter->request_buf); + + if (chdr->MessageId != hdr->MessageId || + iter == work) + continue; + + ksmbd_debug(SMB, + "smb2 with mid %llu cancelled command = 0x%x\n", + le64_to_cpu(hdr->MessageId), + le16_to_cpu(chdr->Command)); + iter->state = KSMBD_WORK_CANCELLED; + break; + } + spin_unlock(&conn->request_lock); + } + + /* For SMB2_CANCEL command itself send no response*/ + work->send_no_response = 1; + return 0; +} + +struct file_lock *smb_flock_init(struct file *f) +{ + struct file_lock *fl; + + fl = locks_alloc_lock(); + if (!fl) + goto out; + + locks_init_lock(fl); + + fl->fl_owner = f; + fl->fl_pid = current->tgid; + fl->fl_file = f; + fl->fl_flags = FL_POSIX; + fl->fl_ops = NULL; + fl->fl_lmops = NULL; + +out: + return fl; +} + +static int smb2_set_flock_flags(struct file_lock *flock, int flags) +{ + int cmd = -EINVAL; + + /* Checking for wrong flag combination during lock request*/ + switch (flags) { + case SMB2_LOCKFLAG_SHARED: + ksmbd_debug(SMB, "received shared request\n"); + cmd = F_SETLKW; + flock->fl_type = F_RDLCK; + flock->fl_flags |= FL_SLEEP; + break; + case SMB2_LOCKFLAG_EXCLUSIVE: + ksmbd_debug(SMB, "received exclusive request\n"); + cmd = F_SETLKW; + flock->fl_type = F_WRLCK; + flock->fl_flags |= FL_SLEEP; + break; + case SMB2_LOCKFLAG_SHARED | SMB2_LOCKFLAG_FAIL_IMMEDIATELY: + ksmbd_debug(SMB, + "received shared & fail immediately request\n"); + cmd = F_SETLK; + flock->fl_type = F_RDLCK; + break; + case SMB2_LOCKFLAG_EXCLUSIVE | SMB2_LOCKFLAG_FAIL_IMMEDIATELY: + ksmbd_debug(SMB, + "received exclusive & fail immediately request\n"); + cmd = F_SETLK; + flock->fl_type = F_WRLCK; + break; + case SMB2_LOCKFLAG_UNLOCK: + ksmbd_debug(SMB, "received unlock request\n"); + flock->fl_type = F_UNLCK; + cmd = 0; + break; + } + + return cmd; +} + +static struct ksmbd_lock *smb2_lock_init(struct file_lock *flock, + unsigned int cmd, int flags, + struct list_head *lock_list) +{ + struct ksmbd_lock *lock; + + lock = kzalloc(sizeof(struct ksmbd_lock), GFP_KERNEL); + if (!lock) + return NULL; + + lock->cmd = cmd; + lock->fl = flock; + lock->start = flock->fl_start; + lock->end = flock->fl_end; + lock->flags = flags; + if (lock->start == lock->end) + lock->zero_len = 1; + INIT_LIST_HEAD(&lock->clist); + INIT_LIST_HEAD(&lock->flist); + INIT_LIST_HEAD(&lock->llist); + list_add_tail(&lock->llist, lock_list); + + return lock; +} + +static void smb2_remove_blocked_lock(void **argv) +{ + struct file_lock *flock = (struct file_lock *)argv[0]; + + ksmbd_vfs_posix_lock_unblock(flock); + wake_up(&flock->fl_wait); +} + +static inline bool lock_defer_pending(struct file_lock *fl) +{ + /* check pending lock waiters */ + return waitqueue_active(&fl->fl_wait); +} + +/** + * smb2_lock() - handler for smb2 file lock command + * @work: smb work containing lock command buffer + * + * Return: 0 on success, otherwise error + */ +int smb2_lock(struct ksmbd_work *work) +{ + struct smb2_lock_req *req = smb2_get_msg(work->request_buf); + struct smb2_lock_rsp *rsp = smb2_get_msg(work->response_buf); + struct smb2_lock_element *lock_ele; + struct ksmbd_file *fp = NULL; + struct file_lock *flock = NULL; + struct file *filp = NULL; + int lock_count; + int flags = 0; + int cmd = 0; + int err = -EIO, i, rc = 0; + u64 lock_start, lock_length; + struct ksmbd_lock *smb_lock = NULL, *cmp_lock, *tmp, *tmp2; + struct ksmbd_conn *conn; + int nolock = 0; + LIST_HEAD(lock_list); + LIST_HEAD(rollback_list); + int prior_lock = 0; + + ksmbd_debug(SMB, "Received lock request\n"); + fp = ksmbd_lookup_fd_slow(work, req->VolatileFileId, req->PersistentFileId); + if (!fp) { + ksmbd_debug(SMB, "Invalid file id for lock : %llu\n", req->VolatileFileId); + err = -ENOENT; + goto out2; + } + + filp = fp->filp; + lock_count = le16_to_cpu(req->LockCount); + lock_ele = req->locks; + + ksmbd_debug(SMB, "lock count is %d\n", lock_count); + if (!lock_count) { + err = -EINVAL; + goto out2; + } + + for (i = 0; i < lock_count; i++) { + flags = le32_to_cpu(lock_ele[i].Flags); + + flock = smb_flock_init(filp); + if (!flock) + goto out; + + cmd = smb2_set_flock_flags(flock, flags); + + lock_start = le64_to_cpu(lock_ele[i].Offset); + lock_length = le64_to_cpu(lock_ele[i].Length); + if (lock_start > U64_MAX - lock_length) { + pr_err("Invalid lock range requested\n"); + rsp->hdr.Status = STATUS_INVALID_LOCK_RANGE; + goto out; + } + + if (lock_start > OFFSET_MAX) + flock->fl_start = OFFSET_MAX; + else + flock->fl_start = lock_start; + + lock_length = le64_to_cpu(lock_ele[i].Length); + if (lock_length > OFFSET_MAX - flock->fl_start) + lock_length = OFFSET_MAX - flock->fl_start; + + flock->fl_end = flock->fl_start + lock_length; + + if (flock->fl_end < flock->fl_start) { + ksmbd_debug(SMB, + "the end offset(%llx) is smaller than the start offset(%llx)\n", + flock->fl_end, flock->fl_start); + rsp->hdr.Status = STATUS_INVALID_LOCK_RANGE; + goto out; + } + + /* Check conflict locks in one request */ + list_for_each_entry(cmp_lock, &lock_list, llist) { + if (cmp_lock->fl->fl_start <= flock->fl_start && + cmp_lock->fl->fl_end >= flock->fl_end) { + if (cmp_lock->fl->fl_type != F_UNLCK && + flock->fl_type != F_UNLCK) { + pr_err("conflict two locks in one request\n"); + err = -EINVAL; + goto out; + } + } + } + + smb_lock = smb2_lock_init(flock, cmd, flags, &lock_list); + if (!smb_lock) { + err = -EINVAL; + goto out; + } + } + + list_for_each_entry_safe(smb_lock, tmp, &lock_list, llist) { + if (smb_lock->cmd < 0) { + err = -EINVAL; + goto out; + } + + if (!(smb_lock->flags & SMB2_LOCKFLAG_MASK)) { + err = -EINVAL; + goto out; + } + + if ((prior_lock & (SMB2_LOCKFLAG_EXCLUSIVE | SMB2_LOCKFLAG_SHARED) && + smb_lock->flags & SMB2_LOCKFLAG_UNLOCK) || + (prior_lock == SMB2_LOCKFLAG_UNLOCK && + !(smb_lock->flags & SMB2_LOCKFLAG_UNLOCK))) { + err = -EINVAL; + goto out; + } + + prior_lock = smb_lock->flags; + + if (!(smb_lock->flags & SMB2_LOCKFLAG_UNLOCK) && + !(smb_lock->flags & SMB2_LOCKFLAG_FAIL_IMMEDIATELY)) + goto no_check_cl; + + nolock = 1; + /* check locks in connection list */ + down_read(&conn_list_lock); + list_for_each_entry(conn, &conn_list, conns_list) { + spin_lock(&conn->llist_lock); + list_for_each_entry_safe(cmp_lock, tmp2, &conn->lock_list, clist) { + if (file_inode(cmp_lock->fl->fl_file) != + file_inode(smb_lock->fl->fl_file)) + continue; + + if (smb_lock->fl->fl_type == F_UNLCK) { + if (cmp_lock->fl->fl_file == smb_lock->fl->fl_file && + cmp_lock->start == smb_lock->start && + cmp_lock->end == smb_lock->end && + !lock_defer_pending(cmp_lock->fl)) { + nolock = 0; + list_del(&cmp_lock->flist); + list_del(&cmp_lock->clist); + spin_unlock(&conn->llist_lock); + up_read(&conn_list_lock); + + locks_free_lock(cmp_lock->fl); + kfree(cmp_lock); + goto out_check_cl; + } + continue; + } + + if (cmp_lock->fl->fl_file == smb_lock->fl->fl_file) { + if (smb_lock->flags & SMB2_LOCKFLAG_SHARED) + continue; + } else { + if (cmp_lock->flags & SMB2_LOCKFLAG_SHARED) + continue; + } + + /* check zero byte lock range */ + if (cmp_lock->zero_len && !smb_lock->zero_len && + cmp_lock->start > smb_lock->start && + cmp_lock->start < smb_lock->end) { + spin_unlock(&conn->llist_lock); + up_read(&conn_list_lock); + pr_err("previous lock conflict with zero byte lock range\n"); + goto out; + } + + if (smb_lock->zero_len && !cmp_lock->zero_len && + smb_lock->start > cmp_lock->start && + smb_lock->start < cmp_lock->end) { + spin_unlock(&conn->llist_lock); + up_read(&conn_list_lock); + pr_err("current lock conflict with zero byte lock range\n"); + goto out; + } + + if (((cmp_lock->start <= smb_lock->start && + cmp_lock->end > smb_lock->start) || + (cmp_lock->start < smb_lock->end && + cmp_lock->end >= smb_lock->end)) && + !cmp_lock->zero_len && !smb_lock->zero_len) { + spin_unlock(&conn->llist_lock); + up_read(&conn_list_lock); + pr_err("Not allow lock operation on exclusive lock range\n"); + goto out; + } + } + spin_unlock(&conn->llist_lock); + } + up_read(&conn_list_lock); +out_check_cl: + if (smb_lock->fl->fl_type == F_UNLCK && nolock) { + pr_err("Try to unlock nolocked range\n"); + rsp->hdr.Status = STATUS_RANGE_NOT_LOCKED; + goto out; + } + +no_check_cl: + if (smb_lock->zero_len) { + err = 0; + goto skip; + } + + flock = smb_lock->fl; + list_del(&smb_lock->llist); +retry: + rc = vfs_lock_file(filp, smb_lock->cmd, flock, NULL); +skip: + if (flags & SMB2_LOCKFLAG_UNLOCK) { + if (!rc) { + ksmbd_debug(SMB, "File unlocked\n"); + } else if (rc == -ENOENT) { + rsp->hdr.Status = STATUS_NOT_LOCKED; + goto out; + } + locks_free_lock(flock); + kfree(smb_lock); + } else { + if (rc == FILE_LOCK_DEFERRED) { + void **argv; + + ksmbd_debug(SMB, + "would have to wait for getting lock\n"); + spin_lock(&work->conn->llist_lock); + list_add_tail(&smb_lock->clist, + &work->conn->lock_list); + spin_unlock(&work->conn->llist_lock); + list_add(&smb_lock->llist, &rollback_list); + + argv = kmalloc(sizeof(void *), GFP_KERNEL); + if (!argv) { + err = -ENOMEM; + goto out; + } + argv[0] = flock; + + rc = setup_async_work(work, + smb2_remove_blocked_lock, + argv); + if (rc) { + err = -ENOMEM; + goto out; + } + spin_lock(&fp->f_lock); + list_add(&work->fp_entry, &fp->blocked_works); + spin_unlock(&fp->f_lock); + + smb2_send_interim_resp(work, STATUS_PENDING); + + ksmbd_vfs_posix_lock_wait(flock); + + spin_lock(&work->conn->request_lock); + spin_lock(&fp->f_lock); + list_del(&work->fp_entry); + work->cancel_fn = NULL; + kfree(argv); + spin_unlock(&fp->f_lock); + spin_unlock(&work->conn->request_lock); + + if (work->state != KSMBD_WORK_ACTIVE) { + list_del(&smb_lock->llist); + spin_lock(&work->conn->llist_lock); + list_del(&smb_lock->clist); + spin_unlock(&work->conn->llist_lock); + locks_free_lock(flock); + + if (work->state == KSMBD_WORK_CANCELLED) { + rsp->hdr.Status = + STATUS_CANCELLED; + kfree(smb_lock); + smb2_send_interim_resp(work, + STATUS_CANCELLED); + work->send_no_response = 1; + goto out; + } + init_smb2_rsp_hdr(work); + smb2_set_err_rsp(work); + rsp->hdr.Status = + STATUS_RANGE_NOT_LOCKED; + kfree(smb_lock); + goto out2; + } + + list_del(&smb_lock->llist); + spin_lock(&work->conn->llist_lock); + list_del(&smb_lock->clist); + spin_unlock(&work->conn->llist_lock); + + goto retry; + } else if (!rc) { + spin_lock(&work->conn->llist_lock); + list_add_tail(&smb_lock->clist, + &work->conn->lock_list); + list_add_tail(&smb_lock->flist, + &fp->lock_list); + spin_unlock(&work->conn->llist_lock); + list_add(&smb_lock->llist, &rollback_list); + ksmbd_debug(SMB, "successful in taking lock\n"); + } else { + goto out; + } + } + } + + if (atomic_read(&fp->f_ci->op_count) > 1) + smb_break_all_oplock(work, fp); + + rsp->StructureSize = cpu_to_le16(4); + ksmbd_debug(SMB, "successful in taking lock\n"); + rsp->hdr.Status = STATUS_SUCCESS; + rsp->Reserved = 0; + inc_rfc1001_len(work->response_buf, 4); + ksmbd_fd_put(work, fp); + return 0; + +out: + list_for_each_entry_safe(smb_lock, tmp, &lock_list, llist) { + locks_free_lock(smb_lock->fl); + list_del(&smb_lock->llist); + kfree(smb_lock); + } + + list_for_each_entry_safe(smb_lock, tmp, &rollback_list, llist) { + struct file_lock *rlock = NULL; + + rlock = smb_flock_init(filp); + rlock->fl_type = F_UNLCK; + rlock->fl_start = smb_lock->start; + rlock->fl_end = smb_lock->end; + + rc = vfs_lock_file(filp, 0, rlock, NULL); + if (rc) + pr_err("rollback unlock fail : %d\n", rc); + + list_del(&smb_lock->llist); + spin_lock(&work->conn->llist_lock); + if (!list_empty(&smb_lock->flist)) + list_del(&smb_lock->flist); + list_del(&smb_lock->clist); + spin_unlock(&work->conn->llist_lock); + + locks_free_lock(smb_lock->fl); + locks_free_lock(rlock); + kfree(smb_lock); + } +out2: + ksmbd_debug(SMB, "failed in taking lock(flags : %x), err : %d\n", flags, err); + + if (!rsp->hdr.Status) { + if (err == -EINVAL) + rsp->hdr.Status = STATUS_INVALID_PARAMETER; + else if (err == -ENOMEM) + rsp->hdr.Status = STATUS_INSUFFICIENT_RESOURCES; + else if (err == -ENOENT) + rsp->hdr.Status = STATUS_FILE_CLOSED; + else + rsp->hdr.Status = STATUS_LOCK_NOT_GRANTED; + } + + smb2_set_err_rsp(work); + ksmbd_fd_put(work, fp); + return err; +} + +static int fsctl_copychunk(struct ksmbd_work *work, + struct copychunk_ioctl_req *ci_req, + unsigned int cnt_code, + unsigned int input_count, + unsigned long long volatile_id, + unsigned long long persistent_id, + struct smb2_ioctl_rsp *rsp) +{ + struct copychunk_ioctl_rsp *ci_rsp; + struct ksmbd_file *src_fp = NULL, *dst_fp = NULL; + struct srv_copychunk *chunks; + unsigned int i, chunk_count, chunk_count_written = 0; + unsigned int chunk_size_written = 0; + loff_t total_size_written = 0; + int ret = 0; + + ci_rsp = (struct copychunk_ioctl_rsp *)&rsp->Buffer[0]; + + rsp->VolatileFileId = volatile_id; + rsp->PersistentFileId = persistent_id; + ci_rsp->ChunksWritten = + cpu_to_le32(ksmbd_server_side_copy_max_chunk_count()); + ci_rsp->ChunkBytesWritten = + cpu_to_le32(ksmbd_server_side_copy_max_chunk_size()); + ci_rsp->TotalBytesWritten = + cpu_to_le32(ksmbd_server_side_copy_max_total_size()); + + chunks = (struct srv_copychunk *)&ci_req->Chunks[0]; + chunk_count = le32_to_cpu(ci_req->ChunkCount); + if (chunk_count == 0) + goto out; + total_size_written = 0; + + /* verify the SRV_COPYCHUNK_COPY packet */ + if (chunk_count > ksmbd_server_side_copy_max_chunk_count() || + input_count < offsetof(struct copychunk_ioctl_req, Chunks) + + chunk_count * sizeof(struct srv_copychunk)) { + rsp->hdr.Status = STATUS_INVALID_PARAMETER; + return -EINVAL; + } + + for (i = 0; i < chunk_count; i++) { + if (le32_to_cpu(chunks[i].Length) == 0 || + le32_to_cpu(chunks[i].Length) > ksmbd_server_side_copy_max_chunk_size()) + break; + total_size_written += le32_to_cpu(chunks[i].Length); + } + + if (i < chunk_count || + total_size_written > ksmbd_server_side_copy_max_total_size()) { + rsp->hdr.Status = STATUS_INVALID_PARAMETER; + return -EINVAL; + } + + src_fp = ksmbd_lookup_foreign_fd(work, + le64_to_cpu(ci_req->ResumeKey[0])); + dst_fp = ksmbd_lookup_fd_slow(work, volatile_id, persistent_id); + ret = -EINVAL; + if (!src_fp || + src_fp->persistent_id != le64_to_cpu(ci_req->ResumeKey[1])) { + rsp->hdr.Status = STATUS_OBJECT_NAME_NOT_FOUND; + goto out; + } + + if (!dst_fp) { + rsp->hdr.Status = STATUS_FILE_CLOSED; + goto out; + } + + /* + * FILE_READ_DATA should only be included in + * the FSCTL_COPYCHUNK case + */ + if (cnt_code == FSCTL_COPYCHUNK && + !(dst_fp->daccess & (FILE_READ_DATA_LE | FILE_GENERIC_READ_LE))) { + rsp->hdr.Status = STATUS_ACCESS_DENIED; + goto out; + } + + ret = ksmbd_vfs_copy_file_ranges(work, src_fp, dst_fp, + chunks, chunk_count, + &chunk_count_written, + &chunk_size_written, + &total_size_written); + if (ret < 0) { + if (ret == -EACCES) + rsp->hdr.Status = STATUS_ACCESS_DENIED; + if (ret == -EAGAIN) + rsp->hdr.Status = STATUS_FILE_LOCK_CONFLICT; + else if (ret == -EBADF) + rsp->hdr.Status = STATUS_INVALID_HANDLE; + else if (ret == -EFBIG || ret == -ENOSPC) + rsp->hdr.Status = STATUS_DISK_FULL; + else if (ret == -EINVAL) + rsp->hdr.Status = STATUS_INVALID_PARAMETER; + else if (ret == -EISDIR) + rsp->hdr.Status = STATUS_FILE_IS_A_DIRECTORY; + else if (ret == -E2BIG) + rsp->hdr.Status = STATUS_INVALID_VIEW_SIZE; + else + rsp->hdr.Status = STATUS_UNEXPECTED_IO_ERROR; + } + + ci_rsp->ChunksWritten = cpu_to_le32(chunk_count_written); + ci_rsp->ChunkBytesWritten = cpu_to_le32(chunk_size_written); + ci_rsp->TotalBytesWritten = cpu_to_le32(total_size_written); +out: + ksmbd_fd_put(work, src_fp); + ksmbd_fd_put(work, dst_fp); + return ret; +} + +static __be32 idev_ipv4_address(struct in_device *idev) +{ + __be32 addr = 0; + + struct in_ifaddr *ifa; + + rcu_read_lock(); + in_dev_for_each_ifa_rcu(ifa, idev) { + if (ifa->ifa_flags & IFA_F_SECONDARY) + continue; + + addr = ifa->ifa_address; + break; + } + rcu_read_unlock(); + return addr; +} + +static int fsctl_query_iface_info_ioctl(struct ksmbd_conn *conn, + struct smb2_ioctl_rsp *rsp, + unsigned int out_buf_len) +{ + struct network_interface_info_ioctl_rsp *nii_rsp = NULL; + int nbytes = 0; + struct net_device *netdev; + struct sockaddr_storage_rsp *sockaddr_storage; + unsigned int flags; + unsigned long long speed; + + rtnl_lock(); + for_each_netdev(&init_net, netdev) { + bool ipv4_set = false; + + if (netdev->type == ARPHRD_LOOPBACK) + continue; + + flags = dev_get_flags(netdev); + if (!(flags & IFF_RUNNING)) + continue; +ipv6_retry: + if (out_buf_len < + nbytes + sizeof(struct network_interface_info_ioctl_rsp)) { + rtnl_unlock(); + return -ENOSPC; + } + + nii_rsp = (struct network_interface_info_ioctl_rsp *) + &rsp->Buffer[nbytes]; + nii_rsp->IfIndex = cpu_to_le32(netdev->ifindex); + + nii_rsp->Capability = 0; + if (netdev->real_num_tx_queues > 1) + nii_rsp->Capability |= cpu_to_le32(RSS_CAPABLE); + if (ksmbd_rdma_capable_netdev(netdev)) + nii_rsp->Capability |= cpu_to_le32(RDMA_CAPABLE); + + nii_rsp->Next = cpu_to_le32(152); + nii_rsp->Reserved = 0; + + if (netdev->ethtool_ops->get_link_ksettings) { + struct ethtool_link_ksettings cmd; + + netdev->ethtool_ops->get_link_ksettings(netdev, &cmd); + speed = cmd.base.speed; + } else { + ksmbd_debug(SMB, "%s %s\n", netdev->name, + "speed is unknown, defaulting to 1Gb/sec"); + speed = SPEED_1000; + } + + speed *= 1000000; + nii_rsp->LinkSpeed = cpu_to_le64(speed); + + sockaddr_storage = (struct sockaddr_storage_rsp *) + nii_rsp->SockAddr_Storage; + memset(sockaddr_storage, 0, 128); + + if (!ipv4_set) { + struct in_device *idev; + + sockaddr_storage->Family = cpu_to_le16(INTERNETWORK); + sockaddr_storage->addr4.Port = 0; + + idev = __in_dev_get_rtnl(netdev); + if (!idev) + continue; + sockaddr_storage->addr4.IPv4address = + idev_ipv4_address(idev); + nbytes += sizeof(struct network_interface_info_ioctl_rsp); + ipv4_set = true; + goto ipv6_retry; + } else { + struct inet6_dev *idev6; + struct inet6_ifaddr *ifa; + __u8 *ipv6_addr = sockaddr_storage->addr6.IPv6address; + + sockaddr_storage->Family = cpu_to_le16(INTERNETWORKV6); + sockaddr_storage->addr6.Port = 0; + sockaddr_storage->addr6.FlowInfo = 0; + + idev6 = __in6_dev_get(netdev); + if (!idev6) + continue; + + list_for_each_entry(ifa, &idev6->addr_list, if_list) { + if (ifa->flags & (IFA_F_TENTATIVE | + IFA_F_DEPRECATED)) + continue; + memcpy(ipv6_addr, ifa->addr.s6_addr, 16); + break; + } + sockaddr_storage->addr6.ScopeId = 0; + nbytes += sizeof(struct network_interface_info_ioctl_rsp); + } + } + rtnl_unlock(); + + /* zero if this is last one */ + if (nii_rsp) + nii_rsp->Next = 0; + + rsp->PersistentFileId = SMB2_NO_FID; + rsp->VolatileFileId = SMB2_NO_FID; + return nbytes; +} + +static int fsctl_validate_negotiate_info(struct ksmbd_conn *conn, + struct validate_negotiate_info_req *neg_req, + struct validate_negotiate_info_rsp *neg_rsp, + unsigned int in_buf_len) +{ + int ret = 0; + int dialect; + + if (in_buf_len < offsetof(struct validate_negotiate_info_req, Dialects) + + le16_to_cpu(neg_req->DialectCount) * sizeof(__le16)) + return -EINVAL; + + dialect = ksmbd_lookup_dialect_by_id(neg_req->Dialects, + neg_req->DialectCount); + if (dialect == BAD_PROT_ID || dialect != conn->dialect) { + ret = -EINVAL; + goto err_out; + } + + if (strncmp(neg_req->Guid, conn->ClientGUID, SMB2_CLIENT_GUID_SIZE)) { + ret = -EINVAL; + goto err_out; + } + + if (le16_to_cpu(neg_req->SecurityMode) != conn->cli_sec_mode) { + ret = -EINVAL; + goto err_out; + } + + if (le32_to_cpu(neg_req->Capabilities) != conn->cli_cap) { + ret = -EINVAL; + goto err_out; + } + + neg_rsp->Capabilities = cpu_to_le32(conn->vals->capabilities); + memset(neg_rsp->Guid, 0, SMB2_CLIENT_GUID_SIZE); + neg_rsp->SecurityMode = cpu_to_le16(conn->srv_sec_mode); + neg_rsp->Dialect = cpu_to_le16(conn->dialect); +err_out: + return ret; +} + +static int fsctl_query_allocated_ranges(struct ksmbd_work *work, u64 id, + struct file_allocated_range_buffer *qar_req, + struct file_allocated_range_buffer *qar_rsp, + unsigned int in_count, unsigned int *out_count) +{ + struct ksmbd_file *fp; + loff_t start, length; + int ret = 0; + + *out_count = 0; + if (in_count == 0) + return -EINVAL; + + start = le64_to_cpu(qar_req->file_offset); + length = le64_to_cpu(qar_req->length); + + if (start < 0 || length < 0) + return -EINVAL; + + fp = ksmbd_lookup_fd_fast(work, id); + if (!fp) + return -ENOENT; + + ret = ksmbd_vfs_fqar_lseek(fp, start, length, + qar_rsp, in_count, out_count); + if (ret && ret != -E2BIG) + *out_count = 0; + + ksmbd_fd_put(work, fp); + return ret; +} + +static int fsctl_pipe_transceive(struct ksmbd_work *work, u64 id, + unsigned int out_buf_len, + struct smb2_ioctl_req *req, + struct smb2_ioctl_rsp *rsp) +{ + struct ksmbd_rpc_command *rpc_resp; + char *data_buf = (char *)&req->Buffer[0]; + int nbytes = 0; + + rpc_resp = ksmbd_rpc_ioctl(work->sess, id, data_buf, + le32_to_cpu(req->InputCount)); + if (rpc_resp) { + if (rpc_resp->flags == KSMBD_RPC_SOME_NOT_MAPPED) { + /* + * set STATUS_SOME_NOT_MAPPED response + * for unknown domain sid. + */ + rsp->hdr.Status = STATUS_SOME_NOT_MAPPED; + } else if (rpc_resp->flags == KSMBD_RPC_ENOTIMPLEMENTED) { + rsp->hdr.Status = STATUS_NOT_SUPPORTED; + goto out; + } else if (rpc_resp->flags != KSMBD_RPC_OK) { + rsp->hdr.Status = STATUS_INVALID_PARAMETER; + goto out; + } + + nbytes = rpc_resp->payload_sz; + if (rpc_resp->payload_sz > out_buf_len) { + rsp->hdr.Status = STATUS_BUFFER_OVERFLOW; + nbytes = out_buf_len; + } + + if (!rpc_resp->payload_sz) { + rsp->hdr.Status = + STATUS_UNEXPECTED_IO_ERROR; + goto out; + } + + memcpy((char *)rsp->Buffer, rpc_resp->payload, nbytes); + } +out: + kvfree(rpc_resp); + return nbytes; +} + +static inline int fsctl_set_sparse(struct ksmbd_work *work, u64 id, + struct file_sparse *sparse) +{ + struct ksmbd_file *fp; + struct user_namespace *user_ns; + int ret = 0; + __le32 old_fattr; + + fp = ksmbd_lookup_fd_fast(work, id); + if (!fp) + return -ENOENT; + user_ns = file_mnt_user_ns(fp->filp); + + old_fattr = fp->f_ci->m_fattr; + if (sparse->SetSparse) + fp->f_ci->m_fattr |= FILE_ATTRIBUTE_SPARSE_FILE_LE; + else + fp->f_ci->m_fattr &= ~FILE_ATTRIBUTE_SPARSE_FILE_LE; + + if (fp->f_ci->m_fattr != old_fattr && + test_share_config_flag(work->tcon->share_conf, + KSMBD_SHARE_FLAG_STORE_DOS_ATTRS)) { + struct xattr_dos_attrib da; + + ret = ksmbd_vfs_get_dos_attrib_xattr(user_ns, + fp->filp->f_path.dentry, &da); + if (ret <= 0) + goto out; + + da.attr = le32_to_cpu(fp->f_ci->m_fattr); + ret = ksmbd_vfs_set_dos_attrib_xattr(user_ns, + fp->filp->f_path.dentry, &da); + if (ret) + fp->f_ci->m_fattr = old_fattr; + } + +out: + ksmbd_fd_put(work, fp); + return ret; +} + +static int fsctl_request_resume_key(struct ksmbd_work *work, + struct smb2_ioctl_req *req, + struct resume_key_ioctl_rsp *key_rsp) +{ + struct ksmbd_file *fp; + + fp = ksmbd_lookup_fd_slow(work, req->VolatileFileId, req->PersistentFileId); + if (!fp) + return -ENOENT; + + memset(key_rsp, 0, sizeof(*key_rsp)); + key_rsp->ResumeKey[0] = req->VolatileFileId; + key_rsp->ResumeKey[1] = req->PersistentFileId; + ksmbd_fd_put(work, fp); + + return 0; +} + +/** + * smb2_ioctl() - handler for smb2 ioctl command + * @work: smb work containing ioctl command buffer + * + * Return: 0 on success, otherwise error + */ +int smb2_ioctl(struct ksmbd_work *work) +{ + struct smb2_ioctl_req *req; + struct smb2_ioctl_rsp *rsp; + unsigned int cnt_code, nbytes = 0, out_buf_len, in_buf_len; + u64 id = KSMBD_NO_FID; + struct ksmbd_conn *conn = work->conn; + int ret = 0; + + if (work->next_smb2_rcv_hdr_off) { + req = ksmbd_req_buf_next(work); + rsp = ksmbd_resp_buf_next(work); + if (!has_file_id(req->VolatileFileId)) { + ksmbd_debug(SMB, "Compound request set FID = %llu\n", + work->compound_fid); + id = work->compound_fid; + } + } else { + req = smb2_get_msg(work->request_buf); + rsp = smb2_get_msg(work->response_buf); + } + + if (!has_file_id(id)) + id = req->VolatileFileId; + + if (req->Flags != cpu_to_le32(SMB2_0_IOCTL_IS_FSCTL)) { + rsp->hdr.Status = STATUS_NOT_SUPPORTED; + goto out; + } + + cnt_code = le32_to_cpu(req->CtlCode); + ret = smb2_calc_max_out_buf_len(work, 48, + le32_to_cpu(req->MaxOutputResponse)); + if (ret < 0) { + rsp->hdr.Status = STATUS_INVALID_PARAMETER; + goto out; + } + out_buf_len = (unsigned int)ret; + in_buf_len = le32_to_cpu(req->InputCount); + + switch (cnt_code) { + case FSCTL_DFS_GET_REFERRALS: + case FSCTL_DFS_GET_REFERRALS_EX: + /* Not support DFS yet */ + rsp->hdr.Status = STATUS_FS_DRIVER_REQUIRED; + goto out; + case FSCTL_CREATE_OR_GET_OBJECT_ID: + { + struct file_object_buf_type1_ioctl_rsp *obj_buf; + + nbytes = sizeof(struct file_object_buf_type1_ioctl_rsp); + obj_buf = (struct file_object_buf_type1_ioctl_rsp *) + &rsp->Buffer[0]; + + /* + * TODO: This is dummy implementation to pass smbtorture + * Need to check correct response later + */ + memset(obj_buf->ObjectId, 0x0, 16); + memset(obj_buf->BirthVolumeId, 0x0, 16); + memset(obj_buf->BirthObjectId, 0x0, 16); + memset(obj_buf->DomainId, 0x0, 16); + + break; + } + case FSCTL_PIPE_TRANSCEIVE: + out_buf_len = min_t(u32, KSMBD_IPC_MAX_PAYLOAD, out_buf_len); + nbytes = fsctl_pipe_transceive(work, id, out_buf_len, req, rsp); + break; + case FSCTL_VALIDATE_NEGOTIATE_INFO: + if (conn->dialect < SMB30_PROT_ID) { + ret = -EOPNOTSUPP; + goto out; + } + + if (in_buf_len < offsetof(struct validate_negotiate_info_req, + Dialects)) { + ret = -EINVAL; + goto out; + } + + if (out_buf_len < sizeof(struct validate_negotiate_info_rsp)) { + ret = -EINVAL; + goto out; + } + + ret = fsctl_validate_negotiate_info(conn, + (struct validate_negotiate_info_req *)&req->Buffer[0], + (struct validate_negotiate_info_rsp *)&rsp->Buffer[0], + in_buf_len); + if (ret < 0) + goto out; + + nbytes = sizeof(struct validate_negotiate_info_rsp); + rsp->PersistentFileId = SMB2_NO_FID; + rsp->VolatileFileId = SMB2_NO_FID; + break; + case FSCTL_QUERY_NETWORK_INTERFACE_INFO: + ret = fsctl_query_iface_info_ioctl(conn, rsp, out_buf_len); + if (ret < 0) + goto out; + nbytes = ret; + break; + case FSCTL_REQUEST_RESUME_KEY: + if (out_buf_len < sizeof(struct resume_key_ioctl_rsp)) { + ret = -EINVAL; + goto out; + } + + ret = fsctl_request_resume_key(work, req, + (struct resume_key_ioctl_rsp *)&rsp->Buffer[0]); + if (ret < 0) + goto out; + rsp->PersistentFileId = req->PersistentFileId; + rsp->VolatileFileId = req->VolatileFileId; + nbytes = sizeof(struct resume_key_ioctl_rsp); + break; + case FSCTL_COPYCHUNK: + case FSCTL_COPYCHUNK_WRITE: + if (!test_tree_conn_flag(work->tcon, KSMBD_TREE_CONN_FLAG_WRITABLE)) { + ksmbd_debug(SMB, + "User does not have write permission\n"); + ret = -EACCES; + goto out; + } + + if (in_buf_len < sizeof(struct copychunk_ioctl_req)) { + ret = -EINVAL; + goto out; + } + + if (out_buf_len < sizeof(struct copychunk_ioctl_rsp)) { + ret = -EINVAL; + goto out; + } + + nbytes = sizeof(struct copychunk_ioctl_rsp); + rsp->VolatileFileId = req->VolatileFileId; + rsp->PersistentFileId = req->PersistentFileId; + fsctl_copychunk(work, + (struct copychunk_ioctl_req *)&req->Buffer[0], + le32_to_cpu(req->CtlCode), + le32_to_cpu(req->InputCount), + req->VolatileFileId, + req->PersistentFileId, + rsp); + break; + case FSCTL_SET_SPARSE: + if (in_buf_len < sizeof(struct file_sparse)) { + ret = -EINVAL; + goto out; + } + + ret = fsctl_set_sparse(work, id, + (struct file_sparse *)&req->Buffer[0]); + if (ret < 0) + goto out; + break; + case FSCTL_SET_ZERO_DATA: + { + struct file_zero_data_information *zero_data; + struct ksmbd_file *fp; + loff_t off, len, bfz; + + if (!test_tree_conn_flag(work->tcon, KSMBD_TREE_CONN_FLAG_WRITABLE)) { + ksmbd_debug(SMB, + "User does not have write permission\n"); + ret = -EACCES; + goto out; + } + + if (in_buf_len < sizeof(struct file_zero_data_information)) { + ret = -EINVAL; + goto out; + } + + zero_data = + (struct file_zero_data_information *)&req->Buffer[0]; + + off = le64_to_cpu(zero_data->FileOffset); + bfz = le64_to_cpu(zero_data->BeyondFinalZero); + if (off < 0 || bfz < 0 || off > bfz) { + ret = -EINVAL; + goto out; + } + + len = bfz - off; + if (len) { + fp = ksmbd_lookup_fd_fast(work, id); + if (!fp) { + ret = -ENOENT; + goto out; + } + + ret = ksmbd_vfs_zero_data(work, fp, off, len); + ksmbd_fd_put(work, fp); + if (ret < 0) + goto out; + } + break; + } + case FSCTL_QUERY_ALLOCATED_RANGES: + if (in_buf_len < sizeof(struct file_allocated_range_buffer)) { + ret = -EINVAL; + goto out; + } + + ret = fsctl_query_allocated_ranges(work, id, + (struct file_allocated_range_buffer *)&req->Buffer[0], + (struct file_allocated_range_buffer *)&rsp->Buffer[0], + out_buf_len / + sizeof(struct file_allocated_range_buffer), &nbytes); + if (ret == -E2BIG) { + rsp->hdr.Status = STATUS_BUFFER_OVERFLOW; + } else if (ret < 0) { + nbytes = 0; + goto out; + } + + nbytes *= sizeof(struct file_allocated_range_buffer); + break; + case FSCTL_GET_REPARSE_POINT: + { + struct reparse_data_buffer *reparse_ptr; + struct ksmbd_file *fp; + + reparse_ptr = (struct reparse_data_buffer *)&rsp->Buffer[0]; + fp = ksmbd_lookup_fd_fast(work, id); + if (!fp) { + pr_err("not found fp!!\n"); + ret = -ENOENT; + goto out; + } + + reparse_ptr->ReparseTag = + smb2_get_reparse_tag_special_file(file_inode(fp->filp)->i_mode); + reparse_ptr->ReparseDataLength = 0; + ksmbd_fd_put(work, fp); + nbytes = sizeof(struct reparse_data_buffer); + break; + } + case FSCTL_DUPLICATE_EXTENTS_TO_FILE: + { + struct ksmbd_file *fp_in, *fp_out = NULL; + struct duplicate_extents_to_file *dup_ext; + loff_t src_off, dst_off, length, cloned; + + if (in_buf_len < sizeof(struct duplicate_extents_to_file)) { + ret = -EINVAL; + goto out; + } + + dup_ext = (struct duplicate_extents_to_file *)&req->Buffer[0]; + + fp_in = ksmbd_lookup_fd_slow(work, dup_ext->VolatileFileHandle, + dup_ext->PersistentFileHandle); + if (!fp_in) { + pr_err("not found file handle in duplicate extent to file\n"); + ret = -ENOENT; + goto out; + } + + fp_out = ksmbd_lookup_fd_fast(work, id); + if (!fp_out) { + pr_err("not found fp\n"); + ret = -ENOENT; + goto dup_ext_out; + } + + src_off = le64_to_cpu(dup_ext->SourceFileOffset); + dst_off = le64_to_cpu(dup_ext->TargetFileOffset); + length = le64_to_cpu(dup_ext->ByteCount); + /* + * XXX: It is not clear if FSCTL_DUPLICATE_EXTENTS_TO_FILE + * should fall back to vfs_copy_file_range(). This could be + * beneficial when re-exporting nfs/smb mount, but note that + * this can result in partial copy that returns an error status. + * If/when FSCTL_DUPLICATE_EXTENTS_TO_FILE_EX is implemented, + * fall back to vfs_copy_file_range(), should be avoided when + * the flag DUPLICATE_EXTENTS_DATA_EX_SOURCE_ATOMIC is set. + */ + cloned = vfs_clone_file_range(fp_in->filp, src_off, + fp_out->filp, dst_off, length, 0); + if (cloned == -EXDEV || cloned == -EOPNOTSUPP) { + ret = -EOPNOTSUPP; + goto dup_ext_out; + } else if (cloned != length) { + cloned = vfs_copy_file_range(fp_in->filp, src_off, + fp_out->filp, dst_off, + length, 0); + if (cloned != length) { + if (cloned < 0) + ret = cloned; + else + ret = -EINVAL; + } + } + +dup_ext_out: + ksmbd_fd_put(work, fp_in); + ksmbd_fd_put(work, fp_out); + if (ret < 0) + goto out; + break; + } + default: + ksmbd_debug(SMB, "not implemented yet ioctl command 0x%x\n", + cnt_code); + ret = -EOPNOTSUPP; + goto out; + } + + rsp->CtlCode = cpu_to_le32(cnt_code); + rsp->InputCount = cpu_to_le32(0); + rsp->InputOffset = cpu_to_le32(112); + rsp->OutputOffset = cpu_to_le32(112); + rsp->OutputCount = cpu_to_le32(nbytes); + rsp->StructureSize = cpu_to_le16(49); + rsp->Reserved = cpu_to_le16(0); + rsp->Flags = cpu_to_le32(0); + rsp->Reserved2 = cpu_to_le32(0); + inc_rfc1001_len(work->response_buf, 48 + nbytes); + + return 0; + +out: + if (ret == -EACCES) + rsp->hdr.Status = STATUS_ACCESS_DENIED; + else if (ret == -ENOENT) + rsp->hdr.Status = STATUS_OBJECT_NAME_NOT_FOUND; + else if (ret == -EOPNOTSUPP) + rsp->hdr.Status = STATUS_NOT_SUPPORTED; + else if (ret == -ENOSPC) + rsp->hdr.Status = STATUS_BUFFER_TOO_SMALL; + else if (ret < 0 || rsp->hdr.Status == 0) + rsp->hdr.Status = STATUS_INVALID_PARAMETER; + smb2_set_err_rsp(work); + return 0; +} + +/** + * smb20_oplock_break_ack() - handler for smb2.0 oplock break command + * @work: smb work containing oplock break command buffer + * + * Return: 0 + */ +static void smb20_oplock_break_ack(struct ksmbd_work *work) +{ + struct smb2_oplock_break *req = smb2_get_msg(work->request_buf); + struct smb2_oplock_break *rsp = smb2_get_msg(work->response_buf); + struct ksmbd_file *fp; + struct oplock_info *opinfo = NULL; + __le32 err = 0; + int ret = 0; + u64 volatile_id, persistent_id; + char req_oplevel = 0, rsp_oplevel = 0; + unsigned int oplock_change_type; + + volatile_id = req->VolatileFid; + persistent_id = req->PersistentFid; + req_oplevel = req->OplockLevel; + ksmbd_debug(OPLOCK, "v_id %llu, p_id %llu request oplock level %d\n", + volatile_id, persistent_id, req_oplevel); + + fp = ksmbd_lookup_fd_slow(work, volatile_id, persistent_id); + if (!fp) { + rsp->hdr.Status = STATUS_FILE_CLOSED; + smb2_set_err_rsp(work); + return; + } + + opinfo = opinfo_get(fp); + if (!opinfo) { + pr_err("unexpected null oplock_info\n"); + rsp->hdr.Status = STATUS_INVALID_OPLOCK_PROTOCOL; + smb2_set_err_rsp(work); + ksmbd_fd_put(work, fp); + return; + } + + if (opinfo->level == SMB2_OPLOCK_LEVEL_NONE) { + rsp->hdr.Status = STATUS_INVALID_OPLOCK_PROTOCOL; + goto err_out; + } + + if (opinfo->op_state == OPLOCK_STATE_NONE) { + ksmbd_debug(SMB, "unexpected oplock state 0x%x\n", opinfo->op_state); + rsp->hdr.Status = STATUS_UNSUCCESSFUL; + goto err_out; + } + + if ((opinfo->level == SMB2_OPLOCK_LEVEL_EXCLUSIVE || + opinfo->level == SMB2_OPLOCK_LEVEL_BATCH) && + (req_oplevel != SMB2_OPLOCK_LEVEL_II && + req_oplevel != SMB2_OPLOCK_LEVEL_NONE)) { + err = STATUS_INVALID_OPLOCK_PROTOCOL; + oplock_change_type = OPLOCK_WRITE_TO_NONE; + } else if (opinfo->level == SMB2_OPLOCK_LEVEL_II && + req_oplevel != SMB2_OPLOCK_LEVEL_NONE) { + err = STATUS_INVALID_OPLOCK_PROTOCOL; + oplock_change_type = OPLOCK_READ_TO_NONE; + } else if (req_oplevel == SMB2_OPLOCK_LEVEL_II || + req_oplevel == SMB2_OPLOCK_LEVEL_NONE) { + err = STATUS_INVALID_DEVICE_STATE; + if ((opinfo->level == SMB2_OPLOCK_LEVEL_EXCLUSIVE || + opinfo->level == SMB2_OPLOCK_LEVEL_BATCH) && + req_oplevel == SMB2_OPLOCK_LEVEL_II) { + oplock_change_type = OPLOCK_WRITE_TO_READ; + } else if ((opinfo->level == SMB2_OPLOCK_LEVEL_EXCLUSIVE || + opinfo->level == SMB2_OPLOCK_LEVEL_BATCH) && + req_oplevel == SMB2_OPLOCK_LEVEL_NONE) { + oplock_change_type = OPLOCK_WRITE_TO_NONE; + } else if (opinfo->level == SMB2_OPLOCK_LEVEL_II && + req_oplevel == SMB2_OPLOCK_LEVEL_NONE) { + oplock_change_type = OPLOCK_READ_TO_NONE; + } else { + oplock_change_type = 0; + } + } else { + oplock_change_type = 0; + } + + switch (oplock_change_type) { + case OPLOCK_WRITE_TO_READ: + ret = opinfo_write_to_read(opinfo); + rsp_oplevel = SMB2_OPLOCK_LEVEL_II; + break; + case OPLOCK_WRITE_TO_NONE: + ret = opinfo_write_to_none(opinfo); + rsp_oplevel = SMB2_OPLOCK_LEVEL_NONE; + break; + case OPLOCK_READ_TO_NONE: + ret = opinfo_read_to_none(opinfo); + rsp_oplevel = SMB2_OPLOCK_LEVEL_NONE; + break; + default: + pr_err("unknown oplock change 0x%x -> 0x%x\n", + opinfo->level, rsp_oplevel); + } + + if (ret < 0) { + rsp->hdr.Status = err; + goto err_out; + } + + opinfo_put(opinfo); + ksmbd_fd_put(work, fp); + opinfo->op_state = OPLOCK_STATE_NONE; + wake_up_interruptible_all(&opinfo->oplock_q); + + rsp->StructureSize = cpu_to_le16(24); + rsp->OplockLevel = rsp_oplevel; + rsp->Reserved = 0; + rsp->Reserved2 = 0; + rsp->VolatileFid = volatile_id; + rsp->PersistentFid = persistent_id; + inc_rfc1001_len(work->response_buf, 24); + return; + +err_out: + opinfo->op_state = OPLOCK_STATE_NONE; + wake_up_interruptible_all(&opinfo->oplock_q); + + opinfo_put(opinfo); + ksmbd_fd_put(work, fp); + smb2_set_err_rsp(work); +} + +static int check_lease_state(struct lease *lease, __le32 req_state) +{ + if ((lease->new_state == + (SMB2_LEASE_READ_CACHING_LE | SMB2_LEASE_HANDLE_CACHING_LE)) && + !(req_state & SMB2_LEASE_WRITE_CACHING_LE)) { + lease->new_state = req_state; + return 0; + } + + if (lease->new_state == req_state) + return 0; + + return 1; +} + +/** + * smb21_lease_break_ack() - handler for smb2.1 lease break command + * @work: smb work containing lease break command buffer + * + * Return: 0 + */ +static void smb21_lease_break_ack(struct ksmbd_work *work) +{ + struct ksmbd_conn *conn = work->conn; + struct smb2_lease_ack *req = smb2_get_msg(work->request_buf); + struct smb2_lease_ack *rsp = smb2_get_msg(work->response_buf); + struct oplock_info *opinfo; + __le32 err = 0; + int ret = 0; + unsigned int lease_change_type; + __le32 lease_state; + struct lease *lease; + + ksmbd_debug(OPLOCK, "smb21 lease break, lease state(0x%x)\n", + le32_to_cpu(req->LeaseState)); + opinfo = lookup_lease_in_table(conn, req->LeaseKey); + if (!opinfo) { + ksmbd_debug(OPLOCK, "file not opened\n"); + smb2_set_err_rsp(work); + rsp->hdr.Status = STATUS_UNSUCCESSFUL; + return; + } + lease = opinfo->o_lease; + + if (opinfo->op_state == OPLOCK_STATE_NONE) { + pr_err("unexpected lease break state 0x%x\n", + opinfo->op_state); + rsp->hdr.Status = STATUS_UNSUCCESSFUL; + goto err_out; + } + + if (check_lease_state(lease, req->LeaseState)) { + rsp->hdr.Status = STATUS_REQUEST_NOT_ACCEPTED; + ksmbd_debug(OPLOCK, + "req lease state: 0x%x, expected state: 0x%x\n", + req->LeaseState, lease->new_state); + goto err_out; + } + + if (!atomic_read(&opinfo->breaking_cnt)) { + rsp->hdr.Status = STATUS_UNSUCCESSFUL; + goto err_out; + } + + /* check for bad lease state */ + if (req->LeaseState & + (~(SMB2_LEASE_READ_CACHING_LE | SMB2_LEASE_HANDLE_CACHING_LE))) { + err = STATUS_INVALID_OPLOCK_PROTOCOL; + if (lease->state & SMB2_LEASE_WRITE_CACHING_LE) + lease_change_type = OPLOCK_WRITE_TO_NONE; + else + lease_change_type = OPLOCK_READ_TO_NONE; + ksmbd_debug(OPLOCK, "handle bad lease state 0x%x -> 0x%x\n", + le32_to_cpu(lease->state), + le32_to_cpu(req->LeaseState)); + } else if (lease->state == SMB2_LEASE_READ_CACHING_LE && + req->LeaseState != SMB2_LEASE_NONE_LE) { + err = STATUS_INVALID_OPLOCK_PROTOCOL; + lease_change_type = OPLOCK_READ_TO_NONE; + ksmbd_debug(OPLOCK, "handle bad lease state 0x%x -> 0x%x\n", + le32_to_cpu(lease->state), + le32_to_cpu(req->LeaseState)); + } else { + /* valid lease state changes */ + err = STATUS_INVALID_DEVICE_STATE; + if (req->LeaseState == SMB2_LEASE_NONE_LE) { + if (lease->state & SMB2_LEASE_WRITE_CACHING_LE) + lease_change_type = OPLOCK_WRITE_TO_NONE; + else + lease_change_type = OPLOCK_READ_TO_NONE; + } else if (req->LeaseState & SMB2_LEASE_READ_CACHING_LE) { + if (lease->state & SMB2_LEASE_WRITE_CACHING_LE) + lease_change_type = OPLOCK_WRITE_TO_READ; + else + lease_change_type = OPLOCK_READ_HANDLE_TO_READ; + } else { + lease_change_type = 0; + } + } + + switch (lease_change_type) { + case OPLOCK_WRITE_TO_READ: + ret = opinfo_write_to_read(opinfo); + break; + case OPLOCK_READ_HANDLE_TO_READ: + ret = opinfo_read_handle_to_read(opinfo); + break; + case OPLOCK_WRITE_TO_NONE: + ret = opinfo_write_to_none(opinfo); + break; + case OPLOCK_READ_TO_NONE: + ret = opinfo_read_to_none(opinfo); + break; + default: + ksmbd_debug(OPLOCK, "unknown lease change 0x%x -> 0x%x\n", + le32_to_cpu(lease->state), + le32_to_cpu(req->LeaseState)); + } + + lease_state = lease->state; + opinfo->op_state = OPLOCK_STATE_NONE; + wake_up_interruptible_all(&opinfo->oplock_q); + atomic_dec(&opinfo->breaking_cnt); + wake_up_interruptible_all(&opinfo->oplock_brk); + opinfo_put(opinfo); + + if (ret < 0) { + rsp->hdr.Status = err; + goto err_out; + } + + rsp->StructureSize = cpu_to_le16(36); + rsp->Reserved = 0; + rsp->Flags = 0; + memcpy(rsp->LeaseKey, req->LeaseKey, 16); + rsp->LeaseState = lease_state; + rsp->LeaseDuration = 0; + inc_rfc1001_len(work->response_buf, 36); + return; + +err_out: + opinfo->op_state = OPLOCK_STATE_NONE; + wake_up_interruptible_all(&opinfo->oplock_q); + atomic_dec(&opinfo->breaking_cnt); + wake_up_interruptible_all(&opinfo->oplock_brk); + + opinfo_put(opinfo); + smb2_set_err_rsp(work); +} + +/** + * smb2_oplock_break() - dispatcher for smb2.0 and 2.1 oplock/lease break + * @work: smb work containing oplock/lease break command buffer + * + * Return: 0 + */ +int smb2_oplock_break(struct ksmbd_work *work) +{ + struct smb2_oplock_break *req = smb2_get_msg(work->request_buf); + struct smb2_oplock_break *rsp = smb2_get_msg(work->response_buf); + + switch (le16_to_cpu(req->StructureSize)) { + case OP_BREAK_STRUCT_SIZE_20: + smb20_oplock_break_ack(work); + break; + case OP_BREAK_STRUCT_SIZE_21: + smb21_lease_break_ack(work); + break; + default: + ksmbd_debug(OPLOCK, "invalid break cmd %d\n", + le16_to_cpu(req->StructureSize)); + rsp->hdr.Status = STATUS_INVALID_PARAMETER; + smb2_set_err_rsp(work); + } + + return 0; +} + +/** + * smb2_notify() - handler for smb2 notify request + * @work: smb work containing notify command buffer + * + * Return: 0 + */ +int smb2_notify(struct ksmbd_work *work) +{ + struct smb2_change_notify_req *req; + struct smb2_change_notify_rsp *rsp; + + WORK_BUFFERS(work, req, rsp); + + if (work->next_smb2_rcv_hdr_off && req->hdr.NextCommand) { + rsp->hdr.Status = STATUS_INTERNAL_ERROR; + smb2_set_err_rsp(work); + return 0; + } + + smb2_set_err_rsp(work); + rsp->hdr.Status = STATUS_NOT_IMPLEMENTED; + return 0; +} + +/** + * smb2_is_sign_req() - handler for checking packet signing status + * @work: smb work containing notify command buffer + * @command: SMB2 command id + * + * Return: true if packed is signed, false otherwise + */ +bool smb2_is_sign_req(struct ksmbd_work *work, unsigned int command) +{ + struct smb2_hdr *rcv_hdr2 = smb2_get_msg(work->request_buf); + + if ((rcv_hdr2->Flags & SMB2_FLAGS_SIGNED) && + command != SMB2_NEGOTIATE_HE && + command != SMB2_SESSION_SETUP_HE && + command != SMB2_OPLOCK_BREAK_HE) + return true; + + return false; +} + +/** + * smb2_check_sign_req() - handler for req packet sign processing + * @work: smb work containing notify command buffer + * + * Return: 1 on success, 0 otherwise + */ +int smb2_check_sign_req(struct ksmbd_work *work) +{ + struct smb2_hdr *hdr; + char signature_req[SMB2_SIGNATURE_SIZE]; + char signature[SMB2_HMACSHA256_SIZE]; + struct kvec iov[1]; + size_t len; + + hdr = smb2_get_msg(work->request_buf); + if (work->next_smb2_rcv_hdr_off) + hdr = ksmbd_req_buf_next(work); + + if (!hdr->NextCommand && !work->next_smb2_rcv_hdr_off) + len = get_rfc1002_len(work->request_buf); + else if (hdr->NextCommand) + len = le32_to_cpu(hdr->NextCommand); + else + len = get_rfc1002_len(work->request_buf) - + work->next_smb2_rcv_hdr_off; + + memcpy(signature_req, hdr->Signature, SMB2_SIGNATURE_SIZE); + memset(hdr->Signature, 0, SMB2_SIGNATURE_SIZE); + + iov[0].iov_base = (char *)&hdr->ProtocolId; + iov[0].iov_len = len; + + if (ksmbd_sign_smb2_pdu(work->conn, work->sess->sess_key, iov, 1, + signature)) + return 0; + + if (memcmp(signature, signature_req, SMB2_SIGNATURE_SIZE)) { + pr_err("bad smb2 signature\n"); + return 0; + } + + return 1; +} + +/** + * smb2_set_sign_rsp() - handler for rsp packet sign processing + * @work: smb work containing notify command buffer + * + */ +void smb2_set_sign_rsp(struct ksmbd_work *work) +{ + struct smb2_hdr *hdr; + struct smb2_hdr *req_hdr; + char signature[SMB2_HMACSHA256_SIZE]; + struct kvec iov[2]; + size_t len; + int n_vec = 1; + + hdr = smb2_get_msg(work->response_buf); + if (work->next_smb2_rsp_hdr_off) + hdr = ksmbd_resp_buf_next(work); + + req_hdr = ksmbd_req_buf_next(work); + + if (!work->next_smb2_rsp_hdr_off) { + len = get_rfc1002_len(work->response_buf); + if (req_hdr->NextCommand) + len = ALIGN(len, 8); + } else { + len = get_rfc1002_len(work->response_buf) - + work->next_smb2_rsp_hdr_off; + len = ALIGN(len, 8); + } + + if (req_hdr->NextCommand) + hdr->NextCommand = cpu_to_le32(len); + + hdr->Flags |= SMB2_FLAGS_SIGNED; + memset(hdr->Signature, 0, SMB2_SIGNATURE_SIZE); + + iov[0].iov_base = (char *)&hdr->ProtocolId; + iov[0].iov_len = len; + + if (work->aux_payload_sz) { + iov[0].iov_len -= work->aux_payload_sz; + + iov[1].iov_base = work->aux_payload_buf; + iov[1].iov_len = work->aux_payload_sz; + n_vec++; + } + + if (!ksmbd_sign_smb2_pdu(work->conn, work->sess->sess_key, iov, n_vec, + signature)) + memcpy(hdr->Signature, signature, SMB2_SIGNATURE_SIZE); +} + +/** + * smb3_check_sign_req() - handler for req packet sign processing + * @work: smb work containing notify command buffer + * + * Return: 1 on success, 0 otherwise + */ +int smb3_check_sign_req(struct ksmbd_work *work) +{ + struct ksmbd_conn *conn = work->conn; + char *signing_key; + struct smb2_hdr *hdr; + struct channel *chann; + char signature_req[SMB2_SIGNATURE_SIZE]; + char signature[SMB2_CMACAES_SIZE]; + struct kvec iov[1]; + size_t len; + + hdr = smb2_get_msg(work->request_buf); + if (work->next_smb2_rcv_hdr_off) + hdr = ksmbd_req_buf_next(work); + + if (!hdr->NextCommand && !work->next_smb2_rcv_hdr_off) + len = get_rfc1002_len(work->request_buf); + else if (hdr->NextCommand) + len = le32_to_cpu(hdr->NextCommand); + else + len = get_rfc1002_len(work->request_buf) - + work->next_smb2_rcv_hdr_off; + + if (le16_to_cpu(hdr->Command) == SMB2_SESSION_SETUP_HE) { + signing_key = work->sess->smb3signingkey; + } else { + chann = lookup_chann_list(work->sess, conn); + if (!chann) { + return 0; + } + signing_key = chann->smb3signingkey; + } + + if (!signing_key) { + pr_err("SMB3 signing key is not generated\n"); + return 0; + } + + memcpy(signature_req, hdr->Signature, SMB2_SIGNATURE_SIZE); + memset(hdr->Signature, 0, SMB2_SIGNATURE_SIZE); + iov[0].iov_base = (char *)&hdr->ProtocolId; + iov[0].iov_len = len; + + if (ksmbd_sign_smb3_pdu(conn, signing_key, iov, 1, signature)) + return 0; + + if (memcmp(signature, signature_req, SMB2_SIGNATURE_SIZE)) { + pr_err("bad smb2 signature\n"); + return 0; + } + + return 1; +} + +/** + * smb3_set_sign_rsp() - handler for rsp packet sign processing + * @work: smb work containing notify command buffer + * + */ +void smb3_set_sign_rsp(struct ksmbd_work *work) +{ + struct ksmbd_conn *conn = work->conn; + struct smb2_hdr *req_hdr, *hdr; + struct channel *chann; + char signature[SMB2_CMACAES_SIZE]; + struct kvec iov[2]; + int n_vec = 1; + size_t len; + char *signing_key; + + hdr = smb2_get_msg(work->response_buf); + if (work->next_smb2_rsp_hdr_off) + hdr = ksmbd_resp_buf_next(work); + + req_hdr = ksmbd_req_buf_next(work); + + if (!work->next_smb2_rsp_hdr_off) { + len = get_rfc1002_len(work->response_buf); + if (req_hdr->NextCommand) + len = ALIGN(len, 8); + } else { + len = get_rfc1002_len(work->response_buf) - + work->next_smb2_rsp_hdr_off; + len = ALIGN(len, 8); + } + + if (conn->binding == false && + le16_to_cpu(hdr->Command) == SMB2_SESSION_SETUP_HE) { + signing_key = work->sess->smb3signingkey; + } else { + chann = lookup_chann_list(work->sess, work->conn); + if (!chann) { + return; + } + signing_key = chann->smb3signingkey; + } + + if (!signing_key) + return; + + if (req_hdr->NextCommand) + hdr->NextCommand = cpu_to_le32(len); + + hdr->Flags |= SMB2_FLAGS_SIGNED; + memset(hdr->Signature, 0, SMB2_SIGNATURE_SIZE); + iov[0].iov_base = (char *)&hdr->ProtocolId; + iov[0].iov_len = len; + if (work->aux_payload_sz) { + iov[0].iov_len -= work->aux_payload_sz; + iov[1].iov_base = work->aux_payload_buf; + iov[1].iov_len = work->aux_payload_sz; + n_vec++; + } + + if (!ksmbd_sign_smb3_pdu(conn, signing_key, iov, n_vec, signature)) + memcpy(hdr->Signature, signature, SMB2_SIGNATURE_SIZE); +} + +/** + * smb3_preauth_hash_rsp() - handler for computing preauth hash on response + * @work: smb work containing response buffer + * + */ +void smb3_preauth_hash_rsp(struct ksmbd_work *work) +{ + struct ksmbd_conn *conn = work->conn; + struct ksmbd_session *sess = work->sess; + struct smb2_hdr *req, *rsp; + + if (conn->dialect != SMB311_PROT_ID) + return; + + WORK_BUFFERS(work, req, rsp); + + if (le16_to_cpu(req->Command) == SMB2_NEGOTIATE_HE && + conn->preauth_info) + ksmbd_gen_preauth_integrity_hash(conn, work->response_buf, + conn->preauth_info->Preauth_HashValue); + + if (le16_to_cpu(rsp->Command) == SMB2_SESSION_SETUP_HE && sess) { + __u8 *hash_value; + + if (conn->binding) { + struct preauth_session *preauth_sess; + + preauth_sess = ksmbd_preauth_session_lookup(conn, sess->id); + if (!preauth_sess) + return; + hash_value = preauth_sess->Preauth_HashValue; + } else { + hash_value = sess->Preauth_HashValue; + if (!hash_value) + return; + } + ksmbd_gen_preauth_integrity_hash(conn, work->response_buf, + hash_value); + } +} + +static void fill_transform_hdr(void *tr_buf, char *old_buf, __le16 cipher_type) +{ + struct smb2_transform_hdr *tr_hdr = tr_buf + 4; + struct smb2_hdr *hdr = smb2_get_msg(old_buf); + unsigned int orig_len = get_rfc1002_len(old_buf); + + /* tr_buf must be cleared by the caller */ + tr_hdr->ProtocolId = SMB2_TRANSFORM_PROTO_NUM; + tr_hdr->OriginalMessageSize = cpu_to_le32(orig_len); + tr_hdr->Flags = cpu_to_le16(TRANSFORM_FLAG_ENCRYPTED); + if (cipher_type == SMB2_ENCRYPTION_AES128_GCM || + cipher_type == SMB2_ENCRYPTION_AES256_GCM) + get_random_bytes(&tr_hdr->Nonce, SMB3_AES_GCM_NONCE); + else + get_random_bytes(&tr_hdr->Nonce, SMB3_AES_CCM_NONCE); + memcpy(&tr_hdr->SessionId, &hdr->SessionId, 8); + inc_rfc1001_len(tr_buf, sizeof(struct smb2_transform_hdr)); + inc_rfc1001_len(tr_buf, orig_len); +} + +int smb3_encrypt_resp(struct ksmbd_work *work) +{ + char *buf = work->response_buf; + struct kvec iov[3]; + int rc = -ENOMEM; + int buf_size = 0, rq_nvec = 2 + (work->aux_payload_sz ? 1 : 0); + + if (ARRAY_SIZE(iov) < rq_nvec) + return -ENOMEM; + + work->tr_buf = kzalloc(sizeof(struct smb2_transform_hdr) + 4, GFP_KERNEL); + if (!work->tr_buf) + return rc; + + /* fill transform header */ + fill_transform_hdr(work->tr_buf, buf, work->conn->cipher_type); + + iov[0].iov_base = work->tr_buf; + iov[0].iov_len = sizeof(struct smb2_transform_hdr) + 4; + buf_size += iov[0].iov_len - 4; + + iov[1].iov_base = buf + 4; + iov[1].iov_len = get_rfc1002_len(buf); + if (work->aux_payload_sz) { + iov[1].iov_len = work->resp_hdr_sz - 4; + + iov[2].iov_base = work->aux_payload_buf; + iov[2].iov_len = work->aux_payload_sz; + buf_size += iov[2].iov_len; + } + buf_size += iov[1].iov_len; + work->resp_hdr_sz = iov[1].iov_len; + + rc = ksmbd_crypt_message(work, iov, rq_nvec, 1); + if (rc) + return rc; + + memmove(buf, iov[1].iov_base, iov[1].iov_len); + *(__be32 *)work->tr_buf = cpu_to_be32(buf_size); + + return rc; +} + +bool smb3_is_transform_hdr(void *buf) +{ + struct smb2_transform_hdr *trhdr = smb2_get_msg(buf); + + return trhdr->ProtocolId == SMB2_TRANSFORM_PROTO_NUM; +} + +int smb3_decrypt_req(struct ksmbd_work *work) +{ + struct ksmbd_session *sess; + char *buf = work->request_buf; + unsigned int pdu_length = get_rfc1002_len(buf); + struct kvec iov[2]; + int buf_data_size = pdu_length - sizeof(struct smb2_transform_hdr); + struct smb2_transform_hdr *tr_hdr = smb2_get_msg(buf); + int rc = 0; + + if (buf_data_size < sizeof(struct smb2_hdr)) { + pr_err("Transform message is too small (%u)\n", + pdu_length); + return -ECONNABORTED; + } + + if (buf_data_size < le32_to_cpu(tr_hdr->OriginalMessageSize)) { + pr_err("Transform message is broken\n"); + return -ECONNABORTED; + } + + sess = ksmbd_session_lookup_all(work->conn, le64_to_cpu(tr_hdr->SessionId)); + if (!sess) { + pr_err("invalid session id(%llx) in transform header\n", + le64_to_cpu(tr_hdr->SessionId)); + return -ECONNABORTED; + } + + iov[0].iov_base = buf; + iov[0].iov_len = sizeof(struct smb2_transform_hdr) + 4; + iov[1].iov_base = buf + sizeof(struct smb2_transform_hdr) + 4; + iov[1].iov_len = buf_data_size; + rc = ksmbd_crypt_message(work, iov, 2, 0); + if (rc) + return rc; + + memmove(buf + 4, iov[1].iov_base, buf_data_size); + *(__be32 *)buf = cpu_to_be32(buf_data_size); + + return rc; +} + +bool smb3_11_final_sess_setup_resp(struct ksmbd_work *work) +{ + struct ksmbd_conn *conn = work->conn; + struct ksmbd_session *sess = work->sess; + struct smb2_hdr *rsp = smb2_get_msg(work->response_buf); + + if (conn->dialect < SMB30_PROT_ID) + return false; + + if (work->next_smb2_rcv_hdr_off) + rsp = ksmbd_resp_buf_next(work); + + if (le16_to_cpu(rsp->Command) == SMB2_SESSION_SETUP_HE && + sess->user && !user_guest(sess->user) && + rsp->Status == STATUS_SUCCESS) + return true; + return false; +} diff --git a/fs/smb/server/smb2pdu.h b/fs/smb/server/smb2pdu.h new file mode 100644 index 0000000000000..dd10f8031606b --- /dev/null +++ b/fs/smb/server/smb2pdu.h @@ -0,0 +1,536 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + * Copyright (C) 2016 Namjae Jeon + * Copyright (C) 2018 Samsung Electronics Co., Ltd. + */ + +#ifndef _SMB2PDU_H +#define _SMB2PDU_H + +#include "ntlmssp.h" +#include "smbacl.h" + +/*Create Action Flags*/ +#define FILE_SUPERSEDED 0x00000000 +#define FILE_OPENED 0x00000001 +#define FILE_CREATED 0x00000002 +#define FILE_OVERWRITTEN 0x00000003 + +/* SMB2 Max Credits */ +#define SMB2_MAX_CREDITS 8192 + +/* BB FIXME - analyze following length BB */ +#define MAX_SMB2_HDR_SIZE 0x78 /* 4 len + 64 hdr + (2*24 wct) + 2 bct + 2 pad */ + +#define SMB21_DEFAULT_IOSIZE (1024 * 1024) +#define SMB3_DEFAULT_TRANS_SIZE (1024 * 1024) +#define SMB3_MIN_IOSIZE (64 * 1024) +#define SMB3_MAX_IOSIZE (8 * 1024 * 1024) +#define SMB3_MAX_MSGSIZE (4 * 4096) + +/* + * Definitions for SMB2 Protocol Data Units (network frames) + * + * See MS-SMB2.PDF specification for protocol details. + * The Naming convention is the lower case version of the SMB2 + * command code name for the struct. Note that structures must be packed. + * + */ + +struct preauth_integrity_info { + /* PreAuth integrity Hash ID */ + __le16 Preauth_HashId; + /* PreAuth integrity Hash Value */ + __u8 Preauth_HashValue[SMB2_PREAUTH_HASH_SIZE]; +}; + +/* offset is sizeof smb2_negotiate_rsp but rounded up to 8 bytes. */ +#ifdef CONFIG_SMB_SERVER_KERBEROS5 +/* sizeof(struct smb2_negotiate_rsp) = + * header(64) + response(64) + GSS_LENGTH(96) + GSS_PADDING(0) + */ +#define OFFSET_OF_NEG_CONTEXT 0xe0 +#else +/* sizeof(struct smb2_negotiate_rsp) = + * header(64) + response(64) + GSS_LENGTH(74) + GSS_PADDING(6) + */ +#define OFFSET_OF_NEG_CONTEXT 0xd0 +#endif + +#define SMB2_SESSION_EXPIRED (0) +#define SMB2_SESSION_IN_PROGRESS BIT(0) +#define SMB2_SESSION_VALID BIT(1) + +#define SMB2_SESSION_TIMEOUT (10 * HZ) + +struct create_durable_req_v2 { + struct create_context ccontext; + __u8 Name[8]; + __le32 Timeout; + __le32 Flags; + __u8 Reserved[8]; + __u8 CreateGuid[16]; +} __packed; + +struct create_durable_reconn_req { + struct create_context ccontext; + __u8 Name[8]; + union { + __u8 Reserved[16]; + struct { + __u64 PersistentFileId; + __u64 VolatileFileId; + } Fid; + } Data; +} __packed; + +struct create_durable_reconn_v2_req { + struct create_context ccontext; + __u8 Name[8]; + struct { + __u64 PersistentFileId; + __u64 VolatileFileId; + } Fid; + __u8 CreateGuid[16]; + __le32 Flags; +} __packed; + +struct create_app_inst_id { + struct create_context ccontext; + __u8 Name[8]; + __u8 Reserved[8]; + __u8 AppInstanceId[16]; +} __packed; + +struct create_app_inst_id_vers { + struct create_context ccontext; + __u8 Name[8]; + __u8 Reserved[2]; + __u8 Padding[4]; + __le64 AppInstanceVersionHigh; + __le64 AppInstanceVersionLow; +} __packed; + +struct create_mxac_req { + struct create_context ccontext; + __u8 Name[8]; + __le64 Timestamp; +} __packed; + +struct create_alloc_size_req { + struct create_context ccontext; + __u8 Name[8]; + __le64 AllocationSize; +} __packed; + +struct create_durable_rsp { + struct create_context ccontext; + __u8 Name[8]; + union { + __u8 Reserved[8]; + __u64 data; + } Data; +} __packed; + +struct create_durable_v2_rsp { + struct create_context ccontext; + __u8 Name[8]; + __le32 Timeout; + __le32 Flags; +} __packed; + +struct create_mxac_rsp { + struct create_context ccontext; + __u8 Name[8]; + __le32 QueryStatus; + __le32 MaximalAccess; +} __packed; + +struct create_disk_id_rsp { + struct create_context ccontext; + __u8 Name[8]; + __le64 DiskFileId; + __le64 VolumeId; + __u8 Reserved[16]; +} __packed; + +/* equivalent of the contents of SMB3.1.1 POSIX open context response */ +struct create_posix_rsp { + struct create_context ccontext; + __u8 Name[16]; + __le32 nlink; + __le32 reparse_tag; + __le32 mode; + /* SidBuffer contain two sids(Domain sid(28), UNIX group sid(16)) */ + u8 SidBuffer[44]; +} __packed; + +struct smb2_buffer_desc_v1 { + __le64 offset; + __le32 token; + __le32 length; +} __packed; + +#define SMB2_0_IOCTL_IS_FSCTL 0x00000001 + +struct smb_sockaddr_in { + __be16 Port; + __be32 IPv4address; + __u8 Reserved[8]; +} __packed; + +struct smb_sockaddr_in6 { + __be16 Port; + __be32 FlowInfo; + __u8 IPv6address[16]; + __be32 ScopeId; +} __packed; + +#define INTERNETWORK 0x0002 +#define INTERNETWORKV6 0x0017 + +struct sockaddr_storage_rsp { + __le16 Family; + union { + struct smb_sockaddr_in addr4; + struct smb_sockaddr_in6 addr6; + }; +} __packed; + +#define RSS_CAPABLE 0x00000001 +#define RDMA_CAPABLE 0x00000002 + +struct network_interface_info_ioctl_rsp { + __le32 Next; /* next interface. zero if this is last one */ + __le32 IfIndex; + __le32 Capability; /* RSS or RDMA Capable */ + __le32 Reserved; + __le64 LinkSpeed; + char SockAddr_Storage[128]; +} __packed; + +struct file_object_buf_type1_ioctl_rsp { + __u8 ObjectId[16]; + __u8 BirthVolumeId[16]; + __u8 BirthObjectId[16]; + __u8 DomainId[16]; +} __packed; + +struct resume_key_ioctl_rsp { + __u64 ResumeKey[3]; + __le32 ContextLength; + __u8 Context[4]; /* ignored, Windows sets to 4 bytes of zero */ +} __packed; + +struct copychunk_ioctl_req { + __le64 ResumeKey[3]; + __le32 ChunkCount; + __le32 Reserved; + __u8 Chunks[1]; /* array of srv_copychunk */ +} __packed; + +struct srv_copychunk { + __le64 SourceOffset; + __le64 TargetOffset; + __le32 Length; + __le32 Reserved; +} __packed; + +struct copychunk_ioctl_rsp { + __le32 ChunksWritten; + __le32 ChunkBytesWritten; + __le32 TotalBytesWritten; +} __packed; + +struct file_sparse { + __u8 SetSparse; +} __packed; + +/* FILE Info response size */ +#define FILE_DIRECTORY_INFORMATION_SIZE 1 +#define FILE_FULL_DIRECTORY_INFORMATION_SIZE 2 +#define FILE_BOTH_DIRECTORY_INFORMATION_SIZE 3 +#define FILE_BASIC_INFORMATION_SIZE 40 +#define FILE_STANDARD_INFORMATION_SIZE 24 +#define FILE_INTERNAL_INFORMATION_SIZE 8 +#define FILE_EA_INFORMATION_SIZE 4 +#define FILE_ACCESS_INFORMATION_SIZE 4 +#define FILE_NAME_INFORMATION_SIZE 9 +#define FILE_RENAME_INFORMATION_SIZE 10 +#define FILE_LINK_INFORMATION_SIZE 11 +#define FILE_NAMES_INFORMATION_SIZE 12 +#define FILE_DISPOSITION_INFORMATION_SIZE 13 +#define FILE_POSITION_INFORMATION_SIZE 14 +#define FILE_FULL_EA_INFORMATION_SIZE 15 +#define FILE_MODE_INFORMATION_SIZE 4 +#define FILE_ALIGNMENT_INFORMATION_SIZE 4 +#define FILE_ALL_INFORMATION_SIZE 104 +#define FILE_ALLOCATION_INFORMATION_SIZE 19 +#define FILE_END_OF_FILE_INFORMATION_SIZE 20 +#define FILE_ALTERNATE_NAME_INFORMATION_SIZE 8 +#define FILE_STREAM_INFORMATION_SIZE 32 +#define FILE_PIPE_INFORMATION_SIZE 23 +#define FILE_PIPE_LOCAL_INFORMATION_SIZE 24 +#define FILE_PIPE_REMOTE_INFORMATION_SIZE 25 +#define FILE_MAILSLOT_QUERY_INFORMATION_SIZE 26 +#define FILE_MAILSLOT_SET_INFORMATION_SIZE 27 +#define FILE_COMPRESSION_INFORMATION_SIZE 16 +#define FILE_OBJECT_ID_INFORMATION_SIZE 29 +/* Number 30 not defined in documents */ +#define FILE_MOVE_CLUSTER_INFORMATION_SIZE 31 +#define FILE_QUOTA_INFORMATION_SIZE 32 +#define FILE_REPARSE_POINT_INFORMATION_SIZE 33 +#define FILE_NETWORK_OPEN_INFORMATION_SIZE 56 +#define FILE_ATTRIBUTE_TAG_INFORMATION_SIZE 8 + +/* FS Info response size */ +#define FS_DEVICE_INFORMATION_SIZE 8 +#define FS_ATTRIBUTE_INFORMATION_SIZE 16 +#define FS_VOLUME_INFORMATION_SIZE 24 +#define FS_SIZE_INFORMATION_SIZE 24 +#define FS_FULL_SIZE_INFORMATION_SIZE 32 +#define FS_SECTOR_SIZE_INFORMATION_SIZE 28 +#define FS_OBJECT_ID_INFORMATION_SIZE 64 +#define FS_CONTROL_INFORMATION_SIZE 48 +#define FS_POSIX_INFORMATION_SIZE 56 + +/* FS_ATTRIBUTE_File_System_Name */ +#define FS_TYPE_SUPPORT_SIZE 44 +struct fs_type_info { + char *fs_name; + long magic_number; +} __packed; + +/* + * PDU query infolevel structure definitions + * BB consider moving to a different header + */ + +struct smb2_file_access_info { + __le32 AccessFlags; +} __packed; + +struct smb2_file_alignment_info { + __le32 AlignmentRequirement; +} __packed; + +struct smb2_file_basic_info { /* data block encoding of response to level 18 */ + __le64 CreationTime; /* Beginning of FILE_BASIC_INFO equivalent */ + __le64 LastAccessTime; + __le64 LastWriteTime; + __le64 ChangeTime; + __le32 Attributes; + __u32 Pad1; /* End of FILE_BASIC_INFO_INFO equivalent */ +} __packed; + +struct smb2_file_alt_name_info { + __le32 FileNameLength; + char FileName[]; +} __packed; + +struct smb2_file_stream_info { + __le32 NextEntryOffset; + __le32 StreamNameLength; + __le64 StreamSize; + __le64 StreamAllocationSize; + char StreamName[]; +} __packed; + +struct smb2_file_ntwrk_info { + __le64 CreationTime; + __le64 LastAccessTime; + __le64 LastWriteTime; + __le64 ChangeTime; + __le64 AllocationSize; + __le64 EndOfFile; + __le32 Attributes; + __le32 Reserved; +} __packed; + +struct smb2_file_standard_info { + __le64 AllocationSize; + __le64 EndOfFile; + __le32 NumberOfLinks; /* hard links */ + __u8 DeletePending; + __u8 Directory; + __le16 Reserved; +} __packed; /* level 18 Query */ + +struct smb2_file_ea_info { + __le32 EASize; +} __packed; + +struct smb2_file_alloc_info { + __le64 AllocationSize; +} __packed; + +struct smb2_file_disposition_info { + __u8 DeletePending; +} __packed; + +struct smb2_file_pos_info { + __le64 CurrentByteOffset; +} __packed; + +#define FILE_MODE_INFO_MASK cpu_to_le32(0x0000100e) + +struct smb2_file_mode_info { + __le32 Mode; +} __packed; + +#define COMPRESSION_FORMAT_NONE 0x0000 +#define COMPRESSION_FORMAT_LZNT1 0x0002 + +struct smb2_file_comp_info { + __le64 CompressedFileSize; + __le16 CompressionFormat; + __u8 CompressionUnitShift; + __u8 ChunkShift; + __u8 ClusterShift; + __u8 Reserved[3]; +} __packed; + +struct smb2_file_attr_tag_info { + __le32 FileAttributes; + __le32 ReparseTag; +} __packed; + +#define SL_RESTART_SCAN 0x00000001 +#define SL_RETURN_SINGLE_ENTRY 0x00000002 +#define SL_INDEX_SPECIFIED 0x00000004 + +struct smb2_ea_info_req { + __le32 NextEntryOffset; + __u8 EaNameLength; + char name[1]; +} __packed; /* level 15 Query */ + +struct smb2_ea_info { + __le32 NextEntryOffset; + __u8 Flags; + __u8 EaNameLength; + __le16 EaValueLength; + char name[1]; + /* optionally followed by value */ +} __packed; /* level 15 Query */ + +struct create_ea_buf_req { + struct create_context ccontext; + __u8 Name[8]; + struct smb2_ea_info ea; +} __packed; + +struct create_sd_buf_req { + struct create_context ccontext; + __u8 Name[8]; + struct smb_ntsd ntsd; +} __packed; + +struct smb2_posix_info { + __le32 NextEntryOffset; + __u32 Ignored; + __le64 CreationTime; + __le64 LastAccessTime; + __le64 LastWriteTime; + __le64 ChangeTime; + __le64 EndOfFile; + __le64 AllocationSize; + __le32 DosAttributes; + __le64 Inode; + __le32 DeviceId; + __le32 Zero; + /* beginning of POSIX Create Context Response */ + __le32 HardLinks; + __le32 ReparseTag; + __le32 Mode; + /* SidBuffer contain two sids (UNIX user sid(16), UNIX group sid(16)) */ + u8 SidBuffer[32]; + __le32 name_len; + u8 name[1]; + /* + * var sized owner SID + * var sized group SID + * le32 filenamelength + * u8 filename[] + */ +} __packed; + +/* functions */ +void init_smb2_1_server(struct ksmbd_conn *conn); +void init_smb3_0_server(struct ksmbd_conn *conn); +void init_smb3_02_server(struct ksmbd_conn *conn); +int init_smb3_11_server(struct ksmbd_conn *conn); + +void init_smb2_max_read_size(unsigned int sz); +void init_smb2_max_write_size(unsigned int sz); +void init_smb2_max_trans_size(unsigned int sz); +void init_smb2_max_credits(unsigned int sz); + +bool is_smb2_neg_cmd(struct ksmbd_work *work); +bool is_smb2_rsp(struct ksmbd_work *work); + +u16 get_smb2_cmd_val(struct ksmbd_work *work); +void set_smb2_rsp_status(struct ksmbd_work *work, __le32 err); +int init_smb2_rsp_hdr(struct ksmbd_work *work); +int smb2_allocate_rsp_buf(struct ksmbd_work *work); +bool is_chained_smb2_message(struct ksmbd_work *work); +int init_smb2_neg_rsp(struct ksmbd_work *work); +void smb2_set_err_rsp(struct ksmbd_work *work); +int smb2_check_user_session(struct ksmbd_work *work); +int smb2_get_ksmbd_tcon(struct ksmbd_work *work); +bool smb2_is_sign_req(struct ksmbd_work *work, unsigned int command); +int smb2_check_sign_req(struct ksmbd_work *work); +void smb2_set_sign_rsp(struct ksmbd_work *work); +int smb3_check_sign_req(struct ksmbd_work *work); +void smb3_set_sign_rsp(struct ksmbd_work *work); +int find_matching_smb2_dialect(int start_index, __le16 *cli_dialects, + __le16 dialects_count); +struct file_lock *smb_flock_init(struct file *f); +int setup_async_work(struct ksmbd_work *work, void (*fn)(void **), + void **arg); +void smb2_send_interim_resp(struct ksmbd_work *work, __le32 status); +struct channel *lookup_chann_list(struct ksmbd_session *sess, + struct ksmbd_conn *conn); +void smb3_preauth_hash_rsp(struct ksmbd_work *work); +bool smb3_is_transform_hdr(void *buf); +int smb3_decrypt_req(struct ksmbd_work *work); +int smb3_encrypt_resp(struct ksmbd_work *work); +bool smb3_11_final_sess_setup_resp(struct ksmbd_work *work); +int smb2_set_rsp_credits(struct ksmbd_work *work); +bool smb3_encryption_negotiated(struct ksmbd_conn *conn); + +/* smb2 misc functions */ +int ksmbd_smb2_check_message(struct ksmbd_work *work); + +/* smb2 command handlers */ +int smb2_handle_negotiate(struct ksmbd_work *work); +int smb2_negotiate_request(struct ksmbd_work *work); +int smb2_sess_setup(struct ksmbd_work *work); +int smb2_tree_connect(struct ksmbd_work *work); +int smb2_tree_disconnect(struct ksmbd_work *work); +int smb2_session_logoff(struct ksmbd_work *work); +int smb2_open(struct ksmbd_work *work); +int smb2_query_info(struct ksmbd_work *work); +int smb2_query_dir(struct ksmbd_work *work); +int smb2_close(struct ksmbd_work *work); +int smb2_echo(struct ksmbd_work *work); +int smb2_set_info(struct ksmbd_work *work); +int smb2_read(struct ksmbd_work *work); +int smb2_write(struct ksmbd_work *work); +int smb2_flush(struct ksmbd_work *work); +int smb2_cancel(struct ksmbd_work *work); +int smb2_lock(struct ksmbd_work *work); +int smb2_ioctl(struct ksmbd_work *work); +int smb2_oplock_break(struct ksmbd_work *work); +int smb2_notify(struct ksmbd_work *ksmbd_work); + +/* + * Get the body of the smb2 message excluding the 4 byte rfc1002 headers + * from request/response buffer. + */ +static inline void *smb2_get_msg(void *buf) +{ + return buf + 4; +} + +#endif /* _SMB2PDU_H */ diff --git a/fs/smb/server/smb_common.c b/fs/smb/server/smb_common.c new file mode 100644 index 0000000000000..05d7f3e910bf4 --- /dev/null +++ b/fs/smb/server/smb_common.c @@ -0,0 +1,797 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Copyright (C) 2018 Samsung Electronics Co., Ltd. + * Copyright (C) 2018 Namjae Jeon + */ + +#include + +#include "smb_common.h" +#include "server.h" +#include "misc.h" +#include "smbstatus.h" +#include "connection.h" +#include "ksmbd_work.h" +#include "mgmt/user_session.h" +#include "mgmt/user_config.h" +#include "mgmt/tree_connect.h" +#include "mgmt/share_config.h" + +/*for shortname implementation */ +static const char basechars[43] = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ_-!@#$%"; +#define MANGLE_BASE (sizeof(basechars) / sizeof(char) - 1) +#define MAGIC_CHAR '~' +#define PERIOD '.' +#define mangle(V) ((char)(basechars[(V) % MANGLE_BASE])) + +struct smb_protocol { + int index; + char *name; + char *prot; + __u16 prot_id; +}; + +static struct smb_protocol smb1_protos[] = { + { + SMB21_PROT, + "\2SMB 2.1", + "SMB2_10", + SMB21_PROT_ID + }, + { + SMB2X_PROT, + "\2SMB 2.???", + "SMB2_22", + SMB2X_PROT_ID + }, +}; + +static struct smb_protocol smb2_protos[] = { + { + SMB21_PROT, + "\2SMB 2.1", + "SMB2_10", + SMB21_PROT_ID + }, + { + SMB30_PROT, + "\2SMB 3.0", + "SMB3_00", + SMB30_PROT_ID + }, + { + SMB302_PROT, + "\2SMB 3.02", + "SMB3_02", + SMB302_PROT_ID + }, + { + SMB311_PROT, + "\2SMB 3.1.1", + "SMB3_11", + SMB311_PROT_ID + }, +}; + +unsigned int ksmbd_server_side_copy_max_chunk_count(void) +{ + return 256; +} + +unsigned int ksmbd_server_side_copy_max_chunk_size(void) +{ + return (2U << 30) - 1; +} + +unsigned int ksmbd_server_side_copy_max_total_size(void) +{ + return (2U << 30) - 1; +} + +inline int ksmbd_min_protocol(void) +{ + return SMB21_PROT; +} + +inline int ksmbd_max_protocol(void) +{ + return SMB311_PROT; +} + +int ksmbd_lookup_protocol_idx(char *str) +{ + int offt = ARRAY_SIZE(smb1_protos) - 1; + int len = strlen(str); + + while (offt >= 0) { + if (!strncmp(str, smb1_protos[offt].prot, len)) { + ksmbd_debug(SMB, "selected %s dialect idx = %d\n", + smb1_protos[offt].prot, offt); + return smb1_protos[offt].index; + } + offt--; + } + + offt = ARRAY_SIZE(smb2_protos) - 1; + while (offt >= 0) { + if (!strncmp(str, smb2_protos[offt].prot, len)) { + ksmbd_debug(SMB, "selected %s dialect idx = %d\n", + smb2_protos[offt].prot, offt); + return smb2_protos[offt].index; + } + offt--; + } + return -1; +} + +/** + * ksmbd_verify_smb_message() - check for valid smb2 request header + * @work: smb work + * + * check for valid smb signature and packet direction(request/response) + * + * Return: 0 on success, otherwise -EINVAL + */ +int ksmbd_verify_smb_message(struct ksmbd_work *work) +{ + struct smb2_hdr *smb2_hdr = ksmbd_req_buf_next(work); + struct smb_hdr *hdr; + + if (smb2_hdr->ProtocolId == SMB2_PROTO_NUMBER) + return ksmbd_smb2_check_message(work); + + hdr = work->request_buf; + if (*(__le32 *)hdr->Protocol == SMB1_PROTO_NUMBER && + hdr->Command == SMB_COM_NEGOTIATE) { + work->conn->outstanding_credits++; + return 0; + } + + return -EINVAL; +} + +/** + * ksmbd_smb_request() - check for valid smb request type + * @conn: connection instance + * + * Return: true on success, otherwise false + */ +bool ksmbd_smb_request(struct ksmbd_conn *conn) +{ + __le32 *proto = (__le32 *)smb2_get_msg(conn->request_buf); + + if (*proto == SMB2_COMPRESSION_TRANSFORM_ID) { + pr_err_ratelimited("smb2 compression not support yet"); + return false; + } + + if (*proto != SMB1_PROTO_NUMBER && + *proto != SMB2_PROTO_NUMBER && + *proto != SMB2_TRANSFORM_PROTO_NUM) + return false; + + return true; +} + +static bool supported_protocol(int idx) +{ + if (idx == SMB2X_PROT && + (server_conf.min_protocol >= SMB21_PROT || + server_conf.max_protocol <= SMB311_PROT)) + return true; + + return (server_conf.min_protocol <= idx && + idx <= server_conf.max_protocol); +} + +static char *next_dialect(char *dialect, int *next_off, int bcount) +{ + dialect = dialect + *next_off; + *next_off = strnlen(dialect, bcount); + if (dialect[*next_off] != '\0') + return NULL; + return dialect; +} + +static int ksmbd_lookup_dialect_by_name(char *cli_dialects, __le16 byte_count) +{ + int i, seq_num, bcount, next; + char *dialect; + + for (i = ARRAY_SIZE(smb1_protos) - 1; i >= 0; i--) { + seq_num = 0; + next = 0; + dialect = cli_dialects; + bcount = le16_to_cpu(byte_count); + do { + dialect = next_dialect(dialect, &next, bcount); + if (!dialect) + break; + ksmbd_debug(SMB, "client requested dialect %s\n", + dialect); + if (!strcmp(dialect, smb1_protos[i].name)) { + if (supported_protocol(smb1_protos[i].index)) { + ksmbd_debug(SMB, + "selected %s dialect\n", + smb1_protos[i].name); + if (smb1_protos[i].index == SMB1_PROT) + return seq_num; + return smb1_protos[i].prot_id; + } + } + seq_num++; + bcount -= (++next); + } while (bcount > 0); + } + + return BAD_PROT_ID; +} + +int ksmbd_lookup_dialect_by_id(__le16 *cli_dialects, __le16 dialects_count) +{ + int i; + int count; + + for (i = ARRAY_SIZE(smb2_protos) - 1; i >= 0; i--) { + count = le16_to_cpu(dialects_count); + while (--count >= 0) { + ksmbd_debug(SMB, "client requested dialect 0x%x\n", + le16_to_cpu(cli_dialects[count])); + if (le16_to_cpu(cli_dialects[count]) != + smb2_protos[i].prot_id) + continue; + + if (supported_protocol(smb2_protos[i].index)) { + ksmbd_debug(SMB, "selected %s dialect\n", + smb2_protos[i].name); + return smb2_protos[i].prot_id; + } + } + } + + return BAD_PROT_ID; +} + +static int ksmbd_negotiate_smb_dialect(void *buf) +{ + int smb_buf_length = get_rfc1002_len(buf); + __le32 proto = ((struct smb2_hdr *)smb2_get_msg(buf))->ProtocolId; + + if (proto == SMB2_PROTO_NUMBER) { + struct smb2_negotiate_req *req; + int smb2_neg_size = + offsetof(struct smb2_negotiate_req, Dialects); + + req = (struct smb2_negotiate_req *)smb2_get_msg(buf); + if (smb2_neg_size > smb_buf_length) + goto err_out; + + if (smb2_neg_size + le16_to_cpu(req->DialectCount) * sizeof(__le16) > + smb_buf_length) + goto err_out; + + return ksmbd_lookup_dialect_by_id(req->Dialects, + req->DialectCount); + } + + proto = *(__le32 *)((struct smb_hdr *)buf)->Protocol; + if (proto == SMB1_PROTO_NUMBER) { + struct smb_negotiate_req *req; + + req = (struct smb_negotiate_req *)buf; + if (le16_to_cpu(req->ByteCount) < 2) + goto err_out; + + if (offsetof(struct smb_negotiate_req, DialectsArray) - 4 + + le16_to_cpu(req->ByteCount) > smb_buf_length) { + goto err_out; + } + + return ksmbd_lookup_dialect_by_name(req->DialectsArray, + req->ByteCount); + } + +err_out: + return BAD_PROT_ID; +} + +#define SMB_COM_NEGOTIATE_EX 0x0 + +/** + * get_smb1_cmd_val() - get smb command value from smb header + * @work: smb work containing smb header + * + * Return: smb command value + */ +static u16 get_smb1_cmd_val(struct ksmbd_work *work) +{ + return SMB_COM_NEGOTIATE_EX; +} + +/** + * init_smb1_rsp_hdr() - initialize smb negotiate response header + * @work: smb work containing smb request + * + * Return: 0 on success, otherwise -EINVAL + */ +static int init_smb1_rsp_hdr(struct ksmbd_work *work) +{ + struct smb_hdr *rsp_hdr = (struct smb_hdr *)work->response_buf; + struct smb_hdr *rcv_hdr = (struct smb_hdr *)work->request_buf; + + /* + * Remove 4 byte direct TCP header. + */ + *(__be32 *)work->response_buf = + cpu_to_be32(sizeof(struct smb_hdr) - 4); + + rsp_hdr->Command = SMB_COM_NEGOTIATE; + *(__le32 *)rsp_hdr->Protocol = SMB1_PROTO_NUMBER; + rsp_hdr->Flags = SMBFLG_RESPONSE; + rsp_hdr->Flags2 = SMBFLG2_UNICODE | SMBFLG2_ERR_STATUS | + SMBFLG2_EXT_SEC | SMBFLG2_IS_LONG_NAME; + rsp_hdr->Pid = rcv_hdr->Pid; + rsp_hdr->Mid = rcv_hdr->Mid; + return 0; +} + +/** + * smb1_check_user_session() - check for valid session for a user + * @work: smb work containing smb request buffer + * + * Return: 0 on success, otherwise error + */ +static int smb1_check_user_session(struct ksmbd_work *work) +{ + unsigned int cmd = work->conn->ops->get_cmd_val(work); + + if (cmd == SMB_COM_NEGOTIATE_EX) + return 0; + + return -EINVAL; +} + +/** + * smb1_allocate_rsp_buf() - allocate response buffer for a command + * @work: smb work containing smb request + * + * Return: 0 on success, otherwise -ENOMEM + */ +static int smb1_allocate_rsp_buf(struct ksmbd_work *work) +{ + work->response_buf = kmalloc(MAX_CIFS_SMALL_BUFFER_SIZE, + GFP_KERNEL | __GFP_ZERO); + work->response_sz = MAX_CIFS_SMALL_BUFFER_SIZE; + + if (!work->response_buf) { + pr_err("Failed to allocate %u bytes buffer\n", + MAX_CIFS_SMALL_BUFFER_SIZE); + return -ENOMEM; + } + + return 0; +} + +static struct smb_version_ops smb1_server_ops = { + .get_cmd_val = get_smb1_cmd_val, + .init_rsp_hdr = init_smb1_rsp_hdr, + .allocate_rsp_buf = smb1_allocate_rsp_buf, + .check_user_session = smb1_check_user_session, +}; + +static int smb1_negotiate(struct ksmbd_work *work) +{ + return ksmbd_smb_negotiate_common(work, SMB_COM_NEGOTIATE); +} + +static struct smb_version_cmds smb1_server_cmds[1] = { + [SMB_COM_NEGOTIATE_EX] = { .proc = smb1_negotiate, }, +}; + +static void init_smb1_server(struct ksmbd_conn *conn) +{ + conn->ops = &smb1_server_ops; + conn->cmds = smb1_server_cmds; + conn->max_cmds = ARRAY_SIZE(smb1_server_cmds); +} + +void ksmbd_init_smb_server(struct ksmbd_work *work) +{ + struct ksmbd_conn *conn = work->conn; + __le32 proto; + + if (conn->need_neg == false) + return; + + proto = *(__le32 *)((struct smb_hdr *)work->request_buf)->Protocol; + if (proto == SMB1_PROTO_NUMBER) + init_smb1_server(conn); + else + init_smb3_11_server(conn); +} + +int ksmbd_populate_dot_dotdot_entries(struct ksmbd_work *work, int info_level, + struct ksmbd_file *dir, + struct ksmbd_dir_info *d_info, + char *search_pattern, + int (*fn)(struct ksmbd_conn *, int, + struct ksmbd_dir_info *, + struct ksmbd_kstat *)) +{ + int i, rc = 0; + struct ksmbd_conn *conn = work->conn; + struct user_namespace *user_ns = file_mnt_user_ns(dir->filp); + + for (i = 0; i < 2; i++) { + struct kstat kstat; + struct ksmbd_kstat ksmbd_kstat; + struct dentry *dentry; + + if (!dir->dot_dotdot[i]) { /* fill dot entry info */ + if (i == 0) { + d_info->name = "."; + d_info->name_len = 1; + dentry = dir->filp->f_path.dentry; + } else { + d_info->name = ".."; + d_info->name_len = 2; + dentry = dir->filp->f_path.dentry->d_parent; + } + + if (!match_pattern(d_info->name, d_info->name_len, + search_pattern)) { + dir->dot_dotdot[i] = 1; + continue; + } + + ksmbd_kstat.kstat = &kstat; + ksmbd_vfs_fill_dentry_attrs(work, + user_ns, + dentry, + &ksmbd_kstat); + rc = fn(conn, info_level, d_info, &ksmbd_kstat); + if (rc) + break; + if (d_info->out_buf_len <= 0) + break; + + dir->dot_dotdot[i] = 1; + if (d_info->flags & SMB2_RETURN_SINGLE_ENTRY) { + d_info->out_buf_len = 0; + break; + } + } + } + + return rc; +} + +/** + * ksmbd_extract_shortname() - get shortname from long filename + * @conn: connection instance + * @longname: source long filename + * @shortname: destination short filename + * + * Return: shortname length or 0 when source long name is '.' or '..' + * TODO: Though this function comforms the restriction of 8.3 Filename spec, + * but the result is different with Windows 7's one. need to check. + */ +int ksmbd_extract_shortname(struct ksmbd_conn *conn, const char *longname, + char *shortname) +{ + const char *p; + char base[9], extension[4]; + char out[13] = {0}; + int baselen = 0; + int extlen = 0, len = 0; + unsigned int csum = 0; + const unsigned char *ptr; + bool dot_present = true; + + p = longname; + if ((*p == '.') || (!(strcmp(p, "..")))) { + /*no mangling required */ + return 0; + } + + p = strrchr(longname, '.'); + if (p == longname) { /*name starts with a dot*/ + strscpy(extension, "___", strlen("___")); + } else { + if (p) { + p++; + while (*p && extlen < 3) { + if (*p != '.') + extension[extlen++] = toupper(*p); + p++; + } + extension[extlen] = '\0'; + } else { + dot_present = false; + } + } + + p = longname; + if (*p == '.') { + p++; + longname++; + } + while (*p && (baselen < 5)) { + if (*p != '.') + base[baselen++] = toupper(*p); + p++; + } + + base[baselen] = MAGIC_CHAR; + memcpy(out, base, baselen + 1); + + ptr = longname; + len = strlen(longname); + for (; len > 0; len--, ptr++) + csum += *ptr; + + csum = csum % (MANGLE_BASE * MANGLE_BASE); + out[baselen + 1] = mangle(csum / MANGLE_BASE); + out[baselen + 2] = mangle(csum); + out[baselen + 3] = PERIOD; + + if (dot_present) + memcpy(&out[baselen + 4], extension, 4); + else + out[baselen + 4] = '\0'; + smbConvertToUTF16((__le16 *)shortname, out, PATH_MAX, + conn->local_nls, 0); + len = strlen(out) * 2; + return len; +} + +static int __smb2_negotiate(struct ksmbd_conn *conn) +{ + return (conn->dialect >= SMB20_PROT_ID && + conn->dialect <= SMB311_PROT_ID); +} + +static int smb_handle_negotiate(struct ksmbd_work *work) +{ + struct smb_negotiate_rsp *neg_rsp = work->response_buf; + + ksmbd_debug(SMB, "Unsupported SMB1 protocol\n"); + + /* Add 2 byte bcc and 2 byte DialectIndex. */ + inc_rfc1001_len(work->response_buf, 4); + neg_rsp->hdr.Status.CifsError = STATUS_SUCCESS; + + neg_rsp->hdr.WordCount = 1; + neg_rsp->DialectIndex = cpu_to_le16(work->conn->dialect); + neg_rsp->ByteCount = 0; + return 0; +} + +int ksmbd_smb_negotiate_common(struct ksmbd_work *work, unsigned int command) +{ + struct ksmbd_conn *conn = work->conn; + int ret; + + conn->dialect = + ksmbd_negotiate_smb_dialect(work->request_buf); + ksmbd_debug(SMB, "conn->dialect 0x%x\n", conn->dialect); + + if (command == SMB2_NEGOTIATE_HE) { + ret = smb2_handle_negotiate(work); + return ret; + } + + if (command == SMB_COM_NEGOTIATE) { + if (__smb2_negotiate(conn)) { + init_smb3_11_server(conn); + init_smb2_neg_rsp(work); + ksmbd_debug(SMB, "Upgrade to SMB2 negotiation\n"); + return 0; + } + return smb_handle_negotiate(work); + } + + pr_err("Unknown SMB negotiation command: %u\n", command); + return -EINVAL; +} + +enum SHARED_MODE_ERRORS { + SHARE_DELETE_ERROR, + SHARE_READ_ERROR, + SHARE_WRITE_ERROR, + FILE_READ_ERROR, + FILE_WRITE_ERROR, + FILE_DELETE_ERROR, +}; + +static const char * const shared_mode_errors[] = { + "Current access mode does not permit SHARE_DELETE", + "Current access mode does not permit SHARE_READ", + "Current access mode does not permit SHARE_WRITE", + "Desired access mode does not permit FILE_READ", + "Desired access mode does not permit FILE_WRITE", + "Desired access mode does not permit FILE_DELETE", +}; + +static void smb_shared_mode_error(int error, struct ksmbd_file *prev_fp, + struct ksmbd_file *curr_fp) +{ + ksmbd_debug(SMB, "%s\n", shared_mode_errors[error]); + ksmbd_debug(SMB, "Current mode: 0x%x Desired mode: 0x%x\n", + prev_fp->saccess, curr_fp->daccess); +} + +int ksmbd_smb_check_shared_mode(struct file *filp, struct ksmbd_file *curr_fp) +{ + int rc = 0; + struct ksmbd_file *prev_fp; + + /* + * Lookup fp in master fp list, and check desired access and + * shared mode between previous open and current open. + */ + read_lock(&curr_fp->f_ci->m_lock); + list_for_each_entry(prev_fp, &curr_fp->f_ci->m_fp_list, node) { + if (file_inode(filp) != file_inode(prev_fp->filp)) + continue; + + if (filp == prev_fp->filp) + continue; + + if (ksmbd_stream_fd(prev_fp) && ksmbd_stream_fd(curr_fp)) + if (strcmp(prev_fp->stream.name, curr_fp->stream.name)) + continue; + + if (prev_fp->attrib_only != curr_fp->attrib_only) + continue; + + if (!(prev_fp->saccess & FILE_SHARE_DELETE_LE) && + curr_fp->daccess & FILE_DELETE_LE) { + smb_shared_mode_error(SHARE_DELETE_ERROR, + prev_fp, + curr_fp); + rc = -EPERM; + break; + } + + /* + * Only check FILE_SHARE_DELETE if stream opened and + * normal file opened. + */ + if (ksmbd_stream_fd(prev_fp) && !ksmbd_stream_fd(curr_fp)) + continue; + + if (!(prev_fp->saccess & FILE_SHARE_READ_LE) && + curr_fp->daccess & (FILE_EXECUTE_LE | FILE_READ_DATA_LE)) { + smb_shared_mode_error(SHARE_READ_ERROR, + prev_fp, + curr_fp); + rc = -EPERM; + break; + } + + if (!(prev_fp->saccess & FILE_SHARE_WRITE_LE) && + curr_fp->daccess & (FILE_WRITE_DATA_LE | FILE_APPEND_DATA_LE)) { + smb_shared_mode_error(SHARE_WRITE_ERROR, + prev_fp, + curr_fp); + rc = -EPERM; + break; + } + + if (prev_fp->daccess & (FILE_EXECUTE_LE | FILE_READ_DATA_LE) && + !(curr_fp->saccess & FILE_SHARE_READ_LE)) { + smb_shared_mode_error(FILE_READ_ERROR, + prev_fp, + curr_fp); + rc = -EPERM; + break; + } + + if (prev_fp->daccess & (FILE_WRITE_DATA_LE | FILE_APPEND_DATA_LE) && + !(curr_fp->saccess & FILE_SHARE_WRITE_LE)) { + smb_shared_mode_error(FILE_WRITE_ERROR, + prev_fp, + curr_fp); + rc = -EPERM; + break; + } + + if (prev_fp->daccess & FILE_DELETE_LE && + !(curr_fp->saccess & FILE_SHARE_DELETE_LE)) { + smb_shared_mode_error(FILE_DELETE_ERROR, + prev_fp, + curr_fp); + rc = -EPERM; + break; + } + } + read_unlock(&curr_fp->f_ci->m_lock); + + return rc; +} + +bool is_asterisk(char *p) +{ + return p && p[0] == '*'; +} + +int ksmbd_override_fsids(struct ksmbd_work *work) +{ + struct ksmbd_session *sess = work->sess; + struct ksmbd_share_config *share = work->tcon->share_conf; + struct cred *cred; + struct group_info *gi; + unsigned int uid; + unsigned int gid; + + uid = user_uid(sess->user); + gid = user_gid(sess->user); + if (share->force_uid != KSMBD_SHARE_INVALID_UID) + uid = share->force_uid; + if (share->force_gid != KSMBD_SHARE_INVALID_GID) + gid = share->force_gid; + + cred = prepare_kernel_cred(NULL); + if (!cred) + return -ENOMEM; + + cred->fsuid = make_kuid(&init_user_ns, uid); + cred->fsgid = make_kgid(&init_user_ns, gid); + + gi = groups_alloc(0); + if (!gi) { + abort_creds(cred); + return -ENOMEM; + } + set_groups(cred, gi); + put_group_info(gi); + + if (!uid_eq(cred->fsuid, GLOBAL_ROOT_UID)) + cred->cap_effective = cap_drop_fs_set(cred->cap_effective); + + WARN_ON(work->saved_cred); + work->saved_cred = override_creds(cred); + if (!work->saved_cred) { + abort_creds(cred); + return -EINVAL; + } + return 0; +} + +void ksmbd_revert_fsids(struct ksmbd_work *work) +{ + const struct cred *cred; + + WARN_ON(!work->saved_cred); + + cred = current_cred(); + revert_creds(work->saved_cred); + put_cred(cred); + work->saved_cred = NULL; +} + +__le32 smb_map_generic_desired_access(__le32 daccess) +{ + if (daccess & FILE_GENERIC_READ_LE) { + daccess |= cpu_to_le32(GENERIC_READ_FLAGS); + daccess &= ~FILE_GENERIC_READ_LE; + } + + if (daccess & FILE_GENERIC_WRITE_LE) { + daccess |= cpu_to_le32(GENERIC_WRITE_FLAGS); + daccess &= ~FILE_GENERIC_WRITE_LE; + } + + if (daccess & FILE_GENERIC_EXECUTE_LE) { + daccess |= cpu_to_le32(GENERIC_EXECUTE_FLAGS); + daccess &= ~FILE_GENERIC_EXECUTE_LE; + } + + if (daccess & FILE_GENERIC_ALL_LE) { + daccess |= cpu_to_le32(GENERIC_ALL_FLAGS); + daccess &= ~FILE_GENERIC_ALL_LE; + } + + return daccess; +} diff --git a/fs/smb/server/smb_common.h b/fs/smb/server/smb_common.h new file mode 100644 index 0000000000000..e63d2a4f466b5 --- /dev/null +++ b/fs/smb/server/smb_common.h @@ -0,0 +1,468 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + * Copyright (C) 2018 Samsung Electronics Co., Ltd. + */ + +#ifndef __SMB_COMMON_H__ +#define __SMB_COMMON_H__ + +#include + +#include "glob.h" +#include "nterr.h" +#include "../common/smb2pdu.h" +#include "smb2pdu.h" + +/* ksmbd's Specific ERRNO */ +#define ESHARE 50000 + +#define SMB1_PROT 0 +#define SMB2_PROT 1 +#define SMB21_PROT 2 +/* multi-protocol negotiate request */ +#define SMB2X_PROT 3 +#define SMB30_PROT 4 +#define SMB302_PROT 5 +#define SMB311_PROT 6 +#define BAD_PROT 0xFFFF + +#define SMB1_VERSION_STRING "1.0" +#define SMB20_VERSION_STRING "2.0" +#define SMB21_VERSION_STRING "2.1" +#define SMB30_VERSION_STRING "3.0" +#define SMB302_VERSION_STRING "3.02" +#define SMB311_VERSION_STRING "3.1.1" + +#define SMB_ECHO_INTERVAL (60 * HZ) + +#define CIFS_DEFAULT_IOSIZE (64 * 1024) +#define MAX_CIFS_SMALL_BUFFER_SIZE 448 /* big enough for most */ + +#define MAX_STREAM_PROT_LEN 0x00FFFFFF + +/* Responses when opening a file. */ +#define F_SUPERSEDED 0 +#define F_OPENED 1 +#define F_CREATED 2 +#define F_OVERWRITTEN 3 + +/* + * File Attribute flags + */ +#define ATTR_POSIX_SEMANTICS 0x01000000 +#define ATTR_BACKUP_SEMANTICS 0x02000000 +#define ATTR_DELETE_ON_CLOSE 0x04000000 +#define ATTR_SEQUENTIAL_SCAN 0x08000000 +#define ATTR_RANDOM_ACCESS 0x10000000 +#define ATTR_NO_BUFFERING 0x20000000 +#define ATTR_WRITE_THROUGH 0x80000000 + +/* List of FileSystemAttributes - see 2.5.1 of MS-FSCC */ +#define FILE_SUPPORTS_SPARSE_VDL 0x10000000 /* faster nonsparse extend */ +#define FILE_SUPPORTS_BLOCK_REFCOUNTING 0x08000000 /* allow ioctl dup extents */ +#define FILE_SUPPORT_INTEGRITY_STREAMS 0x04000000 +#define FILE_SUPPORTS_USN_JOURNAL 0x02000000 +#define FILE_SUPPORTS_OPEN_BY_FILE_ID 0x01000000 +#define FILE_SUPPORTS_EXTENDED_ATTRIBUTES 0x00800000 +#define FILE_SUPPORTS_HARD_LINKS 0x00400000 +#define FILE_SUPPORTS_TRANSACTIONS 0x00200000 +#define FILE_SEQUENTIAL_WRITE_ONCE 0x00100000 +#define FILE_READ_ONLY_VOLUME 0x00080000 +#define FILE_NAMED_STREAMS 0x00040000 +#define FILE_SUPPORTS_ENCRYPTION 0x00020000 +#define FILE_SUPPORTS_OBJECT_IDS 0x00010000 +#define FILE_VOLUME_IS_COMPRESSED 0x00008000 +#define FILE_SUPPORTS_REMOTE_STORAGE 0x00000100 +#define FILE_SUPPORTS_REPARSE_POINTS 0x00000080 +#define FILE_SUPPORTS_SPARSE_FILES 0x00000040 +#define FILE_VOLUME_QUOTAS 0x00000020 +#define FILE_FILE_COMPRESSION 0x00000010 +#define FILE_PERSISTENT_ACLS 0x00000008 +#define FILE_UNICODE_ON_DISK 0x00000004 +#define FILE_CASE_PRESERVED_NAMES 0x00000002 +#define FILE_CASE_SENSITIVE_SEARCH 0x00000001 + +#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 GENERIC_ALL 0x10000000 +#define GENERIC_EXECUTE 0x20000000 +#define GENERIC_WRITE 0x40000000 +#define GENERIC_READ 0x80000000 +/* In summary - Relevant file */ +/* access flags from CIFS are */ +/* file_read_data, file_write_data */ +/* file_execute, file_read_attributes*/ +/* write_dac, and delete. */ + +#define SET_FILE_READ_RIGHTS (FILE_READ_DATA | FILE_READ_EA \ + | FILE_READ_ATTRIBUTES \ + | DELETE | READ_CONTROL | WRITE_DAC \ + | WRITE_OWNER | SYNCHRONIZE) +#define SET_FILE_WRITE_RIGHTS (FILE_WRITE_DATA | FILE_APPEND_DATA \ + | FILE_WRITE_EA \ + | FILE_DELETE_CHILD \ + | FILE_WRITE_ATTRIBUTES \ + | DELETE | READ_CONTROL | WRITE_DAC \ + | WRITE_OWNER | SYNCHRONIZE) +#define SET_FILE_EXEC_RIGHTS (FILE_READ_EA | FILE_WRITE_EA | FILE_EXECUTE \ + | FILE_READ_ATTRIBUTES \ + | FILE_WRITE_ATTRIBUTES \ + | DELETE | READ_CONTROL | WRITE_DAC \ + | WRITE_OWNER | SYNCHRONIZE) + +#define SET_MINIMUM_RIGHTS (FILE_READ_EA | FILE_READ_ATTRIBUTES \ + | READ_CONTROL | SYNCHRONIZE) + +/* generic flags for file open */ +#define GENERIC_READ_FLAGS (READ_CONTROL | FILE_READ_DATA | \ + FILE_READ_ATTRIBUTES | \ + FILE_READ_EA | SYNCHRONIZE) + +#define GENERIC_WRITE_FLAGS (READ_CONTROL | FILE_WRITE_DATA | \ + FILE_WRITE_ATTRIBUTES | FILE_WRITE_EA | \ + FILE_APPEND_DATA | SYNCHRONIZE) + +#define GENERIC_EXECUTE_FLAGS (READ_CONTROL | FILE_EXECUTE | \ + FILE_READ_ATTRIBUTES | SYNCHRONIZE) + +#define GENERIC_ALL_FLAGS (DELETE | READ_CONTROL | WRITE_DAC | \ + WRITE_OWNER | SYNCHRONIZE | FILE_READ_DATA | \ + FILE_WRITE_DATA | FILE_APPEND_DATA | \ + FILE_READ_EA | FILE_WRITE_EA | \ + FILE_EXECUTE | FILE_DELETE_CHILD | \ + FILE_READ_ATTRIBUTES | FILE_WRITE_ATTRIBUTES) + +#define SMB1_PROTO_NUMBER cpu_to_le32(0x424d53ff) +#define SMB_COM_NEGOTIATE 0x72 +#define SMB1_CLIENT_GUID_SIZE (16) + +#define SMBFLG_RESPONSE 0x80 /* this PDU is a response from server */ + +#define SMBFLG2_IS_LONG_NAME cpu_to_le16(0x40) +#define SMBFLG2_EXT_SEC cpu_to_le16(0x800) +#define SMBFLG2_ERR_STATUS cpu_to_le16(0x4000) +#define SMBFLG2_UNICODE cpu_to_le16(0x8000) + +struct smb_hdr { + __be32 smb_buf_length; + __u8 Protocol[4]; + __u8 Command; + union { + struct { + __u8 ErrorClass; + __u8 Reserved; + __le16 Error; + } __packed DosError; + __le32 CifsError; + } __packed Status; + __u8 Flags; + __le16 Flags2; /* note: le */ + __le16 PidHigh; + union { + struct { + __le32 SequenceNumber; /* le */ + __u32 Reserved; /* zero */ + } __packed Sequence; + __u8 SecuritySignature[8]; /* le */ + } __packed Signature; + __u8 pad[2]; + __le16 Tid; + __le16 Pid; + __le16 Uid; + __le16 Mid; + __u8 WordCount; +} __packed; + +struct smb_negotiate_req { + struct smb_hdr hdr; /* wct = 0 */ + __le16 ByteCount; + unsigned char DialectsArray[1]; +} __packed; + +struct smb_negotiate_rsp { + struct smb_hdr hdr; /* wct = 17 */ + __le16 DialectIndex; /* 0xFFFF = no dialect acceptable */ + __le16 ByteCount; +} __packed; + +struct filesystem_attribute_info { + __le32 Attributes; + __le32 MaxPathNameComponentLength; + __le32 FileSystemNameLen; + __le16 FileSystemName[1]; /* do not have to save this - get subset? */ +} __packed; + +struct filesystem_device_info { + __le32 DeviceType; + __le32 DeviceCharacteristics; +} __packed; /* device info level 0x104 */ + +struct filesystem_vol_info { + __le64 VolumeCreationTime; + __le32 SerialNumber; + __le32 VolumeLabelSize; + __le16 Reserved; + __le16 VolumeLabel[1]; +} __packed; + +struct filesystem_info { + __le64 TotalAllocationUnits; + __le64 FreeAllocationUnits; + __le32 SectorsPerAllocationUnit; + __le32 BytesPerSector; +} __packed; /* size info, level 0x103 */ + +#define EXTENDED_INFO_MAGIC 0x43667364 /* Cfsd */ +#define STRING_LENGTH 28 + +struct fs_extended_info { + __le32 magic; + __le32 version; + __le32 release; + __u64 rel_date; + char version_string[STRING_LENGTH]; +} __packed; + +struct object_id_info { + char objid[16]; + struct fs_extended_info extended_info; +} __packed; + +struct file_directory_info { + __le32 NextEntryOffset; + __u32 FileIndex; + __le64 CreationTime; + __le64 LastAccessTime; + __le64 LastWriteTime; + __le64 ChangeTime; + __le64 EndOfFile; + __le64 AllocationSize; + __le32 ExtFileAttributes; + __le32 FileNameLength; + char FileName[1]; +} __packed; /* level 0x101 FF resp data */ + +struct file_names_info { + __le32 NextEntryOffset; + __u32 FileIndex; + __le32 FileNameLength; + char FileName[1]; +} __packed; /* level 0xc FF resp data */ + +struct file_full_directory_info { + __le32 NextEntryOffset; + __u32 FileIndex; + __le64 CreationTime; + __le64 LastAccessTime; + __le64 LastWriteTime; + __le64 ChangeTime; + __le64 EndOfFile; + __le64 AllocationSize; + __le32 ExtFileAttributes; + __le32 FileNameLength; + __le32 EaSize; + char FileName[1]; +} __packed; /* level 0x102 FF resp */ + +struct file_both_directory_info { + __le32 NextEntryOffset; + __u32 FileIndex; + __le64 CreationTime; + __le64 LastAccessTime; + __le64 LastWriteTime; + __le64 ChangeTime; + __le64 EndOfFile; + __le64 AllocationSize; + __le32 ExtFileAttributes; + __le32 FileNameLength; + __le32 EaSize; /* length of the xattrs */ + __u8 ShortNameLength; + __u8 Reserved; + __u8 ShortName[24]; + char FileName[1]; +} __packed; /* level 0x104 FFrsp data */ + +struct file_id_both_directory_info { + __le32 NextEntryOffset; + __u32 FileIndex; + __le64 CreationTime; + __le64 LastAccessTime; + __le64 LastWriteTime; + __le64 ChangeTime; + __le64 EndOfFile; + __le64 AllocationSize; + __le32 ExtFileAttributes; + __le32 FileNameLength; + __le32 EaSize; /* length of the xattrs */ + __u8 ShortNameLength; + __u8 Reserved; + __u8 ShortName[24]; + __le16 Reserved2; + __le64 UniqueId; + char FileName[1]; +} __packed; + +struct file_id_full_dir_info { + __le32 NextEntryOffset; + __u32 FileIndex; + __le64 CreationTime; + __le64 LastAccessTime; + __le64 LastWriteTime; + __le64 ChangeTime; + __le64 EndOfFile; + __le64 AllocationSize; + __le32 ExtFileAttributes; + __le32 FileNameLength; + __le32 EaSize; /* EA size */ + __le32 Reserved; + __le64 UniqueId; /* inode num - le since Samba puts ino in low 32 bit*/ + char FileName[1]; +} __packed; /* level 0x105 FF rsp data */ + +struct smb_version_values { + char *version_string; + __u16 protocol_id; + __le16 lock_cmd; + __u32 capabilities; + __u32 max_read_size; + __u32 max_write_size; + __u32 max_trans_size; + __u32 max_credits; + __u32 large_lock_type; + __u32 exclusive_lock_type; + __u32 shared_lock_type; + __u32 unlock_lock_type; + size_t header_size; + size_t max_header_size; + size_t read_rsp_size; + unsigned int cap_unix; + unsigned int cap_nt_find; + unsigned int cap_large_files; + __u16 signing_enabled; + __u16 signing_required; + size_t create_lease_size; + size_t create_durable_size; + size_t create_durable_v2_size; + size_t create_mxac_size; + size_t create_disk_id_size; + size_t create_posix_size; +}; + +struct filesystem_posix_info { + /* For undefined recommended transfer size return -1 in that field */ + __le32 OptimalTransferSize; /* bsize on some os, iosize on other os */ + __le32 BlockSize; + /* The next three fields are in terms of the block size. + * (above). If block size is unknown, 4096 would be a + * reasonable block size for a server to report. + * Note that returning the blocks/blocksavail removes need + * to make a second call (to QFSInfo level 0x103 to get this info. + * UserBlockAvail is typically less than or equal to BlocksAvail, + * if no distinction is made return the same value in each + */ + __le64 TotalBlocks; + __le64 BlocksAvail; /* bfree */ + __le64 UserBlocksAvail; /* bavail */ + /* For undefined Node fields or FSID return -1 */ + __le64 TotalFileNodes; + __le64 FreeFileNodes; + __le64 FileSysIdentifier; /* fsid */ + /* NB Namelen comes from FILE_SYSTEM_ATTRIBUTE_INFO call */ + /* NB flags can come from FILE_SYSTEM_DEVICE_INFO call */ +} __packed; + +struct smb_version_ops { + u16 (*get_cmd_val)(struct ksmbd_work *swork); + int (*init_rsp_hdr)(struct ksmbd_work *swork); + void (*set_rsp_status)(struct ksmbd_work *swork, __le32 err); + int (*allocate_rsp_buf)(struct ksmbd_work *work); + int (*set_rsp_credits)(struct ksmbd_work *work); + int (*check_user_session)(struct ksmbd_work *work); + int (*get_ksmbd_tcon)(struct ksmbd_work *work); + bool (*is_sign_req)(struct ksmbd_work *work, unsigned int command); + int (*check_sign_req)(struct ksmbd_work *work); + void (*set_sign_rsp)(struct ksmbd_work *work); + int (*generate_signingkey)(struct ksmbd_session *sess, struct ksmbd_conn *conn); + int (*generate_encryptionkey)(struct ksmbd_conn *conn, struct ksmbd_session *sess); + bool (*is_transform_hdr)(void *buf); + int (*decrypt_req)(struct ksmbd_work *work); + int (*encrypt_resp)(struct ksmbd_work *work); +}; + +struct smb_version_cmds { + int (*proc)(struct ksmbd_work *swork); +}; + +int ksmbd_min_protocol(void); +int ksmbd_max_protocol(void); + +int ksmbd_lookup_protocol_idx(char *str); + +int ksmbd_verify_smb_message(struct ksmbd_work *work); +bool ksmbd_smb_request(struct ksmbd_conn *conn); + +int ksmbd_lookup_dialect_by_id(__le16 *cli_dialects, __le16 dialects_count); + +void ksmbd_init_smb_server(struct ksmbd_work *work); + +struct ksmbd_kstat; +int ksmbd_populate_dot_dotdot_entries(struct ksmbd_work *work, + int info_level, + struct ksmbd_file *dir, + struct ksmbd_dir_info *d_info, + char *search_pattern, + int (*fn)(struct ksmbd_conn *, + int, + struct ksmbd_dir_info *, + struct ksmbd_kstat *)); + +int ksmbd_extract_shortname(struct ksmbd_conn *conn, + const char *longname, + char *shortname); + +int ksmbd_smb_negotiate_common(struct ksmbd_work *work, unsigned int command); + +int ksmbd_smb_check_shared_mode(struct file *filp, struct ksmbd_file *curr_fp); +int ksmbd_override_fsids(struct ksmbd_work *work); +void ksmbd_revert_fsids(struct ksmbd_work *work); + +unsigned int ksmbd_server_side_copy_max_chunk_count(void); +unsigned int ksmbd_server_side_copy_max_chunk_size(void); +unsigned int ksmbd_server_side_copy_max_total_size(void); +bool is_asterisk(char *p); +__le32 smb_map_generic_desired_access(__le32 daccess); + +static inline unsigned int get_rfc1002_len(void *buf) +{ + return be32_to_cpu(*((__be32 *)buf)) & 0xffffff; +} + +static inline void inc_rfc1001_len(void *buf, int count) +{ + be32_add_cpu((__be32 *)buf, count); +} +#endif /* __SMB_COMMON_H__ */ diff --git a/fs/smb/server/smbacl.c b/fs/smb/server/smbacl.c new file mode 100644 index 0000000000000..b05ff9b146b55 --- /dev/null +++ b/fs/smb/server/smbacl.c @@ -0,0 +1,1436 @@ +// SPDX-License-Identifier: LGPL-2.1+ +/* + * Copyright (C) International Business Machines Corp., 2007,2008 + * Author(s): Steve French (sfrench@us.ibm.com) + * Copyright (C) 2020 Samsung Electronics Co., Ltd. + * Author(s): Namjae Jeon + */ + +#include +#include +#include +#include + +#include "smbacl.h" +#include "smb_common.h" +#include "server.h" +#include "misc.h" +#include "mgmt/share_config.h" + +static const struct smb_sid domain = {1, 4, {0, 0, 0, 0, 0, 5}, + {cpu_to_le32(21), cpu_to_le32(1), cpu_to_le32(2), cpu_to_le32(3), + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0} }; + +/* security id for everyone/world system group */ +static const struct smb_sid creator_owner = { + 1, 1, {0, 0, 0, 0, 0, 3}, {0} }; +/* security id for everyone/world system group */ +static const struct smb_sid creator_group = { + 1, 1, {0, 0, 0, 0, 0, 3}, {cpu_to_le32(1)} }; + +/* security id for everyone/world system group */ +static const struct smb_sid sid_everyone = { + 1, 1, {0, 0, 0, 0, 0, 1}, {0} }; +/* security id for Authenticated Users system group */ +static const struct smb_sid sid_authusers = { + 1, 1, {0, 0, 0, 0, 0, 5}, {cpu_to_le32(11)} }; + +/* S-1-22-1 Unmapped Unix users */ +static const struct smb_sid sid_unix_users = {1, 1, {0, 0, 0, 0, 0, 22}, + {cpu_to_le32(1), 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0} }; + +/* S-1-22-2 Unmapped Unix groups */ +static const struct smb_sid sid_unix_groups = { 1, 1, {0, 0, 0, 0, 0, 22}, + {cpu_to_le32(2), 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0} }; + +/* + * See http://technet.microsoft.com/en-us/library/hh509017(v=ws.10).aspx + */ + +/* S-1-5-88 MS NFS and Apple style UID/GID/mode */ + +/* S-1-5-88-1 Unix uid */ +static const struct smb_sid sid_unix_NFS_users = { 1, 2, {0, 0, 0, 0, 0, 5}, + {cpu_to_le32(88), + cpu_to_le32(1), 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0} }; + +/* S-1-5-88-2 Unix gid */ +static const struct smb_sid sid_unix_NFS_groups = { 1, 2, {0, 0, 0, 0, 0, 5}, + {cpu_to_le32(88), + cpu_to_le32(2), 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0} }; + +/* S-1-5-88-3 Unix mode */ +static const struct smb_sid sid_unix_NFS_mode = { 1, 2, {0, 0, 0, 0, 0, 5}, + {cpu_to_le32(88), + cpu_to_le32(3), 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0} }; + +/* + * if the two SIDs (roughly equivalent to a UUID for a user or group) are + * the same returns zero, if they do not match returns non-zero. + */ +int compare_sids(const struct smb_sid *ctsid, const struct smb_sid *cwsid) +{ + int i; + int num_subauth, num_sat, num_saw; + + if (!ctsid || !cwsid) + return 1; + + /* compare the revision */ + if (ctsid->revision != cwsid->revision) { + if (ctsid->revision > cwsid->revision) + return 1; + else + return -1; + } + + /* compare all of the six auth values */ + for (i = 0; i < NUM_AUTHS; ++i) { + if (ctsid->authority[i] != cwsid->authority[i]) { + if (ctsid->authority[i] > cwsid->authority[i]) + return 1; + else + return -1; + } + } + + /* compare all of the subauth values if any */ + num_sat = ctsid->num_subauth; + num_saw = cwsid->num_subauth; + num_subauth = num_sat < num_saw ? num_sat : num_saw; + if (num_subauth) { + for (i = 0; i < num_subauth; ++i) { + if (ctsid->sub_auth[i] != cwsid->sub_auth[i]) { + if (le32_to_cpu(ctsid->sub_auth[i]) > + le32_to_cpu(cwsid->sub_auth[i])) + return 1; + else + return -1; + } + } + } + + return 0; /* sids compare/match */ +} + +static void smb_copy_sid(struct smb_sid *dst, const struct smb_sid *src) +{ + int i; + + dst->revision = src->revision; + dst->num_subauth = min_t(u8, src->num_subauth, SID_MAX_SUB_AUTHORITIES); + for (i = 0; i < NUM_AUTHS; ++i) + dst->authority[i] = src->authority[i]; + for (i = 0; i < dst->num_subauth; ++i) + dst->sub_auth[i] = src->sub_auth[i]; +} + +/* + * change posix mode to reflect permissions + * pmode is the existing mode (we only want to overwrite part of this + * bits to set can be: S_IRWXU, S_IRWXG or S_IRWXO ie 00700 or 00070 or 00007 + */ +static umode_t access_flags_to_mode(struct smb_fattr *fattr, __le32 ace_flags, + int type) +{ + __u32 flags = le32_to_cpu(ace_flags); + umode_t mode = 0; + + if (flags & GENERIC_ALL) { + mode = 0777; + ksmbd_debug(SMB, "all perms\n"); + return mode; + } + + if ((flags & GENERIC_READ) || (flags & FILE_READ_RIGHTS)) + mode = 0444; + if ((flags & GENERIC_WRITE) || (flags & FILE_WRITE_RIGHTS)) { + mode |= 0222; + if (S_ISDIR(fattr->cf_mode)) + mode |= 0111; + } + if ((flags & GENERIC_EXECUTE) || (flags & FILE_EXEC_RIGHTS)) + mode |= 0111; + + if (type == ACCESS_DENIED_ACE_TYPE || type == ACCESS_DENIED_OBJECT_ACE_TYPE) + mode = ~mode; + + ksmbd_debug(SMB, "access flags 0x%x mode now %04o\n", flags, mode); + + return mode; +} + +/* + * Generate access flags to reflect permissions mode is the existing mode. + * This function is called for every ACE in the DACL whose SID matches + * with either owner or group or everyone. + */ +static void mode_to_access_flags(umode_t mode, umode_t bits_to_use, + __u32 *pace_flags) +{ + /* reset access mask */ + *pace_flags = 0x0; + + /* bits to use are either S_IRWXU or S_IRWXG or S_IRWXO */ + mode &= bits_to_use; + + /* + * check for R/W/X UGO since we do not know whose flags + * is this but we have cleared all the bits sans RWX for + * either user or group or other as per bits_to_use + */ + if (mode & 0444) + *pace_flags |= SET_FILE_READ_RIGHTS; + if (mode & 0222) + *pace_flags |= FILE_WRITE_RIGHTS; + if (mode & 0111) + *pace_flags |= SET_FILE_EXEC_RIGHTS; + + ksmbd_debug(SMB, "mode: %o, access flags now 0x%x\n", + mode, *pace_flags); +} + +static __u16 fill_ace_for_sid(struct smb_ace *pntace, + const struct smb_sid *psid, int type, int flags, + umode_t mode, umode_t bits) +{ + int i; + __u16 size = 0; + __u32 access_req = 0; + + pntace->type = type; + pntace->flags = flags; + mode_to_access_flags(mode, bits, &access_req); + if (!access_req) + access_req = SET_MINIMUM_RIGHTS; + pntace->access_req = cpu_to_le32(access_req); + + pntace->sid.revision = psid->revision; + pntace->sid.num_subauth = psid->num_subauth; + for (i = 0; i < NUM_AUTHS; i++) + pntace->sid.authority[i] = psid->authority[i]; + for (i = 0; i < psid->num_subauth; i++) + pntace->sid.sub_auth[i] = psid->sub_auth[i]; + + size = 1 + 1 + 2 + 4 + 1 + 1 + 6 + (psid->num_subauth * 4); + pntace->size = cpu_to_le16(size); + + return size; +} + +void id_to_sid(unsigned int cid, uint sidtype, struct smb_sid *ssid) +{ + switch (sidtype) { + case SIDOWNER: + smb_copy_sid(ssid, &server_conf.domain_sid); + break; + case SIDUNIX_USER: + smb_copy_sid(ssid, &sid_unix_users); + break; + case SIDUNIX_GROUP: + smb_copy_sid(ssid, &sid_unix_groups); + break; + case SIDCREATOR_OWNER: + smb_copy_sid(ssid, &creator_owner); + return; + case SIDCREATOR_GROUP: + smb_copy_sid(ssid, &creator_group); + return; + case SIDNFS_USER: + smb_copy_sid(ssid, &sid_unix_NFS_users); + break; + case SIDNFS_GROUP: + smb_copy_sid(ssid, &sid_unix_NFS_groups); + break; + case SIDNFS_MODE: + smb_copy_sid(ssid, &sid_unix_NFS_mode); + break; + default: + return; + } + + /* RID */ + ssid->sub_auth[ssid->num_subauth] = cpu_to_le32(cid); + ssid->num_subauth++; +} + +static int sid_to_id(struct user_namespace *user_ns, + struct smb_sid *psid, uint sidtype, + struct smb_fattr *fattr) +{ + int rc = -EINVAL; + + /* + * If we have too many subauthorities, then something is really wrong. + * Just return an error. + */ + if (unlikely(psid->num_subauth > SID_MAX_SUB_AUTHORITIES)) { + pr_err("%s: %u subauthorities is too many!\n", + __func__, psid->num_subauth); + return -EIO; + } + + if (sidtype == SIDOWNER) { + kuid_t uid; + uid_t id; + + id = le32_to_cpu(psid->sub_auth[psid->num_subauth - 1]); + uid = KUIDT_INIT(id); + uid = from_vfsuid(user_ns, &init_user_ns, VFSUIDT_INIT(uid)); + if (uid_valid(uid)) { + fattr->cf_uid = uid; + rc = 0; + } + } else { + kgid_t gid; + gid_t id; + + id = le32_to_cpu(psid->sub_auth[psid->num_subauth - 1]); + gid = KGIDT_INIT(id); + gid = from_vfsgid(user_ns, &init_user_ns, VFSGIDT_INIT(gid)); + if (gid_valid(gid)) { + fattr->cf_gid = gid; + rc = 0; + } + } + + return rc; +} + +void posix_state_to_acl(struct posix_acl_state *state, + struct posix_acl_entry *pace) +{ + int i; + + pace->e_tag = ACL_USER_OBJ; + pace->e_perm = state->owner.allow; + for (i = 0; i < state->users->n; i++) { + pace++; + pace->e_tag = ACL_USER; + pace->e_uid = state->users->aces[i].uid; + pace->e_perm = state->users->aces[i].perms.allow; + } + + pace++; + pace->e_tag = ACL_GROUP_OBJ; + pace->e_perm = state->group.allow; + + for (i = 0; i < state->groups->n; i++) { + pace++; + pace->e_tag = ACL_GROUP; + pace->e_gid = state->groups->aces[i].gid; + pace->e_perm = state->groups->aces[i].perms.allow; + } + + if (state->users->n || state->groups->n) { + pace++; + pace->e_tag = ACL_MASK; + pace->e_perm = state->mask.allow; + } + + pace++; + pace->e_tag = ACL_OTHER; + pace->e_perm = state->other.allow; +} + +int init_acl_state(struct posix_acl_state *state, int cnt) +{ + int alloc; + + memset(state, 0, sizeof(struct posix_acl_state)); + /* + * In the worst case, each individual acl could be for a distinct + * named user or group, but we don't know which, so we allocate + * enough space for either: + */ + alloc = sizeof(struct posix_ace_state_array) + + cnt * sizeof(struct posix_user_ace_state); + state->users = kzalloc(alloc, GFP_KERNEL); + if (!state->users) + return -ENOMEM; + state->groups = kzalloc(alloc, GFP_KERNEL); + if (!state->groups) { + kfree(state->users); + return -ENOMEM; + } + return 0; +} + +void free_acl_state(struct posix_acl_state *state) +{ + kfree(state->users); + kfree(state->groups); +} + +static void parse_dacl(struct user_namespace *user_ns, + struct smb_acl *pdacl, char *end_of_acl, + struct smb_sid *pownersid, struct smb_sid *pgrpsid, + struct smb_fattr *fattr) +{ + int i, ret; + int num_aces = 0; + unsigned int acl_size; + char *acl_base; + struct smb_ace **ppace; + struct posix_acl_entry *cf_pace, *cf_pdace; + struct posix_acl_state acl_state, default_acl_state; + umode_t mode = 0, acl_mode; + bool owner_found = false, group_found = false, others_found = false; + + if (!pdacl) + return; + + /* validate that we do not go past end of acl */ + if (end_of_acl < (char *)pdacl + sizeof(struct smb_acl) || + end_of_acl < (char *)pdacl + le16_to_cpu(pdacl->size)) { + pr_err("ACL too small to parse DACL\n"); + return; + } + + ksmbd_debug(SMB, "DACL revision %d size %d num aces %d\n", + le16_to_cpu(pdacl->revision), le16_to_cpu(pdacl->size), + le32_to_cpu(pdacl->num_aces)); + + acl_base = (char *)pdacl; + acl_size = sizeof(struct smb_acl); + + num_aces = le32_to_cpu(pdacl->num_aces); + if (num_aces <= 0) + return; + + if (num_aces > ULONG_MAX / sizeof(struct smb_ace *)) + return; + + ppace = kmalloc_array(num_aces, sizeof(struct smb_ace *), GFP_KERNEL); + if (!ppace) + return; + + ret = init_acl_state(&acl_state, num_aces); + if (ret) + return; + ret = init_acl_state(&default_acl_state, num_aces); + if (ret) { + free_acl_state(&acl_state); + return; + } + + /* + * reset rwx permissions for user/group/other. + * Also, if num_aces is 0 i.e. DACL has no ACEs, + * user/group/other have no permissions + */ + for (i = 0; i < num_aces; ++i) { + if (end_of_acl - acl_base < acl_size) + break; + + ppace[i] = (struct smb_ace *)(acl_base + acl_size); + acl_base = (char *)ppace[i]; + acl_size = offsetof(struct smb_ace, sid) + + offsetof(struct smb_sid, sub_auth); + + if (end_of_acl - acl_base < acl_size || + ppace[i]->sid.num_subauth > SID_MAX_SUB_AUTHORITIES || + (end_of_acl - acl_base < + acl_size + sizeof(__le32) * ppace[i]->sid.num_subauth) || + (le16_to_cpu(ppace[i]->size) < + acl_size + sizeof(__le32) * ppace[i]->sid.num_subauth)) + break; + + acl_size = le16_to_cpu(ppace[i]->size); + ppace[i]->access_req = + smb_map_generic_desired_access(ppace[i]->access_req); + + if (!(compare_sids(&ppace[i]->sid, &sid_unix_NFS_mode))) { + fattr->cf_mode = + le32_to_cpu(ppace[i]->sid.sub_auth[2]); + break; + } else if (!compare_sids(&ppace[i]->sid, pownersid)) { + acl_mode = access_flags_to_mode(fattr, + ppace[i]->access_req, + ppace[i]->type); + acl_mode &= 0700; + + if (!owner_found) { + mode &= ~(0700); + mode |= acl_mode; + } + owner_found = true; + } else if (!compare_sids(&ppace[i]->sid, pgrpsid) || + ppace[i]->sid.sub_auth[ppace[i]->sid.num_subauth - 1] == + DOMAIN_USER_RID_LE) { + acl_mode = access_flags_to_mode(fattr, + ppace[i]->access_req, + ppace[i]->type); + acl_mode &= 0070; + if (!group_found) { + mode &= ~(0070); + mode |= acl_mode; + } + group_found = true; + } else if (!compare_sids(&ppace[i]->sid, &sid_everyone)) { + acl_mode = access_flags_to_mode(fattr, + ppace[i]->access_req, + ppace[i]->type); + acl_mode &= 0007; + if (!others_found) { + mode &= ~(0007); + mode |= acl_mode; + } + others_found = true; + } else if (!compare_sids(&ppace[i]->sid, &creator_owner)) { + continue; + } else if (!compare_sids(&ppace[i]->sid, &creator_group)) { + continue; + } else if (!compare_sids(&ppace[i]->sid, &sid_authusers)) { + continue; + } else { + struct smb_fattr temp_fattr; + + acl_mode = access_flags_to_mode(fattr, ppace[i]->access_req, + ppace[i]->type); + temp_fattr.cf_uid = INVALID_UID; + ret = sid_to_id(user_ns, &ppace[i]->sid, SIDOWNER, &temp_fattr); + if (ret || uid_eq(temp_fattr.cf_uid, INVALID_UID)) { + pr_err("%s: Error %d mapping Owner SID to uid\n", + __func__, ret); + continue; + } + + acl_state.owner.allow = ((acl_mode & 0700) >> 6) | 0004; + acl_state.users->aces[acl_state.users->n].uid = + temp_fattr.cf_uid; + acl_state.users->aces[acl_state.users->n++].perms.allow = + ((acl_mode & 0700) >> 6) | 0004; + default_acl_state.owner.allow = ((acl_mode & 0700) >> 6) | 0004; + default_acl_state.users->aces[default_acl_state.users->n].uid = + temp_fattr.cf_uid; + default_acl_state.users->aces[default_acl_state.users->n++].perms.allow = + ((acl_mode & 0700) >> 6) | 0004; + } + } + kfree(ppace); + + if (owner_found) { + /* The owner must be set to at least read-only. */ + acl_state.owner.allow = ((mode & 0700) >> 6) | 0004; + acl_state.users->aces[acl_state.users->n].uid = fattr->cf_uid; + acl_state.users->aces[acl_state.users->n++].perms.allow = + ((mode & 0700) >> 6) | 0004; + default_acl_state.owner.allow = ((mode & 0700) >> 6) | 0004; + default_acl_state.users->aces[default_acl_state.users->n].uid = + fattr->cf_uid; + default_acl_state.users->aces[default_acl_state.users->n++].perms.allow = + ((mode & 0700) >> 6) | 0004; + } + + if (group_found) { + acl_state.group.allow = (mode & 0070) >> 3; + acl_state.groups->aces[acl_state.groups->n].gid = + fattr->cf_gid; + acl_state.groups->aces[acl_state.groups->n++].perms.allow = + (mode & 0070) >> 3; + default_acl_state.group.allow = (mode & 0070) >> 3; + default_acl_state.groups->aces[default_acl_state.groups->n].gid = + fattr->cf_gid; + default_acl_state.groups->aces[default_acl_state.groups->n++].perms.allow = + (mode & 0070) >> 3; + } + + if (others_found) { + fattr->cf_mode &= ~(0007); + fattr->cf_mode |= mode & 0007; + + acl_state.other.allow = mode & 0007; + default_acl_state.other.allow = mode & 0007; + } + + if (acl_state.users->n || acl_state.groups->n) { + acl_state.mask.allow = 0x07; + + if (IS_ENABLED(CONFIG_FS_POSIX_ACL)) { + fattr->cf_acls = + posix_acl_alloc(acl_state.users->n + + acl_state.groups->n + 4, GFP_KERNEL); + if (fattr->cf_acls) { + cf_pace = fattr->cf_acls->a_entries; + posix_state_to_acl(&acl_state, cf_pace); + } + } + } + + if (default_acl_state.users->n || default_acl_state.groups->n) { + default_acl_state.mask.allow = 0x07; + + if (IS_ENABLED(CONFIG_FS_POSIX_ACL)) { + fattr->cf_dacls = + posix_acl_alloc(default_acl_state.users->n + + default_acl_state.groups->n + 4, GFP_KERNEL); + if (fattr->cf_dacls) { + cf_pdace = fattr->cf_dacls->a_entries; + posix_state_to_acl(&default_acl_state, cf_pdace); + } + } + } + free_acl_state(&acl_state); + free_acl_state(&default_acl_state); +} + +static void set_posix_acl_entries_dacl(struct user_namespace *user_ns, + struct smb_ace *pndace, + struct smb_fattr *fattr, u32 *num_aces, + u16 *size, u32 nt_aces_num) +{ + struct posix_acl_entry *pace; + struct smb_sid *sid; + struct smb_ace *ntace; + int i, j; + + if (!fattr->cf_acls) + goto posix_default_acl; + + pace = fattr->cf_acls->a_entries; + for (i = 0; i < fattr->cf_acls->a_count; i++, pace++) { + int flags = 0; + + sid = kmalloc(sizeof(struct smb_sid), GFP_KERNEL); + if (!sid) + break; + + if (pace->e_tag == ACL_USER) { + uid_t uid; + unsigned int sid_type = SIDOWNER; + + uid = posix_acl_uid_translate(user_ns, pace); + if (!uid) + sid_type = SIDUNIX_USER; + id_to_sid(uid, sid_type, sid); + } else if (pace->e_tag == ACL_GROUP) { + gid_t gid; + + gid = posix_acl_gid_translate(user_ns, pace); + id_to_sid(gid, SIDUNIX_GROUP, sid); + } else if (pace->e_tag == ACL_OTHER && !nt_aces_num) { + smb_copy_sid(sid, &sid_everyone); + } else { + kfree(sid); + continue; + } + ntace = pndace; + for (j = 0; j < nt_aces_num; j++) { + if (ntace->sid.sub_auth[ntace->sid.num_subauth - 1] == + sid->sub_auth[sid->num_subauth - 1]) + goto pass_same_sid; + ntace = (struct smb_ace *)((char *)ntace + + le16_to_cpu(ntace->size)); + } + + if (S_ISDIR(fattr->cf_mode) && pace->e_tag == ACL_OTHER) + flags = 0x03; + + ntace = (struct smb_ace *)((char *)pndace + *size); + *size += fill_ace_for_sid(ntace, sid, ACCESS_ALLOWED, flags, + pace->e_perm, 0777); + (*num_aces)++; + if (pace->e_tag == ACL_USER) + ntace->access_req |= + FILE_DELETE_LE | FILE_DELETE_CHILD_LE; + + if (S_ISDIR(fattr->cf_mode) && + (pace->e_tag == ACL_USER || pace->e_tag == ACL_GROUP)) { + ntace = (struct smb_ace *)((char *)pndace + *size); + *size += fill_ace_for_sid(ntace, sid, ACCESS_ALLOWED, + 0x03, pace->e_perm, 0777); + (*num_aces)++; + if (pace->e_tag == ACL_USER) + ntace->access_req |= + FILE_DELETE_LE | FILE_DELETE_CHILD_LE; + } + +pass_same_sid: + kfree(sid); + } + + if (nt_aces_num) + return; + +posix_default_acl: + if (!fattr->cf_dacls) + return; + + pace = fattr->cf_dacls->a_entries; + for (i = 0; i < fattr->cf_dacls->a_count; i++, pace++) { + sid = kmalloc(sizeof(struct smb_sid), GFP_KERNEL); + if (!sid) + break; + + if (pace->e_tag == ACL_USER) { + uid_t uid; + + uid = posix_acl_uid_translate(user_ns, pace); + id_to_sid(uid, SIDCREATOR_OWNER, sid); + } else if (pace->e_tag == ACL_GROUP) { + gid_t gid; + + gid = posix_acl_gid_translate(user_ns, pace); + id_to_sid(gid, SIDCREATOR_GROUP, sid); + } else { + kfree(sid); + continue; + } + + ntace = (struct smb_ace *)((char *)pndace + *size); + *size += fill_ace_for_sid(ntace, sid, ACCESS_ALLOWED, 0x0b, + pace->e_perm, 0777); + (*num_aces)++; + if (pace->e_tag == ACL_USER) + ntace->access_req |= + FILE_DELETE_LE | FILE_DELETE_CHILD_LE; + kfree(sid); + } +} + +static void set_ntacl_dacl(struct user_namespace *user_ns, + struct smb_acl *pndacl, + struct smb_acl *nt_dacl, + unsigned int aces_size, + const struct smb_sid *pownersid, + const struct smb_sid *pgrpsid, + struct smb_fattr *fattr) +{ + struct smb_ace *ntace, *pndace; + int nt_num_aces = le32_to_cpu(nt_dacl->num_aces), num_aces = 0; + unsigned short size = 0; + int i; + + pndace = (struct smb_ace *)((char *)pndacl + sizeof(struct smb_acl)); + if (nt_num_aces) { + ntace = (struct smb_ace *)((char *)nt_dacl + sizeof(struct smb_acl)); + for (i = 0; i < nt_num_aces; i++) { + unsigned short nt_ace_size; + + if (offsetof(struct smb_ace, access_req) > aces_size) + break; + + nt_ace_size = le16_to_cpu(ntace->size); + if (nt_ace_size > aces_size) + break; + + memcpy((char *)pndace + size, ntace, nt_ace_size); + size += nt_ace_size; + aces_size -= nt_ace_size; + ntace = (struct smb_ace *)((char *)ntace + nt_ace_size); + num_aces++; + } + } + + set_posix_acl_entries_dacl(user_ns, pndace, fattr, + &num_aces, &size, nt_num_aces); + pndacl->num_aces = cpu_to_le32(num_aces); + pndacl->size = cpu_to_le16(le16_to_cpu(pndacl->size) + size); +} + +static void set_mode_dacl(struct user_namespace *user_ns, + struct smb_acl *pndacl, struct smb_fattr *fattr) +{ + struct smb_ace *pace, *pndace; + u32 num_aces = 0; + u16 size = 0, ace_size = 0; + uid_t uid; + const struct smb_sid *sid; + + pace = pndace = (struct smb_ace *)((char *)pndacl + sizeof(struct smb_acl)); + + if (fattr->cf_acls) { + set_posix_acl_entries_dacl(user_ns, pndace, fattr, + &num_aces, &size, num_aces); + goto out; + } + + /* owner RID */ + uid = from_kuid(&init_user_ns, fattr->cf_uid); + if (uid) + sid = &server_conf.domain_sid; + else + sid = &sid_unix_users; + ace_size = fill_ace_for_sid(pace, sid, ACCESS_ALLOWED, 0, + fattr->cf_mode, 0700); + pace->sid.sub_auth[pace->sid.num_subauth++] = cpu_to_le32(uid); + pace->size = cpu_to_le16(ace_size + 4); + size += le16_to_cpu(pace->size); + pace = (struct smb_ace *)((char *)pndace + size); + + /* Group RID */ + ace_size = fill_ace_for_sid(pace, &sid_unix_groups, + ACCESS_ALLOWED, 0, fattr->cf_mode, 0070); + pace->sid.sub_auth[pace->sid.num_subauth++] = + cpu_to_le32(from_kgid(&init_user_ns, fattr->cf_gid)); + pace->size = cpu_to_le16(ace_size + 4); + size += le16_to_cpu(pace->size); + pace = (struct smb_ace *)((char *)pndace + size); + num_aces = 3; + + if (S_ISDIR(fattr->cf_mode)) { + pace = (struct smb_ace *)((char *)pndace + size); + + /* creator owner */ + size += fill_ace_for_sid(pace, &creator_owner, ACCESS_ALLOWED, + 0x0b, fattr->cf_mode, 0700); + pace = (struct smb_ace *)((char *)pndace + size); + + /* creator group */ + size += fill_ace_for_sid(pace, &creator_group, ACCESS_ALLOWED, + 0x0b, fattr->cf_mode, 0070); + pace = (struct smb_ace *)((char *)pndace + size); + num_aces = 5; + } + + /* other */ + size += fill_ace_for_sid(pace, &sid_everyone, ACCESS_ALLOWED, 0, + fattr->cf_mode, 0007); + +out: + pndacl->num_aces = cpu_to_le32(num_aces); + pndacl->size = cpu_to_le16(le16_to_cpu(pndacl->size) + size); +} + +static int parse_sid(struct smb_sid *psid, char *end_of_acl) +{ + /* + * validate that we do not go past end of ACL - sid must be at least 8 + * bytes long (assuming no sub-auths - e.g. the null SID + */ + if (end_of_acl < (char *)psid + 8) { + pr_err("ACL too small to parse SID %p\n", psid); + return -EINVAL; + } + + return 0; +} + +/* Convert CIFS ACL to POSIX form */ +int parse_sec_desc(struct user_namespace *user_ns, struct smb_ntsd *pntsd, + int acl_len, struct smb_fattr *fattr) +{ + int rc = 0; + struct smb_sid *owner_sid_ptr, *group_sid_ptr; + struct smb_acl *dacl_ptr; /* no need for SACL ptr */ + char *end_of_acl = ((char *)pntsd) + acl_len; + __u32 dacloffset; + int pntsd_type; + + if (!pntsd) + return -EIO; + + if (acl_len < sizeof(struct smb_ntsd)) + return -EINVAL; + + owner_sid_ptr = (struct smb_sid *)((char *)pntsd + + le32_to_cpu(pntsd->osidoffset)); + group_sid_ptr = (struct smb_sid *)((char *)pntsd + + le32_to_cpu(pntsd->gsidoffset)); + dacloffset = le32_to_cpu(pntsd->dacloffset); + dacl_ptr = (struct smb_acl *)((char *)pntsd + dacloffset); + ksmbd_debug(SMB, + "revision %d type 0x%x ooffset 0x%x goffset 0x%x sacloffset 0x%x dacloffset 0x%x\n", + pntsd->revision, pntsd->type, le32_to_cpu(pntsd->osidoffset), + le32_to_cpu(pntsd->gsidoffset), + le32_to_cpu(pntsd->sacloffset), dacloffset); + + pntsd_type = le16_to_cpu(pntsd->type); + if (!(pntsd_type & DACL_PRESENT)) { + ksmbd_debug(SMB, "DACL_PRESENT in DACL type is not set\n"); + return rc; + } + + pntsd->type = cpu_to_le16(DACL_PRESENT); + + if (pntsd->osidoffset) { + rc = parse_sid(owner_sid_ptr, end_of_acl); + if (rc) { + pr_err("%s: Error %d parsing Owner SID\n", __func__, rc); + return rc; + } + + rc = sid_to_id(user_ns, owner_sid_ptr, SIDOWNER, fattr); + if (rc) { + pr_err("%s: Error %d mapping Owner SID to uid\n", + __func__, rc); + owner_sid_ptr = NULL; + } + } + + if (pntsd->gsidoffset) { + rc = parse_sid(group_sid_ptr, end_of_acl); + if (rc) { + pr_err("%s: Error %d mapping Owner SID to gid\n", + __func__, rc); + return rc; + } + rc = sid_to_id(user_ns, group_sid_ptr, SIDUNIX_GROUP, fattr); + if (rc) { + pr_err("%s: Error %d mapping Group SID to gid\n", + __func__, rc); + group_sid_ptr = NULL; + } + } + + if ((pntsd_type & (DACL_AUTO_INHERITED | DACL_AUTO_INHERIT_REQ)) == + (DACL_AUTO_INHERITED | DACL_AUTO_INHERIT_REQ)) + pntsd->type |= cpu_to_le16(DACL_AUTO_INHERITED); + if (pntsd_type & DACL_PROTECTED) + pntsd->type |= cpu_to_le16(DACL_PROTECTED); + + if (dacloffset) { + parse_dacl(user_ns, dacl_ptr, end_of_acl, + owner_sid_ptr, group_sid_ptr, fattr); + } + + return 0; +} + +/* Convert permission bits from mode to equivalent CIFS ACL */ +int build_sec_desc(struct user_namespace *user_ns, + struct smb_ntsd *pntsd, struct smb_ntsd *ppntsd, + int ppntsd_size, int addition_info, __u32 *secdesclen, + struct smb_fattr *fattr) +{ + int rc = 0; + __u32 offset; + struct smb_sid *owner_sid_ptr, *group_sid_ptr; + struct smb_sid *nowner_sid_ptr, *ngroup_sid_ptr; + struct smb_acl *dacl_ptr = NULL; /* no need for SACL ptr */ + uid_t uid; + gid_t gid; + unsigned int sid_type = SIDOWNER; + + nowner_sid_ptr = kmalloc(sizeof(struct smb_sid), GFP_KERNEL); + if (!nowner_sid_ptr) + return -ENOMEM; + + uid = from_kuid(&init_user_ns, fattr->cf_uid); + if (!uid) + sid_type = SIDUNIX_USER; + id_to_sid(uid, sid_type, nowner_sid_ptr); + + ngroup_sid_ptr = kmalloc(sizeof(struct smb_sid), GFP_KERNEL); + if (!ngroup_sid_ptr) { + kfree(nowner_sid_ptr); + return -ENOMEM; + } + + gid = from_kgid(&init_user_ns, fattr->cf_gid); + id_to_sid(gid, SIDUNIX_GROUP, ngroup_sid_ptr); + + offset = sizeof(struct smb_ntsd); + pntsd->sacloffset = 0; + pntsd->revision = cpu_to_le16(1); + pntsd->type = cpu_to_le16(SELF_RELATIVE); + if (ppntsd) + pntsd->type |= ppntsd->type; + + if (addition_info & OWNER_SECINFO) { + pntsd->osidoffset = cpu_to_le32(offset); + owner_sid_ptr = (struct smb_sid *)((char *)pntsd + offset); + smb_copy_sid(owner_sid_ptr, nowner_sid_ptr); + offset += 1 + 1 + 6 + (nowner_sid_ptr->num_subauth * 4); + } + + if (addition_info & GROUP_SECINFO) { + pntsd->gsidoffset = cpu_to_le32(offset); + group_sid_ptr = (struct smb_sid *)((char *)pntsd + offset); + smb_copy_sid(group_sid_ptr, ngroup_sid_ptr); + offset += 1 + 1 + 6 + (ngroup_sid_ptr->num_subauth * 4); + } + + if (addition_info & DACL_SECINFO) { + pntsd->type |= cpu_to_le16(DACL_PRESENT); + dacl_ptr = (struct smb_acl *)((char *)pntsd + offset); + dacl_ptr->revision = cpu_to_le16(2); + dacl_ptr->size = cpu_to_le16(sizeof(struct smb_acl)); + dacl_ptr->num_aces = 0; + + if (!ppntsd) { + set_mode_dacl(user_ns, dacl_ptr, fattr); + } else { + struct smb_acl *ppdacl_ptr; + unsigned int dacl_offset = le32_to_cpu(ppntsd->dacloffset); + int ppdacl_size, ntacl_size = ppntsd_size - dacl_offset; + + if (!dacl_offset || + (dacl_offset + sizeof(struct smb_acl) > ppntsd_size)) + goto out; + + ppdacl_ptr = (struct smb_acl *)((char *)ppntsd + dacl_offset); + ppdacl_size = le16_to_cpu(ppdacl_ptr->size); + if (ppdacl_size > ntacl_size || + ppdacl_size < sizeof(struct smb_acl)) + goto out; + + set_ntacl_dacl(user_ns, dacl_ptr, ppdacl_ptr, + ntacl_size - sizeof(struct smb_acl), + nowner_sid_ptr, ngroup_sid_ptr, + fattr); + } + pntsd->dacloffset = cpu_to_le32(offset); + offset += le16_to_cpu(dacl_ptr->size); + } + +out: + kfree(nowner_sid_ptr); + kfree(ngroup_sid_ptr); + *secdesclen = offset; + return rc; +} + +static void smb_set_ace(struct smb_ace *ace, const struct smb_sid *sid, u8 type, + u8 flags, __le32 access_req) +{ + ace->type = type; + ace->flags = flags; + ace->access_req = access_req; + smb_copy_sid(&ace->sid, sid); + ace->size = cpu_to_le16(1 + 1 + 2 + 4 + 1 + 1 + 6 + (sid->num_subauth * 4)); +} + +int smb_inherit_dacl(struct ksmbd_conn *conn, + const struct path *path, + unsigned int uid, unsigned int gid) +{ + const struct smb_sid *psid, *creator = NULL; + struct smb_ace *parent_aces, *aces; + struct smb_acl *parent_pdacl; + struct smb_ntsd *parent_pntsd = NULL; + struct smb_sid owner_sid, group_sid; + struct dentry *parent = path->dentry->d_parent; + struct user_namespace *user_ns = mnt_user_ns(path->mnt); + int inherited_flags = 0, flags = 0, i, ace_cnt = 0, nt_size = 0, pdacl_size; + int rc = 0, num_aces, dacloffset, pntsd_type, pntsd_size, acl_len, aces_size; + char *aces_base; + bool is_dir = S_ISDIR(d_inode(path->dentry)->i_mode); + + pntsd_size = ksmbd_vfs_get_sd_xattr(conn, user_ns, + parent, &parent_pntsd); + if (pntsd_size <= 0) + return -ENOENT; + dacloffset = le32_to_cpu(parent_pntsd->dacloffset); + if (!dacloffset || (dacloffset + sizeof(struct smb_acl) > pntsd_size)) { + rc = -EINVAL; + goto free_parent_pntsd; + } + + parent_pdacl = (struct smb_acl *)((char *)parent_pntsd + dacloffset); + acl_len = pntsd_size - dacloffset; + num_aces = le32_to_cpu(parent_pdacl->num_aces); + pntsd_type = le16_to_cpu(parent_pntsd->type); + pdacl_size = le16_to_cpu(parent_pdacl->size); + + if (pdacl_size > acl_len || pdacl_size < sizeof(struct smb_acl)) { + rc = -EINVAL; + goto free_parent_pntsd; + } + + aces_base = kmalloc(sizeof(struct smb_ace) * num_aces * 2, GFP_KERNEL); + if (!aces_base) { + rc = -ENOMEM; + goto free_parent_pntsd; + } + + aces = (struct smb_ace *)aces_base; + parent_aces = (struct smb_ace *)((char *)parent_pdacl + + sizeof(struct smb_acl)); + aces_size = acl_len - sizeof(struct smb_acl); + + if (pntsd_type & DACL_AUTO_INHERITED) + inherited_flags = INHERITED_ACE; + + for (i = 0; i < num_aces; i++) { + int pace_size; + + if (offsetof(struct smb_ace, access_req) > aces_size) + break; + + pace_size = le16_to_cpu(parent_aces->size); + if (pace_size > aces_size) + break; + + aces_size -= pace_size; + + flags = parent_aces->flags; + if (!smb_inherit_flags(flags, is_dir)) + goto pass; + if (is_dir) { + flags &= ~(INHERIT_ONLY_ACE | INHERITED_ACE); + if (!(flags & CONTAINER_INHERIT_ACE)) + flags |= INHERIT_ONLY_ACE; + if (flags & NO_PROPAGATE_INHERIT_ACE) + flags = 0; + } else { + flags = 0; + } + + if (!compare_sids(&creator_owner, &parent_aces->sid)) { + creator = &creator_owner; + id_to_sid(uid, SIDOWNER, &owner_sid); + psid = &owner_sid; + } else if (!compare_sids(&creator_group, &parent_aces->sid)) { + creator = &creator_group; + id_to_sid(gid, SIDUNIX_GROUP, &group_sid); + psid = &group_sid; + } else { + creator = NULL; + psid = &parent_aces->sid; + } + + if (is_dir && creator && flags & CONTAINER_INHERIT_ACE) { + smb_set_ace(aces, psid, parent_aces->type, inherited_flags, + parent_aces->access_req); + nt_size += le16_to_cpu(aces->size); + ace_cnt++; + aces = (struct smb_ace *)((char *)aces + le16_to_cpu(aces->size)); + flags |= INHERIT_ONLY_ACE; + psid = creator; + } else if (is_dir && !(parent_aces->flags & NO_PROPAGATE_INHERIT_ACE)) { + psid = &parent_aces->sid; + } + + smb_set_ace(aces, psid, parent_aces->type, flags | inherited_flags, + parent_aces->access_req); + nt_size += le16_to_cpu(aces->size); + aces = (struct smb_ace *)((char *)aces + le16_to_cpu(aces->size)); + ace_cnt++; +pass: + parent_aces = (struct smb_ace *)((char *)parent_aces + pace_size); + } + + if (nt_size > 0) { + struct smb_ntsd *pntsd; + struct smb_acl *pdacl; + struct smb_sid *powner_sid = NULL, *pgroup_sid = NULL; + int powner_sid_size = 0, pgroup_sid_size = 0, pntsd_size; + + if (parent_pntsd->osidoffset) { + powner_sid = (struct smb_sid *)((char *)parent_pntsd + + le32_to_cpu(parent_pntsd->osidoffset)); + powner_sid_size = 1 + 1 + 6 + (powner_sid->num_subauth * 4); + } + if (parent_pntsd->gsidoffset) { + pgroup_sid = (struct smb_sid *)((char *)parent_pntsd + + le32_to_cpu(parent_pntsd->gsidoffset)); + pgroup_sid_size = 1 + 1 + 6 + (pgroup_sid->num_subauth * 4); + } + + pntsd = kzalloc(sizeof(struct smb_ntsd) + powner_sid_size + + pgroup_sid_size + sizeof(struct smb_acl) + + nt_size, GFP_KERNEL); + if (!pntsd) { + rc = -ENOMEM; + goto free_aces_base; + } + + pntsd->revision = cpu_to_le16(1); + pntsd->type = cpu_to_le16(SELF_RELATIVE | DACL_PRESENT); + if (le16_to_cpu(parent_pntsd->type) & DACL_AUTO_INHERITED) + pntsd->type |= cpu_to_le16(DACL_AUTO_INHERITED); + pntsd_size = sizeof(struct smb_ntsd); + pntsd->osidoffset = parent_pntsd->osidoffset; + pntsd->gsidoffset = parent_pntsd->gsidoffset; + pntsd->dacloffset = parent_pntsd->dacloffset; + + if (pntsd->osidoffset) { + struct smb_sid *owner_sid = (struct smb_sid *)((char *)pntsd + + le32_to_cpu(pntsd->osidoffset)); + memcpy(owner_sid, powner_sid, powner_sid_size); + pntsd_size += powner_sid_size; + } + + if (pntsd->gsidoffset) { + struct smb_sid *group_sid = (struct smb_sid *)((char *)pntsd + + le32_to_cpu(pntsd->gsidoffset)); + memcpy(group_sid, pgroup_sid, pgroup_sid_size); + pntsd_size += pgroup_sid_size; + } + + if (pntsd->dacloffset) { + struct smb_ace *pace; + + pdacl = (struct smb_acl *)((char *)pntsd + le32_to_cpu(pntsd->dacloffset)); + pdacl->revision = cpu_to_le16(2); + pdacl->size = cpu_to_le16(sizeof(struct smb_acl) + nt_size); + pdacl->num_aces = cpu_to_le32(ace_cnt); + pace = (struct smb_ace *)((char *)pdacl + sizeof(struct smb_acl)); + memcpy(pace, aces_base, nt_size); + pntsd_size += sizeof(struct smb_acl) + nt_size; + } + + ksmbd_vfs_set_sd_xattr(conn, user_ns, + path->dentry, pntsd, pntsd_size); + kfree(pntsd); + } + +free_aces_base: + kfree(aces_base); +free_parent_pntsd: + kfree(parent_pntsd); + return rc; +} + +bool smb_inherit_flags(int flags, bool is_dir) +{ + if (!is_dir) + return (flags & OBJECT_INHERIT_ACE) != 0; + + if (flags & OBJECT_INHERIT_ACE && !(flags & NO_PROPAGATE_INHERIT_ACE)) + return true; + + if (flags & CONTAINER_INHERIT_ACE) + return true; + return false; +} + +int smb_check_perm_dacl(struct ksmbd_conn *conn, const struct path *path, + __le32 *pdaccess, int uid) +{ + struct user_namespace *user_ns = mnt_user_ns(path->mnt); + struct smb_ntsd *pntsd = NULL; + struct smb_acl *pdacl; + struct posix_acl *posix_acls; + int rc = 0, pntsd_size, acl_size, aces_size, pdacl_size, dacl_offset; + struct smb_sid sid; + int granted = le32_to_cpu(*pdaccess & ~FILE_MAXIMAL_ACCESS_LE); + struct smb_ace *ace; + int i, found = 0; + unsigned int access_bits = 0; + struct smb_ace *others_ace = NULL; + struct posix_acl_entry *pa_entry; + unsigned int sid_type = SIDOWNER; + unsigned short ace_size; + + ksmbd_debug(SMB, "check permission using windows acl\n"); + pntsd_size = ksmbd_vfs_get_sd_xattr(conn, user_ns, + path->dentry, &pntsd); + if (pntsd_size <= 0 || !pntsd) + goto err_out; + + dacl_offset = le32_to_cpu(pntsd->dacloffset); + if (!dacl_offset || + (dacl_offset + sizeof(struct smb_acl) > pntsd_size)) + goto err_out; + + pdacl = (struct smb_acl *)((char *)pntsd + le32_to_cpu(pntsd->dacloffset)); + acl_size = pntsd_size - dacl_offset; + pdacl_size = le16_to_cpu(pdacl->size); + + if (pdacl_size > acl_size || pdacl_size < sizeof(struct smb_acl)) + goto err_out; + + if (!pdacl->num_aces) { + if (!(pdacl_size - sizeof(struct smb_acl)) && + *pdaccess & ~(FILE_READ_CONTROL_LE | FILE_WRITE_DAC_LE)) { + rc = -EACCES; + goto err_out; + } + goto err_out; + } + + if (*pdaccess & FILE_MAXIMAL_ACCESS_LE) { + granted = READ_CONTROL | WRITE_DAC | FILE_READ_ATTRIBUTES | + DELETE; + + ace = (struct smb_ace *)((char *)pdacl + sizeof(struct smb_acl)); + aces_size = acl_size - sizeof(struct smb_acl); + for (i = 0; i < le32_to_cpu(pdacl->num_aces); i++) { + if (offsetof(struct smb_ace, access_req) > aces_size) + break; + ace_size = le16_to_cpu(ace->size); + if (ace_size > aces_size) + break; + aces_size -= ace_size; + granted |= le32_to_cpu(ace->access_req); + ace = (struct smb_ace *)((char *)ace + le16_to_cpu(ace->size)); + } + + if (!pdacl->num_aces) + granted = GENERIC_ALL_FLAGS; + } + + if (!uid) + sid_type = SIDUNIX_USER; + id_to_sid(uid, sid_type, &sid); + + ace = (struct smb_ace *)((char *)pdacl + sizeof(struct smb_acl)); + aces_size = acl_size - sizeof(struct smb_acl); + for (i = 0; i < le32_to_cpu(pdacl->num_aces); i++) { + if (offsetof(struct smb_ace, access_req) > aces_size) + break; + ace_size = le16_to_cpu(ace->size); + if (ace_size > aces_size) + break; + aces_size -= ace_size; + + if (!compare_sids(&sid, &ace->sid) || + !compare_sids(&sid_unix_NFS_mode, &ace->sid)) { + found = 1; + break; + } + if (!compare_sids(&sid_everyone, &ace->sid)) + others_ace = ace; + + ace = (struct smb_ace *)((char *)ace + le16_to_cpu(ace->size)); + } + + if (*pdaccess & FILE_MAXIMAL_ACCESS_LE && found) { + granted = READ_CONTROL | WRITE_DAC | FILE_READ_ATTRIBUTES | + DELETE; + + granted |= le32_to_cpu(ace->access_req); + + if (!pdacl->num_aces) + granted = GENERIC_ALL_FLAGS; + } + + if (IS_ENABLED(CONFIG_FS_POSIX_ACL)) { + posix_acls = get_acl(d_inode(path->dentry), ACL_TYPE_ACCESS); + if (posix_acls && !found) { + unsigned int id = -1; + + pa_entry = posix_acls->a_entries; + for (i = 0; i < posix_acls->a_count; i++, pa_entry++) { + if (pa_entry->e_tag == ACL_USER) + id = posix_acl_uid_translate(user_ns, pa_entry); + else if (pa_entry->e_tag == ACL_GROUP) + id = posix_acl_gid_translate(user_ns, pa_entry); + else + continue; + + if (id == uid) { + mode_to_access_flags(pa_entry->e_perm, + 0777, + &access_bits); + if (!access_bits) + access_bits = + SET_MINIMUM_RIGHTS; + posix_acl_release(posix_acls); + goto check_access_bits; + } + } + } + if (posix_acls) + posix_acl_release(posix_acls); + } + + if (!found) { + if (others_ace) { + ace = others_ace; + } else { + ksmbd_debug(SMB, "Can't find corresponding sid\n"); + rc = -EACCES; + goto err_out; + } + } + + switch (ace->type) { + case ACCESS_ALLOWED_ACE_TYPE: + access_bits = le32_to_cpu(ace->access_req); + break; + case ACCESS_DENIED_ACE_TYPE: + case ACCESS_DENIED_CALLBACK_ACE_TYPE: + access_bits = le32_to_cpu(~ace->access_req); + break; + } + +check_access_bits: + if (granted & + ~(access_bits | FILE_READ_ATTRIBUTES | READ_CONTROL | WRITE_DAC | DELETE)) { + ksmbd_debug(SMB, "Access denied with winACL, granted : %x, access_req : %x\n", + granted, le32_to_cpu(ace->access_req)); + rc = -EACCES; + goto err_out; + } + + *pdaccess = cpu_to_le32(granted); +err_out: + kfree(pntsd); + return rc; +} + +int set_info_sec(struct ksmbd_conn *conn, struct ksmbd_tree_connect *tcon, + const struct path *path, struct smb_ntsd *pntsd, int ntsd_len, + bool type_check) +{ + int rc; + struct smb_fattr fattr = {{0}}; + struct inode *inode = d_inode(path->dentry); + struct user_namespace *user_ns = mnt_user_ns(path->mnt); + struct iattr newattrs; + + fattr.cf_uid = INVALID_UID; + fattr.cf_gid = INVALID_GID; + fattr.cf_mode = inode->i_mode; + + rc = parse_sec_desc(user_ns, pntsd, ntsd_len, &fattr); + if (rc) + goto out; + + newattrs.ia_valid = ATTR_CTIME; + if (!uid_eq(fattr.cf_uid, INVALID_UID)) { + newattrs.ia_valid |= ATTR_UID; + newattrs.ia_uid = fattr.cf_uid; + } + if (!gid_eq(fattr.cf_gid, INVALID_GID)) { + newattrs.ia_valid |= ATTR_GID; + newattrs.ia_gid = fattr.cf_gid; + } + newattrs.ia_valid |= ATTR_MODE; + newattrs.ia_mode = (inode->i_mode & ~0777) | (fattr.cf_mode & 0777); + + ksmbd_vfs_remove_acl_xattrs(user_ns, path->dentry); + /* Update posix acls */ + if (IS_ENABLED(CONFIG_FS_POSIX_ACL) && fattr.cf_dacls) { + rc = set_posix_acl(user_ns, inode, + ACL_TYPE_ACCESS, fattr.cf_acls); + if (rc < 0) + ksmbd_debug(SMB, + "Set posix acl(ACL_TYPE_ACCESS) failed, rc : %d\n", + rc); + if (S_ISDIR(inode->i_mode) && fattr.cf_dacls) { + rc = set_posix_acl(user_ns, inode, + ACL_TYPE_DEFAULT, fattr.cf_dacls); + if (rc) + ksmbd_debug(SMB, + "Set posix acl(ACL_TYPE_DEFAULT) failed, rc : %d\n", + rc); + } + } + + inode_lock(inode); + rc = notify_change(user_ns, path->dentry, &newattrs, NULL); + inode_unlock(inode); + if (rc) + goto out; + + /* Check it only calling from SD BUFFER context */ + if (type_check && !(le16_to_cpu(pntsd->type) & DACL_PRESENT)) + goto out; + + if (test_share_config_flag(tcon->share_conf, KSMBD_SHARE_FLAG_ACL_XATTR)) { + /* Update WinACL in xattr */ + ksmbd_vfs_remove_sd_xattrs(user_ns, path->dentry); + ksmbd_vfs_set_sd_xattr(conn, user_ns, + path->dentry, pntsd, ntsd_len); + } + +out: + posix_acl_release(fattr.cf_acls); + posix_acl_release(fattr.cf_dacls); + mark_inode_dirty(inode); + return rc; +} + +void ksmbd_init_domain(u32 *sub_auth) +{ + int i; + + memcpy(&server_conf.domain_sid, &domain, sizeof(struct smb_sid)); + for (i = 0; i < 3; ++i) + server_conf.domain_sid.sub_auth[i + 1] = cpu_to_le32(sub_auth[i]); +} diff --git a/fs/smb/server/smbacl.h b/fs/smb/server/smbacl.h new file mode 100644 index 0000000000000..618f2e0236b31 --- /dev/null +++ b/fs/smb/server/smbacl.h @@ -0,0 +1,238 @@ +/* SPDX-License-Identifier: LGPL-2.1+ */ +/* + * Copyright (c) International Business Machines Corp., 2007 + * Author(s): Steve French (sfrench@us.ibm.com) + * Modified by Namjae Jeon (linkinjeon@kernel.org) + */ + +#ifndef _SMBACL_H +#define _SMBACL_H + +#include +#include +#include +#include + +#include "mgmt/tree_connect.h" + +#define NUM_AUTHS (6) /* number of authority fields */ +#define SID_MAX_SUB_AUTHORITIES (15) /* max number of sub authority fields */ + +/* + * ACE types - see MS-DTYP 2.4.4.1 + */ +enum { + ACCESS_ALLOWED, + ACCESS_DENIED, +}; + +/* + * Security ID types + */ +enum { + SIDOWNER = 1, + SIDGROUP, + SIDCREATOR_OWNER, + SIDCREATOR_GROUP, + SIDUNIX_USER, + SIDUNIX_GROUP, + SIDNFS_USER, + SIDNFS_GROUP, + SIDNFS_MODE, +}; + +/* Revision for ACLs */ +#define SD_REVISION 1 + +/* Control flags for Security Descriptor */ +#define OWNER_DEFAULTED 0x0001 +#define GROUP_DEFAULTED 0x0002 +#define DACL_PRESENT 0x0004 +#define DACL_DEFAULTED 0x0008 +#define SACL_PRESENT 0x0010 +#define SACL_DEFAULTED 0x0020 +#define DACL_TRUSTED 0x0040 +#define SERVER_SECURITY 0x0080 +#define DACL_AUTO_INHERIT_REQ 0x0100 +#define SACL_AUTO_INHERIT_REQ 0x0200 +#define DACL_AUTO_INHERITED 0x0400 +#define SACL_AUTO_INHERITED 0x0800 +#define DACL_PROTECTED 0x1000 +#define SACL_PROTECTED 0x2000 +#define RM_CONTROL_VALID 0x4000 +#define SELF_RELATIVE 0x8000 + +/* ACE types - see MS-DTYP 2.4.4.1 */ +#define ACCESS_ALLOWED_ACE_TYPE 0x00 +#define ACCESS_DENIED_ACE_TYPE 0x01 +#define SYSTEM_AUDIT_ACE_TYPE 0x02 +#define SYSTEM_ALARM_ACE_TYPE 0x03 +#define ACCESS_ALLOWED_COMPOUND_ACE_TYPE 0x04 +#define ACCESS_ALLOWED_OBJECT_ACE_TYPE 0x05 +#define ACCESS_DENIED_OBJECT_ACE_TYPE 0x06 +#define SYSTEM_AUDIT_OBJECT_ACE_TYPE 0x07 +#define SYSTEM_ALARM_OBJECT_ACE_TYPE 0x08 +#define ACCESS_ALLOWED_CALLBACK_ACE_TYPE 0x09 +#define ACCESS_DENIED_CALLBACK_ACE_TYPE 0x0A +#define ACCESS_ALLOWED_CALLBACK_OBJECT_ACE_TYPE 0x0B +#define ACCESS_DENIED_CALLBACK_OBJECT_ACE_TYPE 0x0C +#define SYSTEM_AUDIT_CALLBACK_ACE_TYPE 0x0D +#define SYSTEM_ALARM_CALLBACK_ACE_TYPE 0x0E /* Reserved */ +#define SYSTEM_AUDIT_CALLBACK_OBJECT_ACE_TYPE 0x0F +#define SYSTEM_ALARM_CALLBACK_OBJECT_ACE_TYPE 0x10 /* reserved */ +#define SYSTEM_MANDATORY_LABEL_ACE_TYPE 0x11 +#define SYSTEM_RESOURCE_ATTRIBUTE_ACE_TYPE 0x12 +#define SYSTEM_SCOPED_POLICY_ID_ACE_TYPE 0x13 + +/* ACE flags */ +#define OBJECT_INHERIT_ACE 0x01 +#define CONTAINER_INHERIT_ACE 0x02 +#define NO_PROPAGATE_INHERIT_ACE 0x04 +#define INHERIT_ONLY_ACE 0x08 +#define INHERITED_ACE 0x10 +#define SUCCESSFUL_ACCESS_ACE_FLAG 0x40 +#define FAILED_ACCESS_ACE_FLAG 0x80 + +/* + * Maximum size of a string representation of a SID: + * + * The fields are unsigned values in decimal. So: + * + * u8: max 3 bytes in decimal + * u32: max 10 bytes in decimal + * + * "S-" + 3 bytes for version field + 15 for authority field + NULL terminator + * + * For authority field, max is when all 6 values are non-zero and it must be + * represented in hex. So "-0x" + 12 hex digits. + * + * Add 11 bytes for each subauthority field (10 bytes each + 1 for '-') + */ +#define SID_STRING_BASE_SIZE (2 + 3 + 15 + 1) +#define SID_STRING_SUBAUTH_SIZE (11) /* size of a single subauth string */ + +#define DOMAIN_USER_RID_LE cpu_to_le32(513) + +struct ksmbd_conn; + +struct smb_ntsd { + __le16 revision; /* revision level */ + __le16 type; + __le32 osidoffset; + __le32 gsidoffset; + __le32 sacloffset; + __le32 dacloffset; +} __packed; + +struct smb_sid { + __u8 revision; /* revision level */ + __u8 num_subauth; + __u8 authority[NUM_AUTHS]; + __le32 sub_auth[SID_MAX_SUB_AUTHORITIES]; /* sub_auth[num_subauth] */ +} __packed; + +/* size of a struct cifs_sid, sans sub_auth array */ +#define CIFS_SID_BASE_SIZE (1 + 1 + NUM_AUTHS) + +struct smb_acl { + __le16 revision; /* revision level */ + __le16 size; + __le32 num_aces; +} __packed; + +struct smb_ace { + __u8 type; + __u8 flags; + __le16 size; + __le32 access_req; + struct smb_sid sid; /* ie UUID of user or group who gets these perms */ +} __packed; + +struct smb_fattr { + kuid_t cf_uid; + kgid_t cf_gid; + umode_t cf_mode; + __le32 daccess; + struct posix_acl *cf_acls; + struct posix_acl *cf_dacls; +}; + +struct posix_ace_state { + u32 allow; + u32 deny; +}; + +struct posix_user_ace_state { + union { + kuid_t uid; + kgid_t gid; + }; + struct posix_ace_state perms; +}; + +struct posix_ace_state_array { + int n; + struct posix_user_ace_state aces[]; +}; + +/* + * while processing the nfsv4 ace, this maintains the partial permissions + * calculated so far: + */ + +struct posix_acl_state { + struct posix_ace_state owner; + struct posix_ace_state group; + struct posix_ace_state other; + struct posix_ace_state everyone; + struct posix_ace_state mask; /* deny unused in this case */ + struct posix_ace_state_array *users; + struct posix_ace_state_array *groups; +}; + +int parse_sec_desc(struct user_namespace *user_ns, struct smb_ntsd *pntsd, + int acl_len, struct smb_fattr *fattr); +int build_sec_desc(struct user_namespace *user_ns, struct smb_ntsd *pntsd, + struct smb_ntsd *ppntsd, int ppntsd_size, int addition_info, + __u32 *secdesclen, struct smb_fattr *fattr); +int init_acl_state(struct posix_acl_state *state, int cnt); +void free_acl_state(struct posix_acl_state *state); +void posix_state_to_acl(struct posix_acl_state *state, + struct posix_acl_entry *pace); +int compare_sids(const struct smb_sid *ctsid, const struct smb_sid *cwsid); +bool smb_inherit_flags(int flags, bool is_dir); +int smb_inherit_dacl(struct ksmbd_conn *conn, const struct path *path, + unsigned int uid, unsigned int gid); +int smb_check_perm_dacl(struct ksmbd_conn *conn, const struct path *path, + __le32 *pdaccess, int uid); +int set_info_sec(struct ksmbd_conn *conn, struct ksmbd_tree_connect *tcon, + const struct path *path, struct smb_ntsd *pntsd, int ntsd_len, + bool type_check); +void id_to_sid(unsigned int cid, uint sidtype, struct smb_sid *ssid); +void ksmbd_init_domain(u32 *sub_auth); + +static inline uid_t posix_acl_uid_translate(struct user_namespace *mnt_userns, + struct posix_acl_entry *pace) +{ + vfsuid_t vfsuid; + + /* If this is an idmapped mount, apply the idmapping. */ + vfsuid = make_vfsuid(mnt_userns, &init_user_ns, pace->e_uid); + + /* Translate the kuid into a userspace id ksmbd would see. */ + return from_kuid(&init_user_ns, vfsuid_into_kuid(vfsuid)); +} + +static inline gid_t posix_acl_gid_translate(struct user_namespace *mnt_userns, + struct posix_acl_entry *pace) +{ + vfsgid_t vfsgid; + + /* If this is an idmapped mount, apply the idmapping. */ + vfsgid = make_vfsgid(mnt_userns, &init_user_ns, pace->e_gid); + + /* Translate the kgid into a userspace id ksmbd would see. */ + return from_kgid(&init_user_ns, vfsgid_into_kgid(vfsgid)); +} + +#endif /* _SMBACL_H */ diff --git a/fs/smb/server/smbfsctl.h b/fs/smb/server/smbfsctl.h new file mode 100644 index 0000000000000..b98418aae20cd --- /dev/null +++ b/fs/smb/server/smbfsctl.h @@ -0,0 +1,91 @@ +/* SPDX-License-Identifier: LGPL-2.1+ */ +/* + * fs/cifs/smbfsctl.h: SMB, CIFS, SMB2 FSCTL definitions + * + * Copyright (c) International Business Machines Corp., 2002,2009 + * Author(s): Steve French (sfrench@us.ibm.com) + */ + +/* IOCTL information */ +/* + * List of ioctl/fsctl function codes that are or could be useful in the + * future to remote clients like cifs or SMB2 client. There is probably + * a slightly larger set of fsctls that NTFS local filesystem could handle, + * including the seven below that we do not have struct definitions for. + * Even with protocol definitions for most of these now available, we still + * need to do some experimentation to identify which are practical to do + * remotely. Some of the following, such as the encryption/compression ones + * could be invoked from tools via a specialized hook into the VFS rather + * than via the standard vfs entry points + */ + +#ifndef __KSMBD_SMBFSCTL_H +#define __KSMBD_SMBFSCTL_H + +#define FSCTL_DFS_GET_REFERRALS 0x00060194 +#define FSCTL_DFS_GET_REFERRALS_EX 0x000601B0 +#define FSCTL_REQUEST_OPLOCK_LEVEL_1 0x00090000 +#define FSCTL_REQUEST_OPLOCK_LEVEL_2 0x00090004 +#define FSCTL_REQUEST_BATCH_OPLOCK 0x00090008 +#define FSCTL_LOCK_VOLUME 0x00090018 +#define FSCTL_UNLOCK_VOLUME 0x0009001C +#define FSCTL_IS_PATHNAME_VALID 0x0009002C /* BB add struct */ +#define FSCTL_GET_COMPRESSION 0x0009003C /* BB add struct */ +#define FSCTL_SET_COMPRESSION 0x0009C040 /* BB add struct */ +#define FSCTL_QUERY_FAT_BPB 0x00090058 /* BB add struct */ +/* Verify the next FSCTL number, we had it as 0x00090090 before */ +#define FSCTL_FILESYSTEM_GET_STATS 0x00090060 /* BB add struct */ +#define FSCTL_GET_NTFS_VOLUME_DATA 0x00090064 /* BB add struct */ +#define FSCTL_GET_RETRIEVAL_POINTERS 0x00090073 /* BB add struct */ +#define FSCTL_IS_VOLUME_DIRTY 0x00090078 /* BB add struct */ +#define FSCTL_ALLOW_EXTENDED_DASD_IO 0x00090083 /* BB add struct */ +#define FSCTL_REQUEST_FILTER_OPLOCK 0x0009008C +#define FSCTL_FIND_FILES_BY_SID 0x0009008F /* BB add struct */ +#define FSCTL_SET_OBJECT_ID 0x00090098 /* BB add struct */ +#define FSCTL_GET_OBJECT_ID 0x0009009C /* BB add struct */ +#define FSCTL_DELETE_OBJECT_ID 0x000900A0 /* BB add struct */ +#define FSCTL_SET_REPARSE_POINT 0x000900A4 /* BB add struct */ +#define FSCTL_GET_REPARSE_POINT 0x000900A8 /* BB add struct */ +#define FSCTL_DELETE_REPARSE_POINT 0x000900AC /* BB add struct */ +#define FSCTL_SET_OBJECT_ID_EXTENDED 0x000900BC /* BB add struct */ +#define FSCTL_CREATE_OR_GET_OBJECT_ID 0x000900C0 /* BB add struct */ +#define FSCTL_SET_SPARSE 0x000900C4 /* BB add struct */ +#define FSCTL_SET_ZERO_DATA 0x000980C8 /* BB add struct */ +#define FSCTL_SET_ENCRYPTION 0x000900D7 /* BB add struct */ +#define FSCTL_ENCRYPTION_FSCTL_IO 0x000900DB /* BB add struct */ +#define FSCTL_WRITE_RAW_ENCRYPTED 0x000900DF /* BB add struct */ +#define FSCTL_READ_RAW_ENCRYPTED 0x000900E3 /* BB add struct */ +#define FSCTL_READ_FILE_USN_DATA 0x000900EB /* BB add struct */ +#define FSCTL_WRITE_USN_CLOSE_RECORD 0x000900EF /* BB add struct */ +#define FSCTL_SIS_COPYFILE 0x00090100 /* BB add struct */ +#define FSCTL_RECALL_FILE 0x00090117 /* BB add struct */ +#define FSCTL_QUERY_SPARING_INFO 0x00090138 /* BB add struct */ +#define FSCTL_SET_ZERO_ON_DEALLOC 0x00090194 /* BB add struct */ +#define FSCTL_SET_SHORT_NAME_BEHAVIOR 0x000901B4 /* BB add struct */ +#define FSCTL_QUERY_ALLOCATED_RANGES 0x000940CF /* BB add struct */ +#define FSCTL_SET_DEFECT_MANAGEMENT 0x00098134 /* BB add struct */ +#define FSCTL_DUPLICATE_EXTENTS_TO_FILE 0x00098344 +#define FSCTL_SIS_LINK_FILES 0x0009C104 +#define FSCTL_PIPE_PEEK 0x0011400C /* BB add struct */ +#define FSCTL_PIPE_TRANSCEIVE 0x0011C017 /* BB add struct */ +/* strange that the number for this op is not sequential with previous op */ +#define FSCTL_PIPE_WAIT 0x00110018 /* BB add struct */ +#define FSCTL_REQUEST_RESUME_KEY 0x00140078 +#define FSCTL_LMR_GET_LINK_TRACK_INF 0x001400E8 /* BB add struct */ +#define FSCTL_LMR_SET_LINK_TRACK_INF 0x001400EC /* BB add struct */ +#define FSCTL_VALIDATE_NEGOTIATE_INFO 0x00140204 +#define FSCTL_QUERY_NETWORK_INTERFACE_INFO 0x001401FC +#define FSCTL_COPYCHUNK 0x001440F2 +#define FSCTL_COPYCHUNK_WRITE 0x001480F2 + +#define IO_REPARSE_TAG_MOUNT_POINT 0xA0000003 +#define IO_REPARSE_TAG_HSM 0xC0000004 +#define IO_REPARSE_TAG_SIS 0x80000007 + +/* WSL reparse tags */ +#define IO_REPARSE_TAG_LX_SYMLINK_LE cpu_to_le32(0xA000001D) +#define IO_REPARSE_TAG_AF_UNIX_LE cpu_to_le32(0x80000023) +#define IO_REPARSE_TAG_LX_FIFO_LE cpu_to_le32(0x80000024) +#define IO_REPARSE_TAG_LX_CHR_LE cpu_to_le32(0x80000025) +#define IO_REPARSE_TAG_LX_BLK_LE cpu_to_le32(0x80000026) +#endif /* __KSMBD_SMBFSCTL_H */ diff --git a/fs/smb/server/smbstatus.h b/fs/smb/server/smbstatus.h new file mode 100644 index 0000000000000..108a8b6ed24a0 --- /dev/null +++ b/fs/smb/server/smbstatus.h @@ -0,0 +1,1822 @@ +/* SPDX-License-Identifier: LGPL-2.1+ */ +/* + * fs/cifs/smb2status.h + * + * SMB2 Status code (network error) definitions + * Definitions are from MS-ERREF + * + * Copyright (c) International Business Machines Corp., 2009,2011 + * Author(s): Steve French (sfrench@us.ibm.com) + */ + +/* + * 0 1 2 3 4 5 6 7 8 9 0 A B C D E F 0 1 2 3 4 5 6 7 8 9 A B C D E F + * SEV C N <-------Facility--------> <------Error Status Code------> + * + * C is set if "customer defined" error, N bit is reserved and MBZ + */ + +#define STATUS_SEVERITY_SUCCESS cpu_to_le32(0x0000) +#define STATUS_SEVERITY_INFORMATIONAL cpu_to_le32(0x0001) +#define STATUS_SEVERITY_WARNING cpu_to_le32(0x0002) +#define STATUS_SEVERITY_ERROR cpu_to_le32(0x0003) + +struct ntstatus { + /* Facility is the high 12 bits of the following field */ + __le32 Facility; /* low 2 bits Severity, next is Customer, then rsrvd */ + __le32 Code; +}; + +#define STATUS_SUCCESS 0x00000000 +#define STATUS_WAIT_0 cpu_to_le32(0x00000000) +#define STATUS_WAIT_1 cpu_to_le32(0x00000001) +#define STATUS_WAIT_2 cpu_to_le32(0x00000002) +#define STATUS_WAIT_3 cpu_to_le32(0x00000003) +#define STATUS_WAIT_63 cpu_to_le32(0x0000003F) +#define STATUS_ABANDONED cpu_to_le32(0x00000080) +#define STATUS_ABANDONED_WAIT_0 cpu_to_le32(0x00000080) +#define STATUS_ABANDONED_WAIT_63 cpu_to_le32(0x000000BF) +#define STATUS_USER_APC cpu_to_le32(0x000000C0) +#define STATUS_KERNEL_APC cpu_to_le32(0x00000100) +#define STATUS_ALERTED cpu_to_le32(0x00000101) +#define STATUS_TIMEOUT cpu_to_le32(0x00000102) +#define STATUS_PENDING cpu_to_le32(0x00000103) +#define STATUS_REPARSE cpu_to_le32(0x00000104) +#define STATUS_MORE_ENTRIES cpu_to_le32(0x00000105) +#define STATUS_NOT_ALL_ASSIGNED cpu_to_le32(0x00000106) +#define STATUS_SOME_NOT_MAPPED cpu_to_le32(0x00000107) +#define STATUS_OPLOCK_BREAK_IN_PROGRESS cpu_to_le32(0x00000108) +#define STATUS_VOLUME_MOUNTED cpu_to_le32(0x00000109) +#define STATUS_RXACT_COMMITTED cpu_to_le32(0x0000010A) +#define STATUS_NOTIFY_CLEANUP cpu_to_le32(0x0000010B) +#define STATUS_NOTIFY_ENUM_DIR cpu_to_le32(0x0000010C) +#define STATUS_NO_QUOTAS_FOR_ACCOUNT cpu_to_le32(0x0000010D) +#define STATUS_PRIMARY_TRANSPORT_CONNECT_FAILED cpu_to_le32(0x0000010E) +#define STATUS_PAGE_FAULT_TRANSITION cpu_to_le32(0x00000110) +#define STATUS_PAGE_FAULT_DEMAND_ZERO cpu_to_le32(0x00000111) +#define STATUS_PAGE_FAULT_COPY_ON_WRITE cpu_to_le32(0x00000112) +#define STATUS_PAGE_FAULT_GUARD_PAGE cpu_to_le32(0x00000113) +#define STATUS_PAGE_FAULT_PAGING_FILE cpu_to_le32(0x00000114) +#define STATUS_CACHE_PAGE_LOCKED cpu_to_le32(0x00000115) +#define STATUS_CRASH_DUMP cpu_to_le32(0x00000116) +#define STATUS_BUFFER_ALL_ZEROS cpu_to_le32(0x00000117) +#define STATUS_REPARSE_OBJECT cpu_to_le32(0x00000118) +#define STATUS_RESOURCE_REQUIREMENTS_CHANGED cpu_to_le32(0x00000119) +#define STATUS_TRANSLATION_COMPLETE cpu_to_le32(0x00000120) +#define STATUS_DS_MEMBERSHIP_EVALUATED_LOCALLY cpu_to_le32(0x00000121) +#define STATUS_NOTHING_TO_TERMINATE cpu_to_le32(0x00000122) +#define STATUS_PROCESS_NOT_IN_JOB cpu_to_le32(0x00000123) +#define STATUS_PROCESS_IN_JOB cpu_to_le32(0x00000124) +#define STATUS_VOLSNAP_HIBERNATE_READY cpu_to_le32(0x00000125) +#define STATUS_FSFILTER_OP_COMPLETED_SUCCESSFULLY cpu_to_le32(0x00000126) +#define STATUS_INTERRUPT_VECTOR_ALREADY_CONNECTED cpu_to_le32(0x00000127) +#define STATUS_INTERRUPT_STILL_CONNECTED cpu_to_le32(0x00000128) +#define STATUS_PROCESS_CLONED cpu_to_le32(0x00000129) +#define STATUS_FILE_LOCKED_WITH_ONLY_READERS cpu_to_le32(0x0000012A) +#define STATUS_FILE_LOCKED_WITH_WRITERS cpu_to_le32(0x0000012B) +#define STATUS_RESOURCEMANAGER_READ_ONLY cpu_to_le32(0x00000202) +#define STATUS_WAIT_FOR_OPLOCK cpu_to_le32(0x00000367) +#define DBG_EXCEPTION_HANDLED cpu_to_le32(0x00010001) +#define DBG_CONTINUE cpu_to_le32(0x00010002) +#define STATUS_FLT_IO_COMPLETE cpu_to_le32(0x001C0001) +#define STATUS_OBJECT_NAME_EXISTS cpu_to_le32(0x40000000) +#define STATUS_THREAD_WAS_SUSPENDED cpu_to_le32(0x40000001) +#define STATUS_WORKING_SET_LIMIT_RANGE cpu_to_le32(0x40000002) +#define STATUS_IMAGE_NOT_AT_BASE cpu_to_le32(0x40000003) +#define STATUS_RXACT_STATE_CREATED cpu_to_le32(0x40000004) +#define STATUS_SEGMENT_NOTIFICATION cpu_to_le32(0x40000005) +#define STATUS_LOCAL_USER_SESSION_KEY cpu_to_le32(0x40000006) +#define STATUS_BAD_CURRENT_DIRECTORY cpu_to_le32(0x40000007) +#define STATUS_SERIAL_MORE_WRITES cpu_to_le32(0x40000008) +#define STATUS_REGISTRY_RECOVERED cpu_to_le32(0x40000009) +#define STATUS_FT_READ_RECOVERY_FROM_BACKUP cpu_to_le32(0x4000000A) +#define STATUS_FT_WRITE_RECOVERY cpu_to_le32(0x4000000B) +#define STATUS_SERIAL_COUNTER_TIMEOUT cpu_to_le32(0x4000000C) +#define STATUS_NULL_LM_PASSWORD cpu_to_le32(0x4000000D) +#define STATUS_IMAGE_MACHINE_TYPE_MISMATCH cpu_to_le32(0x4000000E) +#define STATUS_RECEIVE_PARTIAL cpu_to_le32(0x4000000F) +#define STATUS_RECEIVE_EXPEDITED cpu_to_le32(0x40000010) +#define STATUS_RECEIVE_PARTIAL_EXPEDITED cpu_to_le32(0x40000011) +#define STATUS_EVENT_DONE cpu_to_le32(0x40000012) +#define STATUS_EVENT_PENDING cpu_to_le32(0x40000013) +#define STATUS_CHECKING_FILE_SYSTEM cpu_to_le32(0x40000014) +#define STATUS_FATAL_APP_EXIT cpu_to_le32(0x40000015) +#define STATUS_PREDEFINED_HANDLE cpu_to_le32(0x40000016) +#define STATUS_WAS_UNLOCKED cpu_to_le32(0x40000017) +#define STATUS_SERVICE_NOTIFICATION cpu_to_le32(0x40000018) +#define STATUS_WAS_LOCKED cpu_to_le32(0x40000019) +#define STATUS_LOG_HARD_ERROR cpu_to_le32(0x4000001A) +#define STATUS_ALREADY_WIN32 cpu_to_le32(0x4000001B) +#define STATUS_WX86_UNSIMULATE cpu_to_le32(0x4000001C) +#define STATUS_WX86_CONTINUE cpu_to_le32(0x4000001D) +#define STATUS_WX86_SINGLE_STEP cpu_to_le32(0x4000001E) +#define STATUS_WX86_BREAKPOINT cpu_to_le32(0x4000001F) +#define STATUS_WX86_EXCEPTION_CONTINUE cpu_to_le32(0x40000020) +#define STATUS_WX86_EXCEPTION_LASTCHANCE cpu_to_le32(0x40000021) +#define STATUS_WX86_EXCEPTION_CHAIN cpu_to_le32(0x40000022) +#define STATUS_IMAGE_MACHINE_TYPE_MISMATCH_EXE cpu_to_le32(0x40000023) +#define STATUS_NO_YIELD_PERFORMED cpu_to_le32(0x40000024) +#define STATUS_TIMER_RESUME_IGNORED cpu_to_le32(0x40000025) +#define STATUS_ARBITRATION_UNHANDLED cpu_to_le32(0x40000026) +#define STATUS_CARDBUS_NOT_SUPPORTED cpu_to_le32(0x40000027) +#define STATUS_WX86_CREATEWX86TIB cpu_to_le32(0x40000028) +#define STATUS_MP_PROCESSOR_MISMATCH cpu_to_le32(0x40000029) +#define STATUS_HIBERNATED cpu_to_le32(0x4000002A) +#define STATUS_RESUME_HIBERNATION cpu_to_le32(0x4000002B) +#define STATUS_FIRMWARE_UPDATED cpu_to_le32(0x4000002C) +#define STATUS_DRIVERS_LEAKING_LOCKED_PAGES cpu_to_le32(0x4000002D) +#define STATUS_MESSAGE_RETRIEVED cpu_to_le32(0x4000002E) +#define STATUS_SYSTEM_POWERSTATE_TRANSITION cpu_to_le32(0x4000002F) +#define STATUS_ALPC_CHECK_COMPLETION_LIST cpu_to_le32(0x40000030) +#define STATUS_SYSTEM_POWERSTATE_COMPLEX_TRANSITION cpu_to_le32(0x40000031) +#define STATUS_ACCESS_AUDIT_BY_POLICY cpu_to_le32(0x40000032) +#define STATUS_ABANDON_HIBERFILE cpu_to_le32(0x40000033) +#define STATUS_BIZRULES_NOT_ENABLED cpu_to_le32(0x40000034) +#define STATUS_WAKE_SYSTEM cpu_to_le32(0x40000294) +#define STATUS_DS_SHUTTING_DOWN cpu_to_le32(0x40000370) +#define DBG_REPLY_LATER cpu_to_le32(0x40010001) +#define DBG_UNABLE_TO_PROVIDE_HANDLE cpu_to_le32(0x40010002) +#define DBG_TERMINATE_THREAD cpu_to_le32(0x40010003) +#define DBG_TERMINATE_PROCESS cpu_to_le32(0x40010004) +#define DBG_CONTROL_C cpu_to_le32(0x40010005) +#define DBG_PRINTEXCEPTION_C cpu_to_le32(0x40010006) +#define DBG_RIPEXCEPTION cpu_to_le32(0x40010007) +#define DBG_CONTROL_BREAK cpu_to_le32(0x40010008) +#define DBG_COMMAND_EXCEPTION cpu_to_le32(0x40010009) +#define RPC_NT_UUID_LOCAL_ONLY cpu_to_le32(0x40020056) +#define RPC_NT_SEND_INCOMPLETE cpu_to_le32(0x400200AF) +#define STATUS_CTX_CDM_CONNECT cpu_to_le32(0x400A0004) +#define STATUS_CTX_CDM_DISCONNECT cpu_to_le32(0x400A0005) +#define STATUS_SXS_RELEASE_ACTIVATION_CONTEXT cpu_to_le32(0x4015000D) +#define STATUS_RECOVERY_NOT_NEEDED cpu_to_le32(0x40190034) +#define STATUS_RM_ALREADY_STARTED cpu_to_le32(0x40190035) +#define STATUS_LOG_NO_RESTART cpu_to_le32(0x401A000C) +#define STATUS_VIDEO_DRIVER_DEBUG_REPORT_REQUEST cpu_to_le32(0x401B00EC) +#define STATUS_GRAPHICS_PARTIAL_DATA_POPULATED cpu_to_le32(0x401E000A) +#define STATUS_GRAPHICS_DRIVER_MISMATCH cpu_to_le32(0x401E0117) +#define STATUS_GRAPHICS_MODE_NOT_PINNED cpu_to_le32(0x401E0307) +#define STATUS_GRAPHICS_NO_PREFERRED_MODE cpu_to_le32(0x401E031E) +#define STATUS_GRAPHICS_DATASET_IS_EMPTY cpu_to_le32(0x401E034B) +#define STATUS_GRAPHICS_NO_MORE_ELEMENTS_IN_DATASET cpu_to_le32(0x401E034C) +#define STATUS_GRAPHICS_PATH_CONTENT_GEOMETRY_TRANSFORMATION_NOT_PINNED \ + cpu_to_le32(0x401E0351) +#define STATUS_GRAPHICS_UNKNOWN_CHILD_STATUS cpu_to_le32(0x401E042F) +#define STATUS_GRAPHICS_LEADLINK_START_DEFERRED cpu_to_le32(0x401E0437) +#define STATUS_GRAPHICS_POLLING_TOO_FREQUENTLY cpu_to_le32(0x401E0439) +#define STATUS_GRAPHICS_START_DEFERRED cpu_to_le32(0x401E043A) +#define STATUS_NDIS_INDICATION_REQUIRED cpu_to_le32(0x40230001) +#define STATUS_GUARD_PAGE_VIOLATION cpu_to_le32(0x80000001) +#define STATUS_DATATYPE_MISALIGNMENT cpu_to_le32(0x80000002) +#define STATUS_BREAKPOINT cpu_to_le32(0x80000003) +#define STATUS_SINGLE_STEP cpu_to_le32(0x80000004) +#define STATUS_BUFFER_OVERFLOW cpu_to_le32(0x80000005) +#define STATUS_NO_MORE_FILES cpu_to_le32(0x80000006) +#define STATUS_WAKE_SYSTEM_DEBUGGER cpu_to_le32(0x80000007) +#define STATUS_HANDLES_CLOSED cpu_to_le32(0x8000000A) +#define STATUS_NO_INHERITANCE cpu_to_le32(0x8000000B) +#define STATUS_GUID_SUBSTITUTION_MADE cpu_to_le32(0x8000000C) +#define STATUS_PARTIAL_COPY cpu_to_le32(0x8000000D) +#define STATUS_DEVICE_PAPER_EMPTY cpu_to_le32(0x8000000E) +#define STATUS_DEVICE_POWERED_OFF cpu_to_le32(0x8000000F) +#define STATUS_DEVICE_OFF_LINE cpu_to_le32(0x80000010) +#define STATUS_DEVICE_BUSY cpu_to_le32(0x80000011) +#define STATUS_NO_MORE_EAS cpu_to_le32(0x80000012) +#define STATUS_INVALID_EA_NAME cpu_to_le32(0x80000013) +#define STATUS_EA_LIST_INCONSISTENT cpu_to_le32(0x80000014) +#define STATUS_INVALID_EA_FLAG cpu_to_le32(0x80000015) +#define STATUS_VERIFY_REQUIRED cpu_to_le32(0x80000016) +#define STATUS_EXTRANEOUS_INFORMATION cpu_to_le32(0x80000017) +#define STATUS_RXACT_COMMIT_NECESSARY cpu_to_le32(0x80000018) +#define STATUS_NO_MORE_ENTRIES cpu_to_le32(0x8000001A) +#define STATUS_FILEMARK_DETECTED cpu_to_le32(0x8000001B) +#define STATUS_MEDIA_CHANGED cpu_to_le32(0x8000001C) +#define STATUS_BUS_RESET cpu_to_le32(0x8000001D) +#define STATUS_END_OF_MEDIA cpu_to_le32(0x8000001E) +#define STATUS_BEGINNING_OF_MEDIA cpu_to_le32(0x8000001F) +#define STATUS_MEDIA_CHECK cpu_to_le32(0x80000020) +#define STATUS_SETMARK_DETECTED cpu_to_le32(0x80000021) +#define STATUS_NO_DATA_DETECTED cpu_to_le32(0x80000022) +#define STATUS_REDIRECTOR_HAS_OPEN_HANDLES cpu_to_le32(0x80000023) +#define STATUS_SERVER_HAS_OPEN_HANDLES cpu_to_le32(0x80000024) +#define STATUS_ALREADY_DISCONNECTED cpu_to_le32(0x80000025) +#define STATUS_LONGJUMP cpu_to_le32(0x80000026) +#define STATUS_CLEANER_CARTRIDGE_INSTALLED cpu_to_le32(0x80000027) +#define STATUS_PLUGPLAY_QUERY_VETOED cpu_to_le32(0x80000028) +#define STATUS_UNWIND_CONSOLIDATE cpu_to_le32(0x80000029) +#define STATUS_REGISTRY_HIVE_RECOVERED cpu_to_le32(0x8000002A) +#define STATUS_DLL_MIGHT_BE_INSECURE cpu_to_le32(0x8000002B) +#define STATUS_DLL_MIGHT_BE_INCOMPATIBLE cpu_to_le32(0x8000002C) +#define STATUS_STOPPED_ON_SYMLINK cpu_to_le32(0x8000002D) +#define STATUS_DEVICE_REQUIRES_CLEANING cpu_to_le32(0x80000288) +#define STATUS_DEVICE_DOOR_OPEN cpu_to_le32(0x80000289) +#define STATUS_DATA_LOST_REPAIR cpu_to_le32(0x80000803) +#define DBG_EXCEPTION_NOT_HANDLED cpu_to_le32(0x80010001) +#define STATUS_CLUSTER_NODE_ALREADY_UP cpu_to_le32(0x80130001) +#define STATUS_CLUSTER_NODE_ALREADY_DOWN cpu_to_le32(0x80130002) +#define STATUS_CLUSTER_NETWORK_ALREADY_ONLINE cpu_to_le32(0x80130003) +#define STATUS_CLUSTER_NETWORK_ALREADY_OFFLINE cpu_to_le32(0x80130004) +#define STATUS_CLUSTER_NODE_ALREADY_MEMBER cpu_to_le32(0x80130005) +#define STATUS_COULD_NOT_RESIZE_LOG cpu_to_le32(0x80190009) +#define STATUS_NO_TXF_METADATA cpu_to_le32(0x80190029) +#define STATUS_CANT_RECOVER_WITH_HANDLE_OPEN cpu_to_le32(0x80190031) +#define STATUS_TXF_METADATA_ALREADY_PRESENT cpu_to_le32(0x80190041) +#define STATUS_TRANSACTION_SCOPE_CALLBACKS_NOT_SET cpu_to_le32(0x80190042) +#define STATUS_VIDEO_HUNG_DISPLAY_DRIVER_THREAD_RECOVERED \ + cpu_to_le32(0x801B00EB) +#define STATUS_FLT_BUFFER_TOO_SMALL cpu_to_le32(0x801C0001) +#define STATUS_FVE_PARTIAL_METADATA cpu_to_le32(0x80210001) +#define STATUS_UNSUCCESSFUL cpu_to_le32(0xC0000001) +#define STATUS_NOT_IMPLEMENTED cpu_to_le32(0xC0000002) +#define STATUS_INVALID_INFO_CLASS cpu_to_le32(0xC0000003) +#define STATUS_INFO_LENGTH_MISMATCH cpu_to_le32(0xC0000004) +#define STATUS_ACCESS_VIOLATION cpu_to_le32(0xC0000005) +#define STATUS_IN_PAGE_ERROR cpu_to_le32(0xC0000006) +#define STATUS_PAGEFILE_QUOTA cpu_to_le32(0xC0000007) +#define STATUS_INVALID_HANDLE cpu_to_le32(0xC0000008) +#define STATUS_BAD_INITIAL_STACK cpu_to_le32(0xC0000009) +#define STATUS_BAD_INITIAL_PC cpu_to_le32(0xC000000A) +#define STATUS_INVALID_CID cpu_to_le32(0xC000000B) +#define STATUS_TIMER_NOT_CANCELED cpu_to_le32(0xC000000C) +#define STATUS_INVALID_PARAMETER cpu_to_le32(0xC000000D) +#define STATUS_NO_SUCH_DEVICE cpu_to_le32(0xC000000E) +#define STATUS_NO_SUCH_FILE cpu_to_le32(0xC000000F) +#define STATUS_INVALID_DEVICE_REQUEST cpu_to_le32(0xC0000010) +#define STATUS_END_OF_FILE cpu_to_le32(0xC0000011) +#define STATUS_WRONG_VOLUME cpu_to_le32(0xC0000012) +#define STATUS_NO_MEDIA_IN_DEVICE cpu_to_le32(0xC0000013) +#define STATUS_UNRECOGNIZED_MEDIA cpu_to_le32(0xC0000014) +#define STATUS_NONEXISTENT_SECTOR cpu_to_le32(0xC0000015) +#define STATUS_MORE_PROCESSING_REQUIRED cpu_to_le32(0xC0000016) +#define STATUS_NO_MEMORY cpu_to_le32(0xC0000017) +#define STATUS_CONFLICTING_ADDRESSES cpu_to_le32(0xC0000018) +#define STATUS_NOT_MAPPED_VIEW cpu_to_le32(0xC0000019) +#define STATUS_UNABLE_TO_FREE_VM cpu_to_le32(0xC000001A) +#define STATUS_UNABLE_TO_DELETE_SECTION cpu_to_le32(0xC000001B) +#define STATUS_INVALID_SYSTEM_SERVICE cpu_to_le32(0xC000001C) +#define STATUS_ILLEGAL_INSTRUCTION cpu_to_le32(0xC000001D) +#define STATUS_INVALID_LOCK_SEQUENCE cpu_to_le32(0xC000001E) +#define STATUS_INVALID_VIEW_SIZE cpu_to_le32(0xC000001F) +#define STATUS_INVALID_FILE_FOR_SECTION cpu_to_le32(0xC0000020) +#define STATUS_ALREADY_COMMITTED cpu_to_le32(0xC0000021) +#define STATUS_ACCESS_DENIED cpu_to_le32(0xC0000022) +#define STATUS_BUFFER_TOO_SMALL cpu_to_le32(0xC0000023) +#define STATUS_OBJECT_TYPE_MISMATCH cpu_to_le32(0xC0000024) +#define STATUS_NONCONTINUABLE_EXCEPTION cpu_to_le32(0xC0000025) +#define STATUS_INVALID_DISPOSITION cpu_to_le32(0xC0000026) +#define STATUS_UNWIND cpu_to_le32(0xC0000027) +#define STATUS_BAD_STACK cpu_to_le32(0xC0000028) +#define STATUS_INVALID_UNWIND_TARGET cpu_to_le32(0xC0000029) +#define STATUS_NOT_LOCKED cpu_to_le32(0xC000002A) +#define STATUS_PARITY_ERROR cpu_to_le32(0xC000002B) +#define STATUS_UNABLE_TO_DECOMMIT_VM cpu_to_le32(0xC000002C) +#define STATUS_NOT_COMMITTED cpu_to_le32(0xC000002D) +#define STATUS_INVALID_PORT_ATTRIBUTES cpu_to_le32(0xC000002E) +#define STATUS_PORT_MESSAGE_TOO_LONG cpu_to_le32(0xC000002F) +#define STATUS_INVALID_PARAMETER_MIX cpu_to_le32(0xC0000030) +#define STATUS_INVALID_QUOTA_LOWER cpu_to_le32(0xC0000031) +#define STATUS_DISK_CORRUPT_ERROR cpu_to_le32(0xC0000032) +#define STATUS_OBJECT_NAME_INVALID cpu_to_le32(0xC0000033) +#define STATUS_OBJECT_NAME_NOT_FOUND cpu_to_le32(0xC0000034) +#define STATUS_OBJECT_NAME_COLLISION cpu_to_le32(0xC0000035) +#define STATUS_PORT_DISCONNECTED cpu_to_le32(0xC0000037) +#define STATUS_DEVICE_ALREADY_ATTACHED cpu_to_le32(0xC0000038) +#define STATUS_OBJECT_PATH_INVALID cpu_to_le32(0xC0000039) +#define STATUS_OBJECT_PATH_NOT_FOUND cpu_to_le32(0xC000003A) +#define STATUS_OBJECT_PATH_SYNTAX_BAD cpu_to_le32(0xC000003B) +#define STATUS_DATA_OVERRUN cpu_to_le32(0xC000003C) +#define STATUS_DATA_LATE_ERROR cpu_to_le32(0xC000003D) +#define STATUS_DATA_ERROR cpu_to_le32(0xC000003E) +#define STATUS_CRC_ERROR cpu_to_le32(0xC000003F) +#define STATUS_SECTION_TOO_BIG cpu_to_le32(0xC0000040) +#define STATUS_PORT_CONNECTION_REFUSED cpu_to_le32(0xC0000041) +#define STATUS_INVALID_PORT_HANDLE cpu_to_le32(0xC0000042) +#define STATUS_SHARING_VIOLATION cpu_to_le32(0xC0000043) +#define STATUS_QUOTA_EXCEEDED cpu_to_le32(0xC0000044) +#define STATUS_INVALID_PAGE_PROTECTION cpu_to_le32(0xC0000045) +#define STATUS_MUTANT_NOT_OWNED cpu_to_le32(0xC0000046) +#define STATUS_SEMAPHORE_LIMIT_EXCEEDED cpu_to_le32(0xC0000047) +#define STATUS_PORT_ALREADY_SET cpu_to_le32(0xC0000048) +#define STATUS_SECTION_NOT_IMAGE cpu_to_le32(0xC0000049) +#define STATUS_SUSPEND_COUNT_EXCEEDED cpu_to_le32(0xC000004A) +#define STATUS_THREAD_IS_TERMINATING cpu_to_le32(0xC000004B) +#define STATUS_BAD_WORKING_SET_LIMIT cpu_to_le32(0xC000004C) +#define STATUS_INCOMPATIBLE_FILE_MAP cpu_to_le32(0xC000004D) +#define STATUS_SECTION_PROTECTION cpu_to_le32(0xC000004E) +#define STATUS_EAS_NOT_SUPPORTED cpu_to_le32(0xC000004F) +#define STATUS_EA_TOO_LARGE cpu_to_le32(0xC0000050) +#define STATUS_NONEXISTENT_EA_ENTRY cpu_to_le32(0xC0000051) +#define STATUS_NO_EAS_ON_FILE cpu_to_le32(0xC0000052) +#define STATUS_EA_CORRUPT_ERROR cpu_to_le32(0xC0000053) +#define STATUS_FILE_LOCK_CONFLICT cpu_to_le32(0xC0000054) +#define STATUS_LOCK_NOT_GRANTED cpu_to_le32(0xC0000055) +#define STATUS_DELETE_PENDING cpu_to_le32(0xC0000056) +#define STATUS_CTL_FILE_NOT_SUPPORTED cpu_to_le32(0xC0000057) +#define STATUS_UNKNOWN_REVISION cpu_to_le32(0xC0000058) +#define STATUS_REVISION_MISMATCH cpu_to_le32(0xC0000059) +#define STATUS_INVALID_OWNER cpu_to_le32(0xC000005A) +#define STATUS_INVALID_PRIMARY_GROUP cpu_to_le32(0xC000005B) +#define STATUS_NO_IMPERSONATION_TOKEN cpu_to_le32(0xC000005C) +#define STATUS_CANT_DISABLE_MANDATORY cpu_to_le32(0xC000005D) +#define STATUS_NO_LOGON_SERVERS cpu_to_le32(0xC000005E) +#define STATUS_NO_SUCH_LOGON_SESSION cpu_to_le32(0xC000005F) +#define STATUS_NO_SUCH_PRIVILEGE cpu_to_le32(0xC0000060) +#define STATUS_PRIVILEGE_NOT_HELD cpu_to_le32(0xC0000061) +#define STATUS_INVALID_ACCOUNT_NAME cpu_to_le32(0xC0000062) +#define STATUS_USER_EXISTS cpu_to_le32(0xC0000063) +#define STATUS_NO_SUCH_USER cpu_to_le32(0xC0000064) +#define STATUS_GROUP_EXISTS cpu_to_le32(0xC0000065) +#define STATUS_NO_SUCH_GROUP cpu_to_le32(0xC0000066) +#define STATUS_MEMBER_IN_GROUP cpu_to_le32(0xC0000067) +#define STATUS_MEMBER_NOT_IN_GROUP cpu_to_le32(0xC0000068) +#define STATUS_LAST_ADMIN cpu_to_le32(0xC0000069) +#define STATUS_WRONG_PASSWORD cpu_to_le32(0xC000006A) +#define STATUS_ILL_FORMED_PASSWORD cpu_to_le32(0xC000006B) +#define STATUS_PASSWORD_RESTRICTION cpu_to_le32(0xC000006C) +#define STATUS_LOGON_FAILURE cpu_to_le32(0xC000006D) +#define STATUS_ACCOUNT_RESTRICTION cpu_to_le32(0xC000006E) +#define STATUS_INVALID_LOGON_HOURS cpu_to_le32(0xC000006F) +#define STATUS_INVALID_WORKSTATION cpu_to_le32(0xC0000070) +#define STATUS_PASSWORD_EXPIRED cpu_to_le32(0xC0000071) +#define STATUS_ACCOUNT_DISABLED cpu_to_le32(0xC0000072) +#define STATUS_NONE_MAPPED cpu_to_le32(0xC0000073) +#define STATUS_TOO_MANY_LUIDS_REQUESTED cpu_to_le32(0xC0000074) +#define STATUS_LUIDS_EXHAUSTED cpu_to_le32(0xC0000075) +#define STATUS_INVALID_SUB_AUTHORITY cpu_to_le32(0xC0000076) +#define STATUS_INVALID_ACL cpu_to_le32(0xC0000077) +#define STATUS_INVALID_SID cpu_to_le32(0xC0000078) +#define STATUS_INVALID_SECURITY_DESCR cpu_to_le32(0xC0000079) +#define STATUS_PROCEDURE_NOT_FOUND cpu_to_le32(0xC000007A) +#define STATUS_INVALID_IMAGE_FORMAT cpu_to_le32(0xC000007B) +#define STATUS_NO_TOKEN cpu_to_le32(0xC000007C) +#define STATUS_BAD_INHERITANCE_ACL cpu_to_le32(0xC000007D) +#define STATUS_RANGE_NOT_LOCKED cpu_to_le32(0xC000007E) +#define STATUS_DISK_FULL cpu_to_le32(0xC000007F) +#define STATUS_SERVER_DISABLED cpu_to_le32(0xC0000080) +#define STATUS_SERVER_NOT_DISABLED cpu_to_le32(0xC0000081) +#define STATUS_TOO_MANY_GUIDS_REQUESTED cpu_to_le32(0xC0000082) +#define STATUS_GUIDS_EXHAUSTED cpu_to_le32(0xC0000083) +#define STATUS_INVALID_ID_AUTHORITY cpu_to_le32(0xC0000084) +#define STATUS_AGENTS_EXHAUSTED cpu_to_le32(0xC0000085) +#define STATUS_INVALID_VOLUME_LABEL cpu_to_le32(0xC0000086) +#define STATUS_SECTION_NOT_EXTENDED cpu_to_le32(0xC0000087) +#define STATUS_NOT_MAPPED_DATA cpu_to_le32(0xC0000088) +#define STATUS_RESOURCE_DATA_NOT_FOUND cpu_to_le32(0xC0000089) +#define STATUS_RESOURCE_TYPE_NOT_FOUND cpu_to_le32(0xC000008A) +#define STATUS_RESOURCE_NAME_NOT_FOUND cpu_to_le32(0xC000008B) +#define STATUS_ARRAY_BOUNDS_EXCEEDED cpu_to_le32(0xC000008C) +#define STATUS_FLOAT_DENORMAL_OPERAND cpu_to_le32(0xC000008D) +#define STATUS_FLOAT_DIVIDE_BY_ZERO cpu_to_le32(0xC000008E) +#define STATUS_FLOAT_INEXACT_RESULT cpu_to_le32(0xC000008F) +#define STATUS_FLOAT_INVALID_OPERATION cpu_to_le32(0xC0000090) +#define STATUS_FLOAT_OVERFLOW cpu_to_le32(0xC0000091) +#define STATUS_FLOAT_STACK_CHECK cpu_to_le32(0xC0000092) +#define STATUS_FLOAT_UNDERFLOW cpu_to_le32(0xC0000093) +#define STATUS_INTEGER_DIVIDE_BY_ZERO cpu_to_le32(0xC0000094) +#define STATUS_INTEGER_OVERFLOW cpu_to_le32(0xC0000095) +#define STATUS_PRIVILEGED_INSTRUCTION cpu_to_le32(0xC0000096) +#define STATUS_TOO_MANY_PAGING_FILES cpu_to_le32(0xC0000097) +#define STATUS_FILE_INVALID cpu_to_le32(0xC0000098) +#define STATUS_ALLOTTED_SPACE_EXCEEDED cpu_to_le32(0xC0000099) +#define STATUS_INSUFFICIENT_RESOURCES cpu_to_le32(0xC000009A) +#define STATUS_DFS_EXIT_PATH_FOUND cpu_to_le32(0xC000009B) +#define STATUS_DEVICE_DATA_ERROR cpu_to_le32(0xC000009C) +#define STATUS_DEVICE_NOT_CONNECTED cpu_to_le32(0xC000009D) +#define STATUS_DEVICE_POWER_FAILURE cpu_to_le32(0xC000009E) +#define STATUS_FREE_VM_NOT_AT_BASE cpu_to_le32(0xC000009F) +#define STATUS_MEMORY_NOT_ALLOCATED cpu_to_le32(0xC00000A0) +#define STATUS_WORKING_SET_QUOTA cpu_to_le32(0xC00000A1) +#define STATUS_MEDIA_WRITE_PROTECTED cpu_to_le32(0xC00000A2) +#define STATUS_DEVICE_NOT_READY cpu_to_le32(0xC00000A3) +#define STATUS_INVALID_GROUP_ATTRIBUTES cpu_to_le32(0xC00000A4) +#define STATUS_BAD_IMPERSONATION_LEVEL cpu_to_le32(0xC00000A5) +#define STATUS_CANT_OPEN_ANONYMOUS cpu_to_le32(0xC00000A6) +#define STATUS_BAD_VALIDATION_CLASS cpu_to_le32(0xC00000A7) +#define STATUS_BAD_TOKEN_TYPE cpu_to_le32(0xC00000A8) +#define STATUS_BAD_MASTER_BOOT_RECORD cpu_to_le32(0xC00000A9) +#define STATUS_INSTRUCTION_MISALIGNMENT cpu_to_le32(0xC00000AA) +#define STATUS_INSTANCE_NOT_AVAILABLE cpu_to_le32(0xC00000AB) +#define STATUS_PIPE_NOT_AVAILABLE cpu_to_le32(0xC00000AC) +#define STATUS_INVALID_PIPE_STATE cpu_to_le32(0xC00000AD) +#define STATUS_PIPE_BUSY cpu_to_le32(0xC00000AE) +#define STATUS_ILLEGAL_FUNCTION cpu_to_le32(0xC00000AF) +#define STATUS_PIPE_DISCONNECTED cpu_to_le32(0xC00000B0) +#define STATUS_PIPE_CLOSING cpu_to_le32(0xC00000B1) +#define STATUS_PIPE_CONNECTED cpu_to_le32(0xC00000B2) +#define STATUS_PIPE_LISTENING cpu_to_le32(0xC00000B3) +#define STATUS_INVALID_READ_MODE cpu_to_le32(0xC00000B4) +#define STATUS_IO_TIMEOUT cpu_to_le32(0xC00000B5) +#define STATUS_FILE_FORCED_CLOSED cpu_to_le32(0xC00000B6) +#define STATUS_PROFILING_NOT_STARTED cpu_to_le32(0xC00000B7) +#define STATUS_PROFILING_NOT_STOPPED cpu_to_le32(0xC00000B8) +#define STATUS_COULD_NOT_INTERPRET cpu_to_le32(0xC00000B9) +#define STATUS_FILE_IS_A_DIRECTORY cpu_to_le32(0xC00000BA) +#define STATUS_NOT_SUPPORTED cpu_to_le32(0xC00000BB) +#define STATUS_REMOTE_NOT_LISTENING cpu_to_le32(0xC00000BC) +#define STATUS_DUPLICATE_NAME cpu_to_le32(0xC00000BD) +#define STATUS_BAD_NETWORK_PATH cpu_to_le32(0xC00000BE) +#define STATUS_NETWORK_BUSY cpu_to_le32(0xC00000BF) +#define STATUS_DEVICE_DOES_NOT_EXIST cpu_to_le32(0xC00000C0) +#define STATUS_TOO_MANY_COMMANDS cpu_to_le32(0xC00000C1) +#define STATUS_ADAPTER_HARDWARE_ERROR cpu_to_le32(0xC00000C2) +#define STATUS_INVALID_NETWORK_RESPONSE cpu_to_le32(0xC00000C3) +#define STATUS_UNEXPECTED_NETWORK_ERROR cpu_to_le32(0xC00000C4) +#define STATUS_BAD_REMOTE_ADAPTER cpu_to_le32(0xC00000C5) +#define STATUS_PRINT_QUEUE_FULL cpu_to_le32(0xC00000C6) +#define STATUS_NO_SPOOL_SPACE cpu_to_le32(0xC00000C7) +#define STATUS_PRINT_CANCELLED cpu_to_le32(0xC00000C8) +#define STATUS_NETWORK_NAME_DELETED cpu_to_le32(0xC00000C9) +#define STATUS_NETWORK_ACCESS_DENIED cpu_to_le32(0xC00000CA) +#define STATUS_BAD_DEVICE_TYPE cpu_to_le32(0xC00000CB) +#define STATUS_BAD_NETWORK_NAME cpu_to_le32(0xC00000CC) +#define STATUS_TOO_MANY_NAMES cpu_to_le32(0xC00000CD) +#define STATUS_TOO_MANY_SESSIONS cpu_to_le32(0xC00000CE) +#define STATUS_SHARING_PAUSED cpu_to_le32(0xC00000CF) +#define STATUS_REQUEST_NOT_ACCEPTED cpu_to_le32(0xC00000D0) +#define STATUS_REDIRECTOR_PAUSED cpu_to_le32(0xC00000D1) +#define STATUS_NET_WRITE_FAULT cpu_to_le32(0xC00000D2) +#define STATUS_PROFILING_AT_LIMIT cpu_to_le32(0xC00000D3) +#define STATUS_NOT_SAME_DEVICE cpu_to_le32(0xC00000D4) +#define STATUS_FILE_RENAMED cpu_to_le32(0xC00000D5) +#define STATUS_VIRTUAL_CIRCUIT_CLOSED cpu_to_le32(0xC00000D6) +#define STATUS_NO_SECURITY_ON_OBJECT cpu_to_le32(0xC00000D7) +#define STATUS_CANT_WAIT cpu_to_le32(0xC00000D8) +#define STATUS_PIPE_EMPTY cpu_to_le32(0xC00000D9) +#define STATUS_CANT_ACCESS_DOMAIN_INFO cpu_to_le32(0xC00000DA) +#define STATUS_CANT_TERMINATE_SELF cpu_to_le32(0xC00000DB) +#define STATUS_INVALID_SERVER_STATE cpu_to_le32(0xC00000DC) +#define STATUS_INVALID_DOMAIN_STATE cpu_to_le32(0xC00000DD) +#define STATUS_INVALID_DOMAIN_ROLE cpu_to_le32(0xC00000DE) +#define STATUS_NO_SUCH_DOMAIN cpu_to_le32(0xC00000DF) +#define STATUS_DOMAIN_EXISTS cpu_to_le32(0xC00000E0) +#define STATUS_DOMAIN_LIMIT_EXCEEDED cpu_to_le32(0xC00000E1) +#define STATUS_OPLOCK_NOT_GRANTED cpu_to_le32(0xC00000E2) +#define STATUS_INVALID_OPLOCK_PROTOCOL cpu_to_le32(0xC00000E3) +#define STATUS_INTERNAL_DB_CORRUPTION cpu_to_le32(0xC00000E4) +#define STATUS_INTERNAL_ERROR cpu_to_le32(0xC00000E5) +#define STATUS_GENERIC_NOT_MAPPED cpu_to_le32(0xC00000E6) +#define STATUS_BAD_DESCRIPTOR_FORMAT cpu_to_le32(0xC00000E7) +#define STATUS_INVALID_USER_BUFFER cpu_to_le32(0xC00000E8) +#define STATUS_UNEXPECTED_IO_ERROR cpu_to_le32(0xC00000E9) +#define STATUS_UNEXPECTED_MM_CREATE_ERR cpu_to_le32(0xC00000EA) +#define STATUS_UNEXPECTED_MM_MAP_ERROR cpu_to_le32(0xC00000EB) +#define STATUS_UNEXPECTED_MM_EXTEND_ERR cpu_to_le32(0xC00000EC) +#define STATUS_NOT_LOGON_PROCESS cpu_to_le32(0xC00000ED) +#define STATUS_LOGON_SESSION_EXISTS cpu_to_le32(0xC00000EE) +#define STATUS_INVALID_PARAMETER_1 cpu_to_le32(0xC00000EF) +#define STATUS_INVALID_PARAMETER_2 cpu_to_le32(0xC00000F0) +#define STATUS_INVALID_PARAMETER_3 cpu_to_le32(0xC00000F1) +#define STATUS_INVALID_PARAMETER_4 cpu_to_le32(0xC00000F2) +#define STATUS_INVALID_PARAMETER_5 cpu_to_le32(0xC00000F3) +#define STATUS_INVALID_PARAMETER_6 cpu_to_le32(0xC00000F4) +#define STATUS_INVALID_PARAMETER_7 cpu_to_le32(0xC00000F5) +#define STATUS_INVALID_PARAMETER_8 cpu_to_le32(0xC00000F6) +#define STATUS_INVALID_PARAMETER_9 cpu_to_le32(0xC00000F7) +#define STATUS_INVALID_PARAMETER_10 cpu_to_le32(0xC00000F8) +#define STATUS_INVALID_PARAMETER_11 cpu_to_le32(0xC00000F9) +#define STATUS_INVALID_PARAMETER_12 cpu_to_le32(0xC00000FA) +#define STATUS_REDIRECTOR_NOT_STARTED cpu_to_le32(0xC00000FB) +#define STATUS_REDIRECTOR_STARTED cpu_to_le32(0xC00000FC) +#define STATUS_STACK_OVERFLOW cpu_to_le32(0xC00000FD) +#define STATUS_NO_SUCH_PACKAGE cpu_to_le32(0xC00000FE) +#define STATUS_BAD_FUNCTION_TABLE cpu_to_le32(0xC00000FF) +#define STATUS_VARIABLE_NOT_FOUND cpu_to_le32(0xC0000100) +#define STATUS_DIRECTORY_NOT_EMPTY cpu_to_le32(0xC0000101) +#define STATUS_FILE_CORRUPT_ERROR cpu_to_le32(0xC0000102) +#define STATUS_NOT_A_DIRECTORY cpu_to_le32(0xC0000103) +#define STATUS_BAD_LOGON_SESSION_STATE cpu_to_le32(0xC0000104) +#define STATUS_LOGON_SESSION_COLLISION cpu_to_le32(0xC0000105) +#define STATUS_NAME_TOO_LONG cpu_to_le32(0xC0000106) +#define STATUS_FILES_OPEN cpu_to_le32(0xC0000107) +#define STATUS_CONNECTION_IN_USE cpu_to_le32(0xC0000108) +#define STATUS_MESSAGE_NOT_FOUND cpu_to_le32(0xC0000109) +#define STATUS_PROCESS_IS_TERMINATING cpu_to_le32(0xC000010A) +#define STATUS_INVALID_LOGON_TYPE cpu_to_le32(0xC000010B) +#define STATUS_NO_GUID_TRANSLATION cpu_to_le32(0xC000010C) +#define STATUS_CANNOT_IMPERSONATE cpu_to_le32(0xC000010D) +#define STATUS_IMAGE_ALREADY_LOADED cpu_to_le32(0xC000010E) +#define STATUS_ABIOS_NOT_PRESENT cpu_to_le32(0xC000010F) +#define STATUS_ABIOS_LID_NOT_EXIST cpu_to_le32(0xC0000110) +#define STATUS_ABIOS_LID_ALREADY_OWNED cpu_to_le32(0xC0000111) +#define STATUS_ABIOS_NOT_LID_OWNER cpu_to_le32(0xC0000112) +#define STATUS_ABIOS_INVALID_COMMAND cpu_to_le32(0xC0000113) +#define STATUS_ABIOS_INVALID_LID cpu_to_le32(0xC0000114) +#define STATUS_ABIOS_SELECTOR_NOT_AVAILABLE cpu_to_le32(0xC0000115) +#define STATUS_ABIOS_INVALID_SELECTOR cpu_to_le32(0xC0000116) +#define STATUS_NO_LDT cpu_to_le32(0xC0000117) +#define STATUS_INVALID_LDT_SIZE cpu_to_le32(0xC0000118) +#define STATUS_INVALID_LDT_OFFSET cpu_to_le32(0xC0000119) +#define STATUS_INVALID_LDT_DESCRIPTOR cpu_to_le32(0xC000011A) +#define STATUS_INVALID_IMAGE_NE_FORMAT cpu_to_le32(0xC000011B) +#define STATUS_RXACT_INVALID_STATE cpu_to_le32(0xC000011C) +#define STATUS_RXACT_COMMIT_FAILURE cpu_to_le32(0xC000011D) +#define STATUS_MAPPED_FILE_SIZE_ZERO cpu_to_le32(0xC000011E) +#define STATUS_TOO_MANY_OPENED_FILES cpu_to_le32(0xC000011F) +#define STATUS_CANCELLED cpu_to_le32(0xC0000120) +#define STATUS_CANNOT_DELETE cpu_to_le32(0xC0000121) +#define STATUS_INVALID_COMPUTER_NAME cpu_to_le32(0xC0000122) +#define STATUS_FILE_DELETED cpu_to_le32(0xC0000123) +#define STATUS_SPECIAL_ACCOUNT cpu_to_le32(0xC0000124) +#define STATUS_SPECIAL_GROUP cpu_to_le32(0xC0000125) +#define STATUS_SPECIAL_USER cpu_to_le32(0xC0000126) +#define STATUS_MEMBERS_PRIMARY_GROUP cpu_to_le32(0xC0000127) +#define STATUS_FILE_CLOSED cpu_to_le32(0xC0000128) +#define STATUS_TOO_MANY_THREADS cpu_to_le32(0xC0000129) +#define STATUS_THREAD_NOT_IN_PROCESS cpu_to_le32(0xC000012A) +#define STATUS_TOKEN_ALREADY_IN_USE cpu_to_le32(0xC000012B) +#define STATUS_PAGEFILE_QUOTA_EXCEEDED cpu_to_le32(0xC000012C) +#define STATUS_COMMITMENT_LIMIT cpu_to_le32(0xC000012D) +#define STATUS_INVALID_IMAGE_LE_FORMAT cpu_to_le32(0xC000012E) +#define STATUS_INVALID_IMAGE_NOT_MZ cpu_to_le32(0xC000012F) +#define STATUS_INVALID_IMAGE_PROTECT cpu_to_le32(0xC0000130) +#define STATUS_INVALID_IMAGE_WIN_16 cpu_to_le32(0xC0000131) +#define STATUS_LOGON_SERVER_CONFLICT cpu_to_le32(0xC0000132) +#define STATUS_TIME_DIFFERENCE_AT_DC cpu_to_le32(0xC0000133) +#define STATUS_SYNCHRONIZATION_REQUIRED cpu_to_le32(0xC0000134) +#define STATUS_DLL_NOT_FOUND cpu_to_le32(0xC0000135) +#define STATUS_OPEN_FAILED cpu_to_le32(0xC0000136) +#define STATUS_IO_PRIVILEGE_FAILED cpu_to_le32(0xC0000137) +#define STATUS_ORDINAL_NOT_FOUND cpu_to_le32(0xC0000138) +#define STATUS_ENTRYPOINT_NOT_FOUND cpu_to_le32(0xC0000139) +#define STATUS_CONTROL_C_EXIT cpu_to_le32(0xC000013A) +#define STATUS_LOCAL_DISCONNECT cpu_to_le32(0xC000013B) +#define STATUS_REMOTE_DISCONNECT cpu_to_le32(0xC000013C) +#define STATUS_REMOTE_RESOURCES cpu_to_le32(0xC000013D) +#define STATUS_LINK_FAILED cpu_to_le32(0xC000013E) +#define STATUS_LINK_TIMEOUT cpu_to_le32(0xC000013F) +#define STATUS_INVALID_CONNECTION cpu_to_le32(0xC0000140) +#define STATUS_INVALID_ADDRESS cpu_to_le32(0xC0000141) +#define STATUS_DLL_INIT_FAILED cpu_to_le32(0xC0000142) +#define STATUS_MISSING_SYSTEMFILE cpu_to_le32(0xC0000143) +#define STATUS_UNHANDLED_EXCEPTION cpu_to_le32(0xC0000144) +#define STATUS_APP_INIT_FAILURE cpu_to_le32(0xC0000145) +#define STATUS_PAGEFILE_CREATE_FAILED cpu_to_le32(0xC0000146) +#define STATUS_NO_PAGEFILE cpu_to_le32(0xC0000147) +#define STATUS_INVALID_LEVEL cpu_to_le32(0xC0000148) +#define STATUS_WRONG_PASSWORD_CORE cpu_to_le32(0xC0000149) +#define STATUS_ILLEGAL_FLOAT_CONTEXT cpu_to_le32(0xC000014A) +#define STATUS_PIPE_BROKEN cpu_to_le32(0xC000014B) +#define STATUS_REGISTRY_CORRUPT cpu_to_le32(0xC000014C) +#define STATUS_REGISTRY_IO_FAILED cpu_to_le32(0xC000014D) +#define STATUS_NO_EVENT_PAIR cpu_to_le32(0xC000014E) +#define STATUS_UNRECOGNIZED_VOLUME cpu_to_le32(0xC000014F) +#define STATUS_SERIAL_NO_DEVICE_INITED cpu_to_le32(0xC0000150) +#define STATUS_NO_SUCH_ALIAS cpu_to_le32(0xC0000151) +#define STATUS_MEMBER_NOT_IN_ALIAS cpu_to_le32(0xC0000152) +#define STATUS_MEMBER_IN_ALIAS cpu_to_le32(0xC0000153) +#define STATUS_ALIAS_EXISTS cpu_to_le32(0xC0000154) +#define STATUS_LOGON_NOT_GRANTED cpu_to_le32(0xC0000155) +#define STATUS_TOO_MANY_SECRETS cpu_to_le32(0xC0000156) +#define STATUS_SECRET_TOO_LONG cpu_to_le32(0xC0000157) +#define STATUS_INTERNAL_DB_ERROR cpu_to_le32(0xC0000158) +#define STATUS_FULLSCREEN_MODE cpu_to_le32(0xC0000159) +#define STATUS_TOO_MANY_CONTEXT_IDS cpu_to_le32(0xC000015A) +#define STATUS_LOGON_TYPE_NOT_GRANTED cpu_to_le32(0xC000015B) +#define STATUS_NOT_REGISTRY_FILE cpu_to_le32(0xC000015C) +#define STATUS_NT_CROSS_ENCRYPTION_REQUIRED cpu_to_le32(0xC000015D) +#define STATUS_DOMAIN_CTRLR_CONFIG_ERROR cpu_to_le32(0xC000015E) +#define STATUS_FT_MISSING_MEMBER cpu_to_le32(0xC000015F) +#define STATUS_ILL_FORMED_SERVICE_ENTRY cpu_to_le32(0xC0000160) +#define STATUS_ILLEGAL_CHARACTER cpu_to_le32(0xC0000161) +#define STATUS_UNMAPPABLE_CHARACTER cpu_to_le32(0xC0000162) +#define STATUS_UNDEFINED_CHARACTER cpu_to_le32(0xC0000163) +#define STATUS_FLOPPY_VOLUME cpu_to_le32(0xC0000164) +#define STATUS_FLOPPY_ID_MARK_NOT_FOUND cpu_to_le32(0xC0000165) +#define STATUS_FLOPPY_WRONG_CYLINDER cpu_to_le32(0xC0000166) +#define STATUS_FLOPPY_UNKNOWN_ERROR cpu_to_le32(0xC0000167) +#define STATUS_FLOPPY_BAD_REGISTERS cpu_to_le32(0xC0000168) +#define STATUS_DISK_RECALIBRATE_FAILED cpu_to_le32(0xC0000169) +#define STATUS_DISK_OPERATION_FAILED cpu_to_le32(0xC000016A) +#define STATUS_DISK_RESET_FAILED cpu_to_le32(0xC000016B) +#define STATUS_SHARED_IRQ_BUSY cpu_to_le32(0xC000016C) +#define STATUS_FT_ORPHANING cpu_to_le32(0xC000016D) +#define STATUS_BIOS_FAILED_TO_CONNECT_INTERRUPT cpu_to_le32(0xC000016E) +#define STATUS_PARTITION_FAILURE cpu_to_le32(0xC0000172) +#define STATUS_INVALID_BLOCK_LENGTH cpu_to_le32(0xC0000173) +#define STATUS_DEVICE_NOT_PARTITIONED cpu_to_le32(0xC0000174) +#define STATUS_UNABLE_TO_LOCK_MEDIA cpu_to_le32(0xC0000175) +#define STATUS_UNABLE_TO_UNLOAD_MEDIA cpu_to_le32(0xC0000176) +#define STATUS_EOM_OVERFLOW cpu_to_le32(0xC0000177) +#define STATUS_NO_MEDIA cpu_to_le32(0xC0000178) +#define STATUS_NO_SUCH_MEMBER cpu_to_le32(0xC000017A) +#define STATUS_INVALID_MEMBER cpu_to_le32(0xC000017B) +#define STATUS_KEY_DELETED cpu_to_le32(0xC000017C) +#define STATUS_NO_LOG_SPACE cpu_to_le32(0xC000017D) +#define STATUS_TOO_MANY_SIDS cpu_to_le32(0xC000017E) +#define STATUS_LM_CROSS_ENCRYPTION_REQUIRED cpu_to_le32(0xC000017F) +#define STATUS_KEY_HAS_CHILDREN cpu_to_le32(0xC0000180) +#define STATUS_CHILD_MUST_BE_VOLATILE cpu_to_le32(0xC0000181) +#define STATUS_DEVICE_CONFIGURATION_ERROR cpu_to_le32(0xC0000182) +#define STATUS_DRIVER_INTERNAL_ERROR cpu_to_le32(0xC0000183) +#define STATUS_INVALID_DEVICE_STATE cpu_to_le32(0xC0000184) +#define STATUS_IO_DEVICE_ERROR cpu_to_le32(0xC0000185) +#define STATUS_DEVICE_PROTOCOL_ERROR cpu_to_le32(0xC0000186) +#define STATUS_BACKUP_CONTROLLER cpu_to_le32(0xC0000187) +#define STATUS_LOG_FILE_FULL cpu_to_le32(0xC0000188) +#define STATUS_TOO_LATE cpu_to_le32(0xC0000189) +#define STATUS_NO_TRUST_LSA_SECRET cpu_to_le32(0xC000018A) +#define STATUS_NO_TRUST_SAM_ACCOUNT cpu_to_le32(0xC000018B) +#define STATUS_TRUSTED_DOMAIN_FAILURE cpu_to_le32(0xC000018C) +#define STATUS_TRUSTED_RELATIONSHIP_FAILURE cpu_to_le32(0xC000018D) +#define STATUS_EVENTLOG_FILE_CORRUPT cpu_to_le32(0xC000018E) +#define STATUS_EVENTLOG_CANT_START cpu_to_le32(0xC000018F) +#define STATUS_TRUST_FAILURE cpu_to_le32(0xC0000190) +#define STATUS_MUTANT_LIMIT_EXCEEDED cpu_to_le32(0xC0000191) +#define STATUS_NETLOGON_NOT_STARTED cpu_to_le32(0xC0000192) +#define STATUS_ACCOUNT_EXPIRED cpu_to_le32(0xC0000193) +#define STATUS_POSSIBLE_DEADLOCK cpu_to_le32(0xC0000194) +#define STATUS_NETWORK_CREDENTIAL_CONFLICT cpu_to_le32(0xC0000195) +#define STATUS_REMOTE_SESSION_LIMIT cpu_to_le32(0xC0000196) +#define STATUS_EVENTLOG_FILE_CHANGED cpu_to_le32(0xC0000197) +#define STATUS_NOLOGON_INTERDOMAIN_TRUST_ACCOUNT cpu_to_le32(0xC0000198) +#define STATUS_NOLOGON_WORKSTATION_TRUST_ACCOUNT cpu_to_le32(0xC0000199) +#define STATUS_NOLOGON_SERVER_TRUST_ACCOUNT cpu_to_le32(0xC000019A) +#define STATUS_DOMAIN_TRUST_INCONSISTENT cpu_to_le32(0xC000019B) +#define STATUS_FS_DRIVER_REQUIRED cpu_to_le32(0xC000019C) +#define STATUS_IMAGE_ALREADY_LOADED_AS_DLL cpu_to_le32(0xC000019D) +#define STATUS_NETWORK_OPEN_RESTRICTION cpu_to_le32(0xC0000201) +#define STATUS_NO_USER_SESSION_KEY cpu_to_le32(0xC0000202) +#define STATUS_USER_SESSION_DELETED cpu_to_le32(0xC0000203) +#define STATUS_RESOURCE_LANG_NOT_FOUND cpu_to_le32(0xC0000204) +#define STATUS_INSUFF_SERVER_RESOURCES cpu_to_le32(0xC0000205) +#define STATUS_INVALID_BUFFER_SIZE cpu_to_le32(0xC0000206) +#define STATUS_INVALID_ADDRESS_COMPONENT cpu_to_le32(0xC0000207) +#define STATUS_INVALID_ADDRESS_WILDCARD cpu_to_le32(0xC0000208) +#define STATUS_TOO_MANY_ADDRESSES cpu_to_le32(0xC0000209) +#define STATUS_ADDRESS_ALREADY_EXISTS cpu_to_le32(0xC000020A) +#define STATUS_ADDRESS_CLOSED cpu_to_le32(0xC000020B) +#define STATUS_CONNECTION_DISCONNECTED cpu_to_le32(0xC000020C) +#define STATUS_CONNECTION_RESET cpu_to_le32(0xC000020D) +#define STATUS_TOO_MANY_NODES cpu_to_le32(0xC000020E) +#define STATUS_TRANSACTION_ABORTED cpu_to_le32(0xC000020F) +#define STATUS_TRANSACTION_TIMED_OUT cpu_to_le32(0xC0000210) +#define STATUS_TRANSACTION_NO_RELEASE cpu_to_le32(0xC0000211) +#define STATUS_TRANSACTION_NO_MATCH cpu_to_le32(0xC0000212) +#define STATUS_TRANSACTION_RESPONDED cpu_to_le32(0xC0000213) +#define STATUS_TRANSACTION_INVALID_ID cpu_to_le32(0xC0000214) +#define STATUS_TRANSACTION_INVALID_TYPE cpu_to_le32(0xC0000215) +#define STATUS_NOT_SERVER_SESSION cpu_to_le32(0xC0000216) +#define STATUS_NOT_CLIENT_SESSION cpu_to_le32(0xC0000217) +#define STATUS_CANNOT_LOAD_REGISTRY_FILE cpu_to_le32(0xC0000218) +#define STATUS_DEBUG_ATTACH_FAILED cpu_to_le32(0xC0000219) +#define STATUS_SYSTEM_PROCESS_TERMINATED cpu_to_le32(0xC000021A) +#define STATUS_DATA_NOT_ACCEPTED cpu_to_le32(0xC000021B) +#define STATUS_NO_BROWSER_SERVERS_FOUND cpu_to_le32(0xC000021C) +#define STATUS_VDM_HARD_ERROR cpu_to_le32(0xC000021D) +#define STATUS_DRIVER_CANCEL_TIMEOUT cpu_to_le32(0xC000021E) +#define STATUS_REPLY_MESSAGE_MISMATCH cpu_to_le32(0xC000021F) +#define STATUS_MAPPED_ALIGNMENT cpu_to_le32(0xC0000220) +#define STATUS_IMAGE_CHECKSUM_MISMATCH cpu_to_le32(0xC0000221) +#define STATUS_LOST_WRITEBEHIND_DATA cpu_to_le32(0xC0000222) +#define STATUS_CLIENT_SERVER_PARAMETERS_INVALID cpu_to_le32(0xC0000223) +#define STATUS_PASSWORD_MUST_CHANGE cpu_to_le32(0xC0000224) +#define STATUS_NOT_FOUND cpu_to_le32(0xC0000225) +#define STATUS_NOT_TINY_STREAM cpu_to_le32(0xC0000226) +#define STATUS_RECOVERY_FAILURE cpu_to_le32(0xC0000227) +#define STATUS_STACK_OVERFLOW_READ cpu_to_le32(0xC0000228) +#define STATUS_FAIL_CHECK cpu_to_le32(0xC0000229) +#define STATUS_DUPLICATE_OBJECTID cpu_to_le32(0xC000022A) +#define STATUS_OBJECTID_EXISTS cpu_to_le32(0xC000022B) +#define STATUS_CONVERT_TO_LARGE cpu_to_le32(0xC000022C) +#define STATUS_RETRY cpu_to_le32(0xC000022D) +#define STATUS_FOUND_OUT_OF_SCOPE cpu_to_le32(0xC000022E) +#define STATUS_ALLOCATE_BUCKET cpu_to_le32(0xC000022F) +#define STATUS_PROPSET_NOT_FOUND cpu_to_le32(0xC0000230) +#define STATUS_MARSHALL_OVERFLOW cpu_to_le32(0xC0000231) +#define STATUS_INVALID_VARIANT cpu_to_le32(0xC0000232) +#define STATUS_DOMAIN_CONTROLLER_NOT_FOUND cpu_to_le32(0xC0000233) +#define STATUS_ACCOUNT_LOCKED_OUT cpu_to_le32(0xC0000234) +#define STATUS_HANDLE_NOT_CLOSABLE cpu_to_le32(0xC0000235) +#define STATUS_CONNECTION_REFUSED cpu_to_le32(0xC0000236) +#define STATUS_GRACEFUL_DISCONNECT cpu_to_le32(0xC0000237) +#define STATUS_ADDRESS_ALREADY_ASSOCIATED cpu_to_le32(0xC0000238) +#define STATUS_ADDRESS_NOT_ASSOCIATED cpu_to_le32(0xC0000239) +#define STATUS_CONNECTION_INVALID cpu_to_le32(0xC000023A) +#define STATUS_CONNECTION_ACTIVE cpu_to_le32(0xC000023B) +#define STATUS_NETWORK_UNREACHABLE cpu_to_le32(0xC000023C) +#define STATUS_HOST_UNREACHABLE cpu_to_le32(0xC000023D) +#define STATUS_PROTOCOL_UNREACHABLE cpu_to_le32(0xC000023E) +#define STATUS_PORT_UNREACHABLE cpu_to_le32(0xC000023F) +#define STATUS_REQUEST_ABORTED cpu_to_le32(0xC0000240) +#define STATUS_CONNECTION_ABORTED cpu_to_le32(0xC0000241) +#define STATUS_BAD_COMPRESSION_BUFFER cpu_to_le32(0xC0000242) +#define STATUS_USER_MAPPED_FILE cpu_to_le32(0xC0000243) +#define STATUS_AUDIT_FAILED cpu_to_le32(0xC0000244) +#define STATUS_TIMER_RESOLUTION_NOT_SET cpu_to_le32(0xC0000245) +#define STATUS_CONNECTION_COUNT_LIMIT cpu_to_le32(0xC0000246) +#define STATUS_LOGIN_TIME_RESTRICTION cpu_to_le32(0xC0000247) +#define STATUS_LOGIN_WKSTA_RESTRICTION cpu_to_le32(0xC0000248) +#define STATUS_IMAGE_MP_UP_MISMATCH cpu_to_le32(0xC0000249) +#define STATUS_INSUFFICIENT_LOGON_INFO cpu_to_le32(0xC0000250) +#define STATUS_BAD_DLL_ENTRYPOINT cpu_to_le32(0xC0000251) +#define STATUS_BAD_SERVICE_ENTRYPOINT cpu_to_le32(0xC0000252) +#define STATUS_LPC_REPLY_LOST cpu_to_le32(0xC0000253) +#define STATUS_IP_ADDRESS_CONFLICT1 cpu_to_le32(0xC0000254) +#define STATUS_IP_ADDRESS_CONFLICT2 cpu_to_le32(0xC0000255) +#define STATUS_REGISTRY_QUOTA_LIMIT cpu_to_le32(0xC0000256) +#define STATUS_PATH_NOT_COVERED cpu_to_le32(0xC0000257) +#define STATUS_NO_CALLBACK_ACTIVE cpu_to_le32(0xC0000258) +#define STATUS_LICENSE_QUOTA_EXCEEDED cpu_to_le32(0xC0000259) +#define STATUS_PWD_TOO_SHORT cpu_to_le32(0xC000025A) +#define STATUS_PWD_TOO_RECENT cpu_to_le32(0xC000025B) +#define STATUS_PWD_HISTORY_CONFLICT cpu_to_le32(0xC000025C) +#define STATUS_PLUGPLAY_NO_DEVICE cpu_to_le32(0xC000025E) +#define STATUS_UNSUPPORTED_COMPRESSION cpu_to_le32(0xC000025F) +#define STATUS_INVALID_HW_PROFILE cpu_to_le32(0xC0000260) +#define STATUS_INVALID_PLUGPLAY_DEVICE_PATH cpu_to_le32(0xC0000261) +#define STATUS_DRIVER_ORDINAL_NOT_FOUND cpu_to_le32(0xC0000262) +#define STATUS_DRIVER_ENTRYPOINT_NOT_FOUND cpu_to_le32(0xC0000263) +#define STATUS_RESOURCE_NOT_OWNED cpu_to_le32(0xC0000264) +#define STATUS_TOO_MANY_LINKS cpu_to_le32(0xC0000265) +#define STATUS_QUOTA_LIST_INCONSISTENT cpu_to_le32(0xC0000266) +#define STATUS_FILE_IS_OFFLINE cpu_to_le32(0xC0000267) +#define STATUS_EVALUATION_EXPIRATION cpu_to_le32(0xC0000268) +#define STATUS_ILLEGAL_DLL_RELOCATION cpu_to_le32(0xC0000269) +#define STATUS_LICENSE_VIOLATION cpu_to_le32(0xC000026A) +#define STATUS_DLL_INIT_FAILED_LOGOFF cpu_to_le32(0xC000026B) +#define STATUS_DRIVER_UNABLE_TO_LOAD cpu_to_le32(0xC000026C) +#define STATUS_DFS_UNAVAILABLE cpu_to_le32(0xC000026D) +#define STATUS_VOLUME_DISMOUNTED cpu_to_le32(0xC000026E) +#define STATUS_WX86_INTERNAL_ERROR cpu_to_le32(0xC000026F) +#define STATUS_WX86_FLOAT_STACK_CHECK cpu_to_le32(0xC0000270) +#define STATUS_VALIDATE_CONTINUE cpu_to_le32(0xC0000271) +#define STATUS_NO_MATCH cpu_to_le32(0xC0000272) +#define STATUS_NO_MORE_MATCHES cpu_to_le32(0xC0000273) +#define STATUS_NOT_A_REPARSE_POINT cpu_to_le32(0xC0000275) +#define STATUS_IO_REPARSE_TAG_INVALID cpu_to_le32(0xC0000276) +#define STATUS_IO_REPARSE_TAG_MISMATCH cpu_to_le32(0xC0000277) +#define STATUS_IO_REPARSE_DATA_INVALID cpu_to_le32(0xC0000278) +#define STATUS_IO_REPARSE_TAG_NOT_HANDLED cpu_to_le32(0xC0000279) +#define STATUS_REPARSE_POINT_NOT_RESOLVED cpu_to_le32(0xC0000280) +#define STATUS_DIRECTORY_IS_A_REPARSE_POINT cpu_to_le32(0xC0000281) +#define STATUS_RANGE_LIST_CONFLICT cpu_to_le32(0xC0000282) +#define STATUS_SOURCE_ELEMENT_EMPTY cpu_to_le32(0xC0000283) +#define STATUS_DESTINATION_ELEMENT_FULL cpu_to_le32(0xC0000284) +#define STATUS_ILLEGAL_ELEMENT_ADDRESS cpu_to_le32(0xC0000285) +#define STATUS_MAGAZINE_NOT_PRESENT cpu_to_le32(0xC0000286) +#define STATUS_REINITIALIZATION_NEEDED cpu_to_le32(0xC0000287) +#define STATUS_ENCRYPTION_FAILED cpu_to_le32(0xC000028A) +#define STATUS_DECRYPTION_FAILED cpu_to_le32(0xC000028B) +#define STATUS_RANGE_NOT_FOUND cpu_to_le32(0xC000028C) +#define STATUS_NO_RECOVERY_POLICY cpu_to_le32(0xC000028D) +#define STATUS_NO_EFS cpu_to_le32(0xC000028E) +#define STATUS_WRONG_EFS cpu_to_le32(0xC000028F) +#define STATUS_NO_USER_KEYS cpu_to_le32(0xC0000290) +#define STATUS_FILE_NOT_ENCRYPTED cpu_to_le32(0xC0000291) +#define STATUS_NOT_EXPORT_FORMAT cpu_to_le32(0xC0000292) +#define STATUS_FILE_ENCRYPTED cpu_to_le32(0xC0000293) +#define STATUS_WMI_GUID_NOT_FOUND cpu_to_le32(0xC0000295) +#define STATUS_WMI_INSTANCE_NOT_FOUND cpu_to_le32(0xC0000296) +#define STATUS_WMI_ITEMID_NOT_FOUND cpu_to_le32(0xC0000297) +#define STATUS_WMI_TRY_AGAIN cpu_to_le32(0xC0000298) +#define STATUS_SHARED_POLICY cpu_to_le32(0xC0000299) +#define STATUS_POLICY_OBJECT_NOT_FOUND cpu_to_le32(0xC000029A) +#define STATUS_POLICY_ONLY_IN_DS cpu_to_le32(0xC000029B) +#define STATUS_VOLUME_NOT_UPGRADED cpu_to_le32(0xC000029C) +#define STATUS_REMOTE_STORAGE_NOT_ACTIVE cpu_to_le32(0xC000029D) +#define STATUS_REMOTE_STORAGE_MEDIA_ERROR cpu_to_le32(0xC000029E) +#define STATUS_NO_TRACKING_SERVICE cpu_to_le32(0xC000029F) +#define STATUS_SERVER_SID_MISMATCH cpu_to_le32(0xC00002A0) +#define STATUS_DS_NO_ATTRIBUTE_OR_VALUE cpu_to_le32(0xC00002A1) +#define STATUS_DS_INVALID_ATTRIBUTE_SYNTAX cpu_to_le32(0xC00002A2) +#define STATUS_DS_ATTRIBUTE_TYPE_UNDEFINED cpu_to_le32(0xC00002A3) +#define STATUS_DS_ATTRIBUTE_OR_VALUE_EXISTS cpu_to_le32(0xC00002A4) +#define STATUS_DS_BUSY cpu_to_le32(0xC00002A5) +#define STATUS_DS_UNAVAILABLE cpu_to_le32(0xC00002A6) +#define STATUS_DS_NO_RIDS_ALLOCATED cpu_to_le32(0xC00002A7) +#define STATUS_DS_NO_MORE_RIDS cpu_to_le32(0xC00002A8) +#define STATUS_DS_INCORRECT_ROLE_OWNER cpu_to_le32(0xC00002A9) +#define STATUS_DS_RIDMGR_INIT_ERROR cpu_to_le32(0xC00002AA) +#define STATUS_DS_OBJ_CLASS_VIOLATION cpu_to_le32(0xC00002AB) +#define STATUS_DS_CANT_ON_NON_LEAF cpu_to_le32(0xC00002AC) +#define STATUS_DS_CANT_ON_RDN cpu_to_le32(0xC00002AD) +#define STATUS_DS_CANT_MOD_OBJ_CLASS cpu_to_le32(0xC00002AE) +#define STATUS_DS_CROSS_DOM_MOVE_FAILED cpu_to_le32(0xC00002AF) +#define STATUS_DS_GC_NOT_AVAILABLE cpu_to_le32(0xC00002B0) +#define STATUS_DIRECTORY_SERVICE_REQUIRED cpu_to_le32(0xC00002B1) +#define STATUS_REPARSE_ATTRIBUTE_CONFLICT cpu_to_le32(0xC00002B2) +#define STATUS_CANT_ENABLE_DENY_ONLY cpu_to_le32(0xC00002B3) +#define STATUS_FLOAT_MULTIPLE_FAULTS cpu_to_le32(0xC00002B4) +#define STATUS_FLOAT_MULTIPLE_TRAPS cpu_to_le32(0xC00002B5) +#define STATUS_DEVICE_REMOVED cpu_to_le32(0xC00002B6) +#define STATUS_JOURNAL_DELETE_IN_PROGRESS cpu_to_le32(0xC00002B7) +#define STATUS_JOURNAL_NOT_ACTIVE cpu_to_le32(0xC00002B8) +#define STATUS_NOINTERFACE cpu_to_le32(0xC00002B9) +#define STATUS_DS_ADMIN_LIMIT_EXCEEDED cpu_to_le32(0xC00002C1) +#define STATUS_DRIVER_FAILED_SLEEP cpu_to_le32(0xC00002C2) +#define STATUS_MUTUAL_AUTHENTICATION_FAILED cpu_to_le32(0xC00002C3) +#define STATUS_CORRUPT_SYSTEM_FILE cpu_to_le32(0xC00002C4) +#define STATUS_DATATYPE_MISALIGNMENT_ERROR cpu_to_le32(0xC00002C5) +#define STATUS_WMI_READ_ONLY cpu_to_le32(0xC00002C6) +#define STATUS_WMI_SET_FAILURE cpu_to_le32(0xC00002C7) +#define STATUS_COMMITMENT_MINIMUM cpu_to_le32(0xC00002C8) +#define STATUS_REG_NAT_CONSUMPTION cpu_to_le32(0xC00002C9) +#define STATUS_TRANSPORT_FULL cpu_to_le32(0xC00002CA) +#define STATUS_DS_SAM_INIT_FAILURE cpu_to_le32(0xC00002CB) +#define STATUS_ONLY_IF_CONNECTED cpu_to_le32(0xC00002CC) +#define STATUS_DS_SENSITIVE_GROUP_VIOLATION cpu_to_le32(0xC00002CD) +#define STATUS_PNP_RESTART_ENUMERATION cpu_to_le32(0xC00002CE) +#define STATUS_JOURNAL_ENTRY_DELETED cpu_to_le32(0xC00002CF) +#define STATUS_DS_CANT_MOD_PRIMARYGROUPID cpu_to_le32(0xC00002D0) +#define STATUS_SYSTEM_IMAGE_BAD_SIGNATURE cpu_to_le32(0xC00002D1) +#define STATUS_PNP_REBOOT_REQUIRED cpu_to_le32(0xC00002D2) +#define STATUS_POWER_STATE_INVALID cpu_to_le32(0xC00002D3) +#define STATUS_DS_INVALID_GROUP_TYPE cpu_to_le32(0xC00002D4) +#define STATUS_DS_NO_NEST_GLOBALGROUP_IN_MIXEDDOMAIN cpu_to_le32(0xC00002D5) +#define STATUS_DS_NO_NEST_LOCALGROUP_IN_MIXEDDOMAIN cpu_to_le32(0xC00002D6) +#define STATUS_DS_GLOBAL_CANT_HAVE_LOCAL_MEMBER cpu_to_le32(0xC00002D7) +#define STATUS_DS_GLOBAL_CANT_HAVE_UNIVERSAL_MEMBER cpu_to_le32(0xC00002D8) +#define STATUS_DS_UNIVERSAL_CANT_HAVE_LOCAL_MEMBER cpu_to_le32(0xC00002D9) +#define STATUS_DS_GLOBAL_CANT_HAVE_CROSSDOMAIN_MEMBER cpu_to_le32(0xC00002DA) +#define STATUS_DS_LOCAL_CANT_HAVE_CROSSDOMAIN_LOCAL_MEMBER \ + cpu_to_le32(0xC00002DB) +#define STATUS_DS_HAVE_PRIMARY_MEMBERS cpu_to_le32(0xC00002DC) +#define STATUS_WMI_NOT_SUPPORTED cpu_to_le32(0xC00002DD) +#define STATUS_INSUFFICIENT_POWER cpu_to_le32(0xC00002DE) +#define STATUS_SAM_NEED_BOOTKEY_PASSWORD cpu_to_le32(0xC00002DF) +#define STATUS_SAM_NEED_BOOTKEY_FLOPPY cpu_to_le32(0xC00002E0) +#define STATUS_DS_CANT_START cpu_to_le32(0xC00002E1) +#define STATUS_DS_INIT_FAILURE cpu_to_le32(0xC00002E2) +#define STATUS_SAM_INIT_FAILURE cpu_to_le32(0xC00002E3) +#define STATUS_DS_GC_REQUIRED cpu_to_le32(0xC00002E4) +#define STATUS_DS_LOCAL_MEMBER_OF_LOCAL_ONLY cpu_to_le32(0xC00002E5) +#define STATUS_DS_NO_FPO_IN_UNIVERSAL_GROUPS cpu_to_le32(0xC00002E6) +#define STATUS_DS_MACHINE_ACCOUNT_QUOTA_EXCEEDED cpu_to_le32(0xC00002E7) +#define STATUS_MULTIPLE_FAULT_VIOLATION cpu_to_le32(0xC00002E8) +#define STATUS_CURRENT_DOMAIN_NOT_ALLOWED cpu_to_le32(0xC00002E9) +#define STATUS_CANNOT_MAKE cpu_to_le32(0xC00002EA) +#define STATUS_SYSTEM_SHUTDOWN cpu_to_le32(0xC00002EB) +#define STATUS_DS_INIT_FAILURE_CONSOLE cpu_to_le32(0xC00002EC) +#define STATUS_DS_SAM_INIT_FAILURE_CONSOLE cpu_to_le32(0xC00002ED) +#define STATUS_UNFINISHED_CONTEXT_DELETED cpu_to_le32(0xC00002EE) +#define STATUS_NO_TGT_REPLY cpu_to_le32(0xC00002EF) +#define STATUS_OBJECTID_NOT_FOUND cpu_to_le32(0xC00002F0) +#define STATUS_NO_IP_ADDRESSES cpu_to_le32(0xC00002F1) +#define STATUS_WRONG_CREDENTIAL_HANDLE cpu_to_le32(0xC00002F2) +#define STATUS_CRYPTO_SYSTEM_INVALID cpu_to_le32(0xC00002F3) +#define STATUS_MAX_REFERRALS_EXCEEDED cpu_to_le32(0xC00002F4) +#define STATUS_MUST_BE_KDC cpu_to_le32(0xC00002F5) +#define STATUS_STRONG_CRYPTO_NOT_SUPPORTED cpu_to_le32(0xC00002F6) +#define STATUS_TOO_MANY_PRINCIPALS cpu_to_le32(0xC00002F7) +#define STATUS_NO_PA_DATA cpu_to_le32(0xC00002F8) +#define STATUS_PKINIT_NAME_MISMATCH cpu_to_le32(0xC00002F9) +#define STATUS_SMARTCARD_LOGON_REQUIRED cpu_to_le32(0xC00002FA) +#define STATUS_KDC_INVALID_REQUEST cpu_to_le32(0xC00002FB) +#define STATUS_KDC_UNABLE_TO_REFER cpu_to_le32(0xC00002FC) +#define STATUS_KDC_UNKNOWN_ETYPE cpu_to_le32(0xC00002FD) +#define STATUS_SHUTDOWN_IN_PROGRESS cpu_to_le32(0xC00002FE) +#define STATUS_SERVER_SHUTDOWN_IN_PROGRESS cpu_to_le32(0xC00002FF) +#define STATUS_NOT_SUPPORTED_ON_SBS cpu_to_le32(0xC0000300) +#define STATUS_WMI_GUID_DISCONNECTED cpu_to_le32(0xC0000301) +#define STATUS_WMI_ALREADY_DISABLED cpu_to_le32(0xC0000302) +#define STATUS_WMI_ALREADY_ENABLED cpu_to_le32(0xC0000303) +#define STATUS_MFT_TOO_FRAGMENTED cpu_to_le32(0xC0000304) +#define STATUS_COPY_PROTECTION_FAILURE cpu_to_le32(0xC0000305) +#define STATUS_CSS_AUTHENTICATION_FAILURE cpu_to_le32(0xC0000306) +#define STATUS_CSS_KEY_NOT_PRESENT cpu_to_le32(0xC0000307) +#define STATUS_CSS_KEY_NOT_ESTABLISHED cpu_to_le32(0xC0000308) +#define STATUS_CSS_SCRAMBLED_SECTOR cpu_to_le32(0xC0000309) +#define STATUS_CSS_REGION_MISMATCH cpu_to_le32(0xC000030A) +#define STATUS_CSS_RESETS_EXHAUSTED cpu_to_le32(0xC000030B) +#define STATUS_PKINIT_FAILURE cpu_to_le32(0xC0000320) +#define STATUS_SMARTCARD_SUBSYSTEM_FAILURE cpu_to_le32(0xC0000321) +#define STATUS_NO_KERB_KEY cpu_to_le32(0xC0000322) +#define STATUS_HOST_DOWN cpu_to_le32(0xC0000350) +#define STATUS_UNSUPPORTED_PREAUTH cpu_to_le32(0xC0000351) +#define STATUS_EFS_ALG_BLOB_TOO_BIG cpu_to_le32(0xC0000352) +#define STATUS_PORT_NOT_SET cpu_to_le32(0xC0000353) +#define STATUS_DEBUGGER_INACTIVE cpu_to_le32(0xC0000354) +#define STATUS_DS_VERSION_CHECK_FAILURE cpu_to_le32(0xC0000355) +#define STATUS_AUDITING_DISABLED cpu_to_le32(0xC0000356) +#define STATUS_PRENT4_MACHINE_ACCOUNT cpu_to_le32(0xC0000357) +#define STATUS_DS_AG_CANT_HAVE_UNIVERSAL_MEMBER cpu_to_le32(0xC0000358) +#define STATUS_INVALID_IMAGE_WIN_32 cpu_to_le32(0xC0000359) +#define STATUS_INVALID_IMAGE_WIN_64 cpu_to_le32(0xC000035A) +#define STATUS_BAD_BINDINGS cpu_to_le32(0xC000035B) +#define STATUS_NETWORK_SESSION_EXPIRED cpu_to_le32(0xC000035C) +#define STATUS_APPHELP_BLOCK cpu_to_le32(0xC000035D) +#define STATUS_ALL_SIDS_FILTERED cpu_to_le32(0xC000035E) +#define STATUS_NOT_SAFE_MODE_DRIVER cpu_to_le32(0xC000035F) +#define STATUS_ACCESS_DISABLED_BY_POLICY_DEFAULT cpu_to_le32(0xC0000361) +#define STATUS_ACCESS_DISABLED_BY_POLICY_PATH cpu_to_le32(0xC0000362) +#define STATUS_ACCESS_DISABLED_BY_POLICY_PUBLISHER cpu_to_le32(0xC0000363) +#define STATUS_ACCESS_DISABLED_BY_POLICY_OTHER cpu_to_le32(0xC0000364) +#define STATUS_FAILED_DRIVER_ENTRY cpu_to_le32(0xC0000365) +#define STATUS_DEVICE_ENUMERATION_ERROR cpu_to_le32(0xC0000366) +#define STATUS_MOUNT_POINT_NOT_RESOLVED cpu_to_le32(0xC0000368) +#define STATUS_INVALID_DEVICE_OBJECT_PARAMETER cpu_to_le32(0xC0000369) +#define STATUS_MCA_OCCURRED cpu_to_le32(0xC000036A) +#define STATUS_DRIVER_BLOCKED_CRITICAL cpu_to_le32(0xC000036B) +#define STATUS_DRIVER_BLOCKED cpu_to_le32(0xC000036C) +#define STATUS_DRIVER_DATABASE_ERROR cpu_to_le32(0xC000036D) +#define STATUS_SYSTEM_HIVE_TOO_LARGE cpu_to_le32(0xC000036E) +#define STATUS_INVALID_IMPORT_OF_NON_DLL cpu_to_le32(0xC000036F) +#define STATUS_NO_SECRETS cpu_to_le32(0xC0000371) +#define STATUS_ACCESS_DISABLED_NO_SAFER_UI_BY_POLICY cpu_to_le32(0xC0000372) +#define STATUS_FAILED_STACK_SWITCH cpu_to_le32(0xC0000373) +#define STATUS_HEAP_CORRUPTION cpu_to_le32(0xC0000374) +#define STATUS_SMARTCARD_WRONG_PIN cpu_to_le32(0xC0000380) +#define STATUS_SMARTCARD_CARD_BLOCKED cpu_to_le32(0xC0000381) +#define STATUS_SMARTCARD_CARD_NOT_AUTHENTICATED cpu_to_le32(0xC0000382) +#define STATUS_SMARTCARD_NO_CARD cpu_to_le32(0xC0000383) +#define STATUS_SMARTCARD_NO_KEY_CONTAINER cpu_to_le32(0xC0000384) +#define STATUS_SMARTCARD_NO_CERTIFICATE cpu_to_le32(0xC0000385) +#define STATUS_SMARTCARD_NO_KEYSET cpu_to_le32(0xC0000386) +#define STATUS_SMARTCARD_IO_ERROR cpu_to_le32(0xC0000387) +#define STATUS_DOWNGRADE_DETECTED cpu_to_le32(0xC0000388) +#define STATUS_SMARTCARD_CERT_REVOKED cpu_to_le32(0xC0000389) +#define STATUS_ISSUING_CA_UNTRUSTED cpu_to_le32(0xC000038A) +#define STATUS_REVOCATION_OFFLINE_C cpu_to_le32(0xC000038B) +#define STATUS_PKINIT_CLIENT_FAILURE cpu_to_le32(0xC000038C) +#define STATUS_SMARTCARD_CERT_EXPIRED cpu_to_le32(0xC000038D) +#define STATUS_DRIVER_FAILED_PRIOR_UNLOAD cpu_to_le32(0xC000038E) +#define STATUS_SMARTCARD_SILENT_CONTEXT cpu_to_le32(0xC000038F) +#define STATUS_PER_USER_TRUST_QUOTA_EXCEEDED cpu_to_le32(0xC0000401) +#define STATUS_ALL_USER_TRUST_QUOTA_EXCEEDED cpu_to_le32(0xC0000402) +#define STATUS_USER_DELETE_TRUST_QUOTA_EXCEEDED cpu_to_le32(0xC0000403) +#define STATUS_DS_NAME_NOT_UNIQUE cpu_to_le32(0xC0000404) +#define STATUS_DS_DUPLICATE_ID_FOUND cpu_to_le32(0xC0000405) +#define STATUS_DS_GROUP_CONVERSION_ERROR cpu_to_le32(0xC0000406) +#define STATUS_VOLSNAP_PREPARE_HIBERNATE cpu_to_le32(0xC0000407) +#define STATUS_USER2USER_REQUIRED cpu_to_le32(0xC0000408) +#define STATUS_STACK_BUFFER_OVERRUN cpu_to_le32(0xC0000409) +#define STATUS_NO_S4U_PROT_SUPPORT cpu_to_le32(0xC000040A) +#define STATUS_CROSSREALM_DELEGATION_FAILURE cpu_to_le32(0xC000040B) +#define STATUS_REVOCATION_OFFLINE_KDC cpu_to_le32(0xC000040C) +#define STATUS_ISSUING_CA_UNTRUSTED_KDC cpu_to_le32(0xC000040D) +#define STATUS_KDC_CERT_EXPIRED cpu_to_le32(0xC000040E) +#define STATUS_KDC_CERT_REVOKED cpu_to_le32(0xC000040F) +#define STATUS_PARAMETER_QUOTA_EXCEEDED cpu_to_le32(0xC0000410) +#define STATUS_HIBERNATION_FAILURE cpu_to_le32(0xC0000411) +#define STATUS_DELAY_LOAD_FAILED cpu_to_le32(0xC0000412) +#define STATUS_AUTHENTICATION_FIREWALL_FAILED cpu_to_le32(0xC0000413) +#define STATUS_VDM_DISALLOWED cpu_to_le32(0xC0000414) +#define STATUS_HUNG_DISPLAY_DRIVER_THREAD cpu_to_le32(0xC0000415) +#define STATUS_INSUFFICIENT_RESOURCE_FOR_SPECIFIED_SHARED_SECTION_SIZE \ + cpu_to_le32(0xC0000416) +#define STATUS_INVALID_CRUNTIME_PARAMETER cpu_to_le32(0xC0000417) +#define STATUS_NTLM_BLOCKED cpu_to_le32(0xC0000418) +#define STATUS_ASSERTION_FAILURE cpu_to_le32(0xC0000420) +#define STATUS_VERIFIER_STOP cpu_to_le32(0xC0000421) +#define STATUS_CALLBACK_POP_STACK cpu_to_le32(0xC0000423) +#define STATUS_INCOMPATIBLE_DRIVER_BLOCKED cpu_to_le32(0xC0000424) +#define STATUS_HIVE_UNLOADED cpu_to_le32(0xC0000425) +#define STATUS_COMPRESSION_DISABLED cpu_to_le32(0xC0000426) +#define STATUS_FILE_SYSTEM_LIMITATION cpu_to_le32(0xC0000427) +#define STATUS_INVALID_IMAGE_HASH cpu_to_le32(0xC0000428) +#define STATUS_NOT_CAPABLE cpu_to_le32(0xC0000429) +#define STATUS_REQUEST_OUT_OF_SEQUENCE cpu_to_le32(0xC000042A) +#define STATUS_IMPLEMENTATION_LIMIT cpu_to_le32(0xC000042B) +#define STATUS_ELEVATION_REQUIRED cpu_to_le32(0xC000042C) +#define STATUS_BEYOND_VDL cpu_to_le32(0xC0000432) +#define STATUS_ENCOUNTERED_WRITE_IN_PROGRESS cpu_to_le32(0xC0000433) +#define STATUS_PTE_CHANGED cpu_to_le32(0xC0000434) +#define STATUS_PURGE_FAILED cpu_to_le32(0xC0000435) +#define STATUS_CRED_REQUIRES_CONFIRMATION cpu_to_le32(0xC0000440) +#define STATUS_CS_ENCRYPTION_INVALID_SERVER_RESPONSE cpu_to_le32(0xC0000441) +#define STATUS_CS_ENCRYPTION_UNSUPPORTED_SERVER cpu_to_le32(0xC0000442) +#define STATUS_CS_ENCRYPTION_EXISTING_ENCRYPTED_FILE cpu_to_le32(0xC0000443) +#define STATUS_CS_ENCRYPTION_NEW_ENCRYPTED_FILE cpu_to_le32(0xC0000444) +#define STATUS_CS_ENCRYPTION_FILE_NOT_CSE cpu_to_le32(0xC0000445) +#define STATUS_INVALID_LABEL cpu_to_le32(0xC0000446) +#define STATUS_DRIVER_PROCESS_TERMINATED cpu_to_le32(0xC0000450) +#define STATUS_AMBIGUOUS_SYSTEM_DEVICE cpu_to_le32(0xC0000451) +#define STATUS_SYSTEM_DEVICE_NOT_FOUND cpu_to_le32(0xC0000452) +#define STATUS_RESTART_BOOT_APPLICATION cpu_to_le32(0xC0000453) +#define STATUS_INVALID_TASK_NAME cpu_to_le32(0xC0000500) +#define STATUS_INVALID_TASK_INDEX cpu_to_le32(0xC0000501) +#define STATUS_THREAD_ALREADY_IN_TASK cpu_to_le32(0xC0000502) +#define STATUS_CALLBACK_BYPASS cpu_to_le32(0xC0000503) +#define STATUS_PORT_CLOSED cpu_to_le32(0xC0000700) +#define STATUS_MESSAGE_LOST cpu_to_le32(0xC0000701) +#define STATUS_INVALID_MESSAGE cpu_to_le32(0xC0000702) +#define STATUS_REQUEST_CANCELED cpu_to_le32(0xC0000703) +#define STATUS_RECURSIVE_DISPATCH cpu_to_le32(0xC0000704) +#define STATUS_LPC_RECEIVE_BUFFER_EXPECTED cpu_to_le32(0xC0000705) +#define STATUS_LPC_INVALID_CONNECTION_USAGE cpu_to_le32(0xC0000706) +#define STATUS_LPC_REQUESTS_NOT_ALLOWED cpu_to_le32(0xC0000707) +#define STATUS_RESOURCE_IN_USE cpu_to_le32(0xC0000708) +#define STATUS_HARDWARE_MEMORY_ERROR cpu_to_le32(0xC0000709) +#define STATUS_THREADPOOL_HANDLE_EXCEPTION cpu_to_le32(0xC000070A) +#define STATUS_THREADPOOL_SET_EVENT_ON_COMPLETION_FAILED cpu_to_le32(0xC000070B) +#define STATUS_THREADPOOL_RELEASE_SEMAPHORE_ON_COMPLETION_FAILED \ + cpu_to_le32(0xC000070C) +#define STATUS_THREADPOOL_RELEASE_MUTEX_ON_COMPLETION_FAILED \ + cpu_to_le32(0xC000070D) +#define STATUS_THREADPOOL_FREE_LIBRARY_ON_COMPLETION_FAILED \ + cpu_to_le32(0xC000070E) +#define STATUS_THREADPOOL_RELEASED_DURING_OPERATION cpu_to_le32(0xC000070F) +#define STATUS_CALLBACK_RETURNED_WHILE_IMPERSONATING cpu_to_le32(0xC0000710) +#define STATUS_APC_RETURNED_WHILE_IMPERSONATING cpu_to_le32(0xC0000711) +#define STATUS_PROCESS_IS_PROTECTED cpu_to_le32(0xC0000712) +#define STATUS_MCA_EXCEPTION cpu_to_le32(0xC0000713) +#define STATUS_CERTIFICATE_MAPPING_NOT_UNIQUE cpu_to_le32(0xC0000714) +#define STATUS_SYMLINK_CLASS_DISABLED cpu_to_le32(0xC0000715) +#define STATUS_INVALID_IDN_NORMALIZATION cpu_to_le32(0xC0000716) +#define STATUS_NO_UNICODE_TRANSLATION cpu_to_le32(0xC0000717) +#define STATUS_ALREADY_REGISTERED cpu_to_le32(0xC0000718) +#define STATUS_CONTEXT_MISMATCH cpu_to_le32(0xC0000719) +#define STATUS_PORT_ALREADY_HAS_COMPLETION_LIST cpu_to_le32(0xC000071A) +#define STATUS_CALLBACK_RETURNED_THREAD_PRIORITY cpu_to_le32(0xC000071B) +#define STATUS_INVALID_THREAD cpu_to_le32(0xC000071C) +#define STATUS_CALLBACK_RETURNED_TRANSACTION cpu_to_le32(0xC000071D) +#define STATUS_CALLBACK_RETURNED_LDR_LOCK cpu_to_le32(0xC000071E) +#define STATUS_CALLBACK_RETURNED_LANG cpu_to_le32(0xC000071F) +#define STATUS_CALLBACK_RETURNED_PRI_BACK cpu_to_le32(0xC0000720) +#define STATUS_CALLBACK_RETURNED_THREAD_AFFINITY cpu_to_le32(0xC0000721) +#define STATUS_DISK_REPAIR_DISABLED cpu_to_le32(0xC0000800) +#define STATUS_DS_DOMAIN_RENAME_IN_PROGRESS cpu_to_le32(0xC0000801) +#define STATUS_DISK_QUOTA_EXCEEDED cpu_to_le32(0xC0000802) +#define STATUS_CONTENT_BLOCKED cpu_to_le32(0xC0000804) +#define STATUS_BAD_CLUSTERS cpu_to_le32(0xC0000805) +#define STATUS_VOLUME_DIRTY cpu_to_le32(0xC0000806) +#define STATUS_FILE_CHECKED_OUT cpu_to_le32(0xC0000901) +#define STATUS_CHECKOUT_REQUIRED cpu_to_le32(0xC0000902) +#define STATUS_BAD_FILE_TYPE cpu_to_le32(0xC0000903) +#define STATUS_FILE_TOO_LARGE cpu_to_le32(0xC0000904) +#define STATUS_FORMS_AUTH_REQUIRED cpu_to_le32(0xC0000905) +#define STATUS_VIRUS_INFECTED cpu_to_le32(0xC0000906) +#define STATUS_VIRUS_DELETED cpu_to_le32(0xC0000907) +#define STATUS_BAD_MCFG_TABLE cpu_to_le32(0xC0000908) +#define STATUS_WOW_ASSERTION cpu_to_le32(0xC0009898) +#define STATUS_INVALID_SIGNATURE cpu_to_le32(0xC000A000) +#define STATUS_HMAC_NOT_SUPPORTED cpu_to_le32(0xC000A001) +#define STATUS_IPSEC_QUEUE_OVERFLOW cpu_to_le32(0xC000A010) +#define STATUS_ND_QUEUE_OVERFLOW cpu_to_le32(0xC000A011) +#define STATUS_HOPLIMIT_EXCEEDED cpu_to_le32(0xC000A012) +#define STATUS_PROTOCOL_NOT_SUPPORTED cpu_to_le32(0xC000A013) +#define STATUS_LOST_WRITEBEHIND_DATA_NETWORK_DISCONNECTED \ + cpu_to_le32(0xC000A080) +#define STATUS_LOST_WRITEBEHIND_DATA_NETWORK_SERVER_ERROR \ + cpu_to_le32(0xC000A081) +#define STATUS_LOST_WRITEBEHIND_DATA_LOCAL_DISK_ERROR cpu_to_le32(0xC000A082) +#define STATUS_XML_PARSE_ERROR cpu_to_le32(0xC000A083) +#define STATUS_XMLDSIG_ERROR cpu_to_le32(0xC000A084) +#define STATUS_WRONG_COMPARTMENT cpu_to_le32(0xC000A085) +#define STATUS_AUTHIP_FAILURE cpu_to_le32(0xC000A086) +#define DBG_NO_STATE_CHANGE cpu_to_le32(0xC0010001) +#define DBG_APP_NOT_IDLE cpu_to_le32(0xC0010002) +#define RPC_NT_INVALID_STRING_BINDING cpu_to_le32(0xC0020001) +#define RPC_NT_WRONG_KIND_OF_BINDING cpu_to_le32(0xC0020002) +#define RPC_NT_INVALID_BINDING cpu_to_le32(0xC0020003) +#define RPC_NT_PROTSEQ_NOT_SUPPORTED cpu_to_le32(0xC0020004) +#define RPC_NT_INVALID_RPC_PROTSEQ cpu_to_le32(0xC0020005) +#define RPC_NT_INVALID_STRING_UUID cpu_to_le32(0xC0020006) +#define RPC_NT_INVALID_ENDPOINT_FORMAT cpu_to_le32(0xC0020007) +#define RPC_NT_INVALID_NET_ADDR cpu_to_le32(0xC0020008) +#define RPC_NT_NO_ENDPOINT_FOUND cpu_to_le32(0xC0020009) +#define RPC_NT_INVALID_TIMEOUT cpu_to_le32(0xC002000A) +#define RPC_NT_OBJECT_NOT_FOUND cpu_to_le32(0xC002000B) +#define RPC_NT_ALREADY_REGISTERED cpu_to_le32(0xC002000C) +#define RPC_NT_TYPE_ALREADY_REGISTERED cpu_to_le32(0xC002000D) +#define RPC_NT_ALREADY_LISTENING cpu_to_le32(0xC002000E) +#define RPC_NT_NO_PROTSEQS_REGISTERED cpu_to_le32(0xC002000F) +#define RPC_NT_NOT_LISTENING cpu_to_le32(0xC0020010) +#define RPC_NT_UNKNOWN_MGR_TYPE cpu_to_le32(0xC0020011) +#define RPC_NT_UNKNOWN_IF cpu_to_le32(0xC0020012) +#define RPC_NT_NO_BINDINGS cpu_to_le32(0xC0020013) +#define RPC_NT_NO_PROTSEQS cpu_to_le32(0xC0020014) +#define RPC_NT_CANT_CREATE_ENDPOINT cpu_to_le32(0xC0020015) +#define RPC_NT_OUT_OF_RESOURCES cpu_to_le32(0xC0020016) +#define RPC_NT_SERVER_UNAVAILABLE cpu_to_le32(0xC0020017) +#define RPC_NT_SERVER_TOO_BUSY cpu_to_le32(0xC0020018) +#define RPC_NT_INVALID_NETWORK_OPTIONS cpu_to_le32(0xC0020019) +#define RPC_NT_NO_CALL_ACTIVE cpu_to_le32(0xC002001A) +#define RPC_NT_CALL_FAILED cpu_to_le32(0xC002001B) +#define RPC_NT_CALL_FAILED_DNE cpu_to_le32(0xC002001C) +#define RPC_NT_PROTOCOL_ERROR cpu_to_le32(0xC002001D) +#define RPC_NT_UNSUPPORTED_TRANS_SYN cpu_to_le32(0xC002001F) +#define RPC_NT_UNSUPPORTED_TYPE cpu_to_le32(0xC0020021) +#define RPC_NT_INVALID_TAG cpu_to_le32(0xC0020022) +#define RPC_NT_INVALID_BOUND cpu_to_le32(0xC0020023) +#define RPC_NT_NO_ENTRY_NAME cpu_to_le32(0xC0020024) +#define RPC_NT_INVALID_NAME_SYNTAX cpu_to_le32(0xC0020025) +#define RPC_NT_UNSUPPORTED_NAME_SYNTAX cpu_to_le32(0xC0020026) +#define RPC_NT_UUID_NO_ADDRESS cpu_to_le32(0xC0020028) +#define RPC_NT_DUPLICATE_ENDPOINT cpu_to_le32(0xC0020029) +#define RPC_NT_UNKNOWN_AUTHN_TYPE cpu_to_le32(0xC002002A) +#define RPC_NT_MAX_CALLS_TOO_SMALL cpu_to_le32(0xC002002B) +#define RPC_NT_STRING_TOO_LONG cpu_to_le32(0xC002002C) +#define RPC_NT_PROTSEQ_NOT_FOUND cpu_to_le32(0xC002002D) +#define RPC_NT_PROCNUM_OUT_OF_RANGE cpu_to_le32(0xC002002E) +#define RPC_NT_BINDING_HAS_NO_AUTH cpu_to_le32(0xC002002F) +#define RPC_NT_UNKNOWN_AUTHN_SERVICE cpu_to_le32(0xC0020030) +#define RPC_NT_UNKNOWN_AUTHN_LEVEL cpu_to_le32(0xC0020031) +#define RPC_NT_INVALID_AUTH_IDENTITY cpu_to_le32(0xC0020032) +#define RPC_NT_UNKNOWN_AUTHZ_SERVICE cpu_to_le32(0xC0020033) +#define EPT_NT_INVALID_ENTRY cpu_to_le32(0xC0020034) +#define EPT_NT_CANT_PERFORM_OP cpu_to_le32(0xC0020035) +#define EPT_NT_NOT_REGISTERED cpu_to_le32(0xC0020036) +#define RPC_NT_NOTHING_TO_EXPORT cpu_to_le32(0xC0020037) +#define RPC_NT_INCOMPLETE_NAME cpu_to_le32(0xC0020038) +#define RPC_NT_INVALID_VERS_OPTION cpu_to_le32(0xC0020039) +#define RPC_NT_NO_MORE_MEMBERS cpu_to_le32(0xC002003A) +#define RPC_NT_NOT_ALL_OBJS_UNEXPORTED cpu_to_le32(0xC002003B) +#define RPC_NT_INTERFACE_NOT_FOUND cpu_to_le32(0xC002003C) +#define RPC_NT_ENTRY_ALREADY_EXISTS cpu_to_le32(0xC002003D) +#define RPC_NT_ENTRY_NOT_FOUND cpu_to_le32(0xC002003E) +#define RPC_NT_NAME_SERVICE_UNAVAILABLE cpu_to_le32(0xC002003F) +#define RPC_NT_INVALID_NAF_ID cpu_to_le32(0xC0020040) +#define RPC_NT_CANNOT_SUPPORT cpu_to_le32(0xC0020041) +#define RPC_NT_NO_CONTEXT_AVAILABLE cpu_to_le32(0xC0020042) +#define RPC_NT_INTERNAL_ERROR cpu_to_le32(0xC0020043) +#define RPC_NT_ZERO_DIVIDE cpu_to_le32(0xC0020044) +#define RPC_NT_ADDRESS_ERROR cpu_to_le32(0xC0020045) +#define RPC_NT_FP_DIV_ZERO cpu_to_le32(0xC0020046) +#define RPC_NT_FP_UNDERFLOW cpu_to_le32(0xC0020047) +#define RPC_NT_FP_OVERFLOW cpu_to_le32(0xC0020048) +#define RPC_NT_CALL_IN_PROGRESS cpu_to_le32(0xC0020049) +#define RPC_NT_NO_MORE_BINDINGS cpu_to_le32(0xC002004A) +#define RPC_NT_GROUP_MEMBER_NOT_FOUND cpu_to_le32(0xC002004B) +#define EPT_NT_CANT_CREATE cpu_to_le32(0xC002004C) +#define RPC_NT_INVALID_OBJECT cpu_to_le32(0xC002004D) +#define RPC_NT_NO_INTERFACES cpu_to_le32(0xC002004F) +#define RPC_NT_CALL_CANCELLED cpu_to_le32(0xC0020050) +#define RPC_NT_BINDING_INCOMPLETE cpu_to_le32(0xC0020051) +#define RPC_NT_COMM_FAILURE cpu_to_le32(0xC0020052) +#define RPC_NT_UNSUPPORTED_AUTHN_LEVEL cpu_to_le32(0xC0020053) +#define RPC_NT_NO_PRINC_NAME cpu_to_le32(0xC0020054) +#define RPC_NT_NOT_RPC_ERROR cpu_to_le32(0xC0020055) +#define RPC_NT_SEC_PKG_ERROR cpu_to_le32(0xC0020057) +#define RPC_NT_NOT_CANCELLED cpu_to_le32(0xC0020058) +#define RPC_NT_INVALID_ASYNC_HANDLE cpu_to_le32(0xC0020062) +#define RPC_NT_INVALID_ASYNC_CALL cpu_to_le32(0xC0020063) +#define RPC_NT_PROXY_ACCESS_DENIED cpu_to_le32(0xC0020064) +#define RPC_NT_NO_MORE_ENTRIES cpu_to_le32(0xC0030001) +#define RPC_NT_SS_CHAR_TRANS_OPEN_FAIL cpu_to_le32(0xC0030002) +#define RPC_NT_SS_CHAR_TRANS_SHORT_FILE cpu_to_le32(0xC0030003) +#define RPC_NT_SS_IN_NULL_CONTEXT cpu_to_le32(0xC0030004) +#define RPC_NT_SS_CONTEXT_MISMATCH cpu_to_le32(0xC0030005) +#define RPC_NT_SS_CONTEXT_DAMAGED cpu_to_le32(0xC0030006) +#define RPC_NT_SS_HANDLES_MISMATCH cpu_to_le32(0xC0030007) +#define RPC_NT_SS_CANNOT_GET_CALL_HANDLE cpu_to_le32(0xC0030008) +#define RPC_NT_NULL_REF_POINTER cpu_to_le32(0xC0030009) +#define RPC_NT_ENUM_VALUE_OUT_OF_RANGE cpu_to_le32(0xC003000A) +#define RPC_NT_BYTE_COUNT_TOO_SMALL cpu_to_le32(0xC003000B) +#define RPC_NT_BAD_STUB_DATA cpu_to_le32(0xC003000C) +#define RPC_NT_INVALID_ES_ACTION cpu_to_le32(0xC0030059) +#define RPC_NT_WRONG_ES_VERSION cpu_to_le32(0xC003005A) +#define RPC_NT_WRONG_STUB_VERSION cpu_to_le32(0xC003005B) +#define RPC_NT_INVALID_PIPE_OBJECT cpu_to_le32(0xC003005C) +#define RPC_NT_INVALID_PIPE_OPERATION cpu_to_le32(0xC003005D) +#define RPC_NT_WRONG_PIPE_VERSION cpu_to_le32(0xC003005E) +#define RPC_NT_PIPE_CLOSED cpu_to_le32(0xC003005F) +#define RPC_NT_PIPE_DISCIPLINE_ERROR cpu_to_le32(0xC0030060) +#define RPC_NT_PIPE_EMPTY cpu_to_le32(0xC0030061) +#define STATUS_PNP_BAD_MPS_TABLE cpu_to_le32(0xC0040035) +#define STATUS_PNP_TRANSLATION_FAILED cpu_to_le32(0xC0040036) +#define STATUS_PNP_IRQ_TRANSLATION_FAILED cpu_to_le32(0xC0040037) +#define STATUS_PNP_INVALID_ID cpu_to_le32(0xC0040038) +#define STATUS_IO_REISSUE_AS_CACHED cpu_to_le32(0xC0040039) +#define STATUS_CTX_WINSTATION_NAME_INVALID cpu_to_le32(0xC00A0001) +#define STATUS_CTX_INVALID_PD cpu_to_le32(0xC00A0002) +#define STATUS_CTX_PD_NOT_FOUND cpu_to_le32(0xC00A0003) +#define STATUS_CTX_CLOSE_PENDING cpu_to_le32(0xC00A0006) +#define STATUS_CTX_NO_OUTBUF cpu_to_le32(0xC00A0007) +#define STATUS_CTX_MODEM_INF_NOT_FOUND cpu_to_le32(0xC00A0008) +#define STATUS_CTX_INVALID_MODEMNAME cpu_to_le32(0xC00A0009) +#define STATUS_CTX_RESPONSE_ERROR cpu_to_le32(0xC00A000A) +#define STATUS_CTX_MODEM_RESPONSE_TIMEOUT cpu_to_le32(0xC00A000B) +#define STATUS_CTX_MODEM_RESPONSE_NO_CARRIER cpu_to_le32(0xC00A000C) +#define STATUS_CTX_MODEM_RESPONSE_NO_DIALTONE cpu_to_le32(0xC00A000D) +#define STATUS_CTX_MODEM_RESPONSE_BUSY cpu_to_le32(0xC00A000E) +#define STATUS_CTX_MODEM_RESPONSE_VOICE cpu_to_le32(0xC00A000F) +#define STATUS_CTX_TD_ERROR cpu_to_le32(0xC00A0010) +#define STATUS_CTX_LICENSE_CLIENT_INVALID cpu_to_le32(0xC00A0012) +#define STATUS_CTX_LICENSE_NOT_AVAILABLE cpu_to_le32(0xC00A0013) +#define STATUS_CTX_LICENSE_EXPIRED cpu_to_le32(0xC00A0014) +#define STATUS_CTX_WINSTATION_NOT_FOUND cpu_to_le32(0xC00A0015) +#define STATUS_CTX_WINSTATION_NAME_COLLISION cpu_to_le32(0xC00A0016) +#define STATUS_CTX_WINSTATION_BUSY cpu_to_le32(0xC00A0017) +#define STATUS_CTX_BAD_VIDEO_MODE cpu_to_le32(0xC00A0018) +#define STATUS_CTX_GRAPHICS_INVALID cpu_to_le32(0xC00A0022) +#define STATUS_CTX_NOT_CONSOLE cpu_to_le32(0xC00A0024) +#define STATUS_CTX_CLIENT_QUERY_TIMEOUT cpu_to_le32(0xC00A0026) +#define STATUS_CTX_CONSOLE_DISCONNECT cpu_to_le32(0xC00A0027) +#define STATUS_CTX_CONSOLE_CONNECT cpu_to_le32(0xC00A0028) +#define STATUS_CTX_SHADOW_DENIED cpu_to_le32(0xC00A002A) +#define STATUS_CTX_WINSTATION_ACCESS_DENIED cpu_to_le32(0xC00A002B) +#define STATUS_CTX_INVALID_WD cpu_to_le32(0xC00A002E) +#define STATUS_CTX_WD_NOT_FOUND cpu_to_le32(0xC00A002F) +#define STATUS_CTX_SHADOW_INVALID cpu_to_le32(0xC00A0030) +#define STATUS_CTX_SHADOW_DISABLED cpu_to_le32(0xC00A0031) +#define STATUS_RDP_PROTOCOL_ERROR cpu_to_le32(0xC00A0032) +#define STATUS_CTX_CLIENT_LICENSE_NOT_SET cpu_to_le32(0xC00A0033) +#define STATUS_CTX_CLIENT_LICENSE_IN_USE cpu_to_le32(0xC00A0034) +#define STATUS_CTX_SHADOW_ENDED_BY_MODE_CHANGE cpu_to_le32(0xC00A0035) +#define STATUS_CTX_SHADOW_NOT_RUNNING cpu_to_le32(0xC00A0036) +#define STATUS_CTX_LOGON_DISABLED cpu_to_le32(0xC00A0037) +#define STATUS_CTX_SECURITY_LAYER_ERROR cpu_to_le32(0xC00A0038) +#define STATUS_TS_INCOMPATIBLE_SESSIONS cpu_to_le32(0xC00A0039) +#define STATUS_MUI_FILE_NOT_FOUND cpu_to_le32(0xC00B0001) +#define STATUS_MUI_INVALID_FILE cpu_to_le32(0xC00B0002) +#define STATUS_MUI_INVALID_RC_CONFIG cpu_to_le32(0xC00B0003) +#define STATUS_MUI_INVALID_LOCALE_NAME cpu_to_le32(0xC00B0004) +#define STATUS_MUI_INVALID_ULTIMATEFALLBACK_NAME cpu_to_le32(0xC00B0005) +#define STATUS_MUI_FILE_NOT_LOADED cpu_to_le32(0xC00B0006) +#define STATUS_RESOURCE_ENUM_USER_STOP cpu_to_le32(0xC00B0007) +#define STATUS_CLUSTER_INVALID_NODE cpu_to_le32(0xC0130001) +#define STATUS_CLUSTER_NODE_EXISTS cpu_to_le32(0xC0130002) +#define STATUS_CLUSTER_JOIN_IN_PROGRESS cpu_to_le32(0xC0130003) +#define STATUS_CLUSTER_NODE_NOT_FOUND cpu_to_le32(0xC0130004) +#define STATUS_CLUSTER_LOCAL_NODE_NOT_FOUND cpu_to_le32(0xC0130005) +#define STATUS_CLUSTER_NETWORK_EXISTS cpu_to_le32(0xC0130006) +#define STATUS_CLUSTER_NETWORK_NOT_FOUND cpu_to_le32(0xC0130007) +#define STATUS_CLUSTER_NETINTERFACE_EXISTS cpu_to_le32(0xC0130008) +#define STATUS_CLUSTER_NETINTERFACE_NOT_FOUND cpu_to_le32(0xC0130009) +#define STATUS_CLUSTER_INVALID_REQUEST cpu_to_le32(0xC013000A) +#define STATUS_CLUSTER_INVALID_NETWORK_PROVIDER cpu_to_le32(0xC013000B) +#define STATUS_CLUSTER_NODE_DOWN cpu_to_le32(0xC013000C) +#define STATUS_CLUSTER_NODE_UNREACHABLE cpu_to_le32(0xC013000D) +#define STATUS_CLUSTER_NODE_NOT_MEMBER cpu_to_le32(0xC013000E) +#define STATUS_CLUSTER_JOIN_NOT_IN_PROGRESS cpu_to_le32(0xC013000F) +#define STATUS_CLUSTER_INVALID_NETWORK cpu_to_le32(0xC0130010) +#define STATUS_CLUSTER_NO_NET_ADAPTERS cpu_to_le32(0xC0130011) +#define STATUS_CLUSTER_NODE_UP cpu_to_le32(0xC0130012) +#define STATUS_CLUSTER_NODE_PAUSED cpu_to_le32(0xC0130013) +#define STATUS_CLUSTER_NODE_NOT_PAUSED cpu_to_le32(0xC0130014) +#define STATUS_CLUSTER_NO_SECURITY_CONTEXT cpu_to_le32(0xC0130015) +#define STATUS_CLUSTER_NETWORK_NOT_INTERNAL cpu_to_le32(0xC0130016) +#define STATUS_CLUSTER_POISONED cpu_to_le32(0xC0130017) +#define STATUS_ACPI_INVALID_OPCODE cpu_to_le32(0xC0140001) +#define STATUS_ACPI_STACK_OVERFLOW cpu_to_le32(0xC0140002) +#define STATUS_ACPI_ASSERT_FAILED cpu_to_le32(0xC0140003) +#define STATUS_ACPI_INVALID_INDEX cpu_to_le32(0xC0140004) +#define STATUS_ACPI_INVALID_ARGUMENT cpu_to_le32(0xC0140005) +#define STATUS_ACPI_FATAL cpu_to_le32(0xC0140006) +#define STATUS_ACPI_INVALID_SUPERNAME cpu_to_le32(0xC0140007) +#define STATUS_ACPI_INVALID_ARGTYPE cpu_to_le32(0xC0140008) +#define STATUS_ACPI_INVALID_OBJTYPE cpu_to_le32(0xC0140009) +#define STATUS_ACPI_INVALID_TARGETTYPE cpu_to_le32(0xC014000A) +#define STATUS_ACPI_INCORRECT_ARGUMENT_COUNT cpu_to_le32(0xC014000B) +#define STATUS_ACPI_ADDRESS_NOT_MAPPED cpu_to_le32(0xC014000C) +#define STATUS_ACPI_INVALID_EVENTTYPE cpu_to_le32(0xC014000D) +#define STATUS_ACPI_HANDLER_COLLISION cpu_to_le32(0xC014000E) +#define STATUS_ACPI_INVALID_DATA cpu_to_le32(0xC014000F) +#define STATUS_ACPI_INVALID_REGION cpu_to_le32(0xC0140010) +#define STATUS_ACPI_INVALID_ACCESS_SIZE cpu_to_le32(0xC0140011) +#define STATUS_ACPI_ACQUIRE_GLOBAL_LOCK cpu_to_le32(0xC0140012) +#define STATUS_ACPI_ALREADY_INITIALIZED cpu_to_le32(0xC0140013) +#define STATUS_ACPI_NOT_INITIALIZED cpu_to_le32(0xC0140014) +#define STATUS_ACPI_INVALID_MUTEX_LEVEL cpu_to_le32(0xC0140015) +#define STATUS_ACPI_MUTEX_NOT_OWNED cpu_to_le32(0xC0140016) +#define STATUS_ACPI_MUTEX_NOT_OWNER cpu_to_le32(0xC0140017) +#define STATUS_ACPI_RS_ACCESS cpu_to_le32(0xC0140018) +#define STATUS_ACPI_INVALID_TABLE cpu_to_le32(0xC0140019) +#define STATUS_ACPI_REG_HANDLER_FAILED cpu_to_le32(0xC0140020) +#define STATUS_ACPI_POWER_REQUEST_FAILED cpu_to_le32(0xC0140021) +#define STATUS_SXS_SECTION_NOT_FOUND cpu_to_le32(0xC0150001) +#define STATUS_SXS_CANT_GEN_ACTCTX cpu_to_le32(0xC0150002) +#define STATUS_SXS_INVALID_ACTCTXDATA_FORMAT cpu_to_le32(0xC0150003) +#define STATUS_SXS_ASSEMBLY_NOT_FOUND cpu_to_le32(0xC0150004) +#define STATUS_SXS_MANIFEST_FORMAT_ERROR cpu_to_le32(0xC0150005) +#define STATUS_SXS_MANIFEST_PARSE_ERROR cpu_to_le32(0xC0150006) +#define STATUS_SXS_ACTIVATION_CONTEXT_DISABLED cpu_to_le32(0xC0150007) +#define STATUS_SXS_KEY_NOT_FOUND cpu_to_le32(0xC0150008) +#define STATUS_SXS_VERSION_CONFLICT cpu_to_le32(0xC0150009) +#define STATUS_SXS_WRONG_SECTION_TYPE cpu_to_le32(0xC015000A) +#define STATUS_SXS_THREAD_QUERIES_DISABLED cpu_to_le32(0xC015000B) +#define STATUS_SXS_ASSEMBLY_MISSING cpu_to_le32(0xC015000C) +#define STATUS_SXS_PROCESS_DEFAULT_ALREADY_SET cpu_to_le32(0xC015000E) +#define STATUS_SXS_EARLY_DEACTIVATION cpu_to_le32(0xC015000F) +#define STATUS_SXS_INVALID_DEACTIVATION cpu_to_le32(0xC0150010) +#define STATUS_SXS_MULTIPLE_DEACTIVATION cpu_to_le32(0xC0150011) +#define STATUS_SXS_SYSTEM_DEFAULT_ACTIVATION_CONTEXT_EMPTY \ + cpu_to_le32(0xC0150012) +#define STATUS_SXS_PROCESS_TERMINATION_REQUESTED cpu_to_le32(0xC0150013) +#define STATUS_SXS_CORRUPT_ACTIVATION_STACK cpu_to_le32(0xC0150014) +#define STATUS_SXS_CORRUPTION cpu_to_le32(0xC0150015) +#define STATUS_SXS_INVALID_IDENTITY_ATTRIBUTE_VALUE cpu_to_le32(0xC0150016) +#define STATUS_SXS_INVALID_IDENTITY_ATTRIBUTE_NAME cpu_to_le32(0xC0150017) +#define STATUS_SXS_IDENTITY_DUPLICATE_ATTRIBUTE cpu_to_le32(0xC0150018) +#define STATUS_SXS_IDENTITY_PARSE_ERROR cpu_to_le32(0xC0150019) +#define STATUS_SXS_COMPONENT_STORE_CORRUPT cpu_to_le32(0xC015001A) +#define STATUS_SXS_FILE_HASH_MISMATCH cpu_to_le32(0xC015001B) +#define STATUS_SXS_MANIFEST_IDENTITY_SAME_BUT_CONTENTS_DIFFERENT \ + cpu_to_le32(0xC015001C) +#define STATUS_SXS_IDENTITIES_DIFFERENT cpu_to_le32(0xC015001D) +#define STATUS_SXS_ASSEMBLY_IS_NOT_A_DEPLOYMENT cpu_to_le32(0xC015001E) +#define STATUS_SXS_FILE_NOT_PART_OF_ASSEMBLY cpu_to_le32(0xC015001F) +#define STATUS_ADVANCED_INSTALLER_FAILED cpu_to_le32(0xC0150020) +#define STATUS_XML_ENCODING_MISMATCH cpu_to_le32(0xC0150021) +#define STATUS_SXS_MANIFEST_TOO_BIG cpu_to_le32(0xC0150022) +#define STATUS_SXS_SETTING_NOT_REGISTERED cpu_to_le32(0xC0150023) +#define STATUS_SXS_TRANSACTION_CLOSURE_INCOMPLETE cpu_to_le32(0xC0150024) +#define STATUS_SMI_PRIMITIVE_INSTALLER_FAILED cpu_to_le32(0xC0150025) +#define STATUS_GENERIC_COMMAND_FAILED cpu_to_le32(0xC0150026) +#define STATUS_SXS_FILE_HASH_MISSING cpu_to_le32(0xC0150027) +#define STATUS_TRANSACTIONAL_CONFLICT cpu_to_le32(0xC0190001) +#define STATUS_INVALID_TRANSACTION cpu_to_le32(0xC0190002) +#define STATUS_TRANSACTION_NOT_ACTIVE cpu_to_le32(0xC0190003) +#define STATUS_TM_INITIALIZATION_FAILED cpu_to_le32(0xC0190004) +#define STATUS_RM_NOT_ACTIVE cpu_to_le32(0xC0190005) +#define STATUS_RM_METADATA_CORRUPT cpu_to_le32(0xC0190006) +#define STATUS_TRANSACTION_NOT_JOINED cpu_to_le32(0xC0190007) +#define STATUS_DIRECTORY_NOT_RM cpu_to_le32(0xC0190008) +#define STATUS_TRANSACTIONS_UNSUPPORTED_REMOTE cpu_to_le32(0xC019000A) +#define STATUS_LOG_RESIZE_INVALID_SIZE cpu_to_le32(0xC019000B) +#define STATUS_REMOTE_FILE_VERSION_MISMATCH cpu_to_le32(0xC019000C) +#define STATUS_CRM_PROTOCOL_ALREADY_EXISTS cpu_to_le32(0xC019000F) +#define STATUS_TRANSACTION_PROPAGATION_FAILED cpu_to_le32(0xC0190010) +#define STATUS_CRM_PROTOCOL_NOT_FOUND cpu_to_le32(0xC0190011) +#define STATUS_TRANSACTION_SUPERIOR_EXISTS cpu_to_le32(0xC0190012) +#define STATUS_TRANSACTION_REQUEST_NOT_VALID cpu_to_le32(0xC0190013) +#define STATUS_TRANSACTION_NOT_REQUESTED cpu_to_le32(0xC0190014) +#define STATUS_TRANSACTION_ALREADY_ABORTED cpu_to_le32(0xC0190015) +#define STATUS_TRANSACTION_ALREADY_COMMITTED cpu_to_le32(0xC0190016) +#define STATUS_TRANSACTION_INVALID_MARSHALL_BUFFER cpu_to_le32(0xC0190017) +#define STATUS_CURRENT_TRANSACTION_NOT_VALID cpu_to_le32(0xC0190018) +#define STATUS_LOG_GROWTH_FAILED cpu_to_le32(0xC0190019) +#define STATUS_OBJECT_NO_LONGER_EXISTS cpu_to_le32(0xC0190021) +#define STATUS_STREAM_MINIVERSION_NOT_FOUND cpu_to_le32(0xC0190022) +#define STATUS_STREAM_MINIVERSION_NOT_VALID cpu_to_le32(0xC0190023) +#define STATUS_MINIVERSION_INACCESSIBLE_FROM_SPECIFIED_TRANSACTION \ + cpu_to_le32(0xC0190024) +#define STATUS_CANT_OPEN_MINIVERSION_WITH_MODIFY_INTENT cpu_to_le32(0xC0190025) +#define STATUS_CANT_CREATE_MORE_STREAM_MINIVERSIONS cpu_to_le32(0xC0190026) +#define STATUS_HANDLE_NO_LONGER_VALID cpu_to_le32(0xC0190028) +#define STATUS_LOG_CORRUPTION_DETECTED cpu_to_le32(0xC0190030) +#define STATUS_RM_DISCONNECTED cpu_to_le32(0xC0190032) +#define STATUS_ENLISTMENT_NOT_SUPERIOR cpu_to_le32(0xC0190033) +#define STATUS_FILE_IDENTITY_NOT_PERSISTENT cpu_to_le32(0xC0190036) +#define STATUS_CANT_BREAK_TRANSACTIONAL_DEPENDENCY cpu_to_le32(0xC0190037) +#define STATUS_CANT_CROSS_RM_BOUNDARY cpu_to_le32(0xC0190038) +#define STATUS_TXF_DIR_NOT_EMPTY cpu_to_le32(0xC0190039) +#define STATUS_INDOUBT_TRANSACTIONS_EXIST cpu_to_le32(0xC019003A) +#define STATUS_TM_VOLATILE cpu_to_le32(0xC019003B) +#define STATUS_ROLLBACK_TIMER_EXPIRED cpu_to_le32(0xC019003C) +#define STATUS_TXF_ATTRIBUTE_CORRUPT cpu_to_le32(0xC019003D) +#define STATUS_EFS_NOT_ALLOWED_IN_TRANSACTION cpu_to_le32(0xC019003E) +#define STATUS_TRANSACTIONAL_OPEN_NOT_ALLOWED cpu_to_le32(0xC019003F) +#define STATUS_TRANSACTED_MAPPING_UNSUPPORTED_REMOTE cpu_to_le32(0xC0190040) +#define STATUS_TRANSACTION_REQUIRED_PROMOTION cpu_to_le32(0xC0190043) +#define STATUS_CANNOT_EXECUTE_FILE_IN_TRANSACTION cpu_to_le32(0xC0190044) +#define STATUS_TRANSACTIONS_NOT_FROZEN cpu_to_le32(0xC0190045) +#define STATUS_TRANSACTION_FREEZE_IN_PROGRESS cpu_to_le32(0xC0190046) +#define STATUS_NOT_SNAPSHOT_VOLUME cpu_to_le32(0xC0190047) +#define STATUS_NO_SAVEPOINT_WITH_OPEN_FILES cpu_to_le32(0xC0190048) +#define STATUS_SPARSE_NOT_ALLOWED_IN_TRANSACTION cpu_to_le32(0xC0190049) +#define STATUS_TM_IDENTITY_MISMATCH cpu_to_le32(0xC019004A) +#define STATUS_FLOATED_SECTION cpu_to_le32(0xC019004B) +#define STATUS_CANNOT_ACCEPT_TRANSACTED_WORK cpu_to_le32(0xC019004C) +#define STATUS_CANNOT_ABORT_TRANSACTIONS cpu_to_le32(0xC019004D) +#define STATUS_TRANSACTION_NOT_FOUND cpu_to_le32(0xC019004E) +#define STATUS_RESOURCEMANAGER_NOT_FOUND cpu_to_le32(0xC019004F) +#define STATUS_ENLISTMENT_NOT_FOUND cpu_to_le32(0xC0190050) +#define STATUS_TRANSACTIONMANAGER_NOT_FOUND cpu_to_le32(0xC0190051) +#define STATUS_TRANSACTIONMANAGER_NOT_ONLINE cpu_to_le32(0xC0190052) +#define STATUS_TRANSACTIONMANAGER_RECOVERY_NAME_COLLISION \ + cpu_to_le32(0xC0190053) +#define STATUS_TRANSACTION_NOT_ROOT cpu_to_le32(0xC0190054) +#define STATUS_TRANSACTION_OBJECT_EXPIRED cpu_to_le32(0xC0190055) +#define STATUS_COMPRESSION_NOT_ALLOWED_IN_TRANSACTION cpu_to_le32(0xC0190056) +#define STATUS_TRANSACTION_RESPONSE_NOT_ENLISTED cpu_to_le32(0xC0190057) +#define STATUS_TRANSACTION_RECORD_TOO_LONG cpu_to_le32(0xC0190058) +#define STATUS_NO_LINK_TRACKING_IN_TRANSACTION cpu_to_le32(0xC0190059) +#define STATUS_OPERATION_NOT_SUPPORTED_IN_TRANSACTION cpu_to_le32(0xC019005A) +#define STATUS_TRANSACTION_INTEGRITY_VIOLATED cpu_to_le32(0xC019005B) +#define STATUS_LOG_SECTOR_INVALID cpu_to_le32(0xC01A0001) +#define STATUS_LOG_SECTOR_PARITY_INVALID cpu_to_le32(0xC01A0002) +#define STATUS_LOG_SECTOR_REMAPPED cpu_to_le32(0xC01A0003) +#define STATUS_LOG_BLOCK_INCOMPLETE cpu_to_le32(0xC01A0004) +#define STATUS_LOG_INVALID_RANGE cpu_to_le32(0xC01A0005) +#define STATUS_LOG_BLOCKS_EXHAUSTED cpu_to_le32(0xC01A0006) +#define STATUS_LOG_READ_CONTEXT_INVALID cpu_to_le32(0xC01A0007) +#define STATUS_LOG_RESTART_INVALID cpu_to_le32(0xC01A0008) +#define STATUS_LOG_BLOCK_VERSION cpu_to_le32(0xC01A0009) +#define STATUS_LOG_BLOCK_INVALID cpu_to_le32(0xC01A000A) +#define STATUS_LOG_READ_MODE_INVALID cpu_to_le32(0xC01A000B) +#define STATUS_LOG_METADATA_CORRUPT cpu_to_le32(0xC01A000D) +#define STATUS_LOG_METADATA_INVALID cpu_to_le32(0xC01A000E) +#define STATUS_LOG_METADATA_INCONSISTENT cpu_to_le32(0xC01A000F) +#define STATUS_LOG_RESERVATION_INVALID cpu_to_le32(0xC01A0010) +#define STATUS_LOG_CANT_DELETE cpu_to_le32(0xC01A0011) +#define STATUS_LOG_CONTAINER_LIMIT_EXCEEDED cpu_to_le32(0xC01A0012) +#define STATUS_LOG_START_OF_LOG cpu_to_le32(0xC01A0013) +#define STATUS_LOG_POLICY_ALREADY_INSTALLED cpu_to_le32(0xC01A0014) +#define STATUS_LOG_POLICY_NOT_INSTALLED cpu_to_le32(0xC01A0015) +#define STATUS_LOG_POLICY_INVALID cpu_to_le32(0xC01A0016) +#define STATUS_LOG_POLICY_CONFLICT cpu_to_le32(0xC01A0017) +#define STATUS_LOG_PINNED_ARCHIVE_TAIL cpu_to_le32(0xC01A0018) +#define STATUS_LOG_RECORD_NONEXISTENT cpu_to_le32(0xC01A0019) +#define STATUS_LOG_RECORDS_RESERVED_INVALID cpu_to_le32(0xC01A001A) +#define STATUS_LOG_SPACE_RESERVED_INVALID cpu_to_le32(0xC01A001B) +#define STATUS_LOG_TAIL_INVALID cpu_to_le32(0xC01A001C) +#define STATUS_LOG_FULL cpu_to_le32(0xC01A001D) +#define STATUS_LOG_MULTIPLEXED cpu_to_le32(0xC01A001E) +#define STATUS_LOG_DEDICATED cpu_to_le32(0xC01A001F) +#define STATUS_LOG_ARCHIVE_NOT_IN_PROGRESS cpu_to_le32(0xC01A0020) +#define STATUS_LOG_ARCHIVE_IN_PROGRESS cpu_to_le32(0xC01A0021) +#define STATUS_LOG_EPHEMERAL cpu_to_le32(0xC01A0022) +#define STATUS_LOG_NOT_ENOUGH_CONTAINERS cpu_to_le32(0xC01A0023) +#define STATUS_LOG_CLIENT_ALREADY_REGISTERED cpu_to_le32(0xC01A0024) +#define STATUS_LOG_CLIENT_NOT_REGISTERED cpu_to_le32(0xC01A0025) +#define STATUS_LOG_FULL_HANDLER_IN_PROGRESS cpu_to_le32(0xC01A0026) +#define STATUS_LOG_CONTAINER_READ_FAILED cpu_to_le32(0xC01A0027) +#define STATUS_LOG_CONTAINER_WRITE_FAILED cpu_to_le32(0xC01A0028) +#define STATUS_LOG_CONTAINER_OPEN_FAILED cpu_to_le32(0xC01A0029) +#define STATUS_LOG_CONTAINER_STATE_INVALID cpu_to_le32(0xC01A002A) +#define STATUS_LOG_STATE_INVALID cpu_to_le32(0xC01A002B) +#define STATUS_LOG_PINNED cpu_to_le32(0xC01A002C) +#define STATUS_LOG_METADATA_FLUSH_FAILED cpu_to_le32(0xC01A002D) +#define STATUS_LOG_INCONSISTENT_SECURITY cpu_to_le32(0xC01A002E) +#define STATUS_LOG_APPENDED_FLUSH_FAILED cpu_to_le32(0xC01A002F) +#define STATUS_LOG_PINNED_RESERVATION cpu_to_le32(0xC01A0030) +#define STATUS_VIDEO_HUNG_DISPLAY_DRIVER_THREAD cpu_to_le32(0xC01B00EA) +#define STATUS_FLT_NO_HANDLER_DEFINED cpu_to_le32(0xC01C0001) +#define STATUS_FLT_CONTEXT_ALREADY_DEFINED cpu_to_le32(0xC01C0002) +#define STATUS_FLT_INVALID_ASYNCHRONOUS_REQUEST cpu_to_le32(0xC01C0003) +#define STATUS_FLT_DISALLOW_FAST_IO cpu_to_le32(0xC01C0004) +#define STATUS_FLT_INVALID_NAME_REQUEST cpu_to_le32(0xC01C0005) +#define STATUS_FLT_NOT_SAFE_TO_POST_OPERATION cpu_to_le32(0xC01C0006) +#define STATUS_FLT_NOT_INITIALIZED cpu_to_le32(0xC01C0007) +#define STATUS_FLT_FILTER_NOT_READY cpu_to_le32(0xC01C0008) +#define STATUS_FLT_POST_OPERATION_CLEANUP cpu_to_le32(0xC01C0009) +#define STATUS_FLT_INTERNAL_ERROR cpu_to_le32(0xC01C000A) +#define STATUS_FLT_DELETING_OBJECT cpu_to_le32(0xC01C000B) +#define STATUS_FLT_MUST_BE_NONPAGED_POOL cpu_to_le32(0xC01C000C) +#define STATUS_FLT_DUPLICATE_ENTRY cpu_to_le32(0xC01C000D) +#define STATUS_FLT_CBDQ_DISABLED cpu_to_le32(0xC01C000E) +#define STATUS_FLT_DO_NOT_ATTACH cpu_to_le32(0xC01C000F) +#define STATUS_FLT_DO_NOT_DETACH cpu_to_le32(0xC01C0010) +#define STATUS_FLT_INSTANCE_ALTITUDE_COLLISION cpu_to_le32(0xC01C0011) +#define STATUS_FLT_INSTANCE_NAME_COLLISION cpu_to_le32(0xC01C0012) +#define STATUS_FLT_FILTER_NOT_FOUND cpu_to_le32(0xC01C0013) +#define STATUS_FLT_VOLUME_NOT_FOUND cpu_to_le32(0xC01C0014) +#define STATUS_FLT_INSTANCE_NOT_FOUND cpu_to_le32(0xC01C0015) +#define STATUS_FLT_CONTEXT_ALLOCATION_NOT_FOUND cpu_to_le32(0xC01C0016) +#define STATUS_FLT_INVALID_CONTEXT_REGISTRATION cpu_to_le32(0xC01C0017) +#define STATUS_FLT_NAME_CACHE_MISS cpu_to_le32(0xC01C0018) +#define STATUS_FLT_NO_DEVICE_OBJECT cpu_to_le32(0xC01C0019) +#define STATUS_FLT_VOLUME_ALREADY_MOUNTED cpu_to_le32(0xC01C001A) +#define STATUS_FLT_ALREADY_ENLISTED cpu_to_le32(0xC01C001B) +#define STATUS_FLT_CONTEXT_ALREADY_LINKED cpu_to_le32(0xC01C001C) +#define STATUS_FLT_NO_WAITER_FOR_REPLY cpu_to_le32(0xC01C0020) +#define STATUS_MONITOR_NO_DESCRIPTOR cpu_to_le32(0xC01D0001) +#define STATUS_MONITOR_UNKNOWN_DESCRIPTOR_FORMAT cpu_to_le32(0xC01D0002) +#define STATUS_MONITOR_INVALID_DESCRIPTOR_CHECKSUM cpu_to_le32(0xC01D0003) +#define STATUS_MONITOR_INVALID_STANDARD_TIMING_BLOCK cpu_to_le32(0xC01D0004) +#define STATUS_MONITOR_WMI_DATABLOCK_REGISTRATION_FAILED cpu_to_le32(0xC01D0005) +#define STATUS_MONITOR_INVALID_SERIAL_NUMBER_MONDSC_BLOCK \ + cpu_to_le32(0xC01D0006) +#define STATUS_MONITOR_INVALID_USER_FRIENDLY_MONDSC_BLOCK \ + cpu_to_le32(0xC01D0007) +#define STATUS_MONITOR_NO_MORE_DESCRIPTOR_DATA cpu_to_le32(0xC01D0008) +#define STATUS_MONITOR_INVALID_DETAILED_TIMING_BLOCK cpu_to_le32(0xC01D0009) +#define STATUS_GRAPHICS_NOT_EXCLUSIVE_MODE_OWNER cpu_to_le32(0xC01E0000) +#define STATUS_GRAPHICS_INSUFFICIENT_DMA_BUFFER cpu_to_le32(0xC01E0001) +#define STATUS_GRAPHICS_INVALID_DISPLAY_ADAPTER cpu_to_le32(0xC01E0002) +#define STATUS_GRAPHICS_ADAPTER_WAS_RESET cpu_to_le32(0xC01E0003) +#define STATUS_GRAPHICS_INVALID_DRIVER_MODEL cpu_to_le32(0xC01E0004) +#define STATUS_GRAPHICS_PRESENT_MODE_CHANGED cpu_to_le32(0xC01E0005) +#define STATUS_GRAPHICS_PRESENT_OCCLUDED cpu_to_le32(0xC01E0006) +#define STATUS_GRAPHICS_PRESENT_DENIED cpu_to_le32(0xC01E0007) +#define STATUS_GRAPHICS_CANNOTCOLORCONVERT cpu_to_le32(0xC01E0008) +#define STATUS_GRAPHICS_NO_VIDEO_MEMORY cpu_to_le32(0xC01E0100) +#define STATUS_GRAPHICS_CANT_LOCK_MEMORY cpu_to_le32(0xC01E0101) +#define STATUS_GRAPHICS_ALLOCATION_BUSY cpu_to_le32(0xC01E0102) +#define STATUS_GRAPHICS_TOO_MANY_REFERENCES cpu_to_le32(0xC01E0103) +#define STATUS_GRAPHICS_TRY_AGAIN_LATER cpu_to_le32(0xC01E0104) +#define STATUS_GRAPHICS_TRY_AGAIN_NOW cpu_to_le32(0xC01E0105) +#define STATUS_GRAPHICS_ALLOCATION_INVALID cpu_to_le32(0xC01E0106) +#define STATUS_GRAPHICS_UNSWIZZLING_APERTURE_UNAVAILABLE cpu_to_le32(0xC01E0107) +#define STATUS_GRAPHICS_UNSWIZZLING_APERTURE_UNSUPPORTED cpu_to_le32(0xC01E0108) +#define STATUS_GRAPHICS_CANT_EVICT_PINNED_ALLOCATION cpu_to_le32(0xC01E0109) +#define STATUS_GRAPHICS_INVALID_ALLOCATION_USAGE cpu_to_le32(0xC01E0110) +#define STATUS_GRAPHICS_CANT_RENDER_LOCKED_ALLOCATION cpu_to_le32(0xC01E0111) +#define STATUS_GRAPHICS_ALLOCATION_CLOSED cpu_to_le32(0xC01E0112) +#define STATUS_GRAPHICS_INVALID_ALLOCATION_INSTANCE cpu_to_le32(0xC01E0113) +#define STATUS_GRAPHICS_INVALID_ALLOCATION_HANDLE cpu_to_le32(0xC01E0114) +#define STATUS_GRAPHICS_WRONG_ALLOCATION_DEVICE cpu_to_le32(0xC01E0115) +#define STATUS_GRAPHICS_ALLOCATION_CONTENT_LOST cpu_to_le32(0xC01E0116) +#define STATUS_GRAPHICS_GPU_EXCEPTION_ON_DEVICE cpu_to_le32(0xC01E0200) +#define STATUS_GRAPHICS_INVALID_VIDPN_TOPOLOGY cpu_to_le32(0xC01E0300) +#define STATUS_GRAPHICS_VIDPN_TOPOLOGY_NOT_SUPPORTED cpu_to_le32(0xC01E0301) +#define STATUS_GRAPHICS_VIDPN_TOPOLOGY_CURRENTLY_NOT_SUPPORTED \ + cpu_to_le32(0xC01E0302) +#define STATUS_GRAPHICS_INVALID_VIDPN cpu_to_le32(0xC01E0303) +#define STATUS_GRAPHICS_INVALID_VIDEO_PRESENT_SOURCE cpu_to_le32(0xC01E0304) +#define STATUS_GRAPHICS_INVALID_VIDEO_PRESENT_TARGET cpu_to_le32(0xC01E0305) +#define STATUS_GRAPHICS_VIDPN_MODALITY_NOT_SUPPORTED cpu_to_le32(0xC01E0306) +#define STATUS_GRAPHICS_INVALID_VIDPN_SOURCEMODESET cpu_to_le32(0xC01E0308) +#define STATUS_GRAPHICS_INVALID_VIDPN_TARGETMODESET cpu_to_le32(0xC01E0309) +#define STATUS_GRAPHICS_INVALID_FREQUENCY cpu_to_le32(0xC01E030A) +#define STATUS_GRAPHICS_INVALID_ACTIVE_REGION cpu_to_le32(0xC01E030B) +#define STATUS_GRAPHICS_INVALID_TOTAL_REGION cpu_to_le32(0xC01E030C) +#define STATUS_GRAPHICS_INVALID_VIDEO_PRESENT_SOURCE_MODE \ + cpu_to_le32(0xC01E0310) +#define STATUS_GRAPHICS_INVALID_VIDEO_PRESENT_TARGET_MODE \ + cpu_to_le32(0xC01E0311) +#define STATUS_GRAPHICS_PINNED_MODE_MUST_REMAIN_IN_SET cpu_to_le32(0xC01E0312) +#define STATUS_GRAPHICS_PATH_ALREADY_IN_TOPOLOGY cpu_to_le32(0xC01E0313) +#define STATUS_GRAPHICS_MODE_ALREADY_IN_MODESET cpu_to_le32(0xC01E0314) +#define STATUS_GRAPHICS_INVALID_VIDEOPRESENTSOURCESET cpu_to_le32(0xC01E0315) +#define STATUS_GRAPHICS_INVALID_VIDEOPRESENTTARGETSET cpu_to_le32(0xC01E0316) +#define STATUS_GRAPHICS_SOURCE_ALREADY_IN_SET cpu_to_le32(0xC01E0317) +#define STATUS_GRAPHICS_TARGET_ALREADY_IN_SET cpu_to_le32(0xC01E0318) +#define STATUS_GRAPHICS_INVALID_VIDPN_PRESENT_PATH cpu_to_le32(0xC01E0319) +#define STATUS_GRAPHICS_NO_RECOMMENDED_VIDPN_TOPOLOGY cpu_to_le32(0xC01E031A) +#define STATUS_GRAPHICS_INVALID_MONITOR_FREQUENCYRANGESET \ + cpu_to_le32(0xC01E031B) +#define STATUS_GRAPHICS_INVALID_MONITOR_FREQUENCYRANGE cpu_to_le32(0xC01E031C) +#define STATUS_GRAPHICS_FREQUENCYRANGE_NOT_IN_SET cpu_to_le32(0xC01E031D) +#define STATUS_GRAPHICS_FREQUENCYRANGE_ALREADY_IN_SET cpu_to_le32(0xC01E031F) +#define STATUS_GRAPHICS_STALE_MODESET cpu_to_le32(0xC01E0320) +#define STATUS_GRAPHICS_INVALID_MONITOR_SOURCEMODESET cpu_to_le32(0xC01E0321) +#define STATUS_GRAPHICS_INVALID_MONITOR_SOURCE_MODE cpu_to_le32(0xC01E0322) +#define STATUS_GRAPHICS_NO_RECOMMENDED_FUNCTIONAL_VIDPN cpu_to_le32(0xC01E0323) +#define STATUS_GRAPHICS_MODE_ID_MUST_BE_UNIQUE cpu_to_le32(0xC01E0324) +#define STATUS_GRAPHICS_EMPTY_ADAPTER_MONITOR_MODE_SUPPORT_INTERSECTION \ + cpu_to_le32(0xC01E0325) +#define STATUS_GRAPHICS_VIDEO_PRESENT_TARGETS_LESS_THAN_SOURCES \ + cpu_to_le32(0xC01E0326) +#define STATUS_GRAPHICS_PATH_NOT_IN_TOPOLOGY cpu_to_le32(0xC01E0327) +#define STATUS_GRAPHICS_ADAPTER_MUST_HAVE_AT_LEAST_ONE_SOURCE \ + cpu_to_le32(0xC01E0328) +#define STATUS_GRAPHICS_ADAPTER_MUST_HAVE_AT_LEAST_ONE_TARGET \ + cpu_to_le32(0xC01E0329) +#define STATUS_GRAPHICS_INVALID_MONITORDESCRIPTORSET cpu_to_le32(0xC01E032A) +#define STATUS_GRAPHICS_INVALID_MONITORDESCRIPTOR cpu_to_le32(0xC01E032B) +#define STATUS_GRAPHICS_MONITORDESCRIPTOR_NOT_IN_SET cpu_to_le32(0xC01E032C) +#define STATUS_GRAPHICS_MONITORDESCRIPTOR_ALREADY_IN_SET cpu_to_le32(0xC01E032D) +#define STATUS_GRAPHICS_MONITORDESCRIPTOR_ID_MUST_BE_UNIQUE \ + cpu_to_le32(0xC01E032E) +#define STATUS_GRAPHICS_INVALID_VIDPN_TARGET_SUBSET_TYPE cpu_to_le32(0xC01E032F) +#define STATUS_GRAPHICS_RESOURCES_NOT_RELATED cpu_to_le32(0xC01E0330) +#define STATUS_GRAPHICS_SOURCE_ID_MUST_BE_UNIQUE cpu_to_le32(0xC01E0331) +#define STATUS_GRAPHICS_TARGET_ID_MUST_BE_UNIQUE cpu_to_le32(0xC01E0332) +#define STATUS_GRAPHICS_NO_AVAILABLE_VIDPN_TARGET cpu_to_le32(0xC01E0333) +#define STATUS_GRAPHICS_MONITOR_COULD_NOT_BE_ASSOCIATED_WITH_ADAPTER \ + cpu_to_le32(0xC01E0334) +#define STATUS_GRAPHICS_NO_VIDPNMGR cpu_to_le32(0xC01E0335) +#define STATUS_GRAPHICS_NO_ACTIVE_VIDPN cpu_to_le32(0xC01E0336) +#define STATUS_GRAPHICS_STALE_VIDPN_TOPOLOGY cpu_to_le32(0xC01E0337) +#define STATUS_GRAPHICS_MONITOR_NOT_CONNECTED cpu_to_le32(0xC01E0338) +#define STATUS_GRAPHICS_SOURCE_NOT_IN_TOPOLOGY cpu_to_le32(0xC01E0339) +#define STATUS_GRAPHICS_INVALID_PRIMARYSURFACE_SIZE cpu_to_le32(0xC01E033A) +#define STATUS_GRAPHICS_INVALID_VISIBLEREGION_SIZE cpu_to_le32(0xC01E033B) +#define STATUS_GRAPHICS_INVALID_STRIDE cpu_to_le32(0xC01E033C) +#define STATUS_GRAPHICS_INVALID_PIXELFORMAT cpu_to_le32(0xC01E033D) +#define STATUS_GRAPHICS_INVALID_COLORBASIS cpu_to_le32(0xC01E033E) +#define STATUS_GRAPHICS_INVALID_PIXELVALUEACCESSMODE cpu_to_le32(0xC01E033F) +#define STATUS_GRAPHICS_TARGET_NOT_IN_TOPOLOGY cpu_to_le32(0xC01E0340) +#define STATUS_GRAPHICS_NO_DISPLAY_MODE_MANAGEMENT_SUPPORT \ + cpu_to_le32(0xC01E0341) +#define STATUS_GRAPHICS_VIDPN_SOURCE_IN_USE cpu_to_le32(0xC01E0342) +#define STATUS_GRAPHICS_CANT_ACCESS_ACTIVE_VIDPN cpu_to_le32(0xC01E0343) +#define STATUS_GRAPHICS_INVALID_PATH_IMPORTANCE_ORDINAL cpu_to_le32(0xC01E0344) +#define STATUS_GRAPHICS_INVALID_PATH_CONTENT_GEOMETRY_TRANSFORMATION \ + cpu_to_le32(0xC01E0345) +#define STATUS_GRAPHICS_PATH_CONTENT_GEOMETRY_TRANSFORMATION_NOT_SUPPORTED \ + cpu_to_le32(0xC01E0346) +#define STATUS_GRAPHICS_INVALID_GAMMA_RAMP cpu_to_le32(0xC01E0347) +#define STATUS_GRAPHICS_GAMMA_RAMP_NOT_SUPPORTED cpu_to_le32(0xC01E0348) +#define STATUS_GRAPHICS_MULTISAMPLING_NOT_SUPPORTED cpu_to_le32(0xC01E0349) +#define STATUS_GRAPHICS_MODE_NOT_IN_MODESET cpu_to_le32(0xC01E034A) +#define STATUS_GRAPHICS_INVALID_VIDPN_TOPOLOGY_RECOMMENDATION_REASON \ + cpu_to_le32(0xC01E034D) +#define STATUS_GRAPHICS_INVALID_PATH_CONTENT_TYPE cpu_to_le32(0xC01E034E) +#define STATUS_GRAPHICS_INVALID_COPYPROTECTION_TYPE cpu_to_le32(0xC01E034F) +#define STATUS_GRAPHICS_UNASSIGNED_MODESET_ALREADY_EXISTS \ + cpu_to_le32(0xC01E0350) +#define STATUS_GRAPHICS_INVALID_SCANLINE_ORDERING cpu_to_le32(0xC01E0352) +#define STATUS_GRAPHICS_TOPOLOGY_CHANGES_NOT_ALLOWED cpu_to_le32(0xC01E0353) +#define STATUS_GRAPHICS_NO_AVAILABLE_IMPORTANCE_ORDINALS cpu_to_le32(0xC01E0354) +#define STATUS_GRAPHICS_INCOMPATIBLE_PRIVATE_FORMAT cpu_to_le32(0xC01E0355) +#define STATUS_GRAPHICS_INVALID_MODE_PRUNING_ALGORITHM cpu_to_le32(0xC01E0356) +#define STATUS_GRAPHICS_INVALID_MONITOR_CAPABILITY_ORIGIN \ + cpu_to_le32(0xC01E0357) +#define STATUS_GRAPHICS_INVALID_MONITOR_FREQUENCYRANGE_CONSTRAINT \ + cpu_to_le32(0xC01E0358) +#define STATUS_GRAPHICS_MAX_NUM_PATHS_REACHED cpu_to_le32(0xC01E0359) +#define STATUS_GRAPHICS_CANCEL_VIDPN_TOPOLOGY_AUGMENTATION \ + cpu_to_le32(0xC01E035A) +#define STATUS_GRAPHICS_INVALID_CLIENT_TYPE cpu_to_le32(0xC01E035B) +#define STATUS_GRAPHICS_CLIENTVIDPN_NOT_SET cpu_to_le32(0xC01E035C) +#define STATUS_GRAPHICS_SPECIFIED_CHILD_ALREADY_CONNECTED \ + cpu_to_le32(0xC01E0400) +#define STATUS_GRAPHICS_CHILD_DESCRIPTOR_NOT_SUPPORTED cpu_to_le32(0xC01E0401) +#define STATUS_GRAPHICS_NOT_A_LINKED_ADAPTER cpu_to_le32(0xC01E0430) +#define STATUS_GRAPHICS_LEADLINK_NOT_ENUMERATED cpu_to_le32(0xC01E0431) +#define STATUS_GRAPHICS_CHAINLINKS_NOT_ENUMERATED cpu_to_le32(0xC01E0432) +#define STATUS_GRAPHICS_ADAPTER_CHAIN_NOT_READY cpu_to_le32(0xC01E0433) +#define STATUS_GRAPHICS_CHAINLINKS_NOT_STARTED cpu_to_le32(0xC01E0434) +#define STATUS_GRAPHICS_CHAINLINKS_NOT_POWERED_ON cpu_to_le32(0xC01E0435) +#define STATUS_GRAPHICS_INCONSISTENT_DEVICE_LINK_STATE cpu_to_le32(0xC01E0436) +#define STATUS_GRAPHICS_NOT_POST_DEVICE_DRIVER cpu_to_le32(0xC01E0438) +#define STATUS_GRAPHICS_ADAPTER_ACCESS_NOT_EXCLUDED cpu_to_le32(0xC01E043B) +#define STATUS_GRAPHICS_OPM_PROTECTED_OUTPUT_DOES_NOT_HAVE_COPP_SEMANTICS \ + cpu_to_le32(0xC01E051C) +#define STATUS_GRAPHICS_OPM_INVALID_INFORMATION_REQUEST cpu_to_le32(0xC01E051D) +#define STATUS_GRAPHICS_OPM_DRIVER_INTERNAL_ERROR cpu_to_le32(0xC01E051E) +#define STATUS_GRAPHICS_OPM_PROTECTED_OUTPUT_DOES_NOT_HAVE_OPM_SEMANTICS \ + cpu_to_le32(0xC01E051F) +#define STATUS_GRAPHICS_OPM_SIGNALING_NOT_SUPPORTED cpu_to_le32(0xC01E0520) +#define STATUS_GRAPHICS_OPM_INVALID_CONFIGURATION_REQUEST \ + cpu_to_le32(0xC01E0521) +#define STATUS_GRAPHICS_OPM_NOT_SUPPORTED cpu_to_le32(0xC01E0500) +#define STATUS_GRAPHICS_COPP_NOT_SUPPORTED cpu_to_le32(0xC01E0501) +#define STATUS_GRAPHICS_UAB_NOT_SUPPORTED cpu_to_le32(0xC01E0502) +#define STATUS_GRAPHICS_OPM_INVALID_ENCRYPTED_PARAMETERS cpu_to_le32(0xC01E0503) +#define STATUS_GRAPHICS_OPM_PARAMETER_ARRAY_TOO_SMALL cpu_to_le32(0xC01E0504) +#define STATUS_GRAPHICS_OPM_NO_PROTECTED_OUTPUTS_EXIST cpu_to_le32(0xC01E0505) +#define STATUS_GRAPHICS_PVP_NO_DISPLAY_DEVICE_CORRESPONDS_TO_NAME \ + cpu_to_le32(0xC01E0506) +#define STATUS_GRAPHICS_PVP_DISPLAY_DEVICE_NOT_ATTACHED_TO_DESKTOP \ + cpu_to_le32(0xC01E0507) +#define STATUS_GRAPHICS_PVP_MIRRORING_DEVICES_NOT_SUPPORTED \ + cpu_to_le32(0xC01E0508) +#define STATUS_GRAPHICS_OPM_INVALID_POINTER cpu_to_le32(0xC01E050A) +#define STATUS_GRAPHICS_OPM_INTERNAL_ERROR cpu_to_le32(0xC01E050B) +#define STATUS_GRAPHICS_OPM_INVALID_HANDLE cpu_to_le32(0xC01E050C) +#define STATUS_GRAPHICS_PVP_NO_MONITORS_CORRESPOND_TO_DISPLAY_DEVICE \ + cpu_to_le32(0xC01E050D) +#define STATUS_GRAPHICS_PVP_INVALID_CERTIFICATE_LENGTH cpu_to_le32(0xC01E050E) +#define STATUS_GRAPHICS_OPM_SPANNING_MODE_ENABLED cpu_to_le32(0xC01E050F) +#define STATUS_GRAPHICS_OPM_THEATER_MODE_ENABLED cpu_to_le32(0xC01E0510) +#define STATUS_GRAPHICS_PVP_HFS_FAILED cpu_to_le32(0xC01E0511) +#define STATUS_GRAPHICS_OPM_INVALID_SRM cpu_to_le32(0xC01E0512) +#define STATUS_GRAPHICS_OPM_OUTPUT_DOES_NOT_SUPPORT_HDCP cpu_to_le32(0xC01E0513) +#define STATUS_GRAPHICS_OPM_OUTPUT_DOES_NOT_SUPPORT_ACP cpu_to_le32(0xC01E0514) +#define STATUS_GRAPHICS_OPM_OUTPUT_DOES_NOT_SUPPORT_CGMSA \ + cpu_to_le32(0xC01E0515) +#define STATUS_GRAPHICS_OPM_HDCP_SRM_NEVER_SET cpu_to_le32(0xC01E0516) +#define STATUS_GRAPHICS_OPM_RESOLUTION_TOO_HIGH cpu_to_le32(0xC01E0517) +#define STATUS_GRAPHICS_OPM_ALL_HDCP_HARDWARE_ALREADY_IN_USE \ + cpu_to_le32(0xC01E0518) +#define STATUS_GRAPHICS_OPM_PROTECTED_OUTPUT_NO_LONGER_EXISTS \ + cpu_to_le32(0xC01E051A) +#define STATUS_GRAPHICS_OPM_SESSION_TYPE_CHANGE_IN_PROGRESS \ + cpu_to_le32(0xC01E051B) +#define STATUS_GRAPHICS_I2C_NOT_SUPPORTED cpu_to_le32(0xC01E0580) +#define STATUS_GRAPHICS_I2C_DEVICE_DOES_NOT_EXIST cpu_to_le32(0xC01E0581) +#define STATUS_GRAPHICS_I2C_ERROR_TRANSMITTING_DATA cpu_to_le32(0xC01E0582) +#define STATUS_GRAPHICS_I2C_ERROR_RECEIVING_DATA cpu_to_le32(0xC01E0583) +#define STATUS_GRAPHICS_DDCCI_VCP_NOT_SUPPORTED cpu_to_le32(0xC01E0584) +#define STATUS_GRAPHICS_DDCCI_INVALID_DATA cpu_to_le32(0xC01E0585) +#define STATUS_GRAPHICS_DDCCI_MONITOR_RETURNED_INVALID_TIMING_STATUS_BYTE \ + cpu_to_le32(0xC01E0586) +#define STATUS_GRAPHICS_DDCCI_INVALID_CAPABILITIES_STRING \ + cpu_to_le32(0xC01E0587) +#define STATUS_GRAPHICS_MCA_INTERNAL_ERROR cpu_to_le32(0xC01E0588) +#define STATUS_GRAPHICS_DDCCI_INVALID_MESSAGE_COMMAND cpu_to_le32(0xC01E0589) +#define STATUS_GRAPHICS_DDCCI_INVALID_MESSAGE_LENGTH cpu_to_le32(0xC01E058A) +#define STATUS_GRAPHICS_DDCCI_INVALID_MESSAGE_CHECKSUM cpu_to_le32(0xC01E058B) +#define STATUS_GRAPHICS_INVALID_PHYSICAL_MONITOR_HANDLE cpu_to_le32(0xC01E058C) +#define STATUS_GRAPHICS_MONITOR_NO_LONGER_EXISTS cpu_to_le32(0xC01E058D) +#define STATUS_GRAPHICS_ONLY_CONSOLE_SESSION_SUPPORTED cpu_to_le32(0xC01E05E0) +#define STATUS_GRAPHICS_NO_DISPLAY_DEVICE_CORRESPONDS_TO_NAME \ + cpu_to_le32(0xC01E05E1) +#define STATUS_GRAPHICS_DISPLAY_DEVICE_NOT_ATTACHED_TO_DESKTOP \ + cpu_to_le32(0xC01E05E2) +#define STATUS_GRAPHICS_MIRRORING_DEVICES_NOT_SUPPORTED cpu_to_le32(0xC01E05E3) +#define STATUS_GRAPHICS_INVALID_POINTER cpu_to_le32(0xC01E05E4) +#define STATUS_GRAPHICS_NO_MONITORS_CORRESPOND_TO_DISPLAY_DEVICE \ + cpu_to_le32(0xC01E05E5) +#define STATUS_GRAPHICS_PARAMETER_ARRAY_TOO_SMALL cpu_to_le32(0xC01E05E6) +#define STATUS_GRAPHICS_INTERNAL_ERROR cpu_to_le32(0xC01E05E7) +#define STATUS_GRAPHICS_SESSION_TYPE_CHANGE_IN_PROGRESS cpu_to_le32(0xC01E05E8) +#define STATUS_FVE_LOCKED_VOLUME cpu_to_le32(0xC0210000) +#define STATUS_FVE_NOT_ENCRYPTED cpu_to_le32(0xC0210001) +#define STATUS_FVE_BAD_INFORMATION cpu_to_le32(0xC0210002) +#define STATUS_FVE_TOO_SMALL cpu_to_le32(0xC0210003) +#define STATUS_FVE_FAILED_WRONG_FS cpu_to_le32(0xC0210004) +#define STATUS_FVE_FAILED_BAD_FS cpu_to_le32(0xC0210005) +#define STATUS_FVE_FS_NOT_EXTENDED cpu_to_le32(0xC0210006) +#define STATUS_FVE_FS_MOUNTED cpu_to_le32(0xC0210007) +#define STATUS_FVE_NO_LICENSE cpu_to_le32(0xC0210008) +#define STATUS_FVE_ACTION_NOT_ALLOWED cpu_to_le32(0xC0210009) +#define STATUS_FVE_BAD_DATA cpu_to_le32(0xC021000A) +#define STATUS_FVE_VOLUME_NOT_BOUND cpu_to_le32(0xC021000B) +#define STATUS_FVE_NOT_DATA_VOLUME cpu_to_le32(0xC021000C) +#define STATUS_FVE_CONV_READ_ERROR cpu_to_le32(0xC021000D) +#define STATUS_FVE_CONV_WRITE_ERROR cpu_to_le32(0xC021000E) +#define STATUS_FVE_OVERLAPPED_UPDATE cpu_to_le32(0xC021000F) +#define STATUS_FVE_FAILED_SECTOR_SIZE cpu_to_le32(0xC0210010) +#define STATUS_FVE_FAILED_AUTHENTICATION cpu_to_le32(0xC0210011) +#define STATUS_FVE_NOT_OS_VOLUME cpu_to_le32(0xC0210012) +#define STATUS_FVE_KEYFILE_NOT_FOUND cpu_to_le32(0xC0210013) +#define STATUS_FVE_KEYFILE_INVALID cpu_to_le32(0xC0210014) +#define STATUS_FVE_KEYFILE_NO_VMK cpu_to_le32(0xC0210015) +#define STATUS_FVE_TPM_DISABLED cpu_to_le32(0xC0210016) +#define STATUS_FVE_TPM_SRK_AUTH_NOT_ZERO cpu_to_le32(0xC0210017) +#define STATUS_FVE_TPM_INVALID_PCR cpu_to_le32(0xC0210018) +#define STATUS_FVE_TPM_NO_VMK cpu_to_le32(0xC0210019) +#define STATUS_FVE_PIN_INVALID cpu_to_le32(0xC021001A) +#define STATUS_FVE_AUTH_INVALID_APPLICATION cpu_to_le32(0xC021001B) +#define STATUS_FVE_AUTH_INVALID_CONFIG cpu_to_le32(0xC021001C) +#define STATUS_FVE_DEBUGGER_ENABLED cpu_to_le32(0xC021001D) +#define STATUS_FVE_DRY_RUN_FAILED cpu_to_le32(0xC021001E) +#define STATUS_FVE_BAD_METADATA_POINTER cpu_to_le32(0xC021001F) +#define STATUS_FVE_OLD_METADATA_COPY cpu_to_le32(0xC0210020) +#define STATUS_FVE_REBOOT_REQUIRED cpu_to_le32(0xC0210021) +#define STATUS_FVE_RAW_ACCESS cpu_to_le32(0xC0210022) +#define STATUS_FVE_RAW_BLOCKED cpu_to_le32(0xC0210023) +#define STATUS_FWP_CALLOUT_NOT_FOUND cpu_to_le32(0xC0220001) +#define STATUS_FWP_CONDITION_NOT_FOUND cpu_to_le32(0xC0220002) +#define STATUS_FWP_FILTER_NOT_FOUND cpu_to_le32(0xC0220003) +#define STATUS_FWP_LAYER_NOT_FOUND cpu_to_le32(0xC0220004) +#define STATUS_FWP_PROVIDER_NOT_FOUND cpu_to_le32(0xC0220005) +#define STATUS_FWP_PROVIDER_CONTEXT_NOT_FOUND cpu_to_le32(0xC0220006) +#define STATUS_FWP_SUBLAYER_NOT_FOUND cpu_to_le32(0xC0220007) +#define STATUS_FWP_NOT_FOUND cpu_to_le32(0xC0220008) +#define STATUS_FWP_ALREADY_EXISTS cpu_to_le32(0xC0220009) +#define STATUS_FWP_IN_USE cpu_to_le32(0xC022000A) +#define STATUS_FWP_DYNAMIC_SESSION_IN_PROGRESS cpu_to_le32(0xC022000B) +#define STATUS_FWP_WRONG_SESSION cpu_to_le32(0xC022000C) +#define STATUS_FWP_NO_TXN_IN_PROGRESS cpu_to_le32(0xC022000D) +#define STATUS_FWP_TXN_IN_PROGRESS cpu_to_le32(0xC022000E) +#define STATUS_FWP_TXN_ABORTED cpu_to_le32(0xC022000F) +#define STATUS_FWP_SESSION_ABORTED cpu_to_le32(0xC0220010) +#define STATUS_FWP_INCOMPATIBLE_TXN cpu_to_le32(0xC0220011) +#define STATUS_FWP_TIMEOUT cpu_to_le32(0xC0220012) +#define STATUS_FWP_NET_EVENTS_DISABLED cpu_to_le32(0xC0220013) +#define STATUS_FWP_INCOMPATIBLE_LAYER cpu_to_le32(0xC0220014) +#define STATUS_FWP_KM_CLIENTS_ONLY cpu_to_le32(0xC0220015) +#define STATUS_FWP_LIFETIME_MISMATCH cpu_to_le32(0xC0220016) +#define STATUS_FWP_BUILTIN_OBJECT cpu_to_le32(0xC0220017) +#define STATUS_FWP_TOO_MANY_BOOTTIME_FILTERS cpu_to_le32(0xC0220018) +#define STATUS_FWP_TOO_MANY_CALLOUTS cpu_to_le32(0xC0220018) +#define STATUS_FWP_NOTIFICATION_DROPPED cpu_to_le32(0xC0220019) +#define STATUS_FWP_TRAFFIC_MISMATCH cpu_to_le32(0xC022001A) +#define STATUS_FWP_INCOMPATIBLE_SA_STATE cpu_to_le32(0xC022001B) +#define STATUS_FWP_NULL_POINTER cpu_to_le32(0xC022001C) +#define STATUS_FWP_INVALID_ENUMERATOR cpu_to_le32(0xC022001D) +#define STATUS_FWP_INVALID_FLAGS cpu_to_le32(0xC022001E) +#define STATUS_FWP_INVALID_NET_MASK cpu_to_le32(0xC022001F) +#define STATUS_FWP_INVALID_RANGE cpu_to_le32(0xC0220020) +#define STATUS_FWP_INVALID_INTERVAL cpu_to_le32(0xC0220021) +#define STATUS_FWP_ZERO_LENGTH_ARRAY cpu_to_le32(0xC0220022) +#define STATUS_FWP_NULL_DISPLAY_NAME cpu_to_le32(0xC0220023) +#define STATUS_FWP_INVALID_ACTION_TYPE cpu_to_le32(0xC0220024) +#define STATUS_FWP_INVALID_WEIGHT cpu_to_le32(0xC0220025) +#define STATUS_FWP_MATCH_TYPE_MISMATCH cpu_to_le32(0xC0220026) +#define STATUS_FWP_TYPE_MISMATCH cpu_to_le32(0xC0220027) +#define STATUS_FWP_OUT_OF_BOUNDS cpu_to_le32(0xC0220028) +#define STATUS_FWP_RESERVED cpu_to_le32(0xC0220029) +#define STATUS_FWP_DUPLICATE_CONDITION cpu_to_le32(0xC022002A) +#define STATUS_FWP_DUPLICATE_KEYMOD cpu_to_le32(0xC022002B) +#define STATUS_FWP_ACTION_INCOMPATIBLE_WITH_LAYER cpu_to_le32(0xC022002C) +#define STATUS_FWP_ACTION_INCOMPATIBLE_WITH_SUBLAYER cpu_to_le32(0xC022002D) +#define STATUS_FWP_CONTEXT_INCOMPATIBLE_WITH_LAYER cpu_to_le32(0xC022002E) +#define STATUS_FWP_CONTEXT_INCOMPATIBLE_WITH_CALLOUT cpu_to_le32(0xC022002F) +#define STATUS_FWP_INCOMPATIBLE_AUTH_METHOD cpu_to_le32(0xC0220030) +#define STATUS_FWP_INCOMPATIBLE_DH_GROUP cpu_to_le32(0xC0220031) +#define STATUS_FWP_EM_NOT_SUPPORTED cpu_to_le32(0xC0220032) +#define STATUS_FWP_NEVER_MATCH cpu_to_le32(0xC0220033) +#define STATUS_FWP_PROVIDER_CONTEXT_MISMATCH cpu_to_le32(0xC0220034) +#define STATUS_FWP_INVALID_PARAMETER cpu_to_le32(0xC0220035) +#define STATUS_FWP_TOO_MANY_SUBLAYERS cpu_to_le32(0xC0220036) +#define STATUS_FWP_CALLOUT_NOTIFICATION_FAILED cpu_to_le32(0xC0220037) +#define STATUS_FWP_INCOMPATIBLE_AUTH_CONFIG cpu_to_le32(0xC0220038) +#define STATUS_FWP_INCOMPATIBLE_CIPHER_CONFIG cpu_to_le32(0xC0220039) +#define STATUS_FWP_TCPIP_NOT_READY cpu_to_le32(0xC0220100) +#define STATUS_FWP_INJECT_HANDLE_CLOSING cpu_to_le32(0xC0220101) +#define STATUS_FWP_INJECT_HANDLE_STALE cpu_to_le32(0xC0220102) +#define STATUS_FWP_CANNOT_PEND cpu_to_le32(0xC0220103) +#define STATUS_NDIS_CLOSING cpu_to_le32(0xC0230002) +#define STATUS_NDIS_BAD_VERSION cpu_to_le32(0xC0230004) +#define STATUS_NDIS_BAD_CHARACTERISTICS cpu_to_le32(0xC0230005) +#define STATUS_NDIS_ADAPTER_NOT_FOUND cpu_to_le32(0xC0230006) +#define STATUS_NDIS_OPEN_FAILED cpu_to_le32(0xC0230007) +#define STATUS_NDIS_DEVICE_FAILED cpu_to_le32(0xC0230008) +#define STATUS_NDIS_MULTICAST_FULL cpu_to_le32(0xC0230009) +#define STATUS_NDIS_MULTICAST_EXISTS cpu_to_le32(0xC023000A) +#define STATUS_NDIS_MULTICAST_NOT_FOUND cpu_to_le32(0xC023000B) +#define STATUS_NDIS_REQUEST_ABORTED cpu_to_le32(0xC023000C) +#define STATUS_NDIS_RESET_IN_PROGRESS cpu_to_le32(0xC023000D) +#define STATUS_NDIS_INVALID_PACKET cpu_to_le32(0xC023000F) +#define STATUS_NDIS_INVALID_DEVICE_REQUEST cpu_to_le32(0xC0230010) +#define STATUS_NDIS_ADAPTER_NOT_READY cpu_to_le32(0xC0230011) +#define STATUS_NDIS_INVALID_LENGTH cpu_to_le32(0xC0230014) +#define STATUS_NDIS_INVALID_DATA cpu_to_le32(0xC0230015) +#define STATUS_NDIS_BUFFER_TOO_SHORT cpu_to_le32(0xC0230016) +#define STATUS_NDIS_INVALID_OID cpu_to_le32(0xC0230017) +#define STATUS_NDIS_ADAPTER_REMOVED cpu_to_le32(0xC0230018) +#define STATUS_NDIS_UNSUPPORTED_MEDIA cpu_to_le32(0xC0230019) +#define STATUS_NDIS_GROUP_ADDRESS_IN_USE cpu_to_le32(0xC023001A) +#define STATUS_NDIS_FILE_NOT_FOUND cpu_to_le32(0xC023001B) +#define STATUS_NDIS_ERROR_READING_FILE cpu_to_le32(0xC023001C) +#define STATUS_NDIS_ALREADY_MAPPED cpu_to_le32(0xC023001D) +#define STATUS_NDIS_RESOURCE_CONFLICT cpu_to_le32(0xC023001E) +#define STATUS_NDIS_MEDIA_DISCONNECTED cpu_to_le32(0xC023001F) +#define STATUS_NDIS_INVALID_ADDRESS cpu_to_le32(0xC0230022) +#define STATUS_NDIS_PAUSED cpu_to_le32(0xC023002A) +#define STATUS_NDIS_INTERFACE_NOT_FOUND cpu_to_le32(0xC023002B) +#define STATUS_NDIS_UNSUPPORTED_REVISION cpu_to_le32(0xC023002C) +#define STATUS_NDIS_INVALID_PORT cpu_to_le32(0xC023002D) +#define STATUS_NDIS_INVALID_PORT_STATE cpu_to_le32(0xC023002E) +#define STATUS_NDIS_LOW_POWER_STATE cpu_to_le32(0xC023002F) +#define STATUS_NDIS_NOT_SUPPORTED cpu_to_le32(0xC02300BB) +#define STATUS_NDIS_DOT11_AUTO_CONFIG_ENABLED cpu_to_le32(0xC0232000) +#define STATUS_NDIS_DOT11_MEDIA_IN_USE cpu_to_le32(0xC0232001) +#define STATUS_NDIS_DOT11_POWER_STATE_INVALID cpu_to_le32(0xC0232002) +#define STATUS_IPSEC_BAD_SPI cpu_to_le32(0xC0360001) +#define STATUS_IPSEC_SA_LIFETIME_EXPIRED cpu_to_le32(0xC0360002) +#define STATUS_IPSEC_WRONG_SA cpu_to_le32(0xC0360003) +#define STATUS_IPSEC_REPLAY_CHECK_FAILED cpu_to_le32(0xC0360004) +#define STATUS_IPSEC_INVALID_PACKET cpu_to_le32(0xC0360005) +#define STATUS_IPSEC_INTEGRITY_CHECK_FAILED cpu_to_le32(0xC0360006) +#define STATUS_IPSEC_CLEAR_TEXT_DROP cpu_to_le32(0xC0360007) + +#define STATUS_NO_PREAUTH_INTEGRITY_HASH_OVERLAP cpu_to_le32(0xC05D0000) +#define STATUS_INVALID_LOCK_RANGE cpu_to_le32(0xC00001a1) diff --git a/fs/smb/server/transport_ipc.c b/fs/smb/server/transport_ipc.c new file mode 100644 index 0000000000000..40c721f9227e4 --- /dev/null +++ b/fs/smb/server/transport_ipc.c @@ -0,0 +1,884 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Copyright (C) 2018 Samsung Electronics Co., Ltd. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "vfs_cache.h" +#include "transport_ipc.h" +#include "server.h" +#include "smb_common.h" + +#include "mgmt/user_config.h" +#include "mgmt/share_config.h" +#include "mgmt/user_session.h" +#include "mgmt/tree_connect.h" +#include "mgmt/ksmbd_ida.h" +#include "connection.h" +#include "transport_tcp.h" +#include "transport_rdma.h" + +#define IPC_WAIT_TIMEOUT (2 * HZ) + +#define IPC_MSG_HASH_BITS 3 +static DEFINE_HASHTABLE(ipc_msg_table, IPC_MSG_HASH_BITS); +static DECLARE_RWSEM(ipc_msg_table_lock); +static DEFINE_MUTEX(startup_lock); + +static DEFINE_IDA(ipc_ida); + +static unsigned int ksmbd_tools_pid; + +static bool ksmbd_ipc_validate_version(struct genl_info *m) +{ + if (m->genlhdr->version != KSMBD_GENL_VERSION) { + pr_err("%s. ksmbd: %d, kernel module: %d. %s.\n", + "Daemon and kernel module version mismatch", + m->genlhdr->version, + KSMBD_GENL_VERSION, + "User-space ksmbd should terminate"); + return false; + } + return true; +} + +struct ksmbd_ipc_msg { + unsigned int type; + unsigned int sz; + unsigned char payload[]; +}; + +struct ipc_msg_table_entry { + unsigned int handle; + unsigned int type; + wait_queue_head_t wait; + struct hlist_node ipc_table_hlist; + + void *response; +}; + +static struct delayed_work ipc_timer_work; + +static int handle_startup_event(struct sk_buff *skb, struct genl_info *info); +static int handle_unsupported_event(struct sk_buff *skb, struct genl_info *info); +static int handle_generic_event(struct sk_buff *skb, struct genl_info *info); +static int ksmbd_ipc_heartbeat_request(void); + +static const struct nla_policy ksmbd_nl_policy[KSMBD_EVENT_MAX] = { + [KSMBD_EVENT_UNSPEC] = { + .len = 0, + }, + [KSMBD_EVENT_HEARTBEAT_REQUEST] = { + .len = sizeof(struct ksmbd_heartbeat), + }, + [KSMBD_EVENT_STARTING_UP] = { + .len = sizeof(struct ksmbd_startup_request), + }, + [KSMBD_EVENT_SHUTTING_DOWN] = { + .len = sizeof(struct ksmbd_shutdown_request), + }, + [KSMBD_EVENT_LOGIN_REQUEST] = { + .len = sizeof(struct ksmbd_login_request), + }, + [KSMBD_EVENT_LOGIN_RESPONSE] = { + .len = sizeof(struct ksmbd_login_response), + }, + [KSMBD_EVENT_SHARE_CONFIG_REQUEST] = { + .len = sizeof(struct ksmbd_share_config_request), + }, + [KSMBD_EVENT_SHARE_CONFIG_RESPONSE] = { + .len = sizeof(struct ksmbd_share_config_response), + }, + [KSMBD_EVENT_TREE_CONNECT_REQUEST] = { + .len = sizeof(struct ksmbd_tree_connect_request), + }, + [KSMBD_EVENT_TREE_CONNECT_RESPONSE] = { + .len = sizeof(struct ksmbd_tree_connect_response), + }, + [KSMBD_EVENT_TREE_DISCONNECT_REQUEST] = { + .len = sizeof(struct ksmbd_tree_disconnect_request), + }, + [KSMBD_EVENT_LOGOUT_REQUEST] = { + .len = sizeof(struct ksmbd_logout_request), + }, + [KSMBD_EVENT_RPC_REQUEST] = { + }, + [KSMBD_EVENT_RPC_RESPONSE] = { + }, + [KSMBD_EVENT_SPNEGO_AUTHEN_REQUEST] = { + }, + [KSMBD_EVENT_SPNEGO_AUTHEN_RESPONSE] = { + }, +}; + +static struct genl_ops ksmbd_genl_ops[] = { + { + .cmd = KSMBD_EVENT_UNSPEC, + .doit = handle_unsupported_event, + }, + { + .cmd = KSMBD_EVENT_HEARTBEAT_REQUEST, + .doit = handle_unsupported_event, + }, + { + .cmd = KSMBD_EVENT_STARTING_UP, + .doit = handle_startup_event, + }, + { + .cmd = KSMBD_EVENT_SHUTTING_DOWN, + .doit = handle_unsupported_event, + }, + { + .cmd = KSMBD_EVENT_LOGIN_REQUEST, + .doit = handle_unsupported_event, + }, + { + .cmd = KSMBD_EVENT_LOGIN_RESPONSE, + .doit = handle_generic_event, + }, + { + .cmd = KSMBD_EVENT_SHARE_CONFIG_REQUEST, + .doit = handle_unsupported_event, + }, + { + .cmd = KSMBD_EVENT_SHARE_CONFIG_RESPONSE, + .doit = handle_generic_event, + }, + { + .cmd = KSMBD_EVENT_TREE_CONNECT_REQUEST, + .doit = handle_unsupported_event, + }, + { + .cmd = KSMBD_EVENT_TREE_CONNECT_RESPONSE, + .doit = handle_generic_event, + }, + { + .cmd = KSMBD_EVENT_TREE_DISCONNECT_REQUEST, + .doit = handle_unsupported_event, + }, + { + .cmd = KSMBD_EVENT_LOGOUT_REQUEST, + .doit = handle_unsupported_event, + }, + { + .cmd = KSMBD_EVENT_RPC_REQUEST, + .doit = handle_unsupported_event, + }, + { + .cmd = KSMBD_EVENT_RPC_RESPONSE, + .doit = handle_generic_event, + }, + { + .cmd = KSMBD_EVENT_SPNEGO_AUTHEN_REQUEST, + .doit = handle_unsupported_event, + }, + { + .cmd = KSMBD_EVENT_SPNEGO_AUTHEN_RESPONSE, + .doit = handle_generic_event, + }, +}; + +static struct genl_family ksmbd_genl_family = { + .name = KSMBD_GENL_NAME, + .version = KSMBD_GENL_VERSION, + .hdrsize = 0, + .maxattr = KSMBD_EVENT_MAX, + .netnsok = true, + .module = THIS_MODULE, + .ops = ksmbd_genl_ops, + .n_ops = ARRAY_SIZE(ksmbd_genl_ops), + .resv_start_op = KSMBD_EVENT_SPNEGO_AUTHEN_RESPONSE + 1, +}; + +static void ksmbd_nl_init_fixup(void) +{ + int i; + + for (i = 0; i < ARRAY_SIZE(ksmbd_genl_ops); i++) + ksmbd_genl_ops[i].validate = GENL_DONT_VALIDATE_STRICT | + GENL_DONT_VALIDATE_DUMP; + + ksmbd_genl_family.policy = ksmbd_nl_policy; +} + +static int rpc_context_flags(struct ksmbd_session *sess) +{ + if (user_guest(sess->user)) + return KSMBD_RPC_RESTRICTED_CONTEXT; + return 0; +} + +static void ipc_update_last_active(void) +{ + if (server_conf.ipc_timeout) + server_conf.ipc_last_active = jiffies; +} + +static struct ksmbd_ipc_msg *ipc_msg_alloc(size_t sz) +{ + struct ksmbd_ipc_msg *msg; + size_t msg_sz = sz + sizeof(struct ksmbd_ipc_msg); + + msg = kvmalloc(msg_sz, GFP_KERNEL | __GFP_ZERO); + if (msg) + msg->sz = sz; + return msg; +} + +static void ipc_msg_free(struct ksmbd_ipc_msg *msg) +{ + kvfree(msg); +} + +static void ipc_msg_handle_free(int handle) +{ + if (handle >= 0) + ksmbd_release_id(&ipc_ida, handle); +} + +static int handle_response(int type, void *payload, size_t sz) +{ + unsigned int handle = *(unsigned int *)payload; + struct ipc_msg_table_entry *entry; + int ret = 0; + + ipc_update_last_active(); + down_read(&ipc_msg_table_lock); + hash_for_each_possible(ipc_msg_table, entry, ipc_table_hlist, handle) { + if (handle != entry->handle) + continue; + + entry->response = NULL; + /* + * Response message type value should be equal to + * request message type + 1. + */ + if (entry->type + 1 != type) { + pr_err("Waiting for IPC type %d, got %d. Ignore.\n", + entry->type + 1, type); + } + + entry->response = kvmalloc(sz, GFP_KERNEL | __GFP_ZERO); + if (!entry->response) { + ret = -ENOMEM; + break; + } + + memcpy(entry->response, payload, sz); + wake_up_interruptible(&entry->wait); + ret = 0; + break; + } + up_read(&ipc_msg_table_lock); + + return ret; +} + +static int ipc_server_config_on_startup(struct ksmbd_startup_request *req) +{ + int ret; + + ksmbd_set_fd_limit(req->file_max); + server_conf.flags = req->flags; + server_conf.signing = req->signing; + server_conf.tcp_port = req->tcp_port; + server_conf.ipc_timeout = req->ipc_timeout * HZ; + server_conf.deadtime = req->deadtime * SMB_ECHO_INTERVAL; + server_conf.share_fake_fscaps = req->share_fake_fscaps; + ksmbd_init_domain(req->sub_auth); + + if (req->smb2_max_read) + init_smb2_max_read_size(req->smb2_max_read); + if (req->smb2_max_write) + init_smb2_max_write_size(req->smb2_max_write); + if (req->smb2_max_trans) + init_smb2_max_trans_size(req->smb2_max_trans); + if (req->smb2_max_credits) + init_smb2_max_credits(req->smb2_max_credits); + if (req->smbd_max_io_size) + init_smbd_max_io_size(req->smbd_max_io_size); + + if (req->max_connections) + server_conf.max_connections = req->max_connections; + + ret = ksmbd_set_netbios_name(req->netbios_name); + ret |= ksmbd_set_server_string(req->server_string); + ret |= ksmbd_set_work_group(req->work_group); + ret |= ksmbd_tcp_set_interfaces(KSMBD_STARTUP_CONFIG_INTERFACES(req), + req->ifc_list_sz); + if (ret) { + pr_err("Server configuration error: %s %s %s\n", + req->netbios_name, req->server_string, + req->work_group); + return ret; + } + + if (req->min_prot[0]) { + ret = ksmbd_lookup_protocol_idx(req->min_prot); + if (ret >= 0) + server_conf.min_protocol = ret; + } + if (req->max_prot[0]) { + ret = ksmbd_lookup_protocol_idx(req->max_prot); + if (ret >= 0) + server_conf.max_protocol = ret; + } + + if (server_conf.ipc_timeout) + schedule_delayed_work(&ipc_timer_work, server_conf.ipc_timeout); + return 0; +} + +static int handle_startup_event(struct sk_buff *skb, struct genl_info *info) +{ + int ret = 0; + +#ifdef CONFIG_SMB_SERVER_CHECK_CAP_NET_ADMIN + if (!netlink_capable(skb, CAP_NET_ADMIN)) + return -EPERM; +#endif + + if (!ksmbd_ipc_validate_version(info)) + return -EINVAL; + + if (!info->attrs[KSMBD_EVENT_STARTING_UP]) + return -EINVAL; + + mutex_lock(&startup_lock); + if (!ksmbd_server_configurable()) { + mutex_unlock(&startup_lock); + pr_err("Server reset is in progress, can't start daemon\n"); + return -EINVAL; + } + + if (ksmbd_tools_pid) { + if (ksmbd_ipc_heartbeat_request() == 0) { + ret = -EINVAL; + goto out; + } + + pr_err("Reconnect to a new user space daemon\n"); + } else { + struct ksmbd_startup_request *req; + + req = nla_data(info->attrs[info->genlhdr->cmd]); + ret = ipc_server_config_on_startup(req); + if (ret) + goto out; + server_queue_ctrl_init_work(); + } + + ksmbd_tools_pid = info->snd_portid; + ipc_update_last_active(); + +out: + mutex_unlock(&startup_lock); + return ret; +} + +static int handle_unsupported_event(struct sk_buff *skb, struct genl_info *info) +{ + pr_err("Unknown IPC event: %d, ignore.\n", info->genlhdr->cmd); + return -EINVAL; +} + +static int handle_generic_event(struct sk_buff *skb, struct genl_info *info) +{ + void *payload; + int sz; + int type = info->genlhdr->cmd; + +#ifdef CONFIG_SMB_SERVER_CHECK_CAP_NET_ADMIN + if (!netlink_capable(skb, CAP_NET_ADMIN)) + return -EPERM; +#endif + + if (type >= KSMBD_EVENT_MAX) { + WARN_ON(1); + return -EINVAL; + } + + if (!ksmbd_ipc_validate_version(info)) + return -EINVAL; + + if (!info->attrs[type]) + return -EINVAL; + + payload = nla_data(info->attrs[info->genlhdr->cmd]); + sz = nla_len(info->attrs[info->genlhdr->cmd]); + return handle_response(type, payload, sz); +} + +static int ipc_msg_send(struct ksmbd_ipc_msg *msg) +{ + struct genlmsghdr *nlh; + struct sk_buff *skb; + int ret = -EINVAL; + + if (!ksmbd_tools_pid) + return ret; + + skb = genlmsg_new(msg->sz, GFP_KERNEL); + if (!skb) + return -ENOMEM; + + nlh = genlmsg_put(skb, 0, 0, &ksmbd_genl_family, 0, msg->type); + if (!nlh) + goto out; + + ret = nla_put(skb, msg->type, msg->sz, msg->payload); + if (ret) { + genlmsg_cancel(skb, nlh); + goto out; + } + + genlmsg_end(skb, nlh); + ret = genlmsg_unicast(&init_net, skb, ksmbd_tools_pid); + if (!ret) + ipc_update_last_active(); + return ret; + +out: + nlmsg_free(skb); + return ret; +} + +static void *ipc_msg_send_request(struct ksmbd_ipc_msg *msg, unsigned int handle) +{ + struct ipc_msg_table_entry entry; + int ret; + + if ((int)handle < 0) + return NULL; + + entry.type = msg->type; + entry.response = NULL; + init_waitqueue_head(&entry.wait); + + down_write(&ipc_msg_table_lock); + entry.handle = handle; + hash_add(ipc_msg_table, &entry.ipc_table_hlist, entry.handle); + up_write(&ipc_msg_table_lock); + + ret = ipc_msg_send(msg); + if (ret) + goto out; + + ret = wait_event_interruptible_timeout(entry.wait, + entry.response != NULL, + IPC_WAIT_TIMEOUT); +out: + down_write(&ipc_msg_table_lock); + hash_del(&entry.ipc_table_hlist); + up_write(&ipc_msg_table_lock); + return entry.response; +} + +static int ksmbd_ipc_heartbeat_request(void) +{ + struct ksmbd_ipc_msg *msg; + int ret; + + msg = ipc_msg_alloc(sizeof(struct ksmbd_heartbeat)); + if (!msg) + return -EINVAL; + + msg->type = KSMBD_EVENT_HEARTBEAT_REQUEST; + ret = ipc_msg_send(msg); + ipc_msg_free(msg); + return ret; +} + +struct ksmbd_login_response *ksmbd_ipc_login_request(const char *account) +{ + struct ksmbd_ipc_msg *msg; + struct ksmbd_login_request *req; + struct ksmbd_login_response *resp; + + if (strlen(account) >= KSMBD_REQ_MAX_ACCOUNT_NAME_SZ) + return NULL; + + msg = ipc_msg_alloc(sizeof(struct ksmbd_login_request)); + if (!msg) + return NULL; + + msg->type = KSMBD_EVENT_LOGIN_REQUEST; + req = (struct ksmbd_login_request *)msg->payload; + req->handle = ksmbd_acquire_id(&ipc_ida); + strscpy(req->account, account, KSMBD_REQ_MAX_ACCOUNT_NAME_SZ); + + resp = ipc_msg_send_request(msg, req->handle); + ipc_msg_handle_free(req->handle); + ipc_msg_free(msg); + return resp; +} + +struct ksmbd_spnego_authen_response * +ksmbd_ipc_spnego_authen_request(const char *spnego_blob, int blob_len) +{ + struct ksmbd_ipc_msg *msg; + struct ksmbd_spnego_authen_request *req; + struct ksmbd_spnego_authen_response *resp; + + msg = ipc_msg_alloc(sizeof(struct ksmbd_spnego_authen_request) + + blob_len + 1); + if (!msg) + return NULL; + + msg->type = KSMBD_EVENT_SPNEGO_AUTHEN_REQUEST; + req = (struct ksmbd_spnego_authen_request *)msg->payload; + req->handle = ksmbd_acquire_id(&ipc_ida); + req->spnego_blob_len = blob_len; + memcpy(req->spnego_blob, spnego_blob, blob_len); + + resp = ipc_msg_send_request(msg, req->handle); + ipc_msg_handle_free(req->handle); + ipc_msg_free(msg); + return resp; +} + +struct ksmbd_tree_connect_response * +ksmbd_ipc_tree_connect_request(struct ksmbd_session *sess, + struct ksmbd_share_config *share, + struct ksmbd_tree_connect *tree_conn, + struct sockaddr *peer_addr) +{ + struct ksmbd_ipc_msg *msg; + struct ksmbd_tree_connect_request *req; + struct ksmbd_tree_connect_response *resp; + + if (strlen(user_name(sess->user)) >= KSMBD_REQ_MAX_ACCOUNT_NAME_SZ) + return NULL; + + if (strlen(share->name) >= KSMBD_REQ_MAX_SHARE_NAME) + return NULL; + + msg = ipc_msg_alloc(sizeof(struct ksmbd_tree_connect_request)); + if (!msg) + return NULL; + + msg->type = KSMBD_EVENT_TREE_CONNECT_REQUEST; + req = (struct ksmbd_tree_connect_request *)msg->payload; + + req->handle = ksmbd_acquire_id(&ipc_ida); + req->account_flags = sess->user->flags; + req->session_id = sess->id; + req->connect_id = tree_conn->id; + strscpy(req->account, user_name(sess->user), KSMBD_REQ_MAX_ACCOUNT_NAME_SZ); + strscpy(req->share, share->name, KSMBD_REQ_MAX_SHARE_NAME); + snprintf(req->peer_addr, sizeof(req->peer_addr), "%pIS", peer_addr); + + if (peer_addr->sa_family == AF_INET6) + req->flags |= KSMBD_TREE_CONN_FLAG_REQUEST_IPV6; + if (test_session_flag(sess, CIFDS_SESSION_FLAG_SMB2)) + req->flags |= KSMBD_TREE_CONN_FLAG_REQUEST_SMB2; + + resp = ipc_msg_send_request(msg, req->handle); + ipc_msg_handle_free(req->handle); + ipc_msg_free(msg); + return resp; +} + +int ksmbd_ipc_tree_disconnect_request(unsigned long long session_id, + unsigned long long connect_id) +{ + struct ksmbd_ipc_msg *msg; + struct ksmbd_tree_disconnect_request *req; + int ret; + + msg = ipc_msg_alloc(sizeof(struct ksmbd_tree_disconnect_request)); + if (!msg) + return -ENOMEM; + + msg->type = KSMBD_EVENT_TREE_DISCONNECT_REQUEST; + req = (struct ksmbd_tree_disconnect_request *)msg->payload; + req->session_id = session_id; + req->connect_id = connect_id; + + ret = ipc_msg_send(msg); + ipc_msg_free(msg); + return ret; +} + +int ksmbd_ipc_logout_request(const char *account, int flags) +{ + struct ksmbd_ipc_msg *msg; + struct ksmbd_logout_request *req; + int ret; + + if (strlen(account) >= KSMBD_REQ_MAX_ACCOUNT_NAME_SZ) + return -EINVAL; + + msg = ipc_msg_alloc(sizeof(struct ksmbd_logout_request)); + if (!msg) + return -ENOMEM; + + msg->type = KSMBD_EVENT_LOGOUT_REQUEST; + req = (struct ksmbd_logout_request *)msg->payload; + req->account_flags = flags; + strscpy(req->account, account, KSMBD_REQ_MAX_ACCOUNT_NAME_SZ); + + ret = ipc_msg_send(msg); + ipc_msg_free(msg); + return ret; +} + +struct ksmbd_share_config_response * +ksmbd_ipc_share_config_request(const char *name) +{ + struct ksmbd_ipc_msg *msg; + struct ksmbd_share_config_request *req; + struct ksmbd_share_config_response *resp; + + if (strlen(name) >= KSMBD_REQ_MAX_SHARE_NAME) + return NULL; + + msg = ipc_msg_alloc(sizeof(struct ksmbd_share_config_request)); + if (!msg) + return NULL; + + msg->type = KSMBD_EVENT_SHARE_CONFIG_REQUEST; + req = (struct ksmbd_share_config_request *)msg->payload; + req->handle = ksmbd_acquire_id(&ipc_ida); + strscpy(req->share_name, name, KSMBD_REQ_MAX_SHARE_NAME); + + resp = ipc_msg_send_request(msg, req->handle); + ipc_msg_handle_free(req->handle); + ipc_msg_free(msg); + return resp; +} + +struct ksmbd_rpc_command *ksmbd_rpc_open(struct ksmbd_session *sess, int handle) +{ + struct ksmbd_ipc_msg *msg; + struct ksmbd_rpc_command *req; + struct ksmbd_rpc_command *resp; + + msg = ipc_msg_alloc(sizeof(struct ksmbd_rpc_command)); + if (!msg) + return NULL; + + msg->type = KSMBD_EVENT_RPC_REQUEST; + req = (struct ksmbd_rpc_command *)msg->payload; + req->handle = handle; + req->flags = ksmbd_session_rpc_method(sess, handle); + req->flags |= KSMBD_RPC_OPEN_METHOD; + req->payload_sz = 0; + + resp = ipc_msg_send_request(msg, req->handle); + ipc_msg_free(msg); + return resp; +} + +struct ksmbd_rpc_command *ksmbd_rpc_close(struct ksmbd_session *sess, int handle) +{ + struct ksmbd_ipc_msg *msg; + struct ksmbd_rpc_command *req; + struct ksmbd_rpc_command *resp; + + msg = ipc_msg_alloc(sizeof(struct ksmbd_rpc_command)); + if (!msg) + return NULL; + + msg->type = KSMBD_EVENT_RPC_REQUEST; + req = (struct ksmbd_rpc_command *)msg->payload; + req->handle = handle; + req->flags = ksmbd_session_rpc_method(sess, handle); + req->flags |= KSMBD_RPC_CLOSE_METHOD; + req->payload_sz = 0; + + resp = ipc_msg_send_request(msg, req->handle); + ipc_msg_free(msg); + return resp; +} + +struct ksmbd_rpc_command *ksmbd_rpc_write(struct ksmbd_session *sess, int handle, + void *payload, size_t payload_sz) +{ + struct ksmbd_ipc_msg *msg; + struct ksmbd_rpc_command *req; + struct ksmbd_rpc_command *resp; + + msg = ipc_msg_alloc(sizeof(struct ksmbd_rpc_command) + payload_sz + 1); + if (!msg) + return NULL; + + msg->type = KSMBD_EVENT_RPC_REQUEST; + req = (struct ksmbd_rpc_command *)msg->payload; + req->handle = handle; + req->flags = ksmbd_session_rpc_method(sess, handle); + req->flags |= rpc_context_flags(sess); + req->flags |= KSMBD_RPC_WRITE_METHOD; + req->payload_sz = payload_sz; + memcpy(req->payload, payload, payload_sz); + + resp = ipc_msg_send_request(msg, req->handle); + ipc_msg_free(msg); + return resp; +} + +struct ksmbd_rpc_command *ksmbd_rpc_read(struct ksmbd_session *sess, int handle) +{ + struct ksmbd_ipc_msg *msg; + struct ksmbd_rpc_command *req; + struct ksmbd_rpc_command *resp; + + msg = ipc_msg_alloc(sizeof(struct ksmbd_rpc_command)); + if (!msg) + return NULL; + + msg->type = KSMBD_EVENT_RPC_REQUEST; + req = (struct ksmbd_rpc_command *)msg->payload; + req->handle = handle; + req->flags = ksmbd_session_rpc_method(sess, handle); + req->flags |= rpc_context_flags(sess); + req->flags |= KSMBD_RPC_READ_METHOD; + req->payload_sz = 0; + + resp = ipc_msg_send_request(msg, req->handle); + ipc_msg_free(msg); + return resp; +} + +struct ksmbd_rpc_command *ksmbd_rpc_ioctl(struct ksmbd_session *sess, int handle, + void *payload, size_t payload_sz) +{ + struct ksmbd_ipc_msg *msg; + struct ksmbd_rpc_command *req; + struct ksmbd_rpc_command *resp; + + msg = ipc_msg_alloc(sizeof(struct ksmbd_rpc_command) + payload_sz + 1); + if (!msg) + return NULL; + + msg->type = KSMBD_EVENT_RPC_REQUEST; + req = (struct ksmbd_rpc_command *)msg->payload; + req->handle = handle; + req->flags = ksmbd_session_rpc_method(sess, handle); + req->flags |= rpc_context_flags(sess); + req->flags |= KSMBD_RPC_IOCTL_METHOD; + req->payload_sz = payload_sz; + memcpy(req->payload, payload, payload_sz); + + resp = ipc_msg_send_request(msg, req->handle); + ipc_msg_free(msg); + return resp; +} + +struct ksmbd_rpc_command *ksmbd_rpc_rap(struct ksmbd_session *sess, void *payload, + size_t payload_sz) +{ + struct ksmbd_ipc_msg *msg; + struct ksmbd_rpc_command *req; + struct ksmbd_rpc_command *resp; + + msg = ipc_msg_alloc(sizeof(struct ksmbd_rpc_command) + payload_sz + 1); + if (!msg) + return NULL; + + msg->type = KSMBD_EVENT_RPC_REQUEST; + req = (struct ksmbd_rpc_command *)msg->payload; + req->handle = ksmbd_acquire_id(&ipc_ida); + req->flags = rpc_context_flags(sess); + req->flags |= KSMBD_RPC_RAP_METHOD; + req->payload_sz = payload_sz; + memcpy(req->payload, payload, payload_sz); + + resp = ipc_msg_send_request(msg, req->handle); + ipc_msg_handle_free(req->handle); + ipc_msg_free(msg); + return resp; +} + +static int __ipc_heartbeat(void) +{ + unsigned long delta; + + if (!ksmbd_server_running()) + return 0; + + if (time_after(jiffies, server_conf.ipc_last_active)) { + delta = (jiffies - server_conf.ipc_last_active); + } else { + ipc_update_last_active(); + schedule_delayed_work(&ipc_timer_work, + server_conf.ipc_timeout); + return 0; + } + + if (delta < server_conf.ipc_timeout) { + schedule_delayed_work(&ipc_timer_work, + server_conf.ipc_timeout - delta); + return 0; + } + + if (ksmbd_ipc_heartbeat_request() == 0) { + schedule_delayed_work(&ipc_timer_work, + server_conf.ipc_timeout); + return 0; + } + + mutex_lock(&startup_lock); + WRITE_ONCE(server_conf.state, SERVER_STATE_RESETTING); + server_conf.ipc_last_active = 0; + ksmbd_tools_pid = 0; + pr_err("No IPC daemon response for %lus\n", delta / HZ); + mutex_unlock(&startup_lock); + return -EINVAL; +} + +static void ipc_timer_heartbeat(struct work_struct *w) +{ + if (__ipc_heartbeat()) + server_queue_ctrl_reset_work(); +} + +int ksmbd_ipc_id_alloc(void) +{ + return ksmbd_acquire_id(&ipc_ida); +} + +void ksmbd_rpc_id_free(int handle) +{ + ksmbd_release_id(&ipc_ida, handle); +} + +void ksmbd_ipc_release(void) +{ + cancel_delayed_work_sync(&ipc_timer_work); + genl_unregister_family(&ksmbd_genl_family); +} + +void ksmbd_ipc_soft_reset(void) +{ + mutex_lock(&startup_lock); + ksmbd_tools_pid = 0; + cancel_delayed_work_sync(&ipc_timer_work); + mutex_unlock(&startup_lock); +} + +int ksmbd_ipc_init(void) +{ + int ret = 0; + + ksmbd_nl_init_fixup(); + INIT_DELAYED_WORK(&ipc_timer_work, ipc_timer_heartbeat); + + ret = genl_register_family(&ksmbd_genl_family); + if (ret) { + pr_err("Failed to register KSMBD netlink interface %d\n", ret); + cancel_delayed_work_sync(&ipc_timer_work); + } + + return ret; +} diff --git a/fs/smb/server/transport_ipc.h b/fs/smb/server/transport_ipc.h new file mode 100644 index 0000000000000..5e5b90a0c1879 --- /dev/null +++ b/fs/smb/server/transport_ipc.h @@ -0,0 +1,47 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + * Copyright (C) 2018 Samsung Electronics Co., Ltd. + */ + +#ifndef __KSMBD_TRANSPORT_IPC_H__ +#define __KSMBD_TRANSPORT_IPC_H__ + +#include + +#define KSMBD_IPC_MAX_PAYLOAD 4096 + +struct ksmbd_login_response * +ksmbd_ipc_login_request(const char *account); + +struct ksmbd_session; +struct ksmbd_share_config; +struct ksmbd_tree_connect; +struct sockaddr; + +struct ksmbd_tree_connect_response * +ksmbd_ipc_tree_connect_request(struct ksmbd_session *sess, + struct ksmbd_share_config *share, + struct ksmbd_tree_connect *tree_conn, + struct sockaddr *peer_addr); +int ksmbd_ipc_tree_disconnect_request(unsigned long long session_id, + unsigned long long connect_id); +int ksmbd_ipc_logout_request(const char *account, int flags); +struct ksmbd_share_config_response * +ksmbd_ipc_share_config_request(const char *name); +struct ksmbd_spnego_authen_response * +ksmbd_ipc_spnego_authen_request(const char *spnego_blob, int blob_len); +int ksmbd_ipc_id_alloc(void); +void ksmbd_rpc_id_free(int handle); +struct ksmbd_rpc_command *ksmbd_rpc_open(struct ksmbd_session *sess, int handle); +struct ksmbd_rpc_command *ksmbd_rpc_close(struct ksmbd_session *sess, int handle); +struct ksmbd_rpc_command *ksmbd_rpc_write(struct ksmbd_session *sess, int handle, + void *payload, size_t payload_sz); +struct ksmbd_rpc_command *ksmbd_rpc_read(struct ksmbd_session *sess, int handle); +struct ksmbd_rpc_command *ksmbd_rpc_ioctl(struct ksmbd_session *sess, int handle, + void *payload, size_t payload_sz); +struct ksmbd_rpc_command *ksmbd_rpc_rap(struct ksmbd_session *sess, void *payload, + size_t payload_sz); +void ksmbd_ipc_release(void); +void ksmbd_ipc_soft_reset(void); +int ksmbd_ipc_init(void); +#endif /* __KSMBD_TRANSPORT_IPC_H__ */ diff --git a/fs/smb/server/transport_rdma.c b/fs/smb/server/transport_rdma.c new file mode 100644 index 0000000000000..c06efc020bd95 --- /dev/null +++ b/fs/smb/server/transport_rdma.c @@ -0,0 +1,2273 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Copyright (C) 2017, Microsoft Corporation. + * Copyright (C) 2018, LG Electronics. + * + * Author(s): Long Li , + * Hyunchul Lee + */ + +#define SUBMOD_NAME "smb_direct" + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "glob.h" +#include "connection.h" +#include "smb_common.h" +#include "smbstatus.h" +#include "transport_rdma.h" + +#define SMB_DIRECT_PORT_IWARP 5445 +#define SMB_DIRECT_PORT_INFINIBAND 445 + +#define SMB_DIRECT_VERSION_LE cpu_to_le16(0x0100) + +/* SMB_DIRECT negotiation timeout in seconds */ +#define SMB_DIRECT_NEGOTIATE_TIMEOUT 120 + +#define SMB_DIRECT_MAX_SEND_SGES 6 +#define SMB_DIRECT_MAX_RECV_SGES 1 + +/* + * Default maximum number of RDMA read/write outstanding on this connection + * This value is possibly decreased during QP creation on hardware limit + */ +#define SMB_DIRECT_CM_INITIATOR_DEPTH 8 + +/* Maximum number of retries on data transfer operations */ +#define SMB_DIRECT_CM_RETRY 6 +/* No need to retry on Receiver Not Ready since SMB_DIRECT manages credits */ +#define SMB_DIRECT_CM_RNR_RETRY 0 + +/* + * User configurable initial values per SMB_DIRECT transport connection + * as defined in [MS-SMBD] 3.1.1.1 + * Those may change after a SMB_DIRECT negotiation + */ + +/* Set 445 port to SMB Direct port by default */ +static int smb_direct_port = SMB_DIRECT_PORT_INFINIBAND; + +/* The local peer's maximum number of credits to grant to the peer */ +static int smb_direct_receive_credit_max = 255; + +/* The remote peer's credit request of local peer */ +static int smb_direct_send_credit_target = 255; + +/* The maximum single message size can be sent to remote peer */ +static int smb_direct_max_send_size = 1364; + +/* The maximum fragmented upper-layer payload receive size supported */ +static int smb_direct_max_fragmented_recv_size = 1024 * 1024; + +/* The maximum single-message size which can be received */ +static int smb_direct_max_receive_size = 1364; + +static int smb_direct_max_read_write_size = SMBD_DEFAULT_IOSIZE; + +static LIST_HEAD(smb_direct_device_list); +static DEFINE_RWLOCK(smb_direct_device_lock); + +struct smb_direct_device { + struct ib_device *ib_dev; + struct list_head list; +}; + +static struct smb_direct_listener { + struct rdma_cm_id *cm_id; +} smb_direct_listener; + +static struct workqueue_struct *smb_direct_wq; + +enum smb_direct_status { + SMB_DIRECT_CS_NEW = 0, + SMB_DIRECT_CS_CONNECTED, + SMB_DIRECT_CS_DISCONNECTING, + SMB_DIRECT_CS_DISCONNECTED, +}; + +struct smb_direct_transport { + struct ksmbd_transport transport; + + enum smb_direct_status status; + bool full_packet_received; + wait_queue_head_t wait_status; + + struct rdma_cm_id *cm_id; + struct ib_cq *send_cq; + struct ib_cq *recv_cq; + struct ib_pd *pd; + struct ib_qp *qp; + + int max_send_size; + int max_recv_size; + int max_fragmented_send_size; + int max_fragmented_recv_size; + int max_rdma_rw_size; + + spinlock_t reassembly_queue_lock; + struct list_head reassembly_queue; + int reassembly_data_length; + int reassembly_queue_length; + int first_entry_offset; + wait_queue_head_t wait_reassembly_queue; + + spinlock_t receive_credit_lock; + int recv_credits; + int count_avail_recvmsg; + int recv_credit_max; + int recv_credit_target; + + spinlock_t recvmsg_queue_lock; + struct list_head recvmsg_queue; + + spinlock_t empty_recvmsg_queue_lock; + struct list_head empty_recvmsg_queue; + + int send_credit_target; + atomic_t send_credits; + spinlock_t lock_new_recv_credits; + int new_recv_credits; + int max_rw_credits; + int pages_per_rw_credit; + atomic_t rw_credits; + + wait_queue_head_t wait_send_credits; + wait_queue_head_t wait_rw_credits; + + mempool_t *sendmsg_mempool; + struct kmem_cache *sendmsg_cache; + mempool_t *recvmsg_mempool; + struct kmem_cache *recvmsg_cache; + + wait_queue_head_t wait_send_pending; + atomic_t send_pending; + + struct delayed_work post_recv_credits_work; + struct work_struct send_immediate_work; + struct work_struct disconnect_work; + + bool negotiation_requested; +}; + +#define KSMBD_TRANS(t) ((struct ksmbd_transport *)&((t)->transport)) + +enum { + SMB_DIRECT_MSG_NEGOTIATE_REQ = 0, + SMB_DIRECT_MSG_DATA_TRANSFER +}; + +static struct ksmbd_transport_ops ksmbd_smb_direct_transport_ops; + +struct smb_direct_send_ctx { + struct list_head msg_list; + int wr_cnt; + bool need_invalidate_rkey; + unsigned int remote_key; +}; + +struct smb_direct_sendmsg { + struct smb_direct_transport *transport; + struct ib_send_wr wr; + struct list_head list; + int num_sge; + struct ib_sge sge[SMB_DIRECT_MAX_SEND_SGES]; + struct ib_cqe cqe; + u8 packet[]; +}; + +struct smb_direct_recvmsg { + struct smb_direct_transport *transport; + struct list_head list; + int type; + struct ib_sge sge; + struct ib_cqe cqe; + bool first_segment; + u8 packet[]; +}; + +struct smb_direct_rdma_rw_msg { + struct smb_direct_transport *t; + struct ib_cqe cqe; + int status; + struct completion *completion; + struct list_head list; + struct rdma_rw_ctx rw_ctx; + struct sg_table sgt; + struct scatterlist sg_list[]; +}; + +void init_smbd_max_io_size(unsigned int sz) +{ + sz = clamp_val(sz, SMBD_MIN_IOSIZE, SMBD_MAX_IOSIZE); + smb_direct_max_read_write_size = sz; +} + +unsigned int get_smbd_max_read_write_size(void) +{ + return smb_direct_max_read_write_size; +} + +static inline int get_buf_page_count(void *buf, int size) +{ + return DIV_ROUND_UP((uintptr_t)buf + size, PAGE_SIZE) - + (uintptr_t)buf / PAGE_SIZE; +} + +static void smb_direct_destroy_pools(struct smb_direct_transport *transport); +static void smb_direct_post_recv_credits(struct work_struct *work); +static int smb_direct_post_send_data(struct smb_direct_transport *t, + struct smb_direct_send_ctx *send_ctx, + struct kvec *iov, int niov, + int remaining_data_length); + +static inline struct smb_direct_transport * +smb_trans_direct_transfort(struct ksmbd_transport *t) +{ + return container_of(t, struct smb_direct_transport, transport); +} + +static inline void +*smb_direct_recvmsg_payload(struct smb_direct_recvmsg *recvmsg) +{ + return (void *)recvmsg->packet; +} + +static inline bool is_receive_credit_post_required(int receive_credits, + int avail_recvmsg_count) +{ + return receive_credits <= (smb_direct_receive_credit_max >> 3) && + avail_recvmsg_count >= (receive_credits >> 2); +} + +static struct +smb_direct_recvmsg *get_free_recvmsg(struct smb_direct_transport *t) +{ + struct smb_direct_recvmsg *recvmsg = NULL; + + spin_lock(&t->recvmsg_queue_lock); + if (!list_empty(&t->recvmsg_queue)) { + recvmsg = list_first_entry(&t->recvmsg_queue, + struct smb_direct_recvmsg, + list); + list_del(&recvmsg->list); + } + spin_unlock(&t->recvmsg_queue_lock); + return recvmsg; +} + +static void put_recvmsg(struct smb_direct_transport *t, + struct smb_direct_recvmsg *recvmsg) +{ + ib_dma_unmap_single(t->cm_id->device, recvmsg->sge.addr, + recvmsg->sge.length, DMA_FROM_DEVICE); + + spin_lock(&t->recvmsg_queue_lock); + list_add(&recvmsg->list, &t->recvmsg_queue); + spin_unlock(&t->recvmsg_queue_lock); +} + +static struct +smb_direct_recvmsg *get_empty_recvmsg(struct smb_direct_transport *t) +{ + struct smb_direct_recvmsg *recvmsg = NULL; + + spin_lock(&t->empty_recvmsg_queue_lock); + if (!list_empty(&t->empty_recvmsg_queue)) { + recvmsg = list_first_entry(&t->empty_recvmsg_queue, + struct smb_direct_recvmsg, list); + list_del(&recvmsg->list); + } + spin_unlock(&t->empty_recvmsg_queue_lock); + return recvmsg; +} + +static void put_empty_recvmsg(struct smb_direct_transport *t, + struct smb_direct_recvmsg *recvmsg) +{ + ib_dma_unmap_single(t->cm_id->device, recvmsg->sge.addr, + recvmsg->sge.length, DMA_FROM_DEVICE); + + spin_lock(&t->empty_recvmsg_queue_lock); + list_add_tail(&recvmsg->list, &t->empty_recvmsg_queue); + spin_unlock(&t->empty_recvmsg_queue_lock); +} + +static void enqueue_reassembly(struct smb_direct_transport *t, + struct smb_direct_recvmsg *recvmsg, + int data_length) +{ + spin_lock(&t->reassembly_queue_lock); + list_add_tail(&recvmsg->list, &t->reassembly_queue); + t->reassembly_queue_length++; + /* + * Make sure reassembly_data_length is updated after list and + * reassembly_queue_length are updated. On the dequeue side + * reassembly_data_length is checked without a lock to determine + * if reassembly_queue_length and list is up to date + */ + virt_wmb(); + t->reassembly_data_length += data_length; + spin_unlock(&t->reassembly_queue_lock); +} + +static struct smb_direct_recvmsg *get_first_reassembly(struct smb_direct_transport *t) +{ + if (!list_empty(&t->reassembly_queue)) + return list_first_entry(&t->reassembly_queue, + struct smb_direct_recvmsg, list); + else + return NULL; +} + +static void smb_direct_disconnect_rdma_work(struct work_struct *work) +{ + struct smb_direct_transport *t = + container_of(work, struct smb_direct_transport, + disconnect_work); + + if (t->status == SMB_DIRECT_CS_CONNECTED) { + t->status = SMB_DIRECT_CS_DISCONNECTING; + rdma_disconnect(t->cm_id); + } +} + +static void +smb_direct_disconnect_rdma_connection(struct smb_direct_transport *t) +{ + if (t->status == SMB_DIRECT_CS_CONNECTED) + queue_work(smb_direct_wq, &t->disconnect_work); +} + +static void smb_direct_send_immediate_work(struct work_struct *work) +{ + struct smb_direct_transport *t = container_of(work, + struct smb_direct_transport, send_immediate_work); + + if (t->status != SMB_DIRECT_CS_CONNECTED) + return; + + smb_direct_post_send_data(t, NULL, NULL, 0, 0); +} + +static struct smb_direct_transport *alloc_transport(struct rdma_cm_id *cm_id) +{ + struct smb_direct_transport *t; + struct ksmbd_conn *conn; + + t = kzalloc(sizeof(*t), GFP_KERNEL); + if (!t) + return NULL; + + t->cm_id = cm_id; + cm_id->context = t; + + t->status = SMB_DIRECT_CS_NEW; + init_waitqueue_head(&t->wait_status); + + spin_lock_init(&t->reassembly_queue_lock); + INIT_LIST_HEAD(&t->reassembly_queue); + t->reassembly_data_length = 0; + t->reassembly_queue_length = 0; + init_waitqueue_head(&t->wait_reassembly_queue); + init_waitqueue_head(&t->wait_send_credits); + init_waitqueue_head(&t->wait_rw_credits); + + spin_lock_init(&t->receive_credit_lock); + spin_lock_init(&t->recvmsg_queue_lock); + INIT_LIST_HEAD(&t->recvmsg_queue); + + spin_lock_init(&t->empty_recvmsg_queue_lock); + INIT_LIST_HEAD(&t->empty_recvmsg_queue); + + init_waitqueue_head(&t->wait_send_pending); + atomic_set(&t->send_pending, 0); + + spin_lock_init(&t->lock_new_recv_credits); + + INIT_DELAYED_WORK(&t->post_recv_credits_work, + smb_direct_post_recv_credits); + INIT_WORK(&t->send_immediate_work, smb_direct_send_immediate_work); + INIT_WORK(&t->disconnect_work, smb_direct_disconnect_rdma_work); + + conn = ksmbd_conn_alloc(); + if (!conn) + goto err; + conn->transport = KSMBD_TRANS(t); + KSMBD_TRANS(t)->conn = conn; + KSMBD_TRANS(t)->ops = &ksmbd_smb_direct_transport_ops; + return t; +err: + kfree(t); + return NULL; +} + +static void free_transport(struct smb_direct_transport *t) +{ + struct smb_direct_recvmsg *recvmsg; + + wake_up_interruptible(&t->wait_send_credits); + + ksmbd_debug(RDMA, "wait for all send posted to IB to finish\n"); + wait_event(t->wait_send_pending, + atomic_read(&t->send_pending) == 0); + + cancel_work_sync(&t->disconnect_work); + cancel_delayed_work_sync(&t->post_recv_credits_work); + cancel_work_sync(&t->send_immediate_work); + + if (t->qp) { + ib_drain_qp(t->qp); + ib_mr_pool_destroy(t->qp, &t->qp->rdma_mrs); + ib_destroy_qp(t->qp); + } + + ksmbd_debug(RDMA, "drain the reassembly queue\n"); + do { + spin_lock(&t->reassembly_queue_lock); + recvmsg = get_first_reassembly(t); + if (recvmsg) { + list_del(&recvmsg->list); + spin_unlock(&t->reassembly_queue_lock); + put_recvmsg(t, recvmsg); + } else { + spin_unlock(&t->reassembly_queue_lock); + } + } while (recvmsg); + t->reassembly_data_length = 0; + + if (t->send_cq) + ib_free_cq(t->send_cq); + if (t->recv_cq) + ib_free_cq(t->recv_cq); + if (t->pd) + ib_dealloc_pd(t->pd); + if (t->cm_id) + rdma_destroy_id(t->cm_id); + + smb_direct_destroy_pools(t); + ksmbd_conn_free(KSMBD_TRANS(t)->conn); + kfree(t); +} + +static struct smb_direct_sendmsg +*smb_direct_alloc_sendmsg(struct smb_direct_transport *t) +{ + struct smb_direct_sendmsg *msg; + + msg = mempool_alloc(t->sendmsg_mempool, GFP_KERNEL); + if (!msg) + return ERR_PTR(-ENOMEM); + msg->transport = t; + INIT_LIST_HEAD(&msg->list); + msg->num_sge = 0; + return msg; +} + +static void smb_direct_free_sendmsg(struct smb_direct_transport *t, + struct smb_direct_sendmsg *msg) +{ + int i; + + if (msg->num_sge > 0) { + ib_dma_unmap_single(t->cm_id->device, + msg->sge[0].addr, msg->sge[0].length, + DMA_TO_DEVICE); + for (i = 1; i < msg->num_sge; i++) + ib_dma_unmap_page(t->cm_id->device, + msg->sge[i].addr, msg->sge[i].length, + DMA_TO_DEVICE); + } + mempool_free(msg, t->sendmsg_mempool); +} + +static int smb_direct_check_recvmsg(struct smb_direct_recvmsg *recvmsg) +{ + switch (recvmsg->type) { + case SMB_DIRECT_MSG_DATA_TRANSFER: { + struct smb_direct_data_transfer *req = + (struct smb_direct_data_transfer *)recvmsg->packet; + struct smb2_hdr *hdr = (struct smb2_hdr *)(recvmsg->packet + + le32_to_cpu(req->data_offset)); + ksmbd_debug(RDMA, + "CreditGranted: %u, CreditRequested: %u, DataLength: %u, RemainingDataLength: %u, SMB: %x, Command: %u\n", + le16_to_cpu(req->credits_granted), + le16_to_cpu(req->credits_requested), + req->data_length, req->remaining_data_length, + hdr->ProtocolId, hdr->Command); + break; + } + case SMB_DIRECT_MSG_NEGOTIATE_REQ: { + struct smb_direct_negotiate_req *req = + (struct smb_direct_negotiate_req *)recvmsg->packet; + ksmbd_debug(RDMA, + "MinVersion: %u, MaxVersion: %u, CreditRequested: %u, MaxSendSize: %u, MaxRecvSize: %u, MaxFragmentedSize: %u\n", + le16_to_cpu(req->min_version), + le16_to_cpu(req->max_version), + le16_to_cpu(req->credits_requested), + le32_to_cpu(req->preferred_send_size), + le32_to_cpu(req->max_receive_size), + le32_to_cpu(req->max_fragmented_size)); + if (le16_to_cpu(req->min_version) > 0x0100 || + le16_to_cpu(req->max_version) < 0x0100) + return -EOPNOTSUPP; + if (le16_to_cpu(req->credits_requested) <= 0 || + le32_to_cpu(req->max_receive_size) <= 128 || + le32_to_cpu(req->max_fragmented_size) <= + 128 * 1024) + return -ECONNABORTED; + + break; + } + default: + return -EINVAL; + } + return 0; +} + +static void recv_done(struct ib_cq *cq, struct ib_wc *wc) +{ + struct smb_direct_recvmsg *recvmsg; + struct smb_direct_transport *t; + + recvmsg = container_of(wc->wr_cqe, struct smb_direct_recvmsg, cqe); + t = recvmsg->transport; + + if (wc->status != IB_WC_SUCCESS || wc->opcode != IB_WC_RECV) { + if (wc->status != IB_WC_WR_FLUSH_ERR) { + pr_err("Recv error. status='%s (%d)' opcode=%d\n", + ib_wc_status_msg(wc->status), wc->status, + wc->opcode); + smb_direct_disconnect_rdma_connection(t); + } + put_empty_recvmsg(t, recvmsg); + return; + } + + ksmbd_debug(RDMA, "Recv completed. status='%s (%d)', opcode=%d\n", + ib_wc_status_msg(wc->status), wc->status, + wc->opcode); + + ib_dma_sync_single_for_cpu(wc->qp->device, recvmsg->sge.addr, + recvmsg->sge.length, DMA_FROM_DEVICE); + + switch (recvmsg->type) { + case SMB_DIRECT_MSG_NEGOTIATE_REQ: + if (wc->byte_len < sizeof(struct smb_direct_negotiate_req)) { + put_empty_recvmsg(t, recvmsg); + return; + } + t->negotiation_requested = true; + t->full_packet_received = true; + t->status = SMB_DIRECT_CS_CONNECTED; + enqueue_reassembly(t, recvmsg, 0); + wake_up_interruptible(&t->wait_status); + break; + case SMB_DIRECT_MSG_DATA_TRANSFER: { + struct smb_direct_data_transfer *data_transfer = + (struct smb_direct_data_transfer *)recvmsg->packet; + unsigned int data_length; + int avail_recvmsg_count, receive_credits; + + if (wc->byte_len < + offsetof(struct smb_direct_data_transfer, padding)) { + put_empty_recvmsg(t, recvmsg); + return; + } + + data_length = le32_to_cpu(data_transfer->data_length); + if (data_length) { + if (wc->byte_len < sizeof(struct smb_direct_data_transfer) + + (u64)data_length) { + put_empty_recvmsg(t, recvmsg); + return; + } + + if (t->full_packet_received) + recvmsg->first_segment = true; + + if (le32_to_cpu(data_transfer->remaining_data_length)) + t->full_packet_received = false; + else + t->full_packet_received = true; + + enqueue_reassembly(t, recvmsg, (int)data_length); + wake_up_interruptible(&t->wait_reassembly_queue); + + spin_lock(&t->receive_credit_lock); + receive_credits = --(t->recv_credits); + avail_recvmsg_count = t->count_avail_recvmsg; + spin_unlock(&t->receive_credit_lock); + } else { + put_empty_recvmsg(t, recvmsg); + + spin_lock(&t->receive_credit_lock); + receive_credits = --(t->recv_credits); + avail_recvmsg_count = ++(t->count_avail_recvmsg); + spin_unlock(&t->receive_credit_lock); + } + + t->recv_credit_target = + le16_to_cpu(data_transfer->credits_requested); + atomic_add(le16_to_cpu(data_transfer->credits_granted), + &t->send_credits); + + if (le16_to_cpu(data_transfer->flags) & + SMB_DIRECT_RESPONSE_REQUESTED) + queue_work(smb_direct_wq, &t->send_immediate_work); + + if (atomic_read(&t->send_credits) > 0) + wake_up_interruptible(&t->wait_send_credits); + + if (is_receive_credit_post_required(receive_credits, avail_recvmsg_count)) + mod_delayed_work(smb_direct_wq, + &t->post_recv_credits_work, 0); + break; + } + default: + break; + } +} + +static int smb_direct_post_recv(struct smb_direct_transport *t, + struct smb_direct_recvmsg *recvmsg) +{ + struct ib_recv_wr wr; + int ret; + + recvmsg->sge.addr = ib_dma_map_single(t->cm_id->device, + recvmsg->packet, t->max_recv_size, + DMA_FROM_DEVICE); + ret = ib_dma_mapping_error(t->cm_id->device, recvmsg->sge.addr); + if (ret) + return ret; + recvmsg->sge.length = t->max_recv_size; + recvmsg->sge.lkey = t->pd->local_dma_lkey; + recvmsg->cqe.done = recv_done; + + wr.wr_cqe = &recvmsg->cqe; + wr.next = NULL; + wr.sg_list = &recvmsg->sge; + wr.num_sge = 1; + + ret = ib_post_recv(t->qp, &wr, NULL); + if (ret) { + pr_err("Can't post recv: %d\n", ret); + ib_dma_unmap_single(t->cm_id->device, + recvmsg->sge.addr, recvmsg->sge.length, + DMA_FROM_DEVICE); + smb_direct_disconnect_rdma_connection(t); + return ret; + } + return ret; +} + +static int smb_direct_read(struct ksmbd_transport *t, char *buf, + unsigned int size, int unused) +{ + struct smb_direct_recvmsg *recvmsg; + struct smb_direct_data_transfer *data_transfer; + int to_copy, to_read, data_read, offset; + u32 data_length, remaining_data_length, data_offset; + int rc; + struct smb_direct_transport *st = smb_trans_direct_transfort(t); + +again: + if (st->status != SMB_DIRECT_CS_CONNECTED) { + pr_err("disconnected\n"); + return -ENOTCONN; + } + + /* + * No need to hold the reassembly queue lock all the time as we are + * the only one reading from the front of the queue. The transport + * may add more entries to the back of the queue at the same time + */ + if (st->reassembly_data_length >= size) { + int queue_length; + int queue_removed = 0; + + /* + * Need to make sure reassembly_data_length is read before + * reading reassembly_queue_length and calling + * get_first_reassembly. This call is lock free + * as we never read at the end of the queue which are being + * updated in SOFTIRQ as more data is received + */ + virt_rmb(); + queue_length = st->reassembly_queue_length; + data_read = 0; + to_read = size; + offset = st->first_entry_offset; + while (data_read < size) { + recvmsg = get_first_reassembly(st); + data_transfer = smb_direct_recvmsg_payload(recvmsg); + data_length = le32_to_cpu(data_transfer->data_length); + remaining_data_length = + le32_to_cpu(data_transfer->remaining_data_length); + data_offset = le32_to_cpu(data_transfer->data_offset); + + /* + * The upper layer expects RFC1002 length at the + * beginning of the payload. Return it to indicate + * the total length of the packet. This minimize the + * change to upper layer packet processing logic. This + * will be eventually remove when an intermediate + * transport layer is added + */ + if (recvmsg->first_segment && size == 4) { + unsigned int rfc1002_len = + data_length + remaining_data_length; + *((__be32 *)buf) = cpu_to_be32(rfc1002_len); + data_read = 4; + recvmsg->first_segment = false; + ksmbd_debug(RDMA, + "returning rfc1002 length %d\n", + rfc1002_len); + goto read_rfc1002_done; + } + + to_copy = min_t(int, data_length - offset, to_read); + memcpy(buf + data_read, (char *)data_transfer + data_offset + offset, + to_copy); + + /* move on to the next buffer? */ + if (to_copy == data_length - offset) { + queue_length--; + /* + * No need to lock if we are not at the + * end of the queue + */ + if (queue_length) { + list_del(&recvmsg->list); + } else { + spin_lock_irq(&st->reassembly_queue_lock); + list_del(&recvmsg->list); + spin_unlock_irq(&st->reassembly_queue_lock); + } + queue_removed++; + put_recvmsg(st, recvmsg); + offset = 0; + } else { + offset += to_copy; + } + + to_read -= to_copy; + data_read += to_copy; + } + + spin_lock_irq(&st->reassembly_queue_lock); + st->reassembly_data_length -= data_read; + st->reassembly_queue_length -= queue_removed; + spin_unlock_irq(&st->reassembly_queue_lock); + + spin_lock(&st->receive_credit_lock); + st->count_avail_recvmsg += queue_removed; + if (is_receive_credit_post_required(st->recv_credits, st->count_avail_recvmsg)) { + spin_unlock(&st->receive_credit_lock); + mod_delayed_work(smb_direct_wq, + &st->post_recv_credits_work, 0); + } else { + spin_unlock(&st->receive_credit_lock); + } + + st->first_entry_offset = offset; + ksmbd_debug(RDMA, + "returning to thread data_read=%d reassembly_data_length=%d first_entry_offset=%d\n", + data_read, st->reassembly_data_length, + st->first_entry_offset); +read_rfc1002_done: + return data_read; + } + + ksmbd_debug(RDMA, "wait_event on more data\n"); + rc = wait_event_interruptible(st->wait_reassembly_queue, + st->reassembly_data_length >= size || + st->status != SMB_DIRECT_CS_CONNECTED); + if (rc) + return -EINTR; + + goto again; +} + +static void smb_direct_post_recv_credits(struct work_struct *work) +{ + struct smb_direct_transport *t = container_of(work, + struct smb_direct_transport, post_recv_credits_work.work); + struct smb_direct_recvmsg *recvmsg; + int receive_credits, credits = 0; + int ret; + int use_free = 1; + + spin_lock(&t->receive_credit_lock); + receive_credits = t->recv_credits; + spin_unlock(&t->receive_credit_lock); + + if (receive_credits < t->recv_credit_target) { + while (true) { + if (use_free) + recvmsg = get_free_recvmsg(t); + else + recvmsg = get_empty_recvmsg(t); + if (!recvmsg) { + if (use_free) { + use_free = 0; + continue; + } else { + break; + } + } + + recvmsg->type = SMB_DIRECT_MSG_DATA_TRANSFER; + recvmsg->first_segment = false; + + ret = smb_direct_post_recv(t, recvmsg); + if (ret) { + pr_err("Can't post recv: %d\n", ret); + put_recvmsg(t, recvmsg); + break; + } + credits++; + } + } + + spin_lock(&t->receive_credit_lock); + t->recv_credits += credits; + t->count_avail_recvmsg -= credits; + spin_unlock(&t->receive_credit_lock); + + spin_lock(&t->lock_new_recv_credits); + t->new_recv_credits += credits; + spin_unlock(&t->lock_new_recv_credits); + + if (credits) + queue_work(smb_direct_wq, &t->send_immediate_work); +} + +static void send_done(struct ib_cq *cq, struct ib_wc *wc) +{ + struct smb_direct_sendmsg *sendmsg, *sibling; + struct smb_direct_transport *t; + struct list_head *pos, *prev, *end; + + sendmsg = container_of(wc->wr_cqe, struct smb_direct_sendmsg, cqe); + t = sendmsg->transport; + + ksmbd_debug(RDMA, "Send completed. status='%s (%d)', opcode=%d\n", + ib_wc_status_msg(wc->status), wc->status, + wc->opcode); + + if (wc->status != IB_WC_SUCCESS || wc->opcode != IB_WC_SEND) { + pr_err("Send error. status='%s (%d)', opcode=%d\n", + ib_wc_status_msg(wc->status), wc->status, + wc->opcode); + smb_direct_disconnect_rdma_connection(t); + } + + if (atomic_dec_and_test(&t->send_pending)) + wake_up(&t->wait_send_pending); + + /* iterate and free the list of messages in reverse. the list's head + * is invalid. + */ + for (pos = &sendmsg->list, prev = pos->prev, end = sendmsg->list.next; + prev != end; pos = prev, prev = prev->prev) { + sibling = container_of(pos, struct smb_direct_sendmsg, list); + smb_direct_free_sendmsg(t, sibling); + } + + sibling = container_of(pos, struct smb_direct_sendmsg, list); + smb_direct_free_sendmsg(t, sibling); +} + +static int manage_credits_prior_sending(struct smb_direct_transport *t) +{ + int new_credits; + + spin_lock(&t->lock_new_recv_credits); + new_credits = t->new_recv_credits; + t->new_recv_credits = 0; + spin_unlock(&t->lock_new_recv_credits); + + return new_credits; +} + +static int smb_direct_post_send(struct smb_direct_transport *t, + struct ib_send_wr *wr) +{ + int ret; + + atomic_inc(&t->send_pending); + ret = ib_post_send(t->qp, wr, NULL); + if (ret) { + pr_err("failed to post send: %d\n", ret); + if (atomic_dec_and_test(&t->send_pending)) + wake_up(&t->wait_send_pending); + smb_direct_disconnect_rdma_connection(t); + } + return ret; +} + +static void smb_direct_send_ctx_init(struct smb_direct_transport *t, + struct smb_direct_send_ctx *send_ctx, + bool need_invalidate_rkey, + unsigned int remote_key) +{ + INIT_LIST_HEAD(&send_ctx->msg_list); + send_ctx->wr_cnt = 0; + send_ctx->need_invalidate_rkey = need_invalidate_rkey; + send_ctx->remote_key = remote_key; +} + +static int smb_direct_flush_send_list(struct smb_direct_transport *t, + struct smb_direct_send_ctx *send_ctx, + bool is_last) +{ + struct smb_direct_sendmsg *first, *last; + int ret; + + if (list_empty(&send_ctx->msg_list)) + return 0; + + first = list_first_entry(&send_ctx->msg_list, + struct smb_direct_sendmsg, + list); + last = list_last_entry(&send_ctx->msg_list, + struct smb_direct_sendmsg, + list); + + last->wr.send_flags = IB_SEND_SIGNALED; + last->wr.wr_cqe = &last->cqe; + if (is_last && send_ctx->need_invalidate_rkey) { + last->wr.opcode = IB_WR_SEND_WITH_INV; + last->wr.ex.invalidate_rkey = send_ctx->remote_key; + } + + ret = smb_direct_post_send(t, &first->wr); + if (!ret) { + smb_direct_send_ctx_init(t, send_ctx, + send_ctx->need_invalidate_rkey, + send_ctx->remote_key); + } else { + atomic_add(send_ctx->wr_cnt, &t->send_credits); + wake_up(&t->wait_send_credits); + list_for_each_entry_safe(first, last, &send_ctx->msg_list, + list) { + smb_direct_free_sendmsg(t, first); + } + } + return ret; +} + +static int wait_for_credits(struct smb_direct_transport *t, + wait_queue_head_t *waitq, atomic_t *total_credits, + int needed) +{ + int ret; + + do { + if (atomic_sub_return(needed, total_credits) >= 0) + return 0; + + atomic_add(needed, total_credits); + ret = wait_event_interruptible(*waitq, + atomic_read(total_credits) >= needed || + t->status != SMB_DIRECT_CS_CONNECTED); + + if (t->status != SMB_DIRECT_CS_CONNECTED) + return -ENOTCONN; + else if (ret < 0) + return ret; + } while (true); +} + +static int wait_for_send_credits(struct smb_direct_transport *t, + struct smb_direct_send_ctx *send_ctx) +{ + int ret; + + if (send_ctx && + (send_ctx->wr_cnt >= 16 || atomic_read(&t->send_credits) <= 1)) { + ret = smb_direct_flush_send_list(t, send_ctx, false); + if (ret) + return ret; + } + + return wait_for_credits(t, &t->wait_send_credits, &t->send_credits, 1); +} + +static int wait_for_rw_credits(struct smb_direct_transport *t, int credits) +{ + return wait_for_credits(t, &t->wait_rw_credits, &t->rw_credits, credits); +} + +static int calc_rw_credits(struct smb_direct_transport *t, + char *buf, unsigned int len) +{ + return DIV_ROUND_UP(get_buf_page_count(buf, len), + t->pages_per_rw_credit); +} + +static int smb_direct_create_header(struct smb_direct_transport *t, + int size, int remaining_data_length, + struct smb_direct_sendmsg **sendmsg_out) +{ + struct smb_direct_sendmsg *sendmsg; + struct smb_direct_data_transfer *packet; + int header_length; + int ret; + + sendmsg = smb_direct_alloc_sendmsg(t); + if (IS_ERR(sendmsg)) + return PTR_ERR(sendmsg); + + /* Fill in the packet header */ + packet = (struct smb_direct_data_transfer *)sendmsg->packet; + packet->credits_requested = cpu_to_le16(t->send_credit_target); + packet->credits_granted = cpu_to_le16(manage_credits_prior_sending(t)); + + packet->flags = 0; + packet->reserved = 0; + if (!size) + packet->data_offset = 0; + else + packet->data_offset = cpu_to_le32(24); + packet->data_length = cpu_to_le32(size); + packet->remaining_data_length = cpu_to_le32(remaining_data_length); + packet->padding = 0; + + ksmbd_debug(RDMA, + "credits_requested=%d credits_granted=%d data_offset=%d data_length=%d remaining_data_length=%d\n", + le16_to_cpu(packet->credits_requested), + le16_to_cpu(packet->credits_granted), + le32_to_cpu(packet->data_offset), + le32_to_cpu(packet->data_length), + le32_to_cpu(packet->remaining_data_length)); + + /* Map the packet to DMA */ + header_length = sizeof(struct smb_direct_data_transfer); + /* If this is a packet without payload, don't send padding */ + if (!size) + header_length = + offsetof(struct smb_direct_data_transfer, padding); + + sendmsg->sge[0].addr = ib_dma_map_single(t->cm_id->device, + (void *)packet, + header_length, + DMA_TO_DEVICE); + ret = ib_dma_mapping_error(t->cm_id->device, sendmsg->sge[0].addr); + if (ret) { + smb_direct_free_sendmsg(t, sendmsg); + return ret; + } + + sendmsg->num_sge = 1; + sendmsg->sge[0].length = header_length; + sendmsg->sge[0].lkey = t->pd->local_dma_lkey; + + *sendmsg_out = sendmsg; + return 0; +} + +static int get_sg_list(void *buf, int size, struct scatterlist *sg_list, int nentries) +{ + bool high = is_vmalloc_addr(buf); + struct page *page; + int offset, len; + int i = 0; + + if (size <= 0 || nentries < get_buf_page_count(buf, size)) + return -EINVAL; + + offset = offset_in_page(buf); + buf -= offset; + while (size > 0) { + len = min_t(int, PAGE_SIZE - offset, size); + if (high) + page = vmalloc_to_page(buf); + else + page = kmap_to_page(buf); + + if (!sg_list) + return -EINVAL; + sg_set_page(sg_list, page, len, offset); + sg_list = sg_next(sg_list); + + buf += PAGE_SIZE; + size -= len; + offset = 0; + i++; + } + return i; +} + +static int get_mapped_sg_list(struct ib_device *device, void *buf, int size, + struct scatterlist *sg_list, int nentries, + enum dma_data_direction dir) +{ + int npages; + + npages = get_sg_list(buf, size, sg_list, nentries); + if (npages < 0) + return -EINVAL; + return ib_dma_map_sg(device, sg_list, npages, dir); +} + +static int post_sendmsg(struct smb_direct_transport *t, + struct smb_direct_send_ctx *send_ctx, + struct smb_direct_sendmsg *msg) +{ + int i; + + for (i = 0; i < msg->num_sge; i++) + ib_dma_sync_single_for_device(t->cm_id->device, + msg->sge[i].addr, msg->sge[i].length, + DMA_TO_DEVICE); + + msg->cqe.done = send_done; + msg->wr.opcode = IB_WR_SEND; + msg->wr.sg_list = &msg->sge[0]; + msg->wr.num_sge = msg->num_sge; + msg->wr.next = NULL; + + if (send_ctx) { + msg->wr.wr_cqe = NULL; + msg->wr.send_flags = 0; + if (!list_empty(&send_ctx->msg_list)) { + struct smb_direct_sendmsg *last; + + last = list_last_entry(&send_ctx->msg_list, + struct smb_direct_sendmsg, + list); + last->wr.next = &msg->wr; + } + list_add_tail(&msg->list, &send_ctx->msg_list); + send_ctx->wr_cnt++; + return 0; + } + + msg->wr.wr_cqe = &msg->cqe; + msg->wr.send_flags = IB_SEND_SIGNALED; + return smb_direct_post_send(t, &msg->wr); +} + +static int smb_direct_post_send_data(struct smb_direct_transport *t, + struct smb_direct_send_ctx *send_ctx, + struct kvec *iov, int niov, + int remaining_data_length) +{ + int i, j, ret; + struct smb_direct_sendmsg *msg; + int data_length; + struct scatterlist sg[SMB_DIRECT_MAX_SEND_SGES - 1]; + + ret = wait_for_send_credits(t, send_ctx); + if (ret) + return ret; + + data_length = 0; + for (i = 0; i < niov; i++) + data_length += iov[i].iov_len; + + ret = smb_direct_create_header(t, data_length, remaining_data_length, + &msg); + if (ret) { + atomic_inc(&t->send_credits); + return ret; + } + + for (i = 0; i < niov; i++) { + struct ib_sge *sge; + int sg_cnt; + + sg_init_table(sg, SMB_DIRECT_MAX_SEND_SGES - 1); + sg_cnt = get_mapped_sg_list(t->cm_id->device, + iov[i].iov_base, iov[i].iov_len, + sg, SMB_DIRECT_MAX_SEND_SGES - 1, + DMA_TO_DEVICE); + if (sg_cnt <= 0) { + pr_err("failed to map buffer\n"); + ret = -ENOMEM; + goto err; + } else if (sg_cnt + msg->num_sge > SMB_DIRECT_MAX_SEND_SGES) { + pr_err("buffer not fitted into sges\n"); + ret = -E2BIG; + ib_dma_unmap_sg(t->cm_id->device, sg, sg_cnt, + DMA_TO_DEVICE); + goto err; + } + + for (j = 0; j < sg_cnt; j++) { + sge = &msg->sge[msg->num_sge]; + sge->addr = sg_dma_address(&sg[j]); + sge->length = sg_dma_len(&sg[j]); + sge->lkey = t->pd->local_dma_lkey; + msg->num_sge++; + } + } + + ret = post_sendmsg(t, send_ctx, msg); + if (ret) + goto err; + return 0; +err: + smb_direct_free_sendmsg(t, msg); + atomic_inc(&t->send_credits); + return ret; +} + +static int smb_direct_writev(struct ksmbd_transport *t, + struct kvec *iov, int niovs, int buflen, + bool need_invalidate, unsigned int remote_key) +{ + struct smb_direct_transport *st = smb_trans_direct_transfort(t); + int remaining_data_length; + int start, i, j; + int max_iov_size = st->max_send_size - + sizeof(struct smb_direct_data_transfer); + int ret; + struct kvec vec; + struct smb_direct_send_ctx send_ctx; + + if (st->status != SMB_DIRECT_CS_CONNECTED) + return -ENOTCONN; + + //FIXME: skip RFC1002 header.. + buflen -= 4; + iov[0].iov_base += 4; + iov[0].iov_len -= 4; + + remaining_data_length = buflen; + ksmbd_debug(RDMA, "Sending smb (RDMA): smb_len=%u\n", buflen); + + smb_direct_send_ctx_init(st, &send_ctx, need_invalidate, remote_key); + start = i = 0; + buflen = 0; + while (true) { + buflen += iov[i].iov_len; + if (buflen > max_iov_size) { + if (i > start) { + remaining_data_length -= + (buflen - iov[i].iov_len); + ret = smb_direct_post_send_data(st, &send_ctx, + &iov[start], i - start, + remaining_data_length); + if (ret) + goto done; + } else { + /* iov[start] is too big, break it */ + int nvec = (buflen + max_iov_size - 1) / + max_iov_size; + + for (j = 0; j < nvec; j++) { + vec.iov_base = + (char *)iov[start].iov_base + + j * max_iov_size; + vec.iov_len = + min_t(int, max_iov_size, + buflen - max_iov_size * j); + remaining_data_length -= vec.iov_len; + ret = smb_direct_post_send_data(st, &send_ctx, &vec, 1, + remaining_data_length); + if (ret) + goto done; + } + i++; + if (i == niovs) + break; + } + start = i; + buflen = 0; + } else { + i++; + if (i == niovs) { + /* send out all remaining vecs */ + remaining_data_length -= buflen; + ret = smb_direct_post_send_data(st, &send_ctx, + &iov[start], i - start, + remaining_data_length); + if (ret) + goto done; + break; + } + } + } + +done: + ret = smb_direct_flush_send_list(st, &send_ctx, true); + + /* + * As an optimization, we don't wait for individual I/O to finish + * before sending the next one. + * Send them all and wait for pending send count to get to 0 + * that means all the I/Os have been out and we are good to return + */ + + wait_event(st->wait_send_pending, + atomic_read(&st->send_pending) == 0); + return ret; +} + +static void smb_direct_free_rdma_rw_msg(struct smb_direct_transport *t, + struct smb_direct_rdma_rw_msg *msg, + enum dma_data_direction dir) +{ + rdma_rw_ctx_destroy(&msg->rw_ctx, t->qp, t->qp->port, + msg->sgt.sgl, msg->sgt.nents, dir); + sg_free_table_chained(&msg->sgt, SG_CHUNK_SIZE); + kfree(msg); +} + +static void read_write_done(struct ib_cq *cq, struct ib_wc *wc, + enum dma_data_direction dir) +{ + struct smb_direct_rdma_rw_msg *msg = container_of(wc->wr_cqe, + struct smb_direct_rdma_rw_msg, cqe); + struct smb_direct_transport *t = msg->t; + + if (wc->status != IB_WC_SUCCESS) { + msg->status = -EIO; + pr_err("read/write error. opcode = %d, status = %s(%d)\n", + wc->opcode, ib_wc_status_msg(wc->status), wc->status); + if (wc->status != IB_WC_WR_FLUSH_ERR) + smb_direct_disconnect_rdma_connection(t); + } + + complete(msg->completion); +} + +static void read_done(struct ib_cq *cq, struct ib_wc *wc) +{ + read_write_done(cq, wc, DMA_FROM_DEVICE); +} + +static void write_done(struct ib_cq *cq, struct ib_wc *wc) +{ + read_write_done(cq, wc, DMA_TO_DEVICE); +} + +static int smb_direct_rdma_xmit(struct smb_direct_transport *t, + void *buf, int buf_len, + struct smb2_buffer_desc_v1 *desc, + unsigned int desc_len, + bool is_read) +{ + struct smb_direct_rdma_rw_msg *msg, *next_msg; + int i, ret; + DECLARE_COMPLETION_ONSTACK(completion); + struct ib_send_wr *first_wr; + LIST_HEAD(msg_list); + char *desc_buf; + int credits_needed; + unsigned int desc_buf_len; + size_t total_length = 0; + + if (t->status != SMB_DIRECT_CS_CONNECTED) + return -ENOTCONN; + + /* calculate needed credits */ + credits_needed = 0; + desc_buf = buf; + for (i = 0; i < desc_len / sizeof(*desc); i++) { + desc_buf_len = le32_to_cpu(desc[i].length); + + credits_needed += calc_rw_credits(t, desc_buf, desc_buf_len); + desc_buf += desc_buf_len; + total_length += desc_buf_len; + if (desc_buf_len == 0 || total_length > buf_len || + total_length > t->max_rdma_rw_size) + return -EINVAL; + } + + ksmbd_debug(RDMA, "RDMA %s, len %#x, needed credits %#x\n", + is_read ? "read" : "write", buf_len, credits_needed); + + ret = wait_for_rw_credits(t, credits_needed); + if (ret < 0) + return ret; + + /* build rdma_rw_ctx for each descriptor */ + desc_buf = buf; + for (i = 0; i < desc_len / sizeof(*desc); i++) { + msg = kzalloc(offsetof(struct smb_direct_rdma_rw_msg, sg_list) + + sizeof(struct scatterlist) * SG_CHUNK_SIZE, GFP_KERNEL); + if (!msg) { + ret = -ENOMEM; + goto out; + } + + desc_buf_len = le32_to_cpu(desc[i].length); + + msg->t = t; + msg->cqe.done = is_read ? read_done : write_done; + msg->completion = &completion; + + msg->sgt.sgl = &msg->sg_list[0]; + ret = sg_alloc_table_chained(&msg->sgt, + get_buf_page_count(desc_buf, desc_buf_len), + msg->sg_list, SG_CHUNK_SIZE); + if (ret) { + kfree(msg); + ret = -ENOMEM; + goto out; + } + + ret = get_sg_list(desc_buf, desc_buf_len, + msg->sgt.sgl, msg->sgt.orig_nents); + if (ret < 0) { + sg_free_table_chained(&msg->sgt, SG_CHUNK_SIZE); + kfree(msg); + goto out; + } + + ret = rdma_rw_ctx_init(&msg->rw_ctx, t->qp, t->qp->port, + msg->sgt.sgl, + get_buf_page_count(desc_buf, desc_buf_len), + 0, + le64_to_cpu(desc[i].offset), + le32_to_cpu(desc[i].token), + is_read ? DMA_FROM_DEVICE : DMA_TO_DEVICE); + if (ret < 0) { + pr_err("failed to init rdma_rw_ctx: %d\n", ret); + sg_free_table_chained(&msg->sgt, SG_CHUNK_SIZE); + kfree(msg); + goto out; + } + + list_add_tail(&msg->list, &msg_list); + desc_buf += desc_buf_len; + } + + /* concatenate work requests of rdma_rw_ctxs */ + first_wr = NULL; + list_for_each_entry_reverse(msg, &msg_list, list) { + first_wr = rdma_rw_ctx_wrs(&msg->rw_ctx, t->qp, t->qp->port, + &msg->cqe, first_wr); + } + + ret = ib_post_send(t->qp, first_wr, NULL); + if (ret) { + pr_err("failed to post send wr for RDMA R/W: %d\n", ret); + goto out; + } + + msg = list_last_entry(&msg_list, struct smb_direct_rdma_rw_msg, list); + wait_for_completion(&completion); + ret = msg->status; +out: + list_for_each_entry_safe(msg, next_msg, &msg_list, list) { + list_del(&msg->list); + smb_direct_free_rdma_rw_msg(t, msg, + is_read ? DMA_FROM_DEVICE : DMA_TO_DEVICE); + } + atomic_add(credits_needed, &t->rw_credits); + wake_up(&t->wait_rw_credits); + return ret; +} + +static int smb_direct_rdma_write(struct ksmbd_transport *t, + void *buf, unsigned int buflen, + struct smb2_buffer_desc_v1 *desc, + unsigned int desc_len) +{ + return smb_direct_rdma_xmit(smb_trans_direct_transfort(t), buf, buflen, + desc, desc_len, false); +} + +static int smb_direct_rdma_read(struct ksmbd_transport *t, + void *buf, unsigned int buflen, + struct smb2_buffer_desc_v1 *desc, + unsigned int desc_len) +{ + return smb_direct_rdma_xmit(smb_trans_direct_transfort(t), buf, buflen, + desc, desc_len, true); +} + +static void smb_direct_disconnect(struct ksmbd_transport *t) +{ + struct smb_direct_transport *st = smb_trans_direct_transfort(t); + + ksmbd_debug(RDMA, "Disconnecting cm_id=%p\n", st->cm_id); + + smb_direct_disconnect_rdma_work(&st->disconnect_work); + wait_event_interruptible(st->wait_status, + st->status == SMB_DIRECT_CS_DISCONNECTED); + free_transport(st); +} + +static void smb_direct_shutdown(struct ksmbd_transport *t) +{ + struct smb_direct_transport *st = smb_trans_direct_transfort(t); + + ksmbd_debug(RDMA, "smb-direct shutdown cm_id=%p\n", st->cm_id); + + smb_direct_disconnect_rdma_work(&st->disconnect_work); +} + +static int smb_direct_cm_handler(struct rdma_cm_id *cm_id, + struct rdma_cm_event *event) +{ + struct smb_direct_transport *t = cm_id->context; + + ksmbd_debug(RDMA, "RDMA CM event. cm_id=%p event=%s (%d)\n", + cm_id, rdma_event_msg(event->event), event->event); + + switch (event->event) { + case RDMA_CM_EVENT_ESTABLISHED: { + t->status = SMB_DIRECT_CS_CONNECTED; + wake_up_interruptible(&t->wait_status); + break; + } + case RDMA_CM_EVENT_DEVICE_REMOVAL: + case RDMA_CM_EVENT_DISCONNECTED: { + ib_drain_qp(t->qp); + + t->status = SMB_DIRECT_CS_DISCONNECTED; + wake_up_interruptible(&t->wait_status); + wake_up_interruptible(&t->wait_reassembly_queue); + wake_up(&t->wait_send_credits); + break; + } + case RDMA_CM_EVENT_CONNECT_ERROR: { + t->status = SMB_DIRECT_CS_DISCONNECTED; + wake_up_interruptible(&t->wait_status); + break; + } + default: + pr_err("Unexpected RDMA CM event. cm_id=%p, event=%s (%d)\n", + cm_id, rdma_event_msg(event->event), + event->event); + break; + } + return 0; +} + +static void smb_direct_qpair_handler(struct ib_event *event, void *context) +{ + struct smb_direct_transport *t = context; + + ksmbd_debug(RDMA, "Received QP event. cm_id=%p, event=%s (%d)\n", + t->cm_id, ib_event_msg(event->event), event->event); + + switch (event->event) { + case IB_EVENT_CQ_ERR: + case IB_EVENT_QP_FATAL: + smb_direct_disconnect_rdma_connection(t); + break; + default: + break; + } +} + +static int smb_direct_send_negotiate_response(struct smb_direct_transport *t, + int failed) +{ + struct smb_direct_sendmsg *sendmsg; + struct smb_direct_negotiate_resp *resp; + int ret; + + sendmsg = smb_direct_alloc_sendmsg(t); + if (IS_ERR(sendmsg)) + return -ENOMEM; + + resp = (struct smb_direct_negotiate_resp *)sendmsg->packet; + if (failed) { + memset(resp, 0, sizeof(*resp)); + resp->min_version = cpu_to_le16(0x0100); + resp->max_version = cpu_to_le16(0x0100); + resp->status = STATUS_NOT_SUPPORTED; + } else { + resp->status = STATUS_SUCCESS; + resp->min_version = SMB_DIRECT_VERSION_LE; + resp->max_version = SMB_DIRECT_VERSION_LE; + resp->negotiated_version = SMB_DIRECT_VERSION_LE; + resp->reserved = 0; + resp->credits_requested = + cpu_to_le16(t->send_credit_target); + resp->credits_granted = cpu_to_le16(manage_credits_prior_sending(t)); + resp->max_readwrite_size = cpu_to_le32(t->max_rdma_rw_size); + resp->preferred_send_size = cpu_to_le32(t->max_send_size); + resp->max_receive_size = cpu_to_le32(t->max_recv_size); + resp->max_fragmented_size = + cpu_to_le32(t->max_fragmented_recv_size); + } + + sendmsg->sge[0].addr = ib_dma_map_single(t->cm_id->device, + (void *)resp, sizeof(*resp), + DMA_TO_DEVICE); + ret = ib_dma_mapping_error(t->cm_id->device, sendmsg->sge[0].addr); + if (ret) { + smb_direct_free_sendmsg(t, sendmsg); + return ret; + } + + sendmsg->num_sge = 1; + sendmsg->sge[0].length = sizeof(*resp); + sendmsg->sge[0].lkey = t->pd->local_dma_lkey; + + ret = post_sendmsg(t, NULL, sendmsg); + if (ret) { + smb_direct_free_sendmsg(t, sendmsg); + return ret; + } + + wait_event(t->wait_send_pending, + atomic_read(&t->send_pending) == 0); + return 0; +} + +static int smb_direct_accept_client(struct smb_direct_transport *t) +{ + struct rdma_conn_param conn_param; + struct ib_port_immutable port_immutable; + u32 ird_ord_hdr[2]; + int ret; + + memset(&conn_param, 0, sizeof(conn_param)); + conn_param.initiator_depth = min_t(u8, t->cm_id->device->attrs.max_qp_rd_atom, + SMB_DIRECT_CM_INITIATOR_DEPTH); + conn_param.responder_resources = 0; + + t->cm_id->device->ops.get_port_immutable(t->cm_id->device, + t->cm_id->port_num, + &port_immutable); + if (port_immutable.core_cap_flags & RDMA_CORE_PORT_IWARP) { + ird_ord_hdr[0] = conn_param.responder_resources; + ird_ord_hdr[1] = 1; + conn_param.private_data = ird_ord_hdr; + conn_param.private_data_len = sizeof(ird_ord_hdr); + } else { + conn_param.private_data = NULL; + conn_param.private_data_len = 0; + } + conn_param.retry_count = SMB_DIRECT_CM_RETRY; + conn_param.rnr_retry_count = SMB_DIRECT_CM_RNR_RETRY; + conn_param.flow_control = 0; + + ret = rdma_accept(t->cm_id, &conn_param); + if (ret) { + pr_err("error at rdma_accept: %d\n", ret); + return ret; + } + return 0; +} + +static int smb_direct_prepare_negotiation(struct smb_direct_transport *t) +{ + int ret; + struct smb_direct_recvmsg *recvmsg; + + recvmsg = get_free_recvmsg(t); + if (!recvmsg) + return -ENOMEM; + recvmsg->type = SMB_DIRECT_MSG_NEGOTIATE_REQ; + + ret = smb_direct_post_recv(t, recvmsg); + if (ret) { + pr_err("Can't post recv: %d\n", ret); + goto out_err; + } + + t->negotiation_requested = false; + ret = smb_direct_accept_client(t); + if (ret) { + pr_err("Can't accept client\n"); + goto out_err; + } + + smb_direct_post_recv_credits(&t->post_recv_credits_work.work); + return 0; +out_err: + put_recvmsg(t, recvmsg); + return ret; +} + +static unsigned int smb_direct_get_max_fr_pages(struct smb_direct_transport *t) +{ + return min_t(unsigned int, + t->cm_id->device->attrs.max_fast_reg_page_list_len, + 256); +} + +static int smb_direct_init_params(struct smb_direct_transport *t, + struct ib_qp_cap *cap) +{ + struct ib_device *device = t->cm_id->device; + int max_send_sges, max_rw_wrs, max_send_wrs; + unsigned int max_sge_per_wr, wrs_per_credit; + + /* need 3 more sge. because a SMB_DIRECT header, SMB2 header, + * SMB2 response could be mapped. + */ + t->max_send_size = smb_direct_max_send_size; + max_send_sges = DIV_ROUND_UP(t->max_send_size, PAGE_SIZE) + 3; + if (max_send_sges > SMB_DIRECT_MAX_SEND_SGES) { + pr_err("max_send_size %d is too large\n", t->max_send_size); + return -EINVAL; + } + + /* Calculate the number of work requests for RDMA R/W. + * The maximum number of pages which can be registered + * with one Memory region can be transferred with one + * R/W credit. And at least 4 work requests for each credit + * are needed for MR registration, RDMA R/W, local & remote + * MR invalidation. + */ + t->max_rdma_rw_size = smb_direct_max_read_write_size; + t->pages_per_rw_credit = smb_direct_get_max_fr_pages(t); + t->max_rw_credits = DIV_ROUND_UP(t->max_rdma_rw_size, + (t->pages_per_rw_credit - 1) * + PAGE_SIZE); + + max_sge_per_wr = min_t(unsigned int, device->attrs.max_send_sge, + device->attrs.max_sge_rd); + max_sge_per_wr = max_t(unsigned int, max_sge_per_wr, + max_send_sges); + wrs_per_credit = max_t(unsigned int, 4, + DIV_ROUND_UP(t->pages_per_rw_credit, + max_sge_per_wr) + 1); + max_rw_wrs = t->max_rw_credits * wrs_per_credit; + + max_send_wrs = smb_direct_send_credit_target + max_rw_wrs; + if (max_send_wrs > device->attrs.max_cqe || + max_send_wrs > device->attrs.max_qp_wr) { + pr_err("consider lowering send_credit_target = %d\n", + smb_direct_send_credit_target); + pr_err("Possible CQE overrun, device reporting max_cqe %d max_qp_wr %d\n", + device->attrs.max_cqe, device->attrs.max_qp_wr); + return -EINVAL; + } + + if (smb_direct_receive_credit_max > device->attrs.max_cqe || + smb_direct_receive_credit_max > device->attrs.max_qp_wr) { + pr_err("consider lowering receive_credit_max = %d\n", + smb_direct_receive_credit_max); + pr_err("Possible CQE overrun, device reporting max_cpe %d max_qp_wr %d\n", + device->attrs.max_cqe, device->attrs.max_qp_wr); + return -EINVAL; + } + + if (device->attrs.max_recv_sge < SMB_DIRECT_MAX_RECV_SGES) { + pr_err("warning: device max_recv_sge = %d too small\n", + device->attrs.max_recv_sge); + return -EINVAL; + } + + t->recv_credits = 0; + t->count_avail_recvmsg = 0; + + t->recv_credit_max = smb_direct_receive_credit_max; + t->recv_credit_target = 10; + t->new_recv_credits = 0; + + t->send_credit_target = smb_direct_send_credit_target; + atomic_set(&t->send_credits, 0); + atomic_set(&t->rw_credits, t->max_rw_credits); + + t->max_send_size = smb_direct_max_send_size; + t->max_recv_size = smb_direct_max_receive_size; + t->max_fragmented_recv_size = smb_direct_max_fragmented_recv_size; + + cap->max_send_wr = max_send_wrs; + cap->max_recv_wr = t->recv_credit_max; + cap->max_send_sge = max_sge_per_wr; + cap->max_recv_sge = SMB_DIRECT_MAX_RECV_SGES; + cap->max_inline_data = 0; + cap->max_rdma_ctxs = t->max_rw_credits; + return 0; +} + +static void smb_direct_destroy_pools(struct smb_direct_transport *t) +{ + struct smb_direct_recvmsg *recvmsg; + + while ((recvmsg = get_free_recvmsg(t))) + mempool_free(recvmsg, t->recvmsg_mempool); + while ((recvmsg = get_empty_recvmsg(t))) + mempool_free(recvmsg, t->recvmsg_mempool); + + mempool_destroy(t->recvmsg_mempool); + t->recvmsg_mempool = NULL; + + kmem_cache_destroy(t->recvmsg_cache); + t->recvmsg_cache = NULL; + + mempool_destroy(t->sendmsg_mempool); + t->sendmsg_mempool = NULL; + + kmem_cache_destroy(t->sendmsg_cache); + t->sendmsg_cache = NULL; +} + +static int smb_direct_create_pools(struct smb_direct_transport *t) +{ + char name[80]; + int i; + struct smb_direct_recvmsg *recvmsg; + + snprintf(name, sizeof(name), "smb_direct_rqst_pool_%p", t); + t->sendmsg_cache = kmem_cache_create(name, + sizeof(struct smb_direct_sendmsg) + + sizeof(struct smb_direct_negotiate_resp), + 0, SLAB_HWCACHE_ALIGN, NULL); + if (!t->sendmsg_cache) + return -ENOMEM; + + t->sendmsg_mempool = mempool_create(t->send_credit_target, + mempool_alloc_slab, mempool_free_slab, + t->sendmsg_cache); + if (!t->sendmsg_mempool) + goto err; + + snprintf(name, sizeof(name), "smb_direct_resp_%p", t); + t->recvmsg_cache = kmem_cache_create(name, + sizeof(struct smb_direct_recvmsg) + + t->max_recv_size, + 0, SLAB_HWCACHE_ALIGN, NULL); + if (!t->recvmsg_cache) + goto err; + + t->recvmsg_mempool = + mempool_create(t->recv_credit_max, mempool_alloc_slab, + mempool_free_slab, t->recvmsg_cache); + if (!t->recvmsg_mempool) + goto err; + + INIT_LIST_HEAD(&t->recvmsg_queue); + + for (i = 0; i < t->recv_credit_max; i++) { + recvmsg = mempool_alloc(t->recvmsg_mempool, GFP_KERNEL); + if (!recvmsg) + goto err; + recvmsg->transport = t; + list_add(&recvmsg->list, &t->recvmsg_queue); + } + t->count_avail_recvmsg = t->recv_credit_max; + + return 0; +err: + smb_direct_destroy_pools(t); + return -ENOMEM; +} + +static int smb_direct_create_qpair(struct smb_direct_transport *t, + struct ib_qp_cap *cap) +{ + int ret; + struct ib_qp_init_attr qp_attr; + int pages_per_rw; + + t->pd = ib_alloc_pd(t->cm_id->device, 0); + if (IS_ERR(t->pd)) { + pr_err("Can't create RDMA PD\n"); + ret = PTR_ERR(t->pd); + t->pd = NULL; + return ret; + } + + t->send_cq = ib_alloc_cq(t->cm_id->device, t, + smb_direct_send_credit_target + cap->max_rdma_ctxs, + 0, IB_POLL_WORKQUEUE); + if (IS_ERR(t->send_cq)) { + pr_err("Can't create RDMA send CQ\n"); + ret = PTR_ERR(t->send_cq); + t->send_cq = NULL; + goto err; + } + + t->recv_cq = ib_alloc_cq(t->cm_id->device, t, + t->recv_credit_max, 0, IB_POLL_WORKQUEUE); + if (IS_ERR(t->recv_cq)) { + pr_err("Can't create RDMA recv CQ\n"); + ret = PTR_ERR(t->recv_cq); + t->recv_cq = NULL; + goto err; + } + + memset(&qp_attr, 0, sizeof(qp_attr)); + qp_attr.event_handler = smb_direct_qpair_handler; + qp_attr.qp_context = t; + qp_attr.cap = *cap; + qp_attr.sq_sig_type = IB_SIGNAL_REQ_WR; + qp_attr.qp_type = IB_QPT_RC; + qp_attr.send_cq = t->send_cq; + qp_attr.recv_cq = t->recv_cq; + qp_attr.port_num = ~0; + + ret = rdma_create_qp(t->cm_id, t->pd, &qp_attr); + if (ret) { + pr_err("Can't create RDMA QP: %d\n", ret); + goto err; + } + + t->qp = t->cm_id->qp; + t->cm_id->event_handler = smb_direct_cm_handler; + + pages_per_rw = DIV_ROUND_UP(t->max_rdma_rw_size, PAGE_SIZE) + 1; + if (pages_per_rw > t->cm_id->device->attrs.max_sgl_rd) { + ret = ib_mr_pool_init(t->qp, &t->qp->rdma_mrs, + t->max_rw_credits, IB_MR_TYPE_MEM_REG, + t->pages_per_rw_credit, 0); + if (ret) { + pr_err("failed to init mr pool count %d pages %d\n", + t->max_rw_credits, t->pages_per_rw_credit); + goto err; + } + } + + return 0; +err: + if (t->qp) { + ib_destroy_qp(t->qp); + t->qp = NULL; + } + if (t->recv_cq) { + ib_destroy_cq(t->recv_cq); + t->recv_cq = NULL; + } + if (t->send_cq) { + ib_destroy_cq(t->send_cq); + t->send_cq = NULL; + } + if (t->pd) { + ib_dealloc_pd(t->pd); + t->pd = NULL; + } + return ret; +} + +static int smb_direct_prepare(struct ksmbd_transport *t) +{ + struct smb_direct_transport *st = smb_trans_direct_transfort(t); + struct smb_direct_recvmsg *recvmsg; + struct smb_direct_negotiate_req *req; + int ret; + + ksmbd_debug(RDMA, "Waiting for SMB_DIRECT negotiate request\n"); + ret = wait_event_interruptible_timeout(st->wait_status, + st->negotiation_requested || + st->status == SMB_DIRECT_CS_DISCONNECTED, + SMB_DIRECT_NEGOTIATE_TIMEOUT * HZ); + if (ret <= 0 || st->status == SMB_DIRECT_CS_DISCONNECTED) + return ret < 0 ? ret : -ETIMEDOUT; + + recvmsg = get_first_reassembly(st); + if (!recvmsg) + return -ECONNABORTED; + + ret = smb_direct_check_recvmsg(recvmsg); + if (ret == -ECONNABORTED) + goto out; + + req = (struct smb_direct_negotiate_req *)recvmsg->packet; + st->max_recv_size = min_t(int, st->max_recv_size, + le32_to_cpu(req->preferred_send_size)); + st->max_send_size = min_t(int, st->max_send_size, + le32_to_cpu(req->max_receive_size)); + st->max_fragmented_send_size = + le32_to_cpu(req->max_fragmented_size); + st->max_fragmented_recv_size = + (st->recv_credit_max * st->max_recv_size) / 2; + + ret = smb_direct_send_negotiate_response(st, ret); +out: + spin_lock_irq(&st->reassembly_queue_lock); + st->reassembly_queue_length--; + list_del(&recvmsg->list); + spin_unlock_irq(&st->reassembly_queue_lock); + put_recvmsg(st, recvmsg); + + return ret; +} + +static int smb_direct_connect(struct smb_direct_transport *st) +{ + int ret; + struct ib_qp_cap qp_cap; + + ret = smb_direct_init_params(st, &qp_cap); + if (ret) { + pr_err("Can't configure RDMA parameters\n"); + return ret; + } + + ret = smb_direct_create_pools(st); + if (ret) { + pr_err("Can't init RDMA pool: %d\n", ret); + return ret; + } + + ret = smb_direct_create_qpair(st, &qp_cap); + if (ret) { + pr_err("Can't accept RDMA client: %d\n", ret); + return ret; + } + + ret = smb_direct_prepare_negotiation(st); + if (ret) { + pr_err("Can't negotiate: %d\n", ret); + return ret; + } + return 0; +} + +static bool rdma_frwr_is_supported(struct ib_device_attr *attrs) +{ + if (!(attrs->device_cap_flags & IB_DEVICE_MEM_MGT_EXTENSIONS)) + return false; + if (attrs->max_fast_reg_page_list_len == 0) + return false; + return true; +} + +static int smb_direct_handle_connect_request(struct rdma_cm_id *new_cm_id) +{ + struct smb_direct_transport *t; + int ret; + + if (!rdma_frwr_is_supported(&new_cm_id->device->attrs)) { + ksmbd_debug(RDMA, + "Fast Registration Work Requests is not supported. device capabilities=%llx\n", + new_cm_id->device->attrs.device_cap_flags); + return -EPROTONOSUPPORT; + } + + t = alloc_transport(new_cm_id); + if (!t) + return -ENOMEM; + + ret = smb_direct_connect(t); + if (ret) + goto out_err; + + KSMBD_TRANS(t)->handler = kthread_run(ksmbd_conn_handler_loop, + KSMBD_TRANS(t)->conn, "ksmbd:r%u", + smb_direct_port); + if (IS_ERR(KSMBD_TRANS(t)->handler)) { + ret = PTR_ERR(KSMBD_TRANS(t)->handler); + pr_err("Can't start thread\n"); + goto out_err; + } + + return 0; +out_err: + free_transport(t); + return ret; +} + +static int smb_direct_listen_handler(struct rdma_cm_id *cm_id, + struct rdma_cm_event *event) +{ + switch (event->event) { + case RDMA_CM_EVENT_CONNECT_REQUEST: { + int ret = smb_direct_handle_connect_request(cm_id); + + if (ret) { + pr_err("Can't create transport: %d\n", ret); + return ret; + } + + ksmbd_debug(RDMA, "Received connection request. cm_id=%p\n", + cm_id); + break; + } + default: + pr_err("Unexpected listen event. cm_id=%p, event=%s (%d)\n", + cm_id, rdma_event_msg(event->event), event->event); + break; + } + return 0; +} + +static int smb_direct_listen(int port) +{ + int ret; + struct rdma_cm_id *cm_id; + struct sockaddr_in sin = { + .sin_family = AF_INET, + .sin_addr.s_addr = htonl(INADDR_ANY), + .sin_port = htons(port), + }; + + cm_id = rdma_create_id(&init_net, smb_direct_listen_handler, + &smb_direct_listener, RDMA_PS_TCP, IB_QPT_RC); + if (IS_ERR(cm_id)) { + pr_err("Can't create cm id: %ld\n", PTR_ERR(cm_id)); + return PTR_ERR(cm_id); + } + + ret = rdma_bind_addr(cm_id, (struct sockaddr *)&sin); + if (ret) { + pr_err("Can't bind: %d\n", ret); + goto err; + } + + smb_direct_listener.cm_id = cm_id; + + ret = rdma_listen(cm_id, 10); + if (ret) { + pr_err("Can't listen: %d\n", ret); + goto err; + } + return 0; +err: + smb_direct_listener.cm_id = NULL; + rdma_destroy_id(cm_id); + return ret; +} + +static int smb_direct_ib_client_add(struct ib_device *ib_dev) +{ + struct smb_direct_device *smb_dev; + + /* Set 5445 port if device type is iWARP(No IB) */ + if (ib_dev->node_type != RDMA_NODE_IB_CA) + smb_direct_port = SMB_DIRECT_PORT_IWARP; + + if (!ib_dev->ops.get_netdev || + !rdma_frwr_is_supported(&ib_dev->attrs)) + return 0; + + smb_dev = kzalloc(sizeof(*smb_dev), GFP_KERNEL); + if (!smb_dev) + return -ENOMEM; + smb_dev->ib_dev = ib_dev; + + write_lock(&smb_direct_device_lock); + list_add(&smb_dev->list, &smb_direct_device_list); + write_unlock(&smb_direct_device_lock); + + ksmbd_debug(RDMA, "ib device added: name %s\n", ib_dev->name); + return 0; +} + +static void smb_direct_ib_client_remove(struct ib_device *ib_dev, + void *client_data) +{ + struct smb_direct_device *smb_dev, *tmp; + + write_lock(&smb_direct_device_lock); + list_for_each_entry_safe(smb_dev, tmp, &smb_direct_device_list, list) { + if (smb_dev->ib_dev == ib_dev) { + list_del(&smb_dev->list); + kfree(smb_dev); + break; + } + } + write_unlock(&smb_direct_device_lock); +} + +static struct ib_client smb_direct_ib_client = { + .name = "ksmbd_smb_direct_ib", + .add = smb_direct_ib_client_add, + .remove = smb_direct_ib_client_remove, +}; + +int ksmbd_rdma_init(void) +{ + int ret; + + smb_direct_listener.cm_id = NULL; + + ret = ib_register_client(&smb_direct_ib_client); + if (ret) { + pr_err("failed to ib_register_client\n"); + return ret; + } + + /* When a client is running out of send credits, the credits are + * granted by the server's sending a packet using this queue. + * This avoids the situation that a clients cannot send packets + * for lack of credits + */ + smb_direct_wq = alloc_workqueue("ksmbd-smb_direct-wq", + WQ_HIGHPRI | WQ_MEM_RECLAIM, 0); + if (!smb_direct_wq) + return -ENOMEM; + + ret = smb_direct_listen(smb_direct_port); + if (ret) { + destroy_workqueue(smb_direct_wq); + smb_direct_wq = NULL; + pr_err("Can't listen: %d\n", ret); + return ret; + } + + ksmbd_debug(RDMA, "init RDMA listener. cm_id=%p\n", + smb_direct_listener.cm_id); + return 0; +} + +void ksmbd_rdma_destroy(void) +{ + if (!smb_direct_listener.cm_id) + return; + + ib_unregister_client(&smb_direct_ib_client); + rdma_destroy_id(smb_direct_listener.cm_id); + + smb_direct_listener.cm_id = NULL; + + if (smb_direct_wq) { + destroy_workqueue(smb_direct_wq); + smb_direct_wq = NULL; + } +} + +bool ksmbd_rdma_capable_netdev(struct net_device *netdev) +{ + struct smb_direct_device *smb_dev; + int i; + bool rdma_capable = false; + + read_lock(&smb_direct_device_lock); + list_for_each_entry(smb_dev, &smb_direct_device_list, list) { + for (i = 0; i < smb_dev->ib_dev->phys_port_cnt; i++) { + struct net_device *ndev; + + ndev = smb_dev->ib_dev->ops.get_netdev(smb_dev->ib_dev, + i + 1); + if (!ndev) + continue; + + if (ndev == netdev) { + dev_put(ndev); + rdma_capable = true; + goto out; + } + dev_put(ndev); + } + } +out: + read_unlock(&smb_direct_device_lock); + + if (rdma_capable == false) { + struct ib_device *ibdev; + + ibdev = ib_device_get_by_netdev(netdev, RDMA_DRIVER_UNKNOWN); + if (ibdev) { + if (rdma_frwr_is_supported(&ibdev->attrs)) + rdma_capable = true; + ib_device_put(ibdev); + } + } + + return rdma_capable; +} + +static struct ksmbd_transport_ops ksmbd_smb_direct_transport_ops = { + .prepare = smb_direct_prepare, + .disconnect = smb_direct_disconnect, + .shutdown = smb_direct_shutdown, + .writev = smb_direct_writev, + .read = smb_direct_read, + .rdma_read = smb_direct_rdma_read, + .rdma_write = smb_direct_rdma_write, +}; diff --git a/fs/smb/server/transport_rdma.h b/fs/smb/server/transport_rdma.h new file mode 100644 index 0000000000000..77aee4e5c9dcd --- /dev/null +++ b/fs/smb/server/transport_rdma.h @@ -0,0 +1,69 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + * Copyright (C) 2017, Microsoft Corporation. + * Copyright (C) 2018, LG Electronics. + */ + +#ifndef __KSMBD_TRANSPORT_RDMA_H__ +#define __KSMBD_TRANSPORT_RDMA_H__ + +#define SMBD_DEFAULT_IOSIZE (8 * 1024 * 1024) +#define SMBD_MIN_IOSIZE (512 * 1024) +#define SMBD_MAX_IOSIZE (16 * 1024 * 1024) + +/* SMB DIRECT negotiation request packet [MS-SMBD] 2.2.1 */ +struct smb_direct_negotiate_req { + __le16 min_version; + __le16 max_version; + __le16 reserved; + __le16 credits_requested; + __le32 preferred_send_size; + __le32 max_receive_size; + __le32 max_fragmented_size; +} __packed; + +/* SMB DIRECT negotiation response packet [MS-SMBD] 2.2.2 */ +struct smb_direct_negotiate_resp { + __le16 min_version; + __le16 max_version; + __le16 negotiated_version; + __le16 reserved; + __le16 credits_requested; + __le16 credits_granted; + __le32 status; + __le32 max_readwrite_size; + __le32 preferred_send_size; + __le32 max_receive_size; + __le32 max_fragmented_size; +} __packed; + +#define SMB_DIRECT_RESPONSE_REQUESTED 0x0001 + +/* SMB DIRECT data transfer packet with payload [MS-SMBD] 2.2.3 */ +struct smb_direct_data_transfer { + __le16 credits_requested; + __le16 credits_granted; + __le16 flags; + __le16 reserved; + __le32 remaining_data_length; + __le32 data_offset; + __le32 data_length; + __le32 padding; + __u8 buffer[]; +} __packed; + +#ifdef CONFIG_SMB_SERVER_SMBDIRECT +int ksmbd_rdma_init(void); +void ksmbd_rdma_destroy(void); +bool ksmbd_rdma_capable_netdev(struct net_device *netdev); +void init_smbd_max_io_size(unsigned int sz); +unsigned int get_smbd_max_read_write_size(void); +#else +static inline int ksmbd_rdma_init(void) { return 0; } +static inline int ksmbd_rdma_destroy(void) { return 0; } +static inline bool ksmbd_rdma_capable_netdev(struct net_device *netdev) { return false; } +static inline void init_smbd_max_io_size(unsigned int sz) { } +static inline unsigned int get_smbd_max_read_write_size(void) { return 0; } +#endif + +#endif /* __KSMBD_TRANSPORT_RDMA_H__ */ diff --git a/fs/smb/server/transport_tcp.c b/fs/smb/server/transport_tcp.c new file mode 100644 index 0000000000000..eff7a1d793f00 --- /dev/null +++ b/fs/smb/server/transport_tcp.c @@ -0,0 +1,649 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Copyright (C) 2016 Namjae Jeon + * Copyright (C) 2018 Samsung Electronics Co., Ltd. + */ + +#include + +#include "smb_common.h" +#include "server.h" +#include "auth.h" +#include "connection.h" +#include "transport_tcp.h" + +#define IFACE_STATE_DOWN BIT(0) +#define IFACE_STATE_CONFIGURED BIT(1) + +static atomic_t active_num_conn; + +struct interface { + struct task_struct *ksmbd_kthread; + struct socket *ksmbd_socket; + struct list_head entry; + char *name; + struct mutex sock_release_lock; + int state; +}; + +static LIST_HEAD(iface_list); + +static int bind_additional_ifaces; + +struct tcp_transport { + struct ksmbd_transport transport; + struct socket *sock; + struct kvec *iov; + unsigned int nr_iov; +}; + +static struct ksmbd_transport_ops ksmbd_tcp_transport_ops; + +static void tcp_stop_kthread(struct task_struct *kthread); +static struct interface *alloc_iface(char *ifname); + +#define KSMBD_TRANS(t) (&(t)->transport) +#define TCP_TRANS(t) ((struct tcp_transport *)container_of(t, \ + struct tcp_transport, transport)) + +static inline void ksmbd_tcp_nodelay(struct socket *sock) +{ + tcp_sock_set_nodelay(sock->sk); +} + +static inline void ksmbd_tcp_reuseaddr(struct socket *sock) +{ + sock_set_reuseaddr(sock->sk); +} + +static inline void ksmbd_tcp_rcv_timeout(struct socket *sock, s64 secs) +{ + lock_sock(sock->sk); + if (secs && secs < MAX_SCHEDULE_TIMEOUT / HZ - 1) + sock->sk->sk_rcvtimeo = secs * HZ; + else + sock->sk->sk_rcvtimeo = MAX_SCHEDULE_TIMEOUT; + release_sock(sock->sk); +} + +static inline void ksmbd_tcp_snd_timeout(struct socket *sock, s64 secs) +{ + sock_set_sndtimeo(sock->sk, secs); +} + +static struct tcp_transport *alloc_transport(struct socket *client_sk) +{ + struct tcp_transport *t; + struct ksmbd_conn *conn; + + t = kzalloc(sizeof(*t), GFP_KERNEL); + if (!t) + return NULL; + t->sock = client_sk; + + conn = ksmbd_conn_alloc(); + if (!conn) { + kfree(t); + return NULL; + } + + conn->transport = KSMBD_TRANS(t); + KSMBD_TRANS(t)->conn = conn; + KSMBD_TRANS(t)->ops = &ksmbd_tcp_transport_ops; + return t; +} + +static void free_transport(struct tcp_transport *t) +{ + kernel_sock_shutdown(t->sock, SHUT_RDWR); + sock_release(t->sock); + t->sock = NULL; + + ksmbd_conn_free(KSMBD_TRANS(t)->conn); + kfree(t->iov); + kfree(t); +} + +/** + * kvec_array_init() - initialize a IO vector segment + * @new: IO vector to be initialized + * @iov: base IO vector + * @nr_segs: number of segments in base iov + * @bytes: total iovec length so far for read + * + * Return: Number of IO segments + */ +static unsigned int kvec_array_init(struct kvec *new, struct kvec *iov, + unsigned int nr_segs, size_t bytes) +{ + size_t base = 0; + + while (bytes || !iov->iov_len) { + int copy = min(bytes, iov->iov_len); + + bytes -= copy; + base += copy; + if (iov->iov_len == base) { + iov++; + nr_segs--; + base = 0; + } + } + + memcpy(new, iov, sizeof(*iov) * nr_segs); + new->iov_base += base; + new->iov_len -= base; + return nr_segs; +} + +/** + * get_conn_iovec() - get connection iovec for reading from socket + * @t: TCP transport instance + * @nr_segs: number of segments in iov + * + * Return: return existing or newly allocate iovec + */ +static struct kvec *get_conn_iovec(struct tcp_transport *t, unsigned int nr_segs) +{ + struct kvec *new_iov; + + if (t->iov && nr_segs <= t->nr_iov) + return t->iov; + + /* not big enough -- allocate a new one and release the old */ + new_iov = kmalloc_array(nr_segs, sizeof(*new_iov), GFP_KERNEL); + if (new_iov) { + kfree(t->iov); + t->iov = new_iov; + t->nr_iov = nr_segs; + } + return new_iov; +} + +static unsigned short ksmbd_tcp_get_port(const struct sockaddr *sa) +{ + switch (sa->sa_family) { + case AF_INET: + return ntohs(((struct sockaddr_in *)sa)->sin_port); + case AF_INET6: + return ntohs(((struct sockaddr_in6 *)sa)->sin6_port); + } + return 0; +} + +/** + * ksmbd_tcp_new_connection() - create a new tcp session on mount + * @client_sk: socket associated with new connection + * + * whenever a new connection is requested, create a conn thread + * (session thread) to handle new incoming smb requests from the connection + * + * Return: 0 on success, otherwise error + */ +static int ksmbd_tcp_new_connection(struct socket *client_sk) +{ + struct sockaddr *csin; + int rc = 0; + struct tcp_transport *t; + + t = alloc_transport(client_sk); + if (!t) { + sock_release(client_sk); + return -ENOMEM; + } + + csin = KSMBD_TCP_PEER_SOCKADDR(KSMBD_TRANS(t)->conn); + if (kernel_getpeername(client_sk, csin) < 0) { + pr_err("client ip resolution failed\n"); + rc = -EINVAL; + goto out_error; + } + + KSMBD_TRANS(t)->handler = kthread_run(ksmbd_conn_handler_loop, + KSMBD_TRANS(t)->conn, + "ksmbd:%u", + ksmbd_tcp_get_port(csin)); + if (IS_ERR(KSMBD_TRANS(t)->handler)) { + pr_err("cannot start conn thread\n"); + rc = PTR_ERR(KSMBD_TRANS(t)->handler); + free_transport(t); + } + return rc; + +out_error: + free_transport(t); + return rc; +} + +/** + * ksmbd_kthread_fn() - listen to new SMB connections and callback server + * @p: arguments to forker thread + * + * Return: 0 on success, error number otherwise + */ +static int ksmbd_kthread_fn(void *p) +{ + struct socket *client_sk = NULL; + struct interface *iface = (struct interface *)p; + int ret; + + while (!kthread_should_stop()) { + mutex_lock(&iface->sock_release_lock); + if (!iface->ksmbd_socket) { + mutex_unlock(&iface->sock_release_lock); + break; + } + ret = kernel_accept(iface->ksmbd_socket, &client_sk, + SOCK_NONBLOCK); + mutex_unlock(&iface->sock_release_lock); + if (ret) { + if (ret == -EAGAIN) + /* check for new connections every 100 msecs */ + schedule_timeout_interruptible(HZ / 10); + continue; + } + + if (server_conf.max_connections && + atomic_inc_return(&active_num_conn) >= server_conf.max_connections) { + pr_info_ratelimited("Limit the maximum number of connections(%u)\n", + atomic_read(&active_num_conn)); + atomic_dec(&active_num_conn); + sock_release(client_sk); + continue; + } + + ksmbd_debug(CONN, "connect success: accepted new connection\n"); + client_sk->sk->sk_rcvtimeo = KSMBD_TCP_RECV_TIMEOUT; + client_sk->sk->sk_sndtimeo = KSMBD_TCP_SEND_TIMEOUT; + + ksmbd_tcp_new_connection(client_sk); + } + + ksmbd_debug(CONN, "releasing socket\n"); + return 0; +} + +/** + * ksmbd_tcp_run_kthread() - start forker thread + * @iface: pointer to struct interface + * + * start forker thread(ksmbd/0) at module init time to listen + * on port 445 for new SMB connection requests. It creates per connection + * server threads(ksmbd/x) + * + * Return: 0 on success or error number + */ +static int ksmbd_tcp_run_kthread(struct interface *iface) +{ + int rc; + struct task_struct *kthread; + + kthread = kthread_run(ksmbd_kthread_fn, (void *)iface, "ksmbd-%s", + iface->name); + if (IS_ERR(kthread)) { + rc = PTR_ERR(kthread); + return rc; + } + iface->ksmbd_kthread = kthread; + + return 0; +} + +/** + * ksmbd_tcp_readv() - read data from socket in given iovec + * @t: TCP transport instance + * @iov_orig: base IO vector + * @nr_segs: number of segments in base iov + * @to_read: number of bytes to read from socket + * @max_retries: maximum retry count + * + * Return: on success return number of bytes read from socket, + * otherwise return error number + */ +static int ksmbd_tcp_readv(struct tcp_transport *t, struct kvec *iov_orig, + unsigned int nr_segs, unsigned int to_read, + int max_retries) +{ + int length = 0; + int total_read; + unsigned int segs; + struct msghdr ksmbd_msg; + struct kvec *iov; + struct ksmbd_conn *conn = KSMBD_TRANS(t)->conn; + + iov = get_conn_iovec(t, nr_segs); + if (!iov) + return -ENOMEM; + + ksmbd_msg.msg_control = NULL; + ksmbd_msg.msg_controllen = 0; + + for (total_read = 0; to_read; total_read += length, to_read -= length) { + try_to_freeze(); + + if (!ksmbd_conn_alive(conn)) { + total_read = -ESHUTDOWN; + break; + } + segs = kvec_array_init(iov, iov_orig, nr_segs, total_read); + + length = kernel_recvmsg(t->sock, &ksmbd_msg, + iov, segs, to_read, 0); + + if (length == -EINTR) { + total_read = -ESHUTDOWN; + break; + } else if (ksmbd_conn_need_reconnect(conn)) { + total_read = -EAGAIN; + break; + } else if (length == -ERESTARTSYS || length == -EAGAIN) { + /* + * If max_retries is negative, Allow unlimited + * retries to keep connection with inactive sessions. + */ + if (max_retries == 0) { + total_read = length; + break; + } else if (max_retries > 0) { + max_retries--; + } + + usleep_range(1000, 2000); + length = 0; + continue; + } else if (length <= 0) { + total_read = length; + break; + } + } + return total_read; +} + +/** + * ksmbd_tcp_read() - read data from socket in given buffer + * @t: TCP transport instance + * @buf: buffer to store read data from socket + * @to_read: number of bytes to read from socket + * + * Return: on success return number of bytes read from socket, + * otherwise return error number + */ +static int ksmbd_tcp_read(struct ksmbd_transport *t, char *buf, + unsigned int to_read, int max_retries) +{ + struct kvec iov; + + iov.iov_base = buf; + iov.iov_len = to_read; + + return ksmbd_tcp_readv(TCP_TRANS(t), &iov, 1, to_read, max_retries); +} + +static int ksmbd_tcp_writev(struct ksmbd_transport *t, struct kvec *iov, + int nvecs, int size, bool need_invalidate, + unsigned int remote_key) + +{ + struct msghdr smb_msg = {.msg_flags = MSG_NOSIGNAL}; + + return kernel_sendmsg(TCP_TRANS(t)->sock, &smb_msg, iov, nvecs, size); +} + +static void ksmbd_tcp_disconnect(struct ksmbd_transport *t) +{ + free_transport(TCP_TRANS(t)); + if (server_conf.max_connections) + atomic_dec(&active_num_conn); +} + +static void tcp_destroy_socket(struct socket *ksmbd_socket) +{ + int ret; + + if (!ksmbd_socket) + return; + + /* set zero to timeout */ + ksmbd_tcp_rcv_timeout(ksmbd_socket, 0); + ksmbd_tcp_snd_timeout(ksmbd_socket, 0); + + ret = kernel_sock_shutdown(ksmbd_socket, SHUT_RDWR); + if (ret) + pr_err("Failed to shutdown socket: %d\n", ret); + sock_release(ksmbd_socket); +} + +/** + * create_socket - create socket for ksmbd/0 + * + * Return: 0 on success, error number otherwise + */ +static int create_socket(struct interface *iface) +{ + int ret; + struct sockaddr_in6 sin6; + struct sockaddr_in sin; + struct socket *ksmbd_socket; + bool ipv4 = false; + + ret = sock_create(PF_INET6, SOCK_STREAM, IPPROTO_TCP, &ksmbd_socket); + if (ret) { + if (ret != -EAFNOSUPPORT) + pr_err("Can't create socket for ipv6, fallback to ipv4: %d\n", ret); + ret = sock_create(PF_INET, SOCK_STREAM, IPPROTO_TCP, + &ksmbd_socket); + if (ret) { + pr_err("Can't create socket for ipv4: %d\n", ret); + goto out_clear; + } + + sin.sin_family = PF_INET; + sin.sin_addr.s_addr = htonl(INADDR_ANY); + sin.sin_port = htons(server_conf.tcp_port); + ipv4 = true; + } else { + sin6.sin6_family = PF_INET6; + sin6.sin6_addr = in6addr_any; + sin6.sin6_port = htons(server_conf.tcp_port); + } + + ksmbd_tcp_nodelay(ksmbd_socket); + ksmbd_tcp_reuseaddr(ksmbd_socket); + + ret = sock_setsockopt(ksmbd_socket, + SOL_SOCKET, + SO_BINDTODEVICE, + KERNEL_SOCKPTR(iface->name), + strlen(iface->name)); + if (ret != -ENODEV && ret < 0) { + pr_err("Failed to set SO_BINDTODEVICE: %d\n", ret); + goto out_error; + } + + if (ipv4) + ret = kernel_bind(ksmbd_socket, (struct sockaddr *)&sin, + sizeof(sin)); + else + ret = kernel_bind(ksmbd_socket, (struct sockaddr *)&sin6, + sizeof(sin6)); + if (ret) { + pr_err("Failed to bind socket: %d\n", ret); + goto out_error; + } + + ksmbd_socket->sk->sk_rcvtimeo = KSMBD_TCP_RECV_TIMEOUT; + ksmbd_socket->sk->sk_sndtimeo = KSMBD_TCP_SEND_TIMEOUT; + + ret = kernel_listen(ksmbd_socket, KSMBD_SOCKET_BACKLOG); + if (ret) { + pr_err("Port listen() error: %d\n", ret); + goto out_error; + } + + iface->ksmbd_socket = ksmbd_socket; + ret = ksmbd_tcp_run_kthread(iface); + if (ret) { + pr_err("Can't start ksmbd main kthread: %d\n", ret); + goto out_error; + } + iface->state = IFACE_STATE_CONFIGURED; + + return 0; + +out_error: + tcp_destroy_socket(ksmbd_socket); +out_clear: + iface->ksmbd_socket = NULL; + return ret; +} + +static int ksmbd_netdev_event(struct notifier_block *nb, unsigned long event, + void *ptr) +{ + struct net_device *netdev = netdev_notifier_info_to_dev(ptr); + struct interface *iface; + int ret, found = 0; + + switch (event) { + case NETDEV_UP: + if (netif_is_bridge_port(netdev)) + return NOTIFY_OK; + + list_for_each_entry(iface, &iface_list, entry) { + if (!strcmp(iface->name, netdev->name)) { + found = 1; + if (iface->state != IFACE_STATE_DOWN) + break; + ret = create_socket(iface); + if (ret) + return NOTIFY_OK; + break; + } + } + if (!found && bind_additional_ifaces) { + iface = alloc_iface(kstrdup(netdev->name, GFP_KERNEL)); + if (!iface) + return NOTIFY_OK; + ret = create_socket(iface); + if (ret) + break; + } + break; + case NETDEV_DOWN: + list_for_each_entry(iface, &iface_list, entry) { + if (!strcmp(iface->name, netdev->name) && + iface->state == IFACE_STATE_CONFIGURED) { + tcp_stop_kthread(iface->ksmbd_kthread); + iface->ksmbd_kthread = NULL; + mutex_lock(&iface->sock_release_lock); + tcp_destroy_socket(iface->ksmbd_socket); + iface->ksmbd_socket = NULL; + mutex_unlock(&iface->sock_release_lock); + + iface->state = IFACE_STATE_DOWN; + break; + } + } + break; + } + + return NOTIFY_DONE; +} + +static struct notifier_block ksmbd_netdev_notifier = { + .notifier_call = ksmbd_netdev_event, +}; + +int ksmbd_tcp_init(void) +{ + register_netdevice_notifier(&ksmbd_netdev_notifier); + + return 0; +} + +static void tcp_stop_kthread(struct task_struct *kthread) +{ + int ret; + + if (!kthread) + return; + + ret = kthread_stop(kthread); + if (ret) + pr_err("failed to stop forker thread\n"); +} + +void ksmbd_tcp_destroy(void) +{ + struct interface *iface, *tmp; + + unregister_netdevice_notifier(&ksmbd_netdev_notifier); + + list_for_each_entry_safe(iface, tmp, &iface_list, entry) { + list_del(&iface->entry); + kfree(iface->name); + kfree(iface); + } +} + +static struct interface *alloc_iface(char *ifname) +{ + struct interface *iface; + + if (!ifname) + return NULL; + + iface = kzalloc(sizeof(struct interface), GFP_KERNEL); + if (!iface) { + kfree(ifname); + return NULL; + } + + iface->name = ifname; + iface->state = IFACE_STATE_DOWN; + list_add(&iface->entry, &iface_list); + mutex_init(&iface->sock_release_lock); + return iface; +} + +int ksmbd_tcp_set_interfaces(char *ifc_list, int ifc_list_sz) +{ + int sz = 0; + + if (!ifc_list_sz) { + struct net_device *netdev; + + rtnl_lock(); + for_each_netdev(&init_net, netdev) { + if (netif_is_bridge_port(netdev)) + continue; + if (!alloc_iface(kstrdup(netdev->name, GFP_KERNEL))) + return -ENOMEM; + } + rtnl_unlock(); + bind_additional_ifaces = 1; + return 0; + } + + while (ifc_list_sz > 0) { + if (!alloc_iface(kstrdup(ifc_list, GFP_KERNEL))) + return -ENOMEM; + + sz = strlen(ifc_list); + if (!sz) + break; + + ifc_list += sz + 1; + ifc_list_sz -= (sz + 1); + } + + bind_additional_ifaces = 0; + + return 0; +} + +static struct ksmbd_transport_ops ksmbd_tcp_transport_ops = { + .read = ksmbd_tcp_read, + .writev = ksmbd_tcp_writev, + .disconnect = ksmbd_tcp_disconnect, +}; diff --git a/fs/smb/server/transport_tcp.h b/fs/smb/server/transport_tcp.h new file mode 100644 index 0000000000000..e338bebe322f1 --- /dev/null +++ b/fs/smb/server/transport_tcp.h @@ -0,0 +1,13 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + * Copyright (C) 2018 Samsung Electronics Co., Ltd. + */ + +#ifndef __KSMBD_TRANSPORT_TCP_H__ +#define __KSMBD_TRANSPORT_TCP_H__ + +int ksmbd_tcp_set_interfaces(char *ifc_list, int ifc_list_sz); +int ksmbd_tcp_init(void); +void ksmbd_tcp_destroy(void); + +#endif /* __KSMBD_TRANSPORT_TCP_H__ */ diff --git a/fs/smb/server/unicode.c b/fs/smb/server/unicode.c new file mode 100644 index 0000000000000..a0db699ddafda --- /dev/null +++ b/fs/smb/server/unicode.c @@ -0,0 +1,384 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Some of the source code in this file came from fs/cifs/cifs_unicode.c + * + * Copyright (c) International Business Machines Corp., 2000,2009 + * Modified by Steve French (sfrench@us.ibm.com) + * Modified by Namjae Jeon (linkinjeon@kernel.org) + */ +#include +#include +#include +#include "glob.h" +#include "unicode.h" +#include "uniupr.h" +#include "smb_common.h" + +/* + * smb_utf16_bytes() - how long will a string be after conversion? + * @from: pointer to input string + * @maxbytes: don't go past this many bytes of input string + * @codepage: destination codepage + * + * Walk a utf16le string and return the number of bytes that the string will + * be after being converted to the given charset, not including any null + * termination required. Don't walk past maxbytes in the source buffer. + * + * Return: string length after conversion + */ +static int smb_utf16_bytes(const __le16 *from, int maxbytes, + const struct nls_table *codepage) +{ + int i; + int charlen, outlen = 0; + int maxwords = maxbytes / 2; + char tmp[NLS_MAX_CHARSET_SIZE]; + __u16 ftmp; + + for (i = 0; i < maxwords; i++) { + ftmp = get_unaligned_le16(&from[i]); + if (ftmp == 0) + break; + + charlen = codepage->uni2char(ftmp, tmp, NLS_MAX_CHARSET_SIZE); + if (charlen > 0) + outlen += charlen; + else + outlen++; + } + + return outlen; +} + +/* + * cifs_mapchar() - convert a host-endian char to proper char in codepage + * @target: where converted character should be copied + * @src_char: 2 byte host-endian source character + * @cp: codepage to which character should be converted + * @mapchar: should character be mapped according to mapchars mount option? + * + * This function handles the conversion of a single character. It is the + * responsibility of the caller to ensure that the target buffer is large + * enough to hold the result of the conversion (at least NLS_MAX_CHARSET_SIZE). + * + * Return: string length after conversion + */ +static int +cifs_mapchar(char *target, const __u16 src_char, const struct nls_table *cp, + bool mapchar) +{ + int len = 1; + + if (!mapchar) + goto cp_convert; + + /* + * BB: Cannot handle remapping UNI_SLASH until all the calls to + * build_path_from_dentry are modified, as they use slash as + * separator. + */ + switch (src_char) { + case UNI_COLON: + *target = ':'; + break; + case UNI_ASTERISK: + *target = '*'; + break; + case UNI_QUESTION: + *target = '?'; + break; + case UNI_PIPE: + *target = '|'; + break; + case UNI_GRTRTHAN: + *target = '>'; + break; + case UNI_LESSTHAN: + *target = '<'; + break; + default: + goto cp_convert; + } + +out: + return len; + +cp_convert: + len = cp->uni2char(src_char, target, NLS_MAX_CHARSET_SIZE); + if (len <= 0) { + *target = '?'; + len = 1; + } + + goto out; +} + +/* + * is_char_allowed() - check for valid character + * @ch: input character to be checked + * + * Return: 1 if char is allowed, otherwise 0 + */ +static inline int is_char_allowed(char *ch) +{ + /* check for control chars, wildcards etc. */ + if (!(*ch & 0x80) && + (*ch <= 0x1f || + *ch == '?' || *ch == '"' || *ch == '<' || + *ch == '>' || *ch == '|')) + return 0; + + return 1; +} + +/* + * smb_from_utf16() - convert utf16le string to local charset + * @to: destination buffer + * @from: source buffer + * @tolen: destination buffer size (in bytes) + * @fromlen: source buffer size (in bytes) + * @codepage: codepage to which characters should be converted + * @mapchar: should characters be remapped according to the mapchars option? + * + * Convert a little-endian utf16le string (as sent by the server) to a string + * in the provided codepage. The tolen and fromlen parameters are to ensure + * that the code doesn't walk off of the end of the buffer (which is always + * a danger if the alignment of the source buffer is off). The destination + * string is always properly null terminated and fits in the destination + * buffer. Returns the length of the destination string in bytes (including + * null terminator). + * + * Note that some windows versions actually send multiword UTF-16 characters + * instead of straight UTF16-2. The linux nls routines however aren't able to + * deal with those characters properly. In the event that we get some of + * those characters, they won't be translated properly. + * + * Return: string length after conversion + */ +static int smb_from_utf16(char *to, const __le16 *from, int tolen, int fromlen, + const struct nls_table *codepage, bool mapchar) +{ + int i, charlen, safelen; + int outlen = 0; + int nullsize = nls_nullsize(codepage); + int fromwords = fromlen / 2; + char tmp[NLS_MAX_CHARSET_SIZE]; + __u16 ftmp; + + /* + * because the chars can be of varying widths, we need to take care + * not to overflow the destination buffer when we get close to the + * end of it. Until we get to this offset, we don't need to check + * for overflow however. + */ + safelen = tolen - (NLS_MAX_CHARSET_SIZE + nullsize); + + for (i = 0; i < fromwords; i++) { + ftmp = get_unaligned_le16(&from[i]); + if (ftmp == 0) + break; + + /* + * check to see if converting this character might make the + * conversion bleed into the null terminator + */ + if (outlen >= safelen) { + charlen = cifs_mapchar(tmp, ftmp, codepage, mapchar); + if ((outlen + charlen) > (tolen - nullsize)) + break; + } + + /* put converted char into 'to' buffer */ + charlen = cifs_mapchar(&to[outlen], ftmp, codepage, mapchar); + outlen += charlen; + } + + /* properly null-terminate string */ + for (i = 0; i < nullsize; i++) + to[outlen++] = 0; + + return outlen; +} + +/* + * smb_strtoUTF16() - Convert character string to unicode string + * @to: destination buffer + * @from: source buffer + * @len: destination buffer size (in bytes) + * @codepage: codepage to which characters should be converted + * + * Return: string length after conversion + */ +int smb_strtoUTF16(__le16 *to, const char *from, int len, + const struct nls_table *codepage) +{ + int charlen; + int i; + wchar_t wchar_to; /* needed to quiet sparse */ + + /* special case for utf8 to handle no plane0 chars */ + if (!strcmp(codepage->charset, "utf8")) { + /* + * convert utf8 -> utf16, we assume we have enough space + * as caller should have assumed conversion does not overflow + * in destination len is length in wchar_t units (16bits) + */ + i = utf8s_to_utf16s(from, len, UTF16_LITTLE_ENDIAN, + (wchar_t *)to, len); + + /* if success terminate and exit */ + if (i >= 0) + goto success; + /* + * if fails fall back to UCS encoding as this + * function should not return negative values + * currently can fail only if source contains + * invalid encoded characters + */ + } + + for (i = 0; len > 0 && *from; i++, from += charlen, len -= charlen) { + charlen = codepage->char2uni(from, len, &wchar_to); + if (charlen < 1) { + /* A question mark */ + wchar_to = 0x003f; + charlen = 1; + } + put_unaligned_le16(wchar_to, &to[i]); + } + +success: + put_unaligned_le16(0, &to[i]); + return i; +} + +/* + * smb_strndup_from_utf16() - copy a string from wire format to the local + * codepage + * @src: source string + * @maxlen: don't walk past this many bytes in the source string + * @is_unicode: is this a unicode string? + * @codepage: destination codepage + * + * Take a string given by the server, convert it to the local codepage and + * put it in a new buffer. Returns a pointer to the new string or NULL on + * error. + * + * Return: destination string buffer or error ptr + */ +char *smb_strndup_from_utf16(const char *src, const int maxlen, + const bool is_unicode, + const struct nls_table *codepage) +{ + int len, ret; + char *dst; + + if (is_unicode) { + len = smb_utf16_bytes((__le16 *)src, maxlen, codepage); + len += nls_nullsize(codepage); + dst = kmalloc(len, GFP_KERNEL); + if (!dst) + return ERR_PTR(-ENOMEM); + ret = smb_from_utf16(dst, (__le16 *)src, len, maxlen, codepage, + false); + if (ret < 0) { + kfree(dst); + return ERR_PTR(-EINVAL); + } + } else { + len = strnlen(src, maxlen); + len++; + dst = kmalloc(len, GFP_KERNEL); + if (!dst) + return ERR_PTR(-ENOMEM); + strscpy(dst, src, len); + } + + return dst; +} + +/* + * Convert 16 bit Unicode pathname to wire format from string in current code + * page. Conversion may involve remapping up the six characters that are + * only legal in POSIX-like OS (if they are present in the string). Path + * names are little endian 16 bit Unicode on the wire + */ +/* + * smbConvertToUTF16() - convert string from local charset to utf16 + * @target: destination buffer + * @source: source buffer + * @srclen: source buffer size (in bytes) + * @cp: codepage to which characters should be converted + * @mapchar: should characters be remapped according to the mapchars option? + * + * Convert 16 bit Unicode pathname to wire format from string in current code + * page. Conversion may involve remapping up the six characters that are + * only legal in POSIX-like OS (if they are present in the string). Path + * names are little endian 16 bit Unicode on the wire + * + * Return: char length after conversion + */ +int smbConvertToUTF16(__le16 *target, const char *source, int srclen, + const struct nls_table *cp, int mapchars) +{ + int i, j, charlen; + char src_char; + __le16 dst_char; + wchar_t tmp; + + if (!mapchars) + return smb_strtoUTF16(target, source, srclen, cp); + + for (i = 0, j = 0; i < srclen; j++) { + src_char = source[i]; + charlen = 1; + switch (src_char) { + case 0: + put_unaligned(0, &target[j]); + return j; + case ':': + dst_char = cpu_to_le16(UNI_COLON); + break; + case '*': + dst_char = cpu_to_le16(UNI_ASTERISK); + break; + case '?': + dst_char = cpu_to_le16(UNI_QUESTION); + break; + case '<': + dst_char = cpu_to_le16(UNI_LESSTHAN); + break; + case '>': + dst_char = cpu_to_le16(UNI_GRTRTHAN); + break; + case '|': + dst_char = cpu_to_le16(UNI_PIPE); + break; + /* + * FIXME: We can not handle remapping backslash (UNI_SLASH) + * until all the calls to build_path_from_dentry are modified, + * as they use backslash as separator. + */ + default: + charlen = cp->char2uni(source + i, srclen - i, &tmp); + dst_char = cpu_to_le16(tmp); + + /* + * if no match, use question mark, which at least in + * some cases serves as wild card + */ + if (charlen < 1) { + dst_char = cpu_to_le16(0x003f); + charlen = 1; + } + } + /* + * character may take more than one byte in the source string, + * but will take exactly two bytes in the target string + */ + i += charlen; + put_unaligned(dst_char, &target[j]); + } + + return j; +} diff --git a/fs/smb/server/unicode.h b/fs/smb/server/unicode.h new file mode 100644 index 0000000000000..076f6034a7899 --- /dev/null +++ b/fs/smb/server/unicode.h @@ -0,0 +1,358 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + * Some of the source code in this file came from fs/cifs/cifs_unicode.c + * cifs_unicode: Unicode kernel case support + * + * Function: + * Convert a unicode character to upper or lower case using + * compressed tables. + * + * Copyright (c) International Business Machines Corp., 2000,2009 + * + * + * Notes: + * These APIs are based on the C library functions. The semantics + * should match the C functions but with expanded size operands. + * + * The upper/lower functions are based on a table created by mkupr. + * This is a compressed table of upper and lower case conversion. + * + */ +#ifndef _CIFS_UNICODE_H +#define _CIFS_UNICODE_H + +#include +#include +#include +#include + +#define UNIUPR_NOLOWER /* Example to not expand lower case tables */ + +/* + * Windows maps these to the user defined 16 bit Unicode range since they are + * reserved symbols (along with \ and /), otherwise illegal to store + * in filenames in NTFS + */ +#define UNI_ASTERISK ((__u16)('*' + 0xF000)) +#define UNI_QUESTION ((__u16)('?' + 0xF000)) +#define UNI_COLON ((__u16)(':' + 0xF000)) +#define UNI_GRTRTHAN ((__u16)('>' + 0xF000)) +#define UNI_LESSTHAN ((__u16)('<' + 0xF000)) +#define UNI_PIPE ((__u16)('|' + 0xF000)) +#define UNI_SLASH ((__u16)('\\' + 0xF000)) + +/* Just define what we want from uniupr.h. We don't want to define the tables + * in each source file. + */ +#ifndef UNICASERANGE_DEFINED +struct UniCaseRange { + wchar_t start; + wchar_t end; + signed char *table; +}; +#endif /* UNICASERANGE_DEFINED */ + +#ifndef UNIUPR_NOUPPER +extern signed char SmbUniUpperTable[512]; +extern const struct UniCaseRange SmbUniUpperRange[]; +#endif /* UNIUPR_NOUPPER */ + +#ifndef UNIUPR_NOLOWER +extern signed char CifsUniLowerTable[512]; +extern const struct UniCaseRange CifsUniLowerRange[]; +#endif /* UNIUPR_NOLOWER */ + +#ifdef __KERNEL__ +int smb_strtoUTF16(__le16 *to, const char *from, int len, + const struct nls_table *codepage); +char *smb_strndup_from_utf16(const char *src, const int maxlen, + const bool is_unicode, + const struct nls_table *codepage); +int smbConvertToUTF16(__le16 *target, const char *source, int srclen, + const struct nls_table *cp, int mapchars); +char *ksmbd_extract_sharename(struct unicode_map *um, const char *treename); +#endif + +/* + * UniStrcat: Concatenate the second string to the first + * + * Returns: + * Address of the first string + */ +static inline wchar_t *UniStrcat(wchar_t *ucs1, const wchar_t *ucs2) +{ + wchar_t *anchor = ucs1; /* save a pointer to start of ucs1 */ + + while (*ucs1++) + /*NULL*/; /* To end of first string */ + ucs1--; /* Return to the null */ + while ((*ucs1++ = *ucs2++)) + /*NULL*/; /* copy string 2 over */ + return anchor; +} + +/* + * UniStrchr: Find a character in a string + * + * Returns: + * Address of first occurrence of character in string + * or NULL if the character is not in the string + */ +static inline wchar_t *UniStrchr(const wchar_t *ucs, wchar_t uc) +{ + while ((*ucs != uc) && *ucs) + ucs++; + + if (*ucs == uc) + return (wchar_t *)ucs; + return NULL; +} + +/* + * UniStrcmp: Compare two strings + * + * Returns: + * < 0: First string is less than second + * = 0: Strings are equal + * > 0: First string is greater than second + */ +static inline int UniStrcmp(const wchar_t *ucs1, const wchar_t *ucs2) +{ + while ((*ucs1 == *ucs2) && *ucs1) { + ucs1++; + ucs2++; + } + return (int)*ucs1 - (int)*ucs2; +} + +/* + * UniStrcpy: Copy a string + */ +static inline wchar_t *UniStrcpy(wchar_t *ucs1, const wchar_t *ucs2) +{ + wchar_t *anchor = ucs1; /* save the start of result string */ + + while ((*ucs1++ = *ucs2++)) + /*NULL*/; + return anchor; +} + +/* + * UniStrlen: Return the length of a string (in 16 bit Unicode chars not bytes) + */ +static inline size_t UniStrlen(const wchar_t *ucs1) +{ + int i = 0; + + while (*ucs1++) + i++; + return i; +} + +/* + * UniStrnlen: Return the length (in 16 bit Unicode chars not bytes) of a + * string (length limited) + */ +static inline size_t UniStrnlen(const wchar_t *ucs1, int maxlen) +{ + int i = 0; + + while (*ucs1++) { + i++; + if (i >= maxlen) + break; + } + return i; +} + +/* + * UniStrncat: Concatenate length limited string + */ +static inline wchar_t *UniStrncat(wchar_t *ucs1, const wchar_t *ucs2, size_t n) +{ + wchar_t *anchor = ucs1; /* save pointer to string 1 */ + + while (*ucs1++) + /*NULL*/; + ucs1--; /* point to null terminator of s1 */ + while (n-- && (*ucs1 = *ucs2)) { /* copy s2 after s1 */ + ucs1++; + ucs2++; + } + *ucs1 = 0; /* Null terminate the result */ + return anchor; +} + +/* + * UniStrncmp: Compare length limited string + */ +static inline int UniStrncmp(const wchar_t *ucs1, const wchar_t *ucs2, size_t n) +{ + if (!n) + return 0; /* Null strings are equal */ + while ((*ucs1 == *ucs2) && *ucs1 && --n) { + ucs1++; + ucs2++; + } + return (int)*ucs1 - (int)*ucs2; +} + +/* + * UniStrncmp_le: Compare length limited string - native to little-endian + */ +static inline int +UniStrncmp_le(const wchar_t *ucs1, const wchar_t *ucs2, size_t n) +{ + if (!n) + return 0; /* Null strings are equal */ + while ((*ucs1 == __le16_to_cpu(*ucs2)) && *ucs1 && --n) { + ucs1++; + ucs2++; + } + return (int)*ucs1 - (int)__le16_to_cpu(*ucs2); +} + +/* + * UniStrncpy: Copy length limited string with pad + */ +static inline wchar_t *UniStrncpy(wchar_t *ucs1, const wchar_t *ucs2, size_t n) +{ + wchar_t *anchor = ucs1; + + while (n-- && *ucs2) /* Copy the strings */ + *ucs1++ = *ucs2++; + + n++; + while (n--) /* Pad with nulls */ + *ucs1++ = 0; + return anchor; +} + +/* + * UniStrncpy_le: Copy length limited string with pad to little-endian + */ +static inline wchar_t *UniStrncpy_le(wchar_t *ucs1, const wchar_t *ucs2, size_t n) +{ + wchar_t *anchor = ucs1; + + while (n-- && *ucs2) /* Copy the strings */ + *ucs1++ = __le16_to_cpu(*ucs2++); + + n++; + while (n--) /* Pad with nulls */ + *ucs1++ = 0; + return anchor; +} + +/* + * UniStrstr: Find a string in a string + * + * Returns: + * Address of first match found + * NULL if no matching string is found + */ +static inline wchar_t *UniStrstr(const wchar_t *ucs1, const wchar_t *ucs2) +{ + const wchar_t *anchor1 = ucs1; + const wchar_t *anchor2 = ucs2; + + while (*ucs1) { + if (*ucs1 == *ucs2) { + /* Partial match found */ + ucs1++; + ucs2++; + } else { + if (!*ucs2) /* Match found */ + return (wchar_t *)anchor1; + ucs1 = ++anchor1; /* No match */ + ucs2 = anchor2; + } + } + + if (!*ucs2) /* Both end together */ + return (wchar_t *)anchor1; /* Match found */ + return NULL; /* No match */ +} + +#ifndef UNIUPR_NOUPPER +/* + * UniToupper: Convert a unicode character to upper case + */ +static inline wchar_t UniToupper(register wchar_t uc) +{ + register const struct UniCaseRange *rp; + + if (uc < sizeof(SmbUniUpperTable)) { + /* Latin characters */ + return uc + SmbUniUpperTable[uc]; /* Use base tables */ + } + + rp = SmbUniUpperRange; /* Use range tables */ + while (rp->start) { + if (uc < rp->start) /* Before start of range */ + return uc; /* Uppercase = input */ + if (uc <= rp->end) /* In range */ + return uc + rp->table[uc - rp->start]; + rp++; /* Try next range */ + } + return uc; /* Past last range */ +} + +/* + * UniStrupr: Upper case a unicode string + */ +static inline __le16 *UniStrupr(register __le16 *upin) +{ + register __le16 *up; + + up = upin; + while (*up) { /* For all characters */ + *up = cpu_to_le16(UniToupper(le16_to_cpu(*up))); + up++; + } + return upin; /* Return input pointer */ +} +#endif /* UNIUPR_NOUPPER */ + +#ifndef UNIUPR_NOLOWER +/* + * UniTolower: Convert a unicode character to lower case + */ +static inline wchar_t UniTolower(register wchar_t uc) +{ + register const struct UniCaseRange *rp; + + if (uc < sizeof(CifsUniLowerTable)) { + /* Latin characters */ + return uc + CifsUniLowerTable[uc]; /* Use base tables */ + } + + rp = CifsUniLowerRange; /* Use range tables */ + while (rp->start) { + if (uc < rp->start) /* Before start of range */ + return uc; /* Uppercase = input */ + if (uc <= rp->end) /* In range */ + return uc + rp->table[uc - rp->start]; + rp++; /* Try next range */ + } + return uc; /* Past last range */ +} + +/* + * UniStrlwr: Lower case a unicode string + */ +static inline wchar_t *UniStrlwr(register wchar_t *upin) +{ + register wchar_t *up; + + up = upin; + while (*up) { /* For all characters */ + *up = UniTolower(*up); + up++; + } + return upin; /* Return input pointer */ +} + +#endif + +#endif /* _CIFS_UNICODE_H */ diff --git a/fs/smb/server/uniupr.h b/fs/smb/server/uniupr.h new file mode 100644 index 0000000000000..26583b776897b --- /dev/null +++ b/fs/smb/server/uniupr.h @@ -0,0 +1,268 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + * Some of the source code in this file came from fs/cifs/uniupr.h + * Copyright (c) International Business Machines Corp., 2000,2002 + * + * uniupr.h - Unicode compressed case ranges + * + */ +#ifndef __KSMBD_UNIUPR_H +#define __KSMBD_UNIUPR_H + +#ifndef UNIUPR_NOUPPER +/* + * Latin upper case + */ +signed char SmbUniUpperTable[512] = { + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 000-00f */ + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 010-01f */ + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 020-02f */ + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 030-03f */ + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 040-04f */ + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 050-05f */ + 0, -32, -32, -32, -32, -32, -32, -32, -32, -32, -32, + -32, -32, -32, -32, -32, /* 060-06f */ + -32, -32, -32, -32, -32, -32, -32, -32, -32, -32, + -32, 0, 0, 0, 0, 0, /* 070-07f */ + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 080-08f */ + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 090-09f */ + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 0a0-0af */ + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 0b0-0bf */ + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 0c0-0cf */ + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 0d0-0df */ + -32, -32, -32, -32, -32, -32, -32, -32, -32, -32, + -32, -32, -32, -32, -32, -32, /* 0e0-0ef */ + -32, -32, -32, -32, -32, -32, -32, 0, -32, -32, + -32, -32, -32, -32, -32, 121, /* 0f0-0ff */ + 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, /* 100-10f */ + 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, /* 110-11f */ + 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, /* 120-12f */ + 0, 0, 0, -1, 0, -1, 0, -1, 0, 0, -1, 0, -1, 0, -1, 0, /* 130-13f */ + -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, 0, -1, 0, -1, 0, -1, /* 140-14f */ + 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, /* 150-15f */ + 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, /* 160-16f */ + 0, -1, 0, -1, 0, -1, 0, -1, 0, 0, -1, 0, -1, 0, -1, 0, /* 170-17f */ + 0, 0, 0, -1, 0, -1, 0, 0, -1, 0, 0, 0, -1, 0, 0, 0, /* 180-18f */ + 0, 0, -1, 0, 0, 0, 0, 0, 0, -1, 0, 0, 0, 0, 0, 0, /* 190-19f */ + 0, -1, 0, -1, 0, -1, 0, 0, -1, 0, 0, 0, 0, -1, 0, 0, /* 1a0-1af */ + -1, 0, 0, 0, -1, 0, -1, 0, 0, -1, 0, 0, 0, -1, 0, 0, /* 1b0-1bf */ + 0, 0, 0, 0, 0, -1, -2, 0, -1, -2, 0, -1, -2, 0, -1, 0, /* 1c0-1cf */ + -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, -79, 0, -1, /* 1d0-1df */ + 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, /* 1e0-1ef */ + 0, 0, -1, -2, 0, -1, 0, 0, 0, -1, 0, -1, 0, -1, 0, -1, /* 1f0-1ff */ +}; + +/* Upper case range - Greek */ +static signed char UniCaseRangeU03a0[47] = { + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -38, -37, -37, -37, /* 3a0-3af */ + 0, -32, -32, -32, -32, -32, -32, -32, -32, -32, -32, -32, + -32, -32, -32, -32, /* 3b0-3bf */ + -32, -32, -31, -32, -32, -32, -32, -32, -32, -32, -32, -32, -64, + -63, -63, +}; + +/* Upper case range - Cyrillic */ +static signed char UniCaseRangeU0430[48] = { + -32, -32, -32, -32, -32, -32, -32, -32, -32, -32, -32, -32, + -32, -32, -32, -32, /* 430-43f */ + -32, -32, -32, -32, -32, -32, -32, -32, -32, -32, -32, -32, + -32, -32, -32, -32, /* 440-44f */ + 0, -80, -80, -80, -80, -80, -80, -80, -80, -80, -80, + -80, -80, 0, -80, -80, /* 450-45f */ +}; + +/* Upper case range - Extended cyrillic */ +static signed char UniCaseRangeU0490[61] = { + 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, /* 490-49f */ + 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, /* 4a0-4af */ + 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, /* 4b0-4bf */ + 0, 0, -1, 0, -1, 0, 0, 0, -1, 0, 0, 0, -1, +}; + +/* Upper case range - Extended latin and greek */ +static signed char UniCaseRangeU1e00[509] = { + 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, /* 1e00-1e0f */ + 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, /* 1e10-1e1f */ + 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, /* 1e20-1e2f */ + 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, /* 1e30-1e3f */ + 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, /* 1e40-1e4f */ + 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, /* 1e50-1e5f */ + 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, /* 1e60-1e6f */ + 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, /* 1e70-1e7f */ + 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, /* 1e80-1e8f */ + 0, -1, 0, -1, 0, -1, 0, 0, 0, 0, 0, -59, 0, -1, 0, -1, /* 1e90-1e9f */ + 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, /* 1ea0-1eaf */ + 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, /* 1eb0-1ebf */ + 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, /* 1ec0-1ecf */ + 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, /* 1ed0-1edf */ + 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, /* 1ee0-1eef */ + 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, 0, 0, 0, 0, 0, /* 1ef0-1eff */ + 8, 8, 8, 8, 8, 8, 8, 8, 0, 0, 0, 0, 0, 0, 0, 0, /* 1f00-1f0f */ + 8, 8, 8, 8, 8, 8, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 1f10-1f1f */ + 8, 8, 8, 8, 8, 8, 8, 8, 0, 0, 0, 0, 0, 0, 0, 0, /* 1f20-1f2f */ + 8, 8, 8, 8, 8, 8, 8, 8, 0, 0, 0, 0, 0, 0, 0, 0, /* 1f30-1f3f */ + 8, 8, 8, 8, 8, 8, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 1f40-1f4f */ + 0, 8, 0, 8, 0, 8, 0, 8, 0, 0, 0, 0, 0, 0, 0, 0, /* 1f50-1f5f */ + 8, 8, 8, 8, 8, 8, 8, 8, 0, 0, 0, 0, 0, 0, 0, 0, /* 1f60-1f6f */ + 74, 74, 86, 86, 86, 86, 100, 100, 0, 0, 112, 112, + 126, 126, 0, 0, /* 1f70-1f7f */ + 8, 8, 8, 8, 8, 8, 8, 8, 0, 0, 0, 0, 0, 0, 0, 0, /* 1f80-1f8f */ + 8, 8, 8, 8, 8, 8, 8, 8, 0, 0, 0, 0, 0, 0, 0, 0, /* 1f90-1f9f */ + 8, 8, 8, 8, 8, 8, 8, 8, 0, 0, 0, 0, 0, 0, 0, 0, /* 1fa0-1faf */ + 8, 8, 0, 9, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 1fb0-1fbf */ + 0, 0, 0, 9, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 1fc0-1fcf */ + 8, 8, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 1fd0-1fdf */ + 8, 8, 0, 0, 0, 7, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 1fe0-1fef */ + 0, 0, 0, 9, 0, 0, 0, 0, 0, 0, 0, 0, 0, +}; + +/* Upper case range - Wide latin */ +static signed char UniCaseRangeUff40[27] = { + 0, -32, -32, -32, -32, -32, -32, -32, -32, -32, -32, + -32, -32, -32, -32, -32, /* ff40-ff4f */ + -32, -32, -32, -32, -32, -32, -32, -32, -32, -32, -32, +}; + +/* + * Upper Case Range + */ +const struct UniCaseRange SmbUniUpperRange[] = { + {0x03a0, 0x03ce, UniCaseRangeU03a0}, + {0x0430, 0x045f, UniCaseRangeU0430}, + {0x0490, 0x04cc, UniCaseRangeU0490}, + {0x1e00, 0x1ffc, UniCaseRangeU1e00}, + {0xff40, 0xff5a, UniCaseRangeUff40}, + {0} +}; +#endif + +#ifndef UNIUPR_NOLOWER +/* + * Latin lower case + */ +signed char CifsUniLowerTable[512] = { + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 000-00f */ + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 010-01f */ + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 020-02f */ + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 030-03f */ + 0, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, + 32, 32, 32, /* 040-04f */ + 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 0, 0, + 0, 0, 0, /* 050-05f */ + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 060-06f */ + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 070-07f */ + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 080-08f */ + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 090-09f */ + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 0a0-0af */ + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 0b0-0bf */ + 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, + 32, 32, 32, 32, /* 0c0-0cf */ + 32, 32, 32, 32, 32, 32, 32, 0, 32, 32, 32, 32, + 32, 32, 32, 0, /* 0d0-0df */ + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 0e0-0ef */ + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 0f0-0ff */ + 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, /* 100-10f */ + 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, /* 110-11f */ + 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, /* 120-12f */ + 0, 0, 1, 0, 1, 0, 1, 0, 0, 1, 0, 1, 0, 1, 0, 1, /* 130-13f */ + 0, 1, 0, 1, 0, 1, 0, 1, 0, 0, 1, 0, 1, 0, 1, 0, /* 140-14f */ + 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, /* 150-15f */ + 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, /* 160-16f */ + 1, 0, 1, 0, 1, 0, 1, 0, -121, 1, 0, 1, 0, 1, 0, + 0, /* 170-17f */ + 0, 0, 1, 0, 1, 0, 0, 1, 0, 0, 0, 1, 0, 0, 79, + 0, /* 180-18f */ + 0, 1, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, /* 190-19f */ + 1, 0, 1, 0, 1, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 1, /* 1a0-1af */ + 0, 0, 0, 1, 0, 1, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, /* 1b0-1bf */ + 0, 0, 0, 0, 2, 1, 0, 2, 1, 0, 2, 1, 0, 1, 0, 1, /* 1c0-1cf */ + 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 0, 1, 0, /* 1d0-1df */ + 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, /* 1e0-1ef */ + 0, 2, 1, 0, 1, 0, 0, 0, 1, 0, 1, 0, 1, 0, 1, 0, /* 1f0-1ff */ +}; + +/* Lower case range - Greek */ +static signed char UniCaseRangeL0380[44] = { + 0, 0, 0, 0, 0, 0, 38, 0, 37, 37, 37, 0, 64, 0, 63, 63, /* 380-38f */ + 0, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, + 32, 32, 32, /* 390-39f */ + 32, 32, 0, 32, 32, 32, 32, 32, 32, 32, 32, 32, +}; + +/* Lower case range - Cyrillic */ +static signed char UniCaseRangeL0400[48] = { + 0, 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, + 0, 80, 80, /* 400-40f */ + 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, + 32, 32, 32, /* 410-41f */ + 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, + 32, 32, 32, /* 420-42f */ +}; + +/* Lower case range - Extended cyrillic */ +static signed char UniCaseRangeL0490[60] = { + 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, /* 490-49f */ + 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, /* 4a0-4af */ + 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, /* 4b0-4bf */ + 0, 1, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, +}; + +/* Lower case range - Extended latin and greek */ +static signed char UniCaseRangeL1e00[504] = { + 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, /* 1e00-1e0f */ + 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, /* 1e10-1e1f */ + 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, /* 1e20-1e2f */ + 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, /* 1e30-1e3f */ + 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, /* 1e40-1e4f */ + 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, /* 1e50-1e5f */ + 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, /* 1e60-1e6f */ + 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, /* 1e70-1e7f */ + 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, /* 1e80-1e8f */ + 1, 0, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 1, 0, 1, 0, /* 1e90-1e9f */ + 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, /* 1ea0-1eaf */ + 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, /* 1eb0-1ebf */ + 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, /* 1ec0-1ecf */ + 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, /* 1ed0-1edf */ + 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, /* 1ee0-1eef */ + 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, /* 1ef0-1eff */ + 0, 0, 0, 0, 0, 0, 0, 0, -8, -8, -8, -8, -8, -8, -8, -8, /* 1f00-1f0f */ + 0, 0, 0, 0, 0, 0, 0, 0, -8, -8, -8, -8, -8, -8, 0, 0, /* 1f10-1f1f */ + 0, 0, 0, 0, 0, 0, 0, 0, -8, -8, -8, -8, -8, -8, -8, -8, /* 1f20-1f2f */ + 0, 0, 0, 0, 0, 0, 0, 0, -8, -8, -8, -8, -8, -8, -8, -8, /* 1f30-1f3f */ + 0, 0, 0, 0, 0, 0, 0, 0, -8, -8, -8, -8, -8, -8, 0, 0, /* 1f40-1f4f */ + 0, 0, 0, 0, 0, 0, 0, 0, 0, -8, 0, -8, 0, -8, 0, -8, /* 1f50-1f5f */ + 0, 0, 0, 0, 0, 0, 0, 0, -8, -8, -8, -8, -8, -8, -8, -8, /* 1f60-1f6f */ + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 1f70-1f7f */ + 0, 0, 0, 0, 0, 0, 0, 0, -8, -8, -8, -8, -8, -8, -8, -8, /* 1f80-1f8f */ + 0, 0, 0, 0, 0, 0, 0, 0, -8, -8, -8, -8, -8, -8, -8, -8, /* 1f90-1f9f */ + 0, 0, 0, 0, 0, 0, 0, 0, -8, -8, -8, -8, -8, -8, -8, -8, /* 1fa0-1faf */ + 0, 0, 0, 0, 0, 0, 0, 0, -8, -8, -74, -74, -9, 0, 0, 0, /* 1fb0-1fbf */ + 0, 0, 0, 0, 0, 0, 0, 0, -86, -86, -86, -86, -9, 0, + 0, 0, /* 1fc0-1fcf */ + 0, 0, 0, 0, 0, 0, 0, 0, -8, -8, -100, -100, 0, 0, 0, 0, /* 1fd0-1fdf */ + 0, 0, 0, 0, 0, 0, 0, 0, -8, -8, -112, -112, -7, 0, + 0, 0, /* 1fe0-1fef */ + 0, 0, 0, 0, 0, 0, 0, 0, +}; + +/* Lower case range - Wide latin */ +static signed char UniCaseRangeLff20[27] = { + 0, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, + 32, /* ff20-ff2f */ + 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, +}; + +/* + * Lower Case Range + */ +const struct UniCaseRange CifsUniLowerRange[] = { + {0x0380, 0x03ab, UniCaseRangeL0380}, + {0x0400, 0x042f, UniCaseRangeL0400}, + {0x0490, 0x04cb, UniCaseRangeL0490}, + {0x1e00, 0x1ff7, UniCaseRangeL1e00}, + {0xff20, 0xff3a, UniCaseRangeLff20}, + {0} +}; +#endif + +#endif /* __KSMBD_UNIUPR_H */ diff --git a/fs/smb/server/vfs.c b/fs/smb/server/vfs.c new file mode 100644 index 0000000000000..36914db8b6616 --- /dev/null +++ b/fs/smb/server/vfs.c @@ -0,0 +1,1911 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Copyright (C) 2016 Namjae Jeon + * Copyright (C) 2018 Samsung Electronics Co., Ltd. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "../../internal.h" /* for vfs_path_lookup */ + +#include "glob.h" +#include "oplock.h" +#include "connection.h" +#include "vfs.h" +#include "vfs_cache.h" +#include "smbacl.h" +#include "ndr.h" +#include "auth.h" +#include "misc.h" + +#include "smb_common.h" +#include "mgmt/share_config.h" +#include "mgmt/tree_connect.h" +#include "mgmt/user_session.h" +#include "mgmt/user_config.h" + +static char *extract_last_component(char *path) +{ + char *p = strrchr(path, '/'); + + if (p && p[1] != '\0') { + *p = '\0'; + p++; + } else { + p = NULL; + } + return p; +} + +static void ksmbd_vfs_inherit_owner(struct ksmbd_work *work, + struct inode *parent_inode, + struct inode *inode) +{ + if (!test_share_config_flag(work->tcon->share_conf, + KSMBD_SHARE_FLAG_INHERIT_OWNER)) + return; + + i_uid_write(inode, i_uid_read(parent_inode)); +} + +/** + * ksmbd_vfs_lock_parent() - lock parent dentry if it is stable + * + * the parent dentry got by dget_parent or @parent could be + * unstable, we try to lock a parent inode and lookup the + * child dentry again. + * + * the reference count of @parent isn't incremented. + */ +int ksmbd_vfs_lock_parent(struct user_namespace *user_ns, struct dentry *parent, + struct dentry *child) +{ + struct dentry *dentry; + int ret = 0; + + inode_lock_nested(d_inode(parent), I_MUTEX_PARENT); + dentry = lookup_one(user_ns, child->d_name.name, parent, + child->d_name.len); + if (IS_ERR(dentry)) { + ret = PTR_ERR(dentry); + goto out_err; + } + + if (dentry != child) { + ret = -ESTALE; + dput(dentry); + goto out_err; + } + + dput(dentry); + return 0; +out_err: + inode_unlock(d_inode(parent)); + return ret; +} + +int ksmbd_vfs_may_delete(struct user_namespace *user_ns, + struct dentry *dentry) +{ + struct dentry *parent; + int ret; + + parent = dget_parent(dentry); + ret = ksmbd_vfs_lock_parent(user_ns, parent, dentry); + if (ret) { + dput(parent); + return ret; + } + + ret = inode_permission(user_ns, d_inode(parent), + MAY_EXEC | MAY_WRITE); + + inode_unlock(d_inode(parent)); + dput(parent); + return ret; +} + +int ksmbd_vfs_query_maximal_access(struct user_namespace *user_ns, + struct dentry *dentry, __le32 *daccess) +{ + struct dentry *parent; + int ret = 0; + + *daccess = cpu_to_le32(FILE_READ_ATTRIBUTES | READ_CONTROL); + + if (!inode_permission(user_ns, d_inode(dentry), MAY_OPEN | MAY_WRITE)) + *daccess |= cpu_to_le32(WRITE_DAC | WRITE_OWNER | SYNCHRONIZE | + FILE_WRITE_DATA | FILE_APPEND_DATA | + FILE_WRITE_EA | FILE_WRITE_ATTRIBUTES | + FILE_DELETE_CHILD); + + if (!inode_permission(user_ns, d_inode(dentry), MAY_OPEN | MAY_READ)) + *daccess |= FILE_READ_DATA_LE | FILE_READ_EA_LE; + + if (!inode_permission(user_ns, d_inode(dentry), MAY_OPEN | MAY_EXEC)) + *daccess |= FILE_EXECUTE_LE; + + parent = dget_parent(dentry); + ret = ksmbd_vfs_lock_parent(user_ns, parent, dentry); + if (ret) { + dput(parent); + return ret; + } + + if (!inode_permission(user_ns, d_inode(parent), MAY_EXEC | MAY_WRITE)) + *daccess |= FILE_DELETE_LE; + + inode_unlock(d_inode(parent)); + dput(parent); + return ret; +} + +/** + * ksmbd_vfs_create() - vfs helper for smb create file + * @work: work + * @name: file name that is relative to share + * @mode: file create mode + * + * Return: 0 on success, otherwise error + */ +int ksmbd_vfs_create(struct ksmbd_work *work, const char *name, umode_t mode) +{ + struct path path; + struct dentry *dentry; + int err; + + dentry = ksmbd_vfs_kern_path_create(work, name, + LOOKUP_NO_SYMLINKS, &path); + if (IS_ERR(dentry)) { + err = PTR_ERR(dentry); + if (err != -ENOENT) + pr_err("path create failed for %s, err %d\n", + name, err); + return err; + } + + mode |= S_IFREG; + err = vfs_create(mnt_user_ns(path.mnt), d_inode(path.dentry), + dentry, mode, true); + if (!err) { + ksmbd_vfs_inherit_owner(work, d_inode(path.dentry), + d_inode(dentry)); + } else { + pr_err("File(%s): creation failed (err:%d)\n", name, err); + } + done_path_create(&path, dentry); + return err; +} + +/** + * ksmbd_vfs_mkdir() - vfs helper for smb create directory + * @work: work + * @name: directory name that is relative to share + * @mode: directory create mode + * + * Return: 0 on success, otherwise error + */ +int ksmbd_vfs_mkdir(struct ksmbd_work *work, const char *name, umode_t mode) +{ + struct user_namespace *user_ns; + struct path path; + struct dentry *dentry; + int err; + + dentry = ksmbd_vfs_kern_path_create(work, name, + LOOKUP_NO_SYMLINKS | LOOKUP_DIRECTORY, + &path); + if (IS_ERR(dentry)) { + err = PTR_ERR(dentry); + if (err != -EEXIST) + ksmbd_debug(VFS, "path create failed for %s, err %d\n", + name, err); + return err; + } + + user_ns = mnt_user_ns(path.mnt); + mode |= S_IFDIR; + err = vfs_mkdir(user_ns, d_inode(path.dentry), dentry, mode); + if (err) { + goto out; + } else if (d_unhashed(dentry)) { + struct dentry *d; + + d = lookup_one(user_ns, dentry->d_name.name, dentry->d_parent, + dentry->d_name.len); + if (IS_ERR(d)) { + err = PTR_ERR(d); + goto out; + } + if (unlikely(d_is_negative(d))) { + dput(d); + err = -ENOENT; + goto out; + } + + ksmbd_vfs_inherit_owner(work, d_inode(path.dentry), d_inode(d)); + dput(d); + } +out: + done_path_create(&path, dentry); + if (err) + pr_err("mkdir(%s): creation failed (err:%d)\n", name, err); + return err; +} + +static ssize_t ksmbd_vfs_getcasexattr(struct user_namespace *user_ns, + struct dentry *dentry, char *attr_name, + int attr_name_len, char **attr_value) +{ + char *name, *xattr_list = NULL; + ssize_t value_len = -ENOENT, xattr_list_len; + + xattr_list_len = ksmbd_vfs_listxattr(dentry, &xattr_list); + if (xattr_list_len <= 0) + goto out; + + for (name = xattr_list; name - xattr_list < xattr_list_len; + name += strlen(name) + 1) { + ksmbd_debug(VFS, "%s, len %zd\n", name, strlen(name)); + if (strncasecmp(attr_name, name, attr_name_len)) + continue; + + value_len = ksmbd_vfs_getxattr(user_ns, + dentry, + name, + attr_value); + if (value_len < 0) + pr_err("failed to get xattr in file\n"); + break; + } + +out: + kvfree(xattr_list); + return value_len; +} + +static int ksmbd_vfs_stream_read(struct ksmbd_file *fp, char *buf, loff_t *pos, + size_t count) +{ + ssize_t v_len; + char *stream_buf = NULL; + + ksmbd_debug(VFS, "read stream data pos : %llu, count : %zd\n", + *pos, count); + + v_len = ksmbd_vfs_getcasexattr(file_mnt_user_ns(fp->filp), + fp->filp->f_path.dentry, + fp->stream.name, + fp->stream.size, + &stream_buf); + if ((int)v_len <= 0) + return (int)v_len; + + if (v_len <= *pos) { + count = -EINVAL; + goto free_buf; + } + + if (v_len - *pos < count) + count = v_len - *pos; + + memcpy(buf, &stream_buf[*pos], count); + +free_buf: + kvfree(stream_buf); + return count; +} + +/** + * check_lock_range() - vfs helper for smb byte range file locking + * @filp: the file to apply the lock to + * @start: lock start byte offset + * @end: lock end byte offset + * @type: byte range type read/write + * + * Return: 0 on success, otherwise error + */ +static int check_lock_range(struct file *filp, loff_t start, loff_t end, + unsigned char type) +{ + struct file_lock *flock; + struct file_lock_context *ctx = file_inode(filp)->i_flctx; + int error = 0; + + if (!ctx || list_empty_careful(&ctx->flc_posix)) + return 0; + + spin_lock(&ctx->flc_lock); + list_for_each_entry(flock, &ctx->flc_posix, fl_list) { + /* check conflict locks */ + if (flock->fl_end >= start && end >= flock->fl_start) { + if (flock->fl_type == F_RDLCK) { + if (type == WRITE) { + pr_err("not allow write by shared lock\n"); + error = 1; + goto out; + } + } else if (flock->fl_type == F_WRLCK) { + /* check owner in lock */ + if (flock->fl_file != filp) { + error = 1; + pr_err("not allow rw access by exclusive lock from other opens\n"); + goto out; + } + } + } + } +out: + spin_unlock(&ctx->flc_lock); + return error; +} + +/** + * ksmbd_vfs_read() - vfs helper for smb file read + * @work: smb work + * @fid: file id of open file + * @count: read byte count + * @pos: file pos + * + * Return: number of read bytes on success, otherwise error + */ +int ksmbd_vfs_read(struct ksmbd_work *work, struct ksmbd_file *fp, size_t count, + loff_t *pos) +{ + struct file *filp = fp->filp; + ssize_t nbytes = 0; + char *rbuf = work->aux_payload_buf; + struct inode *inode = file_inode(filp); + + if (S_ISDIR(inode->i_mode)) + return -EISDIR; + + if (unlikely(count == 0)) + return 0; + + if (work->conn->connection_type) { + if (!(fp->daccess & (FILE_READ_DATA_LE | FILE_EXECUTE_LE))) { + pr_err("no right to read(%pD)\n", fp->filp); + return -EACCES; + } + } + + if (ksmbd_stream_fd(fp)) + return ksmbd_vfs_stream_read(fp, rbuf, pos, count); + + if (!work->tcon->posix_extensions) { + int ret; + + ret = check_lock_range(filp, *pos, *pos + count - 1, READ); + if (ret) { + pr_err("unable to read due to lock\n"); + return -EAGAIN; + } + } + + nbytes = kernel_read(filp, rbuf, count, pos); + if (nbytes < 0) { + pr_err("smb read failed, err = %zd\n", nbytes); + return nbytes; + } + + filp->f_pos = *pos; + return nbytes; +} + +static int ksmbd_vfs_stream_write(struct ksmbd_file *fp, char *buf, loff_t *pos, + size_t count) +{ + char *stream_buf = NULL, *wbuf; + struct user_namespace *user_ns = file_mnt_user_ns(fp->filp); + size_t size, v_len; + int err = 0; + + ksmbd_debug(VFS, "write stream data pos : %llu, count : %zd\n", + *pos, count); + + size = *pos + count; + if (size > XATTR_SIZE_MAX) { + size = XATTR_SIZE_MAX; + count = (*pos + count) - XATTR_SIZE_MAX; + } + + v_len = ksmbd_vfs_getcasexattr(user_ns, + fp->filp->f_path.dentry, + fp->stream.name, + fp->stream.size, + &stream_buf); + if ((int)v_len < 0) { + pr_err("not found stream in xattr : %zd\n", v_len); + err = (int)v_len; + goto out; + } + + if (v_len < size) { + wbuf = kvmalloc(size, GFP_KERNEL | __GFP_ZERO); + if (!wbuf) { + err = -ENOMEM; + goto out; + } + + if (v_len > 0) + memcpy(wbuf, stream_buf, v_len); + kvfree(stream_buf); + stream_buf = wbuf; + } + + memcpy(&stream_buf[*pos], buf, count); + + err = ksmbd_vfs_setxattr(user_ns, + fp->filp->f_path.dentry, + fp->stream.name, + (void *)stream_buf, + size, + 0); + if (err < 0) + goto out; + + fp->filp->f_pos = *pos; + err = 0; +out: + kvfree(stream_buf); + return err; +} + +/** + * ksmbd_vfs_write() - vfs helper for smb file write + * @work: work + * @fid: file id of open file + * @buf: buf containing data for writing + * @count: read byte count + * @pos: file pos + * @sync: fsync after write + * @written: number of bytes written + * + * Return: 0 on success, otherwise error + */ +int ksmbd_vfs_write(struct ksmbd_work *work, struct ksmbd_file *fp, + char *buf, size_t count, loff_t *pos, bool sync, + ssize_t *written) +{ + struct file *filp; + loff_t offset = *pos; + int err = 0; + + if (work->conn->connection_type) { + if (!(fp->daccess & FILE_WRITE_DATA_LE)) { + pr_err("no right to write(%pD)\n", fp->filp); + err = -EACCES; + goto out; + } + } + + filp = fp->filp; + + if (ksmbd_stream_fd(fp)) { + err = ksmbd_vfs_stream_write(fp, buf, pos, count); + if (!err) + *written = count; + goto out; + } + + if (!work->tcon->posix_extensions) { + err = check_lock_range(filp, *pos, *pos + count - 1, WRITE); + if (err) { + pr_err("unable to write due to lock\n"); + err = -EAGAIN; + goto out; + } + } + + /* Do we need to break any of a levelII oplock? */ + smb_break_all_levII_oplock(work, fp, 1); + + err = kernel_write(filp, buf, count, pos); + if (err < 0) { + ksmbd_debug(VFS, "smb write failed, err = %d\n", err); + goto out; + } + + filp->f_pos = *pos; + *written = err; + err = 0; + if (sync) { + err = vfs_fsync_range(filp, offset, offset + *written, 0); + if (err < 0) + pr_err("fsync failed for filename = %pD, err = %d\n", + fp->filp, err); + } + +out: + return err; +} + +/** + * ksmbd_vfs_getattr() - vfs helper for smb getattr + * @work: work + * @fid: file id of open file + * @attrs: inode attributes + * + * Return: 0 on success, otherwise error + */ +int ksmbd_vfs_getattr(const struct path *path, struct kstat *stat) +{ + int err; + + err = vfs_getattr(path, stat, STATX_BTIME, AT_STATX_SYNC_AS_STAT); + if (err) + pr_err("getattr failed, err %d\n", err); + return err; +} + +/** + * ksmbd_vfs_fsync() - vfs helper for smb fsync + * @work: work + * @fid: file id of open file + * + * Return: 0 on success, otherwise error + */ +int ksmbd_vfs_fsync(struct ksmbd_work *work, u64 fid, u64 p_id) +{ + struct ksmbd_file *fp; + int err; + + fp = ksmbd_lookup_fd_slow(work, fid, p_id); + if (!fp) { + pr_err("failed to get filp for fid %llu\n", fid); + return -ENOENT; + } + err = vfs_fsync(fp->filp, 0); + if (err < 0) + pr_err("smb fsync failed, err = %d\n", err); + ksmbd_fd_put(work, fp); + return err; +} + +/** + * ksmbd_vfs_remove_file() - vfs helper for smb rmdir or unlink + * @name: directory or file name that is relative to share + * + * Return: 0 on success, otherwise error + */ +int ksmbd_vfs_remove_file(struct ksmbd_work *work, char *name) +{ + struct user_namespace *user_ns; + struct path path; + struct dentry *parent; + int err; + + if (ksmbd_override_fsids(work)) + return -ENOMEM; + + err = ksmbd_vfs_kern_path(work, name, LOOKUP_NO_SYMLINKS, &path, false); + if (err) { + ksmbd_debug(VFS, "can't get %s, err %d\n", name, err); + ksmbd_revert_fsids(work); + return err; + } + + user_ns = mnt_user_ns(path.mnt); + parent = dget_parent(path.dentry); + err = ksmbd_vfs_lock_parent(user_ns, parent, path.dentry); + if (err) { + dput(parent); + path_put(&path); + ksmbd_revert_fsids(work); + return err; + } + + if (!d_inode(path.dentry)->i_nlink) { + err = -ENOENT; + goto out_err; + } + + if (S_ISDIR(d_inode(path.dentry)->i_mode)) { + err = vfs_rmdir(user_ns, d_inode(parent), path.dentry); + if (err && err != -ENOTEMPTY) + ksmbd_debug(VFS, "%s: rmdir failed, err %d\n", name, + err); + } else { + err = vfs_unlink(user_ns, d_inode(parent), path.dentry, NULL); + if (err) + ksmbd_debug(VFS, "%s: unlink failed, err %d\n", name, + err); + } + +out_err: + inode_unlock(d_inode(parent)); + dput(parent); + path_put(&path); + ksmbd_revert_fsids(work); + return err; +} + +/** + * ksmbd_vfs_link() - vfs helper for creating smb hardlink + * @oldname: source file name + * @newname: hardlink name that is relative to share + * + * Return: 0 on success, otherwise error + */ +int ksmbd_vfs_link(struct ksmbd_work *work, const char *oldname, + const char *newname) +{ + struct path oldpath, newpath; + struct dentry *dentry; + int err; + + if (ksmbd_override_fsids(work)) + return -ENOMEM; + + err = kern_path(oldname, LOOKUP_NO_SYMLINKS, &oldpath); + if (err) { + pr_err("cannot get linux path for %s, err = %d\n", + oldname, err); + goto out1; + } + + dentry = ksmbd_vfs_kern_path_create(work, newname, + LOOKUP_NO_SYMLINKS | LOOKUP_REVAL, + &newpath); + if (IS_ERR(dentry)) { + err = PTR_ERR(dentry); + pr_err("path create err for %s, err %d\n", newname, err); + goto out2; + } + + err = -EXDEV; + if (oldpath.mnt != newpath.mnt) { + pr_err("vfs_link failed err %d\n", err); + goto out3; + } + + err = vfs_link(oldpath.dentry, mnt_user_ns(newpath.mnt), + d_inode(newpath.dentry), + dentry, NULL); + if (err) + ksmbd_debug(VFS, "vfs_link failed err %d\n", err); + +out3: + done_path_create(&newpath, dentry); +out2: + path_put(&oldpath); +out1: + ksmbd_revert_fsids(work); + return err; +} + +static int ksmbd_validate_entry_in_use(struct dentry *src_dent) +{ + struct dentry *dst_dent; + + spin_lock(&src_dent->d_lock); + list_for_each_entry(dst_dent, &src_dent->d_subdirs, d_child) { + struct ksmbd_file *child_fp; + + if (d_really_is_negative(dst_dent)) + continue; + + child_fp = ksmbd_lookup_fd_inode(d_inode(dst_dent)); + if (child_fp) { + spin_unlock(&src_dent->d_lock); + ksmbd_debug(VFS, "Forbid rename, sub file/dir is in use\n"); + return -EACCES; + } + } + spin_unlock(&src_dent->d_lock); + + return 0; +} + +static int __ksmbd_vfs_rename(struct ksmbd_work *work, + struct user_namespace *src_user_ns, + struct dentry *src_dent_parent, + struct dentry *src_dent, + struct user_namespace *dst_user_ns, + struct dentry *dst_dent_parent, + struct dentry *trap_dent, + char *dst_name) +{ + struct dentry *dst_dent; + int err; + + if (!work->tcon->posix_extensions) { + err = ksmbd_validate_entry_in_use(src_dent); + if (err) + return err; + } + + if (d_really_is_negative(src_dent_parent)) + return -ENOENT; + if (d_really_is_negative(dst_dent_parent)) + return -ENOENT; + if (d_really_is_negative(src_dent)) + return -ENOENT; + if (src_dent == trap_dent) + return -EINVAL; + + if (ksmbd_override_fsids(work)) + return -ENOMEM; + + dst_dent = lookup_one(dst_user_ns, dst_name, dst_dent_parent, + strlen(dst_name)); + err = PTR_ERR(dst_dent); + if (IS_ERR(dst_dent)) { + pr_err("lookup failed %s [%d]\n", dst_name, err); + goto out; + } + + err = -ENOTEMPTY; + if (dst_dent != trap_dent && !d_really_is_positive(dst_dent)) { + struct renamedata rd = { + .old_mnt_userns = src_user_ns, + .old_dir = d_inode(src_dent_parent), + .old_dentry = src_dent, + .new_mnt_userns = dst_user_ns, + .new_dir = d_inode(dst_dent_parent), + .new_dentry = dst_dent, + }; + err = vfs_rename(&rd); + } + if (err) + pr_err("vfs_rename failed err %d\n", err); + if (dst_dent) + dput(dst_dent); +out: + ksmbd_revert_fsids(work); + return err; +} + +int ksmbd_vfs_fp_rename(struct ksmbd_work *work, struct ksmbd_file *fp, + char *newname) +{ + struct user_namespace *user_ns; + struct path dst_path; + struct dentry *src_dent_parent, *dst_dent_parent; + struct dentry *src_dent, *trap_dent, *src_child; + char *dst_name; + int err; + + dst_name = extract_last_component(newname); + if (!dst_name) { + dst_name = newname; + newname = ""; + } + + src_dent_parent = dget_parent(fp->filp->f_path.dentry); + src_dent = fp->filp->f_path.dentry; + + err = ksmbd_vfs_kern_path(work, newname, + LOOKUP_NO_SYMLINKS | LOOKUP_DIRECTORY, + &dst_path, false); + if (err) { + ksmbd_debug(VFS, "Cannot get path for %s [%d]\n", newname, err); + goto out; + } + dst_dent_parent = dst_path.dentry; + + trap_dent = lock_rename(src_dent_parent, dst_dent_parent); + dget(src_dent); + dget(dst_dent_parent); + user_ns = file_mnt_user_ns(fp->filp); + src_child = lookup_one(user_ns, src_dent->d_name.name, src_dent_parent, + src_dent->d_name.len); + if (IS_ERR(src_child)) { + err = PTR_ERR(src_child); + goto out_lock; + } + + if (src_child != src_dent) { + err = -ESTALE; + dput(src_child); + goto out_lock; + } + dput(src_child); + + err = __ksmbd_vfs_rename(work, + user_ns, + src_dent_parent, + src_dent, + mnt_user_ns(dst_path.mnt), + dst_dent_parent, + trap_dent, + dst_name); +out_lock: + dput(src_dent); + dput(dst_dent_parent); + unlock_rename(src_dent_parent, dst_dent_parent); + path_put(&dst_path); +out: + dput(src_dent_parent); + return err; +} + +/** + * ksmbd_vfs_truncate() - vfs helper for smb file truncate + * @work: work + * @fid: file id of old file + * @size: truncate to given size + * + * Return: 0 on success, otherwise error + */ +int ksmbd_vfs_truncate(struct ksmbd_work *work, + struct ksmbd_file *fp, loff_t size) +{ + int err = 0; + struct file *filp; + + filp = fp->filp; + + /* Do we need to break any of a levelII oplock? */ + smb_break_all_levII_oplock(work, fp, 1); + + if (!work->tcon->posix_extensions) { + struct inode *inode = file_inode(filp); + + if (size < inode->i_size) { + err = check_lock_range(filp, size, + inode->i_size - 1, WRITE); + } else { + err = check_lock_range(filp, inode->i_size, + size - 1, WRITE); + } + + if (err) { + pr_err("failed due to lock\n"); + return -EAGAIN; + } + } + + err = vfs_truncate(&filp->f_path, size); + if (err) + pr_err("truncate failed, err %d\n", err); + return err; +} + +/** + * ksmbd_vfs_listxattr() - vfs helper for smb list extended attributes + * @dentry: dentry of file for listing xattrs + * @list: destination buffer + * @size: destination buffer length + * + * Return: xattr list length on success, otherwise error + */ +ssize_t ksmbd_vfs_listxattr(struct dentry *dentry, char **list) +{ + ssize_t size; + char *vlist = NULL; + + size = vfs_listxattr(dentry, NULL, 0); + if (size <= 0) + return size; + + vlist = kvmalloc(size, GFP_KERNEL | __GFP_ZERO); + if (!vlist) + return -ENOMEM; + + *list = vlist; + size = vfs_listxattr(dentry, vlist, size); + if (size < 0) { + ksmbd_debug(VFS, "listxattr failed\n"); + kvfree(vlist); + *list = NULL; + } + + return size; +} + +static ssize_t ksmbd_vfs_xattr_len(struct user_namespace *user_ns, + struct dentry *dentry, char *xattr_name) +{ + return vfs_getxattr(user_ns, dentry, xattr_name, NULL, 0); +} + +/** + * ksmbd_vfs_getxattr() - vfs helper for smb get extended attributes value + * @user_ns: user namespace + * @dentry: dentry of file for getting xattrs + * @xattr_name: name of xattr name to query + * @xattr_buf: destination buffer xattr value + * + * Return: read xattr value length on success, otherwise error + */ +ssize_t ksmbd_vfs_getxattr(struct user_namespace *user_ns, + struct dentry *dentry, + char *xattr_name, char **xattr_buf) +{ + ssize_t xattr_len; + char *buf; + + *xattr_buf = NULL; + xattr_len = ksmbd_vfs_xattr_len(user_ns, dentry, xattr_name); + if (xattr_len < 0) + return xattr_len; + + buf = kmalloc(xattr_len + 1, GFP_KERNEL); + if (!buf) + return -ENOMEM; + + xattr_len = vfs_getxattr(user_ns, dentry, xattr_name, + (void *)buf, xattr_len); + if (xattr_len > 0) + *xattr_buf = buf; + else + kfree(buf); + return xattr_len; +} + +/** + * ksmbd_vfs_setxattr() - vfs helper for smb set extended attributes value + * @user_ns: user namespace + * @dentry: dentry to set XATTR at + * @name: xattr name for setxattr + * @value: xattr value to set + * @size: size of xattr value + * @flags: destination buffer length + * + * Return: 0 on success, otherwise error + */ +int ksmbd_vfs_setxattr(struct user_namespace *user_ns, + struct dentry *dentry, const char *attr_name, + void *attr_value, size_t attr_size, int flags) +{ + int err; + + err = vfs_setxattr(user_ns, + dentry, + attr_name, + attr_value, + attr_size, + flags); + if (err) + ksmbd_debug(VFS, "setxattr failed, err %d\n", err); + return err; +} + +/** + * ksmbd_vfs_set_fadvise() - convert smb IO caching options to linux options + * @filp: file pointer for IO + * @options: smb IO options + */ +void ksmbd_vfs_set_fadvise(struct file *filp, __le32 option) +{ + struct address_space *mapping; + + mapping = filp->f_mapping; + + if (!option || !mapping) + return; + + if (option & FILE_WRITE_THROUGH_LE) { + filp->f_flags |= O_SYNC; + } else if (option & FILE_SEQUENTIAL_ONLY_LE) { + filp->f_ra.ra_pages = inode_to_bdi(mapping->host)->ra_pages * 2; + spin_lock(&filp->f_lock); + filp->f_mode &= ~FMODE_RANDOM; + spin_unlock(&filp->f_lock); + } else if (option & FILE_RANDOM_ACCESS_LE) { + spin_lock(&filp->f_lock); + filp->f_mode |= FMODE_RANDOM; + spin_unlock(&filp->f_lock); + } +} + +int ksmbd_vfs_zero_data(struct ksmbd_work *work, struct ksmbd_file *fp, + loff_t off, loff_t len) +{ + smb_break_all_levII_oplock(work, fp, 1); + if (fp->f_ci->m_fattr & FILE_ATTRIBUTE_SPARSE_FILE_LE) + return vfs_fallocate(fp->filp, + FALLOC_FL_PUNCH_HOLE | FALLOC_FL_KEEP_SIZE, + off, len); + + return vfs_fallocate(fp->filp, + FALLOC_FL_ZERO_RANGE | FALLOC_FL_KEEP_SIZE, + off, len); +} + +int ksmbd_vfs_fqar_lseek(struct ksmbd_file *fp, loff_t start, loff_t length, + struct file_allocated_range_buffer *ranges, + unsigned int in_count, unsigned int *out_count) +{ + struct file *f = fp->filp; + struct inode *inode = file_inode(fp->filp); + loff_t maxbytes = (u64)inode->i_sb->s_maxbytes, end; + loff_t extent_start, extent_end; + int ret = 0; + + if (start > maxbytes) + return -EFBIG; + + if (!in_count) + return 0; + + /* + * Shrink request scope to what the fs can actually handle. + */ + if (length > maxbytes || (maxbytes - length) < start) + length = maxbytes - start; + + if (start + length > inode->i_size) + length = inode->i_size - start; + + *out_count = 0; + end = start + length; + while (start < end && *out_count < in_count) { + extent_start = vfs_llseek(f, start, SEEK_DATA); + if (extent_start < 0) { + if (extent_start != -ENXIO) + ret = (int)extent_start; + break; + } + + if (extent_start >= end) + break; + + extent_end = vfs_llseek(f, extent_start, SEEK_HOLE); + if (extent_end < 0) { + if (extent_end != -ENXIO) + ret = (int)extent_end; + break; + } else if (extent_start >= extent_end) { + break; + } + + ranges[*out_count].file_offset = cpu_to_le64(extent_start); + ranges[(*out_count)++].length = + cpu_to_le64(min(extent_end, end) - extent_start); + + start = extent_end; + } + + return ret; +} + +int ksmbd_vfs_remove_xattr(struct user_namespace *user_ns, + struct dentry *dentry, char *attr_name) +{ + return vfs_removexattr(user_ns, dentry, attr_name); +} + +int ksmbd_vfs_unlink(struct user_namespace *user_ns, + struct dentry *dir, struct dentry *dentry) +{ + int err = 0; + + err = ksmbd_vfs_lock_parent(user_ns, dir, dentry); + if (err) + return err; + dget(dentry); + + if (S_ISDIR(d_inode(dentry)->i_mode)) + err = vfs_rmdir(user_ns, d_inode(dir), dentry); + else + err = vfs_unlink(user_ns, d_inode(dir), dentry, NULL); + + dput(dentry); + inode_unlock(d_inode(dir)); + if (err) + ksmbd_debug(VFS, "failed to delete, err %d\n", err); + + return err; +} + +static bool __dir_empty(struct dir_context *ctx, const char *name, int namlen, + loff_t offset, u64 ino, unsigned int d_type) +{ + struct ksmbd_readdir_data *buf; + + buf = container_of(ctx, struct ksmbd_readdir_data, ctx); + buf->dirent_count++; + + return buf->dirent_count <= 2; +} + +/** + * ksmbd_vfs_empty_dir() - check for empty directory + * @fp: ksmbd file pointer + * + * Return: true if directory empty, otherwise false + */ +int ksmbd_vfs_empty_dir(struct ksmbd_file *fp) +{ + int err; + struct ksmbd_readdir_data readdir_data; + + memset(&readdir_data, 0, sizeof(struct ksmbd_readdir_data)); + + set_ctx_actor(&readdir_data.ctx, __dir_empty); + readdir_data.dirent_count = 0; + + err = iterate_dir(fp->filp, &readdir_data.ctx); + if (readdir_data.dirent_count > 2) + err = -ENOTEMPTY; + else + err = 0; + return err; +} + +static bool __caseless_lookup(struct dir_context *ctx, const char *name, + int namlen, loff_t offset, u64 ino, + unsigned int d_type) +{ + struct ksmbd_readdir_data *buf; + int cmp = -EINVAL; + + buf = container_of(ctx, struct ksmbd_readdir_data, ctx); + + if (buf->used != namlen) + return true; + if (IS_ENABLED(CONFIG_UNICODE) && buf->um) { + const struct qstr q_buf = {.name = buf->private, + .len = buf->used}; + const struct qstr q_name = {.name = name, + .len = namlen}; + + cmp = utf8_strncasecmp(buf->um, &q_buf, &q_name); + } + if (cmp < 0) + cmp = strncasecmp((char *)buf->private, name, namlen); + if (!cmp) { + memcpy((char *)buf->private, name, namlen); + buf->dirent_count = 1; + return false; + } + return true; +} + +/** + * ksmbd_vfs_lookup_in_dir() - lookup a file in a directory + * @dir: path info + * @name: filename to lookup + * @namelen: filename length + * + * Return: 0 on success, otherwise error + */ +static int ksmbd_vfs_lookup_in_dir(const struct path *dir, char *name, + size_t namelen, struct unicode_map *um) +{ + int ret; + struct file *dfilp; + int flags = O_RDONLY | O_LARGEFILE; + struct ksmbd_readdir_data readdir_data = { + .ctx.actor = __caseless_lookup, + .private = name, + .used = namelen, + .dirent_count = 0, + .um = um, + }; + + dfilp = dentry_open(dir, flags, current_cred()); + if (IS_ERR(dfilp)) + return PTR_ERR(dfilp); + + ret = iterate_dir(dfilp, &readdir_data.ctx); + if (readdir_data.dirent_count > 0) + ret = 0; + fput(dfilp); + return ret; +} + +/** + * ksmbd_vfs_kern_path() - lookup a file and get path info + * @name: file path that is relative to share + * @flags: lookup flags + * @path: if lookup succeed, return path info + * @caseless: caseless filename lookup + * + * Return: 0 on success, otherwise error + */ +int ksmbd_vfs_kern_path(struct ksmbd_work *work, char *name, + unsigned int flags, struct path *path, bool caseless) +{ + struct ksmbd_share_config *share_conf = work->tcon->share_conf; + int err; + + flags |= LOOKUP_BENEATH; + err = vfs_path_lookup(share_conf->vfs_path.dentry, + share_conf->vfs_path.mnt, + name, + flags, + path); + if (!err) + return 0; + + if (caseless) { + char *filepath; + struct path parent; + size_t path_len, remain_len; + + filepath = kstrdup(name, GFP_KERNEL); + if (!filepath) + return -ENOMEM; + + path_len = strlen(filepath); + remain_len = path_len; + + parent = share_conf->vfs_path; + path_get(&parent); + + while (d_can_lookup(parent.dentry)) { + char *filename = filepath + path_len - remain_len; + char *next = strchrnul(filename, '/'); + size_t filename_len = next - filename; + bool is_last = !next[0]; + + if (filename_len == 0) + break; + + err = ksmbd_vfs_lookup_in_dir(&parent, filename, + filename_len, + work->conn->um); + path_put(&parent); + if (err) + goto out; + + next[0] = '\0'; + + err = vfs_path_lookup(share_conf->vfs_path.dentry, + share_conf->vfs_path.mnt, + filepath, + flags, + &parent); + if (err) + goto out; + else if (is_last) { + *path = parent; + goto out; + } + + next[0] = '/'; + remain_len -= filename_len + 1; + } + + path_put(&parent); + err = -EINVAL; +out: + kfree(filepath); + } + return err; +} + +struct dentry *ksmbd_vfs_kern_path_create(struct ksmbd_work *work, + const char *name, + unsigned int flags, + struct path *path) +{ + char *abs_name; + struct dentry *dent; + + abs_name = convert_to_unix_name(work->tcon->share_conf, name); + if (!abs_name) + return ERR_PTR(-ENOMEM); + + dent = kern_path_create(AT_FDCWD, abs_name, path, flags); + kfree(abs_name); + return dent; +} + +int ksmbd_vfs_remove_acl_xattrs(struct user_namespace *user_ns, + struct dentry *dentry) +{ + char *name, *xattr_list = NULL; + ssize_t xattr_list_len; + int err = 0; + + xattr_list_len = ksmbd_vfs_listxattr(dentry, &xattr_list); + if (xattr_list_len < 0) { + goto out; + } else if (!xattr_list_len) { + ksmbd_debug(SMB, "empty xattr in the file\n"); + goto out; + } + + for (name = xattr_list; name - xattr_list < xattr_list_len; + name += strlen(name) + 1) { + ksmbd_debug(SMB, "%s, len %zd\n", name, strlen(name)); + + if (!strncmp(name, XATTR_NAME_POSIX_ACL_ACCESS, + sizeof(XATTR_NAME_POSIX_ACL_ACCESS) - 1) || + !strncmp(name, XATTR_NAME_POSIX_ACL_DEFAULT, + sizeof(XATTR_NAME_POSIX_ACL_DEFAULT) - 1)) { + err = ksmbd_vfs_remove_xattr(user_ns, dentry, name); + if (err) + ksmbd_debug(SMB, + "remove acl xattr failed : %s\n", name); + } + } +out: + kvfree(xattr_list); + return err; +} + +int ksmbd_vfs_remove_sd_xattrs(struct user_namespace *user_ns, + struct dentry *dentry) +{ + char *name, *xattr_list = NULL; + ssize_t xattr_list_len; + int err = 0; + + xattr_list_len = ksmbd_vfs_listxattr(dentry, &xattr_list); + if (xattr_list_len < 0) { + goto out; + } else if (!xattr_list_len) { + ksmbd_debug(SMB, "empty xattr in the file\n"); + goto out; + } + + for (name = xattr_list; name - xattr_list < xattr_list_len; + name += strlen(name) + 1) { + ksmbd_debug(SMB, "%s, len %zd\n", name, strlen(name)); + + if (!strncmp(name, XATTR_NAME_SD, XATTR_NAME_SD_LEN)) { + err = ksmbd_vfs_remove_xattr(user_ns, dentry, name); + if (err) + ksmbd_debug(SMB, "remove xattr failed : %s\n", name); + } + } +out: + kvfree(xattr_list); + return err; +} + +static struct xattr_smb_acl *ksmbd_vfs_make_xattr_posix_acl(struct user_namespace *user_ns, + struct inode *inode, + int acl_type) +{ + struct xattr_smb_acl *smb_acl = NULL; + struct posix_acl *posix_acls; + struct posix_acl_entry *pa_entry; + struct xattr_acl_entry *xa_entry; + int i; + + if (!IS_ENABLED(CONFIG_FS_POSIX_ACL)) + return NULL; + + posix_acls = get_acl(inode, acl_type); + if (!posix_acls) + return NULL; + + smb_acl = kzalloc(sizeof(struct xattr_smb_acl) + + sizeof(struct xattr_acl_entry) * posix_acls->a_count, + GFP_KERNEL); + if (!smb_acl) + goto out; + + smb_acl->count = posix_acls->a_count; + pa_entry = posix_acls->a_entries; + xa_entry = smb_acl->entries; + for (i = 0; i < posix_acls->a_count; i++, pa_entry++, xa_entry++) { + switch (pa_entry->e_tag) { + case ACL_USER: + xa_entry->type = SMB_ACL_USER; + xa_entry->uid = posix_acl_uid_translate(user_ns, pa_entry); + break; + case ACL_USER_OBJ: + xa_entry->type = SMB_ACL_USER_OBJ; + break; + case ACL_GROUP: + xa_entry->type = SMB_ACL_GROUP; + xa_entry->gid = posix_acl_gid_translate(user_ns, pa_entry); + break; + case ACL_GROUP_OBJ: + xa_entry->type = SMB_ACL_GROUP_OBJ; + break; + case ACL_OTHER: + xa_entry->type = SMB_ACL_OTHER; + break; + case ACL_MASK: + xa_entry->type = SMB_ACL_MASK; + break; + default: + pr_err("unknown type : 0x%x\n", pa_entry->e_tag); + goto out; + } + + if (pa_entry->e_perm & ACL_READ) + xa_entry->perm |= SMB_ACL_READ; + if (pa_entry->e_perm & ACL_WRITE) + xa_entry->perm |= SMB_ACL_WRITE; + if (pa_entry->e_perm & ACL_EXECUTE) + xa_entry->perm |= SMB_ACL_EXECUTE; + } +out: + posix_acl_release(posix_acls); + return smb_acl; +} + +int ksmbd_vfs_set_sd_xattr(struct ksmbd_conn *conn, + struct user_namespace *user_ns, + struct dentry *dentry, + struct smb_ntsd *pntsd, int len) +{ + int rc; + struct ndr sd_ndr = {0}, acl_ndr = {0}; + struct xattr_ntacl acl = {0}; + struct xattr_smb_acl *smb_acl, *def_smb_acl = NULL; + struct inode *inode = d_inode(dentry); + + acl.version = 4; + acl.hash_type = XATTR_SD_HASH_TYPE_SHA256; + acl.current_time = ksmbd_UnixTimeToNT(current_time(inode)); + + memcpy(acl.desc, "posix_acl", 9); + acl.desc_len = 10; + + pntsd->osidoffset = + cpu_to_le32(le32_to_cpu(pntsd->osidoffset) + NDR_NTSD_OFFSETOF); + pntsd->gsidoffset = + cpu_to_le32(le32_to_cpu(pntsd->gsidoffset) + NDR_NTSD_OFFSETOF); + pntsd->dacloffset = + cpu_to_le32(le32_to_cpu(pntsd->dacloffset) + NDR_NTSD_OFFSETOF); + + acl.sd_buf = (char *)pntsd; + acl.sd_size = len; + + rc = ksmbd_gen_sd_hash(conn, acl.sd_buf, acl.sd_size, acl.hash); + if (rc) { + pr_err("failed to generate hash for ndr acl\n"); + return rc; + } + + smb_acl = ksmbd_vfs_make_xattr_posix_acl(user_ns, inode, + ACL_TYPE_ACCESS); + if (S_ISDIR(inode->i_mode)) + def_smb_acl = ksmbd_vfs_make_xattr_posix_acl(user_ns, inode, + ACL_TYPE_DEFAULT); + + rc = ndr_encode_posix_acl(&acl_ndr, user_ns, inode, + smb_acl, def_smb_acl); + if (rc) { + pr_err("failed to encode ndr to posix acl\n"); + goto out; + } + + rc = ksmbd_gen_sd_hash(conn, acl_ndr.data, acl_ndr.offset, + acl.posix_acl_hash); + if (rc) { + pr_err("failed to generate hash for ndr acl\n"); + goto out; + } + + rc = ndr_encode_v4_ntacl(&sd_ndr, &acl); + if (rc) { + pr_err("failed to encode ndr to posix acl\n"); + goto out; + } + + rc = ksmbd_vfs_setxattr(user_ns, dentry, + XATTR_NAME_SD, sd_ndr.data, + sd_ndr.offset, 0); + if (rc < 0) + pr_err("Failed to store XATTR ntacl :%d\n", rc); + + kfree(sd_ndr.data); +out: + kfree(acl_ndr.data); + kfree(smb_acl); + kfree(def_smb_acl); + return rc; +} + +int ksmbd_vfs_get_sd_xattr(struct ksmbd_conn *conn, + struct user_namespace *user_ns, + struct dentry *dentry, + struct smb_ntsd **pntsd) +{ + int rc; + struct ndr n; + struct inode *inode = d_inode(dentry); + struct ndr acl_ndr = {0}; + struct xattr_ntacl acl; + struct xattr_smb_acl *smb_acl = NULL, *def_smb_acl = NULL; + __u8 cmp_hash[XATTR_SD_HASH_SIZE] = {0}; + + rc = ksmbd_vfs_getxattr(user_ns, dentry, XATTR_NAME_SD, &n.data); + if (rc <= 0) + return rc; + + n.length = rc; + rc = ndr_decode_v4_ntacl(&n, &acl); + if (rc) + goto free_n_data; + + smb_acl = ksmbd_vfs_make_xattr_posix_acl(user_ns, inode, + ACL_TYPE_ACCESS); + if (S_ISDIR(inode->i_mode)) + def_smb_acl = ksmbd_vfs_make_xattr_posix_acl(user_ns, inode, + ACL_TYPE_DEFAULT); + + rc = ndr_encode_posix_acl(&acl_ndr, user_ns, inode, smb_acl, + def_smb_acl); + if (rc) { + pr_err("failed to encode ndr to posix acl\n"); + goto out_free; + } + + rc = ksmbd_gen_sd_hash(conn, acl_ndr.data, acl_ndr.offset, cmp_hash); + if (rc) { + pr_err("failed to generate hash for ndr acl\n"); + goto out_free; + } + + if (memcmp(cmp_hash, acl.posix_acl_hash, XATTR_SD_HASH_SIZE)) { + pr_err("hash value diff\n"); + rc = -EINVAL; + goto out_free; + } + + *pntsd = acl.sd_buf; + if (acl.sd_size < sizeof(struct smb_ntsd)) { + pr_err("sd size is invalid\n"); + goto out_free; + } + + (*pntsd)->osidoffset = cpu_to_le32(le32_to_cpu((*pntsd)->osidoffset) - + NDR_NTSD_OFFSETOF); + (*pntsd)->gsidoffset = cpu_to_le32(le32_to_cpu((*pntsd)->gsidoffset) - + NDR_NTSD_OFFSETOF); + (*pntsd)->dacloffset = cpu_to_le32(le32_to_cpu((*pntsd)->dacloffset) - + NDR_NTSD_OFFSETOF); + + rc = acl.sd_size; +out_free: + kfree(acl_ndr.data); + kfree(smb_acl); + kfree(def_smb_acl); + if (rc < 0) { + kfree(acl.sd_buf); + *pntsd = NULL; + } + +free_n_data: + kfree(n.data); + return rc; +} + +int ksmbd_vfs_set_dos_attrib_xattr(struct user_namespace *user_ns, + struct dentry *dentry, + struct xattr_dos_attrib *da) +{ + struct ndr n; + int err; + + err = ndr_encode_dos_attr(&n, da); + if (err) + return err; + + err = ksmbd_vfs_setxattr(user_ns, dentry, XATTR_NAME_DOS_ATTRIBUTE, + (void *)n.data, n.offset, 0); + if (err) + ksmbd_debug(SMB, "failed to store dos attribute in xattr\n"); + kfree(n.data); + + return err; +} + +int ksmbd_vfs_get_dos_attrib_xattr(struct user_namespace *user_ns, + struct dentry *dentry, + struct xattr_dos_attrib *da) +{ + struct ndr n; + int err; + + err = ksmbd_vfs_getxattr(user_ns, dentry, XATTR_NAME_DOS_ATTRIBUTE, + (char **)&n.data); + if (err > 0) { + n.length = err; + if (ndr_decode_dos_attr(&n, da)) + err = -EINVAL; + kfree(n.data); + } else { + ksmbd_debug(SMB, "failed to load dos attribute in xattr\n"); + } + + return err; +} + +/** + * ksmbd_vfs_init_kstat() - convert unix stat information to smb stat format + * @p: destination buffer + * @ksmbd_kstat: ksmbd kstat wrapper + */ +void *ksmbd_vfs_init_kstat(char **p, struct ksmbd_kstat *ksmbd_kstat) +{ + struct file_directory_info *info = (struct file_directory_info *)(*p); + struct kstat *kstat = ksmbd_kstat->kstat; + u64 time; + + info->FileIndex = 0; + info->CreationTime = cpu_to_le64(ksmbd_kstat->create_time); + time = ksmbd_UnixTimeToNT(kstat->atime); + info->LastAccessTime = cpu_to_le64(time); + time = ksmbd_UnixTimeToNT(kstat->mtime); + info->LastWriteTime = cpu_to_le64(time); + time = ksmbd_UnixTimeToNT(kstat->ctime); + info->ChangeTime = cpu_to_le64(time); + + if (ksmbd_kstat->file_attributes & FILE_ATTRIBUTE_DIRECTORY_LE) { + info->EndOfFile = 0; + info->AllocationSize = 0; + } else { + info->EndOfFile = cpu_to_le64(kstat->size); + info->AllocationSize = cpu_to_le64(kstat->blocks << 9); + } + info->ExtFileAttributes = ksmbd_kstat->file_attributes; + + return info; +} + +int ksmbd_vfs_fill_dentry_attrs(struct ksmbd_work *work, + struct user_namespace *user_ns, + struct dentry *dentry, + struct ksmbd_kstat *ksmbd_kstat) +{ + u64 time; + int rc; + + generic_fillattr(user_ns, d_inode(dentry), ksmbd_kstat->kstat); + + time = ksmbd_UnixTimeToNT(ksmbd_kstat->kstat->ctime); + ksmbd_kstat->create_time = time; + + /* + * set default value for the case that store dos attributes is not yes + * or that acl is disable in server's filesystem and the config is yes. + */ + if (S_ISDIR(ksmbd_kstat->kstat->mode)) + ksmbd_kstat->file_attributes = FILE_ATTRIBUTE_DIRECTORY_LE; + else + ksmbd_kstat->file_attributes = FILE_ATTRIBUTE_ARCHIVE_LE; + + if (test_share_config_flag(work->tcon->share_conf, + KSMBD_SHARE_FLAG_STORE_DOS_ATTRS)) { + struct xattr_dos_attrib da; + + rc = ksmbd_vfs_get_dos_attrib_xattr(user_ns, dentry, &da); + if (rc > 0) { + ksmbd_kstat->file_attributes = cpu_to_le32(da.attr); + ksmbd_kstat->create_time = da.create_time; + } else { + ksmbd_debug(VFS, "fail to load dos attribute.\n"); + } + } + + return 0; +} + +ssize_t ksmbd_vfs_casexattr_len(struct user_namespace *user_ns, + struct dentry *dentry, char *attr_name, + int attr_name_len) +{ + char *name, *xattr_list = NULL; + ssize_t value_len = -ENOENT, xattr_list_len; + + xattr_list_len = ksmbd_vfs_listxattr(dentry, &xattr_list); + if (xattr_list_len <= 0) + goto out; + + for (name = xattr_list; name - xattr_list < xattr_list_len; + name += strlen(name) + 1) { + ksmbd_debug(VFS, "%s, len %zd\n", name, strlen(name)); + if (strncasecmp(attr_name, name, attr_name_len)) + continue; + + value_len = ksmbd_vfs_xattr_len(user_ns, dentry, name); + break; + } + +out: + kvfree(xattr_list); + return value_len; +} + +int ksmbd_vfs_xattr_stream_name(char *stream_name, char **xattr_stream_name, + size_t *xattr_stream_name_size, int s_type) +{ + char *type, *buf; + + if (s_type == DIR_STREAM) + type = ":$INDEX_ALLOCATION"; + else + type = ":$DATA"; + + buf = kasprintf(GFP_KERNEL, "%s%s%s", + XATTR_NAME_STREAM, stream_name, type); + if (!buf) + return -ENOMEM; + + *xattr_stream_name = buf; + *xattr_stream_name_size = strlen(buf) + 1; + + return 0; +} + +int ksmbd_vfs_copy_file_ranges(struct ksmbd_work *work, + struct ksmbd_file *src_fp, + struct ksmbd_file *dst_fp, + struct srv_copychunk *chunks, + unsigned int chunk_count, + unsigned int *chunk_count_written, + unsigned int *chunk_size_written, + loff_t *total_size_written) +{ + unsigned int i; + loff_t src_off, dst_off, src_file_size; + size_t len; + int ret; + + *chunk_count_written = 0; + *chunk_size_written = 0; + *total_size_written = 0; + + if (!(src_fp->daccess & (FILE_READ_DATA_LE | FILE_EXECUTE_LE))) { + pr_err("no right to read(%pD)\n", src_fp->filp); + return -EACCES; + } + if (!(dst_fp->daccess & (FILE_WRITE_DATA_LE | FILE_APPEND_DATA_LE))) { + pr_err("no right to write(%pD)\n", dst_fp->filp); + return -EACCES; + } + + if (ksmbd_stream_fd(src_fp) || ksmbd_stream_fd(dst_fp)) + return -EBADF; + + smb_break_all_levII_oplock(work, dst_fp, 1); + + if (!work->tcon->posix_extensions) { + for (i = 0; i < chunk_count; i++) { + src_off = le64_to_cpu(chunks[i].SourceOffset); + dst_off = le64_to_cpu(chunks[i].TargetOffset); + len = le32_to_cpu(chunks[i].Length); + + if (check_lock_range(src_fp->filp, src_off, + src_off + len - 1, READ)) + return -EAGAIN; + if (check_lock_range(dst_fp->filp, dst_off, + dst_off + len - 1, WRITE)) + return -EAGAIN; + } + } + + src_file_size = i_size_read(file_inode(src_fp->filp)); + + for (i = 0; i < chunk_count; i++) { + src_off = le64_to_cpu(chunks[i].SourceOffset); + dst_off = le64_to_cpu(chunks[i].TargetOffset); + len = le32_to_cpu(chunks[i].Length); + + if (src_off + len > src_file_size) + return -E2BIG; + + ret = vfs_copy_file_range(src_fp->filp, src_off, + dst_fp->filp, dst_off, len, 0); + if (ret == -EOPNOTSUPP || ret == -EXDEV) + ret = vfs_copy_file_range(src_fp->filp, src_off, + dst_fp->filp, dst_off, len, + COPY_FILE_SPLICE); + if (ret < 0) + return ret; + + *chunk_count_written += 1; + *total_size_written += ret; + } + return 0; +} + +void ksmbd_vfs_posix_lock_wait(struct file_lock *flock) +{ + wait_event(flock->fl_wait, !flock->fl_blocker); +} + +int ksmbd_vfs_posix_lock_wait_timeout(struct file_lock *flock, long timeout) +{ + return wait_event_interruptible_timeout(flock->fl_wait, + !flock->fl_blocker, + timeout); +} + +void ksmbd_vfs_posix_lock_unblock(struct file_lock *flock) +{ + locks_delete_block(flock); +} + +int ksmbd_vfs_set_init_posix_acl(struct user_namespace *user_ns, + struct inode *inode) +{ + struct posix_acl_state acl_state; + struct posix_acl *acls; + int rc; + + if (!IS_ENABLED(CONFIG_FS_POSIX_ACL)) + return -EOPNOTSUPP; + + ksmbd_debug(SMB, "Set posix acls\n"); + rc = init_acl_state(&acl_state, 1); + if (rc) + return rc; + + /* Set default owner group */ + acl_state.owner.allow = (inode->i_mode & 0700) >> 6; + acl_state.group.allow = (inode->i_mode & 0070) >> 3; + acl_state.other.allow = inode->i_mode & 0007; + acl_state.users->aces[acl_state.users->n].uid = inode->i_uid; + acl_state.users->aces[acl_state.users->n++].perms.allow = + acl_state.owner.allow; + acl_state.groups->aces[acl_state.groups->n].gid = inode->i_gid; + acl_state.groups->aces[acl_state.groups->n++].perms.allow = + acl_state.group.allow; + acl_state.mask.allow = 0x07; + + acls = posix_acl_alloc(6, GFP_KERNEL); + if (!acls) { + free_acl_state(&acl_state); + return -ENOMEM; + } + posix_state_to_acl(&acl_state, acls->a_entries); + rc = set_posix_acl(user_ns, inode, ACL_TYPE_ACCESS, acls); + if (rc < 0) + ksmbd_debug(SMB, "Set posix acl(ACL_TYPE_ACCESS) failed, rc : %d\n", + rc); + else if (S_ISDIR(inode->i_mode)) { + posix_state_to_acl(&acl_state, acls->a_entries); + rc = set_posix_acl(user_ns, inode, ACL_TYPE_DEFAULT, + acls); + if (rc < 0) + ksmbd_debug(SMB, "Set posix acl(ACL_TYPE_DEFAULT) failed, rc : %d\n", + rc); + } + free_acl_state(&acl_state); + posix_acl_release(acls); + return rc; +} + +int ksmbd_vfs_inherit_posix_acl(struct user_namespace *user_ns, + struct inode *inode, struct inode *parent_inode) +{ + struct posix_acl *acls; + struct posix_acl_entry *pace; + int rc, i; + + if (!IS_ENABLED(CONFIG_FS_POSIX_ACL)) + return -EOPNOTSUPP; + + acls = get_acl(parent_inode, ACL_TYPE_DEFAULT); + if (!acls) + return -ENOENT; + pace = acls->a_entries; + + for (i = 0; i < acls->a_count; i++, pace++) { + if (pace->e_tag == ACL_MASK) { + pace->e_perm = 0x07; + break; + } + } + + rc = set_posix_acl(user_ns, inode, ACL_TYPE_ACCESS, acls); + if (rc < 0) + ksmbd_debug(SMB, "Set posix acl(ACL_TYPE_ACCESS) failed, rc : %d\n", + rc); + if (S_ISDIR(inode->i_mode)) { + rc = set_posix_acl(user_ns, inode, ACL_TYPE_DEFAULT, + acls); + if (rc < 0) + ksmbd_debug(SMB, "Set posix acl(ACL_TYPE_DEFAULT) failed, rc : %d\n", + rc); + } + posix_acl_release(acls); + return rc; +} diff --git a/fs/smb/server/vfs.h b/fs/smb/server/vfs.h new file mode 100644 index 0000000000000..593059ca85112 --- /dev/null +++ b/fs/smb/server/vfs.h @@ -0,0 +1,167 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + * Copyright (C) 2016 Namjae Jeon + * Copyright (C) 2018 Samsung Electronics Co., Ltd. + */ + +#ifndef __KSMBD_VFS_H__ +#define __KSMBD_VFS_H__ + +#include +#include +#include +#include +#include +#include + +#include "smbacl.h" +#include "xattr.h" + +/* + * Enumeration for stream type. + */ +enum { + DATA_STREAM = 1, /* type $DATA */ + DIR_STREAM /* type $INDEX_ALLOCATION */ +}; + +/* CreateOptions */ +#define CREATE_TREE_CONNECTION cpu_to_le32(0x00000080) +#define FILE_RESERVE_OPFILTER_LE cpu_to_le32(0x00100000) + +#define CREATE_OPTION_READONLY 0x10000000 +/* system. NB not sent over wire */ +#define CREATE_OPTION_SPECIAL 0x20000000 + +struct ksmbd_work; +struct ksmbd_file; +struct ksmbd_conn; + +struct ksmbd_dir_info { + const char *name; + char *wptr; + char *rptr; + int name_len; + int out_buf_len; + int num_entry; + int data_count; + int last_entry_offset; + bool hide_dot_file; + int flags; + int last_entry_off_align; +}; + +struct ksmbd_readdir_data { + struct dir_context ctx; + union { + void *private; + char *dirent; + }; + + unsigned int used; + unsigned int dirent_count; + unsigned int file_attr; + struct unicode_map *um; +}; + +/* ksmbd kstat wrapper to get valid create time when reading dir entry */ +struct ksmbd_kstat { + struct kstat *kstat; + unsigned long long create_time; + __le32 file_attributes; +}; + +int ksmbd_vfs_lock_parent(struct user_namespace *user_ns, struct dentry *parent, + struct dentry *child); +int ksmbd_vfs_may_delete(struct user_namespace *user_ns, struct dentry *dentry); +int ksmbd_vfs_query_maximal_access(struct user_namespace *user_ns, + struct dentry *dentry, __le32 *daccess); +int ksmbd_vfs_create(struct ksmbd_work *work, const char *name, umode_t mode); +int ksmbd_vfs_mkdir(struct ksmbd_work *work, const char *name, umode_t mode); +int ksmbd_vfs_read(struct ksmbd_work *work, struct ksmbd_file *fp, + size_t count, loff_t *pos); +int ksmbd_vfs_write(struct ksmbd_work *work, struct ksmbd_file *fp, + char *buf, size_t count, loff_t *pos, bool sync, + ssize_t *written); +int ksmbd_vfs_fsync(struct ksmbd_work *work, u64 fid, u64 p_id); +int ksmbd_vfs_remove_file(struct ksmbd_work *work, char *name); +int ksmbd_vfs_link(struct ksmbd_work *work, + const char *oldname, const char *newname); +int ksmbd_vfs_getattr(const struct path *path, struct kstat *stat); +int ksmbd_vfs_fp_rename(struct ksmbd_work *work, struct ksmbd_file *fp, + char *newname); +int ksmbd_vfs_truncate(struct ksmbd_work *work, + struct ksmbd_file *fp, loff_t size); +struct srv_copychunk; +int ksmbd_vfs_copy_file_ranges(struct ksmbd_work *work, + struct ksmbd_file *src_fp, + struct ksmbd_file *dst_fp, + struct srv_copychunk *chunks, + unsigned int chunk_count, + unsigned int *chunk_count_written, + unsigned int *chunk_size_written, + loff_t *total_size_written); +ssize_t ksmbd_vfs_listxattr(struct dentry *dentry, char **list); +ssize_t ksmbd_vfs_getxattr(struct user_namespace *user_ns, + struct dentry *dentry, + char *xattr_name, + char **xattr_buf); +ssize_t ksmbd_vfs_casexattr_len(struct user_namespace *user_ns, + struct dentry *dentry, char *attr_name, + int attr_name_len); +int ksmbd_vfs_setxattr(struct user_namespace *user_ns, + struct dentry *dentry, const char *attr_name, + void *attr_value, size_t attr_size, int flags); +int ksmbd_vfs_xattr_stream_name(char *stream_name, char **xattr_stream_name, + size_t *xattr_stream_name_size, int s_type); +int ksmbd_vfs_remove_xattr(struct user_namespace *user_ns, + struct dentry *dentry, char *attr_name); +int ksmbd_vfs_kern_path(struct ksmbd_work *work, + char *name, unsigned int flags, struct path *path, + bool caseless); +struct dentry *ksmbd_vfs_kern_path_create(struct ksmbd_work *work, + const char *name, + unsigned int flags, + struct path *path); +int ksmbd_vfs_empty_dir(struct ksmbd_file *fp); +void ksmbd_vfs_set_fadvise(struct file *filp, __le32 option); +int ksmbd_vfs_zero_data(struct ksmbd_work *work, struct ksmbd_file *fp, + loff_t off, loff_t len); +struct file_allocated_range_buffer; +int ksmbd_vfs_fqar_lseek(struct ksmbd_file *fp, loff_t start, loff_t length, + struct file_allocated_range_buffer *ranges, + unsigned int in_count, unsigned int *out_count); +int ksmbd_vfs_unlink(struct user_namespace *user_ns, + struct dentry *dir, struct dentry *dentry); +void *ksmbd_vfs_init_kstat(char **p, struct ksmbd_kstat *ksmbd_kstat); +int ksmbd_vfs_fill_dentry_attrs(struct ksmbd_work *work, + struct user_namespace *user_ns, + struct dentry *dentry, + struct ksmbd_kstat *ksmbd_kstat); +void ksmbd_vfs_posix_lock_wait(struct file_lock *flock); +int ksmbd_vfs_posix_lock_wait_timeout(struct file_lock *flock, long timeout); +void ksmbd_vfs_posix_lock_unblock(struct file_lock *flock); +int ksmbd_vfs_remove_acl_xattrs(struct user_namespace *user_ns, + struct dentry *dentry); +int ksmbd_vfs_remove_sd_xattrs(struct user_namespace *user_ns, + struct dentry *dentry); +int ksmbd_vfs_set_sd_xattr(struct ksmbd_conn *conn, + struct user_namespace *user_ns, + struct dentry *dentry, + struct smb_ntsd *pntsd, int len); +int ksmbd_vfs_get_sd_xattr(struct ksmbd_conn *conn, + struct user_namespace *user_ns, + struct dentry *dentry, + struct smb_ntsd **pntsd); +int ksmbd_vfs_set_dos_attrib_xattr(struct user_namespace *user_ns, + struct dentry *dentry, + struct xattr_dos_attrib *da); +int ksmbd_vfs_get_dos_attrib_xattr(struct user_namespace *user_ns, + struct dentry *dentry, + struct xattr_dos_attrib *da); +int ksmbd_vfs_set_init_posix_acl(struct user_namespace *user_ns, + struct inode *inode); +int ksmbd_vfs_inherit_posix_acl(struct user_namespace *user_ns, + struct inode *inode, + struct inode *parent_inode); +#endif /* __KSMBD_VFS_H__ */ diff --git a/fs/smb/server/vfs_cache.c b/fs/smb/server/vfs_cache.c new file mode 100644 index 0000000000000..0ae5dd0829e92 --- /dev/null +++ b/fs/smb/server/vfs_cache.c @@ -0,0 +1,708 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Copyright (C) 2016 Namjae Jeon + * Copyright (C) 2019 Samsung Electronics Co., Ltd. + */ + +#include +#include +#include + +#include "glob.h" +#include "vfs_cache.h" +#include "oplock.h" +#include "vfs.h" +#include "connection.h" +#include "mgmt/tree_connect.h" +#include "mgmt/user_session.h" +#include "smb_common.h" + +#define S_DEL_PENDING 1 +#define S_DEL_ON_CLS 2 +#define S_DEL_ON_CLS_STREAM 8 + +static unsigned int inode_hash_mask __read_mostly; +static unsigned int inode_hash_shift __read_mostly; +static struct hlist_head *inode_hashtable __read_mostly; +static DEFINE_RWLOCK(inode_hash_lock); + +static struct ksmbd_file_table global_ft; +static atomic_long_t fd_limit; +static struct kmem_cache *filp_cache; + +void ksmbd_set_fd_limit(unsigned long limit) +{ + limit = min(limit, get_max_files()); + atomic_long_set(&fd_limit, limit); +} + +static bool fd_limit_depleted(void) +{ + long v = atomic_long_dec_return(&fd_limit); + + if (v >= 0) + return false; + atomic_long_inc(&fd_limit); + return true; +} + +static void fd_limit_close(void) +{ + atomic_long_inc(&fd_limit); +} + +/* + * INODE hash + */ + +static unsigned long inode_hash(struct super_block *sb, unsigned long hashval) +{ + unsigned long tmp; + + tmp = (hashval * (unsigned long)sb) ^ (GOLDEN_RATIO_PRIME + hashval) / + L1_CACHE_BYTES; + tmp = tmp ^ ((tmp ^ GOLDEN_RATIO_PRIME) >> inode_hash_shift); + return tmp & inode_hash_mask; +} + +static struct ksmbd_inode *__ksmbd_inode_lookup(struct inode *inode) +{ + struct hlist_head *head = inode_hashtable + + inode_hash(inode->i_sb, inode->i_ino); + struct ksmbd_inode *ci = NULL, *ret_ci = NULL; + + hlist_for_each_entry(ci, head, m_hash) { + if (ci->m_inode == inode) { + if (atomic_inc_not_zero(&ci->m_count)) + ret_ci = ci; + break; + } + } + return ret_ci; +} + +static struct ksmbd_inode *ksmbd_inode_lookup(struct ksmbd_file *fp) +{ + return __ksmbd_inode_lookup(file_inode(fp->filp)); +} + +static struct ksmbd_inode *ksmbd_inode_lookup_by_vfsinode(struct inode *inode) +{ + struct ksmbd_inode *ci; + + read_lock(&inode_hash_lock); + ci = __ksmbd_inode_lookup(inode); + read_unlock(&inode_hash_lock); + return ci; +} + +int ksmbd_query_inode_status(struct inode *inode) +{ + struct ksmbd_inode *ci; + int ret = KSMBD_INODE_STATUS_UNKNOWN; + + read_lock(&inode_hash_lock); + ci = __ksmbd_inode_lookup(inode); + if (ci) { + ret = KSMBD_INODE_STATUS_OK; + if (ci->m_flags & S_DEL_PENDING) + ret = KSMBD_INODE_STATUS_PENDING_DELETE; + atomic_dec(&ci->m_count); + } + read_unlock(&inode_hash_lock); + return ret; +} + +bool ksmbd_inode_pending_delete(struct ksmbd_file *fp) +{ + return (fp->f_ci->m_flags & S_DEL_PENDING); +} + +void ksmbd_set_inode_pending_delete(struct ksmbd_file *fp) +{ + fp->f_ci->m_flags |= S_DEL_PENDING; +} + +void ksmbd_clear_inode_pending_delete(struct ksmbd_file *fp) +{ + fp->f_ci->m_flags &= ~S_DEL_PENDING; +} + +void ksmbd_fd_set_delete_on_close(struct ksmbd_file *fp, + int file_info) +{ + if (ksmbd_stream_fd(fp)) { + fp->f_ci->m_flags |= S_DEL_ON_CLS_STREAM; + return; + } + + fp->f_ci->m_flags |= S_DEL_ON_CLS; +} + +static void ksmbd_inode_hash(struct ksmbd_inode *ci) +{ + struct hlist_head *b = inode_hashtable + + inode_hash(ci->m_inode->i_sb, ci->m_inode->i_ino); + + hlist_add_head(&ci->m_hash, b); +} + +static void ksmbd_inode_unhash(struct ksmbd_inode *ci) +{ + write_lock(&inode_hash_lock); + hlist_del_init(&ci->m_hash); + write_unlock(&inode_hash_lock); +} + +static int ksmbd_inode_init(struct ksmbd_inode *ci, struct ksmbd_file *fp) +{ + ci->m_inode = file_inode(fp->filp); + atomic_set(&ci->m_count, 1); + atomic_set(&ci->op_count, 0); + atomic_set(&ci->sop_count, 0); + ci->m_flags = 0; + ci->m_fattr = 0; + INIT_LIST_HEAD(&ci->m_fp_list); + INIT_LIST_HEAD(&ci->m_op_list); + rwlock_init(&ci->m_lock); + return 0; +} + +static struct ksmbd_inode *ksmbd_inode_get(struct ksmbd_file *fp) +{ + struct ksmbd_inode *ci, *tmpci; + int rc; + + read_lock(&inode_hash_lock); + ci = ksmbd_inode_lookup(fp); + read_unlock(&inode_hash_lock); + if (ci) + return ci; + + ci = kmalloc(sizeof(struct ksmbd_inode), GFP_KERNEL); + if (!ci) + return NULL; + + rc = ksmbd_inode_init(ci, fp); + if (rc) { + pr_err("inode initialized failed\n"); + kfree(ci); + return NULL; + } + + write_lock(&inode_hash_lock); + tmpci = ksmbd_inode_lookup(fp); + if (!tmpci) { + ksmbd_inode_hash(ci); + } else { + kfree(ci); + ci = tmpci; + } + write_unlock(&inode_hash_lock); + return ci; +} + +static void ksmbd_inode_free(struct ksmbd_inode *ci) +{ + ksmbd_inode_unhash(ci); + kfree(ci); +} + +static void ksmbd_inode_put(struct ksmbd_inode *ci) +{ + if (atomic_dec_and_test(&ci->m_count)) + ksmbd_inode_free(ci); +} + +int __init ksmbd_inode_hash_init(void) +{ + unsigned int loop; + unsigned long numentries = 16384; + unsigned long bucketsize = sizeof(struct hlist_head); + unsigned long size; + + inode_hash_shift = ilog2(numentries); + inode_hash_mask = (1 << inode_hash_shift) - 1; + + size = bucketsize << inode_hash_shift; + + /* init master fp hash table */ + inode_hashtable = vmalloc(size); + if (!inode_hashtable) + return -ENOMEM; + + for (loop = 0; loop < (1U << inode_hash_shift); loop++) + INIT_HLIST_HEAD(&inode_hashtable[loop]); + return 0; +} + +void ksmbd_release_inode_hash(void) +{ + vfree(inode_hashtable); +} + +static void __ksmbd_inode_close(struct ksmbd_file *fp) +{ + struct dentry *dir, *dentry; + struct ksmbd_inode *ci = fp->f_ci; + int err; + struct file *filp; + + filp = fp->filp; + if (ksmbd_stream_fd(fp) && (ci->m_flags & S_DEL_ON_CLS_STREAM)) { + ci->m_flags &= ~S_DEL_ON_CLS_STREAM; + err = ksmbd_vfs_remove_xattr(file_mnt_user_ns(filp), + filp->f_path.dentry, + fp->stream.name); + if (err) + pr_err("remove xattr failed : %s\n", + fp->stream.name); + } + + if (atomic_dec_and_test(&ci->m_count)) { + write_lock(&ci->m_lock); + if (ci->m_flags & (S_DEL_ON_CLS | S_DEL_PENDING)) { + dentry = filp->f_path.dentry; + dir = dentry->d_parent; + ci->m_flags &= ~(S_DEL_ON_CLS | S_DEL_PENDING); + write_unlock(&ci->m_lock); + ksmbd_vfs_unlink(file_mnt_user_ns(filp), dir, dentry); + write_lock(&ci->m_lock); + } + write_unlock(&ci->m_lock); + + ksmbd_inode_free(ci); + } +} + +static void __ksmbd_remove_durable_fd(struct ksmbd_file *fp) +{ + if (!has_file_id(fp->persistent_id)) + return; + + write_lock(&global_ft.lock); + idr_remove(global_ft.idr, fp->persistent_id); + write_unlock(&global_ft.lock); +} + +static void __ksmbd_remove_fd(struct ksmbd_file_table *ft, struct ksmbd_file *fp) +{ + if (!has_file_id(fp->volatile_id)) + return; + + write_lock(&fp->f_ci->m_lock); + list_del_init(&fp->node); + write_unlock(&fp->f_ci->m_lock); + + write_lock(&ft->lock); + idr_remove(ft->idr, fp->volatile_id); + write_unlock(&ft->lock); +} + +static void __ksmbd_close_fd(struct ksmbd_file_table *ft, struct ksmbd_file *fp) +{ + struct file *filp; + struct ksmbd_lock *smb_lock, *tmp_lock; + + fd_limit_close(); + __ksmbd_remove_durable_fd(fp); + __ksmbd_remove_fd(ft, fp); + + close_id_del_oplock(fp); + filp = fp->filp; + + __ksmbd_inode_close(fp); + if (!IS_ERR_OR_NULL(filp)) + fput(filp); + + /* because the reference count of fp is 0, it is guaranteed that + * there are not accesses to fp->lock_list. + */ + list_for_each_entry_safe(smb_lock, tmp_lock, &fp->lock_list, flist) { + spin_lock(&fp->conn->llist_lock); + list_del(&smb_lock->clist); + spin_unlock(&fp->conn->llist_lock); + + list_del(&smb_lock->flist); + locks_free_lock(smb_lock->fl); + kfree(smb_lock); + } + + if (ksmbd_stream_fd(fp)) + kfree(fp->stream.name); + kmem_cache_free(filp_cache, fp); +} + +static struct ksmbd_file *ksmbd_fp_get(struct ksmbd_file *fp) +{ + if (!atomic_inc_not_zero(&fp->refcount)) + return NULL; + return fp; +} + +static struct ksmbd_file *__ksmbd_lookup_fd(struct ksmbd_file_table *ft, + u64 id) +{ + struct ksmbd_file *fp; + + if (!has_file_id(id)) + return NULL; + + read_lock(&ft->lock); + fp = idr_find(ft->idr, id); + if (fp) + fp = ksmbd_fp_get(fp); + read_unlock(&ft->lock); + return fp; +} + +static void __put_fd_final(struct ksmbd_work *work, struct ksmbd_file *fp) +{ + __ksmbd_close_fd(&work->sess->file_table, fp); + atomic_dec(&work->conn->stats.open_files_count); +} + +static void set_close_state_blocked_works(struct ksmbd_file *fp) +{ + struct ksmbd_work *cancel_work; + + spin_lock(&fp->f_lock); + list_for_each_entry(cancel_work, &fp->blocked_works, + fp_entry) { + cancel_work->state = KSMBD_WORK_CLOSED; + cancel_work->cancel_fn(cancel_work->cancel_argv); + } + spin_unlock(&fp->f_lock); +} + +int ksmbd_close_fd(struct ksmbd_work *work, u64 id) +{ + struct ksmbd_file *fp; + struct ksmbd_file_table *ft; + + if (!has_file_id(id)) + return 0; + + ft = &work->sess->file_table; + read_lock(&ft->lock); + fp = idr_find(ft->idr, id); + if (fp) { + set_close_state_blocked_works(fp); + + if (!atomic_dec_and_test(&fp->refcount)) + fp = NULL; + } + read_unlock(&ft->lock); + + if (!fp) + return -EINVAL; + + __put_fd_final(work, fp); + return 0; +} + +void ksmbd_fd_put(struct ksmbd_work *work, struct ksmbd_file *fp) +{ + if (!fp) + return; + + if (!atomic_dec_and_test(&fp->refcount)) + return; + __put_fd_final(work, fp); +} + +static bool __sanity_check(struct ksmbd_tree_connect *tcon, struct ksmbd_file *fp) +{ + if (!fp) + return false; + if (fp->tcon != tcon) + return false; + return true; +} + +struct ksmbd_file *ksmbd_lookup_foreign_fd(struct ksmbd_work *work, u64 id) +{ + return __ksmbd_lookup_fd(&work->sess->file_table, id); +} + +struct ksmbd_file *ksmbd_lookup_fd_fast(struct ksmbd_work *work, u64 id) +{ + struct ksmbd_file *fp = __ksmbd_lookup_fd(&work->sess->file_table, id); + + if (__sanity_check(work->tcon, fp)) + return fp; + + ksmbd_fd_put(work, fp); + return NULL; +} + +struct ksmbd_file *ksmbd_lookup_fd_slow(struct ksmbd_work *work, u64 id, + u64 pid) +{ + struct ksmbd_file *fp; + + if (!has_file_id(id)) { + id = work->compound_fid; + pid = work->compound_pfid; + } + + fp = __ksmbd_lookup_fd(&work->sess->file_table, id); + if (!__sanity_check(work->tcon, fp)) { + ksmbd_fd_put(work, fp); + return NULL; + } + if (fp->persistent_id != pid) { + ksmbd_fd_put(work, fp); + return NULL; + } + return fp; +} + +struct ksmbd_file *ksmbd_lookup_durable_fd(unsigned long long id) +{ + return __ksmbd_lookup_fd(&global_ft, id); +} + +struct ksmbd_file *ksmbd_lookup_fd_cguid(char *cguid) +{ + struct ksmbd_file *fp = NULL; + unsigned int id; + + read_lock(&global_ft.lock); + idr_for_each_entry(global_ft.idr, fp, id) { + if (!memcmp(fp->create_guid, + cguid, + SMB2_CREATE_GUID_SIZE)) { + fp = ksmbd_fp_get(fp); + break; + } + } + read_unlock(&global_ft.lock); + + return fp; +} + +struct ksmbd_file *ksmbd_lookup_fd_inode(struct inode *inode) +{ + struct ksmbd_file *lfp; + struct ksmbd_inode *ci; + + ci = ksmbd_inode_lookup_by_vfsinode(inode); + if (!ci) + return NULL; + + read_lock(&ci->m_lock); + list_for_each_entry(lfp, &ci->m_fp_list, node) { + if (inode == file_inode(lfp->filp)) { + atomic_dec(&ci->m_count); + lfp = ksmbd_fp_get(lfp); + read_unlock(&ci->m_lock); + return lfp; + } + } + atomic_dec(&ci->m_count); + read_unlock(&ci->m_lock); + return NULL; +} + +#define OPEN_ID_TYPE_VOLATILE_ID (0) +#define OPEN_ID_TYPE_PERSISTENT_ID (1) + +static void __open_id_set(struct ksmbd_file *fp, u64 id, int type) +{ + if (type == OPEN_ID_TYPE_VOLATILE_ID) + fp->volatile_id = id; + if (type == OPEN_ID_TYPE_PERSISTENT_ID) + fp->persistent_id = id; +} + +static int __open_id(struct ksmbd_file_table *ft, struct ksmbd_file *fp, + int type) +{ + u64 id = 0; + int ret; + + if (type == OPEN_ID_TYPE_VOLATILE_ID && fd_limit_depleted()) { + __open_id_set(fp, KSMBD_NO_FID, type); + return -EMFILE; + } + + idr_preload(GFP_KERNEL); + write_lock(&ft->lock); + ret = idr_alloc_cyclic(ft->idr, fp, 0, INT_MAX - 1, GFP_NOWAIT); + if (ret >= 0) { + id = ret; + ret = 0; + } else { + id = KSMBD_NO_FID; + fd_limit_close(); + } + + __open_id_set(fp, id, type); + write_unlock(&ft->lock); + idr_preload_end(); + return ret; +} + +unsigned int ksmbd_open_durable_fd(struct ksmbd_file *fp) +{ + __open_id(&global_ft, fp, OPEN_ID_TYPE_PERSISTENT_ID); + return fp->persistent_id; +} + +struct ksmbd_file *ksmbd_open_fd(struct ksmbd_work *work, struct file *filp) +{ + struct ksmbd_file *fp; + int ret; + + fp = kmem_cache_zalloc(filp_cache, GFP_KERNEL); + if (!fp) { + pr_err("Failed to allocate memory\n"); + return ERR_PTR(-ENOMEM); + } + + INIT_LIST_HEAD(&fp->blocked_works); + INIT_LIST_HEAD(&fp->node); + INIT_LIST_HEAD(&fp->lock_list); + spin_lock_init(&fp->f_lock); + atomic_set(&fp->refcount, 1); + + fp->filp = filp; + fp->conn = work->conn; + fp->tcon = work->tcon; + fp->volatile_id = KSMBD_NO_FID; + fp->persistent_id = KSMBD_NO_FID; + fp->f_ci = ksmbd_inode_get(fp); + + if (!fp->f_ci) { + ret = -ENOMEM; + goto err_out; + } + + ret = __open_id(&work->sess->file_table, fp, OPEN_ID_TYPE_VOLATILE_ID); + if (ret) { + ksmbd_inode_put(fp->f_ci); + goto err_out; + } + + atomic_inc(&work->conn->stats.open_files_count); + return fp; + +err_out: + kmem_cache_free(filp_cache, fp); + return ERR_PTR(ret); +} + +static int +__close_file_table_ids(struct ksmbd_file_table *ft, + struct ksmbd_tree_connect *tcon, + bool (*skip)(struct ksmbd_tree_connect *tcon, + struct ksmbd_file *fp)) +{ + unsigned int id; + struct ksmbd_file *fp; + int num = 0; + + idr_for_each_entry(ft->idr, fp, id) { + if (skip(tcon, fp)) + continue; + + set_close_state_blocked_works(fp); + + if (!atomic_dec_and_test(&fp->refcount)) + continue; + __ksmbd_close_fd(ft, fp); + num++; + } + return num; +} + +static bool tree_conn_fd_check(struct ksmbd_tree_connect *tcon, + struct ksmbd_file *fp) +{ + return fp->tcon != tcon; +} + +static bool session_fd_check(struct ksmbd_tree_connect *tcon, + struct ksmbd_file *fp) +{ + return false; +} + +void ksmbd_close_tree_conn_fds(struct ksmbd_work *work) +{ + int num = __close_file_table_ids(&work->sess->file_table, + work->tcon, + tree_conn_fd_check); + + atomic_sub(num, &work->conn->stats.open_files_count); +} + +void ksmbd_close_session_fds(struct ksmbd_work *work) +{ + int num = __close_file_table_ids(&work->sess->file_table, + work->tcon, + session_fd_check); + + atomic_sub(num, &work->conn->stats.open_files_count); +} + +int ksmbd_init_global_file_table(void) +{ + return ksmbd_init_file_table(&global_ft); +} + +void ksmbd_free_global_file_table(void) +{ + struct ksmbd_file *fp = NULL; + unsigned int id; + + idr_for_each_entry(global_ft.idr, fp, id) { + __ksmbd_remove_durable_fd(fp); + kmem_cache_free(filp_cache, fp); + } + + ksmbd_destroy_file_table(&global_ft); +} + +int ksmbd_init_file_table(struct ksmbd_file_table *ft) +{ + ft->idr = kzalloc(sizeof(struct idr), GFP_KERNEL); + if (!ft->idr) + return -ENOMEM; + + idr_init(ft->idr); + rwlock_init(&ft->lock); + return 0; +} + +void ksmbd_destroy_file_table(struct ksmbd_file_table *ft) +{ + if (!ft->idr) + return; + + __close_file_table_ids(ft, NULL, session_fd_check); + idr_destroy(ft->idr); + kfree(ft->idr); + ft->idr = NULL; +} + +int ksmbd_init_file_cache(void) +{ + filp_cache = kmem_cache_create("ksmbd_file_cache", + sizeof(struct ksmbd_file), 0, + SLAB_HWCACHE_ALIGN, NULL); + if (!filp_cache) + goto out; + + return 0; + +out: + pr_err("failed to allocate file cache\n"); + return -ENOMEM; +} + +void ksmbd_exit_file_cache(void) +{ + kmem_cache_destroy(filp_cache); +} diff --git a/fs/smb/server/vfs_cache.h b/fs/smb/server/vfs_cache.h new file mode 100644 index 0000000000000..fcb13413fa8d9 --- /dev/null +++ b/fs/smb/server/vfs_cache.h @@ -0,0 +1,166 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + * Copyright (C) 2019 Samsung Electronics Co., Ltd. + */ + +#ifndef __VFS_CACHE_H__ +#define __VFS_CACHE_H__ + +#include +#include +#include +#include +#include +#include + +#include "vfs.h" + +/* Windows style file permissions for extended response */ +#define FILE_GENERIC_ALL 0x1F01FF +#define FILE_GENERIC_READ 0x120089 +#define FILE_GENERIC_WRITE 0x120116 +#define FILE_GENERIC_EXECUTE 0X1200a0 + +#define KSMBD_START_FID 0 +#define KSMBD_NO_FID (INT_MAX) +#define SMB2_NO_FID (0xFFFFFFFFFFFFFFFFULL) + +struct ksmbd_conn; +struct ksmbd_session; + +struct ksmbd_lock { + struct file_lock *fl; + struct list_head clist; + struct list_head flist; + struct list_head llist; + unsigned int flags; + int cmd; + int zero_len; + unsigned long long start; + unsigned long long end; +}; + +struct stream { + char *name; + ssize_t size; +}; + +struct ksmbd_inode { + rwlock_t m_lock; + atomic_t m_count; + atomic_t op_count; + /* opinfo count for streams */ + atomic_t sop_count; + struct inode *m_inode; + unsigned int m_flags; + struct hlist_node m_hash; + struct list_head m_fp_list; + struct list_head m_op_list; + struct oplock_info *m_opinfo; + __le32 m_fattr; +}; + +struct ksmbd_file { + struct file *filp; + u64 persistent_id; + u64 volatile_id; + + spinlock_t f_lock; + + struct ksmbd_inode *f_ci; + struct ksmbd_inode *f_parent_ci; + struct oplock_info __rcu *f_opinfo; + struct ksmbd_conn *conn; + struct ksmbd_tree_connect *tcon; + + atomic_t refcount; + __le32 daccess; + __le32 saccess; + __le32 coption; + __le32 cdoption; + __u64 create_time; + __u64 itime; + + bool is_nt_open; + bool attrib_only; + + char client_guid[16]; + char create_guid[16]; + char app_instance_id[16]; + + struct stream stream; + struct list_head node; + struct list_head blocked_works; + struct list_head lock_list; + + int durable_timeout; + + /* if ls is happening on directory, below is valid*/ + struct ksmbd_readdir_data readdir_data; + int dot_dotdot[2]; +}; + +static inline void set_ctx_actor(struct dir_context *ctx, + filldir_t actor) +{ + ctx->actor = actor; +} + +#define KSMBD_NR_OPEN_DEFAULT BITS_PER_LONG + +struct ksmbd_file_table { + rwlock_t lock; + struct idr *idr; +}; + +static inline bool has_file_id(u64 id) +{ + return id < KSMBD_NO_FID; +} + +static inline bool ksmbd_stream_fd(struct ksmbd_file *fp) +{ + return fp->stream.name != NULL; +} + +int ksmbd_init_file_table(struct ksmbd_file_table *ft); +void ksmbd_destroy_file_table(struct ksmbd_file_table *ft); +int ksmbd_close_fd(struct ksmbd_work *work, u64 id); +struct ksmbd_file *ksmbd_lookup_fd_fast(struct ksmbd_work *work, u64 id); +struct ksmbd_file *ksmbd_lookup_foreign_fd(struct ksmbd_work *work, u64 id); +struct ksmbd_file *ksmbd_lookup_fd_slow(struct ksmbd_work *work, u64 id, + u64 pid); +void ksmbd_fd_put(struct ksmbd_work *work, struct ksmbd_file *fp); +struct ksmbd_file *ksmbd_lookup_durable_fd(unsigned long long id); +struct ksmbd_file *ksmbd_lookup_fd_cguid(char *cguid); +struct ksmbd_file *ksmbd_lookup_fd_inode(struct inode *inode); +unsigned int ksmbd_open_durable_fd(struct ksmbd_file *fp); +struct ksmbd_file *ksmbd_open_fd(struct ksmbd_work *work, struct file *filp); +void ksmbd_close_tree_conn_fds(struct ksmbd_work *work); +void ksmbd_close_session_fds(struct ksmbd_work *work); +int ksmbd_close_inode_fds(struct ksmbd_work *work, struct inode *inode); +int ksmbd_init_global_file_table(void); +void ksmbd_free_global_file_table(void); +void ksmbd_set_fd_limit(unsigned long limit); + +/* + * INODE hash + */ +int __init ksmbd_inode_hash_init(void); +void ksmbd_release_inode_hash(void); + +enum KSMBD_INODE_STATUS { + KSMBD_INODE_STATUS_OK, + KSMBD_INODE_STATUS_UNKNOWN, + KSMBD_INODE_STATUS_PENDING_DELETE, +}; + +int ksmbd_query_inode_status(struct inode *inode); +bool ksmbd_inode_pending_delete(struct ksmbd_file *fp); +void ksmbd_set_inode_pending_delete(struct ksmbd_file *fp); +void ksmbd_clear_inode_pending_delete(struct ksmbd_file *fp); +void ksmbd_fd_set_delete_on_close(struct ksmbd_file *fp, + int file_info); +int ksmbd_init_file_cache(void); +void ksmbd_exit_file_cache(void); +#endif /* __VFS_CACHE_H__ */ diff --git a/fs/smb/server/xattr.h b/fs/smb/server/xattr.h new file mode 100644 index 0000000000000..16499ca5c82d3 --- /dev/null +++ b/fs/smb/server/xattr.h @@ -0,0 +1,122 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + * Copyright (C) 2021 Samsung Electronics Co., Ltd. + */ + +#ifndef __XATTR_H__ +#define __XATTR_H__ + +/* + * These are on-disk structures to store additional metadata into xattr to + * reproduce windows filesystem semantics. And they are encoded with NDR to + * compatible with samba's xattr meta format. The compatibility with samba + * is important because it can lose the information(file attribute, + * creation time, acls) about the existing files when switching between + * ksmbd and samba. + */ + +/* + * Dos attribute flags used for what variable is valid. + */ +enum { + XATTR_DOSINFO_ATTRIB = 0x00000001, + XATTR_DOSINFO_EA_SIZE = 0x00000002, + XATTR_DOSINFO_SIZE = 0x00000004, + XATTR_DOSINFO_ALLOC_SIZE = 0x00000008, + XATTR_DOSINFO_CREATE_TIME = 0x00000010, + XATTR_DOSINFO_CHANGE_TIME = 0x00000020, + XATTR_DOSINFO_ITIME = 0x00000040 +}; + +/* + * Dos attribute structure which is compatible with samba's one. + * Storing it into the xattr named "DOSATTRIB" separately from inode + * allows ksmbd to faithfully reproduce windows filesystem semantics + * on top of a POSIX filesystem. + */ +struct xattr_dos_attrib { + __u16 version; /* version 3 or version 4 */ + __u32 flags; /* valid flags */ + __u32 attr; /* Dos attribute */ + __u32 ea_size; /* EA size */ + __u64 size; + __u64 alloc_size; + __u64 create_time; /* File creation time */ + __u64 change_time; /* File change time */ + __u64 itime; /* Invented/Initial time */ +}; + +/* + * Enumeration is used for computing posix acl hash. + */ +enum { + SMB_ACL_TAG_INVALID = 0, + SMB_ACL_USER, + SMB_ACL_USER_OBJ, + SMB_ACL_GROUP, + SMB_ACL_GROUP_OBJ, + SMB_ACL_OTHER, + SMB_ACL_MASK +}; + +#define SMB_ACL_READ 4 +#define SMB_ACL_WRITE 2 +#define SMB_ACL_EXECUTE 1 + +struct xattr_acl_entry { + int type; + uid_t uid; + gid_t gid; + mode_t perm; +}; + +/* + * xattr_smb_acl structure is used for computing posix acl hash. + */ +struct xattr_smb_acl { + int count; + int next; + struct xattr_acl_entry entries[]; +}; + +/* 64bytes hash in xattr_ntacl is computed with sha256 */ +#define XATTR_SD_HASH_TYPE_SHA256 0x1 +#define XATTR_SD_HASH_SIZE 64 + +/* + * xattr_ntacl is used for storing ntacl and hashes. + * Hash is used for checking valid posix acl and ntacl in xattr. + */ +struct xattr_ntacl { + __u16 version; /* version 4*/ + void *sd_buf; + __u32 sd_size; + __u16 hash_type; /* hash type */ + __u8 desc[10]; /* posix_acl description */ + __u16 desc_len; + __u64 current_time; + __u8 hash[XATTR_SD_HASH_SIZE]; /* 64bytes hash for ntacl */ + __u8 posix_acl_hash[XATTR_SD_HASH_SIZE]; /* 64bytes hash for posix acl */ +}; + +/* DOS ATTRIBUITE XATTR PREFIX */ +#define DOS_ATTRIBUTE_PREFIX "DOSATTRIB" +#define DOS_ATTRIBUTE_PREFIX_LEN (sizeof(DOS_ATTRIBUTE_PREFIX) - 1) +#define XATTR_NAME_DOS_ATTRIBUTE (XATTR_USER_PREFIX DOS_ATTRIBUTE_PREFIX) +#define XATTR_NAME_DOS_ATTRIBUTE_LEN \ + (sizeof(XATTR_USER_PREFIX DOS_ATTRIBUTE_PREFIX) - 1) + +/* STREAM XATTR PREFIX */ +#define STREAM_PREFIX "DosStream." +#define STREAM_PREFIX_LEN (sizeof(STREAM_PREFIX) - 1) +#define XATTR_NAME_STREAM (XATTR_USER_PREFIX STREAM_PREFIX) +#define XATTR_NAME_STREAM_LEN (sizeof(XATTR_NAME_STREAM) - 1) + +/* SECURITY DESCRIPTOR(NTACL) XATTR PREFIX */ +#define SD_PREFIX "NTACL" +#define SD_PREFIX_LEN (sizeof(SD_PREFIX) - 1) +#define XATTR_NAME_SD (XATTR_SECURITY_PREFIX SD_PREFIX) +#define XATTR_NAME_SD_LEN \ + (sizeof(XATTR_SECURITY_PREFIX SD_PREFIX) - 1) + +#endif /* __XATTR_H__ */ diff --git a/fs/smbfs_common/Makefile b/fs/smbfs_common/Makefile deleted file mode 100644 index cafc61a3bfc37..0000000000000 --- a/fs/smbfs_common/Makefile +++ /dev/null @@ -1,7 +0,0 @@ -# SPDX-License-Identifier: GPL-2.0-only -# -# Makefile for Linux filesystem routines that are shared by client and server. -# - -obj-$(CONFIG_SMBFS_COMMON) += cifs_arc4.o -obj-$(CONFIG_SMBFS_COMMON) += cifs_md4.o diff --git a/fs/smbfs_common/arc4.h b/fs/smbfs_common/arc4.h deleted file mode 100644 index 12e71ec033a18..0000000000000 --- a/fs/smbfs_common/arc4.h +++ /dev/null @@ -1,23 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0+ */ -/* - * Common values for ARC4 Cipher Algorithm - */ - -#ifndef _CRYPTO_ARC4_H -#define _CRYPTO_ARC4_H - -#include - -#define ARC4_MIN_KEY_SIZE 1 -#define ARC4_MAX_KEY_SIZE 256 -#define ARC4_BLOCK_SIZE 1 - -struct arc4_ctx { - u32 S[256]; - u32 x, y; -}; - -int cifs_arc4_setkey(struct arc4_ctx *ctx, const u8 *in_key, unsigned int key_len); -void cifs_arc4_crypt(struct arc4_ctx *ctx, u8 *out, const u8 *in, unsigned int len); - -#endif /* _CRYPTO_ARC4_H */ diff --git a/fs/smbfs_common/cifs_arc4.c b/fs/smbfs_common/cifs_arc4.c deleted file mode 100644 index 043e4cb839fa2..0000000000000 --- a/fs/smbfs_common/cifs_arc4.c +++ /dev/null @@ -1,74 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-or-later -/* - * Cryptographic API - * - * ARC4 Cipher Algorithm - * - * Jon Oberheide - */ - -#include -#include "arc4.h" - -MODULE_LICENSE("GPL"); - -int cifs_arc4_setkey(struct arc4_ctx *ctx, const u8 *in_key, unsigned int key_len) -{ - int i, j = 0, k = 0; - - ctx->x = 1; - ctx->y = 0; - - for (i = 0; i < 256; i++) - ctx->S[i] = i; - - for (i = 0; i < 256; i++) { - u32 a = ctx->S[i]; - - j = (j + in_key[k] + a) & 0xff; - ctx->S[i] = ctx->S[j]; - ctx->S[j] = a; - if (++k >= key_len) - k = 0; - } - - return 0; -} -EXPORT_SYMBOL_GPL(cifs_arc4_setkey); - -void cifs_arc4_crypt(struct arc4_ctx *ctx, u8 *out, const u8 *in, unsigned int len) -{ - u32 *const S = ctx->S; - u32 x, y, a, b; - u32 ty, ta, tb; - - if (len == 0) - return; - - x = ctx->x; - y = ctx->y; - - a = S[x]; - y = (y + a) & 0xff; - b = S[y]; - - do { - S[y] = a; - a = (a + b) & 0xff; - S[x] = b; - x = (x + 1) & 0xff; - ta = S[x]; - ty = (y + ta) & 0xff; - tb = S[ty]; - *out++ = *in++ ^ S[a]; - if (--len == 0) - break; - y = ty; - a = ta; - b = tb; - } while (true); - - ctx->x = x; - ctx->y = y; -} -EXPORT_SYMBOL_GPL(cifs_arc4_crypt); diff --git a/fs/smbfs_common/cifs_md4.c b/fs/smbfs_common/cifs_md4.c deleted file mode 100644 index 50f78cfc6ce92..0000000000000 --- a/fs/smbfs_common/cifs_md4.c +++ /dev/null @@ -1,197 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0 -/* - * Cryptographic API. - * - * MD4 Message Digest Algorithm (RFC1320). - * - * Implementation derived from Andrew Tridgell and Steve French's - * CIFS MD4 implementation, and the cryptoapi implementation - * originally based on the public domain implementation written - * by Colin Plumb in 1993. - * - * Copyright (c) Andrew Tridgell 1997-1998. - * Modified by Steve French (sfrench@us.ibm.com) 2002 - * Copyright (c) Cryptoapi developers. - * Copyright (c) 2002 David S. Miller (davem@redhat.com) - * Copyright (c) 2002 James Morris - * - */ -#include -#include -#include -#include -#include -#include -#include "md4.h" - -MODULE_LICENSE("GPL"); - -static inline u32 lshift(u32 x, unsigned int s) -{ - x &= 0xFFFFFFFF; - return ((x << s) & 0xFFFFFFFF) | (x >> (32 - s)); -} - -static inline u32 F(u32 x, u32 y, u32 z) -{ - return (x & y) | ((~x) & z); -} - -static inline u32 G(u32 x, u32 y, u32 z) -{ - return (x & y) | (x & z) | (y & z); -} - -static inline u32 H(u32 x, u32 y, u32 z) -{ - return x ^ y ^ z; -} - -#define ROUND1(a,b,c,d,k,s) (a = lshift(a + F(b,c,d) + k, s)) -#define ROUND2(a,b,c,d,k,s) (a = lshift(a + G(b,c,d) + k + (u32)0x5A827999,s)) -#define ROUND3(a,b,c,d,k,s) (a = lshift(a + H(b,c,d) + k + (u32)0x6ED9EBA1,s)) - -static void md4_transform(u32 *hash, u32 const *in) -{ - u32 a, b, c, d; - - a = hash[0]; - b = hash[1]; - c = hash[2]; - d = hash[3]; - - ROUND1(a, b, c, d, in[0], 3); - ROUND1(d, a, b, c, in[1], 7); - ROUND1(c, d, a, b, in[2], 11); - ROUND1(b, c, d, a, in[3], 19); - ROUND1(a, b, c, d, in[4], 3); - ROUND1(d, a, b, c, in[5], 7); - ROUND1(c, d, a, b, in[6], 11); - ROUND1(b, c, d, a, in[7], 19); - ROUND1(a, b, c, d, in[8], 3); - ROUND1(d, a, b, c, in[9], 7); - ROUND1(c, d, a, b, in[10], 11); - ROUND1(b, c, d, a, in[11], 19); - ROUND1(a, b, c, d, in[12], 3); - ROUND1(d, a, b, c, in[13], 7); - ROUND1(c, d, a, b, in[14], 11); - ROUND1(b, c, d, a, in[15], 19); - - ROUND2(a, b, c, d, in[0], 3); - ROUND2(d, a, b, c, in[4], 5); - ROUND2(c, d, a, b, in[8], 9); - ROUND2(b, c, d, a, in[12], 13); - ROUND2(a, b, c, d, in[1], 3); - ROUND2(d, a, b, c, in[5], 5); - ROUND2(c, d, a, b, in[9], 9); - ROUND2(b, c, d, a, in[13], 13); - ROUND2(a, b, c, d, in[2], 3); - ROUND2(d, a, b, c, in[6], 5); - ROUND2(c, d, a, b, in[10], 9); - ROUND2(b, c, d, a, in[14], 13); - ROUND2(a, b, c, d, in[3], 3); - ROUND2(d, a, b, c, in[7], 5); - ROUND2(c, d, a, b, in[11], 9); - ROUND2(b, c, d, a, in[15], 13); - - ROUND3(a, b, c, d, in[0], 3); - ROUND3(d, a, b, c, in[8], 9); - ROUND3(c, d, a, b, in[4], 11); - ROUND3(b, c, d, a, in[12], 15); - ROUND3(a, b, c, d, in[2], 3); - ROUND3(d, a, b, c, in[10], 9); - ROUND3(c, d, a, b, in[6], 11); - ROUND3(b, c, d, a, in[14], 15); - ROUND3(a, b, c, d, in[1], 3); - ROUND3(d, a, b, c, in[9], 9); - ROUND3(c, d, a, b, in[5], 11); - ROUND3(b, c, d, a, in[13], 15); - ROUND3(a, b, c, d, in[3], 3); - ROUND3(d, a, b, c, in[11], 9); - ROUND3(c, d, a, b, in[7], 11); - ROUND3(b, c, d, a, in[15], 15); - - hash[0] += a; - hash[1] += b; - hash[2] += c; - hash[3] += d; -} - -static inline void md4_transform_helper(struct md4_ctx *ctx) -{ - le32_to_cpu_array(ctx->block, ARRAY_SIZE(ctx->block)); - md4_transform(ctx->hash, ctx->block); -} - -int cifs_md4_init(struct md4_ctx *mctx) -{ - memset(mctx, 0, sizeof(struct md4_ctx)); - mctx->hash[0] = 0x67452301; - mctx->hash[1] = 0xefcdab89; - mctx->hash[2] = 0x98badcfe; - mctx->hash[3] = 0x10325476; - mctx->byte_count = 0; - - return 0; -} -EXPORT_SYMBOL_GPL(cifs_md4_init); - -int cifs_md4_update(struct md4_ctx *mctx, const u8 *data, unsigned int len) -{ - const u32 avail = sizeof(mctx->block) - (mctx->byte_count & 0x3f); - - mctx->byte_count += len; - - if (avail > len) { - memcpy((char *)mctx->block + (sizeof(mctx->block) - avail), - data, len); - return 0; - } - - memcpy((char *)mctx->block + (sizeof(mctx->block) - avail), - data, avail); - - md4_transform_helper(mctx); - data += avail; - len -= avail; - - while (len >= sizeof(mctx->block)) { - memcpy(mctx->block, data, sizeof(mctx->block)); - md4_transform_helper(mctx); - data += sizeof(mctx->block); - len -= sizeof(mctx->block); - } - - memcpy(mctx->block, data, len); - - return 0; -} -EXPORT_SYMBOL_GPL(cifs_md4_update); - -int cifs_md4_final(struct md4_ctx *mctx, u8 *out) -{ - const unsigned int offset = mctx->byte_count & 0x3f; - char *p = (char *)mctx->block + offset; - int padding = 56 - (offset + 1); - - *p++ = 0x80; - if (padding < 0) { - memset(p, 0x00, padding + sizeof(u64)); - md4_transform_helper(mctx); - p = (char *)mctx->block; - padding = 56; - } - - memset(p, 0, padding); - mctx->block[14] = mctx->byte_count << 3; - mctx->block[15] = mctx->byte_count >> 29; - le32_to_cpu_array(mctx->block, (sizeof(mctx->block) - - sizeof(u64)) / sizeof(u32)); - md4_transform(mctx->hash, mctx->block); - cpu_to_le32_array(mctx->hash, ARRAY_SIZE(mctx->hash)); - memcpy(out, mctx->hash, sizeof(mctx->hash)); - memset(mctx, 0, sizeof(*mctx)); - - return 0; -} -EXPORT_SYMBOL_GPL(cifs_md4_final); diff --git a/fs/smbfs_common/md4.h b/fs/smbfs_common/md4.h deleted file mode 100644 index 5337becc699ab..0000000000000 --- a/fs/smbfs_common/md4.h +++ /dev/null @@ -1,27 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0+ */ -/* - * Common values for ARC4 Cipher Algorithm - */ - -#ifndef _CIFS_MD4_H -#define _CIFS_MD4_H - -#include - -#define MD4_DIGEST_SIZE 16 -#define MD4_HMAC_BLOCK_SIZE 64 -#define MD4_BLOCK_WORDS 16 -#define MD4_HASH_WORDS 4 - -struct md4_ctx { - u32 hash[MD4_HASH_WORDS]; - u32 block[MD4_BLOCK_WORDS]; - u64 byte_count; -}; - - -int cifs_md4_init(struct md4_ctx *mctx); -int cifs_md4_update(struct md4_ctx *mctx, const u8 *data, unsigned int len); -int cifs_md4_final(struct md4_ctx *mctx, u8 *out); - -#endif /* _CIFS_MD4_H */ diff --git a/fs/smbfs_common/smb2pdu.h b/fs/smbfs_common/smb2pdu.h deleted file mode 100644 index 7d605db3bb3b9..0000000000000 --- a/fs/smbfs_common/smb2pdu.h +++ /dev/null @@ -1,1702 +0,0 @@ -/* SPDX-License-Identifier: LGPL-2.1 */ -#ifndef _COMMON_SMB2PDU_H -#define _COMMON_SMB2PDU_H - -/* - * Note that, due to trying to use names similar to the protocol specifications, - * there are many mixed case field names in the structures below. Although - * this does not match typical Linux kernel style, it is necessary to be - * able to match against the protocol specfication. - * - * SMB2 commands - * Some commands have minimal (wct=0,bcc=0), or uninteresting, responses - * (ie no useful data other than the SMB error code itself) and are marked such. - * Knowing this helps avoid response buffer allocations and copy in some cases. - */ - -/* List of commands in host endian */ -#define SMB2_NEGOTIATE_HE 0x0000 -#define SMB2_SESSION_SETUP_HE 0x0001 -#define SMB2_LOGOFF_HE 0x0002 /* trivial request/resp */ -#define SMB2_TREE_CONNECT_HE 0x0003 -#define SMB2_TREE_DISCONNECT_HE 0x0004 /* trivial req/resp */ -#define SMB2_CREATE_HE 0x0005 -#define SMB2_CLOSE_HE 0x0006 -#define SMB2_FLUSH_HE 0x0007 /* trivial resp */ -#define SMB2_READ_HE 0x0008 -#define SMB2_WRITE_HE 0x0009 -#define SMB2_LOCK_HE 0x000A -#define SMB2_IOCTL_HE 0x000B -#define SMB2_CANCEL_HE 0x000C -#define SMB2_ECHO_HE 0x000D -#define SMB2_QUERY_DIRECTORY_HE 0x000E -#define SMB2_CHANGE_NOTIFY_HE 0x000F -#define SMB2_QUERY_INFO_HE 0x0010 -#define SMB2_SET_INFO_HE 0x0011 -#define SMB2_OPLOCK_BREAK_HE 0x0012 - -/* The same list in little endian */ -#define SMB2_NEGOTIATE cpu_to_le16(SMB2_NEGOTIATE_HE) -#define SMB2_SESSION_SETUP cpu_to_le16(SMB2_SESSION_SETUP_HE) -#define SMB2_LOGOFF cpu_to_le16(SMB2_LOGOFF_HE) -#define SMB2_TREE_CONNECT cpu_to_le16(SMB2_TREE_CONNECT_HE) -#define SMB2_TREE_DISCONNECT cpu_to_le16(SMB2_TREE_DISCONNECT_HE) -#define SMB2_CREATE cpu_to_le16(SMB2_CREATE_HE) -#define SMB2_CLOSE cpu_to_le16(SMB2_CLOSE_HE) -#define SMB2_FLUSH cpu_to_le16(SMB2_FLUSH_HE) -#define SMB2_READ cpu_to_le16(SMB2_READ_HE) -#define SMB2_WRITE cpu_to_le16(SMB2_WRITE_HE) -#define SMB2_LOCK cpu_to_le16(SMB2_LOCK_HE) -#define SMB2_IOCTL cpu_to_le16(SMB2_IOCTL_HE) -#define SMB2_CANCEL cpu_to_le16(SMB2_CANCEL_HE) -#define SMB2_ECHO cpu_to_le16(SMB2_ECHO_HE) -#define SMB2_QUERY_DIRECTORY cpu_to_le16(SMB2_QUERY_DIRECTORY_HE) -#define SMB2_CHANGE_NOTIFY cpu_to_le16(SMB2_CHANGE_NOTIFY_HE) -#define SMB2_QUERY_INFO cpu_to_le16(SMB2_QUERY_INFO_HE) -#define SMB2_SET_INFO cpu_to_le16(SMB2_SET_INFO_HE) -#define SMB2_OPLOCK_BREAK cpu_to_le16(SMB2_OPLOCK_BREAK_HE) - -#define SMB2_INTERNAL_CMD cpu_to_le16(0xFFFF) - -#define NUMBER_OF_SMB2_COMMANDS 0x0013 - -/* - * Size of the session key (crypto key encrypted with the password - */ -#define SMB2_NTLMV2_SESSKEY_SIZE 16 -#define SMB2_SIGNATURE_SIZE 16 -#define SMB2_HMACSHA256_SIZE 32 -#define SMB2_CMACAES_SIZE 16 -#define SMB3_GCM128_CRYPTKEY_SIZE 16 -#define SMB3_GCM256_CRYPTKEY_SIZE 32 - -/* - * Size of the smb3 encryption/decryption keys - * This size is big enough to store any cipher key types. - */ -#define SMB3_ENC_DEC_KEY_SIZE 32 - -/* - * Size of the smb3 signing key - */ -#define SMB3_SIGN_KEY_SIZE 16 - -#define CIFS_CLIENT_CHALLENGE_SIZE 8 - -/* Maximum buffer size value we can send with 1 credit */ -#define SMB2_MAX_BUFFER_SIZE 65536 - -/* - * The default wsize is 1M for SMB2 (and for some CIFS cases). - * find_get_pages seems to return a maximum of 256 - * pages in a single call. With PAGE_SIZE == 4k, this means we can - * fill a single wsize request with a single call. - */ -#define SMB3_DEFAULT_IOSIZE (4 * 1024 * 1024) - -/* - * SMB2 Header Definition - * - * "MBZ" : Must be Zero - * "BB" : BugBug, Something to check/review/analyze later - * "PDU" : "Protocol Data Unit" (ie a network "frame") - * - */ - -#define __SMB2_HEADER_STRUCTURE_SIZE 64 -#define SMB2_HEADER_STRUCTURE_SIZE \ - cpu_to_le16(__SMB2_HEADER_STRUCTURE_SIZE) - -#define SMB2_PROTO_NUMBER cpu_to_le32(0x424d53fe) -#define SMB2_TRANSFORM_PROTO_NUM cpu_to_le32(0x424d53fd) -#define SMB2_COMPRESSION_TRANSFORM_ID cpu_to_le32(0x424d53fc) - -/* - * SMB2 flag definitions - */ -#define SMB2_FLAGS_SERVER_TO_REDIR cpu_to_le32(0x00000001) -#define SMB2_FLAGS_ASYNC_COMMAND cpu_to_le32(0x00000002) -#define SMB2_FLAGS_RELATED_OPERATIONS cpu_to_le32(0x00000004) -#define SMB2_FLAGS_SIGNED cpu_to_le32(0x00000008) -#define SMB2_FLAGS_PRIORITY_MASK cpu_to_le32(0x00000070) /* SMB3.1.1 */ -#define SMB2_FLAGS_DFS_OPERATIONS cpu_to_le32(0x10000000) -#define SMB2_FLAGS_REPLAY_OPERATION cpu_to_le32(0x20000000) /* SMB3 & up */ - -/* - * Definitions for SMB2 Protocol Data Units (network frames) - * - * See MS-SMB2.PDF specification for protocol details. - * The Naming convention is the lower case version of the SMB2 - * command code name for the struct. Note that structures must be packed. - * - */ - -/* See MS-SMB2 section 2.2.1 */ -struct smb2_hdr { - __le32 ProtocolId; /* 0xFE 'S' 'M' 'B' */ - __le16 StructureSize; /* 64 */ - __le16 CreditCharge; /* MBZ */ - __le32 Status; /* Error from server */ - __le16 Command; - __le16 CreditRequest; /* CreditResponse */ - __le32 Flags; - __le32 NextCommand; - __le64 MessageId; - union { - struct { - __le32 ProcessId; - __le32 TreeId; - } __packed SyncId; - __le64 AsyncId; - } __packed Id; - __le64 SessionId; - __u8 Signature[16]; -} __packed; - -struct smb2_pdu { - struct smb2_hdr hdr; - __le16 StructureSize2; /* size of wct area (varies, request specific) */ -} __packed; - -#define SMB2_ERROR_STRUCTURE_SIZE2 9 -#define SMB2_ERROR_STRUCTURE_SIZE2_LE cpu_to_le16(SMB2_ERROR_STRUCTURE_SIZE2) - -struct smb2_err_rsp { - struct smb2_hdr hdr; - __le16 StructureSize; - __u8 ErrorContextCount; - __u8 Reserved; - __le32 ByteCount; /* even if zero, at least one byte follows */ - __u8 ErrorData[1]; /* variable length */ -} __packed; - -#define SMB3_AES_CCM_NONCE 11 -#define SMB3_AES_GCM_NONCE 12 - -/* Transform flags (for 3.0 dialect this flag indicates CCM */ -#define TRANSFORM_FLAG_ENCRYPTED 0x0001 -struct smb2_transform_hdr { - __le32 ProtocolId; /* 0xFD 'S' 'M' 'B' */ - __u8 Signature[16]; - __u8 Nonce[16]; - __le32 OriginalMessageSize; - __u16 Reserved1; - __le16 Flags; /* EncryptionAlgorithm for 3.0, enc enabled for 3.1.1 */ - __le64 SessionId; -} __packed; - - -/* See MS-SMB2 2.2.42 */ -struct smb2_compression_transform_hdr_unchained { - __le32 ProtocolId; /* 0xFC 'S' 'M' 'B' */ - __le32 OriginalCompressedSegmentSize; - __le16 CompressionAlgorithm; - __le16 Flags; - __le16 Length; /* if chained it is length, else offset */ -} __packed; - -/* See MS-SMB2 2.2.42.1 */ -#define SMB2_COMPRESSION_FLAG_NONE 0x0000 -#define SMB2_COMPRESSION_FLAG_CHAINED 0x0001 - -struct compression_payload_header { - __le16 CompressionAlgorithm; - __le16 Flags; - __le32 Length; /* length of compressed playload including field below if present */ - /* __le32 OriginalPayloadSize; */ /* optional, present when LZNT1, LZ77, LZ77+Huffman */ -} __packed; - -/* See MS-SMB2 2.2.42.2 */ -struct smb2_compression_transform_hdr_chained { - __le32 ProtocolId; /* 0xFC 'S' 'M' 'B' */ - __le32 OriginalCompressedSegmentSize; - /* struct compression_payload_header[] */ -} __packed; - -/* See MS-SMB2 2.2.42.2.2 */ -struct compression_pattern_payload_v1 { - __le16 Pattern; - __le16 Reserved1; - __le16 Reserved2; - __le32 Repetitions; -} __packed; - -/* See MS-SMB2 section 2.2.9.2 */ -/* Context Types */ -#define SMB2_RESERVED_TREE_CONNECT_CONTEXT_ID 0x0000 -#define SMB2_REMOTED_IDENTITY_TREE_CONNECT_CONTEXT_ID cpu_to_le16(0x0001) - -struct tree_connect_contexts { - __le16 ContextType; - __le16 DataLength; - __le32 Reserved; - __u8 Data[]; -} __packed; - -/* Remoted identity tree connect context structures - see MS-SMB2 2.2.9.2.1 */ -struct smb3_blob_data { - __le16 BlobSize; - __u8 BlobData[]; -} __packed; - -/* Valid values for Attr */ -#define SE_GROUP_MANDATORY 0x00000001 -#define SE_GROUP_ENABLED_BY_DEFAULT 0x00000002 -#define SE_GROUP_ENABLED 0x00000004 -#define SE_GROUP_OWNER 0x00000008 -#define SE_GROUP_USE_FOR_DENY_ONLY 0x00000010 -#define SE_GROUP_INTEGRITY 0x00000020 -#define SE_GROUP_INTEGRITY_ENABLED 0x00000040 -#define SE_GROUP_RESOURCE 0x20000000 -#define SE_GROUP_LOGON_ID 0xC0000000 - -/* struct sid_attr_data is SidData array in BlobData format then le32 Attr */ - -struct sid_array_data { - __le16 SidAttrCount; - /* SidAttrList - array of sid_attr_data structs */ -} __packed; - -struct luid_attr_data { - -} __packed; - -/* - * struct privilege_data is the same as BLOB_DATA - see MS-SMB2 2.2.9.2.1.5 - * but with size of LUID_ATTR_DATA struct and BlobData set to LUID_ATTR DATA - */ - -struct privilege_array_data { - __le16 PrivilegeCount; - /* array of privilege_data structs */ -} __packed; - -struct remoted_identity_tcon_context { - __le16 TicketType; /* must be 0x0001 */ - __le16 TicketSize; /* total size of this struct */ - __le16 User; /* offset to SID_ATTR_DATA struct with user info */ - __le16 UserName; /* offset to null terminated Unicode username string */ - __le16 Domain; /* offset to null terminated Unicode domain name */ - __le16 Groups; /* offset to SID_ARRAY_DATA struct with group info */ - __le16 RestrictedGroups; /* similar to above */ - __le16 Privileges; /* offset to PRIVILEGE_ARRAY_DATA struct */ - __le16 PrimaryGroup; /* offset to SID_ARRAY_DATA struct */ - __le16 Owner; /* offset to BLOB_DATA struct */ - __le16 DefaultDacl; /* offset to BLOB_DATA struct */ - __le16 DeviceGroups; /* offset to SID_ARRAY_DATA struct */ - __le16 UserClaims; /* offset to BLOB_DATA struct */ - __le16 DeviceClaims; /* offset to BLOB_DATA struct */ - __u8 TicketInfo[]; /* variable length buf - remoted identity data */ -} __packed; - -struct smb2_tree_connect_req_extension { - __le32 TreeConnectContextOffset; - __le16 TreeConnectContextCount; - __u8 Reserved[10]; - __u8 PathName[]; /* variable sized array */ - /* followed by array of TreeConnectContexts */ -} __packed; - -/* Flags/Reserved for SMB3.1.1 */ -#define SMB2_TREE_CONNECT_FLAG_CLUSTER_RECONNECT cpu_to_le16(0x0001) -#define SMB2_TREE_CONNECT_FLAG_REDIRECT_TO_OWNER cpu_to_le16(0x0002) -#define SMB2_TREE_CONNECT_FLAG_EXTENSION_PRESENT cpu_to_le16(0x0004) - -struct smb2_tree_connect_req { - struct smb2_hdr hdr; - __le16 StructureSize; /* Must be 9 */ - __le16 Flags; /* Flags in SMB3.1.1 */ - __le16 PathOffset; - __le16 PathLength; - __u8 Buffer[1]; /* variable length */ -} __packed; - -/* Possible ShareType values */ -#define SMB2_SHARE_TYPE_DISK 0x01 -#define SMB2_SHARE_TYPE_PIPE 0x02 -#define SMB2_SHARE_TYPE_PRINT 0x03 - -/* - * Possible ShareFlags - exactly one and only one of the first 4 caching flags - * must be set (any of the remaining, SHI1005, flags may be set individually - * or in combination. - */ -#define SMB2_SHAREFLAG_MANUAL_CACHING 0x00000000 -#define SMB2_SHAREFLAG_AUTO_CACHING 0x00000010 -#define SMB2_SHAREFLAG_VDO_CACHING 0x00000020 -#define SMB2_SHAREFLAG_NO_CACHING 0x00000030 -#define SHI1005_FLAGS_DFS 0x00000001 -#define SHI1005_FLAGS_DFS_ROOT 0x00000002 -#define SHI1005_FLAGS_RESTRICT_EXCLUSIVE_OPENS 0x00000100 -#define SHI1005_FLAGS_FORCE_SHARED_DELETE 0x00000200 -#define SHI1005_FLAGS_ALLOW_NAMESPACE_CACHING 0x00000400 -#define SHI1005_FLAGS_ACCESS_BASED_DIRECTORY_ENUM 0x00000800 -#define SHI1005_FLAGS_FORCE_LEVELII_OPLOCK 0x00001000 -#define SHI1005_FLAGS_ENABLE_HASH_V1 0x00002000 -#define SHI1005_FLAGS_ENABLE_HASH_V2 0x00004000 -#define SHI1005_FLAGS_ENCRYPT_DATA 0x00008000 -#define SMB2_SHAREFLAG_IDENTITY_REMOTING 0x00040000 /* 3.1.1 */ -#define SMB2_SHAREFLAG_COMPRESS_DATA 0x00100000 /* 3.1.1 */ -#define SHI1005_FLAGS_ALL 0x0014FF33 - -/* Possible share capabilities */ -#define SMB2_SHARE_CAP_DFS cpu_to_le32(0x00000008) /* all dialects */ -#define SMB2_SHARE_CAP_CONTINUOUS_AVAILABILITY cpu_to_le32(0x00000010) /* 3.0 */ -#define SMB2_SHARE_CAP_SCALEOUT cpu_to_le32(0x00000020) /* 3.0 */ -#define SMB2_SHARE_CAP_CLUSTER cpu_to_le32(0x00000040) /* 3.0 */ -#define SMB2_SHARE_CAP_ASYMMETRIC cpu_to_le32(0x00000080) /* 3.02 */ -#define SMB2_SHARE_CAP_REDIRECT_TO_OWNER cpu_to_le32(0x00000100) /* 3.1.1 */ - -struct smb2_tree_connect_rsp { - struct smb2_hdr hdr; - __le16 StructureSize; /* Must be 16 */ - __u8 ShareType; /* see below */ - __u8 Reserved; - __le32 ShareFlags; /* see below */ - __le32 Capabilities; /* see below */ - __le32 MaximalAccess; -} __packed; - -struct smb2_tree_disconnect_req { - struct smb2_hdr hdr; - __le16 StructureSize; /* Must be 4 */ - __le16 Reserved; -} __packed; - -struct smb2_tree_disconnect_rsp { - struct smb2_hdr hdr; - __le16 StructureSize; /* Must be 4 */ - __le16 Reserved; -} __packed; - - -/* - * SMB2_NEGOTIATE_PROTOCOL See MS-SMB2 section 2.2.3 - */ -/* SecurityMode flags */ -#define SMB2_NEGOTIATE_SIGNING_ENABLED 0x0001 -#define SMB2_NEGOTIATE_SIGNING_ENABLED_LE cpu_to_le16(0x0001) -#define SMB2_NEGOTIATE_SIGNING_REQUIRED 0x0002 -#define SMB2_NEGOTIATE_SIGNING_REQUIRED_LE cpu_to_le16(0x0002) -#define SMB2_SEC_MODE_FLAGS_ALL 0x0003 - -/* Capabilities flags */ -#define SMB2_GLOBAL_CAP_DFS 0x00000001 -#define SMB2_GLOBAL_CAP_LEASING 0x00000002 /* Resp only New to SMB2.1 */ -#define SMB2_GLOBAL_CAP_LARGE_MTU 0X00000004 /* Resp only New to SMB2.1 */ -#define SMB2_GLOBAL_CAP_MULTI_CHANNEL 0x00000008 /* New to SMB3 */ -#define SMB2_GLOBAL_CAP_PERSISTENT_HANDLES 0x00000010 /* New to SMB3 */ -#define SMB2_GLOBAL_CAP_DIRECTORY_LEASING 0x00000020 /* New to SMB3 */ -#define SMB2_GLOBAL_CAP_ENCRYPTION 0x00000040 /* New to SMB3 */ -/* Internal types */ -#define SMB2_NT_FIND 0x00100000 -#define SMB2_LARGE_FILES 0x00200000 - -#define SMB2_CLIENT_GUID_SIZE 16 -#define SMB2_CREATE_GUID_SIZE 16 - -/* Dialects */ -#define SMB10_PROT_ID 0x0000 /* local only, not sent on wire w/CIFS negprot */ -#define SMB20_PROT_ID 0x0202 -#define SMB21_PROT_ID 0x0210 -#define SMB2X_PROT_ID 0x02FF -#define SMB30_PROT_ID 0x0300 -#define SMB302_PROT_ID 0x0302 -#define SMB311_PROT_ID 0x0311 -#define BAD_PROT_ID 0xFFFF - -#define SMB311_SALT_SIZE 32 -/* Hash Algorithm Types */ -#define SMB2_PREAUTH_INTEGRITY_SHA512 cpu_to_le16(0x0001) -#define SMB2_PREAUTH_HASH_SIZE 64 - -/* Negotiate Contexts - ContextTypes. See MS-SMB2 section 2.2.3.1 for details */ -#define SMB2_PREAUTH_INTEGRITY_CAPABILITIES cpu_to_le16(1) -#define SMB2_ENCRYPTION_CAPABILITIES cpu_to_le16(2) -#define SMB2_COMPRESSION_CAPABILITIES cpu_to_le16(3) -#define SMB2_NETNAME_NEGOTIATE_CONTEXT_ID cpu_to_le16(5) -#define SMB2_TRANSPORT_CAPABILITIES cpu_to_le16(6) -#define SMB2_RDMA_TRANSFORM_CAPABILITIES cpu_to_le16(7) -#define SMB2_SIGNING_CAPABILITIES cpu_to_le16(8) -#define SMB2_POSIX_EXTENSIONS_AVAILABLE cpu_to_le16(0x100) - -struct smb2_neg_context { - __le16 ContextType; - __le16 DataLength; - __le32 Reserved; - /* Followed by array of data. NOTE: some servers require padding to 8 byte boundary */ -} __packed; - -/* - * SaltLength that the server send can be zero, so the only three required - * fields (all __le16) end up six bytes total, so the minimum context data len - * in the response is six bytes which accounts for - * - * HashAlgorithmCount, SaltLength, and 1 HashAlgorithm. - */ -#define MIN_PREAUTH_CTXT_DATA_LEN 6 - -struct smb2_preauth_neg_context { - __le16 ContextType; /* 1 */ - __le16 DataLength; - __le32 Reserved; - __le16 HashAlgorithmCount; /* 1 */ - __le16 SaltLength; - __le16 HashAlgorithms; /* HashAlgorithms[0] since only one defined */ - __u8 Salt[SMB311_SALT_SIZE]; -} __packed; - -/* Encryption Algorithms Ciphers */ -#define SMB2_ENCRYPTION_AES128_CCM cpu_to_le16(0x0001) -#define SMB2_ENCRYPTION_AES128_GCM cpu_to_le16(0x0002) -#define SMB2_ENCRYPTION_AES256_CCM cpu_to_le16(0x0003) -#define SMB2_ENCRYPTION_AES256_GCM cpu_to_le16(0x0004) - -/* Min encrypt context data is one cipher so 2 bytes + 2 byte count field */ -#define MIN_ENCRYPT_CTXT_DATA_LEN 4 -struct smb2_encryption_neg_context { - __le16 ContextType; /* 2 */ - __le16 DataLength; - __le32 Reserved; - /* CipherCount usally 2, but can be 3 when AES256-GCM enabled */ - __le16 CipherCount; /* AES128-GCM and AES128-CCM by default */ - __le16 Ciphers[]; -} __packed; - -/* See MS-SMB2 2.2.3.1.3 */ -#define SMB3_COMPRESS_NONE cpu_to_le16(0x0000) -#define SMB3_COMPRESS_LZNT1 cpu_to_le16(0x0001) -#define SMB3_COMPRESS_LZ77 cpu_to_le16(0x0002) -#define SMB3_COMPRESS_LZ77_HUFF cpu_to_le16(0x0003) -/* Pattern scanning algorithm See MS-SMB2 3.1.4.4.1 */ -#define SMB3_COMPRESS_PATTERN cpu_to_le16(0x0004) /* Pattern_V1 */ - -/* Compression Flags */ -#define SMB2_COMPRESSION_CAPABILITIES_FLAG_NONE cpu_to_le32(0x00000000) -#define SMB2_COMPRESSION_CAPABILITIES_FLAG_CHAINED cpu_to_le32(0x00000001) - -struct smb2_compression_capabilities_context { - __le16 ContextType; /* 3 */ - __le16 DataLength; - __le32 Reserved; - __le16 CompressionAlgorithmCount; - __le16 Padding; - __le32 Flags; - __le16 CompressionAlgorithms[3]; - __u16 Pad; /* Some servers require pad to DataLen multiple of 8 */ - /* Check if pad needed */ -} __packed; - -/* - * For smb2_netname_negotiate_context_id See MS-SMB2 2.2.3.1.4. - * Its struct simply contains NetName, an array of Unicode characters - */ -struct smb2_netname_neg_context { - __le16 ContextType; /* 5 */ - __le16 DataLength; - __le32 Reserved; - __le16 NetName[]; /* hostname of target converted to UCS-2 */ -} __packed; - -/* - * For smb2_transport_capabilities context see MS-SMB2 2.2.3.1.5 - * and 2.2.4.1.5 - */ - -/* Flags */ -#define SMB2_ACCEPT_TRANSPORT_LEVEL_SECURITY 0x00000001 - -struct smb2_transport_capabilities_context { - __le16 ContextType; /* 6 */ - __le16 DataLength; - __u32 Reserved; - __le32 Flags; - __u32 Pad; -} __packed; - -/* - * For rdma transform capabilities context see MS-SMB2 2.2.3.1.6 - * and 2.2.4.1.6 - */ - -/* RDMA Transform IDs */ -#define SMB2_RDMA_TRANSFORM_NONE 0x0000 -#define SMB2_RDMA_TRANSFORM_ENCRYPTION 0x0001 -#define SMB2_RDMA_TRANSFORM_SIGNING 0x0002 - -struct smb2_rdma_transform_capabilities_context { - __le16 ContextType; /* 7 */ - __le16 DataLength; - __u32 Reserved; - __le16 TransformCount; - __u16 Reserved1; - __u32 Reserved2; - __le16 RDMATransformIds[]; -} __packed; - -/* - * For signing capabilities context see MS-SMB2 2.2.3.1.7 - * and 2.2.4.1.7 - */ - -/* Signing algorithms */ -#define SIGNING_ALG_HMAC_SHA256 0 -#define SIGNING_ALG_HMAC_SHA256_LE cpu_to_le16(0) -#define SIGNING_ALG_AES_CMAC 1 -#define SIGNING_ALG_AES_CMAC_LE cpu_to_le16(1) -#define SIGNING_ALG_AES_GMAC 2 -#define SIGNING_ALG_AES_GMAC_LE cpu_to_le16(2) - -struct smb2_signing_capabilities { - __le16 ContextType; /* 8 */ - __le16 DataLength; - __le32 Reserved; - __le16 SigningAlgorithmCount; - __le16 SigningAlgorithms[]; - /* Followed by padding to 8 byte boundary (required by some servers) */ -} __packed; - -#define POSIX_CTXT_DATA_LEN 16 -struct smb2_posix_neg_context { - __le16 ContextType; /* 0x100 */ - __le16 DataLength; - __le32 Reserved; - __u8 Name[16]; /* POSIX ctxt GUID 93AD25509CB411E7B42383DE968BCD7C */ -} __packed; - -struct smb2_negotiate_req { - struct smb2_hdr hdr; - __le16 StructureSize; /* Must be 36 */ - __le16 DialectCount; - __le16 SecurityMode; - __le16 Reserved; /* MBZ */ - __le32 Capabilities; - __u8 ClientGUID[SMB2_CLIENT_GUID_SIZE]; - /* In SMB3.02 and earlier next three were MBZ le64 ClientStartTime */ - __le32 NegotiateContextOffset; /* SMB3.1.1 only. MBZ earlier */ - __le16 NegotiateContextCount; /* SMB3.1.1 only. MBZ earlier */ - __le16 Reserved2; - __le16 Dialects[]; -} __packed; - -struct smb2_negotiate_rsp { - struct smb2_hdr hdr; - __le16 StructureSize; /* Must be 65 */ - __le16 SecurityMode; - __le16 DialectRevision; - __le16 NegotiateContextCount; /* Prior to SMB3.1.1 was Reserved & MBZ */ - __u8 ServerGUID[16]; - __le32 Capabilities; - __le32 MaxTransactSize; - __le32 MaxReadSize; - __le32 MaxWriteSize; - __le64 SystemTime; /* MBZ */ - __le64 ServerStartTime; - __le16 SecurityBufferOffset; - __le16 SecurityBufferLength; - __le32 NegotiateContextOffset; /* Pre:SMB3.1.1 was reserved/ignored */ - __u8 Buffer[1]; /* variable length GSS security buffer */ -} __packed; - - -/* - * SMB2_SESSION_SETUP See MS-SMB2 section 2.2.5 - */ -/* Flags */ -#define SMB2_SESSION_REQ_FLAG_BINDING 0x01 -#define SMB2_SESSION_REQ_FLAG_ENCRYPT_DATA 0x04 - -struct smb2_sess_setup_req { - struct smb2_hdr hdr; - __le16 StructureSize; /* Must be 25 */ - __u8 Flags; - __u8 SecurityMode; - __le32 Capabilities; - __le32 Channel; - __le16 SecurityBufferOffset; - __le16 SecurityBufferLength; - __le64 PreviousSessionId; - __u8 Buffer[1]; /* variable length GSS security buffer */ -} __packed; - -/* Currently defined SessionFlags */ -#define SMB2_SESSION_FLAG_IS_GUEST 0x0001 -#define SMB2_SESSION_FLAG_IS_GUEST_LE cpu_to_le16(0x0001) -#define SMB2_SESSION_FLAG_IS_NULL 0x0002 -#define SMB2_SESSION_FLAG_IS_NULL_LE cpu_to_le16(0x0002) -#define SMB2_SESSION_FLAG_ENCRYPT_DATA 0x0004 -#define SMB2_SESSION_FLAG_ENCRYPT_DATA_LE cpu_to_le16(0x0004) - -struct smb2_sess_setup_rsp { - struct smb2_hdr hdr; - __le16 StructureSize; /* Must be 9 */ - __le16 SessionFlags; - __le16 SecurityBufferOffset; - __le16 SecurityBufferLength; - __u8 Buffer[1]; /* variable length GSS security buffer */ -} __packed; - - -/* - * SMB2_LOGOFF See MS-SMB2 section 2.2.7 - */ -struct smb2_logoff_req { - struct smb2_hdr hdr; - __le16 StructureSize; /* Must be 4 */ - __le16 Reserved; -} __packed; - -struct smb2_logoff_rsp { - struct smb2_hdr hdr; - __le16 StructureSize; /* Must be 4 */ - __le16 Reserved; -} __packed; - - -/* - * SMB2_CLOSE See MS-SMB2 section 2.2.15 - */ -/* Currently defined values for close flags */ -#define SMB2_CLOSE_FLAG_POSTQUERY_ATTRIB cpu_to_le16(0x0001) -struct smb2_close_req { - struct smb2_hdr hdr; - __le16 StructureSize; /* Must be 24 */ - __le16 Flags; - __le32 Reserved; - __u64 PersistentFileId; /* opaque endianness */ - __u64 VolatileFileId; /* opaque endianness */ -} __packed; - -/* - * Maximum size of a SMB2_CLOSE response is 64 (smb2 header) + 60 (data) - */ -#define MAX_SMB2_CLOSE_RESPONSE_SIZE 124 - -struct smb2_close_rsp { - struct smb2_hdr hdr; - __le16 StructureSize; /* 60 */ - __le16 Flags; - __le32 Reserved; - __le64 CreationTime; - __le64 LastAccessTime; - __le64 LastWriteTime; - __le64 ChangeTime; - __le64 AllocationSize; /* Beginning of FILE_STANDARD_INFO equivalent */ - __le64 EndOfFile; - __le32 Attributes; -} __packed; - - -/* - * SMB2_READ See MS-SMB2 section 2.2.19 - */ -/* For read request Flags field below, following flag is defined for SMB3.02 */ -#define SMB2_READFLAG_READ_UNBUFFERED 0x01 -#define SMB2_READFLAG_REQUEST_COMPRESSED 0x02 /* See MS-SMB2 2.2.19 */ - -/* Channel field for read and write: exactly one of following flags can be set*/ -#define SMB2_CHANNEL_NONE cpu_to_le32(0x00000000) -#define SMB2_CHANNEL_RDMA_V1 cpu_to_le32(0x00000001) -#define SMB2_CHANNEL_RDMA_V1_INVALIDATE cpu_to_le32(0x00000002) -#define SMB2_CHANNEL_RDMA_TRANSFORM cpu_to_le32(0x00000003) - -/* SMB2 read request without RFC1001 length at the beginning */ -struct smb2_read_req { - struct smb2_hdr hdr; - __le16 StructureSize; /* Must be 49 */ - __u8 Padding; /* offset from start of SMB2 header to place read */ - __u8 Flags; /* MBZ unless SMB3.02 or later */ - __le32 Length; - __le64 Offset; - __u64 PersistentFileId; - __u64 VolatileFileId; - __le32 MinimumCount; - __le32 Channel; /* MBZ except for SMB3 or later */ - __le32 RemainingBytes; - __le16 ReadChannelInfoOffset; - __le16 ReadChannelInfoLength; - __u8 Buffer[1]; -} __packed; - -/* Read flags */ -#define SMB2_READFLAG_RESPONSE_NONE cpu_to_le32(0x00000000) -#define SMB2_READFLAG_RESPONSE_RDMA_TRANSFORM cpu_to_le32(0x00000001) - -struct smb2_read_rsp { - struct smb2_hdr hdr; - __le16 StructureSize; /* Must be 17 */ - __u8 DataOffset; - __u8 Reserved; - __le32 DataLength; - __le32 DataRemaining; - __le32 Flags; - __u8 Buffer[1]; -} __packed; - - -/* - * SMB2_WRITE See MS-SMB2 section 2.2.21 - */ -/* For write request Flags field below the following flags are defined: */ -#define SMB2_WRITEFLAG_WRITE_THROUGH 0x00000001 /* SMB2.1 or later */ -#define SMB2_WRITEFLAG_WRITE_UNBUFFERED 0x00000002 /* SMB3.02 or later */ - -struct smb2_write_req { - struct smb2_hdr hdr; - __le16 StructureSize; /* Must be 49 */ - __le16 DataOffset; /* offset from start of SMB2 header to write data */ - __le32 Length; - __le64 Offset; - __u64 PersistentFileId; /* opaque endianness */ - __u64 VolatileFileId; /* opaque endianness */ - __le32 Channel; /* MBZ unless SMB3.02 or later */ - __le32 RemainingBytes; - __le16 WriteChannelInfoOffset; - __le16 WriteChannelInfoLength; - __le32 Flags; - __u8 Buffer[1]; -} __packed; - -struct smb2_write_rsp { - struct smb2_hdr hdr; - __le16 StructureSize; /* Must be 17 */ - __u8 DataOffset; - __u8 Reserved; - __le32 DataLength; - __le32 DataRemaining; - __u32 Reserved2; - __u8 Buffer[1]; -} __packed; - - -/* - * SMB2_FLUSH See MS-SMB2 section 2.2.17 - */ -struct smb2_flush_req { - struct smb2_hdr hdr; - __le16 StructureSize; /* Must be 24 */ - __le16 Reserved1; - __le32 Reserved2; - __u64 PersistentFileId; - __u64 VolatileFileId; -} __packed; - -struct smb2_flush_rsp { - struct smb2_hdr hdr; - __le16 StructureSize; - __le16 Reserved; -} __packed; - -#define SMB2_LOCKFLAG_SHARED 0x0001 -#define SMB2_LOCKFLAG_EXCLUSIVE 0x0002 -#define SMB2_LOCKFLAG_UNLOCK 0x0004 -#define SMB2_LOCKFLAG_FAIL_IMMEDIATELY 0x0010 -#define SMB2_LOCKFLAG_MASK 0x0007 - -struct smb2_lock_element { - __le64 Offset; - __le64 Length; - __le32 Flags; - __le32 Reserved; -} __packed; - -struct smb2_lock_req { - struct smb2_hdr hdr; - __le16 StructureSize; /* Must be 48 */ - __le16 LockCount; - /* - * The least significant four bits are the index, the other 28 bits are - * the lock sequence number (0 to 64). See MS-SMB2 2.2.26 - */ - __le32 LockSequenceNumber; - __u64 PersistentFileId; - __u64 VolatileFileId; - /* Followed by at least one */ - struct smb2_lock_element locks[1]; -} __packed; - -struct smb2_lock_rsp { - struct smb2_hdr hdr; - __le16 StructureSize; /* Must be 4 */ - __le16 Reserved; -} __packed; - -struct smb2_echo_req { - struct smb2_hdr hdr; - __le16 StructureSize; /* Must be 4 */ - __u16 Reserved; -} __packed; - -struct smb2_echo_rsp { - struct smb2_hdr hdr; - __le16 StructureSize; /* Must be 4 */ - __u16 Reserved; -} __packed; - -/* - * Valid FileInformation classes for query directory - * - * Note that these are a subset of the (file) QUERY_INFO levels defined - * later in this file (but since QUERY_DIRECTORY uses equivalent numbers - * we do not redefine them here) - * - * FileDirectoryInfomation 0x01 - * FileFullDirectoryInformation 0x02 - * FileIdFullDirectoryInformation 0x26 - * FileBothDirectoryInformation 0x03 - * FileIdBothDirectoryInformation 0x25 - * FileNamesInformation 0x0C - * FileIdExtdDirectoryInformation 0x3C - */ - -/* search (query_directory) Flags field */ -#define SMB2_RESTART_SCANS 0x01 -#define SMB2_RETURN_SINGLE_ENTRY 0x02 -#define SMB2_INDEX_SPECIFIED 0x04 -#define SMB2_REOPEN 0x10 - -struct smb2_query_directory_req { - struct smb2_hdr hdr; - __le16 StructureSize; /* Must be 33 */ - __u8 FileInformationClass; - __u8 Flags; - __le32 FileIndex; - __u64 PersistentFileId; - __u64 VolatileFileId; - __le16 FileNameOffset; - __le16 FileNameLength; - __le32 OutputBufferLength; - __u8 Buffer[1]; -} __packed; - -struct smb2_query_directory_rsp { - struct smb2_hdr hdr; - __le16 StructureSize; /* Must be 9 */ - __le16 OutputBufferOffset; - __le32 OutputBufferLength; - __u8 Buffer[1]; -} __packed; - -/* - * Maximum number of iovs we need for a set-info request. - * The largest one is rename/hardlink - * [0] : struct smb2_set_info_req + smb2_file_[rename|link]_info - * [1] : path - * [2] : compound padding - */ -#define SMB2_SET_INFO_IOV_SIZE 3 - -struct smb2_set_info_req { - struct smb2_hdr hdr; - __le16 StructureSize; /* Must be 33 */ - __u8 InfoType; - __u8 FileInfoClass; - __le32 BufferLength; - __le16 BufferOffset; - __u16 Reserved; - __le32 AdditionalInformation; - __u64 PersistentFileId; - __u64 VolatileFileId; - __u8 Buffer[1]; -} __packed; - -struct smb2_set_info_rsp { - struct smb2_hdr hdr; - __le16 StructureSize; /* Must be 2 */ -} __packed; - -/* - * SMB2_NOTIFY See MS-SMB2 section 2.2.35 - */ -/* notify flags */ -#define SMB2_WATCH_TREE 0x0001 - -/* notify completion filter flags. See MS-FSCC 2.6 and MS-SMB2 2.2.35 */ -#define FILE_NOTIFY_CHANGE_FILE_NAME 0x00000001 -#define FILE_NOTIFY_CHANGE_DIR_NAME 0x00000002 -#define FILE_NOTIFY_CHANGE_ATTRIBUTES 0x00000004 -#define FILE_NOTIFY_CHANGE_SIZE 0x00000008 -#define FILE_NOTIFY_CHANGE_LAST_WRITE 0x00000010 -#define FILE_NOTIFY_CHANGE_LAST_ACCESS 0x00000020 -#define FILE_NOTIFY_CHANGE_CREATION 0x00000040 -#define FILE_NOTIFY_CHANGE_EA 0x00000080 -#define FILE_NOTIFY_CHANGE_SECURITY 0x00000100 -#define FILE_NOTIFY_CHANGE_STREAM_NAME 0x00000200 -#define FILE_NOTIFY_CHANGE_STREAM_SIZE 0x00000400 -#define FILE_NOTIFY_CHANGE_STREAM_WRITE 0x00000800 - -/* SMB2 Notify Action Flags */ -#define FILE_ACTION_ADDED 0x00000001 -#define FILE_ACTION_REMOVED 0x00000002 -#define FILE_ACTION_MODIFIED 0x00000003 -#define FILE_ACTION_RENAMED_OLD_NAME 0x00000004 -#define FILE_ACTION_RENAMED_NEW_NAME 0x00000005 -#define FILE_ACTION_ADDED_STREAM 0x00000006 -#define FILE_ACTION_REMOVED_STREAM 0x00000007 -#define FILE_ACTION_MODIFIED_STREAM 0x00000008 -#define FILE_ACTION_REMOVED_BY_DELETE 0x00000009 - -struct smb2_change_notify_req { - struct smb2_hdr hdr; - __le16 StructureSize; - __le16 Flags; - __le32 OutputBufferLength; - __u64 PersistentFileId; /* opaque endianness */ - __u64 VolatileFileId; /* opaque endianness */ - __le32 CompletionFilter; - __u32 Reserved; -} __packed; - -struct smb2_change_notify_rsp { - struct smb2_hdr hdr; - __le16 StructureSize; /* Must be 9 */ - __le16 OutputBufferOffset; - __le32 OutputBufferLength; - __u8 Buffer[1]; /* array of file notify structs */ -} __packed; - - -/* - * SMB2_CREATE See MS-SMB2 section 2.2.13 - */ -/* Oplock levels */ -#define SMB2_OPLOCK_LEVEL_NONE 0x00 -#define SMB2_OPLOCK_LEVEL_II 0x01 -#define SMB2_OPLOCK_LEVEL_EXCLUSIVE 0x08 -#define SMB2_OPLOCK_LEVEL_BATCH 0x09 -#define SMB2_OPLOCK_LEVEL_LEASE 0xFF -/* Non-spec internal type */ -#define SMB2_OPLOCK_LEVEL_NOCHANGE 0x99 - -/* Impersonation Levels. See MS-WPO section 9.7 and MSDN-IMPERS */ -#define IL_ANONYMOUS cpu_to_le32(0x00000000) -#define IL_IDENTIFICATION cpu_to_le32(0x00000001) -#define IL_IMPERSONATION cpu_to_le32(0x00000002) -#define IL_DELEGATE cpu_to_le32(0x00000003) - -/* File Attrubutes */ -#define FILE_ATTRIBUTE_READONLY 0x00000001 -#define FILE_ATTRIBUTE_HIDDEN 0x00000002 -#define FILE_ATTRIBUTE_SYSTEM 0x00000004 -#define FILE_ATTRIBUTE_DIRECTORY 0x00000010 -#define FILE_ATTRIBUTE_ARCHIVE 0x00000020 -#define FILE_ATTRIBUTE_NORMAL 0x00000080 -#define FILE_ATTRIBUTE_TEMPORARY 0x00000100 -#define FILE_ATTRIBUTE_SPARSE_FILE 0x00000200 -#define FILE_ATTRIBUTE_REPARSE_POINT 0x00000400 -#define FILE_ATTRIBUTE_COMPRESSED 0x00000800 -#define FILE_ATTRIBUTE_OFFLINE 0x00001000 -#define FILE_ATTRIBUTE_NOT_CONTENT_INDEXED 0x00002000 -#define FILE_ATTRIBUTE_ENCRYPTED 0x00004000 -#define FILE_ATTRIBUTE_INTEGRITY_STREAM 0x00008000 -#define FILE_ATTRIBUTE_NO_SCRUB_DATA 0x00020000 -#define FILE_ATTRIBUTE__MASK 0x00007FB7 - -#define FILE_ATTRIBUTE_READONLY_LE cpu_to_le32(0x00000001) -#define FILE_ATTRIBUTE_HIDDEN_LE cpu_to_le32(0x00000002) -#define FILE_ATTRIBUTE_SYSTEM_LE cpu_to_le32(0x00000004) -#define FILE_ATTRIBUTE_DIRECTORY_LE cpu_to_le32(0x00000010) -#define FILE_ATTRIBUTE_ARCHIVE_LE cpu_to_le32(0x00000020) -#define FILE_ATTRIBUTE_NORMAL_LE cpu_to_le32(0x00000080) -#define FILE_ATTRIBUTE_TEMPORARY_LE cpu_to_le32(0x00000100) -#define FILE_ATTRIBUTE_SPARSE_FILE_LE cpu_to_le32(0x00000200) -#define FILE_ATTRIBUTE_REPARSE_POINT_LE cpu_to_le32(0x00000400) -#define FILE_ATTRIBUTE_COMPRESSED_LE cpu_to_le32(0x00000800) -#define FILE_ATTRIBUTE_OFFLINE_LE cpu_to_le32(0x00001000) -#define FILE_ATTRIBUTE_NOT_CONTENT_INDEXED_LE cpu_to_le32(0x00002000) -#define FILE_ATTRIBUTE_ENCRYPTED_LE cpu_to_le32(0x00004000) -#define FILE_ATTRIBUTE_INTEGRITY_STREAM_LE cpu_to_le32(0x00008000) -#define FILE_ATTRIBUTE_NO_SCRUB_DATA_LE cpu_to_le32(0x00020000) -#define FILE_ATTRIBUTE_MASK_LE cpu_to_le32(0x00007FB7) - -/* Desired Access Flags */ -#define FILE_READ_DATA_LE cpu_to_le32(0x00000001) -#define FILE_LIST_DIRECTORY_LE cpu_to_le32(0x00000001) -#define FILE_WRITE_DATA_LE cpu_to_le32(0x00000002) -#define FILE_APPEND_DATA_LE cpu_to_le32(0x00000004) -#define FILE_ADD_SUBDIRECTORY_LE cpu_to_le32(0x00000004) -#define FILE_READ_EA_LE cpu_to_le32(0x00000008) -#define FILE_WRITE_EA_LE cpu_to_le32(0x00000010) -#define FILE_EXECUTE_LE cpu_to_le32(0x00000020) -#define FILE_DELETE_CHILD_LE cpu_to_le32(0x00000040) -#define FILE_READ_ATTRIBUTES_LE cpu_to_le32(0x00000080) -#define FILE_WRITE_ATTRIBUTES_LE cpu_to_le32(0x00000100) -#define FILE_DELETE_LE cpu_to_le32(0x00010000) -#define FILE_READ_CONTROL_LE cpu_to_le32(0x00020000) -#define FILE_WRITE_DAC_LE cpu_to_le32(0x00040000) -#define FILE_WRITE_OWNER_LE cpu_to_le32(0x00080000) -#define FILE_SYNCHRONIZE_LE cpu_to_le32(0x00100000) -#define FILE_ACCESS_SYSTEM_SECURITY_LE cpu_to_le32(0x01000000) -#define FILE_MAXIMAL_ACCESS_LE cpu_to_le32(0x02000000) -#define FILE_GENERIC_ALL_LE cpu_to_le32(0x10000000) -#define FILE_GENERIC_EXECUTE_LE cpu_to_le32(0x20000000) -#define FILE_GENERIC_WRITE_LE cpu_to_le32(0x40000000) -#define FILE_GENERIC_READ_LE cpu_to_le32(0x80000000) -#define DESIRED_ACCESS_MASK cpu_to_le32(0xF21F01FF) - - -#define FILE_READ_DESIRED_ACCESS_LE (FILE_READ_DATA_LE | \ - FILE_READ_EA_LE | \ - FILE_GENERIC_READ_LE) -#define FILE_WRITE_DESIRE_ACCESS_LE (FILE_WRITE_DATA_LE | \ - FILE_APPEND_DATA_LE | \ - FILE_WRITE_EA_LE | \ - FILE_WRITE_ATTRIBUTES_LE | \ - FILE_GENERIC_WRITE_LE) - -/* ShareAccess Flags */ -#define FILE_SHARE_READ_LE cpu_to_le32(0x00000001) -#define FILE_SHARE_WRITE_LE cpu_to_le32(0x00000002) -#define FILE_SHARE_DELETE_LE cpu_to_le32(0x00000004) -#define FILE_SHARE_ALL_LE cpu_to_le32(0x00000007) - -/* CreateDisposition Flags */ -#define FILE_SUPERSEDE_LE cpu_to_le32(0x00000000) -#define FILE_OPEN_LE cpu_to_le32(0x00000001) -#define FILE_CREATE_LE cpu_to_le32(0x00000002) -#define FILE_OPEN_IF_LE cpu_to_le32(0x00000003) -#define FILE_OVERWRITE_LE cpu_to_le32(0x00000004) -#define FILE_OVERWRITE_IF_LE cpu_to_le32(0x00000005) -#define FILE_CREATE_MASK_LE cpu_to_le32(0x00000007) - -#define FILE_READ_RIGHTS (FILE_READ_DATA | FILE_READ_EA \ - | FILE_READ_ATTRIBUTES) -#define FILE_WRITE_RIGHTS (FILE_WRITE_DATA | FILE_APPEND_DATA \ - | FILE_WRITE_EA | FILE_WRITE_ATTRIBUTES) -#define FILE_EXEC_RIGHTS (FILE_EXECUTE) - -/* CreateOptions Flags */ -#define FILE_DIRECTORY_FILE_LE cpu_to_le32(0x00000001) -/* same as #define CREATE_NOT_FILE_LE cpu_to_le32(0x00000001) */ -#define FILE_WRITE_THROUGH_LE cpu_to_le32(0x00000002) -#define FILE_SEQUENTIAL_ONLY_LE cpu_to_le32(0x00000004) -#define FILE_NO_INTERMEDIATE_BUFFERING_LE cpu_to_le32(0x00000008) -#define FILE_NON_DIRECTORY_FILE_LE cpu_to_le32(0x00000040) -#define FILE_COMPLETE_IF_OPLOCKED_LE cpu_to_le32(0x00000100) -#define FILE_NO_EA_KNOWLEDGE_LE cpu_to_le32(0x00000200) -#define FILE_RANDOM_ACCESS_LE cpu_to_le32(0x00000800) -#define FILE_DELETE_ON_CLOSE_LE cpu_to_le32(0x00001000) -#define FILE_OPEN_BY_FILE_ID_LE cpu_to_le32(0x00002000) -#define FILE_OPEN_FOR_BACKUP_INTENT_LE cpu_to_le32(0x00004000) -#define FILE_NO_COMPRESSION_LE cpu_to_le32(0x00008000) -#define FILE_OPEN_REPARSE_POINT_LE cpu_to_le32(0x00200000) -#define FILE_OPEN_NO_RECALL_LE cpu_to_le32(0x00400000) -#define CREATE_OPTIONS_MASK_LE cpu_to_le32(0x00FFFFFF) - -#define FILE_READ_RIGHTS_LE (FILE_READ_DATA_LE | FILE_READ_EA_LE \ - | FILE_READ_ATTRIBUTES_LE) -#define FILE_WRITE_RIGHTS_LE (FILE_WRITE_DATA_LE | FILE_APPEND_DATA_LE \ - | FILE_WRITE_EA_LE | FILE_WRITE_ATTRIBUTES_LE) -#define FILE_EXEC_RIGHTS_LE (FILE_EXECUTE_LE) - -/* Create Context Values */ -#define SMB2_CREATE_EA_BUFFER "ExtA" /* extended attributes */ -#define SMB2_CREATE_SD_BUFFER "SecD" /* security descriptor */ -#define SMB2_CREATE_DURABLE_HANDLE_REQUEST "DHnQ" -#define SMB2_CREATE_DURABLE_HANDLE_RECONNECT "DHnC" -#define SMB2_CREATE_ALLOCATION_SIZE "AISi" -#define SMB2_CREATE_QUERY_MAXIMAL_ACCESS_REQUEST "MxAc" -#define SMB2_CREATE_TIMEWARP_REQUEST "TWrp" -#define SMB2_CREATE_QUERY_ON_DISK_ID "QFid" -#define SMB2_CREATE_REQUEST_LEASE "RqLs" -#define SMB2_CREATE_DURABLE_HANDLE_REQUEST_V2 "DH2Q" -#define SMB2_CREATE_DURABLE_HANDLE_RECONNECT_V2 "DH2C" -#define SMB2_CREATE_TAG_POSIX "\x93\xAD\x25\x50\x9C\xB4\x11\xE7\xB4\x23\x83\xDE\x96\x8B\xCD\x7C" -#define SMB2_CREATE_APP_INSTANCE_ID "\x45\xBC\xA6\x6A\xEF\xA7\xF7\x4A\x90\x08\xFA\x46\x2E\x14\x4D\x74" -#define SMB2_CREATE_APP_INSTANCE_VERSION "\xB9\x82\xD0\xB7\x3B\x56\x07\x4F\xA0\x7B\x52\x4A\x81\x16\xA0\x10" -#define SVHDX_OPEN_DEVICE_CONTEXT "\x9C\xCB\xCF\x9E\x04\xC1\xE6\x43\x98\x0E\x15\x8D\xA1\xF6\xEC\x83" -#define SMB2_CREATE_TAG_AAPL "AAPL" - -/* Flag (SMB3 open response) values */ -#define SMB2_CREATE_FLAG_REPARSEPOINT 0x01 - -struct create_context { - __le32 Next; - __le16 NameOffset; - __le16 NameLength; - __le16 Reserved; - __le16 DataOffset; - __le32 DataLength; - __u8 Buffer[]; -} __packed; - -struct smb2_create_req { - struct smb2_hdr hdr; - __le16 StructureSize; /* Must be 57 */ - __u8 SecurityFlags; - __u8 RequestedOplockLevel; - __le32 ImpersonationLevel; - __le64 SmbCreateFlags; - __le64 Reserved; - __le32 DesiredAccess; - __le32 FileAttributes; - __le32 ShareAccess; - __le32 CreateDisposition; - __le32 CreateOptions; - __le16 NameOffset; - __le16 NameLength; - __le32 CreateContextsOffset; - __le32 CreateContextsLength; - __u8 Buffer[]; -} __packed; - -struct smb2_create_rsp { - struct smb2_hdr hdr; - __le16 StructureSize; /* Must be 89 */ - __u8 OplockLevel; - __u8 Flags; /* 0x01 if reparse point */ - __le32 CreateAction; - __le64 CreationTime; - __le64 LastAccessTime; - __le64 LastWriteTime; - __le64 ChangeTime; - __le64 AllocationSize; - __le64 EndofFile; - __le32 FileAttributes; - __le32 Reserved2; - __u64 PersistentFileId; - __u64 VolatileFileId; - __le32 CreateContextsOffset; - __le32 CreateContextsLength; - __u8 Buffer[1]; -} __packed; - -struct create_posix { - struct create_context ccontext; - __u8 Name[16]; - __le32 Mode; - __u32 Reserved; -} __packed; - -#define SMB2_LEASE_NONE_LE cpu_to_le32(0x00) -#define SMB2_LEASE_READ_CACHING_LE cpu_to_le32(0x01) -#define SMB2_LEASE_HANDLE_CACHING_LE cpu_to_le32(0x02) -#define SMB2_LEASE_WRITE_CACHING_LE cpu_to_le32(0x04) - -#define SMB2_LEASE_FLAG_BREAK_IN_PROGRESS_LE cpu_to_le32(0x02) - -#define SMB2_LEASE_KEY_SIZE 16 - -struct lease_context { - __u8 LeaseKey[SMB2_LEASE_KEY_SIZE]; - __le32 LeaseState; - __le32 LeaseFlags; - __le64 LeaseDuration; -} __packed; - -struct lease_context_v2 { - __u8 LeaseKey[SMB2_LEASE_KEY_SIZE]; - __le32 LeaseState; - __le32 LeaseFlags; - __le64 LeaseDuration; - __u8 ParentLeaseKey[SMB2_LEASE_KEY_SIZE]; - __le16 Epoch; - __le16 Reserved; -} __packed; - -struct create_lease { - struct create_context ccontext; - __u8 Name[8]; - struct lease_context lcontext; -} __packed; - -struct create_lease_v2 { - struct create_context ccontext; - __u8 Name[8]; - struct lease_context_v2 lcontext; - __u8 Pad[4]; -} __packed; - -/* See MS-SMB2 2.2.31 and 2.2.32 */ -struct smb2_ioctl_req { - struct smb2_hdr hdr; - __le16 StructureSize; /* Must be 57 */ - __le16 Reserved; /* offset from start of SMB2 header to write data */ - __le32 CtlCode; - __u64 PersistentFileId; - __u64 VolatileFileId; - __le32 InputOffset; /* Reserved MBZ */ - __le32 InputCount; - __le32 MaxInputResponse; - __le32 OutputOffset; - __le32 OutputCount; - __le32 MaxOutputResponse; - __le32 Flags; - __le32 Reserved2; - __u8 Buffer[]; -} __packed; - -struct smb2_ioctl_rsp { - struct smb2_hdr hdr; - __le16 StructureSize; /* Must be 49 */ - __le16 Reserved; - __le32 CtlCode; - __u64 PersistentFileId; - __u64 VolatileFileId; - __le32 InputOffset; /* Reserved MBZ */ - __le32 InputCount; - __le32 OutputOffset; - __le32 OutputCount; - __le32 Flags; - __le32 Reserved2; - __u8 Buffer[]; -} __packed; - -/* this goes in the ioctl buffer when doing FSCTL_SET_ZERO_DATA */ -struct file_zero_data_information { - __le64 FileOffset; - __le64 BeyondFinalZero; -} __packed; - -/* See MS-FSCC 2.3.7 */ -struct duplicate_extents_to_file { - __u64 PersistentFileHandle; /* source file handle, opaque endianness */ - __u64 VolatileFileHandle; - __le64 SourceFileOffset; - __le64 TargetFileOffset; - __le64 ByteCount; /* Bytes to be copied */ -} __packed; - -/* See MS-FSCC 2.3.8 */ -#define DUPLICATE_EXTENTS_DATA_EX_SOURCE_ATOMIC 0x00000001 -struct duplicate_extents_to_file_ex { - __u64 PersistentFileHandle; /* source file handle, opaque endianness */ - __u64 VolatileFileHandle; - __le64 SourceFileOffset; - __le64 TargetFileOffset; - __le64 ByteCount; /* Bytes to be copied */ - __le32 Flags; - __le32 Reserved; -} __packed; - - -/* See MS-FSCC 2.3.20 */ -struct fsctl_get_integrity_information_rsp { - __le16 ChecksumAlgorithm; - __le16 Reserved; - __le32 Flags; - __le32 ChecksumChunkSizeInBytes; - __le32 ClusterSizeInBytes; -} __packed; - -/* See MS-FSCC 2.3.55 */ -struct fsctl_query_file_regions_req { - __le64 FileOffset; - __le64 Length; - __le32 DesiredUsage; - __le32 Reserved; -} __packed; - -/* DesiredUsage flags see MS-FSCC 2.3.56.1 */ -#define FILE_USAGE_INVALID_RANGE 0x00000000 -#define FILE_USAGE_VALID_CACHED_DATA 0x00000001 -#define FILE_USAGE_NONCACHED_DATA 0x00000002 - -struct file_region_info { - __le64 FileOffset; - __le64 Length; - __le32 DesiredUsage; - __le32 Reserved; -} __packed; - -/* See MS-FSCC 2.3.56 */ -struct fsctl_query_file_region_rsp { - __le32 Flags; - __le32 TotalRegionEntryCount; - __le32 RegionEntryCount; - __u32 Reserved; - struct file_region_info Regions[]; -} __packed; - -/* See MS-FSCC 2.3.58 */ -struct fsctl_query_on_disk_vol_info_rsp { - __le64 DirectoryCount; - __le64 FileCount; - __le16 FsFormatMajVersion; - __le16 FsFormatMinVersion; - __u8 FsFormatName[24]; - __le64 FormatTime; - __le64 LastUpdateTime; - __u8 CopyrightInfo[68]; - __u8 AbstractInfo[68]; - __u8 FormatImplInfo[68]; - __u8 LastModifyImplInfo[68]; -} __packed; - -/* See MS-FSCC 2.3.73 */ -struct fsctl_set_integrity_information_req { - __le16 ChecksumAlgorithm; - __le16 Reserved; - __le32 Flags; -} __packed; - -/* See MS-FSCC 2.3.75 */ -struct fsctl_set_integrity_info_ex_req { - __u8 EnableIntegrity; - __u8 KeepState; - __u16 Reserved; - __le32 Flags; - __u8 Version; - __u8 Reserved2[7]; -} __packed; - -/* Integrity ChecksumAlgorithm choices for above */ -#define CHECKSUM_TYPE_NONE 0x0000 -#define CHECKSUM_TYPE_CRC64 0x0002 -#define CHECKSUM_TYPE_UNCHANGED 0xFFFF /* set only */ - -/* Integrity flags for above */ -#define FSCTL_INTEGRITY_FLAG_CHECKSUM_ENFORCEMENT_OFF 0x00000001 - -/* Reparse structures - see MS-FSCC 2.1.2 */ - -/* struct fsctl_reparse_info_req is empty, only response structs (see below) */ -struct reparse_data_buffer { - __le32 ReparseTag; - __le16 ReparseDataLength; - __u16 Reserved; - __u8 DataBuffer[]; /* Variable Length */ -} __packed; - -struct reparse_guid_data_buffer { - __le32 ReparseTag; - __le16 ReparseDataLength; - __u16 Reserved; - __u8 ReparseGuid[16]; - __u8 DataBuffer[]; /* Variable Length */ -} __packed; - -struct reparse_mount_point_data_buffer { - __le32 ReparseTag; - __le16 ReparseDataLength; - __u16 Reserved; - __le16 SubstituteNameOffset; - __le16 SubstituteNameLength; - __le16 PrintNameOffset; - __le16 PrintNameLength; - __u8 PathBuffer[]; /* Variable Length */ -} __packed; - -#define SYMLINK_FLAG_RELATIVE 0x00000001 - -struct reparse_symlink_data_buffer { - __le32 ReparseTag; - __le16 ReparseDataLength; - __u16 Reserved; - __le16 SubstituteNameOffset; - __le16 SubstituteNameLength; - __le16 PrintNameOffset; - __le16 PrintNameLength; - __le32 Flags; - __u8 PathBuffer[]; /* Variable Length */ -} __packed; - -/* See MS-FSCC 2.1.2.6 and cifspdu.h for struct reparse_posix_data */ - -struct validate_negotiate_info_req { - __le32 Capabilities; - __u8 Guid[SMB2_CLIENT_GUID_SIZE]; - __le16 SecurityMode; - __le16 DialectCount; - __le16 Dialects[4]; /* BB expand this if autonegotiate > 4 dialects */ -} __packed; - -struct validate_negotiate_info_rsp { - __le32 Capabilities; - __u8 Guid[SMB2_CLIENT_GUID_SIZE]; - __le16 SecurityMode; - __le16 Dialect; /* Dialect in use for the connection */ -} __packed; - - -/* Possible InfoType values */ -#define SMB2_O_INFO_FILE 0x01 -#define SMB2_O_INFO_FILESYSTEM 0x02 -#define SMB2_O_INFO_SECURITY 0x03 -#define SMB2_O_INFO_QUOTA 0x04 - -/* SMB2 Query Info see MS-SMB2 (2.2.37) or MS-DTYP */ - -/* List of QUERY INFO levels (those also valid for QUERY_DIR are noted below */ -#define FILE_DIRECTORY_INFORMATION 1 /* also for QUERY_DIR */ -#define FILE_FULL_DIRECTORY_INFORMATION 2 /* also for QUERY_DIR */ -#define FILE_BOTH_DIRECTORY_INFORMATION 3 /* also for QUERY_DIR */ -#define FILE_BASIC_INFORMATION 4 -#define FILE_STANDARD_INFORMATION 5 -#define FILE_INTERNAL_INFORMATION 6 -#define FILE_EA_INFORMATION 7 -#define FILE_ACCESS_INFORMATION 8 -#define FILE_NAME_INFORMATION 9 -#define FILE_RENAME_INFORMATION 10 -#define FILE_LINK_INFORMATION 11 -#define FILE_NAMES_INFORMATION 12 /* also for QUERY_DIR */ -#define FILE_DISPOSITION_INFORMATION 13 -#define FILE_POSITION_INFORMATION 14 -#define FILE_FULL_EA_INFORMATION 15 -#define FILE_MODE_INFORMATION 16 -#define FILE_ALIGNMENT_INFORMATION 17 -#define FILE_ALL_INFORMATION 18 -#define FILE_ALLOCATION_INFORMATION 19 -#define FILE_END_OF_FILE_INFORMATION 20 -#define FILE_ALTERNATE_NAME_INFORMATION 21 -#define FILE_STREAM_INFORMATION 22 -#define FILE_PIPE_INFORMATION 23 -#define FILE_PIPE_LOCAL_INFORMATION 24 -#define FILE_PIPE_REMOTE_INFORMATION 25 -#define FILE_MAILSLOT_QUERY_INFORMATION 26 -#define FILE_MAILSLOT_SET_INFORMATION 27 -#define FILE_COMPRESSION_INFORMATION 28 -#define FILE_OBJECT_ID_INFORMATION 29 -/* Number 30 not defined in documents */ -#define FILE_MOVE_CLUSTER_INFORMATION 31 -#define FILE_QUOTA_INFORMATION 32 -#define FILE_REPARSE_POINT_INFORMATION 33 -#define FILE_NETWORK_OPEN_INFORMATION 34 -#define FILE_ATTRIBUTE_TAG_INFORMATION 35 -#define FILE_TRACKING_INFORMATION 36 -#define FILEID_BOTH_DIRECTORY_INFORMATION 37 /* also for QUERY_DIR */ -#define FILEID_FULL_DIRECTORY_INFORMATION 38 /* also for QUERY_DIR */ -#define FILE_VALID_DATA_LENGTH_INFORMATION 39 -#define FILE_SHORT_NAME_INFORMATION 40 -#define FILE_SFIO_RESERVE_INFORMATION 44 -#define FILE_SFIO_VOLUME_INFORMATION 45 -#define FILE_HARD_LINK_INFORMATION 46 -#define FILE_NORMALIZED_NAME_INFORMATION 48 -#define FILEID_GLOBAL_TX_DIRECTORY_INFORMATION 50 -#define FILE_STANDARD_LINK_INFORMATION 54 -#define FILE_ID_INFORMATION 59 -#define FILE_ID_EXTD_DIRECTORY_INFORMATION 60 /* also for QUERY_DIR */ -/* Used for Query Info and Find File POSIX Info for SMB3.1.1 and SMB1 */ -#define SMB_FIND_FILE_POSIX_INFO 0x064 - -/* Security info type additionalinfo flags. */ -#define OWNER_SECINFO 0x00000001 -#define GROUP_SECINFO 0x00000002 -#define DACL_SECINFO 0x00000004 -#define SACL_SECINFO 0x00000008 -#define LABEL_SECINFO 0x00000010 -#define ATTRIBUTE_SECINFO 0x00000020 -#define SCOPE_SECINFO 0x00000040 -#define BACKUP_SECINFO 0x00010000 -#define UNPROTECTED_SACL_SECINFO 0x10000000 -#define UNPROTECTED_DACL_SECINFO 0x20000000 -#define PROTECTED_SACL_SECINFO 0x40000000 -#define PROTECTED_DACL_SECINFO 0x80000000 - -/* Flags used for FileFullEAinfo */ -#define SL_RESTART_SCAN 0x00000001 -#define SL_RETURN_SINGLE_ENTRY 0x00000002 -#define SL_INDEX_SPECIFIED 0x00000004 - -struct smb2_query_info_req { - struct smb2_hdr hdr; - __le16 StructureSize; /* Must be 41 */ - __u8 InfoType; - __u8 FileInfoClass; - __le32 OutputBufferLength; - __le16 InputBufferOffset; - __u16 Reserved; - __le32 InputBufferLength; - __le32 AdditionalInformation; - __le32 Flags; - __u64 PersistentFileId; - __u64 VolatileFileId; - __u8 Buffer[1]; -} __packed; - -struct smb2_query_info_rsp { - struct smb2_hdr hdr; - __le16 StructureSize; /* Must be 9 */ - __le16 OutputBufferOffset; - __le32 OutputBufferLength; - __u8 Buffer[1]; -} __packed; - -/* - * PDU query infolevel structure definitions - */ - -/* See MS-FSCC 2.3.52 */ -struct file_allocated_range_buffer { - __le64 file_offset; - __le64 length; -} __packed; - -struct smb2_file_internal_info { - __le64 IndexNumber; -} __packed; /* level 6 Query */ - -struct smb2_file_rename_info { /* encoding of request for level 10 */ - __u8 ReplaceIfExists; /* 1 = replace existing target with new */ - /* 0 = fail if target already exists */ - __u8 Reserved[7]; - __u64 RootDirectory; /* MBZ for network operations (why says spec?) */ - __le32 FileNameLength; - char FileName[]; /* New name to be assigned */ - /* padding - overall struct size must be >= 24 so filename + pad >= 6 */ -} __packed; /* level 10 Set */ - -struct smb2_file_link_info { /* encoding of request for level 11 */ - __u8 ReplaceIfExists; /* 1 = replace existing link with new */ - /* 0 = fail if link already exists */ - __u8 Reserved[7]; - __u64 RootDirectory; /* MBZ for network operations (why says spec?) */ - __le32 FileNameLength; - char FileName[]; /* Name to be assigned to new link */ -} __packed; /* level 11 Set */ - -/* - * This level 18, although with struct with same name is different from cifs - * level 0x107. Level 0x107 has an extra u64 between AccessFlags and - * CurrentByteOffset. - */ -struct smb2_file_all_info { /* data block encoding of response to level 18 */ - __le64 CreationTime; /* Beginning of FILE_BASIC_INFO equivalent */ - __le64 LastAccessTime; - __le64 LastWriteTime; - __le64 ChangeTime; - __le32 Attributes; - __u32 Pad1; /* End of FILE_BASIC_INFO_INFO equivalent */ - __le64 AllocationSize; /* Beginning of FILE_STANDARD_INFO equivalent */ - __le64 EndOfFile; /* size ie offset to first free byte in file */ - __le32 NumberOfLinks; /* hard links */ - __u8 DeletePending; - __u8 Directory; - __u16 Pad2; /* End of FILE_STANDARD_INFO equivalent */ - __le64 IndexNumber; - __le32 EASize; - __le32 AccessFlags; - __le64 CurrentByteOffset; - __le32 Mode; - __le32 AlignmentRequirement; - __le32 FileNameLength; - char FileName[1]; -} __packed; /* level 18 Query */ - -struct smb2_file_eof_info { /* encoding of request for level 10 */ - __le64 EndOfFile; /* new end of file value */ -} __packed; /* level 20 Set */ - -/* Level 100 query info */ -struct smb311_posix_qinfo { - __le64 CreationTime; - __le64 LastAccessTime; - __le64 LastWriteTime; - __le64 ChangeTime; - __le64 EndOfFile; - __le64 AllocationSize; - __le32 DosAttributes; - __le64 Inode; - __le32 DeviceId; - __le32 Zero; - /* beginning of POSIX Create Context Response */ - __le32 HardLinks; - __le32 ReparseTag; - __le32 Mode; - u8 Sids[]; - /* - * var sized owner SID - * var sized group SID - * le32 filenamelength - * u8 filename[] - */ -} __packed; - -/* File System Information Classes */ -#define FS_VOLUME_INFORMATION 1 /* Query */ -#define FS_LABEL_INFORMATION 2 /* Set */ -#define FS_SIZE_INFORMATION 3 /* Query */ -#define FS_DEVICE_INFORMATION 4 /* Query */ -#define FS_ATTRIBUTE_INFORMATION 5 /* Query */ -#define FS_CONTROL_INFORMATION 6 /* Query, Set */ -#define FS_FULL_SIZE_INFORMATION 7 /* Query */ -#define FS_OBJECT_ID_INFORMATION 8 /* Query, Set */ -#define FS_DRIVER_PATH_INFORMATION 9 /* Query */ -#define FS_SECTOR_SIZE_INFORMATION 11 /* SMB3 or later. Query */ -#define FS_POSIX_INFORMATION 100 /* SMB3.1.1 POSIX. Query */ - -struct smb2_fs_full_size_info { - __le64 TotalAllocationUnits; - __le64 CallerAvailableAllocationUnits; - __le64 ActualAvailableAllocationUnits; - __le32 SectorsPerAllocationUnit; - __le32 BytesPerSector; -} __packed; - -#define SSINFO_FLAGS_ALIGNED_DEVICE 0x00000001 -#define SSINFO_FLAGS_PARTITION_ALIGNED_ON_DEVICE 0x00000002 -#define SSINFO_FLAGS_NO_SEEK_PENALTY 0x00000004 -#define SSINFO_FLAGS_TRIM_ENABLED 0x00000008 - -/* sector size info struct */ -struct smb3_fs_ss_info { - __le32 LogicalBytesPerSector; - __le32 PhysicalBytesPerSectorForAtomicity; - __le32 PhysicalBytesPerSectorForPerf; - __le32 FSEffPhysicalBytesPerSectorForAtomicity; - __le32 Flags; - __le32 ByteOffsetForSectorAlignment; - __le32 ByteOffsetForPartitionAlignment; -} __packed; - -/* File System Control Information */ -struct smb2_fs_control_info { - __le64 FreeSpaceStartFiltering; - __le64 FreeSpaceThreshold; - __le64 FreeSpaceStopFiltering; - __le64 DefaultQuotaThreshold; - __le64 DefaultQuotaLimit; - __le32 FileSystemControlFlags; - __le32 Padding; -} __packed; - -/* volume info struct - see MS-FSCC 2.5.9 */ -#define MAX_VOL_LABEL_LEN 32 -struct smb3_fs_vol_info { - __le64 VolumeCreationTime; - __u32 VolumeSerialNumber; - __le32 VolumeLabelLength; /* includes trailing null */ - __u8 SupportsObjects; /* True if eg like NTFS, supports objects */ - __u8 Reserved; - __u8 VolumeLabel[]; /* variable len */ -} __packed; - -/* See MS-SMB2 2.2.23 through 2.2.25 */ -struct smb2_oplock_break { - struct smb2_hdr hdr; - __le16 StructureSize; /* Must be 24 */ - __u8 OplockLevel; - __u8 Reserved; - __le32 Reserved2; - __u64 PersistentFid; - __u64 VolatileFid; -} __packed; - -#define SMB2_NOTIFY_BREAK_LEASE_FLAG_ACK_REQUIRED cpu_to_le32(0x01) - -struct smb2_lease_break { - struct smb2_hdr hdr; - __le16 StructureSize; /* Must be 44 */ - __le16 Epoch; - __le32 Flags; - __u8 LeaseKey[16]; - __le32 CurrentLeaseState; - __le32 NewLeaseState; - __le32 BreakReason; - __le32 AccessMaskHint; - __le32 ShareMaskHint; -} __packed; - -struct smb2_lease_ack { - struct smb2_hdr hdr; - __le16 StructureSize; /* Must be 36 */ - __le16 Reserved; - __le32 Flags; - __u8 LeaseKey[16]; - __le32 LeaseState; - __le64 LeaseDuration; -} __packed; - -#define OP_BREAK_STRUCT_SIZE_20 24 -#define OP_BREAK_STRUCT_SIZE_21 36 -#endif /* _COMMON_SMB2PDU_H */ diff --git a/fs/smbfs_common/smbfsctl.h b/fs/smbfs_common/smbfsctl.h deleted file mode 100644 index edd7fc2a7921b..0000000000000 --- a/fs/smbfs_common/smbfsctl.h +++ /dev/null @@ -1,170 +0,0 @@ -/* SPDX-License-Identifier: LGPL-2.1+ */ -/* - * SMB, CIFS, SMB2 FSCTL definitions - * - * Copyright (c) International Business Machines Corp., 2002,2013 - * Author(s): Steve French (sfrench@us.ibm.com) - * - */ - -/* IOCTL information */ -/* - * List of ioctl/fsctl function codes that are or could be useful in the - * future to remote clients like cifs or SMB2/SMB3 client. This is probably - * a slightly larger set of fsctls that NTFS local filesystem could handle, - * including the seven below that we do not have struct definitions for. - * Even with protocol definitions for most of these now available, we still - * need to do some experimentation to identify which are practical to do - * remotely. Some of the following, such as the encryption/compression ones - * could be invoked from tools via a specialized hook into the VFS rather - * than via the standard vfs entry points - * - * See MS-SMB2 Section 2.2.31 (last checked September 2021, all of that list are - * below). Additional detail on less common ones can be found in MS-FSCC - * section 2.3. - */ - -#ifndef __SMBFSCTL_H -#define __SMBFSCTL_H - -/* - * FSCTL values are 32 bits and are constructed as - * - */ -/* Device */ -#define FSCTL_DEVICE_DFS (0x0006 << 16) -#define FSCTL_DEVICE_FILE_SYSTEM (0x0009 << 16) -#define FSCTL_DEVICE_NAMED_PIPE (0x0011 << 16) -#define FSCTL_DEVICE_NETWORK_FILE_SYSTEM (0x0014 << 16) -#define FSCTL_DEVICE_MASK 0xffff0000 -/* Access */ -#define FSCTL_DEVICE_ACCESS_FILE_ANY_ACCESS (0x00 << 14) -#define FSCTL_DEVICE_ACCESS_FILE_READ_ACCESS (0x01 << 14) -#define FSCTL_DEVICE_ACCESS_FILE_WRITE_ACCESS (0x02 << 14) -#define FSCTL_DEVICE_ACCESS_FILE_READ_WRITE_ACCESS (0x03 << 14) -#define FSCTL_DEVICE_ACCESS_MASK 0x0000c000 -/* Function */ -#define FSCTL_DEVICE_FUNCTION_MASK 0x00003ffc -/* Method */ -#define FSCTL_DEVICE_METHOD_BUFFERED 0x00 -#define FSCTL_DEVICE_METHOD_IN_DIRECT 0x01 -#define FSCTL_DEVICE_METHOD_OUT_DIRECT 0x02 -#define FSCTL_DEVICE_METHOD_NEITHER 0x03 -#define FSCTL_DEVICE_METHOD_MASK 0x00000003 - - -#define FSCTL_DFS_GET_REFERRALS 0x00060194 -#define FSCTL_DFS_GET_REFERRALS_EX 0x000601B0 -#define FSCTL_REQUEST_OPLOCK_LEVEL_1 0x00090000 -#define FSCTL_REQUEST_OPLOCK_LEVEL_2 0x00090004 -#define FSCTL_REQUEST_BATCH_OPLOCK 0x00090008 -#define FSCTL_LOCK_VOLUME 0x00090018 -#define FSCTL_UNLOCK_VOLUME 0x0009001C -#define FSCTL_IS_PATHNAME_VALID 0x0009002C /* BB add struct */ -#define FSCTL_GET_COMPRESSION 0x0009003C /* BB add struct */ -#define FSCTL_SET_COMPRESSION 0x0009C040 /* BB add struct */ -#define FSCTL_QUERY_FAT_BPB 0x00090058 /* BB add struct */ -/* Verify the next FSCTL number, we had it as 0x00090090 before */ -#define FSCTL_FILESYSTEM_GET_STATS 0x00090060 /* BB add struct */ -#define FSCTL_GET_NTFS_VOLUME_DATA 0x00090064 /* BB add struct */ -#define FSCTL_GET_RETRIEVAL_POINTERS 0x00090073 /* BB add struct */ -#define FSCTL_IS_VOLUME_DIRTY 0x00090078 /* BB add struct */ -#define FSCTL_ALLOW_EXTENDED_DASD_IO 0x00090083 /* BB add struct */ -#define FSCTL_REQUEST_FILTER_OPLOCK 0x0009008C -#define FSCTL_FIND_FILES_BY_SID 0x0009008F /* BB add struct */ -#define FSCTL_SET_OBJECT_ID 0x00090098 /* BB add struct */ -#define FSCTL_GET_OBJECT_ID 0x0009009C /* BB add struct */ -#define FSCTL_DELETE_OBJECT_ID 0x000900A0 /* BB add struct */ -#define FSCTL_SET_REPARSE_POINT 0x000900A4 /* BB add struct */ -#define FSCTL_GET_REPARSE_POINT 0x000900A8 /* BB add struct */ -#define FSCTL_DELETE_REPARSE_POINT 0x000900AC /* BB add struct */ -#define FSCTL_SET_OBJECT_ID_EXTENDED 0x000900BC /* BB add struct */ -#define FSCTL_CREATE_OR_GET_OBJECT_ID 0x000900C0 /* BB add struct */ -#define FSCTL_SET_SPARSE 0x000900C4 /* BB add struct */ -#define FSCTL_SET_ZERO_DATA 0x000980C8 -#define FSCTL_SET_ENCRYPTION 0x000900D7 /* BB add struct */ -#define FSCTL_ENCRYPTION_FSCTL_IO 0x000900DB /* BB add struct */ -#define FSCTL_WRITE_RAW_ENCRYPTED 0x000900DF /* BB add struct */ -#define FSCTL_READ_RAW_ENCRYPTED 0x000900E3 /* BB add struct */ -#define FSCTL_READ_FILE_USN_DATA 0x000900EB /* BB add struct */ -#define FSCTL_WRITE_USN_CLOSE_RECORD 0x000900EF /* BB add struct */ -#define FSCTL_MARK_HANDLE 0x000900FC /* BB add struct */ -#define FSCTL_SIS_COPYFILE 0x00090100 /* BB add struct */ -#define FSCTL_RECALL_FILE 0x00090117 /* BB add struct */ -#define FSCTL_QUERY_SPARING_INFO 0x00090138 /* BB add struct */ -#define FSCTL_QUERY_ON_DISK_VOLUME_INFO 0x0009013C -#define FSCTL_SET_ZERO_ON_DEALLOC 0x00090194 /* BB add struct */ -#define FSCTL_SET_SHORT_NAME_BEHAVIOR 0x000901B4 /* BB add struct */ -#define FSCTL_GET_INTEGRITY_INFORMATION 0x0009027C -#define FSCTL_QUERY_FILE_REGIONS 0x00090284 -#define FSCTL_GET_REFS_VOLUME_DATA 0x000902D8 /* See MS-FSCC 2.3.24 */ -#define FSCTL_SET_INTEGRITY_INFORMATION_EXT 0x00090380 -#define FSCTL_GET_RETRIEVAL_POINTERS_AND_REFCOUNT 0x000903d3 -#define FSCTL_GET_RETRIEVAL_POINTER_COUNT 0x0009042b -#define FSCTL_REFS_STREAM_SNAPSHOT_MANAGEMENT 0x00090440 -#define FSCTL_QUERY_ALLOCATED_RANGES 0x000940CF -#define FSCTL_OFFLOAD_READ 0x00094264 /* BB add struct */ -#define FSCTL_OFFLOAD_WRITE 0x00098268 /* BB add struct */ -#define FSCTL_SET_DEFECT_MANAGEMENT 0x00098134 /* BB add struct */ -#define FSCTL_FILE_LEVEL_TRIM 0x00098208 /* BB add struct */ -#define FSCTL_DUPLICATE_EXTENTS_TO_FILE 0x00098344 -#define FSCTL_DUPLICATE_EXTENTS_TO_FILE_EX 0x000983E8 -#define FSCTL_SIS_LINK_FILES 0x0009C104 -#define FSCTL_SET_INTEGRITY_INFORMATION 0x0009C280 -#define FSCTL_PIPE_PEEK 0x0011400C /* BB add struct */ -#define FSCTL_PIPE_TRANSCEIVE 0x0011C017 /* BB add struct */ -/* strange that the number for this op is not sequential with previous op */ -#define FSCTL_PIPE_WAIT 0x00110018 /* BB add struct */ -/* Enumerate previous versions of a file */ -#define FSCTL_SRV_ENUMERATE_SNAPSHOTS 0x00144064 -/* Retrieve an opaque file reference for server-side data movement ie copy */ -#define FSCTL_SRV_REQUEST_RESUME_KEY 0x00140078 -#define FSCTL_LMR_REQUEST_RESILIENCY 0x001401D4 -#define FSCTL_LMR_GET_LINK_TRACK_INF 0x001400E8 /* BB add struct */ -#define FSCTL_LMR_SET_LINK_TRACK_INF 0x001400EC /* BB add struct */ -#define FSCTL_VALIDATE_NEGOTIATE_INFO 0x00140204 -/* Perform server-side data movement */ -#define FSCTL_SRV_COPYCHUNK 0x001440F2 -#define FSCTL_SRV_COPYCHUNK_WRITE 0x001480F2 -#define FSCTL_QUERY_NETWORK_INTERFACE_INFO 0x001401FC /* BB add struct */ -#define FSCTL_SRV_READ_HASH 0x001441BB /* BB add struct */ - -/* See FSCC 2.1.2.5 */ -#define IO_REPARSE_TAG_MOUNT_POINT 0xA0000003 -#define IO_REPARSE_TAG_HSM 0xC0000004 -#define IO_REPARSE_TAG_SIS 0x80000007 -#define IO_REPARSE_TAG_HSM2 0x80000006 -#define IO_REPARSE_TAG_DRIVER_EXTENDER 0x80000005 -/* Used by the DFS filter. See MS-DFSC */ -#define IO_REPARSE_TAG_DFS 0x8000000A -/* Used by the DFS filter See MS-DFSC */ -#define IO_REPARSE_TAG_DFSR 0x80000012 -#define IO_REPARSE_TAG_FILTER_MANAGER 0x8000000B -/* See section MS-FSCC 2.1.2.4 */ -#define IO_REPARSE_TAG_SYMLINK 0xA000000C -#define IO_REPARSE_TAG_DEDUP 0x80000013 -#define IO_REPARSE_APPXSTREAM 0xC0000014 -/* NFS symlinks, Win 8/SMB3 and later */ -#define IO_REPARSE_TAG_NFS 0x80000014 -/* - * AzureFileSync - see - * https://docs.microsoft.com/en-us/azure/storage/files/storage-sync-cloud-tiering - */ -#define IO_REPARSE_TAG_AZ_FILE_SYNC 0x8000001e -/* WSL reparse tags */ -#define IO_REPARSE_TAG_LX_SYMLINK 0xA000001D -#define IO_REPARSE_TAG_AF_UNIX 0x80000023 -#define IO_REPARSE_TAG_LX_FIFO 0x80000024 -#define IO_REPARSE_TAG_LX_CHR 0x80000025 -#define IO_REPARSE_TAG_LX_BLK 0x80000026 - -#define IO_REPARSE_TAG_LX_SYMLINK_LE cpu_to_le32(0xA000001D) -#define IO_REPARSE_TAG_AF_UNIX_LE cpu_to_le32(0x80000023) -#define IO_REPARSE_TAG_LX_FIFO_LE cpu_to_le32(0x80000024) -#define IO_REPARSE_TAG_LX_CHR_LE cpu_to_le32(0x80000025) -#define IO_REPARSE_TAG_LX_BLK_LE cpu_to_le32(0x80000026) - -/* fsctl flags */ -/* If Flags is set to this value, the request is an FSCTL not ioctl request */ -#define SMB2_0_IOCTL_IS_FSCTL 0x00000001 -#endif /* __SMBFSCTL_H */ diff --git a/include/acpi/acpixf.h b/include/acpi/acpixf.h index 67c0b9e734b64..911bab2998e22 100644 --- a/include/acpi/acpixf.h +++ b/include/acpi/acpixf.h @@ -751,6 +751,7 @@ ACPI_HW_DEPENDENT_RETURN_STATUS(acpi_status acpi_event_status *event_status)) ACPI_HW_DEPENDENT_RETURN_UINT32(u32 acpi_dispatch_gpe(acpi_handle gpe_device, u32 gpe_number)) +ACPI_HW_DEPENDENT_RETURN_STATUS(acpi_status acpi_hw_disable_all_gpes(void)) ACPI_HW_DEPENDENT_RETURN_STATUS(acpi_status acpi_disable_all_gpes(void)) ACPI_HW_DEPENDENT_RETURN_STATUS(acpi_status acpi_enable_all_runtime_gpes(void)) ACPI_HW_DEPENDENT_RETURN_STATUS(acpi_status acpi_enable_all_wakeup_gpes(void)) diff --git a/include/acpi/actbl3.h b/include/acpi/actbl3.h index 7b9571e00cc4b..832c6464f063f 100644 --- a/include/acpi/actbl3.h +++ b/include/acpi/actbl3.h @@ -443,6 +443,7 @@ struct acpi_tpm2_phy { #define ACPI_TPM2_RESERVED10 10 #define ACPI_TPM2_COMMAND_BUFFER_WITH_ARM_SMC 11 /* V1.2 Rev 8 */ #define ACPI_TPM2_RESERVED 12 +#define ACPI_TPM2_COMMAND_BUFFER_WITH_PLUTON 13 /* Optional trailer appears after any start_method subtables */ diff --git a/include/linux/gpio/driver.h b/include/linux/gpio/driver.h index 6aeea1071b1b2..78bcb1639999e 100644 --- a/include/linux/gpio/driver.h +++ b/include/linux/gpio/driver.h @@ -244,6 +244,14 @@ struct gpio_irq_chip { */ bool initialized; + /** + * @domain_is_allocated_externally: + * + * True it the irq_domain was allocated outside of gpiolib, in which + * case gpiolib won't free the irq_domain itself. + */ + bool domain_is_allocated_externally; + /** * @init_hw: optional routine to initialize hardware before * an IRQ chip will be added. This is quite useful when diff --git a/include/linux/libata.h b/include/linux/libata.h index fe990176e6ee9..9713f4d8f15f4 100644 --- a/include/linux/libata.h +++ b/include/linux/libata.h @@ -834,7 +834,7 @@ struct ata_port { struct mutex scsi_scan_mutex; struct delayed_work hotplug_task; - struct work_struct scsi_rescan_task; + struct delayed_work scsi_rescan_task; unsigned int hsm_task_state; diff --git a/include/linux/regulator/pca9450.h b/include/linux/regulator/pca9450.h index 3c01c2bf84f53..505c908dbb817 100644 --- a/include/linux/regulator/pca9450.h +++ b/include/linux/regulator/pca9450.h @@ -196,11 +196,11 @@ enum { /* PCA9450_REG_LDO3_VOLT bits */ #define LDO3_EN_MASK 0xC0 -#define LDO3OUT_MASK 0x0F +#define LDO3OUT_MASK 0x1F /* PCA9450_REG_LDO4_VOLT bits */ #define LDO4_EN_MASK 0xC0 -#define LDO4OUT_MASK 0x0F +#define LDO4OUT_MASK 0x1F /* PCA9450_REG_LDO5_VOLT bits */ #define LDO5L_EN_MASK 0xC0 diff --git a/include/net/netfilter/nf_tables.h b/include/net/netfilter/nf_tables.h index 22e96b7e1b44a..f3a37cacb32c3 100644 --- a/include/net/netfilter/nf_tables.h +++ b/include/net/netfilter/nf_tables.h @@ -467,7 +467,8 @@ struct nft_set_ops { int (*init)(const struct nft_set *set, const struct nft_set_desc *desc, const struct nlattr * const nla[]); - void (*destroy)(const struct nft_set *set); + void (*destroy)(const struct nft_ctx *ctx, + const struct nft_set *set); void (*gc_init)(const struct nft_set *set); unsigned int elemsize; @@ -804,6 +805,8 @@ int nft_set_elem_expr_clone(const struct nft_ctx *ctx, struct nft_set *set, struct nft_expr *expr_array[]); void nft_set_elem_destroy(const struct nft_set *set, void *elem, bool destroy_expr); +void nf_tables_set_elem_destroy(const struct nft_ctx *ctx, + const struct nft_set *set, void *elem); /** * struct nft_set_gc_batch_head - nf_tables set garbage collection batch @@ -895,6 +898,7 @@ struct nft_expr_type { enum nft_trans_phase { NFT_TRANS_PREPARE, + NFT_TRANS_PREPARE_ERROR, NFT_TRANS_ABORT, NFT_TRANS_COMMIT, NFT_TRANS_RELEASE @@ -1002,7 +1006,10 @@ static inline struct nft_userdata *nft_userdata(const struct nft_rule *rule) return (void *)&rule->data[rule->dlen]; } -void nf_tables_rule_release(const struct nft_ctx *ctx, struct nft_rule *rule); +void nft_rule_expr_activate(const struct nft_ctx *ctx, struct nft_rule *rule); +void nft_rule_expr_deactivate(const struct nft_ctx *ctx, struct nft_rule *rule, + enum nft_trans_phase phase); +void nf_tables_rule_destroy(const struct nft_ctx *ctx, struct nft_rule *rule); static inline void nft_set_elem_update_expr(const struct nft_set_ext *ext, struct nft_regs *regs, @@ -1085,6 +1092,8 @@ int nft_setelem_validate(const struct nft_ctx *ctx, struct nft_set *set, const struct nft_set_iter *iter, struct nft_set_elem *elem); int nft_set_catchall_validate(const struct nft_ctx *ctx, struct nft_set *set); +int nf_tables_bind_chain(const struct nft_ctx *ctx, struct nft_chain *chain); +void nf_tables_unbind_chain(const struct nft_ctx *ctx, struct nft_chain *chain); enum nft_chain_types { NFT_CHAIN_T_DEFAULT = 0, @@ -1121,11 +1130,17 @@ int nft_chain_validate_dependency(const struct nft_chain *chain, int nft_chain_validate_hooks(const struct nft_chain *chain, unsigned int hook_flags); +static inline bool nft_chain_binding(const struct nft_chain *chain) +{ + return chain->flags & NFT_CHAIN_BINDING; +} + static inline bool nft_chain_is_bound(struct nft_chain *chain) { return (chain->flags & NFT_CHAIN_BINDING) && chain->bound; } +int nft_chain_add(struct nft_table *table, struct nft_chain *chain); void nft_chain_del(struct nft_chain *chain); void nf_tables_chain_destroy(struct nft_ctx *ctx); @@ -1543,6 +1558,7 @@ static inline void nft_set_elem_clear_busy(struct nft_set_ext *ext) * struct nft_trans - nf_tables object update in transaction * * @list: used internally + * @binding_list: list of objects with possible bindings * @msg_type: message type * @put_net: ctx->net needs to be put * @ctx: transaction context @@ -1550,6 +1566,7 @@ static inline void nft_set_elem_clear_busy(struct nft_set_ext *ext) */ struct nft_trans { struct list_head list; + struct list_head binding_list; int msg_type; bool put_net; struct nft_ctx ctx; @@ -1560,6 +1577,7 @@ struct nft_trans_rule { struct nft_rule *rule; struct nft_flow_rule *flow; u32 rule_id; + bool bound; }; #define nft_trans_rule(trans) \ @@ -1568,6 +1586,8 @@ struct nft_trans_rule { (((struct nft_trans_rule *)trans->data)->flow) #define nft_trans_rule_id(trans) \ (((struct nft_trans_rule *)trans->data)->rule_id) +#define nft_trans_rule_bound(trans) \ + (((struct nft_trans_rule *)trans->data)->bound) struct nft_trans_set { struct nft_set *set; @@ -1592,13 +1612,17 @@ struct nft_trans_set { (((struct nft_trans_set *)trans->data)->gc_int) struct nft_trans_chain { + struct nft_chain *chain; bool update; char *name; struct nft_stats __percpu *stats; u8 policy; + bool bound; u32 chain_id; }; +#define nft_trans_chain(trans) \ + (((struct nft_trans_chain *)trans->data)->chain) #define nft_trans_chain_update(trans) \ (((struct nft_trans_chain *)trans->data)->update) #define nft_trans_chain_name(trans) \ @@ -1607,6 +1631,8 @@ struct nft_trans_chain { (((struct nft_trans_chain *)trans->data)->stats) #define nft_trans_chain_policy(trans) \ (((struct nft_trans_chain *)trans->data)->policy) +#define nft_trans_chain_bound(trans) \ + (((struct nft_trans_chain *)trans->data)->bound) #define nft_trans_chain_id(trans) \ (((struct nft_trans_chain *)trans->data)->chain_id) @@ -1679,6 +1705,7 @@ static inline int nft_request_module(struct net *net, const char *fmt, ...) { re struct nftables_pernet { struct list_head tables; struct list_head commit_list; + struct list_head binding_list; struct list_head module_list; struct list_head notify_list; struct mutex commit_mutex; diff --git a/include/net/xfrm.h b/include/net/xfrm.h index dbc81f5eb5538..9ec6f2e92ad3a 100644 --- a/include/net/xfrm.h +++ b/include/net/xfrm.h @@ -1039,6 +1039,7 @@ struct xfrm_offload { struct sec_path { int len; int olen; + int verified_cnt; struct xfrm_state *xvec[XFRM_MAX_DEPTH]; struct xfrm_offload ovec[XFRM_MAX_OFFLOAD_DEPTH]; diff --git a/include/trace/events/writeback.h b/include/trace/events/writeback.h index 86b2a82da546a..54e353c9f919f 100644 --- a/include/trace/events/writeback.h +++ b/include/trace/events/writeback.h @@ -68,7 +68,7 @@ DECLARE_EVENT_CLASS(writeback_folio_template, strscpy_pad(__entry->name, bdi_dev_name(mapping ? inode_to_bdi(mapping->host) : NULL), 32); - __entry->ino = mapping ? mapping->host->i_ino : 0; + __entry->ino = (mapping && mapping->host) ? mapping->host->i_ino : 0; __entry->index = folio->index; ), diff --git a/io_uring/net.c b/io_uring/net.c index 05792a6899944..2b44126a876ef 100644 --- a/io_uring/net.c +++ b/io_uring/net.c @@ -190,7 +190,7 @@ static int io_sendmsg_copy_hdr(struct io_kiocb *req, ret = sendmsg_copy_msghdr(&iomsg->msg, sr->umsg, sr->msg_flags, &iomsg->free_iov); /* save msg_control as sys_sendmsg() overwrites it */ - sr->msg_control = iomsg->msg.msg_control; + sr->msg_control = iomsg->msg.msg_control_user; return ret; } @@ -289,7 +289,7 @@ int io_sendmsg(struct io_kiocb *req, unsigned int issue_flags) if (req_has_async_data(req)) { kmsg = req->async_data; - kmsg->msg.msg_control = sr->msg_control; + kmsg->msg.msg_control_user = sr->msg_control; } else { ret = io_sendmsg_copy_hdr(req, &iomsg); if (ret) @@ -313,6 +313,8 @@ int io_sendmsg(struct io_kiocb *req, unsigned int issue_flags) if (ret == -EAGAIN && (issue_flags & IO_URING_F_NONBLOCK)) return io_setup_async_msg(req, kmsg, issue_flags); if (ret > 0 && io_net_retry(sock, flags)) { + kmsg->msg.msg_controllen = 0; + kmsg->msg.msg_control = NULL; sr->done_io += ret; req->flags |= REQ_F_PARTIAL_IO; return io_setup_async_msg(req, kmsg, issue_flags); @@ -779,16 +781,19 @@ retry_multishot: flags = sr->msg_flags; if (force_nonblock) flags |= MSG_DONTWAIT; - if (flags & MSG_WAITALL) - min_ret = iov_iter_count(&kmsg->msg.msg_iter); kmsg->msg.msg_get_inq = 1; - if (req->flags & REQ_F_APOLL_MULTISHOT) + if (req->flags & REQ_F_APOLL_MULTISHOT) { ret = io_recvmsg_multishot(sock, sr, kmsg, flags, &mshot_finished); - else + } else { + /* disable partial retry for recvmsg with cmsg attached */ + if (flags & MSG_WAITALL && !kmsg->msg.msg_controllen) + min_ret = iov_iter_count(&kmsg->msg.msg_iter); + ret = __sys_recvmsg_sock(sock, &kmsg->msg, sr->umsg, kmsg->uaddr, flags); + } if (ret < min_ret) { if (ret == -EAGAIN && force_nonblock) { diff --git a/io_uring/poll.c b/io_uring/poll.c index 4788073ec45d2..869e1d2a44139 100644 --- a/io_uring/poll.c +++ b/io_uring/poll.c @@ -993,8 +993,9 @@ int io_poll_remove(struct io_kiocb *req, unsigned int issue_flags) struct io_hash_bucket *bucket; struct io_kiocb *preq; int ret2, ret = 0; - bool locked; + bool locked = true; + io_ring_submit_lock(ctx, issue_flags); preq = io_poll_find(ctx, true, &cd, &ctx->cancel_table, &bucket); ret2 = io_poll_disarm(preq); if (bucket) @@ -1006,12 +1007,10 @@ int io_poll_remove(struct io_kiocb *req, unsigned int issue_flags) goto out; } - io_ring_submit_lock(ctx, issue_flags); preq = io_poll_find(ctx, true, &cd, &ctx->cancel_table_locked, &bucket); ret2 = io_poll_disarm(preq); if (bucket) spin_unlock(&bucket->lock); - io_ring_submit_unlock(ctx, issue_flags); if (ret2) { ret = ret2; goto out; @@ -1035,7 +1034,7 @@ found: if (poll_update->update_user_data) preq->cqe.user_data = poll_update->new_user_data; - ret2 = io_poll_add(preq, issue_flags); + ret2 = io_poll_add(preq, issue_flags & ~IO_URING_F_UNLOCKED); /* successfully updated, don't complete poll request */ if (!ret2 || ret2 == -EIOCBQUEUED) goto out; @@ -1043,9 +1042,9 @@ found: req_set_fail(preq); io_req_set_res(preq, -ECANCELED, 0); - locked = !(issue_flags & IO_URING_F_UNLOCKED); io_req_task_complete(preq, &locked); out: + io_ring_submit_unlock(ctx, issue_flags); if (ret < 0) { req_set_fail(req); return ret; diff --git a/kernel/bpf/btf.c b/kernel/bpf/btf.c index a8838a32f750e..8220caa488c54 100644 --- a/kernel/bpf/btf.c +++ b/kernel/bpf/btf.c @@ -735,13 +735,12 @@ static bool btf_name_offset_valid(const struct btf *btf, u32 offset) return offset < btf->hdr.str_len; } -static bool __btf_name_char_ok(char c, bool first, bool dot_ok) +static bool __btf_name_char_ok(char c, bool first) { if ((first ? !isalpha(c) : !isalnum(c)) && c != '_' && - ((c == '.' && !dot_ok) || - c != '.')) + c != '.') return false; return true; } @@ -758,20 +757,20 @@ static const char *btf_str_by_offset(const struct btf *btf, u32 offset) return NULL; } -static bool __btf_name_valid(const struct btf *btf, u32 offset, bool dot_ok) +static bool __btf_name_valid(const struct btf *btf, u32 offset) { /* offset must be valid */ const char *src = btf_str_by_offset(btf, offset); const char *src_limit; - if (!__btf_name_char_ok(*src, true, dot_ok)) + if (!__btf_name_char_ok(*src, true)) return false; /* set a limit on identifier length */ src_limit = src + KSYM_NAME_LEN; src++; while (*src && src < src_limit) { - if (!__btf_name_char_ok(*src, false, dot_ok)) + if (!__btf_name_char_ok(*src, false)) return false; src++; } @@ -779,17 +778,14 @@ static bool __btf_name_valid(const struct btf *btf, u32 offset, bool dot_ok) return !*src; } -/* Only C-style identifier is permitted. This can be relaxed if - * necessary. - */ static bool btf_name_valid_identifier(const struct btf *btf, u32 offset) { - return __btf_name_valid(btf, offset, false); + return __btf_name_valid(btf, offset); } static bool btf_name_valid_section(const struct btf *btf, u32 offset) { - return __btf_name_valid(btf, offset, true); + return __btf_name_valid(btf, offset); } static const char *__btf_name_by_offset(const struct btf *btf, u32 offset) @@ -4044,7 +4040,7 @@ static s32 btf_var_check_meta(struct btf_verifier_env *env, } if (!t->name_off || - !__btf_name_valid(env->btf, t->name_off, true)) { + !__btf_name_valid(env->btf, t->name_off)) { btf_verifier_log_type(env, t, "Invalid name"); return -EINVAL; } diff --git a/kernel/bpf/syscall.c b/kernel/bpf/syscall.c index 6c61dba26f4d9..8633ec4f92df3 100644 --- a/kernel/bpf/syscall.c +++ b/kernel/bpf/syscall.c @@ -3383,6 +3383,11 @@ static int bpf_prog_attach_check_attach_type(const struct bpf_prog *prog, return prog->enforce_expected_attach_type && prog->expected_attach_type != attach_type ? -EINVAL : 0; + case BPF_PROG_TYPE_KPROBE: + if (prog->expected_attach_type == BPF_TRACE_KPROBE_MULTI && + attach_type != BPF_TRACE_KPROBE_MULTI) + return -EINVAL; + return 0; default: return 0; } diff --git a/kernel/bpf/verifier.c b/kernel/bpf/verifier.c index 280e689517e10..49c6b5e0855cd 100644 --- a/kernel/bpf/verifier.c +++ b/kernel/bpf/verifier.c @@ -3061,6 +3061,11 @@ static void save_register_state(struct bpf_func_state *state, scrub_spilled_slot(&state->stack[spi].slot_type[i - 1]); } +static bool is_bpf_st_mem(struct bpf_insn *insn) +{ + return BPF_CLASS(insn->code) == BPF_ST && BPF_MODE(insn->code) == BPF_MEM; +} + /* check_stack_{read,write}_fixed_off functions track spill/fill of registers, * stack boundary and alignment are checked in check_mem_access() */ @@ -3072,8 +3077,9 @@ static int check_stack_write_fixed_off(struct bpf_verifier_env *env, { struct bpf_func_state *cur; /* state of the current function */ int i, slot = -off - 1, spi = slot / BPF_REG_SIZE, err; - u32 dst_reg = env->prog->insnsi[insn_idx].dst_reg; + struct bpf_insn *insn = &env->prog->insnsi[insn_idx]; struct bpf_reg_state *reg = NULL; + u32 dst_reg = insn->dst_reg; err = grow_stack_state(state, round_up(slot + 1, BPF_REG_SIZE)); if (err) @@ -3122,6 +3128,16 @@ static int check_stack_write_fixed_off(struct bpf_verifier_env *env, return err; } save_register_state(state, spi, reg, size); + /* Break the relation on a narrowing spill. */ + if (fls64(reg->umax_value) > BITS_PER_BYTE * size) + state->stack[spi].spilled_ptr.id = 0; + } else if (!reg && !(off % BPF_REG_SIZE) && is_bpf_st_mem(insn) && + insn->imm != 0 && env->bpf_capable) { + struct bpf_reg_state fake_reg = {}; + + __mark_reg_known(&fake_reg, (u32)insn->imm); + fake_reg.type = SCALAR_VALUE; + save_register_state(state, spi, &fake_reg, size); } else if (reg && is_spillable_regtype(reg->type)) { /* register containing pointer is being spilled into stack */ if (size != BPF_REG_SIZE) { @@ -3156,7 +3172,8 @@ static int check_stack_write_fixed_off(struct bpf_verifier_env *env, state->stack[spi].spilled_ptr.live |= REG_LIVE_WRITTEN; /* when we zero initialize stack slots mark them as such */ - if (reg && register_is_null(reg)) { + if ((reg && register_is_null(reg)) || + (!reg && is_bpf_st_mem(insn) && insn->imm == 0)) { /* backtracking doesn't work for STACK_ZERO yet. */ err = mark_chain_precision(env, value_regno); if (err) @@ -13819,9 +13836,10 @@ static int jit_subprogs(struct bpf_verifier_env *env) } /* finally lock prog and jit images for all functions and - * populate kallsysm + * populate kallsysm. Begin at the first subprogram, since + * bpf_prog_load will add the kallsyms for the main program. */ - for (i = 0; i < env->subprog_cnt; i++) { + for (i = 1; i < env->subprog_cnt; i++) { bpf_prog_lock_ro(func[i]); bpf_prog_kallsyms_add(func[i]); } @@ -13847,6 +13865,8 @@ static int jit_subprogs(struct bpf_verifier_env *env) prog->jited = 1; prog->bpf_func = func[0]->bpf_func; prog->jited_len = func[0]->jited_len; + prog->aux->extable = func[0]->aux->extable; + prog->aux->num_exentries = func[0]->aux->num_exentries; prog->aux->func = func; prog->aux->func_cnt = env->subprog_cnt; bpf_prog_jit_attempt_done(prog); diff --git a/kernel/cgroup/cgroup.c b/kernel/cgroup/cgroup.c index 36c95626afecc..2380c4daef33d 100644 --- a/kernel/cgroup/cgroup.c +++ b/kernel/cgroup/cgroup.c @@ -1782,7 +1782,7 @@ int rebind_subsystems(struct cgroup_root *dst_root, u16 ss_mask) { struct cgroup *dcgrp = &dst_root->cgrp; struct cgroup_subsys *ss; - int ssid, i, ret; + int ssid, ret; u16 dfl_disable_ss_mask = 0; lockdep_assert_held(&cgroup_mutex); @@ -1826,7 +1826,8 @@ int rebind_subsystems(struct cgroup_root *dst_root, u16 ss_mask) struct cgroup_root *src_root = ss->root; struct cgroup *scgrp = &src_root->cgrp; struct cgroup_subsys_state *css = cgroup_css(scgrp, ss); - struct css_set *cset; + struct css_set *cset, *cset_pos; + struct css_task_iter *it; WARN_ON(!css || cgroup_css(dcgrp, ss)); @@ -1844,9 +1845,22 @@ int rebind_subsystems(struct cgroup_root *dst_root, u16 ss_mask) css->cgroup = dcgrp; spin_lock_irq(&css_set_lock); - hash_for_each(css_set_table, i, cset, hlist) + WARN_ON(!list_empty(&dcgrp->e_csets[ss->id])); + list_for_each_entry_safe(cset, cset_pos, &scgrp->e_csets[ss->id], + e_cset_node[ss->id]) { list_move_tail(&cset->e_cset_node[ss->id], &dcgrp->e_csets[ss->id]); + /* + * all css_sets of scgrp together in same order to dcgrp, + * patch in-flight iterators to preserve correct iteration. + * since the iterator is always advanced right away and + * finished when it->cset_pos meets it->cset_head, so only + * update it->cset_head is enough here. + */ + list_for_each_entry(it, &cset->task_iters, iters_node) + if (it->cset_head == &scgrp->e_csets[ss->id]) + it->cset_head = &dcgrp->e_csets[ss->id]; + } spin_unlock_irq(&css_set_lock); if (ss->css_rstat_flush) { diff --git a/kernel/cgroup/legacy_freezer.c b/kernel/cgroup/legacy_freezer.c index 936473203a6b5..122dacb3a4439 100644 --- a/kernel/cgroup/legacy_freezer.c +++ b/kernel/cgroup/legacy_freezer.c @@ -108,16 +108,18 @@ static int freezer_css_online(struct cgroup_subsys_state *css) struct freezer *freezer = css_freezer(css); struct freezer *parent = parent_freezer(freezer); + cpus_read_lock(); mutex_lock(&freezer_mutex); freezer->state |= CGROUP_FREEZER_ONLINE; if (parent && (parent->state & CGROUP_FREEZING)) { freezer->state |= CGROUP_FREEZING_PARENT | CGROUP_FROZEN; - static_branch_inc(&freezer_active); + static_branch_inc_cpuslocked(&freezer_active); } mutex_unlock(&freezer_mutex); + cpus_read_unlock(); return 0; } @@ -132,14 +134,16 @@ static void freezer_css_offline(struct cgroup_subsys_state *css) { struct freezer *freezer = css_freezer(css); + cpus_read_lock(); mutex_lock(&freezer_mutex); if (freezer->state & CGROUP_FREEZING) - static_branch_dec(&freezer_active); + static_branch_dec_cpuslocked(&freezer_active); freezer->state = 0; mutex_unlock(&freezer_mutex); + cpus_read_unlock(); } static void freezer_css_free(struct cgroup_subsys_state *css) diff --git a/kernel/time/tick-common.c b/kernel/time/tick-common.c index 65b8658da829e..e9138cd7a0f52 100644 --- a/kernel/time/tick-common.c +++ b/kernel/time/tick-common.c @@ -218,19 +218,8 @@ static void tick_setup_device(struct tick_device *td, * this cpu: */ if (tick_do_timer_cpu == TICK_DO_TIMER_BOOT) { - ktime_t next_p; - u32 rem; - tick_do_timer_cpu = cpu; - - next_p = ktime_get(); - div_u64_rem(next_p, TICK_NSEC, &rem); - if (rem) { - next_p -= rem; - next_p += TICK_NSEC; - } - - tick_next_period = next_p; + tick_next_period = ktime_get(); #ifdef CONFIG_NO_HZ_FULL /* * The boot CPU may be nohz_full, in which case set diff --git a/kernel/time/tick-sched.c b/kernel/time/tick-sched.c index a46506f7ec6d0..d6fb6a676bbbb 100644 --- a/kernel/time/tick-sched.c +++ b/kernel/time/tick-sched.c @@ -161,8 +161,19 @@ static ktime_t tick_init_jiffy_update(void) raw_spin_lock(&jiffies_lock); write_seqcount_begin(&jiffies_seq); /* Did we start the jiffies update yet ? */ - if (last_jiffies_update == 0) + if (last_jiffies_update == 0) { + u32 rem; + + /* + * Ensure that the tick is aligned to a multiple of + * TICK_NSEC. + */ + div_u64_rem(tick_next_period, TICK_NSEC, &rem); + if (rem) + tick_next_period += TICK_NSEC - rem; + last_jiffies_update = tick_next_period; + } period = last_jiffies_update; write_seqcount_end(&jiffies_seq); raw_spin_unlock(&jiffies_lock); diff --git a/mm/maccess.c b/mm/maccess.c index 074f6b086671e..518a25667323e 100644 --- a/mm/maccess.c +++ b/mm/maccess.c @@ -5,6 +5,7 @@ #include #include #include +#include bool __weak copy_from_kernel_nofault_allowed(const void *unsafe_src, size_t size) @@ -113,11 +114,16 @@ Efault: long copy_from_user_nofault(void *dst, const void __user *src, size_t size) { long ret = -EFAULT; - if (access_ok(src, size)) { - pagefault_disable(); - ret = __copy_from_user_inatomic(dst, src, size); - pagefault_enable(); - } + + if (!__access_ok(src, size)) + return ret; + + if (!nmi_uaccess_okay()) + return ret; + + pagefault_disable(); + ret = __copy_from_user_inatomic(dst, src, size); + pagefault_enable(); if (ret) return -EFAULT; diff --git a/mm/memfd.c b/mm/memfd.c index 08f5f8304746f..b0104b49bf82c 100644 --- a/mm/memfd.c +++ b/mm/memfd.c @@ -328,7 +328,8 @@ SYSCALL_DEFINE2(memfd_create, if (flags & MFD_ALLOW_SEALING) { file_seals = memfd_file_seals_ptr(file); - *file_seals &= ~F_SEAL_SEAL; + if (file_seals) + *file_seals &= ~F_SEAL_SEAL; } fd_install(fd, file); diff --git a/mm/usercopy.c b/mm/usercopy.c index c1ee15a986338..434fce112acb9 100644 --- a/mm/usercopy.c +++ b/mm/usercopy.c @@ -172,7 +172,7 @@ static inline void check_heap_object(const void *ptr, unsigned long n, return; } - if (is_vmalloc_addr(ptr)) { + if (is_vmalloc_addr(ptr) && !pagefault_disabled()) { struct vmap_area *area = find_vmap_area(addr); if (!area) diff --git a/net/core/sock.c b/net/core/sock.c index 83f590d8d0850..b021cb9c95ef3 100644 --- a/net/core/sock.c +++ b/net/core/sock.c @@ -1355,12 +1355,6 @@ set_sndbuf: __sock_set_mark(sk, val); break; case SO_RCVMARK: - if (!sockopt_ns_capable(sock_net(sk)->user_ns, CAP_NET_RAW) && - !sockopt_ns_capable(sock_net(sk)->user_ns, CAP_NET_ADMIN)) { - ret = -EPERM; - break; - } - sock_valbool_flag(sk, SOCK_RCVMARK, valbool); break; diff --git a/net/ipv4/esp4_offload.c b/net/ipv4/esp4_offload.c index 3969fa805679c..ee848be59e65a 100644 --- a/net/ipv4/esp4_offload.c +++ b/net/ipv4/esp4_offload.c @@ -340,6 +340,9 @@ static int esp_xmit(struct xfrm_state *x, struct sk_buff *skb, netdev_features_ secpath_reset(skb); + if (skb_needs_linearize(skb, skb->dev->features) && + __skb_linearize(skb)) + return -ENOMEM; return 0; } diff --git a/net/ipv4/xfrm4_input.c b/net/ipv4/xfrm4_input.c index ad2afeef4f106..eac206a290d05 100644 --- a/net/ipv4/xfrm4_input.c +++ b/net/ipv4/xfrm4_input.c @@ -164,6 +164,7 @@ drop: kfree_skb(skb); return 0; } +EXPORT_SYMBOL(xfrm4_udp_encap_rcv); int xfrm4_rcv(struct sk_buff *skb) { diff --git a/net/ipv6/esp6_offload.c b/net/ipv6/esp6_offload.c index 242f4295940e6..fc6a5be732634 100644 --- a/net/ipv6/esp6_offload.c +++ b/net/ipv6/esp6_offload.c @@ -375,6 +375,9 @@ static int esp6_xmit(struct xfrm_state *x, struct sk_buff *skb, netdev_features secpath_reset(skb); + if (skb_needs_linearize(skb, skb->dev->features) && + __skb_linearize(skb)) + return -ENOMEM; return 0; } diff --git a/net/ipv6/xfrm6_input.c b/net/ipv6/xfrm6_input.c index 04cbeefd89828..4907ab241d6be 100644 --- a/net/ipv6/xfrm6_input.c +++ b/net/ipv6/xfrm6_input.c @@ -86,6 +86,9 @@ int xfrm6_udp_encap_rcv(struct sock *sk, struct sk_buff *skb) __be32 *udpdata32; __u16 encap_type = up->encap_type; + if (skb->protocol == htons(ETH_P_IP)) + return xfrm4_udp_encap_rcv(sk, skb); + /* if this is not encapsulated socket, then just return now */ if (!encap_type) return 1; diff --git a/net/mptcp/protocol.c b/net/mptcp/protocol.c index 59186997c3e2e..a2c6ce40e4269 100644 --- a/net/mptcp/protocol.c +++ b/net/mptcp/protocol.c @@ -53,7 +53,7 @@ enum { static struct percpu_counter mptcp_sockets_allocated ____cacheline_aligned_in_smp; static void __mptcp_destroy_sock(struct sock *sk); -static void __mptcp_check_send_data_fin(struct sock *sk); +static void mptcp_check_send_data_fin(struct sock *sk); DEFINE_PER_CPU(struct mptcp_delegated_action, mptcp_delegated_actions); static struct net_device mptcp_napi_dev; @@ -420,8 +420,7 @@ static bool mptcp_pending_data_fin_ack(struct sock *sk) { struct mptcp_sock *msk = mptcp_sk(sk); - return !__mptcp_check_fallback(msk) && - ((1 << sk->sk_state) & + return ((1 << sk->sk_state) & (TCPF_FIN_WAIT1 | TCPF_CLOSING | TCPF_LAST_ACK)) && msk->write_seq == READ_ONCE(msk->snd_una); } @@ -579,9 +578,6 @@ static bool mptcp_check_data_fin(struct sock *sk) u64 rcv_data_fin_seq; bool ret = false; - if (__mptcp_check_fallback(msk)) - return ret; - /* Need to ack a DATA_FIN received from a peer while this side * of the connection is in ESTABLISHED, FIN_WAIT1, or FIN_WAIT2. * msk->rcv_data_fin was set when parsing the incoming options @@ -619,7 +615,8 @@ static bool mptcp_check_data_fin(struct sock *sk) } ret = true; - mptcp_send_ack(msk); + if (!__mptcp_check_fallback(msk)) + mptcp_send_ack(msk); mptcp_close_wake_up(sk); } return ret; @@ -846,12 +843,12 @@ static bool __mptcp_finish_join(struct mptcp_sock *msk, struct sock *ssk) return true; } -static void __mptcp_flush_join_list(struct sock *sk) +static void __mptcp_flush_join_list(struct sock *sk, struct list_head *join_list) { struct mptcp_subflow_context *tmp, *subflow; struct mptcp_sock *msk = mptcp_sk(sk); - list_for_each_entry_safe(subflow, tmp, &msk->join_list, node) { + list_for_each_entry_safe(subflow, tmp, join_list, node) { struct sock *ssk = mptcp_subflow_tcp_sock(subflow); bool slow = lock_sock_fast(ssk); @@ -1606,7 +1603,7 @@ out: if (!mptcp_timer_pending(sk)) mptcp_reset_timer(sk); if (do_check_data_fin) - __mptcp_check_send_data_fin(sk); + mptcp_check_send_data_fin(sk); } static void __mptcp_subflow_push_pending(struct sock *sk, struct sock *ssk) @@ -1708,7 +1705,13 @@ static int mptcp_sendmsg_fastopen(struct sock *sk, struct sock *ssk, struct msgh if (ret && ret != -EINPROGRESS && ret != -ERESTARTSYS && ret != -EINTR) *copied_syn = 0; } else if (ret && ret != -EINPROGRESS) { - mptcp_disconnect(sk, 0); + /* The disconnect() op called by tcp_sendmsg_fastopen()/ + * __inet_stream_connect() can fail, due to looking check, + * see mptcp_disconnect(). + * Attempt it again outside the problematic scope. + */ + if (!mptcp_disconnect(sk, 0)) + sk->sk_socket->state = SS_UNCONNECTED; } return ret; @@ -2375,7 +2378,10 @@ static void __mptcp_close_ssk(struct sock *sk, struct sock *ssk, need_push = (flags & MPTCP_CF_PUSH) && __mptcp_retransmit_pending_data(sk); if (!dispose_it) { - tcp_disconnect(ssk, 0); + /* The MPTCP code never wait on the subflow sockets, TCP-level + * disconnect should never fail + */ + WARN_ON_ONCE(tcp_disconnect(ssk, 0)); msk->subflow->state = SS_UNCONNECTED; mptcp_subflow_ctx_reset(subflow); release_sock(ssk); @@ -2656,8 +2662,6 @@ static void mptcp_worker(struct work_struct *work) if (unlikely((1 << state) & (TCPF_CLOSE | TCPF_LISTEN))) goto unlock; - mptcp_check_data_fin_ack(sk); - mptcp_check_fastclose(msk); mptcp_pm_nl_work(msk); @@ -2665,7 +2669,8 @@ static void mptcp_worker(struct work_struct *work) if (test_and_clear_bit(MPTCP_WORK_EOF, &msk->flags)) mptcp_check_for_eof(msk); - __mptcp_check_send_data_fin(sk); + mptcp_check_send_data_fin(sk); + mptcp_check_data_fin_ack(sk); mptcp_check_data_fin(sk); if (test_and_clear_bit(MPTCP_WORK_CLOSE_SUBFLOW, &msk->flags)) @@ -2799,13 +2804,19 @@ void mptcp_subflow_shutdown(struct sock *sk, struct sock *ssk, int how) break; fallthrough; case TCP_SYN_SENT: - tcp_disconnect(ssk, O_NONBLOCK); + WARN_ON_ONCE(tcp_disconnect(ssk, O_NONBLOCK)); break; default: if (__mptcp_check_fallback(mptcp_sk(sk))) { pr_debug("Fallback"); ssk->sk_shutdown |= how; tcp_shutdown(ssk, how); + + /* simulate the data_fin ack reception to let the state + * machine move forward + */ + WRITE_ONCE(mptcp_sk(sk)->snd_una, mptcp_sk(sk)->snd_nxt); + mptcp_schedule_work(sk); } else { pr_debug("Sending DATA_FIN on subflow %p", ssk); tcp_send_ack(ssk); @@ -2845,7 +2856,7 @@ static int mptcp_close_state(struct sock *sk) return next & TCP_ACTION_FIN; } -static void __mptcp_check_send_data_fin(struct sock *sk) +static void mptcp_check_send_data_fin(struct sock *sk) { struct mptcp_subflow_context *subflow; struct mptcp_sock *msk = mptcp_sk(sk); @@ -2863,19 +2874,6 @@ static void __mptcp_check_send_data_fin(struct sock *sk) WRITE_ONCE(msk->snd_nxt, msk->write_seq); - /* fallback socket will not get data_fin/ack, can move to the next - * state now - */ - if (__mptcp_check_fallback(msk)) { - WRITE_ONCE(msk->snd_una, msk->write_seq); - if ((1 << sk->sk_state) & (TCPF_CLOSING | TCPF_LAST_ACK)) { - inet_sk_state_store(sk, TCP_CLOSE); - mptcp_close_wake_up(sk); - } else if (sk->sk_state == TCP_FIN_WAIT1) { - inet_sk_state_store(sk, TCP_FIN_WAIT2); - } - } - mptcp_for_each_subflow(msk, subflow) { struct sock *tcp_sk = mptcp_subflow_tcp_sock(subflow); @@ -2895,7 +2893,7 @@ static void __mptcp_wr_shutdown(struct sock *sk) WRITE_ONCE(msk->write_seq, msk->write_seq + 1); WRITE_ONCE(msk->snd_data_fin_enable, 1); - __mptcp_check_send_data_fin(sk); + mptcp_check_send_data_fin(sk); } static void __mptcp_destroy_sock(struct sock *sk) @@ -3051,13 +3049,18 @@ static int mptcp_disconnect(struct sock *sk, int flags) { struct mptcp_sock *msk = mptcp_sk(sk); + /* Deny disconnect if other threads are blocked in sk_wait_event() + * or inet_wait_for_connect(). + */ + if (sk->sk_wait_pending) + return -EBUSY; + /* We are on the fastopen error path. We can't call straight into the * subflows cleanup code due to lock nesting (we are already under - * msk->firstsocket lock). Do nothing and leave the cleanup to the - * caller. + * msk->firstsocket lock). */ if (msk->fastopening) - return 0; + return -EBUSY; inet_sk_state_store(sk, TCP_CLOSE); @@ -3118,6 +3121,7 @@ struct sock *mptcp_sk_clone_init(const struct sock *sk, inet_sk(nsk)->pinet6 = mptcp_inet6_sk(nsk); #endif + nsk->sk_wait_pending = 0; __mptcp_init_sock(nsk); msk = mptcp_sk(nsk); @@ -3320,9 +3324,14 @@ static void mptcp_release_cb(struct sock *sk) for (;;) { unsigned long flags = (msk->cb_flags & MPTCP_FLAGS_PROCESS_CTX_NEED) | msk->push_pending; + struct list_head join_list; + if (!flags) break; + INIT_LIST_HEAD(&join_list); + list_splice_init(&msk->join_list, &join_list); + /* the following actions acquire the subflow socket lock * * 1) can't be invoked in atomic scope @@ -3333,8 +3342,9 @@ static void mptcp_release_cb(struct sock *sk) msk->push_pending = 0; msk->cb_flags &= ~flags; spin_unlock_bh(&sk->sk_lock.slock); + if (flags & BIT(MPTCP_FLUSH_JOIN_LIST)) - __mptcp_flush_join_list(sk); + __mptcp_flush_join_list(sk, &join_list); if (flags & BIT(MPTCP_PUSH_PENDING)) __mptcp_push_pending(sk, 0); if (flags & BIT(MPTCP_RETRANSMIT)) diff --git a/net/mptcp/subflow.c b/net/mptcp/subflow.c index 336878f8a222a..047e46dd028dd 100644 --- a/net/mptcp/subflow.c +++ b/net/mptcp/subflow.c @@ -1688,14 +1688,16 @@ static void subflow_state_change(struct sock *sk) { struct mptcp_subflow_context *subflow = mptcp_subflow_ctx(sk); struct sock *parent = subflow->conn; + struct mptcp_sock *msk; __subflow_state_change(sk); + msk = mptcp_sk(parent); if (subflow_simultaneous_connect(sk)) { mptcp_propagate_sndbuf(parent, sk); mptcp_do_fallback(sk); - mptcp_rcv_space_init(mptcp_sk(parent), sk); - pr_fallback(mptcp_sk(parent)); + mptcp_rcv_space_init(msk, sk); + pr_fallback(msk); subflow->conn_finished = 1; mptcp_set_connected(parent); } @@ -1711,11 +1713,12 @@ static void subflow_state_change(struct sock *sk) subflow_sched_work_if_closed(mptcp_sk(parent), sk); - if (__mptcp_check_fallback(mptcp_sk(parent)) && - !subflow->rx_eof && subflow_is_done(sk)) { - subflow->rx_eof = 1; - mptcp_subflow_eof(parent); - } + /* when the fallback subflow closes the rx side, trigger a 'dummy' + * ingress data fin, so that the msk state will follow along + */ + if (__mptcp_check_fallback(msk) && subflow_is_done(sk) && msk->first == sk && + mptcp_update_rcv_data_fin(msk, READ_ONCE(msk->ack_seq), true)) + mptcp_schedule_work(parent); } void mptcp_subflow_queue_clean(struct sock *listener_sk, struct sock *listener_ssk) diff --git a/net/netfilter/ipvs/ip_vs_xmit.c b/net/netfilter/ipvs/ip_vs_xmit.c index 0291713798842..7243079ef3546 100644 --- a/net/netfilter/ipvs/ip_vs_xmit.c +++ b/net/netfilter/ipvs/ip_vs_xmit.c @@ -1225,6 +1225,7 @@ ip_vs_tunnel_xmit(struct sk_buff *skb, struct ip_vs_conn *cp, skb->transport_header = skb->network_header; skb_set_inner_ipproto(skb, next_protocol); + skb_set_inner_mac_header(skb, skb_inner_network_offset(skb)); if (tun_type == IP_VS_CONN_F_TUNNEL_TYPE_GUE) { bool check = false; @@ -1373,6 +1374,7 @@ ip_vs_tunnel_xmit_v6(struct sk_buff *skb, struct ip_vs_conn *cp, skb->transport_header = skb->network_header; skb_set_inner_ipproto(skb, next_protocol); + skb_set_inner_mac_header(skb, skb_inner_network_offset(skb)); if (tun_type == IP_VS_CONN_F_TUNNEL_TYPE_GUE) { bool check = false; diff --git a/net/netfilter/nf_tables_api.c b/net/netfilter/nf_tables_api.c index 13d4913266b4d..7f71bdbc82672 100644 --- a/net/netfilter/nf_tables_api.c +++ b/net/netfilter/nf_tables_api.c @@ -153,6 +153,7 @@ static struct nft_trans *nft_trans_alloc_gfp(const struct nft_ctx *ctx, return NULL; INIT_LIST_HEAD(&trans->list); + INIT_LIST_HEAD(&trans->binding_list); trans->msg_type = msg_type; trans->ctx = *ctx; @@ -165,13 +166,20 @@ static struct nft_trans *nft_trans_alloc(const struct nft_ctx *ctx, return nft_trans_alloc_gfp(ctx, msg_type, size, GFP_KERNEL); } -static void nft_trans_destroy(struct nft_trans *trans) +static void nft_trans_list_del(struct nft_trans *trans) { list_del(&trans->list); + list_del(&trans->binding_list); +} + +static void nft_trans_destroy(struct nft_trans *trans) +{ + nft_trans_list_del(trans); kfree(trans); } -static void nft_set_trans_bind(const struct nft_ctx *ctx, struct nft_set *set) +static void __nft_set_trans_bind(const struct nft_ctx *ctx, struct nft_set *set, + bool bind) { struct nftables_pernet *nft_net; struct net *net = ctx->net; @@ -185,16 +193,80 @@ static void nft_set_trans_bind(const struct nft_ctx *ctx, struct nft_set *set) switch (trans->msg_type) { case NFT_MSG_NEWSET: if (nft_trans_set(trans) == set) - nft_trans_set_bound(trans) = true; + nft_trans_set_bound(trans) = bind; break; case NFT_MSG_NEWSETELEM: if (nft_trans_elem_set(trans) == set) - nft_trans_elem_set_bound(trans) = true; + nft_trans_elem_set_bound(trans) = bind; + break; + } + } +} + +static void nft_set_trans_bind(const struct nft_ctx *ctx, struct nft_set *set) +{ + return __nft_set_trans_bind(ctx, set, true); +} + +static void nft_set_trans_unbind(const struct nft_ctx *ctx, struct nft_set *set) +{ + return __nft_set_trans_bind(ctx, set, false); +} + +static void __nft_chain_trans_bind(const struct nft_ctx *ctx, + struct nft_chain *chain, bool bind) +{ + struct nftables_pernet *nft_net; + struct net *net = ctx->net; + struct nft_trans *trans; + + if (!nft_chain_binding(chain)) + return; + + nft_net = nft_pernet(net); + list_for_each_entry_reverse(trans, &nft_net->commit_list, list) { + switch (trans->msg_type) { + case NFT_MSG_NEWCHAIN: + if (nft_trans_chain(trans) == chain) + nft_trans_chain_bound(trans) = bind; + break; + case NFT_MSG_NEWRULE: + if (trans->ctx.chain == chain) + nft_trans_rule_bound(trans) = bind; break; } } } +static void nft_chain_trans_bind(const struct nft_ctx *ctx, + struct nft_chain *chain) +{ + __nft_chain_trans_bind(ctx, chain, true); +} + +int nf_tables_bind_chain(const struct nft_ctx *ctx, struct nft_chain *chain) +{ + if (!nft_chain_binding(chain)) + return 0; + + if (nft_chain_binding(ctx->chain)) + return -EOPNOTSUPP; + + if (chain->bound) + return -EBUSY; + + chain->bound = true; + chain->use++; + nft_chain_trans_bind(ctx, chain); + + return 0; +} + +void nf_tables_unbind_chain(const struct nft_ctx *ctx, struct nft_chain *chain) +{ + __nft_chain_trans_bind(ctx, chain, false); +} + static int nft_netdev_register_hooks(struct net *net, struct list_head *hook_list) { @@ -294,6 +366,19 @@ static void nft_trans_commit_list_add_tail(struct net *net, struct nft_trans *tr { struct nftables_pernet *nft_net = nft_pernet(net); + switch (trans->msg_type) { + case NFT_MSG_NEWSET: + if (!nft_trans_set_update(trans) && + nft_set_is_anonymous(nft_trans_set(trans))) + list_add_tail(&trans->binding_list, &nft_net->binding_list); + break; + case NFT_MSG_NEWCHAIN: + if (!nft_trans_chain_update(trans) && + nft_chain_binding(nft_trans_chain(trans))) + list_add_tail(&trans->binding_list, &nft_net->binding_list); + break; + } + list_add_tail(&trans->list, &nft_net->commit_list); } @@ -340,8 +425,9 @@ static struct nft_trans *nft_trans_chain_add(struct nft_ctx *ctx, int msg_type) ntohl(nla_get_be32(ctx->nla[NFTA_CHAIN_ID])); } } - + nft_trans_chain(trans) = ctx->chain; nft_trans_commit_list_add_tail(ctx->net, trans); + return trans; } @@ -359,8 +445,7 @@ static int nft_delchain(struct nft_ctx *ctx) return 0; } -static void nft_rule_expr_activate(const struct nft_ctx *ctx, - struct nft_rule *rule) +void nft_rule_expr_activate(const struct nft_ctx *ctx, struct nft_rule *rule) { struct nft_expr *expr; @@ -373,9 +458,8 @@ static void nft_rule_expr_activate(const struct nft_ctx *ctx, } } -static void nft_rule_expr_deactivate(const struct nft_ctx *ctx, - struct nft_rule *rule, - enum nft_trans_phase phase) +void nft_rule_expr_deactivate(const struct nft_ctx *ctx, struct nft_rule *rule, + enum nft_trans_phase phase) { struct nft_expr *expr; @@ -497,6 +581,58 @@ static int nft_trans_set_add(const struct nft_ctx *ctx, int msg_type, return __nft_trans_set_add(ctx, msg_type, set, NULL); } +static void nft_setelem_data_deactivate(const struct net *net, + const struct nft_set *set, + struct nft_set_elem *elem); + +static int nft_mapelem_deactivate(const struct nft_ctx *ctx, + struct nft_set *set, + const struct nft_set_iter *iter, + struct nft_set_elem *elem) +{ + nft_setelem_data_deactivate(ctx->net, set, elem); + + return 0; +} + +struct nft_set_elem_catchall { + struct list_head list; + struct rcu_head rcu; + void *elem; +}; + +static void nft_map_catchall_deactivate(const struct nft_ctx *ctx, + struct nft_set *set) +{ + u8 genmask = nft_genmask_next(ctx->net); + struct nft_set_elem_catchall *catchall; + struct nft_set_elem elem; + struct nft_set_ext *ext; + + list_for_each_entry(catchall, &set->catchall_list, list) { + ext = nft_set_elem_ext(set, catchall->elem); + if (!nft_set_elem_active(ext, genmask)) + continue; + + elem.priv = catchall->elem; + nft_setelem_data_deactivate(ctx->net, set, &elem); + break; + } +} + +static void nft_map_deactivate(const struct nft_ctx *ctx, struct nft_set *set) +{ + struct nft_set_iter iter = { + .genmask = nft_genmask_next(ctx->net), + .fn = nft_mapelem_deactivate, + }; + + set->ops->walk(ctx, set, &iter); + WARN_ON_ONCE(iter.err); + + nft_map_catchall_deactivate(ctx, set); +} + static int nft_delset(const struct nft_ctx *ctx, struct nft_set *set) { int err; @@ -505,6 +641,9 @@ static int nft_delset(const struct nft_ctx *ctx, struct nft_set *set) if (err < 0) return err; + if (set->flags & (NFT_SET_MAP | NFT_SET_OBJECT)) + nft_map_deactivate(ctx, set); + nft_deactivate_next(ctx->net, set); ctx->table->use--; @@ -2188,7 +2327,7 @@ static int nft_basechain_init(struct nft_base_chain *basechain, u8 family, return 0; } -static int nft_chain_add(struct nft_table *table, struct nft_chain *chain) +int nft_chain_add(struct nft_table *table, struct nft_chain *chain) { int err; @@ -3315,8 +3454,7 @@ err_fill_rule_info: return err; } -static void nf_tables_rule_destroy(const struct nft_ctx *ctx, - struct nft_rule *rule) +void nf_tables_rule_destroy(const struct nft_ctx *ctx, struct nft_rule *rule) { struct nft_expr *expr, *next; @@ -3333,7 +3471,7 @@ static void nf_tables_rule_destroy(const struct nft_ctx *ctx, kfree(rule); } -void nf_tables_rule_release(const struct nft_ctx *ctx, struct nft_rule *rule) +static void nf_tables_rule_release(const struct nft_ctx *ctx, struct nft_rule *rule) { nft_rule_expr_deactivate(ctx, rule, NFT_TRANS_RELEASE); nf_tables_rule_destroy(ctx, rule); @@ -3421,12 +3559,6 @@ int nft_setelem_validate(const struct nft_ctx *ctx, struct nft_set *set, return 0; } -struct nft_set_elem_catchall { - struct list_head list; - struct rcu_head rcu; - void *elem; -}; - int nft_set_catchall_validate(const struct nft_ctx *ctx, struct nft_set *set) { u8 genmask = nft_genmask_next(ctx->net); @@ -3669,7 +3801,7 @@ err_destroy_flow_rule: if (flow) nft_flow_rule_destroy(flow); err_release_rule: - nft_rule_expr_deactivate(&ctx, rule, NFT_TRANS_PREPARE); + nft_rule_expr_deactivate(&ctx, rule, NFT_TRANS_PREPARE_ERROR); nf_tables_rule_destroy(&ctx, rule); err_release_expr: for (i = 0; i < n; i++) { @@ -4642,6 +4774,9 @@ static int nf_tables_newset(struct sk_buff *skb, const struct nfnl_info *info, if (info->nlh->nlmsg_flags & NLM_F_REPLACE) return -EOPNOTSUPP; + if (nft_set_is_anonymous(set)) + return -EOPNOTSUPP; + err = nft_set_expr_alloc(&ctx, set, nla, exprs, &num_exprs, flags); if (err < 0) return err; @@ -4745,7 +4880,7 @@ err_set_expr_alloc: for (i = 0; i < set->num_exprs; i++) nft_expr_destroy(&ctx, set->exprs[i]); err_set_destroy: - ops->destroy(set); + ops->destroy(&ctx, set); err_set_init: kfree(set->name); err_set_name: @@ -4760,7 +4895,7 @@ static void nft_set_catchall_destroy(const struct nft_ctx *ctx, list_for_each_entry_safe(catchall, next, &set->catchall_list, list) { list_del_rcu(&catchall->list); - nft_set_elem_destroy(set, catchall->elem, true); + nf_tables_set_elem_destroy(ctx, set, catchall->elem); kfree_rcu(catchall, rcu); } } @@ -4775,7 +4910,7 @@ static void nft_set_destroy(const struct nft_ctx *ctx, struct nft_set *set) for (i = 0; i < set->num_exprs; i++) nft_expr_destroy(ctx, set->exprs[i]); - set->ops->destroy(set); + set->ops->destroy(ctx, set); nft_set_catchall_destroy(ctx, set); kfree(set->name); kvfree(set); @@ -4936,10 +5071,60 @@ static void nf_tables_unbind_set(const struct nft_ctx *ctx, struct nft_set *set, } } +static void nft_setelem_data_activate(const struct net *net, + const struct nft_set *set, + struct nft_set_elem *elem); + +static int nft_mapelem_activate(const struct nft_ctx *ctx, + struct nft_set *set, + const struct nft_set_iter *iter, + struct nft_set_elem *elem) +{ + nft_setelem_data_activate(ctx->net, set, elem); + + return 0; +} + +static void nft_map_catchall_activate(const struct nft_ctx *ctx, + struct nft_set *set) +{ + u8 genmask = nft_genmask_next(ctx->net); + struct nft_set_elem_catchall *catchall; + struct nft_set_elem elem; + struct nft_set_ext *ext; + + list_for_each_entry(catchall, &set->catchall_list, list) { + ext = nft_set_elem_ext(set, catchall->elem); + if (!nft_set_elem_active(ext, genmask)) + continue; + + elem.priv = catchall->elem; + nft_setelem_data_activate(ctx->net, set, &elem); + break; + } +} + +static void nft_map_activate(const struct nft_ctx *ctx, struct nft_set *set) +{ + struct nft_set_iter iter = { + .genmask = nft_genmask_next(ctx->net), + .fn = nft_mapelem_activate, + }; + + set->ops->walk(ctx, set, &iter); + WARN_ON_ONCE(iter.err); + + nft_map_catchall_activate(ctx, set); +} + void nf_tables_activate_set(const struct nft_ctx *ctx, struct nft_set *set) { - if (nft_set_is_anonymous(set)) + if (nft_set_is_anonymous(set)) { + if (set->flags & (NFT_SET_MAP | NFT_SET_OBJECT)) + nft_map_activate(ctx, set); + nft_clear(ctx->net, set); + } set->use++; } @@ -4950,14 +5135,28 @@ void nf_tables_deactivate_set(const struct nft_ctx *ctx, struct nft_set *set, enum nft_trans_phase phase) { switch (phase) { - case NFT_TRANS_PREPARE: + case NFT_TRANS_PREPARE_ERROR: + nft_set_trans_unbind(ctx, set); if (nft_set_is_anonymous(set)) nft_deactivate_next(ctx->net, set); + set->use--; + break; + case NFT_TRANS_PREPARE: + if (nft_set_is_anonymous(set)) { + if (set->flags & (NFT_SET_MAP | NFT_SET_OBJECT)) + nft_map_deactivate(ctx, set); + + nft_deactivate_next(ctx->net, set); + } set->use--; return; case NFT_TRANS_ABORT: case NFT_TRANS_RELEASE: + if (nft_set_is_anonymous(set) && + set->flags & (NFT_SET_MAP | NFT_SET_OBJECT)) + nft_map_deactivate(ctx, set); + set->use--; fallthrough; default: @@ -5710,6 +5909,7 @@ static void nft_set_elem_expr_destroy(const struct nft_ctx *ctx, __nft_set_elem_expr_destroy(ctx, expr); } +/* Drop references and destroy. Called from gc, dynset and abort path. */ void nft_set_elem_destroy(const struct nft_set *set, void *elem, bool destroy_expr) { @@ -5731,11 +5931,11 @@ void nft_set_elem_destroy(const struct nft_set *set, void *elem, } EXPORT_SYMBOL_GPL(nft_set_elem_destroy); -/* Only called from commit path, nft_setelem_data_deactivate() already deals - * with the refcounting from the preparation phase. +/* Destroy element. References have been already dropped in the preparation + * path via nft_setelem_data_deactivate(). */ -static void nf_tables_set_elem_destroy(const struct nft_ctx *ctx, - const struct nft_set *set, void *elem) +void nf_tables_set_elem_destroy(const struct nft_ctx *ctx, + const struct nft_set *set, void *elem) { struct nft_set_ext *ext = nft_set_elem_ext(set, elem); @@ -6368,7 +6568,7 @@ err_elem_free: if (obj) obj->use--; err_elem_userdata: - nf_tables_set_elem_destroy(ctx, set, elem.priv); + nft_set_elem_destroy(set, elem.priv, true); err_parse_data: if (nla[NFTA_SET_ELEM_DATA] != NULL) nft_data_release(&elem.data.val, desc.type); @@ -6413,7 +6613,8 @@ static int nf_tables_newsetelem(struct sk_buff *skb, if (IS_ERR(set)) return PTR_ERR(set); - if (!list_empty(&set->bindings) && set->flags & NFT_SET_CONSTANT) + if (!list_empty(&set->bindings) && + (set->flags & (NFT_SET_CONSTANT | NFT_SET_ANONYMOUS))) return -EBUSY; nft_ctx_init(&ctx, net, skb, info->nlh, family, table, NULL, nla); @@ -6446,7 +6647,6 @@ static int nf_tables_newsetelem(struct sk_buff *skb, void nft_data_hold(const struct nft_data *data, enum nft_data_types type) { struct nft_chain *chain; - struct nft_rule *rule; if (type == NFT_DATA_VERDICT) { switch (data->verdict.code) { @@ -6454,15 +6654,6 @@ void nft_data_hold(const struct nft_data *data, enum nft_data_types type) case NFT_GOTO: chain = data->verdict.chain; chain->use++; - - if (!nft_chain_is_bound(chain)) - break; - - chain->table->use++; - list_for_each_entry(rule, &chain->rules, list) - chain->use++; - - nft_chain_add(chain->table, chain); break; } } @@ -6697,7 +6888,9 @@ static int nf_tables_delsetelem(struct sk_buff *skb, set = nft_set_lookup(table, nla[NFTA_SET_ELEM_LIST_SET], genmask); if (IS_ERR(set)) return PTR_ERR(set); - if (!list_empty(&set->bindings) && set->flags & NFT_SET_CONSTANT) + + if (!list_empty(&set->bindings) && + (set->flags & (NFT_SET_CONSTANT | NFT_SET_ANONYMOUS))) return -EBUSY; nft_ctx_init(&ctx, net, skb, info->nlh, family, table, NULL, nla); @@ -7464,6 +7657,7 @@ void nf_tables_deactivate_flowtable(const struct nft_ctx *ctx, enum nft_trans_phase phase) { switch (phase) { + case NFT_TRANS_PREPARE_ERROR: case NFT_TRANS_PREPARE: case NFT_TRANS_ABORT: case NFT_TRANS_RELEASE: @@ -8658,7 +8852,7 @@ static void nf_tables_trans_destroy_work(struct work_struct *w) synchronize_rcu(); list_for_each_entry_safe(trans, next, &head, list) { - list_del(&trans->list); + nft_trans_list_del(trans); nft_commit_release(trans); } } @@ -9025,6 +9219,27 @@ static int nf_tables_commit(struct net *net, struct sk_buff *skb) return 0; } + list_for_each_entry(trans, &nft_net->binding_list, binding_list) { + switch (trans->msg_type) { + case NFT_MSG_NEWSET: + if (!nft_trans_set_update(trans) && + nft_set_is_anonymous(nft_trans_set(trans)) && + !nft_trans_set_bound(trans)) { + pr_warn_once("nftables ruleset with unbound set\n"); + return -EINVAL; + } + break; + case NFT_MSG_NEWCHAIN: + if (!nft_trans_chain_update(trans) && + nft_chain_binding(nft_trans_chain(trans)) && + !nft_trans_chain_bound(trans)) { + pr_warn_once("nftables ruleset with unbound chain\n"); + return -EINVAL; + } + break; + } + } + /* 0. Validate ruleset, otherwise roll back for error reporting. */ if (nf_tables_validate(net) < 0) return -EAGAIN; @@ -9368,7 +9583,7 @@ static int __nf_tables_abort(struct net *net, enum nfnl_abort_action action) kfree(nft_trans_chain_name(trans)); nft_trans_destroy(trans); } else { - if (nft_chain_is_bound(trans->ctx.chain)) { + if (nft_trans_chain_bound(trans)) { nft_trans_destroy(trans); break; } @@ -9385,6 +9600,10 @@ static int __nf_tables_abort(struct net *net, enum nfnl_abort_action action) nft_trans_destroy(trans); break; case NFT_MSG_NEWRULE: + if (nft_trans_rule_bound(trans)) { + nft_trans_destroy(trans); + break; + } trans->ctx.chain->use--; list_del_rcu(&nft_trans_rule(trans)->list); nft_rule_expr_deactivate(&trans->ctx, @@ -9417,6 +9636,9 @@ static int __nf_tables_abort(struct net *net, enum nfnl_abort_action action) case NFT_MSG_DELSET: trans->ctx.table->use++; nft_clear(trans->ctx.net, nft_trans_set(trans)); + if (nft_trans_set(trans)->flags & (NFT_SET_MAP | NFT_SET_OBJECT)) + nft_map_activate(&trans->ctx, nft_trans_set(trans)); + nft_trans_destroy(trans); break; case NFT_MSG_NEWSETELEM: @@ -9494,7 +9716,7 @@ static int __nf_tables_abort(struct net *net, enum nfnl_abort_action action) list_for_each_entry_safe_reverse(trans, next, &nft_net->commit_list, list) { - list_del(&trans->list); + nft_trans_list_del(trans); nf_tables_abort_release(trans); } @@ -9943,22 +10165,12 @@ static int nft_verdict_init(const struct nft_ctx *ctx, struct nft_data *data, static void nft_verdict_uninit(const struct nft_data *data) { struct nft_chain *chain; - struct nft_rule *rule; switch (data->verdict.code) { case NFT_JUMP: case NFT_GOTO: chain = data->verdict.chain; chain->use--; - - if (!nft_chain_is_bound(chain)) - break; - - chain->table->use--; - list_for_each_entry(rule, &chain->rules, list) - chain->use--; - - nft_chain_del(chain); break; } } @@ -10193,6 +10405,9 @@ static void __nft_release_table(struct net *net, struct nft_table *table) list_for_each_entry_safe(set, ns, &table->sets, list) { list_del(&set->list); table->use--; + if (set->flags & (NFT_SET_MAP | NFT_SET_OBJECT)) + nft_map_deactivate(&ctx, set); + nft_set_destroy(&ctx, set); } list_for_each_entry_safe(obj, ne, &table->objects, list) { @@ -10277,6 +10492,7 @@ static int __net_init nf_tables_init_net(struct net *net) INIT_LIST_HEAD(&nft_net->tables); INIT_LIST_HEAD(&nft_net->commit_list); + INIT_LIST_HEAD(&nft_net->binding_list); INIT_LIST_HEAD(&nft_net->module_list); INIT_LIST_HEAD(&nft_net->notify_list); mutex_init(&nft_net->commit_mutex); diff --git a/net/netfilter/nfnetlink_osf.c b/net/netfilter/nfnetlink_osf.c index ee6840bd59337..8f1bfa6ccc2d9 100644 --- a/net/netfilter/nfnetlink_osf.c +++ b/net/netfilter/nfnetlink_osf.c @@ -439,3 +439,4 @@ module_init(nfnl_osf_init); module_exit(nfnl_osf_fini); MODULE_LICENSE("GPL"); +MODULE_ALIAS_NFNL_SUBSYS(NFNL_SUBSYS_OSF); diff --git a/net/netfilter/nft_immediate.c b/net/netfilter/nft_immediate.c index 5f28b21abc7df..900e75e8c3465 100644 --- a/net/netfilter/nft_immediate.c +++ b/net/netfilter/nft_immediate.c @@ -76,11 +76,9 @@ static int nft_immediate_init(const struct nft_ctx *ctx, switch (priv->data.verdict.code) { case NFT_JUMP: case NFT_GOTO: - if (nft_chain_is_bound(chain)) { - err = -EBUSY; - goto err1; - } - chain->bound = true; + err = nf_tables_bind_chain(ctx, chain); + if (err < 0) + return err; break; default: break; @@ -98,6 +96,31 @@ static void nft_immediate_activate(const struct nft_ctx *ctx, const struct nft_expr *expr) { const struct nft_immediate_expr *priv = nft_expr_priv(expr); + const struct nft_data *data = &priv->data; + struct nft_ctx chain_ctx; + struct nft_chain *chain; + struct nft_rule *rule; + + if (priv->dreg == NFT_REG_VERDICT) { + switch (data->verdict.code) { + case NFT_JUMP: + case NFT_GOTO: + chain = data->verdict.chain; + if (!nft_chain_binding(chain)) + break; + + chain_ctx = *ctx; + chain_ctx.chain = chain; + + list_for_each_entry(rule, &chain->rules, list) + nft_rule_expr_activate(&chain_ctx, rule); + + nft_clear(ctx->net, chain); + break; + default: + break; + } + } return nft_data_hold(&priv->data, nft_dreg_to_type(priv->dreg)); } @@ -107,6 +130,43 @@ static void nft_immediate_deactivate(const struct nft_ctx *ctx, enum nft_trans_phase phase) { const struct nft_immediate_expr *priv = nft_expr_priv(expr); + const struct nft_data *data = &priv->data; + struct nft_ctx chain_ctx; + struct nft_chain *chain; + struct nft_rule *rule; + + if (priv->dreg == NFT_REG_VERDICT) { + switch (data->verdict.code) { + case NFT_JUMP: + case NFT_GOTO: + chain = data->verdict.chain; + if (!nft_chain_binding(chain)) + break; + + chain_ctx = *ctx; + chain_ctx.chain = chain; + + list_for_each_entry(rule, &chain->rules, list) + nft_rule_expr_deactivate(&chain_ctx, rule, phase); + + switch (phase) { + case NFT_TRANS_PREPARE_ERROR: + nf_tables_unbind_chain(ctx, chain); + fallthrough; + case NFT_TRANS_PREPARE: + nft_deactivate_next(ctx->net, chain); + break; + default: + nft_chain_del(chain); + chain->bound = false; + chain->table->use--; + break; + } + break; + default: + break; + } + } if (phase == NFT_TRANS_COMMIT) return; @@ -131,15 +191,27 @@ static void nft_immediate_destroy(const struct nft_ctx *ctx, case NFT_GOTO: chain = data->verdict.chain; - if (!nft_chain_is_bound(chain)) + if (!nft_chain_binding(chain)) + break; + + /* Rule construction failed, but chain is already bound: + * let the transaction records release this chain and its rules. + */ + if (chain->bound) { + chain->use--; break; + } + /* Rule has been deleted, release chain and its rules. */ chain_ctx = *ctx; chain_ctx.chain = chain; - list_for_each_entry_safe(rule, n, &chain->rules, list) - nf_tables_rule_release(&chain_ctx, rule); - + chain->use--; + list_for_each_entry_safe(rule, n, &chain->rules, list) { + chain->use--; + list_del(&rule->list); + nf_tables_rule_destroy(&chain_ctx, rule); + } nf_tables_chain_destroy(&chain_ctx); break; default: diff --git a/net/netfilter/nft_set_bitmap.c b/net/netfilter/nft_set_bitmap.c index 96081ac8d2b4c..1e5e7a181e0bc 100644 --- a/net/netfilter/nft_set_bitmap.c +++ b/net/netfilter/nft_set_bitmap.c @@ -271,13 +271,14 @@ static int nft_bitmap_init(const struct nft_set *set, return 0; } -static void nft_bitmap_destroy(const struct nft_set *set) +static void nft_bitmap_destroy(const struct nft_ctx *ctx, + const struct nft_set *set) { struct nft_bitmap *priv = nft_set_priv(set); struct nft_bitmap_elem *be, *n; list_for_each_entry_safe(be, n, &priv->list, head) - nft_set_elem_destroy(set, be, true); + nf_tables_set_elem_destroy(ctx, set, be); } static bool nft_bitmap_estimate(const struct nft_set_desc *desc, u32 features, diff --git a/net/netfilter/nft_set_hash.c b/net/netfilter/nft_set_hash.c index 76de6c8d98655..0b73cb0e752f7 100644 --- a/net/netfilter/nft_set_hash.c +++ b/net/netfilter/nft_set_hash.c @@ -400,19 +400,31 @@ static int nft_rhash_init(const struct nft_set *set, return 0; } +struct nft_rhash_ctx { + const struct nft_ctx ctx; + const struct nft_set *set; +}; + static void nft_rhash_elem_destroy(void *ptr, void *arg) { - nft_set_elem_destroy(arg, ptr, true); + struct nft_rhash_ctx *rhash_ctx = arg; + + nf_tables_set_elem_destroy(&rhash_ctx->ctx, rhash_ctx->set, ptr); } -static void nft_rhash_destroy(const struct nft_set *set) +static void nft_rhash_destroy(const struct nft_ctx *ctx, + const struct nft_set *set) { struct nft_rhash *priv = nft_set_priv(set); + struct nft_rhash_ctx rhash_ctx = { + .ctx = *ctx, + .set = set, + }; cancel_delayed_work_sync(&priv->gc_work); rcu_barrier(); rhashtable_free_and_destroy(&priv->ht, nft_rhash_elem_destroy, - (void *)set); + (void *)&rhash_ctx); } /* Number of buckets is stored in u32, so cap our result to 1U<<31 */ @@ -643,7 +655,8 @@ static int nft_hash_init(const struct nft_set *set, return 0; } -static void nft_hash_destroy(const struct nft_set *set) +static void nft_hash_destroy(const struct nft_ctx *ctx, + const struct nft_set *set) { struct nft_hash *priv = nft_set_priv(set); struct nft_hash_elem *he; @@ -653,7 +666,7 @@ static void nft_hash_destroy(const struct nft_set *set) for (i = 0; i < priv->buckets; i++) { hlist_for_each_entry_safe(he, next, &priv->table[i], node) { hlist_del_rcu(&he->node); - nft_set_elem_destroy(set, he, true); + nf_tables_set_elem_destroy(ctx, set, he); } } } diff --git a/net/netfilter/nft_set_pipapo.c b/net/netfilter/nft_set_pipapo.c index 15e451dc3fc46..0452ee586c1cc 100644 --- a/net/netfilter/nft_set_pipapo.c +++ b/net/netfilter/nft_set_pipapo.c @@ -1974,12 +1974,16 @@ static void nft_pipapo_walk(const struct nft_ctx *ctx, struct nft_set *set, struct nft_set_iter *iter) { struct nft_pipapo *priv = nft_set_priv(set); + struct net *net = read_pnet(&set->net); struct nft_pipapo_match *m; struct nft_pipapo_field *f; int i, r; rcu_read_lock(); - m = rcu_dereference(priv->match); + if (iter->genmask == nft_genmask_cur(net)) + m = rcu_dereference(priv->match); + else + m = priv->clone; if (unlikely(!m)) goto out; @@ -2148,10 +2152,12 @@ out_scratch: /** * nft_set_pipapo_match_destroy() - Destroy elements from key mapping array + * @ctx: context * @set: nftables API set representation * @m: matching data pointing to key mapping array */ -static void nft_set_pipapo_match_destroy(const struct nft_set *set, +static void nft_set_pipapo_match_destroy(const struct nft_ctx *ctx, + const struct nft_set *set, struct nft_pipapo_match *m) { struct nft_pipapo_field *f; @@ -2168,15 +2174,17 @@ static void nft_set_pipapo_match_destroy(const struct nft_set *set, e = f->mt[r].e; - nft_set_elem_destroy(set, e, true); + nf_tables_set_elem_destroy(ctx, set, e); } } /** * nft_pipapo_destroy() - Free private data for set and all committed elements + * @ctx: context * @set: nftables API set representation */ -static void nft_pipapo_destroy(const struct nft_set *set) +static void nft_pipapo_destroy(const struct nft_ctx *ctx, + const struct nft_set *set) { struct nft_pipapo *priv = nft_set_priv(set); struct nft_pipapo_match *m; @@ -2186,7 +2194,7 @@ static void nft_pipapo_destroy(const struct nft_set *set) if (m) { rcu_barrier(); - nft_set_pipapo_match_destroy(set, m); + nft_set_pipapo_match_destroy(ctx, set, m); #ifdef NFT_PIPAPO_ALIGN free_percpu(m->scratch_aligned); @@ -2203,7 +2211,7 @@ static void nft_pipapo_destroy(const struct nft_set *set) m = priv->clone; if (priv->dirty) - nft_set_pipapo_match_destroy(set, m); + nft_set_pipapo_match_destroy(ctx, set, m); #ifdef NFT_PIPAPO_ALIGN free_percpu(priv->clone->scratch_aligned); diff --git a/net/netfilter/nft_set_rbtree.c b/net/netfilter/nft_set_rbtree.c index 2f114aa10f1a7..5c05c9b990fba 100644 --- a/net/netfilter/nft_set_rbtree.c +++ b/net/netfilter/nft_set_rbtree.c @@ -664,7 +664,8 @@ static int nft_rbtree_init(const struct nft_set *set, return 0; } -static void nft_rbtree_destroy(const struct nft_set *set) +static void nft_rbtree_destroy(const struct nft_ctx *ctx, + const struct nft_set *set) { struct nft_rbtree *priv = nft_set_priv(set); struct nft_rbtree_elem *rbe; @@ -675,7 +676,7 @@ static void nft_rbtree_destroy(const struct nft_set *set) while ((node = priv->root.rb_node) != NULL) { rb_erase(node, &priv->root); rbe = rb_entry(node, struct nft_rbtree_elem, node); - nft_set_elem_destroy(set, rbe, true); + nf_tables_set_elem_destroy(ctx, set, rbe); } } diff --git a/net/netfilter/xt_osf.c b/net/netfilter/xt_osf.c index e1990baf3a3b7..dc9485854002a 100644 --- a/net/netfilter/xt_osf.c +++ b/net/netfilter/xt_osf.c @@ -71,4 +71,3 @@ MODULE_AUTHOR("Evgeniy Polyakov "); MODULE_DESCRIPTION("Passive OS fingerprint matching."); MODULE_ALIAS("ipt_osf"); MODULE_ALIAS("ip6t_osf"); -MODULE_ALIAS_NFNL_SUBSYS(NFNL_SUBSYS_OSF); diff --git a/net/sched/sch_netem.c b/net/sched/sch_netem.c index fb00ac40ecb72..aa9842158df0a 100644 --- a/net/sched/sch_netem.c +++ b/net/sched/sch_netem.c @@ -966,6 +966,7 @@ static int netem_change(struct Qdisc *sch, struct nlattr *opt, if (ret < 0) return ret; + sch_tree_lock(sch); /* backup q->clg and q->loss_model */ old_clg = q->clg; old_loss_model = q->loss_model; @@ -974,7 +975,7 @@ static int netem_change(struct Qdisc *sch, struct nlattr *opt, ret = get_loss_clg(q, tb[TCA_NETEM_LOSS]); if (ret) { q->loss_model = old_loss_model; - return ret; + goto unlock; } } else { q->loss_model = CLG_RANDOM; @@ -1041,6 +1042,8 @@ static int netem_change(struct Qdisc *sch, struct nlattr *opt, /* capping jitter to the range acceptable by tabledist() */ q->jitter = min_t(s64, abs(q->jitter), INT_MAX); +unlock: + sch_tree_unlock(sch); return ret; get_table_failure: @@ -1050,7 +1053,8 @@ get_table_failure: */ q->clg = old_clg; q->loss_model = old_loss_model; - return ret; + + goto unlock; } static int netem_init(struct Qdisc *sch, struct nlattr *opt, diff --git a/net/xfrm/Makefile b/net/xfrm/Makefile index 494aa744bfb9a..08a2870fdd36f 100644 --- a/net/xfrm/Makefile +++ b/net/xfrm/Makefile @@ -3,6 +3,8 @@ # Makefile for the XFRM subsystem. # +xfrm_interface-$(CONFIG_XFRM_INTERFACE) += xfrm_interface_core.o + obj-$(CONFIG_XFRM) := xfrm_policy.o xfrm_state.o xfrm_hash.o \ xfrm_input.o xfrm_output.o \ xfrm_sysctl.o xfrm_replay.o xfrm_device.o diff --git a/net/xfrm/xfrm_input.c b/net/xfrm/xfrm_input.c index 2defd89da700d..ac1a645afa8df 100644 --- a/net/xfrm/xfrm_input.c +++ b/net/xfrm/xfrm_input.c @@ -131,6 +131,7 @@ struct sec_path *secpath_set(struct sk_buff *skb) memset(sp->ovec, 0, sizeof(sp->ovec)); sp->olen = 0; sp->len = 0; + sp->verified_cnt = 0; return sp; } diff --git a/net/xfrm/xfrm_interface.c b/net/xfrm/xfrm_interface.c deleted file mode 100644 index 5a67b120c4dbd..0000000000000 --- a/net/xfrm/xfrm_interface.c +++ /dev/null @@ -1,1198 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0 -/* - * XFRM virtual interface - * - * Copyright (C) 2018 secunet Security Networks AG - * - * Author: - * Steffen Klassert - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -static int xfrmi_dev_init(struct net_device *dev); -static void xfrmi_dev_setup(struct net_device *dev); -static struct rtnl_link_ops xfrmi_link_ops __read_mostly; -static unsigned int xfrmi_net_id __read_mostly; -static const struct net_device_ops xfrmi_netdev_ops; - -#define XFRMI_HASH_BITS 8 -#define XFRMI_HASH_SIZE BIT(XFRMI_HASH_BITS) - -struct xfrmi_net { - /* lists for storing interfaces in use */ - struct xfrm_if __rcu *xfrmi[XFRMI_HASH_SIZE]; - struct xfrm_if __rcu *collect_md_xfrmi; -}; - -static const struct nla_policy xfrm_lwt_policy[LWT_XFRM_MAX + 1] = { - [LWT_XFRM_IF_ID] = NLA_POLICY_MIN(NLA_U32, 1), - [LWT_XFRM_LINK] = NLA_POLICY_MIN(NLA_U32, 1), -}; - -static void xfrmi_destroy_state(struct lwtunnel_state *lwt) -{ -} - -static int xfrmi_build_state(struct net *net, struct nlattr *nla, - unsigned int family, const void *cfg, - struct lwtunnel_state **ts, - struct netlink_ext_ack *extack) -{ - struct nlattr *tb[LWT_XFRM_MAX + 1]; - struct lwtunnel_state *new_state; - struct xfrm_md_info *info; - int ret; - - ret = nla_parse_nested(tb, LWT_XFRM_MAX, nla, xfrm_lwt_policy, extack); - if (ret < 0) - return ret; - - if (!tb[LWT_XFRM_IF_ID]) { - NL_SET_ERR_MSG(extack, "if_id must be set"); - return -EINVAL; - } - - new_state = lwtunnel_state_alloc(sizeof(*info)); - if (!new_state) { - NL_SET_ERR_MSG(extack, "failed to create encap info"); - return -ENOMEM; - } - - new_state->type = LWTUNNEL_ENCAP_XFRM; - - info = lwt_xfrm_info(new_state); - - info->if_id = nla_get_u32(tb[LWT_XFRM_IF_ID]); - - if (tb[LWT_XFRM_LINK]) - info->link = nla_get_u32(tb[LWT_XFRM_LINK]); - - *ts = new_state; - return 0; -} - -static int xfrmi_fill_encap_info(struct sk_buff *skb, - struct lwtunnel_state *lwt) -{ - struct xfrm_md_info *info = lwt_xfrm_info(lwt); - - if (nla_put_u32(skb, LWT_XFRM_IF_ID, info->if_id) || - (info->link && nla_put_u32(skb, LWT_XFRM_LINK, info->link))) - return -EMSGSIZE; - - return 0; -} - -static int xfrmi_encap_nlsize(struct lwtunnel_state *lwtstate) -{ - return nla_total_size(sizeof(u32)) + /* LWT_XFRM_IF_ID */ - nla_total_size(sizeof(u32)); /* LWT_XFRM_LINK */ -} - -static int xfrmi_encap_cmp(struct lwtunnel_state *a, struct lwtunnel_state *b) -{ - struct xfrm_md_info *a_info = lwt_xfrm_info(a); - struct xfrm_md_info *b_info = lwt_xfrm_info(b); - - return memcmp(a_info, b_info, sizeof(*a_info)); -} - -static const struct lwtunnel_encap_ops xfrmi_encap_ops = { - .build_state = xfrmi_build_state, - .destroy_state = xfrmi_destroy_state, - .fill_encap = xfrmi_fill_encap_info, - .get_encap_size = xfrmi_encap_nlsize, - .cmp_encap = xfrmi_encap_cmp, - .owner = THIS_MODULE, -}; - -#define for_each_xfrmi_rcu(start, xi) \ - for (xi = rcu_dereference(start); xi; xi = rcu_dereference(xi->next)) - -static u32 xfrmi_hash(u32 if_id) -{ - return hash_32(if_id, XFRMI_HASH_BITS); -} - -static struct xfrm_if *xfrmi_lookup(struct net *net, struct xfrm_state *x) -{ - struct xfrmi_net *xfrmn = net_generic(net, xfrmi_net_id); - struct xfrm_if *xi; - - for_each_xfrmi_rcu(xfrmn->xfrmi[xfrmi_hash(x->if_id)], xi) { - if (x->if_id == xi->p.if_id && - (xi->dev->flags & IFF_UP)) - return xi; - } - - xi = rcu_dereference(xfrmn->collect_md_xfrmi); - if (xi && (xi->dev->flags & IFF_UP)) - return xi; - - return NULL; -} - -static bool xfrmi_decode_session(struct sk_buff *skb, - unsigned short family, - struct xfrm_if_decode_session_result *res) -{ - struct net_device *dev; - struct xfrm_if *xi; - int ifindex = 0; - - if (!secpath_exists(skb) || !skb->dev) - return false; - - switch (family) { - case AF_INET6: - ifindex = inet6_sdif(skb); - break; - case AF_INET: - ifindex = inet_sdif(skb); - break; - } - - if (ifindex) { - struct net *net = xs_net(xfrm_input_state(skb)); - - dev = dev_get_by_index_rcu(net, ifindex); - } else { - dev = skb->dev; - } - - if (!dev || !(dev->flags & IFF_UP)) - return false; - if (dev->netdev_ops != &xfrmi_netdev_ops) - return false; - - xi = netdev_priv(dev); - res->net = xi->net; - - if (xi->p.collect_md) - res->if_id = xfrm_input_state(skb)->if_id; - else - res->if_id = xi->p.if_id; - return true; -} - -static void xfrmi_link(struct xfrmi_net *xfrmn, struct xfrm_if *xi) -{ - struct xfrm_if __rcu **xip = &xfrmn->xfrmi[xfrmi_hash(xi->p.if_id)]; - - rcu_assign_pointer(xi->next , rtnl_dereference(*xip)); - rcu_assign_pointer(*xip, xi); -} - -static void xfrmi_unlink(struct xfrmi_net *xfrmn, struct xfrm_if *xi) -{ - struct xfrm_if __rcu **xip; - struct xfrm_if *iter; - - for (xip = &xfrmn->xfrmi[xfrmi_hash(xi->p.if_id)]; - (iter = rtnl_dereference(*xip)) != NULL; - xip = &iter->next) { - if (xi == iter) { - rcu_assign_pointer(*xip, xi->next); - break; - } - } -} - -static void xfrmi_dev_free(struct net_device *dev) -{ - struct xfrm_if *xi = netdev_priv(dev); - - gro_cells_destroy(&xi->gro_cells); - free_percpu(dev->tstats); -} - -static int xfrmi_create(struct net_device *dev) -{ - struct xfrm_if *xi = netdev_priv(dev); - struct net *net = dev_net(dev); - struct xfrmi_net *xfrmn = net_generic(net, xfrmi_net_id); - int err; - - dev->rtnl_link_ops = &xfrmi_link_ops; - err = register_netdevice(dev); - if (err < 0) - goto out; - - if (xi->p.collect_md) - rcu_assign_pointer(xfrmn->collect_md_xfrmi, xi); - else - xfrmi_link(xfrmn, xi); - - return 0; - -out: - return err; -} - -static struct xfrm_if *xfrmi_locate(struct net *net, struct xfrm_if_parms *p) -{ - struct xfrm_if __rcu **xip; - struct xfrm_if *xi; - struct xfrmi_net *xfrmn = net_generic(net, xfrmi_net_id); - - for (xip = &xfrmn->xfrmi[xfrmi_hash(p->if_id)]; - (xi = rtnl_dereference(*xip)) != NULL; - xip = &xi->next) - if (xi->p.if_id == p->if_id) - return xi; - - return NULL; -} - -static void xfrmi_dev_uninit(struct net_device *dev) -{ - struct xfrm_if *xi = netdev_priv(dev); - struct xfrmi_net *xfrmn = net_generic(xi->net, xfrmi_net_id); - - if (xi->p.collect_md) - RCU_INIT_POINTER(xfrmn->collect_md_xfrmi, NULL); - else - xfrmi_unlink(xfrmn, xi); -} - -static void xfrmi_scrub_packet(struct sk_buff *skb, bool xnet) -{ - skb_clear_tstamp(skb); - skb->pkt_type = PACKET_HOST; - skb->skb_iif = 0; - skb->ignore_df = 0; - skb_dst_drop(skb); - nf_reset_ct(skb); - nf_reset_trace(skb); - - if (!xnet) - return; - - ipvs_reset(skb); - secpath_reset(skb); - skb_orphan(skb); - skb->mark = 0; -} - -static int xfrmi_rcv_cb(struct sk_buff *skb, int err) -{ - const struct xfrm_mode *inner_mode; - struct net_device *dev; - struct xfrm_state *x; - struct xfrm_if *xi; - bool xnet; - int link; - - if (err && !secpath_exists(skb)) - return 0; - - x = xfrm_input_state(skb); - - xi = xfrmi_lookup(xs_net(x), x); - if (!xi) - return 1; - - link = skb->dev->ifindex; - dev = xi->dev; - skb->dev = dev; - - if (err) { - dev->stats.rx_errors++; - dev->stats.rx_dropped++; - - return 0; - } - - xnet = !net_eq(xi->net, dev_net(skb->dev)); - - if (xnet) { - inner_mode = &x->inner_mode; - - if (x->sel.family == AF_UNSPEC) { - inner_mode = xfrm_ip2inner_mode(x, XFRM_MODE_SKB_CB(skb)->protocol); - if (inner_mode == NULL) { - XFRM_INC_STATS(dev_net(skb->dev), - LINUX_MIB_XFRMINSTATEMODEERROR); - return -EINVAL; - } - } - - if (!xfrm_policy_check(NULL, XFRM_POLICY_IN, skb, - inner_mode->family)) - return -EPERM; - } - - xfrmi_scrub_packet(skb, xnet); - if (xi->p.collect_md) { - struct metadata_dst *md_dst; - - md_dst = metadata_dst_alloc(0, METADATA_XFRM, GFP_ATOMIC); - if (!md_dst) - return -ENOMEM; - - md_dst->u.xfrm_info.if_id = x->if_id; - md_dst->u.xfrm_info.link = link; - skb_dst_set(skb, (struct dst_entry *)md_dst); - } - dev_sw_netstats_rx_add(dev, skb->len); - - return 0; -} - -static int -xfrmi_xmit2(struct sk_buff *skb, struct net_device *dev, struct flowi *fl) -{ - struct xfrm_if *xi = netdev_priv(dev); - struct net_device_stats *stats = &xi->dev->stats; - struct dst_entry *dst = skb_dst(skb); - unsigned int length = skb->len; - struct net_device *tdev; - struct xfrm_state *x; - int err = -1; - u32 if_id; - int mtu; - - if (xi->p.collect_md) { - struct xfrm_md_info *md_info = skb_xfrm_md_info(skb); - - if (unlikely(!md_info)) - return -EINVAL; - - if_id = md_info->if_id; - fl->flowi_oif = md_info->link; - } else { - if_id = xi->p.if_id; - } - - dst_hold(dst); - dst = xfrm_lookup_with_ifid(xi->net, dst, fl, NULL, 0, if_id); - if (IS_ERR(dst)) { - err = PTR_ERR(dst); - dst = NULL; - goto tx_err_link_failure; - } - - x = dst->xfrm; - if (!x) - goto tx_err_link_failure; - - if (x->if_id != if_id) - goto tx_err_link_failure; - - tdev = dst->dev; - - if (tdev == dev) { - stats->collisions++; - net_warn_ratelimited("%s: Local routing loop detected!\n", - dev->name); - goto tx_err_dst_release; - } - - mtu = dst_mtu(dst); - if ((!skb_is_gso(skb) && skb->len > mtu) || - (skb_is_gso(skb) && !skb_gso_validate_network_len(skb, mtu))) { - skb_dst_update_pmtu_no_confirm(skb, mtu); - - if (skb->protocol == htons(ETH_P_IPV6)) { - if (mtu < IPV6_MIN_MTU) - mtu = IPV6_MIN_MTU; - - if (skb->len > 1280) - icmpv6_ndo_send(skb, ICMPV6_PKT_TOOBIG, 0, mtu); - else - goto xmit; - } else { - if (!(ip_hdr(skb)->frag_off & htons(IP_DF))) - goto xmit; - icmp_ndo_send(skb, ICMP_DEST_UNREACH, ICMP_FRAG_NEEDED, - htonl(mtu)); - } - - dst_release(dst); - return -EMSGSIZE; - } - -xmit: - xfrmi_scrub_packet(skb, !net_eq(xi->net, dev_net(dev))); - skb_dst_set(skb, dst); - skb->dev = tdev; - - err = dst_output(xi->net, skb->sk, skb); - if (net_xmit_eval(err) == 0) { - dev_sw_netstats_tx_add(dev, 1, length); - } else { - stats->tx_errors++; - stats->tx_aborted_errors++; - } - - return 0; -tx_err_link_failure: - stats->tx_carrier_errors++; - dst_link_failure(skb); -tx_err_dst_release: - dst_release(dst); - return err; -} - -static netdev_tx_t xfrmi_xmit(struct sk_buff *skb, struct net_device *dev) -{ - struct xfrm_if *xi = netdev_priv(dev); - struct net_device_stats *stats = &xi->dev->stats; - struct dst_entry *dst = skb_dst(skb); - struct flowi fl; - int ret; - - memset(&fl, 0, sizeof(fl)); - - switch (skb->protocol) { - case htons(ETH_P_IPV6): - xfrm_decode_session(skb, &fl, AF_INET6); - memset(IP6CB(skb), 0, sizeof(*IP6CB(skb))); - if (!dst) { - fl.u.ip6.flowi6_oif = dev->ifindex; - fl.u.ip6.flowi6_flags |= FLOWI_FLAG_ANYSRC; - dst = ip6_route_output(dev_net(dev), NULL, &fl.u.ip6); - if (dst->error) { - dst_release(dst); - stats->tx_carrier_errors++; - goto tx_err; - } - skb_dst_set(skb, dst); - } - break; - case htons(ETH_P_IP): - xfrm_decode_session(skb, &fl, AF_INET); - memset(IPCB(skb), 0, sizeof(*IPCB(skb))); - if (!dst) { - struct rtable *rt; - - fl.u.ip4.flowi4_oif = dev->ifindex; - fl.u.ip4.flowi4_flags |= FLOWI_FLAG_ANYSRC; - rt = __ip_route_output_key(dev_net(dev), &fl.u.ip4); - if (IS_ERR(rt)) { - stats->tx_carrier_errors++; - goto tx_err; - } - skb_dst_set(skb, &rt->dst); - } - break; - default: - goto tx_err; - } - - fl.flowi_oif = xi->p.link; - - ret = xfrmi_xmit2(skb, dev, &fl); - if (ret < 0) - goto tx_err; - - return NETDEV_TX_OK; - -tx_err: - stats->tx_errors++; - stats->tx_dropped++; - kfree_skb(skb); - return NETDEV_TX_OK; -} - -static int xfrmi4_err(struct sk_buff *skb, u32 info) -{ - const struct iphdr *iph = (const struct iphdr *)skb->data; - struct net *net = dev_net(skb->dev); - int protocol = iph->protocol; - struct ip_comp_hdr *ipch; - struct ip_esp_hdr *esph; - struct ip_auth_hdr *ah ; - struct xfrm_state *x; - struct xfrm_if *xi; - __be32 spi; - - switch (protocol) { - case IPPROTO_ESP: - esph = (struct ip_esp_hdr *)(skb->data+(iph->ihl<<2)); - spi = esph->spi; - break; - case IPPROTO_AH: - ah = (struct ip_auth_hdr *)(skb->data+(iph->ihl<<2)); - spi = ah->spi; - break; - case IPPROTO_COMP: - ipch = (struct ip_comp_hdr *)(skb->data+(iph->ihl<<2)); - spi = htonl(ntohs(ipch->cpi)); - break; - default: - return 0; - } - - switch (icmp_hdr(skb)->type) { - case ICMP_DEST_UNREACH: - if (icmp_hdr(skb)->code != ICMP_FRAG_NEEDED) - return 0; - break; - case ICMP_REDIRECT: - break; - default: - return 0; - } - - x = xfrm_state_lookup(net, skb->mark, (const xfrm_address_t *)&iph->daddr, - spi, protocol, AF_INET); - if (!x) - return 0; - - xi = xfrmi_lookup(net, x); - if (!xi) { - xfrm_state_put(x); - return -1; - } - - if (icmp_hdr(skb)->type == ICMP_DEST_UNREACH) - ipv4_update_pmtu(skb, net, info, 0, protocol); - else - ipv4_redirect(skb, net, 0, protocol); - xfrm_state_put(x); - - return 0; -} - -static int xfrmi6_err(struct sk_buff *skb, struct inet6_skb_parm *opt, - u8 type, u8 code, int offset, __be32 info) -{ - const struct ipv6hdr *iph = (const struct ipv6hdr *)skb->data; - struct net *net = dev_net(skb->dev); - int protocol = iph->nexthdr; - struct ip_comp_hdr *ipch; - struct ip_esp_hdr *esph; - struct ip_auth_hdr *ah; - struct xfrm_state *x; - struct xfrm_if *xi; - __be32 spi; - - switch (protocol) { - case IPPROTO_ESP: - esph = (struct ip_esp_hdr *)(skb->data + offset); - spi = esph->spi; - break; - case IPPROTO_AH: - ah = (struct ip_auth_hdr *)(skb->data + offset); - spi = ah->spi; - break; - case IPPROTO_COMP: - ipch = (struct ip_comp_hdr *)(skb->data + offset); - spi = htonl(ntohs(ipch->cpi)); - break; - default: - return 0; - } - - if (type != ICMPV6_PKT_TOOBIG && - type != NDISC_REDIRECT) - return 0; - - x = xfrm_state_lookup(net, skb->mark, (const xfrm_address_t *)&iph->daddr, - spi, protocol, AF_INET6); - if (!x) - return 0; - - xi = xfrmi_lookup(net, x); - if (!xi) { - xfrm_state_put(x); - return -1; - } - - if (type == NDISC_REDIRECT) - ip6_redirect(skb, net, skb->dev->ifindex, 0, - sock_net_uid(net, NULL)); - else - ip6_update_pmtu(skb, net, info, 0, 0, sock_net_uid(net, NULL)); - xfrm_state_put(x); - - return 0; -} - -static int xfrmi_change(struct xfrm_if *xi, const struct xfrm_if_parms *p) -{ - if (xi->p.link != p->link) - return -EINVAL; - - xi->p.if_id = p->if_id; - - return 0; -} - -static int xfrmi_update(struct xfrm_if *xi, struct xfrm_if_parms *p) -{ - struct net *net = xi->net; - struct xfrmi_net *xfrmn = net_generic(net, xfrmi_net_id); - int err; - - xfrmi_unlink(xfrmn, xi); - synchronize_net(); - err = xfrmi_change(xi, p); - xfrmi_link(xfrmn, xi); - netdev_state_change(xi->dev); - return err; -} - -static int xfrmi_get_iflink(const struct net_device *dev) -{ - struct xfrm_if *xi = netdev_priv(dev); - - return xi->p.link; -} - -static const struct net_device_ops xfrmi_netdev_ops = { - .ndo_init = xfrmi_dev_init, - .ndo_uninit = xfrmi_dev_uninit, - .ndo_start_xmit = xfrmi_xmit, - .ndo_get_stats64 = dev_get_tstats64, - .ndo_get_iflink = xfrmi_get_iflink, -}; - -static void xfrmi_dev_setup(struct net_device *dev) -{ - dev->netdev_ops = &xfrmi_netdev_ops; - dev->header_ops = &ip_tunnel_header_ops; - dev->type = ARPHRD_NONE; - dev->mtu = ETH_DATA_LEN; - dev->min_mtu = ETH_MIN_MTU; - dev->max_mtu = IP_MAX_MTU; - dev->flags = IFF_NOARP; - dev->needs_free_netdev = true; - dev->priv_destructor = xfrmi_dev_free; - netif_keep_dst(dev); - - eth_broadcast_addr(dev->broadcast); -} - -#define XFRMI_FEATURES (NETIF_F_SG | \ - NETIF_F_FRAGLIST | \ - NETIF_F_GSO_SOFTWARE | \ - NETIF_F_HW_CSUM) - -static int xfrmi_dev_init(struct net_device *dev) -{ - struct xfrm_if *xi = netdev_priv(dev); - struct net_device *phydev = __dev_get_by_index(xi->net, xi->p.link); - int err; - - dev->tstats = netdev_alloc_pcpu_stats(struct pcpu_sw_netstats); - if (!dev->tstats) - return -ENOMEM; - - err = gro_cells_init(&xi->gro_cells, dev); - if (err) { - free_percpu(dev->tstats); - return err; - } - - dev->features |= NETIF_F_LLTX; - dev->features |= XFRMI_FEATURES; - dev->hw_features |= XFRMI_FEATURES; - - if (phydev) { - dev->needed_headroom = phydev->needed_headroom; - dev->needed_tailroom = phydev->needed_tailroom; - - if (is_zero_ether_addr(dev->dev_addr)) - eth_hw_addr_inherit(dev, phydev); - if (is_zero_ether_addr(dev->broadcast)) - memcpy(dev->broadcast, phydev->broadcast, - dev->addr_len); - } else { - eth_hw_addr_random(dev); - eth_broadcast_addr(dev->broadcast); - } - - return 0; -} - -static int xfrmi_validate(struct nlattr *tb[], struct nlattr *data[], - struct netlink_ext_ack *extack) -{ - return 0; -} - -static void xfrmi_netlink_parms(struct nlattr *data[], - struct xfrm_if_parms *parms) -{ - memset(parms, 0, sizeof(*parms)); - - if (!data) - return; - - if (data[IFLA_XFRM_LINK]) - parms->link = nla_get_u32(data[IFLA_XFRM_LINK]); - - if (data[IFLA_XFRM_IF_ID]) - parms->if_id = nla_get_u32(data[IFLA_XFRM_IF_ID]); - - if (data[IFLA_XFRM_COLLECT_METADATA]) - parms->collect_md = true; -} - -static int xfrmi_newlink(struct net *src_net, struct net_device *dev, - struct nlattr *tb[], struct nlattr *data[], - struct netlink_ext_ack *extack) -{ - struct net *net = dev_net(dev); - struct xfrm_if_parms p = {}; - struct xfrm_if *xi; - int err; - - xfrmi_netlink_parms(data, &p); - if (p.collect_md) { - struct xfrmi_net *xfrmn = net_generic(net, xfrmi_net_id); - - if (p.link || p.if_id) { - NL_SET_ERR_MSG(extack, "link and if_id must be zero"); - return -EINVAL; - } - - if (rtnl_dereference(xfrmn->collect_md_xfrmi)) - return -EEXIST; - - } else { - if (!p.if_id) { - NL_SET_ERR_MSG(extack, "if_id must be non zero"); - return -EINVAL; - } - - xi = xfrmi_locate(net, &p); - if (xi) - return -EEXIST; - } - - xi = netdev_priv(dev); - xi->p = p; - xi->net = net; - xi->dev = dev; - - err = xfrmi_create(dev); - return err; -} - -static void xfrmi_dellink(struct net_device *dev, struct list_head *head) -{ - unregister_netdevice_queue(dev, head); -} - -static int xfrmi_changelink(struct net_device *dev, struct nlattr *tb[], - struct nlattr *data[], - struct netlink_ext_ack *extack) -{ - struct xfrm_if *xi = netdev_priv(dev); - struct net *net = xi->net; - struct xfrm_if_parms p = {}; - - xfrmi_netlink_parms(data, &p); - if (!p.if_id) { - NL_SET_ERR_MSG(extack, "if_id must be non zero"); - return -EINVAL; - } - - if (p.collect_md) { - NL_SET_ERR_MSG(extack, "collect_md can't be changed"); - return -EINVAL; - } - - xi = xfrmi_locate(net, &p); - if (!xi) { - xi = netdev_priv(dev); - } else { - if (xi->dev != dev) - return -EEXIST; - if (xi->p.collect_md) { - NL_SET_ERR_MSG(extack, - "device can't be changed to collect_md"); - return -EINVAL; - } - } - - return xfrmi_update(xi, &p); -} - -static size_t xfrmi_get_size(const struct net_device *dev) -{ - return - /* IFLA_XFRM_LINK */ - nla_total_size(4) + - /* IFLA_XFRM_IF_ID */ - nla_total_size(4) + - /* IFLA_XFRM_COLLECT_METADATA */ - nla_total_size(0) + - 0; -} - -static int xfrmi_fill_info(struct sk_buff *skb, const struct net_device *dev) -{ - struct xfrm_if *xi = netdev_priv(dev); - struct xfrm_if_parms *parm = &xi->p; - - if (nla_put_u32(skb, IFLA_XFRM_LINK, parm->link) || - nla_put_u32(skb, IFLA_XFRM_IF_ID, parm->if_id) || - (xi->p.collect_md && nla_put_flag(skb, IFLA_XFRM_COLLECT_METADATA))) - goto nla_put_failure; - return 0; - -nla_put_failure: - return -EMSGSIZE; -} - -static struct net *xfrmi_get_link_net(const struct net_device *dev) -{ - struct xfrm_if *xi = netdev_priv(dev); - - return xi->net; -} - -static const struct nla_policy xfrmi_policy[IFLA_XFRM_MAX + 1] = { - [IFLA_XFRM_UNSPEC] = { .strict_start_type = IFLA_XFRM_COLLECT_METADATA }, - [IFLA_XFRM_LINK] = { .type = NLA_U32 }, - [IFLA_XFRM_IF_ID] = { .type = NLA_U32 }, - [IFLA_XFRM_COLLECT_METADATA] = { .type = NLA_FLAG }, -}; - -static struct rtnl_link_ops xfrmi_link_ops __read_mostly = { - .kind = "xfrm", - .maxtype = IFLA_XFRM_MAX, - .policy = xfrmi_policy, - .priv_size = sizeof(struct xfrm_if), - .setup = xfrmi_dev_setup, - .validate = xfrmi_validate, - .newlink = xfrmi_newlink, - .dellink = xfrmi_dellink, - .changelink = xfrmi_changelink, - .get_size = xfrmi_get_size, - .fill_info = xfrmi_fill_info, - .get_link_net = xfrmi_get_link_net, -}; - -static void __net_exit xfrmi_exit_batch_net(struct list_head *net_exit_list) -{ - struct net *net; - LIST_HEAD(list); - - rtnl_lock(); - list_for_each_entry(net, net_exit_list, exit_list) { - struct xfrmi_net *xfrmn = net_generic(net, xfrmi_net_id); - struct xfrm_if __rcu **xip; - struct xfrm_if *xi; - int i; - - for (i = 0; i < XFRMI_HASH_SIZE; i++) { - for (xip = &xfrmn->xfrmi[i]; - (xi = rtnl_dereference(*xip)) != NULL; - xip = &xi->next) - unregister_netdevice_queue(xi->dev, &list); - } - xi = rtnl_dereference(xfrmn->collect_md_xfrmi); - if (xi) - unregister_netdevice_queue(xi->dev, &list); - } - unregister_netdevice_many(&list); - rtnl_unlock(); -} - -static struct pernet_operations xfrmi_net_ops = { - .exit_batch = xfrmi_exit_batch_net, - .id = &xfrmi_net_id, - .size = sizeof(struct xfrmi_net), -}; - -static struct xfrm6_protocol xfrmi_esp6_protocol __read_mostly = { - .handler = xfrm6_rcv, - .input_handler = xfrm_input, - .cb_handler = xfrmi_rcv_cb, - .err_handler = xfrmi6_err, - .priority = 10, -}; - -static struct xfrm6_protocol xfrmi_ah6_protocol __read_mostly = { - .handler = xfrm6_rcv, - .input_handler = xfrm_input, - .cb_handler = xfrmi_rcv_cb, - .err_handler = xfrmi6_err, - .priority = 10, -}; - -static struct xfrm6_protocol xfrmi_ipcomp6_protocol __read_mostly = { - .handler = xfrm6_rcv, - .input_handler = xfrm_input, - .cb_handler = xfrmi_rcv_cb, - .err_handler = xfrmi6_err, - .priority = 10, -}; - -#if IS_REACHABLE(CONFIG_INET6_XFRM_TUNNEL) -static int xfrmi6_rcv_tunnel(struct sk_buff *skb) -{ - const xfrm_address_t *saddr; - __be32 spi; - - saddr = (const xfrm_address_t *)&ipv6_hdr(skb)->saddr; - spi = xfrm6_tunnel_spi_lookup(dev_net(skb->dev), saddr); - - return xfrm6_rcv_spi(skb, IPPROTO_IPV6, spi, NULL); -} - -static struct xfrm6_tunnel xfrmi_ipv6_handler __read_mostly = { - .handler = xfrmi6_rcv_tunnel, - .cb_handler = xfrmi_rcv_cb, - .err_handler = xfrmi6_err, - .priority = 2, -}; - -static struct xfrm6_tunnel xfrmi_ip6ip_handler __read_mostly = { - .handler = xfrmi6_rcv_tunnel, - .cb_handler = xfrmi_rcv_cb, - .err_handler = xfrmi6_err, - .priority = 2, -}; -#endif - -static struct xfrm4_protocol xfrmi_esp4_protocol __read_mostly = { - .handler = xfrm4_rcv, - .input_handler = xfrm_input, - .cb_handler = xfrmi_rcv_cb, - .err_handler = xfrmi4_err, - .priority = 10, -}; - -static struct xfrm4_protocol xfrmi_ah4_protocol __read_mostly = { - .handler = xfrm4_rcv, - .input_handler = xfrm_input, - .cb_handler = xfrmi_rcv_cb, - .err_handler = xfrmi4_err, - .priority = 10, -}; - -static struct xfrm4_protocol xfrmi_ipcomp4_protocol __read_mostly = { - .handler = xfrm4_rcv, - .input_handler = xfrm_input, - .cb_handler = xfrmi_rcv_cb, - .err_handler = xfrmi4_err, - .priority = 10, -}; - -#if IS_REACHABLE(CONFIG_INET_XFRM_TUNNEL) -static int xfrmi4_rcv_tunnel(struct sk_buff *skb) -{ - return xfrm4_rcv_spi(skb, IPPROTO_IPIP, ip_hdr(skb)->saddr); -} - -static struct xfrm_tunnel xfrmi_ipip_handler __read_mostly = { - .handler = xfrmi4_rcv_tunnel, - .cb_handler = xfrmi_rcv_cb, - .err_handler = xfrmi4_err, - .priority = 3, -}; - -static struct xfrm_tunnel xfrmi_ipip6_handler __read_mostly = { - .handler = xfrmi4_rcv_tunnel, - .cb_handler = xfrmi_rcv_cb, - .err_handler = xfrmi4_err, - .priority = 2, -}; -#endif - -static int __init xfrmi4_init(void) -{ - int err; - - err = xfrm4_protocol_register(&xfrmi_esp4_protocol, IPPROTO_ESP); - if (err < 0) - goto xfrm_proto_esp_failed; - err = xfrm4_protocol_register(&xfrmi_ah4_protocol, IPPROTO_AH); - if (err < 0) - goto xfrm_proto_ah_failed; - err = xfrm4_protocol_register(&xfrmi_ipcomp4_protocol, IPPROTO_COMP); - if (err < 0) - goto xfrm_proto_comp_failed; -#if IS_REACHABLE(CONFIG_INET_XFRM_TUNNEL) - err = xfrm4_tunnel_register(&xfrmi_ipip_handler, AF_INET); - if (err < 0) - goto xfrm_tunnel_ipip_failed; - err = xfrm4_tunnel_register(&xfrmi_ipip6_handler, AF_INET6); - if (err < 0) - goto xfrm_tunnel_ipip6_failed; -#endif - - return 0; - -#if IS_REACHABLE(CONFIG_INET_XFRM_TUNNEL) -xfrm_tunnel_ipip6_failed: - xfrm4_tunnel_deregister(&xfrmi_ipip_handler, AF_INET); -xfrm_tunnel_ipip_failed: - xfrm4_protocol_deregister(&xfrmi_ipcomp4_protocol, IPPROTO_COMP); -#endif -xfrm_proto_comp_failed: - xfrm4_protocol_deregister(&xfrmi_ah4_protocol, IPPROTO_AH); -xfrm_proto_ah_failed: - xfrm4_protocol_deregister(&xfrmi_esp4_protocol, IPPROTO_ESP); -xfrm_proto_esp_failed: - return err; -} - -static void xfrmi4_fini(void) -{ -#if IS_REACHABLE(CONFIG_INET_XFRM_TUNNEL) - xfrm4_tunnel_deregister(&xfrmi_ipip6_handler, AF_INET6); - xfrm4_tunnel_deregister(&xfrmi_ipip_handler, AF_INET); -#endif - xfrm4_protocol_deregister(&xfrmi_ipcomp4_protocol, IPPROTO_COMP); - xfrm4_protocol_deregister(&xfrmi_ah4_protocol, IPPROTO_AH); - xfrm4_protocol_deregister(&xfrmi_esp4_protocol, IPPROTO_ESP); -} - -static int __init xfrmi6_init(void) -{ - int err; - - err = xfrm6_protocol_register(&xfrmi_esp6_protocol, IPPROTO_ESP); - if (err < 0) - goto xfrm_proto_esp_failed; - err = xfrm6_protocol_register(&xfrmi_ah6_protocol, IPPROTO_AH); - if (err < 0) - goto xfrm_proto_ah_failed; - err = xfrm6_protocol_register(&xfrmi_ipcomp6_protocol, IPPROTO_COMP); - if (err < 0) - goto xfrm_proto_comp_failed; -#if IS_REACHABLE(CONFIG_INET6_XFRM_TUNNEL) - err = xfrm6_tunnel_register(&xfrmi_ipv6_handler, AF_INET6); - if (err < 0) - goto xfrm_tunnel_ipv6_failed; - err = xfrm6_tunnel_register(&xfrmi_ip6ip_handler, AF_INET); - if (err < 0) - goto xfrm_tunnel_ip6ip_failed; -#endif - - return 0; - -#if IS_REACHABLE(CONFIG_INET6_XFRM_TUNNEL) -xfrm_tunnel_ip6ip_failed: - xfrm6_tunnel_deregister(&xfrmi_ipv6_handler, AF_INET6); -xfrm_tunnel_ipv6_failed: - xfrm6_protocol_deregister(&xfrmi_ipcomp6_protocol, IPPROTO_COMP); -#endif -xfrm_proto_comp_failed: - xfrm6_protocol_deregister(&xfrmi_ah6_protocol, IPPROTO_AH); -xfrm_proto_ah_failed: - xfrm6_protocol_deregister(&xfrmi_esp6_protocol, IPPROTO_ESP); -xfrm_proto_esp_failed: - return err; -} - -static void xfrmi6_fini(void) -{ -#if IS_REACHABLE(CONFIG_INET6_XFRM_TUNNEL) - xfrm6_tunnel_deregister(&xfrmi_ip6ip_handler, AF_INET); - xfrm6_tunnel_deregister(&xfrmi_ipv6_handler, AF_INET6); -#endif - xfrm6_protocol_deregister(&xfrmi_ipcomp6_protocol, IPPROTO_COMP); - xfrm6_protocol_deregister(&xfrmi_ah6_protocol, IPPROTO_AH); - xfrm6_protocol_deregister(&xfrmi_esp6_protocol, IPPROTO_ESP); -} - -static const struct xfrm_if_cb xfrm_if_cb = { - .decode_session = xfrmi_decode_session, -}; - -static int __init xfrmi_init(void) -{ - const char *msg; - int err; - - pr_info("IPsec XFRM device driver\n"); - - msg = "tunnel device"; - err = register_pernet_device(&xfrmi_net_ops); - if (err < 0) - goto pernet_dev_failed; - - msg = "xfrm4 protocols"; - err = xfrmi4_init(); - if (err < 0) - goto xfrmi4_failed; - - msg = "xfrm6 protocols"; - err = xfrmi6_init(); - if (err < 0) - goto xfrmi6_failed; - - - msg = "netlink interface"; - err = rtnl_link_register(&xfrmi_link_ops); - if (err < 0) - goto rtnl_link_failed; - - lwtunnel_encap_add_ops(&xfrmi_encap_ops, LWTUNNEL_ENCAP_XFRM); - - xfrm_if_register_cb(&xfrm_if_cb); - - return err; - -rtnl_link_failed: - xfrmi6_fini(); -xfrmi6_failed: - xfrmi4_fini(); -xfrmi4_failed: - unregister_pernet_device(&xfrmi_net_ops); -pernet_dev_failed: - pr_err("xfrmi init: failed to register %s\n", msg); - return err; -} - -static void __exit xfrmi_fini(void) -{ - xfrm_if_unregister_cb(); - lwtunnel_encap_del_ops(&xfrmi_encap_ops, LWTUNNEL_ENCAP_XFRM); - rtnl_link_unregister(&xfrmi_link_ops); - xfrmi4_fini(); - xfrmi6_fini(); - unregister_pernet_device(&xfrmi_net_ops); -} - -module_init(xfrmi_init); -module_exit(xfrmi_fini); -MODULE_LICENSE("GPL"); -MODULE_ALIAS_RTNL_LINK("xfrm"); -MODULE_ALIAS_NETDEV("xfrm0"); -MODULE_AUTHOR("Steffen Klassert"); -MODULE_DESCRIPTION("XFRM virtual interface"); diff --git a/net/xfrm/xfrm_interface_core.c b/net/xfrm/xfrm_interface_core.c new file mode 100644 index 0000000000000..94a3609548b11 --- /dev/null +++ b/net/xfrm/xfrm_interface_core.c @@ -0,0 +1,1244 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * XFRM virtual interface + * + * Copyright (C) 2018 secunet Security Networks AG + * + * Author: + * Steffen Klassert + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +static int xfrmi_dev_init(struct net_device *dev); +static void xfrmi_dev_setup(struct net_device *dev); +static struct rtnl_link_ops xfrmi_link_ops __read_mostly; +static unsigned int xfrmi_net_id __read_mostly; +static const struct net_device_ops xfrmi_netdev_ops; + +#define XFRMI_HASH_BITS 8 +#define XFRMI_HASH_SIZE BIT(XFRMI_HASH_BITS) + +struct xfrmi_net { + /* lists for storing interfaces in use */ + struct xfrm_if __rcu *xfrmi[XFRMI_HASH_SIZE]; + struct xfrm_if __rcu *collect_md_xfrmi; +}; + +static const struct nla_policy xfrm_lwt_policy[LWT_XFRM_MAX + 1] = { + [LWT_XFRM_IF_ID] = NLA_POLICY_MIN(NLA_U32, 1), + [LWT_XFRM_LINK] = NLA_POLICY_MIN(NLA_U32, 1), +}; + +static void xfrmi_destroy_state(struct lwtunnel_state *lwt) +{ +} + +static int xfrmi_build_state(struct net *net, struct nlattr *nla, + unsigned int family, const void *cfg, + struct lwtunnel_state **ts, + struct netlink_ext_ack *extack) +{ + struct nlattr *tb[LWT_XFRM_MAX + 1]; + struct lwtunnel_state *new_state; + struct xfrm_md_info *info; + int ret; + + ret = nla_parse_nested(tb, LWT_XFRM_MAX, nla, xfrm_lwt_policy, extack); + if (ret < 0) + return ret; + + if (!tb[LWT_XFRM_IF_ID]) { + NL_SET_ERR_MSG(extack, "if_id must be set"); + return -EINVAL; + } + + new_state = lwtunnel_state_alloc(sizeof(*info)); + if (!new_state) { + NL_SET_ERR_MSG(extack, "failed to create encap info"); + return -ENOMEM; + } + + new_state->type = LWTUNNEL_ENCAP_XFRM; + + info = lwt_xfrm_info(new_state); + + info->if_id = nla_get_u32(tb[LWT_XFRM_IF_ID]); + + if (tb[LWT_XFRM_LINK]) + info->link = nla_get_u32(tb[LWT_XFRM_LINK]); + + *ts = new_state; + return 0; +} + +static int xfrmi_fill_encap_info(struct sk_buff *skb, + struct lwtunnel_state *lwt) +{ + struct xfrm_md_info *info = lwt_xfrm_info(lwt); + + if (nla_put_u32(skb, LWT_XFRM_IF_ID, info->if_id) || + (info->link && nla_put_u32(skb, LWT_XFRM_LINK, info->link))) + return -EMSGSIZE; + + return 0; +} + +static int xfrmi_encap_nlsize(struct lwtunnel_state *lwtstate) +{ + return nla_total_size(sizeof(u32)) + /* LWT_XFRM_IF_ID */ + nla_total_size(sizeof(u32)); /* LWT_XFRM_LINK */ +} + +static int xfrmi_encap_cmp(struct lwtunnel_state *a, struct lwtunnel_state *b) +{ + struct xfrm_md_info *a_info = lwt_xfrm_info(a); + struct xfrm_md_info *b_info = lwt_xfrm_info(b); + + return memcmp(a_info, b_info, sizeof(*a_info)); +} + +static const struct lwtunnel_encap_ops xfrmi_encap_ops = { + .build_state = xfrmi_build_state, + .destroy_state = xfrmi_destroy_state, + .fill_encap = xfrmi_fill_encap_info, + .get_encap_size = xfrmi_encap_nlsize, + .cmp_encap = xfrmi_encap_cmp, + .owner = THIS_MODULE, +}; + +#define for_each_xfrmi_rcu(start, xi) \ + for (xi = rcu_dereference(start); xi; xi = rcu_dereference(xi->next)) + +static u32 xfrmi_hash(u32 if_id) +{ + return hash_32(if_id, XFRMI_HASH_BITS); +} + +static struct xfrm_if *xfrmi_lookup(struct net *net, struct xfrm_state *x) +{ + struct xfrmi_net *xfrmn = net_generic(net, xfrmi_net_id); + struct xfrm_if *xi; + + for_each_xfrmi_rcu(xfrmn->xfrmi[xfrmi_hash(x->if_id)], xi) { + if (x->if_id == xi->p.if_id && + (xi->dev->flags & IFF_UP)) + return xi; + } + + xi = rcu_dereference(xfrmn->collect_md_xfrmi); + if (xi && (xi->dev->flags & IFF_UP)) + return xi; + + return NULL; +} + +static bool xfrmi_decode_session(struct sk_buff *skb, + unsigned short family, + struct xfrm_if_decode_session_result *res) +{ + struct net_device *dev; + struct xfrm_if *xi; + int ifindex = 0; + + if (!secpath_exists(skb) || !skb->dev) + return false; + + switch (family) { + case AF_INET6: + ifindex = inet6_sdif(skb); + break; + case AF_INET: + ifindex = inet_sdif(skb); + break; + } + + if (ifindex) { + struct net *net = xs_net(xfrm_input_state(skb)); + + dev = dev_get_by_index_rcu(net, ifindex); + } else { + dev = skb->dev; + } + + if (!dev || !(dev->flags & IFF_UP)) + return false; + if (dev->netdev_ops != &xfrmi_netdev_ops) + return false; + + xi = netdev_priv(dev); + res->net = xi->net; + + if (xi->p.collect_md) + res->if_id = xfrm_input_state(skb)->if_id; + else + res->if_id = xi->p.if_id; + return true; +} + +static void xfrmi_link(struct xfrmi_net *xfrmn, struct xfrm_if *xi) +{ + struct xfrm_if __rcu **xip = &xfrmn->xfrmi[xfrmi_hash(xi->p.if_id)]; + + rcu_assign_pointer(xi->next , rtnl_dereference(*xip)); + rcu_assign_pointer(*xip, xi); +} + +static void xfrmi_unlink(struct xfrmi_net *xfrmn, struct xfrm_if *xi) +{ + struct xfrm_if __rcu **xip; + struct xfrm_if *iter; + + for (xip = &xfrmn->xfrmi[xfrmi_hash(xi->p.if_id)]; + (iter = rtnl_dereference(*xip)) != NULL; + xip = &iter->next) { + if (xi == iter) { + rcu_assign_pointer(*xip, xi->next); + break; + } + } +} + +static void xfrmi_dev_free(struct net_device *dev) +{ + struct xfrm_if *xi = netdev_priv(dev); + + gro_cells_destroy(&xi->gro_cells); + free_percpu(dev->tstats); +} + +static int xfrmi_create(struct net_device *dev) +{ + struct xfrm_if *xi = netdev_priv(dev); + struct net *net = dev_net(dev); + struct xfrmi_net *xfrmn = net_generic(net, xfrmi_net_id); + int err; + + dev->rtnl_link_ops = &xfrmi_link_ops; + err = register_netdevice(dev); + if (err < 0) + goto out; + + if (xi->p.collect_md) + rcu_assign_pointer(xfrmn->collect_md_xfrmi, xi); + else + xfrmi_link(xfrmn, xi); + + return 0; + +out: + return err; +} + +static struct xfrm_if *xfrmi_locate(struct net *net, struct xfrm_if_parms *p) +{ + struct xfrm_if __rcu **xip; + struct xfrm_if *xi; + struct xfrmi_net *xfrmn = net_generic(net, xfrmi_net_id); + + for (xip = &xfrmn->xfrmi[xfrmi_hash(p->if_id)]; + (xi = rtnl_dereference(*xip)) != NULL; + xip = &xi->next) + if (xi->p.if_id == p->if_id) + return xi; + + return NULL; +} + +static void xfrmi_dev_uninit(struct net_device *dev) +{ + struct xfrm_if *xi = netdev_priv(dev); + struct xfrmi_net *xfrmn = net_generic(xi->net, xfrmi_net_id); + + if (xi->p.collect_md) + RCU_INIT_POINTER(xfrmn->collect_md_xfrmi, NULL); + else + xfrmi_unlink(xfrmn, xi); +} + +static void xfrmi_scrub_packet(struct sk_buff *skb, bool xnet) +{ + skb_clear_tstamp(skb); + skb->pkt_type = PACKET_HOST; + skb->skb_iif = 0; + skb->ignore_df = 0; + skb_dst_drop(skb); + nf_reset_ct(skb); + nf_reset_trace(skb); + + if (!xnet) + return; + + ipvs_reset(skb); + secpath_reset(skb); + skb_orphan(skb); + skb->mark = 0; +} + +static int xfrmi_input(struct sk_buff *skb, int nexthdr, __be32 spi, + int encap_type, unsigned short family) +{ + struct sec_path *sp; + + sp = skb_sec_path(skb); + if (sp && (sp->len || sp->olen) && + !xfrm_policy_check(NULL, XFRM_POLICY_IN, skb, family)) + goto discard; + + XFRM_SPI_SKB_CB(skb)->family = family; + if (family == AF_INET) { + XFRM_SPI_SKB_CB(skb)->daddroff = offsetof(struct iphdr, daddr); + XFRM_TUNNEL_SKB_CB(skb)->tunnel.ip4 = NULL; + } else { + XFRM_SPI_SKB_CB(skb)->daddroff = offsetof(struct ipv6hdr, daddr); + XFRM_TUNNEL_SKB_CB(skb)->tunnel.ip6 = NULL; + } + + return xfrm_input(skb, nexthdr, spi, encap_type); +discard: + kfree_skb(skb); + return 0; +} + +static int xfrmi4_rcv(struct sk_buff *skb) +{ + return xfrmi_input(skb, ip_hdr(skb)->protocol, 0, 0, AF_INET); +} + +static int xfrmi6_rcv(struct sk_buff *skb) +{ + return xfrmi_input(skb, skb_network_header(skb)[IP6CB(skb)->nhoff], + 0, 0, AF_INET6); +} + +static int xfrmi4_input(struct sk_buff *skb, int nexthdr, __be32 spi, int encap_type) +{ + return xfrmi_input(skb, nexthdr, spi, encap_type, AF_INET); +} + +static int xfrmi6_input(struct sk_buff *skb, int nexthdr, __be32 spi, int encap_type) +{ + return xfrmi_input(skb, nexthdr, spi, encap_type, AF_INET6); +} + +static int xfrmi_rcv_cb(struct sk_buff *skb, int err) +{ + const struct xfrm_mode *inner_mode; + struct net_device *dev; + struct xfrm_state *x; + struct xfrm_if *xi; + bool xnet; + int link; + + if (err && !secpath_exists(skb)) + return 0; + + x = xfrm_input_state(skb); + + xi = xfrmi_lookup(xs_net(x), x); + if (!xi) + return 1; + + link = skb->dev->ifindex; + dev = xi->dev; + skb->dev = dev; + + if (err) { + dev->stats.rx_errors++; + dev->stats.rx_dropped++; + + return 0; + } + + xnet = !net_eq(xi->net, dev_net(skb->dev)); + + if (xnet) { + inner_mode = &x->inner_mode; + + if (x->sel.family == AF_UNSPEC) { + inner_mode = xfrm_ip2inner_mode(x, XFRM_MODE_SKB_CB(skb)->protocol); + if (inner_mode == NULL) { + XFRM_INC_STATS(dev_net(skb->dev), + LINUX_MIB_XFRMINSTATEMODEERROR); + return -EINVAL; + } + } + + if (!xfrm_policy_check(NULL, XFRM_POLICY_IN, skb, + inner_mode->family)) + return -EPERM; + } + + xfrmi_scrub_packet(skb, xnet); + if (xi->p.collect_md) { + struct metadata_dst *md_dst; + + md_dst = metadata_dst_alloc(0, METADATA_XFRM, GFP_ATOMIC); + if (!md_dst) + return -ENOMEM; + + md_dst->u.xfrm_info.if_id = x->if_id; + md_dst->u.xfrm_info.link = link; + skb_dst_set(skb, (struct dst_entry *)md_dst); + } + dev_sw_netstats_rx_add(dev, skb->len); + + return 0; +} + +static int +xfrmi_xmit2(struct sk_buff *skb, struct net_device *dev, struct flowi *fl) +{ + struct xfrm_if *xi = netdev_priv(dev); + struct net_device_stats *stats = &xi->dev->stats; + struct dst_entry *dst = skb_dst(skb); + unsigned int length = skb->len; + struct net_device *tdev; + struct xfrm_state *x; + int err = -1; + u32 if_id; + int mtu; + + if (xi->p.collect_md) { + struct xfrm_md_info *md_info = skb_xfrm_md_info(skb); + + if (unlikely(!md_info)) + return -EINVAL; + + if_id = md_info->if_id; + fl->flowi_oif = md_info->link; + } else { + if_id = xi->p.if_id; + } + + dst_hold(dst); + dst = xfrm_lookup_with_ifid(xi->net, dst, fl, NULL, 0, if_id); + if (IS_ERR(dst)) { + err = PTR_ERR(dst); + dst = NULL; + goto tx_err_link_failure; + } + + x = dst->xfrm; + if (!x) + goto tx_err_link_failure; + + if (x->if_id != if_id) + goto tx_err_link_failure; + + tdev = dst->dev; + + if (tdev == dev) { + stats->collisions++; + net_warn_ratelimited("%s: Local routing loop detected!\n", + dev->name); + goto tx_err_dst_release; + } + + mtu = dst_mtu(dst); + if ((!skb_is_gso(skb) && skb->len > mtu) || + (skb_is_gso(skb) && !skb_gso_validate_network_len(skb, mtu))) { + skb_dst_update_pmtu_no_confirm(skb, mtu); + + if (skb->protocol == htons(ETH_P_IPV6)) { + if (mtu < IPV6_MIN_MTU) + mtu = IPV6_MIN_MTU; + + if (skb->len > 1280) + icmpv6_ndo_send(skb, ICMPV6_PKT_TOOBIG, 0, mtu); + else + goto xmit; + } else { + if (!(ip_hdr(skb)->frag_off & htons(IP_DF))) + goto xmit; + icmp_ndo_send(skb, ICMP_DEST_UNREACH, ICMP_FRAG_NEEDED, + htonl(mtu)); + } + + dst_release(dst); + return -EMSGSIZE; + } + +xmit: + xfrmi_scrub_packet(skb, !net_eq(xi->net, dev_net(dev))); + skb_dst_set(skb, dst); + skb->dev = tdev; + + err = dst_output(xi->net, skb->sk, skb); + if (net_xmit_eval(err) == 0) { + dev_sw_netstats_tx_add(dev, 1, length); + } else { + stats->tx_errors++; + stats->tx_aborted_errors++; + } + + return 0; +tx_err_link_failure: + stats->tx_carrier_errors++; + dst_link_failure(skb); +tx_err_dst_release: + dst_release(dst); + return err; +} + +static netdev_tx_t xfrmi_xmit(struct sk_buff *skb, struct net_device *dev) +{ + struct xfrm_if *xi = netdev_priv(dev); + struct net_device_stats *stats = &xi->dev->stats; + struct dst_entry *dst = skb_dst(skb); + struct flowi fl; + int ret; + + memset(&fl, 0, sizeof(fl)); + + switch (skb->protocol) { + case htons(ETH_P_IPV6): + xfrm_decode_session(skb, &fl, AF_INET6); + memset(IP6CB(skb), 0, sizeof(*IP6CB(skb))); + if (!dst) { + fl.u.ip6.flowi6_oif = dev->ifindex; + fl.u.ip6.flowi6_flags |= FLOWI_FLAG_ANYSRC; + dst = ip6_route_output(dev_net(dev), NULL, &fl.u.ip6); + if (dst->error) { + dst_release(dst); + stats->tx_carrier_errors++; + goto tx_err; + } + skb_dst_set(skb, dst); + } + break; + case htons(ETH_P_IP): + xfrm_decode_session(skb, &fl, AF_INET); + memset(IPCB(skb), 0, sizeof(*IPCB(skb))); + if (!dst) { + struct rtable *rt; + + fl.u.ip4.flowi4_oif = dev->ifindex; + fl.u.ip4.flowi4_flags |= FLOWI_FLAG_ANYSRC; + rt = __ip_route_output_key(dev_net(dev), &fl.u.ip4); + if (IS_ERR(rt)) { + stats->tx_carrier_errors++; + goto tx_err; + } + skb_dst_set(skb, &rt->dst); + } + break; + default: + goto tx_err; + } + + fl.flowi_oif = xi->p.link; + + ret = xfrmi_xmit2(skb, dev, &fl); + if (ret < 0) + goto tx_err; + + return NETDEV_TX_OK; + +tx_err: + stats->tx_errors++; + stats->tx_dropped++; + kfree_skb(skb); + return NETDEV_TX_OK; +} + +static int xfrmi4_err(struct sk_buff *skb, u32 info) +{ + const struct iphdr *iph = (const struct iphdr *)skb->data; + struct net *net = dev_net(skb->dev); + int protocol = iph->protocol; + struct ip_comp_hdr *ipch; + struct ip_esp_hdr *esph; + struct ip_auth_hdr *ah ; + struct xfrm_state *x; + struct xfrm_if *xi; + __be32 spi; + + switch (protocol) { + case IPPROTO_ESP: + esph = (struct ip_esp_hdr *)(skb->data+(iph->ihl<<2)); + spi = esph->spi; + break; + case IPPROTO_AH: + ah = (struct ip_auth_hdr *)(skb->data+(iph->ihl<<2)); + spi = ah->spi; + break; + case IPPROTO_COMP: + ipch = (struct ip_comp_hdr *)(skb->data+(iph->ihl<<2)); + spi = htonl(ntohs(ipch->cpi)); + break; + default: + return 0; + } + + switch (icmp_hdr(skb)->type) { + case ICMP_DEST_UNREACH: + if (icmp_hdr(skb)->code != ICMP_FRAG_NEEDED) + return 0; + break; + case ICMP_REDIRECT: + break; + default: + return 0; + } + + x = xfrm_state_lookup(net, skb->mark, (const xfrm_address_t *)&iph->daddr, + spi, protocol, AF_INET); + if (!x) + return 0; + + xi = xfrmi_lookup(net, x); + if (!xi) { + xfrm_state_put(x); + return -1; + } + + if (icmp_hdr(skb)->type == ICMP_DEST_UNREACH) + ipv4_update_pmtu(skb, net, info, 0, protocol); + else + ipv4_redirect(skb, net, 0, protocol); + xfrm_state_put(x); + + return 0; +} + +static int xfrmi6_err(struct sk_buff *skb, struct inet6_skb_parm *opt, + u8 type, u8 code, int offset, __be32 info) +{ + const struct ipv6hdr *iph = (const struct ipv6hdr *)skb->data; + struct net *net = dev_net(skb->dev); + int protocol = iph->nexthdr; + struct ip_comp_hdr *ipch; + struct ip_esp_hdr *esph; + struct ip_auth_hdr *ah; + struct xfrm_state *x; + struct xfrm_if *xi; + __be32 spi; + + switch (protocol) { + case IPPROTO_ESP: + esph = (struct ip_esp_hdr *)(skb->data + offset); + spi = esph->spi; + break; + case IPPROTO_AH: + ah = (struct ip_auth_hdr *)(skb->data + offset); + spi = ah->spi; + break; + case IPPROTO_COMP: + ipch = (struct ip_comp_hdr *)(skb->data + offset); + spi = htonl(ntohs(ipch->cpi)); + break; + default: + return 0; + } + + if (type != ICMPV6_PKT_TOOBIG && + type != NDISC_REDIRECT) + return 0; + + x = xfrm_state_lookup(net, skb->mark, (const xfrm_address_t *)&iph->daddr, + spi, protocol, AF_INET6); + if (!x) + return 0; + + xi = xfrmi_lookup(net, x); + if (!xi) { + xfrm_state_put(x); + return -1; + } + + if (type == NDISC_REDIRECT) + ip6_redirect(skb, net, skb->dev->ifindex, 0, + sock_net_uid(net, NULL)); + else + ip6_update_pmtu(skb, net, info, 0, 0, sock_net_uid(net, NULL)); + xfrm_state_put(x); + + return 0; +} + +static int xfrmi_change(struct xfrm_if *xi, const struct xfrm_if_parms *p) +{ + if (xi->p.link != p->link) + return -EINVAL; + + xi->p.if_id = p->if_id; + + return 0; +} + +static int xfrmi_update(struct xfrm_if *xi, struct xfrm_if_parms *p) +{ + struct net *net = xi->net; + struct xfrmi_net *xfrmn = net_generic(net, xfrmi_net_id); + int err; + + xfrmi_unlink(xfrmn, xi); + synchronize_net(); + err = xfrmi_change(xi, p); + xfrmi_link(xfrmn, xi); + netdev_state_change(xi->dev); + return err; +} + +static int xfrmi_get_iflink(const struct net_device *dev) +{ + struct xfrm_if *xi = netdev_priv(dev); + + return xi->p.link; +} + +static const struct net_device_ops xfrmi_netdev_ops = { + .ndo_init = xfrmi_dev_init, + .ndo_uninit = xfrmi_dev_uninit, + .ndo_start_xmit = xfrmi_xmit, + .ndo_get_stats64 = dev_get_tstats64, + .ndo_get_iflink = xfrmi_get_iflink, +}; + +static void xfrmi_dev_setup(struct net_device *dev) +{ + dev->netdev_ops = &xfrmi_netdev_ops; + dev->header_ops = &ip_tunnel_header_ops; + dev->type = ARPHRD_NONE; + dev->mtu = ETH_DATA_LEN; + dev->min_mtu = ETH_MIN_MTU; + dev->max_mtu = IP_MAX_MTU; + dev->flags = IFF_NOARP; + dev->needs_free_netdev = true; + dev->priv_destructor = xfrmi_dev_free; + netif_keep_dst(dev); + + eth_broadcast_addr(dev->broadcast); +} + +#define XFRMI_FEATURES (NETIF_F_SG | \ + NETIF_F_FRAGLIST | \ + NETIF_F_GSO_SOFTWARE | \ + NETIF_F_HW_CSUM) + +static int xfrmi_dev_init(struct net_device *dev) +{ + struct xfrm_if *xi = netdev_priv(dev); + struct net_device *phydev = __dev_get_by_index(xi->net, xi->p.link); + int err; + + dev->tstats = netdev_alloc_pcpu_stats(struct pcpu_sw_netstats); + if (!dev->tstats) + return -ENOMEM; + + err = gro_cells_init(&xi->gro_cells, dev); + if (err) { + free_percpu(dev->tstats); + return err; + } + + dev->features |= NETIF_F_LLTX; + dev->features |= XFRMI_FEATURES; + dev->hw_features |= XFRMI_FEATURES; + + if (phydev) { + dev->needed_headroom = phydev->needed_headroom; + dev->needed_tailroom = phydev->needed_tailroom; + + if (is_zero_ether_addr(dev->dev_addr)) + eth_hw_addr_inherit(dev, phydev); + if (is_zero_ether_addr(dev->broadcast)) + memcpy(dev->broadcast, phydev->broadcast, + dev->addr_len); + } else { + eth_hw_addr_random(dev); + eth_broadcast_addr(dev->broadcast); + } + + return 0; +} + +static int xfrmi_validate(struct nlattr *tb[], struct nlattr *data[], + struct netlink_ext_ack *extack) +{ + return 0; +} + +static void xfrmi_netlink_parms(struct nlattr *data[], + struct xfrm_if_parms *parms) +{ + memset(parms, 0, sizeof(*parms)); + + if (!data) + return; + + if (data[IFLA_XFRM_LINK]) + parms->link = nla_get_u32(data[IFLA_XFRM_LINK]); + + if (data[IFLA_XFRM_IF_ID]) + parms->if_id = nla_get_u32(data[IFLA_XFRM_IF_ID]); + + if (data[IFLA_XFRM_COLLECT_METADATA]) + parms->collect_md = true; +} + +static int xfrmi_newlink(struct net *src_net, struct net_device *dev, + struct nlattr *tb[], struct nlattr *data[], + struct netlink_ext_ack *extack) +{ + struct net *net = dev_net(dev); + struct xfrm_if_parms p = {}; + struct xfrm_if *xi; + int err; + + xfrmi_netlink_parms(data, &p); + if (p.collect_md) { + struct xfrmi_net *xfrmn = net_generic(net, xfrmi_net_id); + + if (p.link || p.if_id) { + NL_SET_ERR_MSG(extack, "link and if_id must be zero"); + return -EINVAL; + } + + if (rtnl_dereference(xfrmn->collect_md_xfrmi)) + return -EEXIST; + + } else { + if (!p.if_id) { + NL_SET_ERR_MSG(extack, "if_id must be non zero"); + return -EINVAL; + } + + xi = xfrmi_locate(net, &p); + if (xi) + return -EEXIST; + } + + xi = netdev_priv(dev); + xi->p = p; + xi->net = net; + xi->dev = dev; + + err = xfrmi_create(dev); + return err; +} + +static void xfrmi_dellink(struct net_device *dev, struct list_head *head) +{ + unregister_netdevice_queue(dev, head); +} + +static int xfrmi_changelink(struct net_device *dev, struct nlattr *tb[], + struct nlattr *data[], + struct netlink_ext_ack *extack) +{ + struct xfrm_if *xi = netdev_priv(dev); + struct net *net = xi->net; + struct xfrm_if_parms p = {}; + + xfrmi_netlink_parms(data, &p); + if (!p.if_id) { + NL_SET_ERR_MSG(extack, "if_id must be non zero"); + return -EINVAL; + } + + if (p.collect_md) { + NL_SET_ERR_MSG(extack, "collect_md can't be changed"); + return -EINVAL; + } + + xi = xfrmi_locate(net, &p); + if (!xi) { + xi = netdev_priv(dev); + } else { + if (xi->dev != dev) + return -EEXIST; + if (xi->p.collect_md) { + NL_SET_ERR_MSG(extack, + "device can't be changed to collect_md"); + return -EINVAL; + } + } + + return xfrmi_update(xi, &p); +} + +static size_t xfrmi_get_size(const struct net_device *dev) +{ + return + /* IFLA_XFRM_LINK */ + nla_total_size(4) + + /* IFLA_XFRM_IF_ID */ + nla_total_size(4) + + /* IFLA_XFRM_COLLECT_METADATA */ + nla_total_size(0) + + 0; +} + +static int xfrmi_fill_info(struct sk_buff *skb, const struct net_device *dev) +{ + struct xfrm_if *xi = netdev_priv(dev); + struct xfrm_if_parms *parm = &xi->p; + + if (nla_put_u32(skb, IFLA_XFRM_LINK, parm->link) || + nla_put_u32(skb, IFLA_XFRM_IF_ID, parm->if_id) || + (xi->p.collect_md && nla_put_flag(skb, IFLA_XFRM_COLLECT_METADATA))) + goto nla_put_failure; + return 0; + +nla_put_failure: + return -EMSGSIZE; +} + +static struct net *xfrmi_get_link_net(const struct net_device *dev) +{ + struct xfrm_if *xi = netdev_priv(dev); + + return xi->net; +} + +static const struct nla_policy xfrmi_policy[IFLA_XFRM_MAX + 1] = { + [IFLA_XFRM_UNSPEC] = { .strict_start_type = IFLA_XFRM_COLLECT_METADATA }, + [IFLA_XFRM_LINK] = { .type = NLA_U32 }, + [IFLA_XFRM_IF_ID] = { .type = NLA_U32 }, + [IFLA_XFRM_COLLECT_METADATA] = { .type = NLA_FLAG }, +}; + +static struct rtnl_link_ops xfrmi_link_ops __read_mostly = { + .kind = "xfrm", + .maxtype = IFLA_XFRM_MAX, + .policy = xfrmi_policy, + .priv_size = sizeof(struct xfrm_if), + .setup = xfrmi_dev_setup, + .validate = xfrmi_validate, + .newlink = xfrmi_newlink, + .dellink = xfrmi_dellink, + .changelink = xfrmi_changelink, + .get_size = xfrmi_get_size, + .fill_info = xfrmi_fill_info, + .get_link_net = xfrmi_get_link_net, +}; + +static void __net_exit xfrmi_exit_batch_net(struct list_head *net_exit_list) +{ + struct net *net; + LIST_HEAD(list); + + rtnl_lock(); + list_for_each_entry(net, net_exit_list, exit_list) { + struct xfrmi_net *xfrmn = net_generic(net, xfrmi_net_id); + struct xfrm_if __rcu **xip; + struct xfrm_if *xi; + int i; + + for (i = 0; i < XFRMI_HASH_SIZE; i++) { + for (xip = &xfrmn->xfrmi[i]; + (xi = rtnl_dereference(*xip)) != NULL; + xip = &xi->next) + unregister_netdevice_queue(xi->dev, &list); + } + xi = rtnl_dereference(xfrmn->collect_md_xfrmi); + if (xi) + unregister_netdevice_queue(xi->dev, &list); + } + unregister_netdevice_many(&list); + rtnl_unlock(); +} + +static struct pernet_operations xfrmi_net_ops = { + .exit_batch = xfrmi_exit_batch_net, + .id = &xfrmi_net_id, + .size = sizeof(struct xfrmi_net), +}; + +static struct xfrm6_protocol xfrmi_esp6_protocol __read_mostly = { + .handler = xfrmi6_rcv, + .input_handler = xfrmi6_input, + .cb_handler = xfrmi_rcv_cb, + .err_handler = xfrmi6_err, + .priority = 10, +}; + +static struct xfrm6_protocol xfrmi_ah6_protocol __read_mostly = { + .handler = xfrm6_rcv, + .input_handler = xfrm_input, + .cb_handler = xfrmi_rcv_cb, + .err_handler = xfrmi6_err, + .priority = 10, +}; + +static struct xfrm6_protocol xfrmi_ipcomp6_protocol __read_mostly = { + .handler = xfrm6_rcv, + .input_handler = xfrm_input, + .cb_handler = xfrmi_rcv_cb, + .err_handler = xfrmi6_err, + .priority = 10, +}; + +#if IS_REACHABLE(CONFIG_INET6_XFRM_TUNNEL) +static int xfrmi6_rcv_tunnel(struct sk_buff *skb) +{ + const xfrm_address_t *saddr; + __be32 spi; + + saddr = (const xfrm_address_t *)&ipv6_hdr(skb)->saddr; + spi = xfrm6_tunnel_spi_lookup(dev_net(skb->dev), saddr); + + return xfrm6_rcv_spi(skb, IPPROTO_IPV6, spi, NULL); +} + +static struct xfrm6_tunnel xfrmi_ipv6_handler __read_mostly = { + .handler = xfrmi6_rcv_tunnel, + .cb_handler = xfrmi_rcv_cb, + .err_handler = xfrmi6_err, + .priority = 2, +}; + +static struct xfrm6_tunnel xfrmi_ip6ip_handler __read_mostly = { + .handler = xfrmi6_rcv_tunnel, + .cb_handler = xfrmi_rcv_cb, + .err_handler = xfrmi6_err, + .priority = 2, +}; +#endif + +static struct xfrm4_protocol xfrmi_esp4_protocol __read_mostly = { + .handler = xfrmi4_rcv, + .input_handler = xfrmi4_input, + .cb_handler = xfrmi_rcv_cb, + .err_handler = xfrmi4_err, + .priority = 10, +}; + +static struct xfrm4_protocol xfrmi_ah4_protocol __read_mostly = { + .handler = xfrm4_rcv, + .input_handler = xfrm_input, + .cb_handler = xfrmi_rcv_cb, + .err_handler = xfrmi4_err, + .priority = 10, +}; + +static struct xfrm4_protocol xfrmi_ipcomp4_protocol __read_mostly = { + .handler = xfrm4_rcv, + .input_handler = xfrm_input, + .cb_handler = xfrmi_rcv_cb, + .err_handler = xfrmi4_err, + .priority = 10, +}; + +#if IS_REACHABLE(CONFIG_INET_XFRM_TUNNEL) +static int xfrmi4_rcv_tunnel(struct sk_buff *skb) +{ + return xfrm4_rcv_spi(skb, IPPROTO_IPIP, ip_hdr(skb)->saddr); +} + +static struct xfrm_tunnel xfrmi_ipip_handler __read_mostly = { + .handler = xfrmi4_rcv_tunnel, + .cb_handler = xfrmi_rcv_cb, + .err_handler = xfrmi4_err, + .priority = 3, +}; + +static struct xfrm_tunnel xfrmi_ipip6_handler __read_mostly = { + .handler = xfrmi4_rcv_tunnel, + .cb_handler = xfrmi_rcv_cb, + .err_handler = xfrmi4_err, + .priority = 2, +}; +#endif + +static int __init xfrmi4_init(void) +{ + int err; + + err = xfrm4_protocol_register(&xfrmi_esp4_protocol, IPPROTO_ESP); + if (err < 0) + goto xfrm_proto_esp_failed; + err = xfrm4_protocol_register(&xfrmi_ah4_protocol, IPPROTO_AH); + if (err < 0) + goto xfrm_proto_ah_failed; + err = xfrm4_protocol_register(&xfrmi_ipcomp4_protocol, IPPROTO_COMP); + if (err < 0) + goto xfrm_proto_comp_failed; +#if IS_REACHABLE(CONFIG_INET_XFRM_TUNNEL) + err = xfrm4_tunnel_register(&xfrmi_ipip_handler, AF_INET); + if (err < 0) + goto xfrm_tunnel_ipip_failed; + err = xfrm4_tunnel_register(&xfrmi_ipip6_handler, AF_INET6); + if (err < 0) + goto xfrm_tunnel_ipip6_failed; +#endif + + return 0; + +#if IS_REACHABLE(CONFIG_INET_XFRM_TUNNEL) +xfrm_tunnel_ipip6_failed: + xfrm4_tunnel_deregister(&xfrmi_ipip_handler, AF_INET); +xfrm_tunnel_ipip_failed: + xfrm4_protocol_deregister(&xfrmi_ipcomp4_protocol, IPPROTO_COMP); +#endif +xfrm_proto_comp_failed: + xfrm4_protocol_deregister(&xfrmi_ah4_protocol, IPPROTO_AH); +xfrm_proto_ah_failed: + xfrm4_protocol_deregister(&xfrmi_esp4_protocol, IPPROTO_ESP); +xfrm_proto_esp_failed: + return err; +} + +static void xfrmi4_fini(void) +{ +#if IS_REACHABLE(CONFIG_INET_XFRM_TUNNEL) + xfrm4_tunnel_deregister(&xfrmi_ipip6_handler, AF_INET6); + xfrm4_tunnel_deregister(&xfrmi_ipip_handler, AF_INET); +#endif + xfrm4_protocol_deregister(&xfrmi_ipcomp4_protocol, IPPROTO_COMP); + xfrm4_protocol_deregister(&xfrmi_ah4_protocol, IPPROTO_AH); + xfrm4_protocol_deregister(&xfrmi_esp4_protocol, IPPROTO_ESP); +} + +static int __init xfrmi6_init(void) +{ + int err; + + err = xfrm6_protocol_register(&xfrmi_esp6_protocol, IPPROTO_ESP); + if (err < 0) + goto xfrm_proto_esp_failed; + err = xfrm6_protocol_register(&xfrmi_ah6_protocol, IPPROTO_AH); + if (err < 0) + goto xfrm_proto_ah_failed; + err = xfrm6_protocol_register(&xfrmi_ipcomp6_protocol, IPPROTO_COMP); + if (err < 0) + goto xfrm_proto_comp_failed; +#if IS_REACHABLE(CONFIG_INET6_XFRM_TUNNEL) + err = xfrm6_tunnel_register(&xfrmi_ipv6_handler, AF_INET6); + if (err < 0) + goto xfrm_tunnel_ipv6_failed; + err = xfrm6_tunnel_register(&xfrmi_ip6ip_handler, AF_INET); + if (err < 0) + goto xfrm_tunnel_ip6ip_failed; +#endif + + return 0; + +#if IS_REACHABLE(CONFIG_INET6_XFRM_TUNNEL) +xfrm_tunnel_ip6ip_failed: + xfrm6_tunnel_deregister(&xfrmi_ipv6_handler, AF_INET6); +xfrm_tunnel_ipv6_failed: + xfrm6_protocol_deregister(&xfrmi_ipcomp6_protocol, IPPROTO_COMP); +#endif +xfrm_proto_comp_failed: + xfrm6_protocol_deregister(&xfrmi_ah6_protocol, IPPROTO_AH); +xfrm_proto_ah_failed: + xfrm6_protocol_deregister(&xfrmi_esp6_protocol, IPPROTO_ESP); +xfrm_proto_esp_failed: + return err; +} + +static void xfrmi6_fini(void) +{ +#if IS_REACHABLE(CONFIG_INET6_XFRM_TUNNEL) + xfrm6_tunnel_deregister(&xfrmi_ip6ip_handler, AF_INET); + xfrm6_tunnel_deregister(&xfrmi_ipv6_handler, AF_INET6); +#endif + xfrm6_protocol_deregister(&xfrmi_ipcomp6_protocol, IPPROTO_COMP); + xfrm6_protocol_deregister(&xfrmi_ah6_protocol, IPPROTO_AH); + xfrm6_protocol_deregister(&xfrmi_esp6_protocol, IPPROTO_ESP); +} + +static const struct xfrm_if_cb xfrm_if_cb = { + .decode_session = xfrmi_decode_session, +}; + +static int __init xfrmi_init(void) +{ + const char *msg; + int err; + + pr_info("IPsec XFRM device driver\n"); + + msg = "tunnel device"; + err = register_pernet_device(&xfrmi_net_ops); + if (err < 0) + goto pernet_dev_failed; + + msg = "xfrm4 protocols"; + err = xfrmi4_init(); + if (err < 0) + goto xfrmi4_failed; + + msg = "xfrm6 protocols"; + err = xfrmi6_init(); + if (err < 0) + goto xfrmi6_failed; + + + msg = "netlink interface"; + err = rtnl_link_register(&xfrmi_link_ops); + if (err < 0) + goto rtnl_link_failed; + + lwtunnel_encap_add_ops(&xfrmi_encap_ops, LWTUNNEL_ENCAP_XFRM); + + xfrm_if_register_cb(&xfrm_if_cb); + + return err; + +rtnl_link_failed: + xfrmi6_fini(); +xfrmi6_failed: + xfrmi4_fini(); +xfrmi4_failed: + unregister_pernet_device(&xfrmi_net_ops); +pernet_dev_failed: + pr_err("xfrmi init: failed to register %s\n", msg); + return err; +} + +static void __exit xfrmi_fini(void) +{ + xfrm_if_unregister_cb(); + lwtunnel_encap_del_ops(&xfrmi_encap_ops, LWTUNNEL_ENCAP_XFRM); + rtnl_link_unregister(&xfrmi_link_ops); + xfrmi4_fini(); + xfrmi6_fini(); + unregister_pernet_device(&xfrmi_net_ops); +} + +module_init(xfrmi_init); +module_exit(xfrmi_fini); +MODULE_LICENSE("GPL"); +MODULE_ALIAS_RTNL_LINK("xfrm"); +MODULE_ALIAS_NETDEV("xfrm0"); +MODULE_AUTHOR("Steffen Klassert"); +MODULE_DESCRIPTION("XFRM virtual interface"); diff --git a/net/xfrm/xfrm_policy.c b/net/xfrm/xfrm_policy.c index e894c269affb1..7b1b93584bdbe 100644 --- a/net/xfrm/xfrm_policy.c +++ b/net/xfrm/xfrm_policy.c @@ -3274,6 +3274,13 @@ xfrm_policy_ok(const struct xfrm_tmpl *tmpl, const struct sec_path *sp, int star if (xfrm_state_ok(tmpl, sp->xvec[idx], family, if_id)) return ++idx; if (sp->xvec[idx]->props.mode != XFRM_MODE_TRANSPORT) { + if (idx < sp->verified_cnt) { + /* Secpath entry previously verified, consider optional and + * continue searching + */ + continue; + } + if (start == -1) start = -2-idx; break; @@ -3648,6 +3655,9 @@ int __xfrm_policy_check(struct sock *sk, int dir, struct sk_buff *skb, * Order is _important_. Later we will implement * some barriers, but at the moment barriers * are implied between each two transformations. + * Upon success, marks secpath entries as having been + * verified to allow them to be skipped in future policy + * checks (e.g. nested tunnels). */ for (i = xfrm_nr-1, k = 0; i >= 0; i--) { k = xfrm_policy_ok(tpp[i], sp, k, family, if_id); @@ -3666,6 +3676,8 @@ int __xfrm_policy_check(struct sock *sk, int dir, struct sk_buff *skb, } xfrm_pols_put(pols, npols); + sp->verified_cnt = k; + return 1; } XFRM_INC_STATS(net, LINUX_MIB_XFRMINPOLBLOCK); diff --git a/scripts/gfp-translate b/scripts/gfp-translate index b2ce416d944b3..6c9aed17cf563 100755 --- a/scripts/gfp-translate +++ b/scripts/gfp-translate @@ -63,11 +63,11 @@ fi # Extract GFP flags from the kernel source TMPFILE=`mktemp -t gfptranslate-XXXXXX` || exit 1 -grep -q ___GFP $SOURCE/include/linux/gfp.h +grep -q ___GFP $SOURCE/include/linux/gfp_types.h if [ $? -eq 0 ]; then - grep "^#define ___GFP" $SOURCE/include/linux/gfp.h | sed -e 's/u$//' | grep -v GFP_BITS > $TMPFILE + grep "^#define ___GFP" $SOURCE/include/linux/gfp_types.h | sed -e 's/u$//' | grep -v GFP_BITS > $TMPFILE else - grep "^#define __GFP" $SOURCE/include/linux/gfp.h | sed -e 's/(__force gfp_t)//' | sed -e 's/u)/)/' | grep -v GFP_BITS | sed -e 's/)\//) \//' > $TMPFILE + grep "^#define __GFP" $SOURCE/include/linux/gfp_types.h | sed -e 's/(__force gfp_t)//' | sed -e 's/u)/)/' | grep -v GFP_BITS | sed -e 's/)\//) \//' > $TMPFILE fi # Parse the flags diff --git a/sound/pci/hda/patch_realtek.c b/sound/pci/hda/patch_realtek.c index 920e44ba998a5..eb049014f87ac 100644 --- a/sound/pci/hda/patch_realtek.c +++ b/sound/pci/hda/patch_realtek.c @@ -9594,6 +9594,7 @@ static const struct snd_pci_quirk alc269_fixup_tbl[] = { SND_PCI_QUIRK(0x10ec, 0x124c, "Intel Reference board", ALC295_FIXUP_CHROME_BOOK), SND_PCI_QUIRK(0x10ec, 0x1252, "Intel Reference board", ALC295_FIXUP_CHROME_BOOK), SND_PCI_QUIRK(0x10ec, 0x1254, "Intel Reference board", ALC295_FIXUP_CHROME_BOOK), + SND_PCI_QUIRK(0x10ec, 0x12cc, "Intel Reference board", ALC225_FIXUP_HEADSET_JACK), SND_PCI_QUIRK(0x10f7, 0x8338, "Panasonic CF-SZ6", ALC269_FIXUP_HEADSET_MODE), SND_PCI_QUIRK(0x144d, 0xc109, "Samsung Ativ book 9 (NP900X3G)", ALC269_FIXUP_INV_DMIC), SND_PCI_QUIRK(0x144d, 0xc169, "Samsung Notebook 9 Pen (NP930SBE-K01US)", ALC298_FIXUP_SAMSUNG_AMP), @@ -9814,6 +9815,7 @@ static const struct snd_pci_quirk alc269_fixup_tbl[] = { SND_PCI_QUIRK(0x8086, 0x2074, "Intel NUC 8", ALC233_FIXUP_INTEL_NUC8_DMIC), SND_PCI_QUIRK(0x8086, 0x2080, "Intel NUC 8 Rugged", ALC256_FIXUP_INTEL_NUC8_RUGGED), SND_PCI_QUIRK(0x8086, 0x2081, "Intel NUC 10", ALC256_FIXUP_INTEL_NUC10), + SND_PCI_QUIRK(0x8086, 0x3038, "Intel NUC 13", ALC225_FIXUP_HEADSET_JACK), SND_PCI_QUIRK(0xf111, 0x0001, "Framework Laptop", ALC295_FIXUP_FRAMEWORK_LAPTOP_MIC_NO_PRESENCE), #if 0 diff --git a/sound/soc/amd/yc/acp6x-mach.c b/sound/soc/amd/yc/acp6x-mach.c index 84b401b685f7f..c1ca3ceac5f2f 100644 --- a/sound/soc/amd/yc/acp6x-mach.c +++ b/sound/soc/amd/yc/acp6x-mach.c @@ -171,6 +171,13 @@ static const struct dmi_system_id yc_acp_quirk_table[] = { DMI_MATCH(DMI_PRODUCT_NAME, "21CL"), } }, + { + .driver_data = &acp6x_card, + .matches = { + DMI_MATCH(DMI_BOARD_VENDOR, "LENOVO"), + DMI_MATCH(DMI_PRODUCT_NAME, "21EF"), + } + }, { .driver_data = &acp6x_card, .matches = { diff --git a/sound/soc/codecs/nau8824.c b/sound/soc/codecs/nau8824.c index 4f19fd9b65d11..5a4db8944d06a 100644 --- a/sound/soc/codecs/nau8824.c +++ b/sound/soc/codecs/nau8824.c @@ -1903,6 +1903,30 @@ static const struct dmi_system_id nau8824_quirk_table[] = { }, .driver_data = (void *)(NAU8824_MONO_SPEAKER), }, + { + /* Positivo CW14Q01P */ + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "Positivo Tecnologia SA"), + DMI_MATCH(DMI_BOARD_NAME, "CW14Q01P"), + }, + .driver_data = (void *)(NAU8824_JD_ACTIVE_HIGH), + }, + { + /* Positivo K1424G */ + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "Positivo Tecnologia SA"), + DMI_MATCH(DMI_BOARD_NAME, "K1424G"), + }, + .driver_data = (void *)(NAU8824_JD_ACTIVE_HIGH), + }, + { + /* Positivo N14ZP74G */ + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "Positivo Tecnologia SA"), + DMI_MATCH(DMI_BOARD_NAME, "N14ZP74G"), + }, + .driver_data = (void *)(NAU8824_JD_ACTIVE_HIGH), + }, {} }; diff --git a/sound/soc/codecs/wcd938x-sdw.c b/sound/soc/codecs/wcd938x-sdw.c index 402286dfaea44..9c10200ff34b2 100644 --- a/sound/soc/codecs/wcd938x-sdw.c +++ b/sound/soc/codecs/wcd938x-sdw.c @@ -1190,7 +1190,6 @@ static const struct regmap_config wcd938x_regmap_config = { .readable_reg = wcd938x_readable_register, .writeable_reg = wcd938x_writeable_register, .volatile_reg = wcd938x_volatile_register, - .can_multi_write = true, }; static const struct sdw_slave_ops wcd9380_slave_ops = { diff --git a/sound/soc/fsl/fsl_sai.c b/sound/soc/fsl/fsl_sai.c index 6d88af5b287fe..b33104715c7ba 100644 --- a/sound/soc/fsl/fsl_sai.c +++ b/sound/soc/fsl/fsl_sai.c @@ -491,14 +491,21 @@ static int fsl_sai_set_bclk(struct snd_soc_dai *dai, bool tx, u32 freq) regmap_update_bits(sai->regmap, reg, FSL_SAI_CR2_MSEL_MASK, FSL_SAI_CR2_MSEL(sai->mclk_id[tx])); - if (savediv == 1) + if (savediv == 1) { regmap_update_bits(sai->regmap, reg, FSL_SAI_CR2_DIV_MASK | FSL_SAI_CR2_BYP, FSL_SAI_CR2_BYP); - else + if (fsl_sai_dir_is_synced(sai, adir)) + regmap_update_bits(sai->regmap, FSL_SAI_xCR2(tx, ofs), + FSL_SAI_CR2_BCI, FSL_SAI_CR2_BCI); + else + regmap_update_bits(sai->regmap, FSL_SAI_xCR2(tx, ofs), + FSL_SAI_CR2_BCI, 0); + } else { regmap_update_bits(sai->regmap, reg, FSL_SAI_CR2_DIV_MASK | FSL_SAI_CR2_BYP, savediv / 2 - 1); + } if (sai->soc_data->max_register >= FSL_SAI_MCTL) { /* SAI is in master mode at this point, so enable MCLK */ diff --git a/sound/soc/fsl/fsl_sai.h b/sound/soc/fsl/fsl_sai.h index 697f6690068c8..c5423f81e4560 100644 --- a/sound/soc/fsl/fsl_sai.h +++ b/sound/soc/fsl/fsl_sai.h @@ -116,6 +116,7 @@ /* SAI Transmit and Receive Configuration 2 Register */ #define FSL_SAI_CR2_SYNC BIT(30) +#define FSL_SAI_CR2_BCI BIT(28) #define FSL_SAI_CR2_MSEL_MASK (0x3 << 26) #define FSL_SAI_CR2_MSEL_BUS 0 #define FSL_SAI_CR2_MSEL_MCLK1 BIT(26) diff --git a/sound/soc/generic/simple-card.c b/sound/soc/generic/simple-card.c index feb55b66239b8..fbb682747f598 100644 --- a/sound/soc/generic/simple-card.c +++ b/sound/soc/generic/simple-card.c @@ -416,6 +416,7 @@ static int __simple_for_each_link(struct asoc_simple_priv *priv, if (ret < 0) { of_node_put(codec); + of_node_put(plat); of_node_put(np); goto error; } diff --git a/tools/testing/selftests/bpf/verifier/bounds_mix_sign_unsign.c b/tools/testing/selftests/bpf/verifier/bounds_mix_sign_unsign.c index c2aa6f26738b4..bf82b923c5fe5 100644 --- a/tools/testing/selftests/bpf/verifier/bounds_mix_sign_unsign.c +++ b/tools/testing/selftests/bpf/verifier/bounds_mix_sign_unsign.c @@ -1,13 +1,14 @@ { "bounds checks mixing signed and unsigned, positive bounds", .insns = { + BPF_EMIT_CALL(BPF_FUNC_ktime_get_ns), + BPF_STX_MEM(BPF_DW, BPF_REG_10, BPF_REG_0, -16), BPF_ST_MEM(BPF_DW, BPF_REG_10, -8, 0), BPF_MOV64_REG(BPF_REG_2, BPF_REG_10), BPF_ALU64_IMM(BPF_ADD, BPF_REG_2, -8), BPF_LD_MAP_FD(BPF_REG_1, 0), BPF_RAW_INSN(BPF_JMP | BPF_CALL, 0, 0, 0, BPF_FUNC_map_lookup_elem), - BPF_JMP_IMM(BPF_JEQ, BPF_REG_0, 0, 7), - BPF_ST_MEM(BPF_DW, BPF_REG_10, -16, -8), + BPF_JMP_IMM(BPF_JEQ, BPF_REG_0, 0, 6), BPF_LDX_MEM(BPF_DW, BPF_REG_1, BPF_REG_10, -16), BPF_MOV64_IMM(BPF_REG_2, 2), BPF_JMP_REG(BPF_JGE, BPF_REG_2, BPF_REG_1, 3), @@ -17,20 +18,21 @@ BPF_MOV64_IMM(BPF_REG_0, 0), BPF_EXIT_INSN(), }, - .fixup_map_hash_8b = { 3 }, + .fixup_map_hash_8b = { 5 }, .errstr = "unbounded min value", .result = REJECT, }, { "bounds checks mixing signed and unsigned", .insns = { + BPF_EMIT_CALL(BPF_FUNC_ktime_get_ns), + BPF_STX_MEM(BPF_DW, BPF_REG_10, BPF_REG_0, -16), BPF_ST_MEM(BPF_DW, BPF_REG_10, -8, 0), BPF_MOV64_REG(BPF_REG_2, BPF_REG_10), BPF_ALU64_IMM(BPF_ADD, BPF_REG_2, -8), BPF_LD_MAP_FD(BPF_REG_1, 0), BPF_RAW_INSN(BPF_JMP | BPF_CALL, 0, 0, 0, BPF_FUNC_map_lookup_elem), - BPF_JMP_IMM(BPF_JEQ, BPF_REG_0, 0, 7), - BPF_ST_MEM(BPF_DW, BPF_REG_10, -16, -8), + BPF_JMP_IMM(BPF_JEQ, BPF_REG_0, 0, 6), BPF_LDX_MEM(BPF_DW, BPF_REG_1, BPF_REG_10, -16), BPF_MOV64_IMM(BPF_REG_2, -1), BPF_JMP_REG(BPF_JGT, BPF_REG_1, BPF_REG_2, 3), @@ -40,20 +42,21 @@ BPF_MOV64_IMM(BPF_REG_0, 0), BPF_EXIT_INSN(), }, - .fixup_map_hash_8b = { 3 }, + .fixup_map_hash_8b = { 5 }, .errstr = "unbounded min value", .result = REJECT, }, { "bounds checks mixing signed and unsigned, variant 2", .insns = { + BPF_EMIT_CALL(BPF_FUNC_ktime_get_ns), + BPF_STX_MEM(BPF_DW, BPF_REG_10, BPF_REG_0, -16), BPF_ST_MEM(BPF_DW, BPF_REG_10, -8, 0), BPF_MOV64_REG(BPF_REG_2, BPF_REG_10), BPF_ALU64_IMM(BPF_ADD, BPF_REG_2, -8), BPF_LD_MAP_FD(BPF_REG_1, 0), BPF_RAW_INSN(BPF_JMP | BPF_CALL, 0, 0, 0, BPF_FUNC_map_lookup_elem), - BPF_JMP_IMM(BPF_JEQ, BPF_REG_0, 0, 9), - BPF_ST_MEM(BPF_DW, BPF_REG_10, -16, -8), + BPF_JMP_IMM(BPF_JEQ, BPF_REG_0, 0, 8), BPF_LDX_MEM(BPF_DW, BPF_REG_1, BPF_REG_10, -16), BPF_MOV64_IMM(BPF_REG_2, -1), BPF_JMP_REG(BPF_JGT, BPF_REG_1, BPF_REG_2, 5), @@ -65,20 +68,21 @@ BPF_MOV64_IMM(BPF_REG_0, 0), BPF_EXIT_INSN(), }, - .fixup_map_hash_8b = { 3 }, + .fixup_map_hash_8b = { 5 }, .errstr = "unbounded min value", .result = REJECT, }, { "bounds checks mixing signed and unsigned, variant 3", .insns = { + BPF_EMIT_CALL(BPF_FUNC_ktime_get_ns), + BPF_STX_MEM(BPF_DW, BPF_REG_10, BPF_REG_0, -16), BPF_ST_MEM(BPF_DW, BPF_REG_10, -8, 0), BPF_MOV64_REG(BPF_REG_2, BPF_REG_10), BPF_ALU64_IMM(BPF_ADD, BPF_REG_2, -8), BPF_LD_MAP_FD(BPF_REG_1, 0), BPF_RAW_INSN(BPF_JMP | BPF_CALL, 0, 0, 0, BPF_FUNC_map_lookup_elem), - BPF_JMP_IMM(BPF_JEQ, BPF_REG_0, 0, 8), - BPF_ST_MEM(BPF_DW, BPF_REG_10, -16, -8), + BPF_JMP_IMM(BPF_JEQ, BPF_REG_0, 0, 7), BPF_LDX_MEM(BPF_DW, BPF_REG_1, BPF_REG_10, -16), BPF_MOV64_IMM(BPF_REG_2, -1), BPF_JMP_REG(BPF_JGT, BPF_REG_1, BPF_REG_2, 4), @@ -89,20 +93,21 @@ BPF_MOV64_IMM(BPF_REG_0, 0), BPF_EXIT_INSN(), }, - .fixup_map_hash_8b = { 3 }, + .fixup_map_hash_8b = { 5 }, .errstr = "unbounded min value", .result = REJECT, }, { "bounds checks mixing signed and unsigned, variant 4", .insns = { + BPF_EMIT_CALL(BPF_FUNC_ktime_get_ns), + BPF_STX_MEM(BPF_DW, BPF_REG_10, BPF_REG_0, -16), BPF_ST_MEM(BPF_DW, BPF_REG_10, -8, 0), BPF_MOV64_REG(BPF_REG_2, BPF_REG_10), BPF_ALU64_IMM(BPF_ADD, BPF_REG_2, -8), BPF_LD_MAP_FD(BPF_REG_1, 0), BPF_RAW_INSN(BPF_JMP | BPF_CALL, 0, 0, 0, BPF_FUNC_map_lookup_elem), - BPF_JMP_IMM(BPF_JEQ, BPF_REG_0, 0, 7), - BPF_ST_MEM(BPF_DW, BPF_REG_10, -16, -8), + BPF_JMP_IMM(BPF_JEQ, BPF_REG_0, 0, 6), BPF_LDX_MEM(BPF_DW, BPF_REG_1, BPF_REG_10, -16), BPF_MOV64_IMM(BPF_REG_2, 1), BPF_ALU64_REG(BPF_AND, BPF_REG_1, BPF_REG_2), @@ -112,19 +117,20 @@ BPF_MOV64_IMM(BPF_REG_0, 0), BPF_EXIT_INSN(), }, - .fixup_map_hash_8b = { 3 }, + .fixup_map_hash_8b = { 5 }, .result = ACCEPT, }, { "bounds checks mixing signed and unsigned, variant 5", .insns = { + BPF_EMIT_CALL(BPF_FUNC_ktime_get_ns), + BPF_STX_MEM(BPF_DW, BPF_REG_10, BPF_REG_0, -16), BPF_ST_MEM(BPF_DW, BPF_REG_10, -8, 0), BPF_MOV64_REG(BPF_REG_2, BPF_REG_10), BPF_ALU64_IMM(BPF_ADD, BPF_REG_2, -8), BPF_LD_MAP_FD(BPF_REG_1, 0), BPF_RAW_INSN(BPF_JMP | BPF_CALL, 0, 0, 0, BPF_FUNC_map_lookup_elem), - BPF_JMP_IMM(BPF_JEQ, BPF_REG_0, 0, 9), - BPF_ST_MEM(BPF_DW, BPF_REG_10, -16, -8), + BPF_JMP_IMM(BPF_JEQ, BPF_REG_0, 0, 8), BPF_LDX_MEM(BPF_DW, BPF_REG_1, BPF_REG_10, -16), BPF_MOV64_IMM(BPF_REG_2, -1), BPF_JMP_REG(BPF_JGT, BPF_REG_1, BPF_REG_2, 5), @@ -135,17 +141,20 @@ BPF_MOV64_IMM(BPF_REG_0, 0), BPF_EXIT_INSN(), }, - .fixup_map_hash_8b = { 3 }, + .fixup_map_hash_8b = { 5 }, .errstr = "unbounded min value", .result = REJECT, }, { "bounds checks mixing signed and unsigned, variant 6", .insns = { + BPF_MOV64_REG(BPF_REG_9, BPF_REG_1), + BPF_EMIT_CALL(BPF_FUNC_ktime_get_ns), + BPF_STX_MEM(BPF_DW, BPF_REG_10, BPF_REG_0, -16), + BPF_MOV64_REG(BPF_REG_1, BPF_REG_9), BPF_MOV64_IMM(BPF_REG_2, 0), BPF_MOV64_REG(BPF_REG_3, BPF_REG_10), BPF_ALU64_IMM(BPF_ADD, BPF_REG_3, -512), - BPF_ST_MEM(BPF_DW, BPF_REG_10, -16, -8), BPF_LDX_MEM(BPF_DW, BPF_REG_4, BPF_REG_10, -16), BPF_MOV64_IMM(BPF_REG_6, -1), BPF_JMP_REG(BPF_JGT, BPF_REG_4, BPF_REG_6, 5), @@ -163,13 +172,14 @@ { "bounds checks mixing signed and unsigned, variant 7", .insns = { + BPF_EMIT_CALL(BPF_FUNC_ktime_get_ns), + BPF_STX_MEM(BPF_DW, BPF_REG_10, BPF_REG_0, -16), BPF_ST_MEM(BPF_DW, BPF_REG_10, -8, 0), BPF_MOV64_REG(BPF_REG_2, BPF_REG_10), BPF_ALU64_IMM(BPF_ADD, BPF_REG_2, -8), BPF_LD_MAP_FD(BPF_REG_1, 0), BPF_RAW_INSN(BPF_JMP | BPF_CALL, 0, 0, 0, BPF_FUNC_map_lookup_elem), - BPF_JMP_IMM(BPF_JEQ, BPF_REG_0, 0, 7), - BPF_ST_MEM(BPF_DW, BPF_REG_10, -16, -8), + BPF_JMP_IMM(BPF_JEQ, BPF_REG_0, 0, 6), BPF_LDX_MEM(BPF_DW, BPF_REG_1, BPF_REG_10, -16), BPF_MOV64_IMM(BPF_REG_2, 1024 * 1024 * 1024), BPF_JMP_REG(BPF_JGT, BPF_REG_1, BPF_REG_2, 3), @@ -179,19 +189,20 @@ BPF_MOV64_IMM(BPF_REG_0, 0), BPF_EXIT_INSN(), }, - .fixup_map_hash_8b = { 3 }, + .fixup_map_hash_8b = { 5 }, .result = ACCEPT, }, { "bounds checks mixing signed and unsigned, variant 8", .insns = { + BPF_EMIT_CALL(BPF_FUNC_ktime_get_ns), + BPF_STX_MEM(BPF_DW, BPF_REG_10, BPF_REG_0, -16), BPF_ST_MEM(BPF_DW, BPF_REG_10, -8, 0), BPF_MOV64_REG(BPF_REG_2, BPF_REG_10), BPF_ALU64_IMM(BPF_ADD, BPF_REG_2, -8), BPF_LD_MAP_FD(BPF_REG_1, 0), BPF_RAW_INSN(BPF_JMP | BPF_CALL, 0, 0, 0, BPF_FUNC_map_lookup_elem), - BPF_JMP_IMM(BPF_JEQ, BPF_REG_0, 0, 9), - BPF_ST_MEM(BPF_DW, BPF_REG_10, -16, -8), + BPF_JMP_IMM(BPF_JEQ, BPF_REG_0, 0, 8), BPF_LDX_MEM(BPF_DW, BPF_REG_1, BPF_REG_10, -16), BPF_MOV64_IMM(BPF_REG_2, -1), BPF_JMP_REG(BPF_JGT, BPF_REG_2, BPF_REG_1, 2), @@ -203,20 +214,21 @@ BPF_MOV64_IMM(BPF_REG_0, 0), BPF_EXIT_INSN(), }, - .fixup_map_hash_8b = { 3 }, + .fixup_map_hash_8b = { 5 }, .errstr = "unbounded min value", .result = REJECT, }, { "bounds checks mixing signed and unsigned, variant 9", .insns = { + BPF_EMIT_CALL(BPF_FUNC_ktime_get_ns), + BPF_STX_MEM(BPF_DW, BPF_REG_10, BPF_REG_0, -16), BPF_ST_MEM(BPF_DW, BPF_REG_10, -8, 0), BPF_MOV64_REG(BPF_REG_2, BPF_REG_10), BPF_ALU64_IMM(BPF_ADD, BPF_REG_2, -8), BPF_LD_MAP_FD(BPF_REG_1, 0), BPF_RAW_INSN(BPF_JMP | BPF_CALL, 0, 0, 0, BPF_FUNC_map_lookup_elem), - BPF_JMP_IMM(BPF_JEQ, BPF_REG_0, 0, 10), - BPF_ST_MEM(BPF_DW, BPF_REG_10, -16, -8), + BPF_JMP_IMM(BPF_JEQ, BPF_REG_0, 0, 9), BPF_LDX_MEM(BPF_DW, BPF_REG_1, BPF_REG_10, -16), BPF_LD_IMM64(BPF_REG_2, -9223372036854775808ULL), BPF_JMP_REG(BPF_JGT, BPF_REG_2, BPF_REG_1, 2), @@ -228,19 +240,20 @@ BPF_MOV64_IMM(BPF_REG_0, 0), BPF_EXIT_INSN(), }, - .fixup_map_hash_8b = { 3 }, + .fixup_map_hash_8b = { 5 }, .result = ACCEPT, }, { "bounds checks mixing signed and unsigned, variant 10", .insns = { + BPF_EMIT_CALL(BPF_FUNC_ktime_get_ns), + BPF_STX_MEM(BPF_DW, BPF_REG_10, BPF_REG_0, -16), BPF_ST_MEM(BPF_DW, BPF_REG_10, -8, 0), BPF_MOV64_REG(BPF_REG_2, BPF_REG_10), BPF_ALU64_IMM(BPF_ADD, BPF_REG_2, -8), BPF_LD_MAP_FD(BPF_REG_1, 0), BPF_RAW_INSN(BPF_JMP | BPF_CALL, 0, 0, 0, BPF_FUNC_map_lookup_elem), - BPF_JMP_IMM(BPF_JEQ, BPF_REG_0, 0, 9), - BPF_ST_MEM(BPF_DW, BPF_REG_10, -16, -8), + BPF_JMP_IMM(BPF_JEQ, BPF_REG_0, 0, 8), BPF_LDX_MEM(BPF_DW, BPF_REG_1, BPF_REG_10, -16), BPF_MOV64_IMM(BPF_REG_2, 0), BPF_JMP_REG(BPF_JGT, BPF_REG_2, BPF_REG_1, 2), @@ -252,20 +265,21 @@ BPF_MOV64_IMM(BPF_REG_0, 0), BPF_EXIT_INSN(), }, - .fixup_map_hash_8b = { 3 }, + .fixup_map_hash_8b = { 5 }, .errstr = "unbounded min value", .result = REJECT, }, { "bounds checks mixing signed and unsigned, variant 11", .insns = { + BPF_EMIT_CALL(BPF_FUNC_ktime_get_ns), + BPF_STX_MEM(BPF_DW, BPF_REG_10, BPF_REG_0, -16), BPF_ST_MEM(BPF_DW, BPF_REG_10, -8, 0), BPF_MOV64_REG(BPF_REG_2, BPF_REG_10), BPF_ALU64_IMM(BPF_ADD, BPF_REG_2, -8), BPF_LD_MAP_FD(BPF_REG_1, 0), BPF_RAW_INSN(BPF_JMP | BPF_CALL, 0, 0, 0, BPF_FUNC_map_lookup_elem), - BPF_JMP_IMM(BPF_JEQ, BPF_REG_0, 0, 9), - BPF_ST_MEM(BPF_DW, BPF_REG_10, -16, -8), + BPF_JMP_IMM(BPF_JEQ, BPF_REG_0, 0, 8), BPF_LDX_MEM(BPF_DW, BPF_REG_1, BPF_REG_10, -16), BPF_MOV64_IMM(BPF_REG_2, -1), BPF_JMP_REG(BPF_JGE, BPF_REG_2, BPF_REG_1, 2), @@ -278,20 +292,21 @@ BPF_MOV64_IMM(BPF_REG_0, 0), BPF_EXIT_INSN(), }, - .fixup_map_hash_8b = { 3 }, + .fixup_map_hash_8b = { 5 }, .errstr = "unbounded min value", .result = REJECT, }, { "bounds checks mixing signed and unsigned, variant 12", .insns = { + BPF_EMIT_CALL(BPF_FUNC_ktime_get_ns), + BPF_STX_MEM(BPF_DW, BPF_REG_10, BPF_REG_0, -16), BPF_ST_MEM(BPF_DW, BPF_REG_10, -8, 0), BPF_MOV64_REG(BPF_REG_2, BPF_REG_10), BPF_ALU64_IMM(BPF_ADD, BPF_REG_2, -8), BPF_LD_MAP_FD(BPF_REG_1, 0), BPF_RAW_INSN(BPF_JMP | BPF_CALL, 0, 0, 0, BPF_FUNC_map_lookup_elem), - BPF_JMP_IMM(BPF_JEQ, BPF_REG_0, 0, 9), - BPF_ST_MEM(BPF_DW, BPF_REG_10, -16, -8), + BPF_JMP_IMM(BPF_JEQ, BPF_REG_0, 0, 8), BPF_LDX_MEM(BPF_DW, BPF_REG_1, BPF_REG_10, -16), BPF_MOV64_IMM(BPF_REG_2, -6), BPF_JMP_REG(BPF_JGE, BPF_REG_2, BPF_REG_1, 2), @@ -303,20 +318,21 @@ BPF_MOV64_IMM(BPF_REG_0, 0), BPF_EXIT_INSN(), }, - .fixup_map_hash_8b = { 3 }, + .fixup_map_hash_8b = { 5 }, .errstr = "unbounded min value", .result = REJECT, }, { "bounds checks mixing signed and unsigned, variant 13", .insns = { + BPF_EMIT_CALL(BPF_FUNC_ktime_get_ns), + BPF_STX_MEM(BPF_DW, BPF_REG_10, BPF_REG_0, -16), BPF_ST_MEM(BPF_DW, BPF_REG_10, -8, 0), BPF_MOV64_REG(BPF_REG_2, BPF_REG_10), BPF_ALU64_IMM(BPF_ADD, BPF_REG_2, -8), BPF_LD_MAP_FD(BPF_REG_1, 0), BPF_RAW_INSN(BPF_JMP | BPF_CALL, 0, 0, 0, BPF_FUNC_map_lookup_elem), - BPF_JMP_IMM(BPF_JEQ, BPF_REG_0, 0, 6), - BPF_ST_MEM(BPF_DW, BPF_REG_10, -16, -8), + BPF_JMP_IMM(BPF_JEQ, BPF_REG_0, 0, 5), BPF_LDX_MEM(BPF_DW, BPF_REG_1, BPF_REG_10, -16), BPF_MOV64_IMM(BPF_REG_2, 2), BPF_JMP_REG(BPF_JGE, BPF_REG_2, BPF_REG_1, 2), @@ -331,7 +347,7 @@ BPF_MOV64_IMM(BPF_REG_0, 0), BPF_EXIT_INSN(), }, - .fixup_map_hash_8b = { 3 }, + .fixup_map_hash_8b = { 5 }, .errstr = "unbounded min value", .result = REJECT, }, @@ -340,13 +356,14 @@ .insns = { BPF_LDX_MEM(BPF_W, BPF_REG_9, BPF_REG_1, offsetof(struct __sk_buff, mark)), + BPF_EMIT_CALL(BPF_FUNC_ktime_get_ns), + BPF_STX_MEM(BPF_DW, BPF_REG_10, BPF_REG_0, -16), BPF_ST_MEM(BPF_DW, BPF_REG_10, -8, 0), BPF_MOV64_REG(BPF_REG_2, BPF_REG_10), BPF_ALU64_IMM(BPF_ADD, BPF_REG_2, -8), BPF_LD_MAP_FD(BPF_REG_1, 0), BPF_RAW_INSN(BPF_JMP | BPF_CALL, 0, 0, 0, BPF_FUNC_map_lookup_elem), - BPF_JMP_IMM(BPF_JEQ, BPF_REG_0, 0, 8), - BPF_ST_MEM(BPF_DW, BPF_REG_10, -16, -8), + BPF_JMP_IMM(BPF_JEQ, BPF_REG_0, 0, 7), BPF_LDX_MEM(BPF_DW, BPF_REG_1, BPF_REG_10, -16), BPF_MOV64_IMM(BPF_REG_2, -1), BPF_MOV64_IMM(BPF_REG_8, 2), @@ -360,20 +377,21 @@ BPF_JMP_REG(BPF_JGT, BPF_REG_1, BPF_REG_2, -3), BPF_JMP_IMM(BPF_JA, 0, 0, -7), }, - .fixup_map_hash_8b = { 4 }, + .fixup_map_hash_8b = { 6 }, .errstr = "unbounded min value", .result = REJECT, }, { "bounds checks mixing signed and unsigned, variant 15", .insns = { + BPF_EMIT_CALL(BPF_FUNC_ktime_get_ns), + BPF_STX_MEM(BPF_DW, BPF_REG_10, BPF_REG_0, -16), BPF_ST_MEM(BPF_DW, BPF_REG_10, -8, 0), BPF_MOV64_REG(BPF_REG_2, BPF_REG_10), BPF_ALU64_IMM(BPF_ADD, BPF_REG_2, -8), BPF_LD_MAP_FD(BPF_REG_1, 0), BPF_RAW_INSN(BPF_JMP | BPF_CALL, 0, 0, 0, BPF_FUNC_map_lookup_elem), - BPF_JMP_IMM(BPF_JEQ, BPF_REG_0, 0, 4), - BPF_ST_MEM(BPF_DW, BPF_REG_10, -16, -8), + BPF_JMP_IMM(BPF_JEQ, BPF_REG_0, 0, 3), BPF_LDX_MEM(BPF_DW, BPF_REG_1, BPF_REG_10, -16), BPF_MOV64_IMM(BPF_REG_2, -6), BPF_JMP_REG(BPF_JGE, BPF_REG_2, BPF_REG_1, 2), @@ -387,7 +405,7 @@ BPF_MOV64_IMM(BPF_REG_0, 0), BPF_EXIT_INSN(), }, - .fixup_map_hash_8b = { 3 }, + .fixup_map_hash_8b = { 5 }, .errstr = "unbounded min value", .result = REJECT, }, diff --git a/tools/testing/selftests/mount_setattr/mount_setattr_test.c b/tools/testing/selftests/mount_setattr/mount_setattr_test.c index 969647228817b..c6a8c732b8021 100644 --- a/tools/testing/selftests/mount_setattr/mount_setattr_test.c +++ b/tools/testing/selftests/mount_setattr/mount_setattr_test.c @@ -104,13 +104,6 @@ #else #define __NR_mount_setattr 442 #endif - -struct mount_attr { - __u64 attr_set; - __u64 attr_clr; - __u64 propagation; - __u64 userns_fd; -}; #endif #ifndef __NR_open_tree diff --git a/tools/testing/selftests/net/fcnal-test.sh b/tools/testing/selftests/net/fcnal-test.sh index 21ca91473c095..ee6880ac3e5ed 100755 --- a/tools/testing/selftests/net/fcnal-test.sh +++ b/tools/testing/selftests/net/fcnal-test.sh @@ -92,6 +92,13 @@ NSC_CMD="ip netns exec ${NSC}" which ping6 > /dev/null 2>&1 && ping6=$(which ping6) || ping6=$(which ping) +# Check if FIPS mode is enabled +if [ -f /proc/sys/crypto/fips_enabled ]; then + fips_enabled=`cat /proc/sys/crypto/fips_enabled` +else + fips_enabled=0 +fi + ################################################################################ # utilities @@ -1216,7 +1223,7 @@ ipv4_tcp_novrf() run_cmd nettest -d ${NSA_DEV} -r ${a} log_test_addr ${a} $? 1 "No server, device client, local conn" - ipv4_tcp_md5_novrf + [ "$fips_enabled" = "1" ] || ipv4_tcp_md5_novrf } ipv4_tcp_vrf() @@ -1270,9 +1277,11 @@ ipv4_tcp_vrf() log_test_addr ${a} $? 1 "Global server, local connection" # run MD5 tests - setup_vrf_dup - ipv4_tcp_md5 - cleanup_vrf_dup + if [ "$fips_enabled" = "0" ]; then + setup_vrf_dup + ipv4_tcp_md5 + cleanup_vrf_dup + fi # # enable VRF global server @@ -2772,7 +2781,7 @@ ipv6_tcp_novrf() log_test_addr ${a} $? 1 "No server, device client, local conn" done - ipv6_tcp_md5_novrf + [ "$fips_enabled" = "1" ] || ipv6_tcp_md5_novrf } ipv6_tcp_vrf() @@ -2842,9 +2851,11 @@ ipv6_tcp_vrf() log_test_addr ${a} $? 1 "Global server, local connection" # run MD5 tests - setup_vrf_dup - ipv6_tcp_md5 - cleanup_vrf_dup + if [ "$fips_enabled" = "0" ]; then + setup_vrf_dup + ipv6_tcp_md5 + cleanup_vrf_dup + fi # # enable VRF global server diff --git a/tools/testing/selftests/net/forwarding/mirror_gre_bridge_1d.sh b/tools/testing/selftests/net/forwarding/mirror_gre_bridge_1d.sh index c5095da7f6bf8..aec752a22e9ec 100755 --- a/tools/testing/selftests/net/forwarding/mirror_gre_bridge_1d.sh +++ b/tools/testing/selftests/net/forwarding/mirror_gre_bridge_1d.sh @@ -93,12 +93,16 @@ cleanup() test_gretap() { + ip neigh replace 192.0.2.130 lladdr $(mac_get $h3) \ + nud permanent dev br2 full_test_span_gre_dir gt4 ingress 8 0 "mirror to gretap" full_test_span_gre_dir gt4 egress 0 8 "mirror to gretap" } test_ip6gretap() { + ip neigh replace 2001:db8:2::2 lladdr $(mac_get $h3) \ + nud permanent dev br2 full_test_span_gre_dir gt6 ingress 8 0 "mirror to ip6gretap" full_test_span_gre_dir gt6 egress 0 8 "mirror to ip6gretap" } diff --git a/tools/testing/selftests/net/forwarding/mirror_gre_bridge_1q.sh b/tools/testing/selftests/net/forwarding/mirror_gre_bridge_1q.sh index 9ff22f28032dd..0cf4c47a46f9b 100755 --- a/tools/testing/selftests/net/forwarding/mirror_gre_bridge_1q.sh +++ b/tools/testing/selftests/net/forwarding/mirror_gre_bridge_1q.sh @@ -90,12 +90,16 @@ cleanup() test_gretap() { + ip neigh replace 192.0.2.130 lladdr $(mac_get $h3) \ + nud permanent dev br1 full_test_span_gre_dir gt4 ingress 8 0 "mirror to gretap" full_test_span_gre_dir gt4 egress 0 8 "mirror to gretap" } test_ip6gretap() { + ip neigh replace 2001:db8:2::2 lladdr $(mac_get $h3) \ + nud permanent dev br1 full_test_span_gre_dir gt6 ingress 8 0 "mirror to ip6gretap" full_test_span_gre_dir gt6 egress 0 8 "mirror to ip6gretap" } diff --git a/tools/testing/selftests/net/mptcp/config b/tools/testing/selftests/net/mptcp/config index 38021a0dd5276..6032f9b23c4c2 100644 --- a/tools/testing/selftests/net/mptcp/config +++ b/tools/testing/selftests/net/mptcp/config @@ -1,3 +1,4 @@ +CONFIG_KALLSYMS=y CONFIG_MPTCP=y CONFIG_IPV6=y CONFIG_MPTCP_IPV6=y diff --git a/tools/testing/selftests/net/mptcp/diag.sh b/tools/testing/selftests/net/mptcp/diag.sh index dd730a35bd128..400cf1ce96e31 100755 --- a/tools/testing/selftests/net/mptcp/diag.sh +++ b/tools/testing/selftests/net/mptcp/diag.sh @@ -42,27 +42,39 @@ fi __chk_nr() { - local condition="$1" + local command="$1" local expected=$2 - local msg nr + local msg="$3" + local skip="${4:-SKIP}" + local nr - shift 2 - msg=$* - nr=$(ss -inmHMN $ns | $condition) + nr=$(eval $command) printf "%-50s" "$msg" if [ $nr != $expected ]; then - echo "[ fail ] expected $expected found $nr" - ret=$test_cnt + if [ $nr = "$skip" ] && ! mptcp_lib_expect_all_features; then + echo "[ skip ] Feature probably not supported" + else + echo "[ fail ] expected $expected found $nr" + ret=$test_cnt + fi else echo "[ ok ]" fi test_cnt=$((test_cnt+1)) } +__chk_msk_nr() +{ + local condition=$1 + shift 1 + + __chk_nr "ss -inmHMN $ns | $condition" "$@" +} + chk_msk_nr() { - __chk_nr "grep -c token:" $* + __chk_msk_nr "grep -c token:" "$@" } wait_msk_nr() @@ -100,37 +112,26 @@ wait_msk_nr() chk_msk_fallback_nr() { - __chk_nr "grep -c fallback" $* + __chk_msk_nr "grep -c fallback" "$@" } chk_msk_remote_key_nr() { - __chk_nr "grep -c remote_key" $* + __chk_msk_nr "grep -c remote_key" "$@" } __chk_listen() { local filter="$1" local expected=$2 + local msg="$3" - shift 2 - msg=$* - - nr=$(ss -N $ns -Ml "$filter" | grep -c LISTEN) - printf "%-50s" "$msg" - - if [ $nr != $expected ]; then - echo "[ fail ] expected $expected found $nr" - ret=$test_cnt - else - echo "[ ok ]" - fi + __chk_nr "ss -N $ns -Ml '$filter' | grep -c LISTEN" "$expected" "$msg" 0 } chk_msk_listen() { lport=$1 - local msg="check for listen socket" # destination port search should always return empty list __chk_listen "dport $lport" 0 "listen match for dport $lport" diff --git a/tools/testing/selftests/net/mptcp/mptcp_connect.c b/tools/testing/selftests/net/mptcp/mptcp_connect.c index e54653ea2ed4f..7df6b9b6f9a84 100644 --- a/tools/testing/selftests/net/mptcp/mptcp_connect.c +++ b/tools/testing/selftests/net/mptcp/mptcp_connect.c @@ -98,8 +98,8 @@ static struct cfg_sockopt_types cfg_sockopt_types; static void die_usage(void) { fprintf(stderr, "Usage: mptcp_connect [-6] [-c cmsg] [-f offset] [-i file] [-I num] [-j] [-l] " - "[-m mode] [-M mark] [-o option] [-p port] [-P mode] [-j] [-l] [-r num] " - "[-s MPTCP|TCP] [-S num] [-r num] [-t num] [-T num] [-u] [-w sec] connect_address\n"); + "[-m mode] [-M mark] [-o option] [-p port] [-P mode] [-r num] [-R num] " + "[-s MPTCP|TCP] [-S num] [-t num] [-T num] [-w sec] connect_address\n"); fprintf(stderr, "\t-6 use ipv6\n"); fprintf(stderr, "\t-c cmsg -- test cmsg type \n"); fprintf(stderr, "\t-f offset -- stop the I/O after receiving and sending the specified amount " @@ -118,13 +118,13 @@ static void die_usage(void) fprintf(stderr, "\t-p num -- use port num\n"); fprintf(stderr, "\t-P [saveWithPeek|saveAfterPeek] -- save data with/after MSG_PEEK form tcp socket\n"); - fprintf(stderr, "\t-t num -- set poll timeout to num\n"); - fprintf(stderr, "\t-T num -- set expected runtime to num ms\n"); fprintf(stderr, "\t-r num -- enable slow mode, limiting each write to num bytes " "-- for remove addr tests\n"); fprintf(stderr, "\t-R num -- set SO_RCVBUF to num\n"); fprintf(stderr, "\t-s [MPTCP|TCP] -- use mptcp(default) or tcp sockets\n"); fprintf(stderr, "\t-S num -- set SO_SNDBUF to num\n"); + fprintf(stderr, "\t-t num -- set poll timeout to num\n"); + fprintf(stderr, "\t-T num -- set expected runtime to num ms\n"); fprintf(stderr, "\t-w num -- wait num sec before closing the socket\n"); exit(1); } diff --git a/tools/testing/selftests/net/mptcp/mptcp_connect.sh b/tools/testing/selftests/net/mptcp/mptcp_connect.sh index c923ce5ff6eb7..36dc2bab7a13c 100755 --- a/tools/testing/selftests/net/mptcp/mptcp_connect.sh +++ b/tools/testing/selftests/net/mptcp/mptcp_connect.sh @@ -144,6 +144,7 @@ cleanup() } mptcp_lib_check_mptcp +mptcp_lib_check_kallsyms ip -Version > /dev/null 2>&1 if [ $? -ne 0 ];then @@ -696,6 +697,15 @@ run_test_transparent() return 0 fi + # IP(V6)_TRANSPARENT has been added after TOS support which came with + # the required infrastructure in MPTCP sockopt code. To support TOS, the + # following function has been exported (T). Not great but better than + # checking for a specific kernel version. + if ! mptcp_lib_kallsyms_has "T __ip_sock_set_tos$"; then + echo "INFO: ${msg} not supported by the kernel: SKIP" + return + fi + ip netns exec "$listener_ns" nft -f /dev/stdin <<"EOF" flush ruleset table inet mangle { @@ -772,6 +782,11 @@ run_tests_disconnect() local old_cin=$cin local old_sin=$sin + if ! mptcp_lib_kallsyms_has "mptcp_pm_data_reset$"; then + echo "INFO: Full disconnect not supported: SKIP" + return + fi + cat $cin $cin $cin > "$cin".disconnect # force do_transfer to cope with the multiple tranmissions diff --git a/tools/testing/selftests/net/mptcp/mptcp_join.sh b/tools/testing/selftests/net/mptcp/mptcp_join.sh index 6be19b1038243..7b65003ee8cff 100755 --- a/tools/testing/selftests/net/mptcp/mptcp_join.sh +++ b/tools/testing/selftests/net/mptcp/mptcp_join.sh @@ -8,6 +8,10 @@ . "$(dirname "${0}")/mptcp_lib.sh" +# ShellCheck incorrectly believes that most of the code here is unreachable +# because it's invoked by variable name, see how the "tests" array is used +#shellcheck disable=SC2317 + ret=0 sin="" sinfail="" @@ -21,6 +25,8 @@ capout="" ns1="" ns2="" ksft_skip=4 +iptables="iptables" +ip6tables="ip6tables" timeout_poll=30 timeout_test=$((timeout_poll * 2 + 1)) capture=0 @@ -73,7 +79,7 @@ init_partial() ip netns add $netns || exit $ksft_skip ip -net $netns link set lo up ip netns exec $netns sysctl -q net.mptcp.enabled=1 - ip netns exec $netns sysctl -q net.mptcp.pm_type=0 + ip netns exec $netns sysctl -q net.mptcp.pm_type=0 2>/dev/null || true ip netns exec $netns sysctl -q net.ipv4.conf.all.rp_filter=0 ip netns exec $netns sysctl -q net.ipv4.conf.default.rp_filter=0 if [ $checksum -eq 1 ]; then @@ -131,13 +137,18 @@ cleanup_partial() check_tools() { mptcp_lib_check_mptcp + mptcp_lib_check_kallsyms if ! ip -Version &> /dev/null; then echo "SKIP: Could not run test without ip tool" exit $ksft_skip fi - if ! iptables -V &> /dev/null; then + # Use the legacy version if available to support old kernel versions + if iptables-legacy -V &> /dev/null; then + iptables="iptables-legacy" + ip6tables="ip6tables-legacy" + elif ! iptables -V &> /dev/null; then echo "SKIP: Could not run all tests without iptables tool" exit $ksft_skip fi @@ -173,6 +184,32 @@ cleanup() cleanup_partial } +# $1: msg +print_title() +{ + printf "%03u %-36s %s" "${TEST_COUNT}" "${TEST_NAME}" "${1}" +} + +# [ $1: fail msg ] +mark_as_skipped() +{ + local msg="${1:-"Feature not supported"}" + + mptcp_lib_fail_if_expected_feature "${msg}" + + print_title "[ skip ] ${msg}" + printf "\n" +} + +# $@: condition +continue_if() +{ + if ! "${@}"; then + mark_as_skipped + return 1 + fi +} + skip_test() { if [ "${#only_tests_ids[@]}" -eq 0 ] && [ "${#only_tests_names[@]}" -eq 0 ]; then @@ -216,6 +253,19 @@ reset() return 0 } +# $1: test name ; $2: counter to check +reset_check_counter() +{ + reset "${1}" || return 1 + + local counter="${2}" + + if ! nstat -asz "${counter}" | grep -wq "${counter}"; then + mark_as_skipped "counter '${counter}' is not available" + return 1 + fi +} + # $1: test name reset_with_cookies() { @@ -235,17 +285,21 @@ reset_with_add_addr_timeout() reset "${1}" || return 1 - tables="iptables" + tables="${iptables}" if [ $ip -eq 6 ]; then - tables="ip6tables" + tables="${ip6tables}" fi ip netns exec $ns1 sysctl -q net.mptcp.add_addr_timeout=1 - ip netns exec $ns2 $tables -A OUTPUT -p tcp \ - -m tcp --tcp-option 30 \ - -m bpf --bytecode \ - "$CBPF_MPTCP_SUBOPTION_ADD_ADDR" \ - -j DROP + + if ! ip netns exec $ns2 $tables -A OUTPUT -p tcp \ + -m tcp --tcp-option 30 \ + -m bpf --bytecode \ + "$CBPF_MPTCP_SUBOPTION_ADD_ADDR" \ + -j DROP; then + mark_as_skipped "unable to set the 'add addr' rule" + return 1 + fi } # $1: test name @@ -289,22 +343,17 @@ reset_with_allow_join_id0() # tc action pedit offset 162 out of bounds # # Netfilter is used to mark packets with enough data. -reset_with_fail() +setup_fail_rules() { - reset "${1}" || return 1 - - ip netns exec $ns1 sysctl -q net.mptcp.checksum_enabled=1 - ip netns exec $ns2 sysctl -q net.mptcp.checksum_enabled=1 - check_invert=1 validate_checksum=1 - local i="$2" - local ip="${3:-4}" + local i="$1" + local ip="${2:-4}" local tables - tables="iptables" + tables="${iptables}" if [ $ip -eq 6 ]; then - tables="ip6tables" + tables="${ip6tables}" fi ip netns exec $ns2 $tables \ @@ -314,15 +363,51 @@ reset_with_fail() -p tcp \ -m length --length 150:9999 \ -m statistic --mode nth --packet 1 --every 99999 \ - -j MARK --set-mark 42 || exit 1 + -j MARK --set-mark 42 || return ${ksft_skip} - tc -n $ns2 qdisc add dev ns2eth$i clsact || exit 1 + tc -n $ns2 qdisc add dev ns2eth$i clsact || return ${ksft_skip} tc -n $ns2 filter add dev ns2eth$i egress \ protocol ip prio 1000 \ handle 42 fw \ action pedit munge offset 148 u8 invert \ pipe csum tcp \ - index 100 || exit 1 + index 100 || return ${ksft_skip} +} + +reset_with_fail() +{ + reset_check_counter "${1}" "MPTcpExtInfiniteMapTx" || return 1 + shift + + ip netns exec $ns1 sysctl -q net.mptcp.checksum_enabled=1 + ip netns exec $ns2 sysctl -q net.mptcp.checksum_enabled=1 + + local rc=0 + setup_fail_rules "${@}" || rc=$? + + if [ ${rc} -eq ${ksft_skip} ]; then + mark_as_skipped "unable to set the 'fail' rules" + return 1 + fi +} + +reset_with_tcp_filter() +{ + reset "${1}" || return 1 + shift + + local ns="${!1}" + local src="${2}" + local target="${3}" + + if ! ip netns exec "${ns}" ${iptables} \ + -A INPUT \ + -s "${src}" \ + -p tcp \ + -j "${target}"; then + mark_as_skipped "unable to set the filter rules" + return 1 + fi } fail_test() @@ -357,8 +442,9 @@ check_transfer() local line if [ -n "$bytes" ]; then + local out_size # when truncating we must check the size explicitly - local out_size=$(wc -c $out | awk '{print $1}') + out_size=$(wc -c $out | awk '{print $1}') if [ $out_size -ne $bytes ]; then echo "[ FAIL ] $what output file has wrong size ($out_size, $bytes)" fail_test @@ -442,11 +528,25 @@ wait_local_port_listen() done } -rm_addr_count() +# $1: ns ; $2: counter +get_counter() { - local ns=${1} + local ns="${1}" + local counter="${2}" + local count + + count=$(ip netns exec ${ns} nstat -asz "${counter}" | awk 'NR==1 {next} {print $2}') + if [ -z "${count}" ]; then + mptcp_lib_fail_if_expected_feature "${counter} counter" + return 1 + fi - ip netns exec ${ns} nstat -as | grep MPTcpExtRmAddr | awk '{print $2}' + echo "${count}" +} + +rm_addr_count() +{ + get_counter "${1}" "MPTcpExtRmAddr" } # $1: ns, $2: old rm_addr counter in $ns @@ -469,11 +569,11 @@ wait_mpj() local ns="${1}" local cnt old_cnt - old_cnt=$(ip netns exec ${ns} nstat -as | grep MPJoinAckRx | awk '{print $2}') + old_cnt=$(get_counter ${ns} "MPTcpExtMPJoinAckRx") local i for i in $(seq 10); do - cnt=$(ip netns exec ${ns} nstat -as | grep MPJoinAckRx | awk '{print $2}') + cnt=$(get_counter ${ns} "MPTcpExtMPJoinAckRx") [ "$cnt" = "${old_cnt}" ] || break sleep 0.1 done @@ -487,6 +587,7 @@ kill_wait() kill_tests_wait() { + #shellcheck disable=SC2046 kill -SIGUSR1 $(ip netns pids $ns2) $(ip netns pids $ns1) wait } @@ -666,15 +767,6 @@ pm_nl_check_endpoint() fi } -filter_tcp_from() -{ - local ns="${1}" - local src="${2}" - local target="${3}" - - ip netns exec "${ns}" iptables -A INPUT -s "${src}" -p tcp -j "${target}" -} - do_transfer() { local listener_ns="$1" @@ -854,11 +946,12 @@ do_transfer() sp=$(grep "type:10" "$evts_ns1" | sed -n 's/.*\(sport:\)\([[:digit:]]*\).*$/\2/p;q') da=$(grep "type:10" "$evts_ns1" | - sed -n 's/.*\(daddr6:\)\([0-9a-f:.]*\).*$/\2/p;q') + sed -n 's/.*\(daddr[46]:\)\([0-9a-f:.]*\).*$/\2/p;q') + echo "$da" | grep -q ":" && addr="::ffff:$addr" dp=$(grep "type:10" "$evts_ns1" | sed -n 's/.*\(dport:\)\([[:digit:]]*\).*$/\2/p;q') ip netns exec ${listener_ns} ./pm_nl_ctl rem token $tk id $id - ip netns exec ${listener_ns} ./pm_nl_ctl dsf lip "::ffff:$addr" \ + ip netns exec ${listener_ns} ./pm_nl_ctl dsf lip "$addr" \ lport $sp rip $da rport $dp token $tk fi @@ -1152,12 +1245,13 @@ chk_csum_nr() fi printf "%-${nr_blank}s %s" " " "sum" - count=$(ip netns exec $ns1 nstat -as | grep MPTcpExtDataCsumErr | awk '{print $2}') - [ -z "$count" ] && count=0 + count=$(get_counter ${ns1} "MPTcpExtDataCsumErr") if [ "$count" != "$csum_ns1" ]; then extra_msg="$extra_msg ns1=$count" fi - if { [ "$count" != $csum_ns1 ] && [ $allow_multi_errors_ns1 -eq 0 ]; } || + if [ -z "$count" ]; then + echo -n "[skip]" + elif { [ "$count" != $csum_ns1 ] && [ $allow_multi_errors_ns1 -eq 0 ]; } || { [ "$count" -lt $csum_ns1 ] && [ $allow_multi_errors_ns1 -eq 1 ]; }; then echo "[fail] got $count data checksum error[s] expected $csum_ns1" fail_test @@ -1166,12 +1260,13 @@ chk_csum_nr() echo -n "[ ok ]" fi echo -n " - csum " - count=$(ip netns exec $ns2 nstat -as | grep MPTcpExtDataCsumErr | awk '{print $2}') - [ -z "$count" ] && count=0 + count=$(get_counter ${ns2} "MPTcpExtDataCsumErr") if [ "$count" != "$csum_ns2" ]; then extra_msg="$extra_msg ns2=$count" fi - if { [ "$count" != $csum_ns2 ] && [ $allow_multi_errors_ns2 -eq 0 ]; } || + if [ -z "$count" ]; then + echo -n "[skip]" + elif { [ "$count" != $csum_ns2 ] && [ $allow_multi_errors_ns2 -eq 0 ]; } || { [ "$count" -lt $csum_ns2 ] && [ $allow_multi_errors_ns2 -eq 1 ]; }; then echo "[fail] got $count data checksum error[s] expected $csum_ns2" fail_test @@ -1213,12 +1308,13 @@ chk_fail_nr() fi printf "%-${nr_blank}s %s" " " "ftx" - count=$(ip netns exec $ns_tx nstat -as | grep MPTcpExtMPFailTx | awk '{print $2}') - [ -z "$count" ] && count=0 + count=$(get_counter ${ns_tx} "MPTcpExtMPFailTx") if [ "$count" != "$fail_tx" ]; then extra_msg="$extra_msg,tx=$count" fi - if { [ "$count" != "$fail_tx" ] && [ $allow_tx_lost -eq 0 ]; } || + if [ -z "$count" ]; then + echo -n "[skip]" + elif { [ "$count" != "$fail_tx" ] && [ $allow_tx_lost -eq 0 ]; } || { [ "$count" -gt "$fail_tx" ] && [ $allow_tx_lost -eq 1 ]; }; then echo "[fail] got $count MP_FAIL[s] TX expected $fail_tx" fail_test @@ -1228,12 +1324,13 @@ chk_fail_nr() fi echo -n " - failrx" - count=$(ip netns exec $ns_rx nstat -as | grep MPTcpExtMPFailRx | awk '{print $2}') - [ -z "$count" ] && count=0 + count=$(get_counter ${ns_rx} "MPTcpExtMPFailRx") if [ "$count" != "$fail_rx" ]; then extra_msg="$extra_msg,rx=$count" fi - if { [ "$count" != "$fail_rx" ] && [ $allow_rx_lost -eq 0 ]; } || + if [ -z "$count" ]; then + echo -n "[skip]" + elif { [ "$count" != "$fail_rx" ] && [ $allow_rx_lost -eq 0 ]; } || { [ "$count" -gt "$fail_rx" ] && [ $allow_rx_lost -eq 1 ]; }; then echo "[fail] got $count MP_FAIL[s] RX expected $fail_rx" fail_test @@ -1265,10 +1362,11 @@ chk_fclose_nr() fi printf "%-${nr_blank}s %s" " " "ctx" - count=$(ip netns exec $ns_tx nstat -as | grep MPTcpExtMPFastcloseTx | awk '{print $2}') - [ -z "$count" ] && count=0 - [ "$count" != "$fclose_tx" ] && extra_msg="$extra_msg,tx=$count" - if [ "$count" != "$fclose_tx" ]; then + count=$(get_counter ${ns_tx} "MPTcpExtMPFastcloseTx") + if [ -z "$count" ]; then + echo -n "[skip]" + elif [ "$count" != "$fclose_tx" ]; then + extra_msg="$extra_msg,tx=$count" echo "[fail] got $count MP_FASTCLOSE[s] TX expected $fclose_tx" fail_test dump_stats=1 @@ -1277,10 +1375,11 @@ chk_fclose_nr() fi echo -n " - fclzrx" - count=$(ip netns exec $ns_rx nstat -as | grep MPTcpExtMPFastcloseRx | awk '{print $2}') - [ -z "$count" ] && count=0 - [ "$count" != "$fclose_rx" ] && extra_msg="$extra_msg,rx=$count" - if [ "$count" != "$fclose_rx" ]; then + count=$(get_counter ${ns_rx} "MPTcpExtMPFastcloseRx") + if [ -z "$count" ]; then + echo -n "[skip]" + elif [ "$count" != "$fclose_rx" ]; then + extra_msg="$extra_msg,rx=$count" echo "[fail] got $count MP_FASTCLOSE[s] RX expected $fclose_rx" fail_test dump_stats=1 @@ -1311,9 +1410,10 @@ chk_rst_nr() fi printf "%-${nr_blank}s %s" " " "rtx" - count=$(ip netns exec $ns_tx nstat -as | grep MPTcpExtMPRstTx | awk '{print $2}') - [ -z "$count" ] && count=0 - if [ $count -lt $rst_tx ]; then + count=$(get_counter ${ns_tx} "MPTcpExtMPRstTx") + if [ -z "$count" ]; then + echo -n "[skip]" + elif [ $count -lt $rst_tx ]; then echo "[fail] got $count MP_RST[s] TX expected $rst_tx" fail_test dump_stats=1 @@ -1322,9 +1422,10 @@ chk_rst_nr() fi echo -n " - rstrx " - count=$(ip netns exec $ns_rx nstat -as | grep MPTcpExtMPRstRx | awk '{print $2}') - [ -z "$count" ] && count=0 - if [ "$count" -lt "$rst_rx" ]; then + count=$(get_counter ${ns_rx} "MPTcpExtMPRstRx") + if [ -z "$count" ]; then + echo -n "[skip]" + elif [ "$count" -lt "$rst_rx" ]; then echo "[fail] got $count MP_RST[s] RX expected $rst_rx" fail_test dump_stats=1 @@ -1345,9 +1446,10 @@ chk_infi_nr() local dump_stats printf "%-${nr_blank}s %s" " " "itx" - count=$(ip netns exec $ns2 nstat -as | grep InfiniteMapTx | awk '{print $2}') - [ -z "$count" ] && count=0 - if [ "$count" != "$infi_tx" ]; then + count=$(get_counter ${ns2} "MPTcpExtInfiniteMapTx") + if [ -z "$count" ]; then + echo -n "[skip]" + elif [ "$count" != "$infi_tx" ]; then echo "[fail] got $count infinite map[s] TX expected $infi_tx" fail_test dump_stats=1 @@ -1356,9 +1458,10 @@ chk_infi_nr() fi echo -n " - infirx" - count=$(ip netns exec $ns1 nstat -as | grep InfiniteMapRx | awk '{print $2}') - [ -z "$count" ] && count=0 - if [ "$count" != "$infi_rx" ]; then + count=$(get_counter ${ns1} "MPTcpExtInfiniteMapRx") + if [ -z "$count" ]; then + echo "[skip]" + elif [ "$count" != "$infi_rx" ]; then echo "[fail] got $count infinite map[s] RX expected $infi_rx" fail_test dump_stats=1 @@ -1390,9 +1493,10 @@ chk_join_nr() fi printf "%03u %-36s %s" "${TEST_COUNT}" "${title}" "syn" - count=$(ip netns exec $ns1 nstat -as | grep MPTcpExtMPJoinSynRx | awk '{print $2}') - [ -z "$count" ] && count=0 - if [ "$count" != "$syn_nr" ]; then + count=$(get_counter ${ns1} "MPTcpExtMPJoinSynRx") + if [ -z "$count" ]; then + echo -n "[skip]" + elif [ "$count" != "$syn_nr" ]; then echo "[fail] got $count JOIN[s] syn expected $syn_nr" fail_test dump_stats=1 @@ -1402,9 +1506,10 @@ chk_join_nr() echo -n " - synack" with_cookie=$(ip netns exec $ns2 sysctl -n net.ipv4.tcp_syncookies) - count=$(ip netns exec $ns2 nstat -as | grep MPTcpExtMPJoinSynAckRx | awk '{print $2}') - [ -z "$count" ] && count=0 - if [ "$count" != "$syn_ack_nr" ]; then + count=$(get_counter ${ns2} "MPTcpExtMPJoinSynAckRx") + if [ -z "$count" ]; then + echo -n "[skip]" + elif [ "$count" != "$syn_ack_nr" ]; then # simult connections exceeding the limit with cookie enabled could go up to # synack validation as the conn limit can be enforced reliably only after # the subflow creation @@ -1420,9 +1525,10 @@ chk_join_nr() fi echo -n " - ack" - count=$(ip netns exec $ns1 nstat -as | grep MPTcpExtMPJoinAckRx | awk '{print $2}') - [ -z "$count" ] && count=0 - if [ "$count" != "$ack_nr" ]; then + count=$(get_counter ${ns1} "MPTcpExtMPJoinAckRx") + if [ -z "$count" ]; then + echo "[skip]" + elif [ "$count" != "$ack_nr" ]; then echo "[fail] got $count JOIN[s] ack expected $ack_nr" fail_test dump_stats=1 @@ -1454,12 +1560,12 @@ chk_stale_nr() local recover_nr printf "%-${nr_blank}s %-18s" " " "stale" - stale_nr=$(ip netns exec $ns nstat -as | grep MPTcpExtSubflowStale | awk '{print $2}') - [ -z "$stale_nr" ] && stale_nr=0 - recover_nr=$(ip netns exec $ns nstat -as | grep MPTcpExtSubflowRecover | awk '{print $2}') - [ -z "$recover_nr" ] && recover_nr=0 - if [ $stale_nr -lt $stale_min ] || + stale_nr=$(get_counter ${ns} "MPTcpExtSubflowStale") + recover_nr=$(get_counter ${ns} "MPTcpExtSubflowRecover") + if [ -z "$stale_nr" ] || [ -z "$recover_nr" ]; then + echo "[skip]" + elif [ $stale_nr -lt $stale_min ] || { [ $stale_max -gt 0 ] && [ $stale_nr -gt $stale_max ]; } || [ $((stale_nr - recover_nr)) -ne $stale_delta ]; then echo "[fail] got $stale_nr stale[s] $recover_nr recover[s], " \ @@ -1495,12 +1601,12 @@ chk_add_nr() timeout=$(ip netns exec $ns1 sysctl -n net.mptcp.add_addr_timeout) printf "%-${nr_blank}s %s" " " "add" - count=$(ip netns exec $ns2 nstat -as MPTcpExtAddAddr | grep MPTcpExtAddAddr | awk '{print $2}') - [ -z "$count" ] && count=0 - + count=$(get_counter ${ns2} "MPTcpExtAddAddr") + if [ -z "$count" ]; then + echo -n "[skip]" # if the test configured a short timeout tolerate greater then expected # add addrs options, due to retransmissions - if [ "$count" != "$add_nr" ] && { [ "$timeout" -gt 1 ] || [ "$count" -lt "$add_nr" ]; }; then + elif [ "$count" != "$add_nr" ] && { [ "$timeout" -gt 1 ] || [ "$count" -lt "$add_nr" ]; }; then echo "[fail] got $count ADD_ADDR[s] expected $add_nr" fail_test dump_stats=1 @@ -1509,9 +1615,10 @@ chk_add_nr() fi echo -n " - echo " - count=$(ip netns exec $ns1 nstat -as | grep MPTcpExtEchoAdd | awk '{print $2}') - [ -z "$count" ] && count=0 - if [ "$count" != "$echo_nr" ]; then + count=$(get_counter ${ns1} "MPTcpExtEchoAdd") + if [ -z "$count" ]; then + echo -n "[skip]" + elif [ "$count" != "$echo_nr" ]; then echo "[fail] got $count ADD_ADDR echo[s] expected $echo_nr" fail_test dump_stats=1 @@ -1521,9 +1628,10 @@ chk_add_nr() if [ $port_nr -gt 0 ]; then echo -n " - pt " - count=$(ip netns exec $ns2 nstat -as | grep MPTcpExtPortAdd | awk '{print $2}') - [ -z "$count" ] && count=0 - if [ "$count" != "$port_nr" ]; then + count=$(get_counter ${ns2} "MPTcpExtPortAdd") + if [ -z "$count" ]; then + echo "[skip]" + elif [ "$count" != "$port_nr" ]; then echo "[fail] got $count ADD_ADDR[s] with a port-number expected $port_nr" fail_test dump_stats=1 @@ -1532,10 +1640,10 @@ chk_add_nr() fi printf "%-${nr_blank}s %s" " " "syn" - count=$(ip netns exec $ns1 nstat -as | grep MPTcpExtMPJoinPortSynRx | - awk '{print $2}') - [ -z "$count" ] && count=0 - if [ "$count" != "$syn_nr" ]; then + count=$(get_counter ${ns1} "MPTcpExtMPJoinPortSynRx") + if [ -z "$count" ]; then + echo -n "[skip]" + elif [ "$count" != "$syn_nr" ]; then echo "[fail] got $count JOIN[s] syn with a different \ port-number expected $syn_nr" fail_test @@ -1545,10 +1653,10 @@ chk_add_nr() fi echo -n " - synack" - count=$(ip netns exec $ns2 nstat -as | grep MPTcpExtMPJoinPortSynAckRx | - awk '{print $2}') - [ -z "$count" ] && count=0 - if [ "$count" != "$syn_ack_nr" ]; then + count=$(get_counter ${ns2} "MPTcpExtMPJoinPortSynAckRx") + if [ -z "$count" ]; then + echo -n "[skip]" + elif [ "$count" != "$syn_ack_nr" ]; then echo "[fail] got $count JOIN[s] synack with a different \ port-number expected $syn_ack_nr" fail_test @@ -1558,10 +1666,10 @@ chk_add_nr() fi echo -n " - ack" - count=$(ip netns exec $ns1 nstat -as | grep MPTcpExtMPJoinPortAckRx | - awk '{print $2}') - [ -z "$count" ] && count=0 - if [ "$count" != "$ack_nr" ]; then + count=$(get_counter ${ns1} "MPTcpExtMPJoinPortAckRx") + if [ -z "$count" ]; then + echo "[skip]" + elif [ "$count" != "$ack_nr" ]; then echo "[fail] got $count JOIN[s] ack with a different \ port-number expected $ack_nr" fail_test @@ -1571,10 +1679,10 @@ chk_add_nr() fi printf "%-${nr_blank}s %s" " " "syn" - count=$(ip netns exec $ns1 nstat -as | grep MPTcpExtMismatchPortSynRx | - awk '{print $2}') - [ -z "$count" ] && count=0 - if [ "$count" != "$mis_syn_nr" ]; then + count=$(get_counter ${ns1} "MPTcpExtMismatchPortSynRx") + if [ -z "$count" ]; then + echo -n "[skip]" + elif [ "$count" != "$mis_syn_nr" ]; then echo "[fail] got $count JOIN[s] syn with a mismatched \ port-number expected $mis_syn_nr" fail_test @@ -1584,10 +1692,10 @@ chk_add_nr() fi echo -n " - ack " - count=$(ip netns exec $ns1 nstat -as | grep MPTcpExtMismatchPortAckRx | - awk '{print $2}') - [ -z "$count" ] && count=0 - if [ "$count" != "$mis_ack_nr" ]; then + count=$(get_counter ${ns1} "MPTcpExtMismatchPortAckRx") + if [ -z "$count" ]; then + echo "[skip]" + elif [ "$count" != "$mis_ack_nr" ]; then echo "[fail] got $count JOIN[s] ack with a mismatched \ port-number expected $mis_ack_nr" fail_test @@ -1631,9 +1739,10 @@ chk_rm_nr() fi printf "%-${nr_blank}s %s" " " "rm " - count=$(ip netns exec $addr_ns nstat -as | grep MPTcpExtRmAddr | awk '{print $2}') - [ -z "$count" ] && count=0 - if [ "$count" != "$rm_addr_nr" ]; then + count=$(get_counter ${addr_ns} "MPTcpExtRmAddr") + if [ -z "$count" ]; then + echo -n "[skip]" + elif [ "$count" != "$rm_addr_nr" ]; then echo "[fail] got $count RM_ADDR[s] expected $rm_addr_nr" fail_test dump_stats=1 @@ -1642,29 +1751,27 @@ chk_rm_nr() fi echo -n " - rmsf " - count=$(ip netns exec $subflow_ns nstat -as | grep MPTcpExtRmSubflow | awk '{print $2}') - [ -z "$count" ] && count=0 - if [ -n "$simult" ]; then + count=$(get_counter ${subflow_ns} "MPTcpExtRmSubflow") + if [ -z "$count" ]; then + echo -n "[skip]" + elif [ -n "$simult" ]; then local cnt suffix - cnt=$(ip netns exec $addr_ns nstat -as | grep MPTcpExtRmSubflow | awk '{print $2}') + cnt=$(get_counter ${addr_ns} "MPTcpExtRmSubflow") # in case of simult flush, the subflow removal count on each side is # unreliable - [ -z "$cnt" ] && cnt=0 count=$((count + cnt)) [ "$count" != "$rm_subflow_nr" ] && suffix="$count in [$rm_subflow_nr:$((rm_subflow_nr*2))]" if [ $count -ge "$rm_subflow_nr" ] && \ [ "$count" -le "$((rm_subflow_nr *2 ))" ]; then - echo "[ ok ] $suffix" + echo -n "[ ok ] $suffix" else echo "[fail] got $count RM_SUBFLOW[s] expected in range [$rm_subflow_nr:$((rm_subflow_nr*2))]" fail_test dump_stats=1 fi - return - fi - if [ "$count" != "$rm_subflow_nr" ]; then + elif [ "$count" != "$rm_subflow_nr" ]; then echo "[fail] got $count RM_SUBFLOW[s] expected $rm_subflow_nr" fail_test dump_stats=1 @@ -1685,9 +1792,10 @@ chk_prio_nr() local dump_stats printf "%-${nr_blank}s %s" " " "ptx" - count=$(ip netns exec $ns1 nstat -as | grep MPTcpExtMPPrioTx | awk '{print $2}') - [ -z "$count" ] && count=0 - if [ "$count" != "$mp_prio_nr_tx" ]; then + count=$(get_counter ${ns1} "MPTcpExtMPPrioTx") + if [ -z "$count" ]; then + echo -n "[skip]" + elif [ "$count" != "$mp_prio_nr_tx" ]; then echo "[fail] got $count MP_PRIO[s] TX expected $mp_prio_nr_tx" fail_test dump_stats=1 @@ -1696,9 +1804,10 @@ chk_prio_nr() fi echo -n " - prx " - count=$(ip netns exec $ns1 nstat -as | grep MPTcpExtMPPrioRx | awk '{print $2}') - [ -z "$count" ] && count=0 - if [ "$count" != "$mp_prio_nr_rx" ]; then + count=$(get_counter ${ns1} "MPTcpExtMPPrioRx") + if [ -z "$count" ]; then + echo "[skip]" + elif [ "$count" != "$mp_prio_nr_rx" ]; then echo "[fail] got $count MP_PRIO[s] RX expected $mp_prio_nr_rx" fail_test dump_stats=1 @@ -1726,7 +1835,7 @@ chk_subflow_nr() cnt1=$(ss -N $ns1 -tOni | grep -c token) cnt2=$(ss -N $ns2 -tOni | grep -c token) - if [ "$cnt1" != "$subflow_nr" -o "$cnt2" != "$subflow_nr" ]; then + if [ "$cnt1" != "$subflow_nr" ] || [ "$cnt2" != "$subflow_nr" ]; then echo "[fail] got $cnt1:$cnt2 subflows expected $subflow_nr" fail_test dump_stats=1 @@ -1774,7 +1883,7 @@ wait_attempt_fail() while [ $time -lt $timeout_ms ]; do local cnt - cnt=$(ip netns exec $ns nstat -as TcpAttemptFails | grep TcpAttemptFails | awk '{print $2}') + cnt=$(get_counter ${ns} "TcpAttemptFails") [ "$cnt" = 1 ] && return 1 time=$((time + 100)) @@ -1867,23 +1976,23 @@ subflows_error_tests() fi # multiple subflows, with subflow creation error - if reset "multi subflows, with failing subflow"; then + if reset_with_tcp_filter "multi subflows, with failing subflow" ns1 10.0.3.2 REJECT && + continue_if mptcp_lib_kallsyms_has "mptcp_pm_subflow_check_next$"; then pm_nl_set_limits $ns1 0 2 pm_nl_set_limits $ns2 0 2 pm_nl_add_endpoint $ns2 10.0.3.2 flags subflow pm_nl_add_endpoint $ns2 10.0.2.2 flags subflow - filter_tcp_from $ns1 10.0.3.2 REJECT run_tests $ns1 $ns2 10.0.1.1 0 0 0 slow chk_join_nr 1 1 1 fi # multiple subflows, with subflow timeout on MPJ - if reset "multi subflows, with subflow timeout"; then + if reset_with_tcp_filter "multi subflows, with subflow timeout" ns1 10.0.3.2 DROP && + continue_if mptcp_lib_kallsyms_has "mptcp_pm_subflow_check_next$"; then pm_nl_set_limits $ns1 0 2 pm_nl_set_limits $ns2 0 2 pm_nl_add_endpoint $ns2 10.0.3.2 flags subflow pm_nl_add_endpoint $ns2 10.0.2.2 flags subflow - filter_tcp_from $ns1 10.0.3.2 DROP run_tests $ns1 $ns2 10.0.1.1 0 0 0 slow chk_join_nr 1 1 1 fi @@ -1891,11 +2000,11 @@ subflows_error_tests() # multiple subflows, check that the endpoint corresponding to # closed subflow (due to reset) is not reused if additional # subflows are added later - if reset "multi subflows, fair usage on close"; then + if reset_with_tcp_filter "multi subflows, fair usage on close" ns1 10.0.3.2 REJECT && + continue_if mptcp_lib_kallsyms_has "mptcp_pm_subflow_check_next$"; then pm_nl_set_limits $ns1 0 1 pm_nl_set_limits $ns2 0 1 pm_nl_add_endpoint $ns2 10.0.3.2 flags subflow - filter_tcp_from $ns1 10.0.3.2 REJECT run_tests $ns1 $ns2 10.0.1.1 0 0 0 slow & # mpj subflow will be in TW after the reset @@ -1995,11 +2104,18 @@ signal_address_tests() # the peer could possibly miss some addr notification, allow retransmission ip netns exec $ns1 sysctl -q net.mptcp.add_addr_timeout=1 run_tests $ns1 $ns2 10.0.1.1 0 0 0 slow - chk_join_nr 3 3 3 - # the server will not signal the address terminating - # the MPC subflow - chk_add_nr 3 3 + # It is not directly linked to the commit introducing this + # symbol but for the parent one which is linked anyway. + if ! mptcp_lib_kallsyms_has "mptcp_pm_subflow_check_next$"; then + chk_join_nr 3 3 2 + chk_add_nr 4 4 + else + chk_join_nr 3 3 3 + # the server will not signal the address terminating + # the MPC subflow + chk_add_nr 3 3 + fi fi } @@ -2240,7 +2356,12 @@ remove_tests() pm_nl_add_endpoint $ns2 10.0.4.2 flags subflow run_tests $ns1 $ns2 10.0.1.1 0 -8 -8 slow chk_join_nr 3 3 3 - chk_rm_nr 0 3 simult + + if mptcp_lib_kversion_ge 5.18; then + chk_rm_nr 0 3 simult + else + chk_rm_nr 3 3 + fi fi # addresses flush @@ -2479,7 +2600,8 @@ v4mapped_tests() backup_tests() { # single subflow, backup - if reset "single subflow, backup"; then + if reset "single subflow, backup" && + continue_if mptcp_lib_kallsyms_has "subflow_rebuild_header$"; then pm_nl_set_limits $ns1 0 1 pm_nl_set_limits $ns2 0 1 pm_nl_add_endpoint $ns2 10.0.3.2 flags subflow,backup @@ -2489,7 +2611,8 @@ backup_tests() fi # single address, backup - if reset "single address, backup"; then + if reset "single address, backup" && + continue_if mptcp_lib_kallsyms_has "subflow_rebuild_header$"; then pm_nl_set_limits $ns1 0 1 pm_nl_add_endpoint $ns1 10.0.2.1 flags signal pm_nl_set_limits $ns2 1 1 @@ -2500,7 +2623,8 @@ backup_tests() fi # single address with port, backup - if reset "single address with port, backup"; then + if reset "single address with port, backup" && + continue_if mptcp_lib_kallsyms_has "subflow_rebuild_header$"; then pm_nl_set_limits $ns1 0 1 pm_nl_add_endpoint $ns1 10.0.2.1 flags signal port 10100 pm_nl_set_limits $ns2 1 1 @@ -2510,14 +2634,16 @@ backup_tests() chk_prio_nr 1 1 fi - if reset "mpc backup"; then + if reset "mpc backup" && + continue_if mptcp_lib_kallsyms_doesnt_have "mptcp_subflow_send_ack$"; then pm_nl_add_endpoint $ns2 10.0.1.2 flags subflow,backup run_tests $ns1 $ns2 10.0.1.1 0 0 0 slow chk_join_nr 0 0 0 chk_prio_nr 0 1 fi - if reset "mpc backup both sides"; then + if reset "mpc backup both sides" && + continue_if mptcp_lib_kallsyms_doesnt_have "mptcp_subflow_send_ack$"; then pm_nl_add_endpoint $ns1 10.0.1.1 flags subflow,backup pm_nl_add_endpoint $ns2 10.0.1.2 flags subflow,backup run_tests $ns1 $ns2 10.0.1.1 0 0 0 slow @@ -2525,14 +2651,16 @@ backup_tests() chk_prio_nr 1 1 fi - if reset "mpc switch to backup"; then + if reset "mpc switch to backup" && + continue_if mptcp_lib_kallsyms_doesnt_have "mptcp_subflow_send_ack$"; then pm_nl_add_endpoint $ns2 10.0.1.2 flags subflow run_tests $ns1 $ns2 10.0.1.1 0 0 0 slow backup chk_join_nr 0 0 0 chk_prio_nr 0 1 fi - if reset "mpc switch to backup both sides"; then + if reset "mpc switch to backup both sides" && + continue_if mptcp_lib_kallsyms_doesnt_have "mptcp_subflow_send_ack$"; then pm_nl_add_endpoint $ns1 10.0.1.1 flags subflow pm_nl_add_endpoint $ns2 10.0.1.2 flags subflow run_tests $ns1 $ns2 10.0.1.1 0 0 0 slow backup @@ -2839,7 +2967,8 @@ fullmesh_tests() fi # set fullmesh flag - if reset "set fullmesh flag test"; then + if reset "set fullmesh flag test" && + continue_if mptcp_lib_kversion_ge 5.18; then pm_nl_set_limits $ns1 4 4 pm_nl_add_endpoint $ns1 10.0.2.1 flags subflow pm_nl_set_limits $ns2 4 4 @@ -2849,7 +2978,8 @@ fullmesh_tests() fi # set nofullmesh flag - if reset "set nofullmesh flag test"; then + if reset "set nofullmesh flag test" && + continue_if mptcp_lib_kversion_ge 5.18; then pm_nl_set_limits $ns1 4 4 pm_nl_add_endpoint $ns1 10.0.2.1 flags subflow,fullmesh pm_nl_set_limits $ns2 4 4 @@ -2859,7 +2989,8 @@ fullmesh_tests() fi # set backup,fullmesh flags - if reset "set backup,fullmesh flags test"; then + if reset "set backup,fullmesh flags test" && + continue_if mptcp_lib_kversion_ge 5.18; then pm_nl_set_limits $ns1 4 4 pm_nl_add_endpoint $ns1 10.0.2.1 flags subflow pm_nl_set_limits $ns2 4 4 @@ -2870,7 +3001,8 @@ fullmesh_tests() fi # set nobackup,nofullmesh flags - if reset "set nobackup,nofullmesh flags test"; then + if reset "set nobackup,nofullmesh flags test" && + continue_if mptcp_lib_kversion_ge 5.18; then pm_nl_set_limits $ns1 4 4 pm_nl_set_limits $ns2 4 4 pm_nl_add_endpoint $ns2 10.0.2.2 flags subflow,backup,fullmesh @@ -2883,14 +3015,14 @@ fullmesh_tests() fastclose_tests() { - if reset "fastclose test"; then + if reset_check_counter "fastclose test" "MPTcpExtMPFastcloseTx"; then run_tests $ns1 $ns2 10.0.1.1 1024 0 fastclose_client chk_join_nr 0 0 0 chk_fclose_nr 1 1 chk_rst_nr 1 1 invert fi - if reset "fastclose server test"; then + if reset_check_counter "fastclose server test" "MPTcpExtMPFastcloseRx"; then run_tests $ns1 $ns2 10.0.1.1 1024 0 fastclose_server chk_join_nr 0 0 0 chk_fclose_nr 1 1 invert @@ -2928,7 +3060,8 @@ fail_tests() userspace_tests() { # userspace pm type prevents add_addr - if reset "userspace pm type prevents add_addr"; then + if reset "userspace pm type prevents add_addr" && + continue_if mptcp_lib_has_file '/proc/sys/net/mptcp/pm_type'; then set_userspace_pm $ns1 pm_nl_set_limits $ns1 0 2 pm_nl_set_limits $ns2 0 2 @@ -2939,7 +3072,8 @@ userspace_tests() fi # userspace pm type does not echo add_addr without daemon - if reset "userspace pm no echo w/o daemon"; then + if reset "userspace pm no echo w/o daemon" && + continue_if mptcp_lib_has_file '/proc/sys/net/mptcp/pm_type'; then set_userspace_pm $ns2 pm_nl_set_limits $ns1 0 2 pm_nl_set_limits $ns2 0 2 @@ -2950,7 +3084,8 @@ userspace_tests() fi # userspace pm type rejects join - if reset "userspace pm type rejects join"; then + if reset "userspace pm type rejects join" && + continue_if mptcp_lib_has_file '/proc/sys/net/mptcp/pm_type'; then set_userspace_pm $ns1 pm_nl_set_limits $ns1 1 1 pm_nl_set_limits $ns2 1 1 @@ -2960,7 +3095,8 @@ userspace_tests() fi # userspace pm type does not send join - if reset "userspace pm type does not send join"; then + if reset "userspace pm type does not send join" && + continue_if mptcp_lib_has_file '/proc/sys/net/mptcp/pm_type'; then set_userspace_pm $ns2 pm_nl_set_limits $ns1 1 1 pm_nl_set_limits $ns2 1 1 @@ -2970,7 +3106,8 @@ userspace_tests() fi # userspace pm type prevents mp_prio - if reset "userspace pm type prevents mp_prio"; then + if reset "userspace pm type prevents mp_prio" && + continue_if mptcp_lib_has_file '/proc/sys/net/mptcp/pm_type'; then set_userspace_pm $ns1 pm_nl_set_limits $ns1 1 1 pm_nl_set_limits $ns2 1 1 @@ -2981,7 +3118,8 @@ userspace_tests() fi # userspace pm type prevents rm_addr - if reset "userspace pm type prevents rm_addr"; then + if reset "userspace pm type prevents rm_addr" && + continue_if mptcp_lib_has_file '/proc/sys/net/mptcp/pm_type'; then set_userspace_pm $ns1 set_userspace_pm $ns2 pm_nl_set_limits $ns1 0 1 @@ -2993,7 +3131,8 @@ userspace_tests() fi # userspace pm add & remove address - if reset "userspace pm add & remove address"; then + if reset "userspace pm add & remove address" && + continue_if mptcp_lib_has_file '/proc/sys/net/mptcp/pm_type'; then set_userspace_pm $ns1 pm_nl_set_limits $ns2 1 1 run_tests $ns1 $ns2 10.0.1.1 0 userspace_1 0 slow @@ -3003,7 +3142,8 @@ userspace_tests() fi # userspace pm create destroy subflow - if reset "userspace pm create destroy subflow"; then + if reset "userspace pm create destroy subflow" && + continue_if mptcp_lib_has_file '/proc/sys/net/mptcp/pm_type'; then set_userspace_pm $ns2 pm_nl_set_limits $ns1 0 1 run_tests $ns1 $ns2 10.0.1.1 0 0 userspace_1 slow @@ -3014,8 +3154,10 @@ userspace_tests() endpoint_tests() { + # subflow_rebuild_header is needed to support the implicit flag # userspace pm type prevents add_addr - if reset "implicit EP"; then + if reset "implicit EP" && + mptcp_lib_kallsyms_has "subflow_rebuild_header$"; then pm_nl_set_limits $ns1 2 2 pm_nl_set_limits $ns2 2 2 pm_nl_add_endpoint $ns1 10.0.2.1 flags signal @@ -3035,7 +3177,8 @@ endpoint_tests() kill_tests_wait fi - if reset "delete and re-add"; then + if reset "delete and re-add" && + mptcp_lib_kallsyms_has "subflow_rebuild_header$"; then pm_nl_set_limits $ns1 1 1 pm_nl_set_limits $ns2 1 1 pm_nl_add_endpoint $ns2 10.0.2.2 id 2 dev ns2eth2 flags subflow diff --git a/tools/testing/selftests/net/mptcp/mptcp_lib.sh b/tools/testing/selftests/net/mptcp/mptcp_lib.sh index 3286536b79d55..f32045b23b893 100644 --- a/tools/testing/selftests/net/mptcp/mptcp_lib.sh +++ b/tools/testing/selftests/net/mptcp/mptcp_lib.sh @@ -38,3 +38,67 @@ mptcp_lib_check_mptcp() { exit ${KSFT_SKIP} fi } + +mptcp_lib_check_kallsyms() { + if ! mptcp_lib_has_file "/proc/kallsyms"; then + echo "SKIP: CONFIG_KALLSYMS is missing" + exit ${KSFT_SKIP} + fi +} + +# Internal: use mptcp_lib_kallsyms_has() instead +__mptcp_lib_kallsyms_has() { + local sym="${1}" + + mptcp_lib_check_kallsyms + + grep -q " ${sym}" /proc/kallsyms +} + +# $1: part of a symbol to look at, add '$' at the end for full name +mptcp_lib_kallsyms_has() { + local sym="${1}" + + if __mptcp_lib_kallsyms_has "${sym}"; then + return 0 + fi + + mptcp_lib_fail_if_expected_feature "${sym} symbol not found" +} + +# $1: part of a symbol to look at, add '$' at the end for full name +mptcp_lib_kallsyms_doesnt_have() { + local sym="${1}" + + if ! __mptcp_lib_kallsyms_has "${sym}"; then + return 0 + fi + + mptcp_lib_fail_if_expected_feature "${sym} symbol has been found" +} + +# !!!AVOID USING THIS!!! +# Features might not land in the expected version and features can be backported +# +# $1: kernel version, e.g. 6.3 +mptcp_lib_kversion_ge() { + local exp_maj="${1%.*}" + local exp_min="${1#*.}" + local v maj min + + # If the kernel has backported features, set this env var to 1: + if [ "${SELFTESTS_MPTCP_LIB_NO_KVERSION_CHECK:-}" = "1" ]; then + return 0 + fi + + v=$(uname -r | cut -d'.' -f1,2) + maj=${v%.*} + min=${v#*.} + + if [ "${maj}" -gt "${exp_maj}" ] || + { [ "${maj}" -eq "${exp_maj}" ] && [ "${min}" -ge "${exp_min}" ]; }; then + return 0 + fi + + mptcp_lib_fail_if_expected_feature "kernel version ${1} lower than ${v}" +} diff --git a/tools/testing/selftests/net/mptcp/mptcp_sockopt.c b/tools/testing/selftests/net/mptcp/mptcp_sockopt.c index ae61f39556ca8..b35148edbf024 100644 --- a/tools/testing/selftests/net/mptcp/mptcp_sockopt.c +++ b/tools/testing/selftests/net/mptcp/mptcp_sockopt.c @@ -87,6 +87,10 @@ struct so_state { uint64_t tcpi_rcv_delta; }; +#ifndef MIN +#define MIN(a, b) ((a) < (b) ? (a) : (b)) +#endif + static void die_perror(const char *msg) { perror(msg); @@ -349,13 +353,14 @@ static void do_getsockopt_tcp_info(struct so_state *s, int fd, size_t r, size_t xerror("getsockopt MPTCP_TCPINFO (tries %d, %m)"); assert(olen <= sizeof(ti)); - assert(ti.d.size_user == ti.d.size_kernel); - assert(ti.d.size_user == sizeof(struct tcp_info)); + assert(ti.d.size_kernel > 0); + assert(ti.d.size_user == + MIN(ti.d.size_kernel, sizeof(struct tcp_info))); assert(ti.d.num_subflows == 1); assert(olen > (socklen_t)sizeof(struct mptcp_subflow_data)); olen -= sizeof(struct mptcp_subflow_data); - assert(olen == sizeof(struct tcp_info)); + assert(olen == ti.d.size_user); if (ti.ti[0].tcpi_bytes_sent == w && ti.ti[0].tcpi_bytes_received == r) @@ -401,13 +406,14 @@ static void do_getsockopt_subflow_addrs(int fd) die_perror("getsockopt MPTCP_SUBFLOW_ADDRS"); assert(olen <= sizeof(addrs)); - assert(addrs.d.size_user == addrs.d.size_kernel); - assert(addrs.d.size_user == sizeof(struct mptcp_subflow_addrs)); + assert(addrs.d.size_kernel > 0); + assert(addrs.d.size_user == + MIN(addrs.d.size_kernel, sizeof(struct mptcp_subflow_addrs))); assert(addrs.d.num_subflows == 1); assert(olen > (socklen_t)sizeof(struct mptcp_subflow_data)); olen -= sizeof(struct mptcp_subflow_data); - assert(olen == sizeof(struct mptcp_subflow_addrs)); + assert(olen == addrs.d.size_user); llen = sizeof(local); ret = getsockname(fd, (struct sockaddr *)&local, &llen); diff --git a/tools/testing/selftests/net/mptcp/mptcp_sockopt.sh b/tools/testing/selftests/net/mptcp/mptcp_sockopt.sh index 08d8533c98c45..a493eaf8633fc 100755 --- a/tools/testing/selftests/net/mptcp/mptcp_sockopt.sh +++ b/tools/testing/selftests/net/mptcp/mptcp_sockopt.sh @@ -84,6 +84,7 @@ cleanup() } mptcp_lib_check_mptcp +mptcp_lib_check_kallsyms ip -Version > /dev/null 2>&1 if [ $? -ne 0 ];then @@ -181,9 +182,14 @@ do_transfer() local_addr="0.0.0.0" fi + cmsg="TIMESTAMPNS" + if mptcp_lib_kallsyms_has "mptcp_ioctl$"; then + cmsg+=",TCPINQ" + fi + timeout ${timeout_test} \ ip netns exec ${listener_ns} \ - $mptcp_connect -t ${timeout_poll} -l -M 1 -p $port -s ${srv_proto} -c TIMESTAMPNS,TCPINQ \ + $mptcp_connect -t ${timeout_poll} -l -M 1 -p $port -s ${srv_proto} -c "${cmsg}" \ ${local_addr} < "$sin" > "$sout" & spid=$! @@ -191,7 +197,7 @@ do_transfer() timeout ${timeout_test} \ ip netns exec ${connector_ns} \ - $mptcp_connect -t ${timeout_poll} -M 2 -p $port -s ${cl_proto} -c TIMESTAMPNS,TCPINQ \ + $mptcp_connect -t ${timeout_poll} -M 2 -p $port -s ${cl_proto} -c "${cmsg}" \ $connect_addr < "$cin" > "$cout" & cpid=$! @@ -248,6 +254,11 @@ do_mptcp_sockopt_tests() { local lret=0 + if ! mptcp_lib_kallsyms_has "mptcp_diag_fill_info$"; then + echo "INFO: MPTCP sockopt not supported: SKIP" + return + fi + ip netns exec "$ns_sbox" ./mptcp_sockopt lret=$? @@ -305,6 +316,11 @@ do_tcpinq_tests() ip netns exec "$ns1" iptables -F ip netns exec "$ns1" ip6tables -F + if ! mptcp_lib_kallsyms_has "mptcp_ioctl$"; then + echo "INFO: TCP_INQ not supported: SKIP" + return + fi + for args in "-t tcp" "-r tcp"; do do_tcpinq_test $args lret=$? diff --git a/tools/testing/selftests/net/mptcp/pm_netlink.sh b/tools/testing/selftests/net/mptcp/pm_netlink.sh index 32f7533e0919a..d02e0d63a8f91 100755 --- a/tools/testing/selftests/net/mptcp/pm_netlink.sh +++ b/tools/testing/selftests/net/mptcp/pm_netlink.sh @@ -73,8 +73,12 @@ check() } check "ip netns exec $ns1 ./pm_nl_ctl dump" "" "defaults addr list" -check "ip netns exec $ns1 ./pm_nl_ctl limits" "accept 0 + +default_limits="$(ip netns exec $ns1 ./pm_nl_ctl limits)" +if mptcp_lib_expect_all_features; then + check "ip netns exec $ns1 ./pm_nl_ctl limits" "accept 0 subflows 2" "defaults limits" +fi ip netns exec $ns1 ./pm_nl_ctl add 10.0.1.1 ip netns exec $ns1 ./pm_nl_ctl add 10.0.1.2 flags subflow dev lo @@ -121,12 +125,10 @@ ip netns exec $ns1 ./pm_nl_ctl flush check "ip netns exec $ns1 ./pm_nl_ctl dump" "" "flush addrs" ip netns exec $ns1 ./pm_nl_ctl limits 9 1 -check "ip netns exec $ns1 ./pm_nl_ctl limits" "accept 0 -subflows 2" "rcv addrs above hard limit" +check "ip netns exec $ns1 ./pm_nl_ctl limits" "$default_limits" "rcv addrs above hard limit" ip netns exec $ns1 ./pm_nl_ctl limits 1 9 -check "ip netns exec $ns1 ./pm_nl_ctl limits" "accept 0 -subflows 2" "subflows above hard limit" +check "ip netns exec $ns1 ./pm_nl_ctl limits" "$default_limits" "subflows above hard limit" ip netns exec $ns1 ./pm_nl_ctl limits 8 8 check "ip netns exec $ns1 ./pm_nl_ctl limits" "accept 8 @@ -176,14 +178,19 @@ subflow,backup 10.0.1.1" "set flags (backup)" ip netns exec $ns1 ./pm_nl_ctl set 10.0.1.1 flags nobackup check "ip netns exec $ns1 ./pm_nl_ctl dump" "id 1 flags \ subflow 10.0.1.1" " (nobackup)" + +# fullmesh support has been added later ip netns exec $ns1 ./pm_nl_ctl set id 1 flags fullmesh -check "ip netns exec $ns1 ./pm_nl_ctl dump" "id 1 flags \ +if ip netns exec $ns1 ./pm_nl_ctl dump | grep -q "fullmesh" || + mptcp_lib_expect_all_features; then + check "ip netns exec $ns1 ./pm_nl_ctl dump" "id 1 flags \ subflow,fullmesh 10.0.1.1" " (fullmesh)" -ip netns exec $ns1 ./pm_nl_ctl set id 1 flags nofullmesh -check "ip netns exec $ns1 ./pm_nl_ctl dump" "id 1 flags \ + ip netns exec $ns1 ./pm_nl_ctl set id 1 flags nofullmesh + check "ip netns exec $ns1 ./pm_nl_ctl dump" "id 1 flags \ subflow 10.0.1.1" " (nofullmesh)" -ip netns exec $ns1 ./pm_nl_ctl set id 1 flags backup,fullmesh -check "ip netns exec $ns1 ./pm_nl_ctl dump" "id 1 flags \ + ip netns exec $ns1 ./pm_nl_ctl set id 1 flags backup,fullmesh + check "ip netns exec $ns1 ./pm_nl_ctl dump" "id 1 flags \ subflow,backup,fullmesh 10.0.1.1" " (backup,fullmesh)" +fi exit $ret diff --git a/tools/testing/selftests/net/mptcp/userspace_pm.sh b/tools/testing/selftests/net/mptcp/userspace_pm.sh index 084a2b0a197ec..eb0f4f6afebd3 100755 --- a/tools/testing/selftests/net/mptcp/userspace_pm.sh +++ b/tools/testing/selftests/net/mptcp/userspace_pm.sh @@ -5,10 +5,15 @@ mptcp_lib_check_mptcp +if ! mptcp_lib_has_file '/proc/sys/net/mptcp/pm_type'; then + echo "userspace pm tests are not supported by the kernel: SKIP" + exit ${KSFT_SKIP} +fi + ip -Version > /dev/null 2>&1 if [ $? -ne 0 ];then echo "SKIP: Cannot not run test without ip tool" - exit 1 + exit ${KSFT_SKIP} fi ANNOUNCED=6 # MPTCP_EVENT_ANNOUNCED diff --git a/tools/testing/selftests/net/tls.c b/tools/testing/selftests/net/tls.c index 2cbb12736596d..c0ad8385441f2 100644 --- a/tools/testing/selftests/net/tls.c +++ b/tools/testing/selftests/net/tls.c @@ -25,6 +25,8 @@ #define TLS_PAYLOAD_MAX_LEN 16384 #define SOL_TLS 282 +static int fips_enabled; + struct tls_crypto_info_keys { union { struct tls12_crypto_info_aes_gcm_128 aes128; @@ -235,7 +237,7 @@ FIXTURE_VARIANT(tls) { uint16_t tls_version; uint16_t cipher_type; - bool nopad; + bool nopad, fips_non_compliant; }; FIXTURE_VARIANT_ADD(tls, 12_aes_gcm) @@ -254,24 +256,28 @@ FIXTURE_VARIANT_ADD(tls, 12_chacha) { .tls_version = TLS_1_2_VERSION, .cipher_type = TLS_CIPHER_CHACHA20_POLY1305, + .fips_non_compliant = true, }; FIXTURE_VARIANT_ADD(tls, 13_chacha) { .tls_version = TLS_1_3_VERSION, .cipher_type = TLS_CIPHER_CHACHA20_POLY1305, + .fips_non_compliant = true, }; FIXTURE_VARIANT_ADD(tls, 13_sm4_gcm) { .tls_version = TLS_1_3_VERSION, .cipher_type = TLS_CIPHER_SM4_GCM, + .fips_non_compliant = true, }; FIXTURE_VARIANT_ADD(tls, 13_sm4_ccm) { .tls_version = TLS_1_3_VERSION, .cipher_type = TLS_CIPHER_SM4_CCM, + .fips_non_compliant = true, }; FIXTURE_VARIANT_ADD(tls, 12_aes_ccm) @@ -311,6 +317,9 @@ FIXTURE_SETUP(tls) int one = 1; int ret; + if (fips_enabled && variant->fips_non_compliant) + SKIP(return, "Unsupported cipher in FIPS mode"); + tls_crypto_info_init(variant->tls_version, variant->cipher_type, &tls12); @@ -1820,4 +1829,17 @@ TEST(tls_v6ops) { close(sfd); } +static void __attribute__((constructor)) fips_check(void) { + int res; + FILE *f; + + f = fopen("/proc/sys/crypto/fips_enabled", "r"); + if (f) { + res = fscanf(f, "%d", &fips_enabled); + if (res != 1) + ksft_print_msg("ERROR: Couldn't read /proc/sys/crypto/fips_enabled\n"); + fclose(f); + } +} + TEST_HARNESS_MAIN diff --git a/tools/testing/selftests/net/vrf-xfrm-tests.sh b/tools/testing/selftests/net/vrf-xfrm-tests.sh index 184da81f554ff..452638ae8aed8 100755 --- a/tools/testing/selftests/net/vrf-xfrm-tests.sh +++ b/tools/testing/selftests/net/vrf-xfrm-tests.sh @@ -264,60 +264,60 @@ setup_xfrm() ip -netns host1 xfrm state add src ${HOST1_4} dst ${HOST2_4} \ proto esp spi ${SPI_1} reqid 0 mode tunnel \ replay-window 4 replay-oseq 0x4 \ - auth-trunc 'hmac(md5)' ${AUTH_1} 96 \ - enc 'cbc(des3_ede)' ${ENC_1} \ + auth-trunc 'hmac(sha1)' ${AUTH_1} 96 \ + enc 'cbc(aes)' ${ENC_1} \ sel src ${h1_4} dst ${h2_4} ${devarg} ip -netns host2 xfrm state add src ${HOST1_4} dst ${HOST2_4} \ proto esp spi ${SPI_1} reqid 0 mode tunnel \ replay-window 4 replay-oseq 0x4 \ - auth-trunc 'hmac(md5)' ${AUTH_1} 96 \ - enc 'cbc(des3_ede)' ${ENC_1} \ + auth-trunc 'hmac(sha1)' ${AUTH_1} 96 \ + enc 'cbc(aes)' ${ENC_1} \ sel src ${h1_4} dst ${h2_4} ip -netns host1 xfrm state add src ${HOST2_4} dst ${HOST1_4} \ proto esp spi ${SPI_2} reqid 0 mode tunnel \ replay-window 4 replay-oseq 0x4 \ - auth-trunc 'hmac(md5)' ${AUTH_2} 96 \ - enc 'cbc(des3_ede)' ${ENC_2} \ + auth-trunc 'hmac(sha1)' ${AUTH_2} 96 \ + enc 'cbc(aes)' ${ENC_2} \ sel src ${h2_4} dst ${h1_4} ${devarg} ip -netns host2 xfrm state add src ${HOST2_4} dst ${HOST1_4} \ proto esp spi ${SPI_2} reqid 0 mode tunnel \ replay-window 4 replay-oseq 0x4 \ - auth-trunc 'hmac(md5)' ${AUTH_2} 96 \ - enc 'cbc(des3_ede)' ${ENC_2} \ + auth-trunc 'hmac(sha1)' ${AUTH_2} 96 \ + enc 'cbc(aes)' ${ENC_2} \ sel src ${h2_4} dst ${h1_4} ip -6 -netns host1 xfrm state add src ${HOST1_6} dst ${HOST2_6} \ proto esp spi ${SPI_1} reqid 0 mode tunnel \ replay-window 4 replay-oseq 0x4 \ - auth-trunc 'hmac(md5)' ${AUTH_1} 96 \ - enc 'cbc(des3_ede)' ${ENC_1} \ + auth-trunc 'hmac(sha1)' ${AUTH_1} 96 \ + enc 'cbc(aes)' ${ENC_1} \ sel src ${h1_6} dst ${h2_6} ${devarg} ip -6 -netns host2 xfrm state add src ${HOST1_6} dst ${HOST2_6} \ proto esp spi ${SPI_1} reqid 0 mode tunnel \ replay-window 4 replay-oseq 0x4 \ - auth-trunc 'hmac(md5)' ${AUTH_1} 96 \ - enc 'cbc(des3_ede)' ${ENC_1} \ + auth-trunc 'hmac(sha1)' ${AUTH_1} 96 \ + enc 'cbc(aes)' ${ENC_1} \ sel src ${h1_6} dst ${h2_6} ip -6 -netns host1 xfrm state add src ${HOST2_6} dst ${HOST1_6} \ proto esp spi ${SPI_2} reqid 0 mode tunnel \ replay-window 4 replay-oseq 0x4 \ - auth-trunc 'hmac(md5)' ${AUTH_2} 96 \ - enc 'cbc(des3_ede)' ${ENC_2} \ + auth-trunc 'hmac(sha1)' ${AUTH_2} 96 \ + enc 'cbc(aes)' ${ENC_2} \ sel src ${h2_6} dst ${h1_6} ${devarg} ip -6 -netns host2 xfrm state add src ${HOST2_6} dst ${HOST1_6} \ proto esp spi ${SPI_2} reqid 0 mode tunnel \ replay-window 4 replay-oseq 0x4 \ - auth-trunc 'hmac(md5)' ${AUTH_2} 96 \ - enc 'cbc(des3_ede)' ${ENC_2} \ + auth-trunc 'hmac(sha1)' ${AUTH_2} 96 \ + enc 'cbc(aes)' ${ENC_2} \ sel src ${h2_6} dst ${h1_6} } diff --git a/virt/kvm/kvm_main.c b/virt/kvm/kvm_main.c index de8e5eb6af106..640113f047efb 100644 --- a/virt/kvm/kvm_main.c +++ b/virt/kvm/kvm_main.c @@ -691,6 +691,24 @@ static __always_inline int kvm_handle_hva_range_no_flush(struct mmu_notifier *mn return __kvm_handle_hva_range(kvm, &range); } + +static bool kvm_change_spte_gfn(struct kvm *kvm, struct kvm_gfn_range *range) +{ + /* + * Skipping invalid memslots is correct if and only change_pte() is + * surrounded by invalidate_range_{start,end}(), which is currently + * guaranteed by the primary MMU. If that ever changes, KVM needs to + * unmap the memslot instead of skipping the memslot to ensure that KVM + * doesn't hold references to the old PFN. + */ + WARN_ON_ONCE(!READ_ONCE(kvm->mn_active_invalidate_count)); + + if (range->slot->flags & KVM_MEMSLOT_INVALID) + return false; + + return kvm_set_spte_gfn(kvm, range); +} + static void kvm_mmu_notifier_change_pte(struct mmu_notifier *mn, struct mm_struct *mm, unsigned long address, @@ -712,7 +730,7 @@ static void kvm_mmu_notifier_change_pte(struct mmu_notifier *mn, if (!READ_ONCE(kvm->mmu_invalidate_in_progress)) return; - kvm_handle_hva_range(mn, address, address + 1, pte, kvm_set_spte_gfn); + kvm_handle_hva_range(mn, address, address + 1, pte, kvm_change_spte_gfn); } void kvm_mmu_invalidate_begin(struct kvm *kvm, unsigned long start,