Re: Linux 6.1.71

From: Greg Kroah-Hartman
Date: Fri Jan 05 2024 - 09:47:03 EST


diff --git a/Makefile b/Makefile
index 270593fcafdc..2840e36fd559 100644
--- a/Makefile
+++ b/Makefile
@@ -1,7 +1,7 @@
# SPDX-License-Identifier: GPL-2.0
VERSION = 6
PATCHLEVEL = 1
-SUBLEVEL = 70
+SUBLEVEL = 71
EXTRAVERSION =
NAME = Curry Ramen

diff --git a/arch/arm/boot/dts/am33xx.dtsi b/arch/arm/boot/dts/am33xx.dtsi
index 32d397b3950b..b2e7f6a71074 100644
--- a/arch/arm/boot/dts/am33xx.dtsi
+++ b/arch/arm/boot/dts/am33xx.dtsi
@@ -349,6 +349,7 @@ usb: target-module@47400000 {
<SYSC_IDLE_NO>,
<SYSC_IDLE_SMART>,
<SYSC_IDLE_SMART_WKUP>;
+ ti,sysc-delay-us = <2>;
clocks = <&l3s_clkctrl AM3_L3S_USB_OTG_HS_CLKCTRL 0>;
clock-names = "fck";
#address-cells = <1>;
diff --git a/drivers/base/property.c b/drivers/base/property.c
index b0c40d973484..eb9b01c2ff1d 100644
--- a/drivers/base/property.c
+++ b/drivers/base/property.c
@@ -17,12 +17,19 @@
#include <linux/property.h>
#include <linux/phy.h>

-struct fwnode_handle *dev_fwnode(const struct device *dev)
+struct fwnode_handle *__dev_fwnode(struct device *dev)
{
return IS_ENABLED(CONFIG_OF) && dev->of_node ?
of_fwnode_handle(dev->of_node) : dev->fwnode;
}
-EXPORT_SYMBOL_GPL(dev_fwnode);
+EXPORT_SYMBOL_GPL(__dev_fwnode);
+
+const struct fwnode_handle *__dev_fwnode_const(const struct device *dev)
+{
+ return IS_ENABLED(CONFIG_OF) && dev->of_node ?
+ of_fwnode_handle(dev->of_node) : dev->fwnode;
+}
+EXPORT_SYMBOL_GPL(__dev_fwnode_const);

/**
* device_property_present - check if a property of a device is present
diff --git a/drivers/iio/imu/adis16475.c b/drivers/iio/imu/adis16475.c
index aec55f7e1f26..2d939773445d 100644
--- a/drivers/iio/imu/adis16475.c
+++ b/drivers/iio/imu/adis16475.c
@@ -1243,50 +1243,6 @@ static int adis16475_config_irq_pin(struct adis16475 *st)
return 0;
}

-static const struct of_device_id adis16475_of_match[] = {
- { .compatible = "adi,adis16470",
- .data = &adis16475_chip_info[ADIS16470] },
- { .compatible = "adi,adis16475-1",
- .data = &adis16475_chip_info[ADIS16475_1] },
- { .compatible = "adi,adis16475-2",
- .data = &adis16475_chip_info[ADIS16475_2] },
- { .compatible = "adi,adis16475-3",
- .data = &adis16475_chip_info[ADIS16475_3] },
- { .compatible = "adi,adis16477-1",
- .data = &adis16475_chip_info[ADIS16477_1] },
- { .compatible = "adi,adis16477-2",
- .data = &adis16475_chip_info[ADIS16477_2] },
- { .compatible = "adi,adis16477-3",
- .data = &adis16475_chip_info[ADIS16477_3] },
- { .compatible = "adi,adis16465-1",
- .data = &adis16475_chip_info[ADIS16465_1] },
- { .compatible = "adi,adis16465-2",
- .data = &adis16475_chip_info[ADIS16465_2] },
- { .compatible = "adi,adis16465-3",
- .data = &adis16475_chip_info[ADIS16465_3] },
- { .compatible = "adi,adis16467-1",
- .data = &adis16475_chip_info[ADIS16467_1] },
- { .compatible = "adi,adis16467-2",
- .data = &adis16475_chip_info[ADIS16467_2] },
- { .compatible = "adi,adis16467-3",
- .data = &adis16475_chip_info[ADIS16467_3] },
- { .compatible = "adi,adis16500",
- .data = &adis16475_chip_info[ADIS16500] },
- { .compatible = "adi,adis16505-1",
- .data = &adis16475_chip_info[ADIS16505_1] },
- { .compatible = "adi,adis16505-2",
- .data = &adis16475_chip_info[ADIS16505_2] },
- { .compatible = "adi,adis16505-3",
- .data = &adis16475_chip_info[ADIS16505_3] },
- { .compatible = "adi,adis16507-1",
- .data = &adis16475_chip_info[ADIS16507_1] },
- { .compatible = "adi,adis16507-2",
- .data = &adis16475_chip_info[ADIS16507_2] },
- { .compatible = "adi,adis16507-3",
- .data = &adis16475_chip_info[ADIS16507_3] },
- { },
-};
-MODULE_DEVICE_TABLE(of, adis16475_of_match);

static int adis16475_probe(struct spi_device *spi)
{
@@ -1300,7 +1256,7 @@ static int adis16475_probe(struct spi_device *spi)

st = iio_priv(indio_dev);

- st->info = device_get_match_data(&spi->dev);
+ st->info = spi_get_device_match_data(spi);
if (!st->info)
return -EINVAL;

@@ -1340,12 +1296,83 @@ static int adis16475_probe(struct spi_device *spi)
return 0;
}

+static const struct of_device_id adis16475_of_match[] = {
+ { .compatible = "adi,adis16470",
+ .data = &adis16475_chip_info[ADIS16470] },
+ { .compatible = "adi,adis16475-1",
+ .data = &adis16475_chip_info[ADIS16475_1] },
+ { .compatible = "adi,adis16475-2",
+ .data = &adis16475_chip_info[ADIS16475_2] },
+ { .compatible = "adi,adis16475-3",
+ .data = &adis16475_chip_info[ADIS16475_3] },
+ { .compatible = "adi,adis16477-1",
+ .data = &adis16475_chip_info[ADIS16477_1] },
+ { .compatible = "adi,adis16477-2",
+ .data = &adis16475_chip_info[ADIS16477_2] },
+ { .compatible = "adi,adis16477-3",
+ .data = &adis16475_chip_info[ADIS16477_3] },
+ { .compatible = "adi,adis16465-1",
+ .data = &adis16475_chip_info[ADIS16465_1] },
+ { .compatible = "adi,adis16465-2",
+ .data = &adis16475_chip_info[ADIS16465_2] },
+ { .compatible = "adi,adis16465-3",
+ .data = &adis16475_chip_info[ADIS16465_3] },
+ { .compatible = "adi,adis16467-1",
+ .data = &adis16475_chip_info[ADIS16467_1] },
+ { .compatible = "adi,adis16467-2",
+ .data = &adis16475_chip_info[ADIS16467_2] },
+ { .compatible = "adi,adis16467-3",
+ .data = &adis16475_chip_info[ADIS16467_3] },
+ { .compatible = "adi,adis16500",
+ .data = &adis16475_chip_info[ADIS16500] },
+ { .compatible = "adi,adis16505-1",
+ .data = &adis16475_chip_info[ADIS16505_1] },
+ { .compatible = "adi,adis16505-2",
+ .data = &adis16475_chip_info[ADIS16505_2] },
+ { .compatible = "adi,adis16505-3",
+ .data = &adis16475_chip_info[ADIS16505_3] },
+ { .compatible = "adi,adis16507-1",
+ .data = &adis16475_chip_info[ADIS16507_1] },
+ { .compatible = "adi,adis16507-2",
+ .data = &adis16475_chip_info[ADIS16507_2] },
+ { .compatible = "adi,adis16507-3",
+ .data = &adis16475_chip_info[ADIS16507_3] },
+ { },
+};
+MODULE_DEVICE_TABLE(of, adis16475_of_match);
+
+static const struct spi_device_id adis16475_ids[] = {
+ { "adis16470", (kernel_ulong_t)&adis16475_chip_info[ADIS16470] },
+ { "adis16475-1", (kernel_ulong_t)&adis16475_chip_info[ADIS16475_1] },
+ { "adis16475-2", (kernel_ulong_t)&adis16475_chip_info[ADIS16475_2] },
+ { "adis16475-3", (kernel_ulong_t)&adis16475_chip_info[ADIS16475_3] },
+ { "adis16477-1", (kernel_ulong_t)&adis16475_chip_info[ADIS16477_1] },
+ { "adis16477-2", (kernel_ulong_t)&adis16475_chip_info[ADIS16477_2] },
+ { "adis16477-3", (kernel_ulong_t)&adis16475_chip_info[ADIS16477_3] },
+ { "adis16465-1", (kernel_ulong_t)&adis16475_chip_info[ADIS16465_1] },
+ { "adis16465-2", (kernel_ulong_t)&adis16475_chip_info[ADIS16465_2] },
+ { "adis16465-3", (kernel_ulong_t)&adis16475_chip_info[ADIS16465_3] },
+ { "adis16467-1", (kernel_ulong_t)&adis16475_chip_info[ADIS16467_1] },
+ { "adis16467-2", (kernel_ulong_t)&adis16475_chip_info[ADIS16467_2] },
+ { "adis16467-3", (kernel_ulong_t)&adis16475_chip_info[ADIS16467_3] },
+ { "adis16500", (kernel_ulong_t)&adis16475_chip_info[ADIS16500] },
+ { "adis16505-1", (kernel_ulong_t)&adis16475_chip_info[ADIS16505_1] },
+ { "adis16505-2", (kernel_ulong_t)&adis16475_chip_info[ADIS16505_2] },
+ { "adis16505-3", (kernel_ulong_t)&adis16475_chip_info[ADIS16505_3] },
+ { "adis16507-1", (kernel_ulong_t)&adis16475_chip_info[ADIS16507_1] },
+ { "adis16507-2", (kernel_ulong_t)&adis16475_chip_info[ADIS16507_2] },
+ { "adis16507-3", (kernel_ulong_t)&adis16475_chip_info[ADIS16507_3] },
+ { }
+};
+MODULE_DEVICE_TABLE(spi, adis16475_ids);
+
static struct spi_driver adis16475_driver = {
.driver = {
.name = "adis16475",
.of_match_table = adis16475_of_match,
},
.probe = adis16475_probe,
+ .id_table = adis16475_ids,
};
module_spi_driver(adis16475_driver);

diff --git a/drivers/spi/spi-atmel.c b/drivers/spi/spi-atmel.c
index c4f22d50dba5..78daf2b2143c 100644
--- a/drivers/spi/spi-atmel.c
+++ b/drivers/spi/spi-atmel.c
@@ -22,6 +22,7 @@
#include <linux/gpio/consumer.h>
#include <linux/pinctrl/consumer.h>
#include <linux/pm_runtime.h>
+#include <linux/iopoll.h>
#include <trace/events/spi.h>

/* SPI register offsets */
@@ -278,6 +279,7 @@ struct atmel_spi {
bool keep_cs;

u32 fifo_size;
+ bool last_polarity;
u8 native_cs_free;
u8 native_cs_for_gpio;
};
@@ -290,6 +292,22 @@ struct atmel_spi_device {
#define SPI_MAX_DMA_XFER 65535 /* true for both PDC and DMA */
#define INVALID_DMA_ADDRESS 0xffffffff

+/*
+ * This frequency can be anything supported by the controller, but to avoid
+ * unnecessary delay, the highest possible frequency is chosen.
+ *
+ * This frequency is the highest possible which is not interfering with other
+ * chip select registers (see Note for Serial Clock Bit Rate configuration in
+ * Atmel-11121F-ATARM-SAMA5D3-Series-Datasheet_02-Feb-16, page 1283)
+ */
+#define DUMMY_MSG_FREQUENCY 0x02
+/*
+ * 8 bits is the minimum data the controller is capable of sending.
+ *
+ * This message can be anything as it should not be treated by any SPI device.
+ */
+#define DUMMY_MSG 0xAA
+
/*
* Version 2 of the SPI controller has
* - CR.LASTXFER
@@ -303,6 +321,43 @@ static bool atmel_spi_is_v2(struct atmel_spi *as)
return as->caps.is_spi2;
}

+/*
+ * Send a dummy message.
+ *
+ * This is sometimes needed when using a CS GPIO to force clock transition when
+ * switching between devices with different polarities.
+ */
+static void atmel_spi_send_dummy(struct atmel_spi *as, struct spi_device *spi, int chip_select)
+{
+ u32 status;
+ u32 csr;
+
+ /*
+ * Set a clock frequency to allow sending message on SPI bus.
+ * The frequency here can be anything, but is needed for
+ * the controller to send the data.
+ */
+ csr = spi_readl(as, CSR0 + 4 * chip_select);
+ csr = SPI_BFINS(SCBR, DUMMY_MSG_FREQUENCY, csr);
+ spi_writel(as, CSR0 + 4 * chip_select, csr);
+
+ /*
+ * Read all data coming from SPI bus, needed to be able to send
+ * the message.
+ */
+ spi_readl(as, RDR);
+ while (spi_readl(as, SR) & SPI_BIT(RDRF)) {
+ spi_readl(as, RDR);
+ cpu_relax();
+ }
+
+ spi_writel(as, TDR, DUMMY_MSG);
+
+ readl_poll_timeout_atomic(as->regs + SPI_SR, status,
+ (status & SPI_BIT(TXEMPTY)), 1, 1000);
+}
+
+
/*
* Earlier SPI controllers (e.g. on at91rm9200) have a design bug whereby
* they assume that spi slave device state will not change on deselect, so
@@ -319,11 +374,17 @@ static bool atmel_spi_is_v2(struct atmel_spi *as)
* Master on Chip Select 0.") No workaround exists for that ... so for
* nCS0 on that chip, we (a) don't use the GPIO, (b) can't support CS_HIGH,
* and (c) will trigger that first erratum in some cases.
+ *
+ * When changing the clock polarity, the SPI controller waits for the next
+ * transmission to enforce the default clock state. This may be an issue when
+ * using a GPIO as Chip Select: the clock level is applied only when the first
+ * packet is sent, once the CS has already been asserted. The workaround is to
+ * avoid this by sending a first (dummy) message before toggling the CS state.
*/
-
static void cs_activate(struct atmel_spi *as, struct spi_device *spi)
{
struct atmel_spi_device *asd = spi->controller_state;
+ bool new_polarity;
int chip_select;
u32 mr;

@@ -352,6 +413,25 @@ static void cs_activate(struct atmel_spi *as, struct spi_device *spi)
}

mr = spi_readl(as, MR);
+
+ /*
+ * Ensures the clock polarity is valid before we actually
+ * assert the CS to avoid spurious clock edges to be
+ * processed by the spi devices.
+ */
+ if (spi_get_csgpiod(spi, 0)) {
+ new_polarity = (asd->csr & SPI_BIT(CPOL)) != 0;
+ if (new_polarity != as->last_polarity) {
+ /*
+ * Need to disable the GPIO before sending the dummy
+ * message because it is already set by the spi core.
+ */
+ gpiod_set_value_cansleep(spi_get_csgpiod(spi, 0), 0);
+ atmel_spi_send_dummy(as, spi, chip_select);
+ as->last_polarity = new_polarity;
+ gpiod_set_value_cansleep(spi_get_csgpiod(spi, 0), 1);
+ }
+ }
} else {
u32 cpol = (spi->mode & SPI_CPOL) ? SPI_BIT(CPOL) : 0;
int i;
diff --git a/drivers/spi/spi.c b/drivers/spi/spi.c
index 5d046be8b2dd..22d227878bc4 100644
--- a/drivers/spi/spi.c
+++ b/drivers/spi/spi.c
@@ -360,6 +360,18 @@ const struct spi_device_id *spi_get_device_id(const struct spi_device *sdev)
}
EXPORT_SYMBOL_GPL(spi_get_device_id);

+const void *spi_get_device_match_data(const struct spi_device *sdev)
+{
+ const void *match;
+
+ match = device_get_match_data(&sdev->dev);
+ if (match)
+ return match;
+
+ return (const void *)spi_get_device_id(sdev)->driver_data;
+}
+EXPORT_SYMBOL_GPL(spi_get_device_match_data);
+
static int spi_match_device(struct device *dev, struct device_driver *drv)
{
const struct spi_device *spi = to_spi_device(dev);
@@ -592,7 +604,7 @@ static void spi_dev_set_name(struct spi_device *spi)
}

dev_set_name(&spi->dev, "%s.%u", dev_name(&spi->controller->dev),
- spi->chip_select);
+ spi_get_chipselect(spi, 0));
}

static int spi_dev_check(struct device *dev, void *data)
@@ -601,7 +613,7 @@ static int spi_dev_check(struct device *dev, void *data)
struct spi_device *new_spi = data;

if (spi->controller == new_spi->controller &&
- spi->chip_select == new_spi->chip_select)
+ spi_get_chipselect(spi, 0) == spi_get_chipselect(new_spi, 0))
return -EBUSY;
return 0;
}
@@ -626,7 +638,7 @@ static int __spi_add_device(struct spi_device *spi)
status = bus_for_each_dev(&spi_bus_type, NULL, spi, spi_dev_check);
if (status) {
dev_err(dev, "chipselect %d already in use\n",
- spi->chip_select);
+ spi_get_chipselect(spi, 0));
return status;
}

@@ -637,7 +649,7 @@ static int __spi_add_device(struct spi_device *spi)
}

if (ctlr->cs_gpiods)
- spi->cs_gpiod = ctlr->cs_gpiods[spi->chip_select];
+ spi_set_csgpiod(spi, 0, ctlr->cs_gpiods[spi_get_chipselect(spi, 0)]);

/*
* Drivers may modify this initial i/o setup, but will
@@ -680,8 +692,8 @@ int spi_add_device(struct spi_device *spi)
int status;

/* Chipselects are numbered 0..max; validate. */
- if (spi->chip_select >= ctlr->num_chipselect) {
- dev_err(dev, "cs%d >= max %d\n", spi->chip_select,
+ if (spi_get_chipselect(spi, 0) >= ctlr->num_chipselect) {
+ dev_err(dev, "cs%d >= max %d\n", spi_get_chipselect(spi, 0),
ctlr->num_chipselect);
return -EINVAL;
}
@@ -702,8 +714,8 @@ static int spi_add_device_locked(struct spi_device *spi)
struct device *dev = ctlr->dev.parent;

/* Chipselects are numbered 0..max; validate. */
- if (spi->chip_select >= ctlr->num_chipselect) {
- dev_err(dev, "cs%d >= max %d\n", spi->chip_select,
+ if (spi_get_chipselect(spi, 0) >= ctlr->num_chipselect) {
+ dev_err(dev, "cs%d >= max %d\n", spi_get_chipselect(spi, 0),
ctlr->num_chipselect);
return -EINVAL;
}
@@ -749,7 +761,7 @@ struct spi_device *spi_new_device(struct spi_controller *ctlr,

WARN_ON(strlen(chip->modalias) >= sizeof(proxy->modalias));

- proxy->chip_select = chip->chip_select;
+ spi_set_chipselect(proxy, 0, chip->chip_select);
proxy->max_speed_hz = chip->max_speed_hz;
proxy->mode = chip->mode;
proxy->irq = chip->irq;
@@ -958,24 +970,23 @@ static void spi_set_cs(struct spi_device *spi, bool enable, bool force)
* Avoid calling into the driver (or doing delays) if the chip select
* isn't actually changing from the last time this was called.
*/
- if (!force && ((enable && spi->controller->last_cs == spi->chip_select) ||
- (!enable && spi->controller->last_cs != spi->chip_select)) &&
+ if (!force && ((enable && spi->controller->last_cs == spi_get_chipselect(spi, 0)) ||
+ (!enable && spi->controller->last_cs != spi_get_chipselect(spi, 0))) &&
(spi->controller->last_cs_mode_high == (spi->mode & SPI_CS_HIGH)))
return;

trace_spi_set_cs(spi, activate);

- spi->controller->last_cs = enable ? spi->chip_select : -1;
+ spi->controller->last_cs = enable ? spi_get_chipselect(spi, 0) : -1;
spi->controller->last_cs_mode_high = spi->mode & SPI_CS_HIGH;

- if ((spi->cs_gpiod || !spi->controller->set_cs_timing) && !activate) {
+ if ((spi_get_csgpiod(spi, 0) || !spi->controller->set_cs_timing) && !activate)
spi_delay_exec(&spi->cs_hold, NULL);
- }

if (spi->mode & SPI_CS_HIGH)
enable = !enable;

- if (spi->cs_gpiod) {
+ if (spi_get_csgpiod(spi, 0)) {
if (!(spi->mode & SPI_NO_CS)) {
/*
* Historically ACPI has no means of the GPIO polarity and
@@ -988,10 +999,10 @@ static void spi_set_cs(struct spi_device *spi, bool enable, bool force)
* into account.
*/
if (has_acpi_companion(&spi->dev))
- gpiod_set_value_cansleep(spi->cs_gpiod, !enable);
+ gpiod_set_value_cansleep(spi_get_csgpiod(spi, 0), !enable);
else
/* Polarity handled by GPIO library */
- gpiod_set_value_cansleep(spi->cs_gpiod, activate);
+ gpiod_set_value_cansleep(spi_get_csgpiod(spi, 0), activate);
}
/* Some SPI masters need both GPIO CS & slave_select */
if ((spi->controller->flags & SPI_MASTER_GPIO_SS) &&
@@ -1001,7 +1012,7 @@ static void spi_set_cs(struct spi_device *spi, bool enable, bool force)
spi->controller->set_cs(spi, !enable);
}

- if (spi->cs_gpiod || !spi->controller->set_cs_timing) {
+ if (spi_get_csgpiod(spi, 0) || !spi->controller->set_cs_timing) {
if (activate)
spi_delay_exec(&spi->cs_setup, NULL);
else
@@ -2291,7 +2302,7 @@ static int of_spi_parse_dt(struct spi_controller *ctlr, struct spi_device *spi,
nc, rc);
return rc;
}
- spi->chip_select = value;
+ spi_set_chipselect(spi, 0, value);

/* Device speed */
if (!of_property_read_u32(nc, "spi-max-frequency", &value))
@@ -2405,7 +2416,7 @@ struct spi_device *spi_new_ancillary_device(struct spi_device *spi,
strscpy(ancillary->modalias, "dummy", sizeof(ancillary->modalias));

/* Use provided chip-select for ancillary device */
- ancillary->chip_select = chip_select;
+ spi_set_chipselect(ancillary, 0, chip_select);

/* Take over SPI mode/speed from SPI main device */
ancillary->max_speed_hz = spi->max_speed_hz;
@@ -2652,7 +2663,7 @@ struct spi_device *acpi_spi_device_alloc(struct spi_controller *ctlr,
spi->mode |= lookup.mode;
spi->irq = lookup.irq;
spi->bits_per_word = lookup.bits_per_word;
- spi->chip_select = lookup.chip_select;
+ spi_set_chipselect(spi, 0, lookup.chip_select);

return spi;
}
@@ -3611,6 +3622,37 @@ static int __spi_validate_bits_per_word(struct spi_controller *ctlr,
return 0;
}

+/**
+ * spi_set_cs_timing - configure CS setup, hold, and inactive delays
+ * @spi: the device that requires specific CS timing configuration
+ *
+ * Return: zero on success, else a negative error code.
+ */
+static int spi_set_cs_timing(struct spi_device *spi)
+{
+ struct device *parent = spi->controller->dev.parent;
+ int status = 0;
+
+ if (spi->controller->set_cs_timing && !spi_get_csgpiod(spi, 0)) {
+ if (spi->controller->auto_runtime_pm) {
+ status = pm_runtime_get_sync(parent);
+ if (status < 0) {
+ pm_runtime_put_noidle(parent);
+ dev_err(&spi->controller->dev, "Failed to power device: %d\n",
+ status);
+ return status;
+ }
+
+ status = spi->controller->set_cs_timing(spi);
+ pm_runtime_mark_last_busy(parent);
+ pm_runtime_put_autosuspend(parent);
+ } else {
+ status = spi->controller->set_cs_timing(spi);
+ }
+ }
+ return status;
+}
+
/**
* spi_setup - setup SPI mode and clock rate
* @spi: the device whose settings are being modified
@@ -3707,6 +3749,12 @@ int spi_setup(struct spi_device *spi)
}
}

+ status = spi_set_cs_timing(spi);
+ if (status) {
+ mutex_unlock(&spi->controller->io_mutex);
+ return status;
+ }
+
if (spi->controller->auto_runtime_pm && spi->controller->set_cs) {
status = pm_runtime_resume_and_get(spi->controller->dev.parent);
if (status < 0) {
@@ -3790,7 +3838,7 @@ static int __spi_validate(struct spi_device *spi, struct spi_message *message)
* cs_change is set for each transfer.
*/
if ((spi->mode & SPI_CS_WORD) && (!(ctlr->mode_bits & SPI_CS_WORD) ||
- spi->cs_gpiod)) {
+ spi_get_csgpiod(spi, 0))) {
size_t maxsize;
int ret;

diff --git a/drivers/usb/host/fotg210-hcd.c b/drivers/usb/host/fotg210-hcd.c
index c4c1fbc12b4c..dc968960769e 100644
--- a/drivers/usb/host/fotg210-hcd.c
+++ b/drivers/usb/host/fotg210-hcd.c
@@ -429,8 +429,6 @@ static void qh_lines(struct fotg210_hcd *fotg210, struct fotg210_qh *qh,
temp = size;
size -= temp;
next += temp;
- if (temp == size)
- goto done;
}

temp = snprintf(next, size, "\n");
@@ -440,7 +438,6 @@ static void qh_lines(struct fotg210_hcd *fotg210, struct fotg210_qh *qh,
size -= temp;
next += temp;

-done:
*sizep = size;
*nextp = next;
}
diff --git a/fs/namei.c b/fs/namei.c
index 5e1c2ab2ae70..b5578f4ce5d6 100644
--- a/fs/namei.c
+++ b/fs/namei.c
@@ -253,6 +253,7 @@ getname_kernel(const char * filename)

return result;
}
+EXPORT_SYMBOL(getname_kernel);

void putname(struct filename *name)
{
@@ -271,6 +272,7 @@ void putname(struct filename *name)
} else
__putname(name);
}
+EXPORT_SYMBOL(putname);

/**
* check_acl - perform ACL permission checking
@@ -1581,8 +1583,9 @@ static struct dentry *lookup_dcache(const struct qstr *name,
* when directory is guaranteed to have no in-lookup children
* at all.
*/
-static struct dentry *__lookup_hash(const struct qstr *name,
- struct dentry *base, unsigned int flags)
+struct dentry *lookup_one_qstr_excl(const struct qstr *name,
+ struct dentry *base,
+ unsigned int flags)
{
struct dentry *dentry = lookup_dcache(name, base, flags);
struct dentry *old;
@@ -1606,6 +1609,7 @@ static struct dentry *__lookup_hash(const struct qstr *name,
}
return dentry;
}
+EXPORT_SYMBOL(lookup_one_qstr_excl);

static struct dentry *lookup_fast(struct nameidata *nd)
{
@@ -2532,16 +2536,17 @@ static int path_parentat(struct nameidata *nd, unsigned flags,
}

/* Note: this does not consume "name" */
-static int filename_parentat(int dfd, struct filename *name,
- unsigned int flags, struct path *parent,
- struct qstr *last, int *type)
+static int __filename_parentat(int dfd, struct filename *name,
+ unsigned int flags, struct path *parent,
+ struct qstr *last, int *type,
+ const struct path *root)
{
int retval;
struct nameidata nd;

if (IS_ERR(name))
return PTR_ERR(name);
- set_nameidata(&nd, dfd, name, NULL);
+ set_nameidata(&nd, dfd, name, root);
retval = path_parentat(&nd, flags | LOOKUP_RCU, parent);
if (unlikely(retval == -ECHILD))
retval = path_parentat(&nd, flags, parent);
@@ -2556,6 +2561,13 @@ static int filename_parentat(int dfd, struct filename *name,
return retval;
}

+static int filename_parentat(int dfd, struct filename *name,
+ unsigned int flags, struct path *parent,
+ struct qstr *last, int *type)
+{
+ return __filename_parentat(dfd, name, flags, parent, last, type, NULL);
+}
+
/* does lookup, returns the object with parent locked */
static struct dentry *__kern_path_locked(struct filename *name, struct path *path)
{
@@ -2571,7 +2583,7 @@ static struct dentry *__kern_path_locked(struct filename *name, struct path *pat
return ERR_PTR(-EINVAL);
}
inode_lock_nested(path->dentry->d_inode, I_MUTEX_PARENT);
- d = __lookup_hash(&last, path->dentry, 0);
+ d = lookup_one_qstr_excl(&last, path->dentry, 0);
if (IS_ERR(d)) {
inode_unlock(path->dentry->d_inode);
path_put(path);
@@ -2599,6 +2611,24 @@ int kern_path(const char *name, unsigned int flags, struct path *path)
}
EXPORT_SYMBOL(kern_path);

+/**
+ * vfs_path_parent_lookup - lookup a parent path relative to a dentry-vfsmount pair
+ * @filename: filename structure
+ * @flags: lookup flags
+ * @parent: pointer to struct path to fill
+ * @last: last component
+ * @type: type of the last component
+ * @root: pointer to struct path of the base directory
+ */
+int vfs_path_parent_lookup(struct filename *filename, unsigned int flags,
+ struct path *parent, struct qstr *last, int *type,
+ const struct path *root)
+{
+ return __filename_parentat(AT_FDCWD, filename, flags, parent, last,
+ type, root);
+}
+EXPORT_SYMBOL(vfs_path_parent_lookup);
+
/**
* vfs_path_lookup - lookup a file path relative to a dentry-vfsmount pair
* @dentry: pointer to dentry of the base directory
@@ -2980,20 +3010,10 @@ static inline int may_create(struct user_namespace *mnt_userns,
return inode_permission(mnt_userns, dir, MAY_WRITE | MAY_EXEC);
}

-/*
- * p1 and p2 should be directories on the same fs.
- */
-struct dentry *lock_rename(struct dentry *p1, struct dentry *p2)
+static struct dentry *lock_two_directories(struct dentry *p1, struct dentry *p2)
{
struct dentry *p;

- if (p1 == p2) {
- inode_lock_nested(p1->d_inode, I_MUTEX_PARENT);
- return NULL;
- }
-
- mutex_lock(&p1->d_sb->s_vfs_rename_mutex);
-
p = d_ancestor(p2, p1);
if (p) {
inode_lock_nested(p2->d_inode, I_MUTEX_PARENT);
@@ -3012,8 +3032,64 @@ struct dentry *lock_rename(struct dentry *p1, struct dentry *p2)
I_MUTEX_PARENT, I_MUTEX_PARENT2);
return NULL;
}
+
+/*
+ * p1 and p2 should be directories on the same fs.
+ */
+struct dentry *lock_rename(struct dentry *p1, struct dentry *p2)
+{
+ if (p1 == p2) {
+ inode_lock_nested(p1->d_inode, I_MUTEX_PARENT);
+ return NULL;
+ }
+
+ mutex_lock(&p1->d_sb->s_vfs_rename_mutex);
+ return lock_two_directories(p1, p2);
+}
EXPORT_SYMBOL(lock_rename);

+/*
+ * c1 and p2 should be on the same fs.
+ */
+struct dentry *lock_rename_child(struct dentry *c1, struct dentry *p2)
+{
+ if (READ_ONCE(c1->d_parent) == p2) {
+ /*
+ * hopefully won't need to touch ->s_vfs_rename_mutex at all.
+ */
+ inode_lock_nested(p2->d_inode, I_MUTEX_PARENT);
+ /*
+ * now that p2 is locked, nobody can move in or out of it,
+ * so the test below is safe.
+ */
+ if (likely(c1->d_parent == p2))
+ return NULL;
+
+ /*
+ * c1 got moved out of p2 while we'd been taking locks;
+ * unlock and fall back to slow case.
+ */
+ inode_unlock(p2->d_inode);
+ }
+
+ mutex_lock(&c1->d_sb->s_vfs_rename_mutex);
+ /*
+ * nobody can move out of any directories on this fs.
+ */
+ if (likely(c1->d_parent != p2))
+ return lock_two_directories(c1->d_parent, p2);
+
+ /*
+ * c1 got moved into p2 while we were taking locks;
+ * we need p2 locked and ->s_vfs_rename_mutex unlocked,
+ * for consistency with lock_rename().
+ */
+ inode_lock_nested(p2->d_inode, I_MUTEX_PARENT);
+ mutex_unlock(&c1->d_sb->s_vfs_rename_mutex);
+ return NULL;
+}
+EXPORT_SYMBOL(lock_rename_child);
+
void unlock_rename(struct dentry *p1, struct dentry *p2)
{
inode_unlock(p1->d_inode);
@@ -3806,7 +3882,8 @@ static struct dentry *filename_create(int dfd, struct filename *name,
if (last.name[last.len] && !want_dir)
create_flags = 0;
inode_lock_nested(path->dentry->d_inode, I_MUTEX_PARENT);
- dentry = __lookup_hash(&last, path->dentry, reval_flag | create_flags);
+ dentry = lookup_one_qstr_excl(&last, path->dentry,
+ reval_flag | create_flags);
if (IS_ERR(dentry))
goto unlock;

@@ -4168,7 +4245,7 @@ int do_rmdir(int dfd, struct filename *name)
goto exit2;

inode_lock_nested(path.dentry->d_inode, I_MUTEX_PARENT);
- dentry = __lookup_hash(&last, path.dentry, lookup_flags);
+ dentry = lookup_one_qstr_excl(&last, path.dentry, lookup_flags);
error = PTR_ERR(dentry);
if (IS_ERR(dentry))
goto exit3;
@@ -4302,7 +4379,7 @@ int do_unlinkat(int dfd, struct filename *name)
goto exit2;
retry_deleg:
inode_lock_nested(path.dentry->d_inode, I_MUTEX_PARENT);
- dentry = __lookup_hash(&last, path.dentry, lookup_flags);
+ dentry = lookup_one_qstr_excl(&last, path.dentry, lookup_flags);
error = PTR_ERR(dentry);
if (!IS_ERR(dentry)) {
struct user_namespace *mnt_userns;
@@ -4876,7 +4953,8 @@ int do_renameat2(int olddfd, struct filename *from, int newdfd,
retry_deleg:
trap = lock_rename(new_path.dentry, old_path.dentry);

- old_dentry = __lookup_hash(&old_last, old_path.dentry, lookup_flags);
+ old_dentry = lookup_one_qstr_excl(&old_last, old_path.dentry,
+ lookup_flags);
error = PTR_ERR(old_dentry);
if (IS_ERR(old_dentry))
goto exit3;
@@ -4884,7 +4962,8 @@ int do_renameat2(int olddfd, struct filename *from, int newdfd,
error = -ENOENT;
if (d_is_negative(old_dentry))
goto exit4;
- new_dentry = __lookup_hash(&new_last, new_path.dentry, lookup_flags | target_flags);
+ new_dentry = lookup_one_qstr_excl(&new_last, new_path.dentry,
+ lookup_flags | target_flags);
error = PTR_ERR(new_dentry);
if (IS_ERR(new_dentry))
goto exit4;
diff --git a/fs/nfsd/nfsctl.c b/fs/nfsd/nfsctl.c
index 573de0d49e17..b3b4542e31ed 100644
--- a/fs/nfsd/nfsctl.c
+++ b/fs/nfsd/nfsctl.c
@@ -716,8 +716,10 @@ static ssize_t __write_ports_addfd(char *buf, struct net *net, const struct cred

err = svc_addsock(nn->nfsd_serv, net, fd, buf, SIMPLE_TRANSACTION_LIMIT, cred);

- if (err >= 0 &&
- !nn->nfsd_serv->sv_nrthreads && !xchg(&nn->keep_active, 1))
+ if (err < 0 && !nn->nfsd_serv->sv_nrthreads && !nn->keep_active)
+ nfsd_last_thread(net);
+ else if (err >= 0 &&
+ !nn->nfsd_serv->sv_nrthreads && !xchg(&nn->keep_active, 1))
svc_get(nn->nfsd_serv);

nfsd_put(net);
@@ -767,6 +769,9 @@ static ssize_t __write_ports_addxprt(char *buf, struct net *net, const struct cr
svc_xprt_put(xprt);
}
out_err:
+ if (!nn->nfsd_serv->sv_nrthreads && !nn->keep_active)
+ nfsd_last_thread(net);
+
nfsd_put(net);
return err;
}
diff --git a/fs/nfsd/nfsd.h b/fs/nfsd/nfsd.h
index 09726c5b9a31..53166cce7062 100644
--- a/fs/nfsd/nfsd.h
+++ b/fs/nfsd/nfsd.h
@@ -97,7 +97,12 @@ int nfsd_pool_stats_open(struct inode *, struct file *);
int nfsd_pool_stats_release(struct inode *, struct file *);
void nfsd_shutdown_threads(struct net *net);

-void nfsd_put(struct net *net);
+static inline void nfsd_put(struct net *net)
+{
+ struct nfsd_net *nn = net_generic(net, nfsd_net_id);
+
+ svc_put(nn->nfsd_serv);
+}

bool i_am_nfsd(void);

@@ -134,6 +139,7 @@ int nfsd_vers(struct nfsd_net *nn, int vers, enum vers_op change);
int nfsd_minorversion(struct nfsd_net *nn, u32 minorversion, enum vers_op change);
void nfsd_reset_versions(struct nfsd_net *nn);
int nfsd_create_serv(struct net *net);
+void nfsd_last_thread(struct net *net);

extern int nfsd_max_blksize;

diff --git a/fs/nfsd/nfssvc.c b/fs/nfsd/nfssvc.c
index f6cc99af8192..350c6c72f793 100644
--- a/fs/nfsd/nfssvc.c
+++ b/fs/nfsd/nfssvc.c
@@ -523,9 +523,14 @@ static struct notifier_block nfsd_inet6addr_notifier = {
/* Only used under nfsd_mutex, so this atomic may be overkill: */
static atomic_t nfsd_notifier_refcount = ATOMIC_INIT(0);

-static void nfsd_last_thread(struct svc_serv *serv, struct net *net)
+void nfsd_last_thread(struct net *net)
{
struct nfsd_net *nn = net_generic(net, nfsd_net_id);
+ struct svc_serv *serv = nn->nfsd_serv;
+
+ spin_lock(&nfsd_notifier_lock);
+ nn->nfsd_serv = NULL;
+ spin_unlock(&nfsd_notifier_lock);

/* check if the notifier still has clients */
if (atomic_dec_return(&nfsd_notifier_refcount) == 0) {
@@ -535,6 +540,8 @@ static void nfsd_last_thread(struct svc_serv *serv, struct net *net)
#endif
}

+ svc_xprt_destroy_all(serv, net);
+
/*
* write_ports can create the server without actually starting
* any threads--if we get shut down before any threads are
@@ -625,7 +632,8 @@ void nfsd_shutdown_threads(struct net *net)
svc_get(serv);
/* Kill outstanding nfsd threads */
svc_set_num_threads(serv, NULL, 0);
- nfsd_put(net);
+ nfsd_last_thread(net);
+ svc_put(serv);
mutex_unlock(&nfsd_mutex);
}

@@ -655,9 +663,6 @@ int nfsd_create_serv(struct net *net)
serv->sv_maxconn = nn->max_connections;
error = svc_bind(serv, net);
if (error < 0) {
- /* NOT nfsd_put() as notifiers (see below) haven't
- * been set up yet.
- */
svc_put(serv);
return error;
}
@@ -700,29 +705,6 @@ int nfsd_get_nrthreads(int n, int *nthreads, struct net *net)
return 0;
}

-/* This is the callback for kref_put() below.
- * There is no code here as the first thing to be done is
- * call svc_shutdown_net(), but we cannot get the 'net' from
- * the kref. So do all the work when kref_put returns true.
- */
-static void nfsd_noop(struct kref *ref)
-{
-}
-
-void nfsd_put(struct net *net)
-{
- struct nfsd_net *nn = net_generic(net, nfsd_net_id);
-
- if (kref_put(&nn->nfsd_serv->sv_refcnt, nfsd_noop)) {
- svc_xprt_destroy_all(nn->nfsd_serv, net);
- nfsd_last_thread(nn->nfsd_serv, net);
- svc_destroy(&nn->nfsd_serv->sv_refcnt);
- spin_lock(&nfsd_notifier_lock);
- nn->nfsd_serv = NULL;
- spin_unlock(&nfsd_notifier_lock);
- }
-}
-
int nfsd_set_nrthreads(int n, int *nthreads, struct net *net)
{
int i = 0;
@@ -773,7 +755,7 @@ int nfsd_set_nrthreads(int n, int *nthreads, struct net *net)
if (err)
break;
}
- nfsd_put(net);
+ svc_put(nn->nfsd_serv);
return err;
}

@@ -788,6 +770,7 @@ nfsd_svc(int nrservs, struct net *net, const struct cred *cred)
int error;
bool nfsd_up_before;
struct nfsd_net *nn = net_generic(net, nfsd_net_id);
+ struct svc_serv *serv;

mutex_lock(&nfsd_mutex);
dprintk("nfsd: creating service\n");
@@ -807,22 +790,25 @@ nfsd_svc(int nrservs, struct net *net, const struct cred *cred)
goto out;

nfsd_up_before = nn->nfsd_net_up;
+ serv = nn->nfsd_serv;

error = nfsd_startup_net(net, cred);
if (error)
goto out_put;
- error = svc_set_num_threads(nn->nfsd_serv, NULL, nrservs);
+ error = svc_set_num_threads(serv, NULL, nrservs);
if (error)
goto out_shutdown;
- error = nn->nfsd_serv->sv_nrthreads;
+ error = serv->sv_nrthreads;
+ if (error == 0)
+ nfsd_last_thread(net);
out_shutdown:
if (error < 0 && !nfsd_up_before)
nfsd_shutdown_net(net);
out_put:
/* Threads now hold service active */
if (xchg(&nn->keep_active, 0))
- nfsd_put(net);
- nfsd_put(net);
+ svc_put(serv);
+ svc_put(serv);
out:
mutex_unlock(&nfsd_mutex);
return error;
@@ -1138,11 +1124,12 @@ int nfsd_pool_stats_open(struct inode *inode, struct file *file)

int nfsd_pool_stats_release(struct inode *inode, struct file *file)
{
+ struct seq_file *seq = file->private_data;
+ struct svc_serv *serv = seq->private;
int ret = seq_release(inode, file);
- struct net *net = inode->i_sb->s_fs_info;

mutex_lock(&nfsd_mutex);
- nfsd_put(net);
+ svc_put(serv);
mutex_unlock(&nfsd_mutex);
return ret;
}
diff --git a/fs/smb/common/smb2pdu.h b/fs/smb/common/smb2pdu.h
index c8a4014f9d39..07549957b309 100644
--- a/fs/smb/common/smb2pdu.h
+++ b/fs/smb/common/smb2pdu.h
@@ -1196,6 +1196,7 @@ struct create_posix {
#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_FLAG_PARENT_LEASE_KEY_SET_LE cpu_to_le32(0x04)

#define SMB2_LEASE_KEY_SIZE 16

diff --git a/fs/smb/server/Kconfig b/fs/smb/server/Kconfig
index e1fe17747ed6..d036ab80fec3 100644
--- a/fs/smb/server/Kconfig
+++ b/fs/smb/server/Kconfig
@@ -1,5 +1,5 @@
config SMB_SERVER
- tristate "SMB3 server support (EXPERIMENTAL)"
+ tristate "SMB3 server support"
depends on INET
depends on MULTIUSER
depends on FILE_LOCKING
@@ -33,14 +33,16 @@ config SMB_SERVER
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
+ available via the 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.
+ establishment via Kerberos or NTLMv2.
+
+if SMB_SERVER

config SMB_SERVER_SMBDIRECT
bool "Support for SMB Direct protocol"
@@ -54,6 +56,8 @@ config SMB_SERVER_SMBDIRECT
SMB Direct allows transferring SMB packets over RDMA. If unsure,
say N.

+endif
+
config SMB_SERVER_CHECK_CAP_NET_ADMIN
bool "Enable check network administration capability"
depends on SMB_SERVER
diff --git a/fs/smb/server/asn1.c b/fs/smb/server/asn1.c
index c03eba090368..4a4b2b03ff33 100644
--- a/fs/smb/server/asn1.c
+++ b/fs/smb/server/asn1.c
@@ -208,32 +208,29 @@ int ksmbd_neg_token_init_mech_type(void *context, size_t hdrlen,
return 0;
}

-int ksmbd_neg_token_init_mech_token(void *context, size_t hdrlen,
- unsigned char tag, const void *value,
- size_t vlen)
+static int ksmbd_neg_token_alloc(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);
+ conn->mechToken = kmemdup_nul(value, vlen, 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,
+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;
+ return ksmbd_neg_token_alloc(context, hdrlen, tag, value, vlen);
+}

- 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)
+{
+ return ksmbd_neg_token_alloc(context, hdrlen, tag, value, vlen);
}
diff --git a/fs/smb/server/auth.c b/fs/smb/server/auth.c
index 15e5684e328c..229a6527870d 100644
--- a/fs/smb/server/auth.c
+++ b/fs/smb/server/auth.c
@@ -1032,11 +1032,15 @@ static struct scatterlist *ksmbd_init_sg(struct kvec *iov, unsigned int nvec,
{
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;
+ int i, *nr_entries, total_entries = 0, sg_idx = 0;

if (!nvec)
return NULL;

+ nr_entries = kcalloc(nvec, sizeof(int), GFP_KERNEL);
+ if (!nr_entries)
+ return NULL;
+
for (i = 0; i < nvec - 1; i++) {
unsigned long kaddr = (unsigned long)iov[i + 1].iov_base;

@@ -1054,8 +1058,10 @@ static struct scatterlist *ksmbd_init_sg(struct kvec *iov, unsigned int nvec,
total_entries += 2;

sg = kmalloc_array(total_entries, sizeof(struct scatterlist), GFP_KERNEL);
- if (!sg)
+ if (!sg) {
+ kfree(nr_entries);
return NULL;
+ }

sg_init_table(sg, total_entries);
smb2_sg_set_buf(&sg[sg_idx++], iov[0].iov_base + 24, assoc_data_len);
@@ -1089,6 +1095,7 @@ static struct scatterlist *ksmbd_init_sg(struct kvec *iov, unsigned int nvec,
}
}
smb2_sg_set_buf(&sg[sg_idx], sign, SMB2_SIGNATURE_SIZE);
+ kfree(nr_entries);
return sg;
}

diff --git a/fs/smb/server/connection.c b/fs/smb/server/connection.c
index ff97cad8d5b4..b6fa1e285c40 100644
--- a/fs/smb/server/connection.c
+++ b/fs/smb/server/connection.c
@@ -114,10 +114,8 @@ 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) {
+ 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);
@@ -127,28 +125,22 @@ void ksmbd_conn_enqueue_request(struct ksmbd_work *work)
}
}

-int ksmbd_conn_try_dequeue_request(struct ksmbd_work *work)
+void 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;
+ return;

- if (!work->multiRsp)
- atomic_dec(&conn->req_running);
+ 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;
- }
+ list_del_init(&work->request_entry);
spin_unlock(&conn->request_lock);
+ if (work->asynchronous)
+ release_async_work(work);

wake_up_all(&conn->req_running_q);
- return ret;
}

void ksmbd_conn_lock(struct ksmbd_conn *conn)
@@ -175,63 +167,31 @@ void ksmbd_all_conn_set_status(u64 sess_id, u32 status)

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->send_no_response)
+ return 0;

- 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;
- }
+ if (!work->iov_idx)
+ return -EINVAL;

ksmbd_conn_lock(conn);
- sent = conn->transport->ops->writev(conn->transport, &iov[0],
- iov_idx, len,
- work->need_invalidate_rkey,
- work->remote_key);
+ sent = conn->transport->ops->writev(conn->transport, work->iov,
+ work->iov_cnt,
+ get_rfc1002_len(work->iov[0].iov_base) + 4,
+ work->need_invalidate_rkey,
+ work->remote_key);
ksmbd_conn_unlock(conn);

if (sent < 0) {
@@ -345,7 +305,7 @@ int ksmbd_conn_handler_loop(void *p)
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",
+ pr_err_ratelimited("PDU length(%u) exceeded maximum allowed pdu size(%u) on connection(%d)\n",
pdu_size, max_allowed_pdu_size,
READ_ONCE(conn->status));
break;
diff --git a/fs/smb/server/connection.h b/fs/smb/server/connection.h
index 335fdd714d59..3c005246a32e 100644
--- a/fs/smb/server/connection.h
+++ b/fs/smb/server/connection.h
@@ -159,7 +159,7 @@ int ksmbd_conn_rdma_write(struct ksmbd_conn *conn,
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_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);
diff --git a/fs/smb/server/ksmbd_netlink.h b/fs/smb/server/ksmbd_netlink.h
index ce866ff159bf..b7521e41402e 100644
--- a/fs/smb/server/ksmbd_netlink.h
+++ b/fs/smb/server/ksmbd_netlink.h
@@ -74,6 +74,7 @@ struct ksmbd_heartbeat {
#define KSMBD_GLOBAL_FLAG_SMB2_LEASES BIT(0)
#define KSMBD_GLOBAL_FLAG_SMB2_ENCRYPTION BIT(1)
#define KSMBD_GLOBAL_FLAG_SMB3_MULTICHANNEL BIT(2)
+#define KSMBD_GLOBAL_FLAG_SMB2_ENCRYPTION_OFF BIT(3)

/*
* IPC request for ksmbd server startup
@@ -351,7 +352,8 @@ enum KSMBD_TREE_CONN_STATUS {
#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)
+#define KSMBD_SHARE_FLAG_UPDATE BIT(14)
+#define KSMBD_SHARE_FLAG_CROSSMNT BIT(15)

/*
* Tree connect request flags.
diff --git a/fs/smb/server/ksmbd_work.c b/fs/smb/server/ksmbd_work.c
index 14b9caebf7a4..d7c676c151e2 100644
--- a/fs/smb/server/ksmbd_work.c
+++ b/fs/smb/server/ksmbd_work.c
@@ -27,18 +27,38 @@ struct ksmbd_work *ksmbd_alloc_work_struct(void)
INIT_LIST_HEAD(&work->async_request_entry);
INIT_LIST_HEAD(&work->fp_entry);
INIT_LIST_HEAD(&work->interim_entry);
+ INIT_LIST_HEAD(&work->aux_read_list);
+ work->iov_alloc_cnt = 4;
+ work->iov = kcalloc(work->iov_alloc_cnt, sizeof(struct kvec),
+ GFP_KERNEL);
+ if (!work->iov) {
+ kmem_cache_free(work_cache, work);
+ work = NULL;
+ }
}
return work;
}

void ksmbd_free_work_struct(struct ksmbd_work *work)
{
+ struct aux_read *ar, *tmp;
+
WARN_ON(work->saved_cred != NULL);

kvfree(work->response_buf);
- kvfree(work->aux_payload_buf);
+
+ list_for_each_entry_safe(ar, tmp, &work->aux_read_list, entry) {
+ kvfree(ar->buf);
+ list_del(&ar->entry);
+ kfree(ar);
+ }
+
kfree(work->tr_buf);
kvfree(work->request_buf);
+ kfree(work->iov);
+ if (!list_empty(&work->interim_entry))
+ list_del(&work->interim_entry);
+
if (work->async_id)
ksmbd_release_id(&work->conn->async_ida, work->async_id);
kmem_cache_free(work_cache, work);
@@ -77,3 +97,81 @@ bool ksmbd_queue_work(struct ksmbd_work *work)
{
return queue_work(ksmbd_wq, &work->work);
}
+
+static inline void __ksmbd_iov_pin(struct ksmbd_work *work, void *ib,
+ unsigned int ib_len)
+{
+ work->iov[++work->iov_idx].iov_base = ib;
+ work->iov[work->iov_idx].iov_len = ib_len;
+ work->iov_cnt++;
+}
+
+static int __ksmbd_iov_pin_rsp(struct ksmbd_work *work, void *ib, int len,
+ void *aux_buf, unsigned int aux_size)
+{
+ struct aux_read *ar = NULL;
+ int need_iov_cnt = 1;
+
+ if (aux_size) {
+ need_iov_cnt++;
+ ar = kmalloc(sizeof(struct aux_read), GFP_KERNEL);
+ if (!ar)
+ return -ENOMEM;
+ }
+
+ if (work->iov_alloc_cnt < work->iov_cnt + need_iov_cnt) {
+ struct kvec *new;
+
+ work->iov_alloc_cnt += 4;
+ new = krealloc(work->iov,
+ sizeof(struct kvec) * work->iov_alloc_cnt,
+ GFP_KERNEL | __GFP_ZERO);
+ if (!new) {
+ kfree(ar);
+ work->iov_alloc_cnt -= 4;
+ return -ENOMEM;
+ }
+ work->iov = new;
+ }
+
+ /* Plus rfc_length size on first iov */
+ if (!work->iov_idx) {
+ work->iov[work->iov_idx].iov_base = work->response_buf;
+ *(__be32 *)work->iov[0].iov_base = 0;
+ work->iov[work->iov_idx].iov_len = 4;
+ work->iov_cnt++;
+ }
+
+ __ksmbd_iov_pin(work, ib, len);
+ inc_rfc1001_len(work->iov[0].iov_base, len);
+
+ if (aux_size) {
+ __ksmbd_iov_pin(work, aux_buf, aux_size);
+ inc_rfc1001_len(work->iov[0].iov_base, aux_size);
+
+ ar->buf = aux_buf;
+ list_add(&ar->entry, &work->aux_read_list);
+ }
+
+ return 0;
+}
+
+int ksmbd_iov_pin_rsp(struct ksmbd_work *work, void *ib, int len)
+{
+ return __ksmbd_iov_pin_rsp(work, ib, len, NULL, 0);
+}
+
+int ksmbd_iov_pin_rsp_read(struct ksmbd_work *work, void *ib, int len,
+ void *aux_buf, unsigned int aux_size)
+{
+ return __ksmbd_iov_pin_rsp(work, ib, len, aux_buf, aux_size);
+}
+
+int allocate_interim_rsp_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;
+}
diff --git a/fs/smb/server/ksmbd_work.h b/fs/smb/server/ksmbd_work.h
index 5ece58e40c97..8ca2c813246e 100644
--- a/fs/smb/server/ksmbd_work.h
+++ b/fs/smb/server/ksmbd_work.h
@@ -19,6 +19,11 @@ enum {
KSMBD_WORK_CLOSED,
};

+struct aux_read {
+ void *buf;
+ struct list_head entry;
+};
+
/* one of these for every pending CIFS request at the connection */
struct ksmbd_work {
/* Server corresponding to this mid */
@@ -31,13 +36,19 @@ struct ksmbd_work {
/* Response buffer */
void *response_buf;

- /* Read data buffer */
- void *aux_payload_buf;
+ struct list_head aux_read_list;
+
+ struct kvec *iov;
+ int iov_alloc_cnt;
+ int iov_cnt;
+ int iov_idx;

/* 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 cmd hdr in compound rsp buf*/
+ int curr_smb2_rsp_hdr_off;

/*
* Current Local FID assigned compound response if SMB2 CREATE
@@ -53,22 +64,17 @@ struct ksmbd_work {
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 asynchronous:1;
bool need_invalidate_rkey:1;

unsigned int remote_key;
@@ -95,6 +101,15 @@ static inline void *ksmbd_resp_buf_next(struct ksmbd_work *work)
return work->response_buf + work->next_smb2_rsp_hdr_off + 4;
}

+/**
+ * ksmbd_resp_buf_curr - Get current buffer on compound response.
+ * @work: smb work containing response buffer
+ */
+static inline void *ksmbd_resp_buf_curr(struct ksmbd_work *work)
+{
+ return work->response_buf + work->curr_smb2_rsp_hdr_off + 4;
+}
+
/**
* ksmbd_req_buf_next - Get next buffer on compound request.
* @work: smb work containing response buffer
@@ -113,5 +128,8 @@ int ksmbd_work_pool_init(void);
int ksmbd_workqueue_init(void);
void ksmbd_workqueue_destroy(void);
bool ksmbd_queue_work(struct ksmbd_work *work);
-
+int ksmbd_iov_pin_rsp_read(struct ksmbd_work *work, void *ib, int len,
+ void *aux_buf, unsigned int aux_size);
+int ksmbd_iov_pin_rsp(struct ksmbd_work *work, void *ib, int len);
+int allocate_interim_rsp_buf(struct ksmbd_work *work);
#endif /* __KSMBD_WORK_H__ */
diff --git a/fs/smb/server/mgmt/share_config.h b/fs/smb/server/mgmt/share_config.h
index 3fd338293942..5f591751b923 100644
--- a/fs/smb/server/mgmt/share_config.h
+++ b/fs/smb/server/mgmt/share_config.h
@@ -34,29 +34,22 @@ struct ksmbd_share_config {
#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)
+static inline umode_t
+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;
+ umode_t mode = (posix_mode ?: (umode_t)-1) & share->create_mask;
+
+ return mode | share->force_create_mode;
}

-static inline int share_config_directory_mode(struct ksmbd_share_config *share,
- umode_t posix_mode)
+static inline umode_t
+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;
- }
+ umode_t mode = (posix_mode ?: (umode_t)-1) & share->directory_mask;

- return share->force_directory_mode & share->directory_mask;
+ return mode | share->force_directory_mode;
}

static inline int test_share_config_flag(struct ksmbd_share_config *share,
diff --git a/fs/smb/server/mgmt/tree_connect.c b/fs/smb/server/mgmt/tree_connect.c
index f07a05f37651..d2c81a8a11dd 100644
--- a/fs/smb/server/mgmt/tree_connect.c
+++ b/fs/smb/server/mgmt/tree_connect.c
@@ -73,7 +73,10 @@ ksmbd_tree_conn_connect(struct ksmbd_conn *conn, struct ksmbd_session *sess,

tree_conn->user = sess->user;
tree_conn->share_conf = sc;
+ tree_conn->t_state = TREE_NEW;
status.tree_conn = tree_conn;
+ atomic_set(&tree_conn->refcount, 1);
+ init_waitqueue_head(&tree_conn->refcount_q);

ret = xa_err(xa_store(&sess->tree_conns, tree_conn->id, tree_conn,
GFP_KERNEL));
@@ -93,14 +96,33 @@ ksmbd_tree_conn_connect(struct ksmbd_conn *conn, struct ksmbd_session *sess,
return status;
}

+void ksmbd_tree_connect_put(struct ksmbd_tree_connect *tcon)
+{
+ /*
+ * Checking waitqueue to releasing tree connect on
+ * tree disconnect. waitqueue_active is safe because it
+ * uses atomic operation for condition.
+ */
+ if (!atomic_dec_return(&tcon->refcount) &&
+ waitqueue_active(&tcon->refcount_q))
+ wake_up(&tcon->refcount_q);
+}
+
int ksmbd_tree_conn_disconnect(struct ksmbd_session *sess,
struct ksmbd_tree_connect *tree_conn)
{
int ret;

+ write_lock(&sess->tree_conns_lock);
+ xa_erase(&sess->tree_conns, tree_conn->id);
+ write_unlock(&sess->tree_conns_lock);
+
+ if (!atomic_dec_and_test(&tree_conn->refcount))
+ wait_event(tree_conn->refcount_q,
+ atomic_read(&tree_conn->refcount) == 0);
+
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;
@@ -111,26 +133,19 @@ struct ksmbd_tree_connect *ksmbd_tree_conn_lookup(struct ksmbd_session *sess,
{
struct ksmbd_tree_connect *tcon;

+ read_lock(&sess->tree_conns_lock);
tcon = xa_load(&sess->tree_conns, id);
if (tcon) {
- if (test_bit(TREE_CONN_EXPIRE, &tcon->status))
+ if (tcon->t_state != TREE_CONNECTED)
+ tcon = NULL;
+ else if (!atomic_inc_not_zero(&tcon->refcount))
tcon = NULL;
}
+ read_unlock(&sess->tree_conns_lock);

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;
@@ -140,8 +155,18 @@ int ksmbd_tree_conn_session_logoff(struct ksmbd_session *sess)
if (!sess)
return -EINVAL;

- xa_for_each(&sess->tree_conns, id, tc)
+ xa_for_each(&sess->tree_conns, id, tc) {
+ write_lock(&sess->tree_conns_lock);
+ if (tc->t_state == TREE_DISCONNECTED) {
+ write_unlock(&sess->tree_conns_lock);
+ ret = -ENOENT;
+ continue;
+ }
+ tc->t_state = TREE_DISCONNECTED;
+ write_unlock(&sess->tree_conns_lock);
+
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
index 700df36cf3e3..6377a70b811c 100644
--- a/fs/smb/server/mgmt/tree_connect.h
+++ b/fs/smb/server/mgmt/tree_connect.h
@@ -14,7 +14,11 @@ struct ksmbd_share_config;
struct ksmbd_user;
struct ksmbd_conn;

-#define TREE_CONN_EXPIRE 1
+enum {
+ TREE_NEW = 0,
+ TREE_CONNECTED,
+ TREE_DISCONNECTED
+};

struct ksmbd_tree_connect {
int id;
@@ -27,7 +31,9 @@ struct ksmbd_tree_connect {

int maximal_access;
bool posix_extensions;
- unsigned long status;
+ atomic_t refcount;
+ wait_queue_head_t refcount_q;
+ unsigned int t_state;
};

struct ksmbd_tree_conn_status {
@@ -46,6 +52,7 @@ struct ksmbd_session;
struct ksmbd_tree_conn_status
ksmbd_tree_conn_connect(struct ksmbd_conn *conn, struct ksmbd_session *sess,
const char *share_name);
+void ksmbd_tree_connect_put(struct ksmbd_tree_connect *tcon);

int ksmbd_tree_conn_disconnect(struct ksmbd_session *sess,
struct ksmbd_tree_connect *tree_conn);
@@ -53,9 +60,6 @@ int ksmbd_tree_conn_disconnect(struct ksmbd_session *sess,
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.h b/fs/smb/server/mgmt/user_config.h
index 6a44109617f1..e068a19fd904 100644
--- a/fs/smb/server/mgmt/user_config.h
+++ b/fs/smb/server/mgmt/user_config.h
@@ -18,7 +18,6 @@ struct ksmbd_user {

size_t passkey_sz;
char *passkey;
- unsigned int failed_login_count;
};

static inline bool user_guest(struct ksmbd_user *user)
diff --git a/fs/smb/server/mgmt/user_session.c b/fs/smb/server/mgmt/user_session.c
index cf6621e21ba3..15f68ee05089 100644
--- a/fs/smb/server/mgmt/user_session.c
+++ b/fs/smb/server/mgmt/user_session.c
@@ -25,7 +25,6 @@ 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)
@@ -58,15 +57,14 @@ static void __session_rpc_close(struct ksmbd_session *sess,
static void ksmbd_session_rpc_clear_list(struct ksmbd_session *sess)
{
struct ksmbd_session_rpc *entry;
+ long index;

- while (!list_empty(&sess->rpc_handle_list)) {
- entry = list_entry(sess->rpc_handle_list.next,
- struct ksmbd_session_rpc,
- list);
-
- list_del(&entry->list);
+ xa_for_each(&sess->rpc_handle_list, index, entry) {
+ xa_erase(&sess->rpc_handle_list, index);
__session_rpc_close(sess, entry);
}
+
+ xa_destroy(&sess->rpc_handle_list);
}

static int __rpc_method(char *rpc_name)
@@ -102,13 +100,13 @@ int ksmbd_session_rpc_open(struct ksmbd_session *sess, char *rpc_name)

entry = kzalloc(sizeof(struct ksmbd_session_rpc), GFP_KERNEL);
if (!entry)
- return -EINVAL;
+ return -ENOMEM;

- list_add(&entry->list, &sess->rpc_handle_list);
entry->method = method;
entry->id = ksmbd_ipc_id_alloc();
if (entry->id < 0)
goto free_entry;
+ xa_store(&sess->rpc_handle_list, entry->id, entry, GFP_KERNEL);

resp = ksmbd_rpc_open(sess, entry->id);
if (!resp)
@@ -117,9 +115,9 @@ int ksmbd_session_rpc_open(struct ksmbd_session *sess, char *rpc_name)
kvfree(resp);
return entry->id;
free_id:
+ xa_erase(&sess->rpc_handle_list, entry->id);
ksmbd_rpc_id_free(entry->id);
free_entry:
- list_del(&entry->list);
kfree(entry);
return -EINVAL;
}
@@ -128,24 +126,17 @@ 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;
- }
- }
+ entry = xa_erase(&sess->rpc_handle_list, id);
+ if (entry)
+ __session_rpc_close(sess, entry);
}

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;
+ entry = xa_load(&sess->rpc_handle_list, id);
+ return entry ? entry->method : 0;
}

void ksmbd_session_destroy(struct ksmbd_session *sess)
@@ -362,8 +353,9 @@ static struct ksmbd_session *__session_create(int protocol)
set_session_flag(sess, protocol);
xa_init(&sess->tree_conns);
xa_init(&sess->ksmbd_chann_list);
- INIT_LIST_HEAD(&sess->rpc_handle_list);
+ xa_init(&sess->rpc_handle_list);
sess->sequence_number = 1;
+ rwlock_init(&sess->tree_conns_lock);

ret = __init_smb2_session(sess);
if (ret)
diff --git a/fs/smb/server/mgmt/user_session.h b/fs/smb/server/mgmt/user_session.h
index 51f38e5b61ab..63cb08fffde8 100644
--- a/fs/smb/server/mgmt/user_session.h
+++ b/fs/smb/server/mgmt/user_session.h
@@ -52,7 +52,7 @@ struct ksmbd_session {
struct xarray ksmbd_chann_list;
struct xarray tree_conns;
struct ida tree_conn_ida;
- struct list_head rpc_handle_list;
+ struct xarray rpc_handle_list;

__u8 smb3encryptionkey[SMB3_ENC_DEC_KEY_SIZE];
__u8 smb3decryptionkey[SMB3_ENC_DEC_KEY_SIZE];
@@ -60,6 +60,7 @@ struct ksmbd_session {

struct ksmbd_file_table file_table;
unsigned long last_active;
+ rwlock_t tree_conns_lock;
};

static inline int test_session_flag(struct ksmbd_session *sess, int bit)
diff --git a/fs/smb/server/oplock.c b/fs/smb/server/oplock.c
index c81aee9ce7ec..af0f6914eca4 100644
--- a/fs/smb/server/oplock.c
+++ b/fs/smb/server/oplock.c
@@ -102,9 +102,10 @@ static int alloc_lease(struct oplock_info *opinfo, struct lease_ctx_info *lctx)
lease->new_state = 0;
lease->flags = lctx->flags;
lease->duration = lctx->duration;
+ lease->is_dir = lctx->is_dir;
memcpy(lease->parent_lease_key, lctx->parent_lease_key, SMB2_LEASE_KEY_SIZE);
lease->version = lctx->version;
- lease->epoch = 0;
+ lease->epoch = le16_to_cpu(lctx->epoch);
INIT_LIST_HEAD(&opinfo->lease_entry);
opinfo->o_lease = lease;

@@ -395,8 +396,8 @@ void close_id_del_oplock(struct ksmbd_file *fp)
{
struct oplock_info *opinfo;

- if (S_ISDIR(file_inode(fp->filp)->i_mode))
- return;
+ if (fp->reserve_lease_break)
+ smb_lazy_parent_lease_break_close(fp);

opinfo = opinfo_get(fp);
if (!opinfo)
@@ -543,12 +544,13 @@ static struct oplock_info *same_client_has_lease(struct ksmbd_inode *ci,
/* upgrading lease */
if ((atomic_read(&ci->op_count) +
atomic_read(&ci->sop_count)) == 1) {
- if (lease->state ==
- (lctx->req_state & lease->state)) {
+ if (lease->state != SMB2_LEASE_NONE_LE &&
+ 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) {
@@ -616,15 +618,6 @@ static int oplock_break_pending(struct oplock_info *opinfo, int req_op_level)
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
@@ -639,7 +632,6 @@ 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;
@@ -648,7 +640,7 @@ static void __smb2_oplock_break_noti(struct work_struct *wk)
if (!fp)
goto out;

- if (allocate_oplock_break_buf(work)) {
+ if (allocate_interim_rsp_buf(work)) {
pr_err("smb2_allocate_rsp_buf failed! ");
ksmbd_fd_put(work, fp);
goto out;
@@ -656,8 +648,6 @@ static void __smb2_oplock_break_noti(struct work_struct *wk)

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);
@@ -684,13 +674,15 @@ static void __smb2_oplock_break_noti(struct work_struct *wk)
rsp->PersistentFid = fp->persistent_id;
rsp->VolatileFid = fp->volatile_id;

- inc_rfc1001_len(work->response_buf, 24);
+ ksmbd_fd_put(work, fp);
+ if (ksmbd_iov_pin_rsp(work, (void *)rsp,
+ sizeof(struct smb2_oplock_break)))
+ goto out;

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:
@@ -751,18 +743,15 @@ 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)) {
+ if (allocate_interim_rsp_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);
@@ -791,7 +780,9 @@ static void __smb2_lease_break_noti(struct work_struct *wk)
rsp->AccessMaskHint = 0;
rsp->ShareMaskHint = 0;

- inc_rfc1001_len(work->response_buf, 44);
+ if (ksmbd_iov_pin_rsp(work, (void *)rsp,
+ sizeof(struct smb2_lease_break)))
+ goto out;

ksmbd_conn_write(work);

@@ -844,7 +835,8 @@ static int smb2_lease_break_noti(struct oplock_info *opinfo)
interim_entry);
setup_async_work(in_work, NULL, NULL);
smb2_send_interim_resp(in_work, STATUS_PENDING);
- list_del(&in_work->interim_entry);
+ list_del_init(&in_work->interim_entry);
+ release_async_work(in_work);
}
INIT_WORK(&work->work, __smb2_lease_break_noti);
ksmbd_queue_work(work);
@@ -910,7 +902,8 @@ static int oplock_break(struct oplock_info *brk_opinfo, int req_op_level)
lease->new_state =
SMB2_LEASE_READ_CACHING_LE;
} else {
- if (lease->state & SMB2_LEASE_HANDLE_CACHING_LE)
+ if (lease->state & SMB2_LEASE_HANDLE_CACHING_LE &&
+ !lease->is_dir)
lease->new_state =
SMB2_LEASE_READ_CACHING_LE;
else
@@ -1042,6 +1035,7 @@ static void copy_lease(struct oplock_info *op1, struct oplock_info *op2)
SMB2_LEASE_KEY_SIZE);
lease2->duration = lease1->duration;
lease2->flags = lease1->flags;
+ lease2->epoch = lease1->epoch++;
}

static int add_lease_global_list(struct oplock_info *opinfo)
@@ -1091,6 +1085,89 @@ static void set_oplock_level(struct oplock_info *opinfo, int level,
}
}

+void smb_send_parent_lease_break_noti(struct ksmbd_file *fp,
+ struct lease_ctx_info *lctx)
+{
+ struct oplock_info *opinfo;
+ struct ksmbd_inode *p_ci = NULL;
+
+ if (lctx->version != 2)
+ return;
+
+ p_ci = ksmbd_inode_lookup_lock(fp->filp->f_path.dentry->d_parent);
+ if (!p_ci)
+ return;
+
+ read_lock(&p_ci->m_lock);
+ list_for_each_entry(opinfo, &p_ci->m_op_list, op_entry) {
+ if (!opinfo->is_lease)
+ continue;
+
+ if (opinfo->o_lease->state != SMB2_OPLOCK_LEVEL_NONE &&
+ (!(lctx->flags & SMB2_LEASE_FLAG_PARENT_LEASE_KEY_SET_LE) ||
+ !compare_guid_key(opinfo, fp->conn->ClientGUID,
+ lctx->parent_lease_key))) {
+ if (!atomic_inc_not_zero(&opinfo->refcount))
+ continue;
+
+ atomic_inc(&opinfo->conn->r_count);
+ if (ksmbd_conn_releasing(opinfo->conn)) {
+ atomic_dec(&opinfo->conn->r_count);
+ continue;
+ }
+
+ read_unlock(&p_ci->m_lock);
+ oplock_break(opinfo, SMB2_OPLOCK_LEVEL_NONE);
+ opinfo_conn_put(opinfo);
+ read_lock(&p_ci->m_lock);
+ }
+ }
+ read_unlock(&p_ci->m_lock);
+
+ ksmbd_inode_put(p_ci);
+}
+
+void smb_lazy_parent_lease_break_close(struct ksmbd_file *fp)
+{
+ struct oplock_info *opinfo;
+ struct ksmbd_inode *p_ci = NULL;
+
+ rcu_read_lock();
+ opinfo = rcu_dereference(fp->f_opinfo);
+ rcu_read_unlock();
+
+ if (!opinfo->is_lease || opinfo->o_lease->version != 2)
+ return;
+
+ p_ci = ksmbd_inode_lookup_lock(fp->filp->f_path.dentry->d_parent);
+ if (!p_ci)
+ return;
+
+ read_lock(&p_ci->m_lock);
+ list_for_each_entry(opinfo, &p_ci->m_op_list, op_entry) {
+ if (!opinfo->is_lease)
+ continue;
+
+ if (opinfo->o_lease->state != SMB2_OPLOCK_LEVEL_NONE) {
+ if (!atomic_inc_not_zero(&opinfo->refcount))
+ continue;
+
+ atomic_inc(&opinfo->conn->r_count);
+ if (ksmbd_conn_releasing(opinfo->conn)) {
+ atomic_dec(&opinfo->conn->r_count);
+ continue;
+ }
+ read_unlock(&p_ci->m_lock);
+ oplock_break(opinfo, SMB2_OPLOCK_LEVEL_NONE);
+ opinfo_conn_put(opinfo);
+ read_lock(&p_ci->m_lock);
+ }
+ }
+ read_unlock(&p_ci->m_lock);
+
+ ksmbd_inode_put(p_ci);
+}
+
/**
* smb_grant_oplock() - handle oplock/lease request on file open
* @work: smb work
@@ -1114,10 +1191,6 @@ int smb_grant_oplock(struct ksmbd_work *work, int req_op_level, u64 pid,
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;
@@ -1374,6 +1447,7 @@ void create_lease_buf(u8 *rbuf, struct lease *lease)
memcpy(buf->lcontext.LeaseKey, lease->lease_key,
SMB2_LEASE_KEY_SIZE);
buf->lcontext.LeaseFlags = lease->flags;
+ buf->lcontext.Epoch = cpu_to_le16(++lease->epoch);
buf->lcontext.LeaseState = lease->state;
memcpy(buf->lcontext.ParentLeaseKey, lease->parent_lease_key,
SMB2_LEASE_KEY_SIZE);
@@ -1410,10 +1484,11 @@ void create_lease_buf(u8 *rbuf, struct lease *lease)
/**
* parse_lease_state() - parse lease context containted in file open request
* @open_req: buffer containing smb2 file open(create) request
+ * @is_dir: whether leasing file is directory
*
* Return: oplock state, -ENOENT if create lease context not found
*/
-struct lease_ctx_info *parse_lease_state(void *open_req)
+struct lease_ctx_info *parse_lease_state(void *open_req, bool is_dir)
{
struct create_context *cc;
struct smb2_create_req *req = (struct smb2_create_req *)open_req;
@@ -1431,8 +1506,14 @@ struct lease_ctx_info *parse_lease_state(void *open_req)
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;
+ if (is_dir) {
+ lreq->req_state = lc->lcontext.LeaseState &
+ ~SMB2_LEASE_WRITE_CACHING_LE;
+ lreq->is_dir = true;
+ } else
+ lreq->req_state = lc->lcontext.LeaseState;
lreq->flags = lc->lcontext.LeaseFlags;
+ lreq->epoch = lc->lcontext.Epoch;
lreq->duration = lc->lcontext.LeaseDuration;
memcpy(lreq->parent_lease_key, lc->lcontext.ParentLeaseKey,
SMB2_LEASE_KEY_SIZE);
diff --git a/fs/smb/server/oplock.h b/fs/smb/server/oplock.h
index 4b0fe6da7694..5b93ea9196c0 100644
--- a/fs/smb/server/oplock.h
+++ b/fs/smb/server/oplock.h
@@ -34,7 +34,9 @@ struct lease_ctx_info {
__le32 flags;
__le64 duration;
__u8 parent_lease_key[SMB2_LEASE_KEY_SIZE];
+ __le16 epoch;
int version;
+ bool is_dir;
};

struct lease_table {
@@ -53,6 +55,7 @@ struct lease {
__u8 parent_lease_key[SMB2_LEASE_KEY_SIZE];
int version;
unsigned short epoch;
+ bool is_dir;
struct lease_table *l_lb;
};

@@ -108,7 +111,7 @@ 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);
+struct lease_ctx_info *parse_lease_state(void *open_req, bool is_dir);
__u8 smb2_map_lease_to_oplock(__le32 lease_state);
int lease_read_to_write(struct oplock_info *opinfo);

@@ -124,4 +127,7 @@ struct oplock_info *lookup_lease_in_table(struct ksmbd_conn *conn,
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);
+void smb_send_parent_lease_break_noti(struct ksmbd_file *fp,
+ struct lease_ctx_info *lctx);
+void smb_lazy_parent_lease_break_close(struct ksmbd_file *fp);
#endif /* __KSMBD_OPLOCK_H */
diff --git a/fs/smb/server/server.c b/fs/smb/server/server.c
index 9804cabe72a8..11b201e6ee44 100644
--- a/fs/smb/server/server.c
+++ b/fs/smb/server/server.c
@@ -115,8 +115,10 @@ static int __process_request(struct ksmbd_work *work, struct ksmbd_conn *conn,
if (check_conn_state(work))
return SERVER_HANDLER_CONTINUE;

- if (ksmbd_verify_smb_message(work))
+ if (ksmbd_verify_smb_message(work)) {
+ conn->ops->set_rsp_status(work, STATUS_INVALID_PARAMETER);
return SERVER_HANDLER_ABORT;
+ }

command = conn->ops->get_cmd_val(work);
*cmd = command;
@@ -163,6 +165,7 @@ static void __handle_ksmbd_work(struct ksmbd_work *work,
{
u16 command = 0;
int rc;
+ bool is_chained = false;

if (conn->ops->allocate_rsp_buf(work))
return;
@@ -229,16 +232,17 @@ static void __handle_ksmbd_work(struct ksmbd_work *work,
}
}

+ is_chained = is_chained_smb2_message(work);
+
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;
+ } while (is_chained == true);

send:
+ if (work->tcon)
+ ksmbd_tree_connect_put(work->tcon);
smb3_preauth_hash_rsp(work);
if (work->sess && work->sess->enc && work->encrypted &&
conn->ops->encrypt_resp) {
@@ -442,11 +446,9 @@ static ssize_t stats_show(struct class *class, struct class_attribute *attr,
"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;
+ return sysfs_emit(buf, "%d %s %d %lu\n", stats_version,
+ state[server_conf.state], server_conf.tcp_port,
+ server_conf.ipc_last_active / HZ);
}

static ssize_t kill_server_store(struct class *class,
@@ -478,19 +480,13 @@ static ssize_t debug_show(struct class *class, struct class_attribute *attr,

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]);
+ pos = sysfs_emit_at(buf, sz, "[%s] ", debug_type_strings[i]);
} else {
- pos = scnprintf(buf + sz,
- PAGE_SIZE - sz,
- "%s ",
- debug_type_strings[i]);
+ pos = sysfs_emit_at(buf, sz, "%s ", debug_type_strings[i]);
}
sz += pos;
}
- sz += scnprintf(buf + sz, PAGE_SIZE - sz, "\n");
+ sz += sysfs_emit_at(buf, sz, "\n");
return sz;
}

@@ -599,8 +595,6 @@ static int __init ksmbd_server_init(void)
if (ret)
goto err_crypto_destroy;

- pr_warn_once("The ksmbd server is experimental\n");
-
return 0;

err_crypto_destroy:
diff --git a/fs/smb/server/smb2misc.c b/fs/smb/server/smb2misc.c
index e881df1d10cb..03dded29a980 100644
--- a/fs/smb/server/smb2misc.c
+++ b/fs/smb/server/smb2misc.c
@@ -106,16 +106,25 @@ static int smb2_get_data_area_len(unsigned int *off, unsigned int *len,
break;
case SMB2_CREATE:
{
+ unsigned short int name_off =
+ le16_to_cpu(((struct smb2_create_req *)hdr)->NameOffset);
+ unsigned short int name_len =
+ le16_to_cpu(((struct smb2_create_req *)hdr)->NameLength);
+
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;
+ if (!name_len)
+ break;
+
+ if (name_off + name_len < (u64)*off + *len)
+ break;
}

- *off = le16_to_cpu(((struct smb2_create_req *)hdr)->NameOffset);
- *len = le16_to_cpu(((struct smb2_create_req *)hdr)->NameLength);
+ *off = name_off;
+ *len = name_len;
break;
}
case SMB2_QUERY_INFO:
@@ -440,10 +449,8 @@ int ksmbd_smb2_check_message(struct ksmbd_work *work)

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);
+ smb2_validate_credit_charge(work->conn, hdr))
return 1;
- }

return 0;
}
diff --git a/fs/smb/server/smb2ops.c b/fs/smb/server/smb2ops.c
index ab23da2120b9..535402629655 100644
--- a/fs/smb/server/smb2ops.c
+++ b/fs/smb/server/smb2ops.c
@@ -221,7 +221,8 @@ void init_smb3_0_server(struct ksmbd_conn *conn)
conn->signing_algorithm = SIGNING_ALG_AES_CMAC_LE;

if (server_conf.flags & KSMBD_GLOBAL_FLAG_SMB2_LEASES)
- conn->vals->capabilities |= SMB2_GLOBAL_CAP_LEASING;
+ conn->vals->capabilities |= SMB2_GLOBAL_CAP_LEASING |
+ SMB2_GLOBAL_CAP_DIRECTORY_LEASING;

if (server_conf.flags & KSMBD_GLOBAL_FLAG_SMB2_ENCRYPTION &&
conn->cli_cap & SMB2_GLOBAL_CAP_ENCRYPTION)
@@ -245,10 +246,12 @@ void init_smb3_02_server(struct ksmbd_conn *conn)
conn->signing_algorithm = SIGNING_ALG_AES_CMAC_LE;

if (server_conf.flags & KSMBD_GLOBAL_FLAG_SMB2_LEASES)
- conn->vals->capabilities |= SMB2_GLOBAL_CAP_LEASING;
+ conn->vals->capabilities |= SMB2_GLOBAL_CAP_LEASING |
+ SMB2_GLOBAL_CAP_DIRECTORY_LEASING;

- if (server_conf.flags & KSMBD_GLOBAL_FLAG_SMB2_ENCRYPTION &&
- conn->cli_cap & SMB2_GLOBAL_CAP_ENCRYPTION)
+ if (server_conf.flags & KSMBD_GLOBAL_FLAG_SMB2_ENCRYPTION ||
+ (!(server_conf.flags & KSMBD_GLOBAL_FLAG_SMB2_ENCRYPTION_OFF) &&
+ conn->cli_cap & SMB2_GLOBAL_CAP_ENCRYPTION))
conn->vals->capabilities |= SMB2_GLOBAL_CAP_ENCRYPTION;

if (server_conf.flags & KSMBD_GLOBAL_FLAG_SMB3_MULTICHANNEL)
@@ -269,7 +272,13 @@ int init_smb3_11_server(struct ksmbd_conn *conn)
conn->signing_algorithm = SIGNING_ALG_AES_CMAC_LE;

if (server_conf.flags & KSMBD_GLOBAL_FLAG_SMB2_LEASES)
- conn->vals->capabilities |= SMB2_GLOBAL_CAP_LEASING;
+ conn->vals->capabilities |= SMB2_GLOBAL_CAP_LEASING |
+ SMB2_GLOBAL_CAP_DIRECTORY_LEASING;
+
+ if (server_conf.flags & KSMBD_GLOBAL_FLAG_SMB2_ENCRYPTION ||
+ (!(server_conf.flags & KSMBD_GLOBAL_FLAG_SMB2_ENCRYPTION_OFF) &&
+ 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;
diff --git a/fs/smb/server/smb2pdu.c b/fs/smb/server/smb2pdu.c
index 1598ad6155fe..ea48dd06d4da 100644
--- a/fs/smb/server/smb2pdu.c
+++ b/fs/smb/server/smb2pdu.c
@@ -144,12 +144,18 @@ void smb2_set_err_rsp(struct ksmbd_work *work)
err_rsp = smb2_get_msg(work->response_buf);

if (err_rsp->hdr.Status != STATUS_STOPPED_ON_SYMLINK) {
+ int err;
+
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);
+ err = ksmbd_iov_pin_rsp(work, (void *)err_rsp,
+ __SMB2_HEADER_STRUCTURE_SIZE +
+ SMB2_ERROR_STRUCTURE_SIZE2);
+ if (err)
+ work->send_no_response = 1;
}
}

@@ -224,11 +230,12 @@ 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 = smb2_get_msg(work->response_buf);
rsp_hdr->Status = err;
+
+ work->iov_idx = 0;
+ work->iov_cnt = 0;
+ work->next_smb2_rcv_hdr_off = 0;
smb2_set_err_rsp(work);
}

@@ -244,9 +251,7 @@ 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);
+ int err;

rsp_hdr = smb2_get_msg(work->response_buf);
memset(rsp_hdr, 0, sizeof(struct smb2_hdr) + 2);
@@ -285,13 +290,14 @@ int init_smb2_neg_rsp(struct ksmbd_work *work)
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;
+ err = ksmbd_iov_pin_rsp(work, rsp,
+ sizeof(struct smb2_negotiate_rsp) -
+ sizeof(rsp->Buffer) + AUTH_GSS_LENGTH);
+ if (err)
+ return err;
conn->use_spnego = true;

ksmbd_conn_set_need_negotiate(conn);
@@ -390,11 +396,12 @@ static void init_chained_smb2_rsp(struct ksmbd_work *work)
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);
+ work->iov[work->iov_idx].iov_len += (new_len - len);
+ inc_rfc1001_len(work->response_buf, new_len - len);
rsp->NextCommand = cpu_to_le32(new_len);

work->next_smb2_rcv_hdr_off += next_hdr_offset;
+ work->curr_smb2_rsp_hdr_off = work->next_smb2_rsp_hdr_off;
work->next_smb2_rsp_hdr_off += new_len;
ksmbd_debug(SMB,
"Compound req new_len = %d rcv off = %d rsp off = %d\n",
@@ -470,10 +477,10 @@ bool is_chained_smb2_message(struct ksmbd_work *work)
len = len - get_rfc1002_len(work->response_buf);
if (len) {
ksmbd_debug(SMB, "padding len %u\n", len);
+ work->iov[work->iov_idx].iov_len += len;
inc_rfc1001_len(work->response_buf, len);
- if (work->aux_payload_sz)
- work->aux_payload_sz += len;
}
+ work->curr_smb2_rsp_hdr_off = work->next_smb2_rsp_hdr_off;
}
return false;
}
@@ -488,11 +495,8 @@ 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;
@@ -508,12 +512,6 @@ int init_smb2_rsp_hdr(struct ksmbd_work *work)
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;
}

@@ -549,7 +547,7 @@ int smb2_allocate_rsp_buf(struct ksmbd_work *work)
if (le32_to_cpu(hdr->NextCommand) > 0)
sz = large_sz;

- work->response_buf = kvmalloc(sz, GFP_KERNEL | __GFP_ZERO);
+ work->response_buf = kvzalloc(sz, GFP_KERNEL);
if (!work->response_buf)
return -ENOMEM;

@@ -659,21 +657,16 @@ smb2_get_name(const char *src, const int maxlen, struct nls_table *local_nls)

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->asynchronous = true;
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",
@@ -691,18 +684,47 @@ int setup_async_work(struct ksmbd_work *work, void (*fn)(void **), void **arg)
return 0;
}

+void release_async_work(struct ksmbd_work *work)
+{
+ struct ksmbd_conn *conn = work->conn;
+
+ spin_lock(&conn->request_lock);
+ list_del_init(&work->async_request_entry);
+ spin_unlock(&conn->request_lock);
+
+ work->asynchronous = 0;
+ work->cancel_fn = NULL;
+ kfree(work->cancel_argv);
+ work->cancel_argv = NULL;
+ if (work->async_id) {
+ ksmbd_release_id(&conn->async_ida, work->async_id);
+ work->async_id = 0;
+ }
+}
+
void smb2_send_interim_resp(struct ksmbd_work *work, __le32 status)
{
struct smb2_hdr *rsp_hdr;
+ struct ksmbd_work *in_work = ksmbd_alloc_work_struct();

- rsp_hdr = smb2_get_msg(work->response_buf);
- smb2_set_err_rsp(work);
+ if (allocate_interim_rsp_buf(in_work)) {
+ pr_err("smb_allocate_rsp_buf failed!\n");
+ ksmbd_free_work_struct(in_work);
+ return;
+ }
+
+ in_work->conn = work->conn;
+ memcpy(smb2_get_msg(in_work->response_buf), ksmbd_resp_buf_next(work),
+ __SMB2_HEADER_STRUCTURE_SIZE);
+
+ rsp_hdr = smb2_get_msg(in_work->response_buf);
+ rsp_hdr->Flags |= SMB2_FLAGS_ASYNC_COMMAND;
+ rsp_hdr->Id.AsyncId = cpu_to_le64(work->async_id);
+ smb2_set_err_rsp(in_work);
rsp_hdr->Status = status;

- work->multiRsp = 1;
- ksmbd_conn_write(work);
- rsp_hdr->Status = 0;
- work->multiRsp = 0;
+ ksmbd_conn_write(in_work);
+ ksmbd_free_work_struct(in_work);
}

static __le32 smb2_get_reparse_tag_special_file(umode_t mode)
@@ -774,19 +796,6 @@ static void build_encrypt_ctxt(struct smb2_encryption_neg_context *pneg_ctxt,
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)
{
@@ -822,11 +831,10 @@ static void build_posix_ctxt(struct smb2_posix_neg_context *pneg_ctxt)
pneg_ctxt->Name[15] = 0x7C;
}

-static void assemble_neg_contexts(struct ksmbd_conn *conn,
- struct smb2_negotiate_rsp *rsp,
- void *smb2_buf_len)
+static unsigned int assemble_neg_contexts(struct ksmbd_conn *conn,
+ struct smb2_negotiate_rsp *rsp)
{
- char *pneg_ctxt = (char *)rsp +
+ char * const pneg_ctxt = (char *)rsp +
le32_to_cpu(rsp->NegotiateContextOffset);
int neg_ctxt_cnt = 1;
int ctxt_size;
@@ -835,62 +843,46 @@ static void assemble_neg_contexts(struct ksmbd_conn *conn,
"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) {
+ /* Round to 8 byte boundary */
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,
+ build_encrypt_ctxt((struct smb2_encryption_neg_context *)
+ (pneg_ctxt + ctxt_size),
conn->cipher_type);
- rsp->NegotiateContextCount = cpu_to_le16(++neg_ctxt_cnt);
+ 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);
- }
+ /* compression context not yet supported */
+ WARN_ON(conn->compress_algorithm != SMB3_COMPRESS_NONE);

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);
+ build_posix_ctxt((struct smb2_posix_neg_context *)
+ (pneg_ctxt + ctxt_size));
+ 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,
+ build_sign_cap_ctxt((struct smb2_signing_capabilities *)
+ (pneg_ctxt + ctxt_size),
conn->signing_algorithm);
- rsp->NegotiateContextCount = cpu_to_le16(++neg_ctxt_cnt);
+ neg_ctxt_cnt++;
ctxt_size += sizeof(struct smb2_signing_capabilities) + 2;
}

- inc_rfc1001_len(smb2_buf_len, ctxt_size);
+ rsp->NegotiateContextCount = cpu_to_le16(neg_ctxt_cnt);
+ return ctxt_size + AUTH_GSS_PADDING;
}

static __le32 decode_preauth_ctxt(struct ksmbd_conn *conn,
@@ -935,7 +927,7 @@ static void decode_encrypt_ctxt(struct ksmbd_conn *conn,
return;
}

- if (!(server_conf.flags & KSMBD_GLOBAL_FLAG_SMB2_ENCRYPTION))
+ if (server_conf.flags & KSMBD_GLOBAL_FLAG_SMB2_ENCRYPTION_OFF)
return;

for (i = 0; i < cph_cnt; i++) {
@@ -1106,7 +1098,7 @@ int smb2_handle_negotiate(struct ksmbd_work *work)
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;
+ unsigned int smb2_buf_len, smb2_neg_size, neg_ctxt_len = 0;
__le32 status;

ksmbd_debug(SMB, "Received negotiate request\n");
@@ -1199,7 +1191,7 @@ int smb2_handle_negotiate(struct ksmbd_work *work)
conn->preauth_info->Preauth_HashValue);
rsp->NegotiateContextOffset =
cpu_to_le32(OFFSET_OF_NEG_CONTEXT);
- assemble_neg_contexts(conn, rsp, work->response_buf);
+ neg_ctxt_len = assemble_neg_contexts(conn, rsp);
break;
case SMB302_PROT_ID:
init_smb3_02_server(conn);
@@ -1249,9 +1241,6 @@ int smb2_handle_negotiate(struct ksmbd_work *work)
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;

@@ -1269,9 +1258,16 @@ int smb2_handle_negotiate(struct ksmbd_work *work)
ksmbd_conn_set_need_negotiate(conn);

err_out:
+ if (rc)
+ rsp->hdr.Status = STATUS_INSUFFICIENT_RESOURCES;
+
+ if (!rc)
+ rc = ksmbd_iov_pin_rsp(work, rsp,
+ sizeof(struct smb2_negotiate_rsp) -
+ sizeof(rsp->Buffer) +
+ AUTH_GSS_LENGTH + neg_ctxt_len);
if (rc < 0)
smb2_set_err_rsp(work);
-
return rc;
}

@@ -1471,7 +1467,6 @@ static int ntlm_authenticate(struct ksmbd_work *work,
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);
@@ -1544,7 +1539,8 @@ static int ntlm_authenticate(struct ksmbd_work *work,
return -EINVAL;
}
sess->enc = true;
- rsp->SessionFlags = SMB2_SESSION_FLAG_ENCRYPT_DATA_LE;
+ if (server_conf.flags & KSMBD_GLOBAL_FLAG_SMB2_ENCRYPTION)
+ rsp->SessionFlags = SMB2_SESSION_FLAG_ENCRYPT_DATA_LE;
/*
* signing is disable if encryption is enable
* on this session
@@ -1616,7 +1612,6 @@ static int krb5_authenticate(struct ksmbd_work *work,
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))
@@ -1630,7 +1625,8 @@ static int krb5_authenticate(struct ksmbd_work *work,
return -EINVAL;
}
sess->enc = true;
- rsp->SessionFlags = SMB2_SESSION_FLAG_ENCRYPT_DATA_LE;
+ if (server_conf.flags & KSMBD_GLOBAL_FLAG_SMB2_ENCRYPTION)
+ rsp->SessionFlags = SMB2_SESSION_FLAG_ENCRYPT_DATA_LE;
sess->sign = false;
}

@@ -1687,7 +1683,6 @@ int smb2_sess_setup(struct ksmbd_work *work)
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) {
@@ -1823,13 +1818,6 @@ int smb2_sess_setup(struct ksmbd_work *work)
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, req, rsp);
if (rc)
@@ -1914,6 +1902,18 @@ int smb2_sess_setup(struct ksmbd_work *work)
ksmbd_conn_set_need_negotiate(conn);
}
}
+ smb2_set_err_rsp(work);
+ } else {
+ unsigned int iov_len;
+
+ if (rsp->SecurityBufferLength)
+ iov_len = offsetof(struct smb2_sess_setup_rsp, Buffer) +
+ le16_to_cpu(rsp->SecurityBufferLength);
+ else
+ iov_len = sizeof(struct smb2_sess_setup_rsp);
+ rc = ksmbd_iov_pin_rsp(work, rsp, iov_len);
+ if (rc)
+ rsp->hdr.Status = STATUS_INSUFFICIENT_RESOURCES;
}

ksmbd_conn_unlock(conn);
@@ -1991,14 +1991,20 @@ int smb2_tree_connect(struct ksmbd_work *work)
if (conn->posix_ext_supported)
status.tree_conn->posix_extensions = true;

+ write_lock(&sess->tree_conns_lock);
+ status.tree_conn->t_state = TREE_CONNECTED;
+ write_unlock(&sess->tree_conns_lock);
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;

+ rc = ksmbd_iov_pin_rsp(work, rsp, sizeof(struct smb2_tree_connect_rsp));
+ if (rc)
+ status.ret = KSMBD_TREE_CONN_STATUS_NOMEM;
+
if (!IS_ERR(treename))
kfree(treename);
if (!IS_ERR(name))
@@ -2111,26 +2117,56 @@ int smb2_tree_disconnect(struct ksmbd_work *work)
struct smb2_tree_disconnect_req *req;
struct ksmbd_session *sess = work->sess;
struct ksmbd_tree_connect *tcon = work->tcon;
+ int err;

WORK_BUFFERS(work, req, rsp);

- 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)) {
+ if (!tcon) {
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;
+ err = -ENOENT;
+ goto err_out;
}

ksmbd_close_tree_conn_fds(work);
- ksmbd_tree_conn_disconnect(sess, tcon);
+
+ write_lock(&sess->tree_conns_lock);
+ if (tcon->t_state == TREE_DISCONNECTED) {
+ write_unlock(&sess->tree_conns_lock);
+ rsp->hdr.Status = STATUS_NETWORK_NAME_DELETED;
+ err = -ENOENT;
+ goto err_out;
+ }
+
+ WARN_ON_ONCE(atomic_dec_and_test(&tcon->refcount));
+ tcon->t_state = TREE_DISCONNECTED;
+ write_unlock(&sess->tree_conns_lock);
+
+ err = ksmbd_tree_conn_disconnect(sess, tcon);
+ if (err) {
+ rsp->hdr.Status = STATUS_NETWORK_NAME_DELETED;
+ goto err_out;
+ }
+
work->tcon = NULL;
+
+ rsp->StructureSize = cpu_to_le16(4);
+ err = ksmbd_iov_pin_rsp(work, rsp,
+ sizeof(struct smb2_tree_disconnect_rsp));
+ if (err) {
+ rsp->hdr.Status = STATUS_INSUFFICIENT_RESOURCES;
+ goto err_out;
+ }
+
return 0;
+
+err_out:
+ smb2_set_err_rsp(work);
+ return err;
+
}

/**
@@ -2146,17 +2182,23 @@ int smb2_session_logoff(struct ksmbd_work *work)
struct smb2_logoff_rsp *rsp;
struct ksmbd_session *sess;
u64 sess_id;
+ int err;

WORK_BUFFERS(work, req, rsp);

- 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_conn_lock(conn);
+ if (!ksmbd_conn_good(conn)) {
+ ksmbd_conn_unlock(conn);
+ rsp->hdr.Status = STATUS_NETWORK_NAME_DELETED;
+ smb2_set_err_rsp(work);
+ return -ENOENT;
+ }
+ sess_id = le64_to_cpu(req->hdr.SessionId);
ksmbd_all_conn_set_status(sess_id, KSMBD_SESS_NEED_RECONNECT);
+ ksmbd_conn_unlock(conn);
+
ksmbd_close_session_fds(work);
ksmbd_conn_wait_idle(conn, sess_id);

@@ -2169,7 +2211,7 @@ int smb2_session_logoff(struct ksmbd_work *work)
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;
+ return -ENOENT;
}

ksmbd_destroy_file_table(&sess->file_table);
@@ -2178,6 +2220,14 @@ int smb2_session_logoff(struct ksmbd_work *work)
ksmbd_free_user(sess->user);
sess->user = NULL;
ksmbd_all_conn_set_status(sess_id, KSMBD_SESS_NEED_NEGOTIATE);
+
+ rsp->StructureSize = cpu_to_le16(4);
+ err = ksmbd_iov_pin_rsp(work, rsp, sizeof(struct smb2_logoff_rsp));
+ if (err) {
+ rsp->hdr.Status = STATUS_INSUFFICIENT_RESOURCES;
+ smb2_set_err_rsp(work);
+ return err;
+ }
return 0;
}

@@ -2230,7 +2280,10 @@ static noinline int create_smb2_pipe(struct ksmbd_work *work)
rsp->CreateContextsOffset = 0;
rsp->CreateContextsLength = 0;

- inc_rfc1001_len(work->response_buf, 88); /* StructureSize - 1*/
+ err = ksmbd_iov_pin_rsp(work, rsp, offsetof(struct smb2_create_rsp, Buffer));
+ if (err)
+ goto out;
+
kfree(name);
return 0;

@@ -2309,7 +2362,7 @@ static int smb2_set_ea(struct smb2_ea_info *eabuf, unsigned int buf_len,
/* delete the EA only when it exits */
if (rc > 0) {
rc = ksmbd_vfs_remove_xattr(user_ns,
- path->dentry,
+ path,
attr_name);

if (rc < 0) {
@@ -2323,9 +2376,9 @@ static int smb2_set_ea(struct smb2_ea_info *eabuf, unsigned int buf_len,
/* 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);
+ rc = ksmbd_vfs_setxattr(user_ns, path, attr_name, value,
+ le16_to_cpu(eabuf->EaValueLength),
+ 0, true);
if (rc < 0) {
ksmbd_debug(SMB,
"ksmbd_vfs_setxattr is failed(%d)\n",
@@ -2388,8 +2441,7 @@ static noinline int smb2_set_stream_name_xattr(const struct path *path,
return -EBADF;
}

- rc = ksmbd_vfs_setxattr(user_ns, path->dentry,
- xattr_stream_name, NULL, 0, 0);
+ rc = ksmbd_vfs_setxattr(user_ns, path, xattr_stream_name, NULL, 0, 0, false);
if (rc < 0)
pr_err("Failed to store XATTR stream name :%d\n", rc);
return 0;
@@ -2417,7 +2469,7 @@ static int smb2_remove_smb_xattrs(const struct path *path)
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,
+ err = ksmbd_vfs_remove_xattr(user_ns, path,
name);
if (err)
ksmbd_debug(SMB, "remove xattr failed : %s\n",
@@ -2464,8 +2516,7 @@ static void smb2_new_xattrs(struct ksmbd_tree_connect *tcon, const struct path *
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);
+ rc = ksmbd_vfs_set_dos_attrib_xattr(mnt_user_ns(path->mnt), path, &da, true);
if (rc)
ksmbd_debug(SMB, "failed to store file attribute into xattr\n");
}
@@ -2492,8 +2543,9 @@ static void smb2_update_xattrs(struct ksmbd_tree_connect *tcon,
}
}

-static int smb2_creat(struct ksmbd_work *work, struct path *path, char *name,
- int open_flags, umode_t posix_mode, bool is_dir)
+static int smb2_creat(struct ksmbd_work *work, struct path *parent_path,
+ 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;
@@ -2520,7 +2572,7 @@ static int smb2_creat(struct ksmbd_work *work, struct path *path, char *name,
return rc;
}

- rc = ksmbd_vfs_kern_path(work, name, 0, path, 0);
+ rc = ksmbd_vfs_kern_path_locked(work, name, 0, parent_path, path, 0);
if (rc) {
pr_err("cannot get linux path (%s), err = %d\n",
name, rc);
@@ -2554,7 +2606,7 @@ static int smb2_create_sd_buffer(struct ksmbd_work *work,
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);
+ le32_to_cpu(sd_buf->ccontext.DataLength), true, false);
}

static void ksmbd_acls_fattr(struct smb_fattr *fattr,
@@ -2590,7 +2642,7 @@ int smb2_open(struct ksmbd_work *work)
struct ksmbd_tree_connect *tcon = work->tcon;
struct smb2_create_req *req;
struct smb2_create_rsp *rsp;
- struct path path;
+ struct path path, parent_path;
struct ksmbd_share_config *share = tcon->share_conf;
struct ksmbd_file *fp = NULL;
struct file *filp = NULL;
@@ -2614,6 +2666,7 @@ int smb2_open(struct ksmbd_work *work)
u64 time;
umode_t posix_mode = 0;
__le32 daccess, maximal_access = 0;
+ int iov_len = 0;

WORK_BUFFERS(work, req, rsp);

@@ -2635,7 +2688,7 @@ int smb2_open(struct ksmbd_work *work)
*(char *)req->Buffer == '\\') {
pr_err("not allow directory name included leading slash\n");
rc = -EINVAL;
- goto err_out1;
+ goto err_out2;
}

name = smb2_get_name(req->Buffer,
@@ -2646,7 +2699,7 @@ int smb2_open(struct ksmbd_work *work)
if (rc != -ENOMEM)
rc = -ENOENT;
name = NULL;
- goto err_out1;
+ goto err_out2;
}

ksmbd_debug(SMB, "converted name = %s\n", name);
@@ -2654,48 +2707,44 @@ int smb2_open(struct ksmbd_work *work)
if (!test_share_config_flag(work->tcon->share_conf,
KSMBD_SHARE_FLAG_STREAMS)) {
rc = -EBADF;
- goto err_out1;
+ goto err_out2;
}
rc = parse_stream_name(name, &stream_name, &s_type);
if (rc < 0)
- goto err_out1;
+ goto err_out2;
}

rc = ksmbd_validate_filename(name);
if (rc < 0)
- goto err_out1;
+ goto err_out2;

if (ksmbd_share_veto_filename(share, name)) {
rc = -ENOENT;
ksmbd_debug(SMB, "Reject open(), vetoed file: %s\n",
name);
- goto err_out1;
+ goto err_out2;
}
} else {
name = kstrdup("", GFP_KERNEL);
if (!name) {
rc = -ENOMEM;
- goto err_out1;
+ goto err_out2;
}
}

- 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;
+ goto err_out2;
}

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;
+ goto err_out2;
} else {
if (req->CreateOptions & FILE_SEQUENTIAL_ONLY_LE &&
req->CreateOptions & FILE_RANDOM_ACCESS_LE)
@@ -2705,13 +2754,13 @@ int smb2_open(struct ksmbd_work *work)
(FILE_OPEN_BY_FILE_ID_LE | CREATE_TREE_CONNECTION |
FILE_RESERVE_OPFILTER_LE)) {
rc = -EOPNOTSUPP;
- goto err_out1;
+ goto err_out2;
}

if (req->CreateOptions & FILE_DIRECTORY_FILE_LE) {
if (req->CreateOptions & FILE_NON_DIRECTORY_FILE_LE) {
rc = -EINVAL;
- goto err_out1;
+ goto err_out2;
} else if (req->CreateOptions & FILE_NO_COMPRESSION_LE) {
req->CreateOptions = ~(FILE_NO_COMPRESSION_LE);
}
@@ -2723,21 +2772,21 @@ int smb2_open(struct ksmbd_work *work)
pr_err("Invalid create disposition : 0x%x\n",
le32_to_cpu(req->CreateDisposition));
rc = -EINVAL;
- goto err_out1;
+ goto err_out2;
}

if (!(req->DesiredAccess & DESIRED_ACCESS_MASK)) {
pr_err("Invalid desired access : 0x%x\n",
le32_to_cpu(req->DesiredAccess));
rc = -EACCES;
- goto err_out1;
+ goto err_out2;
}

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;
+ goto err_out2;
}

if (req->CreateContextsOffset) {
@@ -2745,19 +2794,19 @@ int smb2_open(struct ksmbd_work *work)
context = smb2_find_context_vals(req, SMB2_CREATE_EA_BUFFER, 4);
if (IS_ERR(context)) {
rc = PTR_ERR(context);
- goto err_out1;
+ goto err_out2;
} 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;
+ goto err_out2;
}
if (req->CreateOptions & FILE_NO_EA_KNOWLEDGE_LE) {
rsp->hdr.Status = STATUS_ACCESS_DENIED;
rc = -EACCES;
- goto err_out1;
+ goto err_out2;
}
}

@@ -2765,7 +2814,7 @@ int smb2_open(struct ksmbd_work *work)
SMB2_CREATE_QUERY_MAXIMAL_ACCESS_REQUEST, 4);
if (IS_ERR(context)) {
rc = PTR_ERR(context);
- goto err_out1;
+ goto err_out2;
} else if (context) {
ksmbd_debug(SMB,
"get query maximal access context\n");
@@ -2776,11 +2825,11 @@ int smb2_open(struct ksmbd_work *work)
SMB2_CREATE_TIMEWARP_REQUEST, 4);
if (IS_ERR(context)) {
rc = PTR_ERR(context);
- goto err_out1;
+ goto err_out2;
} else if (context) {
ksmbd_debug(SMB, "get timewarp context\n");
rc = -EBADF;
- goto err_out1;
+ goto err_out2;
}

if (tcon->posix_extensions) {
@@ -2788,7 +2837,7 @@ int smb2_open(struct ksmbd_work *work)
SMB2_CREATE_TAG_POSIX, 16);
if (IS_ERR(context)) {
rc = PTR_ERR(context);
- goto err_out1;
+ goto err_out2;
} else if (context) {
struct create_posix *posix =
(struct create_posix *)context;
@@ -2796,7 +2845,7 @@ int smb2_open(struct ksmbd_work *work)
le32_to_cpu(context->DataLength) <
sizeof(struct create_posix) - 4) {
rc = -EINVAL;
- goto err_out1;
+ goto err_out2;
}
ksmbd_debug(SMB, "get posix context\n");

@@ -2808,11 +2857,14 @@ int smb2_open(struct ksmbd_work *work)

if (ksmbd_override_fsids(work)) {
rc = -ENOMEM;
- goto err_out1;
+ goto err_out2;
}

- rc = ksmbd_vfs_kern_path(work, name, LOOKUP_NO_SYMLINKS, &path, 1);
+ rc = ksmbd_vfs_kern_path_locked(work, name, LOOKUP_NO_SYMLINKS,
+ &parent_path, &path, 1);
if (!rc) {
+ file_present = true;
+
if (req->CreateOptions & FILE_DELETE_ON_CLOSE_LE) {
/*
* If file exists with under flags, return access
@@ -2821,7 +2873,6 @@ int smb2_open(struct ksmbd_work *work)
if (req->CreateDisposition == FILE_OVERWRITE_IF_LE ||
req->CreateDisposition == FILE_OPEN_IF_LE) {
rc = -EACCES;
- path_put(&path);
goto err_out;
}

@@ -2829,26 +2880,23 @@ int smb2_open(struct ksmbd_work *work)
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) {
+ file_present = true;
+ user_ns = mnt_user_ns(path.mnt);
+ } else {
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) {
@@ -2910,11 +2958,9 @@ int smb2_open(struct ksmbd_work *work)
if (!file_present) {
daccess = cpu_to_le32(GENERIC_ALL_FLAGS);
} else {
- rc = ksmbd_vfs_query_maximal_access(user_ns,
+ ksmbd_vfs_query_maximal_access(user_ns,
path.dentry,
&daccess);
- if (rc)
- goto err_out;
already_permitted = true;
}
maximal_access = daccess;
@@ -2935,7 +2981,8 @@ int smb2_open(struct ksmbd_work *work)

/*create file if not present */
if (!file_present) {
- rc = smb2_creat(work, &path, name, open_flags, posix_mode,
+ rc = smb2_creat(work, &parent_path, &path, name, open_flags,
+ posix_mode,
req->CreateOptions & FILE_DIRECTORY_FILE_LE);
if (rc) {
if (rc == -ENOENT) {
@@ -2976,15 +3023,16 @@ int smb2_open(struct ksmbd_work *work)

if ((daccess & FILE_DELETE_LE) ||
(req->CreateOptions & FILE_DELETE_ON_CLOSE_LE)) {
- rc = ksmbd_vfs_may_delete(user_ns,
- path.dentry);
+ rc = inode_permission(user_ns,
+ d_inode(path.dentry->d_parent),
+ MAY_EXEC | MAY_WRITE);
if (rc)
goto err_out;
}
}
}

- rc = ksmbd_query_inode_status(d_inode(path.dentry->d_parent));
+ rc = ksmbd_query_inode_status(path.dentry->d_parent);
if (rc == KSMBD_INODE_STATUS_PENDING_DELETE) {
rc = -EBUSY;
goto err_out;
@@ -3040,7 +3088,7 @@ int smb2_open(struct ksmbd_work *work)
struct inode *inode = d_inode(path.dentry);

posix_acl_rc = ksmbd_vfs_inherit_posix_acl(user_ns,
- inode,
+ &path,
d_inode(path.dentry->d_parent));
if (posix_acl_rc)
ksmbd_debug(SMB, "inherit posix acl failed : %d\n", posix_acl_rc);
@@ -3056,7 +3104,7 @@ int smb2_open(struct ksmbd_work *work)
if (rc) {
if (posix_acl_rc)
ksmbd_vfs_set_init_posix_acl(user_ns,
- inode);
+ &path);

if (test_share_config_flag(work->tcon->share_conf,
KSMBD_SHARE_FLAG_ACL_XATTR)) {
@@ -3096,9 +3144,10 @@ int smb2_open(struct ksmbd_work *work)

rc = ksmbd_vfs_set_sd_xattr(conn,
user_ns,
- path.dentry,
+ &path,
pntsd,
- pntsd_size);
+ pntsd_size,
+ false);
kfree(pntsd);
if (rc)
pr_err("failed to store ntacl in xattr : %d\n",
@@ -3121,11 +3170,6 @@ int smb2_open(struct ksmbd_work *work)

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
@@ -3141,23 +3185,43 @@ int smb2_open(struct ksmbd_work *work)
goto err_out;
}

+ if (file_present || created)
+ ksmbd_vfs_kern_path_unlock(&parent_path, &path);
+
+ 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;
+ }
+
+ req_op_level = req->RequestedOplockLevel;
+ if (req_op_level == SMB2_OPLOCK_LEVEL_LEASE)
+ lc = parse_lease_state(req, S_ISDIR(file_inode(filp)->i_mode));
+
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;
+ goto err_out1;
}
} else {
if (req_op_level == SMB2_OPLOCK_LEVEL_LEASE) {
+ /*
+ * Compare parent lease using parent key. If there is no
+ * a lease that has same parent key, Send lease break
+ * notification.
+ */
+ smb_send_parent_lease_break_noti(fp, lc);
+
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;
+ goto err_out1;
} else if (open_flags == O_RDONLY &&
(req_op_level == SMB2_OPLOCK_LEVEL_BATCH ||
req_op_level == SMB2_OPLOCK_LEVEL_EXCLUSIVE))
@@ -3168,16 +3232,16 @@ int smb2_open(struct ksmbd_work *work)
le32_to_cpu(req->hdr.Id.SyncId.TreeId),
lc, share_ret);
if (rc < 0)
- goto err_out;
+ goto err_out1;
}

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);
+ rc = smb2_create_truncate(&fp->filp->f_path);
if (rc)
- goto err_out;
+ goto err_out1;
}

if (req->CreateContextsOffset) {
@@ -3187,7 +3251,7 @@ int smb2_open(struct ksmbd_work *work)
SMB2_CREATE_ALLOCATION_SIZE, 4);
if (IS_ERR(az_req)) {
rc = PTR_ERR(az_req);
- goto err_out;
+ goto err_out1;
} else if (az_req) {
loff_t alloc_size;
int err;
@@ -3196,7 +3260,7 @@ int smb2_open(struct ksmbd_work *work)
le32_to_cpu(az_req->ccontext.DataLength) <
sizeof(struct create_alloc_size_req)) {
rc = -EINVAL;
- goto err_out;
+ goto err_out1;
}
alloc_size = le64_to_cpu(az_req->AllocationSize);
ksmbd_debug(SMB,
@@ -3214,7 +3278,7 @@ int smb2_open(struct ksmbd_work *work)
context = smb2_find_context_vals(req, SMB2_CREATE_QUERY_ON_DISK_ID, 4);
if (IS_ERR(context)) {
rc = PTR_ERR(context);
- goto err_out;
+ goto err_out1;
} else if (context) {
ksmbd_debug(SMB, "get query on disk id context\n");
query_disk_id = 1;
@@ -3223,7 +3287,7 @@ int smb2_open(struct ksmbd_work *work)

rc = ksmbd_vfs_getattr(&path, &stat);
if (rc)
- goto err_out;
+ goto err_out1;

if (stat.result_mask & STATX_BTIME)
fp->create_time = ksmbd_UnixTimeToNT(stat.btime);
@@ -3266,7 +3330,7 @@ int smb2_open(struct ksmbd_work *work)

rsp->CreateContextsOffset = 0;
rsp->CreateContextsLength = 0;
- inc_rfc1001_len(work->response_buf, 88); /* StructureSize - 1*/
+ iov_len = offsetof(struct smb2_create_rsp, Buffer);

/* If lease is request send lease context response */
if (opinfo && opinfo->is_lease) {
@@ -3281,8 +3345,7 @@ int smb2_open(struct ksmbd_work *work)
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);
+ iov_len += conn->vals->create_lease_size;
next_ptr = &lease_ccontext->Next;
next_off = conn->vals->create_lease_size;
}
@@ -3302,8 +3365,7 @@ int smb2_open(struct ksmbd_work *work)
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);
+ iov_len += conn->vals->create_mxac_size;
if (next_ptr)
*next_ptr = cpu_to_le32(next_off);
next_ptr = &mxac_ccontext->Next;
@@ -3321,8 +3383,7 @@ int smb2_open(struct ksmbd_work *work)
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);
+ iov_len += conn->vals->create_disk_id_size;
if (next_ptr)
*next_ptr = cpu_to_le32(next_off);
next_ptr = &disk_id_ccontext->Next;
@@ -3336,8 +3397,7 @@ int smb2_open(struct ksmbd_work *work)
fp);
le32_add_cpu(&rsp->CreateContextsLength,
conn->vals->create_posix_size);
- inc_rfc1001_len(work->response_buf,
- conn->vals->create_posix_size);
+ iov_len += conn->vals->create_posix_size;
if (next_ptr)
*next_ptr = cpu_to_le32(next_off);
}
@@ -3348,10 +3408,17 @@ int smb2_open(struct ksmbd_work *work)
}

err_out:
- if (file_present || created)
- path_put(&path);
- ksmbd_revert_fsids(work);
+ if (rc && (file_present || created))
+ ksmbd_vfs_kern_path_unlock(&parent_path, &path);
+
err_out1:
+ ksmbd_revert_fsids(work);
+
+err_out2:
+ if (!rc) {
+ ksmbd_update_fstate(&work->sess->file_table, fp, FP_INITED);
+ rc = ksmbd_iov_pin_rsp(work, (void *)rsp, iov_len);
+ }
if (rc) {
if (rc == -EINVAL)
rsp->hdr.Status = STATUS_INVALID_PARAMETER;
@@ -3525,7 +3592,7 @@ static int smb2_populate_readdir_entry(struct ksmbd_conn *conn, int info_level,
goto free_conv_name;
}

- struct_sz = readdir_info_level_struct_sz(info_level) - 1 + conv_len;
+ struct_sz = readdir_info_level_struct_sz(info_level) + conv_len;
next_entry_offset = ALIGN(struct_sz, KSMBD_DIR_INFO_ALIGNMENT);
d_info->last_entry_off_align = next_entry_offset - struct_sz;

@@ -3777,7 +3844,7 @@ static int reserve_populate_dentry(struct ksmbd_dir_info *d_info,
return -EOPNOTSUPP;

conv_len = (d_info->name_len + 1) * 2;
- next_entry_offset = ALIGN(struct_sz - 1 + conv_len,
+ next_entry_offset = ALIGN(struct_sz + conv_len,
KSMBD_DIR_INFO_ALIGNMENT);

if (next_entry_offset > d_info->out_buf_len) {
@@ -4077,7 +4144,10 @@ int smb2_query_dir(struct ksmbd_work *work)
rsp->OutputBufferOffset = cpu_to_le16(0);
rsp->OutputBufferLength = cpu_to_le32(0);
rsp->Buffer[0] = 0;
- inc_rfc1001_len(work->response_buf, 9);
+ rc = ksmbd_iov_pin_rsp(work, (void *)rsp,
+ sizeof(struct smb2_query_directory_rsp));
+ if (rc)
+ goto err_out;
} else {
no_buf_len:
((struct file_directory_info *)
@@ -4089,7 +4159,11 @@ int smb2_query_dir(struct ksmbd_work *work)
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);
+ rc = ksmbd_iov_pin_rsp(work, (void *)rsp,
+ offsetof(struct smb2_query_directory_rsp, Buffer) +
+ d_info.data_count);
+ if (rc)
+ goto err_out;
}

kfree(srch_ptr);
@@ -4130,27 +4204,18 @@ int smb2_query_dir(struct ksmbd_work *work)
* @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)
+ void *rsp_org)
{
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);
+ 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;
}
return 0;
}
@@ -4169,7 +4234,6 @@ static void get_standard_info_pipe(struct smb2_query_info_rsp *rsp,
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,
@@ -4183,7 +4247,6 @@ static void get_internal_info_pipe(struct smb2_query_info_rsp *rsp, u64 num,
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,
@@ -4209,14 +4272,12 @@ static int smb2_get_info_file_pipe(struct ksmbd_session *sess,
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);
+ rsp, rsp_org);
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);
+ rsp, rsp_org);
break;
default:
ksmbd_debug(SMB, "smb2_info_file_pipe for %u not supported\n",
@@ -4384,7 +4445,6 @@ static int smb2_get_ea(struct ksmbd_work *work, struct ksmbd_file *fp,
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;
@@ -4399,7 +4459,6 @@ static void get_file_access_info(struct smb2_query_info_rsp *rsp,
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,
@@ -4429,7 +4488,6 @@ static int get_file_basic_info(struct smb2_query_info_rsp *rsp,
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;
}

@@ -4454,8 +4512,6 @@ static void get_file_standard_info(struct smb2_query_info_rsp *rsp,
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,
@@ -4467,8 +4523,6 @@ static void get_file_alignment_info(struct smb2_query_info_rsp *rsp,
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,
@@ -4532,7 +4586,6 @@ static int get_file_all_info(struct ksmbd_work *work,
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;
}

@@ -4555,7 +4608,6 @@ static void get_file_alternate_info(struct ksmbd_work *work,
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,
@@ -4655,7 +4707,6 @@ static void get_file_stream_info(struct ksmbd_work *work,
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,
@@ -4670,7 +4721,6 @@ static void get_file_internal_info(struct smb2_query_info_rsp *rsp,
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,
@@ -4706,7 +4756,6 @@ static int get_file_network_open_info(struct smb2_query_info_rsp *rsp,
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;
}

@@ -4718,7 +4767,6 @@ static void get_file_ea_info(struct smb2_query_info_rsp *rsp, void *rsp_org)
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,
@@ -4730,7 +4778,6 @@ static void get_file_position_info(struct smb2_query_info_rsp *rsp,
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,
@@ -4742,7 +4789,6 @@ static void get_file_mode_info(struct smb2_query_info_rsp *rsp,
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,
@@ -4764,7 +4810,6 @@ static void get_file_compression_info(struct smb2_query_info_rsp *rsp,

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,
@@ -4783,11 +4828,10 @@ static int get_file_attribute_tag_info(struct smb2_query_info_rsp *rsp,
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,
+static void find_file_posix_info(struct smb2_query_info_rsp *rsp,
struct ksmbd_file *fp, void *rsp_org)
{
struct smb311_posix_qinfo *file_info;
@@ -4825,8 +4869,6 @@ static int find_file_posix_info(struct smb2_query_info_rsp *rsp,
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,
@@ -4836,7 +4878,6 @@ static int smb2_get_info_file(struct ksmbd_work *work,
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,
@@ -4869,85 +4910,69 @@ static int smb2_get_info_file(struct ksmbd_work *work,
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);
+ find_file_posix_info(rsp, fp, work->response_buf);
}
break;
default:
@@ -4957,8 +4982,7 @@ static int smb2_get_info_file(struct ksmbd_work *work,
}
if (!rc)
rc = buffer_check_err(le32_to_cpu(req->OutputBufferLength),
- rsp, work->response_buf,
- file_infoclass_size);
+ rsp, work->response_buf);
ksmbd_fd_put(work, fp);
return rc;
}
@@ -4974,7 +4998,6 @@ static int smb2_get_info_filesystem(struct ksmbd_work *work,
struct kstatfs stfs;
struct path path;
int rc = 0, len;
- int fs_infoclass_size = 0;

if (!share->path)
return -EIO;
@@ -5004,8 +5027,6 @@ static int smb2_get_info_filesystem(struct ksmbd_work *work,
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:
@@ -5034,8 +5055,6 @@ static int smb2_get_info_filesystem(struct ksmbd_work *work,
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:
@@ -5062,8 +5081,6 @@ static int smb2_get_info_filesystem(struct ksmbd_work *work,
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:
@@ -5076,8 +5093,6 @@ static int smb2_get_info_filesystem(struct ksmbd_work *work,
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:
@@ -5093,8 +5108,6 @@ static int smb2_get_info_filesystem(struct ksmbd_work *work,
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:
@@ -5114,8 +5127,6 @@ static int smb2_get_info_filesystem(struct ksmbd_work *work,
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:
@@ -5137,8 +5148,6 @@ static int smb2_get_info_filesystem(struct ksmbd_work *work,
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:
@@ -5159,8 +5168,6 @@ static int smb2_get_info_filesystem(struct ksmbd_work *work,
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:
@@ -5180,8 +5187,6 @@ static int smb2_get_info_filesystem(struct ksmbd_work *work,
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;
}
@@ -5190,8 +5195,7 @@ static int smb2_get_info_filesystem(struct ksmbd_work *work,
return -EOPNOTSUPP;
}
rc = buffer_check_err(le32_to_cpu(req->OutputBufferLength),
- rsp, work->response_buf,
- fs_infoclass_size);
+ rsp, work->response_buf);
path_put(&path);
return rc;
}
@@ -5225,7 +5229,6 @@ static int smb2_get_info_sec(struct ksmbd_work *work,

secdesclen = sizeof(struct smb_ntsd);
rsp->OutputBufferLength = cpu_to_le32(secdesclen);
- inc_rfc1001_len(work->response_buf, secdesclen);

return 0;
}
@@ -5270,7 +5273,6 @@ static int smb2_get_info_sec(struct ksmbd_work *work,
return rc;

rsp->OutputBufferLength = cpu_to_le32(secdesclen);
- inc_rfc1001_len(work->response_buf, secdesclen);
return 0;
}

@@ -5309,6 +5311,14 @@ int smb2_query_info(struct ksmbd_work *work)
rc = -EOPNOTSUPP;
}

+ if (!rc) {
+ rsp->StructureSize = cpu_to_le16(9);
+ rsp->OutputBufferOffset = cpu_to_le16(72);
+ rc = ksmbd_iov_pin_rsp(work, (void *)rsp,
+ offsetof(struct smb2_query_info_rsp, Buffer) +
+ le32_to_cpu(rsp->OutputBufferLength));
+ }
+
if (rc < 0) {
if (rc == -EACCES)
rsp->hdr.Status = STATUS_ACCESS_DENIED;
@@ -5316,6 +5326,8 @@ int smb2_query_info(struct ksmbd_work *work)
rsp->hdr.Status = STATUS_FILE_CLOSED;
else if (rc == -EIO)
rsp->hdr.Status = STATUS_UNEXPECTED_IO_ERROR;
+ else if (rc == -ENOMEM)
+ rsp->hdr.Status = STATUS_INSUFFICIENT_RESOURCES;
else if (rc == -EOPNOTSUPP || rsp->hdr.Status == 0)
rsp->hdr.Status = STATUS_INVALID_INFO_CLASS;
smb2_set_err_rsp(work);
@@ -5324,9 +5336,6 @@ int smb2_query_info(struct ksmbd_work *work)
rc);
return rc;
}
- rsp->StructureSize = cpu_to_le16(9);
- rsp->OutputBufferOffset = cpu_to_le16(72);
- inc_rfc1001_len(work->response_buf, 8);
return 0;
}

@@ -5357,8 +5366,9 @@ static noinline int smb2_close_pipe(struct ksmbd_work *work)
rsp->AllocationSize = 0;
rsp->EndOfFile = 0;
rsp->Attributes = 0;
- inc_rfc1001_len(work->response_buf, 60);
- return 0;
+
+ return ksmbd_iov_pin_rsp(work, (void *)rsp,
+ sizeof(struct smb2_close_rsp));
}

/**
@@ -5463,15 +5473,17 @@ int smb2_close(struct ksmbd_work *work)

err = ksmbd_close_fd(work, volatile_id);
out:
+ if (!err)
+ err = ksmbd_iov_pin_rsp(work, (void *)rsp,
+ sizeof(struct smb2_close_rsp));
+
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;
+ return err;
}

/**
@@ -5489,50 +5501,24 @@ int smb2_echo(struct ksmbd_work *work)

rsp->StructureSize = cpu_to_le16(4);
rsp->Reserved = 0;
- inc_rfc1001_len(work->response_buf, 4);
- return 0;
+ return ksmbd_iov_pin_rsp(work, rsp, sizeof(struct smb2_echo_rsp));
}

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;
+ char *new_name = NULL;
+ int rc, flags = 0;

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 (IS_ERR(new_name))
+ return PTR_ERR(new_name);

if (strchr(new_name, ':')) {
int s_type;
@@ -5558,10 +5544,10 @@ static int smb2_rename(struct ksmbd_work *work,
if (rc)
goto out;

- rc = ksmbd_vfs_setxattr(user_ns,
- fp->filp->f_path.dentry,
+ rc = ksmbd_vfs_setxattr(file_mnt_user_ns(fp->filp),
+ &fp->filp->f_path,
xattr_stream_name,
- NULL, 0, 0);
+ NULL, 0, 0, true);
if (rc < 0) {
pr_err("failed to store stream name in xattr: %d\n",
rc);
@@ -5573,47 +5559,18 @@ static int smb2_rename(struct ksmbd_work *work,
}

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;
- }
- }
+ if (!file_info->ReplaceIfExists)
+ flags = RENAME_NOREPLACE;

- rc = ksmbd_vfs_fp_rename(work, fp, new_name);
+ rc = ksmbd_vfs_rename(work, &fp->filp->f_path, new_name, flags);
out:
- kfree(pathname);
- if (!IS_ERR(new_name))
- kfree(new_name);
+ kfree(new_name);
return rc;
}

@@ -5624,8 +5581,8 @@ static int smb2_create_link(struct ksmbd_work *work,
struct nls_table *local_nls)
{
char *link_name = NULL, *target_name = NULL, *pathname = NULL;
- struct path path;
- bool file_present = true;
+ struct path path, parent_path;
+ bool file_present = false;
int rc;

if (buf_len < (u64)sizeof(struct smb2_file_link_info) +
@@ -5653,18 +5610,17 @@ static int smb2_create_link(struct ksmbd_work *work,
}

ksmbd_debug(SMB, "target name is %s\n", target_name);
- rc = ksmbd_vfs_kern_path(work, link_name, LOOKUP_NO_SYMLINKS, &path, 0);
+ rc = ksmbd_vfs_kern_path_locked(work, link_name, LOOKUP_NO_SYMLINKS,
+ &parent_path, &path, 0);
if (rc) {
if (rc != -ENOENT)
goto out;
- file_present = false;
- } else {
- path_put(&path);
- }
+ } else
+ file_present = true;

if (file_info->ReplaceIfExists) {
if (file_present) {
- rc = ksmbd_vfs_remove_file(work, link_name);
+ rc = ksmbd_vfs_remove_file(work, &path);
if (rc) {
rc = -EINVAL;
ksmbd_debug(SMB, "cannot delete %s\n",
@@ -5684,6 +5640,9 @@ static int smb2_create_link(struct ksmbd_work *work,
if (rc)
rc = -EINVAL;
out:
+ if (file_present)
+ ksmbd_vfs_kern_path_unlock(&parent_path, &path);
+
if (!IS_ERR(link_name))
kfree(link_name);
kfree(pathname);
@@ -5750,8 +5709,8 @@ static int set_file_basic_info(struct ksmbd_file *fp,
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);
+ rc = ksmbd_vfs_set_dos_attrib_xattr(user_ns, &filp->f_path, &da,
+ true);
if (rc)
ksmbd_debug(SMB,
"failed to restore file attribute in EA\n");
@@ -5861,12 +5820,6 @@ 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;
@@ -5876,32 +5829,10 @@ static int set_rename_info(struct ksmbd_work *work, struct ksmbd_file *fp,
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 (!le32_to_cpu(rename_info->FileNameLength))
+ return -EINVAL;

- 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);
+ return smb2_rename(work, fp, rename_info, work->conn->local_nls);
}

static int set_file_disposition_info(struct ksmbd_file *fp,
@@ -6091,7 +6022,7 @@ static int smb2_set_info_sec(struct ksmbd_file *fp, int addition_info,
fp->saccess |= FILE_SHARE_DELETE_LE;

return set_info_sec(fp->conn, fp->tcon, &fp->filp->f_path, pntsd,
- buf_len, false);
+ buf_len, false, true);
}

/**
@@ -6161,7 +6092,10 @@ int smb2_set_info(struct ksmbd_work *work)
goto err_out;

rsp->StructureSize = cpu_to_le16(2);
- inc_rfc1001_len(work->response_buf, 2);
+ rc = ksmbd_iov_pin_rsp(work, (void *)rsp,
+ sizeof(struct smb2_set_info_rsp));
+ if (rc)
+ goto err_out;
ksmbd_fd_put(work, fp);
return 0;

@@ -6208,28 +6142,36 @@ static noinline int smb2_read_pipe(struct ksmbd_work *work)

id = req->VolatileFileId;

- inc_rfc1001_len(work->response_buf, 16);
rpc_resp = ksmbd_rpc_read(work->sess, id);
if (rpc_resp) {
+ void *aux_payload_buf;
+
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) {
+ aux_payload_buf =
+ kvmalloc(rpc_resp->payload_sz, GFP_KERNEL);
+ if (!aux_payload_buf) {
err = -ENOMEM;
goto out;
}

- memcpy(work->aux_payload_buf, rpc_resp->payload,
- rpc_resp->payload_sz);
+ memcpy(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;
+ err = ksmbd_iov_pin_rsp_read(work, (void *)rsp,
+ offsetof(struct smb2_read_rsp, Buffer),
+ aux_payload_buf, nbytes);
+ if (err)
+ goto out;
kvfree(rpc_resp);
+ } else {
+ err = ksmbd_iov_pin_rsp(work, (void *)rsp,
+ offsetof(struct smb2_read_rsp, Buffer));
+ if (err)
+ goto out;
}

rsp->StructureSize = cpu_to_le16(17);
@@ -6238,7 +6180,6 @@ static noinline int smb2_read_pipe(struct ksmbd_work *work)
rsp->DataLength = cpu_to_le32(nbytes);
rsp->DataRemaining = 0;
rsp->Flags = 0;
- inc_rfc1001_len(work->response_buf, nbytes);
return 0;

out:
@@ -6312,13 +6253,8 @@ int smb2_read(struct ksmbd_work *work)
int err = 0;
bool is_rdma_channel = false;
unsigned int max_read_size = conn->vals->max_read_size;
-
- WORK_BUFFERS(work, req, rsp);
- if (work->next_smb2_rcv_hdr_off) {
- work->send_no_response = 1;
- err = -EOPNOTSUPP;
- goto out;
- }
+ unsigned int id = KSMBD_NO_FID, pid = KSMBD_NO_FID;
+ void *aux_payload_buf;

if (test_share_config_flag(work->tcon->share_conf,
KSMBD_SHARE_FLAG_PIPE)) {
@@ -6326,6 +6262,25 @@ int smb2_read(struct ksmbd_work *work)
return smb2_read_pipe(work);
}

+ 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;
+ }
+
if (req->Channel == SMB2_CHANNEL_RDMA_V1_INVALIDATE ||
req->Channel == SMB2_CHANNEL_RDMA_V1) {
is_rdma_channel = true;
@@ -6348,7 +6303,7 @@ int smb2_read(struct ksmbd_work *work)
goto out;
}

- fp = ksmbd_lookup_fd_slow(work, req->VolatileFileId, req->PersistentFileId);
+ fp = ksmbd_lookup_fd_slow(work, id, pid);
if (!fp) {
err = -ENOENT;
goto out;
@@ -6374,21 +6329,20 @@ int smb2_read(struct ksmbd_work *work)
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) {
+ aux_payload_buf = kvzalloc(length, GFP_KERNEL);
+ if (!aux_payload_buf) {
err = -ENOMEM;
goto out;
}

- nbytes = ksmbd_vfs_read(work, fp, length, &offset);
+ nbytes = ksmbd_vfs_read(work, fp, length, &offset, aux_payload_buf);
if (nbytes < 0) {
err = nbytes;
goto out;
}

if ((nbytes == 0 && length != 0) || nbytes < mincount) {
- kvfree(work->aux_payload_buf);
- work->aux_payload_buf = NULL;
+ kvfree(aux_payload_buf);
rsp->hdr.Status = STATUS_END_OF_FILE;
smb2_set_err_rsp(work);
ksmbd_fd_put(work, fp);
@@ -6401,11 +6355,10 @@ int smb2_read(struct ksmbd_work *work)
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,
+ aux_payload_buf,
nbytes);
- kvfree(work->aux_payload_buf);
- work->aux_payload_buf = NULL;
-
+ kvfree(aux_payload_buf);
+ aux_payload_buf = NULL;
nbytes = 0;
if (remain_bytes < 0) {
err = (int)remain_bytes;
@@ -6419,10 +6372,11 @@ int smb2_read(struct ksmbd_work *work)
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);
+ err = ksmbd_iov_pin_rsp_read(work, (void *)rsp,
+ offsetof(struct smb2_read_rsp, Buffer),
+ aux_payload_buf, nbytes);
+ if (err)
+ goto out;
ksmbd_fd_put(work, fp);
return 0;

@@ -6505,8 +6459,8 @@ static noinline int smb2_write_pipe(struct ksmbd_work *work)
rsp->DataLength = cpu_to_le32(length);
rsp->DataRemaining = 0;
rsp->Reserved2 = 0;
- inc_rfc1001_len(work->response_buf, 16);
- return 0;
+ err = ksmbd_iov_pin_rsp(work, (void *)rsp,
+ offsetof(struct smb2_write_rsp, Buffer));
out:
if (err) {
rsp->hdr.Status = STATUS_INVALID_HANDLE;
@@ -6525,7 +6479,7 @@ static ssize_t smb2_write_rdma_channel(struct ksmbd_work *work,
int ret;
ssize_t nbytes;

- data_buf = kvmalloc(length, GFP_KERNEL | __GFP_ZERO);
+ data_buf = kvzalloc(length, GFP_KERNEL);
if (!data_buf)
return -ENOMEM;

@@ -6662,7 +6616,9 @@ int smb2_write(struct ksmbd_work *work)
rsp->DataLength = cpu_to_le32(nbytes);
rsp->DataRemaining = 0;
rsp->Reserved2 = 0;
- inc_rfc1001_len(work->response_buf, 16);
+ err = ksmbd_iov_pin_rsp(work, rsp, offsetof(struct smb2_write_rsp, Buffer));
+ if (err)
+ goto out;
ksmbd_fd_put(work, fp);
return 0;

@@ -6709,15 +6665,11 @@ int smb2_flush(struct ksmbd_work *work)

rsp->StructureSize = cpu_to_le16(4);
rsp->Reserved = 0;
- inc_rfc1001_len(work->response_buf, 4);
- return 0;
+ return ksmbd_iov_pin_rsp(work, rsp, sizeof(struct smb2_flush_rsp));

out:
- if (err) {
- rsp->hdr.Status = STATUS_INVALID_HANDLE;
- smb2_set_err_rsp(work);
- }
-
+ rsp->hdr.Status = STATUS_INVALID_HANDLE;
+ smb2_set_err_rsp(work);
return err;
}

@@ -6843,7 +6795,7 @@ static int smb2_set_flock_flags(struct file_lock *flock, int flags)
case SMB2_LOCKFLAG_UNLOCK:
ksmbd_debug(SMB, "received unlock request\n");
flock->fl_type = F_UNLCK;
- cmd = 0;
+ cmd = F_SETLK;
break;
}

@@ -6949,6 +6901,7 @@ int smb2_lock(struct ksmbd_work *work)
if (lock_start > U64_MAX - lock_length) {
pr_err("Invalid lock range requested\n");
rsp->hdr.Status = STATUS_INVALID_LOCK_RANGE;
+ locks_free_lock(flock);
goto out;
}

@@ -6968,6 +6921,7 @@ int smb2_lock(struct ksmbd_work *work)
"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;
+ locks_free_lock(flock);
goto out;
}

@@ -6979,6 +6933,7 @@ int smb2_lock(struct ksmbd_work *work)
flock->fl_type != F_UNLCK) {
pr_err("conflict two locks in one request\n");
err = -EINVAL;
+ locks_free_lock(flock);
goto out;
}
}
@@ -6987,6 +6942,7 @@ int smb2_lock(struct ksmbd_work *work)
smb_lock = smb2_lock_init(flock, cmd, flags, &lock_list);
if (!smb_lock) {
err = -EINVAL;
+ locks_free_lock(flock);
goto out;
}
}
@@ -7118,10 +7074,6 @@ int smb2_lock(struct ksmbd_work *work)

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);
@@ -7147,19 +7099,12 @@ int smb2_lock(struct ksmbd_work *work)

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) {
@@ -7171,8 +7116,7 @@ int smb2_lock(struct ksmbd_work *work)
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);
@@ -7180,19 +7124,16 @@ int smb2_lock(struct ksmbd_work *work)
}

list_del(&smb_lock->llist);
- spin_lock(&work->conn->llist_lock);
- list_del(&smb_lock->clist);
- spin_unlock(&work->conn->llist_lock);
-
+ release_async_work(work);
goto retry;
} else if (!rc) {
+ list_add(&smb_lock->llist, &rollback_list);
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;
@@ -7207,7 +7148,10 @@ int smb2_lock(struct ksmbd_work *work)
ksmbd_debug(SMB, "successful in taking lock\n");
rsp->hdr.Status = STATUS_SUCCESS;
rsp->Reserved = 0;
- inc_rfc1001_len(work->response_buf, 4);
+ err = ksmbd_iov_pin_rsp(work, rsp, sizeof(struct smb2_lock_rsp));
+ if (err)
+ goto out;
+
ksmbd_fd_put(work, fp);
return 0;

@@ -7226,7 +7170,7 @@ int smb2_lock(struct ksmbd_work *work)
rlock->fl_start = smb_lock->start;
rlock->fl_end = smb_lock->end;

- rc = vfs_lock_file(filp, 0, rlock, NULL);
+ rc = vfs_lock_file(filp, F_SETLK, rlock, NULL);
if (rc)
pr_err("rollback unlock fail : %d\n", rc);

@@ -7648,7 +7592,8 @@ static inline int fsctl_set_sparse(struct ksmbd_work *work, u64 id,

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);
+ &fp->filp->f_path,
+ &da, true);
if (ret)
fp->f_ci->m_fattr = old_fattr;
}
@@ -8003,9 +7948,9 @@ int smb2_ioctl(struct ksmbd_work *work)
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;
+ ret = ksmbd_iov_pin_rsp(work, rsp, sizeof(struct smb2_ioctl_rsp) + nbytes);
+ if (!ret)
+ return ret;

out:
if (ret == -EACCES)
@@ -8140,8 +8085,9 @@ static void smb20_oplock_break_ack(struct ksmbd_work *work)
rsp->Reserved2 = 0;
rsp->VolatileFid = volatile_id;
rsp->PersistentFid = persistent_id;
- inc_rfc1001_len(work->response_buf, 24);
- return;
+ ret = ksmbd_iov_pin_rsp(work, rsp, sizeof(struct smb2_oplock_break));
+ if (!ret)
+ return;

err_out:
opinfo->op_state = OPLOCK_STATE_NONE;
@@ -8273,6 +8219,11 @@ static void smb21_lease_break_ack(struct ksmbd_work *work)
le32_to_cpu(req->LeaseState));
}

+ if (ret < 0) {
+ rsp->hdr.Status = err;
+ goto err_out;
+ }
+
lease_state = lease->state;
opinfo->op_state = OPLOCK_STATE_NONE;
wake_up_interruptible_all(&opinfo->oplock_q);
@@ -8280,22 +8231,17 @@ static void smb21_lease_break_ack(struct ksmbd_work *work)
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;
+ ret = ksmbd_iov_pin_rsp(work, rsp, sizeof(struct smb2_lease_ack));
+ if (!ret)
+ 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);
@@ -8430,43 +8376,19 @@ int smb2_check_sign_req(struct ksmbd_work *work)
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;
+ struct kvec *iov;
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 = ksmbd_resp_buf_curr(work);
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;
+ if (hdr->Command == SMB2_READ) {
+ iov = &work->iov[work->iov_idx - 1];
n_vec++;
+ } else {
+ iov = &work->iov[work->iov_idx];
}

if (!ksmbd_sign_smb2_pdu(work->conn, work->sess->sess_key, iov, n_vec,
@@ -8542,29 +8464,14 @@ int smb3_check_sign_req(struct ksmbd_work *work)
void smb3_set_sign_rsp(struct ksmbd_work *work)
{
struct ksmbd_conn *conn = work->conn;
- struct smb2_hdr *req_hdr, *hdr;
+ struct smb2_hdr *hdr;
struct channel *chann;
char signature[SMB2_CMACAES_SIZE];
- struct kvec iov[2];
+ struct kvec *iov;
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);
- }
+ hdr = ksmbd_resp_buf_curr(work);

if (conn->binding == false &&
le16_to_cpu(hdr->Command) == SMB2_SESSION_SETUP_HE) {
@@ -8580,21 +8487,18 @@ void smb3_set_sign_rsp(struct ksmbd_work *work)
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;
+
+ if (hdr->Command == SMB2_READ) {
+ iov = &work->iov[work->iov_idx - 1];
n_vec++;
+ } else {
+ iov = &work->iov[work->iov_idx];
}

- if (!ksmbd_sign_smb3_pdu(conn, signing_key, iov, n_vec, signature))
+ if (!ksmbd_sign_smb3_pdu(conn, signing_key, iov, n_vec,
+ signature))
memcpy(hdr->Signature, signature, SMB2_SIGNATURE_SIZE);
}

@@ -8661,45 +8565,22 @@ static void fill_transform_hdr(void *tr_buf, char *old_buf, __le16 cipher_type)

int smb3_encrypt_resp(struct ksmbd_work *work)
{
- char *buf = work->response_buf;
- struct kvec iov[3];
+ struct kvec *iov = work->iov;
int rc = -ENOMEM;
- int buf_size = 0, rq_nvec = 2 + (work->aux_payload_sz ? 1 : 0);
-
- if (ARRAY_SIZE(iov) < rq_nvec)
- return -ENOMEM;
+ void *tr_buf;

- work->tr_buf = kzalloc(sizeof(struct smb2_transform_hdr) + 4, GFP_KERNEL);
- if (!work->tr_buf)
+ tr_buf = kzalloc(sizeof(struct smb2_transform_hdr) + 4, GFP_KERNEL);
+ if (!tr_buf)
return rc;

/* fill transform header */
- fill_transform_hdr(work->tr_buf, buf, work->conn->cipher_type);
+ fill_transform_hdr(tr_buf, work->response_buf, work->conn->cipher_type);

- iov[0].iov_base = work->tr_buf;
+ iov[0].iov_base = 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);
+ work->tr_buf = tr_buf;

- return rc;
+ return ksmbd_crypt_message(work, iov, work->iov_idx + 1, 1);
}

bool smb3_is_transform_hdr(void *buf)
diff --git a/fs/smb/server/smb2pdu.h b/fs/smb/server/smb2pdu.h
index 665a83737854..59e3de95961c 100644
--- a/fs/smb/server/smb2pdu.h
+++ b/fs/smb/server/smb2pdu.h
@@ -446,7 +446,7 @@ struct smb2_posix_info {
/* SidBuffer contain two sids (UNIX user sid(16), UNIX group sid(16)) */
u8 SidBuffer[32];
__le32 name_len;
- u8 name[1];
+ u8 name[];
/*
* var sized owner SID
* var sized group SID
@@ -488,6 +488,7 @@ int find_matching_smb2_dialect(int start_index, __le16 *cli_dialects,
struct file_lock *smb_flock_init(struct file *f);
int setup_async_work(struct ksmbd_work *work, void (*fn)(void **),
void **arg);
+void release_async_work(struct ksmbd_work *work);
void smb2_send_interim_resp(struct ksmbd_work *work, __le32 status);
struct channel *lookup_chann_list(struct ksmbd_session *sess,
struct ksmbd_conn *conn);
diff --git a/fs/smb/server/smb_common.c b/fs/smb/server/smb_common.c
index adc41b57b84c..d160363c09eb 100644
--- a/fs/smb/server/smb_common.c
+++ b/fs/smb/server/smb_common.c
@@ -266,7 +266,7 @@ static int ksmbd_negotiate_smb_dialect(void *buf)
if (smb2_neg_size > smb_buf_length)
goto err_out;

- if (smb2_neg_size + le16_to_cpu(req->DialectCount) * sizeof(__le16) >
+ if (struct_size(req, Dialects, le16_to_cpu(req->DialectCount)) >
smb_buf_length)
goto err_out;

@@ -319,12 +319,6 @@ 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;
@@ -359,8 +353,8 @@ static int smb1_check_user_session(struct ksmbd_work *work)
*/
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_buf = kzalloc(MAX_CIFS_SMALL_BUFFER_SIZE,
+ GFP_KERNEL);
work->response_sz = MAX_CIFS_SMALL_BUFFER_SIZE;

if (!work->response_buf) {
@@ -571,10 +565,11 @@ static int smb_handle_negotiate(struct ksmbd_work *work)

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;
+ if (ksmbd_iov_pin_rsp(work, (void *)neg_rsp,
+ sizeof(struct smb_negotiate_rsp) - 4))
+ return -ENOMEM;

+ 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;
diff --git a/fs/smb/server/smb_common.h b/fs/smb/server/smb_common.h
index 1cbb492cdefe..f1092519c0c2 100644
--- a/fs/smb/server/smb_common.h
+++ b/fs/smb/server/smb_common.h
@@ -200,7 +200,7 @@ struct smb_hdr {
struct smb_negotiate_req {
struct smb_hdr hdr; /* wct = 0 */
__le16 ByteCount;
- unsigned char DialectsArray[1];
+ unsigned char DialectsArray[];
} __packed;

struct smb_negotiate_rsp {
@@ -263,14 +263,14 @@ struct file_directory_info {
__le64 AllocationSize;
__le32 ExtFileAttributes;
__le32 FileNameLength;
- char FileName[1];
+ char FileName[];
} __packed; /* level 0x101 FF resp data */

struct file_names_info {
__le32 NextEntryOffset;
__u32 FileIndex;
__le32 FileNameLength;
- char FileName[1];
+ char FileName[];
} __packed; /* level 0xc FF resp data */

struct file_full_directory_info {
@@ -285,7 +285,7 @@ struct file_full_directory_info {
__le32 ExtFileAttributes;
__le32 FileNameLength;
__le32 EaSize;
- char FileName[1];
+ char FileName[];
} __packed; /* level 0x102 FF resp */

struct file_both_directory_info {
@@ -303,7 +303,7 @@ struct file_both_directory_info {
__u8 ShortNameLength;
__u8 Reserved;
__u8 ShortName[24];
- char FileName[1];
+ char FileName[];
} __packed; /* level 0x104 FFrsp data */

struct file_id_both_directory_info {
@@ -323,7 +323,7 @@ struct file_id_both_directory_info {
__u8 ShortName[24];
__le16 Reserved2;
__le64 UniqueId;
- char FileName[1];
+ char FileName[];
} __packed;

struct file_id_full_dir_info {
@@ -340,7 +340,7 @@ struct file_id_full_dir_info {
__le32 EaSize; /* EA size */
__le32 Reserved;
__le64 UniqueId; /* inode num - le since Samba puts ino in low 32 bit*/
- char FileName[1];
+ char FileName[];
} __packed; /* level 0x105 FF rsp data */

struct smb_version_values {
diff --git a/fs/smb/server/smbacl.c b/fs/smb/server/smbacl.c
index c24df86eb112..d9bbd2eb89c3 100644
--- a/fs/smb/server/smbacl.c
+++ b/fs/smb/server/smbacl.c
@@ -97,7 +97,7 @@ int compare_sids(const struct smb_sid *ctsid, const struct smb_sid *cwsid)
/* 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;
+ num_subauth = min(num_sat, num_saw);
if (num_subauth) {
for (i = 0; i < num_subauth; ++i) {
if (ctsid->sub_auth[i] != cwsid->sub_auth[i]) {
@@ -1185,8 +1185,7 @@ int smb_inherit_dacl(struct ksmbd_conn *conn,
pntsd_size += sizeof(struct smb_acl) + nt_size;
}

- ksmbd_vfs_set_sd_xattr(conn, user_ns,
- path->dentry, pntsd, pntsd_size);
+ ksmbd_vfs_set_sd_xattr(conn, user_ns, path, pntsd, pntsd_size, false);
kfree(pntsd);
}

@@ -1313,7 +1312,7 @@ int smb_check_perm_dacl(struct ksmbd_conn *conn, const struct path *path,

if (IS_ENABLED(CONFIG_FS_POSIX_ACL)) {
posix_acls = get_acl(d_inode(path->dentry), ACL_TYPE_ACCESS);
- if (posix_acls && !found) {
+ if (!IS_ERR_OR_NULL(posix_acls) && !found) {
unsigned int id = -1;

pa_entry = posix_acls->a_entries;
@@ -1337,7 +1336,7 @@ int smb_check_perm_dacl(struct ksmbd_conn *conn, const struct path *path,
}
}
}
- if (posix_acls)
+ if (!IS_ERR_OR_NULL(posix_acls))
posix_acl_release(posix_acls);
}

@@ -1378,7 +1377,7 @@ int smb_check_perm_dacl(struct ksmbd_conn *conn, const struct path *path,

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)
+ bool type_check, bool get_write)
{
int rc;
struct smb_fattr fattr = {{0}};
@@ -1406,7 +1405,7 @@ int set_info_sec(struct ksmbd_conn *conn, struct ksmbd_tree_connect *tcon,
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);
+ ksmbd_vfs_remove_acl_xattrs(user_ns, path);
/* Update posix acls */
if (IS_ENABLED(CONFIG_FS_POSIX_ACL) && fattr.cf_dacls) {
rc = set_posix_acl(user_ns, inode,
@@ -1437,15 +1436,14 @@ int set_info_sec(struct ksmbd_conn *conn, struct ksmbd_tree_connect *tcon,

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);
+ ksmbd_vfs_remove_sd_xattrs(user_ns, path);
+ ksmbd_vfs_set_sd_xattr(conn, user_ns, path, pntsd, ntsd_len,
+ get_write);
}

out:
posix_acl_release(fattr.cf_acls);
posix_acl_release(fattr.cf_dacls);
- mark_inode_dirty(inode);
return rc;
}

diff --git a/fs/smb/server/smbacl.h b/fs/smb/server/smbacl.h
index 618f2e0236b3..9651a2551888 100644
--- a/fs/smb/server/smbacl.h
+++ b/fs/smb/server/smbacl.h
@@ -207,7 +207,7 @@ 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);
+ bool type_check, bool get_write);
void id_to_sid(unsigned int cid, uint sidtype, struct smb_sid *ssid);
void ksmbd_init_domain(u32 *sub_auth);

diff --git a/fs/smb/server/transport_ipc.c b/fs/smb/server/transport_ipc.c
index 40c721f9227e..b49d47bdafc9 100644
--- a/fs/smb/server/transport_ipc.c
+++ b/fs/smb/server/transport_ipc.c
@@ -229,7 +229,7 @@ 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);
+ msg = kvzalloc(msg_sz, GFP_KERNEL);
if (msg)
msg->sz = sz;
return msg;
@@ -268,7 +268,7 @@ static int handle_response(int type, void *payload, size_t sz)
entry->type + 1, type);
}

- entry->response = kvmalloc(sz, GFP_KERNEL | __GFP_ZERO);
+ entry->response = kvzalloc(sz, GFP_KERNEL);
if (!entry->response) {
ret = -ENOMEM;
break;
diff --git a/fs/smb/server/transport_rdma.c b/fs/smb/server/transport_rdma.c
index 7578200f63b1..c5629a68c8b7 100644
--- a/fs/smb/server/transport_rdma.c
+++ b/fs/smb/server/transport_rdma.c
@@ -1241,14 +1241,12 @@ static int smb_direct_writev(struct ksmbd_transport *t,

//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;
+ start = i = 1;
buflen = 0;
while (true) {
buflen += iov[i].iov_len;
@@ -2142,8 +2140,7 @@ static int smb_direct_ib_client_add(struct ib_device *ib_dev)
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))
+ if (!rdma_frwr_is_supported(&ib_dev->attrs))
return 0;

smb_dev = kzalloc(sizeof(*smb_dev), GFP_KERNEL);
@@ -2243,17 +2240,38 @@ bool ksmbd_rdma_capable_netdev(struct net_device *netdev)
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 (smb_dev->ib_dev->ops.get_netdev) {
+ ndev = smb_dev->ib_dev->ops.get_netdev(
+ smb_dev->ib_dev, i + 1);
+ if (!ndev)
+ continue;

- if (ndev == netdev) {
+ if (ndev == netdev) {
+ dev_put(ndev);
+ rdma_capable = true;
+ goto out;
+ }
dev_put(ndev);
- rdma_capable = true;
- goto out;
+ /* if ib_dev does not implement ops.get_netdev
+ * check for matching infiniband GUID in hw_addr
+ */
+ } else if (netdev->type == ARPHRD_INFINIBAND) {
+ struct netdev_hw_addr *ha;
+ union ib_gid gid;
+ u32 port_num;
+ int ret;
+
+ netdev_hw_addr_list_for_each(
+ ha, &netdev->dev_addrs) {
+ memcpy(&gid, ha->addr + 4, sizeof(gid));
+ ret = ib_find_gid(smb_dev->ib_dev, &gid,
+ &port_num, NULL);
+ if (!ret) {
+ rdma_capable = true;
+ goto out;
+ }
+ }
}
- dev_put(ndev);
}
}
out:
diff --git a/fs/smb/server/unicode.c b/fs/smb/server/unicode.c
index a0db699ddafd..33fc6d45c0f3 100644
--- a/fs/smb/server/unicode.c
+++ b/fs/smb/server/unicode.c
@@ -14,46 +14,10 @@
#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
+ * @from: host-endian source string
* @cp: codepage to which character should be converted
* @mapchar: should character be mapped according to mapchars mount option?
*
@@ -64,10 +28,13 @@ static int smb_utf16_bytes(const __le16 *from, int maxbytes,
* Return: string length after conversion
*/
static int
-cifs_mapchar(char *target, const __u16 src_char, const struct nls_table *cp,
+cifs_mapchar(char *target, const __u16 *from, const struct nls_table *cp,
bool mapchar)
{
int len = 1;
+ __u16 src_char;
+
+ src_char = *from;

if (!mapchar)
goto cp_convert;
@@ -105,30 +72,66 @@ cifs_mapchar(char *target, const __u16 src_char, const struct nls_table *cp,

cp_convert:
len = cp->uni2char(src_char, target, NLS_MAX_CHARSET_SIZE);
- if (len <= 0) {
- *target = '?';
- len = 1;
- }
+ if (len <= 0)
+ goto surrogate_pair;
+
+ goto out;
+
+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;
goto out;
}

/*
- * is_char_allowed() - check for valid character
- * @ch: input character to be checked
+ * smb_utf16_bytes() - compute converted string length
+ * @from: pointer to input string
+ * @maxbytes: input string length
+ * @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: 1 if char is allowed, otherwise 0
+ * Return: string length after conversion
*/
-static inline int is_char_allowed(char *ch)
+static int smb_utf16_bytes(const __le16 *from, int maxbytes,
+ const struct nls_table *codepage)
{
- /* check for control chars, wildcards etc. */
- if (!(*ch & 0x80) &&
- (*ch <= 0x1f ||
- *ch == '?' || *ch == '"' || *ch == '<' ||
- *ch == '>' || *ch == '|'))
- return 0;
-
- return 1;
+ int i, j;
+ 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;
+ for (j = 1; j <= 2; j++) {
+ if (i + j < maxwords)
+ ftmp[j] = get_unaligned_le16(&from[i + j]);
+ else
+ ftmp[j] = 0;
+ }
+
+ charlen = cifs_mapchar(tmp, ftmp, codepage, 0);
+ if (charlen > 0)
+ outlen += charlen;
+ else
+ outlen++;
+ }
+
+ return outlen;
}

/*
@@ -158,12 +161,12 @@ static inline int is_char_allowed(char *ch)
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 i, j, charlen, safelen;
int outlen = 0;
int nullsize = nls_nullsize(codepage);
int fromwords = fromlen / 2;
char tmp[NLS_MAX_CHARSET_SIZE];
- __u16 ftmp;
+ __u16 ftmp[3]; /* ftmp[3] = 3array x 2bytes = 6bytes UTF-16 */

/*
* because the chars can be of varying widths, we need to take care
@@ -174,9 +177,15 @@ static int smb_from_utf16(char *to, const __le16 *from, int tolen, int fromlen,
safelen = tolen - (NLS_MAX_CHARSET_SIZE + nullsize);

for (i = 0; i < fromwords; i++) {
- ftmp = get_unaligned_le16(&from[i]);
- if (ftmp == 0)
+ ftmp[0] = get_unaligned_le16(&from[i]);
+ if (ftmp[0] == 0)
break;
+ for (j = 1; j <= 2; j++) {
+ if (i + j < fromwords)
+ ftmp[j] = get_unaligned_le16(&from[i + j]);
+ else
+ ftmp[j] = 0;
+ }

/*
* check to see if converting this character might make the
@@ -191,6 +200,19 @@ static int smb_from_utf16(char *to, const __le16 *from, int tolen, int fromlen,
/* put converted char into 'to' buffer */
charlen = cifs_mapchar(&to[outlen], ftmp, codepage, mapchar);
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 */
@@ -325,6 +347,9 @@ int smbConvertToUTF16(__le16 *target, const char *source, int srclen,
char src_char;
__le16 dst_char;
wchar_t tmp;
+ wchar_t wchar_to[6]; /* UTF-16 */
+ int ret;
+ unicode_t u;

if (!mapchars)
return smb_strtoUTF16(target, source, srclen, cp);
@@ -367,11 +392,57 @@ int smbConvertToUTF16(__le16 *target, const char *source, int srclen,
* 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;
+ if (charlen > 0)
+ goto ctoUTF16;
+
+ /* convert SURROGATE_PAIR */
+ if (strcmp(cp->charset, "utf8"))
+ 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
diff --git a/fs/smb/server/vfs.c b/fs/smb/server/vfs.c
index 36914db8b661..fe2c80ea2e47 100644
--- a/fs/smb/server/vfs.c
+++ b/fs/smb/server/vfs.c
@@ -17,6 +17,7 @@
#include <linux/vmalloc.h>
#include <linux/sched/xacct.h>
#include <linux/crc32c.h>
+#include <linux/namei.h>

#include "../../internal.h" /* for vfs_path_lookup */

@@ -36,19 +37,6 @@
#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)
@@ -62,67 +50,96 @@ static void ksmbd_vfs_inherit_owner(struct ksmbd_work *work,

/**
* 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)
+int ksmbd_vfs_lock_parent(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;
+ if (child->d_parent != parent) {
+ inode_unlock(d_inode(parent));
+ return -ENOENT;
}

- 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)
+static int ksmbd_vfs_path_lookup_locked(struct ksmbd_share_config *share_conf,
+ char *pathname, unsigned int flags,
+ struct path *parent_path,
+ struct path *path)
{
- struct dentry *parent;
- int ret;
+ struct qstr last;
+ struct filename *filename;
+ struct path *root_share_path = &share_conf->vfs_path;
+ int err, type;
+ struct dentry *d;
+
+ if (pathname[0] == '\0') {
+ pathname = share_conf->path;
+ root_share_path = NULL;
+ } else {
+ flags |= LOOKUP_BENEATH;
+ }
+
+ filename = getname_kernel(pathname);
+ if (IS_ERR(filename))
+ return PTR_ERR(filename);

- parent = dget_parent(dentry);
- ret = ksmbd_vfs_lock_parent(user_ns, parent, dentry);
- if (ret) {
- dput(parent);
- return ret;
+ err = vfs_path_parent_lookup(filename, flags,
+ parent_path, &last, &type,
+ root_share_path);
+ if (err) {
+ putname(filename);
+ return err;
}

- ret = inode_permission(user_ns, d_inode(parent),
- MAY_EXEC | MAY_WRITE);
+ if (unlikely(type != LAST_NORM)) {
+ path_put(parent_path);
+ putname(filename);
+ return -ENOENT;
+ }

- inode_unlock(d_inode(parent));
- dput(parent);
- return ret;
+ err = mnt_want_write(parent_path->mnt);
+ if (err) {
+ path_put(parent_path);
+ putname(filename);
+ return -ENOENT;
+ }
+
+ inode_lock_nested(parent_path->dentry->d_inode, I_MUTEX_PARENT);
+ d = lookup_one_qstr_excl(&last, parent_path->dentry, 0);
+ if (IS_ERR(d))
+ goto err_out;
+
+ if (d_is_negative(d)) {
+ dput(d);
+ goto err_out;
+ }
+
+ path->dentry = d;
+ path->mnt = mntget(parent_path->mnt);
+
+ if (test_share_config_flag(share_conf, KSMBD_SHARE_FLAG_CROSSMNT)) {
+ err = follow_down(path);
+ if (err < 0) {
+ path_put(path);
+ goto err_out;
+ }
+ }
+
+ putname(filename);
+ return 0;
+
+err_out:
+ inode_unlock(d_inode(parent_path->dentry));
+ mnt_drop_write(parent_path->mnt);
+ path_put(parent_path);
+ putname(filename);
+ return -ENOENT;
}

-int ksmbd_vfs_query_maximal_access(struct user_namespace *user_ns,
+void 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))
@@ -137,19 +154,8 @@ int ksmbd_vfs_query_maximal_access(struct user_namespace *user_ns,
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))
+ if (!inode_permission(user_ns, d_inode(dentry->d_parent), MAY_EXEC | MAY_WRITE))
*daccess |= FILE_DELETE_LE;
-
- inode_unlock(d_inode(parent));
- dput(parent);
- return ret;
}

/**
@@ -185,6 +191,7 @@ int ksmbd_vfs_create(struct ksmbd_work *work, const char *name, umode_t mode)
} else {
pr_err("File(%s): creation failed (err:%d)\n", name, err);
}
+
done_path_create(&path, dentry);
return err;
}
@@ -218,27 +225,26 @@ int ksmbd_vfs_mkdir(struct ksmbd_work *work, const char *name, umode_t mode)
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)) {
+ if (!err && 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;
+ goto out_err;
}
if (unlikely(d_is_negative(d))) {
dput(d);
err = -ENOENT;
- goto out;
+ goto out_err;
}

ksmbd_vfs_inherit_owner(work, d_inode(path.dentry), d_inode(d));
dput(d);
}
-out:
+
+out_err:
done_path_create(&path, dentry);
if (err)
pr_err("mkdir(%s): creation failed (err:%d)\n", name, err);
@@ -358,15 +364,15 @@ static int check_lock_range(struct file *filp, loff_t start, loff_t end,
* @fid: file id of open file
* @count: read byte count
* @pos: file pos
+ * @rbuf: read data buffer
*
* 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)
+ loff_t *pos, char *rbuf)
{
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))
@@ -410,7 +416,8 @@ static int ksmbd_vfs_stream_write(struct ksmbd_file *fp, char *buf, loff_t *pos,
{
char *stream_buf = NULL, *wbuf;
struct user_namespace *user_ns = file_mnt_user_ns(fp->filp);
- size_t size, v_len;
+ size_t size;
+ ssize_t v_len;
int err = 0;

ksmbd_debug(VFS, "write stream data pos : %llu, count : %zd\n",
@@ -427,14 +434,14 @@ static int ksmbd_vfs_stream_write(struct ksmbd_file *fp, char *buf, loff_t *pos,
fp->stream.name,
fp->stream.size,
&stream_buf);
- if ((int)v_len < 0) {
+ if (v_len < 0) {
pr_err("not found stream in xattr : %zd\n", v_len);
- err = (int)v_len;
+ err = v_len;
goto out;
}

if (v_len < size) {
- wbuf = kvmalloc(size, GFP_KERNEL | __GFP_ZERO);
+ wbuf = kvzalloc(size, GFP_KERNEL);
if (!wbuf) {
err = -ENOMEM;
goto out;
@@ -449,11 +456,12 @@ static int ksmbd_vfs_stream_write(struct ksmbd_file *fp, char *buf, loff_t *pos,
memcpy(&stream_buf[*pos], buf, count);

err = ksmbd_vfs_setxattr(user_ns,
- fp->filp->f_path.dentry,
+ &fp->filp->f_path,
fp->stream.name,
(void *)stream_buf,
size,
- 0);
+ 0,
+ true);
if (err < 0)
goto out;

@@ -510,6 +518,9 @@ int ksmbd_vfs_write(struct ksmbd_work *work, struct ksmbd_file *fp,
}
}

+ /* Reserve lease break for parent dir at closing time */
+ fp->reserve_lease_break = true;
+
/* Do we need to break any of a levelII oplock? */
smb_break_all_levII_oplock(work, fp, 1);

@@ -581,54 +592,32 @@ int ksmbd_vfs_fsync(struct ksmbd_work *work, u64 fid, u64 p_id)
*
* Return: 0 on success, otherwise error
*/
-int ksmbd_vfs_remove_file(struct ksmbd_work *work, char *name)
+int ksmbd_vfs_remove_file(struct ksmbd_work *work, const struct path *path)
{
struct user_namespace *user_ns;
- struct path path;
- struct dentry *parent;
+ struct dentry *parent = path->dentry->d_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) {
+ 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);
+ user_ns = mnt_user_ns(path->mnt);
+ 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);
+ ksmbd_debug(VFS, "rmdir failed, err %d\n", err);
} else {
- err = vfs_unlink(user_ns, d_inode(parent), path.dentry, NULL);
+ err = vfs_unlink(user_ns, d_inode(parent), path->dentry, NULL);
if (err)
- ksmbd_debug(VFS, "%s: unlink failed, err %d\n", name,
- err);
+ ksmbd_debug(VFS, "unlink failed, err %d\n", err);
}

out_err:
- inode_unlock(d_inode(parent));
- dput(parent);
- path_put(&path);
ksmbd_revert_fsids(work);
return err;
}
@@ -687,149 +676,120 @@ int ksmbd_vfs_link(struct ksmbd_work *work, const char *oldname,
return err;
}

-static int ksmbd_validate_entry_in_use(struct dentry *src_dent)
+int ksmbd_vfs_rename(struct ksmbd_work *work, const struct path *old_path,
+ char *newname, int flags)
{
- struct dentry *dst_dent;
+ struct dentry *old_parent, *new_dentry, *trap;
+ struct dentry *old_child = old_path->dentry;
+ struct path new_path;
+ struct qstr new_last;
+ struct renamedata rd;
+ struct filename *to;
+ struct ksmbd_share_config *share_conf = work->tcon->share_conf;
+ struct ksmbd_file *parent_fp;
+ int new_type;
+ int err, lookup_flags = LOOKUP_NO_SYMLINKS;
+
+ if (ksmbd_override_fsids(work))
+ return -ENOMEM;

- spin_lock(&src_dent->d_lock);
- list_for_each_entry(dst_dent, &src_dent->d_subdirs, d_child) {
- struct ksmbd_file *child_fp;
+ to = getname_kernel(newname);
+ if (IS_ERR(to)) {
+ err = PTR_ERR(to);
+ goto revert_fsids;
+ }

- if (d_really_is_negative(dst_dent))
- continue;
+retry:
+ err = vfs_path_parent_lookup(to, lookup_flags | LOOKUP_BENEATH,
+ &new_path, &new_last, &new_type,
+ &share_conf->vfs_path);
+ if (err)
+ goto out1;

- 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;
- }
+ if (old_path->mnt != new_path.mnt) {
+ err = -EXDEV;
+ goto out2;
}
- spin_unlock(&src_dent->d_lock);

- return 0;
-}
+ err = mnt_want_write(old_path->mnt);
+ if (err)
+ goto out2;

-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;
+ trap = lock_rename_child(old_child, new_path.dentry);

- if (!work->tcon->posix_extensions) {
- err = ksmbd_validate_entry_in_use(src_dent);
- if (err)
- return err;
+ old_parent = dget(old_child->d_parent);
+ if (d_unhashed(old_child)) {
+ err = -EINVAL;
+ goto out3;
}

- 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;
+ parent_fp = ksmbd_lookup_fd_inode(old_child->d_parent);
+ if (parent_fp) {
+ if (parent_fp->daccess & FILE_DELETE_LE) {
+ pr_err("parent dir is opened with delete access\n");
+ err = -ESHARE;
+ ksmbd_fd_put(work, parent_fp);
+ goto out3;
+ }
+ ksmbd_fd_put(work, parent_fp);
+ }

- 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;
+ new_dentry = lookup_one_qstr_excl(&new_last, new_path.dentry,
+ lookup_flags | LOOKUP_RENAME_TARGET);
+ if (IS_ERR(new_dentry)) {
+ err = PTR_ERR(new_dentry);
+ goto out3;
}

- 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 (d_is_symlink(new_dentry)) {
+ err = -EACCES;
+ goto out4;
}
- 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;
+ if ((flags & RENAME_NOREPLACE) && d_is_positive(new_dentry)) {
+ err = -EEXIST;
+ goto out4;
+ }

- dst_name = extract_last_component(newname);
- if (!dst_name) {
- dst_name = newname;
- newname = "";
+ if (old_child == trap) {
+ err = -EINVAL;
+ goto out4;
}

- src_dent_parent = dget_parent(fp->filp->f_path.dentry);
- src_dent = fp->filp->f_path.dentry;
+ if (new_dentry == trap) {
+ err = -ENOTEMPTY;
+ goto out4;
+ }
+
+ rd.old_mnt_userns = mnt_user_ns(old_path->mnt),
+ rd.old_dir = d_inode(old_parent),
+ rd.old_dentry = old_child,
+ rd.new_mnt_userns = mnt_user_ns(new_path.mnt),
+ rd.new_dir = new_path.dentry->d_inode,
+ rd.new_dentry = new_dentry,
+ rd.flags = flags,
+ rd.delegated_inode = NULL,
+ err = vfs_rename(&rd);
+ if (err)
+ ksmbd_debug(VFS, "vfs_rename failed err %d\n", err);

- 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;
+out4:
+ dput(new_dentry);
+out3:
+ dput(old_parent);
+ unlock_rename(old_parent, new_path.dentry);
+ mnt_drop_write(old_path->mnt);
+out2:
+ path_put(&new_path);
+
+ if (retry_estale(err, lookup_flags)) {
+ lookup_flags |= LOOKUP_REVAL;
+ goto retry;
}
- 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);
+out1:
+ putname(to);
+revert_fsids:
+ ksmbd_revert_fsids(work);
return err;
}

@@ -892,7 +852,7 @@ ssize_t ksmbd_vfs_listxattr(struct dentry *dentry, char **list)
if (size <= 0)
return size;

- vlist = kvmalloc(size, GFP_KERNEL | __GFP_ZERO);
+ vlist = kvzalloc(size, GFP_KERNEL);
if (!vlist)
return -ENOMEM;

@@ -950,28 +910,38 @@ ssize_t ksmbd_vfs_getxattr(struct user_namespace *user_ns,
/**
* 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
+ * @path: path of dentry to set XATTR at
+ * @attr_name: xattr name for setxattr
+ * @attr_value: xattr value to set
+ * @attr_size: size of xattr value
* @flags: destination buffer length
+ * @get_write: get write access to a mount
*
* 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)
+ const struct path *path, const char *attr_name,
+ void *attr_value, size_t attr_size, int flags,
+ bool get_write)
{
int err;

+ if (get_write == true) {
+ err = mnt_want_write(path->mnt);
+ if (err)
+ return err;
+ }
+
err = vfs_setxattr(user_ns,
- dentry,
+ path->dentry,
attr_name,
attr_value,
attr_size,
flags);
if (err)
ksmbd_debug(VFS, "setxattr failed, err %d\n", err);
+ if (get_write == true)
+ mnt_drop_write(path->mnt);
return err;
}

@@ -1075,19 +1045,34 @@ int ksmbd_vfs_fqar_lseek(struct ksmbd_file *fp, loff_t start, loff_t length,
}

int ksmbd_vfs_remove_xattr(struct user_namespace *user_ns,
- struct dentry *dentry, char *attr_name)
+ const struct path *path, char *attr_name)
{
- return vfs_removexattr(user_ns, dentry, attr_name);
+ int err;
+
+ err = mnt_want_write(path->mnt);
+ if (err)
+ return err;
+
+ err = vfs_removexattr(user_ns, path->dentry, attr_name);
+ mnt_drop_write(path->mnt);
+
+ return err;
}

-int ksmbd_vfs_unlink(struct user_namespace *user_ns,
- struct dentry *dir, struct dentry *dentry)
+int ksmbd_vfs_unlink(struct file *filp)
{
int err = 0;
+ struct dentry *dir, *dentry = filp->f_path.dentry;
+ struct user_namespace *user_ns = file_mnt_user_ns(filp);

- err = ksmbd_vfs_lock_parent(user_ns, dir, dentry);
+ err = mnt_want_write(filp->f_path.mnt);
if (err)
return err;
+
+ dir = dget_parent(dentry);
+ err = ksmbd_vfs_lock_parent(dir, dentry);
+ if (err)
+ goto out;
dget(dentry);

if (S_ISDIR(d_inode(dentry)->i_mode))
@@ -1099,6 +1084,9 @@ int ksmbd_vfs_unlink(struct user_namespace *user_ns,
inode_unlock(d_inode(dir));
if (err)
ksmbd_debug(VFS, "failed to delete, err %d\n", err);
+out:
+ dput(dir);
+ mnt_drop_write(filp->f_path.mnt);

return err;
}
@@ -1201,32 +1189,29 @@ static int ksmbd_vfs_lookup_in_dir(const struct path *dir, char *name,
}

/**
- * 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
+ * ksmbd_vfs_kern_path_locked() - lookup a file and get path info
+ * @name: file path that is relative to share
+ * @flags: lookup flags
+ * @parent_path: if lookup succeed, return parent_path info
+ * @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)
+int ksmbd_vfs_kern_path_locked(struct ksmbd_work *work, char *name,
+ unsigned int flags, struct path *parent_path,
+ 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);
+ err = ksmbd_vfs_path_lookup_locked(share_conf, name, flags, parent_path,
+ path);
if (!err)
return 0;

if (caseless) {
char *filepath;
- struct path parent;
size_t path_len, remain_len;

filepath = kstrdup(name, GFP_KERNEL);
@@ -1236,10 +1221,10 @@ int ksmbd_vfs_kern_path(struct ksmbd_work *work, char *name,
path_len = strlen(filepath);
remain_len = path_len;

- parent = share_conf->vfs_path;
- path_get(&parent);
+ *parent_path = share_conf->vfs_path;
+ path_get(parent_path);

- while (d_can_lookup(parent.dentry)) {
+ while (d_can_lookup(parent_path->dentry)) {
char *filename = filepath + path_len - remain_len;
char *next = strchrnul(filename, '/');
size_t filename_len = next - filename;
@@ -1248,12 +1233,11 @@ int ksmbd_vfs_kern_path(struct ksmbd_work *work, char *name,
if (filename_len == 0)
break;

- err = ksmbd_vfs_lookup_in_dir(&parent, filename,
+ err = ksmbd_vfs_lookup_in_dir(parent_path, filename,
filename_len,
work->conn->um);
- path_put(&parent);
if (err)
- goto out;
+ goto out2;

next[0] = '\0';

@@ -1261,26 +1245,50 @@ int ksmbd_vfs_kern_path(struct ksmbd_work *work, char *name,
share_conf->vfs_path.mnt,
filepath,
flags,
- &parent);
+ path);
if (err)
- goto out;
- else if (is_last) {
- *path = parent;
- goto out;
- }
+ goto out2;
+ else if (is_last)
+ goto out1;
+ path_put(parent_path);
+ *parent_path = *path;

next[0] = '/';
remain_len -= filename_len + 1;
}

- path_put(&parent);
err = -EINVAL;
-out:
+out2:
+ path_put(parent_path);
+out1:
kfree(filepath);
}
+
+ if (!err) {
+ err = mnt_want_write(parent_path->mnt);
+ if (err) {
+ path_put(path);
+ path_put(parent_path);
+ return err;
+ }
+
+ err = ksmbd_vfs_lock_parent(parent_path->dentry, path->dentry);
+ if (err) {
+ path_put(path);
+ path_put(parent_path);
+ }
+ }
return err;
}

+void ksmbd_vfs_kern_path_unlock(struct path *parent_path, struct path *path)
+{
+ inode_unlock(d_inode(parent_path->dentry));
+ mnt_drop_write(parent_path->mnt);
+ path_put(path);
+ path_put(parent_path);
+}
+
struct dentry *ksmbd_vfs_kern_path_create(struct ksmbd_work *work,
const char *name,
unsigned int flags,
@@ -1299,13 +1307,13 @@ struct dentry *ksmbd_vfs_kern_path_create(struct ksmbd_work *work,
}

int ksmbd_vfs_remove_acl_xattrs(struct user_namespace *user_ns,
- struct dentry *dentry)
+ const struct path *path)
{
char *name, *xattr_list = NULL;
ssize_t xattr_list_len;
int err = 0;

- xattr_list_len = ksmbd_vfs_listxattr(dentry, &xattr_list);
+ xattr_list_len = ksmbd_vfs_listxattr(path->dentry, &xattr_list);
if (xattr_list_len < 0) {
goto out;
} else if (!xattr_list_len) {
@@ -1321,25 +1329,25 @@ int ksmbd_vfs_remove_acl_xattrs(struct user_namespace *user_ns,
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);
+ err = ksmbd_vfs_remove_xattr(user_ns, path, 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)
+int ksmbd_vfs_remove_sd_xattrs(struct user_namespace *user_ns, const struct path *path)
{
char *name, *xattr_list = NULL;
ssize_t xattr_list_len;
int err = 0;

- xattr_list_len = ksmbd_vfs_listxattr(dentry, &xattr_list);
+ xattr_list_len = ksmbd_vfs_listxattr(path->dentry, &xattr_list);
if (xattr_list_len < 0) {
goto out;
} else if (!xattr_list_len) {
@@ -1352,7 +1360,7 @@ int ksmbd_vfs_remove_sd_xattrs(struct user_namespace *user_ns,
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);
+ err = ksmbd_vfs_remove_xattr(user_ns, path, name);
if (err)
ksmbd_debug(SMB, "remove xattr failed : %s\n", name);
}
@@ -1376,7 +1384,7 @@ static struct xattr_smb_acl *ksmbd_vfs_make_xattr_posix_acl(struct user_namespac
return NULL;

posix_acls = get_acl(inode, acl_type);
- if (!posix_acls)
+ if (IS_ERR_OR_NULL(posix_acls))
return NULL;

smb_acl = kzalloc(sizeof(struct xattr_smb_acl) +
@@ -1429,13 +1437,15 @@ static struct xattr_smb_acl *ksmbd_vfs_make_xattr_posix_acl(struct user_namespac

int ksmbd_vfs_set_sd_xattr(struct ksmbd_conn *conn,
struct user_namespace *user_ns,
- struct dentry *dentry,
- struct smb_ntsd *pntsd, int len)
+ const struct path *path,
+ struct smb_ntsd *pntsd, int len,
+ bool get_write)
{
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 dentry *dentry = path->dentry;
struct inode *inode = d_inode(dentry);

acl.version = 4;
@@ -1487,9 +1497,9 @@ int ksmbd_vfs_set_sd_xattr(struct ksmbd_conn *conn,
goto out;
}

- rc = ksmbd_vfs_setxattr(user_ns, dentry,
+ rc = ksmbd_vfs_setxattr(user_ns, path,
XATTR_NAME_SD, sd_ndr.data,
- sd_ndr.offset, 0);
+ sd_ndr.offset, 0, get_write);
if (rc < 0)
pr_err("Failed to store XATTR ntacl :%d\n", rc);

@@ -1577,8 +1587,9 @@ int ksmbd_vfs_get_sd_xattr(struct ksmbd_conn *conn,
}

int ksmbd_vfs_set_dos_attrib_xattr(struct user_namespace *user_ns,
- struct dentry *dentry,
- struct xattr_dos_attrib *da)
+ const struct path *path,
+ struct xattr_dos_attrib *da,
+ bool get_write)
{
struct ndr n;
int err;
@@ -1587,8 +1598,8 @@ int ksmbd_vfs_set_dos_attrib_xattr(struct user_namespace *user_ns,
if (err)
return err;

- err = ksmbd_vfs_setxattr(user_ns, dentry, XATTR_NAME_DOS_ATTRIBUTE,
- (void *)n.data, n.offset, 0);
+ err = ksmbd_vfs_setxattr(user_ns, path, XATTR_NAME_DOS_ATTRIBUTE,
+ (void *)n.data, n.offset, 0, get_write);
if (err)
ksmbd_debug(SMB, "failed to store dos attribute in xattr\n");
kfree(n.data);
@@ -1824,10 +1835,11 @@ void ksmbd_vfs_posix_lock_unblock(struct file_lock *flock)
}

int ksmbd_vfs_set_init_posix_acl(struct user_namespace *user_ns,
- struct inode *inode)
+ struct path *path)
{
struct posix_acl_state acl_state;
struct posix_acl *acls;
+ struct inode *inode = d_inode(path->dentry);
int rc;

if (!IS_ENABLED(CONFIG_FS_POSIX_ACL))
@@ -1856,6 +1868,7 @@ int ksmbd_vfs_set_init_posix_acl(struct user_namespace *user_ns,
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",
@@ -1868,23 +1881,25 @@ int ksmbd_vfs_set_init_posix_acl(struct user_namespace *user_ns,
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 path *path, struct inode *parent_inode)
{
struct posix_acl *acls;
struct posix_acl_entry *pace;
+ struct inode *inode = d_inode(path->dentry);
int rc, i;

if (!IS_ENABLED(CONFIG_FS_POSIX_ACL))
return -EOPNOTSUPP;

acls = get_acl(parent_inode, ACL_TYPE_DEFAULT);
- if (!acls)
+ if (IS_ERR_OR_NULL(acls))
return -ENOENT;
pace = acls->a_entries;

@@ -1906,6 +1921,7 @@ int ksmbd_vfs_inherit_posix_acl(struct user_namespace *user_ns,
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
index 593059ca8511..e761dde2443e 100644
--- a/fs/smb/server/vfs.h
+++ b/fs/smb/server/vfs.h
@@ -71,25 +71,23 @@ struct ksmbd_kstat {
__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,
+int ksmbd_vfs_lock_parent(struct dentry *parent, struct dentry *child);
+void 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_read(struct ksmbd_work *work, struct ksmbd_file *fp, size_t count,
+ loff_t *pos, char *rbuf);
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_remove_file(struct ksmbd_work *work, const struct path *path);
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_rename(struct ksmbd_work *work, const struct path *old_path,
+ char *newname, int flags);
int ksmbd_vfs_truncate(struct ksmbd_work *work,
struct ksmbd_file *fp, loff_t size);
struct srv_copychunk;
@@ -110,15 +108,17 @@ 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);
+ const struct path *path, const char *attr_name,
+ void *attr_value, size_t attr_size, int flags,
+ bool get_write);
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);
+ const struct path *path, char *attr_name);
+int ksmbd_vfs_kern_path_locked(struct ksmbd_work *work, char *name,
+ unsigned int flags, struct path *parent_path,
+ struct path *path, bool caseless);
+void ksmbd_vfs_kern_path_unlock(struct path *parent_path, struct path *path);
struct dentry *ksmbd_vfs_kern_path_create(struct ksmbd_work *work,
const char *name,
unsigned int flags,
@@ -131,8 +131,7 @@ 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);
+int ksmbd_vfs_unlink(struct file *filp);
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,
@@ -142,26 +141,27 @@ 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);
+ const struct path *path);
+int ksmbd_vfs_remove_sd_xattrs(struct user_namespace *user_ns, const struct path *path);
int ksmbd_vfs_set_sd_xattr(struct ksmbd_conn *conn,
struct user_namespace *user_ns,
- struct dentry *dentry,
- struct smb_ntsd *pntsd, int len);
+ const struct path *path,
+ struct smb_ntsd *pntsd, int len,
+ bool get_write);
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);
+ const struct path *path,
+ struct xattr_dos_attrib *da,
+ bool get_write);
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);
+ struct path *path);
int ksmbd_vfs_inherit_posix_acl(struct user_namespace *user_ns,
- struct inode *inode,
+ struct path *path,
struct inode *parent_inode);
#endif /* __KSMBD_VFS_H__ */
diff --git a/fs/smb/server/vfs_cache.c b/fs/smb/server/vfs_cache.c
index 6ec6c129465d..2528ce8aeebb 100644
--- a/fs/smb/server/vfs_cache.c
+++ b/fs/smb/server/vfs_cache.c
@@ -65,14 +65,14 @@ static unsigned long inode_hash(struct super_block *sb, unsigned long hashval)
return tmp & inode_hash_mask;
}

-static struct ksmbd_inode *__ksmbd_inode_lookup(struct inode *inode)
+static struct ksmbd_inode *__ksmbd_inode_lookup(struct dentry *de)
{
struct hlist_head *head = inode_hashtable +
- inode_hash(inode->i_sb, inode->i_ino);
+ inode_hash(d_inode(de)->i_sb, (unsigned long)de);
struct ksmbd_inode *ci = NULL, *ret_ci = NULL;

hlist_for_each_entry(ci, head, m_hash) {
- if (ci->m_inode == inode) {
+ if (ci->m_de == de) {
if (atomic_inc_not_zero(&ci->m_count))
ret_ci = ci;
break;
@@ -83,26 +83,27 @@ static struct ksmbd_inode *__ksmbd_inode_lookup(struct inode *inode)

static struct ksmbd_inode *ksmbd_inode_lookup(struct ksmbd_file *fp)
{
- return __ksmbd_inode_lookup(file_inode(fp->filp));
+ return __ksmbd_inode_lookup(fp->filp->f_path.dentry);
}

-static struct ksmbd_inode *ksmbd_inode_lookup_by_vfsinode(struct inode *inode)
+struct ksmbd_inode *ksmbd_inode_lookup_lock(struct dentry *d)
{
struct ksmbd_inode *ci;

read_lock(&inode_hash_lock);
- ci = __ksmbd_inode_lookup(inode);
+ ci = __ksmbd_inode_lookup(d);
read_unlock(&inode_hash_lock);
+
return ci;
}

-int ksmbd_query_inode_status(struct inode *inode)
+int ksmbd_query_inode_status(struct dentry *dentry)
{
struct ksmbd_inode *ci;
int ret = KSMBD_INODE_STATUS_UNKNOWN;

read_lock(&inode_hash_lock);
- ci = __ksmbd_inode_lookup(inode);
+ ci = __ksmbd_inode_lookup(dentry);
if (ci) {
ret = KSMBD_INODE_STATUS_OK;
if (ci->m_flags & (S_DEL_PENDING | S_DEL_ON_CLS))
@@ -142,7 +143,7 @@ void ksmbd_fd_set_delete_on_close(struct ksmbd_file *fp,
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);
+ inode_hash(d_inode(ci->m_de)->i_sb, (unsigned long)ci->m_de);

hlist_add_head(&ci->m_hash, b);
}
@@ -156,7 +157,6 @@ static void ksmbd_inode_unhash(struct ksmbd_inode *ci)

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);
@@ -165,6 +165,7 @@ static int ksmbd_inode_init(struct ksmbd_inode *ci, struct ksmbd_file *fp)
INIT_LIST_HEAD(&ci->m_fp_list);
INIT_LIST_HEAD(&ci->m_op_list);
rwlock_init(&ci->m_lock);
+ ci->m_de = fp->filp->f_path.dentry;
return 0;
}

@@ -208,7 +209,7 @@ static void ksmbd_inode_free(struct ksmbd_inode *ci)
kfree(ci);
}

-static void ksmbd_inode_put(struct ksmbd_inode *ci)
+void ksmbd_inode_put(struct ksmbd_inode *ci)
{
if (atomic_dec_and_test(&ci->m_count))
ksmbd_inode_free(ci);
@@ -243,7 +244,6 @@ void ksmbd_release_inode_hash(void)

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;
@@ -252,7 +252,7 @@ static void __ksmbd_inode_close(struct ksmbd_file *fp)
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,
+ &filp->f_path,
fp->stream.name);
if (err)
pr_err("remove xattr failed : %s\n",
@@ -262,11 +262,9 @@ static void __ksmbd_inode_close(struct ksmbd_file *fp)
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);
+ ksmbd_vfs_unlink(filp);
write_lock(&ci->m_lock);
}
write_unlock(&ci->m_lock);
@@ -335,6 +333,9 @@ static void __ksmbd_close_fd(struct ksmbd_file_table *ft, struct ksmbd_file *fp)

static struct ksmbd_file *ksmbd_fp_get(struct ksmbd_file *fp)
{
+ if (fp->f_state != FP_INITED)
+ return NULL;
+
if (!atomic_inc_not_zero(&fp->refcount))
return NULL;
return fp;
@@ -384,15 +385,20 @@ int ksmbd_close_fd(struct ksmbd_work *work, u64 id)
return 0;

ft = &work->sess->file_table;
- read_lock(&ft->lock);
+ write_lock(&ft->lock);
fp = idr_find(ft->idr, id);
if (fp) {
set_close_state_blocked_works(fp);

- if (!atomic_dec_and_test(&fp->refcount))
+ if (fp->f_state != FP_INITED)
fp = NULL;
+ else {
+ fp->f_state = FP_CLOSED;
+ if (!atomic_dec_and_test(&fp->refcount))
+ fp = NULL;
+ }
}
- read_unlock(&ft->lock);
+ write_unlock(&ft->lock);

if (!fp)
return -EINVAL;
@@ -482,12 +488,15 @@ struct ksmbd_file *ksmbd_lookup_fd_cguid(char *cguid)
return fp;
}

-struct ksmbd_file *ksmbd_lookup_fd_inode(struct inode *inode)
+struct ksmbd_file *ksmbd_lookup_fd_inode(struct dentry *dentry)
{
struct ksmbd_file *lfp;
struct ksmbd_inode *ci;
+ struct inode *inode = d_inode(dentry);

- ci = ksmbd_inode_lookup_by_vfsinode(inode);
+ read_lock(&inode_hash_lock);
+ ci = __ksmbd_inode_lookup(dentry);
+ read_unlock(&inode_hash_lock);
if (!ci)
return NULL;

@@ -572,6 +581,7 @@ struct ksmbd_file *ksmbd_open_fd(struct ksmbd_work *work, struct file *filp)
fp->tcon = work->tcon;
fp->volatile_id = KSMBD_NO_FID;
fp->persistent_id = KSMBD_NO_FID;
+ fp->f_state = FP_NEW;
fp->f_ci = ksmbd_inode_get(fp);

if (!fp->f_ci) {
@@ -593,6 +603,17 @@ struct ksmbd_file *ksmbd_open_fd(struct ksmbd_work *work, struct file *filp)
return ERR_PTR(ret);
}

+void ksmbd_update_fstate(struct ksmbd_file_table *ft, struct ksmbd_file *fp,
+ unsigned int state)
+{
+ if (!fp)
+ return;
+
+ write_lock(&ft->lock);
+ fp->f_state = state;
+ write_unlock(&ft->lock);
+}
+
static int
__close_file_table_ids(struct ksmbd_file_table *ft,
struct ksmbd_tree_connect *tcon,
diff --git a/fs/smb/server/vfs_cache.h b/fs/smb/server/vfs_cache.h
index fcb13413fa8d..a528f0cc775a 100644
--- a/fs/smb/server/vfs_cache.h
+++ b/fs/smb/server/vfs_cache.h
@@ -51,7 +51,7 @@ struct ksmbd_inode {
atomic_t op_count;
/* opinfo count for streams */
atomic_t sop_count;
- struct inode *m_inode;
+ struct dentry *m_de;
unsigned int m_flags;
struct hlist_node m_hash;
struct list_head m_fp_list;
@@ -60,6 +60,12 @@ struct ksmbd_inode {
__le32 m_fattr;
};

+enum {
+ FP_NEW = 0,
+ FP_INITED,
+ FP_CLOSED
+};
+
struct ksmbd_file {
struct file *filp;
u64 persistent_id;
@@ -98,6 +104,8 @@ struct ksmbd_file {
/* if ls is happening on directory, below is valid*/
struct ksmbd_readdir_data readdir_data;
int dot_dotdot[2];
+ unsigned int f_state;
+ bool reserve_lease_break;
};

static inline void set_ctx_actor(struct dir_context *ctx,
@@ -131,9 +139,11 @@ 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_inode *ksmbd_inode_lookup_lock(struct dentry *d);
+void ksmbd_inode_put(struct ksmbd_inode *ci);
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);
+struct ksmbd_file *ksmbd_lookup_fd_inode(struct dentry *dentry);
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);
@@ -142,6 +152,8 @@ 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);
+void ksmbd_update_fstate(struct ksmbd_file_table *ft, struct ksmbd_file *fp,
+ unsigned int state);

/*
* INODE hash
@@ -155,7 +167,7 @@ enum KSMBD_INODE_STATUS {
KSMBD_INODE_STATUS_PENDING_DELETE,
};

-int ksmbd_query_inode_status(struct inode *inode);
+int ksmbd_query_inode_status(struct dentry *dentry);
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);
diff --git a/include/linux/blkdev.h b/include/linux/blkdev.h
index 57674b3c5877..07a7eeef47d3 100644
--- a/include/linux/blkdev.h
+++ b/include/linux/blkdev.h
@@ -565,7 +565,7 @@ struct request_queue {
#define QUEUE_FLAG_NOXMERGES 9 /* No extended merges */
#define QUEUE_FLAG_ADD_RANDOM 10 /* Contributes to random pool */
#define QUEUE_FLAG_SAME_FORCE 12 /* force complete on same CPU */
-#define QUEUE_FLAG_HW_WC 18 /* Write back caching supported */
+#define QUEUE_FLAG_HW_WC 13 /* Write back caching supported */
#define QUEUE_FLAG_INIT_DONE 14 /* queue is initialized */
#define QUEUE_FLAG_STABLE_WRITES 15 /* don't modify blks until WB is done */
#define QUEUE_FLAG_POLL 16 /* IO polling enabled if set */
diff --git a/include/linux/export-internal.h b/include/linux/export-internal.h
index fe7e6ba918f1..29de29af9546 100644
--- a/include/linux/export-internal.h
+++ b/include/linux/export-internal.h
@@ -12,6 +12,7 @@

#define SYMBOL_CRC(sym, crc, sec) \
asm(".section \"___kcrctab" sec "+" #sym "\",\"a\"" "\n" \
+ ".balign 4" "\n" \
"__crc_" #sym ":" "\n" \
".long " #crc "\n" \
".previous" "\n")
diff --git a/include/linux/module.h b/include/linux/module.h
index ec61fb53979a..35876e89eb93 100644
--- a/include/linux/module.h
+++ b/include/linux/module.h
@@ -879,8 +879,17 @@ static inline bool module_sig_ok(struct module *module)
}
#endif /* CONFIG_MODULE_SIG */

+#if defined(CONFIG_MODULES) && defined(CONFIG_KALLSYMS)
int module_kallsyms_on_each_symbol(int (*fn)(void *, const char *,
struct module *, unsigned long),
void *data);
+#else
+static inline int module_kallsyms_on_each_symbol(int (*fn)(void *, const char *,
+ struct module *, unsigned long),
+ void *data)
+{
+ return -EOPNOTSUPP;
+}
+#endif /* CONFIG_MODULES && CONFIG_KALLSYMS */

#endif /* _LINUX_MODULE_H */
diff --git a/include/linux/namei.h b/include/linux/namei.h
index 00fee52df842..5c0149603dc3 100644
--- a/include/linux/namei.h
+++ b/include/linux/namei.h
@@ -57,12 +57,18 @@ static inline int user_path_at(int dfd, const char __user *name, unsigned flags,
return user_path_at_empty(dfd, name, flags, path, NULL);
}

+struct dentry *lookup_one_qstr_excl(const struct qstr *name,
+ struct dentry *base,
+ unsigned int flags);
extern int kern_path(const char *, unsigned, struct path *);

extern struct dentry *kern_path_create(int, const char *, struct path *, unsigned int);
extern struct dentry *user_path_create(int, const char __user *, struct path *, unsigned int);
extern void done_path_create(struct path *, struct dentry *);
extern struct dentry *kern_path_locked(const char *, struct path *);
+int vfs_path_parent_lookup(struct filename *filename, unsigned int flags,
+ struct path *parent, struct qstr *last, int *type,
+ const struct path *root);

extern struct dentry *try_lookup_one_len(const char *, struct dentry *, int);
extern struct dentry *lookup_one_len(const char *, struct dentry *, int);
@@ -81,6 +87,7 @@ extern int follow_down(struct path *);
extern int follow_up(struct path *);

extern struct dentry *lock_rename(struct dentry *, struct dentry *);
+extern struct dentry *lock_rename_child(struct dentry *, struct dentry *);
extern void unlock_rename(struct dentry *, struct dentry *);

extern int __must_check nd_jump_link(const struct path *path);
diff --git a/include/linux/property.h b/include/linux/property.h
index 117cc200c656..587b5b666b5b 100644
--- a/include/linux/property.h
+++ b/include/linux/property.h
@@ -32,7 +32,12 @@ enum dev_dma_attr {
DEV_DMA_COHERENT,
};

-struct fwnode_handle *dev_fwnode(const struct device *dev);
+const struct fwnode_handle *__dev_fwnode_const(const struct device *dev);
+struct fwnode_handle *__dev_fwnode(struct device *dev);
+#define dev_fwnode(dev) \
+ _Generic((dev), \
+ const struct device *: __dev_fwnode_const, \
+ struct device *: __dev_fwnode)(dev)

bool device_property_present(struct device *dev, const char *propname);
int device_property_read_u8_array(struct device *dev, const char *propname,
diff --git a/include/linux/spi/spi.h b/include/linux/spi/spi.h
index 877395e075af..8e9054d9f6df 100644
--- a/include/linux/spi/spi.h
+++ b/include/linux/spi/spi.h
@@ -263,6 +263,26 @@ static inline void *spi_get_drvdata(struct spi_device *spi)
return dev_get_drvdata(&spi->dev);
}

+static inline u8 spi_get_chipselect(const struct spi_device *spi, u8 idx)
+{
+ return spi->chip_select;
+}
+
+static inline void spi_set_chipselect(struct spi_device *spi, u8 idx, u8 chipselect)
+{
+ spi->chip_select = chipselect;
+}
+
+static inline struct gpio_desc *spi_get_csgpiod(const struct spi_device *spi, u8 idx)
+{
+ return spi->cs_gpiod;
+}
+
+static inline void spi_set_csgpiod(struct spi_device *spi, u8 idx, struct gpio_desc *csgpiod)
+{
+ spi->cs_gpiod = csgpiod;
+}
+
struct spi_message;

/**
@@ -1515,6 +1535,9 @@ extern void spi_unregister_device(struct spi_device *spi);
extern const struct spi_device_id *
spi_get_device_id(const struct spi_device *sdev);

+extern const void *
+spi_get_device_match_data(const struct spi_device *sdev);
+
static inline bool
spi_transfer_is_last(struct spi_controller *ctlr, struct spi_transfer *xfer)
{
diff --git a/kernel/module/kallsyms.c b/kernel/module/kallsyms.c
index f5c5c9175333..4523f99b0358 100644
--- a/kernel/module/kallsyms.c
+++ b/kernel/module/kallsyms.c
@@ -494,7 +494,6 @@ unsigned long module_kallsyms_lookup_name(const char *name)
return ret;
}

-#ifdef CONFIG_LIVEPATCH
int module_kallsyms_on_each_symbol(int (*fn)(void *, const char *,
struct module *, unsigned long),
void *data)
@@ -531,4 +530,3 @@ int module_kallsyms_on_each_symbol(int (*fn)(void *, const char *,
mutex_unlock(&module_mutex);
return ret;
}
-#endif /* CONFIG_LIVEPATCH */
diff --git a/kernel/trace/ring_buffer.c b/kernel/trace/ring_buffer.c
index 61803208706a..06d52525407b 100644
--- a/kernel/trace/ring_buffer.c
+++ b/kernel/trace/ring_buffer.c
@@ -705,48 +705,6 @@ rb_time_read_cmpxchg(local_t *l, unsigned long expect, unsigned long set)
return ret == expect;
}

-static int rb_time_cmpxchg(rb_time_t *t, u64 expect, u64 set)
-{
- unsigned long cnt, top, bottom, msb;
- unsigned long cnt2, top2, bottom2, msb2;
- u64 val;
-
- /* Any interruptions in this function should cause a failure */
- cnt = local_read(&t->cnt);
-
- /* The cmpxchg always fails if it interrupted an update */
- if (!__rb_time_read(t, &val, &cnt2))
- return false;
-
- if (val != expect)
- return false;
-
- if ((cnt & 3) != cnt2)
- return false;
-
- cnt2 = cnt + 1;
-
- rb_time_split(val, &top, &bottom, &msb);
- msb = rb_time_val_cnt(msb, cnt);
- top = rb_time_val_cnt(top, cnt);
- bottom = rb_time_val_cnt(bottom, cnt);
-
- rb_time_split(set, &top2, &bottom2, &msb2);
- msb2 = rb_time_val_cnt(msb2, cnt);
- top2 = rb_time_val_cnt(top2, cnt2);
- bottom2 = rb_time_val_cnt(bottom2, cnt2);
-
- if (!rb_time_read_cmpxchg(&t->cnt, cnt, cnt2))
- return false;
- if (!rb_time_read_cmpxchg(&t->msb, msb, msb2))
- return false;
- if (!rb_time_read_cmpxchg(&t->top, top, top2))
- return false;
- if (!rb_time_read_cmpxchg(&t->bottom, bottom, bottom2))
- return false;
- return true;
-}
-
#else /* 64 bits */

/* local64_t always succeeds */
@@ -760,13 +718,6 @@ static void rb_time_set(rb_time_t *t, u64 val)
{
local64_set(&t->time, val);
}
-
-static bool rb_time_cmpxchg(rb_time_t *t, u64 expect, u64 set)
-{
- u64 val;
- val = local64_cmpxchg(&t->time, expect, set);
- return val == expect;
-}
#endif

/*
@@ -935,9 +886,14 @@ static __always_inline bool full_hit(struct trace_buffer *buffer, int cpu, int f
if (!nr_pages || !full)
return true;

- dirty = ring_buffer_nr_dirty_pages(buffer, cpu);
+ /*
+ * Add one as dirty will never equal nr_pages, as the sub-buffer
+ * that the writer is on is not counted as dirty.
+ * This is needed if "buffer_percent" is set to 100.
+ */
+ dirty = ring_buffer_nr_dirty_pages(buffer, cpu) + 1;

- return (dirty * 100) > (full * nr_pages);
+ return (dirty * 100) >= (full * nr_pages);
}

/*
@@ -997,7 +953,8 @@ void ring_buffer_wake_waiters(struct trace_buffer *buffer, int cpu)
/* make sure the waiters see the new index */
smp_wmb();

- rb_wake_up_waiters(&rbwork->work);
+ /* This can be called in any context */
+ irq_work_queue(&rbwork->work);
}

/**
@@ -2981,25 +2938,6 @@ static unsigned rb_calculate_event_length(unsigned length)
return length;
}

-static u64 rb_time_delta(struct ring_buffer_event *event)
-{
- switch (event->type_len) {
- case RINGBUF_TYPE_PADDING:
- return 0;
-
- case RINGBUF_TYPE_TIME_EXTEND:
- return rb_event_time_stamp(event);
-
- case RINGBUF_TYPE_TIME_STAMP:
- return 0;
-
- case RINGBUF_TYPE_DATA:
- return event->time_delta;
- default:
- return 0;
- }
-}
-
static inline int
rb_try_to_discard(struct ring_buffer_per_cpu *cpu_buffer,
struct ring_buffer_event *event)
@@ -3008,8 +2946,6 @@ rb_try_to_discard(struct ring_buffer_per_cpu *cpu_buffer,
struct buffer_page *bpage;
unsigned long index;
unsigned long addr;
- u64 write_stamp;
- u64 delta;

new_index = rb_event_index(event);
old_index = new_index + rb_event_ts_length(event);
@@ -3018,14 +2954,10 @@ rb_try_to_discard(struct ring_buffer_per_cpu *cpu_buffer,

bpage = READ_ONCE(cpu_buffer->tail_page);

- delta = rb_time_delta(event);
-
- if (!rb_time_read(&cpu_buffer->write_stamp, &write_stamp))
- return 0;
-
- /* Make sure the write stamp is read before testing the location */
- barrier();
-
+ /*
+ * Make sure the tail_page is still the same and
+ * the next write location is the end of this event
+ */
if (bpage->page == (void *)addr && rb_page_write(bpage) == old_index) {
unsigned long write_mask =
local_read(&bpage->write) & ~RB_WRITE_MASK;
@@ -3036,20 +2968,20 @@ rb_try_to_discard(struct ring_buffer_per_cpu *cpu_buffer,
* to make sure that the next event adds an absolute
* value and does not rely on the saved write stamp, which
* is now going to be bogus.
+ *
+ * By setting the before_stamp to zero, the next event
+ * is not going to use the write_stamp and will instead
+ * create an absolute timestamp. This means there's no
+ * reason to update the wirte_stamp!
*/
rb_time_set(&cpu_buffer->before_stamp, 0);

- /* Something came in, can't discard */
- if (!rb_time_cmpxchg(&cpu_buffer->write_stamp,
- write_stamp, write_stamp - delta))
- return 0;
-
/*
* If an event were to come in now, it would see that the
* write_stamp and the before_stamp are different, and assume
* that this event just added itself before updating
* the write stamp. The interrupting event will fix the
- * write stamp for us, and use the before stamp as its delta.
+ * write stamp for us, and use an absolute timestamp.
*/

/*
@@ -3488,7 +3420,7 @@ static void check_buffer(struct ring_buffer_per_cpu *cpu_buffer,
return;

/*
- * If this interrupted another event,
+ * If this interrupted another event,
*/
if (atomic_inc_return(this_cpu_ptr(&checking)) != 1)
goto out;
@@ -3632,20 +3564,36 @@ __rb_reserve_next(struct ring_buffer_per_cpu *cpu_buffer,
} else {
u64 ts;
/* SLOW PATH - Interrupted between A and C */
- a_ok = rb_time_read(&cpu_buffer->write_stamp, &info->after);
- /* Was interrupted before here, write_stamp must be valid */
+
+ /* Save the old before_stamp */
+ a_ok = rb_time_read(&cpu_buffer->before_stamp, &info->before);
RB_WARN_ON(cpu_buffer, !a_ok);
+
+ /*
+ * Read a new timestamp and update the before_stamp to make
+ * the next event after this one force using an absolute
+ * timestamp. This is in case an interrupt were to come in
+ * between E and F.
+ */
ts = rb_time_stamp(cpu_buffer->buffer);
+ rb_time_set(&cpu_buffer->before_stamp, ts);
+
barrier();
- /*E*/ if (write == (local_read(&tail_page->write) & RB_WRITE_MASK) &&
- info->after < ts &&
- rb_time_cmpxchg(&cpu_buffer->write_stamp,
- info->after, ts)) {
- /* Nothing came after this event between C and E */
+ /*E*/ a_ok = rb_time_read(&cpu_buffer->write_stamp, &info->after);
+ /* Was interrupted before here, write_stamp must be valid */
+ RB_WARN_ON(cpu_buffer, !a_ok);
+ barrier();
+ /*F*/ if (write == (local_read(&tail_page->write) & RB_WRITE_MASK) &&
+ info->after == info->before && info->after < ts) {
+ /*
+ * Nothing came after this event between C and F, it is
+ * safe to use info->after for the delta as it
+ * matched info->before and is still valid.
+ */
info->delta = ts - info->after;
} else {
/*
- * Interrupted between C and E:
+ * Interrupted between C and F:
* Lost the previous events time stamp. Just set the
* delta to zero, and this will be the same time as
* the event this event interrupted. And the events that
diff --git a/kernel/trace/trace.c b/kernel/trace/trace.c
index 87eca95b57fb..deae65af76ec 100644
--- a/kernel/trace/trace.c
+++ b/kernel/trace/trace.c
@@ -1850,6 +1850,9 @@ update_max_tr(struct trace_array *tr, struct task_struct *tsk, int cpu,
__update_max_tr(tr, tsk, cpu);

arch_spin_unlock(&tr->max_lock);
+
+ /* Any waiters on the old snapshot buffer need to wake up */
+ ring_buffer_wake_waiters(tr->array_buffer.buffer, RING_BUFFER_ALL_CPUS);
}

/**
@@ -1901,12 +1904,23 @@ update_max_tr_single(struct trace_array *tr, struct task_struct *tsk, int cpu)

static int wait_on_pipe(struct trace_iterator *iter, int full)
{
+ int ret;
+
/* Iterators are static, they should be filled or empty */
if (trace_buffer_iter(iter, iter->cpu_file))
return 0;

- return ring_buffer_wait(iter->array_buffer->buffer, iter->cpu_file,
- full);
+ ret = ring_buffer_wait(iter->array_buffer->buffer, iter->cpu_file, full);
+
+#ifdef CONFIG_TRACER_MAX_TRACE
+ /*
+ * Make sure this is still the snapshot buffer, as if a snapshot were
+ * to happen, this would now be the main buffer.
+ */
+ if (iter->snapshot)
+ iter->array_buffer = &iter->tr->max_buffer;
+#endif
+ return ret;
}

#ifdef CONFIG_FTRACE_STARTUP_TEST
@@ -8433,7 +8447,7 @@ tracing_buffers_splice_read(struct file *file, loff_t *ppos,

wait_index = READ_ONCE(iter->wait_index);

- ret = wait_on_pipe(iter, iter->tr->buffer_percent);
+ ret = wait_on_pipe(iter, iter->snapshot ? 0 : iter->tr->buffer_percent);
if (ret)
goto out;

diff --git a/kernel/trace/trace_kprobe.c b/kernel/trace/trace_kprobe.c
index a34a4fcdab7b..e3993d19687d 100644
--- a/kernel/trace/trace_kprobe.c
+++ b/kernel/trace/trace_kprobe.c
@@ -714,14 +714,31 @@ static int count_symbols(void *data, unsigned long unused)
return 0;
}

+struct sym_count_ctx {
+ unsigned int count;
+ const char *name;
+};
+
+static int count_mod_symbols(void *data, const char *name,
+ struct module *module, unsigned long unused)
+{
+ struct sym_count_ctx *ctx = data;
+
+ if (strcmp(name, ctx->name) == 0)
+ ctx->count++;
+
+ return 0;
+}
+
static unsigned int number_of_same_symbols(char *func_name)
{
- unsigned int count;
+ struct sym_count_ctx ctx = { .count = 0, .name = func_name };
+
+ kallsyms_on_each_match_symbol(count_symbols, func_name, &ctx.count);

- count = 0;
- kallsyms_on_each_match_symbol(count_symbols, func_name, &count);
+ module_kallsyms_on_each_symbol(count_mod_symbols, &ctx);

- return count;
+ return ctx.count;
}

static int __trace_kprobe_create(int argc, const char *argv[])
diff --git a/mm/filemap.c b/mm/filemap.c
index d633ab8cd56f..10fe6430693b 100644
--- a/mm/filemap.c
+++ b/mm/filemap.c
@@ -2744,6 +2744,15 @@ ssize_t filemap_read(struct kiocb *iocb, struct iov_iter *iter,
goto put_folios;
end_offset = min_t(loff_t, isize, iocb->ki_pos + iter->count);

+ /*
+ * Pairs with a barrier in
+ * block_write_end()->mark_buffer_dirty() or other page
+ * dirtying routines like iomap_write_end() to ensure
+ * changes to page contents are visible before we see
+ * increased inode size.
+ */
+ smp_rmb();
+
/*
* Once we start copying data, we don't want to be touching any
* cachelines that might be contended:
diff --git a/mm/memory-failure.c b/mm/memory-failure.c
index 99de0328d1be..ebd717157c81 100644
--- a/mm/memory-failure.c
+++ b/mm/memory-failure.c
@@ -1421,7 +1421,7 @@ static bool hwpoison_user_mappings(struct page *p, unsigned long pfn,
* This check implies we don't kill processes if their pages
* are in the swap cache early. Those are always late kills.
*/
- if (!page_mapped(hpage))
+ if (!page_mapped(p))
return true;

if (PageKsm(p)) {
@@ -1477,10 +1477,10 @@ static bool hwpoison_user_mappings(struct page *p, unsigned long pfn,
try_to_unmap(folio, ttu);
}

- unmap_success = !page_mapped(hpage);
+ unmap_success = !page_mapped(p);
if (!unmap_success)
pr_err("%#lx: failed to unmap page (mapcount=%d)\n",
- pfn, page_mapcount(hpage));
+ pfn, page_mapcount(p));

/*
* try_to_unmap() might put mlocked page in lru cache, so call
@@ -1560,7 +1560,7 @@ static void unmap_and_kill(struct list_head *to_kill, unsigned long pfn,
* mapping being torn down is communicated in siginfo, see
* kill_proc()
*/
- loff_t start = (index << PAGE_SHIFT) & ~(size - 1);
+ loff_t start = ((loff_t)index << PAGE_SHIFT) & ~(size - 1);

unmap_mapping_range(mapping, start, size, 0);
}
diff --git a/mm/migrate.c b/mm/migrate.c
index 9372a826e6d0..91bd69c61148 100644
--- a/mm/migrate.c
+++ b/mm/migrate.c
@@ -388,6 +388,7 @@ int folio_migrate_mapping(struct address_space *mapping,
int dirty;
int expected_count = folio_expected_refs(mapping, folio) + extra_count;
long nr = folio_nr_pages(folio);
+ long entries, i;

if (!mapping) {
/* Anonymous page without mapping */
@@ -425,8 +426,10 @@ int folio_migrate_mapping(struct address_space *mapping,
folio_set_swapcache(newfolio);
newfolio->private = folio_get_private(folio);
}
+ entries = nr;
} else {
VM_BUG_ON_FOLIO(folio_test_swapcache(folio), folio);
+ entries = 1;
}

/* Move dirty while page refs frozen and newpage not yet exposed */
@@ -436,7 +439,11 @@ int folio_migrate_mapping(struct address_space *mapping,
folio_set_dirty(newfolio);
}

- xas_store(&xas, newfolio);
+ /* Swap cache still stores N entries instead of a high-order entry */
+ for (i = 0; i < entries; i++) {
+ xas_store(&xas, newfolio);
+ xas_next(&xas);
+ }

/*
* Drop cache reference from old page by unfreezing
diff --git a/net/netfilter/nf_tables_api.c b/net/netfilter/nf_tables_api.c
index 05fa5141af51..3d6ebb9877a4 100644
--- a/net/netfilter/nf_tables_api.c
+++ b/net/netfilter/nf_tables_api.c
@@ -9480,7 +9480,7 @@ static void nft_set_commit_update(struct list_head *set_update_list)
list_for_each_entry_safe(set, next, set_update_list, pending_update) {
list_del_init(&set->pending_update);

- if (!set->ops->commit)
+ if (!set->ops->commit || set->dead)
continue;

set->ops->commit(set);