diff --git a/Documentation/devicetree/bindings/clock/sifive/fu740-prci.yaml b/Documentation/devicetree/bindings/clock/sifive/fu740-prci.yaml index e17143cac316..252085a0cf65 100644 --- a/Documentation/devicetree/bindings/clock/sifive/fu740-prci.yaml +++ b/Documentation/devicetree/bindings/clock/sifive/fu740-prci.yaml @@ -42,6 +42,9 @@ properties: "#clock-cells": const: 1 + "#reset-cells": + const: 1 + required: - compatible - reg @@ -57,4 +60,5 @@ examples: reg = <0x10000000 0x1000>; clocks = <&hfclk>, <&rtcclk>; #clock-cells = <1>; + #reset-cells = <1>; }; diff --git a/Documentation/devicetree/bindings/clock/silabs,si5351.txt b/Documentation/devicetree/bindings/clock/silabs,si5351.txt index 8fe6f80afade..bfda6af76bee 100644 --- a/Documentation/devicetree/bindings/clock/silabs,si5351.txt +++ b/Documentation/devicetree/bindings/clock/silabs,si5351.txt @@ -2,7 +2,7 @@ Binding for Silicon Labs Si5351a/b/c programmable i2c clock generator. Reference [1] Si5351A/B/C Data Sheet - https://www.silabs.com/Support%20Documents/TechnicalDocs/Si5351.pdf + https://www.skyworksinc.com/-/media/Skyworks/SL/documents/public/data-sheets/Si5351-B.pdf The Si5351a/b/c are programmable i2c clock generators with up to 8 output clocks. Si5351a also has a reduced pin-count package (MSOP10) where only diff --git a/Documentation/devicetree/bindings/clock/socionext,uniphier-clock.yaml b/Documentation/devicetree/bindings/clock/socionext,uniphier-clock.yaml index c3930edc410f..9a0cc7341630 100644 --- a/Documentation/devicetree/bindings/clock/socionext,uniphier-clock.yaml +++ b/Documentation/devicetree/bindings/clock/socionext,uniphier-clock.yaml @@ -23,6 +23,7 @@ properties: - socionext,uniphier-ld11-clock - socionext,uniphier-ld20-clock - socionext,uniphier-pxs3-clock + - socionext,uniphier-nx1-clock - description: Media I/O (MIO) clock, SD clock enum: - socionext,uniphier-ld4-mio-clock @@ -33,6 +34,7 @@ properties: - socionext,uniphier-ld11-mio-clock - socionext,uniphier-ld20-sd-clock - socionext,uniphier-pxs3-sd-clock + - socionext,uniphier-nx1-sd-clock - description: Peripheral clock enum: - socionext,uniphier-ld4-peri-clock @@ -43,6 +45,10 @@ properties: - socionext,uniphier-ld11-peri-clock - socionext,uniphier-ld20-peri-clock - socionext,uniphier-pxs3-peri-clock + - socionext,uniphier-nx1-peri-clock + - description: SoC-glue clock + enum: + - socionext,uniphier-pro4-sg-clock "#clock-cells": const: 1 diff --git a/block/blk-core.c b/block/blk-core.c index b043de2baaac..9ee32f85d74e 100644 --- a/block/blk-core.c +++ b/block/blk-core.c @@ -809,10 +809,8 @@ noinline_for_stack bool submit_bio_checks(struct bio *bio) if (unlikely(!current->io_context)) create_task_io_context(current, GFP_ATOMIC, q->node); - if (blk_throtl_bio(bio)) { - blkcg_bio_issue_init(bio); + if (blk_throtl_bio(bio)) return false; - } blk_cgroup_bio_start(bio); blkcg_bio_issue_init(bio); diff --git a/block/blk-ia-ranges.c b/block/blk-ia-ranges.c index c246c425d0d7..b925f3db3ab7 100644 --- a/block/blk-ia-ranges.c +++ b/block/blk-ia-ranges.c @@ -104,8 +104,8 @@ static struct kobj_type blk_ia_ranges_ktype = { }; /** - * disk_register_ia_ranges - register with sysfs a set of independent - * access ranges + * disk_register_independent_access_ranges - register with sysfs a set of + * independent access ranges * @disk: Target disk * @new_iars: New set of independent access ranges * diff --git a/block/blk-mq-sched.c b/block/blk-mq-sched.c index 4be652fa38e7..ba21449439cc 100644 --- a/block/blk-mq-sched.c +++ b/block/blk-mq-sched.c @@ -370,9 +370,6 @@ bool blk_mq_sched_bio_merge(struct request_queue *q, struct bio *bio, bool ret = false; enum hctx_type type; - if (bio_queue_enter(bio)) - return false; - if (e && e->type->ops.bio_merge) { ret = e->type->ops.bio_merge(q, bio, nr_segs); goto out_put; @@ -397,7 +394,6 @@ bool blk_mq_sched_bio_merge(struct request_queue *q, struct bio *bio, spin_unlock(&ctx->lock); out_put: - blk_queue_exit(q); return ret; } diff --git a/block/blk-mq.c b/block/blk-mq.c index 629cf421417f..3ab34c4f20da 100644 --- a/block/blk-mq.c +++ b/block/blk-mq.c @@ -2495,8 +2495,9 @@ static inline unsigned short blk_plug_max_rq_count(struct blk_plug *plug) return BLK_MAX_REQUEST_COUNT; } -static bool blk_attempt_bio_merge(struct request_queue *q, struct bio *bio, - unsigned int nr_segs, bool *same_queue_rq) +static bool blk_mq_attempt_bio_merge(struct request_queue *q, + struct bio *bio, unsigned int nr_segs, + bool *same_queue_rq) { if (!blk_queue_nomerges(q) && bio_mergeable(bio)) { if (blk_attempt_plug_merge(q, bio, nr_segs, same_queue_rq)) @@ -2520,12 +2521,8 @@ static struct request *blk_mq_get_new_requests(struct request_queue *q, }; struct request *rq; - if (unlikely(bio_queue_enter(bio))) + if (blk_mq_attempt_bio_merge(q, bio, nsegs, same_queue_rq)) return NULL; - if (unlikely(!submit_bio_checks(bio))) - goto put_exit; - if (blk_attempt_bio_merge(q, bio, nsegs, same_queue_rq)) - goto put_exit; rq_qos_throttle(q, bio); @@ -2542,26 +2539,44 @@ static struct request *blk_mq_get_new_requests(struct request_queue *q, rq_qos_cleanup(q, bio); if (bio->bi_opf & REQ_NOWAIT) bio_wouldblock_error(bio); -put_exit: - blk_queue_exit(q); + return NULL; } +static inline bool blk_mq_can_use_cached_rq(struct request *rq, + struct bio *bio) +{ + if (blk_mq_get_hctx_type(bio->bi_opf) != rq->mq_hctx->type) + return false; + + if (op_is_flush(rq->cmd_flags) != op_is_flush(bio->bi_opf)) + return false; + + return true; +} + static inline struct request *blk_mq_get_request(struct request_queue *q, struct blk_plug *plug, struct bio *bio, unsigned int nsegs, bool *same_queue_rq) { + struct request *rq; + bool checked = false; + if (plug) { - struct request *rq; rq = rq_list_peek(&plug->cached_rq); if (rq && rq->q == q) { if (unlikely(!submit_bio_checks(bio))) return NULL; - if (blk_attempt_bio_merge(q, bio, nsegs, same_queue_rq)) + if (blk_mq_attempt_bio_merge(q, bio, nsegs, + same_queue_rq)) return NULL; + checked = true; + if (!blk_mq_can_use_cached_rq(rq, bio)) + goto fallback; + rq->cmd_flags = bio->bi_opf; plug->cached_rq = rq_list_next(rq); INIT_LIST_HEAD(&rq->queuelist); rq_qos_throttle(q, bio); @@ -2569,7 +2584,15 @@ static inline struct request *blk_mq_get_request(struct request_queue *q, } } - return blk_mq_get_new_requests(q, plug, bio, nsegs, same_queue_rq); +fallback: + if (unlikely(bio_queue_enter(bio))) + return NULL; + if (!checked && !submit_bio_checks(bio)) + return NULL; + rq = blk_mq_get_new_requests(q, plug, bio, nsegs, same_queue_rq); + if (!rq) + blk_queue_exit(q); + return rq; } /** diff --git a/block/blk-mq.h b/block/blk-mq.h index cb0b5482ca5e..8acfa650f575 100644 --- a/block/blk-mq.h +++ b/block/blk-mq.h @@ -89,15 +89,7 @@ static inline struct blk_mq_hw_ctx *blk_mq_map_queue_type(struct request_queue * return q->queue_hw_ctx[q->tag_set->map[type].mq_map[cpu]]; } -/* - * blk_mq_map_queue() - map (cmd_flags,type) to hardware queue - * @q: request queue - * @flags: request command flags - * @ctx: software queue cpu ctx - */ -static inline struct blk_mq_hw_ctx *blk_mq_map_queue(struct request_queue *q, - unsigned int flags, - struct blk_mq_ctx *ctx) +static inline enum hctx_type blk_mq_get_hctx_type(unsigned int flags) { enum hctx_type type = HCTX_TYPE_DEFAULT; @@ -108,8 +100,20 @@ static inline struct blk_mq_hw_ctx *blk_mq_map_queue(struct request_queue *q, type = HCTX_TYPE_POLL; else if ((flags & REQ_OP_MASK) == REQ_OP_READ) type = HCTX_TYPE_READ; - - return ctx->hctxs[type]; + return type; +} + +/* + * blk_mq_map_queue() - map (cmd_flags,type) to hardware queue + * @q: request queue + * @flags: request command flags + * @ctx: software queue cpu ctx + */ +static inline struct blk_mq_hw_ctx *blk_mq_map_queue(struct request_queue *q, + unsigned int flags, + struct blk_mq_ctx *ctx) +{ + return ctx->hctxs[blk_mq_get_hctx_type(flags)]; } /* @@ -149,7 +153,7 @@ struct blk_mq_alloc_data { blk_mq_req_flags_t flags; unsigned int shallow_depth; unsigned int cmd_flags; - unsigned int rq_flags; + req_flags_t rq_flags; /* allocate multiple requests/tags in one go */ unsigned int nr_tags; diff --git a/block/blk-zoned.c b/block/blk-zoned.c index 1d0c76c18fc5..774ecc598bee 100644 --- a/block/blk-zoned.c +++ b/block/blk-zoned.c @@ -429,9 +429,10 @@ int blkdev_zone_mgmt_ioctl(struct block_device *bdev, fmode_t mode, op = REQ_OP_ZONE_RESET; /* Invalidate the page cache, including dirty pages. */ + filemap_invalidate_lock(bdev->bd_inode->i_mapping); ret = blkdev_truncate_zone_range(bdev, mode, &zrange); if (ret) - return ret; + goto fail; break; case BLKOPENZONE: op = REQ_OP_ZONE_OPEN; @@ -449,15 +450,9 @@ int blkdev_zone_mgmt_ioctl(struct block_device *bdev, fmode_t mode, ret = blkdev_zone_mgmt(bdev, op, zrange.sector, zrange.nr_sectors, GFP_KERNEL); - /* - * Invalidate the page cache again for zone reset: writes can only be - * direct for zoned devices so concurrent writes would not add any page - * to the page cache after/during reset. The page cache may be filled - * again due to concurrent reads though and dropping the pages for - * these is fine. - */ - if (!ret && cmd == BLKRESETZONE) - ret = blkdev_truncate_zone_range(bdev, mode, &zrange); +fail: + if (cmd == BLKRESETZONE) + filemap_invalidate_unlock(bdev->bd_inode->i_mapping); return ret; } diff --git a/block/genhd.c b/block/genhd.c index ca2fbab1d425..c5392cc24d37 100644 --- a/block/genhd.c +++ b/block/genhd.c @@ -394,8 +394,8 @@ static void disk_scan_partitions(struct gendisk *disk) * This function registers the partitioning information in @disk * with the kernel. */ -int device_add_disk(struct device *parent, struct gendisk *disk, - const struct attribute_group **groups) +int __must_check device_add_disk(struct device *parent, struct gendisk *disk, + const struct attribute_group **groups) { struct device *ddev = disk_to_dev(disk); @@ -544,7 +544,7 @@ out_disk_release_events: out_free_ext_minor: if (disk->major == BLOCK_EXT_MAJOR) blk_free_ext_minor(disk->first_minor); - return WARN_ON_ONCE(ret); /* keep until all callers handle errors */ + return ret; } EXPORT_SYMBOL(device_add_disk); diff --git a/block/ioctl.c b/block/ioctl.c index d6af0ac97e57..0a1d10ac2e1a 100644 --- a/block/ioctl.c +++ b/block/ioctl.c @@ -113,6 +113,7 @@ static int blk_ioctl_discard(struct block_device *bdev, fmode_t mode, uint64_t range[2]; uint64_t start, len; struct request_queue *q = bdev_get_queue(bdev); + struct inode *inode = bdev->bd_inode; int err; if (!(mode & FMODE_WRITE)) @@ -135,12 +136,17 @@ static int blk_ioctl_discard(struct block_device *bdev, fmode_t mode, if (start + len > bdev_nr_bytes(bdev)) return -EINVAL; + filemap_invalidate_lock(inode->i_mapping); err = truncate_bdev_range(bdev, mode, start, start + len - 1); if (err) - return err; + goto fail; - return blkdev_issue_discard(bdev, start >> 9, len >> 9, - GFP_KERNEL, flags); + err = blkdev_issue_discard(bdev, start >> 9, len >> 9, + GFP_KERNEL, flags); + +fail: + filemap_invalidate_unlock(inode->i_mapping); + return err; } static int blk_ioctl_zeroout(struct block_device *bdev, fmode_t mode, @@ -148,6 +154,7 @@ static int blk_ioctl_zeroout(struct block_device *bdev, fmode_t mode, { uint64_t range[2]; uint64_t start, end, len; + struct inode *inode = bdev->bd_inode; int err; if (!(mode & FMODE_WRITE)) @@ -170,12 +177,17 @@ static int blk_ioctl_zeroout(struct block_device *bdev, fmode_t mode, return -EINVAL; /* Invalidate the page cache, including dirty pages */ + filemap_invalidate_lock(inode->i_mapping); err = truncate_bdev_range(bdev, mode, start, end); if (err) - return err; + goto fail; - return blkdev_issue_zeroout(bdev, start >> 9, len >> 9, GFP_KERNEL, - BLKDEV_ZERO_NOUNMAP); + err = blkdev_issue_zeroout(bdev, start >> 9, len >> 9, GFP_KERNEL, + BLKDEV_ZERO_NOUNMAP); + +fail: + filemap_invalidate_unlock(inode->i_mapping); + return err; } static int put_ushort(unsigned short __user *argp, unsigned short val) diff --git a/drivers/clk/actions/owl-factor.c b/drivers/clk/actions/owl-factor.c index f15e2621fa18..64f316cf7cfc 100644 --- a/drivers/clk/actions/owl-factor.c +++ b/drivers/clk/actions/owl-factor.c @@ -10,7 +10,6 @@ #include #include -#include #include "owl-factor.h" diff --git a/drivers/clk/clk-ast2600.c b/drivers/clk/clk-ast2600.c index bc3be5f3eae1..24dab2312bc6 100644 --- a/drivers/clk/clk-ast2600.c +++ b/drivers/clk/clk-ast2600.c @@ -51,6 +51,8 @@ static DEFINE_SPINLOCK(aspeed_g6_clk_lock); static struct clk_hw_onecell_data *aspeed_g6_clk_data; static void __iomem *scu_g6_base; +/* AST2600 revision: A0, A1, A2, etc */ +static u8 soc_rev; /* * Clocks marked with CLK_IS_CRITICAL: @@ -191,9 +193,8 @@ static struct clk_hw *ast2600_calc_pll(const char *name, u32 val) static struct clk_hw *ast2600_calc_apll(const char *name, u32 val) { unsigned int mult, div; - u32 chip_id = readl(scu_g6_base + ASPEED_G6_SILICON_REV); - if (((chip_id & CHIP_REVISION_ID) >> 16) >= 2) { + if (soc_rev >= 2) { if (val & BIT(24)) { /* Pass through mode */ mult = div = 1; @@ -707,7 +708,7 @@ static const u32 ast2600_a1_axi_ahb200_tbl[] = { static void __init aspeed_g6_cc(struct regmap *map) { struct clk_hw *hw; - u32 val, div, divbits, chip_id, axi_div, ahb_div; + u32 val, div, divbits, axi_div, ahb_div; clk_hw_register_fixed_rate(NULL, "clkin", NULL, 0, 25000000); @@ -738,8 +739,7 @@ static void __init aspeed_g6_cc(struct regmap *map) axi_div = 2; divbits = (val >> 11) & 0x3; - regmap_read(map, ASPEED_G6_SILICON_REV, &chip_id); - if (chip_id & BIT(16)) { + if (soc_rev >= 1) { if (!divbits) { ahb_div = ast2600_a1_axi_ahb200_tbl[(val >> 8) & 0x3]; if (val & BIT(16)) @@ -784,6 +784,8 @@ static void __init aspeed_g6_cc_init(struct device_node *np) if (!scu_g6_base) return; + soc_rev = (readl(scu_g6_base + ASPEED_G6_SILICON_REV) & CHIP_REVISION_ID) >> 16; + aspeed_g6_clk_data = kzalloc(struct_size(aspeed_g6_clk_data, hws, ASPEED_G6_NUM_CLKS), GFP_KERNEL); if (!aspeed_g6_clk_data) diff --git a/drivers/clk/clk-composite.c b/drivers/clk/clk-composite.c index c04ae0e7e4b4..b9c5f904f535 100644 --- a/drivers/clk/clk-composite.c +++ b/drivers/clk/clk-composite.c @@ -97,6 +97,7 @@ static int clk_composite_determine_rate(struct clk_hw *hw, return ret; req->rate = tmp_req.rate; + req->best_parent_hw = tmp_req.best_parent_hw; req->best_parent_rate = tmp_req.best_parent_rate; return 0; diff --git a/drivers/clk/clk-si5351.c b/drivers/clk/clk-si5351.c index 57e4597cdf4c..93fa8c9e11be 100644 --- a/drivers/clk/clk-si5351.c +++ b/drivers/clk/clk-si5351.c @@ -1,15 +1,15 @@ // SPDX-License-Identifier: GPL-2.0-or-later /* - * clk-si5351.c: Silicon Laboratories Si5351A/B/C I2C Clock Generator + * clk-si5351.c: Skyworks / Silicon Labs Si5351A/B/C I2C Clock Generator * * Sebastian Hesselbarth * Rabeeh Khoury * * References: * [1] "Si5351A/B/C Data Sheet" - * https://www.silabs.com/Support%20Documents/TechnicalDocs/Si5351.pdf - * [2] "Manually Generating an Si5351 Register Map" - * https://www.silabs.com/Support%20Documents/TechnicalDocs/AN619.pdf + * https://www.skyworksinc.com/-/media/Skyworks/SL/documents/public/data-sheets/Si5351-B.pdf + * [2] "AN619: Manually Generating an Si5351 Register Map" + * https://www.skyworksinc.com/-/media/Skyworks/SL/documents/public/application-notes/AN619.pdf */ #include diff --git a/drivers/clk/clk-si5351.h b/drivers/clk/clk-si5351.h index 73dc8effc519..e9e2bfdaaedf 100644 --- a/drivers/clk/clk-si5351.h +++ b/drivers/clk/clk-si5351.h @@ -1,6 +1,6 @@ /* SPDX-License-Identifier: GPL-2.0-or-later */ /* - * clk-si5351.h: Silicon Laboratories Si5351A/B/C I2C Clock Generator + * clk-si5351.h: Skyworks / Silicon Labs Si5351A/B/C I2C Clock Generator * * Sebastian Hesselbarth * Rabeeh Khoury diff --git a/drivers/clk/clk-versaclock5.c b/drivers/clk/clk-versaclock5.c index c6d3b1ab3d55..e7be3e54b9be 100644 --- a/drivers/clk/clk-versaclock5.c +++ b/drivers/clk/clk-versaclock5.c @@ -905,7 +905,7 @@ output_error: static const struct of_device_id clk_vc5_of_match[]; -static int vc5_probe(struct i2c_client *client, const struct i2c_device_id *id) +static int vc5_probe(struct i2c_client *client) { unsigned int oe, sd, src_mask = 0, src_val = 0; struct vc5_driver_data *vc5; @@ -1244,7 +1244,7 @@ static struct i2c_driver vc5_driver = { .pm = &vc5_pm_ops, .of_match_table = clk_vc5_of_match, }, - .probe = vc5_probe, + .probe_new = vc5_probe, .remove = vc5_remove, .id_table = vc5_id, }; diff --git a/drivers/clk/imx/clk.h b/drivers/clk/imx/clk.h index 819949973db1..7d220a01de1f 100644 --- a/drivers/clk/imx/clk.h +++ b/drivers/clk/imx/clk.h @@ -391,11 +391,11 @@ struct clk_hw *__imx8m_clk_hw_composite(const char *name, #define imx8m_clk_hw_composite(name, parent_names, reg) \ _imx8m_clk_hw_composite(name, parent_names, reg, \ - IMX_COMPOSITE_CORE, IMX_COMPOSITE_CLK_FLAGS_DEFAULT) + 0, IMX_COMPOSITE_CLK_FLAGS_DEFAULT) #define imx8m_clk_hw_composite_critical(name, parent_names, reg) \ _imx8m_clk_hw_composite(name, parent_names, reg, \ - IMX_COMPOSITE_CORE, IMX_COMPOSITE_CLK_FLAGS_CRITICAL) + 0, IMX_COMPOSITE_CLK_FLAGS_CRITICAL) #define imx8m_clk_hw_composite_bus(name, parent_names, reg) \ _imx8m_clk_hw_composite(name, parent_names, reg, \ diff --git a/drivers/clk/ingenic/cgu.c b/drivers/clk/ingenic/cgu.c index 266c7595d330..af31633a8862 100644 --- a/drivers/clk/ingenic/cgu.c +++ b/drivers/clk/ingenic/cgu.c @@ -453,15 +453,15 @@ ingenic_clk_calc_div(struct clk_hw *hw, } /* Impose hardware constraints */ - div = min_t(unsigned, div, 1 << clk_info->div.bits); - div = max_t(unsigned, div, 1); + div = clamp_t(unsigned int, div, clk_info->div.div, + clk_info->div.div << clk_info->div.bits); /* * If the divider value itself must be divided before being written to * the divider register, we must ensure we don't have any bits set that * would be lost as a result of doing so. */ - div /= clk_info->div.div; + div = DIV_ROUND_UP(div, clk_info->div.div); div *= clk_info->div.div; return div; diff --git a/drivers/clk/mediatek/clk-mt8195-imp_iic_wrap.c b/drivers/clk/mediatek/clk-mt8195-imp_iic_wrap.c index 0e2ac0a30aa0..4ab312eb26a5 100644 --- a/drivers/clk/mediatek/clk-mt8195-imp_iic_wrap.c +++ b/drivers/clk/mediatek/clk-mt8195-imp_iic_wrap.c @@ -10,8 +10,6 @@ #include #include -#include - static const struct mtk_gate_regs imp_iic_wrap_cg_regs = { .set_ofs = 0xe08, .clr_ofs = 0xe04, diff --git a/drivers/clk/qcom/gcc-msm8996.c b/drivers/clk/qcom/gcc-msm8996.c index 3c3a7ff04562..9b1674b28d45 100644 --- a/drivers/clk/qcom/gcc-msm8996.c +++ b/drivers/clk/qcom/gcc-msm8996.c @@ -2937,20 +2937,6 @@ static struct clk_branch gcc_smmu_aggre0_ahb_clk = { }, }; -static struct clk_branch gcc_aggre1_pnoc_ahb_clk = { - .halt_reg = 0x82014, - .clkr = { - .enable_reg = 0x82014, - .enable_mask = BIT(0), - .hw.init = &(struct clk_init_data){ - .name = "gcc_aggre1_pnoc_ahb_clk", - .parent_names = (const char *[]){ "periph_noc_clk_src" }, - .num_parents = 1, - .ops = &clk_branch2_ops, - }, - }, -}; - static struct clk_branch gcc_aggre2_ufs_axi_clk = { .halt_reg = 0x83014, .clkr = { @@ -3474,7 +3460,6 @@ static struct clk_regmap *gcc_msm8996_clocks[] = { [GCC_AGGRE0_CNOC_AHB_CLK] = &gcc_aggre0_cnoc_ahb_clk.clkr, [GCC_SMMU_AGGRE0_AXI_CLK] = &gcc_smmu_aggre0_axi_clk.clkr, [GCC_SMMU_AGGRE0_AHB_CLK] = &gcc_smmu_aggre0_ahb_clk.clkr, - [GCC_AGGRE1_PNOC_AHB_CLK] = &gcc_aggre1_pnoc_ahb_clk.clkr, [GCC_AGGRE2_UFS_AXI_CLK] = &gcc_aggre2_ufs_axi_clk.clkr, [GCC_AGGRE2_USB3_AXI_CLK] = &gcc_aggre2_usb3_axi_clk.clkr, [GCC_QSPI_AHB_CLK] = &gcc_qspi_ahb_clk.clkr, diff --git a/drivers/clk/rockchip/Kconfig b/drivers/clk/rockchip/Kconfig index 2dfd6a383393..3067bdb6e119 100644 --- a/drivers/clk/rockchip/Kconfig +++ b/drivers/clk/rockchip/Kconfig @@ -80,14 +80,14 @@ config CLK_RK3368 Build the driver for RK3368 Clock Driver. config CLK_RK3399 - tristate "Rockchip RK3399 clock controller support" + bool "Rockchip RK3399 clock controller support" depends on ARM64 || COMPILE_TEST default y help Build the driver for RK3399 Clock Driver. config CLK_RK3568 - tristate "Rockchip RK3568 clock controller support" + bool "Rockchip RK3568 clock controller support" depends on ARM64 || COMPILE_TEST default y help diff --git a/drivers/clk/rockchip/clk-rk3399.c b/drivers/clk/rockchip/clk-rk3399.c index 7924598747b6..306910a3a0d3 100644 --- a/drivers/clk/rockchip/clk-rk3399.c +++ b/drivers/clk/rockchip/clk-rk3399.c @@ -1630,7 +1630,6 @@ static const struct of_device_id clk_rk3399_match_table[] = { }, { } }; -MODULE_DEVICE_TABLE(of, clk_rk3399_match_table); static int __init clk_rk3399_probe(struct platform_device *pdev) { @@ -1656,7 +1655,4 @@ static struct platform_driver clk_rk3399_driver = { .suppress_bind_attrs = true, }, }; -module_platform_driver_probe(clk_rk3399_driver, clk_rk3399_probe); - -MODULE_DESCRIPTION("Rockchip RK3399 Clock Driver"); -MODULE_LICENSE("GPL"); +builtin_platform_driver_probe(clk_rk3399_driver, clk_rk3399_probe); diff --git a/drivers/clk/rockchip/clk-rk3568.c b/drivers/clk/rockchip/clk-rk3568.c index 939e7079c334..69a9e8069a48 100644 --- a/drivers/clk/rockchip/clk-rk3568.c +++ b/drivers/clk/rockchip/clk-rk3568.c @@ -1693,7 +1693,6 @@ static const struct of_device_id clk_rk3568_match_table[] = { }, { } }; -MODULE_DEVICE_TABLE(of, clk_rk3568_match_table); static int __init clk_rk3568_probe(struct platform_device *pdev) { @@ -1719,7 +1718,4 @@ static struct platform_driver clk_rk3568_driver = { .suppress_bind_attrs = true, }, }; -module_platform_driver_probe(clk_rk3568_driver, clk_rk3568_probe); - -MODULE_DESCRIPTION("Rockchip RK3568 Clock Driver"); -MODULE_LICENSE("GPL"); +builtin_platform_driver_probe(clk_rk3568_driver, clk_rk3568_probe); diff --git a/drivers/clk/uniphier/clk-uniphier-core.c b/drivers/clk/uniphier/clk-uniphier-core.c index 12380236d7ab..46c66fac48e6 100644 --- a/drivers/clk/uniphier/clk-uniphier-core.c +++ b/drivers/clk/uniphier/clk-uniphier-core.c @@ -132,6 +132,10 @@ static const struct of_device_id uniphier_clk_match[] = { .compatible = "socionext,uniphier-pxs3-clock", .data = uniphier_pxs3_sys_clk_data, }, + { + .compatible = "socionext,uniphier-nx1-clock", + .data = uniphier_nx1_sys_clk_data, + }, /* Media I/O clock, SD clock */ { .compatible = "socionext,uniphier-ld4-mio-clock", @@ -165,6 +169,10 @@ static const struct of_device_id uniphier_clk_match[] = { .compatible = "socionext,uniphier-pxs3-sd-clock", .data = uniphier_pro5_sd_clk_data, }, + { + .compatible = "socionext,uniphier-nx1-sd-clock", + .data = uniphier_pro5_sd_clk_data, + }, /* Peripheral clock */ { .compatible = "socionext,uniphier-ld4-peri-clock", @@ -198,6 +206,15 @@ static const struct of_device_id uniphier_clk_match[] = { .compatible = "socionext,uniphier-pxs3-peri-clock", .data = uniphier_pro4_peri_clk_data, }, + { + .compatible = "socionext,uniphier-nx1-peri-clock", + .data = uniphier_pro4_peri_clk_data, + }, + /* SoC-glue clock */ + { + .compatible = "socionext,uniphier-pro4-sg-clock", + .data = uniphier_pro4_sg_clk_data, + }, { /* sentinel */ } }; diff --git a/drivers/clk/uniphier/clk-uniphier-sys.c b/drivers/clk/uniphier/clk-uniphier-sys.c index 32b301724183..0180470b24db 100644 --- a/drivers/clk/uniphier/clk-uniphier-sys.c +++ b/drivers/clk/uniphier/clk-uniphier-sys.c @@ -20,6 +20,10 @@ UNIPHIER_CLK_FACTOR("sd-200m", -1, "spll", 1, 10), \ UNIPHIER_CLK_FACTOR("sd-133m", -1, "spll", 1, 15) +#define UNIPHIER_NX1_SYS_CLK_SD \ + UNIPHIER_CLK_FACTOR("sd-200m", -1, "spll", 1, 4), \ + UNIPHIER_CLK_FACTOR("sd-133m", -1, "spll", 1, 6) + #define UNIPHIER_LD4_SYS_CLK_NAND(idx) \ UNIPHIER_CLK_FACTOR("nand-50m", -1, "spll", 1, 32), \ UNIPHIER_CLK_GATE("nand", (idx), "nand-50m", 0x2104, 2) @@ -288,6 +292,8 @@ const struct uniphier_clk_data uniphier_pxs3_sys_clk_data[] = { UNIPHIER_CLK_GATE("sata0", 28, NULL, 0x210c, 7), UNIPHIER_CLK_GATE("sata1", 29, NULL, 0x210c, 8), UNIPHIER_CLK_GATE("sata-phy", 30, NULL, 0x210c, 21), + UNIPHIER_LD11_SYS_CLK_AIO(40), + UNIPHIER_LD11_SYS_CLK_EXIV(42), /* CPU gears */ UNIPHIER_CLK_DIV4("cpll", 2, 3, 4, 8), UNIPHIER_CLK_DIV4("spll", 2, 3, 4, 8), @@ -300,3 +306,44 @@ const struct uniphier_clk_data uniphier_pxs3_sys_clk_data[] = { "spll/4", "spll/8", "s2pll/4", "s2pll/8"), { /* sentinel */ } }; + +const struct uniphier_clk_data uniphier_nx1_sys_clk_data[] = { + UNIPHIER_CLK_FACTOR("cpll", -1, "ref", 100, 1), /* ARM: 2500 MHz */ + UNIPHIER_CLK_FACTOR("spll", -1, "ref", 32, 1), /* 800 MHz */ + UNIPHIER_CLK_FACTOR("uart", 0, "spll", 1, 6), + UNIPHIER_CLK_FACTOR("i2c", 1, "spll", 1, 16), + UNIPHIER_NX1_SYS_CLK_SD, + UNIPHIER_CLK_GATE("emmc", 4, NULL, 0x2108, 8), + UNIPHIER_CLK_GATE("ether", 6, NULL, 0x210c, 0), + UNIPHIER_CLK_GATE("usb30-0", 12, NULL, 0x210c, 16), /* =GIO */ + UNIPHIER_CLK_GATE("usb30-1", 13, NULL, 0x210c, 20), /* =GIO1P */ + UNIPHIER_CLK_GATE("usb30-hsphy0", 16, NULL, 0x210c, 24), + UNIPHIER_CLK_GATE("usb30-ssphy0", 17, NULL, 0x210c, 25), + UNIPHIER_CLK_GATE("usb30-ssphy1", 18, NULL, 0x210c, 26), + UNIPHIER_CLK_GATE("pcie", 24, NULL, 0x210c, 8), + UNIPHIER_CLK_GATE("voc", 52, NULL, 0x2110, 0), + UNIPHIER_CLK_GATE("hdmitx", 58, NULL, 0x2110, 8), + /* CPU gears */ + UNIPHIER_CLK_DIV5("cpll", 2, 4, 8, 16, 32), + UNIPHIER_CLK_CPUGEAR("cpu-ca53", 33, 0x8080, 0xf, 5, + "cpll/2", "cpll/4", "cpll/8", "cpll/16", + "cpll/32"), + { /* sentinel */ } +}; + +const struct uniphier_clk_data uniphier_pro4_sg_clk_data[] = { + UNIPHIER_CLK_DIV("gpll", 4), + { + .name = "sata-ref", + .type = UNIPHIER_CLK_TYPE_MUX, + .idx = 0, + .data.mux = { + .parent_names = { "gpll/4", "ref", }, + .num_parents = 2, + .reg = 0x1a28, + .masks = { 0x1, 0x1, }, + .vals = { 0x0, 0x1, }, + }, + }, + { /* sentinel */ } +}; diff --git a/drivers/clk/uniphier/clk-uniphier.h b/drivers/clk/uniphier/clk-uniphier.h index 9e30362e55e1..dea0c7829aee 100644 --- a/drivers/clk/uniphier/clk-uniphier.h +++ b/drivers/clk/uniphier/clk-uniphier.h @@ -119,6 +119,10 @@ struct uniphier_clk_data { UNIPHIER_CLK_DIV2(parent, div0, div1), \ UNIPHIER_CLK_DIV2(parent, div2, div3) +#define UNIPHIER_CLK_DIV5(parent, div0, div1, div2, div3, div4) \ + UNIPHIER_CLK_DIV4(parent, div0, div1, div2, div3), \ + UNIPHIER_CLK_DIV(parent, div4) + struct clk_hw *uniphier_clk_register_cpugear(struct device *dev, struct regmap *regmap, const char *name, @@ -146,9 +150,11 @@ extern const struct uniphier_clk_data uniphier_pxs2_sys_clk_data[]; extern const struct uniphier_clk_data uniphier_ld11_sys_clk_data[]; extern const struct uniphier_clk_data uniphier_ld20_sys_clk_data[]; extern const struct uniphier_clk_data uniphier_pxs3_sys_clk_data[]; +extern const struct uniphier_clk_data uniphier_nx1_sys_clk_data[]; extern const struct uniphier_clk_data uniphier_ld4_mio_clk_data[]; extern const struct uniphier_clk_data uniphier_pro5_sd_clk_data[]; extern const struct uniphier_clk_data uniphier_ld4_peri_clk_data[]; extern const struct uniphier_clk_data uniphier_pro4_peri_clk_data[]; +extern const struct uniphier_clk_data uniphier_pro4_sg_clk_data[]; #endif /* __CLK_UNIPHIER_H__ */ diff --git a/drivers/virtio/virtio_mem.c b/drivers/virtio/virtio_mem.c index 0da0af251c73..96e5a8782769 100644 --- a/drivers/virtio/virtio_mem.c +++ b/drivers/virtio/virtio_mem.c @@ -2889,6 +2889,7 @@ static unsigned int virtio_mem_features[] = { #if defined(CONFIG_NUMA) && defined(CONFIG_ACPI_NUMA) VIRTIO_MEM_F_ACPI_PXM, #endif + VIRTIO_MEM_F_UNPLUGGED_INACCESSIBLE, }; static const struct virtio_device_id virtio_mem_id_table[] = { diff --git a/fs/cifs/cifs_debug.c b/fs/cifs/cifs_debug.c index de2c12bcfa4b..d282caf9f037 100644 --- a/fs/cifs/cifs_debug.c +++ b/fs/cifs/cifs_debug.c @@ -271,7 +271,8 @@ static int cifs_debug_data_proc_show(struct seq_file *m, void *v) c = 0; spin_lock(&cifs_tcp_ses_lock); list_for_each_entry(server, &cifs_tcp_ses_list, tcp_ses_list) { - if (server->is_channel) + /* channel info will be printed as a part of sessions below */ + if (CIFS_SERVER_IS_CHAN(server)) continue; c++; @@ -358,6 +359,8 @@ skip_rdma: seq_printf(m, " signed"); if (server->posix_ext_supported) seq_printf(m, " posix"); + if (server->nosharesock) + seq_printf(m, " nosharesock"); if (server->rdma) seq_printf(m, "\nRDMA "); @@ -412,12 +415,14 @@ skip_rdma: from_kuid(&init_user_ns, ses->linux_uid), from_kuid(&init_user_ns, ses->cred_uid)); + spin_lock(&ses->chan_lock); if (ses->chan_count > 1) { seq_printf(m, "\n\n\tExtra Channels: %zu ", ses->chan_count-1); for (j = 1; j < ses->chan_count; j++) cifs_dump_channel(m, j, &ses->chans[j]); } + spin_unlock(&ses->chan_lock); seq_puts(m, "\n\n\tShares: "); j = 0; diff --git a/fs/cifs/cifs_dfs_ref.c b/fs/cifs/cifs_dfs_ref.c index 007427ba75e5..b0864da9ef43 100644 --- a/fs/cifs/cifs_dfs_ref.c +++ b/fs/cifs/cifs_dfs_ref.c @@ -307,12 +307,8 @@ static struct vfsmount *cifs_dfs_do_mount(struct dentry *mntpt, static struct vfsmount *cifs_dfs_do_automount(struct dentry *mntpt) { struct cifs_sb_info *cifs_sb; - struct cifs_ses *ses; - struct cifs_tcon *tcon; void *page; - char *full_path, *root_path; - unsigned int xid; - int rc; + char *full_path; struct vfsmount *mnt; cifs_dbg(FYI, "in %s\n", __func__); @@ -324,8 +320,6 @@ static struct vfsmount *cifs_dfs_do_automount(struct dentry *mntpt) * the double backslashes usually used in the UNC. This function * gives us the latter, so we must adjust the result. */ - mnt = ERR_PTR(-ENOMEM); - cifs_sb = CIFS_SB(mntpt->d_sb); if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_NO_DFS) { mnt = ERR_PTR(-EREMOTE); @@ -341,60 +335,11 @@ static struct vfsmount *cifs_dfs_do_automount(struct dentry *mntpt) } convert_delimiter(full_path, '\\'); - cifs_dbg(FYI, "%s: full_path: %s\n", __func__, full_path); - if (!cifs_sb_master_tlink(cifs_sb)) { - cifs_dbg(FYI, "%s: master tlink is NULL\n", __func__); - goto free_full_path; - } - - tcon = cifs_sb_master_tcon(cifs_sb); - if (!tcon) { - cifs_dbg(FYI, "%s: master tcon is NULL\n", __func__); - goto free_full_path; - } - - root_path = kstrdup(tcon->treeName, GFP_KERNEL); - if (!root_path) { - mnt = ERR_PTR(-ENOMEM); - goto free_full_path; - } - cifs_dbg(FYI, "%s: root path: %s\n", __func__, root_path); - - ses = tcon->ses; - xid = get_xid(); - - /* - * If DFS root has been expired, then unconditionally fetch it again to - * refresh DFS referral cache. - */ - rc = dfs_cache_find(xid, ses, cifs_sb->local_nls, cifs_remap(cifs_sb), - root_path + 1, NULL, NULL); - if (!rc) { - rc = dfs_cache_find(xid, ses, cifs_sb->local_nls, - cifs_remap(cifs_sb), full_path + 1, - NULL, NULL); - } - - free_xid(xid); - - if (rc) { - mnt = ERR_PTR(rc); - goto free_root_path; - } - /* - * OK - we were able to get and cache a referral for @full_path. - * - * Now, pass it down to cifs_mount() and it will retry every available - * node server in case of failures - no need to do it here. - */ mnt = cifs_dfs_do_mount(mntpt, cifs_sb, full_path); - cifs_dbg(FYI, "%s: cifs_dfs_do_mount:%s , mnt:%p\n", __func__, - full_path + 1, mnt); + cifs_dbg(FYI, "%s: cifs_dfs_do_mount:%s , mnt:%p\n", __func__, full_path + 1, mnt); -free_root_path: - kfree(root_path); free_full_path: free_dentry_path(page); cdda_exit: diff --git a/fs/cifs/cifs_fs_sb.h b/fs/cifs/cifs_fs_sb.h index f97407520ea1..013a4bd65280 100644 --- a/fs/cifs/cifs_fs_sb.h +++ b/fs/cifs/cifs_fs_sb.h @@ -61,11 +61,6 @@ struct cifs_sb_info { /* only used when CIFS_MOUNT_USE_PREFIX_PATH is set */ char *prepath; - /* - * Canonical DFS path initially provided by the mount call. We might connect to something - * different via DFS but we want to keep it to do failover properly. - */ - char *origin_fullpath; /* \\HOST\SHARE\[OPTIONAL PATH] */ /* randomly generated 128-bit number for indexing dfs mount groups in referral cache */ uuid_t dfs_mount_id; /* diff --git a/fs/cifs/cifsglob.h b/fs/cifs/cifsglob.h index abff31dcd005..be74606724c7 100644 --- a/fs/cifs/cifsglob.h +++ b/fs/cifs/cifsglob.h @@ -15,6 +15,7 @@ #include #include #include +#include #include "cifs_fs_sb.h" #include "cifsacl.h" #include @@ -75,7 +76,8 @@ #define SMB_ECHO_INTERVAL_MAX 600 #define SMB_ECHO_INTERVAL_DEFAULT 60 -/* dns resolution interval in seconds */ +/* dns resolution intervals in seconds */ +#define SMB_DNS_RESOLVE_INTERVAL_MIN 120 #define SMB_DNS_RESOLVE_INTERVAL_DEFAULT 600 /* maximum number of PDUs in one compound */ @@ -99,6 +101,8 @@ #define XATTR_DOS_ATTRIB "user.DOSATTRIB" #endif +#define CIFS_MAX_WORKSTATION_LEN (__NEW_UTS_LEN + 1) /* reasonable max for client */ + /* * CIFS vfs client Status information (based on what we know.) */ @@ -592,6 +596,7 @@ struct TCP_Server_Info { struct list_head pending_mid_q; bool noblocksnd; /* use blocking sendmsg */ bool noautotune; /* do not autotune send buf sizes */ + bool nosharesock; bool tcp_nodelay; unsigned int credits; /* send no more requests at once */ unsigned int max_credits; /* can override large 32000 default at mnt */ @@ -685,13 +690,34 @@ struct TCP_Server_Info { */ int nr_targets; bool noblockcnt; /* use non-blocking connect() */ - bool is_channel; /* if a session channel */ + + /* + * If this is a session channel, + * primary_server holds the ref-counted + * pointer to primary channel connection for the session. + */ +#define CIFS_SERVER_IS_CHAN(server) (!!(server)->primary_server) + struct TCP_Server_Info *primary_server; + #ifdef CONFIG_CIFS_SWN_UPCALL bool use_swn_dstaddr; struct sockaddr_storage swn_dstaddr; #endif #ifdef CONFIG_CIFS_DFS_UPCALL bool is_dfs_conn; /* if a dfs connection */ + struct mutex refpath_lock; /* protects leaf_fullpath */ + /* + * Canonical DFS full paths that were used to chase referrals in mount and reconnect. + * + * origin_fullpath: first or original referral path + * leaf_fullpath: last referral path (might be changed due to nested links in reconnect) + * + * current_fullpath: pointer to either origin_fullpath or leaf_fullpath + * NOTE: cannot be accessed outside cifs_reconnect() and smb2_reconnect() + * + * format: \\HOST\SHARE\[OPTIONAL PATH] + */ + char *origin_fullpath, *leaf_fullpath, *current_fullpath; #endif }; @@ -908,6 +934,7 @@ struct cifs_ses { and after mount option parsing we fill it */ char *domainName; char *password; + char *workstation_name; struct session_key auth_key; struct ntlmssp_auth *ntlmssp; /* ciphertext, flags, server challenge */ enum securityEnum sectype; /* what security flavor was specified? */ @@ -933,16 +960,21 @@ struct cifs_ses { * iface_lock should be taken when accessing any of these fields */ spinlock_t iface_lock; + /* ========= begin: protected by iface_lock ======== */ struct cifs_server_iface *iface_list; size_t iface_count; unsigned long iface_last_update; /* jiffies */ + /* ========= end: protected by iface_lock ======== */ + spinlock_t chan_lock; + /* ========= begin: protected by chan_lock ======== */ #define CIFS_MAX_CHANNELS 16 struct cifs_chan chans[CIFS_MAX_CHANNELS]; struct cifs_chan *binding_chan; size_t chan_count; size_t chan_max; atomic_t chan_seq; /* round robin state */ + /* ========= end: protected by chan_lock ======== */ }; /* @@ -1091,7 +1123,6 @@ struct cifs_tcon { struct cached_fid crfid; /* Cached root fid */ /* BB add field for back pointer to sb struct(s)? */ #ifdef CONFIG_CIFS_DFS_UPCALL - char *dfs_path; /* canonical DFS path */ struct list_head ulist; /* cache update list */ #endif }; @@ -1942,4 +1973,14 @@ static inline bool is_tcon_dfs(struct cifs_tcon *tcon) tcon->share_flags & (SHI1005_FLAGS_DFS | SHI1005_FLAGS_DFS_ROOT); } +static inline bool cifs_is_referral_server(struct cifs_tcon *tcon, + const struct dfs_info3_param *ref) +{ + /* + * Check if all targets are capable of handling DFS referrals as per + * MS-DFSC 2.2.4 RESP_GET_DFS_REFERRAL. + */ + return is_tcon_dfs(tcon) || (ref && (ref->flags & DFSREF_REFERRAL_SERVER)); +} + #endif /* _CIFS_GLOB_H */ diff --git a/fs/cifs/cifsproto.h b/fs/cifs/cifsproto.h index d0f85b666662..f3073a62ce57 100644 --- a/fs/cifs/cifsproto.h +++ b/fs/cifs/cifsproto.h @@ -269,8 +269,9 @@ extern void cifs_close_all_deferred_files(struct cifs_tcon *cifs_tcon); extern void cifs_close_deferred_file_under_dentry(struct cifs_tcon *cifs_tcon, const char *path); - -extern struct TCP_Server_Info *cifs_get_tcp_session(struct smb3_fs_context *ctx); +extern struct TCP_Server_Info * +cifs_get_tcp_session(struct smb3_fs_context *ctx, + struct TCP_Server_Info *primary_server); extern void cifs_put_tcp_session(struct TCP_Server_Info *server, int from_reconnect); extern void cifs_put_tcon(struct cifs_tcon *tcon); @@ -607,7 +608,7 @@ int smb2_parse_query_directory(struct cifs_tcon *tcon, struct kvec *rsp_iov, struct super_block *cifs_get_tcp_super(struct TCP_Server_Info *server); void cifs_put_tcp_super(struct super_block *sb); -int update_super_prepath(struct cifs_tcon *tcon, char *prefix); +int cifs_update_super_prepath(struct cifs_sb_info *cifs_sb, char *prefix); char *extract_hostname(const char *unc); char *extract_sharename(const char *unc); @@ -634,4 +635,7 @@ static inline int cifs_create_options(struct cifs_sb_info *cifs_sb, int options) return options; } +struct super_block *cifs_get_tcon_super(struct cifs_tcon *tcon); +void cifs_put_tcon_super(struct super_block *sb); + #endif /* _CIFSPROTO_H */ diff --git a/fs/cifs/connect.c b/fs/cifs/connect.c index 0abbff4e4135..82577a7a5bb1 100644 --- a/fs/cifs/connect.c +++ b/fs/cifs/connect.c @@ -61,6 +61,20 @@ extern bool disable_legacy_dialects; /* Drop the connection to not overload the server */ #define NUM_STATUS_IO_TIMEOUT 5 +struct mount_ctx { + struct cifs_sb_info *cifs_sb; + struct smb3_fs_context *fs_ctx; + unsigned int xid; + struct TCP_Server_Info *server; + struct cifs_ses *ses; + struct cifs_tcon *tcon; +#ifdef CONFIG_CIFS_DFS_UPCALL + struct cifs_ses *root_ses; + uuid_t mount_id; + char *origin_fullpath, *leaf_fullpath; +#endif +}; + static int ip_connect(struct TCP_Server_Info *server); static int generic_ip_connect(struct TCP_Server_Info *server); static void tlink_rb_insert(struct rb_root *root, struct tcon_link *new_tlink); @@ -115,7 +129,7 @@ static int reconn_set_ipaddr_from_hostname(struct TCP_Server_Info *server) * To make sure we don't use the cached entry, retry 1s * after expiry. */ - ttl = (expiry - now + 1); + ttl = max_t(unsigned long, expiry - now, SMB_DNS_RESOLVE_INTERVAL_MIN) + 1; } rc = !rc ? -1 : 0; @@ -148,139 +162,38 @@ static void cifs_resolve_server(struct work_struct *work) mutex_unlock(&server->srv_mutex); } -#ifdef CONFIG_CIFS_DFS_UPCALL -/* These functions must be called with server->srv_mutex held */ -static void reconn_set_next_dfs_target(struct TCP_Server_Info *server, - struct cifs_sb_info *cifs_sb, - struct dfs_cache_tgt_list *tgt_list, - struct dfs_cache_tgt_iterator **tgt_it) -{ - const char *name; - int rc; - - if (!cifs_sb || !cifs_sb->origin_fullpath) - return; - - if (!*tgt_it) { - *tgt_it = dfs_cache_get_tgt_iterator(tgt_list); - } else { - *tgt_it = dfs_cache_get_next_tgt(tgt_list, *tgt_it); - if (!*tgt_it) - *tgt_it = dfs_cache_get_tgt_iterator(tgt_list); - } - - cifs_dbg(FYI, "%s: UNC: %s\n", __func__, cifs_sb->origin_fullpath); - - name = dfs_cache_get_tgt_name(*tgt_it); - - kfree(server->hostname); - - server->hostname = extract_hostname(name); - if (IS_ERR(server->hostname)) { - cifs_dbg(FYI, - "%s: failed to extract hostname from target: %ld\n", - __func__, PTR_ERR(server->hostname)); - return; - } - - rc = reconn_set_ipaddr_from_hostname(server); - if (rc) { - cifs_dbg(FYI, "%s: failed to resolve hostname: %d\n", - __func__, rc); - } -} - -static inline int reconn_setup_dfs_targets(struct cifs_sb_info *cifs_sb, - struct dfs_cache_tgt_list *tl) -{ - if (!cifs_sb->origin_fullpath) - return -EOPNOTSUPP; - return dfs_cache_noreq_find(cifs_sb->origin_fullpath + 1, NULL, tl); -} -#endif - -/* - * cifs tcp session reconnection +/** + * Mark all sessions and tcons for reconnect. * - * mark tcp session as reconnecting so temporarily locked - * mark all smb sessions as reconnecting for tcp session - * reconnect tcp session - * wake up waiters on reconnection? - (not needed currently) + * @server needs to be previously set to CifsNeedReconnect. */ -int -cifs_reconnect(struct TCP_Server_Info *server) +static void cifs_mark_tcp_ses_conns_for_reconnect(struct TCP_Server_Info *server) { - int rc = 0; - struct list_head *tmp, *tmp2; struct cifs_ses *ses; struct cifs_tcon *tcon; - struct mid_q_entry *mid_entry; + struct mid_q_entry *mid, *nmid; struct list_head retry_list; -#ifdef CONFIG_CIFS_DFS_UPCALL - struct super_block *sb = NULL; - struct cifs_sb_info *cifs_sb = NULL; - struct dfs_cache_tgt_list tgt_list = DFS_CACHE_TGT_LIST_INIT(tgt_list); - struct dfs_cache_tgt_iterator *tgt_it = NULL; -#endif + struct TCP_Server_Info *pserver; - spin_lock(&GlobalMid_Lock); - server->nr_targets = 1; -#ifdef CONFIG_CIFS_DFS_UPCALL - spin_unlock(&GlobalMid_Lock); - sb = cifs_get_tcp_super(server); - if (IS_ERR(sb)) { - rc = PTR_ERR(sb); - cifs_dbg(FYI, "%s: will not do DFS failover: rc = %d\n", - __func__, rc); - sb = NULL; - } else { - cifs_sb = CIFS_SB(sb); - rc = reconn_setup_dfs_targets(cifs_sb, &tgt_list); - if (rc) { - cifs_sb = NULL; - if (rc != -EOPNOTSUPP) { - cifs_server_dbg(VFS, "%s: no target servers for DFS failover\n", - __func__); - } - } else { - server->nr_targets = dfs_cache_get_nr_tgts(&tgt_list); - } - } - cifs_dbg(FYI, "%s: will retry %d target(s)\n", __func__, - server->nr_targets); - spin_lock(&GlobalMid_Lock); -#endif - if (server->tcpStatus == CifsExiting) { - /* the demux thread will exit normally - next time through the loop */ - spin_unlock(&GlobalMid_Lock); -#ifdef CONFIG_CIFS_DFS_UPCALL - dfs_cache_free_tgts(&tgt_list); - cifs_put_tcp_super(sb); -#endif - wake_up(&server->response_q); - return rc; - } else - server->tcpStatus = CifsNeedReconnect; - spin_unlock(&GlobalMid_Lock); server->maxBuf = 0; server->max_read = 0; cifs_dbg(FYI, "Mark tcp session as need reconnect\n"); trace_smb3_reconnect(server->CurrentMid, server->conn_id, server->hostname); + /* + * before reconnecting the tcp session, mark the smb session (uid) and the tid bad so they + * are not used until reconnected. + */ + cifs_dbg(FYI, "%s: marking sessions and tcons for reconnect\n", __func__); + + /* If server is a channel, select the primary channel */ + pserver = CIFS_SERVER_IS_CHAN(server) ? server->primary_server : server; - /* before reconnecting the tcp session, mark the smb session (uid) - and the tid bad so they are not used until reconnected */ - cifs_dbg(FYI, "%s: marking sessions and tcons for reconnect\n", - __func__); spin_lock(&cifs_tcp_ses_lock); - list_for_each(tmp, &server->smb_ses_list) { - ses = list_entry(tmp, struct cifs_ses, smb_ses_list); + list_for_each_entry(ses, &pserver->smb_ses_list, smb_ses_list) { ses->need_reconnect = true; - list_for_each(tmp2, &ses->tcon_list) { - tcon = list_entry(tmp2, struct cifs_tcon, tcon_list); + list_for_each_entry(tcon, &ses->tcon_list, tcon_list) tcon->need_reconnect = true; - } if (ses->tcon_ipc) ses->tcon_ipc->need_reconnect = true; } @@ -290,11 +203,11 @@ cifs_reconnect(struct TCP_Server_Info *server) cifs_dbg(FYI, "%s: tearing down socket\n", __func__); mutex_lock(&server->srv_mutex); if (server->ssocket) { - cifs_dbg(FYI, "State: 0x%x Flags: 0x%lx\n", - server->ssocket->state, server->ssocket->flags); + cifs_dbg(FYI, "State: 0x%x Flags: 0x%lx\n", server->ssocket->state, + server->ssocket->flags); kernel_sock_shutdown(server->ssocket, SHUT_WR); - cifs_dbg(FYI, "Post shutdown state: 0x%x Flags: 0x%lx\n", - server->ssocket->state, server->ssocket->flags); + cifs_dbg(FYI, "Post shutdown state: 0x%x Flags: 0x%lx\n", server->ssocket->state, + server->ssocket->flags); sock_release(server->ssocket); server->ssocket = NULL; } @@ -309,23 +222,21 @@ cifs_reconnect(struct TCP_Server_Info *server) INIT_LIST_HEAD(&retry_list); cifs_dbg(FYI, "%s: moving mids to private list\n", __func__); spin_lock(&GlobalMid_Lock); - list_for_each_safe(tmp, tmp2, &server->pending_mid_q) { - mid_entry = list_entry(tmp, struct mid_q_entry, qhead); - kref_get(&mid_entry->refcount); - if (mid_entry->mid_state == MID_REQUEST_SUBMITTED) - mid_entry->mid_state = MID_RETRY_NEEDED; - list_move(&mid_entry->qhead, &retry_list); - mid_entry->mid_flags |= MID_DELETED; + list_for_each_entry_safe(mid, nmid, &server->pending_mid_q, qhead) { + kref_get(&mid->refcount); + if (mid->mid_state == MID_REQUEST_SUBMITTED) + mid->mid_state = MID_RETRY_NEEDED; + list_move(&mid->qhead, &retry_list); + mid->mid_flags |= MID_DELETED; } spin_unlock(&GlobalMid_Lock); mutex_unlock(&server->srv_mutex); cifs_dbg(FYI, "%s: issuing mid callbacks\n", __func__); - list_for_each_safe(tmp, tmp2, &retry_list) { - mid_entry = list_entry(tmp, struct mid_q_entry, qhead); - list_del_init(&mid_entry->qhead); - mid_entry->callback(mid_entry); - cifs_mid_q_entry_release(mid_entry); + list_for_each_entry_safe(mid, nmid, &retry_list, qhead) { + list_del_init(&mid->qhead); + mid->callback(mid); + cifs_mid_q_entry_release(mid); } if (cifs_rdma_enabled(server)) { @@ -333,38 +244,48 @@ cifs_reconnect(struct TCP_Server_Info *server) smbd_destroy(server); mutex_unlock(&server->srv_mutex); } +} + +static bool cifs_tcp_ses_needs_reconnect(struct TCP_Server_Info *server, int num_targets) +{ + spin_lock(&GlobalMid_Lock); + server->nr_targets = num_targets; + if (server->tcpStatus == CifsExiting) { + /* the demux thread will exit normally next time through the loop */ + spin_unlock(&GlobalMid_Lock); + wake_up(&server->response_q); + return false; + } + server->tcpStatus = CifsNeedReconnect; + spin_unlock(&GlobalMid_Lock); + return true; +} + +/* + * cifs tcp session reconnection + * + * mark tcp session as reconnecting so temporarily locked + * mark all smb sessions as reconnecting for tcp session + * reconnect tcp session + * wake up waiters on reconnection? - (not needed currently) + */ +static int __cifs_reconnect(struct TCP_Server_Info *server) +{ + int rc = 0; + + if (!cifs_tcp_ses_needs_reconnect(server, 1)) + return 0; + + cifs_mark_tcp_ses_conns_for_reconnect(server); do { try_to_freeze(); - mutex_lock(&server->srv_mutex); - if (!cifs_swn_set_server_dstaddr(server)) { -#ifdef CONFIG_CIFS_DFS_UPCALL - if (cifs_sb && cifs_sb->origin_fullpath) - /* - * Set up next DFS target server (if any) for reconnect. If DFS - * feature is disabled, then we will retry last server we - * connected to before. - */ - reconn_set_next_dfs_target(server, cifs_sb, &tgt_list, &tgt_it); - else { -#endif - /* - * Resolve the hostname again to make sure that IP address is up-to-date. - */ + /* resolve the hostname again to make sure that IP address is up-to-date */ rc = reconn_set_ipaddr_from_hostname(server); - if (rc) { - cifs_dbg(FYI, "%s: failed to resolve hostname: %d\n", - __func__, rc); - } - -#ifdef CONFIG_CIFS_DFS_UPCALL - } -#endif - - + cifs_dbg(FYI, "%s: reconn_set_ipaddr_from_hostname: rc=%d\n", __func__, rc); } if (cifs_rdma_enabled(server)) @@ -372,8 +293,8 @@ cifs_reconnect(struct TCP_Server_Info *server) else rc = generic_ip_connect(server); if (rc) { - cifs_dbg(FYI, "reconnect error %d\n", rc); mutex_unlock(&server->srv_mutex); + cifs_dbg(FYI, "%s: reconnect error %d\n", __func__, rc); msleep(3000); } else { atomic_inc(&tcpSesReconnectCount); @@ -387,19 +308,6 @@ cifs_reconnect(struct TCP_Server_Info *server) } } while (server->tcpStatus == CifsNeedReconnect); -#ifdef CONFIG_CIFS_DFS_UPCALL - if (tgt_it) { - rc = dfs_cache_noreq_update_tgthint(cifs_sb->origin_fullpath + 1, - tgt_it); - if (rc) { - cifs_server_dbg(VFS, "%s: failed to update DFS target hint: rc = %d\n", - __func__, rc); - } - dfs_cache_free_tgts(&tgt_list); - } - - cifs_put_tcp_super(sb); -#endif if (server->tcpStatus == CifsNeedNegotiate) mod_delayed_work(cifsiod_wq, &server->echo, 0); @@ -407,6 +315,147 @@ cifs_reconnect(struct TCP_Server_Info *server) return rc; } +#ifdef CONFIG_CIFS_DFS_UPCALL +static int __reconnect_target_unlocked(struct TCP_Server_Info *server, const char *target) +{ + int rc; + char *hostname; + + if (!cifs_swn_set_server_dstaddr(server)) { + if (server->hostname != target) { + hostname = extract_hostname(target); + if (!IS_ERR(hostname)) { + kfree(server->hostname); + server->hostname = hostname; + } else { + cifs_dbg(FYI, "%s: couldn't extract hostname or address from dfs target: %ld\n", + __func__, PTR_ERR(hostname)); + cifs_dbg(FYI, "%s: default to last target server: %s\n", __func__, + server->hostname); + } + } + /* resolve the hostname again to make sure that IP address is up-to-date. */ + rc = reconn_set_ipaddr_from_hostname(server); + cifs_dbg(FYI, "%s: reconn_set_ipaddr_from_hostname: rc=%d\n", __func__, rc); + } + /* Reconnect the socket */ + if (cifs_rdma_enabled(server)) + rc = smbd_reconnect(server); + else + rc = generic_ip_connect(server); + + return rc; +} + +static int reconnect_target_unlocked(struct TCP_Server_Info *server, struct dfs_cache_tgt_list *tl, + struct dfs_cache_tgt_iterator **target_hint) +{ + int rc; + struct dfs_cache_tgt_iterator *tit; + + *target_hint = NULL; + + /* If dfs target list is empty, then reconnect to last server */ + tit = dfs_cache_get_tgt_iterator(tl); + if (!tit) + return __reconnect_target_unlocked(server, server->hostname); + + /* Otherwise, try every dfs target in @tl */ + for (; tit; tit = dfs_cache_get_next_tgt(tl, tit)) { + rc = __reconnect_target_unlocked(server, dfs_cache_get_tgt_name(tit)); + if (!rc) { + *target_hint = tit; + break; + } + } + return rc; +} + +static int reconnect_dfs_server(struct TCP_Server_Info *server) +{ + int rc = 0; + const char *refpath = server->current_fullpath + 1; + struct dfs_cache_tgt_list tl = DFS_CACHE_TGT_LIST_INIT(tl); + struct dfs_cache_tgt_iterator *target_hint = NULL; + int num_targets = 0; + + /* + * Determine the number of dfs targets the referral path in @cifs_sb resolves to. + * + * smb2_reconnect() needs to know how long it should wait based upon the number of dfs + * targets (server->nr_targets). It's also possible that the cached referral was cleared + * through /proc/fs/cifs/dfscache or the target list is empty due to server settings after + * refreshing the referral, so, in this case, default it to 1. + */ + if (!dfs_cache_noreq_find(refpath, NULL, &tl)) + num_targets = dfs_cache_get_nr_tgts(&tl); + if (!num_targets) + num_targets = 1; + + if (!cifs_tcp_ses_needs_reconnect(server, num_targets)) + return 0; + + cifs_mark_tcp_ses_conns_for_reconnect(server); + + do { + try_to_freeze(); + mutex_lock(&server->srv_mutex); + + rc = reconnect_target_unlocked(server, &tl, &target_hint); + if (rc) { + /* Failed to reconnect socket */ + mutex_unlock(&server->srv_mutex); + cifs_dbg(FYI, "%s: reconnect error %d\n", __func__, rc); + msleep(3000); + continue; + } + /* + * Socket was created. Update tcp session status to CifsNeedNegotiate so that a + * process waiting for reconnect will know it needs to re-establish session and tcon + * through the reconnected target server. + */ + atomic_inc(&tcpSesReconnectCount); + set_credits(server, 1); + spin_lock(&GlobalMid_Lock); + if (server->tcpStatus != CifsExiting) + server->tcpStatus = CifsNeedNegotiate; + spin_unlock(&GlobalMid_Lock); + cifs_swn_reset_server_dstaddr(server); + mutex_unlock(&server->srv_mutex); + } while (server->tcpStatus == CifsNeedReconnect); + + if (target_hint) + dfs_cache_noreq_update_tgthint(refpath, target_hint); + + dfs_cache_free_tgts(&tl); + + /* Need to set up echo worker again once connection has been established */ + if (server->tcpStatus == CifsNeedNegotiate) + mod_delayed_work(cifsiod_wq, &server->echo, 0); + + wake_up(&server->response_q); + return rc; +} + +int cifs_reconnect(struct TCP_Server_Info *server) +{ + /* If tcp session is not an dfs connection, then reconnect to last target server */ + spin_lock(&cifs_tcp_ses_lock); + if (!server->is_dfs_conn || !server->origin_fullpath || !server->leaf_fullpath) { + spin_unlock(&cifs_tcp_ses_lock); + return __cifs_reconnect(server); + } + spin_unlock(&cifs_tcp_ses_lock); + + return reconnect_dfs_server(server); +} +#else +int cifs_reconnect(struct TCP_Server_Info *server) +{ + return __cifs_reconnect(server); +} +#endif + static void cifs_echo_request(struct work_struct *work) { @@ -665,13 +714,14 @@ dequeue_mid(struct mid_q_entry *mid, bool malformed) * Trying to handle/dequeue a mid after the send_recv() * function has finished processing it is a bug. */ - if (mid->mid_flags & MID_DELETED) + if (mid->mid_flags & MID_DELETED) { + spin_unlock(&GlobalMid_Lock); pr_warn_once("trying to dequeue a deleted mid\n"); - else { + } else { list_del_init(&mid->qhead); mid->mid_flags |= MID_DELETED; + spin_unlock(&GlobalMid_Lock); } - spin_unlock(&GlobalMid_Lock); } static unsigned int @@ -794,6 +844,10 @@ static void clean_demultiplex_info(struct TCP_Server_Info *server) */ } +#ifdef CONFIG_CIFS_DFS_UPCALL + kfree(server->origin_fullpath); + kfree(server->leaf_fullpath); +#endif kfree(server); length = atomic_dec_return(&tcpSesAllocCount); @@ -1217,7 +1271,13 @@ static int match_server(struct TCP_Server_Info *server, struct smb3_fs_context * { struct sockaddr *addr = (struct sockaddr *)&ctx->dstaddr; - if (ctx->nosharesock) + if (ctx->nosharesock) { + server->nosharesock = true; + return 0; + } + + /* this server does not share socket */ + if (server->nosharesock) return 0; /* If multidialect negotiation see if existing sessions match one */ @@ -1283,7 +1343,7 @@ cifs_find_tcp_session(struct smb3_fs_context *ctx) * Skip ses channels since they're only handled in lower layers * (e.g. cifs_send_recv). */ - if (server->is_channel || !match_server(server, ctx)) + if (CIFS_SERVER_IS_CHAN(server) || !match_server(server, ctx)) continue; ++server->srv_count; @@ -1314,6 +1374,10 @@ cifs_put_tcp_session(struct TCP_Server_Info *server, int from_reconnect) list_del_init(&server->tcp_ses_list); spin_unlock(&cifs_tcp_ses_lock); + /* For secondary channels, we pick up ref-count on the primary server */ + if (CIFS_SERVER_IS_CHAN(server)) + cifs_put_tcp_session(server->primary_server, from_reconnect); + cancel_delayed_work_sync(&server->echo); cancel_delayed_work_sync(&server->resolve); @@ -1333,7 +1397,10 @@ cifs_put_tcp_session(struct TCP_Server_Info *server, int from_reconnect) spin_unlock(&GlobalMid_Lock); cifs_crypto_secmech_release(server); - cifs_fscache_release_client_cookie(server); + + /* fscache server cookies are based on primary channel only */ + if (!CIFS_SERVER_IS_CHAN(server)) + cifs_fscache_release_client_cookie(server); kfree(server->session_key.response); server->session_key.response = NULL; @@ -1346,7 +1413,8 @@ cifs_put_tcp_session(struct TCP_Server_Info *server, int from_reconnect) } struct TCP_Server_Info * -cifs_get_tcp_session(struct smb3_fs_context *ctx) +cifs_get_tcp_session(struct smb3_fs_context *ctx, + struct TCP_Server_Info *primary_server) { struct TCP_Server_Info *tcp_ses = NULL; int rc; @@ -1383,6 +1451,10 @@ cifs_get_tcp_session(struct smb3_fs_context *ctx) tcp_ses->in_flight = 0; tcp_ses->max_in_flight = 0; tcp_ses->credits = 1; + if (primary_server) { + ++primary_server->srv_count; + tcp_ses->primary_server = primary_server; + } init_waitqueue_head(&tcp_ses->response_q); init_waitqueue_head(&tcp_ses->request_q); INIT_LIST_HEAD(&tcp_ses->pending_mid_q); @@ -1403,6 +1475,9 @@ cifs_get_tcp_session(struct smb3_fs_context *ctx) INIT_DELAYED_WORK(&tcp_ses->resolve, cifs_resolve_server); INIT_DELAYED_WORK(&tcp_ses->reconnect, smb2_reconnect_server); mutex_init(&tcp_ses->reconnect_mutex); +#ifdef CONFIG_CIFS_DFS_UPCALL + mutex_init(&tcp_ses->refpath_lock); +#endif memcpy(&tcp_ses->srcaddr, &ctx->srcaddr, sizeof(tcp_ses->srcaddr)); memcpy(&tcp_ses->dstaddr, &ctx->dstaddr, @@ -1481,7 +1556,9 @@ smbd_connected: list_add(&tcp_ses->tcp_ses_list, &cifs_tcp_ses_list); spin_unlock(&cifs_tcp_ses_lock); - cifs_fscache_get_client_cookie(tcp_ses); + /* fscache server cookies are based on primary channel only */ + if (!CIFS_SERVER_IS_CHAN(tcp_ses)) + cifs_fscache_get_client_cookie(tcp_ses); /* queue echo request delayed work */ queue_delayed_work(cifsiod_wq, &tcp_ses->echo, tcp_ses->echo_interval); @@ -1501,6 +1578,8 @@ out_err_crypto_release: out_err: if (tcp_ses) { + if (CIFS_SERVER_IS_CHAN(tcp_ses)) + cifs_put_tcp_session(tcp_ses->primary_server, false); kfree(tcp_ses->hostname); if (tcp_ses->ssocket) sock_release(tcp_ses->ssocket); @@ -1519,8 +1598,12 @@ static int match_session(struct cifs_ses *ses, struct smb3_fs_context *ctx) * If an existing session is limited to less channels than * requested, it should not be reused */ - if (ses->chan_max < ctx->max_channels) + spin_lock(&ses->chan_lock); + if (ses->chan_max < ctx->max_channels) { + spin_unlock(&ses->chan_lock); return 0; + } + spin_unlock(&ses->chan_lock); switch (ses->sectype) { case Kerberos: @@ -1655,6 +1738,7 @@ cifs_find_smb_ses(struct TCP_Server_Info *server, struct smb3_fs_context *ctx) void cifs_put_smb_ses(struct cifs_ses *ses) { unsigned int rc, xid; + unsigned int chan_count; struct TCP_Server_Info *server = ses->server; cifs_dbg(FYI, "%s: ses_count=%d\n", __func__, ses->ses_count); @@ -1696,12 +1780,24 @@ void cifs_put_smb_ses(struct cifs_ses *ses) list_del_init(&ses->smb_ses_list); spin_unlock(&cifs_tcp_ses_lock); + spin_lock(&ses->chan_lock); + chan_count = ses->chan_count; + spin_unlock(&ses->chan_lock); + /* close any extra channels */ - if (ses->chan_count > 1) { + if (chan_count > 1) { int i; - for (i = 1; i < ses->chan_count; i++) + for (i = 1; i < chan_count; i++) { + /* + * note: for now, we're okay accessing ses->chans + * without chan_lock. But when chans can go away, we'll + * need to introduce ref counting to make sure that chan + * is not freed from under us. + */ cifs_put_tcp_session(ses->chans[i].server, 0); + ses->chans[i].server = NULL; + } } sesInfoFree(ses); @@ -1885,16 +1981,18 @@ cifs_get_smb_ses(struct TCP_Server_Info *server, struct smb3_fs_context *ctx) ses->status); mutex_lock(&ses->session_mutex); - rc = cifs_negotiate_protocol(xid, ses); - if (rc) { - mutex_unlock(&ses->session_mutex); - /* problem -- put our ses reference */ - cifs_put_smb_ses(ses); - free_xid(xid); - return ERR_PTR(rc); - } if (ses->need_reconnect) { cifs_dbg(FYI, "Session needs reconnect\n"); + + rc = cifs_negotiate_protocol(xid, ses); + if (rc) { + mutex_unlock(&ses->session_mutex); + /* problem -- put our ses reference */ + cifs_put_smb_ses(ses); + free_xid(xid); + return ERR_PTR(rc); + } + rc = cifs_setup_session(xid, ses, ctx->local_nls); if (rc) { @@ -1942,6 +2040,12 @@ cifs_get_smb_ses(struct TCP_Server_Info *server, struct smb3_fs_context *ctx) if (!ses->domainName) goto get_ses_fail; } + if (ctx->workstation_name) { + ses->workstation_name = kstrdup(ctx->workstation_name, + GFP_KERNEL); + if (!ses->workstation_name) + goto get_ses_fail; + } if (ctx->domainauto) ses->domainAuto = ctx->domainauto; ses->cred_uid = ctx->cred_uid; @@ -1952,9 +2056,11 @@ cifs_get_smb_ses(struct TCP_Server_Info *server, struct smb3_fs_context *ctx) mutex_lock(&ses->session_mutex); /* add server as first channel */ + spin_lock(&ses->chan_lock); ses->chans[0].server = server; ses->chan_count = 1; ses->chan_max = ctx->multichannel ? ctx->max_channels:1; + spin_unlock(&ses->chan_lock); rc = cifs_negotiate_protocol(xid, ses); if (!rc) @@ -2286,8 +2392,6 @@ cifs_get_tcon(struct cifs_ses *ses, struct smb3_fs_context *ctx) list_add(&tcon->tcon_list, &ses->tcon_list); spin_unlock(&cifs_tcp_ses_lock); - cifs_fscache_get_super_cookie(tcon); - return tcon; out_fail: @@ -2849,73 +2953,64 @@ int cifs_setup_cifs_sb(struct cifs_sb_info *cifs_sb) } /* Release all succeed connections */ -static inline void mount_put_conns(struct cifs_sb_info *cifs_sb, - unsigned int xid, - struct TCP_Server_Info *server, - struct cifs_ses *ses, struct cifs_tcon *tcon) +static inline void mount_put_conns(struct mount_ctx *mnt_ctx) { int rc = 0; - if (tcon) - cifs_put_tcon(tcon); - else if (ses) - cifs_put_smb_ses(ses); - else if (server) - cifs_put_tcp_session(server, 0); - cifs_sb->mnt_cifs_flags &= ~CIFS_MOUNT_POSIX_PATHS; - free_xid(xid); + if (mnt_ctx->tcon) + cifs_put_tcon(mnt_ctx->tcon); + else if (mnt_ctx->ses) + cifs_put_smb_ses(mnt_ctx->ses); + else if (mnt_ctx->server) + cifs_put_tcp_session(mnt_ctx->server, 0); + mnt_ctx->cifs_sb->mnt_cifs_flags &= ~CIFS_MOUNT_POSIX_PATHS; + free_xid(mnt_ctx->xid); } /* Get connections for tcp, ses and tcon */ -static int mount_get_conns(struct smb3_fs_context *ctx, struct cifs_sb_info *cifs_sb, - unsigned int *xid, - struct TCP_Server_Info **nserver, - struct cifs_ses **nses, struct cifs_tcon **ntcon) +static int mount_get_conns(struct mount_ctx *mnt_ctx) { int rc = 0; - struct TCP_Server_Info *server; - struct cifs_ses *ses; - struct cifs_tcon *tcon; + struct TCP_Server_Info *server = NULL; + struct cifs_ses *ses = NULL; + struct cifs_tcon *tcon = NULL; + struct smb3_fs_context *ctx = mnt_ctx->fs_ctx; + struct cifs_sb_info *cifs_sb = mnt_ctx->cifs_sb; + unsigned int xid; - *nserver = NULL; - *nses = NULL; - *ntcon = NULL; - - *xid = get_xid(); + xid = get_xid(); /* get a reference to a tcp session */ - server = cifs_get_tcp_session(ctx); + server = cifs_get_tcp_session(ctx, NULL); if (IS_ERR(server)) { rc = PTR_ERR(server); - return rc; + server = NULL; + goto out; } - *nserver = server; - /* get a reference to a SMB session */ ses = cifs_get_smb_ses(server, ctx); if (IS_ERR(ses)) { rc = PTR_ERR(ses); - return rc; + ses = NULL; + goto out; } - *nses = ses; - if ((ctx->persistent == true) && (!(ses->server->capabilities & SMB2_GLOBAL_CAP_PERSISTENT_HANDLES))) { cifs_server_dbg(VFS, "persistent handles not supported by server\n"); - return -EOPNOTSUPP; + rc = -EOPNOTSUPP; + goto out; } /* search for existing tcon to this server share */ tcon = cifs_get_tcon(ses, ctx); if (IS_ERR(tcon)) { rc = PTR_ERR(tcon); - return rc; + tcon = NULL; + goto out; } - *ntcon = tcon; - /* if new SMB3.11 POSIX extensions are supported do not remap / and \ */ if (tcon->posix_extensions) cifs_sb->mnt_cifs_flags |= CIFS_MOUNT_POSIX_PATHS; @@ -2926,17 +3021,19 @@ static int mount_get_conns(struct smb3_fs_context *ctx, struct cifs_sb_info *cif * reset of caps checks mount to see if unix extensions disabled * for just this mount. */ - reset_cifs_unix_caps(*xid, tcon, cifs_sb, ctx); + reset_cifs_unix_caps(xid, tcon, cifs_sb, ctx); if ((tcon->ses->server->tcpStatus == CifsNeedReconnect) && (le64_to_cpu(tcon->fsUnixInfo.Capability) & - CIFS_UNIX_TRANSPORT_ENCRYPTION_MANDATORY_CAP)) - return -EACCES; + CIFS_UNIX_TRANSPORT_ENCRYPTION_MANDATORY_CAP)) { + rc = -EACCES; + goto out; + } } else tcon->unix_ext = 0; /* server does not support them */ /* do not care if a following call succeed - informational */ if (!tcon->pipe && server->ops->qfs_tcon) { - server->ops->qfs_tcon(*xid, tcon, cifs_sb); + server->ops->qfs_tcon(xid, tcon, cifs_sb); if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_RO_CACHE) { if (tcon->fsDevInfo.DeviceCharacteristics & cpu_to_le32(FILE_READ_ONLY_DEVICE)) @@ -2946,6 +3043,12 @@ static int mount_get_conns(struct smb3_fs_context *ctx, struct cifs_sb_info *cif cifs_dbg(VFS, "read only mount of RW share\n"); /* no need to log a RW mount of a typical RW share */ } + /* + * The cookie is initialized from volume info returned above. + * Inside cifs_fscache_get_super_cookie it checks + * that we do not get super cookie twice. + */ + cifs_fscache_get_super_cookie(tcon); } /* @@ -2960,7 +3063,13 @@ static int mount_get_conns(struct smb3_fs_context *ctx, struct cifs_sb_info *cif (cifs_sb->ctx->rsize > server->ops->negotiate_rsize(tcon, ctx))) cifs_sb->ctx->rsize = server->ops->negotiate_rsize(tcon, ctx); - return 0; +out: + mnt_ctx->server = server; + mnt_ctx->ses = ses; + mnt_ctx->tcon = tcon; + mnt_ctx->xid = xid; + + return rc; } static int mount_setup_tlink(struct cifs_sb_info *cifs_sb, struct cifs_ses *ses, @@ -2990,18 +3099,17 @@ static int mount_setup_tlink(struct cifs_sb_info *cifs_sb, struct cifs_ses *ses, } #ifdef CONFIG_CIFS_DFS_UPCALL -static int mount_get_dfs_conns(struct smb3_fs_context *ctx, struct cifs_sb_info *cifs_sb, - unsigned int *xid, struct TCP_Server_Info **nserver, - struct cifs_ses **nses, struct cifs_tcon **ntcon) +/* Get unique dfs connections */ +static int mount_get_dfs_conns(struct mount_ctx *mnt_ctx) { int rc; - ctx->nosharesock = true; - rc = mount_get_conns(ctx, cifs_sb, xid, nserver, nses, ntcon); - if (*nserver) { + mnt_ctx->fs_ctx->nosharesock = true; + rc = mount_get_conns(mnt_ctx); + if (mnt_ctx->server) { cifs_dbg(FYI, "%s: marking tcp session as a dfs connection\n", __func__); spin_lock(&cifs_tcp_ses_lock); - (*nserver)->is_dfs_conn = true; + mnt_ctx->server->is_dfs_conn = true; spin_unlock(&cifs_tcp_ses_lock); } return rc; @@ -3043,190 +3151,38 @@ build_unc_path_to_root(const struct smb3_fs_context *ctx, } /* - * expand_dfs_referral - Perform a dfs referral query and update the cifs_sb + * expand_dfs_referral - Update cifs_sb from dfs referral path * - * If a referral is found, cifs_sb->ctx->mount_options will be (re-)allocated - * to a string containing updated options for the submount. Otherwise it - * will be left untouched. - * - * Returns the rc from get_dfs_path to the caller, which can be used to - * determine whether there were referrals. + * cifs_sb->ctx->mount_options will be (re-)allocated to a string containing updated options for the + * submount. Otherwise it will be left untouched. */ -static int -expand_dfs_referral(const unsigned int xid, struct cifs_ses *ses, - struct smb3_fs_context *ctx, struct cifs_sb_info *cifs_sb, - char *ref_path) +static int expand_dfs_referral(struct mount_ctx *mnt_ctx, const char *full_path, + struct dfs_info3_param *referral) { int rc; - struct dfs_info3_param referral = {0}; - char *full_path = NULL, *mdata = NULL; + struct cifs_sb_info *cifs_sb = mnt_ctx->cifs_sb; + struct smb3_fs_context *ctx = mnt_ctx->fs_ctx; + char *fake_devname = NULL, *mdata = NULL; - if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_NO_DFS) - return -EREMOTE; - - full_path = build_unc_path_to_root(ctx, cifs_sb, true); - if (IS_ERR(full_path)) - return PTR_ERR(full_path); - - rc = dfs_cache_find(xid, ses, cifs_sb->local_nls, cifs_remap(cifs_sb), - ref_path, &referral, NULL); - if (!rc) { - char *fake_devname = NULL; - - mdata = cifs_compose_mount_options(cifs_sb->ctx->mount_options, - full_path + 1, &referral, - &fake_devname); - free_dfs_info_param(&referral); - - if (IS_ERR(mdata)) { - rc = PTR_ERR(mdata); - mdata = NULL; - } else { - /* - * We can not clear out the whole structure since we - * no longer have an explicit function to parse - * a mount-string. Instead we need to clear out the - * individual fields that are no longer valid. - */ - kfree(ctx->prepath); - ctx->prepath = NULL; - rc = cifs_setup_volume_info(ctx, mdata, fake_devname); - } - kfree(fake_devname); - kfree(cifs_sb->ctx->mount_options); - cifs_sb->ctx->mount_options = mdata; - } - kfree(full_path); - return rc; -} - -static int get_next_dfs_tgt(struct dfs_cache_tgt_list *tgt_list, - struct dfs_cache_tgt_iterator **tgt_it) -{ - if (!*tgt_it) - *tgt_it = dfs_cache_get_tgt_iterator(tgt_list); - else - *tgt_it = dfs_cache_get_next_tgt(tgt_list, *tgt_it); - return !*tgt_it ? -EHOSTDOWN : 0; -} - -static int update_vol_info(const struct dfs_cache_tgt_iterator *tgt_it, - struct smb3_fs_context *fake_ctx, struct smb3_fs_context *ctx) -{ - const char *tgt = dfs_cache_get_tgt_name(tgt_it); - int len = strlen(tgt) + 2; - char *new_unc; - - new_unc = kmalloc(len, GFP_KERNEL); - if (!new_unc) - return -ENOMEM; - scnprintf(new_unc, len, "\\%s", tgt); - - kfree(ctx->UNC); - ctx->UNC = new_unc; - - if (fake_ctx->prepath) { - kfree(ctx->prepath); - ctx->prepath = fake_ctx->prepath; - fake_ctx->prepath = NULL; - } - memcpy(&ctx->dstaddr, &fake_ctx->dstaddr, sizeof(ctx->dstaddr)); - - return 0; -} - -static int do_dfs_failover(const char *path, const char *full_path, struct cifs_sb_info *cifs_sb, - struct smb3_fs_context *ctx, struct cifs_ses *root_ses, - unsigned int *xid, struct TCP_Server_Info **server, - struct cifs_ses **ses, struct cifs_tcon **tcon) -{ - int rc; - char *npath = NULL; - struct dfs_cache_tgt_list tgt_list = DFS_CACHE_TGT_LIST_INIT(tgt_list); - struct dfs_cache_tgt_iterator *tgt_it = NULL; - struct smb3_fs_context tmp_ctx = {NULL}; - - if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_NO_DFS) - return -EOPNOTSUPP; - - npath = dfs_cache_canonical_path(path, cifs_sb->local_nls, cifs_remap(cifs_sb)); - if (IS_ERR(npath)) - return PTR_ERR(npath); - - cifs_dbg(FYI, "%s: path=%s full_path=%s\n", __func__, npath, full_path); - - rc = dfs_cache_noreq_find(npath, NULL, &tgt_list); - if (rc) - goto out; - /* - * We use a 'tmp_ctx' here because we need pass it down to the mount_{get,put} functions to - * test connection against new DFS targets. - */ - rc = smb3_fs_context_dup(&tmp_ctx, ctx); - if (rc) - goto out; - - for (;;) { - struct dfs_info3_param ref = {0}; - char *fake_devname = NULL, *mdata = NULL; - - /* Get next DFS target server - if any */ - rc = get_next_dfs_tgt(&tgt_list, &tgt_it); - if (rc) - break; - - rc = dfs_cache_get_tgt_referral(npath, tgt_it, &ref); - if (rc) - break; - - cifs_dbg(FYI, "%s: old ctx: UNC=%s prepath=%s\n", __func__, tmp_ctx.UNC, - tmp_ctx.prepath); - - mdata = cifs_compose_mount_options(cifs_sb->ctx->mount_options, full_path + 1, &ref, - &fake_devname); - free_dfs_info_param(&ref); - - if (IS_ERR(mdata)) { - rc = PTR_ERR(mdata); - mdata = NULL; - } else - rc = cifs_setup_volume_info(&tmp_ctx, mdata, fake_devname); - - kfree(mdata); - kfree(fake_devname); - - if (rc) - break; - - cifs_dbg(FYI, "%s: new ctx: UNC=%s prepath=%s\n", __func__, tmp_ctx.UNC, - tmp_ctx.prepath); - - mount_put_conns(cifs_sb, *xid, *server, *ses, *tcon); - rc = mount_get_dfs_conns(&tmp_ctx, cifs_sb, xid, server, ses, tcon); - if (!rc || (*server && *ses)) { - /* - * We were able to connect to new target server. Update current context with - * new target server. - */ - rc = update_vol_info(tgt_it, &tmp_ctx, ctx); - break; - } - } - if (!rc) { - cifs_dbg(FYI, "%s: final ctx: UNC=%s prepath=%s\n", __func__, tmp_ctx.UNC, - tmp_ctx.prepath); + mdata = cifs_compose_mount_options(cifs_sb->ctx->mount_options, full_path + 1, referral, + &fake_devname); + if (IS_ERR(mdata)) { + rc = PTR_ERR(mdata); + mdata = NULL; + } else { /* - * Update DFS target hint in DFS referral cache with the target server we - * successfully reconnected to. + * We can not clear out the whole structure since we no longer have an explicit + * function to parse a mount-string. Instead we need to clear out the individual + * fields that are no longer valid. */ - rc = dfs_cache_update_tgthint(*xid, root_ses ? root_ses : *ses, cifs_sb->local_nls, - cifs_remap(cifs_sb), path, tgt_it); + kfree(ctx->prepath); + ctx->prepath = NULL; + rc = cifs_setup_volume_info(ctx, mdata, fake_devname); } + kfree(fake_devname); + kfree(cifs_sb->ctx->mount_options); + cifs_sb->ctx->mount_options = mdata; -out: - kfree(npath); - smb3_cleanup_fs_context_contents(&tmp_ctx); - dfs_cache_free_tgts(&tgt_list); return rc; } #endif @@ -3333,12 +3289,14 @@ cifs_are_all_path_components_accessible(struct TCP_Server_Info *server, * Check if path is remote (e.g. a DFS share). Return -EREMOTE if it is, * otherwise 0. */ -static int is_path_remote(struct cifs_sb_info *cifs_sb, struct smb3_fs_context *ctx, - const unsigned int xid, - struct TCP_Server_Info *server, - struct cifs_tcon *tcon) +static int is_path_remote(struct mount_ctx *mnt_ctx) { int rc; + struct cifs_sb_info *cifs_sb = mnt_ctx->cifs_sb; + struct TCP_Server_Info *server = mnt_ctx->server; + unsigned int xid = mnt_ctx->xid; + struct cifs_tcon *tcon = mnt_ctx->tcon; + struct smb3_fs_context *ctx = mnt_ctx->fs_ctx; char *full_path; if (!server->ops->is_path_accessible) @@ -3376,111 +3334,26 @@ static int is_path_remote(struct cifs_sb_info *cifs_sb, struct smb3_fs_context * } #ifdef CONFIG_CIFS_DFS_UPCALL -static void set_root_ses(struct cifs_sb_info *cifs_sb, const uuid_t *mount_id, struct cifs_ses *ses, - struct cifs_ses **root_ses) +static void set_root_ses(struct mount_ctx *mnt_ctx) { - if (ses) { + if (mnt_ctx->ses) { spin_lock(&cifs_tcp_ses_lock); - ses->ses_count++; + mnt_ctx->ses->ses_count++; spin_unlock(&cifs_tcp_ses_lock); - dfs_cache_add_refsrv_session(mount_id, ses); + dfs_cache_add_refsrv_session(&mnt_ctx->mount_id, mnt_ctx->ses); } - *root_ses = ses; + mnt_ctx->root_ses = mnt_ctx->ses; } -/* Set up next dfs prefix path in @dfs_path */ -static int next_dfs_prepath(struct cifs_sb_info *cifs_sb, struct smb3_fs_context *ctx, - const unsigned int xid, struct TCP_Server_Info *server, - struct cifs_tcon *tcon, char **dfs_path) -{ - char *path, *npath; - int added_treename = is_tcon_dfs(tcon); - int rc; - - path = cifs_build_path_to_root(ctx, cifs_sb, tcon, added_treename); - if (!path) - return -ENOMEM; - - rc = is_path_remote(cifs_sb, ctx, xid, server, tcon); - if (rc == -EREMOTE) { - struct smb3_fs_context v = {NULL}; - /* if @path contains a tree name, skip it in the prefix path */ - if (added_treename) { - rc = smb3_parse_devname(path, &v); - if (rc) - goto out; - npath = build_unc_path_to_root(&v, cifs_sb, true); - smb3_cleanup_fs_context_contents(&v); - } else { - v.UNC = ctx->UNC; - v.prepath = path + 1; - npath = build_unc_path_to_root(&v, cifs_sb, true); - } - - if (IS_ERR(npath)) { - rc = PTR_ERR(npath); - goto out; - } - - kfree(*dfs_path); - *dfs_path = npath; - rc = -EREMOTE; - } - -out: - kfree(path); - return rc; -} - -/* Check if resolved targets can handle any DFS referrals */ -static int is_referral_server(const char *ref_path, struct cifs_sb_info *cifs_sb, - struct cifs_tcon *tcon, bool *ref_server) +static int is_dfs_mount(struct mount_ctx *mnt_ctx, bool *isdfs, struct dfs_cache_tgt_list *root_tl) { int rc; - struct dfs_info3_param ref = {0}; + struct cifs_sb_info *cifs_sb = mnt_ctx->cifs_sb; + struct smb3_fs_context *ctx = mnt_ctx->fs_ctx; - cifs_dbg(FYI, "%s: ref_path=%s\n", __func__, ref_path); + *isdfs = true; - if (is_tcon_dfs(tcon)) { - *ref_server = true; - } else { - char *npath; - - npath = dfs_cache_canonical_path(ref_path, cifs_sb->local_nls, cifs_remap(cifs_sb)); - if (IS_ERR(npath)) - return PTR_ERR(npath); - - rc = dfs_cache_noreq_find(npath, &ref, NULL); - kfree(npath); - if (rc) { - cifs_dbg(VFS, "%s: dfs_cache_noreq_find: failed (rc=%d)\n", __func__, rc); - return rc; - } - cifs_dbg(FYI, "%s: ref.flags=0x%x\n", __func__, ref.flags); - /* - * Check if all targets are capable of handling DFS referrals as per - * MS-DFSC 2.2.4 RESP_GET_DFS_REFERRAL. - */ - *ref_server = !!(ref.flags & DFSREF_REFERRAL_SERVER); - free_dfs_info_param(&ref); - } - return 0; -} - -int cifs_mount(struct cifs_sb_info *cifs_sb, struct smb3_fs_context *ctx) -{ - int rc = 0; - unsigned int xid; - struct TCP_Server_Info *server = NULL; - struct cifs_ses *ses = NULL, *root_ses = NULL; - struct cifs_tcon *tcon = NULL; - int count = 0; - uuid_t mount_id = {0}; - char *ref_path = NULL, *full_path = NULL; - char *oldmnt = NULL; - bool ref_server = false; - - rc = mount_get_conns(ctx, cifs_sb, &xid, &server, &ses, &tcon); + rc = mount_get_conns(mnt_ctx); /* * If called with 'nodfs' mount option, then skip DFS resolving. Otherwise unconditionally * try to get an DFS referral (even cached) to determine whether it is an DFS mount. @@ -3489,167 +3362,261 @@ int cifs_mount(struct cifs_sb_info *cifs_sb, struct smb3_fs_context *ctx) * to respond with PATH_NOT_COVERED to requests that include the prefix. */ if ((cifs_sb->mnt_cifs_flags & CIFS_MOUNT_NO_DFS) || - dfs_cache_find(xid, ses, cifs_sb->local_nls, cifs_remap(cifs_sb), ctx->UNC + 1, NULL, - NULL)) { + dfs_cache_find(mnt_ctx->xid, mnt_ctx->ses, cifs_sb->local_nls, cifs_remap(cifs_sb), + ctx->UNC + 1, NULL, root_tl)) { if (rc) - goto error; + return rc; /* Check if it is fully accessible and then mount it */ - rc = is_path_remote(cifs_sb, ctx, xid, server, tcon); + rc = is_path_remote(mnt_ctx); if (!rc) - goto out; - if (rc != -EREMOTE) - goto error; + *isdfs = false; + else if (rc != -EREMOTE) + return rc; + } + return 0; +} + +static int connect_dfs_target(struct mount_ctx *mnt_ctx, const char *full_path, + const char *ref_path, struct dfs_cache_tgt_iterator *tit) +{ + int rc; + struct dfs_info3_param ref = {}; + struct cifs_sb_info *cifs_sb = mnt_ctx->cifs_sb; + char *oldmnt = cifs_sb->ctx->mount_options; + + rc = dfs_cache_get_tgt_referral(ref_path, tit, &ref); + if (rc) + goto out; + + rc = expand_dfs_referral(mnt_ctx, full_path, &ref); + if (rc) + goto out; + + /* Connect to new target only if we were redirected (e.g. mount options changed) */ + if (oldmnt != cifs_sb->ctx->mount_options) { + mount_put_conns(mnt_ctx); + rc = mount_get_dfs_conns(mnt_ctx); + } + if (!rc) { + if (cifs_is_referral_server(mnt_ctx->tcon, &ref)) + set_root_ses(mnt_ctx); + rc = dfs_cache_update_tgthint(mnt_ctx->xid, mnt_ctx->root_ses, cifs_sb->local_nls, + cifs_remap(cifs_sb), ref_path, tit); } - mount_put_conns(cifs_sb, xid, server, ses, tcon); - /* - * Ignore error check here because we may failover to other targets from cached a - * referral. - */ - (void)mount_get_dfs_conns(ctx, cifs_sb, &xid, &server, &ses, &tcon); +out: + free_dfs_info_param(&ref); + return rc; +} - /* Get path of DFS root */ - ref_path = build_unc_path_to_root(ctx, cifs_sb, false); - if (IS_ERR(ref_path)) { - rc = PTR_ERR(ref_path); - ref_path = NULL; - goto error; +static int connect_dfs_root(struct mount_ctx *mnt_ctx, struct dfs_cache_tgt_list *root_tl) +{ + int rc; + char *full_path; + struct cifs_sb_info *cifs_sb = mnt_ctx->cifs_sb; + struct smb3_fs_context *ctx = mnt_ctx->fs_ctx; + struct dfs_cache_tgt_iterator *tit; + + /* Put initial connections as they might be shared with other mounts. We need unique dfs + * connections per mount to properly failover, so mount_get_dfs_conns() must be used from + * now on. + */ + mount_put_conns(mnt_ctx); + mount_get_dfs_conns(mnt_ctx); + + full_path = build_unc_path_to_root(ctx, cifs_sb, true); + if (IS_ERR(full_path)) + return PTR_ERR(full_path); + + mnt_ctx->origin_fullpath = dfs_cache_canonical_path(ctx->UNC, cifs_sb->local_nls, + cifs_remap(cifs_sb)); + if (IS_ERR(mnt_ctx->origin_fullpath)) { + rc = PTR_ERR(mnt_ctx->origin_fullpath); + mnt_ctx->origin_fullpath = NULL; + goto out; } - uuid_gen(&mount_id); - set_root_ses(cifs_sb, &mount_id, ses, &root_ses); - do { - /* Save full path of last DFS path we used to resolve final target server */ - kfree(full_path); - full_path = build_unc_path_to_root(ctx, cifs_sb, !!count); - if (IS_ERR(full_path)) { - rc = PTR_ERR(full_path); - full_path = NULL; + /* Try all dfs root targets */ + for (rc = -ENOENT, tit = dfs_cache_get_tgt_iterator(root_tl); + tit; tit = dfs_cache_get_next_tgt(root_tl, tit)) { + rc = connect_dfs_target(mnt_ctx, full_path, mnt_ctx->origin_fullpath + 1, tit); + if (!rc) { + mnt_ctx->leaf_fullpath = kstrdup(mnt_ctx->origin_fullpath, GFP_KERNEL); + if (!mnt_ctx->leaf_fullpath) + rc = -ENOMEM; break; } - /* Chase referral */ - oldmnt = cifs_sb->ctx->mount_options; - rc = expand_dfs_referral(xid, root_ses, ctx, cifs_sb, ref_path + 1); - if (rc) - break; - /* Connect to new DFS target only if we were redirected */ - if (oldmnt != cifs_sb->ctx->mount_options) { - mount_put_conns(cifs_sb, xid, server, ses, tcon); - rc = mount_get_dfs_conns(ctx, cifs_sb, &xid, &server, &ses, &tcon); - } - if (rc && !server && !ses) { - /* Failed to connect. Try to connect to other targets in the referral. */ - rc = do_dfs_failover(ref_path + 1, full_path, cifs_sb, ctx, root_ses, &xid, - &server, &ses, &tcon); - } - if (rc == -EACCES || rc == -EOPNOTSUPP || !server || !ses) - break; - if (!tcon) - continue; + } - /* Make sure that requests go through new root servers */ - rc = is_referral_server(ref_path + 1, cifs_sb, tcon, &ref_server); - if (rc) - break; - if (ref_server) - set_root_ses(cifs_sb, &mount_id, ses, &root_ses); - - /* Get next dfs path and then continue chasing them if -EREMOTE */ - rc = next_dfs_prepath(cifs_sb, ctx, xid, server, tcon, &ref_path); - /* Prevent recursion on broken link referrals */ - if (rc == -EREMOTE && ++count > MAX_NESTED_LINKS) - rc = -ELOOP; - } while (rc == -EREMOTE); - - if (rc || !tcon || !ses) - goto error; - - kfree(ref_path); - /* - * Store DFS full path in both superblock and tree connect structures. - * - * For DFS root mounts, the prefix path (cifs_sb->prepath) is preserved during reconnect so - * only the root path is set in cifs_sb->origin_fullpath and tcon->dfs_path. And for DFS - * links, the prefix path is included in both and may be changed during reconnect. See - * cifs_tree_connect(). - */ - ref_path = dfs_cache_canonical_path(full_path, cifs_sb->local_nls, cifs_remap(cifs_sb)); +out: kfree(full_path); - full_path = NULL; + return rc; +} - if (IS_ERR(ref_path)) { - rc = PTR_ERR(ref_path); - ref_path = NULL; - goto error; +static int __follow_dfs_link(struct mount_ctx *mnt_ctx) +{ + int rc; + struct cifs_sb_info *cifs_sb = mnt_ctx->cifs_sb; + struct smb3_fs_context *ctx = mnt_ctx->fs_ctx; + char *full_path; + struct dfs_cache_tgt_list tl = DFS_CACHE_TGT_LIST_INIT(tl); + struct dfs_cache_tgt_iterator *tit; + + full_path = build_unc_path_to_root(ctx, cifs_sb, true); + if (IS_ERR(full_path)) + return PTR_ERR(full_path); + + kfree(mnt_ctx->leaf_fullpath); + mnt_ctx->leaf_fullpath = dfs_cache_canonical_path(full_path, cifs_sb->local_nls, + cifs_remap(cifs_sb)); + if (IS_ERR(mnt_ctx->leaf_fullpath)) { + rc = PTR_ERR(mnt_ctx->leaf_fullpath); + mnt_ctx->leaf_fullpath = NULL; + goto out; } - cifs_sb->origin_fullpath = ref_path; - ref_path = kstrdup(cifs_sb->origin_fullpath, GFP_KERNEL); - if (!ref_path) { - rc = -ENOMEM; - goto error; + /* Get referral from dfs link */ + rc = dfs_cache_find(mnt_ctx->xid, mnt_ctx->root_ses, cifs_sb->local_nls, + cifs_remap(cifs_sb), mnt_ctx->leaf_fullpath + 1, NULL, &tl); + if (rc) + goto out; + + /* Try all dfs link targets */ + for (rc = -ENOENT, tit = dfs_cache_get_tgt_iterator(&tl); + tit; tit = dfs_cache_get_next_tgt(&tl, tit)) { + rc = connect_dfs_target(mnt_ctx, full_path, mnt_ctx->leaf_fullpath + 1, tit); + if (!rc) { + rc = is_path_remote(mnt_ctx); + break; + } } - spin_lock(&cifs_tcp_ses_lock); - tcon->dfs_path = ref_path; - ref_path = NULL; - spin_unlock(&cifs_tcp_ses_lock); +out: + kfree(full_path); + dfs_cache_free_tgts(&tl); + return rc; +} + +static int follow_dfs_link(struct mount_ctx *mnt_ctx) +{ + int rc; + struct cifs_sb_info *cifs_sb = mnt_ctx->cifs_sb; + struct smb3_fs_context *ctx = mnt_ctx->fs_ctx; + char *full_path; + int num_links = 0; + + full_path = build_unc_path_to_root(ctx, cifs_sb, true); + if (IS_ERR(full_path)) + return PTR_ERR(full_path); + + kfree(mnt_ctx->origin_fullpath); + mnt_ctx->origin_fullpath = dfs_cache_canonical_path(full_path, cifs_sb->local_nls, + cifs_remap(cifs_sb)); + kfree(full_path); + + if (IS_ERR(mnt_ctx->origin_fullpath)) { + rc = PTR_ERR(mnt_ctx->origin_fullpath); + mnt_ctx->origin_fullpath = NULL; + return rc; + } + + do { + rc = __follow_dfs_link(mnt_ctx); + if (!rc || rc != -EREMOTE) + break; + } while (rc = -ELOOP, ++num_links < MAX_NESTED_LINKS); + + return rc; +} + +/* Set up DFS referral paths for failover */ +static void setup_server_referral_paths(struct mount_ctx *mnt_ctx) +{ + struct TCP_Server_Info *server = mnt_ctx->server; + + server->origin_fullpath = mnt_ctx->origin_fullpath; + server->leaf_fullpath = mnt_ctx->leaf_fullpath; + server->current_fullpath = mnt_ctx->leaf_fullpath; + mnt_ctx->origin_fullpath = mnt_ctx->leaf_fullpath = NULL; +} + +int cifs_mount(struct cifs_sb_info *cifs_sb, struct smb3_fs_context *ctx) +{ + int rc; + struct mount_ctx mnt_ctx = { .cifs_sb = cifs_sb, .fs_ctx = ctx, }; + struct dfs_cache_tgt_list tl = DFS_CACHE_TGT_LIST_INIT(tl); + bool isdfs; + + rc = is_dfs_mount(&mnt_ctx, &isdfs, &tl); + if (rc) + goto error; + if (!isdfs) + goto out; + + uuid_gen(&mnt_ctx.mount_id); + rc = connect_dfs_root(&mnt_ctx, &tl); + dfs_cache_free_tgts(&tl); + + if (rc) + goto error; + + rc = is_path_remote(&mnt_ctx); + if (rc == -EREMOTE) + rc = follow_dfs_link(&mnt_ctx); + if (rc) + goto error; + + setup_server_referral_paths(&mnt_ctx); /* - * After reconnecting to a different server, unique ids won't - * match anymore, so we disable serverino. This prevents - * dentry revalidation to think the dentry are stale (ESTALE). + * After reconnecting to a different server, unique ids won't match anymore, so we disable + * serverino. This prevents dentry revalidation to think the dentry are stale (ESTALE). */ cifs_autodisable_serverino(cifs_sb); /* - * Force the use of prefix path to support failover on DFS paths that - * resolve to targets that have different prefix paths. + * Force the use of prefix path to support failover on DFS paths that resolve to targets + * that have different prefix paths. */ cifs_sb->mnt_cifs_flags |= CIFS_MOUNT_USE_PREFIX_PATH; kfree(cifs_sb->prepath); cifs_sb->prepath = ctx->prepath; ctx->prepath = NULL; - uuid_copy(&cifs_sb->dfs_mount_id, &mount_id); + uuid_copy(&cifs_sb->dfs_mount_id, &mnt_ctx.mount_id); out: - free_xid(xid); - cifs_try_adding_channels(cifs_sb, ses); - return mount_setup_tlink(cifs_sb, ses, tcon); + free_xid(mnt_ctx.xid); + cifs_try_adding_channels(cifs_sb, mnt_ctx.ses); + return mount_setup_tlink(cifs_sb, mnt_ctx.ses, mnt_ctx.tcon); error: - kfree(ref_path); - kfree(full_path); - kfree(cifs_sb->origin_fullpath); - dfs_cache_put_refsrv_sessions(&mount_id); - mount_put_conns(cifs_sb, xid, server, ses, tcon); + dfs_cache_put_refsrv_sessions(&mnt_ctx.mount_id); + kfree(mnt_ctx.origin_fullpath); + kfree(mnt_ctx.leaf_fullpath); + mount_put_conns(&mnt_ctx); return rc; } #else int cifs_mount(struct cifs_sb_info *cifs_sb, struct smb3_fs_context *ctx) { int rc = 0; - unsigned int xid; - struct cifs_ses *ses; - struct cifs_tcon *tcon; - struct TCP_Server_Info *server; + struct mount_ctx mnt_ctx = { .cifs_sb = cifs_sb, .fs_ctx = ctx, }; - rc = mount_get_conns(ctx, cifs_sb, &xid, &server, &ses, &tcon); + rc = mount_get_conns(&mnt_ctx); if (rc) goto error; - if (tcon) { - rc = is_path_remote(cifs_sb, ctx, xid, server, tcon); + if (mnt_ctx.tcon) { + rc = is_path_remote(&mnt_ctx); if (rc == -EREMOTE) rc = -EOPNOTSUPP; if (rc) goto error; } - free_xid(xid); - - return mount_setup_tlink(cifs_sb, ses, tcon); + free_xid(mnt_ctx.xid); + return mount_setup_tlink(cifs_sb, mnt_ctx.ses, mnt_ctx.tcon); error: - mount_put_conns(cifs_sb, xid, server, ses, tcon); + mount_put_conns(&mnt_ctx); return rc; } #endif @@ -3818,7 +3785,6 @@ cifs_umount(struct cifs_sb_info *cifs_sb) kfree(cifs_sb->prepath); #ifdef CONFIG_CIFS_DFS_UPCALL dfs_cache_put_refsrv_sessions(&cifs_sb->dfs_mount_id); - kfree(cifs_sb->origin_fullpath); #endif call_rcu(&cifs_sb->rcu, delayed_free); } @@ -4145,104 +4111,246 @@ cifs_prune_tlinks(struct work_struct *work) } #ifdef CONFIG_CIFS_DFS_UPCALL +static void mark_tcon_tcp_ses_for_reconnect(struct cifs_tcon *tcon) +{ + int i; + + for (i = 0; i < tcon->ses->chan_count; i++) { + spin_lock(&GlobalMid_Lock); + if (tcon->ses->chans[i].server->tcpStatus != CifsExiting) + tcon->ses->chans[i].server->tcpStatus = CifsNeedReconnect; + spin_unlock(&GlobalMid_Lock); + } +} + +/* Update dfs referral path of superblock */ +static int update_server_fullpath(struct TCP_Server_Info *server, struct cifs_sb_info *cifs_sb, + const char *target) +{ + int rc = 0; + size_t len = strlen(target); + char *refpath, *npath; + + if (unlikely(len < 2 || *target != '\\')) + return -EINVAL; + + if (target[1] == '\\') { + len += 1; + refpath = kmalloc(len, GFP_KERNEL); + if (!refpath) + return -ENOMEM; + + scnprintf(refpath, len, "%s", target); + } else { + len += sizeof("\\"); + refpath = kmalloc(len, GFP_KERNEL); + if (!refpath) + return -ENOMEM; + + scnprintf(refpath, len, "\\%s", target); + } + + npath = dfs_cache_canonical_path(refpath, cifs_sb->local_nls, cifs_remap(cifs_sb)); + kfree(refpath); + + if (IS_ERR(npath)) { + rc = PTR_ERR(npath); + } else { + mutex_lock(&server->refpath_lock); + kfree(server->leaf_fullpath); + server->leaf_fullpath = npath; + mutex_unlock(&server->refpath_lock); + server->current_fullpath = server->leaf_fullpath; + } + return rc; +} + +static int target_share_matches_server(struct TCP_Server_Info *server, const char *tcp_host, + size_t tcp_host_len, char *share, bool *target_match) +{ + int rc = 0; + const char *dfs_host; + size_t dfs_host_len; + + *target_match = true; + extract_unc_hostname(share, &dfs_host, &dfs_host_len); + + /* Check if hostnames or addresses match */ + if (dfs_host_len != tcp_host_len || strncasecmp(dfs_host, tcp_host, dfs_host_len) != 0) { + cifs_dbg(FYI, "%s: %.*s doesn't match %.*s\n", __func__, (int)dfs_host_len, + dfs_host, (int)tcp_host_len, tcp_host); + rc = match_target_ip(server, dfs_host, dfs_host_len, target_match); + if (rc) + cifs_dbg(VFS, "%s: failed to match target ip: %d\n", __func__, rc); + } + return rc; +} + +static int __tree_connect_dfs_target(const unsigned int xid, struct cifs_tcon *tcon, + struct cifs_sb_info *cifs_sb, char *tree, bool islink, + struct dfs_cache_tgt_list *tl) +{ + int rc; + struct TCP_Server_Info *server = tcon->ses->server; + const struct smb_version_operations *ops = server->ops; + struct cifs_tcon *ipc = tcon->ses->tcon_ipc; + char *share = NULL, *prefix = NULL; + const char *tcp_host; + size_t tcp_host_len; + struct dfs_cache_tgt_iterator *tit; + bool target_match; + + extract_unc_hostname(server->hostname, &tcp_host, &tcp_host_len); + + tit = dfs_cache_get_tgt_iterator(tl); + if (!tit) { + rc = -ENOENT; + goto out; + } + + /* Try to tree connect to all dfs targets */ + for (; tit; tit = dfs_cache_get_next_tgt(tl, tit)) { + const char *target = dfs_cache_get_tgt_name(tit); + struct dfs_cache_tgt_list ntl = DFS_CACHE_TGT_LIST_INIT(ntl); + + kfree(share); + kfree(prefix); + share = prefix = NULL; + + /* Check if share matches with tcp ses */ + rc = dfs_cache_get_tgt_share(server->current_fullpath + 1, tit, &share, &prefix); + if (rc) { + cifs_dbg(VFS, "%s: failed to parse target share: %d\n", __func__, rc); + break; + } + + rc = target_share_matches_server(server, tcp_host, tcp_host_len, share, + &target_match); + if (rc) + break; + if (!target_match) { + rc = -EHOSTUNREACH; + continue; + } + + if (ipc->need_reconnect) { + scnprintf(tree, MAX_TREE_SIZE, "\\\\%s\\IPC$", server->hostname); + rc = ops->tree_connect(xid, ipc->ses, tree, ipc, cifs_sb->local_nls); + if (rc) + break; + } + + scnprintf(tree, MAX_TREE_SIZE, "\\%s", share); + if (!islink) { + rc = ops->tree_connect(xid, tcon->ses, tree, tcon, cifs_sb->local_nls); + break; + } + /* + * If no dfs referrals were returned from link target, then just do a TREE_CONNECT + * to it. Otherwise, cache the dfs referral and then mark current tcp ses for + * reconnect so either the demultiplex thread or the echo worker will reconnect to + * newly resolved target. + */ + if (dfs_cache_find(xid, tcon->ses, cifs_sb->local_nls, cifs_remap(cifs_sb), target, + NULL, &ntl)) { + rc = ops->tree_connect(xid, tcon->ses, tree, tcon, cifs_sb->local_nls); + if (rc) + continue; + rc = dfs_cache_noreq_update_tgthint(server->current_fullpath + 1, tit); + if (!rc) + rc = cifs_update_super_prepath(cifs_sb, prefix); + } else { + /* Target is another dfs share */ + rc = update_server_fullpath(server, cifs_sb, target); + dfs_cache_free_tgts(tl); + + if (!rc) { + rc = -EREMOTE; + list_replace_init(&ntl.tl_list, &tl->tl_list); + } else + dfs_cache_free_tgts(&ntl); + } + break; + } + +out: + kfree(share); + kfree(prefix); + + return rc; +} + +static int tree_connect_dfs_target(const unsigned int xid, struct cifs_tcon *tcon, + struct cifs_sb_info *cifs_sb, char *tree, bool islink, + struct dfs_cache_tgt_list *tl) +{ + int rc; + int num_links = 0; + struct TCP_Server_Info *server = tcon->ses->server; + + do { + rc = __tree_connect_dfs_target(xid, tcon, cifs_sb, tree, islink, tl); + if (!rc || rc != -EREMOTE) + break; + } while (rc = -ELOOP, ++num_links < MAX_NESTED_LINKS); + /* + * If we couldn't tree connect to any targets from last referral path, then retry from + * original referral path. + */ + if (rc && server->current_fullpath != server->origin_fullpath) { + server->current_fullpath = server->origin_fullpath; + mark_tcon_tcp_ses_for_reconnect(tcon); + } + + dfs_cache_free_tgts(tl); + return rc; +} + int cifs_tree_connect(const unsigned int xid, struct cifs_tcon *tcon, const struct nls_table *nlsc) { int rc; struct TCP_Server_Info *server = tcon->ses->server; const struct smb_version_operations *ops = server->ops; - struct dfs_cache_tgt_list tl; - struct dfs_cache_tgt_iterator *it = NULL; + struct super_block *sb = NULL; + struct cifs_sb_info *cifs_sb; + struct dfs_cache_tgt_list tl = DFS_CACHE_TGT_LIST_INIT(tl); char *tree; - const char *tcp_host; - size_t tcp_host_len; - const char *dfs_host; - size_t dfs_host_len; - char *share = NULL, *prefix = NULL; struct dfs_info3_param ref = {0}; - bool isroot; tree = kzalloc(MAX_TREE_SIZE, GFP_KERNEL); if (!tree) return -ENOMEM; - /* If it is not dfs or there was no cached dfs referral, then reconnect to same share */ - if (!tcon->dfs_path || dfs_cache_noreq_find(tcon->dfs_path + 1, &ref, &tl)) { - if (tcon->ipc) { - scnprintf(tree, MAX_TREE_SIZE, "\\\\%s\\IPC$", server->hostname); - rc = ops->tree_connect(xid, tcon->ses, tree, tcon, nlsc); - } else { - rc = ops->tree_connect(xid, tcon->ses, tcon->treeName, tcon, nlsc); - } + if (tcon->ipc) { + scnprintf(tree, MAX_TREE_SIZE, "\\\\%s\\IPC$", server->hostname); + rc = ops->tree_connect(xid, tcon->ses, tree, tcon, nlsc); goto out; } - isroot = ref.server_type == DFS_TYPE_ROOT; + sb = cifs_get_tcp_super(server); + if (IS_ERR(sb)) { + rc = PTR_ERR(sb); + cifs_dbg(VFS, "%s: could not find superblock: %d\n", __func__, rc); + goto out; + } + + cifs_sb = CIFS_SB(sb); + + /* If it is not dfs or there was no cached dfs referral, then reconnect to same share */ + if (!server->current_fullpath || + dfs_cache_noreq_find(server->current_fullpath + 1, &ref, &tl)) { + rc = ops->tree_connect(xid, tcon->ses, tcon->treeName, tcon, cifs_sb->local_nls); + goto out; + } + + rc = tree_connect_dfs_target(xid, tcon, cifs_sb, tree, ref.server_type == DFS_TYPE_LINK, + &tl); free_dfs_info_param(&ref); - extract_unc_hostname(server->hostname, &tcp_host, &tcp_host_len); - - for (it = dfs_cache_get_tgt_iterator(&tl); it; it = dfs_cache_get_next_tgt(&tl, it)) { - bool target_match; - - kfree(share); - kfree(prefix); - share = NULL; - prefix = NULL; - - rc = dfs_cache_get_tgt_share(tcon->dfs_path + 1, it, &share, &prefix); - if (rc) { - cifs_dbg(VFS, "%s: failed to parse target share %d\n", - __func__, rc); - continue; - } - - extract_unc_hostname(share, &dfs_host, &dfs_host_len); - - if (dfs_host_len != tcp_host_len - || strncasecmp(dfs_host, tcp_host, dfs_host_len) != 0) { - cifs_dbg(FYI, "%s: %.*s doesn't match %.*s\n", __func__, (int)dfs_host_len, - dfs_host, (int)tcp_host_len, tcp_host); - - rc = match_target_ip(server, dfs_host, dfs_host_len, &target_match); - if (rc) { - cifs_dbg(VFS, "%s: failed to match target ip: %d\n", __func__, rc); - break; - } - - if (!target_match) { - cifs_dbg(FYI, "%s: skipping target\n", __func__); - continue; - } - } - - if (tcon->ipc) { - scnprintf(tree, MAX_TREE_SIZE, "\\\\%s\\IPC$", share); - rc = ops->tree_connect(xid, tcon->ses, tree, tcon, nlsc); - } else { - scnprintf(tree, MAX_TREE_SIZE, "\\%s", share); - rc = ops->tree_connect(xid, tcon->ses, tree, tcon, nlsc); - /* Only handle prefix paths of DFS link targets */ - if (!rc && !isroot) { - rc = update_super_prepath(tcon, prefix); - break; - } - } - if (rc == -EREMOTE) - break; - } - - kfree(share); - kfree(prefix); - - if (!rc) { - if (it) - rc = dfs_cache_noreq_update_tgthint(tcon->dfs_path + 1, it); - else - rc = -ENOENT; - } - dfs_cache_free_tgts(&tl); out: kfree(tree); + cifs_put_tcp_super(sb); + return rc; } #else diff --git a/fs/cifs/dfs_cache.c b/fs/cifs/dfs_cache.c index 283745592844..5c1259d2eeac 100644 --- a/fs/cifs/dfs_cache.c +++ b/fs/cifs/dfs_cache.c @@ -283,7 +283,7 @@ static int dfscache_proc_show(struct seq_file *m, void *v) seq_printf(m, "cache entry: path=%s,type=%s,ttl=%d,etime=%ld,hdr_flags=0x%x,ref_flags=0x%x,interlink=%s,path_consumed=%d,expired=%s\n", ce->path, ce->srvtype == DFS_TYPE_ROOT ? "root" : "link", - ce->ttl, ce->etime.tv_nsec, ce->ref_flags, ce->hdr_flags, + ce->ttl, ce->etime.tv_nsec, ce->hdr_flags, ce->ref_flags, IS_DFS_INTERLINK(ce->hdr_flags) ? "yes" : "no", ce->path_consumed, cache_entry_expired(ce) ? "yes" : "no"); @@ -1364,9 +1364,9 @@ static void mark_for_reconnect_if_needed(struct cifs_tcon *tcon, struct dfs_cach } /* Refresh dfs referral of tcon and mark it for reconnect if needed */ -static int refresh_tcon(struct cifs_ses **sessions, struct cifs_tcon *tcon, bool force_refresh) +static int __refresh_tcon(const char *path, struct cifs_ses **sessions, struct cifs_tcon *tcon, + bool force_refresh) { - const char *path = tcon->dfs_path + 1; struct cifs_ses *ses; struct cache_entry *ce; struct dfs_info3_param *refs = NULL; @@ -1422,6 +1422,20 @@ out: return rc; } +static int refresh_tcon(struct cifs_ses **sessions, struct cifs_tcon *tcon, bool force_refresh) +{ + struct TCP_Server_Info *server = tcon->ses->server; + + mutex_lock(&server->refpath_lock); + if (strcasecmp(server->leaf_fullpath, server->origin_fullpath)) + __refresh_tcon(server->leaf_fullpath + 1, sessions, tcon, force_refresh); + mutex_unlock(&server->refpath_lock); + + __refresh_tcon(server->origin_fullpath + 1, sessions, tcon, force_refresh); + + return 0; +} + /** * dfs_cache_remount_fs - remount a DFS share * @@ -1435,6 +1449,7 @@ out: int dfs_cache_remount_fs(struct cifs_sb_info *cifs_sb) { struct cifs_tcon *tcon; + struct TCP_Server_Info *server; struct mount_group *mg; struct cifs_ses *sessions[CACHE_MAX_ENTRIES + 1] = {NULL}; int rc; @@ -1443,13 +1458,15 @@ int dfs_cache_remount_fs(struct cifs_sb_info *cifs_sb) return -EINVAL; tcon = cifs_sb_master_tcon(cifs_sb); - if (!tcon->dfs_path) { - cifs_dbg(FYI, "%s: not a dfs tcon\n", __func__); + server = tcon->ses->server; + + if (!server->origin_fullpath) { + cifs_dbg(FYI, "%s: not a dfs mount\n", __func__); return 0; } if (uuid_is_null(&cifs_sb->dfs_mount_id)) { - cifs_dbg(FYI, "%s: tcon has no dfs mount group id\n", __func__); + cifs_dbg(FYI, "%s: no dfs mount group id\n", __func__); return -EINVAL; } @@ -1457,7 +1474,7 @@ int dfs_cache_remount_fs(struct cifs_sb_info *cifs_sb) mg = find_mount_group_locked(&cifs_sb->dfs_mount_id); if (IS_ERR(mg)) { mutex_unlock(&mount_group_list_lock); - cifs_dbg(FYI, "%s: tcon has ipc session to refresh referral\n", __func__); + cifs_dbg(FYI, "%s: no ipc session for refreshing referral\n", __func__); return PTR_ERR(mg); } kref_get(&mg->refcount); @@ -1498,9 +1515,12 @@ static void refresh_mounts(struct cifs_ses **sessions) spin_lock(&cifs_tcp_ses_lock); list_for_each_entry(server, &cifs_tcp_ses_list, tcp_ses_list) { + if (!server->is_dfs_conn) + continue; + list_for_each_entry(ses, &server->smb_ses_list, smb_ses_list) { list_for_each_entry(tcon, &ses->tcon_list, tcon_list) { - if (tcon->dfs_path) { + if (!tcon->ipc && !tcon->need_reconnect) { tcon->tc_count++; list_add_tail(&tcon->ulist, &tcons); } @@ -1510,8 +1530,16 @@ static void refresh_mounts(struct cifs_ses **sessions) spin_unlock(&cifs_tcp_ses_lock); list_for_each_entry_safe(tcon, ntcon, &tcons, ulist) { + struct TCP_Server_Info *server = tcon->ses->server; + list_del_init(&tcon->ulist); - refresh_tcon(sessions, tcon, false); + + mutex_lock(&server->refpath_lock); + if (strcasecmp(server->leaf_fullpath, server->origin_fullpath)) + __refresh_tcon(server->leaf_fullpath + 1, sessions, tcon, false); + mutex_unlock(&server->refpath_lock); + + __refresh_tcon(server->origin_fullpath + 1, sessions, tcon, false); cifs_put_tcon(tcon); } } diff --git a/fs/cifs/file.c b/fs/cifs/file.c index 1b855fcb179e..9fee3af83a73 100644 --- a/fs/cifs/file.c +++ b/fs/cifs/file.c @@ -2692,12 +2692,23 @@ int cifs_strict_fsync(struct file *file, loff_t start, loff_t end, tcon = tlink_tcon(smbfile->tlink); if (!(cifs_sb->mnt_cifs_flags & CIFS_MOUNT_NOSSYNC)) { server = tcon->ses->server; - if (server->ops->flush) - rc = server->ops->flush(xid, tcon, &smbfile->fid); - else + if (server->ops->flush == NULL) { rc = -ENOSYS; + goto strict_fsync_exit; + } + + if ((OPEN_FMODE(smbfile->f_flags) & FMODE_WRITE) == 0) { + smbfile = find_writable_file(CIFS_I(inode), FIND_WR_ANY); + if (smbfile) { + rc = server->ops->flush(xid, tcon, &smbfile->fid); + cifsFileInfo_put(smbfile); + } else + cifs_dbg(FYI, "ignore fsync for file not open for write\n"); + } else + rc = server->ops->flush(xid, tcon, &smbfile->fid); } +strict_fsync_exit: free_xid(xid); return rc; } @@ -2709,6 +2720,7 @@ int cifs_fsync(struct file *file, loff_t start, loff_t end, int datasync) struct cifs_tcon *tcon; struct TCP_Server_Info *server; struct cifsFileInfo *smbfile = file->private_data; + struct inode *inode = file_inode(file); struct cifs_sb_info *cifs_sb = CIFS_FILE_SB(file); rc = file_write_and_wait_range(file, start, end); @@ -2725,12 +2737,23 @@ int cifs_fsync(struct file *file, loff_t start, loff_t end, int datasync) tcon = tlink_tcon(smbfile->tlink); if (!(cifs_sb->mnt_cifs_flags & CIFS_MOUNT_NOSSYNC)) { server = tcon->ses->server; - if (server->ops->flush) - rc = server->ops->flush(xid, tcon, &smbfile->fid); - else + if (server->ops->flush == NULL) { rc = -ENOSYS; + goto fsync_exit; + } + + if ((OPEN_FMODE(smbfile->f_flags) & FMODE_WRITE) == 0) { + smbfile = find_writable_file(CIFS_I(inode), FIND_WR_ANY); + if (smbfile) { + rc = server->ops->flush(xid, tcon, &smbfile->fid); + cifsFileInfo_put(smbfile); + } else + cifs_dbg(FYI, "ignore fsync for file not open for write\n"); + } else + rc = server->ops->flush(xid, tcon, &smbfile->fid); } +fsync_exit: free_xid(xid); return rc; } diff --git a/fs/cifs/fs_context.c b/fs/cifs/fs_context.c index 38d96a480745..6a179ae753c1 100644 --- a/fs/cifs/fs_context.c +++ b/fs/cifs/fs_context.c @@ -308,7 +308,9 @@ smb3_fs_context_dup(struct smb3_fs_context *new_ctx, struct smb3_fs_context *ctx new_ctx->nodename = NULL; new_ctx->username = NULL; new_ctx->password = NULL; + new_ctx->server_hostname = NULL; new_ctx->domainname = NULL; + new_ctx->workstation_name = NULL; new_ctx->UNC = NULL; new_ctx->source = NULL; new_ctx->iocharset = NULL; @@ -323,6 +325,7 @@ smb3_fs_context_dup(struct smb3_fs_context *new_ctx, struct smb3_fs_context *ctx DUP_CTX_STR(UNC); DUP_CTX_STR(source); DUP_CTX_STR(domainname); + DUP_CTX_STR(workstation_name); DUP_CTX_STR(nodename); DUP_CTX_STR(iocharset); @@ -459,6 +462,7 @@ smb3_parse_devname(const char *devname, struct smb3_fs_context *ctx) return -EINVAL; /* record the server hostname */ + kfree(ctx->server_hostname); ctx->server_hostname = kstrndup(devname + 2, pos - devname - 2, GFP_KERNEL); if (!ctx->server_hostname) return -ENOMEM; @@ -720,6 +724,11 @@ static int smb3_verify_reconfigure_ctx(struct fs_context *fc, cifs_errorf(fc, "can not change domainname during remount\n"); return -EINVAL; } + if (new_ctx->workstation_name && + (!old_ctx->workstation_name || strcmp(new_ctx->workstation_name, old_ctx->workstation_name))) { + cifs_errorf(fc, "can not change workstation_name during remount\n"); + return -EINVAL; + } if (new_ctx->nodename && (!old_ctx->nodename || strcmp(new_ctx->nodename, old_ctx->nodename))) { cifs_errorf(fc, "can not change nodename during remount\n"); @@ -753,7 +762,8 @@ static int smb3_reconfigure(struct fs_context *fc) return rc; /* - * We can not change UNC/username/password/domainname/nodename/iocharset + * We can not change UNC/username/password/domainname/ + * workstation_name/nodename/iocharset * during reconnect so ignore what we have in the new context and * just use what we already have in cifs_sb->ctx. */ @@ -762,6 +772,7 @@ static int smb3_reconfigure(struct fs_context *fc) STEAL_STRING(cifs_sb, ctx, username); STEAL_STRING(cifs_sb, ctx, password); STEAL_STRING(cifs_sb, ctx, domainname); + STEAL_STRING(cifs_sb, ctx, workstation_name); STEAL_STRING(cifs_sb, ctx, nodename); STEAL_STRING(cifs_sb, ctx, iocharset); @@ -1414,13 +1425,22 @@ static int smb3_fs_context_parse_param(struct fs_context *fc, int smb3_init_fs_context(struct fs_context *fc) { + int rc; struct smb3_fs_context *ctx; char *nodename = utsname()->nodename; int i; ctx = kzalloc(sizeof(struct smb3_fs_context), GFP_KERNEL); - if (unlikely(!ctx)) - return -ENOMEM; + if (unlikely(!ctx)) { + rc = -ENOMEM; + goto err_exit; + } + + ctx->workstation_name = kstrdup(nodename, GFP_KERNEL); + if (unlikely(!ctx->workstation_name)) { + rc = -ENOMEM; + goto err_exit; + } /* * does not have to be perfect mapping since field is @@ -1493,6 +1513,14 @@ int smb3_init_fs_context(struct fs_context *fc) fc->fs_private = ctx; fc->ops = &smb3_fs_context_ops; return 0; + +err_exit: + if (ctx) { + kfree(ctx->workstation_name); + kfree(ctx); + } + + return rc; } void @@ -1518,6 +1546,8 @@ smb3_cleanup_fs_context_contents(struct smb3_fs_context *ctx) ctx->source = NULL; kfree(ctx->domainname); ctx->domainname = NULL; + kfree(ctx->workstation_name); + ctx->workstation_name = NULL; kfree(ctx->nodename); ctx->nodename = NULL; kfree(ctx->iocharset); diff --git a/fs/cifs/fs_context.h b/fs/cifs/fs_context.h index b2d22cf9cb18..e54090d9ef36 100644 --- a/fs/cifs/fs_context.h +++ b/fs/cifs/fs_context.h @@ -170,6 +170,7 @@ struct smb3_fs_context { char *server_hostname; char *UNC; char *nodename; + char *workstation_name; char *iocharset; /* local code page for mapping to and from Unicode */ char source_rfc1001_name[RFC1001_NAME_LEN_WITH_NULL]; /* clnt nb name */ char target_rfc1001_name[RFC1001_NAME_LEN_WITH_NULL]; /* srvr nb name */ diff --git a/fs/cifs/fscache.c b/fs/cifs/fscache.c index 8eedd20c44ab..7e409a38a2d7 100644 --- a/fs/cifs/fscache.c +++ b/fs/cifs/fscache.c @@ -87,6 +87,14 @@ void cifs_fscache_get_super_cookie(struct cifs_tcon *tcon) char *sharename; struct cifs_fscache_super_auxdata auxdata; + /* + * Check if cookie was already initialized so don't reinitialize it. + * In the future, as we integrate with newer fscache features, + * we may want to instead add a check if cookie has changed + */ + if (tcon->fscache == NULL) + return; + sharename = extract_sharename(tcon->treeName); if (IS_ERR(sharename)) { cifs_dbg(FYI, "%s: couldn't extract sharename\n", __func__); diff --git a/fs/cifs/misc.c b/fs/cifs/misc.c index ba2c3e897b29..5148d48d6a35 100644 --- a/fs/cifs/misc.c +++ b/fs/cifs/misc.c @@ -75,6 +75,7 @@ sesInfoAlloc(void) INIT_LIST_HEAD(&ret_buf->tcon_list); mutex_init(&ret_buf->session_mutex); spin_lock_init(&ret_buf->iface_lock); + spin_lock_init(&ret_buf->chan_lock); } return ret_buf; } @@ -94,6 +95,7 @@ sesInfoFree(struct cifs_ses *buf_to_free) kfree_sensitive(buf_to_free->password); kfree(buf_to_free->user_name); kfree(buf_to_free->domainName); + kfree(buf_to_free->workstation_name); kfree_sensitive(buf_to_free->auth_key.response); kfree(buf_to_free->iface_list); kfree_sensitive(buf_to_free); @@ -138,9 +140,6 @@ tconInfoFree(struct cifs_tcon *buf_to_free) kfree(buf_to_free->nativeFileSystem); kfree_sensitive(buf_to_free->password); kfree(buf_to_free->crfid.fid); -#ifdef CONFIG_CIFS_DFS_UPCALL - kfree(buf_to_free->dfs_path); -#endif kfree(buf_to_free); } @@ -1287,69 +1286,20 @@ out: return rc; } -static void tcon_super_cb(struct super_block *sb, void *arg) +int cifs_update_super_prepath(struct cifs_sb_info *cifs_sb, char *prefix) { - struct super_cb_data *sd = arg; - struct cifs_tcon *tcon = sd->data; - struct cifs_sb_info *cifs_sb; - - if (sd->sb) - return; - - cifs_sb = CIFS_SB(sb); - if (tcon->dfs_path && cifs_sb->origin_fullpath && - !strcasecmp(tcon->dfs_path, cifs_sb->origin_fullpath)) - sd->sb = sb; -} - -static inline struct super_block *cifs_get_tcon_super(struct cifs_tcon *tcon) -{ - return __cifs_get_super(tcon_super_cb, tcon); -} - -static inline void cifs_put_tcon_super(struct super_block *sb) -{ - __cifs_put_super(sb); -} -#else -static inline struct super_block *cifs_get_tcon_super(struct cifs_tcon *tcon) -{ - return ERR_PTR(-EOPNOTSUPP); -} - -static inline void cifs_put_tcon_super(struct super_block *sb) -{ -} -#endif - -int update_super_prepath(struct cifs_tcon *tcon, char *prefix) -{ - struct super_block *sb; - struct cifs_sb_info *cifs_sb; - int rc = 0; - - sb = cifs_get_tcon_super(tcon); - if (IS_ERR(sb)) - return PTR_ERR(sb); - - cifs_sb = CIFS_SB(sb); - kfree(cifs_sb->prepath); if (prefix && *prefix) { cifs_sb->prepath = kstrdup(prefix, GFP_ATOMIC); - if (!cifs_sb->prepath) { - rc = -ENOMEM; - goto out; - } + if (!cifs_sb->prepath) + return -ENOMEM; convert_delimiter(cifs_sb->prepath, CIFS_DIR_SEP(cifs_sb)); } else cifs_sb->prepath = NULL; cifs_sb->mnt_cifs_flags |= CIFS_MOUNT_USE_PREFIX_PATH; - -out: - cifs_put_tcon_super(sb); - return rc; + return 0; } +#endif diff --git a/fs/cifs/ntlmssp.h b/fs/cifs/ntlmssp.h index 25a2b8ef88b9..fe707f45da89 100644 --- a/fs/cifs/ntlmssp.h +++ b/fs/cifs/ntlmssp.h @@ -119,7 +119,9 @@ typedef struct _AUTHENTICATE_MESSAGE { */ int decode_ntlmssp_challenge(char *bcc_ptr, int blob_len, struct cifs_ses *ses); -void build_ntlmssp_negotiate_blob(unsigned char *pbuffer, struct cifs_ses *ses); +int build_ntlmssp_negotiate_blob(unsigned char **pbuffer, u16 *buflen, + struct cifs_ses *ses, + const struct nls_table *nls_cp); int build_ntlmssp_auth_blob(unsigned char **pbuffer, u16 *buflen, struct cifs_ses *ses, const struct nls_table *nls_cp); diff --git a/fs/cifs/sess.c b/fs/cifs/sess.c index 23e02db7923f..2c10b186ed6e 100644 --- a/fs/cifs/sess.c +++ b/fs/cifs/sess.c @@ -54,41 +54,53 @@ bool is_ses_using_iface(struct cifs_ses *ses, struct cifs_server_iface *iface) { int i; + spin_lock(&ses->chan_lock); for (i = 0; i < ses->chan_count; i++) { - if (is_server_using_iface(ses->chans[i].server, iface)) + if (is_server_using_iface(ses->chans[i].server, iface)) { + spin_unlock(&ses->chan_lock); return true; + } } + spin_unlock(&ses->chan_lock); return false; } /* returns number of channels added */ int cifs_try_adding_channels(struct cifs_sb_info *cifs_sb, struct cifs_ses *ses) { - int old_chan_count = ses->chan_count; - int left = ses->chan_max - ses->chan_count; + int old_chan_count, new_chan_count; + int left; int i = 0; int rc = 0; int tries = 0; struct cifs_server_iface *ifaces = NULL; size_t iface_count; + if (ses->server->dialect < SMB30_PROT_ID) { + cifs_dbg(VFS, "multichannel is not supported on this protocol version, use 3.0 or above\n"); + return 0; + } + + spin_lock(&ses->chan_lock); + + new_chan_count = old_chan_count = ses->chan_count; + left = ses->chan_max - ses->chan_count; + if (left <= 0) { cifs_dbg(FYI, "ses already at max_channels (%zu), nothing to open\n", ses->chan_max); - return 0; - } - - if (ses->server->dialect < SMB30_PROT_ID) { - cifs_dbg(VFS, "multichannel is not supported on this protocol version, use 3.0 or above\n"); + spin_unlock(&ses->chan_lock); return 0; } if (!(ses->server->capabilities & SMB2_GLOBAL_CAP_MULTI_CHANNEL)) { cifs_dbg(VFS, "server %s does not support multichannel\n", ses->server->hostname); ses->chan_max = 1; + spin_unlock(&ses->chan_lock); return 0; } + spin_unlock(&ses->chan_lock); /* * Make a copy of the iface list at the time and use that @@ -142,10 +154,11 @@ int cifs_try_adding_channels(struct cifs_sb_info *cifs_sb, struct cifs_ses *ses) cifs_dbg(FYI, "successfully opened new channel on iface#%d\n", i); left--; + new_chan_count++; } kfree(ifaces); - return ses->chan_count - old_chan_count; + return new_chan_count - old_chan_count; } /* @@ -157,10 +170,14 @@ cifs_ses_find_chan(struct cifs_ses *ses, struct TCP_Server_Info *server) { int i; + spin_lock(&ses->chan_lock); for (i = 0; i < ses->chan_count; i++) { - if (ses->chans[i].server == server) + if (ses->chans[i].server == server) { + spin_unlock(&ses->chan_lock); return &ses->chans[i]; + } } + spin_unlock(&ses->chan_lock); return NULL; } @@ -168,6 +185,7 @@ static int cifs_ses_add_channel(struct cifs_sb_info *cifs_sb, struct cifs_ses *ses, struct cifs_server_iface *iface) { + struct TCP_Server_Info *chan_server; struct cifs_chan *chan; struct smb3_fs_context ctx = {NULL}; static const char unc_fmt[] = "\\%s\\foo"; @@ -240,18 +258,19 @@ cifs_ses_add_channel(struct cifs_sb_info *cifs_sb, struct cifs_ses *ses, SMB2_CLIENT_GUID_SIZE); ctx.use_client_guid = true; - mutex_lock(&ses->session_mutex); + chan_server = cifs_get_tcp_session(&ctx, ses->server); + mutex_lock(&ses->session_mutex); + spin_lock(&ses->chan_lock); chan = ses->binding_chan = &ses->chans[ses->chan_count]; - chan->server = cifs_get_tcp_session(&ctx); + chan->server = chan_server; if (IS_ERR(chan->server)) { rc = PTR_ERR(chan->server); chan->server = NULL; + spin_unlock(&ses->chan_lock); goto out; } - spin_lock(&cifs_tcp_ses_lock); - chan->server->is_channel = true; - spin_unlock(&cifs_tcp_ses_lock); + spin_unlock(&ses->chan_lock); /* * We need to allocate the server crypto now as we will need @@ -283,8 +302,11 @@ cifs_ses_add_channel(struct cifs_sb_info *cifs_sb, struct cifs_ses *ses, * ses to the new server. */ + spin_lock(&ses->chan_lock); ses->chan_count++; atomic_set(&ses->chan_seq, 0); + spin_unlock(&ses->chan_lock); + out: ses->binding = false; ses->binding_chan = NULL; @@ -599,18 +621,85 @@ int decode_ntlmssp_challenge(char *bcc_ptr, int blob_len, return 0; } +static int size_of_ntlmssp_blob(struct cifs_ses *ses, int base_size) +{ + int sz = base_size + ses->auth_key.len + - CIFS_SESS_KEY_SIZE + CIFS_CPHTXT_SIZE + 2; + + if (ses->domainName) + sz += sizeof(__le16) * strnlen(ses->domainName, CIFS_MAX_DOMAINNAME_LEN); + else + sz += sizeof(__le16); + + if (ses->user_name) + sz += sizeof(__le16) * strnlen(ses->user_name, CIFS_MAX_USERNAME_LEN); + else + sz += sizeof(__le16); + + sz += sizeof(__le16) * strnlen(ses->workstation_name, CIFS_MAX_WORKSTATION_LEN); + + return sz; +} + +static inline void cifs_security_buffer_from_str(SECURITY_BUFFER *pbuf, + char *str_value, + int str_length, + unsigned char *pstart, + unsigned char **pcur, + const struct nls_table *nls_cp) +{ + unsigned char *tmp = pstart; + int len; + + if (!pbuf) + return; + + if (!pcur) + pcur = &tmp; + + if (!str_value) { + pbuf->BufferOffset = cpu_to_le32(*pcur - pstart); + pbuf->Length = 0; + pbuf->MaximumLength = 0; + *pcur += sizeof(__le16); + } else { + len = cifs_strtoUTF16((__le16 *)*pcur, + str_value, + str_length, + nls_cp); + len *= sizeof(__le16); + pbuf->BufferOffset = cpu_to_le32(*pcur - pstart); + pbuf->Length = cpu_to_le16(len); + pbuf->MaximumLength = cpu_to_le16(len); + *pcur += len; + } +} + /* BB Move to ntlmssp.c eventually */ -/* We do not malloc the blob, it is passed in pbuffer, because - it is fixed size, and small, making this approach cleaner */ -void build_ntlmssp_negotiate_blob(unsigned char *pbuffer, - struct cifs_ses *ses) +int build_ntlmssp_negotiate_blob(unsigned char **pbuffer, + u16 *buflen, + struct cifs_ses *ses, + const struct nls_table *nls_cp) { + int rc = 0; struct TCP_Server_Info *server = cifs_ses_server(ses); - NEGOTIATE_MESSAGE *sec_blob = (NEGOTIATE_MESSAGE *)pbuffer; + NEGOTIATE_MESSAGE *sec_blob; __u32 flags; + unsigned char *tmp; + int len; - memset(pbuffer, 0, sizeof(NEGOTIATE_MESSAGE)); + len = size_of_ntlmssp_blob(ses, sizeof(NEGOTIATE_MESSAGE)); + *pbuffer = kmalloc(len, GFP_KERNEL); + if (!*pbuffer) { + rc = -ENOMEM; + cifs_dbg(VFS, "Error %d during NTLMSSP allocation\n", rc); + *buflen = 0; + goto setup_ntlm_neg_ret; + } + sec_blob = (NEGOTIATE_MESSAGE *)*pbuffer; + + memset(*pbuffer, 0, sizeof(NEGOTIATE_MESSAGE)); memcpy(sec_blob->Signature, NTLMSSP_SIGNATURE, 8); sec_blob->MessageType = NtLmNegotiate; @@ -624,34 +713,25 @@ void build_ntlmssp_negotiate_blob(unsigned char *pbuffer, if (!server->session_estab || ses->ntlmssp->sesskey_per_smbsess) flags |= NTLMSSP_NEGOTIATE_KEY_XCH; + tmp = *pbuffer + sizeof(NEGOTIATE_MESSAGE); sec_blob->NegotiateFlags = cpu_to_le32(flags); - sec_blob->WorkstationName.BufferOffset = 0; - sec_blob->WorkstationName.Length = 0; - sec_blob->WorkstationName.MaximumLength = 0; + /* these fields should be null in negotiate phase MS-NLMP 3.1.5.1.1 */ + cifs_security_buffer_from_str(&sec_blob->DomainName, + NULL, + CIFS_MAX_DOMAINNAME_LEN, + *pbuffer, &tmp, + nls_cp); - /* Domain name is sent on the Challenge not Negotiate NTLMSSP request */ - sec_blob->DomainName.BufferOffset = 0; - sec_blob->DomainName.Length = 0; - sec_blob->DomainName.MaximumLength = 0; -} + cifs_security_buffer_from_str(&sec_blob->WorkstationName, + NULL, + CIFS_MAX_WORKSTATION_LEN, + *pbuffer, &tmp, + nls_cp); -static int size_of_ntlmssp_blob(struct cifs_ses *ses) -{ - int sz = sizeof(AUTHENTICATE_MESSAGE) + ses->auth_key.len - - CIFS_SESS_KEY_SIZE + CIFS_CPHTXT_SIZE + 2; - - if (ses->domainName) - sz += 2 * strnlen(ses->domainName, CIFS_MAX_DOMAINNAME_LEN); - else - sz += 2; - - if (ses->user_name) - sz += 2 * strnlen(ses->user_name, CIFS_MAX_USERNAME_LEN); - else - sz += 2; - - return sz; + *buflen = tmp - *pbuffer; +setup_ntlm_neg_ret: + return rc; } int build_ntlmssp_auth_blob(unsigned char **pbuffer, @@ -663,6 +743,7 @@ int build_ntlmssp_auth_blob(unsigned char **pbuffer, AUTHENTICATE_MESSAGE *sec_blob; __u32 flags; unsigned char *tmp; + int len; rc = setup_ntlmv2_rsp(ses, nls_cp); if (rc) { @@ -670,7 +751,9 @@ int build_ntlmssp_auth_blob(unsigned char **pbuffer, *buflen = 0; goto setup_ntlmv2_ret; } - *pbuffer = kmalloc(size_of_ntlmssp_blob(ses), GFP_KERNEL); + + len = size_of_ntlmssp_blob(ses, sizeof(AUTHENTICATE_MESSAGE)); + *pbuffer = kmalloc(len, GFP_KERNEL); if (!*pbuffer) { rc = -ENOMEM; cifs_dbg(VFS, "Error %d during NTLMSSP allocation\n", rc); @@ -686,7 +769,7 @@ int build_ntlmssp_auth_blob(unsigned char **pbuffer, NTLMSSP_REQUEST_TARGET | NTLMSSP_NEGOTIATE_TARGET_INFO | NTLMSSP_NEGOTIATE_128 | NTLMSSP_NEGOTIATE_UNICODE | NTLMSSP_NEGOTIATE_NTLM | NTLMSSP_NEGOTIATE_EXTENDED_SEC | - NTLMSSP_NEGOTIATE_SEAL; + NTLMSSP_NEGOTIATE_SEAL | NTLMSSP_NEGOTIATE_WORKSTATION_SUPPLIED; if (ses->server->sign) flags |= NTLMSSP_NEGOTIATE_SIGN; if (!ses->server->session_estab || ses->ntlmssp->sesskey_per_smbsess) @@ -719,42 +802,23 @@ int build_ntlmssp_auth_blob(unsigned char **pbuffer, sec_blob->NtChallengeResponse.MaximumLength = 0; } - if (ses->domainName == NULL) { - sec_blob->DomainName.BufferOffset = cpu_to_le32(tmp - *pbuffer); - sec_blob->DomainName.Length = 0; - sec_blob->DomainName.MaximumLength = 0; - tmp += 2; - } else { - int len; - len = cifs_strtoUTF16((__le16 *)tmp, ses->domainName, - CIFS_MAX_DOMAINNAME_LEN, nls_cp); - len *= 2; /* unicode is 2 bytes each */ - sec_blob->DomainName.BufferOffset = cpu_to_le32(tmp - *pbuffer); - sec_blob->DomainName.Length = cpu_to_le16(len); - sec_blob->DomainName.MaximumLength = cpu_to_le16(len); - tmp += len; - } + cifs_security_buffer_from_str(&sec_blob->DomainName, + ses->domainName, + CIFS_MAX_DOMAINNAME_LEN, + *pbuffer, &tmp, + nls_cp); - if (ses->user_name == NULL) { - sec_blob->UserName.BufferOffset = cpu_to_le32(tmp - *pbuffer); - sec_blob->UserName.Length = 0; - sec_blob->UserName.MaximumLength = 0; - tmp += 2; - } else { - int len; - len = cifs_strtoUTF16((__le16 *)tmp, ses->user_name, - CIFS_MAX_USERNAME_LEN, nls_cp); - len *= 2; /* unicode is 2 bytes each */ - sec_blob->UserName.BufferOffset = cpu_to_le32(tmp - *pbuffer); - sec_blob->UserName.Length = cpu_to_le16(len); - sec_blob->UserName.MaximumLength = cpu_to_le16(len); - tmp += len; - } + cifs_security_buffer_from_str(&sec_blob->UserName, + ses->user_name, + CIFS_MAX_USERNAME_LEN, + *pbuffer, &tmp, + nls_cp); - sec_blob->WorkstationName.BufferOffset = cpu_to_le32(tmp - *pbuffer); - sec_blob->WorkstationName.Length = 0; - sec_blob->WorkstationName.MaximumLength = 0; - tmp += 2; + cifs_security_buffer_from_str(&sec_blob->WorkstationName, + ses->workstation_name, + CIFS_MAX_WORKSTATION_LEN, + *pbuffer, &tmp, + nls_cp); if (((ses->ntlmssp->server_flags & NTLMSSP_NEGOTIATE_KEY_XCH) || (ses->ntlmssp->server_flags & NTLMSSP_NEGOTIATE_EXTENDED_SEC)) @@ -1230,6 +1294,7 @@ sess_auth_rawntlmssp_negotiate(struct sess_data *sess_data) struct cifs_ses *ses = sess_data->ses; __u16 bytes_remaining; char *bcc_ptr; + unsigned char *ntlmsspblob = NULL; u16 blob_len; cifs_dbg(FYI, "rawntlmssp session setup negotiate phase\n"); @@ -1253,10 +1318,15 @@ sess_auth_rawntlmssp_negotiate(struct sess_data *sess_data) pSMB = (SESSION_SETUP_ANDX *)sess_data->iov[0].iov_base; /* Build security blob before we assemble the request */ - build_ntlmssp_negotiate_blob(pSMB->req.SecurityBlob, ses); - sess_data->iov[1].iov_len = sizeof(NEGOTIATE_MESSAGE); - sess_data->iov[1].iov_base = pSMB->req.SecurityBlob; - pSMB->req.SecurityBlobLength = cpu_to_le16(sizeof(NEGOTIATE_MESSAGE)); + rc = build_ntlmssp_negotiate_blob(&ntlmsspblob, + &blob_len, ses, + sess_data->nls_cp); + if (rc) + goto out; + + sess_data->iov[1].iov_len = blob_len; + sess_data->iov[1].iov_base = ntlmsspblob; + pSMB->req.SecurityBlobLength = cpu_to_le16(blob_len); rc = _sess_auth_rawntlmssp_assemble_req(sess_data); if (rc) diff --git a/fs/cifs/smb2inode.c b/fs/cifs/smb2inode.c index 8297703492ee..fe5bfa245fa7 100644 --- a/fs/cifs/smb2inode.c +++ b/fs/cifs/smb2inode.c @@ -46,6 +46,10 @@ struct cop_vars { struct smb2_file_link_info link_info; }; +/* + * note: If cfile is passed, the reference to it is dropped here. + * So make sure that you do not reuse cfile after return from this func. + */ static int smb2_compound_op(const unsigned int xid, struct cifs_tcon *tcon, struct cifs_sb_info *cifs_sb, const char *full_path, @@ -536,10 +540,11 @@ smb2_query_path_info(const unsigned int xid, struct cifs_tcon *tcon, create_options |= OPEN_REPARSE_POINT; /* Failed on a symbolic link - query a reparse point info */ + cifs_get_readable_path(tcon, full_path, &cfile); rc = smb2_compound_op(xid, tcon, cifs_sb, full_path, FILE_READ_ATTRIBUTES, FILE_OPEN, create_options, ACL_NO_MODE, - smb2_data, SMB2_OP_QUERY_INFO, NULL); + smb2_data, SMB2_OP_QUERY_INFO, cfile); } if (rc) goto out; @@ -587,10 +592,11 @@ smb311_posix_query_path_info(const unsigned int xid, struct cifs_tcon *tcon, create_options |= OPEN_REPARSE_POINT; /* Failed on a symbolic link - query a reparse point info */ + cifs_get_readable_path(tcon, full_path, &cfile); rc = smb2_compound_op(xid, tcon, cifs_sb, full_path, FILE_READ_ATTRIBUTES, FILE_OPEN, create_options, ACL_NO_MODE, - smb2_data, SMB2_OP_POSIX_QUERY_INFO, NULL); + smb2_data, SMB2_OP_POSIX_QUERY_INFO, cfile); } if (rc) goto out; @@ -707,10 +713,12 @@ smb2_set_path_size(const unsigned int xid, struct cifs_tcon *tcon, struct cifs_sb_info *cifs_sb, bool set_alloc) { __le64 eof = cpu_to_le64(size); + struct cifsFileInfo *cfile; + cifs_get_writable_path(tcon, full_path, FIND_WR_ANY, &cfile); return smb2_compound_op(xid, tcon, cifs_sb, full_path, FILE_WRITE_DATA, FILE_OPEN, 0, ACL_NO_MODE, - &eof, SMB2_OP_SET_EOF, NULL); + &eof, SMB2_OP_SET_EOF, cfile); } int @@ -719,6 +727,8 @@ smb2_set_file_info(struct inode *inode, const char *full_path, { struct cifs_sb_info *cifs_sb = CIFS_SB(inode->i_sb); struct tcon_link *tlink; + struct cifs_tcon *tcon; + struct cifsFileInfo *cfile; int rc; if ((buf->CreationTime == 0) && (buf->LastAccessTime == 0) && @@ -729,10 +739,12 @@ smb2_set_file_info(struct inode *inode, const char *full_path, tlink = cifs_sb_tlink(cifs_sb); if (IS_ERR(tlink)) return PTR_ERR(tlink); + tcon = tlink_tcon(tlink); - rc = smb2_compound_op(xid, tlink_tcon(tlink), cifs_sb, full_path, + cifs_get_writable_path(tcon, full_path, FIND_WR_ANY, &cfile); + rc = smb2_compound_op(xid, tcon, cifs_sb, full_path, FILE_WRITE_ATTRIBUTES, FILE_OPEN, - 0, ACL_NO_MODE, buf, SMB2_OP_SET_INFO, NULL); + 0, ACL_NO_MODE, buf, SMB2_OP_SET_INFO, cfile); cifs_put_tlink(tlink); return rc; } diff --git a/fs/cifs/smb2ops.c b/fs/cifs/smb2ops.c index 7acf71defea7..c5b1dea54ebc 100644 --- a/fs/cifs/smb2ops.c +++ b/fs/cifs/smb2ops.c @@ -2844,6 +2844,7 @@ smb2_get_dfs_refer(const unsigned int xid, struct cifs_ses *ses, struct fsctl_get_dfs_referral_req *dfs_req = NULL; struct get_dfs_referral_rsp *dfs_rsp = NULL; u32 dfs_req_size = 0, dfs_rsp_size = 0; + int retry_count = 0; cifs_dbg(FYI, "%s: path: %s\n", __func__, search_name); @@ -2895,11 +2896,14 @@ smb2_get_dfs_refer(const unsigned int xid, struct cifs_ses *ses, true /* is_fsctl */, (char *)dfs_req, dfs_req_size, CIFSMaxBufSize, (char **)&dfs_rsp, &dfs_rsp_size); - } while (rc == -EAGAIN); + if (!is_retryable_error(rc)) + break; + usleep_range(512, 2048); + } while (++retry_count < 5); if (rc) { - if ((rc != -ENOENT) && (rc != -EOPNOTSUPP)) - cifs_tcon_dbg(VFS, "ioctl error in %s rc=%d\n", __func__, rc); + if (!is_retryable_error(rc) && rc != -ENOENT && rc != -EOPNOTSUPP) + cifs_tcon_dbg(VFS, "%s: ioctl error: rc=%d\n", __func__, rc); goto out; } diff --git a/fs/cifs/smb2pdu.c b/fs/cifs/smb2pdu.c index d2ecb2ea37c0..2f5f2c4c6183 100644 --- a/fs/cifs/smb2pdu.c +++ b/fs/cifs/smb2pdu.c @@ -155,7 +155,11 @@ smb2_reconnect(__le16 smb2_command, struct cifs_tcon *tcon, if (tcon == NULL) return 0; - if (smb2_command == SMB2_TREE_CONNECT) + /* + * Need to also skip SMB2_IOCTL because it is used for checking nested dfs links in + * cifs_tree_connect(). + */ + if (smb2_command == SMB2_TREE_CONNECT || smb2_command == SMB2_IOCTL) return 0; if (tcon->tidStatus == CifsExiting) { @@ -253,7 +257,7 @@ smb2_reconnect(__le16 smb2_command, struct cifs_tcon *tcon, /* * If we are reconnecting an extra channel, bind */ - if (server->is_channel) { + if (CIFS_SERVER_IS_CHAN(server)) { ses->binding = true; ses->binding_chan = cifs_ses_find_chan(ses, server); } @@ -1456,7 +1460,7 @@ SMB2_sess_auth_rawntlmssp_negotiate(struct SMB2_sess_data *sess_data) int rc; struct cifs_ses *ses = sess_data->ses; struct smb2_sess_setup_rsp *rsp = NULL; - char *ntlmssp_blob = NULL; + unsigned char *ntlmssp_blob = NULL; bool use_spnego = false; /* else use raw ntlmssp */ u16 blob_length = 0; @@ -1475,22 +1479,17 @@ SMB2_sess_auth_rawntlmssp_negotiate(struct SMB2_sess_data *sess_data) if (rc) goto out_err; - ntlmssp_blob = kmalloc(sizeof(struct _NEGOTIATE_MESSAGE), - GFP_KERNEL); - if (ntlmssp_blob == NULL) { - rc = -ENOMEM; - goto out; - } + rc = build_ntlmssp_negotiate_blob(&ntlmssp_blob, + &blob_length, ses, + sess_data->nls_cp); + if (rc) + goto out_err; - build_ntlmssp_negotiate_blob(ntlmssp_blob, ses); if (use_spnego) { /* BB eventually need to add this */ cifs_dbg(VFS, "spnego not supported for SMB2 yet\n"); rc = -EOPNOTSUPP; goto out; - } else { - blob_length = sizeof(struct _NEGOTIATE_MESSAGE); - /* with raw NTLMSSP we don't encapsulate in SPNEGO */ } sess_data->iov[1].iov_base = ntlmssp_blob; sess_data->iov[1].iov_len = blob_length; @@ -1841,7 +1840,7 @@ SMB2_tcon(const unsigned int xid, struct cifs_ses *ses, const char *tree, cifs_small_buf_release(req); rsp = (struct smb2_tree_connect_rsp *)rsp_iov.iov_base; trace_smb3_tcon(xid, tcon->tid, ses->Suid, tree, rc); - if (rc != 0) { + if ((rc != 0) || (rsp == NULL)) { cifs_stats_fail_inc(tcon, SMB2_TREE_CONNECT_HE); tcon->need_reconnect = true; goto tcon_error_exit; @@ -2669,7 +2668,18 @@ int smb311_posix_mkdir(const unsigned int xid, struct inode *inode, goto err_free_rsp_buf; } + /* + * Although unlikely to be possible for rsp to be null and rc not set, + * adding check below is slightly safer long term (and quiets Coverity + * warning) + */ rsp = (struct smb2_create_rsp *)rsp_iov.iov_base; + if (rsp == NULL) { + rc = -EIO; + kfree(pc_buf); + goto err_free_req; + } + trace_smb3_posix_mkdir_done(xid, le64_to_cpu(rsp->PersistentFileId), tcon->tid, ses->Suid, CREATE_NOT_FILE, @@ -2942,7 +2952,9 @@ SMB2_open(const unsigned int xid, struct cifs_open_parms *oparms, __le16 *path, tcon->need_reconnect = true; } goto creat_exit; - } else + } else if (rsp == NULL) /* unlikely to happen, but safer to check */ + goto creat_exit; + else trace_smb3_open_done(xid, le64_to_cpu(rsp->PersistentFileId), tcon->tid, ses->Suid, oparms->create_options, @@ -3163,6 +3175,16 @@ SMB2_ioctl(const unsigned int xid, struct cifs_tcon *tcon, u64 persistent_fid, if ((plen == NULL) || (out_data == NULL)) goto ioctl_exit; + /* + * Although unlikely to be possible for rsp to be null and rc not set, + * adding check below is slightly safer long term (and quiets Coverity + * warning) + */ + if (rsp == NULL) { + rc = -EIO; + goto ioctl_exit; + } + *plen = le32_to_cpu(rsp->OutputCount); /* We check for obvious errors in the output buffer length and offset */ diff --git a/fs/cifs/transport.c b/fs/cifs/transport.c index b7379329b741..61ea3d3f95b4 100644 --- a/fs/cifs/transport.c +++ b/fs/cifs/transport.c @@ -1044,14 +1044,17 @@ struct TCP_Server_Info *cifs_pick_channel(struct cifs_ses *ses) if (!ses) return NULL; + spin_lock(&ses->chan_lock); if (!ses->binding) { /* round robin */ if (ses->chan_count > 1) { index = (uint)atomic_inc_return(&ses->chan_seq); index %= ses->chan_count; } + spin_unlock(&ses->chan_lock); return ses->chans[index].server; } else { + spin_unlock(&ses->chan_lock); return cifs_ses_server(ses); } } diff --git a/fs/io-wq.c b/fs/io-wq.c index afd955d53db9..88202de519f6 100644 --- a/fs/io-wq.c +++ b/fs/io-wq.c @@ -423,9 +423,10 @@ static inline unsigned int io_get_work_hash(struct io_wq_work *work) return work->flags >> IO_WQ_HASH_SHIFT; } -static void io_wait_on_hash(struct io_wqe *wqe, unsigned int hash) +static bool io_wait_on_hash(struct io_wqe *wqe, unsigned int hash) { struct io_wq *wq = wqe->wq; + bool ret = false; spin_lock_irq(&wq->hash->wait.lock); if (list_empty(&wqe->wait.entry)) { @@ -433,9 +434,11 @@ static void io_wait_on_hash(struct io_wqe *wqe, unsigned int hash) if (!test_bit(hash, &wq->hash->map)) { __set_current_state(TASK_RUNNING); list_del_init(&wqe->wait.entry); + ret = true; } } spin_unlock_irq(&wq->hash->wait.lock); + return ret; } static struct io_wq_work *io_get_next_work(struct io_wqe_acct *acct, @@ -475,14 +478,21 @@ static struct io_wq_work *io_get_next_work(struct io_wqe_acct *acct, } if (stall_hash != -1U) { + bool unstalled; + /* * Set this before dropping the lock to avoid racing with new * work being added and clearing the stalled bit. */ set_bit(IO_ACCT_STALLED_BIT, &acct->flags); raw_spin_unlock(&wqe->lock); - io_wait_on_hash(wqe, stall_hash); + unstalled = io_wait_on_hash(wqe, stall_hash); raw_spin_lock(&wqe->lock); + if (unstalled) { + clear_bit(IO_ACCT_STALLED_BIT, &acct->flags); + if (wq_has_sleeper(&wqe->wq->hash->wait)) + wake_up(&wqe->wq->hash->wait); + } } return NULL; @@ -564,8 +574,11 @@ get_next: io_wqe_enqueue(wqe, linked); if (hash != -1U && !next_hashed) { + /* serialize hash clear with wake_up() */ + spin_lock_irq(&wq->hash->wait.lock); clear_bit(hash, &wq->hash->map); clear_bit(IO_ACCT_STALLED_BIT, &acct->flags); + spin_unlock_irq(&wq->hash->wait.lock); if (wq_has_sleeper(&wq->hash->wait)) wake_up(&wq->hash->wait); raw_spin_lock(&wqe->lock); diff --git a/fs/ksmbd/Kconfig b/fs/ksmbd/Kconfig index b83cbd756ae5..e1fe17747ed6 100644 --- a/fs/ksmbd/Kconfig +++ b/fs/ksmbd/Kconfig @@ -6,7 +6,6 @@ config SMB_SERVER select NLS select NLS_UTF8 select CRYPTO - select CRYPTO_MD4 select CRYPTO_MD5 select CRYPTO_HMAC select CRYPTO_ECB @@ -19,6 +18,7 @@ config SMB_SERVER select CRYPTO_GCM select ASN1 select OID_REGISTRY + select CRC32 default n help Choose Y here if you want to allow SMB3 compliant clients diff --git a/fs/ksmbd/auth.c b/fs/ksmbd/auth.c index 30a92ddc1817..3503b1c48cb4 100644 --- a/fs/ksmbd/auth.c +++ b/fs/ksmbd/auth.c @@ -873,9 +873,9 @@ int ksmbd_gen_preauth_integrity_hash(struct ksmbd_conn *conn, char *buf, __u8 *pi_hash) { int rc; - struct smb2_hdr *rcv_hdr = (struct smb2_hdr *)buf; + struct smb2_hdr *rcv_hdr = smb2_get_msg(buf); char *all_bytes_msg = (char *)&rcv_hdr->ProtocolId; - int msg_size = be32_to_cpu(rcv_hdr->smb2_buf_length); + int msg_size = get_rfc1002_len(buf); struct ksmbd_crypto_ctx *ctx = NULL; if (conn->preauth_info->Preauth_HashId != @@ -983,7 +983,7 @@ static struct scatterlist *ksmbd_init_sg(struct kvec *iov, unsigned int nvec, u8 *sign) { struct scatterlist *sg; - unsigned int assoc_data_len = sizeof(struct smb2_transform_hdr) - 24; + unsigned int assoc_data_len = sizeof(struct smb2_transform_hdr) - 20; int i, nr_entries[3] = {0}, total_entries = 0, sg_idx = 0; if (!nvec) @@ -1047,9 +1047,8 @@ static struct scatterlist *ksmbd_init_sg(struct kvec *iov, unsigned int nvec, int ksmbd_crypt_message(struct ksmbd_conn *conn, struct kvec *iov, unsigned int nvec, int enc) { - struct smb2_transform_hdr *tr_hdr = - (struct smb2_transform_hdr *)iov[0].iov_base; - unsigned int assoc_data_len = sizeof(struct smb2_transform_hdr) - 24; + struct smb2_transform_hdr *tr_hdr = smb2_get_msg(iov[0].iov_base); + unsigned int assoc_data_len = sizeof(struct smb2_transform_hdr) - 20; int rc; struct scatterlist *sg; u8 sign[SMB2_SIGNATURE_SIZE] = {}; diff --git a/fs/ksmbd/connection.c b/fs/ksmbd/connection.c index b57a0d8a392f..83a94d0bb480 100644 --- a/fs/ksmbd/connection.c +++ b/fs/ksmbd/connection.c @@ -158,26 +158,25 @@ void ksmbd_conn_wait_idle(struct ksmbd_conn *conn) int ksmbd_conn_write(struct ksmbd_work *work) { struct ksmbd_conn *conn = work->conn; - struct smb_hdr *rsp_hdr = work->response_buf; size_t len = 0; int sent; struct kvec iov[3]; int iov_idx = 0; ksmbd_conn_try_dequeue_request(work); - if (!rsp_hdr) { + 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) }; + sizeof(struct smb2_transform_hdr) + 4 }; len += iov[iov_idx++].iov_len; } if (work->aux_payload_sz) { - iov[iov_idx] = (struct kvec) { rsp_hdr, work->resp_hdr_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; @@ -185,8 +184,8 @@ int ksmbd_conn_write(struct ksmbd_work *work) if (work->tr_buf) iov[iov_idx].iov_len = work->resp_hdr_sz; else - iov[iov_idx].iov_len = get_rfc1002_len(rsp_hdr) + 4; - iov[iov_idx].iov_base = rsp_hdr; + 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; } diff --git a/fs/ksmbd/ksmbd_work.c b/fs/ksmbd/ksmbd_work.c index fd58eb4809f6..14b9caebf7a4 100644 --- a/fs/ksmbd/ksmbd_work.c +++ b/fs/ksmbd/ksmbd_work.c @@ -69,7 +69,6 @@ int ksmbd_workqueue_init(void) void ksmbd_workqueue_destroy(void) { - flush_workqueue(ksmbd_wq); destroy_workqueue(ksmbd_wq); ksmbd_wq = NULL; } diff --git a/fs/ksmbd/ksmbd_work.h b/fs/ksmbd/ksmbd_work.h index f7156bc50049..5ece58e40c97 100644 --- a/fs/ksmbd/ksmbd_work.h +++ b/fs/ksmbd/ksmbd_work.h @@ -92,7 +92,7 @@ struct ksmbd_work { */ static inline void *ksmbd_resp_buf_next(struct ksmbd_work *work) { - return work->response_buf + work->next_smb2_rsp_hdr_off; + return work->response_buf + work->next_smb2_rsp_hdr_off + 4; } /** @@ -101,7 +101,7 @@ static inline void *ksmbd_resp_buf_next(struct ksmbd_work *work) */ static inline void *ksmbd_req_buf_next(struct ksmbd_work *work) { - return work->request_buf + work->next_smb2_rcv_hdr_off; + return work->request_buf + work->next_smb2_rcv_hdr_off + 4; } struct ksmbd_work *ksmbd_alloc_work_struct(void); diff --git a/fs/ksmbd/oplock.c b/fs/ksmbd/oplock.c index f9dae6ef2115..077b8761d099 100644 --- a/fs/ksmbd/oplock.c +++ b/fs/ksmbd/oplock.c @@ -629,10 +629,10 @@ static void __smb2_oplock_break_noti(struct work_struct *wk) return; } - rsp_hdr = work->response_buf; + rsp_hdr = smb2_get_msg(work->response_buf); memset(rsp_hdr, 0, sizeof(struct smb2_hdr) + 2); - rsp_hdr->smb2_buf_length = - cpu_to_be32(smb2_hdr_size_no_buflen(conn->vals)); + *(__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); @@ -645,7 +645,7 @@ static void __smb2_oplock_break_noti(struct work_struct *wk) rsp_hdr->SessionId = 0; memset(rsp_hdr->Signature, 0, 16); - rsp = work->response_buf; + rsp = smb2_get_msg(work->response_buf); rsp->StructureSize = cpu_to_le16(24); if (!br_info->open_trunc && @@ -659,7 +659,7 @@ static void __smb2_oplock_break_noti(struct work_struct *wk) rsp->PersistentFid = cpu_to_le64(fp->persistent_id); rsp->VolatileFid = cpu_to_le64(fp->volatile_id); - inc_rfc1001_len(rsp, 24); + inc_rfc1001_len(work->response_buf, 24); ksmbd_debug(OPLOCK, "sending oplock break v_id %llu p_id = %llu lock level = %d\n", @@ -736,10 +736,10 @@ static void __smb2_lease_break_noti(struct work_struct *wk) return; } - rsp_hdr = work->response_buf; + rsp_hdr = smb2_get_msg(work->response_buf); memset(rsp_hdr, 0, sizeof(struct smb2_hdr) + 2); - rsp_hdr->smb2_buf_length = - cpu_to_be32(smb2_hdr_size_no_buflen(conn->vals)); + *(__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); @@ -752,7 +752,7 @@ static void __smb2_lease_break_noti(struct work_struct *wk) rsp_hdr->SessionId = 0; memset(rsp_hdr->Signature, 0, 16); - rsp = work->response_buf; + rsp = smb2_get_msg(work->response_buf); rsp->StructureSize = cpu_to_le16(44); rsp->Epoch = br_info->epoch; rsp->Flags = 0; @@ -768,7 +768,7 @@ static void __smb2_lease_break_noti(struct work_struct *wk) rsp->AccessMaskHint = 0; rsp->ShareMaskHint = 0; - inc_rfc1001_len(rsp, 44); + inc_rfc1001_len(work->response_buf, 44); ksmbd_conn_write(work); ksmbd_free_work_struct(work); @@ -1335,19 +1335,16 @@ __u8 smb2_map_lease_to_oplock(__le32 lease_state) */ void create_lease_buf(u8 *rbuf, struct lease *lease) { - char *LeaseKey = (char *)&lease->lease_key; - if (lease->version == 2) { struct create_lease_v2 *buf = (struct create_lease_v2 *)rbuf; - char *ParentLeaseKey = (char *)&lease->parent_lease_key; memset(buf, 0, sizeof(struct create_lease_v2)); - buf->lcontext.LeaseKeyLow = *((__le64 *)LeaseKey); - buf->lcontext.LeaseKeyHigh = *((__le64 *)(LeaseKey + 8)); + memcpy(buf->lcontext.LeaseKey, lease->lease_key, + SMB2_LEASE_KEY_SIZE); buf->lcontext.LeaseFlags = lease->flags; buf->lcontext.LeaseState = lease->state; - buf->lcontext.ParentLeaseKeyLow = *((__le64 *)ParentLeaseKey); - buf->lcontext.ParentLeaseKeyHigh = *((__le64 *)(ParentLeaseKey + 8)); + memcpy(buf->lcontext.ParentLeaseKey, lease->parent_lease_key, + SMB2_LEASE_KEY_SIZE); buf->ccontext.DataOffset = cpu_to_le16(offsetof (struct create_lease_v2, lcontext)); buf->ccontext.DataLength = cpu_to_le32(sizeof(struct lease_context_v2)); @@ -1362,8 +1359,7 @@ void create_lease_buf(u8 *rbuf, struct lease *lease) struct create_lease *buf = (struct create_lease *)rbuf; memset(buf, 0, sizeof(struct create_lease)); - buf->lcontext.LeaseKeyLow = *((__le64 *)LeaseKey); - buf->lcontext.LeaseKeyHigh = *((__le64 *)(LeaseKey + 8)); + memcpy(buf->lcontext.LeaseKey, lease->lease_key, SMB2_LEASE_KEY_SIZE); buf->lcontext.LeaseFlags = lease->flags; buf->lcontext.LeaseState = lease->state; buf->ccontext.DataOffset = cpu_to_le16(offsetof @@ -1398,7 +1394,7 @@ struct lease_ctx_info *parse_lease_state(void *open_req) if (!lreq) return NULL; - data_offset = (char *)req + 4 + le32_to_cpu(req->CreateContextsOffset); + data_offset = (char *)req + le32_to_cpu(req->CreateContextsOffset); cc = (struct create_context *)data_offset; do { cc = (struct create_context *)((char *)cc + next); @@ -1416,19 +1412,17 @@ struct lease_ctx_info *parse_lease_state(void *open_req) if (sizeof(struct lease_context_v2) == le32_to_cpu(cc->DataLength)) { struct create_lease_v2 *lc = (struct create_lease_v2 *)cc; - *((__le64 *)lreq->lease_key) = lc->lcontext.LeaseKeyLow; - *((__le64 *)(lreq->lease_key + 8)) = lc->lcontext.LeaseKeyHigh; + memcpy(lreq->lease_key, lc->lcontext.LeaseKey, SMB2_LEASE_KEY_SIZE); lreq->req_state = lc->lcontext.LeaseState; lreq->flags = lc->lcontext.LeaseFlags; lreq->duration = lc->lcontext.LeaseDuration; - *((__le64 *)lreq->parent_lease_key) = lc->lcontext.ParentLeaseKeyLow; - *((__le64 *)(lreq->parent_lease_key + 8)) = lc->lcontext.ParentLeaseKeyHigh; + memcpy(lreq->parent_lease_key, lc->lcontext.ParentLeaseKey, + SMB2_LEASE_KEY_SIZE); lreq->version = 2; } else { struct create_lease *lc = (struct create_lease *)cc; - *((__le64 *)lreq->lease_key) = lc->lcontext.LeaseKeyLow; - *((__le64 *)(lreq->lease_key + 8)) = lc->lcontext.LeaseKeyHigh; + memcpy(lreq->lease_key, lc->lcontext.LeaseKey, SMB2_LEASE_KEY_SIZE); lreq->req_state = lc->lcontext.LeaseState; lreq->flags = lc->lcontext.LeaseFlags; lreq->duration = lc->lcontext.LeaseDuration; @@ -1462,7 +1456,7 @@ struct create_context *smb2_find_context_vals(void *open_req, const char *tag) * CreateContextsOffset and CreateContextsLength are guaranteed to * be valid because of ksmbd_smb2_check_message(). */ - cc = (struct create_context *)((char *)req + 4 + + cc = (struct create_context *)((char *)req + le32_to_cpu(req->CreateContextsOffset)); remain_len = le32_to_cpu(req->CreateContextsLength); do { diff --git a/fs/ksmbd/oplock.h b/fs/ksmbd/oplock.h index 119b8047cfbd..0cf7a2b5bbc0 100644 --- a/fs/ksmbd/oplock.h +++ b/fs/ksmbd/oplock.h @@ -28,8 +28,6 @@ #define OPLOCK_WRITE_TO_NONE 0x04 #define OPLOCK_READ_TO_NONE 0x08 -#define SMB2_LEASE_KEY_SIZE 16 - struct lease_ctx_info { __u8 lease_key[SMB2_LEASE_KEY_SIZE]; __le32 req_state; diff --git a/fs/ksmbd/server.c b/fs/ksmbd/server.c index 2a2b2135bfde..2e12f6d8483b 100644 --- a/fs/ksmbd/server.c +++ b/fs/ksmbd/server.c @@ -622,7 +622,6 @@ MODULE_DESCRIPTION("Linux kernel CIFS/SMB SERVER"); MODULE_LICENSE("GPL"); MODULE_SOFTDEP("pre: ecb"); MODULE_SOFTDEP("pre: hmac"); -MODULE_SOFTDEP("pre: md4"); MODULE_SOFTDEP("pre: md5"); MODULE_SOFTDEP("pre: nls"); MODULE_SOFTDEP("pre: aes"); @@ -632,5 +631,6 @@ MODULE_SOFTDEP("pre: sha512"); MODULE_SOFTDEP("pre: aead2"); MODULE_SOFTDEP("pre: ccm"); MODULE_SOFTDEP("pre: gcm"); +MODULE_SOFTDEP("pre: crc32"); module_init(ksmbd_server_init) module_exit(ksmbd_server_exit) diff --git a/fs/ksmbd/smb2misc.c b/fs/ksmbd/smb2misc.c index 030ca57c3784..50d0b1022289 100644 --- a/fs/ksmbd/smb2misc.c +++ b/fs/ksmbd/smb2misc.c @@ -6,7 +6,6 @@ #include "glob.h" #include "nterr.h" -#include "smb2pdu.h" #include "smb_common.h" #include "smbstatus.h" #include "mgmt/user_session.h" @@ -347,23 +346,16 @@ static int smb2_validate_credit_charge(struct ksmbd_conn *conn, int ksmbd_smb2_check_message(struct ksmbd_work *work) { - struct smb2_pdu *pdu = work->request_buf; + struct smb2_pdu *pdu = ksmbd_req_buf_next(work); struct smb2_hdr *hdr = &pdu->hdr; int command; __u32 clc_len; /* calculated length */ - __u32 len = get_rfc1002_len(pdu); + __u32 len = get_rfc1002_len(work->request_buf); - if (work->next_smb2_rcv_hdr_off) { - pdu = ksmbd_req_buf_next(work); - hdr = &pdu->hdr; - } - - if (le32_to_cpu(hdr->NextCommand) > 0) { + if (le32_to_cpu(hdr->NextCommand) > 0) len = le32_to_cpu(hdr->NextCommand); - } else if (work->next_smb2_rcv_hdr_off) { + else if (work->next_smb2_rcv_hdr_off) len -= work->next_smb2_rcv_hdr_off; - len = round_up(len, 8); - } if (check_smb2_hdr(hdr)) return 1; diff --git a/fs/ksmbd/smb2ops.c b/fs/ksmbd/smb2ops.c index fb6a65d23139..0a5d8450e835 100644 --- a/fs/ksmbd/smb2ops.c +++ b/fs/ksmbd/smb2ops.c @@ -6,7 +6,6 @@ #include #include "glob.h" -#include "smb2pdu.h" #include "auth.h" #include "connection.h" @@ -199,7 +198,7 @@ void init_smb2_1_server(struct ksmbd_conn *conn) conn->cmds = smb2_0_server_cmds; conn->max_cmds = ARRAY_SIZE(smb2_0_server_cmds); conn->max_credits = SMB2_MAX_CREDITS; - conn->signing_algorithm = SIGNING_ALG_HMAC_SHA256; + conn->signing_algorithm = SIGNING_ALG_HMAC_SHA256_LE; if (server_conf.flags & KSMBD_GLOBAL_FLAG_SMB2_LEASES) conn->vals->capabilities |= SMB2_GLOBAL_CAP_LEASING; @@ -217,7 +216,7 @@ void init_smb3_0_server(struct ksmbd_conn *conn) conn->cmds = smb2_0_server_cmds; conn->max_cmds = ARRAY_SIZE(smb2_0_server_cmds); conn->max_credits = SMB2_MAX_CREDITS; - conn->signing_algorithm = SIGNING_ALG_AES_CMAC; + conn->signing_algorithm = SIGNING_ALG_AES_CMAC_LE; if (server_conf.flags & KSMBD_GLOBAL_FLAG_SMB2_LEASES) conn->vals->capabilities |= SMB2_GLOBAL_CAP_LEASING; @@ -242,7 +241,7 @@ void init_smb3_02_server(struct ksmbd_conn *conn) conn->cmds = smb2_0_server_cmds; conn->max_cmds = ARRAY_SIZE(smb2_0_server_cmds); conn->max_credits = SMB2_MAX_CREDITS; - conn->signing_algorithm = SIGNING_ALG_AES_CMAC; + conn->signing_algorithm = SIGNING_ALG_AES_CMAC_LE; if (server_conf.flags & KSMBD_GLOBAL_FLAG_SMB2_LEASES) conn->vals->capabilities |= SMB2_GLOBAL_CAP_LEASING; @@ -267,7 +266,7 @@ int init_smb3_11_server(struct ksmbd_conn *conn) conn->cmds = smb2_0_server_cmds; conn->max_cmds = ARRAY_SIZE(smb2_0_server_cmds); conn->max_credits = SMB2_MAX_CREDITS; - conn->signing_algorithm = SIGNING_ALG_AES_CMAC; + conn->signing_algorithm = SIGNING_ALG_AES_CMAC_LE; if (server_conf.flags & KSMBD_GLOBAL_FLAG_SMB2_LEASES) conn->vals->capabilities |= SMB2_GLOBAL_CAP_LEASING; diff --git a/fs/ksmbd/smb2pdu.c b/fs/ksmbd/smb2pdu.c index 7e448df3f847..121f8e8c70ac 100644 --- a/fs/ksmbd/smb2pdu.c +++ b/fs/ksmbd/smb2pdu.c @@ -13,7 +13,6 @@ #include #include "glob.h" -#include "smb2pdu.h" #include "smbfsctl.h" #include "oplock.h" #include "smbacl.h" @@ -44,8 +43,8 @@ static void __wbuf(struct ksmbd_work *work, void **req, void **rsp) *req = ksmbd_req_buf_next(work); *rsp = ksmbd_resp_buf_next(work); } else { - *req = work->request_buf; - *rsp = work->response_buf; + *req = smb2_get_msg(work->request_buf); + *rsp = smb2_get_msg(work->response_buf); } } @@ -93,13 +92,14 @@ struct channel *lookup_chann_list(struct ksmbd_session *sess, struct ksmbd_conn */ int smb2_get_ksmbd_tcon(struct ksmbd_work *work) { - struct smb2_hdr *req_hdr = work->request_buf; + struct smb2_hdr *req_hdr = smb2_get_msg(work->request_buf); + unsigned int cmd = le16_to_cpu(req_hdr->Command); int tree_id; work->tcon = NULL; - if (work->conn->ops->get_cmd_val(work) == SMB2_TREE_CONNECT_HE || - work->conn->ops->get_cmd_val(work) == SMB2_CANCEL_HE || - work->conn->ops->get_cmd_val(work) == SMB2_LOGOFF_HE) { + if (cmd == SMB2_TREE_CONNECT_HE || + cmd == SMB2_CANCEL_HE || + cmd == SMB2_LOGOFF_HE) { ksmbd_debug(SMB, "skip to check tree connect request\n"); return 0; } @@ -130,7 +130,7 @@ void smb2_set_err_rsp(struct ksmbd_work *work) if (work->next_smb2_rcv_hdr_off) err_rsp = ksmbd_resp_buf_next(work); else - err_rsp = work->response_buf; + err_rsp = smb2_get_msg(work->response_buf); if (err_rsp->hdr.Status != STATUS_STOPPED_ON_SYMLINK) { err_rsp->StructureSize = SMB2_ERROR_STRUCTURE_SIZE2_LE; @@ -150,7 +150,7 @@ void smb2_set_err_rsp(struct ksmbd_work *work) */ bool is_smb2_neg_cmd(struct ksmbd_work *work) { - struct smb2_hdr *hdr = work->request_buf; + struct smb2_hdr *hdr = smb2_get_msg(work->request_buf); /* is it SMB2 header ? */ if (hdr->ProtocolId != SMB2_PROTO_NUMBER) @@ -174,7 +174,7 @@ bool is_smb2_neg_cmd(struct ksmbd_work *work) */ bool is_smb2_rsp(struct ksmbd_work *work) { - struct smb2_hdr *hdr = work->response_buf; + struct smb2_hdr *hdr = smb2_get_msg(work->response_buf); /* is it SMB2 header ? */ if (hdr->ProtocolId != SMB2_PROTO_NUMBER) @@ -200,7 +200,7 @@ u16 get_smb2_cmd_val(struct ksmbd_work *work) if (work->next_smb2_rcv_hdr_off) rcv_hdr = ksmbd_req_buf_next(work); else - rcv_hdr = work->request_buf; + rcv_hdr = smb2_get_msg(work->request_buf); return le16_to_cpu(rcv_hdr->Command); } @@ -216,7 +216,7 @@ void set_smb2_rsp_status(struct ksmbd_work *work, __le32 err) if (work->next_smb2_rcv_hdr_off) rsp_hdr = ksmbd_resp_buf_next(work); else - rsp_hdr = work->response_buf; + rsp_hdr = smb2_get_msg(work->response_buf); rsp_hdr->Status = err; smb2_set_err_rsp(work); } @@ -237,13 +237,11 @@ int init_smb2_neg_rsp(struct ksmbd_work *work) if (conn->need_neg == false) return -EINVAL; - rsp_hdr = work->response_buf; + *(__be32 *)work->response_buf = + cpu_to_be32(conn->vals->header_size); + rsp_hdr = smb2_get_msg(work->response_buf); memset(rsp_hdr, 0, sizeof(struct smb2_hdr) + 2); - - rsp_hdr->smb2_buf_length = - cpu_to_be32(smb2_hdr_size_no_buflen(conn->vals)); - rsp_hdr->ProtocolId = SMB2_PROTO_NUMBER; rsp_hdr->StructureSize = SMB2_HEADER_STRUCTURE_SIZE; rsp_hdr->CreditRequest = cpu_to_le16(2); @@ -256,7 +254,7 @@ int init_smb2_neg_rsp(struct ksmbd_work *work) rsp_hdr->SessionId = 0; memset(rsp_hdr->Signature, 0, 16); - rsp = work->response_buf; + rsp = smb2_get_msg(work->response_buf); WARN_ON(ksmbd_conn_good(work)); @@ -277,12 +275,12 @@ int init_smb2_neg_rsp(struct ksmbd_work *work) rsp->SecurityBufferOffset = cpu_to_le16(128); rsp->SecurityBufferLength = cpu_to_le16(AUTH_GSS_LENGTH); - ksmbd_copy_gss_neg_header(((char *)(&rsp->hdr) + - sizeof(rsp->hdr.smb2_buf_length)) + + ksmbd_copy_gss_neg_header((char *)(&rsp->hdr) + le16_to_cpu(rsp->SecurityBufferOffset)); - inc_rfc1001_len(rsp, sizeof(struct smb2_negotiate_rsp) - - sizeof(struct smb2_hdr) - sizeof(rsp->Buffer) + - AUTH_GSS_LENGTH); + 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; @@ -387,8 +385,8 @@ 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) - 4) - + new_len - len)); + inc_rfc1001_len(work->response_buf, + sizeof(struct smb2_hdr) + new_len - len); rsp->NextCommand = cpu_to_le32(new_len); work->next_smb2_rcv_hdr_off += next_hdr_offset; @@ -406,7 +404,7 @@ static void init_chained_smb2_rsp(struct ksmbd_work *work) work->compound_fid = KSMBD_NO_FID; work->compound_pfid = KSMBD_NO_FID; } - memset((char *)rsp_hdr + 4, 0, sizeof(struct smb2_hdr) + 2); + memset((char *)rsp_hdr, 0, sizeof(struct smb2_hdr) + 2); rsp_hdr->ProtocolId = SMB2_PROTO_NUMBER; rsp_hdr->StructureSize = SMB2_HEADER_STRUCTURE_SIZE; rsp_hdr->Command = rcv_hdr->Command; @@ -432,7 +430,7 @@ static void init_chained_smb2_rsp(struct ksmbd_work *work) */ bool is_chained_smb2_message(struct ksmbd_work *work) { - struct smb2_hdr *hdr = work->request_buf; + struct smb2_hdr *hdr = smb2_get_msg(work->request_buf); unsigned int len, next_cmd; if (hdr->ProtocolId != SMB2_PROTO_NUMBER) @@ -483,13 +481,13 @@ bool is_chained_smb2_message(struct ksmbd_work *work) */ int init_smb2_rsp_hdr(struct ksmbd_work *work) { - struct smb2_hdr *rsp_hdr = work->response_buf; - struct smb2_hdr *rcv_hdr = work->request_buf; + 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); - rsp_hdr->smb2_buf_length = - cpu_to_be32(smb2_hdr_size_no_buflen(conn->vals)); + *(__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; @@ -522,7 +520,7 @@ int init_smb2_rsp_hdr(struct ksmbd_work *work) */ int smb2_allocate_rsp_buf(struct ksmbd_work *work) { - struct smb2_hdr *hdr = work->request_buf; + struct smb2_hdr *hdr = smb2_get_msg(work->request_buf); size_t small_sz = MAX_CIFS_SMALL_BUFFER_SIZE; size_t large_sz = small_sz + work->conn->vals->max_trans_size; size_t sz = small_sz; @@ -534,7 +532,7 @@ int smb2_allocate_rsp_buf(struct ksmbd_work *work) if (cmd == SMB2_QUERY_INFO_HE) { struct smb2_query_info_req *req; - req = work->request_buf; + req = smb2_get_msg(work->request_buf); if (req->InfoType == SMB2_O_INFO_FILE && (req->FileInfoClass == FILE_FULL_EA_INFORMATION || req->FileInfoClass == FILE_ALL_INFORMATION)) @@ -561,7 +559,7 @@ int smb2_allocate_rsp_buf(struct ksmbd_work *work) */ int smb2_check_user_session(struct ksmbd_work *work) { - struct smb2_hdr *req_hdr = work->request_buf; + struct smb2_hdr *req_hdr = smb2_get_msg(work->request_buf); struct ksmbd_conn *conn = work->conn; unsigned int cmd = conn->ops->get_cmd_val(work); unsigned long long sess_id; @@ -642,7 +640,7 @@ int setup_async_work(struct ksmbd_work *work, void (*fn)(void **), void **arg) struct ksmbd_conn *conn = work->conn; int id; - rsp_hdr = work->response_buf; + rsp_hdr = smb2_get_msg(work->response_buf); rsp_hdr->Flags |= SMB2_FLAGS_ASYNC_COMMAND; id = ksmbd_acquire_async_msg_id(&conn->async_ida); @@ -674,7 +672,7 @@ void smb2_send_interim_resp(struct ksmbd_work *work, __le32 status) { struct smb2_hdr *rsp_hdr; - rsp_hdr = work->response_buf; + rsp_hdr = smb2_get_msg(work->response_buf); smb2_set_err_rsp(work); rsp_hdr->Status = status; @@ -715,17 +713,17 @@ static int smb2_get_dos_mode(struct kstat *stat, int attribute) int attr = 0; if (S_ISDIR(stat->mode)) { - attr = ATTR_DIRECTORY | - (attribute & (ATTR_HIDDEN | ATTR_SYSTEM)); + attr = FILE_ATTRIBUTE_DIRECTORY | + (attribute & (FILE_ATTRIBUTE_HIDDEN | FILE_ATTRIBUTE_SYSTEM)); } else { - attr = (attribute & 0x00005137) | ATTR_ARCHIVE; - attr &= ~(ATTR_DIRECTORY); + attr = (attribute & 0x00005137) | FILE_ATTRIBUTE_ARCHIVE; + attr &= ~(FILE_ATTRIBUTE_DIRECTORY); if (S_ISREG(stat->mode) && (server_conf.share_fake_fscaps & FILE_SUPPORTS_SPARSE_FILES)) - attr |= ATTR_SPARSE; + attr |= FILE_ATTRIBUTE_SPARSE_FILE; if (smb2_get_reparse_tag_special_file(stat->mode)) - attr |= ATTR_REPARSE; + attr |= FILE_ATTRIBUTE_REPARSE_POINT; } return attr; @@ -753,16 +751,16 @@ 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_ctx *pneg_ctxt, +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_ctx) + 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->Reserved1 = cpu_to_le32(0); + pneg_ctxt->Flags = cpu_to_le32(0); pneg_ctxt->CompressionAlgorithms[0] = comp_algo; } @@ -802,11 +800,11 @@ static void build_posix_ctxt(struct smb2_posix_neg_context *pneg_ctxt) } static void assemble_neg_contexts(struct ksmbd_conn *conn, - struct smb2_negotiate_rsp *rsp) + struct smb2_negotiate_rsp *rsp, + void *smb2_buf_len) { - /* +4 is to account for the RFC1001 len field */ char *pneg_ctxt = (char *)rsp + - le32_to_cpu(rsp->NegotiateContextOffset) + 4; + le32_to_cpu(rsp->NegotiateContextOffset); int neg_ctxt_cnt = 1; int ctxt_size; @@ -815,7 +813,7 @@ static void assemble_neg_contexts(struct ksmbd_conn *conn, 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(rsp, AUTH_GSS_PADDING); + 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); @@ -839,12 +837,12 @@ static void assemble_neg_contexts(struct ksmbd_conn *conn, ksmbd_debug(SMB, "assemble SMB2_COMPRESSION_CAPABILITIES context\n"); /* Temporarily set to SMB3_COMPRESS_NONE */ - build_compression_ctxt((struct smb2_compression_ctx *)pneg_ctxt, + 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_ctx) + 2; + ctxt_size += sizeof(struct smb2_compression_capabilities_context) + 2; /* Round to 8 byte boundary */ - pneg_ctxt += round_up(sizeof(struct smb2_compression_ctx) + 2, + pneg_ctxt += round_up(sizeof(struct smb2_compression_capabilities_context) + 2, 8); } @@ -869,7 +867,7 @@ static void assemble_neg_contexts(struct ksmbd_conn *conn, ctxt_size += sizeof(struct smb2_signing_capabilities) + 2; } - inc_rfc1001_len(rsp, ctxt_size); + inc_rfc1001_len(smb2_buf_len, ctxt_size); } static __le32 decode_preauth_ctxt(struct ksmbd_conn *conn, @@ -918,7 +916,7 @@ static void decode_encrypt_ctxt(struct ksmbd_conn *conn, } static void decode_compress_ctxt(struct ksmbd_conn *conn, - struct smb2_compression_ctx *pneg_ctxt) + struct smb2_compression_capabilities_context *pneg_ctxt) { conn->compress_algorithm = SMB3_COMPRESS_NONE; } @@ -939,8 +937,8 @@ static void decode_sign_cap_ctxt(struct ksmbd_conn *conn, } for (i = 0; i < sign_algo_cnt; i++) { - if (pneg_ctxt->SigningAlgorithms[i] == SIGNING_ALG_HMAC_SHA256 || - pneg_ctxt->SigningAlgorithms[i] == SIGNING_ALG_AES_CMAC) { + if (pneg_ctxt->SigningAlgorithms[i] == SIGNING_ALG_HMAC_SHA256_LE || + pneg_ctxt->SigningAlgorithms[i] == SIGNING_ALG_AES_CMAC_LE) { ksmbd_debug(SMB, "Signing Algorithm ID = 0x%x\n", pneg_ctxt->SigningAlgorithms[i]); conn->signing_negotiated = true; @@ -952,14 +950,14 @@ static void decode_sign_cap_ctxt(struct ksmbd_conn *conn, } static __le32 deassemble_neg_contexts(struct ksmbd_conn *conn, - struct smb2_negotiate_req *req) + struct smb2_negotiate_req *req, + int len_of_smb) { /* +4 is to account for the RFC1001 len field */ - struct smb2_neg_context *pctx = (struct smb2_neg_context *)((char *)req + 4); + struct smb2_neg_context *pctx = (struct smb2_neg_context *)req; int i = 0, len_of_ctxts; int offset = le32_to_cpu(req->NegotiateContextOffset); int neg_ctxt_cnt = le16_to_cpu(req->NegotiateContextCount); - int len_of_smb = be32_to_cpu(req->hdr.smb2_buf_length); __le32 status = STATUS_INVALID_PARAMETER; ksmbd_debug(SMB, "decoding %d negotiate contexts\n", neg_ctxt_cnt); @@ -1011,7 +1009,7 @@ static __le32 deassemble_neg_contexts(struct ksmbd_conn *conn, break; decode_compress_ctxt(conn, - (struct smb2_compression_ctx *)pctx); + (struct smb2_compression_capabilities_context *)pctx); } else if (pctx->ContextType == SMB2_NETNAME_NEGOTIATE_CONTEXT_ID) { ksmbd_debug(SMB, "deassemble SMB2_NETNAME_NEGOTIATE_CONTEXT_ID context\n"); @@ -1044,8 +1042,8 @@ static __le32 deassemble_neg_contexts(struct ksmbd_conn *conn, int smb2_handle_negotiate(struct ksmbd_work *work) { struct ksmbd_conn *conn = work->conn; - struct smb2_negotiate_req *req = work->request_buf; - struct smb2_negotiate_rsp *rsp = work->response_buf; + struct smb2_negotiate_req *req = smb2_get_msg(work->request_buf); + struct smb2_negotiate_rsp *rsp = smb2_get_msg(work->response_buf); int rc = 0; unsigned int smb2_buf_len, smb2_neg_size; __le32 status; @@ -1066,7 +1064,7 @@ int smb2_handle_negotiate(struct ksmbd_work *work) } smb2_buf_len = get_rfc1002_len(work->request_buf); - smb2_neg_size = offsetof(struct smb2_negotiate_req, Dialects) - 4; + smb2_neg_size = offsetof(struct smb2_negotiate_req, Dialects); if (smb2_neg_size > smb2_buf_len) { rsp->hdr.Status = STATUS_INVALID_PARAMETER; rc = -EINVAL; @@ -1115,7 +1113,8 @@ int smb2_handle_negotiate(struct ksmbd_work *work) goto err_out; } - status = deassemble_neg_contexts(conn, req); + status = deassemble_neg_contexts(conn, req, + get_rfc1002_len(work->request_buf)); if (status != STATUS_SUCCESS) { pr_err("deassemble_neg_contexts error(0x%x)\n", status); @@ -1135,7 +1134,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); + assemble_neg_contexts(conn, rsp, work->response_buf); break; case SMB302_PROT_ID: init_smb3_02_server(conn); @@ -1183,10 +1182,9 @@ int smb2_handle_negotiate(struct ksmbd_work *work) rsp->SecurityBufferOffset = cpu_to_le16(128); rsp->SecurityBufferLength = cpu_to_le16(AUTH_GSS_LENGTH); - ksmbd_copy_gss_neg_header(((char *)(&rsp->hdr) + - sizeof(rsp->hdr.smb2_buf_length)) + - le16_to_cpu(rsp->SecurityBufferOffset)); - inc_rfc1001_len(rsp, sizeof(struct smb2_negotiate_rsp) - + 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; @@ -1278,7 +1276,7 @@ static int ntlm_negotiate(struct ksmbd_work *work, struct negotiate_message *negblob, size_t negblob_len) { - struct smb2_sess_setup_rsp *rsp = work->response_buf; + struct smb2_sess_setup_rsp *rsp = smb2_get_msg(work->response_buf); struct challenge_message *chgblob; unsigned char *spnego_blob = NULL; u16 spnego_blob_len; @@ -1386,8 +1384,8 @@ static struct ksmbd_user *session_user(struct ksmbd_conn *conn, static int ntlm_authenticate(struct ksmbd_work *work) { - struct smb2_sess_setup_req *req = work->request_buf; - struct smb2_sess_setup_rsp *rsp = work->response_buf; + struct smb2_sess_setup_req *req = smb2_get_msg(work->request_buf); + struct smb2_sess_setup_rsp *rsp = smb2_get_msg(work->response_buf); struct ksmbd_conn *conn = work->conn; struct ksmbd_session *sess = work->sess; struct channel *chann = NULL; @@ -1410,7 +1408,7 @@ 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(rsp, spnego_blob_len - 1); + inc_rfc1001_len(work->response_buf, spnego_blob_len - 1); } user = session_user(conn, req); @@ -1522,8 +1520,8 @@ binding_session: #ifdef CONFIG_SMB_SERVER_KERBEROS5 static int krb5_authenticate(struct ksmbd_work *work) { - struct smb2_sess_setup_req *req = work->request_buf; - struct smb2_sess_setup_rsp *rsp = work->response_buf; + struct smb2_sess_setup_req *req = smb2_get_msg(work->request_buf); + struct smb2_sess_setup_rsp *rsp = smb2_get_msg(work->response_buf); struct ksmbd_conn *conn = work->conn; struct ksmbd_session *sess = work->sess; char *in_blob, *out_blob; @@ -1538,8 +1536,7 @@ static int krb5_authenticate(struct ksmbd_work *work) out_blob = (char *)&rsp->hdr.ProtocolId + le16_to_cpu(rsp->SecurityBufferOffset); out_len = work->response_sz - - offsetof(struct smb2_hdr, smb2_buf_length) - - le16_to_cpu(rsp->SecurityBufferOffset); + (le16_to_cpu(rsp->SecurityBufferOffset) + 4); /* Check previous session */ prev_sess_id = le64_to_cpu(req->PreviousSessionId); @@ -1556,7 +1553,7 @@ static int krb5_authenticate(struct ksmbd_work *work) return -EINVAL; } rsp->SecurityBufferLength = cpu_to_le16(out_len); - inc_rfc1001_len(rsp, out_len - 1); + inc_rfc1001_len(work->response_buf, out_len - 1); if ((conn->sign || server_conf.enforced_signing) || (req->SecurityMode & SMB2_NEGOTIATE_SIGNING_REQUIRED)) @@ -1612,8 +1609,8 @@ static int krb5_authenticate(struct ksmbd_work *work) int smb2_sess_setup(struct ksmbd_work *work) { struct ksmbd_conn *conn = work->conn; - struct smb2_sess_setup_req *req = work->request_buf; - struct smb2_sess_setup_rsp *rsp = work->response_buf; + struct smb2_sess_setup_req *req = smb2_get_msg(work->request_buf); + struct smb2_sess_setup_rsp *rsp = smb2_get_msg(work->response_buf); struct ksmbd_session *sess; struct negotiate_message *negblob; unsigned int negblob_len, negblob_off; @@ -1625,7 +1622,7 @@ int smb2_sess_setup(struct ksmbd_work *work) rsp->SessionFlags = 0; rsp->SecurityBufferOffset = cpu_to_le16(72); rsp->SecurityBufferLength = 0; - inc_rfc1001_len(rsp, 9); + inc_rfc1001_len(work->response_buf, 9); if (!req->hdr.SessionId) { sess = ksmbd_smb2_session_create(); @@ -1699,7 +1696,7 @@ int smb2_sess_setup(struct ksmbd_work *work) negblob_off = le16_to_cpu(req->SecurityBufferOffset); negblob_len = le16_to_cpu(req->SecurityBufferLength); - if (negblob_off < (offsetof(struct smb2_sess_setup_req, Buffer) - 4) || + if (negblob_off < offsetof(struct smb2_sess_setup_req, Buffer) || negblob_len < offsetof(struct negotiate_message, NegotiateFlags)) return -EINVAL; @@ -1739,7 +1736,8 @@ int smb2_sess_setup(struct ksmbd_work *work) * Note: here total size -1 is done as an * adjustment for 0 size blob */ - inc_rfc1001_len(rsp, le16_to_cpu(rsp->SecurityBufferLength) - 1); + inc_rfc1001_len(work->response_buf, + le16_to_cpu(rsp->SecurityBufferLength) - 1); } else if (negblob->MessageType == NtLmAuthenticate) { rc = ntlm_authenticate(work); @@ -1828,8 +1826,8 @@ out_err: int smb2_tree_connect(struct ksmbd_work *work) { struct ksmbd_conn *conn = work->conn; - struct smb2_tree_connect_req *req = work->request_buf; - struct smb2_tree_connect_rsp *rsp = work->response_buf; + struct smb2_tree_connect_req *req = smb2_get_msg(work->request_buf); + struct smb2_tree_connect_rsp *rsp = smb2_get_msg(work->response_buf); struct ksmbd_session *sess = work->sess; char *treename = NULL, *name = NULL; struct ksmbd_tree_conn_status status; @@ -1894,7 +1892,7 @@ out_err1: rsp->Reserved = 0; /* default manual caching */ rsp->ShareFlags = SMB2_SHAREFLAG_MANUAL_CACHING; - inc_rfc1001_len(rsp, 16); + inc_rfc1001_len(work->response_buf, 16); if (!IS_ERR(treename)) kfree(treename); @@ -1999,17 +1997,18 @@ static int smb2_create_open_flags(bool file_present, __le32 access, */ int smb2_tree_disconnect(struct ksmbd_work *work) { - struct smb2_tree_disconnect_rsp *rsp = work->response_buf; + struct smb2_tree_disconnect_rsp *rsp = smb2_get_msg(work->response_buf); struct ksmbd_session *sess = work->sess; struct ksmbd_tree_connect *tcon = work->tcon; rsp->StructureSize = cpu_to_le16(4); - inc_rfc1001_len(rsp, 4); + inc_rfc1001_len(work->response_buf, 4); ksmbd_debug(SMB, "request\n"); if (!tcon) { - struct smb2_tree_disconnect_req *req = work->request_buf; + struct smb2_tree_disconnect_req *req = + smb2_get_msg(work->request_buf); ksmbd_debug(SMB, "Invalid tid %d\n", req->hdr.Id.SyncId.TreeId); rsp->hdr.Status = STATUS_NETWORK_NAME_DELETED; @@ -2031,11 +2030,11 @@ int smb2_tree_disconnect(struct ksmbd_work *work) int smb2_session_logoff(struct ksmbd_work *work) { struct ksmbd_conn *conn = work->conn; - struct smb2_logoff_rsp *rsp = work->response_buf; + struct smb2_logoff_rsp *rsp = smb2_get_msg(work->response_buf); struct ksmbd_session *sess = work->sess; rsp->StructureSize = cpu_to_le16(4); - inc_rfc1001_len(rsp, 4); + inc_rfc1001_len(work->response_buf, 4); ksmbd_debug(SMB, "request\n"); @@ -2048,7 +2047,7 @@ int smb2_session_logoff(struct ksmbd_work *work) ksmbd_conn_wait_idle(conn); if (ksmbd_tree_conn_session_logoff(sess)) { - struct smb2_logoff_req *req = work->request_buf; + struct smb2_logoff_req *req = smb2_get_msg(work->request_buf); ksmbd_debug(SMB, "Invalid tid %d\n", req->hdr.Id.SyncId.TreeId); rsp->hdr.Status = STATUS_NETWORK_NAME_DELETED; @@ -2075,8 +2074,8 @@ int smb2_session_logoff(struct ksmbd_work *work) */ static noinline int create_smb2_pipe(struct ksmbd_work *work) { - struct smb2_create_rsp *rsp = work->response_buf; - struct smb2_create_req *req = work->request_buf; + struct smb2_create_rsp *rsp = smb2_get_msg(work->response_buf); + struct smb2_create_req *req = smb2_get_msg(work->request_buf); int id; int err; char *name; @@ -2099,7 +2098,7 @@ static noinline int create_smb2_pipe(struct ksmbd_work *work) rsp->hdr.Status = STATUS_SUCCESS; rsp->StructureSize = cpu_to_le16(89); rsp->OplockLevel = SMB2_OPLOCK_LEVEL_NONE; - rsp->Reserved = 0; + rsp->Flags = 0; rsp->CreateAction = cpu_to_le32(FILE_OPENED); rsp->CreationTime = cpu_to_le64(0); @@ -2107,14 +2106,14 @@ static noinline int create_smb2_pipe(struct ksmbd_work *work) rsp->ChangeTime = cpu_to_le64(0); rsp->AllocationSize = cpu_to_le64(0); rsp->EndofFile = cpu_to_le64(0); - rsp->FileAttributes = ATTR_NORMAL_LE; + rsp->FileAttributes = FILE_ATTRIBUTE_NORMAL_LE; rsp->Reserved2 = 0; rsp->VolatileFileId = cpu_to_le64(id); rsp->PersistentFileId = 0; rsp->CreateContextsOffset = 0; rsp->CreateContextsLength = 0; - inc_rfc1001_len(rsp, 88); /* StructureSize - 1*/ + inc_rfc1001_len(work->response_buf, 88); /* StructureSize - 1*/ kfree(name); return 0; @@ -2353,7 +2352,7 @@ static void smb2_update_xattrs(struct ksmbd_tree_connect *tcon, struct xattr_dos_attrib da; int rc; - fp->f_ci->m_fattr &= ~(ATTR_HIDDEN_LE | ATTR_SYSTEM_LE); + fp->f_ci->m_fattr &= ~(FILE_ATTRIBUTE_HIDDEN_LE | FILE_ATTRIBUTE_SYSTEM_LE); /* get FileAttributes from XATTR_NAME_DOS_ATTRIBUTE */ if (!test_share_config_flag(tcon->share_conf, @@ -2463,7 +2462,7 @@ int smb2_open(struct ksmbd_work *work) struct ksmbd_session *sess = work->sess; struct ksmbd_tree_connect *tcon = work->tcon; struct smb2_create_req *req; - struct smb2_create_rsp *rsp, *rsp_org; + struct smb2_create_rsp *rsp; struct path path; struct ksmbd_share_config *share = tcon->share_conf; struct ksmbd_file *fp = NULL; @@ -2489,7 +2488,6 @@ int smb2_open(struct ksmbd_work *work) umode_t posix_mode = 0; __le32 daccess, maximal_access = 0; - rsp_org = work->response_buf; WORK_BUFFERS(work, req, rsp); if (req->hdr.NextCommand && !work->next_smb2_rcv_hdr_off && @@ -2559,7 +2557,7 @@ int smb2_open(struct ksmbd_work *work) if (req_op_level == SMB2_OPLOCK_LEVEL_LEASE) lc = parse_lease_state(req); - if (le32_to_cpu(req->ImpersonationLevel) > le32_to_cpu(IL_DELEGATE_LE)) { + 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; @@ -2567,7 +2565,7 @@ int smb2_open(struct ksmbd_work *work) goto err_out1; } - if (req->CreateOptions && !(req->CreateOptions & CREATE_OPTIONS_MASK)) { + if (req->CreateOptions && !(req->CreateOptions & CREATE_OPTIONS_MASK_LE)) { pr_err("Invalid create options : 0x%x\n", le32_to_cpu(req->CreateOptions)); rc = -EINVAL; @@ -2609,7 +2607,7 @@ int smb2_open(struct ksmbd_work *work) goto err_out1; } - if (req->FileAttributes && !(req->FileAttributes & ATTR_MASK_LE)) { + if (req->FileAttributes && !(req->FileAttributes & FILE_ATTRIBUTE_MASK_LE)) { pr_err("Invalid file attribute : 0x%x\n", le32_to_cpu(req->FileAttributes)); rc = -EINVAL; @@ -2740,7 +2738,7 @@ int smb2_open(struct ksmbd_work *work) } if (req->CreateOptions & FILE_DIRECTORY_FILE_LE && - req->FileAttributes & ATTR_NORMAL_LE) { + req->FileAttributes & FILE_ATTRIBUTE_NORMAL_LE) { rsp->hdr.Status = STATUS_NOT_A_DIRECTORY; rc = -EIO; } @@ -3119,7 +3117,7 @@ int smb2_open(struct ksmbd_work *work) opinfo = rcu_dereference(fp->f_opinfo); rsp->OplockLevel = opinfo != NULL ? opinfo->level : 0; rcu_read_unlock(); - rsp->Reserved = 0; + rsp->Flags = 0; rsp->CreateAction = cpu_to_le32(file_info); rsp->CreationTime = cpu_to_le64(fp->create_time); time = ksmbd_UnixTimeToNT(stat.atime); @@ -3140,7 +3138,7 @@ int smb2_open(struct ksmbd_work *work) rsp->CreateContextsOffset = 0; rsp->CreateContextsLength = 0; - inc_rfc1001_len(rsp_org, 88); /* StructureSize - 1*/ + inc_rfc1001_len(work->response_buf, 88); /* StructureSize - 1*/ /* If lease is request send lease context response */ if (opinfo && opinfo->is_lease) { @@ -3155,7 +3153,8 @@ 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(rsp_org, conn->vals->create_lease_size); + inc_rfc1001_len(work->response_buf, + conn->vals->create_lease_size); next_ptr = &lease_ccontext->Next; next_off = conn->vals->create_lease_size; } @@ -3175,7 +3174,8 @@ 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(rsp_org, conn->vals->create_mxac_size); + inc_rfc1001_len(work->response_buf, + conn->vals->create_mxac_size); if (next_ptr) *next_ptr = cpu_to_le32(next_off); next_ptr = &mxac_ccontext->Next; @@ -3193,7 +3193,8 @@ 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(rsp_org, conn->vals->create_disk_id_size); + inc_rfc1001_len(work->response_buf, + conn->vals->create_disk_id_size); if (next_ptr) *next_ptr = cpu_to_le32(next_off); next_ptr = &disk_id_ccontext->Next; @@ -3207,15 +3208,15 @@ int smb2_open(struct ksmbd_work *work) fp); le32_add_cpu(&rsp->CreateContextsLength, conn->vals->create_posix_size); - inc_rfc1001_len(rsp_org, conn->vals->create_posix_size); + inc_rfc1001_len(work->response_buf, + conn->vals->create_posix_size); if (next_ptr) *next_ptr = cpu_to_le32(next_off); } if (contxt_cnt > 0) { rsp->CreateContextsOffset = - cpu_to_le32(offsetof(struct smb2_create_rsp, Buffer) - - 4); + cpu_to_le32(offsetof(struct smb2_create_rsp, Buffer)); } err_out: @@ -3422,9 +3423,9 @@ static int smb2_populate_readdir_entry(struct ksmbd_conn *conn, int info_level, ffdinfo->EaSize = smb2_get_reparse_tag_special_file(ksmbd_kstat->kstat->mode); if (ffdinfo->EaSize) - ffdinfo->ExtFileAttributes = ATTR_REPARSE_POINT_LE; + ffdinfo->ExtFileAttributes = FILE_ATTRIBUTE_REPARSE_POINT_LE; if (d_info->hide_dot_file && d_info->name[0] == '.') - ffdinfo->ExtFileAttributes |= ATTR_HIDDEN_LE; + ffdinfo->ExtFileAttributes |= FILE_ATTRIBUTE_HIDDEN_LE; memcpy(ffdinfo->FileName, conv_name, conv_len); ffdinfo->NextEntryOffset = cpu_to_le32(next_entry_offset); break; @@ -3438,11 +3439,11 @@ static int smb2_populate_readdir_entry(struct ksmbd_conn *conn, int info_level, fbdinfo->EaSize = smb2_get_reparse_tag_special_file(ksmbd_kstat->kstat->mode); if (fbdinfo->EaSize) - fbdinfo->ExtFileAttributes = ATTR_REPARSE_POINT_LE; + fbdinfo->ExtFileAttributes = FILE_ATTRIBUTE_REPARSE_POINT_LE; fbdinfo->ShortNameLength = 0; fbdinfo->Reserved = 0; if (d_info->hide_dot_file && d_info->name[0] == '.') - fbdinfo->ExtFileAttributes |= ATTR_HIDDEN_LE; + fbdinfo->ExtFileAttributes |= FILE_ATTRIBUTE_HIDDEN_LE; memcpy(fbdinfo->FileName, conv_name, conv_len); fbdinfo->NextEntryOffset = cpu_to_le32(next_entry_offset); break; @@ -3454,7 +3455,7 @@ static int smb2_populate_readdir_entry(struct ksmbd_conn *conn, int info_level, fdinfo = (struct file_directory_info *)kstat; fdinfo->FileNameLength = cpu_to_le32(conv_len); if (d_info->hide_dot_file && d_info->name[0] == '.') - fdinfo->ExtFileAttributes |= ATTR_HIDDEN_LE; + fdinfo->ExtFileAttributes |= FILE_ATTRIBUTE_HIDDEN_LE; memcpy(fdinfo->FileName, conv_name, conv_len); fdinfo->NextEntryOffset = cpu_to_le32(next_entry_offset); break; @@ -3478,11 +3479,11 @@ static int smb2_populate_readdir_entry(struct ksmbd_conn *conn, int info_level, dinfo->EaSize = smb2_get_reparse_tag_special_file(ksmbd_kstat->kstat->mode); if (dinfo->EaSize) - dinfo->ExtFileAttributes = ATTR_REPARSE_POINT_LE; + dinfo->ExtFileAttributes = FILE_ATTRIBUTE_REPARSE_POINT_LE; dinfo->Reserved = 0; dinfo->UniqueId = cpu_to_le64(ksmbd_kstat->kstat->ino); if (d_info->hide_dot_file && d_info->name[0] == '.') - dinfo->ExtFileAttributes |= ATTR_HIDDEN_LE; + dinfo->ExtFileAttributes |= FILE_ATTRIBUTE_HIDDEN_LE; memcpy(dinfo->FileName, conv_name, conv_len); dinfo->NextEntryOffset = cpu_to_le32(next_entry_offset); break; @@ -3496,13 +3497,13 @@ static int smb2_populate_readdir_entry(struct ksmbd_conn *conn, int info_level, fibdinfo->EaSize = smb2_get_reparse_tag_special_file(ksmbd_kstat->kstat->mode); if (fibdinfo->EaSize) - fibdinfo->ExtFileAttributes = ATTR_REPARSE_POINT_LE; + fibdinfo->ExtFileAttributes = FILE_ATTRIBUTE_REPARSE_POINT_LE; fibdinfo->UniqueId = cpu_to_le64(ksmbd_kstat->kstat->ino); fibdinfo->ShortNameLength = 0; fibdinfo->Reserved = 0; fibdinfo->Reserved2 = cpu_to_le16(0); if (d_info->hide_dot_file && d_info->name[0] == '.') - fibdinfo->ExtFileAttributes |= ATTR_HIDDEN_LE; + fibdinfo->ExtFileAttributes |= FILE_ATTRIBUTE_HIDDEN_LE; memcpy(fibdinfo->FileName, conv_name, conv_len); fibdinfo->NextEntryOffset = cpu_to_le32(next_entry_offset); break; @@ -3528,9 +3529,10 @@ static int smb2_populate_readdir_entry(struct ksmbd_conn *conn, int info_level, posix_info->Mode = cpu_to_le32(ksmbd_kstat->kstat->mode); posix_info->Inode = cpu_to_le64(ksmbd_kstat->kstat->ino); posix_info->DosAttributes = - S_ISDIR(ksmbd_kstat->kstat->mode) ? ATTR_DIRECTORY_LE : ATTR_ARCHIVE_LE; + S_ISDIR(ksmbd_kstat->kstat->mode) ? + FILE_ATTRIBUTE_DIRECTORY_LE : FILE_ATTRIBUTE_ARCHIVE_LE; if (d_info->hide_dot_file && d_info->name[0] == '.') - posix_info->DosAttributes |= ATTR_HIDDEN_LE; + posix_info->DosAttributes |= FILE_ATTRIBUTE_HIDDEN_LE; id_to_sid(from_kuid_munged(&init_user_ns, ksmbd_kstat->kstat->uid), SIDNFS_USER, (struct smb_sid *)&posix_info->SidBuffer[0]); id_to_sid(from_kgid_munged(&init_user_ns, ksmbd_kstat->kstat->gid), @@ -3816,7 +3818,7 @@ int smb2_query_dir(struct ksmbd_work *work) { struct ksmbd_conn *conn = work->conn; struct smb2_query_directory_req *req; - struct smb2_query_directory_rsp *rsp, *rsp_org; + struct smb2_query_directory_rsp *rsp; struct ksmbd_share_config *share = work->tcon->share_conf; struct ksmbd_file *dir_fp = NULL; struct ksmbd_dir_info d_info; @@ -3826,7 +3828,6 @@ int smb2_query_dir(struct ksmbd_work *work) int buffer_sz; struct smb2_query_dir_private query_dir_private = {NULL, }; - rsp_org = work->response_buf; WORK_BUFFERS(work, req, rsp); if (ksmbd_override_fsids(work)) { @@ -3947,7 +3948,7 @@ 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(rsp_org, 9); + inc_rfc1001_len(work->response_buf, 9); } else { ((struct file_directory_info *) ((char *)rsp->Buffer + d_info.last_entry_offset)) @@ -3956,7 +3957,7 @@ 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(rsp_org, 8 + d_info.data_count); + inc_rfc1001_len(work->response_buf, 8 + d_info.data_count); } kfree(srch_ptr); @@ -3999,26 +4000,28 @@ err_out2: * Return: 0 on success, otherwise error */ static int buffer_check_err(int reqOutputBufferLength, - struct smb2_query_info_rsp *rsp, int infoclass_size) + struct smb2_query_info_rsp *rsp, + void *rsp_org, int infoclass_size) { if (reqOutputBufferLength < le32_to_cpu(rsp->OutputBufferLength)) { if (reqOutputBufferLength < infoclass_size) { pr_err("Invalid Buffer Size Requested\n"); rsp->hdr.Status = STATUS_INFO_LENGTH_MISMATCH; - rsp->hdr.smb2_buf_length = cpu_to_be32(sizeof(struct smb2_hdr) - 4); + *(__be32 *)rsp_org = cpu_to_be32(sizeof(struct smb2_hdr)); return -EINVAL; } ksmbd_debug(SMB, "Buffer Overflow\n"); rsp->hdr.Status = STATUS_BUFFER_OVERFLOW; - rsp->hdr.smb2_buf_length = cpu_to_be32(sizeof(struct smb2_hdr) - 4 + + *(__be32 *)rsp_org = cpu_to_be32(sizeof(struct smb2_hdr) + reqOutputBufferLength); rsp->OutputBufferLength = cpu_to_le32(reqOutputBufferLength); } return 0; } -static void get_standard_info_pipe(struct smb2_query_info_rsp *rsp) +static void get_standard_info_pipe(struct smb2_query_info_rsp *rsp, + void *rsp_org) { struct smb2_file_standard_info *sinfo; @@ -4031,10 +4034,11 @@ 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, 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) +static void get_internal_info_pipe(struct smb2_query_info_rsp *rsp, u64 num, + void *rsp_org) { struct smb2_file_internal_info *file_info; @@ -4044,12 +4048,13 @@ 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, sizeof(struct smb2_file_internal_info)); + inc_rfc1001_len(rsp_org, sizeof(struct smb2_file_internal_info)); } static int smb2_get_info_file_pipe(struct ksmbd_session *sess, struct smb2_query_info_req *req, - struct smb2_query_info_rsp *rsp) + struct smb2_query_info_rsp *rsp, + void *rsp_org) { u64 id; int rc; @@ -4067,14 +4072,16 @@ static int smb2_get_info_file_pipe(struct ksmbd_session *sess, switch (req->FileInfoClass) { case FILE_STANDARD_INFORMATION: - get_standard_info_pipe(rsp); + get_standard_info_pipe(rsp, rsp_org); rc = buffer_check_err(le32_to_cpu(req->OutputBufferLength), - rsp, FILE_STANDARD_INFORMATION_SIZE); + rsp, rsp_org, + FILE_STANDARD_INFORMATION_SIZE); break; case FILE_INTERNAL_INFORMATION: - get_internal_info_pipe(rsp, id); + get_internal_info_pipe(rsp, id, rsp_org); rc = buffer_check_err(le32_to_cpu(req->OutputBufferLength), - rsp, FILE_INTERNAL_INFORMATION_SIZE); + rsp, rsp_org, + FILE_INTERNAL_INFORMATION_SIZE); break; default: ksmbd_debug(SMB, "smb2_info_file_pipe for %u not supported\n", @@ -4688,7 +4695,7 @@ static int find_file_posix_info(struct smb2_query_info_rsp *rsp, static int smb2_get_info_file(struct ksmbd_work *work, struct smb2_query_info_req *req, - struct smb2_query_info_rsp *rsp, void *rsp_org) + struct smb2_query_info_rsp *rsp) { struct ksmbd_file *fp; int fileinfoclass = 0; @@ -4699,7 +4706,8 @@ static int smb2_get_info_file(struct ksmbd_work *work, if (test_share_config_flag(work->tcon->share_conf, KSMBD_SHARE_FLAG_PIPE)) { /* smb2 info file called for pipe */ - return smb2_get_info_file_pipe(work->sess, req, rsp); + return smb2_get_info_file_pipe(work->sess, req, rsp, + work->response_buf); } if (work->next_smb2_rcv_hdr_off) { @@ -4724,77 +4732,77 @@ static int smb2_get_info_file(struct ksmbd_work *work, switch (fileinfoclass) { case FILE_ACCESS_INFORMATION: - get_file_access_info(rsp, fp, rsp_org); + 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, rsp_org); + 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, rsp_org); + 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, rsp_org); + 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, rsp_org); + 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, rsp_org); + 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, rsp_org); + 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, rsp_org); + 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, rsp_org); + 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, rsp_org); + 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, rsp_org); + 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, rsp_org); + 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, rsp_org); + 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, rsp_org); + 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, rsp_org); + 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: @@ -4802,7 +4810,7 @@ static int smb2_get_info_file(struct ksmbd_work *work, pr_err("client doesn't negotiate with SMB3.1.1 POSIX Extensions\n"); rc = -EOPNOTSUPP; } else { - rc = find_file_posix_info(rsp, fp, rsp_org); + rc = find_file_posix_info(rsp, fp, work->response_buf); file_infoclass_size = sizeof(struct smb311_posix_qinfo); } break; @@ -4813,7 +4821,7 @@ static int smb2_get_info_file(struct ksmbd_work *work, } if (!rc) rc = buffer_check_err(le32_to_cpu(req->OutputBufferLength), - rsp, + rsp, work->response_buf, file_infoclass_size); ksmbd_fd_put(work, fp); return rc; @@ -4821,7 +4829,7 @@ static int smb2_get_info_file(struct ksmbd_work *work, static int smb2_get_info_filesystem(struct ksmbd_work *work, struct smb2_query_info_req *req, - struct smb2_query_info_rsp *rsp, void *rsp_org) + struct smb2_query_info_rsp *rsp) { struct ksmbd_session *sess = work->sess; struct ksmbd_conn *conn = sess->conn; @@ -4857,7 +4865,7 @@ 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(rsp_org, 8); + inc_rfc1001_len(work->response_buf, 8); fs_infoclass_size = FS_DEVICE_INFORMATION_SIZE; break; } @@ -4883,7 +4891,7 @@ 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(rsp_org, sz); + inc_rfc1001_len(work->response_buf, sz); fs_infoclass_size = FS_ATTRIBUTE_INFORMATION_SIZE; break; } @@ -4891,11 +4899,18 @@ static int smb2_get_info_filesystem(struct ksmbd_work *work, { struct filesystem_vol_info *info; size_t sz; + unsigned int serial_crc = 0; info = (struct filesystem_vol_info *)(rsp->Buffer); info->VolumeCreationTime = 0; + serial_crc = crc32_le(serial_crc, share->name, + strlen(share->name)); + serial_crc = crc32_le(serial_crc, share->path, + strlen(share->path)); + serial_crc = crc32_le(serial_crc, ksmbd_netbios_name(), + strlen(ksmbd_netbios_name())); /* Taking dummy value of serial number*/ - info->SerialNumber = cpu_to_le32(0xbc3ac512); + info->SerialNumber = cpu_to_le32(serial_crc); len = smbConvertToUTF16((__le16 *)info->VolumeLabel, share->name, PATH_MAX, conn->local_nls, 0); @@ -4904,7 +4919,7 @@ 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(rsp_org, sz); + inc_rfc1001_len(work->response_buf, sz); fs_infoclass_size = FS_VOLUME_INFORMATION_SIZE; break; } @@ -4918,7 +4933,7 @@ 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(rsp_org, 24); + inc_rfc1001_len(work->response_buf, 24); fs_infoclass_size = FS_SIZE_INFORMATION_SIZE; break; } @@ -4935,7 +4950,7 @@ 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(rsp_org, 32); + inc_rfc1001_len(work->response_buf, 32); fs_infoclass_size = FS_FULL_SIZE_INFORMATION_SIZE; break; } @@ -4956,7 +4971,7 @@ 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(rsp_org, 64); + inc_rfc1001_len(work->response_buf, 64); fs_infoclass_size = FS_OBJECT_ID_INFORMATION_SIZE; break; } @@ -4977,7 +4992,7 @@ 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(rsp_org, 28); + inc_rfc1001_len(work->response_buf, 28); fs_infoclass_size = FS_SECTOR_SIZE_INFORMATION_SIZE; break; } @@ -4999,7 +5014,7 @@ 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(rsp_org, 48); + inc_rfc1001_len(work->response_buf, 48); fs_infoclass_size = FS_CONTROL_INFORMATION_SIZE; break; } @@ -5020,7 +5035,7 @@ 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(rsp_org, 56); + inc_rfc1001_len(work->response_buf, 56); fs_infoclass_size = FS_POSIX_INFORMATION_SIZE; } break; @@ -5030,7 +5045,7 @@ static int smb2_get_info_filesystem(struct ksmbd_work *work, return -EOPNOTSUPP; } rc = buffer_check_err(le32_to_cpu(req->OutputBufferLength), - rsp, + rsp, work->response_buf, fs_infoclass_size); path_put(&path); return rc; @@ -5038,7 +5053,7 @@ static int smb2_get_info_filesystem(struct ksmbd_work *work, static int smb2_get_info_sec(struct ksmbd_work *work, struct smb2_query_info_req *req, - struct smb2_query_info_rsp *rsp, void *rsp_org) + struct smb2_query_info_rsp *rsp) { struct ksmbd_file *fp; struct user_namespace *user_ns; @@ -5065,7 +5080,7 @@ static int smb2_get_info_sec(struct ksmbd_work *work, secdesclen = sizeof(struct smb_ntsd); rsp->OutputBufferLength = cpu_to_le32(secdesclen); - inc_rfc1001_len(rsp_org, secdesclen); + inc_rfc1001_len(work->response_buf, secdesclen); return 0; } @@ -5107,7 +5122,7 @@ static int smb2_get_info_sec(struct ksmbd_work *work, return rc; rsp->OutputBufferLength = cpu_to_le32(secdesclen); - inc_rfc1001_len(rsp_org, secdesclen); + inc_rfc1001_len(work->response_buf, secdesclen); return 0; } @@ -5120,10 +5135,9 @@ static int smb2_get_info_sec(struct ksmbd_work *work, int smb2_query_info(struct ksmbd_work *work) { struct smb2_query_info_req *req; - struct smb2_query_info_rsp *rsp, *rsp_org; + struct smb2_query_info_rsp *rsp; int rc = 0; - rsp_org = work->response_buf; WORK_BUFFERS(work, req, rsp); ksmbd_debug(SMB, "GOT query info request\n"); @@ -5131,15 +5145,15 @@ int smb2_query_info(struct ksmbd_work *work) switch (req->InfoType) { case SMB2_O_INFO_FILE: ksmbd_debug(SMB, "GOT SMB2_O_INFO_FILE\n"); - rc = smb2_get_info_file(work, req, rsp, (void *)rsp_org); + rc = smb2_get_info_file(work, req, rsp); break; case SMB2_O_INFO_FILESYSTEM: ksmbd_debug(SMB, "GOT SMB2_O_INFO_FILESYSTEM\n"); - rc = smb2_get_info_filesystem(work, req, rsp, (void *)rsp_org); + rc = smb2_get_info_filesystem(work, req, rsp); break; case SMB2_O_INFO_SECURITY: ksmbd_debug(SMB, "GOT SMB2_O_INFO_SECURITY\n"); - rc = smb2_get_info_sec(work, req, rsp, (void *)rsp_org); + rc = smb2_get_info_sec(work, req, rsp); break; default: ksmbd_debug(SMB, "InfoType %d not supported yet\n", @@ -5164,7 +5178,7 @@ int smb2_query_info(struct ksmbd_work *work) } rsp->StructureSize = cpu_to_le16(9); rsp->OutputBufferOffset = cpu_to_le16(72); - inc_rfc1001_len(rsp_org, 8); + inc_rfc1001_len(work->response_buf, 8); return 0; } @@ -5177,8 +5191,8 @@ int smb2_query_info(struct ksmbd_work *work) static noinline int smb2_close_pipe(struct ksmbd_work *work) { u64 id; - struct smb2_close_req *req = work->request_buf; - struct smb2_close_rsp *rsp = work->response_buf; + struct smb2_close_req *req = smb2_get_msg(work->request_buf); + struct smb2_close_rsp *rsp = smb2_get_msg(work->response_buf); id = le64_to_cpu(req->VolatileFileId); ksmbd_session_rpc_close(work->sess, id); @@ -5193,7 +5207,7 @@ static noinline int smb2_close_pipe(struct ksmbd_work *work) rsp->AllocationSize = 0; rsp->EndOfFile = 0; rsp->Attributes = 0; - inc_rfc1001_len(rsp, 60); + inc_rfc1001_len(work->response_buf, 60); return 0; } @@ -5209,14 +5223,12 @@ int smb2_close(struct ksmbd_work *work) u64 sess_id; struct smb2_close_req *req; struct smb2_close_rsp *rsp; - struct smb2_close_rsp *rsp_org; struct ksmbd_conn *conn = work->conn; struct ksmbd_file *fp; struct inode *inode; u64 time; int err = 0; - rsp_org = work->response_buf; WORK_BUFFERS(work, req, rsp); if (test_share_config_flag(work->tcon->share_conf, @@ -5306,7 +5318,7 @@ out: rsp->hdr.Status = STATUS_FILE_CLOSED; smb2_set_err_rsp(work); } else { - inc_rfc1001_len(rsp_org, 60); + inc_rfc1001_len(work->response_buf, 60); } return 0; @@ -5320,11 +5332,11 @@ out: */ int smb2_echo(struct ksmbd_work *work) { - struct smb2_echo_rsp *rsp = work->response_buf; + struct smb2_echo_rsp *rsp = smb2_get_msg(work->response_buf); rsp->StructureSize = cpu_to_le16(4); rsp->Reserved = 0; - inc_rfc1001_len(rsp, 4); + inc_rfc1001_len(work->response_buf, 4); return 0; } @@ -5566,14 +5578,14 @@ static int set_file_basic_info(struct ksmbd_file *fp, if (file_info->Attributes) { if (!S_ISDIR(inode->i_mode) && - file_info->Attributes & ATTR_DIRECTORY_LE) { + file_info->Attributes & FILE_ATTRIBUTE_DIRECTORY_LE) { pr_err("can't change a file to a directory\n"); return -EINVAL; } - if (!(S_ISDIR(inode->i_mode) && file_info->Attributes == ATTR_NORMAL_LE)) + if (!(S_ISDIR(inode->i_mode) && file_info->Attributes == FILE_ATTRIBUTE_NORMAL_LE)) fp->f_ci->m_fattr = file_info->Attributes | - (fp->f_ci->m_fattr & ATTR_DIRECTORY_LE); + (fp->f_ci->m_fattr & FILE_ATTRIBUTE_DIRECTORY_LE); } if (test_share_config_flag(share, KSMBD_SHARE_FLAG_STORE_DOS_ATTRS) && @@ -5794,9 +5806,7 @@ static int set_file_mode_info(struct ksmbd_file *fp, mode = file_info->Mode; - if ((mode & ~FILE_MODE_INFO_MASK) || - (mode & FILE_SYNCHRONOUS_IO_ALERT_LE && - mode & FILE_SYNCHRONOUS_IO_NONALERT_LE)) { + if ((mode & ~FILE_MODE_INFO_MASK)) { pr_err("Mode is not valid : 0x%x\n", le32_to_cpu(mode)); return -EINVAL; } @@ -5943,14 +5953,13 @@ static int smb2_set_info_sec(struct ksmbd_file *fp, int addition_info, int smb2_set_info(struct ksmbd_work *work) { struct smb2_set_info_req *req; - struct smb2_set_info_rsp *rsp, *rsp_org; + struct smb2_set_info_rsp *rsp; struct ksmbd_file *fp; int rc = 0; unsigned int id = KSMBD_NO_FID, pid = KSMBD_NO_FID; ksmbd_debug(SMB, "Received set info request\n"); - rsp_org = work->response_buf; if (work->next_smb2_rcv_hdr_off) { req = ksmbd_req_buf_next(work); rsp = ksmbd_resp_buf_next(work); @@ -5961,8 +5970,8 @@ int smb2_set_info(struct ksmbd_work *work) pid = work->compound_pfid; } } else { - req = work->request_buf; - rsp = work->response_buf; + req = smb2_get_msg(work->request_buf); + rsp = smb2_get_msg(work->response_buf); } if (!has_file_id(id)) { @@ -6002,7 +6011,7 @@ int smb2_set_info(struct ksmbd_work *work) goto err_out; rsp->StructureSize = cpu_to_le16(2); - inc_rfc1001_len(rsp_org, 2); + inc_rfc1001_len(work->response_buf, 2); ksmbd_fd_put(work, fp); return 0; @@ -6042,12 +6051,12 @@ static noinline int smb2_read_pipe(struct ksmbd_work *work) int nbytes = 0, err; u64 id; struct ksmbd_rpc_command *rpc_resp; - struct smb2_read_req *req = work->request_buf; - struct smb2_read_rsp *rsp = work->response_buf; + struct smb2_read_req *req = smb2_get_msg(work->request_buf); + struct smb2_read_rsp *rsp = smb2_get_msg(work->response_buf); id = le64_to_cpu(req->VolatileFileId); - inc_rfc1001_len(rsp, 16); + inc_rfc1001_len(work->response_buf, 16); rpc_resp = ksmbd_rpc_read(work->sess, id); if (rpc_resp) { if (rpc_resp->flags != KSMBD_RPC_OK) { @@ -6066,7 +6075,7 @@ static noinline int smb2_read_pipe(struct ksmbd_work *work) rpc_resp->payload_sz); nbytes = rpc_resp->payload_sz; - work->resp_hdr_sz = get_rfc1002_len(rsp) + 4; + work->resp_hdr_sz = get_rfc1002_len(work->response_buf) + 4; work->aux_payload_sz = nbytes; kvfree(rpc_resp); } @@ -6076,8 +6085,8 @@ static noinline int smb2_read_pipe(struct ksmbd_work *work) rsp->Reserved = 0; rsp->DataLength = cpu_to_le32(nbytes); rsp->DataRemaining = 0; - rsp->Reserved2 = 0; - inc_rfc1001_len(rsp, nbytes); + rsp->Flags = 0; + inc_rfc1001_len(work->response_buf, nbytes); return 0; out: @@ -6127,14 +6136,13 @@ int smb2_read(struct ksmbd_work *work) { struct ksmbd_conn *conn = work->conn; struct smb2_read_req *req; - struct smb2_read_rsp *rsp, *rsp_org; + struct smb2_read_rsp *rsp; struct ksmbd_file *fp; loff_t offset; size_t length, mincount; ssize_t nbytes = 0, remain_bytes = 0; int err = 0; - rsp_org = work->response_buf; WORK_BUFFERS(work, req, rsp); if (test_share_config_flag(work->tcon->share_conf, @@ -6215,11 +6223,11 @@ int smb2_read(struct ksmbd_work *work) rsp->Reserved = 0; rsp->DataLength = cpu_to_le32(nbytes); rsp->DataRemaining = cpu_to_le32(remain_bytes); - rsp->Reserved2 = 0; - inc_rfc1001_len(rsp_org, 16); - work->resp_hdr_sz = get_rfc1002_len(rsp_org) + 4; + 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(rsp_org, nbytes); + inc_rfc1001_len(work->response_buf, nbytes); ksmbd_fd_put(work, fp); return 0; @@ -6254,8 +6262,8 @@ out: */ static noinline int smb2_write_pipe(struct ksmbd_work *work) { - struct smb2_write_req *req = work->request_buf; - struct smb2_write_rsp *rsp = work->response_buf; + struct smb2_write_req *req = smb2_get_msg(work->request_buf); + struct smb2_write_rsp *rsp = smb2_get_msg(work->response_buf); struct ksmbd_rpc_command *rpc_resp; u64 id = 0; int err = 0, ret = 0; @@ -6266,13 +6274,14 @@ static noinline int smb2_write_pipe(struct ksmbd_work *work) id = le64_to_cpu(req->VolatileFileId); if (le16_to_cpu(req->DataOffset) == - (offsetof(struct smb2_write_req, Buffer) - 4)) { + offsetof(struct smb2_write_req, Buffer)) { data_buf = (char *)&req->Buffer[0]; } else { - if ((u64)le16_to_cpu(req->DataOffset) + length > get_rfc1002_len(req)) { + if ((u64)le16_to_cpu(req->DataOffset) + length > + get_rfc1002_len(work->request_buf)) { pr_err("invalid write data offset %u, smb_len %u\n", le16_to_cpu(req->DataOffset), - get_rfc1002_len(req)); + get_rfc1002_len(work->request_buf)); err = -EINVAL; goto out; } @@ -6304,7 +6313,7 @@ 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(rsp, 16); + inc_rfc1001_len(work->response_buf, 16); return 0; out: if (err) { @@ -6372,7 +6381,7 @@ static ssize_t smb2_write_rdma_channel(struct ksmbd_work *work, int smb2_write(struct ksmbd_work *work) { struct smb2_write_req *req; - struct smb2_write_rsp *rsp, *rsp_org; + struct smb2_write_rsp *rsp; struct ksmbd_file *fp = NULL; loff_t offset; size_t length; @@ -6381,7 +6390,6 @@ int smb2_write(struct ksmbd_work *work) bool writethrough = false; int err = 0; - rsp_org = work->response_buf; WORK_BUFFERS(work, req, rsp); if (test_share_config_flag(work->tcon->share_conf, KSMBD_SHARE_FLAG_PIPE)) { @@ -6424,13 +6432,14 @@ int smb2_write(struct ksmbd_work *work) if (req->Channel != SMB2_CHANNEL_RDMA_V1 && req->Channel != SMB2_CHANNEL_RDMA_V1_INVALIDATE) { if (le16_to_cpu(req->DataOffset) == - (offsetof(struct smb2_write_req, Buffer) - 4)) { + offsetof(struct smb2_write_req, Buffer)) { data_buf = (char *)&req->Buffer[0]; } else { - if ((u64)le16_to_cpu(req->DataOffset) + length > get_rfc1002_len(req)) { + if ((u64)le16_to_cpu(req->DataOffset) + length > + get_rfc1002_len(work->request_buf)) { pr_err("invalid write data offset %u, smb_len %u\n", le16_to_cpu(req->DataOffset), - get_rfc1002_len(req)); + get_rfc1002_len(work->request_buf)); err = -EINVAL; goto out; } @@ -6468,7 +6477,7 @@ int smb2_write(struct ksmbd_work *work) rsp->DataLength = cpu_to_le32(nbytes); rsp->DataRemaining = 0; rsp->Reserved2 = 0; - inc_rfc1001_len(rsp_org, 16); + inc_rfc1001_len(work->response_buf, 16); ksmbd_fd_put(work, fp); return 0; @@ -6502,10 +6511,9 @@ out: int smb2_flush(struct ksmbd_work *work) { struct smb2_flush_req *req; - struct smb2_flush_rsp *rsp, *rsp_org; + struct smb2_flush_rsp *rsp; int err; - rsp_org = work->response_buf; WORK_BUFFERS(work, req, rsp); ksmbd_debug(SMB, "SMB2_FLUSH called for fid %llu\n", @@ -6519,7 +6527,7 @@ int smb2_flush(struct ksmbd_work *work) rsp->StructureSize = cpu_to_le16(4); rsp->Reserved = 0; - inc_rfc1001_len(rsp_org, 4); + inc_rfc1001_len(work->response_buf, 4); return 0; out: @@ -6540,7 +6548,7 @@ out: int smb2_cancel(struct ksmbd_work *work) { struct ksmbd_conn *conn = work->conn; - struct smb2_hdr *hdr = work->request_buf; + struct smb2_hdr *hdr = smb2_get_msg(work->request_buf); struct smb2_hdr *chdr; struct ksmbd_work *cancel_work = NULL; int canceled = 0; @@ -6555,7 +6563,7 @@ int smb2_cancel(struct ksmbd_work *work) spin_lock(&conn->request_lock); list_for_each_entry(cancel_work, command_list, async_request_entry) { - chdr = cancel_work->request_buf; + chdr = smb2_get_msg(cancel_work->request_buf); if (cancel_work->async_id != le64_to_cpu(hdr->Id.AsyncId)) @@ -6574,7 +6582,7 @@ int smb2_cancel(struct ksmbd_work *work) spin_lock(&conn->request_lock); list_for_each_entry(cancel_work, command_list, request_entry) { - chdr = cancel_work->request_buf; + chdr = smb2_get_msg(cancel_work->request_buf); if (chdr->MessageId != hdr->MessageId || cancel_work == work) @@ -6709,8 +6717,8 @@ static inline bool lock_defer_pending(struct file_lock *fl) */ int smb2_lock(struct ksmbd_work *work) { - struct smb2_lock_req *req = work->request_buf; - struct smb2_lock_rsp *rsp = work->response_buf; + struct smb2_lock_req *req = smb2_get_msg(work->request_buf); + struct smb2_lock_rsp *rsp = smb2_get_msg(work->response_buf); struct smb2_lock_element *lock_ele; struct ksmbd_file *fp = NULL; struct file_lock *flock = NULL; @@ -7017,7 +7025,7 @@ skip: ksmbd_debug(SMB, "successful in taking lock\n"); rsp->hdr.Status = STATUS_SUCCESS; rsp->Reserved = 0; - inc_rfc1001_len(rsp, 4); + inc_rfc1001_len(work->response_buf, 4); ksmbd_fd_put(work, fp); return 0; @@ -7312,7 +7320,7 @@ static int fsctl_validate_negotiate_info(struct ksmbd_conn *conn, int ret = 0; int dialect; - if (in_buf_len < sizeof(struct validate_negotiate_info_req) + + if (in_buf_len < offsetof(struct validate_negotiate_info_req, Dialects) + le16_to_cpu(neg_req->DialectCount) * sizeof(__le16)) return -EINVAL; @@ -7435,9 +7443,9 @@ static inline int fsctl_set_sparse(struct ksmbd_work *work, u64 id, old_fattr = fp->f_ci->m_fattr; if (sparse->SetSparse) - fp->f_ci->m_fattr |= ATTR_SPARSE_FILE_LE; + fp->f_ci->m_fattr |= FILE_ATTRIBUTE_SPARSE_FILE_LE; else - fp->f_ci->m_fattr &= ~ATTR_SPARSE_FILE_LE; + fp->f_ci->m_fattr &= ~FILE_ATTRIBUTE_SPARSE_FILE_LE; if (fp->f_ci->m_fattr != old_fattr && test_share_config_flag(work->tcon->share_conf, @@ -7490,13 +7498,12 @@ static int fsctl_request_resume_key(struct ksmbd_work *work, int smb2_ioctl(struct ksmbd_work *work) { struct smb2_ioctl_req *req; - struct smb2_ioctl_rsp *rsp, *rsp_org; + struct smb2_ioctl_rsp *rsp; unsigned int cnt_code, nbytes = 0, out_buf_len, in_buf_len; u64 id = KSMBD_NO_FID; struct ksmbd_conn *conn = work->conn; int ret = 0; - rsp_org = work->response_buf; if (work->next_smb2_rcv_hdr_off) { req = ksmbd_req_buf_next(work); rsp = ksmbd_resp_buf_next(work); @@ -7506,8 +7513,8 @@ int smb2_ioctl(struct ksmbd_work *work) id = work->compound_fid; } } else { - req = work->request_buf; - rsp = work->response_buf; + req = smb2_get_msg(work->request_buf); + rsp = smb2_get_msg(work->response_buf); } if (!has_file_id(id)) @@ -7787,7 +7794,7 @@ dup_ext_out: rsp->Reserved = cpu_to_le16(0); rsp->Flags = cpu_to_le32(0); rsp->Reserved2 = cpu_to_le32(0); - inc_rfc1001_len(rsp_org, 48 + nbytes); + inc_rfc1001_len(work->response_buf, 48 + nbytes); return 0; @@ -7814,8 +7821,8 @@ out: */ static void smb20_oplock_break_ack(struct ksmbd_work *work) { - struct smb2_oplock_break *req = work->request_buf; - struct smb2_oplock_break *rsp = work->response_buf; + struct smb2_oplock_break *req = smb2_get_msg(work->request_buf); + struct smb2_oplock_break *rsp = smb2_get_msg(work->response_buf); struct ksmbd_file *fp; struct oplock_info *opinfo = NULL; __le32 err = 0; @@ -7922,7 +7929,7 @@ static void smb20_oplock_break_ack(struct ksmbd_work *work) rsp->Reserved2 = 0; rsp->VolatileFid = cpu_to_le64(volatile_id); rsp->PersistentFid = cpu_to_le64(persistent_id); - inc_rfc1001_len(rsp, 24); + inc_rfc1001_len(work->response_buf, 24); return; err_out: @@ -7958,8 +7965,8 @@ static int check_lease_state(struct lease *lease, __le32 req_state) static void smb21_lease_break_ack(struct ksmbd_work *work) { struct ksmbd_conn *conn = work->conn; - struct smb2_lease_ack *req = work->request_buf; - struct smb2_lease_ack *rsp = work->response_buf; + struct smb2_lease_ack *req = smb2_get_msg(work->request_buf); + struct smb2_lease_ack *rsp = smb2_get_msg(work->response_buf); struct oplock_info *opinfo; __le32 err = 0; int ret = 0; @@ -8071,7 +8078,7 @@ static void smb21_lease_break_ack(struct ksmbd_work *work) memcpy(rsp->LeaseKey, req->LeaseKey, 16); rsp->LeaseState = lease_state; rsp->LeaseDuration = 0; - inc_rfc1001_len(rsp, 36); + inc_rfc1001_len(work->response_buf, 36); return; err_out: @@ -8092,8 +8099,8 @@ err_out: */ int smb2_oplock_break(struct ksmbd_work *work) { - struct smb2_oplock_break *req = work->request_buf; - struct smb2_oplock_break *rsp = work->response_buf; + struct smb2_oplock_break *req = smb2_get_msg(work->request_buf); + struct smb2_oplock_break *rsp = smb2_get_msg(work->response_buf); switch (le16_to_cpu(req->StructureSize)) { case OP_BREAK_STRUCT_SIZE_20: @@ -8120,8 +8127,8 @@ int smb2_oplock_break(struct ksmbd_work *work) */ int smb2_notify(struct ksmbd_work *work) { - struct smb2_notify_req *req; - struct smb2_notify_rsp *rsp; + struct smb2_change_notify_req *req; + struct smb2_change_notify_rsp *rsp; WORK_BUFFERS(work, req, rsp); @@ -8145,7 +8152,7 @@ int smb2_notify(struct ksmbd_work *work) */ bool smb2_is_sign_req(struct ksmbd_work *work, unsigned int command) { - struct smb2_hdr *rcv_hdr2 = work->request_buf; + struct smb2_hdr *rcv_hdr2 = smb2_get_msg(work->request_buf); if ((rcv_hdr2->Flags & SMB2_FLAGS_SIGNED) && command != SMB2_NEGOTIATE_HE && @@ -8164,22 +8171,22 @@ bool smb2_is_sign_req(struct ksmbd_work *work, unsigned int command) */ int smb2_check_sign_req(struct ksmbd_work *work) { - struct smb2_hdr *hdr, *hdr_org; + struct smb2_hdr *hdr; char signature_req[SMB2_SIGNATURE_SIZE]; char signature[SMB2_HMACSHA256_SIZE]; struct kvec iov[1]; size_t len; - hdr_org = hdr = work->request_buf; + hdr = smb2_get_msg(work->request_buf); if (work->next_smb2_rcv_hdr_off) hdr = ksmbd_req_buf_next(work); if (!hdr->NextCommand && !work->next_smb2_rcv_hdr_off) - len = be32_to_cpu(hdr_org->smb2_buf_length); + len = get_rfc1002_len(work->request_buf); else if (hdr->NextCommand) len = le32_to_cpu(hdr->NextCommand); else - len = be32_to_cpu(hdr_org->smb2_buf_length) - + len = get_rfc1002_len(work->request_buf) - work->next_smb2_rcv_hdr_off; memcpy(signature_req, hdr->Signature, SMB2_SIGNATURE_SIZE); @@ -8207,25 +8214,26 @@ int smb2_check_sign_req(struct ksmbd_work *work) */ void smb2_set_sign_rsp(struct ksmbd_work *work) { - struct smb2_hdr *hdr, *hdr_org; + struct smb2_hdr *hdr; struct smb2_hdr *req_hdr; char signature[SMB2_HMACSHA256_SIZE]; struct kvec iov[2]; size_t len; int n_vec = 1; - hdr_org = hdr = work->response_buf; + 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(hdr_org); + len = get_rfc1002_len(work->response_buf); if (req_hdr->NextCommand) len = ALIGN(len, 8); } else { - len = get_rfc1002_len(hdr_org) - work->next_smb2_rsp_hdr_off; + len = get_rfc1002_len(work->response_buf) - + work->next_smb2_rsp_hdr_off; len = ALIGN(len, 8); } @@ -8261,23 +8269,23 @@ int smb3_check_sign_req(struct ksmbd_work *work) { struct ksmbd_conn *conn = work->conn; char *signing_key; - struct smb2_hdr *hdr, *hdr_org; + struct smb2_hdr *hdr; struct channel *chann; char signature_req[SMB2_SIGNATURE_SIZE]; char signature[SMB2_CMACAES_SIZE]; struct kvec iov[1]; size_t len; - hdr_org = hdr = work->request_buf; + hdr = smb2_get_msg(work->request_buf); if (work->next_smb2_rcv_hdr_off) hdr = ksmbd_req_buf_next(work); if (!hdr->NextCommand && !work->next_smb2_rcv_hdr_off) - len = be32_to_cpu(hdr_org->smb2_buf_length); + len = get_rfc1002_len(work->request_buf); else if (hdr->NextCommand) len = le32_to_cpu(hdr->NextCommand); else - len = be32_to_cpu(hdr_org->smb2_buf_length) - + len = get_rfc1002_len(work->request_buf) - work->next_smb2_rcv_hdr_off; if (le16_to_cpu(hdr->Command) == SMB2_SESSION_SETUP_HE) { @@ -8318,8 +8326,7 @@ 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; - struct smb2_hdr *hdr, *hdr_org; + struct smb2_hdr *req_hdr, *hdr; struct channel *chann; char signature[SMB2_CMACAES_SIZE]; struct kvec iov[2]; @@ -8327,18 +8334,19 @@ void smb3_set_sign_rsp(struct ksmbd_work *work) size_t len; char *signing_key; - hdr_org = hdr = work->response_buf; + 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(hdr_org); + len = get_rfc1002_len(work->response_buf); if (req_hdr->NextCommand) len = ALIGN(len, 8); } else { - len = get_rfc1002_len(hdr_org) - work->next_smb2_rsp_hdr_off; + len = get_rfc1002_len(work->response_buf) - + work->next_smb2_rsp_hdr_off; len = ALIGN(len, 8); } @@ -8391,7 +8399,7 @@ void smb3_preauth_hash_rsp(struct ksmbd_work *work) if (le16_to_cpu(req->Command) == SMB2_NEGOTIATE_HE && conn->preauth_info) - ksmbd_gen_preauth_integrity_hash(conn, (char *)rsp, + ksmbd_gen_preauth_integrity_hash(conn, work->response_buf, conn->preauth_info->Preauth_HashValue); if (le16_to_cpu(rsp->Command) == SMB2_SESSION_SETUP_HE && sess) { @@ -8409,35 +8417,34 @@ void smb3_preauth_hash_rsp(struct ksmbd_work *work) if (!hash_value) return; } - ksmbd_gen_preauth_integrity_hash(conn, (char *)rsp, + ksmbd_gen_preauth_integrity_hash(conn, work->response_buf, hash_value); } } -static void fill_transform_hdr(struct smb2_transform_hdr *tr_hdr, char *old_buf, - __le16 cipher_type) +static void fill_transform_hdr(void *tr_buf, char *old_buf, __le16 cipher_type) { - struct smb2_hdr *hdr = (struct smb2_hdr *)old_buf; + struct smb2_transform_hdr *tr_hdr = tr_buf + 4; + struct smb2_hdr *hdr = smb2_get_msg(old_buf); unsigned int orig_len = get_rfc1002_len(old_buf); - memset(tr_hdr, 0, sizeof(struct smb2_transform_hdr)); + memset(tr_buf, 0, sizeof(struct smb2_transform_hdr) + 4); tr_hdr->ProtocolId = SMB2_TRANSFORM_PROTO_NUM; tr_hdr->OriginalMessageSize = cpu_to_le32(orig_len); - tr_hdr->Flags = cpu_to_le16(0x01); + tr_hdr->Flags = cpu_to_le16(TRANSFORM_FLAG_ENCRYPTED); if (cipher_type == SMB2_ENCRYPTION_AES128_GCM || cipher_type == SMB2_ENCRYPTION_AES256_GCM) get_random_bytes(&tr_hdr->Nonce, SMB3_AES_GCM_NONCE); else get_random_bytes(&tr_hdr->Nonce, SMB3_AES_CCM_NONCE); memcpy(&tr_hdr->SessionId, &hdr->SessionId, 8); - inc_rfc1001_len(tr_hdr, sizeof(struct smb2_transform_hdr) - 4); - inc_rfc1001_len(tr_hdr, orig_len); + inc_rfc1001_len(tr_buf, sizeof(struct smb2_transform_hdr)); + inc_rfc1001_len(tr_buf, orig_len); } int smb3_encrypt_resp(struct ksmbd_work *work) { char *buf = work->response_buf; - struct smb2_transform_hdr *tr_hdr; struct kvec iov[3]; int rc = -ENOMEM; int buf_size = 0, rq_nvec = 2 + (work->aux_payload_sz ? 1 : 0); @@ -8445,15 +8452,15 @@ int smb3_encrypt_resp(struct ksmbd_work *work) if (ARRAY_SIZE(iov) < rq_nvec) return -ENOMEM; - tr_hdr = kzalloc(sizeof(struct smb2_transform_hdr), GFP_KERNEL); - if (!tr_hdr) + work->tr_buf = kzalloc(sizeof(struct smb2_transform_hdr) + 4, GFP_KERNEL); + if (!work->tr_buf) return rc; /* fill transform header */ - fill_transform_hdr(tr_hdr, buf, work->conn->cipher_type); + fill_transform_hdr(work->tr_buf, buf, work->conn->cipher_type); - iov[0].iov_base = tr_hdr; - iov[0].iov_len = sizeof(struct smb2_transform_hdr); + iov[0].iov_base = work->tr_buf; + iov[0].iov_len = sizeof(struct smb2_transform_hdr) + 4; buf_size += iov[0].iov_len - 4; iov[1].iov_base = buf + 4; @@ -8473,15 +8480,14 @@ int smb3_encrypt_resp(struct ksmbd_work *work) return rc; memmove(buf, iov[1].iov_base, iov[1].iov_len); - tr_hdr->smb2_buf_length = cpu_to_be32(buf_size); - work->tr_buf = tr_hdr; + *(__be32 *)work->tr_buf = cpu_to_be32(buf_size); return rc; } bool smb3_is_transform_hdr(void *buf) { - struct smb2_transform_hdr *trhdr = buf; + struct smb2_transform_hdr *trhdr = smb2_get_msg(buf); return trhdr->ProtocolId == SMB2_TRANSFORM_PROTO_NUM; } @@ -8491,12 +8497,10 @@ int smb3_decrypt_req(struct ksmbd_work *work) struct ksmbd_conn *conn = work->conn; struct ksmbd_session *sess; char *buf = work->request_buf; - struct smb2_hdr *hdr; unsigned int pdu_length = get_rfc1002_len(buf); struct kvec iov[2]; - int buf_data_size = pdu_length + 4 - - sizeof(struct smb2_transform_hdr); - struct smb2_transform_hdr *tr_hdr = (struct smb2_transform_hdr *)buf; + int buf_data_size = pdu_length - sizeof(struct smb2_transform_hdr); + struct smb2_transform_hdr *tr_hdr = smb2_get_msg(buf); int rc = 0; if (buf_data_size < sizeof(struct smb2_hdr)) { @@ -8518,16 +8522,15 @@ int smb3_decrypt_req(struct ksmbd_work *work) } iov[0].iov_base = buf; - iov[0].iov_len = sizeof(struct smb2_transform_hdr); - iov[1].iov_base = buf + sizeof(struct smb2_transform_hdr); + iov[0].iov_len = sizeof(struct smb2_transform_hdr) + 4; + iov[1].iov_base = buf + sizeof(struct smb2_transform_hdr) + 4; iov[1].iov_len = buf_data_size; rc = ksmbd_crypt_message(conn, iov, 2, 0); if (rc) return rc; memmove(buf + 4, iov[1].iov_base, buf_data_size); - hdr = (struct smb2_hdr *)buf; - hdr->smb2_buf_length = cpu_to_be32(buf_data_size); + *(__be32 *)buf = cpu_to_be32(buf_data_size); return rc; } @@ -8535,7 +8538,7 @@ int smb3_decrypt_req(struct ksmbd_work *work) bool smb3_11_final_sess_setup_resp(struct ksmbd_work *work) { struct ksmbd_conn *conn = work->conn; - struct smb2_hdr *rsp = work->response_buf; + struct smb2_hdr *rsp = smb2_get_msg(work->response_buf); if (conn->dialect < SMB30_PROT_ID) return false; diff --git a/fs/ksmbd/smb2pdu.h b/fs/ksmbd/smb2pdu.h index ff5a2f01d34a..4a3e4339d4c4 100644 --- a/fs/ksmbd/smb2pdu.h +++ b/fs/ksmbd/smb2pdu.h @@ -10,60 +10,6 @@ #include "ntlmssp.h" #include "smbacl.h" -/* - * Note that, due to trying to use names similar to the protocol specifications, - * there are many mixed case field names in the structures below. Although - * this does not match typical Linux kernel style, it is necessary to be - * able to match against the protocol specfication. - * - * SMB2 commands - * Some commands have minimal (wct=0,bcc=0), or uninteresting, responses - * (ie no useful data other than the SMB error code itself) and are marked such. - * Knowing this helps avoid response buffer allocations and copy in some cases. - */ - -/* List of commands in host endian */ -#define SMB2_NEGOTIATE_HE 0x0000 -#define SMB2_SESSION_SETUP_HE 0x0001 -#define SMB2_LOGOFF_HE 0x0002 /* trivial request/resp */ -#define SMB2_TREE_CONNECT_HE 0x0003 -#define SMB2_TREE_DISCONNECT_HE 0x0004 /* trivial req/resp */ -#define SMB2_CREATE_HE 0x0005 -#define SMB2_CLOSE_HE 0x0006 -#define SMB2_FLUSH_HE 0x0007 /* trivial resp */ -#define SMB2_READ_HE 0x0008 -#define SMB2_WRITE_HE 0x0009 -#define SMB2_LOCK_HE 0x000A -#define SMB2_IOCTL_HE 0x000B -#define SMB2_CANCEL_HE 0x000C -#define SMB2_ECHO_HE 0x000D -#define SMB2_QUERY_DIRECTORY_HE 0x000E -#define SMB2_CHANGE_NOTIFY_HE 0x000F -#define SMB2_QUERY_INFO_HE 0x0010 -#define SMB2_SET_INFO_HE 0x0011 -#define SMB2_OPLOCK_BREAK_HE 0x0012 - -/* The same list in little endian */ -#define SMB2_NEGOTIATE cpu_to_le16(SMB2_NEGOTIATE_HE) -#define SMB2_SESSION_SETUP cpu_to_le16(SMB2_SESSION_SETUP_HE) -#define SMB2_LOGOFF cpu_to_le16(SMB2_LOGOFF_HE) -#define SMB2_TREE_CONNECT cpu_to_le16(SMB2_TREE_CONNECT_HE) -#define SMB2_TREE_DISCONNECT cpu_to_le16(SMB2_TREE_DISCONNECT_HE) -#define SMB2_CREATE cpu_to_le16(SMB2_CREATE_HE) -#define SMB2_CLOSE cpu_to_le16(SMB2_CLOSE_HE) -#define SMB2_FLUSH cpu_to_le16(SMB2_FLUSH_HE) -#define SMB2_READ cpu_to_le16(SMB2_READ_HE) -#define SMB2_WRITE cpu_to_le16(SMB2_WRITE_HE) -#define SMB2_LOCK cpu_to_le16(SMB2_LOCK_HE) -#define SMB2_IOCTL cpu_to_le16(SMB2_IOCTL_HE) -#define SMB2_CANCEL cpu_to_le16(SMB2_CANCEL_HE) -#define SMB2_ECHO cpu_to_le16(SMB2_ECHO_HE) -#define SMB2_QUERY_DIRECTORY cpu_to_le16(SMB2_QUERY_DIRECTORY_HE) -#define SMB2_CHANGE_NOTIFY cpu_to_le16(SMB2_CHANGE_NOTIFY_HE) -#define SMB2_QUERY_INFO cpu_to_le16(SMB2_QUERY_INFO_HE) -#define SMB2_SET_INFO cpu_to_le16(SMB2_SET_INFO_HE) -#define SMB2_OPLOCK_BREAK cpu_to_le16(SMB2_OPLOCK_BREAK_HE) - /*Create Action Flags*/ #define FILE_SUPERSEDED 0x00000000 #define FILE_OPENED 0x00000001 @@ -96,9 +42,6 @@ /* SMB2 Max Credits */ #define SMB2_MAX_CREDITS 8192 -#define SMB2_CLIENT_GUID_SIZE 16 -#define SMB2_CREATE_GUID_SIZE 16 - /* Maximum buffer size value we can send with 1 credit */ #define SMB2_MAX_BUFFER_SIZE 65536 @@ -107,87 +50,12 @@ /* BB FIXME - analyze following length BB */ #define MAX_SMB2_HDR_SIZE 0x78 /* 4 len + 64 hdr + (2*24 wct) + 2 bct + 2 pad */ -#define SMB2_PROTO_NUMBER cpu_to_le32(0x424d53fe) /* 'B''M''S' */ -#define SMB2_TRANSFORM_PROTO_NUM cpu_to_le32(0x424d53fd) - #define SMB21_DEFAULT_IOSIZE (1024 * 1024) #define SMB3_DEFAULT_IOSIZE (4 * 1024 * 1024) #define SMB3_DEFAULT_TRANS_SIZE (1024 * 1024) #define SMB3_MIN_IOSIZE (64 * 1024) #define SMB3_MAX_IOSIZE (8 * 1024 * 1024) -/* - * SMB2 Header Definition - * - * "MBZ" : Must be Zero - * "BB" : BugBug, Something to check/review/analyze later - * "PDU" : "Protocol Data Unit" (ie a network "frame") - * - */ - -#define __SMB2_HEADER_STRUCTURE_SIZE 64 -#define SMB2_HEADER_STRUCTURE_SIZE \ - cpu_to_le16(__SMB2_HEADER_STRUCTURE_SIZE) - -struct smb2_hdr { - __be32 smb2_buf_length; /* big endian on wire */ - /* - * length is only two or three bytes - with - * one or two byte type preceding it that MBZ - */ - __le32 ProtocolId; /* 0xFE 'S' 'M' 'B' */ - __le16 StructureSize; /* 64 */ - __le16 CreditCharge; /* MBZ */ - __le32 Status; /* Error from server */ - __le16 Command; - __le16 CreditRequest; /* CreditResponse */ - __le32 Flags; - __le32 NextCommand; - __le64 MessageId; - union { - struct { - __le32 ProcessId; - __le32 TreeId; - } __packed SyncId; - __le64 AsyncId; - } __packed Id; - __le64 SessionId; - __u8 Signature[16]; -} __packed; - -struct smb2_pdu { - struct smb2_hdr hdr; - __le16 StructureSize2; /* size of wct area (varies, request specific) */ -} __packed; - -#define SMB3_AES_CCM_NONCE 11 -#define SMB3_AES_GCM_NONCE 12 - -struct smb2_transform_hdr { - __be32 smb2_buf_length; /* big endian on wire */ - /* - * length is only two or three bytes - with - * one or two byte type preceding it that MBZ - */ - __le32 ProtocolId; /* 0xFD 'S' 'M' 'B' */ - __u8 Signature[16]; - __u8 Nonce[16]; - __le32 OriginalMessageSize; - __u16 Reserved1; - __le16 Flags; /* EncryptionAlgorithm */ - __le64 SessionId; -} __packed; - -/* - * SMB2 flag definitions - */ -#define SMB2_FLAGS_SERVER_TO_REDIR cpu_to_le32(0x00000001) -#define SMB2_FLAGS_ASYNC_COMMAND cpu_to_le32(0x00000002) -#define SMB2_FLAGS_RELATED_OPERATIONS cpu_to_le32(0x00000004) -#define SMB2_FLAGS_SIGNED cpu_to_le32(0x00000008) -#define SMB2_FLAGS_DFS_OPERATIONS cpu_to_le32(0x10000000) -#define SMB2_FLAGS_REPLAY_OPERATIONS cpu_to_le32(0x20000000) - /* * Definitions for SMB2 Protocol Data Units (network frames) * @@ -209,425 +77,30 @@ struct smb2_err_rsp { __u8 ErrorData[1]; /* variable length */ } __packed; -struct smb2_negotiate_req { - struct smb2_hdr hdr; - __le16 StructureSize; /* Must be 36 */ - __le16 DialectCount; - __le16 SecurityMode; - __le16 Reserved; /* MBZ */ - __le32 Capabilities; - __u8 ClientGUID[SMB2_CLIENT_GUID_SIZE]; - /* In SMB3.02 and earlier next three were MBZ le64 ClientStartTime */ - __le32 NegotiateContextOffset; /* SMB3.1.1 only. MBZ earlier */ - __le16 NegotiateContextCount; /* SMB3.1.1 only. MBZ earlier */ - __le16 Reserved2; - __le16 Dialects[1]; /* One dialect (vers=) at a time for now */ -} __packed; - -/* SecurityMode flags */ -#define SMB2_NEGOTIATE_SIGNING_ENABLED_LE cpu_to_le16(0x0001) -#define SMB2_NEGOTIATE_SIGNING_REQUIRED 0x0002 -#define SMB2_NEGOTIATE_SIGNING_REQUIRED_LE cpu_to_le16(0x0002) -/* Capabilities flags */ -#define SMB2_GLOBAL_CAP_DFS 0x00000001 -#define SMB2_GLOBAL_CAP_LEASING 0x00000002 /* Resp only New to SMB2.1 */ -#define SMB2_GLOBAL_CAP_LARGE_MTU 0X00000004 /* Resp only New to SMB2.1 */ -#define SMB2_GLOBAL_CAP_MULTI_CHANNEL 0x00000008 /* New to SMB3 */ -#define SMB2_GLOBAL_CAP_PERSISTENT_HANDLES 0x00000010 /* New to SMB3 */ -#define SMB2_GLOBAL_CAP_DIRECTORY_LEASING 0x00000020 /* New to SMB3 */ -#define SMB2_GLOBAL_CAP_ENCRYPTION 0x00000040 /* New to SMB3 */ -/* Internal types */ -#define SMB2_NT_FIND 0x00100000 -#define SMB2_LARGE_FILES 0x00200000 - -#define SMB311_SALT_SIZE 32 -/* Hash Algorithm Types */ -#define SMB2_PREAUTH_INTEGRITY_SHA512 cpu_to_le16(0x0001) - -#define PREAUTH_HASHVALUE_SIZE 64 - struct preauth_integrity_info { /* PreAuth integrity Hash ID */ __le16 Preauth_HashId; /* PreAuth integrity Hash Value */ - __u8 Preauth_HashValue[PREAUTH_HASHVALUE_SIZE]; + __u8 Preauth_HashValue[SMB2_PREAUTH_HASH_SIZE]; }; -/* offset is sizeof smb2_negotiate_rsp - 4 but rounded up to 8 bytes. */ +/* offset is sizeof smb2_negotiate_rsp but rounded up to 8 bytes. */ #ifdef CONFIG_SMB_SERVER_KERBEROS5 -/* sizeof(struct smb2_negotiate_rsp) - 4 = +/* sizeof(struct smb2_negotiate_rsp) = * header(64) + response(64) + GSS_LENGTH(96) + GSS_PADDING(0) */ #define OFFSET_OF_NEG_CONTEXT 0xe0 #else -/* sizeof(struct smb2_negotiate_rsp) - 4 = +/* sizeof(struct smb2_negotiate_rsp) = * header(64) + response(64) + GSS_LENGTH(74) + GSS_PADDING(6) */ #define OFFSET_OF_NEG_CONTEXT 0xd0 #endif -#define SMB2_PREAUTH_INTEGRITY_CAPABILITIES cpu_to_le16(1) -#define SMB2_ENCRYPTION_CAPABILITIES cpu_to_le16(2) -#define SMB2_COMPRESSION_CAPABILITIES cpu_to_le16(3) -#define SMB2_NETNAME_NEGOTIATE_CONTEXT_ID cpu_to_le16(5) -#define SMB2_SIGNING_CAPABILITIES cpu_to_le16(8) -#define SMB2_POSIX_EXTENSIONS_AVAILABLE cpu_to_le16(0x100) - -struct smb2_neg_context { - __le16 ContextType; - __le16 DataLength; - __le32 Reserved; - /* Followed by array of data */ -} __packed; - -struct smb2_preauth_neg_context { - __le16 ContextType; /* 1 */ - __le16 DataLength; - __le32 Reserved; - __le16 HashAlgorithmCount; /* 1 */ - __le16 SaltLength; - __le16 HashAlgorithms; /* HashAlgorithms[0] since only one defined */ - __u8 Salt[SMB311_SALT_SIZE]; -} __packed; - -/* Encryption Algorithms Ciphers */ -#define SMB2_ENCRYPTION_AES128_CCM cpu_to_le16(0x0001) -#define SMB2_ENCRYPTION_AES128_GCM cpu_to_le16(0x0002) -#define SMB2_ENCRYPTION_AES256_CCM cpu_to_le16(0x0003) -#define SMB2_ENCRYPTION_AES256_GCM cpu_to_le16(0x0004) - -struct smb2_encryption_neg_context { - __le16 ContextType; /* 2 */ - __le16 DataLength; - __le32 Reserved; - /* CipherCount usally 2, but can be 3 when AES256-GCM enabled */ - __le16 CipherCount; /* AES-128-GCM and AES-128-CCM by default */ - __le16 Ciphers[]; -} __packed; - -#define SMB3_COMPRESS_NONE cpu_to_le16(0x0000) -#define SMB3_COMPRESS_LZNT1 cpu_to_le16(0x0001) -#define SMB3_COMPRESS_LZ77 cpu_to_le16(0x0002) -#define SMB3_COMPRESS_LZ77_HUFF cpu_to_le16(0x0003) - -struct smb2_compression_ctx { - __le16 ContextType; /* 3 */ - __le16 DataLength; - __le32 Reserved; - __le16 CompressionAlgorithmCount; - __u16 Padding; - __le32 Reserved1; - __le16 CompressionAlgorithms[]; -} __packed; - -#define POSIX_CTXT_DATA_LEN 16 -struct smb2_posix_neg_context { - __le16 ContextType; /* 0x100 */ - __le16 DataLength; - __le32 Reserved; - __u8 Name[16]; /* POSIX ctxt GUID 93AD25509CB411E7B42383DE968BCD7C */ -} __packed; - -struct smb2_netname_neg_context { - __le16 ContextType; /* 0x100 */ - __le16 DataLength; - __le32 Reserved; - __le16 NetName[]; /* hostname of target converted to UCS-2 */ -} __packed; - -/* Signing algorithms */ -#define SIGNING_ALG_HMAC_SHA256 cpu_to_le16(0) -#define SIGNING_ALG_AES_CMAC cpu_to_le16(1) -#define SIGNING_ALG_AES_GMAC cpu_to_le16(2) - -struct smb2_signing_capabilities { - __le16 ContextType; /* 8 */ - __le16 DataLength; - __le32 Reserved; - __le16 SigningAlgorithmCount; - __le16 SigningAlgorithms[]; -} __packed; - -struct smb2_negotiate_rsp { - struct smb2_hdr hdr; - __le16 StructureSize; /* Must be 65 */ - __le16 SecurityMode; - __le16 DialectRevision; - __le16 NegotiateContextCount; /* Prior to SMB3.1.1 was Reserved & MBZ */ - __u8 ServerGUID[16]; - __le32 Capabilities; - __le32 MaxTransactSize; - __le32 MaxReadSize; - __le32 MaxWriteSize; - __le64 SystemTime; /* MBZ */ - __le64 ServerStartTime; - __le16 SecurityBufferOffset; - __le16 SecurityBufferLength; - __le32 NegotiateContextOffset; /* Pre:SMB3.1.1 was reserved/ignored */ - __u8 Buffer[1]; /* variable length GSS security buffer */ -} __packed; - -/* Flags */ -#define SMB2_SESSION_REQ_FLAG_BINDING 0x01 -#define SMB2_SESSION_REQ_FLAG_ENCRYPT_DATA 0x04 - #define SMB2_SESSION_EXPIRED (0) #define SMB2_SESSION_IN_PROGRESS BIT(0) #define SMB2_SESSION_VALID BIT(1) -/* Flags */ -#define SMB2_SESSION_REQ_FLAG_BINDING 0x01 -#define SMB2_SESSION_REQ_FLAG_ENCRYPT_DATA 0x04 - -struct smb2_sess_setup_req { - struct smb2_hdr hdr; - __le16 StructureSize; /* Must be 25 */ - __u8 Flags; - __u8 SecurityMode; - __le32 Capabilities; - __le32 Channel; - __le16 SecurityBufferOffset; - __le16 SecurityBufferLength; - __le64 PreviousSessionId; - __u8 Buffer[1]; /* variable length GSS security buffer */ -} __packed; - -/* Flags/Reserved for SMB3.1.1 */ -#define SMB2_SHAREFLAG_CLUSTER_RECONNECT 0x0001 - -/* Currently defined SessionFlags */ -#define SMB2_SESSION_FLAG_IS_GUEST_LE cpu_to_le16(0x0001) -#define SMB2_SESSION_FLAG_IS_NULL_LE cpu_to_le16(0x0002) -#define SMB2_SESSION_FLAG_ENCRYPT_DATA_LE cpu_to_le16(0x0004) -struct smb2_sess_setup_rsp { - struct smb2_hdr hdr; - __le16 StructureSize; /* Must be 9 */ - __le16 SessionFlags; - __le16 SecurityBufferOffset; - __le16 SecurityBufferLength; - __u8 Buffer[1]; /* variable length GSS security buffer */ -} __packed; - -struct smb2_logoff_req { - struct smb2_hdr hdr; - __le16 StructureSize; /* Must be 4 */ - __le16 Reserved; -} __packed; - -struct smb2_logoff_rsp { - struct smb2_hdr hdr; - __le16 StructureSize; /* Must be 4 */ - __le16 Reserved; -} __packed; - -struct smb2_tree_connect_req { - struct smb2_hdr hdr; - __le16 StructureSize; /* Must be 9 */ - __le16 Reserved; /* Flags in SMB3.1.1 */ - __le16 PathOffset; - __le16 PathLength; - __u8 Buffer[1]; /* variable length */ -} __packed; - -struct smb2_tree_connect_rsp { - struct smb2_hdr hdr; - __le16 StructureSize; /* Must be 16 */ - __u8 ShareType; /* see below */ - __u8 Reserved; - __le32 ShareFlags; /* see below */ - __le32 Capabilities; /* see below */ - __le32 MaximalAccess; -} __packed; - -/* Possible ShareType values */ -#define SMB2_SHARE_TYPE_DISK 0x01 -#define SMB2_SHARE_TYPE_PIPE 0x02 -#define SMB2_SHARE_TYPE_PRINT 0x03 - -/* - * Possible ShareFlags - exactly one and only one of the first 4 caching flags - * must be set (any of the remaining, SHI1005, flags may be set individually - * or in combination. - */ -#define SMB2_SHAREFLAG_MANUAL_CACHING 0x00000000 -#define SMB2_SHAREFLAG_AUTO_CACHING 0x00000010 -#define SMB2_SHAREFLAG_VDO_CACHING 0x00000020 -#define SMB2_SHAREFLAG_NO_CACHING 0x00000030 -#define SHI1005_FLAGS_DFS 0x00000001 -#define SHI1005_FLAGS_DFS_ROOT 0x00000002 -#define SHI1005_FLAGS_RESTRICT_EXCLUSIVE_OPENS 0x00000100 -#define SHI1005_FLAGS_FORCE_SHARED_DELETE 0x00000200 -#define SHI1005_FLAGS_ALLOW_NAMESPACE_CACHING 0x00000400 -#define SHI1005_FLAGS_ACCESS_BASED_DIRECTORY_ENUM 0x00000800 -#define SHI1005_FLAGS_FORCE_LEVELII_OPLOCK 0x00001000 -#define SHI1005_FLAGS_ENABLE_HASH 0x00002000 - -/* Possible share capabilities */ -#define SMB2_SHARE_CAP_DFS cpu_to_le32(0x00000008) - -struct smb2_tree_disconnect_req { - struct smb2_hdr hdr; - __le16 StructureSize; /* Must be 4 */ - __le16 Reserved; -} __packed; - -struct smb2_tree_disconnect_rsp { - struct smb2_hdr hdr; - __le16 StructureSize; /* Must be 4 */ - __le16 Reserved; -} __packed; - -#define ATTR_READONLY_LE cpu_to_le32(ATTR_READONLY) -#define ATTR_HIDDEN_LE cpu_to_le32(ATTR_HIDDEN) -#define ATTR_SYSTEM_LE cpu_to_le32(ATTR_SYSTEM) -#define ATTR_DIRECTORY_LE cpu_to_le32(ATTR_DIRECTORY) -#define ATTR_ARCHIVE_LE cpu_to_le32(ATTR_ARCHIVE) -#define ATTR_NORMAL_LE cpu_to_le32(ATTR_NORMAL) -#define ATTR_TEMPORARY_LE cpu_to_le32(ATTR_TEMPORARY) -#define ATTR_SPARSE_FILE_LE cpu_to_le32(ATTR_SPARSE) -#define ATTR_REPARSE_POINT_LE cpu_to_le32(ATTR_REPARSE) -#define ATTR_COMPRESSED_LE cpu_to_le32(ATTR_COMPRESSED) -#define ATTR_OFFLINE_LE cpu_to_le32(ATTR_OFFLINE) -#define ATTR_NOT_CONTENT_INDEXED_LE cpu_to_le32(ATTR_NOT_CONTENT_INDEXED) -#define ATTR_ENCRYPTED_LE cpu_to_le32(ATTR_ENCRYPTED) -#define ATTR_INTEGRITY_STREAML_LE cpu_to_le32(0x00008000) -#define ATTR_NO_SCRUB_DATA_LE cpu_to_le32(0x00020000) -#define ATTR_MASK_LE cpu_to_le32(0x00007FB7) - -/* Oplock levels */ -#define SMB2_OPLOCK_LEVEL_NONE 0x00 -#define SMB2_OPLOCK_LEVEL_II 0x01 -#define SMB2_OPLOCK_LEVEL_EXCLUSIVE 0x08 -#define SMB2_OPLOCK_LEVEL_BATCH 0x09 -#define SMB2_OPLOCK_LEVEL_LEASE 0xFF -/* Non-spec internal type */ -#define SMB2_OPLOCK_LEVEL_NOCHANGE 0x99 - -/* Desired Access Flags */ -#define FILE_READ_DATA_LE cpu_to_le32(0x00000001) -#define FILE_LIST_DIRECTORY_LE cpu_to_le32(0x00000001) -#define FILE_WRITE_DATA_LE cpu_to_le32(0x00000002) -#define FILE_ADD_FILE_LE cpu_to_le32(0x00000002) -#define FILE_APPEND_DATA_LE cpu_to_le32(0x00000004) -#define FILE_ADD_SUBDIRECTORY_LE cpu_to_le32(0x00000004) -#define FILE_READ_EA_LE cpu_to_le32(0x00000008) -#define FILE_WRITE_EA_LE cpu_to_le32(0x00000010) -#define FILE_EXECUTE_LE cpu_to_le32(0x00000020) -#define FILE_TRAVERSE_LE cpu_to_le32(0x00000020) -#define FILE_DELETE_CHILD_LE cpu_to_le32(0x00000040) -#define FILE_READ_ATTRIBUTES_LE cpu_to_le32(0x00000080) -#define FILE_WRITE_ATTRIBUTES_LE cpu_to_le32(0x00000100) -#define FILE_DELETE_LE cpu_to_le32(0x00010000) -#define FILE_READ_CONTROL_LE cpu_to_le32(0x00020000) -#define FILE_WRITE_DAC_LE cpu_to_le32(0x00040000) -#define FILE_WRITE_OWNER_LE cpu_to_le32(0x00080000) -#define FILE_SYNCHRONIZE_LE cpu_to_le32(0x00100000) -#define FILE_ACCESS_SYSTEM_SECURITY_LE cpu_to_le32(0x01000000) -#define FILE_MAXIMAL_ACCESS_LE cpu_to_le32(0x02000000) -#define FILE_GENERIC_ALL_LE cpu_to_le32(0x10000000) -#define FILE_GENERIC_EXECUTE_LE cpu_to_le32(0x20000000) -#define FILE_GENERIC_WRITE_LE cpu_to_le32(0x40000000) -#define FILE_GENERIC_READ_LE cpu_to_le32(0x80000000) -#define DESIRED_ACCESS_MASK cpu_to_le32(0xF21F01FF) - -/* ShareAccess Flags */ -#define FILE_SHARE_READ_LE cpu_to_le32(0x00000001) -#define FILE_SHARE_WRITE_LE cpu_to_le32(0x00000002) -#define FILE_SHARE_DELETE_LE cpu_to_le32(0x00000004) -#define FILE_SHARE_ALL_LE cpu_to_le32(0x00000007) - -/* CreateDisposition Flags */ -#define FILE_SUPERSEDE_LE cpu_to_le32(0x00000000) -#define FILE_OPEN_LE cpu_to_le32(0x00000001) -#define FILE_CREATE_LE cpu_to_le32(0x00000002) -#define FILE_OPEN_IF_LE cpu_to_le32(0x00000003) -#define FILE_OVERWRITE_LE cpu_to_le32(0x00000004) -#define FILE_OVERWRITE_IF_LE cpu_to_le32(0x00000005) -#define FILE_CREATE_MASK_LE cpu_to_le32(0x00000007) - -#define FILE_READ_DESIRED_ACCESS_LE (FILE_READ_DATA_LE | \ - FILE_READ_EA_LE | \ - FILE_GENERIC_READ_LE) -#define FILE_WRITE_DESIRE_ACCESS_LE (FILE_WRITE_DATA_LE | \ - FILE_APPEND_DATA_LE | \ - FILE_WRITE_EA_LE | \ - FILE_WRITE_ATTRIBUTES_LE | \ - FILE_GENERIC_WRITE_LE) - -/* Impersonation Levels */ -#define IL_ANONYMOUS_LE cpu_to_le32(0x00000000) -#define IL_IDENTIFICATION_LE cpu_to_le32(0x00000001) -#define IL_IMPERSONATION_LE cpu_to_le32(0x00000002) -#define IL_DELEGATE_LE cpu_to_le32(0x00000003) - -/* Create Context Values */ -#define SMB2_CREATE_EA_BUFFER "ExtA" /* extended attributes */ -#define SMB2_CREATE_SD_BUFFER "SecD" /* security descriptor */ -#define SMB2_CREATE_DURABLE_HANDLE_REQUEST "DHnQ" -#define SMB2_CREATE_DURABLE_HANDLE_RECONNECT "DHnC" -#define SMB2_CREATE_ALLOCATION_SIZE "AlSi" -#define SMB2_CREATE_QUERY_MAXIMAL_ACCESS_REQUEST "MxAc" -#define SMB2_CREATE_TIMEWARP_REQUEST "TWrp" -#define SMB2_CREATE_QUERY_ON_DISK_ID "QFid" -#define SMB2_CREATE_REQUEST_LEASE "RqLs" -#define SMB2_CREATE_DURABLE_HANDLE_REQUEST_V2 "DH2Q" -#define SMB2_CREATE_DURABLE_HANDLE_RECONNECT_V2 "DH2C" -#define SMB2_CREATE_APP_INSTANCE_ID "\x45\xBC\xA6\x6A\xEF\xA7\xF7\x4A\x90\x08\xFA\x46\x2E\x14\x4D\x74" - #define SMB2_CREATE_APP_INSTANCE_VERSION "\xB9\x82\xD0\xB7\x3B\x56\x07\x4F\xA0\x7B\x52\x4A\x81\x16\xA0\x10" -#define SVHDX_OPEN_DEVICE_CONTEXT 0x83CE6F1AD851E0986E34401CC9BCFCE9 -#define SMB2_CREATE_TAG_POSIX "\x93\xAD\x25\x50\x9C\xB4\x11\xE7\xB4\x23\x83\xDE\x96\x8B\xCD\x7C" - -struct smb2_create_req { - struct smb2_hdr hdr; - __le16 StructureSize; /* Must be 57 */ - __u8 SecurityFlags; - __u8 RequestedOplockLevel; - __le32 ImpersonationLevel; - __le64 SmbCreateFlags; - __le64 Reserved; - __le32 DesiredAccess; - __le32 FileAttributes; - __le32 ShareAccess; - __le32 CreateDisposition; - __le32 CreateOptions; - __le16 NameOffset; - __le16 NameLength; - __le32 CreateContextsOffset; - __le32 CreateContextsLength; - __u8 Buffer[0]; -} __packed; - -struct smb2_create_rsp { - struct smb2_hdr hdr; - __le16 StructureSize; /* Must be 89 */ - __u8 OplockLevel; - __u8 Reserved; - __le32 CreateAction; - __le64 CreationTime; - __le64 LastAccessTime; - __le64 LastWriteTime; - __le64 ChangeTime; - __le64 AllocationSize; - __le64 EndofFile; - __le32 FileAttributes; - __le32 Reserved2; - __le64 PersistentFileId; - __le64 VolatileFileId; - __le32 CreateContextsOffset; - __le32 CreateContextsLength; - __u8 Buffer[1]; -} __packed; - -struct create_context { - __le32 Next; - __le16 NameOffset; - __le16 NameLength; - __le16 Reserved; - __le16 DataOffset; - __le32 DataLength; - __u8 Buffer[0]; -} __packed; - struct create_durable_req_v2 { struct create_context ccontext; __u8 Name[8]; @@ -743,22 +216,21 @@ struct create_posix_rsp { #define SMB2_LEASE_FLAG_BREAK_IN_PROGRESS_LE cpu_to_le32(0x02) +#define SMB2_LEASE_KEY_SIZE 16 + struct lease_context { - __le64 LeaseKeyLow; - __le64 LeaseKeyHigh; + __u8 LeaseKey[SMB2_LEASE_KEY_SIZE]; __le32 LeaseState; __le32 LeaseFlags; __le64 LeaseDuration; } __packed; struct lease_context_v2 { - __le64 LeaseKeyLow; - __le64 LeaseKeyHigh; + __u8 LeaseKey[SMB2_LEASE_KEY_SIZE]; __le32 LeaseState; __le32 LeaseFlags; __le64 LeaseDuration; - __le64 ParentLeaseKeyLow; - __le64 ParentLeaseKeyHigh; + __u8 ParentLeaseKey[SMB2_LEASE_KEY_SIZE]; __le16 Epoch; __le16 Reserved; } __packed; @@ -776,114 +248,12 @@ struct create_lease_v2 { __u8 Pad[4]; } __packed; -/* Currently defined values for close flags */ -#define SMB2_CLOSE_FLAG_POSTQUERY_ATTRIB cpu_to_le16(0x0001) -struct smb2_close_req { - struct smb2_hdr hdr; - __le16 StructureSize; /* Must be 24 */ - __le16 Flags; - __le32 Reserved; - __le64 PersistentFileId; - __le64 VolatileFileId; -} __packed; - -struct smb2_close_rsp { - struct smb2_hdr hdr; - __le16 StructureSize; /* 60 */ - __le16 Flags; - __le32 Reserved; - __le64 CreationTime; - __le64 LastAccessTime; - __le64 LastWriteTime; - __le64 ChangeTime; - __le64 AllocationSize; /* Beginning of FILE_STANDARD_INFO equivalent */ - __le64 EndOfFile; - __le32 Attributes; -} __packed; - -struct smb2_flush_req { - struct smb2_hdr hdr; - __le16 StructureSize; /* Must be 24 */ - __le16 Reserved1; - __le32 Reserved2; - __le64 PersistentFileId; - __le64 VolatileFileId; -} __packed; - -struct smb2_flush_rsp { - struct smb2_hdr hdr; - __le16 StructureSize; - __le16 Reserved; -} __packed; - struct smb2_buffer_desc_v1 { __le64 offset; __le32 token; __le32 length; } __packed; -#define SMB2_CHANNEL_NONE cpu_to_le32(0x00000000) -#define SMB2_CHANNEL_RDMA_V1 cpu_to_le32(0x00000001) -#define SMB2_CHANNEL_RDMA_V1_INVALIDATE cpu_to_le32(0x00000002) - -struct smb2_read_req { - struct smb2_hdr hdr; - __le16 StructureSize; /* Must be 49 */ - __u8 Padding; /* offset from start of SMB2 header to place read */ - __u8 Reserved; - __le32 Length; - __le64 Offset; - __le64 PersistentFileId; - __le64 VolatileFileId; - __le32 MinimumCount; - __le32 Channel; /* Reserved MBZ */ - __le32 RemainingBytes; - __le16 ReadChannelInfoOffset; /* Reserved MBZ */ - __le16 ReadChannelInfoLength; /* Reserved MBZ */ - __u8 Buffer[1]; -} __packed; - -struct smb2_read_rsp { - struct smb2_hdr hdr; - __le16 StructureSize; /* Must be 17 */ - __u8 DataOffset; - __u8 Reserved; - __le32 DataLength; - __le32 DataRemaining; - __u32 Reserved2; - __u8 Buffer[1]; -} __packed; - -/* For write request Flags field below the following flag is defined: */ -#define SMB2_WRITEFLAG_WRITE_THROUGH 0x00000001 - -struct smb2_write_req { - struct smb2_hdr hdr; - __le16 StructureSize; /* Must be 49 */ - __le16 DataOffset; /* offset from start of SMB2 header to write data */ - __le32 Length; - __le64 Offset; - __le64 PersistentFileId; - __le64 VolatileFileId; - __le32 Channel; /* Reserved MBZ */ - __le32 RemainingBytes; - __le16 WriteChannelInfoOffset; /* Reserved MBZ */ - __le16 WriteChannelInfoLength; /* Reserved MBZ */ - __le32 Flags; - __u8 Buffer[1]; -} __packed; - -struct smb2_write_rsp { - struct smb2_hdr hdr; - __le16 StructureSize; /* Must be 17 */ - __u8 DataOffset; - __u8 Reserved; - __le32 DataLength; - __le32 DataRemaining; - __u32 Reserved2; - __u8 Buffer[1]; -} __packed; - #define SMB2_0_IOCTL_IS_FSCTL 0x00000001 struct duplicate_extents_to_file { @@ -1033,43 +403,6 @@ struct reparse_data_buffer { __u8 DataBuffer[]; /* Variable Length */ } __packed; -/* Completion Filter flags for Notify */ -#define FILE_NOTIFY_CHANGE_FILE_NAME 0x00000001 -#define FILE_NOTIFY_CHANGE_DIR_NAME 0x00000002 -#define FILE_NOTIFY_CHANGE_NAME 0x00000003 -#define FILE_NOTIFY_CHANGE_ATTRIBUTES 0x00000004 -#define FILE_NOTIFY_CHANGE_SIZE 0x00000008 -#define FILE_NOTIFY_CHANGE_LAST_WRITE 0x00000010 -#define FILE_NOTIFY_CHANGE_LAST_ACCESS 0x00000020 -#define FILE_NOTIFY_CHANGE_CREATION 0x00000040 -#define FILE_NOTIFY_CHANGE_EA 0x00000080 -#define FILE_NOTIFY_CHANGE_SECURITY 0x00000100 -#define FILE_NOTIFY_CHANGE_STREAM_NAME 0x00000200 -#define FILE_NOTIFY_CHANGE_STREAM_SIZE 0x00000400 -#define FILE_NOTIFY_CHANGE_STREAM_WRITE 0x00000800 - -/* Flags */ -#define SMB2_WATCH_TREE 0x0001 - -struct smb2_notify_req { - struct smb2_hdr hdr; - __le16 StructureSize; /* Must be 32 */ - __le16 Flags; - __le32 OutputBufferLength; - __le64 PersistentFileId; - __le64 VolatileFileId; - __u32 CompletionFileter; - __u32 Reserved; -} __packed; - -struct smb2_notify_rsp { - struct smb2_hdr hdr; - __le16 StructureSize; /* Must be 9 */ - __le16 OutputBufferOffset; - __le32 OutputBufferLength; - __u8 Buffer[1]; -} __packed; - /* SMB2 Notify Action Flags */ #define FILE_ACTION_ADDED 0x00000001 #define FILE_ACTION_REMOVED 0x00000002 @@ -1528,7 +861,7 @@ struct smb2_file_pos_info { __le64 CurrentByteOffset; } __packed; -#define FILE_MODE_INFO_MASK cpu_to_le32(0x0000103e) +#define FILE_MODE_INFO_MASK cpu_to_le32(0x0000100e) struct smb2_file_mode_info { __le32 Mode; @@ -1705,4 +1038,13 @@ int smb2_ioctl(struct ksmbd_work *work); int smb2_oplock_break(struct ksmbd_work *work); int smb2_notify(struct ksmbd_work *ksmbd_work); +/* + * Get the body of the smb2 message excluding the 4 byte rfc1002 headers + * from request/response buffer. + */ +static inline void *smb2_get_msg(void *buf) +{ + return buf + 4; +} + #endif /* _SMB2PDU_H */ diff --git a/fs/ksmbd/smb_common.c b/fs/ksmbd/smb_common.c index 707490ab1f4c..ef7f42b0290a 100644 --- a/fs/ksmbd/smb_common.c +++ b/fs/ksmbd/smb_common.c @@ -132,7 +132,7 @@ int ksmbd_lookup_protocol_idx(char *str) */ int ksmbd_verify_smb_message(struct ksmbd_work *work) { - struct smb2_hdr *smb2_hdr = work->request_buf + work->next_smb2_rcv_hdr_off; + struct smb2_hdr *smb2_hdr = ksmbd_req_buf_next(work); struct smb_hdr *hdr; if (smb2_hdr->ProtocolId == SMB2_PROTO_NUMBER) @@ -239,14 +239,14 @@ int ksmbd_lookup_dialect_by_id(__le16 *cli_dialects, __le16 dialects_count) static int ksmbd_negotiate_smb_dialect(void *buf) { int smb_buf_length = get_rfc1002_len(buf); - __le32 proto = ((struct smb2_hdr *)buf)->ProtocolId; + __le32 proto = ((struct smb2_hdr *)smb2_get_msg(buf))->ProtocolId; if (proto == SMB2_PROTO_NUMBER) { struct smb2_negotiate_req *req; int smb2_neg_size = - offsetof(struct smb2_negotiate_req, Dialects) - 4; + offsetof(struct smb2_negotiate_req, Dialects); - req = (struct smb2_negotiate_req *)buf; + req = (struct smb2_negotiate_req *)smb2_get_msg(buf); if (smb2_neg_size > smb_buf_length) goto err_out; @@ -445,11 +445,12 @@ int ksmbd_smb_negotiate_common(struct ksmbd_work *work, unsigned int command) struct ksmbd_conn *conn = work->conn; int ret; - conn->dialect = ksmbd_negotiate_smb_dialect(work->request_buf); + conn->dialect = + ksmbd_negotiate_smb_dialect(work->request_buf); ksmbd_debug(SMB, "conn->dialect 0x%x\n", conn->dialect); if (command == SMB2_NEGOTIATE_HE) { - struct smb2_hdr *smb2_hdr = work->request_buf; + struct smb2_hdr *smb2_hdr = smb2_get_msg(work->request_buf); if (smb2_hdr->ProtocolId != SMB2_PROTO_NUMBER) { ksmbd_debug(SMB, "Downgrade to SMB1 negotiation\n"); diff --git a/fs/ksmbd/smb_common.h b/fs/ksmbd/smb_common.h index 6e79e7577f6b..50590842b651 100644 --- a/fs/ksmbd/smb_common.h +++ b/fs/ksmbd/smb_common.h @@ -10,6 +10,7 @@ #include "glob.h" #include "nterr.h" +#include "../smbfs_common/smb2pdu.h" #include "smb2pdu.h" /* ksmbd's Specific ERRNO */ @@ -32,17 +33,6 @@ #define SMB302_VERSION_STRING "3.02" #define SMB311_VERSION_STRING "3.1.1" -/* Dialects */ -#define SMB10_PROT_ID 0x00 -#define SMB20_PROT_ID 0x0202 -#define SMB21_PROT_ID 0x0210 -/* multi-protocol negotiate request */ -#define SMB2X_PROT_ID 0x02FF -#define SMB30_PROT_ID 0x0300 -#define SMB302_PROT_ID 0x0302 -#define SMB311_PROT_ID 0x0311 -#define BAD_PROT_ID 0xFFFF - #define SMB_ECHO_INTERVAL (60 * HZ) #define CIFS_DEFAULT_IOSIZE (64 * 1024) @@ -59,21 +49,6 @@ /* * File Attribute flags */ -#define ATTR_READONLY 0x0001 -#define ATTR_HIDDEN 0x0002 -#define ATTR_SYSTEM 0x0004 -#define ATTR_VOLUME 0x0008 -#define ATTR_DIRECTORY 0x0010 -#define ATTR_ARCHIVE 0x0020 -#define ATTR_DEVICE 0x0040 -#define ATTR_NORMAL 0x0080 -#define ATTR_TEMPORARY 0x0100 -#define ATTR_SPARSE 0x0200 -#define ATTR_REPARSE 0x0400 -#define ATTR_COMPRESSED 0x0800 -#define ATTR_OFFLINE 0x1000 -#define ATTR_NOT_CONTENT_INDEXED 0x2000 -#define ATTR_ENCRYPTED 0x4000 #define ATTR_POSIX_SEMANTICS 0x01000000 #define ATTR_BACKUP_SEMANTICS 0x02000000 #define ATTR_DELETE_ON_CLOSE 0x04000000 @@ -82,23 +57,6 @@ #define ATTR_NO_BUFFERING 0x20000000 #define ATTR_WRITE_THROUGH 0x80000000 -#define ATTR_READONLY_LE cpu_to_le32(ATTR_READONLY) -#define ATTR_HIDDEN_LE cpu_to_le32(ATTR_HIDDEN) -#define ATTR_SYSTEM_LE cpu_to_le32(ATTR_SYSTEM) -#define ATTR_DIRECTORY_LE cpu_to_le32(ATTR_DIRECTORY) -#define ATTR_ARCHIVE_LE cpu_to_le32(ATTR_ARCHIVE) -#define ATTR_NORMAL_LE cpu_to_le32(ATTR_NORMAL) -#define ATTR_TEMPORARY_LE cpu_to_le32(ATTR_TEMPORARY) -#define ATTR_SPARSE_FILE_LE cpu_to_le32(ATTR_SPARSE) -#define ATTR_REPARSE_POINT_LE cpu_to_le32(ATTR_REPARSE) -#define ATTR_COMPRESSED_LE cpu_to_le32(ATTR_COMPRESSED) -#define ATTR_OFFLINE_LE cpu_to_le32(ATTR_OFFLINE) -#define ATTR_NOT_CONTENT_INDEXED_LE cpu_to_le32(ATTR_NOT_CONTENT_INDEXED) -#define ATTR_ENCRYPTED_LE cpu_to_le32(ATTR_ENCRYPTED) -#define ATTR_INTEGRITY_STREAML_LE cpu_to_le32(0x00008000) -#define ATTR_NO_SCRUB_DATA_LE cpu_to_le32(0x00020000) -#define ATTR_MASK_LE cpu_to_le32(0x00007FB7) - /* List of FileSystemAttributes - see 2.5.1 of MS-FSCC */ #define FILE_SUPPORTS_SPARSE_VDL 0x10000000 /* faster nonsparse extend */ #define FILE_SUPPORTS_BLOCK_REFCOUNTING 0x08000000 /* allow ioctl dup extents */ @@ -160,11 +118,6 @@ /* file_execute, file_read_attributes*/ /* write_dac, and delete. */ -#define FILE_READ_RIGHTS (FILE_READ_DATA | FILE_READ_EA | FILE_READ_ATTRIBUTES) -#define FILE_WRITE_RIGHTS (FILE_WRITE_DATA | FILE_APPEND_DATA \ - | FILE_WRITE_EA | FILE_WRITE_ATTRIBUTES) -#define FILE_EXEC_RIGHTS (FILE_EXECUTE) - #define SET_FILE_READ_RIGHTS (FILE_READ_DATA | FILE_READ_EA \ | FILE_READ_ATTRIBUTES \ | DELETE | READ_CONTROL | WRITE_DAC \ @@ -477,12 +430,6 @@ struct smb_version_cmds { int (*proc)(struct ksmbd_work *swork); }; -static inline size_t -smb2_hdr_size_no_buflen(struct smb_version_values *vals) -{ - return vals->header_size - 4; -} - int ksmbd_min_protocol(void); int ksmbd_max_protocol(void); diff --git a/fs/ksmbd/transport_rdma.c b/fs/ksmbd/transport_rdma.c index a2fd5a4d4cd5..7e57cbb0bb35 100644 --- a/fs/ksmbd/transport_rdma.c +++ b/fs/ksmbd/transport_rdma.c @@ -484,7 +484,7 @@ static int smb_direct_check_recvmsg(struct smb_direct_recvmsg *recvmsg) struct smb_direct_data_transfer *req = (struct smb_direct_data_transfer *)recvmsg->packet; struct smb2_hdr *hdr = (struct smb2_hdr *)(recvmsg->packet - + le32_to_cpu(req->data_offset) - 4); + + le32_to_cpu(req->data_offset)); ksmbd_debug(RDMA, "CreditGranted: %u, CreditRequested: %u, DataLength: %u, RemainingDataLength: %u, SMB: %x, Command: %u\n", le16_to_cpu(req->credits_granted), @@ -2043,7 +2043,6 @@ int ksmbd_rdma_destroy(void) smb_direct_listener.cm_id = NULL; if (smb_direct_wq) { - flush_workqueue(smb_direct_wq); destroy_workqueue(smb_direct_wq); smb_direct_wq = NULL; } diff --git a/fs/ksmbd/vfs.c b/fs/ksmbd/vfs.c index 835b384b0895..19d36393974c 100644 --- a/fs/ksmbd/vfs.c +++ b/fs/ksmbd/vfs.c @@ -1013,7 +1013,7 @@ int ksmbd_vfs_zero_data(struct ksmbd_work *work, struct ksmbd_file *fp, loff_t off, loff_t len) { smb_break_all_levII_oplock(work, fp, 1); - if (fp->f_ci->m_fattr & ATTR_SPARSE_FILE_LE) + if (fp->f_ci->m_fattr & FILE_ATTRIBUTE_SPARSE_FILE_LE) return vfs_fallocate(fp->filp, FALLOC_FL_PUNCH_HOLE | FALLOC_FL_KEEP_SIZE, off, len); @@ -1624,7 +1624,7 @@ void *ksmbd_vfs_init_kstat(char **p, struct ksmbd_kstat *ksmbd_kstat) time = ksmbd_UnixTimeToNT(kstat->ctime); info->ChangeTime = cpu_to_le64(time); - if (ksmbd_kstat->file_attributes & ATTR_DIRECTORY_LE) { + if (ksmbd_kstat->file_attributes & FILE_ATTRIBUTE_DIRECTORY_LE) { info->EndOfFile = 0; info->AllocationSize = 0; } else { @@ -1654,9 +1654,9 @@ int ksmbd_vfs_fill_dentry_attrs(struct ksmbd_work *work, * or that acl is disable in server's filesystem and the config is yes. */ if (S_ISDIR(ksmbd_kstat->kstat->mode)) - ksmbd_kstat->file_attributes = ATTR_DIRECTORY_LE; + ksmbd_kstat->file_attributes = FILE_ATTRIBUTE_DIRECTORY_LE; else - ksmbd_kstat->file_attributes = ATTR_ARCHIVE_LE; + ksmbd_kstat->file_attributes = FILE_ATTRIBUTE_ARCHIVE_LE; if (test_share_config_flag(work->tcon->share_conf, KSMBD_SHARE_FLAG_STORE_DOS_ATTRS)) { diff --git a/fs/ksmbd/vfs.h b/fs/ksmbd/vfs.h index b0d5b8feb4a3..adf94a4f22fa 100644 --- a/fs/ksmbd/vfs.h +++ b/fs/ksmbd/vfs.h @@ -25,48 +25,9 @@ enum { }; /* CreateOptions */ -/* Flag is set, it must not be a file , valid for directory only */ -#define FILE_DIRECTORY_FILE_LE cpu_to_le32(0x00000001) -#define FILE_WRITE_THROUGH_LE cpu_to_le32(0x00000002) -#define FILE_SEQUENTIAL_ONLY_LE cpu_to_le32(0x00000004) - -/* Should not buffer on server*/ -#define FILE_NO_INTERMEDIATE_BUFFERING_LE cpu_to_le32(0x00000008) -/* MBZ */ -#define FILE_SYNCHRONOUS_IO_ALERT_LE cpu_to_le32(0x00000010) -/* MBZ */ -#define FILE_SYNCHRONOUS_IO_NONALERT_LE cpu_to_le32(0x00000020) - -/* Flaf must not be set for directory */ -#define FILE_NON_DIRECTORY_FILE_LE cpu_to_le32(0x00000040) - -/* Should be zero */ #define CREATE_TREE_CONNECTION cpu_to_le32(0x00000080) -#define FILE_COMPLETE_IF_OPLOCKED_LE cpu_to_le32(0x00000100) -#define FILE_NO_EA_KNOWLEDGE_LE cpu_to_le32(0x00000200) -#define FILE_OPEN_REMOTE_INSTANCE cpu_to_le32(0x00000400) - -/** - * Doc says this is obsolete "open for recovery" flag should be zero - * in any case. - */ -#define CREATE_OPEN_FOR_RECOVERY cpu_to_le32(0x00000400) -#define FILE_RANDOM_ACCESS_LE cpu_to_le32(0x00000800) -#define FILE_DELETE_ON_CLOSE_LE cpu_to_le32(0x00001000) -#define FILE_OPEN_BY_FILE_ID_LE cpu_to_le32(0x00002000) -#define FILE_OPEN_FOR_BACKUP_INTENT_LE cpu_to_le32(0x00004000) -#define FILE_NO_COMPRESSION_LE cpu_to_le32(0x00008000) - -/* Should be zero*/ -#define FILE_OPEN_REQUIRING_OPLOCK cpu_to_le32(0x00010000) -#define FILE_DISALLOW_EXCLUSIVE cpu_to_le32(0x00020000) #define FILE_RESERVE_OPFILTER_LE cpu_to_le32(0x00100000) -#define FILE_OPEN_REPARSE_POINT_LE cpu_to_le32(0x00200000) -#define FILE_OPEN_NO_RECALL_LE cpu_to_le32(0x00400000) -/* Should be zero */ -#define FILE_OPEN_FOR_FREE_SPACE_QUERY_LE cpu_to_le32(0x00800000) -#define CREATE_OPTIONS_MASK cpu_to_le32(0x00FFFFFF) #define CREATE_OPTION_READONLY 0x10000000 /* system. NB not sent over wire */ #define CREATE_OPTION_SPECIAL 0x20000000 diff --git a/include/linux/genhd.h b/include/linux/genhd.h index 462634b4b48f..74c410263113 100644 --- a/include/linux/genhd.h +++ b/include/linux/genhd.h @@ -205,9 +205,9 @@ static inline dev_t disk_devt(struct gendisk *disk) void disk_uevent(struct gendisk *disk, enum kobject_action action); /* block/genhd.c */ -int device_add_disk(struct device *parent, struct gendisk *disk, - const struct attribute_group **groups); -static inline int add_disk(struct gendisk *disk) +int __must_check device_add_disk(struct device *parent, struct gendisk *disk, + const struct attribute_group **groups); +static inline int __must_check add_disk(struct gendisk *disk) { return device_add_disk(NULL, disk, NULL); } diff --git a/include/uapi/linux/virtio_mem.h b/include/uapi/linux/virtio_mem.h index 70e01c687d5e..e9122f1d0e0c 100644 --- a/include/uapi/linux/virtio_mem.h +++ b/include/uapi/linux/virtio_mem.h @@ -68,9 +68,10 @@ * explicitly triggered (VIRTIO_MEM_REQ_UNPLUG). * * There are no guarantees what will happen if unplugged memory is - * read/written. Such memory should, in general, not be touched. E.g., - * even writing might succeed, but the values will simply be discarded at - * random points in time. + * read/written. In general, unplugged memory should not be touched, because + * the resulting action is undefined. There is one exception: without + * VIRTIO_MEM_F_UNPLUGGED_INACCESSIBLE, unplugged memory inside the usable + * region can be read, to simplify creation of memory dumps. * * It can happen that the device cannot process a request, because it is * busy. The device driver has to retry later. @@ -87,6 +88,8 @@ /* node_id is an ACPI PXM and is valid */ #define VIRTIO_MEM_F_ACPI_PXM 0 +/* unplugged memory must not be accessed */ +#define VIRTIO_MEM_F_UNPLUGGED_INACCESSIBLE 1 /* --- virtio-mem: guest -> host requests --- */ diff --git a/mm/memory-failure.c b/mm/memory-failure.c index f64ebb6226cb..07c875fdeaf0 100644 --- a/mm/memory-failure.c +++ b/mm/memory-failure.c @@ -58,7 +58,6 @@ #include #include #include -#include #include "internal.h" #include "ras/ras_event.h" @@ -868,7 +867,6 @@ static int me_pagecache_clean(struct page_state *ps, struct page *p) { int ret; struct address_space *mapping; - bool extra_pins; delete_from_lru_cache(p); @@ -897,24 +895,18 @@ static int me_pagecache_clean(struct page_state *ps, struct page *p) goto out; } - /* - * The shmem page is kept in page cache instead of truncating - * so is expected to have an extra refcount after error-handling. - */ - extra_pins = shmem_mapping(mapping); - /* * Truncation is a bit tricky. Enable it per file system for now. * * Open: to take i_rwsem or not for this? Right now we don't. */ ret = truncate_error_page(p, page_to_pfn(p), mapping); - if (has_extra_refcount(ps, p, extra_pins)) - ret = MF_FAILED; - out: unlock_page(p); + if (has_extra_refcount(ps, p, false)) + ret = MF_FAILED; + return ret; } diff --git a/mm/shmem.c b/mm/shmem.c index f0eee4e221a7..dc038ce78700 100644 --- a/mm/shmem.c +++ b/mm/shmem.c @@ -2456,7 +2456,6 @@ shmem_write_begin(struct file *file, struct address_space *mapping, struct inode *inode = mapping->host; struct shmem_inode_info *info = SHMEM_I(inode); pgoff_t index = pos >> PAGE_SHIFT; - int ret = 0; /* i_rwsem is held by caller */ if (unlikely(info->seals & (F_SEAL_GROW | @@ -2467,15 +2466,7 @@ shmem_write_begin(struct file *file, struct address_space *mapping, return -EPERM; } - ret = shmem_getpage(inode, index, pagep, SGP_WRITE); - - if (*pagep && PageHWPoison(*pagep)) { - unlock_page(*pagep); - put_page(*pagep); - ret = -EIO; - } - - return ret; + return shmem_getpage(inode, index, pagep, SGP_WRITE); } static int @@ -2562,12 +2553,6 @@ static ssize_t shmem_file_read_iter(struct kiocb *iocb, struct iov_iter *to) if (sgp == SGP_CACHE) set_page_dirty(page); unlock_page(page); - - if (PageHWPoison(page)) { - put_page(page); - error = -EIO; - break; - } } /* @@ -3107,8 +3092,7 @@ static const char *shmem_get_link(struct dentry *dentry, page = find_get_page(inode->i_mapping, 0); if (!page) return ERR_PTR(-ECHILD); - if (PageHWPoison(page) || - !PageUptodate(page)) { + if (!PageUptodate(page)) { put_page(page); return ERR_PTR(-ECHILD); } @@ -3116,11 +3100,6 @@ static const char *shmem_get_link(struct dentry *dentry, error = shmem_getpage(inode, 0, &page, SGP_READ); if (error) return ERR_PTR(error); - if (page && PageHWPoison(page)) { - unlock_page(page); - put_page(page); - return ERR_PTR(-ECHILD); - } unlock_page(page); } set_delayed_call(done, shmem_put_link, page); @@ -3771,13 +3750,6 @@ static void shmem_destroy_inodecache(void) kmem_cache_destroy(shmem_inode_cachep); } -/* Keep the page in page cache instead of truncating it */ -static int shmem_error_remove_page(struct address_space *mapping, - struct page *page) -{ - return 0; -} - const struct address_space_operations shmem_aops = { .writepage = shmem_writepage, .set_page_dirty = __set_page_dirty_no_writeback, @@ -3788,7 +3760,7 @@ const struct address_space_operations shmem_aops = { #ifdef CONFIG_MIGRATION .migratepage = migrate_page, #endif - .error_remove_page = shmem_error_remove_page, + .error_remove_page = generic_error_remove_page, }; EXPORT_SYMBOL(shmem_aops); @@ -4199,10 +4171,6 @@ struct page *shmem_read_mapping_page_gfp(struct address_space *mapping, page = ERR_PTR(error); else unlock_page(page); - - if (PageHWPoison(page)) - page = ERR_PTR(-EIO); - return page; #else /* diff --git a/mm/userfaultfd.c b/mm/userfaultfd.c index 0780c2a57ff1..ac6f036298cd 100644 --- a/mm/userfaultfd.c +++ b/mm/userfaultfd.c @@ -232,11 +232,6 @@ static int mcontinue_atomic_pte(struct mm_struct *dst_mm, goto out; } - if (PageHWPoison(page)) { - ret = -EIO; - goto out_release; - } - ret = mfill_atomic_install_pte(dst_mm, dst_pmd, dst_vma, dst_addr, page, false, wp_copy); if (ret)